[mod.sources] v06i104: A Make for MS-DOS and VAX/VMS

sources-request@mirror.UUCP (08/13/86)

Submitted by: ihnp4!killer!root
Mod.sources: Volume 6, Issue 104
Archive-name: msdos_mk

[  This is so far different from the "standard" make, that it probably
   does not make sense to try to port it, although such a job looks
   to be fairly straightforward.  Some of the ideas contained in this
   program might be interesting to other make fanciers besides myself.
   The manpage is, alas, "nroff output." --r$  ]

#!/bin/sh
# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
# Contents:  cmake.bat file.c macro.c make.bat make.c make.h make.man
#	makefile osdate.asm parsedir.c readme token.c
 
echo x - cmake.bat
sed 's/^XX//' > "cmake.bat" <<'@//E*O*F cmake.bat//'
XXlc -ms -ie:\lc\ make
XXlc -ms -ie:\lc\ macro
XXlc -ms -ie:\lc\ token
XXlc -ms -ie:\lc\ parsedir
XXlc -ms -ie:\lc\ file
XXmasm osdate;
XXlink \lc\s\c make macro token parsedir file osdate,make,nul,\lc\s\lc
@//E*O*F cmake.bat//
chmod u=rw,g=r,o= cmake.bat
 
echo x - file.c
sed 's/^XX//' > "file.c" <<'@//E*O*F file.c//'
XX#include <stdio.h>
XX#include "make.h"


XX/*
XX * Return file-node for 'fname'.
XX * If it doesn't exist, then create one.
XX */
XXFILENODE *filenode(fname)
XXchar *fname;
XX{
XX	FILENODE *f, *afnode(), *gfile();

XX	if((f = gfile(fname)) == NULL)
XX		f = afnode(fname);
XX	return f;
XX}


XX/*
XX * Add a dependency to the node 'fnd'.
XX * 'fnd' will depend on 'fname'.
XX */
XXaddfile(fnd, fname)
XXFILENODE *fnd;
XXchar *fname;
XX{
XX	NODE *n;
XX	FILENODE *f;

XX	if(fnd == NULL)			/* punt if no root file */
XX	{
XX		fprintf(stderr, "No current root, can't add dependency '%s'\n", 
XXfname);
XX		return;
XX	}

XX	f = filenode(fname);
XX	if((n = (NODE *)malloc(sizeof(NODE))) == NULL) allerr();
XX	n->nnext = fnd->fnode;
XX	fnd->fnode = n;
XX	n->nfile = f;
XX}


XX/*
XX * Add a line of method-text to the node 'fnode'.
XX */
XXaddmeth(fnode, methtext)
XXFILENODE *fnode;
XXchar *methtext;
XX{
XX	int len;
XX	char *new;

XX	if(fnode == NULL || methtext == NULL) return;

XX	len = strlen(methtext) + 2;
XX	if(fnode->fmake == NULL)
XX	{
XX		if((fnode->fmake = (char *)malloc(1)) == NULL) allerr();
XX		*(fnode->fmake) = 0;
XX	}
XX	len += strlen(fnode->fmake);

XX/* Lattice C doesn't have 'realloc()', so this kludges around it: */
XX	if((new = (char *)malloc(len)) == NULL) allerr();
XX	strcpy(new, fnode->fmake);
XX	free(fnode->fmake);
XX	fnode->fmake = new;

XX	strcat(fnode->fmake, methtext);
XX	len = strlen(fnode->fmake);
XX	if(len && fnode->fmake[len - 1] != '\n')
XX		strcat(fnode->fmake, "\n");
XX}


XX/*
XX * Get a filenode for the file called 'fn'.
XX * Returns NULL if the node doesn't exist.
XX */
XXFILENODE *gfile(fn)
XXchar *fn;
XX{
XX	FILENODE *f;

XX	for(f = froot; f != NULL; f = f->fnext)
XX		if(!strcmp(fn, f->fname)) return f;
XX	return NULL;
XX}


XX/*
XX * Alloc space for a new file node.
XX */
XXFILENODE *afnode(name)
XXchar *name;
XX{
XX	FILENODE *f;

XX	for(f=froot; f; f=f->fnext)
XX		if(!strcmp(name, f->fname)) return f;

XX	if((f = (FILENODE *)malloc(sizeof(FILENODE))) == NULL) allerr();
XX	if((f->fname = (char *)malloc(strlen(name)+1)) == NULL) allerr();
XX	strcpy(f->fname, name);
XX	f->fmake = NULL;
XX	f->fnode = NULL;
XX	f->fdate = NULL;
XX	f->fflag = 0;

XX	f->fnext = froot;
XX	froot = f;
XX	return f;
XX}


XX/*
XX * Print dependency tree.
XX */
XXprtree()
XX{
XX	FILENODE *f;
XX	NODE *n;

XX	for(f = froot; f != NULL; f = f->fnext)
XX	{
XX		printf("%s%s%s (%u, %u)\n",
XX			f->fname,
XX			(f->fflag & ROOTP) ? " (root)" : "",
XX			(f->fflag & REBUILT) ? " (rebuilt)" : "",
XX			(f->fdate != NULL) ? (f->fdate)->ds_high : 0,
XX			(f->fdate != NULL) ? (f->fdate)->ds_low : 0);
XX		if(f->fmake != NULL)
XX			printf("%s", f->fmake);
XX		for(n = f->fnode; n != NULL; n = n->nnext)
XX			printf("\t%s\n", (n->nfile)->fname);
XX		puts("");
XX	}
XX}
@//E*O*F file.c//
chmod u=rw,g=r,o= file.c
 
