[alt.msdos.programmer] Turbo C wildcard expansion

few@quad1.quad.com (Frank Whaley) (10/26/89)

/*
 *	setargv.c ->	parse arguments
 *
 *	This code evolved out of a requirement for expansion of wild-card
 *	arguments given on the command line.  Turbo C's wildarg.obj
 *	actually produces a reference to __wildargv, which still doesn't
 *	do the job I needed (for example, directory names are not
 *	expanded).
 *
 *	This version both expands wild-card arguments and converts them
 *	to lower case (a cosmetic thing for me).  Arguments that are
 *	quoted (with either " or ') are left alone, but the quote
 *	characters are stripped.  A leading quote may be escaped with
 *	a backslash (\" or \').  Quotes within arguments (ARG="a b c")
 *	are not handled in the Un*x fashion.  If no matching filename
 *	is found, the argument is passed unchanged.
 *
 *	Two conditional-compilation flags are provided:
 *		FIXARG0:	convert argv[0] to lower case and switch
 *				all backslashes to slashes (cosmetic)
 *		SORTARGS:	use qsort() to sort the expansions of
 *				each argument
 *
 *	Further enhancements greatly appreciated.
 *
 *	This code placed in the public domain on October 25, 1989 by
 *	its original author, Frank Whaley.
 */

#define	MAXARG	1024	/*  max arguments  */

#include <ctype.h>
#include <dir.h>
#include <dos.h>
#include <mem.h>
#include <string.h>
#include <alloc.h>

	/*  let's do some things with macros...  */
#define	ISQUOTE(c)	(((c)=='"')||((c)=='\''))
#define	ISBLANK(c)	(((c)==' ')||((c)=='\t'))


static char *cl;		/*  -> local copy of command line  */

	/*  the following are used by run-time startup (c0?.obj)  */
extern int _argc;		/*  number of args  */
extern char **_argv;		/*  arg ptrs  */

	/*  forward declarations  */
static void *allocopy(void *src, int len);
static void getreg(char *av[]);
static void getwild(char *av[]);


/*
 *	_setargv() ->	set argument vector
 */
void
_setargv()
{
	char buf[128];		/*  working buffer  */
	char far *cline;	/*  generic far ptr  */
	char *av[MAXARG];	/*  working vector  */
	int len;		/*  command line length  */

	/*  copy program name from environment  */
	cl = buf;
	cline = MK_FP(*(int far *)MK_FP(_psp, 0x2c), 0);
	while ( *cline )
	{
		if ( !*++cline )
		{
			cline++;
		}
	}
	cline += 3;
	while ( *cline )
	{
#ifdef	FIXARG0
		*cl = tolower(*cline++);
		if ( *cl == '\\' )
		{
			*cl = '/';
		}
		cl++;
#else
		*cl++ = *cline++;
#endif
	}
	*cl = '\0';
	av[0] = allocopy(buf, strlen(buf) + 1);

	/*  copy cmd line from PSP  */
	cl = buf;
	cline = MK_FP(_psp, 0x80);
	len = *cline++;
	while ( len )
	{
		*cl++ = *cline++;
		len--;
	}
	*cl = 0;

	_argc = 1;
	cl = buf;
	while ( *cl )
	{
		/*  deblank  */
		while ( ISBLANK(*cl) )
		{
			cl++;
		}

		/*  pick next argument  */
		while ( *cl && (_argc < MAXARG) )
		{
			if ( iswild() )
			{
				getwild(av);
			}
			else
			{
				getreg(av);
			}

			/*  deblank  */
			while ( ISBLANK(*cl) )
			{
				cl++;
			}
		}
	}

	/*  copy argument vector  */
	_argv = allocopy(av, _argc * sizeof(char *));
}


/*
 *	does current argument contain a wildcard ??
 */
static int
iswild()
{
	char *s = cl;

	if ( ISQUOTE(*s) )
	{
		return ( 0 );
	}

	while ( *s && !ISBLANK(*s) )
	{
		if ( (*s == '\\') && ISQUOTE(*(s + 1)) )
		{
			s += 2;
		}
		else if ( (*s == '?') || (*s == '*') )
		{
			return ( 1 );
		}

		s++;
	}

	return ( 0 );
}


/*
 *	allocopy() ->	allocate space for a copy of something
 */
static void *
allocopy(void *src, int len)
{
	void *copy;

	copy = sbrk(len);
	if ( copy == (void *)(-1) )
	{
		write(2, "\nMemory shortage\n", 17);
		exit(1);
	}
	return ( memcpy(copy, src, len) );
}


