/* wv1522.cpp V 1.5  1997/03/20 00:00:00 heirich */
/*********************************************************************
 *
 * Module: WV1522.CPP
 *
 * send bugreports to <winvn@heirich.in-berlin.de>
 *
 * Purpose: Support for RFC 1522
 * Version 1.7
 *********************************************************************/
/*********************************************************************
 * Notes:
 *
 * there is a flag strictRFC in class WVRFCDecoder
 * if TRUE decoding works like defined in RFC1522
 * if FALSE a modification is done for postings created
 * by Netscape ( Modzilla ) 2.01
 * Netscape doesn't fit RFC1522 because it creates
 * f=?ISO-8859-1?Q?=FC?=r instead =?ISO-8859-1?Q?f=FCr?= which is correct for "für"
 *
 * the classes don't make full syntax-analyses of headers by RFC822
 * BE WARNED: if you use non-ASCII-characters in a part which should not
 * contains these characters, it will work wrong
 *********************************************************************/
/*********************************************************************
 * Changelog:   E = encoder / D = decoder
 *
 * V 1.7	fixed following bugs
 *	      E -during encoding the string expands in length, this causes some
 *		overwrite of data if string isn't long enougth
 *		using WVString class this caused GPF because memory pointers will be
 *		overwritten.
 *
 * V 1.6        fixed following bugs
 * not fixed  D -by RFC is said, that if only ASCII chars in encoded text for ISO-8859-X
 *              we should show this as normal
 * not fixed  D -quoted pairs are not handled properly
 * not fixed  E -a word which begins with =? and ends with ?= should always encoded
 *              using charset US-ASCII if no other need to encode. see chapter 7 of RFC
 * not fixed  E -maybe in text fields like subject "(" and ")" doesn't enclose
 *              a comment. So there is *no* word boundary.
 * not fixed  E -enshure that long words ( > 75 characters after encoding )
 *              handled properly
 * not fixed  E -enshure that the 76 character max line length defined in RFC
 *              is handled properly
 * not fixed  D -enshure that folding ( encoded words delimited by CRLF SPACE )
 *              handled properly
 * not fixed  E -encoding of Ü(was: A) works not properly
 *              maybe we should insert folding with CRLF SPACE before (
 *
 * V 1.5
 *
 * 1997/05/19 D -Error on aborting when unknown charset, status must changed
 *              as last statement when all abort conditions are checked.
 * 
 * 1997/05/03 D -don't decode if it contains 0x0A or 0x0D
 *
 * V 1.4        -added support for the new WVString-class
 *              introduced with 0.99.9 of WinVN 
 *
 * V 1.3        new layout of classes to make it usefull for other purposes
 * 1996/09/05   -counting of nonASCII-characters is done in the new RFCFIFO class
 *              instead of FIFO
 * 1996/09/05   -make the most work of quoted-printable decoding in a internal class
 *              WVQIDecoder, but not the RFC1522 special replacement of _ with 0x20
 *              this is done in WVQDecoder. so we are able to put RFC1521 quoted-printable
 *              into this class ( or a inherited from this ) and take it from.
 * 1996/09/05   -modify it to be able to use in a UNICODE environment
 *              using TCHAR.H
 * 1996/09/04   -make able to encode and decode charsets with 2 byte width ( UNICODE )
 *              by my own additional implementation. THIS DOESN'T FIT IN ANY RFC !
 *
 *              fixed following bugs
 * 1996/09/04   -multiword- encoding in B doesn't work properly. There was a markup of stream
 *              after every word, but we should this only done after all words.
 *
 * V 1.2        fixed following bugs
 * 1996/08/24 D -fixed that a sequence =?text with text > 75 chars was wrong
 *              handled as RFC encoding. Now we abort because no RFC-encoding
 * 1996/08/24 D -fixed that a sequence =?text1?text2 with text2 > 75 chars was
 *              wrong handled as RFC encoding. Now we abort because no RFC-encoding
 * 1996/08/22 D -aborting maps charset an coder to uppercase. this is wrong
 *              fixed, that it works case insensitive
 * 1996/08/22 E -calculate length was improperly done over all text
 *              by RFC B encoding should used if more non-ASCII characters
 *              then ASCII-characters must encode.
 * 1996/08/19 E -in Q-Encoding length calculating works not properly
 *              because ?,= and _ must encoded and (, ) should encoded and needs 3 characters
 * 1996/08/22 D -if there is a characterset which we don't understand
 *              we should abort decoding
 * 1996/08/19 D -if charset, coder or encoded text are empty
 *              we should abort decoding, because all of them has at least
 *              1 character in ABNF grammar in RFC1522
 *
 * V 1.1        initial release
 *              fundamental rewrite from 1.0, allowes multiword encoding
 *              and aborting decoding if we found it doesn't fit RFC1522
 *              syntax
 *
 * V 1.0        earlier release for test and first internal usage
 *
 *********************************************************************/


#ifdef STANDALONE
#define RFC1522

#include <windows.h>
#include <windowsx.h>
#include <tchar.h>
#include <stdio.h>
#include <string.h>


#include "wv1522.h"

BOOL UseRFC1522 = TRUE;
BOOL EncodeRFC1522 = TRUE;


#else

#include <windows.h>
#include <windowsx.h>
#include <malloc.h>
#include "wvglob.h"
#include "winvn.h"
#pragma hdrstop
#include "WVClass.h"

#endif

#include <ctype.h>

#ifdef RFC1522

int WVUniToMulti(char* out, int out_length, wchar_t *in, int in_length, int codepage);
int WVMultiToUni(wchar_t *out, int out_length, char *in, int in_length, int codepage);

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

#ifdef UNICODE
size_t strtowcs(wchar_t *pwcs, char *s)
{
    size_t count = 0;
    
    while (*s) {
        *pwcs++ = *s++;
        count++;
    }
    *pwcs = L'\0';
    return count;
}


size_t strntowcs(wchar_t *pwcs, char *s, size_t n)
{
    size_t count = 0;
    
    while ((--n > 0) && (*s)) {
        *pwcs++ = *s++;
        count++;
    }
    *pwcs = L'\0';
    return count;
}


/* function wcstostr and wcsntostr are only usefull
 * when the wide character string only contains ISO-8859-1 characters
 */
size_t wcstostr(char *s, wchar_t *pwcs)
{
    size_t count = 0;
    
    while (*pwcs) {
        *s++ = (char)*pwcs++;
        count++;
    }
    *s = '\0';
    return count;
}


size_t wcsntostr(char *s, wchar_t *pwcs, size_t n)
{
    size_t count = 0;
    
    while ((--n > 0) && (*pwcs)) {
        *s++ = (char)*pwcs++;
        count++;
    }
    *s = '\0';
    return count;
}
#endif
/*******************************************************************************************/

FIFO::FIFO()
{
    last = anchor = NULL;
    PendingCharacters = 0;
}

FIFO::FIFO(const char * s)
{
    last = anchor = NULL;
    PendingCharacters = 0;
    while (*s) Putchar(*s++);
}

FIFO::FIFO(unsigned int n,const char *s)
{
    last = anchor = NULL;
    PendingCharacters = 0;
    while ( n > 0) {
      Putchar(*s++);
      n--;
    };
}

#ifdef UNICODE
FIFO::FIFO(const wchar_t * s)
{
    last = anchor = NULL;
    PendingCharacters = 0;
    while (*s) Putchar(*s++);
}

FIFO::FIFO(unsigned int n,const wchar_t *s)
{
    last = anchor = NULL;
    PendingCharacters = 0;
    while ( n > 0) {
      Putchar(*s++);
      n--;
    };
}

#endif

FIFO::~FIFO()
{
    DeleteAll();
}


void FIFO::Chain(const TCHAR ch)
{
    WVC_Char *p;

    /* create a new element and setup properly */
    p = new WVC_Char;
    p->next = NULL;
    p->c = ch;

    /* put it in the chain */
    if (anchor == NULL) {
        anchor = p; /* first in the chain */
    } else {
        last->next = p;  /* append after the last of the chain */
    }
    last = p; /* setup last properly */

    PendingCharacters++;
}


void FIFO::Ungetchar(const TCHAR ch)
{
   WVC_Char *p;

   /* create a new element and put it on the begin of the chain */
   p = new WVC_Char;
   p->next = anchor;
   p->c = ch;

   anchor = p;
   PendingCharacters++;
}


void FIFO::DeleteAll()
{
    int junk;
    
    while ((junk = Getchar()) != EOF);
}

int FIFO::Testchar()
{
    if (anchor == NULL )
       return EOF;
    return anchor->c;
}

int FIFO::Getchar()
{
    TCHAR wch;
    WVC_Char * p;

    if (anchor == NULL)
       return EOF;
    p = anchor->next;
    wch = anchor->c;
    delete anchor;
    anchor = p;
    PendingCharacters--;
    return wch;
}

BOOL FIFO::Getallowed()
{
    return (anchor!=NULL);
}


void FIFO::Putchar(const TCHAR ch)
{
    Chain(ch);
}


BOOL FIFO::Putallowed()
{
    return TRUE; /* currently always true, but maybe later usefull */
}

unsigned int FIFO::Pendingchars()
{
    return PendingCharacters;
}

void FIFO::EndOfStream()
{
}

/******************************************************************************************/
RFCFIFO::RFCFIFO()
{
    nonASCIICharacters = 0;
}



RFCFIFO::RFCFIFO(const char * s)
{
    nonASCIICharacters = 0;
    while (*s) Putchar(*s++);
}


RFCFIFO::RFCFIFO(unsigned int n,const char *s)
{
    nonASCIICharacters = 0;
    while ( n > 0) {
      Putchar(*s++);
      n--;
    };
}

#ifdef UNICODE
RFCFIFO::RFCFIFO(const wchar_t * s)
{
    nonASCIICharacters = 0;
    while (*s) Putchar(*s++);
}

RFCFIFO::RFCFIFO(unsigned int n,const wchar_t *s)
{
    nonASCIICharacters = 0;
    while ( n > 0) {
      Putchar(*s++);
      n--;
    };
}
#endif

RFCFIFO::~RFCFIFO()
{
}

void RFCFIFO::Chain(const TCHAR ch)
{
    if (!_istascii(ch))
        nonASCIICharacters++;
    FIFO::Chain(ch);
}

void RFCFIFO::Ungetchar(const TCHAR ch)
{
   if (!_istascii(ch))
      nonASCIICharacters++;
   FIFO::Ungetchar(ch);
}

int RFCFIFO::Getchar()
{
    int wch;
    
    wch = FIFO::Getchar();
    if (!_istascii((TCHAR)wch))
       nonASCIICharacters--;
    return wch;
}

unsigned int RFCFIFO::NonASCIIchars()
{
    return nonASCIICharacters;
}

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

WVBDecoder::~WVBDecoder()
{
}

WVBDecoder::WVBDecoder()
{
#ifdef UNICODE    
    octetwidth = 1;
#endif    
    Base64Index = Base64Bits = 0;
#ifdef RFC1113
    rfc1113 = directCharacters = FALSE;
#endif
}

#ifdef RFC1113
WVBDecoder::WVBDecoder(BOOL useRFC1113)
{
#ifdef UNICODE    
    octetwidth = 1;
#endif    
    Base64Index = Base64Bits = 0;
    directCharacters = FALSE;
    rfc1113 = useRFC1113;
}
#endif

WVBDecoder::WVBDecoder(const char * s)
{
#ifdef UNICODE    
    octetwidth = 1;
#endif    
    Base64Index = Base64Bits = 0;
#ifdef RFC1113
    rfc1113 = directCharacters = FALSE;
#endif
    while (*s) Putchar(*s++);
}


#ifdef RFC1113
WVBDecoder::WVBDecoder(const char * s, BOOL useRFC1113)
{
#ifdef UNICODE    
    octetwith = 1;
#endif    
    Base64Index = Base64Bits = 0;
    directCharacters = FALSE;
    rfc1113 = useRFC1113;
    while (*s) Putchar(*s++);
}
#endif

WVBDecoder::WVBDecoder(unsigned int n, const char * s)
{
#ifdef UNICODE    
    octetwidth = 1;
#endif    
    Base64Index = Base64Bits = 0;
#ifdef RFC1113
    rfc1113 = directCharacters = FALSE;
#endif
    while (n > 0) {
       Putchar(*s++);
       n--;
    }
}

#ifdef RFC1113
WVBDecoder::WVBDecoder(unsigned int n, const char * s, BOOL useRFC1113)
{
#ifdef UNICODE    
    octetwidth = 1;
#endif    
    Base64Index = Base64Bits = 0;
    directCharacters = FALSE;
    rfc1113 = useRFC1113;
    while (n > 0) {
       Putchar(*s++);
       n--;
    }
}
#endif

#ifdef UNICODE
WVBDecoder::WVBDecoder(unsigned int octets)
{
    octetwidth = octets;
    Base64Index = Base64Bits = 0;
#ifdef RFC1113
    rfc1113 = directCharacters = FALSE;
#endif
}

#ifdef RFC1113
WVBDecoder::WVBDecoder(BOOL useRFC1113, unsigned int octets)
{
    octetwidth = octets;
    Base64Index = Base64Bits = 0;
    directCharacters = FALSE;
    rfc1113 = useRFC1113;
}
#endif

