spec.c revision 1.28
1/* $NetBSD: spec.c,v 1.28 2001/10/05 01:03:25 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.28 2001/10/05 01:03:25 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_uid); 177 if (keys & F_MODE) 178 printf("mode=%#o ", cur->st_mode); 179 if (keys & F_NLINK && cur->type != F_DIR && 180 cur->st_nlink != 1) 181 printf("nlink=%d ", cur->st_nlink); 182 if (keys & F_SLINK && cur->slink != NULL) 183 printf("link=%s ", cur->slink); 184 if (keys & F_SIZE && cur->type == F_FILE) 185 printf("size=%lld ", (long long)cur->st_size); 186 if (keys & F_TIME) 187 printf("time=%ld.%ld ", (long)cur->st_mtimespec.tv_sec, 188 cur->st_mtimespec.tv_nsec); 189 if (keys & F_CKSUM && cur->type == F_FILE) 190 printf("cksum=%lu ", cur->cksum); 191 if (keys & F_MD5 && cur->type == F_FILE && cur->md5sum != NULL) 192 printf("md5=%s ", cur->md5sum); 193 if (keys & F_FLAGS) 194 printf("flags=%s ", 195 flags_to_string(cur->st_flags, "none")); 196 if (keys & F_TAGS && cur->tags != NULL) 197 printf("tags=%s ", cur->tags); 198 puts(path); 199 200 if (cur->child) 201 dump_nodes(path, cur->child); 202 } 203} 204 205static void 206set(char *t, NODE *ip) 207{ 208 int type; 209 gid_t gid; 210 uid_t uid; 211 char *kw, *val, *md; 212 void *m; 213 int value, len; 214 char *ep; 215 216 val = NULL; 217 for (; (kw = strtok(t, "= \t\n")) != NULL; t = NULL) { 218 ip->flags |= type = parsekey(kw, &value); 219 if (value && (val = strtok(NULL, " \t\n")) == NULL) 220 mtree_err("missing value"); 221 switch(type) { 222 case F_CKSUM: 223 ip->cksum = strtoul(val, &ep, 10); 224 if (*ep) 225 mtree_err("invalid checksum `%s'", val); 226 break; 227 case F_FLAGS: 228 if (strcmp("none", val) == 0) 229 ip->st_flags = 0; 230 else if (string_to_flags(&val, &ip->st_flags, NULL) != 0) 231 mtree_err("invalid flag `%s'", val); 232 break; 233 case F_GID: 234 ip->st_gid = (gid_t)strtoul(val, &ep, 10); 235 if (*ep) 236 mtree_err("invalid gid `%s'", val); 237 break; 238 case F_GNAME: 239 if (gid_from_group(val, &gid) == -1) 240 mtree_err("unknown group `%s'", val); 241 ip->st_gid = gid; 242 break; 243 case F_IGN: 244 case F_OPT: 245 /* just set flag bit */ 246 break; 247 case F_MD5: 248 if (val[0]=='0' && val[1]=='x') 249 md=&val[2]; 250 else 251 md=val; 252 if ((ip->md5sum = strdup(md)) == NULL) 253 mtree_err("memory allocation error"); 254 break; 255 case F_MODE: 256 if ((m = setmode(val)) == NULL) 257 mtree_err("invalid file mode `%s'", val); 258 ip->st_mode = getmode(m, 0); 259 free(m); 260 break; 261 case F_NLINK: 262 ip->st_nlink = (nlink_t)strtoul(val, &ep, 10); 263 if (*ep) 264 mtree_err("invalid link count `%s'", val); 265 break; 266 case F_SIZE: 267 ip->st_size = (off_t)strtoq(val, &ep, 10); 268 if (*ep) 269 mtree_err("invalid size `%s'", val); 270 break; 271 case F_SLINK: 272 if ((ip->slink = strdup(val)) == NULL) 273 mtree_err("memory allocation error"); 274 break; 275 case F_TAGS: 276 len = strlen(val) + 3; /* "," + str + ",\0" */ 277 if ((ip->tags = malloc(len)) == NULL) 278 mtree_err("memory allocation error"); 279 snprintf(ip->tags, len, ",%s,", val); 280 break; 281 case F_TIME: 282 ip->st_mtimespec.tv_sec = 283 (time_t)strtoul(val, &ep, 10); 284 if (*ep != '.') 285 mtree_err("invalid time `%s'", val); 286 val = ep + 1; 287 ip->st_mtimespec.tv_nsec = strtol(val, &ep, 10); 288 if (*ep) 289 mtree_err("invalid time `%s'", val); 290 break; 291 case F_TYPE: 292 ip->type = parsetype(val); 293 break; 294 case F_UID: 295 ip->st_uid = (uid_t)strtoul(val, &ep, 10); 296 if (*ep) 297 mtree_err("invalid uid `%s'", val); 298 break; 299 case F_UNAME: 300 if (uid_from_user(val, &uid) == -1) 301 mtree_err("unknown user `%s'", val); 302 ip->st_uid = uid; 303 break; 304 default: 305 mtree_err( 306 "set(): unsupported key type 0x%x (INTERNAL ERROR)", 307 type); 308 /* NOTREACHED */ 309 } 310 } 311} 312 313static void 314unset(char *t, NODE *ip) 315{ 316 char *p; 317 318 while ((p = strtok(t, "\n\t ")) != NULL) 319 ip->flags &= ~parsekey(p, NULL); 320} 321