spec.c revision 54375
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.
131553Srgrimes * 3. All advertising materials mentioning features or use of this software
141553Srgrimes *    must display the following acknowledgement:
151553Srgrimes *	This product includes software developed by the University of
161553Srgrimes *	California, Berkeley and its contributors.
171553Srgrimes * 4. Neither the name of the University nor the names of its contributors
181553Srgrimes *    may be used to endorse or promote products derived from this software
191553Srgrimes *    without specific prior written permission.
201553Srgrimes *
211553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221553Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231553Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241553Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251553Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271553Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281553Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291553Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301553Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311553Srgrimes * SUCH DAMAGE.
321553Srgrimes */
331553Srgrimes
341553Srgrimes#ifndef lint
3530027Scharnier#if 0
361553Srgrimesstatic char sccsid[] = "@(#)spec.c	8.1 (Berkeley) 6/6/93";
3730027Scharnier#endif
3830027Scharnierstatic const char rcsid[] =
3950479Speter  "$FreeBSD: head/usr.sbin/mtree/spec.c 54375 1999-12-09 20:38:36Z joe $";
401553Srgrimes#endif /* not lint */
411553Srgrimes
421553Srgrimes#include <sys/types.h>
431553Srgrimes#include <sys/stat.h>
4430027Scharnier#include <ctype.h>
4530027Scharnier#include <err.h>
4630027Scharnier#include <errno.h>
471553Srgrimes#include <fts.h>
4830027Scharnier#include <grp.h>
491553Srgrimes#include <pwd.h>
5030027Scharnier#include <stdio.h>
511553Srgrimes#include <unistd.h>
5242561Sjkoshy#include <vis.h>
531553Srgrimes#include "mtree.h"
541553Srgrimes#include "extern.h"
551553Srgrimes
561553Srgrimesint lineno;				/* Current spec line number. */
571553Srgrimes
581553Srgrimesstatic void	 set __P((char *, NODE *));
591553Srgrimesstatic void	 unset __P((char *, NODE *));
601553Srgrimes
611553SrgrimesNODE *
621553Srgrimesspec()
631553Srgrimes{
641553Srgrimes	register NODE *centry, *last;
651553Srgrimes	register char *p;
661553Srgrimes	NODE ginfo, *root;
671553Srgrimes	int c_cur, c_next;
681553Srgrimes	char buf[2048];
691553Srgrimes
702860Srgrimes	centry = last = root = NULL;
711553Srgrimes	bzero(&ginfo, sizeof(ginfo));
721553Srgrimes	c_cur = c_next = 0;
731553Srgrimes	for (lineno = 1; fgets(buf, sizeof(buf), stdin);
741553Srgrimes	    ++lineno, c_cur = c_next, c_next = 0) {
751553Srgrimes		/* Skip empty lines. */
761553Srgrimes		if (buf[0] == '\n')
771553Srgrimes			continue;
781553Srgrimes
791553Srgrimes		/* Find end of line. */
801553Srgrimes		if ((p = index(buf, '\n')) == NULL)
8130027Scharnier			errx(1, "line %d too long", lineno);
821553Srgrimes
831553Srgrimes		/* See if next line is continuation line. */
841553Srgrimes		if (p[-1] == '\\') {
851553Srgrimes			--p;
861553Srgrimes			c_next = 1;
871553Srgrimes		}
881553Srgrimes
891553Srgrimes		/* Null-terminate the line. */
901553Srgrimes		*p = '\0';
911553Srgrimes
921553Srgrimes		/* Skip leading whitespace. */
931553Srgrimes		for (p = buf; *p && isspace(*p); ++p);
941553Srgrimes
951553Srgrimes		/* If nothing but whitespace or comment char, continue. */
961553Srgrimes		if (!*p || *p == '#')
971553Srgrimes			continue;
981553Srgrimes
991553Srgrimes#ifdef DEBUG
1001553Srgrimes		(void)fprintf(stderr, "line %d: {%s}\n", lineno, p);
1011553Srgrimes#endif
1021553Srgrimes		if (c_cur) {
1031553Srgrimes			set(p, centry);
1041553Srgrimes			continue;
1051553Srgrimes		}
1068857Srgrimes
1071553Srgrimes		/* Grab file name, "$", "set", or "unset". */
1081553Srgrimes		if ((p = strtok(p, "\n\t ")) == NULL)
10930027Scharnier			errx(1, "line %d: missing field", lineno);
1101553Srgrimes
1111553Srgrimes		if (p[0] == '/')
1121553Srgrimes			switch(p[1]) {
1131553Srgrimes			case 's':
1141553Srgrimes				if (strcmp(p + 1, "set"))
1151553Srgrimes					break;
1161553Srgrimes				set(NULL, &ginfo);
1171553Srgrimes				continue;
1181553Srgrimes			case 'u':
1191553Srgrimes				if (strcmp(p + 1, "unset"))
1201553Srgrimes					break;
1211553Srgrimes				unset(NULL, &ginfo);
1221553Srgrimes				continue;
1231553Srgrimes			}
1241553Srgrimes
1251553Srgrimes		if (index(p, '/'))
12630027Scharnier			errx(1, "line %d: slash character in file name",
12730027Scharnier			lineno);
1281553Srgrimes
1291553Srgrimes		if (!strcmp(p, "..")) {
1301553Srgrimes			/* Don't go up, if haven't gone down. */
1311553Srgrimes			if (!root)
1321553Srgrimes				goto noparent;
1331553Srgrimes			if (last->type != F_DIR || last->flags & F_DONE) {
1341553Srgrimes				if (last == root)
1351553Srgrimes					goto noparent;
1361553Srgrimes				last = last->parent;
1371553Srgrimes			}
1381553Srgrimes			last->flags |= F_DONE;
1391553Srgrimes			continue;
1401553Srgrimes
14130027Scharniernoparent:		errx(1, "line %d: no parent node", lineno);
1421553Srgrimes		}
1431553Srgrimes
1441553Srgrimes		if ((centry = calloc(1, sizeof(NODE) + strlen(p))) == NULL)
14530027Scharnier			errx(1, "calloc");
1461553Srgrimes		*centry = ginfo;
1471553Srgrimes#define	MAGIC	"?*["
1481553Srgrimes		if (strpbrk(p, MAGIC))
1491553Srgrimes			centry->flags |= F_MAGIC;
15042561Sjkoshy		if (strunvis(centry->name, p) == -1) {
15142561Sjkoshy			warnx("filename %s is ill-encoded and literally used",
15242561Sjkoshy			    p);
15342561Sjkoshy			strcpy(centry->name, p);
15442561Sjkoshy		}
1551553Srgrimes		set(NULL, centry);
1561553Srgrimes
1571553Srgrimes		if (!root) {
1581553Srgrimes			last = root = centry;
1591553Srgrimes			root->parent = root;
1601553Srgrimes		} else if (last->type == F_DIR && !(last->flags & F_DONE)) {
1611553Srgrimes			centry->parent = last;
1621553Srgrimes			last = last->child = centry;
1631553Srgrimes		} else {
1641553Srgrimes			centry->parent = last->parent;
1651553Srgrimes			centry->prev = last;
1661553Srgrimes			last = last->next = centry;
1671553Srgrimes		}
1681553Srgrimes	}
1691553Srgrimes	return (root);
1701553Srgrimes}
1711553Srgrimes
1721553Srgrimesstatic void
1731553Srgrimesset(t, ip)
1741553Srgrimes	char *t;
17554375Sjoe	NODE *ip;
1761553Srgrimes{
1771553Srgrimes	register int type;
17854375Sjoe	char *kw, *val = NULL;
1791553Srgrimes	struct group *gr;
1801553Srgrimes	struct passwd *pw;
1811553Srgrimes	mode_t *m;
1821553Srgrimes	int value;
1831553Srgrimes	char *ep;
1841553Srgrimes
1852860Srgrimes	for (; (kw = strtok(t, "= \t\n")); t = NULL) {
1861553Srgrimes		ip->flags |= type = parsekey(kw, &value);
1871553Srgrimes		if (value && (val = strtok(NULL, " \t\n")) == NULL)
18830027Scharnier			errx(1, "line %d: missing value", lineno);
1891553Srgrimes		switch(type) {
1901553Srgrimes		case F_CKSUM:
1911553Srgrimes			ip->cksum = strtoul(val, &ep, 10);
1921553Srgrimes			if (*ep)
19330027Scharnier				errx(1, "line %d: invalid checksum %s",
19430027Scharnier				lineno, val);
1951553Srgrimes			break;
1966286Swollman		case F_MD5:
1976286Swollman			ip->md5digest = strdup(val);
1986286Swollman			if(!ip->md5digest) {
19930027Scharnier				errx(1, "strdup");
2006286Swollman			}
2016286Swollman			break;
20244303Swollman		case F_SHA1:
20344303Swollman			ip->sha1digest = strdup(val);
20444303Swollman			if(!ip->sha1digest) {
20544303Swollman				errx(1, "strdup");
20644303Swollman			}
20744303Swollman			break;
20844303Swollman		case F_RMD160:
20944303Swollman			ip->rmd160digest = strdup(val);
21044303Swollman			if(!ip->rmd160digest) {
21144303Swollman				errx(1, "strdup");
21244303Swollman			}
21344303Swollman			break;
21454375Sjoe		case F_FLAGS:
21554375Sjoe			if (strcmp("none", val) == 0)
21654375Sjoe				ip->st_flags = 0;
21754375Sjoe			else if (string_to_flags(&val, &ip->st_flags,NULL) != 0)
21854375Sjoe				errx(1, "line %d: invalid flag %s",lineno, val);
21954375Sjoe 			break;
2201553Srgrimes		case F_GID:
2211553Srgrimes			ip->st_gid = strtoul(val, &ep, 10);
2221553Srgrimes			if (*ep)
22330027Scharnier				errx(1, "line %d: invalid gid %s", lineno, val);
2241553Srgrimes			break;
2251553Srgrimes		case F_GNAME:
2261553Srgrimes			if ((gr = getgrnam(val)) == NULL)
22730027Scharnier			    errx(1, "line %d: unknown group %s", lineno, val);
2281553Srgrimes			ip->st_gid = gr->gr_gid;
2291553Srgrimes			break;
2301553Srgrimes		case F_IGN:
2311553Srgrimes			/* just set flag bit */
2321553Srgrimes			break;
2331553Srgrimes		case F_MODE:
2341553Srgrimes			if ((m = setmode(val)) == NULL)
23530027Scharnier				errx(1, "line %d: invalid file mode %s",
23630027Scharnier				lineno, val);
2371553Srgrimes			ip->st_mode = getmode(m, 0);
23841848Simp			free(m);
2391553Srgrimes			break;
2401553Srgrimes		case F_NLINK:
2411553Srgrimes			ip->st_nlink = strtoul(val, &ep, 10);
2421553Srgrimes			if (*ep)
24330027Scharnier				errx(1, "line %d: invalid link count %s",
24430027Scharnier				lineno,  val);
2451553Srgrimes			break;
2461553Srgrimes		case F_SIZE:
24711282Storstenb			ip->st_size = strtoq(val, &ep, 10);
2481553Srgrimes			if (*ep)
24930027Scharnier				errx(1, "line %d: invalid size %s",
25030027Scharnier				lineno, val);
2511553Srgrimes			break;
2521553Srgrimes		case F_SLINK:
2531553Srgrimes			if ((ip->slink = strdup(val)) == NULL)
25430027Scharnier				errx(1, "strdup");
2551553Srgrimes			break;
2561553Srgrimes		case F_TIME:
25718404Snate			ip->st_mtimespec.tv_sec = strtoul(val, &ep, 10);
2581553Srgrimes			if (*ep != '.')
25930027Scharnier				errx(1, "line %d: invalid time %s",
26030027Scharnier				lineno, val);
2611553Srgrimes			val = ep + 1;
26218404Snate			ip->st_mtimespec.tv_nsec = strtoul(val, &ep, 10);
2631553Srgrimes			if (*ep)
26430027Scharnier				errx(1, "line %d: invalid time %s",
26530027Scharnier				lineno, val);
2661553Srgrimes			break;
2671553Srgrimes		case F_TYPE:
2681553Srgrimes			switch(*val) {
2691553Srgrimes			case 'b':
2701553Srgrimes				if (!strcmp(val, "block"))
2711553Srgrimes					ip->type = F_BLOCK;
2721553Srgrimes				break;
2731553Srgrimes			case 'c':
2741553Srgrimes				if (!strcmp(val, "char"))
2751553Srgrimes					ip->type = F_CHAR;
2761553Srgrimes				break;
2771553Srgrimes			case 'd':
2781553Srgrimes				if (!strcmp(val, "dir"))
2791553Srgrimes					ip->type = F_DIR;
2801553Srgrimes				break;
2811553Srgrimes			case 'f':
2821553Srgrimes				if (!strcmp(val, "file"))
2831553Srgrimes					ip->type = F_FILE;
2841553Srgrimes				if (!strcmp(val, "fifo"))
2851553Srgrimes					ip->type = F_FIFO;
2861553Srgrimes				break;
2871553Srgrimes			case 'l':
2881553Srgrimes				if (!strcmp(val, "link"))
2891553Srgrimes					ip->type = F_LINK;
2901553Srgrimes				break;
2911553Srgrimes			case 's':
2921553Srgrimes				if (!strcmp(val, "socket"))
2931553Srgrimes					ip->type = F_SOCK;
2941553Srgrimes				break;
2951553Srgrimes			default:
29630027Scharnier				errx(1, "line %d: unknown file type %s",
29730027Scharnier				lineno, val);
2981553Srgrimes			}
2991553Srgrimes			break;
3001553Srgrimes		case F_UID:
3011553Srgrimes			ip->st_uid = strtoul(val, &ep, 10);
3021553Srgrimes			if (*ep)
30330027Scharnier				errx(1, "line %d: invalid uid %s", lineno, val);
3041553Srgrimes			break;
3051553Srgrimes		case F_UNAME:
3061553Srgrimes			if ((pw = getpwnam(val)) == NULL)
30730027Scharnier			    errx(1, "line %d: unknown user %s", lineno, val);
3081553Srgrimes			ip->st_uid = pw->pw_uid;
3091553Srgrimes			break;
3101553Srgrimes		}
3111553Srgrimes	}
3121553Srgrimes}
3131553Srgrimes
3141553Srgrimesstatic void
3151553Srgrimesunset(t, ip)
3161553Srgrimes	char *t;
3171553Srgrimes	register NODE *ip;
3181553Srgrimes{
3191553Srgrimes	register char *p;
3201553Srgrimes
3212860Srgrimes	while ((p = strtok(t, "\n\t ")))
3221553Srgrimes		ip->flags &= ~parsekey(p, NULL);
3231553Srgrimes}
324