1 /* $NetBSD: spec.c,v 1.23 2001/07/18 04:51:54 lukem Exp $ */ 2 3 /*- 4 * Copyright (c) 1989, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include <sys/cdefs.h> 37 #ifndef lint 38 #if 0 39 static char sccsid[] = "@(#)spec.c 8.2 (Berkeley) 4/28/95"; 40 #else 41 __RCSID("$NetBSD: spec.c,v 1.23 2001/07/18 04:51:54 lukem Exp $"); 42 #endif 43 #endif /* not lint */ 44 45 #include <sys/types.h> 46 #include <sys/stat.h> 47 48 #include <ctype.h> 49 #include <errno.h> 50 #include <fts.h> 51 #include <grp.h> 52 #include <pwd.h> 53 #include <stdio.h> 54 #include <string.h> 55 #include <unistd.h> 56 #include <vis.h> 57 58 #include "mtree.h" 59 #include "extern.h" 60 61 int lineno; /* Current spec line number. */ 62 63 static void set(char *, NODE *); 64 static void unset(char *, NODE *); 65 66 NODE * 67 spec(void) 68 { 69 NODE *centry, *last; 70 char *p; 71 NODE ginfo, *root; 72 int c_cur, c_next; 73 char buf[2048]; 74 75 root = NULL; 76 centry = last = NULL; 77 memset(&ginfo, 0, sizeof(ginfo)); 78 c_cur = c_next = 0; 79 for (lineno = 1; fgets(buf, sizeof(buf), stdin); 80 ++lineno, c_cur = c_next, c_next = 0) { 81 /* Skip empty lines. */ 82 if (buf[0] == '\n') 83 continue; 84 85 /* Find end of line. */ 86 if ((p = strchr(buf, '\n')) == NULL) 87 mtree_err("line %d too long", lineno); 88 89 /* See if next line is continuation line. */ 90 if (p[-1] == '\\') { 91 --p; 92 c_next = 1; 93 } 94 95 /* Null-terminate the line. */ 96 *p = '\0'; 97 98 /* Skip leading whitespace. */ 99 for (p = buf; *p && isspace((unsigned char)*p); ++p); 100 101 /* If nothing but whitespace or comment char, continue. */ 102 if (!*p || *p == '#') 103 continue; 104 105 #ifdef DEBUG 106 (void)fprintf(stderr, "line %d: {%s}\n", lineno, p); 107 #endif 108 if (c_cur) { 109 set(p, centry); 110 continue; 111 } 112 113 /* Grab file name, "$", "set", or "unset". */ 114 if ((p = strtok(p, "\n\t ")) == NULL) 115 mtree_err("missing field"); 116 117 if (p[0] == '/') { 118 if (strcmp(p + 1, "set") == 0) 119 set(NULL, &ginfo); 120 else if (strcmp(p + 1, "unset") == 0) 121 unset(NULL, &ginfo); 122 else 123 mtree_err("invalid specification `%s'", p); 124 continue; 125 } 126 127 if (strchr(p, '/')) 128 mtree_err("slash character in file name"); 129 130 if (!strcmp(p, "..")) { 131 /* Don't go up, if haven't gone down. */ 132 if (!root) 133 goto noparent; 134 if (last->type != F_DIR || last->flags & F_DONE) { 135 if (last == root) 136 goto noparent; 137 last = last->parent; 138 } 139 last->flags |= F_DONE; 140 continue; 141 142 noparent: mtree_err("no parent node"); 143 } 144 145 if ((centry = calloc(1, sizeof(NODE) + strlen(p))) == NULL) 146 mtree_err("%s", strerror(errno)); 147 *centry = ginfo; 148 if (strunvis(centry->name, p) == -1) 149 mtree_err("strunvis failed on `%s'", p); 150 #define MAGIC "?*[" 151 if (strpbrk(p, MAGIC)) 152 centry->flags |= F_MAGIC; 153 set(NULL, centry); 154 155 if (!root) { 156 last = root = centry; 157 root->parent = root; 158 } else if (last->type == F_DIR && !(last->flags & F_DONE)) { 159 centry->parent = last; 160 last = last->child = centry; 161 } else { 162 centry->parent = last->parent; 163 centry->prev = last; 164 last = last->next = centry; 165 } 166 } 167 return (root); 168 } 169 170 static void 171 set(char *t, NODE *ip) 172 { 173 int type; 174 char *kw, *val, *md; 175 struct group *gr; 176 struct passwd *pw; 177 void *m; 178 int value; 179 char *ep; 180 181 val = NULL; 182 for (; (kw = strtok(t, "= \t\n")) != NULL; t = NULL) { 183 ip->flags |= type = parsekey(kw, &value); 184 if (value && (val = strtok(NULL, " \t\n")) == NULL) 185 mtree_err("missing value"); 186 switch(type) { 187 case F_CKSUM: 188 ip->cksum = strtoul(val, &ep, 10); 189 if (*ep) 190 mtree_err("invalid checksum `%s'", val); 191 break; 192 case F_FLAGS: 193 if (strcmp("none", val) == 0) 194 ip->st_flags = 0; 195 else if (string_to_flags(&val, &ip->st_flags, NULL) != 0) 196 mtree_err("invalid flag `%s'", val); 197 break; 198 case F_GID: 199 ip->st_gid = (gid_t)strtoul(val, &ep, 10); 200 if (*ep) 201 mtree_err("invalid gid `%s'", val); 202 break; 203 case F_GNAME: 204 if ((gr = getgrnam(val)) == NULL) 205 mtree_err("unknown group `%s'", val); 206 ip->st_gid = gr->gr_gid; 207 break; 208 case F_IGN: 209 /* just set flag bit */ 210 break; 211 case F_MD5: 212 if (val[0]=='0' && val[1]=='x') 213 md=&val[2]; 214 else 215 md=val; 216 if ((ip->md5sum = strdup(md)) == NULL) 217 mtree_err("memory allocation error"); 218 break; 219 case F_MODE: 220 if ((m = setmode(val)) == NULL) 221 mtree_err("invalid file mode `%s'", val); 222 ip->st_mode = getmode(m, 0); 223 free(m); 224 break; 225 case F_NLINK: 226 ip->st_nlink = (nlink_t)strtoul(val, &ep, 10); 227 if (*ep) 228 mtree_err("invalid link count `%s'", val); 229 break; 230 case F_SIZE: 231 ip->st_size = (off_t)strtoq(val, &ep, 10); 232 if (*ep) 233 mtree_err("invalid size `%s'", val); 234 break; 235 case F_SLINK: 236 if ((ip->slink = strdup(val)) == NULL) 237 mtree_err("memory allocation error"); 238 break; 239 case F_TIME: 240 ip->st_mtimespec.tv_sec = 241 (time_t)strtoul(val, &ep, 10); 242 if (*ep != '.') 243 mtree_err("invalid time `%s'", val); 244 val = ep + 1; 245 ip->st_mtimespec.tv_nsec = strtol(val, &ep, 10); 246 if (*ep) 247 mtree_err("invalid time `%s'", val); 248 break; 249 case F_TYPE: 250 ip->type = parsetype(val); 251 break; 252 case F_UID: 253 ip->st_uid = (uid_t)strtoul(val, &ep, 10); 254 if (*ep) 255 mtree_err("invalid uid `%s'", val); 256 break; 257 case F_UNAME: 258 if ((pw = getpwnam(val)) == NULL) 259 mtree_err("unknown user `%s'", val); 260 ip->st_uid = pw->pw_uid; 261 break; 262 } 263 } 264 } 265 266 static void 267 unset(char *t, NODE *ip) 268 { 269 char *p; 270 271 while ((p = strtok(t, "\n\t ")) != NULL) 272 ip->flags &= ~parsekey(p, NULL); 273 } 274