spec.c revision 121300
1/*-
2 * Copyright (c) 1989, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided 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 * 3. Neither the name of the University nor the names of its contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#if 0
31#ifndef lint
32static char sccsid[] = "@(#)spec.c	8.1 (Berkeley) 6/6/93";
33#endif /* not lint */
34#endif
35#include <sys/cdefs.h>
36__FBSDID("$FreeBSD: head/usr.sbin/mtree/spec.c 121300 2003-10-21 08:27:05Z phk $");
37
38#include <sys/types.h>
39#include <sys/stat.h>
40#include <ctype.h>
41#include <err.h>
42#include <errno.h>
43#include <fts.h>
44#include <grp.h>
45#include <pwd.h>
46#include <stdio.h>
47#include <unistd.h>
48#include <vis.h>
49#include "mtree.h"
50#include "extern.h"
51
52int lineno;				/* Current spec line number. */
53
54static void	 set(char *, NODE *);
55static void	 unset(char *, NODE *);
56
57NODE *
58spec(void)
59{
60	NODE *centry, *last;
61	char *p;
62	NODE ginfo, *root;
63	int c_cur, c_next;
64	char buf[2048];
65
66	centry = last = root = NULL;
67	bzero(&ginfo, sizeof(ginfo));
68	c_cur = c_next = 0;
69	for (lineno = 1; fgets(buf, sizeof(buf), stdin);
70	    ++lineno, c_cur = c_next, c_next = 0) {
71		/* Skip empty lines. */
72		if (buf[0] == '\n')
73			continue;
74
75		/* Find end of line. */
76		if ((p = index(buf, '\n')) == NULL)
77			errx(1, "line %d too long", lineno);
78
79		/* See if next line is continuation line. */
80		if (p[-1] == '\\') {
81			--p;
82			c_next = 1;
83		}
84
85		/* Null-terminate the line. */
86		*p = '\0';
87
88		/* Skip leading whitespace. */
89		for (p = buf; *p && isspace(*p); ++p);
90
91		/* If nothing but whitespace or comment char, continue. */
92		if (!*p || *p == '#')
93			continue;
94
95#ifdef DEBUG
96		(void)fprintf(stderr, "line %d: {%s}\n", lineno, p);
97#endif
98		if (c_cur) {
99			set(p, centry);
100			continue;
101		}
102
103		/* Grab file name, "$", "set", or "unset". */
104		if ((p = strtok(p, "\n\t ")) == NULL)
105			errx(1, "line %d: missing field", lineno);
106
107		if (p[0] == '/')
108			switch(p[1]) {
109			case 's':
110				if (strcmp(p + 1, "set"))
111					break;
112				set(NULL, &ginfo);
113				continue;
114			case 'u':
115				if (strcmp(p + 1, "unset"))
116					break;
117				unset(NULL, &ginfo);
118				continue;
119			}
120
121		if (index(p, '/'))
122			errx(1, "line %d: slash character in file name",
123			lineno);
124
125		if (!strcmp(p, "..")) {
126			/* Don't go up, if haven't gone down. */
127			if (!root)
128				goto noparent;
129			if (last->type != F_DIR || last->flags & F_DONE) {
130				if (last == root)
131					goto noparent;
132				last = last->parent;
133			}
134			last->flags |= F_DONE;
135			continue;
136
137noparent:		errx(1, "line %d: no parent node", lineno);
138		}
139
140		if ((centry = calloc(1, sizeof(NODE) + strlen(p))) == NULL)
141			errx(1, "calloc");
142		*centry = ginfo;
143#define	MAGIC	"?*["
144		if (strpbrk(p, MAGIC))
145			centry->flags |= F_MAGIC;
146		if (strunvis(centry->name, p) == -1) {
147			warnx("filename %s is ill-encoded and literally used",
148			    p);
149			strcpy(centry->name, p);
150		}
151		set(NULL, centry);
152
153		if (!root) {
154			last = root = centry;
155			root->parent = root;
156		} else if (last->type == F_DIR && !(last->flags & F_DONE)) {
157			centry->parent = last;
158			last = last->child = centry;
159		} else {
160			centry->parent = last->parent;
161			centry->prev = last;
162			last = last->next = centry;
163		}
164	}
165	return (root);
166}
167
168static void
169set(char *t, NODE *ip)
170{
171	int type;
172	char *kw, *val = NULL;
173	struct group *gr;
174	struct passwd *pw;
175	mode_t *m;
176	int value;
177	char *ep;
178
179	for (; (kw = strtok(t, "= \t\n")); t = NULL) {
180		ip->flags |= type = parsekey(kw, &value);
181		if (value && (val = strtok(NULL, " \t\n")) == NULL)
182			errx(1, "line %d: missing value", lineno);
183		switch(type) {
184		case F_CKSUM:
185			ip->cksum = strtoul(val, &ep, 10);
186			if (*ep)
187				errx(1, "line %d: invalid checksum %s",
188				lineno, val);
189			break;
190		case F_MD5:
191			ip->md5digest = strdup(val);
192			if(!ip->md5digest) {
193				errx(1, "strdup");
194			}
195			break;
196		case F_SHA1:
197			ip->sha1digest = strdup(val);
198			if(!ip->sha1digest) {
199				errx(1, "strdup");
200			}
201			break;
202		case F_RMD160:
203			ip->rmd160digest = strdup(val);
204			if(!ip->rmd160digest) {
205				errx(1, "strdup");
206			}
207			break;
208		case F_FLAGS:
209			if (strcmp("none", val) == 0)
210				ip->st_flags = 0;
211			else if (strtofflags(&val, &ip->st_flags, NULL) != 0)
212				errx(1, "line %d: invalid flag %s",lineno, val);
213 			break;
214		case F_GID:
215			ip->st_gid = strtoul(val, &ep, 10);
216			if (*ep)
217				errx(1, "line %d: invalid gid %s", lineno, val);
218			break;
219		case F_GNAME:
220			if ((gr = getgrnam(val)) == NULL)
221			    errx(1, "line %d: unknown group %s", lineno, val);
222			ip->st_gid = gr->gr_gid;
223			break;
224		case F_IGN:
225			/* just set flag bit */
226			break;
227		case F_MODE:
228			if ((m = setmode(val)) == NULL)
229				errx(1, "line %d: invalid file mode %s",
230				lineno, val);
231			ip->st_mode = getmode(m, 0);
232			free(m);
233			break;
234		case F_NLINK:
235			ip->st_nlink = strtoul(val, &ep, 10);
236			if (*ep)
237				errx(1, "line %d: invalid link count %s",
238				lineno,  val);
239			break;
240		case F_SIZE:
241			ip->st_size = strtoq(val, &ep, 10);
242			if (*ep)
243				errx(1, "line %d: invalid size %s",
244				lineno, val);
245			break;
246		case F_SLINK:
247			if ((ip->slink = strdup(val)) == NULL)
248				errx(1, "strdup");
249			break;
250		case F_TIME:
251			ip->st_mtimespec.tv_sec = strtoul(val, &ep, 10);
252			if (*ep != '.')
253				errx(1, "line %d: invalid time %s",
254				lineno, val);
255			val = ep + 1;
256			ip->st_mtimespec.tv_nsec = strtoul(val, &ep, 10);
257			if (*ep)
258				errx(1, "line %d: invalid time %s",
259				lineno, val);
260			break;
261		case F_TYPE:
262			switch(*val) {
263			case 'b':
264				if (!strcmp(val, "block"))
265					ip->type = F_BLOCK;
266				break;
267			case 'c':
268				if (!strcmp(val, "char"))
269					ip->type = F_CHAR;
270				break;
271			case 'd':
272				if (!strcmp(val, "dir"))
273					ip->type = F_DIR;
274				break;
275			case 'f':
276				if (!strcmp(val, "file"))
277					ip->type = F_FILE;
278				if (!strcmp(val, "fifo"))
279					ip->type = F_FIFO;
280				break;
281			case 'l':
282				if (!strcmp(val, "link"))
283					ip->type = F_LINK;
284				break;
285			case 's':
286				if (!strcmp(val, "socket"))
287					ip->type = F_SOCK;
288				break;
289			default:
290				errx(1, "line %d: unknown file type %s",
291				lineno, val);
292			}
293			break;
294		case F_UID:
295			ip->st_uid = strtoul(val, &ep, 10);
296			if (*ep)
297				errx(1, "line %d: invalid uid %s", lineno, val);
298			break;
299		case F_UNAME:
300			if ((pw = getpwnam(val)) == NULL)
301			    errx(1, "line %d: unknown user %s", lineno, val);
302			ip->st_uid = pw->pw_uid;
303			break;
304		}
305	}
306}
307
308static void
309unset(char *t, NODE *ip)
310{
311	char *p;
312
313	while ((p = strtok(t, "\n\t ")))
314		ip->flags &= ~parsekey(p, NULL);
315}
316