]> pd.if.org Git - pdclib/blob - functions/_PDCLIB/print.c
3e48f2ad6569a9998d2c9d8ecfb8e961f055a497
[pdclib] / functions / _PDCLIB / print.c
1 /* $Id$ */
2
3 /* _PDCLIB_print( const char *, struct _PDCLIB_status_t * )
4
5    This file is part of the Public Domain C Library (PDCLib).
6    Permission is granted to use, modify, and / or redistribute at will.
7 */
8
9 #include <stdio.h>
10 #include <stdint.h>
11 #include <stdarg.h>
12 #include <stdlib.h>
13 #include <stddef.h>
14
15 /* Using an integer's bits as flags for both the conversion flags and length
16    modifiers.
17 */
18 /* FIXME: one too many flags to work on a 16-bit machine, join some (e.g. the
19           width flags) into a combined field.
20 */
21 #define E_minus    1<<0
22 #define E_plus     1<<1
23 #define E_alt      1<<2
24 #define E_space    1<<3
25 #define E_zero     1<<4
26 #define E_done     1<<5
27 #define E_char     1<<6
28 #define E_short    1<<7
29 #define E_long     1<<8
30 #define E_llong    1<<9
31 #define E_intmax   1<<10
32 #define E_size     1<<11
33 #define E_ptrdiff  1<<12
34 #define E_intptr   1<<13
35 #define E_ldouble  1<<14
36 #define E_lower    1<<15
37 #define E_unsigned 1<<16
38
39 /* This macro delivers a given character to either a memory buffer or a stream,
40    depending on the contents of 'status' (struct _PDCLIB_status_t).
41    x - the character to be delivered
42    i - pointer to number of characters already delivered in this call
43    n - pointer to maximum number of characters to be delivered in this call
44    s - the buffer into which the character shall be delivered
45    TODO: ref. fputs() for a better way to buffer handling
46 */
47 #define DELIVER( x ) \
48 do { \
49     if ( status->i < status->n ) { \
50         if ( status->stream != NULL ) \
51             putc( x, status->stream ); \
52         else \
53             status->s[status->i] = x; \
54     } \
55     ++(status->i); \
56 } while ( 0 )
57
58
59 static void intformat( intmax_t value, struct _PDCLIB_status_t * status )
60 {
61     /* At worst, we need two prefix characters (hex prefix). */
62     char preface[3] = "\0";
63     size_t preidx = 0;
64     if ( ( status->flags & E_alt ) && ( status->base == 16 || status->base == 8 ) )
65     {
66         /* Octal / hexadecimal prefix for "%#" conversions */
67         preface[ preidx++ ] = '0';
68         if ( status->base == 16 )
69         {
70             preface[ preidx++ ] = ( status->flags & E_lower ) ? 'x' : 'X';
71         }
72     }
73     if ( value < 0 )
74     {
75         /* Negative sign for negative values - at all times. */
76         preface[ preidx++ ] = '-';
77     }
78     else if ( ! ( status->flags & E_unsigned ) )
79     {
80         /* plus sign / extra space are only for unsigned conversions */
81         if ( status->flags & E_plus )
82         {
83             preface[ preidx++ ] = '+';
84         }
85         else if ( status->flags & E_space )
86         {
87             preface[ preidx++ ] = ' ';
88         }
89     }
90     {
91     size_t prec_pads = ( status->prec > status->current ) ? ( status->prec - status->current ) : 0;
92     if ( ! ( status->flags & ( E_minus | E_zero ) ) )
93     {
94         /* Space padding is only done if no zero padding or left alignment
95            is requested. Leave space for any prefixes determined above.
96         */
97         /* The number of characters to be printed, plus prefixes if any. */
98         /* This line contained probably the most stupid, time-wasting bug
99            I've ever perpetrated. Greetings to Samface, DevL, and all
100            sceners at Breakpoint 2006.
101         */
102         size_t characters = preidx + ( ( status->current > status->prec ) ? status->current : status->prec );
103         if ( status->width > characters )
104         {
105             for ( size_t i = 0; i < status->width - characters; ++i )
106             {
107                 DELIVER( ' ' );
108                 ++(status->current);
109             }
110         }
111     }
112     /* Now we did the padding, do the prefixes (if any). */
113     preidx = 0;
114     while ( preface[ preidx ] != '\0' )
115     {
116         DELIVER( preface[ preidx++ ] );
117         ++(status->current);
118     }
119     if ( ( ! ( status->flags & E_minus ) ) && ( status->flags & E_zero ) )
120     {
121         /* If field is not left aligned, and zero padding is requested, do
122            so.
123         */
124         while ( status->current < status->width )
125         {
126             DELIVER( '0' );
127             ++(status->current);
128         }
129     }
130     /* Do the precision padding if necessary. */
131     for ( size_t i = 0; i < prec_pads; ++i )
132     {
133         DELIVER( '0' );
134     }
135     }
136 }
137
138
139 /* This function recursively converts a given integer value to a character
140    stream. The conversion is done under the control of a given status struct
141    and written either to a character string or a stream, depending on that
142    same status struct. The status struct also keeps the function from exceeding
143    snprintf() limits, and enables any necessary padding / prefixing of the
144    output once the number of characters to be printed is known, which happens
145    at the lowermost recursion level.
146 */
147 static void int2base( intmax_t value, struct _PDCLIB_status_t * status )
148 {
149     /* Registering the character being printed at the end of the function here
150        already so it will be taken into account when the deepestmost recursion
151        does the prefix / padding stuff.
152     */
153     ++(status->current);
154     if ( ( value / status->base ) != 0 )
155     {
156         /* More digits to be done - recurse deeper */
157         int2base( value / status->base, status );
158     }
159     else
160     {
161         /* We reached the last digit, the deepest point of our recursion, and
162            only now know how long the number to be printed actually is. Now we
163            have to do the sign, prefix, width, and precision padding stuff
164            before printing the numbers while we resurface from the recursion.
165         */
166         intformat( value, status );
167     }
168     /* Recursion tail - print the current digit. */
169     {
170     int digit = value % status->base;
171     if ( digit < 0 )
172     {
173         digit *= -1;
174     }
175     if ( status->flags & E_lower )
176     {
177         /* Lowercase letters. Same array used for strto...(). */
178         DELIVER( _PDCLIB_digits[ digit ] );
179     }
180     else
181     {
182         /* Uppercase letters. Array only used here, only 0-F. */
183         DELIVER( _PDCLIB_Xdigits[ digit ] );
184     }
185     }
186 }
187
188
189 const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status )
190 {
191     const char * orig_spec = spec;
192     if ( *(++spec) == '%' )
193     {
194         /* %% -> print single '%' */
195         DELIVER( *spec );
196         return ++spec;
197     }
198     /* Initializing status structure */
199     status->flags = 0;
200     status->base  = 0;
201     status->current  = 0;
202     status->width = 0;
203     status->prec  = 0;
204
205     /* First come 0..n flags */
206     do
207     {
208         switch ( *spec )
209         {
210             case '-':
211                 /* left-aligned output */
212                 status->flags |= E_minus;
213                 ++spec;
214                 break;
215             case '+':
216                 /* positive numbers prefixed with '+' */
217                 status->flags |= E_plus;
218                 ++spec;
219                 break;
220             case '#':
221                 /* alternative format (leading 0x for hex, 0 for octal) */
222                 status->flags |= E_alt;
223                 ++spec;
224                 break;
225             case ' ':
226                 /* positive numbers prefixed with ' ' */
227                 status->flags |= E_space;
228                 ++spec;
229                 break;
230             case '0':
231                 /* right-aligned padding done with '0' instead of ' ' */
232                 status->flags |= E_zero;
233                 ++spec;
234                 break;
235             default:
236                 /* not a flag, exit flag parsing */
237                 status->flags |= E_done;
238                 break;
239         }
240     } while ( ! ( status->flags & E_done ) );
241
242     /* Optional field width */
243     if ( *spec == '*' )
244     {
245         /* Retrieve width value from argument stack */
246         int width = va_arg( status->arg, int );
247         if ( width < 0 )
248         {
249             status->flags |= E_minus;
250             status->width = abs( width );
251         }
252         else
253         {
254             status->width = width;
255         }
256         ++spec;
257     }
258     else
259     {
260         /* If a width is given, strtol() will return its value. If not given,
261            strtol() will return zero. In both cases, endptr will point to the
262            rest of the conversion specifier - just what we need.
263         */
264         status->width = (int)strtol( spec, (char**)&spec, 10 );
265     }
266
267     /* Optional precision */
268     if ( *spec == '.' )
269     {
270         ++spec;
271         if ( *spec == '*' )
272         {
273             /* Retrieve precision value from argument stack. A negative value
274                is as if no precision is given - as precision is initalized to
275                EOF (negative), there is no need for testing for negative here.
276             */
277             status->prec = va_arg( status->arg, int );
278         }
279         else
280         {
281             char * endptr;
282             status->prec = (int)strtol( spec, &endptr, 10 );
283             if ( spec == endptr )
284             {
285                 /* Decimal point but no number - bad conversion specifier. */
286                 return orig_spec;
287             }
288             spec = endptr;
289         }
290         /* Having a precision cancels out any zero flag. */
291         status->flags ^= E_zero;
292     }
293
294     /* Optional length modifier
295        We step one character ahead in any case, and step back only if we find
296        there has been no length modifier (or step ahead another character if it
297        has been "hh" or "ll").
298     */
299     switch ( *(spec++) )
300     {
301         case 'h':
302             if ( *spec == 'h' )
303             {
304                 /* hh -> char */
305                 status->flags |= E_char;
306                 ++spec;
307             }
308             else
309             {
310                 /* h -> short */
311                 status->flags |= E_short;
312             }
313             break;
314         case 'l':
315             if ( *spec == 'l' )
316             {
317                 /* ll -> long long */
318                 status->flags |= E_llong;
319                 ++spec;
320             }
321             else
322             {
323                 /* k -> long */
324                 status->flags |= E_long;
325             }
326             break;
327         case 'j':
328             /* j -> intmax_t, which might or might not be long long */
329             status->flags |= E_intmax;
330             break;
331         case 'z':
332             /* z -> size_t, which might or might not be unsigned int */
333             status->flags |= E_size;
334             break;
335         case 't':
336             /* t -> ptrdiff_t, which might or might not be long */
337             status->flags |= E_ptrdiff;
338             break;
339         case 'L':
340             /* L -> long double */
341             status->flags |= E_ldouble;
342             break;
343         default:
344             --spec;
345             break;
346     }
347
348     /* Conversion specifier */
349     switch ( *spec )
350     {
351         case 'd':
352             /* FALLTHROUGH */
353         case 'i':
354             status->base = 10;
355             break;
356         case 'o':
357             status->base = 8;
358             status->flags |= E_unsigned;
359             break;
360         case 'u':
361             status->base = 10;
362             status->flags |= E_unsigned;
363             break;
364         case 'x':
365             status->base = 16;
366             status->flags |= ( E_lower | E_unsigned );
367             break;
368         case 'X':
369             status->base = 16;
370             status->flags |= E_unsigned;
371             break;
372         case 'f':
373         case 'F':
374         case 'e':
375         case 'E':
376         case 'g':
377         case 'G':
378             break;
379         case 'a':
380         case 'A':
381             break;
382         case 'c':
383             /* TODO: Flags, wide chars. */
384             DELIVER( va_arg( status->arg, int ) );
385             return ++spec;
386         case 's':
387             /* TODO: Flags, wide chars. */
388             {
389                 char * s = va_arg( status->arg, char * );
390                 while ( *s != '\0' )
391                 {
392                     DELIVER( *(s++) );
393                 }
394                 return ++spec;
395             }
396         case 'p':
397             /* TODO: E_long -> E_intptr */
398             status->base = 16;
399             status->flags |= ( E_lower | E_unsigned | E_alt | E_long );
400             break;
401         case 'n':
402            {
403                int * val = va_arg( status->arg, int * );
404                *val = status->i;
405                return ++spec;
406            }
407         default:
408             /* No conversion specifier. Bad conversion. */
409             return orig_spec;
410     }
411
412     /* Do the actual output based on our findings */
413     if ( status->base != 0 )
414     {
415         /* Integer conversions */
416         /* TODO: Check for invalid flag combinations. */
417         if ( status->flags & E_unsigned )
418         {
419             uintmax_t value;
420             switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_size ) )
421             {
422                 case E_char:
423                     value = (uintmax_t)(unsigned char)va_arg( status->arg, int );
424                     break;
425                 case E_short:
426                     value = (uintmax_t)(unsigned short)va_arg( status->arg, int );
427                     break;
428                 case 0:
429                     value = (uintmax_t)va_arg( status->arg, unsigned int );
430                     break;
431                 case E_long:
432                     value = (uintmax_t)va_arg( status->arg, unsigned long );
433                     break;
434                 case E_llong:
435                     value = (uintmax_t)va_arg( status->arg, unsigned long long );
436                     break;
437                 case E_size:
438                     value = (uintmax_t)va_arg( status->arg, size_t );
439                     break;
440             }
441             ++(status->current);
442             /* FIXME: The if clause means one-digit values do not get formatted */
443             /* Was introduced originally to get value to "safe" levels re. uintmax_t. */
444             if ( ( value / status->base ) != 0 )
445             {
446                 int2base( (intmax_t)(value / status->base), status );
447             }
448             else
449             {
450                 intformat( (intmax_t)value, status );
451             }
452             int digit = value % status->base;
453             if ( digit < 0 )
454             {
455                 digit *= -1;
456             }
457             if ( status->flags & E_lower )
458             {
459                 DELIVER( _PDCLIB_digits[ digit ] );
460             }
461             else
462             {
463                 DELIVER( _PDCLIB_Xdigits[ digit ] );
464             }
465         }
466         else
467         {
468             switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_intmax ) )
469             {
470                 case E_char:
471                     int2base( (intmax_t)(char)va_arg( status->arg, int ), status );
472                     break;
473                 case E_short:
474                     int2base( (intmax_t)(short)va_arg( status->arg, int ), status );
475                     break;
476                 case 0:
477                     int2base( (intmax_t)va_arg( status->arg, int ), status );
478                     break;
479                 case E_long:
480                     int2base( (intmax_t)va_arg( status->arg, long ), status );
481                     break;
482                 case E_llong:
483                     int2base( (intmax_t)va_arg( status->arg, long long ), status );
484                     break;
485                 case E_ptrdiff:
486                     int2base( (intmax_t)va_arg( status->arg, ptrdiff_t ), status );
487                     break;
488                 case E_intmax:
489                     int2base( va_arg( status->arg, intmax_t ), status );
490                     break;
491             }
492         }
493         if ( status->flags & E_minus )
494         {
495             while ( status->current < status->width )
496             {
497                 DELIVER( ' ' );
498                 ++(status->current);
499             }
500         }
501         if ( status->i >= status->n )
502         {
503             status->s[status->n - 1] = '\0';
504         }
505     }
506     return ++spec;
507 }
508
509 #ifdef TEST
510 #include <_PDCLIB_test.h>
511
512 #include <limits.h>
513 #include <string.h>
514
515 static int testprintf( char * buffer, size_t n, const char * format, ... )
516 {
517     /* Members: base, flags, n, i, current, s, width, prec, stream, arg      */
518     struct _PDCLIB_status_t status;
519     status.base = 0;
520     status.flags = 0;
521     status.n = n;
522     status.i = 0;
523     status.current = 0;
524     status.s = buffer;
525     status.width = 0;
526     status.prec = 0;
527     status.stream = NULL;
528     va_start( status.arg, format );
529     memset( buffer, '\0', 100 );
530     if ( *(_PDCLIB_print( format, &status )) != '\0' )
531     {
532         printf( "_PDCLIB_print() did not return end-of-specifier on '%s'.\n", format );
533         ++TEST_RESULTS;
534     }
535     va_end( status.arg );
536     return status.i;
537 }
538
539 #define TEST_CONVERSION_ONLY
540
541 #define TESTCASE_SPRINTF( x ) TESTCASE( x )
542
543 int main( void )
544 {
545     char buffer[100];
546 #include "printf_testcases.incl"
547
548 #if 0
549     char buffer[100];
550     TESTCASE( testprintf( buffer, 100, "%hhd", CHAR_MIN ) == 4 );
551     TESTCASE( strcmp( buffer, "-128" ) == 0 );
552     TESTCASE( testprintf( buffer, 100, "%hhd", CHAR_MAX ) == 3 );
553     TESTCASE( strcmp( buffer, "127" ) == 0 );
554     TESTCASE( testprintf( buffer, 100, "%hhd", 0 ) == 1 );
555     TESTCASE( strcmp( buffer, "0" ) == 0 );
556     TESTCASE( testprintf( buffer, 100, "%hd", SHRT_MIN ) == 6 );
557     TESTCASE( strcmp( buffer, "-32768" ) == 0 );
558     TESTCASE( testprintf( buffer, 100, "%hd", SHRT_MAX ) == 5 );
559     TESTCASE( strcmp( buffer, "32767" ) == 0 );
560     TESTCASE( testprintf( buffer, 100, "%hd", 0 ) == 1 );
561     TESTCASE( strcmp( buffer, "0" ) == 0 );
562     TESTCASE( testprintf( buffer, 100, "%d", INT_MIN ) == 11 );
563     TESTCASE( strcmp( buffer, "-2147483648" ) == 0 );
564     TESTCASE( testprintf( buffer, 100, "%d", INT_MAX ) == 10 );
565     TESTCASE( strcmp( buffer, "2147483647" ) == 0 );
566     TESTCASE( testprintf( buffer, 100, "%d", 0 ) == 1 );
567     TESTCASE( strcmp( buffer, "0" ) == 0 );
568     TESTCASE( testprintf( buffer, 100, "%ld", LONG_MIN ) == 11 );
569     TESTCASE( strcmp( buffer, "-2147483648" ) == 0 );
570     TESTCASE( testprintf( buffer, 100, "%ld", LONG_MAX ) == 10 );
571     TESTCASE( strcmp( buffer, "2147483647" ) == 0 );
572     TESTCASE( testprintf( buffer, 100, "%ld", 0l ) == 1 );
573     TESTCASE( strcmp( buffer, "0" ) == 0 );
574     TESTCASE( testprintf( buffer, 100, "%lld", LLONG_MIN ) == 20 );
575     TESTCASE( strcmp( buffer, "-9223372036854775808" ) == 0 );
576     TESTCASE( testprintf( buffer, 100, "%lld", LLONG_MAX ) == 19 );
577     TESTCASE( strcmp( buffer, "9223372036854775807" ) == 0 );
578     TESTCASE( testprintf( buffer, 100, "%lld", 0ll ) );
579     TESTCASE( strcmp( buffer, "0" ) == 0 );
580     TESTCASE( testprintf( buffer, 100, "%hhu", UCHAR_MAX ) == 3 );
581     TESTCASE( strcmp( buffer, "255" ) == 0 );
582     TESTCASE( testprintf( buffer, 100, "%hhu", (unsigned char)-1 ) == 3 );
583     TESTCASE( strcmp( buffer, "255" ) == 0 );
584     TESTCASE( testprintf( buffer, 100, "%hu", USHRT_MAX ) == 5 );
585     TESTCASE( strcmp( buffer, "65535" ) == 0 );
586     TESTCASE( testprintf( buffer, 100, "%hu", (unsigned short)-1 ) == 5 );
587     TESTCASE( strcmp( buffer, "65535" ) == 0 );
588     TESTCASE( testprintf( buffer, 100, "%u", UINT_MAX ) == 10 );
589     TESTCASE( strcmp( buffer, "4294967295" ) == 0 );
590     TESTCASE( testprintf( buffer, 100, "%u", -1u ) == 10 );
591     TESTCASE( strcmp( buffer, "4294967295" ) == 0 );
592     TESTCASE( testprintf( buffer, 100, "%lu", ULONG_MAX ) == 10 );
593     TESTCASE( strcmp( buffer, "4294967295" ) == 0 );
594     TESTCASE( testprintf( buffer, 100, "%lu", -1ul ) == 10 );
595     TESTCASE( strcmp( buffer, "4294967295" ) == 0 );
596     TESTCASE( testprintf( buffer, 100, "%llu", ULLONG_MAX ) == 20 );
597     TESTCASE( strcmp( buffer, "18446744073709551615" ) == 0 );
598     TESTCASE( testprintf( buffer, 100, "%llu", -1ull ) == 20 );
599     TESTCASE( strcmp( buffer, "18446744073709551615" ) == 0 );
600     TESTCASE( testprintf( buffer, 100, "%X", UINT_MAX ) == 8 );
601     TESTCASE( strcmp( buffer, "FFFFFFFF" ) == 0 );
602     TESTCASE( testprintf( buffer, 100, "%#X", -1u ) == 10 );
603     TESTCASE( strcmp( buffer, "0XFFFFFFFF" ) == 0 );
604     TESTCASE( testprintf( buffer, 100, "%x", UINT_MAX ) == 8 );
605     TESTCASE( strcmp( buffer, "ffffffff" ) == 0 );
606     TESTCASE( testprintf( buffer, 100, "%#x", -1u ) == 10 );
607     TESTCASE( strcmp( buffer, "0xffffffff" ) == 0 );
608     TESTCASE( testprintf( buffer, 100, "%o", UINT_MAX ) == 11 );
609     TESTCASE( strcmp( buffer, "37777777777" ) == 0 );
610     TESTCASE( testprintf( buffer, 100, "%#o", -1u ) == 12 );
611     TESTCASE( strcmp( buffer, "037777777777" ) == 0 );
612     TESTCASE( testprintf( buffer, 100, "%+d", INT_MIN ) == 11 );
613     TESTCASE( strcmp( buffer, "-2147483648" ) == 0 );
614     TESTCASE( testprintf( buffer, 100, "%+d", INT_MAX ) == 11 );
615     TESTCASE( strcmp( buffer, "+2147483647" ) == 0 );
616     TESTCASE( testprintf( buffer, 100, "%+d", 0 ) == 2 );
617     TESTCASE( strcmp( buffer, "+0" ) == 0 );
618     TESTCASE( testprintf( buffer, 100, "%+u", UINT_MAX ) == 10 );
619     TESTCASE( strcmp( buffer, "4294967295" ) == 0 );
620     TESTCASE( testprintf( buffer, 100, "%+u", -1u ) == 10 );
621     TESTCASE( strcmp( buffer, "4294967295" ) == 0 );
622     TESTCASE( testprintf( buffer, 100, "% d", INT_MIN ) == 11 );
623     TESTCASE( strcmp( buffer, "-2147483648" ) == 0 );
624     TESTCASE( testprintf( buffer, 100, "% d", INT_MAX ) == 11 );
625     TESTCASE( strcmp( buffer, " 2147483647" ) == 0 );
626     TESTCASE( testprintf( buffer, 100, "% d", 0 ) == 2 );
627     TESTCASE( strcmp( buffer, " 0" ) == 0 );
628     TESTCASE( testprintf( buffer, 100, "% u", UINT_MAX ) == 10 );
629     TESTCASE( strcmp( buffer, "4294967295" ) == 0 );
630     TESTCASE( testprintf( buffer, 100, "% u", -1u ) == 10 );
631     TESTCASE( strcmp( buffer, "4294967295" ) == 0 );
632     TESTCASE( testprintf( buffer, 100, "%9d", INT_MIN ) == 11 );
633     TESTCASE( strcmp( buffer, "-2147483648" ) == 0 );
634     TESTCASE( testprintf( buffer, 100, "%9d", INT_MAX ) == 10 );
635     TESTCASE( strcmp( buffer, "2147483647" ) == 0 );
636     TESTCASE( testprintf( buffer, 100, "%10d", INT_MIN ) == 11 );
637     TESTCASE( strcmp( buffer, "-2147483648" ) == 0 );
638     TESTCASE( testprintf( buffer, 100, "%10d", INT_MAX ) == 10 );
639     TESTCASE( strcmp( buffer, "2147483647" ) == 0 );
640     TESTCASE( testprintf( buffer, 100, "%11d", INT_MIN ) == 11 );
641     TESTCASE( strcmp( buffer, "-2147483648" ) == 0 );
642     TESTCASE( testprintf( buffer, 100, "%11d", INT_MAX ) == 11 );
643     TESTCASE( strcmp( buffer, " 2147483647" ) == 0 );
644     TESTCASE( testprintf( buffer, 100, "%12d", INT_MIN ) == 12 );
645     TESTCASE( strcmp( buffer, " -2147483648" ) == 0 );
646     TESTCASE( testprintf( buffer, 100, "%12d", INT_MAX ) == 12 );
647     TESTCASE( strcmp( buffer, "  2147483647" ) == 0 );
648     TESTCASE( testprintf( buffer, 100, "%-9d", INT_MIN ) == 11 );
649     TESTCASE( strcmp( buffer, "-2147483648" ) == 0 );
650     TESTCASE( testprintf( buffer, 100, "%-9d", INT_MAX ) == 10 );
651     TESTCASE( strcmp( buffer, "2147483647" ) == 0 );
652     TESTCASE( testprintf( buffer, 100, "%-10d", INT_MIN ) == 11 );
653     TESTCASE( strcmp( buffer, "-2147483648" ) == 0 );
654     TESTCASE( testprintf( buffer, 100, "%-10d", INT_MAX ) == 10 );
655     TESTCASE( strcmp( buffer, "2147483647" ) == 0 );
656     TESTCASE( testprintf( buffer, 100, "%-11d", INT_MIN ) == 11 );
657     TESTCASE( strcmp( buffer, "-2147483648" ) == 0 );
658     TESTCASE( testprintf( buffer, 100, "%-11d", INT_MAX ) == 11 );
659     TESTCASE( strcmp( buffer, "2147483647 " ) == 0 );
660     TESTCASE( testprintf( buffer, 100, "%-12d", INT_MIN ) == 12 );
661     TESTCASE( strcmp( buffer, "-2147483648 " ) == 0 );
662     TESTCASE( testprintf( buffer, 100, "%-12d", INT_MAX ) == 12 );
663     TESTCASE( strcmp( buffer, "2147483647  " ) == 0 );
664     TESTCASE( testprintf( buffer, 100, "%09d", INT_MIN ) == 11 );
665     TESTCASE( strcmp( buffer, "-2147483648" ) == 0 );
666     TESTCASE( testprintf( buffer, 100, "%09d", INT_MAX ) == 10 );
667     TESTCASE( strcmp( buffer, "2147483647" ) == 0 );
668     TESTCASE( testprintf( buffer, 100, "%010d", INT_MIN ) == 11 );
669     TESTCASE( strcmp( buffer, "-2147483648" ) == 0 );
670     TESTCASE( testprintf( buffer, 100, "%010d", INT_MAX ) == 10 );
671     TESTCASE( strcmp( buffer, "2147483647" ) == 0 );
672     TESTCASE( testprintf( buffer, 100, "%011d", INT_MIN ) == 11 );
673     TESTCASE( strcmp( buffer, "-2147483648" ) == 0 );
674     TESTCASE( testprintf( buffer, 100, "%011d", INT_MAX ) == 11 );
675     TESTCASE( strcmp( buffer, "02147483647" ) == 0 );
676     TESTCASE( testprintf( buffer, 100, "%012d", INT_MIN ) == 12 );
677     TESTCASE( strcmp( buffer, "-02147483648" ) == 0 );
678     TESTCASE( testprintf( buffer, 100, "%012d", INT_MAX ) == 12 );
679     TESTCASE( strcmp( buffer, "002147483647" ) == 0 );
680     TESTCASE( testprintf( buffer, 100, "%-09d", INT_MIN ) == 11 );
681     TESTCASE( strcmp( buffer, "-2147483648" ) == 0 );
682     TESTCASE( testprintf( buffer, 100, "%-09d", INT_MAX ) == 10 );
683     TESTCASE( strcmp( buffer, "2147483647" ) == 0 );
684     TESTCASE( testprintf( buffer, 100, "%-010d", INT_MIN ) == 11 );
685     TESTCASE( strcmp( buffer, "-2147483648" ) == 0 );
686     TESTCASE( testprintf( buffer, 100, "%-010d", INT_MAX ) == 10 );
687     TESTCASE( strcmp( buffer, "2147483647" ) == 0 );
688     TESTCASE( testprintf( buffer, 100, "%-011d", INT_MIN ) == 11 );
689     TESTCASE( strcmp( buffer, "-2147483648" ) == 0 );
690     TESTCASE( testprintf( buffer, 100, "%-011d", INT_MAX ) == 11 );
691     TESTCASE( strcmp( buffer, "2147483647 " ) == 0 );
692     TESTCASE( testprintf( buffer, 100, "%-012d", INT_MIN ) == 12 );
693     TESTCASE( strcmp( buffer, "-2147483648 " ) == 0 );
694     TESTCASE( testprintf( buffer, 100, "%-012d", INT_MAX ) == 12 );
695     TESTCASE( strcmp( buffer, "2147483647  " ) == 0 );
696     TESTCASE( testprintf( buffer, 8, "%9d", INT_MAX ) == 10 );
697     TESTCASE( strcmp( buffer, "2147483" ) == 0 );
698     TESTCASE( testprintf( buffer, 8, "%9d", INT_MIN ) == 11 );
699     TESTCASE( strcmp( buffer, "-214748" ) == 0 );
700     TESTCASE( testprintf( buffer, 9, "%9d", INT_MAX ) == 10 );
701     TESTCASE( strcmp( buffer, "21474836" ) == 0 );
702     TESTCASE( testprintf( buffer, 9, "%9d", INT_MIN ) == 11 );
703     TESTCASE( strcmp( buffer, "-2147483" ) == 0 );
704     TESTCASE( testprintf( buffer, 10, "%9d", INT_MAX ) == 10 );
705     TESTCASE( strcmp( buffer, "214748364" ) == 0 );
706     TESTCASE( testprintf( buffer, 10, "%9d", INT_MIN ) == 11 );
707     TESTCASE( strcmp( buffer, "-21474836" ) == 0 );
708     TESTCASE( testprintf( buffer, 9, "%10d", INT_MAX ) == 10 );
709     TESTCASE( strcmp( buffer, "21474836" ) == 0 );
710     TESTCASE( testprintf( buffer, 9, "%10d", INT_MIN ) == 11 );
711     TESTCASE( strcmp( buffer, "-2147483" ) == 0 );
712     TESTCASE( testprintf( buffer, 10, "%10d", INT_MAX ) == 10 );
713     TESTCASE( strcmp( buffer, "214748364" ) == 0 );
714     TESTCASE( testprintf( buffer, 10, "%10d", INT_MIN ) == 11 );
715     TESTCASE( strcmp( buffer, "-21474836" ) == 0 );
716     TESTCASE( testprintf( buffer, 11, "%10d", INT_MAX ) == 10 );
717     TESTCASE( strcmp( buffer, "2147483647" ) == 0 );
718     TESTCASE( testprintf( buffer, 11, "%10d", INT_MIN ) == 11 );
719     TESTCASE( strcmp( buffer, "-214748364" ) == 0 );
720     TESTCASE( testprintf( buffer, 10, "%11d", INT_MAX ) == 11 );
721     TESTCASE( strcmp( buffer, " 21474836" ) == 0 );
722     TESTCASE( testprintf( buffer, 10, "%11d", INT_MIN ) == 11 );
723     TESTCASE( strcmp( buffer, "-21474836" ) == 0 );
724     TESTCASE( testprintf( buffer, 11, "%11d", INT_MAX ) == 11 );
725     TESTCASE( strcmp( buffer, " 214748364" ) == 0 );
726     TESTCASE( testprintf( buffer, 11, "%11d", INT_MIN ) == 11 );
727     TESTCASE( strcmp( buffer, "-214748364" ) == 0 );
728     TESTCASE( testprintf( buffer, 12, "%11d", INT_MAX ) == 11 );
729     TESTCASE( strcmp( buffer, " 2147483647" ) == 0 );
730     TESTCASE( testprintf( buffer, 12, "%11d", INT_MIN ) == 11 );
731     TESTCASE( strcmp( buffer, "-2147483648" ) == 0 );
732     TESTCASE( testprintf( buffer, 11, "%12d", INT_MAX ) == 12 );
733     TESTCASE( strcmp( buffer, "  21474836" ) == 0 );
734     TESTCASE( testprintf( buffer, 11, "%12d", INT_MIN ) == 12 );
735     TESTCASE( strcmp( buffer, " -21474836" ) == 0 );
736     TESTCASE( testprintf( buffer, 12, "%12d", INT_MAX ) == 12 );
737     TESTCASE( strcmp( buffer, "  214748364" ) == 0 );
738     TESTCASE( testprintf( buffer, 12, "%12d", INT_MIN ) == 12 );
739     TESTCASE( strcmp( buffer, " -214748364" ) == 0 );
740     TESTCASE( testprintf( buffer, 13, "%12d", INT_MAX ) == 12 );
741     TESTCASE( strcmp( buffer, "  2147483647" ) == 0 );
742     TESTCASE( testprintf( buffer, 13, "%12d", INT_MIN ) == 12 );
743     TESTCASE( strcmp( buffer, " -2147483648" ) == 0 );
744     TESTCASE( testprintf( buffer, 100, "%030.20d", INT_MAX ) == 30 );
745     TESTCASE( strcmp( buffer, "          00000000002147483647" ) == 0 );
746     TESTCASE( testprintf( buffer, 100, "%.6x", UINT_MAX ) == 8 );
747     TESTCASE( strcmp( buffer, "ffffffff" ) == 0 );
748     TESTCASE( testprintf( buffer, 100, "%#6.3x", UINT_MAX ) == 10 );
749     TESTCASE( strcmp( buffer, "0xffffffff" ) == 0 );
750     TESTCASE( testprintf( buffer, 100, "%#3.6x", UINT_MAX ) == 10 );
751     TESTCASE( strcmp( buffer, "0xffffffff" ) == 0 );
752     TESTCASE( testprintf( buffer, 100, "%.6d", INT_MIN ) == 11 );
753     TESTCASE( strcmp( buffer, "-2147483648" ) == 0 );
754     TESTCASE( testprintf( buffer, 100, "%6.3d", INT_MIN ) == 11 );
755     TESTCASE( strcmp( buffer, "-2147483648" ) == 0 );
756     TESTCASE( testprintf( buffer, 100, "%3.6d", INT_MIN ) == 11 );
757     TESTCASE( strcmp( buffer, "-2147483648" ) == 0 );
758     TESTCASE( testprintf( buffer, 100, "%#0.6x", UINT_MAX ) == 10 );
759     TESTCASE( strcmp( buffer, "0xffffffff" ) == 0 );
760     TESTCASE( testprintf( buffer, 100, "%#06.3x", UINT_MAX ) == 10 );
761     TESTCASE( strcmp( buffer, "0xffffffff" ) == 0 );
762     TESTCASE( testprintf( buffer, 100, "%#03.6x", UINT_MAX ) == 10 );
763     TESTCASE( strcmp( buffer, "0xffffffff" ) == 0 );
764     TESTCASE( testprintf( buffer, 100, "%#0.6d", INT_MAX ) == 10 );
765     TESTCASE( strcmp( buffer, "2147483647" ) == 0 );
766     TESTCASE( testprintf( buffer, 100, "%#06.3d", INT_MAX ) == 10 );
767     TESTCASE( strcmp( buffer, "2147483647" ) == 0 );
768     TESTCASE( testprintf( buffer, 100, "%#03.6d", INT_MAX ) == 10 );
769     TESTCASE( strcmp( buffer, "2147483647" ) == 0 );
770     TESTCASE( testprintf( buffer, 100, "%#+.6d", INT_MAX ) == 11 );
771     TESTCASE( strcmp( buffer, "+2147483647" ) == 0 );
772     TESTCASE( testprintf( buffer, 100, "%#+6.3d", INT_MAX ) == 11 );
773     TESTCASE( strcmp( buffer, "+2147483647" ) == 0 );
774     TESTCASE( testprintf( buffer, 100, "%#+3.6d", INT_MAX ) == 11 );
775     TESTCASE( strcmp( buffer, "+2147483647" ) == 0 );
776     TESTCASE( testprintf( buffer, 100, "%+0.6d", INT_MAX ) == 11 );
777     TESTCASE( strcmp( buffer, "+2147483647" ) == 0 );
778     TESTCASE( testprintf( buffer, 100, "%+06.3d", INT_MAX ) == 11 );
779     TESTCASE( strcmp( buffer, "+2147483647" ) == 0 );
780     TESTCASE( testprintf( buffer, 100, "%+03.6d", INT_MAX ) == 11 );
781     TESTCASE( strcmp( buffer, "+2147483647" ) == 0 );
782     TESTCASE( testprintf( buffer, 100, "%c", 'x' ) == 1 );
783     TESTCASE( strcmp( buffer, "x" ) == 0 );
784     TESTCASE( testprintf( buffer, 100, "%s", "abcdef" ) == 6 );
785     TESTCASE( strcmp( buffer, "abcdef" ) == 0 );
786     TESTCASE( testprintf( buffer, 100, "%p", (void *)0xdeadbeef ) == 10 );
787     TESTCASE( strcmp( buffer, "0xdeadbeef" ) == 0 );
788 #endif
789     return TEST_RESULTS;
790 }
791
792 #endif