/*
	WARNING: This file was generated by dkct.
	Changes you make here will be lost if dkct is run again!
	You should modify the original source and run dkct on it.
	Original source: dk3uc2l.ctr
*/

/*
Copyright (C) 2011-2013, Dirk Krause

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice,
  this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above opyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.
* Neither the name of the author nor the names of contributors may be used
  to endorse or promote products derived from this software without specific
  prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**	@file dk3uc2l.c The dk3uc2l module.
*/


#line 116 "dk3uc2l.ctr"

#include "dk3all.h"




#line 121 "dk3uc2l.ctr"



/**	LaTeX commands to start and end math mode.
*/
static char const * const dk3uc2lat_mm[] = {
"\\(",
"\\)",
};


/**	Compare two package records.
	@param	l	Left record.
	@param	r	Right record or name.
	@param	cr	Comparison criteria (0=record/record, 1=record/name).
	@return	Comparison result.
*/
static
int
dk3uc2lat_pkg_compare(void *l, void *r, int cr)
{
  int back = 0;
  dk3_uc2lat_pkg_t	*pl;	/* Left pointer. */
  dk3_uc2lat_pkg_t	*pr;	/* Right pointer. */

  pl = (dk3_uc2lat_pkg_t *)l;
  pr = (dk3_uc2lat_pkg_t *)r;
  if(l) {
    if(r) {
      switch(cr) {
        case 1: {
	  if(pl->name) {
	    back = dk3str_c8_cmp(pl->name, (char const *)r);
	  } else { back = -1; }
	} break;
	default: {
	  if(pl->name) {
	    if(pr->name) {
	      back = dk3str_c8_cmp(pl->name, pr->name);
	    } else { back = 1; }
	  } else {
	    if(pr->name) { back = -1; }
	  }
	} break;
      }
    } else { back = 1; }
  } else {
    if(r) { back = -1; }
  }
  if(back < -1) { back = -1; }
  if(back >  1) { back =  1; }
  return back;
}



/**	Compare two UC to LaTeX directory structures.
	@param	l	Left structure.
	@param	r	Right structure/name.
	@param	cr	Comparison criteria (0=struct/struct, 1=struct/name).
	@return	Comparison result.
*/
static
int
dk3uc2lat_dir_compare(void *l, void *r, int cr)
{
  int back = 0;
  dk3_uc2lat_dir_t	*pl;	/* Left pointer. */
  dk3_uc2lat_dir_t	*pr;	/* Right pointer. */

  pl = (dk3_uc2lat_dir_t *)l;
  pr = (dk3_uc2lat_dir_t *)r;
  if(l) {
    if(r) {
      switch(cr) {
        case 1: {
	  if(pl->sn) {
	    back = dk3str_fncmp(pl->sn, (dkChar const *)r);
	  } else { back = -1; }
	} break;
	default: {
	  if(pl->sn) {
	    if(pr->sn) {
	      back = dk3str_fncmp(pl->sn, pr->sn);
	    } else { back = 1; }
	  } else {
	    if(pr->sn) { back = -1; }
	  }
	} break;
      }
    } else { back = 1; }
  } else {
    if(r) { back = -1; }
  }
  return back;
}



/**	Compare two range structures by start and end character.
	@param	l	Left structure.
	@param	r	Right structure/UC character.
	@param	cr	Comparison criteria (0=struct/struct, 1=struct/char).
	@return	Comparison result.
*/
static
int
dk3uc2lat_range_compare(void *l, void *r, int cr)
{
  int back = 0;
  dk3_uc2lat_range_t	*pl;	/* Left pointer. */
  dk3_uc2lat_range_t	*pr;	/* Right pointer. */
  dk3_c32_t		*ucptr;	/* Right pointer is a character pointer. */

  pl = (dk3_uc2lat_range_t *)l;
  pr = (dk3_uc2lat_range_t *)r;
  if(l) {
    if(r) {
      switch(cr) {
        case 1: {
	  ucptr = (dk3_c32_t *)r;
	  if(pl->start > (*ucptr)) {
	    back = 1;
	  } else {
	    if(pl->end < (*ucptr)) {
	      back = -1;
	    }
	  }
	} break;
	default: {
	  if(pl->start > pr->end) {
	    back = 1;
	  } else {
	    if(pl->end < pr->start) {
	      back = -1;
	    }
	  }
	} break;
      }
    } else { back = 1; }
  } else {
    if(r) { back = -1; }
  }
  return back;
}



/**	Destroy package structure, release memory.
	@param	pp	Structure to release.
*/
static
void
dk3uc2lat_pkg_delete(dk3_uc2lat_pkg_t *pp)
{
  

#line 277 "dk3uc2l.ctr"
  if(pp) {	

#line 278 "dk3uc2l.ctr"
    dk3_release(pp->name)
    pp->used = 0x00;
    dk3_delete(pp)
  } 

#line 282 "dk3uc2l.ctr"
}



/**	Create package structure, allocate memory.
	@param	pn	Package name.
	@param	app	Application structure for diagnostics.
	@return	Pointer to new structure on success, NULL on error.
*/
static
dk3_uc2lat_pkg_t *
dk3uc2lat_pkg_new(char const *pn, dk3_app_t *app)
{
  dk3_uc2lat_pkg_t *back = NULL;
  

#line 297 "dk3uc2l.ctr"
  back = dk3_new_app(dk3_uc2lat_pkg_t,1,app);
  if(back) {
    back->used = 0x00;
    back->name = dk3str_c8_dup_app(pn,app);
    if(!(back->name)) {
      dk3uc2lat_pkg_delete(back);
      back = NULL;
    }
  } 

#line 306 "dk3uc2l.ctr"
  return back;
}



