]> pd.if.org Git - pdclib/blob - draft.c
12a510cfed8216fa9004d49a24ef2e8ff7bf6003
[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 #include <stddef.h>
11
12 #undef TEST
13 #include </home/solar/src/pdclib/functions/_PDCLIB/digits.c>
14
15 /* Using an integer's bits as flags for both the conversion flags and length
16    modifiers.
17 */
18 #define E_minus    1<<0
19 #define E_plus     1<<1
20 #define E_alt      1<<2
21 #define E_space    1<<3
22 #define E_zero     1<<4
23 #define E_done     1<<5
24 #define E_char     1<<6
25 #define E_short    1<<7
26 #define E_long     1<<8
27 #define E_llong    1<<9
28 #define E_intmax   1<<10
29 #define E_size     1<<11
30 #define E_ptrdiff  1<<12
31 #define E_double   1<<13
32 #define E_lower    1<<14
33 #define E_unsigned 1<<15
34
35 struct status_t
36 {
37     int           base;  /* base to which the value shall be converted       */
38     int_fast16_t  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     size_t        prec;  /* precision of current field                       */
45     FILE *        stream;/* for to-stream output                             */
46 };
47
48 void parse_out( const char * spec, struct status_t * status, va_list ap );
49 void parse_out_wrapper( const char * spec, struct status_t * status, ... );
50
51 #define TESTCASE( _n, _value, _expect ) \
52     status.n = _n; \
53     status.i = 0; \
54     memset( status.s, '\0', 50 ); \
55     spec = _expect; \
56     ++spec; \
57     parse_out_wrapper( spec, &status, _value ); \
58     rc = snprintf( buffer, _n, _expect, _value ); \
59     if ( ( strcmp( status.s, buffer ) != 0 ) || ( status.i != rc ) ) \
60 { \
61         printf( "Output '%s', RC %d\nExpect '%s', RC %d\n\n", status.s, status.i, buffer, rc ); \
62 }
63
64 int main( void )
65 {
66     struct status_t status;
67     int rc;
68     char * buffer = malloc( 50 );
69     const char * spec;
70     status.s = calloc( 50, 1 );
71     status.i = 0;
72     status.stream = NULL;
73     status.n = SIZE_MAX;
74     puts( "- Signed min / max -\n" );
75     TESTCASE( SIZE_MAX, CHAR_MIN, "%hhd" );
76     TESTCASE( SIZE_MAX, CHAR_MAX, "%hhd" );
77     TESTCASE( SIZE_MAX, 0, "%hhd" );
78     TESTCASE( SIZE_MAX, SHRT_MIN, "%hd" );
79     TESTCASE( SIZE_MAX, SHRT_MAX, "%hd" );
80     TESTCASE( SIZE_MAX, 0, "%hd" );
81     TESTCASE( SIZE_MAX, INT_MIN, "%d" );
82     TESTCASE( SIZE_MAX, INT_MAX, "%d" );
83     TESTCASE( SIZE_MAX, 0, "%d" );
84     TESTCASE( SIZE_MAX, LONG_MIN, "%ld" );
85     TESTCASE( SIZE_MAX, LONG_MAX, "%ld" );
86     TESTCASE( SIZE_MAX, 0l, "%ld" );
87     TESTCASE( SIZE_MAX, LLONG_MIN, "%lld" );
88     TESTCASE( SIZE_MAX, LLONG_MAX, "%lld" );
89     TESTCASE( SIZE_MAX, 0ll, "%lld" ); 
90     puts( "- Unsigned min / max -\n" );
91     TESTCASE( SIZE_MAX, UCHAR_MAX, "%hhu" );
92     TESTCASE( SIZE_MAX, (unsigned char)-1, "%hhu" );
93     TESTCASE( SIZE_MAX, USHRT_MAX, "%hu" );
94     TESTCASE( SIZE_MAX, (unsigned short)-1, "%hu" );
95     TESTCASE( SIZE_MAX, UINT_MAX, "%u" );
96     TESTCASE( SIZE_MAX, -1u, "%u" );
97     TESTCASE( SIZE_MAX, ULONG_MAX, "%lu" );
98     TESTCASE( SIZE_MAX, -1ul, "%lu" );
99     TESTCASE( SIZE_MAX, ULLONG_MAX, "%llu" );
100     TESTCASE( SIZE_MAX, -1ull, "%llu" );
101     puts( "- Hex and Octal, normal and alternative, upper and lowercase -\n" );
102     TESTCASE( SIZE_MAX, UINT_MAX, "%X" );
103     TESTCASE( SIZE_MAX, -1u, "%#X" );
104     TESTCASE( SIZE_MAX, UINT_MAX, "%x" );
105     TESTCASE( SIZE_MAX, -1u, "%#x" );
106     TESTCASE( SIZE_MAX, UINT_MAX, "%o" );
107     TESTCASE( SIZE_MAX, -1u, "%#o" );
108     puts( "- Plus flag -\n" );
109     TESTCASE( SIZE_MAX, INT_MIN, "%+d" );
110     TESTCASE( SIZE_MAX, INT_MAX, "%+d" );
111     TESTCASE( SIZE_MAX, 0, "%+d" );
112     TESTCASE( SIZE_MAX, UINT_MAX, "%+u" );
113     TESTCASE( SIZE_MAX, -1u, "%+u" );
114     puts( "- Space flag -\n" );
115     TESTCASE( SIZE_MAX, INT_MIN, "% d" );
116     TESTCASE( SIZE_MAX, INT_MAX, "% d" );
117     TESTCASE( SIZE_MAX, 0, "% d" );
118     TESTCASE( SIZE_MAX, UINT_MAX, "% u" );
119     TESTCASE( SIZE_MAX, -1u, "% u" );
120     puts( "- Field width -\n" );
121     TESTCASE( SIZE_MAX, INT_MIN, "%9d" );
122     TESTCASE( SIZE_MAX, INT_MAX, "%9d" );
123     TESTCASE( SIZE_MAX, INT_MIN, "%10d" );
124     TESTCASE( SIZE_MAX, INT_MAX, "%10d" );
125     TESTCASE( SIZE_MAX, INT_MIN, "%11d" );
126     TESTCASE( SIZE_MAX, INT_MAX, "%11d" );
127     TESTCASE( SIZE_MAX, INT_MIN, "%12d" );
128     TESTCASE( SIZE_MAX, INT_MAX, "%12d" );
129     puts( "- Field width (left bound) -\n" );
130     TESTCASE( SIZE_MAX, INT_MIN, "%-9d" );
131     TESTCASE( SIZE_MAX, INT_MAX, "%-9d" );
132     TESTCASE( SIZE_MAX, INT_MIN, "%-10d" );
133     TESTCASE( SIZE_MAX, INT_MAX, "%-10d" );
134     TESTCASE( SIZE_MAX, INT_MIN, "%-11d" );
135     TESTCASE( SIZE_MAX, INT_MAX, "%-11d" );
136     TESTCASE( SIZE_MAX, INT_MIN, "%-12d" );
137     TESTCASE( SIZE_MAX, INT_MAX, "%-12d" );
138     puts( "- Field width, zero padding -\n");
139     TESTCASE( SIZE_MAX, INT_MIN, "%09d" );
140     TESTCASE( SIZE_MAX, INT_MAX, "%09d" );
141     TESTCASE( SIZE_MAX, INT_MIN, "%010d" );
142     TESTCASE( SIZE_MAX, INT_MAX, "%010d" );
143     TESTCASE( SIZE_MAX, INT_MIN, "%011d" );
144     TESTCASE( SIZE_MAX, INT_MAX, "%011d" );
145     TESTCASE( SIZE_MAX, INT_MIN, "%012d" );
146     TESTCASE( SIZE_MAX, INT_MAX, "%012d" );
147     puts( "- Field width, zero padding (left bound) -\n" );
148     TESTCASE( SIZE_MAX, INT_MIN, "%-09d" );
149     TESTCASE( SIZE_MAX, INT_MAX, "%-09d" );
150     TESTCASE( SIZE_MAX, INT_MIN, "%-010d" );
151     TESTCASE( SIZE_MAX, INT_MAX, "%-010d" );
152     TESTCASE( SIZE_MAX, INT_MIN, "%-011d" );
153     TESTCASE( SIZE_MAX, INT_MAX, "%-011d" );
154     TESTCASE( SIZE_MAX, INT_MIN, "%-012d" );
155     TESTCASE( SIZE_MAX, INT_MAX, "%-012d" );
156     return 0;
157 }
158
159 /* x - the character to be delivered
160    i - pointer to number of characters already delivered in this call
161    n - pointer to maximum number of characters to be delivered in this call
162    s - the buffer into which the character shall be delivered
163    TODO: Overruns.
164 */
165 #define DELIVER( x ) do { if ( status->i < status->n ) { if ( status->stream != NULL ) putc( x, status->stream ); else status->s[status->i] = x; } ++(status->i); } while ( 0 )
166
167 static void int2base( intmax_t value, struct status_t * status )
168 {
169     ++(status->this);
170     if ( ( value / status->base ) != 0 )
171     {
172         int2base( value / status->base, status );
173     }
174     else
175     {
176         char preface[3] = "\0\0";
177         size_t preidx = 0;
178         if ( ( status->flags & E_alt ) && ( status->base == 16 || status->base == 8 ) )
179         {
180             preface[ preidx++ ] = '0';
181             if ( status->base == 16 )
182             {
183                 preface[ preidx++ ] = ( status->flags & E_lower ) ? 'x' : 'X';
184             }
185         }
186         if ( value < 0 )
187         {
188             preface[ preidx++ ] = '-';
189         }
190         else if ( ! ( status->flags & E_unsigned ) )
191         {
192             if ( status->flags & E_plus )
193             {
194                 preface[ preidx++ ] = '+';
195             }
196             else if ( status->flags & E_space )
197             {
198                 preface[ preidx++ ] = ' ';
199             }
200         }
201         if ( ! ( ( status->flags & E_minus ) || ( status->flags & E_zero ) ) )
202         {
203             while ( ( status->this + preidx ) < status->width )
204             {
205                 DELIVER( ' ' );
206                 ++(status->this);
207             }
208         }
209         preidx = 0;
210         while ( preface[ preidx ] != '\0' )
211         {
212             DELIVER( preface[ preidx++ ] );
213             ++(status->this);
214         }
215         if ( ( ! ( status->flags & E_minus ) ) && ( status->flags & E_zero ) )
216         {
217             while ( status->this < status->width )
218             {
219                 DELIVER( '0' );
220                 ++(status->this);
221             }
222         }
223     }
224     {
225     int digit = value % status->base;
226     if ( digit < 0 )
227     {
228         digit *= -1;
229     }
230     if ( status->flags & E_lower )
231     {
232         DELIVER( _PDCLIB_digits[ digit ] );
233     }
234     else
235     {
236         DELIVER( toupper( _PDCLIB_digits[ digit ] ) );
237     }
238     }
239 }
240
241 void parse_out( const char * spec, struct status_t * status, va_list ap )
242 {
243     /* TODO: "%%" handled correctly? */
244
245     /* Initializing status structure */
246     status->flags = 0;
247     status->base = 0;
248     status->this = 0;
249     status->width = 0;
250     status->prec = 0;
251
252     /* First come 0..n flags */
253     do
254     {
255         switch ( *spec )
256         {
257             case '-':
258                 status->flags |= E_minus;
259                 ++spec;
260                 break;
261             case '+':
262                 status->flags |= E_plus;
263                 ++spec;
264                 break;
265             case '#':
266                 status->flags |= E_alt;
267                 ++spec;
268                 break;
269             case ' ':
270                 status->flags |= E_space;
271                 ++spec;
272                 break;
273             case '0':
274                 status->flags |= E_zero;
275                 ++spec;
276                 break;
277             default:
278                 status->flags |= E_done;
279                 break;
280         }
281     } while ( ! ( status->flags & E_done ) );
282
283     /* Optional field width */
284     if ( *spec == '*' )
285     {
286         /* Retrieve width value from argument stack */
287         if ( ( status->width = va_arg( ap, int ) ) < 0 )
288         {
289             /* Negative value is '-' flag plus absolute value */
290             status->flags |= E_minus;
291             status->width *= -1;
292         }
293         ++spec;
294     }
295     else
296     {
297         /* If a width is given, strtol() will return its value. If not given,
298            strtol() will return zero. In both cases, endptr will point to the
299            rest of the conversion specifier - just what we need.
300         */
301         status->width = (int)strtol( spec, (char**)&spec, 10 );
302     }
303
304     /* Optional precision */
305     if ( *spec == '.' )
306     {
307         ++spec;
308         if ( *spec == '*' )
309         {
310             /* Retrieve precision value from argument stack. A negative value
311                is as if no precision is given - as precision is initalized to
312                EOF (negative), there is no need for testing for negative here.
313             */
314             status->prec = va_arg( ap, int );
315         }
316         else
317         {
318             char * endptr;
319             status->prec = (int)strtol( spec, &endptr, 10 );
320             if ( spec == endptr )
321             {
322                 /* TODO: Decimal point but no number - bad conversion specifier. */
323             }
324         }
325     }
326
327     /* Optional length modifier
328        We step one character ahead in any case, and step back only if we find
329        there has been no length modifier (or step ahead another character if it
330        has been "hh" or "ll").
331     */
332     switch ( *(spec++) )
333     {
334         case 'h':
335             if ( *spec == 'h' )
336             {
337                 status->flags |= E_char;
338                 ++spec;
339             }
340             else
341             {
342                 status->flags |= E_short;
343             }
344             break;
345         case 'l':
346             if ( *spec == 'l' )
347             {
348                 status->flags |= E_llong;
349                 ++spec;
350             }
351             else
352             {
353                 status->flags |= E_long;
354             }
355             break;
356         case 'j':
357             status->flags |= E_intmax;
358             break;
359         case 'z':
360             status->flags |= E_size;
361             break;
362         case 't':
363             status->flags |= E_ptrdiff;
364             break;
365         case 'L':
366             status->flags |= E_double;
367             break;
368         default:
369             --spec;
370             break;
371     }
372
373     /* Conversion specifier */
374     switch ( *spec )
375     {
376         case 'd':
377         case 'i':
378             status->base = 10;
379             break;
380         case 'o':
381             status->base = 8;
382             status->flags |= E_unsigned;
383             break;
384         case 'u':
385             status->base = 10;
386             status->flags |= E_unsigned;
387             break;
388         case 'x':
389             status->base = 16;
390             status->flags |= ( E_lower | E_unsigned );
391             break;
392         case 'X':
393             status->base = 16;
394             status->flags |= E_unsigned;
395             break;
396         case 'f':
397         case 'F':
398         case 'e':
399         case 'E':
400         case 'g':
401         case 'G':
402             break;
403         case 'a':
404         case 'A':
405             break;
406         case 'c':
407             break;
408         case 's':
409             break;
410         case 'p':
411             /* uint2base( 16, (intptr_t)value, true ) */
412         case 'n':
413         case '%':
414             // conversion specifier
415             /* TODO: May this be accompaigned by flags, width, precision, length modifier at all? */
416             break;
417         default:
418             /* TODO: No conversion specifier. Bad conversion. */
419             return;
420     }
421     switch ( status->flags )
422     {
423         /* TODO */
424     }
425     if ( status->base != 0 )
426     {
427         /* Integer conversions */
428         /* TODO: Eliminate the padwrap as far as possible. */
429         if ( status->flags & E_unsigned )
430         {
431             uintmax_t value;
432             switch ( status->flags & ( E_char | E_short | E_long | E_llong ) )
433             {
434                 case E_char:
435                     value = (uintmax_t)(unsigned char)va_arg( ap, int );
436                     break;
437                 case E_short:
438                     value = (uintmax_t)(unsigned short)va_arg( ap, int );
439                     break;
440                 case 0:
441                     value = (uintmax_t)va_arg( ap, unsigned int );
442                     break;
443                 case E_long:
444                     value = (uintmax_t)(unsigned long)va_arg( ap, unsigned long );
445                     break;
446                 case E_llong:
447                     value = (uintmax_t)(unsigned long long)va_arg( ap, unsigned long long );
448                     break;
449             }
450             ++(status->this);
451             if ( ( value / status->base ) != 0 )
452             {
453                 int2base( (intmax_t)(value / status->base), status );
454             }
455             int digit = value % status->base;
456             if ( digit < 0 )
457             {
458                 digit *= -1;
459             }
460             if ( status->flags & E_lower )
461             {
462                 DELIVER( _PDCLIB_digits[ digit ] );
463             }
464             else
465             {
466                 DELIVER( toupper( _PDCLIB_digits[ digit ] ) );
467             }
468         }
469         else
470         {
471             switch ( status->flags & ( E_char | E_short | E_long | E_llong ) )
472             {
473                 case E_char:
474                     int2base( (intmax_t)(char)va_arg( ap, int ), status );
475                     break;
476                 case E_short:
477                     int2base( (intmax_t)(short)va_arg( ap, int ), status );
478                     break;
479                 case 0:
480                     int2base( (intmax_t)va_arg( ap, int ), status );
481                     break;
482                 case E_long:
483                     int2base( (intmax_t)va_arg( ap, long ), status );
484                     break;
485                 case E_llong:
486                     int2base( (intmax_t)va_arg( ap, long long ), status );
487                     break;
488             }
489         }
490         if ( status->flags & E_minus )
491         {
492             while ( status->this < status->width )
493             {
494                 DELIVER( ' ' );
495                 ++(status->this);
496             }
497         }
498         if ( status->i >= status->n )
499         {
500             status->s[status->n - 1] = '\0';
501         }
502     }
503 }
504
505 void parse_out_wrapper( const char * spec, struct status_t * status, ... )
506 {
507     va_list ap;
508     va_start( ap, status );
509     parse_out( spec, status, ap );
510     va_end( ap );
511 }