/***********************************************************************
* *
* This software is part of the ast package *
* Copyright (c) 1985-2010 AT&T Intellectual Property *
* and is licensed under the *
* Common Public License, Version 1.0 *
* by AT&T Intellectual Property *
* *
* A copy of the License is available at *
* http://www.opensource.org/licenses/cpl1.0.txt *
* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
* *
* Information and Software Systems Research *
* AT&T Research *
* Florham Park NJ *
* *
* Glenn Fowler <gsf@research.att.com> *
* David Korn <dgk@research.att.com> *
* Phong Vo <kpv@research.att.com> *
* *
***********************************************************************/
#pragma prototyped
/*
* Glenn Fowler
* AT&T Research
*
* keyword printf support
*/
#include <ast.h>
#include <ccode.h>
#include <ctype.h>
#include <sfdisc.h>
#include <regex.h>
#define FMT_case 1
#define FMT_edit 2
typedef struct
{
Sffmt_t fmt;
void* handle;
Sf_key_lookup_t lookup;
Sf_key_convert_t convert;
Sfio_t* tmp[2];
regex_t red[2];
regex_t* re[2];
int invisible;
int level;
int version;
} Fmt_t;
typedef struct
{
char* next;
int delimiter;
int first;
} Field_t;
typedef union
{
char** p;
char* s;
Sflong_t q;
long l;
int i;
short h;
char c;
} Value_t;
#define initfield(f,s) ((f)->first = (f)->delimiter = *((f)->next = (s)))
static char*
getfield(register Field_t* f, int restore)
{
register char* s;
register int n;
register int c;
register int lp;
register int rp;
char* b;
if (!f->delimiter)
return 0;
s = f->next;
if (f->first)
f->first = 0;
else if (restore)
*s = f->delimiter;
b = ++s;
lp = rp = n = 0;
for (;;)
{
if (!(c = *s++))
{
f->delimiter = 0;
break;
}
else if (c == CC_esc || c == '\\')
{
if (*s)
s++;
}
else if (c == lp)
n++;
else if (c == rp)
n--;
else if (n <= 0)
{
if (c == '(' && restore)
{
lp = '(';
rp = ')';
n = 1;
}
else if (c == '[' && restore)
{
lp = '[';
rp = ']';
n = 1;
}
else if (c == f->delimiter)
{
*(f->next = --s) = 0;
break;
}
}
}
return b;
}
/*
* sfio %! extension function
*/
static int
getfmt(Sfio_t* sp, void* vp, Sffmt_t* dp)
{
register Fmt_t* fp = (Fmt_t*)dp;
Value_t* value = (Value_t*)vp;
register char* v;
char* t;
char* b;
char* a = 0;
char* s = 0;
Sflong_t n = 0;
int h = 0;
int i = 0;
int x = 0;
int d;
Field_t f;
regmatch_t match[10];
fp->level++;
if (fp->fmt.t_str && fp->fmt.n_str > 0 && (v = fmtbuf(fp->fmt.n_str + 1)))
{
memcpy(v, fp->fmt.t_str, fp->fmt.n_str);
v[fp->fmt.n_str] = 0;
b = v;
for (;;)
{
switch (*v++)
{
case 0:
break;
case '(':
h++;
continue;
case ')':
h--;
continue;
case '=':
case ':':
case ',':
if (h <= 0)
{
a = v;
break;
}
continue;
default:
continue;
}
if (i = *--v)
{
*v = 0;
if (i == ':' && fp->fmt.fmt == 's' && strlen(a) > 4 && !isalnum(*(a + 4)))
{
d = *(a + 4);
*(a + 4) = 0;
if (streq(a, "case"))
x = FMT_case;
else if (streq(a, "edit"))
x = FMT_edit;
*(a + 4) = d;
if (x)
a = 0;
}
}
break;
}
n = i;
t = fp->fmt.t_str;
fp->fmt.t_str = b;
h = (*fp->lookup)(fp->handle, &fp->fmt, a, &s, &n);
fp->fmt.t_str = t;
if (i)
*v++ = i;
}
else
{
h = (*fp->lookup)(fp->handle, &fp->fmt, a, &s, &n);
v = 0;
}
fp->fmt.flags |= SFFMT_VALUE;
switch (fp->fmt.fmt)
{
case 'c':
value->c = s ? *s : n;
break;
case 'd':
case 'i':
fp->fmt.size = sizeof(Sflong_t);
value->q = (Sflong_t)(s ? strtoll(s, NiL, 0) : n);
break;
case 'o':
case 'u':
case 'x':
fp->fmt.size = sizeof(Sflong_t);
value->q = s ? (Sflong_t)strtoull(s, NiL, 0) : n;
break;
case 'p':
if (s)
n = strtoll(s, NiL, 0);
value->p = pointerof(n);
break;
case 'q':
if (s)
{
fp->fmt.fmt = 's';
value->s = fmtquote(s, "$'", "'", strlen(s), 0);
}
else
{
fp->fmt.fmt = 'd';
value->q = n;
}
break;
case 's':
if (!s && (!h || !fp->tmp[1] && !(fp->tmp[1] = sfstropen()) || sfprintf(fp->tmp[1], "%I*d", sizeof(n), n) <= 0 || !(s = sfstruse(fp->tmp[1]))))
s = "";
if (x)
{
h = 0;
d = initfield(&f, v + 4);
switch (x)
{
case FMT_case:
while ((a = getfield(&f, 1)) && (v = getfield(&f, 0)))
{
if (strmatch(s, a))
{
Fmt_t fmt;
fmt = *fp;
fmt.fmt.form = v;
for (h = 0; h < elementsof(fmt.tmp); h++)
fmt.tmp[h] = 0;
if (!fp->tmp[0] && !(fp->tmp[0] = sfstropen()) || sfprintf(fp->tmp[0], "%!", &fmt) <= 0 || !(s = sfstruse(fp->tmp[0])))
s = "";
*(v - 1) = d;
if (f.delimiter)
*f.next = d;
for (h = 0; h < elementsof(fmt.tmp); h++)
if (fmt.tmp[h])
sfclose(fmt.tmp[h]);
h = 1;
break;
}
*(v - 1) = d;
}
break;
case FMT_edit:
for (x = 0; *f.next; x ^= 1)
{
if (fp->re[x])
regfree(fp->re[x]);
else
fp->re[x] = &fp->red[x];
if (regcomp(fp->re[x], f.next, REG_DELIMITED|REG_NULL))
break;
f.next += fp->re[x]->re_npat;
if (regsubcomp(fp->re[x], f.next, NiL, 0, 0))
break;
f.next += fp->re[x]->re_npat;
if (!regexec(fp->re[x], s, elementsof(match), match, 0) && !regsubexec(fp->re[x], s, elementsof(match), match))
{
s = fp->re[x]->re_sub->re_buf;
if (fp->re[x]->re_sub->re_flags & REG_SUB_STOP)
break;
}
}
h = 1;
break;
}
if (!h)
s = "";
}
value->s = s;
if (fp->level == 1)
while ((s = strchr(s, CC_esc)) && *(s + 1) == '[')
do fp->invisible++; while (*s && !islower(*s++));
break;
case 'Z':
fp->fmt.fmt = 'c';
value->c = 0;
break;
case '\n':
value->s = "\n";
break;
case '.':
value->i = n;
break;
default:
if ((!fp->convert || !(value->s = (*fp->convert)(fp->handle, &fp->fmt, a, s, n))) && (!fp->tmp[0] && !(fp->tmp[0] = sfstropen()) || sfprintf(fp->tmp[0], "%%%c", fp->fmt.fmt) <= 0 || !(value->s = sfstruse(fp->tmp[0]))))
value->s = "";
break;
}
fp->level--;
return 0;
}
/*
* this is the 20000308 interface with Sffmt_t* callback args
*/
int
sfkeyprintf(Sfio_t* sp, void* handle, const char* format, Sf_key_lookup_t lookup, Sf_key_convert_t convert)
{
register int i;
int r;
Fmt_t fmt;
memset(&fmt, 0, sizeof(fmt));
fmt.version = 20030909;
fmt.fmt.version = SFIO_VERSION;
fmt.fmt.form = (char*)format;
fmt.fmt.extf = getfmt;
fmt.handle = handle;
fmt.lookup = lookup;
fmt.convert = convert;
r = sfprintf(sp, "%!", &fmt) - fmt.invisible;
for (i = 0; i < elementsof(fmt.tmp); i++)
if (fmt.tmp[i])
sfclose(fmt.tmp[i]);
return r;
}
/*
* this is the original interface
*/
#undef sfkeyprintf
int
sfkeyprintf(Sfio_t* sp, void* handle, const char* format, Sf_key_lookup_t lookup, Sf_key_convert_t convert)
{
register int i;
int r;
Fmt_t fmt;
memset(&fmt, 0, sizeof(fmt));
fmt.fmt.version = SFIO_VERSION;
fmt.fmt.form = (char*)format;
fmt.fmt.extf = getfmt;
fmt.handle = handle;
fmt.lookup = lookup;
fmt.convert = convert;
r = sfprintf(sp, "%!", &fmt) - fmt.invisible;
for (i = 0; i < elementsof(fmt.tmp); i++)
if (fmt.tmp[i])
sfclose(fmt.tmp[i]);
for (i = 0; i < elementsof(fmt.re); i++)
if (fmt.re[i])
regfree(fmt.re[i]);
return r;
}