this is otherwise known as how to shove a round peg in a
square hole. but the inefficency doesn't much matter.
i have an ancient unix program called tel. i wrote it in
1988 so it has no relation to tel(1). tel is a simple
address book. recently it's been abused to store account
information that would be useless in factotum, such as dsl
account passwords. the format has been blocks of name tab
value seperated by blank lines.
i've had two problems with tel. foremost, it's insecure.
but it's always been a pain to search.
secstore would be secure enough and ndb could be easier to
search. one problem. ndb assumes full exact matches and i
don't often remember exactly.
the solution to that was ndb/requery, which matches regular
expressions but is otherwise the same as ndb/query. for
example:
; ndb/requery dom aska dom
aska.quanstro.net
oldaska.quanstro.net
stel is a little wrapper that takes care of security details.
- erik
/*
* pattern search the network database for matches
*/
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <ndb.h>
#include <regexp.h>
static int flag[127];
static Biobuf bout;
void
usage(void)
{
fprint(2, "usage: pquery [-i] [-f ndbfile] attr value rattr\n");
exits("usage");
}
static void
prmatch(Ndbtuple *nt, char *rattr)
{
for(; nt; nt = nt->entry)
if(rattr && strcmp(nt->attr, rattr) == 0)
Bprint(&bout, "%s\n", nt->val);
else if(!rattr)
Bprint(&bout, "%s=%s ", nt->attr, nt->val);
if(!rattr)
Bprint(&bout, "\n");
}
char*
lower(char *s)
{
char *p, c;
for(p = s; c = *p; p++)
if(c >= 'A' && c <= 'Z')
*p = c - ('A' - 'a');
return s;
}
int
iregex(Reprog *re, char *val)
{
char *s;
int r;
if(!flag['i'])
return regexec(re, val, 0, 0);
s = lower(strdup(val));
r = regexec(re, s, 0, 0);
free(s);
return r;
}
Ndbtuple*
research(char *dbfile, Ndb *db, char *attr, char *valre, char *rattr)
{
int m;
Reprog *re;
Ndb *p;
Ndbtuple *nt, *t;
re = regcomp(valre);
if(re == nil)
sysfatal("regcomp: %r");
for(m = 0; t = ndbparse(db); m = 0){
/* why doesn't ndb do this for me? */
if(strcmp(t->attr, "database") == 0){
for(nt = t; nt; nt = nt->entry)
if(strcmp(nt->attr, "file") == 0)
if(strcmp(nt->val, dbfile) != 0){
p = ndbopen(nt->val);
if(p == nil)
sysfatal("bad ndb file: %s\n",
nt->val);
research(nt->val, p, attr, valre,
rattr);
ndbclose(p);
}
}
for(nt = t; nt; nt = nt->entry){
if(flag['a'] || strcmp(nt->attr, attr) == 0)
if(iregex(re, nt->val))
m = 1;
}
if(m)
prmatch(t, rattr);
ndbfree(t);
}
free(re);
return 0;
}
void
main(int argc, char **argv)
{
char *dbfile, *attr, *val, *rattr;
Ndb *db;
dbfile = "/lib/ndb/local";
ARGBEGIN{
case 'f':
dbfile = EARGF(usage());
break;
case 'a':
case 'i':
flag[ARGC()] = 1;
break;
default:
usage();
}ARGEND;
attr = nil;
if(flag['a'] == 0){
attr = *argv++;
if(attr == nil)
usage();
}
val = *argv++;
if(val == nil)
usage();
if(flag['i'])
val = lower(val);
rattr = *argv;
if(Binit(&bout, 1, OWRITE) == -1)
sysfatal("Binit: %r");
db = ndbopen(dbfile);
if(db == nil){
fprint(2, "%s: no db files\n", argv0);
exits("no db");
}
research(dbfile, db, attr, val, rattr);
ndbclose(db);
Bterm(&bout);
exits(0);
}
#!/bin/rc
rfork en
if(~ $service cpu){
echo dont run on cpu server>[1=2]
exit usage
}
ramfs -p
cd /tmp
auth/secstore -g accounts.db
if(~ $#* 0 1)
ndb/requery -f accounts.db -ai $*
if not
ndb/requery -f accounts.db -i $*