Thread: Custom "sprintf" type function

  1. #1
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,733

    Custom "sprintf" type function

    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)

  2. #2
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,733
    Came back to it, fixed the typo in specidy_modifier to specify_modifier, sorted a few mistakes, done a full rebuild, haven't tested yet but no compile time issues have been found so I'm putting it here for peops to give their input while I decide how I want to account for leading 0s in floating numbers

    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;
    			arg.sfxl =
    			(
    				!(arg.opts & (1u << FORMAT_FLAG_SUFFIX_LIT))
    				|| specify_text( arg.spec )
    				|| specify_double( arg.spec )
    			) ? 0 : arg.sfxl;
    		}
    		else if ( !(arg.opts & (1u << FORMAT_FLAG_SUFFIX_LIT)) )
    		{
    			arg.sfxl = 0;
    			arg.pfxl = specify_text( arg.spec ) ? 0 : arg.pfxl;
    		}
    
    		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;
    }
    Edit: Forgot to mention that my buffer functions are using a set of common suffixes to indicate what they focus on:
    *b = length of buffer being passed is assessed and passed onto *n (*b is only available only for text functions)
    *n = buffer & length is provided for insertion, appending or replacement (function prefix indicates this)
    *v / *f = same as prinf/scanf family of functions, again prefix indicates what the arguments are used for
    *c = the count is being set, grown or trimmed
    *t = the total elements available is set, grown or trimmed, this can affect the count

    All text varieties of the functions ensure the buffer ends with 0 so besides outright termination or voiding of the object the count will always be at least 1 after the first allocation

    Edit 2: Also looking for suggestions on how to pre-parse the arguments to reduce the allocations to just 1

  3. #3
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,733
    Well I got round to running, had this come up in the output though, haven't spotted what the error is caused by yet.
    Code:
    make test
    ...
    cd ../../bin && ./tryextra._x86_64_linux_cc_d.elf -D APP_DATA=../run
    =================================================================
    ==66238==ERROR: AddressSanitizer: global-buffer-overflow on address 0x7ff5b472cb78 at pc 0x7ff5b471cc49 bp 0x7ffc819cb340 sp 0x7ffc819cb330
    READ of size 4 at 0x7ff5b472cb78 thread T0
        #0 0x7ff5b471cc48 in optsofArg ../../../src/libbasic/buffer/text.c:363
        #1 0x7ff5b471f53b in markTextv ../../../src/libbasic/buffer/text.c:624
        #2 0x7ff5b4716648 in cramAchsv ../../../src/libbasic/buffer/achs.c:312
        #3 0x7ff5b4715fa8 in growAchsv ../../../src/libbasic/buffer/achs.c:263
        #4 0x7ff5b4715b48 in initAchsv ../../../src/libbasic/buffer/achs.c:232
        #5 0x7ff5b4715717 in makeAchsv ../../../src/libbasic/buffer/achs.c:194
        #6 0x7ff5b47158d0 in makeAchsf ../../../src/libbasic/buffer/achs.c:207
        #7 0x556774bb3682 in main ../../src/tryextra/main.c:110
        #8 0x7ff5b454eb24 in __libc_start_main (/usr/lib/libc.so.6+0x27b24)
        #9 0x556774bae64d in _start (/mnt/MEDIA/HOME/gitlab/dragonbuilder/bin/tryextra._x86_64_linux_cc_d.elf+0x364d)
    0x7ff5b472cb78 is located 0 bytes to the right of global variable 'opts' defined in '../../../src/libbasic/buffer/text.c:357:19' (0x7ff5b472cb60) of size 24
    SUMMARY: AddressSanitizer: global-buffer-overflow ../../../src/libbasic/buffer/text.c:363 in optsofArg
    Shadow bytes around the buggy address:
      0x0fff368dd910: f9 f9 f9 f9 00 01 f9 f9 f9 f9 f9 f9 03 f9 f9 f9
      0x0fff368dd920: f9 f9 f9 f9 00 03 f9 f9 f9 f9 f9 f9 00 00 00 04
      0x0fff368dd930: f9 f9 f9 f9 07 f9 f9 f9 f9 f9 f9 f9 00 00 00 00
      0x0fff368dd940: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
      0x0fff368dd950: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    =>0x0fff368dd960: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00[f9]
      0x0fff368dd970: f9 f9 f9 f9 00 00 00 00 00 00 00 00 00 00 00 00
      0x0fff368dd980: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
      0x0fff368dd990: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
      0x0fff368dd9a0: 04 f9 f9 f9 f9 f9 f9 f9 04 f9 f9 f9 f9 f9 f9 f9
      0x0fff368dd9b0: 00 00 00 00 00 00 00 00 00 03 f9 f9 f9 f9 f9 f9
    Shadow byte legend (one shadow byte represents 8 application bytes):
      Addressable:           00
      Partially addressable: 01 02 03 04 05 06 07
      Heap left redzone:       fa
      Freed heap region:       fd
      Stack left redzone:      f1
      Stack mid redzone:       f2
      Stack right redzone:     f3
      Stack after return:      f5
      Stack use after scope:   f8
      Global redzone:          f9
      Global init order:       f6
      Poisoned by user:        f7
      Container overflow:      fc
      Array cookie:            ac
      Intra object redzone:    bb
      ASan internal:           fe
      Left alloca redzone:     ca
      Right alloca redzone:    cb
      Shadow gap:              cc
    ==66238==ABORTING
    make: *** [../../gnu.mak:45: test] Error 1
    Compilation failed.
    A reminder of optsofArg contents:
    Code:
    BASIC_EXP void optsofArg( ARG *arg )
    {
    static dint const opts[FORMAT_FLAG_COUNT] = { '-', '+', ' ', '#', '~', '0' };
    	uint i, opt;
    	arg->opts = 0;
    	for ( i = 0; FORMAT_FLAG_COUNT; ++i )
    	{
    		opt = (arg->text[0] == opts[i]); // This is the reported line
    		arg->opts |= opt << i;
    		arg->text += opt;
    		arg->i += opt;
    	}
    }
    Edit: Just realised opt could loose information if I introduce enough flags so changed it's type to uint, still doesn't fix memory corruption though

    Edit 2:Never mind I just spotted the issue, forgot to add "i < " before the FORMAT_FLAG_COUNT

  4. #4
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    > for ( i = 0; FORMAT_FLAG_COUNT; ++i )
    When do you expect this condition to be false?

    Kinda surprised that the compiler didn't give you an 'expression always true' warning.

    How many of those 100's of lines of code did you write before bothering to compile at test it?
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  5. #5
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,733
    Quote Originally Posted by Salem View Post
    > for ( i = 0; FORMAT_FLAG_COUNT; ++i )
    When do you expect this condition to be false?

    Kinda surprised that the compiler didn't give you an 'expression always true' warning.

    How many of those 100's of lines of code did you write before bothering to compile at test it?
    The whole function, and only saw you're post after finally noticing it while debugging, thx anyways, as for why the compiler didn't warn about it, it's because infinite loops aren't always an error, it's rare for it to be intentional though

  6. #6
    Registered User
    Join Date
    Feb 2022
    Posts
    45
    If you don't mind me asking, why are you trying to recreate a sprintf()-like function? Is this just as an exercise, or do you have a specific need for it?

    Without some sort of comments, documentation, or even just an explanation of what the code is for, we have few if any ways to determine what is or isn't valid.

    I will say, however, that it would help a great deal if you could decompose markTextv() into several helper functions; as it is, it is rather long for a single function, making it harder to read.
    Last edited by Schol-R-LEA-2; 02-14-2022 at 11:03 AM.

  7. #7
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,733
    Quote Originally Posted by Schol-R-LEA-2 View Post
    If you don't mind me asking, why are you trying to recreate a sprintf()-like function? Is this just as an exercise, or do you have a specific need for it?

    Without some sort of comments, documentation, or even just an explanation of what the code is for, we have few if any ways to determine what is or isn't valid.

    I will say, however, that it would help a great deal if you could decompose markTextv() into several helper functions; as it is, it is rather long for a single function, making it harder to read.
    Specific need for it, I created an abstract buffer object and I'm mapping common types to it's api, I figure since I'm mapping strings to it's api I might as well go all out, and do a centralised printf style function with a few extra options for printing, for example the ~ character you can see in the flags list is to compliment the # flag as the suffix of literals (so for example instead of just 0x1 it can be 0x1UL), another example is support for the intptr_t & uintptr_t types (which uptr & dptr map to by default), I'm also making use of the ".*" to specify the length of a string being read (since as far as I know the standard versions don't support doing so at all which I need to be able to do when using part of existing strings such as code loaded from file). As for decomposing I'm doing that at the moment, hit a little bug with string placement though, if you feel like looking with me then here's the functions I've split that part into:

    Code:
    BASIC_EXP dint addWcs( void *ud, DINTS *Text, wchs str, uint not0 )
    {
    	dint *text;
    	uint i, b4;
    	dint err;
    	if ( str )
    		not0 = not0 ? not0 : wchsnot0(str);
    	else
    	{
    		str = L"(null)";
    		not0 = wchsnot0(str);
    	}
    	b4 = Text->count;
    	err = growTextc( ud, Text, not0 );
    	if ( err )
    	{
    		ECHO_ERRNO( stdout, err );
    		return err;
    	}
    	text = Text->array;
    	text += (b4 - !!b4);
    	for ( i = 0; i < not0; ++i )
    		text[i] = str[i];
    	return 0;
    }
    
    BASIC_EXP dint addStr( void *ud, DINTS *Text, achs str, uint not0 )
    {
    	dint *text;
    	uint i, b4;
    	dint err;
    	if ( str )
    		not0 = not0 ? not0 : achsnot0(str);
    	else
    	{
    		str = "(null)";
    		not0 = achsnot0(str);
    	}
    	b4 = Text->count;
    	err = growTextc( ud, Text, not0 );
    	if ( err )
    	{
    		ECHO_ERRNO( stdout, err );
    		return err;
    	}
    	text = Text->array;
    	text += (b4 - !!b4);
    	for ( i = 0; i < not0; ++i )
    		text[i] = str[i];
    	return 0;
    }
    
    BASIC_EXP dint addChar( void *ud, DINTS *Text, dint c, uint num )
    {
    	dint *text;
    	uint i, b4;
    	dint err;
    	num += !num;
    	b4 = Text->count;
    	err = growTextc( ud, Text, num );
    	if ( err )
    	{
    		ECHO_ERRNO( stdout, err );
    		return err;
    	}
    	text = Text->array;
    	text += (b4 - !!b4);
    	for ( i = 0; i < num; ++i )
    		text[i] = c;
    	return 0;
    }
    Text->count always includes the '\0' when not 0 hence the b4 - !!b4 (done that way to eliminate branching there)

  8. #8
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,733
    Edit: Figured out why I was getting an empty string, was an issue with when I was incrementing term, I'd also forgot to increment arg.text, replaced the offending code near the top of markTextv, getting buffer overflow somewhere now, I'll take a look myself before I post it (if at all)

    Original post:
    k, finished splitting markTextv up into smaller parts, I'm not sure it's possible to make the function smaller at this point, since there where some updates to previously posted code I've decided to just post the lot again, I've split them into sections to make it easier to find the section your after, despite all the logic I've put in I'm somehow getting blank strings so any help is welcome
    Code:
    dint specify_type( 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;
    }
    Code:
    typedef struct _ARG
    {
    	uint i;
    	bool addr;
    	uint opts;
    	/* Prefix length */
    	uint pfxl;
    	/* Suffix length */
    	uint sfxl;
    	/* Precision */
    	uint prec;
    	/* Min Count */
    	uint minc;
    	/* Prefix string */
    	achs pfxs;
    	/* Specified type: signed, unsigned, floating point, string, etc */
    	dint spec;
    	/* Variant of type: char, short, long, long long, etc*/
    	dint type;
    	/* Suffix string */
    	achs sfxs;
    	dint const *text;
    } ARG;
    
    typedef enum _ARG_O
    {
    	ARG_O_ALIGN_LEFT = 0,
    	ARG_O_BOTH_SIGNS,
    	ARG_O_PLUS2SPACE,
    	ARG_O_ADD_PREFIX,
    	ARG_O_ADD_SUFFIX,
    	ARG_O_PAD_WITH_0,
    	ARG_O_COUNT,
    	ARG_O_DECIMALDIG = ARG_O_COUNT
    } ARG_O;
    Code:
    BASIC_EXP void optsofArg( ARG *arg )
    {
    static char const opts[ARG_O_COUNT] = { '-', '+', ' ', '#', '~', '0' };
    	uint i, opt;
    	arg->opts = 0;
    	for ( i = 0; i < ARG_O_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 << ARG_O_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 << ARG_O_DECIMALDIG);
    	}
    	return false;
    }
    
    BASIC_EXP void typeofArg( ARG *arg )
    {
    	dint type;
    	arg->type = specify_type( arg->text[0] );
    	switch ( arg->type )
    	{
    	case 'p':
    		arg->spec = specify_integer( arg->text[1] );
    		if ( !(arg->spec) )
    		{
    			arg->opts |= (1u << ARG_O_ADD_PREFIX);
    			arg->pfxs = "0x";
    			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) )
    		{
    			do
    			{
    				arg->spec = specify_integer( arg->text[0] );
    				if ( arg->spec )
    					break;
    				arg->spec = specify_double( arg->text[0] );
    				if ( arg->spec )
    					break;
    				arg->spec = specify_text( arg->text[0] );
    					break;
    				return;
    			}
    			while (0);
    			arg->text++;
    			arg->i++;
    			return;
    		}
    	}
    	type = specify_type( arg->text[1] );
    	if ( type )
    	{
    		if ( type != arg->type )
    		{
    			arg->spec = 'd';
    			return;
    		}
    		switch ( type )
    		{
    		case 'l':
    			arg->type = 'L';
    			arg->text++;
    			arg->i++;
    			break;
    		case 'h':
    			arg->type = '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;
    	}
    }
    Code:
    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': case 'H':
    			text[j++] = arg->type;
    			text[j++] = arg->type;
    			break;
    		case 'l': text[j++] = 'L'; break;
    		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;
    }
    achs baseofArg( ARG *arg )
    {
    	switch ( arg->spec )
    	{
    	case 'X': case 'A': case 'p':
    		arg->pfxs = "0x";
    		return ACHS_0TO9 "ABCDEF";
    	case 'x': case 'a':
    		arg->pfxs = "0x";
    		return ACHS_0TO9 "abcdef";
    	case 'o':
    		arg->pfxs = "0";
    		return "01234567";
    	case 'b':
    		arg->pfxs = "0b";
    		return "01";
    	}
    	arg->pfxs = "";
    	return ACHS_0TO9;
    }
    Code:
    BASIC_EXP dint addWcs( void *ud, DINTS *Text, wchs str, uint not0 )
    {
    	dint *text;
    	uint i, b4;
    	dint err;
    	if ( str )
    		not0 = not0 ? not0 : wchsnot0(str);
    	else
    	{
    		str = L"(null)";
    		not0 = wchsnot0(str);
    	}
    	b4 = Text->count;
    	err = growTextc( ud, Text, not0 );
    	if ( err )
    	{
    		ECHO_ERRNO( stdout, err );
    		return err;
    	}
    	text = Text->array;
    	text += (b4 - !!b4);
    	for ( i = 0; i < not0; ++i )
    		text[i] = str[i];
    	return 0;
    }
    
    BASIC_EXP dint addStr( void *ud, DINTS *Text, achs str, uint not0 )
    {
    	dint *text;
    	uint i, b4;
    	dint err;
    	if ( str )
    		not0 = not0 ? not0 : achsnot0(str);
    	else
    	{
    		str = "(null)";
    		not0 = achsnot0(str);
    	}
    	b4 = Text->count;
    	err = growTextc( ud, Text, not0 );
    	if ( err )
    	{
    		ECHO_ERRNO( stdout, err );
    		return err;
    	}
    	text = Text->array;
    	text += (b4 - !!b4);
    	for ( i = 0; i < not0; ++i )
    		text[i] = str[i];
    	return 0;
    }
    
    BASIC_EXP dint addChar( void *ud, DINTS *Text, dint c, uint num )
    {
    	dint *text;
    	uint i, b4;
    	dint err;
    	num += !num;
    	b4 = Text->count;
    	err = growTextc( ud, Text, num );
    	if ( err )
    	{
    		ECHO_ERRNO( stdout, err );
    		return err;
    	}
    	text = Text->array;
    	text += (b4 - !!b4);
    	for ( i = 0; i < num; ++i )
    		text[i] = c;
    	return 0;
    }
    Code:
    BASIC_EXP dint addUint( void *ud, DINTS *Text, ARG *arg, umax val )
    {
    	dint err;
    	ACHN num = {0};
    	uint minc = (arg->opts & (1u << ARG_O_PAD_WITH_0)) ? arg->minc : 0;
    	uint apfx =
    		(arg->opts & (1u << ARG_O_ADD_PREFIX))
    		? achsnot0( arg->pfxs ) : 0;
    	uint both = !!(arg->opts & (1u << ARG_O_BOTH_SIGNS));
    	umax2achs( &num, val, baseofArg(arg) );
    	if ( both )
    	{
    		err = addChar( ud, Text, '+', 1 );
    		if ( err )
    		{
    			ECHO_ERROR( stdout, err );
    			return err;
    		}
    	}
    	if ( apfx )
    	{
    		err = addStr( ud, Text, arg->pfxs, apfx );
    		if ( err )
    		{
    			ECHO_ERROR( stdout, err );
    			return err;
    		}
    	}
    	if ( num.not0 < minc )
    	{
    		addChar( ud, Text, '0', minc - num.not0 );
    		if ( err )
    		{
    			ECHO_ERROR( stdout, err );
    			return err;
    		}
    	}
    	return addStr( ud, Text, num.text, num.not0 );
    }
    
    BASIC_EXP dint addDint( void *ud, DINTS *Text, ARG *arg, dmax val )
    {
    	dint err;
    	ACHN num = {0};
    	uint not0 = 0;
    	uint minc = (arg->opts & (1u << ARG_O_PAD_WITH_0)) ? arg->minc : 0;
    	uint apfx =
    		(arg->opts & (1u << ARG_O_ADD_PREFIX))
    		? achsnot0( arg->pfxs ) : 0;
    	uint both = !!(arg->opts & (1u << ARG_O_BOTH_SIGNS));
    	dmax2achs( &num, val, baseofArg(arg) );
    	not0 = both + num.not0 + apfx;
    	if ( num.sign[0] == '-' || both )
    	{
    		if ( !minc && arg->minc > not0 )
    		{
    			err = addChar( ud, Text, ' ', arg->minc - not0 );
    			if ( err )
    			{
    				ECHO_ERROR( stdout, err );
    				return err;
    			}
    		}
    		err = addChar( ud, Text, num.sign[0], 1 );
    		if ( err )
    		{
    			ECHO_ERROR( stdout, err );
    			return err;
    		}
    	}
    	if ( apfx )
    	{
    		err = addStr( ud, Text, arg->pfxs, apfx );
    		if ( err )
    		{
    			ECHO_ERROR( stdout, err );
    			return err;
    		}
    	}
    	if ( not0 < minc )
    	{
    		addChar( ud, Text, '0', minc - not0 );
    		if ( err )
    		{
    			ECHO_ERROR( stdout, err );
    			return err;
    		}
    	}
    	return addStr( ud, Text, num.text, num.not0 );
    }
    
    BASIC_EXP dint addPtr( void *ud, DINTS *Text, ARG *arg, ptr val )
    {
    	ARG tmp = *arg;
    	tmp.opts &= ~(1u << ARG_O_BOTH_SIGNS);
    	tmp.opts |=  (1u << ARG_O_ADD_PREFIX);
    	tmp.pfxs = "0x";
    	tmp.type = 'p';
    	tmp.spec = 'X';
    	return addUint( ud, Text, &tmp, (uptr)val );
    }
    
    BASIC_EXP dint addFloat( void *ud, DINTS *Text, ARG *arg, ldnum val )
    {
    	ARG tmp = {0};
    	uint not0, prec, expl;
    	ACHF num;
    	dint err;
    	dint spec = tolower( arg->spec );
    	bool pad0 = !!(arg->opts & (1u << ARG_O_PAD_WITH_0));
    	bool both = !!(arg->opts & (1u << ARG_O_BOTH_SIGNS));
    	ldnum2achs( &num, val, baseofArg( arg ) );
    	/* Exponent, examples: e+10 e-10 */
    	expl = num.exp.not0 + !!(num.exp.not0) + !!(num.exp.not0);
    	prec = (arg->opts & (1u << ARG_O_DECIMALDIG)) ? arg->prec : num.dec.not0;
    	not0 = prec + num.num.not0 + arg->pfxl + expl;
    	tmp.spec = 'd';
    	tmp.pfxs = arg->pfxs;
    	if ( arg->minc > not0 )
    	{
    		if ( pad0 )
    			tmp.minc = arg->minc - not0;
    		else
    		{
    			err = addChar( ud, Text, ' ', arg->minc - not0 );
    			if ( err )
    			{
    				ECHO_ERRNO( stdout, err );
    				return err;
    			}
    		}
    	}
    	if ( num.num.sign[0] == '-' || both )
    	{
    		err = addChar( ud, Text, num.num.sign[0], 1 );
    		if ( err )
    		{
    			ECHO_ERRNO( stdout, err );
    			return err;
    		}
    	}
    	if ( pad0 && tmp.minc )
    	{
    		err = addChar( ud, Text, '0', tmp.minc );
    		if ( err )
    		{
    			ECHO_ERRNO( stdout, err );
    			return err;
    		}
    	}
    	err = addStr( ud, Text, num.num.text, num.num.not0 );
    	if ( err )
    	{
    		ECHO_ERRNO( stdout, err );
    		return err;
    	}
    	if ( prec && num.zero )
    	{
    		not0 = (num.zero < prec) ? num.zero : prec;
    		err = addChar( ud, Text, '0', not0 );
    		if ( err )
    		{
    			ECHO_ERRNO( stdout, err );
    			return err;
    		}
    		prec -= not0;
    	}
    	if ( prec && num.dec.not0 )
    	{
    		not0 = (num.dec.not0 < prec) ? num.dec.not0 : prec;
    		err = addStr( ud, Text, num.dec.text, not0 );
    		if ( err )
    		{
    			ECHO_ERRNO( stdout, err );
    			return err;
    		}
    		prec -= not0;
    	}
    	if ( prec )
    	{
    		err  = addChar( ud, Text, '0', prec );
    		if ( err )
    		{
    			ECHO_ERRNO( stdout, err );
    			return err;
    		}
    	}
    	if ( num.exp.not0 && spec != 'f' )
    	{
    		ach exp[] = { 0, num.exp.sign[0], 0 };
    		switch ( arg->spec )
    		{
    		case 'e': case 'g': exp[0] = 'e'; break;
    		case 'E': case 'G': exp[0] = 'E'; break;
    		case 'a': exp[0] = 'p'; break;
    		case 'A': exp[0] = 'P'; break;
    		}
    		err = addStr( ud, Text, exp, 2 );
    		if ( err )
    		{
    			ECHO_ERRNO( stdout, err );
    			return err;
    		}
    		err = addStr( ud, Text, num.exp.text, num.exp.not0 );
    		if ( err )
    		{
    			ECHO_ERRNO( stdout, err );
    			return err;
    		}
    	}
    	return 0;
    }
    Code:
    BASIC_EXP dint markTextv( void *ud, DINTS *Text, dint const *args, va_list va )
    {
    	dint err = 0;
    	uint i, j, init = 0, term = 0, not0;
    	ACHN num = {0};
    	ACHF fpn = {0};
    	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 = initTextt( ud, Text, sizeof(dint), j );
    	if ( err )
    	{
    		ECHO_ERRNO( stdout, err );
    		return err;
    	}
    
    	/* Actually process the arguments this time */
    	for ( arg.text = args; args[arg.i]; )
    	{
    		if ( arg.text[0] != '%' )
    		{
    			++term;
    			arg.i++;
    			arg.text++;
    			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 << ARG_O_ADD_PREFIX)) )
    		{
    			arg.pfxl = 0;
    			arg.sfxl =
    			(
    				!(arg.opts & (1u << ARG_O_ADD_SUFFIX))
    				|| specify_text( arg.spec )
    				|| specify_double( arg.spec )
    			) ? 0 : arg.sfxl;
    		}
    		else if ( !(arg.opts & (1u << ARG_O_ADD_SUFFIX)) )
    		{
    			arg.sfxl = 0;
    			arg.pfxl = specify_text( arg.spec ) ? 0 : arg.pfxl;
    		}
    
    		memset( &num, 0, sizeof(ACHN) );
    		memset( &fpn, 0, sizeof(ACHN) );
    
    		if ( specify_double( arg.spec ) )
    		{
    			err = addFloat
    			(
    				ud, Text, &arg,
    				(arg.type == 'L')
    					? va_arg( va, ldnum )
    					: va_arg( va, dnum )
    			);
    			if ( err )
    			{
    				ECHO_ERRNO( stdout, err );
    				return err;
    			}
    			continue;
    		}
    
    		switch ( arg.spec )
    		{
    		case 'c':
    			err = addChar( ud, Text, va_arg( va, dint ), arg.prec );
    			if ( err )
    			{
    				ECHO_ERRNO( stdout, err );
    				return err;
    			}
    			continue;
    		case 's':
    			not0 = arg.prec;
    			err = ( arg.type == 'l' )
    				? addWcs( ud, Text, va_arg( va, wchs ), not0 )
    				: addStr( ud, Text, va_arg( va, achs ), not0 );
    			if ( err )
    			{
    				ECHO_ERRNO( stdout, err );
    				return err;
    			}
    			continue;
    		case 'p':
    			err = addPtr( ud, Text, &arg, va_arg( va, ptr ) );
    			if ( err )
    			{
    				ECHO_ERRNO( stdout, err );
    				return err;
    			}
    			continue;
    		case 'd':
    			switch ( arg.type )
    			{
    			case 't': err = addDint( ud, Text, &arg, va_arg( va, ptrdiff_t ) );	break;
    			case 'z': err = addDint( ud, Text, &arg, va_arg( va, size_t ) );	break;
    			case 'p': err = addDint( ud, Text, &arg, va_arg( va, dptr ) );		break;
    			case 'j': err = addDint( ud, Text, &arg, va_arg( va, dmax ) );		break;
    			case 'l': err = addDint( ud, Text, &arg, va_arg( va, dlong ) );		break;
    			default:  err = addDint( ud, Text, &arg, va_arg( va, dint ) ); 		break;
    			}
    			continue;
    		default:
    			switch ( arg.type )
    			{
    			case 't': err = addUint( ud, Text, &arg, va_arg( va, ptrdiff_t ) );	break;
    			case 'z': err = addUint( ud, Text, &arg, va_arg( va, size_t ) );	break;
    			case 'p': err = addUint( ud, Text, &arg, va_arg( va, uptr ) );		break;
    			case 'j': err = addUint( ud, Text, &arg, va_arg( va, umax ) );		break;
    			case 'l': err = addUint( ud, Text, &arg, va_arg( va, ulong ) );		break;
    			default:  err = addUint( ud, Text, &arg, va_arg( va, uint ) );		break;
    			}
    			continue;
    		}
    	}
    	return 0;
    }

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 3
    Last Post: 06-17-2020, 09:30 AM
  2. Not atoi, not sprintf, but "int coded into char*"
    By Mariano L Gappa in forum C Programming
    Replies: 5
    Last Post: 11-26-2005, 01:53 AM
  3. error with custom "chomp" function
    By kinghajj in forum C Programming
    Replies: 12
    Last Post: 01-05-2004, 08:52 AM
  4. "CWnd"-"HWnd","CBitmap"-"HBitmap"...., What is mean by "
    By L.O.K. in forum Windows Programming
    Replies: 2
    Last Post: 12-04-2002, 07:59 AM

Tags for this Thread