// -*- Mode: C++ -*-
// Dieses Programm durchsucht ein LaTeX-Dokument nach fuchischen
// Textteilen und formatiert diese anhand seines Wissens über
// Ligaturen.  Es wird eine neue LaTeX-Datei ausgegeben.

#include "fuchlig.h"

#if 1

const char *Lstr (const longchar *c)
{
  static char buff[20000];
  assert (lstrlen (c) < 20000);
  trystrcpy (buff, c);
  return buff;
}

const char *Lstr2 (const longchar *c)
{
  static char buff[20000];
  assert (lstrlen (c) < 20000);
  trystrcpy (buff, c);
  return buff;
}

bool isvalidlongchar (longchar c)
{
  return (c>=0) && (c<MAXLONGCHAR);
  // dann darf man damit fuchchar indizieren
}

void quit()
{
  if (f) fclose (f);
  if (cdebug) delete cdebug;
  if (fout) {
    fclose (fout);
    unlink (outfilename);
  }
  exit (1);
}                   

void Error (const char *err=errtxt)
{
  fprintf (stderr, "%s:%d: %s\n", fuchlettername, lline, err);
  quit();
}

void error (const char *err=errtxt)
{
  fprintf (stderr, "%s:%d:%d: %s\n", mainfilename, ly, lx-1, err);
  quit();
}

void Interror (const char *err=errtxt)
{
  fprintf (stderr, "%s:%d: %s\n", fuchlettername, lline, err);
  quit();
}

void interror (const char *err=errtxt)
{
  fprintf (stderr, "%s:%d:%d: %s\n", mainfilename, ly,
                   lx-1, err);
  quit();
}

void Warning (const char *err=errtxt)
{
  fprintf (stderr, "%s:%d: %s\n", fuchlettername, lline, err);
}

void warning (const char *err=errtxt)
{
  fprintf (stderr, "%s:%d:%d: %s\n", mainfilename, ly, lx-1, err);
}

void Info (const char *err=errtxt)
{
  fprintf (stderr, "%s:%d: %s\n", fuchlettername, lline, err);
}

void info (const char *err=errtxt)
{
  fprintf (stderr, "%s:%d:%d: %s\n", mainfilename, ly, lx-1, err);
}

extern inline letter *FuchChar (longchar c)
{
  if (c==NONEXIST || !isvalidlongchar(c)) return NULL;
  return fuchchar [c];
}

extern inline letter *FuchCharErr (longchar c)
{
  if (c==NONEXIST || !isvalidlongchar(c))
    EW(error, "`%c' is illegal. character values must be known.", c);
  return fuchchar [c];
}

bool haslo (char cat)
{
  return cat==cat_o || cat==cat_oo || cat==cat_b || cat==cat_B;
}

bool haso (char cat)
{
  return cat==cat_o || cat==cat_oo || cat==cat_b || cat==cat_B;
}

bool hasb (char cat)
{
  return cat==cat_o || cat==cat_u || cat==cat_U || cat==cat_b || cat==cat_B;
}

bool hasu (char cat)
{
  return cat==cat_U || cat==cat_u || cat==cat_uu || cat==cat_b || cat==cat_B;
}

bool haslu (char cat)
{
  return cat==cat_U || cat==cat_u || cat==cat_uu || cat==cat_b || cat==cat_B;
}

bool hasU (char cat)
{
  return cat==cat_U || cat==cat_UU || cat==cat_B;
}

bool hasLU (char cat)
{
  return cat==cat_U || cat==cat_UU || cat==cat_B;
}

ostream &operator<<(ostream &a, ttranslate *p)
{
  if (p)
    return a<<"[`"<<p->store<<"',`"<<p->recurse<<"']";
  else
    return a<<"(NULL)";
}

int _getc (FILE *f)
{
  int c= fgetc (f);
  if (c=='\n') lline++;
  return c;
}

void _ungetc (FILE *f, int c)
{
  ungetc (c, f);
  if (c=='\n') lline--;
}

void parsecomment()
{
  char *c= strchr (com, ' '), *d;
  if (!c || strncmp(com,"line",4)) return;

  while (*c==' ') c++;

  d= strchr (c, ' ');
  if (!d) strchr (c, '\t');
  if (d) *d= 0;
  lline= atoi(c);

  if (d) {
    c= d+1;
    d= strchr (c, ' ');
    if (!d) strchr (c, '\t');
    if (d) *d= 0;
    if (*c=='"') c++;
    if ((d= strchr (c,'"'))) *d= 0,
    assert (strlen (c) < 256);
    strcpy (fuchlettername, c);
  }
  // EW(info, "line correction %s:%d", fuchlettername, lline);
}

int nextword (FILE *f, tokenstr s)
// 0= EOF
// 1= normales Token
// 2= Stringtoken
{
  int c;
  int i;
  //char *os= s;
  int type= 0;

  for (;;) { // Gähn, jumpspaces + killcomments
    while ((c=_getc(f))!=EOF && isspace(c));
    if (c=='#') { // # ist Zeilenanweisung.
      i= 0;
      char *pp= com;
      while ((c=_getc(f))!=EOF && c!='\n') {
        if (i<MAXCOMMENT-2) *pp++= c;
        i++;
      }
      *pp= 0;
      parsecomment ();
    }
    else
      break;
  }
  if (c=='('||c==')') {
    *s++= c;
    type= 1;
  }
  else
  if (c=='"') {
    while ((c=_getc(f))!=EOF && (c!='"')) *s++= c; // String lesen
    type= 2;
  }
  else
  if (c!=EOF) {
    if (MAXTOKENLEN<2)
      EW (Error, "capacity exceeded. tokens may not be "
                 "longer than %d characters", MAXTOKENLEN);
    *s++= c; // erstes Zeichen
    bool quote= c=='\\';
    while ((c=_getc(f))!=EOF &&
            (quote || !isspace(c) && c!=')' && c!='(' && c!='"'))
    {
      *s++= c; // Wort lesen
      quote= c=='\\';
    }
    if (!isspace(c)) _ungetc(f,c);
    type= 1;
  }
  *s= 0;
  return type;
}

longchar getcharacter(const char *t)
{
  if (t[0]=='\\')
    if (t[2]) EW(Error, "`%s' is illegal; one character is "
                        "maximum after \\", t);
    else
      return t[1];
  else
    return atoi(t);
}

int nextstring (FILE *f, int (*isendmarker)(const char*),
                 longchar *s, int buffsize, int *info)
// akzeptiert beliebig viele Zeichen oder Strings und hängt alles aneinander,
// bis isendmarker() sagt, es sei genug oder das Dateiender erreicht wurde.
// Die Funktion sagt dies, indem ein positiver Wert zurückgegeben wird.
// Wird ein negativer Wert zurückgegeben, so wird der String zwar ignoriert,
// aber trotzdem nicht aufgehört, außerdem *info auf die Länge des bisherigen
// Strings gesetzt.
// Isendmarker wird nur aufgerufen, wenn es sich um kein Stringtoken handelt.
// Die Ausgabe der Funktion ist die Ausgabe von isendmarker().
// Als Ausgabe kann also > 0.
{
  int obuffsize= buffsize;
  tokenstr t;
  longchar *l= s;
  int type;
  int result= 0;
  *info= -1;
  forever {
    type= nextword (f, t);
    if (type==0) Error ("unexpected EOF while reading string");
    if (type==1) {
      result= isendmarker (t);
      if (result>0) break; //!
      if (result<0) *info= l-s;
      else {
        if (buffsize<2)
          lengtherror:
          EW(Error,"capacity exceeded. %d is maximum string length",obuffsize);
        longchar b= getcharacter (t);
        if (!b)
          EW(Error, "Strings cannot contain \\0.");
        *l++= b;
        buffsize--;
      }
    }
    else {
      int len= strlen(t);
      if (buffsize<1+len) goto lengtherror;
      lstrcpy (l, t);
      l+= len;
      buffsize-= len;
    }
  }
  *l= 0;
  return result;
}

