/*	From NetBSD: subr_prf.c,v 1.19 1995/06/16 10:52:17 cgd Exp	*/

/*-
 * Copyright (c) 1986, 1988, 1991, 1993
 *	The Regents of the University of California.  All rights reserved.
 * (c) UNIX System Laboratories, Inc.
 * All or some portions of this file are derived from material licensed
 * to the University of California by American Telephone and Telegraph
 * Co. or Unix System Laboratories, Inc. and are reproduced herein with
 * the permission of UNIX System Laboratories, Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)subr_prf.c	8.3 (Berkeley) 1/21/94
 */

#include <stdarg.h>
#include "nonstdio.h"
#include <linux/autoconf.h>

#ifdef CONFIG_XMON

typedef unsigned long u_long;
typedef unsigned int u_int;
typedef unsigned char u_char;

#define NULL	((void *)0)
#define NBBY	8

extern void putc(int c, void *f);
void printf(const char *fmt, ...);
void fprintf(void *f, const char *fmt, ...);
static char *ksprintn (u_long num, int base, int *len, int prec);
void vfprintf (void *f, const char *fmt, va_list ap);

/*
 * Scaled down version of vfprintf(3).
 *
 * Two additional formats:
 *
 * The format %b is supported to decode error registers.
 * Its usage is:
 *
 *	printf("reg=%b\n", regval, "<base><arg>*");
 *
 * where <base> is the output base expressed as a control character, e.g.
 * \10 gives octal; \20 gives hex.  Each arg is a sequence of characters,
 * the first of which gives the bit number to be inspected (origin 1), and
 * the next characters (up to a control character, i.e. a character <= 32),
 * give the name of the register.  Thus:
 *
 *	printf("reg=%b\n", 3, "\10\2BITTWO\1BITONE\n");
 *
 * would produce output:
 *
 *	reg=3<BITTWO,BITONE>
 *
 * The format %r passes an additional format string and argument list
 * recursively.  Its usage is:
 *
 * fn(char *fmt, ...)
 * {
 *	va_list ap;
 *	va_start(ap, fmt);
 *	printf("prefix: %r: suffix\n", fmt, ap);
 *	va_end(ap);
 * }
 *
 * Space or zero padding and a field width are supported for the numeric
 * formats only.
 */
void
xmon_vfprintf(void *f, const char *fmt, va_list ap)
{
	register char *p, *q;
	register int ch, n;
	u_long ul;
	int base, lflag, tmp, width;
	int prec, precspec;
	char padc;

	for (;;) {
		padc = ' ';
		width = 0;
		prec = 0;
		precspec = 0;
		while ((ch = *(u_char *)fmt++) != '%') {
			if (ch == '\0')
				return;
			putc(ch, f);
		}
		lflag = 0;
reswitch:	switch (ch = *(u_char *)fmt++) {
		case '0':
			padc = '0';
			goto reswitch;
		case '1': case '2': case '3': case '4':
		case '5': case '6': case '7': case '8': case '9':
			for (width = 0;; ++fmt) {
				width = width * 10 + ch - '0';
				ch = *fmt;
				if (ch < '0' || ch > '9')
					break;
			}
			goto reswitch;
		case '*':
			width = va_arg(ap, int);
			goto reswitch;
		case '.':
			precspec = 1;
			if (*fmt == '*') {
				prec = va_arg(ap, int);
				++fmt;
				goto reswitch;
			}
			for (prec = 0;; ++fmt) {
				ch = *fmt;
				if (ch < '0' || ch > '9')
					break;
				prec = prec * 10 + ch - '0';
			}
			goto reswitch;
		case 'l':
			lflag = 1;
			goto reswitch;
		case 'b':
			ul = va_arg(ap, int);
			p = va_arg(ap, char *);
			for (q = ksprintn(ul, *p++, NULL, 0); ch = *q--;)
				putc(ch, f);

			if (!ul)
				break;

			for (tmp = 0; n = *p++;) {
				if (ul & (1 << (n - 1))) {
					putc(tmp ? ',' : '<', f);
					for (; (n = *p) > ' '; ++p)
						putc(n, f);
					tmp = 1;
				} else
					for (; *p > ' '; ++p)
						continue;
			}
			if (tmp)
				putc('>', f);
			break;
		case 'c':
			putc(va_arg(ap, int), f);
			break;
		case 'r':
			p = va_arg(ap, char *);
			xmon_vfprintf(f, p, va_arg(ap, va_list));
			break;
		case 's':
			if ((p = va_arg(ap, char *)) == NULL)
				p = "(null)";
			while ((!precspec || --prec >= 0) && (ch = *p++))
				putc(ch, f);
			break;
		case 'd':
			ul = lflag ? va_arg(ap, long) : va_arg(ap, int);
			if ((long)ul < 0) {
				putc('-', f);
				ul = -(long)ul;
			}
			base = 10;
			goto number;
		case 'o':
			ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int);
			base = 8;
			goto number;
		case 'u':
			ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int);
			base = 10;
			goto number;
		case 'p':
			putc('0', f);
			putc('x', f);
			ul = (u_long)va_arg(ap, void *);
			base = 16;
			goto number;
		case 'x':
			ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int);
			base = 16;
