/* Operator.C
 *
 * Some tokens like the curly braces have many meanings in many places.
 * However, for most tokens there is a simple one-to-one mapping between
 * the token and some operation to be performed. There are a number of
 * common types of Operators, and then some special purpose ones.
 *
 * Copyright 1992 Jonathan Monsarrat. Permission given to freely distribute,
 * edit and use as long as this copyright statement remains intact.
 *
 */

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <search.h>
#include <time.h>
#include "Operator.h"
#include "Global.h"
#include "Counter.h"
#include "Document.h"
#include "Font.h"
#include "Justify.h"

Operator::Operator(char *tokentext, int page_specific, int stealth,
		   int paramtype, int subtype, float value, char *replacestr,
		   void (*handle)(int, int, float, char *))
{
   _tokentext = tokentext;
   _page_specific = page_specific;
   _stealth = stealth;
   _paramtype = paramtype;
   _subtype = subtype;
   _value = value;
   _replacestr = replacestr;
   _handle = handle;

   _valid = TRUE;
}

Operator::Operator(const int array_ender)
{
   _valid = array_ender;   // Invalid operator marks the end of the array.
}

/* A list of all the token versus their Operators. Some tokens like "\quote"
 * have more than one Operator because they involve more than one simple
 * parameter change, like a change to both leftmargin and textwidth.
 * DO NOT USE M-x sort-lines TO SORT THIS! Some of the operators have multiple
 * lines, which must be kept preserved in a certain order.
 */
