Plan 9 from Bell Labs’s /usr/web/sources/contrib/quanstro/root/sys/src/cmd/ndb/vrfy.y

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


%{
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <ip.h>

typedef struct T T;
struct T{
	T	*next;
	T	*down;
	char	*s;
	char	*t;
	int	line;
	char	*file;
};
#pragma	varargck	type	"T"	T*

Biobuf	in;
char	*file;
char	*ipmask;
int	line;
int	factotum;
T	*dblist;
T	*ll0;
T	**ll;
T	*tab0;
T	**tab;
int	fd;

extern	int	yyparse(void);
extern	void	yyerror(char*, ...);

#pragma	varargck	argpos	yyerror		1
#pragma	varargck	argpos	tokerror		2

char*
nonil(char *s)
{
	if(s == nil)
		return "";
	return s;
}

int
Tfmt(Fmt *f)
{
	int sharp;
	T *t, *t0;

	t0 = va_arg(f->args, T*);
	sharp = f->flags & FmtSharp;
	for(; t0 != nil; t0 = t0->down){
		if((t = t0) != nil)
			fmtprint(f, "%s=%s", t->s, nonil(t->t));
		while(t = t->next)
			fmtprint(f, " %s=%s", t->s, nonil(t->t));
		if(sharp == 0)
			break;
		if(t0->down != nil)
			fmtprint(f, "\n");
	}
	return 0;
}

T*
newt(char *s, char *t)
{
	T *x;

	x = malloc(sizeof *x);
	if(x == nil)
		sysfatal("malloc: %r");
	memset(x, 0, sizeof *x);
	x->s = s;
	x->t = t;
	x->line = line;
	x->file = file;
	return x;
}

static char *nakedtab[] = {
	"database",
	"soa",
	"trampok",
};

int
cknaked(char *s)
{
	int i;

	for(i = 0; i < nelem(nakedtab); i++)
		if(strcmp(nakedtab[i], s) == 0)
			return 0;
	yyerror("naked token %s", s);
	return -1;
}

%}

%union{
	T	*t;
	char	*s;
}

%type	<s>	rhs
%token	<s>	S
%start	db
%%
db	:
	| db '\n'
	| db tuples '\n'
	;
tuples:	tuple {
		*tab = ll0;
		tab = &ll0->down;

		ll0 = nil;
		ll = &ll0;
	}
	| tuple tuples
	;
tuple:	S rhs {
		T *x;

		if($2 == nil)
			cknaked($1);
		x = *ll = newt($1, $2);
		ll = &x->next;
	}
	;
rhs:		{ $$ = nil; }
	| '=' S	{ $$ = $2; }
	;
%%
void
freet(T *t)
{
	T *n, *d;

	for(; t != nil; t = d){
		d = t->down;
		for(; t != nil; t = n){
			n = t->next;
			free(t->s);
			free(t->t);
			free(t);
		}
	}
}

void
yyinit(char *f)
{
	file = f;
	line = 1;
	fd = open(file, OREAD);
	if(fd == -1)
		sysfatal("open: %r");
	if(Binit(&in, fd, OREAD) == -1)
		sysfatal("Binit: %r");
}

void
yyterm(void)
{
	freet(tab0);
	ll0 = nil;
	tab0 = nil;
	ll = &ll0;
	tab = &tab0;
}

void
yyerror(char *fmt, ...)
{
	char buf[256], *p, *e;
	va_list arg;
	static int nerrors;

	e = buf + sizeof buf;
	va_start(arg, fmt);
	p = seprint(buf, e, "%s:%d ", file, line);
	p = vseprint(p, e, fmt, arg);
	p = seprint(p, e, "\n");
	va_end(arg);
	write(2, buf, p - buf);
	nerrors++;
	if(nerrors > 5)
		sysfatal("too many errors");
}

void
tokerror(T *t, char *fmt, ...)
{
	char buf[256], *p, *e;
	va_list arg;

	e = buf + sizeof buf;
	va_start(arg, fmt);
	p = seprint(buf, e, "%s:%d ", t->file, t->line);
	p = vseprint(p, e, fmt, arg);
	p = seprint(p, e, "\n");
	va_end(arg);
	write(2, buf, p - buf);
}

enum{
	Base,
	Seeneq,
};

