7 /* These can be removed once integrated into PDCLIB make procedure */
9 #include </home/solar/src/pdclib/functions/_PDCLIB/digits.c>
10 #include </home/solar/src/pdclib/functions/_PDCLIB/Xdigits.c>
12 /* Using an integer's bits as flags for both the conversion flags and length
25 #define E_intmax 1<<10
27 #define E_ptrdiff 1<<12
28 #define E_double 1<<13
30 #define E_unsigned 1<<15
34 int base; /* base to which the value shall be converted */
35 int_fast32_t flags; /* flags and length modifiers */
36 size_t n; /* maximum number of characters to be written */
37 size_t i; /* number of characters already written */
38 size_t this; /* number of output chars in the current conversion */
39 char * s; /* target buffer */
40 size_t width; /* width of current field */
41 size_t prec; /* precision of current field */
42 FILE * stream;/* for to-stream output */
43 va_list ap; /* the argument stack passed to the printf function */
46 const char * parse_out( const char * spec, struct status_t * status );
47 inline void test( size_t n, const char * expect, ... );
48 int _PDCLIB_sprintf( char * buffer, size_t n, const char * format, va_list ap );
50 /* The following only for testing. */
57 puts( "- Signed min / max -\n" );
58 test( SIZE_MAX, "%hhd", CHAR_MIN );
59 test( SIZE_MAX, "%hhd", CHAR_MAX );
60 test( SIZE_MAX, "%hhd", 0 );
61 test( SIZE_MAX, "%hd", SHRT_MIN );
62 test( SIZE_MAX, "%hd", SHRT_MAX );
63 test( SIZE_MAX, "%hd", 0 );
64 test( SIZE_MAX, "%d", INT_MIN );
65 test( SIZE_MAX, "%d", INT_MAX );
66 test( SIZE_MAX, "%d", 0 );
67 test( SIZE_MAX, "%ld", LONG_MIN );
68 test( SIZE_MAX, "%ld", LONG_MAX );
69 test( SIZE_MAX, "%ld", 0l );
70 test( SIZE_MAX, "%lld", LLONG_MIN );
71 test( SIZE_MAX, "%lld", LLONG_MAX );
72 test( SIZE_MAX, "%lld", 0ll );
73 puts( "- Unsigned min / max -\n" );
74 test( SIZE_MAX, "%hhu", UCHAR_MAX );
75 test( SIZE_MAX, "%hhu", (unsigned char)-1 );
76 test( SIZE_MAX, "%hu", USHRT_MAX );
77 test( SIZE_MAX, "%hu", (unsigned short)-1 );
78 test( SIZE_MAX, "%u", UINT_MAX );
79 test( SIZE_MAX, "%u", -1u );
80 test( SIZE_MAX, "%lu", ULONG_MAX );
81 test( SIZE_MAX, "%lu", -1ul );
82 test( SIZE_MAX, "%llu", ULLONG_MAX );
83 test( SIZE_MAX, "%llu", -1ull );
84 puts( "- Hex and Octal, normal and alternative, upper and lowercase -\n" );
85 test( SIZE_MAX, "%X", UINT_MAX );
86 test( SIZE_MAX, "%#X", -1u );
87 test( SIZE_MAX, "%x", UINT_MAX );
88 test( SIZE_MAX, "%#x", -1u );
89 test( SIZE_MAX, "%o", UINT_MAX );
90 test( SIZE_MAX, "%#o", -1u );
91 puts( "- Plus flag -\n" );
92 test( SIZE_MAX, "%+d", INT_MIN );
93 test( SIZE_MAX, "%+d", INT_MAX );
94 test( SIZE_MAX, "%+d", 0 );
95 test( SIZE_MAX, "%+u", UINT_MAX );
96 test( SIZE_MAX, "%+u", -1u );
97 puts( "- Space flag -\n" );
98 test( SIZE_MAX, "% d", INT_MIN );
99 test( SIZE_MAX, "% d", INT_MAX );
100 test( SIZE_MAX, "% d", 0 );
101 test( SIZE_MAX, "% u", UINT_MAX );
102 test( SIZE_MAX, "% u", -1u );
103 puts( "- Field width -\n" );
104 test( SIZE_MAX, "%9d", INT_MIN );
105 test( SIZE_MAX, "%9d", INT_MAX );
106 test( SIZE_MAX, "%10d", INT_MIN );
107 test( SIZE_MAX, "%10d", INT_MAX );
108 test( SIZE_MAX, "%11d", INT_MIN );
109 test( SIZE_MAX, "%11d", INT_MAX );
110 test( SIZE_MAX, "%12d", INT_MIN );
111 test( SIZE_MAX, "%12d", INT_MAX );
112 puts( "- Field width (left bound) -\n" );
113 test( SIZE_MAX, "%-9d", INT_MIN );
114 test( SIZE_MAX, "%-9d", INT_MAX );
115 test( SIZE_MAX, "%-10d", INT_MIN );
116 test( SIZE_MAX, "%-10d", INT_MAX );
117 test( SIZE_MAX, "%-11d", INT_MIN );
118 test( SIZE_MAX, "%-11d", INT_MAX );
119 test( SIZE_MAX, "%-12d", INT_MIN );
120 test( SIZE_MAX, "%-12d", INT_MAX );
121 puts( "- Field width, zero padding -\n");
122 test( SIZE_MAX, "%09d", INT_MIN );
123 test( SIZE_MAX, "%09d", INT_MAX );
124 test( SIZE_MAX, "%010d", INT_MIN );
125 test( SIZE_MAX, "%010d", INT_MAX );
126 test( SIZE_MAX, "%011d", INT_MIN );
127 test( SIZE_MAX, "%011d", INT_MAX );
128 test( SIZE_MAX, "%012d", INT_MIN );
129 test( SIZE_MAX, "%012d", INT_MAX );
130 puts( "- Field width, zero padding (left bound) -\n" );
131 test( SIZE_MAX, "%-09d", INT_MIN );
132 test( SIZE_MAX, "%-09d", INT_MAX );
133 test( SIZE_MAX, "%-010d", INT_MIN );
134 test( SIZE_MAX, "%-010d", INT_MAX );
135 test( SIZE_MAX, "%-011d", INT_MIN );
136 test( SIZE_MAX, "%-011d", INT_MAX );
137 test( SIZE_MAX, "%-012d", INT_MIN );
138 test( SIZE_MAX, "%-012d", INT_MAX );
139 puts( "- Limited n -\n" );
140 test( 8, "%9d", INT_MAX );
141 test( 8, "%9d", INT_MIN );
142 test( 9, "%9d", INT_MAX );
143 test( 9, "%9d", INT_MIN );
144 test( 10, "%9d", INT_MAX );
145 test( 10, "%9d", INT_MIN );
146 test( 9, "%10d", INT_MAX );
147 test( 9, "%10d", INT_MIN );
148 test( 10, "%10d", INT_MAX );
149 test( 10, "%10d", INT_MIN );
150 test( 11, "%10d", INT_MAX );
151 test( 11, "%10d", INT_MIN );
152 test( 10, "%11d", INT_MAX );
153 test( 10, "%11d", INT_MIN );
154 test( 11, "%11d", INT_MAX );
155 test( 11, "%11d", INT_MIN );
156 test( 12, "%11d", INT_MAX );
157 test( 12, "%11d", INT_MIN );
158 test( 11, "%12d", INT_MAX );
159 test( 11, "%12d", INT_MIN );
160 test( 12, "%12d", INT_MAX );
161 test( 12, "%12d", INT_MIN );
162 test( 13, "%12d", INT_MAX );
163 test( 13, "%12d", INT_MIN );
164 puts( "- Precision (tbd) -\n" );
166 const char * format = "%030.20d";
168 rc = printf( format, INT_MAX );
169 printf( "', RC %d\n", rc );
170 test( SIZE_MAX, format, INT_MAX );
172 puts( "- vanilla -" );
173 printf( "No width, no precision: %#x\n", 42 );
174 printf( "Width, no precision: %#6x\n", 42 );
175 printf( "No width, precision: %#.6x\n", 42 );
176 printf( "Big width, small precision: %#6.3x\n", 42 );
177 printf( "Small width, big precision: %#3.6x\n", 42 );
178 printf( "No width, no precision: %#d\n", 42 );
179 printf( "Width, no precision: %#6d\n", 42 );
180 printf( "No width, precision: %#.6d\n", 42 );
181 printf( "Big width, small precision: %#6.3d\n", 42 );
182 printf( "Small width, big precision: %#3.6d\n", 42 );
183 puts( "- zero flag -" );
184 printf( "No width, no precision: %#0x\n", 42 );
185 printf( "Width, no precision: %#06x\n", 42 );
186 printf( "No width, precision: %#0.6x\n", 42 );
187 printf( "Big width, small precision: %#06.3x\n", 42 );
188 printf( "Small width, big precision: %#03.6x\n", 42 );
189 printf( "No width, no precision: %#0d\n", 42 );
190 printf( "Width, no precision: %#06d\n", 42 );
191 printf( "No width, precision: %#0.6d\n", 42 );
192 printf( "Big width, small precision: %#06.3d\n", 42 );
193 printf( "Small width, big precision: %#03.6d\n", 42 );
194 puts( "- plus flag -" );
195 printf( "No width, no precision: %#+d\n", 42 );
196 printf( "Width, no precision: %#+6d\n", 42 );
197 printf( "No width, precision: %#+.6d\n", 42 );
198 printf( "Big width, small precision: %#+6.3d\n", 42 );
199 printf( "Small width, big precision: %#+3.6d\n", 42 );
200 puts( "- plus and zero flag -" );
201 printf( "No width, no precision: %#+0d\n", 42 );
202 printf( "Width, no precision: %#+06d\n", 42 );
203 printf( "No width, precision: %#+0.6d\n", 42 );
204 printf( "Big width, small precision: %#+06.3d\n", 42 );
205 printf( "Small width, big precision: %#+03.6d\n", 42 );
209 /* This macro delivers a given character to either a memory buffer or a stream,
210 depending on the contents of 'status' (struct status_t).
211 x - the character to be delivered
212 i - pointer to number of characters already delivered in this call
213 n - pointer to maximum number of characters to be delivered in this call
214 s - the buffer into which the character shall be delivered
217 #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 )
219 /* This function recursively converts a given integer value to a given base
220 into a character string. Persistent information - like the number of digits
221 parsed so far - is recorded in a struct status_t, which allows to avoid
222 overwriting snprintf() limits, and enables the function to do the necessary
223 padding / prefixing of the character string eventually printed.
225 static void int2base( intmax_t value, struct status_t * status )
227 /* Registering the character being printed at the end of the function here
228 already so it will be taken into account when the deepestmost recursion
229 does the prefix / padding stuff.
232 if ( ( value / status->base ) != 0 )
234 /* More digits to be done - recurse deeper */
235 int2base( value / status->base, status );
239 /* We reached the last digit, the deepest point of our recursion, and
240 only now know how long the number to be printed actually is. Now we
241 have to do the sign, prefix, width, and precision padding stuff
242 before printing the numbers while we resurface from the recursion.
244 /* At worst, we need two prefix characters (hex prefix). */
245 char preface[3] = "\0";
247 if ( ( status->flags & E_alt ) && ( status->base == 16 || status->base == 8 ) )
249 /* Octal / hexadecimal prefix for "%#" conversions */
250 preface[ preidx++ ] = '0'; /* TODO: For octal, standard states "extend the precision" */
251 if ( status->base == 16 )
253 preface[ preidx++ ] = ( status->flags & E_lower ) ? 'x' : 'X';
258 /* Negative sign for negative values - at all times. */
259 preface[ preidx++ ] = '-';
261 else if ( ! ( status->flags & E_unsigned ) )
263 /* plus sign / extra space are only for unsigned conversions */
264 if ( status->flags & E_plus )
266 preface[ preidx++ ] = '+';
268 else if ( status->flags & E_space )
270 preface[ preidx++ ] = ' ';
273 if ( ! ( status->flags & ( E_minus | E_zero ) ) )
275 /* Space padding is only done if no zero padding or left alignment
276 is requested. Leave space for any prefixes determined above.
278 /* The number of characters to be printed, plus prefixes if any. */
279 /* This line contained probably the most stupid, time-wasting bug
280 I've ever perpetrated. Greetings to Samface, DevL, and all
281 sceners at Breakpoint 2006.
283 size_t characters = preidx + ( ( status->this > status->prec ) ? status->this : status->prec );
284 if ( status->width > characters )
286 for ( int i = 0; i < status->width - characters; ++i )
289 ++(status->this); /* TODO: Probably have to do something so I still know how many zeroes are required, later. */
293 /* Now we did the padding, do the prefixes (if any). */
295 while ( preface[ preidx ] != '\0' )
297 DELIVER( preface[ preidx++ ] );
300 if ( ( ! ( status->flags & E_minus ) ) && ( status->flags & E_zero ) )
302 /* If field is not left aligned, and zero padding is requested, do
303 so. TODO: This should include precision handling (probably).
305 while ( status->this < status->width )
312 /* Recursion tail - print the current digit. */
314 int digit = value % status->base;
319 if ( status->flags & E_lower )
321 /* Lowercase letters. Same array used for strto...(). */
322 DELIVER( _PDCLIB_digits[ digit ] );
326 /* Uppercase letters. Array only used here, only 0-F. */
327 DELIVER( _PDCLIB_Xdigits[ digit ] );
332 /* This function is to be called with spec pointing to the leading '%' of a
333 printf() conversion specifier, with ap being
335 const char * parse_out( const char * spec, struct status_t * status )
337 const char * orig_spec = spec;
338 if ( *(++spec) == '%' )
343 /* Initializing status structure */
350 /* First come 0..n flags */
356 status->flags |= E_minus;
360 status->flags |= E_plus;
364 status->flags |= E_alt;
368 status->flags |= E_space;
372 status->flags |= E_zero;
376 status->flags |= E_done;
379 } while ( ! ( status->flags & E_done ) );
381 /* Optional field width */
384 /* Retrieve width value from argument stack */
385 if ( ( status->width = va_arg( status->ap, int ) ) < 0 )
387 /* Negative value is '-' flag plus absolute value */
388 status->flags |= E_minus;
395 /* If a width is given, strtol() will return its value. If not given,
396 strtol() will return zero. In both cases, endptr will point to the
397 rest of the conversion specifier - just what we need.
399 status->width = (int)strtol( spec, (char**)&spec, 10 );
402 /* Optional precision */
408 /* Retrieve precision value from argument stack. A negative value
409 is as if no precision is given - as precision is initalized to
410 EOF (negative), there is no need for testing for negative here.
412 status->prec = va_arg( status->ap, int );
417 status->prec = (int)strtol( spec, &endptr, 10 );
418 if ( spec == endptr )
420 /* Decimal point but no number - bad conversion specifier. */
425 status->flags &= ! E_zero;
428 /* Optional length modifier
429 We step one character ahead in any case, and step back only if we find
430 there has been no length modifier (or step ahead another character if it
431 has been "hh" or "ll").
438 status->flags |= E_char;
443 status->flags |= E_short;
449 status->flags |= E_llong;
454 status->flags |= E_long;
458 status->flags |= E_intmax;
461 status->flags |= E_size;
464 status->flags |= E_ptrdiff;
467 status->flags |= E_double;
474 /* Conversion specifier */
483 status->flags |= E_unsigned;
487 status->flags |= E_unsigned;
491 status->flags |= ( E_lower | E_unsigned );
495 status->flags |= E_unsigned;
512 /* uint2base( 16, (intptr_t)value, true ) */
516 /* No conversion specifier. Bad conversion. */
520 /* Do the actual output based on our findings */
521 if ( status->base != 0 )
523 /* Integer conversions */
524 /* TODO: Check for invalid flag combinations. */
525 if ( status->flags & E_unsigned )
528 switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_size ) )
531 value = (uintmax_t)(unsigned char)va_arg( status->ap, int );
534 value = (uintmax_t)(unsigned short)va_arg( status->ap, int );
537 value = (uintmax_t)va_arg( status->ap, unsigned int );
540 value = (uintmax_t)va_arg( status->ap, unsigned long );
543 value = (uintmax_t)va_arg( status->ap, unsigned long long );
546 value = (uintmax_t)va_arg( status->ap, size_t );
550 if ( ( value / status->base ) != 0 )
552 int2base( (intmax_t)(value / status->base), status );
554 int digit = value % status->base;
559 if ( status->flags & E_lower )
561 DELIVER( _PDCLIB_digits[ digit ] );
565 DELIVER( _PDCLIB_Xdigits[ digit ] );
570 switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_intmax ) )
573 int2base( (intmax_t)(char)va_arg( status->ap, int ), status );
576 int2base( (intmax_t)(short)va_arg( status->ap, int ), status );
579 int2base( (intmax_t)va_arg( status->ap, int ), status );
582 int2base( (intmax_t)va_arg( status->ap, long ), status );
585 int2base( (intmax_t)va_arg( status->ap, long long ), status );
588 int2base( (intmax_t)va_arg( status->ap, ptrdiff_t ), status );
591 int2base( va_arg( status->ap, intmax_t ), status );
595 if ( status->flags & E_minus )
597 while ( status->this < status->width )
603 if ( status->i >= status->n )
605 status->s[status->n - 1] = '\0';
611 inline void test( size_t n, const char * expect, ... )
613 char * buffer1 = malloc( 50 );
614 char * buffer2 = malloc( 50 );
618 va_start( ap, expect );
619 myrc = _PDCLIB_sprintf( buffer1, n, expect, ap );
620 rc = vsnprintf( buffer2, n, expect, ap );
621 if ( ( strcmp( buffer1, buffer2 ) != 0 ) || ( myrc != rc ) )
623 printf( "Output '%s', RC %d\nExpect '%s', RC %d\n\n", buffer1, myrc, buffer2, rc );
629 int _PDCLIB_sprintf( char * buffer, size_t n, const char * format, va_list ap )
631 struct status_t status = { 0, 0, n, 0, 0, buffer, 0, 0, NULL, ap };
632 while ( *format != '\0' )
635 if ( ( *format != '%' ) || ( ( rc = parse_out( format, &status ) ) == format ) )
637 /* No conversion specifier, print verbatim */
638 buffer[ status.i++ ] = *(format++);
642 /* Continue parsing after conversion specifier */
646 buffer[ status.i ] = '\0';
651 int _PDCLIB_fprintf( FILE * stream, const char * format, va_list ap )
653 struct status_t status = { 0, 0, SIZE_MAX, 0, 0, NULL, 0, 0, stream, ap };
654 while ( *format != '\0' )
657 if ( ( *format != '%' ) || ( ( rc = parse_out( format, &status, ap ) ) == format ) )
659 /* No conversion specifier, print verbatim */
660 putc( *(format++), stream );
664 /* Continue parsing after conversion specifier */