/*
 *   T E X 2 A S C . C   ---   Rudimentary TeX-to-ASCII converter
 *                             for project Libellus texts.
 */

/*
 *   Copyright (C) 1993 Konrad Schroder.
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 1, or (at your option)
 *   any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>

#ifdef POSIX
# include <unistd.h>
#endif

#include "g_getopt.h"
#include "tex2asc.h"
#include "rd.h"
#include "flags.h"

char *begin_latin,     *end_latin;
char *begin_italics,   *end_italics;
char *begin_bold,      *end_bold;
char *begin_underline, *end_underline;
char *begin_greek,     *end_greek;
char *begin_document,  *end_document;
char *begin_teletype,  *end_teletype;
char *parindent, *end_par, *everypar=NULL;
char italic_space, bold_space, underline_space;
char infile[ML];

struct Option option;
int input_line=2;
int peek_ahead_char='\0';
char footfile[ML];
int good_bye=0;
int footnote_c=0;
int page_width=75;
FILE *ip, *op;
extern struct hash hash_table[];
char workfile[ML];

void k_zero(struct k_str *s)
{
     s->len=0;
     s->str = malloc(GOOD_MAL);
     memset(s->str,'\0',GOOD_MAL-1);
}

void output_line(unsigned char *t, FILE *fp, int termin)
{
     int j,k;
     char u[ML];

     /*
      * Format the line number, if one is present
      */

     if(strchr(t,NUMBER))
     {
          *(strchr(t,NUMBER))='\0';
          k=strlen(t);
          j = atoi(t+k+1);

          if(is_html())
          {
              if(strncasecmp(t,"<ol>",4))
                  fputs("<LI>",fp);
          }
          else if(option.apply_codes != rtf_codes)
          {
               if(k<58)
               {
                    sprintf(u,"%-60.60s %d",t,j);
                    strcpy(t,u);
               }
               else
               {
                    if(k>71)
                         t[72]='\0';
                    sprintf(u,"%-71.71s %d",t,j);
                    strcpy(t,u);
               }
          }
          else /* RTF */
          {
               sprintf(u,"%s {\\qr %d}",t,j);
               strcpy(t,u);
          }
     }

     /*
      * Actually output the string
      */

     if(!(is_html() && strcmp(t,"\003<li>")==0)) /* NOINDENT "li" */
         for(j=k=0;t[j]!='\0';j++,k++)
             switch(t[j])
             {
               case NOPRINT: case NOINDENT:
                 break;
               case RSQ:
                 fputc('\'',fp);
               case TIE:
/*
                 if(option.apply_codes==html_plus_codes)
                     fputs("<nbsp>",fp);
                 else
*/                    if(option.apply_codes==rtf_codes)
                     fputs("\\~",fp);
                 else
                     fputc(' ',fp);
                 break;
               default:
                 if(option.apply_codes==rtf_codes
                    && (unsigned char)t[j] > 0x7f)
                     fprintf(fp,"\\\'%2.2x",(unsigned char)t[j]);
                 else if(is_html() && t[j]==' ' && k > 70)
                 {
                     fputc('\n',fp);
                     k=0;
                 }
                 else
                     fputc(t[j],fp);
                 break;
             }

     if(termin)
          fputs(end_par,fp);
     else
          fputc(' ',fp);

     /* fflush(fp); */
     return;
}

void output(unsigned char *s, FILE *fp)
{
     unsigned char *u, t[ML];
     int i, pin=4;

     u=s;

     for(i=0;s[i]!='\0';i++)
          if(s[i]==DISC_NL)
               s[i]=SP_PAR;

     for(;;)
     {
          memset(t,'\0',ML);

          for(i=0; u[i]!='\n' && u[i]!= SP_PAR && u[i]!='\0'
                   && i<page_width-pin; i++)
          {
               t[i] = u[i];
          }
          if(u[i]!='\n' && u[i]!= SP_PAR && u[i]!='\0')
          {
               if(strchr(t,' ')==NULL)
               {
                    if(t[0]!=NOINDENT)
                         fputs(parindent,fp);
                    output_line(t,fp,!option.few_cr);
                    u += i+1;
                    pin=0;
                    continue;
               }
               else
               {
                    while(t[i]==' ' && i>0) /* trim trailing spaces */
                         t[i--]='\0';
                         
                    while(t[i]!=' ' && i>0)
                         i--;
                    t[i]='\0';
                    if(pin && t[0]!=NOINDENT)
                         fputs(parindent,fp);
                    pin=0;
                    output_line(t,fp,!option.few_cr);
               }
          }
          else
          {
               if(t[0]!=NOINDENT && pin &&
                  (!option.gutenberg || u[i]!=SP_PAR))
                    fputs(parindent,fp);
               pin = 4;  /* we don't care about the value, really */
               output_line(t,fp,1);
          }
          if(u[i]=='\0')
               break;
          u += i+1;
     }
     /* fflush(fp); */
     return;
}

