Training courses

Kernel and Embedded Linux

Bootlin training courses

Embedded Linux, kernel,
Yocto Project, Buildroot, real-time,
graphics, boot time, debugging...

Bootlin logo

Elixir Cross Referencer

/*
 *	minimal printf for Human68k DOS
 *
 *	written by ITOH Yasufumi
 *	public domain
 *
 *	$NetBSD: xprintf.c,v 1.5 2011/02/21 02:31:58 itohy Exp $
 */

#include <sys/types.h>
#ifdef __STDC__
# include <stdarg.h>
#else
# include <varargs.h>
#endif

#include <dos.h>
#include <dos_errno.h>

#include "xprintf.h"

/*
 * From ISO/IEC 9899:1990
 * 7.9.6.1 The fprintf function
 * ...
 * Environment limit
 *    The minimum value for the maximum number of characters
 * produced by any single conversion shall be 509.
 *
 * so the following value shall not be smaller than 510
 * if you want to conform ANSI C (it is only a guideline
 * and maybe no sense on this code, of course :-).
 */
#define PRINTF_BUFSZ	4096

/*
 * Shift-JIS kanji support
 * (No special handling needed for EUC)
 */
#define SJIS

#ifdef SJIS
#define UC(c)		((unsigned char) (c))
#define IS_SJIS1(c)	((UC(c) > 0x80 && UC(c) < 0xa0) ||	\
				(UC(c) >= 0xe0 && UC(c) <= 0xfc))
#define IS_SJIS2(c)	(UC(c) >= 0x40 && UC(c) <= 0xfc && UC(c) != 0x7f)
#endif

#if !defined(__STDC__) && !defined(const)
#define const
#endif

extern const char *const __progname;

static char * numstr(char *buf, long val, int base, int sign);

/*
 * convert number to string
 * buf must have enough space
 */
static char *
numstr(char *buf, long val, int base, int sign)
{
	unsigned long v;
	char rev[32];
	char *r = rev, *b = buf;

	/* negative? */
	if (sign && val < 0) {
		v = -val;
		*b++ = '-';
	} else {
		v = val;
	}

	/* inverse order */
	do {
		*r++ = "0123456789abcdef"[v % base];
		v /= base;
	} while (v);

	/* reverse string */
	while (r > rev)
		*b++ = *--r;

	*b = '\0';
	return buf;
}

/*
 * supported format: %x, %p, %s, %c, %d, %u, %o
 * \n is converted to \r\n
 *
 * XXX argument/parameter types are not strictly handled
 */
size_t
xvsnprintf(char *buf, size_t len, const char *fmt, va_list ap)
{
	char *b = buf;
	const char *s;
	char numbuf[32];

	while (*fmt && len > 1) {
		if (*fmt != '%') {
#ifdef SJIS
			if (IS_SJIS1(*fmt) && IS_SJIS2(fmt[1])) {
				if (len <= 2)
					break;	/* not enough space */
				*b++ = *fmt++;
				len--;
			}
#endif
			if (*fmt == '\n' && (b == buf || b[-1] != '\r')) {
				if (len <= 2)
					break;
				*b++ = '\r';
				len--;
			}
			*b++ = *fmt++;
			len--;
			continue;
		}

		/* %? */
		fmt++;
		switch (*fmt++) {
		case '%':	/* "%%" -> literal % */
			*b++ = '%';
			len--;
			break;

		case 'd':
			s = numstr(numbuf, va_arg(ap, long), 10, 1);
		copy_string:
			for ( ; *s && len > 1; len--)
				*b++ = *s++;
			break;

		case 'u':
			s = numstr(numbuf, va_arg(ap, long), 10, 0);
			goto copy_string;

		case 'p':
			*b++ = '0';
			len--;
			if (len > 1) {
				*b++ = 'x';
				len--;
			}
			/* FALLTHROUGH */
		case 'x':
			s = numstr(numbuf, va_arg(ap, long), 16, 0);
			goto copy_string;

		case 'o':
			s = numstr(numbuf, va_arg(ap, long), 8, 0);
			goto copy_string;

		case 's':
			s = va_arg(ap, char *);
			while (*s && len > 1) {
#ifdef SJIS
				if (IS_SJIS1(*s) && IS_SJIS2(s[1])) {
					if (len <= 2)
						goto break_loop;
					*b++ = *s++;
					len--;
				}
#endif
				if (*s == '\n' && (b == buf || b[-1] != '\r')) {
					if (len <= 2)
						goto break_loop;
					*b++ = '\r';
					len--;
				}
				*b++ = *s++;
				len--;
			}
			break;

		case 'c':
			*b++ = va_arg(ap, int);
			len--;
			break;
		}
	}
break_loop:

	*b = '\0';
	return (char *)b - buf;
}

