]> pd.if.org Git - pdclib/blob - draft.c
Some improvements.
[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    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     int    prec;  /* precision of current field                              */
46 };
47
48 union value_t
49 {
50       int8_t  int8;
51      uint8_t uint8;
52      int16_t  int16;
53     uint16_t uint16;
54      int32_t  int32;
55     uint32_t uint32;
56      int64_t  int64;
57     uint64_t uint64; 
58 };
59
60 /* x - the character to be delivered
61    i - pointer to number of characters already delivered in this call
62    n - pointer to maximum number of characters to be delivered in this call
63    s - the buffer into which the character shall be delivered
64    TODO: Overruns.
65 */
66 #define DELIVER( x ) do { if ( status->i < status->n ) status->s[status->i] = x; ++(status->i); } while ( 0 )
67
68 /* TODO: Left / right alignment - requires track-keeping of width and printed chars.
69    "Father function", die für right alignment "tail recursive" gerufen wird, und
70    "after" für left alignment? Parameter als struct?
71 */
72
73 static void int2base( intmax_t value, struct status_t * status )
74 {
75     ++(status->this);
76     if ( ( value / status->base ) != 0 )
77     {
78         int2base( value / status->base, status );
79     }
80     else
81     {
82         char preface[3] = "\0\0";
83         size_t preidx = 0;
84         if ( ( status->flags & E_alt ) && ( status->base == 16 || status->base == 8 ) )
85         {
86             preface[ preidx++ ] = '0';
87             if ( status->base == 16 )
88             {
89                 preface[ preidx++ ] = ( status->flags & E_lower ) ? 'x' : 'X';
90             }
91         }
92         if ( value < 0 )
93         {
94             preface[ preidx++ ] = '-';
95         }
96         else
97         {
98             if ( status->flags & E_plus )
99             {
100                 preface[ preidx++ ] = '+';
101             }
102             else if ( status->flags & E_space )
103             {
104                 preface[ preidx++ ] = ' ';
105             }
106         }
107         if ( ! ( ( status->flags & E_minus ) || ( status->flags & E_zero ) ) )
108         {
109             while ( ( status->this + preidx ) < status->width )
110             {
111                 DELIVER( ' ' );
112                 ++(status->this);
113             }
114         }
115         preidx = 0;
116         while ( preface[ preidx ] != '\0' )
117         {
118             DELIVER( preface[ preidx++ ] );
119             ++(status->this);
120         }
121         if ( ( ! ( status->flags & E_minus ) ) && ( status->flags & E_zero ) )
122         {
123             while ( status->this < status->width )
124             {
125                 DELIVER( '0' );
126                 ++(status->this);
127             }
128         }
129     }
130     {
131     int digit = value % status->base;
132     if ( digit < 0 )
133     {
134         digit *= -1;
135     }
136     if ( status->flags & E_lower )
137     {
138         DELIVER( _PDCLIB_digits[ digit ] );
139     }
140     else
141     {
142         DELIVER( toupper( _PDCLIB_digits[ digit ] ) );
143     }
144     }
145 }
146
147 static void padwrap( intmax_t value, struct status_t * status )
148 {
149     int2base( value, status );
150     if ( status->flags & E_minus )
151     {
152         while ( status->this < status->width )
153         {
154             DELIVER( ' ' );
155             ++(status->this);
156         }
157     }
158     if ( status->i >= status->n )
159     {
160         status->s[status->n - 1] = '\0';
161     }
162 }
163
164 void parse_out( const char * spec, va_list ap )
165 {
166     /* TODO: '%' handled correctly? */
167     struct status_t status = { 0, 0, 0, 0, 0, NULL, 0, EOF };
168     /* First come 0..n flags */
169     while ( ! ( status.flags & E_done ) )
170     {
171         switch ( *(++spec) )
172         {
173             case '-':
174                 status.flags |= E_minus;
175                 break;
176             case '+':
177                 status.flags |= E_plus;
178                 break;
179             case '#':
180                 status.flags |= E_alt;
181                 break;
182             case ' ':
183                 status.flags |= E_space;
184                 break;
185             case '0':
186                 status.flags |= E_zero;
187                 break;
188             default:
189                 status.flags |= E_done;
190                 break;
191         }
192     }
193     if ( *spec == '*' )
194     {
195         /* Retrieve width value from argument stack */
196         if ( ( status.width = va_arg( ap, int ) ) < 0 )
197         {
198             /* Negative value is '-' flag plus absolute value */
199             status.flags |= E_minus;
200             status.width *= -1;
201         }
202         ++spec;
203     }
204     else
205     {
206         /* If a width is given, strtol() will return its value. If not given,
207            strtol() will return zero. In both cases, endptr will point to the
208            rest of the conversion specifier.
209         */
210         char * endptr;
211         status.width = (int)strtol( spec, &endptr, 10 );
212         spec = endptr;
213     }
214     if ( *spec == '.' )
215     {
216         if ( *(++spec) == '*' )
217         {
218             /* Retrieve precision value from argument stack. A negative value
219                is as if no precision is given - as precision is initalized to
220                EOF (negative), there is no need for testing for negative here.
221             */
222             status.prec = va_arg( ap, int );
223         }
224         else
225         {
226             char * endptr;
227             status.prec = (int)strtol( spec, &endptr, 10 );
228             spec = endptr;
229         }
230     }
231     /* We step one character ahead in any case, and step back only if we find
232        there has been no length modifier (or step ahead another character if it
233        has been "hh" or "ll").
234     */
235     switch ( *(spec++) )
236     {
237         case 'h':
238             if ( *spec == 'h' )
239             {
240                 status.flags |= E_char;
241                 ++spec;
242             }
243             else
244             {
245                 status.flags |= E_short;
246             }
247             break;
248         case 'l':
249             if ( *spec == 'l' )
250             {
251                 status.flags |= E_llong;
252                 ++spec;
253             }
254             else
255             {
256                 status.flags |= E_long;
257             }
258             break;
259         case 'j':
260             status.flags |= E_intmax;
261             break;
262         case 'z':
263             status.flags |= E_size;
264             break;
265         case 't':
266             status.flags |= E_ptrdiff;
267             break;
268         case 'L':
269             status.flags |= E_double;
270             break;
271         default:
272             ++spec;
273             break;
274     }
275     switch ( *spec )
276     {
277         case 'd':
278         case 'i':
279             /* int2base( 10, value, true ) */
280             break;
281         case 'o':
282             /* int2base( 8, value, true ) */
283             break;
284         case 'u':
285             /* uint2base( 10, value, true ) */
286             break;
287         case 'x':
288             /* uint2base( 16, value, true ) */
289             break;
290         case 'X':
291             /* uint2base( 16, value, false ) */
292             break;
293         case 'f':
294         case 'F':
295         case 'e':
296         case 'E':
297         case 'g':
298         case 'G':
299             break;
300         case 'a':
301         case 'A':
302             break;
303         case 'c':
304             break;
305         case 's':
306             break;
307         case 'p':
308             /* uint2base( 16, (intptr_t)value, true ) */
309         case 'n':
310         case '%':
311             // conversion specifier
312             break;
313         default:
314             // undefined
315             return;
316     }
317 }
318
319 /*
320 static void int2base( int value, int base, struct status_t * status )
321
322 #define E_minus   1<<0
323 #define E_plus    1<<1
324 #define E_alt     1<<2
325 #define E_space   1<<3
326 #define E_zero    1<<4
327 #define E_done    1<<5
328 #define E_char    1<<6
329 #define E_short   1<<7
330 #define E_long    1<<8
331 #define E_llong   1<<9
332 #define E_intmax  1<<10
333 #define E_size    1<<11
334 #define E_ptrdiff 1<<12
335 #define E_double  1<<13
336 #define E_lower   1<<14
337
338         struct status_t
339 {
340     int    flags; 
341     size_t n;     
342     size_t i;     
343     char * s;     
344     size_t width; 
345     size_t prec;  
346 };
347 */
348
349 #define TESTCASE( _flags, _n, _width, _prec, _value, _base, _expect ) \
350     status.flags = _flags | E_term; \
351     status.n = _n; \
352     status.i = 0; \
353     status.width = _width; \
354     status.prec = _prec; \
355     status.base = _base; \
356     status.this = 0; \
357     memset( status.s, '\0', 50 ); \
358     padwrap( _value, &status ); \
359     rc = snprintf( buffer, _n, _expect, _value ); \
360     if ( ( strcmp( status.s, buffer ) != 0 ) || ( status.i != rc ) ) \
361     { \
362         printf( "Output '%s', RC %d\nExpect '%s', RC %d\n", status.s, status.i, buffer, rc ); \
363     } \
364
365 int main()
366 {
367     struct status_t status;
368     int rc;
369     int tmp;
370     char * buffer = malloc( 50 );
371     status.s = malloc( 50 );
372     TESTCASE( E_plus, 5, 0, 0, 1234, 10, "%+d" );
373     TESTCASE( E_space, 3, 0, 0, 1234, 10, "% d" );
374     TESTCASE( E_space, 3, 0, 0, -1234, 10, "% d" );
375     TESTCASE( E_plus, 3, 0, 0, -1234, 10, "%+d" );
376     TESTCASE( E_done, 4, 0, 0, 65535, 16, "%X" );
377     TESTCASE( E_lower | E_alt, 4, 0, 0, 65534, 16, "%#x" );
378     TESTCASE( E_done, 4, 0, 0, 62, 8, "%o" );
379     TESTCASE( E_alt, 4, 0, 0, 62, 8, "%#o" );
380     TESTCASE( E_done, 6, 6, 0, 1234, 10, "%6d" );
381     TESTCASE( E_minus, 6, 6, 0, 1234, 10, "%-6d" );
382     TESTCASE( E_minus, 6, 2, 0, 1234, 10, "%-2d" );
383     TESTCASE( E_done, 6, 2, 0, 1234, 10, "%2d" );
384     TESTCASE( E_zero, 6, 6, 0, -1234, 10, "%06d" );
385     /* TODO: These two are *unsigned* conversions! */
386     TESTCASE( E_zero, 7, 7, 0, -65535, 16, "%07X" );
387     TESTCASE( E_zero, 7, 7, 0, -65535, 10, "%07u" );
388
389     TESTCASE( E_zero | E_minus, 6, 6, 0, 1234, 10, "%-06d" );
390     TESTCASE( E_plus, 6, 6, 0, 1234, 10, "%+6d" );
391     TESTCASE( E_space, 6, 6, 0, 1234, 10, "% 6d" );
392     TESTCASE( E_space, 6, 6, 0, -1234, 10, "% 6d" );
393     TESTCASE( E_space | E_minus, 6, 6, 0, -1234, 10, "%- 6d" );
394
395     puts( "--- Serious Tests ---" );
396     puts( "- Signed min / max -" );
397     TESTCASE( E_done, SIZE_MAX, 0, 0, CHAR_MIN, 10, "%hhd" );
398     TESTCASE( E_done, SIZE_MAX, 0, 0, CHAR_MAX, 10, "%hhd" );
399     TESTCASE( E_done, SIZE_MAX, 0, 0, 0, 10, "%hhd" );
400     TESTCASE( E_done, SIZE_MAX, 0, 0, SHRT_MIN, 10, "%hd" );
401     TESTCASE( E_done, SIZE_MAX, 0, 0, SHRT_MAX, 10, "%hd" );
402     TESTCASE( E_done, SIZE_MAX, 0, 0, 0, 10, "%hd" );
403     TESTCASE( E_done, SIZE_MAX, 0, 0, INT_MIN, 10, "%d" );
404     TESTCASE( E_done, SIZE_MAX, 0, 0, INT_MAX, 10, "%d" );
405     TESTCASE( E_done, SIZE_MAX, 0, 0, 0, 10, "%d" );
406     TESTCASE( E_done, SIZE_MAX, 0, 0, LONG_MIN, 10, "%ld" );
407     TESTCASE( E_done, SIZE_MAX, 0, 0, LONG_MAX, 10, "%ld" );
408     TESTCASE( E_done, SIZE_MAX, 0, 0, 0l, 10, "%ld" );
409     TESTCASE( E_done, SIZE_MAX, 0, 0, LLONG_MIN, 10, "%lld" );
410     TESTCASE( E_done, SIZE_MAX, 0, 0, LLONG_MAX, 10, "%lld" );
411     TESTCASE( E_done, SIZE_MAX, 0, 0, 0ll, 10, "%lld" ); 
412     return 0;
413 }