/* @(#)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 #include #include #include #ifdef DEBUG #include #endif #if !defined(HAVE_STDLIB_H) || !defined(HAVE_GCVT) extern char *gcvt __PR((double, int, char *)); #endif #include #include #include /* * 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'; /* * %f.s * %*.* * %n$f.s * %n$*n$.*n$ */ 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 */