echo x - macro.c
sed 's/^XX//' > "macro.c" <<'@//E*O*F macro.c//'
XX#include <stdio.h>
XX#include "make.h"

XX/*
XX * Macro processing
XX */


XX/*
XX * Perform macro substitution from 'orig' to 'dest'.
XX * Return number of macro substitutions made.
XX * A macro reference is in one of two forms:
XX *		<MACCHAR>(macro-name)
XX *  	or	<MACCHAR><single-character>
XX *
XX * "<MACCHAR><MACCHAR>" expands to a single '<MACCHAR>'
XX */
XXmexpand(orig, dest, destsiz, macchar)
XXchar *orig, *dest;
XXint destsiz;
XXchar macchar;
XX{
XX	char *s, *d, mname[STRSIZ];
XX	int di, count;
XX	MACRO *m;

XX	di = count = 0;
XX	for(s=orig; *s;)
XX		if(*s == macchar)
XX		{
XX			if(*++s == macchar)
XX			{
XX				if(di < destsiz-1) dest[di++] = *s++;
XX				continue;
XX			}

XX			if(!*s) break;
XX			d = mname;
XX			if(*s != '(') *d++ = *s++;
XX			else
XX			{
XX				for(++s; *s && *s!=')';) *d++ = *s++;
XX				if(*s != ')') puts("Missed matching ')'");
XX				else ++s;
XX			}
XX			*d = 0;
XX			if((d = gmacro(mname)) == NULL)
XX				fprintf(stderr, "Undefined macro: %s\n", mname);
XX			else
XX			{
XX				while(*d && di < (destsiz - 1))
XX					dest[di++] = *d++;
XX				++count;
XX			}
XX		} else if(di < destsiz-1)
XX			dest[di++] = *s++;

XX	dest[di]=0;
XX	return count;
XX}


XX/*
XX * Define a macro.
XX * Give the macro called 'name' the string expansion 'def'.
XX * Old macro-names are superseded, NOT replaced.
XX * Return ERROR if can't define the macro.
XX */
XXdefmac(name, def)
XXchar *name, *def;
XX{
XX	MACRO *m;

XX	if((m = (MACRO *)malloc(sizeof(MACRO))) == NULL) allerr();
XX	if((m->mname = (char *)malloc(strlen(name)+1)) == NULL) allerr();
XX	if((m->mvalue = (char *)malloc(strlen(def)+1)) == NULL) allerr();

XX	strcpy(m->mname, name);
XX	strcpy(m->mvalue, def);
XX	m->mnext = mroot;
XX	mroot = m;
XX}


XX/*
XX * undefmac - undefine a macro.
XX * Return 0 if macro was succesfully undefined, -1 if not found.
XX */
XXundefmac(name)
XXchar *name;
XX{
XX	MACRO *m = mroot;
XX	MACRO *prev = NULL;

XX	while(m != NULL && strcmp(name, m->mname))
XX	{
XX		prev = m;
XX		m = m->mnext;
XX	}

XX	if(m == NULL) return -1;
XX	if(prev == NULL) mroot = m->mnext;
XX	    else prev->mnext = m->mnext;

XX	free(m->mname);
XX	free(m->mvalue);
XX	free(m);
XX	return 0;
XX}


XX/*
XX * Lookup a macro called 'name'.
XX * Return a pointer to its definition,
XX * or NULL if it does not exist.
XX */
XXchar *gmacro(name)
XXchar *name;
XX{
XX	MACRO *m;

XX	for(m=mroot; m != NULL; m=m->mnext)
XX		if(!strcmp(name, m->mname)) return m->mvalue;
XX	return NULL;
XX}
@//E*O*F macro.c//
chmod u=rw,g=r,o= macro.c
 
echo x - make.bat
sed 's/^XX//' > "make.bat" <<'@//E*O*F make.bat//'
XXecho off
XXif exist make$$$$.bat del make$$$$.bat
XXxmake %1 %2 %3 %4 %5 %6 %7 %8 %9
XXif exist make$$$$.bat make$$$$.bat
@//E*O*F make.bat//
chmod u=rw,g=r,o= make.bat
 
echo x - make.c
sed 's/^XX//' > "make.c" <<'@//E*O*F make.c//'
XX#include <stdio.h>
XX#include <ctype.h>
XX#include "make.h"

XX/*
XX *	MAKE - Maintain seperate source files
XX *
XX *	SYNOPSIS
XX *		MK [-f file] [-a] [-n] [-d] [name] ...
XX *		   f: use 'file' instead of default makefile
XX *		   a: assume all modules are obsolete (recompile everything)
XX *		   n: don't recompile, just list steps to recompile
XX *		   d: debugging (print tree, file info)
XX *		   name: module name to recompile
XX *
XX *		'secret' options (not to be used by humans):
XX *		   -ofile	'file' is the script file to write to
XX *
XX *	AUTHOR
XX *		Landon M. Dyer, Atari Inc.
XX *
XX */

XX#define SCRIPTFILE "make$$$$.bat"	/* (default) script-listing file */
XX#define	INIT	"~INIT"			/* initialization macro */
XX#define	DEINIT	"~DEINIT"		/* de-init macro */
XX#define	BEFORE	"~BEFORE"		/* the per-root 'startup' method */
XX#define	AFTER	"~AFTER"		/* the per-root 'wrapup' method */