#ifdef __STDC__
#define VA_START(a, v)	va_start(a, v)
#else
#define VA_START(a, v)	va_start(a)
#endif

#ifdef __STDC__
size_t
xsnprintf(char *buf, size_t len, const char *fmt, ...)
#else
size_t
xsnprintf(buf, len, fmt, va_alist)
	char *buf;
	size_t len;
	const char *fmt;
	va_dcl
#endif
{
	va_list ap;
	size_t ret;

	VA_START(ap, fmt);
	ret = xvsnprintf(buf, len, fmt, ap);
	va_end(ap);

	return ret;
}

size_t
xvfdprintf(int fd, const char *fmt, va_list ap)
{
	char buf[PRINTF_BUFSZ];
	size_t ret;

	ret = xvsnprintf(buf, sizeof buf, fmt, ap);
	if (ret)
		ret = DOS_WRITE(fd, buf, ret);

	return ret;
}

#ifdef __STDC__
size_t
xprintf(const char *fmt, ...)
#else
size_t
xprintf(fmt, va_alist)
	const char *fmt;
	va_dcl
#endif
{
	va_list ap;
	size_t ret;

	VA_START(ap, fmt);
	ret = xvfdprintf(1, fmt, ap);
	va_end(ap);

	return ret;
}

#ifdef __STDC__
size_t
xerrprintf(const char *fmt, ...)
#else
size_t
xerrprintf(fmt, va_alist)
	const char *fmt;
	va_dcl
#endif
{
	va_list ap;
	size_t ret;

	VA_START(ap, fmt);
	ret = xvfdprintf(2, fmt, ap);
	va_end(ap);

	return ret;
}

__dead void
#ifdef __STDC__
xerr(int eval, const char *fmt, ...)
#else
xerr(eval, fmt, va_alist)
	int eval;
	const char *fmt;
	va_dcl
#endif
{
	int e = dos_errno;
	va_list ap;

	xerrprintf("%s: ", __progname);
	if (fmt) {
		VA_START(ap, fmt);
		xvfdprintf(2, fmt, ap);
		va_end(ap);
		xerrprintf(": ");
	}
	xerrprintf("%s\n", dos_strerror(e));
	DOS_EXIT2(eval);
}

__dead void
#ifdef __STDC__
xerrx(int eval, const char *fmt, ...)
#else
xerrx(eval, fmt, va_alist)
	int eval;
	const char *fmt;
	va_dcl
#endif
{
	va_list ap;

	xerrprintf("%s: ", __progname);
	if (fmt) {
		VA_START(ap, fmt);
		xvfdprintf(2, fmt, ap);
		va_end(ap);
	}
	xerrprintf("\n");
	DOS_EXIT2(eval);
}

void
#ifdef __STDC__
xwarn(const char *fmt, ...)
#else
xwarn(fmt, va_alist)
	const char *fmt;
	va_dcl
#endif
{
	int e = dos_errno;
	va_list ap;

	xerrprintf("%s: ", __progname);
	if (fmt) {
		VA_START(ap, fmt);
		xvfdprintf(2, fmt, ap);
		va_end(ap);
		xerrprintf(": ");
	}
	xerrprintf("%s\n", dos_strerror(e));
}

void
#ifdef __STDC__
xwarnx(const char *fmt, ...)
#else
xwarnx(fmt, va_alist)
	const char *fmt;
	va_dcl
#endif
{
	va_list ap;

	xerrprintf("%s: ", __progname);
	if (fmt) {
		VA_START(ap, fmt);
		xvfdprintf(2, fmt, ap);
		va_end(ap);
	}
	xerrprintf("\n");
}