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