Correct BSD License clause numbering from 1-2-4 to 1-2-3.
[dragonfly.git] / usr.sbin / mtree / spec.c
1 /*-
2  * Copyright (c) 1989, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * @(#)spec.c   8.1 (Berkeley) 6/6/93
30  * $FreeBSD: src/usr.sbin/mtree/spec.c,v 1.13.2.1 2000/06/28 02:33:17 joe Exp $
31  * $DragonFly: src/usr.sbin/mtree/spec.c,v 1.6 2004/08/30 19:27:22 eirikn Exp $
32  */
33
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <ctype.h>
37 #include <err.h>
38 #include <errno.h>
39 #include <fts.h>
40 #include <grp.h>
41 #include <pwd.h>
42 #include <stdio.h>
43 #include <unistd.h>
44 #include <vis.h>
45 #include "mtree.h"
46 #include "extern.h"
47
48 int lineno;                             /* Current spec line number. */
49
50 static void      set(char *, NODE *);
51 static void      unset(char *, NODE *);
52
53 NODE *
54 spec(void)
55 {
56         NODE *centry, *last;
57         char *p;
58         NODE ginfo, *root;
59         int c_cur, c_next;
60         char buf[2048];
61
62         centry = last = root = NULL;
63         bzero(&ginfo, sizeof(ginfo));
64         c_cur = c_next = 0;
65         for (lineno = 1; fgets(buf, sizeof(buf), stdin);
66             ++lineno, c_cur = c_next, c_next = 0) {
67                 /* Skip empty lines. */
68                 if (buf[0] == '\n')
69                         continue;
70
71                 /* Find end of line. */
72                 if ((p = strchr(buf, '\n')) == NULL)
73                         errx(1, "line %d too long", lineno);
74
75                 /* See if next line is continuation line. */
76                 if (p[-1] == '\\') {
77                         --p;
78                         c_next = 1;
79                 }
80
81                 /* Null-terminate the line. */
82                 *p = '\0';
83
84                 /* Skip leading whitespace. */
85                 for (p = buf; *p && isspace(*p); ++p);
86
87                 /* If nothing but whitespace or comment char, continue. */
88                 if (!*p || *p == '#')
89                         continue;
90
91 #ifdef DEBUG
92                 fprintf(stderr, "line %d: {%s}\n", lineno, p);
93 #endif
94                 if (c_cur) {
95                         set(p, centry);
96                         continue;
97                 }
98
99                 /* Grab file name, "$", "set", or "unset". */
100                 if ((p = strtok(p, "\n\t ")) == NULL)
101                         errx(1, "line %d: missing field", lineno);
102
103                 if (p[0] == '/')
104                         switch(p[1]) {
105                         case 's':
106                                 if (strcmp(p + 1, "set"))
107                                         break;
108                                 set(NULL, &ginfo);
109                                 continue;
110                         case 'u':
111                                 if (strcmp(p + 1, "unset"))
112                                         break;
113                                 unset(NULL, &ginfo);
114                                 continue;
115                         }
116
117                 if (strchr(p, '/'))
118                         errx(1, "line %d: slash character in file name",
119                         lineno);
120
121                 if (!strcmp(p, "..")) {
122                         /* Don't go up, if haven't gone down. */
123                         if (!root)
124                                 goto noparent;
125                         if (last->type != F_DIR || last->flags & F_DONE) {
126                                 if (last == root)
127                                         goto noparent;
128                                 last = last->parent;
129                         }
130                         last->flags |= F_DONE;
131                         continue;
132
133 noparent:               errx(1, "line %d: no parent node", lineno);
134                 }
135
136                 if ((centry = calloc(1, sizeof(NODE) + strlen(p))) == NULL)
137                         errx(1, "calloc");
138                 *centry = ginfo;
139 #define MAGIC   "?*["
140                 if (strpbrk(p, MAGIC))
141                         centry->flags |= F_MAGIC;
142                 if (strunvis(centry->name, p) == -1) {
143                         warnx("filename %s is ill-encoded and literally used",
144                             p);
145                         strcpy(centry->name, p);
146                 }
147                 set(NULL, centry);
148
149                 if (!root) {
150                         last = root = centry;
151                         root->parent = root;
152                 } else if (last->type == F_DIR && !(last->flags & F_DONE)) {
153                         centry->parent = last;
154                         last = last->child = centry;
155                 } else {
156                         centry->parent = last->parent;
157                         centry->prev = last;
158                         last = last->next = centry;
159                 }
160         }
161         return (root);
162 }
163
164 static void
165 set(char *t, NODE *ip)
166 {
167         int type;
168         char *kw, *val = NULL;
169         struct group *gr;
170         struct passwd *pw;
171         mode_t *m;
172         int value;
173         char *ep;
174
175         for (; (kw = strtok(t, "= \t\n")); t = NULL) {
176                 ip->flags |= type = parsekey(kw, &value);
177                 if (value && (val = strtok(NULL, " \t\n")) == NULL)
178                         errx(1, "line %d: missing value", lineno);
179                 switch(type) {
180                 case F_CKSUM:
181                         ip->cksum = strtoul(val, &ep, 10);
182                         if (*ep)
183                                 errx(1, "line %d: invalid checksum %s",
184                                 lineno, val);
185                         break;
186                 case F_MD5:
187                         ip->md5digest = strdup(val);
188                         if(!ip->md5digest) {
189                                 errx(1, "strdup");
190                         }
191                         break;
192                 case F_SHA1:
193                         ip->sha1digest = strdup(val);
194                         if(!ip->sha1digest) {
195                                 errx(1, "strdup");
196                         }
197                         break;
198                 case F_RMD160:
199                         ip->rmd160digest = strdup(val);
200                         if(!ip->rmd160digest) {
201                                 errx(1, "strdup");
202                         }
203                         break;
204                 case F_FLAGS:
205                         if (strcmp("none", val) == 0)
206                                 ip->st_flags = 0;
207                         else if (strtofflags(&val, &ip->st_flags, NULL) != 0)
208                                 errx(1, "line %d: invalid flag %s",lineno, val);
209                         break;
210                 case F_GID:
211                         ip->st_gid = strtoul(val, &ep, 10);
212                         if (*ep)
213                                 errx(1, "line %d: invalid gid %s", lineno, val);
214                         break;
215                 case F_GNAME:
216                         if ((gr = getgrnam(val)) == NULL)
217                             errx(1, "line %d: unknown group %s", lineno, val);
218                         ip->st_gid = gr->gr_gid;
219                         break;
220                 case F_IGN:
221                         /* just set flag bit */
222                         break;
223                 case F_MODE:
224                         if ((m = setmode(val)) == NULL)
225                                 errx(1, "line %d: invalid file mode %s",
226                                 lineno, val);
227                         ip->st_mode = getmode(m, 0);
228                         free(m);
229                         break;
230                 case F_NLINK:
231                         ip->st_nlink = strtoul(val, &ep, 10);
232                         if (*ep)
233                                 errx(1, "line %d: invalid link count %s",
234                                 lineno,  val);
235                         break;
236                 case F_SIZE:
237                         ip->st_size = strtoq(val, &ep, 10);
238                         if (*ep)
239                                 errx(1, "line %d: invalid size %s",
240                                 lineno, val);
241                         break;
242                 case F_SLINK:
243                         if ((ip->slink = strdup(val)) == NULL)
244                                 errx(1, "strdup");
245                         break;
246                 case F_TIME:
247                         ip->st_mtimespec.tv_sec = strtoul(val, &ep, 10);
248                         if (*ep != '.')
249                                 errx(1, "line %d: invalid time %s",
250                                 lineno, val);
251                         val = ep + 1;
252                         ip->st_mtimespec.tv_nsec = strtoul(val, &ep, 10);
253                         if (*ep)
254                                 errx(1, "line %d: invalid time %s",
255                                 lineno, val);
256                         break;
257                 case F_TYPE:
258                         switch(*val) {
259                         case 'b':
260                                 if (!strcmp(val, "block"))
261                                         ip->type = F_BLOCK;
262                                 break;
263                         case 'c':
264                                 if (!strcmp(val, "char"))
265                                         ip->type = F_CHAR;
266                                 break;
267                         case 'd':
268                                 if (!strcmp(val, "dir"))
269                                         ip->type = F_DIR;
270                                 break;
271                         case 'f':
272                                 if (!strcmp(val, "file"))
273                                         ip->type = F_FILE;
274                                 if (!strcmp(val, "fifo"))
275                                         ip->type = F_FIFO;
276                                 break;
277                         case 'l':
278                                 if (!strcmp(val, "link"))
279                                         ip->type = F_LINK;
280                                 break;
281                         case 's':
282                                 if (!strcmp(val, "socket"))
283                                         ip->type = F_SOCK;
284                                 break;
285                         default:
286                                 errx(1, "line %d: unknown file type %s",
287                                 lineno, val);
288                         }
289                         break;
290                 case F_UID:
291                         ip->st_uid = strtoul(val, &ep, 10);
292                         if (*ep)
293                                 errx(1, "line %d: invalid uid %s", lineno, val);
294                         break;
295                 case F_UNAME:
296                         if ((pw = getpwnam(val)) == NULL)
297                             errx(1, "line %d: unknown user %s", lineno, val);
298                         ip->st_uid = pw->pw_uid;
299                         break;
300                 }
301         }
302 }
303
304 static void
305 unset(char *t, NODE *ip)
306 {
307         char *p;
308
309         while ((p = strtok(t, "\n\t ")))
310                 ip->flags &= ~parsekey(p, NULL);
311 }