plist.c revision 49637
1178476Sjb#ifndef lint 2178476Sjbstatic const char rcsid[] = 3178476Sjb "$Id: plist.c,v 1.27 1998/09/11 07:26:58 jkh Exp $"; 4178476Sjb#endif 5178476Sjb 6178476Sjb/* 7178476Sjb * FreeBSD install - a package for the installation and maintainance 8178476Sjb * of non-core utilities. 9178476Sjb * 10178476Sjb * Redistribution and use in source and binary forms, with or without 11178476Sjb * modification, are permitted provided that the following conditions 12178476Sjb * are met: 13178476Sjb * 1. Redistributions of source code must retain the above copyright 14178476Sjb * notice, this list of conditions and the following disclaimer. 15178476Sjb * 2. Redistributions in binary form must reproduce the above copyright 16178476Sjb * notice, this list of conditions and the following disclaimer in the 17178476Sjb * documentation and/or other materials provided with the distribution. 18178476Sjb * 19178476Sjb * Jordan K. Hubbard 20178476Sjb * 18 July 1993 21178476Sjb * 22178476Sjb * General packing list routines. 23178476Sjb * 24178476Sjb */ 25178476Sjb 26178476Sjb#include "lib.h" 27178476Sjb#include <err.h> 28178476Sjb#include <md5.h> 29178476Sjb 30178476Sjb/* Add an item to a packing list */ 31178476Sjbvoid 32178476Sjbadd_plist(Package *p, plist_t type, char *arg) 33178476Sjb{ 34178476Sjb PackingList tmp; 35178476Sjb 36178476Sjb tmp = new_plist_entry(); 37178476Sjb tmp->name = copy_string(arg); 38178476Sjb tmp->type = type; 39178476Sjb 40178476Sjb if (!p->head) 41178476Sjb p->head = p->tail = tmp; 42178476Sjb else { 43178476Sjb tmp->prev = p->tail; 44178476Sjb p->tail->next = tmp; 45178476Sjb p->tail = tmp; 46178476Sjb } 47178476Sjb} 48178476Sjb 49178476Sjbvoid 50178476Sjbadd_plist_top(Package *p, plist_t type, char *arg) 51178476Sjb{ 52178476Sjb PackingList tmp; 53178476Sjb 54178476Sjb tmp = new_plist_entry(); 55178476Sjb tmp->name = copy_string(arg); 56178476Sjb tmp->type = type; 57178476Sjb 58178476Sjb if (!p->head) 59178476Sjb p->head = p->tail = tmp; 60178476Sjb else { 61178476Sjb tmp->next = p->head; 62178476Sjb p->head->prev = tmp; 63178476Sjb p->head = tmp; 64178476Sjb } 65178476Sjb} 66178476Sjb 67178476Sjb/* Return the last (most recent) entry in a packing list */ 68PackingList 69last_plist(Package *p) 70{ 71 return p->tail; 72} 73 74/* Mark all items in a packing list to prevent iteration over them */ 75void 76mark_plist(Package *pkg) 77{ 78 PackingList p = pkg->head; 79 80 while (p) { 81 p->marked = TRUE; 82 p = p->next; 83 } 84} 85 86/* Find a given item in a packing list and, if so, return it (else NULL) */ 87PackingList 88find_plist(Package *pkg, plist_t type) 89{ 90 PackingList p = pkg->head; 91 92 while (p) { 93 if (p->type == type) 94 return p; 95 p = p->next; 96 } 97 return NULL; 98} 99 100/* Look for a specific boolean option argument in the list */ 101char * 102find_plist_option(Package *pkg, char *name) 103{ 104 PackingList p = pkg->head; 105 106 while (p) { 107 if (p->type == PLIST_OPTION && !strcmp(p->name, name)) 108 return p->name; 109 p = p->next; 110 } 111 return NULL; 112} 113 114/* 115 * Delete plist item 'type' in the list (if 'name' is non-null, match it 116 * too.) If 'all' is set, delete all items, not just the first occurance. 117 */ 118void 119delete_plist(Package *pkg, Boolean all, plist_t type, char *name) 120{ 121 PackingList p = pkg->head; 122 123 while (p) { 124 PackingList pnext = p->next; 125 126 if (p->type == type && (!name || !strcmp(name, p->name))) { 127 free(p->name); 128 if (p->prev) 129 p->prev->next = pnext; 130 else 131 pkg->head = pnext; 132 if (pnext) 133 pnext->prev = p->prev; 134 else 135 pkg->tail = p->prev; 136 free(p); 137 if (!all) 138 return; 139 p = pnext; 140 } 141 else 142 p = p->next; 143 } 144} 145 146/* Allocate a new packing list entry */ 147PackingList 148new_plist_entry(void) 149{ 150 PackingList ret; 151 152 ret = (PackingList)malloc(sizeof(struct _plist)); 153 bzero(ret, sizeof(struct _plist)); 154 return ret; 155} 156 157/* Free an entire packing list */ 158void 159free_plist(Package *pkg) 160{ 161 PackingList p = pkg->head; 162 163 while (p) { 164 PackingList p1 = p->next; 165 166 free(p->name); 167 free(p); 168 p = p1; 169 } 170 pkg->head = pkg->tail = NULL; 171} 172 173/* 174 * For an ascii string denoting a plist command, return its code and 175 * optionally its argument(s) 176 */ 177int 178plist_cmd(char *s, char **arg) 179{ 180 char cmd[FILENAME_MAX + 20]; /* 20 == fudge for max cmd len */ 181 char *cp, *sp; 182 183 strcpy(cmd, s); 184 str_lowercase(cmd); 185 cp = cmd; 186 sp = s; 187 while (*cp) { 188 if (isspace(*cp)) { 189 *cp = '\0'; 190 while (isspace(*sp)) /* Never sure if macro, increment later */ 191 ++sp; 192 break; 193 } 194 ++cp, ++sp; 195 } 196 if (arg) 197 *arg = sp; 198 if (!strcmp(cmd, "cwd")) 199 return PLIST_CWD; 200 else if (!strcmp(cmd, "srcdir")) 201 return PLIST_SRC; 202 else if (!strcmp(cmd, "cd")) 203 return PLIST_CWD; 204 else if (!strcmp(cmd, "exec")) 205 return PLIST_CMD; 206 else if (!strcmp(cmd, "unexec")) 207 return PLIST_UNEXEC; 208 else if (!strcmp(cmd, "mode")) 209 return PLIST_CHMOD; 210 else if (!strcmp(cmd, "owner")) 211 return PLIST_CHOWN; 212 else if (!strcmp(cmd, "group")) 213 return PLIST_CHGRP; 214 else if (!strcmp(cmd, "comment")) 215 return PLIST_COMMENT; 216 else if (!strcmp(cmd, "ignore")) 217 return PLIST_IGNORE; 218 else if (!strcmp(cmd, "ignore_inst")) 219 return PLIST_IGNORE_INST; 220 else if (!strcmp(cmd, "name")) 221 return PLIST_NAME; 222 else if (!strcmp(cmd, "display")) 223 return PLIST_DISPLAY; 224 else if (!strcmp(cmd, "pkgdep")) 225 return PLIST_PKGDEP; 226 else if (!strcmp(cmd, "mtree")) 227 return PLIST_MTREE; 228 else if (!strcmp(cmd, "dirrm")) 229 return PLIST_DIR_RM; 230 else if (!strcmp(cmd, "option")) 231 return PLIST_OPTION; 232 else 233 return FAIL; 234} 235 236/* Read a packing list from a file */ 237void 238read_plist(Package *pkg, FILE *fp) 239{ 240 char *cp, pline[FILENAME_MAX]; 241 int cmd; 242 243 while (fgets(pline, FILENAME_MAX, fp)) { 244 int len = strlen(pline); 245 246 while (len && isspace(pline[len - 1])) 247 pline[--len] = '\0'; 248 if (!len) 249 continue; 250 cp = pline; 251 if (pline[0] == CMD_CHAR) { 252 cmd = plist_cmd(pline + 1, &cp); 253 if (cmd == FAIL) { 254 cleanup(0); 255 errx(2, "bad command '%s'", pline); 256 } 257 if (*cp == '\0') 258 cp = NULL; 259 } 260 else 261 cmd = PLIST_FILE; 262 add_plist(pkg, cmd, cp); 263 } 264} 265 266/* Write a packing list to a file, converting commands to ascii equivs */ 267void 268write_plist(Package *pkg, FILE *fp) 269{ 270 PackingList plist = pkg->head; 271 272 while (plist) { 273 switch(plist->type) { 274 case PLIST_FILE: 275 fprintf(fp, "%s\n", plist->name); 276 break; 277 278 case PLIST_CWD: 279 fprintf(fp, "%ccwd %s\n", CMD_CHAR, plist->name); 280 break; 281 282 case PLIST_SRC: 283 fprintf(fp, "%csrcdir %s\n", CMD_CHAR, plist->name); 284 break; 285 286 case PLIST_CMD: 287 fprintf(fp, "%cexec %s\n", CMD_CHAR, plist->name); 288 break; 289 290 case PLIST_UNEXEC: 291 fprintf(fp, "%cunexec %s\n", CMD_CHAR, plist->name); 292 break; 293 294 case PLIST_CHMOD: 295 fprintf(fp, "%cmode %s\n", CMD_CHAR, plist->name ? plist->name : ""); 296 break; 297 298 case PLIST_CHOWN: 299 fprintf(fp, "%cowner %s\n", CMD_CHAR, plist->name ? plist->name : ""); 300 break; 301 302 case PLIST_CHGRP: 303 fprintf(fp, "%cgroup %s\n", CMD_CHAR, plist->name ? plist->name : ""); 304 break; 305 306 case PLIST_COMMENT: 307 fprintf(fp, "%ccomment %s\n", CMD_CHAR, plist->name); 308 break; 309 310 case PLIST_IGNORE: 311 case PLIST_IGNORE_INST: /* a one-time non-ignored file */ 312 fprintf(fp, "%cignore\n", CMD_CHAR); 313 break; 314 315 case PLIST_NAME: 316 fprintf(fp, "%cname %s\n", CMD_CHAR, plist->name); 317 break; 318 319 case PLIST_DISPLAY: 320 fprintf(fp, "%cdisplay %s\n", CMD_CHAR, plist->name); 321 break; 322 323 case PLIST_PKGDEP: 324 fprintf(fp, "%cpkgdep %s\n", CMD_CHAR, plist->name); 325 break; 326 327 case PLIST_MTREE: 328 fprintf(fp, "%cmtree %s\n", CMD_CHAR, plist->name); 329 break; 330 331 case PLIST_DIR_RM: 332 fprintf(fp, "%cdirrm %s\n", CMD_CHAR, plist->name); 333 break; 334 335 case PLIST_OPTION: 336 fprintf(fp, "%coption %s\n", CMD_CHAR, plist->name); 337 break; 338 339 default: 340 cleanup(0); 341 errx(2, "unknown command type %d (%s)", plist->type, plist->name); 342 break; 343 } 344 plist = plist->next; 345 } 346} 347 348/* 349 * Delete the results of a package installation. 350 * 351 * This is here rather than in the pkg_delete code because pkg_add needs to 352 * run it too in cases of failure. 353 */ 354int 355delete_package(Boolean ign_err, Boolean nukedirs, Package *pkg) 356{ 357 PackingList p; 358 char *Where = ".", *last_file = ""; 359 Boolean fail = SUCCESS; 360 Boolean preserve; 361 char tmp[FILENAME_MAX], *name = NULL; 362 363 preserve = find_plist_option(pkg, "preserve") ? TRUE : FALSE; 364 for (p = pkg->head; p; p = p->next) { 365 switch (p->type) { 366 case PLIST_NAME: 367 name = p->name; 368 break; 369 370 case PLIST_IGNORE: 371 p = p->next; 372 break; 373 374 case PLIST_CWD: 375 Where = p->name; 376 if (Verbose) 377 printf("Change working directory to %s\n", Where); 378 break; 379 380 case PLIST_UNEXEC: 381 format_cmd(tmp, p->name, Where, last_file); 382 if (Verbose) 383 printf("Execute `%s'\n", tmp); 384 if (!Fake && system(tmp)) { 385 warnx("unexec command for `%s' failed", tmp); 386 fail = FAIL; 387 } 388 break; 389 390 case PLIST_FILE: 391 last_file = p->name; 392 sprintf(tmp, "%s/%s", Where, p->name); 393 if (isdir(tmp) && fexists(tmp)) { 394 warnx("cannot delete specified file `%s' - it is a directory!\n" 395 "this packing list is incorrect - ignoring delete request", tmp); 396 } 397 else { 398 if (p->next && p->next->type == PLIST_COMMENT && !strncmp(p->next->name, "MD5:", 4)) { 399 char *cp, buf[33]; 400 401 if ((cp = MD5File(tmp, buf)) != NULL) { 402 /* Mismatch? */ 403 if (strcmp(cp, p->next->name + 4)) { 404 if (Verbose) 405 printf("%s fails original MD5 checksum - %s\n", 406 tmp, Force ? "deleted anyway." : "not deleted."); 407 if (!Force) { 408 fail = FAIL; 409 continue; 410 } 411 } 412 } 413 } 414 if (Verbose) 415 printf("Delete file %s\n", tmp); 416 if (!Fake) { 417 if (delete_hierarchy(tmp, ign_err, nukedirs)) 418 fail = FAIL; 419 if (preserve && name) { 420 char tmp2[FILENAME_MAX]; 421 422 if (make_preserve_name(tmp2, FILENAME_MAX, name, tmp)) { 423 if (fexists(tmp2)) { 424 if (rename(tmp2, tmp)) 425 warn("preserve: unable to restore %s as %s", 426 tmp2, tmp); 427 } 428 } 429 } 430 } 431 } 432 break; 433 434 case PLIST_DIR_RM: 435 sprintf(tmp, "%s/%s", Where, p->name); 436 if (!isdir(tmp) && fexists(tmp)) { 437 warnx("cannot delete specified directory `%s' - it is a file!\n" 438 "this packing list is incorrect - ignoring delete request", tmp); 439 } 440 else { 441 if (Verbose) 442 printf("Delete directory %s\n", tmp); 443 if (!Fake && delete_hierarchy(tmp, ign_err, FALSE)) { 444 warnx("unable to completely remove directory '%s'", tmp); 445 fail = FAIL; 446 } 447 } 448 last_file = p->name; 449 break; 450 } 451 } 452 return fail; 453} 454 455#ifdef DEBUG 456#define RMDIR(dir) vsystem("%s %s", RMDIR_CMD, dir) 457#define REMOVE(dir,ie) vsystem("%s %s%s", REMOVE_CMD, (ie ? "-f " : ""), dir) 458#else 459#define RMDIR rmdir 460#define REMOVE(file,ie) (remove(file) && !(ie)) 461#endif 462 463/* Selectively delete a hierarchy */ 464int 465delete_hierarchy(char *dir, Boolean ign_err, Boolean nukedirs) 466{ 467 char *cp1, *cp2; 468 469 cp1 = cp2 = dir; 470 if (!fexists(dir)) { 471 if (!ign_err) 472 warnx("%s `%s' doesn't really exist", 473 isdir(dir) ? "directory" : "file", dir); 474 return !ign_err; 475 } 476 else if (nukedirs) { 477 if (vsystem("%s -r%s %s", REMOVE_CMD, (ign_err ? "f" : ""), dir)) 478 return 1; 479 } 480 else if (isdir(dir)) { 481 if (RMDIR(dir) && !ign_err) 482 return 1; 483 } 484 else { 485 if (REMOVE(dir, ign_err)) 486 return 1; 487 } 488 489 if (!nukedirs) 490 return 0; 491 while (cp2) { 492 if ((cp2 = rindex(cp1, '/')) != NULL) 493 *cp2 = '\0'; 494 if (!isemptydir(dir)) 495 return 0; 496 if (RMDIR(dir) && !ign_err) { 497 if (!fexists(dir)) 498 warnx("directory `%s' doesn't really exist", dir); 499 else 500 return 1; 501 } 502 /* back up the pathname one component */ 503 if (cp2) { 504 cp1 = dir; 505 } 506 } 507 return 0; 508} 509