12 #include </home/solar/src/pdclib/functions/_PDCLIB/digits.c>
14 /* Using an integer's bits as flags for both the conversion flags and length
27 #define E_intmax 1<<10
29 #define E_ptrdiff 1<<12
30 #define E_double 1<<13
34 void parse_out( const char * spec, va_list ap );
38 int base; /* base to which the value shall be converted */
39 int_fast16_t flags; /* flags and length modifiers */
40 size_t n; /* maximum number of characters to be written */
41 size_t i; /* number of characters already written */
42 size_t this; /* number of output chars in the current conversion */
43 char * s; /* target buffer */
44 size_t width; /* width of current field */
45 size_t prec; /* precision of current field */
48 /* x - the character to be delivered
49 i - pointer to number of characters already delivered in this call
50 n - pointer to maximum number of characters to be delivered in this call
51 s - the buffer into which the character shall be delivered
54 #define DELIVER( x ) do { if ( status->i < status->n ) status->s[status->i] = x; ++(status->i); } while ( 0 )
56 static void int2base( intmax_t value, struct status_t * status )
59 if ( ( value / status->base ) != 0 )
61 int2base( value / status->base, status );
65 char preface[3] = "\0\0";
67 if ( ( status->flags & E_alt ) && ( status->base == 16 || status->base == 8 ) )
69 preface[ preidx++ ] = '0';
70 if ( status->base == 16 )
72 preface[ preidx++ ] = ( status->flags & E_lower ) ? 'x' : 'X';
77 preface[ preidx++ ] = '-';
81 if ( status->flags & E_plus )
83 preface[ preidx++ ] = '+';
85 else if ( status->flags & E_space )
87 preface[ preidx++ ] = ' ';
90 if ( ! ( ( status->flags & E_minus ) || ( status->flags & E_zero ) ) )
92 while ( ( status->this + preidx ) < status->width )
99 while ( preface[ preidx ] != '\0' )
101 DELIVER( preface[ preidx++ ] );
104 if ( ( ! ( status->flags & E_minus ) ) && ( status->flags & E_zero ) )
106 while ( status->this < status->width )
114 int digit = value % status->base;
119 if ( status->flags & E_lower )
121 DELIVER( _PDCLIB_digits[ digit ] );
125 DELIVER( toupper( _PDCLIB_digits[ digit ] ) );
130 static void padwrap( intmax_t value, struct status_t * status )
132 int2base( value, status );
133 if ( status->flags & E_minus )
135 while ( status->this < status->width )
141 if ( status->i >= status->n )
143 status->s[status->n - 1] = '\0';
147 static void upadwrap( uintmax_t value, struct status_t * status )
150 if ( ( value / status->base ) != 0 )
152 int2base( value / status->base, status );
154 int digit = value % status->base;
159 if ( status->flags & E_lower )
161 DELIVER( _PDCLIB_digits[ digit ] );
165 DELIVER( toupper( _PDCLIB_digits[ digit ] ) );
167 if ( status->flags & E_minus )
169 while ( status->this < status->width )
175 if ( status->i >= status->n )
177 status->s[status->n - 1] = '\0';
181 void parse_out( const char * spec, va_list ap )
183 /* TODO: '%' handled correctly? */
184 struct status_t status = { 0, 0, 0, 0, 0, NULL, 0, EOF };
185 /* First come 0..n flags */
186 while ( ! ( status.flags & E_done ) )
191 status.flags |= E_minus;
194 status.flags |= E_plus;
197 status.flags |= E_alt;
200 status.flags |= E_space;
203 status.flags |= E_zero;
206 status.flags |= E_done;
212 /* Retrieve width value from argument stack */
213 if ( ( status.width = va_arg( ap, int ) ) < 0 )
215 /* Negative value is '-' flag plus absolute value */
216 status.flags |= E_minus;
223 /* If a width is given, strtol() will return its value. If not given,
224 strtol() will return zero. In both cases, endptr will point to the
225 rest of the conversion specifier.
228 status.width = (int)strtol( spec, &endptr, 10 );
233 if ( *(++spec) == '*' )
235 /* Retrieve precision value from argument stack. A negative value
236 is as if no precision is given - as precision is initalized to
237 EOF (negative), there is no need for testing for negative here.
239 status.prec = va_arg( ap, int );
244 status.prec = (int)strtol( spec, &endptr, 10 );
248 /* We step one character ahead in any case, and step back only if we find
249 there has been no length modifier (or step ahead another character if it
250 has been "hh" or "ll").
257 status.flags |= E_char;
262 status.flags |= E_short;
268 status.flags |= E_llong;
273 status.flags |= E_long;
277 status.flags |= E_intmax;
280 status.flags |= E_size;
283 status.flags |= E_ptrdiff;
286 status.flags |= E_double;
296 /* int2base( 10, value, true ) */
299 /* int2base( 8, value, true ) */
302 /* uint2base( 10, value, true ) */
305 /* uint2base( 16, value, true ) */
308 /* uint2base( 16, value, false ) */
325 /* uint2base( 16, (intptr_t)value, true ) */
328 // conversion specifier
336 #define TESTCASE( _flags, _n, _width, _prec, _value, _base, _expect ) \
337 status.flags = _flags | E_term; \
340 status.width = _width; \
341 status.prec = _prec; \
342 status.base = _base; \
344 memset( status.s, '\0', 50 ); \
345 padwrap( _value, &status ); \
346 rc = snprintf( buffer, _n, _expect, _value ); \
347 if ( ( strcmp( status.s, buffer ) != 0 ) || ( status.i != rc ) ) \
349 printf( "Output '%s', RC %d\nExpect '%s', RC %d\n\n", status.s, status.i, buffer, rc ); \
352 #define UTESTCASE( _flags, _n, _width, _prec, _value, _base, _expect ) \
353 status.flags = _flags | E_term; \
356 status.width = _width; \
357 status.prec = _prec; \
358 status.base = _base; \
360 memset( status.s, '\0', 50 ); \
361 upadwrap( _value, &status ); \
362 rc = snprintf( buffer, _n, _expect, _value ); \
363 if ( ( strcmp( status.s, buffer ) != 0 ) || ( status.i != rc ) ) \
365 printf( "Output '%s', RC %d\nExpect '%s', RC %d\n\n", status.s, status.i, buffer, rc ); \
370 struct status_t status;
373 char * buffer = malloc( 50 );
374 status.s = malloc( 50 );
376 TESTCASE( E_plus, 5, 0, 0, 1234, 10, "%+d" );
377 TESTCASE( E_space, 3, 0, 0, 1234, 10, "% d" );
378 TESTCASE( E_space, 3, 0, 0, -1234, 10, "% d" );
379 TESTCASE( E_plus, 3, 0, 0, -1234, 10, "%+d" );
380 TESTCASE( E_done, 4, 0, 0, 65535, 16, "%X" );
381 TESTCASE( E_lower | E_alt, 4, 0, 0, 65534, 16, "%#x" );
382 TESTCASE( E_done, 4, 0, 0, 62, 8, "%o" );
383 TESTCASE( E_alt, 4, 0, 0, 62, 8, "%#o" );
384 TESTCASE( E_done, 6, 6, 0, 1234, 10, "%6d" );
385 TESTCASE( E_minus, 6, 6, 0, 1234, 10, "%-6d" );
386 TESTCASE( E_minus, 6, 2, 0, 1234, 10, "%-2d" );
387 TESTCASE( E_done, 6, 2, 0, 1234, 10, "%2d" );
388 TESTCASE( E_zero, 6, 6, 0, -1234, 10, "%06d" );
389 /* TODO: These two are *unsigned* conversions! */
390 TESTCASE( E_zero, 7, 7, 0, -65535, 16, "%07X" );
391 TESTCASE( E_zero, 7, 7, 0, -65535, 10, "%07u" );
393 TESTCASE( E_zero | E_minus, 6, 6, 0, 1234, 10, "%-06d" );
394 TESTCASE( E_plus, 6, 6, 0, 1234, 10, "%+6d" );
395 TESTCASE( E_space, 6, 6, 0, 1234, 10, "% 6d" );
396 TESTCASE( E_space, 6, 6, 0, -1234, 10, "% 6d" );
397 TESTCASE( E_space | E_minus, 6, 6, 0, -1234, 10, "%- 6d" );
399 puts( "--- Serious Tests ---\n" );
400 puts( "- Signed min / max -\n" );
401 TESTCASE( E_done, SIZE_MAX, 0, 0, CHAR_MIN, 10, "%hhd" );
402 TESTCASE( E_done, SIZE_MAX, 0, 0, CHAR_MAX, 10, "%hhd" );
403 TESTCASE( E_done, SIZE_MAX, 0, 0, 0, 10, "%hhd" );
404 TESTCASE( E_done, SIZE_MAX, 0, 0, SHRT_MIN, 10, "%hd" );
405 TESTCASE( E_done, SIZE_MAX, 0, 0, SHRT_MAX, 10, "%hd" );
406 TESTCASE( E_done, SIZE_MAX, 0, 0, 0, 10, "%hd" );
407 TESTCASE( E_done, SIZE_MAX, 0, 0, INT_MIN, 10, "%d" );
408 TESTCASE( E_done, SIZE_MAX, 0, 0, INT_MAX, 10, "%d" );
409 TESTCASE( E_done, SIZE_MAX, 0, 0, 0, 10, "%d" );
410 TESTCASE( E_done, SIZE_MAX, 0, 0, LONG_MIN, 10, "%ld" );
411 TESTCASE( E_done, SIZE_MAX, 0, 0, LONG_MAX, 10, "%ld" );
412 TESTCASE( E_done, SIZE_MAX, 0, 0, 0l, 10, "%ld" );
413 TESTCASE( E_done, SIZE_MAX, 0, 0, LLONG_MIN, 10, "%lld" );
414 TESTCASE( E_done, SIZE_MAX, 0, 0, LLONG_MAX, 10, "%lld" );
415 TESTCASE( E_done, SIZE_MAX, 0, 0, 0ll, 10, "%lld" );
416 puts( "- Unsigned min / max -\n" );
417 UTESTCASE( E_done, SIZE_MAX, 0, 0, UCHAR_MAX, 10, "%hhu" );
418 UTESTCASE( E_done, SIZE_MAX, 0, 0, -1, 10, "%hhu" );
419 UTESTCASE( E_done, SIZE_MAX, 0, 0, USHRT_MAX, 10, "%hu" );
420 UTESTCASE( E_done, SIZE_MAX, 0, 0, -1, 10, "%hu" );
421 UTESTCASE( E_done, SIZE_MAX, 0, 0, UINT_MAX, 10, "%u" );
422 UTESTCASE( E_done, SIZE_MAX, 0, 0, -1, 10, "%u" );
423 UTESTCASE( E_done, SIZE_MAX, 0, 0, ULONG_MAX, 10, "%lu" );
424 UTESTCASE( E_done, SIZE_MAX, 0, 0, -1l, 10, "%lu" );
425 UTESTCASE( E_done, SIZE_MAX, 0, 0, ULLONG_MAX, 10, "%llu" );
426 UTESTCASE( E_done, SIZE_MAX, 0, 0, -1ll, 10, "%llu" );