3 /* _PDCLIB_print( const char *, struct _PDCLIB_status_t * )
5 This file is part of the Public Domain C Library (PDCLib).
6 Permission is granted to use, modify, and / or redistribute at will.
20 /* Using an integer's bits as flags for both the conversion flags and length
23 /* FIXME: one too many flags to work on a 16-bit machine, join some (e.g. the
24 width flags) into a combined field.
26 #define E_minus (1<<0)
29 #define E_space (1<<3)
33 #define E_short (1<<7)
35 #define E_llong (1<<9)
36 #define E_intmax (1<<10)
37 #define E_size (1<<11)
38 #define E_ptrdiff (1<<12)
39 #define E_intptr (1<<13)
40 #define E_ldouble (1<<14)
41 #define E_lower (1<<15)
42 #define E_unsigned (1<<16)
44 /* This macro delivers a given character to either a memory buffer or a stream,
45 depending on the contents of 'status' (struct _PDCLIB_status_t).
46 x - the character to be delivered
47 i - pointer to number of characters already delivered in this call
48 n - pointer to maximum number of characters to be delivered in this call
49 s - the buffer into which the character shall be delivered
54 if ( status->i < status->n ) { \
55 if ( status->stream != NULL ) \
56 putc( character, status->stream ); \
58 status->s[status->i] = character; \
63 /* Maximum number of output characters =
64 * number of bits in (u)intmax_t / number of bits per character in smallest
65 * base. Smallest base is octal, 3 bits/char.
67 * Additionally require 2 extra characters for prefixes
69 static const size_t maxIntLen = sizeof(intmax_t) * CHAR_BIT / 3 + 1;
71 static void int2base( uintmax_t value, struct _PDCLIB_status_t * status )
74 if ( ! ( status->flags & E_unsigned ) )
76 intmax_t signval = (intmax_t) value;
77 bool negative = signval < 0;
78 value = signval < 0 ? -signval : signval;
84 else if ( status->flags & E_plus )
88 else if (status->flags & E_space )
94 // The user could theoretically ask for a silly buffer length here.
95 // Perhaps after a certain size we should malloc? Or do we refuse to protect
96 // them from their own stupidity?
97 size_t bufLen = (status->width > maxIntLen ? status->width : maxIntLen) + 2;
99 char * outend = outbuf + bufLen;
100 unsigned written = 0;
102 // Build up our output string - backwards
104 const char * digits = (status->flags & E_lower) ?
105 _PDCLIB_digits : _PDCLIB_Xdigits;
106 uintmax_t remaining = value;
108 uintmax_t digit = remaining % status->base;
109 remaining /= status->base;
111 outend[-++written] = digits[digit];
112 } while(remaining != 0);
115 // Pad field out to the precision specification
116 while( (long) written < status->prec ) outend[-++written] = '0';
118 // If a field width specified, and zero padding was requested, then pad to
120 unsigned padding = 0;
121 if ( ( ! ( status->flags & E_minus ) ) && ( status->flags & E_zero ) )
123 while( written < status->width )
125 outend[-++written] = '0';
133 if ( padding == 0 ) written++;
134 outend[-written] = sign;
136 else if ( status->flags & E_alt )
138 switch ( status->base )
141 if ( outend[-written] != '0' ) outend[-++written] = '0';
145 if ( value == 0 ) break;
147 written += padding < 2 ? 2 - padding : 0;
148 outend[-written ] = '0';
149 outend[-written + 1] = (status->flags & E_lower) ? 'x' : 'X';
156 // Space padding to field width
157 if ( ! ( status->flags & ( E_minus | E_zero ) ) )
159 while( written < status->width ) outend[-++written] = ' ';
163 status->current = written;
165 PUT( outend[-written--] );
168 static void printstr( const char * str, struct _PDCLIB_status_t * status )
170 if ( status->width == 0 || status->flags & E_minus )
172 // Simple case or left justification
173 while ( str[status->current] &&
174 ( status->prec < 0 || (long)status->current < status->prec ) )
176 PUT( str[status->current++] );
179 while( status->current < status->width )
185 // Right justification
186 size_t len = status->prec >= 0 ? strnlen( str, status->prec )
188 int padding = status->width - len;
189 while((long)status->current < padding)
195 for( size_t i = 0; i != len; i++ )
203 static void printchar( char chr, struct _PDCLIB_status_t * status )
205 if( ! ( status->flags & E_minus ) )
207 // Right justification
208 for( ; status->current + 1 < status->width; status->current++)
215 // Left justification
219 for( ; status->current < status->width; status->current++)
226 const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status )
228 const char * orig_spec = spec;
229 if ( *(++spec) == '%' )
231 /* %% -> print single '%' */
235 /* Initializing status structure */
242 /* First come 0..n flags */
248 /* left-aligned output */
249 status->flags |= E_minus;
253 /* positive numbers prefixed with '+' */
254 status->flags |= E_plus;
258 /* alternative format (leading 0x for hex, 0 for octal) */
259 status->flags |= E_alt;
263 /* positive numbers prefixed with ' ' */
264 status->flags |= E_space;
268 /* right-aligned padding done with '0' instead of ' ' */
269 status->flags |= E_zero;
273 /* not a flag, exit flag parsing */
274 status->flags |= E_done;
277 } while ( ! ( status->flags & E_done ) );
279 /* Optional field width */
282 /* Retrieve width value from argument stack */
283 int width = va_arg( status->arg, int );
286 status->flags |= E_minus;
287 status->width = abs( width );
291 status->width = width;
297 /* If a width is given, strtol() will return its value. If not given,
298 strtol() will return zero. In both cases, endptr will point to the
299 rest of the conversion specifier - just what we need.
301 status->width = (int)strtol( spec, (char**)&spec, 10 );
304 /* Optional precision */
310 /* Retrieve precision value from argument stack. A negative value
311 is as if no precision is given - as precision is initalized to
312 EOF (negative), there is no need for testing for negative here.
314 status->prec = va_arg( status->arg, int );
318 status->prec = (int)strtol( spec, (char**) &spec, 10 );
320 /* Having a precision cancels out any zero flag. */
321 status->flags &= ~E_zero;
324 /* Optional length modifier
325 We step one character ahead in any case, and step back only if we find
326 there has been no length modifier (or step ahead another character if it
327 has been "hh" or "ll").
335 status->flags |= E_char;
341 status->flags |= E_short;
347 /* ll -> long long */
348 status->flags |= E_llong;
354 status->flags |= E_long;
358 /* j -> intmax_t, which might or might not be long long */
359 status->flags |= E_intmax;
362 /* z -> size_t, which might or might not be unsigned int */
363 status->flags |= E_size;
366 /* t -> ptrdiff_t, which might or might not be long */
367 status->flags |= E_ptrdiff;
370 /* L -> long double */
371 status->flags |= E_ldouble;
378 /* Conversion specifier */
388 status->flags |= E_unsigned;
392 status->flags |= E_unsigned;
396 status->flags |= ( E_lower | E_unsigned );
400 status->flags |= E_unsigned;
413 /* TODO: wide chars. */
414 printchar( va_arg( status->arg, int ), status );
417 /* TODO: wide chars. */
419 char * s = va_arg( status->arg, char * );
420 printstr( s, status );
424 /* TODO: E_long -> E_intptr */
426 status->flags |= ( E_lower | E_unsigned | E_alt | E_long );
430 int * val = va_arg( status->arg, int * );
435 /* No conversion specifier. Bad conversion. */
439 /* Do the actual output based on our findings */
440 if ( status->base != 0 )
442 /* Integer conversions */
443 /* TODO: Check for invalid flag combinations. */
444 if ( status->flags & E_unsigned )
447 switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_size ) )
450 value = (uintmax_t)(unsigned char)va_arg( status->arg, int );
453 value = (uintmax_t)(unsigned short)va_arg( status->arg, int );
456 value = (uintmax_t)va_arg( status->arg, unsigned int );
459 value = (uintmax_t)va_arg( status->arg, unsigned long );
462 value = (uintmax_t)va_arg( status->arg, unsigned long long );
465 value = (uintmax_t)va_arg( status->arg, size_t );
468 int2base( value, status );
472 switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_intmax ) )
475 int2base( (intmax_t)(char)va_arg( status->arg, int ), status );
478 int2base( (intmax_t)(short)va_arg( status->arg, int ), status );
481 int2base( (intmax_t)va_arg( status->arg, int ), status );
484 int2base( (intmax_t)va_arg( status->arg, long ), status );
487 int2base( (intmax_t)va_arg( status->arg, long long ), status );
490 int2base( (intmax_t)va_arg( status->arg, ptrdiff_t ), status );
493 int2base( va_arg( status->arg, intmax_t ), status );
497 if ( status->flags & E_minus )
499 while ( status->current < status->width )
505 if ( status->i >= status->n && status->n > 0 )
507 status->s[status->n - 1] = '\0';
516 #define _PDCLIB_FILEID "_PDCLIB/print.c"
517 #define _PDCLIB_STRINGIO
519 #include <_PDCLIB_test.h>
522 static int testprintf( char * buffer, const char * format, ... )
524 /* Members: base, flags, n, i, current, s, width, prec, stream, arg */
525 struct _PDCLIB_status_t status;
534 status.stream = NULL;
535 va_start( status.arg, format );
536 memset( buffer, '\0', 100 );
537 if ( *(_PDCLIB_print( format, &status )) != '\0' )
539 printf( "_PDCLIB_print() did not return end-of-specifier on '%s'.\n", format );
542 va_end( status.arg );
547 #define TEST_CONVERSION_ONLY
553 #include "printf_testcases.h"