8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / cmd-inet / usr.bin / rdist / expand.c
blobac8f871eec5e18cfc18156826572f5e5090c591d
1 /*
2 * Copyright (c) 1983 Regents of the University of California.
3 * All rights reserved.
5 * Redistribution and use in source and binary forms are permitted
6 * provided that the above copyright notice and this paragraph are
7 * duplicated in all such forms and that any documentation,
8 * advertising materials, and other materials related to such
9 * distribution and use acknowledge that the software was developed
10 * by the University of California, Berkeley. The name of the
11 * University may not be used to endorse or promote products derived
12 * from this software without specific prior written permission.
14 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
15 * Use is subject to license terms.
17 #pragma ident "%Z%%M% %I% %E% SMI"
19 #include "defs.h"
20 #include <string.h>
22 #define GAVSIZ NCARGS / 6
23 #define LC '{'
24 #define RC '}'
26 static char shchars[] = "${[*?";
28 int which; /* bit mask of types to expand */
29 int eargc; /* expanded arg count */
30 char **eargv; /* expanded arg vectors */
31 char *path;
32 char *pathp;
33 char *lastpathp;
34 char *tilde; /* "~user" if not expanding tilde, else "" */
35 char *tpathp;
36 int nleft;
38 int expany; /* any expansions done? */
39 char *entp;
40 char **sortbase;
42 char *index();
44 static int argcmp(const void *arg1, const void *arg2);
45 static void addpath(char c);
46 static void Cat(char *s1, char *s2);
47 static void matchdir(char *pattern);
48 static void expsh(char *s);
49 static void expstr(char *s);
50 static int execbrc(char *p, char *s);
52 #define sort() qsort((char *)sortbase, &eargv[eargc] - sortbase, \
53 sizeof (*sortbase), argcmp), sortbase = &eargv[eargc]
55 #define MIN(a, b) ((a) < (b) ? (a) : (b))
58 * Take a list of names and expand any macros, etc.
59 * wh = E_VARS if expanding variables.
60 * wh = E_SHELL if expanding shell characters.
61 * wh = E_TILDE if expanding `~'.
62 * or any of these or'ed together.
64 * Major portions of this were snarfed from csh/sh.glob.c.
66 struct namelist *
67 expand(list, wh)
68 struct namelist *list;
69 int wh;
71 register struct namelist *nl, *prev;
72 register int n;
73 char pathbuf[LINESIZE];
74 char *argvbuf[GAVSIZ];
76 if (debug) {
77 printf("expand(%x, %d)\nlist = ", list, wh);
78 prnames(list);
81 if (wh == 0) {
82 register char *cp;
84 for (nl = list; nl != NULL; nl = nl->n_next)
85 for (cp = nl->n_name; *cp; cp++)
86 *cp = *cp & TRIM;
87 return (list);
90 which = wh;
91 path = tpathp = pathp = pathbuf;
92 *pathp = '\0';
93 lastpathp = &path[sizeof pathbuf - 2];
94 tilde = "";
95 eargc = 0;
96 eargv = sortbase = argvbuf;
97 *eargv = 0;
98 nleft = NCARGS - 4;
100 * Walk the name list and expand names into eargv[];
102 for (nl = list; nl != NULL; nl = nl->n_next)
103 expstr(nl->n_name);
105 * Take expanded list of names from eargv[] and build a new list.
107 list = prev = NULL;
108 for (n = 0; n < eargc; n++) {
109 nl = makenl(NULL);
110 nl->n_name = eargv[n];
111 if (prev == NULL)
112 list = prev = nl;
113 else {
114 prev->n_next = nl;
115 prev = nl;
118 if (debug) {
119 printf("expanded list = ");
120 prnames(list);
122 return (list);
125 static void
126 expstr(s)
127 char *s;
129 register char *cp, *cp1;
130 register struct namelist *tp;
131 char *tail;
132 char buf[LINESIZE];
133 int savec, oeargc;
134 extern char homedir[];
136 if (s == NULL || *s == '\0')
137 return;
139 if ((which & E_VARS) && (cp = index(s, '$')) != NULL) {
140 *cp++ = '\0';
141 if (*cp == '\0') {
142 yyerror("no variable name after '$'");
143 return;
145 if (*cp == LC) {
146 cp++;
147 if ((tail = index(cp, RC)) == NULL) {
148 yyerror("unmatched '{'");
149 return;
151 *tail++ = savec = '\0';
152 if (*cp == '\0') {
153 yyerror("no variable name after '$'");
154 return;
156 } else {
157 tail = cp + 1;
158 savec = *tail;
159 *tail = '\0';
161 tp = lookup(cp, NULL, 0);
162 if (savec != '\0')
163 *tail = savec;
164 if (tp != NULL) {
165 for (; tp != NULL; tp = tp->n_next) {
166 (void) snprintf(buf, sizeof (buf), "%s%s%s", s,
167 tp->n_name, tail);
168 expstr(buf);
170 return;
172 (void) snprintf(buf, sizeof (buf), "%s%s", s, tail);
173 expstr(buf);
174 return;
176 if ((which & ~E_VARS) == 0 || !strcmp(s, "{") || !strcmp(s, "{}")) {
177 Cat(s, "");
178 sort();
179 return;
181 if (*s == '~') {
182 cp = ++s;
183 if (*cp == '\0' || *cp == '/') {
184 tilde = "~";
185 cp1 = homedir;
186 } else {
187 tilde = cp1 = buf;
188 *cp1++ = '~';
189 do {
190 if (cp1 >= &buf[sizeof (buf)]) {
191 yyerror("User name too long");
192 return;
194 *cp1++ = *cp++;
195 } while (*cp && *cp != '/');
196 *cp1 = '\0';
197 if (pw == NULL || strcmp(pw->pw_name, buf+1) != 0) {
198 if ((pw = getpwnam(buf+1)) == NULL) {
199 static char unknown_user[] =
200 ": unknown user name";
202 cp1 = MIN(cp1,
203 &buf[sizeof (buf)] -
204 sizeof (unknown_user));
205 strcpy(cp1, unknown_user);
206 yyerror(buf+1);
207 return;
210 cp1 = pw->pw_dir;
211 s = cp;
213 for (cp = path; cp <= lastpathp + 1 && (*cp++ = *cp1++); )
215 tpathp = pathp = cp - 1;
216 } else {
217 tpathp = pathp = path;
218 tilde = "";
220 *pathp = '\0';
221 if (!(which & E_SHELL)) {
222 if (which & E_TILDE)
223 Cat(path, s);
224 else
225 Cat(tilde, s);
226 sort();
227 return;
229 oeargc = eargc;
230 expany = 0;
231 expsh(s);
232 if (eargc == oeargc)
233 Cat(s, ""); /* "nonomatch" is set */
234 sort();
237 static int
238 argcmp(const void *arg1, const void *arg2)
240 char *a1 = *(char **)arg1;
241 char *a2 = *(char **)arg2;
243 return (strcmp(a1, a2));
247 * If there are any Shell meta characters in the name,
248 * expand into a list, after searching directory
250 static void
251 expsh(s)
252 char *s;
254 register char *cp;
255 register char *spathp, *oldcp;
256 struct stat stb;
258 spathp = pathp;
259 cp = s;
260 while (!any(*cp, shchars)) {
261 if (*cp == '\0') {
262 if (!expany || stat(path, &stb) >= 0) {
263 if (which & E_TILDE)
264 Cat(path, "");
265 else
266 Cat(tilde, tpathp);
268 goto endit;
270 addpath(*cp++);
272 oldcp = cp;
273 while (cp > s && *cp != '/')
274 cp--, pathp--;
275 if (*cp == '/')
276 cp++, pathp++;
277 *pathp = '\0';
278 if (*oldcp == '{') {
279 execbrc(cp, NULL);
280 return;
282 matchdir(cp);
283 endit:
284 pathp = spathp;
285 *pathp = '\0';
288 static void
289 matchdir(pattern)
290 char *pattern;
292 struct stat stb;
293 register struct dirent *dp;
294 DIR *dirp;
296 dirp = opendir(path);
297 if (dirp == NULL) {
298 if (expany)
299 return;
300 goto patherr2;
302 if (fstat(dirp->dd_fd, &stb) < 0)
303 goto patherr1;
304 if (!ISDIR(stb.st_mode)) {
305 errno = ENOTDIR;
306 goto patherr1;
308 while ((dp = readdir(dirp)) != NULL)
309 if (match(dp->d_name, pattern)) {
310 if (which & E_TILDE)
311 Cat(path, dp->d_name);
312 else {
313 if (pathp + strlen(dp->d_name) - 1 >
314 lastpathp) {
315 errno = ENAMETOOLONG;
316 goto patherr1;
318 strcpy(pathp, dp->d_name);
319 Cat(tilde, tpathp);
320 *pathp = '\0';
323 closedir(dirp);
324 return;
326 patherr1:
327 closedir(dirp);
328 patherr2:
330 char *strerr = strerror(errno);
332 if (path + strlen(path) + strlen(strerr) + 1 > lastpathp)
333 strcpy(lastpathp - strlen(strerr) - 1, ": ");
334 else
335 strcat(path, ": ");
336 strcat(path, strerr);
338 yyerror(path);
341 static int
342 execbrc(p, s)
343 char *p, *s;
345 char restbuf[LINESIZE + 2];
346 register char *pe, *pm, *pl;
347 int brclev = 0;
348 char *lm, savec, *spathp;
350 for (lm = restbuf; *p != '{'; *lm++ = *p++) {
351 if (lm >= &restbuf[sizeof (restbuf)]) {
352 yyerror("Pathname too long");
353 return (0);
356 for (pe = ++p; *pe; pe++)
357 switch (*pe) {
359 case '{':
360 brclev++;
361 continue;
363 case '}':
364 if (brclev == 0)
365 goto pend;
366 brclev--;
367 continue;
369 case '[':
370 for (pe++; *pe && *pe != ']'; pe++)
371 continue;
372 if (!*pe)
373 yyerror("Missing ']'");
374 continue;
376 pend:
377 if (brclev || !*pe) {
378 yyerror("Missing '}'");
379 return (0);
381 for (pl = pm = p; pm <= pe; pm++)
382 switch (*pm & (QUOTE|TRIM)) {
384 case '{':
385 brclev++;
386 continue;
388 case '}':
389 if (brclev) {
390 brclev--;
391 continue;
393 goto doit;
395 case ',':
396 if (brclev)
397 continue;
398 doit:
399 savec = *pm;
400 *pm = 0;
401 if (lm + strlen(pl) + strlen(pe + 1) >=
402 &restbuf[sizeof (restbuf)]) {
403 yyerror("Pathname too long");
404 return (0);
406 strcpy(lm, pl);
407 strcat(restbuf, pe + 1);
408 *pm = savec;
409 if (s == 0) {
410 spathp = pathp;
411 expsh(restbuf);
412 pathp = spathp;
413 *pathp = 0;
414 } else if (amatch(s, restbuf))
415 return (1);
416 sort();
417 pl = pm + 1;
418 continue;
420 case '[':
421 for (pm++; *pm && *pm != ']'; pm++)
422 continue;
423 if (!*pm)
424 yyerror("Missing ']'");
425 continue;
427 return (0);
431 match(s, p)
432 char *s, *p;
434 register int c;
435 register char *sentp;
436 char sexpany = expany;
438 if (*s == '.' && *p != '.')
439 return (0);
440 sentp = entp;
441 entp = s;
442 c = amatch(s, p);
443 entp = sentp;
444 expany = sexpany;
445 return (c);
449 amatch(s, p)
450 register char *s, *p;
452 register int scc;
453 int ok, lc;
454 char *spathp;
455 struct stat stb;
456 int c, cc;
458 expany = 1;
459 for (;;) {
460 scc = *s++ & TRIM;
461 switch (c = *p++) {
463 case '{':
464 return (execbrc(p - 1, s - 1));
466 case '[':
467 ok = 0;
468 lc = 077777;
469 while (cc = *p++) {
470 if (cc == ']') {
471 if (ok)
472 break;
473 return (0);
475 if (cc == '-') {
476 if (lc <= scc && scc <= *p++)
477 ok++;
478 } else
479 if (scc == (lc = cc))
480 ok++;
482 if (cc == 0) {
483 yyerror("Missing ']'");
484 return (0);
486 continue;
488 case '*':
489 if (!*p)
490 return (1);
491 if (*p == '/') {
492 p++;
493 goto slash;
495 for (s--; *s; s++)
496 if (amatch(s, p))
497 return (1);
498 return (0);
500 case '\0':
501 return (scc == '\0');
503 default:
504 if ((c & TRIM) != scc)
505 return (0);
506 continue;
508 case '?':
509 if (scc == '\0')
510 return (0);
511 continue;
513 case '/':
514 if (scc)
515 return (0);
516 slash:
517 s = entp;
518 spathp = pathp;
519 while (*s)
520 addpath(*s++);
521 addpath('/');
522 if (stat(path, &stb) == 0 && ISDIR(stb.st_mode))
523 if (*p == '\0') {
524 if (which & E_TILDE)
525 Cat(path, "");
526 else
527 Cat(tilde, tpathp);
528 } else
529 expsh(p);
530 pathp = spathp;
531 *pathp = '\0';
532 return (0);
538 smatch(s, p)
539 register char *s, *p;
541 register int scc;
542 int ok, lc;
543 int c, cc;
545 for (;;) {
546 scc = *s++ & TRIM;
547 switch (c = *p++) {
549 case '[':
550 ok = 0;
551 lc = 077777;
552 while (cc = *p++) {
553 if (cc == ']') {
554 if (ok)
555 break;
556 return (0);
558 if (cc == '-') {
559 if (lc <= scc && scc <= *p++)
560 ok++;
561 } else
562 if (scc == (lc = cc))
563 ok++;
565 if (cc == 0) {
566 yyerror("Missing ']'");
567 return (0);
569 continue;
571 case '*':
572 if (!*p)
573 return (1);
574 for (s--; *s; s++)
575 if (smatch(s, p))
576 return (1);
577 return (0);
579 case '\0':
580 return (scc == '\0');
582 default:
583 if ((c & TRIM) != scc)
584 return (0);
585 continue;
587 case '?':
588 if (scc == 0)
589 return (0);
590 continue;
596 static void
597 Cat(s1, s2)
598 register char *s1, *s2;
600 int len = strlen(s1) + strlen(s2) + 1;
601 register char *s;
603 nleft -= len;
604 if (nleft <= 0 || ++eargc >= GAVSIZ)
605 fatal("Arguments too long\n");
606 eargv[eargc] = 0;
607 eargv[eargc - 1] = s = (char *)malloc(len);
608 if (s == NULL)
609 fatal("ran out of memory\n");
610 while (*s++ = *s1++ & TRIM)
612 s--;
613 while (*s++ = *s2++ & TRIM)
617 static void
618 addpath(char c)
621 if (pathp > lastpathp)
622 yyerror("Pathname too long");
623 else {
624 *pathp++ = c & TRIM;
625 *pathp = '\0';
630 * Expand file names beginning with `~' into the
631 * user's home directory path name. Return a pointer in buf to the
632 * part corresponding to `file'.
634 char *
635 exptilde(buf, len, file)
636 char buf[];
637 unsigned int len;
638 register char *file;
640 register char *s1, *s2, *s3;
641 extern char homedir[];
643 if (*file != '~') {
644 if (strlen(file) + 1 > len) {
645 error("pathname too long: %s\n", file);
646 return (NULL);
648 strcpy(buf, file);
649 return (buf);
651 if (*++file == '\0') {
652 s2 = homedir;
653 s3 = NULL;
654 } else if (*file == '/') {
655 s2 = homedir;
656 s3 = file;
657 } else {
658 s3 = file;
659 while (*s3 && *s3 != '/')
660 s3++;
661 if (*s3 == '/')
662 *s3 = '\0';
663 else
664 s3 = NULL;
665 if (pw == NULL || strcmp(pw->pw_name, file) != 0) {
666 if ((pw = getpwnam(file)) == NULL) {
667 error("%s: unknown user name\n", file);
668 if (s3 != NULL)
669 *s3 = '/';
670 return (NULL);
673 if (s3 != NULL)
674 *s3 = '/';
675 s2 = pw->pw_dir;
677 for (s1 = buf; s1 < &buf[len] && (*s1++ = *s2++); )
679 s2 = --s1;
680 if (s3 != NULL) {
681 s2++;
682 while (s1 < &buf[len] && (*s1++ = *s3++))
685 if (s1 == &buf[len]) {
686 error("pathname too long: %s\n", file - 1);
687 return (NULL);
689 return (s2);