static bool printchar( char chr, struct _PDCLIB_status_t * status ) { if( ! ( status->flags & E_minus ) ) { // Right justification if ( status-> width ) { size_t justification = status->width - status->current - 1; if ( !cbrept( status, ' ', justification )) return false; } if ( !cbout( status, &chr, 1 )) return false; } else { // Left justification if ( !cbout( status, &chr, 1 )) return false; if ( status->width > status->current ) { if ( !cbrept( status, ' ', status->width - status->current ) ) return false; } } return true; }
int _vcbprintf( void *p, size_t ( *cb ) ( void *p, const char *buf, size_t size ), const char *format, va_list arg ) { struct _PDCLIB_status_t status; status.base = 0; status.flags = 0; status.n = 0; status.i = 0; status.current = 0; status.width = 0; status.prec = 0; status.ctx = p; status.write = cb; va_copy( status.arg, arg ); /* Alternate between outputing runs of verbatim text and conversions */ while ( *format != '\0' ) { const char *mark = format; while ( *format != '\0' && *format != '%') { format++; } if ( mark != format ) { if ( !cbout(&status, mark, format - mark) ) return -1; } if ( *format == '%' ) { int consumed = _PDCLIB_print( format, &status ); if ( consumed > 0 ) { format += consumed; } else if ( consumed == 0 ) { /* not a conversion specifier, print verbatim */ if ( !cbout(&status, format++, 1) ) return -1; } else { /* I/O callback error */ return -1; } } } va_end( status.arg ); return status.i; }
/* repeated output of a single character */ static inline bool cbrept( struct _PDCLIB_status_t * status, char c, size_t times ) { if ( sizeof(size_t) == 8 && CHAR_BIT == 8) { uint64_t spread = UINT64_C(0x0101010101010101) * c; while ( times ) { size_t n = times > 8 ? 8 : times; if ( !cbout( status, &spread, n ) ) return false; times -= n; } return true; } else if ( sizeof(size_t) == 4 && CHAR_BIT == 8) { uint32_t spread = UINT32_C(0x01010101) * c; while ( times ) { size_t n = times > 4 ? 4 : times; if ( !cbout( status, &spread, n ) ) return false; times -= n; } return true; } else { while ( times ) { if ( !cbout( status, &c, 1) ) return false; times--; } return true; } }
/* print a string. returns false if an I/O error occured */ static bool printstr( const char * str, struct _PDCLIB_status_t * status ) { size_t len = status->prec >= 0 ? strnlen( str, status-> prec) : strlen(str); if ( status->width == 0 || status->flags & E_minus ) { // Simple case or left justification if ( status->prec > 0 ) { len = (unsigned) status->prec < len ? (unsigned) status->prec : len; } if ( !cbout( status, str, len ) ) return false; /* right padding */ if ( status->width > status->current ) { len = status->width - status->current; if ( !cbrept( status, ' ', len ) ) return false; } } else { // Right justification if ( status->width > len ) { size_t padding = status->width - len; if ( !cbrept( status, ' ', padding )) return false; } if ( !cbout( status, str, len ) ) return false; } return true; }
int _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status ) { const char * orig_spec = spec; if ( *(++spec) == '%' ) { /* %% -> print single '%' */ if ( !cbout(status, spec, 1) ) return -1; ++spec; return (spec - orig_spec); } /* Initializing status structure */ status->flags = 0; status->base = 0; status->current = 0; status->width = 0; status->prec = EOF; /* First come 0..n flags */ do { switch ( *spec ) { case '-': /* left-aligned output */ status->flags |= E_minus; ++spec; break; case '+': /* positive numbers prefixed with '+' */ status->flags |= E_plus; ++spec; break; case '#': /* alternative format (leading 0x for hex, 0 for octal) */ status->flags |= E_alt; ++spec; break; case ' ': /* positive numbers prefixed with ' ' */ status->flags |= E_space; ++spec; break; case '0': /* right-aligned padding done with '0' instead of ' ' */ status->flags |= E_zero; ++spec; break; default: /* not a flag, exit flag parsing */ status->flags |= E_done; break; } } while ( ! ( status->flags & E_done ) ); /* Optional field width */ if ( *spec == '*' ) { /* Retrieve width value from argument stack */ int width = va_arg( status->arg, int ); if ( width < 0 ) { status->flags |= E_minus; status->width = abs( width ); } else { status->width = width; } ++spec; }
static bool int2base( uintmax_t value, struct _PDCLIB_status_t * status ) { char sign = 0; if ( ! ( status->flags & E_unsigned ) ) { intmax_t signval = (intmax_t) value; bool negative = signval < 0; value = signval < 0 ? -signval : signval; if ( negative ) { sign = '-'; } else if ( status->flags & E_plus ) { sign = '+'; } else if (status->flags & E_space ) { sign = ' '; } } // The user could theoretically ask for a silly buffer length here. // Perhaps after a certain size we should malloc? Or do we refuse to protect // them from their own stupidity? size_t bufLen = (status->width > maxIntLen ? status->width : maxIntLen) + 2; char outbuf[bufLen]; char * outend = outbuf + bufLen; int written = 0; // Build up our output string - backwards { const char * digits = (status->flags & E_lower) ? _PDCLIB_digits : _PDCLIB_Xdigits; uintmax_t remaining = value; if(status->prec != 0 || remaining != 0) do { uintmax_t digit = remaining % status->base; remaining /= status->base; outend[-++written] = digits[digit]; } while(remaining != 0); } // Pad field out to the precision specification while( (long) written < status->prec ) outend[-++written] = '0'; // If a field width specified, and zero padding was requested, then pad to // the field width unsigned padding = 0; if ( ( ! ( status->flags & E_minus ) ) && ( status->flags & E_zero ) ) { while( written < (int) status->width ) { outend[-++written] = '0'; padding++; } } // Prefixes if ( sign != 0 ) { if ( padding == 0 ) written++; outend[-written] = sign; } else if ( status->flags & E_alt ) { switch ( status->base ) { case 8: if ( outend[-written] != '0' ) outend[-++written] = '0'; break; case 16: // No prefix if zero if ( value == 0 ) break; written += padding < 2 ? 2 - padding : 0; outend[-written ] = '0'; outend[-written + 1] = (status->flags & E_lower) ? 'x' : 'X'; break; default: break; } } // Space padding to field width if ( ! ( status->flags & ( E_minus | E_zero ) ) ) { while( written < (int) status->width ) outend[-++written] = ' '; } // Write output return cbout( status, outend - written, written ); }