From: phk Date: Fri, 21 Jul 2006 18:12:56 +0000 (+0000) Subject: Move things over to the correct "VCC" prefix. X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=62e6d5b3bb41364cb8e1972c79b7a1a731f1dc8d;p=varnish Move things over to the correct "VCC" prefix. Split some stuff into separate files while we're at it. git-svn-id: svn+ssh://projects.linpro.no/svn/varnish/trunk@544 d4fa192b-c00b-0410-8231-f00ffab90ce4 --- diff --git a/varnish-cache/lib/libvcl/Makefile.am b/varnish-cache/lib/libvcl/Makefile.am index 4abf53b4..ce34282b 100644 --- a/varnish-cache/lib/libvcl/Makefile.am +++ b/varnish-cache/lib/libvcl/Makefile.am @@ -5,8 +5,11 @@ INCLUDES = -I$(top_srcdir)/include lib_LTLIBRARIES = libvcl.la libvcl_la_SOURCES = \ - vcl_priv.h \ - vcl_token_defs.h \ + vcc_priv.h \ + vcc_compile.h \ + vcc_token_defs.h \ \ - vcl_compile.c \ - vcl_fixed_token.c + vcc_compile.c \ + vcc_fixed_token.c \ + vcc_obj.c \ + vcc_token.c diff --git a/varnish-cache/lib/libvcl/vcc_compile.c b/varnish-cache/lib/libvcl/vcc_compile.c new file mode 100644 index 00000000..a83d027e --- /dev/null +++ b/varnish-cache/lib/libvcl/vcc_compile.c @@ -0,0 +1,1529 @@ +/* + * $Id: vcl_compile.c 531 2006-07-20 22:08:43Z phk $ + */ + +/* + * XXX: + * generate interface structure + * + * XXX: + * Better error messages, throughout. + * >It also accured to me that we could link the errors to the error + * >documentation. + * > + * >Unreferenced function 'request_policy', first mention is + * > Line 8 Pos 4 + * > sub request_policy { + * > ----##############-- + * >Read more about this type of error: + * >http://varnish/doc/error.html#Unreferenced%20function + * > + * > + * > Unknown variable 'obj.bandwidth' + * > At: Line 88 Pos 12 + * > if (obj.bandwidth < 1 kb/h) { + * > ------------#############------------ + * >Read more about this type of error: + * >http://varnish/doc/error.html#Unknown%20variable + * + * XXX: + * Create proper tmp filenames for .h, .c and .o + * + * XXX: + * and all the rest... + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vcc_priv.h" +#include "vcc_compile.h" + +#include "libvcl.h" + +#define ERRCHK(tl) do { if ((tl)->err) return; } while (0) + +/*--------------------------------------------------------------------*/ + +static void Compound(struct tokenlist *tl); +static void Cond_0(struct tokenlist *tl); +static struct proc *AddProc(struct tokenlist *tl, struct token *t, int def); +static void AddCall(struct tokenlist *tl, struct token *t); +const char *vcc_default_vcl_b, *vcc_default_vcl_e; + +/*--------------------------------------------------------------------*/ + +#define ErrInternal(tl) vcc__ErrInternal(tl, __func__, __LINE__) + +#define Expect(a, b) vcc__Expect(a, b, __LINE__) +#define ExpectErr(a, b) do { vcc__Expect(a, b, __LINE__); ERRCHK(a);} while (0) + +#define L(tl, foo) do { \ + tl->indent += INDENT; \ + foo; \ + tl->indent -= INDENT; \ +} while (0) + +#define C(tl, sep) do { \ + Fc(tl, 1, "VRT_count(sp, %u)%s\n", ++tl->cnt, sep); \ + tl->t->cnt = tl->cnt; \ +} while (0) + +/*-------------------------------------------------------------------- + * Printf output to the two sbufs, possibly indented + */ + +static void +Fh(struct tokenlist *tl, int indent, const char *fmt, ...) +{ + va_list ap; + + if (indent) + sbuf_printf(tl->fh, "%*.*s", tl->indent, tl->indent, ""); + va_start(ap, fmt); + sbuf_vprintf(tl->fh, fmt, ap); + va_end(ap); +} + +static void +Fc(struct tokenlist *tl, int indent, const char *fmt, ...) +{ + va_list ap; + + if (indent) + sbuf_printf(tl->fc, "%*.*s", tl->indent, tl->indent, ""); + va_start(ap, fmt); + sbuf_vprintf(tl->fc, fmt, ap); + va_end(ap); +} + +/*--------------------------------------------------------------------*/ + +static char * +EncString(struct token *t) +{ + char *p, *q; + const char *r; + unsigned u; + + assert(t->tok == CSTR); + p = malloc(t->e - t->b); + assert(p != NULL); + q = p; + for (r = t->b + 1; r < t->e - 1; ) { + if (*r != '\\') { + *q++ = *r++; + continue; + } + switch (r[1]) { + case 'n': *q++ = '\n'; r += 2; break; + case 'r': *q++ = '\r'; r += 2; break; + case 'v': *q++ = '\v'; r += 2; break; + case 'f': *q++ = '\f'; r += 2; break; + case 't': *q++ = '\t'; r += 2; break; + case 'b': *q++ = '\b'; r += 2; break; + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + u = r[1] - '0'; + r += 2; + if (isdigit(r[0]) && (r[0] - '0') < 8) { + u <<= 3; + u |= r[0] - '0'; + r++; + if (isdigit(r[0]) && (r[0] - '0') < 8) { + u <<= 3; + u |= r[0] - '0'; + r++; + } + } + *q++ = u; + break; + default: + *q++ = r[1]; + r += 2; + break; + } + } + *q = '\0'; + return (p); +} + + +/*-------------------------------------------------------------------- + * Keep track of definitions and references + */ + +static struct ref * +FindRef(struct tokenlist *tl, struct token *t, enum ref_type type) +{ + struct ref *r; + + TAILQ_FOREACH(r, &tl->refs, list) { + if (r->type != type) + continue; + if (vcc_Teq(r->name, t)) + return (r); + } + r = calloc(sizeof *r, 1); + assert(r != NULL); + r->name = t; + r->type = type; + TAILQ_INSERT_TAIL(&tl->refs, r, list); + return (r); +} + +static int +FindRefStr(struct tokenlist *tl, const char *s, enum ref_type type) +{ + struct ref *r; + + TAILQ_FOREACH(r, &tl->refs, list) { + if (r->type != type) + continue; + if (vcc_IdIs(r->name, s)) + return (1); + } + return (0); +} + +static void +AddRef(struct tokenlist *tl, struct token *t, enum ref_type type) +{ + + FindRef(tl, t, type)->refcnt++; +} + +static void +AddRefStr(struct tokenlist *tl, const char *s, enum ref_type type) +{ + struct token *t; + + t = calloc(sizeof *t, 1); + t->b = s; + t->e = strchr(s, '\0'); + t->tok = METHOD; + AddRef(tl, t, type); +} + +static void +AddDef(struct tokenlist *tl, struct token *t, enum ref_type type) +{ + struct ref *r; + + r = FindRef(tl, t, type); + r->defcnt++; + r->name = t; +} + +/*-------------------------------------------------------------------- + * Recognize and convert units of time, return seconds. + */ + +static double +TimeUnit(struct tokenlist *tl) +{ + double sc = 1.0; + + assert(tl->t->tok == ID); + if (vcc_IdIs(tl->t, "ms")) + sc = 1e-3; + else if (vcc_IdIs(tl->t, "s")) + sc = 1.0; + else if (vcc_IdIs(tl->t, "m")) + sc = 60.0; + else if (vcc_IdIs(tl->t, "h")) + sc = 60.0 * 60.0; + else if (vcc_IdIs(tl->t, "d")) + sc = 60.0 * 60.0 * 24.0; + else { + sbuf_printf(tl->sb, "Unknown time unit "); + vcc_ErrToken(tl, tl->t); + sbuf_printf(tl->sb, ". Legal are 's', 'm', 'h' and 'd'\n"); + vcc_ErrWhere(tl, tl->t); + return (1.0); + } + vcc_NextToken(tl); + return (sc); +} + +/*-------------------------------------------------------------------- + * Recognize and convert units of size, return bytes. + */ + +static double +SizeUnit(struct tokenlist *tl) +{ + double sc = 1.0; + + assert(tl->t->tok == ID); + if (vcc_IdIs(tl->t, "b")) + sc = 1.0; + else if (vcc_IdIs(tl->t, "kb")) + sc = 1024.0; + else if (vcc_IdIs(tl->t, "mb") || vcc_IdIs(tl->t, "Mb")) + sc = 1024.0 * 1024.0; + else if (vcc_IdIs(tl->t, "gb") || vcc_IdIs(tl->t, "Gb")) + sc = 1024.0 * 1024.0 * 1024.0; + else { + sbuf_printf(tl->sb, "Unknown size unit "); + vcc_ErrToken(tl, tl->t); + sbuf_printf(tl->sb, ". Legal are 'kb', 'mb' and 'gb'\n"); + vcc_ErrWhere(tl, tl->t); + return (1.0); + } + vcc_NextToken(tl); + return (sc); +} + +/*-------------------------------------------------------------------- + * Recognize and convert units of rate as { space '/' time } + */ + +static double +RateUnit(struct tokenlist *tl) +{ + double sc = 1.0; + + assert(tl->t->tok == ID); + sc = SizeUnit(tl); + Expect(tl, '/'); + vcc_NextToken(tl); + sc /= TimeUnit(tl); + return (sc); +} + +/*-------------------------------------------------------------------- + * Recognize and convert { CNUM } to unsigned value + */ + +static unsigned +UintVal(struct tokenlist *tl) +{ + unsigned d = 0; + const char *p; + + Expect(tl, CNUM); + for (p = tl->t->b; p < tl->t->e; p++) { + d *= 10; + d += *p - '0'; + } + vcc_NextToken(tl); + return (d); +} + +/*-------------------------------------------------------------------- + * Recognize and convert { CNUM [ '.' [ CNUM ] ] } to double value + */ + +static double +DoubleVal(struct tokenlist *tl) +{ + double d = 0.0, e = 0.1; + const char *p; + + Expect(tl, CNUM); + for (p = tl->t->b; p < tl->t->e; p++) { + d *= 10; + d += *p - '0'; + } + vcc_NextToken(tl); + if (tl->t->tok != '.') + return (d); + vcc_NextToken(tl); + if (tl->t->tok != CNUM) + return (d); + for (p = tl->t->b; p < tl->t->e; p++) { + d += (*p - '0') * e; + e *= 0.1; + } + vcc_NextToken(tl); + return (d); +} + +/*--------------------------------------------------------------------*/ + +static unsigned +IpVal(struct tokenlist *tl) +{ + unsigned u, v; + struct token *t; + + t = tl->t; + u = UintVal(tl); + if (u < 256) { + v = u << 24; + Expect(tl, '.'); + vcc_NextToken(tl); + t = tl->t; + u = UintVal(tl); + if (u < 256) { + v |= u << 16; + Expect(tl, '.'); + vcc_NextToken(tl); + t = tl->t; + u = UintVal(tl); + if (u < 256) { + v |= u << 8; + Expect(tl, '.'); + vcc_NextToken(tl); + t = tl->t; + u = UintVal(tl); + if (u < 256) { + v |= u; + return (v); + } + } + } + } + sbuf_printf(tl->sb, "Illegal octet in IP number\n"); + vcc_ErrWhere(tl, t); + return (0); +} + +/*--------------------------------------------------------------------*/ + +static struct var * +HeaderVar(struct tokenlist *tl __unused, struct token *t, struct var *vh) +{ + char *p; + struct var *v; + int i; + + v = calloc(sizeof *v, 1); + assert(v != NULL); + i = t->e - t->b; + p = malloc(i + 1); + assert(p != NULL); + memcpy(p, t->b, i); + p[i] = '\0'; + v->name = p; + v->fmt = STRING; + asprintf(&p, "VRT_GetHdr(sp, \"\\%03o%s:\")", + strlen(v->name + vh->len) + 1, v->name + vh->len); + assert(p != NULL); + v->rname = p; + return (v); +} + +/*--------------------------------------------------------------------*/ + +static struct var * +FindVar(struct tokenlist *tl, struct token *t, struct var *vl) +{ + struct var *v; + + for (v = vl; v->name != NULL; v++) { + if (v->fmt == HEADER && t->e - t->b <= v->len) + continue; + if (v->fmt != HEADER && t->e - t->b != v->len) + continue; + if (memcmp(t->b, v->name, v->len)) + continue; + if (v->fmt != HEADER) + return (v); + return (HeaderVar(tl, t, v)); + } + sbuf_printf(tl->sb, "Unknown variable "); + vcc_ErrToken(tl, t); + sbuf_cat(tl->sb, "\nAt: "); + vcc_ErrWhere(tl, t); + return (NULL); +} + + +/*--------------------------------------------------------------------*/ + +static void +TimeVal(struct tokenlist *tl) +{ + double v, sc; + + v = DoubleVal(tl); + ExpectErr(tl, ID); + sc = TimeUnit(tl); + Fc(tl, 0, "(%g * %g)", v, sc); +} + +static void +SizeVal(struct tokenlist *tl) +{ + double v, sc; + + v = DoubleVal(tl); + ExpectErr(tl, ID); + sc = SizeUnit(tl); + Fc(tl, 0, "(%g * %g)", v, sc); +} + +static void +RateVal(struct tokenlist *tl) +{ + double v, sc; + + v = DoubleVal(tl); + ExpectErr(tl, ID); + sc = RateUnit(tl); + Fc(tl, 0, "(%g * %g)", v, sc); +} + +/*--------------------------------------------------------------------*/ + +static void +Cond_Ip(struct var *vp, struct tokenlist *tl) +{ + unsigned u; + + switch (tl->t->tok) { + case '~': + vcc_NextToken(tl); + ExpectErr(tl, ID); + AddRef(tl, tl->t, R_ACL); + Fc(tl, 1, "ip_match(%s, acl_%T)\n", vp->rname, tl->t); + vcc_NextToken(tl); + break; + case T_EQ: + case T_NEQ: + Fc(tl, 1, "%s %T ", vp->rname, tl->t); + vcc_NextToken(tl); + u = IpVal(tl); + Fc(tl, 0, "%uU /* %u.%u.%u.%u */\n", u, + (u >> 24) & 0xff, (u >> 16) & 0xff, + (u >> 8) & 0xff, (u) & 0xff); + break; + default: + sbuf_printf(tl->sb, "Illegal condition "); + vcc_ErrToken(tl, tl->t); + sbuf_printf(tl->sb, " on IP number variable\n"); + sbuf_printf(tl->sb, " only '==', '!=' and '~' are legal\n"); + vcc_ErrWhere(tl, tl->t); + break; + } +} + +static void +Cond_String(struct var *vp, struct tokenlist *tl) +{ + + switch (tl->t->tok) { + case '~': + Fc(tl, 1, "string_match(%s, ", vp->rname); + vcc_NextToken(tl); + ExpectErr(tl, CSTR); + Fc(tl, 0, "%T)\n", tl->t); + vcc_NextToken(tl); + break; + case T_EQ: + case T_NEQ: + Fc(tl, 1, "%sstrcmp(%s, ", + tl->t->tok == T_EQ ? "!" : "", vp->rname); + vcc_NextToken(tl); + ExpectErr(tl, CSTR); + Fc(tl, 0, "%T)\n", tl->t); + vcc_NextToken(tl); + break; + default: + Fc(tl, 1, "%s != (void*)0", vp->rname); + break; + } +} + +static void +Cond_Int(struct var *vp, struct tokenlist *tl) +{ + + Fc(tl, 1, "%s ", vp->rname); + switch (tl->t->tok) { + case T_EQ: + case T_NEQ: + case T_LEQ: + case T_GEQ: + case '>': + case '<': + Fc(tl, 0, "%T ", tl->t); + vcc_NextToken(tl); + switch(vp->fmt) { + case TIME: + TimeVal(tl); + break; + case INT: + ExpectErr(tl, CNUM); + Fc(tl, 0, "%T ", tl->t); + vcc_NextToken(tl); + break; + case SIZE: + SizeVal(tl); + break; + default: + sbuf_printf(tl->sb, + "No conditions available for variable '%s'\n", + vp->name); + vcc_ErrWhere(tl, tl->t); + return; + } + Fc(tl, 0, "\n"); + break; + default: + sbuf_printf(tl->sb, "Illegal condition "); + vcc_ErrToken(tl, tl->t); + sbuf_printf(tl->sb, " on integer variable\n"); + sbuf_printf(tl->sb, + " only '==', '!=', '<', '>', '<=' and '>=' are legal\n"); + vcc_ErrWhere(tl, tl->t); + break; + } +} + +static void +Cond_Bool(struct var *vp, struct tokenlist *tl) +{ + + Fc(tl, 1, "%s\n", vp->rname); +} + +static void +Cond_2(struct tokenlist *tl) +{ + struct var *vp; + + C(tl, ","); + if (tl->t->tok == '!') { + Fc(tl, 1, "!(\n"); + vcc_NextToken(tl); + } else { + Fc(tl, 1, "(\n"); + } + if (tl->t->tok == '(') { + vcc_NextToken(tl); + Cond_0(tl); + ExpectErr(tl, ')'); + vcc_NextToken(tl); + } else if (tl->t->tok == VAR) { + vp = FindVar(tl, tl->t, vcc_vars); + ERRCHK(tl); + assert(vp != NULL); + vcc_NextToken(tl); + switch (vp->fmt) { + case INT: L(tl, Cond_Int(vp, tl)); break; + case SIZE: L(tl, Cond_Int(vp, tl)); break; + case BOOL: L(tl, Cond_Bool(vp, tl)); break; + case IP: L(tl, Cond_Ip(vp, tl)); break; + case STRING: L(tl, Cond_String(vp, tl)); break; + case TIME: L(tl, Cond_Int(vp, tl)); break; + /* XXX backend == */ + default: + sbuf_printf(tl->sb, + "Variable '%s'" + " has no conditions that can be checked\n", + vp->name); + vcc_ErrWhere(tl, tl->t); + return; + } + } else { + sbuf_printf(tl->sb, + "Syntax error in condition, expected '(', '!' or" + " variable name, found "); + vcc_ErrToken(tl, tl->t); + sbuf_printf(tl->sb, "\n"); + vcc_ErrWhere(tl, tl->t); + return; + } + Fc(tl, 1, ")\n"); +} + +static void +Cond_1(struct tokenlist *tl) +{ + + Fc(tl, 1, "(\n"); + L(tl, Cond_2(tl)); + while (tl->t->tok == T_CAND) { + vcc_NextToken(tl); + Fc(tl, 1, ") && (\n"); + L(tl, Cond_2(tl)); + } + Fc(tl, 1, ")\n"); +} + +static void +Cond_0(struct tokenlist *tl) +{ + + Fc(tl, 1, "(\n"); + L(tl, Cond_1(tl)); + while (tl->t->tok == T_COR) { + vcc_NextToken(tl); + Fc(tl, 1, ") || (\n"); + L(tl, Cond_1(tl)); + } + Fc(tl, 1, ")\n"); +} + +static void +Conditional(struct tokenlist *tl) +{ + + ExpectErr(tl, '('); + vcc_NextToken(tl); + Fc(tl, 1, "(\n"); + L(tl, Cond_0(tl)); + ERRCHK(tl); + Fc(tl, 1, ")\n"); + ExpectErr(tl, ')'); + vcc_NextToken(tl); +} + +/*--------------------------------------------------------------------*/ + +static void +IfStmt(struct tokenlist *tl) +{ + + ExpectErr(tl, T_IF); + Fc(tl, 1, "if \n"); + vcc_NextToken(tl); + L(tl, Conditional(tl)); + ERRCHK(tl); + L(tl, Compound(tl)); + ERRCHK(tl); + while (1) { + switch (tl->t->tok) { + case T_ELSE: + vcc_NextToken(tl); + if (tl->t->tok != T_IF) { + Fc(tl, 1, "else \n"); + L(tl, Compound(tl)); + ERRCHK(tl); + return; + } + /* FALLTHROUGH */ + case T_ELSEIF: + case T_ELSIF: + Fc(tl, 1, "else if \n"); + vcc_NextToken(tl); + L(tl, Conditional(tl)); + ERRCHK(tl); + L(tl, Compound(tl)); + ERRCHK(tl); + break; + default: + C(tl, ";"); + return; + } + } +} + +/*--------------------------------------------------------------------*/ + +static void +Action(struct tokenlist *tl) +{ + unsigned a, u; + struct var *vp; + struct token *at; + + at = tl->t; + vcc_NextToken(tl); + switch (at->tok) { + case T_NO_NEW_CACHE: + Fc(tl, 1, "VCL_no_new_cache(sp);\n"); + return; + case T_NO_CACHE: + Fc(tl, 1, "VCL_no_cache(sp);\n"); + return; +#define VCL_RET_MAC(a,b,c) case T_##b: \ + Fc(tl, 1, "VRT_done(sp, VCL_RET_%s);\n", #b); \ + tl->curproc->returns |= VCL_RET_##b; \ + tl->curproc->returnt[c] = at; \ + return; +#include "vcl_returns.h" +#undef VCL_RET_MAC + case T_ERROR: + if (tl->t->tok == CNUM) + a = UintVal(tl); + else + a = 0; + Fc(tl, 1, "VRT_error(sp, %u, ", a); + if (tl->t->tok == CSTR) { + Fc(tl, 0, "%T);\n", tl->t); + vcc_NextToken(tl); + } else + Fc(tl, 0, "(const char *)0);\n"); + Fc(tl, 1, "VRT_done(sp, VCL_RET_ERROR);\n"); + return; + case T_SWITCH_CONFIG: + ExpectErr(tl, ID); + Fc(tl, 1, "VCL_switch_config(\"%T\");\n", tl->t); + vcc_NextToken(tl); + return; + case T_CALL: + ExpectErr(tl, ID); + AddCall(tl, tl->t); + AddRef(tl, tl->t, R_FUNC); + Fc(tl, 1, "if (VGC_function_%T(sp))\n", tl->t); + Fc(tl, 1, "\treturn (1);\n"); + vcc_NextToken(tl); + return; + case T_REWRITE: + ExpectErr(tl, CSTR); + Fc(tl, 1, "VCL_rewrite(%T", tl->t); + vcc_NextToken(tl); + ExpectErr(tl, CSTR); + Fc(tl, 0, ", %T);\n", tl->t); + vcc_NextToken(tl); + return; + case T_SET: + ExpectErr(tl, VAR); + vp = FindVar(tl, tl->t, vcc_vars); + ERRCHK(tl); + assert(vp != NULL); + Fc(tl, 1, "%s ", vp->rname); + vcc_NextToken(tl); + switch (vp->fmt) { + case INT: + case SIZE: + case RATE: + case TIME: + case FLOAT: + Fc(tl, 0, "%T ", tl->t); + a = tl->t->tok; + vcc_NextToken(tl); + if (a == T_MUL || a == T_DIV) + Fc(tl, 0, "%g", DoubleVal(tl)); + else if (vp->fmt == TIME) + TimeVal(tl); + else if (vp->fmt == SIZE) + SizeVal(tl); + else if (vp->fmt == RATE) + RateVal(tl); + else + Fc(tl, 0, "%g", DoubleVal(tl)); + Fc(tl, 0, ";\n"); + break; + case IP: + if (tl->t->tok == '=') { + vcc_NextToken(tl); + u = IpVal(tl); + Fc(tl, 0, "= %uU; /* %u.%u.%u.%u */\n", + u, + (u >> 24) & 0xff, + (u >> 16) & 0xff, + (u >> 8) & 0xff, + u & 0xff); + break; + } + sbuf_printf(tl->sb, "Illegal assignment operator "); + vcc_ErrToken(tl, tl->t); + sbuf_printf(tl->sb, + " only '=' is legal for IP numbers\n"); + vcc_ErrWhere(tl, tl->t); + return; + case BACKEND: + if (tl->t->tok == '=') { + vcc_NextToken(tl); + AddRef(tl, tl->t, R_BACKEND); + Fc(tl, 0, "= &VGC_backend_%T;\n", tl->t); + vcc_NextToken(tl); + break; + } + sbuf_printf(tl->sb, "Illegal assignment operator "); + vcc_ErrToken(tl, tl->t); + sbuf_printf(tl->sb, + " only '=' is legal for backend\n"); + vcc_ErrWhere(tl, tl->t); + return; + default: + sbuf_printf(tl->sb, + "Assignments not possible for '%s'\n", vp->name); + vcc_ErrWhere(tl, tl->t); + return; + } + return; + default: + sbuf_printf(tl->sb, "Expected action, 'if' or '}'\n"); + vcc_ErrWhere(tl, at); + return; + } +} + +/*--------------------------------------------------------------------*/ + +static void +Acl(struct tokenlist *tl) +{ + unsigned u, m; + + vcc_NextToken(tl); + + ExpectErr(tl, ID); + AddDef(tl, tl->t, R_ACL); + Fh(tl, 0, "static struct vcl_acl acl_%T[];\n", tl->t); + Fc(tl, 1, "static struct vcl_acl acl_%T[] = {\n", tl->t); + vcc_NextToken(tl); + + tl->indent += INDENT; + + ExpectErr(tl, '{'); + vcc_NextToken(tl); + + while (tl->t->tok == CNUM) { + u = IpVal(tl); + if (tl->t->tok == '/') { + vcc_NextToken(tl); + ExpectErr(tl, CNUM); + m = UintVal(tl); + } else + m = 32; + ExpectErr(tl, ';'); + vcc_NextToken(tl); + Fc(tl, 1, "{ %11uU, %3uU }, /* %u.%u.%u.%u/%u */\n", + u, m, + (u >> 24) & 0xff, (u >> 16) & 0xff, + (u >> 8) & 0xff, (u) & 0xff, m); + } + ExpectErr(tl, '}'); + Fc(tl, 1, "{ %11uU, %3uU }\n", 0, 0); + + tl->indent -= INDENT; + + Fc(tl, 1, "};\n\n"); + vcc_NextToken(tl); +} + +/*--------------------------------------------------------------------*/ + +static void +Compound(struct tokenlist *tl) +{ + + ExpectErr(tl, '{'); + Fc(tl, 1, "{\n"); + tl->indent += INDENT; + C(tl, ";"); + vcc_NextToken(tl); + while (1) { + ERRCHK(tl); + switch (tl->t->tok) { + case '{': + Compound(tl); + break; + case T_IF: + IfStmt(tl); + break; + case '}': + vcc_NextToken(tl); + tl->indent -= INDENT; + Fc(tl, 1, "}\n"); + return; + case EOI: + sbuf_printf(tl->sb, + "End of input while in compound statement\n"); + tl->err = 1; + return; + default: + Action(tl); + ERRCHK(tl); + ExpectErr(tl, ';'); + vcc_NextToken(tl); + break; + } + } +} + +/*--------------------------------------------------------------------*/ + +static const char * +CheckHostPort(const char *host, const char *port) +{ + struct addrinfo *res, hint; + int error; + + memset(&hint, 0, sizeof hint); + hint.ai_family = PF_UNSPEC; + hint.ai_socktype = SOCK_STREAM; + error = getaddrinfo(host, port, &hint, &res); + if (error) + return (gai_strerror(error)); + freeaddrinfo(res); + return (NULL); +} + +static void +Backend(struct tokenlist *tl) +{ + struct var *vp; + struct token *t_be = NULL; + struct token *t_host = NULL; + struct token *t_port = NULL; + char *host = NULL; + char *port = NULL; + const char *ep; + + vcc_NextToken(tl); + ExpectErr(tl, ID); + t_be = tl->t; + AddDef(tl, tl->t, R_BACKEND); + if (tl->nbackend == 0) + AddRef(tl, tl->t, R_BACKEND); + Fh(tl, 1, "#define VGC_backend_%T (VCL_conf.backend[%d])\n", + tl->t, tl->nbackend); + Fc(tl, 0, "static void\n"); + Fc(tl, 1, "VGC_init_backend_%T (void)\n", tl->t); + Fc(tl, 1, "{\n"); + Fc(tl, 1, "\tstruct backend *backend = VGC_backend_%T;\n", tl->t); + Fc(tl, 1, "\tconst char *p;\n"); + Fc(tl, 1, "\n"); + Fc(tl, 1, "\tVRT_set_backend_name(backend, \"%T\");\n", tl->t); + vcc_NextToken(tl); + ExpectErr(tl, '{'); + vcc_NextToken(tl); + while (1) { + if (tl->t->tok == '}') + break; + ExpectErr(tl, T_SET); + vcc_NextToken(tl); + ExpectErr(tl, VAR); + vp = FindVar(tl, tl->t, vcc_be_vars); + ERRCHK(tl); + assert(vp != NULL); + vcc_NextToken(tl); + ExpectErr(tl, '='); + vcc_NextToken(tl); + switch (vp->fmt) { + case HOSTNAME: + ExpectErr(tl, CSTR); + t_host = tl->t; + Fc(tl, 1, "\tp = %T;\n", tl->t); + Fc(tl, 1, "\t"); + Fc(tl, 0, vp->lname, "p"); + Fc(tl, 0, ";\n"); + vcc_NextToken(tl); + break; + case PORTNAME: + ExpectErr(tl, CSTR); + t_port = tl->t; + Fc(tl, 1, "\tp = %T;\n", tl->t); + Fc(tl, 1, "\t"); + Fc(tl, 0, vp->lname, "p"); + Fc(tl, 0, ";\n"); + vcc_NextToken(tl); + break; + default: + sbuf_printf(tl->sb, + "Assignments not possible for '%s'\n", vp->name); + vcc_ErrWhere(tl, tl->t); + return; + } + ExpectErr(tl, ';'); + vcc_NextToken(tl); + } + ExpectErr(tl, '}'); + if (t_host == NULL) { + sbuf_printf(tl->sb, "Backend '%T' has no hostname\n", t_be); + vcc_ErrWhere(tl, tl->t); + return; + } + host = EncString(t_host); + ep = CheckHostPort(host, "80"); + if (ep != NULL) { + sbuf_printf(tl->sb, "Backend '%T': %s\n", t_be, ep); + vcc_ErrWhere(tl, t_host); + return; + } + if (t_port != NULL) { + port = EncString(t_port); + ep = CheckHostPort(host, port); + if (ep != NULL) { + sbuf_printf(tl->sb, "Backend '%T': %s\n", t_be, ep); + vcc_ErrWhere(tl, t_port); + return; + } + } + + vcc_NextToken(tl); + Fc(tl, 1, "}\n"); + Fc(tl, 0, "\n"); + tl->nbackend++; +} + +/*--------------------------------------------------------------------*/ + +static void +Function(struct tokenlist *tl) +{ + + vcc_NextToken(tl); + ExpectErr(tl, ID); + tl->curproc = AddProc(tl, tl->t, 1); + tl->curproc->exists++; + AddDef(tl, tl->t, R_FUNC); + Fh(tl, 0, "static int VGC_function_%T (struct sess *sp);\n", tl->t); + Fc(tl, 1, "static int\n"); + Fc(tl, 1, "VGC_function_%T (struct sess *sp)\n", tl->t); + vcc_NextToken(tl); + L(tl, Compound(tl)); + Fc(tl, 0, "\n"); +} + +/*-------------------------------------------------------------------- + * Top level of parser, recognize: + * Function definitions + * Backend definitions + * End of input + */ + +static void +Parse(struct tokenlist *tl) +{ + + while (tl->t->tok != EOI) { + ERRCHK(tl); + switch (tl->t->tok) { + case T_ACL: + Acl(tl); + break; + case T_SUB: + Function(tl); + break; + case T_BACKEND: + Backend(tl); + break; + case EOI: + break; + default: + sbuf_printf(tl->sb, + "Expected 'acl', 'sub' or 'backend', found "); + vcc_ErrToken(tl, tl->t); + sbuf_printf(tl->sb, " at\n"); + vcc_ErrWhere(tl, tl->t); + return; + } + } +} + +/*-------------------------------------------------------------------- + * Consistency check + */ + +static struct proc * +AddProc(struct tokenlist *tl, struct token *t, int def) +{ + struct proc *p; + + TAILQ_FOREACH(p, &tl->procs, list) { + if (!vcc_Teq(p->name, t)) + continue; + if (def) + p->name = t; + return (p); + } + p = calloc(sizeof *p, 1); + assert(p != NULL); + p->name = t; + TAILQ_INIT(&p->calls); + TAILQ_INSERT_TAIL(&tl->procs, p, list); + return (p); +} + +static void +AddCall(struct tokenlist *tl, struct token *t) +{ + struct proccall *pc; + struct proc *p; + + p = AddProc(tl, t, 0); + TAILQ_FOREACH(pc, &tl->curproc->calls, list) { + if (pc->p == p) + return; + } + pc = calloc(sizeof *pc, 1); + assert(pc != NULL); + pc->p = p; + pc->t = t; + TAILQ_INSERT_TAIL(&tl->curproc->calls, pc, list); +} + +static int +Consist_Decend(struct tokenlist *tl, struct proc *p, unsigned returns) +{ + unsigned u; + struct proccall *pc; + + if (!p->exists) { + sbuf_printf(tl->sb, "Function %T does not exist\n", p->name); + return (1); + } + if (p->active) { + sbuf_printf(tl->sb, "Function recurses on\n"); + vcc_ErrWhere(tl, p->name); + return (1); + } + u = p->returns & ~returns; + if (u) { +#define VCL_RET_MAC(a, b, c) \ + if (u & VCL_RET_##b) { \ + sbuf_printf(tl->sb, "Illegal return for method\n"); \ + vcc_ErrWhere(tl, p->returnt[c]); \ + } +#include "vcl_returns.h" +#undef VCL_RET_MAC + sbuf_printf(tl->sb, "In function\n"); + vcc_ErrWhere(tl, p->name); + return (1); + } + p->active = 1; + TAILQ_FOREACH(pc, &p->calls, list) { + if (Consist_Decend(tl, pc->p, returns)) { + sbuf_printf(tl->sb, "\nCalled from\n"); + vcc_ErrWhere(tl, p->name); + sbuf_printf(tl->sb, "at\n"); + vcc_ErrWhere(tl, pc->t); + return (1); + } + } + p->active = 0; + p->called++; + return (0); +} + +static int +Consistency(struct tokenlist *tl) +{ + struct proc *p; + struct method *m; + + TAILQ_FOREACH(p, &tl->procs, list) { + for(m = method_tab; m->name != NULL; m++) { + if (vcc_IdIs(p->name, m->defname)) + p->called = 1; + if (vcc_IdIs(p->name, m->name)) + break; + } + if (m->name == NULL) + continue; + if (Consist_Decend(tl, p, m->returns)) { + sbuf_printf(tl->sb, + "\nwhich is a %s method\n", m->name); + return (1); + } + } + TAILQ_FOREACH(p, &tl->procs, list) { + if (p->called) + continue; + sbuf_printf(tl->sb, "Function unused\n"); + vcc_ErrWhere(tl, p->name); + return (1); + } + return (0); +} + +/*--------------------------------------------------------------------*/ + +static int +CheckRefs(struct tokenlist *tl) +{ + struct ref *r; + const char *type; + int nerr = 0; + + TAILQ_FOREACH(r, &tl->refs, list) { + if (r->defcnt != 0 && r->refcnt != 0) + continue; + nerr++; + + switch(r->type) { + case R_FUNC: + type = "function"; + break; + case R_ACL: + type = "acl"; + break; + case R_BACKEND: + type = "backend"; + break; + default: + ErrInternal(tl); + sbuf_printf(tl->sb, "Ref "); + vcc_ErrToken(tl, r->name); + sbuf_printf(tl->sb, " has unknown type %d\n", + r->type); + continue; + } + if (r->defcnt == 0 && r->name->tok == METHOD) { + sbuf_printf(tl->sb, + "No definition for method %T\n", r->name); + continue; + } + + if (r->defcnt == 0) { + sbuf_printf(tl->sb, + "Undefined %s %T, first reference:\n", + type, r->name); + vcc_ErrWhere(tl, r->name); + continue; + } + + sbuf_printf(tl->sb, "Unused %s %T, defined:\n", type, r->name); + vcc_ErrWhere(tl, r->name); + } + return (nerr); +} + +/*--------------------------------------------------------------------*/ + +static void +LocTable(struct tokenlist *tl) +{ + struct token *t; + unsigned lin, pos; + const char *p; + + Fh(tl, 0, "#define VGC_NREFS %u\n", tl->cnt + 1); + Fh(tl, 0, "static struct vrt_ref VGC_ref[VGC_NREFS];\n"); + Fc(tl, 0, "static struct vrt_ref VGC_ref[VGC_NREFS] = {\n"); + lin = 1; + pos = 0; + p = tl->b; + TAILQ_FOREACH(t, &tl->tokens, list) { + if (t->cnt == 0) + continue; + for (;p < t->b; p++) { + if (*p == '\n') { + lin++; + pos = 0; + } else if (*p == '\t') { + pos &= ~7; + pos += 8; + } else + pos++; + + } + Fc(tl, 0, " [%3u] = { %4u, %3u, 0, \"%T\" },\n", + t->cnt, lin, pos + 1, t); + } + Fc(tl, 0, "};\n"); +} + + +/*--------------------------------------------------------------------*/ + +static void +EmitInitFunc(struct tokenlist *tl) +{ + struct ref *r; + + Fc(tl, 0, "\nstatic void\nVGC_Init(void)\n{\n\n"); + Fc(tl, 0, "\tVRT_alloc_backends(&VCL_conf);\n"); + + TAILQ_FOREACH(r, &tl->refs, list) { + switch(r->type) { + case R_FUNC: + break; + case R_ACL: + break; + case R_BACKEND: + Fc(tl, 0, "\tVGC_init_backend_%T();\n", r->name); + break; + } + } + Fc(tl, 0, "}\n"); +} + +/*--------------------------------------------------------------------*/ + +static void +EmitStruct(struct tokenlist *tl) +{ + + Fc(tl, 0, "\nstruct VCL_conf VCL_conf = {\n"); + Fc(tl, 0, "\t.magic = VCL_CONF_MAGIC,\n"); + Fc(tl, 0, "\t.init_func = VGC_Init,\n"); + Fc(tl, 0, "\t.nbackend = %d,\n", tl->nbackend); + Fc(tl, 0, "\t.ref = VGC_ref,\n"); + Fc(tl, 0, "\t.nref = VGC_NREFS,\n"); +#define VCL_RET_MAC(l,u,b) +#define VCL_MET_MAC(l,u,b) \ + if (FindRefStr(tl, "vcl_" #l, R_FUNC)) { \ + Fc(tl, 0, "\t." #l "_func = VGC_function_vcl_" #l ",\n"); \ + AddRefStr(tl, "vcl_" #l, R_FUNC); \ + } else { \ + Fc(tl, 0, "\t." #l "_func = VGC_function_default_vcl_" #l ",\n"); \ + } \ + AddRefStr(tl, "default_vcl_" #l, R_FUNC); +#include "vcl_returns.h" +#undef VCL_MET_MAC +#undef VCL_RET_MAC + Fc(tl, 0, "};\n"); +} + +/*--------------------------------------------------------------------*/ + +char * +VCC_Compile(struct sbuf *sb, const char *b, const char *e) +{ + struct tokenlist tokens; + struct ref *r; + struct token *t; + FILE *fo; + char *of = NULL; + char buf[BUFSIZ]; + + memset(&tokens, 0, sizeof tokens); + TAILQ_INIT(&tokens.tokens); + TAILQ_INIT(&tokens.refs); + TAILQ_INIT(&tokens.procs); + tokens.sb = sb; + + tokens.fc = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND); + assert(tokens.fc != NULL); + + tokens.fh = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND); + assert(tokens.fh != NULL); + + Fh(&tokens, 0, "extern struct VCL_conf VCL_conf;\n"); + + tokens.b = b; + if (e == NULL) + e = strchr(b, '\0'); + assert(e != NULL); + tokens.e = e; + vcc_Lexer(&tokens, b, e); + vcc_Lexer(&tokens, vcc_default_vcl_b, vcc_default_vcl_e); + vcc_AddToken(&tokens, EOI, e, e); + if (tokens.err) + goto done; + tokens.t = TAILQ_FIRST(&tokens.tokens); + Parse(&tokens); + if (tokens.err) + goto done; + Consistency(&tokens); + if (tokens.err) + goto done; + LocTable(&tokens); + + EmitInitFunc(&tokens); + + EmitStruct(&tokens); + + if (CheckRefs(&tokens)) + goto done; + + of = strdup("/tmp/vcl.XXXXXXXX"); + assert(of != NULL); + mktemp(of); + + sprintf(buf, + "tee /tmp/_.c |" + "cc -fpic -shared -Wl,-x -o %s -x c - ", of); + + fo = popen(buf, "w"); + assert(fo != NULL); + + vcl_output_lang_h(fo); + + sbuf_finish(tokens.fh); + fputs(sbuf_data(tokens.fh), fo); + sbuf_delete(tokens.fh); + + sbuf_finish(tokens.fc); + fputs(sbuf_data(tokens.fc), fo); + sbuf_delete(tokens.fc); + + pclose(fo); +done: + + /* Free References */ + while (!TAILQ_EMPTY(&tokens.refs)) { + r = TAILQ_FIRST(&tokens.refs); + TAILQ_REMOVE(&tokens.refs, r, list); + free(r); + } + + /* Free Tokens */ + while (!TAILQ_EMPTY(&tokens.tokens)) { + t = TAILQ_FIRST(&tokens.tokens); + TAILQ_REMOVE(&tokens.tokens, t, list); + free(t); + } + return (of); +} + +/*--------------------------------------------------------------------*/ + +char * +VCC_CompileFile(struct sbuf *sb, const char *fn) +{ + char *f, *r; + int fd, i; + struct stat st; + + fd = open(fn, O_RDONLY); + if (fd < 0) { + sbuf_printf(sb, "Cannot open file '%s': %s", + fn, strerror(errno)); + return (NULL); + } + assert(0 == fstat(fd, &st)); + f = malloc(st.st_size + 1); + assert(f != NULL); + i = read(fd, f, st.st_size); + assert(i == st.st_size); + f[i] = '\0'; + r = VCC_Compile(sb, f, NULL); + free(f); + return (r); +} + +/*--------------------------------------------------------------------*/ + +static int +VCC_T_render(FILE *f, const struct printf_info *info __unused, const void *const *args) +{ + const struct token *t; + + t = *((const struct token * const*) (args[0])); + return (fprintf(f, "%*.*s", + t->e - t->b, t->e - t->b, t->b)); +} + +static int +VCC_T_arginfo(const struct printf_info *info __unused, size_t n, int *argtypes) +{ + + if (n > 0) + argtypes[0] = PA_POINTER; + return 1; +} + +/*--------------------------------------------------------------------*/ + +void +VCC_InitCompile(const char *default_vcl) +{ + struct var *v; + + vcc_default_vcl_b = default_vcl; + vcc_default_vcl_e = strchr(default_vcl, '\0'); + assert(vcc_default_vcl_e != NULL); + + register_printf_function ('T', VCC_T_render, VCC_T_arginfo); + vcl_init_tnames(); + for (v = vcc_vars; v->name != NULL; v++) + v->len = strlen(v->name); + for (v = vcc_be_vars; v->name != NULL; v++) + v->len = strlen(v->name); +} diff --git a/varnish-cache/lib/libvcl/vcc_compile.h b/varnish-cache/lib/libvcl/vcc_compile.h new file mode 100644 index 00000000..ca7e1b8f --- /dev/null +++ b/varnish-cache/lib/libvcl/vcc_compile.h @@ -0,0 +1,120 @@ +/* + * $Id$ + */ + +#include "queue.h" +#include "vcl_returns.h" + +#define INDENT 2 + +struct token { + unsigned tok; + const char *b; + const char *e; + TAILQ_ENTRY(token) list; + unsigned cnt; +}; + +struct tokenlist { + TAILQ_HEAD(, token) tokens; + const char *b; + const char *e; + struct token *t; + int indent; + unsigned cnt; + struct sbuf *fc, *fh; + TAILQ_HEAD(, ref) refs; + struct sbuf *sb; + int err; + int nbackend; + TAILQ_HEAD(, proc) procs; + struct proc *curproc; +}; + +enum var_type { + BACKEND, + BOOL, + INT, + FLOAT, + SIZE, + RATE, + TIME, + STRING, + IP, + HOSTNAME, + PORTNAME, + HEADER +}; + +enum ref_type { + R_FUNC, + R_ACL, + R_BACKEND +}; + +struct ref { + enum ref_type type; + struct token *name; + unsigned defcnt; + unsigned refcnt; + TAILQ_ENTRY(ref) list; +}; + +struct var { + const char *name; + enum var_type fmt; + int len; + const char *rname; + const char *lname; +}; + +static struct method { + const char *name; + const char *defname; + unsigned returns; +} method_tab[] = { +#define VCL_RET_MAC(a,b,c) +#define VCL_MET_MAC(a,b,c) { "vcl_"#a, "default_vcl_"#a, c }, +#include "vcl_returns.h" +#undef VCL_MET_MAC +#undef VCL_RET_MAC + { NULL, 0U } +}; + +struct proccall { + TAILQ_ENTRY(proccall) list; + struct proc *p; + struct token *t; +}; + +struct proc { + TAILQ_ENTRY(proc) list; + TAILQ_HEAD(,proccall) calls; + struct token *name; + unsigned returns; + unsigned exists; + unsigned called; + unsigned active; + struct token *returnt[VCL_RET_MAX]; +}; + + +/*--------------------------------------------------------------------*/ + +/* vcc_compile.c */ +extern const char *vcc_default_vcl_b, *vcc_default_vcl_e; + +/* vcc_obj.c */ +extern struct var vcc_be_vars[]; +extern struct var vcc_vars[]; + +/* vcc_token.c */ +void vcc_ErrToken(struct tokenlist *tl, struct token *t); +void vcc_ErrWhere(struct tokenlist *tl, struct token *t); +void vcc__Expect(struct tokenlist *tl, unsigned tok, int line); +int vcc_Teq(struct token *t1, struct token *t2); +int vcc_IdIs(struct token *t, const char *p); +void vcc_Lexer(struct tokenlist *tl, const char *b, const char *e); +void vcc_NextToken(struct tokenlist *tl); +void vcc__ErrInternal(struct tokenlist *tl, const char *func, unsigned line); +void vcc_AddToken(struct tokenlist *tl, unsigned tok, const char *b, const char *e); diff --git a/varnish-cache/lib/libvcl/vcl_fixed_token.c b/varnish-cache/lib/libvcl/vcc_fixed_token.c similarity index 99% rename from varnish-cache/lib/libvcl/vcl_fixed_token.c rename to varnish-cache/lib/libvcl/vcc_fixed_token.c index c1cdd96a..2037ffc8 100644 --- a/varnish-cache/lib/libvcl/vcl_fixed_token.c +++ b/varnish-cache/lib/libvcl/vcc_fixed_token.c @@ -3,12 +3,12 @@ * * NB: This file is machine generated, DO NOT EDIT! * - * Edit vcl_gen_fixed_token.tcl instead + * Edit vcc_gen_fixed_token.tcl instead */ #include #include -#include "vcl_priv.h" +#include "vcc_priv.h" unsigned vcl_fixed_token(const char *p, const char **q) @@ -436,7 +436,7 @@ vcl_output_lang_h(FILE *f) fputs(" *\n", f); fputs(" * NB: This file is machine generated, DO NOT EDIT!\n", f); fputs(" *\n", f); - fputs(" * Edit vcl_gen_fixed_token.tcl instead\n", f); + fputs(" * Edit vcc_gen_fixed_token.tcl instead\n", f); fputs(" */\n", f); fputs("\n", f); fputs("struct sess;\n", f); diff --git a/varnish-cache/lib/libvcl/vcl_gen_fixed_token.tcl b/varnish-cache/lib/libvcl/vcc_gen_fixed_token.tcl similarity index 97% rename from varnish-cache/lib/libvcl/vcl_gen_fixed_token.tcl rename to varnish-cache/lib/libvcl/vcc_gen_fixed_token.tcl index b91967c5..a505b604 100755 --- a/varnish-cache/lib/libvcl/vcl_gen_fixed_token.tcl +++ b/varnish-cache/lib/libvcl/vcc_gen_fixed_token.tcl @@ -84,7 +84,7 @@ proc warns {fd} { puts $fd " *" puts $fd " * NB: This file is machine generated, DO NOT EDIT!" puts $fd " *" - puts $fd " * Edit vcl_gen_fixed_token.tcl instead" + puts $fd " * Edit vcc_gen_fixed_token.tcl instead" puts $fd " */" puts $fd "" } @@ -162,15 +162,15 @@ close $for #---------------------------------------------------------------------- # Build the compiler token table and recognizers -set fo [open "vcl_fixed_token.c" w] +set fo [open "vcc_fixed_token.c" w] warns $fo -set foh [open "vcl_token_defs.h" w] +set foh [open "vcc_token_defs.h" w] warns $foh puts $fo "#include " puts $fo "#include " -puts $fo "#include \"vcl_priv.h\"" +puts $fo "#include \"vcc_priv.h\"" set tn 128 puts $foh "#define LOW_TOKEN $tn" diff --git a/varnish-cache/lib/libvcl/vcc_obj.c b/varnish-cache/lib/libvcl/vcc_obj.c new file mode 100644 index 00000000..86c62982 --- /dev/null +++ b/varnish-cache/lib/libvcl/vcc_obj.c @@ -0,0 +1,1843 @@ +/* + * $Id$ + */ + +#include + +#include "vcc_compile.h" + +struct var vcc_be_vars[] = { + { "backend.host", + HOSTNAME, 0, NULL, "VRT_set_backend_hostname(backend, %s)" }, + { "backend.port", + PORTNAME, 0, NULL, "VRT_set_backend_portname(backend, %s)" }, +}; + + +struct var vcc_vars[] = { + { "req.request", STRING, 0, "VRT_GetReq(sp)" }, + { "obj.valid", BOOL, 0, "VRT_obj_valid(sp)" }, + { "obj.cacheable", BOOL, 0, "VRT_obj_cacheable(sp)" }, + { "obj.backend", BACKEND, 0, "VRT_obj_backend(sp)" }, + { "req.http.", HEADER, 0, NULL }, +#if 0 + { "req.ttlfactor", FLOAT, 0, "req->ttlfactor" }, + { "req.url.host", STRING, 0, "req->url.host" }, + { "req.url.path", STRING, 0, "req->url.path" }, + { "req.useragent", STRING, 0, "req->useragent" }, + { "req.backend", BACKEND, 0, "req->backend" }, + { "client.ip", IP, 0, "client->ip" }, + { "backend.response_time", TIME, 0, "backend->responsetime" }, + { "backend.down", BOOL, 0, "backend->down" }, + { "backend.timeout", TIME, 0, "backend->timeout" }, + { "backend.bandwidth", RATE, 0, "backend->bandwidth" }, + { "obj.exist", BOOL, 0, "obj->exists" }, + { "obj.ttl", TIME, 0, "obj->ttl" }, + { "obj.result", INT, 0, "obj->result" }, + { "obj.size", SIZE, 0, "obj->size" }, + { "obj.usage", INT, 0, "obj->usage" }, +#endif + { NULL, INT, 0, "NULL" } +}; + + +#if 0 + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vcl_priv.h" +#include "vcl_returns.h" +#include "vcl_compile.h" + +#include "libvcl.h" + +#define ERRCHK(tl) do { if ((tl)->err) return; } while (0) + +#define INDENT 2 + +/*-------------------------------------------------------------------- + * Consistency check + */ + +static struct method { + const char *name; + const char *defname; + unsigned returns; +} method_tab[] = { +#define VCL_RET_MAC(a,b,c) +#define VCL_MET_MAC(a,b,c) { "vcl_"#a, "default_vcl_"#a, c }, +#include "vcl_returns.h" +#undef VCL_MET_MAC +#undef VCL_RET_MAC + { NULL, 0U } +}; + +struct proccall { + TAILQ_ENTRY(proccall) list; + struct proc *p; + struct token *t; +}; + +struct proc { + TAILQ_ENTRY(proc) list; + TAILQ_HEAD(,proccall) calls; + struct token *name; + unsigned returns; + unsigned exists; + unsigned called; + unsigned active; + struct token *returnt[VCL_RET_MAX]; +}; + +/*--------------------------------------------------------------------*/ + + +static void Compound(struct tokenlist *tl); +static void Cond_0(struct tokenlist *tl); +static struct proc *AddProc(struct tokenlist *tl, struct token *t, int def); +static void AddCall(struct tokenlist *tl, struct token *t); +static const char *vcc_default_vcl_b, *vcc_default_vcl_e; + +/*--------------------------------------------------------------------*/ + +static void +ErrToken(struct tokenlist *tl, struct token *t) +{ + + if (t->tok == EOI) + sbuf_printf(tl->sb, "end of input"); + else + sbuf_printf(tl->sb, "'%T'", t); +} + +static void +_ErrInternal(struct tokenlist *tl, const char *func, unsigned line) +{ + + sbuf_printf(tl->sb, "VCL compiler internal error at %s():%u\n", + func, line); + tl->err = 1; +} + +#define ErrInternal(tl) _ErrInternal(tl, __func__, __LINE__) + +static void +ErrWhere(struct tokenlist *tl, struct token *t) +{ + unsigned lin, pos, x, y; + const char *p, *l, *f, *b, *e; + + lin = 1; + pos = 0; + if (t->tok == METHOD) + return; + if (t->b >= vcc_default_vcl_b && t->b < vcc_default_vcl_e) { + f = "Default VCL code (compiled in)"; + b = vcc_default_vcl_b; + e = vcc_default_vcl_e; + } else { + f = "VCL code"; + b = tl->b; + e = tl->e; + } + for (l = p = b; p < t->b; p++) { + if (*p == '\n') { + lin++; + pos = 0; + l = p + 1; + } else if (*p == '\t') { + pos &= ~7; + pos += 8; + } else + pos++; + } + sbuf_printf(tl->sb, "In %s Line %d Pos %d\n", f, lin, pos); + x = y = 0; + for (p = l; p < e && *p != '\n'; p++) { + if (*p == '\t') { + y &= ~7; + y += 8; + while (x < y) { + sbuf_bcat(tl->sb, " ", 1); + x++; + } + } else { + x++; + y++; + sbuf_bcat(tl->sb, p, 1); + } + } + sbuf_cat(tl->sb, "\n"); + x = y = 0; + for (p = l; p < e && *p != '\n'; p++) { + if (p >= t->b && p < t->e) { + sbuf_bcat(tl->sb, "#", 1); + x++; + y++; + continue; + } + if (*p == '\t') { + y &= ~7; + y += 8; + } else + y++; + while (x < y) { + sbuf_bcat(tl->sb, "-", 1); + x++; + } + } + sbuf_cat(tl->sb, "\n"); + tl->err = 1; +} + +/*--------------------------------------------------------------------*/ + +static void +NextToken(struct tokenlist *tl) +{ + tl->t = TAILQ_NEXT(tl->t, list); + if (tl->t == NULL) { + sbuf_printf(tl->sb, + "Ran out of input, something is missing or" + " maybe unbalanced (...) or {...}\n"); + tl->err = 1; + return; + } +} + +static void +_Expect(struct tokenlist *tl, unsigned tok, int line) +{ + if (tl->t->tok == tok) + return; + sbuf_printf(tl->sb, "Expected %s got ", vcl_tnames[tok]); + ErrToken(tl, tl->t); + sbuf_printf(tl->sb, "\n(program line %u), at\n", line); + ErrWhere(tl, tl->t); +} + +#define Expect(a, b) _Expect(a, b, __LINE__) +#define ExpectErr(a, b) do { _Expect(a, b, __LINE__); ERRCHK(a);} while (0) + +#define L(tl, foo) do { \ + tl->indent += INDENT; \ + foo; \ + tl->indent -= INDENT; \ +} while (0) + +#define C(tl, sep) do { \ + Fc(tl, 1, "VRT_count(sp, %u)%s\n", ++tl->cnt, sep); \ + tl->t->cnt = tl->cnt; \ +} while (0) + +/*-------------------------------------------------------------------- + * Printf output to the two sbufs, possibly indented + */ + +static void +Fh(struct tokenlist *tl, int indent, const char *fmt, ...) +{ + va_list ap; + + if (indent) + sbuf_printf(tl->fh, "%*.*s", tl->indent, tl->indent, ""); + va_start(ap, fmt); + sbuf_vprintf(tl->fh, fmt, ap); + va_end(ap); +} + +static void +Fc(struct tokenlist *tl, int indent, const char *fmt, ...) +{ + va_list ap; + + if (indent) + sbuf_printf(tl->fc, "%*.*s", tl->indent, tl->indent, ""); + va_start(ap, fmt); + sbuf_vprintf(tl->fc, fmt, ap); + va_end(ap); +} + +/*-------------------------------------------------------------------- + * Compare token to token + */ + +static int +Teq(struct token *t1, struct token *t2) +{ + if (t1->e - t1->b != t2->e - t2->b) + return (0); + return (!memcmp(t1->b, t2->b, t1->e - t1->b)); +} + +/*-------------------------------------------------------------------- + * Compare ID token to string, return true of match + */ + +static int +IdIs(struct token *t, const char *p) +{ + const char *q; + + assert(t->tok == ID); + for (q = t->b; q < t->e && *p != '\0'; p++, q++) + if (*q != *p) + return (0); + if (q != t->e || *p != '\0') + return (0); + return (1); +} + +/*--------------------------------------------------------------------*/ + +static char * +EncString(struct token *t) +{ + char *p, *q; + const char *r; + unsigned u; + + assert(t->tok == CSTR); + p = malloc(t->e - t->b); + assert(p != NULL); + q = p; + for (r = t->b + 1; r < t->e - 1; ) { + if (*r != '\\') { + *q++ = *r++; + continue; + } + switch (r[1]) { + case 'n': *q++ = '\n'; r += 2; break; + case 'r': *q++ = '\r'; r += 2; break; + case 'v': *q++ = '\v'; r += 2; break; + case 'f': *q++ = '\f'; r += 2; break; + case 't': *q++ = '\t'; r += 2; break; + case 'b': *q++ = '\b'; r += 2; break; + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + u = r[1] - '0'; + r += 2; + if (isdigit(r[0]) && (r[0] - '0') < 8) { + u <<= 3; + u |= r[0] - '0'; + r++; + if (isdigit(r[0]) && (r[0] - '0') < 8) { + u <<= 3; + u |= r[0] - '0'; + r++; + } + } + *q++ = u; + break; + default: + *q++ = r[1]; + r += 2; + break; + } + } + *q = '\0'; + return (p); +} + + +/*-------------------------------------------------------------------- + * Keep track of definitions and references + */ + +static struct ref * +FindRef(struct tokenlist *tl, struct token *t, enum ref_type type) +{ + struct ref *r; + + TAILQ_FOREACH(r, &tl->refs, list) { + if (r->type != type) + continue; + if (Teq(r->name, t)) + return (r); + } + r = calloc(sizeof *r, 1); + assert(r != NULL); + r->name = t; + r->type = type; + TAILQ_INSERT_TAIL(&tl->refs, r, list); + return (r); +} + +static int +FindRefStr(struct tokenlist *tl, const char *s, enum ref_type type) +{ + struct ref *r; + + TAILQ_FOREACH(r, &tl->refs, list) { + if (r->type != type) + continue; + if (IdIs(r->name, s)) + return (1); + } + return (0); +} + +static void +AddRef(struct tokenlist *tl, struct token *t, enum ref_type type) +{ + + FindRef(tl, t, type)->refcnt++; +} + +static void +AddRefStr(struct tokenlist *tl, const char *s, enum ref_type type) +{ + struct token *t; + + t = calloc(sizeof *t, 1); + t->b = s; + t->e = strchr(s, '\0'); + t->tok = METHOD; + AddRef(tl, t, type); +} + +static void +AddDef(struct tokenlist *tl, struct token *t, enum ref_type type) +{ + struct ref *r; + + r = FindRef(tl, t, type); + r->defcnt++; + r->name = t; +} + +/*-------------------------------------------------------------------- + * Recognize and convert units of time, return seconds. + */ + +static double +TimeUnit(struct tokenlist *tl) +{ + double sc = 1.0; + + assert(tl->t->tok == ID); + if (IdIs(tl->t, "ms")) + sc = 1e-3; + else if (IdIs(tl->t, "s")) + sc = 1.0; + else if (IdIs(tl->t, "m")) + sc = 60.0; + else if (IdIs(tl->t, "h")) + sc = 60.0 * 60.0; + else if (IdIs(tl->t, "d")) + sc = 60.0 * 60.0 * 24.0; + else { + sbuf_printf(tl->sb, "Unknown time unit "); + ErrToken(tl, tl->t); + sbuf_printf(tl->sb, ". Legal are 's', 'm', 'h' and 'd'\n"); + ErrWhere(tl, tl->t); + return (1.0); + } + NextToken(tl); + return (sc); +} + +/*-------------------------------------------------------------------- + * Recognize and convert units of size, return bytes. + */ + +static double +SizeUnit(struct tokenlist *tl) +{ + double sc = 1.0; + + assert(tl->t->tok == ID); + if (IdIs(tl->t, "b")) + sc = 1.0; + else if (IdIs(tl->t, "kb")) + sc = 1024.0; + else if (IdIs(tl->t, "mb") || IdIs(tl->t, "Mb")) + sc = 1024.0 * 1024.0; + else if (IdIs(tl->t, "gb") || IdIs(tl->t, "Gb")) + sc = 1024.0 * 1024.0 * 1024.0; + else { + sbuf_printf(tl->sb, "Unknown size unit "); + ErrToken(tl, tl->t); + sbuf_printf(tl->sb, ". Legal are 'kb', 'mb' and 'gb'\n"); + ErrWhere(tl, tl->t); + return (1.0); + } + NextToken(tl); + return (sc); +} + +/*-------------------------------------------------------------------- + * Recognize and convert units of rate as { space '/' time } + */ + +static double +RateUnit(struct tokenlist *tl) +{ + double sc = 1.0; + + assert(tl->t->tok == ID); + sc = SizeUnit(tl); + Expect(tl, '/'); + NextToken(tl); + sc /= TimeUnit(tl); + return (sc); +} + +/*-------------------------------------------------------------------- + * Recognize and convert { CNUM } to unsigned value + */ + +static unsigned +UintVal(struct tokenlist *tl) +{ + unsigned d = 0; + const char *p; + + Expect(tl, CNUM); + for (p = tl->t->b; p < tl->t->e; p++) { + d *= 10; + d += *p - '0'; + } + NextToken(tl); + return (d); +} + +/*-------------------------------------------------------------------- + * Recognize and convert { CNUM [ '.' [ CNUM ] ] } to double value + */ + +static double +DoubleVal(struct tokenlist *tl) +{ + double d = 0.0, e = 0.1; + const char *p; + + Expect(tl, CNUM); + for (p = tl->t->b; p < tl->t->e; p++) { + d *= 10; + d += *p - '0'; + } + NextToken(tl); + if (tl->t->tok != '.') + return (d); + NextToken(tl); + if (tl->t->tok != CNUM) + return (d); + for (p = tl->t->b; p < tl->t->e; p++) { + d += (*p - '0') * e; + e *= 0.1; + } + NextToken(tl); + return (d); +} + +/*--------------------------------------------------------------------*/ + +static unsigned +IpVal(struct tokenlist *tl) +{ + unsigned u, v; + struct token *t; + + t = tl->t; + u = UintVal(tl); + if (u < 256) { + v = u << 24; + Expect(tl, '.'); + NextToken(tl); + t = tl->t; + u = UintVal(tl); + if (u < 256) { + v |= u << 16; + Expect(tl, '.'); + NextToken(tl); + t = tl->t; + u = UintVal(tl); + if (u < 256) { + v |= u << 8; + Expect(tl, '.'); + NextToken(tl); + t = tl->t; + u = UintVal(tl); + if (u < 256) { + v |= u; + return (v); + } + } + } + } + sbuf_printf(tl->sb, "Illegal octet in IP number\n"); + ErrWhere(tl, t); + return (0); +} + +/*--------------------------------------------------------------------*/ + +static struct var * +HeaderVar(struct tokenlist *tl __unused, struct token *t, struct var *vh) +{ + char *p; + struct var *v; + int i; + + v = calloc(sizeof *v, 1); + assert(v != NULL); + i = t->e - t->b; + p = malloc(i + 1); + assert(p != NULL); + memcpy(p, t->b, i); + p[i] = '\0'; + v->name = p; + v->fmt = STRING; + asprintf(&p, "VRT_GetHdr(sp, \"\\%03o%s:\")", + strlen(v->name + vh->len) + 1, v->name + vh->len); + assert(p != NULL); + v->rname = p; + return (v); +} + +/*--------------------------------------------------------------------*/ + +static struct var * +FindVar(struct tokenlist *tl, struct token *t, struct var *vl) +{ + struct var *v; + + for (v = vl; v->name != NULL; v++) { + if (v->fmt == HEADER && t->e - t->b <= v->len) + continue; + if (v->fmt != HEADER && t->e - t->b != v->len) + continue; + if (memcmp(t->b, v->name, v->len)) + continue; + if (v->fmt != HEADER) + return (v); + return (HeaderVar(tl, t, v)); + } + sbuf_printf(tl->sb, "Unknown variable "); + ErrToken(tl, t); + sbuf_cat(tl->sb, "\nAt: "); + ErrWhere(tl, t); + return (NULL); +} + + +/*--------------------------------------------------------------------*/ + +static void +TimeVal(struct tokenlist *tl) +{ + double v, sc; + + v = DoubleVal(tl); + ExpectErr(tl, ID); + sc = TimeUnit(tl); + Fc(tl, 0, "(%g * %g)", v, sc); +} + +static void +SizeVal(struct tokenlist *tl) +{ + double v, sc; + + v = DoubleVal(tl); + ExpectErr(tl, ID); + sc = SizeUnit(tl); + Fc(tl, 0, "(%g * %g)", v, sc); +} + +static void +RateVal(struct tokenlist *tl) +{ + double v, sc; + + v = DoubleVal(tl); + ExpectErr(tl, ID); + sc = RateUnit(tl); + Fc(tl, 0, "(%g * %g)", v, sc); +} + +/*--------------------------------------------------------------------*/ + +static void +Cond_Ip(struct var *vp, struct tokenlist *tl) +{ + unsigned u; + + switch (tl->t->tok) { + case '~': + NextToken(tl); + ExpectErr(tl, ID); + AddRef(tl, tl->t, R_ACL); + Fc(tl, 1, "ip_match(%s, acl_%T)\n", vp->rname, tl->t); + NextToken(tl); + break; + case T_EQ: + case T_NEQ: + Fc(tl, 1, "%s %T ", vp->rname, tl->t); + NextToken(tl); + u = IpVal(tl); + Fc(tl, 0, "%uU /* %u.%u.%u.%u */\n", u, + (u >> 24) & 0xff, (u >> 16) & 0xff, + (u >> 8) & 0xff, (u) & 0xff); + break; + default: + sbuf_printf(tl->sb, "Illegal condition "); + ErrToken(tl, tl->t); + sbuf_printf(tl->sb, " on IP number variable\n"); + sbuf_printf(tl->sb, " only '==', '!=' and '~' are legal\n"); + ErrWhere(tl, tl->t); + break; + } +} + +static void +Cond_String(struct var *vp, struct tokenlist *tl) +{ + + switch (tl->t->tok) { + case '~': + Fc(tl, 1, "string_match(%s, ", vp->rname); + NextToken(tl); + ExpectErr(tl, CSTR); + Fc(tl, 0, "%T)\n", tl->t); + NextToken(tl); + break; + case T_EQ: + case T_NEQ: + Fc(tl, 1, "%sstrcmp(%s, ", + tl->t->tok == T_EQ ? "!" : "", vp->rname); + NextToken(tl); + ExpectErr(tl, CSTR); + Fc(tl, 0, "%T)\n", tl->t); + NextToken(tl); + break; + default: + Fc(tl, 1, "%s != (void*)0", vp->rname); + break; + } +} + +static void +Cond_Int(struct var *vp, struct tokenlist *tl) +{ + + Fc(tl, 1, "%s ", vp->rname); + switch (tl->t->tok) { + case T_EQ: + case T_NEQ: + case T_LEQ: + case T_GEQ: + case '>': + case '<': + Fc(tl, 0, "%T ", tl->t); + NextToken(tl); + switch(vp->fmt) { + case TIME: + TimeVal(tl); + break; + case INT: + ExpectErr(tl, CNUM); + Fc(tl, 0, "%T ", tl->t); + NextToken(tl); + break; + case SIZE: + SizeVal(tl); + break; + default: + sbuf_printf(tl->sb, + "No conditions available for variable '%s'\n", + vp->name); + ErrWhere(tl, tl->t); + return; + } + Fc(tl, 0, "\n"); + break; + default: + sbuf_printf(tl->sb, "Illegal condition "); + ErrToken(tl, tl->t); + sbuf_printf(tl->sb, " on integer variable\n"); + sbuf_printf(tl->sb, + " only '==', '!=', '<', '>', '<=' and '>=' are legal\n"); + ErrWhere(tl, tl->t); + break; + } +} + +static void +Cond_Bool(struct var *vp, struct tokenlist *tl) +{ + + Fc(tl, 1, "%s\n", vp->rname); +} + +static void +Cond_2(struct tokenlist *tl) +{ + struct var *vp; + + C(tl, ","); + if (tl->t->tok == '!') { + Fc(tl, 1, "!(\n"); + NextToken(tl); + } else { + Fc(tl, 1, "(\n"); + } + if (tl->t->tok == '(') { + NextToken(tl); + Cond_0(tl); + ExpectErr(tl, ')'); + NextToken(tl); + } else if (tl->t->tok == VAR) { + vp = FindVar(tl, tl->t, vars); + ERRCHK(tl); + assert(vp != NULL); + NextToken(tl); + switch (vp->fmt) { + case INT: L(tl, Cond_Int(vp, tl)); break; + case SIZE: L(tl, Cond_Int(vp, tl)); break; + case BOOL: L(tl, Cond_Bool(vp, tl)); break; + case IP: L(tl, Cond_Ip(vp, tl)); break; + case STRING: L(tl, Cond_String(vp, tl)); break; + case TIME: L(tl, Cond_Int(vp, tl)); break; + /* XXX backend == */ + default: + sbuf_printf(tl->sb, + "Variable '%s'" + " has no conditions that can be checked\n", + vp->name); + ErrWhere(tl, tl->t); + return; + } + } else { + sbuf_printf(tl->sb, + "Syntax error in condition, expected '(', '!' or" + " variable name, found "); + ErrToken(tl, tl->t); + sbuf_printf(tl->sb, "\n"); + ErrWhere(tl, tl->t); + return; + } + Fc(tl, 1, ")\n"); +} + +static void +Cond_1(struct tokenlist *tl) +{ + + Fc(tl, 1, "(\n"); + L(tl, Cond_2(tl)); + while (tl->t->tok == T_CAND) { + NextToken(tl); + Fc(tl, 1, ") && (\n"); + L(tl, Cond_2(tl)); + } + Fc(tl, 1, ")\n"); +} + +static void +Cond_0(struct tokenlist *tl) +{ + + Fc(tl, 1, "(\n"); + L(tl, Cond_1(tl)); + while (tl->t->tok == T_COR) { + NextToken(tl); + Fc(tl, 1, ") || (\n"); + L(tl, Cond_1(tl)); + } + Fc(tl, 1, ")\n"); +} + +static void +Conditional(struct tokenlist *tl) +{ + + ExpectErr(tl, '('); + NextToken(tl); + Fc(tl, 1, "(\n"); + L(tl, Cond_0(tl)); + ERRCHK(tl); + Fc(tl, 1, ")\n"); + ExpectErr(tl, ')'); + NextToken(tl); +} + +/*--------------------------------------------------------------------*/ + +static void +IfStmt(struct tokenlist *tl) +{ + + ExpectErr(tl, T_IF); + Fc(tl, 1, "if \n"); + NextToken(tl); + L(tl, Conditional(tl)); + ERRCHK(tl); + L(tl, Compound(tl)); + ERRCHK(tl); + while (1) { + switch (tl->t->tok) { + case T_ELSE: + NextToken(tl); + if (tl->t->tok != T_IF) { + Fc(tl, 1, "else \n"); + L(tl, Compound(tl)); + ERRCHK(tl); + return; + } + /* FALLTHROUGH */ + case T_ELSEIF: + case T_ELSIF: + Fc(tl, 1, "else if \n"); + NextToken(tl); + L(tl, Conditional(tl)); + ERRCHK(tl); + L(tl, Compound(tl)); + ERRCHK(tl); + break; + default: + C(tl, ";"); + return; + } + } +} + +/*--------------------------------------------------------------------*/ + +static void +Action(struct tokenlist *tl) +{ + unsigned a, u; + struct var *vp; + struct token *at; + + at = tl->t; + NextToken(tl); + switch (at->tok) { + case T_NO_NEW_CACHE: + Fc(tl, 1, "VCL_no_new_cache(sp);\n"); + return; + case T_NO_CACHE: + Fc(tl, 1, "VCL_no_cache(sp);\n"); + return; +#define VCL_RET_MAC(a,b,c) case T_##b: \ + Fc(tl, 1, "VRT_done(sp, VCL_RET_%s);\n", #b); \ + tl->curproc->returns |= VCL_RET_##b; \ + tl->curproc->returnt[c] = at; \ + return; +#include "vcl_returns.h" +#undef VCL_RET_MAC + case T_ERROR: + if (tl->t->tok == CNUM) + a = UintVal(tl); + else + a = 0; + Fc(tl, 1, "VRT_error(sp, %u, ", a); + if (tl->t->tok == CSTR) { + Fc(tl, 0, "%T);\n", tl->t); + NextToken(tl); + } else + Fc(tl, 0, "(const char *)0);\n"); + Fc(tl, 1, "VRT_done(sp, VCL_RET_ERROR);\n"); + return; + case T_SWITCH_CONFIG: + ExpectErr(tl, ID); + Fc(tl, 1, "VCL_switch_config(\"%T\");\n", tl->t); + NextToken(tl); + return; + case T_CALL: + ExpectErr(tl, ID); + AddCall(tl, tl->t); + AddRef(tl, tl->t, R_FUNC); + Fc(tl, 1, "if (VGC_function_%T(sp))\n", tl->t); + Fc(tl, 1, "\treturn (1);\n"); + NextToken(tl); + return; + case T_REWRITE: + ExpectErr(tl, CSTR); + Fc(tl, 1, "VCL_rewrite(%T", tl->t); + NextToken(tl); + ExpectErr(tl, CSTR); + Fc(tl, 0, ", %T);\n", tl->t); + NextToken(tl); + return; + case T_SET: + ExpectErr(tl, VAR); + vp = FindVar(tl, tl->t, vars); + ERRCHK(tl); + assert(vp != NULL); + Fc(tl, 1, "%s ", vp->rname); + NextToken(tl); + switch (vp->fmt) { + case INT: + case SIZE: + case RATE: + case TIME: + case FLOAT: + Fc(tl, 0, "%T ", tl->t); + a = tl->t->tok; + NextToken(tl); + if (a == T_MUL || a == T_DIV) + Fc(tl, 0, "%g", DoubleVal(tl)); + else if (vp->fmt == TIME) + TimeVal(tl); + else if (vp->fmt == SIZE) + SizeVal(tl); + else if (vp->fmt == RATE) + RateVal(tl); + else + Fc(tl, 0, "%g", DoubleVal(tl)); + Fc(tl, 0, ";\n"); + break; + case IP: + if (tl->t->tok == '=') { + NextToken(tl); + u = IpVal(tl); + Fc(tl, 0, "= %uU; /* %u.%u.%u.%u */\n", + u, + (u >> 24) & 0xff, + (u >> 16) & 0xff, + (u >> 8) & 0xff, + u & 0xff); + break; + } + sbuf_printf(tl->sb, "Illegal assignment operator "); + ErrToken(tl, tl->t); + sbuf_printf(tl->sb, + " only '=' is legal for IP numbers\n"); + ErrWhere(tl, tl->t); + return; + case BACKEND: + if (tl->t->tok == '=') { + NextToken(tl); + AddRef(tl, tl->t, R_BACKEND); + Fc(tl, 0, "= &VGC_backend_%T;\n", tl->t); + NextToken(tl); + break; + } + sbuf_printf(tl->sb, "Illegal assignment operator "); + ErrToken(tl, tl->t); + sbuf_printf(tl->sb, + " only '=' is legal for backend\n"); + ErrWhere(tl, tl->t); + return; + default: + sbuf_printf(tl->sb, + "Assignments not possible for '%s'\n", vp->name); + ErrWhere(tl, tl->t); + return; + } + return; + default: + sbuf_printf(tl->sb, "Expected action, 'if' or '}'\n"); + ErrWhere(tl, at); + return; + } +} + +/*--------------------------------------------------------------------*/ + +static void +Acl(struct tokenlist *tl) +{ + unsigned u, m; + + NextToken(tl); + + ExpectErr(tl, ID); + AddDef(tl, tl->t, R_ACL); + Fh(tl, 0, "static struct vcl_acl acl_%T[];\n", tl->t); + Fc(tl, 1, "static struct vcl_acl acl_%T[] = {\n", tl->t); + NextToken(tl); + + tl->indent += INDENT; + + ExpectErr(tl, '{'); + NextToken(tl); + + while (tl->t->tok == CNUM) { + u = IpVal(tl); + if (tl->t->tok == '/') { + NextToken(tl); + ExpectErr(tl, CNUM); + m = UintVal(tl); + } else + m = 32; + ExpectErr(tl, ';'); + NextToken(tl); + Fc(tl, 1, "{ %11uU, %3uU }, /* %u.%u.%u.%u/%u */\n", + u, m, + (u >> 24) & 0xff, (u >> 16) & 0xff, + (u >> 8) & 0xff, (u) & 0xff, m); + } + ExpectErr(tl, '}'); + Fc(tl, 1, "{ %11uU, %3uU }\n", 0, 0); + + tl->indent -= INDENT; + + Fc(tl, 1, "};\n\n"); + NextToken(tl); +} + +/*--------------------------------------------------------------------*/ + +static void +Compound(struct tokenlist *tl) +{ + + ExpectErr(tl, '{'); + Fc(tl, 1, "{\n"); + tl->indent += INDENT; + C(tl, ";"); + NextToken(tl); + while (1) { + ERRCHK(tl); + switch (tl->t->tok) { + case '{': + Compound(tl); + break; + case T_IF: + IfStmt(tl); + break; + case '}': + NextToken(tl); + tl->indent -= INDENT; + Fc(tl, 1, "}\n"); + return; + case EOI: + sbuf_printf(tl->sb, + "End of input while in compound statement\n"); + tl->err = 1; + return; + default: + Action(tl); + ERRCHK(tl); + ExpectErr(tl, ';'); + NextToken(tl); + break; + } + } +} + +/*--------------------------------------------------------------------*/ + +static const char * +CheckHostPort(const char *host, const char *port) +{ + struct addrinfo *res, hint; + int error; + + memset(&hint, 0, sizeof hint); + hint.ai_family = PF_UNSPEC; + hint.ai_socktype = SOCK_STREAM; + error = getaddrinfo(host, port, &hint, &res); + if (error) + return (gai_strerror(error)); + freeaddrinfo(res); + return (NULL); +} + +static void +Backend(struct tokenlist *tl) +{ + struct var *vp; + struct token *t_be = NULL; + struct token *t_host = NULL; + struct token *t_port = NULL; + char *host = NULL; + char *port = NULL; + const char *ep; + + NextToken(tl); + ExpectErr(tl, ID); + t_be = tl->t; + AddDef(tl, tl->t, R_BACKEND); + if (tl->nbackend == 0) + AddRef(tl, tl->t, R_BACKEND); + Fh(tl, 1, "#define VGC_backend_%T (VCL_conf.backend[%d])\n", + tl->t, tl->nbackend); + Fc(tl, 0, "static void\n"); + Fc(tl, 1, "VGC_init_backend_%T (void)\n", tl->t); + Fc(tl, 1, "{\n"); + Fc(tl, 1, "\tstruct backend *backend = VGC_backend_%T;\n", tl->t); + Fc(tl, 1, "\tconst char *p;\n"); + Fc(tl, 1, "\n"); + Fc(tl, 1, "\tVRT_set_backend_name(backend, \"%T\");\n", tl->t); + NextToken(tl); + ExpectErr(tl, '{'); + NextToken(tl); + while (1) { + if (tl->t->tok == '}') + break; + ExpectErr(tl, T_SET); + NextToken(tl); + ExpectErr(tl, VAR); + vp = FindVar(tl, tl->t, be_vars); + ERRCHK(tl); + assert(vp != NULL); + NextToken(tl); + ExpectErr(tl, '='); + NextToken(tl); + switch (vp->fmt) { + case HOSTNAME: + ExpectErr(tl, CSTR); + t_host = tl->t; + Fc(tl, 1, "\tp = %T;\n", tl->t); + Fc(tl, 1, "\t"); + Fc(tl, 0, vp->lname, "p"); + Fc(tl, 0, ";\n"); + NextToken(tl); + break; + case PORTNAME: + ExpectErr(tl, CSTR); + t_port = tl->t; + Fc(tl, 1, "\tp = %T;\n", tl->t); + Fc(tl, 1, "\t"); + Fc(tl, 0, vp->lname, "p"); + Fc(tl, 0, ";\n"); + NextToken(tl); + break; + default: + sbuf_printf(tl->sb, + "Assignments not possible for '%s'\n", vp->name); + ErrWhere(tl, tl->t); + return; + } + ExpectErr(tl, ';'); + NextToken(tl); + } + ExpectErr(tl, '}'); + if (t_host == NULL) { + sbuf_printf(tl->sb, "Backend '%T' has no hostname\n", t_be); + ErrWhere(tl, tl->t); + return; + } + host = EncString(t_host); + ep = CheckHostPort(host, "80"); + if (ep != NULL) { + sbuf_printf(tl->sb, "Backend '%T': %s\n", t_be, ep); + ErrWhere(tl, t_host); + return; + } + if (t_port != NULL) { + port = EncString(t_port); + ep = CheckHostPort(host, port); + if (ep != NULL) { + sbuf_printf(tl->sb, "Backend '%T': %s\n", t_be, ep); + ErrWhere(tl, t_port); + return; + } + } + + NextToken(tl); + Fc(tl, 1, "}\n"); + Fc(tl, 0, "\n"); + tl->nbackend++; +} + +/*--------------------------------------------------------------------*/ + +static void +Function(struct tokenlist *tl) +{ + + NextToken(tl); + ExpectErr(tl, ID); + tl->curproc = AddProc(tl, tl->t, 1); + tl->curproc->exists++; + AddDef(tl, tl->t, R_FUNC); + Fh(tl, 0, "static int VGC_function_%T (struct sess *sp);\n", tl->t); + Fc(tl, 1, "static int\n"); + Fc(tl, 1, "VGC_function_%T (struct sess *sp)\n", tl->t); + NextToken(tl); + L(tl, Compound(tl)); + Fc(tl, 0, "\n"); +} + +/*-------------------------------------------------------------------- + * Top level of parser, recognize: + * Function definitions + * Backend definitions + * End of input + */ + +static void +Parse(struct tokenlist *tl) +{ + + while (tl->t->tok != EOI) { + ERRCHK(tl); + switch (tl->t->tok) { + case T_ACL: + Acl(tl); + break; + case T_SUB: + Function(tl); + break; + case T_BACKEND: + Backend(tl); + break; + case EOI: + break; + default: + sbuf_printf(tl->sb, + "Expected 'acl', 'sub' or 'backend', found "); + ErrToken(tl, tl->t); + sbuf_printf(tl->sb, " at\n"); + ErrWhere(tl, tl->t); + return; + } + } +} + +/*-------------------------------------------------------------------- + * Add a token to the token list. + */ + +static void +AddToken(struct tokenlist *tl, unsigned tok, const char *b, const char *e) +{ + struct token *t; + + t = calloc(sizeof *t, 1); + assert(t != NULL); + t->tok = tok; + t->b = b; + t->e = e; + TAILQ_INSERT_TAIL(&tl->tokens, t, list); + tl->t = t; + if (0) { + fprintf(stderr, "[%s %*.*s] ", + vcl_tnames[tok], + e - b, e - b, b); + if (tok == EOI) + fprintf(stderr, "\n"); + } +} + +/*-------------------------------------------------------------------- + * Lexical analysis and token generation + */ + +static void +Lexer(struct tokenlist *tl, const char *b, const char *e) +{ + const char *p, *q; + unsigned u; + + for (p = b; p < e; ) { + + /* Skip any whitespace */ + if (isspace(*p)) { + p++; + continue; + } + + /* Skip '#.*\n' comments */ + if (*p == '#') { + while (p < e && *p != '\n') + p++; + continue; + } + + /* Skip C-style comments */ + if (*p == '/' && p[1] == '*') { + p += 2; + for (p += 2; p < e; p++) { + if (*p == '*' && p[1] == '/') { + p += 2; + break; + } + } + continue; + } + + /* Match for the fixed tokens (see token.tcl) */ + u = vcl_fixed_token(p, &q); + if (u != 0) { + AddToken(tl, u, p, q); + p = q; + continue; + } + + /* Match strings, with \\ and \" escapes */ + if (*p == '"') { + for (q = p + 1; q < e; q++) { + if (*q == '\\' && q[1] == '\\') + q++; + else if (*q == '\\' && q[1] == '"') + q++; + else if (*q == '"') { + q++; + break; + } + } + AddToken(tl, CSTR, p, q); + p = q; + continue; + } + + /* Match Identifiers */ + if (isident1(*p)) { + for (q = p; q < e; q++) + if (!isident(*q)) + break; + if (isvar(*q)) { + for (; q < e; q++) + if (!isvar(*q)) + break; + AddToken(tl, VAR, p, q); + } else { + AddToken(tl, ID, p, q); + } + p = q; + continue; + } + + /* Match numbers { [0-9]+ } */ + if (isdigit(*p)) { + for (q = p; q < e; q++) + if (!isdigit(*q)) + break; + AddToken(tl, CNUM, p, q); + p = q; + continue; + } + AddToken(tl, EOI, p, p + 1); + sbuf_printf(tl->sb, "Syntax error at\n"); + ErrWhere(tl, tl->t); + return; + } +} + +/*-------------------------------------------------------------------- + * Consistency check + */ + +static struct proc * +AddProc(struct tokenlist *tl, struct token *t, int def) +{ + struct proc *p; + + TAILQ_FOREACH(p, &tl->procs, list) { + if (!Teq(p->name, t)) + continue; + if (def) + p->name = t; + return (p); + } + p = calloc(sizeof *p, 1); + assert(p != NULL); + p->name = t; + TAILQ_INIT(&p->calls); + TAILQ_INSERT_TAIL(&tl->procs, p, list); + return (p); +} + +static void +AddCall(struct tokenlist *tl, struct token *t) +{ + struct proccall *pc; + struct proc *p; + + p = AddProc(tl, t, 0); + TAILQ_FOREACH(pc, &tl->curproc->calls, list) { + if (pc->p == p) + return; + } + pc = calloc(sizeof *pc, 1); + assert(pc != NULL); + pc->p = p; + pc->t = t; + TAILQ_INSERT_TAIL(&tl->curproc->calls, pc, list); +} + +static int +Consist_Decend(struct tokenlist *tl, struct proc *p, unsigned returns) +{ + unsigned u; + struct proccall *pc; + + if (!p->exists) { + sbuf_printf(tl->sb, "Function %T does not exist\n", p->name); + return (1); + } + if (p->active) { + sbuf_printf(tl->sb, "Function recurses on\n"); + ErrWhere(tl, p->name); + return (1); + } + u = p->returns & ~returns; + if (u) { +#define VCL_RET_MAC(a, b, c) \ + if (u & VCL_RET_##b) { \ + sbuf_printf(tl->sb, "Illegal return for method\n"); \ + ErrWhere(tl, p->returnt[c]); \ + } +#include "vcl_returns.h" +#undef VCL_RET_MAC + sbuf_printf(tl->sb, "In function\n"); + ErrWhere(tl, p->name); + return (1); + } + p->active = 1; + TAILQ_FOREACH(pc, &p->calls, list) { + if (Consist_Decend(tl, pc->p, returns)) { + sbuf_printf(tl->sb, "\nCalled from\n"); + ErrWhere(tl, p->name); + sbuf_printf(tl->sb, "at\n"); + ErrWhere(tl, pc->t); + return (1); + } + } + p->active = 0; + p->called++; + return (0); +} + +static int +Consistency(struct tokenlist *tl) +{ + struct proc *p; + struct method *m; + + TAILQ_FOREACH(p, &tl->procs, list) { + for(m = method_tab; m->name != NULL; m++) { + if (IdIs(p->name, m->defname)) + p->called = 1; + if (IdIs(p->name, m->name)) + break; + } + if (m->name == NULL) + continue; + if (Consist_Decend(tl, p, m->returns)) { + sbuf_printf(tl->sb, + "\nwhich is a %s method\n", m->name); + return (1); + } + } + TAILQ_FOREACH(p, &tl->procs, list) { + if (p->called) + continue; + sbuf_printf(tl->sb, "Function unused\n"); + ErrWhere(tl, p->name); + return (1); + } + return (0); +} + +/*--------------------------------------------------------------------*/ + +static int +CheckRefs(struct tokenlist *tl) +{ + struct ref *r; + const char *type; + int nerr = 0; + + TAILQ_FOREACH(r, &tl->refs, list) { + if (r->defcnt != 0 && r->refcnt != 0) + continue; + nerr++; + + switch(r->type) { + case R_FUNC: + type = "function"; + break; + case R_ACL: + type = "acl"; + break; + case R_BACKEND: + type = "backend"; + break; + default: + ErrInternal(tl); + sbuf_printf(tl->sb, "Ref "); + ErrToken(tl, r->name); + sbuf_printf(tl->sb, " has unknown type %d\n", + r->type); + continue; + } + if (r->defcnt == 0 && r->name->tok == METHOD) { + sbuf_printf(tl->sb, + "No definition for method %T\n", r->name); + continue; + } + + if (r->defcnt == 0) { + sbuf_printf(tl->sb, + "Undefined %s %T, first reference:\n", + type, r->name); + ErrWhere(tl, r->name); + continue; + } + + sbuf_printf(tl->sb, "Unused %s %T, defined:\n", type, r->name); + ErrWhere(tl, r->name); + } + return (nerr); +} + +/*--------------------------------------------------------------------*/ + +static void +LocTable(struct tokenlist *tl) +{ + struct token *t; + unsigned lin, pos; + const char *p; + + Fh(tl, 0, "#define VGC_NREFS %u\n", tl->cnt + 1); + Fh(tl, 0, "static struct vrt_ref VGC_ref[VGC_NREFS];\n"); + Fc(tl, 0, "static struct vrt_ref VGC_ref[VGC_NREFS] = {\n"); + lin = 1; + pos = 0; + p = tl->b; + TAILQ_FOREACH(t, &tl->tokens, list) { + if (t->cnt == 0) + continue; + for (;p < t->b; p++) { + if (*p == '\n') { + lin++; + pos = 0; + } else if (*p == '\t') { + pos &= ~7; + pos += 8; + } else + pos++; + + } + Fc(tl, 0, " [%3u] = { %4u, %3u, 0, \"%T\" },\n", + t->cnt, lin, pos + 1, t); + } + Fc(tl, 0, "};\n"); +} + + +/*--------------------------------------------------------------------*/ + +static void +EmitInitFunc(struct tokenlist *tl) +{ + struct ref *r; + + Fc(tl, 0, "\nstatic void\nVGC_Init(void)\n{\n\n"); + Fc(tl, 0, "\tVRT_alloc_backends(&VCL_conf);\n"); + + TAILQ_FOREACH(r, &tl->refs, list) { + switch(r->type) { + case R_FUNC: + break; + case R_ACL: + break; + case R_BACKEND: + Fc(tl, 0, "\tVGC_init_backend_%T();\n", r->name); + break; + } + } + Fc(tl, 0, "}\n"); +} + +/*--------------------------------------------------------------------*/ + +static void +EmitStruct(struct tokenlist *tl) +{ + + Fc(tl, 0, "\nstruct VCL_conf VCL_conf = {\n"); + Fc(tl, 0, "\t.magic = VCL_CONF_MAGIC,\n"); + Fc(tl, 0, "\t.init_func = VGC_Init,\n"); + Fc(tl, 0, "\t.nbackend = %d,\n", tl->nbackend); + Fc(tl, 0, "\t.ref = VGC_ref,\n"); + Fc(tl, 0, "\t.nref = VGC_NREFS,\n"); +#define VCL_RET_MAC(l,u,b) +#define VCL_MET_MAC(l,u,b) \ + if (FindRefStr(tl, "vcl_" #l, R_FUNC)) { \ + Fc(tl, 0, "\t." #l "_func = VGC_function_vcl_" #l ",\n"); \ + AddRefStr(tl, "vcl_" #l, R_FUNC); \ + } else { \ + Fc(tl, 0, "\t." #l "_func = VGC_function_default_vcl_" #l ",\n"); \ + } \ + AddRefStr(tl, "default_vcl_" #l, R_FUNC); +#include "vcl_returns.h" +#undef VCL_MET_MAC +#undef VCL_RET_MAC + Fc(tl, 0, "};\n"); +} + +/*--------------------------------------------------------------------*/ + +char * +VCC_Compile(struct sbuf *sb, const char *b, const char *e) +{ + struct tokenlist tokens; + struct ref *r; + struct token *t; + FILE *fo; + char *of = NULL; + char buf[BUFSIZ]; + + memset(&tokens, 0, sizeof tokens); + TAILQ_INIT(&tokens.tokens); + TAILQ_INIT(&tokens.refs); + TAILQ_INIT(&tokens.procs); + tokens.sb = sb; + + tokens.fc = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND); + assert(tokens.fc != NULL); + + tokens.fh = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND); + assert(tokens.fh != NULL); + + Fh(&tokens, 0, "extern struct VCL_conf VCL_conf;\n"); + + tokens.b = b; + if (e == NULL) + e = strchr(b, '\0'); + assert(e != NULL); + tokens.e = e; + Lexer(&tokens, b, e); + Lexer(&tokens, vcc_default_vcl_b, vcc_default_vcl_e); + AddToken(&tokens, EOI, e, e); + if (tokens.err) + goto done; + tokens.t = TAILQ_FIRST(&tokens.tokens); + Parse(&tokens); + if (tokens.err) + goto done; + Consistency(&tokens); + if (tokens.err) + goto done; + LocTable(&tokens); + + EmitInitFunc(&tokens); + + EmitStruct(&tokens); + + if (CheckRefs(&tokens)) + goto done; + + of = strdup("/tmp/vcl.XXXXXXXX"); + assert(of != NULL); + mktemp(of); + + sprintf(buf, + "tee /tmp/_.c |" + "cc -fpic -shared -Wl,-x -o %s -x c - ", of); + + fo = popen(buf, "w"); + assert(fo != NULL); + + vcl_output_lang_h(fo); + + sbuf_finish(tokens.fh); + fputs(sbuf_data(tokens.fh), fo); + sbuf_delete(tokens.fh); + + sbuf_finish(tokens.fc); + fputs(sbuf_data(tokens.fc), fo); + sbuf_delete(tokens.fc); + + pclose(fo); +done: + + /* Free References */ + while (!TAILQ_EMPTY(&tokens.refs)) { + r = TAILQ_FIRST(&tokens.refs); + TAILQ_REMOVE(&tokens.refs, r, list); + free(r); + } + + /* Free Tokens */ + while (!TAILQ_EMPTY(&tokens.tokens)) { + t = TAILQ_FIRST(&tokens.tokens); + TAILQ_REMOVE(&tokens.tokens, t, list); + free(t); + } + return (of); +} + +/*--------------------------------------------------------------------*/ + +char * +VCC_CompileFile(struct sbuf *sb, const char *fn) +{ + char *f, *r; + int fd, i; + struct stat st; + + fd = open(fn, O_RDONLY); + if (fd < 0) { + sbuf_printf(sb, "Cannot open file '%s': %s", + fn, strerror(errno)); + return (NULL); + } + assert(0 == fstat(fd, &st)); + f = malloc(st.st_size + 1); + assert(f != NULL); + i = read(fd, f, st.st_size); + assert(i == st.st_size); + f[i] = '\0'; + r = VCC_Compile(sb, f, NULL); + free(f); + return (r); +} + +/*--------------------------------------------------------------------*/ + +static int +VCC_T_render(FILE *f, const struct printf_info *info __unused, const void *const *args) +{ + const struct token *t; + + t = *((const struct token * const*) (args[0])); + return (fprintf(f, "%*.*s", + t->e - t->b, t->e - t->b, t->b)); +} + +static int +VCC_T_arginfo(const struct printf_info *info __unused, size_t n, int *argtypes) +{ + + if (n > 0) + argtypes[0] = PA_POINTER; + return 1; +} + +/*--------------------------------------------------------------------*/ + +void +VCC_InitCompile(const char *default_vcl) +{ + struct var *v; + + vcc_default_vcl_b = default_vcl; + vcc_default_vcl_e = strchr(default_vcl, '\0'); + assert(vcc_default_vcl_e != NULL); + + register_printf_function ('T', VCC_T_render, VCC_T_arginfo); + vcl_init_tnames(); + for (v = vars; v->name != NULL; v++) + v->len = strlen(v->name); + for (v = be_vars; v->name != NULL; v++) + v->len = strlen(v->name); +} + +#endif diff --git a/varnish-cache/lib/libvcl/vcl_priv.h b/varnish-cache/lib/libvcl/vcc_priv.h similarity index 92% rename from varnish-cache/lib/libvcl/vcl_priv.h rename to varnish-cache/lib/libvcl/vcc_priv.h index 3b0cc5ce..5ebd8179 100644 --- a/varnish-cache/lib/libvcl/vcl_priv.h +++ b/varnish-cache/lib/libvcl/vcc_priv.h @@ -2,7 +2,7 @@ * Stuff shared between main.c and fixed_token.c */ -#include "vcl_token_defs.h" +#include "vcc_token_defs.h" #define isident1(c) (isalpha(c)) #define isident(c) (isalpha(c) || isdigit(c) || (c) == '_') diff --git a/varnish-cache/lib/libvcl/vcc_token.c b/varnish-cache/lib/libvcl/vcc_token.c new file mode 100644 index 00000000..4396d648 --- /dev/null +++ b/varnish-cache/lib/libvcl/vcc_token.c @@ -0,0 +1,312 @@ +/* + * $Id$ + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vcc_priv.h" +#include "vcl_returns.h" +#include "vcc_compile.h" + +#include "libvcl.h" + +#define ERRCHK(tl) do { if ((tl)->err) return; } while (0) + +#define INDENT 2 + +/*--------------------------------------------------------------------*/ + +void +vcc_ErrToken(struct tokenlist *tl, struct token *t) +{ + + if (t->tok == EOI) + sbuf_printf(tl->sb, "end of input"); + else + sbuf_printf(tl->sb, "'%T'", t); +} + +void +vcc__ErrInternal(struct tokenlist *tl, const char *func, unsigned line) +{ + + sbuf_printf(tl->sb, "VCL compiler internal error at %s():%u\n", + func, line); + tl->err = 1; +} + +void +vcc_ErrWhere(struct tokenlist *tl, struct token *t) +{ + unsigned lin, pos, x, y; + const char *p, *l, *f, *b, *e; + + lin = 1; + pos = 0; + if (t->tok == METHOD) + return; + if (t->b >= vcc_default_vcl_b && t->b < vcc_default_vcl_e) { + f = "Default VCL code (compiled in)"; + b = vcc_default_vcl_b; + e = vcc_default_vcl_e; + } else { + f = "VCL code"; + b = tl->b; + e = tl->e; + } + for (l = p = b; p < t->b; p++) { + if (*p == '\n') { + lin++; + pos = 0; + l = p + 1; + } else if (*p == '\t') { + pos &= ~7; + pos += 8; + } else + pos++; + } + sbuf_printf(tl->sb, "In %s Line %d Pos %d\n", f, lin, pos); + x = y = 0; + for (p = l; p < e && *p != '\n'; p++) { + if (*p == '\t') { + y &= ~7; + y += 8; + while (x < y) { + sbuf_bcat(tl->sb, " ", 1); + x++; + } + } else { + x++; + y++; + sbuf_bcat(tl->sb, p, 1); + } + } + sbuf_cat(tl->sb, "\n"); + x = y = 0; + for (p = l; p < e && *p != '\n'; p++) { + if (p >= t->b && p < t->e) { + sbuf_bcat(tl->sb, "#", 1); + x++; + y++; + continue; + } + if (*p == '\t') { + y &= ~7; + y += 8; + } else + y++; + while (x < y) { + sbuf_bcat(tl->sb, "-", 1); + x++; + } + } + sbuf_cat(tl->sb, "\n"); + tl->err = 1; +} + +/*--------------------------------------------------------------------*/ + +void +vcc_NextToken(struct tokenlist *tl) +{ + tl->t = TAILQ_NEXT(tl->t, list); + if (tl->t == NULL) { + sbuf_printf(tl->sb, + "Ran out of input, something is missing or" + " maybe unbalanced (...) or {...}\n"); + tl->err = 1; + return; + } +} + +void +vcc__Expect(struct tokenlist *tl, unsigned tok, int line) +{ + if (tl->t->tok == tok) + return; + sbuf_printf(tl->sb, "Expected %s got ", vcl_tnames[tok]); + vcc_ErrToken(tl, tl->t); + sbuf_printf(tl->sb, "\n(program line %u), at\n", line); + vcc_ErrWhere(tl, tl->t); +} + +#define Expect(a, b) _Expect(a, b, __LINE__) +#define ExpectErr(a, b) do { _Expect(a, b, __LINE__); ERRCHK(a);} while (0) + +#define L(tl, foo) do { \ + tl->indent += INDENT; \ + foo; \ + tl->indent -= INDENT; \ +} while (0) + +#define C(tl, sep) do { \ + Fc(tl, 1, "VRT_count(sp, %u)%s\n", ++tl->cnt, sep); \ + tl->t->cnt = tl->cnt; \ +} while (0) + + +/*-------------------------------------------------------------------- + * Compare token to token + */ + +int +vcc_Teq(struct token *t1, struct token *t2) +{ + if (t1->e - t1->b != t2->e - t2->b) + return (0); + return (!memcmp(t1->b, t2->b, t1->e - t1->b)); +} + +/*-------------------------------------------------------------------- + * Compare ID token to string, return true of match + */ + +int +vcc_IdIs(struct token *t, const char *p) +{ + const char *q; + + assert(t->tok == ID); + for (q = t->b; q < t->e && *p != '\0'; p++, q++) + if (*q != *p) + return (0); + if (q != t->e || *p != '\0') + return (0); + return (1); +} + +/*-------------------------------------------------------------------- + * Add a token to the token list. + */ + +void +vcc_AddToken(struct tokenlist *tl, unsigned tok, const char *b, const char *e) +{ + struct token *t; + + t = calloc(sizeof *t, 1); + assert(t != NULL); + t->tok = tok; + t->b = b; + t->e = e; + TAILQ_INSERT_TAIL(&tl->tokens, t, list); + tl->t = t; + if (0) { + fprintf(stderr, "[%s %*.*s] ", + vcl_tnames[tok], + e - b, e - b, b); + if (tok == EOI) + fprintf(stderr, "\n"); + } +} + +/*-------------------------------------------------------------------- + * Lexical analysis and token generation + */ + +void +vcc_Lexer(struct tokenlist *tl, const char *b, const char *e) +{ + const char *p, *q; + unsigned u; + + for (p = b; p < e; ) { + + /* Skip any whitespace */ + if (isspace(*p)) { + p++; + continue; + } + + /* Skip '#.*\n' comments */ + if (*p == '#') { + while (p < e && *p != '\n') + p++; + continue; + } + + /* Skip C-style comments */ + if (*p == '/' && p[1] == '*') { + p += 2; + for (p += 2; p < e; p++) { + if (*p == '*' && p[1] == '/') { + p += 2; + break; + } + } + continue; + } + + /* Match for the fixed tokens (see token.tcl) */ + u = vcl_fixed_token(p, &q); + if (u != 0) { + vcc_AddToken(tl, u, p, q); + p = q; + continue; + } + + /* Match strings, with \\ and \" escapes */ + if (*p == '"') { + for (q = p + 1; q < e; q++) { + if (*q == '\\' && q[1] == '\\') + q++; + else if (*q == '\\' && q[1] == '"') + q++; + else if (*q == '"') { + q++; + break; + } + } + vcc_AddToken(tl, CSTR, p, q); + p = q; + continue; + } + + /* Match Identifiers */ + if (isident1(*p)) { + for (q = p; q < e; q++) + if (!isident(*q)) + break; + if (isvar(*q)) { + for (; q < e; q++) + if (!isvar(*q)) + break; + vcc_AddToken(tl, VAR, p, q); + } else { + vcc_AddToken(tl, ID, p, q); + } + p = q; + continue; + } + + /* Match numbers { [0-9]+ } */ + if (isdigit(*p)) { + for (q = p; q < e; q++) + if (!isdigit(*q)) + break; + vcc_AddToken(tl, CNUM, p, q); + p = q; + continue; + } + vcc_AddToken(tl, EOI, p, p + 1); + sbuf_printf(tl->sb, "Syntax error at\n"); + vcc_ErrWhere(tl, tl->t); + return; + } +} diff --git a/varnish-cache/lib/libvcl/vcl_token_defs.h b/varnish-cache/lib/libvcl/vcc_token_defs.h similarity index 95% rename from varnish-cache/lib/libvcl/vcl_token_defs.h rename to varnish-cache/lib/libvcl/vcc_token_defs.h index d5a2ec0d..93b77827 100644 --- a/varnish-cache/lib/libvcl/vcl_token_defs.h +++ b/varnish-cache/lib/libvcl/vcc_token_defs.h @@ -3,7 +3,7 @@ * * NB: This file is machine generated, DO NOT EDIT! * - * Edit vcl_gen_fixed_token.tcl instead + * Edit vcc_gen_fixed_token.tcl instead */ #define LOW_TOKEN 128