www.pudn.com > socks5.zip > glob.c


/* 
 * Copyright (c) 1980 Regents of the University of California. 
 * All rights reserved. 
 * 
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions 
 * are met: 
 * 1. Redistributions of source code must retain the above copyright 
 *    notice, this list of conditions and the following disclaimer. 
 * 2. Redistributions in binary form must reproduce the above copyright 
 *    notice, this list of conditions and the following disclaimer in the 
 *    documentation and/or other materials provided with the distribution. 
 * 3. All advertising materials mentioning features or use of this software 
 *    must display the following acknowledgement: 
 *	This product includes software developed by the University of 
 *	California, Berkeley and its contributors. 
 * 4. Neither the name of the University nor the names of its contributors 
 *    may be used to endorse or promote products derived from this software 
 *    without specific prior written permission. 
 * 
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
 * SUCH DAMAGE. 
 */ 
 
/* 
 * C-shell glob for random programs. 
 */ 
#include "ftp_var.h" 
 
#ifdef HAVE_SYS_STAT_H 
#include  
#endif 
 
#ifdef HAVE_DIRENT_H 
# include  
# define NLENGTH(direct) (strlen((direct)->d_name)) 
#else /* not HAVE_DIRENT_H */ 
# define dirent direct 
# define NLENGTH(direct) ((direct)->d_namlen) 
# ifdef HAVE_SYS_NDIR_H 
#  include  
# endif /* HAVE_SYS_NDIR_H */ 
# ifdef HAVE_SYS_DIR_H 
#  include  
# endif /* HAVE_SYS_DIR_H */ 
# ifdef HAVE_NDIR_H 
#  include  
# endif /* HAVE_NDIR_H */ 
#endif /* HAVE_DIRENT_H */ 
 
#include  
#include  
#include  
#include  
#include  
 
#define	QUOTE 0200 
#define	TRIM 0177 
#define	eq(a,b)		(strcmp(a, b)==0) 
 
#ifndef NCARGS 
#define NCARGS 256 
#endif 
 
#define	GAVSIZ		(NCARGS/6) 
#define	isdir(d)	((d.st_mode & S_IFMT) == S_IFDIR) 
 
static	char **gargv;		/* Pointer to the (stack) arglist */ 
static	int gargc;		/* Number args in gargv */ 
static	int gnleft; 
static	short gflag; 
 
static	char *gpath, *gpathp, *lastgpathp; 
static	int globbed, globcnt; 
static	char **sortbas; 
static	char *entp; 
 
char	*globchars = "`{[*?"; 
char	*globerr; 
char	*home; 
 
extern	int errno; 
 
static int match P((char *, char *)); 
static int execbrc P((char *, char *)); 
 
int any(register int c, register char *s) { 
    while (*s) if (*s++ == c) return(1); 
    return(0); 
} 
 
static int tglob(register char c) { 
    if (any(c, globchars)) gflag |= c == '{' ? 2 : 1; 
    return (c); 
} 
 
 
int letter(register char c) { 
    return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'); 
} 
 
int digit(register char c) { 
    return (c >= '0' && c <= '9'); 
} 
 
int blklen(register char **av) { 
    register int i = 0; 
     
    while (*av++) i++; 
    return (i); 
} 
 
char ** blkcpy(char **oav, register char **bv) { 
    register char **av = oav; 
     
    while ((*av++ = *bv++)); 
    return (oav); 
} 
 
void blkfree(char **av0) { 
    register char **av = av0; 
     
    /* if gflag is 0, we did not malloc() the space! */ 
    if (!av || !gflag) return; 
 
    while (*av)	free(*av++); 
} 
 
static void ginit(char **agargv) { 
    agargv[0] = 0; gargv = agargv; sortbas = agargv; gargc = 0; 
    gnleft = NCARGS - 4; 
} 
 
static void sort() { 
    register char **p1, **p2, *c; 
    char **Gvp = &gargv[gargc]; 
     
    p1 = sortbas; 
    while (p1 < Gvp-1) { 
	p2 = p1; 
	while (++p2 < Gvp) 
	    if (strcmp(*p1, *p2) > 0) 
		c = *p1, *p1 = *p2, *p2 = c; 
	p1++; 
    } 
    sortbas = Gvp; 
} 
 
static void addpath(char c) { 
    if (gpathp >= lastgpathp) 
	globerr = "Pathname too long"; 
    else { 
	*gpathp++ = c; 
	*gpathp = 0; 
    } 
} 
 
static char *strspl(register char *cp, register char *dp) { 
    register char *ep = (char *)malloc((unsigned)(strlen(cp) + strlen(dp) + 1)); 
 
    if (ep == NULL) fatal("Out of memory"); 
    strcpy(ep, cp); 
    strcat(ep, dp); 
    return (ep); 
} 
 
static char *strend(register char *cp) { 
    while (*cp) cp++; 
    return (cp); 
} 
 
