#include <u.h>
#include <libc.h>
enum
{
SIZE = 1024,
IDIGIT = 40,
MAXCONV = 40,
FDIGIT = 30,
FDEFLT = 6,
NONE = -1000,
MAXFMT = 512,
FPLUS = 1<<0,
FMINUS = 1<<1,
FSHARP = 1<<2,
FLONG = 1<<3,
FSHORT = 1<<4,
FUNSIGN = 1<<5,
FVLONG = 1<<6,
};
int printcol;
static int convcount;
static char fmtindex[MAXFMT];
static int noconv(va_list*, Fconv*);
static int flags(va_list*, Fconv*);
static int cconv(va_list*, Fconv*);
static int rconv(va_list*, Fconv*);
static int sconv(va_list*, Fconv*);
static int percent(va_list*, Fconv*);
static int column(va_list*, Fconv*);
int numbconv(va_list*, Fconv*);
static
int (*fmtconv[MAXCONV])(va_list*, Fconv*) =
{
noconv
};
static
void
initfmt(void)
{
int cc;
cc = 0;
fmtconv[cc] = noconv;
cc++;
fmtconv[cc] = flags;
fmtindex['+'] = cc;
fmtindex['-'] = cc;
fmtindex['#'] = cc;
fmtindex['h'] = cc;
fmtindex['l'] = cc;
fmtindex['u'] = cc;
cc++;
fmtconv[cc] = numbconv;
fmtindex['d'] = cc;
fmtindex['o'] = cc;
fmtindex['x'] = cc;
fmtindex['X'] = cc;
cc++;
fmtconv[cc] = cconv;
fmtindex['c'] = cc;
fmtindex['C'] = cc;
cc++;
fmtconv[cc] = rconv;
fmtindex['r'] = cc;
cc++;
fmtconv[cc] = sconv;
fmtindex['s'] = cc;
fmtindex['S'] = cc;
cc++;
fmtconv[cc] = percent;
fmtindex['%'] = cc;
cc++;
fmtconv[cc] = column;
fmtindex['|'] = cc;
cc++;
convcount = cc;
}
int
fmtinstall(int c, int (*f)(va_list*, Fconv*))
{
if(convcount == 0)
initfmt();
if(c < 0 || c >= MAXFMT)
return -1;
if(convcount >= MAXCONV)
return -1;
fmtconv[convcount] = f;
fmtindex[c] = convcount;
convcount++;
return 0;
}
char*
doprint(char *s, char *es, char *fmt, va_list argp)
{
int n, c;
Rune rune;
Fconv local;
if(s >= es)
return s;
local.out = s;
local.eout = es-UTFmax-1;
loop:
c = *fmt & 0xff;
if(c >= Runeself) {
n = chartorune(&rune, fmt);
fmt += n;
c = rune;
} else
fmt++;
switch(c) {
case 0:
*local.out = 0;
return local.out;
default:
printcol++;
goto common;
case '\n':
printcol = 0;
goto common;
case '\t':
printcol = (printcol+8) & ~7;
goto common;
common:
if(local.out < local.eout)
if(c >= Runeself) {
rune = c;
n = runetochar(local.out, &rune);
local.out += n;
} else
*local.out++ = c;
goto loop;
case '%':
break;
}
local.f1 = NONE;
local.f2 = NONE;
local.f3 = 0;
/*
* read one of the following
* 1. number, => f1, f2 in order.
* 2. '*' same as number (from args)
* 3. '.' ignored (separates numbers)
* 4. flag => f3
* 5. verb and terminate
*/
l0:
c = *fmt & 0xff;
if(c >= Runeself) {
n = chartorune(&rune, fmt);
fmt += n;
c = rune;
} else
fmt++;
l1:
if(c == 0) {
fmt--;
goto loop;
}
if(c == '.') {
if(local.f1 == NONE)
local.f1 = 0;
local.f2 = 0;
goto l0;
}
if((c >= '1' && c <= '9') ||
(c == '0' && local.f1 != NONE)) { /* '0' is a digit for f2 */
n = 0;
while(c >= '0' && c <= '9') {
n = n*10 + c-'0';
c = *fmt++;
}
if(local.f1 == NONE)
local.f1 = n;
else
local.f2 = n;
goto l1;
}
if(c == '*') {
n = va_arg(argp, int);
if(local.f1 == NONE)
local.f1 = n;
else
local.f2 = n;
goto l0;
}
n = 0;
if(c >= 0 && c < MAXFMT)
n = fmtindex[c];
local.chr = c;
n = (*fmtconv[n])(&argp, &local);
if(n < 0) {
local.f3 |= -n;
goto l0;
}
goto loop;
}
int
numbconv(va_list *arg, Fconv *fp)
{
char s[IDIGIT];
int i, f, n, b, ucase;
short h;
long v;
vlong vl;
SET(v);
SET(vl);
ucase = 0;
b = fp->chr;
switch(fp->chr) {
case 'u':
fp->f3 |= FUNSIGN;
case 'd':
b = 10;
break;
case 'o':
b = 8;
break;
case 'X':
ucase = 1;
case 'x':
b = 16;
break;
}
f = 0;
switch(fp->f3 & (FVLONG|FLONG|FSHORT|FUNSIGN)) {
case FVLONG|FLONG:
vl = va_arg(*arg, vlong);
break;
case FUNSIGN|FVLONG|FLONG:
vl = va_arg(*arg, uvlong);
break;
case FLONG:
v = va_arg(*arg, long);
break;
case FUNSIGN|FLONG:
v = va_arg(*arg, ulong);
break;
case FSHORT:
h = va_arg(*arg, int);
v = h;
break;
case FUNSIGN|FSHORT:
h = va_arg(*arg, int);
v = (ushort)h;
break;
default:
v = va_arg(*arg, int);
break;
case FUNSIGN:
v = va_arg(*arg, unsigned);
break;
}
if(fp->f3 & FVLONG) {
if(!(fp->f3 & FUNSIGN) && vl < 0) {
vl = -vl;
f = 1;
}
} else {
if(!(fp->f3 & FUNSIGN) && v < 0) {
v = -v;
f = 1;
}
}
s[IDIGIT-1] = 0;
for(i = IDIGIT-2;; i--) {
if(fp->f3 & FVLONG)
n = (uvlong)vl % b;
else
n = (ulong)v % b;
n += '0';
if(n > '9') {
n += 'a' - ('9'+1);
if(ucase)
n += 'A'-'a';
}
s[i] = n;
if(i < 2)
break;
if(fp->f3 & FVLONG)
vl = (uvlong)vl / b;
else
v = (ulong)v / b;
if(fp->f2 != NONE && i >= IDIGIT-fp->f2)
continue;
if(fp->f3 & FVLONG) {
if(vl <= 0)
break;
continue;
}
if(v <= 0)
break;
}
if(fp->f3 & FSHARP) {
if(b == 8 && s[i] != '0')
s[--i] = '0';
if(b == 16) {
if(ucase)
s[--i] = 'X';
else
s[--i] = 'x';
s[--i] = '0';
}
}
if(f)
s[--i] = '-';
fp->f2 = NONE;
strconv(s+i, fp);
return 0;
}
void
Strconv(Rune *s, Fconv *fp)
{
int n, c, i;
Rune rune;
if(fp->f3 & FMINUS)
fp->f1 = -fp->f1;
n = 0;
if(fp->f1 != NONE && fp->f1 >= 0) {
for(; s[n]; n++)
;
while(n < fp->f1) {
if(fp->out < fp->eout)
*fp->out++ = ' ';
printcol++;
n++;
}
}
for(;;) {
c = *s++;
if(c == 0)
break;
n++;
if(fp->f2 == NONE || fp->f2 > 0) {
if(fp->out < fp->eout)
if(c >= Runeself) {
rune = c;
i = runetochar(fp->out, &rune);
fp->out += i;
} else
*fp->out++ = c;
if(fp->f2 != NONE)
fp->f2--;
switch(c) {
default:
printcol++;
break;
case '\n':
printcol = 0;
break;
case '\t':
printcol = (printcol+8) & ~7;
break;
}
}
}
if(fp->f1 != NONE && fp->f1 < 0) {
fp->f1 = -fp->f1;
while(n < fp->f1) {
if(fp->out < fp->eout)
*fp->out++ = ' ';
printcol++;
n++;
}
}
}
void
strconv(char *s, Fconv *fp)
{
int n, c, i;
Rune rune;
if(fp->f3 & FMINUS)
fp->f1 = -fp->f1;
n = 0;
if(fp->f1 != NONE && fp->f1 >= 0) {
n = utflen(s);
while(n < fp->f1) {
if(fp->out < fp->eout)
*fp->out++ = ' ';
printcol++;
n++;
}
}
for(;;) {
c = *s & 0xff;
if(c >= Runeself) {
i = chartorune(&rune, s);
s += i;
c = rune;
} else
s++;
if(c == 0)
break;
n++;
if(fp->f2 == NONE || fp->f2 > 0) {
if(fp->out < fp->eout)
if(c >= Runeself) {
rune = c;
i = runetochar(fp->out, &rune);
fp->out += i;
} else
*fp->out++ = c;
if(fp->f2 != NONE)
fp->f2--;
switch(c) {
default:
printcol++;
break;
case '\n':
printcol = 0;
break;
case '\t':
printcol = (printcol+8) & ~7;
break;
}
}
}
if(fp->f1 != NONE && fp->f1 < 0) {
fp->f1 = -fp->f1;
while(n < fp->f1) {
if(fp->out < fp->eout)
*fp->out++ = ' ';
printcol++;
n++;
}
}
}
static
int
noconv(va_list *arg, Fconv *fp)
{
int n;
char s[10];
if(convcount == 0) {
initfmt();
n = 0;
if(fp->chr >= 0 && fp->chr < MAXFMT)
n = fmtindex[fp->chr];
return (*fmtconv[n])(arg, fp);
}
s[0] = '*';
s[1] = fp->chr;
s[2] = '*';
s[3] = 0;
fp->f1 = 0;
fp->f2 = NONE;
fp->f3 = 0;
strconv(s, fp);
return 0;
}
static
int
rconv(va_list*, Fconv *fp)
{
char s[ERRLEN];
s[0] = 0;
errstr(s);
fp->f2 = NONE;
strconv(s, fp);
return 0;
}
static
int
cconv(va_list *arg, Fconv *fp)
{
char s[10];
Rune rune;
rune = va_arg(*arg, int);
if(fp->chr == 'c')
rune &= 0xff;
s[runetochar(s, &rune)] = 0;
fp->f2 = NONE;
strconv(s, fp);
return 0;
}
static
int
sconv(va_list *arg, Fconv *fp)
{
char *s;
Rune *r;
if(fp->chr == 's') {
s = va_arg(*arg, char*);
if(s == 0)
s = "<null>";
strconv(s, fp);
} else {
r = va_arg(*arg, Rune*);
if(r == 0)
r = L"<null>";
Strconv(r, fp);
}
return 0;
}
static
int
percent(va_list*, Fconv *fp)
{
if(fp->out < fp->eout)
*fp->out++ = '%';
printcol++;
return 0;
}
static
int
column(va_list *arg, Fconv *fp)
{
int col, pc;
col = va_arg(*arg, int);
while(fp->out < fp->eout && printcol < col) {
pc = (printcol+8) & ~7;
if(pc <= col) {
*fp->out++ = '\t';
printcol = pc;
} else {
*fp->out++ = ' ';
printcol++;
}
}
return 0;
}
static
int
flags(va_list*, Fconv *fp)
{
int f;
f = 0;
switch(fp->chr) {
case '+':
f = FPLUS;
break;
case '-':
f = FMINUS;
break;
case '#':
f = FSHARP;
break;
case 'h':
f = FSHORT;
break;
case 'l':
f = FLONG;
if(fp->f3 & FLONG)
f = FVLONG;
break;
case 'u':
f = FUNSIGN;
break;
}
return -f;
}
|