void B(const char *c, bool b)
{
  if (!b) EW(Error, "unexpected EOF (%s)", c);
}

int nextint (FILE *f)
{
  tokenstr t;
  B("nextint", nextword (f, t));
  return atoi (t);
}

longchar nextcharacter (FILE *f)
{
  tokenstr t;
  B("nextcharacter", nextword (f, t));
  return getcharacter (t);
}

void readperhaps (FILE *f, int &i, bool bol)
{
  if (bol) i= nextint (f);
  else
    i= TRANSPARENT;
}

void readsize (FILE *f, char poscat)
{
  tokenstr t;
  char cat;
  if (anztable>=MAXTABLE)
    EW (Error, "capacity exceeded. %d table entries are maximum", MAXTABLE);

  B("readsize 1", nextword(f, t));
  table[anztable]= new letter;
  table[anztable]->name= strdup (t);
  table[anztable]->poscat= poscat;

  B("readsize 2", nextword(f, t));
  cat= 0;
  switch (t[0]) {
    case 'b': cat= cat_b; break;
    case 'B': cat= cat_B; break;
    case 'o':
      switch (t[1]) {
        case 'o': cat= cat_oo; break;
        case 0:   cat= cat_o; break;
      }
      break;
    case 'u':
      switch (t[1]) {
        case 'u': cat= cat_uu; break;
        case 0:   cat= cat_u; break;
      }
      break;
    case 'U':
      switch (t[1]) {
        case 'U': cat= cat_UU; break;
        case 0:   cat= cat_U; break;
      }
      break;
  }
  if (!cat)
    EW(Error, "`%s' is illegal, known categories "
              "are o, u, U, b, B, oo, uu, and UU.", t);

  // table[anztable]->cat= cat;

  for (int i=0; i<4; i++) {
    B("readsize 3", nextword(f, t));
    if (!strcasecmp (t, "none")) {
      table[anztable]->form[i].code= NONEXIST;
    }
    else {
      table[anztable]->form[i].code= getcharacter(t);

      table[anztable]->form[i].width= nextint (f);

      // LINKS:
      readperhaps (f, table[anztable]->form[i].left.lo, haslo (cat));
      readperhaps (f, table[anztable]->form[i].left.o,  haso (cat));
      readperhaps (f, table[anztable]->form[i].left.b,  hasb (cat));
      readperhaps (f, table[anztable]->form[i].left.u,  hasu (cat));
      readperhaps (f, table[anztable]->form[i].left.lu, haslu (cat));
      readperhaps (f, table[anztable]->form[i].left.U,  hasU (cat));
      readperhaps (f, table[anztable]->form[i].left.LU, hasLU (cat));

      // RECHTS:
      readperhaps (f, table[anztable]->form[i].right.lo, haslo (cat));
      readperhaps (f, table[anztable]->form[i].right.o,  haso (cat));
      readperhaps (f, table[anztable]->form[i].right.b,  hasb (cat));
      readperhaps (f, table[anztable]->form[i].right.u,  hasu (cat));
      readperhaps (f, table[anztable]->form[i].right.lu, haslu (cat));
      readperhaps (f, table[anztable]->form[i].right.U,  hasU (cat));
      readperhaps (f, table[anztable]->form[i].right.LU, hasLU (cat));

      //checkfuchinfo (table[anztable]->form[i]);
    }
  }

  anztable++;
}

char *dupnextword (FILE *f)
{
  tokenstr t;
  B("dupnextword", nextword (f, t));
  return strdup (t);
}

longchar *Ldupnextword (FILE *f)
{
  tokenstr t;
  B("Ldupnextword", nextword (f, t));
  return lstrdup (t);
}

longchar *dupnextstring (FILE *f, int maxlen,
                      int (*isend)(const char *),
                      int *info,
                      int *result= NULL)
{
  longchar t[maxlen];
  int r= nextstring (f, isend, t, maxlen, info);
  if (result) *result= r;
  return lstrdup (t);
}

void readdash (FILE *f)
{
  tokenstr t;
  B("readdash", nextword (f, t));
  if (!strcmp(t, "1/4")) dash[0]= Ldupnextword (f);
  else
  if (!strcmp(t, "1/2")) dash[1]= Ldupnextword (f);
  else
  if (!strcmp(t, "1")) dash[2]= Ldupnextword (f);
  else
  if (!strcmp(t, "2")) dash[3]= Ldupnextword (f);
  else
  if (!strcmp(t, "4")) dash[4]= Ldupnextword (f);
  else
    EW (Error, "`%s' is illegal; 1/4, 1/2, 1, 2 or 4 expected", t);
}

int isdotend (const char *c)
{
  if (!strcasecmp(c, "becomes")) return 1;
  else
  if (!strcasecmp(c, "end")) return 2;
  else
  if (!strcasecmp(c, "kern")) return 3;
  else
  if (!strcasecmp(c, ".")) return -1;
  else
    return 0;
}

int isend (const char *c)
{
  if (!strcasecmp(c, "becomes")) return 1;
  else
  if (!strcasecmp(c, "end")) return 2;
  else
  if (!strcasecmp(c, "kern")) return 3;
  else
    return 0;
}

void readprelig (FILE *f)
{
  int i;
  preliginfo *p;

  if (anzprelig>=MAXPRELIG)
    EW (Error, "capacity exceeded. %d prelig entries are maximum", MAXPRELIG);

  p= prelig[anzprelig]= new preliginfo;

  p->input=  dupnextstring (f, MAXSTRINGLEN, isend, &i);
  p->inputlen= lstrlen (p->input);
  p->output= dupnextstring (f, MAXSTRINGLEN, isdotend, &i);
  if (i==-1) Error (". expected in output string");
  p->cut= i;

  anzprelig++;
}

void readtranslate (FILE *f)
{
  int cut;
  longchar *a, *b, *b1, *b2;
  a= dupnextstring (f, MAXSTRINGLEN, isend, &cut);
  b= dupnextstring (f, MAXSTRINGLEN, isdotend, &cut);
  if (cut<0)
    EW(Error, "translate needs a recursion stop mark (.)");
  b1= lsubstr (b, 0, cut);
  b2= lstrdup (b+cut);
  DEBUG(11, "readtranslate: `"<<b1<<"' . `"<<b2<<"'");
  free (b);
  if (lstrlen(a)==2) {
    for (longchar c=a[0]; c<=a[1]; c++) {
      char zahl[20];
      sprintf (zahl, "%d", c);
      SETINDEX (translate, c, Error,
        new ttranslate (
          lstrdupreplchr(b1, -1, zahl),
          lstrdupreplchr(b2, -1, zahl)
        )
      );
    }
    free (b1); // wurden kopiert
    free (b2); // wurden kopiert
  }
  else {
    if (lstrlen(a)!= 1)
      EW (Error, "`%s' is illegal. translate is only implemented for "
                       "exactly one input character", Lstr(a));
    SETINDEX(translate, a[0], Error, new ttranslate (b1, b2));
  }
  free (a);
}

