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( size_t n, const char * expect, ... );
47 int _PDCLIB_sprintf( char * buffer, size_t n, const char * format, va_list ap );
49 /* The following only for testing. */
55 puts( "- Signed min / max -\n" );
56 test( SIZE_MAX, "%hhd", CHAR_MIN );
57 test( SIZE_MAX, "%hhd", CHAR_MAX );
58 test( SIZE_MAX, "%hhd", 0 );
59 test( SIZE_MAX, "%hd", SHRT_MIN );
60 test( SIZE_MAX, "%hd", SHRT_MAX );
61 test( SIZE_MAX, "%hd", 0 );
62 test( SIZE_MAX, "%d", INT_MIN );
63 test( SIZE_MAX, "%d", INT_MAX );
64 test( SIZE_MAX, "%d", 0 );
65 test( SIZE_MAX, "%ld", LONG_MIN );
66 test( SIZE_MAX, "%ld", LONG_MAX );
67 test( SIZE_MAX, "%ld", 0l );
68 test( SIZE_MAX, "%lld", LLONG_MIN );
69 test( SIZE_MAX, "%lld", LLONG_MAX );
70 test( SIZE_MAX, "%lld", 0ll );
71 puts( "- Unsigned min / max -\n" );
72 test( SIZE_MAX, "%hhu", UCHAR_MAX );
73 test( SIZE_MAX, "%hhu", (unsigned char)-1 );
74 test( SIZE_MAX, "%hu", USHRT_MAX );
75 test( SIZE_MAX, "%hu", (unsigned short)-1 );
76 test( SIZE_MAX, "%u", UINT_MAX );
77 test( SIZE_MAX, "%u", -1u );
78 test( SIZE_MAX, "%lu", ULONG_MAX );
79 test( SIZE_MAX, "%lu", -1ul );
80 test( SIZE_MAX, "%llu", ULLONG_MAX );
81 test( SIZE_MAX, "%llu", -1ull );
82 puts( "- Hex and Octal, normal and alternative, upper and lowercase -\n" );
83 test( SIZE_MAX, "%X", UINT_MAX );
84 test( SIZE_MAX, "%#X", -1u );
85 test( SIZE_MAX, "%x", UINT_MAX );
86 test( SIZE_MAX, "%#x", -1u );
87 test( SIZE_MAX, "%o", UINT_MAX );
88 test( SIZE_MAX, "%#o", -1u );
89 puts( "- Plus flag -\n" );
90 test( SIZE_MAX, "%+d", INT_MIN );
91 test( SIZE_MAX, "%+d", INT_MAX );
92 test( SIZE_MAX, "%+d", 0 );
93 test( SIZE_MAX, "%+u", UINT_MAX );
94 test( SIZE_MAX, "%+u", -1u );
95 puts( "- Space flag -\n" );
96 test( SIZE_MAX, "% d", INT_MIN );
97 test( SIZE_MAX, "% d", INT_MAX );
98 test( SIZE_MAX, "% d", 0 );
99 test( SIZE_MAX, "% u", UINT_MAX );
100 test( SIZE_MAX, "% u", -1u );
101 puts( "- Field width -\n" );
102 test( SIZE_MAX, "%9d", INT_MIN );
103 test( SIZE_MAX, "%9d", INT_MAX );
104 test( SIZE_MAX, "%10d", INT_MIN );
105 test( SIZE_MAX, "%10d", INT_MAX );
106 test( SIZE_MAX, "%11d", INT_MIN );
107 test( SIZE_MAX, "%11d", INT_MAX );
108 test( SIZE_MAX, "%12d", INT_MIN );
109 test( SIZE_MAX, "%12d", INT_MAX );
110 puts( "- Field width (left bound) -\n" );
111 test( SIZE_MAX, "%-9d", INT_MIN );
112 test( SIZE_MAX, "%-9d", INT_MAX );
113 test( SIZE_MAX, "%-10d", INT_MIN );
114 test( SIZE_MAX, "%-10d", INT_MAX );
115 test( SIZE_MAX, "%-11d", INT_MIN );
116 test( SIZE_MAX, "%-11d", INT_MAX );
117 test( SIZE_MAX, "%-12d", INT_MIN );
118 test( SIZE_MAX, "%-12d", INT_MAX );
119 puts( "- Field width, zero padding -\n");
120 test( SIZE_MAX, "%09d", INT_MIN );
121 test( SIZE_MAX, "%09d", INT_MAX );
122 test( SIZE_MAX, "%010d", INT_MIN );
123 test( SIZE_MAX, "%010d", INT_MAX );
124 test( SIZE_MAX, "%011d", INT_MIN );
125 test( SIZE_MAX, "%011d", INT_MAX );
126 test( SIZE_MAX, "%012d", INT_MIN );
127 test( SIZE_MAX, "%012d", INT_MAX );
128 puts( "- Field width, zero padding (left bound) -\n" );
129 test( SIZE_MAX, "%-09d", INT_MIN );
130 test( SIZE_MAX, "%-09d", INT_MAX );
131 test( SIZE_MAX, "%-010d", INT_MIN );
132 test( SIZE_MAX, "%-010d", INT_MAX );
133 test( SIZE_MAX, "%-011d", INT_MIN );
134 test( SIZE_MAX, "%-011d", INT_MAX );
135 test( SIZE_MAX, "%-012d", INT_MIN );
136 test( SIZE_MAX, "%-012d", INT_MAX );
140 /* x - the character to be delivered
141 i - pointer to number of characters already delivered in this call
142 n - pointer to maximum number of characters to be delivered in this call
143 s - the buffer into which the character shall be delivered
146 #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 )
148 static void int2base( intmax_t value, struct status_t * status )
151 if ( ( value / status->base ) != 0 )
153 int2base( value / status->base, status );
157 char preface[3] = "\0\0";
159 if ( ( status->flags & E_alt ) && ( status->base == 16 || status->base == 8 ) )
161 preface[ preidx++ ] = '0';
162 if ( status->base == 16 )
164 preface[ preidx++ ] = ( status->flags & E_lower ) ? 'x' : 'X';
169 preface[ preidx++ ] = '-';
171 else if ( ! ( status->flags & E_unsigned ) )
173 if ( status->flags & E_plus )
175 preface[ preidx++ ] = '+';
177 else if ( status->flags & E_space )
179 preface[ preidx++ ] = ' ';
182 if ( ! ( status->flags & ( E_minus | E_zero ) ) )
184 while ( ( status->this + preidx ) < status->width )
191 while ( preface[ preidx ] != '\0' )
193 DELIVER( preface[ preidx++ ] );
196 if ( ( ! ( status->flags & E_minus ) ) && ( status->flags & E_zero ) )
198 while ( status->this < status->width )
206 int digit = value % status->base;
211 if ( status->flags & E_lower )
213 DELIVER( _PDCLIB_digits[ digit ] );
217 DELIVER( _PDCLIB_Xdigits[ digit ] );
222 const char * parse_out( const char * spec, struct status_t * status, va_list ap )
224 const char * orig_spec = spec;
225 if ( *(++spec) == '%' )
230 /* Initializing status structure */
237 /* First come 0..n flags */
243 status->flags |= E_minus;
247 status->flags |= E_plus;
251 status->flags |= E_alt;
255 status->flags |= E_space;
259 status->flags |= E_zero;
263 status->flags |= E_done;
266 } while ( ! ( status->flags & E_done ) );
268 /* Optional field width */
271 /* Retrieve width value from argument stack */
272 if ( ( status->width = va_arg( ap, int ) ) < 0 )
274 /* Negative value is '-' flag plus absolute value */
275 status->flags |= E_minus;
282 /* If a width is given, strtol() will return its value. If not given,
283 strtol() will return zero. In both cases, endptr will point to the
284 rest of the conversion specifier - just what we need.
286 status->width = (int)strtol( spec, (char**)&spec, 10 );
289 /* Optional precision */
295 /* Retrieve precision value from argument stack. A negative value
296 is as if no precision is given - as precision is initalized to
297 EOF (negative), there is no need for testing for negative here.
299 status->prec = va_arg( ap, int );
304 status->prec = (int)strtol( spec, &endptr, 10 );
305 if ( spec == endptr )
307 /* Decimal point but no number - bad conversion specifier. */
313 /* Optional length modifier
314 We step one character ahead in any case, and step back only if we find
315 there has been no length modifier (or step ahead another character if it
316 has been "hh" or "ll").
323 status->flags |= E_char;
328 status->flags |= E_short;
334 status->flags |= E_llong;
339 status->flags |= E_long;
343 status->flags |= E_intmax;
346 status->flags |= E_size;
349 status->flags |= E_ptrdiff;
352 status->flags |= E_double;
359 /* Conversion specifier */
368 status->flags |= E_unsigned;
372 status->flags |= E_unsigned;
376 status->flags |= ( E_lower | E_unsigned );
380 status->flags |= E_unsigned;
397 /* uint2base( 16, (intptr_t)value, true ) */
401 /* No conversion specifier. Bad conversion. */
405 /* Do the actual output based on our findings */
406 if ( status->base != 0 )
408 /* Integer conversions */
409 /* TODO: Check for invalid flag combinations. */
410 if ( status->flags & E_unsigned )
413 switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_size ) )
416 value = (uintmax_t)(unsigned char)va_arg( ap, int );
419 value = (uintmax_t)(unsigned short)va_arg( ap, int );
422 value = (uintmax_t)va_arg( ap, unsigned int );
425 value = (uintmax_t)va_arg( ap, unsigned long );
428 value = (uintmax_t)va_arg( ap, unsigned long long );
431 value = (uintmax_t)va_arg( ap, size_t );
435 if ( ( value / status->base ) != 0 )
437 int2base( (intmax_t)(value / status->base), status );
439 int digit = value % status->base;
444 if ( status->flags & E_lower )
446 DELIVER( _PDCLIB_digits[ digit ] );
450 DELIVER( _PDCLIB_Xdigits[ digit ] );
455 switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_intmax ) )
458 int2base( (intmax_t)(char)va_arg( ap, int ), status );
461 int2base( (intmax_t)(short)va_arg( ap, int ), status );
464 int2base( (intmax_t)va_arg( ap, int ), status );
467 int2base( (intmax_t)va_arg( ap, long ), status );
470 int2base( (intmax_t)va_arg( ap, long long ), status );
473 int2base( (intmax_t)va_arg( ap, ptrdiff_t ), status );
476 int2base( va_arg( ap, intmax_t ), status );
480 if ( status->flags & E_minus )
482 while ( status->this < status->width )
488 if ( status->i >= status->n )
490 status->s[status->n - 1] = '\0';
496 inline void test( size_t n, const char * expect, ... )
498 char * buffer1 = malloc( 50 );
499 char * buffer2 = malloc( 50 );
503 va_start( ap, expect );
504 myrc = _PDCLIB_sprintf( buffer1, n, expect, ap );
505 rc = vsprintf( buffer2, expect, ap );
506 if ( ( strcmp( buffer1, buffer2 ) != 0 ) || ( myrc != rc ) )
508 printf( "Output '%s', RC %d\nExpect '%s', RC %d\n\n", buffer1, myrc, buffer2, rc );
514 int _PDCLIB_sprintf( char * buffer, size_t n, const char * format, va_list ap )
516 struct status_t status = { 0, 0, n, 0, 0, buffer, 0, 0, NULL };
517 while ( *format != '\0' )
520 if ( ( *format != '%' ) || ( ( rc = parse_out( format, &status, ap ) ) == format ) )
522 /* No conversion specifier, print verbatim */
523 buffer[ status.i++ ] = *format;
527 /* Continue parsing after conversion specifier */
531 buffer[ status.i ] = '\0';
536 int _PDCLIB_fprintf( FILE * stream, const char * format, va_list ap )
538 char * buffer = malloc( 50 );
539 struct status_t status = { 0, 0, SIZE_MAX, 0, 0, /* stream->buffer */ buffer, 0, 0, stream };
540 while ( *format != '\0' )
543 if ( ( *format != '%' ) || ( ( rc = parse_out( format, &status, ap ) ) == format ) )
545 /* No conversion specifier, print verbatim */
546 putc( *(format++), stream );
550 /* Continue parsing after conversion specifier */