/* Copyright 1982, 1983 Alcyon Corporation 8716 Production Ave. San Diego, Ca. 92121 @(#)fscanf.c 1.1 11/9/83 */ /* ** formatted read routine ** ** functionally equivalent to scanf in portable C library */ #include #include /* Delimiters */ #define NEWLINE '\n' #define TAB '\t' #define SPACE ' ' #define NULL '\0' /* returns from __next() */ #define CHAR 0 #define NOT_WHT -1 #define NOT_WHT_NL 1 /* returns from __scan() */ #define NORETURN 0 #define VALID 1 #define NOMATCH -1 #define AT_EOF -2 #define ERROR -3 FILE *__stream; char **_p, *__sstr, __holdch; int __smode, __hold; scanf(parlist) char *parlist; { __smode = 0; _p = &parlist; return(__doscanf()); } fscanf(stream,parlist) FILE *stream; char *parlist; { if( (stream->_flag&(_RMODE|_UPDATE)) == 0 || feof(stream) ) return(EOF); __smode = 1; __stream = stream; _p = &parlist; return(__doscanf()); } sscanf(s,parlist) char *s, *parlist; { __smode = 2; __sstr = s; _p = &parlist; return(__doscanf()); } __doscanf() { register int nmatch; register char ch; char *format; register char match_ch; nmatch = __hold = 0; format = *_p++; while( 1 ) { switch (ch = *format++) { case NULL: return(nmatch); case '%': if( *format != '%' ) { switch (__scan(&format, *_p)) { case VALID: /* good return*/ _p++; nmatch++; case NORETURN: /* no return*/ break; case NOMATCH: /* no match */ return(nmatch); case AT_EOF: /* end of file */ return(nmatch ? nmatch : NOMATCH); default: /* syntax error */ return(NOMATCH); } break; } format++; default: match_ch = __next(CHAR); if( ch != match_ch ) { __unget(match_ch); return(nmatch ? nmatch : AT_EOF); } break; } } } /* * main scan routine -- look at characters in the conversion string * and do their bidding */ __scan(spec, result) char **spec; char *result; { register int longf, length; register char ch; extern int __strend(), __splend(); longf = length = 0; while( 1 ) { switch (ch = *(*spec)++) { case '*': result = 0; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': length = length * 10 + ch - '0'; break; case 'l': if( longf ) return(ERROR); longf = 1; break; case 'h': /* short */ if( longf ) return(ERROR); longf = NOMATCH; break; case 'o': case 'O': return(__dec(result, length ? length : 100, 8, longf)); case 'd': case 'D': return(__dec(result, length ? length : 100, 10, longf)); case 'x': case 'X': return(__dec(result, length ? length : 100, 16, longf)); case 'c': case 'C': if( longf ) return(ERROR); return(__char(result, length ? length : 1)); case 's': case 'S': if( longf ) return(ERROR); return(__strx(result, length ? length : 100, __strend)); case 'e': case 'E': case 'f': case 'F': if( longf ) return(ERROR); return(__float(result, length ? length : 100)); /*return(ERROR); /* not yet implemented */ case '[': if( longf ) return(ERROR); if( __inits(spec) ) return(ERROR); return(__strx(result, length ? length : 100, __splend)); default: return(ERROR); } } } /* * get a constant -- octal, decimal, or hex depending on base */ __dec(result, length, base, longf) register int *result; int length; int base; int longf; { register char ch; register int val; register int ndigit; register long *lresult; register long lres; register int ires; register int minus, ok; ires = 0; lres = 0; ndigit = minus = 0; switch (ch = __next(NOT_WHT_NL)) { case NULL: case EOF: return(AT_EOF); case '-': minus = 1; case '+': ndigit++; ch = __next(NOT_WHT); } ok = 0; while( (val = __digit(ch, base)) >= 0 && ndigit++ < length ) { ok++; if( longf ) lres = lres * base + val; else ires = ires * base + val; ch = __next(CHAR); } __unget(ch); if( !ok ) return(NOMATCH); if( !result ) return(NORETURN); if( minus ) if( longf ) lres = -lres; else ires = -ires; if( longf ) { lresult = result; *lresult = lres; } else *result = ires; return(VALID); } /* * get a floating point constant */ __float(result, length) register double *result; int length; { char buffer[100]; double val; int ret, ch; ret = __strx(buffer, 100, __strend); val = atof(buffer); *result = val; return(ret); } __next(mode) int mode; { /* * mode -1: get next non-space or non-tab * mode 0: get next character * mode 1: get next non-space, non-tab, or non-newline */ register int ch; if( (ch = __getch()) == EOF ) return(EOF); if( mode == 0 ) return(ch); while( ch == SPACE || ch == TAB || ch == NEWLINE ) { if( ch == NEWLINE && mode < 0 ) break; ch = __getch(); } return(ch); } /* * check an input character for a valid constant digit (octal, decimal, * or hex) if found, return the proper numeric value. Negative results * indicate error. */ __digit(ch, base) register char ch; register int base; { register int n; if( ch < '0' ) return(NOMATCH); if( ch <= '7' ) return(ch - '0'); if( base == 8 ) return(NOMATCH); if( ch <= '9' ) return(ch - '0'); if( base == 10 || ch < 'A' ) return(NOMATCH); if( ch <= 'F' ) return(ch - 'A' + 10); if( ch < 'a' || ch > 'f' ) return(NOMATCH); return(ch - 'a' + 10); } /* * check for an end of string delimiter */ __strend(cha) char cha; { register char ch; if( (ch = cha) == EOF ) return(EOF); if( ch == SPACE || ch == TAB || ch == NEWLINE || ch == NULL ) return(VALID); return(NORETURN); } char __splset[128]; /* * check for the occurrance of any character in the set which * the user wants to be end-of-string delimiters */ __splend(ch) char ch; { if( ch == EOF ) return(EOF); return(__splset[ch]); } /* * initialize the array which inidcates the special chars which the user * wants to be included (or not included) in strings. */ __inits(spec) register char **spec; { register char ch; register int i; register int val; ch = *(*spec)++; if( ch == '^' ) { val = 0; ch = *(*spec)++; } else val = 1; for (i = 1; i < 128; i++) __splset[i] = val; val = 1 - val; while( ch != ']' ) { if( ch == 0 ) return(NOMATCH); __splset[ch & 0177] = val; ch = *(*spec)++; } __splset[0] = 1; return(NORETURN); } /* * getting a string */ __strx(result, length, endfn) register char *result; register int length; register int (*endfn)(); { register char ch; extern int __splend(); register int imode, notok; notok = 1; imode = (endfn != __splend); if (imode) { ch = __next(NOT_WHT_NL); __unget(ch); /* bypass tab or space... */ } while( !(*endfn)( (ch = __next(imode)) ) && length-- > 0 ) { if( result ) *result++ = ch; imode = notok = 0; } __unget(ch); if( notok ) return(ch == EOF ? AT_EOF : NOMATCH); if( !result ) return(NORETURN); *result = 0; return(VALID); } /* * getting a character constant */ __char(result, length) register char *result; register int length; { register char *r, ch; register int l; r = result; l = length; while( l-- ) { if( (ch = __next(CHAR)) <= 0 ) { if( l + 1 == length ) return(ch == EOF ? AT_EOF : NOMATCH); else return(result != 0); } if( result ) *result++ = ch; } return(result != 0); } __getch() { switch(__smode) { case 0: return(__gstdi()); case 1: return(getc(__stream)); case 2: return(__gs()); } } __unget(ch) char ch; { switch(__smode) { case 0: __ugstdi(ch); break; case 1: ungetc(ch,__stream); break; case 2: __ungs(ch); break; } } /* * return the next char pointed to by *s */ __gs() { register char c; c = *__sstr; if( c ) __sstr++; else return(EOF); return(c); } /* * put back a char for further scanning */ __ungs(c) char c; { if( c ) __sstr--; } __gstdi() { if( !__hold) return(getchar()); else { __hold = 0; return(__holdch); } } __ugstdi(ch) char ch; { __hold = 1; __holdch = ch; } = 1; __holdch = ch; } = 1; __holdch = ch; } = 1; __holdch = ch; }