/* vim: set filetype=c:
 *
 * cscanf.c86  -  scanf() routine for target system
 *
 * Copyright (C) 2003-2007 Gero Kuhlmann   <gero@gkminix.han.de>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * $Id: cscanf.c86,v 1.7 2007/01/06 18:30:51 gkminix Exp $
 */


/* Include target system C headers */
#include <common.h86>
#include <i386/stdarg.h86>
#include <i386/string.h86>
#include <i386/ctype.h86>
#include <i386/conio.h86>



/*
 * Skip white space in input
 */
#define skip()	while (isspace(c)){ \
			if ((c = cgetc()) == '\n') goto done; \
		}



/*
 * cscanf() is like scanf() but it always reads from the keyboard
 */
size_t cscanf(const char * fmt, ...)
{
  long n;
  char c, c2;
  short width, base, wide1, neg, rngflag;
  bool_t lval, store, endnull;
  size_t cnt = 0;
  unsigned char *p, *q;
  static unsigned char delim[128], digits[17];
  va_list ap;

  va_start(ap, fmt);

  if ((c = cgetc()) == '\n' || c == CHR_BREAK)
	return(EOF);

  while (c != '\n' && c != CHR_BREAK) {
	store = 0;
	if (*fmt == '%') {
		n = 0;
		width = -1;
		wide1 = 1;
		base = 10;
		lval = TRUE;
		store = TRUE;
		endnull = TRUE;
		neg = -1;

		_strcpy((char *)delim, "\011\012\013\014\015 ");
		_strcpy((char *)digits, "0123456789ABCDEF");

		if (fmt[1] == '*') {
			endnull = store = FALSE;
			++fmt;
		}

		fmt++;
		while (isdigit(*fmt)) {
			if (width == -1)
				width = 0;
			wide1 = width = (width * 10) + (*fmt - '0');
			fmt++;
		}
		--fmt;

fmtnxt:
		++fmt;
		switch (tolower(*fmt)) {
			case '*':
				endnull = store = FALSE;
				goto fmtnxt;

			case 'l':		/* long data */
				lval = TRUE;
				goto fmtnxt;
			case 'h':		/* short data */
				lval = FALSE;
				goto fmtnxt;

			case 'i':		/* any-base numeric */
				base = 0;
				goto numfmt;

			case 'b':		/* unsigned binary */
				base = 2;
				goto numfmt;

			case 'o':		/* unsigned octal */
				base = 8;
				goto numfmt;

			case 'x':		/* unsigned hexadecimal */
				base = 16;
				goto numfmt;

			case 'd':		/* signed decimal */
				neg = 0;

			case 'u':		/* unsigned decimal */

numfmt:				skip();
				if (isupper(*fmt))
					lval = TRUE;

				if (base == 0) {
					base = 10;
					neg = 0;
					if (c == '%') {
						base = 2;
						goto skip1;
					} else if (c == '0') {
						c = cgetc();
						if (c == '\n' || c == CHR_BREAK)
							goto savnum;
						if (tolower(c) != 'x') {
							base = 8;
							digits[8] = '\0';
							goto zeroin;
						}
						base = 16;
						goto skip1;
					}
				}

				if ((neg == 0) &&
				    (base == 10) &&
				    ((neg = (c == '-')) || (c == '+'))) {
skip1:
					c = cgetc();
					if (c == '\n' || c == CHR_BREAK)
						goto done;
				}

				digits[base] = '\0';
				p = (unsigned char *)_strchr((char *)digits, toupper(c));

				if ((!c || !p) && width)
					goto done;

				while (p && width-- && c) {
					n = (n * base) + (p - digits);
					c = cgetc();
zeroin:
					p = (unsigned char *)
						strchr((char *)digits, toupper(c));
				}

savnum:
				if (store) {
					if (neg == 1)
						n = -n;
					if (lval)
						*va_arg(ap, long *) = n;
					else
						*va_arg(ap, short*) = n;
					++cnt;
				}
				break;

			case 'c':		/* character data */
				width = wide1;
				lval = endnull = FALSE;
				delim[0] = '\0';
				goto strproc;

			case '[':		/* string w/ delimiter set */

				/* get delimiters */
				p = delim;

				fmt++;
				if (*fmt == '^') {
					fmt++;
					lval = FALSE;
				} else
					lval = TRUE;

				rngflag = 2;
				if ((*fmt == ']') || (*fmt == '-')) {
					*p++ = *fmt++;
					rngflag = 0;
				}

				while (*fmt != ']') {
					if (*fmt == '\0')
						goto done;
					switch (rngflag) {
						case 1:
							c2 = *(p - 2);
							if (c2 <= *fmt) {
								p -= 2;
								while (c2 < *fmt)
									*p++ = c2++;
								rngflag = 2;
								break;
							}
							/* fall thru intentional */

						case 0:
							rngflag = (*fmt == '-');
							break;

						case 2:
							rngflag = 0;
					}
					*p++ = *fmt++;
				}

				*p = '\0';
				goto strproc;

			case 's':		/* string data */
				lval = FALSE;
				skip();

strproc:
				/* process string */
				p = va_arg(ap, unsigned char *);
				if (width) {
					q = (unsigned char *)_strchr((char *)delim, c);
					if ((c == '\n') ||
					    (lval == (q == NULL))) {
						if (endnull)
							*p = '\0';
						goto done;
					}
				}

				while (TRUE) {
					if (store)
						*p++ = c;
					if (((c = cgetc()) == '\n') ||
					    (c == CHR_BREAK) ||
					    (--width == 0))
						break;
					q = (unsigned char *)_strchr((char *)delim, c);
					if (lval == (q == NULL))
						break;
				}

				if (store) {
					if (endnull)
						*p = '\0';
					++cnt;
				}
				break;

			case '\0':		/* early EOS */
				--fmt;

			default:
				goto cmatch;
		}
	} else if (isspace(*fmt)) {
		/* Skip white space */
		skip();
	} else {
		/* Normal character match */
cmatch:
		if (c != *fmt)
			break;
		c = cgetc();
	}

	if (!*++fmt)
		break;
  }

done:				/* end of scan */
  if (c != CHR_BREAK)
	cungetc(c);
  va_end(ap);
  return(cnt);
}

