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 /* returns true if callback-based output succeeded; else false */
52 static inline bool cbout(
53 struct _PDCLIB_status_t * status,
57 size_t rv = status->write( status->ctx, buf, size );
59 status->current += rv;
63 /* repeated output of a single character */
64 static inline bool cbrept(
65 struct _PDCLIB_status_t * status,
69 if ( sizeof(size_t) == 8 && CHAR_BIT == 8)
71 uint64_t spread = UINT64_C(0x0101010101010101) * c;
74 size_t n = times > 8 ? 8 : times;
75 if ( !cbout( status, &spread, n ) )
81 else if ( sizeof(size_t) == 4 && CHAR_BIT == 8)
83 uint32_t spread = UINT32_C(0x01010101) * c;
86 size_t n = times > 4 ? 4 : times;
87 if ( !cbout( status, &spread, n ) )
97 if ( !cbout( status, &c, 1) )
106 /* Maximum number of output characters =
107 * number of bits in (u)intmax_t / number of bits per character in smallest
108 * base. Smallest base is octal, 3 bits/char.
110 * Additionally require 2 extra characters for prefixes
112 * Returns false if an I/O error occured.
114 static const size_t maxIntLen = sizeof(intmax_t) * CHAR_BIT / 3 + 1;
116 static bool int2base( uintmax_t value, struct _PDCLIB_status_t * status )
119 if ( ! ( status->flags & E_unsigned ) )
121 intmax_t signval = (intmax_t) value;
122 bool negative = signval < 0;
123 value = signval < 0 ? -signval : signval;
129 else if ( status->flags & E_plus )
133 else if (status->flags & E_space )
139 // The user could theoretically ask for a silly buffer length here.
140 // Perhaps after a certain size we should malloc? Or do we refuse to protect
141 // them from their own stupidity?
142 size_t bufLen = (status->width > maxIntLen ? status->width : maxIntLen) + 2;
144 char * outend = outbuf + bufLen;
147 // Build up our output string - backwards
149 const char * digits = (status->flags & E_lower) ?
150 _PDCLIB_digits : _PDCLIB_Xdigits;
151 uintmax_t remaining = value;
152 if(status->prec != 0 || remaining != 0) do {
153 uintmax_t digit = remaining % status->base;
154 remaining /= status->base;
156 outend[-++written] = digits[digit];
157 } while(remaining != 0);
160 // Pad field out to the precision specification
161 while( (long) written < status->prec ) outend[-++written] = '0';
163 // If a field width specified, and zero padding was requested, then pad to
165 unsigned padding = 0;
166 if ( ( ! ( status->flags & E_minus ) ) && ( status->flags & E_zero ) )
168 while( written < (int) status->width )
170 outend[-++written] = '0';
178 if ( padding == 0 ) written++;
179 outend[-written] = sign;
181 else if ( status->flags & E_alt )
183 switch ( status->base )
186 if ( outend[-written] != '0' ) outend[-++written] = '0';
190 if ( value == 0 ) break;
192 written += padding < 2 ? 2 - padding : 0;
193 outend[-written ] = '0';
194 outend[-written + 1] = (status->flags & E_lower) ? 'x' : 'X';
201 // Space padding to field width
202 if ( ! ( status->flags & ( E_minus | E_zero ) ) )
204 while( written < (int) status->width ) outend[-++written] = ' ';
208 return cbout( status, outend - written, written );
211 /* print a string. returns false if an I/O error occured */
212 static bool printstr( const char * str, struct _PDCLIB_status_t * status )
214 size_t len = status->prec >= 0 ? strnlen( str, status-> prec)
217 if ( status->width == 0 || status->flags & E_minus )
219 // Simple case or left justification
220 if ( status->prec > 0 )
222 len = (unsigned) status->prec < len ? (unsigned) status->prec : len;
225 if ( !cbout( status, str, len ) )
229 if ( status->width > status->current ) {
230 len = status->width - status->current;
232 if ( !cbrept( status, ' ', len ) )
236 // Right justification
238 if ( status->width > len ) {
239 size_t padding = status->width - len;
241 if ( !cbrept( status, ' ', padding ))
245 if ( !cbout( status, str, len ) )
252 static bool printchar( char chr, struct _PDCLIB_status_t * status )
254 if( ! ( status->flags & E_minus ) )
256 // Right justification
257 if ( status-> width ) {
258 size_t justification = status->width - status->current - 1;
259 if ( !cbrept( status, ' ', justification ))
263 if ( !cbout( status, &chr, 1 ))
266 // Left justification
268 if ( !cbout( status, &chr, 1 ))
271 if ( status->width > status->current ) {
272 if ( !cbrept( status, ' ', status->width - status->current ) )
280 int _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status )
282 const char * orig_spec = spec;
283 if ( *(++spec) == '%' )
285 /* %% -> print single '%' */
286 if ( !cbout(status, spec, 1) )
289 return (spec - orig_spec);
291 /* Initializing status structure */
298 /* First come 0..n flags */
304 /* left-aligned output */
305 status->flags |= E_minus;
309 /* positive numbers prefixed with '+' */
310 status->flags |= E_plus;
314 /* alternative format (leading 0x for hex, 0 for octal) */
315 status->flags |= E_alt;
319 /* positive numbers prefixed with ' ' */
320 status->flags |= E_space;
324 /* right-aligned padding done with '0' instead of ' ' */
325 status->flags |= E_zero;
329 /* not a flag, exit flag parsing */
330 status->flags |= E_done;
333 } while ( ! ( status->flags & E_done ) );
335 /* Optional field width */
338 /* Retrieve width value from argument stack */
339 int width = va_arg( status->arg, int );
342 status->flags |= E_minus;
343 status->width = abs( width );
347 status->width = width;
353 /* If a width is given, strtol() will return its value. If not given,
354 strtol() will return zero. In both cases, endptr will point to the
355 rest of the conversion specifier - just what we need.
357 status->width = (int)strtol( spec, (char**)&spec, 10 );
360 /* Optional precision */
366 /* Retrieve precision value from argument stack. A negative value
367 is as if no precision is given - as precision is initalized to
368 EOF (negative), there is no need for testing for negative here.
370 status->prec = va_arg( status->arg, int );
375 status->prec = (int)strtol( spec, (char**) &spec, 10 );
377 /* Having a precision cancels out any zero flag. */
378 status->flags &= ~E_zero;
381 /* Optional length modifier
382 We step one character ahead in any case, and step back only if we find
383 there has been no length modifier (or step ahead another character if it
384 has been "hh" or "ll").
392 status->flags |= E_char;
398 status->flags |= E_short;
404 /* ll -> long long */
405 status->flags |= E_llong;
411 status->flags |= E_long;
415 /* j -> intmax_t, which might or might not be long long */
416 status->flags |= E_intmax;
419 /* z -> size_t, which might or might not be unsigned int */
420 status->flags |= E_size;
423 /* t -> ptrdiff_t, which might or might not be long */
424 status->flags |= E_ptrdiff;
427 /* L -> long double */
428 status->flags |= E_ldouble;
435 /* Conversion specifier */
445 status->flags |= E_unsigned;
449 status->flags |= E_unsigned;
453 status->flags |= ( E_lower | E_unsigned );
457 status->flags |= E_unsigned;
470 /* TODO: wide chars. */
471 if ( !printchar( va_arg( status->arg, int ), status ) )
474 return (spec - orig_spec);
476 /* TODO: wide chars. */
478 char * s = va_arg( status->arg, char * );
479 if ( !printstr( s, status ) )
482 return (spec - orig_spec);
486 status->flags |= ( E_lower | E_unsigned | E_alt | E_intptr );
490 int * val = va_arg( status->arg, int * );
493 return (spec - orig_spec);
496 /* No conversion specifier. Bad conversion. */
499 /* Do the actual output based on our findings */
500 if ( status->base != 0 )
502 /* Integer conversions */
503 /* TODO: Check for invalid flag combinations. */
504 if ( status->flags & E_unsigned )
507 switch ( status->flags & E_TYPES )
510 value = (uintmax_t)(unsigned char)va_arg( status->arg, int );
513 value = (uintmax_t)(unsigned short)va_arg( status->arg, int );
516 value = (uintmax_t)va_arg( status->arg, unsigned int );
519 value = (uintmax_t)va_arg( status->arg, unsigned long );
522 value = (uintmax_t)va_arg( status->arg, unsigned long long );
525 value = (uintmax_t)va_arg( status->arg, size_t );
528 value = (uintmax_t)va_arg( status->arg, uintptr_t );
531 value = (uintmax_t)va_arg( status->arg, ptrdiff_t );
534 value = va_arg( status->arg, uintmax_t );
536 if ( !int2base( value, status ) )
542 switch ( status->flags & E_TYPES )
545 value = (intmax_t)(char)va_arg( status->arg, int );
548 value = (intmax_t)(short)va_arg( status->arg, int );
551 value = (intmax_t)va_arg( status->arg, int );
554 value = (intmax_t)va_arg( status->arg, long );
557 value = (intmax_t)va_arg( status->arg, long long );
560 value = (intmax_t)va_arg( status->arg, size_t );
563 value = (intmax_t)va_arg( status->arg, intptr_t );
566 value = (intmax_t)va_arg( status->arg, ptrdiff_t );
569 value = va_arg( status->arg, intmax_t );
573 if (!int2base( value, status ) )
577 if ( status->flags & E_minus && status->current < status->width )
579 if (!cbrept( status, ' ', status->width - status->current ))
584 return spec - orig_spec;
590 #define _PDCLIB_FILEID "_PDCLIB/print.c"
591 #define _PDCLIB_STRINGIO
593 #include <_PDCLIB_test.h>
596 static size_t testcb( void *p, const char *buf, size_t size )
599 memcpy(*destbuf, buf, size);
604 static int testprintf( char * buffer, const char * format, ... )
606 /* Members: base, flags, n, i, current, width, prec, ctx, cb, arg */
607 struct _PDCLIB_status_t status;
615 status.ctx = &buffer;
616 status.write = testcb;
617 va_start( status.arg, format );
618 memset( buffer, '\0', 100 );
619 if ( _PDCLIB_print( format, &status ) != strlen( format ) )
621 printf( "_PDCLIB_print() did not return end-of-specifier on '%s'.\n", format );
624 va_end( status.arg );
629 #define TEST_CONVERSION_ONLY
635 #include "printf_testcases.h"