1
0
mirror of git://projects.qi-hardware.com/wernermisc.git synced 2024-11-25 16:16:15 +02:00
wernermisc/qpkg/gobble.c
Werner Almesberger 9b5480ab3e qpkg: merge identical versions in package databases
This avoids turning a prerequisite search into a massacre of, guessed,
O(sqrt(2^requisites)).

- gobble.c (gobble_buf, compact_pkg): if a package being added has the same
  version as an existing package with the same name, merge them and keep
  only one
2010-11-19 16:27:55 -03:00

427 lines
6.6 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
#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)
#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->value = 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->value;
pkg->id->value = pkg;
pkg->version = 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);
}
}