/*
 ** Copyright (c) 1990,1991,1992,1993 Keld Simonsen
 ** All rights reserved.
 **
 ** Written by Keld Simonsen, RAP, Sct. Joergens Alle 8,
 **	      DK-1615 Copenhagen V, Denmark
 **
 ** Redistribution and use of this routine in source and binary forms are
 ** permitted provided that: (1) source distributions retain this entire
 ** copyright notice.  (2) The character and character set codes and names
 ** may not be changed. (3) The programming code must remain backwards
 ** compatible.
 **
 ** The restriction on altering of names is done so that all versons
 ** of the code have a chance of being compatible. If you want alterations
 ** or additions, please mail me (preferably email to  keld@dkuug.dk)
 ** and I will consider it for future releases.
 ** 
 ** If the material is included in commercial products, donations
 ** will be most appreciated.
 ** 
 ** Keld Simonsen
 **
 ** Code revised for better sendmail operation by Paul Pomes, University of
 ** Illinois.  
 */

/* 941125/TW: syserr deleted, replaced by syslog when neccessary.
   LOG_ALERT -> LOG_ERR
*/

#include "iso646.h"
#include <ctype.h>
#include <stdlib.h>	/* 941114 / TW */
#include <syslog.h>	/* 941125/TW */
#ifdef SENDMAIL
#include "sendmail.h"
#endif
#ifndef SENDMAIL
#include "charset.h"
#include <stdio.h>
#endif
/* configuration parameters */
#define debug  if (0) printf
#ifndef CHARFILE
#define CHARFILE "charsets.cpl"
  /* #define CHARFILE "/usr/lib/charsets.cpl" */
#endif
static  char	*charfile	= CHARFILE; /* static added 941125/TW */


# ifndef lint
static char RcsId[] = "@(#)$Header: /usr/local/src/mail/sendmail/ida/charset/RCS/strncnv.c,v 2.1 1991/04/05 17:11:49 paul Exp $";
# endif /* !lint */

/* extern char *charfile; Doppeldefiniert 941125/TW */

static FILE  *f = NULL;
/* static IN_CH *chset = NULL; NOT USED 941114 / TW */
static CHARSET *charsets = NULL;
static CHARTAB *chartabs = NULL;
struct chdbhdr   hdr;
static INT16S ot1 ARRAY(C256), ot2 ARRAY(C256);

/* FILE *dfopen();  / TW */
char *xalloc();
#ifndef newstr
char *newstr();
#endif /* newstr */

# ifdef __STDC__
static INT16S * getinch(CHARTAB *);
static CHAR8U * getoutch(CHARTAB *);
static CHARTAB * findchs(char *);
/*
   Changed references to inline / TW
	#  ifdef CC_WONT_PROMOTE
	static char upper(char);
	#  else * !CC_WONT_PROMOTE * 
	static char upper(int);
	#  endif * CC_WONT_PROMOTE  *
*/
# else /* !__STDC__ */
static INT16S * getinch();
static CHAR8U * getoutch();
static CHARTAB * findchs();
 /* static char upper(); TW */
# endif /* __STDC__ */
static int getchbas();
static INT32S ind;
#define		r_out	r_chset->cs->out
#define		s_in	s_chset->cs->in
#define		r_esca	r_chset->esc
#define		r_esc	r_chset->esc1
#define		s_esc	s_chset->esc1
#define		r_esc2	r_chset->esc2
#define		s_esc2	s_chset->esc2

/*
   **  strncnv -- convert a string from one character set to another.
   **
   **	Parameters:
   **		r_chset -- the character set to convert to.
   **		s_chset -- the character set to convert from.
   **		result -- the converted character string.
   **		source -- the character string to convert.
   **		n -- number of characters to convert
   **
   **	Returns:
   **		pointer to result
   **
   **	Side Effects:
   **		none.
   */

CHAR8U *
  strncnv(r_chset, s_chset, result, source, n)
