/***********************************************************************/
/***********************************************************************/
/*                                                                     */
/*                    Tibetan Transcript Translator                    */
/*                                                                     */
/***********************************************************************/
/***********************************************************************/
/* 
   (C) Dec 1996 / Jan, Feb 1997 by 
   Beat Steiner, Brunngasse 12, 3011 Bern, Switzerland
   e-mail: Beat.Steiner@gseved.admin.ch
   Version 0.1 alpha prerelease


   LEGAL STUFF
   -----------
   Refer to GNU General Public License (feel free to use and distribute
   this program at your own risk but at no cost, keeping copyright 
   notices intact).

   
   DESCRIPTION
   -----------
   This program is intended to convert different transcript standards
   of the Tibetan language or even to perform a transcript import into 
   a document processing system.

   At present, it is able to convert transcripts to LaTeX, based
   on Jeff sparkes' and / or Sam Sirlin's fonts and ideas. Since I
   got into trouble to reverse engineer Sparkes' code, I have rewriten
   the complete transcript translator, only picking some ideas from his
   code.

   The main additional feature is fully automatic ligature generation,
   openness towards different transcript standards and inline LaTeX
   command support.

   The main disadvantages are: 
   ** Sparkes' and Sirlin's language mix handling is not supported yet.
   ** vovels on tsa, tsha, dza (resp. TZA, TSA, DZA) do not look nice. 

   WARNING: the ttd file format is still subject to change.
   If you create your own ttd files now, you risk to spend much work
   on updating them.


   INSTALLING
   ----------
   Install Sam Sirlin's fonts from the CTAN archive
   tex/language/tibetan/sirlin
   to the appropriate directory or set the Tex font path.
   Do the same with the tfm files. Look at the README file 
   for help on this. If you dont have metafont, use Jeff Sparkes' 
   pk and tfm files.

   This is a single file program which can be compiled without 
   complication by
   gcc ttt.c -o ttt
   or
   cc ttt.c -o ttt
   or whatever ANSI compliant C compiler you have.


   STARTING THIS PROGRAM
   ---------------------
   In order to translate ACIP to LaTeX, use

   ttt acip.ttd latex.ttd input.tib output.tex

   where acip.ttd   is the input  transcript definition file   
   .     latex.ttd  is the output transcript definition file
   .     input.tib  is your ACIP input text
   .     output.tex is the output file to be processed by latex
 
   On Linux, you will enjoy the feature of magic line support:
   In your input.tib file, add the following two lines at the
   VERY TOP (replacing the respective filenames):

   #!/bin/sh
   ttt acip.ttd latex.ttd $0 output.tex; latex output; exit

   Due to the magic line at the top, (ba)sh will think that your text 
   input.tib is a shell script, executing the 2nd line. 
   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
   This program will ignore the FIRST AND THE SECOND 
   line if the FIRST line starts with #!
   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
   Do not put anything, even not comments above these lines. 
   $0 is automatically replaced by the file name (e.g. input.tib).

   Then turn your input.tib file into an executable by
   chmod 755 input.tib

   Now, you can start this program by entering 
   input.tib
   at the shell command prompt.

   DOS users, please write an apropriate *.BAT file (without $0). 
   Windows users may set the command line options as program properties.


   RETURN VALUES
   -------------
   0: Normal program completion.
   1: x___error occurred (unknown input key, missing output ligature
   .                      and such) 
   2: Could not open a file or bad number of command line arguments
   3: Static memory allocation overrun


   IMPLEMENTATION NOTICE
   ---------------------
   This kind of implementation was motivated by the need of importing
   texts typed at Sera Monastery into the ClearLook document 
   processing system and solving other conversion problems like 
   differences between ACIP and Wiley transcripts plus the need to 
   import both of them into LaTeX, ClearLook, Lout and other programs. 
   
   Therefore, a very flexible solution based on Tibetan Transcript 
   Definition (ttd) files has been chosen. 
   They map the different transcribed letters, control sequences
   and ligatures (btags pa) to common keys.
   
   example: ACIP --> LaTeX conversion for the letter 'tsa
   the acip.ttd file says:
   c___tsa	TZ
   which associates the key 'tsa' to the value 'TZ'
   the tex.ttd file says:
   c___tsa	\tibetan\char16
   which associates the key 'tsa' to the tex font chracter.

   The magic characters before the transcript key have the following
   meaning:

   1st char:
   c: normal consonant
   p: potential prefix (negative bias for ligature generation)
   b: potential btagspa (simplified lower part of ligatureand ha, 
      positive bias for ligature generation)
   .: punctuation (Terminates syllable and suppresses tsheg)
   x: program control

   2nd char and 3rd char:
   reserved for generic ligature generation (join styles, 
   character substitution)

   4th char:
   _: just to increase readability of ttd file
   
   Meaning of x___ keys:

   General controls:
   x___preamble	     Will be at top of output without being asked for
   x___postamble     Will be at bottom of output without being asked for
   x___error	     Error message included to output file

   Generic ligatures:
   x___inner_box{    Encapsulation of individual characters (no vovels)
   x___inner_box}    
   x___outer_box{    Encapsulation of whole ligature without vovel
   x___outer_box}

   Mixed language handling (subject to change, not fully implemented yet)
   x___transp_word   Input will be put out without modification til space
   x___transp_char   Unmodified character passing
   x___transp_reg{   Unmodified region passing start
   x___transp_reg}   end
   x___transp_reg~   toggle


   WARNING: The values of the input ttd and the keys of the output
   ttd file MUST be unique, otherwise the result is undefined.
   
   The input stream is scanned for the largest matched value in the
   input ttd file, giving the common key. A lookup in the output ttd
   file generates the output stream.
   
   The ligature generation is a special issue documented at the 
   rather complicated if construct in the main function.


   To Do:
   ------

   ** Better char comparison in ttread
   ** Write documentation
   ** allow comments in ttd files
   ** Warn on ambiguous transcript (gyin, gyas, ...)
   ** Full support of Sirlin and Sparkes
   ** Mixed language support 
   Two flags allow mixing Tibetan, other languages and control sequences:
   'active' 	says wether or not the lookup and ligature generation
   .            take place or not. If not,
   'transparent' says wether the input stream is passed unmodified to
   .            the output stream or simply be ignored (crunched).

   ** Clean escape char handling for binary output
  
   ** More intelligence for Generic Ligatures
   ** Font: Character fragments for generic ligatures
   ** Vertically stretched (50% or 75%) chars for generic ligatures
   ** Mirrored gigu

   ALMOST DONE
   -----------
   ** Clean up and update comments and program style
   ** Read filenames from command line 
   .    (unflexible, no default handling, magic lines for linux only)
   ** Vovel support for generic ligatures (still a LaTeX problem to solve)
   ** (Only) two vovels on a character supported

   PERFORMANCE
   -----------
   The actual lookup process is performed by a binary search although
   a hash table lookup is expected to be about 5 times faster. This is
   just caused by my not being that much familiar with C++ and g++.
   Furtheron, bsearch is better documented and available on more 
   compilers.
   This program needs less than 1 second to produce 6 a4paper output pages
   on my 5x86 machine. So there are more important problems to solve now.
   */

