Minix1.5/lib/ansi/scanf.c

Compare this file to the similar file:
Show the results in this format:

#include <lib.h>
#include <stdarg.h>
/* scanf - formatted input conversion	Author: Patrick van Kleef */

#include <stdio.h>
#include <ctype.h>

extern int _doscanf();

int scanf(format, args)
char *format;
unsigned args;
{
  return(_doscanf(0, (char *) stdin, format, &args));
}


int fscanf(fp, format, args)
FILE *fp;
char *format;
unsigned args;
{
  return(_doscanf(0, (char *) fp, format, &args));
}


int sscanf(string, format, args)
char *string;			/* source of data */
char *format;			/* control string */
unsigned args;			/* our args */
{
  return(_doscanf(1, string, format, &args));
}


union ptr_union {
  char *chr_p;
  unsigned int *uint_p;
  unsigned long *ulong_p;
};

PRIVATE int ic;			/* the current character */
PRIVATE char *rnc_arg;		/* the string or the filepointer */
PRIVATE rnc_code;		/* 1 = read from string, else from FILE */

PRIVATE _PROTOTYPE( void rnc, (void));
PRIVATE _PROTOTYPE( int scnindex, (int ch, char *string));
PRIVATE _PROTOTYPE( void ugc, (void));

/* Get the next character */

PRIVATE void rnc()
{
  if (rnc_code) {
	if (!(ic = *rnc_arg++)) ic = EOF;
  } else
	ic = getc((FILE *) rnc_arg);
}


/* unget the current character
 */

PRIVATE void ugc()
{

  if (rnc_code)
	--rnc_arg;
  else
	ungetc(ic, (FILE *) rnc_arg);
}


PRIVATE int scnindex(ch, string)
int ch;
char *string;
{
  while (*string++ != (char) ch)
	if (!*string) return(0);
  return(1);
}


/* the routine that does the job
 */

int _doscanf(code, funcarg, format, argpfix)
int code;			/* function to get a character */
char *funcarg;			/* an argument for the function */
char *format;			/* the format control string */
va_list argpfix;		/* our argument list */
{
  int done = 0;			/* number of items done */
  int base;			/* conversion base */
  long val;			/* an integer value */
  int sign;			/* sign flag */
  int do_assign;		/* assignment suppression flag */
  unsigned width;		/* width of field */
  int widflag;			/* width was specified */
  int longflag;			/* true if long */
  int done_some;		/* true if we have seen some data */
  int reverse;			/* reverse the checking in [...] */
  char *endbracket;		/* position of the ] in format string */
  /* convert good form of argp to bad form for further (old) bad conversions */
  union ptr_union *argp = (union ptr_union *) argpfix;

  rnc_arg = funcarg;
  rnc_code = code;

  rnc();			/* read the next character */

  if (ic == EOF) {
	done = EOF;
	goto quit;
  }
  while (1) {
	while (isspace(*format)) ++format;	/* skip whitespace */
	if (!*format) goto all_done;	/* end of format */
	if (ic < 0) goto quit;	/* seen an error */
	if (*format != '%') {
		while (isspace(ic)) rnc();
		if (ic != *format) goto all_done;
		++format;
		rnc();
		continue;
	}
	++format;
	do_assign = 1;
	if (*format == '*') {
		++format;
		do_assign = 0;
	}
	if (isdigit(*format)) {
		widflag = 1;
		for (width = 0; isdigit(*format);)
			width = width * 10 + *format++ - '0';
	} else {
		widflag = 0;	/* no width spec */
		width = (unsigned) UINT_MAX;	/* very wide */
	}
	if (longflag = (tolower(*format) == 'l')) ++format;
	if (*format != 'c') while (isspace(ic))
			rnc();
	done_some = 0;		/* nothing yet */
	switch (*format) {
	    case 'o':
		base = 8;
		goto decimal;
	    case 'u':
	    case 'd':
		base = 10;
		goto decimal;
	    case 'x':
		base = 16;
		if (width >= 2 && ic == '0') {
			rnc();
			if (tolower(ic) == 'x') {
				width -= 2;
				done_some = 1;
				rnc();
			} else {
				ugc();
				ic = '0';
			}
		}
  decimal:
		val = 0L;	/* our result value */
		sign = 0;	/* assume positive */
		if (width && ic == '+')
			rnc();
		else if (width && ic == '-') {
			sign = 1;
			rnc();
		}
		while (width--) {
			if (isdigit(ic) && ic - '0' < base)
				ic -= '0';
			else if (base == 16 && tolower(ic) >= 'a' && tolower(ic) <= 'f')
				ic = 10 + tolower(ic) - 'a';
			else
				break;
			val = val * base + ic;
			rnc();
			done_some = 1;
		}
		if (do_assign) {
			if (sign) val = -val;
			if (longflag)
				*(argp++)->ulong_p = (unsigned long) val;
			else
				*(argp++)->uint_p = (unsigned) val;
		}
		if (done_some) {
			if (do_assign) ++done;
		} else
			goto all_done;
		break;
	    case 'c':
		if (!widflag) width = 1;
		while (width-- && ic >= 0) {
			if (do_assign) *(argp)->chr_p++ = (char) ic;
			rnc();
			done_some = 1;
		}
		if (do_assign) argp++;	/* done with this one */
		if (done_some && do_assign) ++done;
		break;
	    case 's':
		while (width-- && !isspace(ic) && ic > 0) {
			if (do_assign) *(argp)->chr_p++ = (char) ic;
			rnc();
			done_some = 1;
		}
		if (do_assign)	/* terminate the string */
			*(argp++)->chr_p = '\0';
		if (done_some) {
			if (do_assign) ++done;
		} else
			goto all_done;
		break;
	    case '[':
		if (*(++format) == '^') {
			reverse = 1;
			format++;
		} else
			reverse = 0;

		endbracket = format;
		while (*endbracket != ']' && *endbracket != '\0')
			endbracket++;

		if (!*endbracket) goto quit;

		*endbracket = '\0';	/* change format string */

		while (width-- && !isspace(ic) && ic > 0 &&
		       (scnindex(ic, format) ^ reverse)) {
			if (do_assign) *(argp)->chr_p++ = (char) ic;
			rnc();
			done_some = 1;
		}
		format = endbracket;
		*format = ']';	/* put it back */
		if (do_assign)	/* terminate the string */
			*(argp++)->chr_p = '\0';
		if (done_some) {
			if (do_assign) ++done;
		} else
			goto all_done;
		break;
	}			/* end switch */
	++format;
  }
all_done:
  if (ic >= 0) ugc();		/* restore the character */
quit:
  return(done);
}