/*
 *	getreg() ->	pick a regular argument from command line
 */
static void
getreg(char *av[])
{
	char buf[128];
	char *bp = buf;
	char quote;

	/*  copy argument (minus quotes) into local buffer  */
	if ( ISQUOTE(*cl) )
	{
		quote = *cl++;
		while ( *cl&& (*cl != quote) )
		{
			*bp++ = *cl++;
		}
	}
	else
	{
		while ( *cl && !ISBLANK(*cl) )
		{
			if ( (*cl == '\\') && ISQUOTE(*(cl + 1)) )
			{
				cl++;
			}

			*bp++ = *cl++;
		}
	}
	*bp = '\0';

	/*  skip over terminator char  */
	if ( *cl )
	{
		cl++;
	}

	/*  store ptr to copy of string  */
	av[_argc++] = allocopy(buf, strlen(buf) + 1);
}


/*
 *	lwrcat() ->	concatenate strings, conver to lower case
 */
static char *
lwrcat(char *s, char *t)
{
	char *cp = s;

	while ( *cp )
	{
		cp++;
	}
	/*  avoid a warning  */
	while ( *t )
	{
		*cp++ = tolower(*t++);
	}
	*cp = '\0';

	return ( s );
	}


/*
 *	pickpath() ->	pick pathname from argument
 */
static void
pickpath(char *arg, char *path)
{
	char *t;
	int n;

	/*  find beginning of basename  */
	for ( t = arg + strlen(arg) - 1; t >= arg; t-- )
	{
		if ( (*t == '\\') || (*t == '/') || (*t == ':') )
		{
			break;
		}
	}

	/*  pick off path  */
	for ( n = (t - arg) + 1, t = arg; n--; )
	{
		*path = tolower(*t);
		path++;
		t++;
	}
	*path = '\0';
}


#ifdef	SORTARGS
/*
 *	mycmp() ->	comparison routine for qsort()
 */
static int
mycmp(char **s, char **t)
{
	return ( strcmp(*s, *t) );
}
#endif


/*
 *	getwild() ->	get wildcard argument from command line
 */
static void
getwild(char *av[])
{
	char path[128];
	char srch[128];
	char *s = srch;
	struct ffblk f;
#ifdef	SORTARGS
	char **firstv = &av[_argc];
	int nmatched = 0;
#endif

	/*  pick search string  */
	while ( *cl && !ISBLANK(*cl) )
	{
		*s++ = *cl++;
	}
	*s = '\0';

	pickpath(srch, path);

	if ( findfirst(srch, &f, 0x17) )
	{
		/*  no match, just copy argument  */
		av[_argc++] = allocopy(srch, strlen(srch) + 1);
		return;
	}

	/*  add name if not "." or ".."  */
	if ( f.ff_name[0] != '.' )
	{
		strcpy(srch, path);
		lwrcat(srch, f.ff_name);
		av[_argc++] = allocopy(srch, strlen(srch) + 1);
#ifdef	SORTARGS
		nmatched++;
#endif
	}

	/*  find the rest  */
	while ( !findnext(&f) && (_argc < MAXARG) )
	{
		if ( f.ff_name[0] != '.' )
		{
			strcpy(srch, path);
			lwrcat(srch, f.ff_name);
			av[_argc++] = allocopy(srch, strlen(srch) + 1);
#ifdef	SORTARGS
			nmatched++;
#endif
		}
	}

#ifdef	SORTARGS
	/*  sort these entries  */
	qsort(firstv, nmatched, sizeof(char *), mycmp);
#endif
}


/*
 *	END of setargv.c
 */
-- 
Frank Whaley
Senior Development Engineer
Quadratron Systems Incorporated
few@quad1.quad.com
uunet!ccicpg!quad1!few

Water separates the people of the world;
Wine unites them.

nelson@sun.soe.clarkson.edu (Russ Nelson) (10/26/89)

Please remember that anything posted to comp.sys.ibm.pc, alt.msdos.programmer,
and/or comp.binaries.ibm.pc.d doesn't get archived on grape.ecs.clarkson.edu.
For something as valuable as the setargv() function posted in the parent
of this article, that's sad.  Because eventually someone is going to want
to fetch a copy of it, and there won't be any.
--
--russ (nelson@clutx [.bitnet | .clarkson.edu])
Live up to the light thou hast, and more will be granted thee.
A recession now appears more than 2 years away -- John D. Mathon, 4 Oct 1989.

CMH117@PSUVM.BITNET (Charles Hannum) (10/29/89)

