root@hobbes.UUCP (07/16/87)
[There were uuencodes with this. I have split them off; they are in comp.binaries.ibm.pc. ++bsa] Here is a routine which does wildcard expansion for MS-DOS. The shar which follows contains: glob.c glob.h - the actual wildcard expansion routines example.c - a very simple example of its usage chd.c - a more complex example - implements chdir Have fun, mail me if you have problems... John Plocher UUCP: <backbone>!uwvax!geowhiz!uwspan!plocher ============== Internet: plocher%uwspan.UUCP@uwvax.cs.Wisc.EDU FidoNet: 121/0 BITNET: uwvax!geowhiz!uwspan!plocher@psuvax1 *-----*-----*-----*-----*-----*-----*-----*-----*-----*-----* #!/bin/sh # This is a shell archive created by yasa (1.72jmp). # Run the following text with /bin/sh to extract. # # Archive: /usr/src/new/dos/cd/wildcard.shar # # This archive is a complete distribution by itself. # # Packed under system5 by plocher@hobbes (John Plocher) # Packed on Tue Jul 14 20:13:11 1987 # YASASTART=`pwd` echo x - README if [ -s README ] # exists & size>0 then echo "shar: will not overwrite 'README'" else sed "s/^X//" << \!SHAR-EOF! > README X Here is a routine which does wildcard expansion for MS-DOS. XIt could be spiffed up by using the regex routines by Henry Spencer, but Xfor now it expands DOS style wildcards (*,?): (This is also found in Xthe file example.c) X X X #include "glob.h" X main() { X char **names; X names = glob("*.c"); X if (names) X printf("The first file which matches the wildcard: %s\n",names[0]); X else X printf("glob() error number %d\n",globerror); X } X XIt also handles the case of "/usr/pl*/bin/*.exe" where "/usr/pl*/bin" is unique. XUnfortunately, the code does not handle "/*/*/*.c" type expansions. This is Xan arbitrary limitation which is left to the student to implement. :-))))) X XIt also does not handle "C:/bin/*.exe" - the drive spec fouls it up, XMS-DOS seems to have problems with the find first/next functions in the Xroot directory so "/*.c" may give you problems. X XThe shar which follows contains: X glob.c glob.h - the actual wildcard expansion routines X example.c - a very simple example of its usage X chd.c - a more complex example - implements chdir X chd.exe (uuencoded) - compiled version of chd.c X glob.obj (uuencoded) - linkable module containing glob() X XHave fun, mail me if you have problems... X X(PS I am not actively using MS-DOS for now, so this code has been sitting X"on the shelf" for a bit. I saw a few comments in comp.sys.ibm.pc concerning XTurbo C and its lack of wildcard support, so I figured I'd send this out.) X X John Plocher UUCP: <backbone>!uwvax!geowhiz!uwspan!plocher X============== Internet: plocher%uwspan.UUCP@uwvax.cs.Wisc.EDU XFidoNet: 121/0 BITNET: uwvax!geowhiz!uwspan!plocher@psuvax1 !SHAR-EOF! if [ "`wc -c README`" != " 1638 README" ] then echo ' README may be bad' fi fi echo x - chd.c if [ -s chd.c ] # exists & size>0 then echo "shar: will not overwrite 'chd.c'" else sed "s/^X//" << \!SHAR-EOF! > chd.c X X/* X * chd.c change directory X * John Plocher X * X * usage: chd [new directory] X * X * [new directory] may contain wildcards; it may not be ambiguous X * If no arguments, use $HOME variable in environment X * X * C:\BIN > set HOME=/usr/plocher X * C:\BIN > chd \u*\lo* X * C:\USR\LOCAL > chd X * C:\USR\PLOCHER > X * X * uses glob() to expand wildcards in pathname X * X * returns (ERRORLEVEL) X * X * 0 No error X * 1 could not find directory X * 2 ambiguous pathname X * 3 directoryname expected, you gave a filename X * 4 internal error X * X * Change log X * X * Version Date Who Comments X * ------- ---- --- -------- X * 1.00 16-Jan-86 jmp Initially coded X * 1.01 21-Jan-86 jmp Expanded help menu (-?), fixed glob() X */ X X X#include <stdio.h> X#include "glob.h" X X#define VERSION "1.01 21-Jan-86" X/* #define DEBUG */ X Xchar *sccsid = "21-Jan-86 (Plocher) @(#)chd.c 1.01"; X Xextern char *strchr(); Xextern char *getenv(); Xextern char *strdup(); Xextern void usage(); X Xextern char *filename(); Xextern char *fixslash(); Xextern void recover(); X Xmain(argc, argv) Xint argc; Xchar *argv[]; X{ X char **names; /* array of pointers to names returned by glob() */ X char *progname; /* name of program (either argv[0] or hardcoded) */ X char *p; /* temp string pointer */ X char errstring[150];/* for usage() errors */ X char path[127]; /* workspace for getting starting (un-globbed) path */ X X if (argv[0]) /* if there is something here, use it (DOS 3.xx+) */ X progname = filename(argv[0]); X else /* hardcode the program name */ X progname = "chd"; X X if (argc > 2) { /* must have less than 2 arguments */ X usage(progname,"Too many arguments"); X exit(1); X } X if (argc == 1) { /* need to get HOME */ X p = getenv("HOME"); /* p == NULL if not found ... */ X if (p != NULL) X strcpy(path,p); X else { X usage(progname,"Environment variable HOME not found\n"); X exit(1); X } X } else { /* we have a pathspec from command line */ X if (argv[1][0] == '-') { /* if invoked chd -anything, just */ X usage(progname,NULL); /* explain ourselves */ X exit(0); X } X strcpy(path,argv[1]); X } X X fixslash(path); /* convert all '\'s into '/'s */ X X names = glob(path); /* get files which match */ X X if (names == NULL && globerror == GE_BADPATH) { X sprintf(errstring,"Path not found: \"%s\"",path); X usage(progname, errstring); X recover(names); X exit(1); X } X if ((names == NULL && globerror == GE_AMBIGUOUS) || names[1] != NULL) { X sprintf(errstring,"Ambiguous pathname \"%s\"",path); X usage(progname, errstring); X recover(names); X exit(2); X } X if (chdir(names[0])) { /* do the actual chdir */ X usage(progname,"You must specify a DIRECTORY path"); X recover(names); X exit(3); X } X if (names == NULL && (globerror == GE_MAXFILES || globerror == GE_NOMEM)) { X sprintf(errstring,"Internal error: %s", X globerror == GE_MAXFILES ? "Too many files" : "Out of memory"); X usage(progname, errstring); X recover(names); X exit(4); X } X recover(names); /* free memory used by *names[] */ X X exit(0); /* no errors */ X} X X/* X * usage tell the workd about this program X */ Xvoid usage(progname, error) Xchar *progname; X{ Xfprintf(stderr,"%s version %s by John Plocher on FidoNet 121/90\n", X progname,VERSION); Xif (error) X fprintf(stderr,"\nERROR: %s\n",error); Xelse { X fprintf(stderr,"\n%s - a replacement for MS-DOS's chdir & cd commands\n", X progname); X fprintf(stderr,"%s is Copyright 1986 by John Plocher\n\n",progname); X fprintf(stderr,"%s is released to the NON PROFIT, NON COMMERCIAL public.\n", X progname); X fprintf(stderr,"Commercial rights to %s reserved by John Plocher\n", progname); X fprintf(stderr,"%s has some features which the DOS version lacks:\n",progname); X fprintf(stderr,"\t1) ALL wildcards (* and ?) are expanded, no matter where\n"); X fprintf(stderr,"\t they are in the path.\n"); X fprintf(stderr,"\t\tC:\\ >%s \\u*\\p*\\w*\\?\n",progname); X fprintf(stderr,"\t\tC:\\USR\\PLOCHER\\WORK\\C >\n"); X fprintf(stderr,"\t2) If %s is invoked without arguments, it will try to\n",progname); X fprintf(stderr,"\t change to the directory found in the environment\n"); X fprintf(stderr,"\t variable HOME.\n"); X fprintf(stderr,"\t\tC:\\ >SET HOME=\\usr\\plocher -or-\n"); X fprintf(stderr,"\t\tC:\\ >SET HOME=\\u*\\p*\n"); X fprintf(stderr,"\t3) If %s fails, it leaves an indication in ERRORLEVEL.\n",progname); X fprintf(stderr,"\t This exit code can be tested in batch files:\n"); X fprintf(stderr,"\t ERRORLEVEL Meaning\n"); X fprintf(stderr,"\t ---------- -------\n"); X fprintf(stderr,"\t\t0 No error\n"); X fprintf(stderr,"\t\t1 could not find directory\n"); X fprintf(stderr,"\t\t2 ambiguous pathname\n"); X fprintf(stderr,"\t\t3 directoryname expected, you gave a filename\n"); X fprintf(stderr,"\t\t4 internal error (let me know about this!)\n\n"); X} Xfprintf(stderr,"usage: %s <directory name> change to directory\n",progname); Xfprintf(stderr," %s change to HOME\n",progname); Xfprintf(stderr," %s -? help screen\n",progname); X} X X/* X * filename extract the filename from a pathname X * ie C:\usr\plocher\chd.exe results in chd X */ Xchar *filename(path) Xchar *path; X X{ X register char *p, *pd; X X p = strchr(path,'\0'); /* work from the back... */ X while (--p != path && *p != ':' && *p != '\\' && *p != '/'); X if (*p == '\\' || *p == '/' || *p == ':') X p++; X pd = strchr(p,'.'); /* we don't want the .EXE */ X if (pd != NULL) X *pd = '\0'; X strlwr(p); X return (p); X} X X/* X * fixslash convert all '\'s to '/'s X */ Xchar *fixslash(s) Xchar *s; X{ X register int x; X X if (s == NULL) return; X for (x=0; x < strlen(s); x++) X if (s[x] == '\\') X s[x] = '/'; X} X X X/* X * recover free up the space malloc()'d in *names[] by glob() X */ Xvoid recover(names) Xchar **names; X{ X register int i; X X i = 0; X while (names[i] != (char *)0) { X free(names[i]); X i++; X } X free(names); X} X !SHAR-EOF! if [ "`wc -c chd.c`" != " 5981 chd.c" ] then echo ' chd.c may be bad' fi fi echo x - example.c if [ -s example.c ] # exists & size>0 then echo "shar: will not overwrite 'example.c'" else sed "s/^X//" << \!SHAR-EOF! > example.c X/* Example program demonstrating the use of glob() */ X X#include "glob.h" X X main() { X char **names; X names = glob("*.?"); X if (names) { X printf("expansion of *.? is:\n"); X print(names); X } else X printf("glob() error number %d (see glob.h)\n",globerror); X recover(names); X } X X recover(names) /* free() all the space used up by **names; */ X char **names; X { X int i = 0; X while (names[i] != (char *)0) { X free(names[i]); X i++; X } X free(names); X } X X print(names) /* print out all the filenames returned by glob() */ X char **names; X { X int i; X i = 0; X while (names[i] != (char *)0) { X printf("%s\n",names[i]); X i++; X } X } !SHAR-EOF! if [ "`wc -c example.c`" != " 681 example.c" ] then echo ' example.c may be bad' fi fi echo x - glob.c if [ -s glob.c ] # exists & size>0 then echo "shar: will not overwrite 'glob.c'" else sed "s/^X//" << \!SHAR-EOF! > glob.c X X/* X * Copyright 1985, 1986 by John Plocher Non-commercial use approved X * X * glob expand a given pathname with wildcards into X * an argv[] type array of 'strings' which are X * the files (directories) which result from the X * expansion of the pathname. (All characters in X * the returned list of filenames are in lower case) X * X * usage: char **glob(pathname) char *pathname; X * X * pathname is an absolute or relative pathname which may X * contain DOS type wildcards wherever desired: X * X * names = glob("\\usr\\pl*\\*.c"); X * X * It requires an externally declared int in the main function X * named globerror: X * X * extern int globerror; X * X * to be used to return an error indication if glob() fails. X * If glob() fails, it returns NULL with X * X * globerror set to GE_BADPATH invalid path - directory not found X * globerror set to GE_AMBIGUOUS ambiguous directory spec X * globerror set to GE_MAXFILES too many files found (MAXFILES) X * globerror set to GE_NOMEM out of memory X * X * else it returns a pointer to a NULL terminated array of pointers X * to 'strings': X * X * +-[ MAIN.C ]----------------------------------------------------+ X * | | X * | #include "glob.h" | X * | main() { | X * | char **glob(); | X * | char **names; | X * | names = glob("/u??/p*"); | X * | if (names) | X * | printf("the first name returned is %s\n",names[0]); | X * | else | X * | printf("glob() error number %d\n",globerror); | X * | recover(names); | X * | } | X * | | X * +---------------------------------------------------------------+ X * X * ALL strings and the array *names[] are made with calls to X * malloc(). Thus, be sure to free() *names[x] AND names when done! X * X * +-[sample routines]---------------------------------------------+ X * | | X * | (* free() all the space used up by **names; *) | X * | recover(names) | X * | char **names; | X * | { | X * | int i = 0; | X * | | X * | while (names[i] != (char *)0) { | X * | free(names[i]); | X * | i++; | X * | } | X * | free(names); | X * | } | X * | | X * | | X * | (* print out all the filenames returned by glob() *) | X * | print(names) | X * | char **names; | X * | { | X * | int i; | X * | i = 0; | X * | while (names[i] != (char *)0) { | X * | printf("%s\n",names[i]); | X * | i++; | X * | } | X * | } | X * +---------------------------------------------------------------+ X * X * Compile as follows: (Structures MUST be packed in order for X * the program to interface correctly with DOS) X * cl -c -Zp glob.c ** Microsoft C v3 or 4 ** X * X * Using the example given above, compile and link main program: X * cl main.c glob X * X * Written December, 1985 by John Plocher with MicroSoft C 3.0 X * Change log: X * X * version date who comments X * ------- ---- --- -------- X * 1.0 10-Dec-85 jmp Orig. coding X * 1.1 16-Jan-86 jmp added globerror X * 1.2 05-Feb-86 jmp added comments and doccumentation X * X */ X Xstatic char *scssid = "5-Feb-86 (Plocher) @(#)glob.c 1.2"; Xstatic char *copyright = X "Copyright 1985, 1986 John Plocher. Non-commercial use approved"; X X#include <stdio.h> X#include <dos.h> X#include <memory.h> X#include <direct.h> X#include <string.h> X#include "glob.h" X X X/* Change the following if you might have more than this many */ X/* files returned by glob() */ X X#define MAXFILES 256 /* max files returned by glob() */ X X Xstatic char *list[MAXFILES]; /* MAXFILES entries per sub-dir */ X X Xextern char *strlwr(); Xextern int getdir(); Xextern char *pos(); X Xint globerror; /* PUBLIC - global error number */ X X/* X * glob() X */ Xchar ** Xglob(proposed) Xchar *proposed; X{ X int i,j,k,needdir,n; X char *end, filename[128], newpath[127], tmppath[127]; X char *f, *p1, *p2; X char **names; X X strcpy(filename, proposed); X X /* add on current directory if first char is not '/' or '\' */ X if (*filename != '/' && *filename != '\\') { X getcwd(tmppath,128); X p2 = strchr(tmppath,'\0'); X p2--; X if (*p2 != '/' && *p2 != '\\') X strcat(tmppath,"/"); X strcat(tmppath,filename); X strcpy(filename, tmppath); X } X for (i=strlen(filename); i; i--) X if (filename[i] == '\\') X filename[i] = '/'; X i = 0; j = 0; X p2 = strchr(filename,'\0'); X p2--; X if (*p2 == '.') X strcat(filename,"/"); X while ((p1 = pos("/./",filename)) != NULL) { X memccpy(p1+1, p1+3, '\0',strlen(p1)); X } X while ((p1 = pos("../",filename)) != NULL) { X char tmp; X tmp = *(p1 - 1); X *(p1 - 1) = '\0'; X p2 = strrchr(filename,'/'); X if (p2 == NULL) { X globerror = GE_BADFILE; X return(NULL); X } X *(p1 - 1) = tmp; X memccpy(p2+1, p1+3, '\0',strlen(p1)); X } X p2 = strchr(filename,'\0'); X p2--; X if (*p2 == '/') X *p2 = '\0'; Xloop: X while (filename[i] != 0 && /* copy till hit a wildcard */ X filename[i] != '*' && X filename[i] != '?') { X tmppath[j++] = filename[i++]; X } X if (filename[i] != 0) /* wildcard found */ X while (filename[i] != 0 && X filename[i] != '/') X tmppath[j++] = filename[i++]; X if (filename[i] != 0) /* need directory here */ X needdir = 1; X else /* any file will do */ X needdir = 0; X tmppath[j] = 0; X strcpy(newpath,tmppath); X end = strrchr(newpath,'/'); X if (end != NULL) X *end = '\0'; X X n = getdir(tmppath,needdir); X X if (n == -2) { /* out of dynamic memory */ X globerror = GE_NOMEM; X return(NULL); X } X if (n == -1) { /* exceeded filecount */ X globerror = GE_MAXFILES; X return(NULL); X } X if (n == 0) { /* file not found */ X globerror = GE_BADFILE; X return(NULL); X } X if (needdir && n != 1) { /* ambiguous directory reference */ X for (i = 0; i < n; i++) /* This is an arbitrary limit */ X free(list[i]); /* one could follow all paths */ X globerror = GE_AMBIGUOUS; /* and not just the first... */ X return(NULL); X } X if (needdir) { X strcpy(tmppath,newpath); X strcat(tmppath,"/"); X strcat(tmppath,list[0]); X free(list[0]); X j = strlen(tmppath); X goto loop; X } X X names = (char **)malloc((n+1) * sizeof(char *)); X for (i = 0; i < n; i++) { X sprintf(tmppath,"%s/%s", newpath, list[i]); X names[i] = strdup(tmppath); X free(list[i]); X } X names[n] = (char *)0; X X return(names); X} X X X/* X * pos(target, src) X * find target string within src X */ Xstatic char *pos(s1,s2) Xchar *s1, *s2; X{ X int temploc, dist; X char *tp; X X if (*s1) { X for (;;) { X tp = strchr(s2, *s1); X if (tp == NULL) { /* couldn't even find first char */ X return(NULL); X } X if (!strncmp(s1,tp,strlen(s1))) /* match */ { X return(tp); X } X s2 = tp + 1; X } X } X} X X X/* X * File attributes X */ X X#define AT_RO 0x01 /* Read Only */ X#define AT_HIDDEN 0x02 /* hidden file */ X#define AT_SYSTEM 0x04 /* System file */ X#define AT_LABEL 0x08 /* volume label */ X#define AT_DIR 0x10 /* sub directory */ X#define AT_ARCH 0x20 /* archive bit */ X Xstatic struct REGS rregs; Xstatic struct SREGS sregs; X X Xstatic struct DMA { /* For reading directory entry */ X char res[21]; X char attr; X unsigned sec : 5, min : 6, hour : 5; X unsigned day : 5, mon : 4, year : 7; X long size; X char name[13]; X} dma; X X/* X * getdir gather all the files found in this directory X */ Xstatic int Xgetdir(path, needdir) /* return number of 'files' found in this path */ Xchar *path; /* this is where to look */ Xint needdir; /* 1 = we want ONLY AT_DIR type entries */ X /* 0 = we will take EVERYTHING */ X{ X int i, j, n; X char *entry; X X bdos(0x1a, &dma,0); X X n = 0; X rregs.h.ah = 0x4e; X rregs.x.cx = AT_RO | AT_HIDDEN | AT_SYSTEM | AT_DIR; X rregs.x.dx = (unsigned int) path; X j = intdos(&rregs, &rregs); /* Search for first */ X while(j == 0) { X strlwr(dma.name); X if (needdir && (dma.attr & AT_DIR != AT_DIR)){ /* skip this entry */ X rregs.h.ah = 0x4f; /* wes needs dirs, and this ain't one */ X j = intdos(&rregs, &rregs);/* Search for next */ X continue; X } X entry = strdup(dma.name); X if (entry == NULL) { /* out of memory */ X for (i = 0; i < n; i++) X free(list[i]); X return (-2); X } X if (n >= MAXFILES) /* too many files found */ X return(-1); X for (i = n++; i > 0; --i) { /* alphabetize */ X if (strcmp(entry, list[i - 1]) >= 0) X break; X list[i] = list[i - 1]; X } X list[i] = entry; X rregs.h.ah = 0x4f; X j = intdos(&rregs, &rregs); /* Search for next */ X } X return(n); X} X !SHAR-EOF! if [ "`wc -c glob.c`" != " 8494 glob.c" ] then echo ' glob.c may be bad' fi fi echo x - glob.h if [ -s glob.h ] # exists & size>0 then echo "shar: will not overwrite 'glob.h'" else sed "s/^X//" << \!SHAR-EOF! > glob.h X/* X * Header file for wildcard routines X */ X X#define GE_BADPATH -1 /* invalid path - directory not found */ X#define GE_AMBIGUOUS -2 /* ambiguous directory spec */ X#define GE_MAXFILES -3 /* too many files found (MAXFILES) */ X#define GE_NOMEM -4 /* out of memory */ X Xextern int globerror; /* PUBLIC - global error number */ Xextern char **glob( char * ); X !SHAR-EOF! if [ "`wc -c glob.h`" != " 362 glob.h" ] then echo ' glob.h may be bad' fi fi echo x - make.bat if [ -s make.bat ] # exists & size>0 then echo "shar: will not overwrite 'make.bat'" else sed "s/^X//" << \!SHAR-EOF! > make.bat XREM makefile for glob routines by John Plocher XREM glob is a routine which expands DOS style wildcards into an argv array X XREM Microsoft C 3.0 and 4.0 -Must pack structures in glob.c- X Xcl -O -c -Zp glob.c Xcl -O chd.c glob -o chd X !SHAR-EOF! if [ "`wc -c make.bat`" != " 231 make.bat" ] then echo ' make.bat may be bad' fi fi exit