xref: /dragonfly/usr.sbin/mtree/spec.c (revision ef3ac1d1)
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  * @(#)spec.c	8.1 (Berkeley) 6/6/93
30  * $FreeBSD: src/usr.sbin/mtree/spec.c,v 1.13.2.1 2000/06/28 02:33:17 joe Exp $
31  * $DragonFly: src/usr.sbin/mtree/spec.c,v 1.6 2004/08/30 19:27:22 eirikn Exp $
32  */
33 
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <ctype.h>
37 #include <err.h>
38 #include <errno.h>
39 #include <fts.h>
40 #include <grp.h>
41 #include <pwd.h>
42 #include <stdio.h>
43 #include <unistd.h>
44 #include <vis.h>
45 #include "mtree.h"
46 #include "extern.h"
47 
48 int lineno;				/* Current spec line number. */
49 
50 static void	 set(char *, NODE *);
51 static void	 unset(char *, NODE *);
52 
53 NODE *
54 spec(void)
55 {
56 	NODE *centry, *last;
57 	char *p;
58 	NODE ginfo, *root;
59 	int c_cur, c_next;
60 	char buf[2048];
61 
62 	centry = last = root = NULL;
63 	bzero(&ginfo, sizeof(ginfo));
64 	c_cur = c_next = 0;
65 	for (lineno = 1; fgets(buf, sizeof(buf), stdin);
66 	    ++lineno, c_cur = c_next, c_next = 0) {
67 		/* Skip empty lines. */
68 		if (buf[0] == '\n')
69 			continue;
70 
71 		/* Find end of line. */
72 		if ((p = strchr(buf, '\n')) == NULL)
73 			errx(1, "line %d too long", lineno);
74 
75 		/* See if next line is continuation line. */
76 		if (p[-1] == '\\') {
77 			--p;
78 			c_next = 1;
79 		}
80 
81 		/* Null-terminate the line. */
82 		*p = '\0';
83 
84 		/* Skip leading whitespace. */
85 		for (p = buf; *p && isspace(*p); ++p);
86 
87 		/* If nothing but whitespace or comment char, continue. */
88 		if (!*p || *p == '#')
89 			continue;
90 
91 #ifdef DEBUG
92 		fprintf(stderr, "line %d: {%s}\n", lineno, p);
93 #endif
94 		if (c_cur) {
95 			set(p, centry);
96 			continue;
97 		}
98 
99 		/* Grab file name, "$", "set", or "unset". */
100 		if ((p = strtok(p, "\n\t ")) == NULL)
101 			errx(1, "line %d: missing field", lineno);
102 
103 		if (p[0] == '/')
104 			switch(p[1]) {
105 			case 's':
106 				if (strcmp(p + 1, "set"))
107 					break;
108 				set(NULL, &ginfo);
109 				continue;
110 			case 'u':
111 				if (strcmp(p + 1, "unset"))
112 					break;
113 				unset(NULL, &ginfo);
114 				continue;
115 			}
116 
117 		if (strchr(p, '/'))
118 			errx(1, "line %d: slash character in file name",
119 			lineno);
120 
121 		if (!strcmp(p, "..")) {
122 			/* Don't go up, if haven't gone down. */
123 			if (!root)
124 				goto noparent;
125 			if (last->type != F_DIR || last->flags & F_DONE) {
126 				if (last == root)
127 					goto noparent;
128 				last = last->parent;
129 			}
130 			last->flags |= F_DONE;
131 			continue;
132 
133 noparent:		errx(1, "line %d: no parent node", lineno);
134 		}
135 
136 		if ((centry = calloc(1, sizeof(NODE) + strlen(p))) == NULL)
137 			errx(1, "calloc");
138 		*centry = ginfo;
139 #define	MAGIC	"?*["
140 		if (strpbrk(p, MAGIC))
141 			centry->flags |= F_MAGIC;
142 		if (strunvis(centry->name, p) == -1) {
143 			warnx("filename %s is ill-encoded and literally used",
144 			    p);
145 			strcpy(centry->name, p);
146 		}
147 		set(NULL, centry);
148 
149 		if (!root) {
150 			last = root = centry;
151 			root->parent = root;
152 		} else if (last->type == F_DIR && !(last->flags & F_DONE)) {
153 			centry->parent = last;
154 			last = last->child = centry;
155 		} else {
156 			centry->parent = last->parent;
157 			centry->prev = last;
158 			last = last->next = centry;
159 		}
160 	}
161 	return (root);
162 }
163 
164 static void
165 set(char *t, NODE *ip)
166 {
167 	int type;
168 	char *kw, *val = NULL;
169 	struct group *gr;
170 	struct passwd *pw;
171 	mode_t *m;
172 	int value;
173 	char *ep;
174 
175 	for (; (kw = strtok(t, "= \t\n")); t = NULL) {
176 		ip->flags |= type = parsekey(kw, &value);
177 		if (value && (val = strtok(NULL, " \t\n")) == NULL)
178 			errx(1, "line %d: missing value", lineno);
179 		switch(type) {
180 		case F_CKSUM:
181 			ip->cksum = strtoul(val, &ep, 10);
182 			if (*ep)
183 				errx(1, "line %d: invalid checksum %s",
184 				lineno, val);
185 			break;
186 		case F_MD5:
187 			ip->md5digest = strdup(val);
188 			if(!ip->md5digest) {
189 				errx(1, "strdup");
190 			}
191 			break;
192 		case F_SHA1:
193 			ip->sha1digest = strdup(val);
194 			if(!ip->sha1digest) {
195 				errx(1, "strdup");
196 			}
197 			break;
198 		case F_RMD160:
199 			ip->rmd160digest = strdup(val);
200 			if(!ip->rmd160digest) {
201 				errx(1, "strdup");
202 			}
203 			break;
204 		case F_FLAGS:
205 			if (strcmp("none", val) == 0)
206 				ip->st_flags = 0;
207 			else if (strtofflags(&val, &ip->st_flags, NULL) != 0)
208 				errx(1, "line %d: invalid flag %s",lineno, val);
209  			break;
210 		case F_GID:
211 			ip->st_gid = strtoul(val, &ep, 10);
212 			if (*ep)
213 				errx(1, "line %d: invalid gid %s", lineno, val);
214 			break;
215 		case F_GNAME:
216 			if ((gr = getgrnam(val)) == NULL)
217 			    errx(1, "line %d: unknown group %s", lineno, val);
218 			ip->st_gid = gr->gr_gid;
219 			break;
220 		case F_IGN:
221 			/* just set flag bit */
222 			break;
223 		case F_MODE:
224 			if ((m = setmode(val)) == NULL)
225 				errx(1, "line %d: invalid file mode %s",
226 				lineno, val);
227 			ip->st_mode = getmode(m, 0);
228 			free(m);
229 			break;
230 		case F_NLINK:
231 			ip->st_nlink = strtoul(val, &ep, 10);
232 			if (*ep)
233 				errx(1, "line %d: invalid link count %s",
234 				lineno,  val);
235 			break;
236 		case F_SIZE:
237 			ip->st_size = strtoq(val, &ep, 10);
238 			if (*ep)
239 				errx(1, "line %d: invalid size %s",
240 				lineno, val);
241 			break;
242 		case F_SLINK:
243 			if ((ip->slink = strdup(val)) == NULL)
244 				errx(1, "strdup");
245 			break;
246 		case F_TIME:
247 			ip->st_mtimespec.tv_sec = strtoul(val, &ep, 10);
248 			if (*ep != '.')
249 				errx(1, "line %d: invalid time %s",
250 				lineno, val);
251 			val = ep + 1;
252 			ip->st_mtimespec.tv_nsec = strtoul(val, &ep, 10);
253 			if (*ep)
254 				errx(1, "line %d: invalid time %s",
255 				lineno, val);
256 			break;
257 		case F_TYPE:
258 			switch(*val) {
259 			case 'b':
260 				if (!strcmp(val, "block"))
261 					ip->type = F_BLOCK;
262 				break;
263 			case 'c':
264 				if (!strcmp(val, "char"))
265 					ip->type = F_CHAR;
266 				break;
267 			case 'd':
268 				if (!strcmp(val, "dir"))
269 					ip->type = F_DIR;
270 				break;
271 			case 'f':
272 				if (!strcmp(val, "file"))
273 					ip->type = F_FILE;
274 				if (!strcmp(val, "fifo"))
275 					ip->type = F_FIFO;
276 				break;
277 			case 'l':
278 				if (!strcmp(val, "link"))
279 					ip->type = F_LINK;
280 				break;
281 			case 's':
282 				if (!strcmp(val, "socket"))
283 					ip->type = F_SOCK;
284 				break;
285 			default:
286 				errx(1, "line %d: unknown file type %s",
287 				lineno, val);
288 			}
289 			break;
290 		case F_UID:
291 			ip->st_uid = strtoul(val, &ep, 10);
292 			if (*ep)
293 				errx(1, "line %d: invalid uid %s", lineno, val);
294 			break;
295 		case F_UNAME:
296 			if ((pw = getpwnam(val)) == NULL)
297 			    errx(1, "line %d: unknown user %s", lineno, val);
298 			ip->st_uid = pw->pw_uid;
299 			break;
300 		}
301 	}
302 }
303 
304 static void
305 unset(char *t, NODE *ip)
306 {
307 	char *p;
308 
309 	while ((p = strtok(t, "\n\t ")))
310 		ip->flags &= ~parsekey(p, NULL);
311 }
312