home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS - Coast to Coast / simteldosarchivecoasttocoast2.iso / c / make.c < prev    next >
Text File  |  1994-03-04  |  26KB  |  1,291 lines

  1. \b readme
  2.  
  3.  
  4.  
  5. Last month I wrote a version of the Unix(tm) utility MAKE.  It runs under
  6. VAX/VMS and MSDOS 2.0.  I am placing it in the public domain, and it is yours
  7. for the asking.  You may copy it, or give it away.  You can make any changes
  8. you like to it.  All I ask is that you DO NOT TRY TO SELL IT.
  9.  
  10. Anyway, there is now a MAKE for MSDOS.  It is free, and it works pretty well.
  11. I'm giving it away because it might do the world some good.  Who knows?
  12.  
  13. Caveat: this version of MAKE is NOT compatible with the Unix(tm) version.
  14. Some differences are explained in the documentation.  Most of the problem stems
  15. from the fact that I've never had a chance to use the original version of MAKE,
  16. and the documentation I've seen on it has been poor.  My idea of what a make
  17. program should do is almost certainly different from what you Unix(tm) hackers
  18. are used to.  Well, hell -- the software is worth what you paid for it.  Have
  19. fun.
  20.  
  21. In order to get MAKE running on your system, you need to:
  22.  
  23.     1.  Read the documentation file MAKE.MAN.  (Yes, read the
  24.         directions.)
  25.  
  26.     2.  Edit the file MAKE.H to represent your system (VAX/VMS or
  27.         MSDOS 2.0.)
  28.  
  29.     3.  Recompile the source code by following the script file
  30.         CMAKE.COM (for VAX/VMS) or CMAKE.BAT (for MSDOS 2.0.)
  31.  
  32.         VAX/VMS requires the DEC C compiler; MSDOS 2.0 requires
  33.         Lattice C (or another C compiler of comparable quality)
  34.         and the Macro Assembler.
  35.  
  36.     4.  Test out MAKE by running it on itself.  (Make a backup
  37.         first!)
  38.  
  39.  
  40.  
  41.             Good luck,
  42.  
  43.             Landon Dyer (G.DYER @ SU-SCORE)
  44.  
  45.  
  46.  
  47.  
  48.  
  49. \e readme
  50. \b make.man
  51. MAKE(I)                3/10/84                    MAKE(I)
  52.  
  53.  
  54.  
  55. NAME
  56.     MAKE - maintain multiple source files (VAX/VMS and MSDOS 2.0)
  57.  
  58.  
  59. SYNOPSIS
  60.     MAKE [-N] [-A] [-F makefile] [name ...]
  61.  
  62.  
  63. DESCRIPTION
  64.     MAKE is a utility inspired by the Unix(tm) command of the same
  65.     name.  MAKE helps maintain programs that are constructed from
  66.     many files.  MAKE processes a "makefile", a file which describes
  67.     how to build a program from its source files, and produces a
  68.     script file containing the commands necessary to recompile the
  69.     program.
  70.  
  71.     Be careful: this MAKE is NOT compatible with Unix(tm) MAKE!
  72.  
  73.     The 'N' option causes MAKE to print out the steps it would follow
  74.     in order to rebuild the program.  The 'A' option tells MAKE to
  75.     assume that all files are obsolete, and that everything should be
  76.     recompiled.  The 'F' option, followed by a filename, can be used
  77.     to specify a makefile other than the default one.
  78.  
  79.     If no names are specified in the commandline, the first dependency
  80.     in the makefile is examined.  Otherwise, the specified root names
  81.     are brought up to date.
  82.  
  83.     The default makefiles are:
  84.  
  85.         for VAX/VMS:    MAKEFILE
  86.                 [-]MAKEFILE
  87.                 SYS$LOGIN:MAKEFILE
  88.  
  89.         for MSDOS:    MAKEFILE
  90.                 ..\MAKEFILE
  91.  
  92.     If the first makefile cannot be found, MAKE attempts to use the
  93.     next one.  If no makefile is ever found, MAKE prints a diagnostic
  94.     and aborts.
  95.  
  96.  
  97.  
  98.  
  99.  
  100. THE MAKEFILE
  101.     Comments begin with '!' and extend to the end of the line.  A
  102.     '!' (or almost any other character) may be escaped with the escape
  103.     character (backslash (\) on VMS, backquote (`) on MSDOS).  An escape
  104.     character may be typed by doubling it (\\ or ``).  The standard
  105.     Unix escape codes are recognized (\n, \r, \t, \b, \f, `n, `r, `t,
  106.     `b and `f).
  107.  
  108.     A makefile is a list of dependencies.  A dependency consists of
  109.     a root name, a colon, and zero or more names of dependent files.
  110.     (The colon MUST be preceeded by whitespace.)  For instance, in:
  111.  
  112.         make.exe : make.obj parsedir.obj file.obj macro.obj mk.h
  113.  
  114.     the file 'make.exe' depends on five other files.  A root name
  115.     with an empty dependency, as in:
  116.  
  117.         print :
  118.  
  119.     is assumed NEVER up to date, and will always be recompiled.
  120.  
  121.     The dependency list may be continued on successive lines:
  122.  
  123.         bigfile.exe : one.obj two.obj three.obj four.obj
  124.         five.obj six.obj gronk.obj freeple.obj scuzzy.lnk
  125.         frog.txt greeble.out
  126.  
  127.     Any number of 'method' lines may follow a dependency.  Method lines
  128.     begin with an ascii tab.  When a file is to be recompiled, MAKE
  129.     copies these method lines (minus the tab) to the script file.
  130.     For example, in:
  131.  
  132.         make.exe : make.obj parsedir.obj file.obj macro.obj mk.h
  133.             $link make, parsedir, file, macro
  134.             $write sys$output "Just another version of MAKE ..."
  135.             $purge
  136.  
  137.     the three lines following the dependency make up the method for
  138.     recompiling (or in this case, re-linking) the file 'make.exe'.
  139.  
  140.     If the macro "~INIT" is defined, its text will appear first in the
  141.     script file.  If the macro "~DEINIT" is defined, its text will
  142.     appear last in the script file.  By defining these two macros, it
  143.     is possible to configure the shell enviroment:
  144.  
  145.         ~INIT = $set term/nowrap\n$on error then goto err_handler
  146.         ~DEINIT = $set term/wrap\n$exit\$err_handler:\n
  147.         ~DEINIT = #(~DEINIT)$type err.log\n$exit
  148.  
  149.     will expand (in the script file) to:
  150.  
  151.         $set term/nowrap
  152.         $on error then goto err_handler
  153.         .
  154.         .
  155.         $set term/wrap
  156.         $exit
  157.         $err_handler:
  158.         $type err.log
  159.         $exit
  160.  
  161.     When a root's method is defined, the value of the macro "~BEFORE"
  162.     is prefixed to the method, and the value of the macro "~AFTER" is
  163.     appended to it.
  164.  
  165.     Frequently one wants to maintain more than one program with a single
  166.     makefile.  In this case, a "master dependency" can appear first in
  167.     the file:
  168.  
  169.         allOfMyToolsAndHorribleHacks : cat peek poke.exe grunge
  170.         cat : cat.exe
  171.         cat.exe : ....
  172.             (stuff for CAT.EXE)
  173.         peek : peek.exe
  174.         peek.exe : (stuff for PEEK.EXE)
  175.         poke.exe : (stuff for POKE.EXE)
  176.         grunge : grunge.com
  177.         grunge.com : (stuff for grung)
  178.  
  179.     In other words, make will bring everything up to date that is somehow
  180.     connected to the first dependency (its assumed that the incredibly
  181.     lengthy filename specified in this example won't actually exist).
  182.  
  183.  
  184.  
  185.  
  186.  
  187.  
  188.  
  189.  
  190.  
  191. MACROS
  192.     A macro is defined by a line of the form (the '=' MUST be surrounded
  193.     by whitespace):
  194.  
  195.         <macro-name> = <macro-body>
  196.  
  197.     A macro may be deleted by assigning an empty value to it.  Macros
  198.     may be redefined, but old definitions stay around.  If a macro is
  199.     redefined, and the redefinition is later deleted, the first definition
  200.     will take effect:
  201.  
  202.         MAC = first            ! MAC = "first"
  203.         MAC = second            ! MAC = "second"
  204.         MAC = #(MAC) third        ! MAC = "second third"
  205.         MAC =                ! MAC = "second"
  206.         MAC =                ! MAC = "first"
  207.         MAC =                ! MAC has no definition
  208.  
  209.     A macro may be referenced in two ways:
  210.  
  211.             #<char>      or    #(macro-name)
  212.  
  213.     The first way only works if the macro's name is a single character.
  214.     If the macro's name is longer than one character, it must be
  215.     enclosed in parenthesis.  ['#' may be escaped by doubling it ("##".)]
  216.     For example, in:
  217.  
  218.         G = mk.h mk1.h
  219.         OBJS = make.obj file.obj parsedir.obj macro.obj
  220.         BOTH = #(OBJS) #G
  221.     
  222.         make.exe : #(OBJS) #G
  223.         make.exe : #(BOTH)
  224.         make.exe : mk.h mk1.h make.obj file.obj parsedir.obj macro.obj
  225.             $write sys$output "This is a number sign --> ##"
  226.  
  227.     after macro expansion, the three dependencies will appear identical
  228.     and the two '#'s in the last line will turn into one '#'.
  229.  
  230.  
  231.  
  232.  
  233.  
  234.  
  235.  
  236.  
  237.  
  238.  
  239. UNIX(tm) MAKE AND THIS ONE
  240.     They are NOT the same.  Do not expect Unix makefiles to work with
  241.     this MAKE, even if you change the pathnames.  There are some major
  242.     differences between this version and the standard Unix(tm) MAKE:
  243.  
  244.     1. The Unix(tm) comment character is '#', VAX/VMS's is '!'.
  245.  
  246.     2. The Unix(tm) macro-expansion character is '$'.  While this would
  247.        have been easy to leave the same, the '$' character is used so
  248.        often in VAX/VMS command-lines that I thought it best to change
  249.        it to '#'.
  250.  
  251.     3. Multiple root names are not allowed.  Unix(tm) MAKE accepts lines
  252.        of the form:
  253.  
  254.         name1 name2 : depend1 depend2
  255.  
  256.        but this one doesn't.
  257.  
  258.     4. There is no equivalent of double-colon ("::".)
  259.  
  260.     5. There is no equivalent of .SUFFIXES, or the corresponding special
  261.        macros.
  262.  
  263.  
  264.  
  265.  
  266.  
  267.  
  268.  
  269.  
  270.  
  271.  
  272.  
  273.  
  274.  
  275.  
  276.  
  277.  
  278.  
  279.  
  280.  
  281.  
  282.  
  283.  
  284.  
  285.  
  286.  
  287. SAMPLE MAKEFILE
  288.     !
  289.     ! VAX/VMS MAKE
  290.     ! Landon Dyer
  291.     !
  292.     H = make.h
  293.     FILES = #H, make.c, macro.c, token.c, parsedir.c, file.c
  294.     DOCUMENTATION = distr.mem make.man makefile. make.com
  295.     
  296.     make.exe : make.obj macro.obj token.obj parsedir.obj file.obj
  297.         $link make.obj, macro, token, parsedir, file
  298.         $purge
  299.     
  300.     make.obj : make.c #H
  301.         $cc make.c
  302.     
  303.     macro.obj : macro.c #H
  304.         $cc macro
  305.     
  306.     token.obj : token.c #H
  307.         $cc token
  308.     
  309.     parsedir.obj : parsedir.c #H
  310.         $cc parsedir
  311.     
  312.     file.obj : file.c
  313.         $cc file
  314.     
  315.     !
  316.     ! Print files associated with MAKE
  317.     !
  318.     print :
  319.         $print make.man, #(FILES), make.com, makefile.
  320.     
  321.     !
  322.     ! Type out source to MAKE
  323.     !
  324.     type :
  325.         $type #(FILES), make.com, makefile.
  326.     
  327.     !
  328.     ! Make backup of source files.
  329.     !
  330.     BACKUP = [.bak]
  331.     backup :
  332.         $copy #(FILES) #(BACKUP)
  333.         $copy make.man, make.com, makefile. #(BACKUP)
  334.     
  335.     !
  336.     ! Collect MAKE into a distribution file.
  337.     !
  338.     collect :
  339.         $collect collect distr.mem make.man makefile make.com make.h -
  340.             make.c macro.c token.c parsedir.c file.c
  341.  
  342.  
  343. AUTHOR
  344.     Landon Dyer            G.DYER@SU-SCORE.ARPA
  345.     175 Calvert Dr. #F-211        BASHFL::DYER (Atari Coinop)
  346.     Cupertino, CA 95014
  347. \e make.man
  348. \b makefile
  349. !
  350. ! MSDOS Make utility
  351. ! (compile with Lattice C version 2.0)
  352. !
  353.  
  354. CLIB = \bin\lcs
  355. COBJ = \bin\cs
  356. H = make.h
  357. FILES = #H make.c macro.c token.c parsedir.c file.c osdate.asm
  358. DOCUMENTATION = readme make.man makefile
  359.  
  360. makeexe.exe : make.obj macro.obj token.obj parsedir.obj file.obj osdate.obj
  361.     link #(COBJ) make macro token parsedir file osdate,makeexe,,#(CLIB)
  362.  
  363. make.obj : make.c #H
  364.     lc1 make
  365.     lc2 make
  366.  
  367. macro.obj : macro.c #H
  368.     lc1 macro
  369.     lc2 macro
  370.  
  371. token.obj : token.c #H
  372.     lc1 token
  373.     lc2 token
  374.  
  375. parsedir.obj : parsedir.c #H
  376.     lc1 parsedir
  377.     lc2 parsedir
  378.  
  379. file.obj : file.c
  380.     lc1 file
  381.     lc2 file
  382.  
  383. osdate.obj : osdate.asm
  384.     masm osdate;
  385.  
  386. !
  387. ! Print files associated with MAKE
  388. !
  389. print :
  390.     print make.man #(FILES) makefile
  391.  
  392.  
  393. !
  394. ! collect source and documentation files
  395. !
  396. collect :
  397.     collect -o make.col @make.lis
  398.  
  399.  
  400. !
  401. ! copy to distribution disk (on A:)
  402. !
  403. distribution :
  404.     copy readme a:
  405.     copy make.man a:
  406.     copy makefile a:
  407.     copy make.bat a:
  408.     copy make.c a:
  409.     copy macro.c a:
  410.     copy token.c a:
  411.     copy parsedir.c a:
  412.     copy file.c a:
  413.     copy osdate.asm a:
  414.     copy cmake.bat a:
  415.     copy make.lis a:
  416.     copy makeexe.exe a:
  417. \e makefile
  418. \b make.bat
  419. echo off
  420. if exist make$$$$.bat del make$$$$.bat
  421. makeexe %1 %2 %3 %4 %5 %6 %7 %8 %9
  422. if exist make$$$$.bat make$$$$.bat
  423. \e make.bat
  424. \b make.c
  425. #include <stdio.h>
  426. #include <ctype.h>
  427. #include "make.h"
  428.  
  429. /*
  430.  *    MAKE - Maintain seperate source files
  431.  *
  432.  *    SYNOPSIS
  433.  *        MK [-f file] [-a] [-n] [-d] [name] ...
  434.  *           f: use 'file' instead of default makefile
  435.  *           a: assume all modules are obsolete (recompile everything)
  436.  *           n: don't recompile, just list steps to recompile
  437.  *           d: debugging (print tree, file info)
  438.  *           name: module name to recompile
  439.  *
  440.  *        'secret' options (not to be used by humans):
  441.  *           -ofile    'file' is the script file to write to
  442.  *
  443.  *    AUTHOR
  444.  *        Landon M. Dyer, Atari Inc.
  445.  *
  446.  */
  447.  
  448. #define SCRIPTFILE "make$$$$.bat"    /* (default) script-listing file */
  449. #define    INIT    "~INIT"            /* initialization macro */
  450. #define    DEINIT    "~DEINIT"        /* de-init macro */
  451. #define    BEFORE    "~BEFORE"        /* the per-root 'startup' method */
  452. #define    AFTER    "~AFTER"        /* the per-root 'wrapup' method */
  453.  
  454.  
  455. char *mfiles[] = {            /* default makefiles */
  456.     "makefile",
  457.  
  458. #ifdef VAXVMS
  459.     "[-]makefile",
  460.     "sys$login:makefile",
  461. #endif
  462.  
  463. #ifdef MSDOS
  464.     "..\makefile",
  465. #endif
  466.     ""
  467. };
  468.  
  469.  
  470. MACRO *mroot = NULL;        /* root of macro-list */
  471. FILENODE *froot = NULL;        /* root of filenode-list */
  472. FILENODE *firstf = NULL;    /* the very first filenode */
  473. FILE *mkfp = NULL;        /* script file */
  474. char *modnames[MAXMODS];    /* module-names mentioned in commandline */
  475. int modcount = 0;        /* #of module-names */
  476. int debug = 0;            /* nonzero: turn on debugging */
  477. int obsolete = 0;        /* nonzero: every file should be recompiled */
  478. int noscript = 0;        /* nonzero: print methods on standard output */
  479. char *scriptf = SCRIPTFILE;    /* default script file */
  480. DATE bigbang;            /* a date, the very earliest possible */
  481. DATE endoftime;            /* a date, the very last possible */
  482.  
  483.  
  484. main(argc, argv)
  485. int argc;
  486. char **argv;
  487. {
  488.     int arg, i;
  489.     char *mfile = NULL;
  490.     DATE adate();
  491.  
  492.     bigbang = adate(0, 0);        /* init root dates */
  493.     endoftime = adate(~0, ~0);
  494.  
  495.     for(arg = 1; arg < argc; ++arg)
  496.         if(*argv[arg] == '-') switch(tolower(argv[arg][1]))
  497.         {
  498.            case 'f':
  499.             if(++arg >= argc)
  500.             {
  501.                 fprintf(stderr, "-f needs filename argument.\n")
  502. ;
  503.                 return;
  504.             }
  505.             mfile = argv[arg];
  506.             break;
  507.  
  508.            case 'a':
  509.             obsolete = 1;
  510.             break;
  511.  
  512.            case 'n':
  513.             noscript = 1;
  514.             break;
  515.  
  516.            case 'd':
  517.             debug = 1;
  518.             break;
  519.  
  520.            case 'o':
  521.                scriptf = argv[arg] + 2;
  522.             break;
  523.  
  524.            default:
  525.             fprintf(stderr, "Unknown switch: %c\n", argv[arg][1]);
  526.             break;
  527.         } else if(modcount < MAXMODS)
  528.             modnames[modcount++] = argv[arg];
  529.         else
  530.         {
  531.             fprintf(stderr, "Too many module names.\n");
  532.             return;
  533.         }
  534.  
  535.     if(mfile != NULL)
  536.     {
  537.         if(fmake(mfile) == -1)
  538.             fprintf(stderr, "Cannot open makefile '%s'.\n", mfile);
  539.     } else {
  540.         for(i = 0; *mfiles[i]; ++i)
  541.             if(fmake(mfiles[i]) != -1) break;
  542.         if(!*mfiles[i])
  543.             fprintf(stderr, "Cannot open makefile.\n");
  544.     }
  545.  
  546.     if(debug) prtree();
  547. }
  548.  
  549.  
  550. /*
  551.  * Construct dependency tree from the makefile 'fn'.
  552.  * Figure out what has to be recompiled, and write a script file to do that.
  553.  */
  554. fmake(fn)
  555. char *fn;
  556. {
  557.     FILE *fp;
  558.  
  559.     if((fp = fopen(fn, "r")) == NULL) return -1;
  560.  
  561.     fparse(fp);
  562.     determ();
  563.  
  564.     fclose(fp);
  565.     return 0;
  566. }
  567.  
  568.  
  569. /*
  570.  * Parse the input file, defining macros and building the dependency tree.
  571.  */
  572. fparse(fp)
  573. FILE *fp;
  574. {
  575.     char ibuf[STRSIZ], ebuf[STRSIZ];
  576.     char *strp, *tok1, *tok2, *s;
  577.     FILENODE *lastf = NULL;
  578.     FILENODE *sf;
  579.  
  580.     for(;;)
  581.     {
  582.         if(fgets(ibuf, STRSIZ, fp) == NULL) break;
  583.         mexpand(ibuf, ebuf, STRSIZ, MACCHAR);
  584.         escape(ebuf, COMCHAR);
  585.  
  586.             /* clobber last newline in string */
  587.         s = ebuf + strlen(ebuf) - 1;
  588.         if(s >= ebuf && *s == '\n') *s = '\0';
  589.  
  590.         if(*ebuf == '\t')
  591.         {
  592.             addmeth(lastf, ebuf+1);
  593.             continue;
  594.         }
  595.  
  596.         strp = ebuf;
  597.         if((tok1 = token(&strp)) == NULL) continue;
  598.         if((tok2 = token(&strp)) != NULL)
  599.             if(!strcmp(tok2, DEFMAC))
  600.             {
  601.                 if(*strp) defmac(tok1, strp);
  602.                 else if(undefmac(tok1) < 0)
  603.                     fprintf(stderr,
  604.                       "Can't undefine macro '%s'\n", tok1);
  605.                 continue;
  606.             }
  607.             else if(!strcmp(tok2, DEPEND))
  608.             {
  609.                 addmeth(lastf, gmacro(AFTER));
  610.  
  611.                 lastf = filenode(tok1);
  612.                 if(firstf == NULL) firstf = lastf;
  613.                 lastf->fmake = NULL;
  614.  
  615.                 addmeth(lastf, gmacro(BEFORE));
  616.  
  617.                 lastf->fflag |= ROOTP;
  618.                 while((tok1 = token(&strp)) != NULL)
  619.                     addfile(lastf, tok1);
  620.                 continue;
  621.             }
  622.             else addfile(lastf, tok2);
  623.  
  624.         do {
  625.             addfile(lastf, tok1);
  626.         } while((tok1 = token(&strp)) != NULL);
  627.     }
  628.  
  629.     addmeth(lastf, gmacro(AFTER));
  630. }
  631.  
  632.  
  633. /*
  634.  * Determine sequence of recompiles from the creation dates.
  635.  * If there is anything to recompile, then create a script file full of commands
  636. .
  637.  */
  638. determ()
  639. {
  640.     FILENODE *f;
  641.     int i;
  642.     char *m;
  643.  
  644.     if(firstf == NULL)            /* empty tree */
  645.     {
  646.         printf("No changes.\n");
  647.         return;
  648.     }
  649.  
  650.     if(modcount == 0) examine(firstf, endoftime);
  651.     else for(i = 0; i < modcount; ++i)
  652.     {
  653.         if((f = gfile(modnames[i])) == NULL)
  654.         {
  655.             fprintf(stderr, "Can't find root '%s'.\n", modnames[i]);
  656.             continue;
  657.         }
  658.  
  659.         if(f->fflag & ROOTP == 0)
  660.         {
  661.             fprintf(stderr, "'%s' is not a root!\n", f->fname);
  662.             continue;
  663.         }
  664.         examine(f, endoftime);
  665.     }
  666.  
  667.     if(mkfp != NULL)
  668.     {
  669.         if((m = gmacro(DEINIT)) != NULL)
  670.         {
  671.             fputs(m, mkfp);
  672.             fputc('\n', mkfp);
  673.         }
  674.         fclose(mkfp);
  675.     } else printf("No changes.\n");
  676. }
  677.  
  678.  
  679. /*
  680.  * Examine filenode 'fnd' and see if it has to be recompiled.
  681.  * 'date' is the last-touched date of the node's father
  682.  * (or 'endoftime' if its a root file.)
  683.  * Root files with NO dependencies are assumed not to be up to date.
  684.  */
  685. examine(fnd, date)
  686. FILENODE *fnd;
  687. DATE date;
  688. {
  689.     int rebuildp = 0;
  690.     NODE *n;
  691.  
  692.     getdate(fnd);
  693.     if(fnd->fnode == NULL && fnd->fflag & ROOTP)
  694.         rebuildp = 1;
  695.     else for(n = fnd->fnode; n != NULL; n = n->nnext)
  696.         if(examine(n->nfile, fnd->fdate)) rebuildp = 1;
  697.  
  698.     if(rebuildp) recomp(fnd);
  699.     if(obsolete || laterdt(fnd->fdate, date) >= 0)
  700.         rebuildp = 1;
  701.     return rebuildp;
  702. }
  703.  
  704.  
  705. /*
  706.  * Make sure a filenode gets recompiled.
  707.  */
  708. recomp(f)
  709. FILENODE *f;
  710. {
  711.     FILENODE *sf;
  712.     char *m;
  713.  
  714.     if(mkfp == NULL)
  715.     {
  716.         if(noscript) mkfp = stdout;
  717.         else if((mkfp = fopen(scriptf, "w")) == NULL)
  718.             fprintf(stderr, "Cannot create: '%s'\n", scriptf);
  719.  
  720.     if((m = gmacro(INIT)) != NULL)
  721.         {
  722.             fputs(m, mkfp);
  723.             fputc('\n', mkfp);
  724.         }
  725.     }
  726.  
  727.     if(f->fflag & REBUILT) return;
  728.     if(f->fmake != NULL) fputs(f->fmake, mkfp);
  729.     f->fflag |= REBUILT;
  730. }
  731.  
  732.  
  733. /*
  734.  * Complain about being out of memory, and then die.
  735.  */
  736. allerr() {
  737.     fprintf(stderr, "Can't alloc -- no space left (I give up!)\n");
  738.     exit(1);
  739. }
  740. \e make.c
  741. \b macro.c
  742. #include <stdio.h>
  743. #include "make.h"
  744.  
  745. /*
  746.  * Macro processing
  747.  */
  748.  
  749.  
  750. /*
  751.  * Perform macro substitution from 'orig' to 'dest'.
  752.  * Return number of macro substitutions made.
  753.  * A macro reference is in one of two forms:
  754.  *        <MACCHAR>(macro-name)
  755.  *      or    <MACCHAR><single-character>
  756.  *
  757.  * "<MACCHAR><MACCHAR>" expands to a single '<MACCHAR>'
  758.  */
  759. mexpand(orig, dest, destsiz, macchar)
  760. char *orig, *dest;
  761. int destsiz;
  762. char macchar;
  763. {
  764.     char *s, *d, mname[STRSIZ];
  765.     int di, count;
  766.     MACRO *m;
  767.  
  768.     di = count = 0;
  769.     for(s=orig; *s;)
  770.         if(*s == macchar)
  771.         {
  772.             if(*++s == macchar)
  773.             {
  774.                 if(di < destsiz-1) dest[di++] = *s++;
  775.                 continue;
  776.             }
  777.  
  778.             if(!*s) break;
  779.             d = mname;
  780.             if(*s != '(') *d++ = *s++;
  781.             else
  782.             {
  783.                 for(++s; *s && *s!=')';) *d++ = *s++;
  784.                 if(*s != ')') puts("Missed matching ')'");
  785.                 else ++s;
  786.             }
  787.             *d = 0;
  788.             if((d = gmacro(mname)) == NULL)
  789.                 fprintf(stderr, "Undefined macro: %s\n", mname);
  790.             else
  791.             {
  792.                 while(*d && di < (destsiz - 1))
  793.                     dest[di++] = *d++;
  794.                 ++count;
  795.             }
  796.         } else if(di < destsiz-1)
  797.             dest[di++] = *s++;
  798.  
  799.     dest[di]=0;
  800.     return count;
  801. }
  802.  
  803.  
  804. /*
  805.  * Define a macro.
  806.  * Give the macro called 'name' the string expansion 'def'.
  807.  * Old macro-names are superseded, NOT replaced.
  808.  * Return ERROR if can't define the macro.
  809.  */
  810. defmac(name, def)
  811. char *name, *def;
  812. {
  813.     MACRO *m;
  814.  
  815.     if((m = (MACRO *)malloc(sizeof(MACRO))) == NULL) allerr();
  816.     if((m->mname = (char *)malloc(strlen(name)+1)) == NULL) allerr();
  817.     if((m->mvalue = (char *)malloc(strlen(def)+1)) == NULL) allerr();
  818.  
  819.     strcpy(m->mname, name);
  820.     strcpy(m->mvalue, def);
  821.     m->mnext = mroot;
  822.     mroot = m;
  823. }
  824.  
  825.  
  826. /*
  827.  * undefmac - undefine a macro.
  828.  * Return 0 if macro was succesfully undefined, -1 if not found.
  829.  */
  830. undefmac(name)
  831. char *name;
  832. {
  833.     MACRO *m = mroot;
  834.     MACRO *prev = NULL;
  835.  
  836.     while(m != NULL && strcmp(name, m->mname))
  837.     {
  838.         prev = m;
  839.         m = m->mnext;
  840.     }
  841.  
  842.     if(m == NULL) return -1;
  843.     if(prev == NULL) mroot = m->mnext;
  844.         else prev->mnext = m->mnext;
  845.  
  846.     free(m->mname);
  847.     free(m->mvalue);
  848.     free(m);
  849.     return 0;
  850. }
  851.  
  852.  
  853. /*
  854.  * Lookup a macro called 'name'.
  855.  * Return a pointer to its definition,
  856.  * or NULL if it does not exist.
  857.  */
  858. char *gmacro(name)
  859. char *name;
  860. {
  861.     MACRO *m;
  862.  
  863.     for(m=mroot; m != NULL; m=m->mnext)
  864.         if(!strcmp(name, m->mname)) return m->mvalue;
  865.     return NULL;
  866. }
  867. \e macro.c
  868. \b token.c
  869. #include <stdio.h>
  870. #include <ctype.h>
  871. #include "make.h"
  872.  
  873. /*
  874.  * Get next token from the string.  Return a pointer to it, or NULL.
  875.  * Adjust pointer to point to next part of string.
  876.  * The string is modified.
  877.  * A token consists of any number of non-white characters.
  878.  */
  879. char *token(strpp)
  880. char **strpp;
  881. {
  882.     char *s, *beg;
  883.  
  884.     stripwh(strpp);
  885.     if(!**strpp) return NULL;
  886.  
  887.     beg = s = *strpp;
  888.     while(*s && !isspace(*s)) ++s;
  889.     if(*s) *s++ = '\0';
  890.     *strpp = s;
  891.     return beg;
  892. }
  893.  
  894.  
  895. /*
  896.  * Parse character escape-sequences in a line of text.
  897.  *    <EscChar><EscChar> = <EscChar>
  898.  *    <EscChar>n = newline, and so on
  899.  *    <EscChar><char> = <char>
  900.  * The string is truncated at the first non-escaped occurance of 'comchar'.
  901.  */
  902. escape(str, comchar)
  903. char *str, comchar;
  904. {
  905.     char *d, c;
  906.  
  907.     for(d = str; *str && *str != comchar; ++str)
  908.         if(*str == ESCCHAR && *(str + 1)) switch((c = *++str))
  909.         {
  910.            case ESCCHAR:
  911.             *d++ = ESCCHAR;
  912.             break;
  913.  
  914.            case 'n':
  915.             *d++ = '\n';
  916.             break;
  917.  
  918.            case 'r':
  919.             *d++ = '\r';
  920.             break;
  921.  
  922.            case 't':
  923.             *d++ = '\t';
  924.             break;
  925.  
  926.            case 'b':
  927.             *d++ = '\b';
  928.             break;
  929.  
  930.            case 'f':
  931.             *d++ = '\f';
  932.             break;
  933.  
  934.            default:
  935.             *d++ = c;
  936.             break;
  937.         } else *d++ = *str;
  938.  
  939.     *d++ = 0;
  940. }
  941.  
  942.  
  943. stripwh(strpp)
  944. char **strpp;
  945. {
  946.     char *s;
  947.  
  948.     s = *strpp;
  949.     while(isspace(*s)) ++s;
  950.     return (*strpp = s);
  951. }
  952. \e token.c
  953. \b parsedir.c
  954. #include <stdio.h>
  955. #include "make.h"
  956. #ifdef VAXVMS
  957. #include <rms.h>
  958. #endif
  959.  
  960.  
  961. /*
  962.  * Get a file's creation date.
  963.  */
  964. int getdate(f)
  965. FILENODE *f;
  966. {
  967.     if(f->fdate != NULL || filedate(f) != -1) return;
  968.  
  969.     if(f->fflag & ROOTP == 0)
  970.     {
  971.         fprintf(stderr, "Can't get date for file '%s'\n", f->fname);
  972.         f->fdate = endoftime;
  973.     } else f->fdate = bigbang;
  974.     return;
  975. }
  976.  
  977.  
  978. #ifdef VAXVMS
  979. /*
  980.  * filedate - return file's creation date (VAX/VMS only.)
  981.  * Returns -1 if file cannot be found, 0 if succesful.
  982.  */
  983. filedate(fnd)
  984. FILENODE *fnd;
  985. {
  986.     unsigned *datetime;
  987.     DATE adate();
  988.     struct FAB *fptr;
  989.     struct XABDAT *dptr;
  990.  
  991.     fptr = malloc(sizeof(struct FAB));    /* allocate FAB and XABDAT */
  992.     dptr = malloc(sizeof(struct XABDAT));
  993.     if(fptr == NULL || dptr == NULL) allerr();
  994.     *fptr = cc$rms_fab;            /* initialize FAB and XABDAT */
  995.     *dptr = cc$rms_xabdat;
  996.     fptr->fab$l_xab = (char *) dptr;    /* FAB -> XABDAT */
  997.  
  998.     fptr->fab$l_fna = fnd->fname;        /* setup filename */
  999.     fptr->fab$b_fns = strlen(fnd->fname);
  1000.  
  1001.     if(sys$open(fptr) != RMS$_NORMAL ||    /* open the file */
  1002.        sys$display(fptr) != RMS$_NORMAL)    /* get XABDAT info */
  1003.         return -1;
  1004.  
  1005.     datetime = &(dptr->xab$q_cdt);        /* record 64-bit date */
  1006.     fnd->fdate = adate(datetime[0], datetime[1]);
  1007.  
  1008.     sys$close(fptr);            /* close the file */
  1009.  
  1010.     free(dptr);                /* clean up and return */
  1011.     free(fptr);
  1012.     return 0;
  1013. }
  1014. #endif
  1015.  
  1016.  
  1017. #ifdef MSDOS
  1018. /*
  1019.  * filedate - return file's creation date (MSDOS only.)
  1020.  * Returns -1 if file cannot be found, 0 if successful
  1021.  */
  1022. filedate(fnd)
  1023. FILENODE *fnd;
  1024. {
  1025.     unsigned date, time;
  1026.     DATE adate();
  1027.  
  1028.     if(osdate(fnd->fname, &time, &date) == -1) return -1;
  1029.     fnd->fdate = adate(time, date);
  1030. }
  1031. #endif
  1032.  
  1033.  
  1034. /*
  1035.  * laterdt - compare two dates.
  1036.  * Return -1, 0 or 1 if date1 < date2, date1 == date2, or date1 > date2
  1037.  */
  1038. laterdt(date1, date2)
  1039. DATE date1, date2;
  1040. {
  1041.     if(date1->ds_high > date2->ds_high ||
  1042.        (date1->ds_high >= date2->ds_high &&
  1043.         date1->ds_low > date2->ds_low)) return 1;
  1044.     else if(date1->ds_high == date2->ds_high &&
  1045.        date1->ds_low == date2->ds_low) return 0;
  1046.     else return -1;
  1047. }
  1048.  
  1049.  
  1050. /*
  1051.  * adate - allocate a date with the given time
  1052.  */
  1053. DATE adate(time1, time2)
  1054. unsigned time1, time2;
  1055. {
  1056.     DATE d;
  1057.  
  1058.     if((d = (DATE)malloc(sizeof(struct date_str))) == NULL) allerr();
  1059.     d->ds_low = time1;
  1060.     d->ds_high = time2;
  1061.     return d;
  1062.  
  1063. }
  1064. \e parsedir.c
  1065. \b file.c
  1066. #include <stdio.h>
  1067. #include "make.h"
  1068.  
  1069.  
  1070. /*
  1071.  * Return file-node for 'fname'.
  1072.  * If it doesn't exist, then create one.
  1073.  */
  1074. FILENODE *filenode(fname)
  1075. char *fname;
  1076. {
  1077.     FILENODE *f, *afnode(), *gfile();
  1078.  
  1079.     if((f = gfile(fname)) == NULL)
  1080.         f = afnode(fname);
  1081.     return f;
  1082. }
  1083.  
  1084.  
  1085. /*
  1086.  * Add a dependency to the node 'fnd'.
  1087.  * 'fnd' will depend on 'fname'.
  1088.  */
  1089. addfile(fnd, fname)
  1090. FILENODE *fnd;
  1091. char *fname;
  1092. {
  1093.     NODE *n;
  1094.     FILENODE *f;
  1095.  
  1096.     if(fnd == NULL)            /* punt if no root file */
  1097.     {
  1098.         fprintf(stderr, "No current root, can't add dependency '%s'\n", 
  1099. fname);
  1100.         return;
  1101.     }
  1102.  
  1103.     f = filenode(fname);
  1104.     if((n = (NODE *)malloc(sizeof(NODE))) == NULL) allerr();
  1105.     n->nnext = fnd->fnode;
  1106.     fnd->fnode = n;
  1107.     n->nfile = f;
  1108. }
  1109.  
  1110.  
  1111. /*
  1112.  * Add a line of method-text to the node 'fnode'.
  1113.  */
  1114. addmeth(fnode, methtext)
  1115. FILENODE *fnode;
  1116. char *methtext;
  1117. {
  1118.     int len;
  1119.     char *new;
  1120.  
  1121.     if(fnode == NULL || methtext == NULL) return;
  1122.  
  1123.     len = strlen(methtext) + 2;
  1124.     if(fnode->fmake == NULL)
  1125.     {
  1126.         if((fnode->fmake = (char *)malloc(1)) == NULL) allerr();
  1127.         *(fnode->fmake) = 0;
  1128.     }
  1129.     len += strlen(fnode->fmake);
  1130.  
  1131. /* Lattice C doesn't have 'realloc()', so this kludges around it: */
  1132.     if((new = (char *)malloc(len)) == NULL) allerr();
  1133.     strcpy(new, fnode->fmake);
  1134.     free(fnode->fmake);
  1135.     fnode->fmake = new;
  1136.  
  1137.     strcat(fnode->fmake, methtext);
  1138.     len = strlen(fnode->fmake);
  1139.     if(len && fnode->fmake[len - 1] != '\n')
  1140.         strcat(fnode->fmake, "\n");
  1141. }
  1142.  
  1143.  
  1144. /*
  1145.  * Get a filenode for the file called 'fn'.
  1146.  * Returns NULL if the node doesn't exist.
  1147.  */
  1148. FILENODE *gfile(fn)
  1149. char *fn;
  1150. {
  1151.     FILENODE *f;
  1152.  
  1153.     for(f = froot; f != NULL; f = f->fnext)
  1154.         if(!strcmp(fn, f->fname)) return f;
  1155.     return NULL;
  1156. }
  1157.  
  1158.  
  1159. /*
  1160.  * Alloc space for a new file node.
  1161.  */
  1162. FILENODE *afnode(name)
  1163. char *name;
  1164. {
  1165.     FILENODE *f;
  1166.  
  1167.     for(f=froot; f; f=f->fnext)
  1168.         if(!strcmp(name, f->fname)) return f;
  1169.  
  1170.     if((f = (FILENODE *)malloc(sizeof(FILENODE))) == NULL) allerr();
  1171.     if((f->fname = (char *)malloc(strlen(name)+1)) == NULL) allerr();
  1172.     strcpy(f->fname, name);
  1173.     f->fmake = NULL;
  1174.     f->fnode = NULL;
  1175.     f->fdate = NULL;
  1176.     f->fflag = 0;
  1177.  
  1178.     f->fnext = froot;
  1179.     froot = f;
  1180.     return f;
  1181. }
  1182.  
  1183.  
  1184. /*
  1185.  * Print dependency tree.
  1186.  */
  1187. prtree()
  1188. {
  1189.     FILENODE *f;
  1190.     NODE *n;
  1191.  
  1192.     for(f = froot; f != NULL; f = f->fnext)
  1193.     {
  1194.         printf("%s%s%s (%u, %u)\n",
  1195.             f->fname,
  1196.             (f->fflag & ROOTP) ? " (root)" : "",
  1197.             (f->fflag & REBUILT) ? " (rebuilt)" : "",
  1198.             (f->fdate != NULL) ? (f->fdate)->ds_high : 0,
  1199.             (f->fdate != NULL) ? (f->fdate)->ds_low : 0);
  1200.         if(f->fmake != NULL)
  1201.             printf("%s", f->fmake);
  1202.         for(n = f->fnode; n != NULL; n = n->nnext)
  1203.             printf("\t%s\n", (n->nfile)->fname);
  1204.         puts("");
  1205.     }
  1206. }
  1207. \e file.c
  1208. \b osdate.asm
  1209. dos    =    21h
  1210.  
  1211. arg1    =    4            ; lattice argument indexes
  1212. arg2    =    arg1+2
  1213. arg3    =    arg2+2
  1214.  
  1215. pgroup    group    prog
  1216. prog    segment byte public 'prog'
  1217.     public    osdate
  1218.     assume    cs:pgroup
  1219.  
  1220. ;
  1221. ;------
  1222. ; OSDATE - return file's creation-date (called from Lattice), or -1
  1223. ;       if can't find the file.
  1224. ; Synopsis:
  1225. ;        int osdate(filename, time1, time2)
  1226. ;            char *filename;
  1227. ;            int *time1, *time2;
  1228. ;
  1229. osdate proc near
  1230.     push    bp
  1231.     mov    bp,sp
  1232.  
  1233. ;--- Open the file
  1234.     mov    dx,[bp+arg1]
  1235.     xor    al,al
  1236.     mov    ah,3dh
  1237.     int    dos
  1238.     jc    osd$err            ; can't, so complain
  1239.  
  1240. ;--- Get file's creation date and time
  1241.     mov    bx,ax            ; get handle's date info
  1242.     xor    al,al
  1243.     mov    ah,57h
  1244.     int    dos
  1245.     jc    osd$cls            ; "can't happen" (but close it)
  1246.  
  1247. ;--- Install date/time info into caller's variables
  1248.     mov    si,[bp+arg2]        ; *arg2 = time (least significant)
  1249.     mov    [si],cx
  1250.     mov    si,[bp+arg3]        ; *arg3 = date (most significant)
  1251.     mov    [si],dx
  1252.  
  1253. ;--- Close file & return (ok)
  1254.     mov    ah,3eh
  1255.     int    dos
  1256.     xor    ax,ax
  1257.     pop    bp
  1258.     ret
  1259.  
  1260. ;--- Close file & return error condition
  1261. osd$cls:
  1262.     mov    ah,3eh
  1263.     int    dos
  1264. osd$err:
  1265.     mov    ax,-1
  1266.     pop    bp
  1267.     ret
  1268. osdate endp
  1269.  
  1270. prog    ends
  1271.     end
  1272. \e osdate.asm
  1273. \b cmake.bat
  1274. lc1 make
  1275. lc2 make
  1276. lc1 macro
  1277. lc2 macro
  1278. lc1 token
  1279. lc2 token
  1280. lc1 parsedir
  1281. lc2 parsedir
  1282. lc1 file
  1283. lc2 file
  1284. masm afind1st;
  1285. link c:c make macro token parsedir file afind1st,make;
  1286. \e cmake.bat
  1287. \b make.lis
  1288. readme make.man makefile make.bat make.c macro.c token.c
  1289. parsedir.c file.c osdate.asm cmake.bat make.lis
  1290. \e make.lis
  1291.