%{
#include <sys/queue.h>
#include <assert.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <limits.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include "bt_parser.h"
#define UNNAMED_MAP "___unnamed_map_doesnt_have_any_name"
struct bt_ruleq g_rules = TAILQ_HEAD_INITIALIZER(g_rules);
int g_nprobes;
SLIST_HEAD(, bt_var) g_variables;
SLIST_HEAD(, bt_var) l_variables;
struct bt_arg g_nullba = BA_INITIALIZER(0, B_AT_LONG);
struct bt_arg g_maxba = BA_INITIALIZER(LONG_MAX, B_AT_LONG);
struct bt_rule *br_new(struct bt_probe *, struct bt_filter *,
struct bt_stmt *);
struct bt_probe *bp_new(const char *, const char *, const char *, long);
struct bt_arg *ba_append(struct bt_arg *, struct bt_arg *);
struct bt_arg *ba_op(enum bt_argtype, struct bt_arg *, struct bt_arg *);
struct bt_stmt *bs_new(enum bt_action, struct bt_arg *, struct bt_var *);
struct bt_stmt *bs_append(struct bt_stmt *, struct bt_stmt *);
struct bt_var *bg_lookup(const char *);
struct bt_stmt *bg_store(const char *, struct bt_arg *);
struct bt_arg *bg_find(const char *);
struct bt_var *bg_get(const char *);
struct bt_arg *bi_find(struct bt_arg *, unsigned long);
struct bt_var *bl_lookup(const char *);
struct bt_stmt *bl_store(const char *, struct bt_arg *);
struct bt_arg *bl_find(const char *);
struct bt_arg *bm_find(const char *, struct bt_arg *);
struct bt_stmt *bm_insert(const char *, struct bt_arg *, struct bt_arg *);
struct bt_stmt *bm_op(enum bt_action, struct bt_arg *, struct bt_arg *);
struct bt_stmt *bh_inc(const char *, struct bt_arg *, struct bt_arg *);
const char *pbuf;
size_t plen;
size_t pindex;
int perrors = 0;
typedef struct {
union {
long number;
int i;
const char *string;
struct bt_probe *probe;
struct bt_filter *filter;
struct bt_stmt *stmt;
struct bt_arg *arg;
} v;
const char *filename;
int lineno;
int colno;
} yystype;
#define YYSTYPE yystype
static void yyerror(const char *, ...);
static int yylex(void);
static int pflag = 0;
static int beflag = 0;
%}
%token <v.i> ERROR ENDFILT
%token <v.i> OP_EQ OP_NE OP_LE OP_LT OP_GE OP_GT OP_LAND OP_LOR
%token <v.i> BUILTIN BEGIN ELSE END IF STR
%token <v.i> F_DELETE F_PRINT
%token <v.i> MFUNC FUNC0 FUNC1 FUNCN OP1 OP2 OP4 MOP0 MOP1
%token <v.string> STRING CSTRING GVAR LVAR
%token <v.arg> PVAR PNUM
%token <v.number> NUMBER
%type <v.i> beginend
%type <v.probe> plist probe pname
%type <v.filter> filter
%type <v.stmt> action stmt stmtblck stmtlist block
%type <v.arg> vargs mentry mpat pargs
%type <v.arg> expr term fterm variable factor func
%%
grammar :
| grammar '\n'
| grammar rule
| grammar error
;
rule : plist filter action { br_new($1, $2, $3); beflag = 0; }
;
beginend: BEGIN | END ;
plist : plist ',' probe { $$ = bp_append($1, $3); }
| probe
;
probe : { pflag = 1; } pname { $$ = $2; pflag = 0; }
| { beflag = 1; } beginend { $$ = bp_new(NULL, NULL, NULL, $2); }
;
pname : STRING ':' STRING ':' STRING { $$ = bp_new($1, $3, $5, 0); }
| STRING ':' STRING ':' NUMBER { $$ = bp_new($1, $3, NULL, $5); }
;
mentry : GVAR '[' vargs ']' { $$ = bm_find($1, $3); }
;
mpat : MOP0 '(' ')' { $$ = ba_new(NULL, $1); }
| MOP1 '(' expr ')' { $$ = ba_new($3, $1); }
| expr
;
filter : { $$ = NULL; }
| '/' expr ENDFILT { $$ = bc_new(NULL, B_AT_OP_NE, $2); }
;
expr : expr OP_LAND term { $$ = ba_op(B_AT_OP_LAND, $1, $3); }
| expr OP_LOR term { $$ = ba_op(B_AT_OP_LOR, $1, $3); }
| term
;
term : term OP_EQ fterm { $$ = ba_op(B_AT_OP_EQ, $1, $3); }
| term OP_NE fterm { $$ = ba_op(B_AT_OP_NE, $1, $3); }
| term OP_LE fterm { $$ = ba_op(B_AT_OP_LE, $1, $3); }
| term OP_LT fterm { $$ = ba_op(B_AT_OP_LT, $1, $3); }
| term OP_GE fterm { $$ = ba_op(B_AT_OP_GE, $1, $3); }
| term OP_GT fterm { $$ = ba_op(B_AT_OP_GT, $1, $3); }
| term '+' fterm { $$ = ba_op(B_AT_OP_PLUS, $1, $3); }
| term '-' fterm { $$ = ba_op(B_AT_OP_MINUS, $1, $3); }
| term '&' fterm { $$ = ba_op(B_AT_OP_BAND, $1, $3); }
| term '^' fterm { $$ = ba_op(B_AT_OP_XOR, $1, $3); }
| term '|' fterm { $$ = ba_op(B_AT_OP_BOR, $1, $3); }
| fterm
;
fterm : fterm '*' factor { $$ = ba_op(B_AT_OP_MULT, $1, $3); }
| fterm '/' factor { $$ = ba_op(B_AT_OP_DIVIDE, $1, $3); }
| fterm '%' factor { $$ = ba_op(B_AT_OP_MODULO, $1, $3); }
| factor
;
variable: LVAR { $$ = bl_find($1); }
| GVAR { $$ = bg_find($1); }
| variable '.' NUMBER { $$ = bi_find($1, $3); }
;
factor : '(' expr ')' { $$ = $2; }
| '(' vargs ',' expr ')'{ $$ = ba_new(ba_append($2, $4), B_AT_TUPLE); }
| NUMBER { $$ = ba_new($1, B_AT_LONG); }
| BUILTIN { $$ = ba_new(NULL, $1); }
| CSTRING { $$ = ba_new($1, B_AT_STR); }
| PVAR
| PNUM
| variable
| mentry
| func
;
func : STR '(' PVAR ')' { $$ = ba_new($3, B_AT_FN_STR); }
| STR '(' PVAR ',' expr ')' { $$ = ba_op(B_AT_FN_STR, $3, $5); }
;
vargs : expr
| vargs ',' expr { $$ = ba_append($1, $3); }
;
pargs : expr
| GVAR ',' expr { $$ = ba_append(bg_find($1), $3); }
;
NL :
| '\n'
;
stmt : ';' NL { $$ = NULL; }
| GVAR '=' expr { $$ = bg_store($1, $3); }
| LVAR '=' expr { $$ = bl_store($1, $3); }
| GVAR '[' vargs ']' '=' mpat { $$ = bm_insert($1, $3, $6); }
| FUNCN '(' vargs ')' { $$ = bs_new($1, $3, NULL); }
| FUNC1 '(' expr ')' { $$ = bs_new($1, $3, NULL); }
| MFUNC '(' variable ')' { $$ = bs_new($1, $3, NULL); }
| FUNC0 '(' ')' { $$ = bs_new($1, NULL, NULL); }
| F_DELETE '(' mentry ')' { $$ = bm_op($1, $3, NULL); }
| F_PRINT '(' pargs ')' { $$ = bs_new($1, $3, NULL); }
| GVAR '=' OP1 '(' expr ')' { $$ = bh_inc($1, $5, NULL); }
| GVAR '=' OP4 '(' expr ',' vargs ')' { $$ = bh_inc($1, $5, $7); }
;
stmtblck: IF '(' expr ')' block { $$ = bt_new($3, $5, NULL); }
| IF '(' expr ')' block ELSE block { $$ = bt_new($3, $5, $7); }
| IF '(' expr ')' block ELSE stmtblck { $$ = bt_new($3, $5, $7); }
;
stmtlist: stmtlist stmtblck { $$ = bs_append($1, $2); }
| stmtlist stmt { $$ = bs_append($1, $2); }
| stmtblck
| stmt
;
block : action
| stmt ';'
;
action : '{' stmtlist '}' { $$ = $2; }
| '{' '}' { $$ = NULL; }
;
%%
struct bt_arg*
get_varg(int index)
{
extern int nargs;
extern char **vargs;
const char *errstr = NULL;
long val;
if (1 <= index && index <= nargs) {
val = (long)strtonum(vargs[index-1], LONG_MIN, LONG_MAX,
&errstr);
if (errstr == NULL)
return ba_new(val, B_AT_LONG);
return ba_new(vargs[index-1], B_AT_STR);
}
return ba_new(0L, B_AT_NIL);
}
struct bt_arg*
get_nargs(void)
{
extern int nargs;
return ba_new((long) nargs, B_AT_LONG);
}
struct bt_rule *
br_new(struct bt_probe *probe, struct bt_filter *filter, struct bt_stmt *head)
{
struct bt_rule *br;
br = calloc(1, sizeof(*br));
if (br == NULL)
err(1, "bt_rule: calloc");
SLIST_FIRST(&br->br_probes) = probe;
br->br_filter = filter;
SLIST_FIRST(&br->br_action) = head;
SLIST_FIRST(&br->br_variables) = SLIST_FIRST(&l_variables);
SLIST_INIT(&l_variables);
do {
if (probe->bp_type != B_PT_PROBE)
continue;
g_nprobes++;
} while ((probe = SLIST_NEXT(probe, bp_next)) != NULL);
TAILQ_INSERT_TAIL(&g_rules, br, br_next);
return br;
}
struct bt_filter *
bc_new(struct bt_arg *term, enum bt_argtype op, struct bt_arg *ba)
{
struct bt_filter *bf;
bf = calloc(1, sizeof(*bf));
if (bf == NULL)
err(1, "bt_filter: calloc");
bf->bf_condition = bs_new(B_AC_TEST, ba_op(op, term, ba), NULL);
return bf;
}
struct bt_stmt *
bt_new(struct bt_arg *ba, struct bt_stmt *condbs, struct bt_stmt *elsebs)
{
struct bt_arg *bop;
struct bt_cond *bc;
bop = ba_op(B_AT_OP_NE, NULL, ba);
bc = calloc(1, sizeof(*bc));
if (bc == NULL)
err(1, "bt_cond: calloc");
bc->bc_condbs = condbs;
bc->bc_elsebs = elsebs;
return bs_new(B_AC_TEST, bop, (struct bt_var *)bc);
}
static uint64_t
bp_unit_to_nsec(const char *unit, long value)
{
static const struct {
const char *name;
enum { UNIT_HZ, UNIT_US, UNIT_MS, UNIT_S } id;
long long max;
} units[] = {
{ .name = "hz", .id = UNIT_HZ, .max = 1000000LL },
{ .name = "us", .id = UNIT_US, .max = LLONG_MAX / 1000 },
{ .name = "ms", .id = UNIT_MS, .max = LLONG_MAX / 1000000 },
{ .name = "s", .id = UNIT_S, .max = LLONG_MAX / 1000000000 },
};
size_t i;
for (i = 0; i < nitems(units); i++) {
if (strcmp(units[i].name, unit) == 0) {
if (value < 1)
yyerror("Number is invalid: %ld", value);
if (value > units[i].max)
yyerror("Number is too large: %ld", value);
switch (units[i].id) {
case UNIT_HZ:
return (1000000000LLU / value);
case UNIT_US:
return (value * 1000LLU);
case UNIT_MS:
return (value * 1000000LLU);
case UNIT_S:
return (value * 1000000000LLU);
}
}
}
yyerror("Invalid unit: %s", unit);
return 0;
}
struct bt_probe *
bp_new(const char *prov, const char *func, const char *name, long number)
{
struct bt_probe *bp;
enum bt_ptype ptype;
if (prov == NULL && func == NULL && name == NULL)
ptype = number;
else
ptype = B_PT_PROBE;
bp = calloc(1, sizeof(*bp));
if (bp == NULL)
err(1, "bt_probe: calloc");
bp->bp_prov = prov;
bp->bp_func = func;
bp->bp_name = name;
if (ptype == B_PT_PROBE && name == NULL)
bp->bp_nsecs = bp_unit_to_nsec(func, number);
bp->bp_type = ptype;
return bp;
}
struct bt_probe *
bp_append(struct bt_probe *bp0, struct bt_probe *bp1)
{
struct bt_probe *bp = bp0;
assert(bp1 != NULL);
if (bp0 == NULL)
return bp1;
while (SLIST_NEXT(bp, bp_next) != NULL)
bp = SLIST_NEXT(bp, bp_next);
SLIST_INSERT_AFTER(bp, bp1, bp_next);
return bp0;
}
struct bt_arg *
ba_new0(void *val, enum bt_argtype type)
{
struct bt_arg *ba;
ba = calloc(1, sizeof(*ba));
if (ba == NULL)
err(1, "bt_arg: calloc");
ba->ba_value = val;
ba->ba_type = type;
return ba;
}
struct bt_arg *
ba_append(struct bt_arg *da0, struct bt_arg *da1)
{
struct bt_arg *ba = da0;
assert(da1 != NULL);
if (da0 == NULL)
return da1;
while (SLIST_NEXT(ba, ba_next) != NULL)
ba = SLIST_NEXT(ba, ba_next);
SLIST_INSERT_AFTER(ba, da1, ba_next);
return da0;
}
struct bt_arg *
ba_op(enum bt_argtype op, struct bt_arg *da0, struct bt_arg *da1)
{
return ba_new(ba_append(da0, da1), op);
}
struct bt_stmt *
bs_new(enum bt_action act, struct bt_arg *head, struct bt_var *var)
{
struct bt_stmt *bs;
bs = calloc(1, sizeof(*bs));
if (bs == NULL)
err(1, "bt_stmt: calloc");
bs->bs_act = act;
bs->bs_var = var;
SLIST_FIRST(&bs->bs_args) = head;
return bs;
}
struct bt_stmt *
bs_append(struct bt_stmt *ds0, struct bt_stmt *ds1)
{
struct bt_stmt *bs = ds0;
if (ds0 == NULL)
return ds1;
if (ds1 == NULL)
return ds0;
while (SLIST_NEXT(bs, bs_next) != NULL)
bs = SLIST_NEXT(bs, bs_next);
SLIST_INSERT_AFTER(bs, ds1, bs_next);
return ds0;
}
const char *
bv_name(struct bt_var *bv)
{
if (strncmp(bv->bv_name, UNNAMED_MAP, strlen(UNNAMED_MAP)) == 0)
return "";
return bv->bv_name;
}
struct bt_var *
bv_new(const char *vname)
{
struct bt_var *bv;
bv = calloc(1, sizeof(*bv));
if (bv == NULL)
err(1, "bt_var: calloc");
bv->bv_name = vname;
return bv;
}
struct bt_var *
bg_lookup(const char *vname)
{
struct bt_var *bv;
SLIST_FOREACH(bv, &g_variables, bv_next) {
if (strcmp(vname, bv->bv_name) == 0)
break;
}
return bv;
}
struct bt_var *
bg_get(const char *vname)
{
struct bt_var *bv;
bv = bg_lookup(vname);
if (bv == NULL) {
bv = bv_new(vname);
SLIST_INSERT_HEAD(&g_variables, bv, bv_next);
}
return bv;
}
struct bt_arg *
bg_find(const char *vname)
{
return ba_new(bg_get(vname), B_AT_VAR);
}
struct bt_stmt *
bg_store(const char *vname, struct bt_arg *vval)
{
return bs_new(B_AC_STORE, vval, bg_get(vname));
}
struct bt_var *
bl_lookup(const char *vname)
{
struct bt_var *bv;
SLIST_FOREACH(bv, &l_variables, bv_next) {
if (strcmp(vname, bv->bv_name) == 0)
break;
}
return bv;
}
struct bt_arg *
bl_find(const char *vname)
{
struct bt_var *bv;
bv = bl_lookup(vname);
if (bv == NULL) {
bv = bv_new(vname);
SLIST_INSERT_HEAD(&l_variables, bv, bv_next);
}
return ba_new(bv, B_AT_VAR);
}
struct bt_stmt *
bl_store(const char *vname, struct bt_arg *vval)
{
struct bt_var *bv;
bv = bl_lookup(vname);
if (bv == NULL) {
bv = bv_new(vname);
SLIST_INSERT_HEAD(&l_variables, bv, bv_next);
}
return bs_new(B_AC_STORE, vval, bv);
}
struct bt_arg *
bi_find(struct bt_arg *ba, unsigned long index)
{
struct bt_var *bv = ba->ba_value;
ba = ba_new(bv, B_AT_TMEMBER);
ba->ba_key = (void *)index;
return ba;
}
struct bt_stmt *
bm_op(enum bt_action mact, struct bt_arg *ba, struct bt_arg *mval)
{
return bs_new(mact, ba, (struct bt_var *)mval);
}
struct bt_stmt *
bm_insert(const char *mname, struct bt_arg *mkey, struct bt_arg *mval)
{
struct bt_arg *ba;
if (mkey->ba_type == B_AT_TUPLE)
yyerror("tuple cannot be used as map key");
ba = ba_new(bg_get(mname), B_AT_MAP);
ba->ba_key = mkey;
return bs_new(B_AC_INSERT, ba, (struct bt_var *)mval);
}
struct bt_arg *
bm_find(const char *vname, struct bt_arg *mkey)
{
struct bt_arg *ba;
ba = ba_new(bg_get(vname), B_AT_MAP);
ba->ba_key = mkey;
return ba;
}
struct bt_stmt *
bh_inc(const char *hname, struct bt_arg *hval, struct bt_arg *hrange)
{
struct bt_arg *ba;
if (hrange == NULL) {
} else {
long min = 0, max;
int count = 0;
for (ba = hrange; ba != NULL; ba = SLIST_NEXT(ba, ba_next)) {
if (++count > 3)
yyerror("too many arguments");
if (ba->ba_type != B_AT_LONG)
yyerror("type invalid");
switch (count) {
case 1:
min = (long)ba->ba_value;
if (min >= 0)
break;
yyerror("negative minimum");
case 2:
max = (long)ba->ba_value;
if (max > min)
break;
yyerror("maximum smaller than minimum (%d < %d)",
max, min);
case 3:
break;
default:
assert(0);
}
}
if (count < 3)
yyerror("%d missing arguments", 3 - count);
}
ba = ba_new(bg_get(hname), B_AT_HIST);
ba->ba_key = hrange;
return bs_new(B_AC_BUCKETIZE, ba, (struct bt_var *)hval);
}
struct keyword {
const char *word;
int token;
int type;
};
int
kw_cmp(const void *str, const void *xkw)
{
return (strcmp(str, ((const struct keyword *)xkw)->word));
}
struct keyword *
lookup(char *s)
{
static const struct keyword kws[] = {
{ "BEGIN", BEGIN, B_PT_BEGIN },
{ "END", END, B_PT_END },
{ "arg0", BUILTIN, B_AT_BI_ARG0 },
{ "arg1", BUILTIN, B_AT_BI_ARG1 },
{ "arg2", BUILTIN, B_AT_BI_ARG2 },
{ "arg3", BUILTIN, B_AT_BI_ARG3 },
{ "arg4", BUILTIN, B_AT_BI_ARG4 },
{ "arg5", BUILTIN, B_AT_BI_ARG5 },
{ "arg6", BUILTIN, B_AT_BI_ARG6 },
{ "arg7", BUILTIN, B_AT_BI_ARG7 },
{ "arg8", BUILTIN, B_AT_BI_ARG8 },
{ "arg9", BUILTIN, B_AT_BI_ARG9 },
{ "clear", MFUNC, B_AC_CLEAR },
{ "comm", BUILTIN, B_AT_BI_COMM },
{ "count", MOP0, B_AT_MF_COUNT },
{ "cpu", BUILTIN, B_AT_BI_CPU },
{ "delete", F_DELETE, B_AC_DELETE },
{ "else", ELSE, 0 },
{ "exit", FUNC0, B_AC_EXIT },
{ "hist", OP1, 0 },
{ "if", IF, 0 },
{ "kstack", BUILTIN, B_AT_BI_KSTACK },
{ "lhist", OP4, 0 },
{ "max", MOP1, B_AT_MF_MAX },
{ "min", MOP1, B_AT_MF_MIN },
{ "nsecs", BUILTIN, B_AT_BI_NSECS },
{ "pid", BUILTIN, B_AT_BI_PID },
{ "print", F_PRINT, B_AC_PRINT },
{ "printf", FUNCN, B_AC_PRINTF },
{ "probe", BUILTIN, B_AT_BI_PROBE },
{ "retval", BUILTIN, B_AT_BI_RETVAL },
{ "str", STR, B_AT_FN_STR },
{ "sum", MOP1, B_AT_MF_SUM },
{ "tid", BUILTIN, B_AT_BI_TID },
{ "time", FUNC1, B_AC_TIME },
{ "ustack", BUILTIN, B_AT_BI_USTACK },
{ "zero", MFUNC, B_AC_ZERO },
};
return bsearch(s, kws, nitems(kws), sizeof(kws[0]), kw_cmp);
}
int
peek(void)
{
if (pbuf != NULL) {
if (pindex < plen)
return pbuf[pindex];
}
return EOF;
}
int
lgetc(void)
{
if (pbuf != NULL) {
if (pindex < plen) {
yylval.colno++;
return pbuf[pindex++];
}
}
return EOF;
}
void
lungetc(void)
{
if (pbuf != NULL && pindex > 0) {
yylval.colno--;
pindex--;
}
}
static inline int
allowed_to_end_number(int x)
{
return (isspace(x) || x == ')' || x == '/' || x == '{' || x == ';' ||
x == ']' || x == ',' || x == '=');
}
static inline int
allowed_in_string(int x)
{
return (isalnum(x) || x == '_');
}
static int
skip(void)
{
int c;
again:
for (c = lgetc(); isspace(c); c = lgetc()) {
if (c == '\n') {
yylval.lineno++;
yylval.colno = 0;
}
}
if ((c == '/' && peek() == '/') ||
(yylval.lineno == 1 && yylval.colno == 1 && c == '#' &&
peek() == '!')) {
for (c = lgetc(); c != EOF; c = lgetc()) {
if (c == '\n') {
yylval.lineno++;
yylval.colno = 0;
goto again;
}
}
}
if (c == '/' && peek() == '*') {
int pc;
for (pc = 0, c = lgetc(); c != EOF; c = lgetc()) {
if (pc == '*' && c == '/')
goto again;
else if (c == '\n')
yylval.lineno++;
pc = c;
}
}
return c;
}
int
yylex(void)
{
unsigned char buf[1024];
unsigned char *ebuf, *p, *str;
int c;
ebuf = buf + sizeof(buf);
p = buf;
again:
c = skip();
switch (c) {
case '!':
case '=':
if (peek() == '=') {
lgetc();
return (c == '=') ? OP_EQ : OP_NE;
}
return c;
case '<':
if (peek() == '=') {
lgetc();
return OP_LE;
}
return OP_LT;
case '>':
if (peek() == '=') {
lgetc();
return OP_GE;
}
return OP_GT;
case '&':
if (peek() == '&') {
lgetc();
return OP_LAND;
}
return c;
case '|':
if (peek() == '|') {
lgetc();
return OP_LOR;
}
return c;
case '/':
while (isspace(peek())) {
if (lgetc() == '\n') {
yylval.lineno++;
yylval.colno = 0;
}
}
if (peek() == '{' || peek() == '/' || peek() == '\n')
return ENDFILT;
case ',':
case '(':
case ')':
case '{':
case '}':
case ':':
case ';':
return c;
case '$':
c = lgetc();
if (c == '#') {
yylval.v.arg = get_nargs();
return PNUM;
} else if (isdigit(c)) {
do {
*p++ = c;
if (p == ebuf) {
yyerror("line too long");
return ERROR;
}
} while ((c = lgetc()) != EOF && isdigit(c));
lungetc();
*p = '\0';
if (c == EOF || allowed_to_end_number(c)) {
const char *errstr = NULL;
int num;
num = strtonum(buf, 1, INT_MAX, &errstr);
if (errstr) {
yyerror("'$%s' is %s", buf, errstr);
return ERROR;
}
yylval.v.arg = get_varg(num);
return PVAR;
}
} else if (isalpha(c)) {
do {
*p++ = c;
if (p == ebuf) {
yyerror("line too long");
return ERROR;
}
} while ((c = lgetc()) != EOF && allowed_in_string(c));
lungetc();
*p = '\0';
if ((yylval.v.string = strdup(buf)) == NULL)
err(1, "%s", __func__);
return LVAR;
}
yyerror("'$%s%c' is an invalid variable name", buf, c);
return ERROR;
break;
case '@':
c = lgetc();
if (isalpha(c)) {
do {
*p++ = c;
if (p == ebuf) {
yyerror("line too long");
return ERROR;
}
} while ((c = lgetc()) != EOF && allowed_in_string(c));
lungetc();
*p = '\0';
if ((yylval.v.string = strdup(buf)) == NULL)
err(1, "%s", __func__);
return GVAR;
} else if (allowed_to_end_number(c) || c == '[') {
lungetc();
*p = '\0';
yylval.v.string = UNNAMED_MAP;
return GVAR;
}
yyerror("'@%s%c' is an invalid variable name", buf, c);
return ERROR;
break;
case EOF:
return 0;
case '"':
while ((c = lgetc()) != EOF) {
if (c == '"') {
c = skip();
if (c == '"')
continue;
else
lungetc();
break;
}
if (c == '\\') {
c = lgetc();
switch (c) {
case '\\': c = '\\'; break;
case '\'': c = '\''; break;
case '"': c = '"'; break;
case 'a': c = '\a'; break;
case 'b': c = '\b'; break;
case 'e': c = 033; break;
case 'f': c = '\f'; break;
case 'n': c = '\n'; break;
case 'r': c = '\r'; break;
case 't': c = '\t'; break;
case 'v': c = '\v'; break;
default:
yyerror("'%c' unsupported escape", c);
return ERROR;
}
}
*p++ = c;
if (p == ebuf) {
yyerror("line too long");
return ERROR;
}
}
if (c == EOF) {
yyerror("\"%s\" invalid EOF", buf);
return ERROR;
}
*p++ = '\0';
if ((str = strdup(buf)) == NULL)
err(1, "%s", __func__);
yylval.v.string = str;
return CSTRING;
default:
break;
}
if (isdigit(c)) {
do {
*p++ = c;
if (p == ebuf) {
yyerror("line too long");
return ERROR;
}
} while ((c = lgetc()) != EOF &&
(isxdigit(c) || c == 'x' || c == 'X'));
lungetc();
if (c == EOF || allowed_to_end_number(c)) {
*p = '\0';
errno = 0;
yylval.v.number = strtol(buf, NULL, 0);
if (errno == ERANGE) {
yyerror("%sflow", (yylval.v.number == LONG_MIN)
? "under" : "over");
return ERROR;
}
return NUMBER;
} else {
while (p > buf + 1) {
--p;
lungetc();
}
c = *--p;
}
}
if (allowed_in_string(c)) {
struct keyword *kwp;
do {
*p++ = c;
if (p == ebuf) {
yyerror("line too long");
return ERROR;
}
} while ((c = lgetc()) != EOF && (allowed_in_string(c)));
lungetc();
*p = '\0';
kwp = lookup(buf);
if (kwp == NULL) {
if ((yylval.v.string = strdup(buf)) == NULL)
err(1, "%s", __func__);
return STRING;
}
if (pflag) {
yylval.v.string = kwp->word;
return STRING;
} else if (beflag) {
if (kwp->type >= B_AT_BI_ARG0 &&
kwp->type <= B_AT_BI_ARG9)
yyerror("the %s builtin cannot be used with "
"BEGIN or END probes", kwp->word);
}
yylval.v.i = kwp->type;
return kwp->token;
}
if (c == '\n') {
yylval.lineno++;
yylval.colno = 0;
}
if (c == EOF)
return 0;
return c;
}
void
pprint_syntax_error(void)
{
char line[BUFSIZ];
int c, indent = yylval.colno;
size_t i;
strlcpy(line, &pbuf[pindex - yylval.colno], sizeof(line));
for (i = 0; line[i] != '\0' && (c = line[i]) != '\n'; i++) {
if (c == '\t')
indent += (8 - 1);
fputc(c, stderr);
}
fprintf(stderr, "\n%*c\n", indent, '^');
}
void
yyerror(const char *fmt, ...)
{
const char *prefix;
va_list va;
prefix = (yylval.filename != NULL) ? yylval.filename : getprogname();
fprintf(stderr, "%s:%d:%d: ", prefix, yylval.lineno, yylval.colno);
va_start(va, fmt);
vfprintf(stderr, fmt, va);
va_end(va);
fprintf(stderr, ":\n");
pprint_syntax_error();
perrors++;
}
int
btparse(const char *str, size_t len, const char *filename, int debug)
{
if (debug > 0)
yydebug = 1;
pbuf = str;
plen = len;
pindex = 0;
yylval.filename = filename;
yylval.lineno = 1;
yyparse();
if (perrors)
return perrors;
assert(SLIST_EMPTY(&l_variables));
return 0;
}