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.
19 #include <_PDCLIB_io.h>
21 /* Using an integer's bits as flags for both the conversion flags and length
24 /* FIXME: one too many flags to work on a 16-bit machine, join some (e.g. the
25 width flags) into a combined field.
27 #define E_minus (1<<0)
30 #define E_space (1<<3)
35 #define E_short (1<<7)
37 #define E_llong (1<<9)
38 #define E_intmax (1<<10)
39 #define E_size (1<<11)
40 #define E_ptrdiff (1<<12)
41 #define E_intptr (1<<13)
43 #define E_ldouble (1<<14)
45 #define E_lower (1<<15)
46 #define E_unsigned (1<<16)
48 #define E_TYPES (E_char | E_short | E_long | E_llong | E_intmax \
49 | E_size | E_ptrdiff | E_intptr)
51 /* This macro delivers a given character to either a memory buffer or a stream,
52 depending on the contents of 'status' (struct _PDCLIB_status_t).
53 x - the character to be delivered
54 i - pointer to number of characters already delivered in this call
55 n - pointer to maximum number of characters to be delivered in this call
56 s - the buffer into which the character shall be delivered
61 if ( status->i < status->n ) { \
62 if ( status->stream != NULL ) \
63 _PDCLIB_putc_unlocked( character, status->stream ); \
65 status->s[status->i] = character; \
70 /* Maximum number of output characters =
71 * number of bits in (u)intmax_t / number of bits per character in smallest
72 * base. Smallest base is octal, 3 bits/char.
74 * Additionally require 2 extra characters for prefixes
76 static const size_t maxIntLen = sizeof(intmax_t) * CHAR_BIT / 3 + 1;
78 static void int2base( uintmax_t value, struct _PDCLIB_status_t * status )
81 if ( ! ( status->flags & E_unsigned ) )
83 intmax_t signval = (intmax_t) value;
84 bool negative = signval < 0;
85 value = signval < 0 ? -signval : signval;
91 else if ( status->flags & E_plus )
95 else if (status->flags & E_space )
101 // The user could theoretically ask for a silly buffer length here.
102 // Perhaps after a certain size we should malloc? Or do we refuse to protect
103 // them from their own stupidity?
104 size_t bufLen = (status->width > maxIntLen ? status->width : maxIntLen) + 2;
106 char * outend = outbuf + bufLen;
109 // Build up our output string - backwards
111 const char * digits = (status->flags & E_lower) ?
112 _PDCLIB_digits : _PDCLIB_Xdigits;
113 uintmax_t remaining = value;
114 if(status->prec != 0 || remaining != 0) do {
115 uintmax_t digit = remaining % status->base;
116 remaining /= status->base;
118 outend[-++written] = digits[digit];
119 } while(remaining != 0);
122 // Pad field out to the precision specification
123 while( (long) written < status->prec ) outend[-++written] = '0';
125 // If a field width specified, and zero padding was requested, then pad to
127 unsigned padding = 0;
128 if ( ( ! ( status->flags & E_minus ) ) && ( status->flags & E_zero ) )
130 while( written < (int) status->width )
132 outend[-++written] = '0';
140 if ( padding == 0 ) written++;
141 outend[-written] = sign;
143 else if ( status->flags & E_alt )
145 switch ( status->base )
148 if ( outend[-written] != '0' ) outend[-++written] = '0';
152 if ( value == 0 ) break;
154 written += padding < 2 ? 2 - padding : 0;
155 outend[-written ] = '0';
156 outend[-written + 1] = (status->flags & E_lower) ? 'x' : 'X';
163 // Space padding to field width
164 if ( ! ( status->flags & ( E_minus | E_zero ) ) )
166 while( written < (int) status->width ) outend[-++written] = ' ';
170 status->current = written;
172 PUT( outend[-written--] );
175 static void printstr( const char * str, struct _PDCLIB_status_t * status )
177 if ( status->width == 0 || status->flags & E_minus )
179 // Simple case or left justification
180 while ( str[status->current] &&
181 ( status->prec < 0 || (long)status->current < status->prec ) )
183 PUT( str[status->current++] );
186 while( status->current < status->width )
192 // Right justification
193 size_t len = status->prec >= 0 ? strnlen( str, status->prec )
195 int padding = status->width - len;
196 while((long)status->current < padding)
202 for( size_t i = 0; i != len; i++ )
210 static void printchar( char chr, struct _PDCLIB_status_t * status )
212 if( ! ( status->flags & E_minus ) )
214 // Right justification
215 for( ; status->current + 1 < status->width; status->current++)
222 // Left justification
226 for( ; status->current < status->width; status->current++)
233 const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status )
235 const char * orig_spec = spec;
236 if ( *(++spec) == '%' )
238 /* %% -> print single '%' */
242 /* Initializing status structure */
249 /* First come 0..n flags */
255 /* left-aligned output */
256 status->flags |= E_minus;
260 /* positive numbers prefixed with '+' */
261 status->flags |= E_plus;
265 /* alternative format (leading 0x for hex, 0 for octal) */
266 status->flags |= E_alt;
270 /* positive numbers prefixed with ' ' */
271 status->flags |= E_space;
275 /* right-aligned padding done with '0' instead of ' ' */
276 status->flags |= E_zero;
280 /* not a flag, exit flag parsing */
281 status->flags |= E_done;
284 } while ( ! ( status->flags & E_done ) );
286 /* Optional field width */
289 /* Retrieve width value from argument stack */
290 int width = va_arg( status->arg, int );
293 status->flags |= E_minus;
294 status->width = abs( width );
298 status->width = width;
304 /* If a width is given, strtol() will return its value. If not given,
305 strtol() will return zero. In both cases, endptr will point to the
306 rest of the conversion specifier - just what we need.
308 status->width = (int)strtol( spec, (char**)&spec, 10 );
311 /* Optional precision */
317 /* Retrieve precision value from argument stack. A negative value
318 is as if no precision is given - as precision is initalized to
319 EOF (negative), there is no need for testing for negative here.
321 status->prec = va_arg( status->arg, int );
326 status->prec = (int)strtol( spec, (char**) &spec, 10 );
328 /* Having a precision cancels out any zero flag. */
329 status->flags &= ~E_zero;
332 /* Optional length modifier
333 We step one character ahead in any case, and step back only if we find
334 there has been no length modifier (or step ahead another character if it
335 has been "hh" or "ll").
343 status->flags |= E_char;
349 status->flags |= E_short;
355 /* ll -> long long */
356 status->flags |= E_llong;
362 status->flags |= E_long;
366 /* j -> intmax_t, which might or might not be long long */
367 status->flags |= E_intmax;
370 /* z -> size_t, which might or might not be unsigned int */
371 status->flags |= E_size;
374 /* t -> ptrdiff_t, which might or might not be long */
375 status->flags |= E_ptrdiff;
378 /* L -> long double */
379 status->flags |= E_ldouble;
386 /* Conversion specifier */
396 status->flags |= E_unsigned;
400 status->flags |= E_unsigned;
404 status->flags |= ( E_lower | E_unsigned );
408 status->flags |= E_unsigned;
421 /* TODO: wide chars. */
422 printchar( va_arg( status->arg, int ), status );
425 /* TODO: wide chars. */
427 char * s = va_arg( status->arg, char * );
428 printstr( s, status );
433 status->flags |= ( E_lower | E_unsigned | E_alt | E_intptr );
437 int * val = va_arg( status->arg, int * );
442 /* No conversion specifier. Bad conversion. */
445 /* Do the actual output based on our findings */
446 if ( status->base != 0 )
448 /* Integer conversions */
449 /* TODO: Check for invalid flag combinations. */
450 if ( status->flags & E_unsigned )
453 switch ( status->flags & E_TYPES )
456 value = (uintmax_t)(unsigned char)va_arg( status->arg, int );
459 value = (uintmax_t)(unsigned short)va_arg( status->arg, int );
462 value = (uintmax_t)va_arg( status->arg, unsigned int );
465 value = (uintmax_t)va_arg( status->arg, unsigned long );
468 value = (uintmax_t)va_arg( status->arg, unsigned long long );
471 value = (uintmax_t)va_arg( status->arg, size_t );
474 value = (uintmax_t)va_arg( status->arg, uintptr_t );
477 value = (uintmax_t)va_arg( status->arg, ptrdiff_t );
480 value = va_arg( status->arg, uintmax_t );
482 int2base( value, status );
486 switch ( status->flags & E_TYPES )
489 int2base( (intmax_t)(char)va_arg( status->arg, int ), status );
492 int2base( (intmax_t)(short)va_arg( status->arg, int ), status );
495 int2base( (intmax_t)va_arg( status->arg, int ), status );
498 int2base( (intmax_t)va_arg( status->arg, long ), status );
501 int2base( (intmax_t)va_arg( status->arg, long long ), status );
504 int2base( (intmax_t)va_arg( status->arg, size_t ), status );
507 int2base( (intmax_t)va_arg( status->arg, intptr_t ), status );
510 int2base( (intmax_t)va_arg( status->arg, ptrdiff_t ), status );
513 int2base( va_arg( status->arg, intmax_t ), status );
517 if ( status->flags & E_minus )
519 while ( status->current < status->width )
525 if ( status->i >= status->n && status->n > 0 )
527 status->s[status->n - 1] = '\0';
536 #define _PDCLIB_FILEID "_PDCLIB/print.c"
537 #define _PDCLIB_STRINGIO
539 #include <_PDCLIB_test.h>
542 static int testprintf( char * buffer, const char * format, ... )
544 /* Members: base, flags, n, i, current, s, width, prec, stream, arg */
545 struct _PDCLIB_status_t status;
554 status.stream = NULL;
555 va_start( status.arg, format );
556 memset( buffer, '\0', 100 );
557 if ( *(_PDCLIB_print( format, &status )) != '\0' )
559 printf( "_PDCLIB_print() did not return end-of-specifier on '%s'.\n", format );
562 va_end( status.arg );
567 #define TEST_CONVERSION_ONLY
573 #include "printf_testcases.h"