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