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