/***********************************************************************/
/*                May this program contribute to the                   */
/*                  Dharma practice of many people                     */
/***********************************************************************/

/******************************/
/******************************/
/*                            */
/*        Includes            */
/*                            */
/******************************/
/******************************/

#include <stdio.h>
#include <stdlib.h>   /* contians qsort and bsearch */
#include <string.h>


/******************************/
/******************************/
/*                            */
/* Static Memory Allocations  */
/*                            */
/******************************/
/******************************/

const int MAX_SYL_SIZE_LINE = __LINE__ + 1;
#define MAX_SYL_SIZE 50
/* maximum characters per syllable (ligature complexity)
   very cheap and should be enough
   */

const int MAX_KEY_VAL_LENGTH_LINE = __LINE__ + 1;
#define MAX_KEY_VAL_LENGTH 44
/* maximum ttd key/value length
   critical for output preamble
   hint: try to include the preamble
   */

const int TTSIZE_LINE = __LINE__ + 1;
#define TTSIZE 300
/* transcript table size
   critical for predefined ligatures
   keep in mind that you add an appropriate command margin
   to the number of characters in your font.
   */

const int INPUT_LINEBUFFER_LENGTH_LINE = __LINE__ + 1; 
#define INPUT_LINEBUFFER_LENGTH 180
/* Maximum line length of input text file */


