]> pd.if.org Git - pdclib/blob - draft.c
STUPID STUPID STUPID bug fixed!
[pdclib] / 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     va_list       ap;    /* the argument stack passed to the printf function */
44 };
45
46 const char * parse_out( const char * spec, struct status_t * status );
47 inline void test( size_t n, const char * expect, ... );
48 int _PDCLIB_sprintf( char * buffer, size_t n, const char * format, va_list ap );
49
50 /* The following only for testing. */
51 #include <limits.h>
52 #include <string.h>
53
54 int main( void )
55 {
56     int rc;
57     puts( "- Signed min / max -\n" );
58     test( SIZE_MAX, "%hhd", CHAR_MIN );
59     test( SIZE_MAX, "%hhd", CHAR_MAX );
60     test( SIZE_MAX, "%hhd", 0 );
61     test( SIZE_MAX, "%hd", SHRT_MIN );
62     test( SIZE_MAX, "%hd", SHRT_MAX );
63     test( SIZE_MAX, "%hd", 0 );
64     test( SIZE_MAX, "%d", INT_MIN );
65     test( SIZE_MAX, "%d", INT_MAX );
66     test( SIZE_MAX, "%d", 0 );
67     test( SIZE_MAX, "%ld", LONG_MIN );
68     test( SIZE_MAX, "%ld", LONG_MAX );
69     test( SIZE_MAX, "%ld", 0l );
70     test( SIZE_MAX, "%lld", LLONG_MIN );
71     test( SIZE_MAX, "%lld", LLONG_MAX );
72     test( SIZE_MAX, "%lld", 0ll );
73     puts( "- Unsigned min / max -\n" );
74     test( SIZE_MAX, "%hhu", UCHAR_MAX );
75     test( SIZE_MAX, "%hhu", (unsigned char)-1 );
76     test( SIZE_MAX, "%hu", USHRT_MAX );
77     test( SIZE_MAX, "%hu", (unsigned short)-1 );
78     test( SIZE_MAX, "%u", UINT_MAX );
79     test( SIZE_MAX, "%u", -1u );
80     test( SIZE_MAX, "%lu", ULONG_MAX );
81     test( SIZE_MAX, "%lu", -1ul );
82     test( SIZE_MAX, "%llu", ULLONG_MAX );
83     test( SIZE_MAX, "%llu", -1ull );
84     puts( "- Hex and Octal, normal and alternative, upper and lowercase -\n" );
85     test( SIZE_MAX, "%X", UINT_MAX );
86     test( SIZE_MAX, "%#X", -1u );
87     test( SIZE_MAX, "%x", UINT_MAX );
88     test( SIZE_MAX, "%#x", -1u );
89     test( SIZE_MAX, "%o", UINT_MAX );
90     test( SIZE_MAX, "%#o", -1u );
91     puts( "- Plus flag -\n" );
92     test( SIZE_MAX, "%+d", INT_MIN );
93     test( SIZE_MAX, "%+d", INT_MAX );
94     test( SIZE_MAX, "%+d", 0 );
95     test( SIZE_MAX, "%+u", UINT_MAX );
96     test( SIZE_MAX, "%+u", -1u );
97     puts( "- Space flag -\n" );
98     test( SIZE_MAX, "% d", INT_MIN );
99     test( SIZE_MAX, "% d", INT_MAX );
100     test( SIZE_MAX, "% d", 0 );
101     test( SIZE_MAX, "% u", UINT_MAX );
102     test( SIZE_MAX, "% u", -1u );
103     puts( "- Field width -\n" );
104     test( SIZE_MAX, "%9d", INT_MIN );
105     test( SIZE_MAX, "%9d", INT_MAX );
106     test( SIZE_MAX, "%10d", INT_MIN );
107     test( SIZE_MAX, "%10d", INT_MAX );
108     test( SIZE_MAX, "%11d", INT_MIN );
109     test( SIZE_MAX, "%11d", INT_MAX );
110     test( SIZE_MAX, "%12d", INT_MIN );
111     test( SIZE_MAX, "%12d", INT_MAX );
112     puts( "- Field width (left bound) -\n" );
113     test( SIZE_MAX, "%-9d", INT_MIN );
114     test( SIZE_MAX, "%-9d", INT_MAX );
115     test( SIZE_MAX, "%-10d", INT_MIN );
116     test( SIZE_MAX, "%-10d", INT_MAX );
117     test( SIZE_MAX, "%-11d", INT_MIN );
118     test( SIZE_MAX, "%-11d", INT_MAX );
119     test( SIZE_MAX, "%-12d", INT_MIN );
120     test( SIZE_MAX, "%-12d", INT_MAX );
121     puts( "- Field width, zero padding -\n");
122     test( SIZE_MAX, "%09d", INT_MIN );
123     test( SIZE_MAX, "%09d", INT_MAX );
124     test( SIZE_MAX, "%010d", INT_MIN );
125     test( SIZE_MAX, "%010d", INT_MAX );
126     test( SIZE_MAX, "%011d", INT_MIN );
127     test( SIZE_MAX, "%011d", INT_MAX );
128     test( SIZE_MAX, "%012d", INT_MIN );
129     test( SIZE_MAX, "%012d", INT_MAX );
130     puts( "- Field width, zero padding (left bound) -\n" );
131     test( SIZE_MAX, "%-09d", INT_MIN );
132     test( SIZE_MAX, "%-09d", INT_MAX );
133     test( SIZE_MAX, "%-010d", INT_MIN );
134     test( SIZE_MAX, "%-010d", INT_MAX );
135     test( SIZE_MAX, "%-011d", INT_MIN );
136     test( SIZE_MAX, "%-011d", INT_MAX );
137     test( SIZE_MAX, "%-012d", INT_MIN );
138     test( SIZE_MAX, "%-012d", INT_MAX );
139     puts( "- Limited n -\n" );
140     test( 8, "%9d", INT_MAX );
141     test( 8, "%9d", INT_MIN );
142     test( 9, "%9d", INT_MAX );
143     test( 9, "%9d", INT_MIN );
144     test( 10, "%9d", INT_MAX );
145     test( 10, "%9d", INT_MIN );
146     test( 9, "%10d", INT_MAX );
147     test( 9, "%10d", INT_MIN );
148     test( 10, "%10d", INT_MAX );
149     test( 10, "%10d", INT_MIN );
150     test( 11, "%10d", INT_MAX );
151     test( 11, "%10d", INT_MIN );
152     test( 10, "%11d", INT_MAX );
153     test( 10, "%11d", INT_MIN );
154     test( 11, "%11d", INT_MAX );
155     test( 11, "%11d", INT_MIN );
156     test( 12, "%11d", INT_MAX );
157     test( 12, "%11d", INT_MIN );
158     test( 11, "%12d", INT_MAX );
159     test( 11, "%12d", INT_MIN );
160     test( 12, "%12d", INT_MAX );
161     test( 12, "%12d", INT_MIN );
162     test( 13, "%12d", INT_MAX );
163     test( 13, "%12d", INT_MIN );
164     puts( "- Precision (tbd) -\n" );
165     {
166         const char * format = "%030.20d";
167         printf( "glibc  '" );
168         rc = printf( format, INT_MAX );
169         printf( "', RC %d\n", rc );
170         test( SIZE_MAX, format, INT_MAX );
171     }
172     puts( "- vanilla -" );
173     printf( "No width, no precision:     %#x\n", 42 );
174     printf( "Width, no precision:        %#6x\n", 42 );
175     printf( "No width, precision:        %#.6x\n", 42 );
176     printf( "Big width, small precision: %#6.3x\n", 42 );
177     printf( "Small width, big precision: %#3.6x\n", 42 );
178     printf( "No width, no precision:     %#d\n", 42 );
179     printf( "Width, no precision:        %#6d\n", 42 );
180     printf( "No width, precision:        %#.6d\n", 42 );
181     printf( "Big width, small precision: %#6.3d\n", 42 );
182     printf( "Small width, big precision: %#3.6d\n", 42 );
183     puts( "- zero flag -" );
184     printf( "No width, no precision:     %#0x\n", 42 );
185     printf( "Width, no precision:        %#06x\n", 42 );
186     printf( "No width, precision:        %#0.6x\n", 42 );
187     printf( "Big width, small precision: %#06.3x\n", 42 );
188     printf( "Small width, big precision: %#03.6x\n", 42 );
189     printf( "No width, no precision:     %#0d\n", 42 );
190     printf( "Width, no precision:        %#06d\n", 42 );
191     printf( "No width, precision:        %#0.6d\n", 42 );
192     printf( "Big width, small precision: %#06.3d\n", 42 );
193     printf( "Small width, big precision: %#03.6d\n", 42 );
194     puts( "- plus flag -" );
195     printf( "No width, no precision:     %#+d\n", 42 );
196     printf( "Width, no precision:        %#+6d\n", 42 );
197     printf( "No width, precision:        %#+.6d\n", 42 );
198     printf( "Big width, small precision: %#+6.3d\n", 42 );
199     printf( "Small width, big precision: %#+3.6d\n", 42 );
200     puts( "- plus and zero flag -" );
201     printf( "No width, no precision:     %#+0d\n", 42 );
202     printf( "Width, no precision:        %#+06d\n", 42 );
203     printf( "No width, precision:        %#+0.6d\n", 42 );
204     printf( "Big width, small precision: %#+06.3d\n", 42 );
205     printf( "Small width, big precision: %#+03.6d\n", 42 );
206     return 0;
207 }
208
209 /* This macro delivers a given character to either a memory buffer or a stream,
210    depending on the contents of 'status' (struct status_t).
211    x - the character to be delivered
212    i - pointer to number of characters already delivered in this call
213    n - pointer to maximum number of characters to be delivered in this call
214    s - the buffer into which the character shall be delivered
215    TODO: Overruns.
216 */
217 #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 )
218
219 /* This function recursively converts a given integer value to a given base
220    into a character string. Persistent information - like the number of digits
221    parsed so far - is recorded in a struct status_t, which allows to avoid
222    overwriting snprintf() limits, and enables the function to do the necessary
223    padding / prefixing of the character string eventually printed.
224 */
225 static void int2base( intmax_t value, struct status_t * status )
226 {
227     /* Registering the character being printed at the end of the function here
228        already so it will be taken into account when the deepestmost recursion
229        does the prefix / padding stuff.
230     */
231     ++(status->this);
232     if ( ( value / status->base ) != 0 )
233     {
234         /* More digits to be done - recurse deeper */
235         int2base( value / status->base, status );
236     }
237     else
238     {
239         /* We reached the last digit, the deepest point of our recursion, and
240            only now know how long the number to be printed actually is. Now we
241            have to do the sign, prefix, width, and precision padding stuff
242            before printing the numbers while we resurface from the recursion.
243         */
244         /* At worst, we need two prefix characters (hex prefix). */
245         char preface[3] = "\0";
246         size_t preidx = 0;
247         if ( ( status->flags & E_alt ) && ( status->base == 16 || status->base == 8 ) )
248         {
249             /* Octal / hexadecimal prefix for "%#" conversions */
250             preface[ preidx++ ] = '0'; /* TODO: For octal, standard states "extend the precision" */
251             if ( status->base == 16 )
252             {
253                 preface[ preidx++ ] = ( status->flags & E_lower ) ? 'x' : 'X';
254             }
255         }
256         if ( value < 0 )
257         {
258             /* Negative sign for negative values - at all times. */
259             preface[ preidx++ ] = '-';
260         }
261         else if ( ! ( status->flags & E_unsigned ) )
262         {
263             /* plus sign / extra space are only for unsigned conversions */
264             if ( status->flags & E_plus )
265             {
266                 preface[ preidx++ ] = '+';
267             }
268             else if ( status->flags & E_space )
269             {
270                 preface[ preidx++ ] = ' ';
271             }
272         }
273         if ( ! ( status->flags & ( E_minus | E_zero ) ) )
274         {
275             /* Space padding is only done if no zero padding or left alignment
276                is requested. Leave space for any prefixes determined above.
277             */
278             /* The number of characters to be printed, plus prefixes if any. */
279             /* This line contained probably the most stupid, time-wasting bug
280                I've ever perpetrated. Greetings to Samface, DevL, and all
281                sceners at Breakpoint 2006.
282             */
283             size_t characters = preidx + ( ( status->this > status->prec ) ? status->this : status->prec );
284             if ( status->width > characters )
285             {
286                 for ( int i = 0; i < status->width - characters; ++i )
287                 {
288                     DELIVER( ' ' );
289                     ++(status->this); /* TODO: Probably have to do something so I still know how many zeroes are required, later. */
290                 }
291             }
292         }
293         /* Now we did the padding, do the prefixes (if any). */
294         preidx = 0;
295         while ( preface[ preidx ] != '\0' )
296         {
297             DELIVER( preface[ preidx++ ] );
298             ++(status->this);
299         }
300         if ( ( ! ( status->flags & E_minus ) ) && ( status->flags & E_zero ) )
301         {
302             /* If field is not left aligned, and zero padding is requested, do
303                so. TODO: This should include precision handling (probably).
304             */
305             while ( status->this < status->width )
306             {
307                 DELIVER( '0' );
308                 ++(status->this);
309             }
310         }
311     }
312     /* Recursion tail - print the current digit. */
313     {
314     int digit = value % status->base;
315     if ( digit < 0 )
316     {
317         digit *= -1;
318     }
319     if ( status->flags & E_lower )
320     {
321         /* Lowercase letters. Same array used for strto...(). */
322         DELIVER( _PDCLIB_digits[ digit ] );
323     }
324     else
325     {
326         /* Uppercase letters. Array only used here, only 0-F. */
327         DELIVER( _PDCLIB_Xdigits[ digit ] );
328     }
329     }
330 }
331
332 /* This function is to be called with spec pointing to the leading '%' of a
333    printf() conversion specifier, with ap being 
334 */
335 const char * parse_out( const char * spec, struct status_t * status )
336 {
337     const char * orig_spec = spec;
338     if ( *(++spec) == '%' )
339     {
340         DELIVER( *spec );
341         return spec;
342     }
343     /* Initializing status structure */
344     status->flags = 0;
345     status->base  = 0;
346     status->this  = 0;
347     status->width = 0;
348     status->prec  = 0;
349
350     /* First come 0..n flags */
351     do
352     {
353         switch ( *spec )
354         {
355             case '-':
356                 status->flags |= E_minus;
357                 ++spec;
358                 break;
359             case '+':
360                 status->flags |= E_plus;
361                 ++spec;
362                 break;
363             case '#':
364                 status->flags |= E_alt;
365                 ++spec;
366                 break;
367             case ' ':
368                 status->flags |= E_space;
369                 ++spec;
370                 break;
371             case '0':
372                 status->flags |= E_zero;
373                 ++spec;
374                 break;
375             default:
376                 status->flags |= E_done;
377                 break;
378         }
379     } while ( ! ( status->flags & E_done ) );
380
381     /* Optional field width */
382     if ( *spec == '*' )
383     {
384         /* Retrieve width value from argument stack */
385         if ( ( status->width = va_arg( status->ap, int ) ) < 0 )
386         {
387             /* Negative value is '-' flag plus absolute value */
388             status->flags |= E_minus;
389             status->width *= -1;
390         }
391         ++spec;
392     }
393     else
394     {
395         /* If a width is given, strtol() will return its value. If not given,
396            strtol() will return zero. In both cases, endptr will point to the
397            rest of the conversion specifier - just what we need.
398         */
399         status->width = (int)strtol( spec, (char**)&spec, 10 );
400     }
401
402     /* Optional precision */
403     if ( *spec == '.' )
404     {
405         ++spec;
406         if ( *spec == '*' )
407         {
408             /* Retrieve precision value from argument stack. A negative value
409                is as if no precision is given - as precision is initalized to
410                EOF (negative), there is no need for testing for negative here.
411             */
412             status->prec = va_arg( status->ap, int );
413         }
414         else
415         {
416             char * endptr;
417             status->prec = (int)strtol( spec, &endptr, 10 );
418             if ( spec == endptr )
419             {
420                 /* Decimal point but no number - bad conversion specifier. */
421                 return orig_spec;
422             }
423             spec = endptr;
424         }
425         status->flags &= ! E_zero;
426     }
427
428     /* Optional length modifier
429        We step one character ahead in any case, and step back only if we find
430        there has been no length modifier (or step ahead another character if it
431        has been "hh" or "ll").
432     */
433     switch ( *(spec++) )
434     {
435         case 'h':
436             if ( *spec == 'h' )
437             {
438                 status->flags |= E_char;
439                 ++spec;
440             }
441             else
442             {
443                 status->flags |= E_short;
444             }
445             break;
446         case 'l':
447             if ( *spec == 'l' )
448             {
449                 status->flags |= E_llong;
450                 ++spec;
451             }
452             else
453             {
454                 status->flags |= E_long;
455             }
456             break;
457         case 'j':
458             status->flags |= E_intmax;
459             break;
460         case 'z':
461             status->flags |= E_size;
462             break;
463         case 't':
464             status->flags |= E_ptrdiff;
465             break;
466         case 'L':
467             status->flags |= E_double;
468             break;
469         default:
470             --spec;
471             break;
472     }
473
474     /* Conversion specifier */
475     switch ( *spec )
476     {
477         case 'd':
478         case 'i':
479             status->base = 10;
480             break;
481         case 'o':
482             status->base = 8;
483             status->flags |= E_unsigned;
484             break;
485         case 'u':
486             status->base = 10;
487             status->flags |= E_unsigned;
488             break;
489         case 'x':
490             status->base = 16;
491             status->flags |= ( E_lower | E_unsigned );
492             break;
493         case 'X':
494             status->base = 16;
495             status->flags |= E_unsigned;
496             break;
497         case 'f':
498         case 'F':
499         case 'e':
500         case 'E':
501         case 'g':
502         case 'G':
503             break;
504         case 'a':
505         case 'A':
506             break;
507         case 'c':
508             break;
509         case 's':
510             break;
511         case 'p':
512             /* uint2base( 16, (intptr_t)value, true ) */
513         case 'n':
514             break;
515         default:
516             /* No conversion specifier. Bad conversion. */
517             return orig_spec;
518     }
519
520     /* Do the actual output based on our findings */
521     if ( status->base != 0 )
522     {
523         /* Integer conversions */
524         /* TODO: Check for invalid flag combinations. */
525         if ( status->flags & E_unsigned )
526         {
527             uintmax_t value;
528             switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_size ) )
529             {
530                 case E_char:
531                     value = (uintmax_t)(unsigned char)va_arg( status->ap, int );
532                     break;
533                 case E_short:
534                     value = (uintmax_t)(unsigned short)va_arg( status->ap, int );
535                     break;
536                 case 0:
537                     value = (uintmax_t)va_arg( status->ap, unsigned int );
538                     break;
539                 case E_long:
540                     value = (uintmax_t)va_arg( status->ap, unsigned long );
541                     break;
542                 case E_llong:
543                     value = (uintmax_t)va_arg( status->ap, unsigned long long );
544                     break;
545                 case E_size:
546                     value = (uintmax_t)va_arg( status->ap, size_t );
547                     break;
548             }
549             ++(status->this);
550             if ( ( value / status->base ) != 0 )
551             {
552                 int2base( (intmax_t)(value / status->base), status );
553             }
554             int digit = value % status->base;
555             if ( digit < 0 )
556             {
557                 digit *= -1;
558             }
559             if ( status->flags & E_lower )
560             {
561                 DELIVER( _PDCLIB_digits[ digit ] );
562             }
563             else
564             {
565                 DELIVER( _PDCLIB_Xdigits[ digit ] );
566             }
567         }
568         else
569         {
570             switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_intmax ) )
571             {
572                 case E_char:
573                     int2base( (intmax_t)(char)va_arg( status->ap, int ), status );
574                     break;
575                 case E_short:
576                     int2base( (intmax_t)(short)va_arg( status->ap, int ), status );
577                     break;
578                 case 0:
579                     int2base( (intmax_t)va_arg( status->ap, int ), status );
580                     break;
581                 case E_long:
582                     int2base( (intmax_t)va_arg( status->ap, long ), status );
583                     break;
584                 case E_llong:
585                     int2base( (intmax_t)va_arg( status->ap, long long ), status );
586                     break;
587                 case E_ptrdiff:
588                     int2base( (intmax_t)va_arg( status->ap, ptrdiff_t ), status );
589                     break;
590                 case E_intmax:
591                     int2base( va_arg( status->ap, intmax_t ), status );
592                     break;
593             }
594         }
595         if ( status->flags & E_minus )
596         {
597             while ( status->this < status->width )
598             {
599                 DELIVER( ' ' );
600                 ++(status->this);
601             }
602         }
603         if ( status->i >= status->n )
604         {
605             status->s[status->n - 1] = '\0';
606         }
607     }
608     return ++spec;
609 }
610
611 inline void test( size_t n, const char * expect, ... )
612 {
613     char * buffer1 = malloc( 50 );
614     char * buffer2 = malloc( 50 );
615     int myrc;
616     int rc;
617     va_list ap;
618     va_start( ap, expect );
619     myrc = _PDCLIB_sprintf( buffer1, n, expect, ap );
620     rc = vsnprintf( buffer2, n, expect, ap );
621     if ( ( strcmp( buffer1, buffer2 ) != 0 ) || ( myrc != rc ) )
622     {
623         printf( "Output '%s', RC %d\nExpect '%s', RC %d\n\n", buffer1, myrc, buffer2, rc );
624     }
625     free( buffer1 );
626     free( buffer2 );
627 }
628
629 int _PDCLIB_sprintf( char * buffer, size_t n, const char * format, va_list ap )
630 {
631     struct status_t status = { 0, 0, n, 0, 0, buffer, 0, 0, NULL, ap };
632     while ( *format != '\0' )
633     {
634         const char * rc;
635         if ( ( *format != '%' ) || ( ( rc = parse_out( format, &status ) ) == format ) )
636         {
637             /* No conversion specifier, print verbatim */
638             buffer[ status.i++ ] = *(format++);
639         }
640         else
641         {
642             /* Continue parsing after conversion specifier */
643             format = rc;
644         }
645     }
646     buffer[ status.i ] = '\0';
647     return status.i;
648 }
649
650 #if 0
651 int _PDCLIB_fprintf( FILE * stream, const char * format, va_list ap )
652 {
653     struct status_t status = { 0, 0, SIZE_MAX, 0, 0, NULL, 0, 0, stream, ap };
654     while ( *format != '\0' )
655     {
656         const char * rc;
657         if ( ( *format != '%' ) || ( ( rc = parse_out( format, &status, ap ) ) == format ) )
658         {
659             /* No conversion specifier, print verbatim */
660             putc( *(format++), stream );
661         }
662         else
663         {
664             /* Continue parsing after conversion specifier */
665             format = rc;
666         }
667     }
668     return status.i;
669 }
670 #endif