static void Gcat(register char *s1, register char *s2) { 
    register int len = strlen(s1) + strlen(s2) + 1; 
     
    if (len >= gnleft || gargc >= GAVSIZ - 1) 
	globerr = "Arguments too long"; 
    else { 
	gargc++; 
	gnleft -= len; 
	gargv[gargc] = 0; 
	gargv[gargc - 1] = strspl(s1, s2); 
    } 
} 
 
/* 
 * Extract a home directory from the password file 
 * The argument points to a buffer where the name of the 
 * user whose home directory is sought is currently. 
 * We write the home directory of the user back there. 
 */ 
int gethdir(char *home) { 
    register struct passwd *pp = getpwnam(home); 
     
    if (!pp || home + strlen(pp->pw_dir) >= lastgpathp) return (1); 
    strcpy(home, pp->pw_dir); 
    return (0); 
} 
 
static void matchdir(char *pattern) { 
    register struct dirent *dp; 
    DIR *dirp; 
     
    /*  
     * opendir() does not accept a null directory string 
     * to mean the current directory.  Must use ".". But leave 
     *  the gpath prefix alone 
     */   
    if (*gpath == 0) 
	dirp = opendir("."); 
    else 
	dirp = opendir(gpath); 
    if (dirp == NULL) { 
	if (globbed) return; 
	goto patherr2; 
    } 
#ifndef linux 
    { 
	struct stat stb; 
 
	if (fstat(dirp->dd_fd, &stb) < 0) 
	    goto patherr1; 
	if (!isdir(stb)) { 
	    errno = ENOTDIR; 
	    goto patherr1; 
	} 
    } 
#endif /* linux */ 
    while ((dp = readdir(dirp)) != NULL) { 
	if (dp->d_ino == 0) 
	    continue; 
	if (match(dp->d_name, pattern)) { 
	    Gcat(gpath, dp->d_name); 
	    globcnt++; 
	} 
    } 
 
    closedir(dirp); 
    return; 
 
#ifndef linux 
  patherr1: 
#endif 
    closedir(dirp); 
  patherr2: 
    globerr = "Bad directory components"; 
} 
 
static void expand(char *as) { 
    register char *cs; 
    register char *sgpathp, *oldcs; 
    struct stat stb; 
     
    sgpathp = gpathp; 
    cs = as; 
    if (*cs == '~' && gpathp == gpath) { 
	addpath('~'); 
	for (cs++; letter(*cs) || digit(*cs) || *cs == '-';) 
	    addpath(*cs++); 
	if (!*cs || *cs == '/') { 
	    if (gpathp != gpath + 1) { 
		*gpathp = 0; 
		if (gethdir(gpath + 1)) 
		    globerr = "Unknown user name after ~"; 
		strcpy(gpath, gpath + 1); 
	    } else 
		strcpy(gpath, home); 
	    gpathp = strend(gpath); 
	} 
    } 
    while (!any(*cs, globchars)) { 
	if (*cs == 0) { 
	    if (!globbed) 
		Gcat(gpath, ""); 
	    else if (stat(gpath, &stb) >= 0) { 
		Gcat(gpath, ""); 
		globcnt++; 
	    } 
	    goto endit; 
	} 
	addpath(*cs++); 
    } 
    oldcs = cs; 
    while (cs > as && *cs != '/') 
	cs--, gpathp--; 
    if (*cs == '/') 
	cs++, gpathp++; 
    *gpathp = 0; 
    if (*oldcs == '{') { 
	execbrc(cs, ((char *)0)); 
	return; 
    } 
    matchdir(cs); 
  endit: 
    gpathp = sgpathp; 
    *gpathp = 0; 
} 
 
static int amatch(register char *s, register char *p) { 
	register int scc; 
	int ok, lc; 
	char *sgpathp; 
	struct stat stb; 
	int c, cc; 
 
	globbed = 1; 
	for (;;) { 
		scc = *s++ & TRIM; 
		switch (c = *p++) { 
		    case '{': 
			return (execbrc(p - 1, s - 1)); 
			 
		    case '[': 
			ok = 0; 
			lc = 077777; 
			while ((cc = *p++)) { 
			    if (cc == ']') { 
				if (ok) break; 
				return (0); 
			    } 
			    if (cc == '-') { 
				if (lc <= scc && scc <= *p++) ok++; 
			    } else  
			    if (scc == (lc = cc)) ok++; 
			} 
			 
			if (cc == 0) 
			    if (!ok) return 0; 
			    else p--; 
			 
			continue; 
		    case '*': 
			if (!*p) return (1); 
			 
			if (*p == '/') { 
			    p++; 
			    goto slash; 
			} 
			 
			s--; 
			 
			do { 
			    if (amatch(s, p)) return (1); 
			} while (*s++); 
			return (0); 
			 
		    case 0: 
			return (scc == 0); 
			 
		    default: 
			if (c != scc) return (0); 
			continue; 
			 
		    case '?': 
			if (scc == 0) return (0); 
			continue; 
			 
		    case '/': 
			if (scc) return (0); 
 
		  slash: 
			s = entp; 
			sgpathp = gpathp; 
			while (*s) addpath(*s++); 
			addpath('/'); 
			if (stat(gpath, &stb) == 0 && isdir(stb)) 
			    if (*p == 0) { 
				Gcat(gpath, ""); 
				globcnt++; 
			    } else 
				expand(p); 
			gpathp = sgpathp; 
			*gpathp = 0; 
			return (0); 
		} 
	} 
} 
 
