[net.sources] argc and argv for the Macintosh

sjl@amdahl.UUCP (Steve Langdon) (12/12/84)

An offer of the following program in net.micro.mac generated enough
replies to make posting seem appropriate.  The source is written to
be compiled by Mac C (TM) from Consulair Corp.  It should be easy
to modify it for use with other C compilers for the Mac.  I am trying to
bring up SUMacC under UTS (moving it from 4.2 on a VAX to System V rel. 2
on an Amdahl 470/V7 takes a little work).  If and when I succeed, I will
do a SUMacC compatible version myself.

Steve Langdon                  ...!{ihnp4,hplabs,sun,amd}!amdahl!sjl

---------------------------CUT HERE------------------------------------
# This is a shell archive.  Remove anything before this line, then
# unpack it by saving it in a file and typing "sh file".  (Files
# unpacked will be owned by you and have default permissions.)
#
# This archive contains:
# argsim.c argsim.doc

echo x - argsim.c
cat > "argsim.c" << '//E*O*F argsim.c//'
/* This program simulates the shell command line argument processing	*/
/* available on UNIX(TM).  It is, however, very limited compared to	*/
/* the real thing.							*/
/* To use the simulation you must compile the target program with the	*/
/* header file "argsim.h".  This file redefines the "main" function to	*/
/* be "extern wasmain".  The new main is in argsim and it calls		*/
/* "wasmain" after reading a line of input and doing the argument	*/
/* processing stuff.							*/
/* The argument processing logic breaks a line into words. If a word	*/
/* contains a wildcard, then the word is replaced by a sorted list	*/
/* of files that match the specification.				*/
/* Redirection of stdin and stdout can be acomplished using the < and > */
/* (>> to append) characters as on UNIX.				*/
/* A sequence of characters in quotes (e.g. "a sequence") is a single	*/
/* word.								*/
/* A \ can be used to hide the end of a line and to cause < > \ and "	*/
/* to lose their significance as special symbols.			*/
/* After all this it calls "wasmain" passing argc and argv in the normal*/
/* UNIX manner.								*/
/* A filename specification consists of an optional volume part		*/
/* terminated by a colon (':'), and a file part.  Both the volume part	*/
/* and the file part can contain Bourne shell style wildcards.		*/
/* If no volume part is present the default volume name is used.	*/
/* The wildcard specifications accepted are:				*/
/*   '*' => zero or more occurrences of any character;			*/
/*   '?' => any single character;					*/
/*   [set of characters] => any single character in the set;		*/
/*        A "set of characters" can contain the following shorthand:	*/
/*            If the first character is an exclamation mark ('!') the 	*/
/*            set is of the characters which follow the '!'.		*/
/*            If two characters are separated by a hypen ('-'), then all*/
/*            characters lexically between them are in the set.		*/


/* Copyright 1984 S.Langdon.						*/
/* USENET ..!{ihnp4,hplabs,sun,amd}!amdahl!sjl COMPUSERVE [74036,2427]  */
/* 1750 Halford Ave #119, Santa Clara, CA 95051				*/
/*									*/
/* Non commercial use is permitted as long as credit is given.		*/

/* Thanks to Bill & Ann Duval of Consulair Corp. for providing Mac C	*/
/* which has given me a nice programming environment on my Mac.		*/

#include <stdio.h>

#include "OsIO.h"
#include "pbDefs.h"

#define noErr 0

#define MAC

extern wasmain();

#ifndef FALSE
#define FALSE 0
#endif

#ifndef TRUE
#define TRUE 1
#endif

/* These routines are not in the current version of the Mac C libraries.*/
/* They will be in the next release.  Remember to remove them when the	*/
/* library versions are added.						*/

extern int strcspn(s,set)
register char *s, *set;
{
	register i, j;
	i = j = 0;

	while (s[i] != set[j]) {
		if (set[j]) {
			j++;
		}
		else {
			j = 0;
			i++;
		}
	}
	return(i);
}