WVBDecoder::WVBDecoder(const char * s, unsigned int octets)
{
    octetwidth = octets;
    Base64Index = Base64Bits = 0;
#ifdef RFC1113
    rfc1113 = directCharacters = FALSE;
#endif
    while (*s) Putchar(*s++);
}


#ifdef RFC1113
WVBDecoder::WVBDecoder(const char * s, BOOL useRFC1113, unsigned int octets)
{
    octetwith = octets;
    Base64Index = Base64Bits = 0;
    directCharacters = FALSE;
    rfc1113 = useRFC1113;
    while (*s) Putchar(*s++);
}
#endif

WVBDecoder::WVBDecoder(unsigned int n, const char * s, unsigned int octets)
{
    octetwidth = octets;
    Base64Index = Base64Bits = 0;
#ifdef RFC1113
    rfc1113 = directCharacters = FALSE;
#endif
    while (n > 0) {
       Putchar(*s++);
       n--;
    }
}

#ifdef RFC1113
WVBDecoder::WVBDecoder(unsigned int n, const char * s, BOOL useRFC1113, unsigned int octets)
{
    octetwidth = octets;
    Base64Index = Base64Bits = 0;
    directCharacters = FALSE;
    rfc1113 = useRFC1113;
    while (n > 0) {
       Putchar(*s++);
       n--;
    }
}
#endif

WVBDecoder::WVBDecoder(const wchar_t * s)
{
    octetwidth = 1;
    Base64Index = Base64Bits = 0;
#ifdef RFC1113
    rfc1113 = directCharacters = FALSE;
#endif
    while (*s) Putchar(*s++);
}

WVBDecoder::WVBDecoder(const wchar_t * s, unsigned int octets)
{
    octetwidth = octets;
    Base64Index = Base64Bits = 0;
#ifdef RFC1113
    rfc1113 = directCharacters = FALSE;
#endif
    while (*s) Putchar(*s++);
}

#ifdef RFC1113
WVBDecoder::WVBDecoder(const wchar_t * s, BOOL useRFC1113)
{
    octetwidth = 1;
    Base64Index = Base64Bits = 0;
    directCharacters = FALSE;
    rfc1113 = useRFC1113;
    while (*s) Putchar(*s++);
}

WVBDecoder::WVBDecoder(const wchar_t * s, BOOL useRFC1113, unsigned int octets)
{
    octetwidth = octets;
    Base64Index = Base64Bits = 0;
    directCharacters = FALSE;
    rfc1113 = useRFC1113;
    while (*s) Putchar(*s++);
}
#endif

WVBDecoder::WVBDecoder(unsigned int n, const wchar_t * s)
{
    octetwidth = 1;
    Base64Index = Base64Bits = 0;
#ifdef RFC1113
    rfc1113 = directCharacters = FALSE;
#endif
    while (n > 0) {
       Putchar(*s++);
       n--;
    }
}

WVBDecoder::WVBDecoder(unsigned int n, const wchar_t * s, unsigned int octets)
{
    octetwidth = octets;
    Base64Index = Base64Bits = 0;
#ifdef RFC1113
    rfc1113 = directCharacters = FALSE;
#endif
    while (n > 0) {
       Putchar(*s++);
       n--;
    }
}

#ifdef RFC1113
WVBDecoder::WVBDecoder(unsigned int n, const wchar_t * s, BOOL useRFC1113)
{
    octetwidth = 1;
    Base64Index = Base64Bits = 0;
    directCharacters = FALSE;
    rfc1113 = useRFC1113;
    while (n > 0) {
       Putchar(*s++);
       n--;
    }
}

WVBDecoder::WVBDecoder(unsigned int n, const wchar_t * s, BOOL useRFC1113,
                       unsigned int octets)
{
    octetwidth = octets;
    Base64Index = Base64Bits = 0;
    directCharacters = FALSE;
    rfc1113 = useRFC1113;
    while (n > 0) {
       Putchar(*s++);
       n--;
    }
}
#endif
#endif // UNICODE

#if 1 //def JAPANESE
void WVBDecoder::DeleteAll() // S.Yoshida
{
    int junk;

    while ((junk = Getchar()) != EOF);
    Base64Index = Base64Bits = 0;
}
#endif


BYTE WVBDecoder::base64num(const char ch)
{
    if ((ch >= 'A') && (ch <= 'Z'))
       return (BYTE)(ch-'A');
    if ((ch >= 'a') && (ch <= 'z'))
      return (BYTE)(ch-'a'+26);
    if ((ch >= '0') && (ch <= '9'))
      return (BYTE)(ch-'0'+52);
    if (ch == '+')
      return (BYTE)62;
   if (ch == '/')
      return (BYTE)63;
   return (BYTE)0;
}


void WVBDecoder::Putchar(const TCHAR ch)
{
   unsigned int c;
#ifdef RFC1113
    if ((rfc1113) && (directCharacters)) {
      if (ch == '*')
        directCharacters = !directCharacters;  
      else
        Chain(ch);  
    } else {
#endif
      if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') ||
          (ch == '+') || (ch == '/')) {
        Base64Bits = (Base64Bits << 6) + base64num((char)ch);
#ifdef UNICODE
        if (octetwidth == 1) {
#endif
           switch ( ++Base64Index) {
                case 1:
                   break; 
                case 2:
#ifdef JAPANESE
                   if ((c = (Base64Bits >> 4)) != 0)
#else
                   if ((c = (Base64Bits >> 4))>=0x20)
#endif
                        Chain((char)c);
                   Base64Bits = Base64Bits & 0x0F;
                   break;
                case 3:
#ifdef JAPANESE
                   if ((c = (Base64Bits >> 2)) != 0)
#else
                   if ((c = (Base64Bits >> 2)) >= 0x20)
#endif
                        Chain((char)c);
                   Base64Bits = Base64Bits & 0x03;
                   break;
                case 4:
#ifdef JAPANESE
                   if (Base64Bits != 0)
#else
                   if (Base64Bits >= 0x20)
#endif
                       Chain((char)Base64Bits);
                   Base64Bits = 0;
                   Base64Index = 0;
           }
#ifdef UNICODE     
        } else {
           switch ( ++Base64Index) {
                case 1:
                case 2:
                   break; 
                case 3:
#ifdef JAPANESE
                   if ((c = (Base64Bits >> 2)) != 0)
#else
                   if ((c = (Base64Bits >> 2)) >= 0x0020)
#endif
                        Chain((wchar_t)c);
                   Base64Bits = Base64Bits & 0x0003;
                   break;
                case 4:
                case 5:
                   break;
                case 6:
#ifdef JAPANESE
                   if ((c = (Base64Bits >> 4)) != 0)
#else
                   if ((c = (Base64Bits >> 4)) >= 0x0020)
#endif
                        Chain((wchar_t)c);
                   Base64Bits = Base64Bits & 0x000F;
                case 7:
                   break;
                case 8:
#ifdef JAPANESE
                   if (Base64Bits != 0)
#else
                   if (Base64Bits >= 0x0020)
#endif
                       Chain((wchar_t)Base64Bits);
                   Base64Bits = 0;
                   Base64Index = 0;
           } 
        }
#endif  
      }
      if (ch == '=')
         Base64Index = Base64Bits = 0;
#ifdef RFC1113
      if ((rfc1113) && (ch == '*'))
           directCharacters = !directCharacters;
    }
#endif
}


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

WVQIDecoder::~WVQIDecoder()
{
}

WVQIDecoder::WVQIDecoder()
{
#ifdef UNICODE    
    octetwidth = 1;
#endif    
    outstanding = 0;
}

WVQIDecoder::WVQIDecoder(const char * s)
{
#ifdef UNICODE    
    octetwidth = 1;
#endif    
    outstanding = 0;
    while (*s) Putchar(*s++);
}

WVQIDecoder::WVQIDecoder(unsigned int n, const char * s)
{
#ifdef UNICODE    
    octetwidth = 1;
#endif    
    outstanding = 0;
    while (n > 0) {
       Putchar(*s++);
       n--;
    }
}

#ifdef UNICODE
WVQIDecoder::WVQIDecoder(unsigned int octets)
{
    octetwidth = octets;
    outstanding = 0;
}

WVQIDecoder::WVQIDecoder(const char * s, unsigned int octets)
{
    octetwidth = octets;
    outstanding = 0;
    while (*s) Putchar(*s++);
}

WVQIDecoder::WVQIDecoder(unsigned int n, const char * s, unsigned int octets)
{
    octetwidth = octets;
    outstanding = 0;
    while (n > 0) {
       Putchar(*s++);
       n--;
    }
}

WVQIDecoder::WVQIDecoder(const wchar_t * s)
{
    octetwidth = 1;
    outstanding = 0;
    while (*s) Putchar(*s++);
}

WVQIDecoder::WVQIDecoder(const wchar_t * s, unsigned int octets)
{
    octetwidth = octets;
    outstanding = 0;
    while (*s) Putchar(*s++);
}

WVQIDecoder::WVQIDecoder(unsigned int n, const wchar_t * s)
{
    octetwidth = 1;
    outstanding = 0;
    while (n > 0) {
       Putchar(*s++);
       n--;
    }
}

WVQIDecoder::WVQIDecoder(unsigned int n, const wchar_t * s, unsigned int octets)
{
    octetwidth = octets;
    outstanding = 0;
    while (n > 0) {
       Putchar(*s++);
       n--;
    }
}
#endif

BYTE WVQIDecoder::hexnum(const char ch)
{
    if ((ch >= '0') && (ch <= '9'))
       return (BYTE)(ch-'0');
    if ((ch >= 'A') && (ch <= 'F'))
      return (BYTE)(ch-'A'+0x0A);
    if ((ch >= 'a') && (ch <='f'))
      return (BYTE)(ch-'a'+0x0A);
   return (BYTE)0;  
}

void WVQIDecoder::Putchar(const TCHAR ch)
{
    unsigned int i;
    unsigned int j;

    if (outstanding) {
       --outstanding;
       *DecoderPtr++ = (char)ch; /* hexchars in sequence =xy */
       if (!outstanding) {
         /* encoded sequence is complete */
         DecoderPtr = DecoderBlock;
         j = 0;
#ifdef UNICODE
         for (i = 0;i < decodelength ; i++) {
           j = (j << 4) + hexnum(*DecoderPtr++);
         } /* for */
#else
         for (i = 0;i < 2 ; i++) {
           j = (j << 4) + hexnum(*DecoderPtr++);
         } /* for */
#endif
#ifdef JAPANESE
         if (j != 0) /* should be a space or above, no control-chars ! */
#else
         if (j >= 0x20) /* should be a space or above, no control-chars ! */
#endif
           Chain((TCHAR)j);
       } /* if no more outstanding characters */
    } else {
       if (ch == '=') {
#ifdef UNICODE   
         decodelength = outstanding = (octetwidth == 1)?2:4;
#else
         outstanding = 2;
#endif   
         DecoderPtr = DecoderBlock;
       } else {
         Chain(ch);
       }
    }
}

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

WVBCoder::~WVBCoder()
{
}

WVBCoder::WVBCoder()
{
#ifdef UNICODE    
    octetwidth = 1;
#endif    
    Base64Index = Base64Bits = 0;
}


WVBCoder::WVBCoder(const char * s)
{
#ifdef UNICODE    
    octetwidth = 1;
#endif    
    Base64Index = Base64Bits = 0;
    while (*s) Putchar(*s++);
}

WVBCoder::WVBCoder(unsigned int n, const char * s)
{
#ifdef UNICODE   
    octetwidth = 1;
#endif
    Base64Index = Base64Bits = 0;
    while (n > 0) {
       Putchar(*s++);
       n--;
    }
}

#ifdef UNICODE
WVBCoder::WVBCoder(unsigned int octets)
{
    octetwidth = octets;
    Base64Index = Base64Bits = 0;
}


WVBCoder::WVBCoder(const char * s, unsigned int octets)
{
    octetwidth = octets;
    Base64Index = Base64Bits = 0;
    while (*s) Putchar(*s++);
}

WVBCoder::WVBCoder(unsigned int n, const char * s, unsigned int octets)
{
    octetwidth = octets;
    Base64Index = Base64Bits = 0;
    while (n > 0) {
       Putchar(*s++);
       n--;
    }
}

WVBCoder::WVBCoder(const wchar_t * s, unsigned int octets)
{
    octetwidth = octets;
    Base64Index = Base64Bits = 0;
    while (*s) Putchar(*s++);
}


WVBCoder::WVBCoder(unsigned int n, const wchar_t * s, unsigned int octets)
{
    octetwidth = octets;
    Base64Index = Base64Bits = 0;
    while (n > 0) {
       Putchar(*s++);
       n--;
    }
}
#endif

char WVBCoder::base64char(const BYTE base64)
{
    const char Base64Table[]
    ="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    
    if (base64 <= 63)
       return Base64Table[base64];
    return ' ';
}


