enhanced formatted output function

This commit is contained in:
hyung-hwan 2013-10-12 17:04:21 +00:00
parent 561dbe49f5
commit fde2e1a3b0
2 changed files with 209 additions and 118 deletions

View File

@ -56,9 +56,21 @@ static struct
{ LF_Z, 0 }, /* z */ { LF_Z, 0 }, /* z */
}; };
enum
{
FLAGC_DOT = (1 << 0),
FLAGC_SHARP = (1 << 1),
FLAGC_SIGN = (1 << 2),
FLAGC_SPACE = (1 << 3),
FLAGC_LEFTADJ = (1 << 4),
FLAGC_ZEROPAD = (1 << 5),
FLAGC_WIDTH = (1 << 6)
};
#include <stdio.h> /* TODO: remove dependency on this */ #include <stdio.h> /* TODO: remove dependency on this */
static void xputwchar (qse_wchar_t c, void *arg) static void put_wchar (qse_wchar_t c, void *arg)
{ {
qse_cmgr_t* cmgr; qse_cmgr_t* cmgr;
qse_mchar_t mbsbuf[QSE_MBLEN_MAX + 1]; qse_mchar_t mbsbuf[QSE_MBLEN_MAX + 1];
@ -77,7 +89,7 @@ static void xputwchar (qse_wchar_t c, void *arg)
} }
} }
static void xputmchar (qse_mchar_t c, void *arg) static void put_mchar (qse_mchar_t c, void *arg)
{ {
putchar (c); putchar (c);
} }
@ -111,14 +123,14 @@ int qse_mprintf (const char_t *fmt, ...)
va_list ap; va_list ap;
int n; int n;
va_start (ap, fmt); va_start (ap, fmt);
n = qse_mxprintf (fmt, xputmchar, xputwchar, QSE_NULL, ap); n = qse_mxprintf (fmt, put_mchar, put_wchar, QSE_NULL, ap);
va_end (ap); va_end (ap);
return n; return n;
} }
int qse_mvprintf (const char_t* fmt, va_list ap) int qse_mvprintf (const char_t* fmt, va_list ap)
{ {
return qse_mxprintf (fmt, xputmchar, xputwchar, QSE_NULL, ap); return qse_mxprintf (fmt, put_mchar, put_wchar, QSE_NULL, ap);
} }
/* ------------------------------------------------------------------ */ /* ------------------------------------------------------------------ */
@ -142,7 +154,8 @@ int qse_mvprintf (const char_t* fmt, va_list ap)
#define sprintn w_sprintn #define sprintn w_sprintn
#define xprintf qse_wxprintf #define xprintf qse_wxprintf
static const qse_wchar_t w_hex2ascii[] = QSE_WT("0123456789abcdefghijklmnopqrstuvwxyz"); static const qse_wchar_t w_hex2ascii[] =
QSE_WT("0123456789abcdefghijklmnopqrstuvwxyz");
#define hex2ascii(hex) (w_hex2ascii[hex]) #define hex2ascii(hex) (w_hex2ascii[hex])
#include "printf.h" #include "printf.h"
@ -152,12 +165,12 @@ int qse_wprintf (const char_t *fmt, ...)
va_list ap; va_list ap;
int n; int n;
va_start (ap, fmt); va_start (ap, fmt);
n = qse_wxprintf (fmt, xputwchar, xputmchar, QSE_NULL, ap); n = qse_wxprintf (fmt, put_wchar, put_mchar, QSE_NULL, ap);
va_end (ap); va_end (ap);
return n; return n;
} }
int qse_wvprintf (const char_t* fmt, va_list ap) int qse_wvprintf (const char_t* fmt, va_list ap)
{ {
return qse_wxprintf (fmt, xputwchar, xputmchar, QSE_NULL, ap); return qse_wxprintf (fmt, put_wchar, put_mchar, QSE_NULL, ap);
} }

View File

