diff options
Diffstat (limited to 'usr/src/lib/libbc/libc/stdio/common/doprnt.c')
-rw-r--r-- | usr/src/lib/libbc/libc/stdio/common/doprnt.c | 1279 |
1 files changed, 1279 insertions, 0 deletions
diff --git a/usr/src/lib/libbc/libc/stdio/common/doprnt.c b/usr/src/lib/libbc/libc/stdio/common/doprnt.c new file mode 100644 index 0000000000..cc7324534c --- /dev/null +++ b/usr/src/lib/libbc/libc/stdio/common/doprnt.c @@ -0,0 +1,1279 @@ +/* + * 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); +} |