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