extern int strspn(s,set)
register char *s, *set;
{
	register i, j;
	i = j = 0;

	while (set[j]) {
		if (s[i] != set[j]) {
			j++;
		}
		else {
			j = 0;
			if(s[++i] == 0)
				return(i);
		}
	}
	return(i);
}


/* This is a simple line input routine.  It is used rather than an	*/
/* existing library routine so that backspace handling looks reasonable.*/
/* While I was at it, I added the ability to hide a newline with a '\'.	*/

char *mygetline(buffer, size)
char *buffer;
int size;
{
	register int c, i;
	
	inputrtn:
	for(i = 0;i < size;i++){
		c = getchar();
		switch (c){
			case '\b':
				if(i--) {
					/* erase character and move back */
					BackupTTY(buffer[i]);
					i--;
				}
				break;
			case '\n':
				putchar(c);
				if(buffer[i -1] == '\\'){
					/* new line hidden by '\' */
					buffer[--i] = ' ';
					break;
				}
				buffer[i] = '\0';
				return(buffer);
			case '\t':
				putchar(' ');
				buffer[i] = ' ';
				break;
			default:
				putchar(c);
				buffer[i] = c;
				break;
		}
		
	}
	fprintf(stderr,"Try again, maximum line length is %d\n", size);
	goto inputrtn;  /* sorry about this. It looked like the best solution */
}

/* This function parses a text string into tokens.  Tokens are:		*/
/* 	1) The characters '<' and '>'					*/
/*	2) Sequences of non-whitespace characters			*/
/*	3) A sequence of characters enclosed by '"'(double quotes)	*/
/* A '\' (backslash) causes the subsequent character (including 	*/
/* backslash) to lose any special meaning.  Thus, to cause a backslash	*/
/* to form part of a token the sequence '\\' is required.		*/

char *gettoken(source,dest,max)
char *source;
char *dest;
int max;
{
	int i = 0;
	int c;
	int quotemode = FALSE;
	while (i < max){
		c = *source;
		if(!quotemode){
			switch(c){
				case ' ':
				case '\t':{
					source++;
					if (i == 0 )
						continue;
					*dest = '\0';
					return(source);
				}
				case '>':
				case '<':{
					if(i == 0){
						source++;
						*dest++ = c;
						*dest = '\0';
						return(source);
					}
					*dest = '\0';
					return(source);
				}
				case '"':{
					if(i == 0){
						quotemode = TRUE;
						c = *(++source);
						continue;
					}
					*dest = '\0';
					return(source);
				}
				case '\\':{
					/* backslash cannot be the last character */
					/* as it would have hidden the newline.   */
					*(dest++) = *(++source);
					source++;
					i++;
					continue;
				}
				case '\0':{
					*dest = '\0';
					return(source);
				}
				default:{
					*(dest++) = c;
					source++;
					i++;
					continue;
				}
			}
		}
		switch(c){
			case '"':{
				if(i == 0)
					return(NULL);
				*dest = '\0';
				return(++source);
			}
			case '\0':{
				return(NULL);
			}
			case '\\':{
				/* backslash cannot be the last character */
				/* as it would have hidden the newline.   */
				*(dest++) = *(++source);
				source++;
				i++;
				continue;
			}
			default:{
				*dest++ = c;
				source++;
				i++;
				continue;
			}
		}
		
	}
	return(NULL);
}


#define STAR 1
#define ANYC 2
#define SETC 3
#define CHAR 4
#define EOP 5

#define ERRORA 1
#define ERRORB 2

int masktab[] = {
	1, 2, 4, 8, 16, 32, 64, 128};