XXchar *mfiles[] = {			/* default makefiles */
XX	"makefile",

XX#ifdef VAXVMS
XX	"[-]makefile",
XX	"sys$login:makefile",
XX#endif

XX#ifdef MSDOS
XX	"..\makefile",
XX#endif
XX	""
XX};


XXMACRO *mroot = NULL;		/* root of macro-list */
XXFILENODE *froot = NULL;		/* root of filenode-list */
XXFILENODE *firstf = NULL;	/* the very first filenode */
XXFILE *mkfp = NULL;		/* script file */
XXchar *modnames[MAXMODS];	/* module-names mentioned in commandline */
XXint modcount = 0;		/* #of module-names */
XXint debug = 0;			/* nonzero: turn on debugging */
XXint obsolete = 0;		/* nonzero: every file should be recompiled */
XXint noscript = 0;		/* nonzero: print methods on standard output */
XXchar *scriptf = SCRIPTFILE;	/* default script file */
XXDATE bigbang;			/* a date, the very earliest possible */
XXDATE endoftime;			/* a date, the very last possible */


XXmain(argc, argv)
XXint argc;
XXchar **argv;
XX{
XX	int arg, i;
XX	char *mfile = NULL;
XX	DATE adate();

XX	bigbang = adate(0, 0);		/* init root dates */
XX	endoftime = adate(~0, ~0);

XX	for(arg = 1; arg < argc; ++arg)
XX		if(*argv[arg] == '-') switch(tolower(argv[arg][1]))
XX		{
XX		   case 'f':
XX			if(++arg >= argc)
XX			{
XX				fprintf(stderr, "-f needs filename argument.\n")
XX;
XX				return;
XX			}
XX			mfile = argv[arg];
XX			break;

XX		   case 'a':
XX			obsolete = 1;
XX			break;

XX		   case 'n':
XX			noscript = 1;
XX			break;

XX		   case 'd':
XX			debug = 1;
XX			break;

XX		   case 'o':
XX		   	scriptf = argv[arg] + 2;
XX			break;

XX		   default:
XX			fprintf(stderr, "Unknown switch: %c\n", argv[arg][1]);
XX			break;
XX		} else if(modcount < MAXMODS)
XX			modnames[modcount++] = argv[arg];
XX		else
XX		{
XX			fprintf(stderr, "Too many module names.\n");
XX			return;
XX		}

XX	if(mfile != NULL)
XX	{
XX		if(fmake(mfile) == -1)
XX			fprintf(stderr, "Cannot open makefile '%s'.\n", mfile);
XX	} else {
XX		for(i = 0; *mfiles[i]; ++i)
XX			if(fmake(mfiles[i]) != -1) break;
XX		if(!*mfiles[i])
XX			fprintf(stderr, "Cannot open makefile.\n");
XX	}

XX	if(debug) prtree();
XX}


XX/*
XX * Construct dependency tree from the makefile 'fn'.
XX * Figure out what has to be recompiled, and write a script file to do that.
XX */
XXfmake(fn)
XXchar *fn;
XX{
XX	FILE *fp;

XX	if((fp = fopen(fn, "r")) == NULL) return -1;

XX	fparse(fp);
XX	determ();

XX	fclose(fp);
XX	return 0;
XX}


XX/*
XX * Parse the input file, defining macros and building the dependency tree.
XX */
XXfparse(fp)
XXFILE *fp;
XX{
XX	char ibuf[STRSIZ], ebuf[STRSIZ];
XX	char *strp, *tok1, *tok2, *s;
XX	FILENODE *lastf = NULL;
XX	FILENODE *sf;

XX	for(;;)
XX	{
XX		if(fgets(ibuf, STRSIZ, fp) == NULL) break;
XX		mexpand(ibuf, ebuf, STRSIZ, MACCHAR);
XX		escape(ebuf, COMCHAR);

XX			/* clobber last newline in string */
XX		s = ebuf + strlen(ebuf) - 1;
XX		if(s >= ebuf && *s == '\n') *s = '\0';

XX		if(*ebuf == '\t')
XX		{
XX			addmeth(lastf, ebuf+1);
XX			continue;
XX		}

XX		strp = ebuf;
XX		if((tok1 = token(&strp)) == NULL) continue;
XX		if((tok2 = token(&strp)) != NULL)
XX			if(!strcmp(tok2, DEFMAC))
XX			{
XX				if(*strp) defmac(tok1, strp);
XX				else if(undefmac(tok1) < 0)
XX				    fprintf(stderr,
XX					  "Can't undefine macro '%s'\n", tok1);
XX				continue;
XX			}
XX			else if(!strcmp(tok2, DEPEND))
XX			{
XX				addmeth(lastf, gmacro(AFTER));

XX				lastf = filenode(tok1);
XX				if(firstf == NULL) firstf = lastf;
XX				lastf->fmake = NULL;

XX				addmeth(lastf, gmacro(BEFORE));

XX				lastf->fflag |= ROOTP;
XX				while((tok1 = token(&strp)) != NULL)
XX					addfile(lastf, tok1);
XX				continue;
XX			}
XX			else addfile(lastf, tok2);

XX		do {
XX			addfile(lastf, tok1);
XX		} while((tok1 = token(&strp)) != NULL);
XX	}

XX	addmeth(lastf, gmacro(AFTER));
XX}


