doprnt.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* 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.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 1988-1995, by Sun Microsystems, Inc.
* All rights reserved
*/
/* Copyright (c) 1988 AT&T */
/* All Rights Reserved */
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* _doprnt: common code for printf, fprintf, sprintf
* Floating-point code is included or not, depending
* on whether the preprocessor variable FLOAT is 1 or 0.
*/
#define MAXARGS 50
#ifndef FLOAT
#define FLOAT 1 /* YES! we want floating */
#endif
#include <stdio.h>
#include <ctype.h>
#include <varargs.h>
#include <values.h>
#include <locale.h>
#include "doprnt.h"
#include "stdiom.h"
#include <string.h> /* strchr, strlen, strspn */
#define max(a,b) ((a) > (b) ? (a) : (b))
#define min(a,b) ((a) < (b) ? (a) : (b))
/* If this symbol is nonzero, allow '0' as a flag */
/* If this symbol is nonzero, allow '0' as a flag */
#define FZERO 1
#if FLOAT
/*
* libc/gen/common functions for floating-point conversion
*/
#include <floatingpoint.h>
extern void _fourdigitsquick();
#endif
void _mkarglst();
void _getarg();
static char *_check_dol();
#define emitchar(c) { if (--filecnt < 0) { \
register FILE *iop = file; \
if (((iop->_flag & (_IOLBF|_IONBF)) == 0 \
|| -filecnt >= iop->_bufsiz)) { \
iop->_ptr = fileptr; \
if (iop->_flag & _IOSTRG) \
return iop->_ptr - iop->_base; \
else \
(void) _xflsbuf(iop); \
fileptr = iop->_ptr; \
filecnt = iop->_cnt; \
filecnt--; \
} \
} \
*fileptr++ = (unsigned)(c); \
count++; \
}
static char *nullstr = "(null)";
static char *lowerhex = "0123456789abcdef";
static char *upperhex = "0123456789ABCDEF";
/* stva_list is used to subvert C's restriction that a variable with an
* array type can not appear on the left hand side of an assignment operator.
* By putting the array inside a structure, the functionality of assigning to
* the whole array through a simple assignment is achieved..
*/
typedef struct stva_list {
va_list ap;
} stva_list;
_doprnt(format, in_args, file)
char *format;
va_list in_args;
FILE *file;
{
char convertbuffer[1024] ;
/* Current position in format */
register char *cp;
/* Starting and ending points for value to be printed */
register char *bp;
char *p;
/* Pointer and count for I/O buffer */
register unsigned char *fileptr;
register int filecnt;
/* Field width and precision */
int width;
register int prec;
/* Format code */
char fcode;
/* Number of padding zeroes required on the left */
int lzero;
/* Flags - nonzero if corresponding character appears in format */
bool fplus; /* + */
bool fminus; /* - */
bool fblank; /* blank */
bool fsharp; /* # */
#if FZERO
bool ansi_fzero; /* 0 for ansi-dictated formats */
bool compat_fzero; /* 0 for backward compatibility */
#endif
bool Lsize; /* Capital L for size = long double = quadruple */
/* Pointer to sign, "0x", "0X", or empty */
char *prefix;
/* Scratch */
int nblank;
#if FLOAT
/* Exponent or empty */
char *suffix;
/* Buffer to create exponent */
char expbuf[7]; /* "e+xxxx\0" */
/* Number of padding zeroes required on the right */
int rzero;
/* Length of exponent suffix. */
int suffixlength;
/* The value being converted, if real or quadruple */
double dval;
quadruple qval;
/* Output values from fconvert and econvert */
int decpt, sign;
/* Values are developed in this buffer */
char buf[1034]; /* Size of convertbuffer, plus some for exponent and sign. */
/* Current locale's decimal point */
char decpt_char = *(localeconv()->decimal_point);
#else
/* Values are developed in this buffer */
char buf[MAXDIGS];
#endif
/* The value being converted, if integer */
register unsigned long val;
/* Work variables */
register int n;
register char c;
char radix;
int svswitch = 0;
/* count of output characters */
register int count;
/* variables for positional parameters */
char *sformat = format; /* save the beginning of the format */
int fpos = 1; /* 1 if first positional parameter */
stva_list args, /* used to step through the argument list */
args_width, /* for width */
args_prec, /* for prec */
sargs; /* used to save the start of the argument list */
stva_list arglst[MAXARGS];/* array giving the approriate values
* for va_arg() to retrieve the
* corresponding argument:
* arglst[0] is the first argument
* arglst[1] is the second argument, etc.
*/
int index = 0; /* argument placeolder */
/* Initialize args and sargs to the start of the argument list.
* Note that ANSI guarantees that the address of the first member of
* a structure will be the same as the address of the structure. */
args_width = args_prec = args = sargs = *(struct stva_list *)&in_args;
/* initialize p an bp (starting and ending points) bugid 1141781 */
p = bp = NULL;
cp = format;
if ((c = *cp++) != '\0') {
/*
* We know we're going to write something; make sure
* we can write and set up buffers, etc..
*/
if (_WRTCHK(file))
return(EOF);
} else
return(0); /* no fault, no error */
count = 0;
fileptr = file->_ptr;
filecnt = file->_cnt;
/*
* The main loop -- this loop goes through one iteration
* for each ordinary character or format specification.
*/
do {
if (c != '%') {
/* Ordinary (non-%) character */
emitchar(c);
} else {
/*
* % has been spotted!
*
* First, try the 99% cases.
* then parse the format specification.
*
* Note that this code assumes the Sun
* Workstation environment (all params
* passed as int == long, no interrupts
* for fixed point overflow from negating
* the most negative number).
*/
skipit:
switch(c = *cp++) {
case 'l':
case 'h':
/* Quickly ignore long & short specifiers */
goto skipit;
case 's':
bp = va_arg(args.ap, char *);
if (bp == NULL)
bp = nullstr;
while (c = *bp++)
emitchar(c);
p = bp;
continue;
case 'c':
c = va_arg(args.ap, int);
emitc:
emitchar(c);
continue;
case 'i':
case 'd':
case 'D':
val = va_arg(args.ap, int);
if ((long) val < 0) {
emitchar('-');
val = -val;
}
goto udcommon;
case 'U':
case 'u':
val = va_arg(args.ap, unsigned);
udcommon:
{
register char *stringp = lowerhex;
bp = buf+MAXDIGS;
stringp = lowerhex;
do {
*--bp = stringp[val%10];
val /= 10;
} while (val);
}
goto intout;
case 'X':
{
register char *stringp = upperhex;
val = va_arg(args.ap, unsigned);
bp = buf + MAXDIGS;
if (val == 0)
goto zero;
while (val) {
*--bp = stringp[val%16];
val /= 16;
}
}
goto intout;
case 'x':
case 'p':
{
register char *stringp = lowerhex;
val = va_arg(args.ap, unsigned);
bp = buf + MAXDIGS;
if (val == 0)
goto zero;
while (val) {
*--bp = stringp[val%16];
val /= 16;
}
}
goto intout;
case 'O':
case 'o':
{
register char *stringp = lowerhex;
val = va_arg(args.ap, unsigned);
bp = buf + MAXDIGS;
if (val == 0)
goto zero;
while (val) {
*--bp = stringp[val%8];
val /= 8;
}
}
/* Common code to output integers */
intout:
p = buf + MAXDIGS;
while (bp < p) {
c = *bp++;
emitchar(c);
}
continue;
zero:
c = '0';
goto emitc;
default:
/*
* let AT&T deal with it
*/
cp-= 2;
}
Lsize = 0; /* Not long double unless we say so. */
/* Scan the <flags> */
fplus = 0;
fminus = 0;
fblank = 0;
fsharp = 0;
#if FZERO
ansi_fzero = 0;
compat_fzero = 0;
#endif
scan: switch (*++cp) {
case '+':
fplus = 1;
goto scan;
case '-':
fminus = 1;
goto scan;
case ' ':
fblank = 1;
goto scan;
case '#':
fsharp = 1;
goto scan;
#if FZERO
case '0':
ansi_fzero = 1;
compat_fzero = 1;
goto scan;
#endif
}
/* Scan the field width */
if (*cp == '*') {
char *p;
int val;
p = _check_dol(cp+1, &val);
if (p != (char *)NULL) {
/*
* argument re-order
*/
if (fpos) {
_mkarglst(sformat, sargs, arglst);
fpos = 0;
}
if (val <= MAXARGS) {
args_width = arglst[val - 1];
} else {
args_width = arglst[MAXARGS - 1];
_getarg(sformat, &args_width, val);
}
width = va_arg(args_width.ap, int);
if (width < 0) {
width = -width;
fminus = 1;
}
cp = p;
}
else {
width = va_arg(args.ap, int);
if (width < 0) {
width = -width;
fminus = 1;
}
cp++;
}
} else {
index = width = 0;
while (isdigit(*cp)) {
n = tonumber(*cp++);
index = width = width * 10 + n;
}
}
/* Scan the precision */
if (*cp == '.') {
/* '*' instead of digits? */
if (*++cp == '*') {
char *p;
int val;
p = _check_dol(cp+1, &val);
if (p != (char *)NULL) {
/*
* argument re-order
*/
if (fpos) {
_mkarglst(sformat, sargs, arglst);
fpos = 0;
}
if (val <= MAXARGS) {
args_prec = arglst[val - 1];
} else {
args_prec = arglst[MAXARGS - 1];
_getarg(sformat, &args_prec, val);
}
prec = va_arg(args_prec.ap, int);
cp = p;
}
else {
prec = va_arg(args.ap, int);
cp++;
}
} else {
prec = 0;
while (isdigit(*cp)) {
n = tonumber(*cp++);
prec = prec * 10 + n;
}
}
} else
prec = -1;
if (*cp == '$') {
if (fpos) {
_mkarglst(sformat, sargs, arglst);
fpos = 0;
}
if (index <= MAXARGS) {
args = arglst[index - 1];
} else {
args = arglst[MAXARGS - 1];
_getarg(sformat, &args, index);
}
goto scan;
}
/*
* The character addressed by cp must be the
* format letter -- there is nothing left for
* it to be.
*
* The status of the +, -, #, blank, and 0
* flags are reflected in the variables
* "fplus", "fminus", "fsharp", "fblank",
* and "ansi_fzero"/"compat_fzero", respectively.
* "width" and "prec" contain numbers
* corresponding to the digit strings
* before and after the decimal point,
* respectively. If there was no decimal
* point, "prec" is -1.
*
* The following switch sets things up
* for printing. What ultimately gets
* printed will be padding blanks, a prefix,
* left padding zeroes, a value, right padding
* zeroes, a suffix, and more padding
* blanks. Padding blanks will not appear
* simultaneously on both the left and the
* right. Each case in this switch will
* compute the value, and leave in several
* variables the information necessary to
* construct what is to be printed.
*
* The prefix is a sign, a blank, "0x", "0X",
* or null, and is addressed by "prefix".
*
* The suffix is either null or an exponent,
* and is addressed by "suffix".
*
* The value to be printed starts at "bp"
* and continues up to and not including "p".
*
* "lzero" and "rzero" will contain the number
* of padding zeroes required on the left
* and right, respectively. If either of
* these variables is negative, it will be
* treated as if it were zero.
*
* The number of padding blanks, and whether
* they go on the left or the right, will be
* computed on exit from the switch.
*/
lzero = 0;
prefix = "";
#if FLOAT
rzero = 0;
suffix = prefix;
#endif
#if FZERO
/* if both zero-padding and left-justify flags
* are used, ignore zero-padding, per ansi c
*/
if (ansi_fzero & fminus) {
ansi_fzero = 0;
compat_fzero = 0;
}
/* if zero-padding and precision are specified,
* ignore zero-padding for ansi-dictated formats,
* per ansi c
*/
if (ansi_fzero & (prec != -1)) ansi_fzero = 0;
#endif
next:
switch (fcode = *cp++) {
/* toss the length modifier, if any */
case 'l':
case 'h':
goto next;
case 'L':
Lsize = 1; /* Remember long double size. */
goto next;
/*
* fixed point representations
*
* "radix" is the radix for the conversion.
* Conversion is unsigned unless fcode is 'd'.
* We assume a 2's complement machine and
* that fixed point overflow (from negating
* the largest negative int) is ignored.
*/
case 'i':
case 'D':
case 'U':
case 'd':
case 'u':
radix = 10;
goto fixed;
case 'O':
case 'o':
radix = 8;
goto fixed;
case 'X':
case 'x':
radix = 16;
fixed:
/* Establish default precision */
if (prec < 0)
prec = 1;
/* Fetch the argument to be printed */
val = va_arg(args.ap, unsigned);
/* If signed conversion, establish sign */
if (fcode == 'd' || fcode == 'D' || fcode == 'i') {
if ((long) val < 0) {
prefix = "-";
val = -val;
} else if (fplus)
prefix = "+";
else if (fblank)
prefix = " ";
}
/* Set translate table for digits */
{
register char *stringp;
if (fcode == 'X')
stringp = upperhex;
else
stringp = lowerhex;
/* Develop the digits of the value */
bp = buf + MAXDIGS;
switch(radix) {
case 8: /*octal*/
while (val) {
*--bp = stringp[val%8];
val /= 8;
}
break;
case 16:/*hex*/
while (val) {
*--bp = stringp[val%16];
val /= 16;
}
break;
default:
while (val) {
*--bp = stringp[val%10];
val /= 10;
}
break;
} /* switch */
}
/* Calculate padding zero requirement */
p = buf + MAXDIGS;
/* Handle the # flag */
if (fsharp && bp != p) {
switch (fcode) {
case 'x':
prefix = "0x";
break;
case 'X':
prefix = "0X";
break;
}
}
#if FZERO
if (ansi_fzero) {
n = width - strlen(prefix);
if (n > prec)
prec = n;
}
#endif
lzero = bp - p + prec;
/* Handle the # flag for 'o' */
if (fsharp && bp != p && fcode == 'o' &&
lzero < 1) {
lzero = 1;
}
break;
#if FLOAT
#ifdef sparc
#define GETQVAL /* Sun-4 macro to get a quad q from the argument list, passed as a pointer. */ \
{ qval = *(va_arg(args.ap, quadruple*)) ; }
#else
#define GETQVAL /* Sun-3 macro to get a quad q from the argument list, passed as a value. */ \
{ int iq ; unsigned long * pl = (unsigned long *) (&qval) ; for(iq=0;iq<4;iq++) pl[iq] = (unsigned long) va_arg(args.ap, unsigned long) ; }
#endif
case 'E':
case 'e':
/*
* E-format. The general strategy
* here is fairly easy: we take
* what econvert gives us and re-format it.
*/
/* Establish default precision */
if (prec < 0)
prec = 6;
/* Fetch the value */
if (Lsize == 0) { /* Double */
dval = va_arg(args.ap, double);
bp = econvert(dval, prec + 1, &decpt, &sign, convertbuffer);
} else { /* Long Double = quadruple */
GETQVAL;
bp = qeconvert(&qval, prec + 1, &decpt, &sign, convertbuffer);
}
/* Determine the prefix */
if (sign)
prefix = "-";
else if (fplus)
prefix = "+";
else if (fblank)
prefix = " ";
if (convertbuffer[0] > '9')
{ /* handle infinity, nan */
bp = &convertbuffer[0];
for (p = bp+1 ; *p != 0 ; p++) ;
goto ebreak ;
}
{
register char *stringp;
/* Place the first digit in the buffer */
stringp = &buf[0];
*stringp++ = *bp != '\0'? *bp++: '0';
/* Put in a decimal point if needed */
if (prec != 0 || fsharp)
*stringp++ = decpt_char;
/* Create the rest of the mantissa */
rzero = prec;
while (rzero > 0 && *bp!= '\0') {
--rzero;
*stringp++ = *bp++;
}
p = stringp;
}
bp = &buf[0];
/* Create the exponent */
if (convertbuffer[0] != '0')
n = decpt - 1;
else
n = 0 ;
if (n < 0)
n = -n;
_fourdigitsquick( (short unsigned) n, &(expbuf[2]) ) ;
expbuf[6] = 0 ;
if (n < 100)
/*
* Normally two digit exponent field,
* three or four if required.
*/
{ suffix = &(expbuf[4]) ; suffixlength = 4 ; }
else if (n < 1000)
{ suffix = &(expbuf[3]) ; suffixlength = 5 ; }
else
{ suffix = &(expbuf[2]) ; suffixlength = 6 ; }
/* Put in the exponent sign */
*--suffix = (decpt > 0 || convertbuffer[0] == '0' )? '+': '-';
/* Put in the e; note kludge in 'g' format */
*--suffix = fcode;
ebreak:
#if FZERO
if (compat_fzero &! fminus)
/* Calculate padding zero requirement */
lzero = width - (strlen(prefix)
+ (p - buf) + rzero + suffixlength);
#endif
break;
case 'f':
/*
* F-format floating point. This is
* a good deal less simple than E-format.
* The overall strategy will be to call
* fconvert, reformat its result into buf,
* and calculate how many trailing
* zeroes will be required. There will
* never be any leading zeroes needed.
*/
/* Establish default precision */
if (prec < 0)
prec = 6;
if (Lsize == 0) {
dval = va_arg(args.ap, double);
bp = fconvert(dval, prec, &decpt, &sign, convertbuffer);
} else {
GETQVAL ;
bp = qfconvert(&qval, prec, &decpt, &sign, convertbuffer);
}
/* Determine the prefix */
if (sign)
prefix = "-";
else if (fplus)
prefix = "+";
else if (fblank)
prefix = " ";
if (convertbuffer[0] > '9')
{ /* handle infinity, nan */
bp = &convertbuffer[0];
for (p = bp+1 ; *p != 0 ; p++) ;
goto fbreak ;
}
{
register char *stringp;
/* Initialize buffer pointer */
stringp = &buf[0];
/* Emit the digits before the decimal point */
n = decpt;
if (n <= 0)
*stringp++ = '0';
else
do
if (*bp == '\0' )
*stringp++ = '0';
else {
*stringp++ = *bp++;
}
while (--n != 0);
/* Decide whether we need a decimal point */
if (fsharp || prec > 0)
*stringp++ = decpt_char;
/* Digits(if any) after the decimal point */
n = prec;
rzero = prec - n;
while (--n >= 0) {
if (++decpt <= 0 || *bp == '\0')
*stringp++ = '0';
else {
*stringp++ = *bp++;
}
}
#if FZERO
if (compat_fzero &! fminus)
/* Calculate padding zero requirement */
lzero = width - (strlen(prefix)
+ (stringp - buf) + rzero);
#endif
p = stringp;
}
bp = &buf[0];
fbreak:
break;
case 'G':
case 'g':
/*
* g-format. We play around a bit
* and then jump into e or f, as needed.
*/
/* Establish default precision */
if (prec < 0)
prec = 6;
else if (prec == 0)
prec = 1;
if (Lsize == 0) {
dval = va_arg(args.ap, double);
bp = gconvert(dval, prec, fsharp, convertbuffer);
} else {
GETQVAL;
bp = qgconvert(&qval, prec, fsharp, convertbuffer);
}
bp = convertbuffer ;
if (convertbuffer[0] == '-') {
prefix = "-" ;
bp++;
}
else if (fplus)
prefix = "+";
else if (fblank)
prefix = " ";
if (isupper(fcode))
{ /* Put in a big E for small minds. */
for (p = bp ; (*p != NULL) && (*p != 'e') ; p++) ;
if (*p == 'e') *p = 'E' ;
for (; (*p != NULL) ; p++) ;
/* Find end of string. */
}
else
for (p = bp ; *p != NULL ; p++) ;
/* Find end of string. */
rzero = 0;
#if FZERO
if (compat_fzero & !fminus)
/* Calculate padding zero requirement */
lzero = width - (strlen(prefix)
+ (p - bp) + rzero);
#endif
break ;
#endif
case 'c':
buf[0] = va_arg(args.ap, int);
bp = &buf[0];
p = bp + 1;
break;
case 's':
bp = va_arg(args.ap, char *);
if (prec < 0)
prec = MAXINT;
/* avoid *(0) */
if (bp == NULL)
bp = nullstr;
for (n=0; *bp++ != '\0' && n < prec; n++)
;
#if FZERO
if (compat_fzero &! fminus)
lzero = width - n;
#endif
p = --bp;
bp -= n;
break;
case '\0':
/* well, what's the punch line? */
goto out;
case 'n':
svswitch = 1;
break;
default:
p = bp = &fcode;
p++;
break;
}
/* Calculate number of padding blanks */
nblank = width
#if FLOAT
- (rzero < 0? 0: rzero)
- strlen(suffix)
#endif
- (p - bp)
- (lzero < 0? 0: lzero)
- strlen(prefix);
/* Blanks on left if required */
if (!fminus)
while (--nblank >= 0)
emitchar(' ');
/* Prefix, if any */
while (*prefix != '\0') {
emitchar(*prefix);
prefix++;
}
/* Zeroes on the left */
while (--lzero >= 0)
emitchar('0');
/* The value itself */
while (bp < p) {
emitchar(*bp);
bp++;
}
#if FLOAT
/* Zeroes on the right */
while (--rzero >= 0)
emitchar('0');
/* The suffix */
while (*suffix != '\0') {
emitchar(*suffix);
suffix++;
}
#endif
/* Blanks on the right if required */
if (fminus)
while (--nblank >= 0)
emitchar(' ');
/* If %n is seen, save count in argument */
if (svswitch == 1) {
long *svcount;
svcount = va_arg (args.ap, long *);
*svcount = count;
svswitch = 0;
}
} /* else */
} while ((c = *cp++) != '\0'); /* do */
out:
file->_ptr = fileptr;
file->_cnt = filecnt;
if (file->_flag & (_IONBF | _IOLBF) &&
(file->_flag & _IONBF ||
memchr((char *)file->_base, '\n', fileptr - file->_base) != NULL))
(void) _xflsbuf(file);
return (ferror(file)? EOF: count);
}
#ifdef sparc
/*
* We use "double *" instead of "quadruple *" to skip over the pointer to
* long double on the argument list since a pointer is a pointer after all.
*/
#define SKIPQVAL { \
(void) va_arg(args.ap, double *); \
}
#else /* Sun-3 */
#define SKIPQVAL { \
int iq; \
for (iq = 0; iq < 4; iq++) \
(void) va_arg(args.ap, unsigned long); \
}
#endif
/* This function initializes arglst, to contain the appropriate va_list values
* for the first MAXARGS arguments. */
void
_mkarglst(fmt, args, arglst)
char *fmt;
stva_list args;
stva_list arglst[];
{
static char *digits = "01234567890", *skips = "# +-.0123456789h$";
enum types {INT = 1, LONG, CHAR_PTR, DOUBLE, LONG_DOUBLE, VOID_PTR,
LONG_PTR, INT_PTR};
enum types typelst[MAXARGS], curtype;
int maxnum, n, curargno, flags;
/*
* Algorithm 1. set all argument types to zero.
* 2. walk through fmt putting arg types in typelst[].
* 3. walk through args using va_arg(args.ap, typelst[n])
* and set arglst[] to the appropriate values.
* Assumptions: Cannot use %*$... to specify variable position.
*/
(void)memset((void *)typelst, 0, sizeof(typelst));
maxnum = -1;
curargno = 0;
while ((fmt = strchr(fmt, '%')) != 0)
{
fmt++; /* skip % */
if (fmt[n = strspn(fmt, digits)] == '$')
{
curargno = atoi(fmt) - 1; /* convert to zero base */
fmt += n + 1;
}
flags = 0;
again:;
fmt += strspn(fmt, skips);
switch (*fmt++)
{
case '%': /*there is no argument! */
continue;
case 'l':
flags |= 0x1;
goto again;
case 'L':
flags |= 0x8;
goto again;
case '*': /* int argument used for value */
flags |= 0x2;
curtype = INT;
break;
case 'e':
case 'E':
case 'f':
case 'g':
case 'G':
if (flags & 0x8)
curtype = LONG_DOUBLE;
else
curtype = DOUBLE;
break;
case 's':
curtype = CHAR_PTR;
break;
case 'p':
curtype = VOID_PTR;
break;
case 'n':
if (flags & 0x1)
curtype = LONG_PTR;
else
curtype = INT_PTR;
break;
default:
if (flags & 0x1)
curtype = LONG;
else
curtype = INT;
break;
}
if (curargno >= 0 && curargno < MAXARGS)
{
typelst[curargno] = curtype;
if (maxnum < curargno)
maxnum = curargno;
}
curargno++; /* default to next in list */
if (flags & 0x2) /* took care of *, keep going */
{
flags ^= 0x2;
goto again;
}
}
for (n = 0 ; n <= maxnum; n++)
{
arglst[n] = args;
if (typelst[n] == 0)
typelst[n] = INT;
switch (typelst[n])
{
case INT:
va_arg(args.ap, int);
break;
case LONG:
va_arg(args.ap, long);
break;
case CHAR_PTR:
va_arg(args.ap, char *);
break;
case DOUBLE:
va_arg(args.ap, double);
break;
case LONG_DOUBLE:
SKIPQVAL
break;
case VOID_PTR:
va_arg(args.ap, void *);
break;
case LONG_PTR:
va_arg(args.ap, long *);
break;
case INT_PTR:
va_arg(args.ap, int *);
break;
}
}
}
/*
* This function is used to find the va_list value for arguments whose
* position is greater than MAXARGS. This function is slow, so hopefully
* MAXARGS will be big enough so that this function need only be called in
* unusual circumstances.
* pargs is assumed to contain the value of arglst[MAXARGS - 1].
*/
void
_getarg(fmt, pargs, argno)
char *fmt;
stva_list *pargs;
int argno;
{
static char *digits = "01234567890", *skips = "# +-.0123456789h$";
int i, n, curargno, flags;
char *sfmt = fmt;
int found = 1;
curargno = i = MAXARGS;
while (found)
{
fmt = sfmt;
found = 0;
while ((i != argno) && (fmt = strchr(fmt, '%')) != 0)
{
fmt++; /* skip % */
if (fmt[n = strspn(fmt, digits)] == '$')
{
curargno = atoi(fmt);
fmt += n + 1;
}
/* find conversion specifier for next argument */
if (i != curargno)
{
curargno++;
continue;
} else
found = 1;
flags = 0;
again:;
fmt += strspn(fmt, skips);
switch (*fmt++)
{
case '%': /*there is no argument! */
continue;
case 'l':
flags |= 0x1;
goto again;
case 'L':
flags |= 0x8;
goto again;
case '*': /* int argument used for value */
flags |= 0x2;
(void)va_arg((*pargs).ap, int);
break;
case 'e':
case 'E':
case 'f':
case 'g':
case 'G':
if (flags & 0x8) {
#define args (*pargs)
SKIPQVAL
#undef args
}
else
(void)va_arg((*pargs).ap, double);
break;
case 's':
(void)va_arg((*pargs).ap, char *);
break;
case 'p':
(void)va_arg((*pargs).ap, void *);
break;
case 'n':
if (flags & 0x1)
(void)va_arg((*pargs).ap, long *);
else
(void)va_arg((*pargs).ap, int *);
break;
default:
if (flags & 0x1)
(void)va_arg((*pargs).ap, long int);
else
(void)va_arg((*pargs).ap, int);
break;
}
i++;
curargno++; /* default to next in list */
if (flags & 0x2) /* took care of *, keep going */
{
flags ^= 0x2;
goto again;
}
}
/* missing specifier for parameter, assume parameter is an int */
if (!found && i != argno) {
(void)va_arg((*pargs).ap, int);
i++;
curargno++;
found = 1;
}
}
}
/*
* parse a string, mini parse
*/
static char *
_check_dol(s, val)
char *s;
int *val;
{
char *os; /* save old string */
int tmp_val = 0;
int flag = 0;
while (isdigit (*s)) {
++flag;
tmp_val = tmp_val*10 + *s - '0';
s++;
}
if (flag == 0)
return ((char *)NULL);
if (*s == '$') {
*val = tmp_val;
return(++s);
}
return ((char *)NULL);
}