#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "config.h"
#include "xutil.h"
#include "lutil.h"
#include "charset.h"
#include "mime.h"
#include "charconv.h"

#ifndef BUFSIZ
#define BUFSIZ 512
#endif

static int ctoi(s)
char *s;
{
	int i;

	if (!strncmp(s,"0x",2)) sscanf(s+2,"%x",&i);
	else if (*s == '0') sscanf(s,"%o",&i);
	else if (strspn(s,"0123456789") == strlen(s)) sscanf(s,"%d",&i);
	else i=0;
	return i;
}

static int getmaptab(maptab_table,maptab_name)
char **maptab_table;
char *maptab_name;
{
	FILE *fp;
	char buf[BUFSIZ],*p,*q;
	int in,on;

	debug(16,"getmaptab: %s\n",maptab_name);
	if ((fp=fopen(maptab_name,"r")) == NULL)
	{
		fprintf(stderr," cannot open mapchan file \"%s\" ",maptab_name);
		perror("");
		return 0;
	}

	while (fgets(buf,sizeof(buf)-1,fp))
	{
		p=strtok(buf," \t\n#");
		q=strtok(NULL," \t\n#");
		if (p && q)
		{
			in=ctoi(p);
			on=ctoi(q);
			if (in && on) ((char*)maptab_table)[in]=on;
		}
	}
	fclose(fp);

	return 0;
}

char *strnkconv(const char *src, int incode, int outcode, int n)
{
  char ki[10],ko[10];
  int kolen;
  static char *dest;
  int destlen;
  int i;

  outcode = getkcode(outcode, ki, ko);
  kolen = strlen(ko);

  dest = strkconv(src, incode, outcode);
  destlen = strlen(dest);

  if(destlen >= kolen && destlen > strlen(src)) {
    for(i = 0; i < kolen; i++)
      *(dest + n - 1 + i) = ko[i];
    *(dest + n) = '\0';
  }
  
  return dest;
}

char *strkconv(const char *src, int incode, int outcode)
{
  static char *dest;

  if ((incode==outcode) && (incode!=CHRS_NOTSET) && (incode!=CHRS_AUTODETECT))
	return (char *)src;

  if (!src)
	return NULL;

  if((incode == CHRS_AUTODETECT) || (incode == CHRS_NOTSET)) {
	if (LANG_BITS == 16) {
		incode = iso2022_detectcode((char *)src,incode);
	}
  }

/* if(dest)
	free(dest); */
/* FIXME: should be 
 * dest = (char *)malloc((strlen(src) + 1) + (6 * "number of \n + 1"));
 */
  dest = (char *)malloc(((strlen(src) + 1) * 2) + 6 );

  kconv((char *)src, &dest, incode, outcode);
  return dest;
}

