/* Copyright (c) 2004 Russ Cox.  See COPYRIGHT. */

#include "taskimpl.h"
#include <stdio.h>	/* for strerror! */

/*
 * Stripped down print library.  Plan 9 interface, new code.
 */

enum
{
	FlagLong = 1<<0,
	FlagLongLong = 1<<1,
	FlagUnsigned = 1<<2,
};

static char*
printstr(char *dst, char *edst, char *s, int size)
{
	int l, n, sign;

	sign = 1;
	if(size < 0){
		size = -size;
		sign = -1;
	}
	if(dst >= edst)
		return dst;
	l = strlen(s);
	n = l;
	if(n < size)
		n = size;
	if(n >= edst-dst)
		n = (edst-dst)-1;
	if(l > n)
		l = n;
	if(sign < 0){
		memmove(dst, s, l);
		if(n-l)
			memset(dst+l, ' ', n-l);
	}else{
		if(n-l)
			memset(dst, ' ', n-l);
		memmove(dst+n-l, s, l);
	}
	return dst+n;
}
	
char*
vseprint(char *dst, char *edst, char *fmt, va_list arg)
{
	int fl, size, sign, base;
	char *p, *w;
	char cbuf[2];

	w = dst;
	for(p=fmt; *p && w<edst-1; p++){
		switch(*p){
		default:
			*w++ = *p;
			break;
		case '%':
			fl = 0;
			size = 0;
			sign = 1;
			for(p++; *p; p++){
				switch(*p){
				case '-':
					sign = -1;
					break;
				case '0':
				case '1':
				case '2':
				case '3':
				case '4':
				case '5':
				case '6':
				case '7':
				case '8':
				case '9':
					size = size*10 + *p-'0';
					break;
				case 'l':
					if(fl&FlagLong)
						fl |= FlagLongLong;
					else
						fl |= FlagLong;
					break;
				case 'u':
					fl |= FlagUnsigned;
					break;
				case 'd':
					base = 10;
					goto num;
				case 'o':
					base = 8;
					goto num;
				case 'p':
				case 'x':
					base = 16;
					goto num;
				num:
				{
					static char digits[] = "0123456789abcdef";
					char buf[30], *p;
					int neg, zero;
					uvlong luv;
				
					if(fl&FlagLongLong){
						if(fl&FlagUnsigned)
							luv = va_arg(arg, uvlong);
						else
							luv = va_arg(arg, vlong);
					}else{
						if(fl&FlagLong){
							if(fl&FlagUnsigned)
								luv = va_arg(arg, ulong);
							else
								luv = va_arg(arg, long);
						}else{
							if(fl&FlagUnsigned)
								luv = va_arg(arg, uint);
							else
								luv = va_arg(arg, int);
						}
					}
				
					p = buf+sizeof buf;
					neg = 0;
					zero = 0;
					if(!(fl&FlagUnsigned) && (vlong)luv < 0){
						neg = 1;
						luv = -luv;
					}
					if(luv == 0)
						zero = 1;
					*--p = 0;
					while(luv){
						*--p = digits[luv%base];
						luv /= base;
					}
					if(base == 16){
						*--p = 'x';
						*--p = '0';
					}
					if(base == 8 || zero) 
						*--p = '0';
					w = printstr(w, edst, p, size*sign);
					goto break2;
				}
				case 'c':
					cbuf[0] = va_arg(arg, int);
					cbuf[1] = 0;
					w = printstr(w, edst, cbuf, size*sign);
					goto break2;
				case 's':
					w = printstr(w, edst, va_arg(arg, char*), size*sign);
					goto break2;
				case 'r':
					w = printstr(w, edst, strerror(errno), size*sign);
					goto break2;
				default:	
					p = "X*verb*";
					goto break2;
				}
			}
		    break2:
			break;
		}
	}
	
	assert(w < edst);
	*w = 0;
	return dst;
}

char*
vsnprint(char *dst, uint n, char *fmt, va_list arg)
{
	return vseprint(dst, dst+n, fmt, arg);
}

char*
snprint(char *dst, uint n, char *fmt, ...)
{
	va_list arg;

	va_start(arg, fmt);
	vsnprint(dst, n, fmt, arg);
	va_end(arg);
	return dst;
}

char*
seprint(char *dst, char *edst, char *fmt, ...)
{
	va_list arg;

	va_start(arg, fmt);
	vseprint(dst, edst, fmt, arg);
	va_end(arg);
	return dst;
}

int
vfprint(int fd, char *fmt, va_list arg)
{
	char buf[256];

	vseprint(buf, buf+sizeof buf, fmt, arg);
	return write(fd, buf, strlen(buf));
}

int
vprint(char *fmt, va_list arg)
{
	return vfprint(1, fmt, arg);
}

int
fprint(int fd, char *fmt, ...)
{
	int n;
	va_list arg;

	va_start(arg, fmt);
	n = vfprint(fd, fmt, arg);
	va_end(arg);
	return n;
}

int
print(char *fmt, ...)
{
	int n;
	va_list arg;

	va_start(arg, fmt);
	n = vprint(fmt, arg);
	va_end(arg);
	return n;
}

char*
strecpy(char *dst, char *edst, char *src)
{
	*printstr(dst, edst, src, 0) = 0;
	return dst;
}