/******************************/
/******************************/
/*                            */
/*       Global stuff         */
/*                            */
/******************************/
/******************************/

typedef struct
{
  char key[MAX_KEY_VAL_LENGTH];
  char val[MAX_KEY_VAL_LENGTH];
} ttentry;

char ESCAPE_CHAR='\xff';
int KEY_LEN[MAX_KEY_VAL_LENGTH];
int X_ERROR_OCCURRED = 0;
static FILE *OUTTEXT;

/******************************/
/******************************/
/*                            */
/*         Functions          */
/*                            */
/******************************/
/******************************/

void trace(char *message)
{
  /*
  if (message == NULL)
    {
      fprintf(stderr, "<NULL>");
      fflush(stderr);
    }else{
      fprintf(stderr, "<%s>", message);
      fflush(stderr);
    }
  */
}


/******************************/

/* clean interface for strncmp */
int ttcmp(const void *first, const void *second) 
{
  return strncmp(((ttentry*)first)->key, ((ttentry*)second)->key, 
		 MAX_KEY_VAL_LENGTH); }


/******************************/

void ttprepare(ttentry ttable[])
{
  qsort(&ttable[0], TTSIZE, sizeof(ttentry), ttcmp);
}


/******************************/

char *ttlookup(const char *key, const ttentry ttable[])
{
  ttentry *found=NULL;

  if (key)
    {
      found=(ttentry*)bsearch(key, ttable, TTSIZE, 
			      sizeof(ttentry), ttcmp);

      if (found)
	{
	  return found->val;
	}else{
	  return NULL;
	}
    }
}


/******************************/

void ttread(const char *name, int swap, ttentry ttable[])
{
  /* This procedure reads the transcript definition files.
     The first column MUST be strictly left aligned.
     'name' is the filenamae
     'swap' is a flag which swaps the key/value reading order:
     file contents for swap=0:
     <key> <value>
     file contents for swap=1:
     <value> <key>
     'ttable' is the transcript table where the file is read into.
     'TTSIZE' is allocated statically and needed to avoid an 
     index overflow
     */
  char c = ' ';  /* not EOF */
  char *cp = NULL;
  char *cp_start = NULL;  /* Needed for ptr range check */
  FILE *ttd = NULL;
  int idx = 0;

  void CheckCP(void)
    {
      if ((cp - cp_start) >= (MAX_KEY_VAL_LENGTH - 1))
	{
	  fprintf(stderr, "Insufficient static memory allocation. ");
	  fprintf(stderr, "Please increase MAX_KEY_VAL_LENGTH in ");
	  fprintf(stderr, "line %d of %s and compile again.\n",
		  MAX_KEY_VAL_LENGTH_LINE, __FILE__);
	  exit(3);
	}
    }
  
  /* Clear array storing input ttd key length flags
     for boosting performance in charparse() */
  for (idx = 0; idx < MAX_KEY_VAL_LENGTH; idx++)
    {
      KEY_LEN[idx] = 0;
    }
  idx = 0;

  /* Clear transcript table struct arrays */
  for (idx = 0; idx < TTSIZE; idx++)
    {
      *ttable[idx].key = '\0';
      *ttable[idx].val = '\0';
    }
  idx = 0;


  if (!(ttd = fopen(name, "r") ))
    {
      fprintf(stderr, "%s %s\n", "could not open ttd file ", name);
      exit(2);
    }


  /* ev. better with strpbrk() */
  while (c != EOF && idx < TTSIZE)
    {
      /* Read ttd keys/values */
      if (swap) cp = ttable[idx].val;
      else      cp = ttable[idx].key;

      cp_start = cp;

      c = fgetc(ttd);
      while ( c != ' ' && c != '\t' && c != '\n' && c != EOF)
	{
	  *cp++ = c; CheckCP();
	  c=fgetc(ttd);
	}
      *cp++ = '\0'; CheckCP();

      /* Eliminate separtor garbage */
      while ( c == ' ' || c == '\t' )
	{ c = fgetc(ttd);
	}

      /* Read ttd values/keys */
      if (swap) cp = ttable[idx].key;
      else      cp = ttable[idx].val;

      cp_start = cp;
      while ( c != '\t' && c != '\n' && c != EOF ) 
	/* ev. add comment char and spaces */
	{ 
	  *cp++ = c; CheckCP();
	  c = fgetc(ttd);
	}
      *cp++ = '\0'; CheckCP();

      /* Eliminate trailing garbage */
      while ( c != '\n' && c != EOF )
	{ c = fgetc(ttd);
	}

      /* mark existing key lengths */
      KEY_LEN[strlen(ttable[idx].key)] = !0;

      idx++;
    }
  if (idx >= TTSIZE)
    {
      fprintf(stderr, "Insufficient memory allocation. Please increase");
      fprintf(stderr, 
	      " TTSIZE in line %d of %s and compile again.\n", 
	      TTSIZE_LINE, __FILE__);
      exit(3);
    }
}