/* This function takes a character string which can contain Bourne shell*/
/* style wildcards.  From this string it builds a set of instructions	*/
/* used by the "ptrnmatch" function.  The instructions are saved in a	*/
/* buffer provided by the caller.  If the pattern is malformed (ERRORA)	*/
/* or the buffer is too small (ERRORB), a non-zero value is returned.	*/
/* The wildcard specifications accepted are:				*/
/*   '*' => zero or more occurrences of any character;			*/
/*   '?' => any single character;					*/
/*   [set of characters] => any single character in the set;		*/
/*        A "set of characters" can contain the following shorthand:	*/
/*            If the first character is an exclamation mark ('!') the 	*/
/*            set is of the characters which follow the '!'.		*/
/*            If two characters are separated by a hypen ('-'), then all*/
/*            characters lexically between them are in the set.		*/


int ptrncompile(string, buffer, bufflength)
char *string, *buffer;
int bufflength;
{
	register int c;
	int prevc, i, negflag;

	while (c = *string++) {
		switch (c) {

		case '*':
			*buffer++ = STAR;
			break;

		case '?':
			*buffer++ = ANYC;
			break;

		case '[':
			if((bufflength -= 16) <= 0) /* test for space */
				return(ERRORB);
			*buffer++ = SETC;
			for(i = 0; i < 16; i++) { /* clear space */
				buffer[i] = '\0';
			}
			prevc = negflag = 0;
			if((c = *string++) == '!'){
				negflag = 1;
				c = *string++;
			}
			do {
				if (c) {
					if(c == '-') {
						if(prevc) {
							if((c = *string++) == ']') {
								buffer[(prevc >> 3) & 15] |= masktab[prevc & 7];
								break;
							}
							if(c == '\0')
								return(ERRORA);
							while(prevc < c) {
								buffer[(prevc >> 3) & 15] |= masktab[prevc & 7];
								prevc++;
							}
						}
					}
					prevc = c;
					buffer[(prevc >> 3) & 15] |= masktab[prevc & 7];

				}
				else {
					return(ERRORA);
				}
			} while ((c = *string++) != ']');

			if(negflag){
				for(i = 0; i < 16; i++)  /* complement table */
					buffer[i] ^= '\377';
			}
			buffer += 16; /* move pointer */
			break;

		default:
			*buffer++ = CHAR;
			if(!--bufflength)
				return(ERRORB);
			*buffer++ = c;
			/* drop out of switch */
		}
		if(!--bufflength)
			return(ERRORB);
	}
	*buffer = EOP;
	return(0);
}

/* This function takes a character string and a pattern definition.	*/
/* The pattern definition (normally built by "ptrncompile") contains 	*/
/* the following instructions:						*/
/*									*/
/*     STAR => matches zero or more occurrences of any characters.	*/
/*     ANYC => matches any single character.				*/
/*     SETC => followed by a 16 byte bit map.  This matches a single	*/
/*             character for which the corresponding bit is set.	*/
/*     CHAR => followed by a character. This matches the character	*/
/*     EOP  => matches the end of the string.				*/
/*									*/
/* The function returns 0 if the pattern matches the string exactly,	*/
/* 1 if it fails to match, and 2 if the pattern definition is bad.	*/
/*									*/

int ptrnmatch(string, buffer)
char *string, *buffer;
{
	register char *starpos;
	int c;

	for (;;) {
		switch(*buffer++) {
		case CHAR:
			if(*buffer++ == *string++)
				continue;
			return(1);

		case STAR:
			/* save current position in string */
			starpos = string;
			/* find end of string */
			while(*string++);
			/* recursively look from back of string */
			/* to current position */
			do {
				if(ptrnmatch(--string, buffer) == 0)
					return(0);
			} while (string > starpos);
			return(1);

		case ANYC:
			if(*string++)
				continue;
			return(1);
		
		case SETC:
			c = *string++;
			if((buffer[(c >> 3) & 15] & masktab[c & 7])){
				buffer += 16;
				continue;
			}
			return(1);
		
		case EOP:
			if(*string)
				return(1);
			return(0);
		
		default:
			return(2);
		}
	}

}


/* This function takes a volume reference number, a volume name, and	*/
/* a pattern (see the ptrn routines above).  It builds a sorted list	*/
/* of file names on the volume that match the pattern.  The number of	*/
/* files in the list is returned.  The file names placed in the list	*/
/* are fully specified (ie. they are of the form "volume:file").	*/


