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 const char * parse_out( const char * spec, struct status_t * status, va_list ap );
49 const char * parse_out_wrapper( const char * spec, struct status_t * status, ... );
50 int _PDCLIB_printf( FILE * stream, const char * format, va_list ap );
52 #define TESTCASE( _n, _value, _expect ) \
55 memset( status.s, '\0', 50 ); \
58 parse_out_wrapper( spec, &status, _value ); \
59 rc = snprintf( buffer, _n, _expect, _value ); \
60 if ( ( strcmp( status.s, buffer ) != 0 ) || ( status.i != rc ) ) \
62 printf( "Output '%s', RC %d\nExpect '%s', RC %d\n\n", status.s, status.i, buffer, rc ); \
67 struct status_t status;
69 char * buffer = malloc( 50 );
71 status.s = calloc( 50, 1 );
75 puts( "- Signed min / max -\n" );
76 TESTCASE( SIZE_MAX, CHAR_MIN, "%hhd" );
77 TESTCASE( SIZE_MAX, CHAR_MAX, "%hhd" );
78 TESTCASE( SIZE_MAX, 0, "%hhd" );
79 TESTCASE( SIZE_MAX, SHRT_MIN, "%hd" );
80 TESTCASE( SIZE_MAX, SHRT_MAX, "%hd" );
81 TESTCASE( SIZE_MAX, 0, "%hd" );
82 TESTCASE( SIZE_MAX, INT_MIN, "%d" );
83 TESTCASE( SIZE_MAX, INT_MAX, "%d" );
84 TESTCASE( SIZE_MAX, 0, "%d" );
85 TESTCASE( SIZE_MAX, LONG_MIN, "%ld" );
86 TESTCASE( SIZE_MAX, LONG_MAX, "%ld" );
87 TESTCASE( SIZE_MAX, 0l, "%ld" );
88 TESTCASE( SIZE_MAX, LLONG_MIN, "%lld" );
89 TESTCASE( SIZE_MAX, LLONG_MAX, "%lld" );
90 TESTCASE( SIZE_MAX, 0ll, "%lld" );
91 puts( "- Unsigned min / max -\n" );
92 TESTCASE( SIZE_MAX, UCHAR_MAX, "%hhu" );
93 TESTCASE( SIZE_MAX, (unsigned char)-1, "%hhu" );
94 TESTCASE( SIZE_MAX, USHRT_MAX, "%hu" );
95 TESTCASE( SIZE_MAX, (unsigned short)-1, "%hu" );
96 TESTCASE( SIZE_MAX, UINT_MAX, "%u" );
97 TESTCASE( SIZE_MAX, -1u, "%u" );
98 TESTCASE( SIZE_MAX, ULONG_MAX, "%lu" );
99 TESTCASE( SIZE_MAX, -1ul, "%lu" );
100 TESTCASE( SIZE_MAX, ULLONG_MAX, "%llu" );
101 TESTCASE( SIZE_MAX, -1ull, "%llu" );
102 puts( "- Hex and Octal, normal and alternative, upper and lowercase -\n" );
103 TESTCASE( SIZE_MAX, UINT_MAX, "%X" );
104 TESTCASE( SIZE_MAX, -1u, "%#X" );
105 TESTCASE( SIZE_MAX, UINT_MAX, "%x" );
106 TESTCASE( SIZE_MAX, -1u, "%#x" );
107 TESTCASE( SIZE_MAX, UINT_MAX, "%o" );
108 TESTCASE( SIZE_MAX, -1u, "%#o" );
109 puts( "- Plus flag -\n" );
110 TESTCASE( SIZE_MAX, INT_MIN, "%+d" );
111 TESTCASE( SIZE_MAX, INT_MAX, "%+d" );
112 TESTCASE( SIZE_MAX, 0, "%+d" );
113 TESTCASE( SIZE_MAX, UINT_MAX, "%+u" );
114 TESTCASE( SIZE_MAX, -1u, "%+u" );
115 puts( "- Space flag -\n" );
116 TESTCASE( SIZE_MAX, INT_MIN, "% d" );
117 TESTCASE( SIZE_MAX, INT_MAX, "% d" );
118 TESTCASE( SIZE_MAX, 0, "% d" );
119 TESTCASE( SIZE_MAX, UINT_MAX, "% u" );
120 TESTCASE( SIZE_MAX, -1u, "% u" );
121 puts( "- Field width -\n" );
122 TESTCASE( SIZE_MAX, INT_MIN, "%9d" );
123 TESTCASE( SIZE_MAX, INT_MAX, "%9d" );
124 TESTCASE( SIZE_MAX, INT_MIN, "%10d" );
125 TESTCASE( SIZE_MAX, INT_MAX, "%10d" );
126 TESTCASE( SIZE_MAX, INT_MIN, "%11d" );
127 TESTCASE( SIZE_MAX, INT_MAX, "%11d" );
128 TESTCASE( SIZE_MAX, INT_MIN, "%12d" );
129 TESTCASE( SIZE_MAX, INT_MAX, "%12d" );
130 puts( "- Field width (left bound) -\n" );
131 TESTCASE( SIZE_MAX, INT_MIN, "%-9d" );
132 TESTCASE( SIZE_MAX, INT_MAX, "%-9d" );
133 TESTCASE( SIZE_MAX, INT_MIN, "%-10d" );
134 TESTCASE( SIZE_MAX, INT_MAX, "%-10d" );
135 TESTCASE( SIZE_MAX, INT_MIN, "%-11d" );
136 TESTCASE( SIZE_MAX, INT_MAX, "%-11d" );
137 TESTCASE( SIZE_MAX, INT_MIN, "%-12d" );
138 TESTCASE( SIZE_MAX, INT_MAX, "%-12d" );
139 puts( "- Field width, zero padding -\n");
140 TESTCASE( SIZE_MAX, INT_MIN, "%09d" );
141 TESTCASE( SIZE_MAX, INT_MAX, "%09d" );
142 TESTCASE( SIZE_MAX, INT_MIN, "%010d" );
143 TESTCASE( SIZE_MAX, INT_MAX, "%010d" );
144 TESTCASE( SIZE_MAX, INT_MIN, "%011d" );
145 TESTCASE( SIZE_MAX, INT_MAX, "%011d" );
146 TESTCASE( SIZE_MAX, INT_MIN, "%012d" );
147 TESTCASE( SIZE_MAX, INT_MAX, "%012d" );
148 puts( "- Field width, zero padding (left bound) -\n" );
149 TESTCASE( SIZE_MAX, INT_MIN, "%-09d" );
150 TESTCASE( SIZE_MAX, INT_MAX, "%-09d" );
151 TESTCASE( SIZE_MAX, INT_MIN, "%-010d" );
152 TESTCASE( SIZE_MAX, INT_MAX, "%-010d" );
153 TESTCASE( SIZE_MAX, INT_MIN, "%-011d" );
154 TESTCASE( SIZE_MAX, INT_MAX, "%-011d" );
155 TESTCASE( SIZE_MAX, INT_MIN, "%-012d" );
156 TESTCASE( SIZE_MAX, INT_MAX, "%-012d" );
160 /* x - the character to be delivered
161 i - pointer to number of characters already delivered in this call
162 n - pointer to maximum number of characters to be delivered in this call
163 s - the buffer into which the character shall be delivered
166 #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 )
168 static void int2base( intmax_t value, struct status_t * status )
171 if ( ( value / status->base ) != 0 )
173 int2base( value / status->base, status );
177 char preface[3] = "\0\0";
179 if ( ( status->flags & E_alt ) && ( status->base == 16 || status->base == 8 ) )
181 preface[ preidx++ ] = '0';
182 if ( status->base == 16 )
184 preface[ preidx++ ] = ( status->flags & E_lower ) ? 'x' : 'X';
189 preface[ preidx++ ] = '-';
191 else if ( ! ( status->flags & E_unsigned ) )
193 if ( status->flags & E_plus )
195 preface[ preidx++ ] = '+';
197 else if ( status->flags & E_space )
199 preface[ preidx++ ] = ' ';
202 if ( ! ( ( status->flags & E_minus ) || ( status->flags & E_zero ) ) )
204 while ( ( status->this + preidx ) < status->width )
211 while ( preface[ preidx ] != '\0' )
213 DELIVER( preface[ preidx++ ] );
216 if ( ( ! ( status->flags & E_minus ) ) && ( status->flags & E_zero ) )
218 while ( status->this < status->width )
226 int digit = value % status->base;
231 if ( status->flags & E_lower )
233 DELIVER( _PDCLIB_digits[ digit ] );
237 DELIVER( toupper( _PDCLIB_digits[ digit ] ) );
242 const char * parse_out( const char * spec, struct status_t * status, va_list ap )
244 const char * orig_spec = spec;
246 if ( *(++spec) == '%' )
252 /* Initializing status structure */
259 /* First come 0..n flags */
265 status->flags |= E_minus;
269 status->flags |= E_plus;
273 status->flags |= E_alt;
277 status->flags |= E_space;
281 status->flags |= E_zero;
285 status->flags |= E_done;
288 } while ( ! ( status->flags & E_done ) );
290 /* Optional field width */
293 /* Retrieve width value from argument stack */
294 if ( ( status->width = va_arg( ap, int ) ) < 0 )
296 /* Negative value is '-' flag plus absolute value */
297 status->flags |= E_minus;
304 /* If a width is given, strtol() will return its value. If not given,
305 strtol() will return zero. In both cases, endptr will point to the
306 rest of the conversion specifier - just what we need.
308 status->width = (int)strtol( spec, (char**)&spec, 10 );
311 /* Optional precision */
317 /* Retrieve precision value from argument stack. A negative value
318 is as if no precision is given - as precision is initalized to
319 EOF (negative), there is no need for testing for negative here.
321 status->prec = va_arg( ap, int );
326 status->prec = (int)strtol( spec, &endptr, 10 );
327 if ( spec == endptr )
329 /* TODO: Decimal point but no number - bad conversion specifier. */
334 /* Optional length modifier
335 We step one character ahead in any case, and step back only if we find
336 there has been no length modifier (or step ahead another character if it
337 has been "hh" or "ll").
344 status->flags |= E_char;
349 status->flags |= E_short;
355 status->flags |= E_llong;
360 status->flags |= E_long;
364 status->flags |= E_intmax;
367 status->flags |= E_size;
370 status->flags |= E_ptrdiff;
373 status->flags |= E_double;
380 /* Conversion specifier */
389 status->flags |= E_unsigned;
393 status->flags |= E_unsigned;
397 status->flags |= ( E_lower | E_unsigned );
401 status->flags |= E_unsigned;
418 /* uint2base( 16, (intptr_t)value, true ) */
422 /* No conversion specifier. Bad conversion. */
426 /* Do the actual output based on our findings */
427 if ( status->base != 0 )
429 /* Integer conversions */
430 /* TODO: Check for invalid flag combinations. */
431 if ( status->flags & E_unsigned )
434 switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_size ) )
437 value = (uintmax_t)(unsigned char)va_arg( ap, int );
440 value = (uintmax_t)(unsigned short)va_arg( ap, int );
443 value = (uintmax_t)va_arg( ap, unsigned int );
446 value = (uintmax_t)va_arg( ap, unsigned long );
449 value = (uintmax_t)va_arg( ap, unsigned long long );
452 value = (uintmax_t)va_arg( ap, size_t );
456 if ( ( value / status->base ) != 0 )
458 int2base( (intmax_t)(value / status->base), status );
460 int digit = value % status->base;
465 if ( status->flags & E_lower )
467 DELIVER( _PDCLIB_digits[ digit ] );
471 DELIVER( toupper( _PDCLIB_digits[ digit ] ) );
476 switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_intmax ) )
479 int2base( (intmax_t)(char)va_arg( ap, int ), status );
482 int2base( (intmax_t)(short)va_arg( ap, int ), status );
485 int2base( (intmax_t)va_arg( ap, int ), status );
488 int2base( (intmax_t)va_arg( ap, long ), status );
491 int2base( (intmax_t)va_arg( ap, long long ), status );
494 int2base( (intmax_t)va_arg( ap, ptrdiff_t ), status );
497 int2base( va_arg( ap, intmax_t ), status );
501 if ( status->flags & E_minus )
503 while ( status->this < status->width )
509 if ( status->i >= status->n )
511 status->s[status->n - 1] = '\0';
517 const char * parse_out_wrapper( const char * spec, struct status_t * status, ... )
521 va_start( ap, status );
522 rc = parse_out( spec, status, ap );
527 int _PDCLIB_printf( FILE * stream, const char * format, va_list ap )
529 char * buffer = malloc( 50 );
530 struct status_t status = { 0, 0, SIZE_MAX, 0, 0, /* stream->buffer */ buffer, 0, 0, stream };
531 while ( *format != '\0' )
534 if ( ( *format != '%' ) || ( ( rc = parse_out( format, &status, ap ) ) == format ) )
536 /* No conversion specifier, print verbatim */
537 putc( *(format++), stream );
541 /* Continue parsing after conversion specifier */