]> pd.if.org Git - pdclib/blob - draft.c
Checked the remaining 'TODO' comments.
[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 */
196 #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 )
197
198 /* This function recursively converts a given integer value to a given base
199    into a character string. Persistent information - like the number of digits
200    parsed so far - is recorded in a struct status_t, which allows to avoid
201    overwriting snprintf() limits, and enables the function to do the necessary
202    padding / prefixing of the character string eventually printed.
203 */
204 static void int2base( intmax_t value, struct status_t * status )
205 {
206     /* Registering the character being printed at the end of the function here
207        already so it will be taken into account when the deepestmost recursion
208        does the prefix / padding stuff.
209     */
210     ++(status->this);
211     if ( ( value / status->base ) != 0 )
212     {
213         /* More digits to be done - recurse deeper */
214         int2base( value / status->base, status );
215     }
216     else
217     {
218         /* We reached the last digit, the deepest point of our recursion, and
219            only now know how long the number to be printed actually is. Now we
220            have to do the sign, prefix, width, and precision padding stuff
221            before printing the numbers while we resurface from the recursion.
222         */
223         /* At worst, we need two prefix characters (hex prefix). */
224         char preface[3] = "\0";
225         size_t preidx = 0;
226         if ( ( status->flags & E_alt ) && ( status->base == 16 || status->base == 8 ) )
227         {
228             /* Octal / hexadecimal prefix for "%#" conversions */
229             preface[ preidx++ ] = '0';
230             if ( status->base == 16 )
231             {
232                 preface[ preidx++ ] = ( status->flags & E_lower ) ? 'x' : 'X';
233             }
234         }
235         if ( value < 0 )
236         {
237             /* Negative sign for negative values - at all times. */
238             preface[ preidx++ ] = '-';
239         }
240         else if ( ! ( status->flags & E_unsigned ) )
241         {
242             /* plus sign / extra space are only for unsigned conversions */
243             if ( status->flags & E_plus )
244             {
245                 preface[ preidx++ ] = '+';
246             }
247             else if ( status->flags & E_space )
248             {
249                 preface[ preidx++ ] = ' ';
250             }
251         }
252         {
253         size_t prec_pads = ( status->prec > status->this ) ? ( status->prec - status->this ) : 0;
254         if ( ! ( status->flags & ( E_minus | E_zero ) ) )
255         {
256             /* Space padding is only done if no zero padding or left alignment
257                is requested. Leave space for any prefixes determined above.
258             */
259             /* The number of characters to be printed, plus prefixes if any. */
260             /* This line contained probably the most stupid, time-wasting bug
261                I've ever perpetrated. Greetings to Samface, DevL, and all
262                sceners at Breakpoint 2006.
263             */
264             size_t characters = preidx + ( ( status->this > status->prec ) ? status->this : status->prec );
265             if ( status->width > characters )
266             {
267                 for ( int i = 0; i < status->width - characters; ++i )
268                 {
269                     DELIVER( ' ' );
270                     ++(status->this);
271                 }
272             }
273         }
274         /* Now we did the padding, do the prefixes (if any). */
275         preidx = 0;
276         while ( preface[ preidx ] != '\0' )
277         {
278             DELIVER( preface[ preidx++ ] );
279             ++(status->this);
280         }
281         if ( ( ! ( status->flags & E_minus ) ) && ( status->flags & E_zero ) )
282         {
283             /* If field is not left aligned, and zero padding is requested, do
284                so.
285             */
286             while ( status->this < status->width )
287             {
288                 DELIVER( '0' );
289                 ++(status->this);
290             }
291         }
292         /* Do the precision padding if necessary. */
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         DELIVER( *spec );
328         return ++spec;
329     }
330     /* Initializing status structure */
331     status->flags = 0;
332     status->base  = 0;
333     status->this  = 0;
334     status->width = 0;
335     status->prec  = 0;
336
337     /* First come 0..n flags */
338     do
339     {
340         switch ( *spec )
341         {
342             case '-':
343                 status->flags |= E_minus;
344                 ++spec;
345                 break;
346             case '+':
347                 status->flags |= E_plus;
348                 ++spec;
349                 break;
350             case '#':
351                 status->flags |= E_alt;
352                 ++spec;
353                 break;
354             case ' ':
355                 status->flags |= E_space;
356                 ++spec;
357                 break;
358             case '0':
359                 status->flags |= E_zero;
360                 ++spec;
361                 break;
362             default:
363                 status->flags |= E_done;
364                 break;
365         }
366     } while ( ! ( status->flags & E_done ) );
367
368     /* Optional field width */
369     if ( *spec == '*' )
370     {
371         /* Retrieve width value from argument stack */
372         if ( ( status->width = va_arg( status->ap, int ) ) < 0 )
373         {
374             /* Negative value is '-' flag plus absolute value */
375             status->flags |= E_minus;
376             status->width *= -1;
377         }
378         ++spec;
379     }
380     else
381     {
382         /* If a width is given, strtol() will return its value. If not given,
383            strtol() will return zero. In both cases, endptr will point to the
384            rest of the conversion specifier - just what we need.
385         */
386         status->width = (int)strtol( spec, (char**)&spec, 10 );
387     }
388
389     /* Optional precision */
390     if ( *spec == '.' )
391     {
392         ++spec;
393         if ( *spec == '*' )
394         {
395             /* Retrieve precision value from argument stack. A negative value
396                is as if no precision is given - as precision is initalized to
397                EOF (negative), there is no need for testing for negative here.
398             */
399             status->prec = va_arg( status->ap, int );
400         }
401         else
402         {
403             char * endptr;
404             status->prec = (int)strtol( spec, &endptr, 10 );
405             if ( spec == endptr )
406             {
407                 /* Decimal point but no number - bad conversion specifier. */
408                 return orig_spec;
409             }
410             spec = endptr;
411         }
412         /* Having a precision cancels out any zero flag. */
413         status->flags ^= E_zero;
414     }
415
416     /* Optional length modifier
417        We step one character ahead in any case, and step back only if we find
418        there has been no length modifier (or step ahead another character if it
419        has been "hh" or "ll").
420     */
421     switch ( *(spec++) )
422     {
423         case 'h':
424             if ( *spec == 'h' )
425             {
426                 status->flags |= E_char;
427                 ++spec;
428             }
429             else
430             {
431                 status->flags |= E_short;
432             }
433             break;
434         case 'l':
435             if ( *spec == 'l' )
436             {
437                 status->flags |= E_llong;
438                 ++spec;
439             }
440             else
441             {
442                 status->flags |= E_long;
443             }
444             break;
445         case 'j':
446             status->flags |= E_intmax;
447             break;
448         case 'z':
449             status->flags |= E_size;
450             break;
451         case 't':
452             status->flags |= E_ptrdiff;
453             break;
454         case 'L':
455             status->flags |= E_double;
456             break;
457         default:
458             --spec;
459             break;
460     }
461
462     /* Conversion specifier */
463     switch ( *spec )
464     {
465         case 'd':
466         case 'i':
467             status->base = 10;
468             break;
469         case 'o':
470             status->base = 8;
471             status->flags |= E_unsigned;
472             break;
473         case 'u':
474             status->base = 10;
475             status->flags |= E_unsigned;
476             break;
477         case 'x':
478             status->base = 16;
479             status->flags |= ( E_lower | E_unsigned );
480             break;
481         case 'X':
482             status->base = 16;
483             status->flags |= E_unsigned;
484             break;
485         case 'f':
486         case 'F':
487         case 'e':
488         case 'E':
489         case 'g':
490         case 'G':
491             break;
492         case 'a':
493         case 'A':
494             break;
495         case 'c':
496             break;
497         case 's':
498             break;
499         case 'p':
500             /* uint2base( 16, (intptr_t)value, true ) */
501         case 'n':
502             break;
503         default:
504             /* No conversion specifier. Bad conversion. */
505             return orig_spec;
506     }
507
508     /* Do the actual output based on our findings */
509     if ( status->base != 0 )
510     {
511         /* Integer conversions */
512         /* TODO: Check for invalid flag combinations. */
513         if ( status->flags & E_unsigned )
514         {
515             uintmax_t value;
516             switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_size ) )
517             {
518                 case E_char:
519                     value = (uintmax_t)(unsigned char)va_arg( status->ap, int );
520                     break;
521                 case E_short:
522                     value = (uintmax_t)(unsigned short)va_arg( status->ap, int );
523                     break;
524                 case 0:
525                     value = (uintmax_t)va_arg( status->ap, unsigned int );
526                     break;
527                 case E_long:
528                     value = (uintmax_t)va_arg( status->ap, unsigned long );
529                     break;
530                 case E_llong:
531                     value = (uintmax_t)va_arg( status->ap, unsigned long long );
532                     break;
533                 case E_size:
534                     value = (uintmax_t)va_arg( status->ap, size_t );
535                     break;
536             }
537             ++(status->this);
538             if ( ( value / status->base ) != 0 )
539             {
540                 int2base( (intmax_t)(value / status->base), status );
541             }
542             int digit = value % status->base;
543             if ( digit < 0 )
544             {
545                 digit *= -1;
546             }
547             if ( status->flags & E_lower )
548             {
549                 DELIVER( _PDCLIB_digits[ digit ] );
550             }
551             else
552             {
553                 DELIVER( _PDCLIB_Xdigits[ digit ] );
554             }
555         }
556         else
557         {
558             switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_intmax ) )
559             {
560                 case E_char:
561                     int2base( (intmax_t)(char)va_arg( status->ap, int ), status );
562                     break;
563                 case E_short:
564                     int2base( (intmax_t)(short)va_arg( status->ap, int ), status );
565                     break;
566                 case 0:
567                     int2base( (intmax_t)va_arg( status->ap, int ), status );
568                     break;
569                 case E_long:
570                     int2base( (intmax_t)va_arg( status->ap, long ), status );
571                     break;
572                 case E_llong:
573                     int2base( (intmax_t)va_arg( status->ap, long long ), status );
574                     break;
575                 case E_ptrdiff:
576                     int2base( (intmax_t)va_arg( status->ap, ptrdiff_t ), status );
577                     break;
578                 case E_intmax:
579                     int2base( va_arg( status->ap, intmax_t ), status );
580                     break;
581             }
582         }
583         if ( status->flags & E_minus )
584         {
585             while ( status->this < status->width )
586             {
587                 DELIVER( ' ' );
588                 ++(status->this);
589             }
590         }
591         if ( status->i >= status->n )
592         {
593             status->s[status->n - 1] = '\0';
594         }
595     }
596     return ++spec;
597 }
598
599 inline void test( size_t n, const char * expect, ... )
600 {
601     char * buffer1 = malloc( 50 );
602     char * buffer2 = malloc( 50 );
603     int myrc;
604     int rc;
605     va_list ap;
606     va_start( ap, expect );
607     myrc = _PDCLIB_sprintf( buffer1, n, expect, ap );
608     rc = vsnprintf( buffer2, n, expect, ap );
609     if ( ( strcmp( buffer1, buffer2 ) != 0 ) || ( myrc != rc ) )
610     {
611         printf( "Output '%s', RC %d\nExpect '%s', RC %d\n\n", buffer1, myrc, buffer2, rc );
612     }
613     free( buffer1 );
614     free( buffer2 );
615 }
616
617 int _PDCLIB_sprintf( char * buffer, size_t n, const char * format, va_list ap )
618 {
619     struct status_t status = { 0, 0, n, 0, 0, buffer, 0, 0, NULL, ap };
620     while ( *format != '\0' )
621     {
622         const char * rc;
623         if ( ( *format != '%' ) || ( ( rc = parse_out( format, &status ) ) == format ) )
624         {
625             /* No conversion specifier, print verbatim */
626             buffer[ status.i++ ] = *(format++);
627         }
628         else
629         {
630             /* Continue parsing after conversion specifier */
631             format = rc;
632         }
633     }
634     buffer[ status.i ] = '\0';
635     return status.i;
636 }
637
638 #if 0
639 int _PDCLIB_fprintf( FILE * stream, const char * format, va_list ap )
640 {
641     struct status_t status = { 0, 0, SIZE_MAX, 0, 0, NULL, 0, 0, stream, ap };
642     while ( *format != '\0' )
643     {
644         const char * rc;
645         if ( ( *format != '%' ) || ( ( rc = parse_out( format, &status, ap ) ) == format ) )
646         {
647             /* No conversion specifier, print verbatim */
648             putc( *(format++), stream );
649         }
650         else
651         {
652             /* Continue parsing after conversion specifier */
653             format = rc;
654         }
655     }
656     return status.i;
657 }
658 #endif