Plan 9 from Bell Labs’s /sys/src/libstdio/vfscanf.c

Copyright © 2021 Plan 9 Foundation
Distributed under the MIT License.
Download the Plan 9 distribution.


/*
 * pANS stdio -- vfscanf
 */
#include "iolib.h"
#include <ctype.h>

static int icvt_f(FILE *f, va_list *args, int store, int width, int type);
static int icvt_x(FILE *f, va_list *args, int store, int width, int type);
static int icvt_sq(FILE *f, va_list *args, int store, int width, int type);
static int icvt_c(FILE *f, va_list *args, int store, int width, int type);
static int icvt_d(FILE *f, va_list *args, int store, int width, int type);
static int icvt_i(FILE *f, va_list *args, int store, int width, int type);
static int icvt_n(FILE *f, va_list *args, int store, int width, int type);
static int icvt_o(FILE *f, va_list *args, int store, int width, int type);
static int icvt_p(FILE *f, va_list *args, int store, int width, int type);
static int icvt_s(FILE *f, va_list *args, int store, int width, int type);
static int icvt_u(FILE *f, va_list *args, int store, int width, int type);
static int (*icvt[])(FILE *, va_list *, int, int, int)={
0,	0,	0,	0,	0,	0,	0,	0,	/* ^@ ^A ^B ^C ^D ^E ^F ^G */
0,	0,	0,	0,	0,	0,	0,	0,	/* ^H ^I ^J ^K ^L ^M ^N ^O */
0,	0,	0,	0,	0,	0,	0,	0,	/* ^P ^Q ^R ^S ^T ^U ^V ^W */
0,	0,	0,	0,	0,	0,	0,	0,	/* ^X ^Y ^Z ^[ ^\ ^] ^^ ^_ */
0,	0,	0,	0,	0,	0,	0,	0,	/* sp  !  "  #  $  %  &  ' */
0,	0,	0,	0,	0,	0,	0,	0,	/*  (  )  *  +  ,  -  .  / */
0,	0,	0,	0,	0,	0,	0,	0,	/*  0  1  2  3  4  5  6  7 */
0,	0,	0,	0,	0,	0,	0,	0,	/*  8  9  :  ;  <  =  >  ? */
0,	0,	0,	0,	0,	icvt_f,	0,	icvt_f,	/*  @  A  B  C  D  E  F  G */
0,	0,	0,	0,	0,	0,	0,	0,	/*  H  I  J  K  L  M  N  O */
0,	0,	0,	0,	0,	0,	0,	0,	/*  P  Q  R  S  T  U  V  W */
icvt_x,	0,	0,	icvt_sq,0,	0,	0,	0,	/*  X  Y  Z  [  \  ]  ^  _ */
0,	0,	0,	icvt_c,	icvt_d,	icvt_f,	icvt_f,	icvt_f,	/*  `  a  b  c  d  e  f  g */
0,	icvt_i,	0,	0,	0,	0,	icvt_n,	icvt_o,	/*  h  i  j  k  l  m  n  o */
icvt_p,	0,	0,	icvt_s,	0,	icvt_u,	0,	0,	/*  p  q  r  s  t  u  v  w */
icvt_x,	0,	0,	0,	0,	0,	0,	0,	/*  x  y  z  {  |  }  ~ ^? */

0,	0,	0,	0,	0,	0,	0,	0,
0,	0,	0,	0,	0,	0,	0,	0,
0,	0,	0,	0,	0,	0,	0,	0,
0,	0,	0,	0,	0,	0,	0,	0,
0,	0,	0,	0,	0,	0,	0,	0,
0,	0,	0,	0,	0,	0,	0,	0,
0,	0,	0,	0,	0,	0,	0,	0,
0,	0,	0,	0,	0,	0,	0,	0,
0,	0,	0,	0,	0,	0,	0,	0,
0,	0,	0,	0,	0,	0,	0,	0,
0,	0,	0,	0,	0,	0,	0,	0,
0,	0,	0,	0,	0,	0,	0,	0,
0,	0,	0,	0,	0,	0,	0,	0,
0,	0,	0,	0,	0,	0,	0,	0,
0,	0,	0,	0,	0,	0,	0,	0,
0,	0,	0,	0,	0,	0,	0,	0,

};

#define	ngetc(f)		(nread++, getc(f))
#define	nungetc(c, f)		(--nread, ungetc((c), f))
#define	wgetc(c, f, out)	if(width--==0) goto out; (c)=ngetc(f)
#define	wungetc(c, f)		(++width, nungetc(c, f))

static int nread, ncvt;
static const char *fmtp;

