home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume11 / texpp / part02 / texpp.c
Encoding:
C/C++ Source or Header  |  1990-03-25  |  60.0 KB  |  1,589 lines

  1. /***************************************************************************
  2.  *                                       *
  3.  *           texpp TeX preprocessor, Version 1.2.               *
  4.  *              Laci Csirmaz, DIMACS - Rutgers               *
  5.  *                   Feb. 25, 1990                   *
  6.  *                                       *
  7.  ***************************************************************************
  8.  
  9.       You are granted to use, modify, copy, redistribute this program any 
  10.    way you want. However no warranties are made for this program or the 
  11.    accopanying documentation.
  12.  
  13.       To compile the program simply invoke cc by typing
  14.         cc texpp.c -o texpp
  15.    On certain computers the 'strdup()' function is missing from the standard
  16.    C library. In this case, recompile the preprocessor by
  17.         cc -DSTRDUP texpp.c -o texpp
  18.  
  19.       Please send your comments, suggestions, etc. to:
  20.         csirmaz@cs.rutgers.edu
  21.               
  22.  ***************************************************************************/
  23.  
  24.  
  25. /*-------------------------------------------------------------------------*
  26.  |                            include files                           |
  27.  *-------------------------------------------------------------------------*/
  28. #include <ctype.h>
  29. #include <malloc.h>
  30. #include <string.h>
  31. #include <strings.h>
  32. #include <stdio.h>
  33. #include <varargs.h>
  34.  
  35. /*-------------------------------------------------------------------------*
  36.  |                      prototypes not in UNIX                            |
  37.  *-------------------------------------------------------------------------*/
  38. #define byte unsigned char        /* define new mode */
  39. char *calloc(); char *sprintf();
  40.  
  41. /*-------------------------------------------------------------------------*
  42.  |                           mode and style                           |
  43.  *-------------------------------------------------------------------------*/
  44. #define MATH_MODE    0x01
  45. #define DISP_MODE    0x02
  46. #define DEFINE_MODE    0x04
  47. #define COMMENT_MODE    0x08
  48.  
  49. #define SIMPLE_STYLE    0    /* style for `$' or `$$' */
  50. #define DEFINE_STYLE    (-1)    /* style for %mdefine */
  51.  
  52. int global_mode;        /* mode flags - in math, display mode; 
  53.                    skipping a comment, or reading a TeXpp
  54.                    definition. */
  55. int mode_style;            /* MATH and DISP style number to distinguish
  56.                    between different pairs of math and display
  57.                    mode switches. */
  58.  
  59. #define in_comment_mode()    (global_mode&COMMENT_MODE)
  60. #define in_def_mode()        (global_mode&DEFINE_MODE)
  61. #define in_disp_mode()        ((global_mode&DISP_MODE)!=0)
  62. #define in_math_mode()        ((global_mode&MATH_MODE)!=0)
  63. #define in_plain_mode()        ((global_mode&(DISP_MODE|MATH_MODE))==0)
  64. #define set_plain_mode()    {global_mode&= ~(DISP_MODE|MATH_MODE);}
  65. #define set_disp_mode()        {global_mode |= DISP_MODE;}
  66. #define set_math_mode()        {global_mode |= MATH_MODE;}
  67.  
  68. /*--------------------------------------------------------------------------*/
  69. /*                        input/output variables                */
  70. /*--------------------------------------------------------------------------*/
  71. FILE *input_file;        /* stream to read from */
  72. FILE *output_file;        /* stream to write to */
  73. FILE *error_file;        /* stream for error messages */
  74. char *input_file_name;        /* the actual input file name */
  75. int input_line_number;        /* which line we are in */
  76.  
  77. int exit_value=0;        /* 1 if an error occured */
  78.  
  79. unsigned output_position=0;    /* where the next token goes */
  80. unsigned last_output_position=0;/* saved output_position */
  81. unsigned error_position=0;    /* end of last error position */
  82.  
  83. #define LAST_OUT        (last_output_position)
  84. #define CURRENT_OUT        (output_position)
  85. #define ERROR_OUT        (error_position)
  86.  
  87. /*-------------------------------------------------------------------------*
  88.  |                  characters used as special tokens               |
  89.  *-------------------------------------------------------------------------*/
  90. #define E_LBRACE    ('{'|0x80)    /* replaces \{ */
  91. #define E_RBRACE    ('}'|0x80)    /* replaces \} */
  92. #define E_PCT_MARK    ('%'|0x80)    /* replaces \% */
  93. #define E_BACKSLASH    ('\\'|0x80)    /* replaces \\ */
  94. #define E_DOLLAR    ('$'|0x80)    /* replaces \$ */
  95. #define E_EOF        255        /* replaces EOF */
  96. #define E_KEYWORD    254        /* keyword ahead */
  97.  
  98. #define make_par(x)    (((x)-'1')|0x80)/* replaces #1 ... #9 */
  99. #define extract_par(x)    ((x)-0x80)
  100. #define is_par(x)    (0x80<=(x)&&(x)<0x89)
  101.  
  102. /*-------------------------------------------------------------------------*
  103.  |                  Storing and retrieving macro text                      |
  104.  *-------------------------------------------------------------------------*/
  105. typedef struct MACRO {
  106.         byte type;        /* flags */
  107.         byte leftpar,rightpar;    /* number of left and right pars */
  108.         int style;        /* style if mode switch word */
  109.         byte *name;        /* macro name */
  110.         byte *body;        /* macro body, can be NULL */
  111.         struct MACRO *link;    /* pointer to the next macro */
  112.         struct MACRO *keyword;    /* pointer to the next \-keyword */
  113. };
  114.  
  115. /*--------------------------- MACRO flags ---------------------------------*/
  116. #define K_MATH        0x01    /* 1 for MATH and DISP mode only */
  117. #define K_PRESERVE    0x02    /* 1 for \preserve keyword */
  118. #define K_BACKSLASH    0x04    /* 1 if starts with backslash */
  119. #define K_CHECKLETTER    0x08    /* 1 if cannot be followed by a letter */
  120. #define K_INOUT        0x10    /* bit for IN (0) or OUT (1) mode switch */
  121. #define K_MATHDISP    0x20    /* bit for MATH (0) or DISP (1) mode switch */
  122. #define K_STANDALONE    0x40    /* 1 for identical IN and OUT mode switch */
  123.  
  124. #define is_math_macro(x)    ((x)->type & K_MATH)
  125. #define word_length_k(x)    strlen((char *)((x)->name))
  126. #define style_k(x)        ((x)->style)
  127. #define body_k(x)        ((x)->body)
  128. #define left_pars_k(x)        ((x)->leftpar)
  129. #define right_pars_k(x)        ((x)->rightpar)
  130. #define is_preserve_k(x)    ((x)->type & K_PRESERVE)
  131. #define is_backslash_k(x)    ((x)->type & K_BACKSLASH)
  132. #define is_modeswitch_k(x)    ((x)->style > 0)
  133. #define is_math_k(x)        (((x)->type & K_MATHDISP)==0)
  134. #define is_in_k(x)        (((x)->type & K_INOUT)==0)
  135. #define is_standalone_k(x)    ((x)->type & K_STANDALONE)
  136. #define check_letter_k(x)    ((x)->type & K_CHECKLETTER)
  137.  
  138. /*-------------------------------------------------------------------------*
  139.  |                            symbols                                      |
  140.  *-------------------------------------------------------------------------*/
  141. #define SOFT_DELIMITER    1    /* space, tab, newline */
  142. #define HARD_DELIMITER    2    /* newline in DEF_MODE */
  143. #define DELIMITER    3    /* control character, not soft delimiter */
  144. #define MACRO_DELIM    4    /* macro text delimiter in DEF_MODE */
  145. #define MATH_IN        5    /* entering math mode */
  146. #define MATH_OUT    6    /* leaving math mode */
  147. #define DISP_IN        7    /* entering displayed mode */
  148. #define DISP_OUT    8    /* leaving displayed mode */
  149. #define PRESERVE    9    /* \preserve keyword */
  150. #define DEF_KEYWORD    10    /* %define keyword */
  151. #define MDEF_KEYWORD    11    /* %mdefine keyword */
  152. #define UNDEF_KEYWORD    12    /* %undefine keyword */
  153. #define MATH_KEYWORD    13    /* %mathmode keyword */
  154. #define DISP_KEYWORD    14    /* %dispmode keyword */
  155. #define COMMENT        15    /* comment in a line */
  156. #define EMPTY_LINE    16    /* empty line, cannot be in a macro */
  157. #define WORD        17    /* a word of visible characters */
  158. #define OPEN        18    /* { */
  159. #define CLOSE        19    /* } */
  160. #define ENDFILE        20    /* at end of file */
  161. #define PARAMETER    21    /* #1 .. #9 */
  162.  
  163. int SYMBOL;            /* the last symbol */
  164. int S_aux1;            /* if SYMBOL==SOFT_DELIMITER then S_aux1=0
  165.                    says that the delimiter vanishes at
  166.                    substitution; if SYMBOL==PARAMETER then the
  167.                    parameter's value (0..8) */
  168. struct MACRO *S_aux2;        /* if SYMBOL==WORD then the corresponding 
  169.                    MACRO entry, or NULL if none */
  170.  
  171. /*-------------------------------------------------------------------------*
  172.  |               Preprocessor units                   |
  173.  *-------------------------------------------------------------------------*/
  174. #define X_PARAMETER    0    /* a parameter */
  175. #define X_DMODE_OUT    1    /* $ or $$ leaving mode */
  176. #define X_XMODE_OUT    2    /* other mode closing symbol */
  177. #define X_CLOSE        3    /* closing brace */
  178. #define X_ERROR        4    /* error encountered */
  179. #define X_OTHER        5    /* other special symbol */
  180.  
  181. /*-------------------------------------------------------------------------*
  182.  |                         TeX and TeXpp texts                      |
  183.  *-------------------------------------------------------------------------*/
  184. #define    T_DEFINE    "define"    /* TeXpp keywords after % */
  185. #define T_DEFINE_LEN    6
  186. #define T_MDEFINE    "mdefine"
  187. #define T_MDEFINE_LEN    7
  188. #define T_UNDEFINE    "undefine"
  189. #define T_UNDEFINE_LEN    8
  190. #define T_MATHMODE    "mathmode"
  191. #define T_MATHMODE_LEN    8
  192. #define T_DISPMODE    "dispmode"
  193. #define T_DISPMODE_LEN    8
  194.  
  195. #define T_PRESERVE    "\\preserve"    /* should start with backslash!!! */
  196. #define T_PRESERVE_LEN    9
  197.  
  198. #define TeXpp_MACRO_DEFINITION    "%%% TeXpp macro definition %"
  199.     /* replacement text for TeXpp macro definition */
  200.  
  201. /*-------------------------------------------------------------------------*
  202.  |                        error message texts                   |
  203.  *-------------------------------------------------------------------------*/
  204. #define TEX_ERROR_FORMAT        "%%%%%%TeXpp error in %s line %d: "
  205.     /* used as a format to insert error message into TeX text */
  206. #define ERR_ERROR_FORMAT        "Error in %s line %d: "
  207.     /* used as a format to write error message into stderr */
  208.  
  209. #define CANNOT_OPEN_FILE        "cannot open the file"
  210. #define    WRONG_FORMAL_PARAMETER        "no digit after #-mark"
  211. #define PARAMETER_TWICE            "parameter #%d declared twice"
  212. #define WRONG_MACRO_NAME        "macro name expected"
  213. #define WRONG_MODE_SWITCH_DEF        "wrong mode switch keyword definition"
  214. #define MISSING_DELIMITER        "missing macro text delimiter %% "
  215. #define TOO_LESS_LEFT_PARAMS        "less than %d left parameters for %s "
  216. #define TOO_LESS_PARAMS            "less than %d parameters for %s "
  217. #define TOO_LONG_MACRO_DEF        "too long definition for macro %s "
  218. #define TOO_LONG_PARAMETER        "too long parameter for macro %s "
  219. #define UNDEFINED_PARAMETER        "parameter #%d wasn't declared"
  220. #define WRONG_DOLLAR_SWITCH        "erroneous $ mode switch"
  221. #define WRONG_CLOSING_DOLLAR        "erroneous closing $ mode switch"
  222. #define EMPTY_LINE_IN_MODE        "empty line in %s mode"
  223. #define ENDFILE_IN_MODE            "end of file in %s mode"
  224. #define WRONG_MODE_SWITCH        "erroneous %s mode switch"
  225. #define OUT_OF_MEMORY            "no more memory"
  226.  
  227. void error();    /*VARARGS1*/        /* just to clean up things */
  228.  
  229. /*=========================================================================*/
  230. /*                    standard procedures not in UNIX                      */
  231. /*=========================================================================*/
  232. #define upper(x)    ((x)|0x20)    /* convert letters to uppercase */
  233.  
  234. int stricmp(left,right) char *left,*right;
  235. /* compares strings with no case  -- works only with ASCII characters */
  236. {
  237.     while(*left != 0 && (*left == *right || 
  238.     (isalpha(*left) && isalpha(*right) && upper(*left)==upper(*right)))
  239.       ) { left++; right++;}
  240.     return(*left-*right);
  241. }
  242.  
  243. void setmem(to,len,c) byte *to; unsigned len; byte c;
  244. /* fills `len' bytes starting from `to' by the value of `c' */
  245. {unsigned i;
  246.     for(i=0;i<len;i++) *to++=c;
  247. }
  248.  
  249. #ifdef STRDUP
  250. char *strdup(s) char *s;    /* duplicates s */
  251. {char *dup;
  252.     dup=malloc(1+strlen(s));
  253.     if(dup!=NULL) strcpy(dup,s);
  254.     return(dup);
  255. }
  256. #endif
  257.  
  258. /*=========================================================================*/
  259. /*                          token input                                    */
  260. /*=========================================================================*/
  261.  
  262. /*-------------------------------------------------------------------------*
  263.  | The lowest level of the three-level reading is the immediate character  |
  264.  | input from `input_file'. Procedure `next_char()' filters out incoming   |
  265.  | characters ==0 and >126, returns E_EOF on end of file, and makes some   |
  266.  | local translations depending on the mode stored in `global_mode':       |
  267.  | o  in COMMENT_MODE, all characters are returned. In               |
  268.  | o  in other modes the pairs \{ \} \$ \% and \\ are coded as single chars|
  269.  | o  in DEFINE_MODE pairs #1 .. #9 are recognized as parameters; and ##   |
  270.  |     is replaced by a single # char.                       |
  271.  *-------------------------------------------------------------------------*/
  272.  
  273. int next_char()    /* lowest level reading from `input_file' */
  274. {int c,c1;
  275.     while((c=getc(input_file))==0 || c>0x7E);/* skip 0 and >126 chars */
  276.     if(c<0) return(E_EOF);        /* here is the end of the file */
  277.     if(in_comment_mode()) return(c);    /* skipping a comment */
  278.     if(c=='\\'){            /* the char is backslash */
  279.     switch(c1=getc(input_file)){    /* next char */
  280. case '%':   c=E_PCT_MARK; break;
  281. case '{':   c=E_LBRACE; break;
  282. case '}':   c=E_RBRACE; break;
  283. case '\\':  c=E_BACKSLASH; break;
  284. case '$':   c=E_DOLLAR; break;
  285. default:    ungetc(c1,input_file); break;/* simply put back the ahead char */
  286.     }
  287.     } else if(c=='#' && in_def_mode()){    /* check formal parameters */
  288.     c1=getc(input_file); 
  289.     if('1'<=c1 && c1<='9') c=make_par(c1);
  290.     else if(c1!='#'){
  291.         error(WRONG_FORMAL_PARAMETER);
  292.         ungetc(c1,input_file);
  293.     }
  294.     }
  295.     return(c);
  296. }
  297.  
  298. /*-------------------------------------------------------------------------*
  299.  | On the medium level, values given by `next_char()' are passed over as   |
  300.  | tokens. But tokens can be read AHEAD, so special procedures are used to |
  301.  | deal with them. The circular buffer `token_ahead[]' stores the tokens   |
  302.  | read ahead; its size must be a power of 2. Procedures handling tokes:   |
  303.  | -- initialize_token_reading() should be called first.           |
  304.  | -- spy_token() returns the next token but does not advances ahead.      |
  305.  | -- spy_string_ahead() returns TRUE(!=0) if the next item agrees with    |
  306.  |      the parameter, and checks whether the item following the string is |
  307.  |      a white space or is NOT a letter (to agree with TeX's backslash    |
  308.  |      convention).                               |
  309.  | -- get_next_token() simply returns the next character.           |
  310.  | -- skip_tokens(n) skips `n' tokens ahead.                   |
  311.  | To comply with the mode dependent character reading, the spied chars    |
  312.  | should not change the mode -- so be careful when spying ahead ...       |
  313.  *-------------------------------------------------------------------------*/
  314.  
  315. #define MAX_TOKEN_AHEAD    128            /* must be a power of 2 */
  316. byte token_ahead[MAX_TOKEN_AHEAD];        /* circular buffer */
  317. int token_ahead_in=0, token_ahead_out=0;    /* buffer pointers */
  318.  
  319. #define initialize_token_reading()    {token_ahead_in=token_ahead_out=0;}
  320.  
  321. byte spy_token_ahead()    /* Returns the next token but does not advances */
  322. {
  323.     if(token_ahead_in==token_ahead_out){    /* ahead buffer is empty */
  324.     token_ahead[token_ahead_in]=next_char();
  325.     token_ahead_in=(token_ahead_in+1)&(MAX_TOKEN_AHEAD-1);
  326.     }
  327.     return(token_ahead[token_ahead_out]);
  328. }
  329.  
  330. #define FOLLOW_NOTHING        0
  331. #define FOLLOW_NO_LETTER    1
  332. #define FOLLOW_SPACE        2
  333.  
  334. int spy_string_ahead(str,follow_up) char *str; int follow_up;
  335. {int t,i,same; byte tt;
  336.     t=token_ahead_out; same=1;
  337.     while(same && (*str || follow_up)){
  338.     if(t==token_ahead_in){    /* should read ahead */
  339.         i=(token_ahead_in+1)&(MAX_TOKEN_AHEAD-1);
  340.         if(i!=token_ahead_out){
  341.         token_ahead[t]=next_char(); token_ahead_in=i;
  342.         } else return(0);    /* ahead buffer is full, not found */
  343.     }
  344.     tt=token_ahead[t];
  345.     if(*str){
  346.         same=((unsigned char)(*str))==tt;
  347.         str++; t=(t+1)&(MAX_TOKEN_AHEAD-1);
  348.     } else {
  349.         same=follow_up==FOLLOW_NO_LETTER ? ((tt > 127) || !isalpha(tt)) :
  350.                    (tt==' ' || tt=='\t');
  351.         follow_up=0;
  352.     }
  353.     }
  354.     return(same);
  355. }
  356.  
  357. int get_next_token()  /* gives the next token */
  358. {byte res;
  359.     if(token_ahead_in==token_ahead_out)
  360.     return(next_char());
  361.     res=token_ahead[token_ahead_out];
  362.     token_ahead_out=(token_ahead_out+1)&(MAX_TOKEN_AHEAD-1);
  363.     return(res);
  364. }
  365.  
  366. void skip_tokens(n) int n; /* skips the next `n' subsequent tokens */
  367. {int stored;
  368.     stored=(token_ahead_in+MAX_TOKEN_AHEAD-token_ahead_out)
  369.         &(MAX_TOKEN_AHEAD-1);
  370.     if(n<stored){
  371.     token_ahead_out+=n; token_ahead_out&=(MAX_TOKEN_AHEAD-1);
  372.     } else {
  373.     n-=stored;
  374.     token_ahead_out=token_ahead_in;
  375.     while(n-- > 0) next_char();    
  376.     }
  377. }
  378.  
  379. /*=========================================================================*/
  380. /*                          token output                                   */
  381. /*=========================================================================*/
  382.  
  383. /*-------------------------------------------------------------------------*
  384.  | Output is done through double buffering: OUT_BUFFER and OTHER_OUT_       |
  385.  | BUFFER hold the output until the other is full. This means that every   |
  386.  | time the last OUT_BUFFER_LEN output tokens are recoverable. This       |
  387.  | mechanism is used to store macro parameters which are erased after       |
  388.  | substitution.                               |
  389.  | -- output_position is used as an absolute position pointer.             |
  390.  | -- alloc_outbuffers() allocates memory for the buffers.           |
  391.  | -- store_token(t) puts `t' into the output buffer.               |
  392.  | -- store_string(str) puts the tokens of `str' into the output buffer.   |
  393.  | -- flush_output() flushes output buffers.                   |
  394.  | -- set_output_position(pos) erases all output written after the absolute|
  395.  |     position `pos', if it is possible.                   |
  396.  | -- retrieve_out(from,till,to) reads back the output between positions   |
  397.  |     `from' and `till' and stores it at `to'.                   |
  398.  *-------------------------------------------------------------------------*/
  399.  
  400. #define OUT_BUFFER_LEN        16384    /* should be a power of 2 */
  401.  
  402. byte *OUT_BUFFER,*OTHER_OUT_BUFFER;
  403.  
  404. int other_buffer_is_full=0;    /* indicates if OTHER_OUT_BUFFER is full */
  405. int output_index=0;        /* next free place in OUT_BUFFER */
  406.  
  407. int alloc_outbuffers(){
  408.     OUT_BUFFER=(byte*)malloc(OUT_BUFFER_LEN);
  409.     OTHER_OUT_BUFFER=(byte*)malloc(OUT_BUFFER_LEN);
  410.     return(OUT_BUFFER==NULL || OTHER_OUT_BUFFER==NULL);
  411. }
  412.  
  413. void write_output(from,len) byte *from; int len;
  414. /* writes `len' tokens to `output_file' with appropriate translation */
  415. {byte token;
  416.     while(len-- > 0){
  417.     switch(token = *from){
  418. case E_LBRACE:        putc('\\',output_file); putc('{',output_file); break;
  419. case E_RBRACE:        putc('\\',output_file); putc('}',output_file); break;
  420. case E_PCT_MARK:    putc('\\',output_file); putc('%',output_file); break;
  421. case E_BACKSLASH:   putc('\\',output_file); putc('\\',output_file); break;
  422. case E_DOLLAR:        putc('\\',output_file); putc('$',output_file); break;
  423. default:        if(token < 128) putc((char)token,output_file);
  424.     }
  425.     from++;
  426.     }
  427. }
  428.  
  429. void store_token(t) int t;    /* puts token `t' into OUT_BUFFER */
  430. {byte *bf;
  431.     OUT_BUFFER[output_index]=t;
  432.     output_index++; output_index &= OUT_BUFFER_LEN-1;
  433.     output_position++;
  434.     if(output_index==0){        /* overturn */
  435.     if(other_buffer_is_full!=0){    /* write OTHER_OUT_BUFFER */
  436.         write_output(OTHER_OUT_BUFFER,OUT_BUFFER_LEN);
  437.     }
  438.     other_buffer_is_full=1; 
  439.     bf=OUT_BUFFER; OUT_BUFFER=OTHER_OUT_BUFFER; OTHER_OUT_BUFFER=bf;
  440.     }
  441. }
  442.  
  443. void store_string(str) char *str; /* stores the elements of the string */
  444. {
  445.     while(*str){ store_token(*str); str++;}
  446. }
  447.  
  448. void flush_output()    /* writes everything out */
  449. {
  450.     if(other_buffer_is_full)
  451.     write_output(OTHER_OUT_BUFFER,OUT_BUFFER_LEN);
  452.     other_buffer_is_full=0;
  453.     write_output(OUT_BUFFER,output_index);
  454.     output_index=0;
  455. }
  456.  
  457. int set_out_position(pos) unsigned pos;
  458. /* erases everything which was written after position `pos' -- if possible */
  459. {unsigned back; byte *bf;
  460.     if(pos<error_position) pos=error_position;    /* keep error messages */
  461.     back=output_position - pos;            /* how much to go back */
  462.     if(back<=(unsigned)output_index){        /* remain in OUT_BUFFER */
  463.     output_index-=back; output_position=pos;
  464.     return(0);
  465.     }
  466.     if(other_buffer_is_full!=0 && back-output_index <= OUT_BUFFER_LEN ){
  467.     other_buffer_is_full=0;
  468.     output_position=pos;
  469.     bf=OUT_BUFFER; OUT_BUFFER=OTHER_OUT_BUFFER; OTHER_OUT_BUFFER=bf;
  470.     output_index=OUT_BUFFER_LEN - (back - output_index);
  471.     return(0);
  472.     }
  473.     return(1);
  474. }
  475.  
  476. int retrieve_out(from,till,to) unsigned from,till; byte *to;
  477. /* copies the output written between positions `from' and `till' into `to' */
  478. {unsigned back,first_part,len;
  479.     back=output_position-from; len=till-from;
  480.     if(back<=(unsigned)output_index){
  481.     strncpy(to,OUT_BUFFER+(output_index-back),len);
  482.     to[len]=0;
  483.     return(0);
  484.     } 
  485.     first_part=back-output_index;
  486.     if(other_buffer_is_full!=0 && first_part <= OUT_BUFFER_LEN){
  487.     if(len<=first_part)
  488.        strncpy(to,OTHER_OUT_BUFFER+(OUT_BUFFER_LEN-first_part),len);
  489.     else {
  490.        strncpy(to,OTHER_OUT_BUFFER+(OUT_BUFFER_LEN-first_part),first_part);
  491.        strncpy(to+first_part,OUT_BUFFER,len-first_part);
  492.     }
  493.     to[len]=0;
  494.     return(0);
  495.     }
  496.     return(1);        /* too long parameter, cannot handle */
  497. }
  498.  
  499. /*=========================================================================*/
  500. /*                           error handling                                */
  501. /*=========================================================================*/
  502.  
  503. /*-------------------------------------------------------------------------*
  504.  | Whenever an error is discovered, `error()' is called with arguments     |
  505.  | similar to `printf(...)' giving larger freedom to include extra infor-  |
  506.  | mation. Error messages are inserted into the output text, ensuring that |
  507.  | they won't be erased later. Messages are also repeated in `error_file'  |
  508.  | (presumably stderr) using a different starting format.           |
  509.  *-------------------------------------------------------------------------*/
  510.  
  511. void error(format,va_alist) char *format; va_dcl
  512. /* writes an error message using a variable number of arguments */
  513. {va_list varargs; char buffer [1024]; byte *bf;
  514.     exit_value=1;            /* inform that we had an error */
  515.     va_start(varargs);
  516.     /**** error message in TeX ****/
  517.     sprintf(buffer,TEX_ERROR_FORMAT,input_file_name,input_line_number);
  518.     store_string(buffer);
  519.     vsprintf(buffer,format,varargs);
  520.     store_string(buffer); store_string("\n");
  521.     ERROR_OUT=CURRENT_OUT;                /* freeze output */
  522.     /**** error message in error_file ****/
  523.     if(error_file!=NULL){
  524.     fprintf(error_file,ERR_ERROR_FORMAT,input_file_name,input_line_number);
  525.     bf=(byte *)&buffer[0];                /* error message */
  526.     while(*bf){
  527.         switch(*bf){
  528. case E_LBRACE:        fprintf(error_file,"\\{"); break;
  529. case E_RBRACE:        fprintf(error_file,"\\}"); break;
  530. case E_PCT_MARK:    fprintf(error_file,"\\%"); break;
  531. case E_BACKSLASH:   fprintf(error_file,"\\\\"); break;
  532. case E_DOLLAR:        fprintf(error_file,"\\$"); break;
  533. default:        if(*bf < ' ') fprintf(error_file,"^%c",'A'-1+*bf);
  534.             else if(*bf < 128) putc((char)*bf,error_file);
  535.         }
  536.         bf++;
  537.     }
  538.     putc('\n',error_file);
  539.     }
  540.     va_end(varargs);
  541. }
  542.  
  543. /*===========================================================================*/
  544. /*                   Storing and retrieving macro text                       */
  545. /*===========================================================================*/
  546.  
  547. /*---------------------------------------------------------------------------*
  548.  | The MACRO structure is used to store all words which occur as macro names |
  549.  | or as mode switch identifiers. The latter ones starting with backlash are |
  550.  | linked separately so that we can search them sequentially whenever a         |
  551.  | backslash character appears in the input. Otherwise the words are         |
  552.  | searched by hashing: words sharing the same hash code are linked together.|
  553.  | Initially macro texts and structures are stored in a reserved space. If   |
  554.  | that space is full, extra space is reserved for each subsequent           |
  555.  | definition.                                     |
  556.  | -- alloc_macro() reserves initial space.                     |
  557.  | -- new_macro(old,word,hashcode) reserves space for a new macro definition |
  558.  |      given the old definition (if applicable), the macro name and the     |
  559.  |      hashcode.                                 |
  560.  | -- set_macro_structure(macro,new_type,left_par,right_par,body_len) fills  |
  561.  |      the reserved `macro' with the given values; allocates space for the  |
  562.  |      macro body.                                 |
  563.  | -- set_modeswitch(macro,display,standalone,out) resets the type of macro  |
  564.  |      with the given values, and inserts into the list of `mode_keywords'. |
  565.  | -- insert_macro() inserts the reserved macro into the hash table.         |
  566.  | -- unlink_macro(old,hashcode) deletes the `old' macro.             |
  567.  | -- search_word(word,hashcode) searches the macro definition for `word'.   |
  568.  | -- check_backslash_keyword(from) looks whether one of the mode_keywords   |
  569.  |      can be found spying ahead.                         |
  570.  *---------------------------------------------------------------------------*/
  571.  
  572. #define PRIME        1999        /* must be a prime, used as the length
  573.                        of the hash table. Other possible
  574.                        values are: 2503, 2999 */
  575. #define TEXT_LENGTH    20000        /* initial length of a text table 
  576.                        to store macro names and bodies */
  577. byte *macro_text;            /* the text table */
  578. unsigned macro_text_length=0;        /* how much is used up of it */
  579.  
  580. #define MACRO_NO    300        /* initial number of MACRO structures*/
  581. struct MACRO *macro;            /* initial array of macros */
  582. int macro_no=1;                /* how many macros are defined */
  583.  
  584. struct MACRO *mode_keywords;        /* linked list of \-keywords */
  585. int next_style_number=SIMPLE_STYLE;    /* next available style number */
  586.  
  587. struct MACRO **hash_table;        /* the HASH table */
  588.  
  589. /*---------------------------------------------------------------------------*/
  590. int alloc_macro()            /* Allocate space for macro handling */
  591. {
  592. static struct MACRO preserve_keyword={  /* the only \-keyword at start */
  593.     K_PRESERVE | K_CHECKLETTER,    /* type */
  594.     0,0,                /* leftpar, rightpar */
  595.     0,                /* style */
  596.     (byte*)T_PRESERVE,        /* name */
  597.     NULL,NULL,NULL            /* body, link, next keyword */
  598.     };
  599.     macro_text=(byte*)malloc(TEXT_LENGTH);
  600.     macro=(struct MACRO*)calloc(MACRO_NO,sizeof(struct MACRO));
  601.     hash_table=(struct MACRO**)calloc(PRIME,sizeof(struct MACRO*));
  602.     if(macro_text==NULL || macro==NULL || hash_table==NULL) return(1);
  603.     macro[0]=preserve_keyword; macro_no=1;
  604.     mode_keywords=¯o[0];
  605.     return(0);
  606. }
  607.  
  608. unsigned new_hashcode=0;        /* local variables to hold info */
  609. struct MACRO *new_macro_entry=NULL;    /* for 'insert_macro' */
  610.  
  611. struct MACRO *new_macro(old,word,hashcode)
  612.         struct MACRO *old; byte *word; unsigned hashcode;
  613. /* makes a new entry to hash_table */
  614. {
  615.     if(macro_no<MACRO_NO){
  616.     new_macro_entry=macro+macro_no; macro_no++;
  617.     } else {
  618.     new_macro_entry=(struct MACRO *)calloc(1,sizeof(struct MACRO));
  619.     if(new_macro_entry==NULL){ error(OUT_OF_MEMORY); return(NULL); }
  620.     }
  621.     new_hashcode=hashcode%PRIME;
  622.     if(old!=NULL) *new_macro_entry = *old;
  623.     else {
  624.     setmem((byte*)new_macro_entry,sizeof(struct MACRO),0);
  625.     if((new_macro_entry->name=(byte *)strdup(word))==NULL){
  626.         error(OUT_OF_MEMORY); return(NULL);
  627.     }
  628.     }
  629.     new_macro_entry->link=NULL;
  630.     return(new_macro_entry);
  631. }
  632.  
  633. void insert_macro()    /* inserts `new_macro_entry' into its place */
  634. {
  635.     if(new_macro_entry==NULL) return;
  636.     new_macro_entry->link=hash_table[new_hashcode];
  637.     hash_table[new_hashcode]=new_macro_entry;
  638. }
  639.  
  640. void unlink_macro(old,hashcode) struct MACRO *old; unsigned hashcode;
  641. /* unlinks "old" from the hash table */
  642. {struct MACRO *k,*k1;
  643.     hashcode%=PRIME; k=hash_table[hashcode];    /* unlink from hash table */
  644.     if(k==old) hash_table[hashcode]=old->link;
  645.     else {
  646.     while(k1=k->link, k1!=NULL && k1!=old) k=k1;
  647.     k->link=old->link;
  648.     }
  649.     if(is_backslash_k(old)){            /* unlink from keyword */
  650.         if(mode_keywords==old) mode_keywords=old->keyword;
  651.     else {
  652.         k=mode_keywords;
  653.         while(k1=k->keyword, k1!=NULL && k1!=old) k=k1;
  654.         k->keyword=old->keyword;
  655.     }
  656.     }
  657. }
  658.  
  659. int set_macro_structure(k,type,left_par,right_par,len)
  660.     struct MACRO *k; int type,left_par,right_par; unsigned len;
  661. /* fills k with the given values */
  662. {
  663.     k->type &= ~K_MATH;        /* clear K_MATH bit */
  664.     if(type) k->type |= K_MATH;    /* set K_MATH bit if necessary */
  665.     k->leftpar=left_par;
  666.     k->rightpar=right_par;
  667.     if(macro_text_length+len < TEXT_LENGTH){    /* reserved memory */
  668.     k->body=macro_text+macro_text_length;
  669.     macro_text_length+=len;
  670.     return(0);
  671.     } 
  672.     if((k->body=(byte *)malloc(len))==NULL){
  673.     error(OUT_OF_MEMORY); return(1);
  674.     }
  675.     return(0);
  676. }
  677.  
  678. void set_modeswitch(s,disp,standalone,out) 
  679.         struct MACRO *s; int disp,standalone,out;
  680. /* sets the appropriate mode for "s". Also puts it on the mode_keyword list */
  681. {int last_char; struct MACRO *k;
  682.     if(s==NULL) return;
  683.     if(out==0) next_style_number++;
  684.     s->style=next_style_number;
  685.     s->type &= ~(K_INOUT | K_MATHDISP | K_STANDALONE);
  686.     if(standalone) s->type |= K_STANDALONE;
  687.     if(disp) s->type |= K_MATHDISP;
  688.     if(out) s->type |= K_INOUT;
  689.     if(*(s->name)=='\\' && !is_backslash_k(s)){    /* starts with backslash */
  690.     last_char=(s->name)[word_length_k(s)-1];
  691.     if(last_char < 128 && isalpha(last_char))
  692.         s->type |= K_CHECKLETTER;
  693.     s->type |= K_BACKSLASH;
  694.     k=mode_keywords;            /* is it on the list ? */
  695.     while(k!=NULL && k!=s)k=k->keyword;
  696.     if(k==NULL){
  697.         s->keyword=mode_keywords;
  698.         mode_keywords=s;
  699.     }
  700.     }
  701. }
  702.  
  703. /*---------------------------------------------------------------------------*/
  704. struct MACRO *search_word(word,hashcode) byte *word; unsigned hashcode;
  705. /* returns the structure whose name agrees with `word', given its hash code. */
  706. {struct MACRO *k;
  707.     k=hash_table[hashcode%PRIME];
  708.     while(k!=NULL){
  709.     if(strcmp(word,k->name)==0) return(k);
  710.     k=k->link;
  711.     }
  712.     return(NULL);
  713. }
  714.  
  715. struct MACRO *check_backslash_keyword(i) int i;
  716. /* returns the structure whose `name' starting at the `i'-th character agrees
  717.    with the spy_string_ahead. */
  718. {struct MACRO *k;
  719.     k=mode_keywords; while(k!=NULL){
  720.     if(spy_string_ahead((char *)((k->name)+i),
  721.         check_letter_k(k) ? FOLLOW_NO_LETTER : FOLLOW_NOTHING))
  722.         return(k); /* found */
  723.     k=k->keyword;
  724.     }
  725.     return(NULL);
  726. }
  727.  
  728. /*=========================================================================*/
  729. /*                          Word handling                                  */
  730. /*=========================================================================*/
  731.  
  732. /*-------------------------------------------------------------------------*
  733.  | Words, i.e. character sequences between white spaces are stored sepa-   |
  734.  | rately (not only in the output buffers); also their hash code is       |
  735.  | computed "on the fly". Macro handling routines got their approproate    |
  736.  | parameters here.                               |
  737.  | -- alloc_word() allocates initial memory.                   |
  738.  | -- clear_word_store() should be called before a new word is dealt with. |
  739.  | -- store_word_token(t) store `t' as the next word constituent.       |
  740.  | -- close_word_store() closes the word.                   |
  741.  | -- prepare_new_macro_entry() the last word is becoming a new macro.     |
  742.  | -- remove_macro() the last word is a macro to be "undefined".       |
  743.  | -- look_up_word() searches the stored word as a macro.           |
  744.  *-------------------------------------------------------------------------*/
  745. #define MAX_WORD_LENGTH        512    /* no longer words are dealt with */
  746. byte *WORD_STORE;            /* tokens of the last word */
  747. int word_store_index;            /* index to WORD_STORE */
  748. unsigned word_hash_code;        /* hash code computed on the fly */
  749.  
  750. int alloc_word()            /* allocates initial memory */
  751. {   WORD_STORE=(byte*)malloc(MAX_WORD_LENGTH);
  752.     return(WORD_STORE==NULL);
  753. }
  754.  
  755. #define clear_word_store()    {word_store_index=0;word_hash_code=952;}
  756.  
  757. void store_word_token(t) int t;
  758. /* stores the word consitutent `t' in `WORD_STORE[]', and computes the
  759.    hash code of the word "in fly". */
  760. {
  761.     WORD_STORE[word_store_index++]=t;
  762.     word_hash_code = ((t+word_hash_code)<<4)+t;
  763.     if(word_store_index==MAX_WORD_LENGTH) word_store_index--;
  764. }
  765.  
  766. #define close_word_store()    {WORD_STORE[word_store_index]=0;}
  767.  
  768. #define prepare_new_macro_entry()  \
  769.         new_macro(S_aux2,WORD_STORE,word_hash_code)
  770.  
  771. #define remove_macro()        unlink_macro(S_aux2,word_hash_code)
  772.  
  773. #define look_up_word()        search_word(WORD_STORE,word_hash_code)
  774.  
  775. /*========================================================================*/
  776. /*                            symbols                                     */
  777. /*========================================================================*/
  778.  
  779. /*------------------------------------------------------------------------*
  780.  | Highest level reading. The input text is broken into "symbol"s which   |
  781.  | are passed to the main loop. A "symbol" is a sequence of tokens; and   |
  782.  | `store_token()' is called with all tokens in it.              |
  783.  | o  SYMBOL  is the type of the symbol read;                  |
  784.  | o  LAST_OUT holds the output position where the tokens forming the      |
  785.  |      last symbol start;                          |
  786.  | o  S_aux1 contains some extra information about the SYMBOL;          |
  787.  | o  S_aux2 is the associated macro definition for the symbol if it is a |
  788.  |      WORD.                                  |
  789.  | o  LAST_TOKEN and last_keyword are auxiliary variables to prevent      |
  790.  |      double parsing of certain sequences.                  |
  791.  | The procedures which are called outside:                  |
  792.  | -- initialize_symbol_reading() should be called first.          |
  793.  | -- next_symbol() produces the next symbol.                  |
  794.  | -- skip_line_as_comment() skips everything till the end of line.      |
  795.  | -- skip_soft_delimiters() reads until the SYMBOL differs from      |
  796.  |       SOFT_DELIMITER.                          |
  797.  *------------------------------------------------------------------------*/
  798.  
  799. int LAST_TOKEN='\n';            /* last token dealt with */
  800. struct MACRO *last_keyword;        /* parsed keyword */
  801.  
  802. #define initialize_symbol_reading()    {LAST_TOKEN='\n';}
  803.  
  804. int check_token(t) int t;        /* checks the type of the next token */
  805. {
  806.     t &= 0xFF;
  807.     if(t<=' ' || is_par(t)) return(0);    /* word boundary */
  808.     switch(t){
  809. case '{': case '}': case '%': case E_EOF:
  810. case '$':  return(0);             /* word boundary */
  811. case '\\': if(in_def_mode() && spy_string_ahead("\\\n",FOLLOW_NOTHING))
  812.         return(0);        /* word boundary */
  813.         return(2);        /* check for keywords */
  814. default:   return(1);            /* word constituent */
  815.     }
  816. }
  817.  
  818. void next_symbol()        /* produces the next SYMBOL */
  819. {int t,lt,len,i; struct MACRO *k;
  820.     LAST_OUT=CURRENT_OUT;        /* where SYMBOL output starts */
  821.     if(in_comment_mode()){        /* read until the end of line */
  822.     while((t=get_next_token())!='\n' && t!=E_EOF) store_token(t);
  823.     input_line_number++;
  824.     if(t==E_EOF) t='\n';
  825.     LAST_TOKEN=t; store_token(t);
  826.     SYMBOL=COMMENT; return;
  827.     }
  828. try_again:                /* after \newline in def mode */
  829.     t=get_next_token(); lt=LAST_TOKEN; LAST_TOKEN=t;
  830.     store_token(t);
  831.     clear_word_store(); store_word_token(t);
  832.     switch(t){
  833. case E_EOF: LAST_TOKEN='\n'; SYMBOL=ENDFILE; return;
  834. case '{': SYMBOL=OPEN; return;
  835. case '}': SYMBOL=CLOSE; return;
  836. case '%': if(in_def_mode()) {SYMBOL=MACRO_DELIM; return;}
  837.     SYMBOL=COMMENT;
  838.     if(lt=='\n'){            /* check for %keywords */
  839.         len=0;
  840.         if(spy_string_ahead(T_DEFINE,FOLLOW_SPACE)){
  841.         len=T_DEFINE_LEN; SYMBOL=DEF_KEYWORD;
  842.         } else if(spy_string_ahead(T_MDEFINE,FOLLOW_SPACE)){
  843.         len=T_MDEFINE_LEN; SYMBOL=MDEF_KEYWORD;
  844.         } else if(spy_string_ahead(T_UNDEFINE,FOLLOW_SPACE)){
  845.         len=T_UNDEFINE_LEN; SYMBOL=UNDEF_KEYWORD;
  846.         } else if(spy_string_ahead(T_MATHMODE,FOLLOW_SPACE)){
  847.         len=T_MATHMODE_LEN; SYMBOL=MATH_KEYWORD;
  848.         } else if(spy_string_ahead(T_DISPMODE,FOLLOW_SPACE)){
  849.         len=T_DISPMODE_LEN; SYMBOL=DISP_KEYWORD;
  850.         }
  851.         if(len>0) skip_tokens(len);
  852.     }
  853.     return;
  854. case ' ': case '\t': S_aux1=0; SYMBOL=SOFT_DELIMITER; return;
  855.     /* S_aux1==0 says that the delimiter vanishes at substitution */
  856. case '\n': input_line_number++;
  857.     S_aux1=1; SYMBOL= lt=='\n' ? EMPTY_LINE : 
  858.              in_def_mode() ? HARD_DELIMITER : SOFT_DELIMITER; 
  859.     return;
  860. case '$':
  861.     if(in_math_mode() && mode_style==SIMPLE_STYLE) /* single $ */
  862.         SYMBOL=MATH_OUT; 
  863.     else if(spy_token_ahead()=='$'){ /* double $$ */
  864.         skip_tokens(1); store_token('$');
  865.         SYMBOL=in_disp_mode() && mode_style==SIMPLE_STYLE ? 
  866.             DISP_OUT : DISP_IN;
  867.     } else SYMBOL=MATH_IN;
  868.     return;
  869. case '\\': /* E_KEYWORD means a \keyword was succesfully parsed */
  870.     k=lt==E_KEYWORD ? last_keyword : check_backslash_keyword(1);
  871.     if(k!=NULL){            /* LAST_TOKEN=='\\' */
  872.         len=word_length_k(k)-1;    /* number of tokens in k */
  873.         for(i=0;i<len;i++) store_token(get_next_token());
  874.         if(is_preserve_k(k)){
  875.         SYMBOL=PRESERVE;
  876.         return;
  877.         }
  878.         S_aux2=k; SYMBOL=WORD; return;
  879.     }
  880.     if(in_def_mode() && spy_token_ahead()=='\n'){
  881.         set_out_position(LAST_OUT);    /* do not store backslash */
  882.         skip_tokens(1);        /* skip over newline */
  883.         input_line_number++;
  884.         goto try_again;
  885.     }
  886. default:
  887.     if(is_par(t)){ S_aux1=extract_par(t); SYMBOL=PARAMETER; return;}
  888.     if(t<' '){ SYMBOL=DELIMITER; return;}
  889.     /* now t is an inner character of a word (maybe `\') */
  890.     SYMBOL=WORD;
  891.     while(1){switch(check_token(t=spy_token_ahead())){
  892.     case 0: /* word boundary, do not advance */
  893.         close_word_store(); S_aux2=look_up_word(); return;
  894.     case 2: /* backslash */
  895.         k=check_backslash_keyword(0);
  896.         if(k!=NULL){ /* a keyword parsed successfully after a WORD */
  897.         last_keyword=k; LAST_TOKEN=E_KEYWORD;
  898.         close_word_store(); S_aux2=look_up_word(); return;
  899.         }
  900.     case 1: /* word constituent */    
  901.         store_token(get_next_token()); store_word_token(t); break;
  902.     }}
  903.     }
  904. }
  905.  
  906. void skip_line_as_comment() 
  907. /* If not at the end of the line, skip the rest of the line. */
  908. {
  909.     if(LAST_TOKEN=='\n'){        /* we've hit the end of line */
  910.     store_token('\n');
  911.     return;
  912.     }
  913.     global_mode |= COMMENT_MODE; next_symbol(); global_mode &= ~COMMENT_MODE;
  914. }
  915.  
  916. void skip_soft_delimiters()
  917. /* go ahead until a not SOFT_DELIMITER is found */
  918. { while(SYMBOL==SOFT_DELIMITER) next_symbol(); }
  919.  
  920. /*=========================================================================*/
  921. /*                  Parameter stack                   */
  922. /*=========================================================================*/
  923.  
  924. /*-------------------------------------------------------------------------*
  925.  | The potential actual parameters of macro calls are stored in a stack.   |
  926.  | The depth of the stack, however, is bounded, and information ad the       |
  927.  | bottom of the stack loses as the stack grows. Each OPEN symbol opens a  |
  928.  | new range, thus the stack shrinks to that point only. Each entry in the |
  929.  | stack has three fields:                           |
  930.  | o  replace: the position from where the text should be erased (white       |
  931.  |         space before the parameter)                       |
  932.  | o  start: output position where the parameter text starts           |
  933.  | o  end: output position until the parameter text starts.           |
  934.  | Procedures handling the parameter stack:                   |
  935.  | -- alloc_params() allocates initial memory.                   |
  936.  | -- push_par(replace,start,end) puts an entry into the stack.           |
  937.  | -- pop_par(replace,start,end) pops the uppermost entry in the stack.       |
  938.  | -- open_range() opens a new range to prevent the stack shrink below.    |
  939.  | -- close_range() closes the range, shrinks the stack until the        |
  940.  |        corresponding open_range.                       |
  941.  | -- shrink_par_stack() shrinks the stack until the last range boundary.  |
  942.  *-------------------------------------------------------------------------*/
  943.  
  944. #define STACK_DEPTH    256
  945. typedef struct STACK { unsigned replace,start,end;};
  946.  
  947. struct STACK *par_stack;
  948. int stack_pointer=0, stack_bottom=0, border_pointer=0;
  949.  
  950. int alloc_params()
  951. {   par_stack=(struct STACK*)calloc(STACK_DEPTH,sizeof(struct STACK));
  952.     return(par_stack==NULL);
  953. }
  954.  
  955. #define    stack_depth()    ((stack_pointer-border_pointer)%STACK_DEPTH)
  956.  
  957. void push_par(replace,start,end) unsigned replace,start,end;
  958. /* pushes the next entry into the stack */
  959. {
  960.     par_stack[stack_pointer].replace=replace;
  961.     par_stack[stack_pointer].start=start;
  962.     par_stack[stack_pointer].end=end;
  963.     stack_pointer++; stack_pointer%=STACK_DEPTH;
  964.     if(stack_pointer==stack_bottom) {
  965.     stack_bottom++; stack_bottom%=STACK_DEPTH;
  966.     if(stack_pointer==border_pointer) border_pointer=stack_bottom;
  967.     }
  968. }
  969.  
  970. int pop_par(replace,start,end) unsigned *replace,*start, *end;
  971. /* pops the next element from the stack */
  972. {
  973.     if(stack_pointer==border_pointer || stack_pointer==stack_bottom) 
  974.     return(1);
  975.     stack_pointer--; stack_pointer%=STACK_DEPTH;
  976.     *replace=par_stack[stack_pointer].replace;
  977.     *start=par_stack[stack_pointer].start;
  978.     *end=par_stack[stack_pointer].end;
  979.     return(0);
  980. }
  981.  
  982. void open_range()    /* opens a new range */
  983. {
  984.     push_par((unsigned)((stack_pointer-border_pointer)%STACK_DEPTH),0,0);
  985.     border_pointer=stack_pointer;
  986. }
  987.  
  988. void close_range()    /* closes a range if possible */
  989. {unsigned bp,dummy;
  990.     stack_pointer=border_pointer; border_pointer++;    /* fool `pop_par' */
  991.     border_pointer= pop_par(&bp,&dummy,&dummy) &&
  992.         bp < (stack_pointer-stack_bottom)%STACK_DEPTH ?
  993.         (stack_pointer-bp)%STACK_DEPTH : stack_bottom;
  994. }
  995.  
  996. #define shrink_par_stack()    {stack_pointer=border_pointer;}
  997.  
  998. /*=========================================================================*/
  999. /*                       Parameter substitution                            */
  1000. /*=========================================================================*/
  1001.  
  1002. /*-------------------------------------------------------------------------*
  1003.  | Parameter substitution is performed here. Descriptions of parameters    |
  1004.  | are popped out of the stack, retrieved from the output buffer into       |
  1005.  | allocated memory; the output buffer is rewind until the `from' position |
  1006.  | and then the macro body is written into output while replacing the      |
  1007.  | formal parameters. At the end the allocated memory is freed.           |
  1008.  *-------------------------------------------------------------------------*/
  1009. unsigned start[10], pend[10];    /* parameter start and end */
  1010. byte *parameters[10];        /* the parameters themselves */
  1011.  
  1012. int macro_substitution(from,k) unsigned *from; struct MACRO *k;
  1013. /* performs the given substitution */
  1014. {unsigned replace; unsigned len; int i,par_no; char *memory; 
  1015.  byte *p,*body,t;
  1016.     par_no=left_pars_k(k)+right_pars_k(k);
  1017.     len=0; memory=NULL; replace = *from;
  1018.     for(i=par_no-1;i>=0;i--){
  1019.     if(pop_par(&replace,&start[i],&pend[i])) return(0);
  1020.     len += pend[i]-start[i]+1;
  1021.     }
  1022.     if(*from < replace) replace = *from;    /* place to replace from */
  1023.     *from=replace;
  1024.     if(len>0){
  1025.     memory=malloc(len); 
  1026.     p=(byte *)memory;
  1027.     if(p==NULL){
  1028.         error(OUT_OF_MEMORY);
  1029.         return(0);
  1030.     }
  1031.     for(i=0;i<par_no;i++){
  1032.         parameters[i]=p;
  1033.         if(retrieve_out(start[i],pend[i],p)){ /* parameter lost */
  1034.         error(TOO_LONG_PARAMETER,k->name);
  1035.         free(memory); return(0);
  1036.         }
  1037.         p+=pend[i]-start[i]+1;
  1038.     }
  1039.     }
  1040.     if(set_out_position(replace)){
  1041.     error(TOO_LONG_PARAMETER,k->name);
  1042.     if(memory!=NULL) free(memory); return(0);
  1043.     }
  1044.     body=k->body;
  1045.     while(t = *body++){
  1046.     if(is_par(t)){
  1047.         p=parameters[extract_par(t)];
  1048.         while(t = *p++) store_token((int)t);
  1049.     } else store_token((int)t);
  1050.     }
  1051.     if(memory!=NULL) free(memory);
  1052.     return(1);
  1053. }
  1054.  
  1055. /*=========================================================================*/
  1056. /*                           Macro definition                               */
  1057. /*=========================================================================*/
  1058.  
  1059. /*-------------------------------------------------------------------------*
  1060.  | This part deals with macro and keyword definitions. All of them vanish  |
  1061.  | from the output text, and are replaced by TeXpp_MACRO_DEFINITION. The   |
  1062.  | macro text is expanded in the output buffer, and copied into the memory |
  1063.  | later. Calling `translate_parameters()' changes all references to the   |
  1064.  | formal parameters from their face value into their position.            |
  1065.  | -- init_macro_definition() saves the old mode, the actual output posi-  |
  1066.  |        tion, and changes into DEFINE_MODE.                   |
  1067.  | -- close_macro_definition() restores the original mode rewinds the       |
  1068.  |        output, and inserts the appropriate text.               |
  1069.  | -- read_macro_definition(type) handles the macro definition. The `type' |
  1070.  |        tells whether the definition was %mdefine (=1) or not (=0).       |
  1071.  | -- undefine_macro() handler the case %undefine. The macro is unliked    |
  1072.  |        both from the hash table and the keyword list.           |
  1073.  | -- define_keyword(type) deals with the %mathmode and %dispmode keywords |
  1074.  *-------------------------------------------------------------------------*/
  1075.  
  1076. int old_mode, old_style;
  1077. unsigned save_out_position, start_macro_text;
  1078. int params[9];
  1079.  
  1080. void translate_parameters(body) byte *body;
  1081. /* replaces parameter #i by its absolute position */
  1082. {byte p;
  1083.     while((p = *body)){
  1084.     if(is_par(p)) *body=make_par(params[extract_par(p)]+'0');
  1085.     body++;
  1086.     }
  1087. }
  1088.  
  1089. void init_macro_definition()
  1090. {int i;
  1091.     old_mode=global_mode; old_style=mode_style; 
  1092.     global_mode=DEFINE_MODE;        /* save old mode, switch to define */
  1093.     save_out_position=CURRENT_OUT;    /* only a single % has been stored */
  1094.     shrink_par_stack();            /* no previous parameter */
  1095.     flush_output();            /* no backtrack beyond this point */
  1096.     for(i=0;i<9;i++)params[i]=0;    /* no parameters defined */
  1097. }
  1098.  
  1099. void close_macro_definition()
  1100. {
  1101.     set_out_position(save_out_position);/* cancel garbage */
  1102.     store_string(TeXpp_MACRO_DEFINITION);
  1103.     skip_line_as_comment();        /* do not deal with the rest */
  1104.     global_mode=old_mode; mode_style=old_style;
  1105.     
  1106. }
  1107.  
  1108. void read_macro_definition(type) int type;
  1109. /* reads a macro definition -- issues appropriate error messages */
  1110. {int result; struct MACRO *k; int left_params,all_params;
  1111.     init_macro_definition();
  1112.     left_params=0;
  1113. next_left_param:            /* read leftist parameters */
  1114.     next_symbol(); skip_soft_delimiters();
  1115.     if(SYMBOL==PARAMETER){
  1116.     if(params[S_aux1]!=0){        /* declared twice */
  1117.         error(PARAMETER_TWICE,S_aux1+1);
  1118.         close_macro_definition(); return;
  1119.     }
  1120.     params[S_aux1]= ++left_params; 
  1121.     goto next_left_param;
  1122.     }
  1123.     if(SYMBOL!=WORD){
  1124.     error(WRONG_MACRO_NAME); close_macro_definition(); return;
  1125.     }
  1126.     k=prepare_new_macro_entry();    /* if NULL, then no memory */
  1127.     if(k==NULL){ close_macro_definition(); return; }
  1128.     all_params=left_params;
  1129. next_right_param:            /* read rightist parameters */
  1130.     next_symbol(); skip_soft_delimiters();
  1131.     if(SYMBOL==PARAMETER){
  1132.     if(params[S_aux1]!=0){         /* declared twice */
  1133.         error(PARAMETER_TWICE,S_aux1+1);
  1134.         close_macro_definition(); return;
  1135.     }
  1136.     params[S_aux1]= ++all_params;
  1137.     goto next_right_param;
  1138.     }
  1139.     if(SYMBOL!=MACRO_DELIM){
  1140.     error(MISSING_DELIMITER); close_macro_definition(); return;
  1141.     }
  1142.     start_macro_text=CURRENT_OUT;
  1143.     if(type!=0){            /* %mdefine */
  1144.     global_mode |= MATH_MODE; mode_style=DEFINE_STYLE;
  1145.     }
  1146.     do{ next_symbol();} while((result=deal_range())==X_CLOSE);
  1147.     if(result==X_ERROR){
  1148.     close_macro_definition(); return;
  1149.     }
  1150.     if(SYMBOL!=MACRO_DELIM) error(MISSING_DELIMITER);
  1151.     if(set_macro_structure( k,type,left_params,all_params-left_params,
  1152.         LAST_OUT-start_macro_text+1)){        /* no more memory */
  1153.     close_macro_definition(); return;
  1154.     }
  1155.     if(retrieve_out(start_macro_text,LAST_OUT,k->body)){
  1156.     error(TOO_LONG_MACRO_DEF,k->name);
  1157.     close_macro_definition(); return;
  1158.     }
  1159.     translate_parameters(k->body);
  1160.     insert_macro();
  1161.     close_macro_definition();
  1162. }
  1163.  
  1164. void undefine_macro()    /* %undefine <macro_name> */
  1165. {
  1166.     init_macro_definition();
  1167.     next_symbol(); skip_soft_delimiters();
  1168.     if(SYMBOL==WORD && S_aux2!=NULL){        /* delete it */
  1169.     remove_macro();
  1170.     } else error(WRONG_MACRO_NAME);
  1171.     close_macro_definition();
  1172. }
  1173.  
  1174. void define_mode_keyword(type) int type;    /* %mathmode or %dispmode */
  1175. {struct MACRO *k1,*k2;
  1176.     init_macro_definition();
  1177.     next_symbol(); skip_soft_delimiters();    /* to mode keyword */
  1178.     if(SYMBOL!=WORD){
  1179.     error(WRONG_MODE_SWITCH_DEF);
  1180.     close_macro_definition();
  1181.     return;
  1182.     }
  1183.     k1=prepare_new_macro_entry(); 
  1184.     insert_macro();                /* puts to the "keywords" */
  1185.     next_symbol(); skip_soft_delimiters();
  1186.     switch(SYMBOL){                /* from mode keyword */
  1187. case MACRO_DELIM: case HARD_DELIMITER:
  1188.     set_modeswitch(k1,type,1,0);        /* single keyword */
  1189.     break;
  1190. case WORD:
  1191.     if(k1==S_aux2) k2=NULL;
  1192.     else {k2=prepare_new_macro_entry(); insert_macro();}
  1193.     next_symbol(); skip_soft_delimiters();
  1194.     if(SYMBOL==MACRO_DELIM || SYMBOL==HARD_DELIMITER){
  1195.         set_modeswitch(k1,type,k2==NULL,0);    /* single keyword ? */
  1196.         set_modeswitch(k2,type,0,1);
  1197.         break;
  1198.     }
  1199. default:
  1200.     error(WRONG_MODE_SWITCH_DEF); break;
  1201.     }
  1202.     close_macro_definition();
  1203. }
  1204.  
  1205. /*=========================================================================*/
  1206. /*              Macro and mode switch handling               */
  1207. /*=========================================================================*/
  1208.  
  1209. /*-------------------------------------------------------------------------*
  1210.  | -- deal_range() reads things between {...} and returns X_CLOSE, X_ERROR |
  1211.  |    or X_OTHER, skipping unbalanced mode switches. At the end does     |
  1212.  |    not advances.                               |
  1213.  | -- set_mode(k) switches into mode as given by struct MACRO parameter k. |
  1214.  |    Returns !=0 if the switch is unbalanced.               |
  1215.  | -- store_mode_block() stores a block enclosed by mode switches. This    |
  1216.  |    behaves as a single (unbreakable) parameter.               |
  1217.  | -- deal_mode_switch(k) decides whether `k' is a also a mode switch. If  |
  1218.  |    not, then pushes it as a paramter. If yes, skips until the closing |
  1219.  |    switch.                                   |
  1220.  | -- deal_word() checks whether the last word is a macro name, or is a    |
  1221.  |    mode switch. Performs the appropriate actions in each case.       |
  1222.  *-------------------------------------------------------------------------*/
  1223.  
  1224. int deal_range()
  1225. /* Reads things between {...} and returns X_CLOSE, X_ERROR or X_OTHER, 
  1226.    skipping unbalanced mode switches. At the end does not advance. */
  1227. {int result;
  1228.     while(1){
  1229.     while((result=store_parameter(1))==X_PARAMETER);
  1230.     if(result==X_ERROR) return(X_ERROR);
  1231.     if(result==X_OTHER){ switch(SYMBOL){
  1232. case CLOSE:    return(X_CLOSE);
  1233. case DELIMITER:    break;            /* allowed withing braces */
  1234. default:    return(X_OTHER);
  1235.     }}
  1236.     shrink_par_stack(); next_symbol();
  1237.     }
  1238. }
  1239.  
  1240. int set_mode(k) struct MACRO *k;
  1241. /* Switches into math or disp mode. Returns !=0 if the switch is wrong. */
  1242. {
  1243.     if(is_standalone_k(k) || is_in_k(k)){
  1244.     global_mode |= is_math_k(k) ? MATH_MODE : DISP_MODE;
  1245.     mode_style=style_k(k);
  1246.     return(0);
  1247.     }
  1248.     return(1);
  1249. }
  1250.  
  1251. int store_mode_block(replace,from,mode_out) 
  1252.     unsigned replace,from; int mode_out;
  1253. /* advances and stores a mode block closed by `mode_out' */
  1254. {int result;
  1255.     open_range(); next_symbol();
  1256.     while((result=store_parameter(1))==X_PARAMETER);
  1257.     close_range();
  1258.     if(result!=mode_out) return(result);
  1259.     push_par(replace,from,CURRENT_OUT);
  1260.     return(X_PARAMETER);
  1261. }
  1262.  
  1263. int deal_mode_switch(k,replace,from) 
  1264.     struct MACRO *k; unsigned replace,from;
  1265. /* checks whether the last word is also a mode switch */
  1266. {
  1267.     if(k==NULL || !is_modeswitch_k(k)){        /* not a mode switch */
  1268.     push_par(replace,from,CURRENT_OUT);
  1269.     return(X_PARAMETER);
  1270.     }
  1271.     if(in_plain_mode()){            /* switch to mode */
  1272.     if(set_mode(k)){            /* wrong switch */
  1273.         error(WRONG_MODE_SWITCH,k->name);
  1274.         return(X_XMODE_OUT);
  1275.     }
  1276.     return(store_mode_block(replace,from,X_XMODE_OUT));
  1277.     }
  1278.     if(mode_style!=style_k(k) || (!is_standalone_k(k) && is_in_k(k))){
  1279.     error(WRONG_MODE_SWITCH,k->name);
  1280.     set_plain_mode(); set_mode(k);
  1281.     } else set_plain_mode();
  1282.     return(X_XMODE_OUT);
  1283. }
  1284.  
  1285. int deal_word(replace_from,advance) unsigned replace_from; int advance;
  1286. /* Checks whether the word is a macro name. Also checks for mode switch. */
  1287. {struct MACRO *k; int i,replaced,result,right_pars; 
  1288.     k=S_aux2;
  1289.     if(k==NULL || body_k(k)==NULL || (is_math_macro(k) && in_plain_mode())){
  1290.     replaced=0;
  1291.     result=deal_mode_switch(k,replace_from,LAST_OUT);
  1292.     } else {                    /* macro name */
  1293.     if(stack_depth() < left_pars_k(k)) {
  1294.         error(TOO_LESS_LEFT_PARAMS,left_pars_k(k),k->name);
  1295.         return(X_ERROR);
  1296.     }
  1297.     right_pars=right_pars_k(k);
  1298.     if(right_pars>0) next_symbol();
  1299.     result=X_PARAMETER;
  1300.     for(i=1; result==X_PARAMETER && i<=right_pars;i++)
  1301.         result=store_parameter(i<right_pars);
  1302.     if(result!=X_PARAMETER){
  1303.         error(TOO_LESS_PARAMS,left_pars_k(k)+right_pars_k(k),k->name);
  1304.         return(X_ERROR);
  1305.     }
  1306.     replaced=macro_substitution(&replace_from,k);
  1307.     result=deal_mode_switch(k,replace_from,replace_from);
  1308.     }
  1309.     if(result==X_PARAMETER && advance){
  1310.     replaced &= SYMBOL!=CLOSE;    /***** ?????? ******/
  1311.     next_symbol();            /* skip whitespace after a WORD */
  1312.     if(replaced && SYMBOL==SOFT_DELIMITER && S_aux1==0){
  1313.         set_out_position(LAST_OUT); next_symbol();
  1314.     }
  1315.     }
  1316.     return(result);
  1317. }
  1318.  
  1319. /*=========================================================================*/
  1320. /*               Reading parameters                   */
  1321. /*=========================================================================*/
  1322.  
  1323. /*-------------------------------------------------------------------------*
  1324.  | -- skip_balanced_expression() used skipping a {...} parameter for the   |
  1325.  |    keyword \preserve.                           |
  1326.  | -- skip_word() skips until the next SOFT_DELIMITER after \preserve.       |
  1327.  | -- store_parameter(advance) stores and handles the next SYMBOL. If       |
  1328.  |    `advance' is TRUE (!=0) then reads ahead one more SYMBOL.       |
  1329.  *-------------------------------------------------------------------------*/
  1330.  
  1331. void skip_balanced_expression()    /* skip until an unbalanced CLOSE */
  1332. {int level=0;
  1333.     while(1){
  1334.     next_symbol(); switch(SYMBOL){
  1335. case HARD_DELIMITER: case EMPTY_LINE: case ENDFILE:
  1336.         return;
  1337. case OPEN:  level++; break;
  1338. case CLOSE: level--; if(level<0) return;
  1339. default:    break;
  1340.     }
  1341.     }
  1342. }
  1343.  
  1344. void skip_word()        /* skips a word after \preserve */
  1345. {
  1346.     while(1){ switch(SYMBOL){
  1347. case SOFT_DELIMITER: case HARD_DELIMITER: case EMPTY_LINE: case ENDFILE:
  1348.     return;
  1349. default:
  1350.     next_symbol(); break;
  1351.     }}
  1352. }
  1353.  
  1354. int store_parameter(advance) int advance;
  1355. /* Stores a single parameter. If returns !=0 or advance==0 then does not 
  1356.    advances */
  1357. {unsigned replace_from,start; int whitespace; int result;
  1358.     whitespace=0;
  1359. again:
  1360.     if(whitespace==0) replace_from=LAST_OUT;
  1361.     switch(SYMBOL){
  1362. case SOFT_DELIMITER: /* if S_aux1==0  the delimiter vanishes at substitution */
  1363.     if(S_aux1==0){ whitespace=1; replace_from=LAST_OUT;}
  1364.     else whitespace=0;
  1365.     next_symbol();
  1366.     goto again;
  1367. case WORD:
  1368.     return(deal_word(replace_from,advance));
  1369. case PARAMETER:    /* formal parameter in macro text */
  1370.     if(params[S_aux1]==0){
  1371.         error(UNDEFINED_PARAMETER,1+S_aux1);
  1372.         return(X_ERROR);
  1373.     }
  1374.     push_par(replace_from,LAST_OUT,CURRENT_OUT);
  1375.     if(advance) next_symbol();
  1376.     return(X_PARAMETER);
  1377. case PRESERVE:    /* \preserve keyword */
  1378.     start=LAST_OUT;
  1379.     do{ set_out_position(LAST_OUT); next_symbol();}
  1380.         while(SYMBOL==SOFT_DELIMITER);    /* skip soft delimiters */
  1381.     if(SYMBOL==OPEN){    /* skip until the corresponding CLOSE */
  1382.         set_out_position(LAST_OUT);        /* do not copy OPEN */
  1383.         skip_balanced_expression();
  1384.     } else skip_word();
  1385.     set_out_position(LAST_OUT);        /* do not copy CLOSE */
  1386.     if(advance) next_symbol();
  1387.     push_par(replace_from,start,LAST_OUT);
  1388.     return(X_PARAMETER);
  1389. case MATH_IN: case DISP_IN:
  1390.     if(!in_plain_mode()){
  1391.         error(WRONG_DOLLAR_SWITCH);
  1392.         set_plain_mode();
  1393.     }
  1394.     global_mode|= SYMBOL==MATH_IN ? MATH_MODE : DISP_MODE;
  1395.     mode_style=SIMPLE_STYLE;
  1396.     result=store_mode_block(replace_from,LAST_OUT,X_DMODE_OUT);
  1397.     if(result==X_PARAMETER && advance) next_symbol();
  1398.     return(result);
  1399. case MATH_OUT:                    /* do not advance! */
  1400.     if(!in_math_mode() || mode_style!=SIMPLE_STYLE){
  1401.         error(WRONG_CLOSING_DOLLAR);
  1402.     }
  1403.     set_plain_mode();
  1404.     return(X_DMODE_OUT);
  1405. case DISP_OUT:                    /* do not advance! */
  1406.     if(!in_disp_mode() || mode_style!=SIMPLE_STYLE){
  1407.         error(WRONG_CLOSING_DOLLAR);
  1408.     }
  1409.     set_plain_mode();
  1410.     return(X_DMODE_OUT);
  1411. case OPEN:
  1412.     replace_from=LAST_OUT; start=CURRENT_OUT;
  1413.     open_range();
  1414.     next_symbol();                /* advance */
  1415.     result=deal_range();
  1416.     close_range();
  1417.     if(result!=X_CLOSE) return(result);
  1418.     push_par(replace_from,start,LAST_OUT);
  1419.     if(advance) next_symbol();        /* what comes after CLOSE */
  1420.     return(X_PARAMETER);
  1421. default:                    /* do not advance! */
  1422.     return(X_OTHER);
  1423.     }
  1424. }
  1425.  
  1426. /*=========================================================================*/
  1427. /*                Main cycle                   */
  1428. /*=========================================================================*/
  1429.  
  1430. /*-------------------------------------------------------------------------*
  1431.  | read_file() is the main cycle of the program. It is called with all       |
  1432.  | input files. The procedure reads in a cycle until the end of the file,  |
  1433.  | flushing all the output and shrinking the parameter stack in each       |
  1434.  | iteration. Macro parameters cannot go over these constructs, e.g. empty |
  1435.  | line, comment, TeX commands, etc.                       |
  1436.  *-------------------------------------------------------------------------*/
  1437. void read_file()            /* goes through a file */
  1438. {int result;
  1439.     initialize_token_reading();
  1440.     initialize_symbol_reading();
  1441. again:
  1442.     shrink_par_stack();            /* no previous parameter */
  1443.     flush_output();            /* no backtrack beyond this point */
  1444.     next_symbol();            /* first symbol to read */
  1445.     while((result=store_parameter(1))==X_PARAMETER);
  1446.                         /* read until can */
  1447.     if(result!=X_OTHER) goto again;
  1448.     shrink_par_stack();            /* no parameters to deal with */
  1449.     switch(SYMBOL){            /* what caused the trouble */
  1450. case EMPTY_LINE:
  1451.     if(!in_plain_mode()){        /* check math and disp mode */
  1452.         error(EMPTY_LINE_IN_MODE,in_math_mode() ? "math" : "display");
  1453.         set_plain_mode();
  1454.     }
  1455.     goto again;
  1456. case ENDFILE:                /* end of everything */
  1457.     if(!in_plain_mode()){
  1458.         error(ENDFILE_IN_MODE,in_math_mode() ? "math":"display");
  1459.         set_plain_mode();
  1460.     }
  1461.     break;
  1462. case COMMENT:                /* a % sign somewhere */
  1463.     skip_line_as_comment();
  1464.     goto again;
  1465. case DELIMITER:                /* control character */
  1466. case CLOSE:                /* unmatched closing bracket */
  1467.     goto again;
  1468. case DEF_KEYWORD:            /* %define */
  1469.     read_macro_definition(0);
  1470.     goto again;
  1471. case MDEF_KEYWORD:            /* %mdefine */
  1472.     read_macro_definition(1);
  1473.     goto again;
  1474. case UNDEF_KEYWORD:            /* %undefine <name> */
  1475.     undefine_macro();
  1476.     goto again;
  1477. case MATH_KEYWORD:            /* %matmode <in> <out> */
  1478.     define_mode_keyword(0);
  1479.     goto again;
  1480. case DISP_KEYWORD:            /* %dispmode <in> <out> */
  1481.     define_mode_keyword(1);
  1482.     goto again;
  1483. /*** case MATH_IN: case MATH_OUT: case DISP_IN: case DISP_OUT: case PRESERVE:
  1484.      case HARD_DELIMITER: case OPEN: case WORD: case MACRO_DELIM: 
  1485.      case PARAMETER: case SOFT_DELIMITER: ***/
  1486. default:    /* something which should not occur */
  1487.     fprintf(stderr,"Unreachable symbol: %d\n"); break;
  1488.     }
  1489. }
  1490.  
  1491. /*=========================================================================*/
  1492. /*                        Command line arguments                           */
  1493. /*=========================================================================*/
  1494.  
  1495. /*-------------------------------------------------------------------------*
  1496.  | Arguments in the command line are the files which are to be processed.  |
  1497.  | Argument STDIN_ARG means the standard input; a file name preceeded by   |
  1498.  | WRITE_ARG or APPEND_ARG is considered the output file. If no output       |
  1499.  | file is present then the output goes to the standard output. In this    |
  1500.  | latter case error messages are not repeated at stderr.           |
  1501.  *-------------------------------------------------------------------------*/
  1502. #define WRITE_ARG    "-w"
  1503. #define APPEND_ARG    "-a"
  1504. #define STDIN_ARG    "-"
  1505. #define HELP_ARG    "-h"
  1506.  
  1507. FILE *find_output(argc,argv) int argc; char *argv[];
  1508. /* searches the argument list for NOPAR_ARG */
  1509. {FILE *f; int i,found;
  1510.     for(i=1;i<argc;i++){
  1511.     found = stricmp(argv[i],WRITE_ARG)==0 ? 1 :
  1512.         stricmp(argv[i],APPEND_ARG)==0 ? 2 : 0;
  1513.     if(found!=0){
  1514.         i++;
  1515.         if(i==argc){
  1516.         fprintf(stderr,"output file is missing\n");
  1517.         f=NULL;
  1518.         } else{
  1519.         f=fopen(argv[i],found==1 ? "w" : "a");
  1520.         if(f==NULL) 
  1521.            fprintf(stderr,"cannot open or create file %s\n",argv[i]);
  1522.         }
  1523.         return(f);
  1524.     }
  1525.     }
  1526.     return(stdout);
  1527. }
  1528.  
  1529. FILE *arguments(i,argc,argv) int *i,argc; char *argv[];
  1530. /*-------------------------------------------------------------------------*
  1531.  | gives back the i-th argument file, or NULL if error, or no more. Skips  |
  1532.  | `-nopar' arguments, and increases i. Should be called with *i=0 fist.   |
  1533.  | Also initializes `input_file_name' and `input_line_number'.             |
  1534.  *-------------------------------------------------------------------------*/
  1535. {FILE *f; int firstcall;
  1536.     firstcall= (*i)++ == 0;    /* this indicates the first call */
  1537.     input_line_number=1;    /* start new file */
  1538.     input_file_name="stdin";    /* default value */
  1539.     while(*i < argc){
  1540.     if(stricmp(argv[*i],WRITE_ARG)==0) (*i)+=2;
  1541.     else if(stricmp(argv[*i],APPEND_ARG)==0) (*i)+=2;
  1542.     else if(stricmp(argv[*i],STDIN_ARG)==0){
  1543.         return(stdin);
  1544.     } else {
  1545.         input_file_name=argv[*i];
  1546.         f=fopen(input_file_name,"r");
  1547.         if(f==NULL) error(CANNOT_OPEN_FILE);
  1548.         return(f);
  1549.     }
  1550.     }
  1551.     return(firstcall ? stdin : NULL); /* no more parameters */
  1552. }
  1553.  
  1554. int on_line_help(argc,argv) int argc; char *argv[];
  1555. /*-------------------------------------------------------------------------*
  1556.  | gives some useless one line help.                       |
  1557.  *-------------------------------------------------------------------------*/
  1558. {   if(argc==2 && stricmp(argv[1],HELP_ARG)==0){
  1559.     fprintf(stderr,"usage:  %s input_files -[aw] output_file\n",argv[0]);
  1560.     return(1);
  1561.     }
  1562.     return(0);
  1563. }
  1564.  
  1565. /*=========================================================================*/
  1566. /*                          Main routine                                   */
  1567. /*=========================================================================*/
  1568.  
  1569. int main(argc,argv) int argc; char *argv[];
  1570. {int input_file_no;
  1571.     if(on_line_help(argc,argv)) return(0);
  1572.     if(alloc_outbuffers()||alloc_macro()||alloc_word()||alloc_params()){
  1573.     fprintf(stderr,"Not enough memory to run %s\n",argv[0]);
  1574.     return(1);
  1575.     }
  1576.     output_file=find_output(argc,argv);
  1577.     if(output_file==NULL) return(1);
  1578.     error_file=output_file==stdout ? NULL : stderr;
  1579.     input_file_no=0; exit_value=0; set_plain_mode();
  1580.     while((input_file=arguments(&input_file_no,argc,argv))!=NULL){
  1581.     /* for each file on the argument list */
  1582.     read_file();
  1583.     if(input_file!=stdin) fclose(input_file);
  1584.     }
  1585.     flush_output();
  1586.     return(exit_value);
  1587. }
  1588.  
  1589.