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