void WVBCoder::EndOfStream()
{
    Base64Bits = Base64Bits <<8;
    switch ( ++Base64Index ) {
        case 1:
            /* encoding input was integral multiple of 24 Bits */
            break;
        case 2:
            /* encoding input has a final quantum of 8 bit */
            Chain(base64char((BYTE)(Base64Bits >> 4)));
            Chain('=');
            Chain('=');
            break;
        case 3:
            /* encoding input has a final quantum of 16 bit*/
            Chain(base64char((BYTE)(Base64Bits >> 6)));
            Chain('=');
    }
    
    Base64Bits = 0;
    Base64Index = 0;

    FIFO::EndOfStream(); /* say FIFO stack that stream has ended */
}


void WVBCoder::Putchar(const char ch)
{
    Base64Bits = (Base64Bits << 8) + ch;
    switch ( ++Base64Index ) {
        case 1:
            Chain(base64char((BYTE)(Base64Bits >> 2)));
            Base64Bits = Base64Bits & 0x03;
            break;
        case 2:
            Chain(base64char((BYTE)(Base64Bits >> 4)));
            Base64Bits = Base64Bits & 0x0F;
            break;
        case 3:
            Chain(base64char((BYTE)(Base64Bits >> 6)));
            Base64Bits = Base64Bits & 0x3F;
            Chain(base64char((BYTE)Base64Bits));
            Base64Bits = 0;
            Base64Index = 0;
    }
}

#ifdef UNICODE
void WVBCoder::Putchar(const wchar_t ch)
{
    if (octetwidth != 1)
        Putchar((char)(ch >> 8));
    Putchar((char)ch);
}
#endif

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

WVQICoder::~WVQICoder()
{
}

WVQICoder::WVQICoder()
{
#ifdef UNICODE    
    octetwidth = 1;
#endif    
}


WVQICoder::WVQICoder(const char * s)
{
#ifdef UNICODE    
    octetwidth = 1;
#endif    
    while (*s) Putchar(*s++);
}

WVQICoder::WVQICoder(unsigned int n, const char * s)
{
#ifdef UNICODE    
    octetwidth = 1;
#endif    
    while (n > 0) {
       Putchar(*s++);
       n--;
    }
}

#ifdef UNICODE
WVQICoder::WVQICoder(unsigned int octets)
{
    octetwidth = octets;
}


WVQICoder::WVQICoder(const char * s, unsigned int octets)
{
    octetwidth = octets;
    while (*s) Putchar(*s++);
}

WVQICoder::WVQICoder(unsigned int n, const char * s, unsigned int octets)
{
    octetwidth = octets;
    while (n > 0) {
       Putchar(*s++);
       n--;
    }
}

WVQICoder::WVQICoder(const wchar_t * s)
{
    octetwidth = 1;
    while (*s) Putchar(*s++);
}

WVQICoder::WVQICoder(const wchar_t * s, unsigned int octets)
{
    octetwidth = octets;
    while (*s) Putchar(*s++);
}

WVQICoder::WVQICoder(unsigned int n, const wchar_t * s)
{
    octetwidth = 1;
    while (n > 0) {
       Putchar(*s++);
       n--;
    }
}

WVQICoder::WVQICoder(unsigned int n, const wchar_t * s, unsigned int octets)
{
    octetwidth = octets;
    while (n > 0) {
       Putchar(*s++);
       n--;
    }
}
#endif

char WVQICoder::hexchar(const BYTE hex)
{
    const char HexTable[]
    ="0123456789ABCDEF";

    if (hex <= 0x0F)
       return HexTable[hex];
    return ' ';
}

void WVQICoder::Putchar(const TCHAR ch)
{
    if (_istascii(ch) && _istprint(ch) &&
                (ch != '_') && (ch != '=') && (ch != '?') &&
                (ch != '(') && (ch != ')') ) {
          Chain(ch);
    } else {
#ifdef UNICODE  
          if (octetwidth != 1) {
              Chain('=');
              Chain(hexchar( (BYTE)((ch >> 12) & 0x0F)));
              Chain(hexchar( (BYTE)((ch >> 8) & 0x0F)));
              Chain(hexchar( (BYTE)((ch >> 4) & 0x0F)));
              Chain(hexchar( (BYTE)((ch) & 0x0F)));           
          } else {
#endif        
              Chain('=');
              Chain(hexchar( (BYTE)((ch >> 4) & 0x0F)));
              Chain(hexchar( (BYTE)((ch) & 0x0F)));
#ifdef UNICODE        
          }
#endif    
    }
}


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

WVQCoder::~WVQCoder()
{
}

WVQCoder::WVQCoder()
{
}

WVQCoder::WVQCoder(const char * s)
{
    while (*s) Putchar(*s++);
}

WVQCoder::WVQCoder(unsigned int n, const char * s)
{
    while (n > 0) {
       Putchar(*s++);
       n--;
    }
}

#ifdef UNICODE
WVQCoder::WVQCoder(unsigned int octets) : WVQICoder(octets)
{
}


WVQCoder::WVQCoder(const char * s, unsigned int octets) : WVQICoder(octets)
{
    while (*s) Putchar(*s++);
}

WVQCoder::WVQCoder(unsigned int n, const char * s, unsigned int octets) : WVQICoder(octets)
{
    while (n > 0) {
       Putchar(*s++);
       n--;
    }
}

WVQCoder::WVQCoder(const wchar_t * s, unsigned int octets) : WVQICoder(octets)
{
    while (*s) Putchar(*s++);
}


WVQCoder::WVQCoder(unsigned int n, const wchar_t * s, unsigned int octets)
                  : WVQICoder(octets)
{
    while (n > 0) {
       Putchar(*s++);
       n--;
    }
}
#endif

void WVQCoder::Putchar(const TCHAR ch)
{
    if (ch == 0x20 ) {
        WVQICoder::Putchar('_');
    } else {
        WVQICoder::Putchar(ch);
    }
}

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

WVQDecoder::~WVQDecoder()
{
}

WVQDecoder::WVQDecoder()
{
}

WVQDecoder::WVQDecoder(const char * s)
{
    while (*s) Putchar(*s++);
}

WVQDecoder::WVQDecoder(unsigned int n, const char * s)
{
    while (n > 0) {
       Putchar(*s++);
       n--;
    }
}

#ifdef UNICODE
WVQDecoder::WVQDecoder(unsigned int octets) : WVQIDecoder(octets)
{
}

WVQDecoder::WVQDecoder(const char * s, unsigned int octets) : WVQIDecoder(octets)
{
    while (*s) Putchar(*s++);
}

WVQDecoder::WVQDecoder(unsigned int n, const char * s, unsigned int octets)
                      : WVQIDecoder(octets)
{
    while (n > 0) {
       Putchar(*s++);
       n--;
    }
}

WVQDecoder::WVQDecoder(const wchar_t * s)
{
    while (*s) Putchar(*s++);
}


WVQDecoder::WVQDecoder(const wchar_t * s, unsigned int octets) : WVQIDecoder(octets)
{
    while (*s) Putchar(*s++);
}

WVQDecoder::WVQDecoder(unsigned int n, const wchar_t * s)
{
    while (n > 0) {
       Putchar(*s++);
       n--;
    }
}

WVQDecoder::WVQDecoder(unsigned int n, const wchar_t * s, unsigned int octets)
                      : WVQIDecoder(octets)
{
    while (n > 0) {
       Putchar(*s++);
       n--;
    }
}
#endif


void WVQDecoder::Putchar(const TCHAR ch)
{
    
    if (ch == '_')
      WVQIDecoder::Putchar(0x20); /* substitution defined in Q encoding 4.2.(2) */
    else
      WVQIDecoder::Putchar(ch);
}

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

WVDecoder::~WVDecoder()
{
    switch (whichDecoder) {
        case DecoderQ:
              delete (WVQDecoder *)Decoder;
              break;
        case DecoderB:
        default:
              delete (WVBDecoder *)Decoder;
    }
}

#ifdef UNICODE
void WVDecoder::Init(const char * s, unsigned int octets)
{
    if (_stricmp(s,"Q") == 0) {
        whichDecoder = DecoderQ;
    };
    if (_stricmp(s,"B") == 0) {
        whichDecoder = DecoderB;
    };
    switch (whichDecoder) {
        case DecoderQ:
              Decoder = (void *) new WVQDecoder(octets);
              break;
        case DecoderB:
        default:
#ifdef RFC1113
              Decoder = (void *) new WVBDecoder(TRUE,octets);
#else
              Decoder = (void *) new WVBDecoder(octets);
#endif
    }
}
#else //UNICODE
void WVDecoder::Init(const char * s)
{
    if (_stricmp(s,"Q") == 0) {
        whichDecoder = DecoderQ;
    };
    if (_stricmp(s,"B") == 0) {
        whichDecoder = DecoderB;
    };
    switch (whichDecoder) {
        case DecoderQ:
              Decoder = (void *) new WVQDecoder;
              break;
        case DecoderB:
        default:
#ifdef RFC1113
              Decoder = (void *) new WVBDecoder(TRUE);
#else
              Decoder = (void *) new WVBDecoder;
#endif
    }
}
#endif //UNICODE

WVDecoder::WVDecoder(const char * s)
{
#ifdef UNICODE
    Init(s,1);
#else
    Init(s);
#endif    
}

#ifdef UNICODE
WVDecoder::WVDecoder(const char * s, unsigned int octets)
{
    Init(s, octets);
}

WVDecoder::WVDecoder(wchar_t * s)
{
    char mybuf[WVC_CODERNAMELENGTH];

    wcsntostr(mybuf, s, WVC_CODERNAMELENGTH);  
    Init(mybuf,1);
}

WVDecoder::WVDecoder(wchar_t * s, unsigned int octets)
{
    char mybuf[WVC_CODERNAMELENGTH];

    wcsntostr(mybuf, s, WVC_CODERNAMELENGTH);  
    Init(mybuf,octets);
}
#endif

void WVDecoder::EndOfStream()
{
    switch (whichDecoder) {
        case DecoderQ:
              ((WVQDecoder *)Decoder)->EndOfStream();
              break;
        case DecoderB:
        default:
              ((WVBDecoder *)Decoder)->EndOfStream();
    }
}

void WVDecoder::Putchar(const TCHAR ch)
{
    switch (whichDecoder) {
        case DecoderQ:
              ((WVQDecoder *)Decoder)->Putchar(ch);
              break;
        case DecoderB:
        default:
              ((WVBDecoder *)Decoder)->Putchar(ch);
    }
}

BOOL WVDecoder::Putallowed()
{
    switch (whichDecoder) {
        case DecoderQ:
              return ((WVQDecoder *)Decoder)->Putallowed();
        case DecoderB:
        default:
              return ((WVBDecoder *)Decoder)->Putallowed();
    }
}

int WVDecoder::Testchar()
{
    switch (whichDecoder) {
        case DecoderQ:
              return ((WVQDecoder *)Decoder)->Testchar();
        case DecoderB:
        default:
              return ((WVBDecoder *)Decoder)->Testchar();
    }
}


int WVDecoder::Getchar()
{
    switch (whichDecoder) {
        case DecoderQ:
              return ((WVQDecoder *)Decoder)->Getchar();
        case DecoderB:
        default:
              return ((WVBDecoder *)Decoder)->Getchar();
    }
}

void WVDecoder::Ungetchar(const TCHAR ch)
{
    switch (whichDecoder) {
        case DecoderQ:
              ((WVQDecoder *)Decoder)->Ungetchar(ch);
              break;
        case DecoderB:
        default:
              ((WVBDecoder *)Decoder)->Ungetchar(ch);
    }
}

unsigned int WVDecoder::Pendingchars()
{
    switch (whichDecoder) {
        case DecoderQ:
              return ((WVQDecoder *)Decoder)->Pendingchars();
        case DecoderB:
        default:
              return ((WVBDecoder *)Decoder)->Pendingchars();
    }
}

unsigned int WVDecoder::NonASCIIchars()
{
    switch (whichDecoder) {
        case DecoderQ:
              return ((WVQDecoder *)Decoder)->NonASCIIchars();
        case DecoderB:
        default:
              return ((WVBDecoder *)Decoder)->NonASCIIchars();
    }
}

BOOL WVDecoder::Getallowed()
{
    switch (whichDecoder) {
        case DecoderQ:
              return ((WVQDecoder *)Decoder)->Getallowed();
        case DecoderB:
        default:
              return ((WVBDecoder *)Decoder)->Getallowed();
    }
}


/*******************************************************************************************/
#if  1//def JAPANESE

#ifdef UNICODE
WVCharsetDecoder::WVCharsetDecoder(int charset, int coding, unsigned int octets)
{
   WVCharsetDecoder::octets = octets;
   WVCharsetDecoder::charset = charset;
   WVCharsetDecoder::coding = coding;
   state = prevstate = 0;
   BDecoder = NULL;
   uniindex = mbsindex = 0;
}
#endif

WVCharsetDecoder::WVCharsetDecoder(int charset, int coding)
{
#ifdef UNICODE
   WVCharsetDecoder::octets = octets;
#endif
   WVCharsetDecoder::charset = charset;
   WVCharsetDecoder::coding = coding;
   state = prevstate = 0;
   BDecoder = NULL;
   uniindex = mbsindex = 0;
}

WVCharsetDecoder::~WVCharsetDecoder()
{
   delete BDecoder;
}

