X-Git-Url: https://pd.if.org/git/?a=blobdiff_plain;f=functions%2F_PDCLIB%2Fprint.c;fp=functions%2F_PDCLIB%2Fprint.c;h=0000000000000000000000000000000000000000;hb=3082b97d8f2de1584430ad42671a0e056ed33be4;hp=95fbe9c752e62acd964b2f5c981e3c6e2d7d56da;hpb=d7f27d5325d7c44d93be00662b13aa9ffdec76b1;p=pdclib diff --git a/functions/_PDCLIB/print.c b/functions/_PDCLIB/print.c deleted file mode 100644 index 95fbe9c..0000000 --- a/functions/_PDCLIB/print.c +++ /dev/null @@ -1,559 +0,0 @@ -/* $Id$ */ - -/* _PDCLIB_print( const char *, struct _PDCLIB_status_t * ) - - This file is part of the Public Domain C Library (PDCLib). - Permission is granted to use, modify, and / or redistribute at will. -*/ - -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef REGTEST - -/* Using an integer's bits as flags for both the conversion flags and length - modifiers. -*/ -/* FIXME: one too many flags to work on a 16-bit machine, join some (e.g. the - width flags) into a combined field. -*/ -#define E_minus (1<<0) -#define E_plus (1<<1) -#define E_alt (1<<2) -#define E_space (1<<3) -#define E_zero (1<<4) -#define E_done (1<<5) -#define E_char (1<<6) -#define E_short (1<<7) -#define E_long (1<<8) -#define E_llong (1<<9) -#define E_intmax (1<<10) -#define E_size (1<<11) -#define E_ptrdiff (1<<12) -#define E_intptr (1<<13) -#define E_ldouble (1<<14) -#define E_lower (1<<15) -#define E_unsigned (1<<16) - -/* This macro delivers a given character to either a memory buffer or a stream, - depending on the contents of 'status' (struct _PDCLIB_status_t). - x - the character to be delivered - i - pointer to number of characters already delivered in this call - n - pointer to maximum number of characters to be delivered in this call - s - the buffer into which the character shall be delivered -*/ -#define PUT( x ) \ -do { \ - int character = x; \ - if ( status->i < status->n ) { \ - if ( status->stream != NULL ) \ - putc_unlocked( character, status->stream ); \ - else \ - status->s[status->i] = character; \ - } \ - ++(status->i); \ -} while ( 0 ) - -/* Maximum number of output characters = - * number of bits in (u)intmax_t / number of bits per character in smallest - * base. Smallest base is octal, 3 bits/char. - * - * Additionally require 2 extra characters for prefixes - */ -static const size_t maxIntLen = sizeof(intmax_t) * CHAR_BIT / 3 + 1; - -static void 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 - status->current = written; - while ( written ) - PUT( outend[-written--] ); -} - -static void printstr( const char * str, struct _PDCLIB_status_t * status ) -{ - if ( status->width == 0 || status->flags & E_minus ) - { - // Simple case or left justification - while ( str[status->current] && - ( status->prec < 0 || (long)status->current < status->prec ) ) - { - PUT( str[status->current++] ); - } - - while( status->current < status->width ) - { - PUT( ' ' ); - status->current++; - } - } else { - // Right justification - size_t len = status->prec >= 0 ? strnlen( str, status->prec ) - : strlen( str ); - int padding = status->width - len; - while((long)status->current < padding) - { - PUT( ' ' ); - status->current++; - } - - for( size_t i = 0; i != len; i++ ) - { - PUT( str[i] ); - status->current++; - } - } -} - -static void printchar( char chr, struct _PDCLIB_status_t * status ) -{ - if( ! ( status->flags & E_minus ) ) - { - // Right justification - for( ; status->current + 1 < status->width; status->current++) - { - PUT( ' ' ); - } - PUT( chr ); - status->current++; - } else { - // Left justification - PUT( chr ); - status->current++; - - for( ; status->current < status->width; status->current++) - { - PUT( ' ' ); - } - } -} - -const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status ) -{ - const char * orig_spec = spec; - if ( *(++spec) == '%' ) - { - /* %% -> print single '%' */ - PUT( *spec ); - return ++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; - } - else - { - /* If a width is given, strtol() will return its value. If not given, - strtol() will return zero. In both cases, endptr will point to the - rest of the conversion specifier - just what we need. - */ - status->width = (int)strtol( spec, (char**)&spec, 10 ); - } - - /* Optional precision */ - if ( *spec == '.' ) - { - ++spec; - if ( *spec == '*' ) - { - /* Retrieve precision value from argument stack. A negative value - is as if no precision is given - as precision is initalized to - EOF (negative), there is no need for testing for negative here. - */ - status->prec = va_arg( status->arg, int ); - ++spec; - } - else - { - status->prec = (int)strtol( spec, (char**) &spec, 10 ); - } - /* Having a precision cancels out any zero flag. */ - status->flags &= ~E_zero; - } - - /* Optional length modifier - We step one character ahead in any case, and step back only if we find - there has been no length modifier (or step ahead another character if it - has been "hh" or "ll"). - */ - switch ( *(spec++) ) - { - case 'h': - if ( *spec == 'h' ) - { - /* hh -> char */ - status->flags |= E_char; - ++spec; - } - else - { - /* h -> short */ - status->flags |= E_short; - } - break; - case 'l': - if ( *spec == 'l' ) - { - /* ll -> long long */ - status->flags |= E_llong; - ++spec; - } - else - { - /* k -> long */ - status->flags |= E_long; - } - break; - case 'j': - /* j -> intmax_t, which might or might not be long long */ - status->flags |= E_intmax; - break; - case 'z': - /* z -> size_t, which might or might not be unsigned int */ - status->flags |= E_size; - break; - case 't': - /* t -> ptrdiff_t, which might or might not be long */ - status->flags |= E_ptrdiff; - break; - case 'L': - /* L -> long double */ - status->flags |= E_ldouble; - break; - default: - --spec; - break; - } - - /* Conversion specifier */ - switch ( *spec ) - { - case 'd': - /* FALLTHROUGH */ - case 'i': - status->base = 10; - break; - case 'o': - status->base = 8; - status->flags |= E_unsigned; - break; - case 'u': - status->base = 10; - status->flags |= E_unsigned; - break; - case 'x': - status->base = 16; - status->flags |= ( E_lower | E_unsigned ); - break; - case 'X': - status->base = 16; - status->flags |= E_unsigned; - break; - case 'f': - case 'F': - case 'e': - case 'E': - case 'g': - case 'G': - break; - case 'a': - case 'A': - break; - case 'c': - /* TODO: wide chars. */ - printchar( va_arg( status->arg, int ), status ); - return ++spec; - case 's': - /* TODO: wide chars. */ - { - char * s = va_arg( status->arg, char * ); - printstr( s, status ); - return ++spec; - } - case 'p': - /* TODO: E_long -> E_intptr */ - status->base = 16; - status->flags |= ( E_lower | E_unsigned | E_alt | E_long ); - break; - case 'n': - { - int * val = va_arg( status->arg, int * ); - *val = status->i; - return ++spec; - } - default: - /* No conversion specifier. Bad conversion. */ - return orig_spec; - } - - /* Do the actual output based on our findings */ - if ( status->base != 0 ) - { - /* Integer conversions */ - /* TODO: Check for invalid flag combinations. */ - if ( status->flags & E_unsigned ) - { - uintmax_t value; - switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_size ) ) - { - case E_char: - value = (uintmax_t)(unsigned char)va_arg( status->arg, int ); - break; - case E_short: - value = (uintmax_t)(unsigned short)va_arg( status->arg, int ); - break; - case 0: - value = (uintmax_t)va_arg( status->arg, unsigned int ); - break; - case E_long: - value = (uintmax_t)va_arg( status->arg, unsigned long ); - break; - case E_llong: - value = (uintmax_t)va_arg( status->arg, unsigned long long ); - break; - case E_size: - value = (uintmax_t)va_arg( status->arg, size_t ); - break; - } - int2base( value, status ); - } - else - { - switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_intmax ) ) - { - case E_char: - int2base( (intmax_t)(char)va_arg( status->arg, int ), status ); - break; - case E_short: - int2base( (intmax_t)(short)va_arg( status->arg, int ), status ); - break; - case 0: - int2base( (intmax_t)va_arg( status->arg, int ), status ); - break; - case E_long: - int2base( (intmax_t)va_arg( status->arg, long ), status ); - break; - case E_llong: - int2base( (intmax_t)va_arg( status->arg, long long ), status ); - break; - case E_ptrdiff: - int2base( (intmax_t)va_arg( status->arg, ptrdiff_t ), status ); - break; - case E_intmax: - int2base( va_arg( status->arg, intmax_t ), status ); - break; - } - } - if ( status->flags & E_minus ) - { - while ( status->current < status->width ) - { - PUT( ' ' ); - ++(status->current); - } - } - if ( status->i >= status->n && status->n > 0 ) - { - status->s[status->n - 1] = '\0'; - } - } - return ++spec; -} - -#endif - -#ifdef TEST -#define _PDCLIB_FILEID "_PDCLIB/print.c" -#define _PDCLIB_STRINGIO - -#include <_PDCLIB_test.h> - -#ifndef REGTEST -static int testprintf( char * buffer, const char * format, ... ) -{ - /* Members: base, flags, n, i, current, s, width, prec, stream, arg */ - struct _PDCLIB_status_t status; - status.base = 0; - status.flags = 0; - status.n = 100; - status.i = 0; - status.current = 0; - status.s = buffer; - status.width = 0; - status.prec = 0; - status.stream = NULL; - va_start( status.arg, format ); - memset( buffer, '\0', 100 ); - if ( *(_PDCLIB_print( format, &status )) != '\0' ) - { - printf( "_PDCLIB_print() did not return end-of-specifier on '%s'.\n", format ); - ++TEST_RESULTS; - } - va_end( status.arg ); - return status.i; -} -#endif - -#define TEST_CONVERSION_ONLY - -int main( void ) -{ -#ifndef REGTEST - char target[100]; -#include "printf_testcases.h" -#endif - return TEST_RESULTS; -} - -#endif