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