void readlig (FILE *f)
{
  int i;
  longchar *a= dupnextstring (f, MAXSTRINGLEN, isend, &i);
  longchar *b= dupnextstring (f, MAXSTRINGLEN, isend, &i);
  if (lstrlen(a)!=2)
    EW (Error, "`%s' is illegal. lig is only implemented for "
                     "exactly two input characters", Lstr(a));
  if (anzlig>=MAXLIG)
    EW (Error, "capacity exceeded. %d lig table entries are maximum", MAXLIG);

  lig[anzlig]= new liginfo;
  lig[anzlig]->old= a[0];
  lig[anzlig]->c= a[1];
  lig[anzlig]->kern= b;
  free (a);
}

void readcombinechars (FILE *f)
{                                                   
  combinecharA= nextcharacter (f);
  combinecharZ= nextcharacter (f);
}

void readgluechar (FILE *f)
{
  tokenstr t;
  fuchinfo *p;

  if (anztable>=MAXTABLE)
    EW (Error, "capacity exceeded. %d table entries are maximum", MAXTABLE);

  B("readgluechar", nextword (f, t));
  gluechar= getcharacter (t);

  table[anztable]= new letter;
  table[anztable]->name= "gluechar";
  table[anztable]->poscat= F_NORMAL;
  p= &table[anztable]->form[0];

  p->code= gluechar;
  p->width= 0;
  p->left.lo=
  p->left.o=
  p->left.b=
  p->left.u=
  p->left.lu=
  p->left.U=
  p->left.LU=
  p->right.lo=
  p->right.o=
  p->right.b=
  p->right.u=
  p->right.lu=
  p->right.U=
  p->right.LU= VERYLITTLE;

  for (int i=1; i<4; i++)
    table[anztable]->form[i].code= NONEXIST;

  anztable++;
}

void readwbox (FILE *f)
{
  int i;
  longchar *a= dupnextstring (f, MAXSTRINGLEN, isend, &i);
  longchar *b= lstrdupreplchr (a, -1, "%s");
  free (a);
  wordbox= trystrdup (b);
  free (b);
}

void readonestring (FILE *f, longchar *&a)
{
  int i;
  if (a) free (a);
  a= dupnextstring (f, MAXSTRINGLEN, isend, &i);
}

void readonestringc (FILE *f, char *&b)
{
  int i;
  longchar *a= dupnextstring (f, MAXSTRINGLEN, isend, &i);
  b= trystrdup (a);
  free (a);
}

void readvar (FILE *f)
{
  if (vardeft)
    EW(Error, "there must only be one var-statement");
  varstart= nextint (f);
  varend= nextint (f);
  vardeft= true;
}

void showtranslate ()
{
  cout<<"Translate-Table:"<<endl;
  for (longchar i=0; i<MAXLONGCHAR; i++)
    if (translate[i])
      cout<<"Translating "<<i<<" as "<<translate[i]<<endl;
  cout<<"end"<<endl<<endl;
}

void init ()
{
  tokenstr t;
  int i;
  snprintf (fuchlettername, 256, "%s/%s", mypath, configfile);
  fuchlettername[255]= 0;
  FILE *f;

  sprintf (t, "m4 -s %s >"TMPFILE, fuchlettername);
  if (system (t)) {
    fprintf (stderr, "warning: call to m4 for `%s' failed, reading source "
                     "without macro expansion.  This will probably not "
                     "be unsuccessful\n", fuchlettername);
  }
  else {
    fprintf (stderr, "call to m4 for `%s' succeeded\n", fuchlettername);
    assert (strlen (TMPFILE) < 256);
    strcpy (fuchlettername, TMPFILE);
  }

  f= fopen (fuchlettername, "r");
  if (!f)
    EW (Error, "file not found: %s", fuchlettername);

  for (i= 0; i<MAXLONGCHAR; i++)
    translate [i]= NULL;

  *prl= 0;    // preligbuff leeren
  *prepl= 0;  // replacebuff-Puffer leeren
//  *crl= 0; // chtxtbuff leeren

  while (nextword (f, t)) {
    //EW(Info, "read `%s'\n", t);
    if (!strcasecmp (t, "char")) readsize (f, F_NORMAL);
    else
    if (!strcasecmp (t, "startchar")) readsize (f, START);
    else
    if (!strcasecmp (t, "endchar")) readsize (f, END);
    else
    if (!strcasecmp (t, "gluechar")) readgluechar (f);
    else
    if (!strcasecmp (t, "dash")) readdash (f);
    else
    if (!strcasecmp (t, "prelig")) readprelig (f);
    else
    if (!strcasecmp (t, "replace")) readreplace (f);
    else
    if (!strcasecmp (t, "translate")) readtranslate (f);
    else
    if (!strcasecmp (t, "combinechars")) readcombinechars (f);
    else
    if (!strcasecmp (t, "wbox")) readwbox (f);
    else
    if (!strcasecmp (t, "rbox")) readonestring (f, rightbox);
    else
    if (!strcasecmp (t, "lbox")) readonestring (f, leftbox);
    else
    if (!strcasecmp (t, "bbox")) readonestring (f, bothbox);
    else
    if (!strcasecmp (t, "fuchon")) readonestringc (f, fuchon);
    else
    if (!strcasecmp (t, "fuchoff")) readonestringc (f, fuchoff);
    else
    if (!strcasecmp (t, "var")) readvar (f);
    else
    if (!strcasecmp (t, "constraint")) readconstraint (f);
    else
    if (!strcasecmp (t, "lig")) readlig (f);
    else
      EW (Error, "`%s' is illegal. `char', `endchar', `gluechar'"
                       ", `startchar', `prelig', `replace', `translate', `lig'"
                       ", `combinechars', `wbox', `rbox', `lbox', `var'"
                       ", `constraint'"
                       ", or `dash' "
                       "expected",
                       t);
  }

  fclose (f);

  memset (fuchchar, 0, sizeof (fuchchar));

  for (i= 0; i<anztable; i++) {
    longchar p= table [i]->form[NORM].code;
    if (p!=NONEXIST && isvalidlongchar (p))
      fuchchar [p]= table [i];
  }

  if (show) {
    showreplace ();
    showtranslate ();
  }
}

//////////////////////////////////////////////////////////////////////////////

void push (fuchstack *&st, int env, fuchmode mode)
{
  st= new fuchstack (st, env, mode);
}

void pop (fuchstack *&st) // Fehler, falls nachher Stapel leer
{
  fuchstack *help= st;
  if (st)
  {
    st= st->next;
    if (!st) error ("Stack underflow");
    help->next= NULL;
    delete help;
  }
}

//////////////////////////////////////////////////////////////////////////////

bool iscommandchar (char c)
{
  return (c>='a'&&c<='z') || (c>='A' && c<='Z') || (c=='@');
}

char getonechar ()
{
//  char result;
//  if (result= *outchtxt) {
//    outchtxt++;
//    if (!*outchtxt) {  // Alles gelesen? dann zurück an den Anfang
//      outchtxt= outchbuff;
//      *outchtxt= 0;
//    }
//    return result;
//  }
  return fgetc (f);
}

char readchar ()
{
  char result= extrachar ?: getonechar ();
  extrachar= 0;
  if (result== '\n')
  {
    ly++;
    lx= 1;
  }
  else
  if (result== '\r') lx= 1;
  else
    lx++;
  return result;
}