/**	Release an array of string pointers.
	@param	ap	Array base address.
	@param	ns	Number of strings.
*/
static
void
dk3uc2lat_release_pointer_array(char const **ap, size_t ns)
{
  char const	**ptr;	/* Current string in array. */
  size_t	  i;	/* Current string index in array. */
  

#line 322 "dk3uc2l.ctr"
  ptr = ap; i = ns;
  while(i--) {
    dk3_release(*ptr)
    ptr++;
  } 

#line 327 "dk3uc2l.ctr"
  dk3_delete(ap)
}



/**	Destroy range structure, release memory.
	@param	rp	Structure to destroy.
*/
static
void
dk3uc2lat_range_delete(dk3_uc2lat_range_t *rp)
{
  size_t	numchar;	/* Number of characters in this range. */
  dkChar const	**ptr;		/* Release all descriptions. */
  size_t	i;		/* Number of descriptions to release. */
  

#line 343 "dk3uc2l.ctr"
  if(rp) {
    numchar = (size_t)(rp->end - rp->start + 1);
    if(numchar > 0) {
      rp->dir = NULL;
      if(rp->dsc) {
        ptr = rp->dsc;
	i = (size_t)(rp->end - rp->start + 1);
	while(i--) {
	  dk3_release(*ptr)
	  ptr++;
	}
      } rp->dsc = NULL;
      if(rp->a) {
        dk3uc2lat_release_pointer_array(rp->a, numchar);
      } rp->a = NULL;
      if(rp->t) {
        dk3uc2lat_release_pointer_array(rp->t, numchar);
      } rp->t = NULL;
      if(rp->m) {
        dk3uc2lat_release_pointer_array(rp->m, numchar);
      } rp->m = NULL;
      rp->start = (dk3_c32_t)0UL; rp->end = (dk3_c32_t)0UL;
      rp->ia = 0x00;
    }
    dk3_delete(rp)
  } 

#line 369 "dk3uc2l.ctr"
}



/**	Create range structure, allocate memory.
	@param	start	Start character of range.
	@param	end	End character of range.
	@param	dir	Directory structure (parent of this range).
	@param	app	Application structure for diagnostics.
	@return	Pointer to new range structure on success, NULL on error.
*/
static
dk3_uc2lat_range_t *
dk3uc2lat_range_new(
  dk3_c32_t start, dk3_c32_t end, dk3_uc2lat_dir_t *dir, dk3_app_t *app
)
{
  dk3_uc2lat_range_t	*back = NULL;
  char			range_text[32];		/* Buffer for number range. */
  dkChar		dk_range_text[32];	/* Buffer for number range. */
  

#line 390 "dk3uc2l.ctr"
  if(dk3app_max_log_level(app) >= DK3_LL_DEBUG) {
    sprintf(range_text, "%lx-%lx", (long)start, (long)end);
    (void)dk3str_c8_to_str_simple_app(dk_range_text, 32, range_text, app);
    dk3app_log_i3(app, DK3_LL_DEBUG, 238, 239, dk_range_text);
  }
  if(end >= start) {
    back = dk3_new_app(dk3_uc2lat_range_t,1,app);
    if(back) {
      back->dir = dir;
      back->dsc = NULL;
      back->a = NULL;
      back->t = NULL;
      back->m = NULL;
      back->p = NULL;
      back->start = start;
      back->end = end;
      back->ia = 0x00;
    }
  } else {
    if(app) {
      dk3app_log_i1(app, DK3_LL_ERROR, 220);
    }
  } 

#line 413 "dk3uc2l.ctr"
  return back;
}



/**	Destroy UC to LaTeX directory structure, release memory.
	@param	dp	Structure to destroy.
*/
static
void
dk3uc2lat_dir_delete(dk3_uc2lat_dir_t *dp)
{
  

#line 426 "dk3uc2l.ctr"
  if(dp) {
    if(dp->s_ran) {
      if(dp->i_ran) {
        dk3sto_it_close(dp->i_ran);
      } dp->i_ran = NULL;
      dk3sto_close(dp->s_ran);
    } dp->s_ran = NULL;
    dk3_release(dp->sn);
    dp->loaded = 0x00;
    dk3_delete(dp)
  } 

#line 437 "dk3uc2l.ctr"
}



/**	Create UC to LaTeX directory structure, allocate memory.
	@param	sn	Short directory file name.
	@param	app	Application structure for diagnostics.
	@return	Pointer to new directory structure on success, NULL on error.
*/
static
dk3_uc2lat_dir_t *
dk3uc2lat_dir_new(dkChar const *sn, dk3_app_t *app)
{
  dk3_uc2lat_dir_t *back = NULL;
  

#line 452 "dk3uc2l.ctr"
  back = dk3_new_app(dk3_uc2lat_dir_t,1,app);
  if(back) {
    back->s_ran = NULL; back->i_ran = NULL; back->sn = NULL;
    back->loaded = 0x00;
    back->s_ran = dk3sto_open_app(app);
    if(back->s_ran) {
      dk3sto_set_comp(back->s_ran, dk3uc2lat_range_compare, 0);
      back->i_ran = dk3sto_it_open(back->s_ran);
      if(back->i_ran) {
        back->sn = dk3str_dup_app(sn,app);
      }
    }
    if(!((back->s_ran) && (back->i_ran) && (back->sn))) {
      dk3uc2lat_dir_delete(back); back = NULL;
    }
  } 

#line 468 "dk3uc2l.ctr"
  return back;
}