static Operator operators[] =
{
   Operator("",                  0,0,0,                         0,                         0.0,  "",                  Operator::new_paragraph),    // handled
   Operator("\\#",               1,0,0,                         0,                         0.0,  "#",                 Operator::replace),          // handled
   Operator("\\$",               1,0,0,                         0,                         0.0,  "$",                 Operator::replace),          // handled
   Operator("\\%",               1,0,Environment::PFont,        Font::FunnyPrint,          0.0,  "(%)",               Stack::set),                 // handled
   Operator("\\&",               1,0,0,                         0,                         0.0,  "&",                 Operator::replace),          // handled
   Operator("\\Huge",            0,0,Environment::PFont,        Font::Huge,                0.0,  "",                  Stack::set),                 // handled
   Operator("\\LARGE",           0,0,Environment::PFont,        Font::LARGE,               0.0,  "",                  Stack::set),                 // handled
   Operator("\\Large",           0,0,Environment::PFont,        Font::Large,               0.0,  "",                  Stack::set),                 // handled
   Operator("\\STEALTH",         0,0,Environment::PDocument,    Document::Stealth,         2.0,  "",                  Stack::set),                 // handled
   Operator("\\\\",              1,0,Environment::PDocument,    Document::NewLine,         0.0,  "",                  Stack::set),                 // handled
   Operator("\\\\*",             1,0,Environment::PDocument,    Document::NewLine,         0.0,  "",                  Stack::set),                 // handled
   Operator("\\_",               1,0,Environment::PFont,        Font::FunnyPrint,          0.0,  "(_)",               Stack::set),                 // handled
   Operator("\\addtolength",     0,0,0,                         0,                         0.0,  "",                  Operator::addtolength),      // handled
   Operator("\\backslash",       1,0,Environment::PFont,        Font::FunnyPrint,          0.0,  "(\\\\)",            Stack::set),                 // handled
   Operator("\\begin",           0,0,0,                         0,                         0.0,  "",                  Document::begin),            // handled
   Operator("\\bf",              0,0,Environment::PFont,        Font::Bold,                0.0,  "",                  Stack::set),                 // handled
   Operator("\\bigskip",         1,0,0,                         0,                         0.0,  "\\bigskipamount",   Operator::do_vspace), 
   Operator("\\center",          0,0,Environment::PJustify,     Justify::Center,           0.0,  "",                  Stack::set),                 // handled
   Operator("\\chapter",         0,0,0,                         0,                         0.0,  "",                  Operator::clearpage),        // handled
   Operator("\\chapter",         1,0,Environment::PDocument,    Document::JustDidSection,  1.0,  "",                  Stack::set),                 // handled
   Operator("\\chapter",         1,0,0,                         0,                         0.0,  "",                  Stack::push),                // handled
   Operator("\\chapter",         1,0,Environment::PLength,      Length::Parameter,         0.0,  "\\parindent",       Stack::set),                 // handled
   Operator("\\chapter",         1,0,Environment::PFont,        Font::Bold,                0.0,  "",                  Stack::set),                 // handled
   Operator("\\chapter",         1,0,Environment::PFont,        Font::huge,                0.0,  "",                  Stack::set),                 // handled
   Operator("\\chapter",         1,0,Environment::PCounter,     Counter::Chapter,          1.0,  "",                  Stack::set),                 // handled
   Operator("\\chapter*",        0,0,0,                         0,                         0.0,  "",                  Operator::clearpage),        // handled
   Operator("\\chapter*",        1,0,Environment::PDocument,    Document::JustDidSection,  1.0,  "",                  Stack::set),                 // handled
   Operator("\\chapter*",        1,0,0,                         0,                         0.0,  "",                  Stack::push),                // handled
   Operator("\\chapter*",        1,0,Environment::PLength,      Length::Parameter,         0.0,  "\\parindent",       Stack::set),                 // handled
   Operator("\\chapter*",        1,0,Environment::PFont,        Font::Bold,                0.0,  "",                  Stack::set),                 // handled
   Operator("\\chapter*",        1,0,Environment::PFont,        Font::huge,                0.0,  "",                  Stack::set),                 // handled
   Operator("\\chapter*",        1,0,Environment::PCounter,     Counter::Chapter,          0.0,  "",                  Stack::set),                 // handled
   Operator("\\clearpage",       0,0,0,                         0,                         0.0,  "",                  Operator::clearpage),        // handled
   Operator("\\description",     1,0,Environment::PCounter,     Counter::Description,      0.0,  "",                  Stack::set),                 // handled
   Operator("\\description",     1,0,Environment::PLength,      Length::Parameter,        22.5,  "\\oddsidemargin",   Stack::relative_set),        // handled
   Operator("\\description",     1,0,Environment::PLength,      Length::Parameter,       -22.5,  "\\textwidth",       Stack::relative_set),        // handled
   Operator("\\description",     1,0,Environment::PLength,      Length::Parameter,         0.0,  "\\parindent",       Stack::set),                 // handled
   Operator("\\description",     1,0,Environment::PLength,      Length::Parameter,         1.14, "\\baselinestretch", Stack::set),                 // handled
   Operator("\\document",        0,0,Environment::PDocument,    Document::DocumentStart,   1.0,  "",                  Stack::set),                 // handled
   Operator("\\documentstyle",   0,0,0,                         0,                         0.0,  "",                  Document::documentstyle),    // handled
   Operator("\\em",              0,0,Environment::PFont,        Font::Italic,              0.0,  "",                  Stack::set),                 // handled
   Operator("\\end",             0,0,0,                         0,                         0.0,  "",                  Document::end),              // handled
   Operator("\\enumerate",       1,0,Environment::PCounter,     Counter::Enum,             0.0,  "",                  Stack::set),                 // handled
   Operator("\\enumerate",       1,0,Environment::PLength,      Length::Parameter,        22.5,  "\\oddsidemargin",   Stack::relative_set),        // handled
   Operator("\\enumerate",       1,0,Environment::PLength,      Length::Parameter,       -22.5,  "\\textwidth",       Stack::relative_set),        // handled
   Operator("\\enumerate",       1,0,Environment::PLength,      Length::Parameter,         1.14, "\\baselinestretch", Stack::set),                 // handled
   Operator("\\enumerate",       1,0,Environment::PLength,      Length::Parameter,         0.0,  "\\parindent",       Stack::set),                 // handled
   Operator("\\flushleft",       0,0,Environment::PJustify,     Justify::FlushLeft,        0.0,  "",                  Stack::set),                 // handled
   Operator("\\flushright",      0,0,Environment::PJustify,     Justify::FlushRight,       0.0,  "",                  Stack::set),                 // handled
   Operator("\\footnotesize",    0,0,Environment::PFont,        Font::Footnotesize,        0.0,  "",                  Stack::set),                 // handled
   Operator("\\hspace",          1,0,0,                         0,                         0.0,  "",                  Operator::hspace),           // handled
   Operator("\\hspace*",         1,0,0,                         0,                         0.0,  "",                  Operator::hspace),           // handled
   Operator("\\huge",            0,0,Environment::PFont,        Font::huge,                0.0,  "",                  Stack::set),                 // handled
   Operator("\\hyphenation",     0,0,0,                         0,                         0.0,  "",                  Operator::skip),             // skipped
   Operator("\\ignore",          1,1,Environment::PDocument,    Document::Ignore,          1.0,  "",                  Stack::set),                 // handled
   Operator("\\include",         0,0,0,                         0,                         0.0,  "",                  Operator::include_file),     // handled
   Operator("\\includeps",       0,1,0,                         0,                         0.0,  "",                  Operator::includeps),        // handled
   Operator("\\it",              0,0,Environment::PFont,        Font::Italic,              0.0,  "",                  Stack::set),                 // handled
   Operator("\\item",            1,0,Environment::PCounter,     Counter::Item,             0.0,  "",                  Stack::set),                 // handled
   Operator("\\itemize",         1,0,Environment::PCounter,     Counter::Itemize,          0.0,  "",                  Stack::set),                 // handled
   Operator("\\itemize",         1,0,Environment::PLength,      Length::Parameter,        22.5,  "\\oddsidemargin",   Stack::relative_set),        // handled
   Operator("\\itemize",         1,0,Environment::PLength,      Length::Parameter,       -22.5,  "\\textwidth",       Stack::relative_set),        // handled
   Operator("\\itemize",         1,0,Environment::PLength,      Length::Parameter,         1.0,  "\\baselinestretch", Stack::set),                 // handled
   Operator("\\itemize",         1,0,Environment::PLength,      Length::Parameter,         0.0,  "\\parindent",       Stack::set),                 // handled
   Operator("\\label",           1,0,0,                         0,                         0.0,  "",                  Operator::label),            // handled
   Operator("\\large",           0,0,Environment::PFont,        Font::large,               0.0,  "",                  Stack::set),                 // handled
   Operator("\\ldots",           1,0,0,                         0,                         0.0,  "...",               Operator::replace),          // handled
   Operator("\\medskip",         1,0,0,                         0,                         0.0,  "\\medskipamount",   Operator::do_vspace),
   Operator("\\newlength",       0,0,0,                         0,                         0.0,  "",                  Operator::newlength),        // handled
   Operator("\\newline",         1,0,Environment::PDocument,    Document::NewLine,         0.0,  "",                  Stack::set),                 // handled
   Operator("\\normalsize",      0,0,Environment::PFont,        Font::Normalsize,          0.0,  "",                  Stack::set),                 // handled
   Operator("\\par",             1,0,Environment::PDocument,    Document::NewLine,         0.0,  "",                  Stack::set),                 // handled
   Operator("\\paragraph",       1,0,Environment::PDocument,    Document::JustDidSection,  1.0,  "",                  Stack::set),
   Operator("\\paragraph",       1,0,0,                         0,                         0.0,  "",                  Stack::push),                // handled
   Operator("\\paragraph",       1,0,Environment::PLength,      Length::Parameter,         0.0,  "\\parindent",       Stack::set),                 // handled
   Operator("\\paragraph",       1,0,Environment::PFont,        Font::Bold,                0.0,  "",                  Stack::set),                 // handled
   Operator("\\paragraph",       1,0,Environment::PFont,        Font::Normalsize,          0.0,  "",                  Stack::set),                 // handled
   Operator("\\paragraph",       1,0,Environment::PCounter,     Counter::Paragraph,        1.0,  "",                  Stack::set),                 // handled
   Operator("\\paragraph*",      1,0,Environment::PDocument,    Document::JustDidSection,  1.0,  "",                  Stack::set),
   Operator("\\paragraph*",      1,0,0,                         0,                         0.0,  "",                  Stack::push),                // handled
   Operator("\\paragraph*",      1,0,Environment::PLength,      Length::Parameter,         0.0,  "\\parindent",       Stack::set),                 // handled
   Operator("\\paragraph*",      1,0,Environment::PFont,        Font::Bold,                0.0,  "",                  Stack::set),                 // handled
   Operator("\\paragraph*",      1,0,Environment::PFont,        Font::Normalsize,          0.0,  "",                  Stack::set),                 // handled
   Operator("\\paragraph*",      1,0,Environment::PCounter,     Counter::Paragraph,        0.0,  "",                  Stack::set),                 // handled
   Operator("\\part",            0,0,0,                         0,                         0.0,  "",                  Operator::clearpage),
   Operator("\\part",            1,0,Environment::PDocument,    Document::JustDidSection,  1.0,  "",                  Stack::set),
   Operator("\\part",            1,0,0,                         0,                         0.0,  "",                  Stack::push),
   Operator("\\part",            1,0,Environment::PLength,      Length::Parameter,         0.0,  "\\parindent",       Stack::set),
   Operator("\\part",            1,0,Environment::PFont,        Font::Bold,                0.0,  "",                  Stack::set),
   Operator("\\part",            1,0,Environment::PFont,        Font::Huge,                0.0,  "",                  Stack::set),
   Operator("\\part",            1,0,Environment::PCounter,     Counter::Part,             1.0,  "",                  Stack::set),
   Operator("\\part*",           0,0,0,                         0,                         0.0,  "",                  Operator::clearpage),
   Operator("\\part*",           1,0,Environment::PDocument,    Document::JustDidSection,  1.0,  "",                  Stack::set),
   Operator("\\part*",           1,0,0,                         0,                         0.0,  "",                  Stack::push),
   Operator("\\part*",           1,0,Environment::PLength,      Length::Parameter,         0.0,  "\\parindent",       Stack::set),
   Operator("\\part*",           1,0,Environment::PFont,        Font::Bold,                0.0,  "",                  Stack::set),
   Operator("\\part*",           1,0,Environment::PFont,        Font::Huge,                0.0,  "",                  Stack::set),
   Operator("\\part*",           1,0,Environment::PCounter,     Counter::Part,             0.0,  "",                  Stack::set),
   Operator("\\postscript",      1,1,Environment::PDocument,    Document::PostScript,      1.0,  "",                  Stack::set),                 // handled
   Operator("\\pscmd",           0,1,0,                         0,                         0.0,  "",                  Operator::pscmd),            // handled
   Operator("\\pspage",          0,1,0,                         0,                         0.0,  "",                  Operator::pspage),           // handled
   Operator("\\quotation",       1,0,Environment::PDocument,    Document::NewLine,         0.0,  "",                  Stack::set),                 // handled
   Operator("\\quotation",       1,0,Environment::PLength,      Length::Parameter,         0.0,  "\\parindent",       Stack::set),                 // handled
   Operator("\\quotation",       1,0,Environment::PLength,      Length::Parameter,         1.0,  "\\baselinestretch", Stack::set),                 // handled
   Operator("\\quotation",       1,0,Environment::PLength,      Length::Parameter,         0.0,  "\\parskip",         Stack::set),                 // handled
   Operator("\\quotation",       1,0,Environment::PLength,      Length::Parameter,        72.0,  "\\oddsidemargin",   Stack::relative_set),        // handled
   Operator("\\quotation",       1,0,Environment::PLength,      Length::Parameter,       -72.0,  "\\textwidth",       Stack::relative_set),        // handled
   Operator("\\quote",           1,0,Environment::PDocument,    Document::NewLine,         0.0,  "",                  Stack::set),                 // handled
   Operator("\\quote",           1,0,Environment::PLength,      Length::Parameter,         0.0,  "\\parindent",       Stack::set),                 // handled
   Operator("\\quote",           1,0,Environment::PLength,      Length::Parameter,         1.0,  "\\baselinestretch", Stack::set),                 // handled
   Operator("\\quote",           1,0,Environment::PLength,      Length::Parameter,         0.0,  "\\parskip",         Stack::set),                 // handled
   Operator("\\quote",           1,0,Environment::PLength,      Length::Parameter,        72.0,  "\\oddsidemargin",   Stack::relative_set),        // handled
   Operator("\\quote",           1,0,Environment::PLength,      Length::Parameter,       -72.0,  "\\textwidth",       Stack::relative_set),        // handled
   Operator("\\raggedleft",      0,0,Environment::PJustify,     Justify::FlushRight,       0.0,  "",                  Stack::set),                 // handled
   Operator("\\raggedright",     0,0,Environment::PJustify,     Justify::FlushLeft,        0.0,  "",                  Stack::set),                 // handled
   Operator("\\ref",             1,0,0,                         0,                         0.0,  "",                  Operator::ref),              // handled
   Operator("\\rm",              0,0,Environment::PFont,        Font::Roman,               0.0,  "",                  Stack::set),                 // handled
   Operator("\\sc",              0,0,Environment::PFont,        Font::SmallCaps,           0.0,  "",                  Stack::set),                 // handled
   Operator("\\scriptsize",      0,0,Environment::PFont,        Font::Scriptsize,          0.0,  "",                  Stack::set),                 // handled
   Operator("\\section",         1,0,Environment::PDocument,    Document::JustDidSection,  1.0,  "",                  Stack::set),                 // handled
   Operator("\\section",         1,0,0,                         0,                         0.0,  "",                  Stack::push),                // handled
   Operator("\\section",         1,0,Environment::PLength,      Length::Parameter,         0.0,  "\\parindent",       Stack::set),                 // handled
   Operator("\\section",         1,0,Environment::PFont,        Font::Bold,                0.0,  "",                  Stack::set),                 // handled
   Operator("\\section",         1,0,Environment::PFont,        Font::Large,               0.0,  "",                  Stack::set),                 // handled
   Operator("\\section",         1,0,Environment::PCounter,     Counter::Section,          1.0,  "",                  Stack::set),                 // handled
   Operator("\\section*",        1,0,Environment::PDocument,    Document::JustDidSection,  1.0,  "",                  Stack::set),                 // handled
   Operator("\\section*",        1,0,0,                         0,                         0.0,  "",                  Stack::push),                // handled
   Operator("\\section*",        1,0,Environment::PLength,      Length::Parameter,         0.0,  "\\parindent",       Stack::set),                 // handled
   Operator("\\section*",        1,0,Environment::PFont,        Font::Bold,                0.0,  "",                  Stack::set),                 // handled
   Operator("\\section*",        1,0,Environment::PFont,        Font::Large,               0.0,  "",                  Stack::set),                 // handled
   Operator("\\section*",        1,0,Environment::PCounter,     Counter::Section,          0.0,  "",                  Stack::set),                 // handled
   Operator("\\setlength",       0,0,0,                         0,                         0.0,  "",                  Operator::setlength),        // handled
   Operator("\\sf",              0,0,Environment::PFont,        Font::SansSerif,           0.0,  "",                  Stack::set),                 // handled
   Operator("\\sl",              0,0,Environment::PFont,        Font::Slant,               0.0,  "",                  Stack::set),                 // handled
   Operator("\\small",           0,0,Environment::PFont,        Font::Small,               0.0,  "",                  Stack::set),                 // handled
   Operator("\\smallskip",       1,0,0,                         0,                         0.0,  "\\smallskipamount", Operator::do_vspace),
   Operator("\\stealth",         0,0,Environment::PDocument,    Document::Stealth,         1.0,  "",                  Stack::set),                 // handled
   Operator("\\subparagraph",    1,0,Environment::PDocument,    Document::JustDidSection,  1.0,  "",                  Stack::set),                 // handled
   Operator("\\subparagraph",    1,0,0,                         0,                         0.0,  "",                  Stack::push),                // handled
   Operator("\\subparagraph",    1,0,Environment::PLength,      Length::Parameter,         0.0,  "\\parindent",       Stack::set),                 // handled
   Operator("\\subparagraph",    1,0,Environment::PFont,        Font::Bold,                0.0,  "",                  Stack::set),                 // handled
   Operator("\\subparagraph",    1,0,Environment::PFont,        Font::Normalsize,          0.0,  "",                  Stack::set),                 // handled
   Operator("\\subparagraph",    1,0,Environment::PCounter,     Counter::Subparagraph,     1.0,  "",                  Stack::set),                 // handled
   Operator("\\subparagraph*",   1,0,Environment::PDocument,    Document::JustDidSection,  1.0,  "",                  Stack::set),                 // handled
   Operator("\\subparagraph*",   1,0,0,                         0,                         0.0,  "",                  Stack::push),                // handled
   Operator("\\subparagraph*",   1,0,Environment::PLength,      Length::Parameter,         0.0,  "\\parindent",       Stack::set),                 // handled
   Operator("\\subparagraph*",   1,0,Environment::PFont,        Font::Bold,                0.0,  "",                  Stack::set),                 // handled
   Operator("\\subparagraph*",   1,0,Environment::PFont,        Font::Normalsize,          0.0,  "",                  Stack::set),                 // handled
   Operator("\\subparagraph*",   1,0,Environment::PCounter,     Counter::Subparagraph,     0.0,  "",                  Stack::set),                 // handled
   Operator("\\subsection",      1,0,Environment::PDocument,    Document::JustDidSection,  1.0,  "",                  Stack::set),                 // handled
   Operator("\\subsection",      1,0,0,                         0,                         0.0,  "",                  Stack::push),                // handled
   Operator("\\subsection",      1,0,Environment::PLength,      Length::Parameter,         0.0,  "\\parindent",       Stack::set),                 // handled
   Operator("\\subsection",      1,0,Environment::PFont,        Font::Bold,                0.0,  "",                  Stack::set),                 // handled
   Operator("\\subsection",      1,0,Environment::PFont,        Font::large,               0.0,  "",                  Stack::set),                 // handled
   Operator("\\subsection",      1,0,Environment::PCounter,     Counter::Subsection,       1.0,  "",                  Stack::set),                 // handled
   Operator("\\subsection*",     1,0,Environment::PDocument,    Document::JustDidSection,  1.0,  "",                  Stack::set),                 // handled
   Operator("\\subsection*",     1,0,0,                         0,                         0.0,  "",                  Stack::push),                // handled
   Operator("\\subsection*",     1,0,Environment::PLength,      Length::Parameter,         0.0,  "\\parindent",       Stack::set),                 // handled
   Operator("\\subsection*",     1,0,Environment::PFont,        Font::Bold,                0.0,  "",                  Stack::set),                 // handled
   Operator("\\subsection*",     1,0,Environment::PFont,        Font::large,               0.0,  "",                  Stack::set),                 // handled
   Operator("\\subsection*",     1,0,Environment::PCounter,     Counter::Subsection,       0.0,  "",                  Stack::set),                 // handled
   Operator("\\subsubsection",   1,0,Environment::PDocument,    Document::JustDidSection,  1.0,  "",                  Stack::set),                 // handled
   Operator("\\subsubsection",   1,0,0,                         0,                         0.0,  "",                  Stack::push),                // handled
   Operator("\\subsubsection",   1,0,Environment::PLength,      Length::Parameter,         0.0,  "\\parindent",       Stack::set),                 // handled
   Operator("\\subsubsection",   1,0,Environment::PFont,        Font::Bold,                0.0,  "",                  Stack::set),                 // handled
   Operator("\\subsubsection",   1,0,Environment::PFont,        Font::Normalsize,          0.0,  "",                  Stack::set),                 // handled
   Operator("\\subsubsection",   1,0,Environment::PCounter,     Counter::Subsubsection,    1.0,  "",                  Stack::set),                 // handled
   Operator("\\subsubsection*",  1,0,Environment::PDocument,    Document::JustDidSection,  1.0,  "",                  Stack::set),                 // handled
   Operator("\\subsubsection*",  1,0,0,                         0,                         0.0,  "",                  Stack::push),                // handled
   Operator("\\subsubsection*",  1,0,Environment::PLength,      Length::Parameter,         0.0,  "\\parindent",       Stack::set),                 // handled
   Operator("\\subsubsection*",  1,0,Environment::PFont,        Font::Bold,                0.0,  "",                  Stack::set),                 // handled
   Operator("\\subsubsection*",  1,0,Environment::PFont,        Font::Normalsize,          0.0,  "",                  Stack::set),                 // handled
   Operator("\\subsubsection*",  1,0,Environment::PCounter,     Counter::Subsubsection,    0.0,  "",                  Stack::set),                 // handled
   Operator("\\tiny",            0,0,Environment::PFont,        Font::Tiny,                0.0,  "",                  Stack::set),                 // handled
   Operator("\\today",           1,0,0,                         0,                         0.0,  "",                  Operator::today),            // handled
   Operator("\\tt",              0,0,Environment::PFont,        Font::Typewriter,          0.0,  "",                  Stack::set),                 // handled
   Operator("\\verse",           1,0,Environment::PDocument,    Document::NewLine,         0.0,  "",                  Stack::set),                 // handled
   Operator("\\verse",           1,0,Environment::PLength,      Length::Parameter,        45.0,  "\\oddsidemargin",   Stack::relative_set),        // handled
   Operator("\\verse",           1,0,Environment::PLength,      Length::Parameter,       -22.5,  "\\parindent",       Stack::set),                 // handled
   Operator("\\verse",           1,0,Environment::PLength,      Length::Parameter,         1.0,  "\\baselinestretch", Stack::set),                 // handled
   Operator("\\verse",           1,0,Environment::PLength,      Length::Parameter,        11.0,  "\\parskip",         Stack::set),                 // handled
   Operator("\\vspace",          1,0,0,                         0,                         0.0,  "",                  Operator::vspace),           // handled
   Operator("\\vspace*",         1,0,0,                         0,                         0.0,  "",                  Operator::vspace),           // handled
   Operator("\\{",               1,0,Environment::PFont,        Font::FunnyPrint,          0.0,  "({)",               Stack::set),                 // handled
   Operator("\\}",               1,0,Environment::PFont,        Font::FunnyPrint,          0.0,  "(})",               Stack::set),                 // handled
   Operator("{",                 0,0,0,                         0,                         0.0,  "",                  Stack::push),                // handled
   Operator("}",                 0,0,0,                         Document::CloseBrace,      0.0,  "",                  Stack::pop),                 // handled
   0
};

