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