1 /* $NetBSD: plist.c,v 1.1.1.5 2009/08/06 16:55:28 joerg Exp $ */
2
3 #if HAVE_CONFIG_H
4 #include "config.h"
5 #endif
6 #include <nbcompat.h>
7 #if HAVE_SYS_CDEFS_H
8 #include <sys/cdefs.h>
9 #endif
10 __RCSID("$NetBSD: plist.c,v 1.1.1.5 2009/08/06 16:55:28 joerg Exp $");
11
12 /*
13 * FreeBSD install - a package for the installation and maintainance
14 * of non-core utilities.
15 *
16 * Redistribution and use in source and binary forms, with or without
17 * modification, are permitted provided that the following conditions
18 * are met:
19 * 1. Redistributions of source code must retain the above copyright
20 * notice, this list of conditions and the following disclaimer.
21 * 2. Redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution.
24 *
25 * Jordan K. Hubbard
26 * 18 July 1993
27 *
28 * General packing list routines.
29 *
30 */
31
32 /*-
33 * Copyright (c) 2008, 2009 Joerg Sonnenberger <joerg@NetBSD.org>.
34 * All rights reserved.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 *
40 * 1. Redistributions of source code must retain the above copyright
41 * notice, this list of conditions and the following disclaimer.
42 * 2. Redistributions in binary form must reproduce the above copyright
43 * notice, this list of conditions and the following disclaimer in
44 * the documentation and/or other materials provided with the
45 * distribution.
46 *
47 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
48 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
49 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
50 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
51 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
52 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
53 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
54 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
55 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
56 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
57 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
58 * SUCH DAMAGE.
59 */
60
61 #include "lib.h"
62 #if HAVE_ERRNO_H
63 #include <errno.h>
64 #endif
65 #if HAVE_ERR_H
66 #include <err.h>
67 #endif
68 #ifndef NETBSD
69 #include <nbcompat/md5.h>
70 #else
71 #include <md5.h>
72 #endif
73
74 static int delete_with_parents(const char *, Boolean, Boolean);
75
76 /* This struct defines a plist command type */
77 typedef struct cmd_t {
78 const char *c_s; /* string to recognise */
79 pl_ent_t c_type; /* type of command */
80 int c_argc; /* # of arguments */
81 int c_subst; /* can substitute real prefix */
82 } cmd_t;
83
84 /* Commands to recognise */
85 static const cmd_t cmdv[] = {
86 {"cwd", PLIST_CWD, 1, 1},
87 {"src", PLIST_SRC, 1, 1},
88 {"exec", PLIST_CMD, 1, 0},
89 {"unexec", PLIST_UNEXEC, 1, 0},
90 {"mode", PLIST_CHMOD, 1, 0},
91 {"owner", PLIST_CHOWN, 1, 0},
92 {"group", PLIST_CHGRP, 1, 0},
93 {"comment", PLIST_COMMENT, 1, 0},
94 {"ignore", PLIST_IGNORE, 0, 0},
95 {"name", PLIST_NAME, 1, 0},
96 {"display", PLIST_DISPLAY, 1, 0},
97 {"pkgdep", PLIST_PKGDEP, 1, 0},
98 {"pkgcfl", PLIST_PKGCFL, 1, 0},
99 {"pkgdir", PLIST_PKGDIR, 1, 0},
100 {"dirrm", PLIST_DIR_RM, 1, 0},
101 {"option", PLIST_OPTION, 1, 0},
102 {"blddep", PLIST_BLDDEP, 1, 0},
103 {NULL, FAIL, 0, 0}
104 };
105
106 /*
107 * Add an item to the end of a packing list
108 */
109 void
add_plist(package_t * p,pl_ent_t type,const char * arg)110 add_plist(package_t *p, pl_ent_t type, const char *arg)
111 {
112 plist_t *tmp;
113
114 tmp = new_plist_entry();
115 tmp->name = (arg == NULL) ? NULL : xstrdup(arg);
116 tmp->type = type;
117 if (!p->head) {
118 p->head = p->tail = tmp;
119 } else {
120 tmp->prev = p->tail;
121 p->tail->next = tmp;
122 p->tail = tmp;
123 }
124 }
125
126 /*
127 * Add an item to the start of a packing list
128 */
129 void
add_plist_top(package_t * p,pl_ent_t type,const char * arg)130 add_plist_top(package_t *p, pl_ent_t type, const char *arg)
131 {
132 plist_t *tmp;
133
134 tmp = new_plist_entry();
135 tmp->name = (arg == NULL) ? NULL : xstrdup(arg);
136 tmp->type = type;
137 if (!p->head) {
138 p->head = p->tail = tmp;
139 } else {
140 tmp->next = p->head;
141 p->head->prev = tmp;
142 p->head = tmp;
143 }
144 }
145
146 /*
147 * Return the last (most recent) entry in a packing list
148 */
149 plist_t *
last_plist(package_t * p)150 last_plist(package_t *p)
151 {
152 return p->tail;
153 }
154
155 /*
156 * Mark all items in a packing list to prevent iteration over them
157 */
158 void
mark_plist(package_t * pkg)159 mark_plist(package_t *pkg)
160 {
161 plist_t *pp;
162
163 for (pp = pkg->head; pp; pp = pp->next) {
164 pp->marked = TRUE;
165 }
166 }
167
168 /*
169 * Find a given item in a packing list and, if so, return it (else NULL)
170 */
171 plist_t *
find_plist(package_t * pkg,pl_ent_t type)172 find_plist(package_t *pkg, pl_ent_t type)
173 {
174 plist_t *pp;
175
176 for (pp = pkg->head; pp && pp->type != type; pp = pp->next) {
177 }
178 return pp;
179 }
180
181 /*
182 * Look for a specific boolean option argument in the list
183 */
184 char *
find_plist_option(package_t * pkg,const char * name)185 find_plist_option(package_t *pkg, const char *name)
186 {
187 plist_t *p;
188
189 for (p = pkg->head; p; p = p->next) {
190 if (p->type == PLIST_OPTION
191 && strcmp(p->name, name) == 0) {
192 return p->name;
193 }
194 }
195
196 return (char *) NULL;
197 }
198
199 /*
200 * Delete plist item 'type' in the list (if 'name' is non-null, match it
201 * too.) If 'all' is set, delete all items, not just the first occurance.
202 */
203 void
delete_plist(package_t * pkg,Boolean all,pl_ent_t type,char * name)204 delete_plist(package_t *pkg, Boolean all, pl_ent_t type, char *name)
205 {
206 plist_t *p = pkg->head;
207
208 while (p) {
209 plist_t *pnext = p->next;
210
211 if (p->type == type && (!name || !strcmp(name, p->name))) {
212 free(p->name);
213 if (p->prev)
214 p->prev->next = pnext;
215 else
216 pkg->head = pnext;
217 if (pnext)
218 pnext->prev = p->prev;
219 else
220 pkg->tail = p->prev;
221 free(p);
222 if (!all)
223 return;
224 p = pnext;
225 } else
226 p = p->next;
227 }
228 }
229
230 /*
231 * Allocate a new packing list entry, and return a pointer to it.
232 */
233 plist_t *
new_plist_entry(void)234 new_plist_entry(void)
235 {
236 return xcalloc(1, sizeof(plist_t));
237 }
238
239 /*
240 * Free an entire packing list
241 */
242 void
free_plist(package_t * pkg)243 free_plist(package_t *pkg)
244 {
245 plist_t *p = pkg->head;
246
247 while (p) {
248 plist_t *p1 = p->next;
249
250 free(p->name);
251 free(p);
252 p = p1;
253 }
254 pkg->head = pkg->tail = NULL;
255 }
256
257 /*
258 * For an ASCII string denoting a plist command, return its code and
259 * optionally its argument(s)
260 */
261 static int
plist_cmd(const char * s,char ** arg)262 plist_cmd(const char *s, char **arg)
263 {
264 const cmd_t *cmdp;
265 const char *cp, *sp;
266 char *sp2;
267
268 sp = NULL; /* Older GCC can't detect that the loop is executed */
269
270 for (cmdp = cmdv; cmdp->c_s; ++cmdp) {
271 for (sp = s, cp = cmdp->c_s; *sp && *cp; ++cp, ++sp)
272 if (*sp != *cp)
273 break;
274 if (*cp == '\0')
275 break;
276 }
277
278 if (cmdp->c_s == NULL || arg == NULL)
279 return cmdp->c_type;
280
281 while (isspace((unsigned char)*sp))
282 ++sp;
283 *arg = xstrdup(sp);
284 if (*sp) {
285 sp2 = *arg + strlen(*arg) - 1;
286 /*
287 * The earlier loop ensured that at least one non-whitespace
288 * is in the string.
289 */
290 while (isspace((unsigned char)*sp2))
291 --sp2;
292 sp2[1] = '\0';
293 }
294 return cmdp->c_type;
295 }
296
297 /*
298 * Parse a packaging list from a memory buffer.
299 */
300 void
parse_plist(package_t * pkg,const char * buf)301 parse_plist(package_t *pkg, const char *buf)
302 {
303 int cmd;
304 char *line, *cp;
305 const char *eol, *next;
306 size_t len;
307
308 pkg->head = NULL;
309 pkg->tail = NULL;
310
311 for (; *buf; buf = next) {
312 /* Until add_plist can deal with trailing whitespace. */
313 if ((eol = strchr(buf, '\n')) != NULL) {
314 next = eol + 1;
315 len = eol - buf;
316 } else {
317 len = strlen(buf);
318 next = buf + len;
319 }
320
321 while (len && isspace((unsigned char)buf[len - 1]))
322 --len;
323
324 if (len == 0)
325 continue;
326
327 line = xmalloc(len + 1);
328 memcpy(line, buf, len);
329 line[len] = '\0';
330
331 if (*(cp = line) == CMD_CHAR) {
332 if ((cmd = plist_cmd(line + 1, &cp)) == FAIL) {
333 warnx("Unrecognised PLIST command `%s'", line);
334 continue;
335 }
336 if (*cp == '\0') {
337 free(cp);
338 cp = NULL;
339 }
340 } else {
341 cmd = PLIST_FILE;
342 }
343 add_plist(pkg, cmd, cp);
344 free(cp);
345 }
346 }
347
348 /*
349 * Read a packing list from a file
350 */
351 void
append_plist(package_t * pkg,FILE * fp)352 append_plist(package_t *pkg, FILE * fp)
353 {
354 char pline[MaxPathSize];
355 char *cp;
356 int cmd;
357 int len;
358 int free_cp;
359
360 while (fgets(pline, MaxPathSize, fp) != (char *) NULL) {
361 for (len = strlen(pline); len &&
362 isspace((unsigned char) pline[len - 1]);) {
363 pline[--len] = '\0';
364 }
365 if (len == 0) {
366 continue;
367 }
368 free_cp = 0;
369 if (*(cp = pline) == CMD_CHAR) {
370 if ((cmd = plist_cmd(pline + 1, &cp)) == FAIL) {
371 warnx("Unrecognised PLIST command `%s'", pline);
372 continue;
373 }
374 if (*cp == '\0') {
375 free(cp);
376 cp = NULL;
377 }
378 free_cp = 1;
379 } else {
380 cmd = PLIST_FILE;
381 }
382 add_plist(pkg, cmd, cp);
383 if (free_cp)
384 free(cp);
385 }
386 }
387
388 void
read_plist(package_t * pkg,FILE * fp)389 read_plist(package_t *pkg, FILE * fp)
390 {
391 pkg->head = NULL;
392 pkg->tail = NULL;
393
394 append_plist(pkg, fp);
395 }
396
397 /*
398 * Write a packing list to a file, converting commands to ASCII equivs
399 */
400 void
write_plist(package_t * pkg,FILE * fp,char * realprefix)401 write_plist(package_t *pkg, FILE * fp, char *realprefix)
402 {
403 plist_t *p;
404 const cmd_t *cmdp;
405
406 for (p = pkg->head; p; p = p->next) {
407 if (p->type == PLIST_FILE) {
408 /* Fast-track files - these are the most common */
409 (void) fprintf(fp, "%s\n", p->name);
410 continue;
411 }
412 for (cmdp = cmdv; cmdp->c_type != FAIL && cmdp->c_type != p->type; cmdp++) {
413 }
414 if (cmdp->c_type == FAIL) {
415 warnx("Unknown PLIST command type %d (%s)", p->type, p->name);
416 } else if (cmdp->c_argc == 0) {
417 (void) fprintf(fp, "%c%s\n", CMD_CHAR, cmdp->c_s);
418 } else if (cmdp->c_subst && realprefix) {
419 (void) fprintf(fp, "%c%s %s\n", CMD_CHAR, cmdp->c_s, realprefix);
420 } else {
421 (void) fprintf(fp, "%c%s %s\n", CMD_CHAR, cmdp->c_s,
422 (p->name) ? p->name : "");
423 }
424 }
425 }
426
427 /*
428 * Like write_plist, but compute memory string.
429 */
430 void
stringify_plist(package_t * pkg,char ** real_buf,size_t * real_len,const char * realprefix)431 stringify_plist(package_t *pkg, char **real_buf, size_t *real_len,
432 const char *realprefix)
433 {
434 plist_t *p;
435 const cmd_t *cmdp;
436 char *buf;
437 size_t len;
438 int item_len;
439
440 /* Pass One: compute output size only. */
441 len = 0;
442
443 for (p = pkg->head; p; p = p->next) {
444 if (p->type == PLIST_FILE) {
445 len += strlen(p->name) + 1;
446 continue;
447 }
448 for (cmdp = cmdv; cmdp->c_type != FAIL && cmdp->c_type != p->type; cmdp++) {
449 }
450 if (cmdp->c_type == FAIL)
451 continue;
452 if (cmdp->c_argc == 0)
453 len += 1 + strlen(cmdp->c_s) + 1;
454 else if (cmdp->c_subst && realprefix)
455 len += 1 + strlen(cmdp->c_s) + 1 + strlen(realprefix) + 1;
456 else
457 len += 1 + strlen(cmdp->c_s) + 1 + strlen(p->name ? p->name : "") + 1;
458 }
459
460 /* Pass Two: build actual string. */
461 buf = xmalloc(len + 1);
462 *real_buf = buf;
463 *real_len = len;
464 ++len;
465
466 #define UPDATE_LEN \
467 do { \
468 if (item_len < 0 || (size_t)item_len > len) \
469 errx(2, "Size computation failed, aborted."); \
470 buf += item_len; \
471 len -= item_len; \
472 } while (/* CONSTCOND */0)
473
474 for (p = pkg->head; p; p = p->next) {
475 if (p->type == PLIST_FILE) {
476 /* Fast-track files - these are the most common */
477 item_len = snprintf(buf, len, "%s\n", p->name);
478 UPDATE_LEN;
479 continue;
480 }
481 for (cmdp = cmdv; cmdp->c_type != FAIL && cmdp->c_type != p->type; cmdp++) {
482 }
483 if (cmdp->c_type == FAIL) {
484 warnx("Unknown PLIST command type %d (%s)", p->type, p->name);
485 } else if (cmdp->c_argc == 0) {
486 item_len = snprintf(buf, len, "%c%s\n", CMD_CHAR, cmdp->c_s);
487 UPDATE_LEN;
488 } else if (cmdp->c_subst && realprefix) {
489 item_len = snprintf(buf, len, "%c%s %s\n", CMD_CHAR, cmdp->c_s, realprefix);
490 UPDATE_LEN;
491 } else {
492 item_len = snprintf(buf, len, "%c%s %s\n", CMD_CHAR, cmdp->c_s,
493 (p->name) ? p->name : "");
494 UPDATE_LEN;
495 }
496 }
497
498 if (len != 1)
499 errx(2, "Size computation failed, aborted.");
500 }
501
502 /*
503 * Delete the results of a package installation.
504 *
505 * This is here rather than in the pkg_delete code because pkg_add needs to
506 * run it too in cases of failure.
507 */
508 int
delete_package(Boolean ign_err,package_t * pkg,Boolean NoDeleteFiles,const char * destdir)509 delete_package(Boolean ign_err, package_t *pkg, Boolean NoDeleteFiles,
510 const char *destdir)
511 {
512 plist_t *p;
513 const char *last_file = "";
514 int fail = SUCCESS;
515 Boolean preserve;
516 char tmp[MaxPathSize];
517 const char *prefix = NULL, *name = NULL;
518
519 if (!pkgdb_open(ReadWrite)) {
520 err(EXIT_FAILURE, "cannot open pkgdb");
521 }
522
523 preserve = find_plist_option(pkg, "preserve") ? TRUE : FALSE;
524
525 for (p = pkg->head; p; p = p->next) {
526 switch (p->type) {
527 case PLIST_NAME:
528 name = p->name;
529 break;
530 case PLIST_CWD:
531 if (prefix == NULL)
532 prefix = p->name;
533 break;
534 default:
535 break;
536 }
537 }
538
539 if (name == NULL || prefix == NULL)
540 errx(EXIT_FAILURE, "broken PLIST");
541
542 /*
543 * Remove database entries first, directory removal is done
544 * in the main loop below.
545 */
546 for (p = pkg->head; p; p = p->next) {
547 if (p->type == PLIST_PKGDIR)
548 delete_pkgdir(name, prefix, p->name);
549 }
550
551 for (p = pkg->head; p; p = p->next) {
552 switch (p->type) {
553 case PLIST_NAME:
554 /* Handled already */
555 break;
556
557 case PLIST_PKGDIR:
558 case PLIST_DIR_RM:
559 (void) snprintf(tmp, sizeof(tmp), "%s/%s",
560 prefix, p->name);
561 if (has_pkgdir(tmp))
562 continue;
563 (void) snprintf(tmp, sizeof(tmp), "%s%s%s/%s",
564 destdir ? destdir : "", destdir ? "/" : "",
565 prefix, p->name);
566 if (!fexists(tmp)) {
567 if (p->type == PLIST_PKGDIR)
568 warnx("Directory `%s' disappeared, skipping", tmp);
569 } else if (!isdir(tmp)) {
570 warnx("attempting to delete a file `%s' as a directory\n"
571 "this packing list is incorrect - ignoring delete request", tmp);
572 } else if (delete_with_parents(tmp, ign_err, TRUE))
573 fail = FAIL;
574 break;
575
576 case PLIST_IGNORE:
577 p = p->next;
578 break;
579
580 case PLIST_UNEXEC:
581 if (NoDeleteFiles)
582 break;
583 format_cmd(tmp, sizeof(tmp), p->name, prefix, last_file);
584 printf("Executing `%s'\n", tmp);
585 if (!Fake && system(tmp)) {
586 warnx("unexec command for `%s' failed", tmp);
587 fail = FAIL;
588 }
589 break;
590
591 case PLIST_FILE:
592 last_file = p->name;
593 (void) snprintf(tmp, sizeof(tmp), "%s%s%s/%s",
594 destdir ? destdir : "", destdir ? "/" : "",
595 prefix, p->name);
596 if (isdir(tmp)) {
597 warnx("attempting to delete directory `%s' as a file\n"
598 "this packing list is incorrect - ignoring delete request", tmp);
599 } else {
600 int restored = 0; /* restored from preserve? */
601
602 if (p->next && p->next->type == PLIST_COMMENT) {
603 if (strncmp(p->next->name, CHECKSUM_HEADER, ChecksumHeaderLen) == 0) {
604 char *cp, buf[LegibleChecksumLen];
605
606 if ((cp = MD5File(tmp, buf)) != NULL) {
607 /* Mismatch? */
608 if (strcmp(cp, p->next->name + ChecksumHeaderLen) != 0) {
609 printf("original MD5 checksum failed, %s: %s\n",
610 Force ? "deleting anyway" : "not deleting", tmp);
611 if (!Force) {
612 fail = FAIL;
613 goto pkgdb_cleanup;
614 }
615 }
616 }
617 } else if (strncmp(p->next->name, SYMLINK_HEADER, SymlinkHeaderLen) == 0) {
618 char buf[MaxPathSize + SymlinkHeaderLen];
619 int cc;
620
621 (void) strlcpy(buf, SYMLINK_HEADER,
622 sizeof(buf));
623 if ((cc = readlink(tmp, &buf[SymlinkHeaderLen],
624 sizeof(buf) - SymlinkHeaderLen - 1)) < 0) {
625 warn("can't readlink `%s'", tmp);
626 goto pkgdb_cleanup;
627 }
628 buf[SymlinkHeaderLen + cc] = 0x0;
629 if (strcmp(buf, p->next->name) != 0) {
630 if ((cc = readlink(&buf[SymlinkHeaderLen], &buf[SymlinkHeaderLen],
631 sizeof(buf) - SymlinkHeaderLen)) < 0) {
632 printf("symlink %s is not same as recorded value, %s: %s\n",
633 buf, Force ? "deleting anyway" : "not deleting", tmp);
634 if (!Force) {
635 fail = FAIL;
636 goto pkgdb_cleanup;
637 }
638 }
639 buf[SymlinkHeaderLen + cc] = 0x0;
640 if (strcmp(buf, p->next->name) != 0) {
641 printf("symlink %s is not same as recorded value, %s: %s\n",
642 buf, Force ? "deleting anyway" : "not deleting", tmp);
643 if (!Force) {
644 fail = FAIL;
645 goto pkgdb_cleanup;
646 }
647 }
648 }
649 }
650 }
651 if (Verbose && !NoDeleteFiles)
652 printf("Delete file %s\n", tmp);
653 if (!Fake && !NoDeleteFiles) {
654 if (delete_with_parents(tmp, ign_err, FALSE))
655 fail = FAIL;
656 if (preserve && name) {
657 char tmp2[MaxPathSize];
658
659 if (make_preserve_name(tmp2, MaxPathSize, name, tmp)) {
660 if (fexists(tmp2)) {
661 if (rename(tmp2, tmp))
662 warn("preserve: unable to restore %s as %s",
663 tmp2, tmp);
664 else
665 restored = 1;
666 }
667 }
668 }
669 }
670
671 pkgdb_cleanup:
672 if (!Fake) {
673 if (!restored) {
674 errno = 0;
675 if (pkgdb_remove(tmp) && errno)
676 perror("pkgdb_remove");
677 }
678 }
679 }
680 break;
681 default:
682 break;
683 }
684 }
685 pkgdb_close();
686 return fail;
687 }
688
689 /*
690 * Selectively delete a hierarchy
691 * Returns 1 on error, 0 else.
692 */
693 static int
delete_with_parents(const char * fname,Boolean ign_err,Boolean ign_nonempty)694 delete_with_parents(const char *fname, Boolean ign_err, Boolean ign_nonempty)
695 {
696 char *cp, *cp2;
697
698 if (remove(fname)) {
699 if (!ign_err && (!ign_nonempty || errno != ENOTEMPTY))
700 warnx("Couldn't remove %s", fname);
701 return 0;
702 }
703 cp = xstrdup(fname);
704 while (*cp) {
705 if ((cp2 = strrchr(cp, '/')) != NULL)
706 *cp2 = '\0';
707 if (!isemptydir(cp))
708 break;
709 if (has_pkgdir(cp))
710 break;
711 if (rmdir(cp))
712 break;
713 }
714 free(cp);
715
716 return 0;
717 }
718
719 void
add_pkgdir(const char * pkg,const char * prefix,const char * path)720 add_pkgdir(const char *pkg, const char *prefix, const char *path)
721 {
722 char *fullpath, *oldvalue, *newvalue;
723
724 fullpath = xasprintf("%s/%s", prefix, path);
725 oldvalue = pkgdb_retrieve(fullpath);
726 if (oldvalue) {
727 if (strncmp(oldvalue, "@pkgdir ", 8) != 0)
728 errx(EXIT_FAILURE, "Internal error while processing pkgdb, run pkg_admin rebuild");
729 newvalue = xasprintf("%s %s", oldvalue, pkg);
730 pkgdb_remove(fullpath);
731 } else {
732 newvalue = xasprintf("@pkgdir %s", pkg);
733 }
734 pkgdb_store(fullpath, newvalue);
735
736 free(fullpath);
737 free(newvalue);
738 }
739
740 void
delete_pkgdir(const char * pkg,const char * prefix,const char * path)741 delete_pkgdir(const char *pkg, const char *prefix, const char *path)
742 {
743 size_t pkg_len, len;
744 char *fullpath, *oldvalue, *newvalue, *iter;
745
746 fullpath = xasprintf("%s/%s", prefix, path);
747 oldvalue = pkgdb_retrieve(fullpath);
748 if (oldvalue && strncmp(oldvalue, "@pkgdir ", 8) == 0) {
749 newvalue = xstrdup(oldvalue);
750 iter = newvalue + 8;
751 pkg_len = strlen(pkg);
752 while (*iter) {
753 if (strncmp(iter, pkg, pkg_len) == 0 &&
754 (iter[pkg_len] == ' ' || iter[pkg_len] == '\0')) {
755 len = strlen(iter + pkg_len);
756 memmove(iter, iter + pkg_len + 1, len);
757 if (len == 0)
758 *iter = '\0';
759 } else {
760 iter += strcspn(iter, " ");
761 iter += strspn(iter, " ");
762 }
763 }
764 pkgdb_remove(fullpath);
765 if (iter != newvalue + 8)
766 pkgdb_store(fullpath, newvalue);
767 free(newvalue);
768 }
769 free(fullpath);
770 }
771
772 int
has_pkgdir(const char * path)773 has_pkgdir(const char *path)
774 {
775 const char *value;
776
777 value = pkgdb_retrieve(path);
778
779 if (value && strncmp(value, "@pkgdir ", 8) == 0)
780 return 1;
781 else
782 return 0;
783 }
784