int
yylex(void)
{
	char buf[128], *s;
	int c, i;
	Rune r;
	static int state = Base;
	static int quoting;

	if(quoting != 0){
		yyerror("bad quote");
		quoting = 0;
	}
	i = 0;
again:
	c = Bgetrune(&in);
	if(c == Beof)
		return 0;
	r = c;
	switch(r){
	case '
':
	case '\0':
		yyerror("bad character %#ux\n", r);
		goto again;
	case '#':
		for(;;){
			switch(Bgetrune(&in)){
			case Beof:
				yyerror("EOF in comment");
				return 0;
			case '\n':
				Bungetrune(&in);
				goto again;
			}
		}
	case '\n':
		if(state != Base){
			Bungetrune(&in);
			goto tok;
		}
		line++;
		switch(Bgetrune(&in)){
		case Beof:
			return '\n';
		case ' ':
		case '\t':
			goto again;
		}
		Bungetrune(&in);
		return '\n';
	case ' ':
	case '\t':
		if(state != Base){
			Bungetrune(&in);
			goto tok;
		}
		goto again;
	case '=':
		if(state != Base)
			break;
		state = Seeneq;
		return '=';
	default:
		break;
	}

	/* token */
	if(r == '"'){
		quoting = 1;
		c = Bgetrune(&in);
		r = c;
	}
	for(;;){
		if(i >= sizeof buf - UTFmax - 1){
			yyerror("token too long");
			break;
		}
		i += runetochar(buf + i, &r);
		c = Bgetrune(&in);
		if(c == Beof)
			break;
		r = c;
		switch(r){
		case '
':
		case '\0':
			yyerror("bad character %#ux\n", r);
			goto tok;
		case ' ':
		case '\t':
			if(quoting)
				break;
		case '\n':
			Bungetrune(&in);
			goto tok;
		case '=':
			if(state == Base){
				Bungetrune(&in);
				goto tok;
			}
			break;
		case '\'':
			if(factotum)
				goto quote;
			break;
		case '"':
			if(!factotum)
			if(quoting == 1){
				quoting = 0;
				goto tok;
			}
			break; 
		case '#':
			yyerror("# in token");
		}
	}
tok:
	if(state == Base && i == 0)
		yyerror("name is nil");
	if(state == Seeneq)
		state = Base;
	buf[i] = 0;
	s = strdup(buf);
	if(s == nil)
		sysfatal("malloc: %r");
	yylval.s = s;
	return S;

quote:
	quoting = 1;
	for(;;){
		/* extra space for ' if quoting fails.  ndb is wierd */
		if(i >= sizeof buf - UTFmax - 1 - 1){
			yyerror("token too long");
			break;
		}
		i += runetochar(buf + i, &r);

		c = Bgetrune(&in);
		if(c == Beof)
			break;
		r = c;
		switch(r){
		case '\n':
			/* wierd!  this is actually what ndb does */
			Bungetrune(&in);
			memmove(buf, buf + 1, i);
			buf[0] = '\'';
			goto tok;
		case '\'':
			if(Bgetrune(&in) == '\'')
				break;
			Bungetrune(&in);
			goto tok;
		}
	}
	goto tok;
}

void
go(char *file)
{
	yyinit(file);
	yyparse();
	close(fd);
}

int
parseipxmask(uchar *ip, uchar *m, char *s0)
{
	uchar tmp[IPaddrlen];
	char *msk, s[4*8+4];

	snprint(s, sizeof s, "%s", s0);
	msk = strchr(s, '/');
	if(msk == nil){
		if(parseipmask(m, "/120") == -1)
			return -1;
	}else{
		if(parseipmask(m, msk) == -1)
			return -1;
		*msk = 0;
	}
	if(parseip(tmp, s) == -1)
		return -1;
	maskip(tmp, m, ip);
	return 0;
}

void
checkip(void)
{
	uchar m0[IPaddrlen], mask[IPaddrlen], ip1[IPaddrlen], m1[IPaddrlen];
	int havemsk;
	T *d, *t;

	havemsk = 0;
	if(ipmask != nil)
	if(parseipxmask(m0, mask, ipmask) == 0)
		havemsk = 1;
	for(d = tab0; d != nil; d = d->down)
		for(t = d; t != nil; t = t->next){
			if(strcmp(t->s, "ip") != 0)
				continue;
			if(parseip(ip1, t->t) == -1){
				tokerror(t, "bad ip %T", d);
				continue;
			}
			if(havemsk == 0)
				continue;
			maskip(ip1, mask, m1);
			if(ipcmp(m0, m1) != 0)
				tokerror(t, "wrong file: %I", ip1);
		}
}

typedef struct B B;
struct B {
	uchar	ea[IPaddrlen];
	T	*d[10];
	int	nd;
};

static	B	*btab;
static	uint	nbtab;
static	uint	nbtaba;

