/*
 * File:     tex2any.cc
 * Purpose:  Conversion utility for LaTeX files
 *
 *                       wxWindows 1.50
 * Copyright (c) 1993 Artificial Intelligence Applications Institute,
 *                   The University of Edinburgh
 *
 *                     Author: Julian Smart
 *                        Date: 7-9-93
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose is hereby granted without fee, provided
 * that the above copyright notice, author statement and this permission
 * notice appear in all copies of this software and related documentation.
 *
 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, EXPRESS,
 * IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
 *
 * IN NO EVENT SHALL THE ARTIFICIAL INTELLIGENCE APPLICATIONS INSTITUTE OR THE
 * UNIVERSITY OF EDINBURGH BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF THE POSSIBILITY OF
 * DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH
 * THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <wx.h>
#include <ctype.h>
#include "tex2any.h"
#include <stdlib.h>
#include <time.h>

/*
 * Variables accessible from clients
 *
 */
 
TexChunk *      DocumentTitle = NULL;
TexChunk *      DocumentAuthor = NULL;
TexChunk *      DocumentDate = NULL;
char *          CurrentLabel = NULL;
int             DocumentStyle = LATEX_REPORT;
int             MinorDocumentStyle = 0;
wxPathList      TexPathList;
char *          BibliographyStyleString = copystring("plain");
char *          DocumentStyleString = copystring("report");
int             ParSkip = 0;
int             ParIndent = 0;

int             normalFont = 10;
int             smallFont = 8;
int             tinyFont = 6;
int             largeFont1 = 12;
int             LargeFont2 = 14;
int             LARGEFont3 = 18;
int             hugeFont1 = 20;
int             HugeFont2 = 24;
int             HUGEFont3 = 28;

// Section font sizes
int             chapterFont =    LARGEFont3;
int             sectionFont =    LargeFont2;
int             subsectionFont = largeFont1;

/*
 * Section numbering
 *
 */
 
int             chapterNo = 0;
int             sectionNo = 0;
int             subsectionNo = 0;
int             subsubsectionNo = 0;
int             figureNo = 0;

/*
 * Other variables
 *
 */
 
FILE *CurrentOutput1 = NULL;
FILE *CurrentOutput2 = NULL;
FILE *Inputs[15];
int CurrentInputIndex = 0;
char TexFileRoot[300];
char TexBibName[300];         // Bibliography output file name
char TexTmpBibName[300];      // Temporary bibliography output file name

static int currentColumn = 0;
char currentArgData[2000];
Bool haveArgData = FALSE; // If TRUE, we're simulating the data.
TexChunk *currentArgument = NULL;
TexChunk *nextChunk = NULL;
Bool isArgOptional = FALSE;
Bool noArgs = 0;

TexChunk *TopLevel = NULL;
wxList MacroDefs(wxKEY_STRING);
wxStringList IgnorableInputFiles; // Ignorable \input files, e.g. psbox.tex
char *BigBuffer = NULL;  // For reading in large chunks of text
TexMacroDef *SoloBlockDef = NULL;

void TexOutput(char *s, Bool ordinaryText)
{
  int len = strlen(s);

  // Update current column, but only if we're guaranteed to
  // be ordinary text (not mark-up stuff)
  if (ordinaryText)
    for (int i = 0; i < len; i++)
    {
      if (s[i] == 13 || s[i] == 10)
        currentColumn = 0;
      else
        currentColumn ++;
    }

  if (CurrentOutput1)
    fprintf(CurrentOutput1, "%s", s);
  if (CurrentOutput2)
    fprintf(CurrentOutput2, "%s", s);
}

/*
 * Try to find a Latex macro, in one of the following forms:
 * (1) \begin{} ... \end{}
 * (2) \macroname{arg1}...{argn}
 * (3) {\bf arg1}
 */
 
TexMacroDef *MatchMacro(char *buffer, int *pos, char **env, Bool *parseToBrace)
{
  *parseToBrace = TRUE;
  int i = (*pos);
  TexMacroDef *def = NULL;

  // First, try to find begin{thing}
  if (strncmp(buffer+i, "begin{", 6) == 0)
  {
    i += 6;

    wxNode *node = MacroDefs.First();
    while (node && !def)
    {
      if (strncmp(buffer+i, node->key.string, strlen(node->key.string)) == 0)
      { def = (TexMacroDef *)node->Data();
        i += strlen(node->key.string); }
      else node = node->Next();
    }
    if (def)
    {
      *pos = i + 1;
      *env = def->name;
      return def;
    }
    else return NULL;
  }

  // Failed, so try to find macro from definition list
  wxNode *node = MacroDefs.First();
  while (node && !def)
  {
    if (strncmp(buffer+i, node->key.string, strlen(node->key.string)) == 0)
    { def = (TexMacroDef *)node->Data();
      i += strlen(node->key.string); }
    else node = node->Next();
  }
  if (def)
  {
    // We want to check whether this is a space-consuming macro
    // (e.g. {\bf word})
    // No brace, e.g. \input thing.tex instead of \input{thing}
    if ((def->no_args > 0) && ((buffer[i] == 32) || (buffer[i] == '=')))
    {
      i ++;
      *parseToBrace = FALSE;
    }
    *pos = i;
    return def;
  }
  return NULL;
}

Bool FindEndEnvironment(char *buffer, int *pos, char *env)
{
  int i = (*pos);

  // Try to find end{thing}
  if ((strncmp(buffer+i, "end{", 4) == 0) &&
      (strncmp(buffer+i+4, env, strlen(env)) == 0))
  {
    *pos = i + 5 + strlen(env);
    return TRUE;
  }
  else return FALSE;
}

Bool readingVerbatim = FALSE;
Bool readInVerbatim = FALSE;  // Within a verbatim, but not nec. verbatiminput

