I'm taking a break from programming this function, figured I would post it here in the mean time for anyone curious enough to look for problems in it:
Code:
dint specidy_modifier( dint c )
{
switch ( c )
{
case 't': /* ptrdiff_t */
case 'z': /* size_t */
case 'j': /* intmax_t/uintmax_t/dmax/umax */
case 'p': /* intptr_t/uintptr_t/dptr/uptr */
case 'I': /* int8_t etc */
case 'L': /* ldnum */
case 'l': /* long/ulong/wchs/wch */
case 'h': /* char/short */
return c;
}
return 0;
}
dint specify_integer( dint c )
{
switch ( c )
{
case 'd': case 'i': /* signed decimal */
return 'd';
case 'n': /* pointer to integer */
case 'b': /* unsigned binary */
case 'o': /* unsigned octal */
case 'u': /* unsigned decimal */
case 'x': /* unsigned lower case hexedecimal */
case 'X': /* unsigned upper case hexadecimal */
return c;
}
return 0;
}
dint specify_double( dint c )
{
switch ( c )
{
case 'f': case 'F':
case 'g': case 'G':
case 'e': case 'E':
case 'a': case 'A':
return c;
}
return 0;
}
dint specify_text( dint c )
{
switch ( c )
{
case 'c': /* single character */
case 's': /* string */
}
return 0;
}
typedef struct _ARG
{
uint i;
bool addr;
uint opts;
/* Prefix length */
uint pfxl;
/* Precision */
uint prec;
/* Min Count */
uint minc;
/* Specified type: signed, unsigned, floating point, string, etc */
dint spec;
/* Variant of type: char, short, long, long long, etc*/
dint type;
dint const *text;
} ARG;
typedef enum _FORMAT_FLAG
{
FORMAT_FLAG_ALIGN_LEFT = 0,
FORMAT_FLAG_BOTH_SIGNS,
FORMAT_FLAG_PLUS2SPACE,
FORMAT_FLAG_PREFIX_LIT,
FORMAT_FLAG_SUFFIX_LIT,
FORMAT_FLAG_PAD_WITH_0,
FORMAT_FLAG_COUNT,
FORMAT_FLAG_DECIMALDIG = FORMAT_FLAG_COUNT
} FORMAT_FLAG;
BASIC_EXP void optsofArg( ARG *arg )
{
static dint const opts[FORMAT_FLAG_COUNT] = { '-', '+', ' ', '#', '~', '0' };
uint i;
bool opt;
arg->opts = 0;
for ( i = 0; FORMAT_FLAG_COUNT; ++i )
{
opt = (arg->text[0] == opts[i]);
arg->opts |= opt << i;
arg->text += opt;
arg->i += opt;
}
}
BASIC_EXP bool mincofArg( ARG *arg )
{
arg->minc = 0;
if ( arg->text[0] == '*' )
{
arg->text++;
arg->i++;
return true;
}
if ( isdigit( arg->text[0] ) )
{
uint i;
for ( i = 0; i < bitsof(uint); ++i )
{
if ( !isdigit( arg->text[0] ) )
break;
arg->minc *= 10;
arg->minc += (arg->text[0] - '0');
arg->text++;
arg->i++;
}
}
return false;
}
BASIC_EXP bool precofArg( ARG *arg )
{
arg->prec = 0;
if ( arg->text[0] == '.' )
{
uint i;
arg->i++;
arg->text++;
if ( arg->text[0] == '*' )
{
arg->i++;
arg->text++;
arg->opts |= (1u << FORMAT_FLAG_DECIMALDIG);
return true;
}
for ( i = 0; i < bitsof(uint); ++i )
{
if ( !isdigit( arg->text[0] ) )
break;
arg->prec *= 10;
arg->prec += (arg->text[0] - '0');
arg->text++;
arg->i++;
}
arg->opts |= (!!i << FORMAT_FLAG_DECIMALDIG);
}
return false;
}
BASIC_EXP void typeofArg( ARG *arg )
{
dint type;
arg->type = specidy_modifier( arg->text[0] );
switch ( arg->type )
{
case 'p':
arg->spec = specify_integer( arg->text[1] );
if ( !(arg->spec) )
{
arg->opts |= (1u << FORMAT_FLAG_PREFIX_LIT);
arg->spec = 'X';
arg->type = 0;
return;
}
break;
case 'L':
arg->spec = specify_double( arg->text[1] );
if ( !(arg->spec) )
arg->spec = 'g';
else
{
arg->text++;
arg->i++;
}
return;
default: if ( !(arg->type) ) return;
}
type = specidy_modifier( arg->text[1] );
if ( type )
{
if ( type != arg->type )
{
arg->spec = 'd';
return;
}
switch ( type )
{
case 'l':
arg->spec = 'L';
arg->text++;
arg->i++;
break;
case 'h':
arg->spec = 'H';
arg->text++;
arg->i++;
break;
default:
arg->spec = 'd';
return;
}
}
arg->spec = specify_integer( arg->text[1] );
if ( arg->spec )
{
arg->i++;
arg->text++;
return;
}
arg->spec = specify_text( arg->text[1] );
if ( arg->spec )
{
arg->i++;
arg->text++;
return;
}
arg->spec = 'd';
}
/* Extend length */
BASIC_EXP void extlenArg( ARG *arg )
{
arg->pfxl = 0;
arg->sfxl = 0;
if ( specify_text( arg->spec ) )
{
arg->pfxl = 1 + (arg->type == 'l');
arg->sfxl = 1;
return;
}
switch ( arg->spec )
{
case 'b': case 'x': case 'X': case 'a' case 'A':
arg->pfxl = 2;
break;
case 'o':
arg->pfxl = 1;
break;
case 'u':
arg->sfxl = 1;
break;
}
switch ( arg->type )
{
case 'h': case 'l': arg->sfxl += 1; break;
case 'H': case 'L': arg->sfxl += 2; break;
}
}
BASIC_EXP uint addpfx( DINTS *Text, ARG *arg, uint j )
{
dint *text = Text->array;
if ( arg->pfxl )
{
switch ( arg->spec )
{
case 'o': text[j++] = '0'; break;
case 'b': text[j++] = '0'; text[j++] = 'b'; break;
case 'x': case 'a': text[j++] = '0'; text[j++] = 'x'; break;
case 'X': case 'A': text[j++] = '0'; text[j++] = 'X'; break;
case 'c':
text[j] = 'L';
j += (arg->type == 'l');
text[j++] = '\'';
break;
case 's':
text[j] = 'L';
j += (arg->type == 'l');
text[j++] = '"';
}
}
return j;
}
BASIC_EXP uint addsfx( DINTS *Text, ARG *arg, uint j )
{
dint *text = Text->array;
if ( arg->sfxl )
{
switch ( arg->spec )
{
case 'u': text[j++] = 'U'; break;
case 'c': text[j++] = '\''; return j;
case 's': text[j++] = '"'; return j;
}
if ( specify_double( arg->spec ) )
return j;
switch ( arg->type )
{
case 'L': text[j++] = 'L';
case 'l': text[j++] = 'L'; break;
case 'H': text[j++] = 'H';
case 'h': text[j++] = 'H'; break;
case 'j': text[j++] = 'J'; break;
case 'p': text[j++] = 'P'; break;
case 'z': text[j++] = 'Z'; break;
case 't': text[j++] = 'T';
}
}
return j;
}
Code:
BASIC_EXP dint markTextv( void *ud, DINTS *Text, dint const *args, va_list va )
{
dint err = 0;
dint *text = NULL;
uint i, j, k, init = 0, term = 0, prec, not0;
ACHN num = {0};
ACHF fpn = {0};
dmax d; umax u;
achs str;
wchs wcs;
ARG arg = {0};
/* Get a rough total number of potential characters */
for ( i = 0, j = 0; args[i]; ++i, ++j )
j += bitsof(umax) * (args[i] == '%');
/* Reduce the number of allocations needed to print text */
err = markTextt( ud, Text, j );
if ( err )
{
ECHO_ERRNO( stdout, err );
return err;
}
/* Actually process the arguments this time */
for ( arg.text = args; args[arg.i]; arg.i++, ++term )
{
if ( arg.text[arg.i] != '%' )
continue;
if ( term )
{
err = growTextn( ud, Text, args + init, term - init );
if ( err )
{
ECHO_ERRNO( stdout, err );
return err;
}
init = i;
term = i;
}
arg.i++;
arg.text++;
optsofArg( &arg );
if ( mincofArg( &arg ) )
arg.minc = va_arg( va, uint );
if ( precofArg(&arg) )
arg.prec = va_arg( va, uint );
typeofArg(&arg);
extlenArg(&arg);
if ( !(arg->opts & (1u << FORMAT_FLAG_PREFIX_LIT)) )
{
arg->pfxl = 0;
if ( specify_text( arg->spec ) )
arg->sfxl = 0;
}
d = 0;
u = 0;
str = NULL;
wcs = NULL;
memset( &num, 0, sizeof(ACHN) );
memset( &fpn, 0, sizeof(ACHN) );
switch ( arg.spec )
{
case 'X': case 'A': case 'p': str = ACHS_0TO9 "ABCDEF"; break;
case 'x': case 'a': str = ACHS_0TO9 "abcdef"; break;
case 'o': str = "01234567"; break;
case 'b': str = "01"; break;
default: str = ACHS_0TO9; break;
}
if ( specify_double( arg.spec ) )
{
j = Text->count;
ldnum2achs
(
&fpn, (arg.type == 'L')
? va_arg( va, ldnum ) : va_arg( va, dnum ),
str
);
prec = (arg->opts & (1u << FORMAT_FLAG_DECIMALDIG))
? arg->prec : fpn.dec.not0;
k = fpn.exp.not0;
not0 = fpn.num.not0 + prec + k + !!prec + !!k;
not0 += arg->pfxl;
not0 = (not0 < arg->minc) ? arg->minc : not0;
err = growTextc( ud, Text, not0 );
if ( err )
{
ECHO_ERRNO( stdout, err );
return err;
}
text = Text->array;
j = addpfx( Text, &arg, j );
for ( k = 0; k < fpn.num.not0; ++k )
text[j] = fpn.num.text[k];
if ( prec < fpn.dec.not0 )
{
for ( k = 0; k < prec; ++k )
text[j] = fpn.dec.text[k];
}
else if ( prec )
{
/* TODO take note of 0s prior to readable
* number */
for ( k = 0; k < fpn.dec.not0; ++k, --prec )
text[j] = fpn.dec.text[k];
for ( ; prec; text[j] = '0', --prec );
}
for ( k = 0; k < fpn.num.not0; ++k )
text[j] = fpn.num.text[k];
continue;
}
switch ( arg.spec )
{
case 'c':
d = va_arg( va, dint );
j = Text->count;
err = growTextc( ud, Text, 1 );
if ( err )
{
ECHO_ERRNO( stdout, err );
return err;
}
text = Text->array;
text[j] = d;
continue;
case 's':
not0 = arg->prec;
if ( arg.type == 'l' )
{
wcs = va_arg( va, wchs );
if ( wcs )
not0 = not0 ? wchsnot0(wcs) : 0;
else
{
wcs = L"(null)";
not0 = wchsnot0(wcs);
}
j = Text->count;
err = growTextc( ud, Text, not0 + (6 * !not0) );
if ( err )
{
ECHO_ERRNO( stdout, err );
return err;
}
text = Text->array;
for ( k = 0; k < not0; ++k, ++j )
text[j] = wcs[k];
}
else
{
str = va_arg( va, achs );
if ( str )
not0 = not0 ? achsnot0(str) : 0;
else
{
str = "(null)";
not0 = achsnot0(str);
}
j = Text->count;
err = growTextc( ud, Text, not0 );
if ( err )
{
ECHO_ERRNO( stdout, err );
return err;
}
text = Text->array;
for ( k = 0; k < not0; ++k, ++j )
text[j] = str[k];
}
continue;
case 'p':
{
ptr pointer = va_arg( va, void* );
j = Text->count;
umax2achs( &num, (uptr)pointer, str );
err = growTextc( ud, Text, num.not0 + 1 );
if ( err )
{
ECHO_ERRNO( stdout, err );
return err;
}
text = Text->array;
if ( arg.opts && (1u << FORMAT_FLAG_BOTH_SIGNS) )
text[j++] = '+';
else if ( arg.opts && (1u << FORMAT_FLAG_PLUS2SPACE) )
text[j++] = ' ';
for ( k = 0; k < num.not0; ++k, ++j )
text[j] = num.text[k];
continue;
}
case 'd':
switch ( arg.type )
{
case 't': d = va_arg( va, ptrdiff_t ); break;
case 'z': d = va_arg( va, size_t ); break;
case 'p': d = va_arg( va, dptr ); break;
case 'j': d = va_arg( va, dmax ); break;
case 'l': d = va_arg( va, dlong ); break;
default: d = va_arg( va, dint ); break;
}
j = Text->count;
dmax2achs( &num, d, str );
err = growTextc( ud, Text, num.not0 + 1 );
if ( err )
{
ECHO_ERRNO( stdout, err );
return err;
}
text = Text->array;
if ( num.sign[0] == '-' )
text[j++] = '-';
else if ( arg.opts && (1u << FORMAT_FLAG_BOTH_SIGNS) )
text[j++] = '+';
else if ( arg.opts && (1u << FORMAT_FLAG_PLUS2SPACE) )
text[j++] = ' ';
for ( k = 0; k < num.not0; ++k, ++j )
text[j] = num.text[k];
continue;
default:
switch ( arg.type )
{
case 't': u = va_arg( va, ptrdiff_t ); break;
case 'z': u = va_arg( va, size_t ); break;
case 'p': u = va_arg( va, uptr ); break;
case 'j': u = va_arg( va, umax ); break;
case 'l': u = va_arg( va, ulong ); break;
default: u = va_arg( va, uint ); break;
}
j = Text->count;
umax2achs( &num, u, str );
err = growTextc( ud, Text, num.not0 + 1 );
if ( err )
{
ECHO_ERRNO( stdout, err );
return err;
}
text = Text->array;
if ( arg.opts && (1u << FORMAT_FLAG_BOTH_SIGNS) )
text[j++] = '+';
else if ( arg.opts && (1u << FORMAT_FLAG_PLUS2SPACE) )
text[j++] = ' ';
for ( k = 0; k < num.not0; ++k, ++j )
text[j] = num.text[k];
break;
}
}
return 0;
}
dint is just a typedef for signed int, DINTS is just a typedef to my abstract buffer handling object BUFFER, as for ACHN/ACHF:
Code:
typedef struct _ACHN
{
uint not0;
ach sign[2];
ach text[bitsof(umax)];
} ACHN;
typedef struct _ACHF { ACHN num, dec, exp; } ACHF;
typedef union _ACHV { ACHF n; ACHN i[3]; } ACHV;
...
BASIC void umax2achs( ACHN *dst, umax val, achs base )
{
ach tmp[bitsof(umax)] = {0};
uint i = 0, j = 0, max = achsnot0( base );
memset( dst, 0, sizeof(ACHN) );
if ( !max )
return;
while ( val )
{
tmp[j++] = base[val % max];
val /= max;
}
dst->not0 = j;
while ( j )
dst->text[i++] = tmp[--j];
}
BASIC void dmax2achs( ACHN *dst, dmax val, achs base )
{
if ( val < 0 )
{
umax2achs( dst, -val, base );
dst->sign[0] = '-';
return;
}
umax2achs( dst, val, base );
}
BASIC void ldnum2achs( ACHF *dst, ldnum val, achs base )
{
dint exp = 0;
ldnum full = 0, part = 0;
uint max = achsnot0( base );
if ( !max )
{
memset( dst, 0, sizeof(ACHF) );
return;
}
frexpl( val, &exp );
if ( exp > LDBL_MAX_10_EXP )
{
if ( val < 0 )
{
while ( -val > max )
val /= max;
}
else
{
while ( val > max )
val /= max;
}
}
if ( val > 0u )
{
full = floorl( val );
part = val - full;
}
else
{
full = ceill(val);
part = -(val + full);
}
while ( floorl(part) != part )
part *= max;
dmax2achs( &(dst->num), (dmax)full, base );
umax2achs( &(dst->dec), (umax)part, base );
dmax2achs( &(dst->exp), exp, ACHS_0TO9 );
}
Can't remember what ACHV was for so if you think of anything lemme know, oh and achs is just a typedef to char const *, likewise ach is just a typedef to char, they exist to complete the set (I also have wchs, tchs, uchs, c16s & c32s, tch/s being for TCHAR on windows and char anywhere else, uchs is for char or uint_least8_t)