plist.c revision 12219
1#ifndef lint
2static const char *rcsid = "$Id: plist.c,v 1.14 1995/07/28 01:50:35 ache Exp $";
3#endif
4
5/*
6 * FreeBSD install - a package for the installation and maintainance
7 * of non-core utilities.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * Jordan K. Hubbard
19 * 18 July 1993
20 *
21 * General packing list routines.
22 *
23 */
24
25#include "lib.h"
26
27/* Add an item to a packing list */
28void
29add_plist(Package *p, plist_t type, char *arg)
30{
31    PackingList tmp;
32
33    tmp = new_plist_entry();
34    tmp->name = copy_string(arg);
35    tmp->type = type;
36
37    if (!p->head)
38	p->head = p->tail = tmp;
39    else {
40	tmp->prev = p->tail;
41	p->tail->next = tmp;
42	p->tail = tmp;
43    }
44}
45
46void
47add_plist_top(Package *p, plist_t type, char *arg)
48{
49    PackingList tmp;
50
51    tmp = new_plist_entry();
52    tmp->name = copy_string(arg);
53    tmp->type = type;
54
55    if (!p->head)
56	p->head = p->tail = tmp;
57    else {
58	tmp->next = p->head;
59	p->head->prev = tmp;
60	p->head = tmp;
61    }
62}
63
64/* Return the last (most recent) entry in a packing list */
65PackingList
66last_plist(Package *p)
67{
68    return p->tail;
69}
70
71/* Mark all items in a packing list to prevent iteration over them */
72void
73mark_plist(Package *pkg)
74{
75    PackingList p = pkg->head;
76
77    while (p) {
78	p->marked = TRUE;
79	p = p->next;
80    }
81}
82
83/* Find a given item in a packing list and, if so, return it (else NULL) */
84PackingList
85find_plist(Package *pkg, plist_t type)
86{
87    PackingList p = pkg->head;
88
89    while (p) {
90	if (p->type == type)
91	    return p;
92	p = p->next;
93    }
94    return NULL;
95}
96
97/* Look for a specific boolean option argument in the list */
98char *
99find_plist_option(Package *pkg, char *name)
100{
101    PackingList p = pkg->head;
102
103    while (p) {
104	if (p->type == PLIST_OPTION && !strcmp(p->name, name))
105	    return p->name;
106	p = p->next;
107    }
108    return NULL;
109}
110
111/*
112 * Delete plist item 'type' in the list (if 'name' is non-null, match it
113 * too.)  If 'all' is set, delete all items, not just the first occurance.
114 */
115void
116delete_plist(Package *pkg, Boolean all, plist_t type, char *name)
117{
118    PackingList p = pkg->head;
119
120    while (p) {
121	PackingList pnext = p->next;
122
123	if (p->type == type && (!name || !strcmp(name, p->name))) {
124	    free(p->name);
125	    if (p->prev)
126		p->prev->next = pnext;
127	    else
128		pkg->head = pnext;
129	    if (pnext)
130		pnext->prev = p->prev;
131	    else
132		pkg->tail = p->prev;
133	    free(p);
134	    if (!all)
135		return;
136	    p = pnext;
137	}
138	else
139	    p = p->next;
140    }
141}
142
143/* Allocate a new packing list entry */
144PackingList
145new_plist_entry(void)
146{
147    PackingList ret;
148
149    ret = (PackingList)malloc(sizeof(struct _plist));
150    bzero(ret, sizeof(struct _plist));
151    return ret;
152}
153
154/* Free an entire packing list */
155void
156free_plist(Package *pkg)
157{
158    PackingList p = pkg->head;
159
160    while (p) {
161	PackingList p1 = p->next;
162
163	free(p->name);
164	free(p);
165	p = p1;
166    }
167    pkg->head = pkg->tail = NULL;
168}
169
170/*
171 * For an ascii string denoting a plist command, return its code and
172 * optionally its argument(s)
173 */
174int
175plist_cmd(char *s, char **arg)
176{
177    char cmd[FILENAME_MAX + 20];	/* 20 == fudge for max cmd len */
178    char *cp, *sp;
179
180    strcpy(cmd, s);
181    str_lowercase(cmd);
182    cp = cmd;
183    sp = s;
184    while (*cp) {
185	if (isspace(*cp)) {
186	    *cp = '\0';
187	    while (isspace(*sp)) /* Never sure if macro, increment later */
188		++sp;
189	    break;
190	}
191	++cp, ++sp;
192    }
193    if (arg)
194	*arg = sp;
195    if (!strcmp(cmd, "cwd"))
196	return PLIST_CWD;
197    else if (!strcmp(cmd, "srcdir"))
198	return PLIST_SRC;
199    else if (!strcmp(cmd, "cd"))
200	return PLIST_CWD;
201    else if (!strcmp(cmd, "exec"))
202	return PLIST_CMD;
203    else if (!strcmp(cmd, "unexec"))
204	return PLIST_UNEXEC;
205    else if (!strcmp(cmd, "mode"))
206	return PLIST_CHMOD;
207    else if (!strcmp(cmd, "owner"))
208	return PLIST_CHOWN;
209    else if (!strcmp(cmd, "group"))
210	return PLIST_CHGRP;
211    else if (!strcmp(cmd, "comment"))
212	return PLIST_COMMENT;
213    else if (!strcmp(cmd, "ignore"))
214	return PLIST_IGNORE;
215    else if (!strcmp(cmd, "ignore_inst"))
216	return PLIST_IGNORE_INST;
217    else if (!strcmp(cmd, "name"))
218	return PLIST_NAME;
219    else if (!strcmp(cmd, "display"))
220	return PLIST_DISPLAY;
221    else if (!strcmp(cmd, "pkgdep"))
222	return PLIST_PKGDEP;
223    else if (!strcmp(cmd, "mtree"))
224	return PLIST_MTREE;
225    else if (!strcmp(cmd, "dirrm"))
226	return PLIST_DIR_RM;
227    else if (!strcmp(cmd, "option"))
228	return PLIST_OPTION;
229    else
230	return FAIL;
231}
232
233/* Read a packing list from a file */
234void
235read_plist(Package *pkg, FILE *fp)
236{
237    char *cp, pline[FILENAME_MAX];
238    int cmd;
239
240    while (fgets(pline, FILENAME_MAX, fp)) {
241	int len = strlen(pline) - 1;
242
243	while (isspace(pline[len]))
244	    pline[len--] = '\0';
245	if (len <= 0)
246	    continue;
247	cp = pline;
248	if (pline[0] == CMD_CHAR) {
249	    cmd = plist_cmd(pline + 1, &cp);
250	    if (cmd == FAIL)
251		barf("Bad command '%s'", pline);
252	    if (*cp == '\0')
253		cp = NULL;
254	}
255	else
256	    cmd = PLIST_FILE;
257	add_plist(pkg, cmd, cp);
258    }
259}
260
261/* Write a packing list to a file, converting commands to ascii equivs */
262void
263write_plist(Package *pkg, FILE *fp)
264{
265    PackingList plist = pkg->head;
266
267    while (plist) {
268	switch(plist->type) {
269	case PLIST_FILE:
270	    fprintf(fp, "%s\n", plist->name);
271	    break;
272
273	case PLIST_CWD:
274	    fprintf(fp, "%ccwd %s\n", CMD_CHAR, plist->name);
275	    break;
276
277	case PLIST_SRC:
278	    fprintf(fp, "%csrcdir %s\n", CMD_CHAR, plist->name);
279	    break;
280
281	case PLIST_CMD:
282	    fprintf(fp, "%cexec %s\n", CMD_CHAR, plist->name);
283	    break;
284
285	case PLIST_UNEXEC:
286	    fprintf(fp, "%cunexec %s\n", CMD_CHAR, plist->name);
287	    break;
288
289	case PLIST_CHMOD:
290	    fprintf(fp, "%cmode %s\n", CMD_CHAR, plist->name ? plist->name : "");
291	    break;
292
293	case PLIST_CHOWN:
294	    fprintf(fp, "%cowner %s\n", CMD_CHAR, plist->name ? plist->name : "");
295	    break;
296
297	case PLIST_CHGRP:
298	    fprintf(fp, "%cgroup %s\n", CMD_CHAR, plist->name ? plist->name : "");
299	    break;
300
301	case PLIST_COMMENT:
302	    fprintf(fp, "%ccomment %s\n", CMD_CHAR, plist->name);
303	    break;
304
305	case PLIST_IGNORE:
306	case PLIST_IGNORE_INST:		/* a one-time non-ignored file */
307	    fprintf(fp, "%cignore\n", CMD_CHAR);
308	    break;
309
310	case PLIST_NAME:
311	    fprintf(fp, "%cname %s\n", CMD_CHAR, plist->name);
312	    break;
313
314	case PLIST_DISPLAY:
315	    fprintf(fp, "%cdisplay %s\n", CMD_CHAR, plist->name);
316	    break;
317
318	case PLIST_PKGDEP:
319	    fprintf(fp, "%cpkgdep %s\n", CMD_CHAR, plist->name);
320	    break;
321
322	case PLIST_MTREE:
323	    fprintf(fp, "%cmtree %s\n", CMD_CHAR, plist->name);
324	    break;
325
326	case PLIST_DIR_RM:
327	    fprintf(fp, "%cdirrm %s\n", CMD_CHAR, plist->name);
328	    break;
329
330	case PLIST_OPTION:
331	    fprintf(fp, "%coption %s\n", CMD_CHAR, plist->name);
332	    break;
333
334	default:
335	    barf("Unknown command type %d (%s)\n", plist->type, plist->name);
336	    break;
337	}
338	plist = plist->next;
339    }
340}
341
342/*
343 * Delete the results of a package installation.
344 *
345 * This is here rather than in the pkg_delete code because pkg_add needs to
346 * run it too in cases of failure.
347 */
348int
349delete_package(Boolean ign_err, Boolean nukedirs, Package *pkg)
350{
351    PackingList p = pkg->head;
352    char *Where = ".", *last_file = "";
353    Boolean fail = SUCCESS;
354
355    if (!p)
356	return FAIL;
357    while (p) {
358	if (p->type == PLIST_CWD) {
359	    Where = p->name;
360	    if (Verbose)
361		printf("Change working directory to %s\n", Where);
362	}
363	else if (p->type == PLIST_UNEXEC) {
364	    char cmd[FILENAME_MAX];
365
366	    format_cmd(cmd, p->name, Where, last_file);
367	    if (Verbose)
368		printf("Execute `%s'\n", cmd);
369	    if (!Fake && system(cmd)) {
370		whinge("unexec command for `%s' failed.", cmd);
371		fail = FAIL;
372	    }
373	}
374	else if (p->type == PLIST_IGNORE)
375	    p = p->next;
376	else if (p->type == PLIST_FILE || p->type == PLIST_DIR_RM) {
377	    char full_name[FILENAME_MAX];
378
379	    sprintf(full_name, "%s/%s", Where, p->name);
380	    if (isdir(full_name) && p->type == PLIST_FILE) {
381		warn("Attempting to delete directory `%s' as a file\n"
382		     "This packing list is incorrect - ignoring delete request.\n", full_name);
383	    }
384	    else {
385		if (Verbose)
386		    printf("Delete %s %s\n", !isdir(full_name) ? "file" : " directory", full_name);
387
388		if (!Fake && delete_hierarchy(full_name, ign_err, p->type == PLIST_DIR_RM ? FALSE : nukedirs)) {
389		    whinge("Unable to completely remove file '%s'", full_name);
390		    fail = FAIL;
391		}
392	    }
393	    last_file = p->name;
394	}
395	p = p->next;
396    }
397    return fail;
398}
399
400#ifdef DEBUG
401#define RMDIR(dir) vsystem("%s %s", RMDIR_CMD, dir)
402#define REMOVE(dir,ie) vsystem("%s %s%s", REMOVE_CMD, (ie ? "-f " : ""), dir)
403#else
404#define RMDIR rmdir
405#define	REMOVE(file,ie) (remove(file) && !(ie))
406#endif
407
408/* Selectively delete a hierarchy */
409int
410delete_hierarchy(char *dir, Boolean ign_err, Boolean nukedirs)
411{
412    char *cp1, *cp2;
413
414    cp1 = cp2 = dir;
415    if (!fexists(dir)) {
416	if (!ign_err)
417	    whinge("%s `%s' doesn't really exist.", isdir(dir) ? "Directory" : "File", dir);
418    } else if (nukedirs) {
419	if (vsystem("%s -r%s %s", REMOVE_CMD, (ign_err ? "f" : ""), dir))
420	    return 1;
421    } else if (isdir(dir)) {
422	if (RMDIR(dir) && !ign_err)
423	    return 1;
424    } else {
425	if (REMOVE(dir, ign_err))
426	    return 1;
427    }
428
429    if (!nukedirs)
430	return 0;
431    while (cp2) {
432	if ((cp2 = rindex(cp1, '/')) != NULL)
433	    *cp2 = '\0';
434	if (!isemptydir(dir))
435	    return 0;
436	if (RMDIR(dir) && !ign_err)
437	    if (!fexists(dir))
438		whinge("Directory `%s' doesn't really exist.", dir);
439	    else
440		return 1;
441	/* back up the pathname one component */
442	if (cp2) {
443	    cp1 = dir;
444	}
445    }
446    return 0;
447}
448