Bool read_a_line(char *buf)
{
  if (CurrentInputIndex < 0)
  {
    buf[0] = 0;
    return FALSE;
  }
  
  int ch = -2;
  int i = 0;
  buf[0] = 0;
  while (ch != EOF && ch != 10)
  {
    ch = getc(Inputs[CurrentInputIndex]);
    if (ch != EOF)
    {
      // Check for 2 consecutive newlines and replace with \par
      if (ch == 10 && !readInVerbatim)
      {
        int ch1 = getc(Inputs[CurrentInputIndex]);
        if ((ch1 == 10) || (ch1 == 13))
        {
          // Eliminate newline (10) following DOS linefeed
          if (ch1 == 13) ch1 = getc(Inputs[CurrentInputIndex]);
          buf[i] = 0;
          strcat(buf, "\\par\n");
          i += 6;
        }
        else
        {
          ungetc(ch1, Inputs[CurrentInputIndex]);
          buf[i] = ch;
          i ++;
        }
      }
      else
      {
        buf[i] = ch;
        i ++;
      }
    }
    else
    {
      buf[i] = 0;
      fclose(Inputs[CurrentInputIndex]);
      Inputs[CurrentInputIndex] = NULL;
      if (CurrentInputIndex > 0) ch = ' '; // No real end of file
      CurrentInputIndex --;
      if (readingVerbatim)
      {
        readingVerbatim = FALSE;
        readInVerbatim = FALSE;
        strcat(buf, "\\end{verbatim}\n");
        return FALSE;
      }
    }
  }
  buf[i] = 0;

  // Read a verbatim input file as if it were a verbatim environment
  if (strncmp(buf, "\\verbatiminput", 14) == 0)
  {
    int wordLen = 14;
    char *fileName = buf + wordLen + 1;

    int j = i - 1;
    buf[j] = 0;

    // thing}\par -- eliminate the \par!
    if (strncmp((buf + strlen(buf)-5), "\\par", 4) == 0)
    {
      j -= 5;
      buf[j] = 0;
    }
    
    if (buf[j-1] == '}') buf[j-1] = 0; // Ignore final brace

    char *actualFile = TexPathList.FindValidPath(fileName);
    if (!actualFile)
    {
      char errBuf[300];
      strcpy(errBuf, "Could not find file: ");
      strncat(errBuf, fileName, 100);
      OnError(errBuf);
    }
    else
    {
      CurrentInputIndex ++;
      Inputs[CurrentInputIndex] = fopen(actualFile, "r");
      if (!Inputs[CurrentInputIndex])
      {
        CurrentInputIndex --;
        OnError("Could not open verbatiminput file.");
      }
      else
      {
        readingVerbatim = TRUE;
        readInVerbatim = TRUE;
        strcpy(buf, "\\begin{verbatim}\n");
        return FALSE;
      }
    }
    return FALSE;
  }
  else if (strncmp(buf, "\\input", 6) == 0 || strncmp(buf, "\\helpinput", 10) == 0 ||
      strncmp(buf, "\\include", 8) == 0)
  {
    int wordLen;
    if (strncmp(buf, "\\input", 6) == 0)
      wordLen = 6;
    else
    if (strncmp(buf, "\\include", 8) == 0)
      wordLen = 8;
    else
      wordLen = 10;

    char *fileName = buf + wordLen + 1;

    int j = i - 1;
    buf[j] = 0;

    // \input{thing}\par -- eliminate the \par!
    if (strncmp((buf + strlen(buf)-5), "\\par", 4) == 0)
    {
      j -= 5;
      buf[j] = 0;
    }

    if (buf[j-1] == '}') buf[j-1] = 0; // Ignore final brace

    // Ignore some types of input files (e.g. macro definition files)
    char *fileOnly = FileNameFromPath(fileName);
    if (IgnorableInputFiles.Member(fileOnly))
      return read_a_line(buf);

    char *actualFile = TexPathList.FindValidPath(fileName);
    if (!actualFile)
    {
      char errBuf[300];
      strcpy(errBuf, "Could not find file: ");
      strncat(errBuf, fileName, 100);
      OnError(errBuf);
    }
    else
    {
      CurrentInputIndex ++;
      Inputs[CurrentInputIndex] = fopen(actualFile, "r");
      if (!Inputs[CurrentInputIndex])
      {
        CurrentInputIndex --;
        OnError("Could not open include file.");
      }
    }
    Bool succ = read_a_line(buf);
    return succ;
  }
  if (strncmp(buf, "\\begin{verbatim}", 16) == 0)
    readInVerbatim = TRUE;
  else if (strncmp(buf, "\\end{verbatim}", 14) == 0)
    readInVerbatim = FALSE;

  return (ch == EOF);
}

/*
 * Parse newcommand
 *
 */

Bool ParseNewCommand(char *buffer, int *pos)
{
  if (strncmp((buffer+(*pos)), "newcommand", 10) == 0)
  {
    *pos = *pos + 12;
    char commandName[100];
    char commandValue[1000];
    int noArgs = 0;
    int i = 0;
    while (buffer[*pos] != '}' && (buffer[*pos] != 0))
    {
      commandName[i] = buffer[*pos];
      *pos += 1;
      i ++;
    }
    commandName[i] = 0;
    i = 0;
    *pos += 1;
    if (buffer[*pos] == '[')
    {
      *pos += 1;
      noArgs = (int)(buffer[*pos]) - 48;
      *pos += 1;
    }
    Bool end = FALSE;
    Bool braceCount = 0;
    while (!end)
    {
      char ch = buffer[*pos];
      if (ch == '{')
        braceCount ++;
      else if (ch == '}')
      {
        braceCount --;
        if (braceCount == 0)
          end = TRUE;
      }
      else if (ch == 0)
      {
        if (!read_a_line(buffer))
          end = TRUE;
        *pos = 0;
        break;
      }
      commandValue[i] = ch;
      i ++;
      *pos += 1;
    }
    commandValue[i] = 0;

    CustomMacro *macro = new CustomMacro(commandName, noArgs, NULL);
    if (strlen(commandValue) > 0)
      macro->macroBody = copystring(commandValue);
    if (!CustomMacroList.Find(commandName))
    {
      CustomMacroList.Append(commandName, macro);
      AddMacroDef(commandName, noArgs);
    }
    return TRUE;
  }
  else return FALSE;
}

void MacroError(char *buffer)
{
  char errBuf[300];
  char macroBuf[200];
  strcpy(errBuf, "Could not find macro: ");
  macroBuf[0] = '\\';
  int i = 1;
  char ch;
  while (((ch = buffer[i-1]) != '\n') && (ch != 0))
  {
    macroBuf[i] = ch;
    i ++;
  }
  macroBuf[i] = 0;
  if (i > 20)
    macroBuf[20] = 0;
    
  strcat(errBuf, macroBuf);
  OnError(errBuf);
}

/*
 * Parse an argument.
 * 'environment' specifies the name of the macro IFF if we're looking for the end
 * of an environment, e.g. \end{itemize}. Otherwise it's NULL.
 * 'parseToBrace' is TRUE if the argument should extend to the next right brace,
 * e.g. in {\bf an argument} as opposed to \vskip 30pt
 *
 */
