Werner's Miscellanea
Sign in or create your account | Project List | Help
Werner's Miscellanea Git Source Tree
Root/
Source at commit ba5a392555fd70c6f668f0d9750791f9b35eb179 created 12 years 4 months ago. By Werner Almesberger, m1/tools/README: m1nor description | |
---|---|
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 | |
96 | static 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 | |
124 | compact: |
125 | jrb->val = new->more; |
126 | old->flags |= new->flags; |
127 | free_pkg(new); |
128 | } |
129 | |
130 | |
131 | static 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 | |
140 | initial: |
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 | |
349 | conflicts: |
350 | anchor = &pkg->conflicts; |
351 | goto list_with_version; |
352 | |
353 | depends: |
354 | anchor = &pkg->depends; |
355 | goto list_with_version; |
356 | |
357 | package: |
358 | if (pkg) |
359 | finish_pkg(pkg, jrb); |
360 | |
361 | WHITESPACE; |
362 | jrb = ID(packages); |
363 | pkg = new_pkg(jrb); |
364 | goto eol; |
365 | |
366 | version: |
367 | WHITESPACE; |
368 | if (pkg->version) |
369 | FAIL; |
370 | pkg->version = ID(versions)->key; |
371 | goto eol; |
372 | |
373 | architecture: |
374 | WHITESPACE; |
375 | if (pkg->arch) |
376 | FAIL; |
377 | pkg->arch = buf; |
378 | goto skip_data; |
379 | |
380 | provides: |
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 | |
389 | status: |
390 | pkg->flags |= QPKG_INSTALLED; |
391 | /* @@@ later */ |
392 | goto skip_data; |
393 | |
394 | filename: |
395 | WHITESPACE; |
396 | if (pkg->filename) |
397 | FAIL; |
398 | pkg->filename = buf; |
399 | goto skip_data; |
400 | |
401 | eol: |
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 | |
418 | skip_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 | |
430 | list_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 | |
499 | done: |
500 | if (pkg) |
501 | finish_pkg(pkg, jrb); |
502 | return; |
503 | |
504 | fail: |
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 | |
530 | static 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 | |
542 | void 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 |
Branches:
master