/* * 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) 1996, by Sun Microsystems, Inc. * All rights reserved. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * System V.2 Emulation Stdio Library -- vfscanf * * Copyright 1985, 1992 by Mortice Kern Systems Inc. All rights reserved. * */ #ifdef M_RCSID #ifndef lint static char rcsID[] = "$Id: vfscanf.c 1.27 1995/09/20 19:07:52 ant Exp $"; #endif #endif #include #include #include #include #include #include #ifdef __FLOAT__ #include #endif #define CONVTYPE 1 #define STAR 2 #define PERCENT 3 #define NUMBER 4 #define MODCONVL 5 #define NSCAN 6 #define BRACKET 7 #define MODCONVH 8 #define BASE16 16 #define BASE10 10 #define BASE8 8 #define NOBASE 0 #define SIGNED 1 #define UNSIGNED 0 #define CBUFSIZ 100 /* size of character buffer for input conversion */ struct lexlist { char name; char type; }; static struct lexlist *lexp; static struct lexlist lexlist[] ={ '*', STAR, '%', PERCENT, 'l', MODCONVL, 'h', MODCONVH, 'n', NSCAN, '[', BRACKET, 'd', CONVTYPE, 'S', CONVTYPE, /* dummy entry (for multibyte characters) */ 's', CONVTYPE, 'u', CONVTYPE, 'c', CONVTYPE, 'x', CONVTYPE, 'o', CONVTYPE, '0', NUMBER, '1', NUMBER, '2', NUMBER, '3', NUMBER, '4', NUMBER, '5', NUMBER, '6', NUMBER, '7', NUMBER, '8', NUMBER, '9', NUMBER, 'i', CONVTYPE, 'f', CONVTYPE, 'e', CONVTYPE, 'g', CONVTYPE, 0, 0 }; static int scan(int, const char *, const char *); static int gettoken(void); static void whitespace(void); static int match(const char *, char *); static long unsigned getnum(int, int, int); static int getin(void); static void unget(int); #ifdef __FLOAT__ static double lstrtod(void); #endif static int ungot; /* getin/unget char */ static FILE *fpin; /* input file pointer */ static int pflag; /*indicator of conversion description present */ static int width; /* field width value */ static const char *fmtptr; /* format string pointer */ static int charcnt; /* number of characters scanned (for %n) */ static int from; /* token type we've come from */ static int gfail; /* getnum() fail flag, non-zero for fail */ /* * Convert formatted input from given input. * This is the workhorse for scanf, sscanf, and fscanf. * Returns the number of matched and assigned input items. */ int mks_vfscanf(FILE *pfin, const char *fmt, va_list ap) { int nitems; int ltoken; int c; int modconv; /* flag indicating conversion modifier */ int suppression; /* flag to suppress conversion */ long unsigned number; /* return value from getnumber */ ungot = EOF; fpin = pfin; fmtptr = fmt; from = 'X'; nitems = 0; charcnt = 0; for (;;) { if (from == 'X') { pflag = 0; modconv = 0; suppression = 0; width = 0; } ltoken = gettoken(); switch (ltoken) { case 0: goto retitems; case MODCONVL: case MODCONVH: switch (from) { case 'A': case 'D': case 'P': from = 'E'; modconv = ltoken; break; default: from = 'X'; break; } break; case CONVTYPE: switch (from) { int intassign; case 'E': case 'P': case 'D': case 'A': from = 'X'; intassign = 1; pflag = 0; switch (lexp->name) { case 'd': number = getnum(BASE10, width, SIGNED); if (gfail) goto retitems; break; case 'u': number = getnum(BASE10, width, UNSIGNED); if (gfail) goto retitems; break; case 'x': number = getnum(BASE16, width, SIGNED); if (gfail) goto retitems; break; case 'o': number = getnum(BASE8, width, SIGNED); if (gfail) goto retitems; break; case 'i': number = getnum(NOBASE, width, SIGNED); if (gfail) goto retitems; break; case 'c': /* 'S' dummy entry (for multibyte characters) */ case 'S': case 's': { int gotitem = 0; char *str; if (!suppression) str = va_arg(ap, char *); /* Input whitespace is not skipped * for %c, which implies that %c * can return whitespace. */ if (lexp->name != 'c') whitespace(); for (;;) { c = getin(); /* Only %s and %S stop on * whitespace. */ if (lexp->name != 'c' && isspace(c)) { unget(c); break; } if (c == EOF) { if(!gotitem) goto retitems; break; } gotitem = 1; if (!suppression) *str++ = c; if (width) { if (--width == 0) break; } } /* * ANSI C states that %c does not * terminate with a null character. */ if (!suppression && lexp->name != 'c') *str = '\0'; intassign = 0; break; } #ifdef __FLOAT__ case 'f': case 'g': case 'e': { double fresult; fresult = lstrtod(); if(gfail) goto retitems; if(suppression) break; if (modconv == MODCONVL) *(double *)va_arg(ap, double *) = fresult; else *(float *)va_arg(ap, float *) = (float)fresult; /*FALLTHROUGH*/ } #else /* !__FLOAT__ */ case 'f': case 'g': case 'e': #endif /* __FLOAT__ */ default: intassign = 0; break; } if (suppression) break; else nitems++; if (intassign == 0) break; switch (modconv) { case MODCONVH: *(short *)va_arg(ap, short *) = (short)number; break; case MODCONVL: *(long *)va_arg(ap, long *) = (long)number; break; default: *(int *)va_arg(ap, int *) = (int)number; break; } break; default: from = 'X'; break; } break; case STAR: if (from == 'P') { from = 'A'; suppression = 1; } else { from = 'X'; } break; case PERCENT: if (from == 'P') { from = 'X'; pflag = 0; c = getin(); if (c != '%') goto retitems; } else { from = 'X'; } break; case NUMBER: if (from == 'P' || from == 'A') { from = 'D'; } else { from = 'X'; } break; case NSCAN: if (from == 'P') { pflag = 0; if (!suppression) { *(int *)va_arg(ap, int *) = charcnt; } } from = 'X'; break; case BRACKET: switch (from) { case 'A': case 'D': case 'P': { char *ptr; pflag = 0; if (width == 0) width = INT_MAX; ptr = suppression ? NULL : va_arg(ap, char *); if (match(fmtptr, ptr) && !feof(fpin) && !suppression) nitems++; while (*fmtptr++ != ']') ; break; } default: break; } from = 'X'; break; default: c = *(fmtptr-1); if (c == ' ' || c == '\t' || c == '\n' || c == '\f') whitespace(); else { c = getin(); if (c != *(fmtptr-1)) goto retitems; } from = 'X'; break; } } retitems: if (ungot != EOF) { ungetc(ungot, fpin); ungot = EOF; } return nitems==0 ? EOF : nitems; } static int gettoken() { char c; if (*fmtptr == 0) return 0; /* return 0 for end of string */ c = *fmtptr++; if (pflag) { for(lexp=lexlist; lexp->name != 0; lexp++) { if (c == lexp->name) { if (lexp->type == NUMBER) { width = (int) strtol(fmtptr-1, (char **)0, BASE10); while (*fmtptr >= '0' && *fmtptr <= '9') fmtptr++; } else if (c == 'c') { /* No width specified for %c, default * is one. */ width = 1; } return lexp->type; } } return -1; } if (c == '%') { pflag = 1; from = 'P'; return gettoken(); } return -1; } static void whitespace() { register int c; do { c = getin(); } while (isspace(c)); unget(c); } static int scan(int ch, const char *str, const char *estr) { for (; str < estr; ++str) if (*str == ch) return 1; return 0; } static int match(const char *str, char *outstr) { int complement; int i; char start, end; int c; const char *bscan, *escan; if (*str == '^') { complement = 1; str++; } else complement = 0; start = *str++; end = 0; if (*str == '-') { if (str[2] == ']') end = str[1]; } if (start > end) { bscan = str - 1; while (*str++ != ']') ; escan = str - 1; for (i=0; i= start && c <= end) break; else if (outstr != NULL) *outstr++ = c; } else { if (c < start || c > end) break; else if (outstr != NULL) *outstr++ = c; } } } if (i < width) unget(c); if (outstr != NULL) *outstr = '\0'; return (i > 1); } /* * Get a number from the input stream. * The base, if zero, will be determined by the nature of the number. * A leading 0x means hexadecimal, a leading 0 for octal, otherwise decimal. * * if the width is 0 then the max input string length of number is used. * * The sign tell us that a signed number is expected (rather than the * 'u' conversion type which is unsigned). */ static long unsigned getnum(int base, int width, int sign) { char *s; char cbuf[CBUFSIZ]; /* char buffer for number */ int w; register int c; int neg; long ret; gfail = 0; whitespace(); if (width == 0) width = sizeof cbuf; neg = 0; if (sign) { c = getin(); if (c == '+' || c == '-') neg = c=='-' ? 1 : 0; else unget(c); } if (base == 0) { base = 10; c = getin(); if (c == '0') { base = 8; c = getin(); if (c == 'X' || c == 'x') base = 16; else unget(c); } else unget(c); } if (base == 10) { w = 0; s = cbuf; while (w < width && w < sizeof cbuf) { c = getin(); switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': *s++ = c; w++; continue; default: unget(c); w = width; /* force end of loop */ break; } } *s = '\0'; ret = strtol(cbuf, (char **)0, 10); goto retn; } if (base == 8) { w = 0; s = cbuf; while (w < width && w < sizeof cbuf) { c = getin(); switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': *s++ = c; w++; continue; default: unget(c); w = width; /* force end of loop */ break; } } *s = '\0'; ret = strtol(cbuf, (char **)0, 8); goto retn; } if (base == 16) { w = 0; s = cbuf; while (w < width && w < sizeof cbuf) { c = getin(); c = toupper(c); switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': *s++ = c; w++; continue; default: unget(c); w = width; /* force end of loop */ break; } } *s = '\0'; ret = strtol(cbuf, (char **)0, 16); goto retn; } /* * if we get this far then a bad base was passed. */ gfail = -1; retn: if (*cbuf == '\0') /* No number at all?? */ gfail = -1; if (neg) ret = -ret; return ret; } #ifdef __FLOAT__ static double lstrtod() { int slen; int neg, eneg; char cbuf[CBUFSIZ]; register int c; register char *sp, *s1, *s2, *s3; double total, exp, tens; neg = eneg = 1; gfail = 0; whitespace(); c = getin(); if (c == '-' || c == '+') if (c == '-') { neg = -1; c = getin(); } sp = s1 = cbuf; while (c >= '0' && c <= '9') { *sp++ = c; c = getin(); } s2 = sp; if (c == '.') { c = getin(); while (c >= '0' && c <= '9') { *sp++ = c; c = getin(); } } s3 = sp; if (c == 'e' || c == 'E') { c = getin(); if (c == '-' || c == '+') if (c == '-') { eneg = -1; c = getin(); } while (c >= '0' && c <= '9') { *sp++ = c; c = getin(); } } *sp = '\0'; if (s1 == s2 && s2 == s3) { gfail = -1; return 0.0; } unget(c); /* * convert the three strings (integer, fraction, and exponent) * into a floating point number. */ total = 0.0; tens = 1.0; for (sp=s2-1; sp >= s1; sp--) { total += (*sp -'0') * tens; tens *= 10.0; } tens = .1; for (sp=s2; sp < s3; sp++) { total += (*sp - '0') * tens; tens /= 10.0; } total *= (double)neg; exp = 0.0; tens = 1.0; if ((slen = strlen(s3)) > 0) { sp = s3 + slen - 1; for ( ; sp >= s3; sp--) { exp += (*sp - '0') * tens; tens *= 10.0; } } *sp = '\0'; exp *= (double)eneg; total *= pow(10.0, exp); return total; } #endif /* __FLOAT__ */ static int getin() { int c; if (ungot != EOF) { c = ungot; ungot = EOF; } else c = getc(fpin); charcnt++; return c; } static void unget(int c) { /* Dont' use ungetc because it doesn't work with m_fsopen */ ungot = c; charcnt--; }