static int operator_size = 0; // Size of the operators array

int Operator::compare(const void *op1, const void *op2)
{
   Operator *oper1;
   Operator *oper2;
   oper1 = (Operator *) op1;
   oper2 = (Operator *) op2;
   return strcmp(oper1->_tokentext, oper2->_tokentext);
}

/* Do a binary search through the alphabetically sorted array of operators
 * for the first operator (there may be many) that matches this token name
 * Return a pointer to the first operator, or NULL if no match found.
 */
Operator* Operator::get_operator(char *tokenstr)
{
   if(!operator_size)       // Has the operator_size variable been initialized?
      while(operators[operator_size++].isvalid())
	 ;
   Operator key(tokenstr, 0, 0, 0, 0, 0.0, "", NULL);
   Operator *op = (Operator *)
      bsearch((char *)&key, (char *) operators,
	      operator_size, sizeof(Operator), Operator::compare);
   if(op != NULL && !op->match("")) {    // Found a non-null match?
      while((--op)->match(tokenstr))     // Backpeddle until first match found.
	 ;
      op++;
   }
   return op;
}

/* Execute the given operator, which is a pointer into the array of operators
 * "operators". Since there could be more than one operator for a token name,
 * the pointer is incremented and executed until the token name doesn't match.
 */
void Operator::execute()
{
   // Skip the operation if we're in a comment, unless this is a 
   // stealth environment or the command is "\stealth" or "\begin".

   if(Stack::get(Environment::PDocument, Document::Comment, "")) { // Comment?
      if(!match("\\stealth") && !match("\\STEALTH") && !match("\\begin")
	 && !Stack::get(Environment::PDocument, Document::Stealth, ""))
	 return;
   }
   else              // Don't put stealth comments outside of a comment line,
      if(is_stealth()) // because then normal LaTeX cannot process this file.
	 Global::files->fatal_error(
 "Cannot execute stealth command outside of stealth environment in comment line.");

   // We are prepared to handle the Operator. But first, check to see if the
   // page description needs to be defined or if the current page needs to be
   // started with StartPage
   if(_page_specific)
      Global::files->force_start_page();

   _handle(_paramtype, _subtype, _value, _replacestr); // Execute the operator
}

