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 );
319 status->prec = (int)strtol( spec, &endptr, 10 );
320 if ( spec == endptr )
322 /* Decimal point but no number - bad conversion specifier. */
327 /* Having a precision cancels out any zero flag. */
328 status->flags &= ~E_zero;
331 /* Optional length modifier
332 We step one character ahead in any case, and step back only if we find
333 there has been no length modifier (or step ahead another character if it
334 has been "hh" or "ll").
342 status->flags |= E_char;
348 status->flags |= E_short;
354 /* ll -> long long */
355 status->flags |= E_llong;
361 status->flags |= E_long;
365 /* j -> intmax_t, which might or might not be long long */
366 status->flags |= E_intmax;
369 /* z -> size_t, which might or might not be unsigned int */
370 status->flags |= E_size;
373 /* t -> ptrdiff_t, which might or might not be long */
374 status->flags |= E_ptrdiff;
377 /* L -> long double */
378 status->flags |= E_ldouble;
385 /* Conversion specifier */
395 status->flags |= E_unsigned;
399 status->flags |= E_unsigned;
403 status->flags |= ( E_lower | E_unsigned );
407 status->flags |= E_unsigned;
420 /* TODO: wide chars. */
421 printchar( va_arg( status->arg, int ), status );
424 /* TODO: wide chars. */
426 char * s = va_arg( status->arg, char * );
427 printstr( s, status );
431 /* TODO: E_long -> E_intptr */
433 status->flags |= ( E_lower | E_unsigned | E_alt | E_long );
437 int * val = va_arg( status->arg, int * );
442 /* No conversion specifier. Bad conversion. */
446 /* Do the actual output based on our findings */
447 if ( status->base != 0 )
449 /* Integer conversions */
450 /* TODO: Check for invalid flag combinations. */
451 if ( status->flags & E_unsigned )
454 switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_size ) )
457 value = (uintmax_t)(unsigned char)va_arg( status->arg, int );
460 value = (uintmax_t)(unsigned short)va_arg( status->arg, int );
463 value = (uintmax_t)va_arg( status->arg, unsigned int );
466 value = (uintmax_t)va_arg( status->arg, unsigned long );
469 value = (uintmax_t)va_arg( status->arg, unsigned long long );
472 value = (uintmax_t)va_arg( status->arg, size_t );
475 int2base( value, status );
479 switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_intmax ) )
482 int2base( (intmax_t)(char)va_arg( status->arg, int ), status );
485 int2base( (intmax_t)(short)va_arg( status->arg, int ), status );
488 int2base( (intmax_t)va_arg( status->arg, int ), status );
491 int2base( (intmax_t)va_arg( status->arg, long ), status );
494 int2base( (intmax_t)va_arg( status->arg, long long ), status );
497 int2base( (intmax_t)va_arg( status->arg, ptrdiff_t ), status );
500 int2base( va_arg( status->arg, intmax_t ), status );
504 if ( status->flags & E_minus )
506 while ( status->current < status->width )
512 if ( status->i >= status->n && status->n > 0 )
514 status->s[status->n - 1] = '\0';
523 #define _PDCLIB_FILEID "_PDCLIB/print.c"
524 #define _PDCLIB_STRINGIO
526 #include <_PDCLIB_test.h>
529 static int testprintf( char * buffer, const char * format, ... )
531 /* Members: base, flags, n, i, current, s, width, prec, stream, arg */
532 struct _PDCLIB_status_t status;
541 status.stream = NULL;
542 va_start( status.arg, format );
543 memset( buffer, '\0', 100 );
544 if ( *(_PDCLIB_print( format, &status )) != '\0' )
546 printf( "_PDCLIB_print() did not return end-of-specifier on '%s'.\n", format );
549 va_end( status.arg );
554 #define TEST_CONVERSION_ONLY
560 #include "printf_testcases.h"