]> pd.if.org Git - pdclib/blob - draft.c
027f70bd3700196f0a88d54caae3f2f59837e1cb
[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
11 #undef TEST
12 #include </home/solar/src/pdclib/functions/_PDCLIB/digits.c>
13
14 /* Using an integer's bits as flags for both the conversion flags and length
15    modifiers.
16 */
17 #define E_minus    1<<0
18 #define E_plus     1<<1
19 #define E_alt      1<<2
20 #define E_space    1<<3
21 #define E_zero     1<<4
22 #define E_done     1<<5
23 #define E_char     1<<6
24 #define E_short    1<<7
25 #define E_long     1<<8
26 #define E_llong    1<<9
27 #define E_intmax   1<<10
28 #define E_size     1<<11
29 #define E_ptrdiff  1<<12
30 #define E_double   1<<13
31 #define E_lower    1<<14
32 #define E_usigned  1<<15
33
34 void parse_out( const char * spec, va_list ap );
35
36 struct status_t
37 {
38     int          base;  /* base to which the value shall be converted              */
39     int_fast16_t flags; /* flags and length modifiers                              */
40     size_t       n;     /* maximum number of characters to be written              */
41     size_t       i;     /* number of characters already written                    */
42     size_t       this;  /* number of output chars in the current conversion        */
43     char *       s;     /* target buffer                                           */
44     size_t       width; /* width of current field                                  */
45     size_t       prec;  /* precision of current field                              */
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 ) 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_usigned ) )
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     int2base( value, status );
133     if ( status->flags & E_minus )
134     {
135         while ( status->this < status->width )
136         {
137             DELIVER( ' ' );
138             ++(status->this);
139         }
140     }
141     if ( status->i >= status->n )
142     {
143         status->s[status->n - 1] = '\0';
144     }
145 }
146
147 static void upadwrap( uintmax_t value, struct status_t * status )
148 {
149     status->flags |= E_usigned;
150     ++(status->this);
151     if ( ( value / status->base ) != 0 )
152     {
153         int2base( value / status->base, status );
154     }
155     int digit = value % status->base;
156     if ( digit < 0 )
157     {
158         digit *= -1;
159     }
160     if ( status->flags & E_lower )
161     {
162         DELIVER( _PDCLIB_digits[ digit ] );
163     }
164     else
165     {
166         DELIVER( toupper( _PDCLIB_digits[ digit ] ) );
167     }
168     if ( status->flags & E_minus )
169     {
170         while ( status->this < status->width )
171         {
172             DELIVER( ' ' );
173             ++(status->this);
174         }
175     }
176     if ( status->i >= status->n )
177     {
178         status->s[status->n - 1] = '\0';
179     }
180 }
181
182 void parse_out( const char * spec, va_list ap )
183 {
184     /* TODO: '%' handled correctly? */
185     struct status_t status = { 0, 0, 0, 0, 0, NULL, 0, EOF };
186     /* First come 0..n flags */
187     while ( ! ( status.flags & E_done ) )
188     {
189         switch ( *(++spec) )
190         {
191             case '-':
192                 status.flags |= E_minus;
193                 break;
194             case '+':
195                 status.flags |= E_plus;
196                 break;
197             case '#':
198                 status.flags |= E_alt;
199                 break;
200             case ' ':
201                 status.flags |= E_space;
202                 break;
203             case '0':
204                 status.flags |= E_zero;
205                 break;
206             default:
207                 status.flags |= E_done;
208                 break;
209         }
210     }
211     if ( *spec == '*' )
212     {
213         /* Retrieve width value from argument stack */
214         if ( ( status.width = va_arg( ap, int ) ) < 0 )
215         {
216             /* Negative value is '-' flag plus absolute value */
217             status.flags |= E_minus;
218             status.width *= -1;
219         }
220         ++spec;
221     }
222     else
223     {
224         /* If a width is given, strtol() will return its value. If not given,
225            strtol() will return zero. In both cases, endptr will point to the
226            rest of the conversion specifier.
227         */
228         char * endptr;
229         status.width = (int)strtol( spec, &endptr, 10 );
230         spec = endptr;
231     }
232     if ( *spec == '.' )
233     {
234         if ( *(++spec) == '*' )
235         {
236             /* Retrieve precision value from argument stack. A negative value
237                is as if no precision is given - as precision is initalized to
238                EOF (negative), there is no need for testing for negative here.
239             */
240             status.prec = va_arg( ap, int );
241         }
242         else
243         {
244             char * endptr;
245             status.prec = (int)strtol( spec, &endptr, 10 );
246             spec = endptr;
247         }
248     }
249     /* We step one character ahead in any case, and step back only if we find
250        there has been no length modifier (or step ahead another character if it
251        has been "hh" or "ll").
252     */
253     switch ( *(spec++) )
254     {
255         case 'h':
256             if ( *spec == 'h' )
257             {
258                 status.flags |= E_char;
259                 ++spec;
260             }
261             else
262             {
263                 status.flags |= E_short;
264             }
265             break;
266         case 'l':
267             if ( *spec == 'l' )
268             {
269                 status.flags |= E_llong;
270                 ++spec;
271             }
272             else
273             {
274                 status.flags |= E_long;
275             }
276             break;
277         case 'j':
278             status.flags |= E_intmax;
279             break;
280         case 'z':
281             status.flags |= E_size;
282             break;
283         case 't':
284             status.flags |= E_ptrdiff;
285             break;
286         case 'L':
287             status.flags |= E_double;
288             break;
289         default:
290             ++spec;
291             break;
292     }
293     switch ( *spec )
294     {
295         case 'd':
296         case 'i':
297             /* int2base( 10, value, true ) */
298             break;
299         case 'o':
300             /* int2base( 8, value, true ) */
301             break;
302         case 'u':
303             /* uint2base( 10, value, true ) */
304             break;
305         case 'x':
306             /* uint2base( 16, value, true ) */
307             break;
308         case 'X':
309             /* uint2base( 16, value, false ) */
310             break;
311         case 'f':
312         case 'F':
313         case 'e':
314         case 'E':
315         case 'g':
316         case 'G':
317             break;
318         case 'a':
319         case 'A':
320             break;
321         case 'c':
322             break;
323         case 's':
324             break;
325         case 'p':
326             /* uint2base( 16, (intptr_t)value, true ) */
327         case 'n':
328         case '%':
329             // conversion specifier
330             break;
331         default:
332             // undefined
333             return;
334     }
335 }
336
337 #define TESTCASE( _flags, _n, _width, _prec, _value, _base, _expect ) \
338     status.flags = _flags; \
339     status.n = _n; \
340     status.i = 0; \
341     status.width = _width; \
342     status.prec = _prec; \
343     status.base = _base; \
344     status.this = 0; \
345     memset( status.s, '\0', 50 ); \
346     padwrap( _value, &status ); \
347     rc = snprintf( buffer, _n, _expect, _value ); \
348     if ( ( strcmp( status.s, buffer ) != 0 ) || ( status.i != rc ) ) \
349     { \
350         printf( "Output '%s', RC %d\nExpect '%s', RC %d\n\n", status.s, status.i, buffer, rc ); \
351     } \
352
353 #define UTESTCASE( _flags, _n, _width, _prec, _value, _base, _expect ) \
354     status.flags = _flags; \
355     status.n = _n; \
356     status.i = 0; \
357     status.width = _width; \
358     status.prec = _prec; \
359     status.base = _base; \
360     status.this = 0; \
361     memset( status.s, '\0', 50 ); \
362     upadwrap( _value, &status ); \
363     rc = snprintf( buffer, _n, _expect, _value ); \
364     if ( ( strcmp( status.s, buffer ) != 0 ) || ( status.i != rc ) ) \
365 { \
366         printf( "Output '%s', RC %d\nExpect '%s', RC %d\n\n", status.s, status.i, buffer, rc ); \
367 } \
368
369 int main()
370 {
371     struct status_t status;
372     int rc;
373     int tmp;
374     char * buffer = malloc( 50 );
375     status.s = malloc( 50 );
376     puts( "- Signed min / max -\n" );
377     TESTCASE( E_char, SIZE_MAX, 0, 0, CHAR_MIN, 10, "%hhd" );
378     TESTCASE( E_char, SIZE_MAX, 0, 0, CHAR_MAX, 10, "%hhd" );
379     TESTCASE( E_char, SIZE_MAX, 0, 0, 0, 10, "%hhd" );
380     TESTCASE( E_short, SIZE_MAX, 0, 0, SHRT_MIN, 10, "%hd" );
381     TESTCASE( E_short, SIZE_MAX, 0, 0, SHRT_MAX, 10, "%hd" );
382     TESTCASE( E_short, SIZE_MAX, 0, 0, 0, 10, "%hd" );
383     TESTCASE( E_done, SIZE_MAX, 0, 0, INT_MIN, 10, "%d" );
384     TESTCASE( E_done, SIZE_MAX, 0, 0, INT_MAX, 10, "%d" );
385     TESTCASE( E_done, SIZE_MAX, 0, 0, 0, 10, "%d" );
386     TESTCASE( E_long, SIZE_MAX, 0, 0, LONG_MIN, 10, "%ld" );
387     TESTCASE( E_long, SIZE_MAX, 0, 0, LONG_MAX, 10, "%ld" );
388     TESTCASE( E_long, SIZE_MAX, 0, 0, 0l, 10, "%ld" );
389     TESTCASE( E_llong, SIZE_MAX, 0, 0, LLONG_MIN, 10, "%lld" );
390     TESTCASE( E_llong, SIZE_MAX, 0, 0, LLONG_MAX, 10, "%lld" );
391     TESTCASE( E_llong, SIZE_MAX, 0, 0, 0ll, 10, "%lld" ); 
392     puts( "- Unsigned min / max -\n" );
393     UTESTCASE( E_char, SIZE_MAX, 0, 0, UCHAR_MAX, 10, "%hhu" );
394     UTESTCASE( E_char, SIZE_MAX, 0, 0, (unsigned char)-1, 10, "%hhu" );
395     UTESTCASE( E_short, SIZE_MAX, 0, 0, USHRT_MAX, 10, "%hu" );
396     UTESTCASE( E_short, SIZE_MAX, 0, 0, (unsigned short)-1, 10, "%hu" );
397     UTESTCASE( E_done, SIZE_MAX, 0, 0, UINT_MAX, 10, "%u" );
398     UTESTCASE( E_done, SIZE_MAX, 0, 0, -1u, 10, "%u" );
399     UTESTCASE( E_long, SIZE_MAX, 0, 0, ULONG_MAX, 10, "%lu" );
400     UTESTCASE( E_long, SIZE_MAX, 0, 0, -1ul, 10, "%lu" );
401     UTESTCASE( E_llong, SIZE_MAX, 0, 0, ULLONG_MAX, 10, "%llu" );
402     UTESTCASE( E_llong, SIZE_MAX, 0, 0, -1ull, 10, "%llu" );
403     puts( "- Hex and Octal, normal and alternative, upper and lowercase -\n" );
404     UTESTCASE( E_done, SIZE_MAX, 0, 0, UINT_MAX, 16, "%X" );
405     UTESTCASE( E_alt, SIZE_MAX, 0, 0, -1u, 16, "%#X" );
406     UTESTCASE( E_done | E_lower, SIZE_MAX, 0, 0, UINT_MAX, 16, "%x" );
407     UTESTCASE( E_alt | E_lower, SIZE_MAX, 0, 0, -1u, 16, "%#x" );
408     UTESTCASE( E_done, SIZE_MAX, 0, 0, UINT_MAX, 8, "%o" );
409     UTESTCASE( E_alt, SIZE_MAX, 0, 0, -1u, 8, "%#o" );
410     puts( "- Plus flag -\n" );
411     TESTCASE( E_plus, SIZE_MAX, 0, 0, INT_MIN, 10, "%+d" );
412     TESTCASE( E_plus, SIZE_MAX, 0, 0, INT_MAX, 10, "%+d" );
413     TESTCASE( E_plus, SIZE_MAX, 0, 0, 0, 10, "%+d" );
414     UTESTCASE( E_plus, SIZE_MAX, 0, 0, UINT_MAX, 10, "%+u" );
415     UTESTCASE( E_plus, SIZE_MAX, 0, 0, -1u, 10, "%+u" );
416     puts( "- Space flag -\n" );
417     TESTCASE( E_space, SIZE_MAX, 0, 0, INT_MIN, 10, "% d" );
418     TESTCASE( E_space, SIZE_MAX, 0, 0, INT_MAX, 10, "% d" );
419     TESTCASE( E_space, SIZE_MAX, 0, 0, 0, 10, "% d" );
420     UTESTCASE( E_space, SIZE_MAX, 0, 0, UINT_MAX, 10, "% u" );
421     UTESTCASE( E_space, SIZE_MAX, 0, 0, -1u, 10, "% u" );
422     puts( "- Field width -\n" );
423     TESTCASE( E_done, SIZE_MAX, 9, 0, INT_MIN, 10, "%9d" );
424     TESTCASE( E_done, SIZE_MAX, 9, 0, INT_MAX, 10, "%9d" );
425     TESTCASE( E_done, SIZE_MAX, 10, 0, INT_MIN, 10, "%10d" );
426     TESTCASE( E_done, SIZE_MAX, 10, 0, INT_MAX, 10, "%10d" );
427     TESTCASE( E_done, SIZE_MAX, 11, 0, INT_MIN, 10, "%11d" );
428     TESTCASE( E_done, SIZE_MAX, 11, 0, INT_MAX, 10, "%11d" );
429     TESTCASE( E_done, SIZE_MAX, 12, 0, INT_MIN, 10, "%12d" );
430     TESTCASE( E_done, SIZE_MAX, 12, 0, INT_MAX, 10, "%12d" );
431     puts( "- Field width (left bound) -\n" );
432     TESTCASE( E_minus, SIZE_MAX, 9, 0, INT_MIN, 10, "%-9d" );
433     TESTCASE( E_minus, SIZE_MAX, 9, 0, INT_MAX, 10, "%-9d" );
434     TESTCASE( E_minus, SIZE_MAX, 10, 0, INT_MIN, 10, "%-10d" );
435     TESTCASE( E_minus, SIZE_MAX, 10, 0, INT_MAX, 10, "%-10d" );
436     TESTCASE( E_minus, SIZE_MAX, 11, 0, INT_MIN, 10, "%-11d" );
437     TESTCASE( E_minus, SIZE_MAX, 11, 0, INT_MAX, 10, "%-11d" );
438     TESTCASE( E_minus, SIZE_MAX, 12, 0, INT_MIN, 10, "%-12d" );
439     TESTCASE( E_minus, SIZE_MAX, 12, 0, INT_MAX, 10, "%-12d" );
440     puts( "- Field width, zero padding -\n");
441     TESTCASE( E_done | E_zero, SIZE_MAX, 9, 0, INT_MIN, 10, "%09d" );
442     TESTCASE( E_done | E_zero, SIZE_MAX, 9, 0, INT_MAX, 10, "%09d" );
443     TESTCASE( E_done | E_zero, SIZE_MAX, 10, 0, INT_MIN, 10, "%010d" );
444     TESTCASE( E_done | E_zero, SIZE_MAX, 10, 0, INT_MAX, 10, "%010d" );
445     TESTCASE( E_done | E_zero, SIZE_MAX, 11, 0, INT_MIN, 10, "%011d" );
446     TESTCASE( E_done | E_zero, SIZE_MAX, 11, 0, INT_MAX, 10, "%011d" );
447     TESTCASE( E_done | E_zero, SIZE_MAX, 12, 0, INT_MIN, 10, "%012d" );
448     TESTCASE( E_done | E_zero, SIZE_MAX, 12, 0, INT_MAX, 10, "%012d" );
449     puts( "- Field width, zero padding (left bound) -\n" );
450     TESTCASE( E_minus | E_zero, SIZE_MAX, 9, 0, INT_MIN, 10, "%-09d" );
451     TESTCASE( E_minus | E_zero, SIZE_MAX, 9, 0, INT_MAX, 10, "%-09d" );
452     TESTCASE( E_minus | E_zero, SIZE_MAX, 10, 0, INT_MIN, 10, "%-010d" );
453     TESTCASE( E_minus | E_zero, SIZE_MAX, 10, 0, INT_MAX, 10, "%-010d" );
454     TESTCASE( E_minus | E_zero, SIZE_MAX, 11, 0, INT_MIN, 10, "%-011d" );
455     TESTCASE( E_minus | E_zero, SIZE_MAX, 11, 0, INT_MAX, 10, "%-011d" );
456     TESTCASE( E_minus | E_zero, SIZE_MAX, 12, 0, INT_MIN, 10, "%-012d" );
457     TESTCASE( E_minus | E_zero, SIZE_MAX, 12, 0, INT_MAX, 10, "%-012d" );
458     return 0;
459 }