/**	Process one of the directory input files.
	@param	ucp	Conversion structure.
	@param	fn	Full file name.
	@param	sn	Short file name.
	@return	1 on success, 0 on error.
*/
static
int
dk3uc2lat_read_dir_file(
  dk3_uc2lat_t *ucp, dkChar const *fn, dkChar const *sn
)
{
  int		 back = 0;
  dkChar const	*oldfilename = NULL;	/* Previous source file name. */
  unsigned long	 oldlineno = 0UL;	/* Previous line number. */
  unsigned long	 lineno = 0UL;		/* Current line number. */
  FILE		*fipo = NULL;		/* Input file. */
  char		 il[1024];		/* Input line. */
  dk3_uc2lat_dir_t	*dp;		/* Directory pointer. */
  char		*p1;			/* Range start. */
  char		*p2;			/* Range end. */
  long		 l1;			/* Range start. */
  long		 l2;			/* Range end. */
  dk3_uc2lat_range_t	ra;		/* Range for tests. */
  dk3_uc2lat_range_t	*rp;		/* Range pointer. */
  

#line 499 "dk3uc2l.ctr"
  /*
  	Save old source file position.
  */
  if(ucp->app) {
    oldfilename = dk3app_get_source_file(ucp->app);
    oldlineno = dk3app_get_source_line(ucp->app);
    dk3app_set_source_file(ucp->app, fn);
    dk3app_set_source_line(ucp->app, lineno);
  }
  /*
  	Process file.
  */
  dp = dk3uc2lat_dir_new(sn, ucp->app);
  if(dp) {
    if(dk3sto_add(ucp->s_dir, dp)) {
      fipo = dk3sf_fopen_app(fn, dk3app_not_localized(22), ucp->app);
      if(fipo) {
        back = 1;
        while(fgets(il, sizeof(il), fipo)) {
	  p1 = dk3str_c8_start(il, NULL);
	  if(p1) {
	    if(*p1 != '#') {
	      p2 = dk3str_c8_next(p1, NULL);
	      if(p2) {
	        if(sscanf(p1, "%lx", &l1) == 1) {
		  if(sscanf(p2, "%lx", &l2) == 1) {
		    /* Range recognized. */
		    ra.start = (dk3_c32_t)l1;
		    ra.end = (dk3_c32_t)l2;
		    rp = (dk3_uc2lat_range_t *)dk3sto_it_find_like(ucp->i_ran, (void *)(&ra), 0);
		    if(rp) {
		      if(ucp->app) {
		        dk3app_log_i1(ucp->app, DK3_LL_ERROR, 221);
		      }
		      back = 0;
		    } else {
		      rp = dk3uc2lat_range_new(ra.start, ra.end, dp, ucp->app);
		      if(rp) {
		        if(dk3sto_add(ucp->s_ran, (void *)rp)) {
			  if(!dk3sto_add(dp->s_ran, (void *)rp)) {
			    back = 0;
			  }
			} else {
			  back = 0;
			  dk3uc2lat_range_delete(rp); rp = NULL;
			}
		      }
		    }
		  } else {
		    if(ucp->app) {
		      dk3app_log_i1(ucp->app, DK3_LL_ERROR, 222);
		    }
		    back = 0;
		  }
		} else {
		  if(ucp->app) {
		    /* ERROR: Not a hex number. */
		    dk3app_log_i1(ucp->app, DK3_LL_ERROR, 222);
		  }
		  back = 0;
		}
	      } else {
	        if(ucp->app) {
	          /* Syntax error! */
		  dk3app_log_i1(ucp->app, DK3_LL_ERROR, 223);
		}
		back = 0;
	      }
	    } /* Ignore comments. */
	  } /* Ignore empty lines. */
	}
        fclose(fipo);
      }
    } else {
      dk3uc2lat_dir_delete(dp);
    }
  }
  /*
  	Restore old source file position.
  */
  if(ucp->app) {
    dk3app_set_source_file(ucp->app, oldfilename);
    dk3app_set_source_line(ucp->app, oldlineno);
  } 

#line 583 "dk3uc2l.ctr"
  return back;
}



/**	Read UC to LaTeX conversion directory.
	@param	ucp	Conversion structure.
	@return	1 on success, 0 on error.
*/
static
int
dk3uc2lat_read_directory(dk3_uc2lat_t *ucp)
{
  int			back = 0;
  int			found = 0;	/* Flag: At least one matching file. */
  int			res;		/* File processing result. */
  dkChar const		*sn;		/* Short file name. */
  dkChar const		*fn;		/* Full file name. */
  dkChar const		*sf;		/* File suffix. */
  dk3_dir_t		*dir;		/* Directory traversal structure. */
  

#line 604 "dk3uc2l.ctr"
  dir = dk3dir_open_app(ucp->dir, ucp->app);
  if(dir) {
    back = 1;
    while(dk3dir_get_next_file(dir)) {
      sn = dk3dir_get_shortname(dir);
      fn = dk3dir_get_fullname(dir);
      if((sn) && (fn)) {
        sf = dk3str_get_suffix(fn);
	if(sf) {
	  if(dk3str_fncmp(sf, dk3app_not_localized(54)) == 0) {
	    found = 1;
	    dk3app_log_i3(ucp->app, DK3_LL_DEBUG, 236, 237, fn);
	    res = dk3uc2lat_read_dir_file(ucp, fn, sn);
	    if(!res) {
	      back = 0;
	    }
	  } else {
	    /* Not a *.dir file, ignore this file. */
	  }
	} else {
	  /* No suffix, ignore this file. */
	}
      } else {
        /* BUG */
      }
    }
    dk3dir_close(dir);
  } 

#line 632 "dk3uc2l.ctr"
  return back;
}



