home *** CD-ROM | disk | FTP | other *** search
/ Power Programming / powerprogramming1994.iso / progtool / c / gymake12.arc / MAKE.C < prev    next >
C/C++ Source or Header  |  1988-11-18  |  19KB  |  861 lines

  1. /*
  2.  * make.c    An imitation of the Unix MAKE facility
  3.  *
  4.  * 88-10-01 v1.0    created by greg yachuk, placed in the public domain
  5.  * 88-10-06 v1.1    changed prerequisite list handling
  6.  * 88-11-11 v1.2    fixed some bugs and added environment variables
  7.  *
  8.  */
  9.  
  10. #include <stdio.h>
  11. #include <errno.h>
  12. #include <fcntl.h>
  13. #include <string.h>
  14. #include <memory.h>
  15. #include <sys/types.h>
  16. #include <sys/stat.h>
  17. #include <time.h>
  18. #ifdef    MSDOS
  19. #include <stdlib.h>
  20. #include <process.h>
  21. #include <dos.h>
  22. #else
  23. #include <sys/errno.h>
  24. #include <sys/wait.h>
  25. #endif
  26.  
  27. #include "make.h"
  28. #include "tstring.h"
  29. #include "decl.h"
  30.  
  31.  
  32. #ifdef    MSDOS
  33. #define    PATH_SEPARATOR    ";"
  34. #define    FILE_SEPARATOR    "/\\"
  35. #else
  36. #define    PATH_SEPARATOR    ":"
  37. #define    FILE_SEPARATOR    "/"
  38. #endif
  39.  
  40. #define MAKEINI "default.mk"
  41.  
  42. char   *shell_cmds[] = {
  43. #ifdef    MSDOS
  44.     "dir", "type", "rem", "pause", "date", "time", "ren", "rename",
  45.     "ver", "vol", "break", "verify", "mkdir", "md", "exit", "ctty",
  46.     "echo", "if", "cls", "chdir", "cd", "rmdir", "rd", "copy", "del",
  47.     "erase",
  48. #endif
  49.     NULL,
  50. };
  51.  
  52.  
  53. targptr target_list = NULL;    /* list of target nodes */
  54. fileptr file_list   = NULL;    /* list of file nodes */
  55. symptr  symbol_list = NULL;    /* list of symbol nodes */
  56. shellptr shell_list = NULL;    /* list of shell nodes */
  57.  
  58. int    make_level = 0;        /* for counting new_make()'s */
  59.  
  60. targptr first_targ = NULL;    /* first target, in case nothing explicit */
  61. targptr suffix_targ = NULL;    /* .SUFFIXES target pointer */
  62.  
  63. char  **tlist = NULL;        /* command line targets */
  64. char  **flist = NULL;        /* command line make files */
  65. char  **mlist = NULL;        /* command line macros */
  66.  
  67. int    tmax = 0;        /* max size of tlist */
  68. int    fmax = 0;        /* max size of flist */
  69. int    mmax = 0;        /* max size of mlist */
  70.  
  71. int    depend = 0;        /* -d option */
  72. int    display = 0;        /* -D option */
  73. int    envirn = 0;        /* -e option */
  74. int    ignore = 0;        /* -i option */
  75. int    noexec = 0;        /* -n option */
  76. int    readdef = 1;        /* -r option */
  77. int    silent = 0;        /* -s option */
  78. int    touch = 0;        /* -t option */
  79.  
  80. int    dispcount = 0;        /* used for -D option */
  81.  
  82. long    now;            /* time at startup */
  83. char   *makeflags;        /* value to update the MAKEFLAGS macro with */
  84.  
  85.  
  86. main(argc, argv)
  87. int     argc;
  88. char  **argv;
  89. {
  90.     REGISTER int i;
  91.     REGISTER targptr targp;
  92.     symptr    symp;
  93.     char   *envp;
  94.     char  **envv;
  95.  
  96.     /* initialize the various global lists */
  97.  
  98.     depend = 0;
  99.     dispcount = 0;
  100.  
  101.     target_list = NULL;
  102.     file_list = NULL;
  103.     shell_list = NULL;
  104.  
  105.     /* allocate space for command line targets, files and macros */
  106.  
  107.     tlist = grow_list(NULL, &tmax);
  108.     flist = grow_list(NULL, &fmax);
  109.     mlist = grow_list(NULL, &mmax);
  110.  
  111.     /* process MAKEFLAGS environment variable, first */
  112.  
  113.     symp = get_symbol("MAKEFLAGS", 0);
  114.     if (symp->svalue != NULL)
  115.     {
  116.         /* chop up the MAKEFLAGS and feed them to to make_args() */
  117.  
  118.         envp = tstrcpy(symp->svalue);
  119.         envv = tokenize(envp);
  120.         for (i=0; envv[i] != NULL; i++);
  121.         make_args(i, envv);
  122.  
  123.         /* free the vector of pointers, and the string itself, */
  124.         /* since you cannot have macros, targets or makefiles  */
  125.         /* in the MAKEFLAGS macro.                             */
  126.  
  127.         tfree(envv);
  128.         tfree(envp);
  129.         tfree(makeflags);    /* ignore this, since we just read it */
  130.     }
  131.  
  132.     make_args(--argc, ++argv);    /* process command line options */
  133.  
  134.     add_macro(makeflags, 0);    /* update the MAKEFLAGS macro */
  135.     tfree(makeflags);
  136.  
  137.     /* add command line macros, so they DON'T get overridden */
  138.  
  139.     for(i = 0; mlist[i] != NULL; i++)
  140.         add_macro(mlist[i], 1);
  141.  
  142.     tfree(mlist);            /* all done with macros */
  143.  
  144.     if (noexec)
  145.         silent = touch = 0;    /* -n always displays; never touches */
  146.  
  147.     if (dispcount > 1)
  148.         display = 1;        /* display `default.mk' on -DD */
  149.  
  150.     first_targ = NULL;        /* used in parse() */
  151.  
  152.     if (readdef)
  153.         parse(fopenp(MAKEINI, "r"));    /* read in `default.mk' */
  154.  
  155.     if (dispcount > 0)
  156.         display = 1;        /* display makefile's on -D */
  157.  
  158.     first_targ = NULL;        /* get first target in `makefile' */
  159.  
  160.     /* parse the makefiles given on command line */
  161.     for(i = 0; flist[i] != NULL; i++)
  162.     {
  163.         parse(equal(flist[i],"-") ? fdopen(dup(fileno(stdin)), "r")
  164.                       : fopen(flist[i], "r"));
  165.     }
  166.  
  167.     /* no makefiles specified, so use "makefile" */
  168.     if (i == 0)
  169.         parse(fopen("makefile", "r"));
  170.  
  171.     tfree(flist);            /* all done with makefile's */
  172.  
  173.     if ((targp = get_target(".INIT")) != NULL)
  174.         build(targp->tshell);    /* process the .INIT rule */
  175.  
  176.     for (i = 0; tlist[i] != NULL; i++)
  177.         make(tlist[i], 1);    /* process command line targets */
  178.  
  179.     tfree(tlist);            /* all done with targets */
  180.  
  181.     /* if no targets specified, make the first one */
  182.     if (i == 0 && first_targ)
  183.         make(first_targ->tfile->fname, 1);
  184.  
  185.     if ((targp = get_target(".DONE")) != NULL)
  186.         build(targp->tshell);    /* process the .DONE rule */
  187.  
  188.     return (0);            /* not exit(); see new_make() */
  189. }
  190.  
  191.  
  192. /*
  193.  * make_args    - process the command line arguments
  194.  */
  195. make_args(argc, argv)
  196. int    argc;
  197. char  **argv;
  198. {
  199.     REGISTER int tlen;
  200.     REGISTER int flen;
  201.     REGISTER int mlen;
  202.     char   *tmf;
  203.  
  204.     now = time(NULL);        /* get current date & time */
  205.  
  206.     makeflags = tstrcpy("MAKEFLAGS+=");
  207.  
  208.     tlen = flen = mlen = 0;
  209.  
  210.     for (;argc != 0; ++argv, --argc)
  211.     {
  212.         if (**argv != '-')
  213.         {
  214.             /* doesn't start with '-'; must be macro or target */
  215.  
  216.             if (strchr(*argv, '='))
  217.             {        /* store as a macro */
  218.                 if (mlen == mmax)
  219.                     mlist = grow_list(mlist, &mmax);
  220.                 mlist[mlen++] = *argv;
  221.             }
  222.             else
  223.             {        /* store as a target */
  224.                 if (tlen == tmax)
  225.                     tlist = grow_list(tlist, &tmax);
  226.                 tlist[tlen++] = *argv;
  227.             }
  228.             continue;
  229.         }
  230.  
  231.         /* must be an option */
  232.  
  233.         tmf = tstrcat(makeflags, *argv);
  234.         tfree(makeflags);
  235.         makeflags = tstrcat(tmf, " ");
  236.         tfree(tmf);
  237.  
  238.         while (*argv && *++*argv)
  239.         {
  240.             switch (**argv)
  241.             {
  242.             case 'd':        /* show dependencies */
  243.                 depend++;
  244.                 break;
  245.  
  246.             case 'D':        /* display makefiles */
  247.                 dispcount++;
  248.                 break;
  249.  
  250.             case 'e':        /* don't override environment */
  251.                 envirn = 1;
  252.                 break;
  253.  
  254.             case 'f':        /* new makefile name */
  255.                 if (argc < 2)
  256.                     usage();
  257.                 if (flen == fmax)
  258.                     flist = grow_list(flist, &fmax);
  259.                 ++argv, --argc;
  260.                 flist[flen++] = *argv;
  261.  
  262.                 /* save, but ignore, the makefile */
  263.                 tmf = tstrcat(makeflags, *argv);
  264.                 tfree(makeflags);
  265.                 makeflags = tstrcat(tmf, " ");
  266.                 tfree(tmf);
  267.  
  268.                 *argv = NULL;
  269.                 break;
  270.  
  271.             case 'i':        /* ignore errors */
  272.                 ignore = 1;
  273.                 break;
  274.  
  275.             case 'n':        /* don't execute commands */
  276.                 noexec = 1;
  277.                 break;
  278.  
  279.             case 'r':        /* don't read default.mk */
  280.                 readdef = 0;
  281.                 break;
  282.  
  283.             case 's':        /* don't echo commands */
  284.                 silent = 1;
  285.                 break;
  286.  
  287.             case 't':        /* touch files, don't build */
  288.                 touch = 1;
  289.                 break;
  290.  
  291.             default:
  292.                 usage();    /* never returns */
  293.             }
  294.         }
  295.     }
  296.  
  297.     /* terminate all lists with a NULL pointer */
  298.  
  299.     tlist[tlen] = NULL;
  300.     flist[flen] = NULL;
  301.     mlist[mlen] = NULL;
  302.  
  303.     /* let the caller update the makeflags macro */
  304. }
  305.  
  306.  
  307. /*
  308.  * grow_list    - expand the list of pointers by a factor of two
  309.  */
  310. char  **
  311. grow_list(list, len)
  312. char  **list;
  313. int    *len;
  314. {
  315.     REGISTER int l;
  316.  
  317.     l = *len;        /* get current length */
  318.  
  319.     /* if list is NULL, start off with a default list */
  320.  
  321.     if (list == NULL)
  322.         list = (char **) talloc(((l=1)+1) * sizeof(char *));
  323.     else
  324.         list = (char **) trealloc((char *)list,
  325.                       ((l <<=1)+1)*sizeof(char *));
  326.  
  327.     if (list == NULL)
  328.         terror(1, "too many options");
  329.  
  330.     /* if we are initially allocating it, set first pointer to NULL */
  331.  
  332.     if (l == 1)
  333.         *list = NULL;
  334.  
  335.     *len = l;        /* update current length */
  336.     return (list);
  337. }
  338.  
  339.  
  340. /*
  341.  * fopenp    - open file in current directory or along PATH
  342.  */
  343. FILE   *
  344. fopenp(fname, type)
  345. char   *fname;
  346. char   *type;
  347. {
  348.     REGISTER int len;
  349.     REGISTER char *fpath;
  350.     FILE   *fd;
  351.     char   *path;
  352.     char   *tp;
  353.  
  354.     /* try to open file relative to current directory */
  355.     if ((fd = fopen(fname, type)) != NULL)
  356.         return (fd);
  357.  
  358.     /* didn't work, search along path */
  359.  
  360.     if ((path = getenv("PATH")) == NULL)
  361.         return (NULL);
  362.  
  363.     path = tstrcpy(path);        /* allocate string and copy */
  364.     fpath = talloc(strlen(path) + strlen(fname) + 2);
  365.  
  366.     /* look for tokens separated by semi-colons (;) or colons (:) */
  367.  
  368.     tp = token(path, PATH_SEPARATOR);
  369.     while (tp != NULL)
  370.     {
  371.         strcpy(fpath, tp);
  372.         len = strlen(fpath) - 1;
  373.  
  374.         /* make sure there is a separator between path and filename */
  375.  
  376.         if (!strchr(FILE_SEPARATOR, fpath[len]))
  377.             fpath[++len] = '/';
  378.  
  379.         strcpy(&fpath[len+1], fname);    /* attach the filename */
  380.         if ((fd = fopen(fpath, type)) != NULL)
  381.             break;
  382.  
  383.         tp = token(NULL, PATH_SEPARATOR);
  384.     }
  385.  
  386.     tfree(path);
  387.     tfree(fpath);
  388.  
  389.     return (fd);
  390. }
  391.  
  392.  
  393. /*
  394.  * make        - guts of the make command
  395.  *        - make all pre-requisites, and if necessary, build target
  396.  *
  397.  *    returns    -1 target was already up to date w.r.t. pre-requisites
  398.  *         0 target has not been built
  399.  *         1 target is now built (and up to date)
  400.  */
  401. make(targname, worry)
  402. char   *targname;
  403. int    worry;        /* if set, it is an error to NOT build this */
  404. {
  405.     REGISTER targptr targp;
  406.     REGISTER fileptr *preqp;
  407.     fileptr    filep;
  408.     long    targtime;
  409.     long    preqtime;
  410.  
  411.     /* if recorded time of file is not default, we've already built it */
  412.     filep = get_file(targname);
  413.     if (filep && filep->ftime != MAXNEGTIME)
  414.         return (-1);
  415.  
  416.     targp = get_target(targname);    /* find the target node */
  417.     if (targp == NULL)
  418.         return (default_rule(targname, worry, 0));
  419.  
  420.     targtime = file_time(targname);    /* keep actual time of current target */
  421.  
  422.     /* must build non-existant files, even with no pre-requisites */
  423.     preqtime = MAXNEGTIME + 1;
  424.  
  425.     /* make all pre-requisites */
  426.     preqp = targp->tpreq;
  427.     while (preqp && *preqp)
  428.     {
  429.         make((*preqp)->fname, worry);
  430.  
  431.         /* keep track of newest pre-requisite */
  432.         if (preqtime < (*preqp)->ftime)
  433.             preqtime = (*preqp)->ftime;
  434.  
  435.         /* display as necessary */
  436.         if (depend > 1 || (depend && (*preqp)->ftime > targtime))
  437.         {
  438.             display_prereq(targname, targtime, (*preqp)->fname,
  439.                        (*preqp)->ftime);
  440.         }
  441.  
  442.         ++preqp;
  443.     }
  444.  
  445.     if (targp->tshell == NULL)    /* try default rules anyway */
  446.         return (default_rule(targname, 0, preqtime > targtime));
  447.     else if (preqtime > targtime)
  448.     {
  449.         if (touch)        /* won't be set when `noexec' */
  450.             touch_file(targname);
  451.         else
  452.         {
  453.             add_metas("", "", targname);
  454.             build(targp->tshell);
  455.         }
  456.  
  457.         targp->tfile->ftime = (noexec) ? now : file_time(targname);
  458.         return (1);
  459.     }
  460.  
  461.     targp->tfile->ftime = targtime;
  462.  
  463.     return (-1);
  464. }
  465.  
  466.  
  467. /*
  468.  * default_rule    - try the .SUFFIXES when we don't have an explicit target
  469.  *        - if `worry' is set, it is an ERROR to NOT build this target
  470.  *        - `mustbuild' is set if make() has out-of-date prereq's
  471.  *           but no explicit shell rules
  472.  */
  473. default_rule(targname, worry, mustbuild)
  474. char   *targname;
  475. int    worry;
  476. int    mustbuild;
  477. {
  478.     REGISTER targptr targp;
  479.     REGISTER fileptr *preqp;
  480.     fileptr    filep;
  481.     char   *ext;
  482.     char   *basename;
  483.     char   *preqname;
  484.     long    targtime;
  485.     long    preqtime;
  486.     int    built;
  487.     char    suffrule[80];
  488.  
  489.     ext = strrchr(targname, '.');    /* find the extension */
  490.     if (ext == NULL)
  491.         ext = targname + strlen(targname);
  492.  
  493.     basename = tstrncpy(targname, ext - targname);    /* find the base name */
  494.  
  495.     targtime = file_time(targname);
  496.  
  497.     /* suffix_targ is used to (slightly) speed up this function */
  498.     preqp = suffix_targ ? suffix_targ->tpreq : NULL;
  499.     built = 0;
  500.  
  501.     while (preqp && *preqp && !built)
  502.     {
  503.         /* look for a default rule from SUFFIX to `ext' */
  504.         strcat(strcpy(suffrule, (*preqp)->fname), ext);
  505.         targp = get_target(suffrule);    /* e.g. `.c.o' */
  506.  
  507.         if (targp != NULL)
  508.         {
  509.             /* found a rule; see if file exists */
  510.             preqname = tstrcat(basename, (*preqp)->fname);
  511.             preqtime = file_time(preqname);
  512.  
  513.             /*
  514.              * don't bother recursive makes unless necessary
  515.              * e.g. we have .c.o and .l.c, but also .l.o!
  516.              * we want to use .l.o if a .c file does not exist
  517.              */
  518.             if (preqtime != MAXNEGTIME || mustbuild)
  519.                 built = make(preqname, 0);
  520.  
  521.             /* check if pre-req file exists and is newer */
  522.             preqtime = file_time(preqname);
  523.             if (preqtime > targtime || (mustbuild && built))
  524.             {
  525.                 if (depend)
  526.                 {
  527.                     display_prereq(targname, targtime,
  528.                                preqname, preqtime);
  529.                 }
  530.  
  531.                 if (touch)    /* won't be set when `noexec' */
  532.                     touch_file(targname);
  533.                 else
  534.                 {
  535.                     add_metas(basename, preqname, targname);
  536.                     build(targp->tshell);
  537.                 }
  538.                 built = 1;
  539.             }
  540.             else if (depend > 1 && preqtime != MAXNEGTIME)
  541.             {
  542.                 display_prereq(targname, targtime,
  543.                            preqname, preqtime);
  544.             }
  545.  
  546.             tfree(preqname);
  547.         }
  548.  
  549.         ++preqp;        /* try next .SUFFIXES rule */
  550.     }
  551.  
  552.  
  553.     if (!built)
  554.     {
  555.         /* didn't find anything; try the default rule */
  556.         targp = get_target(".DEFAULT");
  557.         if (targp != NULL)
  558.         {
  559.             add_metas(basename, "", targname);
  560.             build(targp->tshell);
  561.             built = 1;
  562.         }
  563.         else if (targtime == MAXNEGTIME && worry)
  564.             terror(1, tstrcat("Don't know how to make ", targname));
  565.     }
  566.  
  567.     tfree(basename);
  568.  
  569.     /* record the current file time */
  570.     if ((filep = get_file(targname)) != NULL)
  571.     {
  572.         filep->ftime = (built == 1 && noexec) ? now
  573.                               : file_time(targname);
  574.     }
  575.  
  576.     return (built ? built : ((targtime == MAXNEGTIME) ? 0 : -1));
  577. }
  578.  
  579.  
  580. /*
  581.  * add_metas    - add symbols for $*, $< and $@
  582.  */
  583. add_metas(basename, preqname, targname)
  584. char   *basename;
  585. char   *preqname;
  586. char   *targname;
  587. {
  588.     add_symbol("*", basename, 0);
  589.     add_symbol("<", preqname, 0);
  590.     add_symbol("@", targname, 0);
  591. }
  592.  
  593.  
  594. /*
  595.  * touch_file    - set the MODIFICATION time of the file to NOW
  596.  */
  597. touch_file(targname)
  598. char   *targname;
  599. {
  600.     REGISTER int handle;
  601. #ifndef    MSDOS
  602.     time_t    timep[2];
  603.  
  604.     time(&timep[0]);
  605.     timep[1] = timep[0];
  606.     handle = utime(targname, timep);
  607. #else
  608.     handle = utime(targname, NULL);
  609. #endif
  610.     fputs("touch ", stdout);
  611.     puts(targname);
  612.  
  613.     if (handle == 0)
  614.         return;
  615.  
  616.     /* create the file, if it did not exist */
  617.     if (errno == ENOENT)
  618.     {
  619.         handle = open(targname, O_CREAT | O_TRUNC, S_IWRITE);
  620.         if (handle != -1)
  621.         {
  622.             close(handle);
  623.             return;
  624.         }
  625.     }
  626.  
  627.     perror("touch");
  628.     exit (1);
  629. }
  630.  
  631.  
  632. /*
  633.  * build    - process the shell commands
  634.  */
  635. build(shellp)
  636. REGISTER shellptr *shellp;
  637. {
  638.     int    runst;        /* exec return status */
  639.     char   *scmd;        /* command with symbols broken out */
  640.     char   *tcmd;        /* copy of scmd for `tokenize' */
  641.     REGISTER char **argv;    /* argified version of scmd */
  642.     char  **shcp;        /* pointer to shell command list */
  643.  
  644.     if (shellp == NULL)
  645.         return;
  646.  
  647.     for (;*shellp != NULL;
  648.          tfree(scmd), tfree(tcmd), tfree(argv), ++shellp)
  649.     {
  650.         /* breakout runtime symbols (e.g. $* $@ $<) */
  651.         scmd = breakout((*shellp)->scmd);
  652.  
  653.         if (!(*shellp)->s_silent && !silent)
  654.             puts(scmd);
  655.  
  656.         /* make a copy because tokenize litters '\0's */
  657.         tcmd = tstrcpy(scmd);
  658.         argv = tokenize(tcmd);
  659.  
  660.         if (equal(argv[0], "make"))
  661.         {
  662.             /* call ourselves recursively */
  663.             new_make(argv);
  664.             continue;
  665.         }
  666.  
  667.         if (noexec)
  668.             continue;
  669.  
  670.         /* any indirection MUST be handled by the shell */
  671.         if (!(*shellp)->s_shell && strpbrk(scmd, "<|>"))
  672.             (*shellp)->s_shell = 1;
  673. #ifdef    MSDOS
  674.         /* likewise, check for COMMAND.COM builtin commands */
  675.         shcp = shell_cmds;
  676.         for (; !(*shellp)->s_shell && *shcp; ++shcp)
  677.         {
  678.             if (equal(*shcp, argv[0]))
  679.             {
  680.                 (*shellp)->s_shell = 1;
  681.                 break;
  682.             }
  683.         }
  684. #endif
  685.         /* run without COMMAND.COM if possible, 'cause it uses RAM */
  686.         if ((*shellp)->s_shell)
  687.             runst = system(scmd);
  688.         else
  689.             runst = spawnvp(P_WAIT, argv[0], argv); 
  690.  
  691.         if (runst == 0)
  692.             continue;
  693.  
  694.         /* uh-oh, an error */
  695.         if (runst == -1)
  696.             perror("make");
  697. #ifdef    MSDOS
  698.         terror(0, tstrcat("\007*** Error code ",itoa(runst, scmd, 10)));
  699. #else
  700.         sprintf(scmd, "\007*** Error code %d\n", runst);
  701.         terror(0, scmd);
  702. #endif
  703.         if (!ignore && !(*shellp)->s_ignore)
  704.             exit(1);
  705.     }
  706. }
  707.  
  708.  
  709. /*
  710.  * new_make    - save current environment
  711.  *        - call make() recursively (actually main())
  712.  *        - clean up new environment
  713.  *        - restore environment
  714.  */
  715. new_make(argv)
  716. char  **argv;
  717. {
  718.     targptr    thead, tnext, tsuffix;
  719.     fileptr    fhead, fnext;
  720.     symptr    shead, snext;
  721.     shellptr shhead, shnext;
  722.     char  **ttlist;
  723.     long    tnow;
  724.     int    tdepend;
  725.     int    i;
  726.  
  727.     /* save all the globals */
  728.     tsuffix = suffix_targ;
  729.     thead = target_list;
  730.     fhead = file_list;
  731.     shead = symbol_list;
  732.     shhead = shell_list;
  733.     ttlist = tlist;
  734.     tnow = now;
  735.     tdepend = depend;
  736.  
  737.     /* count the arguments */
  738.     for (i = 0; argv[i]; ++i);
  739.  
  740.     /* call ourselves recursively; this inherits flags */
  741.     ++make_level;
  742.     main(i, argv);
  743.     --make_level;
  744.  
  745.     /* we're back, so gotta clean up and dispose of a few things */
  746.     while (target_list)
  747.     {
  748.         tnext = target_list->tnext;
  749.         if (target_list->tpreq)
  750.             tfree(target_list->tpreq);
  751.         if (target_list->tshell)
  752.             tfree(target_list->tshell);
  753.         tfree(target_list);
  754.         target_list = tnext;
  755.     }
  756.  
  757.     while (file_list)
  758.     {
  759.         fnext = file_list->fnext;
  760.         tfree(file_list->fname);
  761.         tfree(file_list);
  762.         file_list = fnext;
  763.     }
  764.  
  765.     /* don't drop all symbols, just the new ones */
  766.  
  767.     while (symbol_list != shead)
  768.     {
  769.         snext = symbol_list->snext;
  770.         tfree(symbol_list->sname);
  771.         tfree(symbol_list->svalue);
  772.         tfree(symbol_list);
  773.         symbol_list = snext;
  774.     }
  775.  
  776.     while (shell_list)
  777.     {
  778.         shnext = shell_list->slink;
  779.         tfree(shell_list->scmd);
  780.         tfree(shell_list);
  781.         shell_list = shnext;
  782.     }
  783.  
  784.     /* restore our original globals */
  785.     suffix_targ = tsuffix;
  786.     target_list = thead;
  787.     file_list = fhead;
  788.     symbol_list = shead;
  789.     shell_list = shhead;
  790.     tlist = ttlist;
  791.     now = tnow;
  792.     depend = tdepend;
  793. }
  794.  
  795.  
  796. usage()
  797. {
  798.     puts("make [-f filename] [-dDinrst] [target ...] [macro = value ...]");
  799.     exit(1);
  800. }
  801.  
  802.  
  803. display_prereq(targname, targtime, preqname, preqtime)
  804. char   *targname;
  805. long    targtime;
  806. char   *preqname;
  807. long    preqtime;
  808. {
  809. #ifdef    MSDOS
  810.     char    chtime[10];
  811.  
  812.     fputs(targname, stdout);
  813.     fputs(" (", stdout);
  814.     fputs(ltoa(targtime, chtime, 16), stdout);
  815.     fputs((targtime<=preqtime) ? ") older than ":") newer than ", stdout);
  816.     fputs(preqname, stdout);
  817.     fputs(" (", stdout);
  818.     fputs(ltoa(preqtime, chtime, 16), stdout);
  819.     puts(")");
  820. #else
  821.     printf("%s (%08lx) %s than %s (%08lx)\n",
  822.         targname, targtime, 
  823.         (targtime < preqtime) ? "older" : "newer",
  824.         preqname, preqtime);
  825. #endif
  826. }
  827.  
  828.  
  829. long
  830. file_time(fname)
  831. char   *fname;
  832. {
  833.     struct stat sbuf;
  834.  
  835.     if (stat(fname, &sbuf) != 0)
  836.         return (MAXNEGTIME);
  837.     return (sbuf.st_mtime);
  838. }
  839.  
  840.  
  841. #ifndef    MSDOS
  842. int
  843. spawnvp(mode, path, args)
  844. int    mode;
  845. char   *path;
  846. char  **args;
  847. {
  848.     int    pid = 0;
  849.     union wait waitword;
  850.  
  851.     if (mode != P_OVERLAY)
  852.         pid = fork();
  853.  
  854.     if (pid == 0)
  855.         execvp(path, args);
  856.  
  857.     wait(&waitword);
  858.     return (waitword.w_retcode);
  859. }
  860. #endif
  861.