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 void parse_out( const char * spec, va_list ap )
149 /* TODO: '%' handled correctly? */
150 struct status_t status = { 0, 0, 0, 0, 0, NULL, 0, EOF };
151 /* First come 0..n flags */
152 while ( ! ( status.flags & E_done ) )
157 status.flags |= E_minus;
160 status.flags |= E_plus;
163 status.flags |= E_alt;
166 status.flags |= E_space;
169 status.flags |= E_zero;
172 status.flags |= E_done;
178 /* Retrieve width value from argument stack */
179 if ( ( status.width = va_arg( ap, int ) ) < 0 )
181 /* Negative value is '-' flag plus absolute value */
182 status.flags |= E_minus;
189 /* If a width is given, strtol() will return its value. If not given,
190 strtol() will return zero. In both cases, endptr will point to the
191 rest of the conversion specifier.
194 status.width = (int)strtol( spec, &endptr, 10 );
199 if ( *(++spec) == '*' )
201 /* Retrieve precision value from argument stack. A negative value
202 is as if no precision is given - as precision is initalized to
203 EOF (negative), there is no need for testing for negative here.
205 status.prec = va_arg( ap, int );
210 status.prec = (int)strtol( spec, &endptr, 10 );
214 /* We step one character ahead in any case, and step back only if we find
215 there has been no length modifier (or step ahead another character if it
216 has been "hh" or "ll").
223 status.flags |= E_char;
228 status.flags |= E_short;
234 status.flags |= E_llong;
239 status.flags |= E_long;
243 status.flags |= E_intmax;
246 status.flags |= E_size;
249 status.flags |= E_ptrdiff;
252 status.flags |= E_double;
262 /* int2base( 10, value, true ) */
265 /* int2base( 8, value, true ) */
268 /* uint2base( 10, value, true ) */
271 /* uint2base( 16, value, true ) */
274 /* uint2base( 16, value, false ) */
291 /* uint2base( 16, (intptr_t)value, true ) */
294 // conversion specifier
302 #define TESTCASE( _flags, _n, _width, _prec, _value, _base, _expect ) \
303 status.flags = _flags | E_term; \
306 status.width = _width; \
307 status.prec = _prec; \
308 status.base = _base; \
310 memset( status.s, '\0', 50 ); \
311 padwrap( _value, &status ); \
312 rc = snprintf( buffer, _n, _expect, _value ); \
313 if ( ( strcmp( status.s, buffer ) != 0 ) || ( status.i != rc ) ) \
315 printf( "Output '%s', RC %d\nExpect '%s', RC %d\n\n", status.s, status.i, buffer, rc ); \
320 struct status_t status;
323 char * buffer = malloc( 50 );
324 status.s = malloc( 50 );
326 TESTCASE( E_plus, 5, 0, 0, 1234, 10, "%+d" );
327 TESTCASE( E_space, 3, 0, 0, 1234, 10, "% d" );
328 TESTCASE( E_space, 3, 0, 0, -1234, 10, "% d" );
329 TESTCASE( E_plus, 3, 0, 0, -1234, 10, "%+d" );
330 TESTCASE( E_done, 4, 0, 0, 65535, 16, "%X" );
331 TESTCASE( E_lower | E_alt, 4, 0, 0, 65534, 16, "%#x" );
332 TESTCASE( E_done, 4, 0, 0, 62, 8, "%o" );
333 TESTCASE( E_alt, 4, 0, 0, 62, 8, "%#o" );
334 TESTCASE( E_done, 6, 6, 0, 1234, 10, "%6d" );
335 TESTCASE( E_minus, 6, 6, 0, 1234, 10, "%-6d" );
336 TESTCASE( E_minus, 6, 2, 0, 1234, 10, "%-2d" );
337 TESTCASE( E_done, 6, 2, 0, 1234, 10, "%2d" );
338 TESTCASE( E_zero, 6, 6, 0, -1234, 10, "%06d" );
339 /* TODO: These two are *unsigned* conversions! */
340 TESTCASE( E_zero, 7, 7, 0, -65535, 16, "%07X" );
341 TESTCASE( E_zero, 7, 7, 0, -65535, 10, "%07u" );
343 TESTCASE( E_zero | E_minus, 6, 6, 0, 1234, 10, "%-06d" );
344 TESTCASE( E_plus, 6, 6, 0, 1234, 10, "%+6d" );
345 TESTCASE( E_space, 6, 6, 0, 1234, 10, "% 6d" );
346 TESTCASE( E_space, 6, 6, 0, -1234, 10, "% 6d" );
347 TESTCASE( E_space | E_minus, 6, 6, 0, -1234, 10, "%- 6d" );
349 puts( "--- Serious Tests ---\n" );
350 puts( "- Signed min / max -\n" );
351 TESTCASE( E_done, SIZE_MAX, 0, 0, CHAR_MIN, 10, "%hhd" );
352 TESTCASE( E_done, SIZE_MAX, 0, 0, CHAR_MAX, 10, "%hhd" );
353 TESTCASE( E_done, SIZE_MAX, 0, 0, 0, 10, "%hhd" );
354 TESTCASE( E_done, SIZE_MAX, 0, 0, SHRT_MIN, 10, "%hd" );
355 TESTCASE( E_done, SIZE_MAX, 0, 0, SHRT_MAX, 10, "%hd" );
356 TESTCASE( E_done, SIZE_MAX, 0, 0, 0, 10, "%hd" );
357 TESTCASE( E_done, SIZE_MAX, 0, 0, INT_MIN, 10, "%d" );
358 TESTCASE( E_done, SIZE_MAX, 0, 0, INT_MAX, 10, "%d" );
359 TESTCASE( E_done, SIZE_MAX, 0, 0, 0, 10, "%d" );
360 TESTCASE( E_done, SIZE_MAX, 0, 0, LONG_MIN, 10, "%ld" );
361 TESTCASE( E_done, SIZE_MAX, 0, 0, LONG_MAX, 10, "%ld" );
362 TESTCASE( E_done, SIZE_MAX, 0, 0, 0l, 10, "%ld" );
363 TESTCASE( E_done, SIZE_MAX, 0, 0, LLONG_MIN, 10, "%lld" );
364 TESTCASE( E_done, SIZE_MAX, 0, 0, LLONG_MAX, 10, "%lld" );
365 TESTCASE( E_done, SIZE_MAX, 0, 0, 0ll, 10, "%lld" );
366 puts( "- Unsigned min / max -\n" );
367 TESTCASE( E_done, SIZE_MAX, 0, 0, UCHAR_MAX, 10, "%hhu" );
368 TESTCASE( E_done, SIZE_MAX, 0, 0, -1, 10, "%hhu" );
369 TESTCASE( E_done, SIZE_MAX, 0, 0, USHRT_MAX, 10, "%hu" );
370 TESTCASE( E_done, SIZE_MAX, 0, 0, -1, 10, "%hu" );
371 TESTCASE( E_done, SIZE_MAX, 0, 0, UINT_MAX, 10, "%u" );
372 TESTCASE( E_done, SIZE_MAX, 0, 0, -1, 10, "%u" );
373 TESTCASE( E_done, SIZE_MAX, 0, 0, ULONG_MAX, 10, "%lu" );
374 TESTCASE( E_done, SIZE_MAX, 0, 0, -1l, 10, "%lu" );
375 TESTCASE( E_done, SIZE_MAX, 0, 0, ULLONG_MAX, 10, "%llu" );
376 TESTCASE( E_done, SIZE_MAX, 0, 0, -1ll, 10, "%llu" );