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