/*
 * The CodePages API
 *
 * Copyright 2002 Alexander S.Kresin <alex@belacy.belgorod.su>
 * Copyright 2009-2012 Przemyslaw Czerpak <druzus / at / priv.onet.pl>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; see the file LICENSE.txt.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA (or visit https://www.gnu.org/licenses/).
 *
 * As a special exception, the Harbour Project gives permission for
 * additional uses of the text contained in its release of Harbour.
 *
 * The exception is that, if you link the Harbour libraries with other
 * files to produce an executable, this does not by itself cause the
 * resulting executable to be covered by the GNU General Public License.
 * Your use of that executable is in no way restricted on account of
 * linking the Harbour library code into it.
 *
 * This exception does not however invalidate any other reasons why
 * the executable file might be covered by the GNU General Public License.
 *
 * This exception applies only to the code released by the Harbour
 * Project under the name Harbour.  If you copy code from other
 * Harbour Project or Free Software Foundation releases into a copy of
 * Harbour, as the General Public License permits, the exception does
 * not apply to the code that you add in this way.  To avoid misleading
 * anyone as to the status of such modified files, you must delete
 * this exception notice from them.
 *
 * If you write modifications of your own for Harbour, it is your choice
 * whether to permit this exception to apply to your modifications.
 * If you do not wish that, delete this exception notice.
 *
 */

#include "hbapi.h"
#include "hbapierr.h"
#include "hbapicdp.h"
#include "hbthread.h"


/* MT macros */
#define HB_CDP_LOCK()    hb_threadEnterCriticalSection( &s_cdpMtx )
#define HB_CDP_UNLOCK()  hb_threadLeaveCriticalSection( &s_cdpMtx )
static HB_CRITICAL_NEW( s_cdpMtx );


#define NUMBER_OF_CHARS  256

#define HB_MAX_CTRL_CODE      0x266B

static const HB_WCHAR s_uniCtrls[ 32 ] =
{
   0x2007, 0x263A, 0x263B, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022,
   0x25D8, 0x25CB, 0x25D9, 0x2642, 0x2640, 0x266A, 0x266B, 0x263C,
   0x25BA, 0x25C4, 0x2195, 0x203C, 0x00B6, 0x00A7, 0x25AC, 0x21A8,
   0x2191, 0x2193, 0x2192, 0x2190, 0x221F, 0x2194, 0x25B2, 0x25BC
};

static HB_UCHAR * s_rev_ctrl = NULL;

static const HB_WCHAR s_uniCodes[ NUMBER_OF_CHARS ] =
{
   0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
   0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
   0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
   0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
   0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
   0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
   0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
   0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
   0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
   0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
   0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
   0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
   0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
   0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
   0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
   0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
   0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
   0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
   0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,
   0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,
   0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,
   0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
   0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
   0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
   0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
   0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
   0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
   0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
   0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4,
   0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
   0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248,
   0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
};

HB_UNITABLE hb_uniTbl_437 = { HB_CPID_437, s_uniCodes, NULL, 0 };

static HB_CDP_GET_FUNC( hb_cdpStd_get );
static HB_CDP_PUT_FUNC( hb_cdpStd_put );
static HB_CDP_LEN_FUNC( hb_cdpStd_len );

static HB_CDP_CMP_FUNC( hb_cdpBin_cmp );
static HB_CDP_CMP_FUNC( hb_cdpBin_cmpi );

static HB_CDP_GET_FUNC( hb_cdpUTF8_get );
static HB_CDP_PUT_FUNC( hb_cdpUTF8_put );
static HB_CDP_LEN_FUNC( hb_cdpUTF8_len );

static HB_UNITABLE hb_uniTbl_UTF8 = { HB_CPID_437, s_uniCodes, NULL, 0 };

static HB_UCHAR s_en_buffer[ 0x300 ];

/* pseudo codepage for translations only */
static HB_CODEPAGE s_utf8_codepage =
   { "UTF8", "UTF-8", &hb_uniTbl_UTF8,
     NULL, NULL, NULL, NULL, NULL, 0,
     HB_CDP_TYPE_CUSTOM | HB_CDP_TYPE_UTF8 | HB_CDP_TYPE_BINSORT,
     hb_cdpUTF8_get, hb_cdpUTF8_put, hb_cdpUTF8_len,
     NULL, NULL, NULL, hb_cdpBin_cmp, hb_cdpBin_cmpi,
     0, 0, NULL, NULL, NULL };

HB_CODEPAGE_ANNOUNCE( UTF8 )

static HB_CODEPAGE s_en_codepage =
   { "EN", "English CP-437", HB_UNITB_437,
     NULL, NULL, NULL, NULL, NULL, 0,
     HB_CDP_TYPE_BINSORT,
     hb_cdpStd_get, hb_cdpStd_put, hb_cdpStd_len,
     NULL, NULL, NULL, hb_cdpBin_cmp, hb_cdpBin_cmpi,
     0, 0, NULL, NULL, &s_utf8_codepage };

HB_CODEPAGE_ANNOUNCE( EN )

#define HB_UCFB_FIRST   0x00C0
#define HB_UCFB_LAST    0x1EF9
#define HB_UCFB_BITS    5

static const HB_BYTE s_fb_idx[ 242 ] =
{
   0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
   0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
   0x17, 0x18
};

static const HB_BYTE s_fb_val[ 794 ] =
{
   0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x00, 0x43, 0x45, 0x45, 0x45, 0x45,
   0x49, 0x49, 0x49, 0x49, 0x00, 0x4E, 0x4F, 0x4F, 0x4F, 0x4F, 0x4F, 0x00,
   0x4F, 0x55, 0x55, 0x55, 0x55, 0x59, 0x00, 0x00, 0x61, 0x61, 0x61, 0x61,
   0x61, 0x61, 0x00, 0x63, 0x65, 0x65, 0x65, 0x65, 0x69, 0x69, 0x69, 0x69,
   0x00, 0x6E, 0x6F, 0x6F, 0x6F, 0x6F, 0x6F, 0x00, 0x6F, 0x75, 0x75, 0x75,
   0x75, 0x79, 0x00, 0x79, 0x41, 0x61, 0x41, 0x61, 0x41, 0x61, 0x43, 0x63,
   0x43, 0x63, 0x43, 0x63, 0x43, 0x63, 0x44, 0x64, 0x44, 0x64, 0x45, 0x65,
   0x45, 0x65, 0x45, 0x65, 0x45, 0x65, 0x45, 0x65, 0x47, 0x67, 0x47, 0x67,
   0x47, 0x67, 0x47, 0x67, 0x48, 0x68, 0x48, 0x68, 0x49, 0x69, 0x49, 0x69,
   0x49, 0x69, 0x49, 0x69, 0x49, 0x00, 0x00, 0x00, 0x4A, 0x6A, 0x4B, 0x6B,
   0x00, 0x4C, 0x6C, 0x4C, 0x6C, 0x4C, 0x6C, 0x00, 0x00, 0x4C, 0x6C, 0x4E,
   0x6E, 0x4E, 0x6E, 0x4E, 0x6E, 0x00, 0x00, 0x00, 0x4F, 0x6F, 0x4F, 0x6F,
   0x4F, 0x6F, 0x00, 0x00, 0x52, 0x72, 0x52, 0x72, 0x52, 0x72, 0x53, 0x73,
   0x53, 0x73, 0x53, 0x73, 0x53, 0x73, 0x54, 0x74, 0x54, 0x74, 0x54, 0x74,
   0x55, 0x75, 0x55, 0x75, 0x55, 0x75, 0x55, 0x75, 0x55, 0x75, 0x55, 0x75,
   0x57, 0x77, 0x59, 0x79, 0x59, 0x5A, 0x7A, 0x5A, 0x7A, 0x5A, 0x7A, 0x00,
   0x62, 0x42, 0x42, 0x62, 0x00, 0x00, 0x00, 0x43, 0x63, 0x00, 0x44, 0x44,
   0x64, 0x00, 0x00, 0x00, 0x00, 0x46, 0x66, 0x47, 0x00, 0x00, 0x00, 0x49,
   0x4B, 0x6B, 0x6C, 0x00, 0x00, 0x4E, 0x6E, 0x4F, 0x4F, 0x6F, 0x00, 0x00,
   0x50, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x54, 0x74, 0x54, 0x55,
   0x75, 0x00, 0x56, 0x59, 0x79, 0x5A, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x61, 0x49, 0x69, 0x4F, 0x6F, 0x55,
   0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x47, 0x67, 0x47, 0x67, 0x4B, 0x6B, 0x4F, 0x6F,
   0x00, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x00, 0x00, 0x47, 0x67, 0x00, 0x00,
   0x4E, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x61, 0x41, 0x61,
   0x45, 0x65, 0x45, 0x65, 0x49, 0x69, 0x49, 0x69, 0x4F, 0x6F, 0x4F, 0x6F,
   0x52, 0x72, 0x52, 0x72, 0x55, 0x75, 0x55, 0x75, 0x53, 0x73, 0x54, 0x74,
   0x00, 0x00, 0x48, 0x68, 0x4E, 0x64, 0x00, 0x00, 0x5A, 0x7A, 0x41, 0x61,
   0x45, 0x65, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x6F, 0x00, 0x00, 0x59, 0x79,
   0x6C, 0x6E, 0x74, 0x00, 0x00, 0x00, 0x41, 0x43, 0x63, 0x4C, 0x54, 0x73,
   0x7A, 0x00, 0x00, 0x42, 0x00, 0x00, 0x45, 0x65, 0x4A, 0x6A, 0x00, 0x71,
   0x52, 0x72, 0x59, 0x79, 0x00, 0x00, 0x00, 0x62, 0x00, 0x63, 0x64, 0x64,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x68, 0x00, 0x69, 0x00, 0x00, 0x6C, 0x6C, 0x6C, 0x00, 0x00,
   0x00, 0x6D, 0x6E, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x72, 0x72, 0x72, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x74, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0x00, 0x7A, 0x7A, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x00,
   0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x41, 0x61, 0x42, 0x62, 0x42, 0x62, 0x42, 0x62,
   0x00, 0x00, 0x44, 0x64, 0x44, 0x64, 0x44, 0x64, 0x44, 0x64, 0x44, 0x64,
   0x00, 0x00, 0x00, 0x00, 0x45, 0x65, 0x45, 0x65, 0x00, 0x00, 0x46, 0x66,
   0x47, 0x67, 0x48, 0x68, 0x48, 0x68, 0x48, 0x68, 0x48, 0x68, 0x48, 0x68,
   0x49, 0x69, 0x00, 0x00, 0x4B, 0x6B, 0x4B, 0x6B, 0x4B, 0x6B, 0x4C, 0x6C,
   0x00, 0x00, 0x4C, 0x6C, 0x4C, 0x6C, 0x4D, 0x6D, 0x4D, 0x6D, 0x4D, 0x6D,
   0x4E, 0x6E, 0x4E, 0x6E, 0x4E, 0x6E, 0x4E, 0x6E, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x50, 0x70, 0x50, 0x70, 0x52, 0x72, 0x52, 0x72,
   0x00, 0x00, 0x52, 0x72, 0x53, 0x73, 0x53, 0x73, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x54, 0x74, 0x54, 0x74, 0x54, 0x74, 0x54, 0x74, 0x55, 0x75,
   0x55, 0x75, 0x55, 0x75, 0x00, 0x00, 0x00, 0x00, 0x56, 0x76, 0x56, 0x76,
   0x57, 0x77, 0x57, 0x77, 0x57, 0x77, 0x57, 0x77, 0x57, 0x77, 0x58, 0x78,
   0x58, 0x78, 0x59, 0x79, 0x5A, 0x7A, 0x5A, 0x7A, 0x5A, 0x7A, 0x68, 0x74,
   0x77, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x61, 0x41, 0x61,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x65, 0x45, 0x65,
   0x45, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x49, 0x69, 0x49, 0x69, 0x4F, 0x6F, 0x4F, 0x6F, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x55, 0x75, 0x55, 0x75, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x79, 0x59, 0x79, 0x59, 0x79,
   0x59, 0x79
};

static HB_USHORT s_uc_fback( int n )
{
   n -= HB_UCFB_FIRST;
   if( n >= 0 && n <= ( HB_UCFB_LAST - HB_UCFB_FIRST ) )
   {
      return s_fb_val[ ( s_fb_idx[ n >> HB_UCFB_BITS ] << HB_UCFB_BITS ) +
                       ( n & ( ( 1 << HB_UCFB_BITS ) - 1 ) ) ];
   }
   return 0;
}

static PHB_CODEPAGE s_cdpList = NULL;


/*
 * conversions
 */
void hb_cdpBuildTransTable( PHB_UNITABLE uniTable )
{
   HB_CDP_LOCK();
   if( uniTable->uniTrans == NULL )
   {
      HB_UCHAR * uniTrans;
      HB_WCHAR wcMax = 0;
      HB_BOOL fLatinFallback = HB_TRUE;
      int i;

      for( i = 'A'; i <= 'Z'; ++i )
      {
         if( uniTable->uniCodes[ i ] != ( HB_WCHAR ) i )
         {
            fLatinFallback = HB_FALSE;
            break;
         }
      }
      if( fLatinFallback )
         wcMax = HB_UCFB_LAST;

      for( i = 0; i < 256; ++i )
      {
         HB_WCHAR wc = uniTable->uniCodes[ i ];
         if( wc > wcMax )
            wcMax = wc;
      }
      uniTrans = ( HB_UCHAR * ) hb_xgrabz( ( wcMax + 1 ) * sizeof( HB_UCHAR ) );
      for( i = 0; i < 256; ++i )
      {
         if( uniTable->uniCodes[ i ] )
            uniTrans[ uniTable->uniCodes[ i ] ] = ( HB_UCHAR ) i;
      }
      if( fLatinFallback )
      {
         for( i = HB_UCFB_FIRST; i <= HB_UCFB_LAST; ++i )
         {
            if( uniTrans[ i ] == 0 )
            {
               HB_WCHAR wc = s_uc_fback( i );
               if( wc > 0 && wc <= wcMax && uniTable->uniCodes[ uniTrans[ wc ] ] == wc )
                  uniTrans[ i ] = uniTrans[ wc ];
            }
         }
      }

      uniTable->wcMax = wcMax;
      uniTable->uniTrans = uniTrans;

      if( s_rev_ctrl == NULL )
      {
         wcMax = HB_MAX_CTRL_CODE;
         s_rev_ctrl = ( HB_UCHAR * ) hb_xgrabz( ( wcMax + 1 ) * sizeof( HB_UCHAR ) );
         for( i = 0; i < 32; ++i )
            s_rev_ctrl[ s_uniCtrls[ i ] ] = ( HB_UCHAR ) i;
      }
   }
   HB_CDP_UNLOCK();
}

/*
 * standard conversion functions
 */
static HB_BOOL hb_cdpStd_get( PHB_CODEPAGE cdp,
                              const char * pSrc, HB_SIZE nLen,
                              HB_SIZE * pnIndex, HB_WCHAR * wc )
{
   if( *pnIndex < nLen )
   {
      HB_UCHAR uc = ( HB_UCHAR ) pSrc[ ( *pnIndex )++ ];

      *wc = cdp->uniTable->uniCodes[ uc ];
      if( *wc == 0 )
         *wc = uc;

      return HB_TRUE;
   }
   return HB_FALSE;
}

