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 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 int prec; /* precision of current field */
60 /* x - the character to be delivered
61 i - pointer to number of characters already delivered in this call
62 n - pointer to maximum number of characters to be delivered in this call
63 s - the buffer into which the character shall be delivered
66 #define DELIVER( x ) do { if ( status->i < status->n ) status->s[status->i] = x; ++(status->i); } while ( 0 )
68 /* TODO: Left / right alignment - requires track-keeping of width and printed chars.
69 "Father function", die für right alignment "tail recursive" gerufen wird, und
70 "after" für left alignment? Parameter als struct?
73 static void int2base( intmax_t value, struct status_t * status )
76 if ( ( value / status->base ) != 0 )
78 int2base( value / status->base, status );
82 char preface[3] = "\0\0";
84 if ( ( status->flags & E_alt ) && ( status->base == 16 || status->base == 8 ) )
86 preface[ preidx++ ] = '0';
87 if ( status->base == 16 )
89 preface[ preidx++ ] = ( status->flags & E_lower ) ? 'x' : 'X';
94 preface[ preidx++ ] = '-';
98 if ( status->flags & E_plus )
100 preface[ preidx++ ] = '+';
102 else if ( status->flags & E_space )
104 preface[ preidx++ ] = ' ';
107 if ( ! ( ( status->flags & E_minus ) || ( status->flags & E_zero ) ) )
109 while ( ( status->this + preidx ) < status->width )
116 while ( preface[ preidx ] != '\0' )
118 DELIVER( preface[ preidx++ ] );
121 if ( ( ! ( status->flags & E_minus ) ) && ( status->flags & E_zero ) )
123 while ( status->this < status->width )
131 int digit = value % status->base;
136 if ( status->flags & E_lower )
138 DELIVER( _PDCLIB_digits[ digit ] );
142 DELIVER( toupper( _PDCLIB_digits[ digit ] ) );
147 static void padwrap( intmax_t value, struct status_t * status )
149 int2base( value, status );
150 if ( status->flags & E_minus )
152 while ( status->this < status->width )
158 if ( status->i >= status->n )
160 status->s[status->n - 1] = '\0';
164 void parse_out( const char * spec, va_list ap )
166 /* TODO: '%' handled correctly? */
167 struct status_t status = { 0, 0, 0, 0, 0, NULL, 0, EOF };
168 /* First come 0..n flags */
169 while ( ! ( status.flags & E_done ) )
174 status.flags |= E_minus;
177 status.flags |= E_plus;
180 status.flags |= E_alt;
183 status.flags |= E_space;
186 status.flags |= E_zero;
189 status.flags |= E_done;
195 /* Retrieve width value from argument stack */
196 if ( ( status.width = va_arg( ap, int ) ) < 0 )
198 /* Negative value is '-' flag plus absolute value */
199 status.flags |= E_minus;
206 /* If a width is given, strtol() will return its value. If not given,
207 strtol() will return zero. In both cases, endptr will point to the
208 rest of the conversion specifier.
211 status.width = (int)strtol( spec, &endptr, 10 );
216 if ( *(++spec) == '*' )
218 /* Retrieve precision value from argument stack. A negative value
219 is as if no precision is given - as precision is initalized to
220 EOF (negative), there is no need for testing for negative here.
222 status.prec = va_arg( ap, int );
227 status.prec = (int)strtol( spec, &endptr, 10 );
231 /* We step one character ahead in any case, and step back only if we find
232 there has been no length modifier (or step ahead another character if it
233 has been "hh" or "ll").
240 status.flags |= E_char;
245 status.flags |= E_short;
251 status.flags |= E_llong;
256 status.flags |= E_long;
260 status.flags |= E_intmax;
263 status.flags |= E_size;
266 status.flags |= E_ptrdiff;
269 status.flags |= E_double;
279 /* int2base( 10, value, true ) */
282 /* int2base( 8, value, true ) */
285 /* uint2base( 10, value, true ) */
288 /* uint2base( 16, value, true ) */
291 /* uint2base( 16, value, false ) */
308 /* uint2base( 16, (intptr_t)value, true ) */
311 // conversion specifier
320 static void int2base( int value, int base, struct status_t * status )
332 #define E_intmax 1<<10
334 #define E_ptrdiff 1<<12
335 #define E_double 1<<13
336 #define E_lower 1<<14
349 #define TESTCASE( _flags, _n, _width, _prec, _value, _base, _expect ) \
350 status.flags = _flags | E_term; \
353 status.width = _width; \
354 status.prec = _prec; \
355 status.base = _base; \
357 memset( status.s, '\0', 50 ); \
358 padwrap( _value, &status ); \
359 rc = snprintf( buffer, _n, _expect, _value ); \
360 if ( ( strcmp( status.s, buffer ) != 0 ) || ( status.i != rc ) ) \
362 printf( "Output '%s', RC %d\nExpect '%s', RC %d\n", status.s, status.i, buffer, rc ); \
367 struct status_t status;
370 char * buffer = malloc( 50 );
371 status.s = malloc( 50 );
372 TESTCASE( E_plus, 5, 0, 0, 1234, 10, "%+d" );
373 TESTCASE( E_space, 3, 0, 0, 1234, 10, "% d" );
374 TESTCASE( E_space, 3, 0, 0, -1234, 10, "% d" );
375 TESTCASE( E_plus, 3, 0, 0, -1234, 10, "%+d" );
376 TESTCASE( E_done, 4, 0, 0, 65535, 16, "%X" );
377 TESTCASE( E_lower | E_alt, 4, 0, 0, 65534, 16, "%#x" );
378 TESTCASE( E_done, 4, 0, 0, 62, 8, "%o" );
379 TESTCASE( E_alt, 4, 0, 0, 62, 8, "%#o" );
380 TESTCASE( E_done, 6, 6, 0, 1234, 10, "%6d" );
381 TESTCASE( E_minus, 6, 6, 0, 1234, 10, "%-6d" );
382 TESTCASE( E_minus, 6, 2, 0, 1234, 10, "%-2d" );
383 TESTCASE( E_done, 6, 2, 0, 1234, 10, "%2d" );
384 TESTCASE( E_zero, 6, 6, 0, -1234, 10, "%06d" );
385 /* TODO: These two are *unsigned* conversions! */
386 TESTCASE( E_zero, 7, 7, 0, -65535, 16, "%07X" );
387 TESTCASE( E_zero, 7, 7, 0, -65535, 10, "%07u" );
389 TESTCASE( E_zero | E_minus, 6, 6, 0, 1234, 10, "%-06d" );
390 TESTCASE( E_plus, 6, 6, 0, 1234, 10, "%+6d" );
391 TESTCASE( E_space, 6, 6, 0, 1234, 10, "% 6d" );
392 TESTCASE( E_space, 6, 6, 0, -1234, 10, "% 6d" );
393 TESTCASE( E_space | E_minus, 6, 6, 0, -1234, 10, "%- 6d" );
395 puts( "--- Serious Tests ---" );
396 puts( "- Signed min / max -" );
397 TESTCASE( E_done, SIZE_MAX, 0, 0, CHAR_MIN, 10, "%hhd" );
398 TESTCASE( E_done, SIZE_MAX, 0, 0, CHAR_MAX, 10, "%hhd" );
399 TESTCASE( E_done, SIZE_MAX, 0, 0, 0, 10, "%hhd" );
400 TESTCASE( E_done, SIZE_MAX, 0, 0, SHRT_MIN, 10, "%hd" );
401 TESTCASE( E_done, SIZE_MAX, 0, 0, SHRT_MAX, 10, "%hd" );
402 TESTCASE( E_done, SIZE_MAX, 0, 0, 0, 10, "%hd" );
403 TESTCASE( E_done, SIZE_MAX, 0, 0, INT_MIN, 10, "%d" );
404 TESTCASE( E_done, SIZE_MAX, 0, 0, INT_MAX, 10, "%d" );
405 TESTCASE( E_done, SIZE_MAX, 0, 0, 0, 10, "%d" );
406 TESTCASE( E_done, SIZE_MAX, 0, 0, LONG_MIN, 10, "%ld" );
407 TESTCASE( E_done, SIZE_MAX, 0, 0, LONG_MAX, 10, "%ld" );
408 TESTCASE( E_done, SIZE_MAX, 0, 0, 0l, 10, "%ld" );
409 TESTCASE( E_done, SIZE_MAX, 0, 0, LLONG_MIN, 10, "%lld" );
410 TESTCASE( E_done, SIZE_MAX, 0, 0, LLONG_MAX, 10, "%lld" );
411 TESTCASE( E_done, SIZE_MAX, 0, 0, 0ll, 10, "%lld" );