register CHARSET *r_chset, *s_chset;
CHAR8U	 *result, *source;
int n;
{
  CHAR8U	c, c1, *r_end=result+n-4 , *save = result;
  /* 4 chars for ending: esc two-char nul */
  int	o;		/* intermediate binary value */
  /* INT16S	mnem; ... Unused ... 941114 / TW */
  int	oc; 		/* 2-byte output value */
  int 	bytes;
  int	gotesc2;
  
  bytes = r_chset->cs->outbytes; gotesc2= 0;
  o = 629562346; /* debug / TW */
  /* 
  while ((c = *source++) diff NULL && (result < r_end)) {
  may perhaps be assumed to mean :
  */
  while ((c = *source++) diff 0 && (result < r_end)) {
    debug("%c%c %.2x %.2x ",c,*source,c,*source);	
    if (c == s_esc and (! s_esc2 || s_esc2 == *source)) 
      {
	if (s_esc2) 
	  source++;
	/* Two esc in a row -> one escape
	 * If esc followed by defined mnemonic,
	 * next char is mnemonic.  */
	if (*source == s_esc and (! s_esc2 || s_esc2 == *(source+1))) 
	  {
	    if (s_esc2) 
	      gotesc2++;
	    o = *(s_in + s_esc);
	    /* handle 2 escs */
	    source++;
	    if (s_esc2) 
	      source++;
	    debug("in-esc ");
	  }
	else 
	  { /* mnemonic */
	    c = *source++;
	    if (c == '_') 
	      { 
		/* long ?? */ 
		o = 81369123;	/* TW */
	      }
	    else 
	      {
		c1 = *source++;
		o = *(ot1+*(s_in+c)) + *(ot2+*(s_in+c1));
		debug("in-mnem %d %d %d ",*(s_in+c),*(s_in+c1),o);
	      }
	  }
      }
    else 
      o = *(s_in + c);
    
    if (o == 629562346 || o ==  81369123) fprintf(stderr,"strncnv BUGG o=%d\n",o); /* TW */

    debug("out: o = %d %c%c  ",o,*(r_out+ (hdr.basechrs + o%hdr.basechrs)*bytes),*(r_out+ (hdr.basechrs + o/hdr.basechrs)*bytes));
    if (o > hdr.outsize) /* special handling */ 
      {
	if (o < hdr.begcombtabs ) /* long mnem */ 
	  {
	  }
	else 
	  { /* combtabs */
	    c1 = *source++;
	    debug("combtab %d %x %ld ",o,c1,o-hdr.begcombtabs+1); /* Changed last %d to %ld 941114 / TW */
	    o=(char)*(s_in + (o-hdr.begcombtabs+1)*C256 +c1);
	    debug("new-o %d ",o);
	  }
      }
    if (! o) 
      o = 141; /* if not defined: underline */
    
    if (bytes ==2) 
      oc= *((INT16S *) r_out + o);
    else 
      oc= *(r_out + o);
    if (gotesc2) {
      gotesc2= *(s_in+s_esc2);
      if (! gotesc2) 
	gotesc2= 141;
      debug("gotesc2 %d ",gotesc2); 
      if (bytes ==2) 
	gotesc2= *((INT16S *) r_out + gotesc2);
      else 
	gotesc2= *(r_out + gotesc2);
      oc = oc *C256 + gotesc2;
      gotesc2=0;
    }
    debug("o= %d %ld  oc=%x  ",o,r_esca,oc); /* ch: %d -> %ld 941114 / TW */
    if (oc == r_esca) 
      { /* write 2 escs */
	*result++ = r_esc;
	if (r_esc2) 
	  *result++ = r_esc2;
	*result++ = r_esc;
	if (r_esc2) 
	  *result++ = r_esc2;
	debug("esc %lx\n",r_esca); /* %x -> %lx 941114 / TW */
      }
    else 
      if (oc) 
	{
	  if (oc/C256) 
	    *result++ = oc>>8;
	  *result++ = oc%C256;
	  debug("normal ");
	}
      else 
	{
	  *result++ = r_esc;
	  if (r_esc2) 
	    *result++ = r_esc2;
	  if (bytes == 2) 
	    {
	      *result++ = *((INT16S *)r_out + hdr.basechrs + o%hdr.basechrs);
	      *result++ = *((INT16S *)r_out + hdr.basechrs + o/hdr.basechrs);
	    }
	  else 
	    {
	      *result++ = *(r_out + hdr.basechrs + o%hdr.basechrs);
	      *result++ = *(r_out + hdr.basechrs + o/hdr.basechrs);
	    }
	  debug("mnem %d ",o);
	}
    debug("\n");
  }
  *result++ = '\0';
  return (save);
}
/*
   **  getchset -- fetch the named character set.
   **
   **	First loop through the list of already loaded character sets.
   **	If found, return a pointer to it.  Otherwise read the set into
   **	memory and app}it to the CHARSET chain.
   **
   **	Parameters:
   **		s -- the name of the character set to get.
   **		esc -- the value for the out-of-band escape character.
   **
   **	Returns:
   **		pointer to character set or NULL on error.
   **
   **	Side Effects:
   **		mallocs memory for character set name and for the
   **		CHARSET structure.  Gradually increases size of
   **		running image if many sets are requested.
   */

CHARSET *
  getchset(s, esc)
