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
32 #define E_usigned 1<<15
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++ ] = '-';
79 else if ( ! ( status->flags & E_usigned ) )
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 )
149 status->flags |= E_usigned;
151 if ( ( value / status->base ) != 0 )
153 int2base( value / status->base, status );
155 int digit = value % status->base;
160 if ( status->flags & E_lower )
162 DELIVER( _PDCLIB_digits[ digit ] );
166 DELIVER( toupper( _PDCLIB_digits[ digit ] ) );
168 if ( status->flags & E_minus )
170 while ( status->this < status->width )
176 if ( status->i >= status->n )
178 status->s[status->n - 1] = '\0';
182 void parse_out( const char * spec, va_list ap )
184 /* TODO: '%' handled correctly? */
185 struct status_t status = { 0, 0, 0, 0, 0, NULL, 0, EOF };
186 /* First come 0..n flags */
187 while ( ! ( status.flags & E_done ) )
192 status.flags |= E_minus;
195 status.flags |= E_plus;
198 status.flags |= E_alt;
201 status.flags |= E_space;
204 status.flags |= E_zero;
207 status.flags |= E_done;
213 /* Retrieve width value from argument stack */
214 if ( ( status.width = va_arg( ap, int ) ) < 0 )
216 /* Negative value is '-' flag plus absolute value */
217 status.flags |= E_minus;
224 /* If a width is given, strtol() will return its value. If not given,
225 strtol() will return zero. In both cases, endptr will point to the
226 rest of the conversion specifier.
229 status.width = (int)strtol( spec, &endptr, 10 );
234 if ( *(++spec) == '*' )
236 /* Retrieve precision value from argument stack. A negative value
237 is as if no precision is given - as precision is initalized to
238 EOF (negative), there is no need for testing for negative here.
240 status.prec = va_arg( ap, int );
245 status.prec = (int)strtol( spec, &endptr, 10 );
249 /* We step one character ahead in any case, and step back only if we find
250 there has been no length modifier (or step ahead another character if it
251 has been "hh" or "ll").
258 status.flags |= E_char;
263 status.flags |= E_short;
269 status.flags |= E_llong;
274 status.flags |= E_long;
278 status.flags |= E_intmax;
281 status.flags |= E_size;
284 status.flags |= E_ptrdiff;
287 status.flags |= E_double;
297 /* int2base( 10, value, true ) */
300 /* int2base( 8, value, true ) */
303 /* uint2base( 10, value, true ) */
306 /* uint2base( 16, value, true ) */
309 /* uint2base( 16, value, false ) */
326 /* uint2base( 16, (intptr_t)value, true ) */
329 // conversion specifier
337 #define TESTCASE( _flags, _n, _width, _prec, _value, _base, _expect ) \
338 status.flags = _flags; \
341 status.width = _width; \
342 status.prec = _prec; \
343 status.base = _base; \
345 memset( status.s, '\0', 50 ); \
346 padwrap( _value, &status ); \
347 rc = snprintf( buffer, _n, _expect, _value ); \
348 if ( ( strcmp( status.s, buffer ) != 0 ) || ( status.i != rc ) ) \
350 printf( "Output '%s', RC %d\nExpect '%s', RC %d\n\n", status.s, status.i, buffer, rc ); \
353 #define UTESTCASE( _flags, _n, _width, _prec, _value, _base, _expect ) \
354 status.flags = _flags; \
357 status.width = _width; \
358 status.prec = _prec; \
359 status.base = _base; \
361 memset( status.s, '\0', 50 ); \
362 upadwrap( _value, &status ); \
363 rc = snprintf( buffer, _n, _expect, _value ); \
364 if ( ( strcmp( status.s, buffer ) != 0 ) || ( status.i != rc ) ) \
366 printf( "Output '%s', RC %d\nExpect '%s', RC %d\n\n", status.s, status.i, buffer, rc ); \
371 struct status_t status;
374 char * buffer = malloc( 50 );
375 status.s = malloc( 50 );
376 puts( "- Signed min / max -\n" );
377 TESTCASE( E_char, SIZE_MAX, 0, 0, CHAR_MIN, 10, "%hhd" );
378 TESTCASE( E_char, SIZE_MAX, 0, 0, CHAR_MAX, 10, "%hhd" );
379 TESTCASE( E_char, SIZE_MAX, 0, 0, 0, 10, "%hhd" );
380 TESTCASE( E_short, SIZE_MAX, 0, 0, SHRT_MIN, 10, "%hd" );
381 TESTCASE( E_short, SIZE_MAX, 0, 0, SHRT_MAX, 10, "%hd" );
382 TESTCASE( E_short, SIZE_MAX, 0, 0, 0, 10, "%hd" );
383 TESTCASE( E_done, SIZE_MAX, 0, 0, INT_MIN, 10, "%d" );
384 TESTCASE( E_done, SIZE_MAX, 0, 0, INT_MAX, 10, "%d" );
385 TESTCASE( E_done, SIZE_MAX, 0, 0, 0, 10, "%d" );
386 TESTCASE( E_long, SIZE_MAX, 0, 0, LONG_MIN, 10, "%ld" );
387 TESTCASE( E_long, SIZE_MAX, 0, 0, LONG_MAX, 10, "%ld" );
388 TESTCASE( E_long, SIZE_MAX, 0, 0, 0l, 10, "%ld" );
389 TESTCASE( E_llong, SIZE_MAX, 0, 0, LLONG_MIN, 10, "%lld" );
390 TESTCASE( E_llong, SIZE_MAX, 0, 0, LLONG_MAX, 10, "%lld" );
391 TESTCASE( E_llong, SIZE_MAX, 0, 0, 0ll, 10, "%lld" );
392 puts( "- Unsigned min / max -\n" );
393 UTESTCASE( E_char, SIZE_MAX, 0, 0, UCHAR_MAX, 10, "%hhu" );
394 UTESTCASE( E_char, SIZE_MAX, 0, 0, (unsigned char)-1, 10, "%hhu" );
395 UTESTCASE( E_short, SIZE_MAX, 0, 0, USHRT_MAX, 10, "%hu" );
396 UTESTCASE( E_short, SIZE_MAX, 0, 0, (unsigned short)-1, 10, "%hu" );
397 UTESTCASE( E_done, SIZE_MAX, 0, 0, UINT_MAX, 10, "%u" );
398 UTESTCASE( E_done, SIZE_MAX, 0, 0, -1u, 10, "%u" );
399 UTESTCASE( E_long, SIZE_MAX, 0, 0, ULONG_MAX, 10, "%lu" );
400 UTESTCASE( E_long, SIZE_MAX, 0, 0, -1ul, 10, "%lu" );
401 UTESTCASE( E_llong, SIZE_MAX, 0, 0, ULLONG_MAX, 10, "%llu" );
402 UTESTCASE( E_llong, SIZE_MAX, 0, 0, -1ull, 10, "%llu" );
403 puts( "- Hex and Octal, normal and alternative, upper and lowercase -\n" );
404 UTESTCASE( E_done, SIZE_MAX, 0, 0, UINT_MAX, 16, "%X" );
405 UTESTCASE( E_alt, SIZE_MAX, 0, 0, -1u, 16, "%#X" );
406 UTESTCASE( E_done | E_lower, SIZE_MAX, 0, 0, UINT_MAX, 16, "%x" );
407 UTESTCASE( E_alt | E_lower, SIZE_MAX, 0, 0, -1u, 16, "%#x" );
408 UTESTCASE( E_done, SIZE_MAX, 0, 0, UINT_MAX, 8, "%o" );
409 UTESTCASE( E_alt, SIZE_MAX, 0, 0, -1u, 8, "%#o" );
410 puts( "- Plus flag -\n" );
411 TESTCASE( E_plus, SIZE_MAX, 0, 0, INT_MIN, 10, "%+d" );
412 TESTCASE( E_plus, SIZE_MAX, 0, 0, INT_MAX, 10, "%+d" );
413 TESTCASE( E_plus, SIZE_MAX, 0, 0, 0, 10, "%+d" );
414 UTESTCASE( E_plus, SIZE_MAX, 0, 0, UINT_MAX, 10, "%+u" );
415 UTESTCASE( E_plus, SIZE_MAX, 0, 0, -1u, 10, "%+u" );
416 puts( "- Space flag -\n" );
417 TESTCASE( E_space, SIZE_MAX, 0, 0, INT_MIN, 10, "% d" );
418 TESTCASE( E_space, SIZE_MAX, 0, 0, INT_MAX, 10, "% d" );
419 TESTCASE( E_space, SIZE_MAX, 0, 0, 0, 10, "% d" );
420 UTESTCASE( E_space, SIZE_MAX, 0, 0, UINT_MAX, 10, "% u" );
421 UTESTCASE( E_space, SIZE_MAX, 0, 0, -1u, 10, "% u" );
422 puts( "- Field width -\n" );
423 TESTCASE( E_done, SIZE_MAX, 9, 0, INT_MIN, 10, "%9d" );
424 TESTCASE( E_done, SIZE_MAX, 9, 0, INT_MAX, 10, "%9d" );
425 TESTCASE( E_done, SIZE_MAX, 10, 0, INT_MIN, 10, "%10d" );
426 TESTCASE( E_done, SIZE_MAX, 10, 0, INT_MAX, 10, "%10d" );
427 TESTCASE( E_done, SIZE_MAX, 11, 0, INT_MIN, 10, "%11d" );
428 TESTCASE( E_done, SIZE_MAX, 11, 0, INT_MAX, 10, "%11d" );
429 TESTCASE( E_done, SIZE_MAX, 12, 0, INT_MIN, 10, "%12d" );
430 TESTCASE( E_done, SIZE_MAX, 12, 0, INT_MAX, 10, "%12d" );
431 puts( "- Field width (left bound) -\n" );
432 TESTCASE( E_minus, SIZE_MAX, 9, 0, INT_MIN, 10, "%-9d" );
433 TESTCASE( E_minus, SIZE_MAX, 9, 0, INT_MAX, 10, "%-9d" );
434 TESTCASE( E_minus, SIZE_MAX, 10, 0, INT_MIN, 10, "%-10d" );
435 TESTCASE( E_minus, SIZE_MAX, 10, 0, INT_MAX, 10, "%-10d" );
436 TESTCASE( E_minus, SIZE_MAX, 11, 0, INT_MIN, 10, "%-11d" );
437 TESTCASE( E_minus, SIZE_MAX, 11, 0, INT_MAX, 10, "%-11d" );
438 TESTCASE( E_minus, SIZE_MAX, 12, 0, INT_MIN, 10, "%-12d" );
439 TESTCASE( E_minus, SIZE_MAX, 12, 0, INT_MAX, 10, "%-12d" );
440 puts( "- Field width, zero padding -\n");
441 TESTCASE( E_done | E_zero, SIZE_MAX, 9, 0, INT_MIN, 10, "%09d" );
442 TESTCASE( E_done | E_zero, SIZE_MAX, 9, 0, INT_MAX, 10, "%09d" );
443 TESTCASE( E_done | E_zero, SIZE_MAX, 10, 0, INT_MIN, 10, "%010d" );
444 TESTCASE( E_done | E_zero, SIZE_MAX, 10, 0, INT_MAX, 10, "%010d" );
445 TESTCASE( E_done | E_zero, SIZE_MAX, 11, 0, INT_MIN, 10, "%011d" );
446 TESTCASE( E_done | E_zero, SIZE_MAX, 11, 0, INT_MAX, 10, "%011d" );
447 TESTCASE( E_done | E_zero, SIZE_MAX, 12, 0, INT_MIN, 10, "%012d" );
448 TESTCASE( E_done | E_zero, SIZE_MAX, 12, 0, INT_MAX, 10, "%012d" );
449 puts( "- Field width, zero padding (left bound) -\n" );
450 TESTCASE( E_minus | E_zero, SIZE_MAX, 9, 0, INT_MIN, 10, "%-09d" );
451 TESTCASE( E_minus | E_zero, SIZE_MAX, 9, 0, INT_MAX, 10, "%-09d" );
452 TESTCASE( E_minus | E_zero, SIZE_MAX, 10, 0, INT_MIN, 10, "%-010d" );
453 TESTCASE( E_minus | E_zero, SIZE_MAX, 10, 0, INT_MAX, 10, "%-010d" );
454 TESTCASE( E_minus | E_zero, SIZE_MAX, 11, 0, INT_MIN, 10, "%-011d" );
455 TESTCASE( E_minus | E_zero, SIZE_MAX, 11, 0, INT_MAX, 10, "%-011d" );
456 TESTCASE( E_minus | E_zero, SIZE_MAX, 12, 0, INT_MIN, 10, "%-012d" );
457 TESTCASE( E_minus | E_zero, SIZE_MAX, 12, 0, INT_MAX, 10, "%-012d" );