home *** CD-ROM | disk | FTP | other *** search
/ Amiga Elysian Archive / AmigaElysianArchive.iso / prog / c / cpp.lha / cpp7.c < prev    next >
C/C++ Source or Header  |  1991-08-05  |  31KB  |  1,017 lines

  1. /*
  2.  
  3.  
  4.  Copyright (C) 1990 Texas Instruments Incorporated.
  5.  
  6.  Permission is granted to any individual or institution to use, copy, modify,
  7.  and distribute this software, provided that this complete copyright and
  8.  permission notice is maintained, intact, in all copies and supporting
  9.  documentation.
  10.  
  11.  Texas Instruments Incorporated provides this software "as is" without
  12.  express or implied warranty.
  13.  
  14.  
  15.  *
  16.  *                              C P P 7 . C
  17.  *
  18.  *                         Process Internal/External defmacros
  19.  *
  20.  * Edit history
  21.  * 15-Mar-89    LGO     Initial design and implementation.
  22.  * 20-Oct-89    AFM     OS2, XENIX and AIX port.
  23.  * 19-Jan-90    DKM     MVS support
  24.  * 01-Sep-90    MJF     External macros support for OS/2 by using temp file
  25.  * 25-Jun-91    GPD     Remove #elif to make more portable.
  26.  *                      Added support for Interactive Unix.
  27.  *
  28.  * 29-Jul-91    PB      Adapted to AmigaDOS & SAS/C.
  29.  */
  30.  
  31. #include        <stdio.h>
  32. #include        <ctype.h>
  33. #include        "cppdef.h"
  34.  
  35. #if defined(vms)
  36. #include        <types.h>
  37. #else
  38. #if !defined(SYS_OSVS)
  39. #include        <sys/types.h>
  40. #endif
  41. #endif
  42.  
  43. #if defined(_AIX)
  44. #include        <sys/select.h>
  45. #endif
  46.  
  47. #include        "cpp.h"
  48.  
  49. #if defined(M_XENIX)
  50. #include        <string.h>
  51. #include        <sys/fcntl.h>
  52. #include        <sys/file.h>
  53. #else
  54. #if defined(DOS) || defined(MSDOS) || defined(AMIGA)
  55. #include        <string.h>
  56. #include        <fcntl.h>
  57. #else
  58. #if defined(SYS_OSVS)
  59. #include        <string.h>
  60. #include        <fcntl.h>
  61. #else
  62. #if defined(vms)
  63. #include        <file.h>
  64. #else
  65. #if defined(M_INTERACTIVE)
  66. #include        <unistd.h>
  67. #include        <sys/bsdtypes.h>
  68. #include        <sys/fcntl.h>
  69. #endif
  70. #include        <sys/file.h>
  71. #endif
  72. #endif
  73. #endif
  74. #endif
  75.  
  76. #if HOST == SYS_VMS
  77. #define F_OK            0      /* does file exist */
  78. #define X_OK            1      /* is file executable */
  79. #define W_OK            2      /* is file writable */
  80. #define R_OK            4      /* is file readable */
  81. #endif
  82.  
  83. int current_line;                       /* line at start of defmacro global */
  84. char current_file[256];                 /* file at start of defmacro global */
  85.  
  86. FILEINFO *MacInFile, *MacOutFile;
  87. char* MacOutEnd;
  88.  
  89. struct macroargs {
  90.   char* name;
  91.   char  expanding;              /* Non-zero when args are macro expanded */
  92.   char  recursive;              /* Non-zero when recursive */
  93.   char  delimiter;              /* Delimiter */
  94.   char  conditional;            /* Expand only if this char found after name */
  95.   internal_expander expander;   /* Macro expander or NULL if file */
  96.   char *program;                /* macro program pathname */
  97.   char *args[1];                /* arg list (vector of strings) */
  98. };
  99.  
  100. extern FILEINFO* getfile();
  101.  
  102. /* Getfile without side effects */
  103. FILEINFO*
  104. get_temp_file (bufsize, name)
  105.      int bufsize;
  106.      char* name;
  107. {
  108.   FILEINFO* file = getfile(bufsize, name);
  109.   infile = file->parent;
  110.   file->parent = NULL;
  111.   line = infile->line;
  112.   return(file);
  113. }
  114.  
  115. static FILEINFO*
  116. next_buffer(Buffer)
  117.    FILEINFO* Buffer;
  118. {
  119.   FILEINFO* new = get_temp_file(NBUFF, Buffer->filename);
  120.   new->parent = Buffer->parent; /* When new ends read from infile */
  121.   Buffer->parent = new;         /* When Buffer dries up, read from new */
  122.   *Buffer->bptr = EOS;
  123.   Buffer->bptr = Buffer->buffer;
  124.   return new;
  125. }
  126.  
  127. /* Helper function for the getchar macro used by internal defmacros */
  128. char
  129. NextMacInChar() {
  130.   char c;
  131.   do {
  132.     if (MacInFile->parent == NULL) {
  133.       return(EOF);                        /* End of file */
  134.     } else {
  135.       FILEINFO* new = MacInFile->parent;
  136.       free(MacInFile->filename);          /* Free name and      */
  137.       if (MacInFile->progname != NULL)    /* if a #line was seen, */
  138.         free(MacInFile->progname);        /* free it, too.      */
  139.       free(MacInFile->buffer);            /* Free buffer */
  140.       free((char *) MacInFile);           /* Free file space    */
  141.  
  142.       *new->bptr = EOS;
  143.       new->bptr = new->buffer;
  144.       c = *new->bptr++;
  145.       MacInFile = new;
  146.     }
  147.   }
  148.   while (c == EOS);
  149.   return (c);
  150. }
  151.  
  152. void
  153. NextMacOutBuffer()
  154. {
  155.   MacOutFile = next_buffer(MacOutFile);
  156.   MacOutEnd = MacOutFile->buffer + (NBUFF - 1);
  157. }
  158.  
  159. void
  160. NextMacOutString(s)
  161. char* s;
  162. {
  163.   int slen = strlen(s);
  164.   int blen;
  165.   while ((blen = (MacOutEnd - MacOutFile->bptr) - 1) < slen) {
  166.     if(blen>0) {
  167.       strncpy(MacOutFile->bptr, s, blen);
  168.       MacOutFile->bptr += blen;
  169.       s += blen;
  170.       slen -= blen;
  171.     }
  172.     NextMacOutBuffer();
  173.   }
  174.   strcpy(MacOutFile->bptr, s);
  175.   MacOutFile->bptr += slen;
  176. }
  177.  
  178. static char* new_buffer (obuf, obufend, file)
  179.      char*      obuf;                   /* Output Buffer */
  180.      char**     obufend;                /* End of Output Buffer */
  181.      FILEINFO   **file;                 /* Funny #include       */
  182. {
  183.   FILEINFO* old = *file;
  184.   FILEINFO* new = get_temp_file(NBUFF, old->filename);
  185.   *obuf = EOS;
  186.   /* NOTE: this code leaves file->bptr pointing at the END of the */
  187.   /*       buffer.  This is to keep double_copy from having to use */
  188.   /*       strlen to get the number of bytes in the buffer */
  189.   old->bptr = obuf;
  190.   old->parent = new;              /* When file dries up, read from new */
  191.   *obufend = new->buffer + (NBUFF - 1);
  192.   *file = new;
  193.   return new->bptr;
  194. }
  195. /*
  196.  * Copy from the input stream to a buffer, stopping at the first
  197.  * delim, but including everything with matching {} [] ()
  198.  * <> "" '' and comments found along the way.
  199.  */
  200. static FILEINFO*
  201. copy_body(file, delim, is_string, is_top_level, expand)
  202.      FILEINFO   *file;                  /* Funny #include       */
  203.      char delim;                        /* Char to stop on */
  204.      int is_string;                     /* True when inside a string */
  205.      int is_top_level;                  /* True when the outer most call */
  206.      int expand;                        /* if non-zero, macroexpand */
  207. {
  208.   register char* obuf;                  /* Current output pointer */
  209.   register char c;                      /* Current character */
  210.   char p;                               /* Previous character */
  211.   char* obufend;                        /* End of output buffer */
  212.   char newdelim;                        /* New delimiter to look for */
  213.   int new_is_string = FALSE;            /* New is_string flag */
  214.  
  215.   obuf = file->bptr;                    /* -> output buffer     */
  216.   obufend = file->buffer + (NBUFF - 1); /* Note its end         */
  217.  
  218.   *obuf = EOS;
  219.   for (p = EOS; (c = get()) != EOF_CHAR; p = c) {
  220.     if (obuf >= obufend)                /* End of buffer, get a new one */
  221.       obuf = new_buffer(obuf, &obufend, &file);
  222.     if(expand && !is_string && type[c] == LET) {    /* expand macros */
  223.       c = macroid(c);                   /* Grab the token       */
  224.       if (obuf >= obufend-strlen(tokenbuf)) /* End of buffer, get a new one */
  225.         obuf = new_buffer(obuf, &obufend, &file);
  226.       if(type[c] == LET) {
  227.         strcpy(obuf, tokenbuf);
  228.         obuf += strlen(tokenbuf);
  229.         continue;
  230.       }
  231.     }
  232.     *obuf++ = c;
  233.     if (c == delim)                     /* Quit when delimeter found */
  234.       if (delim != '/' || p == '*') {   /* Special test for end of comment */
  235.         *obuf = EOS;
  236.         file->bptr = obuf;
  237.         return(file);
  238.       }
  239.     if (!is_string) {
  240.       switch (c) {             /* Look for new delimeters when not is_string */
  241.       case DEF_MAGIC:
  242.       case TOK_SEP:
  243.       case COM_SEP:
  244.         obuf--; continue;                 /* Ignore magic characters */
  245.       case '{':  newdelim = '}'; break;
  246.       case '[':  newdelim = ']'; break;
  247.       case '(':  newdelim = ')'; break;
  248.       case '<':
  249.         if (delim != '>') continue;
  250.         newdelim = '>'; break;
  251.       case '\'':  newdelim = '\''; new_is_string = TRUE; break;
  252.       case '\"':  newdelim = '\"'; new_is_string = TRUE; break;
  253.       case '*':                           /* Start of C comment? */
  254.         if (p == '/') {
  255.           if (keepcomments) {
  256.             newdelim = '/';
  257.             new_is_string = TRUE;
  258.             break;
  259.           }                               /* Eat comment */
  260.           for (; (c = get()) != EOF_CHAR; p = c) {
  261.             if (c == '\n') {
  262.               if (obuf >= obufend)        /* End of buffer, get a new one */
  263.                 obuf = new_buffer(obuf, &obufend, &file);
  264.               *obuf++ = c;                /* Preserve newlines */
  265.             } else if (c == '/' && p == '*') break;
  266.           }
  267.           if (obuf+2 >= obufend)          /* End of buffer, get a new one */
  268.             obuf = new_buffer(obuf, &obufend, &file);
  269.           *obuf++ = '*';                  /* Terminate the comment */
  270.           *obuf++ = '/';
  271.         }
  272.         continue;
  273.  
  274.       case '/':                         /* Start of C++ comment? */
  275.         if (p == '/') {
  276.           if (keepcomments) {
  277.             newdelim = '\n';
  278.             new_is_string = TRUE;
  279.             break;
  280.           }                               /* Eat comment */
  281.           while ((c = get()) != EOF_CHAR && c != '\n');
  282.           if (obuf >= obufend)    /* End of buffer, get a new one */
  283.             obuf = new_buffer(obuf, &obufend, &file);
  284.           *obuf++ = c;            /* Preserve newlines */
  285.         }
  286.         continue;
  287.  
  288.       default:
  289.           continue;     /* Loop to next character, unless break above */
  290.       } /* end switch */
  291.       if (is_top_level && newdelim == delim) {
  292.         is_top_level = FALSE;
  293.         continue;               /* at top_level, with left side of delimiter */
  294.       }
  295.       file->bptr = obuf;
  296.       file = copy_body(file, newdelim, new_is_string, FALSE, expand);
  297.       if (file == NULL)
  298.         return(NULL);
  299.       obuf = file->bptr;                /* -> output buffer     */
  300.       obufend = file->buffer + (NBUFF - 1); /* Note its end             */
  301.       new_is_string = FALSE;
  302.       c = EOS; /* don't know what last character is anymore */
  303.     }  /* if !is_string */
  304.   } /* end while */
  305.   fprintf(stderr, "Searching for '%c'\n", delim); /* DEBUG */
  306.   infile = getfile(1, current_file);      /* Hack up infile */
  307.   infile->fp = stdin;                     /* to print correct file */
  308.   line = current_line;                    /* and line number */
  309.   cfatal("End of file during %s macro expansion.", file->filename);
  310.   return(NULL);
  311. } /* copy_body */
  312.  
  313. #if !(HOST == SYS_OS2 || HOST == SYS_VMS || HOST == SYS_MVS || HOST == SYS_AMIGA)
  314. /*
  315.  *  support for fork and pipe on unix machines only
  316.  */
  317.  
  318. /*
  319.  * Copy two files in parallel: from in1 to out2 and in2 to out2
  320.  * Returns when both copies are complete, closing all files.
  321.  * in1 and out2 will not block (i.e. they're files)
  322.  * out1 and in2 may be pipes and can block for long periods
  323.  */
  324. static void
  325. doublecopy (in1, out1, in2, out2)
  326.  
  327.   FILEINFO      *in1, *out2;
  328.   int           out1, in2;
  329. {
  330.   fd_set readfds;
  331.   fd_set writefds;
  332.   fd_set errorfds;
  333.   int n, width;
  334.   int is_first = TRUE;
  335.   char* outptr = in1->buffer;
  336.  
  337.   width = 1 + ((in2 > out1) ? in2 : out1); /* width = max(in2, out1); */
  338.  
  339.   do {
  340.     FD_ZERO(&readfds);
  341.     FD_ZERO(&writefds);
  342.     FD_ZERO(&errorfds);
  343.     if (in2  >= 0) FD_SET(in2, &readfds);
  344.     if (out1 >= 0) FD_SET(out1, &writefds);
  345.  
  346.     if (select(width, &readfds, &writefds, &errorfds, NULL) <= 0)
  347.       perror("Select Error");
  348.  
  349.     if (out1>=0 && FD_ISSET(out1, &errorfds))
  350.       perror("Select output Error");
  351.  
  352.     if (in2>=0 && FD_ISSET(in2, &errorfds))
  353.       perror("Select input Error");
  354.  
  355.     if (!(out1>=0 && FD_ISSET(out1, &writefds)) &&
  356.         !(in2>=0 && FD_ISSET(in2, &readfds))) {
  357.       perror("Select when nothing happened");
  358.     }
  359.  
  360.     if (out1>=0 && FD_ISSET(out1, &writefds)) {
  361.       if (in1 == NULL) {
  362.         close(out1);
  363.         out1 = -1;
  364.       } else {
  365.         int nbytes = in1->bptr - outptr;
  366.         int wbytes = write(out1, outptr, nbytes);
  367.         if (wbytes < 0)
  368.           perror("Error writing to pipe");
  369.         outptr += wbytes;
  370.         if (nbytes == wbytes) {
  371.           in1 = in1->parent;
  372.           if (in1 != NULL)
  373.             outptr = in1->buffer;
  374.         }
  375.       }
  376.     }
  377.  
  378.     if (in2>=0 && FD_ISSET(in2, &readfds)) {
  379.       if (is_first)
  380.         is_first = FALSE;
  381.       else {                            /* Chain in a new buffer */
  382.         FILEINFO* new = get_temp_file(NBUFF, out2->filename);
  383.         new->parent = out2->parent;     /* When new ends read from infile */
  384.         out2->parent = new;             /* When out2 dries up, read from new */
  385.         out2 = new;
  386.       }
  387.       n = read(in2, out2->buffer, NBUFF);
  388.       if (n <= 0) {
  389.         if(n<0)
  390.           perror("Error reading from pipe");
  391.         close(in2);
  392.         in2 = -1;
  393.       } else {
  394.         char* p = out2->buffer + n;
  395.         *p = EOS;
  396.       }
  397.     }
  398.   } while (in2>=0 || out1>=0);
  399. } /* end doublecopy */
  400.  
  401. /*
  402.  * Fork an external macro expander
  403.  */
  404. static void
  405. fork_macro (args, ifile, ofile)
  406.      struct macroargs* args;
  407.      FILEINFO *ifile, *ofile;
  408. {
  409.   int inchannel, outchannel;
  410.   int forkin, forkout;
  411.   int pid, status;
  412.   {                                     /* Create the pipes */
  413.     int sv[2];
  414.     pipe (sv);
  415.     forkin = sv[0];
  416.     outchannel = sv[1];
  417.     pipe (sv);
  418.     inchannel = sv[0];
  419.     forkout = sv[1];
  420.   }
  421.                                         /* Fork the macro process */
  422.   if ((pid = fork ()) == 0) {
  423.     /* child process.  Move pipes to stdin and stdout then exec */
  424.     close(0); dup2(forkin, 0);
  425.     close(1); dup2(forkout, 1);
  426.     close(forkin);
  427.     close(forkout);
  428.     close(inchannel);  /* Close files used only by the parent */
  429.     close(outchannel);
  430.     execv(args->program, args->args);
  431.     /* execv never returns */
  432.   }
  433.   close(forkin);  /* Close files used only by the child */
  434.   close(forkout);
  435.   if (pid < 0)
  436.     cfatal("Can't fork %s macro.", args->program);
  437.   /* Make output to the pipe non-blocking to avoid deadlocks */
  438.   fcntl(outchannel, F_SETFL, FNDELAY | fcntl(outchannel, F_GETFL, 0));
  439.   doublecopy (ifile, outchannel, inchannel, ofile);
  440.   wait(&status);
  441.   if(status != 0)
  442.     cerror("Error during %s macro expansion", args->program);
  443. } /* end fork_macro */
  444.  
  445. #else
  446. /*
  447.  * execute macro using temp files to redirect stdin and stdout
  448.  * temp file implementation used for DOS, VMS and MVS ports
  449.  * instead of fork/pipe implementation
  450.  */
  451. void run_macro (macname, args, in, out)
  452.     char* macname;
  453.     char* args[];
  454.     char *in, *out;
  455. {
  456.     int status;
  457.     int ifd, ofd;
  458. #if HOST != SYS_OS2 && HOST != SYS_MVS && HOST != SYS_AMIGA
  459.     /*
  460.      * no fork function on OS2 or MVS or AmigaOS
  461.      */
  462.     int pid;
  463.     if ((pid=fork()) == 0) {
  464.       /* child process */
  465.       ifd = open(in, O_RDONLY);
  466.       if (ifd < 0) {
  467.         perror("cpp exec macro error");
  468.         cerror("Cannot open external macro temp file \"%s\"", in);
  469.         return;
  470.       }
  471.       ofd = open(out, O_WRONLY | O_CREAT | O_TRUNC, 0644);
  472.       if (ofd < 0) {
  473.         perror("cpp exec macro error");
  474.         cerror("Cannot open external macro temp file \"%s\"", out);
  475.         return;
  476.       }
  477.       if (dup2(ifd, 0) < 0) {
  478.         perror("cpp exec macro error");
  479.         cerror("Cannot dup external macro temp file \"%s\"", in);
  480.         return;
  481.       }
  482.       if (dup2(ofd, 1) < 0) {
  483.         perror("cpp exec macro error");
  484.         cerror("Cannot dup external macro temp file \"%s\"", out);
  485.         return;
  486.       }
  487.       execvp(macname, args);
  488.       perror("cpp exec macro error");
  489.       cerror(stderr, "cpp: Cannot execute %s\n", macname);
  490.       close(ifd);
  491.       close(ofd);
  492.     }
  493.     if (pid < 0)
  494.       cfatal("Cannot execute %s\n", macname);
  495.     wait(&status);
  496. #else
  497.     /* for OS/2 and MVS and AmigaOS
  498.      * use system function to execute macro with command interpreter
  499.      */
  500.     char arglist[256];
  501.     char subarg[256];
  502.     char command[512];
  503.     char* c;
  504.     /* build arglist of macro command */
  505.     char** argp = &args[1];               /* skip macro name */
  506.     if (*argp != NULL) {
  507.       sprintf(arglist,"%s", *argp);
  508.       for (++argp; *argp !=NULL; argp++) {
  509.         strcpy(subarg,arglist);
  510.         sprintf(arglist,"%s %s",subarg, *argp);
  511.       }
  512.     } else strcpy(arglist,"");
  513.     for (c = macname; *c != '\n'; c++)
  514.       if (*c == '/') *c = '\\';
  515.     /* build complete command redirecting stdin and stdout to temp files */
  516. #ifdef AMIGA
  517.     sprintf(command,"%s %s < %s > %s",macname,arglist,in,out);
  518. #else /* !AMIGA */
  519.     sprintf(command, "%s < %s > %s %s", macname, in, out, arglist);
  520. #endif /* AMIGA */
  521.     /* execute command */
  522.     status = system(command);
  523. #endif
  524.     if(status != 0)
  525.       cerror("Error during %s macro expansion", macname);
  526. } /* end of run_macro */
  527.  
  528. #if HOST == SYS_OS2 || HOST == SYS_MVS
  529. static char* TMPDIR = "E:\\";
  530. #else
  531. static char* TMPDIR = "/tmp/";
  532. #endif
  533.  
  534. /* exec macro using temp files to redirect stdin and stdout */
  535. void exec_macro (args, ifile, ofile)
  536.      struct macroargs* args;
  537.      FILEINFO *ifile, *ofile;
  538. {
  539.   char tmpin[256];
  540.   char tmpout[256];
  541.   FILEINFO *f;
  542.   int n, ifds,ofds;
  543.   char* p;
  544.  
  545.   /* make temp file names using process id */
  546. #if HOST == SYS_AMIGA
  547. #ifdef LATTICE
  548. #include <proto/exec.h>
  549. #endif
  550.   sprintf(tmpin, "T:cpp_in%x", FindTask(NULL));
  551.   sprintf(tmpout, "T:cpp_out%x", FindTask(NULL));
  552. #else
  553.   sprintf(tmpin, "%s%s%d", TMPDIR, "in", getpid() % 9999);
  554.   sprintf(tmpout, "%s%s%d", TMPDIR, "out", getpid() % 9999);
  555. #endif
  556.  
  557.   /* copy ifile to input temp file and close it */
  558.   ifds = open(tmpin, O_WRONLY | O_CREAT | O_TRUNC, 0644);
  559.   if (ifds < 0) {
  560.     perror("cpp exec macro error");
  561.     cerror("Cannot open external macro temp file \"%s\"", tmpin);
  562.     return;
  563.   }
  564.   f = ifile;
  565.   for (;;) {
  566.     n = write(ifds, f->buffer, f->bptr - f->buffer);
  567.     if (n < 0) {
  568.       perror("cpp macro error");
  569.       cerror("Cannot write to external macro temp file \"%s\"", tmpin);
  570.       close(ifds);
  571.       return;
  572.     }
  573.     if ((f = f->parent) == NULL) break;
  574.   }
  575.   close(ifds);
  576.  
  577.   /*  fork and exec external macro */
  578.   run_macro(args->program, args->args, tmpin, tmpout);
  579.  
  580.   /* copy output temp file to ofile and close it */
  581.   ofds = open(tmpout, O_RDONLY);
  582.   if (ofds < 0) return;
  583.   n = read(ofds, ofile->buffer, NBUFF);
  584.   for (;;) {
  585.     FILEINFO* new;
  586.     if (n <= 0) break;
  587.     p = ofile->buffer + n;
  588.     *p = EOS;
  589.     new = get_temp_file(NBUFF, ofile->filename);
  590.     new->parent = ofile->parent;
  591.     ofile->parent = new;
  592.     ofile = new;
  593.     n = read(ofds, ofile->bptr, NBUFF);
  594.   }
  595.   close(ofds);
  596.  
  597.   /* remove temp files */
  598.   unlink(tmpin);
  599.   unlink(tmpout);
  600. }
  601.  
  602. #endif  /* for DOS or VMS or MVS or AmigaOS */
  603.  
  604. /*
  605.  * Call an internal macro expander
  606.  */
  607. static void
  608. call_expander (args, ifile, ofile)
  609.      struct macroargs* args;
  610.      FILEINFO *ifile, *ofile;
  611. {
  612.   int nargs = 0;
  613.   char** arg = args->args;
  614.   while(*arg++ != NULL) nargs++;        /* Count args */
  615.   *ifile->bptr = EOS;
  616.   ifile->bptr = ifile->buffer;
  617.   MacInFile = ifile;                    /* Set global I/O buffers */
  618.   MacOutFile = ofile;
  619.   MacOutEnd = ofile->buffer + (NBUFF - 1);
  620.   if (((*args->expander)(nargs, args->args)) != 0)
  621.     cerror("Error during internal %s macro expansion", args->program);
  622.   *MacOutFile->bptr = EOS;
  623.   MacOutFile->bptr = MacOutFile->buffer;
  624. }
  625. /*
  626.  * Expand an external macro.
  627.  *   1. Grab the macro name and everything until the next semicolon
  628.  *      (includling all matching {} [] () <> "" '' and comments found along
  629.  *      the way), and put it in a macro buffer.  Commands found along
  630.  *      the way are processed.
  631.  *   2. Pipe this into the macro, and put the result in another macro buffer.
  632.  *   3. Process the macro expansion.
  633.  */
  634. void
  635. expand_external(dp)
  636.   char* dp;
  637. {
  638.   FILEINFO      *ifile, *ofile; /* The first output file buffer */
  639.   struct macroargs* args = (struct macroargs*) dp; /* macro arguments */
  640.   char* name = args->name;              /* Macro name */
  641.   extern int macroid();                 /* Get a macro-expanded character */
  642.  
  643.   if(!args->recursive) {
  644.     FILEINFO *f;                /* If recursion where it isn't allowed */
  645.     for (f = infile; (f->parent != NULL); f = f->parent) {
  646.       if(strcmp(f->filename, name) == 0) {
  647.         fputs(name, stdout);    /* Skip expansion */
  648.         return;}}}
  649.   /*
  650.    * Ensure the start is correct
  651.    */
  652.   strcpy(work, name);
  653.   workp = work + strlen(name);
  654.   { char c;                     /* current character */
  655.     char d = args->conditional;
  656.     if(d != EOS) {              /* check for conditional character */
  657.       instring = TRUE;
  658.       while(isspace(c = get())) *workp++ = c;
  659.       *workp = EOS;
  660.       instring = FALSE;
  661.       unget();
  662.       if (c != d) {             /* When no conditional character, exit */
  663. #ifdef paranoia
  664.         cwarn("defmacro \"%s\" needs arguments", name);
  665. #endif
  666.                                 /* Place macro name back in input stream */
  667.                                 /* being careful to avoid recursion */
  668.         /* fputs(work, stdout); this isn't good enough */
  669.         ofile = get_temp_file(strlen(work)+1, name);
  670.         ofile->parent = infile;
  671.         ofile->buffer[0] = DEF_MAGIC;
  672.         strcpy(ofile->buffer+1, work);
  673.         infile = ofile;
  674.         return;
  675.       }
  676.     }
  677.   }
  678.   current_line = line;
  679.   strcpy(current_file,
  680.          (infile->progname != NULL) ? infile->progname : infile->filename);
  681.   /*
  682.    * Copy everything to the next ;
  683.    */
  684.   ifile = get_temp_file(NBUFF, name);
  685.   ofile = get_temp_file(NBUFF, name);
  686.   strcpy(ifile->buffer, work);  /* Send name to macro expander */
  687.   ifile->bptr = ifile->buffer + strlen(work);
  688.   instring = TRUE;              /* Ensure comments get copied too */
  689.   if (copy_body(ifile, args->delimiter, FALSE, TRUE, args->expanding) == NULL) {
  690.     instring = FALSE;
  691.     return;
  692.   }
  693.   instring = FALSE;
  694.   infile->line = line;
  695.   /*
  696.    * Expand the macro
  697.    */
  698.   ofile->parent = infile;
  699.   if (args->expander == NULL) {
  700. #if !(HOST == SYS_OS2 || HOST == SYS_VMS || HOST == SYS_MVS || HOST == SYS_AMIGA)
  701.     fork_macro(args, ifile, ofile);
  702. #else
  703.     exec_macro(args, ifile, ofile);
  704. #endif
  705.   }
  706.   else
  707.     call_expander(args, ifile, ofile);
  708.   infile = ofile;
  709. }
  710.  
  711. /*
  712.  * scanthing(delim)
  713.  *   Copy from input to work, stopping at delim
  714.  *   but including everything with matching {} [] ()
  715.  *   <> "" '' and comments found along the way.
  716.  */
  717. static int
  718. scanthing(delim, is_string, is_top_level)
  719.   char delim;                           /* Current delimiter */
  720.   int is_string, is_top_level;          /* flags */
  721. {
  722.   char c;                               /* Current character */
  723.   char newdelim;                        /* New delimiter to look for */
  724.   int new_is_string = FALSE;            /* New is_string flag */
  725.  
  726.   if (is_top_level) {                   /* Skip leading whitespace */
  727.     workp = work;
  728.     c = skipws();
  729.     unget();
  730.   }
  731.   while ((c = get()) != EOF_CHAR) {
  732.     save(c);
  733.     if (delim == c &&                   /* Quit when delimeter found */
  734.         delim != ' ') {
  735.       *workp = EOS;
  736.       return FALSE;
  737.     }
  738.     if (!is_string) {
  739.       switch (c) {              /* Look for new delimeters when not is_string */
  740.       case ' ':
  741.       case ',':
  742.       case '\t':
  743.       case '\n':
  744.         if (delim == ' ') {
  745.           unget();
  746.           workp -= 1;
  747.           *workp = EOS;
  748.           return FALSE;
  749.         } else continue;
  750.       case '{':  newdelim = '}'; break;
  751.       case '[':  newdelim = ']'; break;
  752.       case '(':  newdelim = ')'; break;
  753.       case '\'':  newdelim = '\''; new_is_string = TRUE; break;
  754.       case '\"':  newdelim = '\"'; new_is_string = TRUE; break;
  755.       default:
  756.         continue;               /* Loop to next character, unless break above */
  757.       } /* end switch */
  758.       if (is_top_level && newdelim == delim) {
  759.         is_top_level = FALSE;
  760.         continue;               /* at top_level, with left side of delimiter */
  761.       }
  762.       scanthing(newdelim, new_is_string, FALSE);
  763.       new_is_string = FALSE;
  764.     }  /* if !is_string */
  765.   } /* end while */
  766.   return (c == EOF_CHAR);
  767. }
  768. /*
  769.  * create a new defmacro (internal function)
  770.  */
  771. void
  772. new_defmacro(name, expanding, recursive, delimiter, conditional,
  773.              expander, program, args)
  774.   char* name;
  775.   char  expanding;              /* Non-zero when args are macro expanded */
  776.   char  recursive;              /* Non-zero when recursive */
  777.   char  delimiter;              /* Delimiter */
  778.   char  conditional;            /* Expand only if this char found after name */
  779.   internal_expander expander;   /* Macro expander or NULL if file */
  780.   char *program;                /* macro program pathname */
  781.   char** args;                  /* arg list (vector of strings) */
  782. {
  783.   struct macroargs* new;
  784.   int nargs = 1;
  785.   char** argp;
  786.  
  787.   if (args == NULL) {                     /* Setup default args */
  788.     args = (char**) getmem(sizeof(char*)*2);
  789.     args[0] = program;
  790.     args[1] = NULL;
  791.   }
  792.   for(argp = args; *argp++!=NULL; nargs++);     /* Count arguments */
  793.  
  794.   new = (struct macroargs*) getmem(sizeof(struct macroargs) +
  795.                                    (nargs*sizeof(char*)));
  796.   new->name = name;
  797.   new->expanding = expanding;
  798.   new->recursive = recursive;
  799.   new->delimiter = delimiter;
  800.   new->conditional = conditional;
  801.   new->expander = expander;
  802.   new->program = program;
  803.   while(nargs-- > 0)
  804.     new->args[nargs] = args[nargs];
  805.   define_builtin(name, expand_external, (char*) new);
  806. }
  807.  
  808. /*
  809.  * Define an external macro handler
  810.  *
  811.  *     Called when #pragma defmacro
  812.  *     is found in the input.
  813.  * The complete form is:
  814.  * #pragma defmacro name <file> options
  815.  * #pragma defmacro name "file" options
  816.  * #pragma defmacro name program options
  817.  *
  818.  * where "options" is zero or more of the following:
  819.  *  recursive    - when present, the macro is recursively expanded
  820.  *  delimiter=?  - the default delimiter of ; is replaced with ?
  821.  *  condition=?  - Expand only if this char found after name
  822.  *  other        - other options are passed as arguments to the
  823.  *                 macro expander
  824.  */
  825. void
  826. define_external()
  827. {
  828.   char c;
  829.   char delim;
  830.   char* name;
  831.   char* program;
  832.   int nargs = 1;
  833.   int maxargs = 32;
  834.   char* args[32];
  835.   int recursive = 0;
  836.   int expanding = 0;
  837.   int delimiter = ';';
  838.   int conditional = EOS;
  839.   internal_expander expander = NULL;
  840.  
  841.   c = skipws();
  842.   scanid(c);
  843.   name = savestring(tokenbuf);
  844.   program = name;
  845.                 /* Parse_include returns EOS for internal macros (no " or <) */
  846.   delim = parse_include();                /* Get program to work */
  847.   strcpy(tokenbuf, work); /* Keep copy because findinclude mangles it */
  848. #if HOST == SYS_OS2
  849.   strcat(work,".exe");
  850. #endif
  851.   if (delim == EOS || findinclude(work, (delim == '"'), X_OK) == FALSE) {
  852.     struct expander_pair* p = internal_macros; /* Macro file not found */
  853.     strcpy(work, tokenbuf);                    /* restore progam name */
  854.     for (; p->name != NULL; p++)               /* Search for internal macro */
  855.       if (strcmp(p->name, work) == 0) {
  856.         expander = p->function;
  857.         goto found_internal;
  858.       }
  859.     cerror("Cannot open macro file \"%s\"", work);
  860.     goto macerr;
  861.   }
  862. /*
  863. #if HOST == SYS_VMS || HOST == SYS_MVS
  864.   else {
  865.     cerror("External macros not supported for \"%s\"", work);
  866.     goto macerr;
  867.   }
  868. #endif
  869. */
  870. found_internal:
  871.   program = args[0] = savestring(work);
  872.  
  873.   c = skipws();
  874.   while (c != '\n' && c != EOF_CHAR) {
  875.     while (c == ',') {
  876.       args[nargs++] = ",";
  877.       c = skipws();
  878.     }
  879.     unget();
  880.     if (scanthing(' ', FALSE, TRUE))
  881.       cfatal("End of file during #pragma defmacro %s processing.", name);
  882.     if(strcmp(work, "recursive") == 0) {
  883.       recursive = 1;
  884.       c = skipws();
  885.       continue;
  886.     }
  887.     if(strcmp(work, "expanding") == 0) {
  888.       expanding = 1;
  889.       continue;
  890.     }
  891.     {
  892.       int* option = NULL;
  893.       if(strncmp(work, "delimiter", 9) == 0) option = &delimiter;
  894.       if(strncmp(work, "condition", 9) == 0) option = &conditional;
  895.       if(option != NULL) {
  896.         if (work[9] != '=' && (c = skipws()) != '=') {
  897.           cerror("In #pragma defmacro, Missing = after %s", work);
  898.           goto macerr;
  899.         }
  900.         if(strlen(work) == 11)
  901.           *option = work[10];
  902.         else
  903.           *option = skipws();
  904.         if(*option != '\n') c = skipws();
  905.         continue;
  906.       }
  907.     }                /* must be a program option */
  908.     if (nargs >= maxargs) {
  909.       cerror("In #pragma defmacro %s, too many arguments.", name);
  910.       goto macerr;
  911.     }
  912.     args[nargs] = savestring(work);
  913.     nargs++;
  914.     c = skipws();
  915.   } /* end while not newline */
  916.   unget();                      /* Force nl after includee      */
  917.   args[nargs] = NULL;
  918.   new_defmacro(name, expanding, recursive, delimiter, conditional,
  919.                expander, program, args);
  920.   return;
  921.  
  922.  macerr:
  923.   skipnl();                     /* Skip to end of line */
  924.   unget();                      /* Force nl */
  925.   return;
  926. }
  927.  
  928. /*-----------------------------------------------------------------------------
  929.  * Process DEFPACKAGE command.
  930.  * format is: defpackage NAME <path> options
  931.  * where options are optional and seperated by comma's.
  932.  *
  933.  * This really should be an internal defmacro, but
  934.  * it was so much easier to handle it here...
  935.  */
  936. static char* defpackage_options[] = {
  937.   "length", "case",
  938.   "start", "increment", "template", "max", "use_first", "nospace", NULL};
  939.  
  940. void
  941. define_package(dp)
  942.   char* dp;
  943. {
  944.   extern void package_define();
  945.   char c;
  946.   char delim;
  947.   char* pkgname;
  948.   char* pkgfile;
  949.   char* options[8];
  950.   long loptions[6];
  951.   int np;
  952.   char errmsg[100];
  953.  
  954.   options[0]=""; options[1]=""; options[2]=""; options[3]="";
  955.   options[4]=""; options[5]=""; options[6]=""; options[7]="";
  956.   c = skipws();
  957.   scanid(c);
  958.   pkgname = savestring(tokenbuf);
  959.   if((delim = parse_include()) == EOS) {
  960.     cerror("DEFPACKAGE syntax error", NULLST);
  961.     goto macerr;
  962.   }
  963.   if (findinclude(work, (delim == '"'), R_OK) == FALSE) {
  964.     cerror("Cannot open package header file \"%s\"", work);
  965.     goto macerr;
  966.   }
  967.   pkgfile = savestring(work);
  968.                                           /* Get options */
  969.   while ((c=skipws()) != '\n' && c != EOF_CHAR) {
  970.     char** p;
  971.     if (c == ',')                         /* Skip over commas */
  972.       if ((c=skipws()) == '\n' || c == EOF_CHAR)
  973.         break;
  974.     scanid(c);
  975.     np = 0;
  976.     p = defpackage_options;
  977.     for (; *p != NULL; p++, np++)
  978.       if(strcmp(tokenbuf, *p) == 0) break;
  979.     if (*p == NULL) {
  980.       sprintf(errmsg, "In DEFPACKAGE %s, unknown option \"%s\"",
  981.               pkgname, tokenbuf);
  982.       cerror(errmsg, NULLST);
  983.       goto macerr;
  984.     } else if ((c = skipws()) != '=') {
  985.       sprintf(errmsg, "In DEFPACKAGE %s, Missing = after %s option",
  986.               pkgname, tokenbuf);
  987.       cerror(errmsg, NULLST);
  988.       goto macerr;
  989.     } else {
  990.       c = skipws();
  991.       scanid(c);
  992.       options[np] = savestring(tokenbuf);
  993.     }
  994.   }
  995.   unget();  /* Force nl after includee */
  996.                                           /* Convert numeric options */
  997.   for(np=2; np<8; np++) {
  998.     char* endp[1];
  999.     long val = strtol(options[np], endp, 0);
  1000.     if(**endp != EOS) {
  1001.       sprintf(errmsg, "In DEFPACKAGE %s, Illegal value %s for %s option",
  1002.               pkgname, options[np], defpackage_options[np]);
  1003.       cwarn(errmsg, NULLST);
  1004.     }
  1005.     loptions[np-2] = val;
  1006.   }
  1007.   package_define(pkgname, pkgfile,
  1008.                  options[0], options[1], loptions[0], loptions[1],
  1009.                  loptions[2], loptions[3], loptions[4], loptions[5]);
  1010.   return;
  1011.  
  1012.  macerr:
  1013.   skipnl();                     /* Skip to end of line */
  1014.   unget();                      /* Force nl */
  1015.   return;
  1016. }
  1017.