@ -59,97 +59,143 @@ static char_t* sprintn (char_t* nbuf, qse_uintmax_t num, int base, int *lenp, in
while (num /= base); while (num /= base);
if (lenp) *lenp = p - nbuf; if (lenp) *lenp = p - nbuf;
return (p); return p;
} }
#define PCHAR(c) do { \ #undef PUT_CHAR
func (c, arg); \ #undef PUT_OCHAR
/* TODO: error check */
#define PUT_CHAR(c) do { \
put_char (c, arg); \
retval++; \ retval++; \
} while (0) } while (0)
#define OPCHAR(c) do { \ #define PUT_OCHAR(c) do { \
ofunc (c, arg); \ put_ochar (c, arg); \
retval++; \ retval++; \
} while (0) } while (0)
int xprintf (char_t const *fmt, void (*func)(char_t, void*), void (*ofunc) (ochar_t, void*), void *arg, va_list ap) int xprintf (char_t const *fmt, void (*put_char)(char_t, void*), void (*put_ochar) (ochar_t, void*), void *arg, va_list ap)
{ {
char_t nbuf[MAXNBUF]; char_t nbuf[MAXNBUF];
const char_t* p, * percent; const char_t* p, * percent;
uchar_t ch; uchar_t ch;
int n; int n;
qse_uintmax_t num; qse_uintmax_t num;
int base, tmp, width, ladjust, sharpflag, neg, sign, dot; int base, tmp, width, neg, sign;
int dwidth, upper; int precision, upper;
char_t padc, * sp; char_t padc, * sp;
ochar_t opadc, * osp; ochar_t opadc, * osp;
int stop = 0, retval = 0; int stop = 0, retval = 0;
int lm_flag, lm_dflag; int lm_flag, lm_dflag, flagc;
int numlen;
num = 0; num = 0;
if (fmt == QSE_NULL) fmt = T("(fmt null)\n"); /*if (fmt == QSE_NULL) fmt = T("(fmt null)\n");*/
while (1) while (1)
{ {
padc = T(' ');
opadc = OT(' ');
width = 0;
while ((ch = (uchar_t)*fmt++) != T('%') || stop) while ((ch = (uchar_t)*fmt++) != T('%') || stop)
{ {
if (ch == T('\0')) return (retval); if (ch == T('\0')) return retval;
PCHAR(ch); PUT_CHAR(ch);
} }
percent = fmt - 1; percent = fmt - 1;
ladjust = 0; sharpflag = 0; neg = 0;
sign = 0; dot = 0; dwidth = 0; upper = 0;
lm_flag = 0; lm_dflag = 0; padc = T(' '); opadc = OT(' ');
width = 0; precision = 0;
neg = 0; sign = 0; upper = 0;
lm_flag = 0; lm_dflag = 0; flagc = 0;
reswitch: reswitch:
switch (ch = (uchar_t)*fmt++) switch (ch = (uchar_t)*fmt++)
{ {
case T('%'): /* %% */ case T('%'): /* %% */
PCHAR(ch); PUT_CHAR(ch);
break; break;
/* flag characters */
case T('.'): case T('.'):
dot = 1; flagc |= FLAGC_DOT;
goto reswitch;
case T('#'):
sharpflag = 1;
goto reswitch;
case T('+'):
sign = 1;
goto reswitch;
case T('-'):
ladjust = 1;
goto reswitch; goto reswitch;
case T('*'): case T('#'):
if (!dot) if (flagc & (FLAGC_WIDTH | FLAGC_DOT)) goto invalid_format;
flagc |= FLAGC_SHARP;
goto reswitch;
case T(' '):
if (flagc & (FLAGC_WIDTH | FLAGC_DOT)) goto invalid_format;
flagc |= FLAGC_SPACE;
goto reswitch;
case T('+'): /* place sign for signed conversion */
if (flagc & (FLAGC_WIDTH | FLAGC_DOT)) goto invalid_format;
flagc |= FLAGC_SIGN;
goto reswitch;
case T('-'): /* left adjusted */
if (flagc & (FLAGC_WIDTH | FLAGC_DOT)) goto invalid_format;
if (flagc & FLAGC_DOT)
{
goto invalid_format;
}
else
{
flagc |= FLAGC_LEFTADJ;
if (flagc & FLAGC_ZEROPAD)
{
padc = T(' ');
opadc = OT(' ');
flagc &= ~FLAGC_ZEROPAD;
}
}
goto reswitch;
case T('*'): /* take the length from the parameter */
if (!(flagc & FLAGC_DOT))
{ {
width = va_arg(ap, int); width = va_arg(ap, int);
if (width < 0) if (width < 0)
{ {
ladjust = !ladjust; /*
if (flagc & FLAGC_LEFTADJ)
flagc &= ~FLAGC_LEFTADJ;
else
*/
flagc |= FLAGC_LEFTADJ;
width = -width; width = -width;
} }
} }
else else
{ {
dwidth = va_arg(ap, int); precision = va_arg(ap, int);
if (precision < 0)
{
/* if precision is less than 0,
* treat it as if no .precision is specified */
flagc &= ~FLAGC_DOT;
precision = 0;
}
} }
goto reswitch; goto reswitch;
case T('0'): case T('0'): /* zero pad */
if (!dot) if (flagc & (FLAGC_WIDTH | FLAGC_DOT)) goto invalid_format;
if (!(flagc & FLAGC_LEFTADJ))
{ {
padc = T('0'); padc = T('0');
opadc = OT('0'); opadc = OT('0');
flagc |= FLAGC_ZEROPAD;
goto reswitch; goto reswitch;
} }
/* end of flags characters */
case T('1'): case T('2'): case T('3'): case T('4'): case T('1'): case T('2'): case T('3'): case T('4'):
case T('5'): case T('6'): case T('7'): case T('8'): case T('9'): case T('5'): case T('6'): case T('7'): case T('8'): case T('9'):
for (n = 0;; ++fmt) for (n = 0;; ++fmt)
@ -158,32 +204,15 @@ reswitch:
ch = *fmt; ch = *fmt;
if (ch < T('0') || ch > T('9')) break; if (ch < T('0') || ch > T('9')) break;
} }
if (dot) dwidth = n; if (flagc & FLAGC_DOT) precision = n;
else width = n; else
{
width = n;
flagc |= FLAGC_WIDTH;
}
goto reswitch; goto reswitch;
case T('c'): /* length modifiers */
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_c;
lowercase_c:
if (QSE_SIZEOF(char_t) < QSE_SIZEOF(int))
PCHAR(va_arg(ap, int));
else
PCHAR(va_arg(ap, char_t));
break;
case T('C'):
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 lowercase_c;
uppercase_c:
if (QSE_SIZEOF(ochar_t) < QSE_SIZEOF(int))
OPCHAR(va_arg(ap, int));
else
OPCHAR(va_arg(ap, ochar_t));
break;
/* length modifiers */
case T('h'): /* short int */ case T('h'): /* short int */
case T('l'): /* long int */ case T('l'): /* long int */
case T('q'): /* long long int */ case T('q'): /* long long int */
@ -193,10 +222,10 @@ reswitch:
if (lm_dflag) if (lm_dflag)
{ {
/* error */ /* error */
PCHAR (fmt[-4]); PUT_CHAR (fmt[-4]);
PCHAR (fmt[-3]); PUT_CHAR (fmt[-3]);
PCHAR (fmt[-2]); PUT_CHAR (fmt[-2]);
PCHAR (fmt[-1]); PUT_CHAR (fmt[-1]);
break; break;
} }
else if (lm_flag) else if (lm_flag)
@ -211,9 +240,9 @@ reswitch:
else else
{ {
/* error */ /* error */
PCHAR (fmt[-3]); PUT_CHAR (fmt[-3]);
PCHAR (fmt[-2]); PUT_CHAR (fmt[-2]);
PCHAR (fmt[-1]); PUT_CHAR (fmt[-1]);
break; break;
} }
} }
@ -223,7 +252,7 @@ reswitch:
goto reswitch; goto reswitch;
} }
break; break;
/* end of length modifiers */ /* end of length modifiers */
case T('n'): case T('n'):
if (lm_flag & LF_J) if (lm_flag & LF_J)
@ -244,14 +273,19 @@ reswitch:
*(va_arg(ap, int*)) = retval; *(va_arg(ap, int*)) = retval;
break; break;
case T('o'):
base = 8; /* signed integer conversions */
goto handle_nosign;
case T('d'): case T('d'):
case T('i'): case T('i'): /* signed conversion */
base = 10; base = 10;
sign = 1; sign = 1;
goto handle_sign; goto handle_sign;
/* end of signed integer conversions */
/* unsigned integer conversions */
case T('o'):
base = 8;
goto handle_nosign;
case T('u'): case T('u'):
base = 10; base = 10;
goto handle_nosign; goto handle_nosign;
@ -260,18 +294,37 @@ reswitch:
case T('x'): case T('x'):
base = 16; base = 16;
goto handle_nosign; goto handle_nosign;
case T('y'): /* end of unsigned integer conversions */
base = 16;
sign = 1;
goto handle_sign;
case T('p'): case T('p'): /* pointer */
base = 16; base = 16;
sharpflag = (width == 0);
sign = 0; if (width == 0) flagc |= FLAGC_SHARP;
num = (qse_uintptr_t)va_arg(ap, void *); else flagc &= ~FLAGC_SHARP;
num = (qse_uintptr_t)va_arg(ap, void*);
goto number; goto number;
case T('c'):
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_c;
lowercase_c:
if (QSE_SIZEOF(char_t) < QSE_SIZEOF(int))
PUT_CHAR(va_arg(ap, int));
else
PUT_CHAR(va_arg(ap, char_t));
break;
case T('C'):
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 lowercase_c;
uppercase_c:
if (QSE_SIZEOF(ochar_t) < QSE_SIZEOF(int))
PUT_OCHAR(va_arg(ap, int));
else
PUT_OCHAR(va_arg(ap, ochar_t));
break;
case T('s'): case T('s'):
{ {
if (((lm_flag & LF_H) && (QSE_SIZEOF(char_t) > QSE_SIZEOF(ochar_t))) || if (((lm_flag & LF_H) && (QSE_SIZEOF(char_t) > QSE_SIZEOF(ochar_t))) ||
@ -279,7 +332,7 @@ reswitch:
lowercase_s: lowercase_s:
sp = va_arg (ap, char_t *); sp = va_arg (ap, char_t *);
if (sp == QSE_NULL) p = T("(null)"); if (sp == QSE_NULL) p = T("(null)");
if (!dot) if (!(flagc & FLAGC_DOT))
{ {
char_t* p = sp; char_t* p = sp;
while (*p) p++; while (*p) p++;
@ -287,19 +340,19 @@ reswitch:
} }
else else
{ {
for (n = 0; n < dwidth && sp[n]; n++) continue; for (n = 0; n < precision && sp[n]; n++) continue;
} }
width -= n; width -= n;
if (!ladjust && width > 0) if (!(flagc & FLAGC_LEFTADJ) && width > 0)
{ {
while (width--) PCHAR(padc); while (width--) PUT_CHAR(padc);
} }
while (n--) PCHAR(*sp++); while (n--) PUT_CHAR(*sp++);
if (ladjust && width > 0) if ((flagc & FLAGC_LEFTADJ) && width > 0)
{ {
while (width--) PCHAR(padc); while (width--) PUT_CHAR(padc);
} }
break; break;
} }
@ -311,7 +364,7 @@ reswitch:
uppercase_s: uppercase_s:
osp = va_arg (ap, ochar_t*); osp = va_arg (ap, ochar_t*);
if (osp == QSE_NULL) osp = OT("(null)"); if (osp == QSE_NULL) osp = OT("(null)");
if (!dot) if (!(flagc & FLAGC_DOT))
{ {
ochar_t* p = osp; ochar_t* p = osp;
while (*p) p++; while (*p) p++;
@ -319,19 +372,19 @@ reswitch:
} }
else else
{ {
for (n = 0; n < dwidth && osp[n]; n++) continue; for (n = 0; n < precision && osp[n]; n++) continue;
} }
width -= n; width -= n;
if (!ladjust && width > 0) if (!(flagc & FLAGC_LEFTADJ) && width > 0)
{ {
while (width--) OPCHAR (opadc); while (width--) PUT_OCHAR (opadc);
} }
while (n--) OPCHAR(*osp++); while (n--) PUT_OCHAR(*osp++);
if (ladjust && width > 0) if ((flagc & FLAGC_LEFTADJ) && width > 0)
{ {
while (width--) OPCHAR (opadc); while (width--) PUT_OCHAR (opadc);
} }
break; break;
} }
@ -391,46 +444,71 @@ number:
num = -(qse_intmax_t)num; num = -(qse_intmax_t)num;
} }
p = sprintn (nbuf, num, base, &tmp, upper); p = sprintn (nbuf, num, base, &tmp, upper);
if (sharpflag && num != 0) if ((flagc & FLAGC_SHARP) && num != 0)
{ {
if (base == 8) tmp++; if (base == 8) tmp++;
else if (base == 16) tmp += 2; else if (base == 16) tmp += 2;
} }
if (neg) tmp++; if (neg) tmp++;
else if (flagc & FLAGC_SIGN) tmp++;
else if (flagc & FLAGC_SPACE) tmp++;
if (!ladjust && padc != T('0') && width && (width -= tmp) > 0) numlen = p - nbuf;
if ((flagc & FLAGC_DOT) && precision > numlen)
{ {
while (width--) PCHAR(padc); /* extra zeros fro precision specified */
tmp += (precision - numlen);
} }
if (neg) PCHAR(T('-'));
if (sharpflag && num != 0) if (!(flagc & FLAGC_LEFTADJ) && !(flagc & FLAGC_ZEROPAD) && width > 0 && (width -= tmp) > 0)
{ {
if (base == 8) { while (width--) PUT_CHAR(padc);
PCHAR(T('0')); }
if (neg) PUT_CHAR(T('-'));
else if (flagc & FLAGC_SIGN) PUT_CHAR(T('+'));
else if (flagc & FLAGC_SPACE) PUT_CHAR(T(' '));
if ((flagc & FLAGC_SHARP) && num != 0)
{
if (base == 8)
{
PUT_CHAR(T('0'));
} }
else if (base == 16) else if (base == 16)
{ {
PCHAR(T('0')); PUT_CHAR(T('0'));
PCHAR(T('x')); PUT_CHAR(T('x'));
} }
} }
if (!ladjust && width && (width -= tmp) > 0)
{
while (width--) PCHAR(padc);
}
while (*p) PCHAR(*p--); if ((flagc & FLAGC_DOT) && precision > numlen)
if (ladjust && width && (width -= tmp) > 0)
{ {
while (width--) PCHAR(padc); /* extra zeros for precision specified */
while (numlen < precision)
{
PUT_CHAR (T('0'));
numlen++;
}
}
if (!(flagc & FLAGC_LEFTADJ) && width > 0 && (width -= tmp) > 0)
{
while (width-- > 0) PUT_CHAR (padc);
}
while (*p) PUT_CHAR(*p--); /* output actual digits */
if ((flagc & FLAGC_LEFTADJ) && width > 0 && (width -= tmp) > 0)
{
while (width-- > 0) PUT_CHAR (padc);
} }
break; break;
default: default:
while (percent < fmt) PCHAR(*percent++); invalid_format:
while (percent < fmt) PUT_CHAR(*percent++);
/* /*
* Since we ignore an formatting argument it is no * Since we ignore an formatting argument it is no
* longer safe to obey the remaining formatting * longer safe to obey the remaining formatting
@ -442,6 +520,6 @@ number:
} }
} }
} }
#undef PCHAR #undef PUT_CHAR
#undef OPCHAR #undef PUT_OCHAR