450 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			450 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
| /*
 | |
|  * doscan.c - scan formatted input
 | |
|  */
 | |
| /* $Header$ */
 | |
| 
 | |
| #include	<stdio.h>
 | |
| #include	<stdlib.h>
 | |
| #include	<ctype.h>
 | |
| #include	<stdarg.h>
 | |
| #include	"loc_incl.h"
 | |
| 
 | |
| #if	_EM_WSIZE == _EM_PSIZE
 | |
| #define set_pointer(flags)				/* nothing */
 | |
| #elif	_EM_LSIZE == _EM_PSIZE
 | |
| #define set_pointer(flags)	(flags |= FL_LONG)
 | |
| #else
 | |
| #error garbage pointer size
 | |
| #define set_pointer(flags)		/* compilation might continue */
 | |
| #endif
 | |
| 
 | |
| #define	NUMLEN	512
 | |
| #define	NR_CHARS	256
 | |
| 
 | |
| static char	Xtable[NR_CHARS];
 | |
| static char	inp_buf[NUMLEN];
 | |
| 
 | |
| /* Collect a number of characters which constitite an ordinal number.
 | |
|  * When the type is 'i', the base can be 8, 10, or 16, depending on the
 | |
|  * first 1 or 2 characters. This means that the base must be adjusted
 | |
|  * according to the format of the number. At the end of the function, base
 | |
|  * is then set to 0, so strtol() will get the right argument.
 | |
|  */
 | |
| static char *
 | |
| o_collect(register int c, register FILE *stream, char type,
 | |
| 			unsigned int width, int *basep)
 | |
| {
 | |
| 	register char *bufp = inp_buf;
 | |
| 	register int base;
 | |
| 
 | |
| 	switch (type) {
 | |
| 	case 'i':	/* i means octal, decimal or hexadecimal */
 | |
| 	case 'p':
 | |
| 	case 'x':
 | |
| 	case 'X':	base = 16;	break;
 | |
| 	case 'd':
 | |
| 	case 'u':	base = 10;	break;
 | |
| 	case 'o':	base = 8;	break;
 | |
| 	case 'b':	base = 2;	break;
 | |
| 	}
 | |
| 
 | |
| 	if (c == '-' || c == '+') {
 | |
| 		*bufp++ = c;
 | |
| 		if (--width)
 | |
| 		    c = getc(stream);
 | |
| 	}
 | |
| 
 | |
| 	if (width && c == '0' && base == 16) {
 | |
| 		*bufp++ = c;
 | |
| 		if (--width)
 | |
| 			c = getc(stream);
 | |
| 		if (c != 'x' && c != 'X') {
 | |
| 			if (type == 'i') base = 8;
 | |
| 		}
 | |
| 		else if (width) {
 | |
| 			*bufp++ = c;
 | |
| 			if (--width)
 | |
| 				c = getc(stream);
 | |
| 		}
 | |
| 	}
 | |
| 	else if (type == 'i') base = 10;
 | |
| 
 | |
| 	while (width) {
 | |
| 		if (((base == 10) && isdigit(c))
 | |
| 		    || ((base == 16) && isxdigit(c))
 | |
| 		    || ((base == 8) && isdigit(c) && (c < '8'))
 | |
| 		    || ((base == 2) && isdigit(c) && (c < '2'))) {
 | |
| 			*bufp++ = c;
 | |
| 			if (--width)
 | |
| 				c = getc(stream);
 | |
| 		}
 | |
| 		else break;
 | |
| 	}
 | |
| 
 | |
| 	if (width && c != EOF) ungetc(c, stream);
 | |
| 	if (type == 'i') base = 0;
 | |
| 	*basep = base;
 | |
| 	*bufp = '\0';
 | |
| 	return bufp - 1;
 | |
| }
 | |
| 
 | |
| #ifndef	NOFLOAT
 | |
| /* The function f_collect() reads a string that has the format of a
 | |
|  * floating-point number. The function returns as soon as a format-error
 | |
|  * is encountered, leaving the offending character in the input. This means
 | |
|  * that 1.el leaves the 'l' in the input queue. Since all detection of
 | |
|  * format errors is done here, _doscan() doesn't call strtod() when it's
 | |
|  * not necessary, although the use of the width field can cause incomplete
 | |
|  * numbers to be passed to strtod(). (e.g. 1.3e+)
 | |
|  */
 | |
| static char *
 | |
| f_collect(register int c, register FILE *stream, register unsigned int width)
 | |