void kconv(char *in, char **out, int incode, int outcode)
{
  char ki[10],ko[10];

  outcode = getkcode(outcode, ki, ko);
  if (incode == outcode) noconv(in,out);
  else {
    switch (incode) {
      case CHRS_NOTSET :	noconv(in,out); break;
      case CHRS_ASCII :		noconv(in,out); break;
      case CHRS_BIG5 :
        switch (outcode) {
          default :		  noconv(in,out); break;
        }
        break;
      case CHRS_CP424 :
        switch (outcode) {
          case CHRS_CP862 :	 eight2eight(in,out,CP424__CP862); break;
          case CHRS_ISO_8859_8 : eight2eight(in,out,CP424__ISO_8859_8); break;
          default :		 noconv(in,out); break;
        }
        break;
      case CHRS_CP437 :
        switch (outcode) {
          case CHRS_ISO_8859_1 :
          case CHRS_ISO_8859_15: eight2eight(in,out,CP437__ISO_8859_1); break;
          case CHRS_MACINTOSH :	 eight2eight(in,out,CP437__MACINTOSH); break;
          default : 		 noconv(in,out); break;
        }
        break;
      case CHRS_CP850 :
        switch (outcode) {
          case CHRS_ISO_8859_1 :
          case CHRS_ISO_8859_15: eight2eight(in,out,CP850__ISO_8859_1); break;
          case CHRS_MACINTOSH :  eight2eight(in,out,CP850__MACINTOSH); break;
          default :              noconv(in,out); break;
        }
        break;
      case CHRS_CP852 :
        switch (outcode) {
          case CHRS_FIDOMAZOVIA : eight2eight(in,out,CP852__FIDOMAZOVIA); break;
          case CHRS_ISO_8859_2 :  eight2eight(in,out,CP852__ISO_8859_2); break;
          default :		  noconv(in,out); break;
        }
        break;
      case CHRS_CP862 :
	switch (outcode) {
          case CHRS_CP424 :	 eight2eight(in,out,CP862__CP424); break;
	  case CHRS_ISO_8859_8 : eight2eight(in,out,CP862__ISO_8859_8); break;
	  default :		 noconv(in,out); break;
	}
	break;
      case CHRS_CP866 :
	switch (outcode) {
	  case CHRS_ISO_8859_5 : eight2eight(in,out,CP866__ISO_8859_5); break;
          case CHRS_KOI8_R :
          case CHRS_KOI8_U :	 eight2eight(in,out,CP866__KOI8); break;
          default :		 noconv(in,out); break;
        }
        break;
      case CHRS_CP895 :
        switch (outcode) {
          case CHRS_ISO_8859_2 : eight2eight(in,out,CP895__ISO_8859_2); break;
          case CHRS_CP437 :	 eight2eight(in,out,CP895__CP437); break;
          default :		 noconv(in,out); break;
        }
        break;
      case CHRS_EUC_JP :
        switch (outcode) {
          case CHRS_EUC_JP :	  euc2euc(in,out,incode,0); break;
          case CHRS_ISO_2022_JP : euc2seven(in,out,incode,ki,ko); break;
          case CHRS_NEC :	  euc2seven(in,out,incode,ki,ko); break;
          case CHRS_SJIS :	  euc2shift(in,out,incode,0); break;
          default :		  noconv(in,out); break;
        }
        break;
      case CHRS_EUC_KR :
        switch (outcode) {
          default :		 noconv(in,out); break;
        }
        break;
      case CHRS_FIDOMAZOVIA :
        switch (outcode) {
          case CHRS_CP852 : 	 eight2eight(in,out,FIDOMAZOVIA__CP852); break;
          case CHRS_ISO_8859_2 : eight2eight(in,out,FIDOMAZOVIA__ISO_8859_2); break;
          default :		 noconv(in,out); break;
        }
        break;
      case CHRS_GB :
        switch (outcode) {
          case CHRS_HZ :	gb2hz(in,out); break;
          default :		noconv(in,out); break;
        }
      case CHRS_HZ :
        switch (outcode) {
          case CHRS_GB :	hz2gb(in,out); break;
          default :		noconv(in,out); break;
        }
      case CHRS_ISO_11 :
        switch (outcode) {
          case CHRS_ISO_8859_1 :
          case CHRS_ISO_8859_15: eight2eight(in,out,ISO_11__ISO_8859_1); break;
          default :		 noconv(in,out); break;
        }
        break;
      case CHRS_ISO_4 :
        switch (outcode) {
          case CHRS_ISO_8859_1 :
          case CHRS_ISO_8859_15: eight2eight(in,out,ISO_4__ISO_8859_1); break;
          default :		 noconv(in,out); break;
        }
        break;
      case CHRS_ISO_60 :
        switch (outcode) {
          case CHRS_ISO_8859_1 :
          case CHRS_ISO_8859_15: eight2eight(in,out,ISO_60__ISO_8859_1); break;
          default :		 noconv(in,out); break;
        }
        break;
      case CHRS_ISO_2022_CN :
        switch (outcode) {
          default :		 noconv(in,out); break;
        }
        break;
      case CHRS_ISO_2022_JP :
        switch (outcode) {
          case CHRS_EUC_JP :	  seven2euc(in,out); break;
          case CHRS_ISO_2022_JP : seven2seven(in,out,ki,ko); break;
          case CHRS_NEC :	  seven2seven(in,out,ki,ko); break;
          case CHRS_SJIS :	  seven2shift(in,out); break;
          default :		  noconv(in,out); break;
        }
        break;
      case CHRS_ISO_2022_KR :
        switch (outcode) {
          default :		 noconv(in,out); break;
        }
        break;
      case CHRS_ISO_2022_TW :
        switch (outcode) {
          default :		 noconv(in,out); break;
        }
        break;
      case CHRS_ISO_8859_1 :
      case CHRS_ISO_8859_15:
        switch (outcode) {
	  case CHRS_CP437 :	 eight2eight(in,out,ISO_8859_1__CP437); break;
	  case CHRS_CP850 :	 eight2eight(in,out,ISO_8859_1__CP850); break;
          case CHRS_MACINTOSH :  eight2eight(in,out,ISO_8859_1__MACINTOSH); break;
          case CHRS_ISO_8859_1 :
          case CHRS_ISO_8859_15: noconv(in,out); break;
          default :		 noconv(in,out); break;
        }
        break;
      case CHRS_ISO_8859_2 :
        switch (outcode) {
          case CHRS_CP852 :	  eight2eight(in,out,ISO_8859_2__CP852); break;
          case CHRS_CP895 :	  eight2eight(in,out,ISO_8859_2__CP895); break;
          case CHRS_FIDOMAZOVIA : eight2eight(in,out,ISO_8859_2__FIDOMAZOVIA); break;
          default :		  noconv(in,out); break;
        }
        break;
      case CHRS_ISO_8859_3 :
        switch (outcode) {
          default :		noconv(in,out); break;
        }
        break;
      case CHRS_ISO_8859_4 :
        switch (outcode) {
          default :		 noconv(in,out); break;
        }
        break;
      case CHRS_ISO_8859_5 :
        switch (outcode) {
          case CHRS_CP866 :   eight2eight(in,out,ISO_8859_5__CP866); break;
          case CHRS_KOI8_R :
          case CHRS_KOI8_U :  eight2eight(in,out,ISO_8859_5__KOI8); break;
          case CHRS_MIK_CYR : eight2eight(in,out,ISO_8859_5__MIK_CYR); break;
          default :	      noconv(in,out); break;
        }
        break;
      case CHRS_ISO_8859_6 :
        switch (outcode) {
          default :		noconv(in,out); break;
        }
        break;
      case CHRS_ISO_8859_7 :
        switch (outcode) {
          default : 		noconv(in,out); break;
        }
        break;
      case CHRS_ISO_8859_8 :
        switch (outcode) {
          case CHRS_CP424 : eight2eight(in,out,ISO_8859_8__CP424); break;
          case CHRS_CP862 : eight2eight(in,out,ISO_8859_8__CP862); break;
          default :	    noconv(in,out); break;
        }
        break;
      case CHRS_ISO_8859_9 :
        switch (outcode) {
          default :		noconv(in,out); break;
        }
        break;
      case CHRS_ISO_8859_10 :
        switch (outcode) {
          default :             noconv(in,out); break;
        }
        break;
      case CHRS_ISO_8859_11 :
        switch (outcode) {
          default :             noconv(in,out); break;
        }
        break;
      case CHRS_KOI8_R :
      case CHRS_KOI8_U :
        switch (outcode) {
          case CHRS_CP866 :	 eight2eight(in,out,KOI8__CP866); break;
          case CHRS_ISO_8859_5 : eight2eight(in,out,KOI8__ISO_8859_5); break;
          case CHRS_MIK_CYR :	 eight2eight(in,out,KOI8__MIK_CYR); break;
          default :		 noconv(in,out); break;
        }
        break;
      case CHRS_MACINTOSH :
        switch (outcode) {
          case CHRS_CP437 :	 eight2eight(in,out,MACINTOSH__CP437); break;
	  case CHRS_CP850 :	 eight2eight(in,out,MACINTOSH__CP850); break;
          case CHRS_ISO_8859_1 :
          case CHRS_ISO_8859_15: eight2eight(in,out,MACINTOSH__ISO_8859_1); break;
          default :		 noconv(in,out); break;
        }
        break;
      case CHRS_MIK_CYR :
        switch (outcode) {
          case CHRS_ISO_8859_5 : eight2eight(in,out,MIK_CYR__ISO_8859_5); break;
          case CHRS_KOI8_R :
          case CHRS_KOI8_U :	 eight2eight(in,out,MIK_CYR__KOI8); break;
          default :		 noconv(in,out); break;
        }
        break;
      case CHRS_NEC :
        switch (outcode) {
          case CHRS_EUC_JP :	  seven2euc(in,out); break;
          case CHRS_ISO_2022_JP : seven2seven(in,out,ki,ko); break;
          case CHRS_NEC :	  seven2seven(in,out,ki,ko); break;
          case CHRS_SJIS :	  seven2shift(in,out); break;
          default :		  noconv(in,out); break;
        }
        break;
      case CHRS_SJIS :
        switch (outcode) {
          case CHRS_EUC_JP :	  shift2euc(in,out,incode,0); break;
          case CHRS_ISO_2022_JP : shift2seven(in,out,incode,ki,ko); break;
          case CHRS_NEC :	  shift2seven(in,out,incode,ki,ko); break;
          case CHRS_SJIS :	  shift2shift(in,out,incode,0); break;
          default :		  noconv(in,out); break;
        }
        break;
      case CHRS_UTF_7 :		utf7_to_eight(in,out,&outcode);
        break;
      case CHRS_UTF_8 :		utf8_to_eight(in,out,&outcode);
	break;

      case CHRS_ZW :
        switch (outcode) {
          case CHRS_HZ :	zw2hz(in,out); break;
          case CHRS_GB :	zw2gb(in,out); break;
          default :             noconv(in,out); break;
        }
        break;
      default :			noconv(in,out); break;
    }
  }
}

