home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 5 Edit
/
05-Edit.zip
/
me34src.zip
/
me3
/
util
/
fxpand.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-01-14
|
15KB
|
532 lines
/* fxpand.c : fxpand(): expand file names
* Input:
* name: A file name which can contain: ?, \, [], [^] and *
* If name ends with "." (eg *.) then only names with no extensions will
* be matched.
* If name begins with ~ then: "~/" expands to "$(HOME)/" and "~name"
* expands to "<home directory of name>".
* MS-DOS: \ is same as /. Does not quote.
* UNIX: \ quotes the next character.
* ATARI (from jwahar r. bammi (bammi@cadence.com)):
* Like MS-DOS, \ == /, except that we have the POSIX opendir() etc.
* onlyone: If TRUE return the first match otherwise all matches are
* returned. For example, this controls how "*" is expanded.
* nopath: If TRUE, only return the matching file names. Otherwise
* return the path and name. For example, "/foo/bar" => "bar".
* slash_dir: If TRUE, append a slash to the end of a file name if the
* file is a directory. For example, input of "dir" would generate
* "dir/".
* name_heap:
* NULL: Call process_fname.
* Pointer to char[]: Stuff the file names in here. The names are
* separated by a blank and there are no trailing blanks.
* process_fname: A function which is passed a char *. The string
* contains the expanded file name.
* Returns: 0 if all OK, >1 an error to be returned by fxpand().
* Output:
* 0: All OK
* 1: Something screwed up. Most likely the name is bad.
* n: An error code returned by process_fname().
*
* Notes:
* When compiled on Apollo, this routine also works with the Domain/OS
* "//" notation. This is mostly luck - I don't collapse "/"s and a
* relaxed check lets this work.
* Input error checking is pretty grim.
* Unix Notes:
* When wildcard matching, hidden files (those that start with ".") are
* skipped unless you ask to see them. To match ".fred", you could use
* ".f*". To match "fred/.sam/geo", you would need something like
* "fred/.s* /g*".
* When appending slashes (slash_dir), expanding something like "*" can be
* very slow. This is because I have to stat() to find out if the file
* is a directory and stat() can take a long time to stat files over nfs
* mounts, follow links, etc.
*
* Craig Durland
*/
/* Copyright 1989, 1990, 1991 Craig Durland
* Distributed under the terms of the GNU General Public License.
* Distributed "as is", without warranties of any kind, but comments,
* suggestions and bug reports are welcome.
*/
#ifdef __STDC__
#ifdef __hpux /* for ANSI C on HP-UX */
#define _HPUX_SOURCE
#endif /* __hpux */
#ifdef __apollo /* for ANSI C on Apollo BSD */
#define _BSD_SOURCE
#endif /* __apollo */
#ifdef _AIX /* for ANSI C on IBM AIX */
#define _POSIX_SOURCE
#endif /* _AIX */
#endif /* __STDC__ */
#include <stdio.h>
#include "os.h"
#include "const.h"
extern char *strchr(), *getenv(), *strcpy(), *strcat();
static char *name_list;
static int prepend_blank;
static int stuff_name(name) char *name;
{
if (prepend_blank) strcat(name_list," ");
else prepend_blank = TRUE;
strcat(name_list,name);
return 0;
}
#if MSDOZ
#define SLASH "/" /* "/" or "\\", your preference */
/* A note about MS-DOS: Its file find routines are brain dead: If you
* ask for a directory, you will get all file types. Also, since
* there is a file type==0, you can't just use AND to filter out
* unwanted types. What a pain in the butt!
*/
#include <dos.h>
#ifdef LATTICE /* from dos.h */
typedef struct FILEINFO File_Info; /* in dos.h */
#define FIND_DIRECTORY(info, name, attr) dfind(info, name, attr)
#define NEXT_DIRECTORY(info) dnext(info)
#define ATTR attr
#define NAME name
#else /* JPI Topspeed C, Turbo C, hopefully others */
#include <dir.h>
typedef struct ffblk File_Info; /* in dos.h */
#define FIND_DIRECTORY(info, name, attr) findfirst(name, info, attr)
#define NEXT_DIRECTORY(info) findnext(info)
#define ATTR ff_attrib
#define NAME ff_name
#endif /* LATTICE */
#define FATTR (FA_ARCHIVE | FA_RD_ONLY | FA_DIRECTORY) /* file attributes */
fxpand(name, onlyone,nopath,slash_dir, name_heap,process_fname)
char *name,*name_heap; pfi process_fname;
{
char path[128], word[100], *ptr, *qtr, tbuf[128];
File_Info de;
int atend, eval, found, needadot, path_len, s;
if (name_heap) /* store the found file names in name_heap */
{
process_fname = stuff_name;
*(name_list = name_heap) = '\0';
prepend_blank = FALSE;
}
*path = '\0';
if (*name == '~')
{
name++;
if (ptr = getenv("HOME")) strcpy(path,ptr);
}
else
if (name[1] == ':')
{ strncpy(path,name,2); path[2] = '\0'; name += 2; }
atend = FALSE; needadot = FALSE;
while (!atend)
{
atend = get_dpart(&name,word,&eval);
path_len = strlen(path);
uppercase(word); /* Since the directory entries are uppercase */
if (eval) /* wildcards in need of expansion */
{
if (word[strlen(word)-1] == '.') needadot = TRUE;
found = FALSE;
ptr = path +path_len; strcpy(ptr,"*.*");
if (FIND_DIRECTORY(&de,path,FATTR)) return 1; /* No files found */
*ptr = '\0'; /* get rid of "*.*" */
do /* look at all entries in this directory */
{
if (!atend) /* only care about directories */
{ if (de.ATTR != FA_DIRECTORY) continue; }
else
if (de.ATTR && /* if de.ATTR==0, its a regular old file */
(de.ATTR & FATTR) == 0) continue;
ptr = qtr = de.NAME;
if (needadot && !strchr(ptr,'.')) ptr = strcat(strcpy(tbuf,ptr),".");
if (*qtr != '.' && wildmat(ptr,word)) /* ignore . & .. */
{
found = TRUE;
if (!atend) /* something like foo/<you are here>/bar */
{
strcat(strcat(path,qtr), SLASH);
break;
}
else /* something like foo/<you are here> */
{
strcpy(path +path_len, qtr);
s = procz(process_fname, slash_dir && (de.ATTR == FA_DIRECTORY),
path, nopath ? path_len : 0);
path[path_len] = '\0';
if (s) return s;
if (onlyone) break;
}
}
} while (!NEXT_DIRECTORY(&de));
if (!found) return 1;
}
else
{
strcpy(path + path_len, word);
if (atend) /* all done */
return procz(process_fname, slash_dir && is_dir(path),
path, nopath ? path_len : 0);
/* ??? if (!is_dir(path)) return 1; /* Make sure path is real */
}
} /* end while */
return 0;
}
static int procz(process_fname, slash_it, path, path_len)
pfi process_fname; char *path;
{
if (slash_it && (path[path_len] != '\0')) strcat(path, "/");
return (*process_fname)(path + path_len);
}
/*
* Notes:
* For some (unknown to me) reason, FA_DIRECTORY matches everything,
* not just directories.
* WARNING:
* This routine changes the DTA. If you are in the middle of a
* FIND_DIRECTORY/NEXT_DIRECTORY loop, this will mess that up.
*/
static int is_dir(path) char *path;
{
File_Info de;
return (!FIND_DIRECTORY(&de,path,FA_DIRECTORY) && (de.ATTR == FA_DIRECTORY));
}
/* MS-DOS stuff for get_dpart() */
#define ASLASH case '/': case '\\'
#define GOTTA_EVAL case '?': case '[': case '*'
#endif /* MSDOZ */
#if ATARI /* Atari has Posix opendir(), etc */
#undef POSIX_OS
#define POSIX_OS 1 /* turn on POSIX and UX_OS */
#endif /* ATARI */
#if UX_OS /* and Atari */
#define BADGETPW 1 /* 1 if system getpw... routines are screwed up */
#include <sys/types.h>
#include <pwd.h>
#include <sys/stat.h>
/* Posix, SysV: HP-UX, Apollo SysV, DEC, Atari */
/* defined(POSIX) is a DECism */
#if POSIX_OS || SYSV_OS || defined(POSIX)
#include <dirent.h>
#else /* Pure BSD: Apollo bsd4.3 */
#include <sys/dir.h>
#endif
static int get_dpart(), getpwhome(), procz(), is_dir();
/* cases to check:
* "~", "~fred", "/", "~/", ""
*/
fxpand(name, onlyone,nopath,slash_dir, name_heap,process_fname)
char *name,*name_heap; pfi process_fname;
{
char path[512], word[256], *ptr, *qtr, tbuf[256];
DIR *dir;
#if POSIX_OS || SYSV_OS || defined(POSIX)
struct dirent *dtr;
#else /* Apollo bsd4.3, (some)DEC */
struct direct *dtr;
#endif
int
atend, needadot,
eval, found, path_len, s,
skip_dot_files; /* ignore names starting with "." */
struct passwd *pd;
if (name_heap) /* store the found file names in name_heap */
{
process_fname = stuff_name;
*(name_list = name_heap) = '\0';
prepend_blank = FALSE;
}
*path = '\0';
if (*name == '~') /* csh/ksh home directory expansion */
{
name++;
if (ISSLASH(*name) || *name == '\0') /* ~/foo/bar or ~ */
{
if (ptr = getenv("HOME")) strcpy(path,ptr);
else /* no $HOME, see if the OS knows */
{
#if BADGETPW
return 1; /* !!! a sleeze */
#else
if ((pd = getpwuid(getuid())) == NULL) return 1;
strcpy(path,pd->pw_dir);
#endif /* BADGETPW */
}
}
else /* ~fred => user freds' home directory */
{
atend = get_dpart(&name,word,&eval);
if (eval) return 1; /* no wildcards allowed in user name */
name--;
if (!atend) word[strlen(word)-1] = '\0'; /* remove "/" from "~fred/" */
#if BADGETPW
if (!getpwhome(word)) return 1;
strcpy(path,word);
#else
if ((pd = getpwnam(word)) == NULL) return 1;
strcpy(path,pd->pw_dir);
#endif /* BADGETPW */
}
}
/* at this point, maybe: strlen(path)!=0 && strlen(name)==0 */
atend = FALSE; needadot = FALSE;
while (!atend)
{
atend = get_dpart(&name,word,&eval);
skip_dot_files = (*word != '.'); /* ".fred" means look at dot files */
path_len = strlen(path);
if (eval) /* wildcards in need of expansion */
{
if (word[strlen(word)-1] == '.') needadot = TRUE;
found = FALSE;
if ((dir = opendir(path_len == 0 ? "." : path)) == NULL) return 1;
while (TRUE) /* look at all entries in this directory */
{
if ((dtr = readdir(dir)) == NULL) break;
ptr = qtr = dtr->d_name;
if (skip_dot_files && *ptr == '.') continue;
if (needadot && !strchr(ptr,'.')) ptr = strcat(strcpy(tbuf,ptr),".");
if (wildmat(ptr,word))
{
if (!atend) /* something like foo/<you are here>/bar */
{
strcpy(path + path_len, qtr);
/* make sure its a real directory */
if (is_dir(path)) { strcat(path,"/"); found = TRUE; break; }
}
else /* something like foo/<you are here> */
{
found = TRUE;
strcpy(path +path_len, qtr);
s = procz(process_fname, slash_dir, path, nopath ? path_len : 0);
path[path_len] = '\0';
if (s) { closedir(dir); return s; }
if (onlyone) break;
}
}
} /* end while (TRUE) */
closedir(dir);
if (!found) return 1;
}
else /* No wildcards: something like: .../bar/... or .../bar */
{ /* word may == "" (for input like "~fred") */
strcpy(path + path_len, word);
if (atend) /* all done */
return procz(process_fname, slash_dir, path, nopath ? path_len : 0);
/* ??? if (!is_dir(path)) return 1; /* Make sure path is real */
}
} /* end while */
return 0;
}
/* Input:
* process_fname: pointer to function to call
* slash_dir: TRUE: if path is a directory, append a slash
* path: Full path name
* path_len: offset in path. Used if what to pass just part of path
* to process_fname.
* Returns:
* What ever process_fname returns.
* Notes:
* Need to check path[path_len] before appending a slash because: if
* don't want a path (path_len != 0) and no name (eg expanding "~"),
* would process_fname("/") which is not what is expected.
*/
static int procz(process_fname, slash_dir, path, path_len)
pfi process_fname; char *path;
{
if (slash_dir && (path[path_len] != '\0') && is_dir(path))
strcat(path, "/");
return (*process_fname)(path + path_len);
}
static int is_dir(path) char *path;
{
struct stat sbuf;
if (stat(path,&sbuf)) return FALSE;
/* make sure its a directory */
#ifdef S_ISDIR
return S_ISDIR(sbuf.st_mode);
#else
return ((sbuf.st_mode & 0170000) == 040000);
#endif /* S_ISDIR */
}
#if BADGETPW
/* Get the home directory out of the password file.
* Only use this if the system getpw... routines are
* screwed up.
*/
static int getpwhome(name) char *name;
{
char buf[256], *ptr;
FILE *fptr;
int n, s = FALSE;
if ((fptr = fopen("/etc/passwd","r")) == NULL) return FALSE;
while (fgets(buf,255,fptr))
{
for (ptr = buf; *ptr != ':'; ptr++) ; *ptr = '\0';
if (strcmp(name,buf)) continue;
for (n = 0; n < 4; ptr++) if (*ptr == ':') n++;
while (*ptr != ':') *name++ = *ptr++; *name = '\0';
s = TRUE;
break;
}
fclose(fptr);
return s;
}
#endif /* BADGETPW */
#if ATARI /* Atari stuff for get_dpart(), same as MS-DOS */
#define ASLASH case '/': case '\\'
#define GOTTA_EVAL case '?': case '[': case '*'
#else
/* UNIX stuff for get_dpart() */
#define ASLASH case '/'
#define GOTTA_EVAL case '?': case '\\': case '[': case '*'
#endif /* ATARI */
#endif /* UX_OS || ATARI */
/* Get the next part of the filename (ie the stuff between "/"s). The
* parts of "/foo/*.c" are: "/", "foo", "*.c".
* Input:
* Output:
* eval: TRUE if part contains wildcards needing expansion.
* word: The part.
* start: Points after the part (after the "/" or '\0').
* Returns:
* TRUE: Hit the end of the filename else FALSE.
*/
static int get_dpart(start,word,eval) char **start, *word; int *eval;
{
register char *ptr = *start;
int atend = TRUE;
*eval = FALSE;
while (TRUE)
{
switch (*word++ = *ptr++)
{
ASLASH:
atend = FALSE;
if (*eval) word--; /* remove trailing "/" */
case '\0': *word = '\0'; *start = ptr; return atend;
GOTTA_EVAL: *eval = TRUE; break;
}
}
}
#ifdef TEST
/* **************** TEST ******************************************** */
#include "dtable.h"
declare_and_init_dTable(ntable,char *);
extern char *savestr();
add_to_table(name) char *name;
{
static int n = 0;
xpand_dTable(&ntable, 1, 50, 25);
ntable.table[n++] = savestr(name);
return 0;
}
char name_heap[3000];
main()
{
char buf[80], *zim = NULL;
int j, nopath;
printf("Use name_heap? "); gets(buf); if (atoi(buf)) zim = name_heap;
printf("No path? "); gets(buf); nopath = atoi(buf);
printf(": "); gets(buf);
if (fxpand(buf,FALSE,nopath,zim,add_to_table)) puts("blew up");
else
{
if (zim) printf(">%s<\n",zim);
else
{
for (j = 0; j < sizeof_dTable(&ntable); j++)
printf("table[%d]: %s\n",j,ntable.table[j]);
}
}
}
#endif