190792Sgshapiro/* 2261363Sgshapiro * Copyright (c) 2000-2001, 2004 Proofpoint, Inc. and its suppliers. 390792Sgshapiro * All rights reserved. 490792Sgshapiro * Copyright (c) 1990, 1993 590792Sgshapiro * The Regents of the University of California. All rights reserved. 690792Sgshapiro * 790792Sgshapiro * This code is derived from software contributed to Berkeley by 890792Sgshapiro * Chris Torek. 990792Sgshapiro * 1090792Sgshapiro * By using this file, you agree to the terms and conditions set 1190792Sgshapiro * forth in the LICENSE file which can be found at the top level of 1290792Sgshapiro * the sendmail distribution. 1390792Sgshapiro */ 1490792Sgshapiro 1590792Sgshapiro#include <sm/gen.h> 16266692SgshapiroSM_IDSTR(id, "@(#)$Id: vfscanf.c,v 1.55 2013-11-22 20:51:44 ca Exp $") 1790792Sgshapiro 1890792Sgshapiro#include <ctype.h> 1990792Sgshapiro#include <stdlib.h> 2090792Sgshapiro#include <errno.h> 2190792Sgshapiro#include <setjmp.h> 22157001Sgshapiro#include <sm/time.h> 2390792Sgshapiro#include <sm/varargs.h> 2490792Sgshapiro#include <sm/config.h> 2590792Sgshapiro#include <sm/io.h> 2690792Sgshapiro#include <sm/signal.h> 2790792Sgshapiro#include <sm/clock.h> 2890792Sgshapiro#include <sm/string.h> 2990792Sgshapiro#include "local.h" 3090792Sgshapiro 3190792Sgshapiro#define BUF 513 /* Maximum length of numeric string. */ 3290792Sgshapiro 3390792Sgshapiro/* Flags used during conversion. */ 3490792Sgshapiro#define LONG 0x01 /* l: long or double */ 3590792Sgshapiro#define SHORT 0x04 /* h: short */ 3690792Sgshapiro#define QUAD 0x08 /* q: quad (same as ll) */ 3790792Sgshapiro#define SUPPRESS 0x10 /* suppress assignment */ 3890792Sgshapiro#define POINTER 0x20 /* weird %p pointer (`fake hex') */ 3990792Sgshapiro#define NOSKIP 0x40 /* do not skip blanks */ 4090792Sgshapiro 4190792Sgshapiro/* 4290792Sgshapiro** The following are used in numeric conversions only: 4390792Sgshapiro** SIGNOK, NDIGITS, DPTOK, and EXPOK are for floating point; 4490792Sgshapiro** SIGNOK, NDIGITS, PFXOK, and NZDIGITS are for integral. 4590792Sgshapiro*/ 4690792Sgshapiro 4790792Sgshapiro#define SIGNOK 0x080 /* +/- is (still) legal */ 4890792Sgshapiro#define NDIGITS 0x100 /* no digits detected */ 4990792Sgshapiro 5090792Sgshapiro#define DPTOK 0x200 /* (float) decimal point is still legal */ 5190792Sgshapiro#define EXPOK 0x400 /* (float) exponent (e+3, etc) still legal */ 5290792Sgshapiro 5390792Sgshapiro#define PFXOK 0x200 /* 0x prefix is (still) legal */ 5490792Sgshapiro#define NZDIGITS 0x400 /* no zero digits detected */ 5590792Sgshapiro 5690792Sgshapiro/* Conversion types. */ 5790792Sgshapiro#define CT_CHAR 0 /* %c conversion */ 5890792Sgshapiro#define CT_CCL 1 /* %[...] conversion */ 5990792Sgshapiro#define CT_STRING 2 /* %s conversion */ 6090792Sgshapiro#define CT_INT 3 /* integer, i.e., strtoll or strtoull */ 6190792Sgshapiro#define CT_FLOAT 4 /* floating, i.e., strtod */ 6290792Sgshapiro 63141858Sgshapirostatic void scanalrm __P((int)); 64141858Sgshapirostatic unsigned char *sm_sccl __P((char *, unsigned char *)); 65141858Sgshapirostatic jmp_buf ScanTimeOut; 6690792Sgshapiro 6790792Sgshapiro/* 6890792Sgshapiro** SCANALRM -- handler when timeout activated for sm_io_vfscanf() 6990792Sgshapiro** 7090792Sgshapiro** Returns flow of control to where setjmp(ScanTimeOut) was set. 7190792Sgshapiro** 7290792Sgshapiro** Parameters: 7390792Sgshapiro** sig -- unused 7490792Sgshapiro** 7590792Sgshapiro** Returns: 7690792Sgshapiro** does not return 7790792Sgshapiro** 7890792Sgshapiro** Side Effects: 7990792Sgshapiro** returns flow of control to setjmp(ScanTimeOut). 8090792Sgshapiro** 8190792Sgshapiro** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 8290792Sgshapiro** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 8390792Sgshapiro** DOING. 8490792Sgshapiro*/ 8590792Sgshapiro 8690792Sgshapiro/* ARGSUSED0 */ 8790792Sgshapirostatic void 8890792Sgshapiroscanalrm(sig) 8990792Sgshapiro int sig; 9090792Sgshapiro{ 9190792Sgshapiro longjmp(ScanTimeOut, 1); 9290792Sgshapiro} 9390792Sgshapiro 9490792Sgshapiro/* 9590792Sgshapiro** SM_VFSCANF -- convert input into data units 9690792Sgshapiro** 9790792Sgshapiro** Parameters: 9890792Sgshapiro** fp -- file pointer for input data 9990792Sgshapiro** timeout -- time intvl allowed to complete (milliseconds) 10090792Sgshapiro** fmt0 -- format for finding data units 10190792Sgshapiro** ap -- vectors for memory location for storing data units 10290792Sgshapiro** 10390792Sgshapiro** Results: 10490792Sgshapiro** Success: number of data units assigned 10590792Sgshapiro** Failure: SM_IO_EOF 10690792Sgshapiro*/ 10790792Sgshapiro 10890792Sgshapiroint 10990792Sgshapirosm_vfscanf(fp, timeout, fmt0, ap) 11090792Sgshapiro register SM_FILE_T *fp; 11190792Sgshapiro int SM_NONVOLATILE timeout; 11290792Sgshapiro char const *fmt0; 11390792Sgshapiro va_list SM_NONVOLATILE ap; 11490792Sgshapiro{ 11590792Sgshapiro register unsigned char *SM_NONVOLATILE fmt = (unsigned char *) fmt0; 11690792Sgshapiro register int c; /* character from format, or conversion */ 11790792Sgshapiro register size_t width; /* field width, or 0 */ 11890792Sgshapiro register char *p; /* points into all kinds of strings */ 11990792Sgshapiro register int n; /* handy integer */ 12090792Sgshapiro register int flags; /* flags as defined above */ 12190792Sgshapiro register char *p0; /* saves original value of p when necessary */ 12290792Sgshapiro int nassigned; /* number of fields assigned */ 12390792Sgshapiro int nread; /* number of characters consumed from fp */ 12490792Sgshapiro int base; /* base argument to strtoll/strtoull */ 125168515Sgshapiro 126168515Sgshapiro /* conversion function (strtoll/strtoull) */ 127168515Sgshapiro ULONGLONG_T (*ccfn) __P((const char *, char **, int)); 12890792Sgshapiro char ccltab[256]; /* character class table for %[...] */ 12990792Sgshapiro char buf[BUF]; /* buffer for numeric conversions */ 13090792Sgshapiro SM_EVENT *evt = NULL; 13190792Sgshapiro 13290792Sgshapiro /* `basefix' is used to avoid `if' tests in the integer scanner */ 13390792Sgshapiro static short basefix[17] = 13490792Sgshapiro { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; 13590792Sgshapiro 13690792Sgshapiro if (timeout == SM_TIME_DEFAULT) 13790792Sgshapiro timeout = fp->f_timeout; 13890792Sgshapiro if (timeout == SM_TIME_IMMEDIATE) 13990792Sgshapiro { 14090792Sgshapiro /* 14190792Sgshapiro ** Filling the buffer will take time and we are wanted to 14290792Sgshapiro ** return immediately. So... 14390792Sgshapiro */ 14490792Sgshapiro 14590792Sgshapiro errno = EAGAIN; 14690792Sgshapiro return SM_IO_EOF; 14790792Sgshapiro } 14890792Sgshapiro 14990792Sgshapiro if (timeout != SM_TIME_FOREVER) 15090792Sgshapiro { 15190792Sgshapiro if (setjmp(ScanTimeOut) != 0) 15290792Sgshapiro { 15390792Sgshapiro errno = EAGAIN; 15490792Sgshapiro return SM_IO_EOF; 15590792Sgshapiro } 15690792Sgshapiro 15790792Sgshapiro evt = sm_seteventm(timeout, scanalrm, 0); 15890792Sgshapiro } 15990792Sgshapiro 16090792Sgshapiro nassigned = 0; 16190792Sgshapiro nread = 0; 16290792Sgshapiro base = 0; /* XXX just to keep gcc happy */ 16390792Sgshapiro ccfn = NULL; /* XXX just to keep gcc happy */ 16490792Sgshapiro for (;;) 16590792Sgshapiro { 16690792Sgshapiro c = *fmt++; 16790792Sgshapiro if (c == 0) 16890792Sgshapiro { 16990792Sgshapiro if (evt != NULL) 17090792Sgshapiro sm_clrevent(evt); /* undo our timeout */ 17190792Sgshapiro return nassigned; 17290792Sgshapiro } 17390792Sgshapiro if (isspace(c)) 17490792Sgshapiro { 17590792Sgshapiro while ((fp->f_r > 0 || sm_refill(fp, SM_TIME_FOREVER) 17690792Sgshapiro == 0) && 17790792Sgshapiro isspace(*fp->f_p)) 17890792Sgshapiro nread++, fp->f_r--, fp->f_p++; 17990792Sgshapiro continue; 18090792Sgshapiro } 18190792Sgshapiro if (c != '%') 18290792Sgshapiro goto literal; 18390792Sgshapiro width = 0; 18490792Sgshapiro flags = 0; 18590792Sgshapiro 18690792Sgshapiro /* 18790792Sgshapiro ** switch on the format. continue if done; 18890792Sgshapiro ** break once format type is derived. 18990792Sgshapiro */ 19090792Sgshapiro 19190792Sgshapiroagain: c = *fmt++; 19290792Sgshapiro switch (c) 19390792Sgshapiro { 19490792Sgshapiro case '%': 19590792Sgshapiroliteral: 19690792Sgshapiro if (fp->f_r <= 0 && sm_refill(fp, SM_TIME_FOREVER)) 19790792Sgshapiro goto input_failure; 19890792Sgshapiro if (*fp->f_p != c) 19990792Sgshapiro goto match_failure; 20090792Sgshapiro fp->f_r--, fp->f_p++; 20190792Sgshapiro nread++; 20290792Sgshapiro continue; 20390792Sgshapiro 20490792Sgshapiro case '*': 20590792Sgshapiro flags |= SUPPRESS; 20690792Sgshapiro goto again; 20790792Sgshapiro case 'h': 20890792Sgshapiro flags |= SHORT; 20990792Sgshapiro goto again; 21090792Sgshapiro case 'l': 21190792Sgshapiro if (*fmt == 'l') 21290792Sgshapiro { 21390792Sgshapiro fmt++; 21490792Sgshapiro flags |= QUAD; 21590792Sgshapiro } 21690792Sgshapiro else 21790792Sgshapiro { 21890792Sgshapiro flags |= LONG; 21990792Sgshapiro } 22090792Sgshapiro goto again; 22190792Sgshapiro case 'q': 22290792Sgshapiro flags |= QUAD; 22390792Sgshapiro goto again; 22490792Sgshapiro 22590792Sgshapiro case '0': case '1': case '2': case '3': case '4': 22690792Sgshapiro case '5': case '6': case '7': case '8': case '9': 22790792Sgshapiro width = width * 10 + c - '0'; 22890792Sgshapiro goto again; 22990792Sgshapiro 23090792Sgshapiro /* 23190792Sgshapiro ** Conversions. 23290792Sgshapiro ** Those marked `compat' are for 4.[123]BSD compatibility. 23390792Sgshapiro ** 23490792Sgshapiro ** (According to ANSI, E and X formats are supposed 23590792Sgshapiro ** to the same as e and x. Sorry about that.) 23690792Sgshapiro */ 23790792Sgshapiro 23890792Sgshapiro case 'D': /* compat */ 23990792Sgshapiro flags |= LONG; 24090792Sgshapiro /* FALLTHROUGH */ 24190792Sgshapiro case 'd': 24290792Sgshapiro c = CT_INT; 24390792Sgshapiro ccfn = (ULONGLONG_T (*)())sm_strtoll; 24490792Sgshapiro base = 10; 24590792Sgshapiro break; 24690792Sgshapiro 24790792Sgshapiro case 'i': 24890792Sgshapiro c = CT_INT; 24990792Sgshapiro ccfn = (ULONGLONG_T (*)())sm_strtoll; 25090792Sgshapiro base = 0; 25190792Sgshapiro break; 25290792Sgshapiro 25390792Sgshapiro case 'O': /* compat */ 25490792Sgshapiro flags |= LONG; 25590792Sgshapiro /* FALLTHROUGH */ 25690792Sgshapiro case 'o': 25790792Sgshapiro c = CT_INT; 25890792Sgshapiro ccfn = sm_strtoull; 25990792Sgshapiro base = 8; 26090792Sgshapiro break; 26190792Sgshapiro 26290792Sgshapiro case 'u': 26390792Sgshapiro c = CT_INT; 26490792Sgshapiro ccfn = sm_strtoull; 26590792Sgshapiro base = 10; 26690792Sgshapiro break; 26790792Sgshapiro 26890792Sgshapiro case 'X': 26990792Sgshapiro case 'x': 27090792Sgshapiro flags |= PFXOK; /* enable 0x prefixing */ 27190792Sgshapiro c = CT_INT; 27290792Sgshapiro ccfn = sm_strtoull; 27390792Sgshapiro base = 16; 27490792Sgshapiro break; 27590792Sgshapiro 27690792Sgshapiro case 'E': 27790792Sgshapiro case 'G': 27890792Sgshapiro case 'e': 27990792Sgshapiro case 'f': 28090792Sgshapiro case 'g': 28190792Sgshapiro c = CT_FLOAT; 28290792Sgshapiro break; 28390792Sgshapiro 28490792Sgshapiro case 's': 28590792Sgshapiro c = CT_STRING; 28690792Sgshapiro break; 28790792Sgshapiro 28890792Sgshapiro case '[': 28990792Sgshapiro fmt = sm_sccl(ccltab, fmt); 29090792Sgshapiro flags |= NOSKIP; 29190792Sgshapiro c = CT_CCL; 29290792Sgshapiro break; 29390792Sgshapiro 29490792Sgshapiro case 'c': 29590792Sgshapiro flags |= NOSKIP; 29690792Sgshapiro c = CT_CHAR; 29790792Sgshapiro break; 29890792Sgshapiro 29990792Sgshapiro case 'p': /* pointer format is like hex */ 30090792Sgshapiro flags |= POINTER | PFXOK; 30190792Sgshapiro c = CT_INT; 30290792Sgshapiro ccfn = sm_strtoull; 30390792Sgshapiro base = 16; 30490792Sgshapiro break; 30590792Sgshapiro 30690792Sgshapiro case 'n': 30790792Sgshapiro if (flags & SUPPRESS) /* ??? */ 30890792Sgshapiro continue; 30990792Sgshapiro if (flags & SHORT) 31090792Sgshapiro *SM_VA_ARG(ap, short *) = nread; 31190792Sgshapiro else if (flags & LONG) 31290792Sgshapiro *SM_VA_ARG(ap, long *) = nread; 31390792Sgshapiro else 31490792Sgshapiro *SM_VA_ARG(ap, int *) = nread; 31590792Sgshapiro continue; 31690792Sgshapiro 31790792Sgshapiro /* Disgusting backwards compatibility hacks. XXX */ 31890792Sgshapiro case '\0': /* compat */ 31990792Sgshapiro if (evt != NULL) 32090792Sgshapiro sm_clrevent(evt); /* undo our timeout */ 32190792Sgshapiro return SM_IO_EOF; 32290792Sgshapiro 32390792Sgshapiro default: /* compat */ 32490792Sgshapiro if (isupper(c)) 32590792Sgshapiro flags |= LONG; 32690792Sgshapiro c = CT_INT; 32790792Sgshapiro ccfn = (ULONGLONG_T (*)()) sm_strtoll; 32890792Sgshapiro base = 10; 32990792Sgshapiro break; 33090792Sgshapiro } 33190792Sgshapiro 33290792Sgshapiro /* We have a conversion that requires input. */ 33390792Sgshapiro if (fp->f_r <= 0 && sm_refill(fp, SM_TIME_FOREVER)) 33490792Sgshapiro goto input_failure; 33590792Sgshapiro 33690792Sgshapiro /* 33790792Sgshapiro ** Consume leading white space, except for formats 33890792Sgshapiro ** that suppress this. 33990792Sgshapiro */ 34090792Sgshapiro 34190792Sgshapiro if ((flags & NOSKIP) == 0) 34290792Sgshapiro { 34390792Sgshapiro while (isspace(*fp->f_p)) 34490792Sgshapiro { 34590792Sgshapiro nread++; 34690792Sgshapiro if (--fp->f_r > 0) 34790792Sgshapiro fp->f_p++; 34890792Sgshapiro else if (sm_refill(fp, SM_TIME_FOREVER)) 34990792Sgshapiro goto input_failure; 35090792Sgshapiro } 35190792Sgshapiro /* 35290792Sgshapiro ** Note that there is at least one character in 35390792Sgshapiro ** the buffer, so conversions that do not set NOSKIP 35490792Sgshapiro ** can no longer result in an input failure. 35590792Sgshapiro */ 35690792Sgshapiro } 35790792Sgshapiro 35890792Sgshapiro /* Do the conversion. */ 35990792Sgshapiro switch (c) 36090792Sgshapiro { 36190792Sgshapiro case CT_CHAR: 36290792Sgshapiro /* scan arbitrary characters (sets NOSKIP) */ 36390792Sgshapiro if (width == 0) 36490792Sgshapiro width = 1; 36590792Sgshapiro if (flags & SUPPRESS) 36690792Sgshapiro { 36790792Sgshapiro size_t sum = 0; 36890792Sgshapiro for (;;) 36990792Sgshapiro { 37090792Sgshapiro if ((size_t) (n = fp->f_r) < width) 37190792Sgshapiro { 37290792Sgshapiro sum += n; 37390792Sgshapiro width -= n; 37490792Sgshapiro fp->f_p += n; 37590792Sgshapiro if (sm_refill(fp, 37690792Sgshapiro SM_TIME_FOREVER)) 37790792Sgshapiro { 37890792Sgshapiro if (sum == 0) 37990792Sgshapiro goto input_failure; 38090792Sgshapiro break; 38190792Sgshapiro } 38290792Sgshapiro } 38390792Sgshapiro else 38490792Sgshapiro { 38590792Sgshapiro sum += width; 38690792Sgshapiro fp->f_r -= width; 38790792Sgshapiro fp->f_p += width; 38890792Sgshapiro break; 38990792Sgshapiro } 39090792Sgshapiro } 39190792Sgshapiro nread += sum; 39290792Sgshapiro } 39390792Sgshapiro else 39490792Sgshapiro { 39590792Sgshapiro size_t r; 39690792Sgshapiro 39790792Sgshapiro r = sm_io_read(fp, SM_TIME_FOREVER, 39890792Sgshapiro (void *) SM_VA_ARG(ap, char *), 39990792Sgshapiro width); 40090792Sgshapiro if (r == 0) 40190792Sgshapiro goto input_failure; 40290792Sgshapiro nread += r; 40390792Sgshapiro nassigned++; 40490792Sgshapiro } 40590792Sgshapiro break; 40690792Sgshapiro 40790792Sgshapiro case CT_CCL: 40890792Sgshapiro /* scan a (nonempty) character class (sets NOSKIP) */ 40990792Sgshapiro if (width == 0) 41090792Sgshapiro width = (size_t)~0; /* `infinity' */ 41190792Sgshapiro 41290792Sgshapiro /* take only those things in the class */ 41390792Sgshapiro if (flags & SUPPRESS) 41490792Sgshapiro { 41590792Sgshapiro n = 0; 41690792Sgshapiro while (ccltab[*fp->f_p] != '\0') 41790792Sgshapiro { 41890792Sgshapiro n++, fp->f_r--, fp->f_p++; 41990792Sgshapiro if (--width == 0) 42090792Sgshapiro break; 42190792Sgshapiro if (fp->f_r <= 0 && 42290792Sgshapiro sm_refill(fp, SM_TIME_FOREVER)) 42390792Sgshapiro { 42490792Sgshapiro if (n == 0) /* XXX how? */ 42590792Sgshapiro goto input_failure; 42690792Sgshapiro break; 42790792Sgshapiro } 42890792Sgshapiro } 42990792Sgshapiro if (n == 0) 43090792Sgshapiro goto match_failure; 43190792Sgshapiro } 43290792Sgshapiro else 43390792Sgshapiro { 43490792Sgshapiro p0 = p = SM_VA_ARG(ap, char *); 43590792Sgshapiro while (ccltab[*fp->f_p] != '\0') 43690792Sgshapiro { 43790792Sgshapiro fp->f_r--; 43890792Sgshapiro *p++ = *fp->f_p++; 43990792Sgshapiro if (--width == 0) 44090792Sgshapiro break; 44190792Sgshapiro if (fp->f_r <= 0 && 44290792Sgshapiro sm_refill(fp, SM_TIME_FOREVER)) 44390792Sgshapiro { 44490792Sgshapiro if (p == p0) 44590792Sgshapiro goto input_failure; 44690792Sgshapiro break; 44790792Sgshapiro } 44890792Sgshapiro } 44990792Sgshapiro n = p - p0; 45090792Sgshapiro if (n == 0) 45190792Sgshapiro goto match_failure; 45290792Sgshapiro *p = 0; 45390792Sgshapiro nassigned++; 45490792Sgshapiro } 45590792Sgshapiro nread += n; 45690792Sgshapiro break; 45790792Sgshapiro 45890792Sgshapiro case CT_STRING: 45990792Sgshapiro /* like CCL, but zero-length string OK, & no NOSKIP */ 46090792Sgshapiro if (width == 0) 46190792Sgshapiro width = (size_t)~0; 46290792Sgshapiro if (flags & SUPPRESS) 46390792Sgshapiro { 46490792Sgshapiro n = 0; 46590792Sgshapiro while (!isspace(*fp->f_p)) 46690792Sgshapiro { 46790792Sgshapiro n++, fp->f_r--, fp->f_p++; 46890792Sgshapiro if (--width == 0) 46990792Sgshapiro break; 47090792Sgshapiro if (fp->f_r <= 0 && 47190792Sgshapiro sm_refill(fp, SM_TIME_FOREVER)) 47290792Sgshapiro break; 47390792Sgshapiro } 47490792Sgshapiro nread += n; 47590792Sgshapiro } 47690792Sgshapiro else 47790792Sgshapiro { 47890792Sgshapiro p0 = p = SM_VA_ARG(ap, char *); 47990792Sgshapiro while (!isspace(*fp->f_p)) 48090792Sgshapiro { 48190792Sgshapiro fp->f_r--; 48290792Sgshapiro *p++ = *fp->f_p++; 48390792Sgshapiro if (--width == 0) 48490792Sgshapiro break; 48590792Sgshapiro if (fp->f_r <= 0 && 48690792Sgshapiro sm_refill(fp, SM_TIME_FOREVER)) 48790792Sgshapiro break; 48890792Sgshapiro } 48990792Sgshapiro *p = 0; 49090792Sgshapiro nread += p - p0; 49190792Sgshapiro nassigned++; 49290792Sgshapiro } 49390792Sgshapiro continue; 49490792Sgshapiro 49590792Sgshapiro case CT_INT: 49690792Sgshapiro /* scan an integer as if by strtoll/strtoull */ 49790792Sgshapiro#if SM_CONF_BROKEN_SIZE_T 49890792Sgshapiro if (width == 0 || width > sizeof(buf) - 1) 49990792Sgshapiro width = sizeof(buf) - 1; 50090792Sgshapiro#else /* SM_CONF_BROKEN_SIZE_T */ 50190792Sgshapiro /* size_t is unsigned, hence this optimisation */ 50290792Sgshapiro if (--width > sizeof(buf) - 2) 50390792Sgshapiro width = sizeof(buf) - 2; 50490792Sgshapiro width++; 50590792Sgshapiro#endif /* SM_CONF_BROKEN_SIZE_T */ 50690792Sgshapiro flags |= SIGNOK | NDIGITS | NZDIGITS; 50790792Sgshapiro for (p = buf; width > 0; width--) 50890792Sgshapiro { 50990792Sgshapiro c = *fp->f_p; 51090792Sgshapiro 51190792Sgshapiro /* 51290792Sgshapiro ** Switch on the character; `goto ok' 51390792Sgshapiro ** if we accept it as a part of number. 51490792Sgshapiro */ 51590792Sgshapiro 51690792Sgshapiro switch (c) 51790792Sgshapiro { 51890792Sgshapiro 51990792Sgshapiro /* 52090792Sgshapiro ** The digit 0 is always legal, but is 52190792Sgshapiro ** special. For %i conversions, if no 52290792Sgshapiro ** digits (zero or nonzero) have been 52390792Sgshapiro ** scanned (only signs), we will have 52490792Sgshapiro ** base==0. In that case, we should set 52590792Sgshapiro ** it to 8 and enable 0x prefixing. 52690792Sgshapiro ** Also, if we have not scanned zero digits 52790792Sgshapiro ** before this, do not turn off prefixing 52890792Sgshapiro ** (someone else will turn it off if we 52990792Sgshapiro ** have scanned any nonzero digits). 53090792Sgshapiro */ 53190792Sgshapiro 53290792Sgshapiro case '0': 53390792Sgshapiro if (base == 0) 53490792Sgshapiro { 53590792Sgshapiro base = 8; 53690792Sgshapiro flags |= PFXOK; 53790792Sgshapiro } 53890792Sgshapiro if (flags & NZDIGITS) 53990792Sgshapiro flags &= ~(SIGNOK|NZDIGITS|NDIGITS); 54090792Sgshapiro else 54190792Sgshapiro flags &= ~(SIGNOK|PFXOK|NDIGITS); 54290792Sgshapiro goto ok; 54390792Sgshapiro 54490792Sgshapiro /* 1 through 7 always legal */ 54590792Sgshapiro case '1': case '2': case '3': 54690792Sgshapiro case '4': case '5': case '6': case '7': 54790792Sgshapiro base = basefix[base]; 54890792Sgshapiro flags &= ~(SIGNOK | PFXOK | NDIGITS); 54990792Sgshapiro goto ok; 55090792Sgshapiro 55190792Sgshapiro /* digits 8 and 9 ok iff decimal or hex */ 55290792Sgshapiro case '8': case '9': 55390792Sgshapiro base = basefix[base]; 55490792Sgshapiro if (base <= 8) 55590792Sgshapiro break; /* not legal here */ 55690792Sgshapiro flags &= ~(SIGNOK | PFXOK | NDIGITS); 55790792Sgshapiro goto ok; 55890792Sgshapiro 55990792Sgshapiro /* letters ok iff hex */ 56090792Sgshapiro case 'A': case 'B': case 'C': 56190792Sgshapiro case 'D': case 'E': case 'F': 56290792Sgshapiro case 'a': case 'b': case 'c': 56390792Sgshapiro case 'd': case 'e': case 'f': 56490792Sgshapiro 56590792Sgshapiro /* no need to fix base here */ 56690792Sgshapiro if (base <= 10) 56790792Sgshapiro break; /* not legal here */ 56890792Sgshapiro flags &= ~(SIGNOK | PFXOK | NDIGITS); 56990792Sgshapiro goto ok; 57090792Sgshapiro 57190792Sgshapiro /* sign ok only as first character */ 57290792Sgshapiro case '+': case '-': 57390792Sgshapiro if (flags & SIGNOK) 57490792Sgshapiro { 57590792Sgshapiro flags &= ~SIGNOK; 57690792Sgshapiro goto ok; 57790792Sgshapiro } 57890792Sgshapiro break; 57990792Sgshapiro 58090792Sgshapiro /* x ok iff flag still set & 2nd char */ 58190792Sgshapiro case 'x': case 'X': 58290792Sgshapiro if (flags & PFXOK && p == buf + 1) 58390792Sgshapiro { 58490792Sgshapiro base = 16; /* if %i */ 58590792Sgshapiro flags &= ~PFXOK; 58690792Sgshapiro goto ok; 58790792Sgshapiro } 58890792Sgshapiro break; 58990792Sgshapiro } 59090792Sgshapiro 59190792Sgshapiro /* 59290792Sgshapiro ** If we got here, c is not a legal character 59390792Sgshapiro ** for a number. Stop accumulating digits. 59490792Sgshapiro */ 59590792Sgshapiro 59690792Sgshapiro break; 59790792Sgshapiro ok: 59890792Sgshapiro /* c is legal: store it and look at the next. */ 59990792Sgshapiro *p++ = c; 60090792Sgshapiro if (--fp->f_r > 0) 60190792Sgshapiro fp->f_p++; 60290792Sgshapiro else if (sm_refill(fp, SM_TIME_FOREVER)) 60390792Sgshapiro break; /* SM_IO_EOF */ 60490792Sgshapiro } 60590792Sgshapiro 60690792Sgshapiro /* 60790792Sgshapiro ** If we had only a sign, it is no good; push 60890792Sgshapiro ** back the sign. If the number ends in `x', 60990792Sgshapiro ** it was [sign] '0' 'x', so push back the x 61090792Sgshapiro ** and treat it as [sign] '0'. 61190792Sgshapiro */ 61290792Sgshapiro 61390792Sgshapiro if (flags & NDIGITS) 61490792Sgshapiro { 61590792Sgshapiro if (p > buf) 61690792Sgshapiro (void) sm_io_ungetc(fp, SM_TIME_DEFAULT, 61790792Sgshapiro *(unsigned char *)--p); 61890792Sgshapiro goto match_failure; 61990792Sgshapiro } 62090792Sgshapiro c = ((unsigned char *)p)[-1]; 62190792Sgshapiro if (c == 'x' || c == 'X') 62290792Sgshapiro { 62390792Sgshapiro --p; 62490792Sgshapiro (void) sm_io_ungetc(fp, SM_TIME_DEFAULT, c); 62590792Sgshapiro } 62690792Sgshapiro if ((flags & SUPPRESS) == 0) 62790792Sgshapiro { 62890792Sgshapiro ULONGLONG_T res; 62990792Sgshapiro 63090792Sgshapiro *p = 0; 63190792Sgshapiro res = (*ccfn)(buf, (char **)NULL, base); 63290792Sgshapiro if (flags & POINTER) 63390792Sgshapiro *SM_VA_ARG(ap, void **) = 63490792Sgshapiro (void *)(long) res; 63590792Sgshapiro else if (flags & QUAD) 63690792Sgshapiro *SM_VA_ARG(ap, LONGLONG_T *) = res; 63790792Sgshapiro else if (flags & LONG) 63890792Sgshapiro *SM_VA_ARG(ap, long *) = res; 63990792Sgshapiro else if (flags & SHORT) 64090792Sgshapiro *SM_VA_ARG(ap, short *) = res; 64190792Sgshapiro else 64290792Sgshapiro *SM_VA_ARG(ap, int *) = res; 64390792Sgshapiro nassigned++; 64490792Sgshapiro } 64590792Sgshapiro nread += p - buf; 64690792Sgshapiro break; 64790792Sgshapiro 64890792Sgshapiro case CT_FLOAT: 64990792Sgshapiro /* scan a floating point number as if by strtod */ 65090792Sgshapiro if (width == 0 || width > sizeof(buf) - 1) 65190792Sgshapiro width = sizeof(buf) - 1; 65290792Sgshapiro flags |= SIGNOK | NDIGITS | DPTOK | EXPOK; 65390792Sgshapiro for (p = buf; width; width--) 65490792Sgshapiro { 65590792Sgshapiro c = *fp->f_p; 65690792Sgshapiro 65790792Sgshapiro /* 65890792Sgshapiro ** This code mimicks the integer conversion 65990792Sgshapiro ** code, but is much simpler. 66090792Sgshapiro */ 66190792Sgshapiro 66290792Sgshapiro switch (c) 66390792Sgshapiro { 66490792Sgshapiro 66590792Sgshapiro case '0': case '1': case '2': case '3': 66690792Sgshapiro case '4': case '5': case '6': case '7': 66790792Sgshapiro case '8': case '9': 66890792Sgshapiro flags &= ~(SIGNOK | NDIGITS); 66990792Sgshapiro goto fok; 67090792Sgshapiro 67190792Sgshapiro case '+': case '-': 67290792Sgshapiro if (flags & SIGNOK) 67390792Sgshapiro { 67490792Sgshapiro flags &= ~SIGNOK; 67590792Sgshapiro goto fok; 67690792Sgshapiro } 67790792Sgshapiro break; 67890792Sgshapiro case '.': 67990792Sgshapiro if (flags & DPTOK) 68090792Sgshapiro { 68190792Sgshapiro flags &= ~(SIGNOK | DPTOK); 68290792Sgshapiro goto fok; 68390792Sgshapiro } 68490792Sgshapiro break; 68590792Sgshapiro case 'e': case 'E': 68690792Sgshapiro 68790792Sgshapiro /* no exponent without some digits */ 68890792Sgshapiro if ((flags&(NDIGITS|EXPOK)) == EXPOK) 68990792Sgshapiro { 69090792Sgshapiro flags = 69190792Sgshapiro (flags & ~(EXPOK|DPTOK)) | 69290792Sgshapiro SIGNOK | NDIGITS; 69390792Sgshapiro goto fok; 69490792Sgshapiro } 69590792Sgshapiro break; 69690792Sgshapiro } 69790792Sgshapiro break; 69890792Sgshapiro fok: 69990792Sgshapiro *p++ = c; 70090792Sgshapiro if (--fp->f_r > 0) 70190792Sgshapiro fp->f_p++; 70290792Sgshapiro else if (sm_refill(fp, SM_TIME_FOREVER)) 70390792Sgshapiro break; /* SM_IO_EOF */ 70490792Sgshapiro } 70590792Sgshapiro 70690792Sgshapiro /* 70790792Sgshapiro ** If no digits, might be missing exponent digits 70890792Sgshapiro ** (just give back the exponent) or might be missing 70990792Sgshapiro ** regular digits, but had sign and/or decimal point. 71090792Sgshapiro */ 71190792Sgshapiro 71290792Sgshapiro if (flags & NDIGITS) 71390792Sgshapiro { 71490792Sgshapiro if (flags & EXPOK) 71590792Sgshapiro { 71690792Sgshapiro /* no digits at all */ 71790792Sgshapiro while (p > buf) 71890792Sgshapiro (void) sm_io_ungetc(fp, 71990792Sgshapiro SM_TIME_DEFAULT, 72090792Sgshapiro *(unsigned char *)--p); 72190792Sgshapiro goto match_failure; 72290792Sgshapiro } 72390792Sgshapiro 72490792Sgshapiro /* just a bad exponent (e and maybe sign) */ 72590792Sgshapiro c = *(unsigned char *) --p; 72690792Sgshapiro if (c != 'e' && c != 'E') 72790792Sgshapiro { 72890792Sgshapiro (void) sm_io_ungetc(fp, SM_TIME_DEFAULT, 72990792Sgshapiro c); /* sign */ 73090792Sgshapiro c = *(unsigned char *)--p; 73190792Sgshapiro } 73290792Sgshapiro (void) sm_io_ungetc(fp, SM_TIME_DEFAULT, c); 73390792Sgshapiro } 73490792Sgshapiro if ((flags & SUPPRESS) == 0) 73590792Sgshapiro { 73690792Sgshapiro double res; 73790792Sgshapiro 73890792Sgshapiro *p = 0; 73990792Sgshapiro res = strtod(buf, (char **) NULL); 74090792Sgshapiro if (flags & LONG) 74190792Sgshapiro *SM_VA_ARG(ap, double *) = res; 74290792Sgshapiro else 74390792Sgshapiro *SM_VA_ARG(ap, float *) = res; 74490792Sgshapiro nassigned++; 74590792Sgshapiro } 74690792Sgshapiro nread += p - buf; 74790792Sgshapiro break; 74890792Sgshapiro } 74990792Sgshapiro } 75090792Sgshapiroinput_failure: 75190792Sgshapiro if (evt != NULL) 75290792Sgshapiro sm_clrevent(evt); /* undo our timeout */ 75390792Sgshapiro return nassigned ? nassigned : -1; 75490792Sgshapiromatch_failure: 75590792Sgshapiro if (evt != NULL) 75690792Sgshapiro sm_clrevent(evt); /* undo our timeout */ 75790792Sgshapiro return nassigned; 75890792Sgshapiro} 75990792Sgshapiro 76090792Sgshapiro/* 76190792Sgshapiro** SM_SCCL -- sequenced character comparison list 76290792Sgshapiro** 76390792Sgshapiro** Fill in the given table from the scanset at the given format 76490792Sgshapiro** (just after `['). Return a pointer to the character past the 76590792Sgshapiro** closing `]'. The table has a 1 wherever characters should be 76690792Sgshapiro** considered part of the scanset. 76790792Sgshapiro** 76890792Sgshapiro** Parameters: 76990792Sgshapiro** tab -- array flagging "active" char's to match (returned) 77090792Sgshapiro** fmt -- character list (within "[]") 77190792Sgshapiro** 77290792Sgshapiro** Results: 77390792Sgshapiro*/ 77490792Sgshapiro 77590792Sgshapirostatic unsigned char * 77690792Sgshapirosm_sccl(tab, fmt) 77790792Sgshapiro register char *tab; 77890792Sgshapiro register unsigned char *fmt; 77990792Sgshapiro{ 78090792Sgshapiro register int c, n, v; 78190792Sgshapiro 78290792Sgshapiro /* first `clear' the whole table */ 78390792Sgshapiro c = *fmt++; /* first char hat => negated scanset */ 78490792Sgshapiro if (c == '^') 78590792Sgshapiro { 78690792Sgshapiro v = 1; /* default => accept */ 78790792Sgshapiro c = *fmt++; /* get new first char */ 78890792Sgshapiro } 78990792Sgshapiro else 79090792Sgshapiro v = 0; /* default => reject */ 79190792Sgshapiro 79290792Sgshapiro /* should probably use memset here */ 79390792Sgshapiro for (n = 0; n < 256; n++) 79490792Sgshapiro tab[n] = v; 79590792Sgshapiro if (c == 0) 79690792Sgshapiro return fmt - 1; /* format ended before closing ] */ 79790792Sgshapiro 79890792Sgshapiro /* 79990792Sgshapiro ** Now set the entries corresponding to the actual scanset 80090792Sgshapiro ** to the opposite of the above. 80190792Sgshapiro ** 80290792Sgshapiro ** The first character may be ']' (or '-') without being special; 80390792Sgshapiro ** the last character may be '-'. 80490792Sgshapiro */ 80590792Sgshapiro 80690792Sgshapiro v = 1 - v; 80790792Sgshapiro for (;;) 80890792Sgshapiro { 80990792Sgshapiro tab[c] = v; /* take character c */ 81090792Sgshapirodoswitch: 81190792Sgshapiro n = *fmt++; /* and examine the next */ 81290792Sgshapiro switch (n) 81390792Sgshapiro { 81490792Sgshapiro 81590792Sgshapiro case 0: /* format ended too soon */ 81690792Sgshapiro return fmt - 1; 81790792Sgshapiro 81890792Sgshapiro case '-': 81990792Sgshapiro /* 82090792Sgshapiro ** A scanset of the form 82190792Sgshapiro ** [01+-] 82290792Sgshapiro ** is defined as `the digit 0, the digit 1, 82390792Sgshapiro ** the character +, the character -', but 82490792Sgshapiro ** the effect of a scanset such as 82590792Sgshapiro ** [a-zA-Z0-9] 82690792Sgshapiro ** is implementation defined. The V7 Unix 82790792Sgshapiro ** scanf treats `a-z' as `the letters a through 82890792Sgshapiro ** z', but treats `a-a' as `the letter a, the 82990792Sgshapiro ** character -, and the letter a'. 83090792Sgshapiro ** 83190792Sgshapiro ** For compatibility, the `-' is not considerd 83290792Sgshapiro ** to define a range if the character following 83390792Sgshapiro ** it is either a close bracket (required by ANSI) 83490792Sgshapiro ** or is not numerically greater than the character 83590792Sgshapiro ** we just stored in the table (c). 83690792Sgshapiro */ 83790792Sgshapiro 83890792Sgshapiro n = *fmt; 83990792Sgshapiro if (n == ']' || n < c) 84090792Sgshapiro { 84190792Sgshapiro c = '-'; 84290792Sgshapiro break; /* resume the for(;;) */ 84390792Sgshapiro } 84490792Sgshapiro fmt++; 84590792Sgshapiro do 84690792Sgshapiro { 84790792Sgshapiro /* fill in the range */ 84890792Sgshapiro tab[++c] = v; 84990792Sgshapiro } while (c < n); 85090792Sgshapiro#if 1 /* XXX another disgusting compatibility hack */ 85190792Sgshapiro 85290792Sgshapiro /* 85390792Sgshapiro ** Alas, the V7 Unix scanf also treats formats 85490792Sgshapiro ** such as [a-c-e] as `the letters a through e'. 85590792Sgshapiro ** This too is permitted by the standard.... 85690792Sgshapiro */ 85790792Sgshapiro 85890792Sgshapiro goto doswitch; 85990792Sgshapiro#else 86090792Sgshapiro c = *fmt++; 86190792Sgshapiro if (c == 0) 86290792Sgshapiro return fmt - 1; 86390792Sgshapiro if (c == ']') 86490792Sgshapiro return fmt; 86590792Sgshapiro break; 86690792Sgshapiro#endif 86790792Sgshapiro 86890792Sgshapiro case ']': /* end of scanset */ 86990792Sgshapiro return fmt; 87090792Sgshapiro 87190792Sgshapiro default: /* just another character */ 87290792Sgshapiro c = n; 87390792Sgshapiro break; 87490792Sgshapiro } 87590792Sgshapiro } 87690792Sgshapiro /* NOTREACHED */ 87790792Sgshapiro} 878