void resize(struct k_str *s)
{
     s->len = strlen(s->str);
     s->str = realloc(s->str,((s->len+1)/GOOD_MAL+1)*GOOD_MAL);
     return;
}

void output_paragraph(struct k_str *s,  FILE *fp)
{
     int i, j;

     for(i=0;i<s->len;i++)
          if(s->str[i]==DISC_NL)
               s->str[i]=SP_PAR;

     for(i=0;s->str[i]=='\n' || s->str[i]==SP_PAR;i++)
          ;

     if(!strchr(s->str+i,'\n') && !strchr(s->str+i,SP_PAR))
     {
          for(j=0;s->str[i+j]!='\0';j++)
               s->str[j] = s->str[i+j];
          s->str[j]='\0';
          resize(s);
          return;
     }
     for(;s->str[i]!='\0' && s->str[i]!=SP_PAR && s->str[i]!='\n';i++)
          ;

     s->str[i]='\0';
     output(s->str, fp);

     if(i<s->len)
          strcpy(s->str,s->str+i+1);
     else
          s->str[0]='\0';

     resize(s);
     return;
}

char greek_accent(char c, char accent_char)
{
     /*
      * As with normal accents, Greek accents are looked up in a table.
      * The Accent character is one of
      *      ' ` ~ > < >' <' >` <` >~ <~ "' "` "
      * and the Base character is one of
      *      '`~aehiouwr
      * The reason that '`~ are both accents and bases is simply because
      * Greek characters can have both an accent and a breathing at once.
      *
      * If the target is something other than GreekKeys or WinGreek, this
      * function should never be invoked.  SuperGreek can't handle accented
      * characters, and ASCII can't do Greek at all.  (I won't even mention
      * BETA code....)
      */

     char *GK_accents="\'`~><\205\206\207\210\211\212";
     char *WG_accents="\'`~><\224\223\226\225\222\221";
     char *bases="\'`~aehiouwr";
     char GK_accent_table[11][11] =
     {
          { '*', '*', '*', 133, 134, '*', '*', '*', '*', '*', '*' },
          { '*', '*', '*', 135, 136, '*', '*', '*', '*', '*', '*' },
          { '*', '*', '*', 137, 138, '*', '*', '*', '*', '*', '*' },
          { 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149 },
          { 161, 162, 'e', 164, 165, 166, 167, 168, 169, 'e', 'e' },
          { 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184 },
          { 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229 },
          { 241, 242, 'o', 244, 245, 246, 247, 248, 249, 'o', 'o' },
          { 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240 },
          { 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207 },
          { 'r', 'r', 'r', 'r', 61,  'r', 'r', 'r', 'r', 'r', 'r' }
     };
     char WG_accent_table[11][11] = {
          { '*', '*', '*', 0x94, 0x93, '*', '*', '*', '*', '*', '*' },
          { '*', '*', '*', 0x96, 0x95, '*', '*', '*', '*', '*', '*' },
          { '*', '*', '*', 0x92, 0x91, '*', '*', '*', '*', '*', '*' },
          { 163, 166, 169, 162, 161, 165, 164, 168, 167, 171, 170 },
          { 154, 157, 'e', 153, 152, 156, 155, 159, 158, 'e', 'e' },
          { 187, 190, 193, 186, 185, 189, 188, 192, 191, 195, 194 },
          { 133, 136, 139, 132, 131, 135, 134, 138, 137, 141, 140 },
          { 210, 213, 'o', 209, 208, 212, 211, 215, 214, 'o', 'o' },
          { 218, 221, 224, 217, 216, 220, 219, 223, 222, 226, 225 },
          { 232, 235, 238, 231, 230, 234, 233, 237, 236, 240, 239 },
          { '*', '*', '*', 184, 183, '*', '*', '*', '*', '*', '*' },
     };
     char *u, *v;

     if((v=strchr(bases,c))==NULL)
          return c;

     if(option.greek == greek_keys)
     {
          if((u=strchr(GK_accents,accent_char))==NULL)
               return c;
          return GK_accent_table[v-bases][u-GK_accents];
     }
     else if(option.greek == win_greek)
     {
          if((u=strchr(WG_accents,accent_char))==NULL)
               return c;
          return WG_accent_table[v-bases][u-WG_accents];
     }
     else return c;
}

