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 void parse_out( const char * spec, struct status_t * status, va_list ap );
49 void parse_out_wrapper( const char * spec, struct status_t * status, ... );
51 #define TESTCASE( _n, _value, _expect ) \
54 memset( status.s, '\0', 50 ); \
57 parse_out_wrapper( spec, &status, _value ); \
58 rc = snprintf( buffer, _n, _expect, _value ); \
59 if ( ( strcmp( status.s, buffer ) != 0 ) || ( status.i != rc ) ) \
61 printf( "Output '%s', RC %d\nExpect '%s', RC %d\n\n", status.s, status.i, buffer, rc ); \
66 struct status_t status;
68 char * buffer = malloc( 50 );
70 status.s = calloc( 50, 1 );
74 puts( "- Signed min / max -\n" );
75 TESTCASE( SIZE_MAX, CHAR_MIN, "%hhd" );
76 TESTCASE( SIZE_MAX, CHAR_MAX, "%hhd" );
77 TESTCASE( SIZE_MAX, 0, "%hhd" );
78 TESTCASE( SIZE_MAX, SHRT_MIN, "%hd" );
79 TESTCASE( SIZE_MAX, SHRT_MAX, "%hd" );
80 TESTCASE( SIZE_MAX, 0, "%hd" );
81 TESTCASE( SIZE_MAX, INT_MIN, "%d" );
82 TESTCASE( SIZE_MAX, INT_MAX, "%d" );
83 TESTCASE( SIZE_MAX, 0, "%d" );
84 TESTCASE( SIZE_MAX, LONG_MIN, "%ld" );
85 TESTCASE( SIZE_MAX, LONG_MAX, "%ld" );
86 TESTCASE( SIZE_MAX, 0l, "%ld" );
87 TESTCASE( SIZE_MAX, LLONG_MIN, "%lld" );
88 TESTCASE( SIZE_MAX, LLONG_MAX, "%lld" );
89 TESTCASE( SIZE_MAX, 0ll, "%lld" );
90 puts( "- Unsigned min / max -\n" );
91 TESTCASE( SIZE_MAX, UCHAR_MAX, "%hhu" );
92 TESTCASE( SIZE_MAX, (unsigned char)-1, "%hhu" );
93 TESTCASE( SIZE_MAX, USHRT_MAX, "%hu" );
94 TESTCASE( SIZE_MAX, (unsigned short)-1, "%hu" );
95 TESTCASE( SIZE_MAX, UINT_MAX, "%u" );
96 TESTCASE( SIZE_MAX, -1u, "%u" );
97 TESTCASE( SIZE_MAX, ULONG_MAX, "%lu" );
98 TESTCASE( SIZE_MAX, -1ul, "%lu" );
99 TESTCASE( SIZE_MAX, ULLONG_MAX, "%llu" );
100 TESTCASE( SIZE_MAX, -1ull, "%llu" );
101 puts( "- Hex and Octal, normal and alternative, upper and lowercase -\n" );
102 TESTCASE( SIZE_MAX, UINT_MAX, "%X" );
103 TESTCASE( SIZE_MAX, -1u, "%#X" );
104 TESTCASE( SIZE_MAX, UINT_MAX, "%x" );
105 TESTCASE( SIZE_MAX, -1u, "%#x" );
106 TESTCASE( SIZE_MAX, UINT_MAX, "%o" );
107 TESTCASE( SIZE_MAX, -1u, "%#o" );
108 puts( "- Plus flag -\n" );
109 TESTCASE( SIZE_MAX, INT_MIN, "%+d" );
110 TESTCASE( SIZE_MAX, INT_MAX, "%+d" );
111 TESTCASE( SIZE_MAX, 0, "%+d" );
112 TESTCASE( SIZE_MAX, UINT_MAX, "%+u" );
113 TESTCASE( SIZE_MAX, -1u, "%+u" );
114 puts( "- Space flag -\n" );
115 TESTCASE( SIZE_MAX, INT_MIN, "% d" );
116 TESTCASE( SIZE_MAX, INT_MAX, "% d" );
117 TESTCASE( SIZE_MAX, 0, "% d" );
118 TESTCASE( SIZE_MAX, UINT_MAX, "% u" );
119 TESTCASE( SIZE_MAX, -1u, "% u" );
120 puts( "- Field width -\n" );
121 TESTCASE( SIZE_MAX, INT_MIN, "%9d" );
122 TESTCASE( SIZE_MAX, INT_MAX, "%9d" );
123 TESTCASE( SIZE_MAX, INT_MIN, "%10d" );
124 TESTCASE( SIZE_MAX, INT_MAX, "%10d" );
125 TESTCASE( SIZE_MAX, INT_MIN, "%11d" );
126 TESTCASE( SIZE_MAX, INT_MAX, "%11d" );
127 TESTCASE( SIZE_MAX, INT_MIN, "%12d" );
128 TESTCASE( SIZE_MAX, INT_MAX, "%12d" );
129 puts( "- Field width (left bound) -\n" );
130 TESTCASE( SIZE_MAX, INT_MIN, "%-9d" );
131 TESTCASE( SIZE_MAX, INT_MAX, "%-9d" );
132 TESTCASE( SIZE_MAX, INT_MIN, "%-10d" );
133 TESTCASE( SIZE_MAX, INT_MAX, "%-10d" );
134 TESTCASE( SIZE_MAX, INT_MIN, "%-11d" );
135 TESTCASE( SIZE_MAX, INT_MAX, "%-11d" );
136 TESTCASE( SIZE_MAX, INT_MIN, "%-12d" );
137 TESTCASE( SIZE_MAX, INT_MAX, "%-12d" );
138 puts( "- Field width, zero padding -\n");
139 TESTCASE( SIZE_MAX, INT_MIN, "%09d" );
140 TESTCASE( SIZE_MAX, INT_MAX, "%09d" );
141 TESTCASE( SIZE_MAX, INT_MIN, "%010d" );
142 TESTCASE( SIZE_MAX, INT_MAX, "%010d" );
143 TESTCASE( SIZE_MAX, INT_MIN, "%011d" );
144 TESTCASE( SIZE_MAX, INT_MAX, "%011d" );
145 TESTCASE( SIZE_MAX, INT_MIN, "%012d" );
146 TESTCASE( SIZE_MAX, INT_MAX, "%012d" );
147 puts( "- Field width, zero padding (left bound) -\n" );
148 TESTCASE( SIZE_MAX, INT_MIN, "%-09d" );
149 TESTCASE( SIZE_MAX, INT_MAX, "%-09d" );
150 TESTCASE( SIZE_MAX, INT_MIN, "%-010d" );
151 TESTCASE( SIZE_MAX, INT_MAX, "%-010d" );
152 TESTCASE( SIZE_MAX, INT_MIN, "%-011d" );
153 TESTCASE( SIZE_MAX, INT_MAX, "%-011d" );
154 TESTCASE( SIZE_MAX, INT_MIN, "%-012d" );
155 TESTCASE( SIZE_MAX, INT_MAX, "%-012d" );
159 /* x - the character to be delivered
160 i - pointer to number of characters already delivered in this call
161 n - pointer to maximum number of characters to be delivered in this call
162 s - the buffer into which the character shall be delivered
165 #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 )
167 static void int2base( intmax_t value, struct status_t * status )
170 if ( ( value / status->base ) != 0 )
172 int2base( value / status->base, status );
176 char preface[3] = "\0\0";
178 if ( ( status->flags & E_alt ) && ( status->base == 16 || status->base == 8 ) )
180 preface[ preidx++ ] = '0';
181 if ( status->base == 16 )
183 preface[ preidx++ ] = ( status->flags & E_lower ) ? 'x' : 'X';
188 preface[ preidx++ ] = '-';
190 else if ( ! ( status->flags & E_unsigned ) )
192 if ( status->flags & E_plus )
194 preface[ preidx++ ] = '+';
196 else if ( status->flags & E_space )
198 preface[ preidx++ ] = ' ';
201 if ( ! ( ( status->flags & E_minus ) || ( status->flags & E_zero ) ) )
203 while ( ( status->this + preidx ) < status->width )
210 while ( preface[ preidx ] != '\0' )
212 DELIVER( preface[ preidx++ ] );
215 if ( ( ! ( status->flags & E_minus ) ) && ( status->flags & E_zero ) )
217 while ( status->this < status->width )
225 int digit = value % status->base;
230 if ( status->flags & E_lower )
232 DELIVER( _PDCLIB_digits[ digit ] );
236 DELIVER( toupper( _PDCLIB_digits[ digit ] ) );
241 void parse_out( const char * spec, struct status_t * status, va_list ap )
243 /* TODO: "%%" handled correctly? */
245 /* Initializing status structure */
252 /* First come 0..n flags */
258 status->flags |= E_minus;
262 status->flags |= E_plus;
266 status->flags |= E_alt;
270 status->flags |= E_space;
274 status->flags |= E_zero;
278 status->flags |= E_done;
281 } while ( ! ( status->flags & E_done ) );
283 /* Optional field width */
286 /* Retrieve width value from argument stack */
287 if ( ( status->width = va_arg( ap, int ) ) < 0 )
289 /* Negative value is '-' flag plus absolute value */
290 status->flags |= E_minus;
297 /* If a width is given, strtol() will return its value. If not given,
298 strtol() will return zero. In both cases, endptr will point to the
299 rest of the conversion specifier - just what we need.
301 status->width = (int)strtol( spec, (char**)&spec, 10 );
304 /* Optional precision */
310 /* Retrieve precision value from argument stack. A negative value
311 is as if no precision is given - as precision is initalized to
312 EOF (negative), there is no need for testing for negative here.
314 status->prec = va_arg( ap, int );
319 status->prec = (int)strtol( spec, &endptr, 10 );
320 if ( spec == endptr )
322 /* TODO: Decimal point but no number - bad conversion specifier. */
327 /* Optional length modifier
328 We step one character ahead in any case, and step back only if we find
329 there has been no length modifier (or step ahead another character if it
330 has been "hh" or "ll").
337 status->flags |= E_char;
342 status->flags |= E_short;
348 status->flags |= E_llong;
353 status->flags |= E_long;
357 status->flags |= E_intmax;
360 status->flags |= E_size;
363 status->flags |= E_ptrdiff;
366 status->flags |= E_double;
373 /* Conversion specifier */
382 status->flags |= E_unsigned;
386 status->flags |= E_unsigned;
390 status->flags |= ( E_lower | E_unsigned );
394 status->flags |= E_unsigned;
411 /* uint2base( 16, (intptr_t)value, true ) */
414 // conversion specifier
415 /* TODO: May this be accompaigned by flags, width, precision, length modifier at all? */
418 /* TODO: No conversion specifier. Bad conversion. */
421 switch ( status->flags )
425 if ( status->base != 0 )
427 /* Integer conversions */
428 /* TODO: Eliminate the padwrap as far as possible. */
429 if ( status->flags & E_unsigned )
432 switch ( status->flags & ( E_char | E_short | E_long | E_llong ) )
435 value = (uintmax_t)(unsigned char)va_arg( ap, int );
438 value = (uintmax_t)(unsigned short)va_arg( ap, int );
441 value = (uintmax_t)va_arg( ap, unsigned int );
444 value = (uintmax_t)(unsigned long)va_arg( ap, unsigned long );
447 value = (uintmax_t)(unsigned long long)va_arg( ap, unsigned long long );
451 if ( ( value / status->base ) != 0 )
453 int2base( (intmax_t)(value / status->base), status );
455 int digit = value % status->base;
460 if ( status->flags & E_lower )
462 DELIVER( _PDCLIB_digits[ digit ] );
466 DELIVER( toupper( _PDCLIB_digits[ digit ] ) );
471 switch ( status->flags & ( E_char | E_short | E_long | E_llong ) )
474 int2base( (intmax_t)(char)va_arg( ap, int ), status );
477 int2base( (intmax_t)(short)va_arg( ap, int ), status );
480 int2base( (intmax_t)va_arg( ap, int ), status );
483 int2base( (intmax_t)va_arg( ap, long ), status );
486 int2base( (intmax_t)va_arg( ap, long long ), status );
490 if ( status->flags & E_minus )
492 while ( status->this < status->width )
498 if ( status->i >= status->n )
500 status->s[status->n - 1] = '\0';
505 void parse_out_wrapper( const char * spec, struct status_t * status, ... )
508 va_start( ap, status );
509 parse_out( spec, status, ap );