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 */
45 const char * parse_out( const char * spec, struct status_t * status, va_list ap );
46 inline void test( char * buffer, size_t n, const char * expect, struct status_t * status, ... );
47 int _PDCLIB_sprintf( char * buffer, const char * format, va_list ap );
49 /* The following only for testing. */
55 struct status_t status;
56 char * buffer = malloc( 50 );
57 status.s = calloc( 50, 1 );
61 puts( "- Signed min / max -\n" );
62 test( buffer, SIZE_MAX, "%hhd", &status, CHAR_MIN );
63 test( buffer, SIZE_MAX, "%hhd", &status, CHAR_MAX );
64 test( buffer, SIZE_MAX, "%hhd", &status, 0 );
65 test( buffer, SIZE_MAX, "%hd", &status, SHRT_MIN );
66 test( buffer, SIZE_MAX, "%hd", &status, SHRT_MAX );
67 test( buffer, SIZE_MAX, "%hd", &status, 0 );
68 test( buffer, SIZE_MAX, "%d", &status, INT_MIN );
69 test( buffer, SIZE_MAX, "%d", &status, INT_MAX );
70 test( buffer, SIZE_MAX, "%d", &status, 0 );
71 test( buffer, SIZE_MAX, "%ld", &status, LONG_MIN );
72 test( buffer, SIZE_MAX, "%ld", &status, LONG_MAX );
73 test( buffer, SIZE_MAX, "%ld", &status, 0l );
74 test( buffer, SIZE_MAX, "%lld", &status, LLONG_MIN );
75 test( buffer, SIZE_MAX, "%lld", &status, LLONG_MAX );
76 test( buffer, SIZE_MAX, "%lld", &status, 0ll );
77 puts( "- Unsigned min / max -\n" );
78 test( buffer, SIZE_MAX, "%hhu", &status, UCHAR_MAX );
79 test( buffer, SIZE_MAX, "%hhu", &status, (unsigned char)-1 );
80 test( buffer, SIZE_MAX, "%hu", &status, USHRT_MAX );
81 test( buffer, SIZE_MAX, "%hu", &status, (unsigned short)-1 );
82 test( buffer, SIZE_MAX, "%u", &status, UINT_MAX );
83 test( buffer, SIZE_MAX, "%u", &status, -1u );
84 test( buffer, SIZE_MAX, "%lu", &status, ULONG_MAX );
85 test( buffer, SIZE_MAX, "%lu", &status, -1ul );
86 test( buffer, SIZE_MAX, "%llu", &status, ULLONG_MAX );
87 test( buffer, SIZE_MAX, "%llu", &status, -1ull );
88 puts( "- Hex and Octal, normal and alternative, upper and lowercase -\n" );
89 test( buffer, SIZE_MAX, "%X", &status, UINT_MAX );
90 test( buffer, SIZE_MAX, "%#X", &status, -1u );
91 test( buffer, SIZE_MAX, "%x", &status, UINT_MAX );
92 test( buffer, SIZE_MAX, "%#x", &status, -1u );
93 test( buffer, SIZE_MAX, "%o", &status, UINT_MAX );
94 test( buffer, SIZE_MAX, "%#o", &status, -1u );
95 puts( "- Plus flag -\n" );
96 test( buffer, SIZE_MAX, "%+d", &status, INT_MIN );
97 test( buffer, SIZE_MAX, "%+d", &status, INT_MAX );
98 test( buffer, SIZE_MAX, "%+d", &status, 0 );
99 test( buffer, SIZE_MAX, "%+u", &status, UINT_MAX );
100 test( buffer, SIZE_MAX, "%+u", &status, -1u );
101 puts( "- Space flag -\n" );
102 test( buffer, SIZE_MAX, "% d", &status, INT_MIN );
103 test( buffer, SIZE_MAX, "% d", &status, INT_MAX );
104 test( buffer, SIZE_MAX, "% d", &status, 0 );
105 test( buffer, SIZE_MAX, "% u", &status, UINT_MAX );
106 test( buffer, SIZE_MAX, "% u", &status, -1u );
107 puts( "- Field width -\n" );
108 test( buffer, SIZE_MAX, "%9d", &status, INT_MIN );
109 test( buffer, SIZE_MAX, "%9d", &status, INT_MAX );
110 test( buffer, SIZE_MAX, "%10d", &status, INT_MIN );
111 test( buffer, SIZE_MAX, "%10d", &status, INT_MAX );
112 test( buffer, SIZE_MAX, "%11d", &status, INT_MIN );
113 test( buffer, SIZE_MAX, "%11d", &status, INT_MAX );
114 test( buffer, SIZE_MAX, "%12d", &status, INT_MIN );
115 test( buffer, SIZE_MAX, "%12d", &status, INT_MAX );
116 puts( "- Field width (left bound) -\n" );
117 test( buffer, SIZE_MAX, "%-9d", &status, INT_MIN );
118 test( buffer, SIZE_MAX, "%-9d", &status, INT_MAX );
119 test( buffer, SIZE_MAX, "%-10d", &status, INT_MIN );
120 test( buffer, SIZE_MAX, "%-10d", &status, INT_MAX );
121 test( buffer, SIZE_MAX, "%-11d", &status, INT_MIN );
122 test( buffer, SIZE_MAX, "%-11d", &status, INT_MAX );
123 test( buffer, SIZE_MAX, "%-12d", &status, INT_MIN );
124 test( buffer, SIZE_MAX, "%-12d", &status, INT_MAX );
125 puts( "- Field width, zero padding -\n");
126 test( buffer, SIZE_MAX, "%09d", &status, INT_MIN );
127 test( buffer, SIZE_MAX, "%09d", &status, INT_MAX );
128 test( buffer, SIZE_MAX, "%010d", &status, INT_MIN );
129 test( buffer, SIZE_MAX, "%010d", &status, INT_MAX );
130 test( buffer, SIZE_MAX, "%011d", &status, INT_MIN );
131 test( buffer, SIZE_MAX, "%011d", &status, INT_MAX );
132 test( buffer, SIZE_MAX, "%012d", &status, INT_MIN );
133 test( buffer, SIZE_MAX, "%012d", &status, INT_MAX );
134 puts( "- Field width, zero padding (left bound) -\n" );
135 test( buffer, SIZE_MAX, "%-09d", &status, INT_MIN );
136 test( buffer, SIZE_MAX, "%-09d", &status, INT_MAX );
137 test( buffer, SIZE_MAX, "%-010d", &status, INT_MIN );
138 test( buffer, SIZE_MAX, "%-010d", &status, INT_MAX );
139 test( buffer, SIZE_MAX, "%-011d", &status, INT_MIN );
140 test( buffer, SIZE_MAX, "%-011d", &status, INT_MAX );
141 test( buffer, SIZE_MAX, "%-012d", &status, INT_MIN );
142 test( buffer, SIZE_MAX, "%-012d", &status, INT_MAX );
146 /* x - the character to be delivered
147 i - pointer to number of characters already delivered in this call
148 n - pointer to maximum number of characters to be delivered in this call
149 s - the buffer into which the character shall be delivered
152 #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 )
154 static void int2base( intmax_t value, struct status_t * status )
157 if ( ( value / status->base ) != 0 )
159 int2base( value / status->base, status );
163 char preface[3] = "\0\0";
165 if ( ( status->flags & E_alt ) && ( status->base == 16 || status->base == 8 ) )
167 preface[ preidx++ ] = '0';
168 if ( status->base == 16 )
170 preface[ preidx++ ] = ( status->flags & E_lower ) ? 'x' : 'X';
175 preface[ preidx++ ] = '-';
177 else if ( ! ( status->flags & E_unsigned ) )
179 if ( status->flags & E_plus )
181 preface[ preidx++ ] = '+';
183 else if ( status->flags & E_space )
185 preface[ preidx++ ] = ' ';
188 if ( ! ( status->flags & ( E_minus | E_zero ) ) )
190 while ( ( status->this + preidx ) < status->width )
197 while ( preface[ preidx ] != '\0' )
199 DELIVER( preface[ preidx++ ] );
202 if ( ( ! ( status->flags & E_minus ) ) && ( status->flags & E_zero ) )
204 while ( status->this < status->width )
212 int digit = value % status->base;
217 if ( status->flags & E_lower )
219 DELIVER( _PDCLIB_digits[ digit ] );
223 DELIVER( _PDCLIB_Xdigits[ digit ] );
228 const char * parse_out( const char * spec, struct status_t * status, va_list ap )
230 const char * orig_spec = spec;
231 if ( *(++spec) == '%' )
236 /* Initializing status structure */
243 /* First come 0..n flags */
249 status->flags |= E_minus;
253 status->flags |= E_plus;
257 status->flags |= E_alt;
261 status->flags |= E_space;
265 status->flags |= E_zero;
269 status->flags |= E_done;
272 } while ( ! ( status->flags & E_done ) );
274 /* Optional field width */
277 /* Retrieve width value from argument stack */
278 if ( ( status->width = va_arg( ap, int ) ) < 0 )
280 /* Negative value is '-' flag plus absolute value */
281 status->flags |= E_minus;
288 /* If a width is given, strtol() will return its value. If not given,
289 strtol() will return zero. In both cases, endptr will point to the
290 rest of the conversion specifier - just what we need.
292 status->width = (int)strtol( spec, (char**)&spec, 10 );
295 /* Optional precision */
301 /* Retrieve precision value from argument stack. A negative value
302 is as if no precision is given - as precision is initalized to
303 EOF (negative), there is no need for testing for negative here.
305 status->prec = va_arg( ap, int );
310 status->prec = (int)strtol( spec, &endptr, 10 );
311 if ( spec == endptr )
313 /* Decimal point but no number - bad conversion specifier. */
319 /* Optional length modifier
320 We step one character ahead in any case, and step back only if we find
321 there has been no length modifier (or step ahead another character if it
322 has been "hh" or "ll").
329 status->flags |= E_char;
334 status->flags |= E_short;
340 status->flags |= E_llong;
345 status->flags |= E_long;
349 status->flags |= E_intmax;
352 status->flags |= E_size;
355 status->flags |= E_ptrdiff;
358 status->flags |= E_double;
365 /* Conversion specifier */
374 status->flags |= E_unsigned;
378 status->flags |= E_unsigned;
382 status->flags |= ( E_lower | E_unsigned );
386 status->flags |= E_unsigned;
403 /* uint2base( 16, (intptr_t)value, true ) */
407 /* No conversion specifier. Bad conversion. */
411 /* Do the actual output based on our findings */
412 if ( status->base != 0 )
414 /* Integer conversions */
415 /* TODO: Check for invalid flag combinations. */
416 if ( status->flags & E_unsigned )
419 switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_size ) )
422 value = (uintmax_t)(unsigned char)va_arg( ap, int );
425 value = (uintmax_t)(unsigned short)va_arg( ap, int );
428 value = (uintmax_t)va_arg( ap, unsigned int );
431 value = (uintmax_t)va_arg( ap, unsigned long );
434 value = (uintmax_t)va_arg( ap, unsigned long long );
437 value = (uintmax_t)va_arg( ap, size_t );
441 if ( ( value / status->base ) != 0 )
443 int2base( (intmax_t)(value / status->base), status );
445 int digit = value % status->base;
450 if ( status->flags & E_lower )
452 DELIVER( _PDCLIB_digits[ digit ] );
456 DELIVER( _PDCLIB_Xdigits[ digit ] );
461 switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_intmax ) )
464 int2base( (intmax_t)(char)va_arg( ap, int ), status );
467 int2base( (intmax_t)(short)va_arg( ap, int ), status );
470 int2base( (intmax_t)va_arg( ap, int ), status );
473 int2base( (intmax_t)va_arg( ap, long ), status );
476 int2base( (intmax_t)va_arg( ap, long long ), status );
479 int2base( (intmax_t)va_arg( ap, ptrdiff_t ), status );
482 int2base( va_arg( ap, intmax_t ), status );
486 if ( status->flags & E_minus )
488 while ( status->this < status->width )
494 if ( status->i >= status->n )
496 status->s[status->n - 1] = '\0';
502 inline void test( char * buffer, size_t n, const char * expect, struct status_t * status, ... )
507 va_start( ap, status ); // y
508 memset( status->s, '\0', 50 ); // n
509 myrc = _PDCLIB_sprintf( status->s, expect, ap );
510 rc = vsnprintf( buffer, n, expect, ap ); // n
511 if ( ( strcmp( status->s, buffer ) != 0 ) || ( myrc != rc ) )
513 printf( "Output '%s', RC %d\nExpect '%s', RC %d\n\n", buffer, myrc, buffer, rc );
517 int _PDCLIB_sprintf( char * buffer, const char * format, va_list ap )
519 struct status_t status = { 0, 0, SIZE_MAX, 0, 0, buffer, 0, 0, NULL };
520 while ( *format != '\0' )
523 if ( ( *format != '%' ) || ( ( rc = parse_out( format, &status, ap ) ) == format ) )
525 /* No conversion specifier, print verbatim */
526 buffer[ status.i++ ] = *format;
530 /* Continue parsing after conversion specifier */
538 int _PDCLIB_fprintf( FILE * stream, const char * format, va_list ap )
540 char * buffer = malloc( 50 );
541 struct status_t status = { 0, 0, SIZE_MAX, 0, 0, /* stream->buffer */ buffer, 0, 0, stream };
542 while ( *format != '\0' )
545 if ( ( *format != '%' ) || ( ( rc = parse_out( format, &status, ap ) ) == format ) )
547 /* No conversion specifier, print verbatim */
548 putc( *(format++), stream );
552 /* Continue parsing after conversion specifier */