home *** CD-ROM | disk | FTP | other *** search
/ Garbo / Garbo.cdr / pc / source / texpp.zoo / texpp.2 < prev   
Encoding:
Text File  |  1990-04-04  |  62.8 KB  |  1,630 lines

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