XX/*
XX * Determine sequence of recompiles from the creation dates.
XX * If there is anything to recompile, then create a script file full of commands
XX.
XX */
XXdeterm()
XX{
XX	FILENODE *f;
XX	int i;
XX	char *m;

XX	if(firstf == NULL)			/* empty tree */
XX	{
XX		printf("No changes.\n");
XX		return;
XX	}

XX	if(modcount == 0) examine(firstf, endoftime);
XX	else for(i = 0; i < modcount; ++i)
XX	{
XX		if((f = gfile(modnames[i])) == NULL)
XX		{
XX			fprintf(stderr, "Can't find root '%s'.\n", modnames[i]);
XX			continue;
XX		}

XX		if(f->fflag & ROOTP == 0)
XX		{
XX			fprintf(stderr, "'%s' is not a root!\n", f->fname);
XX			continue;
XX		}
XX		examine(f, endoftime);
XX	}

XX	if(mkfp != NULL)
XX	{
XX		if((m = gmacro(DEINIT)) != NULL)
XX		{
XX			fputs(m, mkfp);
XX			fputc('\n', mkfp);
XX		}
XX		fclose(mkfp);
XX	} else printf("No changes.\n");
XX}


XX/*
XX * Examine filenode 'fnd' and see if it has to be recompiled.
XX * 'date' is the last-touched date of the node's father
XX * (or 'endoftime' if its a root file.)
XX * Root files with NO dependencies are assumed not to be up to date.
XX */
XXexamine(fnd, date)
XXFILENODE *fnd;
XXDATE date;
XX{
XX	int rebuildp = 0;
XX	NODE *n;

XX	getdate(fnd);
XX	if(fnd->fnode == NULL && fnd->fflag & ROOTP)
XX		rebuildp = 1;
XX	else for(n = fnd->fnode; n != NULL; n = n->nnext)
XX		if(examine(n->nfile, fnd->fdate)) rebuildp = 1;

XX	if(rebuildp) recomp(fnd);
XX	if(obsolete || laterdt(fnd->fdate, date) >= 0)
XX		rebuildp = 1;
XX	return rebuildp;
XX}


XX/*
XX * Make sure a filenode gets recompiled.
XX */
XXrecomp(f)
XXFILENODE *f;
XX{
XX	FILENODE *sf;
XX	char *m;

XX	if(mkfp == NULL)
XX	{
XX		if(noscript) mkfp = stdout;
XX		else if((mkfp = fopen(scriptf, "w")) == NULL)
XX			fprintf(stderr, "Cannot create: '%s'\n", scriptf);

XX	if((m = gmacro(INIT)) != NULL)
XX		{
XX			fputs(m, mkfp);
XX			fputc('\n', mkfp);
XX		}
XX	}

XX	if(f->fflag & REBUILT) return;
XX	if(f->fmake != NULL) fputs(f->fmake, mkfp);
XX	f->fflag |= REBUILT;
XX}


XX/*
XX * Complain about being out of memory, and then die.
XX */
XXallerr() {
XX	fprintf(stderr, "Can't alloc -- no space left (I give up!)\n");
XX	exit(1);
XX}
@//E*O*F make.c//
chmod u=rw,g=r,o= make.c
 
echo x - make.h
sed 's/^XX//' > "make.h" <<'@//E*O*F make.h//'
XX/* #define	VAXVMS	1 */		/* uncomment for VAX/VMS */
XX/* #define	MSDOS	1 */		/* uncomment for MSDOS */

XX#ifdef VAXVMS
XX#define ESCCHAR	`\\`		/* ok to use backslash on VMS */
XX#endif

XX#ifdef MSDOS
XX#define	ESCCHAR	'`'		/* since pathname char is backslash (yech) */
XX#endif

XX#define	MACCHAR '#'		/* macro-definition char */
XX#define	COMCHAR	'!'		/* comment char */
XX#define	DEFMAC	"="		/* macro-definition token */
XX#define	DEPEND	":"		/* dependency-definition token */

XX#define	DEBUG	if(0)
XX#define	STRSIZ	512
XX#define	MAXMODS	50

XX/* file attributes */
XX#define	REBUILT	0x01		/* file has been reconstructed */
XX#define	ROOTP	0x02		/* file was named on left side of DEPEND */


XXstruct date_str {
XX	unsigned ds_low, ds_high;
XX};
XXtypedef struct date_str *DATE;


XXstruct node {
XX	struct filenode *nfile;	/* this node's file */
XX	struct node *nnext;	/* the next node */
XX};
XXtypedef struct node NODE;


XXstruct filenode {
XX	char *fname;		/* the filename */
XX	char *fmake;		/* remake string for file */
XX	DATE fdate;		/* 32 bit last-modification date */
XX	NODE *fnode;		/* files this file depends on */
XX	char fflag;		/* magic flag bits */
XX	struct filenode *fnext;	/* the next file */
XX};
XXtypedef struct filenode FILENODE;


XXstruct macro {
XX	char *mname;		/* the macro's name */
XX	char *mvalue;		/* the macro's definition */
XX	struct macro *mnext;	/* the next macro */
XX};
XXtypedef struct macro MACRO;


XXextern MACRO *mroot;
XXextern FILENODE *froot;
XXextern DATE bigbang;		/* Far, far in the past */
XXextern DATE endoftime;		/* Far, far in the future */
XXchar *gmacro();
XXFILENODE *filenode(), *gfile();
XXchar *token();
@//E*O*F make.h//
chmod u=rw,g=r,o= make.h
 
