diff options
author | tcsullivan <tullivan99@gmail.com> | 2018-11-17 13:02:57 -0500 |
---|---|---|
committer | tcsullivan <tullivan99@gmail.com> | 2018-11-17 13:02:57 -0500 |
commit | c6ef89664b8c0d7aa85bddd5c7014aa6df82cbe7 (patch) | |
tree | d1f9d09412a46bdf4344fe30392455070a72993d /src/pdclib/functions/_PDCLIB | |
parent | db38c4b9dac461de0ed75bf6d079dacba1b31bc9 (diff) |
added pdclib, removed sash
Diffstat (limited to 'src/pdclib/functions/_PDCLIB')
23 files changed, 3038 insertions, 0 deletions
diff --git a/src/pdclib/functions/_PDCLIB/Readme.txt b/src/pdclib/functions/_PDCLIB/Readme.txt new file mode 100644 index 0000000..13ad05c --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/Readme.txt @@ -0,0 +1,10 @@ +This directory holds various "internals" of PDCLib: + +- definitions of helper functions not specified by the standard (hidden in the + _PDCLIB_* namespace); + +- definitions of data objects, both internal (like _PDCLIB_digits) and specified by + the standard (_PDCLIB_errno); + +- test drivers for functionality that does not have its own implementation + file to put the test driver in (stdarg). diff --git a/src/pdclib/functions/_PDCLIB/_PDCLIB_atomax.c b/src/pdclib/functions/_PDCLIB/_PDCLIB_atomax.c new file mode 100644 index 0000000..eaf7ced --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/_PDCLIB_atomax.c @@ -0,0 +1,46 @@ +/* _PDCLIB_atomax( const char * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include <string.h> +#include <ctype.h> + +#ifndef REGTEST + +_PDCLIB_intmax_t _PDCLIB_atomax( const char * s ) +{ + _PDCLIB_intmax_t rc = 0; + char sign = '+'; + const char * x; + /* TODO: In other than "C" locale, additional patterns may be defined */ + while ( isspace( *s ) ) ++s; + if ( *s == '+' ) ++s; + else if ( *s == '-' ) sign = *(s++); + /* TODO: Earlier version was missing tolower() but was not caught by tests */ + while ( ( x = memchr( _PDCLIB_digits, tolower(*(s++)), 10 ) ) != NULL ) + { + rc = rc * 10 + ( x - _PDCLIB_digits ); + } + return ( sign == '+' ) ? rc : -rc; +} + +#endif + +#ifdef TEST + +#include "_PDCLIB_test.h" + +int main( void ) +{ +#ifndef REGTEST + /* basic functionality */ + TESTCASE( _PDCLIB_atomax( "123" ) == 123 ); + /* testing skipping of leading whitespace and trailing garbage */ + TESTCASE( _PDCLIB_atomax( " \n\v\t\f123xyz" ) == 123 ); +#endif + return TEST_RESULTS; +} + +#endif diff --git a/src/pdclib/functions/_PDCLIB/_PDCLIB_closeall.c b/src/pdclib/functions/_PDCLIB/_PDCLIB_closeall.c new file mode 100644 index 0000000..3c928e7 --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/_PDCLIB_closeall.c @@ -0,0 +1,37 @@ +/* _PDCLIB_closeall( void ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include <stdio.h> + +#ifndef REGTEST + +extern struct _PDCLIB_file_t * _PDCLIB_filelist; + +void _PDCLIB_closeall( void ) +{ + struct _PDCLIB_file_t * stream = _PDCLIB_filelist; + struct _PDCLIB_file_t * next; + while ( stream != NULL ) + { + next = stream->next; + fclose( stream ); + stream = next; + } +} + +#endif + +#ifdef TEST + +#include "_PDCLIB_test.h" + +int main( void ) +{ + /* No testdriver */ + return TEST_RESULTS; +} + +#endif diff --git a/src/pdclib/functions/_PDCLIB/_PDCLIB_digits.c b/src/pdclib/functions/_PDCLIB/_PDCLIB_digits.c new file mode 100644 index 0000000..69c4f2b --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/_PDCLIB_digits.c @@ -0,0 +1,33 @@ +/* _PDCLIB_digits + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#ifndef REGTEST + +#include "pdclib/_PDCLIB_int.h" + +const char _PDCLIB_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz"; + +/* For _PDCLIB/print.c only; obsolete with ctype.h */ +const char _PDCLIB_Xdigits[] = "0123456789ABCDEF"; + +#endif + +#ifdef TEST + +#include "_PDCLIB_test.h" + +#include <string.h> + +int main( void ) +{ +#ifndef REGTEST + TESTCASE( strcmp( _PDCLIB_digits, "0123456789abcdefghijklmnopqrstuvwxyz" ) == 0 ); + TESTCASE( strcmp( _PDCLIB_Xdigits, "0123456789ABCDEF" ) == 0 ); +#endif + return TEST_RESULTS; +} + +#endif diff --git a/src/pdclib/functions/_PDCLIB/_PDCLIB_filemode.c b/src/pdclib/functions/_PDCLIB/_PDCLIB_filemode.c new file mode 100644 index 0000000..2555a00 --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/_PDCLIB_filemode.c @@ -0,0 +1,90 @@ +/* _PDCLIB_filemode( const char * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include <stddef.h> + +#ifndef REGTEST + +/* Helper function that parses the C-style mode string passed to fopen() into + the PDCLib flags FREAD, FWRITE, FAPPEND, FRW (read-write) and FBIN (binary + mode). +*/ +unsigned int _PDCLIB_filemode( const char * const mode ) +{ + unsigned rc = 0; + size_t i; + switch ( mode[0] ) + { + case 'r': + rc |= _PDCLIB_FREAD; + break; + case 'w': + rc |= _PDCLIB_FWRITE; + break; + case 'a': + rc |= _PDCLIB_FAPPEND | _PDCLIB_FWRITE; + break; + default: + /* Other than read, write, or append - invalid */ + return 0; + } + for ( i = 1; i < 4; ++i ) + { + switch ( mode[i] ) + { + case '+': + if ( rc & _PDCLIB_FRW ) return 0; /* Duplicates are invalid */ + rc |= _PDCLIB_FRW; + break; + case 'b': + if ( rc & _PDCLIB_FBIN ) return 0; /* Duplicates are invalid */ + rc |= _PDCLIB_FBIN; + break; + case '\0': + /* End of mode */ + return rc; + default: + /* Other than read/write or binary - invalid. */ + return 0; + } + } + /* Longer than three chars - invalid. */ + return 0; +} + +#endif + +#ifdef TEST + +#include "_PDCLIB_test.h" + +int main( void ) +{ +#ifndef REGTEST + TESTCASE( _PDCLIB_filemode( "r" ) == _PDCLIB_FREAD ); + TESTCASE( _PDCLIB_filemode( "w" ) == _PDCLIB_FWRITE ); + TESTCASE( _PDCLIB_filemode( "a" ) == ( _PDCLIB_FAPPEND | _PDCLIB_FWRITE ) ); + TESTCASE( _PDCLIB_filemode( "r+" ) == ( _PDCLIB_FREAD | _PDCLIB_FRW ) ); + TESTCASE( _PDCLIB_filemode( "w+" ) == ( _PDCLIB_FWRITE | _PDCLIB_FRW ) ); + TESTCASE( _PDCLIB_filemode( "a+" ) == ( _PDCLIB_FAPPEND | _PDCLIB_FWRITE | _PDCLIB_FRW ) ); + TESTCASE( _PDCLIB_filemode( "rb" ) == ( _PDCLIB_FREAD | _PDCLIB_FBIN ) ); + TESTCASE( _PDCLIB_filemode( "wb" ) == ( _PDCLIB_FWRITE | _PDCLIB_FBIN ) ); + TESTCASE( _PDCLIB_filemode( "ab" ) == ( _PDCLIB_FAPPEND | _PDCLIB_FWRITE | _PDCLIB_FBIN ) ); + TESTCASE( _PDCLIB_filemode( "r+b" ) == ( _PDCLIB_FREAD | _PDCLIB_FRW | _PDCLIB_FBIN ) ); + TESTCASE( _PDCLIB_filemode( "w+b" ) == ( _PDCLIB_FWRITE | _PDCLIB_FRW | _PDCLIB_FBIN ) ); + TESTCASE( _PDCLIB_filemode( "a+b" ) == ( _PDCLIB_FAPPEND | _PDCLIB_FWRITE | _PDCLIB_FRW | _PDCLIB_FBIN ) ); + TESTCASE( _PDCLIB_filemode( "rb+" ) == ( _PDCLIB_FREAD | _PDCLIB_FRW | _PDCLIB_FBIN ) ); + TESTCASE( _PDCLIB_filemode( "wb+" ) == ( _PDCLIB_FWRITE | _PDCLIB_FRW | _PDCLIB_FBIN ) ); + TESTCASE( _PDCLIB_filemode( "ab+" ) == ( _PDCLIB_FAPPEND | _PDCLIB_FWRITE | _PDCLIB_FRW | _PDCLIB_FBIN ) ); + TESTCASE( _PDCLIB_filemode( "x" ) == 0 ); + TESTCASE( _PDCLIB_filemode( "r++" ) == 0 ); + TESTCASE( _PDCLIB_filemode( "wbb" ) == 0 ); + TESTCASE( _PDCLIB_filemode( "a+bx" ) == 0 ); +#endif + return TEST_RESULTS; +} + +#endif diff --git a/src/pdclib/functions/_PDCLIB/_PDCLIB_is_leap.c b/src/pdclib/functions/_PDCLIB/_PDCLIB_is_leap.c new file mode 100644 index 0000000..2407337 --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/_PDCLIB_is_leap.c @@ -0,0 +1,39 @@ +/* _PDCLIB_is_leap( int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#ifndef REGTEST + +#include "pdclib/_PDCLIB_int.h" + +int _PDCLIB_is_leap( int year_offset ) +{ + /* year given as offset from 1900, matching tm.tm_year in <time.h> */ + long long year = year_offset + 1900ll; + return ( ( year % 4 ) == 0 && ( ( year % 25 ) != 0 || ( year % 400 ) == 0 ) ); +} + +#endif + +#ifdef TEST + +#include "_PDCLIB_test.h" + +int main( void ) +{ +#ifndef REGTEST + /* 1901 not leap */ + TESTCASE( ! _PDCLIB_is_leap( 1 ) ); + /* 1904 leap */ + TESTCASE( _PDCLIB_is_leap( 4 ) ); + /* 1900 not leap */ + TESTCASE( ! _PDCLIB_is_leap( 0 ) ); + /* 2000 leap */ + TESTCASE( _PDCLIB_is_leap( 100 ) ); +#endif + return TEST_RESULTS; +} + +#endif diff --git a/src/pdclib/functions/_PDCLIB/_PDCLIB_load_lc_collate.c b/src/pdclib/functions/_PDCLIB/_PDCLIB_load_lc_collate.c new file mode 100644 index 0000000..d66b996 --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/_PDCLIB_load_lc_collate.c @@ -0,0 +1,63 @@ +/* _PDCLIB_load_lc_collate( const char *, const char * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#ifndef REGTEST + +#include <locale.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "pdclib/_PDCLIB_int.h" + +struct _PDCLIB_lc_collate_t * _PDCLIB_load_lc_collate( const char * path, const char * locale ) +{ + struct _PDCLIB_lc_collate_t * rc = NULL; + const char * extension = "_collate.dat"; + char * file = malloc( strlen( path ) + strlen( locale ) + strlen( extension ) + 1 ); + + if ( file ) + { + FILE * fh; + + strcpy( file, path ); + strcat( file, locale ); + strcat( file, extension ); + + if ( ( fh = fopen( file, "rb" ) ) != NULL ) + { + if ( ( rc = malloc( sizeof( struct _PDCLIB_lc_collate_t ) ) ) != NULL ) + { + /* TODO: Collation data */ + + rc->alloced = 1; + } + + fclose( fh ); + } + + free( file ); + } + + return rc; +} + +#endif + +#ifdef TEST + +#include "_PDCLIB_test.h" + +int main( void ) +{ +#ifndef REGTEST + TESTCASE( NO_TESTDRIVER ); +#endif + + return TEST_RESULTS; +} + +#endif diff --git a/src/pdclib/functions/_PDCLIB/_PDCLIB_load_lc_ctype.c b/src/pdclib/functions/_PDCLIB/_PDCLIB_load_lc_ctype.c new file mode 100644 index 0000000..81fb18f --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/_PDCLIB_load_lc_ctype.c @@ -0,0 +1,365 @@ +/* _PDCLIB_load_lc_ctype( const char *, const char * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#ifndef REGTEST + +#include <inttypes.h> +#include <limits.h> +#include <locale.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "pdclib/_PDCLIB_int.h" + +struct _PDCLIB_lc_ctype_t * _PDCLIB_load_lc_ctype( const char * path, const char * locale ) +{ + struct _PDCLIB_lc_ctype_t * rc = NULL; + const char * extension = "_ctype.dat"; + char * file = malloc( strlen( path ) + strlen( locale ) + strlen( extension ) + 1 ); + + if ( file ) + { + FILE * fh; + + strcpy( file, path ); + strcat( file, locale ); + strcat( file, extension ); + + if ( ( fh = fopen( file, "rb" ) ) != NULL ) + { + if ( ( rc = malloc( sizeof( struct _PDCLIB_lc_ctype_t ) ) ) != NULL ) + { + struct _PDCLIB_lc_ctype_entry_t * entry; + + if ( ( entry = malloc( sizeof( struct _PDCLIB_lc_ctype_entry_t ) * _PDCLIB_CHARSET_SIZE + 1 ) ) != NULL ) + { + rc->entry = entry + 1; + rc->entry[ -1 ].flags = rc->entry[ -1 ].upper = rc->entry[ -1 ].lower = 0; + + if ( fscanf( fh, "%x %x %x %x %x %x", &rc->digits_low, &_PDCLIB_lc_ctype.digits_high, &_PDCLIB_lc_ctype.Xdigits_low, &_PDCLIB_lc_ctype.Xdigits_high, &_PDCLIB_lc_ctype.xdigits_low, &_PDCLIB_lc_ctype.xdigits_high ) == 6 ) + { + size_t i; + + for ( i = 0; i < _PDCLIB_CHARSET_SIZE; ++i ) + { + if ( fscanf( fh, "%x %hhx %hhx", &rc->entry[ i ].flags, &rc->entry[ i ].upper, &rc->entry[ i ].lower ) != 3 ) + { + fclose( fh ); + free( file ); + free( rc->entry - 1 ); + free( rc ); + return NULL; + } + } + } + + rc->alloced = 1; + } + else + { + free( rc ); + } + } + + fclose( fh ); + } + + free( file ); + } + + return rc; +} + +#endif + +#ifdef TEST + +#include "_PDCLIB_test.h" + +#include <ctype.h> + +int main( void ) +{ +#ifndef REGTEST + FILE * fh = fopen( "test_ctype.dat", "wb" ); + TESTCASE( fh != NULL ); + /* For test purposes, let's set up a charset that only has the hex digits */ + /* 0x00..0x09 - digits */ + /* 0x11..0x16 - Xdigits */ + /* 0x21..0x26 - xdigits */ + TESTCASE( fprintf( fh, "%x %x\n", 0x00, 0x09 ) ); + TESTCASE( fprintf( fh, "%x %x %x %x\n", 0x11, 0x16, 0x21, 0x26 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH, 0x00, 0x00 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH, 0x01, 0x01 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH, 0x02, 0x02 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH, 0x03, 0x03 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH, 0x04, 0x04 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH, 0x05, 0x05 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH, 0x06, 0x06 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH, 0x07, 0x07 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH, 0x08, 0x08 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH, 0x09, 0x09 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x0A, 0x0A ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x0B, 0x0B ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x0C, 0x0C ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x0D, 0x0D ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x0E, 0x0E ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x0F, 0x0F ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x10, 0x10 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x11, 0x11 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x12, 0x12 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x13, 0x13 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x14, 0x14 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x15, 0x15 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_UPPER, 0x16, 0x16 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x17, 0x17 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x18, 0x18 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x19, 0x19 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x1A, 0x1A ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x1B, 0x1B ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x1C, 0x1C ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x1D, 0x1D ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x1E, 0x1E ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x1F, 0x1F ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x20, 0x20 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x21, 0x21 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x22, 0x22 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x23, 0x23 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x24, 0x24 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x25, 0x25 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", _PDCLIB_CTYPE_GRAPH | _PDCLIB_CTYPE_ALPHA | _PDCLIB_CTYPE_LOWER, 0x26, 0x26 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x27, 0x27 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x28, 0x28 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x29, 0x29 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x2A, 0x2A ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x2B, 0x2B ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x2C, 0x2C ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x2D, 0x2D ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x2E, 0x2E ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x2F, 0x2F ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x30, 0x30 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x31, 0x31 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x32, 0x32 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x33, 0x33 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x34, 0x34 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x35, 0x35 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x36, 0x36 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x37, 0x37 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x38, 0x38 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x39, 0x39 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x3A, 0x3A ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x3B, 0x3B ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x3C, 0x3C ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x3D, 0x3D ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x3E, 0x3E ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x3F, 0x3F ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x40, 0x40 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x41, 0x41 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x42, 0x42 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x43, 0x43 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x44, 0x44 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x45, 0x45 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x46, 0x46 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x47, 0x47 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x48, 0x48 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x49, 0x49 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x4A, 0x4A ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x4B, 0x4B ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x4C, 0x4C ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x4D, 0x4D ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x4E, 0x4E ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x4F, 0x4F ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x50, 0x50 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x51, 0x51 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x52, 0x52 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x53, 0x53 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x54, 0x54 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x55, 0x55 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x56, 0x56 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x57, 0x57 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x58, 0x58 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x59, 0x59 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x5A, 0x5A ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x5B, 0x5B ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x5C, 0x5C ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x5D, 0x5D ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x5E, 0x5E ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x5F, 0x5F ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x60, 0x60 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x61, 0x61 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x62, 0x62 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x63, 0x63 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x64, 0x64 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x65, 0x65 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x66, 0x66 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x67, 0x67 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x68, 0x68 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x69, 0x69 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x6A, 0x6A ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x6B, 0x6B ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x6C, 0x6C ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x6D, 0x6D ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x6E, 0x6E ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x6F, 0x6F ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x70, 0x70 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x71, 0x71 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x72, 0x72 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x73, 0x73 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x74, 0x74 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x75, 0x75 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x76, 0x76 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x77, 0x77 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x78, 0x78 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x79, 0x79 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x7A, 0x7A ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x7B, 0x7B ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x7C, 0x7C ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x7D, 0x7D ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x7E, 0x7E ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x7F, 0x7F ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x80, 0x80 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x81, 0x81 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x82, 0x82 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x83, 0x83 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x84, 0x84 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x85, 0x85 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x86, 0x86 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x87, 0x87 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x88, 0x88 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x89, 0x89 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x8A, 0x8A ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x8B, 0x8B ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x8C, 0x8C ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x8D, 0x8D ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x8E, 0x8E ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x8F, 0x8F ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x90, 0x90 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x91, 0x91 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x92, 0x92 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x93, 0x93 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x94, 0x94 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x95, 0x95 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x96, 0x96 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x97, 0x97 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x98, 0x98 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x99, 0x99 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x9A, 0x9A ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x9B, 0x9B ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x9C, 0x9C ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x9D, 0x9D ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x9E, 0x9E ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0x9F, 0x9F ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xA0, 0xA0 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xA1, 0xA1 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xA2, 0xA2 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xA3, 0xA3 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xA4, 0xA4 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xA5, 0xA5 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xA6, 0xA6 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xA7, 0xA7 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xA8, 0xA8 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xA9, 0xA9 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xAA, 0xAA ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xAB, 0xAB ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xAC, 0xAC ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xAD, 0xAD ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xAE, 0xAE ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xAF, 0xAF ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xB0, 0xB0 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xB1, 0xB1 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xB2, 0xB2 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xB3, 0xB3 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xB4, 0xB4 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xB5, 0xB5 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xB6, 0xB6 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xB7, 0xB7 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xB8, 0xB8 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xB9, 0xB9 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xBA, 0xBA ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xBB, 0xBB ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xBC, 0xBC ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xBD, 0xBD ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xBE, 0xBE ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xBF, 0xBF ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xC0, 0xC0 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xC1, 0xC1 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xC2, 0xC2 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xC3, 0xC3 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xC4, 0xC4 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xC5, 0xC5 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xC6, 0xC6 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xC7, 0xC7 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xC8, 0xC8 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xC9, 0xC9 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xCA, 0xCA ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xCB, 0xCB ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xCC, 0xCC ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xCD, 0xCD ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xCE, 0xCE ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xCF, 0xCF ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xD0, 0xD0 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xD1, 0xD1 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xD2, 0xD2 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xD3, 0xD3 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xD4, 0xD4 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xD5, 0xD5 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xD6, 0xD6 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xD7, 0xD7 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xD8, 0xD8 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xD9, 0xD9 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xDA, 0xDA ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xDB, 0xDB ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xDC, 0xDC ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xDD, 0xDD ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xDE, 0xDE ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xDF, 0xDF ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xE0, 0xE0 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xE1, 0xE1 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xE2, 0xE2 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xE3, 0xE3 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xE4, 0xE4 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xE5, 0xE5 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xE6, 0xE6 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xE7, 0xE7 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xE8, 0xE8 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xE9, 0xE9 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xEA, 0xEA ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xEB, 0xEB ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xEC, 0xEC ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xED, 0xED ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xEE, 0xEE ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xEF, 0xEF ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xF0, 0xF0 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xF1, 0xF1 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xF2, 0xF2 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xF3, 0xF3 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xF4, 0xF4 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xF5, 0xF5 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xF6, 0xF6 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xF7, 0xF7 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xF8, 0xF8 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xF9, 0xF9 ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xFA, 0xFA ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xFB, 0xFB ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xFC, 0xFC ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xFD, 0xFD ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xFE, 0xFE ) ); + TESTCASE( fprintf( fh, "%x %x %x\n", 0x00, 0xFF, 0xFF) ); + fclose( fh ); + TESTCASE( _PDCLIB_load_lc_ctype( "./", "test" ) != NULL ); + remove( "test_ctype.dat" ); + /* + TESTCASE( isdigit( 0x00 ) && ! isxdigit( 0x00 ) && ! isalpha( 0x00 ) ); + TESTCASE( ! isdigit( 0x11 ) && isxdigit( 0x11 ) && isalpha( 0x11 ) && isupper( 0x11 ) && ! islower( 0x11 ) ); + TESTCASE( ! isdigit( 0x21 ) && isxdigit( 0x21 ) && isalpha( 0x21 ) && ! isupper( 0x11 ) && islower( 0x11 ) ); + */ +#endif + return TEST_RESULTS; +} + +#endif diff --git a/src/pdclib/functions/_PDCLIB/_PDCLIB_load_lc_messages.c b/src/pdclib/functions/_PDCLIB/_PDCLIB_load_lc_messages.c new file mode 100644 index 0000000..6ff2a50 --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/_PDCLIB_load_lc_messages.c @@ -0,0 +1,88 @@ +/* _PDCLIB_load_lc_messages( const char *, const char * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#ifndef REGTEST + +#include <locale.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "pdclib/_PDCLIB_int.h" + +struct _PDCLIB_lc_messages_t * _PDCLIB_load_lc_messages( const char * path, const char * locale ) +{ + struct _PDCLIB_lc_messages_t * rc = NULL; + const char * extension = "_messages.dat"; + char * file = malloc( strlen( path ) + strlen( locale ) + strlen( extension ) + 1 ); + + if ( file ) + { + FILE * fh; + + strcpy( file, path ); + strcat( file, locale ); + strcat( file, extension ); + + if ( ( fh = fopen( file, "rb" ) ) != NULL ) + { + if ( ( rc = malloc( sizeof( struct _PDCLIB_lc_messages_t ) ) ) != NULL ) + { + char * data = _PDCLIB_load_lines( fh, _PDCLIB_ERRNO_MAX ); + + if ( data != NULL ) + { + size_t i; + + for ( i = 0; i < _PDCLIB_ERRNO_MAX; ++i ) + { + rc->errno_texts[ i ] = data; + data += strlen( data ) + 1; + } + + rc->alloced = 1; + } + else + { + free( rc ); + rc = NULL; + } + } + + fclose( fh ); + } + + free( file ); + } + + return rc; +} + +#endif + +#ifdef TEST + +#include "_PDCLIB_test.h" + +int main( void ) +{ +#ifndef REGTEST + FILE * fh = fopen( "test_numeric.dat", "wb" ); + struct _PDCLIB_lc_lconv_numeric_t * lc; + TESTCASE( fh != NULL ); + TESTCASE( fputs( ",\n.\n\n", fh ) != EOF ); + fclose( fh ); + TESTCASE( ( lc = _PDCLIB_load_lc_numeric( "./", "test" ) ) ); + remove( "test_numeric.dat" ); + TESTCASE( strcmp( lc->decimal_point, "," ) == 0 ); + TESTCASE( strcmp( lc->thousands_sep, "." ) == 0 ); + TESTCASE( strcmp( lc->grouping, "" ) == 0 ); +#endif + + return TEST_RESULTS; +} + +#endif diff --git a/src/pdclib/functions/_PDCLIB/_PDCLIB_load_lc_monetary.c b/src/pdclib/functions/_PDCLIB/_PDCLIB_load_lc_monetary.c new file mode 100644 index 0000000..e2dcbb4 --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/_PDCLIB_load_lc_monetary.c @@ -0,0 +1,158 @@ +/* _PDCLIB_load_lc_monetary( const char *, const char * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#ifndef REGTEST + +#include <locale.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "pdclib/_PDCLIB_int.h" + +struct _PDCLIB_lc_lconv_monetary_t * _PDCLIB_load_lc_monetary( const char * path, const char * locale ) +{ + struct _PDCLIB_lc_lconv_monetary_t * rc = NULL; + const char * extension = "_monetary.dat"; + char * file = malloc( strlen( path ) + strlen( locale ) + strlen( extension ) + 1 ); + + if ( file ) + { + FILE * fh; + + strcpy( file, path ); + strcat( file, locale ); + strcat( file, extension ); + + if ( ( fh = fopen( file, "rb" ) ) != NULL ) + { + if ( ( rc = malloc( sizeof( struct _PDCLIB_lc_lconv_monetary_t ) ) ) != NULL ) + { + char buffer[ 14 ]; + char * data = _PDCLIB_load_lines( fh, 7 ); + + if ( data != NULL ) + { + if ( fread( buffer, 1, 14, fh ) == 14 ) + { + rc->mon_decimal_point = data; + data += strlen( data ) + 1; + rc->mon_thousands_sep = data; + data += strlen( data ) + 1; + rc->mon_grouping = data; + data += strlen( data ) + 1; + rc->positive_sign = data; + data += strlen( data ) + 1; + rc->negative_sign = data; + data += strlen( data ) + 1; + rc->currency_symbol = data; + data += strlen( data ) + 1; + rc->int_curr_symbol = data; + + rc->frac_digits = buffer[ 0 ]; + rc->p_cs_precedes = buffer[ 1 ]; + rc->n_cs_precedes = buffer[ 2 ]; + rc->p_sep_by_space = buffer[ 3 ]; + rc->n_sep_by_space = buffer[ 4 ]; + rc->p_sign_posn = buffer[ 5 ]; + rc->n_sign_posn = buffer[ 6 ]; + rc->int_frac_digits = buffer[ 7 ]; + rc->int_p_cs_precedes = buffer[ 8 ]; + rc->int_n_cs_precedes = buffer[ 9 ]; + rc->int_p_sep_by_space = buffer[ 10 ]; + rc->int_n_sep_by_space = buffer[ 11 ]; + rc->int_p_sign_posn = buffer[ 12 ]; + rc->int_n_sign_posn= buffer[ 13 ]; + } + else + { + free( data ); + free( rc ); + rc = NULL; + } + } + else + { + free( rc ); + rc = NULL; + } + } + + fclose( fh ); + } + + free( file ); + } + + return rc; +} + +#endif + +#ifdef TEST + +#include "_PDCLIB_test.h" + +int main( void ) +{ +#ifndef REGTEST + FILE * fh = fopen( "test_monetary.dat", "wb" ); + struct _PDCLIB_lc_lconv_monetary_t * lc; + TESTCASE( fh != NULL ); + fprintf( fh, "%s\n", "," ); /* mon_decimal_point */ + fprintf( fh, "%s\n", "." ); /* mon_thousands_sep */ + fprintf( fh, "%s\n", "3" ); /* mon_grouping */ + fprintf( fh, "%s\n", "" ); /* positive_sign */ + fprintf( fh, "%s\n", "-" ); /* negative_sign */ + fprintf( fh, "%s\n", "\xa4" ); /* currency_symbol */ + fprintf( fh, "%s\n", "EUR" ); /* int_curr_symbol */ + fputc( 2, fh ); /* frac_digits */ + fputc( 0, fh ); /* p_cs_precedes */ + fputc( 0, fh ); /* n_cs_precedes */ + fputc( 1, fh ); /* p_sep_by_space */ + fputc( 1, fh ); /* n_sep_by_space */ + fputc( 1, fh ); /* p_sign_posn */ + fputc( 1, fh ); /* n_sign_posn */ + fputc( 2, fh ); /* int_frac_digits */ + fputc( 0, fh ); /* int_p_cs_precedes */ + fputc( 0, fh ); /* int_n_cs_precedes */ + fputc( 1, fh ); /* int_p_sep_by_space */ + fputc( 1, fh ); /* int_n_sep_by_space */ + fputc( 1, fh ); /* int_p_sign_posn */ + fputc( 1, fh ); /* int_n_sign_posn */ + fprintf( fh, "\n" ); + fclose( fh ); + TESTCASE( ( lc = _PDCLIB_load_lc_monetary( "./", "test" ) ) ); + remove( "test_monetary.dat" ); + TESTCASE( strcmp( lc->mon_decimal_point, "," ) == 0 ); + TESTCASE( strcmp( lc->mon_thousands_sep, "." ) == 0 ); + TESTCASE( strcmp( lc->mon_grouping, "3" ) == 0 ); + TESTCASE( strcmp( lc->positive_sign, "" ) == 0 ); + TESTCASE( strcmp( lc->negative_sign, "-" ) == 0 ); + TESTCASE( strcmp( lc->currency_symbol, "\xa4" ) == 0 ); + TESTCASE( strcmp( lc->int_curr_symbol, "EUR" ) == 0 ); + + TESTCASE( lc->frac_digits == 2 ); + TESTCASE( lc->p_cs_precedes == 0 ); + TESTCASE( lc->n_cs_precedes == 0 ); + TESTCASE( lc->p_sep_by_space == 1 ); + TESTCASE( lc->n_sep_by_space == 1 ); + TESTCASE( lc->p_sign_posn == 1 ); + TESTCASE( lc->n_sign_posn == 1 ); + TESTCASE( lc->int_frac_digits == 2 ); + TESTCASE( lc->int_p_cs_precedes == 0 ); + TESTCASE( lc->int_n_cs_precedes == 0 ); + TESTCASE( lc->int_p_sep_by_space == 1 ); + TESTCASE( lc->int_n_sep_by_space == 1 ); + TESTCASE( lc->int_p_sign_posn == 1 ); + TESTCASE( lc->int_n_sign_posn == 1 ); +#endif + + return TEST_RESULTS; +} + +#endif diff --git a/src/pdclib/functions/_PDCLIB/_PDCLIB_load_lc_numeric.c b/src/pdclib/functions/_PDCLIB/_PDCLIB_load_lc_numeric.c new file mode 100644 index 0000000..de42bbd --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/_PDCLIB_load_lc_numeric.c @@ -0,0 +1,84 @@ +/* _PDCLIB_load_lc_numeric( const char *, const char * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#ifndef REGTEST + +#include <locale.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "pdclib/_PDCLIB_int.h" + +struct _PDCLIB_lc_lconv_numeric_t * _PDCLIB_load_lc_numeric( const char * path, const char * locale ) +{ + struct _PDCLIB_lc_lconv_numeric_t * rc = NULL; + const char * extension = "_numeric.dat"; + char * file = malloc( strlen( path ) + strlen( locale ) + strlen( extension ) + 1 ); + + if ( file ) + { + FILE * fh; + + strcpy( file, path ); + strcat( file, locale ); + strcat( file, extension ); + + if ( ( fh = fopen( file, "rb" ) ) != NULL ) + { + if ( ( rc = malloc( sizeof( struct _PDCLIB_lc_lconv_numeric_t ) ) ) != NULL ) + { + char * data = _PDCLIB_load_lines( fh, 3 ); + + if ( data != NULL ) + { + rc->decimal_point = data; + data += strlen( data ) + 1; + rc->thousands_sep = data; + data += strlen( data ) + 1; + rc->grouping = data; + } + else + { + free( rc ); + rc = NULL; + } + } + + fclose( fh ); + } + + free( file ); + } + + return rc; +} + +#endif + +#ifdef TEST + +#include "_PDCLIB_test.h" + +int main( void ) +{ +#ifndef REGTEST + FILE * fh = fopen( "test_numeric.dat", "wb" ); + struct _PDCLIB_lc_lconv_numeric_t * lc; + TESTCASE( fh != NULL ); + TESTCASE( fputs( ",\n.\n\n", fh ) != EOF ); + fclose( fh ); + TESTCASE( ( lc = _PDCLIB_load_lc_numeric( "./", "test" ) ) ); + remove( "test_numeric.dat" ); + TESTCASE( strcmp( lc->decimal_point, "," ) == 0 ); + TESTCASE( strcmp( lc->thousands_sep, "." ) == 0 ); + TESTCASE( strcmp( lc->grouping, "" ) == 0 ); +#endif + + return TEST_RESULTS; +} + +#endif diff --git a/src/pdclib/functions/_PDCLIB/_PDCLIB_load_lc_time.c b/src/pdclib/functions/_PDCLIB/_PDCLIB_load_lc_time.c new file mode 100644 index 0000000..733e5c7 --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/_PDCLIB_load_lc_time.c @@ -0,0 +1,165 @@ +/* _PDCLIB_load_lc_time( const char *, const char * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#ifndef REGTEST + +#include <locale.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "pdclib/_PDCLIB_int.h" + +struct _PDCLIB_lc_time_t * _PDCLIB_load_lc_time( const char * path, const char * locale ) +{ + struct _PDCLIB_lc_time_t * rc = NULL; + const char * extension = "_time.dat"; + char * file = malloc( strlen( path ) + strlen( locale ) + strlen( extension ) + 1 ); + + if ( file ) + { + FILE * fh; + + strcpy( file, path ); + strcat( file, locale ); + strcat( file, extension ); + + if ( ( fh = fopen( file, "rb" ) ) != NULL ) + { + if ( ( rc = malloc( sizeof( struct _PDCLIB_lc_time_t ) ) ) != NULL ) + { + char * data = _PDCLIB_load_lines( fh, 44 ); + + if ( data != NULL ) + { + size_t i; + + for ( i = 0; i < 12; ++i ) + { + rc->month_name_abbr[ i ] = data; + data += strlen( data ) + 1; + } + + for ( i = 0; i < 12; ++i ) + { + rc->month_name_full[ i ] = data; + data += strlen( data ) + 1; + } + + for ( i = 0; i < 7; ++i ) + { + rc->day_name_abbr[ i ] = data; + data += strlen( data ) + 1; + } + + for ( i = 0; i < 7; ++i ) + { + rc->day_name_full[ i ] = data; + data += strlen( data ) + 1; + } + + rc->alloced = 1; + } + else + { + free( rc ); + rc = NULL; + } + } + + fclose( fh ); + } + + free( file ); + } + + return rc; +} + +#endif + +#ifdef TEST + +#include "_PDCLIB_test.h" + +int main( void ) +{ +#ifndef REGTEST + FILE * fh = fopen( "test_time.dat", "wb" ); + struct _PDCLIB_lc_time_t * lc; + + TESTCASE( fh != NULL ); + + /* month name abbreviation */ + TESTCASE( fprintf( fh, "%s\n", "Jan" ) == 4 ); + TESTCASE( fprintf( fh, "%s\n", "Feb" ) == 4 ); + TESTCASE( fprintf( fh, "%s\n", "M\xe4r" ) == 4 ); + TESTCASE( fprintf( fh, "%s\n", "Apr" ) == 4 ); + TESTCASE( fprintf( fh, "%s\n", "Mai" ) == 4 ); + TESTCASE( fprintf( fh, "%s\n", "Jun" ) == 4 ); + TESTCASE( fprintf( fh, "%s\n", "Jul" ) == 4 ); + TESTCASE( fprintf( fh, "%s\n", "Aug" ) == 4 ); + TESTCASE( fprintf( fh, "%s\n", "Sep" ) == 4 ); + TESTCASE( fprintf( fh, "%s\n", "Okt" ) == 4 ); + TESTCASE( fprintf( fh, "%s\n", "Nov" ) == 4 ); + TESTCASE( fprintf( fh, "%s\n", "Dez" ) == 4 ); + /* month name full */ + TESTCASE( fprintf( fh, "%s\n", "Januar" ) == 7 ); + TESTCASE( fprintf( fh, "%s\n", "Februar" ) == 8 ); + TESTCASE( fprintf( fh, "%s\n", "M\xe4rz" ) == 5 ); + TESTCASE( fprintf( fh, "%s\n", "April" ) == 6 ); + TESTCASE( fprintf( fh, "%s\n", "Mai" ) == 4 ); + TESTCASE( fprintf( fh, "%s\n", "Juni" ) == 5 ); + TESTCASE( fprintf( fh, "%s\n", "Juli" ) == 5 ); + TESTCASE( fprintf( fh, "%s\n", "August" ) == 7 ); + TESTCASE( fprintf( fh, "%s\n", "September" ) == 10 ); + TESTCASE( fprintf( fh, "%s\n", "Oktober" ) == 8 ); + TESTCASE( fprintf( fh, "%s\n", "November" ) == 9 ); + TESTCASE( fprintf( fh, "%s\n", "Dezember" ) == 9 ); + /* day name abbreviation */ + TESTCASE( fprintf( fh, "%s\n", "So" ) == 3 ); + TESTCASE( fprintf( fh, "%s\n", "Mo" ) == 3 ); + TESTCASE( fprintf( fh, "%s\n", "Di" ) == 3 ); + TESTCASE( fprintf( fh, "%s\n", "Mi" ) == 3 ); + TESTCASE( fprintf( fh, "%s\n", "Do" ) == 3 ); + TESTCASE( fprintf( fh, "%s\n", "Fr" ) == 3 ); + TESTCASE( fprintf( fh, "%s\n", "Sa" ) == 3 ); + /* day name full */ + TESTCASE( fprintf( fh, "%s\n", "Sonntag" ) == 8 ); + TESTCASE( fprintf( fh, "%s\n", "Montag" ) == 7 ); + TESTCASE( fprintf( fh, "%s\n", "Dienstag" ) == 9 ); + TESTCASE( fprintf( fh, "%s\n", "Mittwoch" ) == 9 ); + TESTCASE( fprintf( fh, "%s\n", "Donnerstag" ) == 11 ); + TESTCASE( fprintf( fh, "%s\n", "Freitag" ) == 8 ); + TESTCASE( fprintf( fh, "%s\n", "Samstag" ) == 8 ); + + TESTCASE( fprintf( fh, "%s\n", "%a %d %b %Y %T %Z" ) == 18 ); /* date time format (%c) */ + TESTCASE( fprintf( fh, "%s\n", "%I:%M:%S" ) == 9 ); /* 12-hour time format (%r) */ + TESTCASE( fprintf( fh, "%s\n", "%d.%m.%Y" ) == 9 ); /* date format (%x) */ + TESTCASE( fprintf( fh, "%s\n", "%H:%M:%S" ) == 9 ); /* time format (%X) */ + + TESTCASE( fprintf( fh, "%s\n", "" ) == 1 ); /* AM */ + TESTCASE( fprintf( fh, "%s\n", "" ) == 1 ); /* PM */ + fclose( fh ); + TESTCASE( ( lc = _PDCLIB_load_lc_time( "./", "test" ) ) ); + remove( "test_time.dat" ); + + TESTCASE( strcmp( lc->month_name_abbr[ 0 ], "Jan" ) == 0 ); + TESTCASE( strcmp( lc->month_name_abbr[ 11 ], "Dez" ) == 0 ); + TESTCASE( strcmp( lc->month_name_full[ 0 ], "Januar" ) == 0 ); + TESTCASE( strcmp( lc->month_name_full[ 11 ], "Dezember" ) == 0 ); + TESTCASE( strcmp( lc->day_name_abbr[ 0 ], "So" ) == 0 ); + TESTCASE( strcmp( lc->day_name_abbr[ 6 ], "Sa" ) == 0 ); + TESTCASE( strcmp( lc->day_name_full[ 0 ], "Sonntag" ) == 0 ); + TESTCASE( strcmp( lc->day_name_full[ 6 ], "Samstag" ) == 0 ); + +#endif + + return TEST_RESULTS; +} + +#endif diff --git a/src/pdclib/functions/_PDCLIB/_PDCLIB_load_lines.c b/src/pdclib/functions/_PDCLIB/_PDCLIB_load_lines.c new file mode 100644 index 0000000..aaaa743 --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/_PDCLIB_load_lines.c @@ -0,0 +1,81 @@ +/* _PDCLIB_load_lines( FILE *, size_t ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include <stdio.h> +#include <stdlib.h> + +#ifndef REGTEST + +char * _PDCLIB_load_lines( FILE * fh, size_t lines ) +{ + size_t required = 0; + long pos = ftell( fh ); + char * rc = NULL; + int c; + + /* Count the number of characters */ + while ( lines && ( c = fgetc( fh ) ) != EOF ) + { + if ( c == '\n' ) + { + --lines; + } + + ++required; + } + + if ( ! feof( fh ) ) + { + if ( ( rc = malloc( required ) ) != NULL ) + { + size_t i; + + fseek( fh, pos, SEEK_SET ); + fread( rc, 1, required, fh ); + + for ( i = 0; i < required; ++i ) + { + if ( rc[ i ] == '\n' ) + { + rc[ i ] = '\0'; + } + } + } + } + + return rc; +} + +#endif + +#ifdef TEST + +#include "_PDCLIB_test.h" + +int main( void ) +{ +#ifndef REGTEST + FILE * fh = fopen( "test_lines.txt", "w+" ); + char * rc; + + TESTCASE( fh != NULL ); + TESTCASE( fputs( "Foo\n\nBar\n", fh ) != EOF ); + + rewind( fh ); + rc = _PDCLIB_load_lines( fh, 3 ); + fclose( fh ); + remove( "test_lines.txt" ); + + TESTCASE( rc != NULL ); + TESTCASE( strcmp( rc, "Foo" ) == 0 ); + TESTCASE( strcmp( rc + 4, "" ) == 0 ); + TESTCASE( strcmp( rc + 5, "Bar" ) == 0 ); + +#endif + return TEST_RESULTS; +} + +#endif diff --git a/src/pdclib/functions/_PDCLIB/_PDCLIB_prepread.c b/src/pdclib/functions/_PDCLIB/_PDCLIB_prepread.c new file mode 100644 index 0000000..1d60642 --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/_PDCLIB_prepread.c @@ -0,0 +1,50 @@ +/* _PDCLIB_prepread( struct _PDCLIB_file_t * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include <stdio.h> + +#ifndef REGTEST + +#include "pdclib/_PDCLIB_glue.h" + +int _PDCLIB_prepread( struct _PDCLIB_file_t * stream ) +{ + if ( ( stream->bufidx > stream->bufend ) || + ( stream->status & ( _PDCLIB_FWRITE | _PDCLIB_FAPPEND | _PDCLIB_ERRORFLAG | _PDCLIB_WIDESTREAM | _PDCLIB_EOFFLAG ) ) || + ! ( stream->status & ( _PDCLIB_FREAD | _PDCLIB_FRW ) ) ) + { + /* Function called on illegal (e.g. output) stream. + See comments on implementation-defined errno values in + <_PDCLIB_config.h>. + */ + _PDCLIB_errno = _PDCLIB_ERROR; + stream->status |= _PDCLIB_ERRORFLAG; + return EOF; + } + stream->status |= _PDCLIB_FREAD | _PDCLIB_BYTESTREAM; + if ( ( stream->bufidx == stream->bufend ) && ( stream->ungetidx == 0 ) ) + { + return _PDCLIB_fillbuffer( stream ); + } + else + { + return 0; + } +} + +#endif + +#ifdef TEST + +#include "_PDCLIB_test.h" + +int main( void ) +{ + /* Testing covered by ftell.c */ + return TEST_RESULTS; +} + +#endif diff --git a/src/pdclib/functions/_PDCLIB/_PDCLIB_prepwrite.c b/src/pdclib/functions/_PDCLIB/_PDCLIB_prepwrite.c new file mode 100644 index 0000000..dcdc970 --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/_PDCLIB_prepwrite.c @@ -0,0 +1,41 @@ +/* _PDCLIB_prepwrite( struct _PDCLIB_file_t * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include <stdio.h> + +#ifndef REGTEST + +int _PDCLIB_prepwrite( struct _PDCLIB_file_t * stream ) +{ + if ( ( stream->bufidx < stream->bufend ) || ( stream->ungetidx > 0 ) || + ( stream->status & ( _PDCLIB_FREAD | _PDCLIB_ERRORFLAG | _PDCLIB_WIDESTREAM | _PDCLIB_EOFFLAG ) ) || + ! ( stream->status & ( _PDCLIB_FWRITE | _PDCLIB_FAPPEND | _PDCLIB_FRW ) ) ) + { + /* Function called on illegal (e.g. input) stream. + See the comments on implementation-defined errno values in + <_PDCLIB_config.h>. + */ + _PDCLIB_errno = _PDCLIB_ERROR; + stream->status |= _PDCLIB_ERRORFLAG; + return EOF; + } + stream->status |= _PDCLIB_FWRITE | _PDCLIB_BYTESTREAM; + return 0; +} + +#endif + +#ifdef TEST + +#include "_PDCLIB_test.h" + +int main( void ) +{ + /* Testing covered by ftell.c */ + return TEST_RESULTS; +} + +#endif diff --git a/src/pdclib/functions/_PDCLIB/_PDCLIB_print.c b/src/pdclib/functions/_PDCLIB/_PDCLIB_print.c new file mode 100644 index 0000000..3c13da2 --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/_PDCLIB_print.c @@ -0,0 +1,625 @@ +/* _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 <stdio.h> +#include <stdint.h> +#include <stdarg.h> +#include <stdlib.h> +#include <stddef.h> +#include <string.h> + +#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_pointer (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( character, status->stream ); \ + else \ + status->s[status->i] = character; \ + } \ + ++(status->i); \ +} while ( 0 ) + + +static void intformat( intmax_t value, struct _PDCLIB_status_t * status ) +{ + /* At worst, we need two prefix characters (hex prefix). */ + char preface[3] = "\0"; + size_t preidx = 0; + if ( status->prec < 0 ) + { + status->prec = 1; + } + if ( ( status->flags & E_alt ) && ( status->base == 16 || status->base == 8 ) && ( value != 0 ) ) + { + /* 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++ ] = ' '; + } + } + { + /* At this point, status->current has the number of digits queued up. + Determine if we have a precision requirement to pad those. + */ + size_t prec_pads = ( (_PDCLIB_size_t)status->prec > status->current ) ? ( (_PDCLIB_size_t)status->prec - status->current ) : 0; + if ( ! ( status->flags & ( E_minus | E_zero ) ) ) + { + /* Space padding is only done if no zero padding or left alignment + is requested. Calculate the number of characters that WILL be + printed, including 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->current > (_PDCLIB_size_t)status->prec ) ? status->current : (_PDCLIB_size_t)status->prec ); + if ( status->width > characters ) + { + size_t i; + for ( i = 0; i < status->width - characters; ++i ) + { + PUT( ' ' ); + ++(status->current); + } + } + } + /* Now we did the padding, do the prefixes (if any). */ + preidx = 0; + while ( preface[ preidx ] != '\0' ) + { + PUT( preface[ preidx++ ] ); + ++(status->current); + } + /* Do the precision padding if necessary. */ + while ( prec_pads-- > 0 ) + { + PUT( '0' ); + ++(status->current); + } + if ( ( ! ( status->flags & E_minus ) ) && ( status->flags & E_zero ) ) + { + /* If field is not left aligned, and zero padding is requested, do + so. + */ + while ( status->current < status->width ) + { + PUT( '0' ); + ++(status->current); + } + } + } +} + + +/* This function recursively converts a given integer value to a character + stream. The conversion is done under the control of a given status struct + and written either to a character string or a stream, depending on that + same status struct. The status struct also keeps the function from exceeding + snprintf() limits, and enables any necessary padding / prefixing of the + output once the number of characters to be printed is known, which happens + at the lowermost recursion level. +*/ +#define INT2BASE() \ +do \ +{ \ + /* Special case: zero value, zero precision -- no output (but padding) */ \ + if ( status->current == 0 && value == 0 && status->prec == 0 ) \ + { \ + intformat( value, status ); \ + } \ + else \ + { \ + /* 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->current); \ + 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. \ + */ \ + intformat( value, status ); \ + } \ + /* 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...(). */ \ + PUT( _PDCLIB_digits[ digit ] ); \ + } \ + else \ + { \ + /* Uppercase letters. Array only used here, only 0-F. */ \ + PUT( _PDCLIB_Xdigits[ digit ] ); \ + } \ + } \ + } \ +} while ( 0 ) + + +static void int2base( intmax_t value, struct _PDCLIB_status_t * status ) +{ + INT2BASE(); +} + + +static void stringformat( const char * s, struct _PDCLIB_status_t * status ) +{ + if ( status->flags & E_char ) + { + status->prec = 1; + } + else + { + if ( status->prec < 0 ) + { + status->prec = strlen( s ); + } + else + { + int i; + for ( i = 0; i < status->prec; ++i ) + { + if ( s[i] == 0 ) + { + status->prec = i; + break; + } + } + } + } + if ( ! ( status->flags & E_minus ) && ( status->width > (_PDCLIB_size_t)status->prec ) ) + { + while ( status->current < ( status->width - status->prec ) ) + { + PUT( ' ' ); + ++(status->current); + } + } + while ( status->prec > 0 ) + { + PUT( *(s++) ); + --(status->prec); + ++(status->current); + } + if ( status->flags & E_minus ) + { + while ( status->width > status->current ) + { + PUT( ' ' ); + ++(status->current); + } + } +} + + +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 + { + char * endptr; + status->prec = (int)strtol( spec, &endptr, 10 ); + if ( spec == endptr ) + { + /* Decimal point but no number - equals zero */ + status->prec = 0; + } + 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' ) + { + /* 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. */ + { + char c[1]; + c[0] = (char)va_arg( status->arg, int ); + status->flags |= E_char; + stringformat( c, status ); + return ++spec; + } + case 's': + /* TODO: wide chars. */ + stringformat( va_arg( status->arg, char * ), status ); + return ++spec; + case 'p': + status->base = 16; + status->flags |= ( E_lower | E_unsigned | E_alt | E_pointer ); + 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 | E_pointer | E_intmax ) ) + { + 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; + case E_pointer: + value = (uintmax_t)(uintptr_t)va_arg( status->arg, void * ); + break; + case E_intmax: + value = va_arg( status->arg, uintmax_t ); + break; + default: + puts( "UNSUPPORTED PRINTF FLAG COMBINATION" ); + return NULL; + } + INT2BASE(); + } + else + { + intmax_t value; + switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_intmax ) ) + { + case E_char: + value = (intmax_t)(char)va_arg( status->arg, int ); + break; + case E_short: + value = (intmax_t)(short)va_arg( status->arg, int ); + break; + case 0: + value = (intmax_t)va_arg( status->arg, int ); + break; + case E_long: + value = (intmax_t)va_arg( status->arg, long ); + break; + case E_llong: + value = (intmax_t)va_arg( status->arg, long long ); + break; + case E_ptrdiff: + value = (intmax_t)va_arg( status->arg, ptrdiff_t ); + break; + case E_intmax: + value = va_arg( status->arg, intmax_t ); + break; + default: + puts( "UNSUPPORTED PRINTF FLAG COMBINATION" ); + return NULL; + } + INT2BASE(); + } + 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 = EOF; + 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 diff --git a/src/pdclib/functions/_PDCLIB/_PDCLIB_scan.c b/src/pdclib/functions/_PDCLIB/_PDCLIB_scan.c new file mode 100644 index 0000000..1f8fd71 --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/_PDCLIB_scan.c @@ -0,0 +1,639 @@ +/* _PDCLIB_scan( 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 <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <stdint.h> +#include <ctype.h> +#include <string.h> +#include <stddef.h> +#include <limits.h> + +#ifndef REGTEST + +/* Using an integer's bits as flags for both the conversion flags and length + modifiers. +*/ +#define E_suppressed 1<<0 +#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_pointer 1<<13 +#define E_ldouble 1<<14 +#define E_unsigned 1<<16 + + +/* Helper function to get a character from the string or stream, whatever is + used for input. When reading from a string, returns EOF on end-of-string + so that handling of the return value can be uniform for both streams and + strings. +*/ +static int GET( struct _PDCLIB_status_t * status ) +{ + int rc = EOF; + if ( status->stream != NULL ) + { + rc = getc( status->stream ); + } + else + { + rc = ( *status->s == '\0' ) ? EOF : (unsigned char)*((status->s)++); + } + if ( rc != EOF ) + { + ++(status->i); + ++(status->current); + } + return rc; +} + + +/* Helper function to put a read character back into the string or stream, + whatever is used for input. +*/ +static void UNGET( int c, struct _PDCLIB_status_t * status ) +{ + if ( status->stream != NULL ) + { + ungetc( c, status->stream ); /* TODO: Error? */ + } + else + { + --(status->s); + } + --(status->i); + --(status->current); +} + + +/* Helper function to check if a character is part of a given scanset */ +static int IN_SCANSET( const char * scanlist, const char * end_scanlist, int rc ) +{ + /* SOLAR */ + int previous = -1; + while ( scanlist != end_scanlist ) + { + if ( ( *scanlist == '-' ) && ( previous != -1 ) ) + { + /* possible scangroup ("a-z") */ + if ( ++scanlist == end_scanlist ) + { + /* '-' at end of scanlist does not describe a scangroup */ + return rc == '-'; + } + while ( ++previous <= (unsigned char)*scanlist ) + { + if ( previous == rc ) + { + return 1; + } + } + previous = -1; + } + else + { + /* not a scangroup, check verbatim */ + if ( rc == (unsigned char)*scanlist ) + { + return 1; + } + previous = (unsigned char)(*scanlist++); + } + } + return 0; +} + + +const char * _PDCLIB_scan( const char * spec, struct _PDCLIB_status_t * status ) +{ + /* generic input character */ + int rc; + const char * prev_spec; + const char * orig_spec = spec; + int value_parsed; + if ( *(++spec) == '%' ) + { + /* %% -> match single '%' */ + rc = GET( status ); + switch ( rc ) + { + case EOF: + /* input error */ + if ( status->n == 0 ) + { + status->n = -1; + } + return NULL; + case '%': + return ++spec; + default: + UNGET( rc, status ); + break; + } + } + /* Initializing status structure */ + status->flags = 0; + status->base = -1; + status->current = 0; + status->width = 0; + status->prec = 0; + + /* '*' suppresses assigning parsed value to variable */ + if ( *spec == '*' ) + { + status->flags |= E_suppressed; + ++spec; + } + + /* 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. + */ + prev_spec = spec; + status->width = (int)strtol( spec, (char**)&spec, 10 ); + if ( spec == prev_spec ) + { + status->width = SIZE_MAX; + } + + /* 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 + { + /* l -> 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 */ + + /* whether valid input had been parsed */ + value_parsed = 0; + + switch ( *spec ) + { + case 'd': + status->base = 10; + break; + case 'i': + status->base = 0; + 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_unsigned; + break; + case 'f': + case 'F': + case 'e': + case 'E': + case 'g': + case 'G': + case 'a': + case 'A': + break; + case 'c': + { + char * c = va_arg( status->arg, char * ); + /* for %c, default width is one */ + if ( status->width == SIZE_MAX ) + { + status->width = 1; + } + /* reading until width reached or input exhausted */ + while ( ( status->current < status->width ) && + ( ( rc = GET( status ) ) != EOF ) ) + { + *(c++) = rc; + value_parsed = 1; + } + /* width or input exhausted */ + if ( value_parsed ) + { + ++status->n; + return ++spec; + } + else + { + /* input error, no character read */ + if ( status->n == 0 ) + { + status->n = -1; + } + return NULL; + } + } + case 's': + { + char * c = va_arg( status->arg, char * ); + while ( ( status->current < status->width ) && + ( ( rc = GET( status ) ) != EOF ) ) + { + if ( isspace( rc ) ) + { + UNGET( rc, status ); + if ( value_parsed ) + { + /* matching sequence terminated by whitespace */ + *c = '\0'; + ++status->n; + return ++spec; + } + else + { + /* matching error */ + return NULL; + } + } + else + { + /* match */ + value_parsed = 1; + *(c++) = rc; + } + } + /* width or input exhausted */ + if ( value_parsed ) + { + *c = '\0'; + ++status->n; + return ++spec; + } + else + { + /* input error, no character read */ + if ( status->n == 0 ) + { + status->n = -1; + } + return NULL; + } + } + case '[': + { + const char * endspec = spec; + int negative_scanlist = 0; + char * c; + if ( *(++endspec) == '^' ) + { + negative_scanlist = 1; + ++endspec; + } + spec = endspec; + do + { + /* TODO: This can run beyond a malformed format string */ + ++endspec; + } while ( *endspec != ']' ); + /* read according to scanlist, equiv. to %s above */ + c = va_arg( status->arg, char * ); + while ( ( status->current < status->width ) && + ( ( rc = GET( status ) ) != EOF ) ) + { + if ( negative_scanlist ) + { + if ( IN_SCANSET( spec, endspec, rc ) ) + { + UNGET( rc, status ); + break; + } + } + else + { + if ( ! IN_SCANSET( spec, endspec, rc ) ) + { + UNGET( rc, status ); + break; + } + } + value_parsed = 1; + *(c++) = rc; + } + if ( value_parsed ) + { + *c = '\0'; + ++status->n; + return ++endspec; + } + else + { + if ( rc == EOF ) + { + status->n = -1; + } + return NULL; + } + } + case 'p': + status->base = 16; + status->flags |= E_pointer; + break; + case 'n': + { + int * val = va_arg( status->arg, int * ); + *val = status->i; + return ++spec; + } + default: + /* No conversion specifier. Bad conversion. */ + return orig_spec; + } + + if ( status->base != -1 ) + { + /* integer conversion */ + uintmax_t value = 0; /* absolute value read */ + int prefix_parsed = 0; + int sign = 0; + while ( ( status->current < status->width ) && + ( ( rc = GET( status ) ) != EOF ) ) + { + if ( isspace( rc ) ) + { + if ( sign ) + { + /* matching sequence terminated by whitespace */ + UNGET( rc, status ); + break; + } + else + { + /* leading whitespace not counted against width */ + status->current--; + } + } + else if ( ! sign ) + { + /* no sign parsed yet */ + switch ( rc ) + { + case '-': + sign = -1; + break; + case '+': + sign = 1; + break; + default: + /* not a sign; put back character */ + sign = 1; + UNGET( rc, status ); + break; + } + } + else if ( ! prefix_parsed ) + { + /* no prefix (0x... for hex, 0... for octal) parsed yet */ + prefix_parsed = 1; + if ( rc != '0' ) + { + /* not a prefix; if base not yet set, set to decimal */ + if ( status->base == 0 ) + { + status->base = 10; + } + UNGET( rc, status ); + } + else + { + /* starts with zero, so it might be a prefix. */ + /* check what follows next (might be 0x...) */ + if ( ( status->current < status->width ) && + ( ( rc = GET( status ) ) != EOF ) ) + { + if ( tolower( rc ) == 'x' ) + { + /* 0x... would be prefix for hex base... */ + if ( ( status->base == 0 ) || + ( status->base == 16 ) ) + { + status->base = 16; + } + else + { + /* ...unless already set to other value */ + UNGET( rc, status ); + value_parsed = 1; + } + } + else + { + /* 0... but not 0x.... would be octal prefix */ + UNGET( rc, status ); + if ( status->base == 0 ) + { + status->base = 8; + } + /* in any case we have read a zero */ + value_parsed = 1; + } + } + else + { + /* failed to read beyond the initial zero */ + value_parsed = 1; + break; + } + } + } + else + { + char * digitptr = memchr( _PDCLIB_digits, tolower( rc ), status->base ); + if ( digitptr == NULL ) + { + /* end of input item */ + UNGET( rc, status ); + break; + } + value *= status->base; + value += digitptr - _PDCLIB_digits; + value_parsed = 1; + } + } + /* width or input exhausted, or non-matching character */ + if ( ! value_parsed ) + { + /* out of input before anything could be parsed - input error */ + /* FIXME: if first character does not match, value_parsed is not set - but it is NOT an input error */ + if ( ( status->n == 0 ) && ( rc == EOF ) ) + { + status->n = -1; + } + return NULL; + } + /* convert value to target type and assign to parameter */ + if ( ! ( status->flags & E_suppressed ) ) + { + switch ( status->flags & ( E_char | E_short | E_long | E_llong | + E_intmax | E_size | E_ptrdiff | E_pointer | + E_unsigned ) ) + { + case E_char: + *( va_arg( status->arg, char * ) ) = (char)( value * sign ); + break; + case E_char | E_unsigned: + *( va_arg( status->arg, unsigned char * ) ) = (unsigned char)( value * sign ); + break; + + case E_short: + *( va_arg( status->arg, short * ) ) = (short)( value * sign ); + break; + case E_short | E_unsigned: + *( va_arg( status->arg, unsigned short * ) ) = (unsigned short)( value * sign ); + break; + + case 0: + *( va_arg( status->arg, int * ) ) = (int)( value * sign ); + break; + case E_unsigned: + *( va_arg( status->arg, unsigned int * ) ) = (unsigned int)( value * sign ); + break; + + case E_long: + *( va_arg( status->arg, long * ) ) = (long)( value * sign ); + break; + case E_long | E_unsigned: + *( va_arg( status->arg, unsigned long * ) ) = (unsigned long)( value * sign ); + break; + + case E_llong: + *( va_arg( status->arg, long long * ) ) = (long long)( value * sign ); + break; + case E_llong | E_unsigned: + *( va_arg( status->arg, unsigned long long * ) ) = (unsigned long long)( value * sign ); + break; + + case E_intmax: + *( va_arg( status->arg, intmax_t * ) ) = (intmax_t)( value * sign ); + break; + case E_intmax | E_unsigned: + *( va_arg( status->arg, uintmax_t * ) ) = (uintmax_t)( value * sign ); + break; + + case E_size: + /* E_size always implies unsigned */ + *( va_arg( status->arg, size_t * ) ) = (size_t)( value * sign ); + break; + + case E_ptrdiff: + /* E_ptrdiff always implies signed */ + *( va_arg( status->arg, ptrdiff_t * ) ) = (ptrdiff_t)( value * sign ); + break; + + case E_pointer: + /* E_pointer always implies unsigned */ + *( uintptr_t* )( va_arg( status->arg, void * ) ) = (uintptr_t)( value * sign ); + break; + + default: + puts( "UNSUPPORTED SCANF FLAG COMBINATION" ); + return NULL; /* behaviour unspecified */ + } + ++(status->n); + } + return ++spec; + } + /* TODO: Floats. */ + return NULL; +} + +#endif + +#ifdef TEST +#define _PDCLIB_FILEID "_PDCLIB/scan.c" +#define _PDCLIB_STRINGIO + +#include "_PDCLIB_test.h" + +#ifndef REGTEST + +static int testscanf( const char * s, const char * format, ... ) +{ + struct _PDCLIB_status_t status; + status.n = 0; + status.i = 0; + status.s = (char *)s; + status.stream = NULL; + va_start( status.arg, format ); + if ( *(_PDCLIB_scan( format, &status )) != '\0' ) + { + printf( "_PDCLIB_scan() did not return end-of-specifier on '%s'.\n", format ); + ++TEST_RESULTS; + } + va_end( status.arg ); + return status.n; +} + +#endif + +#define TEST_CONVERSION_ONLY + +int main( void ) +{ +#ifndef REGTEST + char source[100]; +#include "scanf_testcases.h" +#endif + return TEST_RESULTS; +} + +#endif diff --git a/src/pdclib/functions/_PDCLIB/_PDCLIB_seed.c b/src/pdclib/functions/_PDCLIB/_PDCLIB_seed.c new file mode 100644 index 0000000..ce7c31c --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/_PDCLIB_seed.c @@ -0,0 +1,19 @@ +/* _PDCLIB_seed + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +unsigned long int _PDCLIB_seed = 1; + +#ifdef TEST + +#include "_PDCLIB_test.h" + +int main( void ) +{ + /* no tests for raw data */ + return TEST_RESULTS; +} + +#endif diff --git a/src/pdclib/functions/_PDCLIB/_PDCLIB_strtox_main.c b/src/pdclib/functions/_PDCLIB/_PDCLIB_strtox_main.c new file mode 100644 index 0000000..4808952 --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/_PDCLIB_strtox_main.c @@ -0,0 +1,88 @@ +/* _PDCLIB_strtox_main( const char * *, int, _PDCLIB_uintmax_t, _PDCLIB_uintmax_t, int ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include <ctype.h> +#include <errno.h> +#include <string.h> +#include <stdint.h> + +#ifndef REGTEST + +_PDCLIB_uintmax_t _PDCLIB_strtox_main( const char ** p, unsigned int base, uintmax_t error, uintmax_t limval, int limdigit, char * sign ) +{ + _PDCLIB_uintmax_t rc = 0; + int digit = -1; + const char * x; + while ( ( x = memchr( _PDCLIB_digits, tolower(**p), base ) ) != NULL ) + { + digit = x - _PDCLIB_digits; + if ( ( rc < limval ) || ( ( rc == limval ) && ( digit <= limdigit ) ) ) + { + rc = rc * base + (unsigned)digit; + ++(*p); + } + else + { + errno = ERANGE; + /* TODO: Only if endptr != NULL - but do we really want *another* parameter? */ + /* TODO: Earlier version was missing tolower() here but was not caught by tests */ + while ( memchr( _PDCLIB_digits, tolower(**p), base ) != NULL ) ++(*p); + /* TODO: This is ugly, but keeps caller from negating the error value */ + *sign = '+'; + return error; + } + } + if ( digit == -1 ) + { + *p = NULL; + return 0; + } + return rc; +} + +#endif + +#ifdef TEST + +#include "_PDCLIB_test.h" + +#include <errno.h> + +int main( void ) +{ +#ifndef REGTEST + const char * p; + char test[] = "123_"; + char fail[] = "xxx"; + char sign = '-'; + /* basic functionality */ + p = test; + errno = 0; + TESTCASE( _PDCLIB_strtox_main( &p, 10u, (uintmax_t)999, (uintmax_t)12, 3, &sign ) == 123 ); + TESTCASE( errno == 0 ); + TESTCASE( p == &test[3] ); + /* proper functioning to smaller base */ + p = test; + TESTCASE( _PDCLIB_strtox_main( &p, 8u, (uintmax_t)999, (uintmax_t)12, 3, &sign ) == 0123 ); + TESTCASE( errno == 0 ); + TESTCASE( p == &test[3] ); + /* overflowing subject sequence must still return proper endptr */ + p = test; + TESTCASE( _PDCLIB_strtox_main( &p, 4u, (uintmax_t)999, (uintmax_t)1, 2, &sign ) == 999 ); + TESTCASE( errno == ERANGE ); + TESTCASE( p == &test[3] ); + TESTCASE( sign == '+' ); + /* testing conversion failure */ + errno = 0; + p = fail; + sign = '-'; + TESTCASE( _PDCLIB_strtox_main( &p, 10u, (uintmax_t)999, (uintmax_t)99, 8, &sign ) == 0 ); + TESTCASE( p == NULL ); +#endif + return TEST_RESULTS; +} + +#endif diff --git a/src/pdclib/functions/_PDCLIB/_PDCLIB_strtox_prelim.c b/src/pdclib/functions/_PDCLIB/_PDCLIB_strtox_prelim.c new file mode 100644 index 0000000..6e16b35 --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/_PDCLIB_strtox_prelim.c @@ -0,0 +1,94 @@ +/* _PDCLIB_strtox_prelim( const char *, char *, int * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include <ctype.h> +#include <stddef.h> +#include <string.h> + +#ifndef REGTEST + +const char * _PDCLIB_strtox_prelim( const char * p, char * sign, int * base ) +{ + /* skipping leading whitespace */ + while ( isspace( *p ) ) ++p; + /* determining / skipping sign */ + if ( *p != '+' && *p != '-' ) *sign = '+'; + else *sign = *(p++); + /* determining base */ + if ( *p == '0' ) + { + ++p; + if ( ( *base == 0 || *base == 16 ) && ( *p == 'x' || *p == 'X' ) ) + { + *base = 16; + ++p; + /* catching a border case here: "0x" followed by a non-digit should + be parsed as the unprefixed zero. + We have to "rewind" the parsing; having the base set to 16 if it + was zero previously does not hurt, as the result is zero anyway. + */ + if ( memchr( _PDCLIB_digits, tolower(*p), *base ) == NULL ) + { + p -= 2; + } + } + else if ( *base == 0 ) + { + *base = 8; + } + else + { + --p; + } + } + else if ( ! *base ) + { + *base = 10; + } + return ( ( *base >= 2 ) && ( *base <= 36 ) ) ? p : NULL; +} + +#endif + +#ifdef TEST + +#include "_PDCLIB_test.h" + +int main( void ) +{ +#ifndef REGTEST + int base = 0; + char sign = '\0'; + char test1[] = " 123"; + char test2[] = "\t+0123"; + char test3[] = "\v-0x123"; + TESTCASE( _PDCLIB_strtox_prelim( test1, &sign, &base ) == &test1[2] ); + TESTCASE( sign == '+' ); + TESTCASE( base == 10 ); + base = 0; + sign = '\0'; + TESTCASE( _PDCLIB_strtox_prelim( test2, &sign, &base ) == &test2[3] ); + TESTCASE( sign == '+' ); + TESTCASE( base == 8 ); + base = 0; + sign = '\0'; + TESTCASE( _PDCLIB_strtox_prelim( test3, &sign, &base ) == &test3[4] ); + TESTCASE( sign == '-' ); + TESTCASE( base == 16 ); + base = 10; + sign = '\0'; + TESTCASE( _PDCLIB_strtox_prelim( test3, &sign, &base ) == &test3[2] ); + TESTCASE( sign == '-' ); + TESTCASE( base == 10 ); + base = 1; + TESTCASE( _PDCLIB_strtox_prelim( test3, &sign, &base ) == NULL ); + base = 37; + TESTCASE( _PDCLIB_strtox_prelim( test3, &sign, &base ) == NULL ); +#endif + return TEST_RESULTS; +} + +#endif diff --git a/src/pdclib/functions/_PDCLIB/assert.c b/src/pdclib/functions/_PDCLIB/assert.c new file mode 100644 index 0000000..f84265f --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/assert.c @@ -0,0 +1,71 @@ +/* _PDCLIB_assert( const char * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> + +#ifndef REGTEST + +#include "pdclib/_PDCLIB_aux.h" + +void _PDCLIB_assert99( const char * const message1, const char * const function, const char * const message2 ) +{ + fputs( message1, stderr ); + fputs( function, stderr ); + fputs( message2, stderr ); + abort(); +} + +void _PDCLIB_assert89( const char * const message ) +{ + fputs( message, stderr ); + abort(); +} + +#endif + +#ifdef TEST + +#include "_PDCLIB_test.h" + +#include <signal.h> + +static int EXPECTED_ABORT = 0; +static int UNEXPECTED_ABORT = 1; + +static void aborthandler( int sig ) +{ + TESTCASE( ! EXPECTED_ABORT ); + exit( (signed int)TEST_RESULTS ); +} + +#define NDEBUG + +#include <assert.h> + +static int disabled_test( void ) +{ + int i = 0; + assert( i == 0 ); /* NDEBUG set, condition met */ + assert( i == 1 ); /* NDEBUG set, condition fails */ + return i; +} + +#undef NDEBUG + +#include <assert.h> + +int main( void ) +{ + TESTCASE( signal( SIGABRT, &aborthandler ) != SIG_ERR ); + TESTCASE( disabled_test() == 0 ); + assert( UNEXPECTED_ABORT ); /* NDEBUG not set, condition met */ + assert( EXPECTED_ABORT ); /* NDEBUG not set, condition fails - should abort */ + return TEST_RESULTS; +} + +#endif diff --git a/src/pdclib/functions/_PDCLIB/errno.c b/src/pdclib/functions/_PDCLIB/errno.c new file mode 100644 index 0000000..13270fc --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/errno.c @@ -0,0 +1,37 @@ +/* _PDCLIB_errno + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#ifndef REGTEST + +#include "pdclib/_PDCLIB_int.h" + +int _PDCLIB_errno = 0; + +int * _PDCLIB_errno_func() +{ + return &_PDCLIB_errno; +} + +#endif + +#ifdef TEST + +#include "_PDCLIB_test.h" + +#include <errno.h> + +int main( void ) +{ + errno = 0; + TESTCASE( errno == 0 ); + errno = EDOM; + TESTCASE( errno == EDOM ); + errno = ERANGE; + TESTCASE( errno == ERANGE ); + return TEST_RESULTS; +} + +#endif diff --git a/src/pdclib/functions/_PDCLIB/stdarg.c b/src/pdclib/functions/_PDCLIB/stdarg.c new file mode 100644 index 0000000..7ad1087 --- /dev/null +++ b/src/pdclib/functions/_PDCLIB/stdarg.c @@ -0,0 +1,115 @@ +/* stdarg + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include <stdarg.h> +#include <limits.h> +#include <float.h> + +#ifdef TEST + +#include "_PDCLIB_test.h" + +typedef int (*intfunc_t)( void ); + +enum tag_t +{ + TAG_END, + TAG_INT, + TAG_LONG, + TAG_LLONG, + TAG_DBL, + TAG_LDBL, + TAG_INTPTR, + TAG_LDBLPTR, + TAG_FUNCPTR +}; + +static int dummy( void ) +{ + return INT_MAX; +} + +static int test( enum tag_t s, ... ) +{ + enum tag_t tag = s; + va_list ap; + va_start( ap, s ); + for (;;) + { + switch ( tag ) + { + case TAG_INT: + { + TESTCASE( va_arg( ap, int ) == INT_MAX ); + tag = va_arg( ap, enum tag_t ); + break; + } + case TAG_LONG: + { + TESTCASE( va_arg( ap, long ) == LONG_MAX ); + tag = va_arg( ap, enum tag_t ); + break; + } + case TAG_LLONG: + { + TESTCASE( va_arg( ap, long long ) == LLONG_MAX ); + tag = va_arg( ap, enum tag_t ); + break; + } + case TAG_DBL: + { + TESTCASE( va_arg( ap, double ) == DBL_MAX ); + tag = va_arg( ap, enum tag_t ); + break; + } + case TAG_LDBL: + { + TESTCASE( va_arg( ap, long double ) == LDBL_MAX ); + tag = va_arg( ap, enum tag_t ); + break; + } + case TAG_INTPTR: + { + TESTCASE( *( va_arg( ap, int * ) ) == INT_MAX ); + tag = va_arg( ap, enum tag_t ); + break; + } + case TAG_LDBLPTR: + { + TESTCASE( *( va_arg( ap, long double * ) ) == LDBL_MAX ); + tag = va_arg( ap, enum tag_t ); + break; + } + case TAG_FUNCPTR: + { + intfunc_t function; + TESTCASE( ( function = va_arg( ap, intfunc_t ) ) == dummy ); + TESTCASE( function() == INT_MAX ); + tag = va_arg( ap, enum tag_t ); + break; + } + case TAG_END: + { + va_end( ap ); + return 0; + } + } + } +} + +int main( void ) +{ + int x = INT_MAX; + long double d = LDBL_MAX; + test( TAG_END ); + test( TAG_INT, INT_MAX, TAG_END ); + test( TAG_LONG, LONG_MAX, TAG_LLONG, LLONG_MAX, TAG_END ); + test( TAG_DBL, DBL_MAX, TAG_LDBL, LDBL_MAX, TAG_END ); + test( TAG_INTPTR, &x, TAG_LDBLPTR, &d, TAG_FUNCPTR, dummy, TAG_END ); + return TEST_RESULTS; +} + +#endif |