]> pd.if.org Git - pdclib/blob - functions/_PDCLIB/print.c
0ac327220234d628f76893252f703b673bbea137
[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 */
19 #define E_minus    1<<0
20 #define E_plus     1<<1
21 #define E_alt      1<<2
22 #define E_space    1<<3
23 #define E_zero     1<<4
24 #define E_done     1<<5
25 #define E_char     1<<6
26 #define E_short    1<<7
27 #define E_long     1<<8
28 #define E_llong    1<<9
29 #define E_intmax   1<<10
30 #define E_size     1<<11
31 #define E_ptrdiff  1<<12
32 #define E_intptr   1<<13
33 #define E_ldouble  1<<14
34 #define E_lower    1<<15
35 #define E_unsigned 1<<16
36
37 /* This macro delivers a given character to either a memory buffer or a stream,
38    depending on the contents of 'status' (struct _PDCLIB_status_t).
39    x - the character to be delivered
40    i - pointer to number of characters already delivered in this call
41    n - pointer to maximum number of characters to be delivered in this call
42    s - the buffer into which the character shall be delivered
43 */
44 #define DELIVER( x ) \
45 do { \
46     if ( status->i < status->n ) { \
47         if ( status->stream != NULL ) { \
48             status->stream->buffer[status->stream->bufidx++] = x; \
49             if ( ( status->stream->bufidx == status->stream->bufsize ) \
50               || ( ( status->stream->status & _IOLBF ) && ( x == '\n' ) ) \
51               || ( status->stream->status & _IONBF ) ) \
52                 fflush( status->stream ); \
53         } else \
54             status->s[status->i] = x; \
55     } \
56     ++(status->i); \
57 } while ( 0 )
58
59 /* This function recursively converts a given integer value to a character
60    stream. The conversion is done under the control of a given status struct
61    and written either to a character string or a stream, depending on that
62    same status struct. The status struct also keeps the function from exceeding
63    snprintf() limits, and enables any necessary padding / prefixing of the
64    output once the number of characters to be printed is known, which happens
65    at the lowermost recursion level.
66 */
67 static void int2base( intmax_t value, struct _PDCLIB_status_t * status )
68 {
69     /* Registering the character being printed at the end of the function here
70        already so it will be taken into account when the deepestmost recursion
71        does the prefix / padding stuff.
72     */
73     ++(status->this);
74     if ( ( value / status->base ) != 0 )
75     {
76         /* More digits to be done - recurse deeper */
77         int2base( value / status->base, status );
78     }
79     else
80     {
81         /* We reached the last digit, the deepest point of our recursion, and
82            only now know how long the number to be printed actually is. Now we
83            have to do the sign, prefix, width, and precision padding stuff
84            before printing the numbers while we resurface from the recursion.
85         */
86         /* At worst, we need two prefix characters (hex prefix). */
87         char preface[3] = "\0";
88         size_t preidx = 0;
89         if ( ( status->flags & E_alt ) && ( status->base == 16 || status->base == 8 ) )
90         {
91             /* Octal / hexadecimal prefix for "%#" conversions */
92             preface[ preidx++ ] = '0';
93             if ( status->base == 16 )
94             {
95                 preface[ preidx++ ] = ( status->flags & E_lower ) ? 'x' : 'X';
96             }
97         }
98         if ( value < 0 )
99         {
100             /* Negative sign for negative values - at all times. */
101             preface[ preidx++ ] = '-';
102         }
103         else if ( ! ( status->flags & E_unsigned ) )
104         {
105             /* plus sign / extra space are only for unsigned conversions */
106             if ( status->flags & E_plus )
107             {
108                 preface[ preidx++ ] = '+';
109             }
110             else if ( status->flags & E_space )
111             {
112                 preface[ preidx++ ] = ' ';
113             }
114         }
115         {
116         size_t prec_pads = ( status->prec > status->this ) ? ( status->prec - status->this ) : 0;
117         if ( ! ( status->flags & ( E_minus | E_zero ) ) )
118         {
119             /* Space padding is only done if no zero padding or left alignment
120                is requested. Leave space for any prefixes determined above.
121             */
122             /* The number of characters to be printed, plus prefixes if any. */
123             /* This line contained probably the most stupid, time-wasting bug
124                I've ever perpetrated. Greetings to Samface, DevL, and all
125                sceners at Breakpoint 2006.
126             */
127             size_t characters = preidx + ( ( status->this > status->prec ) ? status->this : status->prec );
128             if ( status->width > characters )
129             {
130                 for ( size_t i = 0; i < status->width - characters; ++i )
131                 {
132                     DELIVER( ' ' );
133                     /*
134                     do
135                     {
136                         if ( status->i < status->n )
137                         {
138                             if ( status->stream != 0 )
139                                 do
140                                 {
141                                     status->stream->buffer[status->stream->bufidx++] = (char)' ',
142                                     if ( ( status->stream->bufidx == status->stream->bufsize )
143                                       || ( ( status->stream->status & 2 ) && (char)' ' == '\n' )
144                                       || ( status->stream->status & 4 ) )
145                                         fflush( status->stream )
146                                 } while (0),
147                                 ' ';
148                             else status->s[status->i] = ' ';
149                         }
150                         ++(status->i);
151                     } while ( 0 );
152                     */
153                     ++(status->this);
154                 }
155             }
156         }
157         /* Now we did the padding, do the prefixes (if any). */
158         preidx = 0;
159         while ( preface[ preidx ] != '\0' )
160         {
161             DELIVER( preface[ preidx++ ] );
162             ++(status->this);
163         }
164         if ( ( ! ( status->flags & E_minus ) ) && ( status->flags & E_zero ) )
165         {
166             /* If field is not left aligned, and zero padding is requested, do
167                so.
168             */
169             while ( status->this < status->width )
170             {
171                 DELIVER( '0' );
172                 ++(status->this);
173             }
174         }
175         /* Do the precision padding if necessary. */
176         for ( size_t i = 0; i < prec_pads; ++i )
177         {
178             DELIVER( '0' );
179         }
180         }
181     }
182     /* Recursion tail - print the current digit. */
183     {
184     int digit = value % status->base;
185     if ( digit < 0 )
186     {
187         digit *= -1;
188     }
189     if ( status->flags & E_lower )
190     {
191         /* Lowercase letters. Same array used for strto...(). */
192         DELIVER( _PDCLIB_digits[ digit ] );
193     }
194     else
195     {
196         /* Uppercase letters. Array only used here, only 0-F. */
197         DELIVER( _PDCLIB_Xdigits[ digit ] );
198     }
199     }
200 }
201
202 const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status )
203 {
204     const char * orig_spec = spec;
205     if ( *(++spec) == '%' )
206     {
207         /* %% -> print single '%' */
208         DELIVER( *spec );
209         return ++spec;
210     }
211     /* Initializing status structure */
212     status->flags = 0;
213     status->base  = 0;
214     status->this  = 0;
215     status->width = 0;
216     status->prec  = 0;
217
218     /* First come 0..n flags */
219     do
220     {
221         switch ( *spec )
222         {
223             case '-':
224                 /* left-aligned output */
225                 status->flags |= E_minus;
226                 ++spec;
227                 break;
228             case '+':
229                 /* positive numbers prefixed with '+' */
230                 status->flags |= E_plus;
231                 ++spec;
232                 break;
233             case '#':
234                 /* alternative format (leading 0x for hex, 0 for octal) */
235                 status->flags |= E_alt;
236                 ++spec;
237                 break;
238             case ' ':
239                 /* positive numbers prefixed with ' ' */
240                 status->flags |= E_space;
241                 ++spec;
242                 break;
243             case '0':
244                 /* right-aligned padding done with '0' instead of ' ' */
245                 status->flags |= E_zero;
246                 ++spec;
247                 break;
248             default:
249                 /* not a flag, exit flag parsing */
250                 status->flags |= E_done;
251                 break;
252         }
253     } while ( ! ( status->flags & E_done ) );
254
255     /* Optional field width */
256     if ( *spec == '*' )
257     {
258         /* Retrieve width value from argument stack */
259 #if 1
260         int width = va_arg( status->arg, int );
261         if ( width < 0 )
262         {
263             status->flags |= E_minus;
264             status->width = width * -1; /* FIXME: Should be abs( width ) */
265         }
266         else
267         {
268             status->width = width;
269         }
270 #else
271         /* FIXME: Old version - with unsigned status->width, condition <0 is never true */
272         if ( ( status->width = va_arg( status->arg, int ) ) < 0 )
273         {
274             /* Negative value is '-' flag plus absolute value */
275             status->flags |= E_minus;
276             status->width *= -1;
277         }
278 #endif
279         ++spec;
280     }
281     else
282     {
283         /* If a width is given, strtol() will return its value. If not given,
284            strtol() will return zero. In both cases, endptr will point to the
285            rest of the conversion specifier - just what we need.
286         */
287         status->width = (int)strtol( spec, (char**)&spec, 10 );
288     }
289
290     /* Optional precision */
291     if ( *spec == '.' )
292     {
293         ++spec;
294         if ( *spec == '*' )
295         {
296             /* Retrieve precision value from argument stack. A negative value
297                is as if no precision is given - as precision is initalized to
298                EOF (negative), there is no need for testing for negative here.
299             */
300             status->prec = va_arg( status->arg, int );
301         }
302         else
303         {
304             char * endptr;
305             status->prec = (int)strtol( spec, &endptr, 10 );
306             if ( spec == endptr )
307             {
308                 /* Decimal point but no number - bad conversion specifier. */
309                 return orig_spec;
310             }
311             spec = endptr;
312         }
313         /* Having a precision cancels out any zero flag. */
314         status->flags ^= E_zero;
315     }
316
317     /* Optional length modifier
318        We step one character ahead in any case, and step back only if we find
319        there has been no length modifier (or step ahead another character if it
320        has been "hh" or "ll").
321     */
322     switch ( *(spec++) )
323     {
324         case 'h':
325             if ( *spec == 'h' )
326             {
327                 /* hh -> char */
328                 status->flags |= E_char;
329                 ++spec;
330             }
331             else
332             {
333                 /* h -> short */
334                 status->flags |= E_short;
335             }
336             break;
337         case 'l':
338             if ( *spec == 'l' )
339             {
340                 /* ll -> long long */
341                 status->flags |= E_llong;
342                 ++spec;
343             }
344             else
345             {
346                 /* k -> long */
347                 status->flags |= E_long;
348             }
349             break;
350         case 'j':
351             /* j -> intmax_t, which might or might not be long long */
352             status->flags |= E_intmax;
353             break;
354         case 'z':
355             /* z -> size_t, which might or might not be unsigned int */
356             status->flags |= E_size;
357             break;
358         case 't':
359             /* t -> ptrdiff_t, which might or might not be long */
360             status->flags |= E_ptrdiff;
361             break;
362         case 'L':
363             /* L -> long double */
364             status->flags |= E_ldouble;
365             break;
366         default:
367             --spec;
368             break;
369     }
370
371     /* Conversion specifier */
372     switch ( *spec )
373     {
374         case 'd':
375             /* FALLTHROUGH */
376         case 'i':
377             status->base = 10;
378             break;
379         case 'o':
380             status->base = 8;
381             status->flags |= E_unsigned;
382             break;
383         case 'u':
384             status->base = 10;
385             status->flags |= E_unsigned;
386             break;
387         case 'x':
388             status->base = 16;
389             status->flags |= ( E_lower | E_unsigned );
390             break;
391         case 'X':
392             status->base = 16;
393             status->flags |= E_unsigned;
394             break;
395         case 'f':
396         case 'F':
397         case 'e':
398         case 'E':
399         case 'g':
400         case 'G':
401             break;
402         case 'a':
403         case 'A':
404             break;
405         case 'c':
406             /* TODO: Flags, wide chars. */
407             DELIVER( va_arg( status->arg, int ) );
408             return ++spec;
409         case 's':
410             /* TODO: Flags, wide chars. */
411             {
412                 char * s = va_arg( status->arg, char * );
413                 while ( *s != '\0' )
414                 {
415                     DELIVER( *(s++) );
416                 }
417                 return ++spec;
418             }
419         case 'p':
420             /* TODO: E_long -> E_intptr */
421             status->base = 16;
422             status->flags |= ( E_lower | E_unsigned | E_alt | E_long );
423             break;
424         case 'n':
425            {
426                int * val = va_arg( status->arg, int * );
427                *val = status->i;
428                return ++spec;
429            }
430         default:
431             /* No conversion specifier. Bad conversion. */
432             return orig_spec;
433     }
434
435     /* Do the actual output based on our findings */
436     if ( status->base != 0 )
437     {
438         /* Integer conversions */
439         /* TODO: Check for invalid flag combinations. */
440         if ( status->flags & E_unsigned )
441         {
442             uintmax_t value;
443             switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_size ) )
444             {
445                 case E_char:
446                     value = (uintmax_t)(unsigned char)va_arg( status->arg, int );
447                     break;
448                 case E_short:
449                     value = (uintmax_t)(unsigned short)va_arg( status->arg, int );
450                     break;
451                 case 0:
452                     value = (uintmax_t)va_arg( status->arg, unsigned int );
453                     break;
454                 case E_long:
455                     value = (uintmax_t)va_arg( status->arg, unsigned long );
456                     break;
457                 case E_llong:
458                     value = (uintmax_t)va_arg( status->arg, unsigned long long );
459                     break;
460                 case E_size:
461                     value = (uintmax_t)va_arg( status->arg, size_t );
462                     break;
463             }
464             ++(status->this);
465             if ( ( value / status->base ) != 0 )
466             {
467                 int2base( (intmax_t)(value / status->base), status );
468             }
469             int digit = value % status->base;
470             if ( digit < 0 )
471             {
472                 digit *= -1;
473             }
474             if ( status->flags & E_lower )
475             {
476                 DELIVER( _PDCLIB_digits[ digit ] );
477             }
478             else
479             {
480                 DELIVER( _PDCLIB_Xdigits[ digit ] );
481             }
482         }
483         else
484         {
485             switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_intmax ) )
486             {
487                 case E_char:
488                     int2base( (intmax_t)(char)va_arg( status->arg, int ), status );
489                     break;
490                 case E_short:
491                     int2base( (intmax_t)(short)va_arg( status->arg, int ), status );
492                     break;
493                 case 0:
494                     int2base( (intmax_t)va_arg( status->arg, int ), status );
495                     break;
496                 case E_long:
497                     int2base( (intmax_t)va_arg( status->arg, long ), status );
498                     break;
499                 case E_llong:
500                     int2base( (intmax_t)va_arg( status->arg, long long ), status );
501                     break;
502                 case E_ptrdiff:
503                     int2base( (intmax_t)va_arg( status->arg, ptrdiff_t ), status );
504                     break;
505                 case E_intmax:
506                     int2base( va_arg( status->arg, intmax_t ), status );
507                     break;
508             }
509         }
510         if ( status->flags & E_minus )
511         {
512             while ( status->this < status->width )
513             {
514                 DELIVER( ' ' );
515                 ++(status->this);
516             }
517         }
518         if ( status->i >= status->n )
519         {
520             status->s[status->n - 1] = '\0';
521         }
522     }
523     return ++spec;
524 }
525
526 #ifdef TEST
527 #include <_PDCLIB_test.h>
528
529 #include <limits.h>
530 #include <string.h>
531
532 static int testprintf( char * buffer, size_t n, const char * format, ... )
533 {
534     /* Members: base, flags, n, i, this, s, width, prec, stream, arg         */
535     struct _PDCLIB_status_t status = { 0, 0, n, 0, 0, buffer, 0, 0, NULL, NULL };
536     memset( buffer, '\0', 100 );
537     va_start( status.arg, format );
538     if ( *(_PDCLIB_print( format, &status )) != '\0' )
539     {
540         printf( "_PDCLIB_print() did not return end-of-specifier on '%s'.\n", format );
541         ++rc;
542     }
543     va_end( status.arg );
544     return status.i;
545 }
546
547 int main( void )
548 {
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     return TEST_RESULTS;
789 }
790
791 #endif