static HB_BOOL hb_cdpStd_put( PHB_CODEPAGE cdp,
                              char * pDst, HB_SIZE nLen,
                              HB_SIZE * pnIndex, HB_WCHAR wc )
{
   if( *pnIndex < nLen )
   {
      if( cdp->uniTable->uniTrans == NULL )
         hb_cdpBuildTransTable( cdp->uniTable );

      if( wc <= cdp->uniTable->wcMax &&
          cdp->uniTable->uniTrans[ wc ] )
         pDst[ ( *pnIndex )++ ] = cdp->uniTable->uniTrans[ wc ];
      else
         pDst[ ( *pnIndex )++ ] = wc >= 0x100 ? HB_CDP_ERROR_ASCCHAR : ( HB_UCHAR ) wc;

      return HB_TRUE;
   }
   return HB_FALSE;
}

static int hb_cdpStd_len( PHB_CODEPAGE cdp, HB_WCHAR wc )
{
   HB_SYMBOL_UNUSED( cdp );
   HB_SYMBOL_UNUSED( wc );
   return 1;
}

static int hb_cdpBin_cmp( PHB_CODEPAGE cdp,
                          const char * szFirst, HB_SIZE nLenFirst,
                          const char * szSecond, HB_SIZE nLenSecond,
                          HB_BOOL fExact )
{
   HB_SIZE nLen = nLenFirst < nLenSecond ? nLenFirst : nLenSecond;
   int iRet = memcmp( szFirst, szSecond, nLen );

   HB_SYMBOL_UNUSED( cdp );

   if( iRet == 0 )
   {
      if( nLenSecond > nLenFirst )
         iRet = -1;
      else if( fExact && nLenSecond < nLenFirst )
         iRet = 1;
   }
   else if( iRet > 0 )
      iRet = 1;
   else
      iRet = -1;

   return iRet;
}

static int hb_cdpBin_cmpi( PHB_CODEPAGE cdp,
                           const char * szFirst, HB_SIZE nLenFirst,
                           const char * szSecond, HB_SIZE nLenSecond,
                           HB_BOOL fExact )
{
   HB_SIZE nLen = nLenFirst < nLenSecond ? nLenFirst : nLenSecond;
   int iRet = 0;

   while( nLen-- )
   {
      HB_UCHAR u1 = cdp->upper[ ( HB_UCHAR ) *szFirst++ ],
               u2 = cdp->upper[ ( HB_UCHAR ) *szSecond++ ];
      if( u1 != u2 )
      {
         iRet = ( u1 < u2 ) ? -1 : 1;
         break;
      }
   }

   if( iRet == 0 )
   {
      if( nLenSecond > nLenFirst )
         iRet = -1;
      else if( fExact && nLenSecond < nLenFirst )
         iRet = 1;
   }

   return iRet;
}

static int hb_cdpStd_cmp( PHB_CODEPAGE cdp,
                          const char * szFirst, HB_SIZE nLenFirst,
                          const char * szSecond, HB_SIZE nLenSecond,
                          HB_BOOL fExact )
{
   int iRet = 0, iAcc = 0, n1, n2;
   HB_SIZE nPos, nLen;

   nLen = nLenFirst < nLenSecond ? nLenFirst : nLenSecond;
   for( nPos = 0; nPos < nLen; ++szFirst, ++szSecond, ++nPos )
   {
      if( *szFirst != *szSecond )
      {
         n1 = ( HB_UCHAR ) cdp->sort[ ( HB_UCHAR ) *szFirst ];
         n2 = ( HB_UCHAR ) cdp->sort[ ( HB_UCHAR ) *szSecond ];
         if( n1 != n2 )
         {
            iRet = ( n1 < n2 ) ? -1 : 1;
            break;
         }
         if( iAcc == 0 && ( fExact || ( nLenFirst == nLenSecond && cdp->acc ) ) )
         {
            if( cdp->acc )
               iAcc = ( cdp->acc[ ( HB_UCHAR ) *szFirst ] <
                        cdp->acc[ ( HB_UCHAR ) *szSecond ] ) ? -1 : 1;
            else
               iAcc = ( ( HB_UCHAR ) *szFirst < ( HB_UCHAR ) *szSecond ) ? -1 : 1;
         }
      }
   }

   if( ! iRet )
   {
      if( iAcc )
         iRet = iAcc;
      else if( nLenSecond > nLenFirst )
         iRet = -1;
      else if( fExact && nLenSecond < nLenFirst )
         iRet = 1;
   }

   return iRet;
}

static int hb_cdpStd_cmpi( PHB_CODEPAGE cdp,
                           const char * szFirst, HB_SIZE nLenFirst,
                           const char * szSecond, HB_SIZE nLenSecond,
                           HB_BOOL fExact )
{
   int iRet = 0, iAcc = 0;
   HB_SIZE nPos, nLen;

   nLen = nLenFirst < nLenSecond ? nLenFirst : nLenSecond;
   for( nPos = 0; nPos < nLen; ++szFirst, ++szSecond, ++nPos )
   {
      int u1 = cdp->upper[ ( HB_UCHAR ) *szFirst ];
      int u2 = cdp->upper[ ( HB_UCHAR ) *szSecond ];

      if( u1 != u2 )
      {
         int n1 = ( HB_UCHAR ) cdp->sort[ u1 ];
         int n2 = ( HB_UCHAR ) cdp->sort[ u2 ];
         if( n1 != n2 )
         {
            iRet = ( n1 < n2 ) ? -1 : 1;
            break;
         }
         if( iAcc == 0 && ( fExact || ( nLenFirst == nLenSecond && cdp->acc ) ) )
         {
            if( cdp->acc )
               iAcc = ( cdp->acc[ u1 ] < cdp->acc[ u2 ] ) ? -1 : 1;
            else
               iAcc = ( u1 < u2 ) ? -1 : 1;
         }
      }
   }

   if( ! iRet )
   {
      if( iAcc )
         iRet = iAcc;
      else if( nLenSecond > nLenFirst )
         iRet = -1;
      else if( fExact && nLenSecond < nLenFirst )
         iRet = 1;
   }

   return iRet;
}


static HB_BOOL hb_cdpUTF8_get( PHB_CODEPAGE cdp,
                               const char * pSrc, HB_SIZE nLen,
                               HB_SIZE * pnIndex, HB_WCHAR * wc )
{
   HB_SYMBOL_UNUSED( cdp );

   if( *pnIndex < nLen )
   {
      hb_cdpUTF8GetU16( pSrc, nLen, pnIndex, wc );
      return HB_TRUE;
   }
   *wc = 0;
   return HB_FALSE;
}

static HB_BOOL hb_cdpUTF8_put( PHB_CODEPAGE cdp,
                               char * pDst, HB_SIZE nLen,
                               HB_SIZE * pnIndex, HB_WCHAR wc )
{
   int i = hb_cdpUTF8CharSize( wc );

   HB_SYMBOL_UNUSED( cdp );

   if( *pnIndex + i <= nLen )
   {
      hb_cdpU16CharToUTF8( &pDst[ *pnIndex ], wc );
      *pnIndex += i;
      return HB_TRUE;
   }
   return HB_FALSE;
}

static int hb_cdpUTF8_len( PHB_CODEPAGE cdp, HB_WCHAR wc )
{
   HB_SYMBOL_UNUSED( cdp );

   return hb_cdpUTF8CharSize( wc );
}

static HB_BOOL hb_cdpMulti_get( PHB_CODEPAGE cdp,
                                const char * pSrc, HB_SIZE nLen,
                                HB_SIZE * pnIndex, HB_WCHAR * wc )
{
   if( *pnIndex < nLen )
   {
      HB_UCHAR uc = ( HB_UCHAR ) pSrc[ ( *pnIndex )++ ];

      *wc = cdp->uniTable->uniCodes[ uc ];
      if( *wc == 0 )
         *wc = uc;
      else if( ( cdp->flags[ uc ] & HB_CDP_MULTI1 ) != 0 &&
          *pnIndex < nLen )
      {
         HB_UCHAR uc2 = ( HB_UCHAR ) pSrc[ *pnIndex + 1 ];
         if( ( cdp->flags[ uc2 ] & HB_CDP_MULTI2 ) != 0 )
         {
            int i;
            for( i = 0; i < cdp->nMulti; ++i )
            {
               if( uc2 == cdp->multi[ i ].cLast[ 0 ] ||
                   uc2 == cdp->multi[ i ].cLast[ 1 ] )
               {
                  if( uc == cdp->multi[ i ].cFirst[ 0 ] )
                  {
                     if( cdp->multi[ i ].wcUp )
                     {
                        *wc = cdp->multi[ i ].wcUp;
                        ++( *pnIndex );
                     }
                     break;
                  }
                  else if( uc == cdp->multi[ i ].cFirst[ 1 ] )
                  {
                     if( cdp->multi[ i ].wcLo )
                     {
                        *wc = cdp->multi[ i ].wcLo;
                        ++( *pnIndex );
                     }
                     break;
                  }
               }
            }
         }
      }
      return HB_TRUE;
   }
   return HB_FALSE;
}

static HB_BOOL hb_cdpMulti_put( PHB_CODEPAGE cdp,
                                char * pDst, HB_SIZE nLen,
                                HB_SIZE * pnIndex, HB_WCHAR wc )
{
   if( *pnIndex < nLen )
   {
      if( cdp->uniTable->uniTrans == NULL )
         hb_cdpBuildTransTable( cdp->uniTable );

      if( wc <= cdp->uniTable->wcMax && cdp->uniTable->uniTrans[ wc ] )
         pDst[ ( *pnIndex )++ ] = cdp->uniTable->uniTrans[ wc ];
      else if( wc == 0 )
         pDst[ ( *pnIndex )++ ] = 0;
      else
      {
         int i;
         for( i = 0; i < cdp->nMulti; ++i )
         {
            if( wc == cdp->multi[ i ].wcUp )
            {
               pDst[ ( *pnIndex )++ ] = cdp->multi[ i ].cFirst[ 0 ];
               if( *pnIndex < nLen )
                  pDst[ ( *pnIndex )++ ] = cdp->multi[ i ].cLast[ 0 ];
               return HB_TRUE;
            }
            if( wc == cdp->multi[ i ].wcLo )
            {
               pDst[ ( *pnIndex )++ ] = cdp->multi[ i ].cFirst[ 1 ];
               if( *pnIndex < nLen )
                  pDst[ ( *pnIndex )++ ] = cdp->multi[ i ].cLast[ 1 ];
               return HB_TRUE;
            }
         }
         pDst[ ( *pnIndex )++ ] = wc >= 0x100 ? HB_CDP_ERROR_ASCCHAR : ( HB_UCHAR ) wc;
      }
      return HB_TRUE;
   }
   return HB_FALSE;
}

static int hb_cdpMulti_len( PHB_CODEPAGE cdp, HB_WCHAR wc )
{
   int n = 1;

   if( wc )
   {
      int i;
      for( i = 0; i < cdp->nMulti; ++i )
      {
         if( wc == cdp->multi[ i ].wcUp ||
             wc == cdp->multi[ i ].wcLo )
         {
            ++n;
            break;
         }
      }
   }
   return n;
}

static int hb_cdpMulti_weight( PHB_CODEPAGE cdp, const char * szChar )
{
   PHB_MULTICHAR pmulti = cdp->multi;
   int i;

   for( i = cdp->nMulti; i; --i, ++pmulti )
   {
      if( ( szChar[ 0 ] == pmulti->cFirst[ 0 ] ||
            szChar[ 0 ] == pmulti->cFirst[ 1 ] ) &&
          ( szChar[ 1 ] == pmulti->cLast[ 0 ] ||
            szChar[ 1 ] == pmulti->cLast[ 1 ] ) )
      {
         return ( szChar[ 0 ] == pmulti->cFirst[ 0 ] ) ?
                pmulti->sortUp : pmulti->sortLo;
      }
   }

   return 0;
}

static int hb_cdpMulti_cmp( PHB_CODEPAGE cdp,
                            const char * szFirst, HB_SIZE nLenFirst,
                            const char * szSecond, HB_SIZE nLenSecond,
                            HB_BOOL fExact )
{
   int iRet = 0, iAcc = 0, n;
   HB_SIZE nPos, nLen;

   nLen = nLenFirst < nLenSecond ? nLenFirst : nLenSecond;
   for( nPos = 0; nPos < nLen; ++szFirst, ++szSecond, ++nPos )
   {
      int n1, n2;

      HB_UCHAR u1 = ( HB_UCHAR ) *szFirst;
      HB_UCHAR u2 = ( HB_UCHAR ) *szSecond;

      n1 = cdp->sort[ u1 ];
      if( ( cdp->flags[ u1 ] & HB_CDP_MULTI1 ) != 0 &&
          ( nPos < nLenFirst - 1 ) &&
          ( cdp->flags[ ( HB_UCHAR ) szFirst[ 1 ] ] & HB_CDP_MULTI2 ) != 0 )
      {
         n = hb_cdpMulti_weight( cdp, szFirst );
         if( n != 0 )
         {
            n1 = n;
            ++szFirst;
            if( --nLenFirst < nLen )
               nLen = nLenFirst;
         }
      }
      n2 = cdp->sort[ u2 ];
      if( ( cdp->flags[ u2 ] & HB_CDP_MULTI1 ) != 0 &&
          ( nPos < nLenSecond - 1 ) &&
          ( cdp->flags[ ( HB_UCHAR ) szSecond[ 1 ] ] & HB_CDP_MULTI2 ) != 0 )
      {
         n = hb_cdpMulti_weight( cdp, szSecond );
         if( n != 0 )
         {
            n2 = n;
            ++szSecond;
            if( --nLenSecond < nLen )
               nLen = nLenSecond;
         }
      }
      if( n1 != n2 )
      {
         if( n1 == 0 || n2 == 0 )
            /* One of characters doesn't belong to the national characters */
            iRet = ( u1 < u2 ) ? -1 : 1;
         else
            iRet = ( n1 < n2 ) ? -1 : 1;
         break;
      }
      else if( u1 != u2 )
      {
         if( n1 == 0 )
         {
            iRet = ( u1 < u2 ) ? -1 : 1;
            break;
         }
         if( iAcc == 0 && ( fExact || ( nLenFirst == nLenSecond && cdp->acc ) ) )
         {
            if( cdp->acc )
               iAcc = ( cdp->acc[ u1 ] < cdp->acc[ u2 ] ) ? -1 : 1;
            else
               iAcc = ( u1 < u2 ) ? -1 : 1;
         }
      }
   }

   if( ! iRet )
   {
      if( iAcc )
         iRet = iAcc;
      else if( nLenSecond > nLenFirst )
         iRet = -1;
      else if( fExact && nLenSecond < nLenFirst )
         iRet = 1;
   }

   return iRet;
}

static int hb_cdpMulti_weightI( PHB_CODEPAGE cdp, const char * szChar )
{
   PHB_MULTICHAR pmulti = cdp->multi;
   int i;

   for( i = cdp->nMulti; i; --i, ++pmulti )
   {
      if( ( szChar[ 0 ] == pmulti->cFirst[ 0 ] ||
            szChar[ 0 ] == pmulti->cFirst[ 1 ] ) &&
          ( szChar[ 1 ] == pmulti->cLast[ 0 ] ||
            szChar[ 1 ] == pmulti->cLast[ 1 ] ) )
      {
         return pmulti->sortUp;
      }
   }

   return 0;
}

