|
|
@ -12,6 +12,11 @@ |
|
|
|
|
|
|
|
static bool verbose = false; |
|
|
|
|
|
|
|
struct bolt_file { |
|
|
|
const char *prefix; |
|
|
|
const char *contents; |
|
|
|
}; |
|
|
|
|
|
|
|
/* Turn any whitespace into a single space. */ |
|
|
|
static char *canonicalize(char *str) |
|
|
|
{ |
|
|
@ -36,71 +41,91 @@ static char *canonicalize(char *str) |
|
|
|
return str; |
|
|
|
} |
|
|
|
|
|
|
|
static char **get_bolt_files(const char *dir) |
|
|
|
static void get_files(const char *dir, const char *subdir, |
|
|
|
struct bolt_file **files) |
|
|
|
{ |
|
|
|
char *path = path_join(NULL, dir, subdir); |
|
|
|
DIR *d = opendir(path); |
|
|
|
size_t n = tal_count(*files); |
|
|
|
struct dirent *e; |
|
|
|
char **bolts = tal_arr(NULL, char *, 0); |
|
|
|
DIR *d = opendir(dir); |
|
|
|
|
|
|
|
if (!d) |
|
|
|
err(1, "Opening BOLT dir %s", dir); |
|
|
|
err(1, "Opening BOLT dir %s", path); |
|
|
|
|
|
|
|
while ((e = readdir(d)) != NULL) { |
|
|
|
char *endp; |
|
|
|
unsigned long l; |
|
|
|
|
|
|
|
/* Must start with the bold number. */ |
|
|
|
l = strtoul(e->d_name, &endp, 10); |
|
|
|
if (endp == e->d_name) |
|
|
|
continue; |
|
|
|
int preflen; |
|
|
|
|
|
|
|
/* Must end in .md */ |
|
|
|
if (!strends(e->d_name, ".md")) |
|
|
|
continue; |
|
|
|
|
|
|
|
if (l >= tal_count(bolts)) |
|
|
|
tal_resizez(&bolts, l+1); |
|
|
|
/* Prefix is anything up to - */ |
|
|
|
preflen = strspn(e->d_name, |
|
|
|
"0123456789" |
|
|
|
"abcdefghijklmnopqrstuvwxyz" |
|
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"); |
|
|
|
if (!preflen) |
|
|
|
continue; |
|
|
|
if (preflen + strlen(".md") != strlen(e->d_name) |
|
|
|
&& e->d_name[preflen] != '-') |
|
|
|
continue; |
|
|
|
|
|
|
|
if (verbose) |
|
|
|
printf("Found bolt %s: #%lu\n", e->d_name, l); |
|
|
|
printf("Found bolt %.*s\n", preflen, e->d_name); |
|
|
|
|
|
|
|
bolts[l] = canonicalize(grab_file(NULL, |
|
|
|
path_join(NULL, dir, |
|
|
|
e->d_name))); |
|
|
|
tal_resize(files, n+1); |
|
|
|
(*files)[n].prefix = tal_strndup(*files, |
|
|
|
e->d_name, preflen); |
|
|
|
(*files)[n].contents |
|
|
|
= canonicalize(grab_file(*files, |
|
|
|
path_join(path, path, |
|
|
|
e->d_name))); |
|
|
|
n++; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static struct bolt_file *get_bolt_files(const char *dir) |
|
|
|
{ |
|
|
|
struct bolt_file *bolts = tal_arr(NULL, struct bolt_file, 0); |
|
|
|
|
|
|
|
get_files(dir, "bolts", &bolts); |
|
|
|
get_files(dir, "early-drafts", &bolts); |
|
|
|
return bolts; |
|
|
|
} |
|
|
|
|
|
|
|
static char *find_bolt_ref(char *p, size_t *len, size_t *bolt) |
|
|
|
static char *find_bolt_ref(char **p, size_t *len) |
|
|
|
{ |
|
|
|
for (;;) { |
|
|
|
char *end; |
|
|
|
char *bolt, *end; |
|
|
|
size_t preflen; |
|
|
|
|
|
|
|
/* BOLT #X: */ |
|
|
|
p = strstr(p, "BOLT"); |
|
|
|
if (!p) |
|
|
|
*p = strstr(*p, "BOLT"); |
|
|
|
if (!*p) |
|
|
|
return NULL; |
|
|
|
p += 4; |
|
|
|
while (cisspace(*p)) |
|
|
|
p++; |
|
|
|
if (*p != '#') |
|
|
|
continue; |
|
|
|
p++; |
|
|
|
*bolt = strtoul(p, &end, 10); |
|
|
|
if (!*bolt || p == end) |
|
|
|
*p += 4; |
|
|
|
while (cisspace(**p)) |
|
|
|
(*p)++; |
|
|
|
if (**p != '#') |
|
|
|
continue; |
|
|
|
p = end; |
|
|
|
while (cisspace(*p)) |
|
|
|
p++; |
|
|
|
if (*p != ':') |
|
|
|
(*p)++; |
|
|
|
|
|
|
|
preflen = strcspn(*p, " :"); |
|
|
|
bolt = tal_strndup(NULL, *p, preflen); |
|
|
|
|
|
|
|
(*p) += preflen; |
|
|
|
while (cisspace(**p)) |
|
|
|
(*p)++; |
|
|
|
if (**p != ':') |
|
|
|
continue; |
|
|
|
p++; |
|
|
|
(*p)++; |
|
|
|
|
|
|
|
end = strstr(p, "*/"); |
|
|
|
end = strstr(*p, "*/"); |
|
|
|
if (!end) |
|
|
|
*len = strlen(p); |
|
|
|
*len = strlen(*p); |
|
|
|
else |
|
|
|
*len = end - p; |
|
|
|
return p; |
|
|
|
*len = end - *p; |
|
|
|
return bolt; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -108,7 +133,7 @@ static char *code_to_regex(const char *code, size_t len, bool escape) |
|
|
|
{ |
|
|
|
char *pattern = tal_arr(NULL, char, len*2 + 1), *p; |
|
|
|
size_t i; |
|
|
|
bool after_nl = false; |
|
|
|
bool after_nl = true; |
|
|
|
|
|
|
|
/* We swallow '*' if first in line: block comments */ |
|
|
|
p = pattern; |
|
|
@ -153,46 +178,81 @@ static char *code_to_regex(const char *code, size_t len, bool escape) |
|
|
|
return canonicalize(pattern); |
|
|
|
} |
|
|
|
|
|
|
|
static void fail(const char *filename, const char *raw, const char *pos, |
|
|
|
size_t len, const char *bolt) |
|
|
|
/* Moves *pos to start of line. */ |
|
|
|
static unsigned linenum(const char *raw, const char **pos) |
|
|
|
{ |
|
|
|
unsigned line = 0; /* Out-by-one below */ |
|
|
|
const char *l = raw; |
|
|
|
const char *l = raw, *point = *pos; |
|
|
|
|
|
|
|
while (l < pos) { |
|
|
|
while (l < point) { |
|
|
|
*pos = l; |
|
|
|
l = strchr(l, '\n'); |
|
|
|
line++; |
|
|
|
if (!l) |
|
|
|
l = pos + strlen(pos); |
|
|
|
else |
|
|
|
l++; |
|
|
|
break; |
|
|
|
l++; |
|
|
|
} |
|
|
|
return line; |
|
|
|
} |
|
|
|
|
|
|
|
if (bolt) { |
|
|
|
char *try; |
|
|
|
|
|
|
|
fprintf(stderr, "%s:%u:%.*s\n", filename, line, |
|
|
|
(int)(l - pos), pos); |
|
|
|
/* Try to find longest match, as a hint. */ |
|
|
|
try = code_to_regex(pos, len, false); |
|
|
|
while (strlen(try)) { |
|
|
|
const char *p = strstr(bolt, try); |
|
|
|
if (p) { |
|
|
|
fprintf(stderr, "Closest match: %s...[%.20s]\n", |
|
|
|
try, p + strlen(try)); |
|
|
|
break; |
|
|
|
} |
|
|
|
try[strlen(try)-1] = '\0'; |
|
|
|
static void fail_mismatch(const char *filename, |
|
|
|
const char *raw, const char *pos, |
|
|
|
size_t len, struct bolt_file *bolt) |
|
|
|
{ |
|
|
|
unsigned line = linenum(raw, &pos); |
|
|
|
char *try; |
|
|
|
|
|
|
|
fprintf(stderr, "%s:%u:mismatch:%.*s\n", |
|
|
|
filename, line, (int)strcspn(pos, "\n"), pos); |
|
|
|
/* Try to find longest match, as a hint. */ |
|
|
|
try = code_to_regex(pos + strcspn(pos, "\n"), len, false); |
|
|
|
while (strlen(try)) { |
|
|
|
const char *p = strstr(bolt->contents, try); |
|
|
|
if (p) { |
|
|
|
fprintf(stderr, "Closest match: %s...[%.20s]\n", |
|
|
|
try, p + strlen(try)); |
|
|
|
break; |
|
|
|
} |
|
|
|
} else { |
|
|
|
fprintf(stderr, "%s:%u:Unknown bolt\n", filename, line); |
|
|
|
} |
|
|
|
try[strlen(try)-1] = '\0'; |
|
|
|
} |
|
|
|
exit(1); |
|
|
|
} |
|
|
|
|
|
|
|
static void fail_nobolt(const char *filename, |
|
|
|
const char *raw, const char *pos, |
|
|
|
const char *bolt_prefix) |
|
|
|
{ |
|
|
|
unsigned line = linenum(raw, &pos); |
|
|
|
|
|
|
|
fprintf(stderr, "%s:%u:unknown bolt %s\n", |
|
|
|
filename, line, bolt_prefix); |
|
|
|
exit(1); |
|
|
|
} |
|
|
|
|
|
|
|
static struct bolt_file *find_bolt(const char *bolt_prefix, |
|
|
|
struct bolt_file *bolts) |
|
|
|
{ |
|
|
|
size_t i, n = tal_count(bolts); |
|
|
|
size_t boltnum; |
|
|
|
|
|
|
|
for (i = 0; i < n; i++) |
|
|
|
if (streq(bolts[i].prefix, bolt_prefix)) |
|
|
|
return bolts+i; |
|
|
|
|
|
|
|
/* Now search for numerical match. */ |
|
|
|
boltnum = atoi(bolt_prefix); |
|
|
|
if (boltnum) { |
|
|
|
for (i = 0; i < n; i++) |
|
|
|
if (atoi(bolts[i].prefix) == boltnum) |
|
|
|
return bolts+i; |
|
|
|
} |
|
|
|
|
|
|
|
return NULL; |
|
|
|
} |
|
|
|
|
|
|
|
int main(int argc, char *argv[]) |
|
|
|
{ |
|
|
|
char **bolts; |
|
|
|
struct bolt_file *bolts; |
|
|
|
int i; |
|
|
|
|
|
|
|
err_set_progname(argv[0]); |
|
|
@ -211,8 +271,8 @@ int main(int argc, char *argv[]) |
|
|
|
bolts = get_bolt_files(argv[1]); |
|
|
|
|
|
|
|
for (i = 2; i < argc; i++) { |
|
|
|
char *f = grab_file(NULL, argv[i]), *p; |
|
|
|
size_t len, bolt; |
|
|
|
char *f = grab_file(NULL, argv[i]), *p, *bolt; |
|
|
|
size_t len; |
|
|
|
if (!f) |
|
|
|
err(1, "Loading %s", argv[i]); |
|
|
|
|
|
|
@ -220,16 +280,17 @@ int main(int argc, char *argv[]) |
|
|
|
printf("Checking %s...\n", argv[i]); |
|
|
|
|
|
|
|
p = f; |
|
|
|
while ((p = find_bolt_ref(p, &len, &bolt)) != NULL) { |
|
|
|
while ((bolt = find_bolt_ref(&p, &len)) != NULL) { |
|
|
|
char *pattern = code_to_regex(p, len, true); |
|
|
|
if (bolt >= tal_count(bolts) || !bolts[bolt]) |
|
|
|
fail(argv[i], f, p, len, NULL); |
|
|
|
if (!tal_strreg(f, bolts[bolt], pattern, NULL)) |
|
|
|
fail(argv[i], f, p, len, bolts[bolt]); |
|
|
|
struct bolt_file *b = find_bolt(bolt, bolts); |
|
|
|
if (!b) |
|
|
|
fail_nobolt(argv[i], f, p, bolt); |
|
|
|
if (!tal_strreg(f, b->contents, pattern, NULL)) |
|
|
|
fail_mismatch(argv[i], f, p, len, b); |
|
|
|
|
|
|
|
if (verbose) |
|
|
|
printf(" Found %.10s... in %zu\n", |
|
|
|
p, bolt); |
|
|
|
printf(" Found %.10s... in %s\n", |
|
|
|
p, b->prefix); |
|
|
|
p += len; |
|
|
|
} |
|
|
|
tal_free(f); |
|
|
|