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