11553Srgrimes/*-
21553Srgrimes * Copyright (c) 1989, 1993
31553Srgrimes *	The Regents of the University of California.  All rights reserved.
41553Srgrimes *
51553Srgrimes * Redistribution and use in source and binary forms, with or without
61553Srgrimes * modification, are permitted provided that the following conditions
71553Srgrimes * are met:
81553Srgrimes * 1. Redistributions of source code must retain the above copyright
91553Srgrimes *    notice, this list of conditions and the following disclaimer.
101553Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111553Srgrimes *    notice, this list of conditions and the following disclaimer in the
121553Srgrimes *    documentation and/or other materials provided with the distribution.
13121300Sphk * 3. Neither the name of the University nor the names of its contributors
141553Srgrimes *    may be used to endorse or promote products derived from this software
151553Srgrimes *    without specific prior written permission.
161553Srgrimes *
171553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181553Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191553Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201553Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211553Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231553Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241553Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251553Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261553Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271553Srgrimes * SUCH DAMAGE.
281553Srgrimes */
291553Srgrimes
30114601Sobrien#if 0
311553Srgrimes#ifndef lint
321553Srgrimesstatic char sccsid[] = "@(#)spec.c	8.1 (Berkeley) 6/6/93";
33114601Sobrien#endif /* not lint */
3430027Scharnier#endif
35114601Sobrien#include <sys/cdefs.h>
36114601Sobrien__FBSDID("$FreeBSD$");
371553Srgrimes
381553Srgrimes#include <sys/types.h>
391553Srgrimes#include <sys/stat.h>
4030027Scharnier#include <ctype.h>
4130027Scharnier#include <err.h>
4230027Scharnier#include <errno.h>
431553Srgrimes#include <fts.h>
4430027Scharnier#include <grp.h>
451553Srgrimes#include <pwd.h>
4630027Scharnier#include <stdio.h>
471553Srgrimes#include <unistd.h>
4842561Sjkoshy#include <vis.h>
491553Srgrimes#include "mtree.h"
501553Srgrimes#include "extern.h"
511553Srgrimes
521553Srgrimesint lineno;				/* Current spec line number. */
531553Srgrimes
5499800Salfredstatic void	 set(char *, NODE *);
5599800Salfredstatic void	 unset(char *, NODE *);
561553Srgrimes
571553SrgrimesNODE *
58122135Sphkmtree_readspec(FILE *fi)
591553Srgrimes{
60121299Sphk	NODE *centry, *last;
61121299Sphk	char *p;
621553Srgrimes	NODE ginfo, *root;
631553Srgrimes	int c_cur, c_next;
641553Srgrimes	char buf[2048];
651553Srgrimes
662860Srgrimes	centry = last = root = NULL;
671553Srgrimes	bzero(&ginfo, sizeof(ginfo));
681553Srgrimes	c_cur = c_next = 0;
69122135Sphk	for (lineno = 1; fgets(buf, sizeof(buf), fi);
701553Srgrimes	    ++lineno, c_cur = c_next, c_next = 0) {
711553Srgrimes		/* Skip empty lines. */
721553Srgrimes		if (buf[0] == '\n')
731553Srgrimes			continue;
741553Srgrimes
751553Srgrimes		/* Find end of line. */
76229403Sed		if ((p = strchr(buf, '\n')) == NULL)
7730027Scharnier			errx(1, "line %d too long", lineno);
781553Srgrimes
791553Srgrimes		/* See if next line is continuation line. */
801553Srgrimes		if (p[-1] == '\\') {
811553Srgrimes			--p;
821553Srgrimes			c_next = 1;
831553Srgrimes		}
841553Srgrimes
851553Srgrimes		/* Null-terminate the line. */
861553Srgrimes		*p = '\0';
871553Srgrimes
881553Srgrimes		/* Skip leading whitespace. */
891553Srgrimes		for (p = buf; *p && isspace(*p); ++p);
901553Srgrimes
911553Srgrimes		/* If nothing but whitespace or comment char, continue. */
921553Srgrimes		if (!*p || *p == '#')
931553Srgrimes			continue;
941553Srgrimes
951553Srgrimes#ifdef DEBUG
961553Srgrimes		(void)fprintf(stderr, "line %d: {%s}\n", lineno, p);
971553Srgrimes#endif
981553Srgrimes		if (c_cur) {
991553Srgrimes			set(p, centry);
1001553Srgrimes			continue;
1011553Srgrimes		}
1028857Srgrimes
1031553Srgrimes		/* Grab file name, "$", "set", or "unset". */
1041553Srgrimes		if ((p = strtok(p, "\n\t ")) == NULL)
10530027Scharnier			errx(1, "line %d: missing field", lineno);
1061553Srgrimes
1071553Srgrimes		if (p[0] == '/')
1081553Srgrimes			switch(p[1]) {
1091553Srgrimes			case 's':
1101553Srgrimes				if (strcmp(p + 1, "set"))
1111553Srgrimes					break;
1121553Srgrimes				set(NULL, &ginfo);
1131553Srgrimes				continue;
1141553Srgrimes			case 'u':
1151553Srgrimes				if (strcmp(p + 1, "unset"))
1161553Srgrimes					break;
1171553Srgrimes				unset(NULL, &ginfo);
1181553Srgrimes				continue;
1191553Srgrimes			}
1201553Srgrimes
121229403Sed		if (strchr(p, '/'))
12230027Scharnier			errx(1, "line %d: slash character in file name",
12330027Scharnier			lineno);
1241553Srgrimes
1251553Srgrimes		if (!strcmp(p, "..")) {
1261553Srgrimes			/* Don't go up, if haven't gone down. */
1271553Srgrimes			if (!root)
1281553Srgrimes				goto noparent;
1291553Srgrimes			if (last->type != F_DIR || last->flags & F_DONE) {
1301553Srgrimes				if (last == root)
1311553Srgrimes					goto noparent;
1321553Srgrimes				last = last->parent;
1331553Srgrimes			}
1341553Srgrimes			last->flags |= F_DONE;
1351553Srgrimes			continue;
1361553Srgrimes
13730027Scharniernoparent:		errx(1, "line %d: no parent node", lineno);
1381553Srgrimes		}
1391553Srgrimes
1401553Srgrimes		if ((centry = calloc(1, sizeof(NODE) + strlen(p))) == NULL)
14130027Scharnier			errx(1, "calloc");
1421553Srgrimes		*centry = ginfo;
1431553Srgrimes#define	MAGIC	"?*["
1441553Srgrimes		if (strpbrk(p, MAGIC))
1451553Srgrimes			centry->flags |= F_MAGIC;
146121734Sphk		if (strunvis(centry->name, p) == -1)
147121734Sphk			errx(1, "filename %s is ill-encoded", p);
1481553Srgrimes		set(NULL, centry);
1491553Srgrimes
1501553Srgrimes		if (!root) {
1511553Srgrimes			last = root = centry;
1521553Srgrimes			root->parent = root;
1531553Srgrimes		} else if (last->type == F_DIR && !(last->flags & F_DONE)) {
1541553Srgrimes			centry->parent = last;
1551553Srgrimes			last = last->child = centry;
1561553Srgrimes		} else {
1571553Srgrimes			centry->parent = last->parent;
1581553Srgrimes			centry->prev = last;
1591553Srgrimes			last = last->next = centry;
1601553Srgrimes		}
1611553Srgrimes	}
1621553Srgrimes	return (root);
1631553Srgrimes}
1641553Srgrimes
1651553Srgrimesstatic void
166121299Sphkset(char *t, NODE *ip)
1671553Srgrimes{
168121299Sphk	int type;
16954375Sjoe	char *kw, *val = NULL;
1701553Srgrimes	struct group *gr;
1711553Srgrimes	struct passwd *pw;
1721553Srgrimes	mode_t *m;
1731553Srgrimes	int value;
1741553Srgrimes	char *ep;
1751553Srgrimes
1762860Srgrimes	for (; (kw = strtok(t, "= \t\n")); t = NULL) {
1771553Srgrimes		ip->flags |= type = parsekey(kw, &value);
1781553Srgrimes		if (value && (val = strtok(NULL, " \t\n")) == NULL)
17930027Scharnier			errx(1, "line %d: missing value", lineno);
1801553Srgrimes		switch(type) {
1811553Srgrimes		case F_CKSUM:
1821553Srgrimes			ip->cksum = strtoul(val, &ep, 10);
1831553Srgrimes			if (*ep)
18430027Scharnier				errx(1, "line %d: invalid checksum %s",
18530027Scharnier				lineno, val);
1861553Srgrimes			break;
1876286Swollman		case F_MD5:
1886286Swollman			ip->md5digest = strdup(val);
189122135Sphk			if(!ip->md5digest)
19030027Scharnier				errx(1, "strdup");
1916286Swollman			break;
19244303Swollman		case F_SHA1:
19344303Swollman			ip->sha1digest = strdup(val);
194122135Sphk			if(!ip->sha1digest)
19544303Swollman				errx(1, "strdup");
19644303Swollman			break;
197144295Stobez		case F_SHA256:
198144295Stobez			ip->sha256digest = strdup(val);
199144295Stobez			if(!ip->sha256digest)
200144295Stobez				errx(1, "strdup");
201144295Stobez			break;
20244303Swollman		case F_RMD160:
20344303Swollman			ip->rmd160digest = strdup(val);
204122135Sphk			if(!ip->rmd160digest)
20544303Swollman				errx(1, "strdup");
20644303Swollman			break;
20754375Sjoe		case F_FLAGS:
20854375Sjoe			if (strcmp("none", val) == 0)
20954375Sjoe				ip->st_flags = 0;
21061749Sjoe			else if (strtofflags(&val, &ip->st_flags, NULL) != 0)
21154375Sjoe				errx(1, "line %d: invalid flag %s",lineno, val);
21254375Sjoe 			break;
2131553Srgrimes		case F_GID:
2141553Srgrimes			ip->st_gid = strtoul(val, &ep, 10);
2151553Srgrimes			if (*ep)
21630027Scharnier				errx(1, "line %d: invalid gid %s", lineno, val);
2171553Srgrimes			break;
2181553Srgrimes		case F_GNAME:
2191553Srgrimes			if ((gr = getgrnam(val)) == NULL)
22030027Scharnier			    errx(1, "line %d: unknown group %s", lineno, val);
2211553Srgrimes			ip->st_gid = gr->gr_gid;
2221553Srgrimes			break;
2231553Srgrimes		case F_IGN:
2241553Srgrimes			/* just set flag bit */
2251553Srgrimes			break;
2261553Srgrimes		case F_MODE:
2271553Srgrimes			if ((m = setmode(val)) == NULL)
22830027Scharnier				errx(1, "line %d: invalid file mode %s",
22930027Scharnier				lineno, val);
2301553Srgrimes			ip->st_mode = getmode(m, 0);
23141848Simp			free(m);
2321553Srgrimes			break;
2331553Srgrimes		case F_NLINK:
2341553Srgrimes			ip->st_nlink = strtoul(val, &ep, 10);
2351553Srgrimes			if (*ep)
23630027Scharnier				errx(1, "line %d: invalid link count %s",
23730027Scharnier				lineno,  val);
2381553Srgrimes			break;
239160083Smaxim		case F_OPT:
240160083Smaxim			/* just set flag bit */
241160083Smaxim			break;
2421553Srgrimes		case F_SIZE:
24311282Storstenb			ip->st_size = strtoq(val, &ep, 10);
2441553Srgrimes			if (*ep)
24530027Scharnier				errx(1, "line %d: invalid size %s",
24630027Scharnier				lineno, val);
2471553Srgrimes			break;
2481553Srgrimes		case F_SLINK:
249122896Sphk			ip->slink = malloc(strlen(val) + 1);
250121734Sphk			if (ip->slink == NULL)
251121734Sphk				errx(1, "malloc");
252121734Sphk			if (strunvis(ip->slink, val) == -1)
253121734Sphk				errx(1, "symlink %s is ill-encoded", val);
2541553Srgrimes			break;
2551553Srgrimes		case F_TIME:
25618404Snate			ip->st_mtimespec.tv_sec = strtoul(val, &ep, 10);
257187910Skientzle			if (*ep == '.') {
258187940Skientzle				/* Note: we require exactly nine
259187940Skientzle				 * digits after the decimal point. */
260187910Skientzle				val = ep + 1;
261187910Skientzle				ip->st_mtimespec.tv_nsec
262187910Skientzle				    = strtoul(val, &ep, 10);
263187910Skientzle			} else
264187910Skientzle				ip->st_mtimespec.tv_nsec = 0;
2651553Srgrimes			if (*ep)
26630027Scharnier				errx(1, "line %d: invalid time %s",
267187910Skientzle				    lineno, val);
2681553Srgrimes			break;
2691553Srgrimes		case F_TYPE:
2701553Srgrimes			switch(*val) {
2711553Srgrimes			case 'b':
2721553Srgrimes				if (!strcmp(val, "block"))
2731553Srgrimes					ip->type = F_BLOCK;
2741553Srgrimes				break;
2751553Srgrimes			case 'c':
2761553Srgrimes				if (!strcmp(val, "char"))
2771553Srgrimes					ip->type = F_CHAR;
2781553Srgrimes				break;
2791553Srgrimes			case 'd':
2801553Srgrimes				if (!strcmp(val, "dir"))
2811553Srgrimes					ip->type = F_DIR;
2821553Srgrimes				break;
2831553Srgrimes			case 'f':
2841553Srgrimes				if (!strcmp(val, "file"))
2851553Srgrimes					ip->type = F_FILE;
2861553Srgrimes				if (!strcmp(val, "fifo"))
2871553Srgrimes					ip->type = F_FIFO;
2881553Srgrimes				break;
2891553Srgrimes			case 'l':
2901553Srgrimes				if (!strcmp(val, "link"))
2911553Srgrimes					ip->type = F_LINK;
2921553Srgrimes				break;
2931553Srgrimes			case 's':
2941553Srgrimes				if (!strcmp(val, "socket"))
2951553Srgrimes					ip->type = F_SOCK;
2961553Srgrimes				break;
2971553Srgrimes			default:
29830027Scharnier				errx(1, "line %d: unknown file type %s",
29930027Scharnier				lineno, val);
3001553Srgrimes			}
3011553Srgrimes			break;
3021553Srgrimes		case F_UID:
3031553Srgrimes			ip->st_uid = strtoul(val, &ep, 10);
3041553Srgrimes			if (*ep)
30530027Scharnier				errx(1, "line %d: invalid uid %s", lineno, val);
3061553Srgrimes			break;
3071553Srgrimes		case F_UNAME:
3081553Srgrimes			if ((pw = getpwnam(val)) == NULL)
30930027Scharnier			    errx(1, "line %d: unknown user %s", lineno, val);
3101553Srgrimes			ip->st_uid = pw->pw_uid;
3111553Srgrimes			break;
3121553Srgrimes		}
3131553Srgrimes	}
3141553Srgrimes}
3151553Srgrimes
3161553Srgrimesstatic void
317121299Sphkunset(char *t, NODE *ip)
3181553Srgrimes{
319121299Sphk	char *p;
3201553Srgrimes
3212860Srgrimes	while ((p = strtok(t, "\n\t ")))
3221553Srgrimes		ip->flags &= ~parsekey(p, NULL);
3231553Srgrimes}
324