1 /*
2 * Copyright 2005-2014 Vasil Dimov
3 * All rights reserved
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted providing that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
22 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
23 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include <sys/cdefs.h>
28 #include <sys/types.h>
29 #include <sys/param.h>
30 #include <sys/stat.h>
31 #include <sys/uio.h>
32 #include <sys/utsname.h>
33
34 #include <assert.h>
35 #include <ctype.h>
36 #include <err.h>
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <limits.h>
40 #include <regex.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <sysexits.h>
45 #include <unistd.h>
46
47 #include "display.h"
48 #include "exhaust_fp.h"
49 #include "parse_indexln.h"
50 #include "portdef.h"
51 #include "store.h"
52 #include "vector.h"
53 #include "xlibc.h"
54
55 #define RSi '\n' /* record separator for index file */
56 #define FSi '|' /* field separator for index file */
57
58 /* RSp must be '\n' because we use fgets */
59 #define RSp '\n' /* record separator for plist file */
60 #define FSp '|' /* field separator for plist file */
61
62 struct pline_t {
63 unsigned portid;
64 char *pfile;
65 };
66
67 struct plist_t {
68 char *raw;
69 size_t plines_cnt;
70 struct pline_t *plines;
71 };
72
73 struct store_t {
74 char dir[PATH_MAX];
75 char newdir[PATH_MAX];
76 char olddir[PATH_MAX];
77
78 char index_fn[PATH_MAX];
79 char plist_fn[PATH_MAX];
80 char index_new_fn[PATH_MAX];
81 char plist_new_fn[PATH_MAX];
82 char index_old_fn[PATH_MAX];
83 char plist_old_fn[PATH_MAX];
84
85 FILE *index_fp;
86 FILE *plist_fp;
87 FILE *index_new_fp;
88 FILE *plist_new_fp;
89
90 struct ports_t ports;
91 char *ports_raw;
92 struct plist_t *plist;
93 };
94
95 /* gather_pfiles argument */
96 struct garg_t {
97 regex_t re;
98 struct store_t *store;
99 int should_have_matched;
100 };
101
102 /*
103 * Set index and plist filenames
104 */
105 static void set_filenames(struct store_t *store);
106
107 /*
108 * Load file from disk, ``raw'' will be exact image of the file's content
109 */
110 static void load_file(const char *filename, char **raw);
111
112 /*
113 * Free data allocated by load_file()
114 */
115 static void free_file(char *raw);
116
117 /*
118 * Add port's plist to store
119 */
120 static void add_port_plist(struct store_t *s, const struct port_t *port);
121
122 /*
123 * Add port's basic data to store
124 */
125 static void add_port_index(struct store_t *s, const struct port_t *port);
126
127 /*
128 * Add SEARCH_BY_PFILE to `matched' member of all ports that have
129 * `search_file' in their plist. If `should_have_matched' is nonzero than
130 * skip all ports that have `matched' member equal to zero.
131 */
132 static void filter_ports_by_pfile(struct store_t *s, int should_have_matched,
133 const char *search_file, int regcomp_flags);
134
135 /*
136 * Place plist files that match `arg->re' in the appropriate `plist'
137 * members of the `arg->ports' structure
138 */
139 static void gather_pfiles(char *line, void *arg);
140
141 /*
142 * Retrieve port by its id, exit if port is not found
143 */
144 static void get_port_by_id(struct ports_t *ports, const char *portid,
145 struct port_t **port);
146
147 /*
148 * Calculate number of occurences of `sep' in `raw'
149 */
150 static size_t records_cnt(const char *raw, char sep);
151
152 /*
153 * Compare 2 ports accoring to their IDs
154 */
155 static int ports_cmp(const void *p1v, const void *p2v);
156
157 /*
158 * Load whole index file (all ports) from disk
159 */
160 static void load_index(struct store_t *s);
161
162 /*
163 * Free data allocated by load_index()
164 */
165 static void free_index(struct store_t *s);
166
167 /*
168 * Load whole plist file from disk
169 */
170 static void load_plist(struct store_t *s);
171
172 /*
173 * Free data allocated by load_plist()
174 */
175 static void free_plist(struct store_t *s);
176
177 /*
178 * Compare 2 plist lines, according to their portids
179 */
180 static int plines_cmp(const void *l1v, const void *l2v);
181
182 /*
183 * Remove old database directory
184 */
185 static void rm_olddir(const struct store_t *s);
186
187 /***/
188
189 void
alloc_store(struct store_t ** s)190 alloc_store(struct store_t **s)
191 {
192 *s = (struct store_t *)xmalloc(sizeof(struct store_t));
193 }
194
195 void
free_store(struct store_t * s)196 free_store(struct store_t *s)
197 {
198 xfree(s);
199 }
200
201 int
s_exists()202 s_exists()
203 {
204 struct store_t store;
205
206 set_filenames(&store);
207
208 if (access(store.index_fn, F_OK) == -1 ||
209 access(store.plist_fn, F_OK) == -1)
210 return 0;
211
212 return 1;
213 }
214
215 struct ports_t *
get_ports(struct store_t * s)216 get_ports(struct store_t *s)
217 {
218 return &s->ports;
219 }
220
221 void
s_new_start(struct store_t * s)222 s_new_start(struct store_t *s)
223 {
224 const char *mkdirs[] = {DBDIR, s->newdir};
225 int i;
226
227 set_filenames(s);
228
229 for (i = 0; i < sizeof(mkdirs) / sizeof(const char *); i++)
230 if (mkdir(mkdirs[i], 0755) == -1)
231 if (errno != EEXIST)
232 err(EX_CANTCREAT, "mkdir(): %s", mkdirs[i]);
233
234 if ((s->index_new_fp = fopen(s->index_new_fn, "w")) == NULL)
235 err(EX_CANTCREAT, "fopen(): %s", s->index_new_fn);
236
237 if ((s->plist_new_fp = fopen(s->plist_new_fn, "w")) == NULL)
238 err(EX_CANTCREAT, "fopen(): %s", s->plist_new_fn);
239 }
240
241 void
s_new_end(struct store_t * s)242 s_new_end(struct store_t *s)
243 {
244 /* close newly created db files */
245 xfclose(s->index_new_fp, s->index_new_fn);
246
247 xfclose(s->plist_new_fp, s->plist_new_fn);
248
249 rm_olddir(s);
250
251 /* move current db out of the way */
252 if (rename(s->dir, s->olddir) == -1)
253 if (errno != ENOENT)
254 err(EX_CANTCREAT, "rename(): %s to %s", s->dir, s->olddir);
255
256 /* make new db current */
257 if (rename(s->newdir, s->dir) == -1)
258 err(EX_CANTCREAT, "rename(): %s to %s", s->newdir, s->dir);
259
260 rm_olddir(s);
261 }
262
263 void
s_add_port(struct store_t * s,const struct port_t * port)264 s_add_port(struct store_t *s, const struct port_t *port)
265 {
266 add_port_plist(s, port);
267 add_port_index(s, port);
268 }
269
270 static void
add_port_plist(struct store_t * s,const struct port_t * port)271 add_port_plist(struct store_t *s, const struct port_t *port)
272 {
273 struct vector_iterator_t vi;
274 char *file;
275
276 vi_reset(&vi, &port->plist);
277
278 while (vi_next(&vi, (void **)&file))
279 if (fprintf(s->plist_new_fp,
280 "%u%c""%s%c",
281 port->id, FSp,
282 file, RSp) == -1)
283 err(EX_IOERR, "fprintf(): %s", s->plist_new_fn);
284 }
285
286 static void
add_port_index(struct store_t * s,const struct port_t * port)287 add_port_index(struct store_t *s, const struct port_t *port)
288 {
289 if (fprintf(s->index_new_fp,
290 "%u%c"
291 "%s%c""%s%c""%s%c"
292 "%s%c""%s%c""%s%c"
293 "%s%c""%s%c""%s%c"
294 "%s%c""%s%c""%s%c"
295 "%s%c",
296 port->id, FSi,
297 port->pkgname, FSi, port->path, FSi, port->prefix, FSi,
298 port->comment, FSi, port->pkgdescr, FSi, port->maint, FSi,
299 port->categories, FSi, port->bdep, FSi, port->rdep, FSi,
300 port->www, FSi, port->edep, FSi, port->pdep, FSi,
301 port->fdep, RSi) == -1)
302 err(EX_IOERR, "fprintf(): %s", s->index_new_fn);
303 }
304
305 /***/
306
307 void
s_read_start(struct store_t * s)308 s_read_start(struct store_t *s)
309 {
310 set_filenames(s);
311
312 load_index(s);
313 load_plist(s);
314 }
315
316 void
s_read_end(struct store_t * s)317 s_read_end(struct store_t *s)
318 {
319 free_plist(s);
320 free_index(s);
321 }
322
323 void
s_search_start(struct store_t * s)324 s_search_start(struct store_t *s)
325 {
326 set_filenames(s);
327
328 load_index(s);
329 }
330
331 void
s_search_end(struct store_t * s)332 s_search_end(struct store_t *s)
333 {
334 free_index(s);
335 }
336
337 void
filter_ports(struct store_t * s,const struct options_t * opts)338 filter_ports(struct store_t *s, const struct options_t *opts)
339 {
340 struct port_t *cur_port;
341 regex_t name_re;
342 regex_t key_re;
343 regex_t path_re;
344 regex_t info_re;
345 regex_t maint_re;
346 regex_t cat_re;
347 regex_t fdep_re;
348 regex_t edep_re;
349 regex_t pdep_re;
350 regex_t bdep_re;
351 regex_t rdep_re;
352 regex_t dep_re;
353 regex_t www_re;
354 int regcomp_flags_fields;
355 int regcomp_flags_pfiles;
356 size_t i;
357
358 regcomp_flags_fields = REG_EXTENDED | REG_NOSUB;
359 regcomp_flags_pfiles = REG_EXTENDED | REG_NOSUB;
360
361 if (opts->icase_fields)
362 regcomp_flags_fields |= REG_ICASE;
363 if (opts->icase_pfiles)
364 regcomp_flags_pfiles |= REG_ICASE;
365
366 if (opts->search_crit & SEARCH_BY_NAME)
367 xregcomp(&name_re, opts->search_name, regcomp_flags_fields);
368 if (opts->search_crit & SEARCH_BY_KEY)
369 xregcomp(&key_re, opts->search_key, regcomp_flags_fields);
370 if (opts->search_crit & SEARCH_BY_PATH)
371 xregcomp(&path_re, opts->search_path, regcomp_flags_fields);
372 if (opts->search_crit & SEARCH_BY_INFO)
373 xregcomp(&info_re, opts->search_info, regcomp_flags_fields);
374 if (opts->search_crit & SEARCH_BY_MAINT)
375 xregcomp(&maint_re, opts->search_maint, regcomp_flags_fields);
376 if (opts->search_crit & SEARCH_BY_CAT)
377 xregcomp(&cat_re, opts->search_cat, regcomp_flags_fields);
378 if (opts->search_crit & SEARCH_BY_FDEP)
379 xregcomp(&fdep_re, opts->search_fdep, regcomp_flags_fields);
380 if (opts->search_crit & SEARCH_BY_EDEP)
381 xregcomp(&edep_re, opts->search_edep, regcomp_flags_fields);
382 if (opts->search_crit & SEARCH_BY_PDEP)
383 xregcomp(&pdep_re, opts->search_pdep, regcomp_flags_fields);
384 if (opts->search_crit & SEARCH_BY_BDEP)
385 xregcomp(&bdep_re, opts->search_bdep, regcomp_flags_fields);
386 if (opts->search_crit & SEARCH_BY_RDEP)
387 xregcomp(&rdep_re, opts->search_rdep, regcomp_flags_fields);
388 if (opts->search_crit & SEARCH_BY_WWW)
389 xregcomp(&www_re, opts->search_www, regcomp_flags_fields);
390 if (opts->search_crit & SEARCH_BY_DEP)
391 xregcomp(&dep_re, opts->search_dep, regcomp_flags_fields);
392
393 for (i = 0; i < s->ports.sz; i++)
394 if (s->ports.arr[i] != NULL)
395 {
396 cur_port = s->ports.arr[i];
397
398 if (opts->search_crit & SEARCH_BY_NAME)
399 if (regexec(&name_re, cur_port->pkgname, 0, NULL, 0) == 0)
400 cur_port->matched |= SEARCH_BY_NAME;
401
402 if (opts->search_crit & SEARCH_BY_KEY)
403 if (regexec(&key_re, cur_port->pkgname, 0, NULL, 0) == 0 ||
404 regexec(&key_re, cur_port->comment, 0, NULL, 0) == 0 ||
405 regexec(&key_re, cur_port->fdep, 0, NULL, 0) == 0 ||
406 regexec(&key_re, cur_port->edep, 0, NULL, 0) == 0 ||
407 regexec(&key_re, cur_port->pdep, 0, NULL, 0) == 0 ||
408 regexec(&key_re, cur_port->bdep, 0, NULL, 0) == 0 ||
409 regexec(&key_re, cur_port->rdep, 0, NULL, 0) == 0)
410 cur_port->matched |= SEARCH_BY_KEY;
411
412 if (opts->search_crit & SEARCH_BY_PATH)
413 if (regexec(&path_re, cur_port->path, 0, NULL, 0) == 0)
414 cur_port->matched |= SEARCH_BY_PATH;
415
416 if (opts->search_crit & SEARCH_BY_INFO)
417 if (regexec(&info_re, cur_port->comment, 0, NULL, 0) == 0)
418 cur_port->matched |= SEARCH_BY_INFO;
419
420 if (opts->search_crit & SEARCH_BY_MAINT)
421 if (regexec(&maint_re, cur_port->maint, 0, NULL, 0) == 0)
422 cur_port->matched |= SEARCH_BY_MAINT;
423
424 if (opts->search_crit & SEARCH_BY_CAT)
425 if (regexec(&cat_re, cur_port->categories, 0, NULL, 0) == 0)
426 cur_port->matched |= SEARCH_BY_CAT;
427
428 if (opts->search_crit & SEARCH_BY_FDEP)
429 if (regexec(&fdep_re, cur_port->fdep, 0, NULL, 0) == 0)
430 cur_port->matched |= SEARCH_BY_FDEP;
431
432 if (opts->search_crit & SEARCH_BY_EDEP)
433 if (regexec(&edep_re, cur_port->edep, 0, NULL, 0) == 0)
434 cur_port->matched |= SEARCH_BY_EDEP;
435
436 if (opts->search_crit & SEARCH_BY_PDEP)
437 if (regexec(&pdep_re, cur_port->pdep, 0, NULL, 0) == 0)
438 cur_port->matched |= SEARCH_BY_PDEP;
439
440 if (opts->search_crit & SEARCH_BY_BDEP)
441 if (regexec(&bdep_re, cur_port->bdep, 0, NULL, 0) == 0)
442 cur_port->matched |= SEARCH_BY_BDEP;
443
444 if (opts->search_crit & SEARCH_BY_RDEP)
445 if (regexec(&rdep_re, cur_port->rdep, 0, NULL, 0) == 0)
446 cur_port->matched |= SEARCH_BY_RDEP;
447
448 if (opts->search_crit & SEARCH_BY_DEP)
449 if (regexec(&dep_re, cur_port->bdep, 0, NULL, 0) == 0 ||
450 regexec(&dep_re, cur_port->rdep, 0, NULL, 0) == 0)
451 cur_port->matched |= SEARCH_BY_DEP;
452
453 if (opts->search_crit & SEARCH_BY_WWW)
454 if (regexec(&www_re, cur_port->www, 0, NULL, 0) == 0)
455 cur_port->matched |= SEARCH_BY_WWW;
456 }
457
458 /*
459 * Optimization:
460 * Call filter_ports_by_pfile() after other filtering because it
461 * loads plists only for ports that have been matched by other
462 * search criteria (if any). Thus we avoid loading plists for all
463 * ports in the case of ``-p ports-mgmt/portseach -f .*'' for example.
464 */
465 if (opts->search_crit & SEARCH_BY_PFILE)
466 filter_ports_by_pfile(s, opts->search_crit & ~SEARCH_BY_PFILE,
467 opts->search_file, regcomp_flags_pfiles);
468
469 if (opts->search_crit & SEARCH_BY_NAME)
470 xregfree(&name_re);
471 if (opts->search_crit & SEARCH_BY_KEY)
472 xregfree(&key_re);
473 if (opts->search_crit & SEARCH_BY_PATH)
474 xregfree(&path_re);
475 if (opts->search_crit & SEARCH_BY_INFO)
476 xregfree(&info_re);
477 if (opts->search_crit & SEARCH_BY_MAINT)
478 xregfree(&maint_re);
479 if (opts->search_crit & SEARCH_BY_CAT)
480 xregfree(&cat_re);
481 if (opts->search_crit & SEARCH_BY_FDEP)
482 xregfree(&fdep_re);
483 if (opts->search_crit & SEARCH_BY_EDEP)
484 xregfree(&edep_re);
485 if (opts->search_crit & SEARCH_BY_PDEP)
486 xregfree(&pdep_re);
487 if (opts->search_crit & SEARCH_BY_BDEP)
488 xregfree(&bdep_re);
489 if (opts->search_crit & SEARCH_BY_RDEP)
490 xregfree(&rdep_re);
491 if (opts->search_crit & SEARCH_BY_WWW)
492 xregfree(&www_re);
493 }
494
495 static void
filter_ports_by_pfile(struct store_t * s,int should_have_matched,const char * search_file,int regcomp_flags)496 filter_ports_by_pfile(struct store_t *s, int should_have_matched,
497 const char *search_file, int regcomp_flags)
498 {
499 FILE *plist_fp;
500 struct garg_t garg;
501
502 garg.store = s;
503 garg.should_have_matched = should_have_matched;
504
505 xregcomp(&garg.re, search_file, regcomp_flags);
506
507 plist_fp = xfopen(s->plist_fn, "r");
508
509 exhaust_fp(plist_fp, gather_pfiles, &garg);
510
511 xfclose(plist_fp, s->plist_fn);
512
513 xregfree(&garg.re);
514 }
515
516 /***/
517
518 static void
set_filenames(struct store_t * s)519 set_filenames(struct store_t *s)
520 {
521 struct utsname un;
522 /* number of characters to pick from the start of un.release */
523 int release_chars;
524
525 if (uname(&un) == -1)
526 err(EX_OSERR, "uname()");
527
528 /* un.release is something like "10.1-BETA3", pick up "10" from it. */
529 release_chars = 0;
530 while (isdigit(un.release[release_chars])) {
531 release_chars++;
532 }
533
534 /* If un.release contains something unexpected, then pick it all. */
535 if (release_chars == 0) {
536 release_chars = strlen(un.release);
537 }
538
539 snprintf(s->dir, sizeof(s->dir), "%s/%.*s-%s",
540 DBDIR, release_chars, un.release, un.machine);
541
542 snprintf(s->newdir, sizeof(s->newdir), "%s.new", s->dir);
543 snprintf(s->olddir, sizeof(s->olddir), "%s.old", s->dir);
544
545 snprintf(s->index_fn, sizeof(s->index_fn), "%s/index", s->dir);
546 snprintf(s->plist_fn, sizeof(s->plist_fn), "%s/plist", s->dir);
547
548 snprintf(s->index_new_fn, sizeof(s->index_new_fn), "%s/index", s->newdir);
549 snprintf(s->plist_new_fn, sizeof(s->plist_new_fn), "%s/plist", s->newdir);
550
551 snprintf(s->index_old_fn, sizeof(s->index_old_fn), "%s/index", s->olddir);
552 snprintf(s->plist_old_fn, sizeof(s->plist_old_fn), "%s/plist", s->olddir);
553 }
554
555 static void
load_index(struct store_t * s)556 load_index(struct store_t *s)
557 {
558 char rs[2] = {RSi, '\0'};
559 char *raw_p;
560 char *rec;
561 size_t rec_idx;
562 size_t i;
563 struct port_t *cur_port;
564
565 load_file(s->index_fn, &s->ports_raw);
566
567 s->ports.sz = records_cnt(s->ports_raw, RSi);
568
569 s->ports.arr =
570 (struct port_t **)xmalloc(s->ports.sz * sizeof(struct port_t *));
571
572 for (i = 0; i < s->ports.sz; i++)
573 s->ports.arr[i] =
574 (struct port_t *)xmalloc(sizeof(struct port_t));
575
576 raw_p = s->ports_raw;
577
578 for (rec_idx = 0; (rec = strsep(&raw_p, rs)) != NULL; rec_idx++)
579 {
580 if (rec[0] == '\0')
581 continue;
582
583 cur_port = s->ports.arr[rec_idx];
584
585 cur_port->matched = 0;
586
587 cur_port->indexln_raw = xstrchr(rec, FSi);
588 cur_port->indexln_raw[0] = '\0';
589 cur_port->indexln_raw++;
590 parse_indexln(cur_port);
591 cur_port->id = (unsigned)strtoul(rec, NULL, 10);
592 }
593
594 /* normally ports are loaded ordered, but just to make sure */
595 if (mergesort(s->ports.arr,
596 s->ports.sz,
597 sizeof(struct port_t **),
598 ports_cmp) == -1)
599 err(EX_OSERR, "mergesort()");
600 }
601
602 static void
free_index(struct store_t * s)603 free_index(struct store_t *s)
604 {
605 size_t i;
606
607 for (i = 0; i < s->ports.sz; i++)
608 if (s->ports.arr[i] != NULL)
609 xfree(s->ports.arr[i]);
610
611 xfree(s->ports.arr);
612
613 free_file(s->ports_raw);
614 }
615
616 static void
load_file(const char * filename,char ** raw)617 load_file(const char *filename, char **raw)
618 {
619 int fd;
620 struct stat sb;
621 size_t sz;
622 size_t offt;
623 ssize_t rd_len;
624
625 if ((fd = open(filename, O_RDONLY)) == -1)
626 err(EX_NOINPUT, "open(): %s", filename);
627
628 if (fstat(fd, &sb) == -1)
629 err(EX_OSERR, "fstat(): %s", filename);
630
631 sz = sb.st_size;
632
633 *raw = (char *)xmalloc(sz + 1);
634
635 offt = 0;
636
637 while ((rd_len = read(fd, *raw, sz - offt)) <= sz - offt)
638 {
639 if (rd_len == -1)
640 err(EX_IOERR, "read(): %s", filename);
641
642 if (rd_len == 0)
643 break;
644
645 offt += rd_len;
646 }
647
648 if (sz != offt)
649 errx(EX_PROTOCOL, "while reading %s: fstat returned %u bytes "
650 "for file size but we got %u from it",
651 filename, (unsigned)sz, (unsigned)offt);
652
653 (*raw)[sz] = '\0';
654
655 close(fd);
656 }
657
658 static void
free_file(char * raw)659 free_file(char *raw)
660 {
661 xfree(raw);
662 }
663
664 static void
gather_pfiles(char * line,void * arg_void)665 gather_pfiles(char *line, void *arg_void)
666 {
667 struct garg_t *arg = (struct garg_t *)arg_void;
668
669 static unsigned line_num = 0;
670
671 char *portid;
672 struct port_t *port;
673 char *FSp_pos;
674 char *filename;
675
676 line_num++;
677
678 if ((FSp_pos = strchr(line, FSp)) == NULL)
679 errx(EX_DATAERR, "corrupted datafile: %s: "
680 "``%c'' not found on line %u",
681 arg->store->plist_fn, FSp, line_num);
682
683 if (regexec(&arg->re, FSp_pos + 1, 0, NULL, 0) != 0)
684 return;
685
686 /* match */
687
688 /* split line into portid and filename */
689 filename = FSp_pos + 1;
690 FSp_pos[0] = '\0';
691 portid = line;
692
693 get_port_by_id(&arg->store->ports, portid, &port);
694
695 if (arg->should_have_matched && !port->matched)
696 return;
697
698 if ((port->matched & SEARCH_BY_PFILE) == 0)
699 v_start(&port->plist, 2);
700
701 port->matched |= SEARCH_BY_PFILE;
702
703 v_add(&port->plist, filename, strlen(filename) + 1);
704 }
705
706 static void
get_port_by_id(struct ports_t * ports,const char * portid,struct port_t ** port)707 get_port_by_id(struct ports_t *ports, const char *portid, struct port_t **port)
708 {
709 struct port_t key;
710 struct port_t *key_p;
711 struct port_t **res;
712
713 key_p = &key;
714
715 key.id = (unsigned)strtoul(portid, NULL, 10);
716
717 res = (struct port_t **)bsearch(&key_p, ports->arr, ports->sz,
718 sizeof(struct port_t **), ports_cmp);
719
720 if (res == NULL)
721 errx(EX_DATAERR, "corrupted database: port with id %u exists in "
722 "plist file but not found in index file", key.id);
723
724 *port = *res;
725 }
726
727 static size_t
records_cnt(const char * content,char sep)728 records_cnt(const char *content, char sep)
729 {
730 const char *ch;
731 size_t cnt;
732
733 for (cnt = 0, ch = content; *ch != '\0'; ch++)
734 if (*ch == sep)
735 cnt++;
736
737 return cnt;
738 }
739
740 static int
ports_cmp(const void * p1v,const void * p2v)741 ports_cmp(const void *p1v, const void *p2v)
742 {
743 struct port_t *p1;
744 struct port_t *p2;
745
746 p1 = *(struct port_t **)p1v;
747 p2 = *(struct port_t **)p2v;
748
749 if (p1 == NULL && p2 == NULL)
750 return 0;
751 if (p1 == NULL)
752 return -1;
753 if (p2 == NULL)
754 return 1;
755
756 if (p1->id < p2->id)
757 return -1;
758 if (p1->id > p2->id)
759 return 1;
760 return 0;
761 }
762
763 int
s_load_port_by_path(struct store_t * s,const char * path,struct port_t ** port)764 s_load_port_by_path(struct store_t *s, const char *path, struct port_t **port)
765 {
766 size_t i;
767
768 for (i = 0; i < s->ports.sz; i++)
769 {
770 if (s->ports.arr[i] == NULL)
771 continue;
772
773 if (strcmp(s->ports.arr[i]->path, path) == 0)
774 {
775 *port = s->ports.arr[i];
776 return 0;
777 }
778 }
779
780 return -1;
781 }
782
783 void
s_load_port_plist(struct store_t * s,struct port_t * port)784 s_load_port_plist(struct store_t *s, struct port_t *port)
785 {
786 struct pline_t search_pline;
787 struct pline_t *found_pline;
788 struct pline_t *p;
789
790 search_pline.portid = port->id;
791
792 found_pline = (struct pline_t *)bsearch(&search_pline,
793 s->plist->plines,
794 s->plist->plines_cnt,
795 sizeof(struct pline_t),
796 plines_cmp);
797
798 if (found_pline != NULL)
799 {
800 for (p = found_pline - 1; p->portid == port->id; p--)
801 v_add(&port->plist, p->pfile, strlen(p->pfile) + 1);
802
803 for (p = found_pline; p->portid == port->id; p++)
804 v_add(&port->plist, p->pfile, strlen(p->pfile) + 1);
805 }
806 }
807
808 static void
load_plist(struct store_t * s)809 load_plist(struct store_t *s)
810 {
811 char rs[2] = {RSp, '\0'};
812 char fs[2] = {FSp, '\0'};
813 char *raw_p, *rec, *portid;
814 size_t rec_idx;
815
816 s->plist = (struct plist_t *)xmalloc(sizeof(struct plist_t));
817
818 load_file(s->plist_fn, &s->plist->raw);
819
820 s->plist->plines_cnt = records_cnt(s->plist->raw, RSp);
821
822 s->plist->plines = (struct pline_t *)xmalloc(s->plist->plines_cnt *
823 sizeof(struct pline_t));
824
825 raw_p = s->plist->raw;
826
827 for (rec_idx = 0; (rec = strsep(&raw_p, rs)) != NULL; rec_idx++)
828 {
829 if (rec[0] == '\0')
830 continue;
831
832 portid = strsep(&rec, fs);
833
834 s->plist->plines[rec_idx].portid =
835 (unsigned)strtoull(portid, NULL, 10);
836
837 s->plist->plines[rec_idx].pfile = rec;
838 }
839
840 /* normally plist is loaded ordered, but just to make sure */
841 if (mergesort(s->plist->plines,
842 s->plist->plines_cnt,
843 sizeof(struct pline_t),
844 plines_cmp) == -1)
845 err(EX_OSERR, "mergesort()");
846 }
847
848 static void
free_plist(struct store_t * s)849 free_plist(struct store_t *s)
850 {
851 xfree(s->plist->plines);
852
853 free_file(s->plist->raw);
854
855 xfree(s->plist);
856 }
857
858 static int
plines_cmp(const void * l1v,const void * l2v)859 plines_cmp(const void *l1v, const void *l2v)
860 {
861 struct pline_t *l1;
862 struct pline_t *l2;
863
864 l1 = (struct pline_t *)l1v;
865 l2 = (struct pline_t *)l2v;
866
867 if (l1->portid < l2->portid)
868 return -1;
869 if (l1->portid > l2->portid)
870 return 1;
871 return 0;
872 }
873
874 static void
rm_olddir(const struct store_t * s)875 rm_olddir(const struct store_t *s)
876 {
877 const char *ents[] = {s->index_old_fn, s->plist_old_fn};
878 int i;
879
880 for (i = 0; i < sizeof(ents) / sizeof(const char *); i++)
881 if (unlink(ents[i]) == -1)
882 if (errno != ENOENT)
883 err(EX_UNAVAILABLE, "unlink(): %s", ents[i]);
884
885 if (rmdir(s->olddir) == -1)
886 if (errno != ENOENT)
887 err(EX_UNAVAILABLE, "rmdir(): %s", s->olddir);
888 }
889
890 /* EOF */
891