int getfiles(volrefn, volname, pattern, filelist, listlength)
char *volname, *pattern, **filelist;
int volrefn, listlength;
{
	int count = 0;
	int j, t, vnamelen;
	char filename[64];
	char fullname[256];
	FileParam fblock;
	
	vnamelen = strlen(volname);
	strcpy(fullname, volname);
	fullname[vnamelen++] = ':';
	fblock.ioNamePtr = filename;
	fblock.ioVRefNum = (short)volrefn;
	
	for(j = 0;;j++){
		fblock.ioFDirIndex = (short)j + 1;
		PBGetFInfo(&fblock,0);
		if(fblock.ioResult == noErr){
			PtoCstr(filename);
			if((ptrnmatch(filename, pattern)) == 0) {
				if(listlength-- == 0){
					fprintf(stderr,"ran out of room storing files\n");
					exit(2); /* return ? */
				}
				/* build up full filename */
				strcpy(fullname + vnamelen, filename);
				/* insert name in sorted position in dest */
				t = count;
				if(count != 0){
					while(strcmp(fullname, filelist[t-1]) < 0){
						filelist[t] = filelist[t-1];
						t--;
						if(t == 0){
							break;
						}
					}
				}
				filelist[t] = malloc(strlen(fullname) + 1);
				strcpy(filelist[t], fullname);
				count++;
			}
		}
		else {
			if(fblock.ioResult == fnfErr){
				return(count);
			}
			fprintf(stderr,"Unexpected return %d from PBGetFInfo\n",fblock.ioResult);
			exit(2);
		}
	}
}

/* This function takes a pattern (see the ptrn routines above).  It	*/
/* builds a sorted list	of volume names that match the pattern.  For	*/
/* each volume, a second list contains the volume reference number.	*/
/* The number of volumes in the list is returned.			*/

getvols(pattern, namelist, refnlist, listlength)
char *pattern, **namelist;
int listlength, refnlist[];
{
	int count = 0;
	int i, t;
	char volname[64];
	VolumeParam vblock;
	
	vblock.ioNamePtr = volname;
	for(i = 0;;i++){
		vblock.ioVolIndex = (short)i+1;
		PBGetVInfo(&vblock,0);
		if(vblock.ioResult == noErr){
			
			PtoCstr(volname);
			if((ptrnmatch(volname, pattern)) == 0) {
				if(listlength-- == 0){
					fprintf(stderr,"ran out of room storing volumes\n");
					exit(2); /* return ? */
				}
				/* insert in sorted position in namelist */
				t = count;
				if(count != 0){
					while(strcmp(volname,namelist[t-1]) < 0){
						namelist[t] = namelist[t-1];
						refnlist[t] = refnlist[t-1];
						t--;
						if(t == 0){
							break;
						}
					}
				}
				namelist[t] = malloc(strlen(volname) + 1);
				strcpy(namelist[t],volname);
				refnlist[t] = vblock.ioVRefNum;
				count++;
			}
		}
		else {
			if(vblock.ioResult == nsvErr)
				return(count);
			fprintf(stderr,"Unexpected return %d from PBGetVInfo\n",vblock.ioResult);
			exit(2);
		}
	}
}

#define MAXVOLS 10
#define BUFFLEN 100
/* Given a string containing a filename specification (the "word" 	*/
/* parameter) this function builds a sorted list of files matching the	*/
/* specification (in the filelist parameter), and returns the number of	*/
/* files in the list. A filename specification consists of an optional	*/
/* volume part terminated   by a colon (':'), and a file part.  Both the*/
/* volume part and the file part can contain Bourne shell style 	*/
/* wildcards (see comments on ptrn routines).				*/
/* If no volume part is present the default volume name is used.	*/

char *metachars = "*?[";