ARGH!  That has to be some of the most convoluted code I have ever seen!  If
you only have 1 statement in a block (an IF or ELSE clause, for example), there
is *NO* reason to include begin and end-block braces!  It just makes the code
longer and harder to read.

Not being mean, but I like short and concise.

dopey%looney@Sun.COM (Can't ya tell by the name) (10/31/89)

In article <89302.005130CMH117@PSUVM.BITNET> CMH117@PSUVM.BITNET (Charles Hannum) writes:
>ARGH!  That has to be some of the most convoluted code I have ever seen!  If
>you only have 1 statement in a block (an IF or ELSE clause, for example), there
>is *NO* reason to include begin and end-block braces!  It just makes the code
>longer and harder to read.
>
>Not being mean, but I like short and concise.


Not to start a discussion on this issue, but I have found that the
little extra space you use for this kind of coding saves hours of time
debuging.  I have help hundreds of people debug thier code and have
found that those who like thier code THAT short and concise are the
ones that have the mose difficult time debugging it. In fact thier
have been those who I would not help untill they "expanded and
formatted" thier code, and strange thing happened, they didn't need my
help - they found it themselves.

The most common reason for this is that without the begin and end
braces they add a line of code to the for/while/if statement and
forget to add the braces and . . . Well they are just to close to
the tree to see the forest.

I will not discuss this further in this group, but didn't want
possibly new programmers to think that because you could and
someone said you should that it was gospel, there are VERY good
reasons for doing it the way the original poster did.

flames to /dev/null or alt.religion.lang (or whatever that group is
that I find so amusing to read).

wew@naucse.UUCP (Bill Wilson) (10/31/89)

> found that those who like thier code THAT short and concise are the
> ones that have the mose difficult time debugging it. In fact thier
> have been those who I would not help untill they "expanded and
> formatted" thier code, and strange thing happened, they didn't need my
>
Here is a good example of the statement that exceptional programmers
master every high level language but english.  Let's police our
flame based mail before sending it out.  Also discussions on style
normally do not get anywhere and are a waste of band width.
 
-- 
Let sleeping dragons lie........               | The Bit Chaser
----------------------------------------------------------------
Bill Wilson             (Bitnet: ucc2wew@nauvm | wilson@nauvax)
Northern AZ Univ  Flagstaff, AZ 86011

ejd@caen.engin.umich.edu (Edward J Driscoll) (10/31/89)

In article <89302.005130CMH117@PSUVM.BITNET> CMH117@PSUVM.BITNET 
(Charles Hannum) writes:
>ARGH!  That has to be some of the most convoluted code I have ever seen!  If
>you only have 1 statement in a block (an IF or ELSE clause, for example), there
>is *NO* reason to include begin and end-block braces!  It just makes the code
>longer and harder to read.
>

There's no *technical* reason.  It might make it harder for you to 
read, but others might find it easier to read (I, for one, appreciate
the parallel structure it gives to ALL the if statements).  It
certainly makes it less likely that you'll add in statements later
and forget to block them off with braces.  I do this all the time
to add in debugging code, and typing in the braces ahead of time
saves a lot of extra mental processing.  I'd say it's more a matter
of taste and style than a never-to-be-violated rule.

>Not being mean, but I like short and concise.
                     ^^^^^^
And I like braces that make it blatantly obvious which
statements are in the block.  See what I mean?
-- 
Ed Driscoll
The University of Michigan
ejd@caen.engin.umich.edu

ralphc@tekcae.CAX.TEK.COM (Ralph Carpenter) (11/04/89)

In article <1773@naucse.UUCP>, wew@naucse.UUCP (Bill Wilson) writes:
> > found that those who like thier code THAT short and concise are the
> > ones that have the mose difficult time debugging it. In fact thier
> > have been those who I would not help untill they "expanded and
> > formatted" thier code, and strange thing happened, they didn't need my
> Here is a good example of the statement that exceptional programmers
> master every high level language but english.  Let's police our
> flame based mail before sending it out.  Also discussions on style
> normally do not get anywhere and are a waste of band width.
> Bill Wilson             (Bitnet: ucc2wew@nauvm | wilson@nauvax)

I have read many good postings by Bill Wilson, but I would disagree a little
with him here.  I personally like the insight that comes from watching two
learned people engage in a (well-mannered) discussion.  As far as the typo-s,
if it's something worthwhile, I can overlook most of them.

Ralph Carpenter
Tektronix, Inc.
M/S 19-075
Beaverton, OR 97077