/* Returns true if the given token string matches the Operator's token string
 */
int Operator::match(char *tokenstr)
{
   return !strcmp(_tokentext,tokenstr);
}

void Operator::addtolength(int paramtype, int subtype, float value,
                         char *replacestr)
{
   Length *length;
   length = Global::stack->get_length();
   length->addtolength(paramtype, subtype, value, replacestr);
}

void Operator::clearpage(int, int, float, char *)
{
   if(Stack::get(Environment::PDocument, Document::StartPage, "")) {
      Global::files->outfile << endl;
      Global::files->outfile << "ENDPAGE" << endl;
      // We don't start a new page unless we have to! This is just a marker
      // that a new page has not been started.
      Stack::set(Environment::PDocument, Document::StartPage, 0.0, "");
   }
}

void Operator::document(int, int, float, char *)
{
   Global::files->outfile << "in document()" << endl;
}

void Operator::do_vspace(int, int, float height, char *length_param)
{
   if(length_param[0])
      height = Global::stack->get(Environment::PLength,
				  Length::Parameter, length_param);

   // Suppress any new paragraphs before text
   Global::files->blankline_area = 1; 
   Global::files->newline_in_this_blankline_area = 1; 
   Global::files->vspace_in_this_blankline_area += height; 
   Global::files->got_whitespace();
}

