From b70e378041f6e4ca31158629a2f33400fa483e4c Mon Sep 17 00:00:00 2001 From: hyung-hwan Date: Tue, 15 Oct 2013 16:35:23 +0000 Subject: [PATCH] added some code to format floating-point numbers --- qse/include/qse/cmn/fmt.h | 8 + qse/include/qse/types.h | 11 +- qse/lib/cmn/fmt.c | 466 +++++--------------------------------- qse/lib/cmn/fmt.h | 220 ++++++++++++++++++ qse/lib/cmn/printf.c | 15 +- qse/lib/cmn/printf.h | 45 +++- 6 files changed, 339 insertions(+), 426 deletions(-) create mode 100644 qse/lib/cmn/fmt.h diff --git a/qse/include/qse/cmn/fmt.h b/qse/include/qse/cmn/fmt.h index 38496bc7..c8026ff7 100644 --- a/qse/include/qse/cmn/fmt.h +++ b/qse/include/qse/cmn/fmt.h @@ -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 diff --git a/qse/include/qse/types.h b/qse/include/qse/types.h index c6f1849a..bf9191d3 100644 --- a/qse/include/qse/types.h +++ b/qse/include/qse/types.h @@ -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. diff --git a/qse/lib/cmn/fmt.c b/qse/lib/cmn/fmt.c index 17b15388..d8552d4a 100644 --- a/qse/lib/cmn/fmt.c +++ b/qse/lib/cmn/fmt.c @@ -21,210 +21,30 @@ #include #include +#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; +} + diff --git a/qse/lib/cmn/fmt.h b/qse/lib/cmn/fmt.h new file mode 100644 index 00000000..a433a00c --- /dev/null +++ b/qse/lib/cmn/fmt.h @@ -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 . + */ + +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; +} diff --git a/qse/lib/cmn/printf.c b/qse/lib/cmn/printf.c index 2d2b21e8..18383087 100644 --- a/qse/lib/cmn/printf.c +++ b/qse/lib/cmn/printf.c @@ -23,6 +23,7 @@ #include #include #include +#include "mem.h" /* number of bits in a byte */ #define NBBY 8 @@ -97,7 +98,7 @@ enum #include /* 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 diff --git a/qse/lib/cmn/printf.h b/qse/lib/cmn/printf.h index 71034b3b..6962b127 100644 --- a/qse/lib/cmn/printf.h +++ b/qse/lib/cmn/printf.h @@ -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