int glob(word, filelist, listlength)
char *word, **filelist;
int listlength;
{
	int i;
	int vcount;	/* volume count */
	int fcount = 0;	/* file count */
	int vnamelen;	/* length of volume name */
	char *pointer;	/* used in spliting volume and file parts of a name */
	
	char buffer[BUFFLEN];	/* for use by ptrncompile/ptrnmatch */
	char volpart[64];	/* volume part of name */
	char filepart[64];	/* file part of name */
	char *vollist[MAXVOLS];	/* holds pointers to volume names */
	int vrefnlist[MAXVOLS];	/* holds volume reference numbers */
	
	VolumeParam vblock;	/* parameter block for PBGetVol */

	if(strcspn(word, metachars) != strlen(word)){
		/* A wildcard character is present */
		if((pointer = strchr(word, ':')) != NULL) {
			/* the name contains a volume spec */
			vnamelen = pointer - word;
			strncpy(volpart, word, vnamelen);
			volpart[vnamelen] = '\0';
			strcpy(filepart, pointer + 1);
			if(ptrncompile(volpart, buffer, BUFFLEN)){
				fprintf(stderr, "bad wildcards in volume spec\n");
				exit(2);
			}
			/* check volumes */
			vcount = getvols(buffer, vollist, vrefnlist, MAXVOLS);
			if(vcount == 0) {
				fprintf(stderr, "no match on volume spec\n");
				exit(2);
			}
			
		}
		else {
			/* no volume specified - get default */
			vcount = 1;
			vblock.ioNamePtr = volpart;
			PBGetVol(&vblock,0);
			if(vblock.ioResult != noErr){
				fprintf(stderr,"No default volume\n");
				exit(2);
			}
			PtoCStr(volpart);
			vollist[0] = strcpy(malloc(strlen(volpart) + 1), volpart);
			vrefnlist[0] = vblock.ioVRefNum;
			strcpy(filepart, word);
		}
		/* now vollist contains a sorted list of volume names     */
		/* vrefnlist contains the corresponding reference numbers */
		/* and vcount is the number of volumes                    */
		if(ptrncompile(filepart, buffer, BUFFLEN)){
			fprintf(stderr, "bad wildcards in file spec\n");
			exit(2);
		}
		for (i = 0; i < vcount; i++) {
			/* the following line is broken to stay under 80 characters */
			fcount += getfiles(vrefnlist[i], vollist[i],
			buffer, filelist + fcount, listlength - fcount);
		}
	}
	else {
		fcount = 1;
		filelist[0] = strcpy(malloc(strlen(word)+1), word);
	}
	return(fcount);
}


#define MAXARGS 100
#define MAXLARGS 32

				
#define GETARGS 0  /* reading arguments and no redirection character seen */
#define SETOUT 1   /* output redirection character has been seen */
#define SETIN 2    /* input redirection character has been seen */

#define SUCCESS 0 /* result when argument processing was successfull */
#define ERROR 1   /* result when an error was detected */
#define NOTKNOWN 2 /* result when argument processing is incomplete */


