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.
15 extern char * _PDCLIB_Xdigits;
17 /* Using an integer's bits as flags for both the conversion flags and length
30 #define E_intmax 1<<10
32 #define E_ptrdiff 1<<12
33 #define E_intptr 1<<13
34 #define E_double 1<<14
36 #define E_unsigned 1<<16
38 /* This macro delivers a given character to either a memory buffer or a stream,
39 depending on the contents of 'status' (struct _PDCLIB_status_t).
40 x - the character to be delivered
41 i - pointer to number of characters already delivered in this call
42 n - pointer to maximum number of characters to be delivered in this call
43 s - the buffer into which the character shall be delivered
45 #define DELIVER( x ) do { if ( status->i < status->n ) { if ( status->stream != NULL ) putc( x, status->stream ); else status->s[status->i] = x; } ++(status->i); } while ( 0 )
47 /* This function recursively converts a given integer value to a given base
48 into a character string. Persistent information - like the number of digits
49 parsed so far - is recorded in a struct _PDCLIB_status_t, which allows to
50 avoid overwriting snprintf() limits, and enables the function to do the
51 necessary padding / prefixing of the character string eventually printed.
53 static void int2base( intmax_t value, struct _PDCLIB_status_t * status )
55 /* Registering the character being printed at the end of the function here
56 already so it will be taken into account when the deepestmost recursion
57 does the prefix / padding stuff.
60 if ( ( value / status->base ) != 0 )
62 /* More digits to be done - recurse deeper */
63 int2base( value / status->base, status );
67 /* We reached the last digit, the deepest point of our recursion, and
68 only now know how long the number to be printed actually is. Now we
69 have to do the sign, prefix, width, and precision padding stuff
70 before printing the numbers while we resurface from the recursion.
72 /* At worst, we need two prefix characters (hex prefix). */
73 char preface[3] = "\0";
75 if ( ( status->flags & E_alt ) && ( status->base == 16 || status->base == 8 ) )
77 /* Octal / hexadecimal prefix for "%#" conversions */
78 preface[ preidx++ ] = '0';
79 if ( status->base == 16 )
81 preface[ preidx++ ] = ( status->flags & E_lower ) ? 'x' : 'X';
86 /* Negative sign for negative values - at all times. */
87 preface[ preidx++ ] = '-';
89 else if ( ! ( status->flags & E_unsigned ) )
91 /* plus sign / extra space are only for unsigned conversions */
92 if ( status->flags & E_plus )
94 preface[ preidx++ ] = '+';
96 else if ( status->flags & E_space )
98 preface[ preidx++ ] = ' ';
102 size_t prec_pads = ( status->prec > status->this ) ? ( status->prec - status->this ) : 0;
103 if ( ! ( status->flags & ( E_minus | E_zero ) ) )
105 /* Space padding is only done if no zero padding or left alignment
106 is requested. Leave space for 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->this > status->prec ) ? status->this : status->prec );
114 if ( status->width > characters )
116 for ( int i = 0; i < status->width - characters; ++i )
123 /* Now we did the padding, do the prefixes (if any). */
125 while ( preface[ preidx ] != '\0' )
127 DELIVER( preface[ preidx++ ] );
130 if ( ( ! ( status->flags & E_minus ) ) && ( status->flags & E_zero ) )
132 /* If field is not left aligned, and zero padding is requested, do
135 while ( status->this < status->width )
141 /* Do the precision padding if necessary. */
142 for ( int i = 0; i < prec_pads; ++i )
148 /* Recursion tail - print the current digit. */
150 int digit = value % status->base;
155 if ( status->flags & E_lower )
157 /* Lowercase letters. Same array used for strto...(). */
158 DELIVER( _PDCLIB_digits[ digit ] );
162 /* Uppercase letters. Array only used here, only 0-F. */
163 DELIVER( _PDCLIB_Xdigits[ digit ] );
168 /* This function is to be called with spec pointing to the leading '%' of a
169 printf() conversion specifier.
171 const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status )
173 const char * orig_spec = spec;
174 if ( *(++spec) == '%' )
179 /* Initializing status structure */
186 /* First come 0..n flags */
192 status->flags |= E_minus;
196 status->flags |= E_plus;
200 status->flags |= E_alt;
204 status->flags |= E_space;
208 status->flags |= E_zero;
212 status->flags |= E_done;
215 } while ( ! ( status->flags & E_done ) );
217 /* Optional field width */
220 /* Retrieve width value from argument stack */
221 if ( ( status->width = va_arg( status->arg, int ) ) < 0 )
223 /* Negative value is '-' flag plus absolute value */
224 status->flags |= E_minus;
231 /* If a width is given, strtol() will return its value. If not given,
232 strtol() will return zero. In both cases, endptr will point to the
233 rest of the conversion specifier - just what we need.
235 status->width = (int)strtol( spec, (char**)&spec, 10 );
238 /* Optional precision */
244 /* Retrieve precision value from argument stack. A negative value
245 is as if no precision is given - as precision is initalized to
246 EOF (negative), there is no need for testing for negative here.
248 status->prec = va_arg( status->arg, int );
253 status->prec = (int)strtol( spec, &endptr, 10 );
254 if ( spec == endptr )
256 /* Decimal point but no number - bad conversion specifier. */
261 /* Having a precision cancels out any zero flag. */
262 status->flags ^= E_zero;
265 /* Optional length modifier
266 We step one character ahead in any case, and step back only if we find
267 there has been no length modifier (or step ahead another character if it
268 has been "hh" or "ll").
275 status->flags |= E_char;
280 status->flags |= E_short;
286 status->flags |= E_llong;
291 status->flags |= E_long;
295 status->flags |= E_intmax;
298 status->flags |= E_size;
301 status->flags |= E_ptrdiff;
304 status->flags |= E_double;
311 /* Conversion specifier */
321 status->flags |= E_unsigned;
325 status->flags |= E_unsigned;
329 status->flags |= ( E_lower | E_unsigned );
333 status->flags |= E_unsigned;
346 /* TODO: Flags, wide chars. */
347 DELIVER( va_arg( status->arg, int ) );
350 /* TODO: Flags, wide chars. */
352 char * s = va_arg( status->arg, char * );
360 /* TODO: E_long -> E_intptr */
362 status->flags |= ( E_lower | E_unsigned | E_alt | E_long );
366 int * val = va_arg( status->arg, int * );
371 /* No conversion specifier. Bad conversion. */
375 /* Do the actual output based on our findings */
376 if ( status->base != 0 )
378 /* Integer conversions */
379 /* TODO: Check for invalid flag combinations. */
380 if ( status->flags & E_unsigned )
383 switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_size ) )
386 value = (uintmax_t)(unsigned char)va_arg( status->arg, int );
389 value = (uintmax_t)(unsigned short)va_arg( status->arg, int );
392 value = (uintmax_t)va_arg( status->arg, unsigned int );
395 value = (uintmax_t)va_arg( status->arg, unsigned long );
398 value = (uintmax_t)va_arg( status->arg, unsigned long long );
401 value = (uintmax_t)va_arg( status->arg, size_t );
405 if ( ( value / status->base ) != 0 )
407 int2base( (intmax_t)(value / status->base), status );
409 int digit = value % status->base;
414 if ( status->flags & E_lower )
416 DELIVER( _PDCLIB_digits[ digit ] );
420 DELIVER( _PDCLIB_Xdigits[ digit ] );
425 switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_intmax ) )
428 int2base( (intmax_t)(char)va_arg( status->arg, int ), status );
431 int2base( (intmax_t)(short)va_arg( status->arg, int ), status );
434 int2base( (intmax_t)va_arg( status->arg, int ), status );
437 int2base( (intmax_t)va_arg( status->arg, long ), status );
440 int2base( (intmax_t)va_arg( status->arg, long long ), status );
443 int2base( (intmax_t)va_arg( status->arg, ptrdiff_t ), status );
446 int2base( va_arg( status->arg, intmax_t ), status );
450 if ( status->flags & E_minus )
452 while ( status->this < status->width )
458 if ( status->i >= status->n )
460 status->s[status->n - 1] = '\0';
467 #include <_PDCLIB_test.h>
471 TESTCASE( NO_TESTDRIVER );