static int hb_cdpMulti_cmpi( PHB_CODEPAGE cdp,
                             const char * szFirst, HB_SIZE nLenFirst,
                             const char * szSecond, HB_SIZE nLenSecond,
                             HB_BOOL fExact )
{
   int iRet = 0, iAcc = 0;
   HB_SIZE nPos, nLen;

   nLen = nLenFirst < nLenSecond ? nLenFirst : nLenSecond;
   for( nPos = 0; nPos < nLen; ++szFirst, ++szSecond, ++nPos )
   {
      int n, u1, u2, n1, n2;

      u1 = cdp->upper[ ( HB_UCHAR ) *szFirst ];
      u2 = cdp->upper[ ( HB_UCHAR ) *szSecond ];

      if( ( cdp->flags[ u1 ] & HB_CDP_MULTI1 ) != 0 &&
          ( nPos < nLenFirst - 1 ) &&
          ( cdp->flags[ ( HB_UCHAR ) szFirst[ 1 ] ] & HB_CDP_MULTI2 ) != 0 )
      {
         n = hb_cdpMulti_weightI( cdp, szFirst );
         if( n != 0 )
         {
            n1 = n;
            ++szFirst;
            if( --nLenFirst < nLen )
               nLen = nLenFirst;
         }
         else
            n1 = cdp->sort[ u1 ];
      }
      else
         n1 = cdp->sort[ u1 ];

      if( ( cdp->flags[ u2 ] & HB_CDP_MULTI1 ) != 0 &&
          ( nPos < nLenSecond - 1 ) &&
          ( cdp->flags[ ( HB_UCHAR ) szSecond[ 1 ] ] & HB_CDP_MULTI2 ) != 0 )
      {
         n = hb_cdpMulti_weightI( cdp, szSecond );
         if( n != 0 )
         {
            n2 = n;
            ++szSecond;
            if( --nLenSecond < nLen )
               nLen = nLenSecond;
         }
         else
            n2 = cdp->sort[ u2 ];
      }
      else
         n2 = cdp->sort[ u2 ];

      if( n1 != n2 )
      {
         if( n1 == 0 || n2 == 0 )
            /* One of characters doesn't belong to the national characters */
            iRet = ( u1 < u2 ) ? -1 : 1;
         else
            iRet = ( n1 < n2 ) ? -1 : 1;
         break;
      }
      else if( u1 != u2 )
      {
         if( n1 == 0 )
         {
            iRet = ( u1 < u2 ) ? -1 : 1;
            break;
         }
         if( iAcc == 0 && ( fExact || ( nLenFirst == nLenSecond && cdp->acc ) ) )
         {
            if( cdp->acc )
               iAcc = ( cdp->acc[ u1 ] < cdp->acc[ u2 ] ) ? -1 : 1;
            else
               iAcc = ( u1 < u2 ) ? -1 : 1;
         }
      }
   }

   if( ! iRet )
   {
      if( iAcc )
         iRet = iAcc;
      else if( nLenSecond > nLenFirst )
         iRet = -1;
      else if( fExact && nLenSecond < nLenFirst )
         iRet = 1;
   }

   return iRet;
}


/* Warning: this functions works only with byte oriented CPs */
HB_BOOL hb_cdpIsDigit( PHB_CODEPAGE cdp, int iChar )
{
   if( cdp )
      return ( cdp->flags[ iChar & 0x0ff ] & HB_CDP_DIGIT ) != 0;
   else
      return HB_ISDIGIT( iChar );
}

HB_BOOL hb_cdpIsAlpha( PHB_CODEPAGE cdp, int iChar )
{
   if( cdp )
      return ( cdp->flags[ iChar & 0x0ff ] & HB_CDP_ALPHA ) != 0;
   else
      return HB_ISALPHA( iChar );
}

HB_BOOL hb_cdpIsLower( PHB_CODEPAGE cdp, int iChar )
{
   if( cdp )
      return ( cdp->flags[ iChar & 0x0ff ] & HB_CDP_LOWER ) != 0;
   else
      return HB_ISLOWER( iChar );
}

HB_BOOL hb_cdpIsUpper( PHB_CODEPAGE cdp, int iChar )
{
   if( cdp )
      return ( cdp->flags[ iChar & 0x0ff ] & HB_CDP_UPPER ) != 0;
   else
      return HB_ISUPPER( iChar );
}

HB_BOOL hb_charIsDigit( int iChar )
{
   PHB_CODEPAGE cdp = hb_vmCDP();

   if( cdp )
      return ( cdp->flags[ iChar & 0x0ff ] & HB_CDP_DIGIT ) != 0;
   else
      return HB_ISDIGIT( iChar );
}

HB_BOOL hb_charIsAlpha( int iChar )
{
   PHB_CODEPAGE cdp = hb_vmCDP();

   if( cdp )
      return ( cdp->flags[ iChar & 0x0ff ] & HB_CDP_ALPHA ) != 0;
   else
      return HB_ISALPHA( iChar );
}

HB_BOOL hb_charIsLower( int iChar )
{
   PHB_CODEPAGE cdp = hb_vmCDP();

   if( cdp )
      return ( cdp->flags[ iChar & 0x0ff ] & HB_CDP_LOWER ) != 0;
   else
      return HB_ISLOWER( iChar );
}

HB_BOOL hb_charIsUpper( int iChar )
{
   PHB_CODEPAGE cdp = hb_vmCDP();

   if( cdp )
      return ( cdp->flags[ iChar & 0x0ff ] & HB_CDP_UPPER ) != 0;
   else
      return HB_ISUPPER( iChar );
}

int hb_charLower( int iChar )
{
   PHB_CODEPAGE cdp = hb_vmCDP();

   if( cdp )
      return cdp->lower[ iChar & 0x0ff ];
   else
      return HB_TOLOWER( iChar );
}

int hb_charUpper( int iChar )
{
   PHB_CODEPAGE cdp = hb_vmCDP();

   if( cdp )
      return cdp->upper[ iChar & 0x0ff ];
   else
      return HB_TOUPPER( iChar );
}

char * hb_strLower( char * szText, HB_SIZE nLen )
{
   HB_TRACE( HB_TR_DEBUG, ( "hb_strLower(%s, %" HB_PFS "u)", szText, nLen ) );

   {
      PHB_CODEPAGE cdp = hb_vmCDP();
      HB_SIZE u;

      if( cdp )
         for( u = 0; u < nLen; u++ )
            szText[ u ] = ( char ) cdp->lower[ ( HB_UCHAR ) szText[ u ] ];
      else
         for( u = 0; u < nLen; u++ )
            szText[ u ] = HB_TOLOWER( szText[ u ] );
   }

   return szText;
}

char * hb_strUpper( char * szText, HB_SIZE nLen )
{
   HB_TRACE( HB_TR_DEBUG, ( "hb_strUpper(%s, %" HB_PFS "u)", szText, nLen ) );

   {
      PHB_CODEPAGE cdp = hb_vmCDP();
      HB_SIZE u;

      if( cdp )
         for( u = 0; u < nLen; u++ )
            szText[ u ] = ( char ) cdp->upper[ ( HB_UCHAR ) szText[ u ] ];
      else
         for( u = 0; u < nLen; u++ )
            szText[ u ] = HB_TOUPPER( szText[ u ] );
   }

   return szText;
}

/* end of byte only functions */


/*
 * basic CP functions
 */

HB_BOOL hb_strIsDigit( const char * szChar )
{
   PHB_CODEPAGE cdp = hb_vmCDP();

   if( cdp )
   {
      if( HB_CDP_ISCUSTOM( cdp ) && cdp->wcharFlags )
      {
         HB_SIZE n = 0;
         HB_WCHAR wc;
         if( HB_CDPCHAR_GET( cdp, szChar, hb_strnlen( szChar, 6 ), &n, &wc ) )
            return ( HB_CDPCHAR_FLAGS( cdp, wc ) & HB_CDP_DIGIT ) != 0;
         else
            return HB_FALSE;
      }
      else
         return ( cdp->flags[ ( HB_UCHAR ) *szChar ] & HB_CDP_DIGIT ) != 0;
   }
   else
   {
      int iChar = ( HB_UCHAR ) *szChar;
      return HB_ISDIGIT( iChar );
   }
}

HB_BOOL hb_strIsAlpha( const char * szChar )
{
   PHB_CODEPAGE cdp = hb_vmCDP();

   if( cdp )
   {
      if( HB_CDP_ISCUSTOM( cdp ) && cdp->wcharFlags )
      {
         HB_SIZE n = 0;
         HB_WCHAR wc;
         if( HB_CDPCHAR_GET( cdp, szChar, hb_strnlen( szChar, 6 ), &n, &wc ) )
            return ( HB_CDPCHAR_FLAGS( cdp, wc ) & HB_CDP_ALPHA ) != 0;
         else
            return HB_FALSE;
      }
      else
         return ( cdp->flags[ ( HB_UCHAR ) *szChar ] & HB_CDP_ALPHA ) != 0;
   }
   else
   {
      int iChar = ( HB_UCHAR ) *szChar;
      return HB_ISALPHA( iChar );
   }
}

HB_BOOL hb_strIsLower( const char * szChar )
{
   PHB_CODEPAGE cdp = hb_vmCDP();

   if( cdp )
   {
      if( HB_CDP_ISCUSTOM( cdp ) && cdp->wcharFlags )
      {
         HB_SIZE n = 0;
         HB_WCHAR wc;
         if( HB_CDPCHAR_GET( cdp, szChar, hb_strnlen( szChar, 6 ), &n, &wc ) )
            return ( HB_CDPCHAR_FLAGS( cdp, wc ) & HB_CDP_LOWER ) != 0;
         else
            return HB_FALSE;
      }
      else
         return ( cdp->flags[ ( HB_UCHAR ) *szChar ] & HB_CDP_LOWER ) != 0;
   }
   else
   {
      int iChar = ( HB_UCHAR ) *szChar;
      return HB_ISLOWER( iChar );
   }
}

HB_BOOL hb_strIsUpper( const char * szChar )
{
   PHB_CODEPAGE cdp = hb_vmCDP();

   if( cdp )
   {
      if( HB_CDP_ISCUSTOM( cdp ) && cdp->wcharFlags )
      {
         HB_SIZE n = 0;
         HB_WCHAR wc;
         if( HB_CDPCHAR_GET( cdp, szChar, hb_strnlen( szChar, 6 ), &n, &wc ) )
            return ( HB_CDPCHAR_FLAGS( cdp, wc ) & HB_CDP_UPPER ) != 0;
         else
            return HB_FALSE;
      }
      else
         return ( cdp->flags[ ( HB_UCHAR ) *szChar ] & HB_CDP_UPPER ) != 0;
   }
   else
   {
      int iChar = ( HB_UCHAR ) *szChar;
      return HB_ISUPPER( iChar );
   }
}

/*
 * comparison
 */

const HB_UCHAR * hb_cdpGetSortTab( PHB_CODEPAGE cdp )
{
   return ( cdp->nMulti == 0 && cdp->nACSort == 0 &&
            cdp->wcharCmp == NULL ) ? cdp->sort : NULL;
}

int hb_cdpcmp( const char * szFirst, HB_SIZE nLenFirst,
               const char * szSecond, HB_SIZE nLenSecond,
               PHB_CODEPAGE cdp, HB_BOOL fExact )
{
   return HB_CDPCHAR_CMP( cdp, szFirst, nLenFirst, szSecond, nLenSecond, fExact );
}

int hb_cdpicmp( const char * szFirst, HB_SIZE nLenFirst,
                const char * szSecond, HB_SIZE nLenSecond,
                PHB_CODEPAGE cdp, HB_BOOL fExact )
{
   return HB_CDPCHAR_CMPI( cdp, szFirst, nLenFirst, szSecond, nLenSecond, fExact );
}

/*
 * UTF-8 conversions
 */
int hb_cdpUTF8CharSize( HB_WCHAR32 wc )
{
   if ( ( HB_I32 ) wc < 0 )
      wc = HB_CDP_ERROR_UNICHAR;

   if( wc < 0x0080 )
      return 1;
   else if( wc < 0x0800 )
      return 2;
   else if( wc < 0xFFFF )
      return 3;
   else if( wc < 0x1FFFFF )
      return 4;
   else if( wc < 0x3FFFFFF )
      return 5;
   else                         /* if( wc <= 0x7FFFFFFF ) */
      return 6;
}

int hb_cdpU32CharToUTF8( char * szUTF8, HB_WCHAR32 wc )
{
   int n;

   if( ( HB_I32 ) wc < 0 )
      wc = HB_CDP_ERROR_UNICHAR;

   if( wc < 0x0080 )
   {
      szUTF8[ 0 ] = wc & 0xFF;
      n = 1;
   }
   else if( wc < 0x0800 )
   {
      szUTF8[ 0 ] = 0xc0 | ( ( wc >> 6 ) & 0x1F );
      szUTF8[ 1 ] = 0x80 | ( wc & 0x3F );
      n = 2;
   }
   else if( wc < 0xFFFF )
   {
      szUTF8[ 0 ] = 0xE0 | ( ( wc >> 12 ) & 0x0F );
      szUTF8[ 1 ] = 0x80 | ( ( wc >> 6 ) & 0x3F );
      szUTF8[ 2 ] = 0x80 | ( wc & 0x3F );
      n = 3;
   }
   else if( wc < 0x1FFFFF )
   {
      szUTF8[ 0 ] = 0xF0 | ( ( wc >> 18 ) & 0x07 );
      szUTF8[ 1 ] = 0x80 | ( ( wc >> 12 ) & 0x3F );
      szUTF8[ 2 ] = 0x80 | ( ( wc >> 6 ) & 0x3F );
      szUTF8[ 3 ] = 0x80 | ( wc & 0x3F );
      n = 4;
   }
   else if( wc < 0x3FFFFFF )
   {
      szUTF8[ 0 ] = 0xF8 | ( ( wc >> 24 ) & 0x03 );
      szUTF8[ 1 ] = 0x80 | ( ( wc >> 18 ) & 0x3F );
      szUTF8[ 2 ] = 0x80 | ( ( wc >> 12 ) & 0x3F );
      szUTF8[ 3 ] = 0x80 | ( ( wc >> 6 ) & 0x3F );
      szUTF8[ 4 ] = 0x80 | ( wc & 0x3F );
      n = 5;
   }
   else                         /* if( wc <= 0x7FFFFFFF ) */
   {
      szUTF8[ 0 ] = 0xFC | ( ( wc >> 30 ) & 0x01 );
      szUTF8[ 1 ] = 0x80 | ( ( wc >> 24 ) & 0x3F );
      szUTF8[ 2 ] = 0x80 | ( ( wc >> 18 ) & 0x3F );
      szUTF8[ 3 ] = 0x80 | ( ( wc >> 12 ) & 0x3F );
      szUTF8[ 4 ] = 0x80 | ( ( wc >> 6 ) & 0x3F );
      szUTF8[ 5 ] = 0x80 | ( wc & 0x3F );
      n = 6;
   }
   return n;
}

int hb_cdpU16CharToUTF8( char * szUTF8, HB_WCHAR wc )
{
   int n;

   if( wc < 0x0080 )
   {
      szUTF8[ 0 ] = wc & 0xff;
      n = 1;
   }
   else if( wc < 0x0800 )
   {
      szUTF8[ 0 ] = 0xc0 | ( ( wc >> 6 ) & 0x1f );
      szUTF8[ 1 ] = 0x80 | ( wc & 0x3f );
      n = 2;
   }
   else                         /* if( wc <= 0xffff ) */
   {
      szUTF8[ 0 ] = 0xe0 | ( ( wc >> 12 ) & 0x0f );
      szUTF8[ 1 ] = 0x80 | ( ( wc >> 6 ) & 0x3f );
      szUTF8[ 2 ] = 0x80 | ( wc & 0x3f );
      n = 3;
   }
/*
   else
   {
      n = 0;
   }
 */
   return n;
}

HB_BOOL hb_cdpUTF8ToU16NextChar( HB_UCHAR ucChar, int * n, HB_WCHAR * pwc )
{
   if( *n > 0 )
   {
      if( ( ucChar & 0xc0 ) != 0x80 )
      {
         *n = 0;
         return HB_FALSE;
      }
      *pwc = ( *pwc << 6 ) | ( ucChar & 0x3f );
      ( *n )--;
      return HB_TRUE;
   }

   *n = 0;
   *pwc = ucChar;
   if( ucChar >= 0xc0 )
   {
      if( ucChar < 0xe0 )
      {
         *pwc &= 0x1f;
         *n = 1;
      }
      else if( ucChar < 0xf0 )
      {
         *pwc &= 0x0f;
         *n = 2;
      }
      else if( ucChar < 0xf8 )
      {
         *pwc &= 0x07;
         *n = 3;
      }
      else if( ucChar < 0xfc )
      {
         *pwc &= 0x03;
         *n = 4;
      }
      else if( ucChar < 0xfe )
      {
         *pwc &= 0x01;
         *n = 5;
      }
      else
      {
         *n = 0;
         return HB_FALSE;
      }
   }
   return HB_TRUE;
}

