]> pd.if.org Git - pdclib/blob - draft.c
5925e7069cbdab6383114bb935ae678446ea19eb
[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_term    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
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->this);
150     if ( ( value / status->base ) != 0 )
151     {
152         int2base( value / status->base, status );
153     }
154     int digit = value % status->base;
155     if ( digit < 0 )
156     {
157         digit *= -1;
158     }
159     if ( status->flags & E_lower )
160     {
161         DELIVER( _PDCLIB_digits[ digit ] );
162     }
163     else
164     {
165         DELIVER( toupper( _PDCLIB_digits[ digit ] ) );
166     }
167     if ( status->flags & E_minus )
168     {
169         while ( status->this < status->width )
170         {
171             DELIVER( ' ' );
172             ++(status->this);
173         }
174     }
175     if ( status->i >= status->n )
176     {
177         status->s[status->n - 1] = '\0';
178     }
179 }
180
181 void parse_out( const char * spec, va_list ap )
182 {
183     /* TODO: '%' handled correctly? */
184     struct status_t status = { 0, 0, 0, 0, 0, NULL, 0, EOF };
185     /* First come 0..n flags */
186     while ( ! ( status.flags & E_done ) )
187     {
188         switch ( *(++spec) )
189         {
190             case '-':
191                 status.flags |= E_minus;
192                 break;
193             case '+':
194                 status.flags |= E_plus;
195                 break;
196             case '#':
197                 status.flags |= E_alt;
198                 break;
199             case ' ':
200                 status.flags |= E_space;
201                 break;
202             case '0':
203                 status.flags |= E_zero;
204                 break;
205             default:
206                 status.flags |= E_done;
207                 break;
208         }
209     }
210     if ( *spec == '*' )
211     {
212         /* Retrieve width value from argument stack */
213         if ( ( status.width = va_arg( ap, int ) ) < 0 )
214         {
215             /* Negative value is '-' flag plus absolute value */
216             status.flags |= E_minus;
217             status.width *= -1;
218         }
219         ++spec;
220     }
221     else
222     {
223         /* If a width is given, strtol() will return its value. If not given,
224            strtol() will return zero. In both cases, endptr will point to the
225            rest of the conversion specifier.
226         */
227         char * endptr;
228         status.width = (int)strtol( spec, &endptr, 10 );
229         spec = endptr;
230     }
231     if ( *spec == '.' )
232     {
233         if ( *(++spec) == '*' )
234         {
235             /* Retrieve precision value from argument stack. A negative value
236                is as if no precision is given - as precision is initalized to
237                EOF (negative), there is no need for testing for negative here.
238             */
239             status.prec = va_arg( ap, int );
240         }
241         else
242         {
243             char * endptr;
244             status.prec = (int)strtol( spec, &endptr, 10 );
245             spec = endptr;
246         }
247     }
248     /* We step one character ahead in any case, and step back only if we find
249        there has been no length modifier (or step ahead another character if it
250        has been "hh" or "ll").
251     */
252     switch ( *(spec++) )
253     {
254         case 'h':
255             if ( *spec == 'h' )
256             {
257                 status.flags |= E_char;
258                 ++spec;
259             }
260             else
261             {
262                 status.flags |= E_short;
263             }
264             break;
265         case 'l':
266             if ( *spec == 'l' )
267             {
268                 status.flags |= E_llong;
269                 ++spec;
270             }
271             else
272             {
273                 status.flags |= E_long;
274             }
275             break;
276         case 'j':
277             status.flags |= E_intmax;
278             break;
279         case 'z':
280             status.flags |= E_size;
281             break;
282         case 't':
283             status.flags |= E_ptrdiff;
284             break;
285         case 'L':
286             status.flags |= E_double;
287             break;
288         default:
289             ++spec;
290             break;
291     }
292     switch ( *spec )
293     {
294         case 'd':
295         case 'i':
296             /* int2base( 10, value, true ) */
297             break;
298         case 'o':
299             /* int2base( 8, value, true ) */
300             break;
301         case 'u':
302             /* uint2base( 10, value, true ) */
303             break;
304         case 'x':
305             /* uint2base( 16, value, true ) */
306             break;
307         case 'X':
308             /* uint2base( 16, value, false ) */
309             break;
310         case 'f':
311         case 'F':
312         case 'e':
313         case 'E':
314         case 'g':
315         case 'G':
316             break;
317         case 'a':
318         case 'A':
319             break;
320         case 'c':
321             break;
322         case 's':
323             break;
324         case 'p':
325             /* uint2base( 16, (intptr_t)value, true ) */
326         case 'n':
327         case '%':
328             // conversion specifier
329             break;
330         default:
331             // undefined
332             return;
333     }
334 }
335
336 #define TESTCASE( _flags, _n, _width, _prec, _value, _base, _expect ) \
337     status.flags = _flags | E_term; \
338     status.n = _n; \
339     status.i = 0; \
340     status.width = _width; \
341     status.prec = _prec; \
342     status.base = _base; \
343     status.this = 0; \
344     memset( status.s, '\0', 50 ); \
345     padwrap( _value, &status ); \
346     rc = snprintf( buffer, _n, _expect, _value ); \
347     if ( ( strcmp( status.s, buffer ) != 0 ) || ( status.i != rc ) ) \
348     { \
349         printf( "Output '%s', RC %d\nExpect '%s', RC %d\n\n", status.s, status.i, buffer, rc ); \
350     } \
351
352 #define UTESTCASE( _flags, _n, _width, _prec, _value, _base, _expect ) \
353     status.flags = _flags | E_term; \
354     status.n = _n; \
355     status.i = 0; \
356     status.width = _width; \
357     status.prec = _prec; \
358     status.base = _base; \
359     status.this = 0; \
360     memset( status.s, '\0', 50 ); \
361     upadwrap( _value, &status ); \
362     rc = snprintf( buffer, _n, _expect, _value ); \
363     if ( ( strcmp( status.s, buffer ) != 0 ) || ( status.i != rc ) ) \
364 { \
365         printf( "Output '%s', RC %d\nExpect '%s', RC %d\n\n", status.s, status.i, buffer, rc ); \
366 } \
367
368 int main()
369 {
370     struct status_t status;
371     int rc;
372     int tmp;
373     char * buffer = malloc( 50 );
374     status.s = malloc( 50 );
375 #if 0
376     TESTCASE( E_plus, 5, 0, 0, 1234, 10, "%+d" );
377     TESTCASE( E_space, 3, 0, 0, 1234, 10, "% d" );
378     TESTCASE( E_space, 3, 0, 0, -1234, 10, "% d" );
379     TESTCASE( E_plus, 3, 0, 0, -1234, 10, "%+d" );
380     TESTCASE( E_done, 4, 0, 0, 65535, 16, "%X" );
381     TESTCASE( E_lower | E_alt, 4, 0, 0, 65534, 16, "%#x" );
382     TESTCASE( E_done, 4, 0, 0, 62, 8, "%o" );
383     TESTCASE( E_alt, 4, 0, 0, 62, 8, "%#o" );
384     TESTCASE( E_done, 6, 6, 0, 1234, 10, "%6d" );
385     TESTCASE( E_minus, 6, 6, 0, 1234, 10, "%-6d" );
386     TESTCASE( E_minus, 6, 2, 0, 1234, 10, "%-2d" );
387     TESTCASE( E_done, 6, 2, 0, 1234, 10, "%2d" );
388     TESTCASE( E_zero, 6, 6, 0, -1234, 10, "%06d" );
389     /* TODO: These two are *unsigned* conversions! */
390     TESTCASE( E_zero, 7, 7, 0, -65535, 16, "%07X" );
391     TESTCASE( E_zero, 7, 7, 0, -65535, 10, "%07u" );
392
393     TESTCASE( E_zero | E_minus, 6, 6, 0, 1234, 10, "%-06d" );
394     TESTCASE( E_plus, 6, 6, 0, 1234, 10, "%+6d" );
395     TESTCASE( E_space, 6, 6, 0, 1234, 10, "% 6d" );
396     TESTCASE( E_space, 6, 6, 0, -1234, 10, "% 6d" );
397     TESTCASE( E_space | E_minus, 6, 6, 0, -1234, 10, "%- 6d" );
398 #endif
399     puts( "--- Serious Tests ---\n" );
400     puts( "- Signed min / max -\n" );
401     TESTCASE( E_done, SIZE_MAX, 0, 0, CHAR_MIN, 10, "%hhd" );
402     TESTCASE( E_done, SIZE_MAX, 0, 0, CHAR_MAX, 10, "%hhd" );
403     TESTCASE( E_done, SIZE_MAX, 0, 0, 0, 10, "%hhd" );
404     TESTCASE( E_done, SIZE_MAX, 0, 0, SHRT_MIN, 10, "%hd" );
405     TESTCASE( E_done, SIZE_MAX, 0, 0, SHRT_MAX, 10, "%hd" );
406     TESTCASE( E_done, SIZE_MAX, 0, 0, 0, 10, "%hd" );
407     TESTCASE( E_done, SIZE_MAX, 0, 0, INT_MIN, 10, "%d" );
408     TESTCASE( E_done, SIZE_MAX, 0, 0, INT_MAX, 10, "%d" );
409     TESTCASE( E_done, SIZE_MAX, 0, 0, 0, 10, "%d" );
410     TESTCASE( E_done, SIZE_MAX, 0, 0, LONG_MIN, 10, "%ld" );
411     TESTCASE( E_done, SIZE_MAX, 0, 0, LONG_MAX, 10, "%ld" );
412     TESTCASE( E_done, SIZE_MAX, 0, 0, 0l, 10, "%ld" );
413     TESTCASE( E_done, SIZE_MAX, 0, 0, LLONG_MIN, 10, "%lld" );
414     TESTCASE( E_done, SIZE_MAX, 0, 0, LLONG_MAX, 10, "%lld" );
415     TESTCASE( E_done, SIZE_MAX, 0, 0, 0ll, 10, "%lld" ); 
416     puts( "- Unsigned min / max -\n" );
417     UTESTCASE( E_done, SIZE_MAX, 0, 0, UCHAR_MAX, 10, "%hhu" );
418     UTESTCASE( E_done, SIZE_MAX, 0, 0, -1, 10, "%hhu" );
419     UTESTCASE( E_done, SIZE_MAX, 0, 0, USHRT_MAX, 10, "%hu" );
420     UTESTCASE( E_done, SIZE_MAX, 0, 0, -1, 10, "%hu" );
421     UTESTCASE( E_done, SIZE_MAX, 0, 0, UINT_MAX, 10, "%u" );
422     UTESTCASE( E_done, SIZE_MAX, 0, 0, -1, 10, "%u" );
423     UTESTCASE( E_done, SIZE_MAX, 0, 0, ULONG_MAX, 10, "%lu" );
424     UTESTCASE( E_done, SIZE_MAX, 0, 0, -1l, 10, "%lu" );
425     UTESTCASE( E_done, SIZE_MAX, 0, 0, ULLONG_MAX, 10, "%llu" );
426     UTESTCASE( E_done, SIZE_MAX, 0, 0, -1ll, 10, "%llu" );
427     return 0;
428 }