int ParseArg(TexChunk *thisArg, wxList& children, char *buffer, int pos, char *environment, Bool parseToBrace, TexChunk *customMacroArgs)
{
  Bool eof = FALSE;
  BigBuffer[0] = 0;
  int buf_ptr = 0;
  int len;

  Bool isOptional = FALSE;

  // Consume leading brace or square bracket, but ONLY if not following
  // a space, because this could be e.g. {\large {\bf thing}} where {\bf thing}
  // is the argument of \large AS WELL as being a block in its
  // own right.
  if (!environment)
  {
    if ((pos > 0) && (buffer[pos-1] != ' ') && buffer[pos] == '{')
      pos ++;
    else if ((pos > 0) && (buffer[pos-1] != ' ') && (buffer[pos] == '[' || buffer[pos] == '('))
    {
      isOptional = TRUE;
      pos ++;
    }
    else if ((pos > 1) && (buffer[pos-1] != ' ') && (buffer[pos+1] == '[' || buffer[pos+1] == '('))
    {
      isOptional = TRUE;
      pos += 2;
    }
  }
    
  // If not parsing to brace, just read the next word
  // (e.g. \vskip 20pt)
  if (!parseToBrace)
  {
    int ch = buffer[pos];
    while (!eof && ch != 13 && ch != 32 && ch != 10 &&
           ch != 0 && ch != '(' && ch != '{')
    {
      BigBuffer[buf_ptr] = ch;
      buf_ptr ++;
      pos ++;
      ch = buffer[pos];
    }
    if (buf_ptr > 0)
    {
      TexChunk *chunk = new TexChunk(CHUNK_TYPE_STRING);
      BigBuffer[buf_ptr] = 0;
      buf_ptr = 0;
      chunk->value = copystring(BigBuffer);
      children.Append(chunk);
    }
    return pos;
  }

  while (!eof)
  {
    len = strlen(buffer);
    if (pos >= len)
    {
      eof = read_a_line(buffer);
      pos = 0;
      len = strlen(buffer);
      // Check for verbatim
      if (strncmp(buffer, "\\begin{verbatim}", 16) == 0)
      {
        if (buf_ptr > 0)
        {
          TexChunk *chunk = new TexChunk(CHUNK_TYPE_STRING);
          BigBuffer[buf_ptr] = 0;
          buf_ptr = 0;
          chunk->value = copystring(BigBuffer);
          children.Append(chunk);
        }
        BigBuffer[0] = 0;
        buf_ptr = 0;

        eof = read_a_line(buffer);
        while (!eof && (strncmp(buffer, "\\end{verbatim}", 14) != 0))
	{
          strcat(BigBuffer, buffer);
          buf_ptr += strlen(buffer);
          eof = read_a_line(buffer);
	}
        eof = read_a_line(buffer);
        buf_ptr = 0;

        TexChunk *chunk = new TexChunk(CHUNK_TYPE_MACRO);
        chunk->no_args = 1;
        chunk->name = copystring("verbatim");
        TexChunk *arg = new TexChunk(CHUNK_TYPE_ARG);
        arg->argn = 1;
        arg->name = copystring("verbatim");
        TexChunk *str = new TexChunk(CHUNK_TYPE_STRING);
        str->value = copystring(BigBuffer);

        children.Append(chunk);
        chunk->children.Append(arg);
        arg->children.Append(str);
      }
    }

    char ch = buffer[pos];
    // End of optional argument -- pretend it's right brace for simplicity
    if (isOptional && (ch == ']' || ch == ')'))
      ch = '}';

    switch (ch)
    {
      case 0:
      case '}':  // End of argument
      {
        if (buf_ptr > 0)
        {
          TexChunk *chunk = new TexChunk(CHUNK_TYPE_STRING);
          BigBuffer[buf_ptr] = 0;
          buf_ptr = 0;
          chunk->value = copystring(BigBuffer);
          thisArg->optional = isOptional;
          children.Append(chunk);
        }
        if (ch == '}') pos ++;
        return pos;
        break;
      }
      case '\\':
      {
        if (buf_ptr > 0)  // Finish off the string we've read so far
        {
          TexChunk *chunk = new TexChunk(CHUNK_TYPE_STRING);
          BigBuffer[buf_ptr] = 0;
          buf_ptr = 0;
          chunk->value = copystring(BigBuffer);
          children.Append(chunk);
        }
        pos ++;

        // Try matching \end{environment}
        if (environment && FindEndEnvironment(buffer, &pos, environment))
        {
          // Eliminate newline after an \end{} if possible
          if (buffer[pos] == 13)
          {
            pos ++;
            if (buffer[pos] == 10)
              pos ++;
          }
          return pos;
        }

        if (ParseNewCommand(buffer, &pos))
          break;
	  
        char *env = NULL;
        Bool tmpParseToBrace = TRUE;
        TexMacroDef *def = MatchMacro(buffer, &pos, &env, &tmpParseToBrace);
        if (def)
        {
          CustomMacro *customMacro = FindCustomMacro(def->name);

          TexChunk *chunk = new TexChunk(CHUNK_TYPE_MACRO);
          chunk->no_args = def->no_args;
          chunk->name = copystring(def->name);
          if  (!customMacro)
            children.Append(chunk);

          // Eliminate newline after a \begin{} or a \\ if possible
          if (env || strcmp(def->name, "\\") == 0)
            if (buffer[pos] == 13)
            {
              pos ++;
              if (buffer[pos] == 10)
                pos ++;
            }

          pos = ParseMacroBody(chunk->name, chunk, chunk->no_args,
                     buffer, pos, env, tmpParseToBrace, customMacroArgs);

          // If custom macro, parse the body substituting the above found args.
          if (customMacro)
          {
            if (customMacro->macroBody)
            {
              char macroBuf[300];
//              strcpy(macroBuf, "{");
              strcpy(macroBuf, customMacro->macroBody);
              strcat(macroBuf, "}");
              ParseArg(thisArg, children, macroBuf, 0, NULL, TRUE, chunk);
            }
            
//            delete chunk; // Might delete children
          }
        }
        else
        {
          MacroError(buffer+pos);
        }
        break;
      }
      // Parse constructs like {\bf thing} as if they were
      // \bf{thing}
      case '{':
      {
        pos ++;
        if (buffer[pos] == '\\')
        {
          if (buf_ptr > 0)
          {
            TexChunk *chunk = new TexChunk(CHUNK_TYPE_STRING);
            BigBuffer[buf_ptr] = 0;
            buf_ptr = 0;
            chunk->value = copystring(BigBuffer);
            children.Append(chunk);
          }
          pos ++;

          char *env;
          Bool tmpParseToBrace;
          TexMacroDef *def = MatchMacro(buffer, &pos, &env, &tmpParseToBrace);
          if (def)
          {
            CustomMacro *customMacro = FindCustomMacro(def->name);

            TexChunk *chunk = new TexChunk(CHUNK_TYPE_MACRO);
            chunk->no_args = def->no_args;
            chunk->name = copystring(def->name);
            if (!customMacro)
              children.Append(chunk);

            pos = ParseMacroBody(chunk->name, chunk, chunk->no_args,
                       buffer, pos, NULL, TRUE, customMacroArgs);

            // If custom macro, parse the body substituting the above found args.
            if (customMacro)
            {
              if (customMacro->macroBody)
              {
                char macroBuf[300];
//                strcpy(macroBuf, "{");
                strcpy(macroBuf, customMacro->macroBody);
                strcat(macroBuf, "}");
                ParseArg(thisArg, children, macroBuf, 0, NULL, TRUE, chunk);
              }
            
//            delete chunk; // Might delete children
	    }
          }
          else
          {
            MacroError(buffer+pos);
          }
        }
        else
	{
         /*
          * If all else fails, we assume that we have
          * a pair of braces on their own, so return a `dummy' macro
          * definition with just one argument to parse.
          */
          if (!SoloBlockDef)
          {
            SoloBlockDef = new TexMacroDef("solo block", 1, FALSE);
          }
          // Save text so far
          if (buf_ptr > 0)
          {
            TexChunk *chunk1 = new TexChunk(CHUNK_TYPE_STRING);
            BigBuffer[buf_ptr] = 0;
            buf_ptr = 0;
            chunk1->value = copystring(BigBuffer);
            children.Append(chunk1);
          }
          TexChunk *chunk = new TexChunk(CHUNK_TYPE_MACRO);
          chunk->no_args = SoloBlockDef->no_args;
          chunk->name = copystring(SoloBlockDef->name);
          children.Append(chunk);
          pos = ParseMacroBody(chunk->name, chunk, chunk->no_args,
                       buffer, pos, NULL, TRUE, customMacroArgs);
	}
        break;
      }
      case '$':
      {
        if (buf_ptr > 0)
        {
          TexChunk *chunk = new TexChunk(CHUNK_TYPE_STRING);
          BigBuffer[buf_ptr] = 0;
          buf_ptr = 0;
          chunk->value = copystring(BigBuffer);
          children.Append(chunk);
        }

        pos ++;

        if (buffer[pos] == '$')
        {
          TexChunk *chunk = new TexChunk(CHUNK_TYPE_MACRO);
          chunk->no_args = 0;
          chunk->name = copystring("$$");
          children.Append(chunk);
          pos ++;
        }
        else
        {
          TexChunk *chunk = new TexChunk(CHUNK_TYPE_MACRO);
          chunk->no_args = 0;
          chunk->name = copystring("_$");
          children.Append(chunk);
        }
        break;
      }
      case '~':
      {
        if (buf_ptr > 0)
        {
          TexChunk *chunk = new TexChunk(CHUNK_TYPE_STRING);
          BigBuffer[buf_ptr] = 0;
          buf_ptr = 0;
          chunk->value = copystring(BigBuffer);
          children.Append(chunk);
        }

        pos ++;
        TexChunk *chunk = new TexChunk(CHUNK_TYPE_MACRO);
        chunk->no_args = 0;
        chunk->name = copystring("_~");
        children.Append(chunk);
        break;
      }
      case '#': // Either treat as a special TeX character or as a macro arg
      {
        if (buf_ptr > 0)
        {
          TexChunk *chunk = new TexChunk(CHUNK_TYPE_STRING);
          BigBuffer[buf_ptr] = 0;
          buf_ptr = 0;
          chunk->value = copystring(BigBuffer);
          children.Append(chunk);
        }

        pos ++;
        if (!customMacroArgs)
        {
          TexChunk *chunk = new TexChunk(CHUNK_TYPE_MACRO);
          chunk->no_args = 0;
          chunk->name = copystring("_#");
          children.Append(chunk);
        }
        else
        {
          if (isdigit(buffer[pos]))
          {
            int n = buffer[pos] - 48;
            pos ++;
            wxNode *node = customMacroArgs->children.Nth(n-1);
            if (node)
            {
              TexChunk *argChunk = (TexChunk *)node->Data();
              children.Append(new TexChunk(*argChunk));
            }
          }
        }
        break;
      }
      case '&':
      {
        // Remove white space before and after the ampersand,
        // since this is probably a table column separator with
        // some convenient -- but useless -- white space in the text.
        while ((buf_ptr > 0) && ((BigBuffer[buf_ptr-1] == ' ') || (BigBuffer[buf_ptr-1] == 9)))
          buf_ptr --;

        if (buf_ptr > 0)
        {
          TexChunk *chunk = new TexChunk(CHUNK_TYPE_STRING);
          BigBuffer[buf_ptr] = 0;
          buf_ptr = 0;
          chunk->value = copystring(BigBuffer);
          children.Append(chunk);
        }

        pos ++;

        while (buffer[pos] == ' ' || buffer[pos] == 9)
          pos ++;

        TexChunk *chunk = new TexChunk(CHUNK_TYPE_MACRO);
        chunk->no_args = 0;
        chunk->name = copystring("_&");
        children.Append(chunk);
        break;
      }
      // Eliminate end-of-line comment
      case '%':
      {
        ch = buffer[pos];
        while (ch != 10 && ch != 13 && ch != 0)
        {
          pos ++;
          ch = buffer[pos];
        }
        if (buffer[pos] == 10 || buffer[pos] == 13)
        {
          pos ++;
          if (buffer[pos] == 10) pos ++; // Eliminate newline following DOS line feed
        }
        break;
      }
      // Eliminate tab
      case 9:
      {
        BigBuffer[buf_ptr] = ' ';
        BigBuffer[buf_ptr+1] = 0;
        buf_ptr ++;
        pos ++;
        break;
      }
      default:
      {
        BigBuffer[buf_ptr] = ch;
        BigBuffer[buf_ptr+1] = 0;
        buf_ptr ++;
        pos ++;
        break;
      }
    }
  }
  return pos;
}

