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_intptr 1<<13
29 #define E_double 1<<14
31 #define E_unsigned 1<<16
35 int base; /* base to which the value shall be converted */
36 int_fast32_t flags; /* flags and length modifiers */
37 size_t n; /* maximum number of characters to be written */
38 size_t i; /* number of characters already written */
39 size_t this; /* number of output chars in the current conversion */
40 char * s; /* target buffer */
41 size_t width; /* width of current field */
42 size_t prec; /* precision of current field */
43 FILE * stream;/* for to-stream output */
44 va_list ap; /* the argument stack passed to the printf function */
47 const char * parse_out( const char * spec, struct status_t * status );
48 inline void test( size_t n, const char * expect, ... );
49 int _PDCLIB_vsnprintf( char * buffer, size_t n, const char * format, va_list ap );
50 int _PDCLIB_snprintf( char * s, size_t n, const char * format, ... );
52 /* The following only for testing. */
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 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 test( SIZE_MAX, "%X", UINT_MAX );
84 test( SIZE_MAX, "%#X", -1u );
85 test( SIZE_MAX, "%x", UINT_MAX );
86 test( SIZE_MAX, "%#x", -1u );
87 test( SIZE_MAX, "%o", UINT_MAX );
88 test( SIZE_MAX, "%#o", -1u );
89 test( SIZE_MAX, "%.0#o", 0 );
90 test( SIZE_MAX, "%+d", INT_MIN );
91 test( SIZE_MAX, "%+d", INT_MAX );
92 test( SIZE_MAX, "%+d", 0 );
93 test( SIZE_MAX, "%+u", UINT_MAX );
94 test( SIZE_MAX, "%+u", -1u );
95 test( SIZE_MAX, "% d", INT_MIN );
96 test( SIZE_MAX, "% d", INT_MAX );
97 test( SIZE_MAX, "% d", 0 );
98 test( SIZE_MAX, "% u", UINT_MAX );
99 test( SIZE_MAX, "% u", -1u );
100 test( SIZE_MAX, "%9d", INT_MIN );
101 test( SIZE_MAX, "%9d", INT_MAX );
102 test( SIZE_MAX, "%10d", INT_MIN );
103 test( SIZE_MAX, "%10d", INT_MAX );
104 test( SIZE_MAX, "%11d", INT_MIN );
105 test( SIZE_MAX, "%11d", INT_MAX );
106 test( SIZE_MAX, "%12d", INT_MIN );
107 test( SIZE_MAX, "%12d", INT_MAX );
108 test( SIZE_MAX, "%-9d", INT_MIN );
109 test( SIZE_MAX, "%-9d", INT_MAX );
110 test( SIZE_MAX, "%-10d", INT_MIN );
111 test( SIZE_MAX, "%-10d", INT_MAX );
112 test( SIZE_MAX, "%-11d", INT_MIN );
113 test( SIZE_MAX, "%-11d", INT_MAX );
114 test( SIZE_MAX, "%-12d", INT_MIN );
115 test( SIZE_MAX, "%-12d", INT_MAX );
116 test( SIZE_MAX, "%09d", INT_MIN );
117 test( SIZE_MAX, "%09d", INT_MAX );
118 test( SIZE_MAX, "%010d", INT_MIN );
119 test( SIZE_MAX, "%010d", INT_MAX );
120 test( SIZE_MAX, "%011d", INT_MIN );
121 test( SIZE_MAX, "%011d", INT_MAX );
122 test( SIZE_MAX, "%012d", INT_MIN );
123 test( SIZE_MAX, "%012d", INT_MAX );
124 test( SIZE_MAX, "%-09d", INT_MIN );
125 test( SIZE_MAX, "%-09d", INT_MAX );
126 test( SIZE_MAX, "%-010d", INT_MIN );
127 test( SIZE_MAX, "%-010d", INT_MAX );
128 test( SIZE_MAX, "%-011d", INT_MIN );
129 test( SIZE_MAX, "%-011d", INT_MAX );
130 test( SIZE_MAX, "%-012d", INT_MIN );
131 test( SIZE_MAX, "%-012d", INT_MAX );
132 test( 8, "%9d", INT_MAX );
133 test( 8, "%9d", INT_MIN );
134 test( 9, "%9d", INT_MAX );
135 test( 9, "%9d", INT_MIN );
136 test( 10, "%9d", INT_MAX );
137 test( 10, "%9d", INT_MIN );
138 test( 9, "%10d", INT_MAX );
139 test( 9, "%10d", INT_MIN );
140 test( 10, "%10d", INT_MAX );
141 test( 10, "%10d", INT_MIN );
142 test( 11, "%10d", INT_MAX );
143 test( 11, "%10d", INT_MIN );
144 test( 10, "%11d", INT_MAX );
145 test( 10, "%11d", INT_MIN );
146 test( 11, "%11d", INT_MAX );
147 test( 11, "%11d", INT_MIN );
148 test( 12, "%11d", INT_MAX );
149 test( 12, "%11d", INT_MIN );
150 test( 11, "%12d", INT_MAX );
151 test( 11, "%12d", INT_MIN );
152 test( 12, "%12d", INT_MAX );
153 test( 12, "%12d", INT_MIN );
154 test( 13, "%12d", INT_MAX );
155 test( 13, "%12d", INT_MIN );
156 test( SIZE_MAX, "%030.20d", INT_MAX );
157 test( SIZE_MAX, "%.6x", UINT_MAX );
158 test( SIZE_MAX, "%#6.3x", UINT_MAX );
159 test( SIZE_MAX, "%#3.6x", UINT_MAX );
160 test( SIZE_MAX, "%.6d", INT_MIN );
161 test( SIZE_MAX, "%6.3d", INT_MIN );
162 test( SIZE_MAX, "%3.6d", INT_MIN );
163 test( SIZE_MAX, "%#0.6x", UINT_MAX );
164 test( SIZE_MAX, "%#06.3x", UINT_MAX );
165 test( SIZE_MAX, "%#03.6x", UINT_MAX );
166 test( SIZE_MAX, "%#0.6d", INT_MAX );
167 test( SIZE_MAX, "%#06.3d", INT_MAX );
168 test( SIZE_MAX, "%#03.6d", INT_MAX );
169 test( SIZE_MAX, "%#+.6d", INT_MAX );
170 test( SIZE_MAX, "%#+6.3d", INT_MAX );
171 test( SIZE_MAX, "%#+3.6d", INT_MAX );
172 test( SIZE_MAX, "%+0.6d", INT_MAX );
173 test( SIZE_MAX, "%+06.3d", INT_MAX );
174 test( SIZE_MAX, "%+03.6d", INT_MAX );
175 test( SIZE_MAX, "- %d", INT_MAX );
176 test( SIZE_MAX, "- %d %% %d", INT_MAX, INT_MIN );
177 test( SIZE_MAX, "%c", 'x' );
178 test( SIZE_MAX, "%s", "abcdef" );
179 test( SIZE_MAX, "%p", 0xdeadbeef );
182 int val1, val2, val3, val4;
183 snprintf( buffer, SIZE_MAX, "123456%n789%n", &val1, &val2 );
184 _PDCLIB_snprintf( buffer, SIZE_MAX, "123456%n789%n", &val3, &val4 );
185 if ( ( val1 != val3 ) || ( val2 != val4 ) )
187 printf( "Output %d/%d\nExpect %d/%d\n\n", val1, val2, val3, val4 );
193 /* This macro delivers a given character to either a memory buffer or a stream,
194 depending on the contents of 'status' (struct status_t).
195 x - the character to be delivered
196 i - pointer to number of characters already delivered in this call
197 n - pointer to maximum number of characters to be delivered in this call
198 s - the buffer into which the character shall be delivered
200 #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 )
202 /* This function recursively converts a given integer value to a given base
203 into a character string. Persistent information - like the number of digits
204 parsed so far - is recorded in a struct status_t, which allows to avoid
205 overwriting snprintf() limits, and enables the function to do the necessary
206 padding / prefixing of the character string eventually printed.
208 static void int2base( intmax_t value, struct status_t * status )
210 /* Registering the character being printed at the end of the function here
211 already so it will be taken into account when the deepestmost recursion
212 does the prefix / padding stuff.
215 if ( ( value / status->base ) != 0 )
217 /* More digits to be done - recurse deeper */
218 int2base( value / status->base, status );
222 /* We reached the last digit, the deepest point of our recursion, and
223 only now know how long the number to be printed actually is. Now we
224 have to do the sign, prefix, width, and precision padding stuff
225 before printing the numbers while we resurface from the recursion.
227 /* At worst, we need two prefix characters (hex prefix). */
228 char preface[3] = "\0";
230 if ( ( status->flags & E_alt ) && ( status->base == 16 || status->base == 8 ) )
232 /* Octal / hexadecimal prefix for "%#" conversions */
233 preface[ preidx++ ] = '0';
234 if ( status->base == 16 )
236 preface[ preidx++ ] = ( status->flags & E_lower ) ? 'x' : 'X';
241 /* Negative sign for negative values - at all times. */
242 preface[ preidx++ ] = '-';
244 else if ( ! ( status->flags & E_unsigned ) )
246 /* plus sign / extra space are only for unsigned conversions */
247 if ( status->flags & E_plus )
249 preface[ preidx++ ] = '+';
251 else if ( status->flags & E_space )
253 preface[ preidx++ ] = ' ';
257 size_t prec_pads = ( status->prec > status->this ) ? ( status->prec - status->this ) : 0;
258 if ( ! ( status->flags & ( E_minus | E_zero ) ) )
260 /* Space padding is only done if no zero padding or left alignment
261 is requested. Leave space for any prefixes determined above.
263 /* The number of characters to be printed, plus prefixes if any. */
264 /* This line contained probably the most stupid, time-wasting bug
265 I've ever perpetrated. Greetings to Samface, DevL, and all
266 sceners at Breakpoint 2006.
268 size_t characters = preidx + ( ( status->this > status->prec ) ? status->this : status->prec );
269 if ( status->width > characters )
271 for ( int i = 0; i < status->width - characters; ++i )
278 /* Now we did the padding, do the prefixes (if any). */
280 while ( preface[ preidx ] != '\0' )
282 DELIVER( preface[ preidx++ ] );
285 if ( ( ! ( status->flags & E_minus ) ) && ( status->flags & E_zero ) )
287 /* If field is not left aligned, and zero padding is requested, do
290 while ( status->this < status->width )
296 /* Do the precision padding if necessary. */
297 for ( int i = 0; i < prec_pads; ++i )
303 /* Recursion tail - print the current digit. */
305 int digit = value % status->base;
310 if ( status->flags & E_lower )
312 /* Lowercase letters. Same array used for strto...(). */
313 DELIVER( _PDCLIB_digits[ digit ] );
317 /* Uppercase letters. Array only used here, only 0-F. */
318 DELIVER( _PDCLIB_Xdigits[ digit ] );
323 /* This function is to be called with spec pointing to the leading '%' of a
324 printf() conversion specifier, with ap being
326 const char * parse_out( const char * spec, struct status_t * status )
328 const char * orig_spec = spec;
329 if ( *(++spec) == '%' )
334 /* Initializing status structure */
341 /* First come 0..n flags */
347 status->flags |= E_minus;
351 status->flags |= E_plus;
355 status->flags |= E_alt;
359 status->flags |= E_space;
363 status->flags |= E_zero;
367 status->flags |= E_done;
370 } while ( ! ( status->flags & E_done ) );
372 /* Optional field width */
375 /* Retrieve width value from argument stack */
376 if ( ( status->width = va_arg( status->ap, int ) ) < 0 )
378 /* Negative value is '-' flag plus absolute value */
379 status->flags |= E_minus;
386 /* If a width is given, strtol() will return its value. If not given,
387 strtol() will return zero. In both cases, endptr will point to the
388 rest of the conversion specifier - just what we need.
390 status->width = (int)strtol( spec, (char**)&spec, 10 );
393 /* Optional precision */
399 /* Retrieve precision value from argument stack. A negative value
400 is as if no precision is given - as precision is initalized to
401 EOF (negative), there is no need for testing for negative here.
403 status->prec = va_arg( status->ap, int );
408 status->prec = (int)strtol( spec, &endptr, 10 );
409 if ( spec == endptr )
411 /* Decimal point but no number - bad conversion specifier. */
416 /* Having a precision cancels out any zero flag. */
417 status->flags ^= E_zero;
420 /* Optional length modifier
421 We step one character ahead in any case, and step back only if we find
422 there has been no length modifier (or step ahead another character if it
423 has been "hh" or "ll").
430 status->flags |= E_char;
435 status->flags |= E_short;
441 status->flags |= E_llong;
446 status->flags |= E_long;
450 status->flags |= E_intmax;
453 status->flags |= E_size;
456 status->flags |= E_ptrdiff;
459 status->flags |= E_double;
466 /* Conversion specifier */
476 status->flags |= E_unsigned;
480 status->flags |= E_unsigned;
484 status->flags |= ( E_lower | E_unsigned );
488 status->flags |= E_unsigned;
501 /* TODO: Flags, wide chars. */
502 DELIVER( va_arg( status->ap, int ) );
505 /* TODO: Flags, wide chars. */
507 char * s = va_arg( status->ap, char * );
515 /* TODO: E_long -> E_intptr */
517 status->flags |= ( E_lower | E_unsigned | E_alt | E_long );
521 int * val = va_arg( status->ap, int * );
526 /* No conversion specifier. Bad conversion. */
530 /* Do the actual output based on our findings */
531 if ( status->base != 0 )
533 /* Integer conversions */
534 /* TODO: Check for invalid flag combinations. */
535 if ( status->flags & E_unsigned )
538 switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_size ) )
541 value = (uintmax_t)(unsigned char)va_arg( status->ap, int );
544 value = (uintmax_t)(unsigned short)va_arg( status->ap, int );
547 value = (uintmax_t)va_arg( status->ap, unsigned int );
550 value = (uintmax_t)va_arg( status->ap, unsigned long );
553 value = (uintmax_t)va_arg( status->ap, unsigned long long );
556 value = (uintmax_t)va_arg( status->ap, size_t );
560 if ( ( value / status->base ) != 0 )
562 int2base( (intmax_t)(value / status->base), status );
564 int digit = value % status->base;
569 if ( status->flags & E_lower )
571 DELIVER( _PDCLIB_digits[ digit ] );
575 DELIVER( _PDCLIB_Xdigits[ digit ] );
580 switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_intmax ) )
583 int2base( (intmax_t)(char)va_arg( status->ap, int ), status );
586 int2base( (intmax_t)(short)va_arg( status->ap, int ), status );
589 int2base( (intmax_t)va_arg( status->ap, int ), status );
592 int2base( (intmax_t)va_arg( status->ap, long ), status );
595 int2base( (intmax_t)va_arg( status->ap, long long ), status );
598 int2base( (intmax_t)va_arg( status->ap, ptrdiff_t ), status );
601 int2base( va_arg( status->ap, intmax_t ), status );
605 if ( status->flags & E_minus )
607 while ( status->this < status->width )
613 if ( status->i >= status->n )
615 status->s[status->n - 1] = '\0';
621 inline void test( size_t n, const char * expect, ... )
623 char * buffer1 = malloc( 50 );
624 char * buffer2 = malloc( 50 );
628 va_start( ap, expect );
629 myrc = _PDCLIB_vsnprintf( buffer1, n, expect, ap );
630 rc = vsnprintf( buffer2, n, expect, ap );
631 if ( ( strcmp( buffer1, buffer2 ) != 0 ) || ( myrc != rc ) )
633 printf( "Output '%s', RC %d\nExpect '%s', RC %d\n\n", buffer1, myrc, buffer2, rc );
639 int _PDCLIB_vsnprintf( char * buffer, size_t n, const char * format, va_list ap )
641 struct status_t status = { 0, 0, n, 0, 0, buffer, 0, 0, NULL, ap };
642 while ( *format != '\0' )
645 if ( ( *format != '%' ) || ( ( rc = parse_out( format, &status ) ) == format ) )
647 /* No conversion specifier, print verbatim */
648 buffer[ status.i++ ] = *(format++);
652 /* Continue parsing after conversion specifier */
656 buffer[ status.i ] = '\0';
660 int _PDCLIB_snprintf( char * s, size_t n, const char * format, ... )
663 va_start( ap, format );
664 return _PDCLIB_vsnprintf( s, n, format, ap );
668 int _PDCLIB_fprintf( FILE * stream, const char * format, va_list ap )
670 struct status_t status = { 0, 0, SIZE_MAX, 0, 0, NULL, 0, 0, stream, ap };
671 while ( *format != '\0' )
674 if ( ( *format != '%' ) || ( ( rc = parse_out( format, &status, ap ) ) == format ) )
676 /* No conversion specifier, print verbatim */
677 putc( *(format++), stream );
681 /* Continue parsing after conversion specifier */