mirror of
https://github.com/reactos/reactos.git
synced 2024-10-31 20:02:55 +00:00
1707 lines
34 KiB
C
1707 lines
34 KiB
C
/* @(#)format.c 1.62 17/08/03 Copyright 1985-2017 J. Schilling */
|
|
/*
|
|
* format
|
|
* common code for printf fprintf & sprintf
|
|
*
|
|
* allows recursive printf with "%r", used in:
|
|
* error, comerr, comerrno, errmsg, errmsgno and the like
|
|
*
|
|
* Copyright (c) 1985-2017 J. Schilling
|
|
*/
|
|
/*
|
|
* The contents of this file are subject to the terms of the
|
|
* Common Development and Distribution License, Version 1.0 only
|
|
* (the "License"). You may not use this file except in compliance
|
|
* with the License.
|
|
*
|
|
* See the file CDDL.Schily.txt in this distribution for details.
|
|
* A copy of the CDDL is also available via the Internet at
|
|
* http://www.opensource.org/licenses/cddl1.txt
|
|
*
|
|
* When distributing Covered Code, include this CDDL HEADER in each
|
|
* file and include the License file CDDL.Schily.txt from this distribution.
|
|
*/
|
|
|
|
#include <schily/mconfig.h>
|
|
#include <schily/varargs.h>
|
|
#include <schily/string.h>
|
|
#include <schily/stdlib.h>
|
|
#ifdef DEBUG
|
|
#include <schily/unistd.h>
|
|
#endif
|
|
#if !defined(HAVE_STDLIB_H) || !defined(HAVE_GCVT)
|
|
extern char *gcvt __PR((double, int, char *));
|
|
#endif
|
|
#include <schily/standard.h>
|
|
#include <schily/utypes.h>
|
|
#include <schily/schily.h>
|
|
|
|
/*
|
|
* As Llong is currently a 'best effort' long long, we usually need to
|
|
* include long long print formats.
|
|
* This may go away, if we implement maxint_t formats.
|
|
*/
|
|
#ifndef USE_LONGLONG
|
|
#define USE_LONGLONG
|
|
#endif
|
|
|
|
#ifdef NO_LONGLONG
|
|
#undef USE_LONGLONG
|
|
#endif
|
|
|
|
#ifndef USE_NL_ARGS
|
|
#define USE_NL_ARGS
|
|
#endif
|
|
|
|
#ifdef NO_NL_ARGS
|
|
#undef USE_NL_ARGS
|
|
#endif
|
|
|
|
/*
|
|
* Avoid to keep copies of the variable arg list in case that
|
|
* format() was compiled without including NL support for
|
|
* argument reordering.
|
|
*/
|
|
#ifdef USE_NL_ARGS
|
|
#define args fargs.ap /* Use working copy */
|
|
#else
|
|
#define args oargs /* Directly use format() arg */
|
|
#endif
|
|
|
|
/*
|
|
* We may need to decide whether we should check whether all
|
|
* flags occur according to the standard which is either directly past:
|
|
* "%" or directly past "%n$".
|
|
*
|
|
* This however may make printf() slower in some cases.
|
|
*/
|
|
#ifdef USE_CHECKFLAG
|
|
#define CHECKFLAG() if (fa.flags & GOTSTAR) goto flagerror
|
|
#else
|
|
#define CHECKFLAG()
|
|
#endif
|
|
|
|
#ifdef NO_USER_XCVT
|
|
/*
|
|
* We cannot define our own gcvt() so we need to use a
|
|
* local name instead.
|
|
*/
|
|
#ifndef HAVE_GCVT
|
|
# define gcvt js_gcvt
|
|
EXPORT char *gcvt __PR((double value, int ndigit, char *buf));
|
|
#endif
|
|
#endif
|
|
|
|
/*
|
|
* Some CPU's (e.g. PDP-11) cannot do logical shifts.
|
|
* They use rotate instead. Masking the low bits before,
|
|
* makes rotate work too.
|
|
*/
|
|
#define allmask(t) ((unsigned t)~((unsigned t)0))
|
|
#define lowmask(t, x) ((unsigned t)~((unsigned t)((1 << (x))-1)))
|
|
#define rshiftmask(t, s)((allmask(t) & lowmask(t, s)) >> (s))
|
|
|
|
#define CHARMASK makemask(char)
|
|
#define SHORTMASK makemask(short)
|
|
#define INTMASK makemask(int)
|
|
#define LONGMASK makemask(long)
|
|
|
|
#ifdef DIVLBYS
|
|
extern long divlbys();
|
|
extern long modlbys();
|
|
#else
|
|
#define divlbys(val, base) ((val)/(base))
|
|
#define modlbys(val, base) ((val)%(base))
|
|
#endif
|
|
|
|
/*
|
|
* We use macros here to avoid the need to link to the international
|
|
* character routines.
|
|
* We don't need internationalization for our purpose.
|
|
*/
|
|
#define is_dig(c) (((c) >= '0') && ((c) <= '9'))
|
|
#define is_cap(c) ((c) >= 'A' && (c) <= 'Z')
|
|
#define to_cap(c) (is_cap(c) ? c : c - 'a' + 'A')
|
|
#define cap_ty(c) (is_cap(c) ? 'L' : 'I')
|
|
|
|
#ifdef HAVE_LONGLONG
|
|
typedef union {
|
|
Ullong ll;
|
|
Ulong l[2];
|
|
char c[8];
|
|
} quad_u;
|
|
#endif
|
|
|
|
typedef struct f_args {
|
|
#ifdef FORMAT_BUFFER
|
|
#define BFSIZ 256
|
|
char *ptr; /* Current ptr in buf */
|
|
int cnt; /* Free char count in buf */
|
|
#else
|
|
void (*outf)__PR((char, void *)); /* Func from format(fun, arg) */
|
|
#endif
|
|
void *farg; /* Arg from format (fun, arg) */
|
|
int minusflag; /* Fieldwidth is negative */
|
|
int flags; /* General flags (+-#) */
|
|
int fldwidth; /* Field width as in %3d */
|
|
int signific; /* Significant chars as in %.4d */
|
|
int lzero; /* Left '0' pad flag */
|
|
char *buf; /* Out print buffer */
|
|
char *bufp; /* Write ptr into buffer */
|
|
char fillc; /* Left fill char (' ' or '0') */
|
|
char *prefix; /* Prefix to print before buf */
|
|
int prefixlen; /* Len of prefix ('+','-','0x') */
|
|
#ifdef FORMAT_BUFFER
|
|
/* rarely used members last: */
|
|
char iobuf[BFSIZ]; /* buffer for stdio */
|
|
FILE *fp; /* FILE * for fprformat() */
|
|
int err; /* FILE * I/O error */
|
|
#endif
|
|
} f_args;
|
|
|
|
#define MINUSFLG 1 /* '-' flag */
|
|
#define PLUSFLG 2 /* '+' flag */
|
|
#define SPACEFLG 4 /* ' ' flag */
|
|
#define HASHFLG 8 /* '#' flag */
|
|
#define APOFLG 16 /* '\'' flag */
|
|
#define GOTDOT 32 /* '.' found */
|
|
#define GOTSTAR 64 /* '*' found */
|
|
|
|
#define FMT_ARGMAX 30 /* Number of fast args */
|
|
|
|
LOCAL void prnum __PR((Ulong, unsigned, f_args *));
|
|
LOCAL void prdnum __PR((Ulong, f_args *));
|
|
LOCAL void pronum __PR((Ulong, f_args *));
|
|
LOCAL void prxnum __PR((Ulong, f_args *));
|
|
LOCAL void prXnum __PR((Ulong, f_args *));
|
|
#ifdef USE_LONGLONG
|
|
LOCAL void prlnum __PR((Ullong, unsigned, f_args *));
|
|
LOCAL void prldnum __PR((Ullong, f_args *));
|
|
LOCAL void prlonum __PR((Ullong, f_args *));
|
|
LOCAL void prlxnum __PR((Ullong, f_args *));
|
|
LOCAL void prlXnum __PR((Ullong, f_args *));
|
|
#endif
|
|
LOCAL int prbuf __PR((const char *, f_args *));
|
|
LOCAL int prc __PR((char, f_args *));
|
|
LOCAL int prstring __PR((const char *, f_args *));
|
|
#ifdef DEBUG
|
|
LOCAL void dbg_print __PR((char *fmt, int a, int b, int c, int d, int e, int f, int g, int h, int i));
|
|
#endif
|
|
|
|
#ifdef USE_NL_ARGS
|
|
#ifndef FORMAT_FUNC_NAME
|
|
#define FORMAT_IMPL
|
|
EXPORT void _fmtarglist __PR((const char *fmt, va_lists_t, va_lists_t arglist[]));
|
|
EXPORT void _fmtgetarg __PR((const char *fmt, int num, va_lists_t *));
|
|
#else
|
|
extern void _fmtarglist __PR((const char *fmt, va_lists_t, va_lists_t arglist[]));
|
|
extern void _fmtgetarg __PR((const char *fmt, int num, va_lists_t *));
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef FORMAT_BUFFER
|
|
LOCAL char xflsbuf __PR((int c, f_args *ap));
|
|
|
|
LOCAL char
|
|
xflsbuf(c, ap)
|
|
int c;
|
|
f_args *ap;
|
|
{
|
|
*ap->ptr++ = c;
|
|
if (filewrite((FILE *)ap->fp, ap->iobuf, ap->ptr - ap->iobuf) < 0)
|
|
ap->err = 1;
|
|
|
|
ap->cnt = BFSIZ;
|
|
ap->ptr = ap->iobuf;
|
|
return (c);
|
|
}
|
|
|
|
#undef ofun
|
|
#define ofun(c, xp) (--((f_args *)xp)->cnt <= 0 ? \
|
|
xflsbuf(c, (f_args *)xp) : \
|
|
(*(((f_args *)xp)->ptr)++ = (c)))
|
|
|
|
#endif
|
|
|
|
#ifndef FORMAT_FUNC_NAME
|
|
#define FORMAT_FUNC_NAME format
|
|
#define FORMAT_FUNC_PARM
|
|
|
|
#define FORMAT_FUNC_PROTO_DECL void (*fun)(char, void *),
|
|
#define FORMAT_FUNC_KR_DECL register void (*fun)();
|
|
#define FORMAT_FUNC_KR_ARGS fun,
|
|
|
|
#define ofun(c, fp) (*fun)(c, fp)
|
|
#endif
|
|
|
|
#ifdef FORMAT_BUFFER
|
|
#define FARG ((void *)((UIntptr_t)farg|1))
|
|
#else
|
|
#define FARG farg
|
|
#endif
|
|
|
|
#ifdef PROTOTYPES
|
|
EXPORT int
|
|
FORMAT_FUNC_NAME(FORMAT_FUNC_PROTO_DECL
|
|
void *farg,
|
|
const char *fmt,
|
|
va_list oargs)
|
|
#else
|
|
EXPORT int
|
|
FORMAT_FUNC_NAME(FORMAT_FUNC_KR_ARGS farg, fmt, oargs)
|
|
FORMAT_FUNC_KR_DECL
|
|
register void *farg;
|
|
register char *fmt;
|
|
va_list oargs;
|
|
#endif
|
|
{
|
|
#ifdef FORMAT_LOW_MEM
|
|
char buf[512];
|
|
#else
|
|
char buf[8192];
|
|
#endif
|
|
const char *sfmt;
|
|
register int unsflag;
|
|
register long val;
|
|
register char type;
|
|
register char mode;
|
|
register char c;
|
|
int count;
|
|
int num;
|
|
int i;
|
|
short sh;
|
|
const char *str;
|
|
double dval;
|
|
#ifdef USE_LONGLONG
|
|
Llong llval = 0;
|
|
#endif
|
|
Ulong res;
|
|
char *rfmt;
|
|
f_args fa;
|
|
#ifdef USE_NL_ARGS
|
|
va_lists_t fargs; /* Used to get arguments */
|
|
va_lists_t sargs; /* Saved argument state */
|
|
va_lists_t arglist[FMT_ARGMAX+1]; /* List of fast args */
|
|
const char *ofmt = fmt; /* Saved original format */
|
|
BOOL didlist = FALSE; /* Need to scan arguments */
|
|
#endif
|
|
|
|
#ifdef FORMAT_BUFFER
|
|
if (((UIntptr_t)farg & 1) == 0) { /* Called externally */
|
|
fa.cnt = BFSIZ;
|
|
fa.ptr = fa.iobuf;
|
|
fa.fp = (FILE *)farg;
|
|
fa.err = 0;
|
|
farg = fa.farg = &fa;
|
|
} else { /* recursion */
|
|
farg = (void *)((UIntptr_t)farg & ~1);
|
|
}
|
|
#endif
|
|
#ifdef FORMAT_FUNC_PARM
|
|
fa.outf = fun;
|
|
#endif
|
|
fa.farg = farg;
|
|
count = 0;
|
|
|
|
#ifdef USE_NL_ARGS
|
|
va_copy(sargs.ap, oargs); /* Keep a copy in sargs */
|
|
fargs = sargs; /* Make a working copy */
|
|
#endif
|
|
|
|
/*
|
|
* Main loop over the format string.
|
|
* Increment and check for end of string is made here.
|
|
*/
|
|
for (; *fmt != '\0'; fmt++) {
|
|
c = *fmt;
|
|
while (c != '%') {
|
|
if (c == '\0')
|
|
goto out;
|
|
ofun(c, farg);
|
|
c = *(++fmt);
|
|
count++;
|
|
}
|
|
|
|
/*
|
|
* We reached a '%' sign.
|
|
*/
|
|
buf[0] = '\0';
|
|
fa.buf = fa.bufp = buf;
|
|
fa.minusflag = 0;
|
|
fa.flags = 0;
|
|
fa.fldwidth = 0;
|
|
fa.signific = -1;
|
|
fa.lzero = 0;
|
|
fa.fillc = ' ';
|
|
fa.prefixlen = 0;
|
|
sfmt = fmt;
|
|
unsflag = FALSE;
|
|
type = '\0';
|
|
mode = '\0';
|
|
/*
|
|
* %<flags>f.s<length-mod><conversion-spec>
|
|
* %<flags>*.*<length-mod><conversion-spec>
|
|
* %n$<flags>f.s<length-mod><conversion-spec>
|
|
* %n$<flags>*n$.*n$<length-mod><conversion-spec>
|
|
*/
|
|
newflag:
|
|
switch (*(++fmt)) {
|
|
|
|
case '+':
|
|
CHECKFLAG();
|
|
fa.flags |= PLUSFLG;
|
|
goto newflag;
|
|
|
|
case '-':
|
|
CHECKFLAG();
|
|
fa.minusflag++;
|
|
fa.flags |= MINUSFLG;
|
|
goto newflag;
|
|
|
|
case ' ':
|
|
CHECKFLAG();
|
|
/*
|
|
* If the space and the + flag are present,
|
|
* the space flag will be ignored.
|
|
*/
|
|
fa.flags |= SPACEFLG;
|
|
goto newflag;
|
|
|
|
case '#':
|
|
CHECKFLAG();
|
|
fa.flags |= HASHFLG;
|
|
goto newflag;
|
|
|
|
case '\'':
|
|
CHECKFLAG();
|
|
fa.flags |= APOFLG;
|
|
goto newflag;
|
|
|
|
case '.':
|
|
fa.flags |= GOTDOT;
|
|
fa.signific = 0;
|
|
goto newflag;
|
|
|
|
case '*':
|
|
fa.flags |= GOTSTAR;
|
|
#ifdef USE_NL_ARGS
|
|
if (is_dig(fmt[1])) { /* *n$ */
|
|
fmt++; /* Eat up '*' */
|
|
goto dodig;
|
|
}
|
|
#endif
|
|
if (!(fa.flags & GOTDOT)) {
|
|
fa.fldwidth = va_arg(args, int);
|
|
/*
|
|
* A negative fieldwith is a minus flag with a
|
|
* positive fieldwidth.
|
|
*/
|
|
if (fa.fldwidth < 0) {
|
|
fa.fldwidth = -fa.fldwidth;
|
|
fa.minusflag = 1;
|
|
}
|
|
} else {
|
|
/*
|
|
* A negative significance (precision) is taken
|
|
* as if the precision and '.' were omitted.
|
|
*/
|
|
fa.signific = va_arg(args, int);
|
|
if (fa.signific < 0)
|
|
fa.signific = -1;
|
|
}
|
|
goto newflag;
|
|
|
|
case '0':
|
|
/*
|
|
* '0' may be a flag.
|
|
*/
|
|
if (!(fa.flags & (GOTDOT | GOTSTAR | MINUSFLG)))
|
|
fa.fillc = '0';
|
|
/* FALLTHRU */
|
|
case '1': case '2': case '3': case '4':
|
|
case '5': case '6': case '7': case '8': case '9':
|
|
#ifdef USE_NL_ARGS
|
|
dodig:
|
|
#endif
|
|
num = *fmt++ - '0';
|
|
while (c = *fmt, is_dig(c)) {
|
|
num *= 10;
|
|
num += c - '0';
|
|
fmt++;
|
|
}
|
|
#ifdef USE_NL_ARGS
|
|
if (c == '$')
|
|
goto doarglist;
|
|
#endif
|
|
fmt--; /* backup to last digit */
|
|
if (!(fa.flags & GOTDOT))
|
|
fa.fldwidth = num;
|
|
else
|
|
fa.signific = num;
|
|
goto newflag;
|
|
|
|
#ifdef USE_NL_ARGS
|
|
doarglist:
|
|
{
|
|
va_lists_t tmp; /* Temporary arg state */
|
|
if (num <= 0) /* Illegal arg offset */
|
|
goto newflag; /* Continue after '$' */
|
|
if (!didlist) { /* Need to init arglist */
|
|
_fmtarglist(ofmt, sargs, arglist);
|
|
didlist = TRUE;
|
|
}
|
|
if (num <= FMT_ARGMAX) {
|
|
tmp = arglist[num-1];
|
|
} else {
|
|
tmp = arglist[FMT_ARGMAX-1];
|
|
_fmtgetarg(ofmt, num, &tmp);
|
|
}
|
|
if (!(fa.flags & GOTSTAR)) {
|
|
fargs = tmp;
|
|
} else {
|
|
if (!(fa.flags & GOTDOT)) {
|
|
fa.fldwidth = va_arg(tmp.ap, int);
|
|
/*
|
|
* A negative fieldwith is a minus flag
|
|
* with a positive fieldwidth.
|
|
*/
|
|
if (fa.fldwidth < 0) {
|
|
fa.fldwidth = -fa.fldwidth;
|
|
fa.minusflag = 1;
|
|
}
|
|
} else {
|
|
/*
|
|
* A negative significance (precision)
|
|
* is taken as if the precision and '.'
|
|
* were omitted.
|
|
*/
|
|
fa.signific = va_arg(tmp.ap, int);
|
|
if (fa.signific < 0)
|
|
fa.signific = -1;
|
|
}
|
|
}
|
|
goto newflag;
|
|
}
|
|
#endif
|
|
|
|
#ifdef USE_CHECKFLAG
|
|
flagerror:
|
|
fmt = ++sfmt; /* Don't print '%' */
|
|
continue;
|
|
#endif
|
|
}
|
|
|
|
if (strchr("UCSIL", *fmt)) {
|
|
/*
|
|
* Enhancements to K&R and ANSI:
|
|
*
|
|
* got a type specifyer
|
|
*
|
|
* XXX 'S' in C99 is %ls, 'S' should become 'H'
|
|
*/
|
|
if (*fmt == 'U') {
|
|
fmt++;
|
|
unsflag = TRUE;
|
|
}
|
|
if (!strchr("CSILZODX", *fmt)) {
|
|
/*
|
|
* Got only 'U'nsigned specifyer,
|
|
* use default type and mode.
|
|
*/
|
|
type = 'I';
|
|
mode = 'D';
|
|
fmt--;
|
|
} else if (!strchr("CSIL", *fmt)) {
|
|
/*
|
|
* no type, use default
|
|
*/
|
|
type = 'I';
|
|
mode = *fmt;
|
|
} else {
|
|
/*
|
|
* got CSIL type
|
|
*/
|
|
type = *fmt++;
|
|
if (!strchr("ZODX", mode = *fmt)) {
|
|
/*
|
|
* Check long double "Le", "Lf" or "Lg"
|
|
*/
|
|
if (type == 'L' &&
|
|
(mode == 'e' ||
|
|
mode == 'f' ||
|
|
mode == 'g'))
|
|
goto checkfmt;
|
|
fmt--;
|
|
mode = 'D'; /* default mode */
|
|
}
|
|
}
|
|
} else {
|
|
checkfmt:
|
|
switch (*fmt) {
|
|
|
|
case 'h':
|
|
if (!type)
|
|
type = 'H'; /* convert to short type */
|
|
goto getmode;
|
|
|
|
case 'l':
|
|
if (!type)
|
|
type = 'L'; /* convert to long type */
|
|
goto getmode;
|
|
|
|
case 'j':
|
|
if (!type)
|
|
type = 'J'; /* convert to intmax_t type */
|
|
goto getmode;
|
|
|
|
case 'z': /* size_t */
|
|
#if SIZEOF_SIZE_T == SIZEOF_INT
|
|
if (!type)
|
|
type = 'I'; /* convert to int type */
|
|
#else
|
|
#if SIZEOF_SIZE_T == SIZEOF_LONG_INT
|
|
if (!type)
|
|
type = 'L'; /* convert to long type */
|
|
#else
|
|
#if SIZEOF_SIZE_T == SIZEOF_LLONG
|
|
if (!type)
|
|
type = 'Q'; /* convert to long long type */
|
|
#else
|
|
error sizeof (size_t) is unknown
|
|
#endif
|
|
#endif
|
|
#endif
|
|
goto getmode;
|
|
|
|
case 't': /* ptrdiff_t */
|
|
#if SIZEOF_PTRDIFF_T == SIZEOF_INT
|
|
if (!type)
|
|
type = 'I'; /* convert to int type */
|
|
#else
|
|
#if SIZEOF_PTRDIFF_T == SIZEOF_LONG_INT
|
|
if (!type)
|
|
type = 'L'; /* convert to long type */
|
|
#else
|
|
#if SIZEOF_PTRDIFF_T == SIZEOF_LLONG
|
|
if (!type)
|
|
type = 'Q'; /* convert to long long type */
|
|
#else
|
|
error sizeof (ptrdiff_t) is unknown
|
|
#endif
|
|
#endif
|
|
#endif
|
|
/*
|
|
* XXX Future length modifiers:
|
|
* XXX 'L' with double: long double
|
|
*/
|
|
|
|
getmode:
|
|
if (!strchr("udioxXn", *(++fmt))) {
|
|
/*
|
|
* %hhd -> char in decimal
|
|
*/
|
|
if (type == 'H' && *fmt == 'h') {
|
|
type = 'C';
|
|
goto getmode;
|
|
}
|
|
#ifdef USE_LONGLONG
|
|
if (type == 'L' && *fmt == 'l') {
|
|
type = 'Q';
|
|
goto getmode;
|
|
}
|
|
#endif
|
|
fmt--;
|
|
mode = 'D';
|
|
} else { /* One of "udioxXn": */
|
|
mode = *fmt;
|
|
if (mode == 'n')
|
|
goto gotn;
|
|
if (mode != 'x')
|
|
mode = to_cap(mode);
|
|
if (mode == 'U')
|
|
unsflag = TRUE;
|
|
else if (mode == 'I') /* XXX */
|
|
mode = 'D';
|
|
}
|
|
break;
|
|
case 'x':
|
|
mode = 'x';
|
|
goto havemode;
|
|
case 'X':
|
|
mode = 'X';
|
|
type = 'I';
|
|
goto havemode;
|
|
case 'u':
|
|
unsflag = TRUE;
|
|
/*
|
|
* XXX Need to remove uppercase letters for 'long'
|
|
* XXX in future for POSIX/C99 compliance.
|
|
*/
|
|
/* FALLTHRU */
|
|
case 'o': case 'O':
|
|
case 'd': case 'D':
|
|
case 'i': case 'I':
|
|
case 'Z':
|
|
mode = to_cap(*fmt);
|
|
havemode:
|
|
if (!type)
|
|
type = cap_ty(*fmt);
|
|
#ifdef DEBUG
|
|
dbg_print("*fmt: '%c' mode: '%c' type: '%c'\n",
|
|
*fmt, mode, type);
|
|
#endif
|
|
if (mode == 'I') /* XXX kann entfallen */
|
|
mode = 'D'; /* wenn besseres uflg */
|
|
break;
|
|
case 'p':
|
|
mode = 'P';
|
|
type = 'L';
|
|
break;
|
|
|
|
case '%':
|
|
count += prc('%', &fa);
|
|
continue;
|
|
case ' ':
|
|
count += prbuf("", &fa);
|
|
continue;
|
|
case 'c':
|
|
c = va_arg(args, int);
|
|
count += prc(c, &fa);
|
|
continue;
|
|
case 's':
|
|
str = va_arg(args, char *);
|
|
count += prstring(str, &fa);
|
|
continue;
|
|
case 'b':
|
|
str = va_arg(args, char *);
|
|
fa.signific = va_arg(args, int);
|
|
count += prstring(str, &fa);
|
|
continue;
|
|
|
|
#ifndef NO_FLOATINGPOINT
|
|
case 'e':
|
|
if (fa.signific == -1)
|
|
fa.signific = 6;
|
|
if (type == 'L') {
|
|
#ifdef HAVE_LONGDOUBLE
|
|
long double ldval = va_arg(args, long double);
|
|
|
|
#if (defined(HAVE_QECVT) || defined(HAVE__LDECVT))
|
|
qftoes(buf, ldval, 0, fa.signific);
|
|
count += prbuf(buf, &fa);
|
|
continue;
|
|
#else
|
|
dval = ldval;
|
|
#endif
|
|
#endif
|
|
}
|
|
dval = va_arg(args, double);
|
|
ftoes(buf, dval, 0, fa.signific);
|
|
count += prbuf(buf, &fa);
|
|
continue;
|
|
case 'f':
|
|
if (fa.signific == -1)
|
|
fa.signific = 6;
|
|
if (type == 'L') {
|
|
#ifdef HAVE_LONGDOUBLE
|
|
long double ldval = va_arg(args, long double);
|
|
|
|
#if (defined(HAVE_QFCVT) || defined(HAVE__LDFCVT))
|
|
qftofs(buf, ldval, 0, fa.signific);
|
|
count += prbuf(buf, &fa);
|
|
continue;
|
|
#else
|
|
dval = ldval;
|
|
#endif
|
|
#endif
|
|
}
|
|
dval = va_arg(args, double);
|
|
ftofs(buf, dval, 0, fa.signific);
|
|
count += prbuf(buf, &fa);
|
|
continue;
|
|
case 'g':
|
|
if (fa.signific == -1)
|
|
fa.signific = 6;
|
|
if (fa.signific == 0)
|
|
fa.signific = 1;
|
|
if (type == 'L') {
|
|
#ifdef HAVE_LONGDOUBLE
|
|
long double ldval = va_arg(args, long double);
|
|
|
|
#if (defined(HAVE_QGCVT) || defined(HAVE__LDGCVT))
|
|
|
|
#ifdef HAVE__LDGCVT
|
|
#define qgcvt(ld, n, b) _ldgcvt(*(long_double *)&ld, n, b)
|
|
#endif
|
|
(void) qgcvt(ldval, fa.signific, buf);
|
|
count += prbuf(buf, &fa);
|
|
continue;
|
|
#else
|
|
dval = ldval;
|
|
#endif
|
|
#endif
|
|
}
|
|
dval = va_arg(args, double);
|
|
(void) gcvt(dval, fa.signific, buf);
|
|
count += prbuf(buf, &fa);
|
|
continue;
|
|
#else
|
|
# ifdef USE_FLOATINGARGS
|
|
case 'e':
|
|
case 'f':
|
|
case 'g':
|
|
dval = va_arg(args, double);
|
|
continue;
|
|
# endif
|
|
#endif
|
|
|
|
case 'r': /* recursive printf */
|
|
case 'R': /* recursive printf */
|
|
rfmt = va_arg(args, char *);
|
|
/*
|
|
* I don't know any portable way to get an arbitrary
|
|
* C object from a var arg list so I use a
|
|
* system-specific routine __va_arg_list() that knows
|
|
* if 'va_list' is an array. You will not be able to
|
|
* assign the value of __va_arg_list() but it works
|
|
* to be used as an argument of a function.
|
|
* It is a requirement for recursive printf to be able
|
|
* to use this function argument. If your system
|
|
* defines va_list to be an array you need to know this
|
|
* via autoconf or another mechanism.
|
|
* It would be nice to have something like
|
|
* __va_arg_list() in stdarg.h
|
|
*/
|
|
count += FORMAT_FUNC_NAME(FORMAT_FUNC_KR_ARGS
|
|
FARG, rfmt, __va_arg_list(args));
|
|
continue;
|
|
|
|
gotn:
|
|
case 'n':
|
|
switch (type) {
|
|
|
|
case 'C': {
|
|
signed char *cp = va_arg(args, signed char *);
|
|
|
|
*cp = count;
|
|
}
|
|
continue;
|
|
case 'H': {
|
|
short *sp = va_arg(args, short *);
|
|
|
|
*sp = count;
|
|
}
|
|
continue;
|
|
case 'L': {
|
|
long *lp = va_arg(args, long *);
|
|
|
|
*lp = count;
|
|
}
|
|
continue;
|
|
#ifdef USE_LONGLONG
|
|
case 'J': /* For now Intmax_t is Llong */
|
|
case 'Q': {
|
|
Llong *qp = va_arg(args, Llong *);
|
|
|
|
*qp = count;
|
|
}
|
|
continue;
|
|
#endif
|
|
default: {
|
|
int *ip = va_arg(args, int *);
|
|
|
|
*ip = count;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
default: /* Unknown '%' format */
|
|
sfmt++; /* Dont't print '%' */
|
|
count += fmt - sfmt;
|
|
while (sfmt < fmt)
|
|
ofun(*(sfmt++), farg);
|
|
if (*fmt == '\0') {
|
|
fmt--;
|
|
continue;
|
|
} else {
|
|
ofun(*fmt, farg);
|
|
count++;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* print numbers:
|
|
* first prepare type 'C'har, s'H'ort, 'I'nt, or 'L'ong
|
|
* or 'Q'ad and 'J'==maxint_t
|
|
*/
|
|
switch (type) {
|
|
|
|
case 'C':
|
|
c = va_arg(args, int);
|
|
val = c; /* extend sign here */
|
|
if (unsflag || mode != 'D')
|
|
#ifdef DO_MASK
|
|
val &= CHARMASK;
|
|
#else
|
|
val = (unsigned char)val;
|
|
#endif
|
|
break;
|
|
case 'H':
|
|
case 'S': /* XXX remove 'S' in future */
|
|
sh = va_arg(args, int);
|
|
val = sh; /* extend sign here */
|
|
if (unsflag || mode != 'D')
|
|
#ifdef DO_MASK
|
|
val &= SHORTMASK;
|
|
#else
|
|
val = (unsigned short)val;
|
|
#endif
|
|
break;
|
|
case 'I':
|
|
default:
|
|
i = va_arg(args, int);
|
|
val = i; /* extend sign here */
|
|
if (unsflag || mode != 'D')
|
|
#ifdef DO_MASK
|
|
val &= INTMASK;
|
|
#else
|
|
val = (unsigned int)val;
|
|
#endif
|
|
break;
|
|
case 'P':
|
|
case 'L':
|
|
val = va_arg(args, long);
|
|
break;
|
|
#ifdef USE_LONGLONG
|
|
case 'J': /* For now Intmax_t is Llong */
|
|
type = 'Q'; /* use 'Q' for processing */
|
|
case 'Q':
|
|
llval = va_arg(args, Llong);
|
|
val = llval != 0;
|
|
break;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Final print out, take care of mode:
|
|
* mode is one of: 'O'ctal, 'D'ecimal, or he'X'
|
|
* oder 'Z'weierdarstellung.
|
|
*/
|
|
fa.bufp = &buf[sizeof (buf)-1];
|
|
*--fa.bufp = '\0';
|
|
|
|
if (val == 0 && mode != 'D') {
|
|
printzero:
|
|
/*
|
|
* Printing '0' with fieldwidth 0 results in no chars.
|
|
*/
|
|
fa.lzero = -1;
|
|
if (fa.signific >= 0)
|
|
fa.fillc = ' ';
|
|
count += prstring("0", &fa);
|
|
continue;
|
|
} else switch (mode) {
|
|
|
|
case 'D':
|
|
#ifdef USE_LONGLONG
|
|
if (type == 'Q') {
|
|
if (!unsflag && llval < 0) {
|
|
fa.prefix = "-";
|
|
fa.prefixlen = 1;
|
|
llval = -llval;
|
|
} else if (fa.flags & PLUSFLG) {
|
|
fa.prefix = "+";
|
|
fa.prefixlen = 1;
|
|
} else if (fa.flags & SPACEFLG) {
|
|
fa.prefix = " ";
|
|
fa.prefixlen = 1;
|
|
}
|
|
if (llval == 0)
|
|
goto printzero;
|
|
goto prunsigned;
|
|
}
|
|
#endif
|
|
if (!unsflag && val < 0) {
|
|
fa.prefix = "-";
|
|
fa.prefixlen = 1;
|
|
val = -val;
|
|
} else if (fa.flags & PLUSFLG) {
|
|
fa.prefix = "+";
|
|
fa.prefixlen = 1;
|
|
} else if (fa.flags & SPACEFLG) {
|
|
fa.prefix = " ";
|
|
fa.prefixlen = 1;
|
|
}
|
|
if (val == 0)
|
|
goto printzero;
|
|
/* FALLTHRU */
|
|
case 'U':
|
|
/* output a long unsigned decimal number */
|
|
#ifdef USE_LONGLONG
|
|
prunsigned:
|
|
if (type == 'Q')
|
|
prldnum(llval, &fa);
|
|
else
|
|
#endif
|
|
prdnum(val, &fa);
|
|
break;
|
|
case 'O':
|
|
/* output a long octal number */
|
|
if (fa.flags & HASHFLG) {
|
|
fa.prefix = "0";
|
|
fa.prefixlen = 1;
|
|
}
|
|
#ifdef USE_LONGLONG
|
|
if (type == 'Q') {
|
|
prlonum(llval, &fa);
|
|
} else
|
|
#endif
|
|
{
|
|
pronum(val & 07, &fa);
|
|
if ((res = (val>>3) & rshiftmask(long, 3)) != 0)
|
|
pronum(res, &fa);
|
|
}
|
|
break;
|
|
case 'p':
|
|
case 'x':
|
|
/* output a hex long */
|
|
if (fa.flags & HASHFLG) {
|
|
fa.prefix = "0x";
|
|
fa.prefixlen = 2;
|
|
}
|
|
#ifdef USE_LONGLONG
|
|
if (type == 'Q')
|
|
prlxnum(llval, &fa);
|
|
else
|
|
#endif
|
|
{
|
|
prxnum(val & 0xF, &fa);
|
|
if ((res = (val>>4) & rshiftmask(long, 4)) != 0)
|
|
prxnum(res, &fa);
|
|
}
|
|
break;
|
|
case 'P':
|
|
case 'X':
|
|
/* output a hex long */
|
|
if (fa.flags & HASHFLG) {
|
|
fa.prefix = "0X";
|
|
fa.prefixlen = 2;
|
|
}
|
|
#ifdef USE_LONGLONG
|
|
if (type == 'Q')
|
|
prlXnum(llval, &fa);
|
|
else
|
|
#endif
|
|
{
|
|
prXnum(val & 0xF, &fa);
|
|
if ((res = (val>>4) & rshiftmask(long, 4)) != 0)
|
|
prXnum(res, &fa);
|
|
}
|
|
break;
|
|
case 'Z':
|
|
/* output a binary long */
|
|
#ifdef USE_LONGLONG
|
|
if (type == 'Q')
|
|
prlnum(llval, 2, &fa);
|
|
else
|
|
#endif
|
|
{
|
|
prnum(val & 0x1, 2, &fa);
|
|
if ((res = (val>>1) & rshiftmask(long, 1)) != 0)
|
|
prnum(res, 2, &fa);
|
|
}
|
|
}
|
|
fa.lzero = -1;
|
|
/*
|
|
* If a precision (fielwidth) is specified
|
|
* on diouXx conversions, the '0' flag is ignored.
|
|
*/
|
|
if (fa.signific >= 0)
|
|
fa.fillc = ' ';
|
|
count += prbuf(fa.bufp, &fa);
|
|
}
|
|
out:
|
|
#ifdef FORMAT_BUFFER
|
|
if (farg == &fa) { /* Top level call, flush buffer */
|
|
if (fa.err)
|
|
return (EOF);
|
|
if ((fa.ptr != fa.iobuf) &&
|
|
(filewrite(fa.fp, fa.iobuf, fa.ptr - fa.iobuf) < 0))
|
|
return (EOF);
|
|
}
|
|
#endif
|
|
return (count);
|
|
}
|
|
|
|
/*
|
|
* Routines to print (not negative) numbers in an arbitrary base
|
|
*/
|
|
LOCAL unsigned char dtab[] = "0123456789abcdef";
|
|
LOCAL unsigned char udtab[] = "0123456789ABCDEF";
|
|
|
|
LOCAL void
|
|
prnum(val, base, fa)
|
|
register Ulong val;
|
|
register unsigned base;
|
|
f_args *fa;
|
|
{
|
|
register char *p = fa->bufp;
|
|
|
|
do {
|
|
*--p = dtab[modlbys(val, base)];
|
|
val = divlbys(val, base);
|
|
} while (val > 0);
|
|
|
|
fa->bufp = p;
|
|
}
|
|
|
|
LOCAL void
|
|
prdnum(val, fa)
|
|
register Ulong val;
|
|
f_args *fa;
|
|
{
|
|
register char *p = fa->bufp;
|
|
|
|
do {
|
|
*--p = dtab[modlbys(val, (unsigned)10)];
|
|
val = divlbys(val, (unsigned)10);
|
|
} while (val > 0);
|
|
|
|
fa->bufp = p;
|
|
}
|
|
|
|
/*
|
|
* We may need to use division here too (PDP-11, non two's complement ...)
|
|
*/
|
|
LOCAL void
|
|
pronum(val, fa)
|
|
register Ulong val;
|
|
f_args *fa;
|
|
{
|
|
register char *p = fa->bufp;
|
|
|
|
do {
|
|
*--p = dtab[val & 7];
|
|
val >>= 3;
|
|
} while (val > 0);
|
|
|
|
fa->bufp = p;
|
|
}
|
|
|
|
LOCAL void
|
|
prxnum(val, fa)
|
|
register Ulong val;
|
|
f_args *fa;
|
|
{
|
|
register char *p = fa->bufp;
|
|
|
|
do {
|
|
*--p = dtab[val & 15];
|
|
val >>= 4;
|
|
} while (val > 0);
|
|
|
|
fa->bufp = p;
|
|
}
|
|
|
|
LOCAL void
|
|
prXnum(val, fa)
|
|
register Ulong val;
|
|
f_args *fa;
|
|
{
|
|
register char *p = fa->bufp;
|
|
|
|
do {
|
|
*--p = udtab[val & 15];
|
|
val >>= 4;
|
|
} while (val > 0);
|
|
|
|
fa->bufp = p;
|
|
}
|
|
|
|
#ifdef USE_LONGLONG
|
|
LOCAL void
|
|
prlnum(val, base, fa)
|
|
register Ullong val;
|
|
register unsigned base;
|
|
f_args *fa;
|
|
{
|
|
register char *p = fa->bufp;
|
|
|
|
do {
|
|
*--p = dtab[modlbys(val, base)];
|
|
val = divlbys(val, base);
|
|
} while (val > 0);
|
|
|
|
fa->bufp = p;
|
|
}
|
|
|
|
LOCAL void
|
|
prldnum(val, fa)
|
|
register Ullong val;
|
|
f_args *fa;
|
|
{
|
|
register char *p = fa->bufp;
|
|
|
|
do {
|
|
*--p = dtab[val % (unsigned)10];
|
|
val = val / (unsigned)10;
|
|
} while (val > 0);
|
|
|
|
fa->bufp = p;
|
|
}
|
|
|
|
LOCAL void
|
|
prlonum(val, fa)
|
|
register Ullong val;
|
|
f_args *fa;
|
|
{
|
|
register char *p = fa->bufp;
|
|
|
|
do {
|
|
*--p = dtab[val & 7];
|
|
val >>= 3;
|
|
} while (val > 0);
|
|
|
|
fa->bufp = p;
|
|
}
|
|
|
|
LOCAL void
|
|
prlxnum(val, fa)
|
|
register Ullong val;
|
|
f_args *fa;
|
|
{
|
|
register char *p = fa->bufp;
|
|
|
|
do {
|
|
*--p = dtab[val & 15];
|
|
val >>= 4;
|
|
} while (val > 0);
|
|
|
|
fa->bufp = p;
|
|
}
|
|
|
|
LOCAL void
|
|
prlXnum(val, fa)
|
|
register Ullong val;
|
|
f_args *fa;
|
|
{
|
|
register char *p = fa->bufp;
|
|
|
|
do {
|
|
*--p = udtab[val & 15];
|
|
val >>= 4;
|
|
} while (val > 0);
|
|
|
|
fa->bufp = p;
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
* Final buffer print out routine.
|
|
*/
|
|
LOCAL int
|
|
prbuf(s, fa)
|
|
register const char *s;
|
|
f_args *fa;
|
|
{
|
|
register int diff;
|
|
register int rfillc;
|
|
register void *arg = fa->farg;
|
|
#ifdef FORMAT_FUNC_PARM
|
|
register void (*fun) __PR((char, void *)) = fa->outf;
|
|
#endif
|
|
register int count;
|
|
register int lzero = 0;
|
|
|
|
count = strlen(s);
|
|
|
|
/*
|
|
* lzero becomes the number of left fill chars needed to reach signific
|
|
*/
|
|
if (fa->lzero < 0 && count < fa->signific)
|
|
lzero = fa->signific - count;
|
|
count += lzero + fa->prefixlen;
|
|
diff = fa->fldwidth - count;
|
|
if (diff > 0)
|
|
count += diff;
|
|
|
|
if (fa->prefixlen && fa->fillc != ' ') {
|
|
while (*fa->prefix != '\0')
|
|
ofun(*fa->prefix++, arg);
|
|
}
|
|
if (!fa->minusflag) {
|
|
rfillc = fa->fillc;
|
|
while (--diff >= 0)
|
|
ofun(rfillc, arg);
|
|
}
|
|
if (fa->prefixlen && fa->fillc == ' ') {
|
|
while (*fa->prefix != '\0')
|
|
ofun(*fa->prefix++, arg);
|
|
}
|
|
if (lzero > 0) {
|
|
rfillc = '0';
|
|
while (--lzero >= 0)
|
|
ofun(rfillc, arg);
|
|
}
|
|
while (*s != '\0')
|
|
ofun(*s++, arg);
|
|
if (fa->minusflag) {
|
|
rfillc = ' ';
|
|
while (--diff >= 0)
|
|
ofun(rfillc, arg);
|
|
}
|
|
return (count);
|
|
}
|
|
|
|
/*
|
|
* Print out one char, allowing prc('\0')
|
|
* Similar to prbuf()
|
|
*/
|
|
#ifdef PROTOTYPES
|
|
|
|
LOCAL int
|
|
prc(char c, f_args *fa)
|
|
|
|
#else
|
|
|
|
LOCAL int
|
|
prc(c, fa)
|
|
char c;
|
|
f_args *fa;
|
|
#endif
|
|
{
|
|
register int diff;
|
|
register int rfillc;
|
|
register void *arg = fa->farg;
|
|
#ifdef FORMAT_FUNC_PARM
|
|
register void (*fun) __PR((char, void *)) = fa->outf;
|
|
#endif
|
|
register int count;
|
|
|
|
count = 1;
|
|
diff = fa->fldwidth - 1;
|
|
if (diff > 0)
|
|
count += diff;
|
|
|
|
if (!fa->minusflag) {
|
|
rfillc = fa->fillc;
|
|
while (--diff >= 0)
|
|
ofun(rfillc, arg);
|
|
}
|
|
ofun(c, arg);
|
|
if (fa->minusflag) {
|
|
rfillc = ' ';
|
|
while (--diff >= 0)
|
|
ofun(rfillc, arg);
|
|
}
|
|
return (count);
|
|
}
|
|
|
|
/*
|
|
* String output routine.
|
|
* If fa->signific is >= 0, it uses only fa->signific chars.
|
|
* If fa->signific is 0, print no characters.
|
|
*/
|
|
LOCAL int
|
|
prstring(s, fa)
|
|
register const char *s;
|
|
f_args *fa;
|
|
{
|
|
register char *bp;
|
|
register int signific;
|
|
|
|
if (s == NULL)
|
|
return (prbuf("(NULL POINTER)", fa));
|
|
|
|
if (fa->signific < 0)
|
|
return (prbuf(s, fa));
|
|
|
|
bp = fa->buf;
|
|
signific = fa->signific;
|
|
|
|
while (--signific >= 0 && *s != '\0')
|
|
*bp++ = *s++;
|
|
*bp = '\0';
|
|
|
|
return (prbuf(fa->buf, fa));
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
LOCAL void
|
|
dbg_print(fmt, a, b, c, d, e, f, g, h, i)
|
|
char *fmt;
|
|
{
|
|
char ff[1024];
|
|
|
|
sprintf(ff, fmt, a, b, c, d, e, f, g, h, i);
|
|
write(STDERR_FILENO, ff, strlen(ff));
|
|
}
|
|
#endif
|
|
|
|
#ifdef USE_NL_ARGS
|
|
#ifdef FORMAT_IMPL
|
|
/*
|
|
* The following code is shared between format() and fprformat().
|
|
*/
|
|
|
|
/*
|
|
* Format argument types.
|
|
* As "char" and "short" type arguments are fetched as "int"
|
|
* we start with size "int" and ignore the 'h' modifier when
|
|
* parsing sizes.
|
|
*/
|
|
#define AT_NONE 0
|
|
#define AT_INT 1
|
|
#define AT_LONG 2
|
|
#define AT_LONG_LONG 3
|
|
#define AT_DOUBLE 4
|
|
#define AT_LONG_DOUBLE 5
|
|
#define AT_VOID_PTR 6
|
|
#define AT_CHAR_PTR 7
|
|
#define AT_SHORT_PTR 8
|
|
#define AT_INT_PTR 9
|
|
#define AT_LONG_PTR 10
|
|
#define AT_LONG_LONG_PTR 11
|
|
#define AT_R_FMT 12
|
|
#define AT_R_VA_LIST 13
|
|
#define AT_BOUNDS 14
|
|
|
|
#define AF_NONE 0
|
|
#define AF_LONG 1
|
|
#define AF_LONG_LONG 2
|
|
#define AF_LONG_DOUBLE 4
|
|
#define AF_STAR 8
|
|
|
|
static const char skips[] = "+- #'.$h1234567890";
|
|
static const char *digits = &skips[8];
|
|
|
|
/*
|
|
* Parse the format string and store the first FMT_ARGMAX args in the arglist
|
|
* parameter.
|
|
*
|
|
* This is done in two stages:
|
|
* 1 parse the format string and store the types in argtypes[].
|
|
* 2 use the type list in argtypes[], fetch the args in order and
|
|
* store the related va_list state in arglist[]
|
|
*/
|
|
EXPORT void
|
|
_fmtarglist(fmt, fargs, arglist)
|
|
const char *fmt;
|
|
va_lists_t fargs;
|
|
va_lists_t arglist[];
|
|
{
|
|
int i;
|
|
int argindex;
|
|
int maxindex;
|
|
int thistype;
|
|
int thisflag;
|
|
int argtypes[FMT_ARGMAX+1];
|
|
|
|
for (i = 0; i < FMT_ARGMAX; i++)
|
|
argtypes[i] = AT_NONE;
|
|
|
|
maxindex = -1;
|
|
argindex = 0;
|
|
while ((fmt = strchr(fmt, '%')) != NULL) {
|
|
fmt++;
|
|
i = strspn(fmt, digits);
|
|
if (fmt[i] == '$') {
|
|
int c;
|
|
|
|
argindex = *fmt++ - '0';
|
|
while (c = *fmt, is_dig(c)) {
|
|
argindex *= 10;
|
|
argindex += c - '0';
|
|
fmt++;
|
|
}
|
|
argindex -= 1;
|
|
}
|
|
thistype = AT_NONE;
|
|
thisflag = AF_NONE;
|
|
newarg:
|
|
fmt += strspn(fmt, skips);
|
|
switch (*fmt++) {
|
|
|
|
case '%': /* %% format no arg */
|
|
continue;
|
|
|
|
case 'l':
|
|
if (thisflag & AF_LONG) {
|
|
thisflag |= AF_LONG_LONG;
|
|
} else {
|
|
thisflag |= AF_LONG;
|
|
}
|
|
goto newarg;
|
|
case 'j': /* intmax_t for now is long long */
|
|
thisflag |= AF_LONG_LONG;
|
|
goto newarg;
|
|
case 'z': /* size_t */
|
|
#if SIZEOF_SIZE_T == SIZEOF_INT
|
|
if (thistype == AT_NONE)
|
|
thistype = AT_INT;
|
|
#else
|
|
#if SIZEOF_SIZE_T == SIZEOF_LONG_INT
|
|
if (thistype == AT_NONE)
|
|
thistype = AT_LONG;
|
|
#else
|
|
#if SIZEOF_SIZE_T == SIZEOF_LLONG
|
|
if (thistype == AT_NONE)
|
|
thistype = AT_LONG_LONG;
|
|
#else
|
|
error sizeof (size_t) is unknown
|
|
#endif
|
|
#endif
|
|
#endif
|
|
goto newarg;
|
|
case 't': /* ptrdiff_t */
|
|
#if SIZEOF_PTRDIFF_T == SIZEOF_INT
|
|
if (thistype == AT_NONE)
|
|
thistype = AT_INT;
|
|
#else
|
|
#if SIZEOF_PTRDIFF_T == SIZEOF_LONG_INT
|
|
if (thistype == AT_NONE)
|
|
thistype = AT_LONG;
|
|
#else
|
|
#if SIZEOF_PTRDIFF_T == SIZEOF_LLONG
|
|
if (thistype == AT_NONE)
|
|
thistype = AT_LONG_LONG;
|
|
#else
|
|
error sizeof (ptrdiff_t) is unknown
|
|
#endif
|
|
#endif
|
|
#endif
|
|
goto newarg;
|
|
#ifndef NO_UCSIL
|
|
/*
|
|
* Enhancements to K&R and ANSI:
|
|
*
|
|
* got a type specifyer
|
|
*
|
|
* XXX 'S' in C99 is %ls, 'S' should become 'H'
|
|
*/
|
|
case 'U':
|
|
if (!strchr("CSILZODX", *fmt)) {
|
|
/*
|
|
* Got only 'U'nsigned specifyer,
|
|
* use default type and mode.
|
|
*/
|
|
thistype = AT_INT;
|
|
break;
|
|
}
|
|
if (!strchr("CSIL", *fmt)) {
|
|
/*
|
|
* Got 'U' and ZODX.
|
|
* no type, use default
|
|
*/
|
|
thistype = AT_INT;
|
|
fmt++; /* Skip ZODX */
|
|
break;
|
|
}
|
|
fmt++; /* Unsigned, skip 'U', get CSIL */
|
|
/* FALLTHRU */
|
|
case 'C':
|
|
case 'S':
|
|
case 'I':
|
|
case 'L':
|
|
fmt--; /* Undo fmt++ from switch() */
|
|
{
|
|
/*
|
|
* got CSIL type
|
|
*/
|
|
int type = *fmt++; /* Undo above fmt-- */
|
|
int mode = *fmt;
|
|
if (!strchr("ZODX", mode)) {
|
|
/*
|
|
* Check long double "Le", "Lf" or "Lg"
|
|
*/
|
|
if (type == 'L' &&
|
|
(mode == 'e' ||
|
|
mode == 'f' ||
|
|
mode == 'g')) {
|
|
thisflag |= AF_LONG_DOUBLE;
|
|
goto newarg;
|
|
}
|
|
} else {
|
|
fmt++; /* Skip ZODX */
|
|
}
|
|
if (type == 'L')
|
|
thistype = AT_LONG;
|
|
else
|
|
thistype = AT_INT;
|
|
}
|
|
break;
|
|
#else
|
|
case 'L':
|
|
thisflag |= AF_LONG_DOUBLE;
|
|
goto newarg;
|
|
#endif
|
|
|
|
case 'e':
|
|
case 'E':
|
|
case 'f':
|
|
case 'F':
|
|
case 'g':
|
|
case 'G':
|
|
if (thisflag & AF_LONG_DOUBLE)
|
|
thistype = AT_LONG_DOUBLE;
|
|
else
|
|
thistype = AT_DOUBLE;
|
|
break;
|
|
|
|
case 'p':
|
|
thistype = AT_VOID_PTR;
|
|
break;
|
|
case 's':
|
|
thistype = AT_CHAR_PTR;
|
|
break;
|
|
case 'b':
|
|
thistype = AT_BOUNDS;
|
|
break;
|
|
case 'n':
|
|
if (thisflag & AF_LONG_LONG)
|
|
thistype = AT_LONG_LONG_PTR;
|
|
else if (thistype & AF_LONG)
|
|
thistype = AT_LONG_PTR;
|
|
else
|
|
thistype = AT_INT_PTR;
|
|
break;
|
|
case 'r':
|
|
thistype = AT_R_FMT;
|
|
break;
|
|
default:
|
|
if (thistype == AT_NONE) {
|
|
if (thisflag & AF_LONG_LONG)
|
|
thistype = AT_LONG_LONG;
|
|
else if (thistype & AF_LONG)
|
|
thistype = AT_LONG;
|
|
else
|
|
thistype = AT_INT;
|
|
}
|
|
break;
|
|
|
|
case '*':
|
|
if (is_dig(*fmt)) {
|
|
int c;
|
|
int starindex;
|
|
|
|
starindex = *fmt++ - '0';
|
|
while (c = *fmt, is_dig(c)) {
|
|
starindex *= 10;
|
|
starindex += c - '0';
|
|
fmt++;
|
|
}
|
|
starindex -= 1;
|
|
if (starindex >= 0 && starindex < FMT_ARGMAX) {
|
|
argtypes[starindex] = AT_INT;
|
|
if (starindex > maxindex)
|
|
maxindex = starindex;
|
|
}
|
|
goto newarg;
|
|
}
|
|
thistype = AT_INT;
|
|
thisflag |= AF_STAR; /* Make sure to rescan for type */
|
|
break;
|
|
}
|
|
if (argindex >= 0 && argindex < FMT_ARGMAX) {
|
|
argtypes[argindex] = thistype;
|
|
if (thistype == AT_R_FMT)
|
|
argtypes[++argindex] = AT_R_VA_LIST;
|
|
else if (thistype == AT_BOUNDS)
|
|
argtypes[++argindex] = AT_INT;
|
|
|
|
if (argindex > maxindex)
|
|
maxindex = argindex;
|
|
}
|
|
++argindex; /* Default to next arg in list */
|
|
if (thisflag & AF_STAR) { /* Found '*', continue for type */
|
|
thisflag &= ~AF_STAR;
|
|
goto newarg;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i <= maxindex; i++) { /* Do not fetch more args than known */
|
|
arglist[i] = fargs; /* Save state before fetching this */
|
|
|
|
switch (argtypes[i]) {
|
|
|
|
default:
|
|
/* FALLTHRU */
|
|
case AT_NONE: /* This matches '*' args */
|
|
/* FALLTHRU */
|
|
case AT_INT:
|
|
(void) va_arg(fargs.ap, int);
|
|
break;
|
|
case AT_LONG:
|
|
(void) va_arg(fargs.ap, long);
|
|
break;
|
|
case AT_LONG_LONG:
|
|
(void) va_arg(fargs.ap, Llong);
|
|
break;
|
|
case AT_DOUBLE:
|
|
(void) va_arg(fargs.ap, double);
|
|
break;
|
|
case AT_LONG_DOUBLE:
|
|
#ifdef HAVE_LONGDOUBLE
|
|
(void) va_arg(fargs.ap, long double);
|
|
#endif
|
|
break;
|
|
case AT_VOID_PTR:
|
|
(void) va_arg(fargs.ap, void *);
|
|
break;
|
|
case AT_CHAR_PTR:
|
|
(void) va_arg(fargs.ap, char *);
|
|
break;
|
|
case AT_SHORT_PTR:
|
|
(void) va_arg(fargs.ap, short *);
|
|
break;
|
|
case AT_INT_PTR:
|
|
(void) va_arg(fargs.ap, int *);
|
|
break;
|
|
case AT_LONG_PTR:
|
|
(void) va_arg(fargs.ap, long *);
|
|
break;
|
|
case AT_LONG_LONG_PTR:
|
|
(void) va_arg(fargs.ap, Llong *);
|
|
break;
|
|
case AT_R_FMT:
|
|
(void) va_arg(fargs.ap, char *);
|
|
arglist[++i] = fargs;
|
|
(void) __va_arg_list(fargs.ap);
|
|
break;
|
|
case AT_R_VA_LIST:
|
|
break;
|
|
case AT_BOUNDS:
|
|
(void) va_arg(fargs.ap, char *);
|
|
arglist[++i] = fargs;
|
|
(void) va_arg(fargs.ap, int);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* In case that the format references an argument > FMT_ARGMAX, we use this
|
|
* implementation. It is slow (n*n - where n is (argno - FMT_ARGMAX)).
|
|
* Fortunately, it is most unlikely that there are more positional args than
|
|
* the current FMT_ARGMAX definition of 30.
|
|
*/
|
|
EXPORT void
|
|
_fmtgetarg(fmt, num, fargs)
|
|
const char *fmt;
|
|
int num;
|
|
va_lists_t *fargs;
|
|
{
|
|
const char *sfmt = fmt;
|
|
int i;
|
|
|
|
/*
|
|
* Hacky preliminary support for all int type args bejond FMT_ARGMAX.
|
|
*/
|
|
for (i = FMT_ARGMAX; i < num; i++)
|
|
(void) va_arg((*fargs).ap, int);
|
|
}
|
|
#endif /* FORMAT_IMPL */
|
|
#endif /* USE_NL_ARGS */
|