]> pd.if.org Git - pdclib/blob - draft.c
Whitespace cleanup.
[pdclib] / draft.c
1 #include <stdarg.h>
2 #include <stddef.h>
3 #include <stdint.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6
7 /* These can be removed once integrated into PDCLIB make procedure */
8 #undef TEST
9 #include </home/solar/src/pdclib/functions/_PDCLIB/digits.c>
10 #include </home/solar/src/pdclib/functions/_PDCLIB/Xdigits.c>
11
12 /* Using an integer's bits as flags for both the conversion flags and length
13    modifiers.
14 */
15 #define E_minus    1<<0
16 #define E_plus     1<<1
17 #define E_alt      1<<2
18 #define E_space    1<<3
19 #define E_zero     1<<4
20 #define E_done     1<<5
21 #define E_char     1<<6
22 #define E_short    1<<7
23 #define E_long     1<<8
24 #define E_llong    1<<9
25 #define E_intmax   1<<10
26 #define E_size     1<<11
27 #define E_ptrdiff  1<<12
28 #define E_double   1<<13
29 #define E_lower    1<<14
30 #define E_unsigned 1<<15
31
32 struct status_t
33 {
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                             */
43 };
44
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, ... );
47
48 /* The following only for testing. */
49 #include <limits.h>
50 #include <string.h>
51
52 int main( void )
53 {
54     struct status_t status;
55     char * buffer = malloc( 50 );
56     status.s = calloc( 50, 1 );
57     status.i = 0;
58     status.stream = NULL;
59     status.n = SIZE_MAX;
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 );
143     return 0;
144 }
145
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
150    TODO: Overruns.
151 */
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 )
153
154 static void int2base( intmax_t value, struct status_t * status )
155 {
156     ++(status->this);
157     if ( ( value / status->base ) != 0 )
158     {
159         int2base( value / status->base, status );
160     }
161     else
162     {
163         char preface[3] = "\0\0";
164         size_t preidx = 0;
165         if ( ( status->flags & E_alt ) && ( status->base == 16 || status->base == 8 ) )
166         {
167             preface[ preidx++ ] = '0';
168             if ( status->base == 16 )
169             {
170                 preface[ preidx++ ] = ( status->flags & E_lower ) ? 'x' : 'X';
171             }
172         }
173         if ( value < 0 )
174         {
175             preface[ preidx++ ] = '-';
176         }
177         else if ( ! ( status->flags & E_unsigned ) )
178         {
179             if ( status->flags & E_plus )
180             {
181                 preface[ preidx++ ] = '+';
182             }
183             else if ( status->flags & E_space )
184             {
185                 preface[ preidx++ ] = ' ';
186             }
187         }
188         if ( ! ( status->flags & ( E_minus | E_zero ) ) )
189         {
190             while ( ( status->this + preidx ) < status->width )
191             {
192                 DELIVER( ' ' );
193                 ++(status->this);
194             }
195         }
196         preidx = 0;
197         while ( preface[ preidx ] != '\0' )
198         {
199             DELIVER( preface[ preidx++ ] );
200             ++(status->this);
201         }
202         if ( ( ! ( status->flags & E_minus ) ) && ( status->flags & E_zero ) )
203         {
204             while ( status->this < status->width )
205             {
206                 DELIVER( '0' );
207                 ++(status->this);
208             }
209         }
210     }
211     {
212     int digit = value % status->base;
213     if ( digit < 0 )
214     {
215         digit *= -1;
216     }
217     if ( status->flags & E_lower )
218     {
219         DELIVER( _PDCLIB_digits[ digit ] );
220     }
221     else
222     {
223         DELIVER( _PDCLIB_Xdigits[ digit ] );
224     }
225     }
226 }
227
228 const char * parse_out( const char * spec, struct status_t * status, va_list ap )
229 {
230     const char * orig_spec = spec;
231 #if 0
232     if ( *(++spec) == '%' )
233     {
234         DELIVER( *spec );
235         return spec;
236     }
237 #endif
238     /* Initializing status structure */
239     status->flags = 0;
240     status->base = 0;
241     status->this = 0;
242     status->width = 0;
243     status->prec = 0;
244
245     /* First come 0..n flags */
246     do
247     {
248         switch ( *spec )
249         {
250             case '-':
251                 status->flags |= E_minus;
252                 ++spec;
253                 break;
254             case '+':
255                 status->flags |= E_plus;
256                 ++spec;
257                 break;
258             case '#':
259                 status->flags |= E_alt;
260                 ++spec;
261                 break;
262             case ' ':
263                 status->flags |= E_space;
264                 ++spec;
265                 break;
266             case '0':
267                 status->flags |= E_zero;
268                 ++spec;
269                 break;
270             default:
271                 status->flags |= E_done;
272                 break;
273         }
274     } while ( ! ( status->flags & E_done ) );
275
276     /* Optional field width */
277     if ( *spec == '*' )
278     {
279         /* Retrieve width value from argument stack */
280         if ( ( status->width = va_arg( ap, int ) ) < 0 )
281         {
282             /* Negative value is '-' flag plus absolute value */
283             status->flags |= E_minus;
284             status->width *= -1;
285         }
286         ++spec;
287     }
288     else
289     {
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.
293         */
294         status->width = (int)strtol( spec, (char**)&spec, 10 );
295     }
296
297     /* Optional precision */
298     if ( *spec == '.' )
299     {
300         ++spec;
301         if ( *spec == '*' )
302         {
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.
306             */
307             status->prec = va_arg( ap, int );
308         }
309         else
310         {
311             char * endptr;
312             status->prec = (int)strtol( spec, &endptr, 10 );
313             if ( spec == endptr )
314             {
315                 /* Decimal point but no number - bad conversion specifier. */
316                 return orig_spec;
317             }
318         }
319     }
320
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").
325     */
326     switch ( *(spec++) )
327     {
328         case 'h':
329             if ( *spec == 'h' )
330             {
331                 status->flags |= E_char;
332                 ++spec;
333             }
334             else
335             {
336                 status->flags |= E_short;
337             }
338             break;
339         case 'l':
340             if ( *spec == 'l' )
341             {
342                 status->flags |= E_llong;
343                 ++spec;
344             }
345             else
346             {
347                 status->flags |= E_long;
348             }
349             break;
350         case 'j':
351             status->flags |= E_intmax;
352             break;
353         case 'z':
354             status->flags |= E_size;
355             break;
356         case 't':
357             status->flags |= E_ptrdiff;
358             break;
359         case 'L':
360             status->flags |= E_double;
361             break;
362         default:
363             --spec;
364             break;
365     }
366
367     /* Conversion specifier */
368     switch ( *spec )
369     {
370         case 'd':
371         case 'i':
372             status->base = 10;
373             break;
374         case 'o':
375             status->base = 8;
376             status->flags |= E_unsigned;
377             break;
378         case 'u':
379             status->base = 10;
380             status->flags |= E_unsigned;
381             break;
382         case 'x':
383             status->base = 16;
384             status->flags |= ( E_lower | E_unsigned );
385             break;
386         case 'X':
387             status->base = 16;
388             status->flags |= E_unsigned;
389             break;
390         case 'f':
391         case 'F':
392         case 'e':
393         case 'E':
394         case 'g':
395         case 'G':
396             break;
397         case 'a':
398         case 'A':
399             break;
400         case 'c':
401             break;
402         case 's':
403             break;
404         case 'p':
405             /* uint2base( 16, (intptr_t)value, true ) */
406         case 'n':
407             break;
408         default:
409             /* No conversion specifier. Bad conversion. */
410             return orig_spec;
411     }
412
413     /* Do the actual output based on our findings */
414     if ( status->base != 0 )
415     {
416         /* Integer conversions */
417         /* TODO: Check for invalid flag combinations. */
418         if ( status->flags & E_unsigned )
419         {
420             uintmax_t value;
421             switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_size ) )
422             {
423                 case E_char:
424                     value = (uintmax_t)(unsigned char)va_arg( ap, int );
425                     break;
426                 case E_short:
427                     value = (uintmax_t)(unsigned short)va_arg( ap, int );
428                     break;
429                 case 0:
430                     value = (uintmax_t)va_arg( ap, unsigned int );
431                     break;
432                 case E_long:
433                     value = (uintmax_t)va_arg( ap, unsigned long );
434                     break;
435                 case E_llong:
436                     value = (uintmax_t)va_arg( ap, unsigned long long );
437                     break;
438                 case E_size:
439                     value = (uintmax_t)va_arg( ap, size_t );
440                     break;
441             }
442             ++(status->this);
443             if ( ( value / status->base ) != 0 )
444             {
445                 int2base( (intmax_t)(value / status->base), status );
446             }
447             int digit = value % status->base;
448             if ( digit < 0 )
449             {
450                 digit *= -1;
451             }
452             if ( status->flags & E_lower )
453             {
454                 DELIVER( _PDCLIB_digits[ digit ] );
455             }
456             else
457             {
458                 DELIVER( _PDCLIB_Xdigits[ digit ] );
459             }
460         }
461         else
462         {
463             switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_intmax ) )
464             {
465                 case E_char:
466                     int2base( (intmax_t)(char)va_arg( ap, int ), status );
467                     break;
468                 case E_short:
469                     int2base( (intmax_t)(short)va_arg( ap, int ), status );
470                     break;
471                 case 0:
472                     int2base( (intmax_t)va_arg( ap, int ), status );
473                     break;
474                 case E_long:
475                     int2base( (intmax_t)va_arg( ap, long ), status );
476                     break;
477                 case E_llong:
478                     int2base( (intmax_t)va_arg( ap, long long ), status );
479                     break;
480                 case E_ptrdiff:
481                     int2base( (intmax_t)va_arg( ap, ptrdiff_t ), status );
482                     break;
483                 case E_intmax:
484                     int2base( va_arg( ap, intmax_t ), status );
485                     break;
486             }
487         }
488         if ( status->flags & E_minus )
489         {
490             while ( status->this < status->width )
491             {
492                 DELIVER( ' ' );
493                 ++(status->this);
494             }
495         }
496         if ( status->i >= status->n )
497         {
498             status->s[status->n - 1] = '\0';
499         }
500     }
501     return ++spec;
502 }
503
504 inline void test( char * buffer, size_t n, const char * expect, struct status_t * status, ... )
505 {
506     int rc;
507     va_list ap;
508     va_start( ap, status );
509     status->n = n;
510     status->i = 0;
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 ) )
515     {
516         printf( "Output '%s', RC %d\nExpect '%s', RC %d\n\n", status->s, status->i, buffer, rc );
517     }
518 }
519
520 #if 0
521 int _PDCLIB_fprintf( FILE * stream, const char * format, va_list ap )
522 {
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' )
526     {
527         const char * rc;
528         if ( ( *format != '%' ) || ( ( rc = parse_out( format, &status, ap ) ) == format ) )
529         {
530             /* No conversion specifier, print verbatim */
531             putc( *(format++), stream );
532         }
533         else
534         {
535             /* Continue parsing after conversion specifier */
536             format = rc;
537         }
538     }
539     return status.i;
540 }
541 #endif