3 /* _PDCLIB_print( const char *, struct _PDCLIB_status_t * )
5 This file is part of the Public Domain C Library (PDCLib).
6 Permission is granted to use, modify, and / or redistribute at will.
19 #include <_PDCLIB_io.h>
21 /* Using an integer's bits as flags for both the conversion flags and length
24 /* FIXME: one too many flags to work on a 16-bit machine, join some (e.g. the
25 width flags) into a combined field.
27 #define E_minus (1<<0)
30 #define E_space (1<<3)
34 #define E_short (1<<7)
36 #define E_llong (1<<9)
37 #define E_intmax (1<<10)
38 #define E_size (1<<11)
39 #define E_ptrdiff (1<<12)
40 #define E_intptr (1<<13)
41 #define E_ldouble (1<<14)
42 #define E_lower (1<<15)
43 #define E_unsigned (1<<16)
45 /* This macro delivers a given character to either a memory buffer or a stream,
46 depending on the contents of 'status' (struct _PDCLIB_status_t).
47 x - the character to be delivered
48 i - pointer to number of characters already delivered in this call
49 n - pointer to maximum number of characters to be delivered in this call
50 s - the buffer into which the character shall be delivered
55 if ( status->i < status->n ) { \
56 if ( status->stream != NULL ) \
57 _PDCLIB_putc_unlocked( character, status->stream ); \
59 status->s[status->i] = character; \
64 /* Maximum number of output characters =
65 * number of bits in (u)intmax_t / number of bits per character in smallest
66 * base. Smallest base is octal, 3 bits/char.
68 * Additionally require 2 extra characters for prefixes
70 static const size_t maxIntLen = sizeof(intmax_t) * CHAR_BIT / 3 + 1;
72 static void int2base( uintmax_t value, struct _PDCLIB_status_t * status )
75 if ( ! ( status->flags & E_unsigned ) )
77 intmax_t signval = (intmax_t) value;
78 bool negative = signval < 0;
79 value = signval < 0 ? -signval : signval;
85 else if ( status->flags & E_plus )
89 else if (status->flags & E_space )
95 // The user could theoretically ask for a silly buffer length here.
96 // Perhaps after a certain size we should malloc? Or do we refuse to protect
97 // them from their own stupidity?
98 size_t bufLen = (status->width > maxIntLen ? status->width : maxIntLen) + 2;
100 char * outend = outbuf + bufLen;
103 // Build up our output string - backwards
105 const char * digits = (status->flags & E_lower) ?
106 _PDCLIB_digits : _PDCLIB_Xdigits;
107 uintmax_t remaining = value;
108 if(status->prec != 0 || remaining != 0) do {
109 uintmax_t digit = remaining % status->base;
110 remaining /= status->base;
112 outend[-++written] = digits[digit];
113 } while(remaining != 0);
116 // Pad field out to the precision specification
117 while( (long) written < status->prec ) outend[-++written] = '0';
119 // If a field width specified, and zero padding was requested, then pad to
121 unsigned padding = 0;
122 if ( ( ! ( status->flags & E_minus ) ) && ( status->flags & E_zero ) )
124 while( written < (int) status->width )
126 outend[-++written] = '0';
134 if ( padding == 0 ) written++;
135 outend[-written] = sign;
137 else if ( status->flags & E_alt )
139 switch ( status->base )
142 if ( outend[-written] != '0' ) outend[-++written] = '0';
146 if ( value == 0 ) break;
148 written += padding < 2 ? 2 - padding : 0;
149 outend[-written ] = '0';
150 outend[-written + 1] = (status->flags & E_lower) ? 'x' : 'X';
157 // Space padding to field width
158 if ( ! ( status->flags & ( E_minus | E_zero ) ) )
160 while( written < (int) status->width ) outend[-++written] = ' ';
164 status->current = written;
166 PUT( outend[-written--] );
169 static void printstr( const char * str, struct _PDCLIB_status_t * status )
171 if ( status->width == 0 || status->flags & E_minus )
173 // Simple case or left justification
174 while ( str[status->current] &&
175 ( status->prec < 0 || (long)status->current < status->prec ) )
177 PUT( str[status->current++] );
180 while( status->current < status->width )
186 // Right justification
187 size_t len = status->prec >= 0 ? strnlen( str, status->prec )
189 int padding = status->width - len;
190 while((long)status->current < padding)
196 for( size_t i = 0; i != len; i++ )
204 static void printchar( char chr, struct _PDCLIB_status_t * status )
206 if( ! ( status->flags & E_minus ) )
208 // Right justification
209 for( ; status->current + 1 < status->width; status->current++)
216 // Left justification
220 for( ; status->current < status->width; status->current++)
227 const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status )
229 const char * orig_spec = spec;
230 if ( *(++spec) == '%' )
232 /* %% -> print single '%' */
236 /* Initializing status structure */
243 /* First come 0..n flags */
249 /* left-aligned output */
250 status->flags |= E_minus;
254 /* positive numbers prefixed with '+' */
255 status->flags |= E_plus;
259 /* alternative format (leading 0x for hex, 0 for octal) */
260 status->flags |= E_alt;
264 /* positive numbers prefixed with ' ' */
265 status->flags |= E_space;
269 /* right-aligned padding done with '0' instead of ' ' */
270 status->flags |= E_zero;
274 /* not a flag, exit flag parsing */
275 status->flags |= E_done;
278 } while ( ! ( status->flags & E_done ) );
280 /* Optional field width */
283 /* Retrieve width value from argument stack */
284 int width = va_arg( status->arg, int );
287 status->flags |= E_minus;
288 status->width = abs( width );
292 status->width = width;
298 /* If a width is given, strtol() will return its value. If not given,
299 strtol() will return zero. In both cases, endptr will point to the
300 rest of the conversion specifier - just what we need.
302 status->width = (int)strtol( spec, (char**)&spec, 10 );
305 /* Optional precision */
311 /* Retrieve precision value from argument stack. A negative value
312 is as if no precision is given - as precision is initalized to
313 EOF (negative), there is no need for testing for negative here.
315 status->prec = va_arg( status->arg, int );
320 status->prec = (int)strtol( spec, (char**) &spec, 10 );
322 /* Having a precision cancels out any zero flag. */
323 status->flags &= ~E_zero;
326 /* Optional length modifier
327 We step one character ahead in any case, and step back only if we find
328 there has been no length modifier (or step ahead another character if it
329 has been "hh" or "ll").
337 status->flags |= E_char;
343 status->flags |= E_short;
349 /* ll -> long long */
350 status->flags |= E_llong;
356 status->flags |= E_long;
360 /* j -> intmax_t, which might or might not be long long */
361 status->flags |= E_intmax;
364 /* z -> size_t, which might or might not be unsigned int */
365 status->flags |= E_size;
368 /* t -> ptrdiff_t, which might or might not be long */
369 status->flags |= E_ptrdiff;
372 /* L -> long double */
373 status->flags |= E_ldouble;
380 /* Conversion specifier */
390 status->flags |= E_unsigned;
394 status->flags |= E_unsigned;
398 status->flags |= ( E_lower | E_unsigned );
402 status->flags |= E_unsigned;
415 /* TODO: wide chars. */
416 printchar( va_arg( status->arg, int ), status );
419 /* TODO: wide chars. */
421 char * s = va_arg( status->arg, char * );
422 printstr( s, status );
426 /* TODO: E_long -> E_intptr */
428 status->flags |= ( E_lower | E_unsigned | E_alt | E_long );
432 int * val = va_arg( status->arg, int * );
437 /* No conversion specifier. Bad conversion. */
441 /* Do the actual output based on our findings */
442 if ( status->base != 0 )
444 /* Integer conversions */
445 /* TODO: Check for invalid flag combinations. */
446 if ( status->flags & E_unsigned )
449 switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_size ) )
452 value = (uintmax_t)(unsigned char)va_arg( status->arg, int );
455 value = (uintmax_t)(unsigned short)va_arg( status->arg, int );
458 value = (uintmax_t)va_arg( status->arg, unsigned int );
461 value = (uintmax_t)va_arg( status->arg, unsigned long );
464 value = (uintmax_t)va_arg( status->arg, unsigned long long );
467 value = (uintmax_t)va_arg( status->arg, size_t );
470 int2base( value, status );
474 switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_intmax ) )
477 int2base( (intmax_t)(char)va_arg( status->arg, int ), status );
480 int2base( (intmax_t)(short)va_arg( status->arg, int ), status );
483 int2base( (intmax_t)va_arg( status->arg, int ), status );
486 int2base( (intmax_t)va_arg( status->arg, long ), status );
489 int2base( (intmax_t)va_arg( status->arg, long long ), status );
492 int2base( (intmax_t)va_arg( status->arg, ptrdiff_t ), status );
495 int2base( va_arg( status->arg, intmax_t ), status );
499 if ( status->flags & E_minus )
501 while ( status->current < status->width )
507 if ( status->i >= status->n && status->n > 0 )
509 status->s[status->n - 1] = '\0';
518 #define _PDCLIB_FILEID "_PDCLIB/print.c"
519 #define _PDCLIB_STRINGIO
521 #include <_PDCLIB_test.h>
524 static int testprintf( char * buffer, const char * format, ... )
526 /* Members: base, flags, n, i, current, s, width, prec, stream, arg */
527 struct _PDCLIB_status_t status;
536 status.stream = NULL;
537 va_start( status.arg, format );
538 memset( buffer, '\0', 100 );
539 if ( *(_PDCLIB_print( format, &status )) != '\0' )
541 printf( "_PDCLIB_print() did not return end-of-specifier on '%s'.\n", format );
544 va_end( status.arg );
549 #define TEST_CONVERSION_ONLY
555 #include "printf_testcases.h"