void WVCharsetDecoder::SwitchStream(int charset, int coding)
{
   if( charset != WVCharsetDecoder::charset
    || coding  != WVCharsetDecoder::coding ) {
     EndOfStream();
     WVCharsetDecoder::charset = charset;
     WVCharsetDecoder::coding  = coding;
     delete BDecoder;
     BDecoder = NULL;
     state = prevstate = 0;
     uniindex = mbsindex = 0;
   }
}

enum s_iso2022 {
    s_roman, s_kanji, s_kanji_2, s_kana,
    s_esc, s_esc_dol, s_esc_dolpar, s_esc_par, s_so
};

void WVCharsetDecoder::Putchar(const TCHAR ch)
{
#ifdef JAPANESE
  unsigned char highbyte, lowbyte;
#endif

  switch( coding ) {

#ifdef JAPANESE

  case WVCDS_ISO2022:
    if (ch == 0x0e) {
      prevstate = state;
      state = s_so;
      return;
    }

    switch (state) {
    case s_roman:
      if( ch == 0x1b ) {
        prevstate = state;
        state = s_esc;
        break;
      }
      Thruchar(ch);
      break;

    case s_so:
      if( ch == 0x0f ) {
        state = prevstate;
        break;
      }
      Thruchar(ch | 0x80);
      break;

    case s_kana:
      if( ch == 0x1b ) {
        prevstate = state;
        state = s_esc;
        break;
      }
      Thruchar(ch | 0x80);
      break;

    case s_kanji:
      if( ch == 0x1b ) {
        prevstate = state;
        state = s_esc;
        break;
      }
      if( ch < '!' ) {
        state = s_roman;
        break;
      }
      inbuf[0] = ch;
      state = s_kanji_2;
      break;

    case s_kanji_2:
      if( ch < '!' ) {
        state = s_roman;
        Thruchar( inbuf[0] );
        break;
      }

      state = s_kanji;
      highbyte = ((inbuf[0] - 0x21) >> 1) + 0x81;
      if (highbyte >= 0xa0) {
        highbyte += 0x40;
      }

      if (inbuf[0] & 1) {
        lowbyte = ch + 0x1f;
        if (lowbyte >= 0x7f) lowbyte ++;
        else
        if (lowbyte <= 0x3f) lowbyte |= 0x80; //mozilla
      } else {
        lowbyte = ch + 0x7e;
      }

      Thruchar( highbyte );
      Thruchar( lowbyte );
      break;

    case s_esc:
      if( ch == '$' ) {
        state = s_esc_dol;
      } else if( ch == '(' ) {
        state = s_esc_par;
      } else {
        state = prevstate;
        Thruchar( 0x1b );
        Thruchar( ch );
        break;
      }
      break;

    case s_esc_dol:
      if( ch == '@' || ch == 'B' ) {
        state = s_kanji;
      } else {
        state = prevstate;
        Thruchar( 0x1b );
        Thruchar( '$' );
        Thruchar( ch );
        break;
      }
      return;

    case s_esc_par:
      if( ch == 'B' || ch == 'J' ) {
        state = s_roman;
      } else if( ch == 'I' ) {
        state = s_kana;
      } else {
        state = prevstate;
        Thruchar( 0x1b );
        Thruchar( '(' );
        Thruchar( ch );
        break;
      }
      return;
    }
    return;
#endif //JAPANESE

  case WVCDS_UTF7:
    if( !state ) 
	{
      if( ch != '+' ) 
	  {
        break;
      }
      state = 1;
      if (!BDecoder) BDecoder = new WVBDecoder;
            else     BDecoder->DeleteAll();
    } 
	else if( state == 1 && ch == '-' ) 
	{
      Thruchar('+');
    } 
	else {
      state = 2;
      BDecoder->Putchar( ch );
      if( BDecoder->Getallowed() ) {
        PutHalfwchar( (TCHAR) BDecoder->Getchar() );
      }
    }
    return;



  case WVCDS_UTF8: //
    if( !state ) 
	{
      if( (ch & 0x80) && (ch & 0x40) ) // at least 2 byte seq 0xC0+	 U+0080 to U+07FF
	  {
        if( ch & 0x20 ) //3 byte seq 0xE0+
		{
          if( ch & 0x10 ) //4 bytes or illegal 0xF0+
		  {
            if( ch & 0x08 )  
			{
              if( ch & 0x04 ) 
			  {
                //6 bytes 
				inbuf[state = 5] = ch & 0x01;
              } 
			  else //5 bytes
			  {
                inbuf[state = 4] = ch & 0x03;
              }
            } 
			else //4bytes 11110www 10xxxxxx 10yyyyyy 10zzzzzz
			{
              inbuf[state = 3] = ch & 0x07;
            }
          } 
		  else //3 bytes  1110xxxx 10yyyyyy 10zzzzzz
		  {
            inbuf[state = 2] = ch & 0x0f;
          }
        } 
		else  //2bytes 110yyyyy 10zzzzzz
		{
          inbuf[state = 1] = ch & 0x1f;
        }
      } 
	  else //else ascii	 0zzzzzzz
	  {
        break;
      }
    } 
	else 
	{
      inbuf[--state] = ch & 0x3f;
      if( state == 0 ) 
	  {
        /* UCS4.0 (inbuf[5] << 6) | (inbuf[4])
           UCS4.1 (inbuf[3] << 2) | (inbuf[2]>>4)
           UCS4.2 (inbuf[2] << 4) | (inbuf[1]>>2)
           UCS4.3 (inbuf[1] << 6) | (inbuf[0])       */
        PutHalfwchar( (TCHAR)( (inbuf[2] << 4)|(inbuf[1] >> 2) ) );
        PutHalfwchar( (TCHAR)( (inbuf[1] << 6)|(inbuf[0])      ) );
      }
    }

    return;

  case WVCDS_IGNORECTRL:
    if (ch < 0x20)
      return;
    break;

  case WVCDS_8859_15:
	{
		WCHAR out[2] = {0};
		char in[2] = {0};
		*in = ch;
		int i = MultiByteToWideChar(28605, 0, in, 1, out, 2);
		if (i)
		{
			PutHalfwchar( out[0] >> 8);
			PutHalfwchar( out[0] & 0xFF);
			Flushwcache();
			return;
		}

	}
	break;
  case WVCDS_1252:
    {
		WCHAR out[2] = {0};
		char in[2] = {0};
		*in = ch;
		int i = MultiByteToWideChar(1252, 0, in, 1, out, 2);
		if (i)
		{
			PutHalfwchar( out[0] >> 8);
			PutHalfwchar( out[0] & 0xFF);
			Flushwcache();
			return;
		}

	}
	break;
  case WVCDS_1254:
    {
		WCHAR out[2] = {0};
		char in[2] = {0};
		*in = ch;
		int i = MultiByteToWideChar(1254, 0, in, 1, out, 2);
		if (i)
		{
			PutHalfwchar( out[0] >> 8);
			PutHalfwchar( out[0] & 0xFF);
			Flushwcache();
			return;
		}

	}
	break;

  case WVCDS_THRU:
    break;
  }
  if (uniindex)	 //must flush characters in sequence
	Flushwcache();
  Thruchar(ch);
}

void WVCharsetDecoder::Flushwcache()
{
#ifdef UNICODE
#else
  if( 0 < uniindex ) 
  {
    int i,j;
	for (j = 0, i = 0; i < (uniindex+1)/2; i++)
	{
		WCHAR* wCache = (WCHAR*) unicache;
		if ( wCache[i] != 0xFEFF)
		{
			wCache[j++] = wCache[i];	
		}

	}
	uniindex = 2*j;
	
	int x = WVUniToMulti(mbscache, sizeof(mbscache), (wchar_t*)unicache, (uniindex+1) / 2, CP_ACP );
    for( i = 0; i < x; ++ i ) 
	{
      Thruchar( mbscache[i] );
    }
    uniindex = 0;
  }
#endif
}

void WVCharsetDecoder::PutHalfwchar(TCHAR wch)
{
#ifdef UNICODE
  Thruchar( wch );
#else
  if( uniindex & 1 ) 
  {
    //unicache[ uniindex ] = 0;
    unicache[ uniindex - 1 ] = wch;
  } 
  else 
  {
    unicache[ uniindex ] = 0;
	unicache[ uniindex + 1 ] = wch;
  }
  
  ++ uniindex; 
  if( uniindex >= sizeof(unicache) ) 
  {
    Flushwcache();
  }
#endif
}

void WVCharsetDecoder::EndOfStream()
{
  switch( coding ) {
  case WVCDS_UTF7:
    if( state ) 
	{
      BDecoder->EndOfStream();
      while( BDecoder->Getallowed() ) 
	  {
        PutHalfwchar( (TCHAR) BDecoder->Getchar() );
      }
      BDecoder->DeleteAll();
      Flushwcache();
    }
    break;
  case WVCDS_UTF8:
  case WVCDS_8859_15:
  case WVCDS_1252:
  case WVCDS_1254:
    Flushwcache();
    state = 0;
    break;
  }
}

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

#endif // JAPANESE


void WVRFCDecoder::Init()
{
    strictRFC = TRUE;
    status = WVC_RFC_ENCODING_ALLOWED;
    CharsetPtr = Charsetname;
    CoderPtr = Codername;
    *CharsetPtr = *CoderPtr = '\0';
    
    typedef struct _CPTABLE {
		int codepage;
		char* pName;
	} CPTABLE;
	
	static CPTABLE CodepageTable[] =  { 
    { 37,  "IBM037"},
    { 437,  "IBM437"},
    { 500,  "IBM500"},
    { 708,  "ASMO-708"},
    { 720,  "DOS-720"},
    { 737,  "ibm737"},
    { 775,  "ibm775"},
    { 850,  "ibm850"},
    { 852,  "ibm852"},
    { 855,  "IBM855"},
    { 857,  "ibm857"},
    { 858,  "IBM00858"},
    { 860,  "IBM860"},
    { 861,  "ibm861"},
    { 862,  "DOS-862"},
    { 863,  "IBM863"},
    { 864,  "IBM864"},
    { 865,  "IBM865"},
    { 866,  "cp866"},
    { 869,  "ibm869"},
    { 870,  "IBM870"},
    { 874,  "windows-874"},
    { 875,  "cp875"},
    { 932,  "shift_jis"},
    { 936,  "gb2312"},
    { 949,  "ks_c_5601-1987"},
    { 950,  "big5"},
    { 1026,  "IBM1026"},
    { 1047,  "IBM01047"},
    { 1140,  "IBM01140"},
    { 1141,  "IBM01141"},
    { 1142,  "IBM01142"},
    { 1143,  "IBM01143"},
    { 1144,  "IBM01144"},
    { 1145,  "IBM01145"},
    { 1146,  "IBM01146"},
    { 1147,  "IBM01147"},
    { 1148,  "IBM01148"},
    { 1149,  "IBM01149"},
    { 1200,  "utf-16"},
    { 1201,  "unicodeFFFE"},
    { 1250,  "windows-1250"},
    { 1251,  "windows-1251"},
    { 1252,  "Windows-1252"},
    { 1253,  "windows-1253"},
    { 1254,  "windows-1254"},
    { 1255,  "windows-1255"},
    { 1256,  "windows-1256"},
    { 1257,  "windows-1257"},
    { 1258,  "windows-1258"},
    { 1361,  "Johab"},
    { 10000,  "macintosh"},
    { 10001,  "x-mac-japanese"},
    { 10002,  "x-mac-chinesetrad"},
    { 10003,  "x-mac-korean"},
    { 10004,  "x-mac-arabic"},
    { 10005,  "x-mac-hebrew"},
    { 10006,  "x-mac-greek"},
    { 10007,  "x-mac-cyrillic"},
    { 10008,  "x-mac-chinesesimp"},
    { 10010,  "x-mac-romanian"},
    { 10017,  "x-mac-ukrainian"},
    { 10021,  "x-mac-thai"},
    { 10029,  "x-mac-ce"},
    { 10079,  "x-mac-icelandic"},
    { 10081,  "x-mac-turkish"},
    { 10082,  "x-mac-croatian"},
    { 12000,  "utf-32"},
    { 12001,  "utf-32BE"},
    { 20000,  "x-Chinese-CNS"},
    { 20001,  "x-cp20001"},
    { 20002,  "x-Chinese-Eten"},
    { 20003,  "x-cp20003"},
    { 20004,  "x-cp20004"},
    { 20005,  "x-cp20005"},
    { 20105,  "x-IA5"},
    { 20106,  "x-IA5-German"},
    { 20107,  "x-IA5-Swedish"},
    { 20108,  "x-IA5-Norwegian"},
    { 20127,  "us-ascii"},
    { 20261,  "x-cp20261"},
    { 20269,  "x-cp20269"},
    { 20273,  "IBM273"},
    { 20277,  "IBM277"},
    { 20278,  "IBM278"},
    { 20280,  "IBM280"},
    { 20284,  "IBM284"},
    { 20285,  "IBM285"},
    { 20290,  "IBM290"},
    { 20297,  "IBM297"},
    { 20420,  "IBM420"},
    { 20423,  "IBM423"},
    { 20424,  "IBM424"},
    { 20833,  "x-EBCDIC-KoreanExtended"},
    { 20838,  "IBM-Thai"},
    { 20866,  "koi8-r"},
    { 20871,  "IBM871"},
    { 20880,  "IBM880"},
    { 20905,  "IBM905"},
    { 20924,  "IBM00924"},
    { 20932,  "EUC-JP"},
    { 20936,  "x-cp20936"},
    { 20949,  "x-cp20949"},
    { 21025,  "cp1025"},
    { 21866,  "koi8-u"},
    { 28591,  "iso-8859-1"},
    { 28592,  "iso-8859-2"},
    { 28593,  "iso-8859-3"},
    { 28594,  "iso-8859-4"},
    { 28595,  "iso-8859-5"},
    { 28596,  "iso-8859-6"},
    { 28597,  "iso-8859-7"},
    { 28598,  "iso-8859-8"},
    { 28599,  "iso-8859-9"},
    { 28603,  "iso-8859-13"},
    { 28605,  "iso-8859-15"},
    { 29001,  "x-Europa"},
    { 38598,  "iso-8859-8-i"},
    { 50220,  "iso-2022-jp"},
    { 50221,  "csISO2022JP"},
    { 50222,  "iso-2022-jp"},
    { 50225,  "iso-2022-kr"},
    { 50227,  "x-cp50227"},
    { 51932,  "euc-jp"},
    { 51936,  "EUC-CN"},
    { 51949,  "euc-kr"},
    { 52936,  "hz-gb-2312"},
    { 54936,  "GB18030"},
    { 57002,  "x-iscii-de"},
    { 57003,  "x-iscii-be"},
    { 57004,  "x-iscii-ta"},
    { 57005,  "x-iscii-te"},
    { 57006,  "x-iscii-as"},
    { 57007,  "x-iscii-or"},
    { 57008,  "x-iscii-ka"},
    { 57009,  "x-iscii-ma"},
    { 57010,  "x-iscii-gu"},
    { 57011,  "x-iscii-pa"},
    { 65000,  "utf-7"},
    { 65001,  "utf-8"}
  };

    original = NULL;
    charsets = NULL;
    WVC_CharsetList *p = RegisterCharsetName("US-ASCII"); /* this characterset is always valid uppercase !! */
    if (p)
	{
		p->coding = WVCDS_THRU;
		p->codepage = 0;
	}
	
	p = RegisterCharsetName("utf-8"); 
	if (p)
	{
		p->coding = WVCDS_UTF8;
		p->codepage = 0;		
	}
    p = RegisterCharsetName("utf-7"); /* this characterset is always valid uppercase !! */
	if (p)
	{
		p->coding = WVCDS_UTF7;
		p->codepage = 0;		
	}
    p = RegisterCharsetName("iso-8859-15"); 
	if (p)
	{
		p->coding = WVCDS_8859_15;
		p->codepage = 28605;		
	}
	p = RegisterCharsetName("windows-1252");
	if (p)
	{
		p->coding = WVCDS_1252;
		p->codepage = 1252;
    }
	p = RegisterCharsetName("iso-8859-9");
	if (p)
	{
		p->coding = WVCDS_1254;
		p->codepage = 1254;
    }

#ifdef JAPANESE
    foldingStatus = 0;
    RegisterCharsetName("ISO-2022-JP");
    charsets->coding = WVCDS_ISO2022;
    charsets->codepage = OSCP_JAP_SJIS;
#endif
}