/*
 * Consume as many arguments as the macro definition specifies
 *
 */
 
int ParseMacroBody(char *macro_name, TexChunk *parent,
                   int no_args, char *buffer, int pos,
                   char *environment, Bool parseToBrace,
                   TexChunk *customMacroArgs)
{
  // Check for a first optional argument
  if (buffer[pos] == '[')
    no_args ++;

  int maxArgs = 0;

  for (int i = 0; i < no_args; i++)
  {
    maxArgs ++;
    TexChunk *arg = new TexChunk(CHUNK_TYPE_ARG);

    parent->children.Append(arg);
    arg->name = copystring(macro_name);
    arg->argn = maxArgs;

    // To parse the first arg of a 2 arg \begin{thing}{arg} ... \end{thing}
    // have to fool parser into thinking this is a regular kind of block.
    char *actualEnv;
    if ((no_args == 2) && (i == 0))
      actualEnv = NULL;
    else
      actualEnv = environment;

    pos = ParseArg(arg, arg->children, buffer, pos, actualEnv, parseToBrace, customMacroArgs);

    // If we've encountered an OPTIONAL argument, go another time around
    // the loop, because we've got more than we thought.
    // Hopefully optional args don't occur at the end of a macro use
    // or we might miss it.
    // Don't increment no of times round loop if the first optional arg
    // -- we already did it before the loop.
    if (arg->optional && (i > 0))
      i --;
  }
  parent->no_args = maxArgs;

  // Tell each argument how many args there are (useful when processing an arg)
  wxNode *node = parent->children.First();
  while (node)
  {
    TexChunk *chunk = (TexChunk *)node->Data();
    chunk->no_args = maxArgs;
    node = node->Next();
  }
  return pos;
}

Bool TexLoadFile(char *filename, char *customMacroFile)
{
  strcpy(TexFileRoot, filename);
  StripExtension(TexFileRoot);
  sprintf(TexBibName, "%s.bb", TexFileRoot);
  sprintf(TexTmpBibName, "%s.bb1", TexFileRoot);

  TexPathList.EnsureFileAccessible(filename);

#ifdef wx_msw
  TexPathList.AddEnvList("TEXINPUT");
#endif
#ifdef wx_x
  TexPathList.AddEnvList("TEXINPUTS");
#endif
  if (!customMacroFile)
    customMacroFile = "tex2rtf.ini";
  if (TexPathList.FindValidPath(customMacroFile))
    ReadCustomMacros(customMacroFile);

  static char line_buffer[500];
  Inputs[0] = fopen(filename, "r");
  if (Inputs[0])
  {
    read_a_line(line_buffer);
    ParseMacroBody("toplevel", TopLevel, 1, line_buffer, 0, NULL, TRUE);
    if (Inputs[0]) fclose(Inputs[0]);
    return TRUE;
  }
  else return FALSE;
}

TexMacroDef::TexMacroDef(char *the_name, int n, Bool ig)
{
  name = copystring(the_name);
  no_args = n;
  ignore = ig;
}

TexMacroDef::~TexMacroDef(void)
{
  if (name) delete[] name;
}

TexChunk::TexChunk(int the_type)
{
  type = the_type;
  no_args = 0;
  argn = 0;
  name = NULL;
  value = NULL;
  optional = FALSE;
  children.DeleteContents(TRUE);
}

TexChunk::TexChunk(TexChunk& toCopy)
{
  type = toCopy.type;
  no_args = toCopy.no_args;
  argn = toCopy.argn;
  if (toCopy.name)
    name = copystring(toCopy.name);
  else
    name = NULL;

  if (toCopy.value)
    value = copystring(toCopy.value);
  else
    value = NULL;
  
  optional = toCopy.optional;
  wxNode *node = toCopy.children.First();
  while (node)
  {
    TexChunk *child = (TexChunk *)node->Data();
    children.Append(new TexChunk(*child));
    node = node->Next();
  }
  children.DeleteContents(TRUE);
}

TexChunk::~TexChunk(void)
{
  if (name) delete[] name;
  if (value) delete[] value;
}

Bool IsArgOptional(void)  // Is this argument an optional argument?
{
  return isArgOptional;
}

int GetNoArgs(void) // Number of args for this macro
{
  return noArgs;
}

/* Gets the text of a chunk on request (must be for small arguments
 * only!)
 *
 */
 