/******************************/

void prepare_flags(ttentry inptrans[], ttentry outtrans[], 
		   int *active, int *transparent) 
{
  char *charptr;

  /* Set activity flag according to INPUT ttd */
  charptr=ttlookup("x___init_active", inptrans);
  if (charptr)
    {
      if ( strcasecmp("yes", charptr) == 0 )
	{ *active = 1;    }else{      *active = 0;
	}
    }

  /* Set transparency flag according to OUTPUT ttd */
  charptr=ttlookup("x___init_transprent", outtrans);
  if (charptr)
    {
      if ( strcasecmp("yes", charptr) == 0 )
	{ *transparent = 1;    }else{      *transparent = 0;
	}
    }


  /* Set escape character
     In this release, escapechar is only accepted if it is the first
     char in the output ttd */

  charptr=ttlookup("x___escape_char", outtrans);
  if (charptr)
    {
      ESCAPE_CHAR = *charptr;
    }
}


/******************************/

char *charparse(const char *input, int *parsestep,
                const ttentry *inptrans) 
{
  char yigcpy[MAX_KEY_VAL_LENGTH+1];
  char *charptr=NULL;
  int parselen;
  int yiglen;

  strncpy(yigcpy, input, MAX_KEY_VAL_LENGTH);
  parselen=strlen(input);
  if (parselen > MAX_KEY_VAL_LENGTH) parselen=MAX_KEY_VAL_LENGTH;
  yiglen=parselen;

  while (yiglen > 0 && charptr == NULL)
    {
      yigcpy[yiglen] = '\0';
      if (KEY_LEN[yiglen]) charptr = ttlookup(yigcpy, inptrans);
      yiglen--;
    }
  *parsestep=yiglen+1;
  return charptr;
}


/******************************/

void yigprint(char *yig, 
              ttentry *outtrans)
{
  char *charptr=NULL;
  if (yig != NULL)
    {
      charptr=ttlookup(yig, outtrans);
      if (charptr)
	{
	  /* DIRTY: detects escape char only if it is the first one. */
	  if (*charptr != ESCAPE_CHAR)
	    {
              if (*charptr != '\0') fprintf(OUTTEXT, "%s", charptr);
	      /* fflush(OUTTEXT); */
	    }else{
	      charptr++;	/* skip the escape char itself */
	      fprintf(OUTTEXT, "%c", (char)strtol(charptr, NULL, 0));
	      /* fflush(OUTTEXT); */
	    }
	}else{ /* no output ttd match */
	  X_ERROR_OCCURRED = !0;
	  fprintf(OUTTEXT, "%s", ttlookup("x___error", outtrans));
	  fflush(OUTTEXT);
	  trace(yig);
	}
    }else{
      /* trace("gugus"); */
    }
}

 
/******************************/