char iota_subscript(char c)
{
     if(option.greek == greek_keys)
     {
          if(c=='a')
               return 38;
          else if(c=='h')
               return 250;
          else if(c=='w')
               return 196;

          return c+11;
     }
     else if(option.greek == win_greek)
     {
          if(c=='a')
               return 0x76;
          else if(c=='h')
               return 0x56;
          else if(c=='w')
               return 0x4A;

          return c+11;
     }
     /* NOT REACHED */
     return 0;
}

char *accent(char c, char accent_char)
{
     /*
      * The following tables take accents from the string '`^"~ and apply them
      * to the string "AEIOUaeiouyNnCc".  The values given are for American
      * machines; if your machine is using a different character set, you may
      * have to modify the table(s).  (And if you do, please send me a copy!)
      */

     char accent_set[] = "\'`^\"~=";
     char letters[]    = "AEIOUaeiouyNnCc";
     char *xterm[7][16] = {
          {"\xC1", "\xC9","\xCD","\xD3","\xDA","\xE1","\xE9","\xED","\xF3","\xFA","\xFD",
                "N","n","C","c"},
          {"\xC0","\xC8","\xCC","\xD2","\xD9","\xE0","\xE8","\xEC","\xF2","\xF9","y",
                "N","n","C","c"},
          {"\xC2","\xCA","\xCE","\xD4","\xDB","\xE2","\xEA","\xEE","\xF4","\xFB","y",
                "N","n","C","c"},
          {"\xC4","\xCB","\xCF","\xD6","\xDC","\xE4","\xEB","\xEF","\xF6","\xFC","\xFF",
                "N","n","C","c"},
          {"\xC3","E","I","\xD5","U","\xE3","e","i","\xF5","u","y",
           "\xD1", "\xF1", "C", "c"},
          {"A","E","I","O","U","a","e","i","o","u","y","N","n","\xC7","\xE7"},
          {"A","E","I","O","U","a","e","i","o","u","y","N","n","C","c"},
     };
     char *mac[7][16] = {
          {"A","\x83","I","O","U","\x87","\x8e","\x92","\x97","\x9c","y","N","n","C","c"},
          {"\xCB","E","I","O","U","\x88","\x8f","\x93","\x98","\x9d","y","N","n","C","c"},
          {"A","E","I","O","U","\x89","\x90","\x94","\x99","\x9E","y","N","n","C","c"},
          {"\x80","E","I","\x85","\x86",
                "\x8a","\x91","\x95","\x9a","\x9f","\xD8","N","n","C","c"},
          {"\xCC","E","I","O","U","\x8b","\x92","\x96","\x9b","u","y","\x84","\x96","C","c"},
          {"A","E","I","O","U","a","e","i","o","u","y","N","n","\x82","\x8D"},
          {"A","E","I","O","U","a","e","i","o","u","y","N","n","C","c"},
     };
     char *ibm[7][16] = {
          {"A","\x90","I","O","U","\xA0","\x82","\xA1","\xA2","\xA3","y","N","n","C","c"},
          {"A","E","I","O","U","\x85","\x8a","\x8D","\x95","\x97","y","N","n","C","c"},
          {"A","E","I","O","U","\x83","\x88","\x8c","\x93","\x96","y","N","n","C","c"},
          {"\x8E","E","I","\x99","\x9A",
                "\x84","\x89","\x8b","\x94","\x81","\x98","N","n","C","c"},
          {"A","E","I","O","U","a","e","i","o","u","y","\xA5","\xA4","C","c"},
          {"A","E","I","O","U","a","e","i","o","u","y","N","n","\x80","\x87"},
          {"A","E","I","O","U","a","e","i","o","u","y","N","n","C","c"},
     };
     char *html[7][16] = {
	/* ' */ {"&Aacute;", "&Eacute;", "&Iacute;", "&Oacute;", "&Uacute;",
		 "&aacute;", "&eacute;", "&iacute;", "&oacute;", "&uacute;",
		 "&yacute;", "N", "n", "C", "c"},
	/* ` */ {"&Agrave;", "&Egrave;", "&Igrave;", "&Ograve;", "&Ugrave;",
		 "&agrave;", "&egrave;", "&igrave;", "&ograve;", "&ugrave;",
		 "y", "N", "n", "C", "c"},
	/* ^ */ {"&Acirc;", "&Ecirc;", "&Icirc;", "&Ocirc;", "&Ucirc;",
		 "&acirc;", "&ecirc;", "&icirc;", "&ocirc;", "&ucirc;",
		 "y", "N", "n", "C", "c"},
	/* " */ {"&Auml;", "&Euml;", "&Iuml;", "&Ouml;", "&Uuml;",
		 "&auml;", "&euml;", "&iuml;", "&ouml;", "&uuml;",
		 "y", "N", "n", "C", "c"},
	/* ~ */ {"&Atilde;", "&Etilde;", "&Itilde;", "&Otilde;", "&Utilde;",
		 "&atilde;", "&egrave;", "&igrave;", "&ograve;", "&ugrave;",
		 "y", "&Ntilde;", "&ntilde;", "C", "c"},
	/* = */ {"&Aacute;", "&Eacute;", "&Iacute;", "&Oacute;", "&Uacute;",
		 "&aacute;", "&eacute;", "&iacute;", "&oacute;", "&uacute;",
		 "&yacute;", "N", "n", "C", "c"},
     };
     char *t, *s;


     if(option.character_set==0)
          return NULL;
     if((t=strchr(accent_set,accent_char))==NULL)
          return NULL;

     if((s=strchr(letters,c))==NULL)
          return NULL;

     if(option.character_set == html_chars)
          return html[t-accent_set][s-letters];
     else if(option.character_set==ibm_pc_chars)
          return ibm[t-accent_set][s-letters];
     else if(option.character_set==mac_chars)
          return mac[t-accent_set][s-letters];
     else
          return xterm[t-accent_set][s-letters];
}