int
dk3uc2lat_direct(dk3_c32_t c32)
{
  int back = 0;
  char c;	/* 8-bit character version of c32. */
  

#line 643 "dk3uc2l.ctr"
  if((unsigned long)c32 < 128UL) {
    c = (char)c32;
    switch(c) {
      case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
      case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
      case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
      case 'v': case 'w': case 'x': case 'y': case 'z': case 'A': case 'B':
      case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I':
      case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P':
      case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W':
      case 'X': case 'Y': case 'Z': case '0': case '1': case '2': case '3':
      case '4': case '5': case '6': case '7': case '8': case '9': case ',':
      case '.': case ':': case ';': case '+': case '-': case '?': case '!':
      case '|': case '@': case '(': case ')': case '/': case '=': case ' ':
      {
        back = 1;
      }
      break;
    }
  } 

#line 663 "dk3uc2l.ctr"
  return back;
}



dk3_uc2lat_t *
dk3uc2lat_open_app(dkChar const *d, int f_desc, int f_utf8, dk3_app_t *app)
{
  dk3_uc2lat_t *back = NULL;
  dkChar const	*dn;			/* Directory name pointer. */
  dkChar	 bu[DK3_MAX_PATH];	/* Buffer for directory. */
  int		 ok = 0;		/* Flag: Success. */
  dkChar 	*p1;			/* Start of preference value. */
  dkChar const	*p2;			/* Share directory. */
  

#line 678 "dk3uc2l.ctr"
  if((d) || (app)) {
    back = dk3_new_app(dk3_uc2lat_t,1,app);
    if(back) {
      /*
      	Initialize new structure.
      */
      back->dir = NULL;
      back->app = app;
      back->buf = NULL;
      back->s_ran = NULL;
      back->i_ran = NULL;
      back->s_dir = NULL;
      back->i_dir = NULL;
      back->s_pkg = NULL;
      back->i_pkg = NULL;
      back->rca   = NULL;
      back->szbuf = 0;
      back->f_dsc = f_desc;
      back->f_utf8 = f_utf8;
      /*
      	Search for directory name.
      */
      dn = NULL;
      if(d) {
        if(dk3str_len(d) < DK3_SIZEOF(bu,dkChar)) {
	  dk3str_cpy(bu, d);
	  dk3str_correct_filename(bu);
	  if(app) {
	    dk3app_log_i3(app, DK3_LL_DEBUG, 234, 235, bu);
	  }
	  if(dk3sf_is_dir_app(bu, app)) { dn = bu; }
	}
      }
      if((!(dn)) && (app)) {
      if(dk3app_get_pref(app,dk3app_not_localized(48),bu,DK3_SIZEOF(bu,dkChar)))
      {
        p1 = dk3str_start(bu, NULL);
	if(p1) {
	  dk3str_chomp(p1, NULL);
	  dk3str_correct_filename(p1);
	  if(app) {
	    dk3app_log_i3(app, DK3_LL_DEBUG, 234, 235, bu);
	  }
	  if(dk3sf_is_dir_app(p1, app)) { dn = p1; }
	}
      }
      }
      if((!(dn)) && (app)) {
        p2 = dk3app_get_sharedir(app);
	if(p2) {
	  if((dk3str_len(p2)) < DK3_SIZEOF(bu,dkChar)) {
	    dk3str_cpy(bu, p2);
	    p2 = dk3app_not_localized(49);
	    if((dk3str_len(p2) + dk3str_len(bu)) < DK3_SIZEOF(bu,dkChar))
	    {
	      dk3str_cat(bu, p2);
	      dk3str_correct_filename(bu);
	      if(app) {
	        dk3app_log_i3(app, DK3_LL_DEBUG, 234, 235, bu);
	      }
	      if(dk3sf_is_dir_app(bu, app)) { dn = bu; }
	    }
	  }
	}
      }
      /*
      	Allocate internal data structures.
      */
      ok = 0;
      if(dn) {
        if(app) {
	  dk3app_log_i3(app, DK3_LL_DEBUG, 232, 233, dn);
	}
        back->dir = dk3str_dup_app(dn, app);
	if(back->dir) {
	  back->buf = dk3_new_app(char,16,app);
	  if(back->buf) {
	    back->szbuf = 16;
	    back->s_ran = dk3sto_open_app(app);
	    if(back->s_ran) {
	      dk3sto_set_comp(back->s_ran, dk3uc2lat_range_compare, 0);
	      back->i_ran = dk3sto_it_open(back->s_ran);
	      if(back->i_ran) {
	        back->s_dir = dk3sto_open_app(app);
	        if(back->s_dir) {
		  dk3sto_set_comp(back->s_dir, dk3uc2lat_dir_compare, 0);
	          back->i_dir = dk3sto_it_open(back->s_dir);
		  if(back->i_dir) {
		    back->s_pkg = dk3sto_open_app(app);
		    if(back->s_pkg) {
		      dk3sto_set_comp(back->s_pkg, dk3uc2lat_pkg_compare, 0);
		      back->i_pkg = dk3sto_it_open(back->s_pkg);
		      if(back->i_pkg) {
		        ok = dk3uc2lat_read_directory(back);
		      }
		    }
		  }
	        }
	      }
	    }
	  }
	}
      }
      /*
      	Destroy object if initialization did not succeed.
      */
      if(!(ok)) {
        dk3uc2lat_close(back); back = NULL;
      }
    }
  } 

#line 789 "dk3uc2l.ctr"
  return back;
}



