1 /* _PDCLIB_print( const char *, struct _PDCLIB_status_t * )
3 This file is part of the Public Domain C Library (PDCLib).
4 Permission is granted to use, modify, and / or redistribute at will.
17 #include <_PDCLIB_io.h>
19 /* Using an integer's bits as flags for both the conversion flags and length
22 /* FIXME: one too many flags to work on a 16-bit machine, join some (e.g. the
23 width flags) into a combined field.
25 #define E_minus (1<<0)
28 #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)
41 #define E_ldouble (1<<14)
43 #define E_lower (1<<15)
44 #define E_unsigned (1<<16)
46 #define E_TYPES (E_char | E_short | E_long | E_llong | E_intmax \
47 | E_size | E_ptrdiff | E_intptr)
49 /* returns true if callback-based output succeeded; else false */
50 static inline bool cbout(
51 struct _PDCLIB_status_t * status,
55 size_t rv = status->write( status->ctx, buf, size );
57 status->current += rv;
61 /* repeated output of a single character */
62 static inline bool cbrept(
63 struct _PDCLIB_status_t * status,
67 if ( sizeof(size_t) == 8 && CHAR_BIT == 8)
69 uint64_t spread = UINT64_C(0x0101010101010101) * c;
72 size_t n = times > 8 ? 8 : times;
73 if ( !cbout( status, &spread, n ) )
79 else if ( sizeof(size_t) == 4 && CHAR_BIT == 8)
81 uint32_t spread = UINT32_C(0x01010101) * c;
84 size_t n = times > 4 ? 4 : times;
85 if ( !cbout( status, &spread, n ) )
95 if ( !cbout( status, &c, 1) )
104 /* Maximum number of output characters =
105 * number of bits in (u)intmax_t / number of bits per character in smallest
106 * base. Smallest base is octal, 3 bits/char.
108 * Additionally require 2 extra characters for prefixes
110 * Returns false if an I/O error occured.
112 static const size_t maxIntLen = sizeof(intmax_t) * CHAR_BIT / 3 + 1;
114 static bool int2base( uintmax_t value, struct _PDCLIB_status_t * status )
117 if ( ! ( status->flags & E_unsigned ) )
119 intmax_t signval = (intmax_t) value;
120 bool negative = signval < 0;
121 value = signval < 0 ? -signval : signval;
127 else if ( status->flags & E_plus )
131 else if (status->flags & E_space )
137 // The user could theoretically ask for a silly buffer length here.
138 // Perhaps after a certain size we should malloc? Or do we refuse to protect
139 // them from their own stupidity?
140 size_t bufLen = (status->width > maxIntLen ? status->width : maxIntLen) + 2;
142 char * outend = outbuf + bufLen;
145 // Build up our output string - backwards
147 const char * digits = (status->flags & E_lower) ?
148 _PDCLIB_digits : _PDCLIB_Xdigits;
149 uintmax_t remaining = value;
150 if(status->prec != 0 || remaining != 0) do {
151 uintmax_t digit = remaining % status->base;
152 remaining /= status->base;
154 outend[-++written] = digits[digit];
155 } while(remaining != 0);
158 // Pad field out to the precision specification
159 while( (long) written < status->prec ) outend[-++written] = '0';
161 // If a field width specified, and zero padding was requested, then pad to
163 unsigned padding = 0;
164 if ( ( ! ( status->flags & E_minus ) ) && ( status->flags & E_zero ) )
166 while( written < (int) status->width )
168 outend[-++written] = '0';
176 if ( padding == 0 ) written++;
177 outend[-written] = sign;
179 else if ( status->flags & E_alt )
181 switch ( status->base )
184 if ( outend[-written] != '0' ) outend[-++written] = '0';
188 if ( value == 0 ) break;
190 written += padding < 2 ? 2 - padding : 0;
191 outend[-written ] = '0';
192 outend[-written + 1] = (status->flags & E_lower) ? 'x' : 'X';
199 // Space padding to field width
200 if ( ! ( status->flags & ( E_minus | E_zero ) ) )
202 while( written < (int) status->width ) outend[-++written] = ' ';
206 return cbout( status, outend - written, written );
209 /* print a string. returns false if an I/O error occured */
210 static bool printstr( const char * str, struct _PDCLIB_status_t * status )
212 size_t len = status->prec >= 0 ? strnlen( str, status-> prec)
215 if ( status->width == 0 || status->flags & E_minus )
217 // Simple case or left justification
218 if ( status->prec > 0 )
220 len = (unsigned) status->prec < len ? (unsigned) status->prec : len;
223 if ( !cbout( status, str, len ) )
227 if ( status->width > status->current ) {
228 len = status->width - status->current;
230 if ( !cbrept( status, ' ', len ) )
234 // Right justification
236 if ( status->width > len ) {
237 size_t padding = status->width - len;
239 if ( !cbrept( status, ' ', padding ))
243 if ( !cbout( status, str, len ) )
250 static bool printchar( char chr, struct _PDCLIB_status_t * status )
252 if( ! ( status->flags & E_minus ) )
254 // Right justification
255 if ( status-> width ) {
256 size_t justification = status->width - status->current - 1;
257 if ( !cbrept( status, ' ', justification ))
261 if ( !cbout( status, &chr, 1 ))
264 // Left justification
266 if ( !cbout( status, &chr, 1 ))
269 if ( status->width > status->current ) {
270 if ( !cbrept( status, ' ', status->width - status->current ) )
278 int _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status )
280 const char * orig_spec = spec;
281 if ( *(++spec) == '%' )
283 /* %% -> print single '%' */
284 if ( !cbout(status, spec, 1) )
287 return (spec - orig_spec);
289 /* Initializing status structure */
296 /* First come 0..n flags */
302 /* left-aligned output */
303 status->flags |= E_minus;
307 /* positive numbers prefixed with '+' */
308 status->flags |= E_plus;
312 /* alternative format (leading 0x for hex, 0 for octal) */
313 status->flags |= E_alt;
317 /* positive numbers prefixed with ' ' */
318 status->flags |= E_space;
322 /* right-aligned padding done with '0' instead of ' ' */
323 status->flags |= E_zero;
327 /* not a flag, exit flag parsing */
328 status->flags |= E_done;
331 } while ( ! ( status->flags & E_done ) );
333 /* Optional field width */
336 /* Retrieve width value from argument stack */
337 int width = va_arg( status->arg, int );
340 status->flags |= E_minus;
341 status->width = abs( width );
345 status->width = width;
351 /* If a width is given, strtol() will return its value. If not given,
352 strtol() will return zero. In both cases, endptr will point to the
353 rest of the conversion specifier - just what we need.
355 status->width = (int)strtol( spec, (char**)&spec, 10 );
358 /* Optional precision */
364 /* Retrieve precision value from argument stack. A negative value
365 is as if no precision is given - as precision is initalized to
366 EOF (negative), there is no need for testing for negative here.
368 status->prec = va_arg( status->arg, int );
373 status->prec = (int)strtol( spec, (char**) &spec, 10 );
375 /* Having a precision cancels out any zero flag. */
376 status->flags &= ~E_zero;
379 /* Optional length modifier
380 We step one character ahead in any case, and step back only if we find
381 there has been no length modifier (or step ahead another character if it
382 has been "hh" or "ll").
390 status->flags |= E_char;
396 status->flags |= E_short;
402 /* ll -> long long */
403 status->flags |= E_llong;
409 status->flags |= E_long;
413 /* j -> intmax_t, which might or might not be long long */
414 status->flags |= E_intmax;
417 /* z -> size_t, which might or might not be unsigned int */
418 status->flags |= E_size;
421 /* t -> ptrdiff_t, which might or might not be long */
422 status->flags |= E_ptrdiff;
425 /* L -> long double */
426 status->flags |= E_ldouble;
433 /* Conversion specifier */
443 status->flags |= E_unsigned;
447 status->flags |= E_unsigned;
451 status->flags |= ( E_lower | E_unsigned );
455 status->flags |= E_unsigned;
468 /* TODO: wide chars. */
469 if ( !printchar( va_arg( status->arg, int ), status ) )
472 return (spec - orig_spec);
474 /* TODO: wide chars. */
476 char * s = va_arg( status->arg, char * );
477 if ( !printstr( s, status ) )
480 return (spec - orig_spec);
484 status->flags |= ( E_lower | E_unsigned | E_alt | E_intptr );
488 int * val = va_arg( status->arg, int * );
491 return (spec - orig_spec);
494 /* No conversion specifier. Bad conversion. */
497 /* Do the actual output based on our findings */
498 if ( status->base != 0 )
500 /* Integer conversions */
501 /* TODO: Check for invalid flag combinations. */
502 if ( status->flags & E_unsigned )
505 switch ( status->flags & E_TYPES )
508 value = (uintmax_t)(unsigned char)va_arg( status->arg, int );
511 value = (uintmax_t)(unsigned short)va_arg( status->arg, int );
514 value = (uintmax_t)va_arg( status->arg, unsigned int );
517 value = (uintmax_t)va_arg( status->arg, unsigned long );
520 value = (uintmax_t)va_arg( status->arg, unsigned long long );
523 value = (uintmax_t)va_arg( status->arg, size_t );
526 value = (uintmax_t)va_arg( status->arg, uintptr_t );
529 value = (uintmax_t)va_arg( status->arg, ptrdiff_t );
532 value = va_arg( status->arg, uintmax_t );
534 if ( !int2base( value, status ) )
540 switch ( status->flags & E_TYPES )
543 value = (intmax_t)(char)va_arg( status->arg, int );
546 value = (intmax_t)(short)va_arg( status->arg, int );
549 value = (intmax_t)va_arg( status->arg, int );
552 value = (intmax_t)va_arg( status->arg, long );
555 value = (intmax_t)va_arg( status->arg, long long );
558 value = (intmax_t)va_arg( status->arg, size_t );
561 value = (intmax_t)va_arg( status->arg, intptr_t );
564 value = (intmax_t)va_arg( status->arg, ptrdiff_t );
567 value = va_arg( status->arg, intmax_t );
571 if (!int2base( value, status ) )
575 if ( status->flags & E_minus && status->current < status->width )
577 if (!cbrept( status, ' ', status->width - status->current ))
582 return spec - orig_spec;
588 #define _PDCLIB_FILEID "_PDCLIB/print.c"
589 #define _PDCLIB_STRINGIO
591 #include <_PDCLIB_test.h>
594 static size_t testcb( void *p, const char *buf, size_t size )
597 memcpy(*destbuf, buf, size);
602 static int testprintf( char * buffer, const char * format, ... )
604 /* Members: base, flags, n, i, current, width, prec, ctx, cb, arg */
605 struct _PDCLIB_status_t status;
613 status.ctx = &buffer;
614 status.write = testcb;
615 va_start( status.arg, format );
616 memset( buffer, '\0', 100 );
617 if ( _PDCLIB_print( format, &status ) != strlen( format ) )
619 printf( "_PDCLIB_print() did not return end-of-specifier on '%s'.\n", format );
622 va_end( status.arg );
627 #define TEST_CONVERSION_ONLY
633 #include "printf_testcases.h"