HB_BOOL hb_cdpUTF8GetU32( const char * pSrc, HB_SIZE nLen,
                          HB_SIZE * pnIndex, HB_WCHAR32 * pWC )
{
   HB_SIZE nIndex = *pnIndex;
   HB_WCHAR32 wc = 0;
   int n = -1;

   if( nIndex < nLen )
   {
      HB_WCHAR32 wcMin = 0;   /* forbid overlong encodings */
      HB_UCHAR uc = ( HB_UCHAR ) pSrc[ nIndex++ ];

      if( uc < 0x80 )
      {
         wc = uc;
         n = 0;
      }
      else if( uc >= 0xc0 )
      {
         if( uc < 0xe0 )
         {
            wc = uc & 0x1f;
            n = 1;
            wcMin = 0x80;
         }
         else if( uc < 0xf0 )
         {
            wc = uc & 0x0f;
            n = 2;
            wcMin = 0x800;
         }
         else if( uc < 0xf8 )
         {
            wc = uc & 0x07;
            n = 3;
            wcMin = 0x10000;
         }
         else if( uc < 0xfc )
         {
            wc = uc & 0x03;
            n = 4;
            wcMin = 0x200000;
         }
         else if( uc < 0xfe )
         {
            wc = uc & 0x01;
            n = 5;
            wcMin = 0x4000000;
         }
         while( n > 0 && nIndex < nLen )
         {
            uc = ( HB_UCHAR ) pSrc[ nIndex ];
            if( ( uc & 0xc0 ) != 0x80 )
               break;
            wc = ( wc << 6 ) | ( uc & 0x3f );
            ++nIndex;
            --n;
         }
      }

      if( n != 0 || wc < wcMin )
      {
         wc = HB_CDP_ERROR_UNICHAR;
         while( n-- > 0 && nIndex < nLen )
         {
            uc = ( HB_UCHAR ) pSrc[ nIndex ];
            if( uc < 0x80 || ( uc >= 0xc2 && uc <= 0xf4 ) )
               break;
            ++nIndex;
         }
         n = -1;
      }
   }

   *pnIndex = nIndex;
   *pWC = wc;

   return n == 0;
}

HB_BOOL hb_cdpUTF8GetUCS( const char * pSrc, HB_SIZE nLen,
                          HB_SIZE * pnIndex, HB_WCHAR32 * pWC )
{
   HB_BOOL fResult;

   fResult = hb_cdpUTF8GetU32( pSrc, nLen, pnIndex, pWC );
   if( fResult && ( *pWC > HB_CDP_UNICODE_MAX ||
         ( *pWC >= HB_CDP_SURROGATE_FIRST && *pWC <= HB_CDP_SURROGATE_LAST ) ) )
   {
      *pWC = HB_CDP_ERROR_UNICHAR;
      fResult = HB_FALSE;
   }
   return fResult;
}

HB_BOOL hb_cdpUTF8GetU16( const char * pSrc, HB_SIZE nLen,
                          HB_SIZE * pnIndex, HB_WCHAR16 * pWC )
{
   HB_WCHAR32 wc;
   HB_BOOL fResult;

   fResult = hb_cdpUTF8GetU32( pSrc, nLen, pnIndex, &wc );

   if( fResult && wc > 0xFFFF )
   {
      wc = HB_CDP_ERROR_UNICHAR;
      fResult = HB_FALSE;
   }
   *pWC = ( HB_WCHAR16 ) wc;

   return fResult;
}

HB_BOOL hb_cdpUTF8Validate( const char * pSrc, HB_SIZE nLen )
{
   HB_SIZE nIndex = 0;

   while( nIndex < nLen )
   {
      HB_WCHAR32 wc;
      if( ! hb_cdpUTF8GetUCS( pSrc, nLen, &nIndex, &wc ) )
         return HB_FALSE;
   }
   return HB_TRUE;
}

HB_SIZE hb_cdpUTF8StringLength( const char * pSrc, HB_SIZE nLen )
{
   HB_SIZE nIndex = 0, nChars = 0;
   HB_WCHAR32 wc;

   while( nIndex < nLen )
   {
       hb_cdpUTF8GetU32( pSrc, nLen, &nIndex, &wc );
       ++nChars;
   }

   return nChars;
}

HB_SIZE hb_cdpUTF8StringAt( const char * szNeedle, HB_SIZE nLenN,
                            const char * szHaystack, HB_SIZE nLenH,
                            HB_SIZE nStart, HB_SIZE nEnd, HB_BOOL fReverse )
{
   HB_SIZE nPosN = 0;
   HB_SIZE nPosH = 0;
   HB_SIZE nPosX = 0;
   HB_SIZE nPos = 0;
   HB_SIZE nRAt = 0;
   HB_SIZE nAt = 0;

   HB_WCHAR32 wcN = 0;
   HB_WCHAR32 wcH = 0;

   while( nPosH < nLenH && nPosN < nLenN && nPos < nEnd )
   {
      hb_cdpUTF8GetU32( szHaystack, nLenH, &nPosH, &wcH );
      if( ++nPos < nStart )
         continue;

      hb_cdpUTF8GetU32( szNeedle, nLenN, &nPosN, &wcN );

      if( wcH == wcN )
      {
         if( nAt == 0 )
         {
            nAt = nPos;
            nPosX = nPosH;
         }

         if( nPosN == nLenN )
         {
            if( fReverse )
            {
               nRAt = nAt;
               nPos = nAt;
               nAt = 0;
               nPosH = nPosX;
               nPosX = 0;
               nPosN = 0;
            }
            else
               return nAt;
         }
      }
      else
      {
         if( nAt )
         {
            nPos = nAt;
            nAt = 0;
            nPosH = nPosX;
            nPosX = 0;
         }
         nPosN = 0;
      }
   }

   return nRAt;
}

HB_WCHAR32 hb_cdpUTF8StringPeek( const char * pSrc, HB_SIZE nLen, HB_SIZE nPos )
{
   HB_SIZE nIndex = 0;

   while( nIndex < nLen )
   {
      HB_WCHAR32 wc;
      hb_cdpUTF8GetU32( pSrc, nLen, &nIndex, &wc );
      if( nPos-- == 0 )
         return wc;
   }
   return 0;
}

/* caller must free the returned buffer if not NULL */
char * hb_cdpUTF8StringSubstr( const char * pSrc, HB_SIZE nLen,
                               HB_SIZE nFrom, HB_SIZE nCount, HB_SIZE * pulDest )
{
   HB_SIZE nDst = 0;
   char * pDst = NULL;

   if( nCount && nLen )
   {
      HB_WCHAR32 wc;
      HB_SIZE nPos = 0;

      while( nPos < nLen && nFrom )
      {
         hb_cdpUTF8GetU32( pSrc, nLen, &nPos, &wc );
         --nFrom;
      }

      if( nPos < nLen )
      {
         HB_SIZE nCnt = nCount;

         nFrom = nPos;
         do
         {
            hb_cdpUTF8GetU32( pSrc, nLen, &nPos, &wc );
         }
         while( nPos < nLen && --nCnt );

         nDst = nPos - nFrom;
         pDst = ( char * ) hb_xgrab( nDst + 1 );
         memcpy( pDst, &pSrc[ nFrom ], nDst );
         pDst[ nDst ] = '\0';
      }
   }

   if( pulDest )
      *pulDest = nDst;

   return pDst;
}

HB_BOOL hb_cdpGetFromUTF8( PHB_CODEPAGE cdp, HB_UCHAR ch,
                           int * n, HB_WCHAR * pwc )
{
   if( hb_cdpUTF8ToU16NextChar( ch, n, pwc ) )
   {
      if( *n == 0 && cdp )
      {
         if( HB_CDP_ISCUSTOM( cdp ) )
         {
            if( HB_CDPCHAR_LEN( cdp, *pwc ) == 1 )
            {
               HB_SIZE nSize = 0;
               char c;

               if( HB_CDPCHAR_PUT( cdp, &c, 1, &nSize, *pwc ) )
                  *pwc = ( HB_UCHAR ) c;
            }
         }
         else
         {
            if( cdp->uniTable->uniTrans == NULL )
               hb_cdpBuildTransTable( cdp->uniTable );

            if( *pwc <= cdp->uniTable->wcMax )
            {
               HB_UCHAR uc = cdp->uniTable->uniTrans[ *pwc ];
               if( uc )
                  *pwc = uc;
            }
         }
      }
      return HB_TRUE;
   }
   return HB_FALSE;
}

HB_SIZE hb_cdpStrAsUTF8Len( PHB_CODEPAGE cdp,
                            const char * pSrc, HB_SIZE nSrc,
                            HB_SIZE nMax )
{
   HB_SIZE nPosS, nPosD;
   int i, n;

   if( HB_CDP_ISUTF8( cdp ) )
      return ( nMax && nSrc > nMax ) ? nMax : nSrc;
   else if( HB_CDP_ISCUSTOM( cdp ) )
   {
      HB_WCHAR wc;
      nPosS = nPosD = 0;
      while( HB_CDPCHAR_GET( cdp, pSrc, nSrc, &nPosS, &wc ) )
      {
         i = hb_cdpUTF8CharSize( wc );
         if( nMax && nPosD + i > nMax )
            break;
         nPosD += i;
      }
   }
   else
   {
      const HB_WCHAR * uniCodes = cdp->uniTable->uniCodes;
      for( nPosS = nPosD = 0; nPosS < nSrc; ++nPosS )
      {
         HB_UCHAR uc = ( HB_UCHAR ) pSrc[ nPosS ];
         HB_WCHAR wc = uniCodes[ uc ];

         if( wc == 0 )
            wc = uc;
         n = hb_cdpUTF8CharSize( wc );
         if( nMax && nPosD + n > nMax )
            break;
         nPosD += n;
      }
   }

   return nPosD;
}

HB_SIZE hb_cdpStrToUTF8( PHB_CODEPAGE cdp,
                         const char * pSrc, HB_SIZE nSrc,
                         char * pDst, HB_SIZE nDst )
{
   HB_SIZE nPosS, nPosD, u;

   if( HB_CDP_ISUTF8( cdp ) )
   {
      if( nSrc > nDst )
         nSrc = nDst;
      else if( nSrc < nDst )
         pDst[ nSrc ] = '\0';
      memcpy( pDst, pSrc, nSrc );
      return nSrc;
   }
   else if( HB_CDP_ISCUSTOM( cdp ) )
   {
      HB_WCHAR wc;
      nPosS = nPosD = 0;
      while( nPosD < nDst && HB_CDPCHAR_GET( cdp, pSrc, nSrc, &nPosS, &wc ) )
      {
         u = hb_cdpUTF8CharSize( wc );
         if( nPosD + u <= nDst )
         {
            hb_cdpU16CharToUTF8( &pDst[ nPosD ], wc );
            nPosD += u;
         }
         else
            break;
      }
   }
   else
   {
      const HB_WCHAR * uniCodes = cdp->uniTable->uniCodes;
      for( nPosS = nPosD = 0; nPosS < nSrc && nPosD < nDst; ++nPosS )
      {
         HB_UCHAR uc = ( HB_UCHAR ) pSrc[ nPosS ];
         HB_WCHAR wc = uniCodes[ uc ];

         if( wc == 0 )
            wc = uc;
         u = hb_cdpUTF8CharSize( wc );
         if( nPosD + u <= nDst )
         {
            hb_cdpU16CharToUTF8( &pDst[ nPosD ], wc );
            nPosD += u;
         }
         else
            break;
      }
   }

   if( nPosD < nDst )
      pDst[ nPosD ] = '\0';

   return nPosD;
}

HB_SIZE hb_cdpStrToUTF8Disp( PHB_CODEPAGE cdp,
                             const char * pSrc, HB_SIZE nSrc,
                             char * pDst, HB_SIZE nDst )
{
   HB_SIZE nPosS, nPosD, u;

   if( HB_CDP_ISUTF8( cdp ) )
   {
      if( nSrc > nDst )
         nSrc = nDst;
      else if( nSrc < nDst )
         pDst[ nSrc ] = '\0';
      memcpy( pDst, pSrc, nSrc );
      return nSrc;
   }
   else if( HB_CDP_ISCUSTOM( cdp ) )
   {
      HB_WCHAR wc;
      nPosS = nPosD = 0;
      while( nPosD < nDst && HB_CDPCHAR_GET( cdp, pSrc, nSrc, &nPosS, &wc ) )
      {
         if( wc < 32 )
            wc = s_uniCtrls[ wc ];
         u = hb_cdpUTF8CharSize( wc );
         if( nPosD + u <= nDst )
         {
            hb_cdpU16CharToUTF8( &pDst[ nPosD ], wc );
            nPosD += u;
         }
         else
            break;
      }
   }
   else
   {
      const HB_WCHAR * uniCodes = cdp->uniTable->uniCodes;
      for( nPosS = nPosD = 0; nPosS < nSrc && nPosD < nDst; ++nPosS )
      {
         HB_UCHAR uc = ( HB_UCHAR ) pSrc[ nPosS ];
         HB_WCHAR wc = uniCodes[ uc ];

         if( wc == 0 )
            wc = uc < 32 ? s_uniCtrls[ uc ] : s_uniCodes[ uc ];

         u = hb_cdpUTF8CharSize( wc );
         if( nPosD + u <= nDst )
         {
            hb_cdpU16CharToUTF8( &pDst[ nPosD ], wc );
            nPosD += u;
         }
         else
            break;
      }
   }

   if( nPosD < nDst )
      pDst[ nPosD ] = '\0';

   return nPosD;
}

HB_SIZE hb_cdpUTF8AsStrLen( PHB_CODEPAGE cdp, const char * pSrc, HB_SIZE nSrc,
                            HB_SIZE nMax )
{
   HB_WCHAR wc;
   HB_SIZE nPosS, nPosD;

   if( HB_CDP_ISUTF8( cdp ) )
      return ( nMax && nSrc > nMax ) ? nMax : nSrc;
   else if( HB_CDP_ISCUSTOM( cdp ) )
   {
      for( nPosS = nPosD = 0; nPosS < nSrc; )
      {
         int i;
         hb_cdpUTF8GetU16( pSrc, nSrc, &nPosS, &wc );
         i = HB_CDPCHAR_LEN( cdp, wc );
         if( nMax && nPosD + i > nMax )
            break;
         nPosD += i;
      }
   }
   else
   {
      for( nPosS = nPosD = 0; nPosS < nSrc; )
      {
         hb_cdpUTF8GetU16( pSrc, nSrc, &nPosS, &wc );
         ++nPosD;
         if( nMax && nPosD >= nMax )
            break;
      }
   }

   return nPosD;
}