int getkcode(int code,char ki[],char ko[])
{
  if (code == CHRS_ISO_2022_CN) {
    strcpy(ki,"$A");
    strcpy(ko,"(T");
  }
  else if (code == CHRS_ISO_2022_JP) {
    strcpy(ki,"$B");
    strcpy(ko,"(B");
  }
  else if (code == CHRS_ISO_2022_KR) {
    strcpy(ki,"$(C");
    strcpy(ko,"(B");
  }
  else if (code == CHRS_ISO_2022_TW) {
    strcpy(ki,"$(G");
    strcpy(ko,"(B");
  }
  return code;	
}

int SkipESCSeq(FILE *in,int temp,int *intwobyte)
{
  int tempdata;

  tempdata = *intwobyte;
  if (temp == '$' || temp == '(')
    fgetc(in);
  if (temp == 'K' || temp == '$')
    *intwobyte = TRUE;
  else
    *intwobyte = FALSE;
  if (tempdata == *intwobyte)
    return FALSE;
  else
    return TRUE;
}

void noconv(char *in, char **out)
{
  char *p;

  p=*out;
  while (*in) *p++=*in++;
  *p='\0';

}

void eight2eight(char *in,char **out, char *filemap)
{
  char *p;
  int i;

  if (oldfilemap != filemap)
  {
	oldfilemap=filemap;
    	filemap=xstrcpy(maptabdir);
    	if (maptabdir[strlen(maptabdir)-1] != '/') filemap=xstrcat(filemap,"/");
	filemap=xstrcat(filemap,oldfilemap);

	for (i=0;i<256;i++)
		maptab[i] = (unsigned char)i;
/*
"\000\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017"
"\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037"
"\040\041\042\043\044\045\046\047\050\051\052\053\054\055\056\057"
"\060\061\062\063\064\065\066\067\070\071\072\073\074\075\076\077"
"\100\101\102\103\104\105\106\107\110\111\112\113\114\115\116\117"
"\120\121\122\123\124\125\126\127\130\131\132\133\134\135\136\137"
"\140\141\142\143\144\145\146\147\150\151\152\153\154\155\156\157"
"\160\161\162\163\164\165\166\167\170\171\172\173\174\175\176\177"
"\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217"
"\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237"
"\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257"
"\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277"
"\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317"
"\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337"
"\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357"
"\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377"
	};
*/

  	getmaptab(maptab,filemap);
  }	
  p=*out;
  while (*in)
  {
	*p=maptab[*in & 0xff];
	in++;
	p++;
  }
  *p='\0';

}

