spec.c revision 1.29
1/* $NetBSD: spec.c,v 1.29 2001/10/05 12:44:47 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 39static char sccsid[] = "@(#)spec.c 8.2 (Berkeley) 4/28/95"; 40#else 41__RCSID("$NetBSD: spec.c,v 1.29 2001/10/05 12:44:47 lukem Exp $"); 42#endif 43#endif /* not lint */ 44 45#include <sys/param.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 <util.h> 57#include <vis.h> 58 59#include "mtree.h" 60#include "extern.h" 61 62size_t lineno; /* Current spec line number. */ 63 64static void set(char *, NODE *); 65static void unset(char *, NODE *); 66 67NODE * 68spec(void) 69{ 70 NODE *centry, *last; 71 char *p; 72 NODE ginfo, *root; 73 char *buf; 74 75 root = NULL; 76 centry = last = NULL; 77 memset(&ginfo, 0, sizeof(ginfo)); 78 for (lineno = 0; 79 (buf = fparseln(stdin, NULL, &lineno, NULL, 80 FPARSELN_UNESCCOMM | FPARSELN_UNESCCONT | FPARSELN_UNESCESC)); 81 free(buf)) { 82 /* Skip leading whitespace. */ 83 for (p = buf; *p && isspace((unsigned char)*p); ++p) 84 continue; 85 86 /* If nothing but whitespace, continue. */ 87 if (!*p) 88 continue; 89 90#ifdef DEBUG 91 (void)fprintf(stderr, "line %d: {%s}\n", lineno, p); 92#endif 93 94 /* Grab file name, "$", "set", or "unset". */ 95 if ((p = strtok(p, "\n\t ")) == NULL) 96 mtree_err("missing field"); 97 98 if (p[0] == '/') { 99 if (strcmp(p + 1, "set") == 0) 100 set(NULL, &ginfo); 101 else if (strcmp(p + 1, "unset") == 0) 102 unset(NULL, &ginfo); 103 else 104 mtree_err("invalid specification `%s'", p); 105 continue; 106 } 107 108 if (strchr(p, '/')) 109 mtree_err("slash character in file name"); 110 111 if (!strcmp(p, "..")) { 112 /* Don't go up, if haven't gone down. */ 113 if (!root) 114 goto noparent; 115 if (last->type != F_DIR || last->flags & F_DONE) { 116 if (last == root) 117 goto noparent; 118 last = last->parent; 119 } 120 last->flags |= F_DONE; 121 continue; 122 123noparent: mtree_err("no parent node"); 124 } 125 126 if ((centry = calloc(1, sizeof(NODE) + strlen(p))) == NULL) 127 mtree_err("%s", strerror(errno)); 128 *centry = ginfo; 129 if (strunvis(centry->name, p) == -1) 130 mtree_err("strunvis failed on `%s'", p); 131#define MAGIC "?*[" 132 if (strpbrk(p, MAGIC)) 133 centry->flags |= F_MAGIC; 134 set(NULL, centry); 135 136 if (!root) { 137 last = root = centry; 138 root->parent = root; 139 } else if (last->type == F_DIR && !(last->flags & F_DONE)) { 140 centry->parent = last; 141 last = last->child = centry; 142 } else { 143 centry->parent = last->parent; 144 centry->prev = last; 145 last = last->next = centry; 146 } 147 } 148 return (root); 149} 150 151/* 152 * dump_nodes -- 153 * dump the NODEs from `cur', based in the directory `dir' 154 */ 155void 156dump_nodes(const char *dir, NODE *root) 157{ 158 NODE *cur; 159 char path[MAXPATHLEN + 1]; 160 161 for (cur = root; cur != NULL; cur = cur->next) { 162 if (cur->type != F_DIR && !matchtags(cur)) 163 continue; 164 165 if (snprintf(path, sizeof(path), "%s%s%s", 166 dir, *dir ? "/" : "", cur->name) 167 >= sizeof(path)) 168 mtree_err("Pathname too long."); 169 if (keys & F_TYPE) 170 printf("type=%s ", nodetype(cur->type)); 171 if (keys & F_UID) 172 printf("uid=%u ", cur->st_uid); 173 if (keys & F_UNAME) 174 printf("uname=%s ", user_from_uid(cur->st_uid, 0)); 175 if (keys & F_GID) 176 printf("gid=%u ", cur->st_gid); 177 if (keys & F_GNAME) 178 printf("gname=%s ", group_from_gid(cur->st_gid, 0)); 179 if (keys & F_MODE) 180 printf("mode=%#o ", cur->st_mode); 181 if (keys & F_NLINK && cur->type != F_DIR && 182 cur->st_nlink != 1) 183 printf("nlink=%d ", cur->st_nlink); 184 if (keys & F_SLINK && cur->slink != NULL) 185 printf("link=%s ", cur->slink); 186 if (keys & F_SIZE && cur->type == F_FILE) 187 printf("size=%lld ", (long long)cur->st_size); 188 if (keys & F_TIME) 189 printf("time=%ld.%ld ", (long)cur->st_mtimespec.tv_sec, 190 cur->st_mtimespec.tv_nsec); 191 if (keys & F_CKSUM && cur->type == F_FILE) 192 printf("cksum=%lu ", cur->cksum); 193 if (keys & F_MD5 && cur->type == F_FILE && cur->md5sum != NULL) 194 printf("md5=%s ", cur->md5sum); 195 if (keys & F_FLAGS) 196 printf("flags=%s ", 197 flags_to_string(cur->st_flags, "none")); 198 if (keys & F_TAGS && cur->tags != NULL) 199 printf("tags=%s ", cur->tags); 200 puts(path); 201 202 if (cur->child) 203 dump_nodes(path, cur->child); 204 } 205} 206 207static void 208set(char *t, NODE *ip) 209{ 210 int type; 211 gid_t gid; 212 uid_t uid; 213 char *kw, *val, *md; 214 void *m; 215 int value, len; 216 char *ep; 217 218 val = NULL; 219 for (; (kw = strtok(t, "= \t\n")) != NULL; t = NULL) { 220 ip->flags |= type = parsekey(kw, &value); 221 if (value && (val = strtok(NULL, " \t\n")) == NULL) 222 mtree_err("missing value"); 223 switch(type) { 224 case F_CKSUM: 225 ip->cksum = strtoul(val, &ep, 10); 226 if (*ep) 227 mtree_err("invalid checksum `%s'", val); 228 break; 229 case F_FLAGS: 230 if (strcmp("none", val) == 0) 231 ip->st_flags = 0; 232 else if (string_to_flags(&val, &ip->st_flags, NULL) != 0) 233 mtree_err("invalid flag `%s'", val); 234 break; 235 case F_GID: 236 ip->st_gid = (gid_t)strtoul(val, &ep, 10); 237 if (*ep) 238 mtree_err("invalid gid `%s'", val); 239 break; 240 case F_GNAME: 241 if (gid_from_group(val, &gid) == -1) 242 mtree_err("unknown group `%s'", val); 243 ip->st_gid = gid; 244 break; 245 case F_IGN: 246 case F_OPT: 247 /* just set flag bit */ 248 break; 249 case F_MD5: 250 if (val[0]=='0' && val[1]=='x') 251 md=&val[2]; 252 else 253 md=val; 254 if ((ip->md5sum = strdup(md)) == NULL) 255 mtree_err("memory allocation error"); 256 break; 257 case F_MODE: 258 if ((m = setmode(val)) == NULL) 259 mtree_err("invalid file mode `%s'", val); 260 ip->st_mode = getmode(m, 0); 261 free(m); 262 break; 263 case F_NLINK: 264 ip->st_nlink = (nlink_t)strtoul(val, &ep, 10); 265 if (*ep) 266 mtree_err("invalid link count `%s'", val); 267 break; 268 case F_SIZE: 269 ip->st_size = (off_t)strtoq(val, &ep, 10); 270 if (*ep) 271 mtree_err("invalid size `%s'", val); 272 break; 273 case F_SLINK: 274 if ((ip->slink = strdup(val)) == NULL) 275 mtree_err("memory allocation error"); 276 break; 277 case F_TAGS: 278 len = strlen(val) + 3; /* "," + str + ",\0" */ 279 if ((ip->tags = malloc(len)) == NULL) 280 mtree_err("memory allocation error"); 281 snprintf(ip->tags, len, ",%s,", val); 282 break; 283 case F_TIME: 284 ip->st_mtimespec.tv_sec = 285 (time_t)strtoul(val, &ep, 10); 286 if (*ep != '.') 287 mtree_err("invalid time `%s'", val); 288 val = ep + 1; 289 ip->st_mtimespec.tv_nsec = strtol(val, &ep, 10); 290 if (*ep) 291 mtree_err("invalid time `%s'", val); 292 break; 293 case F_TYPE: 294 ip->type = parsetype(val); 295 break; 296 case F_UID: 297 ip->st_uid = (uid_t)strtoul(val, &ep, 10); 298 if (*ep) 299 mtree_err("invalid uid `%s'", val); 300 break; 301 case F_UNAME: 302 if (uid_from_user(val, &uid) == -1) 303 mtree_err("unknown user `%s'", val); 304 ip->st_uid = uid; 305 break; 306 default: 307 mtree_err( 308 "set(): unsupported key type 0x%x (INTERNAL ERROR)", 309 type); 310 /* NOTREACHED */ 311 } 312 } 313} 314 315static void 316unset(char *t, NODE *ip) 317{ 318 char *p; 319 320 while ((p = strtok(t, "\n\t ")) != NULL) 321 ip->flags &= ~parsekey(p, NULL); 322} 323