HB_SIZE hb_cdpUTF8ToStr( PHB_CODEPAGE cdp,
                         const char * pSrc, HB_SIZE nSrc,
                         char * pDst, HB_SIZE nDst )
{
   HB_WCHAR wcMax, wc;
   HB_SIZE nPosS, nPosD;

   if( HB_CDP_ISUTF8( cdp ) )
   {
      if( nSrc > nDst )
         nSrc = nDst;
      else if( nSrc < nDst )
         pDst[ nSrc ] = '\0';
      memcpy( pDst, pSrc, nSrc );
      return nSrc;
   }
   else if( HB_CDP_ISCUSTOM( cdp ) )
   {
      for( nPosS = nPosD = 0; nPosS < nSrc && nPosD < nDst; )
      {
         hb_cdpUTF8GetU16( pSrc, nSrc, &nPosS, &wc );
         if( ! HB_CDPCHAR_PUT( cdp, pDst, nDst, &nPosD, wc ) )
            break;
      }
   }
   else
   {
      HB_UCHAR * uniTrans;

      if( cdp->uniTable->uniTrans == NULL )
         hb_cdpBuildTransTable( cdp->uniTable );
      uniTrans = cdp->uniTable->uniTrans;
      wcMax = cdp->uniTable->wcMax;

      for( nPosS = nPosD = 0; nPosS < nSrc && nPosD < nDst; )
      {
         hb_cdpUTF8GetU16( pSrc, nSrc, &nPosS, &wc );
         if( wc <= wcMax && uniTrans[ wc ] )
            pDst[ nPosD++ ] = uniTrans[ wc ];
         else
            pDst[ nPosD++ ] = wc >= 0x100 ? HB_CDP_ERROR_ASCCHAR : ( HB_UCHAR ) wc;
      }
   }

   if( nPosD < nDst )
      pDst[ nPosD ] = '\0';

   return nPosD;
}

/*
 * U16 (hb wide char) conversions
 */
HB_WCHAR hb_cdpGetU16( PHB_CODEPAGE cdp, HB_UCHAR ch )
{
   if( cdp )
   {
      HB_WCHAR wc;
      if( HB_CDP_ISCUSTOM( cdp ) )
      {
         HB_SIZE n = 0;
         if( ! HB_CDPCHAR_GET( cdp, ( const char * ) &ch, 1, &n, &wc ) )
            wc = 0;
      }
      else
         wc = cdp->uniTable->uniCodes[ ch ];
      return wc == 0 ? ch : wc;
   }
   else
      return ch;
}

HB_WCHAR hb_cdpGetWC( PHB_CODEPAGE cdp, HB_UCHAR ch, HB_WCHAR wcDef )
{
   if( cdp )
   {
      if( HB_CDP_ISCUSTOM( cdp ) )
      {
         HB_WCHAR wc;
         HB_SIZE n = 0;
         if( HB_CDPCHAR_GET( cdp, ( const char * ) &ch, 1, &n, &wc ) )
            wcDef = wc;
      }
      else if( cdp->uniTable->uniCodes[ ch ] )
         wcDef = cdp->uniTable->uniCodes[ ch ];
   }
   else if( ch >= 32 && ch <= 126 )
      wcDef = ch;
   return wcDef;
}

HB_WCHAR hb_cdpGetU16Disp( PHB_CODEPAGE cdp, HB_UCHAR ch )
{
   if( cdp )
   {
      HB_WCHAR wc;
      if( HB_CDP_ISCUSTOM( cdp ) )
      {
         HB_SIZE n = 0;
         if( ! HB_CDPCHAR_GET( cdp, ( const char * ) &ch, 1, &n, &wc ) )
            wc = 0;
      }
      else
         wc = cdp->uniTable->uniCodes[ ch ];
      if( wc == 0 )
         wc = ch < 32 ? s_uniCtrls[ ch ] : s_uniCodes[ ch ];
      return wc;
   }
   else
      return ch;
}

HB_UCHAR hb_cdpGetChar( PHB_CODEPAGE cdp, HB_WCHAR wc )
{
   if( cdp )
   {
      if( HB_CDP_ISCUSTOM( cdp ) )
      {
         if( HB_CDPCHAR_LEN( cdp, wc ) == 1 )
         {
            HB_SIZE n = 0;
            char c;

            if( ! HB_CDPCHAR_PUT( cdp, &c, 1, &n, wc ) )
               wc = HB_CDP_ERROR_ASCCHAR;
            else
               wc = ( HB_UCHAR ) c;
         }
         else
            wc = HB_CDP_ERROR_ASCCHAR;
      }
      else
      {
         if( cdp->uniTable->uniTrans == NULL )
            hb_cdpBuildTransTable( cdp->uniTable );

         if( wc <= cdp->uniTable->wcMax )
         {
            HB_UCHAR uc = cdp->uniTable->uniTrans[ wc ];
            if( uc )
               wc = uc;
         }
      }
   }
   return wc >= 0x100 ? HB_CDP_ERROR_ASCCHAR : ( HB_UCHAR ) wc;
}

HB_UCHAR hb_cdpGetUC( PHB_CODEPAGE cdp, HB_WCHAR wc, HB_UCHAR ucDef )
{
   if( cdp )
   {
      if( HB_CDP_ISCUSTOM( cdp ) )
      {
         if( HB_CDPCHAR_LEN( cdp, wc ) == 1 )
         {
            HB_SIZE n = 0;
            char c;
            if( HB_CDPCHAR_PUT( cdp, &c, 1, &n, wc ) )
               ucDef = ( HB_UCHAR ) c;
         }
      }
      else
      {
         if( cdp->uniTable->uniTrans == NULL )
            hb_cdpBuildTransTable( cdp->uniTable );

         if( wc <= cdp->uniTable->wcMax )
         {
            HB_UCHAR uc = cdp->uniTable->uniTrans[ wc ];
            if( uc )
               ucDef = uc;
         }
         if( ucDef == 0 && wc <= HB_MAX_CTRL_CODE )
         {
            HB_UCHAR uc = s_rev_ctrl[ wc ];
            if( uc )
               ucDef = uc;
         }
      }
   }
   else if( wc <= 0xFF )
      ucDef = ( HB_UCHAR ) wc;

   return ucDef;
}

HB_WCHAR hb_cdpGetU16Ctrl( HB_WCHAR wc )
{
   return wc < 32 ? s_uniCtrls[ wc ] : ( wc == 0x007F ? 0x2302 : wc );
}

HB_SIZE hb_cdpStrAsU16Len( PHB_CODEPAGE cdp,
                           const char * pSrc, HB_SIZE nSrc,
                           HB_SIZE nMax )
{
   if( HB_CDP_ISUTF8( cdp ) )
   {
      nSrc = hb_cdpUTF8StringLength( pSrc, nSrc );
   }
   else if( HB_CDP_ISCUSTOM( cdp ) )
   {
      HB_WCHAR wc;
      HB_SIZE nPosS, nPosD;
      nPosS = nPosD = 0;
      while( HB_CDPCHAR_GET( cdp, pSrc, nSrc, &nPosS, &wc ) )
      {
         ++nPosD;
         if( nMax && nPosD >= nMax )
            break;
      }
      return nPosD;
   }

   return ( nMax && nSrc > nMax ) ? nMax : nSrc;
}

#undef HB_CDP_ENDIAN_SWAP
#if defined( HB_BIG_ENDIAN )
#  define HB_CDP_ENDIAN_SWAP  HB_CDP_ENDIAN_LITTLE
#elif defined( HB_LITTLE_ENDIAN )
#  define HB_CDP_ENDIAN_SWAP  HB_CDP_ENDIAN_BIG
#endif

HB_SIZE hb_cdpStrToU16( PHB_CODEPAGE cdp, int iEndian,
                        const char * pSrc, HB_SIZE nSrc,
                        HB_WCHAR * pDst, HB_SIZE nDst )
{
   const HB_WCHAR * uniCodes;
   HB_SIZE nPosS, nPosD;

   if( HB_CDP_ISUTF8( cdp ) )
   {
      HB_WCHAR wc;

      for( nPosS = nPosD = 0; nPosS < nSrc && nPosD < nDst; )
      {
         hb_cdpUTF8GetU16( pSrc, nSrc, &nPosS, &wc );
#if defined( HB_CDP_ENDIAN_SWAP )
         if( iEndian == HB_CDP_ENDIAN_SWAP )
            wc = HB_SWAP_UINT16( wc );
         pDst[ nPosD++ ] = wc;
#else
         if( iEndian == HB_CDP_ENDIAN_LITTLE )
            HB_PUT_LE_UINT16( &pDst[ nPosD ], wc );
         else if( iEndian == HB_CDP_ENDIAN_BIG )
            HB_PUT_BE_UINT16( &pDst[ nPosD ], wc );
         else
            pDst[ nPosD ] = wc;
         ++nPosD;
#endif
      }
   }
   else if( HB_CDP_ISCUSTOM( cdp ) )
   {
      HB_WCHAR wc;
      nPosS = nPosD = 0;
      while( nPosD < nDst && HB_CDPCHAR_GET( cdp, pSrc, nSrc, &nPosS, &wc ) )
      {
#if defined( HB_CDP_ENDIAN_SWAP )
         if( iEndian == HB_CDP_ENDIAN_SWAP )
            wc = HB_SWAP_UINT16( wc );
         pDst[ nPosD++ ] = wc;
#else
         if( iEndian == HB_CDP_ENDIAN_LITTLE )
            HB_PUT_LE_UINT16( &pDst[ nPosD ], wc );
         else if( iEndian == HB_CDP_ENDIAN_BIG )
            HB_PUT_BE_UINT16( &pDst[ nPosD ], wc );
         else
            pDst[ nPosD ] = wc;
         ++nPosD;
#endif
      }
   }
   else
   {
      uniCodes = cdp->uniTable->uniCodes;
      for( nPosS = nPosD = 0; nPosS < nSrc && nPosD < nDst; ++nPosS )
      {
         HB_UCHAR uc = ( HB_UCHAR ) pSrc[ nPosS ];
         HB_WCHAR wc = uniCodes[ uc ];

         if( wc == 0 )
            wc = uc;
#if defined( HB_CDP_ENDIAN_SWAP )
         if( iEndian == HB_CDP_ENDIAN_SWAP )
            wc = HB_SWAP_UINT16( wc );
         pDst[ nPosD++ ] = wc;
#else
         if( iEndian == HB_CDP_ENDIAN_LITTLE )
            HB_PUT_LE_UINT16( &pDst[ nPosD ], wc );
         else if( iEndian == HB_CDP_ENDIAN_BIG )
            HB_PUT_BE_UINT16( &pDst[ nPosD ], wc );
         else
            pDst[ nPosD ] = wc;
         ++nPosD;
#endif
      }
   }

   if( nPosD < nDst )
      pDst[ nPosD ] = '\0';

   return nPosD;
}

HB_WCHAR * hb_cdpnStrDupU16( PHB_CODEPAGE cdp, int iEndian,
                             const char * pSrc, HB_SIZE nSrc,
                             HB_SIZE * pnDst )
{
   HB_SIZE nLen = hb_cdpStrAsU16Len( cdp, pSrc, nSrc, 0 );
   HB_WCHAR * pDst = ( HB_WCHAR * ) hb_xgrab( ( nLen + 1 ) * sizeof( HB_WCHAR ) );

   hb_cdpStrToU16( cdp, iEndian, pSrc, nSrc, pDst, nLen + 1 );
   if( pnDst )
      *pnDst = nLen;
   return pDst;
}

HB_WCHAR * hb_cdpStrDupU16( PHB_CODEPAGE cdp, int iEndian, const char * pSrc )
{
   return hb_cdpnStrDupU16( cdp, iEndian, pSrc, strlen( pSrc ), NULL );
}

HB_WCHAR * hb_cdpStrDupnU16( PHB_CODEPAGE cdp, int iEndian, const char * pSrc, HB_SIZE nLen )
{
   return hb_cdpnStrDupU16( cdp, iEndian, pSrc, hb_strnlen( pSrc, nLen ), NULL );
}

HB_SIZE hb_cdpU16AsStrLen( PHB_CODEPAGE cdp,
                           const HB_WCHAR * pSrc, HB_SIZE nSrc,
                           HB_SIZE nMax )
{
   HB_SIZE nPosS, nPosD;
   int i;

   if( HB_CDP_ISUTF8( cdp ) )
   {
      for( nPosS = nPosD = 0; nPosS < nSrc; ++nPosS )
      {
         i = hb_cdpUTF8CharSize( pSrc[ nPosS ] );
         if( nMax && nPosD + i > nMax )
            break;
         nPosD += i;
      }
   }
   else if( HB_CDP_ISCUSTOM( cdp ) )
   {
      for( nPosS = nPosD = 0; nPosS < nSrc; ++nPosS )
      {
         i = HB_CDPCHAR_LEN( cdp, pSrc[ nPosS ] );
         if( nMax && nPosD + i > nMax )
            break;
         nPosD += i;
      }
   }
   else
      nPosD = ( nMax && nSrc > nMax ) ? nMax : nSrc;

   return nPosD;
}

HB_SIZE hb_cdpU16ToStr( PHB_CODEPAGE cdp, int iEndian,
                        const HB_WCHAR * pSrc, HB_SIZE nSrc,
                        char * pDst, HB_SIZE nDst )
{
   HB_UCHAR * uniTrans;
   HB_WCHAR wcMax, wc;
   HB_SIZE nPosS, nPosD;

   if( HB_CDP_ISUTF8( cdp ) )
   {
      for( nPosS = nPosD = 0; nPosS < nSrc; ++nPosS )
      {
         int i;
#if defined( HB_CDP_ENDIAN_SWAP )
         wc = pSrc[ nPosS ];
         if( iEndian == HB_CDP_ENDIAN_SWAP )
            wc = HB_SWAP_UINT16( wc );
#else
         if( iEndian == HB_CDP_ENDIAN_LITTLE )
            wc = HB_GET_LE_UINT16( &pSrc[ nPosS ] );
         else if( iEndian == HB_CDP_ENDIAN_BIG )
            wc = HB_GET_BE_UINT16( &pSrc[ nPosS ] );
         else
            wc = pSrc[ nPosS ];
#endif
         i = hb_cdpUTF8CharSize( wc );
         if( nPosD + i <= nDst )
         {
            hb_cdpU16CharToUTF8( &pDst[ nPosD ], wc );
            nPosD += i;
         }
         else
            break;
      }
   }
   else if( HB_CDP_ISCUSTOM( cdp ) )
   {
      for( nPosS = nPosD = 0; nPosS < nSrc; ++nPosS )
      {
#if defined( HB_CDP_ENDIAN_SWAP )
         wc = pSrc[ nPosS ];
         if( iEndian == HB_CDP_ENDIAN_SWAP )
            wc = HB_SWAP_UINT16( wc );
#else
         if( iEndian == HB_CDP_ENDIAN_LITTLE )
            wc = HB_GET_LE_UINT16( &pSrc[ nPosS ] );
         else if( iEndian == HB_CDP_ENDIAN_BIG )
            wc = HB_GET_BE_UINT16( &pSrc[ nPosS ] );
         else
            wc = pSrc[ nPosS ];
#endif
         if( ! HB_CDPCHAR_PUT( cdp, pDst, nDst, &nPosD, wc ) )
            break;
      }
   }
   else
   {
      if( cdp->uniTable->uniTrans == NULL )
         hb_cdpBuildTransTable( cdp->uniTable );
      uniTrans = cdp->uniTable->uniTrans;
      wcMax = cdp->uniTable->wcMax;

      for( nPosS = nPosD = 0; nPosS < nSrc && nPosD < nDst; ++nPosS )
      {
#if defined( HB_CDP_ENDIAN_SWAP )
         wc = pSrc[ nPosS ];
         if( iEndian == HB_CDP_ENDIAN_SWAP )
            wc = HB_SWAP_UINT16( wc );
#else
         if( iEndian == HB_CDP_ENDIAN_LITTLE )
            wc = HB_GET_LE_UINT16( &pSrc[ nPosS ] );
         else if( iEndian == HB_CDP_ENDIAN_BIG )
            wc = HB_GET_BE_UINT16( &pSrc[ nPosS ] );
         else
            wc = pSrc[ nPosS ];
#endif
         if( wc <= wcMax && uniTrans[ wc ] )
            pDst[ nPosD++ ] = uniTrans[ wc ];
         else
            pDst[ nPosD++ ] = wc >= 0x100 ? HB_CDP_ERROR_ASCCHAR : ( HB_UCHAR ) wc;
      }
   }

   if( nPosD < nDst )
      pDst[ nPosD ] = '\0';

   return nPosD;
}


