[net.sources] Make for PC

dolphin@uw-beaver (Dolphin Users) (09/06/84)

Here is a PUBLIC DOMAIN make for the IBM PC, posted recently to the net;
  I am reposting in the same spirit of generosity demonstrated by its author,
  a widely-known and beloved net figure.  Have fun, and be not concerned if
  you miss it this time: I will be reposting periodically!  Posted in shar
  format, natch.

------------------------CUT HERE, PUBLIC-DOMAINERS!--------------------------

#!/bin/sh
cat >make.tx <<'------ EOF ------'
: This is a shar archieve.  Extract with sh, not csh.
: The rest of this file will extract:
: make.c find.c path.c errout.c makefile.dat
echo extracting - make.c
sed 's/^X//' > make.c << '/*EOF'
X/* Make
X
X	Version 1.1, May 14, 1984
X	Changed to use DOS calls 4E and 4F and to allow wildcard
X	characters ('*' and '?') in depended-upon files. 'Howto'
X	lines are now defined as lines that start with either a
X	tab or a space (previous version accepted only tabs).
X
X	Any "contributions" gratefully accepted.  I "suggest"
X	a contribution of $25 (assuming that you find this useful).
X
X	(c) Copyright, 1984, John M. Sellens
X
X*/
X
X/*
X	This a called 'Make' and is a much simplified version of
X	the make utility on UNIX (a trademark or something of AT&T)
X	written using the DeSmet C compiler/assembler and editor
X	for the IBM Personal Computer.  The DeSmet package is
X	available from C Ware, P.O. Box 710097, San Jose, California,
X	95171-0097, (408) 736-6905 for around $100US.  I think it's
X	a good deal.
X
X	'Make' takes a file of dependencies (a 'makefile') and
X	decides what commands have to be executed to bring the files
X	up to date.  These commands are either executed directly from
X	'Make' or written to the standard output without executing
X	them.
X
X'Makefile' format:
X	- There must be a 'makefile'; you can't take input from the
X	standard input.
X	- The default name of the 'makefile' is 'MAKEFILE.DAT' on the
X	default disk.  Different 'makefiles' can be specified using
X	the '-f' option on the command line.  If the '-f' option is
X	used, the default 'makefile' is not processed.
X	- Any blank lines in the 'makefile(s)' are ignored.
X	- A line in a 'makefile' that starts with a tab character is
X	a 'howto' line and consists of a command name followed by
X	arguments.  The command name must be a full file name, e.g.
X	'cc.exe'.  When commands are executed, the PATH environment
X	variable is used to find the command, in (hopefully) the
X	same manner as DOS does.  If a command name contains a ':'
X	or a '\', it is assumed to be a complete pathname and the
X	PATH is not searched.  'Howto' lines apply to the most
X	recently preceding 'dependency' line.  It is an error for
X	a 'howto' line to precede the first 'dependency' line.
X	- Any other non-blank line is a 'dependency' line.  'Dependency'
X	lines consist of a filename followed by a (possibly empty) list
X	of dependent filenames.
X
XOperation:
X	Syntax:
X		make [filename] [-f makefilename] [-i] [-n]
X	-i means continue even if an error is encountered while
X		executing a command.
X	-n means don't execute the commands, just write the ones that
X		should be executed to the standard output.  This is useful
X		for creating batch files, for example.
X	-f specifies that the following argument is the name of a makefile
X		to be used instead of the default (MAKEFILE.DAT).
X	All arguments may be repeated and relative position of the
X	arguments is not important.  If multiple definitions of a file
X	are found, only the first one is significant.
X
X	First, 'Make' reads all of the makefiles.  It then proceeds through
X	all of the filename arguments, 'making' each one in turn.  A file
X	is remade if it is out of date with respect to the files it depends
X	on or is non-existent.  Dependencies are processed in a 'tree' fashion,
X	so that the lowest-order files are remade first.
X
X	'Make' cannot execute DOS built-in commands e.g. 'cd' or 'dir'.
X	'Make' uses the first 75k or so after the resident portion of DOS.
X	64k of that is stack and heap space: all definitions and howto's
X	are stored in dynamically allocated struct's.  Any executed commands
X	are loaded above 'Make' in memory.
X
X	'Make' REQUIRES DOS 2.0 (or higher?).
X
X	'Make' requires 'Find' and 'Path' in order to search the PATH.
X	These are in (hopefully) adjacent articles.
X
X
X   Known portability problems:
X		- assembler code embedded in the C code
X		- uses 'exec()' to execute the commands.
X		- 'Path' requires that code segment register (CS) be set to the
X		  same value as at program invocation so that the
X		  program segment prefix can be located.  This would
X		  probably only be a problem if a "large model"
X		  compiler is used.
X
X	The code is a little kludgy in places.
X
X	No guarantees or warranties of any kind:  I think it works and
X	I use it.
X
X	Any suggestions for improvements gratefully accepted.
X
X	I believe that commercial versions exist.  I also beleive that they
X	would be superior to this.
X
X	If you do not have DeSmet C, I am willing to provide source,
X	object, and linked files for 'Make', 'Find' and 'Path' for a price.
X	Send me a floppy in a sturdy mailer, and $15 (you can tell I really
X	don't want to do this, but I will), and I will return your disk to you.
X	The $15 includes postage back to you.
X
X
X*/
X
X
X/*
XWritten by John M Sellens, April, 1984
X
XCode is all original except where indicated otherwise.
X
XUntil August, 1984:
X	jmsellens@watrose.UUCP
X
X	107 - 180 Brybeck Cres.
X	Kitchener, Ontario
X	N2M 5G4
X
XAfter August, 1984:
X	c/o 1135 Lansdowne Ave. SW
X	Calgary, Alberta
X	T2S 1A4
X
X(c) Copyright 1984 John M Sellens
XPermission is granted to use, distribute and/or modify this code unless
Xdone for direct commercial profit.  If you find these routines useful,
Xmodest contributions (monetary or otherwise) will be gratefully accepted.
XAuthor's name, address and this notice must be included in any copies.
X
X*/
X
X
X
X#include <stdio.h>
X
X#define DEFAULT	"MAKEFILE.DAT"
X#define INMAX	130		/* maximum input line length */
X
X#define DEPENDANT	1	/* used in calling getmodified */
X#define DEFINED		2
X
Xstruct howrec {
X	char *howcom,*howargs;
X	struct howrec *nexthow;
X};
X
Xstruct deprec {
X	char *name;
X	struct defnrec *def;
X	struct deprec *nextdep;
X};
X
Xstruct defnrec {
X	char *name;
X	int uptodate;
X	long modified;
X	struct deprec *dependson;
X	struct howrec *howto;
X	struct defnrec *nextdefn;
X};
X
Xstruct dorec {
X	char *name;
X	struct dorec *nextdo;
X};
X
Xstruct defnrec *defnlist;
Xstruct dorec *dolist;
X
Xint execute;
Xint stopOnErr;
Xint madesomething;
Xint knowhow;
X
Xmain(argc,argv)
Xint argc;
Xchar *argv[];
X{
X	long make();
X
X	init(argc,argv);
X
X	/* now fall down the dolist and do them all */
X	while (dolist != NULL) {
X		madesomething = FALSE;
X		(void)make(dolist->name);	/* ignore return value */
X		if (!madesomething) {
X			if (knowhow) {
X				errout("Make: '");errout(dolist->name);
X				errout("' is up to date\r\n");
X			} else {
X				errout("Make: Don't know how to make '");
X				errout(dolist->name);errout("'\r\n");
X				if (stopOnErr)
X					exit(-1);
X			}
X		}
X		dolist = dolist->nextdo;
X	}
X
X}
X
X
Xinit(argc,argv)
Xint argc;
Xchar *argv[];
X{
X	int i, usedefault;
X	dolist = NULL;
X	defnlist = NULL;
X	usedefault = TRUE;
X	execute = TRUE;
X	stopOnErr = TRUE;
X
X	for (i=1; i < argc; i++) {
X		if (argv[i][0] == '-') {	/* option */
X			switch (argv[i][1]) {
X				case 'f': case 'F':	/* arg following is a makefile */
X					if (++i < argc) {
X						readmakefile(argv[i]);
X						usedefault = FALSE;
X					} else {
X						errout("Make: '-f' requires filename\r\n");
X						exit(-1);
X					}
X					break;
X				case 'i': case 'I':	/* ignore errors on execution */
X					stopOnErr = FALSE;
X					break;
X				case 'n': case 'N':	/* don't execute commands - just print */
X					execute = FALSE;
X					break;
X				default:
X					errout("Make: unknown option '");errout(argv[i]);
X					errout("'\r\n");
X			}
X		} else {	/* it must be something to make */
X			add_do(argv[i]);
X		}
X	}
X	if (usedefault)
X		readmakefile(DEFAULT);
X
X}
X
Xlong make(s)	/* returns the modified date/time */
Xchar *s;
X{
X	struct defnrec *defnp;
X	struct deprec *depp;
X	struct howrec *howp;
X	long latest, getmodified(), max();
X
X	/* look for the definition */
X	defnp = defnlist;
X	while (defnp != NULL) {
X		if (strcmp(defnp->name,s) == 0)
X			break;
X		defnp = defnp->nextdefn;
X	}
X
X	if (defnp == NULL) {	/* don't know how to make it */
X		knowhow = FALSE;
X		latest = getmodified(s,DEFINED);
X		if (latest==0) {	/* doesn't exist but don't know how to make */
X			errout("Make: Can't make '");errout(s);errout("'\r\n");
X			exit(-1);
X		} else	/* exists - assume it's up to date since we don't know */
X			return(latest);
X	}
X
X	if (defnp->uptodate)
X		return(defnp->modified);
X
X	/* now make sure everything that it depends on is up to date */
X	latest = 0;
X	depp = defnp->dependson;
X	while (depp != NULL) {
X		latest = max(make(depp->name),latest);
X		depp = depp->nextdep;
X	}
X
X	knowhow = TRUE;	/* has dependencies therefore we know how */
X
X	/* if necessary, execute all of the commands to make it */
X	/* if (out of date) || (depends on nothing)             */
X	if (latest > defnp->modified || defnp->dependson==NULL) {
X		/* make those suckers */
X		howp = defnp->howto;
X		while (howp != NULL) {
X			printf("%s %s\n",howp->howcom,howp->howargs);
X			if (execute) {
X				char filename[100];	/* extra space */
X				if (find(howp->howcom,filename)) {
X					if (exec(filename,howp->howargs) != 0) {
X						errout("\r\nMake: error on '");errout(filename);
X						errout(" ");errout(howp->howargs);errout("'");
X						if (stopOnErr)
X							exit(-1);
X					}
X				} else {
X					errout("\r\nMake: Can't find '");
X					errout(howp->howcom);errout("'\r\n");
X					if (stopOnErr)
X						exit(-1);
X				}
X				putchar('\n');	/* in case command leaves us dangling */
X			}
X			howp = howp->nexthow;
X		}
X/* Only say it`s as recent as it's latest dependent - that way
X	if we don't actually have a file for this dependency, it works
X	ok
X*/
X		defnp->modified = latest;
X		defnp->uptodate = TRUE;
X		if (defnp->howto != NULL)	/* we had instructions */
X			madesomething = TRUE;
X	}
X
X	return(defnp->modified);
X
X}
X
X
Xadd_do(s)
Xchar *s;
X{
X	struct dorec *ptr1, *ptr2;
X	char *getmem();
X
X	ptr1 = getmem(sizeof(struct dorec));
X
X	ptr1->name = s;	/* okay since only called with an argv */
X	ptr1->nextdo = NULL;
X
X	uppercase(ptr1->name);
X
X	/* now go down the dolist */
X	if (dolist == NULL)
X		dolist = ptr1;
X	else {
X		ptr2 = dolist;
X		while (ptr2->nextdo != NULL)
X			ptr2 = ptr2->nextdo;
X		ptr2->nextdo = ptr1;
X	}
X
X}
X
X
Xreadmakefile(s)
Xchar *s;
X{
X	int fil, doneline, pos, i, j;
X	char inline[INMAX], info[INMAX];
X	char *getmem();
X	struct defnrec *defnp, *defnp2;
X	struct deprec *depp, *depp2;
X	struct howrec *howp, *howp2;
X
X	if ( (fil = open(s,0)) < 0) {
X		errout("Make: Couldn't open '");errout(s);errout("'\r\n");
X		return;
X	}
X
X	while (fgets(inline,INMAX,fil) != NULL) {
X		inline[strlen(inline)-1] = '\0';	/* strip trailing newline */
X
X		if (inline[0] == '\0')	/* ignore blank lines */
X			continue;
X
X		if (!isspace(inline[0])) {	/* start of a new definition */
X			uppercase(inline);
X
X			/* get what we're defining into info */
X			if (sscanf(inline,"%s ",info) != 1) {
X				errout("Make: Can't scan: '");errout(inline);errout("'\r\n");
X				continue;
X			}
X
X			/* test for wildcards in file being defined */
X			for(i=0;info[i]!='\0';i++)
X				if (s[i]=='?' || s[i]=='*') {
X					errout("Make: Definition of '");errout(info);
X					errout("' contains wildcard character(s)\r\n");
X					errout("Make: This may or may not make any sense\r\n");
X					break;
X				}
X
X			/* get a new struct */
X			defnp = getmem(sizeof(struct defnrec));
X			/* add it to the end of defnlist */
X			if (defnlist == NULL)
X				defnlist = defnp;
X			else {
X				defnp2 = defnlist;
X				while (defnp2->nextdefn != NULL)
X					defnp2 = defnp2->nextdefn;
X				defnp2->nextdefn = defnp;
X			}
X			/* initialize it */
X			defnp->name = getmem(strlen(info)+1);
X			strcpy(defnp->name,info);
X			defnp->uptodate = FALSE;	/* actually unknown */
X			defnp->modified = getmodified(defnp->name,DEFINED);
X			defnp->dependson = NULL;
X			defnp->howto = NULL;
X			defnp->nextdefn = NULL;
X
X			/* now go through all of its dependecies */
X			/* first move past the first name */
X			pos = 0;
X			while (isspace(inline[pos]))
X				pos++;
X			while (!isspace(inline[pos]) && inline[pos]!='\0')
X				pos++;
X			/* now loop through those suckers */
X			doneline = FALSE;
X			while (!doneline) {
X				while (isspace(inline[pos]))
X					pos++;
X				if (inline[pos] == '\0') {
X					doneline = TRUE;
X					continue;
X				}
X				for(i = 0; !isspace(inline[pos]) && inline[pos]!='\0'; )
X					info[i++] = inline[pos++];
X				info[i] = '\0';
X				/* get a new struct */
X				depp = getmem(sizeof(struct deprec));
X				/* add it to the end of deplist */
X				if (defnp->dependson == NULL)
X					defnp->dependson = depp;
X				else {
X					depp2 = defnp->dependson;
X					while (depp2->nextdep != NULL)
X						depp2 = depp2->nextdep;
X					depp2->nextdep = depp;
X				}
X				depp->name = getmem(strlen(info)+1);
X				strcpy(depp->name,info);
X				depp->nextdep = NULL;
X			}
X		} else {	/* a how to line that starts with blank or tab */
X			if (defnp == NULL) {
X				errout("Make: Howto line without a definition\r\n");
X				errout("Make: '");errout(inline);errout("'\r\n");
X			}
X			/* now split the line up into command and args */
X			for (pos=0;isspace(inline[pos]); pos++);
X				;
X			for (i=pos; !isspace(inline[i]) && inline[i]!='\0'; i++)
X				;
X			/* if there is something there, allocate mem and copy */
X			if (i != pos) {
X				/* get a new struct */
X				howp = getmem(sizeof(struct howrec));
X				/* add it to the end of howlist */
X				if (defnp->howto == NULL)
X					defnp->howto = howp;
X				else {
X					howp2 = defnp->howto;
X					while (howp2->nexthow != NULL)
X						howp2 = howp2->nexthow;
X					howp2->nexthow = howp;
X				}
X				/* copy command filename */
X				howp->howcom = getmem(i-pos+1);
X				for(j=0; pos < i; )
X					howp->howcom[j++] = inline[pos++];
X				howp->howcom[j] = '\0';
X				/* now look for any argumentative part */
X				while (isspace(inline[pos]))
X					pos++;
X				howp->howargs = getmem(strlen(inline)-pos + 1);
X				for(i=0; inline[pos] != '\0'; )
X					howp->howargs[i++] = inline[pos++];
X				howp->howargs[i] = '\0';
X				howp->nexthow = NULL;
X			}
X		}
X	}
X}
X
X
Xuppercase(s)
Xchar *s;
X{
X	for( ; *s != '\0'; s++)
X		*s = toupper(*s);
X}
X
X
Xchar *getmem(size)
Xint size;
X{
X	char *p;
X	if ((p = malloc(size)) == 0) {
X		errout("Make: Ran out of memory...\r\n");
X		exit(-1);
X	}
X	return(p);
X}
X
X
Xlong getmodified(name,which)
Xchar *name;
Xint which;
X{
X	static int save_es, dta_seg, dta_offset;
X	long datetime, dateof(), max(), min();
X#asm
X	mov		word getmodified_save_es_,es	; save ES just in case
X	mov		ah,2fh							; get dta into es:bx
X	int		21h
X	mov		word getmodified_dta_seg_,es
X	mov		word getmodified_dta_offset_,bx
X	mov		es,word getmodified_save_es_	; restore
X#
X	datetime = 0;	/* as old as possible if does not exist */
X	if (_os(0x4e,name) == 0) {	/* at least one matching file exists */
X		datetime = dateof(dta_seg,dta_offset);
X		/* now loop through all the rest of the matching files */
X		while (_os(0x4f,0) == 0) {
X			if (which == DEPENDANT)
X				datetime = max(datetime, dateof(dta_seg,dta_offset));
X			else	/* this is DEFINED */
X				datetime = min(datetime, dateof(dta_seg,dta_offset));
X		}
X	} else	/* doesn't exist */
X		datetime = 0;	/* as old as possible */
X	return(datetime);
X}
X
Xlong dateof(dta_seg,dta_offset)
X/*	return a long encoding the date and time of the file just found */
Xint  dta_seg, dta_offset;
X{
X	static long ret_dt;
X	static int save_es;
X
X#asm
X	mov		word dateof_save_es_,es		; save it just in case
X	mov		es,word [bp+4]
X	mov		bx,word [bp+6]
X	mov		ax,es:[bx+22]
X	mov		word dateof_ret_dt_,ax	; store time in low word of ret_dt.
X	mov		ax,es:[bx+24]
X	mov		word dateof_ret_dt_+2,ax	; date to high word of ret_dt
X	mov		es,word dateof_save_es_		; restore
X#
X	return ret_dt;
X}
X
Xlong max(a,b)
Xlong a,b;
X{
X	return(a>b ? a : b);
X}
X
Xlong min(a,b)
Xlong a,b;
X{
X	return(a>b ? b : a);
X}
/*EOF
echo extracting - find.c
sed 's/^X//' > find.c << '/*EOF'
X/*
X	Routines in DeSmet C to find files.
X	Requires DOS 2.0.
X	Imbedded assembler code makes portability difficult.
X	Used by 'Make'.  Requires 'Path'.
X*/
X
X
X/*
XWritten by John M Sellens, April, 1984
X
XCode is all original except where indicated otherwise.
X
XUntil August, 1984:
X	jmsellens@watrose.UUCP
X
X	107 - 180 Brybeck Cres.
X	Kitchener, Ontario
X	N2M 5G4
X
XAfter August, 1984:
X	c/o 1135 Lansdowne Ave. SW
X	Calgary, Alberta
X	T2S 1A4
X
X(c) Copyright 1984 John M Sellens
XPermission is granted to use, distribute and/or modify this code unless
Xdone for direct commercial profit.  If you find these routines useful,
Xmodest contributions (monetary or otherwise) will be gratefully accepted.
XAuthor's name, address and this notice must be included in any copies.
X
X*/
X
X
X#include <stdio.h>
X
Xfind(src,dest)
Xchar *src, *dest;
X{
X	/* look for a file (src) by following the PATH string
X	   if the file is found, copy its full name into dest
X	   and return TRUE; otherwise return FALSE.  No guar-
X	   antee what will be in dest if FALSE.  No guarantee
X	   that it will behave reasonably if the path string
X	   is silly.  If the src string contains a ':' or a '\',
X	   the PATH string is not followed.
X	*/
X
X	char *index();
X	char **paths;
X	int i;
X
X	/* see if src exists just as it is */
X	if (exist(src)) {
X		strcpy(dest,src);
X		return(TRUE);
X	}
X
X	if ( (paths = parse_path(get_path())) == NULL)
X		return(FALSE);	/* no path set, so can't be anywhere else */
X
X	/* if has a ':' or a '\', don't search PATH */
X	if ( (index(src,':')!=NULL) || (index(src,'\\')!=NULL) )
X		return(FALSE);
X
X
X	/* now try each path one by one */
X	for (i=0; paths[i] != NULL; i++) {
X		strcpy(dest,paths[i]);
X		if (dest[strlen(dest)-1] != '\\')
X			strcat(dest,"\\");
X		strcat(dest,src);
X		if (exist(dest)) {
X			free(paths);
X			return(TRUE);
X		}
X	}
X
X	free(paths);
X	return(FALSE);
X
X}
X
X
Xexist(f)
Xchar *f;
X{
X	/* takes an ASCIIZ string and tests for existence of a
X	   'normal' file with that name.
X	*/
X
X	/* dangerous coding - assumes that if doesn't use CX */
X#asm
X	xor cx,cx	;set zero to look for only normal files
X#
X	if (_os(0x4E,f) == 0)
X		return(TRUE);
X	else
X		return(FALSE);
X
X}
/*EOF
echo extracting - path.c
sed 's/^X//' > path.c << '/*EOF'
X/* The following are two routines to make it easier to access the
X   PATH environment variable.  They are written for use with the
X   DeSmet C Compiler.  Known portability problems:
X		- assembler code embedded in the C code
X		- requires that code segment register (CS) be set to the
X		  same value as at program invocation so that the
X		  program segment prefix can be located.  This would
X		  probably only be a problem if a "large model"
X		  compiler is used.
X	The code is a little kludgy in places but...
X
X	Any suggestions for improvements gratefully accepted.
X*/
X
X
X/*
XWritten by John M Sellens, April, 1984
X
XCode is all original except where indicated otherwise.
X
XUntil August, 1984:
X	jmsellens@watrose.UUCP
X
X	107 - 180 Brybeck Cres.
X	Kitchener, Ontario
X	N2M 5G4
X
XAfter August, 1984:
X	c/o 1135 Lansdowne Ave. SW
X	Calgary, Alberta
X	T2S 1A4
X
X(c) Copyright 1984 John M Sellens
XPermission is granted to use, distribute and/or modify this code unless
Xdone for direct commercial profit.  If you find these routines useful,
Xmodest contributions (monetary or otherwise) will be gratefully accepted.
XAuthor's name, address and this notice must be included in any copies.
X
X*/
X
X
X
X#include <stdio.h>
X
Xtypedef char *char_ptr;
X
Xmain()
X{
X	/* Example of how to use parse_path() and get_path() */
X
X	int i;
X	char *get_path();
X	char_ptr *paths, *parse_path();
X
X	paths = parse_path(get_path());
X	if (paths != NULL)
X		for (i=0; paths[i]!=NULL; i++)
X			printf("%s\n",paths[i]);
X	else
X		printf("No path is set\n");
X}
X
X
X
Xchar_ptr *parse_path(ps)
Xchar *ps;
X{
X	/* Takes the path string returned by get_path and destroys it,
X	   by inserting NULL's and returning an array of pointers to
X	   characters that point to locations in the original path
X	   string.  The pointer after the last pointer into the path
X	   string is NULL.  If the path string is NULL, then NULL is
X	   returned.
X	*/
X
X	int i, j, num;
X	char_ptr *pa;
X
X	if (ps[0] == '\0')	/* no path */
X		return(NULL);
X
X	/* count the number of semi-colons, add 1 = number of paths */
X	for (num=1, i=0; ps[i] != '\0'; i++)
X		if (ps[i] == ';')
X			num++;
X
X	pa = (char_ptr *)malloc(sizeof(char_ptr) * (num+1));
X	pa[num] = NULL;
X
X	/* now loop through and point to each path */
X	for (i=j=0; i<num; i++) {
X		pa[i] = &ps[j];
X		while(ps[j]!=';' && ps[j]!='\0')
X			j++;
X		ps[j++] = '\0';
X	}
X
X	return(pa);
X
X}
X
X
Xchar *get_path()
X{
X	/* Returns a pointer to a string containing the value of
X	   the environment variable PATH.  E.g. if PATH=A:\;B:\
X	   the string "A:\;B:\" is returned.  If no PATH is set,
X	   an empty string is returned.
X	*/
X
X	static int path_seg;
X	/* these three declarations must be first so that they can be
X	   referred to by stack address near the end when copying
X	   the PATH variable */
X	char *base, *path;
X	int siz;
X
X	char *next;
X	char env_str[10];	/* leave a little extra space just in case */
X	int i, done;
X
X#asm
X;		determine the segment that the environment starts at
X;		this requires that cs is set to the same value as at
X;		program entry i.e. 100H past the program segment prefix
X		push es
X		mov ax,cs
X		sbb ax,10h
X		mov es,ax
X		mov ax,es:[2cH]
X		pop es
X		mov word get_path_path_seg_,ax
X#
X
X	/* look for the start of PATH= */
X	/* there's always going to be at least one thing in environment */
X	done = FALSE;
X	next = 0;
X	env_str[0] = '\0';
X	while ((strncmp("PATH=",env_str,5) != 0) && !done) {
X		base = next;
X		/* set DS to path_seg */
X#asm
X		push ds
X		mov ds,word get_path_path_seg_
X#
X		if (base[0] == '\0')
X			done = TRUE;	/* no more variables to look at */
X		else {
X			/* now copy 5 characters into memory */
X			for (i=0; i<5; i++)
X				env_str[i] = base[i];	/* ok since env_str is on stack */
X			/* now set next to first character after end of this string */
X			while ((next++)[0] != '\0')
X				;
X		}
X		/* reset DS */
X#asm
X		pop ds
X#
X	}
X	if (done) {	/* no PATH environment variable set */
X		path = (char *)malloc(1);
X		path[0] = '\0';
X	} else {
X		siz = next - base - 5;
X		path = (char *)malloc(siz);
X		base += 5;	/* skip over 'PATH=' */
X		/* copy the PATH string to path */
X#asm
X		push cx
X		push es
X		push ds
X		mov cx,ds
X		mov es,cx
X		mov ds,word get_path_path_seg_
X		mov si,word [bp-2]	;base
X		mov di,word [bp-4]	;path
X		mov cx,word [bp-6]	;siz
X		rep movsb
X		pop ds
X		pop es
X		pop cx
X#
X	}
X	return(path);
X}
/*EOF
echo extracting - errout.c
sed 's/^X//' > errout.c << '/*EOF'
Xerrout(s)
Xchar *s;
X/* Writes the string that is it's argument on the error output */
X/* Remember to use <cr><lf> not just '\n' i.e. '\r\n' */
X/* This is required on DeSmet C v2.2 since fprintf(stderr, ...) */
X/* doesn't work when output is redirected.                      */
X{
X	int i;
X	i = strlen(s);
X#asm
X	mov bx,2			;2 is the number of stderr
X	mov cx,word [bp-2]	;i
X	mov dx,word [bp+4]	;s
X	mov ah,40h
X	int 21h
X#
X}
/*EOF
echo extracting - makefile.dat
sed 's/^X//' > makefile.dat << '/*EOF'
Xmake.exe make.o find.o path.o errout.o
X	bind.exe make find path errout exec.o
X
Xmake32.exe make.o find.o path.o errout.o
X	bind.exe make find path errout exec.o -s8000 -omake32
X
Xmake.o make.c
X	c88.exe make
X
Xfind.o find.c
X	c88.exe find
X
Xpath.o path.c
X	c88.exe path
X
Xerrout.o errout.c
X	c88.exe errout
/*EOF

------ EOF ------
ls -l make.tx
cat >make.head <<'------ EOF ------'
-- Messages from file: PS:<ROSE>MAIL.TXT.1 --
   Thursday, August 9, 1984 10:25:35-PDT

       4)  9-Aug John M Sellens  Make (26163 chars)


Message 4 -- ************************
 9-Aug-84 01:25:20-PDT,26163;000000000001
Return-Path: <jmsellens%watmath%waterloo.csnet@csnet-relay.arpa>
Received: from csnet-relay by WASHINGTON.ARPA with TCP; Thu 9 Aug 84 01:24:42-PDT
Received: From waterloo.csnet by csnet-relay;  9 Aug 84 2:49 EDT
Date: Thu, 9 Aug 84 02:08:42 edt
From: John M Sellens <jmsellens%watmath%waterloo.csnet@csnet-relay.arpa>
To: rose@washington.arpa
Subject: Make

As per your request:

Here is DeSmet C code.  I can provide Lattice and CI86 C code (both
translated by others).  If necessary, I can provide a uuencoded version
(but only if necessary).

Please note that I encourage 'contributions'.  Since it is a
'research project', you may have limited funds.  If you do not have
limited funds, please remember that I do :-) .  Non-monetary
contributions (e.g. usefull utilities, hardware you don't need (got an
XT??), and undying gratitude) are also accepted.

The following stuff is what I posted:
-------------------------------------------------------

Okay - here is an updated version of Make for the IBM PC.
It is written in DeSmet C.  Lots of details below, including how
to get a compiled version (if you need one).  We have been using it
on an XT for a couple of months with no problems (we have about
125 or so inter-dependent files described in about 8 makefiles).

As it says below, any "contributions" to my welfare are very
gratefully accepted.

Tear at the dotted line, and run through 'sh'.

John M Sellens
UUCP:  watmath!watrose!jmsellens
CSNET: jmsellens%watrose@waterloo.csnet
ARPA:  jmsellens%watrose%waterloo.csnet@csnet-relay.arpa

----------------------------------------------------------------------
------ EOF ------
ls -l make.head
cat >makefile.dat <<'------ EOF ------'
make.exe make.o find.o path.o errout.o
	bind.exe make find path errout exec.o

make32.exe make.o find.o path.o errout.o
	bind.exe make find path errout exec.o -s8000 -omake32

make.o make.c
	c88.exe make

find.o find.c
	c88.exe find

path.o path.c
	c88.exe path

errout.o errout.c
	c88.exe errout
------ EOF ------
ls -l makefile.dat