// -*- 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 <iostream.h>
#include <fstream.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <assert.h>
#include <ht/stddef.h>
#include <ht/longchar.h>
#include <ht/longcharcpp.h>
#include <ht/array.h>
#include <ht/match.h>

#undef  PRINT
#define VERYMUCH   20000
#define VERYLITTLE (-VERYMUCH)

#define NONEXIST  -1

#define cat_o  'o'
#define cat_u  'u'
#define cat_b  'b'
#define cat_B  'B'
#define cat_U  'U'
#define cat_oo '1'
#define cat_uu '2'
#define cat_UU '3'

#define DEFCONFIGFILE "fuchletter.m4"
#define TMPFILE       "/tmp/tmp.fuchlig.config"

#define EW(E,X...) \
  ({ \
    sprintf (errtxt , ## X); \
    E(); \
  })

#define DEBUG(L,X...) \
  ({  if (cdebug && debuglevel>=L) \
        (*cdebug) << "debug "<<L<<": "<<X<<" "<<endl; \
  })

#define SETINDEX(A,I,E,V) \
  ({ longchar ___i= (I); \
     if (!isvalidlongchar(___i)) { \
       sprintf (errtxt, "capacity exceeded. long chars may " \
                        "range from 0 to %d only", MAXLONGCHAR); \
       E (); \
     }; \
     A[___i]= V; \
  })

#define MAXSTRINGLEN 1024

#define MAXTOKENLEN 256
typedef char tokenstr[MAXTOKENLEN];

#define _EI extern inline

LONGSTRINGCASEEQUALANDHASH
()

#define TRACE(X) X
#define ENTER(X) TRACE(DEBUG(19, "enter "<<entercounter++<< \
                 " L"<<__LINE__<<" "<<X))
#define EXIT(X)  TRACE(DEBUG(20, "exit  "<<--entercounter<< \
                 " L"<<__LINE__<<" "<<X))
#define PRINT(X) TRACE(DEBUG(21, "result "<<entercounter<<": "<<X))
#define NL()

extern char errtxt[256];

void Interror (const char *err=errtxt) _NORET_;
void interror (const char *err=errtxt) _NORET_;
void Error (const char *err=errtxt) _NORET_;
void error (const char *err=errtxt) _NORET_;

extern int      entercounter;
extern ofstream *cdebug;
extern int      debuglevel;

enum tokentype {
  space    = 0,
  comment  = 1,
  onechar  = 2,
  command  = 3,
  openenv  = 4,
  closeenv = 5,
  math     = 6,
  eof      = 7
};

enum fuchmode {
  fuchtext = 0,
  normtext = 1,
  mathtext = 2
};

#define TRANSPARENT 999999

struct abst {
  int lo,o,b,u,lu,U,LU;
};

struct fuchinfo {
  longchar code;               // im Zeichensatz, NONEXIST= gibt's nicht
  int      width;
  abst     left, right; // Maße
};

struct ttranslate {
  const longchar *store;
  const longchar *recurse;
  ttranslate (const longchar *astore, const longchar *arecurse):
    store(astore),
    recurse(arecurse)
  {}
};

#define START  0
#define F_NORMAL 1
#define END    2
struct letter {
  char *name;
  char poscat;
  fuchinfo form[4];
#define NORM  0
#define LEFT  1
#define RIGHT 2
#define BOTH  3
};

struct fuchstack {
  fuchstack *next;
  int       env;
  fuchmode  mode;

  fuchstack (fuchstack *anext, int aenv, fuchmode amode):
    next (anext), env (aenv), mode (amode) {}

  ~fuchstack ()
  {
    if (next) delete next;
  }
};

struct preliginfo {
  const longchar *input;
  int  inputlen; // zur Beschleunigung
  const longchar *output;
  int cut; // soviele werden aus output geschnitten bevor weitergesucht wird.
};

struct liginfo {
  longchar old;
  longchar c;
  longchar *kern;
};

#define MAXLONGCHAR   1024
  // Alles drüber wird ignoriert und kann nur aus Steuerzeichen benutzt
  // werden.

#define BRACKET     1024
  // ab hier sind die Zeichen wieder reserviert

#define USER        3000
  // ab hier darf der Benutzer eigene Zeichen definieren ohne das Programm
  // zu verwirren.

#define MAXWORDLEN  300

#define MAXPRELIG 256
extern preliginfo *prelig[MAXPRELIG];
extern int    anzprelig;

#define MAXREPLACE 256
extern preliginfo *replace[MAXREPLACE];
extern int    anzreplace;

//#define MAXCHTXT 100
//preliginfo *chtxt[MAXCHTXT];
//int    anzchtxt= 0;

#define MAXTABLE 300
extern letter *table[MAXTABLE];
extern int    anztable;

#define MAXLIG 1000
extern liginfo *lig[MAXLIG];
extern int    anzlig;

extern char   *configfile;

extern array<const longchar *, longchar> combines;
 // enthält die schon kombinierten neuen Buchstaben.

extern bool   vardeft;
extern int    varstart;
extern int    varend;
extern condition constr;
extern int    mindist;
extern letter *fuchchar[MAXLONGCHAR];
extern FILE   *f, *fout;
extern int    extrachar;
extern int    lx, ly;
#define maxoutx 78
extern int    outx;
extern bool   nofuch;    // nach %!F+
extern char   mypath[200];
extern int    lline;
extern longchar replacebuff[MAXWORDLEN], *prepl, *preplend;
extern longchar preligbuff[MAXWORDLEN], *prl, *prlend;
extern longchar hprelig[MAXWORDLEN];
extern longchar gluechar;
extern longchar combinecharA;
extern longchar combinecharZ;
extern char     *wordbox;
extern longchar *leftbox;
extern longchar *rightbox;
extern longchar *bothbox;
extern char     *fuchon;
extern char     *fuchoff;
extern ttranslate *translate[MAXLONGCHAR];

