1 /***************************************************************************
2 JSPICE3 adaptation of Spice3e2 - Copyright (c) Stephen R. Whiteley 1992
3 Copyright 1990 Regents of the University of California. All rights reserved.
4 Authors: 1985 Wayne A. Christopher
5 1992 Stephen R. Whiteley
6 ****************************************************************************/
7
8 /*
9 * Expand global characters.
10 */
11
12 #include "spice.h"
13 #include "misc.h"
14 #include "cpdefs.h"
15
16 #include <sys/types.h>
17 #ifdef HAVE_DIRENT_H
18 #include <dirent.h>
19 #ifndef direct
20 #define direct dirent
21 #endif
22 #else
23 #ifdef HAVE_SYS_DIR_H
24 #include <sys/dir.h>
25 #endif
26 #endif
27 #ifdef HAVE_GETPWUID
28 #include <pwd.h>
29 #endif
30
31 #ifdef __STDC__
32 static wordlist *bracexpand(char*);
33 static wordlist *brac1(char*);
34 static wordlist *brac2(char*);
35 static wordlist *globexpand(char*);
36 static char *pcanon(char*);
37 static int sortcmp(char**,char**);
38 static char *tilde_expand(char*);
39 static bool noglobs(char*);
40 #else
41 static wordlist *bracexpand();
42 static wordlist *brac1();
43 static wordlist *brac2();
44 static wordlist *globexpand();
45 static char *pcanon();
46 static int sortcmp();
47 static char *tilde_expand();
48 static bool noglobs();
49 #endif
50
51 char cp_comma = ',';
52 char cp_ocurl = '{';
53 char cp_ccurl = '}';
54 char cp_huh = '?';
55 char cp_star = '*';
56 char cp_obrac = '[';
57 char cp_cbrac = ']';
58 char cp_til = '~';
59
60 /* For each word, go through two steps: expand the {}'s, and then do ?*[]
61 * globbing in them. Sort after the second phase but not the first...
62 */
63
64 void
cp_doglob(list)65 cp_doglob(list)
66
67 wordlist **list;
68 {
69 wordlist *wl, *w, *nwl, *wlist;
70 char *s;
71
72 /* Expand {a,b,c} */
73
74 if (list == NULL) return;
75
76 wlist = *list;
77
78 for (wl = wlist; wl; wl = wl->wl_next) {
79 w = bracexpand(wl->wl_word);
80 if (!w) {
81 wl_free(wlist);
82 *list = NULL;
83 return;
84 }
85 nwl = wl_splice(wl, w);
86 if (wlist == wl)
87 wlist = w;
88 wl = nwl;
89 }
90
91 /* Do tilde expansion. */
92
93 for (wl = wlist; wl; wl = wl->wl_next)
94 if (*wl->wl_word == cp_til) {
95 s = cp_tildexpand(wl->wl_word);
96 tfree(wl->wl_word);
97 if (!s)
98 wl->wl_word = copy("");
99 else
100 wl->wl_word = s;
101 }
102
103 /* Now, expand *?[] for each word. unset * and unalias * mean
104 * something special
105 */
106
107 if ((cp_noglob == true) || eq(wlist->wl_word, "unset") ||
108 eq(wlist->wl_word, "unalias")) {
109 *list = wlist;
110 return;
111 }
112
113 for (wl = wlist; wl; wl = wl->wl_next) {
114 if (noglobs(wl->wl_word))
115 continue;
116 w = globexpand(wl->wl_word);
117 if (w == NULL)
118 continue;
119 nwl = wl_splice(wl, w);
120 if (wlist == wl)
121 wlist = w;
122 wl = nwl;
123 }
124 *list = wlist;
125 return;
126 }
127
128
129 static wordlist *
bracexpand(string)130 bracexpand(string)
131
132 char *string;
133 {
134 wordlist *wl, *w;
135 char *s;
136
137 if (!string)
138 return (NULL);
139 wl = brac1(string);
140 if (!wl)
141 return (NULL);
142 for (w = wl; w; w = w->wl_next) {
143 s = w->wl_word;
144 w->wl_word = copy(s);
145 tfree(s);
146 }
147 return (wl);
148 }
149
150
151 /* Given a string, returns a wordlist of all the {} expansions. This is
152 * called recursively by cp_brac2(). All the words here will be of size
153 * BSIZE_SP, so it is a good idea to copy() and free() the old words.
154 */
155
156 static wordlist *
brac1(string)157 brac1(string)
158
159 char *string;
160 {
161 wordlist *words, *wl, *w, *nw, *nwl, *newwl;
162 char *s;
163 int nb;
164
165 words = alloc(struct wordlist);
166 words->wl_word = tmalloc(BSIZE_SP);
167 for (s = string; *s; s++) {
168 if (*s == cp_ocurl) {
169 nwl = brac2(s);
170 nb = 0;
171 for (;;) {
172 if (*s == cp_ocurl)
173 nb++;
174 if (*s == cp_ccurl)
175 nb--;
176 if (*s == '\0') { /* { */
177 fprintf(cp_err, "Error: missing }.\n");
178 wl_free(words);
179 return (NULL);
180 }
181 if (nb == 0)
182 break;
183 s++;
184 }
185 /* Add nwl to the rest of the strings in words. */
186 newwl = NULL;
187 for (wl = words; wl; wl = wl->wl_next)
188 for (w = nwl; w; w = w->wl_next) {
189 nw = alloc(struct wordlist);
190 nw->wl_word = tmalloc(BSIZE_SP);
191 (void) strcpy(nw->wl_word, wl->wl_word);
192 (void) strcat(nw->wl_word, w->wl_word);
193 newwl = wl_append(newwl, nw);
194 }
195 wl_free(words);
196 words = newwl;
197 }
198 else
199 for (wl = words; wl; wl = wl->wl_next)
200 appendc(wl->wl_word, *s);
201 }
202 return (words);
203 }
204
205
206 /* Given a string starting with a {, return a wordlist of the expansions
207 * for the text until the matching }.
208 */
209
210 static wordlist *
brac2(string)211 brac2(string)
212
213 char *string;
214 {
215 wordlist *wlist = NULL, *nwl;
216 char buf[BSIZE_SP], *s;
217 int nb;
218 bool eflag = false;
219
220 string++; /* Get past the first open brace... */
221 for (;;) {
222 (void) strcpy(buf, string);
223 nb = 0;
224 s = buf;
225 for (;;) {
226 if ((*s == cp_ccurl) && (nb == 0)) {
227 eflag = true;
228 break;
229 }
230 if ((*s == cp_comma) && (nb == 0))
231 break;
232 if (*s == cp_ocurl)
233 nb++;
234 if (*s == cp_ccurl)
235 nb--;
236 if (*s == '\0') { /* { */
237 fprintf(cp_err, "Error: missing }.\n");
238 wl_free(wlist);
239 return (NULL);
240 }
241 s++;
242 }
243 *s = '\0';
244 nwl = brac1(buf);
245 wlist = wl_append(wlist, nwl);
246 string += s - buf + 1;
247 if (eflag)
248 return (wlist);
249 }
250 }
251
252
253 /* Return a wordlist, with *?[] expanded and sorted. This is the idea: set
254 * up an array with possible matches, and go through each path component
255 * and search the appropriate directories for things that match, and add
256 * those that do to the array.
257 */
258
259 static wordlist *
globexpand(string)260 globexpand(string)
261
262 char *string;
263 {
264 char *poss[MAXWORDS];
265 char buf[BSIZE_SP];
266 char *s;
267 char *point; /* Where we are at in the pathname. */
268 int i, j, level;
269 bool found;
270 wordlist *wlist = NULL, *wl, *lwl = NULL; /* Make lint shut up. */
271 #if defined(HAVE_DIRENT_H) || defined(HAVE_SYS_DIR_H)
272 DIR *wdir;
273 struct direct *de;
274 #endif
275
276 bzero((char *) poss, MAXWORDS * sizeof (char *));
277 string = pcanon(string);
278 point = string;
279
280 if (*point == DIR_TERM) {
281 point++;
282 poss[0] = copy(DIR_PATHSEP);
283 }
284 else if (point[0] == '.' && point[1] == '.' && point[2] == DIR_TERM) {
285 poss[0] = copy("..");
286 point += 3;
287 }
288 #ifdef MSDOS
289 else if (point[1] == ':') {
290 point += 2;
291 if (point[0] == DIR_TERM) {
292 point++;
293 poss[0] = copy("?:\\");
294 }
295 else
296 poss[0] = copy("?:.");
297 *poss[0] = *string;
298 }
299 #endif
300 else
301 poss[0] = copy(DIR_CWD);
302
303 level = 0;
304 nextcomp:
305 level++;
306 (void) strcpy(buf, point);
307 s = strchr(buf, DIR_TERM);
308 if (s)
309 *s = '\0';
310 #if defined(HAVE_DIRENT_H) || defined(HAVE_SYS_DIR_H)
311 for (i = 0; i < MAXWORDS; i++) {
312 if (!poss[i] || (poss[i][0] == '\0'))
313 continue;
314 found = false;
315 wdir = opendir(poss[i]);
316 if (wdir == NULL) {
317 if (level > 1) {
318 tfree(poss[i]);
319 continue;
320 }
321 goto err;
322 }
323 while ((de = readdir(wdir)) != NULL)
324 if (cp_globmatch(buf, de->d_name)) {
325 found = true;
326 for (j = 0; j < MAXWORDS; j++)
327 if (!poss[j])
328 break;
329 if (j == MAXWORDS) {
330 fprintf(cp_err,
331 "Too many arguments.\n");
332 wl = NULL;
333 goto err1;
334 }
335 poss[j] = tmalloc(BSIZE_SP);
336 (void) strcpy(poss[j] + 1, poss[i]);
337 (void) strcat(poss[j] + 1, DIR_PATHSEP);
338 (void) strcat(poss[j] + 1, de->d_name);
339 }
340 tfree(poss[i]);
341 poss[i] = NULL;
342 (void) closedir(wdir);
343 if (!found) {
344 if (level > 1) {
345 continue;
346 }
347 goto err;
348 }
349 }
350 /* Hide the newly found words from the globbing process by making
351 * the first byte a '\0'.
352 */
353 for (i = 0; i < MAXWORDS; i++)
354 if (poss[i])
355 for (j = 0; poss[i][j] = poss[i][j+1]; j++);
356 if (strchr(point, DIR_TERM)) {
357 point = strchr(point, DIR_TERM) + 1;
358 goto nextcomp;
359 }
360 #endif
361
362 /* Compact everything properly. */
363
364 for (i = j = 0; i < MAXWORDS; i++) {
365 if (!poss[i]) continue;
366
367 if (i != j) {
368 poss[j] = poss[i];
369 poss[i] = NULL;
370 }
371 j++;
372 }
373 if (j == 0)
374 goto err;
375
376 /* Now, sort the stuff and make it into wordlists. */
377
378 qsort((char *) poss, j, sizeof (char *),
379 #ifdef __STDC__
380 (int(*)(const void*,const void*))sortcmp);
381 #else
382 sortcmp);
383 #endif
384
385 for (i = 0; i < j; i++) {
386
387 if (wlist == NULL)
388 wlist = wl = alloc(wordlist);
389 else {
390 wl->wl_next = alloc(wordlist);
391 wl->wl_next->wl_prev = wl;
392 wl = wl->wl_next;
393 }
394 wl->wl_word = pcanon(poss[i]);
395 tfree(poss[i]);
396 }
397 tfree(string);
398 return (wlist);
399
400 err:
401 if (cp_nonomatch) {
402 wl = alloc(wordlist);
403 wl->wl_word = copy(string);
404 }
405 else {
406 fprintf(cp_err, "%s: no match.\n", string);
407 wl = NULL;
408 }
409
410 err1:
411 for (i = 0; i < MAXWORDS; i++)
412 if (poss[i])
413 tfree(poss[i]);
414 return (NULL);
415 }
416
417
418 /* Normalize filenames (get rid of extra ///, .///... etc.. ) */
419
420 static char *
pcanon(string)421 pcanon(string)
422
423 char *string;
424 {
425 char *p, *s;
426
427 s = p = tmalloc(strlen(string) + 1);
428
429 bcomp:
430 if (!strncmp(string, DIR_CWD, sizeof(DIR_CWD) - 1)
431 && (*(string + 1) == DIR_TERM)) {
432 string += 2;
433 goto bcomp;
434 }
435 morew:
436 if (*string == DIR_TERM) {
437 *s++ = DIR_TERM;
438 while (*++string == DIR_TERM);
439 goto bcomp;
440 }
441 if (!*string) {
442 if (*(s - 1) == DIR_TERM)
443 s--;
444 *s = '\0';
445 return (p);
446 }
447 *s++ = *string++;
448 goto morew;
449 }
450
451
452 static int
sortcmp(s1,s2)453 sortcmp(s1, s2)
454
455 char **s1, **s2;
456 {
457 char *a, *b;
458
459 a = *s1;
460 b = *s2;
461 for (;;) {
462 if (*a > *b)
463 return (1);
464 if (*a < *b)
465 return (-1);
466 if (*a == '\0')
467 return (0);
468 a++;
469 b++;
470 }
471 }
472
473
474 /* Expand tildes. */
475
476 char *
cp_tildexpand(string)477 cp_tildexpand(string)
478
479 char *string;
480 {
481 char *result;
482
483 result = tilde_expand(string);
484
485 if (!result) {
486 if (cp_nonomatch)
487 return (copy(string));
488 else
489 return (NULL);
490 }
491 return (result);
492 }
493
494
495 void
cp_pathfix(buf)496 cp_pathfix(buf)
497
498 char *buf;
499 {
500 #ifdef MSDOS
501 char *s, *t;
502 int bcnt = 0, ecnt = 0;
503
504 s = t = buf;
505 while (*t != '\0') {
506 if (*t == '/' || *t == '\\') {
507 *s++ = '\\';
508 t++;
509 bcnt = 0;
510 ecnt = 0;
511 }
512 else if (*t == '.') {
513 *s++ = *t++;
514 ecnt = 1;
515 }
516 else if (!ecnt) {
517 if (bcnt++ < 8) *s++ = *t++;
518 else t++;
519 }
520 else {
521 if (ecnt++ < 4) *s++ = *t++;
522 else t++;
523 }
524 }
525 *s = '\0';
526
527 #else
528 char *s;
529
530 if (index(buf,'~')) {
531 s = cp_tildexpand(buf);
532 if (s) {
533 strcpy(buf,s);
534 txfree(s);
535 }
536 }
537 #endif
538 }
539
540
541 static char *
tilde_expand(string)542 tilde_expand(string)
543
544 char *string;
545 {
546 #ifdef HAVE_GETPWUID
547 struct passwd *pw;
548 char *tail;
549 char buf[BSIZE_SP];
550 char *k, c;
551
552 if (!string)
553 return (NULL);
554
555 while (*string && isspace(*string))
556 string++;
557
558 if (*string != '~')
559 return copy(string);
560
561 string += 1;
562
563 if (!*string || *string == '/') {
564 pw = getpwuid(getuid());
565 *buf = 0;
566 }
567 else {
568 k = buf;
569 while ((c = *string) && c != '/')
570 *k++ = c, string++;
571 *k = 0;
572 pw = getpwnam(buf);
573 }
574
575 if (pw) {
576 strcpy(buf, pw->pw_dir);
577 if (*string)
578 strcat(buf, string);
579 }
580 else
581 return (NULL);
582
583 return (copy(buf));
584
585 #else
586 return (copy(string));
587 #endif
588 }
589
590
591 /* Say whether the pattern p can match the string s. */
592
593 bool
cp_globmatch(p,s)594 cp_globmatch(p, s)
595
596 char *p, *s;
597 {
598 char schar, pchar, bc, fc;
599 bool bchar, except;
600
601 if ((*s == '.') && ((*p == cp_huh) || (*p == cp_star)))
602 return (false);
603
604 for (;;) {
605 schar = strip(*s++);
606 pchar = *p++;
607 if (pchar == cp_star) {
608 if (*p == '\0')
609 return (true);
610 for (s--; *s != '\0'; s++)
611 if (cp_globmatch(p, s))
612 return (true);
613 return (false);
614 }
615 else if (pchar == cp_huh) {
616 if (schar == '\0')
617 return (false);
618 continue;
619 }
620 else if (pchar == cp_obrac) {
621 bchar = false;
622 if (*p == '^') {
623 except = true;
624 p++;
625 }
626 else
627 except = false;
628 fc = -1;
629 while (bc = *p++) {
630 if (bc == cp_cbrac) {
631 if ((bchar && !except) ||
632 (!bchar && except))
633 break;
634 else
635 return (false);
636 }
637 if (bc == '-') {
638 if (fc <= schar && schar <= *p++)
639 bchar = true;
640 }
641 else {
642 fc = bc;
643 if (fc == schar)
644 bchar = true;
645 }
646 }
647 if (bc == '\0') {
648 fprintf(cp_err, "Error: missing ].\n");
649 return (false);
650 }
651 continue;
652 }
653 else if (pchar == '\0') {
654 if (schar == '\0')
655 return (true);
656 else
657 return (false);
658 }
659 else {
660 if (strip(pchar) != schar)
661 return (false);
662 continue;
663 }
664 }
665 }
666
667
668 static bool
noglobs(string)669 noglobs(string)
670
671 char *string;
672 {
673 if (strchr(string, cp_star) || strchr(string, cp_huh) ||
674 strchr(string, cp_obrac))
675 return (false);
676 else
677 return (true);
678 }
679