void GetArgData1(TexChunk *chunk)
{
  switch (chunk->type)
  {
    case CHUNK_TYPE_MACRO:
    {
      wxNode *node = MacroDefs.Find(chunk->name);
      if (node)
      {
        TexMacroDef *def = (TexMacroDef *)node->Data();
        if (def->ignore)
          return;
      }

      strcat(currentArgData, "\\");
      strcat(currentArgData, chunk->name);

      node = chunk->children.First();
      while (node)
      {
        TexChunk *child_chunk = (TexChunk *)node->Data();
        strcat(currentArgData, "{");
        GetArgData1(child_chunk);
        strcat(currentArgData, "}");
        node = node->Next();
      }
      break;
    }
    case CHUNK_TYPE_ARG:
    {
      wxNode *node = chunk->children.First();
      while (node)
      {
        TexChunk *child_chunk = (TexChunk *)node->Data();
        GetArgData1(child_chunk);
        node = node->Next();
      }
      break;
    }
    case CHUNK_TYPE_STRING:
    {
      if (chunk->value)
        strcat(currentArgData, chunk->value);
      break;
    }
  }
}

char *GetArgData(void)
{
  if (!haveArgData)
  {
    currentArgData[0] = 0;
    GetArgData1(currentArgument);
  }
  return currentArgData;
}

TexChunk *GetArgChunk(void)
{
  return currentArgument;
}

TexChunk *GetNextChunk(void)     // Look ahead to the next chunk
{
  return nextChunk;
}

TexChunk *GetTopLevelChunk(void)
{
  return TopLevel;
}

int GetCurrentColumn(void)
{
  return currentColumn;
}

/*
 * Traverses document calling functions to allow the client to
 * write out the appropriate stuff
 */


void TraverseFromChunk(TexChunk *chunk, wxNode *thisNode, Bool childrenOnly)
{
  switch (chunk->type)
  {
    case CHUNK_TYPE_MACRO:
    {
      wxNode *node = MacroDefs.Find(chunk->name);
      if (node)
      {
        TexMacroDef *def = (TexMacroDef *)node->Data();
        if (def->ignore)
          return;
      }

      if (!childrenOnly)
        OnMacro(chunk->name, chunk->no_args, TRUE);

      node = chunk->children.First();
      while (node)
      {
        TexChunk *child_chunk = (TexChunk *)node->Data();
        TraverseFromChunk(child_chunk, node);
        node = node->Next();
      }

      if (thisNode && thisNode->Next()) nextChunk = (TexChunk *)thisNode->Next()->Data();

      if (!childrenOnly)
        OnMacro(chunk->name, chunk->no_args, FALSE);
      break;
    }
    case CHUNK_TYPE_ARG:
    {
      currentArgument = chunk;

      isArgOptional = chunk->optional;
      noArgs = chunk->no_args;

      // If OnArgument returns FALSE, don't output.

      if (childrenOnly || OnArgument(chunk->name, chunk->argn, TRUE))
      {
        wxNode *node = chunk->children.First();
        while (node)
        {
          TexChunk *child_chunk = (TexChunk *)node->Data();
          TraverseFromChunk(child_chunk, node);
          node = node->Next();
        }
      }

      currentArgument = chunk;

      if (thisNode && thisNode->Next()) nextChunk = (TexChunk *)thisNode->Next()->Data();

      isArgOptional = chunk->optional;
      noArgs = chunk->no_args;

      if (!childrenOnly)
        (void)OnArgument(chunk->name, chunk->argn, FALSE);
      break;
    }
    case CHUNK_TYPE_STRING:
    {
      if (chunk->value)
        TexOutput(chunk->value, TRUE);
      break;
    }
  }
}

void TraverseDocument(void)
{
  TraverseFromChunk(TopLevel, NULL);
}

void SetCurrentOutput(FILE *fd)
{
  CurrentOutput1 = fd;
  CurrentOutput2 = NULL;
}

void SetCurrentOutputs(FILE *fd1, FILE *fd2)
{
  CurrentOutput1 = fd1;
  CurrentOutput2 = fd2;
}

void AddMacroDef(char *name, int n, Bool ignore)
{
  MacroDefs.Append(name, new TexMacroDef(name, n, ignore));
}

void TexInitialize(void)
{
  for (int i = 0; i < 15; i++)
    Inputs[i] = NULL;

  IgnorableInputFiles.Add("psbox.tex");
  BigBuffer = new char[20000];
  AddMacroDef("toplevel", 1);
  TopLevel = new TexChunk(CHUNK_TYPE_MACRO);
  TopLevel->name = copystring("toplevel");
  TopLevel->no_args = 1;
}

void TexCleanUp(void)
{
  for (int i = 0; i < 15; i++)
    Inputs[i] = NULL;

  chapterNo = 0;
  sectionNo = 0;
  subsectionNo = 0;
  subsubsectionNo = 0;
  figureNo = 0;

  CurrentOutput1 = NULL;
  CurrentOutput2 = NULL;
  CurrentInputIndex = 0;
  haveArgData = FALSE;
  noArgs = 0;

  if (TopLevel)
    delete TopLevel;
  TopLevel = new TexChunk(CHUNK_TYPE_MACRO);
  TopLevel->name = copystring("toplevel");
  TopLevel->no_args = 1;

  DocumentTitle = NULL;
  DocumentAuthor = NULL;
  DocumentDate = NULL;
  CurrentLabel = NULL;
  DocumentStyle = LATEX_REPORT;
  MinorDocumentStyle = 0;
  BibliographyStyleString = copystring("plain");
  DocumentStyleString = copystring("report");
  SetFontSizes(10);
  wxNode *node = CustomMacroList.First();
  while (node)
  {
    CustomMacro *macro = (CustomMacro *)node->Data();
    delete macro;
    delete node;
    node = CustomMacroList.First();
  }
  node = TexReferences.First();
  while (node)
  {
    TexRef *ref = (TexRef *)node->Data();
    delete ref;
    delete node;
    node = TexReferences.First();
  }
  node = BibList.First();
  while (node)
  {
    BibEntry *entry = (BibEntry *)node->Data();
    delete entry;
    delete node;
    node = BibList.First();
  }
  CitationList.Clear();
  ResetTopicCounter();
}