extern char fuchlettername[256];
extern char mainfilename[256];
extern char outfilename[256];

#define MAXCOMMENT 256
extern char com[MAXCOMMENT];

#define MAXCOMBINE    256
#define MAXCOMBINESTR 64
extern longchar combinebuff[MAXCOMBINE], *comb;
extern longchar combinecharcounter;
extern int      combinecount;
extern bool     empty;
extern const abst abstNULL;
extern const longchar *dash[5];
extern bool show;
#define DEFMAXREPLACE 10000
extern int maxreplace;

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

const char *Lstr (const longchar *c);
const char *Lstr2 (const longchar *c);
bool isvalidlongchar (longchar c);
void quit() _NORET_;
void Warning (const char *err=errtxt);
void warning (const char *err=errtxt);
void Info (const char *err=errtxt);
void info (const char *err=errtxt);
extern inline letter *FuchChar (longchar c) _CONST_;
extern inline letter *FuchCharErr (longchar c) _CONST_;
bool haslo (char cat);
bool haso (char cat);
bool hasb (char cat);
bool hasu (char cat);
bool haslu (char cat);
bool hasU (char cat);
bool hasLU (char cat);
int _getc (FILE *f);
void _ungetc (FILE *f, int c);
void parsecomment();
int nextword (FILE *f, tokenstr s);
longchar getcharacter(const char *t);
int nextstring (FILE *f, int (*isendmarker)(const char*),
                 longchar *s, int buffsize, int *info);
void B(const char *c, bool b);
int nextint (FILE *f);
longchar nextcharacter (FILE *f);
void readperhaps (FILE *f, int &i, bool bol);
void readsize (FILE *f, char poscat);
char *dupnextword (FILE *f);
longchar *Ldupnextword (FILE *f);
longchar *dupnextstring (FILE *f, int maxlen,
                      int (*isend)(const char *), int *info, int *r= NULL);
void readdash (FILE *f);
int isdotend (const char *c);
int isend (const char *c);
void readprelig (FILE *f);
void readreplace (FILE *f);
void readtranslate (FILE *f);
void readlig (FILE *f);
void readcombinechars (FILE *f);
void readgluechar (FILE *f);
void readwbox (FILE *f);
void readonestring (FILE *f, longchar *&a);
void readonestringc (FILE *f, char *&b);
void readvar (FILE *f);
constraint *nextconstraint(FILE *f);
bool isklammerauf (const char *c);
bool isklammerzu (const char *c);
bool isvar (longchar i);
constraint *interpretconstr (FILE *f, char *t);
void readconstraint (FILE *f);
void init ();
void push (fuchstack *&st, int env, fuchmode mode);
void pop (fuchstack *&st);
bool iscommandchar (char c);
char getonechar ();
char readchar ();
void unreadchar (char c);
void readTeXtoken (char *token, tokentype &type);
void outtext (const char *steuer, const char *inhalt, bool breakifness);
void outnl ();
extern int xlo, xo, xb, xu, xlu, xU, xLU;
#define MAXFUCH 1000
extern char     fuchpuff [MAXFUCH], *fc;
extern longchar old;
extern longchar sonder;
extern int      oldform;
void fuchstore_aux (longchar c);
void fuchstore (longchar c);
void fuchstore_direct (char c);
void fuchstorestr (const longchar *s);
void fuchstorestr_direct (const char *s);
void initfuch ();
void storebindeaux (int &rest, int length, const longchar *c);
int insertbinde (const abst &a, bool doinsert);
void changex (int &x, int a, int w);
void insertletter (longchar c, const fuchinfo &f, bool doinsert);
bool testlig (const fuchinfo &one, const fuchinfo &two);
bool isoben (const abst &a);
bool isunten (const abst &a);
bool isUNTEN (const abst &a);
bool isuntenorUNTEN (const abst &a);
bool isbeide (const abst &a);
bool isnuroben (const abst &a);
bool isnurunten (const abst &a);
bool touchimpossible (const abst &a, const abst &b);
const longchar *special (longchar old, longchar c);
void putzwischen (const abst &a, char poscat);
void insertfuch_aux (longchar c);
void findmatchprelig_aux (int ominlen,
                          const longchar *buff, preliginfo *&p, int &o);
void findmatchprelig (int ominlen, preliginfo *&p, int &vor, int &nach);
void adjust (int &x, int a, int w);
void combinecharright (longchar newc, letter *p, const longchar *string);
void combinecharleft (longchar newc, letter *p, const longchar *string);
void combinecharboth (longchar newc, letter *p, const longchar *string);
void newcharacter (longchar newc, longchar dir, const longchar *string);
longchar combinecombine ();
extern inline void insertcombine (longchar c);
void insertfuch_aux2 (longchar c);
int replaceonce ();
void replaceall ();
void insertfuch_aux3 (longchar c);
void flushprelig ();
void matchprelig (int minimalo);
void insertfuch (longchar c);
void flushfuch (); // darf (viel) zu oft aufgerufen werden
void startfuch ();
void stopfuch ();
void dofuch (fuchstack *&st, int envcount, fuchmode mode, bool &fuchfound);
void fuchaus (fuchstack *&st);
void convert ();
void help (const char *name, bool descr);
void showreplace ();
ostream &operator<<(ostream &, ttranslate*);
void showtranslate ();


