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 */
45 FILE * stream;/* for to-stream output */
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 ) { if ( status->stream != NULL ) putc( x, status->stream ); else 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_unsigned ) )
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 if ( status->flags & E_char )
136 else if ( status->flags & E_short )
138 value = (short)value;
140 else if ( status->flags & E_long )
144 else if ( status->flags & E_llong )
146 value = (long long)value;
148 else if ( status->flags & E_ptrdiff )
150 value = (ptrdiff_t)value;
152 else if ( ! ( status->flags & E_intmax ) )
156 int2base( value, status );
157 if ( status->flags & E_minus )
159 while ( status->this < status->width )
165 if ( status->i >= status->n )
167 status->s[status->n - 1] = '\0';
171 static void upadwrap( uintmax_t value, struct status_t * status )
173 if ( status->flags & E_char )
175 value = (unsigned char)value;
177 else if ( status->flags & E_short )
179 value = (unsigned short)value;
181 else if ( status->flags & E_long )
183 value = (unsigned long)value;
185 else if ( status->flags & E_llong )
187 value = (unsigned long long)value;
189 else if ( status->flags & E_size )
191 value = (size_t)value;
195 value = (unsigned int)value;
197 status->flags |= E_unsigned;
199 if ( ( value / status->base ) != 0 )
201 int2base( (intmax_t)(value / status->base), status );
203 int digit = value % status->base;
208 if ( status->flags & E_lower )
210 DELIVER( _PDCLIB_digits[ digit ] );
214 DELIVER( toupper( _PDCLIB_digits[ digit ] ) );
216 if ( status->flags & E_minus )
218 while ( status->this < status->width )
224 if ( status->i >= status->n )
226 status->s[status->n - 1] = '\0';
230 void parse_out( const char * spec, struct status_t * status, va_list ap );
232 void parse_out( const char * spec, struct status_t * status, va_list ap )
234 /* TODO: "%%" handled correctly? */
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 /* TODO: Decimal point but no number - bad conversion specifier. */
318 /* Optional length modifier
319 We step one character ahead in any case, and step back only if we find
320 there has been no length modifier (or step ahead another character if it
321 has been "hh" or "ll").
328 status->flags |= E_char;
333 status->flags |= E_short;
339 status->flags |= E_llong;
344 status->flags |= E_long;
348 status->flags |= E_intmax;
351 status->flags |= E_size;
354 status->flags |= E_ptrdiff;
357 status->flags |= E_double;
364 /* Conversion specifier */
373 status->flags |= E_unsigned;
377 status->flags |= E_unsigned;
381 status->flags |= ( E_lower | E_unsigned );
385 status->flags |= E_unsigned;
402 /* uint2base( 16, (intptr_t)value, true ) */
405 // conversion specifier
406 /* TODO: May this be accompaigned by flags, width, precision, length modifier at all? */
409 /* TODO: No conversion specifier. Bad conversion. */
412 switch ( status->flags )
416 if ( status->base != 0 )
418 /* Integer conversions */
419 switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_unsigned ) )
422 padwrap( (intmax_t)(char)va_arg( ap, int ), status );
424 case E_char | E_unsigned:
425 upadwrap( (uintmax_t)(unsigned char)va_arg( ap, int ), status );
428 padwrap( (intmax_t)(short)va_arg( ap, int ), status );
430 case E_short | E_unsigned:
431 upadwrap( (uintmax_t)(unsigned short)va_arg( ap, int ), status );
434 padwrap( (intmax_t)va_arg( ap, int ), status );
437 upadwrap( (uintmax_t)va_arg( ap, unsigned int ), status );
440 padwrap( (intmax_t)va_arg( ap, long ), status );
442 case E_long | E_unsigned:
443 upadwrap( (uintmax_t)va_arg( ap, unsigned long ), status );
446 padwrap( (intmax_t)va_arg( ap, long long ), status );
448 case E_llong | E_unsigned:
449 upadwrap( (uintmax_t)va_arg( ap, unsigned long long ), status );
455 void parse_out_wrapper( const char * spec, struct status_t * status, ... );
457 void parse_out_wrapper( const char * spec, struct status_t * status, ... )
460 va_start( ap, status );
461 parse_out( spec, status, ap );
465 #define TESTCASE( _n, _value, _expect ) \
468 memset( status.s, '\0', 50 ); \
471 parse_out_wrapper( spec, &status, _value ); \
472 rc = snprintf( buffer, _n, _expect, _value ); \
473 if ( ( strcmp( status.s, buffer ) != 0 ) || ( status.i != rc ) ) \
475 printf( "Output '%s', RC %d\nExpect '%s', RC %d\n\n", status.s, status.i, buffer, rc ); \
480 struct status_t status;
482 char * buffer = malloc( 50 );
484 status.s = calloc( 50, 1 );
486 status.stream = NULL;
488 puts( "- Signed min / max -\n" );
489 TESTCASE( SIZE_MAX, CHAR_MIN, "%hhd" );
490 TESTCASE( SIZE_MAX, CHAR_MAX, "%hhd" );
491 TESTCASE( SIZE_MAX, 0, "%hhd" );
492 TESTCASE( SIZE_MAX, SHRT_MIN, "%hd" );
493 TESTCASE( SIZE_MAX, SHRT_MAX, "%hd" );
494 TESTCASE( SIZE_MAX, 0, "%hd" );
495 TESTCASE( SIZE_MAX, INT_MIN, "%d" );
496 TESTCASE( SIZE_MAX, INT_MAX, "%d" );
497 TESTCASE( SIZE_MAX, 0, "%d" );
498 TESTCASE( SIZE_MAX, LONG_MIN, "%ld" );
499 TESTCASE( SIZE_MAX, LONG_MAX, "%ld" );
500 TESTCASE( SIZE_MAX, 0l, "%ld" );
501 TESTCASE( SIZE_MAX, LLONG_MIN, "%lld" );
502 TESTCASE( SIZE_MAX, LLONG_MAX, "%lld" );
503 TESTCASE( SIZE_MAX, 0ll, "%lld" );
504 puts( "- Unsigned min / max -\n" );
505 TESTCASE( SIZE_MAX, UCHAR_MAX, "%hhu" );
506 TESTCASE( SIZE_MAX, (unsigned char)-1, "%hhu" );
507 TESTCASE( SIZE_MAX, USHRT_MAX, "%hu" );
508 TESTCASE( SIZE_MAX, (unsigned short)-1, "%hu" );
509 TESTCASE( SIZE_MAX, UINT_MAX, "%u" );
510 TESTCASE( SIZE_MAX, -1u, "%u" );
511 TESTCASE( SIZE_MAX, ULONG_MAX, "%lu" );
512 TESTCASE( SIZE_MAX, -1ul, "%lu" );
513 TESTCASE( SIZE_MAX, ULLONG_MAX, "%llu" );
514 TESTCASE( SIZE_MAX, -1ull, "%llu" );
515 puts( "- Hex and Octal, normal and alternative, upper and lowercase -\n" );
516 TESTCASE( SIZE_MAX, UINT_MAX, "%X" );
517 TESTCASE( SIZE_MAX, -1u, "%#X" );
518 TESTCASE( SIZE_MAX, UINT_MAX, "%x" );
519 TESTCASE( SIZE_MAX, -1u, "%#x" );
520 TESTCASE( SIZE_MAX, UINT_MAX, "%o" );
521 TESTCASE( SIZE_MAX, -1u, "%#o" );
522 puts( "- Plus flag -\n" );
523 TESTCASE( SIZE_MAX, INT_MIN, "%+d" );
524 TESTCASE( SIZE_MAX, INT_MAX, "%+d" );
525 TESTCASE( SIZE_MAX, 0, "%+d" );
526 TESTCASE( SIZE_MAX, UINT_MAX, "%+u" );
527 TESTCASE( SIZE_MAX, -1u, "%+u" );
528 puts( "- Space flag -\n" );
529 TESTCASE( SIZE_MAX, INT_MIN, "% d" );
530 TESTCASE( SIZE_MAX, INT_MAX, "% d" );
531 TESTCASE( SIZE_MAX, 0, "% d" );
532 TESTCASE( SIZE_MAX, UINT_MAX, "% u" );
533 TESTCASE( SIZE_MAX, -1u, "% u" );
534 puts( "- Field width -\n" );
535 TESTCASE( SIZE_MAX, INT_MIN, "%9d" );
536 TESTCASE( SIZE_MAX, INT_MAX, "%9d" );
537 TESTCASE( SIZE_MAX, INT_MIN, "%10d" );
538 TESTCASE( SIZE_MAX, INT_MAX, "%10d" );
539 TESTCASE( SIZE_MAX, INT_MIN, "%11d" );
540 TESTCASE( SIZE_MAX, INT_MAX, "%11d" );
541 TESTCASE( SIZE_MAX, INT_MIN, "%12d" );
542 TESTCASE( SIZE_MAX, INT_MAX, "%12d" );
543 puts( "- Field width (left bound) -\n" );
544 TESTCASE( SIZE_MAX, INT_MIN, "%-9d" );
545 TESTCASE( SIZE_MAX, INT_MAX, "%-9d" );
546 TESTCASE( SIZE_MAX, INT_MIN, "%-10d" );
547 TESTCASE( SIZE_MAX, INT_MAX, "%-10d" );
548 TESTCASE( SIZE_MAX, INT_MIN, "%-11d" );
549 TESTCASE( SIZE_MAX, INT_MAX, "%-11d" );
550 TESTCASE( SIZE_MAX, INT_MIN, "%-12d" );
551 TESTCASE( SIZE_MAX, INT_MAX, "%-12d" );
552 puts( "- Field width, zero padding -\n");
553 TESTCASE( SIZE_MAX, INT_MIN, "%09d" );
554 TESTCASE( SIZE_MAX, INT_MAX, "%09d" );
555 TESTCASE( SIZE_MAX, INT_MIN, "%010d" );
556 TESTCASE( SIZE_MAX, INT_MAX, "%010d" );
557 TESTCASE( SIZE_MAX, INT_MIN, "%011d" );
558 TESTCASE( SIZE_MAX, INT_MAX, "%011d" );
559 TESTCASE( SIZE_MAX, INT_MIN, "%012d" );
560 TESTCASE( SIZE_MAX, INT_MAX, "%012d" );
561 puts( "- Field width, zero padding (left bound) -\n" );
562 TESTCASE( SIZE_MAX, INT_MIN, "%-09d" );
563 TESTCASE( SIZE_MAX, INT_MAX, "%-09d" );
564 TESTCASE( SIZE_MAX, INT_MIN, "%-010d" );
565 TESTCASE( SIZE_MAX, INT_MAX, "%-010d" );
566 TESTCASE( SIZE_MAX, INT_MIN, "%-011d" );
567 TESTCASE( SIZE_MAX, INT_MAX, "%-011d" );
568 TESTCASE( SIZE_MAX, INT_MIN, "%-012d" );
569 TESTCASE( SIZE_MAX, INT_MAX, "%-012d" );