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