Fix up mix of man(7)/mdoc(7).
[netbsd-mini2440.git] / usr.bin / rdist / expand.c
blobb1b2ef2d8424275908cc279859f6c768b84e13ad
1 /* $NetBSD: expand.c,v 1.17 2009/04/13 04:35:36 lukem Exp $ */
3 /*
4 * Copyright (c) 1983, 1993
5 * The Regents of the University of California. All rights reserved.
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. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
32 #include <sys/cdefs.h>
33 #ifndef lint
34 #if 0
35 static char sccsid[] = "@(#)expand.c 8.1 (Berkeley) 6/9/93";
36 #else
37 __RCSID("$NetBSD: expand.c,v 1.17 2009/04/13 04:35:36 lukem Exp $");
38 #endif
39 #endif /* not lint */
41 #include <sys/types.h>
43 #include <errno.h>
44 #include <pwd.h>
46 #include "defs.h"
48 #define GAVSIZ NCARGS / 6
49 #define LC '{'
50 #define RC '}'
52 static char shchars[] = "${[*?";
54 int which; /* bit mask of types to expand */
55 int eargc; /* expanded arg count */
56 char **eargv; /* expanded arg vectors */
57 char *path;
58 char *pathp;
59 char *lastpathp;
60 const char *tilde; /* "~user" if not expanding tilde, else "" */
61 char *tpathp;
62 int nleft;
64 int expany; /* any expansions done? */
65 char *entp;
66 char **sortbase;
68 #define sort() qsort((char *)sortbase, &eargv[eargc] - sortbase, \
69 sizeof(*sortbase), argcmp), sortbase = &eargv[eargc]
71 static void Cat(const char *, const char *);
72 static void addpath(int);
73 static int amatch(char *, char *);
74 static int argcmp(const void *, const void *);
75 static int execbrc(char *, char *);
76 static void expsh(char *);
77 static void expstr(char *);
78 static int match(char *, char *);
79 static void matchdir(char *);
82 * Take a list of names and expand any macros, etc.
83 * wh = E_VARS if expanding variables.
84 * wh = E_SHELL if expanding shell characters.
85 * wh = E_TILDE if expanding `~'.
86 * or any of these or'ed together.
88 * Major portions of this were snarfed from csh/sh.glob.c.
90 struct namelist *
91 expand(struct namelist *list, int wh)
93 struct namelist *nl, *prev;
94 int n;
95 char pathbuf[BUFSIZ];
96 char *argvbuf[GAVSIZ];
98 if (debug) {
99 printf("expand(%lx, %d)\nlist = ", (long)list, wh);
100 prnames(list);
103 if (wh == 0) {
104 char *cp;
106 for (nl = list; nl != NULL; nl = nl->n_next)
107 for (cp = nl->n_name; *cp; cp++)
108 *cp = *cp & TRIM;
109 return(list);
112 which = wh;
113 path = tpathp = pathp = pathbuf;
114 *pathp = '\0';
115 lastpathp = &path[sizeof pathbuf - 2];
116 tilde = "";
117 eargc = 0;
118 eargv = sortbase = argvbuf;
119 *eargv = 0;
120 nleft = NCARGS - 4;
122 * Walk the name list and expand names into eargv[];
124 for (nl = list; nl != NULL; nl = nl->n_next)
125 expstr(nl->n_name);
127 * Take expanded list of names from eargv[] and build a new list.
129 list = prev = NULL;
130 for (n = 0; n < eargc; n++) {
131 nl = makenl(NULL);
132 nl->n_name = eargv[n];
133 if (prev == NULL)
134 list = prev = nl;
135 else {
136 prev->n_next = nl;
137 prev = nl;
140 if (debug) {
141 printf("expanded list = ");
142 prnames(list);
144 return(list);
147 static void
148 expstr(char *s)
150 char *cp, *cp1;
151 struct namelist *tp;
152 char *tail;
153 char expbuf[BUFSIZ];
154 int savec, oeargc;
155 extern char homedir[];
157 if (s == NULL || *s == '\0')
158 return;
160 if ((which & E_VARS) && (cp = strchr(s, '$')) != NULL) {
161 *cp++ = '\0';
162 if (*cp == '\0') {
163 yyerror("no variable name after '$'");
164 return;
166 if (*cp == LC) {
167 cp++;
168 if ((tail = strchr(cp, RC)) == NULL) {
169 yyerror("unmatched '{'");
170 return;
172 *tail++ = savec = '\0';
173 if (*cp == '\0') {
174 yyerror("no variable name after '$'");
175 return;
177 } else {
178 tail = cp + 1;
179 savec = *tail;
180 *tail = '\0';
182 tp = lookup(cp, 0, 0);
183 if (savec != '\0')
184 *tail = savec;
185 if (tp != NULL) {
186 for (; tp != NULL; tp = tp->n_next) {
187 snprintf(expbuf, sizeof(expbuf), "%s%s%s", s,
188 tp->n_name, tail);
189 expstr(expbuf);
191 return;
193 snprintf(expbuf, sizeof(expbuf), "%s%s", s, tail);
194 expstr(expbuf);
195 return;
197 if ((which & ~E_VARS) == 0 || !strcmp(s, "{") || !strcmp(s, "{}")) {
198 Cat(s, "");
199 sort();
200 return;
202 if (*s == '~') {
203 cp = ++s;
204 if (*cp == '\0' || *cp == '/') {
205 tilde = "~";
206 cp1 = homedir;
207 } else {
208 tilde = cp1 = expbuf;
209 *cp1++ = '~';
211 *cp1++ = *cp++;
212 while (*cp && *cp != '/');
213 *cp1 = '\0';
214 if (pw == NULL || strcmp(pw->pw_name, expbuf+1) != 0) {
215 if ((pw = getpwnam(expbuf+1)) == NULL) {
216 strlcat(expbuf, ": unknown user name",
217 sizeof(expbuf));
218 yyerror(expbuf+1);
219 return;
222 cp1 = pw->pw_dir;
223 s = cp;
225 for (cp = path; (*cp++ = *cp1++) != 0; )
227 tpathp = pathp = cp - 1;
228 } else {
229 tpathp = pathp = path;
230 tilde = "";
232 *pathp = '\0';
233 if (!(which & E_SHELL)) {
234 if (which & E_TILDE)
235 Cat(path, s);
236 else
237 Cat(tilde, s);
238 sort();
239 return;
241 oeargc = eargc;
242 expany = 0;
243 expsh(s);
244 if (eargc == oeargc)
245 Cat(s, ""); /* "nonomatch" is set */
246 sort();
249 static int
250 argcmp(const void *a1, const void *a2)
253 return (strcmp(*(const char * const *)a1, *(const char * const *)a2));
257 * If there are any Shell meta characters in the name,
258 * expand into a list, after searching directory
260 static void
261 expsh(char *s)
263 char *cp;
264 char *spathp, *oldcp;
265 struct stat stb;
267 spathp = pathp;
268 cp = s;
269 while (!any(*cp, shchars)) {
270 if (*cp == '\0') {
271 if (!expany || stat(path, &stb) >= 0) {
272 if (which & E_TILDE)
273 Cat(path, "");
274 else
275 Cat(tilde, tpathp);
277 goto endit;
279 addpath(*cp++);
281 oldcp = cp;
282 while (cp > s && *cp != '/')
283 cp--, pathp--;
284 if (*cp == '/')
285 cp++, pathp++;
286 *pathp = '\0';
287 if (*oldcp == '{') {
288 execbrc(cp, NULL);
289 return;
291 matchdir(cp);
292 endit:
293 pathp = spathp;
294 *pathp = '\0';
297 static void
298 matchdir(char *pattern)
300 struct stat stb;
301 struct dirent *dp;
302 DIR *dirp;
304 dirp = opendir(path);
305 if (dirp == NULL) {
306 if (expany)
307 return;
308 goto patherr2;
310 if (fstat(dirp->dd_fd, &stb) < 0)
311 goto patherr1;
312 if (!S_ISDIR(stb.st_mode)) {
313 errno = ENOTDIR;
314 goto patherr1;
316 while ((dp = readdir(dirp)) != NULL)
317 if (match(dp->d_name, pattern)) {
318 if (which & E_TILDE)
319 Cat(path, dp->d_name);
320 else {
321 strcpy(pathp, dp->d_name);
322 Cat(tilde, tpathp);
323 *pathp = '\0';
326 closedir(dirp);
327 return;
329 patherr1:
330 closedir(dirp);
331 patherr2:
332 strcat(path, ": ");
333 strcat(path, strerror(errno));
334 yyerror(path);
337 static int
338 execbrc(char *p, char *s)
340 char restbuf[BUFSIZ + 2];
341 char *pe, *pm, *pl;
342 int brclev = 0;
343 char *lm, savec, *spathp;
345 for (lm = restbuf; *p != '{'; *lm++ = *p++)
346 continue;
347 for (pe = ++p; *pe; pe++)
348 switch (*pe) {
350 case '{':
351 brclev++;
352 continue;
354 case '}':
355 if (brclev == 0)
356 goto pend;
357 brclev--;
358 continue;
360 case '[':
361 for (pe++; *pe && *pe != ']'; pe++)
362 continue;
363 if (!*pe)
364 yyerror("Missing ']'");
365 continue;
367 pend:
368 if (brclev || !*pe) {
369 yyerror("Missing '}'");
370 return (0);
372 for (pl = pm = p; pm <= pe; pm++)
373 switch (*pm & (QUOTE|TRIM)) {
375 case '{':
376 brclev++;
377 continue;
379 case '}':
380 if (brclev) {
381 brclev--;
382 continue;
384 goto doit;
386 case ',':
387 if (brclev)
388 continue;
389 doit:
390 savec = *pm;
391 *pm = 0;
392 strlcpy(lm, pl, sizeof(restbuf) - (lm - restbuf));
393 strlcat(restbuf, pe + 1, sizeof(restbuf));
394 *pm = savec;
395 if (s == 0) {
396 spathp = pathp;
397 expsh(restbuf);
398 pathp = spathp;
399 *pathp = 0;
400 } else if (amatch(s, restbuf))
401 return (1);
402 sort();
403 pl = pm + 1;
404 continue;
406 case '[':
407 for (pm++; *pm && *pm != ']'; pm++)
408 continue;
409 if (!*pm)
410 yyerror("Missing ']'");
411 continue;
413 return (0);
416 static int
417 match(char *s, char *p)
419 int c;
420 char *sentp;
421 char sexpany = expany;
423 if (*s == '.' && *p != '.')
424 return (0);
425 sentp = entp;
426 entp = s;
427 c = amatch(s, p);
428 entp = sentp;
429 expany = sexpany;
430 return (c);
433 static int
434 amatch(char *s, char *p)
436 int scc;
437 int ok, lc;
438 char *spathp;
439 struct stat stb;
440 int c, cc;
442 expany = 1;
443 for (;;) {
444 scc = *s++ & TRIM;
445 switch (c = *p++) {
447 case '{':
448 return (execbrc(p - 1, s - 1));
450 case '[':
451 ok = 0;
452 lc = 077777;
453 while ((cc = *p++) != 0) {
454 if (cc == ']') {
455 if (ok)
456 break;
457 return (0);
459 if (cc == '-') {
460 if (lc <= scc && scc <= *p++)
461 ok++;
462 } else
463 if (scc == (lc = cc))
464 ok++;
466 if (cc == 0) {
467 yyerror("Missing ']'");
468 return (0);
470 continue;
472 case '*':
473 if (!*p)
474 return (1);
475 if (*p == '/') {
476 p++;
477 goto slash;
479 for (s--; *s; s++)
480 if (amatch(s, p))
481 return (1);
482 return (0);
484 case '\0':
485 return (scc == '\0');
487 default:
488 if ((c & TRIM) != scc)
489 return (0);
490 continue;
492 case '?':
493 if (scc == '\0')
494 return (0);
495 continue;
497 case '/':
498 if (scc)
499 return (0);
500 slash:
501 s = entp;
502 spathp = pathp;
503 while (*s)
504 addpath(*s++);
505 addpath('/');
506 if (stat(path, &stb) == 0 && S_ISDIR(stb.st_mode)) {
507 if (*p == '\0') {
508 if (which & E_TILDE)
509 Cat(path, "");
510 else
511 Cat(tilde, tpathp);
512 } else
513 expsh(p);
515 pathp = spathp;
516 *pathp = '\0';
517 return (0);
522 static void
523 Cat(const char *s1, const char *s2)
525 int len = strlen(s1) + strlen(s2) + 1;
526 char *s;
528 nleft -= len;
529 if (nleft <= 0 || ++eargc >= GAVSIZ)
530 yyerror("Arguments too long");
531 eargv[eargc] = 0;
532 eargv[eargc - 1] = s = malloc(len);
533 if (s == NULL)
534 fatal("ran out of memory\n");
535 while ((*s++ = *s1++ & TRIM) != 0)
537 s--;
538 while ((*s++ = *s2++ & TRIM) != 0)
542 static void
543 addpath(int c)
546 if (pathp >= lastpathp)
547 yyerror("Pathname too long");
548 else {
549 *pathp++ = c & TRIM;
550 *pathp = '\0';
555 * Expand file names beginning with `~' into the
556 * user's home directory path name. Return a pointer in buf to the
557 * part corresponding to `file'.
559 char *
560 exptilde(char *expbuf, char *file)
562 char *s1, *s2, *s3;
563 extern char homedir[];
565 if (*file != '~') {
566 strcpy(expbuf, file);
567 return(expbuf);
569 if (*++file == '\0') {
570 s2 = homedir;
571 s3 = NULL;
572 } else if (*file == '/') {
573 s2 = homedir;
574 s3 = file;
575 } else {
576 s3 = file;
577 while (*s3 && *s3 != '/')
578 s3++;
579 if (*s3 == '/')
580 *s3 = '\0';
581 else
582 s3 = NULL;
583 if (pw == NULL || strcmp(pw->pw_name, file) != 0) {
584 if ((pw = getpwnam(file)) == NULL) {
585 error("%s: unknown user name\n", file);
586 if (s3 != NULL)
587 *s3 = '/';
588 return(NULL);
591 if (s3 != NULL)
592 *s3 = '/';
593 s2 = pw->pw_dir;
595 for (s1 = expbuf; (*s1++ = *s2++) != 0; )
597 s2 = --s1;
598 if (s3 != NULL) {
599 s2++;
600 while ((*s1++ = *s3++) != 0)
603 return(s2);