added some code to format floating-point numbers
This commit is contained in:
		| @ -291,6 +291,14 @@ QSE_EXPORT int qse_fmtuintmaxtowcs ( | ||||
| #	define qse_fmtuintmax(b,sz,v,bf,pr,fc,pf) qse_fmtuintmaxtowcs(b,sz,v,bf,pr,fc,pf) | ||||
| #endif | ||||
|  | ||||
| QSE_EXPORT int qse_fmtfltmaxtombs ( | ||||
| 	qse_mchar_t* buf,  | ||||
| 	qse_size_t   bufsize, | ||||
| 	qse_fltmax_t f, | ||||
| 	qse_mchar_t  point,  | ||||
| 	int          digits | ||||
| ); | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
|  | ||||
| @ -454,7 +454,7 @@ typedef qse_int_t qse_intptr_t; | ||||
|  | ||||
| /** @typedef qse_flt_t | ||||
|  * The qse_flt_t type defines the largest floating-pointer number type | ||||
|  * supported. | ||||
|  * naturally supported. | ||||
|  */ | ||||
| #if defined(__FreeBSD__) | ||||
| 	/* TODO: check if the support for long double is complete. | ||||
| @ -469,16 +469,17 @@ typedef qse_int_t qse_intptr_t; | ||||
| #	define QSE_SIZEOF_FLT_T QSE_SIZEOF_DOUBLE | ||||
| #endif | ||||
|  | ||||
| /* TODO: qse_fltmax_t to include the quadruple precision floating-point type.  | ||||
|  * | ||||
| #if QSE_SIZEOF___FLOAT128 > 0 | ||||
| /** @typedef qse_fltmax_t | ||||
|  * The qse_fltmax_t type defines the largest floating-pointer number type | ||||
|  * ever supported. | ||||
|  */ | ||||
| #if QSE_SIZEOF__FLOAT128 > QSE_SIZEOF_FLT_T | ||||
| 	typedef __float128 qse_fltmax_t; | ||||
| #	define QSE_SIZEOF_FLTMAX_T QSE_SIZEOF___FLOAT128 | ||||
| #else | ||||
| 	typedef qse_flt_t qse_fltmax_t; | ||||
| #	define QSE_SIZEOF_FLTMAX_T QSE_SIZEOF_FLT_T | ||||
| #endif | ||||
|  */ | ||||
|  | ||||
| /**  | ||||
|  * The qse_mchar_t type defines a multi-byte character type. | ||||
|  | ||||
| @ -21,210 +21,30 @@ | ||||
| #include <qse/cmn/fmt.h> | ||||
| #include <qse/cmn/str.h> | ||||
|  | ||||
| #undef T | ||||
| #undef char_t | ||||
| #undef fmt_uintmax | ||||
| #undef strlen | ||||
|  | ||||
| #define T(x) QSE_MT(x) | ||||
| #define char_t qse_mchar_t | ||||
| #define fmt_uintmax fmt_unsigned_to_mbs | ||||
| #define strlen(x) qse_mbslen(x) | ||||
| #include "fmt.h" | ||||
|  | ||||
| #undef T | ||||
| #undef char_t | ||||
| #undef fmt_uintmax | ||||
| #undef strlen | ||||
|  | ||||
| #define T(x) QSE_WT(x) | ||||
| #define char_t qse_wchar_t | ||||
| #define fmt_uintmax fmt_unsigned_to_wcs | ||||
| #define strlen(x) qse_wcslen(x) | ||||
| #include "fmt.h" | ||||
|  | ||||
| /* ==================== multibyte ===================================== */ | ||||
|  | ||||
| static int fmt_unsigned_to_mbs ( | ||||
| 	qse_mchar_t* buf, int size,  | ||||
| 	qse_uintmax_t value, int base_and_flags, int prec, | ||||
| 	qse_mchar_t fillchar, qse_mchar_t signchar, const qse_mchar_t* prefix) | ||||
| { | ||||
| 	qse_mchar_t tmp[(QSE_SIZEOF(qse_uintmax_t) * 8)]; | ||||
| 	int reslen, base, fillsize, reqlen, pflen, preczero; | ||||
| 	qse_mchar_t* p, * bp, * be; | ||||
| 	const qse_mchar_t* xbasestr; | ||||
|  | ||||
| 	base = base_and_flags & 0x3F; | ||||
| 	if (base < 2 || base > 36) return -1; | ||||
|  | ||||
| 	xbasestr = (base_and_flags & QSE_FMTINTMAXTOMBS_UPPERCASE)? | ||||
| 		QSE_MT("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"): | ||||
| 		QSE_MT("0123456789abcdefghijklmnopqrstuvwxyz"); | ||||
|  | ||||
|  | ||||
| 	if ((base_and_flags & QSE_FMTINTMAXTOMBS_NOZERO) && value == 0)  | ||||
| 	{ | ||||
| 		p = tmp;  | ||||
| 		if (base_and_flags & QSE_FMTINTMAXTOMBS_ZEROLEAD)  | ||||
| 		{ | ||||
| 			/* NOZERO emits no digit, ZEROLEAD emits 1 digit. | ||||
| 			 * so it emits '0' */ | ||||
| 			reslen = 1; | ||||
| 			preczero = 1; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			/* since the value is zero, emit no digits */ | ||||
| 			reslen = 0; | ||||
| 			preczero = 0; | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		qse_uintmax_t v = value; | ||||
|  | ||||
| 		/* store the resulting numeric string into 'tmp' first */ | ||||
| 		p = tmp;  | ||||
| 		do | ||||
| 		{ | ||||
| 			*p++ = xbasestr[v % base]; | ||||
| 			v /= base; | ||||
| 		} | ||||
| 		while (v > 0); | ||||
|  | ||||
| 		/* reslen is the length of the resulting string without padding. */ | ||||
| 		reslen = (int)(p - tmp); | ||||
| 	 | ||||
| 		/* precision specified the minum number of digits to produce. | ||||
| 		 * so if the precision is larger that the digits produced,  | ||||
| 		 * reslen should be adjusted to precision */ | ||||
| 		if (prec > reslen)  | ||||
| 		{ | ||||
| 			/* if the precision is greater than the actual digits | ||||
| 			 * made from the value, 0 is inserted in front. | ||||
| 			 * ZEROLEAD doesn't have to be handled explicitly | ||||
| 			 * since it's achieved effortlessly */ | ||||
| 			preczero = prec - reslen; | ||||
| 			reslen = prec; | ||||
| 		} | ||||
| 		else  | ||||
| 		{ | ||||
| 			preczero = 0; | ||||
| 			if ((base_and_flags & QSE_FMTINTMAXTOMBS_ZEROLEAD) && value != 0)  | ||||
| 			{ | ||||
| 				/* if value is zero, 0 is emitted from it.  | ||||
| 				 * so ZEROLEAD don't need to add another 0. */ | ||||
| 				preczero++; | ||||
| 				reslen++; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (signchar) reslen++; /* increment reslen for the sign character */ | ||||
| 	if (prefix) | ||||
| 	{ | ||||
| 		/* since the length can be truncated for different type sizes, | ||||
| 		 * don't pass in a very long prefix. */ | ||||
| 		pflen = (int)qse_mbslen(prefix); | ||||
| 		reslen += pflen; | ||||
| 	} | ||||
| 	else pflen = 0; | ||||
|  | ||||
| 	/* get the required buffer size for lossless formatting */ | ||||
| 	reqlen = (base_and_flags & QSE_FMTINTMAXTOMBS_NONULL)? reslen: (reslen + 1); | ||||
|  | ||||
| 	if (size <= 0 || | ||||
| 	    ((base_and_flags & QSE_FMTINTMAXTOMBS_NOTRUNC) && size < reqlen)) | ||||
| 	{ | ||||
| 		return -reqlen; | ||||
| 	} | ||||
|  | ||||
| 	/* get the size to fill with fill characters */ | ||||
| 	fillsize = (base_and_flags & QSE_FMTINTMAXTOMBS_NONULL)? size: (size - 1); | ||||
| 	bp = buf; | ||||
| 	be = buf + fillsize; | ||||
|  | ||||
| 	/* fill space */ | ||||
| 	if (fillchar != QSE_MT('\0')) | ||||
| 	{ | ||||
| 		if (base_and_flags & QSE_FMTINTMAXTOMBS_FILLRIGHT) | ||||
| 		{ | ||||
| 			/* emit sign */ | ||||
| 			if (signchar && bp < be) *bp++ = signchar; | ||||
|  | ||||
| 			/* copy prefix if necessary */ | ||||
| 			if (prefix) while (*prefix && bp < be) *bp++ = *prefix++; | ||||
|  | ||||
| 			/* add 0s for precision */ | ||||
| 			while (preczero > 0 && bp < be)  | ||||
| 			{  | ||||
| 				*bp++ = QSE_MT('0'); | ||||
| 				preczero--;  | ||||
| 			} | ||||
|  | ||||
| 			/* copy the numeric string to the destination buffer */ | ||||
| 			while (p > tmp && bp < be) *bp++ = *--p; | ||||
|  | ||||
| 			/* fill the right side */ | ||||
| 			while (fillsize > reslen) | ||||
| 			{ | ||||
| 				*bp++ = fillchar; | ||||
| 				fillsize--; | ||||
| 			} | ||||
| 		} | ||||
| 		else if (base_and_flags & QSE_FMTINTMAXTOMBS_FILLCENTER) | ||||
| 		{ | ||||
| 			/* emit sign */ | ||||
| 			if (signchar && bp < be) *bp++ = signchar; | ||||
|  | ||||
| 			/* fill the left side */ | ||||
| 			while (fillsize > reslen) | ||||
| 			{ | ||||
| 				*bp++ = fillchar; | ||||
| 				fillsize--; | ||||
| 			} | ||||
|  | ||||
| 			/* copy prefix if necessary */ | ||||
| 			if (prefix) while (*prefix && bp < be) *bp++ = *prefix++; | ||||
|  | ||||
| 			/* add 0s for precision */ | ||||
| 			while (preczero > 0 && bp < be)  | ||||
| 			{  | ||||
| 				*bp++ = QSE_MT('0'); | ||||
| 				preczero--;  | ||||
| 			} | ||||
|  | ||||
| 			/* copy the numeric string to the destination buffer */ | ||||
| 			while (p > tmp && bp < be) *bp++ = *--p; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			/* fill the left side */ | ||||
| 			while (fillsize > reslen) | ||||
| 			{ | ||||
| 				*bp++ = fillchar; | ||||
| 				fillsize--; | ||||
| 			} | ||||
|  | ||||
| 			/* emit sign */ | ||||
| 			if (signchar && bp < be) *bp++ = signchar; | ||||
|  | ||||
| 			/* copy prefix if necessary */ | ||||
| 			if (prefix) while (*prefix && bp < be) *bp++ = *prefix++; | ||||
|  | ||||
| 			/* add 0s for precision */ | ||||
| 			while (preczero > 0 && bp < be)  | ||||
| 			{  | ||||
| 				*bp++ = QSE_MT('0'); | ||||
| 				preczero--;  | ||||
| 			} | ||||
|  | ||||
| 			/* copy the numeric string to the destination buffer */ | ||||
| 			while (p > tmp && bp < be) *bp++ = *--p; | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		/* emit sign */ | ||||
| 		if (signchar && bp < be) *bp++ = signchar; | ||||
|  | ||||
| 		/* copy prefix if necessary */ | ||||
| 		if (prefix) while (*prefix && bp < be) *bp++ = *prefix++; | ||||
|  | ||||
| 		/* add 0s for precision */ | ||||
| 		while (preczero > 0 && bp < be)  | ||||
| 		{  | ||||
| 			*bp++ = QSE_MT('0'); | ||||
| 			preczero--;  | ||||
| 		} | ||||
|  | ||||
| 		/* copy the numeric string to the destination buffer */ | ||||
| 		while (p > tmp && bp < be) *bp++ = *--p; | ||||
| 	} | ||||
|  | ||||
| 	if (!(base_and_flags & QSE_FMTINTMAXTOMBS_NONULL)) *bp = QSE_MT('\0'); | ||||
| 	return bp - buf; | ||||
| } | ||||
|  | ||||
| int qse_fmtintmaxtombs ( | ||||
| 	qse_mchar_t* buf, int size,  | ||||
| 	qse_intmax_t value, int base_and_flags, int prec, | ||||
| @ -283,211 +103,8 @@ int qse_fmtuintmaxtombs ( | ||||
| 		buf, size, value, base_and_flags, prec, fillchar, signchar, prefix); | ||||
| } | ||||
|  | ||||
|  | ||||
| /* ==================== wide-char ===================================== */ | ||||
|  | ||||
| static int fmt_unsigned_to_wcs ( | ||||
| 	qse_wchar_t* buf, int size,  | ||||
| 	qse_uintmax_t value, int base_and_flags, int prec, | ||||
| 	qse_wchar_t fillchar, qse_wchar_t signchar, const qse_wchar_t* prefix) | ||||
| { | ||||
| 	qse_wchar_t tmp[(QSE_SIZEOF(qse_uintmax_t) * 8)]; | ||||
| 	int reslen, base, fillsize, reqlen, pflen, preczero; | ||||
| 	qse_wchar_t* p, * bp, * be; | ||||
| 	const qse_wchar_t* xbasestr; | ||||
|  | ||||
| 	base = base_and_flags & 0x3F; | ||||
| 	if (base < 2 || base > 36) return -1; | ||||
|  | ||||
| 	xbasestr = (base_and_flags & QSE_FMTINTMAXTOWCS_UPPERCASE)? | ||||
| 		QSE_WT("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"): | ||||
| 		QSE_WT("0123456789abcdefghijklmnopqrstuvwxyz"); | ||||
|  | ||||
|  | ||||
| 	if ((base_and_flags & QSE_FMTINTMAXTOWCS_NOZERO) && value == 0)  | ||||
| 	{ | ||||
| 		p = tmp;  | ||||
| 		if (base_and_flags & QSE_FMTINTMAXTOWCS_ZEROLEAD)  | ||||
| 		{ | ||||
| 			/* NOZERO emits no digit, ZEROLEAD emits 1 digit. | ||||
| 			 * so it emits '0' */ | ||||
| 			reslen = 1; | ||||
| 			preczero = 1; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			/* since the value is zero, emit no digits */ | ||||
| 			reslen = 0; | ||||
| 			preczero = 0; | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		qse_uintmax_t v = value; | ||||
|  | ||||
| 		/* store the resulting numeric string into 'tmp' first */ | ||||
| 		p = tmp;  | ||||
| 		do | ||||
| 		{ | ||||
| 			*p++ = xbasestr[v % base]; | ||||
| 			v /= base; | ||||
| 		} | ||||
| 		while (v > 0); | ||||
|  | ||||
| 		/* reslen is the length of the resulting string without padding. */ | ||||
| 		reslen = (int)(p - tmp); | ||||
| 	 | ||||
| 		/* precision specified the minum number of digits to produce. | ||||
| 		 * so if the precision is larger that the digits produced,  | ||||
| 		 * reslen should be adjusted to precision */ | ||||
| 		if (prec > reslen)  | ||||
| 		{ | ||||
| 			/* if the precision is greater than the actual digits | ||||
| 			 * made from the value, 0 is inserted in front. | ||||
| 			 * ZEROLEAD doesn't have to be handled explicitly | ||||
| 			 * since it's achieved effortlessly */ | ||||
| 			preczero = prec - reslen; | ||||
| 			reslen = prec; | ||||
| 		} | ||||
| 		else  | ||||
| 		{ | ||||
| 			preczero = 0; | ||||
| 			if ((base_and_flags & QSE_FMTINTMAXTOWCS_ZEROLEAD) && value != 0)  | ||||
| 			{ | ||||
| 				/* if value is zero, 0 is emitted from it.  | ||||
| 				 * so ZEROLEAD don't need to add another 0. */ | ||||
| 				preczero++; | ||||
| 				reslen++; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (signchar) reslen++; /* increment reslen for the sign character */ | ||||
| 	if (prefix) | ||||
| 	{ | ||||
| 		/* since the length can be truncated for different type sizes, | ||||
| 		 * don't pass in a very long prefix. */ | ||||
| 		pflen = (int)qse_wcslen(prefix); | ||||
| 		reslen += pflen; | ||||
| 	} | ||||
| 	else pflen = 0; | ||||
|  | ||||
| 	/* get the required buffer size for lossless formatting */ | ||||
| 	reqlen = (base_and_flags & QSE_FMTINTMAXTOWCS_NONULL)? reslen: (reslen + 1); | ||||
|  | ||||
| 	if (size <= 0 || | ||||
| 	    ((base_and_flags & QSE_FMTINTMAXTOWCS_NOTRUNC) && size < reqlen)) | ||||
| 	{ | ||||
| 		return -reqlen; | ||||
| 	} | ||||
|  | ||||
| 	/* get the size to fill with fill characters */ | ||||
| 	fillsize = (base_and_flags & QSE_FMTINTMAXTOWCS_NONULL)? size: (size - 1); | ||||
| 	bp = buf; | ||||
| 	be = buf + fillsize; | ||||
|  | ||||
| 	/* fill space */ | ||||
| 	if (fillchar != QSE_WT('\0')) | ||||
| 	{ | ||||
| 		if (base_and_flags & QSE_FMTINTMAXTOWCS_FILLRIGHT) | ||||
| 		{ | ||||
| 			/* emit sign */ | ||||
| 			if (signchar && bp < be) *bp++ = signchar; | ||||
|  | ||||
| 			/* copy prefix if necessary */ | ||||
| 			if (prefix) while (*prefix && bp < be) *bp++ = *prefix++; | ||||
|  | ||||
| 			/* add 0s for precision */ | ||||
| 			while (preczero > 0 && bp < be)  | ||||
| 			{  | ||||
| 				*bp++ = QSE_WT('0'); | ||||
| 				preczero--;  | ||||
| 			} | ||||
|  | ||||
| 			/* copy the numeric string to the destination buffer */ | ||||
| 			while (p > tmp && bp < be) *bp++ = *--p; | ||||
|  | ||||
| 			/* fill the right side */ | ||||
| 			while (fillsize > reslen) | ||||
| 			{ | ||||
| 				*bp++ = fillchar; | ||||
| 				fillsize--; | ||||
| 			} | ||||
| 		} | ||||
| 		else if (base_and_flags & QSE_FMTINTMAXTOWCS_FILLCENTER) | ||||
| 		{ | ||||
| 			/* emit sign */ | ||||
| 			if (signchar && bp < be) *bp++ = signchar; | ||||
|  | ||||
| 			/* fill the left side */ | ||||
| 			while (fillsize > reslen) | ||||
| 			{ | ||||
| 				*bp++ = fillchar; | ||||
| 				fillsize--; | ||||
| 			} | ||||
|  | ||||
| 			/* copy prefix if necessary */ | ||||
| 			if (prefix) while (*prefix && bp < be) *bp++ = *prefix++; | ||||
|  | ||||
| 			/* add 0s for precision */ | ||||
| 			while (preczero > 0 && bp < be)  | ||||
| 			{  | ||||
| 				*bp++ = QSE_WT('0'); | ||||
| 				preczero--;  | ||||
| 			} | ||||
|  | ||||
| 			/* copy the numeric string to the destination buffer */ | ||||
| 			while (p > tmp && bp < be) *bp++ = *--p; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			/* fill the left side */ | ||||
| 			while (fillsize > reslen) | ||||
| 			{ | ||||
| 				*bp++ = fillchar; | ||||
| 				fillsize--; | ||||
| 			} | ||||
|  | ||||
| 			/* emit sign */ | ||||
| 			if (signchar && bp < be) *bp++ = signchar; | ||||
|  | ||||
| 			/* copy prefix if necessary */ | ||||
| 			if (prefix) while (*prefix && bp < be) *bp++ = *prefix++; | ||||
|  | ||||
| 			/* add 0s for precision */ | ||||
| 			while (preczero > 0 && bp < be)  | ||||
| 			{  | ||||
| 				*bp++ = QSE_WT('0'); | ||||
| 				preczero--;  | ||||
| 			} | ||||
|  | ||||
| 			/* copy the numeric string to the destination buffer */ | ||||
| 			while (p > tmp && bp < be) *bp++ = *--p; | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		/* emit sign */ | ||||
| 		if (signchar && bp < be) *bp++ = signchar; | ||||
|  | ||||
| 		/* copy prefix if necessary */ | ||||
| 		if (prefix) while (*prefix && bp < be) *bp++ = *prefix++; | ||||
|  | ||||
| 		/* add 0s for precision */ | ||||
| 		while (preczero > 0 && bp < be)  | ||||
| 		{  | ||||
| 			*bp++ = QSE_WT('0'); | ||||
| 			preczero--;  | ||||
| 		} | ||||
|  | ||||
| 		/* copy the numeric string to the destination buffer */ | ||||
| 		while (p > tmp && bp < be) *bp++ = *--p; | ||||
| 	} | ||||
|  | ||||
| 	if (!(base_and_flags & QSE_FMTINTMAXTOWCS_NONULL)) *bp = QSE_WT('\0'); | ||||
| 	return bp - buf; | ||||
| } | ||||
|  | ||||
| int qse_fmtintmaxtowcs ( | ||||
| 	qse_wchar_t* buf, int size,  | ||||
| 	qse_intmax_t value, int base_and_flags, int prec, | ||||
| @ -546,3 +163,42 @@ int qse_fmtuintmaxtowcs ( | ||||
| 		buf, size, value, base_and_flags, prec, fillchar, signchar, prefix); | ||||
| } | ||||
|  | ||||
|  | ||||
| /* ==================== floating-point number =========================== */ | ||||
|  | ||||
| /* TODO: finish this function */ | ||||
| int qse_fmtfltmaxtombs (qse_mchar_t* buf, qse_size_t bufsize, qse_fltmax_t f, qse_mchar_t point, int digits) | ||||
| { | ||||
| 	qse_size_t len; | ||||
| 	qse_uintmax_t v; | ||||
|  | ||||
| 	/*if (bufsize <= 0) return -reqlen; TODO: */ | ||||
|  | ||||
| 	if (f < 0)  | ||||
| 	{ | ||||
| 		f *= -1; | ||||
| 		v = (qse_uintmax_t)f;   | ||||
| 		len = qse_fmtuintmaxtombs (buf, bufsize, v, 10, 0, QSE_MT('\0'), QSE_MT("-")); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		v = (qse_uintmax_t)f;   | ||||
| 		len = qse_fmtuintmaxtombs (buf, bufsize, v, 10, 0, QSE_MT('\0'), QSE_NULL); | ||||
| 	} | ||||
|   | ||||
| 	if (len + 1 < bufsize) | ||||
| 	{ | ||||
| 		buf[len++] = point;  // add decimal point to string | ||||
| 		buf[len] = QSE_MT('\0'); | ||||
| 	} | ||||
|          | ||||
| 	while (len + 1 < bufsize && digits-- > 0) | ||||
| 	{ | ||||
| 		f = (f - (qse_fltmax_t)v) * 10; | ||||
| 		v = (qse_uintmax_t)f; | ||||
| 		len += qse_fmtuintmaxtombs (&buf[len], bufsize - len, v, 10, 0, QSE_MT('\0'), QSE_NULL); | ||||
| 	} | ||||
|  | ||||
| 	return (int)len; | ||||
| } | ||||
|  | ||||
|  | ||||
							
								
								
									
										220
									
								
								qse/lib/cmn/fmt.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										220
									
								
								qse/lib/cmn/fmt.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,220 @@ | ||||
| /* | ||||
|  * $Id$ | ||||
|  * | ||||
|     Copyright 2006-2012 Chung, Hyung-Hwan. | ||||
|     This file is part of QSE. | ||||
|  | ||||
|     QSE is free software: you can redistribute it and/or modify | ||||
|     it under the terms of the GNU Lesser General Public License as  | ||||
|     published by the Free Software Foundation, either version 3 of  | ||||
|     the License, or (at your option) any later version. | ||||
|  | ||||
|     QSE is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU Lesser General Public License for more details. | ||||
|  | ||||
|     You should have received a copy of the GNU Lesser General Public  | ||||
|     License along with QSE. If not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| static int fmt_uintmax ( | ||||
| 	char_t* buf, int size,  | ||||
| 	qse_uintmax_t value, int base_and_flags, int prec, | ||||
| 	char_t fillchar, char_t signchar, const char_t* prefix) | ||||
| { | ||||
| 	char_t tmp[(QSE_SIZEOF(qse_uintmax_t) * 8)]; | ||||
| 	int reslen, base, fillsize, reqlen, pflen, preczero; | ||||
| 	char_t* p, * bp, * be; | ||||
| 	const char_t* xbasestr; | ||||
|  | ||||
| 	base = base_and_flags & 0x3F; | ||||
| 	if (base < 2 || base > 36) return -1; | ||||
|  | ||||
| 	xbasestr = (base_and_flags & QSE_FMTINTMAX_UPPERCASE)? | ||||
| 		T("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"): | ||||
| 		T("0123456789abcdefghijklmnopqrstuvwxyz"); | ||||
|  | ||||
| 	if ((base_and_flags & QSE_FMTINTMAX_NOZERO) && value == 0)  | ||||
| 	{ | ||||
| 		p = tmp;  | ||||
| 		if (base_and_flags & QSE_FMTINTMAX_ZEROLEAD)  | ||||
| 		{ | ||||
| 			/* NOZERO emits no digit, ZEROLEAD emits 1 digit. | ||||
| 			 * so it emits '0' */ | ||||
| 			reslen = 1; | ||||
| 			preczero = 1; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			/* since the value is zero, emit no digits */ | ||||
| 			reslen = 0; | ||||
| 			preczero = 0; | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		qse_uintmax_t v = value; | ||||
|  | ||||
| 		/* store the resulting numeric string into 'tmp' first */ | ||||
| 		p = tmp;  | ||||
| 		do | ||||
| 		{ | ||||
| 			*p++ = xbasestr[v % base]; | ||||
| 			v /= base; | ||||
| 		} | ||||
| 		while (v > 0); | ||||
|  | ||||
| 		/* reslen is the length of the resulting string without padding. */ | ||||
| 		reslen = (int)(p - tmp); | ||||
| 	 | ||||
| 		/* precision specified the minum number of digits to produce. | ||||
| 		 * so if the precision is larger that the digits produced,  | ||||
| 		 * reslen should be adjusted to precision */ | ||||
| 		if (prec > reslen)  | ||||
| 		{ | ||||
| 			/* if the precision is greater than the actual digits | ||||
| 			 * made from the value, 0 is inserted in front. | ||||
| 			 * ZEROLEAD doesn't have to be handled explicitly | ||||
| 			 * since it's achieved effortlessly */ | ||||
| 			preczero = prec - reslen; | ||||
| 			reslen = prec; | ||||
| 		} | ||||
| 		else  | ||||
| 		{ | ||||
| 			preczero = 0; | ||||
| 			if ((base_and_flags & QSE_FMTINTMAX_ZEROLEAD) && value != 0)  | ||||
| 			{ | ||||
| 				/* if value is zero, 0 is emitted from it.  | ||||
| 				 * so ZEROLEAD don't need to add another 0. */ | ||||
| 				preczero++; | ||||
| 				reslen++; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (signchar) reslen++; /* increment reslen for the sign character */ | ||||
| 	if (prefix) | ||||
| 	{ | ||||
| 		/* since the length can be truncated for different type sizes, | ||||
| 		 * don't pass in a very long prefix. */ | ||||
| 		pflen = (int) strlen(prefix); | ||||
| 		reslen += pflen; | ||||
| 	} | ||||
| 	else pflen = 0; | ||||
|  | ||||
| 	/* get the required buffer size for lossless formatting */ | ||||
| 	reqlen = (base_and_flags & QSE_FMTINTMAX_NONULL)? reslen: (reslen + 1); | ||||
|  | ||||
| 	if (size <= 0 || | ||||
| 	    ((base_and_flags & QSE_FMTINTMAX_NOTRUNC) && size < reqlen)) | ||||
| 	{ | ||||
| 		return -reqlen; | ||||
| 	} | ||||
|  | ||||
| 	/* get the size to fill with fill characters */ | ||||
| 	fillsize = (base_and_flags & QSE_FMTINTMAX_NONULL)? size: (size - 1); | ||||
| 	bp = buf; | ||||
| 	be = buf + fillsize; | ||||
|  | ||||
| 	/* fill space */ | ||||
| 	if (fillchar != T('\0')) | ||||
| 	{ | ||||
| 		if (base_and_flags & QSE_FMTINTMAX_FILLRIGHT) | ||||
| 		{ | ||||
| 			/* emit sign */ | ||||
| 			if (signchar && bp < be) *bp++ = signchar; | ||||
|  | ||||
| 			/* copy prefix if necessary */ | ||||
| 			if (prefix) while (*prefix && bp < be) *bp++ = *prefix++; | ||||
|  | ||||
| 			/* add 0s for precision */ | ||||
| 			while (preczero > 0 && bp < be)  | ||||
| 			{  | ||||
| 				*bp++ = T('0'); | ||||
| 				preczero--;  | ||||
| 			} | ||||
|  | ||||
| 			/* copy the numeric string to the destination buffer */ | ||||
| 			while (p > tmp && bp < be) *bp++ = *--p; | ||||
|  | ||||
| 			/* fill the right side */ | ||||
| 			while (fillsize > reslen) | ||||
| 			{ | ||||
| 				*bp++ = fillchar; | ||||
| 				fillsize--; | ||||
| 			} | ||||
| 		} | ||||
| 		else if (base_and_flags & QSE_FMTINTMAX_FILLCENTER) | ||||
| 		{ | ||||
| 			/* emit sign */ | ||||
| 			if (signchar && bp < be) *bp++ = signchar; | ||||
|  | ||||
| 			/* fill the left side */ | ||||
| 			while (fillsize > reslen) | ||||
| 			{ | ||||
| 				*bp++ = fillchar; | ||||
| 				fillsize--; | ||||
| 			} | ||||
|  | ||||
| 			/* copy prefix if necessary */ | ||||
| 			if (prefix) while (*prefix && bp < be) *bp++ = *prefix++; | ||||
|  | ||||
| 			/* add 0s for precision */ | ||||
| 			while (preczero > 0 && bp < be)  | ||||
| 			{  | ||||
| 				*bp++ = T('0'); | ||||
| 				preczero--;  | ||||
| 			} | ||||
|  | ||||
| 			/* copy the numeric string to the destination buffer */ | ||||
| 			while (p > tmp && bp < be) *bp++ = *--p; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			/* fill the left side */ | ||||
| 			while (fillsize > reslen) | ||||
| 			{ | ||||
| 				*bp++ = fillchar; | ||||
| 				fillsize--; | ||||
| 			} | ||||
|  | ||||
| 			/* emit sign */ | ||||
| 			if (signchar && bp < be) *bp++ = signchar; | ||||
|  | ||||
| 			/* copy prefix if necessary */ | ||||
| 			if (prefix) while (*prefix && bp < be) *bp++ = *prefix++; | ||||
|  | ||||
| 			/* add 0s for precision */ | ||||
| 			while (preczero > 0 && bp < be)  | ||||
| 			{  | ||||
| 				*bp++ = T('0'); | ||||
| 				preczero--;  | ||||
| 			} | ||||
|  | ||||
| 			/* copy the numeric string to the destination buffer */ | ||||
| 			while (p > tmp && bp < be) *bp++ = *--p; | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		/* emit sign */ | ||||
| 		if (signchar && bp < be) *bp++ = signchar; | ||||
|  | ||||
| 		/* copy prefix if necessary */ | ||||
| 		if (prefix) while (*prefix && bp < be) *bp++ = *prefix++; | ||||
|  | ||||
| 		/* add 0s for precision */ | ||||
| 		while (preczero > 0 && bp < be)  | ||||
| 		{  | ||||
| 			*bp++ = T('0'); | ||||
| 			preczero--;  | ||||
| 		} | ||||
|  | ||||
| 		/* copy the numeric string to the destination buffer */ | ||||
| 		while (p > tmp && bp < be) *bp++ = *--p; | ||||
| 	} | ||||
|  | ||||
| 	if (!(base_and_flags & QSE_FMTINTMAX_NONULL)) *bp = T('\0'); | ||||
| 	return bp - buf; | ||||
| } | ||||
| @ -23,6 +23,7 @@ | ||||
| #include <qse/cmn/str.h> | ||||
| #include <qse/cmn/mbwc.h> | ||||
| #include <stdarg.h> | ||||
| #include "mem.h" | ||||
|  | ||||
| /* number of bits in a byte */ | ||||
| #define NBBY    8                | ||||
| @ -97,7 +98,7 @@ enum | ||||
|  | ||||
| #include <stdio.h> /* TODO: remove dependency on this */ | ||||
|  | ||||
| static void put_wchar (qse_wchar_t c, void *arg) | ||||
| static int put_wchar (qse_wchar_t c, void *arg) | ||||
| { | ||||
| 	qse_cmgr_t* cmgr; | ||||
| 	qse_mchar_t mbsbuf[QSE_MBLEN_MAX + 1]; | ||||
| @ -107,18 +108,22 @@ static void put_wchar (qse_wchar_t c, void *arg) | ||||
| 	n = cmgr->wctomb (c, mbsbuf, QSE_COUNTOF(mbsbuf)); | ||||
| 	if (n <= 0 || n > QSE_COUNTOF(mbsbuf)) | ||||
| 	{ | ||||
| 		putchar ('?'); | ||||
| 		return (putchar ('?') == EOF)? -1: 0; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		qse_size_t i; | ||||
| 		for (i = 0; i < n; i++) putchar (mbsbuf[i]); | ||||
| 		for (i = 0; i < n; i++)  | ||||
| 		{ | ||||
| 			if (putchar (mbsbuf[i]) == EOF) return -1; | ||||
| 		} | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void put_mchar (qse_mchar_t c, void *arg) | ||||
| static int put_mchar (qse_mchar_t c, void *arg) | ||||
| { | ||||
| 	putchar (c); | ||||
| 	return (putchar (c) == EOF)? -1: 0; | ||||
| } | ||||
|  | ||||
| #undef char_t | ||||
|  | ||||
| @ -86,16 +86,16 @@ static char_t* sprintn (char_t* nbuf, qse_uintmax_t num, int base, int *lenp, in | ||||
|  | ||||
| /* TODO: error check */ | ||||
| #define PUT_CHAR(c) do { \ | ||||
| 	put_char (c, arg); \ | ||||
| 	if (put_char (c, arg) <= -1) goto oops; \ | ||||
| 	retval++; \ | ||||
| } while (0) | ||||
|  | ||||
| #define PUT_OCHAR(c) do { \ | ||||
| 	put_ochar (c, arg); \ | ||||
| 	if (put_ochar (c, arg) <= -1) goto oops; \ | ||||
| 	retval++; \ | ||||
| } while (0) | ||||
|  | ||||
| int xprintf (char_t const *fmt, void (*put_char)(char_t, void*), void (*put_ochar) (ochar_t, void*), void *arg, va_list ap) | ||||
| int xprintf (char_t const *fmt, int (*put_char)(char_t, void*), int (*put_ochar) (ochar_t, void*), void *arg, va_list ap) | ||||
| { | ||||
| 	char_t nbuf[MAXNBUF]; | ||||
| 	const char_t* p, * percent; | ||||
| @ -108,6 +108,10 @@ int xprintf (char_t const *fmt, void (*put_char)(char_t, void*), void (*put_ocha | ||||
| 	int stop = 0, retval = 0; | ||||
| 	char_t fltbuf[100]; | ||||
|  | ||||
| 	qse_mchar_t fltfmt[64]; | ||||
| 	qse_mchar_t* fltfmtp = QSE_NULL; | ||||
| 	qse_size_t fltfmtc; | ||||
|  | ||||
| 	while (1) | ||||
| 	{ | ||||
| 		while ((ch = (uchar_t)*fmt++) != T('%') || stop)  | ||||
| @ -397,7 +401,7 @@ reswitch: | ||||
| 			if (((lm_flag & LF_H) && (QSE_SIZEOF(char_t) > QSE_SIZEOF(ochar_t))) || | ||||
| 			    ((lm_flag & LF_L) && (QSE_SIZEOF(char_t) < QSE_SIZEOF(ochar_t)))) goto uppercase_s; | ||||
| 		lowercase_s: | ||||
| 			sp = va_arg (ap, char_t *); | ||||
| 			sp = va_arg (ap, char_t*); | ||||
| 			if (sp == QSE_NULL) p = T("(null)"); | ||||
| 		 | ||||
| 		print_lowercase_s: | ||||
| @ -467,7 +471,8 @@ reswitch: | ||||
| 		*/ | ||||
| 		{ | ||||
| 			qse_mchar_t tmpbuf[100]; | ||||
| 			qse_mchar_t tmpfmt[100]; | ||||
| 			qse_mchar_t* tmpbufp; | ||||
|  | ||||
| 			const char_t* xx; | ||||
| 			const qse_mchar_t* yy; | ||||
| 			int q; | ||||
| @ -475,12 +480,24 @@ reswitch: | ||||
| 		#if defined(NO_MERCY) | ||||
| 			if (lm_flag & ~LF_LD) goto invalid_format; | ||||
| 		#endif | ||||
| 			for (xx = percent, q = 0; xx < fmt; xx++) tmpfmt[q++] = *xx; | ||||
| 			tmpfmt[q] = QSE_MT('\0'); | ||||
| 			if (lm_flag & LF_LD) | ||||
| 				snprintf (tmpbuf, QSE_SIZEOF(tmpbuf), tmpfmt, va_arg (ap, long double)); | ||||
|  | ||||
| 			if (fmt - percent < QSE_COUNTOF(fltfmt)) | ||||
| 			{ | ||||
| 				fltfmtp = fltfmt; | ||||
| 			} | ||||
| 			else | ||||
| 				snprintf (tmpbuf, QSE_SIZEOF(tmpbuf), tmpfmt, va_arg (ap, double)); | ||||
| 			{ | ||||
| 				fltfmtp = QSE_MMGR_ALLOC (QSE_MMGR_GETDFL(), QSE_SIZEOF(*fltfmtp) * (fmt - percent + 1)); | ||||
| 				if (fltfmtp == QSE_NULL) goto oops; | ||||
| 			} | ||||
| 			for (xx = percent, q = 0; xx < fmt; xx++) fltfmtp[q++] = *xx; | ||||
| 			fltfmtp[q] = QSE_MT('\0'); | ||||
|  | ||||
| 			if (lm_flag & LF_LD) | ||||
| 				snprintf (tmpbuf, QSE_SIZEOF(tmpbuf), fltfmtp, va_arg (ap, long double)); | ||||
| 			else | ||||
| 				snprintf (tmpbuf, QSE_SIZEOF(tmpbuf), fltfmtp, va_arg (ap, double)); | ||||
|  | ||||
| 			for (yy = tmpbuf, q = 0; *yy; ) fltbuf[q++] = *yy++; | ||||
| 			fltbuf[q] = QSE_T('\0'); | ||||
| 			sp = fltbuf;	 | ||||
| @ -488,7 +505,6 @@ reswitch: | ||||
| 			width = 0; | ||||
| 			precision = 0; | ||||
| 			goto print_lowercase_s; | ||||
| 			break; | ||||
| 		} | ||||
|  | ||||
| handle_nosign: | ||||
| @ -667,6 +683,13 @@ invalid_format: | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| oops: | ||||
| 	if (fltfmtp && fltfmtp != fltfmt) | ||||
| 	{ | ||||
| 		QSE_MMGR_FREE (QSE_MMGR_GETDFL(), fltfmtp); | ||||
| 	} | ||||
| 	return -1; | ||||
| } | ||||
| #undef PUT_CHAR | ||||
| #undef PUT_OCHAR | ||||
|  | ||||
		Reference in New Issue
	
	Block a user