int
vfscanf(FILE *f, const char *s, va_list args)
{
	int c, width, type, store;

	nread = 0;
	ncvt = 0;
	fmtp = s;
	for (; *fmtp; fmtp++)
		switch (*fmtp) {
		default:
			if (isspace(*fmtp)) {
				do
					c = ngetc(f);
				while (isspace(c));
				if (c == EOF)
					return ncvt ? ncvt : EOF;
				nungetc(c, f);
				break;
			}
NonSpecial:
			c = ngetc(f);
			if (c == EOF)
				return ncvt ? ncvt : EOF;
			if (c != *fmtp) {
				nungetc(c, f);
				return ncvt;
			}
			break;
		case '%':
			fmtp++;
			if (*fmtp != '*')
				store = 1;
			else {
				store = 0;
				fmtp++;
			}
			if ('0' <= *fmtp && *fmtp <= '9') {
				width = 0;
				while ('0' <= *fmtp && *fmtp <= '9')
					width = width * 10 + *fmtp++ - '0';
			} else
				width = -1;
			type = *fmtp == 'h' || *fmtp == 'l' ||
				*fmtp == 'L' ? *fmtp++ : 'n';
			if (!icvt[*fmtp])
				goto NonSpecial;
			if (!(*icvt[*fmtp])(f, &args, store, width, type))
				return ncvt ? ncvt : EOF;
			if (*fmtp == '\0')
				break;
			if (store)
				ncvt++;
		}
	return ncvt;
}

static int
icvt_n(FILE *f, va_list *args, int store, int width, int type)
{
	USED(f, width);
	if (store) {
		--ncvt;			/* this assignment doesn't count! */
		switch (type) {
		case 'h':
			*va_arg(*args, short *) = nread;
			break;
		case 'n':
			*va_arg(*args, int *) = nread;
			break;
		case 'l':
		case 'L':
			*va_arg(*args, long *) = nread;
			break;
		}
	}
	return 1;
}

#define	SIGNED		1
#define	UNSIGNED	2
#define	POINTER		3

/*
 * Generic fixed-point conversion
 *	f is the input FILE *;
 *	args is the va_list * into which to store the number;
 *	store is a flag to enable storing;
 *	width is the maximum field width;
 *	type is 'h' 'l' or 'L', the scanf type modifier;
 *	unsgned is SIGNED, UNSIGNED or POINTER, giving part of the type to store in;
 *	base is the number base -- if 0, C number syntax is used.
 */
static int
icvt_fixed(FILE *f, va_list *args, int store, int width, int type,
	int unsgned, int base)
{
	unsigned long num = 0, numsign;
	int c, sign = 1, ndig = 0, dig;

	do
		c = ngetc(f);
	while (isspace(c));
	if (width-- == 0) {
		nungetc(c, f);
		goto Done;
	}
	if (c == '+') {
		wgetc(c, f, Done);
	} else if (c == '-') {
		sign = -1;
		wgetc(c, f, Done);
	}
	switch (base) {
	case 0:
		if (c == '0') {
			wgetc(c, f, Done);
			if (c == 'x' || c == 'X') {
				wgetc(c, f, Done);
				base = 16;
			} else {
				ndig = 1;
				base = 8;
			}
		} else
			base = 10;
		break;
	case 16:
		if (c == '0') {
			wgetc(c, f, Done);
			if (c == 'x' || c == 'X') {
				wgetc(c, f, Done);
			} else
				ndig = 1;
		}
		break;
	}
	while ('0' <= c && c <= '9' || 'a' <= c && c <= 'f' ||
	    'A' <= c && c <= 'F') {
		dig = '0' <= c && c <= '9' ? c - '0' : 'a' <= c &&
			c <= 'f' ? c - 'a' + 10 : c - 'A' + 10;
		if (dig >= base)
			break;
		ndig++;
		num = num*base + dig;
		wgetc(c, f, Done);
	}
	nungetc(c, f);
Done:
	if (ndig == 0)
		return 0;
	if (store) {
		numsign = num * sign;
		switch (unsgned) {
		case SIGNED:
			switch (type) {
			case 'h':
				*va_arg(*args,  short *) = numsign;
				break;
			case 'n':
				*va_arg(*args,  int *) = numsign;
				break;
			case 'l':
			case 'L':
				*va_arg(*args,  long *) = numsign;
				break;
			}
			break;
		case UNSIGNED:
			switch (type) {
			case 'h':
				*va_arg(*args, unsigned short *) = numsign;
				break;
			case 'n':
				*va_arg(*args, unsigned int *) = numsign;
				break;
			case 'l':
			case 'L':
				*va_arg(*args, unsigned long *) = numsign;
				break;
			}
			break;
		case POINTER:
			*va_arg(*args, void **) = (void *)numsign;
			break;
		}
	}
	return 1;
}