WVC_CharsetList * WVRFCDecoder::RegisterCharsetName(const char * str)
{
    WVC_CharsetList * p;
    unsigned int len;

    p = new WVC_CharsetList;
	if (p)
	{
		len = strlen(str);
#ifdef UNICODE    
		p->octetwidth = 1;
#endif    
		p->name = new char[len+1];
		if (p->name)
			strcpy(p->name, str);
#ifdef JAPANESE
		p->coding = WVCDS_IGNORECTRL;
		p->codepage = OSCP_THRU;
#endif
		p->next = charsets;
		charsets = p;
	}
	return p;
}

#ifdef UNICODE
void WVRFCDecoder::RegisterCharsetName(const char * str, unsigned int octets)
{
    WVC_CharsetList * p;
    unsigned int len;

    if ((octets < 1) || (octets > 2))
        octets = 1;

    p = new WVC_CharsetList;
	if (p)
	{
		len = strlen(str);
		p->octetwidth = octets;
		p->name = new char[len+1];
		if (p->name)
			strcpy(p->name, str);
		p->next = charsets;
#ifdef JAPANESE
		p->coding = WVCDS_IGNORECTRL;
		p->charset = OSCP_THRU;
#endif
		charsets = p;
	}
}

void WVRFCDecoder::RegisterCharsetName(wchar_t * str)
{
    char mybuf[WVC_CHARSETNAMELENGTH];

    wcsntostr(mybuf,str,WVC_CHARSETNAMELENGTH);
    RegisterCharsetName(mybuf);
}

void WVRFCDecoder::RegisterCharsetName(wchar_t * str, unsigned int octets)
{
    char mybuf[WVC_CHARSETNAMELENGTH];

    wcsntostr(mybuf,str,WVC_CHARSETNAMELENGTH);
    RegisterCharsetName(mybuf,octets);
}
#endif

WVRFCDecoder::WVRFCDecoder()
{
    Init();
}


WVRFCDecoder::WVRFCDecoder(const BOOL RFCstrictly)
{
    Init();
    strictRFC = RFCstrictly;
}

WVRFCDecoder::WVRFCDecoder(const char * s)
{
    Init();
    RegisterCharsetName(s);
}

WVRFCDecoder::WVRFCDecoder(const BOOL RFCstrictly, const char * s)
{
    Init();
    strictRFC = RFCstrictly;
    RegisterCharsetName(s);
}

#ifdef UNICODE
WVRFCDecoder::WVRFCDecoder(const char * s, unsigned int octets)
{
    Init();
    RegisterCharsetName(s,octets);
}

WVRFCDecoder::WVRFCDecoder(const BOOL RFCstrictly, const char * s, unsigned int octets)
{
    Init();
    strictRFC = RFCstrictly;
    RegisterCharsetName(s,octets);
}

WVRFCDecoder::WVRFCDecoder(wchar_t * s)
{
    Init();
    RegisterCharsetName(s);
}

WVRFCDecoder::WVRFCDecoder(wchar_t * s, unsigned int octets)
{
    Init();
    RegisterCharsetName(s,octets);
}

WVRFCDecoder::WVRFCDecoder(const BOOL RFCstrictly, wchar_t * s)
{
    Init();
    strictRFC = RFCstrictly;
    RegisterCharsetName(s);
}

WVRFCDecoder::WVRFCDecoder(const BOOL RFCstrictly, wchar_t * s, unsigned int octets)
{
    Init();
    strictRFC = RFCstrictly;
    RegisterCharsetName(s,octets);
}
#endif //UNICODE

WVRFCDecoder::~WVRFCDecoder()
{
    WVC_CharsetList * p;
    WVC_CharsetList * nxt;

    p = charsets;
    while ( p != NULL ) {
        nxt = p->next;
        delete p->name;
        delete p;
        p = nxt;
    }
    if (original != NULL)
        delete original;
}

void WVRFCDecoder::ChainSetStatus(const TCHAR ch)
{
#if 1 //def JAPANESE
    CharsetDecoder.Putchar(ch);
    if (CharsetDecoder.Getallowed()) {
      Chain( (TCHAR)CharsetDecoder.Getchar() );
    }
#else
    Chain(ch); /* append character which causes abort past regenerated sequence */
#endif
    status = WVC_US_ASCII_TEXT;
    if ((ch == ' ') || (ch == '(') || (ch == TAB))
       status = WVC_RFC_ENCODING_ALLOWED;
}

void WVRFCDecoder::abort()
{
    /* regenerate input if syntax errors in encoding */
    if ((status == WVC_US_ASCII_TEXT) || (status == WVC_RFC_ENCODING_ALLOWED)) {
        return;  
    };
    Chain('='); /*  after = */
    if (status == WVC_POSSIBLE_RFC_ENCODING) {
        return;
    };
    Chain('?');
    for (CharsetPtr = Charsetname; *CharsetPtr; CharsetPtr++)
        Chain(*CharsetPtr);
    if (status == WVC_CHARSET_PART) {
        return;
    };
    Chain('?');
    for (CoderPtr = Codername; *CoderPtr; CoderPtr++)
        Chain(*CoderPtr);
    if (status == WVC_CODING_PART) {
        return;
    };    
    Chain('?');
    if (original != NULL) {
        original->EndOfStream();
        while (original->Getallowed())
           Chain((TCHAR)original->Getchar());
        delete original;
        original = NULL;
    };
    if (status == WVC_TEXT_PART) {
        return;
    }
    Chain('?');
    if (status == WVC_WAS_RFC_ENCODING) {
        return;
    }
    Chain('=');
}

void WVRFCDecoder::Putchar(const TCHAR ch)
{
       
       switch (ch) {
             case '=' :
                      switch (status) {
                          case WVC_POSSIBLE_RFC_ENCODING:
                                   /* sequence = = no RFC encoding */
                                   Chain('='); /* put the first of them in chain */
                          case WVC_US_ASCII_TEXT:
                                   if (strictRFC) {
                                       /* by RFC a encoded word begins at a start
                                       of a field or after a whitespace or ( */
                                       ChainSetStatus('=');
                                       break;
                                   };
                          case WVC_RFC_ENCODING_ALLOWED:    
                                   status = WVC_POSSIBLE_RFC_ENCODING;
                                   break;
                                   
                          case WVC_WAS_RFC_ENCODING:
                                   status = WVC_SUCCESSFULL_RFC_ENCODING;
                                   if (!strictRFC) {
                                       /* OK it was a RFC encoding, get all out of
                                       the Decoder, which it contains */
                            #if 1 //def JAPANESE
                                       while (Decoder->Getallowed()) {
                                              TCHAR ch = (TCHAR) Decoder->Getchar();
                                              CharsetDecoder.Putchar(ch);
                                              if (CharsetDecoder.Getallowed())
                                                Chain( (TCHAR)CharsetDecoder.Getchar() );
                                       }
                                       CharsetDecoder.EndOfStream();
                                       while( CharsetDecoder.Getallowed() ) {
                                         Chain( (TCHAR)CharsetDecoder.Getchar() );
                                       }
                            #else
                                       while (Decoder->Getallowed()) {
                                              Chain((TCHAR)Decoder->Getchar());
                                       }
                            #endif
                                       delete Decoder;
                                       status = WVC_US_ASCII_TEXT;
                                   }
                                   break;
                          case WVC_TEXT_PART:
								   if (original)
                                       original->Putchar(ch);
                                   Decoder->Putchar(ch);
                                   break;
                          case WVC_CHARSET_PART:
                          case WVC_CODING_PART:
                          case WVC_SUCCESSFULL_RFC_ENCODING:
                                   abort();
                                   ChainSetStatus(ch);
                      };
                      break;
             case '?' :
                      switch (status) {
                          case WVC_US_ASCII_TEXT:
                          case WVC_RFC_ENCODING_ALLOWED:
                                   ChainSetStatus(ch);
                                   break;
                          case WVC_POSSIBLE_RFC_ENCODING:
                                   status = WVC_CHARSET_PART;
                                   CharsetPtr = Charsetname;
                                   *CharsetPtr = '\0';
                                   CharsetnameLen = 0;
                                   break;
                          case WVC_CHARSET_PART:
                                   if (Charsetname[0] == '\0') {
                                       abort();
                                       ChainSetStatus(ch);
                                       break;
                                   };
                                   status = WVC_CODING_PART;
                                   CoderPtr = Codername;
                                   *CoderPtr = '\0';
                                   CodernameLen = 0;
                                   break;
                          case WVC_CODING_PART:
                                   if (Codername[0] == '\0') {
                                       abort();
                                       ChainSetStatus(ch);
                                       break;
                                   };

                                   if (original != NULL ) /* delete old FIFO if exist */
                                      delete original;
                                   original = new FIFO;
                                   
                                   /* now Test what charset we should use */
                                   actualcharset = charsets;
                                   while (actualcharset != NULL) {
                                       if (!_stricmp(actualcharset->name, Charsetname))
                                           break;
                                       actualcharset = actualcharset->next;
                                   };
                                   if (actualcharset == NULL) {
                                       /* no registered charset fit, so  we abort */
                                       abort();
                                       ChainSetStatus(ch);
                                       break;
                                   };
#if	1 //def JAPANESE
                                   CharsetDecoder.SwitchStream( actualcharset->codepage, actualcharset->coding );
                                   while( CharsetDecoder.Getallowed() ) {
                                     Chain( (TCHAR)CharsetDecoder.Getchar() );
                                   }
#endif

#ifdef UNICODE                                 
                                   Decoder = new WVDecoder(Codername,
                                                  actualcharset->octetwidth);
#else
                                   Decoder = new WVDecoder(Codername);
#endif
                                   status = WVC_TEXT_PART;               
                                   break;
                          case WVC_TEXT_PART:
                                   if (original->Pendingchars() == 0) {
                                       abort();
                                       ChainSetStatus(ch);
                                       break;
                                   };
                                   Decoder->EndOfStream();
                                   
                                   status = WVC_WAS_RFC_ENCODING;
                                   break;
                          case WVC_WAS_RFC_ENCODING:
                          case WVC_SUCCESSFULL_RFC_ENCODING:
                                   abort();
                                   ChainSetStatus(ch);
                      };
                      break;
                          
             case '(':
             case ')':
             case '<':
             case '>':
             case '@':
             case ',':
             case ';':
             case ':':
             case '"':
             case '/':
             case '[':
             case ']':
             case '.':    
                  /* these characters are defined in RFC 1522 as especials */
                  /* and not allowed in Characterset-Name or Name of Encoder */
                  /* in Encoded Text OK by syntax, but maybe not usefull */
                  if (status == WVC_TEXT_PART) {
                      if (original)
                          original->Putchar(ch);
                      Decoder->Putchar(ch);
                      break;
                  };
                  if ((status == WVC_SUCCESSFULL_RFC_ENCODING) && (ch != ')')) {
                      abort();
                      ChainSetStatus(ch);
                      break;
                  };
             case 0x00:
             case CR:
             case LF:
             case TAB:             
             case ' ':
                  switch (status) {
                      case WVC_SUCCESSFULL_RFC_ENCODING:
                              while (Decoder->Getallowed()) {
                                     Chain((TCHAR)Decoder->Getchar());
                              }
                              delete Decoder;
                      case WVC_RFC_ENCODING_ALLOWED:
                      case WVC_US_ASCII_TEXT:            
                              ChainSetStatus(ch);
                              break;
                              
                      case WVC_WAS_RFC_ENCODING:
                      case WVC_POSSIBLE_RFC_ENCODING:
                      case WVC_CHARSET_PART:
                      case WVC_CODING_PART:
                      case WVC_TEXT_PART:
                              abort();
                              ChainSetStatus(ch);
                  };
                  break;
             default:
                  switch (status) {
                      case WVC_CHARSET_PART:
                               if (_istascii(ch) &&
                                   (++CharsetnameLen < WVC_CHARSETNAMELENGTH)) {
                                    *CharsetPtr++ = (char)ch;
                                    *CharsetPtr = '\0';
                               } else {
                                     abort();
                                     ChainSetStatus(ch);
                               }
                               break;
                      case WVC_CODING_PART:
                               if (_istascii(ch) &&
                                   (++CodernameLen < WVC_CODERNAMELENGTH)) {
                                    *CoderPtr++ = (char)ch;
                                    *CoderPtr = '\0';
                               } else {
                                    abort();
                                    ChainSetStatus(ch);
                               }
                               break;

                      case WVC_TEXT_PART:
                               if (original)
                                   original->Putchar(ch);
                               Decoder->Putchar(ch);
                               break;
                      case WVC_POSSIBLE_RFC_ENCODING:
                      case WVC_RFC_ENCODING_ALLOWED:
                      case WVC_US_ASCII_TEXT:
                      case WVC_WAS_RFC_ENCODING:
                      case WVC_SUCCESSFULL_RFC_ENCODING:
                               abort();
                               ChainSetStatus(ch);
                  };
       } /* switch (ch) */
}