// There is likely to be one set of macros used by all utilities.
void DefineDefaultMacros(void)
{
  // Put names which subsume other names at the TOP
  // so they get recognized first

  AddMacroDef("abstract", 1);
  AddMacroDef("addcontentsline", 3);
  AddMacroDef("addtocounter", 2);
  AddMacroDef("alph", 1);
  AddMacroDef("Alph", 1);
  AddMacroDef("appendix", 0);
  AddMacroDef("arabic", 1);
  AddMacroDef("array", 1);
  AddMacroDef("author", 1);

  AddMacroDef("backslash", 0);
  AddMacroDef("baselineskip", 1);
  AddMacroDef("bf", 1);
  AddMacroDef("bibitem", 2);  // For convenience, bibitem has 2 args: label and item.
                              // The Latex syntax permits writing as 2 args.
  AddMacroDef("bibliographystyle", 1);
  AddMacroDef("bibliography", 1);
  AddMacroDef("boxit", 1);

  AddMacroDef("caption*", 1);
  AddMacroDef("caption", 1);
  AddMacroDef("cdots", 0);
  AddMacroDef("centerline", 1);
  AddMacroDef("centering", 0);
  AddMacroDef("center", 1);
  AddMacroDef("cextract", 0);
  AddMacroDef("chapter*", 1);
  AddMacroDef("chapter", 1);
  AddMacroDef("cinsert", 0);
  AddMacroDef("cite", 1);
  AddMacroDef("class", 1);
  AddMacroDef("cleardoublepage", 0);
  AddMacroDef("clearpage", 0);
  AddMacroDef("cline", 1);
  AddMacroDef("clipsfunc", 3);
  AddMacroDef("columnsep", 1);
  AddMacroDef("comment", 1, TRUE);
  AddMacroDef("copyright", 0);
  AddMacroDef("cparam", 2);

  AddMacroDef("date", 1);
  AddMacroDef("description", 1);
  AddMacroDef("destruct", 1);
  AddMacroDef("documentstyle", 1);
  AddMacroDef("document", 1);
  AddMacroDef("doublespace", 1);

  AddMacroDef("em", 1);
  AddMacroDef("enumerate", 1);
  AddMacroDef("equation", 1);
  AddMacroDef("evensidemargin", 1);

  AddMacroDef("fbox", 1);
  AddMacroDef("figure", 1);
  AddMacroDef("flushleft", 1);
  AddMacroDef("flushright", 1);
  AddMacroDef("footheight", 1);
  AddMacroDef("footnote", 1);
  AddMacroDef("footskip", 1);
  AddMacroDef("framebox", 1);
  AddMacroDef("functionsection", 1);
  AddMacroDef("func", 3);

  AddMacroDef("glossary", 1);
  AddMacroDef("gloss", 1);

  AddMacroDef("headheight", 1);
  AddMacroDef("helpignore", 1, TRUE);
  AddMacroDef("helponly", 1);
  AddMacroDef("helpinput", 1);
  AddMacroDef("helpfontfamily", 1);
  AddMacroDef("helpfontsize", 1);
  AddMacroDef("helprefn", 2);
  AddMacroDef("helpref", 2);
  AddMacroDef("hfill", 0);
  AddMacroDef("hline", 0);
  AddMacroDef("hrule", 0);
  AddMacroDef("hspace*", 1);
  AddMacroDef("hspace", 1);
  AddMacroDef("hskip*", 1);
  AddMacroDef("hskip", 1);
  AddMacroDef("huge", 1);
  AddMacroDef("Huge", 1);
  AddMacroDef("HUGE", 1);

  AddMacroDef("includeonly", 1);
  AddMacroDef("include", 1);
  AddMacroDef("index", 1);
  AddMacroDef("input", 1);
  AddMacroDef("itemize", 1);
  AddMacroDef("item", 0);
  AddMacroDef("image", 2);
  AddMacroDef("it", 1);

  AddMacroDef("kill", 0);

  AddMacroDef("label", 1);
  AddMacroDef("large", 1);
  AddMacroDef("Large", 1);
  AddMacroDef("LARGE", 1);
  AddMacroDef("LaTeX", 0);
  AddMacroDef("lbox", 1);
  AddMacroDef("ldots", 0);
  AddMacroDef("linebreak", 0);
  AddMacroDef("listoffigures", 0);
  AddMacroDef("listoftables", 0);

  AddMacroDef("makeglossary", 0);
  AddMacroDef("makeindex", 0);
  AddMacroDef("maketitle", 0);
  AddMacroDef("markright", 1);
  AddMacroDef("markboth", 2);
  AddMacroDef("marginparwidth", 1);
  AddMacroDef("marginpar", 1);
  AddMacroDef("mbox", 1);
  AddMacroDef("membersection", 1);
  AddMacroDef("member", 2);
  AddMacroDef("multicolumn", 3);
  AddMacroDef("myheading", 1);

  AddMacroDef("newcounter", 1);
  AddMacroDef("newline", 0);
  AddMacroDef("newpage", 0);
  AddMacroDef("nocite", 1);
  AddMacroDef("noindent", 0);
  AddMacroDef("nolinebreak", 0);
  AddMacroDef("nopagebreak", 0);
  AddMacroDef("normalsize", 1);

  AddMacroDef("onecolumn", 0);
  AddMacroDef("oddsidemargin", 1);

  AddMacroDef("pagebreak", 0);
  AddMacroDef("pageref", 1);
  AddMacroDef("pagestyle", 1);
  AddMacroDef("pagenumbering", 1);
  AddMacroDef("paragraph*", 1);
  AddMacroDef("paragraph", 1);
  AddMacroDef("param", 2);
  AddMacroDef("parindent", 1);
  AddMacroDef("parskip", 1);
  AddMacroDef("part*", 1);
  AddMacroDef("part", 1);
  AddMacroDef("par", 0);
  AddMacroDef("pfunc", 3);
  AddMacroDef("picture", 1);
  AddMacroDef("printindex", 0);
  AddMacroDef("psboxto", 1);
  AddMacroDef("psbox", 1);

  AddMacroDef("quote", 1);
  AddMacroDef("quotation", 1);

  AddMacroDef("raggedbottom", 0);
  AddMacroDef("raggedleft", 0);
  AddMacroDef("raggedright", 0);
  AddMacroDef("ref", 1);
  AddMacroDef("rm", 1);
  AddMacroDef("roman", 1);
  AddMacroDef("Roman", 1);
//  AddMacroDef("row", 1);
  AddMacroDef("rtfsp", 0);
//  AddMacroDef("ruledrow", 1);

  AddMacroDef("sc", 1);
  AddMacroDef("section*", 1);
  AddMacroDef("section", 1);
  AddMacroDef("setcounter", 2);
  AddMacroDef("sf", 1);
  AddMacroDef("shortcite", 1);
  AddMacroDef("singlespace", 1);
  AddMacroDef("sloppypar", 1);
  AddMacroDef("sloppy", 0);
  AddMacroDef("sl", 1);
  AddMacroDef("small", 1);
  AddMacroDef("subitem", 0);
  AddMacroDef("subparagraph*", 1);
  AddMacroDef("subparagraph", 1);
  AddMacroDef("special", 1);
  AddMacroDef("subsection*", 1);
  AddMacroDef("subsection", 1);
  AddMacroDef("subsubsection*", 1);
  AddMacroDef("subsubsection", 1);

  AddMacroDef("tabbing", 2);
  AddMacroDef("tableofcontents", 0);
  AddMacroDef("table", 1);
  AddMacroDef("tabular", 2);
  AddMacroDef("tab", 0);
  AddMacroDef("TeX", 0);
  AddMacroDef("textwidth", 1);
  AddMacroDef("textheight", 1);
  AddMacroDef("thebibliography", 2);
  AddMacroDef("titlepage", 1);
  AddMacroDef("title", 1);
  AddMacroDef("tiny", 1);
  AddMacroDef("today", 0);
  AddMacroDef("topmargin", 1);
  AddMacroDef("topskip", 1);
  AddMacroDef("tt", 1);
  AddMacroDef("typein", 1);
  AddMacroDef("typeout", 1);
  AddMacroDef("twocolumn", 0);

  AddMacroDef("underline", 1);
  
  AddMacroDef("vdots", 0);
  AddMacroDef("verbatiminput", 1);
  AddMacroDef("verbatim", 1);
  AddMacroDef("verb", 0);
  AddMacroDef("verse", 1);
  AddMacroDef("vfill", 0);
  AddMacroDef("vline", 0);
  AddMacroDef("void", 0);
  AddMacroDef("vrule", 0);
  AddMacroDef("vspace*", 1);
  AddMacroDef("vskip*", 1);
  AddMacroDef("vspace", 1);
  AddMacroDef("vskip", 1);

  AddMacroDef("wxclips", 0);

  AddMacroDef(" ", 0);
  AddMacroDef("\\", 0);
  AddMacroDef("|", 0);
  AddMacroDef("/", 0);
  AddMacroDef("_", 0);
  AddMacroDef("&", 0);
  AddMacroDef("%", 0);
  AddMacroDef("$", 0);
  AddMacroDef("#", 0);
  AddMacroDef("(", 0);
  AddMacroDef(")", 0);
  AddMacroDef("{", 0);
  AddMacroDef("}", 0);
  AddMacroDef("=", 0);
  AddMacroDef(">", 0);
  AddMacroDef("<", 0);
  AddMacroDef("+", 0);
  AddMacroDef("-", 0);
  AddMacroDef("'", 0);
  AddMacroDef("`", 0);
}

