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