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