/*
 * Default behaviour, should be called by client if can't match locally.
 *
 */
 
// Called on start/end of macro examination
void DefaultOnMacro(char *name, int no_args, Bool start)
{
  // Default behaviour for abstract: should really do better than this.
  if (strcmp(name, "abstract") == 0)
  {
    if (start)
    {
      OnMacro("flushleft", 1, TRUE);
      OnArgument("flushleft", 1, TRUE);
        OnMacro("bf", 1, TRUE);
        OnArgument("bf", 1, TRUE);
          OnMacro("LARGE", 1, TRUE);
          OnArgument("LARGE", 1, TRUE);
            TexOutput("Abstract", TRUE);
          OnArgument("LARGE", 1, FALSE);
          OnMacro("LARGE", 1, FALSE);
        OnArgument("bf", 1, FALSE);
        OnMacro("bf", 1, FALSE);
      OnArgument("flushleft", 1, FALSE);

      OnMacro("par", 0, TRUE);
      OnMacro("par", 0, TRUE);
      OnMacro("par", 0, TRUE);
      OnMacro("par", 0, FALSE);
      OnMacro("par", 0, FALSE);
      OnMacro("par", 0, FALSE);
    }
  }
  else if ((strcmp(name, "cinsert") == 0) && start)
    TexOutput("<<", TRUE);
  else if ((strcmp(name, "cextract") == 0) && start)
    TexOutput(">>", TRUE);
  else if ((strcmp(name, "destruct") == 0) && start)
    TexOutput("~", TRUE);
  else if ((strcmp(name, "~") == 0) && start)
    TexOutput("~", TRUE);
  else if ((strcmp(name, "_~") == 0) && start)
    TexOutput(" ", TRUE);
  else if ((strcmp(name, ",") == 0) && start)
    TexOutput(" ", TRUE);
  else if ((strcmp(name, ":") == 0) && start)
    TexOutput(" ", TRUE);
  else if ((strcmp(name, ";") == 0) && start)
    TexOutput(" ", TRUE);
  else if ((strcmp(name, "!") == 0) && start)
    TexOutput(" ", TRUE);
  else if ((strcmp(name, "_") == 0) && start)
    TexOutput("_", TRUE);
  else if ((strcmp(name, "#") == 0) && start)
    TexOutput("#", TRUE);
  else if ((strcmp(name, "&") == 0) && start)
    TexOutput("&", TRUE);
  else if ((strcmp(name, " ") == 0) && start)
    TexOutput(" ", TRUE);
  else if ((strcmp(name, "|") == 0) && start)
    TexOutput("|", TRUE);
  else if ((strcmp(name, "%") == 0) && start)
    TexOutput("%", TRUE);
  else if ((strcmp(name, "$") == 0) && start)
    TexOutput("$", TRUE);
  else if ((strcmp(name, "(") == 0) && start)
    TexOutput("", TRUE);
  else if ((strcmp(name, ")") == 0) && start)
    TexOutput("", TRUE);
  else if ((strcmp(name, "{") == 0) && start)
    TexOutput("{", TRUE);
  else if ((strcmp(name, "}") == 0) && start)
    TexOutput("}", TRUE);
  else if ((strcmp(name, "copyright") == 0) && start)
    TexOutput("(c)", TRUE);
  else if ((strcmp(name, "backslash") == 0) && start)
    TexOutput("\\", TRUE);
  else if (start && (strcmp(name, "ldots") == 0 || strcmp(name, "cdots") == 0))
    TexOutput("...", TRUE);
  else if (start && (strcmp(name, "vdots") == 0))
    TexOutput("|", TRUE);
  else if ((strcmp(name, "LaTeX") == 0) && start)
    TexOutput("LaTeX", TRUE);
  else if ((strcmp(name, "TeX") == 0) && start)
    TexOutput("TeX", TRUE);
  else if ((strcmp(name, "pounds") == 0) && start)
    TexOutput("£", TRUE);
  else if (strcmp(name, "sl") == 0)
  {
    OnMacro("it", no_args, start);
  }
  else if (strcmp(name, "today") == 0)
  {
#ifdef wx_x
    long when;
#endif
#ifdef wx_msw
    time_t when;
#endif
    (void) time(&when);
    TexOutput(ctime(&when), TRUE);
  }
  else if (strcmp(name, "noindent") == 0)
  {
    if (start)
      ParIndent = 0;
  }
  // Default way of writing the references title
  else if (strcmp(name, "references") == 0 && start)
  {
    OnMacro("flushleft", 1, TRUE);
    OnArgument("flushleft", 1, TRUE);
      OnMacro("LARGE", 1, TRUE);
      OnArgument("LARGE", 1, TRUE);
        OnMacro("bf", 1, TRUE);
        OnArgument("bf", 1, TRUE);
          TexOutput("References");
        OnArgument("bf", 1, FALSE);
        OnMacro("bf", 1, FALSE);
      OnArgument("LARGE", 1, FALSE);
      OnMacro("LARGE", 1, FALSE);
    OnArgument("flushleft", 1, FALSE);
    OnMacro("flushleft", 1, FALSE);

    OnMacro("par", 0, TRUE);
    OnMacro("par", 0, FALSE);
  }
}