char *chapter(MODE *mode)
{
     static char s[ML];

     if(is_html())
     {
         mode->chapcount++;
         sprintf(s,"<b>[<a name=%d>%d</a>]</b>%c",
                 mode->chapcount,mode->chapcount, TIE);
     }
     else
         sprintf(s,"[-%d-]%c", ++mode->chapcount, TIE);

     mode->seccount=1;
     if(option.number_sec)
          return s;
     else
          return "";
}

char *section(MODE *mode)
{
     static char s[ML];

     if(is_html())
     {
         mode->seccount++;
         sprintf(s,"[<a name=%d.%d>%d</a>]%c",
                 mode->chapcount, mode->seccount, mode->seccount, TIE);
     }
     else
         sprintf(s,"[%d]%c", ++mode->seccount, TIE);
     if(option.number_sec)
          return s;
     else
          return "";
}

char *independent_section(MODE *mode)
{
     static char s[ML];

     if(is_html())
     {
         mode->iseccount++;
         sprintf(s,"[<a name=i%d>%d</a>]%c",
                 mode->iseccount, mode->iseccount, TIE);
     }
     else
         sprintf(s,"[%d]%c", ++mode->iseccount, TIE);
     if(option.number_sec)
          return s;
     else
          return "";
}

void add_str(struct k_str *s, unsigned char *t)
{
     int j;

     j = strlen(t);

     if( (s->len+1)/GOOD_MAL != (s->len+j+1)/GOOD_MAL )
          s->str = (char *)realloc(s->str,
                                   ((s->len+j+1)/GOOD_MAL+1)*GOOD_MAL);
     if(t[0]==DISC_NL)
     {
          if(s->str[s->len-1]=='\n' || s->str[s->len-1]==SP_PAR ||
	     s->str[s->len-1]==NOPRINT)
          {
               strcat(s->str,t+1);
               s->len += (j-1);
               return;
          }
     }
     strcat(s->str,t);
     s->len += j;

     return;
}

