1
0
mirror of git://projects.qi-hardware.com/wernermisc.git synced 2024-11-15 08:34:05 +02:00
wernermisc/qpkg/gobble.c
Werner Almesberger ed0281f306 qpkg/gobble.c (EXPECT): added variant that just skips over expected text
If the package database is sane, then we don't need to check that all
the field names match what we expect. This saves a few more CPU cycles.
However, if anything goes wrong, the consequences may be unpleasant.
Thus, we default to the slow but safe approach.
2010-11-19 22:29:19 -03:00

436 lines
6.8 KiB
C

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include "util.h"
#include "id.h"
#include "qpkg.h"
#include "gobble.h"
#define CHARS_AFTER_ERROR 20
#ifdef BREAKNECK_SPEED
#define EXPECT(s) do { buf += sizeof(s)-1; } while (0)
#else /* !BREAKNECK_SPEED */
#define EXPECT(s) \
do { \
if (end-buf < sizeof(s)-1) \
FAIL; \
if (memcmp(buf, s, sizeof(s)-1)) \
FAIL; \
buf += sizeof(s)-1; \
} \
while (0)
#endif
#define NEXT (buf == end ? '?' : *buf++)
#define WHITESPACE \
do { \
if (buf == end) \
FAIL; \
if (*buf == '\n') \
break; \
if (!isspace(*buf)) \
break; \
buf++; \
} \
while (0)
#define ISTERM(c) \
((c) == ' ' || (c) == '\t' || (c) == '\n' || \
(c) == ',' || (c) == ')')
#define ID(tree) \
({ \
const char *start; \
\
if (buf == end) \
FAIL; \
start = buf; \
while (buf != end && !ISTERM(*buf)) \
buf++; \
make_id(tree, start, buf-start); \
})
#define FAIL \
do { \
failed_at = __LINE__; \
goto fail; \
} \
while (0)
#define DONE goto done
static void compact_pkg(struct pkg *new)
{
struct pkg *old;
for (old = new->more; old; old = old->more)
if (old->version == new->version)
goto compact;
return;
compact:
new->id->jrb->val = new->more;
old->installed = old->installed || new->installed;
/* @@@ we may leak a little */
free(new);
}
static void gobble_buf(const char *name, const char *buf, size_t len)
{
const char *end = buf+len;
int lineno = 1;
struct pkg *pkg = NULL; /* current package */
struct ref **anchor = NULL;
int i, failed_at = 0;
initial:
if (buf == end)
DONE;
if (*buf == '\n') {
lineno++;
buf++;
goto initial;
}
/* decode the tag */
switch (*buf++) {
case 'A': /* Architecture // Auto-Installed */
switch (NEXT) {
case 'r':
EXPECT("chitecture:");
/* @@@ use later */
goto skip_data;
case 'u':
EXPECT("to-Installed:");
goto skip_data;
default:
FAIL;
}
case 'C': /* Conflicts // Conffiles */
EXPECT("onf");
switch (NEXT) {
case 'l':
EXPECT("icts:");
goto conflicts;
case 'f':
EXPECT("iles:");
goto skip_data;
default:
FAIL;
}
case 'D': /* Depends, Description */
EXPECT("e");
switch (NEXT) {
case 'p':
EXPECT("ends:");
goto depends;
case 's':
EXPECT("cription:");
goto skip_data;
default:
FAIL;
}
case 'F': /* Filename */
EXPECT("ilename:");
goto filename;
case 'H': /* HomePage, Homepage */
EXPECT("ome");
switch (NEXT) {
case 'P':
case 'p':
EXPECT("age:");
goto skip_data;
default:
FAIL;
}
case 'I': /* Installed-Time */
EXPECT("nstalled-Time:");
goto skip_data;
case 'L': /* License */
EXPECT("icense:");
goto skip_data;
case 'M': /* Maintainer, MD5Sum */
switch (NEXT) {
case 'a':
EXPECT("intainer:");
goto skip_data;
case 'D':
EXPECT("5Sum:");
goto skip_data;
default:
FAIL;
}
case 'O': /* OE */
EXPECT("E:");
goto skip_data;
case 'P': /* Package, Priority, Provides */
switch (NEXT) {
case 'a':
EXPECT("ckage:");
goto package;
case 'r':
break;
default:
FAIL;
}
switch (NEXT) {
case 'i':
EXPECT("ority:");
goto skip_data;
case 'o':
EXPECT("vides:");
goto provides;
default:
FAIL;
}
case 'R': /* Recommends, Replaces */
EXPECT("e");
switch (NEXT) {
case 'c':
EXPECT("ommends:");
goto skip_data;
case 'p':
EXPECT("laces:");
goto skip_data;
default:
FAIL;
}
case 'S': /* Section, Size, Source, Suggests // Status */
switch (NEXT) {
case 'e':
EXPECT("ction:");
goto skip_data;
case 'i':
EXPECT("ze:");
goto skip_data;
case 'o':
EXPECT("urce:");
goto skip_data;
case 'u':
EXPECT("ggests:");
goto skip_data;
case 't':
EXPECT("atus:");
goto status;
default:
FAIL;
}
case 'V': /* Version */
EXPECT("ersion:");
goto version;
default:
FAIL;
}
conflicts:
anchor = &pkg->conflicts;
goto list_with_version;
depends:
anchor = &pkg->depends;
goto list_with_version;
package:
if (pkg)
compact_pkg(pkg);
WHITESPACE;
pkg = alloc_type(struct pkg);
pkg->id = ID(packages);
pkg->more = pkg->id->jrb->val;
pkg->id->jrb->val = pkg;
pkg->version = NULL;
pkg->conflicts = pkg->depends = NULL;
pkg->filename = NULL;
pkg->installed = 0;
pkg->mark = 0;
goto eol;
version:
WHITESPACE;
if (pkg->version)
FAIL;
pkg->version = ID(versions);
goto eol;
provides:
/* @@@ later */
goto skip_data;
status:
pkg->installed = 1;
/* @@@ later */
goto skip_data;
filename:
WHITESPACE;
pkg->filename = buf;
goto skip_data;
eol:
while (buf != end) {
if (*buf == ' ' || *buf == '\t') {
buf++;
continue;
}
if (*buf++ != '\n')
FAIL;
lineno++;
if (buf == end)
DONE;
if (*buf == ' ' || *buf == '\t')
FAIL;
goto initial;
}
DONE;
skip_data:
while (buf != end) {
if (*buf++ != '\n')
continue;
lineno++;
if (buf == end)
DONE;
if (*buf != ' ' && *buf != '\t')
goto initial;
}
DONE;
list_with_version:
while (1) {
struct ref *ref;
WHITESPACE;
ref = alloc_type(struct ref);
ref->pkg = ID(packages);
/*
* Work around the Wireshark Anomaly
*/
if (buf != end && *buf == ')')
buf++;
WHITESPACE;
if (buf == end || *buf != '(')
ref->version = NULL;
else {
buf++;
if (buf == end)
FAIL;
switch (*buf++) {
case '=':
ref->relop = rel_eq;
break;
case '<':
ref->relop = rel_lt;
break;
case '>':
EXPECT("=");
ref->relop = rel_ge;
break;
default:
FAIL;
}
WHITESPACE;
ref->version = ID(versions);
EXPECT(")");
}
ref->next = *anchor;
*anchor = ref;
if (buf == end)
DONE;
if (*buf != ',')
break;
buf++;
}
anchor = NULL;
goto eol;
done:
if (pkg)
compact_pkg(pkg);
return;
fail:
fprintf(stderr, "syntax derailment #%d at %s line %d: ",
failed_at, name, lineno);
for (i = 0; i != CHARS_AFTER_ERROR && buf != end; i++) {
if (*buf == '\n')
fprintf(stderr, "\\n");
else if (isspace(*buf))
fputc(' ', stderr);
else if (isprint(*buf))
fputc(*buf, stderr);
buf++;
}
fprintf(stderr, "%s\n", buf == end ? "": "...");
exit(1);
}
void gobble(const char *name)
{
int fd;
struct stat st;
void *map;
fd = open(name, O_RDONLY);
if (fd < 0) {
perror(name);
exit(1);
}
if (fstat(fd, &st) < 0) {
perror("fstat");
exit(1);
}
map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (map == MAP_FAILED) {
perror("mmap");
exit(1);
}
if (posix_madvise(map, st.st_size, POSIX_MADV_WILLNEED) < 0) {
perror("posix_madvise(POSIX_MADV_WILLNEED)");
exit(1);
}
gobble_buf(name, map, st.st_size);
if (posix_madvise(map, st.st_size, POSIX_MADV_RANDOM) < 0) {
perror("posix_madvise(POSIX_MADV_RANDOM)");
exit(1);
}
}