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