/*
 * CP translations
 */
HB_SIZE hb_cdpTransLen( const char * pSrc, HB_SIZE nSrc, HB_SIZE nMax,
                        PHB_CODEPAGE cdpIn, PHB_CODEPAGE cdpOut )
{
   if( cdpIn && cdpOut && cdpIn != cdpOut &&
       ( cdpIn->uniTable != cdpOut->uniTable ||
         HB_CDP_ISCUSTOM( cdpIn ) || HB_CDP_ISCUSTOM( cdpOut ) ) )
   {
      if( HB_CDP_ISUTF8( cdpIn ) )
         return hb_cdpUTF8AsStrLen( cdpOut, pSrc, nSrc, nMax );
      else if( HB_CDP_ISUTF8( cdpOut ) )
         return hb_cdpStrAsUTF8Len( cdpIn, pSrc, nSrc, nMax );
      else if( HB_CDP_ISCUSTOM( cdpIn ) || HB_CDP_ISCUSTOM( cdpOut ) )
      {
         HB_SIZE nPosS, nSize;
         HB_WCHAR wc;

         nPosS = nSize = 0;
         while( HB_CDPCHAR_GET( cdpIn, pSrc, nSrc, &nPosS, &wc ) )
         {
            int i = HB_CDPCHAR_LEN( cdpOut, wc );
            if( nMax && nSize + i > nMax )
               break;
            nSize += i;
         }
         return nSize;
      }
   }

   return ( nMax && nSrc > nMax ) ? nMax : nSrc;
}

HB_SIZE hb_cdpTransTo( const char * pSrc, HB_SIZE nSrc,
                       char * pDst, HB_SIZE nDst,
                       PHB_CODEPAGE cdpIn, PHB_CODEPAGE cdpOut )
{
   HB_SIZE nSize;

   if( cdpIn && cdpOut && cdpIn != cdpOut &&
       ( cdpIn->uniTable != cdpOut->uniTable ||
         HB_CDP_ISCUSTOM( cdpIn ) || HB_CDP_ISCUSTOM( cdpOut ) ) )
   {
      if( HB_CDP_ISUTF8( cdpIn ) )
         return hb_cdpUTF8ToStr( cdpOut, pSrc, nSrc, pDst, nDst );
      else if( HB_CDP_ISUTF8( cdpOut ) )
         return hb_cdpStrToUTF8( cdpIn, pSrc, nSrc, pDst, nDst );
      else if( HB_CDP_ISCUSTOM( cdpIn ) || HB_CDP_ISCUSTOM( cdpOut ) )
      {
         HB_SIZE nPosS;
         HB_WCHAR wc;

         nPosS = nSize = 0;
         while( nSize < nDst && HB_CDPCHAR_GET( cdpIn, pSrc, nSrc, &nPosS, &wc ) )
         {
            if( ! HB_CDPCHAR_PUT( cdpOut, pDst, nDst, &nSize, wc ) )
               break;
         }
      }
      else
      {
         HB_UCHAR * uniTrans;
         HB_WCHAR wcMax;

         if( cdpOut->uniTable->uniTrans == NULL )
            hb_cdpBuildTransTable( cdpOut->uniTable );
         uniTrans = cdpOut->uniTable->uniTrans;
         wcMax = cdpOut->uniTable->wcMax;

         if( nSrc > nDst )
            nSrc = nDst;
         for( nSize = 0; nSize < nSrc; ++nSize )
         {
            HB_UCHAR uc = ( HB_UCHAR ) pSrc[ nSize ];
            HB_WCHAR wc = cdpIn->uniTable->uniCodes[ uc ];
            if( wc && wc <= wcMax && uniTrans[ wc ] )
               uc = uniTrans[ wc ];
            pDst[ nSize ] = uc;
         }
      }
   }
   else
   {
      nSize = ( nSrc > nDst ) ? nDst : nSrc;
      memcpy( pDst, pSrc, nSize );
   }

   if( nSize < nDst )
      pDst[ nSize ] = '\0';

   return nSize;
}

int hb_cdpTranslateChar( int iChar, PHB_CODEPAGE cdpIn, PHB_CODEPAGE cdpOut )
{
   if( cdpIn && cdpOut && cdpIn != cdpOut &&
       ( cdpIn->uniTable != cdpOut->uniTable ||
         HB_CDP_ISCUSTOM( cdpIn ) || HB_CDP_ISCUSTOM( cdpOut ) ) &&
       iChar >= 0 && iChar < 256 )
   {
      HB_WCHAR wc;

      if( HB_CDP_ISCUSTOM( cdpIn ) || HB_CDP_ISCUSTOM( cdpOut ) )
      {
         HB_SIZE n = 0;
         char c = ( char ) iChar;

         if( HB_CDPCHAR_GET( cdpIn, &c, 1, &n, &wc ) )
         {
            if( HB_CDPCHAR_PUT( cdpOut, &c, 1, &n, wc ) )
            {
               if( c != HB_CDP_ERROR_ASCCHAR )
                  iChar = ( HB_UCHAR ) c;
            }
         }
      }
      else
      {
         wc = cdpIn->uniTable->uniCodes[ iChar ];

         if( wc )
         {
            if( cdpOut->uniTable->uniTrans == NULL )
               hb_cdpBuildTransTable( cdpOut->uniTable );

            if( wc <= cdpOut->uniTable->wcMax )
            {
               wc = cdpOut->uniTable->uniTrans[ wc ];
               if( wc )
                  iChar = wc;
            }
         }
      }
   }

   return iChar;
}

int hb_cdpTranslateDispChar( int iChar, PHB_CODEPAGE cdpIn, PHB_CODEPAGE cdpOut )
{
   if( cdpIn && cdpOut && cdpIn != cdpOut &&
       ( cdpIn->uniTable != cdpOut->uniTable ||
         HB_CDP_ISCUSTOM( cdpIn ) || HB_CDP_ISCUSTOM( cdpOut ) ) &&
       iChar >= 0 && iChar < 256 )
   {
      HB_WCHAR wc;


      if( HB_CDP_ISCUSTOM( cdpIn ) || HB_CDP_ISCUSTOM( cdpOut ) )
      {
         HB_SIZE n = 0;
         char c = ( char ) iChar;

         if( ! HB_CDPCHAR_GET( cdpIn, &c, 1, &n, &wc ) )
            wc = ( HB_WCHAR ) iChar;
         if( wc < 32 )
            wc = s_uniCtrls[ iChar ];
         if( HB_CDPCHAR_PUT( cdpOut, &c, 1, &n, wc ) )
         {
            if( c != HB_CDP_ERROR_ASCCHAR )
               iChar = ( HB_UCHAR ) c;
         }
      }
      else
      {
         wc = cdpIn->uniTable->uniCodes[ iChar ];

         if( wc == 0 )
            wc = iChar < 32 ? s_uniCtrls[ iChar ] : s_uniCodes[ iChar ];

         if( wc )
         {
            if( cdpOut->uniTable->uniTrans == NULL )
               hb_cdpBuildTransTable( cdpOut->uniTable );

            if( wc <= cdpOut->uniTable->wcMax )
            {
               wc = cdpOut->uniTable->uniTrans[ wc ];
               if( wc )
                  iChar = wc;
            }
         }
      }
   }

   return iChar;
}

HB_SIZE hb_cdpnDupLen( const char * pSrc, HB_SIZE nSrc,
                       PHB_CODEPAGE cdpIn, PHB_CODEPAGE cdpOut )
{
   return hb_cdpTransLen( pSrc, nSrc, 0, cdpIn, cdpOut );
}

HB_SIZE hb_cdpnDup2Len( const char * pSrc, HB_SIZE nSrc, HB_SIZE nMax,
                        PHB_CODEPAGE cdpIn, PHB_CODEPAGE cdpOut )
{
   return hb_cdpTransLen( pSrc, nSrc, nMax, cdpIn, cdpOut );
}

char * hb_cdpnDup( const char * pSrc, HB_SIZE * pnLen,
                   PHB_CODEPAGE cdpIn, PHB_CODEPAGE cdpOut )
{
   char * pDst;
   HB_SIZE nDst;

   nDst = hb_cdpTransLen( pSrc, *pnLen, 0, cdpIn, cdpOut );
   pDst = ( char * ) hb_xgrab( nDst + 1 );
   hb_cdpTransTo( pSrc, *pnLen, pDst, nDst + 1, cdpIn, cdpOut );
   *pnLen = nDst;

   return pDst;
}

const char * hb_cdpnDup2( const char * pSrc, HB_SIZE nSrc,
                          char * pDst, HB_SIZE * pnDst,
                          PHB_CODEPAGE cdpIn, PHB_CODEPAGE cdpOut )
{
   *pnDst = hb_cdpTransTo( pSrc, nSrc, pDst, *pnDst, cdpIn, cdpOut );
   return pDst;
}

const char * hb_cdpnDup3( const char * pSrc, HB_SIZE nSrc,
                          char * pDst, HB_SIZE * pnDst,
                          char ** pFree, HB_SIZE * pnSize,
                          PHB_CODEPAGE cdpIn, PHB_CODEPAGE cdpOut )
{
   if( cdpIn && cdpOut && cdpIn != cdpOut && nSrc &&
       ( ! HB_CDP_ISUTF8( cdpIn ) || ! HB_CDP_ISUTF8( cdpOut ) ) &&
       ( cdpIn->uniTable != cdpOut->uniTable ||
         HB_CDP_ISCUSTOM( cdpIn ) || HB_CDP_ISCUSTOM( cdpOut ) ) )
   {
      char * pPrev = NULL;
      HB_SIZE nDst = hb_cdpTransLen( pSrc, nSrc, 0, cdpIn, cdpOut );

      if( pDst == NULL )
      {
         pDst = *pFree;
         if( pDst == NULL && *pnSize > 0 )
            pDst = ( char * ) HB_UNCONST( pSrc );
      }

      if( nDst >= *pnSize || ( pDst == pSrc && HB_CDP_ISCUSTOM( cdpOut ) ) )
      {
         pPrev = *pFree;
         pDst = *pFree = ( char * ) hb_xgrab( nDst + 1 );
         *pnSize = nDst + 1;
      }

      nDst = hb_cdpTransTo( pSrc, nSrc, pDst, *pnSize, cdpIn, cdpOut );

      if( pPrev )
         hb_xfree( pPrev );
      if( pnDst )
         *pnDst = nDst;
      return pDst;
   }

   if( pnDst )
      *pnDst = nSrc;
   return pSrc;
}

char * hb_cdpDup( const char * pszSrc, PHB_CODEPAGE cdpIn, PHB_CODEPAGE cdpOut )
{
   HB_SIZE nLen = strlen( pszSrc );

   return hb_cdpnDup( pszSrc, &nLen, cdpIn, cdpOut );
}

char * hb_cdpDupn( const char * pszSrc, HB_SIZE nLen, PHB_CODEPAGE cdpIn, PHB_CODEPAGE cdpOut )
{
   nLen = hb_strnlen( pszSrc, nLen );
   return hb_cdpnDup( pszSrc, &nLen, cdpIn, cdpOut );
}

char * hb_cdpnDupUpper( PHB_CODEPAGE cdp, const char * pszText, HB_SIZE * pnSize )
{
   HB_SIZE nSize = pnSize ? *pnSize : strlen( pszText ), n;
   char * pszDst = ( char * ) hb_xgrab( nSize + 1 );

   if( cdp )
   {
      if( HB_CDP_ISCUSTOM( cdp ) && cdp->wcharUpper )
      {
         HB_SIZE nS = 0, nD = 0, nSrc = nSize;
         HB_WCHAR wc;

         while( HB_CDPCHAR_GET( cdp, pszText, nSrc, &nS, &wc ) )
         {
            wc = HB_CDPCHAR_UPPER( cdp, wc );
            if( ! HB_CDPCHAR_PUT( cdp, pszDst, nSize, &nD, wc ) )
            {
               nSize += ( nSrc - nS + 2 );
               pszDst = ( char * ) hb_xrealloc( pszDst, nSize + 1 );
               if( ! HB_CDPCHAR_PUT( cdp, pszDst, nSize, &nD, wc ) )
                  break;
            }
         }
         nSize = nD;
         if( pnSize )
            *pnSize = nSize;
      }
      else
         for( n = 0; n < nSize; n++ )
            pszDst[ n ] = ( char ) cdp->upper[ ( HB_UCHAR ) pszText[ n ] ];
   }
   else
      for( n = 0; n < nSize; n++ )
         pszDst[ n ] = HB_TOUPPER( pszText[ n ] );
   pszDst[ nSize ] = '\0';

   return pszDst;
}

char * hb_cdpnDupLower( PHB_CODEPAGE cdp, const char * pszText, HB_SIZE * pnSize )
{
   HB_SIZE nSize = pnSize ? *pnSize : strlen( pszText ), n;
   char * pszDst = ( char * ) hb_xgrab( nSize + 1 );

   if( cdp )
   {
      if( HB_CDP_ISCUSTOM( cdp ) && cdp->wcharLower )
      {
         HB_SIZE nS = 0, nD = 0, nSrc = nSize;
         HB_WCHAR wc;

         while( HB_CDPCHAR_GET( cdp, pszText, nSrc, &nS, &wc ) )
         {
            wc = HB_CDPCHAR_LOWER( cdp, wc );
            if( ! HB_CDPCHAR_PUT( cdp, pszDst, nSize, &nD, wc ) )
            {
               nSize += ( nSrc - nS + 2 );
               pszDst = ( char * ) hb_xrealloc( pszDst, nSize + 1 );
               if( ! HB_CDPCHAR_PUT( cdp, pszDst, nSize, &nD, wc ) )
                  break;
            }
         }
         nSize = nD;
         if( pnSize )
            *pnSize = nSize;
      }
      else
         for( n = 0; n < nSize; n++ )
            pszDst[ n ] = ( char ) cdp->lower[ ( HB_UCHAR ) pszText[ n ] ];
   }
   else
      for( n = 0; n < nSize; n++ )
         pszDst[ n ] = HB_TOLOWER( pszText[ n ] );
   pszDst[ nSize ] = '\0';

   return pszDst;
}

HB_SIZE hb_cdpnDup2Upper( PHB_CODEPAGE cdp, const char * pszText, HB_SIZE nSize, char * pBuffer, HB_SIZE nBuffLen )
{
   HB_SIZE nMax = HB_MIN( nSize, nBuffLen ), n;

   if( ! cdp )
      for( n = 0; n < nMax; n++ )
         pBuffer[ n ] = HB_TOUPPER( pszText[ n ] );
   else if( ! HB_CDP_ISCUSTOM( cdp ) || ! cdp->wcharUpper )
      for( n = 0; n < nMax; n++ )
         pBuffer[ n ] = ( char ) cdp->upper[ ( HB_UCHAR ) pszText[ n ] ];
   else
   {
      HB_SIZE nS = 0;
      HB_WCHAR wc;

      nMax = 0;
      while( HB_CDPCHAR_GET( cdp, pszText, nSize, &nS, &wc ) )
      {
         wc = HB_CDPCHAR_UPPER( cdp, wc );
         if( ! HB_CDPCHAR_PUT( cdp, pBuffer, nBuffLen, &nMax, wc ) )
            break;
      }
   }

   if( nMax < nBuffLen )
      pBuffer[ nMax ] = '\0';

   return nMax;
}

