1 /* $NetBSD: spec.c,v 1.21 2001/03/09 03:09:46 simonb 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.21 2001/03/09 03:09:46 simonb 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 switch(p[1]) { 119 case 's': 120 if (strcmp(p + 1, "set")) 121 break; 122 set(NULL, &ginfo); 123 continue; 124 case 'u': 125 if (strcmp(p + 1, "unset")) 126 break; 127 unset(NULL, &ginfo); 128 continue; 129 } 130 131 if (strchr(p, '/')) 132 mtree_err("slash character in file name"); 133 134 if (!strcmp(p, "..")) { 135 /* Don't go up, if haven't gone down. */ 136 if (!root) 137 goto noparent; 138 if (last->type != F_DIR || last->flags & F_DONE) { 139 if (last == root) 140 goto noparent; 141 last = last->parent; 142 } 143 last->flags |= F_DONE; 144 continue; 145 146 noparent: mtree_err("no parent node"); 147 } 148 149 if ((centry = calloc(1, sizeof(NODE) + strlen(p))) == NULL) 150 mtree_err("%s", strerror(errno)); 151 *centry = ginfo; 152 if (strunvis(centry->name, p) == -1) 153 mtree_err("strunvis failed on %s", p); 154 #define MAGIC "?*[" 155 if (strpbrk(p, MAGIC)) 156 centry->flags |= F_MAGIC; 157 set(NULL, centry); 158 159 if (!root) { 160 last = root = centry; 161 root->parent = root; 162 } else if (last->type == F_DIR && !(last->flags & F_DONE)) { 163 centry->parent = last; 164 last = last->child = centry; 165 } else { 166 centry->parent = last->parent; 167 centry->prev = last; 168 last = last->next = centry; 169 } 170 } 171 return (root); 172 } 173 174 static void 175 set(char *t, NODE *ip) 176 { 177 int type; 178 char *kw, *val, *md; 179 struct group *gr; 180 struct passwd *pw; 181 void *m; 182 int value; 183 char *ep; 184 185 val = NULL; 186 for (; (kw = strtok(t, "= \t\n")) != NULL; t = NULL) { 187 ip->flags |= type = parsekey(kw, &value); 188 if (value && (val = strtok(NULL, " \t\n")) == NULL) 189 mtree_err("missing value"); 190 switch(type) { 191 case F_CKSUM: 192 ip->cksum = strtoul(val, &ep, 10); 193 if (*ep) 194 mtree_err("invalid checksum %s", val); 195 break; 196 case F_FLAGS: 197 if (strcmp("none", val) == 0) 198 ip->st_flags = 0; 199 else if (string_to_flags(&val, &ip->st_flags, NULL) != 0) 200 mtree_err("invalid flag %s", val); 201 break; 202 case F_GID: 203 ip->st_gid = (gid_t)strtoul(val, &ep, 10); 204 if (*ep) 205 mtree_err("invalid gid %s", val); 206 break; 207 case F_GNAME: 208 if ((gr = getgrnam(val)) == NULL) 209 mtree_err("unknown group %s", val); 210 ip->st_gid = gr->gr_gid; 211 break; 212 case F_IGN: 213 /* just set flag bit */ 214 break; 215 case F_MD5: 216 if (val[0]=='0' && val[1]=='x') 217 md=&val[2]; 218 else 219 md=val; 220 if ((ip->md5sum = strdup(md)) == NULL) 221 mtree_err("memory allocation error"); 222 break; 223 case F_MODE: 224 if ((m = setmode(val)) == NULL) 225 mtree_err("invalid file mode %s", val); 226 ip->st_mode = getmode(m, 0); 227 free(m); 228 break; 229 case F_NLINK: 230 ip->st_nlink = (nlink_t)strtoul(val, &ep, 10); 231 if (*ep) 232 mtree_err("invalid link count %s", val); 233 break; 234 case F_SIZE: 235 ip->st_size = (off_t)strtoq(val, &ep, 10); 236 if (*ep) 237 mtree_err("invalid size %s", val); 238 break; 239 case F_SLINK: 240 if ((ip->slink = strdup(val)) == NULL) 241 mtree_err("memory allocation error"); 242 break; 243 case F_TIME: 244 ip->st_mtimespec.tv_sec = 245 (time_t)strtoul(val, &ep, 10); 246 if (*ep != '.') 247 mtree_err("invalid time %s", val); 248 val = ep + 1; 249 ip->st_mtimespec.tv_nsec = strtol(val, &ep, 10); 250 if (*ep) 251 mtree_err("invalid time %s", val); 252 break; 253 case F_TYPE: 254 switch(*val) { 255 case 'b': 256 if (!strcmp(val, "block")) 257 ip->type = F_BLOCK; 258 break; 259 case 'c': 260 if (!strcmp(val, "char")) 261 ip->type = F_CHAR; 262 break; 263 case 'd': 264 if (!strcmp(val, "dir")) 265 ip->type = F_DIR; 266 break; 267 case 'f': 268 if (!strcmp(val, "file")) 269 ip->type = F_FILE; 270 if (!strcmp(val, "fifo")) 271 ip->type = F_FIFO; 272 break; 273 case 'l': 274 if (!strcmp(val, "link")) 275 ip->type = F_LINK; 276 break; 277 case 's': 278 if (!strcmp(val, "socket")) 279 ip->type = F_SOCK; 280 break; 281 default: 282 mtree_err("unknown file type %s", val); 283 } 284 break; 285 case F_UID: 286 ip->st_uid = (uid_t)strtoul(val, &ep, 10); 287 if (*ep) 288 mtree_err("invalid uid %s", val); 289 break; 290 case F_UNAME: 291 if ((pw = getpwnam(val)) == NULL) 292 mtree_err("unknown user %s", val); 293 ip->st_uid = pw->pw_uid; 294 break; 295 } 296 } 297 } 298 299 static void 300 unset(char *t, NODE *ip) 301 { 302 char *p; 303 304 while ((p = strtok(t, "\n\t ")) != NULL) 305 ip->flags &= ~parsekey(p, NULL); 306 } 307