]> pd.if.org Git - pdclib.old/blob - draft.c
Preparing to step up from padwrap() to parse_out_wrapper().
[pdclib.old] / draft.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <stdarg.h>
4 #include <stdint.h>
5 #include <stdbool.h>
6 #include <ctype.h>
7 #include <assert.h>
8 #include <string.h>
9 #include <limits.h>
10 #include <stddef.h>
11
12 #undef TEST
13 #include </home/solar/src/pdclib/functions/_PDCLIB/digits.c>
14
15 /* Using an integer's bits as flags for both the conversion flags and length
16    modifiers.
17 */
18 #define E_minus    1<<0
19 #define E_plus     1<<1
20 #define E_alt      1<<2
21 #define E_space    1<<3
22 #define E_zero     1<<4
23 #define E_done     1<<5
24 #define E_char     1<<6
25 #define E_short    1<<7
26 #define E_long     1<<8
27 #define E_llong    1<<9
28 #define E_intmax   1<<10
29 #define E_size     1<<11
30 #define E_ptrdiff  1<<12
31 #define E_double   1<<13
32 #define E_lower    1<<14
33 #define E_unsigned 1<<15
34
35 struct status_t
36 {
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 };
46
47 /* x - the character to be delivered
48    i - pointer to number of characters already delivered in this call
49    n - pointer to maximum number of characters to be delivered in this call
50    s - the buffer into which the character shall be delivered
51    TODO: Overruns.
52 */
53 #define DELIVER( x ) do { if ( status->i < status->n ) status->s[status->i] = x; ++(status->i); } while ( 0 )
54
55 static void int2base( intmax_t value, struct status_t * status )
56 {
57     ++(status->this);
58     if ( ( value / status->base ) != 0 )
59     {
60         int2base( value / status->base, status );
61     }
62     else
63     {
64         char preface[3] = "\0\0";
65         size_t preidx = 0;
66         if ( ( status->flags & E_alt ) && ( status->base == 16 || status->base == 8 ) )
67         {
68             preface[ preidx++ ] = '0';
69             if ( status->base == 16 )
70             {
71                 preface[ preidx++ ] = ( status->flags & E_lower ) ? 'x' : 'X';
72             }
73         }
74         if ( value < 0 )
75         {
76             preface[ preidx++ ] = '-';
77         }
78         else if ( ! ( status->flags & E_unsigned ) )
79         {
80             if ( status->flags & E_plus )
81             {
82                 preface[ preidx++ ] = '+';
83             }
84             else if ( status->flags & E_space )
85             {
86                 preface[ preidx++ ] = ' ';
87             }
88         }
89         if ( ! ( ( status->flags & E_minus ) || ( status->flags & E_zero ) ) )
90         {
91             while ( ( status->this + preidx ) < status->width )
92             {
93                 DELIVER( ' ' );
94                 ++(status->this);
95             }
96         }
97         preidx = 0;
98         while ( preface[ preidx ] != '\0' )
99         {
100             DELIVER( preface[ preidx++ ] );
101             ++(status->this);
102         }
103         if ( ( ! ( status->flags & E_minus ) ) && ( status->flags & E_zero ) )
104         {
105             while ( status->this < status->width )
106             {
107                 DELIVER( '0' );
108                 ++(status->this);
109             }
110         }
111     }
112     {
113     int digit = value % status->base;
114     if ( digit < 0 )
115     {
116         digit *= -1;
117     }
118     if ( status->flags & E_lower )
119     {
120         DELIVER( _PDCLIB_digits[ digit ] );
121     }
122     else
123     {
124         DELIVER( toupper( _PDCLIB_digits[ digit ] ) );
125     }
126     }
127 }
128
129 static void padwrap( intmax_t value, struct status_t * status )
130 {
131     if ( status->flags & E_char )
132     {
133         value = (char)value;
134     }
135     else if ( status->flags & E_short )
136     {
137         value = (short)value;
138     }
139     else if ( status->flags & E_long )
140     {
141         value = (long)value;
142     }
143     else if ( status->flags & E_llong )
144     {
145         value = (long long)value;
146     }
147     else if ( status->flags & E_ptrdiff )
148     {
149         value = (ptrdiff_t)value;
150     }
151     else if ( ! ( status->flags & E_intmax ) )
152     {
153         value = (int)value;
154     }
155     int2base( value, status );
156     if ( status->flags & E_minus )
157     {
158         while ( status->this < status->width )
159         {
160             DELIVER( ' ' );
161             ++(status->this);
162         }
163     }
164     if ( status->i >= status->n )
165     {
166         status->s[status->n - 1] = '\0';
167     }
168 }
169
170 static void upadwrap( uintmax_t value, struct status_t * status )
171 {
172     if ( status->flags & E_char )
173     {
174         value = (unsigned char)value;
175     }
176     else if ( status->flags & E_short )
177     {
178         value = (unsigned short)value;
179     }
180     else if ( status->flags & E_long )
181     {
182         value = (unsigned long)value;
183     }
184     else if ( status->flags & E_llong )
185     {
186         value = (unsigned long long)value;
187     }
188     else if ( status->flags & E_size )
189     {
190         value = (size_t)value;
191     }
192     else
193     {
194         value = (unsigned int)value;
195     }
196     status->flags |= E_unsigned;
197     ++(status->this);
198     if ( ( value / status->base ) != 0 )
199     {
200         int2base( (intmax_t)(value / status->base), status );
201     }
202     int digit = value % status->base;
203     if ( digit < 0 )
204     {
205         digit *= -1;
206     }
207     if ( status->flags & E_lower )
208     {
209         DELIVER( _PDCLIB_digits[ digit ] );
210     }
211     else
212     {
213         DELIVER( toupper( _PDCLIB_digits[ digit ] ) );
214     }
215     if ( status->flags & E_minus )
216     {
217         while ( status->this < status->width )
218         {
219             DELIVER( ' ' );
220             ++(status->this);
221         }
222     }
223     if ( status->i >= status->n )
224     {
225         status->s[status->n - 1] = '\0';
226     }
227 }
228
229 void parse_out( const char * spec, struct status_t * status, va_list ap );
230
231 void parse_out( const char * spec, struct status_t * status, va_list ap )
232 {
233     /* TODO: "%%" handled correctly? */
234
235     /* Initializing status structure */
236     status->flags = 0;
237     status->base = 0;
238     status->this = 0;
239     status->width = 0;
240     status->prec = 0;
241
242     /* First come 0..n flags */
243     do
244     {
245         switch ( *spec )
246         {
247             case '-':
248                 status->flags |= E_minus;
249                 ++spec;
250                 break;
251             case '+':
252                 status->flags |= E_plus;
253                 ++spec;
254                 break;
255             case '#':
256                 status->flags |= E_alt;
257                 ++spec;
258                 break;
259             case ' ':
260                 status->flags |= E_space;
261                 ++spec;
262                 break;
263             case '0':
264                 status->flags |= E_zero;
265                 ++spec;
266                 break;
267             default:
268                 status->flags |= E_done;
269                 break;
270         }
271     } while ( ! ( status->flags & E_done ) );
272
273     /* Optional field width */
274     if ( *spec == '*' )
275     {
276         /* Retrieve width value from argument stack */
277         if ( ( status->width = va_arg( ap, int ) ) < 0 )
278         {
279             /* Negative value is '-' flag plus absolute value */
280             status->flags |= E_minus;
281             status->width *= -1;
282         }
283         ++spec;
284     }
285     else
286     {
287         /* If a width is given, strtol() will return its value. If not given,
288            strtol() will return zero. In both cases, endptr will point to the
289            rest of the conversion specifier - just what we need.
290         */
291         status->width = (int)strtol( spec, (char**)&spec, 10 );
292     }
293
294     /* Optional precision */
295     if ( *spec == '.' )
296     {
297         ++spec;
298         if ( *spec == '*' )
299         {
300             /* Retrieve precision value from argument stack. A negative value
301                is as if no precision is given - as precision is initalized to
302                EOF (negative), there is no need for testing for negative here.
303             */
304             status->prec = va_arg( ap, int );
305         }
306         else
307         {
308             char * endptr;
309             status->prec = (int)strtol( spec, &endptr, 10 );
310             if ( spec == endptr )
311             {
312                 /* TODO: Decimal point but no number - bad conversion specifier. */
313             }
314         }
315     }
316
317     /* Optional length modifier
318        We step one character ahead in any case, and step back only if we find
319        there has been no length modifier (or step ahead another character if it
320        has been "hh" or "ll").
321     */
322     switch ( *(spec++) )
323     {
324         case 'h':
325             if ( *spec == 'h' )
326             {
327                 status->flags |= E_char;
328                 ++spec;
329             }
330             else
331             {
332                 status->flags |= E_short;
333             }
334             break;
335         case 'l':
336             if ( *spec == 'l' )
337             {
338                 status->flags |= E_llong;
339                 ++spec;
340             }
341             else
342             {
343                 status->flags |= E_long;
344             }
345             break;
346         case 'j':
347             status->flags |= E_intmax;
348             break;
349         case 'z':
350             status->flags |= E_size;
351             break;
352         case 't':
353             status->flags |= E_ptrdiff;
354             break;
355         case 'L':
356             status->flags |= E_double;
357             break;
358         default:
359             --spec;
360             break;
361     }
362
363     /* Conversion specifier */
364     switch ( *spec )
365     {
366         case 'd':
367         case 'i':
368             status->base = 10;
369             break;
370         case 'o':
371             status->base = 8;
372             break;
373         case 'u':
374             status->base = 10;
375             status->flags |= E_unsigned;
376             break;
377         case 'x':
378             status->base = 16;
379             status->flags |= ( E_lower | E_unsigned );
380             break;
381         case 'X':
382             status->flags = E_unsigned;
383             status->base = 16;
384             break;
385         case 'f':
386         case 'F':
387         case 'e':
388         case 'E':
389         case 'g':
390         case 'G':
391             break;
392         case 'a':
393         case 'A':
394             break;
395         case 'c':
396             break;
397         case 's':
398             break;
399         case 'p':
400             /* uint2base( 16, (intptr_t)value, true ) */
401         case 'n':
402         case '%':
403             // conversion specifier
404             /* TODO: May this be accompaigned by flags, width, precision, length modifier at all? */
405             break;
406         default:
407             /* TODO: No conversion specifier. Bad conversion. */
408             return;
409     }
410     switch ( status->flags )
411     {
412         /* TODO */
413     }
414     if ( status->base != 0 )
415     {
416         /* Integer conversions */
417         switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_unsigned ) )
418         {
419             case E_char:
420                 padwrap( (intmax_t)(char)va_arg( ap, int ), status );
421                 break;
422             case E_char | E_unsigned:
423                 upadwrap( (uintmax_t)(unsigned char)va_arg( ap, int ), status );
424                 break;
425             case E_short:
426                 padwrap( (intmax_t)(short)va_arg( ap, int ), status );
427                 break;
428             case E_short | E_unsigned:
429                 upadwrap( (uintmax_t)(unsigned short)va_arg( ap, int ), status );
430                 break;
431             case 0:
432                 padwrap( (intmax_t)va_arg( ap, int ), status );
433                 break;
434             case E_unsigned:
435                 upadwrap( (uintmax_t)va_arg( ap, unsigned int ), status );
436                 break;
437             case E_long:
438                 padwrap( (intmax_t)va_arg( ap, long ), status );
439                 break;
440             case E_long | E_unsigned:
441                 upadwrap( (uintmax_t)va_arg( ap, unsigned long ), status );
442                 break;
443             case E_llong:
444                 padwrap( (intmax_t)va_arg( ap, long long ), status );
445                 break;
446             case E_llong | E_unsigned:
447                 upadwrap( (uintmax_t)va_arg( ap, unsigned long long ), status );
448                 break;
449         }
450     }
451 }
452
453 void parse_out_wrapper( const char * spec, struct status_t * status, ... );
454
455 void parse_out_wrapper( const char * spec, struct status_t * status, ... )
456 {
457     va_list ap;
458     va_start( ap, status );
459     parse_out( spec, status, ap );
460     va_end( ap );
461 }
462
463 #define TESTCASE( _flags, _n, _width, _prec, _value, _base, _expect ) \
464     status.flags = _flags; \
465     status.n = _n; \
466     status.i = 0; \
467     status.width = _width; \
468     status.prec = _prec; \
469     status.base = _base; \
470     status.this = 0; \
471     memset( status.s, '\0', 50 ); \
472     padwrap( (intmax_t)_value, &status ); \
473     rc = snprintf( buffer, _n, _expect, _value ); \
474     if ( ( strcmp( status.s, buffer ) != 0 ) || ( status.i != rc ) ) \
475     { \
476         printf( "Output '%s', RC %d\nExpect '%s', RC %d\n\n", status.s, status.i, buffer, rc ); \
477     }
478
479 #define UTESTCASE( _flags, _n, _width, _prec, _value, _base, _expect ) \
480     status.flags = _flags; \
481     status.n = _n; \
482     status.i = 0; \
483     status.width = _width; \
484     status.prec = _prec; \
485     status.base = _base; \
486     status.this = 0; \
487     memset( status.s, '\0', 50 ); \
488     upadwrap( (uintmax_t)_value, &status ); \
489     rc = snprintf( buffer, _n, _expect, _value ); \
490     if ( ( strcmp( status.s, buffer ) != 0 ) || ( status.i != rc ) ) \
491     { \
492         printf( "Output '%s', RC %d\nExpect '%s', RC %d\n\n", status.s, status.i, buffer, rc ); \
493     }
494
495 int main( void )
496 {
497     struct status_t status;
498     int rc;
499     char * buffer = malloc( 50 );
500     status.s = calloc( 50, 1 );
501     status.i = 0;
502     status.n = SIZE_MAX;
503     puts( "- parse_out() -\n" );
504     parse_out_wrapper( "d", &status, 1234 );
505     puts( status.s );
506     puts( "- Signed min / max -\n" );
507     TESTCASE( E_char, SIZE_MAX, 0, 0, CHAR_MIN, 10, "%hhd" );
508     TESTCASE( E_char, SIZE_MAX, 0, 0, CHAR_MAX, 10, "%hhd" );
509     TESTCASE( E_char, SIZE_MAX, 0, 0, 0, 10, "%hhd" );
510     TESTCASE( E_short, SIZE_MAX, 0, 0, SHRT_MIN, 10, "%hd" );
511     TESTCASE( E_short, SIZE_MAX, 0, 0, SHRT_MAX, 10, "%hd" );
512     TESTCASE( E_short, SIZE_MAX, 0, 0, 0, 10, "%hd" );
513     TESTCASE( E_done, SIZE_MAX, 0, 0, INT_MIN, 10, "%d" );
514     TESTCASE( E_done, SIZE_MAX, 0, 0, INT_MAX, 10, "%d" );
515     TESTCASE( E_done, SIZE_MAX, 0, 0, 0, 10, "%d" );
516     TESTCASE( E_long, SIZE_MAX, 0, 0, LONG_MIN, 10, "%ld" );
517     TESTCASE( E_long, SIZE_MAX, 0, 0, LONG_MAX, 10, "%ld" );
518     TESTCASE( E_long, SIZE_MAX, 0, 0, 0l, 10, "%ld" );
519     TESTCASE( E_llong, SIZE_MAX, 0, 0, LLONG_MIN, 10, "%lld" );
520     TESTCASE( E_llong, SIZE_MAX, 0, 0, LLONG_MAX, 10, "%lld" );
521     TESTCASE( E_llong, SIZE_MAX, 0, 0, 0ll, 10, "%lld" ); 
522     puts( "- Unsigned min / max -\n" );
523     UTESTCASE( E_char, SIZE_MAX, 0, 0, UCHAR_MAX, 10, "%hhu" );
524     UTESTCASE( E_char, SIZE_MAX, 0, 0, (unsigned char)-1, 10, "%hhu" );
525     UTESTCASE( E_short, SIZE_MAX, 0, 0, USHRT_MAX, 10, "%hu" );
526     UTESTCASE( E_short, SIZE_MAX, 0, 0, (unsigned short)-1, 10, "%hu" );
527     UTESTCASE( E_done, SIZE_MAX, 0, 0, UINT_MAX, 10, "%u" );
528     UTESTCASE( E_done, SIZE_MAX, 0, 0, -1u, 10, "%u" );
529     UTESTCASE( E_long, SIZE_MAX, 0, 0, ULONG_MAX, 10, "%lu" );
530     UTESTCASE( E_long, SIZE_MAX, 0, 0, -1ul, 10, "%lu" );
531     UTESTCASE( E_llong, SIZE_MAX, 0, 0, ULLONG_MAX, 10, "%llu" );
532     UTESTCASE( E_llong, SIZE_MAX, 0, 0, -1ull, 10, "%llu" );
533     puts( "- Hex and Octal, normal and alternative, upper and lowercase -\n" );
534     UTESTCASE( E_done, SIZE_MAX, 0, 0, UINT_MAX, 16, "%X" );
535     UTESTCASE( E_alt, SIZE_MAX, 0, 0, -1u, 16, "%#X" );
536     UTESTCASE( E_done | E_lower, SIZE_MAX, 0, 0, UINT_MAX, 16, "%x" );
537     UTESTCASE( E_alt | E_lower, SIZE_MAX, 0, 0, -1u, 16, "%#x" );
538     UTESTCASE( E_done, SIZE_MAX, 0, 0, UINT_MAX, 8, "%o" );
539     UTESTCASE( E_alt, SIZE_MAX, 0, 0, -1u, 8, "%#o" );
540     puts( "- Plus flag -\n" );
541     TESTCASE( E_plus, SIZE_MAX, 0, 0, INT_MIN, 10, "%+d" );
542     TESTCASE( E_plus, SIZE_MAX, 0, 0, INT_MAX, 10, "%+d" );
543     TESTCASE( E_plus, SIZE_MAX, 0, 0, 0, 10, "%+d" );
544     UTESTCASE( E_plus, SIZE_MAX, 0, 0, UINT_MAX, 10, "%+u" );
545     UTESTCASE( E_plus, SIZE_MAX, 0, 0, -1u, 10, "%+u" );
546     puts( "- Space flag -\n" );
547     TESTCASE( E_space, SIZE_MAX, 0, 0, INT_MIN, 10, "% d" );
548     TESTCASE( E_space, SIZE_MAX, 0, 0, INT_MAX, 10, "% d" );
549     TESTCASE( E_space, SIZE_MAX, 0, 0, 0, 10, "% d" );
550     UTESTCASE( E_space, SIZE_MAX, 0, 0, UINT_MAX, 10, "% u" );
551     UTESTCASE( E_space, SIZE_MAX, 0, 0, -1u, 10, "% u" );
552     puts( "- Field width -\n" );
553     TESTCASE( E_done, SIZE_MAX, 9, 0, INT_MIN, 10, "%9d" );
554     TESTCASE( E_done, SIZE_MAX, 9, 0, INT_MAX, 10, "%9d" );
555     TESTCASE( E_done, SIZE_MAX, 10, 0, INT_MIN, 10, "%10d" );
556     TESTCASE( E_done, SIZE_MAX, 10, 0, INT_MAX, 10, "%10d" );
557     TESTCASE( E_done, SIZE_MAX, 11, 0, INT_MIN, 10, "%11d" );
558     TESTCASE( E_done, SIZE_MAX, 11, 0, INT_MAX, 10, "%11d" );
559     TESTCASE( E_done, SIZE_MAX, 12, 0, INT_MIN, 10, "%12d" );
560     TESTCASE( E_done, SIZE_MAX, 12, 0, INT_MAX, 10, "%12d" );
561     puts( "- Field width (left bound) -\n" );
562     TESTCASE( E_minus, SIZE_MAX, 9, 0, INT_MIN, 10, "%-9d" );
563     TESTCASE( E_minus, SIZE_MAX, 9, 0, INT_MAX, 10, "%-9d" );
564     TESTCASE( E_minus, SIZE_MAX, 10, 0, INT_MIN, 10, "%-10d" );
565     TESTCASE( E_minus, SIZE_MAX, 10, 0, INT_MAX, 10, "%-10d" );
566     TESTCASE( E_minus, SIZE_MAX, 11, 0, INT_MIN, 10, "%-11d" );
567     TESTCASE( E_minus, SIZE_MAX, 11, 0, INT_MAX, 10, "%-11d" );
568     TESTCASE( E_minus, SIZE_MAX, 12, 0, INT_MIN, 10, "%-12d" );
569     TESTCASE( E_minus, SIZE_MAX, 12, 0, INT_MAX, 10, "%-12d" );
570     puts( "- Field width, zero padding -\n");
571     TESTCASE( E_done | E_zero, SIZE_MAX, 9, 0, INT_MIN, 10, "%09d" );
572     TESTCASE( E_done | E_zero, SIZE_MAX, 9, 0, INT_MAX, 10, "%09d" );
573     TESTCASE( E_done | E_zero, SIZE_MAX, 10, 0, INT_MIN, 10, "%010d" );
574     TESTCASE( E_done | E_zero, SIZE_MAX, 10, 0, INT_MAX, 10, "%010d" );
575     TESTCASE( E_done | E_zero, SIZE_MAX, 11, 0, INT_MIN, 10, "%011d" );
576     TESTCASE( E_done | E_zero, SIZE_MAX, 11, 0, INT_MAX, 10, "%011d" );
577     TESTCASE( E_done | E_zero, SIZE_MAX, 12, 0, INT_MIN, 10, "%012d" );
578     TESTCASE( E_done | E_zero, SIZE_MAX, 12, 0, INT_MAX, 10, "%012d" );
579     puts( "- Field width, zero padding (left bound) -\n" );
580     TESTCASE( E_minus | E_zero, SIZE_MAX, 9, 0, INT_MIN, 10, "%-09d" );
581     TESTCASE( E_minus | E_zero, SIZE_MAX, 9, 0, INT_MAX, 10, "%-09d" );
582     TESTCASE( E_minus | E_zero, SIZE_MAX, 10, 0, INT_MIN, 10, "%-010d" );
583     TESTCASE( E_minus | E_zero, SIZE_MAX, 10, 0, INT_MAX, 10, "%-010d" );
584     TESTCASE( E_minus | E_zero, SIZE_MAX, 11, 0, INT_MIN, 10, "%-011d" );
585     TESTCASE( E_minus | E_zero, SIZE_MAX, 11, 0, INT_MAX, 10, "%-011d" );
586     TESTCASE( E_minus | E_zero, SIZE_MAX, 12, 0, INT_MIN, 10, "%-012d" );
587     TESTCASE( E_minus | E_zero, SIZE_MAX, 12, 0, INT_MAX, 10, "%-012d" );
588     return 0;
589 }