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