int
addrcmp(uchar *a, uchar *b)
{
	int d;

	d = memcmp(a, b, 6);
	if(d > 0)
		return 1;
	if(d < 0)
		return -1;
	return 0;
}

static B*
bsearch(uchar *ea)
{
	B *tab, *m;
	int n, i, d;

	tab = btab;
	for(n = nbtab; n > 0;){
		i = n/2;
		m = tab+i;
		d = addrcmp(m->ea, ea);
		if(d == 0)
			return m;
		if(d < 0){
			tab += i+1;
			n -= i+1;
		}else
			n = i;
	}
	return nil;
}

void
binsert(B *m, T *d, uchar *ea)
{
	char buf[256], *p, *e;
	int i;
	T **x;

	if(m == nil){
		if(nbtab + 1 >= nbtaba){
			nbtaba = nbtab + 1 << 1;
			btab = realloc(btab, sizeof btab[0]*nbtaba);
			if(btab == nil)
				sysfatal("realloc: %r");
		}
		for(m=btab+nbtab; m > btab && addrcmp(m[-1].ea, ea) > 0; m--)
			m[0] = m[-1];
		memset(m, 0, sizeof *m);
		m->nd = 0;
		memcpy(m->ea, ea, IPaddrlen);
		nbtab++;
	}
	if(m->nd < nelem(m->d))
		m->d[m->nd++] = d;
	if(m->nd > 1){
		x = m->d;
		p = buf;
		e = buf + sizeof buf;
		for(i = 0; i < m->nd - 1; i++)
			p = seprint(p, e, "%s:%d ", x[i]->file, x[i]->line);
		tokerror(d, "duplicate ea %E %s", m->ea, buf);
	}
}

void
prdb(void)
{
	int i;

	for(i = 0; i < nbtab; i++)
		print("%E\n", btab[i].ea);
}

void
checkether(void)
{
	uchar ea[IPaddrlen];
	T *d, *t;

	for(d = tab0; d != nil; d = d->down)
		for(t = d; t != nil; t = t->next){
			if(strcmp(t->s, "ether") != 0)
				continue;
			if(strspn(t->t, "0123456789abcdef") != 12)
				tokerror(t, "bad ea %s", t->t);
			parseether(ea, t->t);
			binsert(bsearch(ea), d, ea);
		}
}

static char *soaattr[] = {
	"refresh",
	"ttl",
	"ns",
	"mx",
	"pref",
	"mbox",
};

static char *cattrtab[] = {
	"clog",			/* only in consoledb */
	"console",
	"dev",
	"gid",
	"group",
	"speed",
	"uid",
	"openondemand",
};

static char *xattrtab[] = {
	"auth",
	"authdom",
	"bootf",
	"cname",
	"console",
	"database",
	"dns",
	"dnsdomain",
	"dom",
	"el",
	"ether",
	"file",
	"fs",
	"gw",
	"imap",
	"ip",
	"ipgw",
	"ipmask",
	"ipnet",
	"mb",
	"mbox",
	"mx",
	"ns",
	"pref",
	"proto",
	"refresh",
	"smtp",
	"soa",
	"sys",
	"trampok",
	"ttl",
	"txtrr",

	"pri",
	"srv",
	"weight",

	"gre",
	"il",
	"ipv4proto",
	"port",
	"protocol",
	"restricted",
	"tcp",
	"udp",

	"uid",
	"hostid"
};

char	**attrtab	= xattrtab;
int	nattrtab	= nelem(xattrtab);

void
checkattrs(void)
{
	int i;
	T *d, *t;

	for(d = tab0; d != nil; d = d->down)
		for(t = d; t != nil; t = t->next){
			for(i = 0; i < nattrtab; i++)
				if(strcmp(t->s, attrtab[i]) == 0)
					break;
			if(i == nattrtab)
				tokerror(t, "unknown attr %s", t->s);
		}
}

void
usage(void)
{
	fprint(2, "usage: ndb/vrfy [-m ip/mask]  [file ...]\n");
	exits("usage");
}

void
main(int argc, char **argv)
{
	int i;

	fmtinstall('T', Tfmt);
	fmtinstall('I', eipfmt);
	fmtinstall('M', eipfmt);
	fmtinstall('E', eipfmt);
	ARGBEGIN{
	case 'c':
		attrtab = cattrtab;
		nattrtab = nelem(cattrtab);
		break;
	case 'm':
		ipmask = EARGF(usage());
		break;
	default:
		usage();
	}ARGEND
	yyterm();
	for(i = 0; i < argc; i++)
		go(argv[i]);
	checkip();
	checkether();
	checkattrs();
	yyterm();

	exits("");
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.