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_unlocked( 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;
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;
107 if(status->prec != 0 || remaining != 0) do {
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 < (int) 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 < (int) 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, (char**) &spec, 10 );
321 /* Having a precision cancels out any zero flag. */
322 status->flags &= ~E_zero;
325 /* Optional length modifier
326 We step one character ahead in any case, and step back only if we find
327 there has been no length modifier (or step ahead another character if it
328 has been "hh" or "ll").
336 status->flags |= E_char;
342 status->flags |= E_short;
348 /* ll -> long long */
349 status->flags |= E_llong;
355 status->flags |= E_long;
359 /* j -> intmax_t, which might or might not be long long */
360 status->flags |= E_intmax;
363 /* z -> size_t, which might or might not be unsigned int */
364 status->flags |= E_size;
367 /* t -> ptrdiff_t, which might or might not be long */
368 status->flags |= E_ptrdiff;
371 /* L -> long double */
372 status->flags |= E_ldouble;
379 /* Conversion specifier */
389 status->flags |= E_unsigned;
393 status->flags |= E_unsigned;
397 status->flags |= ( E_lower | E_unsigned );
401 status->flags |= E_unsigned;
414 /* TODO: wide chars. */
415 printchar( va_arg( status->arg, int ), status );
418 /* TODO: wide chars. */
420 char * s = va_arg( status->arg, char * );
421 printstr( s, status );
425 /* TODO: E_long -> E_intptr */
427 status->flags |= ( E_lower | E_unsigned | E_alt | E_long );
431 int * val = va_arg( status->arg, int * );
436 /* No conversion specifier. Bad conversion. */
440 /* Do the actual output based on our findings */
441 if ( status->base != 0 )
443 /* Integer conversions */
444 /* TODO: Check for invalid flag combinations. */
445 if ( status->flags & E_unsigned )
448 switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_size ) )
451 value = (uintmax_t)(unsigned char)va_arg( status->arg, int );
454 value = (uintmax_t)(unsigned short)va_arg( status->arg, int );
457 value = (uintmax_t)va_arg( status->arg, unsigned int );
460 value = (uintmax_t)va_arg( status->arg, unsigned long );
463 value = (uintmax_t)va_arg( status->arg, unsigned long long );
466 value = (uintmax_t)va_arg( status->arg, size_t );
469 int2base( value, status );
473 switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_intmax ) )
476 int2base( (intmax_t)(char)va_arg( status->arg, int ), status );
479 int2base( (intmax_t)(short)va_arg( status->arg, int ), status );
482 int2base( (intmax_t)va_arg( status->arg, int ), status );
485 int2base( (intmax_t)va_arg( status->arg, long ), status );
488 int2base( (intmax_t)va_arg( status->arg, long long ), status );
491 int2base( (intmax_t)va_arg( status->arg, ptrdiff_t ), status );
494 int2base( va_arg( status->arg, intmax_t ), status );
498 if ( status->flags & E_minus )
500 while ( status->current < status->width )
506 if ( status->i >= status->n && status->n > 0 )
508 status->s[status->n - 1] = '\0';
517 #define _PDCLIB_FILEID "_PDCLIB/print.c"
518 #define _PDCLIB_STRINGIO
520 #include <_PDCLIB_test.h>
523 static int testprintf( char * buffer, const char * format, ... )
525 /* Members: base, flags, n, i, current, s, width, prec, stream, arg */
526 struct _PDCLIB_status_t status;
535 status.stream = NULL;
536 va_start( status.arg, format );
537 memset( buffer, '\0', 100 );
538 if ( *(_PDCLIB_print( format, &status )) != '\0' )
540 printf( "_PDCLIB_print() did not return end-of-specifier on '%s'.\n", format );
543 va_end( status.arg );
548 #define TEST_CONVERSION_ONLY
554 #include "printf_testcases.h"