int iso2022_detectcode(char *in,int whatcode)
{
  int c=0;

  while (((whatcode == CHRS_NOTSET) || 
          (whatcode==CHRS_AUTODETECT)) &&
         (*in)) {
    if ((c = (unsigned int)(*in++))) {
      if (c == ESC) {
        c = (unsigned int)(*in++);
        if (c == '$') {
          c = (unsigned int)(*in++);
          switch (c) {
	    case 'A' :	whatcode = CHRS_ISO_2022_CN; break;
	    case 'B' :
	    case '@' :	whatcode = CHRS_ISO_2022_JP; break;
            case '(' :
	    case ')' : {
          	c = (unsigned int)(*in++);
          	switch (c) {
			case 'A' :	whatcode = CHRS_ISO_2022_CN; break;
			case 'C' :	whatcode = CHRS_ISO_2022_KR; break;
			case 'D' :	whatcode = CHRS_ISO_2022_JP; break;
			case 'E' :	whatcode = CHRS_ISO_2022_CN; break;
			case 'G' :
			case 'H' :
			case 'I' :
			case 'J' :
			case 'K' :
			case 'L' :
			case 'M' :	whatcode = CHRS_ISO_2022_TW; break;
			case 'X' :	whatcode = CHRS_ISO_2022_CN; break;
			default:	break;
		} } break;
	    case '*' : {
                c = (unsigned int)(*in++);
                switch (c) {
			case 'H' :
			case 'X' :	whatcode = CHRS_ISO_2022_CN; break;
			default:	break;
		} } break;
	    case '+' : {
                c = (unsigned int)(*in++);
                switch (c) {
			case 'H' :
			case 'I' :
                        case 'J' :
                        case 'K' :
                        case 'L' :
                        case 'M' :
                        case 'X' :      whatcode = CHRS_ISO_2022_CN; break;
                        default:        break;
                } } break;
	    default:	break;
            }
          }
      }
      else if (whatcode == CHRS_NOTSET)
	return whatcode;
#if (LANG_DEFAULT == LANG_JAPAN)
      else if ((c >= 129 && c <= 141) || (c >= 143 && c <= 159))
        whatcode = CHRS_SJIS;
      else if (c == 142) {
        c = (unsigned int)(*in++);
        if ((c >= 64 && c <= 126) || (c >= 128 && c <= 160) || (c >= 224 && c <= 252))
          whatcode = CHRS_SJIS;
        else if (c >= 161 && c <= 223)
          whatcode = CHRS_AUTODETECT;
      }
      else if (c >= 161 && c <= 223) {
        c = (unsigned int)(*in++);
        if (c >= 240 && c <= 254)
          whatcode = CHRS_EUC_JP;
        else if (c >= 161 && c <= 223)
          whatcode = CHRS_AUTODETECT;
        else if (c >= 224 && c <= 239) {
          whatcode = CHRS_AUTODETECT;
          while (c >= 64 && c != EOF && whatcode == CHRS_AUTODETECT) {
            if (c >= 129) {
              if (c <= 141 || (c >= 143 && c <= 159))
                whatcode = CHRS_SJIS;
              else if (c >= 253 && c <= 254)
                whatcode = CHRS_EUC_JP;
            }
            c = (unsigned int)(*in++);
          }
        }
        else if (c <= 159)
          whatcode = CHRS_SJIS;
      }
      else if (c >= 240 && c <= 254)
        whatcode = CHRS_EUC_JP;
      else if (c >= 224 && c <= 239) {
        c = (unsigned int)(*in++);
        if ((c >= 64 && c <= 126) || (c >= 128 && c <= 160))
          whatcode = CHRS_SJIS;
        else if (c >= 253 && c <= 254)
          whatcode = CHRS_EUC_JP;
        else if (c >= 161 && c <= 252)
          whatcode = CHRS_AUTODETECT;
      }
#endif /* (LANG_DEFAULT == LANG_JAPAN) */
    }
  }
  return whatcode;
}


