home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / X / mit / config / imake.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-07-25  |  18.9 KB  |  827 lines

  1. /* $XConsortium: imake.c,v 1.65 91/07/25 17:50:17 rws Exp $ */
  2.  
  3. /*****************************************************************************\
  4.  *                                                                           *
  5.  *                                Porting Note                               *
  6.  *                                                                           *
  7.  * Add the value of BOOTSTRAPCFLAGS to the cpp_argv table so that it will be *
  8.  * passed to the template file.                                              *
  9.  *                                                                           *
  10. \*****************************************************************************/
  11.  
  12. /*
  13.  * 
  14.  * Copyright 1985, 1986, 1987 by the Massachusetts Institute of Technology
  15.  * 
  16.  * Permission to use, copy, modify, and distribute this
  17.  * software and its documentation for any purpose and without
  18.  * fee is hereby granted, provided that the above copyright
  19.  * notice appear in all copies and that both that copyright
  20.  * notice and this permission notice appear in supporting
  21.  * documentation, and that the name of M.I.T. not be used in
  22.  * advertising or publicity pertaining to distribution of the
  23.  * software without specific, written prior permission.
  24.  * M.I.T. makes no representations about the suitability of
  25.  * this software for any purpose.  It is provided "as is"
  26.  * without express or implied warranty.
  27.  * 
  28.  * Original Author:
  29.  *    Todd Brunhoff
  30.  *    Tektronix, inc.
  31.  *    While a guest engineer at Project Athena, MIT
  32.  *
  33.  * imake: the include-make program.
  34.  *
  35.  * Usage: imake [-Idir] [-Ddefine] [-T] [-f imakefile ] [-s] [-e] [-v] [make flags]
  36.  *
  37.  * Imake takes a template makefile (Imake.tmpl) and runs cpp on it
  38.  * producing a temporary makefile in /tmp.  It then runs make on
  39.  * this pre-processed makefile.
  40.  * Options:
  41.  *        -D    define.  Same as cpp -D argument.
  42.  *        -I    Include directory.  Same as cpp -I argument.
  43.  *        -T    template.  Designate a template other
  44.  *             than Imake.tmpl
  45.  *        -s[F]    show.  Show the produced makefile on the standard
  46.  *            output.  Make is not run is this case.  If a file
  47.  *            argument is provided, the output is placed there.
  48.  *              -e[F]   execute instead of show; optionally name Makefile F
  49.  *        -v    verbose.  Show the make command line executed.
  50.  *
  51.  * Environment variables:
  52.  *        
  53.  *        IMAKEINCLUDE    Include directory to use in addition to "."
  54.  *        IMAKECPP    Cpp to use instead of /lib/cpp
  55.  *        IMAKEMAKE    make program to use other than what is
  56.  *                found by searching the $PATH variable.
  57.  * Other features:
  58.  *    imake reads the entire cpp output into memory and then scans it
  59.  *    for occurences of "@@".  If it encounters them, it replaces it with
  60.  *    a newline.  It also trims any trailing white space on output lines
  61.  *    (because make gets upset at them).  This helps when cpp expands
  62.  *    multi-line macros but you want them to appear on multiple lines.
  63.  *
  64.  *    The macros MAKEFILE and MAKE are provided as macros
  65.  *    to make.  MAKEFILE is set to imake's makefile (not the constructed,
  66.  *    preprocessed one) and MAKE is set to argv[0], i.e. the name of
  67.  *    the imake program.
  68.  *
  69.  * Theory of operation:
  70.  *   1. Determine the name of the imakefile from the command line (-f)
  71.  *    or from the content of the current directory (Imakefile or imakefile).
  72.  *    Call this <imakefile>.  This gets added to the arguments for
  73.  *    make as MAKEFILE=<imakefile>.
  74.  *   2. Determine the name of the template from the command line (-T)
  75.  *    or the default, Imake.tmpl.  Call this <template>
  76.  *   3. Start up cpp an provide it with three lines of input:
  77.  *        #define IMAKE_TEMPLATE        " <template> "
  78.  *        #define INCLUDE_IMAKEFILE    < <imakefile> >
  79.  *        #include IMAKE_TEMPLATE
  80.  *    Note that the define for INCLUDE_IMAKEFILE is intended for
  81.  *    use in the template file.  This implies that the imake is
  82.  *    useless unless the template file contains at least the line
  83.  *        #include INCLUDE_IMAKEFILE
  84.  *   4. Gather the output from cpp, and clean it up, expanding @@ to
  85.  *    newlines, stripping trailing white space, cpp control lines,
  86.  *    and extra blank lines.  This cleaned output is placed in a
  87.  *    temporary file.  Call this <makefile>.
  88.  *   5. Start up make specifying <makefile> as its input.
  89.  *
  90.  * The design of the template makefile should therefore be:
  91.  *    <set global macros like CFLAGS, etc.>
  92.  *    <include machine dependent additions>
  93.  *    #include INCLUDE_IMAKEFILE
  94.  *    <add any global targets like 'clean' and long dependencies>
  95.  */
  96. #include <stdio.h>
  97. #if (defined(SVR4) || defined(_IBMR2) || defined(SYSV386)) && __STDC__
  98. FILE * fdopen();
  99. #endif
  100. #include <ctype.h>
  101. #include "Xosdefs.h"
  102. #ifndef X_NOT_POSIX
  103. #define _POSIX_SOURCE
  104. #endif
  105. #include <sys/types.h>
  106. #include <fcntl.h>
  107. #ifdef X_NOT_POSIX
  108. #include <sys/file.h>
  109. #else
  110. #include <unistd.h>
  111. #endif
  112. #if defined(X_NOT_POSIX) || defined(_POSIX_SOURCE)
  113. #include <signal.h>
  114. #else
  115. #define _POSIX_SOURCE
  116. #include <signal.h>
  117. #undef _POSIX_SOURCE
  118. #endif
  119. #include <sys/stat.h>
  120. #ifndef X_NOT_POSIX
  121. #ifdef _POSIX_SOURCE
  122. #include <sys/wait.h>
  123. #else
  124. #define _POSIX_SOURCE
  125. #include <sys/wait.h>
  126. #undef _POSIX_SOURCE
  127. #endif
  128. #define waitCode(w)    WEXITSTATUS(w)
  129. #define waitSig(w)    WTERMSIG(w)
  130. typedef int        waitType;
  131. #else /* X_NOT_POSIX */
  132. #ifdef SYSV
  133. #define waitCode(w)    (((w) >> 8) & 0x7f)
  134. #define waitSig(w)    ((w) & 0xff)
  135. typedef int        waitType;
  136. #else /* SYSV */
  137. #include <sys/wait.h>
  138. #define waitCode(w)    ((w).w_T.w_Retcode)
  139. #define waitSig(w)    ((w).w_T.w_Termsig)
  140. typedef union wait    waitType;
  141. #endif
  142. #ifndef WIFSIGNALED
  143. #define WIFSIGNALED(w) waitSig(w)
  144. #endif
  145. #ifndef WIFEXITED
  146. #define WIFEXITED(w) waitCode(w)
  147. #endif
  148. #endif /* X_NOT_POSIX */
  149. #ifndef X_NOT_STDC_ENV
  150. #include <stdlib.h>
  151. #else
  152. char *malloc(), *realloc();
  153. void exit();
  154. #endif
  155. #if defined(macII) && !defined(__STDC__)  /* stdlib.h fails to define these */
  156. char *malloc(), *realloc();
  157. #endif /* macII */
  158. #ifdef X_NOT_STDC_ENV
  159. extern char    *getenv();
  160. #endif
  161. #include <errno.h>
  162. extern int    errno;
  163. #include "imakemdep.h"
  164.  
  165.  
  166. #define    TRUE        1
  167. #define    FALSE        0
  168.  
  169. #ifdef FIXUP_CPP_WHITESPACE
  170. int    InRule = FALSE;
  171. #endif
  172.  
  173. /*
  174.  * Some versions of cpp reduce all tabs in macro expansion to a single
  175.  * space.  In addition, the escaped newline may be replaced with a
  176.  * space instead of being deleted.  Blech.
  177.  */
  178. #ifndef FIXUP_CPP_WHITESPACE
  179. #define KludgeOutputLine(arg)
  180. #define KludgeResetRule()
  181. #endif
  182.  
  183. typedef    unsigned char    boolean;
  184.  
  185. #ifndef DEFAULT_CPP
  186. #ifdef USE_CC_E
  187. #define DEFAULT_CPP "/bin/cc"
  188. #else
  189. #ifdef CPP_PROGRAM
  190. #define DEFAULT_CPP CPP_PROGRAM
  191. #else
  192. #define DEFAULT_CPP "/lib/cpp"
  193. #endif
  194. #endif
  195. #endif
  196.  
  197. char *cpp = DEFAULT_CPP;
  198.  
  199. char    *tmpMakefile    = "/tmp/Imf.XXXXXX";
  200. char    *tmpImakefile    = "/tmp/IIf.XXXXXX";
  201. char    *make_argv[ ARGUMENTS ] = { "make" };
  202.  
  203. int    make_argindex;
  204. int    cpp_argindex;
  205. char    *make = NULL;
  206. char    *Imakefile = NULL;
  207. char    *Makefile = "Makefile";
  208. char    *Template = "Imake.tmpl";
  209. char    *program;
  210. char    *FindImakefile();
  211. char    *ReadLine();
  212. char    *CleanCppInput();
  213. char    *Strdup();
  214. char    *Emalloc();
  215.  
  216. boolean    verbose = FALSE;
  217. boolean    show = TRUE;
  218.  
  219. main(argc, argv)
  220.     int    argc;
  221.     char    **argv;
  222. {
  223.     FILE    *tmpfd;
  224.     char    makeMacro[ BUFSIZ ];
  225.     char    makefileMacro[ BUFSIZ ];
  226.  
  227.     program = argv[0];
  228.     init();
  229.     SetOpts(argc, argv);
  230. #ifdef USE_CC_E
  231.     AddCppArg("-");
  232. #endif
  233.  
  234.     Imakefile = FindImakefile(Imakefile);
  235.     if (Makefile)
  236.         tmpMakefile = Makefile;
  237.     else {
  238.         tmpMakefile = Strdup(tmpMakefile);
  239.         (void) mktemp(tmpMakefile);
  240.     }
  241.     AddMakeArg("-f");
  242.     AddMakeArg( tmpMakefile );
  243.     sprintf(makeMacro, "MAKE=%s", program);
  244.     AddMakeArg( makeMacro );
  245.     sprintf(makefileMacro, "MAKEFILE=%s", Imakefile);
  246.     AddMakeArg( makefileMacro );
  247.  
  248.     if ((tmpfd = fopen(tmpMakefile, "w+")) == NULL)
  249.         LogFatal("Cannot create temporary file %s.", tmpMakefile);
  250.  
  251.     cppit(Imakefile, Template, tmpfd, tmpMakefile);
  252.  
  253.     if (show) {
  254.         if (Makefile == NULL)
  255.             showit(tmpfd);
  256.     } else
  257.         makeit();
  258.     wrapup();
  259.     exit(0);
  260. }
  261.  
  262. showit(fd)
  263.     FILE    *fd;
  264. {
  265.     char    buf[ BUFSIZ ];
  266.     int    red;
  267.  
  268.     fseek(fd, 0, 0);
  269.     while ((red = fread(buf, 1, BUFSIZ, fd)) > 0)
  270.         fwrite(buf, red, 1, stdout);
  271.     if (red < 0)
  272.         LogFatal("Cannot write stdout.", "");
  273. }
  274.  
  275. wrapup()
  276. {
  277.     if (tmpMakefile != Makefile)
  278.         unlink(tmpMakefile);
  279.     unlink(tmpImakefile);
  280. }
  281.  
  282. #ifdef SIGNALRETURNSINT
  283. int
  284. #else
  285. void
  286. #endif
  287. catch(sig)
  288.     int    sig;
  289. {
  290.     errno = 0;
  291.     LogFatalI("Signal %d.", sig);
  292. }
  293.  
  294. /*
  295.  * Initialize some variables.
  296.  */
  297. init()
  298. {
  299.     char    *p;
  300.  
  301.     make_argindex=0;
  302.     while (make_argv[ make_argindex ] != NULL)
  303.         make_argindex++;
  304.     cpp_argindex = 0;
  305.     while (cpp_argv[ cpp_argindex ] != NULL)
  306.         cpp_argindex++;
  307.  
  308.     /*
  309.      * See if the standard include directory is different than
  310.      * the default.  Or if cpp is not the default.  Or if the make
  311.      * found by the PATH variable is not the default.
  312.      */
  313.     if (p = getenv("IMAKEINCLUDE")) {
  314.         if (*p != '-' || *(p+1) != 'I')
  315.             LogFatal("Environment var IMAKEINCLUDE %s\n",
  316.                 "must begin with -I");
  317.         AddCppArg(p);
  318.         for (; *p; p++)
  319.             if (*p == ' ') {
  320.                 *p++ = '\0';
  321.                 AddCppArg(p);
  322.             }
  323.     }
  324.     if (p = getenv("IMAKECPP"))
  325.         cpp = p;
  326.     if (p = getenv("IMAKEMAKE"))
  327.         make = p;
  328.  
  329.     if (signal(SIGINT, SIG_IGN) != SIG_IGN)
  330.         signal(SIGINT, catch);
  331. }
  332.  
  333. AddMakeArg(arg)
  334.     char    *arg;
  335. {
  336.     errno = 0;
  337.     if (make_argindex >= ARGUMENTS-1)
  338.         LogFatal("Out of internal storage.", "");
  339.     make_argv[ make_argindex++ ] = arg;
  340.     make_argv[ make_argindex ] = NULL;
  341. }
  342.  
  343. AddCppArg(arg)
  344.     char    *arg;
  345. {
  346.     errno = 0;
  347.     if (cpp_argindex >= ARGUMENTS-1)
  348.         LogFatal("Out of internal storage.", "");
  349.     cpp_argv[ cpp_argindex++ ] = arg;
  350.     cpp_argv[ cpp_argindex ] = NULL;
  351. }
  352.  
  353. SetOpts(argc, argv)
  354.     int    argc;
  355.     char    **argv;
  356. {
  357.     errno = 0;
  358.     /*
  359.      * Now gather the arguments for make
  360.      */
  361.     for(argc--, argv++; argc; argc--, argv++) {
  362.         /*
  363.          * We intercept these flags.
  364.          */
  365.         if (argv[0][0] == '-') {
  366.         if (argv[0][1] == 'D') {
  367.             AddCppArg(argv[0]);
  368.         } else if (argv[0][1] == 'I') {
  369.             AddCppArg(argv[0]);
  370.         } else if (argv[0][1] == 'f') {
  371.             if (argv[0][2])
  372.             Imakefile = argv[0]+2;
  373.             else {
  374.             argc--, argv++;
  375.             if (! argc)
  376.                 LogFatal("No description arg after -f flag\n", "");
  377.             Imakefile = argv[0];
  378.             }
  379.         } else if (argv[0][1] == 's') {
  380.             if (argv[0][2])
  381.             Makefile = ((argv[0][2] == '-') && !argv[0][3]) ?
  382.                 NULL : argv[0]+2;
  383.             else {
  384.             argc--, argv++;
  385.             if (!argc)
  386.                 LogFatal("No description arg after -s flag\n", "");
  387.             Makefile = ((argv[0][0] == '-') && !argv[0][1]) ?
  388.                 NULL : argv[0];
  389.             }
  390.             show = TRUE;
  391.         } else if (argv[0][1] == 'e') {
  392.            Makefile = (argv[0][2] ? argv[0]+2 : NULL);
  393.            show = FALSE;
  394.         } else if (argv[0][1] == 'T') {
  395.             if (argv[0][2])
  396.             Template = argv[0]+2;
  397.             else {
  398.             argc--, argv++;
  399.             if (! argc)
  400.                 LogFatal("No description arg after -T flag\n", "");
  401.             Template = argv[0];
  402.             }
  403.         } else if (argv[0][1] == 'v') {
  404.             verbose = TRUE;
  405.         } else
  406.             AddMakeArg(argv[0]);
  407.         } else
  408.         AddMakeArg(argv[0]);
  409.     }
  410. }
  411.  
  412. char *FindImakefile(Imakefile)
  413.     char    *Imakefile;
  414. {
  415.     int    fd;
  416.  
  417.     if (Imakefile) {
  418.         if ((fd = open(Imakefile, O_RDONLY)) < 0)
  419.             LogFatal("Cannot open %s.", Imakefile);
  420.     } else {
  421.         if ((fd = open("Imakefile", O_RDONLY)) < 0)
  422.             if ((fd = open("imakefile", O_RDONLY)) < 0)
  423.                 LogFatal("No description file.", "");
  424.             else
  425.                 Imakefile = "imakefile";
  426.         else
  427.             Imakefile = "Imakefile";
  428.     }
  429.     close (fd);
  430.     return(Imakefile);
  431. }
  432.  
  433. LogFatalI(s, i)
  434.     char *s;
  435.     int i;
  436. {
  437.     /*NOSTRICT*/
  438.     LogFatal(s, (char *)i);
  439. }
  440.  
  441. LogFatal(x0,x1)
  442.     char *x0, *x1;
  443. {
  444.     extern char    *sys_errlist[];
  445.     static boolean    entered = FALSE;
  446.  
  447.     if (entered)
  448.         return;
  449.     entered = TRUE;
  450.  
  451.     fprintf(stderr, "%s: ", program);
  452.     if (errno)
  453.         fprintf(stderr, "%s: ", sys_errlist[ errno ]);
  454.     fprintf(stderr, x0,x1);
  455.     fprintf(stderr, "  Stop.\n");
  456.     wrapup();
  457.     exit(1);
  458. }
  459.  
  460. showargs(argv)
  461.     char    **argv;
  462. {
  463.     for (; *argv; argv++)
  464.         fprintf(stderr, "%s ", *argv);
  465.     fprintf(stderr, "\n");
  466. }
  467.  
  468. cppit(Imakefile, template, outfd, outfname)
  469.     char    *Imakefile;
  470.     char    *template;
  471.     FILE    *outfd;
  472.     char    *outfname;
  473. {
  474.     FILE    *pipeFile;
  475.     int    pid, pipefd[2];
  476.     waitType    status;
  477.     char    *cleanedImakefile;
  478.  
  479.     /*
  480.      * Get a pipe.
  481.      */
  482.     if (pipe(pipefd) < 0)
  483.         LogFatal("Cannot make a pipe.", "");
  484.  
  485.     /*
  486.      * Fork and exec cpp
  487.      */
  488.     pid = fork();
  489.     if (pid < 0)
  490.         LogFatal("Cannot fork.", "");
  491.     if (pid) {    /* parent */
  492.         close(pipefd[0]);
  493.         cleanedImakefile = CleanCppInput(Imakefile);
  494.         if ((pipeFile = fdopen(pipefd[1], "w")) == NULL)
  495.             LogFatalI("Cannot fdopen fd %d for output.", pipefd[1]);
  496.         fprintf(pipeFile, "#define IMAKE_TEMPLATE\t\"%s\"\n",
  497.             template);
  498.         fprintf(pipeFile, "#define INCLUDE_IMAKEFILE\t<%s>\n",
  499.             cleanedImakefile);
  500.         fprintf(pipeFile, "#include IMAKE_TEMPLATE\n");
  501.         fclose(pipeFile);
  502.         while (wait(&status) > 0) {
  503.             errno = 0;
  504.             if (WIFSIGNALED(status))
  505.                 LogFatalI("Signal %d.", waitSig(status));
  506.             if (WIFEXITED(status) && waitCode(status))
  507.                 LogFatalI("Exit code %d.", waitCode(status));
  508.         }
  509.         CleanCppOutput(outfd, outfname);
  510.     } else {    /* child... dup and exec cpp */
  511.         if (verbose)
  512.             showargs(cpp_argv);
  513.         dup2(pipefd[0], 0);
  514.         dup2(fileno(outfd), 1);
  515.         close(pipefd[1]);
  516.         execv(cpp, cpp_argv);
  517.         LogFatal("Cannot exec %s.", cpp);
  518.     }
  519. }
  520.  
  521. makeit()
  522. {
  523.     int    pid;
  524.     waitType    status;
  525.  
  526.     /*
  527.      * Fork and exec make
  528.      */
  529.     pid = fork();
  530.     if (pid < 0)
  531.         LogFatal("Cannot fork.", "");
  532.     if (pid) {    /* parent... simply wait */
  533.         while (wait(&status) > 0) {
  534.             errno = 0;
  535.             if (WIFSIGNALED(status))
  536.                 LogFatalI("Signal %d.", waitSig(status));
  537.             if (WIFEXITED(status) && waitCode(status))
  538.                 LogFatalI("Exit code %d.", waitCode(status));
  539.         }
  540.     } else {    /* child... dup and exec cpp */
  541.         if (verbose)
  542.             showargs(make_argv);
  543.         if (make)
  544.             execv(make, make_argv);
  545.         else
  546.             execvp("make", make_argv);
  547.         LogFatal("Cannot exec %s.", make);
  548.     }
  549. }
  550.  
  551. char *CleanCppInput(Imakefile)
  552.     char    *Imakefile;
  553. {
  554.     FILE    *outFile = NULL;
  555.     int    infd;
  556.     char    *buf,        /* buffer for file content */
  557.         *pbuf,        /* walking pointer to buf */
  558.         *punwritten,    /* pointer to unwritten portion of buf */
  559.         *cleanedImakefile = Imakefile,    /* return value */
  560.         *ptoken,    /* pointer to # token */
  561.         *pend,        /* pointer to end of # token */
  562.         savec;        /* temporary character holder */
  563.     struct stat    st;
  564.  
  565.     /*
  566.      * grab the entire file.
  567.      */
  568.     if ((infd = open(Imakefile, O_RDONLY)) < 0)
  569.         LogFatal("Cannot open %s for input.", Imakefile);
  570.     fstat(infd, &st);
  571.     buf = Emalloc(st.st_size+1);
  572.     if (read(infd, buf, st.st_size) != st.st_size)
  573.         LogFatal("Cannot read all of %s:", Imakefile);
  574.     close(infd);
  575.     buf[ st.st_size ] = '\0';
  576.  
  577.     punwritten = pbuf = buf;
  578.     while (*pbuf) {
  579.         /* pad make comments for cpp */
  580.         if (*pbuf == '#' && (pbuf == buf || pbuf[-1] == '\n')) {
  581.  
  582.         ptoken = pbuf+1;
  583.         while (*ptoken == ' ' || *ptoken == '\t')
  584.             ptoken++;
  585.         pend = ptoken;
  586.         while (*pend && *pend != ' ' && *pend != '\t' && *pend != '\n')
  587.             pend++;
  588.         savec = *pend;
  589.         *pend = '\0';
  590.         if (strcmp(ptoken, "include")
  591.          && strcmp(ptoken, "define")
  592.          && strcmp(ptoken, "undef")
  593.          && strcmp(ptoken, "ifdef")
  594.          && strcmp(ptoken, "ifndef")
  595.          && strcmp(ptoken, "else")
  596.          && strcmp(ptoken, "endif")
  597.          && strcmp(ptoken, "if")) {
  598.             if (outFile == NULL) {
  599.             tmpImakefile = Strdup(tmpImakefile);
  600.             (void) mktemp(tmpImakefile);
  601.             cleanedImakefile = tmpImakefile;
  602.             outFile = fopen(tmpImakefile, "w");
  603.             if (outFile == NULL)
  604.                 LogFatal("Cannot open %s for write.\n",
  605.                 tmpImakefile);
  606.             }
  607.             fwrite(punwritten, sizeof(char), pbuf-punwritten, outFile);
  608.             fputs("/**/", outFile);
  609.             punwritten = pbuf;
  610.         }
  611.         *pend = savec;
  612.         }
  613.         pbuf++;
  614.     }
  615.     if (outFile) {
  616.         fwrite(punwritten, sizeof(char), pbuf-punwritten, outFile);
  617.         fclose(outFile); /* also closes the pipe */
  618.     }
  619.  
  620.     return(cleanedImakefile);
  621. }
  622.  
  623. CleanCppOutput(tmpfd, tmpfname)
  624.     FILE    *tmpfd;
  625.     char    *tmpfname;
  626. {
  627.     char    *input;
  628.     int    blankline = 0;
  629.  
  630.     while(input = ReadLine(tmpfd, tmpfname)) {
  631.         if (isempty(input)) {
  632.             if (blankline++)
  633.                 continue;
  634.             KludgeResetRule();
  635.         } else {
  636.             blankline = 0;
  637.             KludgeOutputLine(&input);
  638.             fputs(input, tmpfd);
  639.         }
  640.         putc('\n', tmpfd);
  641.     }
  642.     fflush(tmpfd);
  643. #ifdef NFS_STDOUT_BUG
  644.     /*
  645.      * On some systems, NFS seems to leave a large number of nulls at
  646.      * the end of the file.  Ralph Swick says that this kludge makes the
  647.      * problem go away.
  648.      */
  649.     ftruncate (fileno(tmpfd), (off_t)ftell(tmpfd));
  650. #endif
  651. }
  652.  
  653. /*
  654.  * Determine of a line has nothing in it.  As a side effect, we trim white
  655.  * space from the end of the line.  Cpp magic cookies are also thrown away.
  656.  */
  657. isempty(line)
  658.     char    *line;
  659. {
  660.     char    *pend;
  661.  
  662.     /*
  663.      * Check for lines of the form
  664.      *    # n "...
  665.      * or
  666.      *    # line n "...
  667.      */
  668.     if (*line == '#') {
  669.         pend = line+1;
  670.         if (*pend == ' ')
  671.             pend++;
  672.         if (strncmp(pend, "line ", 5) == 0)
  673.             pend += 5;
  674.         if (isdigit(*pend)) {
  675.             while (isdigit(*pend))
  676.                 pend++;
  677.             if (*pend++ == ' ' && *pend == '"')
  678.                 return(TRUE);
  679.         }
  680.     }
  681.  
  682.     /*
  683.      * Find the end of the line and then walk back.
  684.      */
  685.     for (pend=line; *pend; pend++) ;
  686.  
  687.     pend--;
  688.     while (pend >= line && (*pend == ' ' || *pend == '\t'))
  689.         pend--;
  690.     *++pend = '\0';
  691.     return (*line == '\0');
  692. }
  693.  
  694. /*ARGSUSED*/
  695. char *ReadLine(tmpfd, tmpfname)
  696.     FILE    *tmpfd;
  697.     char    *tmpfname;
  698. {
  699.     static boolean    initialized = FALSE;
  700.     static char    *buf, *pline, *end;
  701.     char    *p1, *p2;
  702.  
  703.     if (! initialized) {
  704.         int    total_red;
  705.         struct stat    st;
  706.  
  707.         /*
  708.          * Slurp it all up.
  709.          */
  710.         fseek(tmpfd, 0, 0);
  711.         fstat(fileno(tmpfd), &st);
  712.         pline = buf = Emalloc(st.st_size+1);
  713.         total_red = read(fileno(tmpfd), buf, st.st_size);
  714.         if (total_red != st.st_size)
  715.             LogFatal("cannot read %s\n", tmpMakefile);
  716.         end = buf + st.st_size;
  717.         *end = '\0';
  718.         lseek(fileno(tmpfd), 0, 0);
  719. #ifdef SYSV
  720.         freopen(tmpfname, "w+", tmpfd);
  721. #else    /* !SYSV */
  722.         ftruncate(fileno(tmpfd), 0);
  723. #endif    /* !SYSV */
  724.         initialized = TRUE;
  725.         fprintf (tmpfd, "# Makefile generated by imake - do not edit!\n");
  726.         fprintf (tmpfd, "# %s\n",
  727.         "$XConsortium: imake.c,v 1.65 91/07/25 17:50:17 rws Exp $");
  728.  
  729. #ifdef FIXUP_CPP_WHITESPACE
  730.         {
  731.         static char *cpp_warning[] = {
  732. "#",
  733. "# The cpp used on this machine replaces all newlines and multiple tabs and",
  734. "# spaces in a macro expansion with a single space.  Imake tries to compensate",
  735. "# for this, but is not always successful.",
  736. "#",
  737. NULL };
  738.         char **cpp;
  739.  
  740.         for (cpp = cpp_warning; *cpp; cpp++) {
  741.             fprintf (tmpfd, "%s\n", *cpp);
  742.         }
  743.         }
  744. #endif /* FIXUP_CPP_WHITESPACE */
  745.     }
  746.  
  747.     for (p1 = pline; p1 < end; p1++) {
  748.         if (*p1 == '@' && *(p1+1) == '@') { /* soft EOL */
  749.             *p1++ = '\0';
  750.             p1++; /* skip over second @ */
  751.             break;
  752.         }
  753.         else if (*p1 == '\n') { /* real EOL */
  754.             *p1++ = '\0';
  755.             break;
  756.         }
  757.     }
  758.  
  759.     /*
  760.      * return NULL at the end of the file.
  761.      */
  762.     p2 = (pline == p1 ? NULL : pline);
  763.     pline = p1;
  764.     return(p2);
  765. }
  766.  
  767. writetmpfile(fd, buf, cnt)
  768.     FILE    *fd;
  769.     int    cnt;
  770.     char    *buf;
  771. {
  772.     errno = 0;
  773.     if (fwrite(buf, cnt, 1, fd) != 1)
  774.         LogFatal("Cannot write to %s.", tmpMakefile);
  775. }
  776.  
  777. char *Emalloc(size)
  778.     int    size;
  779. {
  780.     char    *p;
  781.  
  782.     if ((p = malloc(size)) == NULL)
  783.         LogFatalI("Cannot allocate %d bytes\n", size);
  784.     return(p);
  785. }
  786.  
  787. #ifdef FIXUP_CPP_WHITESPACE
  788. KludgeOutputLine(pline)
  789.     char    **pline;
  790. {
  791.     char    *p = *pline;
  792.  
  793.     switch (*p) {
  794.         case '#':    /*Comment - ignore*/
  795.         break;
  796.         case '\t':    /*Already tabbed - ignore it*/
  797.             break;
  798.         case ' ':    /*May need a tab*/
  799.         default:
  800.         for (; *p; p++) if (p[0] == ':' && 
  801.                     p > *pline && p[-1] != '\\') {
  802.             if (**pline == ' ')
  803.             (*pline)++;
  804.             InRule = TRUE;
  805.             break;
  806.         }
  807.         if (InRule && **pline == ' ')
  808.             **pline = '\t';
  809.         break;
  810.     }
  811. }
  812.  
  813. KludgeResetRule()
  814. {
  815.     InRule = FALSE;
  816. }
  817. #endif /* FIXUP_CPP_WHITESPACE */
  818.  
  819. char *Strdup(cp)
  820.     register char *cp;
  821. {
  822.     register char *new = Emalloc(strlen(cp) + 1);
  823.  
  824.     strcpy(new, cp);
  825.     return new;
  826. }
  827.