void Operator::hspace(int, int, float, char *)
{
   Token openbrace;
   if(!openbrace.match("{"))
      Global::files->fatal_error("Expecting '{' after \\vspace");
   
   float width = Stack::get(Environment::PLength, Length::Parse_Length, "");
   Global::files->outfile << endl;
   Global::files->outfile << width << " HSpace" << endl;
}

/* Handle the LaTeX command \include, which includes a file at the current
 * place in the filestream.
 */
void Operator::include_file(int, int, float, char *)
{
   Token openbrace;
   if(!openbrace.match("{"))
      Global::files->fatal_error("Expecting '{' after \\include statement");
   
   Token filename;
   if(filename.match("}"))
      Global::files->warning(
            "Expecting filename before closing '}' in \\include");

   Token closebrace;
   if(!closebrace.match("}"))
      Global::files->fatal_error(
         "More than one word before closing '}' in \\include");

   Global::files->include_file(filename.get_text());
}

/* Handle the LameTeX stealth command \includeps, which includes a
 * postscript file at the current place in the filestream.
 */
void Operator::includeps(int, int, float, char *)
{
   Token openbrace;
   if(!openbrace.match("{"))
      Global::files->fatal_error("Expecting '{' after \\includeps statement");
   
   Token filename;
   if(filename.match("}"))
      Global::files->warning(
            "Expecting filename before closing '}' in \\includeps");

   Token closebrace;
   if(!closebrace.match("}"))
      Global::files->fatal_error(
         "More than one word before closing '}' in \\includeps");

   Global::files->include_file_ps(filename.get_text(), FALSE);
}


