1df930be7Sderaadt /* $NetBSD: spec.c,v 1.6 1995/03/07 21:12:12 cgd Exp $ */ 2*c7b4a4caSotto /* $OpenBSD: spec.c,v 1.20 2004/07/01 18:25:48 otto 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. 1629295d1cSmillert * 3. Neither the name of the University nor the names of its contributors 17df930be7Sderaadt * may be used to endorse or promote products derived from this software 18df930be7Sderaadt * without specific prior written permission. 19df930be7Sderaadt * 20df930be7Sderaadt * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21df930be7Sderaadt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22df930be7Sderaadt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23df930be7Sderaadt * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24df930be7Sderaadt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25df930be7Sderaadt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26df930be7Sderaadt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27df930be7Sderaadt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28df930be7Sderaadt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29df930be7Sderaadt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30df930be7Sderaadt * SUCH DAMAGE. 31df930be7Sderaadt */ 32df930be7Sderaadt 33df930be7Sderaadt #ifndef lint 34df930be7Sderaadt #if 0 35ea11b3eaSmillert static const char sccsid[] = "@(#)spec.c 8.1 (Berkeley) 6/6/93"; 36df930be7Sderaadt #else 37*c7b4a4caSotto static const char rcsid[] = "$OpenBSD: spec.c,v 1.20 2004/07/01 18:25:48 otto Exp $"; 38df930be7Sderaadt #endif 39df930be7Sderaadt #endif /* not lint */ 40df930be7Sderaadt 41df930be7Sderaadt #include <sys/types.h> 42df930be7Sderaadt #include <sys/stat.h> 43df930be7Sderaadt #include <pwd.h> 44df930be7Sderaadt #include <grp.h> 45df930be7Sderaadt #include <errno.h> 46df930be7Sderaadt #include <unistd.h> 47df930be7Sderaadt #include <stdio.h> 48df930be7Sderaadt #include <ctype.h> 4924f00e12Smillert #include <vis.h> 50df930be7Sderaadt #include "mtree.h" 51df930be7Sderaadt #include "extern.h" 52df930be7Sderaadt 53df930be7Sderaadt int lineno; /* Current spec line number. */ 54df930be7Sderaadt 55c72b5b24Smillert static void set(char *, NODE *); 56c72b5b24Smillert static void unset(char *, NODE *); 57df930be7Sderaadt 58df930be7Sderaadt NODE * 59df930be7Sderaadt spec() 60df930be7Sderaadt { 610ac0d02eSmpech NODE *centry, *last; 620ac0d02eSmpech char *p; 63df930be7Sderaadt NODE ginfo, *root; 64df930be7Sderaadt int c_cur, c_next; 65df930be7Sderaadt char buf[2048]; 66e296a845Shenning size_t len; 67df930be7Sderaadt 6846360e3cSderaadt centry = last = root = NULL; 69df930be7Sderaadt bzero(&ginfo, sizeof(ginfo)); 70df930be7Sderaadt c_cur = c_next = 0; 71df930be7Sderaadt for (lineno = 1; fgets(buf, sizeof(buf), stdin); 72df930be7Sderaadt ++lineno, c_cur = c_next, c_next = 0) { 73df930be7Sderaadt /* Skip empty lines. */ 74df930be7Sderaadt if (buf[0] == '\n') 75df930be7Sderaadt continue; 76df930be7Sderaadt 77df930be7Sderaadt /* Find end of line. */ 78180acc8fSmillert if ((p = strchr(buf, '\n')) == NULL) 79642654b6Smillert error("line %d too long", lineno); 80df930be7Sderaadt 81df930be7Sderaadt /* See if next line is continuation line. */ 82df930be7Sderaadt if (p[-1] == '\\') { 83df930be7Sderaadt --p; 84df930be7Sderaadt c_next = 1; 85df930be7Sderaadt } 86df930be7Sderaadt 87df930be7Sderaadt /* Null-terminate the line. */ 88df930be7Sderaadt *p = '\0'; 89df930be7Sderaadt 90df930be7Sderaadt /* Skip leading whitespace. */ 91df930be7Sderaadt for (p = buf; *p && isspace(*p); ++p); 92df930be7Sderaadt 93df930be7Sderaadt /* If nothing but whitespace or comment char, continue. */ 94df930be7Sderaadt if (!*p || *p == '#') 95df930be7Sderaadt continue; 96df930be7Sderaadt 97df930be7Sderaadt #ifdef DEBUG 98df930be7Sderaadt (void)fprintf(stderr, "line %d: {%s}\n", lineno, p); 99df930be7Sderaadt #endif 100df930be7Sderaadt if (c_cur) { 101df930be7Sderaadt set(p, centry); 102df930be7Sderaadt continue; 103df930be7Sderaadt } 104df930be7Sderaadt 105df930be7Sderaadt /* Grab file name, "$", "set", or "unset". */ 106df930be7Sderaadt if ((p = strtok(p, "\n\t ")) == NULL) 107642654b6Smillert error("missing field"); 108df930be7Sderaadt 109df930be7Sderaadt if (p[0] == '/') 110df930be7Sderaadt switch(p[1]) { 111df930be7Sderaadt case 's': 112df930be7Sderaadt if (strcmp(p + 1, "set")) 113df930be7Sderaadt break; 114df930be7Sderaadt set(NULL, &ginfo); 115df930be7Sderaadt continue; 116df930be7Sderaadt case 'u': 117df930be7Sderaadt if (strcmp(p + 1, "unset")) 118df930be7Sderaadt break; 119df930be7Sderaadt unset(NULL, &ginfo); 120df930be7Sderaadt continue; 121df930be7Sderaadt } 122df930be7Sderaadt 123180acc8fSmillert if (strchr(p, '/')) 124642654b6Smillert error("slash character in file name"); 125df930be7Sderaadt 126df930be7Sderaadt if (!strcmp(p, "..")) { 127df930be7Sderaadt /* Don't go up, if haven't gone down. */ 128df930be7Sderaadt if (!root) 129df930be7Sderaadt goto noparent; 130df930be7Sderaadt if (last->type != F_DIR || last->flags & F_DONE) { 131df930be7Sderaadt if (last == root) 132df930be7Sderaadt goto noparent; 133df930be7Sderaadt last = last->parent; 134df930be7Sderaadt } 135df930be7Sderaadt last->flags |= F_DONE; 136df930be7Sderaadt continue; 137df930be7Sderaadt 138642654b6Smillert noparent: error("no parent node"); 139df930be7Sderaadt } 140df930be7Sderaadt 141e296a845Shenning len = strlen(p) + 1; /* NUL in struct _node */ 142e296a845Shenning if ((centry = calloc(1, sizeof(NODE) + len - 1)) == NULL) 143642654b6Smillert error("%s", strerror(errno)); 144df930be7Sderaadt *centry = ginfo; 145df930be7Sderaadt #define MAGIC "?*[" 146df930be7Sderaadt if (strpbrk(p, MAGIC)) 147df930be7Sderaadt centry->flags |= F_MAGIC; 14824f00e12Smillert if (strunvis(centry->name, p) == -1) { 14924f00e12Smillert fprintf(stderr, 15024f00e12Smillert "mtree: filename (%s) encoded incorrectly\n", p); 151e296a845Shenning strlcpy(centry->name, p, len); 15224f00e12Smillert } 153df930be7Sderaadt set(NULL, centry); 154df930be7Sderaadt 155df930be7Sderaadt if (!root) { 156df930be7Sderaadt last = root = centry; 157df930be7Sderaadt root->parent = root; 158df930be7Sderaadt } else if (last->type == F_DIR && !(last->flags & F_DONE)) { 159df930be7Sderaadt centry->parent = last; 160df930be7Sderaadt last = last->child = centry; 161df930be7Sderaadt } else { 162df930be7Sderaadt centry->parent = last->parent; 163df930be7Sderaadt centry->prev = last; 164df930be7Sderaadt last = last->next = centry; 165df930be7Sderaadt } 166df930be7Sderaadt } 167df930be7Sderaadt return (root); 168df930be7Sderaadt } 169df930be7Sderaadt 170df930be7Sderaadt static void 171df930be7Sderaadt set(t, ip) 172df930be7Sderaadt char *t; 1730ac0d02eSmpech NODE *ip; 174df930be7Sderaadt { 1750ac0d02eSmpech int type; 17631636a68Smillert char *kw, *val = NULL; 177df930be7Sderaadt struct group *gr; 178df930be7Sderaadt struct passwd *pw; 179*c7b4a4caSotto void *m; 180df930be7Sderaadt int value; 18131636a68Smillert u_int32_t fset, fclr; 182df930be7Sderaadt char *ep; 183e296a845Shenning size_t len; 184df930be7Sderaadt 18546360e3cSderaadt for (; (kw = strtok(t, "= \t\n")); t = NULL) { 186df930be7Sderaadt ip->flags |= type = parsekey(kw, &value); 187df930be7Sderaadt if (value && (val = strtok(NULL, " \t\n")) == NULL) 188642654b6Smillert error("missing value"); 189df930be7Sderaadt switch(type) { 190df930be7Sderaadt case F_CKSUM: 191df930be7Sderaadt ip->cksum = strtoul(val, &ep, 10); 192df930be7Sderaadt if (*ep) 193642654b6Smillert error("invalid checksum %s", val); 194df930be7Sderaadt break; 19546360e3cSderaadt case F_MD5: 19646360e3cSderaadt ip->md5digest = strdup(val); 1971a0a9baeSmillert if (!ip->md5digest) 198642654b6Smillert error("%s", strerror(errno)); 19946360e3cSderaadt break; 20031636a68Smillert case F_FLAGS: 20131636a68Smillert if (!strcmp(val, "none")) { 20231636a68Smillert ip->file_flags = 0; 20331636a68Smillert break; 20431636a68Smillert } 20531636a68Smillert if (strtofflags(&val, &fset, &fclr)) 20631636a68Smillert error("%s", strerror(errno)); 20731636a68Smillert ip->file_flags = fset; 20831636a68Smillert break; 209df930be7Sderaadt case F_GID: 210df930be7Sderaadt ip->st_gid = strtoul(val, &ep, 10); 211df930be7Sderaadt if (*ep) 212642654b6Smillert error("invalid gid %s", val); 213df930be7Sderaadt break; 214df930be7Sderaadt case F_GNAME: 215df930be7Sderaadt if ((gr = getgrnam(val)) == NULL) 216642654b6Smillert error("unknown group %s", val); 217df930be7Sderaadt ip->st_gid = gr->gr_gid; 218df930be7Sderaadt break; 219df930be7Sderaadt case F_IGN: 220df930be7Sderaadt /* just set flag bit */ 221df930be7Sderaadt break; 222df930be7Sderaadt case F_MODE: 223df930be7Sderaadt if ((m = setmode(val)) == NULL) 224642654b6Smillert error("invalid file mode %s", val); 225df930be7Sderaadt ip->st_mode = getmode(m, 0); 22653962ae7Smillert free(m); 227df930be7Sderaadt break; 228df930be7Sderaadt case F_NLINK: 229df930be7Sderaadt ip->st_nlink = strtoul(val, &ep, 10); 230df930be7Sderaadt if (*ep) 231642654b6Smillert error("invalid link count %s", val); 232df930be7Sderaadt break; 23337841f9cSmillert case F_RMD160: 23437841f9cSmillert ip->rmd160digest = strdup(val); 23537841f9cSmillert if (!ip->rmd160digest) 236642654b6Smillert error("%s", strerror(errno)); 23737841f9cSmillert break; 2381a0a9baeSmillert case F_SHA1: 2391a0a9baeSmillert ip->sha1digest = strdup(val); 2401a0a9baeSmillert if (!ip->sha1digest) 241642654b6Smillert error("%s", strerror(errno)); 2421a0a9baeSmillert break; 243df930be7Sderaadt case F_SIZE: 24494bb8e67Smillert ip->st_size = strtouq(val, &ep, 10); 245df930be7Sderaadt if (*ep) 246642654b6Smillert error("invalid size %s", val); 247df930be7Sderaadt break; 248df930be7Sderaadt case F_SLINK: 249e296a845Shenning len = strlen(val) + 1; 250e296a845Shenning if ((ip->slink = malloc(len)) == NULL) 251642654b6Smillert error("%s", strerror(errno)); 252e34d4c20Smillert if (strunvis(ip->slink, val) == -1) { 253e34d4c20Smillert fprintf(stderr, 254e34d4c20Smillert "mtree: filename (%s) encoded incorrectly\n", val); 255e296a845Shenning strlcpy(ip->slink, val, len); 256e34d4c20Smillert } 257df930be7Sderaadt break; 258df930be7Sderaadt case F_TIME: 259c9fd28a4Stholo ip->st_mtimespec.tv_sec = strtoul(val, &ep, 10); 260df930be7Sderaadt if (*ep != '.') 261642654b6Smillert error("invalid time %s", val); 262df930be7Sderaadt val = ep + 1; 263c9fd28a4Stholo ip->st_mtimespec.tv_nsec = strtoul(val, &ep, 10); 264df930be7Sderaadt if (*ep) 265642654b6Smillert error("invalid time %s", val); 266df930be7Sderaadt break; 267df930be7Sderaadt case F_TYPE: 268df930be7Sderaadt switch(*val) { 269df930be7Sderaadt case 'b': 270df930be7Sderaadt if (!strcmp(val, "block")) 271df930be7Sderaadt ip->type = F_BLOCK; 272df930be7Sderaadt break; 273df930be7Sderaadt case 'c': 274df930be7Sderaadt if (!strcmp(val, "char")) 275df930be7Sderaadt ip->type = F_CHAR; 276df930be7Sderaadt break; 277df930be7Sderaadt case 'd': 278df930be7Sderaadt if (!strcmp(val, "dir")) 279df930be7Sderaadt ip->type = F_DIR; 280df930be7Sderaadt break; 281df930be7Sderaadt case 'f': 282df930be7Sderaadt if (!strcmp(val, "file")) 283df930be7Sderaadt ip->type = F_FILE; 284df930be7Sderaadt if (!strcmp(val, "fifo")) 285df930be7Sderaadt ip->type = F_FIFO; 286df930be7Sderaadt break; 287df930be7Sderaadt case 'l': 288df930be7Sderaadt if (!strcmp(val, "link")) 289df930be7Sderaadt ip->type = F_LINK; 290df930be7Sderaadt break; 291df930be7Sderaadt case 's': 292df930be7Sderaadt if (!strcmp(val, "socket")) 293df930be7Sderaadt ip->type = F_SOCK; 294df930be7Sderaadt break; 295df930be7Sderaadt default: 296642654b6Smillert error("unknown file type %s", val); 297df930be7Sderaadt } 298df930be7Sderaadt break; 299df930be7Sderaadt case F_UID: 300df930be7Sderaadt ip->st_uid = strtoul(val, &ep, 10); 301df930be7Sderaadt if (*ep) 302642654b6Smillert error("invalid uid %s", val); 303df930be7Sderaadt break; 304df930be7Sderaadt case F_UNAME: 305df930be7Sderaadt if ((pw = getpwnam(val)) == NULL) 306642654b6Smillert error("unknown user %s", val); 307df930be7Sderaadt ip->st_uid = pw->pw_uid; 308df930be7Sderaadt break; 309df930be7Sderaadt } 310df930be7Sderaadt } 311df930be7Sderaadt } 312df930be7Sderaadt 313df930be7Sderaadt static void 314df930be7Sderaadt unset(t, ip) 315df930be7Sderaadt char *t; 3160ac0d02eSmpech NODE *ip; 317df930be7Sderaadt { 3180ac0d02eSmpech char *p; 319df930be7Sderaadt 32046360e3cSderaadt while ((p = strtok(t, "\n\t "))) 321df930be7Sderaadt ip->flags &= ~parsekey(p, NULL); 322df930be7Sderaadt } 323