1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
29
30 /*
31 * University Copyright- Copyright (c) 1982, 1986, 1988
32 * The Regents of the University of California
33 * All Rights Reserved
34 *
35 * University Acknowledgment- Portions of this document are derived from
36 * software developed by the University of California, Berkeley, and its
37 * contributors.
38 */
39
40 /*
41 * C-shell glob for random programs.
42 */
43
44 #include "ftp_var.h"
45
46 #ifndef NCARGS
47 #define NCARGS 5120
48 #endif
49
50 #define QUOTE 0200
51 #define TRIM 0177
52 #define eq(a, b) (strcmp(a, b) == 0)
53
54 /*
55 * According to the person who wrote the C shell "glob" code, a reasonable
56 * limit on number of arguments would seem to be the maximum number of
57 * characters in an arg list / 6.
58 *
59 * XXX: With the new VM system, NCARGS has become enormous, making
60 * it impractical to allocate arrays with NCARGS / 6 entries on
61 * the stack. The proper fix is to revamp code elsewhere (in
62 * sh.dol.c and sh.glob.c) to use a different technique for handling
63 * command line arguments. In the meantime, we simply fall back
64 * on using the old value of NCARGS.
65 */
66 #ifdef notyet
67 #define GAVSIZ (NCARGS / 6)
68 #else /* notyet */
69 #define GAVSIZ (10240 / 6)
70 #endif /* notyet */
71
72 static char **gargv; /* Pointer to the (stack) arglist */
73 static char **agargv;
74 static int agargv_size;
75 static long gargc; /* Number args in gargv */
76 static short gflag;
77 static char *strspl();
78 static char *strend(char *cp);
79 static char *strspl(char *cp, char *dp);
80 static int tglob(char c);
81 static char **copyblk(char **v);
82 static void ginit(char **agargv);
83 static void addpath(char c);
84 static int any(int c, char *s);
85 static void Gcat(char *s1, char *s2);
86 static void collect(char *as);
87 static void acollect(char *as);
88 static void sort(void);
89 static void expand(char *as);
90 static void matchdir(char *pattern);
91 static int execbrc(char *p, char *s);
92 static int ftp_fnmatch(wchar_t t_ch, wchar_t t_fch, wchar_t t_lch);
93 static int gethdir(char *home);
94 static void xfree(char *cp);
95 static void rscan(char **t, int (*f)(char));
96 static int letter(char c);
97 static int digit(char c);
98 static int match(char *s, char *p);
99 static int amatch(char *s, char *p);
100 static int blklen(char **av);
101 static char **blkcpy(char **oav, char **bv);
102
103 static int globcnt;
104
105 static char *globchars = "`{[*?";
106
107 static char *gpath, *gpathp, *lastgpathp;
108 static int globbed;
109 static char *entp;
110 static char **sortbas;
111
112 char **
glob(char * v)113 glob(char *v)
114 {
115 char agpath[FTPBUFSIZ];
116 char *vv[2];
117
118 if (agargv == NULL) {
119 agargv = (char **)malloc(GAVSIZ * sizeof (char *));
120 agargv_size = GAVSIZ;
121 if (agargv == NULL) {
122 globerr = "Arguments too long.";
123 return (0);
124 }
125 }
126 vv[0] = v;
127 vv[1] = 0;
128 globerr = 0;
129 gflag = 0;
130 rscan(vv, tglob);
131 if (gflag == 0)
132 return (copyblk(vv));
133
134 gpath = agpath;
135 gpathp = gpath;
136 *gpathp = 0;
137 lastgpathp = &gpath[sizeof (agpath) - 2];
138 ginit(agargv);
139 globcnt = 0;
140 collect(v);
141 if (globcnt == 0 && (gflag&1)) {
142 blkfree(gargv);
143 if (gargv == agargv)
144 agargv = 0;
145 gargv = 0;
146 return (0);
147 } else
148 return (gargv = copyblk(gargv));
149 }
150
151 static void
ginit(char ** agargv)152 ginit(char **agargv)
153 {
154
155 agargv[0] = 0;
156 gargv = agargv;
157 sortbas = agargv;
158 gargc = 0;
159 }
160
161 static void
collect(char * as)162 collect(char *as)
163 {
164 if (eq(as, "{") || eq(as, "{}")) {
165 Gcat(as, "");
166 sort();
167 } else
168 acollect(as);
169 }
170
171 static void
acollect(char * as)172 acollect(char *as)
173 {
174 register long ogargc = gargc;
175
176 gpathp = gpath; *gpathp = 0; globbed = 0;
177 expand(as);
178 if (gargc != ogargc)
179 sort();
180 }
181
182 static void
sort(void)183 sort(void)
184 {
185 register char **p1, **p2, *c;
186 char **Gvp = &gargv[gargc];
187
188 p1 = sortbas;
189 while (p1 < Gvp-1) {
190 p2 = p1;
191 while (++p2 < Gvp)
192 if (strcmp(*p1, *p2) > 0)
193 c = *p1, *p1 = *p2, *p2 = c;
194 p1++;
195 }
196 sortbas = Gvp;
197 }
198
199 static void
expand(char * as)200 expand(char *as)
201 {
202 register char *cs;
203 register char *sgpathp, *oldcs;
204 struct stat stb;
205
206 sgpathp = gpathp;
207 cs = as;
208 if (*cs == '~' && gpathp == gpath) {
209 addpath('~');
210 cs++;
211 while (letter(*cs) || digit(*cs) || *cs == '-')
212 addpath(*cs++);
213 if (!*cs || *cs == '/') {
214 if (gpathp != gpath + 1) {
215 *gpathp = 0;
216 if (gethdir(gpath + 1))
217 globerr = "Unknown user name after ~";
218 (void) strcpy(gpath, gpath + 1);
219 } else
220 (void) strcpy(gpath, home);
221 gpathp = strend(gpath);
222 }
223 }
224 while (!any(*cs, globchars)) {
225 if (*cs == 0) {
226 if (!globbed)
227 Gcat(gpath, "");
228 else if (stat(gpath, &stb) >= 0) {
229 Gcat(gpath, "");
230 globcnt++;
231 }
232 goto endit;
233 }
234 addpath(*cs++);
235 }
236 oldcs = cs;
237 while (cs > as && *cs != '/')
238 cs--, gpathp--;
239 if (*cs == '/')
240 cs++, gpathp++;
241 *gpathp = 0;
242 if (*oldcs == '{') {
243 (void) execbrc(cs, ((char *)0));
244 return;
245 }
246 matchdir(cs);
247 endit:
248 gpathp = sgpathp;
249 *gpathp = 0;
250 }
251
252 static void
matchdir(char * pattern)253 matchdir(char *pattern)
254 {
255 struct stat stb;
256 register struct dirent *dp;
257 DIR *dirp;
258
259 /*
260 * BSD/SunOS open() system call maps a null pathname into
261 * "." while System V does not.
262 */
263 if (*gpath == (char)0) {
264 dirp = opendir(".");
265 } else
266 dirp = opendir(gpath);
267 if (dirp == NULL) {
268 if (globbed)
269 return;
270 goto patherr2;
271 }
272 if (fstat(dirp->dd_fd, &stb) < 0)
273 goto patherr1;
274 if (!S_ISDIR(stb.st_mode)) {
275 errno = ENOTDIR;
276 goto patherr1;
277 }
278 while ((dp = readdir(dirp)) != NULL) {
279 if (dp->d_ino == 0)
280 continue;
281 if (match(dp->d_name, pattern)) {
282 Gcat(gpath, dp->d_name);
283 globcnt++;
284 }
285 }
286 closedir(dirp);
287 return;
288
289 patherr1:
290 closedir(dirp);
291 patherr2:
292 globerr = "Bad directory components";
293 }
294
295 static int
execbrc(char * p,char * s)296 execbrc(char *p, char *s)
297 {
298 char restbuf[FTPBUFSIZ + 2];
299 register char *pe, *pm, *pl;
300 int brclev = 0;
301 char *lm, savec, *sgpathp;
302 int len;
303
304 for (lm = restbuf; *p != '{'; *lm += len, p += len) {
305 if ((len = mblen(p, MB_CUR_MAX)) <= 0)
306 len = 1;
307 memcpy(lm, p, len);
308 }
309
310 for (pe = ++p; *pe; pe += len) {
311 if ((len = mblen(pe, MB_CUR_MAX)) <= 0)
312 len = 1;
313
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 += len) {
328 if ((len = mblen(pe, MB_CUR_MAX)) <= 0)
329 len = 1;
330 }
331 len = 1;
332 continue;
333 }
334 }
335 pend:
336 brclev = 0;
337 for (pl = pm = p; pm <= pe; pm += len) {
338 if ((len = mblen(pm, MB_CUR_MAX)) <= 0)
339 len = 1;
340
341 switch (*pm & (QUOTE|TRIM)) {
342
343 case '{':
344 brclev++;
345 continue;
346
347 case '}':
348 if (brclev) {
349 brclev--;
350 continue;
351 }
352 goto doit;
353
354 case ','|QUOTE:
355 case ',':
356 if (brclev)
357 continue;
358 doit:
359 savec = *pm;
360 *pm = 0;
361 (void) strcpy(lm, pl);
362 (void) strcat(restbuf, pe + 1);
363 *pm = savec;
364 if (s == 0) {
365 sgpathp = gpathp;
366 expand(restbuf);
367 gpathp = sgpathp;
368 *gpathp = 0;
369 } else if (amatch(s, restbuf))
370 return (1);
371 sort();
372 pl = pm + 1;
373 if (brclev)
374 return (0);
375 continue;
376
377 case '[':
378 for (pm++; *pm && *pm != ']'; pm += len) {
379 if ((len = mblen(pm, MB_CUR_MAX)) <= 0)
380 len = 1;
381 }
382 len = 1;
383 if (!*pm)
384 pm--;
385 continue;
386 }
387 }
388 if (brclev)
389 goto doit;
390 return (0);
391 }
392
393 static int
match(char * s,char * p)394 match(char *s, char *p)
395 {
396 register int c;
397 register char *sentp;
398 char sglobbed = globbed;
399
400 if (*s == '.' && *p != '.')
401 return (0);
402 sentp = entp;
403 entp = s;
404 c = amatch(s, p);
405 entp = sentp;
406 globbed = sglobbed;
407 return (c);
408 }
409
410 static int
amatch(char * s,char * p)411 amatch(char *s, char *p)
412 {
413 wchar_t scc;
414 int ok;
415 wchar_t lc1, lc2;
416 char *sgpathp;
417 struct stat stb;
418 wchar_t c, cc;
419 int len_s, len_p;
420
421 globbed = 1;
422 for (;;) {
423 if ((len_s = mbtowc(&scc, s, MB_CUR_MAX)) <= 0) {
424 scc = (unsigned char)*s;
425 len_s = 1;
426 }
427 /* scc = *s++ & TRIM; */
428 s += len_s;
429
430 if ((len_p = mbtowc(&c, p, MB_CUR_MAX)) <= 0) {
431 c = (unsigned char)*p;
432 len_p = 1;
433 }
434 p += len_p;
435 switch (c) {
436
437 case '{':
438 return (execbrc(p - len_p, s - len_s));
439
440 case '[':
441 ok = 0;
442 lc1 = 0;
443 while ((cc = *p) != '\0') {
444 if ((len_p = mbtowc(&cc, p, MB_CUR_MAX)) <= 0) {
445 cc = (unsigned char)*p;
446 len_p = 1;
447 }
448 p += len_p;
449 if (cc == ']') {
450 if (ok)
451 break;
452 return (0);
453 }
454 if (cc == '-') {
455 if ((len_p = mbtowc(&lc2, p,
456 MB_CUR_MAX)) <= 0) {
457 lc2 = (unsigned char)*p;
458 len_p = 1;
459 }
460 p += len_p;
461 if (ftp_fnmatch(scc, lc1, lc2))
462 ok++;
463 } else
464 if (scc == (lc1 = cc))
465 ok++;
466 }
467 if (cc == 0)
468 if (!ok)
469 return (0);
470 continue;
471
472 case '*':
473 if (!*p)
474 return (1);
475 if (*p == '/') {
476 p++;
477 goto slash;
478 }
479 s -= len_s;
480 do {
481 if (amatch(s, p))
482 return (1);
483 } while (*s++);
484 return (0);
485
486 case 0:
487 return (scc == 0);
488
489 default:
490 if (c != scc)
491 return (0);
492 continue;
493
494 case '?':
495 if (scc == 0)
496 return (0);
497 continue;
498
499 case '/':
500 if (scc)
501 return (0);
502 slash:
503 s = entp;
504 sgpathp = gpathp;
505 while (*s)
506 addpath(*s++);
507 addpath('/');
508 if (stat(gpath, &stb) == 0 && S_ISDIR(stb.st_mode))
509 if (*p == 0) {
510 Gcat(gpath, "");
511 globcnt++;
512 } else
513 expand(p);
514 gpathp = sgpathp;
515 *gpathp = 0;
516 return (0);
517 }
518 }
519 }
520
521 #ifdef notdef
522 static
Gmatch(s,p)523 Gmatch(s, p)
524 register char *s, *p;
525 {
526 register int scc;
527 int ok, lc;
528 int c, cc;
529
530 for (;;) {
531 scc = *s++ & TRIM;
532 switch (c = *p++) {
533
534 case '[':
535 ok = 0;
536 lc = 077777;
537 while (cc = *p++) {
538 if (cc == ']') {
539 if (ok)
540 break;
541 return (0);
542 }
543 if (cc == '-') {
544 if (lc <= scc && scc <= *p++)
545 ok++;
546 } else
547 if (scc == (lc = cc))
548 ok++;
549 }
550 if (cc == 0)
551 if (ok)
552 p--;
553 else
554 return (0);
555 continue;
556
557 case '*':
558 if (!*p)
559 return (1);
560 for (s--; *s; s++)
561 if (Gmatch(s, p))
562 return (1);
563 return (0);
564
565 case 0:
566 return (scc == 0);
567
568 default:
569 if ((c & TRIM) != scc)
570 return (0);
571 continue;
572
573 case '?':
574 if (scc == 0)
575 return (0);
576 continue;
577
578 }
579 }
580 }
581 #endif
582
583 static void
Gcat(char * s1,char * s2)584 Gcat(char *s1, char *s2)
585 {
586 if (gargc >= agargv_size - 1) {
587 char **tmp;
588
589 if (globerr) {
590 return;
591 }
592 tmp = (char **)realloc(agargv,
593 (agargv_size + GAVSIZ) * sizeof (char *));
594 if (tmp == NULL) {
595 globerr = "Arguments too long";
596 return;
597 } else {
598 agargv = tmp;
599 agargv_size += GAVSIZ;
600 }
601 gargv = agargv;
602 sortbas = agargv;
603 }
604 gargc++;
605 gargv[gargc] = 0;
606 gargv[gargc - 1] = strspl(s1, s2);
607 }
608
609 static void
addpath(char c)610 addpath(char c)
611 {
612
613 if (gpathp >= lastgpathp)
614 globerr = "Pathname too long";
615 else {
616 *gpathp++ = c;
617 *gpathp = 0;
618 }
619 }
620
621 static void
rscan(char ** t,int (* f)(char))622 rscan(char **t, int (*f)(char))
623 {
624 register char *p, c;
625 int len;
626
627 while (p = *t++) {
628 if (f == tglob)
629 if (*p == '~')
630 gflag |= 2;
631 else if (eq(p, "{") || eq(p, "{}"))
632 continue;
633 while ((c = *p) != '\0') {
634 (void) (*f)(c);
635 if ((len = mblen(p, MB_CUR_MAX)) <= 0)
636 len = 1;
637 p += len;
638 }
639 }
640 }
641
642 static int
tglob(char c)643 tglob(char c)
644 {
645 if (any(c, globchars))
646 gflag |= c == '{' ? 2 : 1;
647 return (c);
648 }
649
650 static int
letter(char c)651 letter(char c)
652 {
653 return (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '_');
654 }
655
656 static int
digit(char c)657 digit(char c)
658 {
659 return (c >= '0' && c <= '9');
660 }
661
662 static int
any(int c,char * s)663 any(int c, char *s)
664 {
665 int len;
666
667 while (*s) {
668 if (*s == c)
669 return (1);
670 if ((len = mblen(s, MB_CUR_MAX)) <= 0)
671 len = 1;
672 s += len;
673 }
674 return (0);
675 }
676
677 static int
blklen(char ** av)678 blklen(char **av)
679 {
680 register int i = 0;
681
682 while (*av++)
683 i++;
684 return (i);
685 }
686
687 static char **
blkcpy(char ** oav,char ** bv)688 blkcpy(char **oav, char **bv)
689 {
690 register char **av = oav;
691
692 while (*av++ = *bv++)
693 continue;
694 return (oav);
695 }
696
697 void
blkfree(char ** av0)698 blkfree(char **av0)
699 {
700 register char **av = av0;
701
702 while (*av)
703 xfree(*av++);
704 free(av0);
705 }
706
707 static void
xfree(char * cp)708 xfree(char *cp)
709 {
710 extern char end[];
711
712 if (cp >= end && cp < (char *)&cp)
713 free(cp);
714 }
715
716 static char *
strspl(char * cp,char * dp)717 strspl(char *cp, char *dp)
718 {
719 register char *ep = malloc((unsigned)(strlen(cp) + strlen(dp) + 1));
720
721 if (ep == (char *)0)
722 fatal("Out of memory");
723 (void) strcpy(ep, cp);
724 (void) strcat(ep, dp);
725 return (ep);
726 }
727
728 static char **
copyblk(char ** v)729 copyblk(char **v)
730 {
731 register char **nv = (char **)malloc((unsigned)((blklen(v) + 1) *
732 sizeof (char **)));
733
734 if (nv == (char **)0)
735 fatal("Out of memory");
736
737 return (blkcpy(nv, v));
738 }
739
740 static char *
strend(char * cp)741 strend(char *cp)
742 {
743
744 while (*cp)
745 cp++;
746 return (cp);
747 }
748 /*
749 * Extract a home directory from the password file
750 * The argument points to a buffer where the name of the
751 * user whose home directory is sought is currently.
752 * We write the home directory of the user back there.
753 */
754 static int
gethdir(char * home)755 gethdir(char *home)
756 {
757 register struct passwd *pp = getpwnam(home);
758
759 if (!pp || home + strlen(pp->pw_dir) >= lastgpathp)
760 return (1);
761 (void) strcpy(home, pp->pw_dir);
762 return (0);
763 }
764
765 static int
ftp_fnmatch(wchar_t t_ch,wchar_t t_fch,wchar_t t_lch)766 ftp_fnmatch(wchar_t t_ch, wchar_t t_fch, wchar_t t_lch)
767 {
768 char t_char[MB_LEN_MAX + 1];
769 char t_patan[MB_LEN_MAX * 2 + 8];
770 char *p;
771 int i;
772
773 if ((t_ch == t_fch) || (t_ch == t_lch))
774 return (1);
775
776 p = t_patan;
777 if ((i = wctomb(t_char, (wchar_t)t_ch)) <= 0)
778 return (0);
779 t_char[i] = 0;
780
781 *p++ = '[';
782 if ((i = wctomb(p, (wchar_t)t_fch)) <= 0)
783 return (0);
784 p += i;
785 *p++ = '-';
786 if ((i = wctomb(p, (wchar_t)t_lch)) <= 0)
787 return (0);
788 p += i;
789 *p++ = ']';
790 *p = 0;
791
792 if (fnmatch(t_patan, t_char, FNM_NOESCAPE))
793 return (0);
794 return (1);
795 }
796