echo x - make.man
sed 's/^XX//' > "make.man" <<'@//E*O*F make.man//'
XXMAKE(I)				3/10/84					MAKE(I)



XXNAME
XX	MAKE - maintain multiple source files (VAX/VMS and MSDOS 2.0)


XXSYNOPSIS
XX	MAKE [-N] [-A] [-F makefile] [name ...]


XXDESCRIPTION
XX	MAKE is a utility inspired by the Unix(tm) command of the same
XX	name.  MAKE helps maintain programs that are constructed from
XX	many files.  MAKE processes a "makefile", a file which describes
XX	how to build a program from its source files, and produces a
XX	script file containing the commands necessary to recompile the
XX	program.

XX	Be careful: this MAKE is NOT compatible with Unix(tm) MAKE!

XX	The 'N' option causes MAKE to print out the steps it would follow
XX	in order to rebuild the program.  The 'A' option tells MAKE to
XX	assume that all files are obsolete, and that everything should be
XX	recompiled.  The 'F' option, followed by a filename, can be used
XX	to specify a makefile other than the default one.

XX	If no names are specified in the commandline, the first dependency
XX	in the makefile is examined.  Otherwise, the specified root names
XX	are brought up to date.

XX	The default makefiles are:

XX		for VAX/VMS:	MAKEFILE
XX				[-]MAKEFILE
XX				SYS$LOGIN:MAKEFILE

XX		for MSDOS:	MAKEFILE
XX				..\MAKEFILE

XX	If the first makefile cannot be found, MAKE attempts to use the
XX	next one.  If no makefile is ever found, MAKE prints a diagnostic
XX	and aborts.





