[net.sources] make for msdos -aug 3

grayson@uiucuxc.Uiuc.ARPA (08/04/85)

				Aug. 3, 1985

	Here is the make.c for MSDOS which I got from the Time, Inc., bulletin
board in New York.  I've made several improvements to it, removed errors and
bugs, added features.  It's still not final, but some of you seem to want it
anyway.  It seems pretty reliable now.
	In the fall I will release a version that allows for automatic
insertion of filename extensions and separators, and a default file for
default rules to be read first.

	This version uses the Microsoft version 3.00 compiler, which provides
routines for executing other programs.  If you do not have the compiler, let me
know - I will post the .EXE file on a local BBS - it's 25K.

	uucp:	{ihnp4,pur-ee}!uiucdcs!uiucuxc!grayson
		Dan Grayson, Math Dept, Univ of Ill, Urbana 61801

-----Cut Here-----Cut Here-----Cut Here-----Cut Here-----
#!/bin/sh
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	make.doc
#	make.c
#	makefile
#	makelu.dat
#	mstring.h
#	mstring.c
echo shar: extracting make.doc
sed 's/^X//' << 'SHAR_EOF' > make.doc
XThe majority of the text of this file was written by:  
X
X                Jeffrey Spidle
X                Systems Analyst
X                Office of Continuing Education
X                Iowa State University
X                Ames,   IA 50011
X
XSince there is a similarity in operation between his program and this one,  I
Xsaw no reason to "re-invent the wheel".
X
X        This a utility called 'MAKE' and is a much simplified version of the 
XMAKE utility on UNIX (a trademark or something of AT&T).  This program was ori-
Xginally written by Larry Campbell of DEC using the CI-C86 compiler.  I have re-
Xwritten it using the Lattice C compiler Ver. 2.14 running under MS/PC-DOS 2.1x. 
XAdded features include macro capability, command line parsing of macros, silent
Xoperation (-s option), ignore/obey errors (-i option), faster operation, and 
Xthe ability to run any DOS-level command.       --      March 31,  1985
X
X                                Mike Hickey
X                                Systems Programmer
X                                Computer Center
X                                University of DC
X                                Washington, DC
X
X
X/* modifications made June, 1985 by Dan Grayson, 2409 S. Vine St. , Urbana, 
X        IL 61801 */
X
X
X
X        'MAKE' takes a file of dependencies (a  'makefile') and decides what
Xcommands have to be executed to bring the  files up to  date.  These commands 
Xare either executed directly from 'MAKE' or written to the standard output with-
Xout executing them.
X
X'MAKEFILE' format:
X
X        -       There must be a 'makefile' --  you can't take input from the
X                standard input.
X
X        -       The default name of the 'makefile' is 'MAKEFILE' on the default
X                disk.  Different 'makefiles' can be specified using the '-f' 
X                option on the command  line.  If the '-f' option is used, the 
X                default 'makefile' is not processed.
X
X        -       Any line starting with a '!' or a "#" is considered to be a
X                comment line and is ignored by MAKE.  So is a line which is
X                completely blank.
X
X        -       A line in a 'makefile' that starts with a tab or space is a 
X                'howto' line and consists of a command name followed by arg-
X                uments.  When
X                commands are executed, the PATH environment variable is used to
X                find the command, in (hopefully) the same manner as DOS does. 
X                'Howto' lines apply to the most recently preceding 'dependency'
X                line.  It is an error for a 'howto' line  to precede the first 
X                'dependency' line.  Howto lines may have any combination of the
X                following characters to the left of the command:
X
X                        @               will not echo the command line
X
X                        -               MAKE will ignore the exit code of the
X                                        command, i.e. the ERRORLEVEL of MSDOS.
X                                        Without this, make terminates when a
X                                        nonzero exit code is returned.
X
X                        +               MAKE will use command.com to execute
X                                        the command - you must use this if the
X                                        command is non resident and you use
X                                        io redirection with < or > or | .
X
X        -       A line of the form
X                foobar = this and that and more of those
X                is a symbol definition line.  Later in the makefile, any line
X                containing $foobar or $(foobar) will have that bit replaced by
X                ' this and that and more of those'.
X
X        -       Any other non-blank line is a 'dependency' line.  'Dependency'
X                lines consist of a filenames, then ':', then the filenames on
X                which the previous ones depend.  If one of the files (call this
X                one the target) to the
X                left of the ':' needs to be made, first all the files to right
X                will be made.  Then if any of the dates on the right is more
X                recent than the target, the howto lines (if any) following this
X                dependency line will be executed.  If there weren't any howto
X                lines, then we try to make the target according to any
X                existing wildcard dependency lines - see the next item.
X
X        -       There is a special sort of dependency line which allows for
X                wildcards in the file names.  It looks like this (along with
X                an example howto line) :
X
X                *.obj : *.c
X                        msc $*.c ;
X
X                This says that whenever a file of the form *.obj needs to be
X                made, say it's called foo.obj, then provided foo.c can
X                be made, we will make foo.obj by means of the command
X
X                        msc foo.c ;
X
X                which on my system, is the way I run the C compiler.
X
X
X                        Operation
XSyntax:
X        make [filename]  [-f makefilename] [-i] [-n] [-s]
X
X        -i      means continue even if an error is encountered while executing 
X                a command.
X
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
X        -f      specifies that the following argument is the name of a makefile
X                to be used instead of the default (MAKEFILE).
X
X        -s      suppress MAKE echoing commands.  Only text echoed from an
X                invoked program will appear on the screen.
X
X        First, MAKE reads all of the makefiles.  It then proceeds through all of
Xthe filename arguments,  'making' each one in turn, unless there are none, in
Xwhich case it makes the first item in the makefile.  A file is remade if it is 
Xout of date with respect to the files it depends on or is non-existent.  Depen-
Xdencies are processed in a 'tree' fashion,  so that the lowest-order files are 
Xremade first.
X
X        NOTE:   MAKE REQUIRES DOS 2.0 OR HIGHER
X
X
SHAR_EOF
echo 'Orignal Sum -> 42088     7'
echo -n 'Current Sum -> '
sum make.doc
echo shar: extracting make.c
sed 's/^X//' << 'SHAR_EOF' > make.c
X/*
X * make.c
X *
X *      An imitation of the Unix MAKE facility
X *
X *      Copyright (C) 1984 by Larry Campbell, 73 Concord St., Maynard, Mass.
X *      Rewritten w/modifications by Mike Hickey, University of DC
X *
X *      This software may be freely copied and disseminated for noncommercial 
X *      purposes, if and only if this entire copyright statement and notice 
X *      is included intact.  This software, and the information contained 
X *      herein, may not be used for commercial purposes without my prior 
X *      written permission.
X *
X *      This program runs a new shell (COMMAND.COM) for each command specified 
X *      in the makefile.  This, I recommend that you put a copy of the shell in
X *      ramdisk (if you have one).  Assuming your ramdisk is called, say, drive
X *      F:, you would:
X *      
X *
X *              COPY A:\COMMAND.COM F:
X *              SET COMSPEC=F:\COMMAND.COM
X *
X */
X
X/* Certain portions of this software are Copyright (C) 1985 by Dan Grayson,
X   2409 S. Vine St, Urbana, IL 61801, namely all those lines which differ
X   between versions 2.10 and 2.11.  Qualitative descriptions of these 
X   changes are described in the edit history below.
X        Provided this copyright notice is not changed, and VERS211 below
X   is not changed, and VERS211 is still prints when 'usage()' runs, these
X   portions may be freely copied and used for any purpose, with one exception,
X   namely:  those persons or corporations who claim a copyright on this
X   program or a part of it may not use these portions for any commercial
X   purpose whatsoever.  In particular, they may not collect royalties on
X   any version of this program which includes these portions. */
X
X/*
X * Edit history:
X *
X *  2.11        Fixed breakout_symbols, which tried to return a pointer to
X                        a local variable!
X                Made symbol substitution occur in all lines, not just shell
X                        command lines.
X                Fixed breakout_symbols, which blew up when a symbol was 
X                        undefined.
X                Allowed blank lines, which are ignored now.
X                Change command line length to 1000.
X                Fixed it so MAKE, when no targets are specified on the command
X                        line, will simply make the first target in the makefile.
X                Remove the command line symbol definition option.
X                Changed the line continuation character to \ (it was - )
X                Now symbol definition lines do NOT begin with $.
X                Fixed numerous bugs dealing with character arrays and their
X                        lengths.
X                Now a shell command line which begins with @ is not echoed.
X                A shell command line beginning with + is executed through
X                        command.com ; this makes io redirection and pipes
X                        available, but the exit code of the program cannot
X                        be checked due to a misfeature of command.com.
X                A shell command line beginning with - may return a nonzero
X                        exit code without halting 'make'.
X                Fixed it so a target:prerequisite line followed by no how-to
X                        lines is interpreted not as an error, and not as 
X                        sharing the how-to lines following the next
X                        target:prerequisite line, but is considered fulfilled
X                        by no action other than making all the 
X                        prerequisites.
X                Fixed the bug which meant the return code from the commmand
X                        was never dicovered.  This resulted from using 
X                        "system", which uses "command.com", which hides the
X                        return code of the program it runs.  Resident 
X                        commands can still be used, nevertheless.
X                Error messages now include the line number of the makefile,
X                        if relevant.
X                Made the return code of the command print out if nonzero.
X                Now the copyright notice only prints when the usage appears.
X                Convert to Microsoft vers 3.00, large memory model.
X                                - dan grayson
X *  2.10        Fix bug in abort routine, update copyright notice
X *  2.09        Set up for command line parsing of macros
X *  2.08        Remove edit 2.05; keep debug a compile-time option
X *  2.07        Finish macro parsing
X *  2.06        Add initial code for macro handling
X *  2.05        Add -d (debug) switch
X *  2.04        Add error message handling (doserror).
X *  2.03        Add -i (ignore errors) switch.
X *  2.02        Add -s (silent run) switch.
X *  2.01        Convert to Lattice 2.14.  Clean up code for faster execution.
X *  1.11        Make default makefilename be MAKEFILE.
X *  1.10        Add -n (trace) switch.
X *  1.09        Allow multiple targets before colon.
X *  1.08        Cleanup and speedup of makefile parsing.
X *  1.07        Add continuation lines (hyphen as last char of line)
X *  1.06        Don't need to make dummy FCBs, zero seems to work OK.
X *  1.05        Fix bug finding COMSPEC when it's not first env variable
X *  1.04        Wordier usage text, including copyright notice.
X *              Remove printf's (except when DEBUG on) to shrink EXE
X *  1.03        Don't uppercase shell command and fix datetime bug
X *  1.02        Random cleanup
X *  1.01        The beginning
X */
X
X#define VERSION "MAKE ver. 2.10 Copyright (C) 1984 by Larry Campbell, Maynard Mass."
X#define VERS211 "MAKE ver. 2.11 Portions copyright (C) 1985 by Dan Grayson, Urbana IL."
X#define LINT_ARGS       /* for Microsoft v. 3.00 */
X#include <assert.h>
X#include <process.h>
X#include <dos.h>
X#include <stdio.h>
X#include <malloc.h>
X#include <ctype.h>
X#include "mstring.h"
X#include <string.h>
X
Xchar *dos_commands[] = {
X      "dir", "type", "rem", "pause", "date", "time",
X      "ren", "rename", "ver", "vol", "break", "verify",
X      "mkdir", "md", "exit", "ctty", "echo", "if", "cls",
X      "chdir", "cd", "rmdir", "rd", "copy", "del", "erase",
X                         NULL };
X
X#define PREREQ_MAGIC 123
X#define FILE_MAGIC 543
X#define SHELL_MAGIC 678
X#define TARG_MAGIC 987
X#define SYMBOL_MAGIC 653
X#define MAXLIN 1000
X#define SYMLEN 1000
X#define MAXTARGETS 100
X#define TRUE 1
X#define FALSE 0
X#define EOS '\0'                /* End Of String */
X
X#define LINESIZE 1000            /* max length of input line */
X#define MAXCOMMON 8             /* max no. of targets with common prereqs */
X
X#define tfree(x) if (x) free(x),x=NULL
X
Xextern unsigned char _osmajor;           /* in MS ver 3, gives version of OS to left of point */
Xchar  *talloc(), *strperm();
Xextern int _doserrno;
Xextern int linenumber;   /* defined in mstring.c */
Xunsigned long getdatetime ();
X
X    static int dontworry=0;
X
Xunion REGS inregs, outregs;
Xstruct SREGS segregs;
Xstruct SREGS seg;
X
Xstruct {
X    char reserved[21];
X    char attr;
X    unsigned time, date, size_l, size_h;
X    char pname[13];
X} 
Xfind_buf;
X
X/*
X * MAKE parses the make file and constructs a somewhat tangled
X * directed graph describing dependencies.  There is a list of
X * TargNode structures, each of which points to a list of
X * prereq_node structures.  These both point to FileNode structures,
X * which contain information about files such as date-time of write
X * and the filename.  To keep things simple, MAKE insures that only
X * one FileNode exists for any given file.
X */
X
Xtypedef struct FileNode {
X#ifndef NDEBUG
X    int magic;
X#endif
X    char *fname;
X    struct FileNode *chain;
X    }    *fileptr;
Xfileptr  FileNodeList, NewFileNode ();
X
Xtypedef struct TargNode {
X#ifndef NDEBUG
X    int magic;
X#endif
X    struct TargNode *next;
X    fileptr file;
X    struct prereq_node *PreqList;
X    struct shell_node *shell_list;
X    }    *targptr;
Xtargptr target_list, new_target (), lookup_target ();
X
Xtypedef struct prereq_node {
X#ifndef NDEBUG
X    int magic;
X#endif
X    struct prereq_node *next;
X    fileptr file;
X    } *preqptr;
Xpreqptr NewPreqNode ();
X
Xtypedef struct shell_node {
X#ifndef NDEBUG
X    int magic;
X    struct shell_node *next;
X    char *command;
X    unsigned quiet : 1, ignore : 1, useshell : 1;
X    } *shellptr;
X
Xtypedef struct symbol_node {
X#ifndef NDEBUG
X    int magic;
X#endif
X    char *token, *value;
X    struct symbol_node *next;
X    }    *symbptr;
Xsymbptr SymbolList;
X
Xstatic char *makefilename;
Xstatic int status, tracing, quietly, ignore_errors;
X
Xusage ()
X{
X    puts (VERS211);
X    puts (VERSION);
X    puts ("This program may be copied freely for noncommercial purposes.  It may");
X    puts ("not be copied for commercial use without the author's written permission.\n");
X    puts ("This  program is an imitation of the MAKE program supplied with Unix.");
X    puts ("It works somewhat like Unix MAKE (and VAX/VMS MMS) except that it  has");
X    puts ("no default rules.\n");
X    puts ("Usage: make [target ...] [options ...]");
X    puts ("Options:");
X    puts ("        -f filename specify makefile, default is MAKEFILE");
X    puts ("        -i          ignore errors while processing");
X    puts ("        -n          trace and print, but don't execute commands");
X    puts ("        -s          suppress MAKE echoing commands");
X    exit (1);
X}
X
Xmain (argc, argv)
Xint argc;
Xchar **argv;
X{
X    int
X        argi,
X        targi,
X        linlen;
X
X    char  *targname[MAXTARGETS];
X
X    if (_osmajor < 2) error ("Must have DOS 2.XX or higher");
X    makefilename = "MAKEFILE";
X    tracing = quietly = ignore_errors = FALSE;
X    target_list = 0;
X    SymbolList = 0;
X    FileNodeList = 0;
X    targi = 0;
X    for (argi = 1; argi < argc; argi++) {
X        if (argv[argi][0] == '-')               /* switch */
X            switch (argv[argi][1]) {            /* switch character */
X            case 'f':
X                if (argi < (argc - 1))      makefilename = argv[++argi];
X                else                        usage ();
X                break;
X            case 'i':                ignore_errors = TRUE;             break;
X            case 'n':                tracing = TRUE;                   break;
X            case 's':                quietly = TRUE;                   break;
X            default:                 usage ();
X            }
X        else {                          /* target name */
X            if (targi == MAXTARGETS)    error ("Too many target files");
X            targname[targi] = strperm (argv[argi]);
X            targi++;
X        }
X    }
X    if (tracing && quietly) quietly = 0;
X
X    parse (makefilename);
X
X    if (target_list == NULL) error ("No targets in makefile");
X
X    if (targi)
X            for (argi = 0; argi < targi; argi++) make (targname[argi]);
X    else {
X            targptr p;
X            for (p=target_list; p->next ; p = p->next );
X            assert (p->magic == TARG_MAGIC);
X            make(p->file->fname);  /* make first target in makefile
X                                        i.e. the last one on the list */
X            }
X    return 0;           /* need good return code */
X}
X
Xparse (makefilename)
Xchar *makefilename;
X{
X    FILE  *fd;
X    int targi=0,   i;
X    char  c,  *sp,  *dp;
X    mstring input=NULL;
X    targptr targ[MAXTARGETS];
X
X    fd = fopen (makefilename, "r");
X    if (fd == NULL) error ("Can't open makefile");
X    while (1) {
X        tfree(input);
X        input = mfgets(fd);
X        if (input==NULL) break;
X#if DEBUG
X        printf ("Makefile Input: \"%s\"\n", input);
X#endif
X        sp = input;
X        passpace(&sp);
X        if (*sp==0 || *sp=='!' || *sp=='#') continue;
X           /* ignore comment lines and blank lines */
X
X        if (isspace(*input)) {   /* if leading space, then this is a shell line */
X                if (targi == 0) error ("Target line must come before shell lines");
X                sp = input; passpace(&sp);
X                for (i = 0; i < targi; i++)  NewShellLine (targ[i], sp);
X                continue;
X        }
X
X        {               /* substitute for symbols - this will be done later
X                           for shell lines, to take special symbols like $*
X                           into account, which can only be known at run time
X                           */
X              breakout_symbols(&input);
X        }
X
X        {       /*** check for the form 'name = value'   ***/
X                char *endword;
X                sp=input;
X                password(&sp);    endword = sp;
X                passpace(&sp);
X                if (*sp == '=') {
X                    targi=0;
X                    sp++;
X                    *endword = EOS;
X                    SetSymbol (input, sp) ;
X                    continue;
X                }
X        }                               /* end of macro parsing */
X
X
X        /**** now we know this is a 'targets : prerequisite' line ***/
X
X
X        targi=0;
X        for ( dp = sp = input; 1 ; sp++)        /*** collect the targets ***/
X            if (*sp == ':' || isspace (*sp)) { /* space or colon ends target name */
X                if (targi == MAXTARGETS) error ("Too many targets in one line");
X                c = *sp;  *sp = EOS;
X                targ[targi++] = new_target (dp);
X                *sp = c;
X                passpace(&sp);
X                if (*sp == ':') break;
X                dp = sp;
X            }
X            else if (*sp == EOS) error ("no colon");
X
X        sp++;
X        if (targi == 0) error ("No target file before ':' ");
X        while(1) {              /*** collect the prerequisites ***/
X            passpace(&sp);
X            if (*sp == EOS) break;            /* end of line */
X            dp = sp;                    /* beginning of prereq word */
X            passnonsp(&sp);
X            c = *sp;
X            *sp = EOS ;                /* end of prereq word */
X            for (i = 0; i < targi; i++) LinkPreq (targ[i], NewFileNode(dp) );
X            *sp = c;
X        }
X    }   /* end while */
X    tfree(input);
X    fclose (fd);
X    linenumber = 0;
X}
X
X/*
X * new_target
X *
X *      We think we have a new target;  this routine scans the
X *      target list and if we've already seen this name returns
X *      the node we already built.  Otherwise, a new node is
X *      allocated and linked.
X */
X
Xtargptr new_target (name)
Xchar *name;
X{
X    targptr targ ;
X
X#if DEBUG
X    printf ("new_target (\"%s\")\n", name);
X#endif
X    for ( targ = target_list; targ ; targ = targ->next ) {
X        assert (targ->magic == TARG_MAGIC);
X        if (strcmp (targ->file->fname, name) == 0)  return targ;
X    }
X    targ = (targptr) talloc (sizeof (struct TargNode));
X#ifndef NDEBUG
X    targ->magic = TARG_MAGIC;
X#endif
X    targ->file = NewFileNode (name);
X    targ->next = target_list;
X    targ->shell_list = NULL;
X    targ->PreqList = NULL;
X    target_list = targ;
X    return targ;
X}
X
XSetSymbol (name, value)
Xchar *name, *value;
X{
X    symbptr sym;
X    for (sym = SymbolList; sym; sym = sym->next)
X         if (0==strcmp(sym->token,name)) {
X                free(sym->value);
X                sym->value = strperm(value) ;
X                return;
X                }
X    sym = (symbptr) talloc (sizeof (struct symbol_node));
X#ifndef NDEBUG
X    sym->magic = SYMBOL_MAGIC;
X#endif
X    sym->token = strperm (name);
X    sym->value = strperm (value);
X    sym->next = SymbolList;
X    SymbolList = sym;
X}
X
X/*
X * NewShellLine
X *
X *      Add a shell command to the list of commands needed to update
X *      a target file
X */
X
XNewShellLine (targ, line)
Xtargptr targ;
Xchar *line;
X{
X    shellptr snode, new;
X
X#if DEBUG
X    printf ("NewShellLine (%lx, \"%s\")\n", targ, line);
X#endif
X    new = (shellptr) talloc (sizeof (struct shell_node));
X    new->next = 0;
X#ifndef NDEBUG
X    new->magic = SHELL_MAGIC;
X#endif
X    new->useshell = new->ignore = 0;
X    new -> quiet = quietly ;
X    for ( ; 1 ; line++, passpace(&line) )
X       if      (line[0] == '@')  new->quiet = 1;
X       else if (line[0] == '+')  new->useshell = 1;
X       else if (line[0] == '-')  new->ignore = 1;
X       else break;
X    new->command = strperm (line);
X    snode = targ->shell_list;
X    if (snode) {
X        assert (snode->magic == SHELL_MAGIC);
X        while (snode->next) {
X                snode = snode->next;
X                assert (snode->magic == SHELL_MAGIC);
X                }
X        snode->next = new;
X    }
X    else
X        targ->shell_list = new;
X}
X
X/*
X * LinkPreq
X *
X *      Link a new prerequisite file onto prereq list for a target.
X */
X
XLinkPreq (targ, fnode)
X   targptr targ;  fileptr fnode; {  preqptr p;
X
X#if DEBUG
X    printf ("LinkPreq (\"%s\")\n", fnode->fname );
X#endif
X
X    p = targ->PreqList;
X    ( targ->PreqList = NewPreqNode(fnode) ) -> next = p;
X    }
X
X/*
X * NewPreqNode
X *
X *      Allocate and return a new prerequisite node
X */
X
X        preqptr 
XNewPreqNode (fnode)
X        fileptr fnode;
X{
X    preqptr new;
X
X#if DEBUG
X    printf ("NewPreqNode (struct FileNode *%lx, \"%s\")\n",fnode,fnode->fname);
X#endif
X    new = (preqptr) talloc (sizeof (struct prereq_node));
X    new->next = NULL;
X#ifndef NDEBUG
X    new->magic = PREREQ_MAGIC;
X#endif
X    new->file = fnode;
X    return new;
X}
X
X/*
X * NewFileNode
X *
X *      Return FileNode pointer for a file;  returns pointer to
X *      existing FileNode if this file already seen, else allocates
X *      and inits a new node
X */
X
X        fileptr
XNewFileNode (name)
X        char *name;
X{
X    fileptr fnode;
X
X#if DEBUG
X    printf ("NewFileNode (\"%s\")\n", name);
X#endif
X    for ( fnode = FileNodeList; fnode; fnode = fnode->chain) {
X        assert (fnode->magic == FILE_MAGIC);
X        if (strcmp (name, fnode->fname) == 0) {
X#if DEBUG
X            printf ("NewFileNode returning existing node\n");
X#endif
X            return fnode;
X        }
X        
X    }
X    fnode = (fileptr) talloc (sizeof (struct FileNode));
X    fnode->fname = strperm (name);
X    fnode->chain = 0;
X#ifndef NDEBUG
X    fnode->magic = FILE_MAGIC;
X#endif
X    fnode -> chain = FileNodeList;
X    FileNodeList = fnode;
X    return fnode;
X}
X
X/*
X * getdatetime
X *
X *      Return date-time of a file squished into a unsigned long so compares
X *      are easy
X */
X
X        unsigned long 
Xgetdatetime (name)
X        char *name;
X{
X    unsigned long datetime;
X    int dma_off, dma_seg;
X
X#ifdef DEBUG
X    printf("getdatetime(\"%s\")\n",name);
X#endif
X
X    inregs.x.ax = 0x2F00;            /* get current DMA address */
X    intdosx (&inregs, &outregs, &segregs);   /*  .. */
X    dma_off = outregs.x.bx;           /* and save for later restoration */
X    dma_seg = segregs.es;
X    {  char *p = (char *) & find_buf;
X       segregs.ds = FP_SEG(p);
X       inregs.x.dx = FP_OFF(p);  /* set DMA to GNJFN block */
X    }
X    inregs.x.ax = 0x1A00;
X    intdosx (&inregs, &outregs, &segregs);
X
X    segregs.ds = FP_SEG(name);
X    inregs.x.dx = FP_OFF(name);       /* pathname */
X    inregs.x.cx = 0;                 /* attributes */
X    inregs.x.ax = 0x4E00;            /* GTJFN */
X    outregs.x.cflag = 0;
X    status = intdosx (&inregs, &outregs, &segregs);
X    if (outregs.x.cflag || (status & 1)) {
X#if DEBUG
X        printf ("File \"%s\" does not exist\n", name);
X#endif
X        return 0;
X    }
X
X    segregs.ds = dma_seg;           /* restore DMA address */
X    inregs.x.dx = dma_off;
X    inregs.x.ax = 0x1A00;
X    intdosx (&inregs, &outregs, &segregs);
X
X#if DEBUG
X    printf ("filespec = \"%s\", date = %4x, time = %4x, sizel = %d\n",
X        find_buf.pname, find_buf.date, find_buf.time, find_buf.size_l);
X#endif
X    datetime = (unsigned long) find_buf.date;
X    datetime = datetime << 16;
X    datetime = datetime + find_buf.time;
X    return datetime;
X}
X
X/*
X * make (name)
X *
X *      This routine actually does the work.  It scans the list of
X *      targets parsed from the makefile, and checks the target's
X *      prerequisites date/time values against the target's.  If
X *      the prerequisite is itself a target (present in target_list),
X *      we call make recursively to check it.  Then, if any of our
X *      prerequisites are newer than we are, we execute all our shell
X *      commands.  If there are no prerequisites specified at all, then
X *      also execute all our shell commands.
X */
X
X        int
Xmake (targname)         /* use fnode instead of fname here */
X    char *targname;
X{          
X    targptr targ;
X    preqptr prereq;
X    unsigned long NewestPreq=0;
X
X#if DEBUG
X    printf ("Making %s\n", targname);
X#endif
X
X    if ((targ = lookup_target (targname)) == 0)
X       return TryDefault( targname );
X    prereq = targ->PreqList; 
X    if (prereq)
X        {
X        for ( ; prereq; prereq = prereq->next) {
X            unsigned long date;
X            make (prereq->file->fname);             /* recursively make */
X            date =  getdatetime(prereq->file->fname);
X            if (date > NewestPreq) NewestPreq = date;
X        }
X#if DEBUG
X        printf ("Target \"%s\" datetime is %08lx, newest prereq is %08lx\n",
X                targ->file->fname, getdatetime(targ->file->fname), NewestPreq);
X#endif
X        if (getdatetime(targ->file->fname) < NewestPreq) build (targ);
X    }
X    else build(targ);   /* if no prerequisites were listed, do it ! */
X
X    if (targ->shell_list == NULL) {
X        int i;
X        dontworry ++;
X        i = TryDefault( targname );
X        dontworry --;
X        return i;
X        }
X    return 1;
X}
X
X
XTryDefault(targname)
X        char *targname;{
X        targptr targ;
X        char * ext = strchr (targname, '.');
X        dontworry ++;
X        if (ext != NULL)
X            for (targ = target_list ; targ ; targ = targ -> next )
X                if (targ->file->fname[0] == '*' &&
X                    0 == strcmp ( ext , targ->file->fname+1 ) ) {
X                        char * root = msubstr( targname , 0 , ext-targname );
X                        char *cname;
X                        int worked;
X                        cname = mstrcat( root ,targ->PreqList->file->fname+1 );
X                        worked = make ( cname ) ;
X                        SetSymbol ( "*" , root ) ;
X                        free(cname);
X                        free(root);
X                        if (!worked) goto ret0;
X                        if (getdatetime(cname) <= getdatetime(targname)) goto ret1;
X                        build ( targ ) ;
X                        goto ret1;
X                        }
X
X        if (getdatetime(targname) > 0) goto ret1;
X
X        ret0:                   /* unsuccessful return */
X            if (--dontworry) return 0;
X            else error ("Don't know how to make %s",targname);
X
X        ret1:                   /* successful return */
X            dontworry--;
X            return 1;
X        }
X
X
X
X
X/*
X * build
X *
X *      Invoke shell commands to build a target file
X */
X
Xbuild (targ)
X    targptr targ;
X    {
X    shellptr snode;
X    char *cmd;
X    int  runsts = 0;
X
X#if DEBUG
X    printf ("Building \"%s\"\n", targ->file->fname);
X#endif
X    for ( snode = targ->shell_list; snode; snode = snode->next, free(cmd) ) {
X        char *p, **q, *cmdname;
X
X        assert (snode->magic == SHELL_MAGIC);
X        cmd = strperm(snode->command);
X        breakout_symbols(&cmd);           /* notice that this may introduce a space at the beginning of the command line */
X        cmdname = cmd; passpace(&cmdname);
X
X        if (!snode->quiet)  fputs (cmdname, stdout);
X        if (tracing) {
X               puts ("");   /* EXEC does newline, so must be faked */
X               continue;
X               }
X
X        p = cmdname ; passnonsp(&p);
X        if (*p)  *p++ = EOS ;       /* terminate the name of the cmd */
X
X                                  /* find whether it is a dos command */
X        strlwr(cmdname);          /* lower  case for comparison */
X        for (q=dos_commands ; *q ; q++) if (0==strcmp(*q,cmdname)) break;
X
X        if (*q || snode->useshell)        /* must we use command.com ? */
X                if (0==strcmp(cmdname,"chdir") || 0==strcmp(cmdname,"cd"))
X                        if (passpace (&p) , *p) {   /* chdir with arg */
X                            char *q=p;
X                            passnonsp(&q);  *q = EOS;
X                            runsts = chdir(p);
X                            }
X                        else {                      /* chdir without arg */
X                            char name[200];
X                            if (getcwd(name,200)) {
X                                if (!snode->quiet) putchar('\n');
X                                fputs(name,stdout);
X                                }
X                            else error("path name too long");
X                             }
X                else    {                           /* resident command */
X                        if (*p) *--p = ' ';         /* splice command line */
X                        if (strlen(cmdname) > 128) error("shell command line too long");
X                        runsts = system(cmdname);
X                        }
X        else    {                                   /* transient command */
X                if (strlen(p)+1 > 128) error("shell command line too long");
X                if (!snode->quiet) putchar ('\n');
X                runsts = spawnlp (P_WAIT, cmdname, "", p, NULL);
X                /* can't use 'system()' here, because command.com does not
X                   return the exit code of the program */
X                }
X        putchar('\n');      /* some programs do not end with return */
X        if (runsts == -1) perror("program not found : "), exit(1);
X        if (runsts > 0 && !snode->ignore && !ignore_errors)
X              printf ( " --- return code %d ---\7", runsts),
X              exit(runsts);
X                     
X    }
X}
X
X        targptr 
Xlookup_target (name)
Xchar *name;
X{
X    targptr targ;
X    for ( targ = target_list; targ ; targ = targ->next)
X        if (strcmp (name, targ->file->fname) == 0) break;
X    return targ;
X}
X
Xbreakout_symbols (cmdlinptr)
Xchar **cmdlinptr; {
X    char *cmdlin = *cmdlinptr, *cmd = talloc(LINESIZE+100);
X    symbptr sym;
X    char   symcmp[SYMLEN];
X    int i, paren, cmdptr;
X
X#if DEBUG
X    printf("breakout_symbols (\"%s\")\n", cmdlin);
X#endif
X    /* this routine doesn't check for overflow of the line ! */
X
X    strcpy ( cmd, "");
X    cmdptr = 0;
X    while (1) {
X        while (*cmdlin != '$' && *cmdlin != EOS) {
X                if (cmdptr >= LINESIZE) error ("Line too long after symbol substitution");
X                cmd[cmdptr++] = *cmdlin++;
X                }
X        if (cmdptr >= LINESIZE) error ("Line too long after symbol substitution");
X        cmd[cmdptr] = EOS;
X        if (0==*cmdlin) break;            /* end of line */
X        cmdlin++;               /* pass the $ */
X        /* now we know we are looking at a symbol */
X        if (*cmdlin == '(') paren = 1, cmdlin++; else paren=0;
X        for (i = 0; i < SYMLEN-1 && (*cmdlin == '*' || isalnum (*cmdlin)); )
X            symcmp[i++] = *cmdlin++;
X        symcmp[i] = EOS;
X        if (paren)
X           if (*cmdlin == ')') cmdlin++;
X           else puts ("No closing paren on shell line macro");
X        for ( sym = SymbolList; 1 ; sym = sym->next) {
X            if (sym==NULL)  error ("Undefined symbol %s", symcmp );
X            assert (sym->magic == SYMBOL_MAGIC);
X            if (strcmp (sym->token, symcmp) == 0) break;
X        }
X        strcpy ( cmd + cmdptr , sym->value );
X        cmdptr = strlen ( cmd ) ;
X    }
X
X    free(*cmdlinptr);
X    *cmdlinptr = strperm(cmd);
X    free(cmd);
X
X#if DEBUG
X    printf ("breakout_symbols returning (\"%s\")\n", cmd);
X#endif
X}
X
X
SHAR_EOF
echo 'Orignal Sum -> 04085    27'
echo -n 'Current Sum -> '
sum make.c
echo shar: extracting makefile
sed 's/^X//' << 'SHAR_EOF' > makefile
X        ### this is the makefile for make.exe itself
X
XBIN=pd
X
Xexe : make.exe
Xall : lib install
Xinstall : \$BIN\make.exe
Xlib : make.lbr
Xclean : 
X        del make.lbr
X        del *.?Q?
Xtest : mstring.obj
X        msc /DDEBUG /AL make.c ;
X        link make+mstring/STACK:9000;
X        ren make.exe nmake.exe
X        mv nmake.exe \$BIN
X
X     ###      lines below this one are not for the user                 ###
X
X*.obj : *.c
X        msc /AL /Ota $*.c;
X\$BIN\make.exe : make.exe
X        copy make.exe \$BIN
Xmake.lbr : make.dqc make.eqe make.cq makefile makelu.dat mstring.c mstring.h
X        @ echo Making library
X        @ if exist make.lbr del make.lbr
X        @ + lu -u make make.dqc make.eqe make.cq makefile mstring.c mstring.h makelu.dat < makelu.dat
X        @ echo Library make.lbr is ready.
Xmake.exe : make.obj  mstring.obj
X        link make+mstring/STACK:9000;
Xmstring.obj : mstring.c mstring.h
Xmake.dqc : make.doc
X        sq make.doc
Xmake.cq : make.c
X        sq make.c
Xmake.eqe : make.exe
X        - exepack make.exe mak.exe
X        del make.exe
X        ren mak.exe make.exe
X        sq make.exe
SHAR_EOF
echo 'Orignal Sum -> 55236     2'
echo -n 'Current Sum -> '
sum makefile
echo shar: extracting makelu.dat
sed 's/^X//' << 'SHAR_EOF' > makelu.dat
X9
X
SHAR_EOF
echo 'Orignal Sum -> 16413     1'
echo -n 'Current Sum -> '
sum makelu.dat
echo shar: extracting mstring.h
sed 's/^X//' << 'SHAR_EOF' > mstring.h
X/* mstring.h */
X
Xtypedef char * mstring;
Xmstring mfgets(), mstrcat(), msubstr(), talloc();
SHAR_EOF
echo 'Orignal Sum -> 06843     1'
echo -n 'Current Sum -> '
sum mstring.h
echo shar: extracting mstring.c
sed 's/^X//' << 'SHAR_EOF' > mstring.c
X/* mstring.c */
X
X/* The purpose of this file is to provide subroutines for handling
X   strings whose space is allocated with malloc - in this way we remove
X   all limitations on length of strings */
X
X#include "mstring.h"
X#include <ctype.h>
X#include <malloc.h>
X#include <stdio.h>
X
X#define DLEN 80
X
Xint linenumber = 0;
X
X        char
Xlastchar(p)
X        char *p;{
X        char c=0;
X        while (*p) c = *p++;
X        return c;
X        }
X
X        char *
Xendptr(p)
X        char *p;{
X        while (*p) p++;
X        return p;
X        }
X
X        mstring
Xmfgets (stream)
X        FILE *stream;{
X        mstring p; int plen;
X        if (feof(stream)) return NULL;
X        p = talloc(plen = DLEN);
X        p[0] = '\0';
X        while (1) {
X                if (strlen(p) + DLEN > plen) {
X                        p = realloc(p, plen += DLEN);
X                        if (p==NULL) puts("no more memory (mfgets)"), exit(1);
X                        }
X                if (NULL == fgets(endptr(p),DLEN,stream))
X                        if (*p) return p;
X                        else {
X                                free(p);
X                                return NULL;
X                                }
X                if (lastchar(p) != '\n') continue;
X                linenumber++;
X                endptr(p)[-1] = 0;
X                if (lastchar(p) != '\\') {
X                        p = realloc (p,strlen(p)+1) ;
X                        if (p==NULL) puts("no more memory (mfgets)"), exit(1);
X                        return p;
X                        }
X                endptr(p)[-1] = 0;
X                }
X        }
X
X        mstring
Xmsubstr(p,i,l)  /* creates a string from p[i],p[i+1],...,p[i+l-1] */
X        mstring p;{
X        mstring q;
X        q = talloc(l+1);
X        strncpy(q,p+i,l);
X        q[l] = '\0';
X        return q;
X        }
X
X        mstring
Xmstrcat(p,q)
X        mstring p,q;{
X        mstring r = talloc (strlen(p) + strlen(q) + 1);
X        strcpy(r,p);
X        strcat(r,q);
X        return r;
X        }
X
X        mstring
Xstrperm(s)
X        char *s;{       /* allocate space for s, return new pointer */
X        char *t = talloc(strlen(s)+1);
X        strcpy(t,s);
X        return t;
X        }
X
Xpasspace(p)
X        char **p;{
X        while (isspace (**p)) (*p)++;
X        }
X
Xpassnonsp(p)
X        char **p;{
X        while (**p && !isspace(**p)) (*p)++;
X        }
X
Xpassword(p)
X        char **p;{
X        while (isalnum(**p)) (*p)++;
X        }
X
Xerror (errmsg,a,b,c,d,e,f,g,h)
Xchar *errmsg;long a,b,c,d,e,f,g,h;
X{
X    /* unfortunately, this assumes only one file is being used */
X    if (linenumber) fprintf(stderr,"at line %d : ",linenumber);
X    fprintf(stderr,errmsg,a,b,c,d,e,f,g,h);
X    exit (1);
X}
X
X        mstring
Xtalloc(i)
X        int i;{
X        char *p;
X        char *malloc();
X        p = malloc(i);
X        if (p==NULL) error ("no more memory");
X        return p;
X        }
X
SHAR_EOF
echo 'Orignal Sum -> 15744     3'
echo -n 'Current Sum -> '
sum mstring.c
exit 0