char *hdrnconv(s, incode, outcode, n)
char *s;
int incode, outcode, n;
{
	char ki[10],ko[10];
	int kolen;
	static char *dest;
	int destlen;
	int i;

	getkcode(outcode, ki, ko);
	kolen = strlen(ko);
	dest = hdrconv(s, incode, outcode);
	destlen = strlen(dest);

	if(destlen >= kolen && destlen > n) {
		for(i = 0; i < kolen; i++)
			*(dest + n - 1 - kolen + i) = ko[i];
		*(dest + n) = '\0';
	}

	return dest;
}

char *hdrconv(s, incode, outcode)
char *s;
int incode, outcode;
{
#define BCODAGE		1
#define QCODAGE		2

	char tbuf[1024];
	char *iptr, *tptr;
	char *xbuf=NULL, *buf=NULL, *q;
	int codage;

	iptr = s;
	while (*iptr) {
		if (!strncmp(iptr,"=?",2)) {
			q=strchr(iptr+2,'?');
			if (q) {
				incode=getcode(iptr+2);
				if (incode==CHRS_NOTSET) return s;
				iptr=q;
			} else {
				return s;
			}
			if (!strncasecmp(iptr,"?Q?",3)) {
				codage = QCODAGE;
				iptr+=3;
			}
			else if (!strncasecmp(iptr,"?B?",3)) {
				codage = BCODAGE;
				iptr+=3;
			}
			else {
				iptr=xstrcpy(iptr);
				*(iptr+3)='\0';
				loginf("mimehdr_decode: unknown codage %s",iptr);
				return s;
			}
			tptr = tbuf;
			while ((*iptr) && (strncmp(iptr,"?=",2))) *tptr++ = *iptr++;
			*tptr = '\0';
			if (!strncmp(iptr,"?=",2)) { iptr++; iptr++; }
			if (codage==QCODAGE) {
				while ((q = strchr(tbuf, '_'))) *q=' ';
				xbuf=xstrcat(xbuf,qp_decode(tbuf));
			}
			else if (codage==BCODAGE) {
				xbuf=xstrcat(xbuf,b64_decode(tbuf));
			}
                }
		else /* not coded */
		{
			*tbuf=*iptr;
			*(tbuf+1)='\0';
			xbuf=xstrcat(xbuf,tbuf);
			iptr++;
		}
	}
	buf=strkconv(xbuf, incode, outcode);
	return buf;
}

