1 /*- 2 * Copyright (c) 1989 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 static char sccsid[] = "@(#)spec.c 5.18 (Berkeley) 06/18/92"; 10 #endif /* not lint */ 11 12 #include <sys/types.h> 13 #include <sys/stat.h> 14 #include <fts.h> 15 #include <pwd.h> 16 #include <grp.h> 17 #include <errno.h> 18 #include <unistd.h> 19 #include <stdio.h> 20 #include <ctype.h> 21 #include "mtree.h" 22 #include "extern.h" 23 24 int lineno; /* Current spec line number. */ 25 26 static void set __P((char *, NODE *)); 27 static void unset __P((char *, NODE *)); 28 29 NODE * 30 spec() 31 { 32 register NODE *centry, *last; 33 register char *p; 34 NODE ginfo, *root; 35 int c_cur, c_next; 36 char buf[2048]; 37 38 root = NULL; 39 bzero(&ginfo, sizeof(ginfo)); 40 c_cur = c_next = 0; 41 for (lineno = 1; fgets(buf, sizeof(buf), stdin); 42 ++lineno, c_cur = c_next, c_next = 0) { 43 /* Skip empty lines. */ 44 if (buf[0] == '\n') 45 continue; 46 47 /* Find end of line. */ 48 if ((p = index(buf, '\n')) == NULL) 49 err("line %d too long", lineno); 50 51 /* See if next line is continuation line. */ 52 if (p[-1] == '\\') { 53 --p; 54 c_next = 1; 55 } 56 57 /* Null-terminate the line. */ 58 *p = '\0'; 59 60 /* Skip leading whitespace. */ 61 for (p = buf; *p && isspace(*p); ++p); 62 63 /* If nothing but whitespace or comment char, continue. */ 64 if (!*p || *p == '#') 65 continue; 66 67 #ifdef DEBUG 68 (void)fprintf(stderr, "line %d: {%s}\n", lineno, p); 69 #endif 70 if (c_cur) { 71 set(p, centry); 72 continue; 73 } 74 75 /* Grab file name, "$", "set", or "unset". */ 76 if ((p = strtok(p, "\n\t ")) == NULL) 77 err("missing field"); 78 79 if (p[0] == '/') 80 switch(p[1]) { 81 case 's': 82 if (strcmp(p + 1, "set")) 83 break; 84 set(NULL, &ginfo); 85 continue; 86 case 'u': 87 if (strcmp(p + 1, "unset")) 88 break; 89 unset(NULL, &ginfo); 90 continue; 91 } 92 93 if (index(p, '/')) 94 err("slash character in file name"); 95 96 if (!strcmp(p, "..")) { 97 /* Don't go up, if haven't gone down. */ 98 if (!root) 99 goto noparent; 100 if (last->type != F_DIR || last->flags & F_DONE) { 101 if (last == root) 102 goto noparent; 103 last = last->parent; 104 } 105 last->flags |= F_DONE; 106 continue; 107 108 noparent: err("no parent node"); 109 } 110 111 if ((centry = calloc(1, sizeof(NODE) + strlen(p))) == NULL) 112 err("%s", strerror(errno)); 113 *centry = ginfo; 114 (void)strcpy(centry->name, p); 115 #define MAGIC "?*[" 116 if (strpbrk(p, MAGIC)) 117 centry->flags |= F_MAGIC; 118 set(NULL, centry); 119 120 if (!root) { 121 last = root = centry; 122 root->parent = root; 123 } else if (last->type == F_DIR && !(last->flags & F_DONE)) { 124 centry->parent = last; 125 last = last->child = centry; 126 } else { 127 centry->parent = last->parent; 128 centry->prev = last; 129 last = last->next = centry; 130 } 131 } 132 return (root); 133 } 134 135 static void 136 set(t, ip) 137 char *t; 138 register NODE *ip; 139 { 140 register int type; 141 register char *kw, *val; 142 struct group *gr; 143 struct passwd *pw; 144 mode_t *m; 145 int value; 146 char *ep; 147 148 for (; kw = strtok(t, "= \t\n"); t = NULL) { 149 ip->flags |= type = parsekey(kw, &value); 150 if (value && (val = strtok(NULL, " \t\n")) == NULL) 151 err("missing value"); 152 switch(type) { 153 case F_CKSUM: 154 ip->cksum = strtoul(val, &ep, 10); 155 if (*ep) 156 err("invalid checksum %s", val); 157 break; 158 case F_GID: 159 ip->st_gid = strtoul(val, &ep, 10); 160 if (*ep) 161 err("invalid gid %s", val); 162 break; 163 case F_GNAME: 164 if ((gr = getgrnam(val)) == NULL) 165 err("unknown group %s", val); 166 ip->st_gid = gr->gr_gid; 167 break; 168 case F_IGN: 169 /* just set flag bit */ 170 break; 171 case F_MODE: 172 if ((m = setmode(val)) == NULL) 173 err("invalid file mode %s", val); 174 ip->st_mode = getmode(m, 0); 175 break; 176 case F_NLINK: 177 ip->st_nlink = strtoul(val, &ep, 10); 178 if (*ep) 179 err("invalid link count %s", val); 180 break; 181 case F_SIZE: 182 ip->st_size = strtoul(val, &ep, 10); 183 if (*ep) 184 err("invalid size %s", val); 185 break; 186 case F_SLINK: 187 if ((ip->slink = strdup(val)) == NULL) 188 err("%s", strerror(errno)); 189 break; 190 case F_TIME: 191 ip->st_mtimespec.ts_sec = strtoul(val, &ep, 10); 192 if (*ep != '.') 193 err("invalid time %s", val); 194 val = ep + 1; 195 ip->st_mtimespec.ts_nsec = strtoul(val, &ep, 10); 196 if (*ep) 197 err("invalid time %s", val); 198 break; 199 case F_TYPE: 200 switch(*val) { 201 case 'b': 202 if (!strcmp(val, "block")) 203 ip->type = F_BLOCK; 204 break; 205 case 'c': 206 if (!strcmp(val, "char")) 207 ip->type = F_CHAR; 208 break; 209 case 'd': 210 if (!strcmp(val, "dir")) 211 ip->type = F_DIR; 212 break; 213 case 'f': 214 if (!strcmp(val, "file")) 215 ip->type = F_FILE; 216 if (!strcmp(val, "fifo")) 217 ip->type = F_FIFO; 218 break; 219 case 'l': 220 if (!strcmp(val, "link")) 221 ip->type = F_LINK; 222 break; 223 case 's': 224 if (!strcmp(val, "socket")) 225 ip->type = F_SOCK; 226 break; 227 default: 228 err("unknown file type %s", val); 229 } 230 break; 231 case F_UID: 232 ip->st_uid = strtoul(val, &ep, 10); 233 if (*ep) 234 err("invalid uid %s", val); 235 break; 236 case F_UNAME: 237 if ((pw = getpwnam(val)) == NULL) 238 err("unknown user %s", val); 239 ip->st_uid = pw->pw_uid; 240 break; 241 } 242 } 243 } 244 245 static void 246 unset(t, ip) 247 char *t; 248 register NODE *ip; 249 { 250 register char *p; 251 252 while (p = strtok(t, "\n\t ")) 253 ip->flags &= ~parsekey(p, NULL); 254 } 255