/* @(#)tex.c 17.1.1.1 (ES0-DMD) 01/25/02 17:47:38 */ /*=========================================================================== Copyright (C) 1995 European Southern Observatory (ESO) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Massachusetss Ave, Cambridge, MA 02139, USA. Corresponding concerning ESO-MIDAS should be addressed as follows: Internet e-mail: midas@eso.org Postal address: European Southern Observatory Data Management Division Karl-Schwarzschild-Strasse 2 D 85748 Garching bei Muenchen GERMANY ===========================================================================*/ /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .TYPE Module .IDENTIFICATION tex.c .AUTHOR Francois Ochsenbein [ESO-IPG] .LANGUAGE C .VERSION 1.0 25-Apr-1988: Creation .VERSION 1.1 26-May-1988: Added \if \else \fi \today \now \time \len .VERSION 1.2 13-Oct-1988: Cosmetic Modifications .VERSION 1.3 14-Mar-1989: Removed bug in tex_subs (\iffalse executions) .VERSION 1.4 29-Mar-1989: Include file default extension = ``.tex'' Also corrected \if's stacks .VERSION 1.5 21-Jul-1989: Removed bug in getparm .VERSION 1.6 22-Mar-1990: Changed tex_rescan to have another parameter asking not to record errors. .VERSION 1.7 27-Oct-1990: Added call to atex_ to force inclusion of the atex module (problem with Guu-cc on Vax/Vms) .VERSION 1.8 21-Nov-1990: Added \obeylines \obeyspaces facility. .KEYWORDS Text scanning .ENVIRONMENT Any .COMMENTS \begin{TeX} This module includes the necessary functions to scan a piece of TeX text, and to perform the macro definitions / substitutions. The actions are executed \via the functions (included in the TeX structure): \begin{itemize} \item output function (text, length) which performs the actual output. A typical example of such a function could be {\tt write(1,{\rm text, length})}. \item action routine, triggered by the macro {\tt \b action\{{\em text}\}}. Parameters are the {\em text} action and the length of this action. Return is 0 / -1 (Stop) / len \end{itemize} All these user-defined functions return the number of bytes processed; a 0 value indicates ``stop processing'' Standard actions (calling the action routine, with a one-byte argument) are: $$\begin{tabular}{|ll|} \hline \{ &triggered by \{\\ \} &triggered by \}\\ \ &stretchable blank (one or several blanks / newlines / tabs)\\ \b r &triggered by \b\b (go to next line)\\ \b n &new paragraph (triggered by blank lines)\\ \^{ } & triggered by the carret\\ \_ &triggered by the underscore\\ \& &triggered by the ampersand\\ \$ &triggered by the \$ sign\\ 1\dots9 &returns in TeX structure the address of parameter \#$n$\\ \hline \end{tabular}$$ Primitives macros are: \begin{itemize} \item {\tt\b def} macro \eg {\tt\b def\b{\em name}\{{\em equivalence}\}} \item {\tt\b action\{{\em action string}\}} \item {\tt\b begin\{verbatim\}} and {\tt\b end\{verbatim\}} \item {\tt\b len\{{\em text}\}} computes length of a text \item {\tt\b input\{{\em filename}\}} \item {\tt\b inputverbatim\{{\em filename}\}} \item {\tt\b defenv} macro, as {\tt\b defenv\b{\em name[\#$n$]}\{{\em start options}\}\{{\em end options}\} }. It is suggested to use {\tt\b0} as start option, triggering action $0$ and providing in the TeX structure the environment name. \item {\tt\b b} to issue a backslash. \item {\tt\b EOF} end-of-file marker \item {\tt \~{ }} non-breakable space \item {\tt \b\b} go to next line \item {\tt \b\{} \quad {\tt\b\}} \quad {\tt\b\$} \quad {\tt\b\_} \quad {\tt\b\^{ }} \quad {\tt\b\~{ }} \quad {\tt\b\#} \quad {\tt\b\&} to issue the single (escape) character \item {\tt\b iftrue} {\tt\b iffalse} {\tt\b else} {\tt\b fi} {\tt\b ifdef} for tests \item {\tt\b obeylines} {\tt\b obeyspaces} {\tt\b noobey} to keep the line breaks / spaces \\ {\br Remark}: the {\tt \{verbatim\}} mode stops the {\tt obey} mode. \item {\tt\b today} {\tt\b now} {\tt\b time} for current date / time \item {\tt\b0} to {\tt\b9} call action routine, setting parameter \#$n$ in the TeX structure \end{itemize} {\bf Description of Method} Pieces of text to scan are defined via {\em SDV} descriptors, which are used here instead of standard character strings. The text after substitution is stacked into the {\em substitute} buffer, which can be automatically expanded. An {\em SDV} descriptor contains: \begin{itemize} \item the position of the text to scan, as a {\em text} char pointer and an {\em offset} number. The {\em text} pointer is null if the text is located in the {\em substitute} buffer, because the address may change when the {\em substitute} buffer is expanded. \item the {\em length} of the text \item a {\em pos} position of the text in the {\em substitute} buffer, taking a value of $-1$ if the text is not in the {\em substitute} buffer or is not to be deallocated from this buffer. \end{itemize} Macro definitions are stored as a hash table, which address is a part of the TeX structure. The equivalence string of a macro in the hash table is a character string, starting with a byte indicating the number of parameters. Two functions, included in the TeX structure, are required: \begin{enumerate} \item the {\em output} function, called to output the substituted text \item the {\em action} function, called when one of the actions listed above is found, or when the {\tt\b action} macro is encountered. \end{enumerate} \end{TeX} ----------------------------------------------------------------------------*/ #define DEBUG 0 /* For debugging only */ #define PM_LEVEL LEVEL_STR #define PASCAL_DEF 0 #include #include /* Definition of TeX structure */ #include /* Definition of TeX attributes */ #include /* Uses a Hash table for Macros */ #include /* Need to know buffers */ #include /* Time operations */ #include #define HSIZE 200 /* Default Size of Hash table */ #define DEFAULT_EXTENSION ".tex" /* Default extension for include files */ #define SUBSTITUTE_MAX 32 /* Maximal substitution */ #define IFS_MAX 16 /* Maximal embedded if's */ typedef struct s_SDV{ /* Descriptor of pieces of memory */ int pos; /* Position in substitute buffer */ int offset; /* Present position */ int len; /* Length of string */ char *text; /* Text to scan: NULL if in substitute buffer */ } SDV; typedef struct { short len; char buf[TeX_BUF]; } OUT_BUF; static OUT_BUF *out_buf; static unsigned char ifs[IFS_MAX]; static unsigned char *ifc; #define ifs_min (&ifs[0]) #define ifs_max (&ifs[IFS_MAX]) #define StackIFS() if (++ifc >= ifs_max) { \ ERROR("Too many embedded \\if's"); \ ifc--; status = NOK; FINISH; } \ else *ifc = *(ifc-1) #define UnstackIFS() if (ifc > ifs_min) ifc-- static SDV sdv_pieces[SUBSTITUTE_MAX]; #define sdv_min (&sdv_pieces[0]) #define sdv_max (&sdv_pieces[SUBSTITUTE_MAX]) static SDV *sdvc; /* Current piece to substitute */ static SDV sdvb0 = {-1, 0, 1, (char *)0}; /* Blank descriptor */ static SDV ssdvp[10]; /* Where are the parameters */ static SDV *sdvp; /* Parameters addresses */ #define GetString(sdv) ((sdv)->text ? (sdv)->text : substitute->buf) #define GetText(sdv) GetString(sdv) + (sdv)->offset #define Text(string,sdv) string + (sdv)->offset #define Length(sdv) ((sdv)->len - (sdv)->offset) #define GobbleSpaces(s,sdv) \ (sdv)->offset += skip_spaces(Text(s,sdv),Length(sdv),\ (char *)0) #define GobbleNewlines(s,sdv,ab) \ (sdv)->offset += skip_spaces(Text(s,sdv),Length(sdv),\ ab) #define StackSDV() if (++sdvc >= sdv_max) { \ ERR_SDV(); status = NOK; FINISH; } #define UnstackSDV() if (sdvc->pos >= 0) substitute->used = sdvc->pos;\ sdvc-- static TeX *stex; static char *mode; static H_TABLE *macros; /* Table of Macros */ static BUFFER *substitute; /* Stack of Substitutions */ static BUFFER *param; /* Stack of Parameters */ static int (*fout)(); /* Output function */ static int (*fact)(); /* Action function */ static char len_text[4]; /* Length of a text */ static int *alen; static int error_opt = 1; /* Error report option */ static char EOF_marker[2] = { '0', -1}; MID_RSTATIC char char0 = 0; #define time_pic "dd MMMMMMMMMM YYYY, hh:mm:ss" #define o_time 20 static char time_buf[sizeof(time_pic)]; static SDV sdv_time = {-1, 0, sizeof(time_buf)-1, time_buf}; #define FINISH goto FIN /* Static Functions used before they are defined */ static int tex_rescan(); /* Mandatory, recursivity... */ MONITOR(TeX); /*==========================================================================*/ static int ERR_SDV() /*+++ .PURPOSE Error (too many sdv's) .RETURNS NOK .REMARKS ---*/ { register SDV *sdv; for (sdv = sdv_max-5; sdv < sdv_max; sdv++) ERR_ED_STR2("... ", GetText(sdv), Length(sdv)); sdvc--; ERR_ED_I("Max. substitutions reached: ", SUBSTITUTE_MAX); return(NOK); } /*==========================================================================*/ static int skip_comment (str, len) /*+++ .PURPOSE Skip the comment, gobbling the newline character. .RETURNS Index of first character following comment .REMARKS Not traced. ---*/ char *str; /* IN: text; first byte is % */ int len; /* IN: Length of text */ { register int i; i = oscloc(str, len, '\n'); if (i < len) i++; return(i); } /*==========================================================================*/ static int skip_spaces (str, len, blank) /*+++ .PURPOSE Span spaces, including comments .RETURNS Index of first non-space character .REMARKS Blank contains in return a blank or a newline (if empty lines) ---*/ char *str; /* IN: text */ int len; /* IN: Length of text */ char *blank; /* OUT: blank or newline; if null pointer, stop at \ the first newline */ { char *p, *pe; int nl; for (nl = 0, p = str, pe = str + len; p < pe; ) { if (*p == '\n') { if_not(blank) FINISH; nl++, p++; continue; } if (isTeX(*p, _TeX_SPACE_)) { p++; continue; } if (*p == '\0') { p++; continue; } if (*p == '%') { p += skip_comment(p, pe-p); continue; } break; } if_not(blank) FINISH; *blank = (nl >= 2 ? '\n' : ' '); FIN: return(p-str); } /*==========================================================================*/ static int tex_pout(str, len) /*+++ .PURPOSE Output substituted text in param buffer .RETURNS Length of scanned text .REMARKS *** For internal use ***. At input str-> macro to define. ---*/ char *str; /* IN: String to scan */ int len; /* IN: Length of str */ { BUF_AppendItems(param, char, str, len); return(len); } /*==========================================================================*/ static int tex_lact(str, len) /*+++ .PURPOSE Action routine four count. .RETURNS len .REMARKS Count only blanks. ---*/ char *str; /* IN: String to scan */ int len; /* IN: Length of str */ { char *p, *pe; for (p = str, pe = p + len; p < pe; p ++) { if (*p == ' ') *alen += 1; } return(len); } /*==========================================================================*/ static int tex_lout(str, len) /*+++ .PURPOSE Count total length .RETURNS len .REMARKS Zero bytes are not counted. ---*/ char *str; /* IN: String to scan */ int len; /* IN: Length of str */ { char *p, *pe; int i; for (p = str, pe = p + len; p < pe; p += i) { i = oscloc(p, pe-p, '\0'); *alen += i; if (i == 0) i = 1; } return(len); } /*==========================================================================*/ static int act0() /*+++ .PURPOSE Internal action routine, to output the collected buffer .RETURNS OK (processed) / NOK (not fully processed) .REMARKS ---*/ { int i; i = (*fout)((&out_buf->buf[0]), out_buf->len); if (i < out_buf->len) /* Not all bytes processed */ out_buf->len = oscopy(&out_buf->buf[0], &out_buf->buf[i], out_buf->len - i), i = NOK; else out_buf->len = 0, i = OK; return(i); } /*==========================================================================*/ static int tex_macro(str, len) /*+++ .PURPOSE Compute length of a macro .RETURNS Length .REMARKS Not traced. ---*/ char *str; /* IN: text; first byte is \ */ int len; /* IN: Length of text */ { register int i; if (len < 2) return(len); if (isTeX(*(str+1), _TeX_ALPHA_)) /* Macro name with alpha char */ i = 1 + TeX_span(str+1, len-1, _TeX_ALPHA_); else i = 2; return(i); } /*==========================================================================*/ static int getlen(str, len) /*+++ .PURPOSE Get length of text .RETURNS Length of text, also edited in len_text .REMARKS Parameter is rescanned. ---*/ char *str; /* IN: Text string */ int len; /* IN: Length of text */ { int count, i, n; SDV sdv; sdv.pos = -1, sdv.offset = 0, sdv.len = len, sdv.text = str; count = 0; alen = &count; tex_rescan(&sdv, tex_lout, tex_lact, error_opt); /* Edit the resulting count */ for (i = sizeof(len_text), n = count; --i >= 0; n /= 10) len_text[i] = '0' + n%10; return(count); } /*==========================================================================*/ static int tex_init(htex) /*+++ .PURPOSE Initialize TeX parsing .RETURNS OK / NOK .REMARKS Copies locally parts of TeX structure for easier communication with subroutines. TeX structure also installed if not yet done. ---*/ TeX *htex; /* IN: The TeX header */ { int status, id, ld, ieq, leq; /* Note: sequence \05\03mask for Set Mode Mask, \05\04mask for Clear Mode Mask */ MID_STATIC char definitions[] = {"\ \\begin:1\01#1\0\ \\end:1\02#1\0\ \\iffalse:0\021\0\ \\iftrue:0\021\01\0\ \\else:0\021\02\0\ \\fi:0\021\03\0\ \\action:1\03\0\ \\ifdef:0\06\0\ \\def:1\06\0\ \\defenv:2\06\0\ \01verbatim:0\05\01\0\ \02verbatim:0\05\02\0\ \\obeylines:0\05\03\101\0\ \\obeyspaces:0\05\03\103\0\ \\noobey:0\05\04\103\0\ \\b:0\05\\\0\ \\\\:0\03\r\04\0\ \\len:1\022#1\022\0\ \\today:0\023A\0\ \\now:0\023.\0\ \\time:0\023B\0\ \\inputverbatim:1\07\05\0\ \\input:1\07\01"}; ENTER("tex_init"); status = NOK; sdvp = ssdvp; stex = htex; macros = htex->macros; fout = htex->output; fact = htex->action; mode = &(htex->mode); out_buf = (OUT_BUF *)(&(htex->nbuf)); if_not(fout) { ERROR("No output function"); FINISH; } if_not(fact) { ERROR("No Action function"); FINISH; } /* Look if contents of TeX header is OK */ if_not(substitute) if_not(substitute = BUF_Open(char,1024,1024)) FINISH; if_not(param) if_not(param = BUF_Open(char,128,128)) FINISH; if_not(macros) macros = H_Create(HSIZE); if_not(macros) FINISH; /* Failed to allocate */ htex->macros = macros; if(macros->symbols == 0) /* Insert Basic Macros */ { atex_(); /* Ensures the inclusion of atex module */ for (id=0; idoffset += lm; TRACE_ED_STR2("Substitute: ", p, lm); if_not(pi = h_look(macros, p, lm)) /* Macro Not Found */ { if ((*ifc) && (error_opt)) { if (*p == '\\') ERR_ED_STR2("Undefined Macro: ", p, lm); else ERR_ED_STR2("Unknown Environment: ", p+1,lm-1); } status = NOK; FINISH; } sdv0.offset= 0; sdv0.len = lm; sdv0.text = &pi->strings[0]; eq = &(pi->strings[1+lm]); /* Equivalence text of macro */ GobbleSpaces(string,sdvin); /* 2. Allocate new SDV */ StackSDV(); sdvc->pos = -1, sdvc->offset = 0, sdvc->len = 0, sdvc->text = NULL_PTR(char); /* 3. If the macro has no parameter, exit here */ np = *(eq++) - '0'; if ((np == 0) && (*eq != _TeX_DEF_)) /* No parameters. Simply give the string */ { sdvc->text = eq; sdvc->len = strlen(eq); *sdvp = sdv0; /* Save macro name */ FINISH; } /* 3'. If Text to be ignored, exit here */ if (*ifc == 0) { status = NOK; FINISH; } /* 4. In case of \def: get name of macro to define */ string = GetString(sdvin); if (*eq == _TeX_DEF_) { p = Text(string, sdvin); sdv0 = *sdvin, sdv0.pos = -1; lm = tex_macro(p, Length(sdvin)); sdvin->offset += lm; sdv0.len = sdv0.offset + lm; /* 4': Ifdef macro */ if (np == 0) { ifdef[1] = (h_look(macros, p, lm) ? '\01' : '\0'); sdvc->text = ifdef, sdvc->len = 2; FINISH; } /* 4": Retrieve number of parameters in \def, * form #1#2... */ GobbleSpaces(string,sdvin); for (npdef='0'; Length(sdvin)>0; ) { p = Text(string, sdvin); if (*p != '#') break; p++, sdvin->offset += 1; if (isTeX(*p, _TeX_DIGIT_)) npdef = *p; else ERR_ED_STR2("Bad # in \\def\\", GetText(&sdv0), Length(&sdv0)), status = NOK; sdvin->offset += 1; } } /* 5: Get addresses of parameters */ for (i = 1; i <= np; i++) { sdvx = (*eq == _TeX_ACTION_ ? &sdva : sdvp+i); GobbleNewlines(string, sdvin, &blank); sdvx->pos = -1, sdvx->offset = 0, sdvx->len = 0; while ((Length(sdvin) <= 0) && (sdvin > sdv_min)) /* Try to get previous */ sdvin--, string = GetString(sdvin), GobbleNewlines(string, sdvin, &blank); if ( (Length(sdvin) <= 0) || (string[sdvin->offset] != '{')) { ERR_ED_STR2("Missing parameter(s) in ", &pi->strings[0], (int)pi->ls); status = NOK; continue; } sdvin->offset += 1; /* Skip the { */ *sdvx = *sdvin, sdvx->pos = -1; lm = tex_unit(Text(string, sdvin), Length(sdvin)); sdvin->offset += lm; sdvx->len = sdvx->offset + lm; if ( string[sdvin->offset] != '}') ERR_ED_STR2("Missing } in ", &pi->strings[0], (int)pi->ls), status = NOK; else sdvin->offset += 1; /* Skip the } */ } /* 6. If \def, insert now definition in table */ if (*eq == _TeX_DEF_) { string = GetText(&sdv0), lm = Length(&sdv0); x = *string; for (i=1; i <= np; i++, npdef = '0') { sdvx = sdvp + i; sdvx->offset -= 1; /* Get 1 byte for parameters */ if (np > 1) *string = i; p = GetText(sdvx); y = *p, *p = npdef; h_add(macros, string, lm, p, Length(sdvx)); *p = y; } *string = x; UnstackSDV(); FINISH; } sdvc->pos = substitute->used; /* To compute length of substituted string */ sdvc->offset = sdvc->pos; /* 7. If \action, rescan text to get correct action. */ if (*eq == _TeX_ACTION_) { i = param->used; tex_rescan(&sdva, tex_pout, tex_pout, error_opt); BUF_AppendItems(substitute, char, &defaction[0], 1); BUF_AppendItems(substitute, char, param->buf + i, param->used - i); BUF_AppendItems(substitute, char, &defaction[1], 1); param->used = i; sdvc->len = substitute->used; FINISH; } /* 8. Copy equivalence text, with substitution of #'s by parameters * Note that parameters are preceded / followed with nuls * to avoid concatenation in names */ *sdvp = sdv0; /* Save macro name */ for ( ; *eq; eq += i) { i = 0; switch(*eq) { case '\\': /* Just in case of \ followed by EOS */ i = (*(eq+1) ? 2 : 1); break; case '#': /* Substitute parameter */ eq++, i = 1; if_not(isTeX(*eq,_TeX_DIGIT_)) { ERR_ED_STR2("Bad #-parameter(s) in ", &pi->strings[0], (int)pi->ls); status = NOK; } else { sdvx = sdvp + (*eq - '0'); p = substitute->buf + substitute->used - 1; if (isTeX(*p,_TeX_GRAPH_)) BUF_AppendItem(substitute, char, &char0); BUF_AppendItems(substitute, char, GetText(sdvx), Length(sdvx)); p = substitute->buf + substitute->used - 1; if (isTeX(*p,_TeX_GRAPH_)) BUF_AppendItem(substitute, char, &char0); } continue; } if (i == 0) i = TeX_span(eq, LENGTH, _TeX_NORMAL_); if (i == 0) i = 1; BUF_AppendItems(substitute, char, eq, i); } sdvc->len = substitute->used; FIN: EXIT(status); } /*==========================================================================*/ static int getparm(np) /*+++ .PURPOSE Get parameter #np in TeX structure .RETURNS OK / NOK (np outside range 0..9) .REMARKS Parameter is rescanned. ---*/ int np; /* IN: Parameter number, range [0...9] */ { int status, old_offset; status = OK; old_offset = param->offset; param->offset = param->used; if ((np>0) && (np <= 9)) { tex_rescan(sdvp+np, tex_pout, tex_pout, error_opt); BUF_AppendItem(param, char, &char0); /* Terminate with EOS */ stex->ap = param->buf + old_offset; } else if (np == 0) /* Don't rescan parameter 0 = macro name */ stex->ap = GetText(sdvp); else stex->ap = &char0, status = NOK; param->used = param->offset; param->offset = old_offset; return(status); } /*==========================================================================*/ static int act9(sdv, len) /*+++ .PURPOSE Internal action routine, to substitute parameters .RETURNS OK / NOK / EOF .REMARKS ---*/ SDV *sdv; /* IN: Action string */ int len; /* IN: Length of string */ { int i; char *str; str = GetText(sdv); if ((out_buf) && (out_buf->len)) /* First, output what remains in buffer */ if_not(act0()) return(0); if_not(*ifc) return(len); /* Nothing iffalse */ i = TeX_scan(str, len, _TeX_DIGIT_); /* Digit action => get parameters */ if (i < len) { i = *(str + i) - '0'; getparm(i); str = GetText(sdv); } return((*fact)(str,len)); } /*==========================================================================*/ static int out9(str, len) /*+++ .PURPOSE Internal output routine. .RETURNS Length processed. .REMARKS Copied to tex buffer. ---*/ char *str; /* IN: Action string */ int len; /* IN: Length of string */ { int i; i = 0; if_not(*ifc) return(len); /* Nothing iffalse */ if_not(out_buf) goto Immediate_Output; if (*mode == _TeX_VERBATIM_) /* Output immediately */ { if (out_buf->len) /* First, output what remains in buffer */ if_not(act0()) FINISH; } if ((out_buf->len + len) > TeX_BUF) if (out_buf->len) if_not(act0()) FINISH; if (len > TeX_BUF) goto Immediate_Output; else i = oscopy(&out_buf->buf[out_buf->len], str, len), out_buf->len += i; FINISH; Immediate_Output: i = (*fout)(str,len); FIN: return(i); } /*==========================================================================*/ static int load1(fn, pos, len, opt) /*+++ .PURPOSE Loads len bytes from file fid. .RETURNS Number of bytes loaded. .REMARKS The file is closed... ---*/ char *fn; /* IN: File Name */ long pos; /* IN: Position to start loading */ int len; /* IN: Number of bytes to load */ int opt; /* IN: Option _TeX_VERBATIM_ for inputverbatim */ { char *b; int status, l; status = 0; l = len; if (opt == _TeX_VERBATIM_) l += 4; StackSDV(); sdvc->pos = substitute->used; sdvc->offset = sdvc->pos; sdvc->len = sdvc->pos; sdvc->text = NULL_PTR(char); if_not (BUF_AllocateItems(substitute, char, l)) FINISH; b = substitute->buf + sdvc->pos; if (opt == _TeX_VERBATIM_) /* Add begin{verbatim} */ *b = '\05', *(b+1) = '\01', b += 2; b += fi_load(NameFile(fn,DEFAULT_EXTENSION), pos, b, len); if (opt == _TeX_VERBATIM_) /* Add end{verbatim} */ *b = '\05', *(b+1) = '\02', b += 2; sdvc->len = (b - substitute->buf); /* Final Length */ status = Length(sdvc); TRACE_ED_STR2("Input: ", substitute->buf + sdvc->offset, status); FIN: return(status); } /*==========================================================================*/ static int input1(opt) /*+++ .PURPOSE Includes a file in the stack (macro \input{}) .RETURNS OK / NOK .REMARKS Parameter is sdvp[1]. ---*/ int opt; /* IN: Option _TeX_VERBATIM_ for inputverbatim */ { char *endfile, *b, x; SDV *sdvx; int status, fid; long int fs; sdvx = sdvp + 1; b = GetText(sdvx); endfile = b + Length(sdvx); x = *endfile; /* Save ending character */ *endfile = EOS; status = NOK; fs = fi_size(NameFile(b,DEFAULT_EXTENSION)); /* Mod. 28-Mar-89 Size of File */ load1(b, 0L, (int)fs, opt); *endfile = x; /* Restore last byte */ return(status); } /*==========================================================================*/ static int tex_1exec(sdv_stop) /*+++ .PURPOSE Transform the string .RETURNS OK / NOK (not finished) .REMARKS ---*/ SDV *sdv_stop; /* IN: Ending position */ { char *p; int i, l, n, status, returned_status; char blank; SDV sdvb; /* Blank descriptor */ returned_status = NOK; status = NOK; sdvb = sdvb0; sdvb.text = ␣ if ((out_buf) && (out_buf->len)) /* Output what's in the buffer */ if_not(act0()) FINISH; for( ; sdvc >= sdv_stop; sdvc->offset += i) { i = 0; if (sdvc->offset >= sdvc->len) /* This piece is exhausted */ { UnstackSDV(); continue; } p = GetText(sdvc), l = Length(sdvc); if (status == EOF) /* \EOF was encountered */ { i = l, status = NOK; /* To force unstack */ continue; } switch(*p) { case _TeX_VERBATIM_: /* Simply switch state */ i = 2; switch(*++p) { case _TeX_BEGIN_: *mode = _TeX_VERBATIM_; break; case _TeX_END_: *mode = 0; break; case _TeX_ACTION_: i = 3; *mode |= p[1]; break; case _TeX_ENDACTION_: i = 3; *mode &= ~p[1]; break; default: if_not(out9(p,1)) FINISH; } continue; case _TeX_IF_: /* Test */ switch(*++p) { case 0: case 1: /* New if */ StackIFS(); *ifc &= * (unsigned char *)p; break; case 2: /* Else */ if (ifc == ifs_min) ERROR("\\else outside \\if ... \\fi"); else *ifc = *(ifc-1) & (*ifc ^ 1); break; default: /* Endif */ UnstackIFS(); break; } i = 2; continue; case -1: /* EOF */ if ((out_buf) && (out_buf->len)) if_not(act0()) FINISH; status = EOF; i = l; continue; case 0: case _TeX_ENDACTION_: i = 1; continue; case '~': /* Non-breakable space */ blank = (*mode == _TeX_VERBATIM_ ? '~' : ' '); if_not(i = out9(&blank, 1)) FINISH; continue; case '\\': if (*mode == _TeX_VERBATIM_) { if (l < 14) break; if (oscomp(p,"\\end{verbatim}", 14) != 0) break; /* Not the \end{verbatim} */ } blank = *(p+1); /* Char following escape */ if (isTeX(blank, _TeX_DIGIT_)) { status = act9(&sdvb, 1); if(status == 0) FINISH; i = 2; continue; } if (isTeX(blank, _TeX_SPACE_|_TeX_ESCAPE_)) { if(blank != '\\') { if (isTeX(blank, _TeX_SPACE_)) blank = ' '; if_not(out9(&blank,1)) FINISH; i = 2; continue; } } case _TeX_BEGIN_: case _TeX_END_: /* Also macro and \\ */ tex_subs(); continue; case '%': /* Fetch the next newline */ i = skip_comment(p, l); if (*mode == _TeX_VERBATIM_) break; continue; case _TeX_INCLUDE_: /* Special Action */ sdvc->offset += 2; /* ... will change SDV */ if (*ifc) input1(*++p); continue; case _TeX_LEN_: /* Special Action */ i = oscloc(++p, l-1, _TeX_LEN_); n = getlen(p, i); i += 2; /* Skip beginning / end of string */ if_not(out9(len_text, sizeof(len_text))) FINISH; continue; case _TeX_TIME_: /* Special Action */ sdvc->offset += 2; /* ... will change SDV */ ed_t(time_buf, time_pic, oshtime()); StackSDV(); *sdvc = sdv_time; switch(*++p) { case 'A': /* Date only */ sdvc->len = o_time - 2; break; case 'B': /* Time only */ sdvc->offset = o_time; break; default : /* Both */ break; } continue; case _TeX_ACTION_: /* Special Action */ n = oscskip(p, l, _TeX_ACTION_); p += n, l -= n; i = oscloc(p, l, _TeX_ENDACTION_); if (i) /* There is an action ... */ { sdvc->offset += n; status = act9(sdvc, i); sdvc->offset -= n; if (status == 0) FINISH; } i += n+1; /* Skip Action */ continue; } if (*mode == _TeX_VERBATIM_) { if (i == 0) i = TeX_span(p, l, _TeX_NORMAL_); if (i == 0) i = 1; if_not(i = out9(p,i)) FINISH; continue; } if (isTeX(*p,_TeX_GRAPH_)) /* Scan Normal Text */ { i = TeX_span(p, l, _TeX_GRAPH_); if_not(i = out9(p, i)) FINISH; continue; } if (isTeX(*p,_TeX_SPACE_)) { /* Scan Blank Text */ if (*mode & 0100) { /* Obey spaces... */ i = skip_spaces(p, l, (char *)0); if (i && (*mode & 02)) { /* ALL SPACES */ if_not(i = out9(p, i)) FINISH; continue; } if (i == 0) blank = '\r', i = 1; /* Next Line */ else blank = ' '; } else i = skip_spaces(p, l, &blank); status = act9(&sdvb, 1); if(status == 0) FINISH; continue; } if_not(isTeX(*p, _TeX_ESCAPE_)) /* Bad Character... */ { ERR_ED_STR2("Bad character: ", p, l); i = 1; continue; } i = 1; /* Escape */ status = act9(sdvc,1); if(status == 0) FINISH; } if ((out_buf) && (out_buf->len)) if_not(act0()) FINISH; returned_status = OK; FIN: return(returned_status); } /*==========================================================================*/ static int tex_rescan(sdv, f_out, f_act, er_opt) /*+++ .PURPOSE Rescan text, storing result via supplied output / action routines .RETURNS OK / NOK .REMARKS ---*/ SDV *sdv; /* IN: String to rescan */ int (*f_out)(); /* IN: The output routine to use */ int (*f_act)(); /* IN: The action routine to use */ int er_opt; /* IN: Error report option: 0=no report */ { int status, old_eropt; int (*old_act)(), (*old_out)(); OUT_BUF *old_buf; SDV sdvp1[10], *old_parm; status = NOK; StackSDV(); *sdvc = *sdv; sdvc->pos = -1; /* Don't free */ param->offset = param->used; old_eropt = error_opt; error_opt = er_opt; old_parm = sdvp; sdvp = &sdvp1[0]; old_act = fact, old_out = fout, old_buf = out_buf; fact = f_act, fout = f_out, out_buf = (OUT_BUF *)0; status = tex_1exec(sdvc); fout = old_out, fact = old_act, out_buf = old_buf; sdvp = old_parm; FIN: error_opt = old_eropt; return(status); } /*==========================================================================*/ int tex_unit(str, len) /*+++ .PURPOSE Scan for a closing brace } .RETURNS Offset of matching } .REMARKS Not traced. ---*/ char *str; /* IN: String to scan; points to char following { */ int len; /* IN: Length of String */ { char *p, *pe; int lb; for (lb = 1, p = str, pe = p+len; p < pe; p++) { switch(*p) { case '\\': p++; continue; case '{': lb++; continue; case '}': lb--; break; case '%': p += skip_comment(p, pe-p) - 1; continue; default: continue; } if (lb == 0) break; } if (lb != 0) ERR_ED_STR2("Missing Right Brace in {",str, p-str); return(p-str); } /*==========================================================================*/ int tex_input(htex, filename, opt) /*+++ .PURPOSE Loads a file, .RETURNS Number of bytes loaded. .REMARKS ---*/ TeX *htex; /* IN: TeX structure */ char *filename; /* IN: Name of file to load. */ int opt; /* IN: Option _TeX_VERBATIM_ for inputverbatim */ { long int fs; int status; ENTER("tex_input"); status = NOK; if_not(tex_init(htex)) FINISH; fs = fi_size(NameFile(filename,DEFAULT_EXTENSION)); /* Size of File */ status = load1(filename, 0L, (int)fs, opt); FIN: EXIT(status); } /*==========================================================================*/ int tex_load(htex, fid, len, opt) /*+++ .PURPOSE Load n bytes from a file, .RETURNS Number of bytes loaded. .REMARKS File is closed... ---*/ TeX *htex; /* IN: TeX structure */ int fid; /* IN: File identifier */ int len; /* IN: Number of bytes to load */ int opt; /* IN: Option _TeX_VERBATIM_ for inputverbatim */ { int status; char *fn; long pos; ENTER("tex_load"); status = NOK; if_not(tex_init(htex)) FINISH; fn = fi_name(fid), pos = fi_tell(fid); close(fid); status = load1(fn, pos, len, opt); FIN: EXIT(status); } /*==========================================================================*/ int tex_list(htex) /*+++ .PURPOSE Display stored macros in Log file. .RETURNS Free space for symbol definitions. .REMARKS ---*/ TeX *htex; /* IN: TeX structure */ { int n, i; H_ITEM *pi; ENTER("+tex_list"); if_not(tex_init(htex)) FINISH; n = h_log(macros); /* Log the size / collisions / free */ for (i=0; i < macros->size; i++) { if_not(macros->start[i]) continue; for (pi = macros->start[i]; pi; pi = pi->next) LOG_ED_STRING((pi->next ? "+" : " "), &pi->strings[0]); } FIN: EXIT(n); } /*==========================================================================*/ int tex_getvparm(np) /*+++ .PURPOSE Get ``verbatim'' parameter #np in TeX structure .RETURNS Length of the parameter. The address is in the TeX structure. -1 is returned in case of error. .REMARKS Parameter is NOT rescanned. ---*/ int np; /* IN: Parameter number, range [0...9] */ { int status; SDV *p; ENTER("+tex_getvparm"); if ((np>0) && (np <= 9)) { p = sdvp + np; stex->ap = GetText(p); status = Length(p); } else status = -1; EXIT(status); } /*==========================================================================*/ int tex_getparm(np) /*+++ .PURPOSE Get parameter #np in TeX structure .RETURNS OK / NOK (np outside range 0..9) .REMARKS Parameter is rescanned. ---*/ int np; /* IN: Parameter number, range [0...9] */ { int status; ENTER("tex_getparm"); BUF_Clear(param); status = getparm(np); EXIT(status); } /*==========================================================================*/ int tex_exec(htex, str, len) /*+++ .PURPOSE Transform the string .RETURNS OK / NOK (not finished) .REMARKS ---*/ TeX *htex; /* IN: The TeX header */ char *str; /* IN: String to scan; NULL to continue */ int len; /* IN: Length of string */ { int status; ENTER("tex_exec"); status = NOK; if_not(tex_init(htex)) FINISH; if(str) /* Start a new set of substitutions */ { BUF_Clear(substitute), BUF_Clear(param); htex->nbuf = 0; ifc = ifs_min; *ifc = 1; /* TRUE */ sdvc = sdv_min; sdvc->pos = -1; sdvc->offset = 0; sdvc->len = len; sdvc->text = str; error_opt = 1; /* Error report is ON */ } status = tex_1exec(sdv_min); FIN: EXIT(status); } /*==========================================================================*/ int tex_mexec(htex, str, nstrings) /*+++ .PURPOSE Multiple transformations .RETURNS OK / NOK (not finished) .REMARKS ---*/ TeX *htex; /* IN: The TeX header */ char **str; /* IN: Array of strings to scan */ int nstrings; /* IN: Number of strings */ { int status; char **s; ENTER("tex_mexec"); status = NOK; if_not(tex_init(htex)) FINISH; if (nstrings >= SUBSTITUTE_MAX - 2) { ERR_ED_I("To many strings to substitute: ", nstrings); FINISH; } if(nstrings > 0) /* Start a new set of substitutions */ { BUF_Clear(substitute), BUF_Clear(param); htex->nbuf = 0; ifc = ifs_min; *ifc = 1; /* TRUE */ error_opt = 1; /* Error report is ON */ for (s = str + 2*nstrings, sdvc = sdv_min; s > str; sdvc++) { s -= 2; sdvc->pos = -1; sdvc->offset = 0; sdvc->text = *s; sdvc->len = *(s+1) - sdvc->text; } sdvc--; } status = tex_1exec(sdv_min); FIN: EXIT(status); } /*==========================================================================*/ int tex_tell() /*+++ .PURPOSE Tell where we are in the TeX processing .RETURNS Offset from last string passed to tex_exec / -1 if terminated .REMARKS ---*/ { int status; ENTER("+tex_tell"); status = (sdvc >= sdv_min ? (sdv_min)->offset : -1); EXIT(status); } /*==========================================================================*/ char *tex_symbol(str) /*+++ .PURPOSE Retrieve a symbol .RETURNS Address of processed symbol / NULL .REMARKS Uses the last TeX structure ---*/ char *str; /* IN: String to substitute */ { char *p; int old_offset; SDV sdvl; ENTER("*tex_symbol"); sdvl.pos = -1; sdvl.offset = 0; sdvl.len = strlen(str); sdvl.text = str; old_offset = param->offset; /* Save */ param->offset = param->used; tex_rescan(&sdvl, tex_pout, tex_pout, 0); BUF_AppendItem(param, char, &char0); /* Terminate with EOS */ param->used = param->offset; /* Restore */ param->offset = old_offset; p = param->buf + param->offset; EXITp(p); }