mirror of
git://projects.qi-hardware.com/wernermisc.git
synced 2024-11-25 18:08:27 +02:00
9b5480ab3e
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
427 lines
6.6 KiB
C
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);
|
|
}
|
|
}
|