void cleanup(MODE mode, MODE oldmode, struct k_str *s)
{
     if(!(mode.font & UNDERLINE) && (oldmode.font & UNDERLINE))
          add_str(s,end_underline);
     if(!(mode.font & TELETYPE) && (oldmode.font & TELETYPE))
          add_str(s,end_teletype);
     if(!(mode.font & LATIN) && (oldmode.font & LATIN))
          add_str(s,end_latin);
     if(!(mode.font & ITALICS) && (oldmode.font & ITALICS))
          add_str(s,end_italics);
     if(!(mode.font & GREEK) && (oldmode.font & GREEK))
          add_str(s,end_greek);
     if(!(mode.font & BOLD) && (oldmode.font & BOLD))
          add_str(s,end_bold);
     if((mode.font & BOLD) && !(oldmode.font & BOLD))
          add_str(s,begin_bold);
     if((mode.font & GREEK) && !(oldmode.font & GREEK))
          add_str(s,begin_greek);
     if((mode.font & ITALICS) && !(oldmode.font & ITALICS))
          add_str(s,begin_italics);
     if((mode.font & LATIN) && !(oldmode.font & LATIN))
          add_str(s,begin_latin);
     if((mode.font & TELETYPE) && !(oldmode.font & TELETYPE))
          add_str(s,begin_teletype);
     if((mode.font & UNDERLINE) && !(oldmode.font & UNDERLINE))
          add_str(s,begin_underline);
     if(is_html() && mode.countlines && !(oldmode.countlines))
         add_str(s,"</OL>");
     return;
}

int get_next_char(void)
{
     int ret_val;

     if(peek_ahead_char < 0)
          return EOF;
     else if(peek_ahead_char=='\0')
          peek_ahead_char=fgetc(ip);

     ret_val=peek_ahead_char;
     peek_ahead_char=fgetc(ip);

     if(ret_val=='\n')
          input_line++;

     return ret_val;
}

void add_char(struct k_str *s, char c)
{
     if( (s->len+1)/GOOD_MAL != (s->len+2)/GOOD_MAL )
          s->str = realloc(s->str,((s->len+2)/GOOD_MAL+1)*GOOD_MAL);
     s->str[s->len]=c;
     s->str[s->len+1]='\0';
     s->len++;

     return;
}
     
void c_print(char *s)
{
     while(*s!='\0')
     {
          if(isprint(*s) && !isspace(*s))
               putchar(*s);
          else
               printf("<%2.2x>",*s);
          s++;
     }
     return;
}

