7 /* These can be removed once integrated into PDCLIB make procedure */
9 #include </home/solar/src/pdclib/functions/_PDCLIB/digits.c>
10 #include </home/solar/src/pdclib/functions/_PDCLIB/Xdigits.c>
12 /* Using an integer's bits as flags for both the conversion flags and length
25 #define E_intmax 1<<10
27 #define E_ptrdiff 1<<12
28 #define E_double 1<<13
30 #define E_unsigned 1<<15
34 int base; /* base to which the value shall be converted */
35 int_fast32_t flags; /* flags and length modifiers */
36 size_t n; /* maximum number of characters to be written */
37 size_t i; /* number of characters already written */
38 size_t this; /* number of output chars in the current conversion */
39 char * s; /* target buffer */
40 size_t width; /* width of current field */
41 size_t prec; /* precision of current field */
42 FILE * stream;/* for to-stream output */
45 const char * parse_out( const char * spec, struct status_t * status, va_list ap );
46 inline void test( char * buffer, size_t n, const char * expect, struct status_t * status, ... );
48 /* The following only for testing. */
54 struct status_t status;
55 char * buffer = malloc( 50 );
56 status.s = calloc( 50, 1 );
60 puts( "- Signed min / max -\n" );
61 // inline void test( char * buffer, size_t n, const char * expect, struct status_t * status, ... );
62 test( buffer, SIZE_MAX, "%hhd", &status, CHAR_MIN );
63 test( buffer, SIZE_MAX, "%hhd", &status, CHAR_MAX );
64 test( buffer, SIZE_MAX, "%hhd", &status, 0 );
65 test( buffer, SIZE_MAX, "%hd", &status, SHRT_MIN );
66 test( buffer, SIZE_MAX, "%hd", &status, SHRT_MAX );
67 test( buffer, SIZE_MAX, "%hd", &status, 0 );
68 test( buffer, SIZE_MAX, "%d", &status, INT_MIN );
69 test( buffer, SIZE_MAX, "%d", &status, INT_MAX );
70 test( buffer, SIZE_MAX, "%d", &status, 0 );
71 test( buffer, SIZE_MAX, "%ld", &status, LONG_MIN );
72 test( buffer, SIZE_MAX, "%ld", &status, LONG_MAX );
73 test( buffer, SIZE_MAX, "%ld", &status, 0l );
74 test( buffer, SIZE_MAX, "%lld", &status, LLONG_MIN );
75 test( buffer, SIZE_MAX, "%lld", &status, LLONG_MAX );
76 test( buffer, SIZE_MAX, "%lld", &status, 0ll );
77 puts( "- Unsigned min / max -\n" );
78 test( buffer, SIZE_MAX, "%hhu", &status, UCHAR_MAX );
79 test( buffer, SIZE_MAX, "%hhu", &status, (unsigned char)-1 );
80 test( buffer, SIZE_MAX, "%hu", &status, USHRT_MAX );
81 test( buffer, SIZE_MAX, "%hu", &status, (unsigned short)-1 );
82 test( buffer, SIZE_MAX, "%u", &status, UINT_MAX );
83 test( buffer, SIZE_MAX, "%u", &status, -1u );
84 test( buffer, SIZE_MAX, "%lu", &status, ULONG_MAX );
85 test( buffer, SIZE_MAX, "%lu", &status, -1ul );
86 test( buffer, SIZE_MAX, "%llu", &status, ULLONG_MAX );
87 test( buffer, SIZE_MAX, "%llu", &status, -1ull );
88 puts( "- Hex and Octal, normal and alternative, upper and lowercase -\n" );
89 test( buffer, SIZE_MAX, "%X", &status, UINT_MAX );
90 test( buffer, SIZE_MAX, "%#X", &status, -1u );
91 test( buffer, SIZE_MAX, "%x", &status, UINT_MAX );
92 test( buffer, SIZE_MAX, "%#x", &status, -1u );
93 test( buffer, SIZE_MAX, "%o", &status, UINT_MAX );
94 test( buffer, SIZE_MAX, "%#o", &status, -1u );
95 puts( "- Plus flag -\n" );
96 test( buffer, SIZE_MAX, "%+d", &status, INT_MIN );
97 test( buffer, SIZE_MAX, "%+d", &status, INT_MAX );
98 test( buffer, SIZE_MAX, "%+d", &status, 0 );
99 test( buffer, SIZE_MAX, "%+u", &status, UINT_MAX );
100 test( buffer, SIZE_MAX, "%+u", &status, -1u );
101 puts( "- Space flag -\n" );
102 test( buffer, SIZE_MAX, "% d", &status, INT_MIN );
103 test( buffer, SIZE_MAX, "% d", &status, INT_MAX );
104 test( buffer, SIZE_MAX, "% d", &status, 0 );
105 test( buffer, SIZE_MAX, "% u", &status, UINT_MAX );
106 test( buffer, SIZE_MAX, "% u", &status, -1u );
107 puts( "- Field width -\n" );
108 test( buffer, SIZE_MAX, "%9d", &status, INT_MIN );
109 test( buffer, SIZE_MAX, "%9d", &status, INT_MAX );
110 test( buffer, SIZE_MAX, "%10d", &status, INT_MIN );
111 test( buffer, SIZE_MAX, "%10d", &status, INT_MAX );
112 test( buffer, SIZE_MAX, "%11d", &status, INT_MIN );
113 test( buffer, SIZE_MAX, "%11d", &status, INT_MAX );
114 test( buffer, SIZE_MAX, "%12d", &status, INT_MIN );
115 test( buffer, SIZE_MAX, "%12d", &status, INT_MAX );
116 puts( "- Field width (left bound) -\n" );
117 test( buffer, SIZE_MAX, "%-9d", &status, INT_MIN );
118 test( buffer, SIZE_MAX, "%-9d", &status, INT_MAX );
119 test( buffer, SIZE_MAX, "%-10d", &status, INT_MIN );
120 test( buffer, SIZE_MAX, "%-10d", &status, INT_MAX );
121 test( buffer, SIZE_MAX, "%-11d", &status, INT_MIN );
122 test( buffer, SIZE_MAX, "%-11d", &status, INT_MAX );
123 test( buffer, SIZE_MAX, "%-12d", &status, INT_MIN );
124 test( buffer, SIZE_MAX, "%-12d", &status, INT_MAX );
125 puts( "- Field width, zero padding -\n");
126 test( buffer, SIZE_MAX, "%09d", &status, INT_MIN );
127 test( buffer, SIZE_MAX, "%09d", &status, INT_MAX );
128 test( buffer, SIZE_MAX, "%010d", &status, INT_MIN );
129 test( buffer, SIZE_MAX, "%010d", &status, INT_MAX );
130 test( buffer, SIZE_MAX, "%011d", &status, INT_MIN );
131 test( buffer, SIZE_MAX, "%011d", &status, INT_MAX );
132 test( buffer, SIZE_MAX, "%012d", &status, INT_MIN );
133 test( buffer, SIZE_MAX, "%012d", &status, INT_MAX );
134 puts( "- Field width, zero padding (left bound) -\n" );
135 test( buffer, SIZE_MAX, "%-09d", &status, INT_MIN );
136 test( buffer, SIZE_MAX, "%-09d", &status, INT_MAX );
137 test( buffer, SIZE_MAX, "%-010d", &status, INT_MIN );
138 test( buffer, SIZE_MAX, "%-010d", &status, INT_MAX );
139 test( buffer, SIZE_MAX, "%-011d", &status, INT_MIN );
140 test( buffer, SIZE_MAX, "%-011d", &status, INT_MAX );
141 test( buffer, SIZE_MAX, "%-012d", &status, INT_MIN );
142 test( buffer, SIZE_MAX, "%-012d", &status, INT_MAX );
146 /* x - the character to be delivered
147 i - pointer to number of characters already delivered in this call
148 n - pointer to maximum number of characters to be delivered in this call
149 s - the buffer into which the character shall be delivered
152 #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 )
154 static void int2base( intmax_t value, struct status_t * status )
157 if ( ( value / status->base ) != 0 )
159 int2base( value / status->base, status );
163 char preface[3] = "\0\0";
165 if ( ( status->flags & E_alt ) && ( status->base == 16 || status->base == 8 ) )
167 preface[ preidx++ ] = '0';
168 if ( status->base == 16 )
170 preface[ preidx++ ] = ( status->flags & E_lower ) ? 'x' : 'X';
175 preface[ preidx++ ] = '-';
177 else if ( ! ( status->flags & E_unsigned ) )
179 if ( status->flags & E_plus )
181 preface[ preidx++ ] = '+';
183 else if ( status->flags & E_space )
185 preface[ preidx++ ] = ' ';
188 if ( ! ( status->flags & ( E_minus | E_zero ) ) )
190 while ( ( status->this + preidx ) < status->width )
197 while ( preface[ preidx ] != '\0' )
199 DELIVER( preface[ preidx++ ] );
202 if ( ( ! ( status->flags & E_minus ) ) && ( status->flags & E_zero ) )
204 while ( status->this < status->width )
212 int digit = value % status->base;
217 if ( status->flags & E_lower )
219 DELIVER( _PDCLIB_digits[ digit ] );
223 DELIVER( _PDCLIB_Xdigits[ digit ] );
228 const char * parse_out( const char * spec, struct status_t * status, va_list ap )
230 const char * orig_spec = spec;
232 if ( *(++spec) == '%' )
238 /* Initializing status structure */
245 /* First come 0..n flags */
251 status->flags |= E_minus;
255 status->flags |= E_plus;
259 status->flags |= E_alt;
263 status->flags |= E_space;
267 status->flags |= E_zero;
271 status->flags |= E_done;
274 } while ( ! ( status->flags & E_done ) );
276 /* Optional field width */
279 /* Retrieve width value from argument stack */
280 if ( ( status->width = va_arg( ap, int ) ) < 0 )
282 /* Negative value is '-' flag plus absolute value */
283 status->flags |= E_minus;
290 /* If a width is given, strtol() will return its value. If not given,
291 strtol() will return zero. In both cases, endptr will point to the
292 rest of the conversion specifier - just what we need.
294 status->width = (int)strtol( spec, (char**)&spec, 10 );
297 /* Optional precision */
303 /* Retrieve precision value from argument stack. A negative value
304 is as if no precision is given - as precision is initalized to
305 EOF (negative), there is no need for testing for negative here.
307 status->prec = va_arg( ap, int );
312 status->prec = (int)strtol( spec, &endptr, 10 );
313 if ( spec == endptr )
315 /* Decimal point but no number - bad conversion specifier. */
321 /* Optional length modifier
322 We step one character ahead in any case, and step back only if we find
323 there has been no length modifier (or step ahead another character if it
324 has been "hh" or "ll").
331 status->flags |= E_char;
336 status->flags |= E_short;
342 status->flags |= E_llong;
347 status->flags |= E_long;
351 status->flags |= E_intmax;
354 status->flags |= E_size;
357 status->flags |= E_ptrdiff;
360 status->flags |= E_double;
367 /* Conversion specifier */
376 status->flags |= E_unsigned;
380 status->flags |= E_unsigned;
384 status->flags |= ( E_lower | E_unsigned );
388 status->flags |= E_unsigned;
405 /* uint2base( 16, (intptr_t)value, true ) */
409 /* No conversion specifier. Bad conversion. */
413 /* Do the actual output based on our findings */
414 if ( status->base != 0 )
416 /* Integer conversions */
417 /* TODO: Check for invalid flag combinations. */
418 if ( status->flags & E_unsigned )
421 switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_size ) )
424 value = (uintmax_t)(unsigned char)va_arg( ap, int );
427 value = (uintmax_t)(unsigned short)va_arg( ap, int );
430 value = (uintmax_t)va_arg( ap, unsigned int );
433 value = (uintmax_t)va_arg( ap, unsigned long );
436 value = (uintmax_t)va_arg( ap, unsigned long long );
439 value = (uintmax_t)va_arg( ap, size_t );
443 if ( ( value / status->base ) != 0 )
445 int2base( (intmax_t)(value / status->base), status );
447 int digit = value % status->base;
452 if ( status->flags & E_lower )
454 DELIVER( _PDCLIB_digits[ digit ] );
458 DELIVER( _PDCLIB_Xdigits[ digit ] );
463 switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_intmax ) )
466 int2base( (intmax_t)(char)va_arg( ap, int ), status );
469 int2base( (intmax_t)(short)va_arg( ap, int ), status );
472 int2base( (intmax_t)va_arg( ap, int ), status );
475 int2base( (intmax_t)va_arg( ap, long ), status );
478 int2base( (intmax_t)va_arg( ap, long long ), status );
481 int2base( (intmax_t)va_arg( ap, ptrdiff_t ), status );
484 int2base( va_arg( ap, intmax_t ), status );
488 if ( status->flags & E_minus )
490 while ( status->this < status->width )
496 if ( status->i >= status->n )
498 status->s[status->n - 1] = '\0';
504 inline void test( char * buffer, size_t n, const char * expect, struct status_t * status, ... )
508 va_start( ap, status );
511 memset( status->s, '\0', 50 );
512 parse_out( expect + 1, status, ap );
513 rc = vsnprintf( buffer, n, expect, ap );
514 if ( ( strcmp( status->s, buffer ) != 0 ) || ( status->i != rc ) )
516 printf( "Output '%s', RC %d\nExpect '%s', RC %d\n\n", status->s, status->i, buffer, rc );
521 int _PDCLIB_fprintf( FILE * stream, const char * format, va_list ap )
523 char * buffer = malloc( 50 );
524 struct status_t status = { 0, 0, SIZE_MAX, 0, 0, /* stream->buffer */ buffer, 0, 0, stream };
525 while ( *format != '\0' )
528 if ( ( *format != '%' ) || ( ( rc = parse_out( format, &status, ap ) ) == format ) )
530 /* No conversion specifier, print verbatim */
531 putc( *(format++), stream );
535 /* Continue parsing after conversion specifier */