void unreadchar (char c)
{
  lx--;
  if (!lx) ly--;
  extrachar= c;
}

void readTeXtoken (char *token, tokentype &type)
{
  char c= readchar ();
  int ii;
  switch (c)
  {
    case EOF:
       type= eof;
       break;

    case '%':
       type= comment;
       *(token++)= c;
       ii= 0;
       do
       {
         c= readchar ();
         if (ii==0 && c=='!') { // Spezialsyntax: #!F+  und #!F-
           *token++= c;
           c= readchar ();
           if (c=='F') {
             *token++= c;
             c= readchar ();
             if (c=='+') nofuch= false;
             else
             if (c=='-') nofuch= true;
             else
             if (c>='0' && c<='9') {
               mindist= 0;
               while (c>='0' && c<='9') {
                 mindist= (mindist*10) + c-'0';
                 *token++= c;
                 c= readchar ();
               }
             }
             ii++;
           }
         }
         ii++;
         if (c!=EOF) *(token++)= c;
       }
       while (c!= '\n' && c!= '\r' && c!= EOF);
       break;

    case ' ':
    case '\t':
    case '\r':
    case '\n':
       type= space;
       do
       {
         *(token++)= c;
         c= readchar ();
       }
       while (c==' '||c=='\t'||c=='\n'||c=='\r');
       unreadchar (c);
       break;

    case '\\':
       type= command;
       *(token++)=c;
       c= readchar (); // der nächste gehört sicher dazu.
       do
       {
         *(token++)= c;
         c= readchar ();
       }
       while (iscommandchar (c));
       unreadchar (c);
       break;

    case '$':
       type= math;
       *(token++)= c;
       break;

    case '{':
       type= openenv;
       *(token++)= c;
       break;

    case '}':
       type= closeenv;
       *(token++)= c;
       break;

    default:
       type= onechar;
       *(token++)= c;
  }
  *token= 0;
}

//////////////////////////////////////////////////////////////////////////////

void outtext (const char *steuer, const char *inhalt, bool breakifness)
{
  char buffer[1000], *c;
  sprintf (buffer, steuer, inhalt);
  int old= outx;
  for (c=buffer; *c; c++)
  {
    if (*c=='\n' || *c=='\r') outx= 0;
    else
      outx++;
  }
  if (old>5 && outx>maxoutx && breakifness)
     // Ui, ich war überrascht, daß ich das
     // schon programmiert hatte, als ich
     // es entdeckte.
  {
    fprintf (fout, "\n");
    outx= 0;
  }
  fprintf (fout, "%s", buffer);
}

void outnl ()
{
  outtext ("\n", "", false);
}

//////////////////////////////////////////////////////////////////////////////

void fuchstore_direct (char c)
{
  DEBUG(16, "fuchstore_direct: `"<<c<<"'");
  if (fc-fuchpuff>=MAXFUCH-1)
    EW (error, "capacity exceeded. %d is the maximum size "
               "for fukhian words. This error also occurs when"
               "you have infinite recurrences in your preligs "
               "or replaces"
               ,
                MAXFUCH);
  *(fc++)= c & 0xff;
}

void fuchstorestr (const longchar *s)
{
  if (s) {
    const longchar *c;
    for (c=s; *c; c++)
      fuchstore_aux (*c);
  }
}

void fuchstorestr_direct (const char *s)
{
  if (s) {
    const char *c;
    for (c=s; *c; c++)
      fuchstore_direct (*c);
  }
}

void fuchstore_aux_direct (longchar c)
{
  if (c<-128 || c>255)
    EW (warning, "warning: trying to output non-ascii character %d", c);
  else
    fuchstore_direct (c & 0xff);
}

void fuchstorestr_direct (const longchar *s)
{
  if (s) {
    const longchar *c;
    for (c=s; *c; c++)
      fuchstore_aux_direct (*c);
  }
}

void fuchstore_aux (longchar c)
{
  if (isvalidlongchar(c)) {
    if (translate[c]) {
      fuchstorestr_direct (translate[c]->store);  // dies soll sofort raus,
      fuchstorestr (translate[c]->recurse);       // dies rekursiv
    }
    else
      fuchstore_aux_direct (c);
  }
}

void fuchstore (longchar c)
{
  //if (c==gluechar) return; unnötig wegen der Übersetzungen
  fuchstore_aux (c);
}

void initfuch ()
{
  xlo= xo= xb= xu= xlu= xU= xLU= 0;
  fc= fuchpuff;
  old= NONEXIST;
  oldform= NORM;
  sonder= 0;
}

void storebindeaux (int &rest, int length, const longchar *c)
{
  if (c) {
    while (rest>=length) {
      fuchstorestr (c);
      rest-= length;
    }
  }
}

int insertbinde (const abst &a, bool doinsert)
// fügt Bindezeichen ein, bis der Buchstabe berührungsfrei ans Ende paßt.
{
  int add= VERYLITTLE, i;
  if (a.lo!=TRANSPARENT && xlo+a.lo>add) add= xlo+a.lo;
  if (a.o !=TRANSPARENT && xo +a.o >add) add= xo +a.o;
  //if (b !=TRANSPARENT && xb +b >add) add= xb +b;
  if (a.u !=TRANSPARENT && xu +a.u >add) add= xu +a.u;
  if (a.lu!=TRANSPARENT && xlu+a.lu>add) add= xlu+a.lu;
  if (a.U !=TRANSPARENT && xU +a.U >add) add= xU +a.U;
  if (a.LU!=TRANSPARENT && xLU+a.LU>add) add= xLU+a.LU;
  add+= mindist;

  if (add>0) // <0 zeigt an, daß sich die Buchstaben nicht einmal berühren.
  {
    xlo-= add;
    xo-=  add;
    xb= 0;
    xu-=  add;
    xlu-= add;
    xU-=  add;
    xLU-= add;
    if (doinsert) {
      i= add;
      storebindeaux (i, 16, dash[4]);
      storebindeaux (i, 8, dash[3]);
      storebindeaux (i, 4, dash[2]);
      storebindeaux (i, 2, dash[1]);
      storebindeaux (i, 1, dash[0]);
    }
  }
  return add;
}

//void insertbindechar ()
//{
//  xlo-= 4;
//  xo-= 4;
//  xb= 0;
//  xu-= 4;
//  xlu-= 4;
//  xU-= 4;
//  xLU-= 4;
//  fuchstore (dash[2]);
//}

void changex (int &x, int a, int w)
// siehe auch adjust().
{
  if (a==TRANSPARENT) x-= w;
  else
    x= a;           
}

void insertletter (longchar c, const fuchinfo &f, bool doinsert)
{
  changex (xlo, f.right.lo, f.width);
  changex (xo,  f.right.o,  f.width);
  changex (xb,  f.right.b,  f.width);
  changex (xu,  f.right.u,  f.width);
  changex (xlu, f.right.lu, f.width);
  changex (xU,  f.right.U,  f.width);
  changex (xLU, f.right.LU, f.width);

  if (doinsert)
    fuchstore (c);  // abspeichern
}

bool testlig (const fuchinfo &one, const fuchinfo &two)
// guckt, ob die Buchstaben als Ligatur an den Text passen.
{
  int rxlo= xlo,
      rxo= xo,  // alte Werte retten
      rxb= xb,
      rxu= xu,
      rxlu= xlu,
      rxU= xU,
      rxLU= xLU,
      anz;

  insertbinde  (one.left, false);
  insertletter (0, one, false);

  anz= insertbinde (two.left, false);
  // wieviele Leerstellen würden eingefügt?

  xlo= rxlo;
  xo= rxo;
  xb= rxb;
  xu= rxu;
  xlu= rxlu;
  xU= rxU;
  xLU= rxLU;

  return anz <= 0;  // wenn gar nichts eingefügt werden muß, true
}