void Operator::label(int, int, float, char *)
{
   Token openbrace;
   if(!openbrace.match("{"))
      Global::files->fatal_error("Expecting '{' after \\label statement");
   
   Token labelname;
   if(labelname.match("}"))
      Global::files->warning(
            "Expecting label name before closing '}' in \\label");

   Token closebrace;
   if(!closebrace.match("}"))
      Global::files->fatal_error(
         "More than token before closing '}' in \\label. Sorry, you must rename your label.");

   Global::labels->add_label(labelname.get_text());
}

void Operator::newlength(int paramtype, int subtype, float value,
                         char *replacestr)
{
   Length *length;
   length = Global::stack->get_length();
   length->newlength(paramtype, subtype, value, replacestr);
}

void Operator::new_paragraph(int, int, float, char *)
{
   if(Global::files->blankline_area)
      return;

   // Suppress more new paragraphs.
   Global::files->newline_in_this_blankline_area = 1; 
}

/* LaTeX Fonts are bulky, and in LameTeX (just like LaTeX) the fonts
 * are included one character at a time, not one fontfamily at a time.
 * A fontfamily is much too large to load in if we're only going to
 * use a couple of characters from the font type.
 *
 * So, whenever we send to PostScript a character meant to be part of a string
 * we should register that the font description for this character in the
 * current font type should be included.
 *
 * This routine performs this registration. Also it adds the syntax
 * necessary for PostScript to print this string, returning it in textout.
 */