void
dk3uc2lat_close(dk3_uc2lat_t *u)
{
  dk3_uc2lat_range_t	*rp;	/* Range pointer. */
  dk3_uc2lat_dir_t	*dp;	/* Directory pointer. */
  dk3_uc2lat_pkg_t	*pp;	/* Package pointer. */
  

#line 801 "dk3uc2l.ctr"
  if(u) {
    if(u->s_ran) {
      if(u->i_ran) {
        dk3sto_it_reset(u->i_ran);
	while((rp = (dk3_uc2lat_range_t *)dk3sto_it_next(u->i_ran)) != NULL) {
	  /* DELETE RANGE */
	  dk3uc2lat_range_delete(rp);
	}
        dk3sto_it_close(u->i_ran);
      }
      dk3sto_close(u->s_ran);
    } u->s_ran = NULL; u->i_ran = NULL;
    if(u->s_dir) {
      if(u->i_dir) {
        dk3sto_it_reset(u->i_dir);
	while((dp = (dk3_uc2lat_dir_t *)dk3sto_it_next(u->i_dir)) != NULL) {
	  /* DELETE DIR */
	  dk3uc2lat_dir_delete(dp);
	}
        dk3sto_it_close(u->i_dir);
      }
      dk3sto_close(u->s_dir);
    } u->s_dir = NULL; u->i_dir = NULL;
    if(u->s_pkg) {
      if(u->i_pkg) {
        dk3sto_it_reset(u->i_pkg);
	while((pp = (dk3_uc2lat_pkg_t *)dk3sto_it_next(u->i_pkg)) != NULL) {
	  /* DELETE PACKAGE */
	  dk3uc2lat_pkg_delete(pp);
	}
        dk3sto_it_close(u->i_pkg);
      }
      dk3sto_close(u->s_pkg);
    } u->s_pkg = NULL; u->i_pkg = NULL;
    dk3_release(u->buf)
    dk3_release(u->dir)
    u->szbuf = 0;
    u->app = NULL;
    dk3_delete(u);
  } 

#line 841 "dk3uc2l.ctr"
}



/**	Initialize a range (allocate the array of pointers).
	@param	ra	Range to initialize.
	@param	f_desc	Flag: Allocate pointer array for descriptions too.
	@param	app	Application structure for diagnostics.
*/
static
void
dk3uc2lat_initialize_range(dk3_uc2lat_range_t *ra, int f_desc, dk3_app_t *app)
{
  size_t sz;	/* Number of elements in range. */
  size_t i;	/* Traverse range. */
  

#line 857 "dk3uc2l.ctr"
  sz = (size_t)(ra->end - ra->start + 1UL);
  ra->a = dk3_new_app(DK3_PCCHAR,sz,app);
  if(ra->a) { for(i = 0; i < sz; i++) { (ra->a)[i] = NULL; } }
  ra->t = dk3_new_app(DK3_PCCHAR,sz,app);
  if(ra->t) { for(i = 0; i < sz; i++) { (ra->t)[i] = NULL; } }
  ra->m = dk3_new_app(DK3_PCCHAR,sz,app);
  if(ra->m) { for(i = 0; i < sz; i++) { (ra->m)[i] = NULL; } }
  ra->p = dk3_new_app(dk3_uc2lat_pkg_ptr,sz,app);
  if(ra->p) { for(i = 0; i < sz; i++) { (ra->p)[i] = NULL; } }
  if(f_desc) {
    ra->dsc = dk3_new_app(DK3_PCDKCHAR,sz,app);
    if(ra->dsc) { for(i = 0; i < sz; i++) { (ra->dsc)[i] = NULL; } }
  }
  ra->ia = 0x01; 

#line 871 "dk3uc2l.ctr"
}



/**	Save text entry to pointer.
	@param	d	Address of destination pointer.
	@param	s	Source text.
	@param	a	Application structure for diagnostics.
	@param	c32	Character.
	@param	m	Flag: Math mode (probably used in error message).
*/
static
void
dk3uc2lat_save_text(
  char const **d, char const *s, dk3_app_t *a, dk3_c32_t c32, int m
)
{
  char const *nc;	/* New copy. */
  

#line 890 "dk3uc2l.ctr"
  nc = dk3str_c8_dup_app(s, a);
  if(nc) {	

#line 892 "dk3uc2l.ctr"
    if(*d) {
      dk3_release(*d);
      if(a) {
        /* ERROR: Redefinition for c32 in m mode! */
	dk3app_log_i1(a, DK3_LL_ERROR, 224);
      }
    }
    *d = nc;
  } 

#line 901 "dk3uc2l.ctr"
}