| {
 | |
| 	register char *bufp = inp_buf;
 | |
| 	int digit_seen = 0;
 | |
| 
 | |
| 	if (c == '-' || c == '+') {
 | |
| 		*bufp++ = c;
 | |
| 		if (--width)
 | |
| 			c = getc(stream);
 | |
| 	}
 | |
| 
 | |
| 	while (width && isdigit(c)) {
 | |
| 		digit_seen++;
 | |
| 		*bufp++ = c;
 | |
| 		if (--width)
 | |
| 			c = getc(stream);
 | |
| 	}
 | |
| 	if (width && c == '.') {
 | |
| 		*bufp++ = c;
 | |
| 		if(--width)
 | |
| 			c = getc(stream);
 | |
| 		while (width && isdigit(c)) {
 | |
| 			digit_seen++;
 | |
| 			*bufp++ = c;
 | |
| 			if (--width)
 | |
| 				c = getc(stream);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (!digit_seen) {
 | |
| 		if (width && c != EOF) ungetc(c, stream);
 | |
| 		return inp_buf - 1;
 | |
| 	}
 | |
| 	else digit_seen = 0;
 | |
| 
 | |
| 	if (width && (c == 'e' || c == 'E')) {
 | |
| 		*bufp++ = c;
 | |
| 		if (--width)
 | |
| 			c = getc(stream);
 | |
| 		if (width && (c == '+' || c == '-')) {
 | |
| 			*bufp++ = c;
 | |
| 			if (--width)
 | |
| 				c = getc(stream);
 | |
| 		}
 | |
| 		while (width && isdigit(c)) {
 | |
| 			digit_seen++;
 | |
| 			*bufp++ = c;
 | |
| 			if (--width)
 | |
| 				c = getc(stream);
 | |
| 		}
 | |
| 		if (!digit_seen) {
 | |
| 			if (width && c != EOF) ungetc(c,stream);
 | |
| 			return inp_buf - 1;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (width && c != EOF) ungetc(c, stream);
 | |
| 	*bufp = '\0';
 | |
| 	return bufp - 1;
 | |
| }
 | |
| #endif	/* NOFLOAT */
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * the routine that does the scanning 
 | |
|  */
 | |
| 
 | |
| int
 | |
| _doscan(register FILE *stream, const char *format, va_list ap)
 | |
| {
 | |
| 	int		done = 0;	/* number of items done */
 | |
| 	int		nrchars = 0;	/* number of characters read */
 | |
| 	int		conv = 0;	/* # of conversions */
 | |
| 	int		base;		/* conversion base */
 | |
| 	unsigned long	val;		/* an integer value */
 | |
| 	register char	*str;		/* temporary pointer */
 | |
| 	char		*tmp_string;	/* ditto */
 | |
| 	unsigned	width = 0;	/* width of field */
 | |
| 	int		flags;		/* some flags */
 | |
| 	int		reverse;	/* reverse the checking in [...] */
 | |
| 	int		kind;
 | |
| 	register int	ic = EOF;	/* the input character */
 | |
| #ifndef	NOFLOAT
 | |
| 	long double	ld_val;
 | |
| #endif
 | |
| 
 | |
| 	if (!*format) return 0;
 | |
| 
 | |
| 	while (1) {
 | |
| 		if (isspace(*format)) {
 | |
| 			while (isspace(*format))
 | |
| 				format++;	/* skip whitespace */
 | |
| 			ic = getc(stream);
 | |
| 			nrchars++;
 | |
| 			while (isspace (ic)) {
 | |
| 				ic = getc(stream);
 | |
| 				nrchars++;
 | |
| 			}
 | |
| 			if (ic != EOF) ungetc(ic,stream);
 | |
| 			nrchars--;
 | |
| 		}
 | |
| 		if (!*format) break;	/* end of format */
 | |
| 
 | |
| 		if (*format != '%') {
 | |
| 			ic = getc(stream);
 | |
| 			nrchars++;
 | |
| 			if (ic != *format++) break;	/* error */
 | |
| 			continue;
 | |
| 		}
 | |
| 		format++;
 | |
| 		if (*format == '%') {
 | |
| 			ic = getc(stream);
 | |
| 			nrchars++;
 | |
| 			if (ic == '%') {
 | |
| 				format++;
 | |
| 				continue;
 | |
| 			}
 | |
| 			else break;
 | |
| 		}
 | |
| 		flags = 0;
 | |
| 		if (*format == '*') {
 | |
| 			format++;
 | |
| 			flags |= FL_NOASSIGN;
 | |
| 		}
 | |
| 		if (isdigit (*format)) {
 | |
| 			flags |= FL_WIDTHSPEC;
 | |
| 			for (width = 0; isdigit (*format);)
 | |
| 				width = width * 10 + *format++ - '0';
 | |
| 		}
 | |
| 
 | |
| 		switch (*format) {
 | |
| 		case 'h': flags |= FL_SHORT; format++; break;
 | |
| 		case 'l': flags |= FL_LONG; format++; break;
 | |
| 		case 'L': flags |= FL_LONGDOUBLE; format++; break;
 | |
| 		}
 | |
| 		kind = *format;
 | |
| 		if ((kind != 'c') && (kind != '[') && (kind != 'n')) {
 | |
| 			do {
 | |
| 				ic = getc(stream);
 | |
| 				nrchars++;
 | |
| 			} while (isspace(ic));
 | |
| 			if (ic == EOF) break;		/* outer while */
 | |
| 		} else if (kind != 'n') {		/* %c or %[ */
 | |
| 			ic = getc(stream);
 | |
| 			if (ic == EOF) break;		/* outer while */
 | |
| 			nrchars++;
 | |
| 		}
 | |
| 		switch (kind) {
 | |
| 		default:
 | |
| 			/* not recognized, like %q */
 | |
| 			return conv || (ic != EOF) ? done : EOF;
 | |
| 			break;
 | |
| 		case 'n':
 | |
| 			if (!(flags & FL_NOASSIGN)) {	/* silly, though */
 | |
| 				if (flags & FL_SHORT)
 | |
| 					*va_arg(ap, short *) = (short) nrchars;
 | |
| 				else if (flags & FL_LONG)
 | |
| 					*va_arg(ap, long *) = (long) nrchars;
 | |
| 				else
 | |
| 					*va_arg(ap, int *) = (int) nrchars;
 | |
| 			}
 | |
| 			break;
 | |
| 		case 'p':		/* pointer */
 | |
| 			set_pointer(flags);
 | |
| 			/* fallthrough */
 | |
| 		case 'b':		/* binary */
 | |
| 		case 'd':		/* decimal */
 | |
| 		case 'i':		/* general integer */
 | |
| 		case 'o':		/* octal */
 | |
| 		case 'u':		/* unsigned */
 | |
| 		case 'x':		/* hexadecimal */
 | |
| 		case 'X':		/* ditto */
 | |
| 			if (!(flags & FL_WIDTHSPEC) || width > NUMLEN)
 | |
| 				width = NUMLEN;
 | |
| 			if (!width) return done;
 | |
| 
 | |
| 			str = o_collect(ic, stream, kind, width, &base);
 | |
| 			if (str < inp_buf
 | |
| 			    || (str == inp_buf
 | |
| 				    && (*str == '-'
 | |
| 					|| *str == '+'))) return done;
 | |
| 
 | |
| 			/*
 | |
| 			 * Although the length of the number is str-inp_buf+1
 | |
| 			 * we don't add the 1 since we counted it already
 | |
| 			 */
 | |
| 			nrchars += str - inp_buf;
 | |
| 
 | |
| 			if (!(flags & FL_NOASSIGN)) {
 | |
| 				if (kind == 'd' || kind == 'i')
 | |
| 				    val = strtol(inp_buf, &tmp_string, base);
 | |
| 				else
 | |
| 				    val = strtoul(inp_buf, &tmp_string, base);
 | |
| 				if (flags & FL_LONG)
 | |
| 					*va_arg(ap, unsigned long *) = (unsigned long) val;
 | |
| 				else if (flags & FL_SHORT)
 | |
| 					*va_arg(ap, unsigned short *) = (unsigned short) val;
 | |
| 				else
 | |
| 					*va_arg(ap, unsigned *) = (unsigned) val;
 | |
| 			}
 | |
| 			break;
 | |
| 		case 'c':
 | |
| 			if (!(flags & FL_WIDTHSPEC))
 | |
| 				width = 1;
 | |
| 			if (!(flags & FL_NOASSIGN))
 | |
| 				str = va_arg(ap, char *);
 | |
| 			if (!width) return done;
 | |
| 
 | |
| 			while (width && ic != EOF) {
 | |
| 				if (!(flags & FL_NOASSIGN))
 | |
| 					*str++ = (char) ic;
 | |
| 				if (--width) {
 | |
| 					ic = getc(stream);
 | |
| 					nrchars++;
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			if (width) {
 | |
| 				if (ic != EOF) ungetc(ic,stream);
 | |
| 				nrchars--;
 | |
| 			}
 | |
| 			break;
 | |
| 		case 's':
 | |
| 			if (!(flags & FL_WIDTHSPEC))
 | |
| 				width = 0xffff;
 | |
| 			if (!(flags & FL_NOASSIGN))
 | |
| 				str = va_arg(ap, char *);
 | |
| 			if (!width) return done;
 | |
| 
 | |
| 			while (width && ic != EOF && !isspace(ic)) {
 | |
| 				if (!(flags & FL_NOASSIGN))
 | |
| 					*str++ = (char) ic;
 | |
| 				if (--width) {
 | |
| 					ic = getc(stream);
 | |
| 					nrchars++;
 | |
| 				}
 | |
| 			}
 | |
| 			/* terminate the string */
 | |
| 			if (!(flags & FL_NOASSIGN))
 | |
| 				*str = '\0';	
 | |
| 			if (width) {
 | |
| 				if (ic != EOF) ungetc(ic,stream);
 | |
| 				nrchars--;
 | |
| 			}
 | |
| 			break;
 | |
| 		case '[':
 | |
| 			if (!(flags & FL_WIDTHSPEC))
 | |
| 				width = 0xffff;
 | |
| 			if (!width) return done;
 | |
| 
 | |
| 			if ( *++format == '^' ) {
 | |
| 				reverse = 1;
 | |
| 				format++;
 | |
| 			} else
 | |
| 				reverse = 0;
 | |
| 
 | |
| 			for (str = Xtable; str < &Xtable[NR_CHARS]
 | |
| 							; str++)
 | |
| 				*str = 0;
 | |
| 
 | |
| 			if (*format == ']') Xtable[*format++] = 1;
 | |
| 
 | |
| 			while (*format && *format != ']') {
 | |
| 				Xtable[*format++] = 1;
 | |
| 				if (*format == '-') {
 | |
| 					format++;
 | |
| 					if (*format
 | |
| 					    && *format != ']'
 | |
| 					    && *(format) >= *(format -2)) {
 | |
| 						int c;
 | |
| 
 | |
| 						for( c = *(format -2) + 1
 | |
| 						    ; c <= *format ; c++)
 | |
| 							Xtable[c] = 1;
 | |
| 						format++;
 | |
| 					}
 | |
| 					else Xtable['-'] = 1;
 | |
| 				}
 | |
| 			}
 | |
| 			if (!*format) return done;
 | |
| 			
 | |
| 			if (!(Xtable[ic] ^ reverse)) {
 | |
| 			/* MAT 8/9/96 no match must return character */
 | |
| 				ungetc(ic, stream);
 | |
| 				return done;
 | |
| 			}
 | |
| 
 | |
| 			if (!(flags & FL_NOASSIGN))
 | |
| 				str = va_arg(ap, char *);
 | |
| 
 | |
| 			do {
 | |
| 				if (!(flags & FL_NOASSIGN))
 | |
| 					*str++ = (char) ic;
 | |
| 				if (--width) {
 | |
| 					ic = getc(stream);
 | |
| 					nrchars++;
 | |
| 				}
 | |
| 			} while (width && ic != EOF && (Xtable[ic] ^ reverse));
 | |
| 
 | |
| 			if (width) {
 | |
| 				if (ic != EOF) ungetc(ic, stream);
 | |
| 				nrchars--;
 | |
| 			}
 | |
| 			if (!(flags & FL_NOASSIGN)) {	/* terminate string */
 | |
| 				*str = '\0';	
 | |
| 			}
 | |
| 			break;
 | |
| #ifndef	NOFLOAT
 | |
| 		case 'e':
 | |
| 		case 'E':
 | |
| 		case 'f':
 | |
| 		case 'g':
 | |
| 		case 'G':
 | |
| 			if (!(flags & FL_WIDTHSPEC) || width > NUMLEN)
 | |
| 				width = NUMLEN;
 | |
| 
 | |
| 			if (!width) return done;
 | |
| 			str = f_collect(ic, stream, width);
 | |
| 
 | |
| 			if (str < inp_buf
 | |
| 			    || (str == inp_buf
 | |
| 				&& (*str == '-'
 | |
| 				    || *str == '+'))) return done;
 | |
| 
 | |
| 			/*
 | |
| 			 * Although the length of the number is str-inp_buf+1
 | |
| 			 * we don't add the 1 since we counted it already
 | |
| 			 */
 | |
| 			nrchars += str - inp_buf;
 | |
| 
 | |
| 			if (!(flags & FL_NOASSIGN)) {
 | |
| 				ld_val = strtod(inp_buf, &tmp_string);
 | |
| 				if (flags & FL_LONGDOUBLE)
 | |
| 					*va_arg(ap, long double *) = (long double) ld_val;
 | |
| 				else
 | |
| 				    if (flags & FL_LONG)
 | |
| 					*va_arg(ap, double *) = (double) ld_val;
 | |
| 				else
 | |
| 					*va_arg(ap, float *) = (float) ld_val;
 | |
| 			}
 | |
| 			break;
 | |
| #endif
 | |
| 		}		/* end switch */
 | |
| 		conv++;
 | |
| 		if (!(flags & FL_NOASSIGN) && kind != 'n') done++;
 | |
| 		format++;
 | |
| 	}
 | |
| 	return conv || (ic != EOF) ? done : EOF;
 | |
| }
 | 
