u9fs/doprint.c

611 lines
8.4 KiB
C

#include <plan9.h>
#define lock(x)
#define unlock(x)
enum
{
IDIGIT = 40,
MAXCONV = 40,
FDIGIT = 30,
FDEFLT = 6,
NONE = -1000,
MAXFMT = 512,
FPLUS = 1<<0,
FMINUS = 1<<1,
FSHARP = 1<<2,
FLONG = 1<<3,
FUNSIGN = 1<<5,
FVLONG = 1<<6,
FPOINTER= 1<<7
};
int printcol;
static struct
{
/* Lock; */
int convcount;
char index[MAXFMT];
int (*conv[MAXCONV])(va_list*, Fconv*);
} fmtalloc;
static int noconv(va_list*, Fconv*);
static int flags(va_list*, Fconv*);
static int cconv(va_list*, Fconv*);
static int sconv(va_list*, Fconv*);
static int percent(va_list*, Fconv*);
static int column(va_list*, Fconv*);
extern int numbconv(va_list*, Fconv*);
static void
initfmt(void)
{
int cc;
lock(&fmtalloc);
if(fmtalloc.convcount <= 0) {
cc = 0;
fmtalloc.conv[cc] = noconv;
cc++;
fmtalloc.conv[cc] = flags;
fmtalloc.index['+'] = cc;
fmtalloc.index['-'] = cc;
fmtalloc.index['#'] = cc;
fmtalloc.index['l'] = cc;
fmtalloc.index['u'] = cc;
cc++;
fmtalloc.conv[cc] = numbconv;
fmtalloc.index['d'] = cc;
fmtalloc.index['o'] = cc;
fmtalloc.index['x'] = cc;
fmtalloc.index['X'] = cc;
fmtalloc.index['p'] = cc;
cc++;
fmtalloc.conv[cc] = cconv;
fmtalloc.index['c'] = cc;
fmtalloc.index['C'] = cc;
cc++;
fmtalloc.conv[cc] = sconv;
fmtalloc.index['s'] = cc;
fmtalloc.index['S'] = cc;
cc++;
fmtalloc.conv[cc] = percent;
fmtalloc.index['%'] = cc;
cc++;
fmtalloc.conv[cc] = column;
fmtalloc.index['|'] = cc;
cc++;
fmtalloc.convcount = cc;
}
unlock(&fmtalloc);
}
int
fmtinstall(int c, int (*f)(va_list*, Fconv*))
{
if(fmtalloc.convcount <= 0)
initfmt();
lock(&fmtalloc);
if(c < 0 || c >= MAXFMT) {
unlock(&fmtalloc);
return -1;
}
if(fmtalloc.convcount >= MAXCONV) {
unlock(&fmtalloc);
return -1;
}
fmtalloc.conv[fmtalloc.convcount] = f;
fmtalloc.index[c] = fmtalloc.convcount;
fmtalloc.convcount++;
unlock(&fmtalloc);
return 0;
}
static void
pchar(Rune c, Fconv *fp)
{
int n;
n = fp->eout - fp->out;
if(n > 0) {
if(c < Runeself) {
*fp->out++ = c;
return;
}
if(n >= UTFmax || n >= runelen(c)) {
n = runetochar(fp->out, &c);
fp->out += n;
return;
}
fp->eout = fp->out;
}
}
char*
doprint(char *s, char *es, char *fmt, va_list *argp)
{
int n, c;
Rune rune;
Fconv local;
if(fmtalloc.convcount <= 0)
initfmt();
if(s >= es)
return s;
local.out = s;
local.eout = es-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:
pchar(c, &local);
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 = fmtalloc.index[c];
local.chr = c;
n = (*fmtalloc.conv[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;
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 'b':
b = 2;
break;
case 'o':
b = 8;
break;
case 'X':
ucase = 1;
case 'x':
b = 16;
break;
case 'p':
fp->f3 |= FPOINTER|FUNSIGN;
b = 16;
break;
}
f = 0;
switch(fp->f3 & (FVLONG|FLONG|FUNSIGN|FPOINTER)) {
case FVLONG|FLONG:
vl = va_arg(*arg, vlong);
break;
case FUNSIGN|FVLONG|FLONG:
vl = va_arg(*arg, uvlong);
break;
case FUNSIGN|FPOINTER:
v = (ulong)va_arg(*arg, void*);
break;
case FLONG:
v = va_arg(*arg, long);
break;
case FUNSIGN|FLONG:
v = va_arg(*arg, ulong);
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] = '-';
else if(fp->f3 & FPLUS)
s[--i] = '+';
fp->f2 = NONE;
strconv(s+i, fp);
return 0;
}
void
Strconv(Rune *s, Fconv *fp)
{
int n, c;
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) {
pchar(' ', fp);
printcol++;
n++;
}
}
for(;;) {
c = *s++;
if(c == 0)
break;
n++;
if(fp->f2 == NONE || fp->f2 > 0) {
pchar(c, fp);
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) {
pchar(' ', fp);
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) {
pchar(' ', fp);
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) {
pchar(c, fp);
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) {
pchar(' ', fp);
printcol++;
n++;
}
}
}
static int
noconv(va_list *va, Fconv *fp)
{
char s[10];
USED(va);
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
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 Rune null[] = { L'<', L'n', L'u', L'l', L'l', L'>', L'\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 = null;
Strconv(r, fp);
}
return 0;
}
static int
percent(va_list *va, Fconv *fp)
{
USED(va);
pchar('%', fp);
printcol++;
return 0;
}
static int
column(va_list *arg, Fconv *fp)
{
int col, pc;
col = va_arg(*arg, int);
while(printcol < col) {
pc = (printcol+8) & ~7;
if(pc <= col) {
pchar('\t', fp);
printcol = pc;
} else {
pchar(' ', fp);
printcol++;
}
}
return 0;
}
static int
flags(va_list *va, Fconv *fp)
{
int f;
USED(va);
f = 0;
switch(fp->chr) {
case '+':
f = FPLUS;
break;
case '-':
f = FMINUS;
break;
case '#':
f = FSHARP;
break;
case 'l':
f = FLONG;
if(fp->f3 & FLONG)
f = FVLONG;
break;
case 'u':
f = FUNSIGN;
break;
}
return -f;
}
/*
* This code is superseded by the more accurate (but more complex)
* algorithm in fltconv.c and dtoa.c. Uncomment this routine to avoid
* using the more complex code.
*
*/