1 /* _PDCLIB_print( const char *, struct _PDCLIB_status_t * )
3 This file is part of the Public Domain C Library (PDCLib).
4 Permission is granted to use, modify, and / or redistribute at will.
15 /* Using an integer's bits as flags for both the conversion flags and length
18 /* FIXME: one too many flags to work on a 16-bit machine, join some (e.g. the
19 width flags) into a combined field.
21 #define E_minus (1<<0)
24 #define E_space (1<<3)
29 #define E_short (1<<7)
31 #define E_llong (1<<9)
32 #define E_intmax (1<<10)
33 #define E_size (1<<11)
34 #define E_ptrdiff (1<<12)
35 #define E_pointer (1<<13)
37 #define E_ldouble (1<<14)
39 #define E_lower (1<<15)
40 #define E_unsigned (1<<16)
42 /* This macro delivers a given character to either a memory buffer or a stream,
43 depending on the contents of 'status' (struct _PDCLIB_status_t).
44 x - the character to be delivered
45 i - pointer to number of characters already delivered in this call
46 n - pointer to maximum number of characters to be delivered in this call
47 s - the buffer into which the character shall be delivered
52 if ( status->i < status->n ) { \
53 if ( status->stream != NULL ) \
54 putc( character, status->stream ); \
56 status->s[status->i] = character; \
62 static void intformat( intmax_t value, struct _PDCLIB_status_t * status )
64 if ( status->prec < 0 )
68 /* At worst, we need two prefix characters (hex prefix). */
69 char preface[3] = "\0";
71 if ( ( status->flags & E_alt ) && ( status->base == 16 || status->base == 8 ) && ( value != 0 ) )
73 /* Octal / hexadecimal prefix for "%#" conversions */
74 preface[ preidx++ ] = '0';
75 if ( status->base == 16 )
77 preface[ preidx++ ] = ( status->flags & E_lower ) ? 'x' : 'X';
82 /* Negative sign for negative values - at all times. */
83 preface[ preidx++ ] = '-';
85 else if ( ! ( status->flags & E_unsigned ) )
87 /* plus sign / extra space are only for unsigned conversions */
88 if ( status->flags & E_plus )
90 preface[ preidx++ ] = '+';
92 else if ( status->flags & E_space )
94 preface[ preidx++ ] = ' ';
98 /* At this point, status->current has the number of digits queued up.
99 Determine if we have a precision requirement to pad those.
101 size_t prec_pads = ( (_PDCLIB_size_t)status->prec > status->current ) ? ( (_PDCLIB_size_t)status->prec - status->current ) : 0;
102 if ( ! ( status->flags & ( E_minus | E_zero ) ) )
104 /* Space padding is only done if no zero padding or left alignment
105 is requested. Calculate the number of characters that WILL be
106 printed, including any prefixes determined above.
108 /* The number of characters to be printed, plus prefixes if any. */
109 /* This line contained probably the most stupid, time-wasting bug
110 I've ever perpetrated. Greetings to Samface, DevL, and all
111 sceners at Breakpoint 2006.
113 size_t characters = preidx + ( ( status->current > (_PDCLIB_size_t)status->prec ) ? status->current : (_PDCLIB_size_t)status->prec );
114 if ( status->width > characters )
116 for ( size_t i = 0; i < status->width - characters; ++i )
123 /* Now we did the padding, do the prefixes (if any). */
125 while ( preface[ preidx ] != '\0' )
127 PUT( preface[ preidx++ ] );
130 /* Do the precision padding if necessary. */
131 while ( prec_pads-- > 0 )
136 if ( ( ! ( status->flags & E_minus ) ) && ( status->flags & E_zero ) )
138 /* If field is not left aligned, and zero padding is requested, do
141 while ( status->current < status->width )
151 /* This function recursively converts a given integer value to a character
152 stream. The conversion is done under the control of a given status struct
153 and written either to a character string or a stream, depending on that
154 same status struct. The status struct also keeps the function from exceeding
155 snprintf() limits, and enables any necessary padding / prefixing of the
156 output once the number of characters to be printed is known, which happens
157 at the lowermost recursion level.
159 static void int2base( intmax_t value, struct _PDCLIB_status_t * status )
161 /* Registering the character being printed at the end of the function here
162 already so it will be taken into account when the deepestmost recursion
163 does the prefix / padding stuff.
166 if ( ( value / status->base ) != 0 )
168 /* More digits to be done - recurse deeper */
169 int2base( value / status->base, status );
173 /* We reached the last digit, the deepest point of our recursion, and
174 only now know how long the number to be printed actually is. Now we
175 have to do the sign, prefix, width, and precision padding stuff
176 before printing the numbers while we resurface from the recursion.
178 intformat( value, status );
180 /* Recursion tail - print the current digit. */
182 int digit = value % status->base;
187 if ( status->flags & E_lower )
189 /* Lowercase letters. Same array used for strto...(). */
190 PUT( _PDCLIB_digits[ digit ] );
194 /* Uppercase letters. Array only used here, only 0-F. */
195 PUT( _PDCLIB_Xdigits[ digit ] );
201 const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status )
203 const char * orig_spec = spec;
204 if ( *(++spec) == '%' )
206 /* %% -> print single '%' */
210 /* Initializing status structure */
217 /* First come 0..n flags */
223 /* left-aligned output */
224 status->flags |= E_minus;
228 /* positive numbers prefixed with '+' */
229 status->flags |= E_plus;
233 /* alternative format (leading 0x for hex, 0 for octal) */
234 status->flags |= E_alt;
238 /* positive numbers prefixed with ' ' */
239 status->flags |= E_space;
243 /* right-aligned padding done with '0' instead of ' ' */
244 status->flags |= E_zero;
248 /* not a flag, exit flag parsing */
249 status->flags |= E_done;
252 } while ( ! ( status->flags & E_done ) );
254 /* Optional field width */
257 /* Retrieve width value from argument stack */
258 int width = va_arg( status->arg, int );
261 status->flags |= E_minus;
262 status->width = abs( width );
266 status->width = width;
272 /* If a width is given, strtol() will return its value. If not given,
273 strtol() will return zero. In both cases, endptr will point to the
274 rest of the conversion specifier - just what we need.
276 status->width = (int)strtol( spec, (char**)&spec, 10 );
279 /* Optional precision */
285 /* Retrieve precision value from argument stack. A negative value
286 is as if no precision is given - as precision is initalized to
287 EOF (negative), there is no need for testing for negative here.
289 status->prec = va_arg( status->arg, int );
295 status->prec = (int)strtol( spec, &endptr, 10 );
296 if ( spec == endptr )
298 /* Decimal point but no number - equals zero */
303 /* Having a precision cancels out any zero flag. */
304 status->flags &= ~E_zero;
307 /* Optional length modifier
308 We step one character ahead in any case, and step back only if we find
309 there has been no length modifier (or step ahead another character if it
310 has been "hh" or "ll").
318 status->flags |= E_char;
324 status->flags |= E_short;
330 /* ll -> long long */
331 status->flags |= E_llong;
337 status->flags |= E_long;
341 /* j -> intmax_t, which might or might not be long long */
342 status->flags |= E_intmax;
345 /* z -> size_t, which might or might not be unsigned int */
346 status->flags |= E_size;
349 /* t -> ptrdiff_t, which might or might not be long */
350 status->flags |= E_ptrdiff;
353 /* L -> long double */
354 status->flags |= E_ldouble;
361 /* Conversion specifier */
371 status->flags |= E_unsigned;
375 status->flags |= E_unsigned;
379 status->flags |= ( E_lower | E_unsigned );
383 status->flags |= E_unsigned;
396 /* TODO: Flags, wide chars. */
397 PUT( va_arg( status->arg, int ) );
400 /* TODO: Flags, wide chars. */
402 char * s = va_arg( status->arg, char * );
410 /* TODO: E_long -> E_intptr */
412 status->flags |= ( E_lower | E_unsigned | E_alt | E_pointer );
416 int * val = va_arg( status->arg, int * );
421 /* No conversion specifier. Bad conversion. */
425 /* Do the actual output based on our findings */
426 if ( status->base != 0 )
428 /* Integer conversions */
429 /* TODO: Check for invalid flag combinations. */
430 if ( status->flags & E_unsigned )
433 switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_size | E_pointer ) )
436 value = (uintmax_t)(unsigned char)va_arg( status->arg, int );
439 value = (uintmax_t)(unsigned short)va_arg( status->arg, int );
442 value = (uintmax_t)va_arg( status->arg, unsigned int );
445 value = (uintmax_t)va_arg( status->arg, unsigned long );
448 value = (uintmax_t)va_arg( status->arg, unsigned long long );
451 value = (uintmax_t)va_arg( status->arg, size_t );
454 value = (uintmax_t)(uintptr_t)va_arg( status->arg, void * );
457 puts( "UNSUPPORTED PRINTF FLAG COMBINATION" );
461 if ( ( value / status->base ) != 0 )
463 /* Get value to "safe" levels re. uintmax_t. */
464 int2base( (intmax_t)(value / status->base), status );
468 intformat( (intmax_t)value, status );
470 int digit = value % status->base;
475 if ( status->flags & E_lower )
477 PUT( _PDCLIB_digits[ digit ] );
481 PUT( _PDCLIB_Xdigits[ digit ] );
486 switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_intmax ) )
489 int2base( (intmax_t)(char)va_arg( status->arg, int ), status );
492 int2base( (intmax_t)(short)va_arg( status->arg, int ), status );
495 int2base( (intmax_t)va_arg( status->arg, int ), status );
498 int2base( (intmax_t)va_arg( status->arg, long ), status );
501 int2base( (intmax_t)va_arg( status->arg, long long ), status );
504 int2base( (intmax_t)va_arg( status->arg, ptrdiff_t ), status );
507 int2base( va_arg( status->arg, intmax_t ), status );
510 puts( "UNSUPPORTED PRINTF FLAG COMBINATION" );
514 if ( status->flags & E_minus )
516 while ( status->current < status->width )
522 if ( status->i >= status->n && status->n > 0 )
524 status->s[status->n - 1] = '\0';
533 #define _PDCLIB_FILEID "_PDCLIB/print.c"
534 #define _PDCLIB_STRINGIO
536 #include "_PDCLIB_test.h"
540 static int testprintf( char * buffer, const char * format, ... )
542 /* Members: base, flags, n, i, current, s, width, prec, stream, arg */
543 struct _PDCLIB_status_t status;
552 status.stream = NULL;
553 va_start( status.arg, format );
554 memset( buffer, '\0', 100 );
555 if ( *(_PDCLIB_print( format, &status )) != '\0' )
557 printf( "_PDCLIB_print() did not return end-of-specifier on '%s'.\n", format );
560 va_end( status.arg );
566 #define TEST_CONVERSION_ONLY
572 #include "printf_testcases.h"