bool isoben (const abst &a)
{
  return (a.lo!=TRANSPARENT) || (a.o!=TRANSPARENT);
}

bool isunten (const abst &a)
{
  return (a.u!=TRANSPARENT) || (a.lu!=TRANSPARENT);
}

bool isUNTEN (const abst &a)
{
  return (a.U!=TRANSPARENT) || (a.LU!=TRANSPARENT);
}

bool isuntenorUNTEN (const abst &a)
{
  return isunten(a) || isUNTEN(a);
}

bool isbeide (const abst &a)
{
  return isoben (a) && isuntenorUNTEN (a);
}

bool isnuroben (const abst &a)
{
  return isoben (a) && !isuntenorUNTEN (a);
}

bool isnurunten (const abst &a)
{
  return isunten (a) && !isoben (a);
}

bool touchimpossible (const abst &a, const abst &b)
{
  return (isnuroben (a) && isnurunten (b) ||
          isnurunten (a) && isnuroben (b));
}

const longchar *special (longchar old, longchar c)
{
  //static char *kern1= "\\kern-0.08em{}";
  for (int i=0; i<anzlig; i++)
    if (lig[i]->old==old && lig[i]->c==c)
      return lig[i]->kern;
  return NULL;     
}

void putzwischen (const abst &a, char poscat)
{
  int anz;
//char puff [100];
  if (poscat==START)
  {
    anz= insertbinde (a, false);
//    if (anz>0)
//    {
//      sprintf (puff, "\\kern%gpt{}", anz/4.0);
//      fuchstorestr (puff);
//    }
  }
  else
  {
    anz= insertbinde (a, true);
    //insertbindecharperhaps ('B', anz);
  }
}

void insertfuch_aux (longchar c)
// dabei gibt es viel zu überprüfen:
{
  int newform= NORM,
      newoldform= oldform;
  fuchinfo *f;
  const longchar *specstr= NULL;
  DEBUG(9, "insertfuch_aux: `"<<c<<"'  (t: "
           <<(isvalidlongchar(c)?translate[c]:(ttranslate*)NULL)
           <<")");

  if (old!=NONEXIST)
  {
    if (FuchChar (old))
    {
      newoldform= oldform==NORM ? RIGHT : BOTH;
      if (FuchChar (c) &&
          FuchCharErr (old)->form[newoldform].code!=NONEXIST &&
          FuchCharErr (c)->form[LEFT].code!=NONEXIST &&
          (FuchCharErr(old)->form[BOTH].code!=NONEXIST || // diese Bed. verhindert z.B. l+e
           FuchCharErr(c)->form[BOTH].code!=NONEXIST)     //
         )
      {  // Ligatur gefunden?
        if (touchimpossible (FuchCharErr (old)->form[newoldform].right,
                             FuchCharErr (c)->form[LEFT].left) &&
            testlig (FuchCharErr (old)->form[newoldform],
                     FuchCharErr (c)->form[LEFT])
           )
        { // Ligatur paßt dran. Also einfügen.
          newform= LEFT;
          oldform= newoldform;
        }
      }
      f= &FuchCharErr (old)->form[oldform];

      putzwischen (f->left, FuchCharErr(old)->poscat);

      insertletter (FuchCharErr (old)->form[oldform].code, *f, true);

      if (FuchCharErr (c) && (specstr= special (old, c)))  // auch noch besonders?
      {
        fuchstorestr (specstr);
        xlo= xo= xb= xu= xlu= xU= xLU= VERYLITTLE;
           // c soll im nächsten Durchgang in jedem
           // Falle passen
      }
    }
    else
    {
      putzwischen (abstNULL, false);
      fuchstore(old);
      xlo= xo= xb= xu= xlu= xU= xLU= 0;
    }
  }
  DEBUG(8, "Werte nach "<<c<<":"
           <<xlo<<" "<<xo <<" "<<xb<<" "
           <<xu <<" "<<xlu<<" "<<xU<<" "<<xLU);
  old= c;
  oldform= newform;
}

void findmatchprelig_aux (int ominlen,
                          const longchar *buff, preliginfo *&p, int &o)
// sucht einen Match.
{
  int len= lstrlen (buff);
  preliginfo *q;
  p= NULL; // wird umgesetzt, wenn Hypothesen gefunden wurden.
  for (int i=0; i<anzprelig; i++) {
    q= prelig[i];
    if (q->inputlen>=len) {
      if (!lstrncmp (buff, q->input, len)) {
        p= q;
        o= len-q->inputlen; //!
        DEBUG(12, "a: ominlen="<<ominlen<<", o="<<o);
        if (o>=ominlen) {
          DEBUG(12,"a: found match: `"<<buff<<"': `"
                   <<q->input<<"' -> `"<<q->output<<"'");
          return;
        }
      }
    }
    else {
      if (!lstrncmp (buff, q->input, q->inputlen)) {
        p= q;
        o= len-q->inputlen;
        DEBUG(12, "b: ominlen="<<ominlen<<", o="<<o);
        if (o>=ominlen) {
          DEBUG(12,"b: found match: `"<<buff<<"': `"
                   <<q->input<<"' -> `"<<q->output<<"'");
          return;
        }
      }
    }
  }
}

void findmatchprelig (int ominlen, preliginfo *&p, int &vor, int &nach)
// vor ist die Anzahl der Zeichen, die VOR dem Match im prelig-Puffer
// stehen, nach die Anzahl derer danach. Nach kann negativ sein, d.h.
// der Match ist noch unvollständig.  Ist vor!=0, so kann man die
// Zeichen sofort weiterschicken.
{
  const longchar *buff;
  for (buff= preligbuff, vor= 0; *buff; buff++, vor++) {
    findmatchprelig_aux (ominlen, buff, p, nach);
    if (p) return;  // den ersten finden.
  }
  p= NULL;
}

//void findmatchchtxt (preliginfo *&p, int &o)
//// sucht einen Match.
//{
//  int len= strlen (chtxtbuff);
//  preliginfo *q;
//  for (int i=0; i<anzchtxt; i++) {
//    q= chtxt[i];
//    if (q->inputlen>=len) {
//      if (!strncmp (chtxtbuff, q->input, len)) {
//        p= q;
//        o= len-q->inputlen; //!
//        return;
//      }
//    }
//    else {
//      if (!strncmp (chtxtbuff, q->input, q->inputlen)) {
//        p= q;
//        o= len-q->inputlen;
//        return;
//      }
//    }
//  }
//  p= NULL;
//}

void adjust (int &x, int a, int w)
// siehe auch adjust().
{
  if (a!=TRANSPARENT)
    x= w+a;
}

