summaryrefslogtreecommitdiff
path: root/usr/src/cmd/csh/sh.glob.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/csh/sh.glob.c')
-rw-r--r--usr/src/cmd/csh/sh.glob.c972
1 files changed, 972 insertions, 0 deletions
diff --git a/usr/src/cmd/csh/sh.glob.c b/usr/src/cmd/csh/sh.glob.c
new file mode 100644
index 0000000000..1d639392e5
--- /dev/null
+++ b/usr/src/cmd/csh/sh.glob.c
@@ -0,0 +1,972 @@
+/*
+ * Copyright 2001 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * Copyright (c) 1980 Regents of the University of California.
+ * All rights reserved. The Berkeley Software License Agreement
+ * specifies the terms and conditions for redistribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "sh.h"
+#include "sh.tconst.h"
+#include <dirent.h>
+#ifdef MBCHAR
+#include <widec.h> /* wcsetno() */
+#include <fnmatch.h> /* fnmatch() */
+#endif /* MBCHAR */
+
+/*
+ * C Shell
+ */
+
+int globcnt;
+
+tchar *gpath, *gpathp, *lastgpathp;
+int globbed;
+bool noglob;
+bool nonomatch;
+tchar *entp;
+tchar **sortbas;
+int sortscmp();
+extern DIR *opendir_();
+
+#define sort() qsort((char *)sortbas, &gargv[gargc] - sortbas, \
+ sizeof (*sortbas), (int (*)(const void *, \
+ const void *)) sortscmp), sortbas = &gargv[gargc]
+
+
+tchar **
+glob(v)
+ register tchar **v;
+{
+ tchar agpath[BUFSIZ];
+ tchar *agargv[GAVSIZ];
+
+ gpath = agpath; gpathp = gpath; *gpathp = 0;
+ lastgpathp = &gpath[BUFSIZ - 2];
+ ginit(agargv); globcnt = 0;
+#ifdef TRACE
+ tprintf("TRACE- glob()\n");
+#endif
+#ifdef GDEBUG
+ printf("glob entered: "); blkpr(v); printf("\n");
+#endif
+ noglob = adrof(S_noglob /* "noglob" */) != 0;
+ nonomatch = adrof(S_nonomatch /* "nonomatch" */) != 0;
+ globcnt = noglob | nonomatch;
+ while (*v)
+ collect(*v++);
+#ifdef GDEBUG
+ printf("glob done, globcnt=%d, gflag=%d: ", globcnt, gflag);
+ blkpr(gargv); printf("\n");
+#endif
+ if (globcnt == 0 && (gflag&1)) {
+ blkfree(gargv), gargv = 0;
+ return (0);
+ } else
+ return (gargv = copyblk(gargv));
+}
+
+ginit(agargv)
+ tchar **agargv;
+{
+
+ agargv[0] = 0; gargv = agargv; sortbas = agargv; gargc = 0;
+ gnleft = NCARGS - 4;
+}
+
+collect(as)
+ register tchar *as;
+{
+ register int i;
+
+#ifdef TRACE
+ tprintf("TRACE- collect()\n");
+#endif
+ if (any('`', as)) {
+#ifdef GDEBUG
+ printf("doing backp of %t\n", as);
+#endif
+ (void) dobackp(as, 0);
+#ifdef GDEBUG
+ printf("backp done, acollect'ing\n");
+#endif
+ /*
+ * dobackp has the side effect of messing with
+ * gflag, since it does more globbing, so check
+ * if the results is still globbable
+ */
+ tglob(pargv);
+
+ for (i = 0; i < pargc; i++)
+ if (noglob) {
+ Gcat(pargv[i], S_ /* "" */);
+ sortbas = &gargv[gargc];
+ } else
+ acollect(pargv[i]);
+ if (pargv)
+ blkfree(pargv), pargv = 0;
+#ifdef GDEBUG
+ printf("acollect done\n");
+#endif
+ } else if (noglob || eq(as, S_LBRA /* "{" */) ||
+ eq(as, S_BRABRA /* "{}" */)) {
+ Gcat(as, S_ /* "" */);
+ sort();
+ } else
+ acollect(as);
+}
+
+acollect(as)
+ register tchar *as;
+{
+ register long ogargc = gargc;
+
+#ifdef TRACE
+ tprintf("TRACE- acollect()\n");
+#endif
+ gpathp = gpath; *gpathp = 0; globbed = 0;
+ expand(as);
+ if (gargc == ogargc) {
+ if (nonomatch) {
+ Gcat(as, S_ /* "" */);
+ sort();
+ }
+ } else
+ sort();
+}
+
+/*
+ * String compare for qsort. Also used by filec code in sh.file.c.
+ */
+sortscmp(a1, a2)
+ tchar **a1, **a2;
+{
+
+ return (strcoll_(*a1, *a2));
+}
+
+expand(as)
+ tchar *as;
+{
+ register tchar *cs;
+ register tchar *sgpathp, *oldcs;
+ struct stat stb;
+
+#ifdef TRACE
+ tprintf("TRACE- expand()\n");
+#endif
+ sgpathp = gpathp;
+ cs = as;
+ if (*cs == '~' && gpathp == gpath) {
+ addpath('~');
+ for (cs++; alnum(*cs) || *cs == '-'; )
+ addpath(*cs++);
+ if (!*cs || *cs == '/') {
+ if (gpathp != gpath + 1) {
+ *gpathp = 0;
+ if (gethdir(gpath + 1))
+ /*
+ * modified from %s to %t
+ */
+ error("Unknown user: %t", gpath + 1);
+ (void) strcpy_(gpath, gpath + 1);
+ } else
+ (void) strcpy_(gpath,
+ value(S_home /* "home" */));
+ gpathp = strend(gpath);
+ }
+ }
+ while (!isglob(*cs)) {
+ if (*cs == 0) {
+ if (!globbed)
+ Gcat(gpath, S_ /* "" */);
+ else if (lstat_(gpath, &stb) >= 0) {
+ Gcat(gpath, S_ /* "" */);
+ globcnt++;
+ }
+ goto endit;
+ }
+ addpath(*cs++);
+ }
+ oldcs = cs;
+ while (cs > as && *cs != '/')
+ cs--, gpathp--;
+ if (*cs == '/')
+ cs++, gpathp++;
+ *gpathp = 0;
+ if (*oldcs == '{') {
+ (void) execbrc(cs, NOSTR);
+ return;
+ }
+ matchdir_(cs);
+endit:
+ gpathp = sgpathp;
+ *gpathp = 0;
+}
+
+matchdir_(pattern)
+ tchar *pattern;
+{
+ struct stat stb;
+ register struct dirent *dp;
+ register DIR *dirp;
+ tchar curdir_[MAXNAMLEN+1];
+ int slproc = 0;
+
+#ifdef TRACE
+ tprintf("TRACE- matchdir()\n");
+#endif
+ /*
+ * BSD's opendir would open "." if argument is NULL, but not S5
+ */
+
+ if (*gpath == NULL)
+ dirp = opendir_(S_DOT /* "." */);
+ else
+ dirp = opendir_(gpath);
+ if (dirp == NULL) {
+ if (globbed)
+ return;
+ goto patherr2;
+ }
+ if (fstat(dirp->dd_fd, &stb) < 0)
+ goto patherr1;
+ if (!isdir(stb)) {
+ errno = ENOTDIR;
+ goto patherr1;
+ }
+ while ((dp = readdir(dirp)) != NULL) {
+
+ if (dp->d_ino == 0)
+ continue;
+ strtots(curdir_, dp->d_name);
+ slproc = 0;
+ if (match(curdir_, pattern, &slproc)) {
+ Gcat(gpath, curdir_);
+ globcnt++;
+ }
+ }
+ unsetfd(dirp->dd_fd);
+ closedir_(dirp);
+ return;
+
+patherr1:
+ unsetfd(dirp->dd_fd);
+ closedir_(dirp);
+patherr2:
+ Perror(gpath);
+}
+
+execbrc(p, s)
+ tchar *p, *s;
+{
+ tchar restbuf[BUFSIZ + 2];
+ register tchar *pe, *pm, *pl;
+ int brclev = 0;
+ tchar *lm, savec, *sgpathp;
+ int slproc = 0;
+
+#ifdef TRACE
+ tprintf("TRACE- execbrc()\n");
+#endif
+ for (lm = restbuf; *p != '{'; *lm++ = *p++)
+ continue;
+ for (pe = ++p; *pe; pe++)
+ switch (*pe) {
+
+ case '{':
+ brclev++;
+ continue;
+
+ case '}':
+ if (brclev == 0)
+ goto pend;
+ brclev--;
+ continue;
+
+ case '[':
+ for (pe++; *pe && *pe != ']'; pe++)
+ continue;
+ if (!*pe)
+ error("Missing ]");
+ continue;
+ }
+pend:
+ if (brclev || !*pe)
+ error("Missing }");
+ for (pl = pm = p; pm <= pe; pm++)
+ switch (*pm & (QUOTE|TRIM)) {
+
+ case '{':
+ brclev++;
+ continue;
+
+ case '}':
+ if (brclev) {
+ brclev--;
+ continue;
+ }
+ goto doit;
+
+ case ',':
+ if (brclev)
+ continue;
+doit:
+ savec = *pm;
+ *pm = 0;
+ (void) strcpy_(lm, pl);
+ (void) strcat_(restbuf, pe + 1);
+ *pm = savec;
+ if (s == 0) {
+ sgpathp = gpathp;
+ expand(restbuf);
+ gpathp = sgpathp;
+ *gpathp = 0;
+ } else if (amatch(s, restbuf, &slproc))
+ return (1);
+ sort();
+ pl = pm + 1;
+ continue;
+
+ case '[':
+ for (pm++; *pm && *pm != ']'; pm++)
+ continue;
+ if (!*pm)
+ error("Missing ]");
+ continue;
+ }
+ return (0);
+}
+
+match(s, p, slproc)
+ tchar *s, *p;
+ int *slproc;
+{
+ register int c;
+ register tchar *sentp;
+ tchar sglobbed = globbed;
+
+#ifdef TRACE
+ tprintf("TRACE- match()\n");
+#endif
+ if (*s == '.' && *p != '.')
+ return (0);
+ sentp = entp;
+ entp = s;
+ c = amatch(s, p, slproc);
+ entp = sentp;
+ globbed = sglobbed;
+ return (c);
+}
+
+amatch(s, p, slproc)
+ register tchar *s, *p;
+ int *slproc;
+{
+ register int scc;
+ int ok, lc;
+ tchar *sgpathp;
+ struct stat stb;
+ int c, cc;
+
+#ifdef TRACE
+ tprintf("TRACE- amatch()\n");
+#endif
+ globbed = 1;
+ for (;;) {
+ scc = *s++ & TRIM;
+ switch (c = *p++) {
+
+ case '{':
+ return (execbrc(p - 1, s - 1));
+
+ case '[':
+ ok = 0;
+ lc = TRIM;
+ while (cc = *p++) {
+ if (cc == ']') {
+ if (ok)
+ break;
+ return (0);
+ }
+ if (cc == '-') {
+#ifdef MBCHAR
+ wchar_t rc = *p++;
+ if (rc == ']') {
+ p--;
+ continue;
+ }
+ /*
+ * Both ends of the char range
+ * must belong to the same codeset.
+ */
+ if (sh_bracket_exp(scc, lc, rc))
+ ok++;
+#else /* !MBCHAR */
+ if (lc <= scc && scc <= (int) *p++)
+ ok++;
+#endif /* !MBCHAR */
+ } else
+ if (scc == (lc = cc))
+ ok++;
+ }
+ if (cc == 0)
+ error("Missing ]");
+ continue;
+
+ case '*':
+ if (!*p)
+ return (1);
+ if (*p == '/') {
+ p++;
+ goto slash;
+ } else if (*p == '*') {
+ s--;
+ continue;
+ }
+
+ for (s--; *s; s++)
+ if (amatch(s, p, slproc))
+ return (1);
+
+ return (0);
+
+ case 0:
+ return (scc == 0);
+
+ default:
+ if ((c & TRIM) != scc)
+ return (0);
+ continue;
+
+ case '?':
+ if (scc == 0)
+ return (0);
+ continue;
+
+ case '/':
+ if (scc)
+ return (0);
+slash:
+ if (*slproc) /* Need to expand "/" only once */
+ return (0);
+ else
+ *slproc = 1;
+
+ s = entp;
+ sgpathp = gpathp;
+ while (*s)
+ addpath(*s++);
+ addpath('/');
+ if (stat_(gpath, &stb) == 0 && isdir(stb))
+ if (*p == 0) {
+ Gcat(gpath, S_ /* "" */);
+ globcnt++;
+ } else
+ expand(p);
+ gpathp = sgpathp;
+ *gpathp = 0;
+ return (0);
+ }
+ }
+}
+
+Gmatch(s, p)
+ register tchar *s, *p;
+{
+ register int scc;
+ int ok, lc;
+ int c, cc;
+
+#ifdef TRACE
+ tprintf("TRACE- Gmatch()\n");
+#endif
+ for (;;) {
+ scc = *s++ & TRIM;
+ switch (c = *p++) {
+
+ case '[':
+ ok = 0;
+ lc = TRIM;
+ while (cc = *p++) {
+ if (cc == ']') {
+ if (ok)
+ break;
+ return (0);
+ }
+ if (cc == '-') {
+#ifdef MBCHAR
+ wchar_t rc = *p++;
+ /*
+ * Both ends of the char range
+ * must belong to the same codeset...
+ */
+ if (sh_bracket_exp(scc, lc, rc))
+ ok++;
+#else /* !MBCHAR */
+ if (lc <= scc && scc <= (int) *p++)
+ ok++;
+#endif /* !MBCHAR */
+ } else
+ if (scc == (lc = cc))
+ ok++;
+ }
+ if (cc == 0)
+ bferr("Missing ]");
+ continue;
+
+ case '*':
+ if (!*p)
+ return (1);
+ for (s--; *s; s++)
+ if (Gmatch(s, p))
+ return (1);
+ return (0);
+
+ case 0:
+ return (scc == 0);
+
+ default:
+ if ((c & TRIM) != scc)
+ return (0);
+ continue;
+
+ case '?':
+ if (scc == 0)
+ return (0);
+ continue;
+
+ }
+ }
+}
+
+Gcat(s1, s2)
+ tchar *s1, *s2;
+{
+ register tchar *p, *q;
+ int n;
+
+#ifdef TRACE
+ tprintf("TRACE- Gcat()\n");
+#endif
+ for (p = s1; *p++; )
+ ;
+ for (q = s2; *q++; )
+ ;
+ gnleft -= (n = (p - s1) + (q - s2) - 1);
+ if (gnleft <= 0 || ++gargc >= GAVSIZ)
+ error("Arguments too long");
+ gargv[gargc] = 0;
+ p = gargv[gargc - 1] = (tchar *) xalloc((unsigned)n*sizeof (tchar));
+
+ for (q = s1; *p++ = *q++; )
+ ;
+ for (p--, q = s2; *p++ = *q++; )
+ ;
+}
+
+addpath(c)
+ tchar c;
+{
+
+#ifdef TRACE
+ tprintf("TRACE- addpath()\n");
+#endif
+ if (gpathp >= lastgpathp)
+ error("Pathname too long");
+ *gpathp++ = c & TRIM;
+ *gpathp = 0;
+}
+
+rscan(t, f)
+ register tchar **t;
+ int (*f)();
+{
+ register tchar *p;
+
+#ifdef TRACE
+ tprintf("TRACE- rscan()\n");
+#endif
+ while (p = *t++)
+ while (*p)
+ (*f)(*p++);
+}
+
+trim(t)
+ register tchar **t;
+{
+ register tchar *p;
+
+#ifdef TRACE
+ tprintf("TRACE- trim()\n");
+#endif
+ while (p = *t++)
+ while (*p)
+ *p++ &= TRIM;
+}
+
+tglob(t)
+ register tchar **t;
+{
+ register tchar *p, c;
+
+#ifdef TRACE
+ tprintf("TRACE- tglob()\n");
+#endif
+ while (p = *t++) {
+ if (*p == '~')
+ gflag |= 2;
+ else if (*p == '{' && (p[1] == '\0' ||
+ p[1] == '}' && p[2] == '\0'))
+ continue;
+ while (c = *p++)
+ if (isglob(c))
+ gflag |= c == '{' ? 2 : 1;
+ }
+}
+
+tchar *
+globone(str)
+ register tchar *str;
+{
+ tchar *gv[2];
+ register tchar **gvp;
+ register tchar *cp;
+
+#ifdef TRACE
+ tprintf("TRACE- globone()\n");
+#endif
+ gv[0] = str;
+ gv[1] = 0;
+ gflag = 0;
+ tglob(gv);
+ if (gflag) {
+ gvp = glob(gv);
+ if (gvp == 0) {
+ setname(str);
+ bferr("No match");
+ }
+ cp = *gvp++;
+ if (cp == 0)
+ cp = S_ /* "" */;
+ else if (*gvp) {
+ setname(str);
+ bferr("Ambiguous");
+ } else
+ cp = strip(cp);
+/*
+ if (cp == 0 || *gvp) {
+ setname(str);
+ bferr(cp ? "Ambiguous" : "No output");
+ }
+*/
+ xfree((char *)gargv); gargv = 0;
+ } else {
+ trim(gv);
+ cp = savestr(gv[0]);
+ }
+ return (cp);
+}
+
+/*
+ * Command substitute cp. If literal, then this is
+ * a substitution from a << redirection, and so we should
+ * not crunch blanks and tabs, separating words only at newlines.
+ */
+tchar **
+dobackp(cp, literal)
+ tchar *cp;
+ bool literal;
+{
+ register tchar *lp, *rp;
+ tchar *ep;
+ tchar word[BUFSIZ];
+ tchar *apargv[GAVSIZ + 2];
+
+#ifdef TRACE
+ tprintf("TRACE- dobackp()\n");
+#endif
+ if (pargv) {
+ blkfree(pargv);
+ }
+ pargv = apargv;
+ pargv[0] = NOSTR;
+ pargcp = pargs = word;
+ pargc = 0;
+ pnleft = BUFSIZ - 4;
+ for (;;) {
+ for (lp = cp; *lp != '`'; lp++) {
+ if (*lp == 0) {
+ if (pargcp != pargs)
+ pword();
+#ifdef GDEBUG
+ printf("leaving dobackp\n");
+#endif
+ return (pargv = copyblk(pargv));
+ }
+ psave(*lp);
+ }
+ lp++;
+ for (rp = lp; *rp && *rp != '`'; rp++)
+ if (*rp == '\\') {
+ rp++;
+ if (!*rp)
+ goto oops;
+ }
+ if (!*rp)
+oops:
+ error("Unmatched `");
+ ep = savestr(lp);
+ ep[rp - lp] = 0;
+ backeval(ep, literal);
+#ifdef GDEBUG
+ printf("back from backeval\n");
+#endif
+ cp = rp + 1;
+ }
+}
+
+backeval(cp, literal)
+ tchar *cp;
+ bool literal;
+{
+ int pvec[2];
+ int quoted = (literal || (cp[0] & QUOTE)) ? QUOTE : 0;
+ tchar ibuf[BUFSIZ];
+ register int icnt = 0, c;
+ register tchar *ip;
+ bool hadnl = 0;
+ tchar *fakecom[2];
+ struct command faket;
+
+#ifdef TRACE
+ tprintf("TRACE- backeval()\n");
+#endif
+ faket.t_dtyp = TCOM;
+ faket.t_dflg = 0;
+ faket.t_dlef = 0;
+ faket.t_drit = 0;
+ faket.t_dspr = 0;
+ faket.t_dcom = fakecom;
+ fakecom[0] = S_QPPPQ; /* "` ... `" */;
+ fakecom[1] = 0;
+ /*
+ * We do the psave job to temporarily change the current job
+ * so that the following fork is considered a separate job.
+ * This is so that when backquotes are used in a
+ * builtin function that calls glob the "current job" is not corrupted.
+ * We only need one level of pushed jobs as long as we are sure to
+ * fork here.
+ */
+ psavejob();
+ /*
+ * It would be nicer if we could integrate this redirection more
+ * with the routines in sh.sem.c by doing a fake execute on a builtin
+ * function that was piped out.
+ */
+ mypipe(pvec);
+ if (pfork(&faket, -1) == 0) {
+ struct wordent paraml;
+ struct command *t;
+ tchar oHIST;
+
+ new_process();
+ (void) close(pvec[0]);
+ unsetfd(pvec[0]);
+ (void) dmove(pvec[1], 1);
+ (void) dmove(SHDIAG, 2);
+ reinitdesc(0, NULL);
+ arginp = cp;
+ while (*cp)
+ *cp++ &= TRIM;
+ /*
+ * disable history subsitution in sub-shell
+ * of `` evaluation prevents possible
+ * infinite recursion of `` evaluation
+ */
+ oHIST = HIST;
+ HIST = 0;
+ (void) lex(&paraml);
+ HIST = oHIST;
+ if (err)
+ error("%s", gettext(err));
+ alias(&paraml);
+ t = syntax(paraml.next, &paraml, 0);
+ if (err)
+ error("%s", gettext(err));
+ if (t)
+ t->t_dflg |= FPAR;
+ (void) signal(SIGTSTP, SIG_IGN);
+ (void) signal(SIGTTIN, SIG_IGN);
+ (void) signal(SIGTTOU, SIG_IGN);
+ execute(t, -1);
+ exitstat();
+ }
+ xfree(cp);
+ (void) close(pvec[1]);
+ unsetfd(pvec[1]);
+ do {
+ int cnt = 0;
+ for (;;) {
+ if (icnt == 0) {
+ ip = ibuf;
+ icnt = read_(pvec[0], ip, BUFSIZ);
+ if (icnt <= 0) {
+ c = -1;
+ break;
+ }
+ }
+ if (hadnl)
+ break;
+ --icnt;
+ c = (*ip++ & TRIM);
+ if (c == 0)
+ break;
+ if (c == '\n') {
+ /*
+ * Continue around the loop one
+ * more time, so that we can eat
+ * the last newline without terminating
+ * this word.
+ */
+ hadnl = 1;
+ continue;
+ }
+ if (!quoted && issp(c))
+ break;
+ cnt++;
+ psave(c | quoted);
+ }
+ /*
+ * Unless at end-of-file, we will form a new word
+ * here if there were characters in the word, or in
+ * any case when we take text literally. If
+ * we didn't make empty words here when literal was
+ * set then we would lose blank lines.
+ */
+ if (c != -1 && (cnt || literal)) {
+ if (pargc == GAVSIZ)
+ break;
+ pword();
+ }
+ hadnl = 0;
+ } while (c >= 0);
+#ifdef GDEBUG
+ printf("done in backeval, pvec: %d %d\n", pvec[0], pvec[1]);
+ printf("also c = %c <%o>\n", (tchar) c, (tchar) c);
+#endif
+ (void) close(pvec[0]);
+ unsetfd(pvec[0]);
+ pwait();
+ prestjob();
+}
+
+psave(c)
+ tchar c;
+{
+#ifdef TRACE
+ tprintf("TRACE- psave()\n");
+#endif
+
+ if (--pnleft <= 0)
+ error("Word too long");
+ *pargcp++ = c;
+}
+
+pword()
+{
+#ifdef TRACE
+ tprintf("TRACE- pword()\n");
+#endif
+
+ psave(0);
+ if (pargc == GAVSIZ)
+ error("Too many words from ``");
+ pargv[pargc++] = savestr(pargs);
+ pargv[pargc] = NOSTR;
+#ifdef GDEBUG
+ printf("got word %t\n", pargv[pargc-1]);
+#endif
+ pargcp = pargs;
+ pnleft = BUFSIZ - 4;
+}
+
+
+
+/*
+ * returns pathname of the form dir/file;
+ * dir is a null-terminated string;
+ */
+char *
+makename(dir, file)
+ char *dir;
+ char *file;
+{
+ /*
+ * Maximum length of a
+ * file/dir name in ls-command;
+ * dfile is static as this is returned
+ * by makename();
+ */
+ static char dfile[MAXNAMLEN];
+
+ register char *dp, *fp;
+
+ dp = dfile;
+ fp = dir;
+ while (*fp)
+ *dp++ = *fp++;
+ if (dp > dfile && *(dp - 1) != '/')
+ *dp++ = '/';
+ fp = file;
+ while (*fp)
+ *dp++ = *fp++;
+ *dp = '\0';
+ /*
+ * dfile points to the absolute pathname. We are
+ * only interested in the last component.
+ */
+ return (rindex(dfile, '/') + 1);
+}
+
+sh_bracket_exp(t_ch, t_fch, t_lch)
+tchar t_ch;
+tchar t_fch;
+tchar t_lch;
+{
+ char t_char[MB_LEN_MAX + 1];
+ char t_patan[MB_LEN_MAX * 2 + 8];
+ char *p;
+ int i;
+
+ if ((t_ch == t_fch) || (t_ch == t_lch))
+ return(1);
+
+ p = t_patan;
+ if ((i = wctomb(t_char, (wchar_t)t_ch)) <= 0)
+ return(0);
+ t_char[i] = 0;
+
+ *p++ = '[';
+ if ((i = wctomb(p, (wchar_t)t_fch)) <= 0)
+ return(0);
+ p += i;
+ *p++ = '-';
+ if ((i = wctomb(p, (wchar_t)t_lch)) <= 0)
+ return(0);
+ p += i;
+ *p++ = ']';
+ *p = 0;
+
+ if (fnmatch(t_patan, t_char, FNM_NOESCAPE))
+ return(0);
+ return(1);
+}