HB_SIZE hb_cdpnDup2Lower( PHB_CODEPAGE cdp, const char * pszText, HB_SIZE nSize, char * pBuffer, HB_SIZE nBuffLen )
{
   HB_SIZE nMax = HB_MIN( nSize, nBuffLen ), n;

   if( ! cdp )
      for( n = 0; n < nMax; n++ )
         pBuffer[ n ] = HB_TOLOWER( pszText[ n ] );
   else if( ! HB_CDP_ISCUSTOM( cdp ) || ! cdp->wcharLower )
      for( n = 0; n < nMax; n++ )
         pBuffer[ n ] = ( char ) cdp->lower[ ( HB_UCHAR ) pszText[ n ] ];
   else
   {
      HB_SIZE nS = 0;
      HB_WCHAR wc;

      nMax = 0;
      while( HB_CDPCHAR_GET( cdp, pszText, nSize, &nS, &wc ) )
      {
         wc = HB_CDPCHAR_LOWER( cdp, wc );
         if( ! HB_CDPCHAR_PUT( cdp, pBuffer, nBuffLen, &nMax, wc ) )
            break;
      }
   }

   if( nMax < nBuffLen )
      pBuffer[ nMax ] = '\0';

   return nMax;
}

HB_WCHAR hb_cdpUpperWC( PHB_CODEPAGE cdp, HB_WCHAR wc )
{
   if( ! cdp )
      return HB_TOUPPER( wc );
   else if( ! HB_CDP_ISCUSTOM( cdp ) || ! cdp->wcharUpper )
   {
      if( cdp->uniTable->uniTrans == NULL )
         hb_cdpBuildTransTable( cdp->uniTable );

      if( wc <= cdp->uniTable->wcMax &&
          cdp->uniTable->uniTrans[ wc ] )
      {
         wc = cdp->uniTable->uniCodes[ cdp->upper[ cdp->uniTable->uniTrans[ wc ] ] ];
      }
      return wc;
   }
   else
      return HB_CDPCHAR_UPPER( cdp, wc );
}

/* functions operating on character indexes */
HB_SIZE hb_cdpTextLen( PHB_CODEPAGE cdp, const char * pText, HB_SIZE nSize )
{
   if( cdp && HB_CDP_ISCUSTOM( cdp ) )
   {
      HB_SIZE nPos = 0, nIndex = 0;
      HB_WCHAR wc;

      while( HB_CDPCHAR_GET( cdp, pText, nSize, &nPos, &wc ) )
         ++nIndex;

      return nIndex;
   }
   return nSize;
}

HB_SIZE hb_cdpTextPos( PHB_CODEPAGE cdp, const char * pText, HB_SIZE nSize, HB_SIZE nIndex )
{
   if( nIndex > 0 )
   {
      if( cdp && HB_CDP_ISCUSTOM( cdp ) )
      {
         HB_SIZE nPos = 0;
         HB_WCHAR wc;

         do
         {
            if( ! HB_CDPCHAR_GET( cdp, pText, nSize, &nPos, &wc ) )
               break;
         }
         while( --nIndex );

         return nPos;
      }
      else
         return nIndex >= nSize ? nSize : nIndex;
   }
   return 0;
}

HB_SIZE hb_cdpTextPosEx( PHB_CODEPAGE cdp, const char * pText, HB_SIZE nSize, HB_SIZE * pnIndex )
{
   HB_SIZE nIndex = *pnIndex;

   if( nIndex > 0 )
   {
      if( cdp && HB_CDP_ISCUSTOM( cdp ) )
      {
         HB_SIZE nPos = 0;
         HB_WCHAR wc;

         do
         {
            if( ! HB_CDPCHAR_GET( cdp, pText, nSize, &nPos, &wc ) )
               break;
         }
         while( --nIndex );
         *pnIndex = nIndex;
         return nPos;
      }
      else if( nIndex > nSize )
      {
         *pnIndex -= nSize;
         return nSize;
      }
      else
      {
         *pnIndex = 0;
         return nIndex;
      }
   }
   *pnIndex = 0;
   return 0;
}

HB_WCHAR hb_cdpTextGetU16( PHB_CODEPAGE cdp, const char * szText, HB_SIZE nLen )
{
   HB_WCHAR wc = 0;

   if( szText && nLen > 0 )
   {
      if( cdp )
      {
         if( HB_CDP_ISCUSTOM( cdp ) )
         {
            HB_SIZE n = 0;
            if( ! HB_CDPCHAR_GET( cdp, szText, nLen, &n, &wc ) )
               wc = 0;
         }
         else
            wc = cdp->uniTable->uniCodes[ ( HB_UCHAR ) *szText ];
      }
      else
         wc = ( HB_UCHAR ) *szText;
   }

   return wc;
}

HB_SIZE hb_cdpTextPutU16( PHB_CODEPAGE cdp, char * szText, HB_SIZE nSize, HB_WCHAR wc )
{
   HB_SIZE nLen = 0;

   if( szText && nSize > 0 )
   {
      if( cdp )
      {
         if( HB_CDP_ISCUSTOM( cdp ) )
            HB_CDPCHAR_PUT( cdp, szText, nSize, &nLen, wc );
         else
         {
            if( cdp->uniTable->uniTrans == NULL )
               hb_cdpBuildTransTable( cdp->uniTable );

            if( wc <= cdp->uniTable->wcMax )
            {
               HB_UCHAR uc = cdp->uniTable->uniTrans[ wc ];
               if( uc )
                  szText[ nLen++ ] = uc;
            }
         }
      }
      else
         szText[ nLen++ ] = ( char ) wc;

      if( nLen < nSize )
         szText[ nLen ] = '\0';
   }

   return nLen;
}

HB_BOOL hb_cdpCharEq( PHB_CODEPAGE cdp, const char * szText1, HB_SIZE nLen1, HB_SIZE * pnPos1,
                      const char * szText2, HB_SIZE nLen2, HB_SIZE * pnPos2 )
{
   if( *pnPos1 < nLen1 && *pnPos2 < nLen2 )
   {
      if( cdp && HB_CDP_ISCUSTOM( cdp ) )
      {
         HB_WCHAR wc1, wc2;
         return HB_CDPCHAR_GET( cdp, szText1, nLen1, pnPos1, &wc1 ) &&
                HB_CDPCHAR_GET( cdp, szText2, nLen2, pnPos2, &wc2 ) &&
                wc1 == wc2;
      }
      else
         return szText1[ ( *pnPos1 )++ ] == szText2[ ( *pnPos2 )++ ];
   }
   return HB_FALSE;
}

HB_BOOL hb_cdpCharCaseEq( PHB_CODEPAGE cdp, const char * szText1, HB_SIZE nLen1, HB_SIZE * pnPos1,
                          const char * szText2, HB_SIZE nLen2, HB_SIZE * pnPos2 )
{
   if( *pnPos1 < nLen1 && *pnPos2 < nLen2 )
   {
      if( ! cdp )
      {
         HB_UCHAR uc1 = szText1[ ( *pnPos1 )++ ],
                  uc2 = szText2[ ( *pnPos2 )++ ];
         return HB_TOUPPER( uc1 ) == HB_TOUPPER( uc2 );
      }
      else if( ! HB_CDP_ISCUSTOM( cdp ) || ! cdp->wcharUpper )
      {
         HB_UCHAR uc1 = szText1[ ( *pnPos1 )++ ],
                  uc2 = szText2[ ( *pnPos2 )++ ];
         return cdp->upper[ uc1 ] == cdp->upper[ uc2 ];
      }
      else
      {
         HB_WCHAR wc1, wc2;
         if( HB_CDPCHAR_GET( cdp, szText1, nLen1, pnPos1, &wc1 ) &&
             HB_CDPCHAR_GET( cdp, szText2, nLen2, pnPos2, &wc2 ) )
         {
            return wc1 == wc2 ||
                   HB_CDPCHAR_UPPER( cdp, wc1 ) == HB_CDPCHAR_UPPER( cdp, wc2 );
         }
      }
   }
   return HB_FALSE;
}

/*
 * CP management
 */
static HB_UCHAR hb_cdpUtf8Char( const char ** pStrPtr, PHB_UNITABLE uniTable )
{
   const char * pszString = *pStrPtr;
   HB_UCHAR uc = 0;

   if( *pszString )
   {
      HB_SIZE nIndex = 0;
      HB_WCHAR wc;

      if( hb_cdpUTF8GetU16( pszString, hb_strnlen( pszString, 6 ), &nIndex, &wc ) )
      {
         if( wc < 127 )
            uc = ( HB_UCHAR ) wc;
         else
         {
            int n;
            for( n = 0; n < 256; ++n )
            {
               if( wc == uniTable->uniCodes[ n ] )
               {
                  uc = ( HB_UCHAR ) n;
                  break;
               }
            }
         }
      }
      pszString += nIndex;
   }
   if( uc == 0 )
   {
      while( *pszString )
         pszString++;
   }
   *pStrPtr = ( const char * ) pszString;

   return uc;
}

#define _HB_CDP_GETUC( p )  ( ! fUtf8 ? ( HB_UCHAR ) ( *( p ) ? *( p )++ : 0 ) \
                                      : hb_cdpUtf8Char( &( p ), uniTable ) )

