[comp.sources.misc] "glob" in C for MS-DOS

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