/**	Load data for one directory file.
	@param	u	Conversion object.
	@param	udir	Directory to load.
*/
static
void
dk3uc2lat_load_directory_data(dk3_uc2lat_t *u, dk3_uc2lat_dir_t *udir)
{
  dk3_uc2lat_range_t	*ra;	/* Current range to process. */
  dkChar		fnb[DK3_MAX_PATH];	/* Construct file name. */
  dkChar		*ptr;	/* File name suffix of directory file. */
  char			il[256];	/* Input line. */
  int			ok = 0;	/* Flag: Data file name ok. */
  dkChar const		*oldsourcename = NULL;	/* Old source file name. */	
  unsigned long		oldsourceline = 0UL;	/* Old source file line. */
  long			l;	/* Current 32-bit character. */
  dk3_c32_t		c32;	/* Current 32-bit character. */
  char			*p1;	/* Start of data line. */
  char			*p2;	/* Closing square bracket. */
  size_t		 sz;	/* File name length / data position index. */
  dk3_uc2lat_pkg_t	*pa;	/* Package. */
  FILE			*fipo;	/* Input file. */
  unsigned long		 lineno = 0UL;	/* Current line number. */

  

#line 930 "dk3uc2l.ctr"
  /*
  	Allocate memory to store the information.
  */
  dk3sto_it_reset(udir->i_ran);
  while((ra = (dk3_uc2lat_range_t *)dk3sto_it_next(udir->i_ran)) != NULL) {
    if(!(ra->ia)) {
      dk3uc2lat_initialize_range(ra, u->f_dsc, u->app);
    }
  }
  /*
  	Create file name for data file.
  */
  

#line 943 "dk3uc2l.ctr"
  dk3str_cpy(fnb, u->dir);		

#line 944 "dk3uc2l.ctr"
  sz = dk3str_len(fnb) + dk3str_len(dk3app_not_localized(20))
       + dk3str_len(udir->sn);
  if(sz < DK3_SIZEOF(fnb,dkChar)) {	

#line 947 "dk3uc2l.ctr"
    dk3str_cat(fnb, dk3app_not_localized(20));	

#line 948 "dk3uc2l.ctr"
    dk3str_cat(fnb, udir->sn);
    dk3str_correct_filename(fnb);
    ptr = dk3str_get_suffix(fnb);
    if(ptr) {
      *ptr = dkT('\0');			

#line 953 "dk3uc2l.ctr"
      sz = dk3str_len(fnb) + dk3str_len(dk3app_not_localized(55));
      if(sz < DK3_SIZEOF(fnb,dkChar)) {	

#line 955 "dk3uc2l.ctr"
        dk3str_cat(fnb, dk3app_not_localized(55));
	ok = 1;	

#line 957 "dk3uc2l.ctr"
      } else {				

#line 958 "dk3uc2l.ctr"
      }
    }
  } else {				

#line 961 "dk3uc2l.ctr"
  }
  /*
  	Load information from data file.
  */
  if(ok) {				

#line 966 "dk3uc2l.ctr"
    if(u->app) {
      oldsourcename = dk3app_get_source_file(u->app);
      oldsourceline = dk3app_get_source_line(u->app);
      dk3app_set_source_file(u->app, fnb);
      dk3app_set_source_line(u->app, 0UL);
      dk3app_log_i3(u->app, DK3_LL_DEBUG, 240, 241, fnb);
    }
    fipo = dk3sf_fopen_app(fnb, dk3app_not_localized(22), u->app);
    if(fipo) {				

#line 975 "dk3uc2l.ctr"
      ra = NULL; sz = 0;
      while(fgets(il, sizeof(il), fipo)) {
        lineno++; if(u->app) { dk3app_set_source_line(u->app, lineno); }
	dk3str_c8_delnl(il);		

#line 979 "dk3uc2l.ctr"
	p1 = dk3str_c8_start(il, NULL);
	if(p1) {			

#line 981 "dk3uc2l.ctr"
	  if(*p1 != '#') {		

#line 982 "dk3uc2l.ctr"
	    if(*p1 == '[') {		

#line 983 "dk3uc2l.ctr"
	      ra = NULL; sz = 0;
	      p1++;
	      p2 = dk3str_c8_chr(p1, ']');
	      if(p2) {
	        *p2 = '\0';
	      } else {
	        if(u->app) {
	          /* ERROR: Syntax, missing closing bracket. */
		  dk3app_log_i1(u->app, DK3_LL_ERROR, 223);
		}
	      }
	      if(sscanf(p1, "%lx", &l) == 1) {	

#line 995 "dk3uc2l.ctr"
	        c32 = (dk3_c32_t)l;
		ra = (dk3_uc2lat_range_t *)dk3sto_it_find_like(
		  u->i_ran, (void *)(&c32), 1
		);
		if(ra) {			

#line 1000 "dk3uc2l.ctr"
		  sz = (size_t)(c32 - ra->start);	/* Success. */
		} else {			

#line 1002 "dk3uc2l.ctr"
		  if(u->app) {
		    /* ERROR: Range not declared in directory. */
		    dk3app_log_i1(u->app, DK3_LL_ERROR, 225);
		  }
		}
	      } else {				

#line 1008 "dk3uc2l.ctr"
	        if(u->app) {
	          /* ERROR: Syntax, not a hex number. */
		  dk3app_log_i1(u->app, DK3_LL_ERROR, 222);
		}
	      }
	    } else {				

#line 1014 "dk3uc2l.ctr"
	      if(ra) {
	        if(!(ra->ia)) {			

#line 1016 "dk3uc2l.ctr"
		  dk3uc2lat_initialize_range(ra, u->f_dsc, u->app);
		}
	        switch(*p1) {
		  case 'l': {			

#line 1020 "dk3uc2l.ctr"
		    if(ra->a) {
		      p1++;
		      dk3uc2lat_save_text(&((ra->a)[sz]), p1, u->app, c32, 0);
		    }
		  } break;
		  case 't': {			

#line 1026 "dk3uc2l.ctr"
		    if(ra->t) {
		      p1++;
		      dk3uc2lat_save_text(&((ra->t)[sz]), p1, u->app, c32, 1);
		    }
		  } break;
		  case 'm': {			

#line 1032 "dk3uc2l.ctr"
		    if(ra->m) {
		      p1++;
		      dk3uc2lat_save_text(&((ra->m)[sz]), p1, u->app, c32, 2);
		    }
		  } break;
		  case 'p': {			

#line 1038 "dk3uc2l.ctr"
		    if(ra->p) {
		      p1++;
		      pa = (dk3_uc2lat_pkg_t *)dk3sto_it_find_like(
		        u->i_pkg, p1, 1
		      );
		      if(pa) {		

#line 1044 "dk3uc2l.ctr"
		        if((ra->p)[sz]) {
			  if(u->app) {
			    /* Warning: Overwriting package. */
			    dk3app_log_i1(u->app, DK3_LL_WARNING, 226);
			  }
			}
		        (ra->p)[sz] = pa;
		      } else {		

#line 1052 "dk3uc2l.ctr"
		        pa = dk3uc2lat_pkg_new(p1, u->app);
			if(pa) {	

#line 1054 "dk3uc2l.ctr"
			  if(dk3sto_add(u->s_pkg, pa)) {	

#line 1055 "dk3uc2l.ctr"
			    if((ra->p)[sz]) {
			      if(u->app) {
			        /* Warning: Overwriting package. */
				dk3app_log_i1(u->app, DK3_LL_WARNING, 226);
			      }
			    }
			    (ra->p)[sz] = pa;
			  } else {				

#line 1063 "dk3uc2l.ctr"
			    dk3uc2lat_pkg_delete(pa); pa = NULL;
			  }
			} else {	

#line 1066 "dk3uc2l.ctr"
			}
		      }
		    }
		  } break;
		}
	      } /* else: No range for character, already reported. */
	    }
	  } /* else: Ignore comment lines. */
	} /* else: Ignore empty lines. */
      }
      fclose(fipo);
    } else {				

#line 1078 "dk3uc2l.ctr"
    }
    if(u->app) {
      dk3app_set_source_file(u->app, oldsourcename);
      dk3app_set_source_line(u->app, oldsourceline);
    }
  }
  udir->loaded = 0x01; 

#line 1085 "dk3uc2l.ctr"
}