static PHB_CODEPAGE hb_buildCodePage( const char * id, const char * info,
                                      PHB_UNITABLE uniTable,
                                      const char * pszUpper,
                                      const char * pszLower,
                                      unsigned int nACSort,
                                      unsigned int nCaseSort,
                                      HB_BOOL fUtf8 )
{
   HB_BOOL lSort, fError;
   int iMulti, iAcc, iAccUp, iAccLo, iSortUp, iSortLo, i;
   const char * pup, * plo;
   HB_UCHAR ucUp, ucLo, ucUp2, ucLo2;
   HB_SIZE nSize, ul;
   HB_UCHAR * buffer, * flags, * upper, * lower, * sort, * acc;
   HB_UCHAR used[ 256 ];
   PHB_CODEPAGE cdp;
   PHB_MULTICHAR multi;

   memset( used, '\0', sizeof( used ) );

   iMulti = iAcc = iSortUp = iSortLo = 0;
   fError = lSort = HB_FALSE;

   ucUp2 = ucLo2 = 0;
   pup = pszUpper;
   plo = pszLower;
   while( *pup || *plo )
   {
      ucUp = _HB_CDP_GETUC( pup );
      ucLo = _HB_CDP_GETUC( plo );
      if( ucUp == 0 || ucLo == 0 ||
          ( ucUp == '.' && ucLo != '.' ) ||
          ( ucUp == '~' && ucLo != '~' ) )
      {
         fError = HB_TRUE;
         break;
      }

      if( ucUp == '.' )
      {
         HB_UCHAR ucU1, ucU2, ucL1, ucL2;

         ucU1 = _HB_CDP_GETUC( pup );
         ucU2 = _HB_CDP_GETUC( pup );
         ucL1 = _HB_CDP_GETUC( plo );
         ucL2 = _HB_CDP_GETUC( plo );

         if( ucU1 && ucU2 && ( *pup == '.' || *pup == '=' ) &&
             ucL1 && ucL2 && ( *plo == '.' || *plo == '=' ) )
         {
            if( ( ucU1 != ' ' || ucL1 != ' ' ) &&
                ( ucU1 == ucU2 || ( ucU1 != ' ' && ucU2 != ' ' ) ) &&
                ( ucL1 == ucL2 || ( ucL1 != ' ' && ucL2 != ' ' ) ) )
            {
               if( ucU1 != ' ' )
                  ++iSortLo;

               if( *pup == '=' )
               {
                  do
                  {
                     ++pup;
                  }
                  while( HB_ISXDIGIT( *pup ) );
               }
               if( *plo == '=' )
               {
                  do
                  {
                     ++plo;
                  }
                  while( HB_ISXDIGIT( *plo ) );
               }
               if( *pup == '.' && *plo == '.' )
               {
                  lSort = HB_TRUE;
                  iMulti++;
                  pup++;
                  plo++;
                  continue;
               }
            }
         }
         fError = HB_TRUE;
         break;
      }
      if( ucUp == '~' )
      {
         ucUp = _HB_CDP_GETUC( pup );
         ucLo = _HB_CDP_GETUC( plo );
         if( ucUp == '\0' || ucLo == '\0' )
         {
            fError = HB_TRUE;
            break;
         }
         ++iAcc;
      }
      if( used[ ucUp ] != 0 )
         ucUp = ' ';
      if( used[ ucLo ] != 0 )
         ucLo = ' ';
      if( ucUp == ' ' && ucLo == ' ' )
      {
         fError = HB_TRUE;
         break;
      }
      if( ucUp != ' ' )
      {
         used[ ucUp ] = 1;
         if( ucUp < ucUp2 )
            lSort = HB_TRUE;
         ucUp2 = ucUp;
         ++iSortLo;
      }
      if( ucLo != ' ' )
      {
         used[ ucLo ] = 1;
         if( ucLo < ucLo2 )
            lSort = HB_TRUE;
         ucLo2 = ucLo;
      }
   }

   if( iMulti > 64 )
      fError = HB_TRUE;

   if( fError || nACSort > HB_CDP_ACSORT_INTERLEAVED ||
       nCaseSort > HB_CDP_CSSORT_IGNORE )
   {
#ifdef __HB_IGNORE_CP_ERRORS
      fprintf( stderr, "Harbour CP (%s) initialization failure (1)\n", id ); fflush( stderr );
      return NULL;
#else
      hb_errInternal( 9994, "Harbour CP (%s) initialization failure", id, NULL );
#endif
   }

   if( iAcc == 0 )
      nACSort = HB_CDP_ACSORT_NONE;
   else if( nACSort != HB_CDP_ACSORT_NONE )
      lSort = HB_TRUE;

   if( nCaseSort != HB_CDP_CSSORT_UPLO )
      lSort = HB_TRUE;

   nSize = 0x300;
   if( lSort )
   {
      nSize += 0x100;
      if( nACSort == HB_CDP_ACSORT_INTERLEAVED )
         nSize += 0x100;
   }
   ul = nSize;
   nSize += sizeof( HB_CODEPAGE );
   if( iMulti )
      nSize += iMulti * sizeof( HB_MULTICHAR );

   buffer = ( HB_UCHAR * ) hb_xgrabz( nSize );
   cdp = ( PHB_CODEPAGE ) &buffer[ ul ];
   cdp->buffer = buffer;

   cdp->flags = flags = buffer;
   buffer += 0x100;
   cdp->upper = upper = buffer;
   buffer += 0x100;
   cdp->lower = lower = buffer;
   buffer += 0x100;
   sort = acc = NULL;
   if( lSort )
   {
      cdp->sort = sort = buffer;
      buffer += 0x100;
      if( nACSort == HB_CDP_ACSORT_INTERLEAVED )
      {
         cdp->acc = acc = buffer;
         buffer += 0x100;
      }
   }
   if( iMulti )
      cdp->multi = ( PHB_MULTICHAR ) &buffer[ sizeof( HB_CODEPAGE ) ];

   cdp->id = id;
   cdp->info = info;
   cdp->uniTable = uniTable;
   cdp->nACSort = nACSort;
   cdp->nMulti = iMulti;
   for( i = 0; i < 0x100; ++i )
   {
      if( HB_ISDIGIT( i ) )
         flags[ i ] |= HB_CDP_DIGIT;
      if( HB_ISALPHA( i ) )
         flags[ i ] |= HB_CDP_ALPHA;
      if( HB_ISUPPER( i ) )
         flags[ i ] |= HB_CDP_UPPER;
      if( HB_ISLOWER( i ) )
         flags[ i ] |= HB_CDP_LOWER;
      upper[ i ] = ( HB_UCHAR ) HB_TOUPPER( i );
      lower[ i ] = ( HB_UCHAR ) HB_TOLOWER( i );
   }

   iAccUp = iAccLo = 0;
   multi = cdp->multi;
   pup = pszUpper;
   plo = pszLower;
   ucUp2 = ucLo2 = 255;
   memset( used, '\0', sizeof( used ) );
   while( *pup )
   {
      ucUp = _HB_CDP_GETUC( pup );
      ucLo = _HB_CDP_GETUC( plo );
      if( ucUp == '.' )
      {
         multi->cFirst[ 0 ] = _HB_CDP_GETUC( pup );
         multi->cLast[ 0 ]  = _HB_CDP_GETUC( pup );
         multi->cFirst[ 1 ] = _HB_CDP_GETUC( plo );
         multi->cLast[ 1 ]  = _HB_CDP_GETUC( plo );
         if( multi->cFirst[ 0 ] != ' ' )
         {
            flags[ ( HB_UCHAR ) multi->cFirst[ 0 ] ] |= HB_CDP_MULTI1;
            flags[ ( HB_UCHAR ) multi->cLast[ 0 ] ]  |= HB_CDP_MULTI2;
            multi->sortUp = ++iSortUp - iAccUp;
         }
         if( multi->cFirst[ 1 ] != ' ' )
         {
            flags[ ( HB_UCHAR ) multi->cFirst[ 1 ] ] |= HB_CDP_MULTI1;
            flags[ ( HB_UCHAR ) multi->cLast[ 1 ] ]  |= HB_CDP_MULTI2;

            if( nCaseSort == HB_CDP_CSSORT_UPLO )
               ++iSortLo;
            else if( nCaseSort == HB_CDP_CSSORT_MIXED )
               iSortLo = ++iSortUp;
            else
               iSortLo = iSortUp;
            multi->sortLo = iSortLo - iAccLo;
         }
         if( *pup == '=' )
         {
            ++pup;
            while( HB_ISXDIGIT( *pup ) )
            {
               multi->wcUp = ( multi->wcUp << 4 ) |
                             ( *pup >= 'a' ? ( *pup - 'a' + 10 ) :
                               ( *pup >= 'A' ? ( *pup - 'A' + 10 ) :
                                               ( *pup - '0' ) ) );
               ++pup;
            }
         }
         pup++;
         if( *plo == '=' )
         {
            ++plo;
            while( HB_ISXDIGIT( *plo ) )
            {
               multi->wcLo = ( multi->wcLo << 4 ) |
                             ( *plo >= 'a' ? ( *plo - 'a' + 10 ) :
                               ( *plo >= 'A' ? ( *plo - 'A' + 10 ) :
                                               ( *plo - '0' ) ) );
               ++plo;
            }
         }
         plo++;
         if( multi->wcUp || multi->wcLo )
            cdp->nMultiUC++;
         multi++;
      }
      else
      {
         iAcc = 0;
         if( ucUp == '~' )
         {
            iAcc = 1;
            ucUp = _HB_CDP_GETUC( pup );
            ucLo = _HB_CDP_GETUC( plo );
         }
         if( ucUp != ' ' )
         {
            flags[ ucUp ] |= HB_CDP_ALPHA;
            flags[ ucUp ] |= HB_CDP_UPPER;
            if( ucLo != ' ' && ( used[ ucUp ] & HB_CDP_UPPER ) == 0 )
            {
               lower[ ucUp ] = ucLo;
               used[ ucUp ] |= HB_CDP_UPPER;
            }
            if( sort )
            {
               if( sort[ ucUp ] == 0 )
               {
                  if( iAcc && nACSort != HB_CDP_ACSORT_NONE )
                     ++iAccUp;
                  sort[ ucUp ] = ( HB_UCHAR ) ( ++iSortUp - iAccUp );
                  if( acc )
                     acc[ ucUp ] = ( HB_UCHAR ) iSortUp;
                  if( ucUp2 > ucUp )
                     ucUp2 = ucUp;
               }
            }
         }
         if( ucLo != ' ' )
         {
            flags[ ucLo ] |= HB_CDP_ALPHA;
            flags[ ucLo ] |= HB_CDP_LOWER;
            if( ucUp != ' ' && ( used[ ucLo ] & HB_CDP_LOWER ) == 0 )
            {
               upper[ ucLo ] = ucUp;
               used[ ucLo ] |= HB_CDP_LOWER;
            }
            if( sort )
            {
               if( sort[ ucLo ] == 0 )
               {
                  if( nCaseSort == HB_CDP_CSSORT_UPLO )
                  {
                     if( iAcc && nACSort != HB_CDP_ACSORT_NONE )
                        ++iAccLo;
                     ++iSortLo;
                  }
                  else
                  {
                     if( nCaseSort == HB_CDP_CSSORT_MIXED )
                     {
                        if( iAcc && nACSort != HB_CDP_ACSORT_NONE )
                           ++iAccUp;
                        ++iSortUp;
                     }
                     iAccLo = iAccUp;
                     iSortLo = iSortUp;
                  }
                  sort[ ucLo ] = ( HB_UCHAR ) ( iSortLo - iAccLo );
                  if( acc )
                     acc[ ucLo ] = ( HB_UCHAR ) iSortLo;
                  if( ucLo2 > ucLo )
                     ucLo2 = ucLo;
               }
            }
         }
      }
   }

   if( sort )
   {
      int iUp, iLo, iSort1, iSort2, iSort3, iAdd;

      if( iMulti > 0 )
      {
         if( iMulti > ucUp2 || iMulti > ucLo2 )
         {
#ifdef __HB_IGNORE_CP_ERRORS
            fprintf( stderr, "Harbour CP (%s) initialization failure (2)\n", id ); fflush( stderr );
            hb_xfree( buffer );
            return NULL;
#else
            hb_errInternal( 9994, "Harbour CP (%s) initialization failure", id, NULL );
#endif
         }

         if( iMulti <= 32 )
            iMulti = 33;
         else
            iMulti = 65;
      }
      else
         iMulti = 1;

      if( nCaseSort != HB_CDP_CSSORT_UPLO )
         ucLo2 = 0;

      for( iUp = iLo = 0, i = iMulti; i < 256; ++i )
      {
         if( sort[ i ] == 0 )
         {
            if( i < ( int ) ucUp2 )
               ++iUp;
            else if( i < ( int ) ucLo2 )
               ++iLo;
         }
      }
      for( iSort1 = iSort2 = iSort3 = 0, i = iMulti; i < 256; ++i )
      {
         if( sort[ i ] == 0 )
         {
            if( i < ( int ) ucUp2 )
               iAdd = ++iSort1;
            else if( i < ( int ) ucLo2 )
               iAdd = ++iSort2 + iSortUp + iUp;
            else
               iAdd = ++iSort3 + iUp + iSortLo + iLo;
         }
         else if( sort[ i ] <= iSortUp )
            iAdd = iUp;
         else
            iAdd = iUp + iLo;

         sort[ i ] += ( HB_UCHAR ) iAdd;
         if( acc )
            acc[ i ] += ( HB_UCHAR ) iAdd;
      }
      for( i = 0; i < cdp->nMulti; ++i )
      {
         cdp->multi[ i ].sortUp += iUp;
         cdp->multi[ i ].sortLo += iUp + iLo;
      }
   }

   if( cdp->nMultiUC )
   {
      cdp->wcharGet = hb_cdpMulti_get;
      cdp->wcharPut = hb_cdpMulti_put;
      cdp->wcharLen = hb_cdpMulti_len;
      cdp->type |= HB_CDP_TYPE_CUSTOM;
   }
   else
   {
      cdp->wcharGet = hb_cdpStd_get;
      cdp->wcharPut = hb_cdpStd_put;
      cdp->wcharLen = hb_cdpStd_len;
   }

   if( cdp->sort == NULL )
   {
      cdp->wcharCmp = hb_cdpBin_cmp;
      cdp->wcharCmpI = hb_cdpBin_cmpi;
      cdp->type |= HB_CDP_TYPE_BINSORT;
   }
   else if( cdp->nMulti )
   {
      cdp->wcharCmp = hb_cdpMulti_cmp;
      cdp->wcharCmpI = hb_cdpMulti_cmpi;
   }
   else
   {
      cdp->wcharCmp = hb_cdpStd_cmp;
      cdp->wcharCmpI = hb_cdpStd_cmpi;
   }

   return cdp;
}

static PHB_CODEPAGE * hb_cdpFindPos( const char * id )
{
   PHB_CODEPAGE * cdp_ptr;

   if( s_cdpList == NULL )
   {
      HB_UCHAR * flags, * upper, * lower;
      int i;

      memset( s_en_buffer, '\0', 0x300 );
      s_en_codepage.flags = flags = ( HB_UCHAR * ) s_en_buffer;
      s_en_codepage.upper = upper = ( HB_UCHAR * ) s_en_buffer + 0x100;
      s_en_codepage.lower = lower = ( HB_UCHAR * ) s_en_buffer + 0x200;
      for( i = 0; i < 0x100; ++i )
      {
         if( HB_ISDIGIT( i ) )
            flags[ i ] |= HB_CDP_DIGIT;
         if( HB_ISALPHA( i ) )
            flags[ i ] |= HB_CDP_ALPHA;
         if( HB_ISUPPER( i ) )
            flags[ i ] |= HB_CDP_UPPER;
         if( HB_ISLOWER( i ) )
            flags[ i ] |= HB_CDP_LOWER;
         upper[ i ] = ( HB_UCHAR ) HB_TOUPPER( i );
         lower[ i ] = ( HB_UCHAR ) HB_TOLOWER( i );
      }
      s_utf8_codepage.flags = s_en_codepage.flags;
      s_utf8_codepage.upper = s_en_codepage.upper;
      s_utf8_codepage.lower = s_en_codepage.lower;
      s_utf8_codepage.next  = NULL;
      s_en_codepage.next = &s_utf8_codepage;
      s_cdpList = &s_en_codepage;
   }

   cdp_ptr = &s_cdpList;

   while( *cdp_ptr )
   {
      if( strcmp( ( *cdp_ptr )->id, id ) == 0 )
         break;
      if( hb_stricmp( ( *cdp_ptr )->uniTable->uniID, id ) == 0 )
         break;
      cdp_ptr = &( *cdp_ptr )->next;
   }

   return cdp_ptr;
}

HB_BOOL hb_cdpRegisterRaw( PHB_CODEPAGE cdp )
{
   PHB_CODEPAGE * cdp_ptr;

   HB_TRACE( HB_TR_DEBUG, ( "hb_cdpRegisterRaw(%p)", ( void * ) cdp ) );

   cdp_ptr = hb_cdpFindPos( cdp->id );
   if( *cdp_ptr == NULL )
   {
      if( ! HB_CDP_ISCUSTOM( cdp ) )
      {
         cdp->wcharGet = hb_cdpStd_get;
         cdp->wcharPut = hb_cdpStd_put;
         cdp->wcharLen = hb_cdpStd_len;
      }
      if( cdp->wcharCmp == NULL )
         cdp->wcharCmp = cdp->sort == NULL ? hb_cdpBin_cmp :
                         ( cdp->nMulti ? hb_cdpMulti_cmp : hb_cdpStd_cmp );
      if( cdp->wcharCmpI == NULL )
         cdp->wcharCmpI = cdp->sort == NULL ? hb_cdpBin_cmpi :
                          ( cdp->nMulti ? hb_cdpMulti_cmpi : hb_cdpStd_cmpi );

      if( cdp->wcharCmp == hb_cdpBin_cmp && cdp->wcharCmpI == hb_cdpBin_cmpi )
         cdp->type |= HB_CDP_TYPE_BINSORT;

      *cdp_ptr = cdp;
      return HB_TRUE;
   }
   return HB_FALSE;
}

HB_BOOL hb_cdpRegisterNew( const char * id, const char * info,
                           PHB_UNITABLE uniTable,
                           const char * pszUpper, const char * pszLower,
                           unsigned int nACSort,
                           unsigned int nCaseSort,
                           HB_BOOL fUtf8 )
{
   PHB_CODEPAGE * cdp_ptr;

   HB_TRACE( HB_TR_DEBUG, ( "hb_cdpRegisterNew(%s,%s,%s,%s,%u,%u,%d)", id, info, pszUpper, pszLower, nACSort, nCaseSort, fUtf8 ) );

   cdp_ptr = hb_cdpFindPos( id );
   if( *cdp_ptr == NULL )
   {
      *cdp_ptr = hb_buildCodePage( id, info, uniTable, pszUpper, pszLower,
                                   nACSort, nCaseSort, fUtf8 );
      return *cdp_ptr != NULL;
   }
   return HB_FALSE;
}

void hb_cdpReleaseAll( void )
{
   HB_TRACE( HB_TR_DEBUG, ( "hb_cdpReleaseAll()" ) );

   while( s_cdpList )
   {
      void * buffer = s_cdpList->buffer;
      if( s_cdpList->uniTable->uniTrans )
      {
         hb_xfree( s_cdpList->uniTable->uniTrans );
         s_cdpList->uniTable->uniTrans = NULL;
      }
      s_cdpList = s_cdpList->next;
      if( buffer )
         hb_xfree( buffer );
   }
   if( s_rev_ctrl != NULL )
   {
      hb_xfree( s_rev_ctrl );
      s_rev_ctrl = NULL;
   }
}

PHB_CODEPAGE hb_cdpFind( const char * id )
{
   HB_TRACE( HB_TR_DEBUG, ( "hb_cdpFind(%s)", id ) );

   return id ? *hb_cdpFindPos( id ) : NULL;
}

PHB_CODEPAGE hb_cdpFindExt( const char * id )
{
   HB_TRACE( HB_TR_DEBUG, ( "hb_cdpFindExt(%s)", id ) );

   if( id )
   {
      PHB_CODEPAGE cdp = *hb_cdpFindPos( id );

      if( cdp )
         return cdp;

      hb_errRT_BASE( EG_ARG, 1302, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
   }
   return NULL;
}

HB_BOOL hb_cdpIsUTF8( PHB_CODEPAGE cdp )
{
   HB_TRACE( HB_TR_DEBUG, ( "hb_cdpIsUTF8(%p)", ( void * ) cdp ) );

   if( cdp == NULL )
      cdp = hb_vmCDP();

   return HB_CDP_ISUTF8( cdp );
}

PHB_CODEPAGE hb_cdpSelect( PHB_CODEPAGE cdp )
{
   PHB_CODEPAGE cdpOld;

   HB_TRACE( HB_TR_DEBUG, ( "hb_cdpSelect(%p)", ( void * ) cdp ) );

   cdpOld = hb_vmCDP();
   if( cdp )
      hb_vmSetCDP( cdp );

   return cdpOld;
}

const char * hb_cdpID( void )
{
   PHB_CODEPAGE cdp;

   HB_TRACE( HB_TR_DEBUG, ( "hb_cdpID()" ) );

   cdp = hb_vmCDP();

   return cdp ? cdp->id : NULL;
}

const char * hb_cdpSelectID( const char * id )
{
   PHB_CODEPAGE cdp;

   HB_TRACE( HB_TR_DEBUG, ( "hb_cdpSelectID(%s)", id ) );

   cdp = hb_cdpSelect( hb_cdpFindExt( id ) );

   return cdp ? cdp->id : NULL;
}

/* Caller must release the pointer */
const char ** hb_cdpList( void )
{
   PHB_CODEPAGE cdp;
   int iCount, iPos;
   const char ** list;

   cdp = s_cdpList;
   iCount = 0;
   while( cdp )
   {
      ++iCount;
      cdp = cdp->next;
   }

   list = ( const char ** ) hb_xgrab( ( iCount + 1 ) * sizeof( char * ) );

   cdp = s_cdpList;
   iPos = 0;
   while( cdp && iPos < iCount )
   {
      list[ iPos++ ] = cdp->id;
      cdp = cdp->next;
   }
   list[ iPos ] = NULL;

   return list;
}
