Root/qpkg/gobble.c

Source at commit 6a3e471304b939fa920adcf6f6a9ac31b22fd976 created 8 years 8 months ago.
By Werner Almesberger, qpkg: added some field names and relational operators from Ubuntu
1/*
2 * gobble.c - Read a package database file in a hurry
3 *
4 * Written 2010 by Werner Almesberger
5 * Copyright 2010 Werner Almesberger
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 */
12
13
14#include <stdlib.h>
15#include <stdio.h>
16#include <string.h>
17#include <ctype.h>
18#include <fcntl.h>
19#include <sys/stat.h>
20#include <sys/mman.h>
21
22#include "util.h"
23#include "id.h"
24#include "pkg.h"
25#include "qpkg.h"
26#include "gobble.h"
27
28
29#define CHARS_AFTER_ERROR 20
30
31
32#ifdef BREAKNECK_SPEED
33
34#define EXPECT(s) do { buf += sizeof(s)-1; } while (0)
35
36#else /* !BREAKNECK_SPEED */
37
38#define EXPECT(s) \
39    do { \
40        if (end-buf < sizeof(s)-1) \
41            FAIL; \
42        if (memcmp(buf, s, sizeof(s)-1)) \
43            FAIL; \
44        buf += sizeof(s)-1; \
45    } \
46    while (0)
47
48#endif
49
50
51#define NEXT (buf == end ? '?' : *buf++)
52
53
54#define WHITESPACE \
55    do { \
56        if (buf == end) \
57            FAIL; \
58        if (*buf == '\n') \
59            break; \
60        if (!isspace(*buf)) \
61            break; \
62        buf++; \
63    } \
64    while (0)
65
66
67#define ISTERM(c) \
68    ((c) == ' ' || (c) == '\t' || (c) == '\n' || \
69     (c) == ',' || (c) == ')')
70
71
72#define ID(tree) \
73    ({ \
74        const char *start; \
75                            \
76        if (buf == end) \
77            FAIL; \
78        start = buf; \
79        while (buf != end && !ISTERM(*buf)) \
80            buf++; \
81        make_id(tree, start, buf-start); \
82    })
83
84
85#define FAIL \
86    do { \
87        failed_at = __LINE__; \
88        goto fail; \
89    } \
90    while (0)
91
92
93#define DONE goto done
94
95
96static void finish_pkg(struct pkg *new, struct jrb *jrb)
97{
98    struct pkg *old;
99
100    if (!new->version) {
101        fprintf(stderr, "package %.*s has no version\n",
102            ID2PF(new->id));
103        exit(1);
104    }
105    if (!new->arch) {
106        fprintf(stderr,
107            "package %.*s version %.*s has no architecture\n",
108            ID2PF(new->id), ID2PF(new->version));
109        exit(1);
110    }
111    if (!new->filename && !(new->flags & QPKG_INSTALLED)) {
112        fprintf(stderr,
113            "package %.*s version %.*s has no file name "
114            "(nor is it installed)\n",
115            ID2PF(new->id), ID2PF(new->version));
116        exit(1);
117    }
118
119    for (old = new->more; old; old = old->more)
120        if (old->version == new->version)
121            goto compact;
122    return;
123
124compact:
125    jrb->val = new->more;
126    old->flags |= new->flags;
127    free_pkg(new);
128}
129
130
131static void gobble_buf(const char *name, const char *buf, size_t len)
132{
133    const char *end = buf+len;
134    int lineno = 1;
135    struct pkg *pkg = NULL; /* current package */
136    struct jrb *jrb = NULL; /* RB tree node of current package */
137    struct ref **anchor = NULL;
138    int i, failed_at = 0;
139
140initial:
141    if (buf == end)
142        DONE;
143    if (*buf == '\n') {
144        lineno++;
145        buf++;
146        goto initial;
147    }
148
149    /* decode the tag */
150
151    switch (*buf++) {
152    case 'A': /* Architecture // Auto-Installed */
153        switch (NEXT) {
154        case 'r':
155            EXPECT("chitecture:");
156            goto architecture;
157        case 'u':
158            EXPECT("to-Installed:");
159            goto skip_data;
160        default:
161            FAIL;
162        }
163
164    case 'B': /* Bugs */
165        EXPECT("ugs:");
166        goto skip_data;
167
168    case 'C': /* Conflicts // Conffiles */
169        EXPECT("onf");
170        switch (NEXT) {
171        case 'l':
172            EXPECT("icts:");
173            goto conflicts;
174        case 'f':
175            EXPECT("iles:");
176            goto skip_data;
177        default:
178            FAIL;
179        }
180
181    case 'D': /* Depends, Description */
182        EXPECT("e");
183        switch (NEXT) {
184        case 'p':
185            EXPECT("ends:");
186            goto depends;
187        case 's':
188            EXPECT("cription:");
189            goto skip_data;
190        default:
191            FAIL;
192        }
193
194    case 'F': /* Filename */
195        EXPECT("ilename:");
196        goto filename;
197
198    case 'H': /* HomePage, Homepage */
199        EXPECT("ome");
200        switch (NEXT) {
201        case 'P':
202        case 'p':
203            EXPECT("age:");
204            goto skip_data;
205        default:
206            FAIL;
207        }
208
209    case 'I': /* Installed-Size, Installed-Time */
210        EXPECT("nstalled-");
211        switch (NEXT) {
212        case 'S':
213            EXPECT("ize:");
214            goto skip_data;
215        case 'T':
216            EXPECT("ime:");
217            goto skip_data;
218        default:
219            FAIL;
220        }
221
222    case 'L': /* License */
223        EXPECT("icense:");
224        goto skip_data;
225
226    case 'M': /* Maintainer, MD5Sum, MD5sum */
227        switch (NEXT) {
228        case 'a':
229            EXPECT("intainer:");
230            goto skip_data;
231        case 'D':
232            EXPECT("5");
233            switch (NEXT) {
234            case 'S':
235            case 's':
236                break;
237            default:
238                FAIL;
239            }
240            EXPECT("um:");
241            goto skip_data;
242        default:
243            FAIL;
244        }
245
246    case 'O': /* OE, Origin, Original-Maintainer */
247        switch (NEXT) {
248        case 'E':
249            EXPECT(":");
250            goto skip_data;
251        case 'r':
252            EXPECT("igin");
253            switch (NEXT) {
254            case ':':
255                break;
256            case 'a':
257                EXPECT("l-Maintainer:");
258                break;
259            default:
260                FAIL;
261            }
262            goto skip_data;
263        default:
264            FAIL;
265        }
266        goto skip_data;
267
268    case 'P': /* Package, Priority, Provides */
269        switch (NEXT) {
270        case 'a':
271            EXPECT("ckage:");
272            goto package;
273        case 'r':
274            break;
275        default:
276            FAIL;
277        }
278        switch (NEXT) {
279        case 'i':
280            EXPECT("ority:");
281            goto skip_data;
282        case 'o':
283            EXPECT("vides:");
284            goto provides;
285        default:
286            FAIL;
287        }
288
289    case 'R': /* Recommends, Replaces */
290        EXPECT("e");
291        switch (NEXT) {
292        case 'c':
293            EXPECT("ommends:");
294            goto skip_data;
295        case 'p':
296            EXPECT("laces:");
297            goto skip_data;
298        default:
299            FAIL;
300        }
301
302    case 'S': /* Section, SHA1, SHA256, Size, Source, Suggests
303               // Status */
304        switch (NEXT) {
305        case 'e':
306            EXPECT("ction:");
307            goto skip_data;
308        case 'H':
309            EXPECT("A");
310            switch (NEXT) {
311            case '1':
312                EXPECT(":");
313                break;
314            case '2':
315                EXPECT("56:");
316                break;
317            default:
318                FAIL;
319            }
320            goto skip_data;
321        case 'i':
322            EXPECT("ze:");
323            goto skip_data;
324        case 'o':
325            EXPECT("urce:");
326            goto skip_data;
327        case 'u':
328            EXPECT("ggests:");
329            goto skip_data;
330        case 't':
331            EXPECT("atus:");
332            goto status;
333        default:
334            FAIL;
335        }
336
337    case 'T': /* Task */
338        EXPECT("ask:");
339        goto skip_data;
340
341    case 'V': /* Version */
342        EXPECT("ersion:");
343        goto version;
344
345    default:
346        FAIL;
347    }
348
349conflicts:
350    anchor = &pkg->conflicts;
351    goto list_with_version;
352
353depends:
354    anchor = &pkg->depends;
355    goto list_with_version;
356
357package:
358    if (pkg)
359        finish_pkg(pkg, jrb);
360
361    WHITESPACE;
362    jrb = ID(packages);
363    pkg = new_pkg(jrb);
364    goto eol;
365
366version:
367    WHITESPACE;
368    if (pkg->version)
369        FAIL;
370    pkg->version = ID(versions)->key;
371    goto eol;
372
373architecture:
374    WHITESPACE;
375    if (pkg->arch)
376        FAIL;
377    pkg->arch = buf;
378    goto skip_data;
379
380provides:
381    anchor = &pkg->provides;
382    /*
383     * There should never be a version in the provisions, so it's a bit
384     * wasteful to use a structure that has a version field. But then, code
385     * reuse is nice, too.
386     */
387    goto list_with_version;
388
389status:
390    pkg->flags |= QPKG_INSTALLED;
391    /* @@@ later */
392    goto skip_data;
393
394filename:
395    WHITESPACE;
396    if (pkg->filename)
397        FAIL;
398    pkg->filename = buf;
399    goto skip_data;
400
401eol:
402    while (buf != end) {
403        if (*buf == ' ' || *buf == '\t') {
404            buf++;
405            continue;
406        }
407        if (*buf++ != '\n')
408            FAIL;
409        lineno++;
410        if (buf == end)
411            DONE;
412        if (*buf == ' ' || *buf == '\t')
413            FAIL;
414        goto initial;
415    }
416    DONE;
417
418skip_data:
419    while (buf != end) {
420        if (*buf++ != '\n')
421            continue;
422        lineno++;
423        if (buf == end)
424            DONE;
425        if (*buf != ' ' && *buf != '\t')
426            goto initial;
427    }
428    DONE;
429
430list_with_version:
431    while (1) {
432        struct ref *ref;
433
434        WHITESPACE;
435        ref = alloc_type(struct ref);
436        ref->pkg = ID(packages)->key;
437
438        /*
439         * Work around the Wireshark Anomaly
440         */
441        if (buf != end && *buf == ')')
442            buf++;
443
444        WHITESPACE;
445        if (buf == end || *buf != '(')
446            ref->version = NULL;
447        else {
448            buf++;
449            switch (NEXT) {
450            case '=':
451                ref->relop = rel_eq;
452                break;
453            case '<':
454                switch (NEXT) {
455                case ' ':
456                    ref->relop = rel_lt;
457                    break;
458                case '=':
459                    ref->relop = rel_le;
460                    break;
461                case '<':
462                    ref->relop = rel_ll;
463                    break;
464                default:
465                    buf--;
466                }
467                break;
468            case '>':
469                switch (NEXT) {
470                case '=':
471                    ref->relop = rel_ge;
472                    break;
473                case '>':
474                    ref->relop = rel_gg;
475                    break;
476                default:
477                    FAIL;
478                }
479                break;
480            default:
481                FAIL;
482            }
483            WHITESPACE;
484            ref->version = ID(versions)->key;
485            EXPECT(")");
486        }
487        *anchor = ref;
488        ref->next = NULL;
489        anchor = &ref->next;
490        if (buf == end)
491            DONE;
492        if (*buf != ',')
493            break;
494        buf++;
495    }
496    anchor = NULL;
497    goto eol;
498
499done:
500    if (pkg)
501        finish_pkg(pkg, jrb);
502    return;
503
504fail:
505    fprintf(stderr, "syntax derailment #%d at %s line %d: ",
506        failed_at, name, lineno);
507    for (i = 0; i != CHARS_AFTER_ERROR && buf != end; i++) {
508        if (*buf == '\n')
509            fprintf(stderr, "\\n");
510        else if (isspace(*buf))
511            fputc(' ', stderr);
512        else if (isprint(*buf))
513            fputc(*buf, stderr);
514        buf++;
515    }
516    fprintf(stderr, "%s\n", buf == end ? "": "...");
517    exit(1);
518}
519
520
521/*
522 * We should be able to test for __UCLIBC_HAS_ADVANCED_REALTIME__ specifically,
523 * but that doesn't work for some reason. So let's just omit posix_madvise on
524 * __UCLIBC__ in general.
525 */
526
527
528#if !defined(__UCLIBC__)
529
530static int do_madvise(void *addr, size_t len, int advice)
531{
532    return posix_madvise(addr, len, advice);
533}
534
535#else /* __UCLIBC__ */
536
537#define do_madvise(addr, len, advice) 0
538
539#endif /* __UCLIBC__ */
540
541
542void gobble(const char *name)
543{
544    int fd;
545    struct stat st;
546    void *map;
547
548    fd = open(name, O_RDONLY);
549    if (fd < 0) {
550        perror(name);
551        exit(1);
552    }
553    if (fstat(fd, &st) < 0) {
554        perror("fstat");
555        exit(1);
556    }
557    map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
558    if (map == MAP_FAILED) {
559        perror("mmap");
560        exit(1);
561    }
562    if (do_madvise(map, st.st_size, POSIX_MADV_WILLNEED) < 0) {
563        perror("posix_madvise(POSIX_MADV_WILLNEED)");
564        exit(1);
565    }
566    gobble_buf(name, map, st.st_size);
567    if (do_madvise(map, st.st_size, POSIX_MADV_RANDOM) < 0) {
568        perror("posix_madvise(POSIX_MADV_RANDOM)");
569        exit(1);
570    }
571}
572

Archive Download this file

Branches:
master



interactive