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