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