char *s;
INT16S esc;
{
  /* register TW */ CHARTAB *t, *t1;
  /* register TW */ CHARSET *c, *c1;
  /* register TW */ char *p;
  
  /* Up case the character set name. */
  for (p = s; *p; p++) 
    if (islower(*p)) *p = toupper(*p); 
    /* Removed call to upper()  / TW */
  
  /* Load the character base file into chset. */
  if (getchbas() == 0) 
    return (NULL);
  
  /* Loop through the chain of character set names. */
  debug("in-core charsets: ");
  for (t = chartabs; t && strcmp(t->name, s); t = t->next)
    { 
      debug("%s ",t->name); 
      t1 = t; 
    }
  debug("\n");
  if (! t)
    {
      /*
       * The requested character set wasn't found on the in-memory chain.
       * Load it up from the disk file.
       */
      if((t = findchs(s)) == NULL)
	return(NULL); /* %x -> %lx below 941114 / TW */
      debug("bits=%d  g0= %lx  comb=%d intro=%d\n",t->bits,t->g0esc,t->combtabs,esc);
      t->next = NULL;
      t->name = newstr(s);
      t->in = getinch(t);
      if (! t->in)
	{
	  free(t);
	  return (NULL);
	}
      t->out = getoutch(t);
      if (chartabs) 
	t1->next = t;
      else 
	chartabs = t;
    }
  
  /* loop thru chain of charsets */
  for (c = charsets; c && ! (t == c->cs && esc == c->esc);
       c = c->next) c1 = c;
  if (! c) 
    {
      c = (CHARSET *) xalloc(sizeof (CHARSET));
      c->next = NULL;
      c->cs = t;
      c->esc = esc;
      if (esc/C256) 
	{
	  c->esc1 = esc/C256;
	  c->esc2 = esc%C256;
	}
      else 
	{
	  c->esc1 = esc;
	  c->esc2 = 0;
	}
      if (charsets) 
	c1->next = c;
      else 
	charsets = c;
    }
  return (c);
}
/*
   **  getchbas -- load character set base definition file
   **
   **	Load the character set base definition file and assign the
   **	global var chset to the {ning of it.
   **
   **	Parameters:
   **		none.
   **
   **	Returns:
   **		1 if file load successful or previously done, NULL
   **		on error.
   **
   **	Side Effects:
   **		mallocs storage that global chset points at.
   */
#ifdef	EMIL
extern	FILE *	char_fd;	/* /TW/ May be set in  emil */
#endif

static int
  getchbas()
{
  if (! hdr.offend || ! f || (fseek(f,hdr.offend,0) diff 0))
    {
#ifdef SENDMAIL_8
#ifdef	EMIL
      if ((f = char_fd) == NULL && (f = fopen(charfile, "r")) == NULL) /* TW */
#else
      if ((f = fopen(charfile, "r")) == NULL) /* TW */
#endif
#else
#ifdef	EMIL
	if ((f = char_fd) == NULL && (f = fopen(charfile,"rb")) == NULL) /* TW */
#else
	if ((f = fopen(charfile,"rb")) == NULL) /* TW */
#endif
#endif /* SENDMAIL_8 */
	  {
	    syslog(LOG_ERR, "getchbas: can't open charset def'n file %s: %m", charfile);
	    return (0);
	  }
    }

  if (! hdr.offend) 
    {
      /* Read header of the CHARFILE file with offsets etc. */
      if (fread(&hdr, sizeof (hdr), 1, f) diff 1)
	{
	  syslog(LOG_ERR, "getchbas: error reading header of charset def'n file %s: %m", charfile);
	  return (0);
	}
      fread(ot1, C256, 2, f);
      fread(ot2, C256, 2, f);
    }
  return (1);
}
/*
   **  findchs -- find a character set description
   **
   **	Parameters:
   **		charset -- the character set to load from.
   **
   **	Returns:
   **		pointer to result or NULL if error
   **
   **	Side Effects:
   **		allocates space for data read from files.
   */

static CHARTAB *
  findchs(s)
