shithub: riscv

ref: 5c448c618a89ab8933838da1b5b97ed7c4a42e08
dir: /sys/src/ape/lib/ap/stdio/vfscanf.c/

View raw version
/*
 * pANS stdio -- vfscanf
 */
#include "iolib.h"
#include <stdarg.h>
#include <math.h>
#include <stdlib.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));
			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 = 'n';
		if(*fmtp=='h' || *fmtp=='l' || *fmtp=='L' || *fmtp=='j' || *fmtp=='z' || *fmtp=='t')
			type = *fmtp++;
		if(type == 'l' && *fmtp == 'l'){
			type = 'V';
			fmtp++;
		}else if(type == 'h' && *fmtp == 'h'){
			type = 'H';
			fmtp++;
		}
		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 *, va_list *args, int store, int, int type)
{
	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 long num=0;
	int sign=1, ndig=0, dig;
	int c;
	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){
		switch(unsgned){
		case SIGNED:
			switch(type){
			case 'H': *va_arg(*args, char *)=num*sign; break;
			case 'h': *va_arg(*args, short *)=num*sign; break;
			case 'n': *va_arg(*args, int *)=num*sign; break;
			case 'l':
			case 'L': *va_arg(*args, long *)=num*sign; break;
			case 'j':
			case 'V': *va_arg(*args, long long*)=num*sign; break;
			case 'z': *va_arg(*args, ssize_t*)=num*sign; break;
			case 't': *va_arg(*args, ptrdiff_t*)=num*sign; break;
			}
			break;
		case UNSIGNED:
			switch(type){
			case 'H': *va_arg(*args, unsigned char *)=num*sign; break;
			case 'h': *va_arg(*args, unsigned short *)=num*sign; break;
			case 'n': *va_arg(*args, unsigned int *)=num*sign; break;
			case 'l':
			case 'L': *va_arg(*args, unsigned long *)=num*sign; break;
			case 'j':
			case 'V': *va_arg(*args, unsigned long long *)=num*sign; break;
			case 'z': *va_arg(*args, size_t*)=num*sign; break;
			case 't': *va_arg(*args, ptrdiff_t*)=num*sign; break;
			}
			break;
		case POINTER:
			*va_arg(*args, void **)=(void *)(num*sign); 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)
{
	int c, nn;
	char *s;

	s = 0;
	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)
{
	int c;
	char *s;

	s = 0;
	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)
{
	int c, nn;
	char *s;
	char *pat;

	s = 0;
	pat=(char*)++fmtp;
	if(*fmtp=='^') fmtp++;
	if(*fmtp!='\0') fmtp++;
	while(*fmtp!='\0' && *fmtp!=']') fmtp++;
	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)){
			nungetc(c, f);
			goto Done;
		}
		if(store)
			*s++=c;
		nn++;
	}
Done:
	if(store) *s='\0';
	return nn > 0;
}