<< Previous Message Main Index Next Message >>
<< Previous Message in Thread This Month Next Message in Thread >>
Date   : Thu, 31 Jul 1986 07:23:23 GMT
From   : Michael Kersenbrock <michaelk%copper.uucp@BRL.ARPA>
Subject: MAKE for CPM 3.0 part 1 of 2

<-------------->
This is a make (clone) program for cp/m 3.0. It is a better version
than the one I posted earlier.  This make program was recently posted
on USENET for "EON" (??).  I have "ported" this program to CP/M,
and it seems to work well.  This one has macros and rules (which
the earlier one I posted did not).

Below is the source and man files. Part two (posted separately) contains
the uuencoded version of the make.com (z80) binary.  This binary
was generated via compilation with Manx Aztec C II version 1.05g.
I have "enhanced" my compiler library continuously over the last several
years, and don't always remember what was originally there and what it
is that which I added myself.  I hope porting my port doesn't cause too many
problems for those that may want to recompile the source.

Enjoy...

Mike Kersenbrock
...!tektronix!copper!michaelk


#      This is a shell archive.
#      Remove everything above and including the cut line.
#      Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:    Shell Archiver
#      Run the following text with /bin/sh to create:
#      MAKE.DOC
#      MAKEFILE
#      MAKE.C
#      CHECK.C
#      INPUT.C
#      MACRO.C
#      MAIN.C
#      READER.C
#      RULES.C
#      H.H
# This archive created: Wed Jul 30 23:44:42 1986
# By:  Michael D. Kersenbrock (Oregon HomePower Software)
echo shar: extracting make.doc
cat << \SHAR_EOF > make.doc


  MAKE(CPM+)                                                  MAKE(CPM+)

       
       SYNTAX

               make [-f makefile] [-nprst] [macro=val ...] [target(s) ...]

       WHERE

               -i means don't continue if an error is encountered

               -f specifies that the following argument is the name of
                  a makefile to be used instead of default "MAKEFILE.DAT"
                  or "MAKEFILE".

               -n means don't execute the commands, just write the ones that
                  would be executed to the standard output. 

               -p Print all macros & targets

               -r Do not use inbuilt rules

               -s Make silently

               -t Touch files instead of making them

       "macro" -  is a macro name, and...

         "val" -  is the string value to set it to.  Use quotes around
                  the "macro=val" if there are imbedded spaces.


       DESCRIPTION

               This program is a slightly simplified clone of the UNIX
               (tm of AT&T) utility of the same name.  Dependancy
               information is extracted from a makefile and acted upon.

               This version requires CP/M-80 version 3.0 ("CP/M PLUS")
               with time-of-day-clock implemented.  Also the file timestamps
               must be "turned on". Current time-of-day call to CP/M is
               also used.  Note that "CP/M" is a trademark of Digital
               Research.



       MAKEFILE FORMAT

               For general usage, refer to a UNIX(tm) man-page.

               "Make" without parameters makes the first target in the
               makefile.

               The default name of the 'makefile' is 'MAKEFILE.DAT'
               or 'MAKEFILE' in that order.  If the '-f' option is used,
               the default makefile is not processed.
       
               Any blank lines in the 'makefile(s)' are ignored, as are
               lines that have a "#" in the first column.




  MAKE(CPM+)                   -2-                        MAKE(CPM+)

       
       
               Lines starting with a tab character are 'howto'
               lines, and consist of a command name followed by arguments.

               Any other line is a 'dependency' line.  'Dependency'
               lines consist of a filename followed by a (possibly empty)
               list of dependent filenames.  A colon is required after
               the target filename.
       
               'Howto' lines apply to the most recently preceding 
               'dependency' line.  It is improper for  a 'howto' line
                to precede the first 'dependency' line.

               Lines ending with "\" join the next-line to the current
               one with all but one-character of the next-line's leading
               whitespace removed.


       SIDE EFFECTS

               This program works by producing a temporary file "MAKE@@@.SUB"
               then chaining to it for execution of the command list.  This
               file then deletes itself upon successful completion.  Should
               the execution be aborted, then this file will remain
               in the file system (but will not harm later invocations of
               make).

               Everything has been made case-independent (CP/M forces command
               lines upper case.  That makes this case-independence a
               requirement.)

        BUGS
               The -i option is opposite of "normal" so that error codes
               are normally ignored.  If the -i option is used, colons
               will proceed commands that aren't to execute after an
               error.  This sortof almost works in CP/M Plus.  To be
               made to work three things need to be done. 1) an RSX
               written that makes a compiler(etc) set the error flag (should
               be easy to do). 2) Keep CP/M 3.0 and/or the CCP from resetting
               the error flag on each command that is executed (patch
               somewhere?), and 3) Let the ":" exclusion work in front
               of .SUB files as well as .COM files (patch somewhere?).
               Discription of the ":" is in the CP/M Plus Programmer's
               Guide in the description of bdos function 108.





  MAKE(CPM+)                   -3-                         MAKE(CPM+)

       
       
       RULES
               The built in rules currently are these:
               
               ".o" files depend on ".c" files and are made by:
                       $(CCC) $(CFLAGS) $(CWHICH)
                       
                       where:
                               CCC    = cc-c   (mdk's submit file)
                               CFLAGS =        (they are in cc-c.sub)
                               CWHICH = $*     (target's basename)


               ".rel" files depend on ".asm" files and are made by:
                       $(ASM) $(ASMWHICH) $(ASMFLAGS)

                       where:
                               ASM      = rmac
                               ASMWHICH = $*
                               ASMFLAGS = $$PZ SZ

               Note: These macros can be redefined in the makefile.



       AUTHOR

               Originally written for "EON" (whatever that is).
               This program was originally posted by, and presumably
               written by:

                  Neil Russell
                  Organization: TIME. Office Computers, Sydney, Australia
                  UUCP:  ...!seismo!munnari!tictoc.oz!caret
               
               The port to CP/M 3.0 consists of numerous small and medium
               modifications seemingly everywhere. That plus the generation
               of this psuedo-man page was done by:

                  Michael D. Kersenbrock
                  Aloha, Oregon
                  UUCP: ...!tektronix!copper!michaelk


SHAR_EOF
if test 4453 -ne "`wc -c make.doc`"
then
echo shar: error transmitting make.doc '(should have been 4453 characters)'
fi
echo shar: extracting makefile
cat << \SHAR_EOF > makefile
#
# This is for "make.com" itself (under CP/M-80 V3.0)
#

OBJS   =       make.o check.o input.o macro.o main.o reader.o rules.o

make.com:      $(OBJS)
               linkc $(OBJS)

$(OBJS):       h.h
SHAR_EOF
if test 169 -ne "`wc -c makefile`"
then
echo shar: error transmitting makefile '(should have been 169 characters)'
fi
echo shar: extracting make.c
cat << \SHAR_EOF > make.c
/*
 *     Do the actual making for make
 */

#include "c:stdio.h"         /* "c:" is my ramdisk */
#include "c:fcntl.h"
#include "h.h"

FILE *execfile;                /* Submit file to execute the "shell-exec's" */

extern long ftime();

/*
 *     Exec a shell that returns exit status correctly (/bin/esh).
 *     The standard EON shell returns the process number of the last
 *     async command, used by the debugger (ugg).
 *     [exec on eon is like a fork+exec on unix]
 */
int
dosh(command, args)
char *command;
char *args;
{
       if (!execfile) {
               execfile = fopen(MAKERUN, "w");
               if (!execfile) {
                       errout("Make: can't create ");
                       errout(MAKERUN);
                       errout("\r\n");
                       exit(-1);
               }
       }

       fprintf(execfile, "%s %s", command, args);
       return 0;
}


/*
 *     Do commands to make a target
 */
void
docmds(np)
struct name *          np;
{
       bool                    ssilent;
       bool                    signore;
       int                     estat;
       register char *         q;
       register char *         p;
       char *                  shell;
       register struct line *  lp;
       register struct cmd *   cp;
       ssilent = silent;

       /*
        * Under cp/m the "ignore or not to ignore" is sorta done in dosh()
        * where colons are inserted or not into the cp/m submit file.
        * The actual ignoring (or not) is done when cpm 3.0's submit
        * program executes the submit file.
        *
        *                                      -mdk
        */

       signore = ignore;

       if (*(shell = getmacro("SHELL")) == '\0')
               shell = ":bin/esh";

       for (lp = np->n_line; lp; lp = lp->l_next)
               for (cp = lp->l_cmd; cp; cp = cp->c_next)
               {
                       strcpy(str1, cp->c_cmd);
                       expand(str1);
                       q = str1;
                       while ((*q == '@') || (*q == '-'))
                       {
                               if (*q == '@')     /*  Specific silent  */
                                       ssilent = TRUE;
                               else               /*  Specific ignore  */
                                       signore = TRUE;
                               q++;               /*  Not part of the command  */
                       }

                       if (!ssilent)
                               fputs("   ", stdout);

                       if ((!ssilent) && (!signore)) { /* cp/m stuff */
                               putchar(':');
                               putchar(' ');
                       }
                       for (p=q; *p; p++)
                       {
                               if (*p == '\n' && p[1] != '\0')
                               {
                                       *p = ' ';
                                       if (!ssilent)
                                               fputs("\\\n", stdout);
                               }
                               else if (!ssilent)
                                       putchar(*p);
                       }
                       if (!ssilent)
                               putchar('\n');

                       if (domake)
                       {                       /*  Get the shell to execute it  */
                               /*
                                * Colon in front of line makes execution
                                * conditional on error code in cp/m 3.0 .
                                * (Sortof, with one "minor" problem)
                                */

                               if (!signore)   
                                       dosh(":","");
                               
                               if ((estat = dosh(q,"\n")) != 0)
                               {
                                       if (estat == -1)
                                               fatal("Couldn't execute %s", shell);
                                       else
                                       {
                                               printf("%s: Error code %d", myname, estat);
                                               if (signore)
                                                       fputs(" (Ignored)\n", stdout);
                                               else
                                               {
                                                       putchar('\n');
                                                       if (!(np->n_flag & N_PREC))
                                                               if (unlink(np->n_name) == 0)
                                                                       printf("%s: '%s' removed.\n", myname, np->n_name);
                                                       exit(estat);
                                               }
                                       }
                               }
                       }
               }
}



/*
 *     Update the mod time of a file to now.
 */
void
touch(np)
struct name *          np;
{
       char                    c;
       int                     fd;
       char fcb[36];                   /* set area for CP/M fcb */
       char tempbuffer[128];           /* CPM record size */


       if (!domake || !silent)
               printf("    touch %s\n", np->n_name);

       if (domake)  {
               if ((fd=open(np->n_name,O_RDONLY)) < 0 ) {
                       printf("%s: '%s' not touched - non-existant\n",
                                       myname, np->n_name);
               }
               else {
                       close(fd);
                       OpenRandomFile(np->n_name,fcb);   /* open file      */
                       ReadRandomFile(tempbuffer,0,fcb); /* read record    */
                       WriteRandomFile(tempbuffer,0,fcb); /* write it back */
               }
               CloseRandomFile(fcb);
       }
}


/*
 *     Recursive routine to make a target.
 */
int
make(np, level)
struct name *          np;
int                    level;
{
       register struct depend *        dp;
       register struct line *          lp;
       time_t                          dtime = 1l;


       if (np->n_flag & N_DONE)
               return 0;

       if (!np->n_time) {
               np->n_time = ftime(np->n_name);/*  Gets modtime of this file*/
       }

       if (rules) {
               for (lp = np->n_line; lp; lp = lp->l_next)
                       if (lp->l_cmd)
                               break;
               if (!lp)
                       dyndep(np);
       }

       if (!(np->n_flag & N_TARG) && np->n_time == 0L)
               fatal("Don't know how to make %s", np->n_name);

       for (lp = np->n_line; lp; lp = lp->l_next)
               for (dp = lp->l_dep; dp; dp = dp->d_next)
               {
                       make(dp->d_name, level+1);
                       dtime = max(dtime, dp->d_name->n_time);
               }

       np->n_flag |= N_DONE;

       if (quest)
       {
               time(&np->n_time);       /* used to be rtime() zzz */
               return np->n_time < dtime;
       }
       else if (np->n_time < dtime)
       {
               if (dotouch)
                       touch(np);
               else
               {
                       setmacro("@", np->n_name);
                       docmds(np);
               }
               time(&np->n_time);       /* used to be rtime() zzz */
       }
       else if (level == 0)
               printf("%s: '%s' is up to date\n", myname, np->n_name);
       return 0;
}
SHAR_EOF
if test 4491 -ne "`wc -c make.c`"
then
echo shar: error transmitting make.c '(should have been 4491 characters)'
fi
echo shar: extracting check.c
cat << \SHAR_EOF > check.c
/*
 *     Check structures for make.
 */

#include "c:stdio.h"
#include "h.h"


/*
 *     Prints out the structures as defined in memory.  Good for check
 *     that you make file does what you want (and for debugging make).
 */
void
prt()
{
       register struct name *          np;
       register struct depend *        dp;
       register struct line *          lp;
       register struct cmd *           cp;
       register struct macro *         mp;


       for (mp = macrohead; mp; mp = mp->m_next)
               fprintf(stderr, "%s = %s\n", mp->m_name, mp->m_val);

       fputc('\n', stderr);

       for (np = namehead.n_next; np; np = np->n_next)
       {
               fprintf(stderr, "%s:\n", np->n_name);
               for (lp = np->n_line; lp; lp = lp->l_next)
               {
                       fputc(':', stderr);
                       for (dp = lp->l_dep; dp; dp = dp->d_next)
                               fprintf(stderr, " %s", dp->d_name->n_name);
                       fputc('\n', stderr);

                       for (cp = lp->l_cmd; cp; cp = cp->c_next)
                               fprintf(stderr, "-\t%s\n", cp->c_cmd);
                       fputc('\n', stderr);
               }
               fputc('\n', stderr);
       }
}


/*
 *     Recursive routine that does the actual checking.
 */
void
check(np)
struct name *          np;
{
       register struct depend *        dp;
       register struct line *          lp;


       if (np->n_flag & N_MARK)
               fatal("Circular dependency from %s", np->n_name);

       np->n_flag |= N_MARK;

       for (lp = np->n_line; lp; lp = lp->l_next)
               for (dp = lp->l_dep; dp; dp = dp->d_next)
                       check(dp->d_name);

       np->n_flag &= ~N_MARK;
}


/*
 *     Look for circular dependancies.
 *     ie.
 *             a: b
 *             b: a
 *     is a circular dep
 */
void
circh()
{
       register struct name *  np;


       for (np = namehead.n_next; np; np = np->n_next)
               check(np);
}


/*
 *     Check the target .PRECIOUS, and mark its dependentd as precious
 */
void
precious()
{
       register struct depend *        dp;
       register struct line *          lp;
       register struct name *          np;


       if (!((np = newname(".PRECIOUS"))->n_flag & N_TARG))
               return;

       for (lp = np->n_line; lp; lp = lp->l_next)
               for (dp = lp->l_dep; dp; dp = dp->d_next)
                       dp->d_name->n_flag |= N_PREC;
}
SHAR_EOF
if test 1895 -ne "`wc -c check.c`"
then
echo shar: error transmitting check.c '(should have been 1895 characters)'
fi
echo shar: extracting input.c
cat << \SHAR_EOF > input.c
/*
 *     Parse a makefile
 */


#include "c:stdio.h"
#include "h.h"

extern int endoffile;

struct name            namehead;
struct name *          firstname;

char                   str1[LZ];               /*  General store  */
char                   str2[LZ];


/*
 *     Intern a name.  Return a pointer to the name struct
 */
struct name *
newname(name)
char *                 name;
{
       register struct name *  rp;
       register struct name *  rrp;
       register char *         cp;


       for
       (
               rp = namehead.n_next, rrp = &namehead;
               rp;
               rp = rp->n_next, rrp = rrp->n_next
       )
               if (no_case_cmp(name, rp->n_name) == 0)
                       return rp;

       if ((rp = (struct name *)malloc(sizeof (struct name)))
                               == (struct name *)0)
               fatal("No memory for name");
       rrp->n_next = rp;
       rp->n_next = (struct name *)0;
       if ((cp = malloc(strlen(name)+1)) == (char *)0)
               fatal("No memory for name");
       strcpy(cp, name);
       rp->n_name = cp;
       rp->n_line = (struct line *)0;
       rp->n_time = (time_t)0;
       rp->n_flag = 0;

       return rp;
}


/*
 *     Add a dependant to the end of the supplied list of dependants.
 *     Return the new head pointer for that list.
 */
struct depend *
newdep(np, dp)
struct name *          np;
struct depend *                dp;
{
       register struct depend *        rp;
       register struct depend *        rrp;


       if ((rp = (struct depend *)malloc(sizeof (struct depend)))
                               == (struct depend *)0)
               fatal("No memory for dependant");
       rp->d_next = (struct depend *)0;
       rp->d_name = np;

       if (dp == (struct depend *)0)
               return rp;

       for (rrp = dp; rrp->d_next; rrp = rrp->d_next)
               ;

       rrp->d_next = rp;

       return dp;
}


/*
 *     Add a command to the end of the supplied list of commands.
 *     Return the new head pointer for that list.
 */
struct cmd *
newcmd(str, cp)
char *                 str;
struct cmd *           cp;
{
       register struct cmd *   rp;
       register struct cmd *   rrp;
       register char *         rcp;


       if (rcp = rindex(str, '\n'))
               *rcp = '\0';            /*  Loose newline  */

       while (isspace(*str))
               str++;

       if (*str == '\0')               /*  If nothing left, the exit  */
               return;

       if ((rp = (struct cmd *)malloc(sizeof (struct cmd)))
                               == (struct cmd *)0)
               fatal("No memory for command");
       rp->c_next = (struct cmd *)0;
       if ((rcp = malloc(strlen(str)+1)) == (char *)0)
               fatal("No memory for command");
       strcpy(rcp, str);
       rp->c_cmd = rcp;

       if (cp == (struct cmd *)0)
               return rp;

       for (rrp = cp; rrp->c_next; rrp = rrp->c_next)
               ;

       rrp->c_next = rp;

       return cp;
}


/*
 *     Add a new 'line' of stuff to a target.  This check to see
 *     if commands already exist for the target.
 */
void
newline(np, dp, cp)
struct name *          np;
struct depend *                dp;
struct cmd *           cp;
{
       bool                    hascmds = FALSE;  /*  Target has commands  */
       register struct line *  rp;
       register struct line *  rrp;


       for
       (
               rp = np->n_line, rrp = (struct line *)0;
               rp;
               rrp = rp, rp = rp->l_next
       )
               if (rp->l_cmd)
                       hascmds = TRUE;

       if (hascmds && cp)
               error("Commands defined twice for target %s", np->n_name);

       if ((rp = (struct line *)malloc(sizeof (struct line)))
                               == (struct line *)0)
               fatal("No memory for line");
       rp->l_next = (struct line *)0;
       rp->l_dep = dp;
       rp->l_cmd = cp;

       if (rrp)
               rrp->l_next = rp;
       else
               np->n_line = rp;

       np->n_flag |= N_TARG;
}


/*
 *     Parse input from the makefile, and construct a tree structure
 *     of it.
 */
void
input(fd)
FILE *                 fd;
{
       char *                  p;              /*  General  */
       char *                  q;
       struct name *           np;
       struct depend *         dp;
       struct cmd *            cp;


       if (getline(str1, fd))  /*  Read the first line  */
               return;

       for(;;)
       {
               if (*str1 == '\t')      /*  Rules without targets  */
                       error("Rules not allowed here");

               p = str1;

               while (isspace(*p))     /*  Find first target  */
                       p++;

               while (((q = index(p, '=')) != (char *)0) &&
                   (p != q) && (q[-1] == '\\'))        /*  Find value */
               {
                       register char *         a;

                       a = q - 1;      /*  Del \ chr; move rest back  */
                       p = q;
                       while(*a++ = *q++)
                               ;
               }

               if (q != (char *)0)
               {
                       register char *         a;

                       *q++ = '\0';            /*  Separate name and val  */
                       while (isspace(*q))
                               q++;
                       if (p = rindex(q, '\n'))
                               *p = '\0';

                       p = str1;
                       if ((a = gettok(&p)) == (char *)0)
                               error("No macro name");

                       setmacro(a, q);

                       if (getline(str1, fd))
                               return;
                       continue;
               }

               expand(str1);
               p = str1;

               while (((q = index(p, ':')) != (char *)0) &&
                   (p != q) && (q[-1] == '\\'))        /*  Find dependents  */
               {
                       register char *         a;

                       a = q - 1;      /*  Del \ chr; move rest back  */
                       p = q;
                       while(*a++ = *q++)
                               ;
               }

               if (q == (char *)0)
                       error("No targets provided");

               *q++ = '\0';    /*  Separate targets and dependents  */

               for (dp = (struct depend *)0; ((p = gettok(&q)) != (char *)0);)
                                       /*  get list of dep's */
               {
                       np = newname(p);                /*  Intern name  */
                       dp = newdep(np, dp);            /*  Add to dep list */
               }

               *((q = str1) + strlen(str1) + 1) = '\0';
                       /*  Need two nulls for gettok (Remember separation)  */

               cp = (struct cmd *)0;
               if (getline(str2, fd) == FALSE)         /*  Get commands  */
               {
                       while (*str2 == '\t')
                       {
                               cp = newcmd(&str2[0], cp);
                               if (getline(str2, fd))
                                       break;
                       }
               }

               while ((p = gettok(&q)) != (char *)0)       /* Get list of targ's */
               {
                       np = newname(p);                /*  Intern name  */
                       newline(np, dp, cp);
                       if (!firstname)
                               firstname = np;
               }

               if (feof(fd) != 0  || endoffile == TRUE)        /*  EOF?  */
                       return;

               strcpy(str1, str2);
       }
}


/*
 * Case insensitive strcmp() then used for = or != purposes.
 *
 * This makes cp/m makefile usage a bit simpler
 *
 *                                             -mdk
 */

no_case_cmp(first,second)
char *first;
char *second;
{
       register char *p1,*p2;

       for (p1=first,p2=second ; *p1 != '\0' ; p1++, p2++) {
               if ((islower(*p1) ? tolower(*p1) : *p1) != 
                               (islower(*p2) ? tolower(*p2) : *p2) ) {
                       break;
               }
       }

       if (*p1 == '\0' && *p2 == '\0') {
               return(0);
       }
       return(1);
}
SHAR_EOF
if test 5650 -ne "`wc -c input.c`"
then
echo shar: error transmitting input.c '(should have been 5650 characters)'
fi
echo shar: extracting macro.c
cat << \SHAR_EOF > macro.c
/*
 *     Macro control for make
 */


#include "h.h"


struct macro *         macrohead;


struct macro *
getmp(name)
char *                 name;
{
       register struct macro * rp;

       for (rp = macrohead; rp; rp = rp->m_next)
               if (strcmp(name, rp->m_name) == 0)
                       return rp;
       return (struct macro *)0;
}


char *
getmacro(name)
char *                 name;
{
       struct macro *          mp;

       if (mp = getmp(name))
               return mp->m_val;
       else
               return "";
}


struct macro *
setmacro(name, val)
char *                 name;
char *                 val;
{
       register struct macro * rp;
       register char *         cp;


                       /*  Replace macro definition if it exists  */
       for (rp = macrohead; rp; rp = rp->m_next)
               if (strcmp(name, rp->m_name) == 0)
               {
                       free(rp->m_val);     /*  Free space from old  */
                       break;
               }

       if (!rp)                /*  If not defined, allocate space for new  */
       {
               if ((rp = (struct macro *)malloc(sizeof (struct macro)))
                                        == (struct macro *)0)
                       fatal("No memory for macro");

               rp->m_next = macrohead;
               macrohead = rp;
               rp->m_flag = FALSE;

               if ((cp = malloc(strlen(name)+1)) == (char *)0)
                       fatal("No memory for macro");
               strcpy(cp, name);
               rp->m_name = cp;
       }

       if ((cp = malloc(strlen(val)+1)) == (char *)0)
               fatal("No memory for macro");
       strcpy(cp, val);                /*  Copy in new value  */
       rp->m_val = cp;

       return rp;
}


/*
 *     Do the dirty work for expand
 */
void
doexp(to, from, len, buf)
char **                        to;
char *                 from;
int *                  len;
char *                 buf;
{
       register char *         rp;
       register char *         p;
       register char *         q;
       register struct macro * mp;


       rp = from;
       p = *to;
       while (*rp)
       {
               if (*rp != '$')
               {
                       *p++ = *rp++;
                       (*len)--;
               }
               else
               {
                       q = buf;
                       if (*++rp == '(')
                               while (*++rp && *rp != ')')
                                       *q++ = *rp;
                       else if (!*rp)
                       {
                               *p++ = '$';
                               break;
                       }
                       else
                               *q++ = *rp;
                       *q = '\0';
                       if (*rp)
                               rp++;
                       if (!(mp = getmp(buf)))
                               mp = setmacro(buf, "");
                       if (mp->m_flag)
                               fatal("Infinitely recursive macro %s", mp->m_name);
                       mp->m_flag = TRUE;
                       *to = p;
                       doexp(to, mp->m_val, len, buf);
                       p = *to;
                       mp->m_flag = FALSE;
               }
               if (*len <= 0)
                       error("Expanded line too line");
       }
       *p = '\0';
       *to = p;
}


/*
 *     Expand any macros in str.
 */
void
expand(str)
char *         str;
{
       static char             a[LZ];
       static char             b[LZ];
       char *                  p;
       int                     len = LZ-1;

       p = str;

       strcpy(a, str);
       doexp(&p, a, &len, b);
}
SHAR_EOF
if test 2298 -ne "`wc -c macro.c`"
then
echo shar: error transmitting macro.c '(should have been 2298 characters)'
fi
echo shar: extracting main.c
cat << \SHAR_EOF > main.c
/*
 *      make [-f makefile] [-nprst] [macro=val ...] [target(s) ...]
 *
 *     (Better than EON mk & old CPM make but not quite as good as UNIX make)
 *
 *     -f makefile name
 *     -i don't ignore exit status             
 *     -n Pretend to make
 *     -p Print all macros & targets
 *     -q Question up-to-dateness of target.  Return exit status 1 if not
 *     -r Don't not use inbuilt rules
 *     -s Make silently
 *     -t Touch files instead of making them
 *     -m Change memory requirements   - N/A under CP/M
 */

#include "c:stdio.h"                 /* c: is my ramdisk  */
#include "c:fcntl.h"
#include "h.h"

char Usage1[]="   -f makefile name\n\
       -i don't ignore exit status\n\
       -n Pretend to make\n\
       -p Print all macros & targets\n\
       -r Do not use inbuilt rules\n\
       -s Make silently\n\
       -t Touch files instead of making them\n";


#define MEMSPACE       (16384)         /* superfluous under cp/m */


char *                 myname;
char *                 makefile;       /*  The make file  */
unsigned               memspace = MEMSPACE;

extern FILE *execfile;

FILE *                 ifd;            /*  Input file desciptor  */
bool                   domake = TRUE;  /*  Go through the motions option  */
bool                   ignore = TRUE;  /*  Ignore exit status option  */
bool                   silent = FALSE; /*  Silent option  */
bool                   print = FALSE;  /*  Print debuging information  */
bool                   rules = TRUE;   /*  Use inbuilt rules  */
bool                   dotouch = FALSE;/*  Touch files instead of making  */
bool                   quest = FALSE;  /*  Question up-to-dateness of file  */


void
main(argc, argv)
char **                        argv;
int                    argc;
{
       register char *         p;      /*  For argument processing  */
       int                     estat;  /*  For question  */
       register struct name *  np;


       /*
        * CP/M makes all command line junque upper case.  All things
        * being equal, I'd rather they be all forced lower.  So I will.
        *
        * I have also forced everything in the makefile to lower case,
        * so everything should be case insensitive.
        */
       for (estat = argc ; --estat > 0 ;) {
                       strlower(argv[estat]);
       }

       unlink(MAKERUN);        /* delete possible existing make-submit file */

       myname = (argc-- < 1) ? "make" : *argv++;
       /*
        * CP/M can't do argv[0] (OS limitation), so we don't know who we are.
        */
       myname = "make";

       while ((argc > 0) && (**argv == '-'))
       {
               argc--;         /*  One less to process  */
               p = *argv++;    /*  Now processing this one  */

               while (*++p != '\0')
               {
                       /*
                        * Elaborate switch not really needed, but I
                        * hacked this in before I made all argv parameters
                        * lower case (for cp/m).
                        */
                       switch( isupper(*p) ? tolower (*p) : *p )
                       {
                       case 'f':       /*  Alternate file name  */
                               if (*++p == '\0')
                               {
                                       if (argc-- <= 0)
                                               usage();
                                       p = *argv++;
                               }
                               makefile = p;
                               goto end_of_args;
                       case 'm':       /*  Change space requirements  */
                               if (*++p == '\0')
                               {
                                       if (argc-- <= 0)
                                               usage();
                                       p = *argv++;
                               }
                               memspace = atoi(p);
                               goto end_of_args;
                       case 'n':       /*  Pretend mode  */
                               domake = FALSE;
                               break;
                       case 'i':       /*  Ignore fault mode  */
                               ignore = FALSE;
                               break;
                       case 's':       /*  Silent about commands  */
                               silent = TRUE;
                               break;
                       case 'p':
                               print = TRUE;
                               break;
                       case 'r':
                               rules = FALSE;
                               break;
                       case 't':
                               dotouch = TRUE;
                               break;
                       case 'q':
                               quest = TRUE;
                               break;
                       default:        /*  Wrong option  */
                               usage();
                       }
               }
       end_of_args:;
       }

/*     if (initalloc(memspace) == 0xffff)    Must get memory for alloc  
               fatal("Cannot initalloc memory");
*/
       if (strcmp(makefile, "-") == 0)       /*  Can use stdin as makefile  */
               ifd = stdin;
       else
               if (!makefile)          /*  If no file, then use default */
               {
                    if ((ifd = fopen(DEFN1, "r")) == (FILE *)0 
                         && ((ifd = fopen(DEFN2, "r")) == (FILE *)0)) {
                               fatal("Can't open %s or %s; error %02x",
                                                        DEFN1,DEFN2, errno);
                    }
               
               }
               else
                       if ((ifd = fopen(makefile, "r")) == (FILE *)0)
                               fatal("Can't open %s", makefile);

       if (rules)
               makerules();

       setmacro("$", "$");

       while (argc && (p = index(*argv, '=')))
       {
               char            c;

               c = *p;
               *p = '\0';
               setmacro(*argv, p+1);
               *p = c;

               argv++;
               argc--;
       }

       input(ifd);     /*  Input all the gunga  */
       fclose(ifd);    /*  Finished with makefile  */
       lineno = 0;     /*  Any calls to error now print no line number */

       if (print)
               prt();  /*  Print out structures  */

       np = newname(".SILENT");
       if (np->n_flag & N_TARG)
               silent = TRUE;

       np = newname(".IGNORE");
       if (np->n_flag & N_TARG)
               ignore = TRUE;

       precious();

       if (!domake)
               silent = FALSE;

       if (!firstname)
               fatal("No targets defined");

       circh();        /*  Check circles in target definitions  */

       if (!argc)
               estat = make(firstname, 0);
       else while (argc--)
       {
               if (!print && !silent && strcmp(*argv, "love") == 0)
                       printf("Not war!\n");
               estat |= make(newname(*argv++), 0);
       }


       if (execfile == NULL) {
               exit(0);        /* no file made, must be up to date! */
       }

       /*
        * The "trick" algorithm used for CP/M (which can't execute
        * commands while "make" is running) is that dosh() really
        * puts commands into a submit file MAKERUN, then after
        * the make is "done", that submit file is chained-to (below).
        * The first code-line below enters the submit file's last command
        * to delete itself (so this chaining trick is mostly transparent.
        *
        *                                              -mdk
        */

       dosh("era ",MAKERUN);   /* have the submit file delete itself */
       dosh("\n","");
       fclose(execfile);       /* close the writing to this file     */

       bdos(108,0);            /* Reset CP/M Program return code     */

       strcpy((char *)0x80,MAKERUN);   /* load submit file name into DMA buf*/
       bdos(47,0xff);          /* chain to the generated submit file */


/*     if (quest)
               exit(estat);
       else
               exit(0);
*/

}


usage()
{
       fprintf(stderr, "Usage: %s [-f makefile] [-nprst] [macro=val ...] [target(s)
...]\n%s", myname,Usage1);
       exit(1);
}


void
fatal(msg, a1, a2, a3)
char   *msg;
{
       fprintf(stderr, "%s: ", myname);
       fprintf(stderr, msg, a1, a2, a3);
       fputc('\n', stderr);
       exit(1);
}


       
strlower(string)
char *string;
{
       register char *pointer;
       char c;
       for (pointer = string ; (c=*pointer) != '\0' ; pointer++ ) {
               if (isupper(c))
                       *pointer = tolower(c);
       }
}
SHAR_EOF
if test 5995 -ne "`wc -c main.c`"
then
echo shar: error transmitting main.c '(should have been 5995 characters)'
fi
echo shar: extracting reader.c
cat << \SHAR_EOF > reader.c
/*
 *     Read in makefile
 */


#include "c:stdio.h"
#include "h.h"


int                    lineno;

int endoffile = FALSE;

/*
 *     Syntax error handler.  Print message, with line number, and exits.
 */
void
error(msg, a1, a2, a3)
char *                 msg;
{
       fprintf(stderr, "%s: ", myname);
       fprintf(stderr, msg, a1, a2, a3);
       if (lineno)
               fprintf(stderr, " on line %d", lineno);
       fputc('\n', stderr);
       exit(1);
}


/*
 *     Read a line into the supplied string of length LZ.  Remove
 *     comments, ignore blank lines. Deal with quoted (\) #, and
 *     quoted newlines.  If EOF return TRUE.
 */
bool
getline(str, fd)
char *         str;
FILE *         fd;
{
       register char *         p;
       char *                  q;
       int                     pos = 0;
       int                     concatflag = 0;
       int                     size;


       for (;;)
       {
               if (fgets(str+pos, LZ-pos, fd) == (char *)0) {
                       endoffile = TRUE;
                       return (TRUE);          /*  EOF  */
                       
               }
               if (index(str+pos,'\032') != (char *)0) {
                       endoffile = TRUE;
                       return(TRUE);
               }

               /*
                * Strip CP/M CR characters
                */
               if ((p=index(str+pos,'\015')) != (char *)0) {
                       do {
                               *p = p[1];
                       } while (*(++p) != '\0');
               }

               /*
                * Shorten leading whitespace on line extensions.
                */
               if (concatflag != 0) {
                       for (p = str+pos ; iswhite(*p) ; p++ ) ;
                       size = p-str-pos-1;
                       if (size > 0) {
                               p = str+pos+1;
                               while ((*p = p[size]) != '\0') {
                                       p++;
                               }
                       }
               }

               strlower(str+pos); /* let's do everything inlower case */

               lineno++;

               if ((p = index(str+pos, '\n')) == (char *)0)
                       error("Line too long");

               /* 
                * I want to actually join lines that are logically joined
                * so that when link commands are generated for CP/M,
                * a usable CCP command line will be generated.
                */
               if (p[-1] == '\\') {
                       *(--p) = '\0';
                       pos = p - str;
                       concatflag = 1;
                       continue;
               }

               p = str;
               while (((q = index(p, '#')) != (char *)0) &&
                   (p != q) && (q[-1] == '\\'))
               {
                       char    *a;

                       a = q - 1;      /*  Del \ chr; move rest back  */
                       p = q;
                       while (*a++ = *q++)
                               ;
               }
               if (q != (char *)0)
               {
                       q[0] = '\n';
                       q[1] = '\0';
               }

               p = str;
               while (isspace(*p))     /*  Checking for blank  */
                       p++;

               if (*p != '\0')
                       return FALSE;
               pos = 0;
       }
}


/*
 *     Get a word from the current line, surounded by white space.
 *     return a pointer to it. String returned has no white spaces
 *     in it.
 */
char *
gettok(ptr)
char   **ptr;
{
       register char *         p;


       while (isspace(**ptr))  /*  Skip spaces  */
               (*ptr)++;

       if (**ptr == '\0')      /*  Nothing after spaces  */
               return NULL;

       p = *ptr;               /*  word starts here  */

       while ((**ptr != '\0') && (!isspace(**ptr)))
               (*ptr)++;       /*  Find end of word  */

       *(*ptr)++ = '\0';       /*  Terminate it  */

       return(p);
}
SHAR_EOF
if test 2614 -ne "`wc -c reader.c`"
then
echo shar: error transmitting reader.c '(should have been 2614 characters)'
fi
echo shar: extracting rules.c
cat << \SHAR_EOF > rules.c
/*
 *     Control of the implicit suffix rules
 */


#include "h.h"

extern long ftime();
extern long time();
extern char *rindex();
extern struct name *newname();
extern char *setmacro();

/*
 *     Return a pointer to the suffix of a name
 */
char *
suffix(name)
char *                 name;
{
       return(rindex(name, '.'));
}


/*
 *     Dynamic dependency.  This routine applies the suffix rules
 *     to try and find a source and a set of rules for a missing
 *     target.  If found, np is made into a target with the implicit
 *     source name, and rules.  Returns TRUE if np was made into
 *     a target.
 */
bool
dyndep(np)
struct name *          np;
{
       register char *         p;
       register char *         q;
       register char *         suff;           /*  Old suffix  */
       register char *         basename;       /*  Name without suffix  */
       struct name *           op;             /*  New dependent  */
       struct name *           sp;             /*  Suffix  */
       struct line *           lp;
       struct depend *         dp;
       char *                  newsuff;


       p = str1;
       q = np->n_name;
       suff = suffix(q);
       while (q < suff)
               *p++ = *q++;
       *p = '\0';
       basename = setmacro("*", str1)->m_val;

       if (!((sp = newname(".suffixes"))->n_flag & N_TARG))
               return FALSE;

       for (lp = sp->n_line; lp; lp = lp->l_next)
               for (dp = lp->l_dep; dp; dp = dp->d_next)
               {
                       newsuff = dp->d_name->n_name;
                       if (strlen(suff)+strlen(newsuff)+1 >= LZ)
                               fatal("Suffix rule too long");
                       p = str1;
                       q = newsuff;
                       while (*p++ = *q++)
                               ;
                       p--;
                       q = suff;
                       while (*p++ = *q++)
                               ;
                       sp = newname(str1);
                       if (sp->n_flag & N_TARG)
                       {
                               p = str1;
                               q = basename;
                               if (strlen(basename) + strlen(newsuff)+1 >= LZ)
                                       fatal("Implicit name too long");
                               while (*p++ = *q++)
                                       ;
                               p--;
                               q = newsuff;
                               while (*p++ = *q++)
                                       ;
                               op = newname(str1);
                               if (!op->n_time)
               op->n_time = ftime(op->n_name);/*  Gets modtime of this file*/
                               if (op->n_time)
                               {
                                       dp = newdep(op, 0);
                                       newline(np, dp, sp->n_line->l_cmd);
                                       setmacro("<", op->n_name);
                                       return TRUE;
                               }
                       }
               }
       return FALSE;
}


/*
 *     Make the default rules
 */
void
makerules()
{
       struct cmd *            cp;
       struct name *           np;
       struct depend *         dp;


#ifdef xyz123zzz

       /*
        * Sure would have been nice if this had been documented as
        * to exactly what was going on with the routine calls.
        *
        *                                      -mdk
        */
       setmacro("BDSCC", "asm");
       /*      setmacro("BDSCFLAGS", "");  */
       cp = newcmd("$(BDSCC) $(BDSCFLAGS) -n $<", 0);
       np = newname(".c.o");
       newline(np, 0, cp);

       setmacro("CC", "c");
       setmacro("CFLAGS", "-O");
       cp = newcmd("$(CC) $(CFLAGS) -c $<", 0);
       np = newname(".c.obj");
       newline(np, 0, cp);

       setmacro("M80", "asm -n");
       /*      setmacro("M80FLAGS", "");   */
       cp = newcmd("$(M80) $(M80FLAGS) $<", 0);
       np = newname(".mac.o");
       newline(np, 0, cp);

       setmacro("AS", "zas");
       /*      setmacro("ASFLAGS", "");    */
       cp = newcmd("$(AS) $(ASFLAGS) -o $@ $<", 0);
       np = newname(".as.obj");
       newline(np, 0, cp);

       np = newname(".as");
       dp = newdep(np, 0);
       np = newname(".obj");
       dp = newdep(np, dp);
       np = newname(".c");
       dp = newdep(np, dp);
       np = newname(".o");
       dp = newdep(np, dp);
       np = newname(".mac");
       dp = newdep(np, dp);
       np = newname(".suffixes");
       newline(np, dp, 0);
#endif
       

       /*
        * C compilation.  I use cc-c.sub to do what unix cc -c does.
        *
        *      Macros can be overridden in makfile if I change and am
        *      too lazy to recompile this make program's rules.
        *
        */
       setmacro("ccc", "cc-c");
       setmacro("cflags", "");
       setmacro("cwhich", "$*");
       cp = newcmd("$(ccc) $(cflags) $(cwhich)", 0);
       np = newname(".c.o");
       newline(np, 0, cp);

       /*
        * Assembly using RMAC.
        */
       setmacro("asm", "rmac");
       setmacro("asmflags", "$$PZ SZ");
       setmacro("asmwhich", "$*");
       cp = newcmd("$(asm) $(asmwhich) $(asmflags)", 0);
       np = newname(".asm.rel");
       newline(np, 0, cp);
               
       /*
        * No point in my Z80 assembler or MAC.  Rules for
        * non-linkable one-module language-tools makes no
        * sense because it is a one-file process anyway.
        */


       np = newname(".asm");
       dp = newdep(np, 0);
       np = newname(".rel");
       dp = newdep(np,dp);
       np = newname(".c");
       dp = newdep(np, dp);
       np = newname(".o");
       dp = newdep(np,dp);
       np = newname(".suffixes");
       newline(np, dp, 0);

}
SHAR_EOF
if test 4094 -ne "`wc -c rules.c`"
then
echo shar: error transmitting rules.c '(should have been 4094 characters)'
fi
echo shar: extracting h.h
cat << \SHAR_EOF > h.h
/*
 *     Include header for make
 */


#ifndef uchar
#define uchar          char    /* regular char's are unsigned w/Aztec C */
#endif

#define void           int

#define bool           uchar
#define time_t         long
#define TRUE           (1)
#define FALSE          (0)
#ifndef max
#define max(a,b)       ((a)>(b)?(a):(b))
#endif

#define DEFN1          "MAKEFILE.DAT"                /*  Default names  */
#define DEFN2          "MAKEFILE"
#define errout(s) fputs(s, stderr) /* No need for DeSmet kludge */
#define MAKERUN "MAKE@@@.SUB" /* File on which commands are written */

#define LZ             (1024)                  /*  Line size  */



/*
 *     A name.  This represents a file, either to be made, or existant
 */

struct name
{
       struct name *           n_next;         /* Next in the list of names */
       char *                  n_name;         /* Called */
       struct line *           n_line;         /* Dependencies */
       time_t                  n_time;         /* Modify time of this name */
       uchar                   n_flag;         /* Info about the name */
};

#define N_MARK         0x01                    /* For cycle check */
#define N_DONE         0x02                    /* Name looked at */
#define N_TARG         0x04                    /* Name is a target */
#define N_PREC         0x08                    /* Target is precious */

/*
 *     Definition of a target line.
 */
struct line
{
       struct line *           l_next;         /* Next line (for ::) */
       struct depend *         l_dep;          /* Dependents for this line */
       struct cmd *            l_cmd;          /* Commands for this line */
};


/*
 *     List of dependents for a line
 */
struct depend
{
       struct depend *         d_next;         /* Next dependent */
       struct name *           d_name;         /* Name of dependent */
};


/*
 *     Commands for a line
 */
struct cmd
{
       struct cmd *            c_next;         /* Next command line */
       char *                  c_cmd;          /* Command line */
};


/*
 *     Macro storage
 */
struct macro
{
       struct macro *          m_next;         /* Next variable */
       char *                  m_name;         /* Called ... */
       char *                  m_val;          /* Its value */
       uchar                   m_flag;         /* Infinite loop check */
};

extern char *          myname;
extern struct name     namehead;
extern struct macro *  macrohead;
extern struct name *   firstname;
extern bool            silent;
extern bool            ignore;
extern bool            rules;
extern bool            dotouch;
extern bool            quest;
extern bool            domake;
extern char            str1[];
extern char            str2[];
extern int             lineno;

char *                 fgets();
char *                 index();
char *                 rindex();
char *                 malloc();
extern int             errno;

char *                 getmacro();
struct macro *         setmacro();
void                   input();
void                   error();
void                   fatal();
int                    make();
struct name *          newname();
struct depend *                newdep();
struct cmd *           newcmd();
void                   newline();
char *                 suffix();
void                   touch();
void                   makerules();
char *                 gettok();
void                   precious();
SHAR_EOF
if test 2448 -ne "`wc -c h.h`"
then
echo shar: error transmitting h.h '(should have been 2448 characters)'
fi
#      End of shell archive
exit 0

-- 

Mike Kersenbrock
Tektronix Computer Aided Software Engineering
Aloha, Oregon
<< Previous Message Main Index Next Message >>
<< Previous Message in Thread This Month Next Message in Thread >>