static int icvt_d(FILE *f, va_list *args, int store, int width, int type){
	return icvt_fixed(f, args, store, width, type, SIGNED, 10);
}
static int icvt_x(FILE *f, va_list *args, int store, int width, int type){
	return icvt_fixed(f, args, store, width, type, UNSIGNED, 16);
}
static int icvt_o(FILE *f, va_list *args, int store, int width, int type){
	return icvt_fixed(f, args, store, width, type, UNSIGNED, 8);
}
static int icvt_i(FILE *f, va_list *args, int store, int width, int type){
	return icvt_fixed(f, args, store, width, type, SIGNED, 0);
}
static int icvt_u(FILE *f, va_list *args, int store, int width, int type){
	return icvt_fixed(f, args, store, width, type, UNSIGNED, 10);
}
static int icvt_p(FILE *f, va_list *args, int store, int width, int type){
	return icvt_fixed(f, args, store, width, type, POINTER, 16);
}

#define	NBUF	509

static int
icvt_f(FILE *f, va_list *args, int store, int width, int type)
{
	char buf[NBUF+1];
	char *s = buf;
	int c, ndig = 0, ndpt = 0, nexp = 1;

	if (width < 0 || NBUF < width)
		width = NBUF;	/* bug -- no limit specified in ansi */
	do
		c = ngetc(f);
	while (isspace(c));
	if (width-- == 0) {
		nungetc(c, f);
		goto Done;
	}
	if (c == '+' || c == '-') {
		*s++ = c;
		wgetc(c, f, Done);
	}
	while ('0' <= c && c <= '9' || ndpt == 0 && c == '.') {
		if (c == '.')
			ndpt++;
		else
			ndig++;
		*s++ = c;
		wgetc(c, f, Done);
	}
	if (c == 'e' || c == 'E') {
		*s++ = c;
		nexp = 0;
		wgetc(c, f, Done);
		if (c == '+' || c == '-') {
			*s++ = c;
			wgetc(c, f, Done);
		}
		while ('0' <= c && c <= '9') {
			*s++ = c;
			nexp++;
			wgetc(c, f, Done);
		}
	}
	nungetc(c, f);
Done:
	if (ndig == 0 || nexp == 0)
		return 0;
	*s = '\0';
	if (store)
		switch (type) {
		case 'h':
		case 'n':
			*va_arg(*args, float *) = atof(buf);
			break;
		case 'L': /* bug -- should store in a long double */
		case 'l':
			*va_arg(*args, double *) = atof(buf);
			break;
		}
	return 1;
}

static int
icvt_s(FILE *f, va_list *args, int store, int width, int type)
{
	USED(type);
	int c, nn;
	char *s;

	s = nil;				/* silence used and not set */
	if (store)
		s = va_arg(*args, char * );
	do
		c = ngetc(f);
	while (isspace(c));
	if (width-- == 0) {
		nungetc(c, f);
		goto Done;
	}
	nn = 0;
	while (!isspace(c)) {
		if (c == EOF) {
			nread--;
			if (nn == 0)
				return 0;
			else
				goto Done;
		}
		nn++;
		if (store)
			*s++ = c;
		wgetc(c, f, Done);
	}
	nungetc(c, f);
Done:
	if (store)
		*s = '\0';
	return 1;
}

static int
icvt_c(FILE *f, va_list *args, int store, int width, int type)
{
	USED(type);
	int c;
	char *s;

	s = nil;				/* silence used and not set */
	if (store)
		s = va_arg(*args, char *);
	if (width < 0)
		width = 1;
	for (; ; ) {
		wgetc(c, f, Done);
		if (c == EOF)
			return 0;
		if (store)
			*s++ = c;
	}
Done:
	return 1;
}

static int
match(int c, const char *pat)
{
	int ok = 1;

	if (*pat == '^') {
		ok = !ok;
		pat++;
	}
	while (pat != fmtp) {
		if (pat + 2 < fmtp && pat[1] == '-') {
			if (pat[0] <= c && c <= pat[2] ||
			    pat[2] <= c && c <= pat[0])
				return ok;
			pat += 2;
		} else if (c == *pat)
			return ok;
		pat++;
	}
	return !ok;
}

static int
icvt_sq(FILE *f, va_list *args, int store, int width, int type)
{
	USED(type);
	int c, nn;
	char *s;
	const char *pat;

	pat = ++fmtp;
	if (*fmtp == '^')
		fmtp++;
	if (*fmtp != '\0')
		fmtp++;
	while (*fmtp != '\0' && *fmtp != ']')
		fmtp++;
	s = nil;				/* silence used and not set */
	if (store)
		s = va_arg(*args, char *);
	nn = 0;
	for (; ; ) {
		wgetc(c, f, Done);
		if (c == EOF) {
			nread--;
			if (nn == 0)
				return 0;
			else
				goto Done;
		}
		if (!match(c, pat))
			break;
		if (store)
			*s++ = c;
		nn++;
	}
	nungetc(c, f);
Done:
	if (store)
		*s = '\0';
	return 1;
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@plan9.bell-labs.com.