void combinecharright (longchar newc, letter *p, const longchar *string)
{
  longchar key= *string;
  int      thwithboxlen= lstrlenreplchr (rightbox, -1, string+1);
  DEBUG(10, "Length of `" << rightbox <<
          "' merged with `" << (string+1) << "' = " << thwithboxlen);
  longchar *withbox= lstrdupreplchr (rightbox, -1, string+1);
  int      withboxlen= lstrlen (withbox);
  longchar *help;
  int      h;

  if (!FuchChar(key) || FuchCharErr(key)->form[NORM].code==NONEXIST)
    EW(error, "I need a valid character to combine, not `%c' (%d)", key, key);

  SETINDEX(fuchchar, newc, error, p);

  for (int i=0; i<4; i++) {
    if (FuchCharErr(key)->form[i].code!=NONEXIST) {
      h= (i==NORM) ? newc : (combinecharcounter++);
      translate[h]= new ttranslate (NULL, help= new longchar[withboxlen+2]);
      p->form[i].code= h;
      assert (((int)(1 + lstrlen (withbox))) < ((int)(withboxlen+2)));
      lstrcpy (lstrocpyend (help, FuchCharErr(key)->form[i].code), withbox);

      p->form[i].width= FuchCharErr(key)->form[i].width;
      p->form[i].left=  FuchCharErr(key)->form[i].left;
      p->form[i].right= FuchCharErr(key)->form[i].right;

      int gesw= 0;
      for (const longchar *c= string+1; *c; c++) {
        int width= FuchCharErr(*c)->form[NORM].width;
        gesw+= width;
        adjust (p->form[i].right.lo,FuchCharErr(*c)->form[NORM].right.lo,gesw);
        adjust (p->form[i].right.o, FuchCharErr(*c)->form[NORM].right.o, gesw);
        adjust (p->form[i].right.b, FuchCharErr(*c)->form[NORM].right.b, gesw);
        adjust (p->form[i].right.u, FuchCharErr(*c)->form[NORM].right.u, gesw);
        adjust (p->form[i].right.lu,FuchCharErr(*c)->form[NORM].right.lu,gesw);
        adjust (p->form[i].right.U, FuchCharErr(*c)->form[NORM].right.U, gesw);
        adjust (p->form[i].right.LU,FuchCharErr(*c)->form[NORM].right.LU,gesw);
      }
      DEBUG(6,"Form: "<<i<<" w="<<p->form[i].width
              <<"  l="
              <<p->form[i].left.lo<<" "
              <<p->form[i].left.o<<" "
              <<p->form[i].left.b<<" "
              <<p->form[i].left.u<<" "
              <<p->form[i].left.lu<<" "
              <<p->form[i].left.U<<" "
              <<p->form[i].left.LU<<" "
              <<"  r="
              <<p->form[i].right.lo<<" "
              <<p->form[i].right.o<<" "
              <<p->form[i].right.b<<" "
              <<p->form[i].right.u<<" "
              <<p->form[i].right.lu<<" "
              <<p->form[i].right.U<<" "
              <<p->form[i].right.LU);
    }
  }
  free (withbox);
}

void combinecharleft (longchar newc, letter *p, const longchar *string)
{
  longchar key= string[lstrlen(string)-1];
  longchar *rest= lsubstr (string, 0, lstrlen(string)-1);

  longchar *withbox= lstrdupreplchr (leftbox, -1, rest);
  int      withboxlen= lstrlen (withbox);
  longchar *help;
  int      h;

  if (!FuchChar(key) || FuchCharErr(key)->form[NORM].code==NONEXIST)
    EW(error, "I need a valid character to combine, not `%c' (%d)", key, key);

  SETINDEX(fuchchar, newc, error, p);

  for (int i=0; i<4; i++) {
    if (FuchCharErr(key)->form[i].code!=NONEXIST) {
      h= (i==NORM) ? newc : (combinecharcounter++);
      translate[h]= new ttranslate (NULL, help= new longchar[withboxlen+2]);
      p->form[i].code= h;
      assert (((int)lstrlen (withbox) + 1) < ((int)withboxlen+2));
      lstrcpy (lstrocpyend (help, withbox), FuchCharErr(key)->form[i].code);

      p->form[i].width= FuchCharErr(key)->form[i].width;
      p->form[i].left=  FuchCharErr(key)->form[i].left;
      p->form[i].right= FuchCharErr(key)->form[i].right;

      int gesw= 0;
      for (const longchar *c= rest+lstrlen(rest)-1; c>=rest; c--) {
        int width= FuchCharErr(*c)->form[NORM].width;
        gesw+= width;
        adjust (p->form[i].left.lo, FuchCharErr(*c)->form[NORM].left.lo, gesw);
        adjust (p->form[i].left.o,  FuchCharErr(*c)->form[NORM].left.o,  gesw);
        adjust (p->form[i].left.b,  FuchCharErr(*c)->form[NORM].left.b,  gesw);
        adjust (p->form[i].left.u,  FuchCharErr(*c)->form[NORM].left.u,  gesw);
        adjust (p->form[i].left.lu, FuchCharErr(*c)->form[NORM].left.lu, gesw);
        adjust (p->form[i].left.U,  FuchCharErr(*c)->form[NORM].left.U,  gesw);
        adjust (p->form[i].left.LU, FuchCharErr(*c)->form[NORM].left.LU, gesw);
      }
      DEBUG(6,"Form: "<<i<<" w="<<p->form[i].width
              <<"  l="
              <<p->form[i].left.lo<<" "
              <<p->form[i].left.o<<" "
              <<p->form[i].left.b<<" "
              <<p->form[i].left.u<<" "
              <<p->form[i].left.lu<<" "
              <<p->form[i].left.U<<" "
              <<p->form[i].left.LU<<" "
              <<"  r="
              <<p->form[i].right.lo<<" "
              <<p->form[i].right.o<<" "
              <<p->form[i].right.b<<" "
              <<p->form[i].right.u<<" "
              <<p->form[i].right.lu<<" "
              <<p->form[i].right.U<<" "
              <<p->form[i].right.LU);
    }
  }
  free (withbox);
  free (rest);
}

void combinecharboth (longchar newc, letter *p, const longchar *string)
{
  longchar keyl= *string;
  longchar keyr= string[lstrlen(string)-1];
  longchar *rest= lsubstr (string, 1, lstrlen(string)-2);
  longchar *withbox= lstrdupreplchr (bothbox, -1, rest);
  int      withboxlen= lstrlen (withbox);
  longchar *help;
  int      h;

  if (!FuchChar(keyl) || FuchCharErr(keyl)->form[NORM].code==NONEXIST)
    EW(error, "I need a valid character to combine, not `%c' (%d)",keyl,keyl);

  if (!FuchChar(keyr) || FuchCharErr(keyr)->form[NORM].code==NONEXIST)
    EW(error, "I need a valid character to combine, not `%c' (%d)",keyr,keyr);

  SETINDEX(fuchchar, newc, error, p);

  for (int i=0; i<4; i++) {
    int il, ir;
    switch (i) {
      case 1: il= 1; ir= 0; break;
      case 2: il= 0; ir= 2; break;
      case 3: il= 1; ir= 2; break;
      default:
        il= ir= 0;
    };
    if (FuchCharErr(keyl)->form[il].code!=NONEXIST &&
        FuchCharErr(keyr)->form[ir].code!=NONEXIST)
    {
      h= (i==NORM) ? newc : (combinecharcounter++);
      translate[h]= new ttranslate (NULL, help= new longchar[withboxlen+3]);
      p->form[i].code= h;
      assert (((int)(2 + lstrlen (withbox))) < (((int)withboxlen+3)));
      lstrcpy (
        lstrcpyend (
          lstrocpyend (help,
            FuchCharErr(keyl)->form[il].code),
            withbox),
            FuchCharErr(keyr)->form[ir].code);

      p->form[i].width= FuchCharErr(keyl)->form[il].width+
                        FuchCharErr(keyr)->form[ir].width;
      p->form[i].left=  FuchCharErr(keyl)->form[il].left;
      p->form[i].right= FuchCharErr(keyr)->form[ir].right;

      for (const longchar *c= rest; *c; c++)
        p->form[i].width+= FuchCharErr(*c)->form[NORM].width;

      // NYI: angepaßte left- und right-Felder anhand der inneren Buchstaben

      DEBUG(6,"Form: "<<i
            <<"  w="<<p->form[i].width
            <<"  l="
            <<p->form[i].left.lo<<" "
            <<p->form[i].left.o<<" "
            <<p->form[i].left.b<<" "
            <<p->form[i].left.u<<" "
            <<p->form[i].left.lu<<" "
            <<p->form[i].left.U<<" "
            <<p->form[i].left.LU<<" "
            <<"  r="
            <<p->form[i].right.lo<<" "
            <<p->form[i].right.o<<" "
            <<p->form[i].right.b<<" "
            <<p->form[i].right.u<<" "
            <<p->form[i].right.lu<<" "
            <<p->form[i].right.U<<" "
            <<p->form[i].right.LU);
    }
  }
  free (withbox);
  free (rest);
}

