home *** CD-ROM | disk | FTP | other *** search
- /***************************************************************************
- * *
- * texpp TeX preprocessor, Version 1.2. *
- * Laci Csirmaz, DIMACS - Rutgers *
- * Feb. 25, 1990 *
- * *
- ***************************************************************************
-
- You are granted to use, modify, copy, redistribute this program any
- way you want. However no warranties are made for this program or the
- accopanying documentation.
-
- To compile the program simply invoke cc by typing
- cc texpp.c -o texpp
- On certain computers the 'strdup()' function is missing from the standard
- C library. In this case, recompile the preprocessor by
- cc -DSTRDUP texpp.c -o texpp
-
- Please send your comments, suggestions, etc. to:
- csirmaz@cs.rutgers.edu
-
- ***************************************************************************/
-
-
- /*-------------------------------------------------------------------------*
- | include files |
- *-------------------------------------------------------------------------*/
- #include <ctype.h>
- #include <malloc.h>
- #include <string.h>
- #include <strings.h>
- #include <stdio.h>
- #include <varargs.h>
-
- /*-------------------------------------------------------------------------*
- | prototypes not in UNIX |
- *-------------------------------------------------------------------------*/
- #define byte unsigned char /* define new mode */
- char *calloc(); char *sprintf();
-
- /*-------------------------------------------------------------------------*
- | mode and style |
- *-------------------------------------------------------------------------*/
- #define MATH_MODE 0x01
- #define DISP_MODE 0x02
- #define DEFINE_MODE 0x04
- #define COMMENT_MODE 0x08
-
- #define SIMPLE_STYLE 0 /* style for `$' or `$$' */
- #define DEFINE_STYLE (-1) /* style for %mdefine */
-
- int global_mode; /* mode flags - in math, display mode;
- skipping a comment, or reading a TeXpp
- definition. */
- int mode_style; /* MATH and DISP style number to distinguish
- between different pairs of math and display
- mode switches. */
-
- #define in_comment_mode() (global_mode&COMMENT_MODE)
- #define in_def_mode() (global_mode&DEFINE_MODE)
- #define in_disp_mode() ((global_mode&DISP_MODE)!=0)
- #define in_math_mode() ((global_mode&MATH_MODE)!=0)
- #define in_plain_mode() ((global_mode&(DISP_MODE|MATH_MODE))==0)
- #define set_plain_mode() {global_mode&= ~(DISP_MODE|MATH_MODE);}
- #define set_disp_mode() {global_mode |= DISP_MODE;}
- #define set_math_mode() {global_mode |= MATH_MODE;}
-
- /*--------------------------------------------------------------------------*/
- /* input/output variables */
- /*--------------------------------------------------------------------------*/
- FILE *input_file; /* stream to read from */
- FILE *output_file; /* stream to write to */
- FILE *error_file; /* stream for error messages */
- char *input_file_name; /* the actual input file name */
- int input_line_number; /* which line we are in */
-
- int exit_value=0; /* 1 if an error occured */
-
- unsigned output_position=0; /* where the next token goes */
- unsigned last_output_position=0;/* saved output_position */
- unsigned error_position=0; /* end of last error position */
-
- #define LAST_OUT (last_output_position)
- #define CURRENT_OUT (output_position)
- #define ERROR_OUT (error_position)
-
- /*-------------------------------------------------------------------------*
- | characters used as special tokens |
- *-------------------------------------------------------------------------*/
- #define E_LBRACE ('{'|0x80) /* replaces \{ */
- #define E_RBRACE ('}'|0x80) /* replaces \} */
- #define E_PCT_MARK ('%'|0x80) /* replaces \% */
- #define E_BACKSLASH ('\\'|0x80) /* replaces \\ */
- #define E_DOLLAR ('$'|0x80) /* replaces \$ */
- #define E_EOF 255 /* replaces EOF */
- #define E_KEYWORD 254 /* keyword ahead */
-
- #define make_par(x) (((x)-'1')|0x80)/* replaces #1 ... #9 */
- #define extract_par(x) ((x)-0x80)
- #define is_par(x) (0x80<=(x)&&(x)<0x89)
-
- /*-------------------------------------------------------------------------*
- | Storing and retrieving macro text |
- *-------------------------------------------------------------------------*/
- typedef struct MACRO {
- byte type; /* flags */
- byte leftpar,rightpar; /* number of left and right pars */
- int style; /* style if mode switch word */
- byte *name; /* macro name */
- byte *body; /* macro body, can be NULL */
- struct MACRO *link; /* pointer to the next macro */
- struct MACRO *keyword; /* pointer to the next \-keyword */
- };
-
- /*--------------------------- MACRO flags ---------------------------------*/
- #define K_MATH 0x01 /* 1 for MATH and DISP mode only */
- #define K_PRESERVE 0x02 /* 1 for \preserve keyword */
- #define K_BACKSLASH 0x04 /* 1 if starts with backslash */
- #define K_CHECKLETTER 0x08 /* 1 if cannot be followed by a letter */
- #define K_INOUT 0x10 /* bit for IN (0) or OUT (1) mode switch */
- #define K_MATHDISP 0x20 /* bit for MATH (0) or DISP (1) mode switch */
- #define K_STANDALONE 0x40 /* 1 for identical IN and OUT mode switch */
-
- #define is_math_macro(x) ((x)->type & K_MATH)
- #define word_length_k(x) strlen((char *)((x)->name))
- #define style_k(x) ((x)->style)
- #define body_k(x) ((x)->body)
- #define left_pars_k(x) ((x)->leftpar)
- #define right_pars_k(x) ((x)->rightpar)
- #define is_preserve_k(x) ((x)->type & K_PRESERVE)
- #define is_backslash_k(x) ((x)->type & K_BACKSLASH)
- #define is_modeswitch_k(x) ((x)->style > 0)
- #define is_math_k(x) (((x)->type & K_MATHDISP)==0)
- #define is_in_k(x) (((x)->type & K_INOUT)==0)
- #define is_standalone_k(x) ((x)->type & K_STANDALONE)
- #define check_letter_k(x) ((x)->type & K_CHECKLETTER)
-
- /*-------------------------------------------------------------------------*
- | symbols |
- *-------------------------------------------------------------------------*/
- #define SOFT_DELIMITER 1 /* space, tab, newline */
- #define HARD_DELIMITER 2 /* newline in DEF_MODE */
- #define DELIMITER 3 /* control character, not soft delimiter */
- #define MACRO_DELIM 4 /* macro text delimiter in DEF_MODE */
- #define MATH_IN 5 /* entering math mode */
- #define MATH_OUT 6 /* leaving math mode */
- #define DISP_IN 7 /* entering displayed mode */
- #define DISP_OUT 8 /* leaving displayed mode */
- #define PRESERVE 9 /* \preserve keyword */
- #define DEF_KEYWORD 10 /* %define keyword */
- #define MDEF_KEYWORD 11 /* %mdefine keyword */
- #define UNDEF_KEYWORD 12 /* %undefine keyword */
- #define MATH_KEYWORD 13 /* %mathmode keyword */
- #define DISP_KEYWORD 14 /* %dispmode keyword */
- #define COMMENT 15 /* comment in a line */
- #define EMPTY_LINE 16 /* empty line, cannot be in a macro */
- #define WORD 17 /* a word of visible characters */
- #define OPEN 18 /* { */
- #define CLOSE 19 /* } */
- #define ENDFILE 20 /* at end of file */
- #define PARAMETER 21 /* #1 .. #9 */
-
- int SYMBOL; /* the last symbol */
- int S_aux1; /* if SYMBOL==SOFT_DELIMITER then S_aux1=0
- says that the delimiter vanishes at
- substitution; if SYMBOL==PARAMETER then the
- parameter's value (0..8) */
- struct MACRO *S_aux2; /* if SYMBOL==WORD then the corresponding
- MACRO entry, or NULL if none */
-
- /*-------------------------------------------------------------------------*
- | Preprocessor units |
- *-------------------------------------------------------------------------*/
- #define X_PARAMETER 0 /* a parameter */
- #define X_DMODE_OUT 1 /* $ or $$ leaving mode */
- #define X_XMODE_OUT 2 /* other mode closing symbol */
- #define X_CLOSE 3 /* closing brace */
- #define X_ERROR 4 /* error encountered */
- #define X_OTHER 5 /* other special symbol */
-
- /*-------------------------------------------------------------------------*
- | TeX and TeXpp texts |
- *-------------------------------------------------------------------------*/
- #define T_DEFINE "define" /* TeXpp keywords after % */
- #define T_DEFINE_LEN 6
- #define T_MDEFINE "mdefine"
- #define T_MDEFINE_LEN 7
- #define T_UNDEFINE "undefine"
- #define T_UNDEFINE_LEN 8
- #define T_MATHMODE "mathmode"
- #define T_MATHMODE_LEN 8
- #define T_DISPMODE "dispmode"
- #define T_DISPMODE_LEN 8
-
- #define T_PRESERVE "\\preserve" /* should start with backslash!!! */
- #define T_PRESERVE_LEN 9
-
- #define TeXpp_MACRO_DEFINITION "%%% TeXpp macro definition %"
- /* replacement text for TeXpp macro definition */
-
- /*-------------------------------------------------------------------------*
- | error message texts |
- *-------------------------------------------------------------------------*/
- #define TEX_ERROR_FORMAT "%%%%%%TeXpp error in %s line %d: "
- /* used as a format to insert error message into TeX text */
- #define ERR_ERROR_FORMAT "Error in %s line %d: "
- /* used as a format to write error message into stderr */
-
- #define CANNOT_OPEN_FILE "cannot open the file"
- #define WRONG_FORMAL_PARAMETER "no digit after #-mark"
- #define PARAMETER_TWICE "parameter #%d declared twice"
- #define WRONG_MACRO_NAME "macro name expected"
- #define WRONG_MODE_SWITCH_DEF "wrong mode switch keyword definition"
- #define MISSING_DELIMITER "missing macro text delimiter %% "
- #define TOO_LESS_LEFT_PARAMS "less than %d left parameters for %s "
- #define TOO_LESS_PARAMS "less than %d parameters for %s "
- #define TOO_LONG_MACRO_DEF "too long definition for macro %s "
- #define TOO_LONG_PARAMETER "too long parameter for macro %s "
- #define UNDEFINED_PARAMETER "parameter #%d wasn't declared"
- #define WRONG_DOLLAR_SWITCH "erroneous $ mode switch"
- #define WRONG_CLOSING_DOLLAR "erroneous closing $ mode switch"
- #define EMPTY_LINE_IN_MODE "empty line in %s mode"
- #define ENDFILE_IN_MODE "end of file in %s mode"
- #define WRONG_MODE_SWITCH "erroneous %s mode switch"
- #define OUT_OF_MEMORY "no more memory"
-
- void error(); /*VARARGS1*/ /* just to clean up things */
-
- /*=========================================================================*/
- /* standard procedures not in UNIX */
- /*=========================================================================*/
- #define upper(x) ((x)|0x20) /* convert letters to uppercase */
-
- int stricmp(left,right) char *left,*right;
- /* compares strings with no case -- works only with ASCII characters */
- {
- while(*left != 0 && (*left == *right ||
- (isalpha(*left) && isalpha(*right) && upper(*left)==upper(*right)))
- ) { left++; right++;}
- return(*left-*right);
- }
-
- void setmem(to,len,c) byte *to; unsigned len; byte c;
- /* fills `len' bytes starting from `to' by the value of `c' */
- {unsigned i;
- for(i=0;i<len;i++) *to++=c;
- }
-
- #ifdef STRDUP
- char *strdup(s) char *s; /* duplicates s */
- {char *dup;
- dup=malloc(1+strlen(s));
- if(dup!=NULL) strcpy(dup,s);
- return(dup);
- }
- #endif
-
- /*=========================================================================*/
- /* token input */
- /*=========================================================================*/
-
- /*-------------------------------------------------------------------------*
- | The lowest level of the three-level reading is the immediate character |
- | input from `input_file'. Procedure `next_char()' filters out incoming |
- | characters ==0 and >126, returns E_EOF on end of file, and makes some |
- | local translations depending on the mode stored in `global_mode': |
- | o in COMMENT_MODE, all characters are returned. In |
- | o in other modes the pairs \{ \} \$ \% and \\ are coded as single chars|
- | o in DEFINE_MODE pairs #1 .. #9 are recognized as parameters; and ## |
- | is replaced by a single # char. |
- *-------------------------------------------------------------------------*/
-
- int next_char() /* lowest level reading from `input_file' */
- {int c,c1;
- while((c=getc(input_file))==0 || c>0x7E);/* skip 0 and >126 chars */
- if(c<0) return(E_EOF); /* here is the end of the file */
- if(in_comment_mode()) return(c); /* skipping a comment */
- if(c=='\\'){ /* the char is backslash */
- switch(c1=getc(input_file)){ /* next char */
- case '%': c=E_PCT_MARK; break;
- case '{': c=E_LBRACE; break;
- case '}': c=E_RBRACE; break;
- case '\\': c=E_BACKSLASH; break;
- case '$': c=E_DOLLAR; break;
- default: ungetc(c1,input_file); break;/* simply put back the ahead char */
- }
- } else if(c=='#' && in_def_mode()){ /* check formal parameters */
- c1=getc(input_file);
- if('1'<=c1 && c1<='9') c=make_par(c1);
- else if(c1!='#'){
- error(WRONG_FORMAL_PARAMETER);
- ungetc(c1,input_file);
- }
- }
- return(c);
- }
-
- /*-------------------------------------------------------------------------*
- | On the medium level, values given by `next_char()' are passed over as |
- | tokens. But tokens can be read AHEAD, so special procedures are used to |
- | deal with them. The circular buffer `token_ahead[]' stores the tokens |
- | read ahead; its size must be a power of 2. Procedures handling tokes: |
- | -- initialize_token_reading() should be called first. |
- | -- spy_token() returns the next token but does not advances ahead. |
- | -- spy_string_ahead() returns TRUE(!=0) if the next item agrees with |
- | the parameter, and checks whether the item following the string is |
- | a white space or is NOT a letter (to agree with TeX's backslash |
- | convention). |
- | -- get_next_token() simply returns the next character. |
- | -- skip_tokens(n) skips `n' tokens ahead. |
- | To comply with the mode dependent character reading, the spied chars |
- | should not change the mode -- so be careful when spying ahead ... |
- *-------------------------------------------------------------------------*/
-
- #define MAX_TOKEN_AHEAD 128 /* must be a power of 2 */
- byte token_ahead[MAX_TOKEN_AHEAD]; /* circular buffer */
- int token_ahead_in=0, token_ahead_out=0; /* buffer pointers */
-
- #define initialize_token_reading() {token_ahead_in=token_ahead_out=0;}
-
- byte spy_token_ahead() /* Returns the next token but does not advances */
- {
- if(token_ahead_in==token_ahead_out){ /* ahead buffer is empty */
- token_ahead[token_ahead_in]=next_char();
- token_ahead_in=(token_ahead_in+1)&(MAX_TOKEN_AHEAD-1);
- }
- return(token_ahead[token_ahead_out]);
- }
-
- #define FOLLOW_NOTHING 0
- #define FOLLOW_NO_LETTER 1
- #define FOLLOW_SPACE 2
-
- int spy_string_ahead(str,follow_up) char *str; int follow_up;
- {int t,i,same; byte tt;
- t=token_ahead_out; same=1;
- while(same && (*str || follow_up)){
- if(t==token_ahead_in){ /* should read ahead */
- i=(token_ahead_in+1)&(MAX_TOKEN_AHEAD-1);
- if(i!=token_ahead_out){
- token_ahead[t]=next_char(); token_ahead_in=i;
- } else return(0); /* ahead buffer is full, not found */
- }
- tt=token_ahead[t];
- if(*str){
- same=((unsigned char)(*str))==tt;
- str++; t=(t+1)&(MAX_TOKEN_AHEAD-1);
- } else {
- same=follow_up==FOLLOW_NO_LETTER ? ((tt > 127) || !isalpha(tt)) :
- (tt==' ' || tt=='\t');
- follow_up=0;
- }
- }
- return(same);
- }
-
- int get_next_token() /* gives the next token */
- {byte res;
- if(token_ahead_in==token_ahead_out)
- return(next_char());
- res=token_ahead[token_ahead_out];
- token_ahead_out=(token_ahead_out+1)&(MAX_TOKEN_AHEAD-1);
- return(res);
- }
-
- void skip_tokens(n) int n; /* skips the next `n' subsequent tokens */
- {int stored;
- stored=(token_ahead_in+MAX_TOKEN_AHEAD-token_ahead_out)
- &(MAX_TOKEN_AHEAD-1);
- if(n<stored){
- token_ahead_out+=n; token_ahead_out&=(MAX_TOKEN_AHEAD-1);
- } else {
- n-=stored;
- token_ahead_out=token_ahead_in;
- while(n-- > 0) next_char();
- }
- }
-
- /*=========================================================================*/
- /* token output */
- /*=========================================================================*/
-
- /*-------------------------------------------------------------------------*
- | Output is done through double buffering: OUT_BUFFER and OTHER_OUT_ |
- | BUFFER hold the output until the other is full. This means that every |
- | time the last OUT_BUFFER_LEN output tokens are recoverable. This |
- | mechanism is used to store macro parameters which are erased after |
- | substitution. |
- | -- output_position is used as an absolute position pointer. |
- | -- alloc_outbuffers() allocates memory for the buffers. |
- | -- store_token(t) puts `t' into the output buffer. |
- | -- store_string(str) puts the tokens of `str' into the output buffer. |
- | -- flush_output() flushes output buffers. |
- | -- set_output_position(pos) erases all output written after the absolute|
- | position `pos', if it is possible. |
- | -- retrieve_out(from,till,to) reads back the output between positions |
- | `from' and `till' and stores it at `to'. |
- *-------------------------------------------------------------------------*/
-
- #define OUT_BUFFER_LEN 16384 /* should be a power of 2 */
-
- byte *OUT_BUFFER,*OTHER_OUT_BUFFER;
-
- int other_buffer_is_full=0; /* indicates if OTHER_OUT_BUFFER is full */
- int output_index=0; /* next free place in OUT_BUFFER */
-
- int alloc_outbuffers(){
- OUT_BUFFER=(byte*)malloc(OUT_BUFFER_LEN);
- OTHER_OUT_BUFFER=(byte*)malloc(OUT_BUFFER_LEN);
- return(OUT_BUFFER==NULL || OTHER_OUT_BUFFER==NULL);
- }
-
- void write_output(from,len) byte *from; int len;
- /* writes `len' tokens to `output_file' with appropriate translation */
- {byte token;
- while(len-- > 0){
- switch(token = *from){
- case E_LBRACE: putc('\\',output_file); putc('{',output_file); break;
- case E_RBRACE: putc('\\',output_file); putc('}',output_file); break;
- case E_PCT_MARK: putc('\\',output_file); putc('%',output_file); break;
- case E_BACKSLASH: putc('\\',output_file); putc('\\',output_file); break;
- case E_DOLLAR: putc('\\',output_file); putc('$',output_file); break;
- default: if(token < 128) putc((char)token,output_file);
- }
- from++;
- }
- }
-
- void store_token(t) int t; /* puts token `t' into OUT_BUFFER */
- {byte *bf;
- OUT_BUFFER[output_index]=t;
- output_index++; output_index &= OUT_BUFFER_LEN-1;
- output_position++;
- if(output_index==0){ /* overturn */
- if(other_buffer_is_full!=0){ /* write OTHER_OUT_BUFFER */
- write_output(OTHER_OUT_BUFFER,OUT_BUFFER_LEN);
- }
- other_buffer_is_full=1;
- bf=OUT_BUFFER; OUT_BUFFER=OTHER_OUT_BUFFER; OTHER_OUT_BUFFER=bf;
- }
- }
-
- void store_string(str) char *str; /* stores the elements of the string */
- {
- while(*str){ store_token(*str); str++;}
- }
-
- void flush_output() /* writes everything out */
- {
- if(other_buffer_is_full)
- write_output(OTHER_OUT_BUFFER,OUT_BUFFER_LEN);
- other_buffer_is_full=0;
- write_output(OUT_BUFFER,output_index);
- output_index=0;
- }
-
- int set_out_position(pos) unsigned pos;
- /* erases everything which was written after position `pos' -- if possible */
- {unsigned back; byte *bf;
- if(pos<error_position) pos=error_position; /* keep error messages */
- back=output_position - pos; /* how much to go back */
- if(back<=(unsigned)output_index){ /* remain in OUT_BUFFER */
- output_index-=back; output_position=pos;
- return(0);
- }
- if(other_buffer_is_full!=0 && back-output_index <= OUT_BUFFER_LEN ){
- other_buffer_is_full=0;
- output_position=pos;
- bf=OUT_BUFFER; OUT_BUFFER=OTHER_OUT_BUFFER; OTHER_OUT_BUFFER=bf;
- output_index=OUT_BUFFER_LEN - (back - output_index);
- return(0);
- }
- return(1);
- }
-
- int retrieve_out(from,till,to) unsigned from,till; byte *to;
- /* copies the output written between positions `from' and `till' into `to' */
- {unsigned back,first_part,len;
- back=output_position-from; len=till-from;
- if(back<=(unsigned)output_index){
- strncpy(to,OUT_BUFFER+(output_index-back),len);
- to[len]=0;
- return(0);
- }
- first_part=back-output_index;
- if(other_buffer_is_full!=0 && first_part <= OUT_BUFFER_LEN){
- if(len<=first_part)
- strncpy(to,OTHER_OUT_BUFFER+(OUT_BUFFER_LEN-first_part),len);
- else {
- strncpy(to,OTHER_OUT_BUFFER+(OUT_BUFFER_LEN-first_part),first_part);
- strncpy(to+first_part,OUT_BUFFER,len-first_part);
- }
- to[len]=0;
- return(0);
- }
- return(1); /* too long parameter, cannot handle */
- }
-
- /*=========================================================================*/
- /* error handling */
- /*=========================================================================*/
-
- /*-------------------------------------------------------------------------*
- | Whenever an error is discovered, `error()' is called with arguments |
- | similar to `printf(...)' giving larger freedom to include extra infor- |
- | mation. Error messages are inserted into the output text, ensuring that |
- | they won't be erased later. Messages are also repeated in `error_file' |
- | (presumably stderr) using a different starting format. |
- *-------------------------------------------------------------------------*/
-
- void error(format,va_alist) char *format; va_dcl
- /* writes an error message using a variable number of arguments */
- {va_list varargs; char buffer [1024]; byte *bf;
- exit_value=1; /* inform that we had an error */
- va_start(varargs);
- /**** error message in TeX ****/
- sprintf(buffer,TEX_ERROR_FORMAT,input_file_name,input_line_number);
- store_string(buffer);
- vsprintf(buffer,format,varargs);
- store_string(buffer); store_string("\n");
- ERROR_OUT=CURRENT_OUT; /* freeze output */
- /**** error message in error_file ****/
- if(error_file!=NULL){
- fprintf(error_file,ERR_ERROR_FORMAT,input_file_name,input_line_number);
- bf=(byte *)&buffer[0]; /* error message */
- while(*bf){
- switch(*bf){
- case E_LBRACE: fprintf(error_file,"\\{"); break;
- case E_RBRACE: fprintf(error_file,"\\}"); break;
- case E_PCT_MARK: fprintf(error_file,"\\%"); break;
- case E_BACKSLASH: fprintf(error_file,"\\\\"); break;
- case E_DOLLAR: fprintf(error_file,"\\$"); break;
- default: if(*bf < ' ') fprintf(error_file,"^%c",'A'-1+*bf);
- else if(*bf < 128) putc((char)*bf,error_file);
- }
- bf++;
- }
- putc('\n',error_file);
- }
- va_end(varargs);
- }
-
- /*===========================================================================*/
- /* Storing and retrieving macro text */
- /*===========================================================================*/
-
- /*---------------------------------------------------------------------------*
- | The MACRO structure is used to store all words which occur as macro names |
- | or as mode switch identifiers. The latter ones starting with backlash are |
- | linked separately so that we can search them sequentially whenever a |
- | backslash character appears in the input. Otherwise the words are |
- | searched by hashing: words sharing the same hash code are linked together.|
- | Initially macro texts and structures are stored in a reserved space. If |
- | that space is full, extra space is reserved for each subsequent |
- | definition. |
- | -- alloc_macro() reserves initial space. |
- | -- new_macro(old,word,hashcode) reserves space for a new macro definition |
- | given the old definition (if applicable), the macro name and the |
- | hashcode. |
- | -- set_macro_structure(macro,new_type,left_par,right_par,body_len) fills |
- | the reserved `macro' with the given values; allocates space for the |
- | macro body. |
- | -- set_modeswitch(macro,display,standalone,out) resets the type of macro |
- | with the given values, and inserts into the list of `mode_keywords'. |
- | -- insert_macro() inserts the reserved macro into the hash table. |
- | -- unlink_macro(old,hashcode) deletes the `old' macro. |
- | -- search_word(word,hashcode) searches the macro definition for `word'. |
- | -- check_backslash_keyword(from) looks whether one of the mode_keywords |
- | can be found spying ahead. |
- *---------------------------------------------------------------------------*/
-
- #define PRIME 1999 /* must be a prime, used as the length
- of the hash table. Other possible
- values are: 2503, 2999 */
- #define TEXT_LENGTH 20000 /* initial length of a text table
- to store macro names and bodies */
- byte *macro_text; /* the text table */
- unsigned macro_text_length=0; /* how much is used up of it */
-
- #define MACRO_NO 300 /* initial number of MACRO structures*/
- struct MACRO *macro; /* initial array of macros */
- int macro_no=1; /* how many macros are defined */
-
- struct MACRO *mode_keywords; /* linked list of \-keywords */
- int next_style_number=SIMPLE_STYLE; /* next available style number */
-
- struct MACRO **hash_table; /* the HASH table */
-
- /*---------------------------------------------------------------------------*/
- int alloc_macro() /* Allocate space for macro handling */
- {
- static struct MACRO preserve_keyword={ /* the only \-keyword at start */
- K_PRESERVE | K_CHECKLETTER, /* type */
- 0,0, /* leftpar, rightpar */
- 0, /* style */
- (byte*)T_PRESERVE, /* name */
- NULL,NULL,NULL /* body, link, next keyword */
- };
- macro_text=(byte*)malloc(TEXT_LENGTH);
- macro=(struct MACRO*)calloc(MACRO_NO,sizeof(struct MACRO));
- hash_table=(struct MACRO**)calloc(PRIME,sizeof(struct MACRO*));
- if(macro_text==NULL || macro==NULL || hash_table==NULL) return(1);
- macro[0]=preserve_keyword; macro_no=1;
- mode_keywords=¯o[0];
- return(0);
- }
-
- unsigned new_hashcode=0; /* local variables to hold info */
- struct MACRO *new_macro_entry=NULL; /* for 'insert_macro' */
-
- struct MACRO *new_macro(old,word,hashcode)
- struct MACRO *old; byte *word; unsigned hashcode;
- /* makes a new entry to hash_table */
- {
- if(macro_no<MACRO_NO){
- new_macro_entry=macro+macro_no; macro_no++;
- } else {
- new_macro_entry=(struct MACRO *)calloc(1,sizeof(struct MACRO));
- if(new_macro_entry==NULL){ error(OUT_OF_MEMORY); return(NULL); }
- }
- new_hashcode=hashcode%PRIME;
- if(old!=NULL) *new_macro_entry = *old;
- else {
- setmem((byte*)new_macro_entry,sizeof(struct MACRO),0);
- if((new_macro_entry->name=(byte *)strdup(word))==NULL){
- error(OUT_OF_MEMORY); return(NULL);
- }
- }
- new_macro_entry->link=NULL;
- return(new_macro_entry);
- }
-
- void insert_macro() /* inserts `new_macro_entry' into its place */
- {
- if(new_macro_entry==NULL) return;
- new_macro_entry->link=hash_table[new_hashcode];
- hash_table[new_hashcode]=new_macro_entry;
- }
-
- void unlink_macro(old,hashcode) struct MACRO *old; unsigned hashcode;
- /* unlinks "old" from the hash table */
- {struct MACRO *k,*k1;
- hashcode%=PRIME; k=hash_table[hashcode]; /* unlink from hash table */
- if(k==old) hash_table[hashcode]=old->link;
- else {
- while(k1=k->link, k1!=NULL && k1!=old) k=k1;
- k->link=old->link;
- }
- if(is_backslash_k(old)){ /* unlink from keyword */
- if(mode_keywords==old) mode_keywords=old->keyword;
- else {
- k=mode_keywords;
- while(k1=k->keyword, k1!=NULL && k1!=old) k=k1;
- k->keyword=old->keyword;
- }
- }
- }
-
- int set_macro_structure(k,type,left_par,right_par,len)
- struct MACRO *k; int type,left_par,right_par; unsigned len;
- /* fills k with the given values */
- {
- k->type &= ~K_MATH; /* clear K_MATH bit */
- if(type) k->type |= K_MATH; /* set K_MATH bit if necessary */
- k->leftpar=left_par;
- k->rightpar=right_par;
- if(macro_text_length+len < TEXT_LENGTH){ /* reserved memory */
- k->body=macro_text+macro_text_length;
- macro_text_length+=len;
- return(0);
- }
- if((k->body=(byte *)malloc(len))==NULL){
- error(OUT_OF_MEMORY); return(1);
- }
- return(0);
- }
-
- void set_modeswitch(s,disp,standalone,out)
- struct MACRO *s; int disp,standalone,out;
- /* sets the appropriate mode for "s". Also puts it on the mode_keyword list */
- {int last_char; struct MACRO *k;
- if(s==NULL) return;
- if(out==0) next_style_number++;
- s->style=next_style_number;
- s->type &= ~(K_INOUT | K_MATHDISP | K_STANDALONE);
- if(standalone) s->type |= K_STANDALONE;
- if(disp) s->type |= K_MATHDISP;
- if(out) s->type |= K_INOUT;
- if(*(s->name)=='\\' && !is_backslash_k(s)){ /* starts with backslash */
- last_char=(s->name)[word_length_k(s)-1];
- if(last_char < 128 && isalpha(last_char))
- s->type |= K_CHECKLETTER;
- s->type |= K_BACKSLASH;
- k=mode_keywords; /* is it on the list ? */
- while(k!=NULL && k!=s)k=k->keyword;
- if(k==NULL){
- s->keyword=mode_keywords;
- mode_keywords=s;
- }
- }
- }
-
- /*---------------------------------------------------------------------------*/
- struct MACRO *search_word(word,hashcode) byte *word; unsigned hashcode;
- /* returns the structure whose name agrees with `word', given its hash code. */
- {struct MACRO *k;
- k=hash_table[hashcode%PRIME];
- while(k!=NULL){
- if(strcmp(word,k->name)==0) return(k);
- k=k->link;
- }
- return(NULL);
- }
-
- struct MACRO *check_backslash_keyword(i) int i;
- /* returns the structure whose `name' starting at the `i'-th character agrees
- with the spy_string_ahead. */
- {struct MACRO *k;
- k=mode_keywords; while(k!=NULL){
- if(spy_string_ahead((char *)((k->name)+i),
- check_letter_k(k) ? FOLLOW_NO_LETTER : FOLLOW_NOTHING))
- return(k); /* found */
- k=k->keyword;
- }
- return(NULL);
- }
-
- /*=========================================================================*/
- /* Word handling */
- /*=========================================================================*/
-
- /*-------------------------------------------------------------------------*
- | Words, i.e. character sequences between white spaces are stored sepa- |
- | rately (not only in the output buffers); also their hash code is |
- | computed "on the fly". Macro handling routines got their approproate |
- | parameters here. |
- | -- alloc_word() allocates initial memory. |
- | -- clear_word_store() should be called before a new word is dealt with. |
- | -- store_word_token(t) store `t' as the next word constituent. |
- | -- close_word_store() closes the word. |
- | -- prepare_new_macro_entry() the last word is becoming a new macro. |
- | -- remove_macro() the last word is a macro to be "undefined". |
- | -- look_up_word() searches the stored word as a macro. |
- *-------------------------------------------------------------------------*/
- #define MAX_WORD_LENGTH 512 /* no longer words are dealt with */
- byte *WORD_STORE; /* tokens of the last word */
- int word_store_index; /* index to WORD_STORE */
- unsigned word_hash_code; /* hash code computed on the fly */
-
- int alloc_word() /* allocates initial memory */
- { WORD_STORE=(byte*)malloc(MAX_WORD_LENGTH);
- return(WORD_STORE==NULL);
- }
-
- #define clear_word_store() {word_store_index=0;word_hash_code=952;}
-
- void store_word_token(t) int t;
- /* stores the word consitutent `t' in `WORD_STORE[]', and computes the
- hash code of the word "in fly". */
- {
- WORD_STORE[word_store_index++]=t;
- word_hash_code = ((t+word_hash_code)<<4)+t;
- if(word_store_index==MAX_WORD_LENGTH) word_store_index--;
- }
-
- #define close_word_store() {WORD_STORE[word_store_index]=0;}
-
- #define prepare_new_macro_entry() \
- new_macro(S_aux2,WORD_STORE,word_hash_code)
-
- #define remove_macro() unlink_macro(S_aux2,word_hash_code)
-
- #define look_up_word() search_word(WORD_STORE,word_hash_code)
-
- /*========================================================================*/
- /* symbols */
- /*========================================================================*/
-
- /*------------------------------------------------------------------------*
- | Highest level reading. The input text is broken into "symbol"s which |
- | are passed to the main loop. A "symbol" is a sequence of tokens; and |
- | `store_token()' is called with all tokens in it. |
- | o SYMBOL is the type of the symbol read; |
- | o LAST_OUT holds the output position where the tokens forming the |
- | last symbol start; |
- | o S_aux1 contains some extra information about the SYMBOL; |
- | o S_aux2 is the associated macro definition for the symbol if it is a |
- | WORD. |
- | o LAST_TOKEN and last_keyword are auxiliary variables to prevent |
- | double parsing of certain sequences. |
- | The procedures which are called outside: |
- | -- initialize_symbol_reading() should be called first. |
- | -- next_symbol() produces the next symbol. |
- | -- skip_line_as_comment() skips everything till the end of line. |
- | -- skip_soft_delimiters() reads until the SYMBOL differs from |
- | SOFT_DELIMITER. |
- *------------------------------------------------------------------------*/
-
- int LAST_TOKEN='\n'; /* last token dealt with */
- struct MACRO *last_keyword; /* parsed keyword */
-
- #define initialize_symbol_reading() {LAST_TOKEN='\n';}
-
- int check_token(t) int t; /* checks the type of the next token */
- {
- t &= 0xFF;
- if(t<=' ' || is_par(t)) return(0); /* word boundary */
- switch(t){
- case '{': case '}': case '%': case E_EOF:
- case '$': return(0); /* word boundary */
- case '\\': if(in_def_mode() && spy_string_ahead("\\\n",FOLLOW_NOTHING))
- return(0); /* word boundary */
- return(2); /* check for keywords */
- default: return(1); /* word constituent */
- }
- }
-
- void next_symbol() /* produces the next SYMBOL */
- {int t,lt,len,i; struct MACRO *k;
- LAST_OUT=CURRENT_OUT; /* where SYMBOL output starts */
- if(in_comment_mode()){ /* read until the end of line */
- while((t=get_next_token())!='\n' && t!=E_EOF) store_token(t);
- input_line_number++;
- if(t==E_EOF) t='\n';
- LAST_TOKEN=t; store_token(t);
- SYMBOL=COMMENT; return;
- }
- try_again: /* after \newline in def mode */
- t=get_next_token(); lt=LAST_TOKEN; LAST_TOKEN=t;
- store_token(t);
- clear_word_store(); store_word_token(t);
- switch(t){
- case E_EOF: LAST_TOKEN='\n'; SYMBOL=ENDFILE; return;
- case '{': SYMBOL=OPEN; return;
- case '}': SYMBOL=CLOSE; return;
- case '%': if(in_def_mode()) {SYMBOL=MACRO_DELIM; return;}
- SYMBOL=COMMENT;
- if(lt=='\n'){ /* check for %keywords */
- len=0;
- if(spy_string_ahead(T_DEFINE,FOLLOW_SPACE)){
- len=T_DEFINE_LEN; SYMBOL=DEF_KEYWORD;
- } else if(spy_string_ahead(T_MDEFINE,FOLLOW_SPACE)){
- len=T_MDEFINE_LEN; SYMBOL=MDEF_KEYWORD;
- } else if(spy_string_ahead(T_UNDEFINE,FOLLOW_SPACE)){
- len=T_UNDEFINE_LEN; SYMBOL=UNDEF_KEYWORD;
- } else if(spy_string_ahead(T_MATHMODE,FOLLOW_SPACE)){
- len=T_MATHMODE_LEN; SYMBOL=MATH_KEYWORD;
- } else if(spy_string_ahead(T_DISPMODE,FOLLOW_SPACE)){
- len=T_DISPMODE_LEN; SYMBOL=DISP_KEYWORD;
- }
- if(len>0) skip_tokens(len);
- }
- return;
- case ' ': case '\t': S_aux1=0; SYMBOL=SOFT_DELIMITER; return;
- /* S_aux1==0 says that the delimiter vanishes at substitution */
- case '\n': input_line_number++;
- S_aux1=1; SYMBOL= lt=='\n' ? EMPTY_LINE :
- in_def_mode() ? HARD_DELIMITER : SOFT_DELIMITER;
- return;
- case '$':
- if(in_math_mode() && mode_style==SIMPLE_STYLE) /* single $ */
- SYMBOL=MATH_OUT;
- else if(spy_token_ahead()=='$'){ /* double $$ */
- skip_tokens(1); store_token('$');
- SYMBOL=in_disp_mode() && mode_style==SIMPLE_STYLE ?
- DISP_OUT : DISP_IN;
- } else SYMBOL=MATH_IN;
- return;
- case '\\': /* E_KEYWORD means a \keyword was succesfully parsed */
- k=lt==E_KEYWORD ? last_keyword : check_backslash_keyword(1);
- if(k!=NULL){ /* LAST_TOKEN=='\\' */
- len=word_length_k(k)-1; /* number of tokens in k */
- for(i=0;i<len;i++) store_token(get_next_token());
- if(is_preserve_k(k)){
- SYMBOL=PRESERVE;
- return;
- }
- S_aux2=k; SYMBOL=WORD; return;
- }
- if(in_def_mode() && spy_token_ahead()=='\n'){
- set_out_position(LAST_OUT); /* do not store backslash */
- skip_tokens(1); /* skip over newline */
- input_line_number++;
- goto try_again;
- }
- default:
- if(is_par(t)){ S_aux1=extract_par(t); SYMBOL=PARAMETER; return;}
- if(t<' '){ SYMBOL=DELIMITER; return;}
- /* now t is an inner character of a word (maybe `\') */
- SYMBOL=WORD;
- while(1){switch(check_token(t=spy_token_ahead())){
- case 0: /* word boundary, do not advance */
- close_word_store(); S_aux2=look_up_word(); return;
- case 2: /* backslash */
- k=check_backslash_keyword(0);
- if(k!=NULL){ /* a keyword parsed successfully after a WORD */
- last_keyword=k; LAST_TOKEN=E_KEYWORD;
- close_word_store(); S_aux2=look_up_word(); return;
- }
- case 1: /* word constituent */
- store_token(get_next_token()); store_word_token(t); break;
- }}
- }
- }
-
- void skip_line_as_comment()
- /* If not at the end of the line, skip the rest of the line. */
- {
- if(LAST_TOKEN=='\n'){ /* we've hit the end of line */
- store_token('\n');
- return;
- }
- global_mode |= COMMENT_MODE; next_symbol(); global_mode &= ~COMMENT_MODE;
- }
-
- void skip_soft_delimiters()
- /* go ahead until a not SOFT_DELIMITER is found */
- { while(SYMBOL==SOFT_DELIMITER) next_symbol(); }
-
- /*=========================================================================*/
- /* Parameter stack */
- /*=========================================================================*/
-
- /*-------------------------------------------------------------------------*
- | The potential actual parameters of macro calls are stored in a stack. |
- | The depth of the stack, however, is bounded, and information ad the |
- | bottom of the stack loses as the stack grows. Each OPEN symbol opens a |
- | new range, thus the stack shrinks to that point only. Each entry in the |
- | stack has three fields: |
- | o replace: the position from where the text should be erased (white |
- | space before the parameter) |
- | o start: output position where the parameter text starts |
- | o end: output position until the parameter text starts. |
- | Procedures handling the parameter stack: |
- | -- alloc_params() allocates initial memory. |
- | -- push_par(replace,start,end) puts an entry into the stack. |
- | -- pop_par(replace,start,end) pops the uppermost entry in the stack. |
- | -- open_range() opens a new range to prevent the stack shrink below. |
- | -- close_range() closes the range, shrinks the stack until the |
- | corresponding open_range. |
- | -- shrink_par_stack() shrinks the stack until the last range boundary. |
- *-------------------------------------------------------------------------*/
-
- #define STACK_DEPTH 256
- typedef struct STACK { unsigned replace,start,end;};
-
- struct STACK *par_stack;
- int stack_pointer=0, stack_bottom=0, border_pointer=0;
-
- int alloc_params()
- { par_stack=(struct STACK*)calloc(STACK_DEPTH,sizeof(struct STACK));
- return(par_stack==NULL);
- }
-
- #define stack_depth() ((stack_pointer-border_pointer)%STACK_DEPTH)
-
- void push_par(replace,start,end) unsigned replace,start,end;
- /* pushes the next entry into the stack */
- {
- par_stack[stack_pointer].replace=replace;
- par_stack[stack_pointer].start=start;
- par_stack[stack_pointer].end=end;
- stack_pointer++; stack_pointer%=STACK_DEPTH;
- if(stack_pointer==stack_bottom) {
- stack_bottom++; stack_bottom%=STACK_DEPTH;
- if(stack_pointer==border_pointer) border_pointer=stack_bottom;
- }
- }
-
- int pop_par(replace,start,end) unsigned *replace,*start, *end;
- /* pops the next element from the stack */
- {
- if(stack_pointer==border_pointer || stack_pointer==stack_bottom)
- return(1);
- stack_pointer--; stack_pointer%=STACK_DEPTH;
- *replace=par_stack[stack_pointer].replace;
- *start=par_stack[stack_pointer].start;
- *end=par_stack[stack_pointer].end;
- return(0);
- }
-
- void open_range() /* opens a new range */
- {
- push_par((unsigned)((stack_pointer-border_pointer)%STACK_DEPTH),0,0);
- border_pointer=stack_pointer;
- }
-
- void close_range() /* closes a range if possible */
- {unsigned bp,dummy;
- stack_pointer=border_pointer; border_pointer++; /* fool `pop_par' */
- border_pointer= pop_par(&bp,&dummy,&dummy) &&
- bp < (stack_pointer-stack_bottom)%STACK_DEPTH ?
- (stack_pointer-bp)%STACK_DEPTH : stack_bottom;
- }
-
- #define shrink_par_stack() {stack_pointer=border_pointer;}
-
- /*=========================================================================*/
- /* Parameter substitution */
- /*=========================================================================*/
-
- /*-------------------------------------------------------------------------*
- | Parameter substitution is performed here. Descriptions of parameters |
- | are popped out of the stack, retrieved from the output buffer into |
- | allocated memory; the output buffer is rewind until the `from' position |
- | and then the macro body is written into output while replacing the |
- | formal parameters. At the end the allocated memory is freed. |
- *-------------------------------------------------------------------------*/
- unsigned start[10], pend[10]; /* parameter start and end */
- byte *parameters[10]; /* the parameters themselves */
-
- int macro_substitution(from,k) unsigned *from; struct MACRO *k;
- /* performs the given substitution */
- {unsigned replace; unsigned len; int i,par_no; char *memory;
- byte *p,*body,t;
- par_no=left_pars_k(k)+right_pars_k(k);
- len=0; memory=NULL; replace = *from;
- for(i=par_no-1;i>=0;i--){
- if(pop_par(&replace,&start[i],&pend[i])) return(0);
- len += pend[i]-start[i]+1;
- }
- if(*from < replace) replace = *from; /* place to replace from */
- *from=replace;
- if(len>0){
- memory=malloc(len);
- p=(byte *)memory;
- if(p==NULL){
- error(OUT_OF_MEMORY);
- return(0);
- }
- for(i=0;i<par_no;i++){
- parameters[i]=p;
- if(retrieve_out(start[i],pend[i],p)){ /* parameter lost */
- error(TOO_LONG_PARAMETER,k->name);
- free(memory); return(0);
- }
- p+=pend[i]-start[i]+1;
- }
- }
- if(set_out_position(replace)){
- error(TOO_LONG_PARAMETER,k->name);
- if(memory!=NULL) free(memory); return(0);
- }
- body=k->body;
- while(t = *body++){
- if(is_par(t)){
- p=parameters[extract_par(t)];
- while(t = *p++) store_token((int)t);
- } else store_token((int)t);
- }
- if(memory!=NULL) free(memory);
- return(1);
- }
-
- /*=========================================================================*/
- /* Macro definition */
- /*=========================================================================*/
-
- /*-------------------------------------------------------------------------*
- | This part deals with macro and keyword definitions. All of them vanish |
- | from the output text, and are replaced by TeXpp_MACRO_DEFINITION. The |
- | macro text is expanded in the output buffer, and copied into the memory |
- | later. Calling `translate_parameters()' changes all references to the |
- | formal parameters from their face value into their position. |
- | -- init_macro_definition() saves the old mode, the actual output posi- |
- | tion, and changes into DEFINE_MODE. |
- | -- close_macro_definition() restores the original mode rewinds the |
- | output, and inserts the appropriate text. |
- | -- read_macro_definition(type) handles the macro definition. The `type' |
- | tells whether the definition was %mdefine (=1) or not (=0). |
- | -- undefine_macro() handler the case %undefine. The macro is unliked |
- | both from the hash table and the keyword list. |
- | -- define_keyword(type) deals with the %mathmode and %dispmode keywords |
- *-------------------------------------------------------------------------*/
-
- int old_mode, old_style;
- unsigned save_out_position, start_macro_text;
- int params[9];
-
- void translate_parameters(body) byte *body;
- /* replaces parameter #i by its absolute position */
- {byte p;
- while((p = *body)){
- if(is_par(p)) *body=make_par(params[extract_par(p)]+'0');
- body++;
- }
- }
-
- void init_macro_definition()
- {int i;
- old_mode=global_mode; old_style=mode_style;
- global_mode=DEFINE_MODE; /* save old mode, switch to define */
- save_out_position=CURRENT_OUT; /* only a single % has been stored */
- shrink_par_stack(); /* no previous parameter */
- flush_output(); /* no backtrack beyond this point */
- for(i=0;i<9;i++)params[i]=0; /* no parameters defined */
- }
-
- void close_macro_definition()
- {
- set_out_position(save_out_position);/* cancel garbage */
- store_string(TeXpp_MACRO_DEFINITION);
- skip_line_as_comment(); /* do not deal with the rest */
- global_mode=old_mode; mode_style=old_style;
-
- }
-
- void read_macro_definition(type) int type;
- /* reads a macro definition -- issues appropriate error messages */
- {int result; struct MACRO *k; int left_params,all_params;
- init_macro_definition();
- left_params=0;
- next_left_param: /* read leftist parameters */
- next_symbol(); skip_soft_delimiters();
- if(SYMBOL==PARAMETER){
- if(params[S_aux1]!=0){ /* declared twice */
- error(PARAMETER_TWICE,S_aux1+1);
- close_macro_definition(); return;
- }
- params[S_aux1]= ++left_params;
- goto next_left_param;
- }
- if(SYMBOL!=WORD){
- error(WRONG_MACRO_NAME); close_macro_definition(); return;
- }
- k=prepare_new_macro_entry(); /* if NULL, then no memory */
- if(k==NULL){ close_macro_definition(); return; }
- all_params=left_params;
- next_right_param: /* read rightist parameters */
- next_symbol(); skip_soft_delimiters();
- if(SYMBOL==PARAMETER){
- if(params[S_aux1]!=0){ /* declared twice */
- error(PARAMETER_TWICE,S_aux1+1);
- close_macro_definition(); return;
- }
- params[S_aux1]= ++all_params;
- goto next_right_param;
- }
- if(SYMBOL!=MACRO_DELIM){
- error(MISSING_DELIMITER); close_macro_definition(); return;
- }
- start_macro_text=CURRENT_OUT;
- if(type!=0){ /* %mdefine */
- global_mode |= MATH_MODE; mode_style=DEFINE_STYLE;
- }
- do{ next_symbol();} while((result=deal_range())==X_CLOSE);
- if(result==X_ERROR){
- close_macro_definition(); return;
- }
- if(SYMBOL!=MACRO_DELIM) error(MISSING_DELIMITER);
- if(set_macro_structure( k,type,left_params,all_params-left_params,
- LAST_OUT-start_macro_text+1)){ /* no more memory */
- close_macro_definition(); return;
- }
- if(retrieve_out(start_macro_text,LAST_OUT,k->body)){
- error(TOO_LONG_MACRO_DEF,k->name);
- close_macro_definition(); return;
- }
- translate_parameters(k->body);
- insert_macro();
- close_macro_definition();
- }
-
- void undefine_macro() /* %undefine <macro_name> */
- {
- init_macro_definition();
- next_symbol(); skip_soft_delimiters();
- if(SYMBOL==WORD && S_aux2!=NULL){ /* delete it */
- remove_macro();
- } else error(WRONG_MACRO_NAME);
- close_macro_definition();
- }
-
- void define_mode_keyword(type) int type; /* %mathmode or %dispmode */
- {struct MACRO *k1,*k2;
- init_macro_definition();
- next_symbol(); skip_soft_delimiters(); /* to mode keyword */
- if(SYMBOL!=WORD){
- error(WRONG_MODE_SWITCH_DEF);
- close_macro_definition();
- return;
- }
- k1=prepare_new_macro_entry();
- insert_macro(); /* puts to the "keywords" */
- next_symbol(); skip_soft_delimiters();
- switch(SYMBOL){ /* from mode keyword */
- case MACRO_DELIM: case HARD_DELIMITER:
- set_modeswitch(k1,type,1,0); /* single keyword */
- break;
- case WORD:
- if(k1==S_aux2) k2=NULL;
- else {k2=prepare_new_macro_entry(); insert_macro();}
- next_symbol(); skip_soft_delimiters();
- if(SYMBOL==MACRO_DELIM || SYMBOL==HARD_DELIMITER){
- set_modeswitch(k1,type,k2==NULL,0); /* single keyword ? */
- set_modeswitch(k2,type,0,1);
- break;
- }
- default:
- error(WRONG_MODE_SWITCH_DEF); break;
- }
- close_macro_definition();
- }
-
- /*=========================================================================*/
- /* Macro and mode switch handling */
- /*=========================================================================*/
-
- /*-------------------------------------------------------------------------*
- | -- deal_range() reads things between {...} and returns X_CLOSE, X_ERROR |
- | or X_OTHER, skipping unbalanced mode switches. At the end does |
- | not advances. |
- | -- set_mode(k) switches into mode as given by struct MACRO parameter k. |
- | Returns !=0 if the switch is unbalanced. |
- | -- store_mode_block() stores a block enclosed by mode switches. This |
- | behaves as a single (unbreakable) parameter. |
- | -- deal_mode_switch(k) decides whether `k' is a also a mode switch. If |
- | not, then pushes it as a paramter. If yes, skips until the closing |
- | switch. |
- | -- deal_word() checks whether the last word is a macro name, or is a |
- | mode switch. Performs the appropriate actions in each case. |
- *-------------------------------------------------------------------------*/
-
- int deal_range()
- /* Reads things between {...} and returns X_CLOSE, X_ERROR or X_OTHER,
- skipping unbalanced mode switches. At the end does not advance. */
- {int result;
- while(1){
- while((result=store_parameter(1))==X_PARAMETER);
- if(result==X_ERROR) return(X_ERROR);
- if(result==X_OTHER){ switch(SYMBOL){
- case CLOSE: return(X_CLOSE);
- case DELIMITER: break; /* allowed withing braces */
- default: return(X_OTHER);
- }}
- shrink_par_stack(); next_symbol();
- }
- }
-
- int set_mode(k) struct MACRO *k;
- /* Switches into math or disp mode. Returns !=0 if the switch is wrong. */
- {
- if(is_standalone_k(k) || is_in_k(k)){
- global_mode |= is_math_k(k) ? MATH_MODE : DISP_MODE;
- mode_style=style_k(k);
- return(0);
- }
- return(1);
- }
-
- int store_mode_block(replace,from,mode_out)
- unsigned replace,from; int mode_out;
- /* advances and stores a mode block closed by `mode_out' */
- {int result;
- open_range(); next_symbol();
- while((result=store_parameter(1))==X_PARAMETER);
- close_range();
- if(result!=mode_out) return(result);
- push_par(replace,from,CURRENT_OUT);
- return(X_PARAMETER);
- }
-
- int deal_mode_switch(k,replace,from)
- struct MACRO *k; unsigned replace,from;
- /* checks whether the last word is also a mode switch */
- {
- if(k==NULL || !is_modeswitch_k(k)){ /* not a mode switch */
- push_par(replace,from,CURRENT_OUT);
- return(X_PARAMETER);
- }
- if(in_plain_mode()){ /* switch to mode */
- if(set_mode(k)){ /* wrong switch */
- error(WRONG_MODE_SWITCH,k->name);
- return(X_XMODE_OUT);
- }
- return(store_mode_block(replace,from,X_XMODE_OUT));
- }
- if(mode_style!=style_k(k) || (!is_standalone_k(k) && is_in_k(k))){
- error(WRONG_MODE_SWITCH,k->name);
- set_plain_mode(); set_mode(k);
- } else set_plain_mode();
- return(X_XMODE_OUT);
- }
-
- int deal_word(replace_from,advance) unsigned replace_from; int advance;
- /* Checks whether the word is a macro name. Also checks for mode switch. */
- {struct MACRO *k; int i,replaced,result,right_pars;
- k=S_aux2;
- if(k==NULL || body_k(k)==NULL || (is_math_macro(k) && in_plain_mode())){
- replaced=0;
- result=deal_mode_switch(k,replace_from,LAST_OUT);
- } else { /* macro name */
- if(stack_depth() < left_pars_k(k)) {
- error(TOO_LESS_LEFT_PARAMS,left_pars_k(k),k->name);
- return(X_ERROR);
- }
- right_pars=right_pars_k(k);
- if(right_pars>0) next_symbol();
- result=X_PARAMETER;
- for(i=1; result==X_PARAMETER && i<=right_pars;i++)
- result=store_parameter(i<right_pars);
- if(result!=X_PARAMETER){
- error(TOO_LESS_PARAMS,left_pars_k(k)+right_pars_k(k),k->name);
- return(X_ERROR);
- }
- replaced=macro_substitution(&replace_from,k);
- result=deal_mode_switch(k,replace_from,replace_from);
- }
- if(result==X_PARAMETER && advance){
- replaced &= SYMBOL!=CLOSE; /***** ?????? ******/
- next_symbol(); /* skip whitespace after a WORD */
- if(replaced && SYMBOL==SOFT_DELIMITER && S_aux1==0){
- set_out_position(LAST_OUT); next_symbol();
- }
- }
- return(result);
- }
-
- /*=========================================================================*/
- /* Reading parameters */
- /*=========================================================================*/
-
- /*-------------------------------------------------------------------------*
- | -- skip_balanced_expression() used skipping a {...} parameter for the |
- | keyword \preserve. |
- | -- skip_word() skips until the next SOFT_DELIMITER after \preserve. |
- | -- store_parameter(advance) stores and handles the next SYMBOL. If |
- | `advance' is TRUE (!=0) then reads ahead one more SYMBOL. |
- *-------------------------------------------------------------------------*/
-
- void skip_balanced_expression() /* skip until an unbalanced CLOSE */
- {int level=0;
- while(1){
- next_symbol(); switch(SYMBOL){
- case HARD_DELIMITER: case EMPTY_LINE: case ENDFILE:
- return;
- case OPEN: level++; break;
- case CLOSE: level--; if(level<0) return;
- default: break;
- }
- }
- }
-
- void skip_word() /* skips a word after \preserve */
- {
- while(1){ switch(SYMBOL){
- case SOFT_DELIMITER: case HARD_DELIMITER: case EMPTY_LINE: case ENDFILE:
- return;
- default:
- next_symbol(); break;
- }}
- }
-
- int store_parameter(advance) int advance;
- /* Stores a single parameter. If returns !=0 or advance==0 then does not
- advances */
- {unsigned replace_from,start; int whitespace; int result;
- whitespace=0;
- again:
- if(whitespace==0) replace_from=LAST_OUT;
- switch(SYMBOL){
- case SOFT_DELIMITER: /* if S_aux1==0 the delimiter vanishes at substitution */
- if(S_aux1==0){ whitespace=1; replace_from=LAST_OUT;}
- else whitespace=0;
- next_symbol();
- goto again;
- case WORD:
- return(deal_word(replace_from,advance));
- case PARAMETER: /* formal parameter in macro text */
- if(params[S_aux1]==0){
- error(UNDEFINED_PARAMETER,1+S_aux1);
- return(X_ERROR);
- }
- push_par(replace_from,LAST_OUT,CURRENT_OUT);
- if(advance) next_symbol();
- return(X_PARAMETER);
- case PRESERVE: /* \preserve keyword */
- start=LAST_OUT;
- do{ set_out_position(LAST_OUT); next_symbol();}
- while(SYMBOL==SOFT_DELIMITER); /* skip soft delimiters */
- if(SYMBOL==OPEN){ /* skip until the corresponding CLOSE */
- set_out_position(LAST_OUT); /* do not copy OPEN */
- skip_balanced_expression();
- } else skip_word();
- set_out_position(LAST_OUT); /* do not copy CLOSE */
- if(advance) next_symbol();
- push_par(replace_from,start,LAST_OUT);
- return(X_PARAMETER);
- case MATH_IN: case DISP_IN:
- if(!in_plain_mode()){
- error(WRONG_DOLLAR_SWITCH);
- set_plain_mode();
- }
- global_mode|= SYMBOL==MATH_IN ? MATH_MODE : DISP_MODE;
- mode_style=SIMPLE_STYLE;
- result=store_mode_block(replace_from,LAST_OUT,X_DMODE_OUT);
- if(result==X_PARAMETER && advance) next_symbol();
- return(result);
- case MATH_OUT: /* do not advance! */
- if(!in_math_mode() || mode_style!=SIMPLE_STYLE){
- error(WRONG_CLOSING_DOLLAR);
- }
- set_plain_mode();
- return(X_DMODE_OUT);
- case DISP_OUT: /* do not advance! */
- if(!in_disp_mode() || mode_style!=SIMPLE_STYLE){
- error(WRONG_CLOSING_DOLLAR);
- }
- set_plain_mode();
- return(X_DMODE_OUT);
- case OPEN:
- replace_from=LAST_OUT; start=CURRENT_OUT;
- open_range();
- next_symbol(); /* advance */
- result=deal_range();
- close_range();
- if(result!=X_CLOSE) return(result);
- push_par(replace_from,start,LAST_OUT);
- if(advance) next_symbol(); /* what comes after CLOSE */
- return(X_PARAMETER);
- default: /* do not advance! */
- return(X_OTHER);
- }
- }
-
- /*=========================================================================*/
- /* Main cycle */
- /*=========================================================================*/
-
- /*-------------------------------------------------------------------------*
- | read_file() is the main cycle of the program. It is called with all |
- | input files. The procedure reads in a cycle until the end of the file, |
- | flushing all the output and shrinking the parameter stack in each |
- | iteration. Macro parameters cannot go over these constructs, e.g. empty |
- | line, comment, TeX commands, etc. |
- *-------------------------------------------------------------------------*/
- void read_file() /* goes through a file */
- {int result;
- initialize_token_reading();
- initialize_symbol_reading();
- again:
- shrink_par_stack(); /* no previous parameter */
- flush_output(); /* no backtrack beyond this point */
- next_symbol(); /* first symbol to read */
- while((result=store_parameter(1))==X_PARAMETER);
- /* read until can */
- if(result!=X_OTHER) goto again;
- shrink_par_stack(); /* no parameters to deal with */
- switch(SYMBOL){ /* what caused the trouble */
- case EMPTY_LINE:
- if(!in_plain_mode()){ /* check math and disp mode */
- error(EMPTY_LINE_IN_MODE,in_math_mode() ? "math" : "display");
- set_plain_mode();
- }
- goto again;
- case ENDFILE: /* end of everything */
- if(!in_plain_mode()){
- error(ENDFILE_IN_MODE,in_math_mode() ? "math":"display");
- set_plain_mode();
- }
- break;
- case COMMENT: /* a % sign somewhere */
- skip_line_as_comment();
- goto again;
- case DELIMITER: /* control character */
- case CLOSE: /* unmatched closing bracket */
- goto again;
- case DEF_KEYWORD: /* %define */
- read_macro_definition(0);
- goto again;
- case MDEF_KEYWORD: /* %mdefine */
- read_macro_definition(1);
- goto again;
- case UNDEF_KEYWORD: /* %undefine <name> */
- undefine_macro();
- goto again;
- case MATH_KEYWORD: /* %matmode <in> <out> */
- define_mode_keyword(0);
- goto again;
- case DISP_KEYWORD: /* %dispmode <in> <out> */
- define_mode_keyword(1);
- goto again;
- /*** case MATH_IN: case MATH_OUT: case DISP_IN: case DISP_OUT: case PRESERVE:
- case HARD_DELIMITER: case OPEN: case WORD: case MACRO_DELIM:
- case PARAMETER: case SOFT_DELIMITER: ***/
- default: /* something which should not occur */
- fprintf(stderr,"Unreachable symbol: %d\n"); break;
- }
- }
-
- /*=========================================================================*/
- /* Command line arguments */
- /*=========================================================================*/
-
- /*-------------------------------------------------------------------------*
- | Arguments in the command line are the files which are to be processed. |
- | Argument STDIN_ARG means the standard input; a file name preceeded by |
- | WRITE_ARG or APPEND_ARG is considered the output file. If no output |
- | file is present then the output goes to the standard output. In this |
- | latter case error messages are not repeated at stderr. |
- *-------------------------------------------------------------------------*/
- #define WRITE_ARG "-w"
- #define APPEND_ARG "-a"
- #define STDIN_ARG "-"
- #define HELP_ARG "-h"
-
- FILE *find_output(argc,argv) int argc; char *argv[];
- /* searches the argument list for NOPAR_ARG */
- {FILE *f; int i,found;
- for(i=1;i<argc;i++){
- found = stricmp(argv[i],WRITE_ARG)==0 ? 1 :
- stricmp(argv[i],APPEND_ARG)==0 ? 2 : 0;
- if(found!=0){
- i++;
- if(i==argc){
- fprintf(stderr,"output file is missing\n");
- f=NULL;
- } else{
- f=fopen(argv[i],found==1 ? "w" : "a");
- if(f==NULL)
- fprintf(stderr,"cannot open or create file %s\n",argv[i]);
- }
- return(f);
- }
- }
- return(stdout);
- }
-
- FILE *arguments(i,argc,argv) int *i,argc; char *argv[];
- /*-------------------------------------------------------------------------*
- | gives back the i-th argument file, or NULL if error, or no more. Skips |
- | `-nopar' arguments, and increases i. Should be called with *i=0 fist. |
- | Also initializes `input_file_name' and `input_line_number'. |
- *-------------------------------------------------------------------------*/
- {FILE *f; int firstcall;
- firstcall= (*i)++ == 0; /* this indicates the first call */
- input_line_number=1; /* start new file */
- input_file_name="stdin"; /* default value */
- while(*i < argc){
- if(stricmp(argv[*i],WRITE_ARG)==0) (*i)+=2;
- else if(stricmp(argv[*i],APPEND_ARG)==0) (*i)+=2;
- else if(stricmp(argv[*i],STDIN_ARG)==0){
- return(stdin);
- } else {
- input_file_name=argv[*i];
- f=fopen(input_file_name,"r");
- if(f==NULL) error(CANNOT_OPEN_FILE);
- return(f);
- }
- }
- return(firstcall ? stdin : NULL); /* no more parameters */
- }
-
- int on_line_help(argc,argv) int argc; char *argv[];
- /*-------------------------------------------------------------------------*
- | gives some useless one line help. |
- *-------------------------------------------------------------------------*/
- { if(argc==2 && stricmp(argv[1],HELP_ARG)==0){
- fprintf(stderr,"usage: %s input_files -[aw] output_file\n",argv[0]);
- return(1);
- }
- return(0);
- }
-
- /*=========================================================================*/
- /* Main routine */
- /*=========================================================================*/
-
- int main(argc,argv) int argc; char *argv[];
- {int input_file_no;
- if(on_line_help(argc,argv)) return(0);
- if(alloc_outbuffers()||alloc_macro()||alloc_word()||alloc_params()){
- fprintf(stderr,"Not enough memory to run %s\n",argv[0]);
- return(1);
- }
- output_file=find_output(argc,argv);
- if(output_file==NULL) return(1);
- error_file=output_file==stdout ? NULL : stderr;
- input_file_no=0; exit_value=0; set_plain_mode();
- while((input_file=arguments(&input_file_no,argc,argv))!=NULL){
- /* for each file on the argument list */
- read_file();
- if(input_file!=stdin) fclose(input_file);
- }
- flush_output();
- return(exit_value);
- }
-
-