main()
{
	FILE *tempfile;
	
	int  i; /* argument index */
	int  j;
	int inflag; /* TRUE if input is to be redirected */
	int outflag;/* TRUE if output is to be redirected */
	int append; /* TRUE if redirected output is to append */
	int mode;   /* holds state of argument processing routine */
	int result; /* holds result of argument processing routine */
	
	char *buffptr1;
	char *buffptr2;
	
	char *argv [MAXARGS];
	char linebuff[MAXLINE];  /* buffer for input line */
	char parmbuff[MAXLARGS]; /* buffer for input parameter */
	char *infname;   /* holds file name for input redirection */
	char *outfname; /* holds file name for output redirection */
	
	argv[0] = malloc(32); /* allocate space for application name */
	GetAppParms(argv[0], &i, &buffptr1); /* i and buffptr1 are just dummies */
	PtoCStr(argv[0]);/* convert string into usable form */
	mygetline(linebuff, MAXLINE);
	inflag = outflag = append = FALSE;
	mode = GETARGS;
	result = NOTKNOWN;
	i = 1;
	buffptr1 = linebuff;
	while((buffptr2 = gettoken(buffptr1, parmbuff, MAXLARGS - 1)) != NULL){
		switch(mode){
			case GETARGS:{
				if(buffptr1 == buffptr2){
					result = SUCCESS;
					break;
				}
				if(strlen(parmbuff) > 1){
					i += glob(parmbuff, &argv[i], MAXARGS - i); 
					break;
				}
				switch(*parmbuff){
					case '>':{
						mode = SETOUT;
						break;
					}
					case '<':{
						mode = SETIN;
						break;
					}
					default:{
						i += glob(parmbuff, &argv[i], MAXARGS - i); 
						break;
					}
				}
				break;
			}
			case SETOUT:{
				if(outflag){
					fprintf(stderr,"you attempted to redirect STDOUT twice\n");
					result = ERROR;
					break;
				}
				if(buffptr1 == buffptr2){
					result = ERROR;
					break;
				}
				if (strlen(parmbuff) > 1){
					j = glob(parmbuff, &argv[i], MAXARGS - i); 
					if(j = 1){
						outflag = TRUE;
						outfname = argv[i];
						mode = GETARGS;
					}
					else {
						fprintf(stderr,"ambiguous output redirection\n");
						result = ERROR;
					}
					break;
				}
				switch(*parmbuff){
					case '>':{
						if(append)
							result = ERROR;
						append = TRUE;
						break;
					}
					case '<':{
						result = ERROR;
						break;
					}
					default:{
						j = glob(parmbuff, &argv[i], MAXARGS - i); 
						if(j = 1){
							outflag = TRUE;
							outfname = argv[i];
							mode = GETARGS;
						}
						else {
							fprintf(stderr,"ambiguous output redirection\n");
							result = ERROR;
						}
						break;
					}
				}
				break;
			}
			case SETIN:{
				if(inflag){
					fprintf(stderr,"you attempted to redirect STDIN twice\n");
					result = ERROR;
					break;
				}
				if(buffptr1 == buffptr2){
					result = ERROR;
					break;
				}
				if (strlen(parmbuff) > 1){
					j = glob(parmbuff, &argv[i], MAXARGS - i); 
					if(j = 1){
						inflag = TRUE;
						infname = argv[i];
						mode = GETARGS;
					}
					else {
						fprintf(stderr,"ambiguous input redirection\n");
						result = ERROR;
					}
					break;
				}
				switch(*parmbuff){
					case '>':{
						result = ERROR;
						break;
					}
					case '<':{
						result = ERROR;
						break;
					}
					default:{
						j = glob(parmbuff, &argv[i], MAXARGS - i); 
						if(j = 1){
							inflag = TRUE;
							infname = argv[i];
							mode = GETARGS;
						}
						else {
							fprintf(stderr,"ambiguous input redirection\n");
							result = ERROR;
						}
						break;
					}
				}
				break;
			}
		}
		if(result == SUCCESS){
			if(inflag){
				
				tempfile = fopen(infname, "r");
				if (tempfile == NULL) {
					fprintf(stderr, "could not open %s as STDIN\n", infname);
					exit(1);
				}
				stdin = tempfile;
			}
			if(outflag){
				tempfile = fopen(outfname, append ? "a" : "w");
				if (tempfile == NULL) {
					fprintf(stderr, "could not open %s as STDOUT\n", outfname);
					exit(1);
				}
				stdout = tempfile;
				SetFileType(outfname, (long)'TEXT');
			}
			wasmain(i, argv);
			getchar();
			exit(0); /* return the memory? */
		}
		else if(result == ERROR){
			fprintf(stderr,"error in argument processing\n");
			getchar();
			exit(1);
		}
		else if(result == NOTKNOWN){
			if (i > MAXARGS) {
				fprintf(stderr,"too many arguments - maximum is %d\n", MAXARGS);
				exit(1);
			}
			buffptr1 = buffptr2; /* update current position in buffer */
			
		}
	
	}
	fprintf(stderr,"Error in arguments\n");
	exit(1);
}
//E*O*F argsim.c//

