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. */
56 puts( "- Signed min / max -\n" );
57 test( SIZE_MAX, "%hhd", CHAR_MIN );
58 test( SIZE_MAX, "%hhd", CHAR_MAX );
59 test( SIZE_MAX, "%hhd", 0 );
60 test( SIZE_MAX, "%hd", SHRT_MIN );
61 test( SIZE_MAX, "%hd", SHRT_MAX );
62 test( SIZE_MAX, "%hd", 0 );
63 test( SIZE_MAX, "%d", INT_MIN );
64 test( SIZE_MAX, "%d", INT_MAX );
65 test( SIZE_MAX, "%d", 0 );
66 test( SIZE_MAX, "%ld", LONG_MIN );
67 test( SIZE_MAX, "%ld", LONG_MAX );
68 test( SIZE_MAX, "%ld", 0l );
69 test( SIZE_MAX, "%lld", LLONG_MIN );
70 test( SIZE_MAX, "%lld", LLONG_MAX );
71 test( SIZE_MAX, "%lld", 0ll );
72 puts( "- Unsigned min / max -\n" );
73 test( SIZE_MAX, "%hhu", UCHAR_MAX );
74 test( SIZE_MAX, "%hhu", (unsigned char)-1 );
75 test( SIZE_MAX, "%hu", USHRT_MAX );
76 test( SIZE_MAX, "%hu", (unsigned short)-1 );
77 test( SIZE_MAX, "%u", UINT_MAX );
78 test( SIZE_MAX, "%u", -1u );
79 test( SIZE_MAX, "%lu", ULONG_MAX );
80 test( SIZE_MAX, "%lu", -1ul );
81 test( SIZE_MAX, "%llu", ULLONG_MAX );
82 test( SIZE_MAX, "%llu", -1ull );
83 puts( "- Hex and Octal, normal and alternative, upper and lowercase -\n" );
84 test( SIZE_MAX, "%X", UINT_MAX );
85 test( SIZE_MAX, "%#X", -1u );
86 test( SIZE_MAX, "%x", UINT_MAX );
87 test( SIZE_MAX, "%#x", -1u );
88 test( SIZE_MAX, "%o", UINT_MAX );
89 test( SIZE_MAX, "%#o", -1u );
90 puts( "- Plus flag -\n" );
91 test( SIZE_MAX, "%+d", INT_MIN );
92 test( SIZE_MAX, "%+d", INT_MAX );
93 test( SIZE_MAX, "%+d", 0 );
94 test( SIZE_MAX, "%+u", UINT_MAX );
95 test( SIZE_MAX, "%+u", -1u );
96 puts( "- Space flag -\n" );
97 test( SIZE_MAX, "% d", INT_MIN );
98 test( SIZE_MAX, "% d", INT_MAX );
99 test( SIZE_MAX, "% d", 0 );
100 test( SIZE_MAX, "% u", UINT_MAX );
101 test( SIZE_MAX, "% u", -1u );
102 puts( "- Field width -\n" );
103 test( SIZE_MAX, "%9d", INT_MIN );
104 test( SIZE_MAX, "%9d", INT_MAX );
105 test( SIZE_MAX, "%10d", INT_MIN );
106 test( SIZE_MAX, "%10d", INT_MAX );
107 test( SIZE_MAX, "%11d", INT_MIN );
108 test( SIZE_MAX, "%11d", INT_MAX );
109 test( SIZE_MAX, "%12d", INT_MIN );
110 test( SIZE_MAX, "%12d", INT_MAX );
111 puts( "- Field width (left bound) -\n" );
112 test( SIZE_MAX, "%-9d", INT_MIN );
113 test( SIZE_MAX, "%-9d", INT_MAX );
114 test( SIZE_MAX, "%-10d", INT_MIN );
115 test( SIZE_MAX, "%-10d", INT_MAX );
116 test( SIZE_MAX, "%-11d", INT_MIN );
117 test( SIZE_MAX, "%-11d", INT_MAX );
118 test( SIZE_MAX, "%-12d", INT_MIN );
119 test( SIZE_MAX, "%-12d", INT_MAX );
120 puts( "- Field width, zero padding -\n");
121 test( SIZE_MAX, "%09d", INT_MIN );
122 test( SIZE_MAX, "%09d", INT_MAX );
123 test( SIZE_MAX, "%010d", INT_MIN );
124 test( SIZE_MAX, "%010d", INT_MAX );
125 test( SIZE_MAX, "%011d", INT_MIN );
126 test( SIZE_MAX, "%011d", INT_MAX );
127 test( SIZE_MAX, "%012d", INT_MIN );
128 test( SIZE_MAX, "%012d", INT_MAX );
129 puts( "- Field width, zero padding (left bound) -\n" );
130 test( SIZE_MAX, "%-09d", INT_MIN );
131 test( SIZE_MAX, "%-09d", INT_MAX );
132 test( SIZE_MAX, "%-010d", INT_MIN );
133 test( SIZE_MAX, "%-010d", INT_MAX );
134 test( SIZE_MAX, "%-011d", INT_MIN );
135 test( SIZE_MAX, "%-011d", INT_MAX );
136 test( SIZE_MAX, "%-012d", INT_MIN );
137 test( SIZE_MAX, "%-012d", INT_MAX );
138 puts( "- Limited n -\n" );
139 test( 8, "%9d", INT_MAX );
140 test( 8, "%9d", INT_MIN );
141 test( 9, "%9d", INT_MAX );
142 test( 9, "%9d", INT_MIN );
143 test( 10, "%9d", INT_MAX );
144 test( 10, "%9d", INT_MIN );
145 test( 9, "%10d", INT_MAX );
146 test( 9, "%10d", INT_MIN );
147 test( 10, "%10d", INT_MAX );
148 test( 10, "%10d", INT_MIN );
149 test( 11, "%10d", INT_MAX );
150 test( 11, "%10d", INT_MIN );
151 test( 10, "%11d", INT_MAX );
152 test( 10, "%11d", INT_MIN );
153 test( 11, "%11d", INT_MAX );
154 test( 11, "%11d", INT_MIN );
155 test( 12, "%11d", INT_MAX );
156 test( 12, "%11d", INT_MIN );
157 test( 11, "%12d", INT_MAX );
158 test( 11, "%12d", INT_MIN );
159 test( 12, "%12d", INT_MAX );
160 test( 12, "%12d", INT_MIN );
161 test( 13, "%12d", INT_MAX );
162 test( 13, "%12d", INT_MIN );
163 puts( "- Precision -\n" );
164 test( SIZE_MAX, "%030.20d", INT_MAX );
165 test( SIZE_MAX, "%.6x", UINT_MAX );
166 test( SIZE_MAX, "%#6.3x", UINT_MAX );
167 test( SIZE_MAX, "%#3.6x", UINT_MAX );
168 test( SIZE_MAX, "%.6d", INT_MIN );
169 test( SIZE_MAX, "%6.3d", INT_MIN );
170 test( SIZE_MAX, "%3.6d", INT_MIN );
171 test( SIZE_MAX, "%#0.6x", UINT_MAX );
172 test( SIZE_MAX, "%#06.3x", UINT_MAX );
173 test( SIZE_MAX, "%#03.6x", UINT_MAX );
174 test( SIZE_MAX, "%#0.6d", INT_MAX );
175 test( SIZE_MAX, "%#06.3d", INT_MAX );
176 test( SIZE_MAX, "%#03.6d", INT_MAX );
177 test( SIZE_MAX, "%#+.6d", INT_MAX );
178 test( SIZE_MAX, "%#+6.3d", INT_MAX );
179 test( SIZE_MAX, "%#+3.6d", INT_MAX );
180 test( SIZE_MAX, "%+0.6d", INT_MAX );
181 test( SIZE_MAX, "%+06.3d", INT_MAX );
182 test( SIZE_MAX, "%+03.6d", INT_MAX );
183 puts( "- Multiple outputs -\n" );
184 test( SIZE_MAX, "- %d", INT_MAX );
185 test( SIZE_MAX, "- %d %% %d", INT_MAX, INT_MIN );
189 /* This macro delivers a given character to either a memory buffer or a stream,
190 depending on the contents of 'status' (struct status_t).
191 x - the character to be delivered
192 i - pointer to number of characters already delivered in this call
193 n - pointer to maximum number of characters to be delivered in this call
194 s - the buffer into which the character shall be delivered
197 #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 )
199 /* This function recursively converts a given integer value to a given base
200 into a character string. Persistent information - like the number of digits
201 parsed so far - is recorded in a struct status_t, which allows to avoid
202 overwriting snprintf() limits, and enables the function to do the necessary
203 padding / prefixing of the character string eventually printed.
205 static void int2base( intmax_t value, struct status_t * status )
207 /* Registering the character being printed at the end of the function here
208 already so it will be taken into account when the deepestmost recursion
209 does the prefix / padding stuff.
212 if ( ( value / status->base ) != 0 )
214 /* More digits to be done - recurse deeper */
215 int2base( value / status->base, status );
219 /* We reached the last digit, the deepest point of our recursion, and
220 only now know how long the number to be printed actually is. Now we
221 have to do the sign, prefix, width, and precision padding stuff
222 before printing the numbers while we resurface from the recursion.
224 /* At worst, we need two prefix characters (hex prefix). */
225 char preface[3] = "\0";
227 if ( ( status->flags & E_alt ) && ( status->base == 16 || status->base == 8 ) )
229 /* Octal / hexadecimal prefix for "%#" conversions */
230 preface[ preidx++ ] = '0'; /* TODO: For octal, standard states "extend the precision" */
231 if ( status->base == 16 )
233 preface[ preidx++ ] = ( status->flags & E_lower ) ? 'x' : 'X';
238 /* Negative sign for negative values - at all times. */
239 preface[ preidx++ ] = '-';
241 else if ( ! ( status->flags & E_unsigned ) )
243 /* plus sign / extra space are only for unsigned conversions */
244 if ( status->flags & E_plus )
246 preface[ preidx++ ] = '+';
248 else if ( status->flags & E_space )
250 preface[ preidx++ ] = ' ';
254 size_t prec_pads = ( status->prec > status->this ) ? ( status->prec - status->this ) : 0;
255 if ( ! ( status->flags & ( E_minus | E_zero ) ) )
257 /* Space padding is only done if no zero padding or left alignment
258 is requested. Leave space for any prefixes determined above.
260 /* The number of characters to be printed, plus prefixes if any. */
261 /* This line contained probably the most stupid, time-wasting bug
262 I've ever perpetrated. Greetings to Samface, DevL, and all
263 sceners at Breakpoint 2006.
265 size_t characters = preidx + ( ( status->this > status->prec ) ? status->this : status->prec );
266 if ( status->width > characters )
268 for ( int i = 0; i < status->width - characters; ++i )
271 ++(status->this); /* TODO: Probably have to do something so I still know how many zeroes are required, later. */
275 /* Now we did the padding, do the prefixes (if any). */
277 while ( preface[ preidx ] != '\0' )
279 DELIVER( preface[ preidx++ ] );
282 if ( ( ! ( status->flags & E_minus ) ) && ( status->flags & E_zero ) )
284 /* If field is not left aligned, and zero padding is requested, do
285 so. TODO: This should include precision handling (probably).
287 while ( status->this < status->width )
293 for ( int i = 0; i < prec_pads; ++i )
299 /* Recursion tail - print the current digit. */
301 int digit = value % status->base;
306 if ( status->flags & E_lower )
308 /* Lowercase letters. Same array used for strto...(). */
309 DELIVER( _PDCLIB_digits[ digit ] );
313 /* Uppercase letters. Array only used here, only 0-F. */
314 DELIVER( _PDCLIB_Xdigits[ digit ] );
319 /* This function is to be called with spec pointing to the leading '%' of a
320 printf() conversion specifier, with ap being
322 const char * parse_out( const char * spec, struct status_t * status )
324 const char * orig_spec = spec;
325 if ( *(++spec) == '%' )
329 /* Initializing status structure */
336 /* First come 0..n flags */
342 status->flags |= E_minus;
346 status->flags |= E_plus;
350 status->flags |= E_alt;
354 status->flags |= E_space;
358 status->flags |= E_zero;
362 status->flags |= E_done;
365 } while ( ! ( status->flags & E_done ) );
367 /* Optional field width */
370 /* Retrieve width value from argument stack */
371 if ( ( status->width = va_arg( status->ap, int ) ) < 0 )
373 /* Negative value is '-' flag plus absolute value */
374 status->flags |= E_minus;
381 /* If a width is given, strtol() will return its value. If not given,
382 strtol() will return zero. In both cases, endptr will point to the
383 rest of the conversion specifier - just what we need.
385 status->width = (int)strtol( spec, (char**)&spec, 10 );
388 /* Optional precision */
394 /* Retrieve precision value from argument stack. A negative value
395 is as if no precision is given - as precision is initalized to
396 EOF (negative), there is no need for testing for negative here.
398 status->prec = va_arg( status->ap, int );
403 status->prec = (int)strtol( spec, &endptr, 10 );
404 if ( spec == endptr )
406 /* Decimal point but no number - bad conversion specifier. */
411 /* Having a precision cancels out any zero flag. */
412 status->flags ^= E_zero;
415 /* Optional length modifier
416 We step one character ahead in any case, and step back only if we find
417 there has been no length modifier (or step ahead another character if it
418 has been "hh" or "ll").
425 status->flags |= E_char;
430 status->flags |= E_short;
436 status->flags |= E_llong;
441 status->flags |= E_long;
445 status->flags |= E_intmax;
448 status->flags |= E_size;
451 status->flags |= E_ptrdiff;
454 status->flags |= E_double;
461 /* Conversion specifier */
470 status->flags |= E_unsigned;
474 status->flags |= E_unsigned;
478 status->flags |= ( E_lower | E_unsigned );
482 status->flags |= E_unsigned;
499 /* uint2base( 16, (intptr_t)value, true ) */
503 /* No conversion specifier. Bad conversion. */
507 /* Do the actual output based on our findings */
508 if ( status->base != 0 )
510 /* Integer conversions */
511 /* TODO: Check for invalid flag combinations. */
512 if ( status->flags & E_unsigned )
515 switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_size ) )
518 value = (uintmax_t)(unsigned char)va_arg( status->ap, int );
521 value = (uintmax_t)(unsigned short)va_arg( status->ap, int );
524 value = (uintmax_t)va_arg( status->ap, unsigned int );
527 value = (uintmax_t)va_arg( status->ap, unsigned long );
530 value = (uintmax_t)va_arg( status->ap, unsigned long long );
533 value = (uintmax_t)va_arg( status->ap, size_t );
537 if ( ( value / status->base ) != 0 )
539 int2base( (intmax_t)(value / status->base), status );
541 int digit = value % status->base;
546 if ( status->flags & E_lower )
548 DELIVER( _PDCLIB_digits[ digit ] );
552 DELIVER( _PDCLIB_Xdigits[ digit ] );
557 switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_intmax ) )
560 int2base( (intmax_t)(char)va_arg( status->ap, int ), status );
563 int2base( (intmax_t)(short)va_arg( status->ap, int ), status );
566 int2base( (intmax_t)va_arg( status->ap, int ), status );
569 int2base( (intmax_t)va_arg( status->ap, long ), status );
572 int2base( (intmax_t)va_arg( status->ap, long long ), status );
575 int2base( (intmax_t)va_arg( status->ap, ptrdiff_t ), status );
578 int2base( va_arg( status->ap, intmax_t ), status );
582 if ( status->flags & E_minus )
584 while ( status->this < status->width )
590 if ( status->i >= status->n )
592 status->s[status->n - 1] = '\0';
598 inline void test( size_t n, const char * expect, ... )
600 char * buffer1 = malloc( 50 );
601 char * buffer2 = malloc( 50 );
605 va_start( ap, expect );
606 myrc = _PDCLIB_sprintf( buffer1, n, expect, ap );
607 rc = vsnprintf( buffer2, n, expect, ap );
608 if ( ( strcmp( buffer1, buffer2 ) != 0 ) || ( myrc != rc ) )
610 printf( "Output '%s', RC %d\nExpect '%s', RC %d\n\n", buffer1, myrc, buffer2, rc );
616 int _PDCLIB_sprintf( char * buffer, size_t n, const char * format, va_list ap )
618 struct status_t status = { 0, 0, n, 0, 0, buffer, 0, 0, NULL, ap };
619 while ( *format != '\0' )
622 if ( ( *format != '%' ) || ( ( rc = parse_out( format, &status ) ) == format ) )
624 /* No conversion specifier, print verbatim */
625 buffer[ status.i++ ] = *(format++);
629 /* Continue parsing after conversion specifier */
633 buffer[ status.i ] = '\0';
638 int _PDCLIB_fprintf( FILE * stream, const char * format, va_list ap )
640 struct status_t status = { 0, 0, SIZE_MAX, 0, 0, NULL, 0, 0, stream, ap };
641 while ( *format != '\0' )
644 if ( ( *format != '%' ) || ( ( rc = parse_out( format, &status, ap ) ) == format ) )
646 /* No conversion specifier, print verbatim */
647 putc( *(format++), stream );
651 /* Continue parsing after conversion specifier */