void newcharacter (longchar newc, longchar dir, const longchar *string)
{
  letter *p;
  if (anztable>=MAXTABLE)
    EW (Error, "capacity exceeded. %d table entries are maximum", MAXTABLE);

  DEBUG(6,"newcharacter: "<<newc<<" "<<dir<<" `"<<string<<"'");

  p= table[anztable]= new letter;
  trystrcpy (p->name= new char[lstrlen(string)+2], string);
  p->poscat= F_NORMAL;

  if (dir=='L' || dir=='l') combinecharleft (newc, p, string);
  else
  if (dir=='R' || dir=='r') combinecharright (newc, p, string);
  else
  if (dir=='B' || dir=='b') combinecharboth (newc, p, string);
  else
    EW (Error, "`%c' (%d) is illegal. `R' or `L' expected for direction of "
               "combination.", (char) dir & 0xff, dir);

  fuchchar [newc]= table[anztable++];
}

longchar combinecombine ()
  return result= combinecharcounter++;

#define STORE(C) \
  ({ if (c>=s+MAXCOMBINESTR) {                                       \
       EW(error, "capacity exceeded: characters may be adjoint up "  \
                 "to length %d", MAXCOMBINESTR);                     \
     }                                                               \
     *c++= C;                                                        \
  })

{
  longchar s[MAXCOMBINESTR], *c= s;
  comb++; // combinecharA überschlagen
  forever {
    if (!*comb)
      EW(interror, "combinecombine: string ends before parsed");

    if (*comb==combinecharA) STORE (combinecombine());
    else
    if (*comb==combinecharZ) { // so, Ende ist gefunden
      STORE(0);
      newcharacter (result, *s, s+1);
      return; //! (kein comb++ mehr!)
    }
    else
      STORE(*comb);
    comb++;
  }
}

inline void insertcombine (longchar c)
{
  if (comb>=combinebuff+MAXCOMBINE)
    EW(error, "capacity exceeded: buffer for combined characters is only %d "
              "characters wide", MAXCOMBINE);
  *comb++= c;
}

void insertfuch_aux2 (longchar c)
{
  if (c==combinecharA) {
    if (!combinecount)
      comb= combinebuff;
    combinecount++;
    insertcombine (c);
  }
  else
  if (c==combinecharZ) {
    insertcombine (c);
    combinecount--;
    if (!combinecount) {
      insertcombine (0);
      comb= combinebuff;
      DEBUG (7, "combinebuff= " << combinebuff);
      longchar bb= combines.index(combinebuff);
      if (bb==NONEXIST) {
        bb= combinecombine ();
        combines.set(lstrdup(combinebuff), bb);
      }
      insertfuch_aux (bb);
    }
  }
  else {
    if (combinecount) *comb++= c;
    else
      insertfuch_aux (c);
  }
}

void flushprelig ()
{
  for (longchar *c= preligbuff; *c; c++)
    insertfuch_aux3 (*c);
  prl= preligbuff;
}

void matchprelig (int minimalo)
// minimalo gibt an, wieviele Zeichen in preligbuff mindestens stehenbleiben
// müssen bevor der Match angewendet wird.
{
  DEBUG(9,"matchprelig "<<minimalo<<" in `"<<preligbuff<<"'");
  int v, o, i;
  preliginfo *p;
  const longchar *c;
  forever {
    findmatchprelig(minimalo, p, v, o);
    // p= der gefundene Match,  o= Anzahl der Zeichen, die am Anfang in
    // preligbuff stehenbleiben, wenn man den Match anwendet.
    // ist o<0, so ist der Match unvollständig. (siehe auch bei
    // findmatch()).
    if (!p) {
      flushprelig ();  // alles raus
      break; //!
    }

    if (o<minimalo) break; //! Match gefunden, aber es bleiben nicht
                           // genügend Zeichen stehen.
    DEBUG(5,"found match: `"
             <<p->input<<"' -> `"<<p->output
             <<"', preligbuff="<<preligbuff
             <<", v="<<v
             <<", o="<<o);

    // Das folgende kann nicht vor die vorangehende Zeile gezogen werden,
    // da sich angefangene Matches als falsch herausstellen könnten.
    if (v>0) { // unverändertes Präfix kopieren
      for (c= preligbuff, i=0; *c && i<v; i++, c++)
        insertfuch_aux3 (*c);
      for (prl= preligbuff; *c; c++, prl++)
        if (prl<prlend)
          *prl= *c;
        else
          EW(error, "capacity exceeded: preligbuff has size %d", MAXWORDLEN);
      *prl= 0;
    }

    else { // Match anwenden.
      // erstens: nicht weiter zu untersuchende Ersetzung raus.
      for (c= p->output, i=0; i<p->cut; c++, i++)
        insertfuch_aux3 (*c);

      // zweitens: preligbuff neu zusammensetzen.
      assert (lstrlen (c) + lstrlen (preligbuff+p->inputlen) < MAXWORDLEN);
      lstrcpy (hprelig, c);                 // Als erstes Restersetzung
      lstrcat (hprelig, preligbuff+p->inputlen); // dann Rest der Eingabe

      assert (lstrlen (hprelig) < MAXWORDLEN);
      lstrcpy (preligbuff, hprelig);        // zurückkopieren
      prl= preligbuff+lstrlen (preligbuff); // prl richtig setzen
    }
  }
}

void insertfuch (longchar c)
{
  DEBUG(9, "insertfuch: "<<c);
  if (c==NONEXIST) { // flush!
    matchprelig (0);
    flushprelig ();
    insertfuch_aux3 (NONEXIST);
    empty= true;
  }
  else {
    empty= false;
    if (prl>=prlend)
      EW(error, "capacity exceeded. preligbuff has size %d", MAXWORDLEN);
    *prl++= c;
    *prl= 0;
    matchprelig (1);
  }
  *prl= 0;
}


void flushfuch () // darf (viel) zu oft aufgerufen werden
{
  if (!empty) insertfuch (NONEXIST);
  *fc= 0;                               // nicht hochzählen!
  fc= fuchpuff;
  if (*fuchpuff)
  {
    outtext (wordbox, fuchpuff, true);
    initfuch ();
  }
}

void startfuch ()
{
  flushfuch ();
  initfuch ();
}

void stopfuch ()
{
  flushfuch ();
}

