First stable version.
[unix-history] / usr / src / usr.bin / rdist / expand.c
CommitLineData
ab72ea7b 1#ifndef lint
82572cb6 2static char *sccsid = "@(#)expand.c 4.2 (Berkeley) 83/09/27";
ab72ea7b
RC
3#endif
4
5#include "defs.h"
6
82572cb6 7char shchars[] = "{[*?";
ab72ea7b 8
82572cb6
RC
9int argc;
10char **argv;
11char *path, *pathp, *lastpathp;
12int nleft;
ab72ea7b 13
82572cb6
RC
14int argcnt;
15int expany; /* any expansions done? */
16char *entp;
17char **sortbase;
ab72ea7b
RC
18
19char *index();
20
21/*
82572cb6 22 * Take a list of names and expand any macros, etc.
ab72ea7b
RC
23 */
24struct block *
82572cb6 25expand(list, noshexp)
ab72ea7b 26 struct block *list;
82572cb6 27 int noshexp;
ab72ea7b
RC
28{
29 register struct block *prev, *bp, *tp;
30 register char *cp, *s;
31 register int n;
32 char *var, *tail;
33 int c;
82572cb6
RC
34 char pathbuf[BUFSIZ];
35 char *argvbuf[GAVSIZ];
ab72ea7b
RC
36
37 for (prev = NULL, bp = list; bp != NULL; prev = bp, bp = bp->b_next) {
38 again:
39 cp = index(bp->b_name, '$');
40 if (cp == NULL || cp != bp->b_name && cp[-1] == '\\')
41 continue;
42 *cp++ = '\0';
43 if (*cp == '\0')
44 fatal("no variable name after '$'");
45 if (*cp == '{') {
46 cp++;
47 if ((tail = index(cp, '}')) == NULL)
48 fatal("missing '}'");
49 *tail++ = c = '\0';
50 if (*cp == '\0')
51 fatal("no variable name after '$'");
52 } else {
53 tail = cp + 1;
54 c = *tail;
55 *tail = '\0';
56 }
82572cb6 57 tp = lookup(cp, NULL, 0);
ab72ea7b
RC
58 if ((tp = tp->b_args) != NULL) {
59 struct block *first = tp;
60
61 if (prev == NULL)
62 list = tp;
63 else
64 prev->b_next = tp;
65 if (c)
66 *tail = c;
67 makestr(tp, bp->b_name, tail);
68 while (tp->b_next != NULL) {
69 tp = tp->b_next;
70 makestr(tp, bp->b_name, tail);
71 }
72 tp->b_next = bp->b_next;
73 free(bp->b_name);
74 free(bp);
75 bp = first;
76 goto again;
77 } else {
78 if (prev == NULL)
82572cb6 79 list = tp = bp->b_next;
ab72ea7b
RC
80 else
81 prev->b_next = tp = bp->b_next;
82 free(bp->b_name);
83 free(bp);
84 if (tp != NULL) {
85 bp = tp;
86 goto again;
87 }
88 break;
89 }
90 }
82572cb6
RC
91
92 if (noshexp)
93 return(list);
94
95 path = pathp = pathbuf;
96 *pathp = '\0';
97 lastpathp = &path[sizeof pathbuf - 2];
98 argc = 0;
99 argv = sortbase = argvbuf;
100 *argv = 0;
101 nleft = NCARGS - 4;
102 argcnt = 0;
103 for (bp = list; bp != NULL; bp = bp->b_next)
104 expsh(bp->b_name);
105 for (bp = list; bp != NULL; bp = tp) {
106 tp = bp->b_next;
107 free(bp->b_name);
108 free(bp);
109 }
110 prev = NULL;
111 for (n = 0; n < argc; n++) {
112 bp = ALLOC(block);
113 if (bp == NULL)
114 fatal("ran out of memory\n");
115 bp->b_type = NAME;
116 bp->b_next = bp->b_args = NULL;
117 bp->b_name = argv[n];
118 if (prev == NULL)
119 list = prev = bp;
120 else {
121 prev->b_next = bp;
122 prev = bp;
123 }
124 }
ab72ea7b
RC
125 return(list);
126}
127
128/*
129 * Concat head, bp->b_name, and tail
130 */
131makestr(bp, head, tail)
132 struct block *bp;
133 char *head, *tail;
134{
135 register int n;
136 register char *cp;
137
138 if (!*head && !*tail)
139 return;
140 n = strlen(bp->b_name) + strlen(head) + strlen(tail) + 1;
141 cp = (char *) malloc(n);
142 if (cp == NULL)
143 fatal("ran out of memory");
144 sprintf(cp, "%s%s%s", head, bp->b_name, tail);
145 free(bp->b_name);
146 bp->b_name = cp;
147}
148
149/*
150 * If there are any Shell meta characters in the name,
151 * expand into a list, after searching directory
ab72ea7b 152 */
82572cb6
RC
153expsh(s)
154 register char *s;
155{
156 register int i;
157 register int oargc = argc;
158
159 if (!strcmp(s, "{") || !strcmp(s, "{}")) {
160 Cat(s, "");
161 sort();
162 return;
163 }
164
165 pathp = path;
166 *pathp = 0;
167 expany = 0;
168 expstr(s);
169 if (argc != oargc)
170 sort();
171}
172
173/*
174 * Bubble sort any new entries
175 */
176sort()
177{
178 register char **p1, **p2, *c;
179 char **ap = &argv[argc];
180
181 p1 = sortbase;
182 while (p1 < ap-1) {
183 p2 = p1;
184 while (++p2 < ap)
185 if (strcmp(*p1, *p2) > 0)
186 c = *p1, *p1 = *p2, *p2 = c;
187 p1++;
188 }
189 sortbase = ap;
190}
191
192expstr(s)
193 char *s;
194{
195 register char *cp;
196 register char *spathp, *oldcp;
197 struct stat stb;
198
199 spathp = pathp;
200 cp = s;
201 while (!any(*cp, shchars)) {
202 if (*cp == '\0') {
203 if (!expany)
204 Cat(path, "");
205 else if (stat(path, &stb) >= 0) {
206 Cat(path, "");
207 argcnt++;
208 }
209 goto endit;
210 }
211 addpath(*cp++);
212 }
213 oldcp = cp;
214 while (cp > s && *cp != '/')
215 cp--, pathp--;
216 if (*cp == '/')
217 cp++, pathp++;
218 *pathp = '\0';
219 if (*oldcp == '{') {
220 execbrc(cp, NULL);
221 return;
222 }
223 matchdir(cp);
224endit:
225 pathp = spathp;
226 *pathp = '\0';
227}
228
229matchdir(pattern)
230 char *pattern;
231{
232 struct stat stb;
233 register struct direct *dp;
234 DIR *dirp;
235 register int cnt;
236
237 dirp = opendir(path);
238 if (dirp == NULL) {
239 if (expany)
240 return;
241 goto patherr2;
242 }
243 if (fstat(dirp->dd_fd, &stb) < 0)
244 goto patherr1;
245 if ((stb.st_mode & S_IFMT) != S_IFDIR) {
246 errno = ENOTDIR;
247 goto patherr1;
248 }
249 while ((dp = readdir(dirp)) != NULL)
250 if (match(dp->d_name, pattern)) {
251 Cat(path, dp->d_name);
252 argcnt++;
253 }
254 closedir(dirp);
255 return;
256
257patherr1:
258 closedir(dirp);
259patherr2:
260 fatal("%s: %s\n", path, sys_errlist[errno]);
261}
262
263execbrc(p, s)
264 char *p, *s;
265{
266 char restbuf[BUFSIZ + 2];
267 register char *pe, *pm, *pl;
268 int brclev = 0;
269 char *lm, savec, *spathp;
270
271 for (lm = restbuf; *p != '{'; *lm++ = *p++)
272 continue;
273 for (pe = ++p; *pe; pe++)
274 switch (*pe) {
275
276 case '{':
277 brclev++;
278 continue;
279
280 case '}':
281 if (brclev == 0)
282 goto pend;
283 brclev--;
284 continue;
285
286 case '[':
287 for (pe++; *pe && *pe != ']'; pe++)
288 continue;
289 if (!*pe)
290 fatal("Missing ]\n");
291 continue;
292 }
293pend:
294 if (brclev || !*pe)
295 fatal("Missing }\n");
296 for (pl = pm = p; pm <= pe; pm++)
297 switch (*pm & (QUOTE|TRIM)) {
298
299 case '{':
300 brclev++;
301 continue;
302
303 case '}':
304 if (brclev) {
305 brclev--;
306 continue;
307 }
308 goto doit;
309
310 case ',':
311 if (brclev)
312 continue;
313doit:
314 savec = *pm;
315 *pm = 0;
316 strcpy(lm, pl);
317 strcat(restbuf, pe + 1);
318 *pm = savec;
319 if (s == 0) {
320 spathp = pathp;
321 expstr(restbuf);
322 pathp = spathp;
323 *pathp = 0;
324 } else if (amatch(s, restbuf))
325 return (1);
326 sort();
327 pl = pm + 1;
328 continue;
329
330 case '[':
331 for (pm++; *pm && *pm != ']'; pm++)
332 continue;
333 if (!*pm)
334 fatal("Missing ]\n");
335 continue;
336 }
337 return (0);
338}
339
340match(s, p)
341 char *s, *p;
342{
343 register int c;
344 register char *sentp;
345 char sexpany = expany;
346
347 if (*s == '.' && *p != '.')
348 return (0);
349 sentp = entp;
350 entp = s;
351 c = amatch(s, p);
352 entp = sentp;
353 expany = sexpany;
354 return (c);
355}
356
357amatch(s, p)
358 register char *s, *p;
359{
360 register int scc;
361 int ok, lc;
362 char *spathp;
363 struct stat stb;
364 int c, cc;
365
366 expany = 1;
367 for (;;) {
368 scc = *s++ & TRIM;
369 switch (c = *p++) {
370
371 case '{':
372 return (execbrc(p - 1, s - 1));
373
374 case '[':
375 ok = 0;
376 lc = 077777;
377 while (cc = *p++) {
378 if (cc == ']') {
379 if (ok)
380 break;
381 return (0);
382 }
383 if (cc == '-') {
384 if (lc <= scc && scc <= *p++)
385 ok++;
386 } else
387 if (scc == (lc = cc))
388 ok++;
389 }
390 if (cc == 0)
391 fatal("Missing ]\n");
392 continue;
393
394 case '*':
395 if (!*p)
396 return (1);
397 if (*p == '/') {
398 p++;
399 goto slash;
400 }
401 for (s--; *s; s++)
402 if (amatch(s, p))
403 return (1);
404 return (0);
405
406 case '\0':
407 return (scc == '\0');
408
409 default:
410 if (c != scc)
411 return (0);
412 continue;
413
414 case '?':
415 if (scc == '\0')
416 return (0);
417 continue;
418
419 case '/':
420 if (scc)
421 return (0);
422slash:
423 s = entp;
424 spathp = pathp;
425 while (*s)
426 addpath(*s++);
427 addpath('/');
428 if (stat(path, &stb) == 0 &&
429 (stb.st_mode & S_IFMT) == S_IFDIR)
430 if (*p == '\0') {
431 Cat(path, "");
432 argcnt++;
433 } else
434 expstr(p);
435 pathp = spathp;
436 *pathp = '\0';
437 return (0);
438 }
439 }
440}
441
442smatch(s, p)
443 register char *s, *p;
444{
445 register int scc;
446 int ok, lc;
447 int c, cc;
448
449 for (;;) {
450 scc = *s++ & TRIM;
451 switch (c = *p++) {
452
453 case '[':
454 ok = 0;
455 lc = 077777;
456 while (cc = *p++) {
457 if (cc == ']') {
458 if (ok)
459 break;
460 return (0);
461 }
462 if (cc == '-') {
463 if (lc <= scc && scc <= *p++)
464 ok++;
465 } else
466 if (scc == (lc = cc))
467 ok++;
468 }
469 if (cc == 0)
470 fatal("Missing ]\n");
471 continue;
472
473 case '*':
474 if (!*p)
475 return (1);
476 for (s--; *s; s++)
477 if (smatch(s, p))
478 return (1);
479 return (0);
480
481 case '\0':
482 return (scc == '\0');
483
484 default:
485 if ((c & TRIM) != scc)
486 return (0);
487 continue;
488
489 case '?':
490 if (scc == 0)
491 return (0);
492 continue;
493
494 }
495 }
496}
497
498Cat(s1, s2)
499 register char *s1, *s2;
500{
501 int len = strlen(s1) + strlen(s2) + 1;
502 register char *s, *ep;
503
504 nleft -= len;
505 if (nleft <= 0 || ++argc >= GAVSIZ)
506 fatal("Arguments too long\n");
507 argv[argc] = 0;
508 argv[argc - 1] = s = (char *) malloc(len);
509 if (s == NULL)
510 fatal("ran out of memory\n");
511 while (*s++ = *s1++ & TRIM)
512 ;
513 s--;
514 while (*s++ = *s2++ & TRIM)
515 ;
516}
517
518addpath(c)
519 char c;
520{
521
522 if (pathp >= lastpathp)
523 fatal("Pathname too long\n");
524 *pathp++ = c;
525 *pathp = '\0';
526}
527
528/*
529 * Expand file names beginning with `~' into the
530 * user's home directory path name.
531 */
532exptilde(buf, file)
ab72ea7b
RC
533 char buf[];
534 register char *file;
535{
536 register char *s1, *s2, *s3;
537 register struct passwd *pw;
538 extern char *homedir;
539
540 if (*file != '~') {
541 strcpy(buf, file);
542 return;
543 }
544 file++;
545 if (*file == '\0' || *file == '/') {
546 s2 = homedir;
547 s3 = file;
548 } else {
549 for (s3 = file; *s3 && *s3 != '/'; s3++)
550 ;
551 if (*s3 == '/')
552 *s3 = '\0';
553 else
554 s3 = NULL;
ab72ea7b
RC
555 pw = getpwnam(file);
556 if (pw == NULL) {
82572cb6 557 fatal("unknown user %s\n", file);
ab72ea7b
RC
558 if (s3 != NULL)
559 *s3 = '/';
560 return;
561 }
562 if (s3 != NULL)
563 *s3 = '/';
564 s2 = pw->pw_dir;
565 }
566 for (s1 = buf; *s1++ = *s2++; )
567 ;
568 if (s3 == NULL)
569 return;
570 s1--;
571 while (*s1++ = *s3++)
572 ;
573}