From 46983693f019c28a1c2420e9e7dfc204916af411 Mon Sep 17 00:00:00 2001 From: solar Date: Wed, 26 Apr 2006 09:34:21 +0000 Subject: [PATCH] Temporary integration of _PDCLIB_print(), broken. --- functions/_PDCLIB/print.c | 459 ++++++++++++++++++++++++++++++++++++ functions/stdio/fopen.c | 2 +- functions/stdio/getchar.c | 2 +- functions/stdio/gets.c | 1 + functions/stdio/vfprintf.c | 21 +- functions/stdio/vsnprintf.c | 24 +- functions/stdio/vsprintf.c | 4 +- includes/stdio.h | 3 + internals/_PDCLIB_int.h | 50 ++-- 9 files changed, 541 insertions(+), 25 deletions(-) create mode 100644 functions/_PDCLIB/print.c diff --git a/functions/_PDCLIB/print.c b/functions/_PDCLIB/print.c new file mode 100644 index 0000000..28cd4bc --- /dev/null +++ b/functions/_PDCLIB/print.c @@ -0,0 +1,459 @@ +/* $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 + +/* Using an integer's bits as flags for both the conversion flags and length + modifiers. +*/ +#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_double 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 DELIVER( x ) do { if ( status->i < status->n ) { if ( status->stream != NULL ) putc( x, status->stream ); else status->s[status->i] = x; } ++(status->i); } while ( 0 ) + +/* This function recursively converts a given integer value to a given base + into a character string. Persistent information - like the number of digits + parsed so far - is recorded in a struct _PDCLIB_status_t, which allows to + avoid overwriting snprintf() limits, and enables the function to do the + necessary padding / prefixing of the character string eventually printed. +*/ +static void int2base( intmax_t value, struct _PDCLIB_status_t * status ) +{ + /* Registering the character being printed at the end of the function here + already so it will be taken into account when the deepestmost recursion + does the prefix / padding stuff. + */ + ++(status->this); + if ( ( value / status->base ) != 0 ) + { + /* More digits to be done - recurse deeper */ + int2base( value / status->base, status ); + } + else + { + /* We reached the last digit, the deepest point of our recursion, and + only now know how long the number to be printed actually is. Now we + have to do the sign, prefix, width, and precision padding stuff + before printing the numbers while we resurface from the recursion. + */ + /* At worst, we need two prefix characters (hex prefix). */ + char preface[3] = "\0"; + size_t preidx = 0; + if ( ( status->flags & E_alt ) && ( status->base == 16 || status->base == 8 ) ) + { + /* Octal / hexadecimal prefix for "%#" conversions */ + preface[ preidx++ ] = '0'; + if ( status->base == 16 ) + { + preface[ preidx++ ] = ( status->flags & E_lower ) ? 'x' : 'X'; + } + } + if ( value < 0 ) + { + /* Negative sign for negative values - at all times. */ + preface[ preidx++ ] = '-'; + } + else if ( ! ( status->flags & E_unsigned ) ) + { + /* plus sign / extra space are only for unsigned conversions */ + if ( status->flags & E_plus ) + { + preface[ preidx++ ] = '+'; + } + else if ( status->flags & E_space ) + { + preface[ preidx++ ] = ' '; + } + } + { + size_t prec_pads = ( status->prec > status->this ) ? ( status->prec - status->this ) : 0; + if ( ! ( status->flags & ( E_minus | E_zero ) ) ) + { + /* Space padding is only done if no zero padding or left alignment + is requested. Leave space for any prefixes determined above. + */ + /* The number of characters to be printed, plus prefixes if any. */ + /* This line contained probably the most stupid, time-wasting bug + I've ever perpetrated. Greetings to Samface, DevL, and all + sceners at Breakpoint 2006. + */ + size_t characters = preidx + ( ( status->this > status->prec ) ? status->this : status->prec ); + if ( status->width > characters ) + { + for ( int i = 0; i < status->width - characters; ++i ) + { + DELIVER( ' ' ); + ++(status->this); + } + } + } + /* Now we did the padding, do the prefixes (if any). */ + preidx = 0; + while ( preface[ preidx ] != '\0' ) + { + DELIVER( preface[ preidx++ ] ); + ++(status->this); + } + if ( ( ! ( status->flags & E_minus ) ) && ( status->flags & E_zero ) ) + { + /* If field is not left aligned, and zero padding is requested, do + so. + */ + while ( status->this < status->width ) + { + DELIVER( '0' ); + ++(status->this); + } + } + /* Do the precision padding if necessary. */ + for ( int i = 0; i < prec_pads; ++i ) + { + DELIVER( '0' ); + } + } + } + /* Recursion tail - print the current digit. */ + { + int digit = value % status->base; + if ( digit < 0 ) + { + digit *= -1; + } + if ( status->flags & E_lower ) + { + /* Lowercase letters. Same array used for strto...(). */ + DELIVER( _PDCLIB_digits[ digit ] ); + } + else + { + /* Uppercase letters. Array only used here, only 0-F. */ + DELIVER( _PDCLIB_Xdigits[ digit ] ); + } + } +} + +/* This function is to be called with spec pointing to the leading '%' of a + printf() conversion specifier. +*/ +const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status ) +{ + const char * orig_spec = spec; + if ( *(++spec) == '%' ) + { + DELIVER( *spec ); + return ++spec; + } + /* Initializing status structure */ + status->flags = 0; + status->base = 0; + status->this = 0; + status->width = 0; + status->prec = 0; + + /* First come 0..n flags */ + do + { + switch ( *spec ) + { + case '-': + status->flags |= E_minus; + ++spec; + break; + case '+': + status->flags |= E_plus; + ++spec; + break; + case '#': + status->flags |= E_alt; + ++spec; + break; + case ' ': + status->flags |= E_space; + ++spec; + break; + case '0': + status->flags |= E_zero; + ++spec; + break; + default: + status->flags |= E_done; + break; + } + } while ( ! ( status->flags & E_done ) ); + + /* Optional field width */ + if ( *spec == '*' ) + { + /* Retrieve width value from argument stack */ + if ( ( status->width = va_arg( status->arg, int ) ) < 0 ) + { + /* Negative value is '-' flag plus absolute value */ + status->flags |= E_minus; + status->width *= -1; + } + ++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 ); + } + else + { + char * endptr; + status->prec = (int)strtol( spec, &endptr, 10 ); + if ( spec == endptr ) + { + /* Decimal point but no number - bad conversion specifier. */ + return orig_spec; + } + spec = endptr; + } + /* 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' ) + { + status->flags |= E_char; + ++spec; + } + else + { + status->flags |= E_short; + } + break; + case 'l': + if ( *spec == 'l' ) + { + status->flags |= E_llong; + ++spec; + } + else + { + status->flags |= E_long; + } + break; + case 'j': + status->flags |= E_intmax; + break; + case 'z': + status->flags |= E_size; + break; + case 't': + status->flags |= E_ptrdiff; + break; + case 'L': + status->flags |= E_double; + 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: Flags, wide chars. */ + DELIVER( va_arg( status->arg, int ) ); + return ++spec; + case 's': + /* TODO: Flags, wide chars. */ + { + char * s = va_arg( status->arg, char * ); + while ( *s != '\0' ) + { + DELIVER( *(s++) ); + } + 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; + } + ++(status->this); + if ( ( value / status->base ) != 0 ) + { + int2base( (intmax_t)(value / status->base), status ); + } + int digit = value % status->base; + if ( digit < 0 ) + { + digit *= -1; + } + if ( status->flags & E_lower ) + { + DELIVER( _PDCLIB_digits[ digit ] ); + } + else + { + DELIVER( _PDCLIB_Xdigits[ digit ] ); + } + } + 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->this < status->width ) + { + DELIVER( ' ' ); + ++(status->this); + } + } + if ( status->i >= status->n ) + { + status->s[status->n - 1] = '\0'; + } + } + return ++spec; +} diff --git a/functions/stdio/fopen.c b/functions/stdio/fopen.c index f22287d..244549c 100644 --- a/functions/stdio/fopen.c +++ b/functions/stdio/fopen.c @@ -12,7 +12,7 @@ #ifndef REGTEST #include <_PDCLIB_glue.h> -static const FILE * _PDCLIB_filelist = NULL; +static FILE * _PDCLIB_filelist = NULL; static int filemode( char const * const mode ) { diff --git a/functions/stdio/getchar.c b/functions/stdio/getchar.c index a98f6d7..0aef0c3 100644 --- a/functions/stdio/getchar.c +++ b/functions/stdio/getchar.c @@ -10,7 +10,7 @@ #ifndef REGTEST -int getchar( void ) +int (getchar)( void ) { return fgetc( stdin ); } diff --git a/functions/stdio/gets.c b/functions/stdio/gets.c index 4aa45ff..2757522 100644 --- a/functions/stdio/gets.c +++ b/functions/stdio/gets.c @@ -7,6 +7,7 @@ */ #include +#include #ifndef REGTEST diff --git a/functions/stdio/vfprintf.c b/functions/stdio/vfprintf.c index 12a0909..3656843 100644 --- a/functions/stdio/vfprintf.c +++ b/functions/stdio/vfprintf.c @@ -8,13 +8,30 @@ #include #include +#include #ifndef REGTEST int vfprintf( FILE * _PDCLIB_restrict stream, const char * _PDCLIB_restrict format, va_list arg ) { - /* TODO: Implement using vsnprintf() writing to file buffer */ - return 0; + /* TODO: This function should interpret format as multibyte characters. */ + /* Members: base, flags, n, i, this, s, width, prec, stream, arg */ + struct _PDCLIB_status_t status = { 0, 0, SIZE_MAX, 0, 0, NULL, 0, 0, stream, arg }; + while ( *format != '\0' ) + { + const char * rc; + if ( ( *format != '%' ) || ( ( rc = _PDCLIB_print( format, &status ) ) == format ) ) + { + /* No conversion specifier, print verbatim */ + putc( *(format++), stream ); + } + else + { + /* Continue parsing after conversion specifier */ + format = rc; + } + } + return status.i; } #endif diff --git a/functions/stdio/vsnprintf.c b/functions/stdio/vsnprintf.c index 08f2355..84ad51d 100644 --- a/functions/stdio/vsnprintf.c +++ b/functions/stdio/vsnprintf.c @@ -11,11 +11,27 @@ #ifndef REGTEST -int vsnprintf( char * str, size_t size, const char * format, _PDCLIB_va_list arg ) +int vsnprintf( char * s, size_t n, const char * format, _PDCLIB_va_list arg ) { - /* TODO: This function should interpret format as multibyte characters. */ - /* TODO: Implement */ - return 0; + /* TODO: This function should interpret format as multibyte characters. */ + /* Members: base, flags, n, i, this, s, width, prec, stream, arg */ + struct _PDCLIB_status_t status = { 0, 0, n, 0, 0, s, 0, 0, NULL, arg }; + while ( *format != '\0' ) + { + const char * rc; + if ( ( *format != '%' ) || ( ( rc = _PDCLIB_print( format, &status ) ) == format ) ) + { + /* No conversion specifier, print verbatim */ + s[ status.i++ ] = *(format++); + } + else + { + /* Continue parsing after conversion specifier */ + format = rc; + } + } + s[ status.i ] = '\0'; + return status.i; } #endif diff --git a/functions/stdio/vsprintf.c b/functions/stdio/vsprintf.c index 27e41d8..c93f5cd 100644 --- a/functions/stdio/vsprintf.c +++ b/functions/stdio/vsprintf.c @@ -12,7 +12,9 @@ #ifndef REGTEST -int vsprintf( char * str, const char * format, _PDCLIB_va_list arg ) +int foo = SIZE_MAX; + +int vsprintf( char * str, const char * format, va_list arg ) { return vsnprintf( str, SIZE_MAX, format, arg ); } diff --git a/includes/stdio.h b/includes/stdio.h index 05d5b6e..f8232cd 100644 --- a/includes/stdio.h +++ b/includes/stdio.h @@ -44,6 +44,9 @@ typedef struct _PDCLIB_file_t FILE; #define SEEK_END 2 #define SEEK_SET 4 +#define stdin NULL +#define stdout NULL + /* Operations on files */ /* Remove the given file. diff --git a/internals/_PDCLIB_int.h b/internals/_PDCLIB_int.h index 5e7d840..f3252e1 100644 --- a/internals/_PDCLIB_int.h +++ b/internals/_PDCLIB_int.h @@ -261,7 +261,7 @@ typedef unsigned _PDCLIB_intmax _PDCLIB_uintmax_t; #define _PDCLIB_FRW 8 #define _PDCLIB_FBIN 16 -struct +struct _PDCLIB_file_t { _PDCLIB_fd_t handle; /* OS-specific file descriptor */ _PDCLIB_fpos_t position; /* file position indicator */ @@ -270,21 +270,7 @@ struct int status; /* misc. status bits */ /*mbstate_t mbstate; multibyte parse state - TODO: Unmask. */ struct _PDCLIB_file_t * next; /* provisions for linked list handling */ -} _PDCLIB_file_t; - -/* -------------------------------------------------------------------------- */ -/* Declaration of helper functions (implemented in functions/_PDCLIB). */ -/* -------------------------------------------------------------------------- */ - -/* This is the main function called by atoi(), atol() and atoll(). */ -_PDCLIB_intmax_t _PDCLIB_atomax( const char * s ); - -/* Two helper functions used by strtol(), strtoul() and long long variants. */ -const char * _PDCLIB_strtox_prelim( const char * p, char * sign, int * base ); -_PDCLIB_uintmax_t _PDCLIB_strtox_main( const char ** p, unsigned int base, _PDCLIB_uintmax_t error, _PDCLIB_uintmax_t limval, _PDCLIB_uintmax_t limdigit, char * sign ); - -/* Digits array used by various integer conversion functions in */ -extern char _PDCLIB_digits[]; +}; /* -------------------------------------------------------------------------- */ /* Internal data types */ @@ -310,6 +296,21 @@ struct _PDCLIB_memnode_t struct _PDCLIB_memnode_t * next; }; +/* Status structure required by _PDCLIB_print(). */ +struct _PDCLIB_status_t +{ + int base; /* base to which the value shall be converted */ + _PDCLIB_int_fast32_t flags; /* flags and length modifiers */ + _PDCLIB_size_t n; /* maximum number of characters to be written */ + _PDCLIB_size_t i; /* number of characters already written */ + _PDCLIB_size_t this; /* output chars in the current conversion */ + char * s; /* target buffer */ + _PDCLIB_size_t width; /* width of current field */ + _PDCLIB_size_t prec; /* precision of current field */ + struct _PDCLIB_file_t * stream;/* for to-stream output */ + _PDCLIB_va_list arg; /* argument stack passed to the printf function */ +}; + #if 0 /* fpos_t, an object type (not an array!) capable of storing any position @@ -321,3 +322,20 @@ typedef unsigned long long int _PDCLIB_fpos_t; typedef int _PDCLIB_fd_t; #endif + +/* -------------------------------------------------------------------------- */ +/* Declaration of helper functions (implemented in functions/_PDCLIB). */ +/* -------------------------------------------------------------------------- */ + +/* This is the main function called by atoi(), atol() and atoll(). */ +_PDCLIB_intmax_t _PDCLIB_atomax( const char * s ); + +/* Two helper functions used by strtol(), strtoul() and long long variants. */ +const char * _PDCLIB_strtox_prelim( const char * p, char * sign, int * base ); +_PDCLIB_uintmax_t _PDCLIB_strtox_main( const char ** p, unsigned int base, _PDCLIB_uintmax_t error, _PDCLIB_uintmax_t limval, _PDCLIB_uintmax_t limdigit, char * sign ); + +/* Digits array used by various integer conversion functions in */ +extern char _PDCLIB_digits[]; + +/* The worker for all printf() type of functions. */ +const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status ); -- 2.40.0