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