1df930be7Sderaadt /* $NetBSD: spec.c,v 1.6 1995/03/07 21:12:12 cgd Exp $ */ 2*e296a845Shenning /* $OpenBSD: spec.c,v 1.17 2003/04/19 10:43:55 henning Exp $ */ 3df930be7Sderaadt 4df930be7Sderaadt /*- 5df930be7Sderaadt * Copyright (c) 1989, 1993 6df930be7Sderaadt * The Regents of the University of California. All rights reserved. 7df930be7Sderaadt * 8df930be7Sderaadt * Redistribution and use in source and binary forms, with or without 9df930be7Sderaadt * modification, are permitted provided that the following conditions 10df930be7Sderaadt * are met: 11df930be7Sderaadt * 1. Redistributions of source code must retain the above copyright 12df930be7Sderaadt * notice, this list of conditions and the following disclaimer. 13df930be7Sderaadt * 2. Redistributions in binary form must reproduce the above copyright 14df930be7Sderaadt * notice, this list of conditions and the following disclaimer in the 15df930be7Sderaadt * documentation and/or other materials provided with the distribution. 16df930be7Sderaadt * 3. All advertising materials mentioning features or use of this software 17df930be7Sderaadt * must display the following acknowledgement: 18df930be7Sderaadt * This product includes software developed by the University of 19df930be7Sderaadt * California, Berkeley and its contributors. 20df930be7Sderaadt * 4. Neither the name of the University nor the names of its contributors 21df930be7Sderaadt * may be used to endorse or promote products derived from this software 22df930be7Sderaadt * without specific prior written permission. 23df930be7Sderaadt * 24df930be7Sderaadt * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25df930be7Sderaadt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26df930be7Sderaadt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27df930be7Sderaadt * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28df930be7Sderaadt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29df930be7Sderaadt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30df930be7Sderaadt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31df930be7Sderaadt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32df930be7Sderaadt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33df930be7Sderaadt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34df930be7Sderaadt * SUCH DAMAGE. 35df930be7Sderaadt */ 36df930be7Sderaadt 37df930be7Sderaadt #ifndef lint 38df930be7Sderaadt #if 0 39ea11b3eaSmillert static const char sccsid[] = "@(#)spec.c 8.1 (Berkeley) 6/6/93"; 40df930be7Sderaadt #else 41*e296a845Shenning static const char rcsid[] = "$OpenBSD: spec.c,v 1.17 2003/04/19 10:43:55 henning Exp $"; 42df930be7Sderaadt #endif 43df930be7Sderaadt #endif /* not lint */ 44df930be7Sderaadt 45df930be7Sderaadt #include <sys/types.h> 46df930be7Sderaadt #include <sys/stat.h> 47df930be7Sderaadt #include <fts.h> 48df930be7Sderaadt #include <pwd.h> 49df930be7Sderaadt #include <grp.h> 50df930be7Sderaadt #include <errno.h> 51df930be7Sderaadt #include <unistd.h> 52df930be7Sderaadt #include <stdio.h> 53df930be7Sderaadt #include <ctype.h> 5424f00e12Smillert #include <vis.h> 55df930be7Sderaadt #include "mtree.h" 56df930be7Sderaadt #include "extern.h" 57df930be7Sderaadt 58df930be7Sderaadt int lineno; /* Current spec line number. */ 59df930be7Sderaadt 60c72b5b24Smillert static void set(char *, NODE *); 61c72b5b24Smillert static void unset(char *, NODE *); 62df930be7Sderaadt 63df930be7Sderaadt NODE * 64df930be7Sderaadt spec() 65df930be7Sderaadt { 660ac0d02eSmpech NODE *centry, *last; 670ac0d02eSmpech char *p; 68df930be7Sderaadt NODE ginfo, *root; 69df930be7Sderaadt int c_cur, c_next; 70df930be7Sderaadt char buf[2048]; 71*e296a845Shenning size_t len; 72df930be7Sderaadt 7346360e3cSderaadt centry = last = root = NULL; 74df930be7Sderaadt bzero(&ginfo, sizeof(ginfo)); 75df930be7Sderaadt c_cur = c_next = 0; 76df930be7Sderaadt for (lineno = 1; fgets(buf, sizeof(buf), stdin); 77df930be7Sderaadt ++lineno, c_cur = c_next, c_next = 0) { 78df930be7Sderaadt /* Skip empty lines. */ 79df930be7Sderaadt if (buf[0] == '\n') 80df930be7Sderaadt continue; 81df930be7Sderaadt 82df930be7Sderaadt /* Find end of line. */ 83180acc8fSmillert if ((p = strchr(buf, '\n')) == NULL) 84642654b6Smillert error("line %d too long", lineno); 85df930be7Sderaadt 86df930be7Sderaadt /* See if next line is continuation line. */ 87df930be7Sderaadt if (p[-1] == '\\') { 88df930be7Sderaadt --p; 89df930be7Sderaadt c_next = 1; 90df930be7Sderaadt } 91df930be7Sderaadt 92df930be7Sderaadt /* Null-terminate the line. */ 93df930be7Sderaadt *p = '\0'; 94df930be7Sderaadt 95df930be7Sderaadt /* Skip leading whitespace. */ 96df930be7Sderaadt for (p = buf; *p && isspace(*p); ++p); 97df930be7Sderaadt 98df930be7Sderaadt /* If nothing but whitespace or comment char, continue. */ 99df930be7Sderaadt if (!*p || *p == '#') 100df930be7Sderaadt continue; 101df930be7Sderaadt 102df930be7Sderaadt #ifdef DEBUG 103df930be7Sderaadt (void)fprintf(stderr, "line %d: {%s}\n", lineno, p); 104df930be7Sderaadt #endif 105df930be7Sderaadt if (c_cur) { 106df930be7Sderaadt set(p, centry); 107df930be7Sderaadt continue; 108df930be7Sderaadt } 109df930be7Sderaadt 110df930be7Sderaadt /* Grab file name, "$", "set", or "unset". */ 111df930be7Sderaadt if ((p = strtok(p, "\n\t ")) == NULL) 112642654b6Smillert error("missing field"); 113df930be7Sderaadt 114df930be7Sderaadt if (p[0] == '/') 115df930be7Sderaadt switch(p[1]) { 116df930be7Sderaadt case 's': 117df930be7Sderaadt if (strcmp(p + 1, "set")) 118df930be7Sderaadt break; 119df930be7Sderaadt set(NULL, &ginfo); 120df930be7Sderaadt continue; 121df930be7Sderaadt case 'u': 122df930be7Sderaadt if (strcmp(p + 1, "unset")) 123df930be7Sderaadt break; 124df930be7Sderaadt unset(NULL, &ginfo); 125df930be7Sderaadt continue; 126df930be7Sderaadt } 127df930be7Sderaadt 128180acc8fSmillert if (strchr(p, '/')) 129642654b6Smillert error("slash character in file name"); 130df930be7Sderaadt 131df930be7Sderaadt if (!strcmp(p, "..")) { 132df930be7Sderaadt /* Don't go up, if haven't gone down. */ 133df930be7Sderaadt if (!root) 134df930be7Sderaadt goto noparent; 135df930be7Sderaadt if (last->type != F_DIR || last->flags & F_DONE) { 136df930be7Sderaadt if (last == root) 137df930be7Sderaadt goto noparent; 138df930be7Sderaadt last = last->parent; 139df930be7Sderaadt } 140df930be7Sderaadt last->flags |= F_DONE; 141df930be7Sderaadt continue; 142df930be7Sderaadt 143642654b6Smillert noparent: error("no parent node"); 144df930be7Sderaadt } 145df930be7Sderaadt 146*e296a845Shenning len = strlen(p) + 1; /* NUL in struct _node */ 147*e296a845Shenning if ((centry = calloc(1, sizeof(NODE) + len - 1)) == NULL) 148642654b6Smillert error("%s", strerror(errno)); 149df930be7Sderaadt *centry = ginfo; 150df930be7Sderaadt #define MAGIC "?*[" 151df930be7Sderaadt if (strpbrk(p, MAGIC)) 152df930be7Sderaadt centry->flags |= F_MAGIC; 15324f00e12Smillert if (strunvis(centry->name, p) == -1) { 15424f00e12Smillert fprintf(stderr, 15524f00e12Smillert "mtree: filename (%s) encoded incorrectly\n", p); 156*e296a845Shenning strlcpy(centry->name, p, len); 15724f00e12Smillert } 158df930be7Sderaadt set(NULL, centry); 159df930be7Sderaadt 160df930be7Sderaadt if (!root) { 161df930be7Sderaadt last = root = centry; 162df930be7Sderaadt root->parent = root; 163df930be7Sderaadt } else if (last->type == F_DIR && !(last->flags & F_DONE)) { 164df930be7Sderaadt centry->parent = last; 165df930be7Sderaadt last = last->child = centry; 166df930be7Sderaadt } else { 167df930be7Sderaadt centry->parent = last->parent; 168df930be7Sderaadt centry->prev = last; 169df930be7Sderaadt last = last->next = centry; 170df930be7Sderaadt } 171df930be7Sderaadt } 172df930be7Sderaadt return (root); 173df930be7Sderaadt } 174df930be7Sderaadt 175df930be7Sderaadt static void 176df930be7Sderaadt set(t, ip) 177df930be7Sderaadt char *t; 1780ac0d02eSmpech NODE *ip; 179df930be7Sderaadt { 1800ac0d02eSmpech int type; 18131636a68Smillert char *kw, *val = NULL; 182df930be7Sderaadt struct group *gr; 183df930be7Sderaadt struct passwd *pw; 184df930be7Sderaadt mode_t *m; 185df930be7Sderaadt int value; 18631636a68Smillert u_int32_t fset, fclr; 187df930be7Sderaadt char *ep; 188*e296a845Shenning size_t len; 189df930be7Sderaadt 19046360e3cSderaadt for (; (kw = strtok(t, "= \t\n")); t = NULL) { 191df930be7Sderaadt ip->flags |= type = parsekey(kw, &value); 192df930be7Sderaadt if (value && (val = strtok(NULL, " \t\n")) == NULL) 193642654b6Smillert error("missing value"); 194df930be7Sderaadt switch(type) { 195df930be7Sderaadt case F_CKSUM: 196df930be7Sderaadt ip->cksum = strtoul(val, &ep, 10); 197df930be7Sderaadt if (*ep) 198642654b6Smillert error("invalid checksum %s", val); 199df930be7Sderaadt break; 20046360e3cSderaadt case F_MD5: 20146360e3cSderaadt ip->md5digest = strdup(val); 2021a0a9baeSmillert if (!ip->md5digest) 203642654b6Smillert error("%s", strerror(errno)); 20446360e3cSderaadt break; 20531636a68Smillert case F_FLAGS: 20631636a68Smillert if (!strcmp(val, "none")) { 20731636a68Smillert ip->file_flags = 0; 20831636a68Smillert break; 20931636a68Smillert } 21031636a68Smillert if (strtofflags(&val, &fset, &fclr)) 21131636a68Smillert error("%s", strerror(errno)); 21231636a68Smillert ip->file_flags = fset; 21331636a68Smillert break; 214df930be7Sderaadt case F_GID: 215df930be7Sderaadt ip->st_gid = strtoul(val, &ep, 10); 216df930be7Sderaadt if (*ep) 217642654b6Smillert error("invalid gid %s", val); 218df930be7Sderaadt break; 219df930be7Sderaadt case F_GNAME: 220df930be7Sderaadt if ((gr = getgrnam(val)) == NULL) 221642654b6Smillert error("unknown group %s", val); 222df930be7Sderaadt ip->st_gid = gr->gr_gid; 223df930be7Sderaadt break; 224df930be7Sderaadt case F_IGN: 225df930be7Sderaadt /* just set flag bit */ 226df930be7Sderaadt break; 227df930be7Sderaadt case F_MODE: 228df930be7Sderaadt if ((m = setmode(val)) == NULL) 229642654b6Smillert error("invalid file mode %s", val); 230df930be7Sderaadt ip->st_mode = getmode(m, 0); 23153962ae7Smillert free(m); 232df930be7Sderaadt break; 233df930be7Sderaadt case F_NLINK: 234df930be7Sderaadt ip->st_nlink = strtoul(val, &ep, 10); 235df930be7Sderaadt if (*ep) 236642654b6Smillert error("invalid link count %s", val); 237df930be7Sderaadt break; 23837841f9cSmillert case F_RMD160: 23937841f9cSmillert ip->rmd160digest = strdup(val); 24037841f9cSmillert if (!ip->rmd160digest) 241642654b6Smillert error("%s", strerror(errno)); 24237841f9cSmillert break; 2431a0a9baeSmillert case F_SHA1: 2441a0a9baeSmillert ip->sha1digest = strdup(val); 2451a0a9baeSmillert if (!ip->sha1digest) 246642654b6Smillert error("%s", strerror(errno)); 2471a0a9baeSmillert break; 248df930be7Sderaadt case F_SIZE: 24994bb8e67Smillert ip->st_size = strtouq(val, &ep, 10); 250df930be7Sderaadt if (*ep) 251642654b6Smillert error("invalid size %s", val); 252df930be7Sderaadt break; 253df930be7Sderaadt case F_SLINK: 254*e296a845Shenning len = strlen(val) + 1; 255*e296a845Shenning if ((ip->slink = malloc(len)) == NULL) 256642654b6Smillert error("%s", strerror(errno)); 257e34d4c20Smillert if (strunvis(ip->slink, val) == -1) { 258e34d4c20Smillert fprintf(stderr, 259e34d4c20Smillert "mtree: filename (%s) encoded incorrectly\n", val); 260*e296a845Shenning strlcpy(ip->slink, val, len); 261e34d4c20Smillert } 262df930be7Sderaadt break; 263df930be7Sderaadt case F_TIME: 264c9fd28a4Stholo ip->st_mtimespec.tv_sec = strtoul(val, &ep, 10); 265df930be7Sderaadt if (*ep != '.') 266642654b6Smillert error("invalid time %s", val); 267df930be7Sderaadt val = ep + 1; 268c9fd28a4Stholo ip->st_mtimespec.tv_nsec = strtoul(val, &ep, 10); 269df930be7Sderaadt if (*ep) 270642654b6Smillert error("invalid time %s", val); 271df930be7Sderaadt break; 272df930be7Sderaadt case F_TYPE: 273df930be7Sderaadt switch(*val) { 274df930be7Sderaadt case 'b': 275df930be7Sderaadt if (!strcmp(val, "block")) 276df930be7Sderaadt ip->type = F_BLOCK; 277df930be7Sderaadt break; 278df930be7Sderaadt case 'c': 279df930be7Sderaadt if (!strcmp(val, "char")) 280df930be7Sderaadt ip->type = F_CHAR; 281df930be7Sderaadt break; 282df930be7Sderaadt case 'd': 283df930be7Sderaadt if (!strcmp(val, "dir")) 284df930be7Sderaadt ip->type = F_DIR; 285df930be7Sderaadt break; 286df930be7Sderaadt case 'f': 287df930be7Sderaadt if (!strcmp(val, "file")) 288df930be7Sderaadt ip->type = F_FILE; 289df930be7Sderaadt if (!strcmp(val, "fifo")) 290df930be7Sderaadt ip->type = F_FIFO; 291df930be7Sderaadt break; 292df930be7Sderaadt case 'l': 293df930be7Sderaadt if (!strcmp(val, "link")) 294df930be7Sderaadt ip->type = F_LINK; 295df930be7Sderaadt break; 296df930be7Sderaadt case 's': 297df930be7Sderaadt if (!strcmp(val, "socket")) 298df930be7Sderaadt ip->type = F_SOCK; 299df930be7Sderaadt break; 300df930be7Sderaadt default: 301642654b6Smillert error("unknown file type %s", val); 302df930be7Sderaadt } 303df930be7Sderaadt break; 304df930be7Sderaadt case F_UID: 305df930be7Sderaadt ip->st_uid = strtoul(val, &ep, 10); 306df930be7Sderaadt if (*ep) 307642654b6Smillert error("invalid uid %s", val); 308df930be7Sderaadt break; 309df930be7Sderaadt case F_UNAME: 310df930be7Sderaadt if ((pw = getpwnam(val)) == NULL) 311642654b6Smillert error("unknown user %s", val); 312df930be7Sderaadt ip->st_uid = pw->pw_uid; 313df930be7Sderaadt break; 314df930be7Sderaadt } 315df930be7Sderaadt } 316df930be7Sderaadt } 317df930be7Sderaadt 318df930be7Sderaadt static void 319df930be7Sderaadt unset(t, ip) 320df930be7Sderaadt char *t; 3210ac0d02eSmpech NODE *ip; 322df930be7Sderaadt { 3230ac0d02eSmpech char *p; 324df930be7Sderaadt 32546360e3cSderaadt while ((p = strtok(t, "\n\t "))) 326df930be7Sderaadt ip->flags &= ~parsekey(p, NULL); 327df930be7Sderaadt } 328