int vovel_left(char *syl[MAX_SYL_SIZE], int start, int syllength)
{
  int i;
  int result = 0;

  for (i=start; i<syllength; i++)
    {
      /* trace(syl[i]); */
      if (*syl[i] == 'v') result = !0;
    }
  return result;
}


/**********************************************************************/
/**********************************************************************/
/*********************                            *********************/
/*********************           Main             *********************/
/*********************                            *********************/
/**********************************************************************/
/**********************************************************************/

int main(int argc, char **argv)
{
  /* Trnscript tables
     TTSIZE is #defined at the static memory allocations at top of code
     */
  ttentry inptrans[TTSIZE];
  ttentry outtrans[TTSIZE];

  /* Filter activity flag */
  int active = 0; /* 0=non-Tibetan, 1=Tibetan text */

  /* Filter transparency flag */
  int transparent = 0; /* 0=crunch non-Tibetan, 1=pass non-Tibetan */

  int pending_tsheg = 0;        /* (c) by Jeff Sparkes */
  int pending_shad_space = 0;	/* (c) by Jeff Sparkes */

  /* Char & syllable buffers */
  char linebuf [INPUT_LINEBUFFER_LENGTH + 1];
  char inner_vovel_key_buf [MAX_KEY_VAL_LENGTH + 2];  
  char outer_vovel_key_buf [MAX_KEY_VAL_LENGTH + 2];
  char lig_key[MAX_SYL_SIZE * MAX_KEY_VAL_LENGTH];
  char *charptr  = NULL;
  char *parseptr = NULL;
  char *yig      = NULL;
/*  char *yigbuf   = NULL; */
  
  char *syllable[MAX_SYL_SIZE];
  int  sylindex;
  int  sylend;
  int  parsestep;
  int  i;
  int  geni;
  int  firstline = !0;
  int  skipline  = 0;

  char *active_start  = NULL;
  char *active_end    = NULL;
  char *active_toggle = NULL;

  int  active_start_len  = 0;
  int  active_end_len    = 0;
  int  active_toggle_len = 0;
  
  
  /* Input text file */
  static FILE *inptext;
  /* OUTTEXT is defined in the global section */


/******************************/
/*                            */
/*        Preparation         */
/*                            */
/******************************/


  if (argc != 5)
    {
      /* fprintf(stderr, "You gave %d arguments:\n", argc - 1);
	 fprintf(stderr, "Input  ttd: %s\n", argv[1]);
	 fprintf(stderr, "Output ttd: %s\n", argv[2]); */
      fprintf(stderr, 
	      "USAGE: %s input.ttd output.ttd input.text output.text\n",
	      argv[0]);
      exit(2);
    }


  /* Read transcript tables from files and prepare (sort) them
     for binary search. Input ttd MUST be read last in order to
     maintain the KEY_LEN array
     */
  ttread (argv[2], 0, outtrans);
  ttread (argv[1], 1, inptrans);
  ttprepare(inptrans);
  ttprepare(outtrans);


trace(ttlookup("c___ka", outtrans));

trace("prepare activity flags");

//  active_start      = ttlookup("x___active_in{", outtrans);
//  if (active_start  != NULL) active_start_len  = strlen(active_start);
//  trace(active_start);
//  active_end        = ttlookup("x___active_in}", outtrans);
//  if (active_end    != NULL) active_end_len    = strlen(active_end);
//  trace(active_end);
//  active_toggle     = ttlookup("x___active_in~", outtrans);
//  if (active_toggle != NULL) active_toggle_len = strlen(active_toggle);
//  trace(active_toggle);

  if (ttlookup("x___active_init", inptrans) != NULL)
    {
      active = !0;
    }else{
      active = 0;
    }

/******************************/
/*                            */
/*        Open Files          */
/*                            */
/******************************/


  /* Open input text file */
  if (strncmp(argv[3], "-", 2) != 0)
    {
      if (!( inptext = fopen(argv[3], "r") ))
	{
	  fprintf(stderr, "%s", "Could not open input text file");
	  exit(2);
	}
    }else{
      inptext = stdin;
    }
  
  /* Open output text file (outtext is global)*/
  if (strncmp(argv[4], "-", 2) != 0)
    {
      if (!( OUTTEXT = fopen(argv[4], "w") ))
	{
	  fprintf(stderr, "%s", "Could not open output text file");
	  exit(2);
	}
    }else{
      OUTTEXT = stdout;
    }


/******************************/
/*                            */
/*       Start Output         */
/*                            */
/******************************/

  /* Output preamble */
  yigprint("x___preamble", outtrans);
  
  prepare_flags(inptrans, outtrans, 
		&active, &transparent);  
  

  /* read input text file */
  while (fgets(linebuf, INPUT_LINEBUFFER_LENGTH, inptext) != NULL)
    {
      if (strlen(linebuf) >= (INPUT_LINEBUFFER_LENGTH-1))
	{
	  fprintf(stderr, "Input file contains too long lines. ");
	  fprintf(stderr, "Please keep them shorter than %d ",
		  INPUT_LINEBUFFER_LENGTH);
	  fprintf(stderr, "characters or increase ");
	  fprintf(stderr, "INPUT_LINEBUFFER_LENGTH in line %d of %s",
		  INPUT_LINEBUFFER_LENGTH_LINE, __FILE__);
	  fprintf(stderr, " and compile again.\n");
	  exit(3);
	}

      if (skipline)
	{
	  *linebuf = '\0';
	  skipline = 0;
	}

      /* Filter out magic lines for Linux */
      parseptr = linebuf + 1;
      if (firstline && (*linebuf == '#') && (*parseptr == '!'))
	{
	  *linebuf = '\0';
	  skipline = !0;
	}
      firstline = 0;
 
      parseptr = linebuf;
      
      /* parse until end of line */
      while (*parseptr != '\0' && *parseptr != '\n' && *parseptr != '\13')
	{
	  yig = charparse(parseptr, &parsestep, inptrans);
	  if (yig != NULL)
	    {
	      if (*yig == 'x')  trace(yig);

	      if (strncmp(yig, "x___active{", MAX_KEY_VAL_LENGTH) == 0)
		{
		  parseptr += parsestep;
		  active = !0;
		  if (ttlookup("x___active{", outtrans))
		    {
		      yigprint("x___active{", outtrans);
		    }
		}
	  
	      if (strncmp(yig, "x___active}", MAX_KEY_VAL_LENGTH) == 0)
		{
		  parseptr += parsestep;
		  active = 0;
		  if (ttlookup("x___active}", outtrans))
		    {
		      yigprint("x___active}", outtrans);
		    }
		}

	      if (strncmp(yig, "x___active~", MAX_KEY_VAL_LENGTH) == 0)
		{
		  parseptr += parsestep;
		  trace("active toggle");
		  active = !active;
		  if (active)
		    {
		      if (ttlookup("x___active{", outtrans))
			{
			  yigprint("x___active{", outtrans);
			}
		    }else{
		      if (ttlookup("x___active}", outtrans))
			{
			  yigprint("x___active}", outtrans);
			}
		    }
		}
	    }
	  if (active)
	    {	  
	      /*** read one syllable ***/
	      sylindex = 0;
	  
	      do{
		yig = charparse(parseptr, &parsestep, inptrans);
		if (yig == NULL)
		  {
		    sylend = !0;
		    if (   *parseptr == ' '  || *parseptr == '\x0D' 
			   || *parseptr == '\0' || *parseptr == '\n'
			   || *parseptr == '\t'   )
		      {
		      }else{
			X_ERROR_OCCURRED = !0;
			yigprint("x___error", outtrans);
			/* fprintf(OUTTEXT, "Input not defined"); */
		      }
		    parseptr++;
		  }else{
		    if (*yig != '.' && *yig != 'x')
		      {
			sylend = 0; 
		      }else{
			pending_tsheg = 0;
			sylend = !0;
			if (strncmp(yig, ".___shad", MAX_KEY_VAL_LENGTH) == 0)
			  {
			    pending_shad_space = !0;
			  }
			if (strncmp(yig, ".___tshegshad", 
				    MAX_KEY_VAL_LENGTH) == 0)
			  {
			    pending_shad_space = !0;
			  }
			if (strncmp(yig, "x___transp_line",
				    MAX_KEY_VAL_LENGTH) == 0)
			  {
			    while(!(   *parseptr == '\0'
				    || *parseptr == '\n'))
			      {
				fprintf(OUTTEXT, "%c", *parseptr++);
			      }
			  }
			if (strncmp(yig, "x___transp_word",
				    MAX_KEY_VAL_LENGTH) == 0)
			  {
			    while(!(*parseptr == ' '  
				    || *parseptr == '\x0D' 
				    || *parseptr == '\0'
				    || *parseptr == '\t'
				    || *parseptr == '\n'))
			      {
				fprintf(OUTTEXT, "%c", *parseptr++);
			      }
			  }
			if (strncmp(yig, "x___transp_char",
				    MAX_KEY_VAL_LENGTH) == 0)
			  {
			    fprintf(OUTTEXT, "%c", *parseptr++);
			  }
		      }
		    if (   *yig == 'v' || *yig == 'c' 
			   || *yig == 'p' || *yig == 'b')
		      {
			pending_tsheg = !0;
		      }
		    syllable[sylindex++] = yig;
		    if (parsestep > 0)
		      {
			parseptr += parsestep;
		      }else{
			X_ERROR_OCCURRED = !0;
			yigprint("x___error", outtrans);
			fprintf(OUTTEXT, "sylr 2");
			parseptr++;
		      }
		  }
	      }
	      while (!sylend);
	  
	      /*** trace("syllable read"); ***/
	  
	      /******************************/
	      /*                            */
	      /*     Syllable Analysis      */
	      /*                            */
	      /******************************/

	  
	      /* does syllable contain a prefix ? */
	      if (sylindex >= 3)		
		/* at least 2 chars + 1 (inherent)vovel */
		{
		  if (*syllable[0] == 'p'		
		      /* potential prefix marked as such in ttd */

		      && *syllable[1] != 'v'	
		      /* vovel? -> pot. prefix is main letter */

		      /* is pot. prefix part of a ligature? */
		      && !(strncmp(syllable[0], "p___'a", 
				   MAX_KEY_VAL_LENGTH) != 0  
			   /* 'a is NEVER upper part of a ligature */
			   /* ( I at least hope so ) */

			   && *syllable[1] == 'b'
			   /* ya, ra, la, wa subjoined to pot. prefix */

			   && *syllable[2] == 'v' 
			   /* ligature complete -> no prefix
			      examples: bya, gya ....
			      negative examples: brgyad ... */
		       
			   /* WARNING: gyin, gyas ... give wrong result 
			      (ambiguous transcript).
			      Work around this using the 
			      dummy vovel (usually '-').
			      Examples:  g-yin, g-yas, bu -ddha, ...
			      */                         
			   ) 
		      )
		    {
		      yigprint(syllable[0], outtrans);   /* output prefix */
		      for (i = 0; i < sylindex; i++)
			{
			  /* shift away prefix */
			  syllable[i] = syllable[i + 1]; 
			}
		      sylindex--;
		    }
		} /* min length for prefix */

	      /******************************/
	      /*                            */
	      /*     Ligature Generation    */
	      /*                            */
	      /******************************/

	      i = 0;
	      while (vovel_left(syllable, i, sylindex))
		{
		  if (*syllable[i] == 'v' && 
		      strncmp(syllable[i], "v___dummy", MAX_KEY_VAL_LENGTH) 
		      != 0)
		    {
		      strncpy(lig_key, "c___a", MAX_KEY_VAL_LENGTH);
		    }
		  else if(*syllable[i + 1] == 'v')
		    {
		      strncpy(lig_key, syllable[i++], MAX_KEY_VAL_LENGTH);
		    }
		  else
		    {
		      geni = i;
		      strncpy(lig_key, "l__", MAX_KEY_VAL_LENGTH);
		      while (i < sylindex && *syllable[i] != 'v')  
			/* compose ligature key */
			{
			  charptr = syllable[i]; 
			  charptr += 3; /* cut off type */
			  /* example p___ba + b___ya => l___ba_ya */

			  strncat(lig_key, charptr, MAX_KEY_VAL_LENGTH);
			  i++;
			}
		    }
		  strncpy(inner_vovel_key_buf, syllable[i++], 
			  MAX_KEY_VAL_LENGTH);

		  if (i < sylindex)
		    {
		      if (*syllable[i] == 'v')
			{
			  strncpy( outer_vovel_key_buf, syllable[i++], 
				   MAX_KEY_VAL_LENGTH);
			  strncat( outer_vovel_key_buf, "{", 
				   MAX_KEY_VAL_LENGTH);
			  yigprint(outer_vovel_key_buf, outtrans);
			}else{
			  *outer_vovel_key_buf = '\0';
			}
		    }else{
		      *outer_vovel_key_buf = '\0';
		    }
		  strncat( inner_vovel_key_buf, "{", MAX_KEY_VAL_LENGTH);
		  yigprint(inner_vovel_key_buf, outtrans);

		  if (ttlookup(lig_key, outtrans))
		    {
		      /* Predefined ligature */
		      yigprint(lig_key, outtrans);
		    }else{
		      /* Automatic ligature generation */
		      yigprint("x___outer_box{", outtrans);
		      while (*syllable[geni] != 'v')
			{
			  yigprint("x___inner_box{", outtrans);
		      
		      
			  /* Look for predefined two-letter ligatures 
			     within generic ligature */
			  charptr = NULL;
			  if (*syllable[geni+1] != 'v')  
			    {
			      strncpy(lig_key, "l__", MAX_KEY_VAL_LENGTH);
			      charptr = syllable[geni]; 
			      charptr += 3; 
			      strncat(lig_key, charptr, MAX_KEY_VAL_LENGTH);
			      charptr = syllable[geni+1]; 
			      charptr += 3; 
			      strncat(lig_key, charptr, MAX_KEY_VAL_LENGTH);
			      charptr = ttlookup(lig_key, outtrans);
			    }
		      
			  if (charptr)  
			    {
			      yigprint(lig_key, outtrans);
			      geni += 2;
			    }else{
			      /* Single-letter generic ligature part */
			      yigprint(syllable[geni++], outtrans);
			    }
			  yigprint("x___inner_box}", outtrans);
			}
		      yigprint("x___outer_box}", outtrans); 
		    }
	      
		  inner_vovel_key_buf[strlen(inner_vovel_key_buf) - 1] = '}';
		  yigprint(inner_vovel_key_buf, outtrans);

		  if (*outer_vovel_key_buf != '\0')
		    {
		      outer_vovel_key_buf[strlen(outer_vovel_key_buf) - 1]
			= '}';
		      yigprint(outer_vovel_key_buf, outtrans);
		    }

		} /* while vovel left */
	  
	      while (i < sylindex)		/* output suffix */
		{
		  yigprint(syllable[i], outtrans);
		  i++;
		}
	  
	      if (pending_tsheg)
		{
		  yigprint(".___tsheg", outtrans);
		  pending_tsheg = 0;
		}
	  
	      if (pending_shad_space && (*parseptr == ' ' 
					 || *parseptr == '\t'
					 || *parseptr == '\x0D' 
					 || *parseptr == '\0' 
					 || *parseptr == '\n'))
		{
		  yigprint(".___shad_space", outtrans);
		}
	      pending_shad_space = 0;     /* MUST be after '}' */
	  
	    }else{  /* not active */
//	      if (strncmp(parseptr, "%%", 2) == 0)
//		{
//		  active = !0;
	      fprintf(OUTTEXT, "%c", *parseptr++);
	    }
	  
	} /* while parseptr */
      
      fprintf(OUTTEXT, "\n");
      
    } /* while fgets */
  
  yigprint("x___postamble", outtrans);
  fprintf(OUTTEXT, "\n");

  if (X_ERROR_OCCURRED)
    {
      exit(1);
    }else{
      exit(0);
    }
}






