void Operator::registrar(char *textin, char *textout)
{
   Stack::set(Environment::PFont, Font::Used, 0.0, textin);

   textout[0]='(';
   for(int in=0,out=1; textin[in]; in++,out++) {
      switch(textin[in]) {
      case '\n':
	 textout[out++]='\\';
         textout[out]='n';
         break;
      case '\t':
	 textout[out++]='\\';
         textout[out]='t';
         break;
      case '\r':
	 textout[out++]='\\';
         textout[out]='r';
         break;
      case '`':
         if(textin[in+1]=='`') {
           in++;
           textout[out++]='\\';
           textout[out]='\\';
         }
         else
         textout[out]='`';
         break;
      case '\'':
         if(textin[in+1]=='\'') {
           in++;
           textout[out]='\"';
         }
         else
         textout[out]='\'';
         break;
      case '\\':
      case ')':
      case '(':
	 textout[out++]='\\';
      default:
	 textout[out]=textin[in];
      }
   }
   textout[out++]=')';
   textout[out]='\0';
}

/* If we are inside the middle of the document, then print this plaintext
 * normally.
 */
void Operator::plaintext(char *text)
{
   // Plaintext is not valid in a comment that's not in a stealth environment.
   if(Stack::get(Environment::PDocument, Document::Comment, "")
      && !Stack::get(Environment::PDocument, Document::Stealth, ""))
      return;

   if(!Stack::get(Environment::PDocument,Document::DocumentStart,"")) {
      Global::files->warning("Missing \\begin{document}.");
      return;
   }

   Global::files->force_start_page();  // Start a new page if not started.

   // This is NOT just after a new section.
   Global::stack->set(Environment::PDocument,
		      Document::JustDidSection, 0.0, "");

   if(Global::files->readjust_vspace > 0.0) {
      Global::files->outfile << endl
	 << Global::files->readjust_vspace
	 << " woids 1 gt { 0 exch READJUST } { pop } ifelse" << endl;
      Global::files->readjust_vspace = 0.0;
   }

   // Must process for special characters!
   char output[MAXSTRING];
   Operator::registrar(text, output);
   Global::files->outfile << " " << output << " NW";
}

