spec.c revision 11282
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
351553Srgrimesstatic char sccsid[] = "@(#)spec.c	8.1 (Berkeley) 6/6/93";
361553Srgrimes#endif /* not lint */
371553Srgrimes
381553Srgrimes#include <sys/types.h>
391553Srgrimes#include <sys/stat.h>
401553Srgrimes#include <fts.h>
411553Srgrimes#include <pwd.h>
421553Srgrimes#include <grp.h>
431553Srgrimes#include <errno.h>
441553Srgrimes#include <unistd.h>
451553Srgrimes#include <stdio.h>
461553Srgrimes#include <ctype.h>
471553Srgrimes#include "mtree.h"
481553Srgrimes#include "extern.h"
491553Srgrimes
501553Srgrimesint lineno;				/* Current spec line number. */
511553Srgrimes
521553Srgrimesstatic void	 set __P((char *, NODE *));
531553Srgrimesstatic void	 unset __P((char *, NODE *));
541553Srgrimes
551553SrgrimesNODE *
561553Srgrimesspec()
571553Srgrimes{
581553Srgrimes	register NODE *centry, *last;
591553Srgrimes	register char *p;
601553Srgrimes	NODE ginfo, *root;
611553Srgrimes	int c_cur, c_next;
621553Srgrimes	char buf[2048];
631553Srgrimes
642860Srgrimes	centry = last = root = NULL;
651553Srgrimes	bzero(&ginfo, sizeof(ginfo));
661553Srgrimes	c_cur = c_next = 0;
671553Srgrimes	for (lineno = 1; fgets(buf, sizeof(buf), stdin);
681553Srgrimes	    ++lineno, c_cur = c_next, c_next = 0) {
691553Srgrimes		/* Skip empty lines. */
701553Srgrimes		if (buf[0] == '\n')
711553Srgrimes			continue;
721553Srgrimes
731553Srgrimes		/* Find end of line. */
741553Srgrimes		if ((p = index(buf, '\n')) == NULL)
751553Srgrimes			err("line %d too long", lineno);
761553Srgrimes
771553Srgrimes		/* See if next line is continuation line. */
781553Srgrimes		if (p[-1] == '\\') {
791553Srgrimes			--p;
801553Srgrimes			c_next = 1;
811553Srgrimes		}
821553Srgrimes
831553Srgrimes		/* Null-terminate the line. */
841553Srgrimes		*p = '\0';
851553Srgrimes
861553Srgrimes		/* Skip leading whitespace. */
871553Srgrimes		for (p = buf; *p && isspace(*p); ++p);
881553Srgrimes
891553Srgrimes		/* If nothing but whitespace or comment char, continue. */
901553Srgrimes		if (!*p || *p == '#')
911553Srgrimes			continue;
921553Srgrimes
931553Srgrimes#ifdef DEBUG
941553Srgrimes		(void)fprintf(stderr, "line %d: {%s}\n", lineno, p);
951553Srgrimes#endif
961553Srgrimes		if (c_cur) {
971553Srgrimes			set(p, centry);
981553Srgrimes			continue;
991553Srgrimes		}
1008857Srgrimes
1011553Srgrimes		/* Grab file name, "$", "set", or "unset". */
1021553Srgrimes		if ((p = strtok(p, "\n\t ")) == NULL)
1031553Srgrimes			err("missing field");
1041553Srgrimes
1051553Srgrimes		if (p[0] == '/')
1061553Srgrimes			switch(p[1]) {
1071553Srgrimes			case 's':
1081553Srgrimes				if (strcmp(p + 1, "set"))
1091553Srgrimes					break;
1101553Srgrimes				set(NULL, &ginfo);
1111553Srgrimes				continue;
1121553Srgrimes			case 'u':
1131553Srgrimes				if (strcmp(p + 1, "unset"))
1141553Srgrimes					break;
1151553Srgrimes				unset(NULL, &ginfo);
1161553Srgrimes				continue;
1171553Srgrimes			}
1181553Srgrimes
1191553Srgrimes		if (index(p, '/'))
1201553Srgrimes			err("slash character in file name");
1211553Srgrimes
1221553Srgrimes		if (!strcmp(p, "..")) {
1231553Srgrimes			/* Don't go up, if haven't gone down. */
1241553Srgrimes			if (!root)
1251553Srgrimes				goto noparent;
1261553Srgrimes			if (last->type != F_DIR || last->flags & F_DONE) {
1271553Srgrimes				if (last == root)
1281553Srgrimes					goto noparent;
1291553Srgrimes				last = last->parent;
1301553Srgrimes			}
1311553Srgrimes			last->flags |= F_DONE;
1321553Srgrimes			continue;
1331553Srgrimes
1341553Srgrimesnoparent:		err("no parent node");
1351553Srgrimes		}
1361553Srgrimes
1371553Srgrimes		if ((centry = calloc(1, sizeof(NODE) + strlen(p))) == NULL)
1381553Srgrimes			err("%s", strerror(errno));
1391553Srgrimes		*centry = ginfo;
1401553Srgrimes		(void)strcpy(centry->name, p);
1411553Srgrimes#define	MAGIC	"?*["
1421553Srgrimes		if (strpbrk(p, MAGIC))
1431553Srgrimes			centry->flags |= F_MAGIC;
1441553Srgrimes		set(NULL, centry);
1451553Srgrimes
1461553Srgrimes		if (!root) {
1471553Srgrimes			last = root = centry;
1481553Srgrimes			root->parent = root;
1491553Srgrimes		} else if (last->type == F_DIR && !(last->flags & F_DONE)) {
1501553Srgrimes			centry->parent = last;
1511553Srgrimes			last = last->child = centry;
1521553Srgrimes		} else {
1531553Srgrimes			centry->parent = last->parent;
1541553Srgrimes			centry->prev = last;
1551553Srgrimes			last = last->next = centry;
1561553Srgrimes		}
1571553Srgrimes	}
1581553Srgrimes	return (root);
1591553Srgrimes}
1601553Srgrimes
1611553Srgrimesstatic void
1621553Srgrimesset(t, ip)
1631553Srgrimes	char *t;
1641553Srgrimes	register NODE *ip;
1651553Srgrimes{
1661553Srgrimes	register int type;
1672860Srgrimes	register char *kw, *val = NULL;
1681553Srgrimes	struct group *gr;
1691553Srgrimes	struct passwd *pw;
1701553Srgrimes	mode_t *m;
1711553Srgrimes	int value;
1721553Srgrimes	char *ep;
1731553Srgrimes
1742860Srgrimes	for (; (kw = strtok(t, "= \t\n")); t = NULL) {
1751553Srgrimes		ip->flags |= type = parsekey(kw, &value);
1761553Srgrimes		if (value && (val = strtok(NULL, " \t\n")) == NULL)
1771553Srgrimes			err("missing value");
1781553Srgrimes		switch(type) {
1791553Srgrimes		case F_CKSUM:
1801553Srgrimes			ip->cksum = strtoul(val, &ep, 10);
1811553Srgrimes			if (*ep)
1821553Srgrimes				err("invalid checksum %s", val);
1831553Srgrimes			break;
1846286Swollman		case F_MD5:
1856286Swollman			ip->md5digest = strdup(val);
1866286Swollman			if(!ip->md5digest) {
1876286Swollman				err("%s", strerror(errno));
1886286Swollman			}
1896286Swollman			break;
1901553Srgrimes		case F_GID:
1911553Srgrimes			ip->st_gid = strtoul(val, &ep, 10);
1921553Srgrimes			if (*ep)
1931553Srgrimes				err("invalid gid %s", val);
1941553Srgrimes			break;
1951553Srgrimes		case F_GNAME:
1961553Srgrimes			if ((gr = getgrnam(val)) == NULL)
1971553Srgrimes			    err("unknown group %s", val);
1981553Srgrimes			ip->st_gid = gr->gr_gid;
1991553Srgrimes			break;
2001553Srgrimes		case F_IGN:
2011553Srgrimes			/* just set flag bit */
2021553Srgrimes			break;
2031553Srgrimes		case F_MODE:
2041553Srgrimes			if ((m = setmode(val)) == NULL)
2051553Srgrimes				err("invalid file mode %s", val);
2061553Srgrimes			ip->st_mode = getmode(m, 0);
2071553Srgrimes			break;
2081553Srgrimes		case F_NLINK:
2091553Srgrimes			ip->st_nlink = strtoul(val, &ep, 10);
2101553Srgrimes			if (*ep)
2111553Srgrimes				err("invalid link count %s", val);
2121553Srgrimes			break;
2131553Srgrimes		case F_SIZE:
21411282Storstenb			ip->st_size = strtoq(val, &ep, 10);
2151553Srgrimes			if (*ep)
2161553Srgrimes				err("invalid size %s", val);
2171553Srgrimes			break;
2181553Srgrimes		case F_SLINK:
2191553Srgrimes			if ((ip->slink = strdup(val)) == NULL)
2201553Srgrimes				err("%s", strerror(errno));
2211553Srgrimes			break;
2221553Srgrimes		case F_TIME:
2231553Srgrimes			ip->st_mtimespec.ts_sec = strtoul(val, &ep, 10);
2241553Srgrimes			if (*ep != '.')
2251553Srgrimes				err("invalid time %s", val);
2261553Srgrimes			val = ep + 1;
2271553Srgrimes			ip->st_mtimespec.ts_nsec = strtoul(val, &ep, 10);
2281553Srgrimes			if (*ep)
2291553Srgrimes				err("invalid time %s", val);
2301553Srgrimes			break;
2311553Srgrimes		case F_TYPE:
2321553Srgrimes			switch(*val) {
2331553Srgrimes			case 'b':
2341553Srgrimes				if (!strcmp(val, "block"))
2351553Srgrimes					ip->type = F_BLOCK;
2361553Srgrimes				break;
2371553Srgrimes			case 'c':
2381553Srgrimes				if (!strcmp(val, "char"))
2391553Srgrimes					ip->type = F_CHAR;
2401553Srgrimes				break;
2411553Srgrimes			case 'd':
2421553Srgrimes				if (!strcmp(val, "dir"))
2431553Srgrimes					ip->type = F_DIR;
2441553Srgrimes				break;
2451553Srgrimes			case 'f':
2461553Srgrimes				if (!strcmp(val, "file"))
2471553Srgrimes					ip->type = F_FILE;
2481553Srgrimes				if (!strcmp(val, "fifo"))
2491553Srgrimes					ip->type = F_FIFO;
2501553Srgrimes				break;
2511553Srgrimes			case 'l':
2521553Srgrimes				if (!strcmp(val, "link"))
2531553Srgrimes					ip->type = F_LINK;
2541553Srgrimes				break;
2551553Srgrimes			case 's':
2561553Srgrimes				if (!strcmp(val, "socket"))
2571553Srgrimes					ip->type = F_SOCK;
2581553Srgrimes				break;
2591553Srgrimes			default:
2601553Srgrimes				err("unknown file type %s", val);
2611553Srgrimes			}
2621553Srgrimes			break;
2631553Srgrimes		case F_UID:
2641553Srgrimes			ip->st_uid = strtoul(val, &ep, 10);
2651553Srgrimes			if (*ep)
2661553Srgrimes				err("invalid uid %s", val);
2671553Srgrimes			break;
2681553Srgrimes		case F_UNAME:
2691553Srgrimes			if ((pw = getpwnam(val)) == NULL)
2701553Srgrimes			    err("unknown user %s", val);
2711553Srgrimes			ip->st_uid = pw->pw_uid;
2721553Srgrimes			break;
2731553Srgrimes		}
2741553Srgrimes	}
2751553Srgrimes}
2761553Srgrimes
2771553Srgrimesstatic void
2781553Srgrimesunset(t, ip)
2791553Srgrimes	char *t;
2801553Srgrimes	register NODE *ip;
2811553Srgrimes{
2821553Srgrimes	register char *p;
2831553Srgrimes
2842860Srgrimes	while ((p = strtok(t, "\n\t ")))
2851553Srgrimes		ip->flags &= ~parsekey(p, NULL);
2861553Srgrimes}
287