void WVRFCDecoder::EndOfStream()
{
    
    switch (status) {
        case WVC_SUCCESSFULL_RFC_ENCODING:
                Decoder->EndOfStream();
                while (Decoder->Getallowed()) {
                   Chain((TCHAR)Decoder->Getchar());
                }
                delete Decoder;                          
        case WVC_RFC_ENCODING_ALLOWED:
        case WVC_US_ASCII_TEXT:            
                break;
                                      
        case WVC_WAS_RFC_ENCODING:
        case WVC_POSSIBLE_RFC_ENCODING:
        case WVC_CHARSET_PART:
        case WVC_CODING_PART:
        case WVC_TEXT_PART:
                abort();
    };
    RFCFIFO::EndOfStream();
}

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


WVC_prequeue::WVC_prequeue()
{
    next = NULL;
    Specialcharacters = 0;
}

WVC_prequeue::~WVC_prequeue()
{
}

void WVC_prequeue::setnextword(WVC_prequeue * nextword)
{
    next = nextword;
}

WVC_prequeue * WVC_prequeue::getnextword()
{
    return next;
}


unsigned int WVC_prequeue::GetSpecialchars()
{
    return Specialcharacters;
}


void WVC_prequeue::Putchar(const TCHAR ch)
{
    if ((ch == '?') || (ch == '=') || (ch == '_') || (ch == '(') || (ch == ')'))
       Specialcharacters++; 
    Chain(ch);
}

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

WVRFCCoder::WVRFCCoder()
{
    strcpy(Charsetname,"US-ASCII");
#ifdef UNICODE    
    octetwidth = 1;
#endif    
    lastchar = ' ';
    prequeue = root = new WVC_prequeue;
}

WVRFCCoder::WVRFCCoder(const char * s)
{
    Charsetname[0] = '\0';
    if (strlen(s) < WVC_CHARSETNAMELENGTH)
       strcpy(Charsetname,s);
#ifdef UNICODE       
    octetwidth = 1;
#endif    
    lastchar = ' ';
    prequeue = root = new WVC_prequeue;
}

#ifdef UNICODE
WVRFCCoder::WVRFCCoder(const char * s, unsigned int octets)
{
    Charsetname[0] = '\0';
    if (strlen(s) < WVC_CHARSETNAMELENGTH)
       strcpy(Charsetname,s);
    octetwidth = octets;
    lastchar = ' ';
    prequeue = root = new WVC_prequeue;
}

WVRFCCoder::WVRFCCoder(wchar_t * s)
{
    Charsetname[0] = '\0';
    wcsntostr(Charsetname, s, WVC_CHARSETNAMELENGTH);
    octetwidth = 1;
    lastchar = ' ';
    prequeue = root = new WVC_prequeue;
}

WVRFCCoder::WVRFCCoder(wchar_t * s, unsigned int octets)
{
    Charsetname[0] = '\0';
    wcsntostr(Charsetname, s, WVC_CHARSETNAMELENGTH);
    octetwidth = octets;
    lastchar = ' ';
    prequeue = root = new WVC_prequeue;
}
#endif

WVRFCCoder::~WVRFCCoder()
{
}


BOOL WVRFCCoder::whitespace(const TCHAR ch)
{
     return ((ch == ' ') || (ch == TAB) || (ch == CR) || (ch == LF) || (ch== 0x00) );
}

void WVRFCCoder::processwords(BOOL mustEncode)
{
    char * s;
    WVC_prequeue * p;
    WVC_prequeue * Qstop;        /* this points to the last word of Q-multiwordencoding */
    WVC_prequeue * Bstop;        /* this points to the last word of B-multiwordencoding */

#ifdef UNICODE
    WVQCoder QCoder(octetwidth);
    WVBCoder BCoder(octetwidth);
#else
    WVQCoder QCoder;
    WVBCoder BCoder;
#endif    

    unsigned int Qlen;           /* used to calculate current length which Q will use */
    unsigned int Blen;           /* used to calculate current length which B will use */
    unsigned int MaxQBlen;       /* max length for Q or B encoding output */
    unsigned int Total;          /* count for all characters in multiwordencoding */
    unsigned int maybeTotal;     /* same if we encounter a word to encode later */
    unsigned int ToEncode;       /* count for characters to encode in multiwordencoding */
    unsigned int maybeToEncode;  /* same if we encounter a word to encode later */
    
    
    p = root;
    do {
        if (p->NonASCIIchars() == 0 ) {
            /* all characters are us-ascii */
            while (p->Getallowed())
              Chain((TCHAR)p->Getchar()); /* put it in the inherited fifo */
            root = p->getnextword();
            delete p;
            p = root;
        }   
    } while ((p != NULL) && (p->NonASCIIchars() == 0));
            /* while a word and and all US-ASCII */
    
    if (root) {
        /* there are words which contains non US-ASCII */
        /* determine max length allowed by RFC */
        /* maxlength includes =? charsetname ? Q|B ? and later ?= */
        Qstop = Bstop = p = root;
        MaxQBlen = WVC_MAX_ENCODING_LENGTH - 2 - strlen(Charsetname) -1 - 1 - 1 - 2;
        Total = ToEncode = maybeTotal = maybeToEncode = 0; 
        do {
           maybeTotal += p->Pendingchars();
           maybeToEncode += p->NonASCIIchars() + p->GetSpecialchars();

#ifdef UNICODE
           if (octetwidth == 1) {
#endif
               /* now calculate if we reached maximum encoding length defined in RFC */
               /* in Q-encoding ascii represents itsself nonascii are 3 bytes */           
               Qlen = maybeTotal + 2 * maybeToEncode;

               Blen = 4 * (maybeTotal / 3 );
               /* if not a integral quantum of 24 bits, add padding chars */
               if ((maybeTotal % 3) !=0 ) 
                  Blen += 4;
#ifdef UNICODE
           } else {
               /* this is only usefull for my additional implementation of encoding UNICODE */
               /* THIS DOESN'T FIT IN ANY RFC ! */
               /* WARNING: to be confirm to RFC never use a other octetwidth than 1 */
               /* now calculate if we reached maximum encoding length defined in RFC */
               /* in Q-encoding ascii represents itsself nonascii are 5 bytes */           

               /* Qlen = maybeTotal + 4 * maybeToEncode; */
               Qlen = MaxQBlen + 1; /* we use only B-encoding yet, so set QLen to a maximum */
               
               Blen = 4 * ((2 * maybeTotal) / 3 );
               /* if not a integral quantum of 24 bits, add padding chars */
               if (((2*maybeTotal) % 3) !=0 ) 
                  Blen += 4;
           }
#endif       
           if (p->NonASCIIchars() > 0) {
              /* this is a word which must encoded */
              if (Qlen < MaxQBlen) {
                  /* we have not too much characters, this word is ok */
                  Total = maybeTotal;
                  ToEncode = maybeToEncode;
                  Qstop = p;
              }
              if (Blen < MaxQBlen) {
                  /* we have not too much characters, this word is ok */
                  Total = maybeTotal;
                  ToEncode = maybeToEncode;
                  Bstop = p;
              }
           }                
           p = p->getnextword();   
        } while ((p != NULL) && ((Blen < MaxQBlen ) || (Qlen < MaxQBlen)));
        
        if ((p!=NULL) || (mustEncode)) {
            /* enough words to encode or we must encode because no more data */
            /* use the encoder which give us a encoded word with smallest length */
            /* this is the one which contains still most characters */
            Chain('=');
            Chain('?');
            s = Charsetname;
            while (*s)
               Chain(*s++);
            Chain('?');
            
            /* now we have got the problem to determine which encoding either Q or B we
            should use, by RFC using of Q is recommended, when  most of the characters
            to be encoded are in ASCII. otherwise B is recommended.
            But there is a problem in this recommendation:
            if 16,66 % or more of all characters are nonASCII Q-encoding results a longer
            encoded word then B-encoding.
            The other side is, that Q-Encoding is more useable to read if a newsreader not
            confirms to rfc1522.
            if ISO-8859-* is assumed we can encode:
            
            20 % non ASCII chars means about 41 chars in Q-encoding 42 chars in B-encoding
            30 % non ASCII chars means about 36 chars in Q-encoding 42 chars in B-encoding
            40 % non ASCII chars means about 32 chars in Q-encoding 42 chars in B-encoding
            50 % non ASCII chars means about 29 chars in Q-encoding 42 chars in B-encoding

            Since a text with 40 % non ASCII is NOT readable by people with a old newsreader
            we should probably decrease from 50 % to 40 % in the future.
            
            Nevertheless, if one of Q or B encoding needs to encode in more then these
            characters which is allowed by RFC ( = 75, including RFC1522 envelope )
            we use the encoding which fits.
            */
            if ((Qlen > MaxQBlen ) || /* Q-encoding doesn't fit */
            
              /* ((Blen <= MaxQBlen ) && ( 5 * ToEncode >= 2 * Total )) ) */ /* 40 % */
                 ((Blen <= MaxQBlen ) && ( 2 * ToEncode >= Total )) ) {  /* 50 % */
        
              Chain('B');
              Chain('?');
              do {
                 p = root;
                 while (p->Getallowed()) {
                    BCoder.Putchar((TCHAR)p->Getchar());
                 }
                 root = p->getnextword();
                 delete p;    
              } while ((root != NULL) && (p != Bstop));
              BCoder.EndOfStream();
              while (BCoder.Getallowed())
                  Chain((TCHAR)BCoder.Getchar());
            } else {
              Chain('Q');
              Chain('?');
              do {
                 p = root;
                 while (p->Getallowed()) {
                    QCoder.Putchar((TCHAR)p->Getchar());
                 }
                 root = p->getnextword();
                 delete p;    
              } while ((root != NULL) && (p != Qstop));
              QCoder.EndOfStream();
              while (QCoder.Getallowed())
                  Chain((TCHAR)QCoder.Getchar());
            }
                        
            Chain('?');
            Chain('=');
        } 
    } else {
        prequeue = root; /* there is nothing in chain */
    }    
}