static int match(char *s, char *p) { 
	register int c; 
	register char *sentp; 
	char sglobbed = globbed; 
 
	if (*s == '.' && *p != '.') 
		return (0); 
	sentp = entp; 
	entp = s; 
	c = amatch(s, p); 
	entp = sentp; 
	globbed = sglobbed; 
	return (c); 
} 
 
static int execbrc(char *p, char *s) { 
    char restbuf[BUFSIZ + 2]; 
    register char *pe, *pm, *pl; 
    int brclev = 0; 
    char *lm, savec, *sgpathp; 
     
    for (lm = restbuf; *p != '{'; *lm++ = *p++);  
 
    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; 
	} 
pend: 
    brclev = 0; 
 
    for (pl = pm = p; pm <= pe; pm++) 
	switch (*pm & (QUOTE|TRIM)) { 
	    case '{': 
		brclev++; 
		continue; 
	    case '}': 
		if (brclev) { 
		    brclev--; 
		    continue; 
		} 
		goto doit; 
	    case ','|QUOTE: 
	    case ',': 
		if (brclev) continue; 
doit: 
		savec = *pm; 
		*pm = 0; 
		strcpy(lm, pl); 
		strcat(restbuf, pe + 1); 
		*pm = savec; 
 
		if (s == 0) { 
		    sgpathp = gpathp; 
		    expand(restbuf); 
		    gpathp = sgpathp; 
		    *gpathp = 0; 
		} else if (amatch(s, restbuf)) { 
		    return (1); 
		} 
 
		sort(); 
		pl = pm + 1; 
		if (brclev) return (0); 
		continue; 
	    case '[': 
		for (pm++; *pm && *pm != ']'; pm++); 
		if (!*pm) pm--; 
		continue; 
	} 
 
    if (brclev) goto doit; 
    return (0); 
} 
 
static void acollect(register char *as) { 
    register int ogargc = gargc; 
     
    gpathp = gpath; *gpathp = 0; globbed = 0; 
    expand(as); 
 
    if (gargc != ogargc) sort(); 
} 
 
static void collect(register char *as) { 
    if (eq(as, "{") || eq(as, "{}")) { 
	Gcat(as, ""); 
	sort(); 
    } else 
	acollect(as); 
} 
 
static int Gmatch(register char *s, register char *p) { 
    register int scc; 
    int ok, lc; 
    int c, cc; 
     
    for (;;) { 
	scc = *s++ & TRIM; 
	switch (c = *p++) { 
	    case '[': 
		ok = 0; 
		lc = 077777; 
 
		while ((cc = *p++)) { 
		    if (cc == ']') { 
			if (ok) break; 
			return (0); 
		    } 
		    if (cc == '-') { 
			if (lc <= scc && scc <= *p++) ok++; 
		    } else 
			if (scc == (lc = cc)) ok++; 
		} 
 
		if (cc == 0) 
		    if (!ok) return 0; 
		    else p--; 
 
		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; 
	} 
    } 
} 
 
static void rscan(register char **t, int (*f)P((char))) { 
    register char *p, c; 
     
    while ((p = *t++)) { 
	if (f == tglob) 
	    if (*p == '~') 
		gflag |= 2; 
	    else if (eq(p, "{") || eq(p, "{}")) 
		continue; 
	while ((c = *p++)) (*f)(c); 
    } 
} 
 
char **copyblk(register char **v) { 
    register char **nv = (char **)malloc((unsigned)((blklen(v) + 1) * sizeof(char **))); 
 
    if (nv == (char **)0) fatal("Out of memory"); 
    return (blkcpy(nv, v)); 
} 
 
char **ftpglob(register char *v) { 
	char agpath[BUFSIZ]; 
	char *agargv[GAVSIZ]; 
	char *vv[2]; 
 
	vv[0] = v; 
	vv[1] = 0; 
 
	gflag = 0; 
	rscan(vv, tglob); 
 
	if (gflag == 0) 
		return (copyblk(vv)); 
 
	globerr = 0; 
	gpath = agpath; gpathp = gpath; *gpathp = 0; 
	lastgpathp = &gpath[sizeof agpath - 2]; 
	ginit(agargv); globcnt = 0; 
	collect(v); 
 
	if (globcnt == 0 && (gflag&1)) { 
	    blkfree(gargv), gargv = 0; 
	    return (0); 
	} else { 
	    return (gargv = copyblk(gargv)); 
	} 
}