number:			p = ksprintn(ul, base, &tmp, prec);
			if (width && (width -= tmp) > 0)
				while (width--)
					putc(padc, f);
			while (ch = *p--)
				putc(ch, f);
			break;
		default:
			putc('%', f);
			if (lflag)
				putc('l', f);
			/* FALLTHROUGH */
		case '%':
			putc(ch, f);
		}
	}
}

void
xmon_printf(const char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	xmon_vfprintf(stdout, fmt, ap);
	va_end(ap);
}

void
xmon_fprintf(void *f, const char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	xmon_vfprintf(f, fmt, ap);
	va_end(ap);
}

/*
 * Scaled down version of sprintf(3).
 */
void
kvsprintf(char *buf, const char *cfmt, va_list ap)
{
	register const char *fmt = cfmt;
	register char *p, *bp;
	register int ch, base;
	u_long ul;
	int lflag, tmp, width;
	int prec, precspec;
	char padc;

	for (bp = buf; ; ) {
		padc = ' ';
		width = 0;
		prec = 0;
		precspec = 0;
		while ((ch = *(u_char *)fmt++) != '%')
			if ((*bp++ = ch) == '\0')
				return;

		lflag = 0;
reswitch:	switch (ch = *(u_char *)fmt++) {
		case '0':
			padc = '0';
			goto reswitch;
		case '1': case '2': case '3': case '4':
		case '5': case '6': case '7': case '8': case '9':
			for (width = 0;; ++fmt) {
				width = width * 10 + ch - '0';
				ch = *fmt;
				if (ch < '0' || ch > '9')
					break;
			}
			goto reswitch;
		case '*':
			width = va_arg(ap, int);
			goto reswitch;
		case '.':
			precspec = 1;
			if (*fmt == '*') {
				prec = va_arg(ap, int);
				++fmt;
				goto reswitch;
			}
			for (prec = 0;; ++fmt) {
				ch = *fmt;
				if (ch < '0' || ch > '9')
					break;
				prec = prec * 10 + ch - '0';
			}
			goto reswitch;
		case 'l':
			lflag = 1;
			goto reswitch;
		/* case 'b': ... break; XXX */
		case 'c':
			*bp++ = va_arg(ap, int);
			break;
		/* case 'r': ... break; XXX */
		case 's':
			p = va_arg(ap, char *);
			while ((!precspec || --prec >= 0) && (*bp = *p++))
				++bp;
			break;
		case 'd':
			ul = lflag ? va_arg(ap, long) : va_arg(ap, int);
			if ((long)ul < 0) {
				*bp++ = '-';
				ul = -(long)ul;
			}
			base = 10;
			goto number;
			break;
		case 'o':
			ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int);
			base = 8;
			goto number;
			break;
		case 'u':
			ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int);
			base = 10;
			goto number;
			break;
		case 'p':
			*bp++ = '0';
			*bp++ = 'x';
			ul = (u_long)va_arg(ap, void *);
			base = 16;
			goto number;
		case 'x':
		case 'X':
			ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int);
			base = 16;
number:			p = ksprintn(ul, base, &tmp, prec);
			if (width && (width -= tmp) > 0)
				while (width--)
					*bp++ = padc;
			while (ch = *p--)
				*bp++ = ch;
			break;
		default:
			*bp++ = '%';
			if (lflag)
				*bp++ = 'l';
			/* FALLTHROUGH */
		case '%':
			*bp++ = ch;
		}
	}
	va_end(ap);
}

void
xmon_sprintf(char *buf,const char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	kvsprintf(buf, fmt, ap);
	va_end(ap);
}
void
ksprintf(char *buf,const char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	kvsprintf(buf, fmt, ap);
	va_end(ap);
}

/*
 * Put a number (base <= 16) in a buffer in reverse order; return an
 * optional length and a pointer to the NULL terminated (preceded?)
 * buffer.
 */
static char *
ksprintn(ul, base, lenp, prec)
	register u_long ul;
	register int base, *lenp, prec;
{					/* A long in base 8, plus NULL. */
	static char buf[sizeof(long) * NBBY / 3 + 2];
	register char *p;

	p = buf;
	do {
		*++p = "0123456789abcdef"[ul % base];
		ul /= base;
	} while ((--prec > 0 || ul != 0) && p < buf + sizeof(buf) - 1);
	if (lenp)
		*lenp = p - buf;
	return (p);
}
#endif CONFIG_XMON