XXTHE MAKEFILE
XX	Comments begin with '!' and extend to the end of the line.  A
XX	'!' (or almost any other character) may be escaped with the escape
XX	character (backslash (\) on VMS, backquote (`) on MSDOS).  An escape
XX	character may be typed by doubling it (\\ or ``).  The standard
XX	Unix escape codes are recognized (\n, \r, \t, \b, \f, `n, `r, `t,
XX	`b and `f).

XX	A makefile is a list of dependencies.  A dependency consists of
XX	a root name, a colon, and zero or more names of dependent files.
XX	(The colon MUST be preceeded by whitespace.)  For instance, in:

XX		make.exe : make.obj parsedir.obj file.obj macro.obj mk.h

XX	the file 'make.exe' depends on five other files.  A root name
XX	with an empty dependency, as in:

XX		print :

XX	is assumed NEVER up to date, and will always be recompiled.

XX	The dependency list may be continued on successive lines:

XX		bigfile.exe : one.obj two.obj three.obj four.obj
XX		five.obj six.obj gronk.obj freeple.obj scuzzy.lnk
XX		frog.txt greeble.out

XX	Any number of 'method' lines may follow a dependency.  Method lines
XX	begin with an ascii tab.  When a file is to be recompiled, MAKE
XX	copies these method lines (minus the tab) to the script file.
XX	For example, in:

XX		make.exe : make.obj parsedir.obj file.obj macro.obj mk.h
XX			$link make, parsedir, file, macro
XX			$write sys$output "Just another version of MAKE ..."
XX			$purge

XX	the three lines following the dependency make up the method for
XX	recompiling (or in this case, re-linking) the file 'make.exe'.

XX	If the macro "~INIT" is defined, its text will appear first in the
XX	script file.  If the macro "~DEINIT" is defined, its text will
XX	appear last in the script file.  By defining these two macros, it
XX	is possible to configure the shell enviroment:

XX		~INIT = $set term/nowrap\n$on error then goto err_handler
XX		~DEINIT = $set term/wrap\n$exit\$err_handler:\n
XX		~DEINIT = #(~DEINIT)$type err.log\n$exit

XX	will expand (in the script file) to:

XX		$set term/nowrap
XX		$on error then goto err_handler
XX		.
XX		.
XX		$set term/wrap
XX		$exit
XX		$err_handler:
XX		$type err.log
XX		$exit

XX	When a root's method is defined, the value of the macro "~BEFORE"
XX	is prefixed to the method, and the value of the macro "~AFTER" is
XX	appended to it.

XX	Frequently one wants to maintain more than one program with a single
XX	makefile.  In this case, a "master dependency" can appear first in
XX	the file:

XX		allOfMyToolsAndHorribleHacks : cat peek poke.exe grunge
XX		cat : cat.exe
XX		cat.exe : ....
XX			(stuff for CAT.EXE)
XX		peek : peek.exe
XX		peek.exe : (stuff for PEEK.EXE)
XX		poke.exe : (stuff for POKE.EXE)
XX		grunge : grunge.com
XX		grunge.com : (stuff for grung)

XX	In other words, make will bring everything up to date that is somehow
XX	connected to the first dependency (its assumed that the incredibly
XX	lengthy filename specified in this example won't actually exist).









XXMACROS
XX	A macro is defined by a line of the form (the '=' MUST be surrounded
XX	by whitespace):

XX		<macro-name> = <macro-body>

XX	A macro may be deleted by assigning an empty value to it.  Macros
XX	may be redefined, but old definitions stay around.  If a macro is
XX	redefined, and the redefinition is later deleted, the first definition
XX	will take effect:

XX		MAC = first			! MAC = "first"
XX		MAC = second			! MAC = "second"
XX		MAC = #(MAC) third		! MAC = "second third"
XX		MAC =				! MAC = "second"
XX		MAC =				! MAC = "first"
XX		MAC =				! MAC has no definition

XX	A macro may be referenced in two ways:

XX			#<char>	  or	#(macro-name)

XX	The first way only works if the macro's name is a single character.
XX	If the macro's name is longer than one character, it must be
XX	enclosed in parenthesis.  ['#' may be escaped by doubling it ("##".)]
XX	For example, in:

XX		G = mk.h mk1.h
XX		OBJS = make.obj file.obj parsedir.obj macro.obj
XX		BOTH = #(OBJS) #G
XX	
XX		make.exe : #(OBJS) #G
XX		make.exe : #(BOTH)
XX		make.exe : mk.h mk1.h make.obj file.obj parsedir.obj macro.obj
XX			$write sys$output "This is a number sign --> ##"

XX	after macro expansion, the three dependencies will appear identical
XX	and the two '#'s in the last line will turn into one '#'.










XXUNIX(tm) MAKE AND THIS ONE
XX	They are NOT the same.  Do not expect Unix makefiles to work with
XX	this MAKE, even if you change the pathnames.  There are some major
XX	differences between this version and the standard Unix(tm) MAKE:

XX	1. The Unix(tm) comment character is '#', VAX/VMS's is '!'.

XX	2. The Unix(tm) macro-expansion character is '$'.  While this would
XX	   have been easy to leave the same, the '$' character is used so
XX	   often in VAX/VMS command-lines that I thought it best to change
XX	   it to '#'.

XX	3. Multiple root names are not allowed.  Unix(tm) MAKE accepts lines
XX	   of the form:

XX		name1 name2 : depend1 depend2

XX	   but this one doesn't.

XX	4. There is no equivalent of double-colon ("::".)

XX	5. There is no equivalent of .SUFFIXES, or the corresponding special
XX	   macros.

























XXSAMPLE MAKEFILE
XX	!
XX	! VAX/VMS MAKE
XX	! Landon Dyer
XX	!
XX	H = make.h
XX	FILES = #H, make.c, macro.c, token.c, parsedir.c, file.c
XX	DOCUMENTATION = distr.mem make.man makefile. make.com
XX	
XX	make.exe : make.obj macro.obj token.obj parsedir.obj file.obj
XX		$link make.obj, macro, token, parsedir, file
XX		$purge
XX	
XX	make.obj : make.c #H
XX		$cc make.c
XX	
XX	macro.obj : macro.c #H
XX		$cc macro
XX	
XX	token.obj : token.c #H
XX		$cc token
XX	
XX	parsedir.obj : parsedir.c #H
XX		$cc parsedir
XX	
XX	file.obj : file.c
XX		$cc file
XX	
XX	!
XX	! Print files associated with MAKE
XX	!
XX	print :
XX		$print make.man, #(FILES), make.com, makefile.
XX	
XX	!
XX	! Type out source to MAKE
XX	!
XX	type :
XX		$type #(FILES), make.com, makefile.
XX	
XX	!
XX	! Make backup of source files.
XX	!
XX	BACKUP = [.bak]
XX	backup :
XX		$copy #(FILES) #(BACKUP)
XX		$copy make.man, make.com, makefile. #(BACKUP)
XX	
XX	!
XX	! Collect MAKE into a distribution file.
XX	!
XX	collect :
XX		$collect collect distr.mem make.man makefile make.com make.h -
XX			make.c macro.c token.c parsedir.c file.c


XXAUTHOR
XX	Landon Dyer			G.DYER@SU-SCORE.ARPA
XX	175 Calvert Dr. #F-211		BASHFL::DYER (Atari Coinop)
XX	Cupertino, CA 95014
@//E*O*F make.man//
chmod u=rw,g=r,o= make.man
 
echo x - makefile
sed 's/^XX//' > "makefile" <<'@//E*O*F makefile//'
XX!
XX! MSDOS Make utility
XX! (compile with Lattice C version 2.1)
XX!

XXCLIB = e:\lc\s\lc
XXCOBJ = e:\lc\s\c
XXLCS = lc -ms -ie:\lc\
XXH = make.h
XXFILES = #H make.c macro.c token.c parsedir.c file.c osdate.asm
XXDOCUMENTATION = readme make.man makefile

XXxmake.exe : make.obj macro.obj token.obj parsedir.obj file.obj osdate.obj
XX	link #(COBJ) make macro token parsedir file osdate,xmake,nul,#(CLIB)

XXmake.obj : make.c #H
XX	#(LCS) make

XXmacro.obj : macro.c #H
XX	#(LCS) macro

XXtoken.obj : token.c #H
XX	#(LCS) token

XXparsedir.obj : parsedir.c #H
XX	#(LCS) parsedir

XXfile.obj : file.c
XX	#(LCS) file

XXosdate.obj : osdate.asm
XX	masm osdate;

XX!
XX! Print files associated with MAKE
XX!
XXprint :
XX	print make.man #(FILES) makefile


XX!
XX! collect source and documentation files
XX!
XXcollect :
XX	collect -o make.col @make.lis


XX!
XX! copy to distribution disk (on A:)
XX!
XXdistribution :
XX	copy readme a:
XX	copy make.man a:
XX	copy makefile a:
XX	copy make.bat a:
XX	copy make.c a:
XX	copy macro.c a:
XX	copy token.c a:
XX	copy parsedir.c a:
XX	copy file.c a:
XX	copy osdate.asm a:
XX	copy cmake.bat a:
XX	copy make.lis a:
XX	copy xmake.exe a:
@//E*O*F makefile//
chmod u=rw,g=r,o= makefile
 
echo x - osdate.asm
sed 's/^XX//' > "osdate.asm" <<'@//E*O*F osdate.asm//'
XXdos	=	21h

XXarg1	=	4			; lattice argument indexes
XXarg2	=	arg1+2
XXarg3	=	arg2+2

XXpgroup	group	prog
XXprog	segment byte public 'prog'
XX	public	osdate
XX	assume	cs:pgroup

XX;
XX;------
XX; OSDATE - return file's creation-date (called from Lattice), or -1
XX;	   if can't find the file.
XX; Synopsis:
XX;		int osdate(filename, time1, time2)
XX;			char *filename;
XX;			int *time1, *time2;
XX;
XXosdate proc near
XX	push	bp
XX	mov	bp,sp

XX;--- Open the file
XX	mov	dx,[bp+arg1]
XX	xor	al,al
XX	mov	ah,3dh
XX	int	dos
XX	jc	osd$err			; can't, so complain

XX;--- Get file's creation date and time
XX	mov	bx,ax			; get handle's date info
XX	xor	al,al
XX	mov	ah,57h
XX	int	dos
XX	jc	osd$cls			; "can't happen" (but close it)

XX;--- Install date/time info into caller's variables
XX	mov	si,[bp+arg2]		; *arg2 = time (least significant)
XX	mov	[si],cx
XX	mov	si,[bp+arg3]		; *arg3 = date (most significant)
XX	mov	[si],dx

XX;--- Close file & return (ok)
XX	mov	ah,3eh
XX	int	dos
XX	xor	ax,ax
XX	pop	bp
XX	ret

XX;--- Close file & return error condition
XXosd$cls:
XX	mov	ah,3eh
XX	int	dos
XXosd$err:
XX	mov	ax,-1
XX	pop	bp
XX	ret
XXosdate endp

XXprog	ends
XX	end
@//E*O*F osdate.asm//
chmod u=rw,g=r,o= osdate.asm
 
echo x - parsedir.c
sed 's/^XX//' > "parsedir.c" <<'@//E*O*F parsedir.c//'
XX#include <stdio.h>
XX#include "make.h"
XX#ifdef VAXVMS
XX#include <rms.h>
XX#endif


XX/*
XX * Get a file's creation date.
XX */
XXint getdate(f)
XXFILENODE *f;
XX{
XX	if(f->fdate != NULL || filedate(f) != -1) return;

XX	if(f->fflag & ROOTP == 0)
XX	{
XX		fprintf(stderr, "Can't get date for file '%s'\n", f->fname);
XX		f->fdate = endoftime;
XX	} else f->fdate = bigbang;
XX	return;
XX}


XX#ifdef VAXVMS
XX/*
XX * filedate - return file's creation date (VAX/VMS only.)
XX * Returns -1 if file cannot be found, 0 if succesful.
XX */
XXfiledate(fnd)
XXFILENODE *fnd;
XX{
XX	unsigned *datetime;
XX	DATE adate();
XX	struct FAB *fptr;
XX	struct XABDAT *dptr;

XX	fptr = malloc(sizeof(struct FAB));	/* allocate FAB and XABDAT */
XX	dptr = malloc(sizeof(struct XABDAT));
XX	if(fptr == NULL || dptr == NULL) allerr();
XX	*fptr = cc$rms_fab;			/* initialize FAB and XABDAT */
XX	*dptr = cc$rms_xabdat;
XX	fptr->fab$l_xab = (char *) dptr;	/* FAB -> XABDAT */

XX	fptr->fab$l_fna = fnd->fname;		/* setup filename */
XX	fptr->fab$b_fns = strlen(fnd->fname);

XX	if(sys$open(fptr) != RMS$_NORMAL ||	/* open the file */
XX	   sys$display(fptr) != RMS$_NORMAL)	/* get XABDAT info */
XX		return -1;

XX	datetime = &(dptr->xab$q_cdt);		/* record 64-bit date */
XX	fnd->fdate = adate(datetime[0], datetime[1]);

XX	sys$close(fptr);			/* close the file */

XX	free(dptr);				/* clean up and return */
XX	free(fptr);
XX	return 0;
XX}
XX#endif


XX#ifdef MSDOS
XX/*
XX * filedate - return file's creation date (MSDOS only.)
XX * Returns -1 if file cannot be found, 0 if successful
XX */
XXfiledate(fnd)
XXFILENODE *fnd;
XX{
XX	unsigned date, time;
XX	DATE adate();

XX	if(osdate(fnd->fname, &time, &date) == -1) return -1;
XX	fnd->fdate = adate(time, date);
XX}
XX#endif


XX/*
XX * laterdt - compare two dates.
XX * Return -1, 0 or 1 if date1 < date2, date1 == date2, or date1 > date2
XX */
XXlaterdt(date1, date2)
XXDATE date1, date2;
XX{
XX	if(date1->ds_high > date2->ds_high ||
XX	   (date1->ds_high >= date2->ds_high &&
XX	    date1->ds_low > date2->ds_low)) return 1;
XX	else if(date1->ds_high == date2->ds_high &&
XX	   date1->ds_low == date2->ds_low) return 0;
XX	else return -1;
XX}


XX/*
XX * adate - allocate a date with the given time
XX */
XXDATE adate(time1, time2)
XXunsigned time1, time2;
XX{
XX	DATE d;

XX	if((d = (DATE)malloc(sizeof(struct date_str))) == NULL) allerr();
XX	d->ds_low = time1;
XX	d->ds_high = time2;
XX	return d;

XX}
@//E*O*F parsedir.c//
chmod u=rw,g=r,o= parsedir.c
 
echo x - readme
sed 's/^XX//' > "readme" <<'@//E*O*F readme//'

XXLast month I wrote a version of the Unix(tm) utility MAKE.  It runs under
XXVAX/VMS and MSDOS 2.0.  I am placing it in the public domain, and it is yours
XXfor the asking.  You may copy it, or give it away.  You can make any changes
XXyou like to it.  All I ask is that you DO NOT TRY TO SELL IT.

XXAnyway, there is now a MAKE for MSDOS.  It is free, and it works pretty well.
XXI'm giving it away because it might do the world some good.  Who knows?

XXCaveat: this version of MAKE is NOT compatible with the Unix(tm) version.
XXSome differences are explained in the documentation.  Most of the problem stems
XXfrom the fact that I've never had a chance to use the original version of MAKE,
XXand the documentation I've seen on it has been poor.  My idea of what a make
XXprogram should do is almost certainly different from what you Unix(tm) hackers
XXare used to.  Well, hell -- the software is worth what you paid for it.  Have
XXfun.

XXIn order to get MAKE running on your system, you need to:

XX	1.  Read the documentation file MAKE.MAN.  (Yes, read the
XX	    directions.)

XX	2.  Edit the file MAKE.H to represent your system (VAX/VMS or
XX	    MSDOS 2.0.)

XX	3.  Recompile the source code by following the script file
XX	    CMAKE.COM (for VAX/VMS) or CMAKE.BAT (for MSDOS 2.0.)

XX	    VAX/VMS requires the DEC C compiler; MSDOS 2.0 requires
XX	    Lattice C (or another C compiler of comparable quality)
XX	    and the Macro Assembler.

XX	4.  Test out MAKE by running it on itself.  (Make a backup
XX	    first!)



XX			Good luck,

XX			Landon Dyer (G.DYER @ SU-SCORE)
@//E*O*F readme//
chmod u=rw,g=r,o= readme
 
echo x - token.c
sed 's/^XX//' > "token.c" <<'@//E*O*F token.c//'
XX#include <stdio.h>
XX#include <ctype.h>
XX#include "make.h"

XX/*
XX * Get next token from the string.  Return a pointer to it, or NULL.
XX * Adjust pointer to point to next part of string.
XX * The string is modified.
XX * A token consists of any number of non-white characters.
XX */
XXchar *token(strpp)
XXchar **strpp;
XX{
XX	char *s, *beg;

XX	stripwh(strpp);
XX	if(!**strpp) return NULL;

XX	beg = s = *strpp;
XX	while(*s && !isspace(*s)) ++s;
XX	if(*s) *s++ = '\0';
XX	*strpp = s;
XX	return beg;
XX}


XX/*
XX * Parse character escape-sequences in a line of text.
XX *	<EscChar><EscChar> = <EscChar>
XX *	<EscChar>n = newline, and so on
XX *	<EscChar><char> = <char>
XX * The string is truncated at the first non-escaped occurance of 'comchar'.
XX */
XXescape(str, comchar)
XXchar *str, comchar;
XX{
XX	char *d, c;

XX	for(d = str; *str && *str != comchar; ++str)
XX	    if(*str == ESCCHAR && *(str + 1)) switch((c = *++str))
XX		{
XX		   case ESCCHAR:
XX			*d++ = ESCCHAR;
XX			break;

XX		   case 'n':
XX			*d++ = '\n';
XX			break;

XX		   case 'r':
XX			*d++ = '\r';
XX			break;

XX		   case 't':
XX			*d++ = '\t';
XX			break;

XX		   case 'b':
XX			*d++ = '\b';
XX			break;

XX		   case 'f':
XX			*d++ = '\f';
XX			break;

XX		   default:
XX			*d++ = c;
XX			break;
XX		} else *d++ = *str;

XX	*d++ = 0;
XX}


XXstripwh(strpp)
XXchar **strpp;
XX{
XX	char *s;

XX	s = *strpp;
XX	while(isspace(*s)) ++s;
XX	return (*strpp = s);
XX}
@//E*O*F token.c//
chmod u=rw,g=r,o= token.c
 
echo Inspecting for damage in transit...
temp=/tmp/sharin$$; dtemp=/tmp/sharout$$
trap "rm -f $temp $dtemp; exit" 0 1 2 3 15
cat > $temp <<\!!!
      7     30    193 cmake.bat
    141    380   2556 file.c
    125    341   2263 macro.c
      4     21    116 make.bat
    315    920   6271 make.c
     64    263   1576 make.h
    296   1146   7304 make.man
     64    169   1070 makefile
     63    180   1058 osdate.asm
    110    336   2258 parsedir.c
     40    272   1535 readme
     83    211   1309 token.c
   1312   4269  27509 total
!!!
wc  cmake.bat file.c macro.c make.bat make.c make.h make.man makefile osdate.asm parsedir.c readme token.c | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp
if test -s $dtemp
then echo "Ouch [diff of wc output]:" ; cat $dtemp
else echo "No problems found."
fi
exit 0