LCOV - code coverage report
Current view: top level - preproc/eqn - lex.cpp (source / functions) Hit Total Coverage
Test: GNU roff Lines: 389 629 61.8 %
Date: 2026-01-16 17:51:41 Functions: 46 67 68.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* Copyright 1989-2025 Free Software Foundation, Inc.
       2             :      Written by James Clark (jjc@jclark.com)
       3             : 
       4             : This file is part of groff, the GNU roff typesetting system.
       5             : 
       6             : groff is free software; you can redistribute it and/or modify it under
       7             : the terms of the GNU General Public License as published by the Free
       8             : Software Foundation, either version 3 of the License, or
       9             : (at your option) any later version.
      10             : 
      11             : groff is distributed in the hope that it will be useful, but WITHOUT ANY
      12             : WARRANTY; without even the implied warranty of MERCHANTABILITY or
      13             : FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      14             : for more details.
      15             : 
      16             : You should have received a copy of the GNU General Public License
      17             : along with this program.  If not, see <http://www.gnu.org/licenses/>. */
      18             : 
      19             : #ifdef HAVE_CONFIG_H
      20             : #include <config.h>
      21             : #endif
      22             : 
      23             : #include <stdcountof.h>
      24             : 
      25             : #include "ptable.h"
      26             : #include "stringclass.h"
      27             : 
      28             : #include "eqn.h"
      29             : #include "eqn.hpp"
      30             : 
      31             : // declarations to avoid friend name injection problems
      32             : int get_char();
      33             : int peek_char();
      34             : int get_location(char **, int *);
      35             : 
      36             : struct definition {
      37             :   char is_macro;
      38             :   char is_simple;
      39             :   union {
      40             :     int tok;
      41             :     char *contents;
      42             :   };
      43             :   definition();
      44             :   ~definition();
      45             : };
      46             : 
      47       11317 : definition::definition() : is_macro(1), is_simple(0)
      48             : {
      49       11317 :   contents = 0;
      50       11317 : }
      51             : 
      52           0 : definition::~definition()
      53             : {
      54           0 :   if (is_macro)
      55           0 :     free(contents);
      56           0 : }
      57             : 
      58             : declare_ptable(definition)
      59      311697 : implement_ptable(definition)
      60             : 
      61             : PTABLE(definition) macro_table;
      62             : 
      63             : static struct eqn_primitive {
      64             :   const char *name;
      65             :   int token;
      66             : } token_table[] = {
      67             :   { "over", OVER },
      68             :   { "smallover", SMALLOVER },
      69             :   { "sqrt", SQRT },
      70             :   { "sub", SUB },
      71             :   { "sup", SUP },
      72             :   { "lpile", LPILE },
      73             :   { "rpile", RPILE },
      74             :   { "cpile", CPILE },
      75             :   { "pile", PILE },
      76             :   { "left", LEFT },
      77             :   { "right", RIGHT },
      78             :   { "to", TO },
      79             :   { "from", FROM },
      80             :   { "size", SIZE },
      81             :   { "font", FONT },
      82             :   { "roman", ROMAN },
      83             :   { "bold", BOLD },
      84             :   { "italic", ITALIC },
      85             :   { "fat", FAT },
      86             :   { "bar", BAR },
      87             :   { "under", UNDER },
      88             :   { "accent", ACCENT },
      89             :   { "uaccent", UACCENT },
      90             :   { "above", ABOVE },
      91             :   { "fwd", FWD },
      92             :   { "back", BACK },
      93             :   { "down", DOWN },
      94             :   { "up", UP },
      95             :   { "matrix", MATRIX },
      96             :   { "col", COL },
      97             :   { "lcol", LCOL },
      98             :   { "rcol", RCOL },
      99             :   { "ccol", CCOL },
     100             :   { "mark", MARK },
     101             :   { "lineup", LINEUP },
     102             :   { "space", SPACE },
     103             :   { "gifont", GIFONT },
     104             :   { "gfont", GFONT }, // for backward compatibility
     105             :   { "gsize", GSIZE },
     106             :   { "define", DEFINE },
     107             :   { "sdefine", SDEFINE },
     108             :   { "ndefine", NDEFINE },
     109             :   { "tdefine", TDEFINE },
     110             :   { "undef", UNDEF },
     111             :   { "ifdef", IFDEF },
     112             :   { "include", INCLUDE },
     113             :   { "copy", INCLUDE },
     114             :   { "delim", DELIM },
     115             :   { "chartype", CHARTYPE },
     116             :   { "type", TYPE },
     117             :   { "vcenter", VCENTER },
     118             :   { "set", SET },
     119             :   { "reset", RESET },
     120             :   { "opprime", PRIME },
     121             :   { "grfont", GRFONT },
     122             :   { "gbfont", GBFONT },
     123             :   { "split", SPLIT },
     124             :   { "nosplit", NOSPLIT },
     125             :   { "special", SPECIAL },
     126             : };
     127             : 
     128             : struct builtin_def {
     129             :   const char *name;
     130             :   const char *def;
     131             : };
     132             : 
     133             : static struct builtin_def common_defs[] = {
     134             :   { "ALPHA", "\\(*A" },
     135             :   { "BETA", "\\(*B" },
     136             :   { "CHI", "\\(*X" },
     137             :   { "DELTA", "\\(*D" },
     138             :   { "EPSILON", "\\(*E" },
     139             :   { "ETA", "\\(*Y" },
     140             :   { "GAMMA", "\\(*G" },
     141             :   { "IOTA", "\\(*I" },
     142             :   { "KAPPA", "\\(*K" },
     143             :   { "LAMBDA", "\\(*L" },
     144             :   { "MU", "\\(*M" },
     145             :   { "NU", "\\(*N" },
     146             :   { "OMEGA", "\\(*W" },
     147             :   { "OMICRON", "\\(*O" },
     148             :   { "PHI", "\\(*F" },
     149             :   { "PI", "\\(*P" },
     150             :   { "PSI", "\\(*Q" },
     151             :   { "RHO", "\\(*R" },
     152             :   { "SIGMA", "\\(*S" },
     153             :   { "TAU", "\\(*T" },
     154             :   { "THETA", "\\(*H" },
     155             :   { "UPSILON", "\\(*U" },
     156             :   { "XI", "\\(*C" },
     157             :   { "ZETA", "\\(*Z" },
     158             :   { "Alpha", "\\(*A" },
     159             :   { "Beta", "\\(*B" },
     160             :   { "Chi", "\\(*X" },
     161             :   { "Delta", "\\(*D" },
     162             :   { "Epsilon", "\\(*E" },
     163             :   { "Eta", "\\(*Y" },
     164             :   { "Gamma", "\\(*G" },
     165             :   { "Iota", "\\(*I" },
     166             :   { "Kappa", "\\(*K" },
     167             :   { "Lambda", "\\(*L" },
     168             :   { "Mu", "\\(*M" },
     169             :   { "Nu", "\\(*N" },
     170             :   { "Omega", "\\(*W" },
     171             :   { "Omicron", "\\(*O" },
     172             :   { "Phi", "\\(*F" },
     173             :   { "Pi", "\\(*P" },
     174             :   { "Psi", "\\(*Q" },
     175             :   { "Rho", "\\(*R" },
     176             :   { "Sigma", "\\(*S" },
     177             :   { "Tau", "\\(*T" },
     178             :   { "Theta", "\\(*H" },
     179             :   { "Upsilon", "\\(*U" },
     180             :   { "Xi", "\\(*C" },
     181             :   { "Zeta", "\\(*Z" },
     182             :   { "alpha", "\\(*a" },
     183             :   { "beta", "\\(*b" },
     184             :   { "chi", "\\(*x" },
     185             :   { "delta", "\\(*d" },
     186             :   { "epsilon", "\\(*e" },
     187             :   { "eta", "\\(*y" },
     188             :   { "gamma", "\\(*g" },
     189             :   { "iota", "\\(*i" },
     190             :   { "kappa", "\\(*k" },
     191             :   { "lambda", "\\(*l" },
     192             :   { "mu", "\\(*m" },
     193             :   { "nu", "\\(*n" },
     194             :   { "omega", "\\(*w" },
     195             :   { "omicron", "\\(*o" },
     196             :   { "phi", "\\(*f" },
     197             :   { "pi", "\\(*p" },
     198             :   { "psi", "\\(*q" },
     199             :   { "rho", "\\(*r" },
     200             :   { "sigma", "\\(*s" },
     201             :   { "tau", "\\(*t" },
     202             :   { "theta", "\\(*h" },
     203             :   { "upsilon", "\\(*u" },
     204             :   { "xi", "\\(*c" },
     205             :   { "zeta", "\\(*z" },
     206             :   { "max", "{type \"operator\" roman \"max\"}" },
     207             :   { "min", "{type \"operator\" roman \"min\"}" },
     208             :   { "lim", "{type \"operator\" roman \"lim\"}" },
     209             :   { "sin", "{type \"operator\" roman \"sin\"}" },
     210             :   { "cos", "{type \"operator\" roman \"cos\"}" },
     211             :   { "tan", "{type \"operator\" roman \"tan\"}" },
     212             :   { "sinh", "{type \"operator\" roman \"sinh\"}" },
     213             :   { "cosh", "{type \"operator\" roman \"cosh\"}" },
     214             :   { "tanh", "{type \"operator\" roman \"tanh\"}" },
     215             :   { "arc", "{type \"operator\" roman \"arc\"}" },
     216             :   { "log", "{type \"operator\" roman \"log\"}" },
     217             :   { "ln", "{type \"operator\" roman \"ln\"}" },
     218             :   { "exp", "{type \"operator\" roman \"exp\"}" },
     219             :   { "Re", "{type \"operator\" roman \"Re\"}" },
     220             :   { "Im", "{type \"operator\" roman \"Im\"}" },
     221             :   { "det", "{type \"operator\" roman \"det\"}" },
     222             :   { "and", "{roman \"and\"}" },
     223             :   { "if", "{roman \"if\"}" },
     224             :   { "for", "{roman \"for\"}" },
     225             :   { "times", "type \"binary\" \\(mu" },
     226             :   { "ldots", "type \"inner\" { . . . }" },
     227             :   { "inf", "\\(if" },
     228             :   { "partial", "\\(pd" },
     229             :   { "nothing", "\"\"" },
     230             :   { "half", "{1 smallover 2}" },
     231             :   { "hat_def", "roman \"^\"" },
     232             :   { "hat", "accent { hat_def }" },
     233             :   { "tilde_def", "roman \"~\"" },
     234             :   { "tilde", "accent { tilde_def }" },
     235             :   { "==", "type \"relation\" \\(==" },
     236             :   { "!=", "type \"relation\" \\(!=" },
     237             :   { "+-", "type \"binary\" \\(+-" },
     238             :   { "->", "type \"relation\" \\(->" },
     239             :   { "<-", "type \"relation\" \\(<-" },
     240             :   { "<<", "type \"relation\" \\(<<" },
     241             :   { ">>", "type \"relation\" \\(>>" },
     242             :   { "prime", "'" },
     243             :   { "approx", "type \"relation\" \"\\(~=\"" },
     244             :   { "grad", "\\(gr" },
     245             :   { "del", "\\(gr" },
     246             :   { "cdot", "type \"binary\" \\(md" },
     247             :   { "cdots", "type \"inner\" { \\(md \\(md \\(md }" },
     248             :   { "dollar", "$" },
     249             : };  
     250             : 
     251             : /* composite definitions that require troff size and motion operators */
     252             : static struct builtin_def troff_defs[] = {
     253             :   { "sum", "{type \"operator\" vcenter size +5 \\(*S}" },
     254             :   { "prod", "{type \"operator\" vcenter size +5 \\(*P}" },
     255             :   { "int", "{type \"operator\" vcenter size +8 \\(is}" },
     256             :   { "union", "{type \"operator\" vcenter size +5 \\(cu}" },
     257             :   { "inter", "{type \"operator\" vcenter size +5 \\(ca}" },
     258             :   { "dot_def", "up 52 back 15 \".\"" },
     259             :   { "dot", "accent { dot_def }" },
     260             :   { "dotdot_def", "up 52 back 25 \"..\"" },
     261             :   { "dotdot", "accent { dotdot_def }" },
     262             :   { "utilde_def", "down 75 roman \"~\"" },
     263             :   { "utilde", "uaccent { utilde_def }" },
     264             :   { "vec_def", "up 52 size -5 \\(->" },
     265             :   { "vec", "accent { vec_def }" },
     266             :   { "dyad_def", "up 52 size -5 { \\(<> }" },
     267             :   { "dyad", "accent { dyad_def }" },
     268             :   { "...", "type \"inner\" { . . . }" },
     269             : };
     270             : 
     271             : /* equivalent definitions for MathML mode */
     272             : static struct builtin_def mathml_defs[] = {
     273             :   { "sum", "{type \"operator\" size big \\(*S}" },
     274             :   { "prod", "{type \"operator\" size big \\(*P}" },
     275             :   { "int", "{type \"operator\" size big \\(is}" },
     276             :   { "union", "{type \"operator\" size big \\(cu}" },
     277             :   { "inter", "{type \"operator\" size big \\(ca}" },
     278             :   { "dot", "accent { \".\" }" },
     279             :   { "dotdot", "accent { \"..\" }" },
     280             :   { "utilde", "uaccent { \"~\" }" },
     281             :   { "vec", "accent { \\(-> }" },
     282             :   { "dyad", "accent { \\(<> }" },
     283             :   { "...", "type \"inner\" { . . . }" },
     284             : };
     285             : 
     286          59 : void init_table(const char *device)
     287             : {
     288             :   unsigned int i;
     289        3540 :   for (i = 0; i < countof(token_table); i++) {
     290        6962 :     definition *def = new definition[1];
     291        3481 :     def->is_macro = 0;
     292        3481 :     def->tok = token_table[i].token;
     293        3481 :     macro_table.define(token_table[i].name, def);
     294             :   }
     295        6844 :   for (i = 0; i < countof(common_defs); i++) {
     296       13570 :     definition *def = new definition[1];
     297        6785 :     def->is_macro = 1;
     298        6785 :     def->contents = strsave(common_defs[i].def);
     299        6785 :     def->is_simple = 1;
     300        6785 :     macro_table.define(common_defs[i].name, def);
     301             :   }
     302          59 :   if (output_format == troff) {
     303        1003 :     for (i = 0; i < countof(troff_defs); i++) {
     304        1888 :       definition *def = new definition[1];
     305         944 :       def->is_macro = 1;
     306         944 :       def->contents = strsave(troff_defs[i].def);
     307         944 :       def->is_simple = 1;
     308         944 :       macro_table.define(troff_defs[i].name, def);
     309             :     }
     310             :   }
     311           0 :   else if (output_format == mathml) {
     312           0 :     for (i = 0; i < countof(mathml_defs); i++) {
     313           0 :       definition *def = new definition[1];
     314           0 :       def->is_macro = 1;
     315           0 :       def->contents = strsave(mathml_defs[i].def);
     316           0 :       def->is_simple = 1;
     317           0 :       macro_table.define(mathml_defs[i].name, def);
     318             :     }
     319             :   }
     320         118 :   definition *def = new definition[1];
     321          59 :   def->is_macro = 1;
     322          59 :   def->contents = strsave("1");
     323          59 :   macro_table.define(device, def);
     324          59 : }
     325             : 
     326             : class input {
     327             :   input *next;
     328             : public:
     329             :   input(input *p);
     330             :   virtual ~input();
     331             :   virtual int get() = 0;
     332             :   virtual int peek() = 0;
     333             :   virtual int get_location(char **, int *);
     334             : 
     335             :   friend int get_char();
     336             :   friend int peek_char();
     337             :   friend int get_location(char **, int *);
     338             :   friend void init_lex(const char *str, const char *filename,
     339             :                        int lineno);
     340             : };
     341             : 
     342             : class file_input : public input {
     343             :   FILE *fp;
     344             :   char *filename;
     345             :   int lineno;
     346             :   string line;
     347             :   const char *ptr;
     348             :   int read_line();
     349             : public:
     350             :   file_input(FILE *, const char *, input *);
     351             :   ~file_input();
     352             :   int get();
     353             :   int peek();
     354             :   int get_location(char **, int *);
     355             : };
     356             : 
     357             : 
     358             : class macro_input : public input {
     359             :   char *s;
     360             :   char *p;
     361             : public:
     362             :   macro_input(const char *, input *);
     363             :   ~macro_input();
     364             :   int get();
     365             :   int peek();
     366             : };
     367             : 
     368             : class top_input : public macro_input {
     369             :   char *filename;
     370             :   int lineno;
     371             :  public:
     372             :   top_input(const char *, const char *, int, input *);
     373             :   ~top_input();
     374             :   int get();
     375             :   int get_location(char **, int *);
     376             : };
     377             : 
     378             : class argument_macro_input: public input {
     379             :   char *s;
     380             :   char *p;
     381             :   char *ap;
     382             :   int argc;
     383             :   char *argv[9];
     384             : public:
     385             :   argument_macro_input(const char *, int, char **, input *);
     386             :   ~argument_macro_input();
     387             :   int get();
     388             :   int peek();
     389             : };
     390             : 
     391        4262 : input::input(input *x) : next(x)
     392             : {
     393        4262 : }
     394             : 
     395        4262 : input::~input()
     396             : {
     397        4262 : }
     398             : 
     399          38 : int input::get_location(char **, int *)
     400             : {
     401          38 :   return 0;
     402             : }
     403             : 
     404           0 : file_input::file_input(FILE *f, const char *fn, input *p)
     405           0 : : input(p), lineno(0), ptr("")
     406             : {
     407           0 :   fp = f;
     408           0 :   filename = strsave(fn);
     409           0 : }
     410             : 
     411           0 : file_input::~file_input()
     412             : {
     413           0 :   if (fclose(fp) < 0)
     414           0 :     fatal("unable to close '%1': %2", filename, strerror(errno));
     415           0 :   delete[] filename;
     416           0 : }
     417             : 
     418           0 : int file_input::read_line()
     419             : {
     420             :   for (;;) {
     421           0 :     line.clear();
     422           0 :     lineno++;
     423             :     for (;;) {
     424           0 :       int c = getc(fp);
     425           0 :       if (c == '\r') {
     426           0 :         c = getc(fp);
     427           0 :         if (c != '\n')
     428           0 :           lex_error("invalid input character code %1", '\r');
     429             :       }
     430           0 :       if (c == EOF)
     431           0 :         break;
     432           0 :       else if (is_invalid_input_char(c))
     433           0 :         lex_error("invalid input character code %1", c);
     434             :       else {
     435           0 :         line += char(c);
     436           0 :         if (c == '\n') 
     437           0 :           break;
     438             :       }
     439           0 :     }
     440           0 :     if (line.length() == 0)
     441           0 :       return 0;
     442           0 :     if (!(line.length() >= 3 && line[0] == '.' && line[1] == 'E'
     443           0 :           && (line[2] == 'Q' || line[2] == 'N')
     444           0 :           && (line.length() == 3 || line[3] == ' ' || line[3] == '\n'
     445           0 :               || compatible_flag))) {
     446           0 :       line += '\0';
     447           0 :       ptr = line.contents();
     448           0 :       return 1;
     449             :     }
     450           0 :   }
     451             : }
     452             : 
     453           0 : int file_input::get()
     454             : {
     455           0 :   if (*ptr != '\0' || read_line())
     456           0 :     return *ptr++ & 0377;
     457             :   else
     458           0 :     return EOF;
     459             : }
     460             : 
     461           0 : int file_input::peek()
     462             : {
     463           0 :   if (*ptr != '\0' || read_line())
     464           0 :     return *ptr;
     465             :   else
     466           0 :     return EOF;
     467             : }
     468             : 
     469           0 : int file_input::get_location(char **fnp, int *lnp)
     470             : {
     471           0 :   *fnp = filename;
     472           0 :   *lnp = lineno;
     473           0 :   return 1;
     474             : }
     475             : 
     476        4259 : macro_input::macro_input(const char *str, input *x) : input(x)
     477             : {
     478        4259 :   p = s = strsave(str);
     479        4259 : }
     480             : 
     481        4446 : macro_input::~macro_input()
     482             : {
     483        4259 :   free(s);
     484        4446 : }
     485             : 
     486      113865 : int macro_input::get()
     487             : {
     488      113865 :   if (p == 0 || *p == '\0')
     489        4259 :     return EOF;
     490             :   else
     491      109606 :     return *p++ & 0377;
     492             : }
     493             : 
     494       36980 : int macro_input::peek()
     495             : {
     496       36980 :   if (p == 0 || *p == '\0')
     497         207 :     return EOF;
     498             :   else
     499       36773 :     return *p & 0377;
     500             : }
     501             : 
     502        4072 : top_input::top_input(const char *str, const char *fn, int ln, input *x)
     503        4072 : : macro_input(str, x), lineno(ln)
     504             : {
     505        4072 :   filename = strsave(fn);
     506        4072 : }
     507             : 
     508        8144 : top_input::~top_input()
     509             : {
     510        4072 :   free(filename);
     511        8144 : }
     512             : 
     513      109870 : int top_input::get()
     514             : {
     515      109870 :   int c = macro_input::get();
     516      109870 :   if (c == '\n')
     517        6863 :     lineno++;
     518      109870 :   return c;
     519             : }
     520             : 
     521        1165 : int top_input::get_location(char **fnp, int *lnp)
     522             : {
     523        1165 :   *fnp = filename;
     524        1165 :   *lnp = lineno;
     525        1165 :   return 1;
     526             : }
     527             : 
     528             : // Character representing $1.  Must be invalid input character.
     529             : #define ARG1 14
     530             : 
     531           3 : argument_macro_input::argument_macro_input(const char *body, int ac, 
     532           3 :                                            char **av, input *x)
     533           3 : : input(x), ap(0), argc(ac)
     534             : {
     535             :   int i;
     536           9 :   for (i = 0; i < argc; i++)
     537           6 :     argv[i] = av[i];
     538           3 :   p = s = strsave(body);
     539           3 :   int j = 0;
     540          57 :   for (i = 0; s[i] != '\0'; i++)
     541          54 :     if (s[i] == '$' && s[i+1] >= '0' && s[i+1] <= '9') {
     542           6 :       if (s[i+1] != '0')
     543           6 :         s[j++] = ARG1 + s[++i] - '1';
     544             :     }
     545             :     else
     546          48 :       s[j++] = s[i];
     547           3 :   s[j] = '\0';
     548           3 : }
     549             : 
     550             : 
     551           6 : argument_macro_input::~argument_macro_input()
     552             : {
     553           9 :   for (int i = 0; i < argc; i++)
     554           6 :     delete[] argv[i];
     555           3 :   delete[] s;
     556           6 : }
     557             : 
     558         165 : int argument_macro_input::get()
     559             : {
     560         165 :   if (ap) {
     561         111 :     if (*ap != '\0')
     562         108 :       return *ap++ & 0377;
     563           3 :     ap = 0;
     564             :   }
     565          57 :   if (p == 0)
     566           0 :     return EOF;
     567          57 :   while (*p >= ARG1 && *p <= ARG1 + 8) {
     568           6 :     int i = *p++ - ARG1;
     569           6 :     if (i < argc && argv[i] != 0 && argv[i][0] != '\0') {
     570           6 :       ap = argv[i];
     571           6 :       return *ap++ & 0377;
     572             :     }
     573             :   }
     574          51 :   if (*p == '\0')
     575           3 :     return EOF;
     576          48 :   return *p++ & 0377;
     577             : }
     578             : 
     579          78 : int argument_macro_input::peek()
     580             : {
     581          78 :   if (ap) {
     582          66 :     if (*ap != '\0')
     583          63 :       return *ap & 0377;
     584           3 :     ap = 0;
     585             :   }
     586          15 :   if (p == 0)
     587           0 :     return EOF;
     588          15 :   while (*p >= ARG1 && *p <= ARG1 + 8) {
     589           0 :     int i = *p++ - ARG1;
     590           0 :     if (i < argc && argv[i] != 0 && argv[i][0] != '\0') {
     591           0 :       ap = argv[i];
     592           0 :       return *ap & 0377;
     593             :     }
     594             :   }
     595          15 :   if (*p == '\0')
     596           0 :     return EOF;
     597          15 :   return *p & 0377;
     598             : }
     599             : 
     600             : static input *current_input = 0;
     601             : 
     602             : /* we insert a newline between input from different levels */
     603             : 
     604      118102 : int get_char()
     605             : {
     606      118102 :   if (current_input == 0)
     607        4072 :     return EOF;
     608             :   else {
     609      114030 :     int c = current_input->get();
     610      114030 :     if (c != EOF)
     611      109768 :       return c;
     612             :     else {
     613        4262 :       input *tem = current_input;
     614        4262 :       current_input = current_input->next;
     615        4262 :       delete tem;
     616        4262 :       return '\n';
     617             :     }
     618             :   }
     619             : }
     620             : 
     621       37058 : int peek_char()
     622             : {
     623       37058 :   if (current_input == 0)
     624           0 :     return EOF;
     625             :   else {
     626       37058 :     int c = current_input->peek();
     627       37058 :     if (c != EOF)
     628       36851 :       return c;
     629             :     else
     630         207 :       return '\n';
     631             :   }
     632             : }
     633             : 
     634        1165 : int get_location(char **fnp, int *lnp)
     635             : {
     636        1203 :   for (input *p = current_input; p; p = p->next)
     637        1203 :     if (p->get_location(fnp, lnp))
     638        1165 :       return 1;
     639           0 :   return 0;
     640             : }
     641             : 
     642             : string token_buffer;
     643             : const int NCONTEXT = 4;
     644             : string context_ring[NCONTEXT];
     645             : int context_index = 0;
     646             : 
     647        4072 : void flush_context()
     648             : {
     649       20360 :   for (int i = 0; i < NCONTEXT; i++)
     650       16288 :     context_ring[i] = "";
     651        4072 :   context_index = 0;
     652        4072 : }
     653             : 
     654           0 : void show_context()
     655             : {
     656           0 :   int i = context_index;
     657           0 :   fputs(" context is\n\t", stderr);
     658             :   for (;;) {
     659           0 :     int j = (i + 1) % NCONTEXT;
     660           0 :     if (j == context_index) {
     661           0 :       fputs(">>> ", stderr);
     662           0 :       put_string(context_ring[i], stderr);
     663           0 :       fputs(" <<<", stderr);
     664           0 :       break;
     665             :     }
     666           0 :     else if (context_ring[i].length() > 0) {
     667           0 :       put_string(context_ring[i], stderr);
     668           0 :       putc(' ', stderr);
     669             :     }
     670           0 :     i = j;
     671           0 :   }
     672           0 :   putc('\n', stderr);
     673           0 : }
     674             : 
     675       13013 : void add_context(const string &s)
     676             : {
     677       13013 :   context_ring[context_index] = s;
     678       13013 :   context_index = (context_index + 1) % NCONTEXT;
     679       13013 : }
     680             : 
     681         312 : void add_context(char c)
     682             : {
     683         312 :   context_ring[context_index] = c;
     684         312 :   context_index = (context_index + 1) % NCONTEXT;
     685         312 : }
     686             : 
     687          87 : void add_quoted_context(const string &s)
     688             : {
     689          87 :   string &r = context_ring[context_index];
     690          87 :   r = '"';
     691         591 :   for (int i = 0; i < s.length(); i++)
     692         504 :     if (s[i] == '"')
     693           0 :       r += "\\\"";
     694             :     else
     695         504 :       r += s[i];
     696          87 :   r += '"';
     697          87 :   context_index = (context_index + 1) % NCONTEXT;
     698          87 : }
     699             : 
     700        4072 : void init_lex(const char *str, const char *filename, int lineno)
     701             : {
     702        4072 :   while (current_input != 0) {
     703           0 :     input *tem = current_input;
     704           0 :     current_input = current_input->next;
     705           0 :     delete tem;
     706             :   }
     707        4072 :   current_input = new top_input(str, filename, lineno, 0);
     708        4072 :   flush_context();
     709        4072 : }
     710             : 
     711             : 
     712        1165 : void get_delimited_text()
     713             : {
     714             :   char *filename, *last_seen_filename;
     715             :   int lineno;
     716        1165 :   int got_location = get_location(&filename, &lineno);
     717             :   // `filename` gets invalidated if we iterate off the end of the file.
     718        1165 :   last_seen_filename = strdup(filename);
     719        1165 :   int start = get_char();
     720        2330 :   while (start == ' ' || start == '\t' || start == '\n')
     721        1165 :     start = get_char();
     722        1165 :   token_buffer.clear();
     723        1165 :   if (start == EOF) {
     724           0 :     current_lineno = 0;
     725           0 :     if (got_location)
     726           0 :       error_with_file_and_line(last_seen_filename, lineno,
     727             :                                "end of input while defining macro");
     728             :     else
     729           0 :       error("end of input while defining macro");
     730           0 :     free(last_seen_filename);
     731           0 :     return;
     732             :   }
     733             :   for (;;) {
     734       49251 :     int c = get_char();
     735       49251 :     if (c == EOF) {
     736           0 :       current_lineno = 0;
     737           0 :       if (got_location)
     738           0 :         error_with_file_and_line(last_seen_filename, lineno,
     739             :                                  "end of input while defining macro");
     740             :       else
     741           0 :         error("end of input while defining macro");
     742           0 :       add_context(start + token_buffer);
     743           0 :       free(last_seen_filename);
     744           0 :       return;
     745             :     }
     746       49251 :     if (c == start)
     747        1165 :       break;
     748       48086 :     token_buffer += char(c);
     749       48086 :   }
     750        1165 :   add_context(start + token_buffer + start);
     751        1165 :   free(last_seen_filename);
     752             : }
     753             : 
     754           3 : void interpolate_macro_with_args(const char *body)
     755             : {
     756             :   char *argv[9];
     757           3 :   int argc = 0;
     758             :   int i;
     759          30 :   for (i = 0; i < 9; i++)
     760          27 :     argv[i] = 0;
     761           3 :   int level = 0;
     762             :   int c;
     763           3 :   do {
     764           6 :     token_buffer.clear();
     765             :     for (;;) {
     766         120 :       c = get_char();
     767         120 :       if (c == EOF) {
     768           0 :         lex_error("end of input while scanning macro arguments");
     769           0 :         break;
     770             :       }
     771         120 :       if (level == 0 && (c == ',' || c == ')')) {
     772           6 :         if (token_buffer.length() > 0) {
     773           6 :           token_buffer +=  '\0';
     774           6 :           argv[argc] = strsave(token_buffer.contents());
     775             :         }
     776             :         // for 'foo()', argc = 0
     777           6 :         if (argc > 0 || c != ')' || i > 0)
     778           6 :           argc++;
     779           6 :         break;
     780             :       }
     781         114 :       token_buffer += char(c);
     782         114 :       if (c == '(')
     783           0 :         level++;
     784         114 :       else if (c == ')')
     785           0 :         level--;
     786             :     }
     787           6 :   } while (c != ')' && c != EOF);
     788           3 :   current_input = new argument_macro_input(body, argc, argv,
     789           3 :                                            current_input);
     790           3 : }
     791             : 
     792             : /* If lookup flag is non-zero the token will be looked up to see
     793             : if it is macro. If it's 1, it will looked up to see if it's a token.
     794             : */
     795             : 
     796       12378 : int get_token(int lookup_flag = 0)
     797             : {
     798             :   for (;;) {
     799       12378 :     int c = get_char();
     800       25151 :     while (c == ' ' || c == '\n')
     801       12773 :       c = get_char();
     802       12378 :     switch (c) {
     803        4072 :     case EOF:
     804             :       {
     805        4072 :         add_context("end of input");
     806             :       }
     807        4072 :       return 0;
     808          87 :     case '"':
     809             :       {
     810          87 :         int quoted = 0;
     811          87 :         token_buffer.clear();
     812             :         for (;;) {
     813         591 :           c = get_char();
     814         591 :           if (c == EOF) {
     815           0 :             lex_error("missing \"");
     816           0 :             break;
     817             :           }
     818         591 :           else if (c == '\n') {
     819           0 :             lex_error("newline before end of quoted text");
     820           0 :             break;
     821             :           }
     822         591 :           else if (c == '"') {
     823          87 :             if (!quoted)
     824          87 :               break;
     825           0 :             token_buffer[token_buffer.length() - 1] = '"';
     826           0 :             quoted = 0;
     827             :           }
     828             :           else {
     829         504 :             token_buffer += c;
     830         504 :             quoted = quoted ? 0 : c == '\\';
     831             :           }
     832             :         }
     833             :       }
     834          87 :       add_quoted_context(token_buffer);
     835          87 :       return QUOTED_TEXT;
     836         312 :     case '{':
     837             :     case '}':
     838             :     case '^':
     839             :     case '~':
     840             :     case '\t':
     841         312 :       add_context(c);
     842         312 :       return c;
     843        7907 :     default:
     844             :       {
     845        7907 :         int break_flag = 0;
     846        7907 :         int quoted = 0;
     847        7907 :         token_buffer.clear();
     848        7907 :         if (c == '\\')
     849          52 :           quoted = 1;
     850             :         else
     851        7855 :           token_buffer += c;
     852        7907 :         int done = 0;
     853       43047 :         while (!done) {
     854       35143 :           c = peek_char();
     855       35143 :           if (!quoted && lookup_flag != 0 && c == '(') {
     856          11 :             token_buffer += '\0';
     857          11 :             definition *def = macro_table.lookup(token_buffer.contents());
     858          11 :             if (def && def->is_macro && !def->is_simple) {
     859           3 :               (void)get_char(); // skip initial '('
     860           3 :               interpolate_macro_with_args(def->contents);
     861           3 :               break_flag = 1;
     862           3 :               break;
     863             :             }
     864           8 :             token_buffer.set_length(token_buffer.length() - 1);
     865             :           }
     866       35140 :           if (quoted) {
     867          52 :             quoted = 0;
     868             :             switch (c) {
     869           0 :             case EOF:
     870           0 :               lex_error("'\\' ignored at end of equation");
     871           0 :               done = 1;
     872           0 :               break;
     873           0 :             case '\n':
     874           0 :               lex_error("'\\' ignored because followed by newline");
     875           0 :               done = 1;
     876           0 :               break;
     877           0 :             case '\t':
     878           0 :               lex_error("'\\' ignored because followed by tab");
     879           0 :               done = 1;
     880           0 :               break;
     881           0 :             case '"':
     882           0 :               (void)get_char();
     883           0 :               token_buffer += '"';
     884           0 :               break;
     885          52 :             default:
     886          52 :               (void)get_char();
     887          52 :               token_buffer += '\\';
     888          52 :               token_buffer += c;
     889          52 :               break;
     890             :             }
     891             :           }
     892             :           else {
     893             :             switch (c) {
     894        7904 :             case EOF:
     895             :             case '{':
     896             :             case '}':
     897             :             case '^':
     898             :             case '~':
     899             :             case '"':
     900             :             case ' ':
     901             :             case '\t':
     902             :             case '\n':
     903        7904 :               done = 1;
     904        7904 :               break;
     905           0 :             case '\\':
     906           0 :               (void)get_char();
     907           0 :               quoted = 1;
     908           0 :               break;
     909       27184 :             default:
     910       27184 :               (void)get_char();
     911       27184 :               token_buffer += char(c);
     912       27184 :               break;
     913             :             }
     914             :           }
     915             :         }
     916        7907 :         if (break_flag || token_buffer.length() == 0)
     917           3 :           break;
     918        7904 :         if (lookup_flag != 0) {
     919        6564 :           token_buffer += '\0';
     920        6564 :           definition *def = macro_table.lookup(token_buffer.contents());
     921        6564 :           token_buffer.set_length(token_buffer.length() - 1);
     922        6564 :           if (def) {
     923        5665 :             if (def->is_macro) {
     924         128 :               current_input = new macro_input(def->contents, current_input);
     925         128 :               break;
     926             :             }
     927        5537 :             else if (lookup_flag == 1) {
     928        5537 :               add_context(token_buffer);
     929        5537 :               return def->tok;
     930             :             }
     931             :           }
     932             :         }
     933        2239 :         add_context(token_buffer);
     934        2239 :         return TEXT;
     935             :       }
     936             :     }
     937         131 :   }
     938             : }
     939             : 
     940           0 : void do_include()
     941             : {
     942           0 :   int t = get_token(2);
     943           0 :   if (t != TEXT && t != QUOTED_TEXT) {
     944           0 :     lex_error("invalid file name specified for inclusion");
     945           0 :     return;
     946             :   }
     947           0 :   token_buffer += '\0';
     948           0 :   const char *filename = token_buffer.contents();
     949           0 :   errno = 0;
     950           0 :   FILE *fp = fopen(filename, "r");
     951           0 :   if (fp == 0) {
     952           0 :     lex_error("cannot open included file '%1'", filename);
     953           0 :     return;
     954             :   }
     955           0 :   current_input = new file_input(fp, filename, current_input);
     956             : }
     957             : 
     958           0 : void ignore_definition()
     959             : {
     960           0 :   int t = get_token();
     961           0 :   if (t != TEXT) {
     962           0 :     lex_error("invalid definition");
     963           0 :     return;
     964             :   }
     965           0 :   get_delimited_text();
     966             : }
     967             : 
     968         415 : void do_definition(int is_simple)
     969             : {
     970         415 :   int t = get_token();
     971         415 :   if (t != TEXT) {
     972           0 :     lex_error("invalid definition");
     973           0 :     return;
     974             :   }
     975         415 :   token_buffer += '\0';
     976         415 :   const char *name = token_buffer.contents();
     977         415 :   definition *def = macro_table.lookup(name);
     978         415 :   if (def == 0) {
     979          96 :     def = new definition[1];
     980          48 :     macro_table.define(name, def);
     981             :   }
     982         367 :   else if (def->is_macro) {
     983         367 :     free(def->contents);
     984             :   }
     985         415 :   get_delimited_text();
     986         415 :   token_buffer += '\0';
     987         415 :   def->is_macro = 1;
     988         415 :   def->contents = strsave(token_buffer.contents());
     989         415 :   def->is_simple = is_simple;
     990             : }
     991             : 
     992         150 : void do_undef()
     993             : {
     994         150 :   int t = get_token();
     995         150 :   if (t != TEXT) {
     996           0 :     lex_error("invalid undefinition");
     997           0 :     return;
     998             :   }
     999         150 :   token_buffer += '\0';
    1000         150 :   macro_table.define(token_buffer.contents(), 0);
    1001             : }
    1002             : 
    1003           3 : void do_gsize()
    1004             : {
    1005           3 :   int t = get_token(2);
    1006           3 :   if (t != TEXT && t != QUOTED_TEXT) {
    1007           0 :     lex_error("invalid argument to gsize primitive");
    1008           0 :     return;
    1009             :   }
    1010           3 :   token_buffer += '\0';
    1011           3 :   if (!set_gsize(token_buffer.contents()))
    1012           0 :     lex_error("invalid size '%1'", token_buffer.contents());
    1013             : }
    1014             : 
    1015           0 : void do_gfont()
    1016             : {
    1017           0 :   int t = get_token(2);
    1018           0 :   if (t != TEXT && t != QUOTED_TEXT) {
    1019           0 :     lex_error("invalid argument to gfont primitive");
    1020           0 :     return;
    1021             :   }
    1022           0 :   token_buffer += '\0';
    1023           0 :   set_gifont(token_buffer.contents());
    1024             : }
    1025             : 
    1026           0 : void do_gifont()
    1027             : {
    1028           0 :   int t = get_token(2);
    1029           0 :   if (t != TEXT && t != QUOTED_TEXT) {
    1030           0 :     lex_error("invalid argument to gifont primitive");
    1031           0 :     return;
    1032             :   }
    1033           0 :   token_buffer += '\0';
    1034           0 :   set_gifont(token_buffer.contents());
    1035             : }
    1036             : 
    1037           0 : void do_grfont()
    1038             : {
    1039           0 :   int t = get_token(2);
    1040           0 :   if (t != TEXT && t != QUOTED_TEXT) {
    1041           0 :     lex_error("invalid argument to grfont primitive");
    1042           0 :     return;
    1043             :   }
    1044           0 :   token_buffer += '\0';
    1045           0 :   set_grfont(token_buffer.contents());
    1046             : }
    1047             : 
    1048           0 : void do_gbfont()
    1049             : {
    1050           0 :   int t = get_token(2);
    1051           0 :   if (t != TEXT && t != QUOTED_TEXT) {
    1052           0 :     lex_error("invalid argument to gbfont primitive");
    1053           0 :     return;
    1054             :   }
    1055           0 :   token_buffer += '\0';
    1056           0 :   set_gbfont(token_buffer.contents());
    1057             : }
    1058             : 
    1059           4 : void do_space()
    1060             : {
    1061           4 :   int t = get_token(2);
    1062           4 :   if (t != TEXT && t != QUOTED_TEXT) {
    1063           0 :     lex_error("invalid argument to space primitive");
    1064           0 :     return;
    1065             :   }
    1066           4 :   token_buffer += '\0';
    1067             :   char *ptr;
    1068           4 :   long n = strtol(token_buffer.contents(), &ptr, 10);
    1069           4 :   if (ptr == token_buffer.contents())
    1070           0 :     lex_error("invalid argument '%1' to space primitive",
    1071           0 :               token_buffer.contents());
    1072             :   else
    1073           4 :     set_space(int(n));
    1074             : }
    1075             : 
    1076         750 : void do_ifdef()
    1077             : {
    1078         750 :   int t = get_token();
    1079         750 :   if (t != TEXT) {
    1080           0 :     lex_error("invalid ifdef");
    1081           0 :     return;
    1082             :   }
    1083         750 :   token_buffer += '\0';
    1084         750 :   definition *def = macro_table.lookup(token_buffer.contents());
    1085         750 :   int result = def && def->is_macro && !def->is_simple;
    1086         750 :   get_delimited_text();
    1087         750 :   if (result) {
    1088          59 :     token_buffer += '\0';
    1089         118 :     current_input = new macro_input(token_buffer.contents(),
    1090          59 :                                     current_input);
    1091             :   }
    1092             : }
    1093             : 
    1094             : char start_delim_saved = '\0';
    1095             : char end_delim_saved = '\0';
    1096             : 
    1097        3835 : void do_delim()
    1098             : {
    1099        3835 :   int c = get_char();
    1100        7670 :   while (c == ' ' || c == '\n')
    1101        3835 :     c = get_char();
    1102             :   int d;
    1103        3835 :   if (c == EOF || (d = get_char()) == EOF)
    1104           0 :     lex_error("end of input while reading argument to 'delim'");
    1105             :   else {
    1106        3835 :     if (c == 'o' && d == 'f' && peek_char() == 'f') {
    1107        1915 :       (void)get_char();
    1108        1915 :       start_delim_saved = start_delim;
    1109        1915 :       end_delim_saved = end_delim;
    1110        1915 :       start_delim = end_delim = '\0';
    1111             :     }
    1112        1920 :     else if (c == 'o' && d == 'n') {
    1113        1909 :       start_delim = start_delim_saved;
    1114        1909 :       end_delim = end_delim_saved;
    1115             :     }
    1116             :     else {
    1117          11 :       start_delim = c;
    1118          11 :       end_delim = d;
    1119             :     }
    1120             :   }
    1121        3835 : }
    1122             : 
    1123           3 : void do_chartype()
    1124             : {
    1125           3 :   int t = get_token(2);
    1126           3 :   if (t != TEXT && t != QUOTED_TEXT) {
    1127           0 :     lex_error("invalid type argument to chartype primitive");
    1128           0 :     return;
    1129             :   }
    1130           3 :   token_buffer += '\0';
    1131           3 :   string type = token_buffer;
    1132           3 :   t = get_token();
    1133           3 :   if (t != TEXT && t != QUOTED_TEXT) {
    1134           0 :     lex_error("invalid character sequence argument to chartype"
    1135             :               " primitive");
    1136           0 :     return;
    1137             :   }
    1138           3 :   token_buffer += '\0';
    1139           3 :   set_char_type(type.contents(), strsave(token_buffer.contents()));
    1140             : }
    1141             : 
    1142          22 : void do_set()
    1143             : {
    1144          22 :   int t = get_token(2);
    1145          22 :   if (t != TEXT && t != QUOTED_TEXT) {
    1146           0 :     lex_error("invalid parameter name argument to 'set' primitive");
    1147           0 :     return;
    1148             :   }
    1149          22 :   token_buffer += '\0';
    1150          22 :   string param = token_buffer;
    1151          22 :   t = get_token();
    1152          22 :   if (t != TEXT && t != QUOTED_TEXT) {
    1153           0 :     lex_error("invalid parameter value argument to 'set' primitive");
    1154           0 :     return;
    1155             :   }
    1156          22 :   token_buffer += '\0';
    1157             :   int n;
    1158          22 :   if (sscanf(&token_buffer[0], "%d", &n) != 1) {
    1159           0 :     lex_error("invalid number '%1'", token_buffer.contents());
    1160           0 :     return;
    1161             :   }
    1162          22 :   set_param(param.contents(), n);
    1163             : }
    1164             : 
    1165           1 : void do_reset()
    1166             : {
    1167           1 :   int t = get_token(2);
    1168           1 :   if (t != TEXT && t != QUOTED_TEXT) {
    1169           0 :     lex_error("invalid parameter name argument to 'reset' primitive");
    1170           0 :     return;
    1171             :   }
    1172           1 :   token_buffer += '\0';
    1173           2 :   string param = token_buffer;
    1174           1 :   reset_param(param.contents());
    1175             : }
    1176             : 
    1177       10874 : int yylex()
    1178             : {
    1179             :   for (;;) {
    1180       10874 :     int tk = get_token(1);
    1181       10874 :     switch(tk) {
    1182         150 :     case UNDEF:
    1183         150 :       do_undef();
    1184         150 :       break;
    1185         367 :     case SDEFINE:
    1186         367 :       do_definition(1);
    1187         367 :       break;
    1188          48 :     case DEFINE:
    1189          48 :       do_definition(0);
    1190          48 :       break;
    1191           0 :     case TDEFINE:
    1192           0 :       if (!get_param("nroff"))
    1193           0 :         do_definition(0);
    1194             :       else
    1195           0 :         ignore_definition();
    1196           0 :       break;
    1197           0 :     case NDEFINE:
    1198           0 :       if (get_param("nroff"))
    1199           0 :         do_definition(0);
    1200             :       else
    1201           0 :         ignore_definition();
    1202           0 :       break;
    1203           3 :     case GSIZE:
    1204           3 :       do_gsize();
    1205           3 :       break;
    1206           0 :     case GFONT:
    1207           0 :       do_gfont();
    1208           0 :       break;
    1209           0 :     case GIFONT:
    1210           0 :       do_gifont();
    1211           0 :       break;
    1212           0 :     case GRFONT:
    1213           0 :       do_grfont();
    1214           0 :       break;
    1215           0 :     case GBFONT:
    1216           0 :       do_gbfont();
    1217           0 :       break;
    1218           4 :     case SPACE:
    1219           4 :       do_space();
    1220           4 :       break;
    1221           0 :     case INCLUDE:
    1222           0 :       do_include();
    1223           0 :       break;
    1224         750 :     case IFDEF:
    1225         750 :       do_ifdef();
    1226         750 :       break;
    1227        3835 :     case DELIM:
    1228        3835 :       do_delim();
    1229        3835 :       break;
    1230           3 :     case CHARTYPE:
    1231           3 :       do_chartype();
    1232           3 :       break;
    1233          22 :     case SET:
    1234          22 :       do_set();
    1235          22 :       break;
    1236           1 :     case RESET:
    1237           1 :       do_reset();
    1238           1 :       break;
    1239         953 :     case QUOTED_TEXT:
    1240             :     case TEXT:
    1241         953 :       token_buffer += '\0';
    1242         953 :       yylval.str = strsave(token_buffer.contents());
    1243             :       // fall through
    1244        5691 :     default:
    1245        5691 :       return tk;
    1246             :     }
    1247        5183 :   }
    1248             : }
    1249             : 
    1250           0 : void lex_error(const char *message,
    1251             :                const errarg &arg1,
    1252             :                const errarg &arg2,
    1253             :                const errarg &arg3)
    1254             : {
    1255             :   char *filename;
    1256             :   int lineno;
    1257           0 :   if (!get_location(&filename, &lineno))
    1258           0 :     error(message, arg1, arg2, arg3);
    1259             :   else
    1260           0 :     error_with_file_and_line(filename, lineno, message, arg1, arg2,
    1261             :                              arg3);
    1262           0 : }
    1263             : 
    1264           0 : void yyerror(const char *s)
    1265             : {
    1266             :   char *filename;
    1267             :   int lineno;
    1268           0 :   if (!get_location(&filename, &lineno))
    1269           0 :     error(s);
    1270             :   else
    1271           0 :     error_with_file_and_line(filename, lineno, s);
    1272           0 :   show_context();
    1273           0 : }
    1274             : 
    1275             : // Local Variables:
    1276             : // fill-column: 72
    1277             : // mode: C++
    1278             : // End:
    1279             : // vim: set cindent noexpandtab shiftwidth=2 textwidth=72:

Generated by: LCOV version 1.14