Root/qpkg/gobble.c

Source at commit a9f12d56661a8e6def5a2b32519c3efd55e38d31 created 9 years 22 hours ago.
By Werner Almesberger, qpkg: converted ID comparison from "struct id *" to "void *"
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->conflicts = pkg->depends = NULL;
271    pkg->filename = NULL;
272    pkg->installed = 0;
273    pkg->mark = 0;
274    goto eol;
275
276version:
277    WHITESPACE;
278    if (pkg->version)
279        FAIL;
280    pkg->version = ID(versions);
281    goto eol;
282
283provides:
284    /* @@@ later */
285    goto skip_data;
286
287status:
288    pkg->installed = 1;
289    /* @@@ later */
290    goto skip_data;
291
292filename:
293    WHITESPACE;
294    pkg->filename = buf;
295    goto skip_data;
296
297eol:
298    while (buf != end) {
299        if (*buf == ' ' || *buf == '\t') {
300            buf++;
301            continue;
302        }
303        if (*buf++ != '\n')
304            FAIL;
305        lineno++;
306        if (buf == end)
307            DONE;
308        if (*buf == ' ' || *buf == '\t')
309            FAIL;
310        goto initial;
311    }
312    DONE;
313
314skip_data:
315    while (buf != end) {
316        if (*buf++ != '\n')
317            continue;
318        lineno++;
319        if (buf == end)
320            DONE;
321        if (*buf != ' ' && *buf != '\t')
322            goto initial;
323    }
324    DONE;
325
326list_with_version:
327    while (1) {
328        struct ref *ref;
329
330        WHITESPACE;
331        ref = alloc_type(struct ref);
332        ref->pkg = ID(packages);
333
334        /*
335         * Work around the Wireshark Anomaly
336         */
337        if (buf != end && *buf == ')')
338            buf++;
339
340        WHITESPACE;
341        if (buf == end || *buf != '(')
342            ref->version = NULL;
343        else {
344            buf++;
345            if (buf == end)
346                FAIL;
347            switch (*buf++) {
348            case '=':
349                ref->relop = rel_eq;
350                break;
351            case '<':
352                ref->relop = rel_lt;
353                break;
354            case '>':
355                EXPECT("=");
356                ref->relop = rel_ge;
357                break;
358            default:
359                FAIL;
360            }
361            WHITESPACE;
362            ref->version = ID(versions);
363            EXPECT(")");
364        }
365        ref->next = *anchor;
366        *anchor = ref;
367        if (buf == end)
368            DONE;
369        if (*buf != ',')
370            break;
371        buf++;
372    }
373    anchor = NULL;
374    goto eol;
375
376done:
377    if (pkg)
378        compact_pkg(pkg);
379    return;
380
381fail:
382    fprintf(stderr, "syntax derailment #%d at %s line %d: ",
383        failed_at, name, lineno);
384    for (i = 0; i != CHARS_AFTER_ERROR && buf != end; i++) {
385        if (*buf == '\n')
386            fprintf(stderr, "\\n");
387        else if (isspace(*buf))
388            fputc(' ', stderr);
389        else if (isprint(*buf))
390            fputc(*buf, stderr);
391        buf++;
392    }
393    fprintf(stderr, "%s\n", buf == end ? "": "...");
394    exit(1);
395}
396
397
398void gobble(const char *name)
399{
400    int fd;
401    struct stat st;
402    void *map;
403
404    fd = open(name, O_RDONLY);
405    if (fd < 0) {
406        perror(name);
407        exit(1);
408    }
409    if (fstat(fd, &st) < 0) {
410        perror("fstat");
411        exit(1);
412    }
413    map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
414    if (map == MAP_FAILED) {
415        perror("mmap");
416        exit(1);
417    }
418    if (posix_madvise(map, st.st_size, POSIX_MADV_WILLNEED) < 0) {
419        perror("posix_madvise(POSIX_MADV_WILLNEED)");
420        exit(1);
421    }
422    gobble_buf(name, map, st.st_size);
423    if (posix_madvise(map, st.st_size, POSIX_MADV_RANDOM) < 0) {
424        perror("posix_madvise(POSIX_MADV_RANDOM)");
425        exit(1);
426    }
427}
428

Archive Download this file

Branches:
master



interactive