Root/qpkg/gobble.c

Source at commit 2787a45c436c35fc6fd0ed452903ad045fe9571d created 9 years 17 days ago.
By Werner Almesberger, qpkg: added James S. Plank's red-black trees
1#include <stdlib.h>
2#include <stdio.h>
3#include <string.h>
4#include <ctype.h>
5#include <fcntl.h>
6#include <sys/stat.h>
7#include <sys/mman.h>
8
9#include "util.h"
10#include "id.h"
11#include "qpkg.h"
12#include "gobble.h"
13
14
15#define CHARS_AFTER_ERROR 20
16
17
18#define EXPECT(s) \
19    do { \
20        if (end-buf < sizeof(s)-1) \
21            FAIL; \
22        if (memcmp(buf, s, sizeof(s)-1)) \
23            FAIL; \
24        buf += sizeof(s)-1; \
25    } \
26    while (0)
27
28
29#define NEXT (buf == end ? '?' : *buf++)
30
31
32#define WHITESPACE \
33    do { \
34        if (buf == end) \
35            FAIL; \
36        if (*buf == '\n') \
37            break; \
38        if (!isspace(*buf)) \
39            break; \
40        buf++; \
41    } \
42    while (0)
43
44
45#define ISTERM(c) \
46    ((c) == ' ' || (c) == '\t' || (c) == '\n' || \
47     (c) == ',' || (c) == ')')
48
49
50#define ID(tree) \
51    ({ \
52        const char *start; \
53                            \
54        if (buf == end) \
55            FAIL; \
56        start = buf; \
57        while (buf != end && !ISTERM(*buf)) \
58            buf++; \
59        make_id(tree, start, buf-start); \
60    })
61
62
63#define FAIL \
64    do { \
65        failed_at = __LINE__; \
66        goto fail; \
67    } \
68    while (0)
69
70
71#define DONE goto done
72
73
74static void compact_pkg(struct pkg *new)
75{
76    struct pkg *old;
77
78    for (old = new->more; old; old = old->more)
79        if (old->version == new->version)
80            goto compact;
81    return;
82
83compact:
84    new->id->value = new->more;
85    old->installed = old->installed || new->installed;
86    /* @@@ we may leak a little */
87    free(new);
88}
89
90
91static void gobble_buf(const char *name, const char *buf, size_t len)
92{
93    const char *end = buf+len;
94    int lineno = 1;
95    struct pkg *pkg = NULL; /* current package */
96    struct ref **anchor = NULL;
97    int i, failed_at = 0;
98
99initial:
100    if (buf == end)
101        DONE;
102    if (*buf == '\n') {
103        lineno++;
104        buf++;
105        goto initial;
106    }
107
108    /* decode the tag */
109
110    switch (*buf++) {
111    case 'A': /* Architecture // Auto-Installed */
112        switch (NEXT) {
113        case 'r':
114            EXPECT("chitecture:");
115            /* @@@ use later */
116            goto skip_data;
117        case 'u':
118            EXPECT("to-Installed:");
119            goto skip_data;
120        default:
121            FAIL;
122        }
123
124    case 'C': /* Conflicts // Conffiles */
125        EXPECT("onf");
126        switch (NEXT) {
127        case 'l':
128            EXPECT("icts:");
129            goto conflicts;
130        case 'f':
131            EXPECT("iles:");
132            goto skip_data;
133        default:
134            FAIL;
135        }
136
137    case 'D': /* Depends, Description */
138        EXPECT("e");
139        switch (NEXT) {
140        case 'p':
141            EXPECT("ends:");
142            goto depends;
143        case 's':
144            EXPECT("cription:");
145            goto skip_data;
146        default:
147            FAIL;
148        }
149
150    case 'F': /* Filename */
151        EXPECT("ilename:");
152        goto filename;
153
154    case 'H': /* HomePage, Homepage */
155        EXPECT("ome");
156        switch (NEXT) {
157        case 'P':
158        case 'p':
159            EXPECT("age:");
160            goto skip_data;
161        default:
162            FAIL;
163        }
164
165    case 'I': /* Installed-Time */
166        EXPECT("nstalled-Time:");
167        goto skip_data;
168
169    case 'L': /* License */
170        EXPECT("icense:");
171        goto skip_data;
172
173    case 'M': /* Maintainer, MD5Sum */
174        switch (NEXT) {
175        case 'a':
176            EXPECT("intainer:");
177            goto skip_data;
178        case 'D':
179            EXPECT("5Sum:");
180            goto skip_data;
181        default:
182            FAIL;
183        }
184
185    case 'O': /* OE */
186        EXPECT("E:");
187        goto skip_data;
188
189    case 'P': /* Package, Priority, Provides */
190        switch (NEXT) {
191        case 'a':
192            EXPECT("ckage:");
193            goto package;
194        case 'r':
195            break;
196        default:
197            FAIL;
198        }
199        switch (NEXT) {
200        case 'i':
201            EXPECT("ority:");
202            goto skip_data;
203        case 'o':
204            EXPECT("vides:");
205            goto provides;
206        default:
207            FAIL;
208        }
209
210    case 'R': /* Recommends, Replaces */
211        EXPECT("e");
212        switch (NEXT) {
213        case 'c':
214            EXPECT("ommends:");
215            goto skip_data;
216        case 'p':
217            EXPECT("laces:");
218            goto skip_data;
219        default:
220            FAIL;
221        }
222
223    case 'S': /* Section, Size, Source, Suggests // Status */
224        switch (NEXT) {
225        case 'e':
226            EXPECT("ction:");
227            goto skip_data;
228        case 'i':
229            EXPECT("ze:");
230            goto skip_data;
231        case 'o':
232            EXPECT("urce:");
233            goto skip_data;
234        case 'u':
235            EXPECT("ggests:");
236            goto skip_data;
237        case 't':
238            EXPECT("atus:");
239            goto status;
240        default:
241            FAIL;
242        }
243
244    case 'V': /* Version */
245        EXPECT("ersion:");
246        goto version;
247
248    default:
249        FAIL;
250    }
251
252conflicts:
253    anchor = &pkg->conflicts;
254    goto list_with_version;
255
256depends:
257    anchor = &pkg->depends;
258    goto list_with_version;
259
260package:
261    if (pkg)
262        compact_pkg(pkg);
263
264    WHITESPACE;
265    pkg = alloc_type(struct pkg);
266    pkg->id = ID(packages);
267    pkg->more = pkg->id->value;
268    pkg->id->value = pkg;
269    pkg->version = NULL;
270    pkg->filename = NULL;
271    pkg->installed = 0;
272    pkg->mark = 0;
273    goto eol;
274
275version:
276    WHITESPACE;
277    if (pkg->version)
278        FAIL;
279    pkg->version = ID(versions);
280    goto eol;
281
282provides:
283    /* @@@ later */
284    goto skip_data;
285
286status:
287    pkg->installed = 1;
288    /* @@@ later */
289    goto skip_data;
290
291filename:
292    WHITESPACE;
293    pkg->filename = buf;
294    goto skip_data;
295
296eol:
297    while (buf != end) {
298        if (*buf == ' ' || *buf == '\t') {
299            buf++;
300            continue;
301        }
302        if (*buf++ != '\n')
303            FAIL;
304        lineno++;
305        if (buf == end)
306            DONE;
307        if (*buf == ' ' || *buf == '\t')
308            FAIL;
309        goto initial;
310    }
311    DONE;
312
313skip_data:
314    while (buf != end) {
315        if (*buf++ != '\n')
316            continue;
317        lineno++;
318        if (buf == end)
319            DONE;
320        if (*buf != ' ' && *buf != '\t')
321            goto initial;
322    }
323    DONE;
324
325list_with_version:
326    while (1) {
327        struct ref *ref;
328
329        WHITESPACE;
330        ref = alloc_type(struct ref);
331        ref->pkg = ID(packages);
332
333        /*
334         * Work around the Wireshark Anomaly
335         */
336        if (buf != end && *buf == ')')
337            buf++;
338
339        WHITESPACE;
340        if (buf == end || *buf != '(')
341            ref->version = NULL;
342        else {
343            buf++;
344            if (buf == end)
345                FAIL;
346            switch (*buf++) {
347            case '=':
348                ref->relop = rel_eq;
349                break;
350            case '<':
351                ref->relop = rel_lt;
352                break;
353            case '>':
354                EXPECT("=");
355                ref->relop = rel_ge;
356                break;
357            default:
358                FAIL;
359            }
360            WHITESPACE;
361            ref->version = ID(versions);
362            EXPECT(")");
363        }
364        ref->next = *anchor;
365        *anchor = ref;
366        if (buf == end)
367            DONE;
368        if (*buf != ',')
369            break;
370        buf++;
371    }
372    anchor = NULL;
373    goto eol;
374
375done:
376    if (pkg)
377        compact_pkg(pkg);
378    return;
379
380fail:
381    fprintf(stderr, "syntax derailment #%d at %s line %d: ",
382        failed_at, name, lineno);
383    for (i = 0; i != CHARS_AFTER_ERROR && buf != end; i++) {
384        if (*buf == '\n')
385            fprintf(stderr, "\\n");
386        else if (isspace(*buf))
387            fputc(' ', stderr);
388        else if (isprint(*buf))
389            fputc(*buf, stderr);
390        buf++;
391    }
392    fprintf(stderr, "%s\n", buf == end ? "": "...");
393    exit(1);
394}
395
396
397void gobble(const char *name)
398{
399    int fd;
400    struct stat st;
401    void *map;
402
403    fd = open(name, O_RDONLY);
404    if (fd < 0) {
405        perror(name);
406        exit(1);
407    }
408    if (fstat(fd, &st) < 0) {
409        perror("fstat");
410        exit(1);
411    }
412    map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
413    if (map == MAP_FAILED) {
414        perror("mmap");
415        exit(1);
416    }
417    if (posix_madvise(map, st.st_size, POSIX_MADV_WILLNEED) < 0) {
418        perror("posix_madvise(POSIX_MADV_WILLNEED)");
419        exit(1);
420    }
421    gobble_buf(name, map, st.st_size);
422    if (posix_madvise(map, st.st_size, POSIX_MADV_RANDOM) < 0) {
423        perror("posix_madvise(POSIX_MADV_RANDOM)");
424        exit(1);
425    }
426}
427

Archive Download this file

Branches:
master



interactive