/* 
 * $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.
 */

/*LINTLIBRARY*/

/*	Copyright (c) 1987 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/

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

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

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

int
nl_fprintf(iop, format, va_alist)
FILE *iop;
char *format;
va_dcl
{
	register int count;
	va_list ap;

	va_start(ap);
	if (!(iop->_flag & _IOWRT)) {
		/* if no write flag */
		if (iop->_flag & _IORW) {
			/* if ok, cause read-write */
			iop->_flag |= _IOWRT;
		} else {
			/* else error */
			return EOF;
		}
	}
	pnl_to_sv(strcpy(newform, format), ap, newap);
	count = _doprnt(newform, newap, iop);
	va_end(ap);
	return(ferror(iop)? EOF: count);
}
/*LINTLIBRARY*/
/*
 *	printf - nl_printf
 *	common code
 */


/*VARARGS1*/
int
nl_printf(format, va_alist)
char *format;
va_dcl
{
	register int count;
	va_list ap;

	va_start(ap);
	if (!(stdout->_flag & _IOWRT)) {
		/* if no write flag */
		if (stdout->_flag & _IORW) {
			/* if ok, cause read-write */
			stdout->_flag |= _IOWRT;
		} else {
			/* else error */
			return EOF;
		}
	}
	pnl_to_sv(strcpy(newform, format), ap, newap);
	count = _doprnt(newform, newap, stdout);
	va_end(ap);
	return(ferror(stdout)? EOF: count);
}

/*
 *	sprintf - nl_sprintf
 *
 *	common code
 */


int
nl_sprintf(string, format, va_alist)
char *string, *format;
va_dcl
{
	register int count;
	FILE siop;
	va_list ap;

	siop._cnt = MAXINT;
	siop._base = siop._ptr = (unsigned char *)string;
	siop._flag = _IOWRT;
	siop._file = _NFILE;
	va_start(ap);
	pnl_to_sv(strcpy(newform, format), ap, newap);
	count = _doprnt(newform, newap, &siop);
	va_end(ap);
	*siop._ptr = '\0'; /* plant terminating null character */
	return(count);
}

/*
 * This function reorganises the format string and argument list
 * for the nl_printf family to make it compatible with printf 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;
		unsigned	a_u;
		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_USIGN		3
#define A_DOUBLE	4
#define A_STRING	5

pnl_to_sv(format, list, newlist)
unsigned char *format, *list, *newlist;
{
	register i, fcode, 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;
		length = 0;
	charswitch:
		switch (fcode = *format++) {
		case 'l':
			length++;
		case 'h':
		case '+':
		case '-':
		case ' ':
		case '#':
		case '.':
			goto charswitch;
		case '*':
			num = 0;
			fmtsav = format;
			while (isdigit(fcode = *format)) {
				num = num * 10 + fcode - '0';
				format++;
			}
			if (num && *format == '$') {
				nl_fmt++;
				args[i].a_start = fmtsav;
				args[i].a_end = ++format;
				if (num > NL_ARGMAX)
					num = num;
				args[i].a_num = num;
				args[num].a_ptype = A_INT;
			}
			if ( num == 0 ) {
				args[i].a_start = args[i].a_end = format - 1;
				args[i].a_num = j++;
			}
			args[i].a_type = A_INT;
			args[args[i].a_num].a_ptype = A_INT;
			i++;
			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 'i':
		case 'd':
			if (!args[i].a_num) {
				args[i].a_start = args[i].a_end = format - 1;
				args[i].a_num = j++;
			}
			args[i].a_type = length? A_LONG: A_INT;
			args[args[i].a_num].a_ptype = args[i].a_type;
			i++;
			break;
		case 'u':
		case 'o':
		case 'x':
		case 'X':
			if (!args[i].a_num) {
				args[i].a_start = args[i].a_end = format - 1;
				args[i].a_num = j++;
			}
			args[i].a_type = length? A_LONG: A_USIGN;
			args[args[i].a_num].a_ptype = args[i].a_type;
			i++;
			break;
		case 'e':
		case 'E':
		case 'f':
		case 'g':
		case 'G':
			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_DOUBLE;
			args[args[i].a_num].a_ptype = args[i].a_type;
			i++;
			break;
		case 'c':
			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_INT;
			args[args[i].a_num].a_ptype = args[i].a_type;
			i++;
			break;
		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 '%':
			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_USIGN:
			args[i].a_val.a_u = ((unsigned *)(list += sizeof(unsigned)))[-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_USIGN:
			{
			unsigned *ptr;
			ptr = (unsigned *)newlist;
			*ptr = args[args[i].a_num].a_val.a_u;
			newlist += sizeof(unsigned);
			}
			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;
		}
	}
}