echo x - argsim.doc
cat > "argsim.doc" << '//E*O*F argsim.doc//'
The argument simulator is designed to mimic the behavior of the UNIX(TM)
shell, with respect to I/O redirection and placing command line arguments
in argc and argv.  As the Mac file system does not support a UNIX style
file system there are some significant differences in filename handling.

The argument simulator is used by renaming the "main" function in the
target program "wasmain".  I do this using a very short header file
called "argsim.h" which contains the following #define:

#define main extern wasmain

You then link the target program, argsim, and the necessary library files
together to generate an executable application.  An example of an Apple
MDS linker control file which links an implementation of wc (I wrote it
myself, so no license violations are involved), is as follows:

!start

stdlib
MacCmem
MacCstrings
MacCstringutil
MacCutil
MacCio
MacCiosupp
MacCioprim
MacCFileio
MacCTTY
MacCSIOLib

argsim
wc
$

A filename on the Mac consists of an optional volume name which, if present,
is followed by a colon, and then the name of the file itself.  When the
volume name is absent the "default volume" is assumed.  The default
volume has a vague correspondence to the current directory on UNIX.
Both volume names and file names can contain spaces.

The argument processing routine uses the tty simulation window supported
by Mac C(TM) to read a line of input.  If you are using a different C
compiler you will probably have to modify the source to use whatever
your compiler supports for naive keyboard input and screen output.

The line is broken up into words in a fairly obvious way.  Double quotes
("") can be used to surround words with imbedded spaces.  If a word contains
the characters *, ?, or [ it is assumed to be a pattern and filename
substitution is attempted.  The characters < > and >> have their usual
meaning (the file type of a file receiving redirected output is set to
TEXT).

Filename substitution is performed in the following manner.  If the word
contains a colon (:), the volume portion of the name is matched against
all mounted volumes generating a list of matching volumes, otherwise
the default volume name is used.  Then the file portion of the name
is matched against files on the selected volume(s).

The source is fairly heavily commented, so I hope that you can figure out
the answer to any detailed questions without too much pain.  The Mac C
specific areas of the program are:

	1) The use of the tty simulation window (mentioned above).
	2) The BackupTTY call to do an erasing backspace.
	3) The way in which the following trap calls are implemented
		PBGetFInfo
		PBGetVInfo
		PBGetVol
	4) The PtoCstr and CtoPstr calls for in-place string conversion.
	5) The method for reassigning stdin and stdout.
	6) The SetFileType call to make output files type "TEXT".


There are obviously lots of improvements that could be made to the current
version of the code.  Some examples are:

	Make it a separate segment and unload it after use.
	Use a TE window instead of using the teletype simulation.
	Add a warning if stdout is redirected into an existing file.
	Add syntax to use finder info in selecting files.

Unfortunately, I only have a limited amount of time to devote to this
effort, so I hope that you find it useful in the current form.

Steve

Copyright 1984 S.Langdon.
USENET ..!{ihnp4,hplabs,sun,amd}!amdahl!sjl COMPUSERVE [74036,2427]
1750 Halford Ave #119, Santa Clara, CA 95051

Non commercial use is permitted as long as credit is given.
//E*O*F argsim.doc//

echo Possible errors detected by \'wc\' [hopefully none]:
temp=/tmp/shar$$
trap "rm -f $temp; exit" 0 1 2 3 15
cat > $temp <<\!!!
    829   2984  20471 argsim.c
     91    583   3505 argsim.doc
    920   3567  23976 total
!!!
wc  argsim.c argsim.doc | sed 's=[^ ]*/==' | diff -b $temp -
exit 0
-- 
Stephen J. Langdon                  ...!{ihnp4,hplabs,sun,amd}!amdahl!sjl

[ The article above is not an official statement from any organization
  in the known universe. ]