home *** CD-ROM | disk | FTP | other *** search
/ Power Programming / powerprogramming1994.iso / progtool / c / abmake14.arc / make.c < prev    next >
C/C++ Source or Header  |  1989-08-10  |  24KB  |  1,046 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-17 v1.2 AB    added lots of options
  7.  * 89-04-30 v1.3 AB    added environment flag MAKEFLAGS=
  8.  * 89-06-16 v1.4 AB lib response file not working
  9.  *
  10.  */
  11.  
  12. #include <stdio.h>
  13. #include <errno.h>
  14. #include <fcntl.h>
  15. #include <string.h>
  16. #include <memory.h>
  17. #include <ctype.h>
  18. #include <sys/types.h>
  19. #include <sys/stat.h>
  20. #ifdef    MSDOS
  21. #include <stdlib.h>
  22. #include <process.h>
  23. #include <dos.h>
  24. #include <io.h>
  25. #include <direct.h>
  26. #else
  27. #include <sys/errno.h>
  28. #include <sys/wait.h>
  29. #endif
  30.  
  31. #include "make.h"
  32. #include "tstring.h"
  33. #include "decl.h"
  34.  
  35.  
  36. #ifdef    MSDOS
  37. #define    PATH_SEPARATOR    ";"
  38. #define    FILE_SEPARATOR    "/\\"
  39. #define    RD_PERM            4            /* access() function */
  40. #else
  41. #define    PATH_SEPARATOR    ":"
  42. #define    FILE_SEPARATOR    "/"
  43. #define    RD_PERM            4            /* access() function */
  44. #endif
  45.  
  46. #define MAKEINI "default.mk"
  47.  
  48. char  **resp_cmds;                /* .RESPONSE commands */
  49. char   *shell_cmds[] = {
  50. #ifdef    MSDOS
  51.     "dir", "type", "rem", "pause", "date", "time", "ren", "rename",
  52.     "ver", "vol", "break", "verify", "mkdir", "md", "exit", "ctty",
  53.     "echo", "if", "cls", "chdir", "cd", "rmdir", "rd", "copy", "del",
  54.     "erase", "set", "for", "prompt", "path",
  55. #endif
  56.     NULL,
  57. };
  58.  
  59. char    *makelist[] =                /* List of possible makefile names */
  60. {
  61.     "makefile",
  62.     "Makefile",
  63.     NULL
  64. };
  65.  
  66. char    *usage_str[] =
  67. {
  68. #ifdef    MSDOS
  69.   "Make v1.4 (DOS version)\n",
  70. #else
  71.   "Make v1.4\n",
  72. #endif
  73.   "Usage : make [-f filename] [-CdeiknrstFV] [target ...] [macro = value ...]",
  74.   "",
  75.   "Options:",
  76.   "",
  77.   "    -f  the next argument is the name of a makefile",
  78.   "    -C  force forking shell for building target",
  79.   "    -d  show target dependencies",
  80.   "!   -D  show text of makefile",
  81.   "    -e  environment variables override macro assignments",
  82.   "    -F  force target updates",
  83.   "    -i  ignore error codes retunred by commands",
  84.   "    -k  abandon building current target, continue with other targets",
  85.   "    -n  don't execute, list actions",
  86.   "!   -p  print out macro definitions and target description",
  87.   "!   -P  print out macro definitions and target description",
  88.   "!   -q  question mode, make returns zero or nonzero status code",
  89.   "    -r  don't use builtin rules",
  90.   "    -s  run silently without listing actions",
  91.   "    -S  undo the effect of the -k option",
  92.   "    -t  touch the target files (bring them up to date)",
  93.   "    -V  list the current program version",
  94.   "",
  95.   "",
  96.   "Macro usage:",
  97.   ".DEFAULT  - define default rule to build target",
  98.   ".DONE     - target to build, after all other targets are build",
  99.   ".IGNORE   - ignore all error codes during building of targets",
  100.   ".INIT     - first target to build, before any target is build",
  101.   ".SILENT   - do not echo commands",
  102.   ".RESPONSE - define a response file for long ( > 128) command lines",
  103.   ".SUFFIXES - the suffixes list for selecting implicit rules",
  104.   NULL
  105. };
  106.  
  107. static    char **version = &usage_str[0];
  108.  
  109.  
  110. targptr target_list;        /* list of target nodes */
  111. fileptr file_list;            /* list of file nodes */
  112. symptr  symbol_list;        /* list of symbol nodes */
  113. shellptr shell_list;        /* list of shell nodes */
  114.  
  115. targptr first_targ = NULL;    /* first target, in case nothing explicit */
  116. targptr suffix_targ = NULL;    /* .SUFFIXES target pointer */
  117.  
  118. char  **tlist = NULL;        /* command line targets */
  119. char  **flist = NULL;        /* command line make files */
  120. char  **mlist = NULL;        /* command line macros */
  121.  
  122. int    tmax = 0;        /* max size of tlist */
  123. int    fmax = 0;        /* max size of flist */
  124. int    mmax = 0;        /* max size of mlist */
  125.  
  126. short   forceshell = 0;    /* -C option */
  127. short   debug = 0;        /* -d option */
  128. short   touchenv = 1;    /* -e option */
  129. short   ignore = 0;        /* -i option */
  130. short   single_ign = 0;    /* -k option */
  131. short   noexec = 0;        /* -n option */
  132. short   silent = 0;        /* -s option */
  133. short   touch = 0;        /* -t option */
  134. short   readdef = 1;    /* -r option */
  135. short   depend = 0;        /* -D option */
  136. short   force = 0;        /* -F option */
  137.  
  138. long    now;            /* time at startup */
  139.  
  140. #ifndef MSDOS
  141. static    void _searchenv();
  142. #endif
  143.  
  144. main(argc, argv)
  145. int     argc;
  146. char  **argv;
  147. {
  148. REGISTER int i;
  149. REGISTER targptr targp;
  150. targptr    resp_targ;        /* file list for long command lines>response file */
  151. int        done = 0;
  152. char    **makefile;
  153. char    *mk, *envargs, **eargv;
  154.  
  155.     /* get current date & time */
  156.  
  157.     now = curr_time();
  158.  
  159.     /* initialize the various global lists */
  160.  
  161.     target_list = NULL;
  162.     file_list = NULL;
  163.     symbol_list = NULL;
  164.     shell_list = NULL;
  165.  
  166.     /* allocate space for command line targets, files and macros */
  167.  
  168.     tlist = grow_list(NULL, &tmax);
  169.     flist = grow_list(NULL, &fmax);
  170.     mlist = grow_list(NULL, &mmax);
  171.  
  172.     add_symbol("MAKE", "make");    /* Default $(MAKE) macro */
  173.  
  174.     if ( (envargs = getenv("MAKEFLAGS")) )
  175.         {
  176.         eargv = tokenize(tstrcpy(envargs));
  177.         make_args(-1, eargv);
  178.         tfree(eargv);
  179.         }
  180.  
  181.     make_args(--argc, ++argv);        /* process command line options */
  182.  
  183.     if (noexec)
  184.         silent = touch = 0;        /* -n always displays; never touches */
  185.  
  186.     first_targ = NULL;            /* used in parse() */
  187.  
  188.     if (readdef)
  189.         parse(fopenp(MAKEINI, "r"), 0);    /* read in `default.mk' */
  190.  
  191.     first_targ = NULL;            /* get first target in `makefile' */
  192.  
  193.     /* parse the makefiles given on command line */
  194.     for(i = 0; flist[i] != NULL; i++)
  195.         parse(fopen(flist[i], "r"), 0);
  196.  
  197.     /* no makefiles specified, so use "makefile" */
  198.     if (i == 0)
  199.         {
  200.         for ( makefile = makelist; !done && *makefile; makefile++ )
  201.             {
  202.             if ( debug > 1 )
  203.                 printf("*** Accessing makefile '%s'\n", *makefile);
  204.             if ( (done = !access(*makefile, RD_PERM)) )
  205.                 {
  206.                 if ( debug )
  207.                     printf("*** Reading %s\n", *makefile);
  208.                 parse(fopen(*makefile, "r"), 0);
  209.                 }
  210.             }
  211.         }
  212.  
  213.     tfree(flist);                /* all done with makefile's */
  214.  
  215.     for(i = 0; mlist[i] != NULL; i++)
  216.         add_macro(mlist[i]);    /* add command line macros */
  217.  
  218.     tfree(mlist);                /* all done with macros */
  219.  
  220.     if ( (resp_targ = get_target(".RESPONSE")) )
  221.         resp_cmds = tokenize(breakout((*resp_targ->tshell)->scmd));
  222.  
  223.     if ((targp = get_target(".INIT")) != NULL)
  224.         build(targp->tshell);    /* process the .INIT rule */
  225.  
  226.     for (i = 0; tlist[i] != NULL; i++)
  227.         make(tlist[i], 1);        /* process command line targets */
  228.  
  229.     tfree(tlist);                /* all done with targets */
  230.  
  231.     /* if no targets specified, make the first one */
  232.     if (i == 0 && first_targ)
  233.         make(first_targ->tfile->fname, 1);
  234.  
  235.     if ((targp = get_target(".DONE")) != NULL)
  236.         build(targp->tshell);    /* process the .DONE rule */
  237.  
  238.     return (0);                    /* not exit(); see new_make() */
  239. }
  240.  
  241. /*    decode all command line & environment options    */
  242. /*    no -f option accepted from env, argc set neg    */
  243.  
  244. make_args(argc, argv)
  245. int        argc;
  246. char  **argv;
  247. {
  248. static    int tlen = 0;
  249. static    int flen = 0;
  250. static    int mlen = 0;
  251. char    no_k = 0;
  252.  
  253.     for ( ; *argv && argc; *(++argv) && --argc )
  254.         {
  255.         /* look for macros and targets */
  256.         if ( **argv != '-' )
  257.             {
  258.             if ( strchr(*argv, '=') )
  259.                 {        /* store as a macro */
  260.                 if (mlen == mmax)
  261.                     mlist = grow_list(mlist, &mmax);
  262.                 mlist[mlen++] = *argv;
  263.                 }
  264.             else
  265.                 {        /* store as a target */
  266.                 if ( tlen == tmax )
  267.                     tlist = grow_list(tlist, &tmax);
  268.                 tlist[tlen++] = *argv;
  269.                 }
  270.             continue;
  271.             }
  272.  
  273.         while ( *argv && *++*argv )
  274.             {
  275.             switch (**argv)
  276.                 {
  277.                 case 'C':        /* force shell forking for target */
  278.                     forceshell = 1;
  279.                     break;
  280.  
  281.                 case 'd':        /* show dependencies */
  282.                     if ( isdigit(*(*argv + 1)) )
  283.                         debug = *++*argv;
  284.                     else
  285.                         debug++;
  286.                     break;
  287.  
  288.                 case 'e':        /* environment overrides assignments */
  289.                     touchenv = 0;
  290.                     break;
  291.  
  292.                 case 'f':        /* new makefile name */
  293.                     if ( argc > 0 )
  294.                         {
  295.                         if (argc < 2)
  296.                             usage();
  297.                         if (flen == fmax)
  298.                             flist = grow_list(flist, &fmax);
  299.                         flist[flen++] = *(++argv);
  300.                         }
  301.                     else
  302.                         {
  303.                         printf("Illegal -f option in environment, ignored\n");
  304.                         ++argv;
  305.                         }
  306.                     --argc;
  307.                     *argv = NULL;
  308.                     break;
  309.  
  310.                 case 'i':        /* ignore errors */
  311.                     ignore = 1;
  312.                     break;
  313.  
  314.                 case 'k':        /* ignore single target errors only */
  315.                     single_ign = 1;
  316.                     break;
  317.  
  318.                 case 'n':        /* don't execute commands */
  319.                     noexec = 1;
  320.                     break;
  321.  
  322.                 case 'r':        /* don't read default.mk */
  323.                     readdef = 0;
  324.                     break;
  325.  
  326.                 case 's':        /* don't echo commands */
  327.                     silent = 1;
  328.                     break;
  329.  
  330.                 case 'S':        /* undo the -k option */
  331.                     no_k = 1;
  332.                     break;
  333.  
  334.                 case 't':        /* touch files, don't build */
  335.                     touch = 1;
  336.                     break;
  337.  
  338.                 case 'D':        /* display makefiles */
  339.                     depend = 1;
  340.                     break;
  341.  
  342.                 case 'F':        /* ignore target date, force make */
  343.                     force = 1;
  344.                     break;
  345.  
  346.                 case 'V':        /* ignore target date, force make */
  347.                     puts(*version);
  348.                     break;
  349.  
  350.                 default:
  351.                     usage();
  352.                 }
  353.             }
  354.         }
  355.  
  356.     if ( no_k )
  357.         single_ign = 0;
  358.  
  359.     /* terminate all lists with a NULL pointer */
  360.  
  361.     tlist[tlen] = NULL;
  362.     flist[flen] = NULL;
  363.     mlist[mlen] = NULL;
  364. }
  365.  
  366.  
  367. /*
  368.  * grow_list    - expand the list of pointers by a factor of two
  369.  */
  370. char **grow_list(list, len)
  371. char    **list;
  372. int        *len;
  373. {
  374. REGISTER int l;
  375.  
  376.     l = *len;        /* get current length */
  377.  
  378.     /* if list is NULL, start off with a default list */
  379.  
  380.     if ( list == NULL )
  381.         list = (char **)talloc(((l=1)+1) * sizeof(char *));
  382.     else
  383.         list = (char **) trealloc((char *)list, ((l <<=1)+1)*sizeof(char *));
  384.  
  385.     if ( list == NULL )
  386.         terror(1, "too many options");
  387.  
  388.     /* if we are initially allocating it, set first pointer to NULL */
  389.  
  390.     if ( l == 1 )
  391.         *list = NULL;
  392.  
  393.     *len = l;        /* update current length */
  394.     return (list);
  395. }
  396.  
  397.  
  398. /*
  399.  * fopenp    - open file in current directory or along PATH
  400.  */
  401. FILE *fopenp(fname, type)
  402. char   *fname;
  403. char   *type;
  404. {
  405. REGISTER int len;
  406. REGISTER char *fpath;
  407. FILE   *fd;
  408.  
  409.     /* if the filename is -, use stdin */
  410.     if (equal(fname, "-"))
  411.         return (stdin);
  412.  
  413.     fpath = talloc(256 + strlen(fname) + 2);
  414.  
  415.     _searchenv(fname, "PATH", fpath);
  416.  
  417.     /* try to open file relative to current directory */
  418.  
  419.     if ( (fd = fopen(fpath, type)) != NULL && debug )
  420.         printf("*** Reading default rules from %s\n", fpath);
  421.  
  422.     tfree(fpath);
  423.  
  424.     return (fd);
  425. }
  426.  
  427. #ifndef MSDOS
  428. void _searchenv(fname, env, fpath)
  429. char    *fname, *env, *fpath;
  430. {
  431. char   *path;
  432. char   *tp;
  433.  
  434.     *fpath = '\0';
  435.  
  436.     if ((path = getenv(env)) == NULL)
  437.         return;
  438.  
  439.     path = tstrcpy(path);        /* allocate string and copy */
  440.  
  441.     /* look for tokens separated by semi-colons (;) or colons (:) */
  442.  
  443.     tp = token(path, PATH_SEPARATOR);
  444.     while (tp != NULL)
  445.     {
  446.         strcpy(fpath, tp);
  447.         len = strlen(fpath) - 1;
  448.  
  449.         /* make sure there is a separator between path and filename */
  450.  
  451.         if (!strchr(FILE_SEPARATOR, fpath[len]))
  452.             fpath[++len] = "/";
  453.  
  454.         strcpy(&fpath[len+1], fname);
  455.         if ( !access(fpath, RD_PERM)) )
  456.             break;
  457.  
  458.         tp = token(NULL, PATH_SEPARATOR);
  459.     }
  460.     tfree(path);
  461. }
  462. #endif
  463.  
  464.  
  465. /*
  466.  * make        - guts of the make command
  467.  *            - make all pre-requisites, and if necessary, build target
  468.  *
  469.  *    returns    -1 target was already up to date w.r.t. pre-requisites
  470.  *             0 target has not been built
  471.  *             1 target is now built (and up to date)
  472.  */
  473. make(targname, worry)
  474. char   *targname;
  475. int        worry;        /* if set, it is an error to NOT build this */
  476. {
  477.     REGISTER targptr targp;
  478.     REGISTER fileptr *preqp;
  479.     fileptr    filep;
  480.     long    targtime;
  481.     long    preqtime;
  482.  
  483.     /* if recorded time of file is not default, we've already built it */
  484.     filep = get_file(targname);
  485.     if (filep && filep->ftime != MAXNEGTIME)
  486.         return (-1);
  487.  
  488.     targp = get_target(targname);    /* find the target node */
  489.     if (targp == NULL)
  490.         return (default_rule(targname, worry, 0));
  491.  
  492.     targtime = file_time(targname);    /* keep actual time of current target */
  493.  
  494.     /* must build non-existant files, even with no pre-requisites */
  495.     preqtime = MAXNEGTIME + 1;
  496.  
  497.     /* make all pre-requisites */
  498.     preqp = targp->tpreq;
  499.     while (*preqp)
  500.     {
  501.         make((*preqp)->fname, worry);
  502.  
  503.         /* keep track of newest pre-requisite */
  504.         if (preqtime < (*preqp)->ftime)
  505.             preqtime = (*preqp)->ftime;
  506.  
  507.         /* display as necessary */
  508.         if (debug || (debug && (*preqp)->ftime > targtime))
  509.         {
  510.             display_prereq(targname, targtime, (*preqp)->fname,
  511.                        (*preqp)->ftime);
  512.         }
  513.  
  514.         ++preqp;
  515.     }
  516.  
  517.     if (targp->tshell == NULL)    /* try default rules anyway */
  518.         return (default_rule(targname, 0, force || preqtime > targtime));
  519.     else if (force || preqtime > targtime)
  520.     {
  521.         if (touch)        /* won't be set when `noexec' */
  522.             touch_file(targname);
  523.         else
  524.         {
  525.             add_metas("", "", targname);
  526.             build(targp->tshell);
  527.         }
  528.  
  529.         targp->tfile->ftime = (noexec) ? now : file_time(targname);
  530.         return (1);
  531.     }
  532.  
  533.     targp->tfile->ftime = targtime;
  534.  
  535.     return (-1);
  536. }
  537.  
  538.  
  539. /*
  540.  * default_rule    - try the .SUFFIXES when we don't have an explicit target
  541.  *        - if `worry' is set, it is an ERROR to NOT build this target
  542.  *        - `mustbuild' is set if make() has out-of-date prereq's
  543.  *           but no explicit shell rules
  544.  */
  545. default_rule(targname, worry, mustbuild)
  546. char   *targname;
  547. int    worry;
  548. int    mustbuild;
  549. {
  550. REGISTER targptr targp;
  551. REGISTER fileptr *preqp;
  552. fileptr    filep;
  553. char   *ext;
  554. char   *basename;
  555. char   *preqname;
  556. long    targtime;
  557. long    preqtime;
  558. int        built;
  559. char    suffrule[80];
  560.  
  561.     /* find the extension */
  562.     ext = strrchr(targname, '.');
  563.     if (ext == NULL)
  564.         ext = targname + strlen(targname);
  565.  
  566.     /* find the base name */
  567.     basename = tstrncpy(targname, ext - targname);
  568.  
  569.     targtime = file_time(targname);
  570.  
  571.     /* suffix_targ is used to (slightly) speed up this function */
  572.     preqp = suffix_targ ? suffix_targ->tpreq : NULL;
  573.     built = 0;
  574.  
  575.     while (preqp && *preqp && !built)
  576.         {
  577.         /* look for a default rule from SUFFIX to `ext' */
  578.         strcat(strcpy(suffrule, (*preqp)->fname), ext);
  579.         targp = get_target(suffrule);    /* e.g. `.c.o' */
  580.  
  581.         if (targp != NULL)
  582.             {
  583.             /* found a rule; see if file exists */
  584.             preqname = tstrcat(basename, (*preqp)->fname);
  585.             preqtime = file_time(preqname);
  586.  
  587.             /*
  588.              * don't bother recursive makes unless necessary
  589.              * e.g. we have .c.o and .l.c, but also .l.o!
  590.              * we want to use .l.o if a .c file does not exist
  591.              */
  592.             if (preqtime != MAXNEGTIME || mustbuild)
  593.                 built = make(preqname, 0);
  594.  
  595.             /* check if pre-req file exists and is newer */
  596.             preqtime = file_time(preqname);
  597.             if (preqtime > targtime || (mustbuild && built))
  598.                 {
  599.                 if (debug)
  600.                     display_prereq(targname, targtime, preqname, preqtime);
  601.  
  602.                 if (touch)    /* won't be set when `noexec' */
  603.                     touch_file(targname);
  604.                 else
  605.                     {
  606.                     add_metas(basename, preqname, targname);
  607.                     build(targp->tshell);
  608.                     }
  609.                 built = 1;
  610.                 }
  611.             else if (debug && preqtime != MAXNEGTIME)
  612.                 display_prereq(targname, targtime, preqname, preqtime);
  613.  
  614.             tfree(preqname);
  615.             }
  616.  
  617.         ++preqp;        /* try next .SUFFIXES rule */
  618.         }
  619.  
  620.  
  621.     if (!built)
  622.     {
  623.         /* didn't find anything; try the default rule */
  624.         targp = get_target(".DEFAULT");
  625.         if (targp != NULL)
  626.         {
  627.             add_metas(basename, "", targname);
  628.             build(targp->tshell);
  629.             built = 1;
  630.         }
  631.         else if (targtime == MAXNEGTIME && worry)
  632.             terror(1, tstrcat("Don't know how to make ", targname));
  633.     }
  634.  
  635.     tfree(basename);
  636.  
  637.     /* record the current file time */
  638.     if ((filep = get_file(targname)) != NULL)
  639.     {
  640.         filep->ftime = (built == 1 && noexec) ? now
  641.                               : file_time(targname);
  642.     }
  643.  
  644.     return (built ? built : ((targtime == MAXNEGTIME) ? 0 : -1));
  645. }
  646.  
  647.  
  648. /*
  649.  * add_metas    - add symbols for $*, $< and $@
  650.  */
  651. add_metas(basename, preqname, targname)
  652. char   *basename;
  653. char   *preqname;
  654. char   *targname;
  655. {
  656.     add_symbol("*", basename);
  657.     add_symbol("<", preqname);
  658.     add_symbol("@", targname);
  659. }
  660.  
  661.  
  662. /*
  663.  * touch_file    - set the MODIFICATION time of the file to NOW
  664.  */
  665. touch_file(targname)
  666. char   *targname;
  667. {
  668.     REGISTER int handle;
  669. #ifndef    MSDOS
  670.     time_t    timep[2];
  671.  
  672.     time(&timep[0]);
  673.     timep[1] = timep[0];
  674.     handle = utime(targname, timep);
  675. #else
  676.     handle = utime(targname, NULL);
  677. #endif
  678.     fputs("*** touching : ", stdout);
  679.     puts(targname);
  680.  
  681.     if (handle == 0)
  682.         return;
  683.  
  684.     /* create the file, if it did not exist */
  685.     if (errno == ENOENT)
  686.     {
  687.         handle = open(targname, O_CREAT | O_TRUNC, S_IWRITE);
  688.         if (handle != -1)
  689.         {
  690.             close(handle);
  691.             return;
  692.         }
  693.     }
  694.  
  695.     perror("make");
  696.     exit (1);
  697. }
  698.  
  699.  
  700. /*
  701.  * build    - process the shell commands
  702.  */
  703. build(shellp)
  704. REGISTER shellptr *shellp;
  705. {
  706. REGISTER char **argv;    /* argified version of scmd */
  707. int        runst = 0;        /* exec return status */
  708. char   *scmd;            /* command with symbols broken out */
  709. char   *tcmd;            /* copy of scmd for `tokenize' */
  710. char   *mk;                /* builtin make macro */
  711. char  **shcp;            /* pointer to shell command list */
  712. symptr    symp;            /* points to macro definition */
  713. #ifndef MSDOS
  714. char    *format;        /* for displaying error msg's */
  715. #else
  716. int        resp_file;        /* response file possible */
  717. char    *cwd;            /* current work directory */
  718. FILE    *fd;            /* temporary file (if needed) */
  719. char    *fname, *comma;    /* temporary name pointers (if needed) */
  720. int        i;
  721. #endif
  722.  
  723.     symp = get_symbol("MAKE");
  724.     mk = symp->svalue;
  725.  
  726. #ifdef MSDOS
  727.     cwd = getcwd(NULL, 1);    /* where are we ? */
  728. #endif
  729.  
  730.     for (;*shellp != NULL && runst == 0;
  731.          tfree(scmd), tfree(tcmd), tfree(argv), ++shellp)
  732.     {
  733.         /* breakout runtime symbols (e.g. $* $@ $<) */
  734.         scmd = breakout((*shellp)->scmd);
  735.  
  736.         if (!(*shellp)->s_silent && !silent)
  737.             puts(scmd);
  738.  
  739.         /* make a copy because tokenize litters '\0's */
  740.         tcmd = tstrcpy(scmd);
  741.         argv = tokenize(tcmd);
  742.  
  743. #ifdef    MSDOS
  744.         /* check for .RESPONSE command */
  745.         shcp = resp_cmds;
  746.  
  747.         for (resp_file = 0; !resp_file && *shcp; ++shcp)
  748.             resp_file = equal(*shcp, argv[0]);
  749.  
  750.         if ( !noexec && resp_file && strlen(scmd) > 128 )
  751.             {                /* Create temporary response file */
  752.             if ( (fname = tempnam("\TMP", "MAK")) )
  753.                 {
  754.                 fd = fopen(fname, "w");
  755.                 
  756.                 for ( i = 1; argv[i]; i++ )
  757.                     {
  758.                     for ( comma = argv[i];
  759.                       *comma && (comma = strchr(comma, ',')); argv[i] = comma )
  760.                         {
  761.                         *comma++ = '\0';
  762.                         fprintf(fd, "%s,\n", argv[i]);
  763.                         }
  764.                     fprintf(fd, "%s +\n", argv[i]);
  765.                     }
  766.                 fputc(';', fd);
  767.                 fputc('\n', fd);
  768.                 fclose(fd);
  769.                 argv[1] = tstrcat("@", fname);
  770.                 argv[2] = NULL;
  771.                 }
  772.             }
  773.         else
  774.             resp_file = 0;
  775. #endif
  776.  
  777.         if ( equal(argv[0], mk) )
  778.             {
  779.             /* call ourselves recursively */
  780.             new_make(argv);
  781.             continue;
  782.             }
  783.  
  784.         if (noexec)
  785.             continue;
  786.  
  787.         /* any indirection MUST be handled by the shell */
  788.         if (!(*shellp)->s_shell && strpbrk(scmd, "<|>"))
  789.             (*shellp)->s_shell = 1;
  790. #ifdef    MSDOS
  791.         /* likewise, check for COMMAND.COM builtin commands */
  792.         
  793.         for (shcp = shell_cmds; !(*shellp)->s_shell && *shcp; ++shcp)
  794.             (*shellp)->s_shell = equal(*shcp, argv[0]);
  795. #endif
  796.         /* run without COMMAND.COM if possible, 'cause it uses RAM */
  797.         if ( forceshell || (*shellp)->s_shell )
  798.             runst = system(scmd);
  799.         else
  800.             runst = spawnvp(P_WAIT, argv[0], argv); 
  801.  
  802. #ifdef MSDOS
  803.         chdir(cwd);                    /* back to work directory */
  804.  
  805.         if ( resp_file )            /* free temporary file argument */
  806.             {
  807.             remove(fname);
  808.             tfree(argv[2]);
  809.             free(fname);
  810.             }
  811. #endif
  812.  
  813.         if (runst == 0)
  814.             continue;
  815.  
  816.         /* uh-oh, an error */
  817.         if (runst == -1)
  818.             perror("make");
  819. #ifdef    MSDOS
  820.         itoa(runst, scmd, 10);
  821.         
  822.         if ( !single_ign && (ignore || (*shellp)->s_ignore) )
  823.             strcat(scmd, " (ignored)");
  824.         else if ( single_ign && !(ignore || (*shellp)->s_ignore) )
  825.             strcat(scmd, " (target aborted)");
  826.         terror(0, tstrcat("\n\n*** Error code ",scmd));
  827.         putchar('\n');
  828. #else
  829.         if ( !single_ign && (ignore || (*shellp)->s_ignore) )
  830.             format = "(ignored)");
  831.         else if ( single_ign && !(ignore || (*shellp)->s_ignore) )
  832.             format = "(target aborted)");
  833.         else
  834.             format = "";
  835.         
  836.         sprintf(scmd, "\n\n*** Error code %d %s\n", runst, format);
  837.         terror(0, scmd);
  838. #endif
  839.         if (!single_ign && !ignore && !(*shellp)->s_ignore)
  840.             exit(1);
  841.         else if ( !single_ign )
  842.             runst = 0;
  843.     }
  844.  
  845. #ifdef MSDOS
  846.     free(cwd);                    /* free directory path space */
  847. #endif
  848. }
  849.  
  850.  
  851. /*
  852.  * new_make    - save current environment
  853.  *        - call make() recursively (actually main())
  854.  *        - clean up new environment
  855.  *        - restore environment
  856.  */
  857. new_make(argv)
  858. char  **argv;
  859. {
  860.     targptr    thead, tnext;
  861.     fileptr    fhead, fnext;
  862.     symptr    shead, snext;
  863.     shellptr shhead, shnext;
  864.     char  **ttlist;
  865.     long    tnow;
  866.     int    i;
  867.  
  868.     /* save all the globals */
  869.     thead = target_list;
  870.     fhead = file_list;
  871.     shead = symbol_list;
  872.     shhead = shell_list;
  873.     ttlist = tlist;
  874.     tnow = now;
  875.  
  876.     /* count the arguments */
  877.     for (i = 0; argv[i]; ++i);
  878.  
  879.     /* call ourselves recursively; this inherits flags */
  880.     main(i, argv);
  881.  
  882.     /* we're back, so gotta clean up and dispose of a few things */
  883.     while (target_list)
  884.     {
  885.         tnext = target_list->tnext;
  886.         if (target_list->tpreq)
  887.             tfree(target_list->tpreq);
  888.         if (target_list->tshell)
  889.             tfree(target_list->tshell);
  890.         tfree(target_list);
  891.         target_list = tnext;
  892.     }
  893.  
  894.     while (file_list)
  895.     {
  896.         fnext = file_list->fnext;
  897.         tfree(file_list->fname);
  898.         tfree(file_list);
  899.         file_list = fnext;
  900.     }
  901.  
  902.     while (symbol_list)
  903.     {
  904.         snext = symbol_list->snext;
  905.         tfree(symbol_list->sname);
  906.         tfree(symbol_list->svalue);
  907.         tfree(symbol_list);
  908.         symbol_list = snext;
  909.     }
  910.  
  911.     while (shell_list)
  912.     {
  913.         shnext = shell_list->slink;
  914.         tfree(shell_list->scmd);
  915.         tfree(shell_list);
  916.         shell_list = shnext;
  917.     }
  918.  
  919.     /* restore our original globals */
  920.     target_list = thead;
  921.     file_list = fhead;
  922.     symbol_list = shead;
  923.     shell_list = shhead;
  924.     tlist = ttlist;
  925.     now = tnow;
  926. }
  927.  
  928.  
  929. usage()
  930. {
  931. char    **us_str;
  932. int        i;
  933.  
  934.     for ( i = 1, us_str = usage_str; *us_str; puts(*us_str++), i++ )
  935.         if ( !(i % 24) )
  936.             {
  937.             printf("Press key to continue...");
  938.             getchar();
  939.             }
  940.  
  941.     exit(1);
  942. }
  943.  
  944.  
  945. display_prereq(targname, targtime, preqname, preqtime)
  946. char   *targname;
  947. long    targtime;
  948. char   *preqname;
  949. long    preqtime;
  950. {
  951.     printf("%s (%08lx) %s than %s (%08lx)\n",
  952.         targname, targtime, 
  953.         (targtime < preqtime) ? "older" : "newer",
  954.         preqname, preqtime);
  955. }
  956.  
  957.  
  958. long file_time(fname)
  959. char   *fname;
  960. {
  961. REGISTER int handle;
  962. #ifdef    MSDOS
  963. /*
  964. union
  965. {
  966.     long    ltime;
  967.     struct
  968.     {
  969.         unsigned time;
  970.         unsigned date;
  971.     }    s;
  972. }    ftime;
  973. */
  974. struct stat sbuf;
  975. #else
  976. struct stat sbuf;
  977. #endif
  978.  
  979.     handle = open(fname, O_RDONLY);
  980.     if (handle == -1)
  981.         return (MAXNEGTIME);
  982.  
  983. #ifdef    MSDOS
  984. /*
  985.     if (_dos_getftime(handle, &ftime.s.date, &ftime.s.time))
  986.         ftime.ltime = MAXNEGTIME;
  987. */
  988.     fstat(handle, &sbuf);
  989. #else
  990.     fstat(handle, &sbuf);
  991. #endif
  992.     close(handle);
  993.  
  994. #ifdef    MSDOS
  995. /*
  996.     return (ftime.ltime);
  997. */
  998.     return (sbuf.st_mtime);
  999. #else
  1000.     return (sbuf.st_mtime);
  1001. #endif
  1002. }
  1003.  
  1004.  
  1005. /*
  1006.  * curr_time    - return current time in same format as file_time()
  1007.  *                - used with the `-n' option
  1008.  */
  1009. long
  1010. curr_time()
  1011. {
  1012. #ifndef    MSDOS
  1013.     return (time(NULL));
  1014. #else
  1015.     struct dosdate_t date;
  1016.     struct dostime_t time;
  1017.  
  1018.     _dos_getdate(&date);
  1019.     _dos_gettime(&time);
  1020.  
  1021.     return ((((long) (date.year-1980)<<9 | date.month<<5 | date.day) << 15)
  1022.            | (time.hour << 11 | time.minute << 5 | time.second >> 1) >> 1);
  1023. #endif
  1024. }
  1025.  
  1026. #ifndef    MSDOS
  1027. int
  1028. spawnvp(mode, path, args)
  1029. int    mode;
  1030. char   *path;
  1031. char  **args;
  1032. {
  1033.     int    pid = 0;
  1034.     union wait waitword;
  1035.  
  1036.     if (mode != P_OVERLAY)
  1037.         pid = fork();
  1038.  
  1039.     if (pid == 0)
  1040.         execvp(path, args);
  1041.  
  1042.     wait(&waitword);
  1043.     return (waitword.w_retcode);
  1044. }
  1045. #endif
  1046.