unsigned char *read_frame(MODE *mode, int level, char term_char)
{
     int was_a_space=0, was_a_nl=0;
     int c;
     struct k_str s;
     unsigned char *t, temp[ML];
     struct direc_and_last *d_l;
     MODE oldmode = *mode, thismode = *mode;
     char *cp;

     struct direc_and_last *read_directive(MODE *, int);

     k_zero(&s);

     while((c=get_next_char())!='\0')
     {
          if(c==term_char)
               c='}';      /* for things like \beginsection */
        top:
          if(!isspace(c))
          {
               was_a_space=0;
               was_a_nl = 0;
          }
          if((thismode.font & GREEK) && isalpha(c))
          {
               if(option.greek==greek_keys)
               {
                    /*
                     * About 10 characters have to be transliterated between
                     * TeX greek and GreekKeys greek.  Also, take care of
                     * the conventional difference between medial and
                     * terminal lowercase sigma.
                     */
                    switch(c)
                    {
                       case 'J':
                         c='Y';
                         break;
                       case 'X':
                         c='J';
                         break;
                       case 'Q':
                         c='X';
                         break;
                       case 'Y':
                         c='C';
                         break;
                       case 'W':
                         c='V';
                         break;
                       case 'j':
                         c='y';
                         break;
                       case 'x':
                         c='j';
                         break;
                       case 'q':
                         c='x';
                         break;
                       case 'y':
                         c='c';
                         break;
                       case 'w':
                         c='v';
                         break;
                       case 's':
                         if(isspace(peek_ahead_char) || peek_ahead_char=='\0')
                              c=119;
                         break;
                    }
               }
               if(option.greek==win_greek)
               {
                    /*
                     * Same story with WinGreek, but the font is a
                     * closer match with TeX.
                     */
                    switch(c)
                    {
                       case 'Q':
                         c='C';
                         break;
                       case 'J':
                         c='Q';
                         break;
                       case 'q':
                         c='c';
                         break;
                       case 'j':
                         c='q';
                         break;
                       case ';':
                         c=':';
                         break;
                       case '?':
                         c=';';
                         break;
                       case 's':
                         if(isspace(peek_ahead_char))
                              c=0x6A;
                         break;
                    }
               }
          }

          switch(c)
          {
             case EOF:
               goto bottom;
             case '\n':
               if(was_a_nl)
               {
                    add_char(&s,'\n');
                    if(everypar)
                        add_str(&s,everypar);
                    was_a_nl = was_a_space = 1;
                    while( level==0 && 
                          (strchr(s.str,'\n') || strchr(s.str,SP_PAR)) )
                    {
                         output_paragraph(&s,op);
                    }
                    while(isspace(peek_ahead_char))
                         get_next_char();
                    break;
               }
               if(thismode.obeylines)
               {
                   if(is_html() && thismode.countlines)
                   {
                       add_str(&s,"<a name=");
                       sprintf(temp,"%d",thismode.linecount);
                       add_str(&s,temp);
                       add_str(&s,"> </a>");
                       add_char(&s,NUMBER);
                   }
                   else if(option.number_lines && thismode.countlines &&
                           (thismode.linecount % 5 == 0))
                   {
                       add_char(&s,NUMBER);
                       sprintf(temp,"%d",thismode.linecount);
                       add_str(&s,temp);
                   }
                   thismode.linecount++;
                   add_char(&s,SP_PAR);
                   if(is_html())
                       add_char(&s,NOINDENT);

                   while( level==0 && 
                         (strchr(s.str,'\n') || strchr(s.str,SP_PAR)) )
                   {
                       output_paragraph(&s,op);
                   }
                   was_a_nl = was_a_space = 1;
                   break;
               }
               else
                    was_a_nl=1;
               /* FALLS THROUGH */
             case ' ': case '\t':
#if defined MAC
               MainEvent();
#endif
               if(was_a_space)
                    break;
               else
               {
                    /* Special treatment of spaces for ASCII emphasis */

                    if(option.apply_codes==ascii_codes)
                    {
                         if(thismode.font & UNDERLINE)
                              add_char(&s,underline_space);
                         else if(thismode.font & ITALICS)
                              add_char(&s,italic_space);
                         else if(thismode.font & BOLD)
                              add_char(&s,bold_space);
                         else
                              add_char(&s,' ');
                    }
                    else
                         add_char(&s,' ');
               }
               was_a_space=1;
               break;
             case '-':
               if(peek_ahead_char=='-')
               {
                   get_next_char();
                   if(peek_ahead_char=='-')
                   {
                       get_next_char();
                       /* --- */
                       add_str(&s,hash_table[S_emdash].replace_word);
                   }
                   else
                       add_str(&s,hash_table[S_endash].replace_word);
               }
               else
                   add_char(&s,'-');
               break;
             case '\\':
               d_l=read_directive(&thismode, level);
               if(good_bye)
               {
                    if(level>1)
                    {
                         /* error of some kind */
                    }
                    cleanup(oldmode,thismode,&s);
                    return s.str;
               }
               t=d_l->direc.str;
               c=d_l->c;

               if(t[0]!='\0')
               {
                    add_str(&s,t);
               }
               free(d_l->direc.str);
               free(d_l);
               if(c!='\0')
                    goto top;
               break;
             case '{':
               t = read_frame(&thismode,level+1,'}');
               if(t[0]!='\0')
                    add_str(&s,t);
               free(t);
               if(good_bye)
               {
                    cleanup(oldmode,thismode,&s);
                    return s.str;
               }
               break;
             case '}':
               if(level==0)
               {
                    sprintf(temp,"tex2asc: %s: Too many }s in line %d\n",
                            workfile, input_line);
#ifdef MAC
                    MacDisplayWarning(temp);
#else
                    fprintf(stderr,temp);
#endif
               }
               cleanup(oldmode, thismode, &s);
               return s.str;
             case '&':
               add_char(&s,'\t');
               break;
             case '$':
               thismode.math_mode = !thismode.math_mode;
               break;
             case '#':
               add_char(&s,TEX_HASH);
               break;
             case '^': case '_':
               break; /* IGNORE super- and sub-script */
             case '%':
               while((c=get_next_char())!='\n' && c!=EOF)
                    ;
               break;
             case '=': case '+':
               if(thismode.font & LATIN) {
                   cp = accent(peek_ahead_char, c);
                   if (cp) {
                       add_str(&s,cp);
                       get_next_char();
                   }
               } else
                   add_char(&s,c);
               break;
             case '<': case '>': case '\'': case '`': case '~':
               if(thismode.font & GREEK)
               {
                    if(c=='\'' && peek_ahead_char=='\'')
                    {
                         c=RSQ;
                         add_char(&s,c);
                         get_next_char();
                    }
                    else
                    {
                         if(strchr("\'`~",peek_ahead_char))
                         {
                              c = greek_accent(get_next_char(),c);
                         }
                         if(strchr("aehiouw",peek_ahead_char))
                         {
                              c = greek_accent(get_next_char(),c);
                              if(peek_ahead_char=='|')
                              {
                                   c = iota_subscript(c);
                                   get_next_char();  /* use up the '|' */
                              }
                         }
                    }
               }
               else if(c=='~')
                    c=TIE;
               add_char(&s,c);
               break;
             case 'a': case 'e': case 'i': case 'o': case 'u': case 'h':
             case 'w':
               if(thismode.font & GREEK)
               {
                    if(peek_ahead_char=='|')
                    {
                         c=iota_subscript(c);
                         get_next_char(); /* use up the '|' */
                    }
               }
               add_char(&s,c);
               break;
             case ':': case '.': case '?':
               if(isspace(peek_ahead_char))
               {
                    add_char(&s,c);
                    if(option.apply_codes==ascii_codes)
                    {
                         if(thismode.font & UNDERLINE)
                              add_char(&s,underline_space);
                         else if(thismode.font & ITALICS)
                              add_char(&s,italic_space);
                         else if(thismode.font & BOLD)
                              add_char(&s,bold_space);
                         else
                              add_char(&s,' ');
                    }
                    else
                         add_char(&s,' ');
                    break;
               }
               /* FALLS THROUGH */
             default:
               if((thismode.font & GREEK) && option.greek==greek_keys)
               {
                    if(peek_ahead_char=='|')
                    {
                         c=iota_subscript(c);
                         get_next_char();         /* use up the '|' */
                    }
               }
               add_char(&s,c);
               break;
          }
     }
   bottom:
     if(level==0)
     {
          if(thismode.font & GREEK)
               add_str(&s, end_greek);
          if(thismode.font & ITALICS)
               add_str(&s, end_italics);
          if(thismode.font & BOLD)
               add_str(&s, end_bold);
          if(thismode.font & UNDERLINE)
               add_str(&s, end_underline);
     }
     return s.str;
}