char *s;
{
  /* register TW */ CHARTAB *t;
  /* register TW */ char *p;
  char *buf;
  int i;
  
  /* Up case the character set name. */
  for (p = s; *p; p++) 
    if (islower(*p)) *p = toupper(*p); /* Removed call to upper()  / TW */
    					/* *p = upper(*p); */
  
  /* Loop through the chain of character set names. */
  for (t = chartabs; t && strcmp(t->name, s); t = t->next)
    ;
  
  if (! t) {
    debug("findchs: ! incore %s\n",s);
    buf = (char *) xalloc(hdr.sizdir);
    if (fseek(f,hdr.offdir,0))	/* (int) below added 941114 / TW */
      syslog(LOG_WARNING,
		"findchs: fseek error of charset name table %d, %m",
		(int) hdr.offdir);
    if (fread(buf, hdr.sizdir , 1, f) diff 1)
      {
	syslog(LOG_ERR, 
		"findchs: read error of charset name table: %d %d %m",
		(int) hdr.sizdir, (int) hdr.offdir); /* 941125/TW */
	return (NULL);
      }
    
    /* find charset name in chain, index = i */
    i= 0;
    for (p= buf; p < buf+hdr.sizdir and strcmp(s,p+1); p += *p+1)
      { 
	debug("%d,%s\n",*p,p+1); 
	i++; 
      }
    if (! (p < buf+hdr.sizdir)) 
      {
	syslog(LOG_ERR, "findchs: charset name not found %s: %m",s);
	return (NULL);
      }
    debug("findchs: found %*.*s\n",*p,*p,p+1);
    
    /* get charset header */
    fseek(f,i*sizeof(ind)+hdr.offdat,0);
    fread(&ind,sizeof(ind),1,f);
    fseek(f,ind,0);
    t = (CHARTAB *) xalloc(sizeof(CHARTAB));
    fread(t,sizeof(CHARTAB),1,f);
    free(buf);
  }
  return t;
}
/*
   **  getinch -- load a character set input translation table.
   **
   **	Parameters:
   **		charset -- the character set to load from.
   **
   **	Returns:
   **		pointer to result or NULL if error
   **
   **	Side Effects:
   **		allocates space for data read from files.
   */

static INT16S *
  getinch(t)
CHARTAB	*t;
{
  INT32S	sz,adr;
  IN_CH	*in;
  
  sz = C256 * 2 * (1+t->combtabs);
  if (t->bits == 16) 
    sz = 2 *C256*C256;
  if (t->bits == 14) 
    sz = 2 *128*128;
  in = (IN_CH *) xalloc(sz);
  adr = t->fileoffset+sizeof(CHARTAB)
    +hdr.outsize*t->outbytes*(1-t->createout);
  /* Assume in=value wanted instead of address (??) 941114 / TW */
  /* Also %d changed to %ld */
  /* debug("in=%so insz= %o adr=%o off=%o\n",in,sz, adr,t->fileoffset); */
  debug("in=%o insz= %lo adr=%lo off=%lo\n",*in,sz, adr,t->fileoffset);
  debug("getinch: in seek: %s %d\n",t->name,(fseek(f,adr,0))); 
  
  sz = fread(in, sz, 1, f);
  debug("feof %o ferror %o sz %ld \n",feof(f),ferror(f),sz); /* %d -> %ld 941114/TW */
  if (sz diff 1)
    {
      syslog(LOG_ERR, 
		"getinch: read of charset input translation table %s(%d): %m", 
		t->name, (int) sz);
      return (NULL);
    }
  /* *in = '\0'; */
  /* debug(" %c %d \n",c,*in);  */
  return (in);
}
/*
   **  getoutch -- load a character set output translation table.
   **
   **	Parameters:
   **		charset -- the character set to load from.
   **
   **	Returns:
   **		pointer to result or NULL if error
   **
   **	Side Effects:
   **		allocates space for data read from files.
   */

static CHAR8U *
  getoutch(t)
CHARTAB	*t;
{
  unsigned sz1;
  register int i;
  register OUT_CH	*out;
  register IN_CH *in= t->in;
  
  sz1 = hdr.outsize*t->outbytes;
  (void) fseek(f, t->fileoffset + sizeof (CHARTAB), 0);
  out = (OUT_CH *) xalloc(sz1 * sizeof (OUT_CH));
  /* Assuming value wanted instead of address 941114/TW */
  /* debug("out=%o osz= %o createout=%d\n",out,sz1,t->createout); */
  debug("out=%o osz= %o createout=%d\n",* out,sz1,t->createout);
  if (t->createout) 
    {
      if (! in) 
	return NULL;
      for (i=0; i<sz1; i++) 
	*(out+i) = 0;
      for (i=0; i<C256; i++) 
	if ( *(in+i) < sz1)
	  *(out+ *(in+i)) = i;
    }
  else 
    if (fread(out, sizeof (OUT_CH), sz1, f) diff sz1) 
      {
	syslog(LOG_ERR, "getoutch: read of charset output translation table %s: %m", t->name);
	return (NULL);
      }
  *out = '\0';
  return (out);
}
/*
   **  UPPER -- turn letter into upper case.
   **
   **	Parameters:
   **		c -- character to turn into upper case.
   **
   **	Returns:
   **		c, in upper case.
   **
   **	Side Effects:
   **		none.

        Not used, changed references to inline / TW

	static char
  	upper(c)
	register char c;
	{
  	return (islower(c) ? toupper(c) : c);
	}

   */

