/* 
 * $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */

#ident "$Header: nl_scanf.c 1.2 90/06/25 $@(#)nl_scanf.c	1.3 89/12/01"

/*LINTLIBRARY*/
/*
 *	scanf - nl_scanf
 *	common code
 */


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

#define ARGMAX	64
static unsigned char newap[ARGMAX * sizeof(double)];
static unsigned char newform[256];
extern char *strcpy();

int
nl_scanf(fmt, va_alist)
char *fmt;
va_dcl
{
	va_list ap;

	va_start(ap);
	snl_to_sv(strcpy(newform, fmt), ap, newap);
	return(_doscan(stdin, newform, newap));
}

/*VARARGS2*/
int
nl_fscanf(iop, fmt, va_alist)
FILE *iop;
char *fmt;
va_dcl
{
	va_list ap;

	va_start(ap);
	snl_to_sv(strcpy(newform, fmt), ap, newap);
	return(_doscan(iop, newform, newap));
}

/*VARARGS2*/
int
nl_sscanf(str, fmt, va_alist)
register char *str;
char *fmt;
va_dcl
{
	va_list ap;
	FILE strbuf;

	va_start(ap);
	strbuf._flag = _IOREAD;
	strbuf._ptr = strbuf._base = (unsigned char*)str;
	strbuf._cnt = strlen(str);
	strbuf._file = _NFILE;
	snl_to_sv(strcpy(newform, fmt), ap, newap);
	return(_doscan(&strbuf, newform, newap));
}


/*
 * This function reorganises the format string and argument list
 * for the nl_scanf family to make it compatible with scanf family
 */


#ifndef NL_ARGMAX
#define NL_ARGMAX	9
#endif

struct al {
	int	a_num;		/* arg # specified at this position */
	int	a_type;		/* type of arg specified at this position */
	union	at {
		int	*a_i;
		long	*a_l;
		short	*a_u;
		float	*a_f;
		double	*a_d;
		unsigned char	*a_s;
	} a_val;		/* arg at this position in arg list */
	int	a_ptype;	/* type of arg here in arg list */
	unsigned char	*a_start;	/* ptr to 'n' part of '%n$' in format str */
	unsigned char	*a_end;		/* ptr to '$'+1 part of '%n$' in format str */
};

#define A_INT		1
#define A_LONG		2
#define A_SHORT		3
#define A_FLOAT		4
#define A_DOUBLE	5
#define A_STRING	6

snl_to_sv(format, list, newlist)
unsigned char *format, *list, *newlist;
{
	register i, fcode, shorty, length, nl_fmt, num, j;
	unsigned char *fmtsav;
	struct al args[ARGMAX + 1];

	for (i = 0; i <= ARGMAX; args[i++].a_num = 0);
	nl_fmt = 0;
	i = j = 1;
	while (*format) {
		while ((fcode = *format++) != '\0' && fcode != '%') ;
		if (!fcode || i > ARGMAX)
			break;
		shorty = length = 0;
	charswitch:
		switch (fcode = *format++) {
		case 'l':
			length++;
			goto charswitch;
		case 'h':
			shorty++;
			goto charswitch;
		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
			num = fcode - '0';
			fmtsav = format;
			while (isdigit(fcode = *format)) {
				num = num * 10 + fcode - '0';
				format++;
			}
			if (*format == '$') {
				nl_fmt++;
				args[i].a_start = fmtsav - 1;
				args[i].a_end = ++format;
				if (num > NL_ARGMAX)
					num = num;
				args[i].a_num = num;
			}
			goto charswitch;
	/* now have arg type only to parse */
		case 'd':
		case 'u':
		case 'o':
		case 'x':
			if (!args[i].a_num) {
				args[i].a_start = args[i].a_end = format - 1;
				args[i].a_num = j++;
			}
			if (length)
				args[i].a_type = A_LONG;
			else if (shorty)
				args[i].a_type = A_SHORT;
			else
				args[i].a_type = A_INT;
			args[args[i].a_num].a_ptype = args[i].a_type;
			i++;
			break;
		case 'e':
		case 'f':
		case 'g':
			if (!args[i].a_num) {
				args[i].a_start = args[i].a_end = format - 1;
				args[i].a_num = j++;
			}
			if (length)
				args[i].a_type = A_DOUBLE;
			else if (shorty)
				args[i].a_type = A_FLOAT;
			else
				args[i].a_type = A_FLOAT;
			args[args[i].a_num].a_ptype = args[i].a_type;
			i++;
			break;
		case 'c':
		case '[':
		case 's':
			if (!args[i].a_num) {
				args[i].a_start = args[i].a_end = format - 1;
				args[i].a_num = j++;
			}
			args[i].a_type = A_STRING;
			args[args[i].a_num].a_ptype = args[i].a_type;
			i++;
			break;
		case '*':
		case '%':
			break;
		default:
			format--;
			break;
		}
	}
	length = i;
	if (nl_fmt) {
		unsigned char *aptr, *bptr, *cptr;
		for (i = 1; i < length && args[i].a_num == 0; i++);
		cptr = aptr = args[i].a_start;
		do {
			bptr = args[i++].a_end;
			for (; i < length && args[i].a_num == 0; i++);
			if (i == length) {
				while (*cptr++);
			} else {
				cptr = args[i].a_start;
			}
			for (; bptr != cptr; *aptr++ = *bptr++);
		} while (i < length);
	}
	for (i = 1; i < length; i++) {
		switch(args[i].a_ptype) {
		case A_INT:
			args[i].a_val.a_i = ((int **)(list += sizeof(int *)))[-1];
			break;
		case A_LONG:
			args[i].a_val.a_l = ((long **)(list += sizeof(long *)))[-1];
			break;
		case A_SHORT:
			args[i].a_val.a_u = ((short **)(list += sizeof(short *)))[-1];
			break;
		case A_FLOAT:
			args[i].a_val.a_f = ((float **)(list += sizeof(float *)))[-1];
			break;
		case A_DOUBLE:
			args[i].a_val.a_d = ((double **)(list += sizeof(double *)))[-1];
			break;
		case A_STRING:
			args[i].a_val.a_s = ((unsigned char **)(list += sizeof(unsigned char *)))[-1];
			break;
		default:
			args[i].a_val.a_i = (int *)0;
		}
	}
	for (i = 1; i < length; i++) {
		switch(args[i].a_type) {
		default:
		case A_INT:
			{
			int **ptr;
			ptr = (int **)newlist;
			*ptr = args[args[i].a_num].a_val.a_i;
			newlist += sizeof(int *);
			}
			break;
		case A_LONG:
			{
			long **ptr;
			ptr = (long **)newlist;
			*ptr = args[args[i].a_num].a_val.a_l;
			newlist += sizeof(long *);
			}
			break;
		case A_SHORT:
			{
			short **ptr;
			ptr = (short **)newlist;
			*ptr = args[args[i].a_num].a_val.a_u;
			newlist += sizeof(short *);
			}
			break;
		case A_FLOAT:
			{
			float **ptr;
			ptr = (float **)newlist;
			*ptr = args[args[i].a_num].a_val.a_f;
			newlist += sizeof(float *);
			}
			break;
		case A_DOUBLE:
			{
			double **ptr;
			ptr = (double **)newlist;
			*ptr = args[args[i].a_num].a_val.a_d;
			newlist += sizeof(double *);
			}
			break;
		case A_STRING:
			{
			unsigned char **ptr;
			ptr = (unsigned char **)newlist;
			*ptr = args[args[i].a_num].a_val.a_s;
			newlist += sizeof(unsigned char *);
			}
			break;
		}
	}
}

