]> pd.if.org Git - pdclib/blob - draft.c
Houston, we have lift off!
[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 };
44
45 const char * parse_out( const char * spec, struct status_t * status, va_list ap );
46 inline void test( size_t n, const char * expect, ... );
47 int _PDCLIB_sprintf( char * buffer, size_t n, const char * format, va_list ap );
48
49 /* The following only for testing. */
50 #include <limits.h>
51 #include <string.h>
52
53 int main( void )
54 {
55     puts( "- Signed min / max -\n" );
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     puts( "- Unsigned min / max -\n" );
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     puts( "- Hex and Octal, normal and alternative, upper and lowercase -\n" );
83     test( SIZE_MAX, "%X", UINT_MAX );
84     test( SIZE_MAX, "%#X", -1u );
85     test( SIZE_MAX, "%x", UINT_MAX );
86     test( SIZE_MAX, "%#x", -1u );
87     test( SIZE_MAX, "%o", UINT_MAX );
88     test( SIZE_MAX, "%#o", -1u );
89     puts( "- Plus flag -\n" );
90     test( SIZE_MAX, "%+d", INT_MIN );
91     test( SIZE_MAX, "%+d", INT_MAX );
92     test( SIZE_MAX, "%+d", 0 );
93     test( SIZE_MAX, "%+u", UINT_MAX );
94     test( SIZE_MAX, "%+u", -1u );
95     puts( "- Space flag -\n" );
96     test( SIZE_MAX, "% d", INT_MIN );
97     test( SIZE_MAX, "% d", INT_MAX );
98     test( SIZE_MAX, "% d", 0 );
99     test( SIZE_MAX, "% u", UINT_MAX );
100     test( SIZE_MAX, "% u", -1u );
101     puts( "- Field width -\n" );
102     test( SIZE_MAX, "%9d", INT_MIN );
103     test( SIZE_MAX, "%9d", INT_MAX );
104     test( SIZE_MAX, "%10d", INT_MIN );
105     test( SIZE_MAX, "%10d", INT_MAX );
106     test( SIZE_MAX, "%11d", INT_MIN );
107     test( SIZE_MAX, "%11d", INT_MAX );
108     test( SIZE_MAX, "%12d", INT_MIN );
109     test( SIZE_MAX, "%12d", INT_MAX );
110     puts( "- Field width (left bound) -\n" );
111     test( SIZE_MAX, "%-9d", INT_MIN );
112     test( SIZE_MAX, "%-9d", INT_MAX );
113     test( SIZE_MAX, "%-10d", INT_MIN );
114     test( SIZE_MAX, "%-10d", INT_MAX );
115     test( SIZE_MAX, "%-11d", INT_MIN );
116     test( SIZE_MAX, "%-11d", INT_MAX );
117     test( SIZE_MAX, "%-12d", INT_MIN );
118     test( SIZE_MAX, "%-12d", INT_MAX );
119     puts( "- Field width, zero padding -\n");
120     test( SIZE_MAX, "%09d", INT_MIN );
121     test( SIZE_MAX, "%09d", INT_MAX );
122     test( SIZE_MAX, "%010d", INT_MIN );
123     test( SIZE_MAX, "%010d", INT_MAX );
124     test( SIZE_MAX, "%011d", INT_MIN );
125     test( SIZE_MAX, "%011d", INT_MAX );
126     test( SIZE_MAX, "%012d", INT_MIN );
127     test( SIZE_MAX, "%012d", INT_MAX );
128     puts( "- Field width, zero padding (left bound) -\n" );
129     test( SIZE_MAX, "%-09d", INT_MIN );
130     test( SIZE_MAX, "%-09d", INT_MAX );
131     test( SIZE_MAX, "%-010d", INT_MIN );
132     test( SIZE_MAX, "%-010d", INT_MAX );
133     test( SIZE_MAX, "%-011d", INT_MIN );
134     test( SIZE_MAX, "%-011d", INT_MAX );
135     test( SIZE_MAX, "%-012d", INT_MIN );
136     test( SIZE_MAX, "%-012d", INT_MAX );
137     return 0;
138 }
139
140 /* x - the character to be delivered
141    i - pointer to number of characters already delivered in this call
142    n - pointer to maximum number of characters to be delivered in this call
143    s - the buffer into which the character shall be delivered
144    TODO: Overruns.
145 */
146 #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 )
147
148 static void int2base( intmax_t value, struct status_t * status )
149 {
150     ++(status->this);
151     if ( ( value / status->base ) != 0 )
152     {
153         int2base( value / status->base, status );
154     }
155     else
156     {
157         char preface[3] = "\0\0";
158         size_t preidx = 0;
159         if ( ( status->flags & E_alt ) && ( status->base == 16 || status->base == 8 ) )
160         {
161             preface[ preidx++ ] = '0';
162             if ( status->base == 16 )
163             {
164                 preface[ preidx++ ] = ( status->flags & E_lower ) ? 'x' : 'X';
165             }
166         }
167         if ( value < 0 )
168         {
169             preface[ preidx++ ] = '-';
170         }
171         else if ( ! ( status->flags & E_unsigned ) )
172         {
173             if ( status->flags & E_plus )
174             {
175                 preface[ preidx++ ] = '+';
176             }
177             else if ( status->flags & E_space )
178             {
179                 preface[ preidx++ ] = ' ';
180             }
181         }
182         if ( ! ( status->flags & ( E_minus | E_zero ) ) )
183         {
184             while ( ( status->this + preidx ) < status->width )
185             {
186                 DELIVER( ' ' );
187                 ++(status->this);
188             }
189         }
190         preidx = 0;
191         while ( preface[ preidx ] != '\0' )
192         {
193             DELIVER( preface[ preidx++ ] );
194             ++(status->this);
195         }
196         if ( ( ! ( status->flags & E_minus ) ) && ( status->flags & E_zero ) )
197         {
198             while ( status->this < status->width )
199             {
200                 DELIVER( '0' );
201                 ++(status->this);
202             }
203         }
204     }
205     {
206     int digit = value % status->base;
207     if ( digit < 0 )
208     {
209         digit *= -1;
210     }
211     if ( status->flags & E_lower )
212     {
213         DELIVER( _PDCLIB_digits[ digit ] );
214     }
215     else
216     {
217         DELIVER( _PDCLIB_Xdigits[ digit ] );
218     }
219     }
220 }
221
222 const char * parse_out( const char * spec, struct status_t * status, va_list ap )
223 {
224     const char * orig_spec = spec;
225     if ( *(++spec) == '%' )
226     {
227         DELIVER( *spec );
228         return spec;
229     }
230     /* Initializing status structure */
231     status->flags = 0;
232     status->base = 0;
233     status->this = 0;
234     status->width = 0;
235     status->prec = 0;
236
237     /* First come 0..n flags */
238     do
239     {
240         switch ( *spec )
241         {
242             case '-':
243                 status->flags |= E_minus;
244                 ++spec;
245                 break;
246             case '+':
247                 status->flags |= E_plus;
248                 ++spec;
249                 break;
250             case '#':
251                 status->flags |= E_alt;
252                 ++spec;
253                 break;
254             case ' ':
255                 status->flags |= E_space;
256                 ++spec;
257                 break;
258             case '0':
259                 status->flags |= E_zero;
260                 ++spec;
261                 break;
262             default:
263                 status->flags |= E_done;
264                 break;
265         }
266     } while ( ! ( status->flags & E_done ) );
267
268     /* Optional field width */
269     if ( *spec == '*' )
270     {
271         /* Retrieve width value from argument stack */
272         if ( ( status->width = va_arg( ap, int ) ) < 0 )
273         {
274             /* Negative value is '-' flag plus absolute value */
275             status->flags |= E_minus;
276             status->width *= -1;
277         }
278         ++spec;
279     }
280     else
281     {
282         /* If a width is given, strtol() will return its value. If not given,
283            strtol() will return zero. In both cases, endptr will point to the
284            rest of the conversion specifier - just what we need.
285         */
286         status->width = (int)strtol( spec, (char**)&spec, 10 );
287     }
288
289     /* Optional precision */
290     if ( *spec == '.' )
291     {
292         ++spec;
293         if ( *spec == '*' )
294         {
295             /* Retrieve precision value from argument stack. A negative value
296                is as if no precision is given - as precision is initalized to
297                EOF (negative), there is no need for testing for negative here.
298             */
299             status->prec = va_arg( ap, int );
300         }
301         else
302         {
303             char * endptr;
304             status->prec = (int)strtol( spec, &endptr, 10 );
305             if ( spec == endptr )
306             {
307                 /* Decimal point but no number - bad conversion specifier. */
308                 return orig_spec;
309             }
310         }
311     }
312
313     /* Optional length modifier
314        We step one character ahead in any case, and step back only if we find
315        there has been no length modifier (or step ahead another character if it
316        has been "hh" or "ll").
317     */
318     switch ( *(spec++) )
319     {
320         case 'h':
321             if ( *spec == 'h' )
322             {
323                 status->flags |= E_char;
324                 ++spec;
325             }
326             else
327             {
328                 status->flags |= E_short;
329             }
330             break;
331         case 'l':
332             if ( *spec == 'l' )
333             {
334                 status->flags |= E_llong;
335                 ++spec;
336             }
337             else
338             {
339                 status->flags |= E_long;
340             }
341             break;
342         case 'j':
343             status->flags |= E_intmax;
344             break;
345         case 'z':
346             status->flags |= E_size;
347             break;
348         case 't':
349             status->flags |= E_ptrdiff;
350             break;
351         case 'L':
352             status->flags |= E_double;
353             break;
354         default:
355             --spec;
356             break;
357     }
358
359     /* Conversion specifier */
360     switch ( *spec )
361     {
362         case 'd':
363         case 'i':
364             status->base = 10;
365             break;
366         case 'o':
367             status->base = 8;
368             status->flags |= E_unsigned;
369             break;
370         case 'u':
371             status->base = 10;
372             status->flags |= E_unsigned;
373             break;
374         case 'x':
375             status->base = 16;
376             status->flags |= ( E_lower | E_unsigned );
377             break;
378         case 'X':
379             status->base = 16;
380             status->flags |= E_unsigned;
381             break;
382         case 'f':
383         case 'F':
384         case 'e':
385         case 'E':
386         case 'g':
387         case 'G':
388             break;
389         case 'a':
390         case 'A':
391             break;
392         case 'c':
393             break;
394         case 's':
395             break;
396         case 'p':
397             /* uint2base( 16, (intptr_t)value, true ) */
398         case 'n':
399             break;
400         default:
401             /* No conversion specifier. Bad conversion. */
402             return orig_spec;
403     }
404
405     /* Do the actual output based on our findings */
406     if ( status->base != 0 )
407     {
408         /* Integer conversions */
409         /* TODO: Check for invalid flag combinations. */
410         if ( status->flags & E_unsigned )
411         {
412             uintmax_t value;
413             switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_size ) )
414             {
415                 case E_char:
416                     value = (uintmax_t)(unsigned char)va_arg( ap, int );
417                     break;
418                 case E_short:
419                     value = (uintmax_t)(unsigned short)va_arg( ap, int );
420                     break;
421                 case 0:
422                     value = (uintmax_t)va_arg( ap, unsigned int );
423                     break;
424                 case E_long:
425                     value = (uintmax_t)va_arg( ap, unsigned long );
426                     break;
427                 case E_llong:
428                     value = (uintmax_t)va_arg( ap, unsigned long long );
429                     break;
430                 case E_size:
431                     value = (uintmax_t)va_arg( ap, size_t );
432                     break;
433             }
434             ++(status->this);
435             if ( ( value / status->base ) != 0 )
436             {
437                 int2base( (intmax_t)(value / status->base), status );
438             }
439             int digit = value % status->base;
440             if ( digit < 0 )
441             {
442                 digit *= -1;
443             }
444             if ( status->flags & E_lower )
445             {
446                 DELIVER( _PDCLIB_digits[ digit ] );
447             }
448             else
449             {
450                 DELIVER( _PDCLIB_Xdigits[ digit ] );
451             }
452         }
453         else
454         {
455             switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_intmax ) )
456             {
457                 case E_char:
458                     int2base( (intmax_t)(char)va_arg( ap, int ), status );
459                     break;
460                 case E_short:
461                     int2base( (intmax_t)(short)va_arg( ap, int ), status );
462                     break;
463                 case 0:
464                     int2base( (intmax_t)va_arg( ap, int ), status );
465                     break;
466                 case E_long:
467                     int2base( (intmax_t)va_arg( ap, long ), status );
468                     break;
469                 case E_llong:
470                     int2base( (intmax_t)va_arg( ap, long long ), status );
471                     break;
472                 case E_ptrdiff:
473                     int2base( (intmax_t)va_arg( ap, ptrdiff_t ), status );
474                     break;
475                 case E_intmax:
476                     int2base( va_arg( ap, intmax_t ), status );
477                     break;
478             }
479         }
480         if ( status->flags & E_minus )
481         {
482             while ( status->this < status->width )
483             {
484                 DELIVER( ' ' );
485                 ++(status->this);
486             }
487         }
488         if ( status->i >= status->n )
489         {
490             status->s[status->n - 1] = '\0';
491         }
492     }
493     return ++spec;
494 }
495
496 inline void test( size_t n, const char * expect, ... )
497 {
498     char * buffer1 = malloc( 50 );
499     char * buffer2 = malloc( 50 );
500     int myrc;
501     int rc;
502     va_list ap;
503     va_start( ap, expect );
504     myrc = _PDCLIB_sprintf( buffer1, n, expect, ap );
505     rc = vsprintf( buffer2, expect, ap );
506     if ( ( strcmp( buffer1, buffer2 ) != 0 ) || ( myrc != rc ) )
507     {
508         printf( "Output '%s', RC %d\nExpect '%s', RC %d\n\n", buffer1, myrc, buffer2, rc );
509     }
510     free( buffer1 );
511     free( buffer2 );
512 }
513
514 int _PDCLIB_sprintf( char * buffer, size_t n, const char * format, va_list ap )
515 {
516     struct status_t status = { 0, 0, n, 0, 0, buffer, 0, 0, NULL };
517     while ( *format != '\0' )
518     {
519         const char * rc;
520         if ( ( *format != '%' ) || ( ( rc = parse_out( format, &status, ap ) ) == format ) )
521         {
522             /* No conversion specifier, print verbatim */
523             buffer[ status.i++ ] = *format;
524         }
525         else
526         {
527             /* Continue parsing after conversion specifier */
528             format = rc;
529         }
530     }
531     buffer[ status.i ] = '\0';
532     return status.i;
533 }
534
535 #if 0
536 int _PDCLIB_fprintf( FILE * stream, const char * format, va_list ap )
537 {
538     char * buffer = malloc( 50 );
539     struct status_t status = { 0, 0, SIZE_MAX, 0, 0, /* stream->buffer */ buffer, 0, 0, stream };
540     while ( *format != '\0' )
541     {
542         const char * rc;
543         if ( ( *format != '%' ) || ( ( rc = parse_out( format, &status, ap ) ) == format ) )
544         {
545             /* No conversion specifier, print verbatim */
546             putc( *(format++), stream );
547         }
548         else
549         {
550             /* Continue parsing after conversion specifier */
551             format = rc;
552         }
553     }
554     return status.i;
555 }
556 #endif