void usage(void)
{
     puts("usage: tex2asc [-acRrSs] [-f file]");
     puts("               [--strip-controls] [--ascii-controls]");
     puts("               [--ansi-controls] [--no-accents] [--ibm-set]");
     puts("               [--mac-set] [--ps2-set] [--xterm-set]");
     puts("               [--strip-notes] [--leave-notes] [--end-notes]");
     puts("               [--file-notes] [--number-lines[=0]]");
     puts("               [--number-sections[=0]]");
     return;
}

#ifdef MAC

int main(int argc, char **argv)
{
     MacStartupStuff();
     option.gutenberg=0;
     option.apply_codes=0;
     option.footnote=3;
     option.number_lines=1;
     option.character_set=2;
     option.few_cr=1;
     option.number_sec=1;
     /* while(MainEvent())
           ;*/
     strcpy(infile, "holmes1.tex");
     do_file(infile);
     exit(0);
}

#else

int main(int argc, char **argv)
{
     envarg(&argc, &argv, "TEX2ASC");   /* read TEX2ASC, then flags */
     gethandopt(argc, argv, infile);
     do_file(infile);
     return EXIT_SUCCESS;
}

#endif


int do_file(char *infile)
{
     char outfile[ML], s[ML], *t;
     MODE mode;
     int vers, subvers; /* not using a float for a reason... */
     FILE *fp;
#ifdef Mac
     Rect r;
#endif

     mode.margin=75;
     mode.countlines=0;
     mode.obeylines=0;
     mode.chapcount=0;
     mode.iseccount=0;
     mode.seccount=0;
     mode.font=0;
     mode.underline=0;
     mode.foot_mode=0;
     mode.margin=75;
     mode.leftmargin=0;
     footnote_c=0;

     init_hash();
     init_chars();
     init_codes();

     if(option.footnote==end_notes)
     {
          tmpnam(footfile);
     }
     if(infile[0]!='\0')
     {
          strcpy(workfile,infile);
          if((ip=fopen(infile,"r"))==NULL)
          {
               fprintf(stderr,"tex2asc: cannot open input file: %s\n",infile);
               exit(3);
          }
          strcpy(outfile,infile);

          t=(char *)strrchr(outfile,'.');
          if(t!=NULL)
               *t='\0';
               
          if(option.footnote==end_notes)
          {
               if(outfile[0]=='\0')
                    strcpy(footfile,"footnote.asc");
               else
                    sprintf(footfile,"%s.ftn",outfile);
          }
          strcat(outfile,(option.apply_codes==rtf_codes?".rtf":
                          (is_html()?".html" : ".asc")));

#ifndef MAC /* The Macintosh version will always overwrite */
          if(access(outfile,0)==0)
          {
               printf("The file %s already exists.\nShould I overwrite it? ",
                      outfile);
               if(getchar()!='y')
               {
                    printf("OK.\n");
                    exit(0);
               }
          }
#endif
          printf("Placing output into the file %s.\n",outfile);
          if(option.gutenberg)
               op = fopen(outfile,"wb");
          else
               op = fopen(outfile,"w");
          if(op==NULL)
          {
               fprintf(stderr,"tex2asc: cannot open output file: %s\n",outfile);
               exit(3);
          }
     }
     else
     {
          strcpy(workfile,"stdin");
          ip=stdin;
          op=stdout;
     }
     if(option.footnote>1)
     {
          if(footfile[0]=='\0')
               strcpy(footfile,"footnote.asc");
          fp = fopen(footfile,"w");
          fprintf(fp,"\n\nFootnotes:\n\n");
          fclose(fp);
     }

     fgets(s,ML,ip);
     if(sscanf(s,"%% tex2asc-version: %d.%d",&vers,&subvers)<=0)
          fprintf(stderr,"tex2asc: warning: %s missing version marker\n",infile);
     else if(vers > VERSION || (vers==VERSION && subvers>SUBVERSION))
     {
          fprintf(stderr,"tex2asc: warning: %s expects a newer version\
 of tex2asc\n",infile);
          printf("tex2asc: warning: %s expects a newer version\
 of tex2asc\n",infile);
     }
     if(infile[0])
         rewind(ip);
     fputs(begin_document,op);
     output(read_frame(&mode,0,'}'),op);
     fclose(ip);
     if(option.footnote==end_notes && footnote_c>0)
     {
          ip = fopen(footfile,"r");
          while(fgets(s,ML,ip)!=NULL)
               fputs(s,op);
          fclose(ip);
          unlink(footfile);
     }
     fputs(end_document,op);
     fclose(op);

     return 0;
}