//////////////////////////////////////////////////////////////////////////////

void dofuch (fuchstack *&st, int envcount, fuchmode mode, bool &fuchfound)
{
  bool old= st->mode==fuchtext;
  push(st, envcount, mode);
  if (st->mode==fuchtext && !old) startfuch();
  if (st->mode!=fuchtext && old) stopfuch();
  fuchfound= false;
}

void fuchaus (fuchstack *&st)
{
  bool old= st->mode==fuchtext;
  pop (st);
  if (st->mode==fuchtext && !old) startfuch();
  if (st->mode!=fuchtext && old) stopfuch();
}

void convert ()
{
  int  envcount= 1;
  bool fuchfound= false;
  fuchstack *st= NULL;
  char token [256];
  tokentype type;

  push (st, envcount, normtext);

  forever
  {
    readTeXtoken (token, type);

    switch (type)
    {
      case command:
        flushfuch ();
        if (fuchfound) // dann ist es diese Umgebung
          dofuch (st, envcount, fuchtext, fuchfound);

        if (!strncmp (token, fuchoff, 7))
          dofuch (st, envcount, normtext, fuchfound);

        fuchfound= !nofuch && !strncmp (token, fuchon, 5);
        outtext ("%s", token, false);
        break;

      case openenv:
        flushfuch ();
        outtext ("%s", token, false);
        envcount++;

        if (fuchfound)  // dann ist es erst die folgende Umgebung
          dofuch (st, envcount, fuchtext, fuchfound);
        break;

      case closeenv:
        flushfuch ();
        fuchfound= false;    // das gibt es nicht: \fuch}

        while (st->env==envcount)
          fuchaus (st);  // dann vorhergehende Umgebung

        envcount--;
        outtext ("%s", token, false);
        break;

      case math:
        flushfuch ();
        if (st->mode==mathtext)
        {
          outtext ("%s", token, false);
          fuchaus (st);
        }
        else
        {
          dofuch (st, envcount, mathtext, fuchfound);
          outtext ("%s", token, false);
        }
        break;

      case onechar:
        if (fuchfound)
          dofuch (st, envcount, fuchtext, fuchfound);

        if (st->mode==fuchtext)
          insertfuch (token[0]); // es ist nur ein Zeichen!
        else
        {
          flushfuch ();
          outtext ("%s", token, false);
        }
        break;

      case space:
      case comment:
        flushfuch ();
        outtext ("%s", token, false);
        break;

      case eof:
        flushfuch ();
        return;

      default:
        error ("interner Fehler 0001");
    }
  }
  delete st; // muß gehen, da pop darauf achtet, daß der Stapel nicht leer wird
}

void help (const char *name, bool descr)
{
#define fp(A) fprintf(stderr, A)
  fprintf (stderr, "usage: %s [options] file [outputfile "
                   "[debugfile]] [options]\n", name);
  if (descr)
  {
    fp(" This programme converts fukhian text into fukhian text with\n");
    fp(" inserted ligatures and spaces. It searches the input for all\n");
    fp(" TeX commands beginning with `\\fuch'. If an environment follows\n");
    fp(" immediately this is the scope of operation, otherwise the\n");
    fp(" surrounding environment is. Math mode text embedded in $ is\n");
    fp(" ignored.\n");
    fp(" Additionally, you can mark long sections to be untouchable by this\n");
    fp(" programme by using #!F- ... #!F+.  Those are TeX-comments with a\n");
    fp(" special meaning to this programme only.\n");
    fp(" Another special comment ist #!F directly followed by a number.  This\n");
    fp(" number ist the minimal space between two fukhian characters.  The\n");
    fp(" default is 6.  Remember that 0 is not sensible since the line width\n");
    fp(" is not taken into account by this programme.\n");
    fp(" Be careful: TeX commands are not understood. Therefore you must\n");
    fp("  handle \\(, \\), \\[, \\], \\mbox, etc. yourself. Use \\nofuch to\n");
    fp("  tell this programme that inside the current environment you want\n");
    fp("  no more fukhian.\n");
    fp(" E.g:\n");
    fp("  wrong: {\\fuch anyfuch \\mbox {mist} morefuch}\n");
    fp("  right: {\\fuch anyfuch \\mbox {\\nofuch mist} morefuch}\n");
    fp("\n");
    fp("  wrong: {\\fuch anyfuch \\( a^2+b^2=x^2 \\) morefuch}\n");
    fp("  wrong: {\\fuch anyfuch \\nofuch\\( a^2+b^2=c^2 \\) morefuch}\n");
    fp("  right: {\\fuch anyfuch {\\nofuch\\( a^2+b^2=c^2 \\)} morefuch}\n");
    fp("  right: {\\fuch anyfuch $ a^2+b^2=c^2 $ morefuch}\n");
    fp("  right: \\fuch{fuch1 {\\nofuch foo {\\fuch fuch2} bar} fuch3}\n");
    fp("The TeX-commens which begin and end the fukhian block may be changed\n");
    fp("in the configuration file.\n");
  }
  fp("options:\n");
  fp("  -h         : help and description\n");
  fp("  -debug N   : set debug level to N\n");
  fp("               current levels are: 5-10, 19-21\n");
  fp("  -cf file   : use different configuration file.  This file must\n");
  fp("               be in the same directory as the executable.\n");
  fp("  -show      : display internal structures after loading configuration\n");
  fp("               file\n");
  fp("  -maxreplace N: wait for an error to tell you why to use this.\n");
  quit();
}

int main (int argc, char **argv)
{
  assert (strlen (argv[0]) < 200);
  strcpy (mypath, argv[0]);
  char *c= strrchr (mypath, '/');
  if (c) *c= 0;
  else
    strcpy (mypath, ".");

  for (int i=1; i<argc; i++)
  {
    if (argv[i][0]=='-')
    {
      if (argv[i][1])
        if (!strcmp (argv[i], "-h")) help (argv [0], true);
        else
        if (!strcmp (argv[i], "-show")) show= true;
        else
        if (!strcmp (argv[i], "-maxreplace")) {
          if (++i>=argc) help (argv[0], false);
          maxreplace= atoi(argv[i]);
        }
        else
        if (!strcmp (argv[i], "-debug")) {
          if (++i>=argc) help (argv[0], false);
          debuglevel= atoi(argv[i]);
        }
        else
        if (!strcmp (argv[i], "-cf")) {
          if (++i>=argc) help (argv[0], false);
          configfile= argv[i];
        }
        else
          help (argv[0], false);
      else
      if (f)
        help(argv[0], false);
      else
        f= stdin;
    }
    else
    if (f)
    {
      if (fout)
        if (cdebug) help (argv[0], false);
        else
          cdebug= new ofstream (argv[i]);
      else {
        assert (strlen (argv[i]) < 256);
        strcpy (outfilename, argv[i]);
        fout= fopen(outfilename, "w");
      }
    }
    else {
      if (strchr (argv[i], '/')) {
        assert (strlen (argv[i]) < 256);
        strcpy (mainfilename, argv[i]);
      }
      else {
        assert (strlen (argv[i]) + 2 < 256);
        strcpy (mainfilename, "./");
        strcat (mainfilename, argv[i]);
      }
      f= fopen (mainfilename, "r");
    }
  }
  if (!f) help (argv[0], false);
  if (!fout) fout= stdout;
  if (!cdebug) cdebug= new ofstream (STDERR_FILENO);

  init ();
  convert ();
  exit (0);
}

#endif