char const *
dk3uc2lat_get(dk3_uc2lat_t *u, dk3_c32_t c32, int ismath)
{
  char const		*back = NULL;
  size_t	 	sz;		/* Buffer length used. */
  dk3_uc2lat_pkg_t	*cpkg;		/* Current package. */
  

#line 1096 "dk3uc2l.ctr"
  if(u) {
    if(dk3uc2lat_direct(c32)) {
      if(u->f_utf8) {
        sz = dk3enc_uc2utf8(c32, (unsigned char *)(u->buf), u->szbuf);
	(u->buf)[sz] = '\0';
      } else {
        (u->buf)[0] = (char)c32;
	(u->buf)[1] = '\0';
      }
      back = u->buf;
    } else {
      if(u->rca) {
        if(u->rca->start > c32) {
	  u->rca = NULL;
	} else {
	  if(u->rca->end < c32) {
	    u->rca = NULL;
	  }
	}
      }
      if(!(u->rca)) {
        u->rca = (dk3_uc2lat_range_t *)dk3sto_it_find_like(
	  u->i_ran, (void *)(&c32), 1
	);
      }
      if(u->rca) {
        if(!(u->rca->ia)) {
	  dk3uc2lat_initialize_range(u->rca, u->f_dsc, u->app);
	}
	if(!(u->rca->dir->loaded)) {
	  dk3uc2lat_load_directory_data(u, u->rca->dir);
	}
	sz = (size_t)(c32 - u->rca->start);
	if(ismath) {
	  if(u->rca->m) {
	    back = (u->rca->m)[sz];
	  }
	  if(!(back)) {
	    if(u->rca->a) {
	      back = (u->rca->a)[sz];
	    }
	  }
	} else {
	  if(u->rca->t) {
	    back = (u->rca->t)[sz];
	  }
	  if(!(back)) {
	    if(u->rca->a) {
	      back = (u->rca->a)[sz];
	    }
	  }
	}
	cpkg = NULL;
	if(u->rca->p) {		

#line 1150 "dk3uc2l.ctr"
	  cpkg = (u->rca->p)[sz];
	  if(cpkg) {		

#line 1152 "dk3uc2l.ctr"
	    cpkg->used = 0x01;	

#line 1153 "dk3uc2l.ctr"
	  }
	}
      } else {
        if(u->app) {
          /* No such range in directory files. */
	  dk3app_log_i1(u->app, DK3_LL_WARNING, 225);
	}
      }
    }
  }
  

#line 1164 "dk3uc2l.ctr"
  return back;
}



/**	Write LaTeX encoding for one 32-bit character to stream.
	@param	u	LaTeX encoder.
	@param	st	Stream to write to.
	@param	c32	Character to write.
	@param	mm	Pointer to math mode flag variable.
	@return	1 on success, 0 on error.
*/
static
int
dk3uc2lat_stputc(dk3_uc2lat_t *u, dk3_stream_t *st, dk3_c32_t c32, int *mm)
{
  int back = 0;
  char const *string;	/* LaTeX encoding for c32. */

  string = dk3uc2lat_get(u, c32, 0);
  if(string) {
    if(*mm) {
      *mm = 0;
      dk3stream_c8_fputs(st, dk3uc2lat_mm[1]);
    }
    dk3stream_c8_fputs(st, string);
    back = 1;
  } else {
    string = dk3uc2lat_get(u, c32, 1);
    if(string) {
      if(*mm == 0) {
        *mm = 1;
	dk3stream_c8_fputs(st, dk3uc2lat_mm[0]);
      }
      dk3stream_c8_fputs(st, string);
      back = 1;
    }
  }
  return back;
}



int
dk3uc2lat_c8_plain_stputs(dk3_uc2lat_t *u, dk3_stream_t *st, char const *t)
{
  int 		back = 0;
  int		mm = 0;		/* Flag: In math mode. */
  char const	*ptr;		/* Current position. */
  dk3_c32_t	ul;		/* Output character. */
  char		c;		/* Current character. */
  

#line 1216 "dk3uc2l.ctr"
  if((u) && (st) && (t)) {
    ptr = t; back = 1;
    while(*ptr) {
      c = *ptr;
      ul = (unsigned long)c;
      ul &= 0x000000FFUL;
      if(!dk3uc2lat_stputc(u, st, ul, &mm)) { back = 0; }
      ptr++;
    }
    if(mm) {
      dk3stream_c8_fputs(st, dk3uc2lat_mm[1]);
    }
  }
  

#line 1230 "dk3uc2l.ctr"
  return back;
}