/* Shleps a PostScript operator into the word list */
void Operator::pscmd(int, int, float, char *)
{
   char commandline[MAXSTRING+5];
   char buildline[MAXSTRING];

   Token openbrace;
   if(!openbrace.match("{"))
      Global::files->fatal_error("Expecting '{' after \\pscmd statement");
   
   strcpy(buildline,"");

   int x=0;
   for(Token command; !command.match("}"); command = Token()) {
      if(x++>0)
	 strcat(buildline, " ");
      strcat(buildline, command.get_text());
   }

   if(x>1 || buildline[0]!='/')
      sprintf(commandline,"{ %s }", buildline);
   else
      strcpy(commandline, buildline);
   
   Stack::set(Environment::PFont, Font::Currentused, 0.0, commandline);
}


/* Gives the postscript file describing parameters for the next page */
void Operator::pspage(int, int, float, char *)
{
   Token openbrace;
   if(!openbrace.match("{"))
      Global::files->fatal_error("Expecting '{' after \\pspage statement");
   
   Token filename;
   if(filename.match("}"))
      Global::files->warning(
            "Expecting filename before closing '}' in \\pspage");

   Token closebrace;
   if(!closebrace.match("}"))
      Global::files->fatal_error(
         "More than one word before closing '}' in \\pspage");

   // Use the new pspage.
   Global::files->use_pspage(filename.get_text());
}

void Operator::ref(int, int, float, char *)
{
   Token openbrace;
   if(!openbrace.match("{"))
      Global::files->fatal_error("Expecting '{' after \\ref statement");
   
   Token labelname;
   if(labelname.match("}"))
      Global::files->warning(
            "Expecting label name before closing '}' in \\ref");

   Token closebrace;
   if(!closebrace.match("}"))
      Global::files->fatal_error(
         "More than token before closing '}' in \\ref. Sorry, you must rename your label.");

   Global::labels->print_ref(labelname.get_text());
}

void Operator::replace(int, int, float, char *replacestr)
{
   if(replacestr[0])    // A null string means there is no replacement
      plaintext(replacestr);
}

void Operator::setlength(int paramtype, int subtype, float value,
                         char *replacestr)
{
   Length *length;
   length = Global::stack->get_length();
   length->setlength(paramtype, subtype, value, replacestr);
}

// Skip the contents of the next brace set
void Operator::skip(int paramtype, int subtype, float value,
		    char *replacestr)
{
   Token openbrace;
   if(!openbrace.match("{"))
      Global::files->fatal_error("Missing {");
      
   Token token;
   while(!token.match("}")) {
      token = Token();
      if(token.match("\\end"))
	 Operator::skip(paramtype, subtype, value, replacestr);
   }
}

void Operator::today(int, int, float, char*)
{
   char buffer[20];
   time_t clock;
   
   time(&clock);
   struct tm *tm = localtime(&clock);

   strftime(buffer, 20, "%B", tm);
   Operator::plaintext(buffer);
   strftime(buffer, 20, "%d,", tm);
   if(buffer[0]=='0')
      Operator::plaintext(&buffer[1]);
   else
      Operator::plaintext(buffer);
   strftime(buffer, 20, "%Y", tm);
   Operator::plaintext(buffer);
}

void Operator::vspace(int, int, float, char *)
{
   Token openbrace;
   if(!openbrace.match("{"))
      Global::files->fatal_error("Expecting '{' after \\vspace");
   
   float height = Stack::get(Environment::PLength, Length::Parse_Length, "");
   Operator::do_vspace(0, 0, height, "");
}

int Operator::isvalid()
{
   return _valid;
}

int Operator::is_stealth()
{
   return _stealth;
}