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