int
dk3uc2lat_c8_utf8_stputs(dk3_uc2lat_t *u, dk3_stream_t *st, char const *t)
{
  int				back = 0;
  int				mm = 0;	/* Flag: Math mode. */
  unsigned char const		*sp;	/* String pointer, current pos. */
  size_t			sl;	/* Remaining bytes. */
  size_t			used;	/* Bytes used for current c32. */
  dk3_c32_t			c32;	/* Current character. */
  

#line 1245 "dk3uc2l.ctr"
  if((u) && (st) && (t)) {
    sp = (unsigned char const *)t; sl = dk3str_c8_len(t); back = 1;
    while(sl) {
      used = 0;
      if(dk3enc_utf82uc(&c32, sp, sl, &used)) {
        if(!dk3uc2lat_stputc(u, st, c32, &mm)) {
	  back = 0;
	}
        if(used) {
	  if(sl >= used) {
	    sl = sl - used;
	    sp = &(sp[used]);
	  } else {
	    sl = 0; back = 0;
	  }
	} else {
	  sl = 0; back = 0; /* No byte used, will appear again and again. */
	}
      } else {
        sl = 0; back = 0;
	if(u->app) {
	  /* ERROR: Decoding failed! */
	  dk3app_log_i1(u->app, DK3_LL_ERROR, 118);
	}
      }
    }
    if(mm) {
      dk3stream_c8_fputs(st, dk3uc2lat_mm[1]);
    }
  }
  

#line 1276 "dk3uc2l.ctr"
  return back;
}



int
dk3uc2lat_c16_stputs(dk3_uc2lat_t *u, dk3_stream_t *st, dk3_c16_t const *t)
{
  int 			back = 0;
  int			mm = 0;	/* Flag: Math mode. */
  dk3_c16_t const	*sp;	/* Current position. */
  size_t		sl;	/* Number of 16-bit characters remaining. */
  size_t		used;	/* Number of 16-bit characters used. */
  dk3_c32_t		c32;	/* Current output character. */
  

#line 1291 "dk3uc2l.ctr"
  if((u) && (st) && (t)) {
    sp = t; sl = dk3str_c16_len(t); back = 1;
    while(sl) {
      used = 0;
      if(dk3enc_utf162uc(&c32, sp, sl, &used)) {
        if(used) {
	  if(!dk3uc2lat_stputc(u, st, c32, &mm)) { back = 0; }
	  if(sl >= used) {
	    sl = sl - used;
	    sp = &(sp[used]);
	  } else {
	    sl = 0;
	  }
	} else {
	  back = 0; sl = 0;
	  if(u->app) {
	    /* ERROR: Decoding */
	    dk3app_log_i1(u->app, DK3_LL_ERROR, 119);
	  }
	}
      } else {
        sl = 0; back = 0;
	if(u->app) {
	  /* Decoding error */
	  dk3app_log_i1(u->app, DK3_LL_ERROR, 119);
	}
      }
    }
    if(mm) {
      dk3stream_c8_fputs(st, dk3uc2lat_mm[1]);
    }
  }
  

#line 1324 "dk3uc2l.ctr"
  return back;
}



int
dk3uc2lat_c32_stputs(dk3_uc2lat_t *u, dk3_stream_t *st, dk3_c32_t const *t)
{
  int			back = 0;
  int			mm = 0;	/* Flag: Math mode. */
  dk3_c32_t const	*ptr;	/* Current position. */
  

#line 1336 "dk3uc2l.ctr"
  if((u) && (st) && (t)) {
    back = 1;
    ptr = t;
    while(*ptr) {
      if(!dk3uc2lat_stputc(u, st, *(ptr++), &mm)) {
        back = 0;
      }
    }
    if(mm) {
      dk3stream_c8_fputs(st, dk3uc2lat_mm[1]);
    }
  }
  

#line 1349 "dk3uc2l.ctr"
  return back;
}



int
dk3uc2lat_stputs(dk3_uc2lat_t *u, dk3_stream_t *st, dkChar const *t, int e)
{
  int back = 0;
  

#line 1359 "dk3uc2l.ctr"
#if DK3_CHAR_SIZE > 1
#if Dk3_CHAR_SIZE > 2
  back = dk3uc2lat_c32_stputs(u, st, t);
#else
  back = dk3uc2lat_c16_stputs(u, st, t);
#endif
#else
  switch(e) {
    case DK3_ENCODING_UTF8: {
      back = dk3uc2lat_c8_utf8_stputs(u, st, t);
    } break;
    default: {
      back = dk3uc2lat_c8_plain_stputs(u, st, t);
    } break;
  }
#endif
  

#line 1376 "dk3uc2l.ctr"
  return back;
}



void
dk3uc2lat_package_reset(dk3_uc2lat_t *u)
{
  

#line 1385 "dk3uc2l.ctr"
  if(u) {
    if(u->i_pkg) {
      dk3sto_it_reset(u->i_pkg);
    }
  }
  

#line 1391 "dk3uc2l.ctr"
}




dk3_uc2lat_pkg_t *
dk3uc2lat_package_next(dk3_uc2lat_t *u)
{
  dk3_uc2lat_pkg_t	*back = NULL;
  

#line 1401 "dk3uc2l.ctr"
  if(u) {
    if(u->i_pkg) {
      back = (dk3_uc2lat_pkg_t *)dk3sto_it_next(u->i_pkg);
    }
  }
  

#line 1407 "dk3uc2l.ctr"
  return back;
}



/* vim: set ai sw=2 : */