void WVRFCCoder::Putchar(const TCHAR ch)
{
    WVC_prequeue * p;
    BOOL wordboundary;

    wordboundary = FALSE;
    if (whitespace(ch) && !whitespace(lastchar))
           wordboundary = TRUE;
    if (!whitespace(ch) && whitespace(lastchar))
           wordboundary = TRUE;
           
    /* ( and ) stands always alone as a word */
    if ((ch == ')') || (ch == '('))
           wordboundary = TRUE;
    if ((lastchar == ')') || (lastchar == '('))
           wordboundary = TRUE;
           
    if (wordboundary) {
        prequeue->EndOfStream(); /* say FIFO for this word it was all */
        processwords(FALSE); /*check if we can send some characters */
        /* maybe the queue is empty */
        
        if (root != NULL) {
           p = new WVC_prequeue; /* make ready for the next word */
           prequeue->setnextword(p);
           prequeue = p;
        } else {
           prequeue = root = new WVC_prequeue;
        }
    }
    prequeue->Putchar(ch);
    lastchar = ch;
}

void WVRFCCoder::EndOfStream()
{
    while (root != NULL)
       processwords(TRUE);
    FIFO::EndOfStream();
}


/******************************************************************************************/
/* Only internal, don't use it directly, use encodeHeader instead                         */
/******************************************************************************************/

#ifdef UNICODE
void encodeRFC1522W(WVString &str)
{
    WVRFCCoder Coder(L"ISO-8859-1",1);

    const wchar_t *p;
    wchar_t lastchar;

    WVString tmp;
    int idx;

    p = (const wchar_t *)str;

    idx = 0;

    while ( *p ) {
        if (Coder.Putallowed())
            Coder.Putchar(*p++);
        if (Coder.Getallowed()) {
            lastchar = (wchar_t)Coder.Getchar();
	    if (lastchar) {
		tmp += L" "; /* add one blank to get a byte in memory */
		tmp[idx++] = lastchar; /* and replace it with the current char */
	    }
	}
    };
    Coder.EndOfStream();
    while (Coder.Getallowed()) {
        lastchar = (wchar_t)Coder.Getchar();
        if (lastchar) {
		tmp += L" "; /* add one blank */
		tmp[idx++] = lastchar; /* and replace it */
	}
    }
    str = tmp;

}
#endif

void encodeRFC1522A(WVString &str)
{
    WVRFCCoder Coder("ISO-8859-1");

    const char *p;
    char lastchar;

    WVString tmp;
    int idx;

    p = (const char *)str;

    idx = 0;

    while ( *p ) {
        if (Coder.Putallowed())
            Coder.Putchar(*p++);
        if (Coder.Getallowed()) {
            lastchar = (char)Coder.Getchar();
	    if (lastchar) {
		tmp += " "; /* add one blank to get a byte in memory */
		tmp[idx++] = lastchar; /* and replace it with the current char */
	    }
	}
    };
    Coder.EndOfStream();
    while (Coder.Getallowed()) {
        lastchar = (char)Coder.Getchar();
        if (lastchar) {
		tmp += " "; /* add one blank */
		tmp[idx++] = lastchar; /* and replace it */
	}
    }
    str = tmp;
}


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

#ifdef UNICODE
void encodeHeaderW(WVString &str)
{

    if (EncodeRFC1522)
        encodeRFC1522W(str);
}
#endif

void encodeHeaderA(WVString &str)
{

    if (EncodeRFC1522)
        encodeRFC1522A(str);
}

#ifdef UNICODE
void decodeRFC1522W(WVString &str)
{
    WVRFCDecoder Decoder(FALSE,"ISO-8859-1");
    
    const wchar_t *p;
    wchar_t *o;
    wchar_t lastchar;

    if (UseRFC1522) {
        Decoder.RegisterCharsetName("UNICODE-1-1",2);
        p = (const wchar_t *)str;
        o = (wchar_t *)p;
        while ( *p ) {
            if (Decoder.Putallowed())
                Decoder.Putchar(*p++);
            if (Decoder.Getallowed() && (o<p))
                /* there is a character and we have a byte where we can store it */
                *o++ = lastchar = (wchar_t)Decoder.Getchar();
        };
        Decoder.EndOfStream();
        while (Decoder.Getallowed())
            *o++ = lastchar = (wchar_t)Decoder.Getchar();
        if (lastchar)    
            *o = '\0'; /* make shure there is a \0 on the end of the string */
    }
}
#endif

void decodeRFC1522A(WVString &str)
{
    WVRFCDecoder Decoder(FALSE,"ISO-8859-1");

    const char *p;
    char *o;
    char lastchar ='\0';

    if (UseRFC1522 && str) 
	{
        p = (const char *)str;
        o = (char * )p;
		if (!strncmp(p, "\xEF\xBB\xBF", 3))	  //BOM marker
		{
			Decoder.SetEncoding(WVCDS_UTF8);
			p+=3;
		}
        while ( *p ) 
		{
            if (!strncmp(p, "\xEF\xBB\xBF", 3))
			{
				//embeded BOM
				p+=3;
				continue;
			}
			if (Decoder.Putallowed())
                Decoder.Putchar(*p++);
            if (Decoder.Getallowed() && (o<p))
                /* there is a character and we have a byte where we can store it */
                *o++ = lastchar = (char)Decoder.Getchar();
        };
        Decoder.EndOfStream();
        while (Decoder.Getallowed() && (o<p))
            *o++ = lastchar = (char)Decoder.Getchar();
        if (lastchar)    
            *o = '\0'; /* make shure there is a \0 on the end of the string */

        //one more time for embedded UTF-8 in decoded stream
		if (strstr(str, "\xEF\xBB\xBF")	)
		{
			p = (const char *)str;
			o = (char * )p;
			WVRFCDecoder Decoder(FALSE,"UTF-8");

			while ( *p ) 
			{
				if (!strncmp(p, "\xEF\xBB\xBF", 3))	  //BOM marker
				{
					Decoder.SetEncoding(WVCDS_UTF8);
					p+=3;
				}
				if (Decoder.Putallowed())
					Decoder.Putchar(*p++);
				if (Decoder.Getallowed() && (o<p))
					/* there is a character and we have a byte where we can store it */
					*o++ = lastchar = (char)Decoder.Getchar();
			};
			Decoder.EndOfStream();
			while (Decoder.Getallowed() && (o<p))
				*o++ = lastchar = (char)Decoder.Getchar();
			if (lastchar)    
				*o = '\0'; /* make shure there is a \0 on the end of the string */
		}



    }
}


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

#ifdef UNICODE
void encodeStrHeaderW(wchar_t * s)
{
    WVString str;

    if (EncodeRFC1522) {
        str = s;
        encodeRFC1522W(str);
        strcpy(s,(const wchar_t *) str);
    }
}
#endif

void encodeStrHeaderA(char * s)
{
    WVString str;

    if (EncodeRFC1522) {
        str = s;
        encodeRFC1522A(str);
        strcpy(s,(const char *) str);
    }
}

#ifdef UNICODE
void decodeStrRFC1522W(wchar_t * s)
{
    WVRFCDecoder Decoder(FALSE,"ISO-8859-1");
    
    const wchar_t *p;
    wchar_t *o;
    wchar_t lastchar;

    if (UseRFC1522) {
        Decoder.RegisterCharsetName("UNICODE-1-1",2);
        o = p = s;
        while ( *p ) {
            if (Decoder.Putallowed())
                Decoder.Putchar(*p++);
            if (Decoder.Getallowed() && (o<p))
                /* there is a character and we have a byte where we can store it */
                *o++ = lastchar = (wchar_t)Decoder.Getchar();
        };
        Decoder.EndOfStream();
        while (Decoder.Getallowed())
            *o++ = lastchar = (wchar_t)Decoder.Getchar();
        if (lastchar)    
            *o = '\0'; /* make shure there is a \0 on the end of the string */
    }
}
#endif

void decodeStrRFC1522A(char * s)
{
    WVRFCDecoder Decoder(FALSE,"ISO-8859-1");

    char *p;
    char *o;
#ifdef JAPANESE
    char lastchar = !0;
#else
    char lastchar='\0';
#endif


    if (UseRFC1522) {
        o = p = s;
        while ( *p ) {
            if (Decoder.Putallowed())
                Decoder.Putchar(*p++);
            if (Decoder.Getallowed() && (o<p))
                /* there is a character and we have a byte where we can store it */
                *o++ = lastchar = (char)Decoder.Getchar();
        };
        Decoder.EndOfStream();
        while (Decoder.Getallowed())
            *o++ = lastchar = (char)Decoder.Getchar();
        if (lastchar)    
            *o = '\0'; /* make shure there is a \0 on the end of the string */
    }
}


#if 1 //#ifdef JAPANESE
#define MLTLN_INIT 0

BOOL // FALSE- no more
decodeMLStrRFC1522(char *s) // MultiLine Decode wrapper
{
    static WVRFCDecoder *Decoder = NULL;
    static FIFO *Pending = NULL;
    static int FlushMode = 0; // 0:NO 1:TONEXTLINE 2:ALL -1:ERR
    int Pendingchars;

    if (!UseRFC1522) {
      return *s;
    }

    if (s == MLTLN_INIT) {
      delete Decoder;
      Decoder = NULL;
      delete Pending;
      Pending = NULL;
      FlushMode = 0;
      return 0;
    }

    if (!Decoder && FlushMode != -1) {
      Decoder = new WVRFCDecoder(FALSE, "ISO-8859-1");
      Pending = new FIFO;
      if (!Decoder || !Pending) {
        delete Decoder;
        Decoder = NULL;
        delete Pending;
        Pending = NULL;
        FlushMode = -1;
      }
    }
    if (FlushMode == -1)
      return *s;
    else if (*s == '.' && *(s+1) == '\0')
      FlushMode = 2;
    else if (FlushMode == 0 && (*s != ' ' && *s != '\t'))
      FlushMode = 1;

    Pendingchars = Pending->Pendingchars();

    if (FlushMode != 2) {
      char *p = s;
      while (*p) {
        if (Pending->Putallowed())
          Pending->Putchar(*p);
        ++ p;
      }
    }

    if (FlushMode) {
      char *o = s;
      char *f = s + sizeof(CommLineIn) - 2;
      char lastchar = !0;
      char ch = 0;

      while( Pending->Getallowed() && Pendingchars -- ) {
        ch = Pending->Getchar();
        if (Decoder->Putallowed()) {
          Decoder->Putchar(ch);
        }
        if (Decoder->Getallowed()) {
          ch = Decoder->Getchar();
          if (ch == '\n') {
            Decoder->Ungetchar(ch);
            break;
          }
          if (o < f) {
            *o ++ = lastchar = ch;
          }
        }
      }
      Decoder->EndOfStream();
      while( Decoder->Getallowed() ) {
        ch = Decoder->Getchar();
        if (ch == '\n') {
          //Decoder->Ungetchar(ch);
          break;
        }
        if (o < f)
          *o ++ = lastchar = ch;
      }
      if (lastchar) *o = '\0';
      if (Decoder->Getallowed() == NULL && FlushMode == 1)
        FlushMode = 0;
    } else {
      *s = 0;
    }
    return *s;
}

void decodeNetworkCoding(char *p, int coding)
{
    WVCharsetDecoder Decoder(0,coding);
    char *o = p;
    char lastchar = !0;
    while ( *p ) {
      if (Decoder.Putallowed()) {
        Decoder.Putchar(*p);
      }
      ++ p;
      if (Decoder.Getallowed() && o < p ) {
        *o ++ = lastchar = Decoder.Getchar();
      }
    }
    Decoder.EndOfStream();
    while (Decoder.Getallowed()) {
      *o ++ = lastchar = Decoder.Getchar();
    }
    if (lastchar) *o = '\0';
}

void encodeNetworkCoding(char *p, int coding)
{
}
#endif


int my_wtos(char *out, int out_length, wchar_t *in, int in_length)
{
  while( in_length && out_length ) {
    *out ++ = (char) *in ++;
    in_length --;
    out_length --;
  }
  *out = '\0';
  return in_length;
}

int my_stow(wchar_t *out, int out_length, char *in, int in_length)
{
  while( in_length && out_length ) {
    *out ++ = *in ++;
    in_length --;
    out_length --;
  }
  *out = '\0';
  return in_length;
}

#ifdef _WIN32
 extern int system_codepage;
#else
 //TODO: support external dll.
 #define system_codepage      (-1)
 #define IsValidCodePage(cp)  (FALSE)
#endif

#ifdef _MSC_VER
  #define AllocA(ptr, size) \
       void *ptr = _alloca(size)
#else
  class stackcompat {
  public:
    stackcompat(size_t size);
    ~stackcompat();
    char *get();
  protected:
    char *ptr;
  } ALIGNED;
  stackcompat::stackcompat(size_t size)
  {
    ptr = new char[size+1];
  }
  stackcompat::~stackcompat()
  {
    delete[] ptr;
  }
  char *stackcompat::get()
  {
    return ptr;
  }
  #define AllocA(ptr, size) \
       stackcompat alloca##ptr(size); char *ptr = alloca##ptr.get();
#endif
  #define FUNCUSE_UNICONV(ptr, len) AllocA(ptr, len)

