13 #include </home/solar/src/pdclib/functions/_PDCLIB/digits.c>
15 /* Using an integer's bits as flags for both the conversion flags and length
28 #define E_intmax 1<<10
30 #define E_ptrdiff 1<<12
31 #define E_double 1<<13
33 #define E_unsigned 1<<15
37 int base; /* base to which the value shall be converted */
38 int_fast16_t flags; /* flags and length modifiers */
39 size_t n; /* maximum number of characters to be written */
40 size_t i; /* number of characters already written */
41 size_t this; /* number of output chars in the current conversion */
42 char * s; /* target buffer */
43 size_t width; /* width of current field */
44 size_t prec; /* precision of current field */
47 /* x - the character to be delivered
48 i - pointer to number of characters already delivered in this call
49 n - pointer to maximum number of characters to be delivered in this call
50 s - the buffer into which the character shall be delivered
53 #define DELIVER( x ) do { if ( status->i < status->n ) status->s[status->i] = x; ++(status->i); } while ( 0 )
55 static void int2base( intmax_t value, struct status_t * status )
58 if ( ( value / status->base ) != 0 )
60 int2base( value / status->base, status );
64 char preface[3] = "\0\0";
66 if ( ( status->flags & E_alt ) && ( status->base == 16 || status->base == 8 ) )
68 preface[ preidx++ ] = '0';
69 if ( status->base == 16 )
71 preface[ preidx++ ] = ( status->flags & E_lower ) ? 'x' : 'X';
76 preface[ preidx++ ] = '-';
78 else if ( ! ( status->flags & E_unsigned ) )
80 if ( status->flags & E_plus )
82 preface[ preidx++ ] = '+';
84 else if ( status->flags & E_space )
86 preface[ preidx++ ] = ' ';
89 if ( ! ( ( status->flags & E_minus ) || ( status->flags & E_zero ) ) )
91 while ( ( status->this + preidx ) < status->width )
98 while ( preface[ preidx ] != '\0' )
100 DELIVER( preface[ preidx++ ] );
103 if ( ( ! ( status->flags & E_minus ) ) && ( status->flags & E_zero ) )
105 while ( status->this < status->width )
113 int digit = value % status->base;
118 if ( status->flags & E_lower )
120 DELIVER( _PDCLIB_digits[ digit ] );
124 DELIVER( toupper( _PDCLIB_digits[ digit ] ) );
129 static void padwrap( intmax_t value, struct status_t * status )
131 if ( status->flags & E_char )
135 else if ( status->flags & E_short )
137 value = (short)value;
139 else if ( status->flags & E_long )
143 else if ( status->flags & E_llong )
145 value = (long long)value;
147 else if ( status->flags & E_ptrdiff )
149 value = (ptrdiff_t)value;
151 else if ( ! ( status->flags & E_intmax ) )
155 int2base( value, status );
156 if ( status->flags & E_minus )
158 while ( status->this < status->width )
164 if ( status->i >= status->n )
166 status->s[status->n - 1] = '\0';
170 static void upadwrap( uintmax_t value, struct status_t * status )
172 if ( status->flags & E_char )
174 value = (unsigned char)value;
176 else if ( status->flags & E_short )
178 value = (unsigned short)value;
180 else if ( status->flags & E_long )
182 value = (unsigned long)value;
184 else if ( status->flags & E_llong )
186 value = (unsigned long long)value;
188 else if ( status->flags & E_size )
190 value = (size_t)value;
194 value = (unsigned int)value;
196 status->flags |= E_unsigned;
198 if ( ( value / status->base ) != 0 )
200 int2base( (intmax_t)(value / status->base), status );
202 int digit = value % status->base;
207 if ( status->flags & E_lower )
209 DELIVER( _PDCLIB_digits[ digit ] );
213 DELIVER( toupper( _PDCLIB_digits[ digit ] ) );
215 if ( status->flags & E_minus )
217 while ( status->this < status->width )
223 if ( status->i >= status->n )
225 status->s[status->n - 1] = '\0';
229 void parse_out( const char * spec, struct status_t * status, va_list ap );
231 void parse_out( const char * spec, struct status_t * status, va_list ap )
233 /* TODO: "%%" handled correctly? */
235 /* Initializing status structure */
242 /* First come 0..n flags */
248 status->flags |= E_minus;
252 status->flags |= E_plus;
256 status->flags |= E_alt;
260 status->flags |= E_space;
264 status->flags |= E_zero;
268 status->flags |= E_done;
271 } while ( ! ( status->flags & E_done ) );
273 /* Optional field width */
276 /* Retrieve width value from argument stack */
277 if ( ( status->width = va_arg( ap, int ) ) < 0 )
279 /* Negative value is '-' flag plus absolute value */
280 status->flags |= E_minus;
287 /* If a width is given, strtol() will return its value. If not given,
288 strtol() will return zero. In both cases, endptr will point to the
289 rest of the conversion specifier - just what we need.
291 status->width = (int)strtol( spec, (char**)&spec, 10 );
294 /* Optional precision */
300 /* Retrieve precision value from argument stack. A negative value
301 is as if no precision is given - as precision is initalized to
302 EOF (negative), there is no need for testing for negative here.
304 status->prec = va_arg( ap, int );
309 status->prec = (int)strtol( spec, &endptr, 10 );
310 if ( spec == endptr )
312 /* TODO: Decimal point but no number - bad conversion specifier. */
317 /* Optional length modifier
318 We step one character ahead in any case, and step back only if we find
319 there has been no length modifier (or step ahead another character if it
320 has been "hh" or "ll").
327 status->flags |= E_char;
332 status->flags |= E_short;
338 status->flags |= E_llong;
343 status->flags |= E_long;
347 status->flags |= E_intmax;
350 status->flags |= E_size;
353 status->flags |= E_ptrdiff;
356 status->flags |= E_double;
363 /* Conversion specifier */
375 status->flags |= E_unsigned;
379 status->flags |= ( E_lower | E_unsigned );
382 status->flags = E_unsigned;
400 /* uint2base( 16, (intptr_t)value, true ) */
403 // conversion specifier
404 /* TODO: May this be accompaigned by flags, width, precision, length modifier at all? */
407 /* TODO: No conversion specifier. Bad conversion. */
410 switch ( status->flags )
414 if ( status->base != 0 )
416 /* Integer conversions */
417 switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_unsigned ) )
420 padwrap( (intmax_t)(char)va_arg( ap, int ), status );
422 case E_char | E_unsigned:
423 upadwrap( (uintmax_t)(unsigned char)va_arg( ap, int ), status );
426 padwrap( (intmax_t)(short)va_arg( ap, int ), status );
428 case E_short | E_unsigned:
429 upadwrap( (uintmax_t)(unsigned short)va_arg( ap, int ), status );
432 padwrap( (intmax_t)va_arg( ap, int ), status );
435 upadwrap( (uintmax_t)va_arg( ap, unsigned int ), status );
438 padwrap( (intmax_t)va_arg( ap, long ), status );
440 case E_long | E_unsigned:
441 upadwrap( (uintmax_t)va_arg( ap, unsigned long ), status );
444 padwrap( (intmax_t)va_arg( ap, long long ), status );
446 case E_llong | E_unsigned:
447 upadwrap( (uintmax_t)va_arg( ap, unsigned long long ), status );
453 void parse_out_wrapper( const char * spec, struct status_t * status, ... );
455 void parse_out_wrapper( const char * spec, struct status_t * status, ... )
458 va_start( ap, status );
459 parse_out( spec, status, ap );
463 #define TESTCASE( _flags, _n, _width, _prec, _value, _base, _expect ) \
464 status.flags = _flags; \
467 status.width = _width; \
468 status.prec = _prec; \
469 status.base = _base; \
471 memset( status.s, '\0', 50 ); \
472 padwrap( (intmax_t)_value, &status ); \
473 rc = snprintf( buffer, _n, _expect, _value ); \
474 if ( ( strcmp( status.s, buffer ) != 0 ) || ( status.i != rc ) ) \
476 printf( "Output '%s', RC %d\nExpect '%s', RC %d\n\n", status.s, status.i, buffer, rc ); \
479 #define UTESTCASE( _flags, _n, _width, _prec, _value, _base, _expect ) \
480 status.flags = _flags; \
483 status.width = _width; \
484 status.prec = _prec; \
485 status.base = _base; \
487 memset( status.s, '\0', 50 ); \
488 upadwrap( (uintmax_t)_value, &status ); \
489 rc = snprintf( buffer, _n, _expect, _value ); \
490 if ( ( strcmp( status.s, buffer ) != 0 ) || ( status.i != rc ) ) \
492 printf( "Output '%s', RC %d\nExpect '%s', RC %d\n\n", status.s, status.i, buffer, rc ); \
497 struct status_t status;
499 char * buffer = malloc( 50 );
500 status.s = calloc( 50, 1 );
503 puts( "- parse_out() -\n" );
504 parse_out_wrapper( "d", &status, 1234 );
506 puts( "- Signed min / max -\n" );
507 TESTCASE( E_char, SIZE_MAX, 0, 0, CHAR_MIN, 10, "%hhd" );
508 TESTCASE( E_char, SIZE_MAX, 0, 0, CHAR_MAX, 10, "%hhd" );
509 TESTCASE( E_char, SIZE_MAX, 0, 0, 0, 10, "%hhd" );
510 TESTCASE( E_short, SIZE_MAX, 0, 0, SHRT_MIN, 10, "%hd" );
511 TESTCASE( E_short, SIZE_MAX, 0, 0, SHRT_MAX, 10, "%hd" );
512 TESTCASE( E_short, SIZE_MAX, 0, 0, 0, 10, "%hd" );
513 TESTCASE( E_done, SIZE_MAX, 0, 0, INT_MIN, 10, "%d" );
514 TESTCASE( E_done, SIZE_MAX, 0, 0, INT_MAX, 10, "%d" );
515 TESTCASE( E_done, SIZE_MAX, 0, 0, 0, 10, "%d" );
516 TESTCASE( E_long, SIZE_MAX, 0, 0, LONG_MIN, 10, "%ld" );
517 TESTCASE( E_long, SIZE_MAX, 0, 0, LONG_MAX, 10, "%ld" );
518 TESTCASE( E_long, SIZE_MAX, 0, 0, 0l, 10, "%ld" );
519 TESTCASE( E_llong, SIZE_MAX, 0, 0, LLONG_MIN, 10, "%lld" );
520 TESTCASE( E_llong, SIZE_MAX, 0, 0, LLONG_MAX, 10, "%lld" );
521 TESTCASE( E_llong, SIZE_MAX, 0, 0, 0ll, 10, "%lld" );
522 puts( "- Unsigned min / max -\n" );
523 UTESTCASE( E_char, SIZE_MAX, 0, 0, UCHAR_MAX, 10, "%hhu" );
524 UTESTCASE( E_char, SIZE_MAX, 0, 0, (unsigned char)-1, 10, "%hhu" );
525 UTESTCASE( E_short, SIZE_MAX, 0, 0, USHRT_MAX, 10, "%hu" );
526 UTESTCASE( E_short, SIZE_MAX, 0, 0, (unsigned short)-1, 10, "%hu" );
527 UTESTCASE( E_done, SIZE_MAX, 0, 0, UINT_MAX, 10, "%u" );
528 UTESTCASE( E_done, SIZE_MAX, 0, 0, -1u, 10, "%u" );
529 UTESTCASE( E_long, SIZE_MAX, 0, 0, ULONG_MAX, 10, "%lu" );
530 UTESTCASE( E_long, SIZE_MAX, 0, 0, -1ul, 10, "%lu" );
531 UTESTCASE( E_llong, SIZE_MAX, 0, 0, ULLONG_MAX, 10, "%llu" );
532 UTESTCASE( E_llong, SIZE_MAX, 0, 0, -1ull, 10, "%llu" );
533 puts( "- Hex and Octal, normal and alternative, upper and lowercase -\n" );
534 UTESTCASE( E_done, SIZE_MAX, 0, 0, UINT_MAX, 16, "%X" );
535 UTESTCASE( E_alt, SIZE_MAX, 0, 0, -1u, 16, "%#X" );
536 UTESTCASE( E_done | E_lower, SIZE_MAX, 0, 0, UINT_MAX, 16, "%x" );
537 UTESTCASE( E_alt | E_lower, SIZE_MAX, 0, 0, -1u, 16, "%#x" );
538 UTESTCASE( E_done, SIZE_MAX, 0, 0, UINT_MAX, 8, "%o" );
539 UTESTCASE( E_alt, SIZE_MAX, 0, 0, -1u, 8, "%#o" );
540 puts( "- Plus flag -\n" );
541 TESTCASE( E_plus, SIZE_MAX, 0, 0, INT_MIN, 10, "%+d" );
542 TESTCASE( E_plus, SIZE_MAX, 0, 0, INT_MAX, 10, "%+d" );
543 TESTCASE( E_plus, SIZE_MAX, 0, 0, 0, 10, "%+d" );
544 UTESTCASE( E_plus, SIZE_MAX, 0, 0, UINT_MAX, 10, "%+u" );
545 UTESTCASE( E_plus, SIZE_MAX, 0, 0, -1u, 10, "%+u" );
546 puts( "- Space flag -\n" );
547 TESTCASE( E_space, SIZE_MAX, 0, 0, INT_MIN, 10, "% d" );
548 TESTCASE( E_space, SIZE_MAX, 0, 0, INT_MAX, 10, "% d" );
549 TESTCASE( E_space, SIZE_MAX, 0, 0, 0, 10, "% d" );
550 UTESTCASE( E_space, SIZE_MAX, 0, 0, UINT_MAX, 10, "% u" );
551 UTESTCASE( E_space, SIZE_MAX, 0, 0, -1u, 10, "% u" );
552 puts( "- Field width -\n" );
553 TESTCASE( E_done, SIZE_MAX, 9, 0, INT_MIN, 10, "%9d" );
554 TESTCASE( E_done, SIZE_MAX, 9, 0, INT_MAX, 10, "%9d" );
555 TESTCASE( E_done, SIZE_MAX, 10, 0, INT_MIN, 10, "%10d" );
556 TESTCASE( E_done, SIZE_MAX, 10, 0, INT_MAX, 10, "%10d" );
557 TESTCASE( E_done, SIZE_MAX, 11, 0, INT_MIN, 10, "%11d" );
558 TESTCASE( E_done, SIZE_MAX, 11, 0, INT_MAX, 10, "%11d" );
559 TESTCASE( E_done, SIZE_MAX, 12, 0, INT_MIN, 10, "%12d" );
560 TESTCASE( E_done, SIZE_MAX, 12, 0, INT_MAX, 10, "%12d" );
561 puts( "- Field width (left bound) -\n" );
562 TESTCASE( E_minus, SIZE_MAX, 9, 0, INT_MIN, 10, "%-9d" );
563 TESTCASE( E_minus, SIZE_MAX, 9, 0, INT_MAX, 10, "%-9d" );
564 TESTCASE( E_minus, SIZE_MAX, 10, 0, INT_MIN, 10, "%-10d" );
565 TESTCASE( E_minus, SIZE_MAX, 10, 0, INT_MAX, 10, "%-10d" );
566 TESTCASE( E_minus, SIZE_MAX, 11, 0, INT_MIN, 10, "%-11d" );
567 TESTCASE( E_minus, SIZE_MAX, 11, 0, INT_MAX, 10, "%-11d" );
568 TESTCASE( E_minus, SIZE_MAX, 12, 0, INT_MIN, 10, "%-12d" );
569 TESTCASE( E_minus, SIZE_MAX, 12, 0, INT_MAX, 10, "%-12d" );
570 puts( "- Field width, zero padding -\n");
571 TESTCASE( E_done | E_zero, SIZE_MAX, 9, 0, INT_MIN, 10, "%09d" );
572 TESTCASE( E_done | E_zero, SIZE_MAX, 9, 0, INT_MAX, 10, "%09d" );
573 TESTCASE( E_done | E_zero, SIZE_MAX, 10, 0, INT_MIN, 10, "%010d" );
574 TESTCASE( E_done | E_zero, SIZE_MAX, 10, 0, INT_MAX, 10, "%010d" );
575 TESTCASE( E_done | E_zero, SIZE_MAX, 11, 0, INT_MIN, 10, "%011d" );
576 TESTCASE( E_done | E_zero, SIZE_MAX, 11, 0, INT_MAX, 10, "%011d" );
577 TESTCASE( E_done | E_zero, SIZE_MAX, 12, 0, INT_MIN, 10, "%012d" );
578 TESTCASE( E_done | E_zero, SIZE_MAX, 12, 0, INT_MAX, 10, "%012d" );
579 puts( "- Field width, zero padding (left bound) -\n" );
580 TESTCASE( E_minus | E_zero, SIZE_MAX, 9, 0, INT_MIN, 10, "%-09d" );
581 TESTCASE( E_minus | E_zero, SIZE_MAX, 9, 0, INT_MAX, 10, "%-09d" );
582 TESTCASE( E_minus | E_zero, SIZE_MAX, 10, 0, INT_MIN, 10, "%-010d" );
583 TESTCASE( E_minus | E_zero, SIZE_MAX, 10, 0, INT_MAX, 10, "%-010d" );
584 TESTCASE( E_minus | E_zero, SIZE_MAX, 11, 0, INT_MIN, 10, "%-011d" );
585 TESTCASE( E_minus | E_zero, SIZE_MAX, 11, 0, INT_MAX, 10, "%-011d" );
586 TESTCASE( E_minus | E_zero, SIZE_MAX, 12, 0, INT_MIN, 10, "%-012d" );
587 TESTCASE( E_minus | E_zero, SIZE_MAX, 12, 0, INT_MAX, 10, "%-012d" );