// Called on start/end of argument examination
Bool DefaultOnArgument(char *macro_name, int arg_no, Bool start)
{
  if (strcmp(macro_name, "ref") == 0)
  {
    if (arg_no == 1 && start)
    {
      char *refName = GetArgData();
      if (refName)
      {
        wxNode *node = TexReferences.Find(refName);
        if (node)
        {
          TexRef *texRef = (TexRef *)node->Data();

          // Must strip the 'section' or 'chapter' or 'figure' text
          // from a normal 'ref' reference
          char buf[150];
          strcpy(buf, texRef->sectionNumber);
          int len = strlen(buf);
          int i = 0;
          if (strcmp(buf, "??") != 0)
          {
            while (i < len)
            {
              if (buf[i] == ' ')
              {
                i ++;
                break;
              }
              else i ++;
            }
          }
          TexOutput(texRef->sectionNumber + i, TRUE);
        }
        else
        {
          char buf[300];
          TexOutput("??", TRUE);
          sprintf(buf, "Warning: unresolved reference %s.", refName); 
          OnInform(buf);
        }
      }
      else TexOutput("??", TRUE);
      return FALSE;
    }
  }
  else if (strcmp(macro_name, "label") == 0)
  {
    if (start && (arg_no == 1))
    {
      if (CurrentLabel) delete CurrentLabel;
      CurrentLabel = copystring(GetArgData());
    }
    return FALSE;
  }
  else if (strcmp(macro_name, "author") == 0)
  {
    if (start && (arg_no == 1))
      DocumentAuthor = GetArgChunk();
    return FALSE;
  }
  else if (strcmp(macro_name, "date") == 0)
  {
    if (start && (arg_no == 1))
      DocumentDate = GetArgChunk();
    return FALSE;
  }
  else if (strcmp(macro_name, "title") == 0)
  {
    if (start && (arg_no == 1))
      DocumentTitle = GetArgChunk();
    return FALSE;
  }
  else if (strcmp(macro_name, "documentstyle") == 0)
  {
    if (start && !IsArgOptional())
    {
      DocumentStyleString = copystring(GetArgData());
      if (strncmp(DocumentStyleString, "art", 3) == 0)
        DocumentStyle = LATEX_ARTICLE;
      else if (strncmp(DocumentStyleString, "rep", 3) == 0)
        DocumentStyle = LATEX_REPORT;
      else if (strncmp(DocumentStyleString, "book", 4) == 0 ||
               strncmp(DocumentStyleString, "thesis", 6) == 0)
        DocumentStyle = LATEX_BOOK;
      else if (strncmp(DocumentStyleString, "letter", 6) == 0)
        DocumentStyle = LATEX_LETTER;
      else if (strncmp(DocumentStyleString, "slides", 6) == 0)
        DocumentStyle = LATEX_SLIDES;
        
      if (StringMatch("10", DocumentStyleString))
        SetFontSizes(10);
      else if (StringMatch("11", DocumentStyleString))
        SetFontSizes(11);
      else if (StringMatch("12", DocumentStyleString))
        SetFontSizes(12);

      OnMacro("helpfontsize", 1, TRUE);
      sprintf(currentArgData, "%d", normalFont);
      haveArgData = TRUE;
      OnArgument("helpfontsize", 1, TRUE);
      OnArgument("helpfontsize", 1, FALSE);
      haveArgData = FALSE;
      OnMacro("helpfontsize", 1, FALSE);
    }
    else if (start && IsArgOptional())
    {
      char *optionalStyle = copystring(GetArgData());

      if (StringMatch("10", optionalStyle))
        SetFontSizes(10);
      else if (StringMatch("11", optionalStyle))
        SetFontSizes(11);
      else if (StringMatch("12", optionalStyle))
        SetFontSizes(12);
    }
    return FALSE;
  }
  else if (strcmp(macro_name, "bibliographystyle") == 0)
  {
    if (start && !IsArgOptional())
      BibliographyStyleString = copystring(GetArgData());
    return FALSE;
  }
  else if (strcmp(macro_name, "cite") == 0 || strcmp(macro_name, "shortcite") == 0)
  {
    if (start && !IsArgOptional())
    {
      char *citeKey = GetArgData();
      AddCitation(citeKey);
      wxNode *node = TexReferences.Find(citeKey);
      if (node)
      {
        TexRef *ref = (TexRef *)node->Data();
        TexOutput(ref->sectionNumber, TRUE);
        if (strcmp(ref->sectionNumber, "??") == 0)
        {
          char buf[300];
          sprintf(buf, "Warning: unresolved citation %s.", citeKey);
          OnInform(buf);
        }
      }
      return FALSE;
    }
  }
  else if (strcmp(macro_name, "nocite") == 0)
  {
    if (start && !IsArgOptional())
    {
      char *citeKey = GetArgData();
      AddCitation(citeKey);
      return FALSE;
    }
  }
  else if (strcmp(macro_name, "helpfontsize") == 0)
  {
    if (start)
    {
      char *data = GetArgData();
      if (strcmp(data, "10") == 0)
        SetFontSizes(10);
      else if (strcmp(data, "11") == 0)
        SetFontSizes(11);
      else if (strcmp(data, "12") == 0)
        SetFontSizes(12);
      return FALSE;
    }
  }
  else if (strcmp(macro_name, "pageref") == 0)
  {
    if (start)
    {
      TexOutput(" ??", TRUE);
      return FALSE;
    }
  }
  else if (strcmp(macro_name, "parskip") == 0)
  {
    if (start && arg_no == 1)
    {
      char *data = GetArgData();
      ParSkip = ParseUnitArgument(data);
      return FALSE;
    }
  }
  else if (strcmp(macro_name, "parindent") == 0)
  {
    if (start && arg_no == 1)
    {
      char *data = GetArgData();
      ParIndent = ParseUnitArgument(data);
      return FALSE;
    }
  }
  else if (strcmp(macro_name, "sl") == 0)
  {
    return OnArgument("it", arg_no, start);
  }
  else if (strcmp(macro_name, "typeout") == 0)
  {
    if (start)
      OnInform(GetArgData());
  }
  else if (strcmp(macro_name, "footnote") == 0)
  {
    if (start)
      TexOutput(" (", TRUE);
    else
      TexOutput(")", TRUE);
  }
  else if (strcmp(macro_name, "bibliography") == 0)
  {
    if (start)
    {
      FILE *fd;
      char ch;
      char smallBuf[2];
      smallBuf[1] = 0;
      if (fd = fopen(TexBibName, "r"))
      {
        ch = getc(fd);
        smallBuf[0] = ch;
        while (ch != EOF)
        {
          TexOutput(smallBuf);
          ch = getc(fd);
          smallBuf[0] = ch;
        }
        fclose(fd);
      }
      else
      {
        OnInform("Run Tex2RTF again to include bibliography.");
      }

      // Read in the .bib file, resolve all known references, write out the RTF.
      char *bibFile = GetArgData();
      char fileBuf[300];
      strcpy(fileBuf, bibFile);
      char *actualFile = TexPathList.FindValidPath(fileBuf);
      if (!actualFile)
      {
        strcat(fileBuf, ".bib");
        actualFile = TexPathList.FindValidPath(fileBuf);
      }
      Bool success = FALSE;
      if (actualFile)
      {
        if (ReadBib(actualFile))
        {
          ResolveBibReferences();
          success = TRUE;
        }
      }
      if (success)
      {
        // Write it a new bib section in the appropriate format.
        FILE *save1 = CurrentOutput1;
        FILE *save2 = CurrentOutput2;
        FILE *Biblio = fopen(TexTmpBibName, "w");
        SetCurrentOutput(Biblio);
        OutputBib();
        fclose(Biblio);
        if (FileExists(TexTmpBibName))
        {
          if (FileExists(TexBibName)) wxRemoveFile(TexBibName);
          wxRenameFile(TexTmpBibName, TexBibName);
        }
        SetCurrentOutputs(save1, save2);
      }
      else
        OnError(".bib file not found or malformed.");
      return FALSE;
    }
  }
  else if (strcmp(macro_name, "multicolumn") == 0)
  {
    if (start && (arg_no == 3))
      return TRUE;
    else
      return FALSE;
  }
  else if (strcmp(macro_name, "pagestyle") == 0 ||
           strcmp(macro_name, "cline") == 0 ||
           strcmp(macro_name, "arabic") == 0 ||
           strcmp(macro_name, "alph") == 0 ||
           strcmp(macro_name, "Alph") == 0 ||
           strcmp(macro_name, "roman") == 0 ||
           strcmp(macro_name, "Roman") == 0 ||
           strcmp(macro_name, "setcounter") == 0 ||
           strcmp(macro_name, "addtocounter") == 0 ||
           strcmp(macro_name, "addcontentsline") == 0 ||
           strcmp(macro_name, "newcounter") == 0 ||
           strcmp(macro_name, "textwidth") == 0 ||
           strcmp(macro_name, "textheight") == 0 ||
           strcmp(macro_name, "baselineskip") == 0 ||
           strcmp(macro_name, "vspace*") == 0 ||
           strcmp(macro_name, "hspace*") == 0 ||
           strcmp(macro_name, "vspace") == 0 ||
           strcmp(macro_name, "hspace") == 0 ||
           strcmp(macro_name, "vskip*") == 0 ||
           strcmp(macro_name, "hskip*") == 0 ||
           strcmp(macro_name, "vskip") == 0 ||
           strcmp(macro_name, "hskip") == 0 ||
           strcmp(macro_name, "pagenumbering") == 0)
    return FALSE;
  return TRUE;
}