void WVDecodeCharset(WVString& str, int codepage)
{
#ifdef _WIN32
  if( codepage == system_codepage || !IsValidCodePage(codepage) )
#endif
  {
    return;
  }
#ifdef _WIN32
  const char *p = (const char *)str;
  int ilen = strlen(p);
  int olen;
  AllocA( utmp, ilen * 3 );  /* MultiByte1~2 -> Unicode1(*2) */
  olen = WVMultiToUni( (wchar_t*)utmp, ilen * 3 / 2, (char *)p, ilen, codepage);
  char *mtmp = str.MakeBuffer( ilen * 3 ); /* Mb 1~2 -> Mb 1~2 */
  WVUniToMulti(mtmp, ilen * 3, (wchar_t*)utmp, olen, CP_ACP);
#endif
}

int WVMultiToUni(wchar_t *out, int out_length, char *in, int in_length, int codepage)
{
#ifdef _WIN32
  int i = MultiByteToWideChar(codepage, 0, in, in_length, out, out_length);
  out[i] = '\0';
  return i;
#endif
  return my_stow(out, out_length, in, in_length);
}

int WVUniToMulti(char* out, int out_length, wchar_t *in, int in_length, int codepage)
{
#ifdef _WIN32
  BOOL DefaultUsed;
  if ( (*in >= 0x2000 && *in <= 0x200b) && in_length == 1)
  {
	out[0] = '\0';
	return 0;
  }
  int i = WideCharToMultiByte(codepage, 0, in, in_length, out, out_length, NULL, &DefaultUsed);
  out[i] = '\0';
  return i;
#endif
  return my_wtos(out, out_length, in, in_length);
}

//#endif // JAPANESE

#endif // RFC1522


#ifdef STANDALONE


main()
{
    char line[256];
    wchar_t wline[256];

    
    strcpy(line,"=?ISO-8859-1?Q?Kristian_K=F6hntop?=");
    strtowcs(wline,line);
    
    decodeRFC1522W(wline, 256 );
    
    WVMessageBox ( NULL,wline, L"Unicode Test", MB_OK | MB_ICONSTOP);
    
    strcpy(line,"=?unicode-1-1?b?IVoA1yFWAD0hUw==?= (was: Üass)");
    strtowcs(wline,line);
    
    decodeRFC1522W(wline, 256);
    encodeHeaderW(wline, 256);
        
    WVMessageBox ( NULL,wline, L"Unicode Test", MB_OK | MB_ICONSTOP);
}
#endif


int CodepageByName( const char * name )
{
    
	struct lang_t {
		const char *name;
		int  codepage_id;
	};



	lang_t langs[] = {
    { "437", 437 },
    { "_iso-2022-jp", 50221 },
    { "_iso-2022-jp$sio", 50222 },
    { "ansi_x3.4-1968", 1252 },
    { "ansi_x3.4-1986", 1252 },
    { "arabic", 28596 },
    { "ascii", 1252 },
    { "asmo-708", 708 },
    { "big5", 950 },
    { "chinese", 936 },
    { "cn-big5", 950 },
    { "cn-gb", 936 },
    { "codepage437", 437 },
    { "cp1026", 1026 },
    { "cp1256", 1256 },
    { "cp367", 1252 },
    { "cp437", 437 },
    { "cp500", 775 },
    { "cp819", 1252 },
    { "cp852", 852 },
    { "cp866", 866 },
    { "cp870", 870 },
    { "csascii", 1252 },
    { "csbig5", 950 },
    { "cseuckr", 51949 },
    { "cseucpkdfmtjapanese", 51932 },
    { "csgb2312", 936 },
    { "csgb231280", 936 },
    { "csiso", 28591 },
    { "csiso2022jp", 50221 },
    { "csiso2022kr", 50225 },
    { "csiso58gb231280", 936 },
    { "csisolatin2", 28592 },
    { "csisolatin4", 28594 },
    { "csisolatin5", 28595 },
    { "csisolatinarabic", 28596 },
    { "csisolatincyrillic", 28595 },
    { "csisolatingreek", 28597 },
    { "csisolatinhebrew", 28598 },
    { "cskoi8r", 20866 },
    { "csksc56011987", 949 },
    { "cspc8", 437 },
    { "csshiftjis", 932 },
    { "csunicode11utf7", 65000 },
    { "cswindows31j", 932 },
    { "cyrillic", 28595 },
    { "dos-720", 720 },
    { "dos-862", 862 },
    { "dos-874", 874 },
    { "ebcdic-cp-us", 37 },
    { "ecma-114", 28596 },
    { "ecma-118", 28597 },
    { "elot_928", 28597 },
    { "euc-cn", 51936 },
    { "euc-jp", 51932 },
    { "euc-kr", 51949 },
    { "extended_unix_code_packed_format_for_japanese", 51932 },
    { "gb2312", 936 },
    { "gb2312-80", 936 },
    { "gb231280", 936 },
    { "gb_2312-80", 936 },
    { "gbk", 936 },
    { "greek", 28597 },
    { "greek8", 28597 },
    { "hebrew", 28598 },
    { "hz-gb-2312", 52936 },
    { "ibm367", 1252 },
    { "ibm437", 437 },
    { "ibm737", 737 },
    { "ibm775", 775 },
    { "ibm819", 1252 },
    { "ibm850", 850 },
    { "ibm852", 852 },
    { "ibm857", 857 },
    { "ibm861", 861 },
    { "ibm866", 866 },
    { "ibm869", 869 },
    { "iso-2022-jp", 50222 },
    { "iso-2022-kr", 50225 },
    { "iso-8859-1", 1252 },
    { "iso-8859-11", 874 },
    { "iso-8859-15", 28605 },
    { "iso-8859-2", 28592 },
    { "iso-8859-3", 28593 },
    { "iso-8859-4", 28594 },
    { "iso-8859-5", 28595 },
    { "iso-8859-6", 28596 },
    { "iso-8859-7", 28597 },
    { "iso-8859-8", 1255 },
    { "iso-8859-8-i", 38598 },
    { "iso-8859-9", 1254 },
    { "iso-ir-100", 1252 },
    { "iso-ir-101", 28592 },
    { "iso-ir-109", 28593 },
    { "iso-ir-110", 28594 },
    { "iso-ir-126", 28597 },
    { "iso-ir-127", 28596 },
    { "iso-ir-138", 28598 },
    { "iso-ir-144", 28595 },
    { "iso-ir-148", 1254 },
    { "iso-ir-149", 949 },
    { "iso-ir-58", 936 },
    { "iso-ir-6", 1252 },
    { "iso-ir-6us", 20127 },
    { "iso646-us", 1252 },
    { "iso8859-1", 1252 },
    { "iso8859-2", 28592 },
    { "iso_646.irv:1991", 1252 },
    { "iso_8859-1", 1252 },
    { "iso_8859-15", 28605 },
    { "iso_8859-1:1987", 1252 },
    { "iso_8859-2", 28592 },
    { "iso_8859-2:1987", 28592 },
    { "iso_8859-3", 28593 },
    { "iso_8859-3:1988", 28593 },
    { "iso_8859-4", 28594 },
    { "iso_8859-4:1988", 28594 },
    { "iso_8859-5", 28595 },
    { "iso_8859-5:1988", 28595 },
    { "iso_8859-6", 28596 },
    { "iso_8859-6:1987", 28596 },
    { "iso_8859-7", 28597 },
    { "iso_8859-7:1987", 28597 },
    { "iso_8859-8", 28598 },
    { "iso_8859-8-i", 1255 },
    { "iso_8859-8:1988", 28598 },
    { "iso_8859-9", 1254 },
    { "iso_8859-9:1989", 1254 },
    { "johab", 1361 },
    { "koi", 20866 },
    { "koi8", 20866 },
    { "koi8-r", 20866 },
    { "koi8-ru", 21866 },
    { "koi8-u", 21866 },
    { "koi8r", 20866 },
    { "korean", 949 },
    { "ks_c_5601", 949 },
    { "ks_c_5601-1987", 949 },
    { "ks_c_5601-1989", 949 },
    { "ks_c_5601_1987", 949 },
    { "ksc5601", 949 },
    { "ksc_5601", 949 },
    { "l1", 28591 },
    { "l2", 28592 },
    { "l3", 28593 },
    { "l4", 28594 },
    { "l5", 28599 },
    { "l9", 28605 },
    { "latin1", 1252 },
    { "latin2", 28592 },
    { "latin3", 28593 },
    { "latin4", 28594 },
    { "latin5", 1254 },
    { "latin9", 28605 },
    { "logical", 38598 },
    { "macintosh", 10000 },
    { "ms_kanji", 932 },
    { "shift-jis", 932 },
    { "shift_jis", 932 },
    { "tis-620", 874 },
    { "unicode", 1200 },
    { "unicode-1-1-utf-7", 65000 },
    { "unicode-1-1-utf-8", 65001 },
    { "unicode-2-0-utf-8", 65001 },
    { "unicodefffe", 1201 },
    { "us", 1252 },
    { "us-ascii", 1252 },
    { "utf-16", 1200 },
    { "utf-7", 65000 },
    { "utf-8", 65001 },
    { "visual", 1255 },
    { "windows-1250", 1250 },
    { "windows-1251", 1251 },
    { "windows-1252", 1252 },
    { "windows-1253", 1253 },
    { "windows-1254", 1254 },
    { "windows-1255", 1255 },
    { "windows-1256", 1256 },
    { "windows-1257", 1257 },
    { "windows-1258", 1258 },
    { "windows-874", 874 },
    { "x-ansi", 1252 },
    { "x-chinese-cns", 20000 },
    { "x-chinese-eten", 20002 },
    { "x-cp1250", 1250 },
    { "x-cp1251", 1251 },
    { "x-ebcdic-arabic", 20420 },
    { "x-ebcdic-cp-us-euro", 1140 },
    { "x-ebcdic-cyrillicrussian", 20880 },
    { "x-ebcdic-cyrillicserbianbulgarian", 21025 },
    { "x-ebcdic-denmarknorway", 20277 },
    { "x-ebcdic-denmarknorway-euro", 1142 },
    { "x-ebcdic-finlandsweden", 20278 },
    { "x-ebcdic-finlandsweden-euro", 1143 },
    { "x-ebcdic-france", 1143 },
    { "x-ebcdic-france-euro", 1147 },
    { "x-ebcdic-germany", 20273 },
    { "x-ebcdic-germany-euro", 1141 },
    { "x-ebcdic-greek", 20423 },
    { "x-ebcdic-greekmodern", 875 },
    { "x-ebcdic-hebrew", 20424 },
    { "x-ebcdic-icelandic", 20871 },
    { "x-ebcdic-icelandic-euro", 1149 },
    { "x-ebcdic-international-euro", 1148 },
    { "x-ebcdic-italy", 20280 },
    { "x-ebcdic-italy-euro", 1144 },
    { "x-ebcdic-japaneseandjapaneselatin", 50939 },
    { "x-ebcdic-japaneseandkana", 50930 },
    { "x-ebcdic-japaneseanduscanada", 50931 },
    { "x-ebcdic-japanesekatakana", 20290 },
    { "x-ebcdic-koreanandkoreanextended", 50933 },
    { "x-ebcdic-koreanextended", 20833 },
    { "x-ebcdic-simplifiedchinese", 50935 },
    { "x-ebcdic-spain", 20284 },
    { "x-ebcdic-spain-euro", 1145 },
    { "x-ebcdic-thai", 20838 },
    { "x-ebcdic-traditionalchinese", 50937 },
    { "x-ebcdic-turkish", 20905 },
    { "x-ebcdic-uk", 20285 },
    { "x-ebcdic-uk-euro", 1146 },
    { "x-euc", 51932 },
    { "x-euc-cn", 51936 },
    { "x-euc-jp", 51932 },
    { "x-europa", 29001 },
    { "x-ia5", 20105 },
    { "x-ia5-german", 20106 },
    { "x-ia5-norwegian", 20108 },
    { "x-ia5-swedish", 20107 },
    { "x-iscii-as", 57006 },
    { "x-iscii-be", 57003 },
    { "x-iscii-de", 57002 },
    { "x-iscii-gu", 57010 },
    { "x-iscii-ka", 57008 },
    { "x-iscii-ma", 57009 },
    { "x-iscii-or", 57007 },
    { "x-iscii-pa", 57011 },
    { "x-iscii-ta", 57004 },
    { "x-iscii-te", 57005 },
    { "x-mac-arabic", 10004 },
    { "x-mac-ce", 10029 },
    { "x-mac-chinesesimp", 10008 },
    { "x-mac-chinesetrad", 10002 },
    { "x-mac-cyrillic", 10007 },
    { "x-mac-greek", 10006 },
    { "x-mac-hebrew", 10005 },
    { "x-mac-icelandic", 10079 },
    { "x-mac-japanese", 10001 },
    { "x-mac-korean", 10003 },
    { "x-mac-turkish", 10081 },
    { "x-ms-cp932", 932 },
    { "x-sjis", 932 },
    { "x-unicode-2-0-utf-7", 65000 },
    { "x-unicode-2-0-utf-8", 65001 },
    { "x-x-big5", 950 }
	};
	
	
	int begin = -1;
    int end = sizeof( langs ) / sizeof( lang_t );    
    while(1)
    {
        int centre = ( begin + end ) / 2;
        if( centre == begin )
            return -1;
        int cmp = stricmp( name, langs[ centre ].name );
        if( !cmp ) 
            return langs[ centre ].codepage_id;
        ( ( cmp < 0 ) ? end : begin ) = centre;
    } 
    return -1;
}
