static char rcsid[] = "@(#)$Id: cs_binary.c,v 1.48 2006/04/16 21:01:35 hurtta Exp $";

/******************************************************************************
 *  The Elm (ME+) Mail System  -  $Revision: 1.48 $   $State: Exp $
 *
 *  Author: Kari Hurtta <hurtta+elm@posti.FMI.FI> (was hurtta+elm@ozone.FMI.FI)
 *
 *  Table koi82unicode is from RFC 1489, 
 *  author Andrew A. Chernov <ache@astral.msk.su>
 *
 *  Table cp12512unicode is contributed by 
 *    Andrey A. Chernov <ache@nagual.pp.ru>
 *
 *  Table cp12522unicode is from 
 *     http://www.microsoft.com/globaldev/reference/sbcs/1252.htm
 *  reference given on windows-1252 charset registeration.
 *
 *  Table iso8859152unicode is from
 #      http://www.unicode.org/Public/MAPPINGS/ISO8859/
 #  file 8859-15.TXT 
 #   (from same place that files on charset/MAPPINGS/ISO8859 directory)
 *
 *  Table koi8u_additions is contributed by 
 *     Mikhail Teterin <mi@aldan.algebra.com>
 *
 *****************************************************************************/

#include "headers.h"
#include "s_me.h"
#include "cs_imp.h"
#include "bindata.h"

DEBUG_VAR(Debug,__FILE__,"charset");

#include <errno.h>
#ifndef ANSI_C
extern int errno;
#endif


/* bindata ---------------------------------------------------------------- */

static uint16 lookup_binmap_0 P_((unsigned int ch,
				  struct  map_info *map,
				  int *found));

static uint16 lookup_binmap_0(ch,map,found)
     unsigned int ch;
     struct  map_info *map;
     int *found;
{

    *found = 0;

    if (&cs_ascii  != map->map_type &&
	&cs_onebyte != map->map_type)
	panic("STRING PANIC",__FILE__,__LINE__,"lookup_binmap_0",
	      "Bad maptype",0);
    
    return lookup_binmap(ch,map->c.bindata,found);
}

static unsigned char lookup_binmap_rev_0 P_((struct map_info *map,
					     unsigned int val,
					     int *found));

static unsigned char lookup_binmap_rev_0(map,val,found)
     struct map_info *map;
     unsigned int val;
     int *found;
{

    *found = 0;

    if (&cs_ascii  != map->map_type &&
	&cs_onebyte != map->map_type)
	panic("STRING PANIC",__FILE__,__LINE__,"lookup_binmap_rev_0",
	      "Bad maptype",0);
    
    return lookup_binmap_rev(map->c.bindata,val,found);
}

static struct bindata_ops bindata_ops = {
    lookup_binmap_0,
    lookup_binmap_rev_0
};


/* ascii ------------------------------------------------------------------ */

static void map_ascii_init_rev P_((struct map_info *map)); 
static void map_ascii_init_rev(map) 
     struct map_info *map; 
{
    int i;
    int col = 0;

    for (i = 0; i < MAP_REV_MAP_DIV; i++)
	map->b.ascii->map_ascii_rev[i] = 0;

    for (i = 0; i < 128; i++)
	map->b.ascii->map_ascii_rev[i] = i;

    for (i = 0; i < 128; i++) {
	unsigned char c = i + 128;
	uint16      val = map->b.ascii->map_ascii_upper[i];
	if (val != MAPPING_NONE) {
	    if (map->b.ascii->map_ascii_rev[val % MAP_REV_MAP_DIV])
		col++;
	    map->b.ascii->map_ascii_rev[val % MAP_REV_MAP_DIV] = c;
	}
    }
	
    if (col) {
	DPRINT(Debug,4,(&Debug,
			"Ascii/upper map %s -- %d collisions\n",
			map->map_name,col));
    }
}

static unsigned char cs_map_ascii_rev_0 P_((struct map_info *map,
					    unsigned int val,
					    int *found));
static unsigned char cs_map_ascii_rev_0(map,val,found) 
     struct map_info *map;
     unsigned int val;
     int *found;
{
    unsigned char c;
    int i;

    *found = 0;

    if (&cs_ascii != map->map_type)
	panic("STRING PANIC",__FILE__,__LINE__,"cs_map_ascii_rev_0",
	      "Bad maptype",0);

    c = map->b.ascii->map_ascii_rev[val % MAP_REV_MAP_DIV];
    if (!c)
	return 0x3F;  /* '?' */
    /* Can't be < 128, because val >= 128 here */
    if (c < 128) 
	return 0x3F;  /* '?' */
    /* Check case no collision */
    if (map->b.ascii->map_ascii_upper[c - 128] == val) {
	*found = 1;
	return c;
    }
	
    /* Do scan on case of collision */
    for (i = 0; i < 128; i++) {
	unsigned char c = i + 128;
	if (val == map->b.ascii->map_ascii_upper[i]) {
	    *found = 1;
	    return c;
	}
    }
    return 0x3F; /* '?' */  /* Not found */
}

static uint16 cs_unicode_ascii_helper_0 P_((unsigned int ch,
					    struct  map_info *map,
					    int *found));
static uint16 cs_unicode_ascii_helper_0(ch,map,found)
     unsigned int ch;
     struct  map_info *map;
     int *found;
{
    uint16  val;

    if (&cs_ascii != map->map_type || ch > 255)
	panic("STRING PANIC",__FILE__,__LINE__,"cs_unicode_ascii_helper_0",
	      "Bad maptype or value",0);
    
    val = map->b.ascii->map_ascii_upper[ch-128];
    if (val != MAPPING_NONE) {
	*found = 1;
	return val;
    }
    return 0x003F;  /* '?' */
}

static struct bindata_ops ascii_ops = {
    cs_unicode_ascii_helper_0,
    cs_map_ascii_rev_0
};

uint16 cs_unicode_ascii_helper_1(ch,map,found)
     unsigned int ch;
     struct  map_info *map;
     int *found;
{
    if (ch <= 127) {
	*found = 1;
	return ch;
    }

    if (!map->map_initialized)
	map->map_init_it(map);

    if (&cs_ascii != map->map_type || ch > 255)
	panic("STRING PANIC",__FILE__,__LINE__,"cs_unicode_ascii_helper_1",
	      "Bad maptype or value",0);

    return map->d.binary_ops->map_it(ch,map,found);
}

unsigned char cs_map_ascii_rev(map,val,found) 
     struct map_info *map;
     unsigned int val;
     int *found;
{
    unsigned char c;
    int i;

    *found = 0;

    if (val < 128) {
	*found = 1;
	return val;
    }

    if (&cs_ascii != map->map_type)
	panic("STRING PANIC",__FILE__,__LINE__,"cs_map_ascii_rev",
	      "Bad maptype",0);

    return map->d.binary_ops->map_rev_it(map,val,found);
}

static void map_init_bad P_((struct  map_info *map));
static void map_init_bad(map)
     struct  map_info *map;
{
    panic("STRING PANIC",__FILE__,__LINE__,"map_init_bad",
	  "map_init_bad called",0);
}

#if ANSI_C
static set_map_callmap  set_asciimap_value;
#endif
static int set_asciimap_value(name,map_info,l1,l2,fn)
     CONST char *name;
     struct map_info *map_info;
     long l1;
     long l2;
     CONST char *fn;
{
    if (map_info -> map_type != &cs_ascii)
	panic("STRING PANIC",__FILE__,__LINE__,"set_asciimap_value",
	      "map_info bad",0);

    if (l1 < 0 || l1 > 0xFF) {
	lib_error(CATGETS(elm_msg_cat, MeSet,MeMapBadValueL,
			  "Map %s: %s: Bad value: %ld"),
		  name,fn,l1);
	return 0;
    }

    if (l2 < 0 || l2 > 0xFFFF) {
	lib_error(CATGETS(elm_msg_cat, MeSet,MeMapBadValueL,
			  "Map %s: %s: Bad value: %ld"),
		  name,fn,l2);
	return 0;
    }

    if (l1 < 128) {
	if (l1 != l2) {
	    lib_error(CATGETS(elm_msg_cat, MeSet,MeMapBadValueL,
			      "Map %s: %s: Bad value: %ld"),
		      name,fn,l1);
	    return 0;
	}
	return 1;
    }

    map_info -> b.ascii->map_ascii_upper[l1-128] = l2;

    return 1;
}

static void add_text_to_asciimap P_((CONST char *name,
				     struct map_info *map,
				     FILE *F, char *fn));
static void add_text_to_asciimap(name,map,F,fn)
     CONST char *name;
     struct map_info *map;
     FILE *F;
     char *fn;
{
    static enum  field_type vector[3] = { 
	val_byte, val_unicode, val_comment
    };	
    int l;

    if (map -> map_type != &cs_ascii)
	panic("STRING PANIC",__FILE__,__LINE__,"add_text_to_asciimap",
	      "map bad",0);

    if (!map -> b.ascii)
	map -> b.ascii = safe_malloc(sizeof (* (map -> b.ascii)));

    for (l = 0; l < 128; l++)
	map -> b.ascii->map_ascii_upper[l] = MAPPING_NONE;

    read_map_format_a(name,map,NULL,
		      F,
		      set_asciimap_value,
		      3,vector,fn);
	
    map_ascii_init_rev(map);
}


static struct map_info * open_asciimap P_((const char *name));
static struct map_info * open_asciimap(name)
     CONST char *name;
{
    struct map_info *ret = NULL;
    char *fn = NULL;
    FILE *F = NULL;
    struct bindata_mapped_data * F1 = default_bindata();
    
    if (F1) {
	struct bindata_map * X = give_mapping(F1,name);

	if (X) {
	    ret = safe_malloc(sizeof (*ret));
	    ret -> map_type = &cs_ascii;
	    ret -> map_name = safe_strdup(name);
	    ret -> b.ascii  = NULL;
	    ret -> c.bindata    = X; 
	    ret -> d.binary_ops = &bindata_ops;

	    ret -> map_initialized = 1;
	    ret -> map_init_it = map_init_bad;

	    DPRINT(Debug,5,(&Debug,
			    "Map %s -- using bindata\n",ret->map_name));

	    return ret;
	}
    }

    F = open_mapname(name,&fn);
    if (F) {
	    
	ret = safe_malloc(sizeof (*ret));
	ret -> map_type = &cs_ascii;
	ret -> map_name = safe_strdup(name);
	ret -> b.ascii = safe_malloc(sizeof (* (ret -> b.ascii)));
	ret -> c.dummy      = NULL;
	ret -> d.binary_ops = &ascii_ops;

	ret -> map_initialized = 1;
	ret -> map_init_it = map_init_bad;
	    	
	add_text_to_asciimap(name,ret,F,fn);

	fclose(F);

	DPRINT(Debug,5,(&Debug,
			"Map %s -- using text data\n",ret->map_name));
    }

    if (fn)
	free(fn);
    return ret;
}



/* US-ASCII ------------------------------------------------------------- */

static void map_init_us_ascii P_((struct  map_info *map));
static void map_init_us_ascii(map) 
     struct  map_info *map;
{
    int i;
    
    map -> b.ascii = safe_malloc(sizeof (* (map -> b.ascii)));
    for (i = 0; i < 128; i++) {
	map->b.ascii->map_ascii_upper[i] = MAPPING_NONE;
    }
    map_ascii_init_rev(map);
    map->map_initialized = 1;
    
    DPRINT(Debug,5,(&Debug,
		    "Map %s initialized\n",map->map_name));
}


struct  map_info map_ascii = { 
    &cs_ascii, "US-ASCII", 0, map_init_us_ascii, NULL, NULL, &ascii_ops };

/* ISO-8859-1 ------------------------------------------------------------ */

static void map_init_latin1 P_((struct  map_info *map));
static void map_init_latin1(map) 
     struct  map_info *map;
{
    int i;
    
    map -> b.ascii = safe_malloc(sizeof (* (map -> b.ascii)));
    for (i = 0; i < 128; i++) {
	map->b.ascii->map_ascii_upper[i] = i + 128;
    }

    map_ascii_init_rev(map);
    map->map_initialized = 1;
    
    DPRINT(Debug,5,(&Debug,
	      "Map %s initialized\n",map->map_name));
}

struct  map_info map_latin1 = {
    &cs_ascii, "ISO-8859-1", 0, map_init_latin1, NULL, NULL, &ascii_ops };


/* KOI8-R ------------------------------------------------------------ */

/* Table compied from RFC 1489 */

static uint16 koi82unicode[128] = {
     0x2500,0x2502,0x250c,0x2510,0x2514,0x2518,0x251c,0x2524,
     0x252c,0x2534,0x253c,0x2580,0x2584,0x2588,0x258c,0x2590,
     0x2591,0x2592,0x2593,0x2320,0x25a0,0x2219,0x221a,0x2248,
     0x2264,0x2265,0x00a0,0x2321,0x00b0,0x00b2,0x00b7,0x00f7,
     0x2550,0x2551,0x2552,0x0451,0x2553,0x2554,0x2555,0x2556,
     0x2557,0x2558,0x2559,0x255a,0x255b,0x255c,0x255d,0x255e,
     0x255f,0x2560,0x2561,0x0401,0x2562,0x2563,0x2564,0x2565,
     0x2566,0x2567,0x2568,0x2569,0x256a,0x256b,0x256c,0x00a9,
     0x044e,0x0430,0x0431,0x0446,0x0434,0x0435,0x0444,0x0433,
     0x0445,0x0438,0x0439,0x043a,0x043b,0x043c,0x043d,0x043e,
     0x043f,0x044f,0x0440,0x0441,0x0442,0x0443,0x0436,0x0432,
     0x044c,0x044b,0x0437,0x0448,0x044d,0x0449,0x0447,0x044a,
     0x042e,0x0410,0x0411,0x0426,0x0414,0x0415,0x0424,0x0413,
     0x0425,0x0418,0x0419,0x041a,0x041b,0x041c,0x041d,0x041e,
     0x041f,0x042f,0x0420,0x0421,0x0422,0x0423,0x0416,0x0412,
     0x042c,0x042b,0x0417,0x0428,0x042d,0x0429,0x0427,0x042a
   };


static void map_init_koi8r P_((struct  map_info *map));
static void map_init_koi8r(map) 
     struct  map_info *map;
{
    int i;
    
    map -> b.ascii = safe_malloc(sizeof (* (map -> b.ascii)));
    for (i = 0; i < 128; i++) {
	map->b.ascii->map_ascii_upper[i] = koi82unicode[i];
    }

    map_ascii_init_rev(map);
    map->map_initialized = 1;
    
    DPRINT(Debug,5,(&Debug,
	      "Map %s initialized\n",map->map_name));
}

struct  map_info map_koi8r = {
    &cs_ascii, "KOI8-R", 0, map_init_koi8r, NULL, NULL, &ascii_ops };

/* KOI8-U ------------------------------------------------------------ */

/* Table compied from RFC 2319 */

/*
 * The koi8-u is a "superset" of koi8-r -- adding the Ukrainian
 * and Byelorussian specific characters at the expense of several
 * of the pseudo-graphics characters. The KOI8-U RFC is 2319. See,
 * for example: http://www.park.kiev.ua/multiling/koi8-u/rfc2319.txt
 */

struct charshort {
	unsigned char	byte;
	unsigned short	unicode;
} koi8u_additions[] = {
	{ 164, 0x454 }, /* SMALL UKRAINIAN IE */
	{ 166, 0x456 }, /* SMALL BYELORUSSIAN-UKRAINIAN I */
	{ 167, 0x457 }, /* SMALL YI (UKRAINIAN) */
	{ 173, 0x491 }, /* SMALL GHE WITH UPTURN */
	{ 180, 0x403 }, /* CAPITAL UKRAINIAN IE */
	{ 182, 0x406 }, /* CAPITAL BYELORUSSIAN-UKRAINIAN I */
	{ 183, 0x407 }, /* CAPITAL YI (UKRAINIAN) */
	{ 189, 0x490 }, /* CAPITAL GHE WITH UPTURN */
	{ 0, 0 }
};

static void map_init_koi8u P_((struct  map_info *map));
static void map_init_koi8u(map) 
     struct  map_info *map;
{
    struct charshort *replace;
    int i;
    
    map -> b.ascii = safe_malloc(sizeof (* (map -> b.ascii)));
    /* Initialize with koi8r */
    for (i = 0; i < 128; i++) {
	map->b.ascii->map_ascii_upper[i] = koi82unicode[i];
    }
    
    /* replace the few koi8-u specific additions: */
    for (replace = koi8u_additions; replace->byte; replace++)
	map->b.ascii->map_ascii_upper[replace->byte-128] =
	    replace->unicode;
    
    map_ascii_init_rev(map);
    map->map_initialized = 1;
    
    DPRINT(Debug,5,(&Debug,
		    "Map %s initialized\n",map->map_name));
}

struct  map_info map_koi8u = {
    &cs_ascii, "KOI8-U", 0, map_init_koi8u, NULL, NULL, &ascii_ops };

/* CP1251 ------------------------------------------------------------ */

/* Table compied from official Unicode mapping 2.01 */

static uint16 cp12512unicode[128] = {
     0x0402,0x0403,0x201A,0x0453,0x201E,0x2026,0x2020,0x2021,
     0x20AC,0x2030,0x0409,0x2039,0x040A,0x040C,0x040B,0x040F,
     0x0452,0x2018,0x2019,0x201C,0x201D,0x2022,0x2013,0x2014,
     0x003F,0x2122,0x0459,0x203A,0x045A,0x045C,0x045B,0x045F,
     0x00A0,0x040E,0x045E,0x0408,0x00A4,0x0490,0x00A6,0x00A7,
     0x0401,0x00A9,0x0404,0x00AB,0x00AC,0x00AD,0x00AE,0x0407,
     0x00B0,0x00B1,0x0406,0x0456,0x0491,0x00B5,0x00B6,0x00B7,
     0x0451,0x2116,0x0454,0x00BB,0x0458,0x0405,0x0455,0x0457,
     0x0410,0x0411,0x0412,0x0413,0x0414,0x0415,0x0416,0x0417,
     0x0418,0x0419,0x041A,0x041B,0x041C,0x041D,0x041E,0x041F,
     0x0420,0x0421,0x0422,0x0423,0x0424,0x0425,0x0426,0x0427,
     0x0428,0x0429,0x042A,0x042B,0x042C,0x042D,0x042E,0x042F,
     0x0430,0x0431,0x0432,0x0433,0x0434,0x0435,0x0436,0x0437,
     0x0438,0x0439,0x043A,0x043B,0x043C,0x043D,0x043E,0x043F,
     0x0440,0x0441,0x0442,0x0443,0x0444,0x0445,0x0446,0x0447,
     0x0448,0x0449,0x044A,0x044B,0x044C,0x044D,0x044E,0x044F
   };

static void map_init_cp1251 P_((struct  map_info *map));
static void map_init_cp1251(map)
     struct  map_info *map;
{
    int i;
    
    map -> b.ascii = safe_malloc(sizeof (* (map -> b.ascii)));
    for (i = 0; i < 128; i++) {
	map->b.ascii->map_ascii_upper[i] = cp12512unicode[i];
    }

    map_ascii_init_rev(map);
    map->map_initialized = 1;
    
    DPRINT(Debug,5,(&Debug,
		    "Map %s initialized\n",map->map_name));
}

struct  map_info map_cp1251 = {
    &cs_ascii, "CP1251", 0, map_init_cp1251, NULL, NULL, &ascii_ops };

/* CP1252 ------------------------------------------------------------ */

/* windows-1252 registeration gives:

   Charset name: windows-1252
   The name is suitable for use as the value of a MIME content-type
   parameter.

   Published specification(s):
   http://www.microsoft.com/globaldev/reference/sbcs/1252.htm
 
*/

static uint16 cp12522unicode[128] = {
    0x20AC,MAPPING_NONE,0x201A,0x0192,0x201E,0x2026,0x2020,0x2021,
    0x02C6,0x2030,0x0160,0x2039,0x0152,MAPPING_NONE,0x017D,MAPPING_NONE,
    MAPPING_NONE,0x2018,0x2019,0x201C,0x201D,0x2022,0x2013,0x2014,
    0x02DC,0x2122,0x0161,0x203A,0x0153,MAPPING_NONE,0x017E,0x0178,
    /* rest of mapping is identity ... */
    0x00A0,0x00A1,0x00A2,0x00A3,0x00A4,0x00A5,0x00A6,0x00A7,
    0x00A8,0x00A9,0x00AA,0x00AB,0x00AC,0x00AD,0x00AE,0x00AF,
    0x00B0,0x00B1,0x00B2,0x00B3,0x00B4,0x00B5,0x00B6,0x00B7,
    0x00B8,0x00B9,0x00BA,0x00BB,0x00BC,0x00BD,0x00BE,0x00BF,
    0x00C0,0x00C1,0x00C2,0x00C3,0x00C4,0x00C5,0x00C6,0x00C7,
    0x00C8,0x00C9,0x00CA,0x00CB,0x00CC,0x00CD,0x00CE,0x00CF,
    0x00D0,0x00D1,0x00D2,0x00D3,0x00D4,0x00D5,0x00D6,0x00D7,
    0x00D8,0x00D9,0x00DA,0x00DB,0x00DC,0x00DD,0x00DE,0x00DF,
    0x00E0,0x00E1,0x00E2,0x00E3,0x00E4,0x00E5,0x00E6,0x00E7,
    0x00E8,0x00E9,0x00EA,0x00EB,0x00EC,0x00ED,0x00EE,0x00EF,
    0x00F0,0x00F1,0x00F2,0x00F3,0x00F4,0x00F5,0x00F6,0x00F7,
    0x00F8,0x00F9,0x00FA,0x00FB,0x00FC,0x00FD,0x00FE,0x00FF };

static void map_init_cp1252 P_((struct  map_info *map));
static void map_init_cp1252(map)
     struct  map_info *map;
{
    int i;
    
    map -> b.ascii = safe_malloc(sizeof (* (map -> b.ascii)));
    for (i = 0; i < 128; i++) {
	map->b.ascii->map_ascii_upper[i] = cp12522unicode[i];
    }

    map_ascii_init_rev(map);
    map->map_initialized = 1;
    
    DPRINT(Debug,5,(&Debug,
		    "Map %s initialized\n",map->map_name));
}

struct  map_info map_cp1252 = { &cs_ascii, "CP1252", 0, 
				map_init_cp1252, NULL, NULL, &ascii_ops };


/* ISO-8859-15 ----------------------------------------------------------  */

static uint16  iso8859152unicode[128] = {
    0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 
    0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, 
    0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 
    0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, 
    0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x20AC, 0x00A5, 0x0160, 0x00A7, 
    0x0161, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, 
    0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x017D, 0x00B5, 0x00B6, 0x00B7, 
    0x017E, 0x00B9, 0x00BA, 0x00BB, 0x0152, 0x0153, 0x0178, 0x00BF, 
    0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 
    0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, 
    0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 
    0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, 
    0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 
    0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 
    0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 
    0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF
};

static void map_init_885915 P_((struct  map_info *map));
static void map_init_885915(map)
     struct  map_info *map;
{
    int i;
    
    map -> b.ascii = safe_malloc(sizeof (* (map -> b.ascii)));
    for (i = 0; i < 128; i++) {
	map->b.ascii->map_ascii_upper[i] = iso8859152unicode[i];
    }

    map_ascii_init_rev(map);
    map->map_initialized = 1;
    
    DPRINT(Debug,5,(&Debug,
		    "Map %s initialized\n",map->map_name));
}


struct  map_info map_885915 = { &cs_ascii, "ISO-8859-15", 0, map_init_885915, 
				NULL, NULL, &ascii_ops };

/* bytemap --------------------------------------------------------------- */

static void map_bytemap_init_rev P_((struct map_info *map)); 
static void map_bytemap_init_rev(map) 
     struct map_info *map; 
{
    int i;
    int col = 0;

    for (i = 0; i < MAP_REV_MAP_DIV; i++)
	map->b.bytemap->map_bytemap_rev[i] = 0;

    for (i = 0; i < 256; i++) {
	uint16      val = map->b.bytemap->map[i];
	if (val != MAPPING_NONE) {
	    if (map->b.bytemap->map_bytemap_rev[val % MAP_REV_MAP_DIV])
		col++;
	    map->b.bytemap->map_bytemap_rev[val % MAP_REV_MAP_DIV] = i;
	}
    }
	
    if (col) {
	DPRINT(Debug,5,(&Debug,
			"Single byte map %s -- %d collisions\n",
			map->map_name,col));
    }
}

static unsigned char cs_map_bytemap_rev_0 P_((struct map_info *map,
					      unsigned int val,
					      int * found));
static unsigned char cs_map_bytemap_rev_0(map,val,found) 
     struct map_info *map;
     unsigned int val;
     int * found;
{
    unsigned char c;
    int i;

    if (&cs_onebyte != map->map_type)
	panic("STRING PANIC",__FILE__,__LINE__," cs_map_bytemap_rev_0",
	      "Bad maptype",0);

    *found = 0;
    c = map->b.bytemap->map_bytemap_rev[val % MAP_REV_MAP_DIV];

    /* Check case no collision */
    if (map->b.bytemap->map[c] == val) {
	*found = 1;
	return c;
    }

    if (!c)
	return 0;
	
    /* Do scan on case of collision */
    for (i = 0; i < 256; i++) {
	if (val == map->b.bytemap->map[i]) {
	    *found = 1;
	    return i;
	}
    }
    return 0;  /* Not found */
}

static uint16 cs_unicode_bytemap_helper_0 P_((unsigned int ch,
					      struct  map_info *map,
					      int *found));
static uint16 cs_unicode_bytemap_helper_0(ch,map,found)
     unsigned int ch;
     struct  map_info *map;
     int *found;
{
    uint16  val;
	
    if (&cs_onebyte != map->map_type || ch > 255)
	panic("STRING PANIC",__FILE__,__LINE__,"cs_unicode_bytemap_helper_0",
	      "Bad maptype or value",0);

    val = map->b.bytemap->map[ch];
    if (val != MAPPING_NONE) {
	*found = 1;
	return val;
    }
    
    return 0x003F;  /* '?' */
}


static struct bindata_ops bytemap_ops = {
    cs_unicode_bytemap_helper_0,
    cs_map_bytemap_rev_0
};

uint16 cs_unicode_bytemap_helper_1(ch,map,found)
     unsigned int ch;
     struct  map_info *map;
     int *found;
{
	
    if (&cs_onebyte != map->map_type || ch > 255)
	panic("STRING PANIC",__FILE__,__LINE__,"cs_unicode_bytemap_helper_1",
	      "Bad maptype or value",0);

    return map->d.binary_ops->map_it(ch,map,found);
}

unsigned char cs_map_bytemap_rev(map,val,found) 
     struct map_info *map;
     unsigned int val;
     int * found;
{
    unsigned char c;
    int i;

    if (&cs_onebyte != map->map_type)
	panic("STRING PANIC",__FILE__,__LINE__," cs_map_bytemap_rev",
	      "Bad maptype",0);

    return map->d.binary_ops->map_rev_it(map,val,found);
}


#if ANSI_C
static set_map_callmap  set_bytemap_value;
#endif
static int set_bytemap_value(name,map_info,l1,l2,fn)
     CONST char *name;
     struct map_info *map_info;
     long l1;
     long l2;
     CONST char *fn;
{
    if (map_info -> map_type != &cs_onebyte)
	panic("STRING PANIC",__FILE__,__LINE__,"set_bytemap_value",
	      "map_info bad",0);

    if (l1 < 0 || l1 > 0xFF) {
	lib_error(CATGETS(elm_msg_cat, MeSet,MeMapBadValueL,
			  "Map %s: %s: Bad value: %ld"),
		  name,fn,l1);
	return 0;
    }

    if (l2 < 0 || l2 > 0xFFFF) {
	lib_error(CATGETS(elm_msg_cat, MeSet,MeMapBadValueL,
			  "Map %s: %s: Bad value: %ld"),
		  name,fn,l2);
	return 0;
    }

    map_info -> b.bytemap->map[l1] = l2;

    return 1;
}

static void add_text_to_bytemap P_((CONST char *name,
				     struct map_info *map,
				     FILE *F, char *fn));
static void add_text_to_bytemap(name,map,F,fn)
     CONST char *name;
     struct map_info *map;
     FILE *F;
     char *fn;
{
    static enum  field_type vector[3] = { 
	val_byte, val_unicode, val_comment
    };
    int l;


    if (map -> map_type != &cs_onebyte)
	panic("STRING PANIC",__FILE__,__LINE__,"add_text_to_bytemap",
	      "map bad",0);

    if (!map -> b.bytemap)
	map -> b.bytemap    = safe_malloc(sizeof (* (map -> b.bytemap)));


    /* We assume that values < 32 are same control characters
     * on all sets -- after all MIME requires that on all
     * character sets characters CR and LF are on same position
     */
    for (l = 0; l < 32; l++)
	map -> b.bytemap->map[l] = l;
    
    for (l = 32; l < 256; l++)
	map -> b.bytemap->map[l] = MAPPING_NONE;
    
    read_map_format_a(name,map,NULL,
		      F,
		      set_bytemap_value,
		      3,vector,fn);
    
    map_bytemap_init_rev(map);    
}


static struct map_info * open_bytemap P_((const char *name));
static struct map_info * open_bytemap(name)
     CONST char *name;
{
    struct map_info *ret = NULL;
    char *fn = NULL;

    FILE *F = NULL;
    struct bindata_mapped_data * F1 = default_bindata();
    
    if (F1) {
	struct bindata_map * X = give_mapping(F1,name);

	if (X) {
	    ret = safe_malloc(sizeof (*ret));
	    ret -> map_type = &cs_onebyte;
	    ret -> map_name = safe_strdup(name);
	    ret -> b.bytemap    = NULL;
	    ret -> c.bindata    = X; 
	    ret -> d.binary_ops = &bindata_ops;

	    ret -> map_initialized = 1;
	    ret -> map_init_it = map_init_bad;

	    DPRINT(Debug,5,(&Debug,
			    "Map %s -- using bindata\n",ret->map_name));

	    return ret;
	}
    }

    F = open_mapname(name,&fn);

    if (F) {
	    
	ret = safe_malloc(sizeof (struct map_info));
	ret -> map_type = &cs_onebyte;
	ret -> map_name = safe_strdup(name);
	ret -> b.bytemap    = safe_malloc(sizeof (* (ret -> b.bytemap)));
	ret -> c.dummy      = NULL;
	ret -> d.binary_ops = &bytemap_ops;
	ret -> map_initialized = 1;
	ret -> map_init_it = map_init_bad;
	    
	add_text_to_bytemap(name,ret,F,fn);

	fclose(F);

	DPRINT(Debug,5,(&Debug,
			"Map %s -- using text data\n",ret->map_name));
    }

    if (fn)
	free(fn);
    return ret;
}

/* ISO 646 --------------------------------------------------------------  */

static int variant_vector[12] = {
    0x23, 0x24, 0x40, 0x5B, 0x5C, 0x5D, 0x5E, 0x60, 0x7B, 0x7C, 0x7D, 0x7E 
};

static int variant_index P_((unsigned int ch));
static int variant_index(ch)
     unsigned int ch; 
{
    switch (ch) {
    case 0x23:    return 0;
    case 0x24:    return 1;
    case 0x40:    return 2;
    case 0x5B:    return 3;
    case 0x5C:    return 4;
    case 0x5D:    return 5;
    case 0x5E:    return 6;
    case 0x60:    return 7;
    case 0x7B:    return 8;
    case 0x7C:    return 9;
    case 0x7D:    return 10;
    case 0x7E:    return 11;
    }
    return -1;
}

static unsigned char map_iso646_rev P_((struct map_info *map,
					unsigned int val, int *found)); 
static unsigned char map_iso646_rev(map,val,found) 
     struct map_info *map;
     unsigned int val;
     int * found;
{
    int i;
    /* Same than on ASCII */
    if (val < 128 && -1 == variant_index(val)) {
	*found = 1;
	return val;
    }

    if (map) {
	if (!map->map_initialized)
	    map->map_init_it(map);

	/* ISO 646 variant part */
	for (i = 0; i < 12; i++) {
	    if (map->b.iso646->iso646[i] == val) {
		*found = 1;
		return variant_vector[i];
	    }
	}
    }

    *found = 0;
    return 0x3F; /* '?' */
}

static uint16 map_iso646 P_((struct map_info *map,
			     unsigned int ch)); 
static uint16 map_iso646(map,ch)
     struct map_info *map;
     unsigned int ch; 
{
    int idx;

    if (ch > 128)
	return MAPPING_NONE;   /* iso646 sets are 7-bit */

    idx = variant_index(ch);
    if (-1 == idx) {
	return ch;       /* Invariant part */
    }

    if (map) {
	if (!map->map_initialized)
	    map->map_init_it(map);

	/* ISO 646 variant part */
	return map->b.iso646->iso646[idx];
    }
    return MAPPING_NONE;
}

/* INVARIANT ------------------------------------------------------------ */

static void map_init_invariant P_((struct  map_info *map));
static void map_init_invariant(map) 
     struct  map_info *map;
{
    int i;

    map -> b.iso646 = safe_malloc(sizeof (* (map -> b.iso646)));
    for (i = 0; i < 12; i++)
	map->b.iso646->iso646[i] = MAPPING_NONE;

    map->map_initialized = 1;
    
    DPRINT(Debug,5,(&Debug,
		    "Map %s initialized\n",map->map_name));
}


struct  map_info map_invariant = { &cs_iso646, "INVARIANT", 0,
				   map_init_invariant, NULL, NULL, NULL };

/* ----------------------------------------------------------------------- */

/* Used by generate_bindata and cs_find_ascii */
static struct map_info **asciimaps = NULL;
static int               asciimap_count = 0;

/* Used by generate_bindata and cs_find_bytemap */
static struct map_info **bytemaps = NULL;
static int               bytemap_count = 0;




struct bindata_mapped_data *generate_bindata()
{
    struct bindata_mapped_data *r;

    int c = asciimap_count + bytemap_count;
    int i;

    r = malloc_bindata(c);           

    for (i = 0; i < asciimap_count; i++) {
	if (is_on_mapdir(asciimaps[i]->map_name)) {

	    /* MAP do not have necessary b.ascii structure
	       if bindata is on use, because we want regenerate it
	       we need parse textual map
	    */

	    if (!asciimaps[i]->b.ascii) {
		char *fn = NULL;
		FILE * F = open_mapname(asciimaps[i]->map_name,&fn);
		if (F) {

		    add_text_to_asciimap(asciimaps[i]->map_name,
					 asciimaps[i],F,fn);

		    fclose(F);
		}

		if (fn)
		    free(fn);
	    }

	    if (asciimaps[i]->b.ascii) {
		uint16 vector[256];

		int x;
		for (x = 0; x < 128; x++)
		    vector[x] = x;
		for (x = 128; x < 256; x++)
		    vector[x] = asciimaps[i]->b.ascii->map_ascii_upper[x-128];
		
		DPRINT(Debug,4,(&Debug,
				"... Adding ascii/upper map %s\n",
				asciimaps[i]->map_name));

		if (!add_bindata_map(r,vector,
				     asciimaps[i]->b.ascii->map_ascii_rev,
				     asciimaps[i]->map_name)) {
		    DPRINT(Debug,4,(&Debug,
				    "Ascii/upper map %s  can't be added to binary table\n",
				    asciimaps[i]->map_name));
		}    
	    }
	}
    }


    for (i = 0; i < bytemap_count; i++) {

	if (is_on_mapdir(bytemaps[i]->map_name)) {

	    /* MAP do not have necessary b.bytemap structure
	       if bindata is on use, because we want regenerate it
	       we need parse textual map
	    */
	    
	    if (!bytemaps[i]->b.bytemap) {
		char *fn = NULL;
		FILE * F = open_mapname(bytemaps[i]->map_name,&fn);
		if (F) {
		    
		    add_text_to_bytemap(bytemaps[i]->map_name,
					bytemaps[i],F,fn);

		    fclose(F);
		}
		
		if (fn)
		    free(fn);
	    }
	    	   
	    if (bytemaps[i]->b.bytemap) {
		
		DPRINT(Debug,4,(&Debug,
				"... Adding single byte map %s\n",
				bytemaps[i]->map_name));


		if (!add_bindata_map(r,
				     bytemaps[i]->b.bytemap->map,
				     bytemaps[i]->b.bytemap->map_bytemap_rev,
				     bytemaps[i]->map_name)) {
		    DPRINT(Debug,4,(&Debug,
				    "Single byte map %s  can't be added to binary table\n",
				    bytemaps[i]->map_name));
		}    	       
	    }
	}
    }

    trim_bindata(r);
   
    return r;
}


/* ======================================================================== */


static void cs_init_binary P_((struct string *str));
static void cs_init_binary(str)
     struct string *str;
{
    str->p->len = 0;
    str->p->a.bytes = 0;
}

static void cs_free_binary P_((struct string *str));
static void cs_free_binary(str)
     struct string *str;
{
    if (str->p->a.bytes) {
	free(str->p->a.bytes);
	str->p->a.bytes = NULL;
    }
    str->p->len = 0;
}

static void cs_init_s_binary P_((struct charset_state *st));
static void cs_init_s_binary(st)
     struct charset_state *st;
{
    st->p->ready  = 0;
    st->p->a.byte = 0;
}

static void cs_free_s_binary P_((struct charset_state *st));
static void cs_free_s_binary(st)
     struct charset_state *st;
{
    st->p->ready = 0;
    st->p->a.byte    = 0;
}

static void cs_soft_reset_s_binary P_((struct charset_state *st));
static void cs_soft_reset_s_binary(st)
     struct charset_state *st;
{
    st->p->ready = 0;
    st->p->a.byte    = 0;
}

static int cs_add_streambyte_to_s_binary P_((struct charset_state *st, 
					     int ch));
static int cs_add_streambyte_to_s_binary(st,ch)
     struct charset_state *st; 
     int ch;
{
    st->p->ready        = 1;
    st->p->a.byte    = ch;
    return 1;
}

static int cs_add_streambyte_to_binary P_((struct string *str,int ch));
static int cs_add_streambyte_to_binary(str,ch)
     struct string *str;
     int ch;
{
    /* NOTE:  str->p->a.bytes is not NUL terminated        */

    str->p->a.bytes = safe_realloc(str->p->a.bytes,str->p->len+1);
    str->p->a.bytes[str->p->len++] = ch;

    return 1;
}

static void cs_add_state_to_binary P_((struct string *str, 
				      struct charset_state *ch));  
static void cs_add_state_to_binary(str,ch)
     struct string *str;
     struct charset_state *ch;
{
    if (str->string_type->charset_type !=
	ch->charset->charset_type) 
	panic("STRING PANIC",__FILE__,__LINE__,"cs_add_state_to_binary",
	      "String/state type mismatch",0);

    /* NOTE:  str->p->a.bytes is not NUL terminated        */

    str->p->a.bytes = safe_realloc(str->p->a.bytes,str->p->len+1);
    str->p->a.bytes[str->p->len++] = ch->p->a.byte;
}  

static int cs_s_binary_same_char P_((struct charset_state *A,
				     struct charset_state *B,
				     int ignore_case));
static int cs_s_binary_same_char(A,B,ignore_case)
     struct charset_state *A;
     struct charset_state *B;
     int ignore_case;
{
    unsigned char c2 = B->p->a.byte; 
    unsigned char c1 = A->p->a.byte;

    if (c1 == c2)
	return 1;

    if (!ignore_case) 
	return 0;
    
    return -1;   /* Use UNICODE comparision on upper level instead */
}


/* Returns number of bytes added */
static int cs_add_streambytes_to_binary P_((struct string *str, 
					    int count, 
					    const unsigned char *data,
					    int *errors));

static int cs_add_streambytes_to_binary(str,count,data, errors)
     struct string *str;
     int count;
     CONST unsigned char *data;
     int *errors;
{
    int i;

    if (count < 0)
	panic("STRING PANIC",__FILE__,__LINE__,"cs_add_streambytes_to_binary",
	      "Negative length",0);

    *errors = 0;

    if (count > 0) {
	/* realloc with size 0 is equivalent of free and may 
	   corrupt memory ...
	*/

	/* NOTE:  str->p->a.bytes is not NUL terminated        */
	str->p->a.bytes = safe_realloc(str->p->a.bytes,str->p->len+count);
	for (i = 0; i < count; i++)
	    str->p->a.bytes[str->p->len++] = data[i];
	return count;
    }
    return 0;
}

static void cs_add_intdata_to_binary P_((struct string *str,
					 const struct string *data));
static void cs_add_intdata_to_binary(str,data)
     struct string *str;
     CONST struct string *data;
{
    int i;

    if (str->string_type->charset_type !=
	data->string_type->charset_type) 
	panic("STRING PANIC",__FILE__,__LINE__,"cs_add_intdata_to_binary",
	      "String type mismatch",0);
    

    if (data->p->len > 0) {
	/* realloc with size 0 is equivalent of free and may 
	   corrupt memory ...
	*/

	/* NOTE:  str->p->a.bytes is not NUL terminated        */
	str->p->a.bytes = safe_realloc(str->p->a.bytes,
				       data->p->len+str->p->len);
	
	for (i = 0; i < data->p->len; i++)
	    str->p->a.bytes[str->p->len++] = data->p->a.bytes[i];       
    }
}

static uint16 cs_unicode_bytemap_helper P_((unsigned int ch,
					    charset_t set, int *found));

static uint16 cs_unicode_bytemap_helper(ch,set, found)
     unsigned int ch;
     charset_t set;
     int *found;
{
    *found = 0;

    if (set->map_info) {
	return cs_unicode_bytemap_helper_1(ch,set->map_info,found);
    }
    /* We assume that values < 32 are same control characters
     * on all sets -- after all MIME requires that on all
     * character sets characters CR and LF are on same position
     */

    if (ch < 32) {
	*found = 1;
	return ch;
    }
    return 0x005F;  /* '_' */
}

/* Does compression, recalculation and so on ... 
   so 'const' is not very const
 */
static void cs_check_length_binary P_((const struct string *str));
static void cs_check_length_binary(str)
     CONST struct string *str;
{
    /* No compression */
}

static uint16 cs_give_unicode_from_bytemap P_((const struct string *str,
					       int pos, int *found));
static uint16 cs_give_unicode_from_bytemap(str,pos, found)
     CONST struct string *str;
     int pos;
     int *found;
{
    unsigned char ch;

    if (pos < 0 || pos >= str->p->len)
	panic("STRING PANIC",__FILE__,__LINE__,"cs_give_unicode_from_bytemap",
	      "Index out of array",0);

    ch = str->p->a.bytes[pos];
    return cs_unicode_bytemap_helper(ch,str->string_type,found);
}

static uint16 cs_give_unicode_from_s_bytemap P_((struct charset_state *st,
						 int *found));
static uint16 cs_give_unicode_from_s_bytemap(st,found)
     struct charset_state *st;
     int *found;
{
    unsigned char ch;

    ch = st->p->a.byte;
    return cs_unicode_bytemap_helper(ch,st->charset,found);
}

static uint16 cs_unicode_ascii_helper P_((unsigned int ch,
					  charset_t set, int *found));
static uint16 cs_unicode_ascii_helper(ch,set, found)
     unsigned int ch;
     charset_t set;
     int *found;
{
    *found = 0;
    /* Characters <= 127 are also UNICODE values on
       US-ASCII and it's supersets
    */

    if (ch <= 127) {
	*found = 1;
	return ch;
    }

    if (set->map_info) {
	return cs_unicode_ascii_helper_1(ch,set->map_info,found);
    }

    return 0x005F;  /* '_' */    
}

static uint16 cs_give_unicode_from_ascii P_((const struct string *str,
					     int pos, int *found));
static uint16 cs_give_unicode_from_ascii(str,pos, found)
     CONST struct string *str;
     int pos;
     int *found;
{
    unsigned char ch;

    if (pos < 0 || pos >= str->p->len)
	panic("STRING PANIC",__FILE__,__LINE__,"cs_give_unicode_from_ascii",
	      "Index out of array",0);

    ch = str->p->a.bytes[pos];
    return cs_unicode_ascii_helper(ch,str->string_type,found);
}

static uint16 cs_give_unicode_from_s_ascii P_((struct charset_state *st,
					       int *found));
static uint16 cs_give_unicode_from_s_ascii(st,found)
     struct charset_state *st;
     int *found;
{
    unsigned char ch;

    ch = st->p->a.byte;
    return cs_unicode_ascii_helper(ch,st->charset,found);
}


static uint16 cs_give_unicode_from_iso646 P_((const struct string *str,
					     int pos, int *found));
static uint16 cs_give_unicode_from_iso646(str,pos, found)
     CONST struct string *str;
     int pos;
     int *found;
{
    unsigned char ch;
    uint16 res;

    if (pos < 0 || pos >= str->p->len)
	panic("STRING PANIC",__FILE__,__LINE__,"cs_give_unicode_from_iso646",
	      "Index out of array",0);

    *found = 0;
    ch = str->p->a.bytes[pos];

    res = map_iso646(str->string_type->map_info,ch);

    if (res != MAPPING_NONE)
	*found = 1;
    else
	res = 0x003F;  /* '?' */
    return res;
}

static uint16 cs_give_unicode_from_s_iso646 P_((struct charset_state *st,
						int *found));
static uint16 cs_give_unicode_from_s_iso646(st,found)
     struct charset_state *st;
     int *found;
{
    unsigned char ch;
    uint16 res;

    *found = 0;
    ch = st->p->a.byte;

    res = map_iso646(st->charset->map_info,ch);
    if (res == MAPPING_NONE)
	res = 0x003F;  /* '?' */
    else
	*found = 1;
    return res;
}

static void cs_add_unicodedata_to_bytemap P_((struct string *str,
					     int len, const uint16 *data));
static void cs_add_unicodedata_to_bytemap(str,len,data)
     struct string *str;
     int len; 
     CONST uint16 *data;
{
    int i;

    if (len < 0)
	panic("STRING PANIC",__FILE__,__LINE__,"cs_add_unicodedata_to_bytemap",
	      "Negative length",0);

    if (len > 0) {
	/* realloc with size 0 is equivalent of free and may 
	   corrupt memory ...
	*/
	
	/* NOTE:  str->p->a.bytes is not NUL terminated        */
	str->p->a.bytes = safe_realloc(str->p->a.bytes,len+str->p->len);
	
	for (i = 0; i < len; i++) {
	    int found = 0;
	    unsigned char ch = '\0';
	    if (str->string_type->map_info) {
		ch = cs_map_bytemap_rev(str->string_type->map_info,data[i],
				     &found);
		if (!found) {
		    /* Try replacement character ... */
		    ch = cs_map_bytemap_rev(str->string_type->map_info,
					    0x005F /* '_' */,
					    &found);
		}
	    }

	    /* We assume that values < 32 are same control characters
	     * on all sets -- after all MIME requires that on all
	     * character sets characters CR and LF are on same position
	     */
	    
	    if (found)
		str->p->a.bytes[str->p->len++] = ch;
	    else if (data[i] < 32)
		str->p->a.bytes[str->p->len++] = data[i];	
	    
	    /* Because character set is unknow we do not know any
	     * replacement character what we can use, therefore
	     * we add nothing
	     */
	}
    }
}


static void cs_add_unicodedata_to_ascii P_((struct string *str,
					     int len, const uint16 *data));
static void cs_add_unicodedata_to_ascii(str,len,data)
     struct string *str;
     int len; 
     CONST uint16 *data;
{
    int i;

    if (len < 0)
	panic("STRING PANIC",__FILE__,__LINE__,"cs_add_unicodedata_to_ascii",
	      "Negative length",0);

    if (len > 0) {
	/* realloc with size 0 is equivalent of free and may 
	   corrupt memory ...
	*/

	/* NOTE:  str->p->a.bytes is not NUL terminated        */
	str->p->a.bytes = safe_realloc(str->p->a.bytes,len+str->p->len);

	for (i = 0; i < len; i++) {
	    int found;  /* Not used */

	    /* Characters <= 127 are also UNICODE values on
	       US-ASCII and it's supersets
	    */
	    
	    if (data[i] <= 127)
		str->p->a.bytes[str->p->len++] = data[i];
	    else if (str->string_type->map_info) {
		if (!str->string_type->map_info->map_initialized)
		    str->string_type->map_info->map_init_it(str->string_type->map_info);
		
		str->p->a.bytes[str->p->len++] = 
		    cs_map_ascii_rev(str->string_type->map_info,data[i],&found);
	    } else 
		str->p->a.bytes[str->p->len++] = 0x5F; /* '_' */
	}	    
    }
}

static void cs_add_unicodedata_to_iso646 P_((struct string *str,
					     int len, const uint16 *data));
static void cs_add_unicodedata_to_iso646(str,len,data)
     struct string *str;
     int len; 
     CONST uint16 *data;
{
    int i;

    if (len > 0) {
	/* realloc with size 0 is equivalent of free and may 
	   corrupt memory ...
	*/

	/* NOTE:  str->p->a.bytes is not NUL terminated        */
	str->p->a.bytes = safe_realloc(str->p->a.bytes,len+str->p->len);
	
	for (i = 0; i < len; i++) {
	    int found = 0;
	    unsigned char ch = map_iso646_rev(str->string_type->map_info,
					      data[i],
					      &found);
	    if (found)
		str->p->a.bytes[str->p->len++] = ch;
	    else
		str->p->a.bytes[str->p->len++] = 0x5F; /* '_' */
	}
    }   
}

static int cs_cmp_binary P_((const struct string *str1,const struct string *str2));
static int cs_cmp_binary(str1,str2)
     CONST struct string *str1;
     CONST struct string *str2;
{
    int i;

    if (str1->string_type->charset_type !=
	str2->string_type->charset_type) 
	panic("STRING PANIC",__FILE__,__LINE__,"cs_cmp_binary",
	      "String type mismatch",0);

    for (i = 0; i < str1->p->len && i < str2->p->len; i++) {
	if (str1->p->a.bytes[i] < str2->p->a.bytes[i])
	    return -1;
	if (str1->p->a.bytes[i] > str2->p->a.bytes[i])
	    return 1;
    }

    if (i < str1->p->len)
	return 1;
    if (i < str2->p->len)
	return -1;
    return 0;
}

#ifndef ASCII_CTYPE
unsigned char *cs_stream_from_locale P_((const struct string *str,
					 int *reslen));
unsigned char *cs_stream_from_locale(str,reslen)
     CONST struct string *str;
     int *reslen;
{
    unsigned char * ret;
    int l = 0,i;
    ret = safe_malloc(str->p->len+1);
    
    for (i = 0; i < str->p->len; i++) {
	if (!isprint(str->p->a.bytes[i]))
	    ret[l++] = '?';
	else
	    ret[l++] = str->p->a.bytes[i];
    }
    ret[l] = '\0';
    *reslen = l;

    return ret;    
}

int cs_s_locale_printable P_((struct charset_state *st));
int cs_s_locale_printable(st)
     struct charset_state *st;
{
    unsigned char c1 = st->p->a.byte;

    return isprint(c1);
}
#endif

static int cs_s_generic_printable P_((struct charset_state *st));
static int cs_s_generic_printable(st)
     struct charset_state *st;
{
#ifndef ASCII_CTYPE
    if (st->charset == system_charset)
	return cs_s_locale_printable(st);
#endif

    return -1; /* Use unicode values */
}

static int cs_s_binary_is_onebyte P_((struct charset_state *st));
static int cs_s_binary_is_onebyte(st)
     struct charset_state *st;
{
    unsigned char c1 = st->p->a.byte;
    return c1;
}


static unsigned char *cs_stream_from_bytemap P_((const struct string *str, 
						 int printable,
						 screen_info_p terminal,
						 int *reslen));
static unsigned char *cs_stream_from_bytemap(str,printable,terminal,reslen)
     CONST struct string *str;
     int printable;
     screen_info_p terminal;    /* NOT USED */
     int *reslen;
{
    unsigned char * ret;
    int l = 0,i;
 
#ifndef ASCII_CTYPE
    if (printable &&
	str->string_type == system_charset)
	return cs_stream_from_locale(str,reslen);
#endif

    ret = safe_malloc(str->p->len+1);
    
    if  (printable) {
	for (i = 0; i < str->p->len; i++) {
	    int found;
	    uint16 X = cs_unicode_bytemap_helper(str->p->a.bytes[i],
						 str->string_type,&found);

	    /* Check if correspond UNICODE character is printable
	    */
		
	    if (found && unicode_ch(X,UOP_printable))
		ret[l++] = str->p->a.bytes[i];
	    else {
		/* Try replacement character ... */
		unsigned char ch;
		int found;
		ch = cs_map_bytemap_rev(str->string_type->map_info,
				     0x003F /* '?' */,
				     &found);
		if (found)
		    ret[l++] = ch;
		/* Because charset is unknown we do not have able to print
		 * printable characters -- also we do not know replacement
		 * character....
		 */
	    }
	}
    } else {
	for (i = 0; i < str->p->len; i++) {
	    ret[l++] = str->p->a.bytes[i];
	}
    }
    ret[l] = '\0';
    *reslen = l;
    
    return ret;
}

static unsigned char *cs_stream_from_ascii P_((const struct string *str, 
					       int printable,
					       screen_info_p terminal,
					       int *reslen));
static unsigned char *cs_stream_from_ascii(str,printable,terminal,reslen)
     CONST struct string *str;
     int printable;
     screen_info_p terminal;  /* NOT USED */
     int *reslen;
{
    unsigned char * ret;
    int l = 0,i;

#ifndef ASCII_CTYPE
    if (printable &&
	str->string_type == system_charset)
	return cs_stream_from_locale(str,reslen);
#endif
    ret = safe_malloc(str->p->len+1);
    
    for (i = 0; i < str->p->len; i++) {
	if (str->p->a.bytes[i] < 32 && printable)
	    ret[l++] = 0x3F; /* '?' */
	else if (str->p->a.bytes[i] > 126 && printable) {
	    int found;
	    uint16 X = cs_unicode_ascii_helper(str->p->a.bytes[i],
					       str->string_type,&found);
	    		
	    if (found && unicode_ch(X,UOP_printable))
		ret[l++] = str->p->a.bytes[i];
	    else
		ret[l++] = 0x3F; /* '?' */		
	} else
	    ret[l++] = str->p->a.bytes[i];
    }
    ret[l] = '\0';
    *reslen = l;
    
    return ret;
}

static unsigned char *cs_stream_from_iso646 P_((const struct string *str, 
						int printable, 
						screen_info_p terminal,
						int *reslen));
static unsigned char *cs_stream_from_iso646(str,printable,terminal,reslen)
     CONST struct string *str;
     int printable;
     screen_info_p terminal;  /* NOT USED */
     int *reslen;
{
    unsigned char * ret;
    int l = 0,i;
 
#ifndef ASCII_CTYPE
    if (printable &&
	str->string_type == system_charset)
	return cs_stream_from_locale(str,reslen);
#endif
    ret = safe_malloc(str->p->len+1);
    
    for (i = 0; i < str->p->len; i++) {
	if ((str->p->a.bytes[i] < 32 && printable) ||
	    (str->p->a.bytes[i] > 126 && printable))
	    ret[l++] = 0x3F; /* '?' */
	/* ISO 646 variant part */
	else if (printable) { 
	    
	    if (map_iso646(str->string_type->map_info,str->p->a.bytes[i])
		!= MAPPING_NONE)
		ret[l++] = str->p->a.bytes[i];  /* Assume printable */
	    else
		ret[l++] = 0x3F; /* '?' */	   
	} else
	    ret[l++] = str->p->a.bytes[i];
    }
    ret[l] = '\0';
    *reslen = l;

    return ret;

}

#ifndef ASCII_CTYPE
unsigned char *cs_streamclip_from_locale(str,pos,len)
     CONST struct string *str;
     int *pos; 
     int len;
{
    unsigned char * ret;
    int l = 0,i;

    ret = safe_malloc(len+1);

    for (i = 0; i < len && *pos < str->p->len; i++, (*pos)++) {
	if (!isprint(str->p->a.bytes[*pos]))
	    ret[l++] = '?';
	else
	    ret[l++] = str->p->a.bytes[*pos];
    }
    ret[l] = '\0';

    return ret;
}

#endif

static unsigned char *cs_streamclip_from_bytemap P_((const struct string *str,
						     int *pos, int len,
						     screen_info_p terminal,
						     struct cs_printable_len *printable_len));
static unsigned char *cs_streamclip_from_bytemap(str,pos,len,terminal,printable_len)
     CONST struct string *str;
     int *pos; 
     int len;
     screen_info_p terminal; /* NOT USED */
     struct cs_printable_len *printable_len; /* NOT USED */
{
    unsigned char * ret;
    int l = 0,i;

    if (*pos < 0)
	panic("STRING PANIC",__FILE__,__LINE__,"cs_streamclip_from_bytemap",
	      "Index out of array",0);

    if (len < 0)
	panic("STRING PANIC",__FILE__,__LINE__,"cs_streamclip_from_bytemap",
	      "Negative size",0);

#ifndef ASCII_CTYPE
    if (str->string_type == system_charset)
	return cs_streamclip_from_locale(str,pos,len);
#endif

    ret = safe_malloc(len+1);
    for (i = 0; i < len && *pos < str->p->len; i++, (*pos)++) {
	int found;
	uint16 X = cs_unicode_bytemap_helper(str->p->a.bytes[*pos],
					     str->string_type,&found);

	    
	if (found && unicode_ch(X,UOP_printable))
	    ret[l++] = str->p->a.bytes[*pos];
	else {
	    /* Try replacement character ... */
	    unsigned char ch;
	    int found;
	    ch = cs_map_bytemap_rev(str->string_type->map_info,
				 0x003F /* '?' */,
				 &found);
	    if (found)
		ret[l++] = ch;
	    /* Because charset is unknown we do not have able to print
	     * printable characters -- also we do not know replacement
	     * character....
	     */
	}
    }
    ret[l] = '\0';
    
    return ret;
}

static unsigned char *cs_streamclip_from_ascii P_((const struct string *str,
						   int *pos, int len,
						   screen_info_p terminal,
						   struct cs_printable_len *printable_len));
static unsigned char *cs_streamclip_from_ascii(str,pos,len,terminal,printable_len)
     CONST struct string *str;
     int *pos; 
     int len;
     screen_info_p terminal; /* NOT USED */
     struct cs_printable_len *printable_len; /* NOT USED */
{
    unsigned char * ret;
    int l = 0,i;

    if (*pos < 0)
	panic("STRING PANIC",__FILE__,__LINE__,"cs_streamclip_from_ascii",
	      "Index out of array",0);

    if (len < 0)
	panic("STRING PANIC",__FILE__,__LINE__,"cs_streamclip_from_ascii",
	      "Negative size",0);

#ifndef ASCII_CTYPE
    if (str->string_type == system_charset)
	return cs_streamclip_from_locale(str,pos,len);
#endif

    ret = safe_malloc(len+1);

    for (i = 0; i < len && *pos < str->p->len; i++, (*pos)++) {

	if (str->p->a.bytes[*pos] < 32)  
	    ret[l++] = 0x3F; /* '?' */
	else if (str->p->a.bytes[*pos] > 126) {
	    int found;
	    uint16 X = cs_unicode_ascii_helper(str->p->a.bytes[*pos],
					       str->string_type,&found);
	    
	    if (found && unicode_ch(X,UOP_printable)) 
		ret[l++] = str->p->a.bytes[*pos];
	    else
		ret[l++] = 0x3F; /* '?' */		
	} else
	    ret[l++] = str->p->a.bytes[*pos];
    }
    ret[l] = '\0';

    return ret;
}

static unsigned char *cs_streamclip_from_iso646 P_((const struct string *str,
						    int *pos, int len,
						    screen_info_p terminal,
						    struct cs_printable_len *printable_len));
static unsigned char *cs_streamclip_from_iso646(str,pos,len,terminal,printable_len)
     CONST struct string *str;
     int *pos; 
     int len;
     screen_info_p terminal; /* NOT USED */
     struct cs_printable_len *printable_len; /* NOT USED */
{
    unsigned char * ret;
    int i, l=0;

    if (*pos < 0)
	panic("STRING PANIC",__FILE__,__LINE__,"cs_streamclip_from_iso646",
	      "Index out of array",0);
    
    if (len < 0)
	panic("STRING PANIC",__FILE__,__LINE__,"cs_streamclip_from_iso646",
	      "Negative size",0);

#ifndef ASCII_CTYPE
    if (str->string_type == system_charset)
	return cs_streamclip_from_locale(str,pos,len);
#endif

    ret = safe_malloc(len+1);
    for (i = 0; i < len && *pos < str->p->len; i++, (*pos)++) {
	if (str->p->a.bytes[*pos] < 32 ||
	    str->p->a.bytes[*pos] > 126)
	    ret[l++] = 0x3F; /* '?' */
	/* ISO 646 variant part */
	else if (MAPPING_NONE != map_iso646(str->string_type->map_info,
					    str->p->a.bytes[*pos]))
	    ret[l++] = str->p->a.bytes[*pos];  /* Assume printable */
	else
	    ret[l++] = 0x3F; /* '?' */	   
    }
    ret[l] = '\0';
    return ret;
}

static void cs_clip_from_binary P_((struct string *ret,
				    const struct string *str,
				    int *pos, int len));

static void cs_clip_from_binary(ret,str,pos,len)
     struct string *ret;
     CONST struct string *str;
     int *pos; 
     int len;
{
    int i;

    if (ret->string_type->charset_type !=
	str->string_type->charset_type) 
	panic("STRING PANIC",__FILE__,__LINE__,"cs_clip_from_binary",
	      "String type mismatch",0);

    if (*pos < 0)
	panic("STRING PANIC",__FILE__,__LINE__,"cs_clip_from_binary",
	      "Index out of array",0);

    if (len < 0)
	panic("STRING PANIC",__FILE__,__LINE__,"cs_clip_from_binary",
	      "Negative size",0);

    if (len > 0) {
	int POS = *pos;
	int source_len = str->p->len;
	unsigned char * target_bytes;
	unsigned char * source_bytes = str->p->a.bytes;


	/* realloc with len == 0 is equivalent of freeing and
	   may result corruption of memory ...
	*/

	/* NOTE:  str->p->a.bytes is not NUL terminated        */
	ret->p->a.bytes = safe_realloc(ret->p->a.bytes,len);
	ret->p->len = 0;
	
	target_bytes = ret->p->a.bytes;

	for (i = 0; i < len && POS < source_len; i++, POS++) {   
	    target_bytes[i] = source_bytes[POS];
	}
	ret->p->len = i;
	*pos = POS;
    } else {  
	/* NULL (empty) string */

	if (ret->p->a.bytes)
	    free(ret->p->a.bytes);
	ret->p->a.bytes = NULL;
	ret->p->len = 0;
    }
}

static int cs_can_ascii_bytemap P_((const struct string *str));
static int cs_can_ascii_bytemap(str)
     CONST struct string *str;
{
    if (str->string_type->map_info) {
	int i;

	for (i = 0; i < str->p->len; i++) {
	    unsigned char ch = str->p->a.bytes[i];

	    int found;
	    uint16 val = val = 
		cs_unicode_bytemap_helper_1(ch,str->string_type->map_info,
					    &found);

	    if (!found || val > 127)
		return 0;
	}
	return 1;
    } else
	return 0;
}

static int cs_can_ascii_ascii P_((const struct string *str));
static int cs_can_ascii_ascii(str)
     CONST struct string *str;
{
    int i;

    for (i = 0; i < str->p->len; i++) {
	/* NOTE: str->p->a.bytes is unsigned */
	if (str->p->a.bytes[i] > 127)
	    return 0;
    }
    return 1;
}


static int cs_can_ascii_iso646 P_((const struct string *str));
static int cs_can_ascii_iso646(str)
     CONST struct string *str;
{
    int i;

    for (i = 0; i < str->p->len; i++) {
	/* NOTE: str->p->a.bytes is unsigned */
	if (str->p->a.bytes[i] > 127)
	    return 0;
	/* MAPPING_NONE > 127 */
	if (map_iso646(str->string_type->map_info,
		   str->p->a.bytes[i]) > 127)
	    return 0;
    }
    return 1;    
}

#ifndef ASCII_CTYPE

int cs_find_pattern_from_locale(str,pattern)
     CONST struct string *str;
     CONST struct string *pattern;
{
    int ret = 0;
    int i, j;

    for (i = 0; i < str->p->len; ) {
	CONST int s = i + 1;

	for (j = 0; j < pattern->p->len && i < str->p->len; j++,i++) {
	    unsigned char c2 = pattern->p->a.bytes[j];
	    unsigned char c1 = str->p->a.bytes[i];

	    c1 = tolower(c1);	
	    c2 = tolower(c2);
	    
	    if (c1 != c2)
		break;
	}
	if (j >= pattern->p->len) {
	    DPRINT(Debug,63,(&Debug,
			     "cs_find_pattern_from_locale=1 MATCH -- str='%.*s' pattern='%.*s'\n",
			     str->p->len,str->p->a.bytes,
			     pattern->p->len,pattern->p->a.bytes));
	    
	    ret = 1;
	    break;
	}
	i = s;
    }
    if (!ret) {
	DPRINT(Debug,63,(&Debug,
			 "cs_find_pattern_from_locale=0 NO MATCH -- str='%.*s' pattern='%.*s'\n",
			 str->p->len,str->p->a.bytes,
			 pattern->p->len,pattern->p->a.bytes));
    }
    return ret;
}
#endif

static int cs_find_pattern_from_bytemap P_((const struct string *str,
					  const struct string *pattern,
					  int ignore_case));
static int cs_find_pattern_from_bytemap(str,pattern,ignore_case)
     CONST struct string *str;
     CONST struct string *pattern;
     int ignore_case;
{
    if (pattern->string_type != str->string_type) 
	panic("STRING PANIC",__FILE__,__LINE__,"cs_find_pattern_from_bytemap",
	      "Charset mismatch",0);
    
#ifndef ASCII_CTYPE
    if (ignore_case && str->string_type == system_charset)
	return cs_find_pattern_from_locale(str,pattern);
#endif

    return -1;   /* Use UNICODE comparision on upper level instead */
}

static int cs_find_pattern_from_ascii P_((const struct string *str,
					  const struct string *pattern,
					  int ignore_case));

static int cs_find_pattern_from_ascii(str,pattern,ignore_case)
     CONST struct string *str;
     CONST struct string *pattern;
     int ignore_case;
{
    int ret = 0;
    int i, j;

    if (pattern->string_type->charset_type !=
	str->string_type->charset_type) 
	panic("STRING PANIC",__FILE__,__LINE__,"cs_find_pattern_from_ascii",
	      "String type mismatch",0);

#ifndef ASCII_CTYPE
    if (ignore_case && str->string_type == system_charset)
	return cs_find_pattern_from_locale(str,pattern);
#endif

    for (i = 0; i < str->p->len; ) {
	CONST int s = i + 1;
	
	for (j = 0; j < pattern->p->len && i < str->p->len; j++,i++) {
	    unsigned char c2 = pattern->p->a.bytes[j];
	    unsigned char c1 = str->p->a.bytes[i];
		

	    if (ignore_case && (c1 >= 0x80 || c2 >= 0x80)) {
		/* Use UNICODE comparision on upper level instead */

		DPRINT(Debug,63,(&Debug,
				 "cs_find_pattern_from_ascii=-1\n"));	    
		return -1;   
	    }

	    if (ignore_case && c1 >= 0x41 && c1 <= 0x5A) {
		c1 = ( c1 - 0x41) + 0x61;
	    }
    
	    if (ignore_case && c2 >= 0x41 && c2 <= 0x5A) {
		c2 = ( c2 - 0x41) + 0x61;
	    }

	    if (c1 != c2)
		break;
	}
	if (j >= pattern->p->len) {
	    DPRINT(Debug,63,(&Debug,
			     "cs_find_pattern_from_ascii=1 MATCH\n"));
	    ret = 1;
	    break;
	}
	i = s;
    }
    if (!ret) {
	DPRINT(Debug,63,(&Debug,
			 "cs_find_pattern_from_ascii=0 NO MATCH\n"));

    }
    return ret;
}
static int cs_find_pattern_from_iso646 P_((const struct string *str,
					  const struct string *pattern,
					  int ignore_case));
static int cs_find_pattern_from_iso646(str,pattern,ignore_case)
     CONST struct string *str;
     CONST struct string *pattern;
     int ignore_case;
{
    int ret = 0;
    int i, j;
    
    if (pattern->string_type->charset_type !=
	str->string_type->charset_type) 
	panic("STRING PANIC",__FILE__,__LINE__,"cs_find_pattern_from_iso646",
	      "String type mismatch",0);

#ifndef ASCII_CTYPE
    if (ignore_case && str->string_type == system_charset)
	return cs_find_pattern_from_locale(str,pattern);
#endif

    for (i = 0; i < str->p->len; ) {
	CONST int s = i + 1;
	
	for (j = 0; j < pattern->p->len && i < str->p->len; j++,i++) {

	    /* TODO: We should check also mapping to unicode and use
	       unicode information to see what is lowecase equivalent 
	       of character...
	    */

	    unsigned char c2 = pattern->p->a.bytes[j];
	    unsigned char c1 = str->p->a.bytes[i];
		
	    if (ignore_case && c1 >= 0x41 && c1 <= 0x5A) {
		c1 = ( c1 - 0x41) + 0x61;
	    }
    
	    if (ignore_case && c2 >= 0x41 && c2 <= 0x5A) {
		c2 = ( c2 - 0x41) + 0x61;
	    }

	    if (c1 != c2)
		break;
	}
	if (j >= pattern->p->len) {
	    DPRINT(Debug,63,(&Debug,
			     "cs_find_pattern_from_iso646=1 MATCH\n"));
	    ret = 1;
	    break;
	}
	i = s;
    }
    if (!ret) {
	DPRINT(Debug,63,(&Debug,
			 "cs_find_pattern_from_iso646=0 NO MATCH\n"));

    }
    return ret;
}

#ifndef ASCII_CTYPE
void cs_remove_control_locale(str,repl)
     CONST struct string *str;
     unsigned int repl;
{
    int i;
    for (i = 0; i < str->p->len; i++) {
	
	if (iscntrl(str->p->a.bytes[i]))
	    str->p->a.bytes[i] = repl;
    }
}

#endif


static void cs_remove_control_ascii P_((const struct string *str));
static void cs_remove_control_ascii(str)
     CONST struct string *str;
{
    int i;

#ifndef ASCII_CTYPE
    /* 32 is space on ascii... */       
    if (str->string_type == system_charset) {
	cs_remove_control_locale(str,32);
	return;
    }
#endif

    for (i = 0; i < str->p->len; i++) {
	if (str->p->a.bytes[i] < 32 ||
	    str->p->a.bytes[i] == 127)
	    str->p->a.bytes[i] = 32;
	else if (str->p->a.bytes[i] > 127 &&
		 str->string_type->map_info) {
	    unsigned char ch = str->p->a.bytes[i];
	    int found;
	    uint16  val = cs_unicode_ascii_helper_1(ch,str->string_type->map_info,&found);

	    if (!found ||
		unicode_ch(val,UOP_noctrl) == 0)
		str->p->a.bytes[i] = 32;
	}
    }
}

static void cs_remove_control_bytemap P_((const struct string *str));
static void cs_remove_control_bytemap(str)
     CONST struct string *str;
{
    int i;
    int found;
    unsigned char rep = 32;
    
    if (str->string_type->map_info) 
	rep = cs_map_bytemap_rev(str->string_type->map_info,
				 0x0020 /* '?' */,
				 &found);
#ifndef ASCII_CTYPE
    if (str->string_type == system_charset) {
	cs_remove_control_locale(str,rep);
	return;
    }
#endif

    for (i = 0; i < str->p->len; i++) {

	if (str->string_type->map_info) {
	    unsigned char ch = str->p->a.bytes[i];
	    int found;
	    uint16 val = cs_unicode_bytemap_helper_1(ch,str->string_type->map_info,&found);

	    if (!found ||
		unicode_ch(val,UOP_noctrl) == 0)
		str->p->a.bytes[i] = rep;
	}
	/* We assume that values < 32 are same control characters
	 * on all sets -- after all MIME requires that on all
	 * character sets characters CR and LF are on same position
	 */	
	else if (str->p->a.bytes[i] < 32)
	    str->p->a.bytes[i] = rep; 
    }
}

static void cs_remove_control_iso646 P_((const struct string *str));
static void cs_remove_control_iso646(str)
     CONST struct string *str;
{
    int i;
    for (i = 0; i < str->p->len; i++) {
	if (str->p->a.bytes[i] < 32 ||
	    str->p->a.bytes[i] >= 127)
	    str->p->a.bytes[i] = 32;
	else {
	    if (map_iso646(str->string_type->map_info,str->p->a.bytes[i])
		== MAPPING_NONE)
		str->p->a.bytes[i] = 0x3F;  /* '?' */
	}
    }
}

static struct  map_info * cs_find_bytemap P_((const char * map_name));
static struct  map_info * cs_find_bytemap(map_name)
     CONST char * map_name;
{
    int i;
    struct  map_info *ret;

    for (i =0; i < bytemap_count; i++)
	if (0 == strcmp(bytemaps[i]->map_name,map_name))
	    return bytemaps[i];

    ret = open_bytemap(map_name);
    if (ret) {
	bytemaps = safe_realloc(bytemaps,
				(bytemap_count + 1) * 
				sizeof (struct  map_info *));
	bytemaps[bytemap_count++] = ret;
    }
    return ret;
}

static struct  map_info * cs_find_ascii P_((const char * map_name));
static struct  map_info * cs_find_ascii(map_name)
     CONST char * map_name;
{
    struct map_info *ret;


    int i;
    static struct  map_info * maps[] = { &map_ascii, &map_latin1, 
					 &map_koi8r, &map_cp1251, 
					 &map_cp1252, &map_koi8u, 
					 &map_885915, NULL };

    for (i = 0; maps[i]; i++)
	if (0 == strcmp(map_name,maps[i]->map_name))
	    return maps[i];

    for (i =0; i < asciimap_count; i++)
	if (0 == strcmp(asciimaps[i]->map_name,map_name))
	    return asciimaps[i];

    ret = open_asciimap(map_name);
    if (ret) {
	asciimaps = safe_realloc(asciimaps,
				 (asciimap_count + 1) * 
				 sizeof (struct  map_info *));
	asciimaps[asciimap_count++] = ret;
    }

    return ret;
}


static struct  map_info * cs_find_iso646 P_((const char * map_name));
static struct  map_info * cs_find_iso646(map_name)
     CONST char * map_name;
{
    int vector[12];
    char * str, *p;
    struct  map_info * ret = NULL;
    int i;
    int err = 0;

    static struct  map_info ** dyn_maps = NULL;
    static            int dyn_map_count = 0;

    if (0 == istrcmp(map_name,map_invariant.map_name))
	return &map_invariant;

    for (i =0; i < dyn_map_count; i++)
	if (0 == strcmp(dyn_maps[i]->map_name,map_name))
	    return dyn_maps[i];

    str = safe_strdup(map_name);
    
    i = 0;
    for (p = strtok(str," \t,"); p; p = strtok(NULL," \t,")) {
	char *c = '\0';
	long l;
	if (i >= 12) {
	    lib_error(CATGETS(elm_msg_cat, MeSet,MeTooManyPositions,
			      "Map %s: Too many positions given on defination"),
		      map_name);
	    err++;
	    break;
	}
	if (0 == strcmp(p,"-"))
	    vector[i] = l = MAPPING_NONE;
	else if ('#' == p[0])
	    vector[i] = l = strtol(p+1,&c,10);
	else
	    vector[i] = l = strtol(p,&c,16);
	if (*c || l < 0 || l > 0xFFFF) {
	    lib_error(CATGETS(elm_msg_cat, MeSet,MeTooManyPositions,
			      "Map %s: Bad value on position: %s"),
		      map_name,p);
	    err++;
	}
	i++;
    }
    if (i < 12) {
	lib_error(CATGETS(elm_msg_cat, MeSet,MeTooFewPositions,
			  "Map %s: Too few positions given on defination"),
		  map_name);
	err++;
    }

    if (!err) {
	for (i = 0; i < dyn_map_count; i++) {
	    int j;
	    for (j = 0; j < 12; j++) 
		if (vector[j] != dyn_maps[i]->b.iso646->iso646[j])
		    break;
	    if (12 == j)
		ret = dyn_maps[i];
	}
    }

    if (!err && !ret) {
	ret = safe_malloc(sizeof (struct map_info));
	ret -> map_type = &cs_iso646;
	ret -> map_name = safe_strdup(map_name);
	ret -> b.iso646 = safe_malloc(sizeof (* (ret -> b.iso646)));
	ret -> map_initialized = 1;
	ret -> map_init_it = map_init_bad;
	    
	for (i = 0; i < 12; i++)
	    ret->b.iso646->iso646[i] = vector[i];

	dyn_maps = safe_realloc(dyn_maps,
				(dyn_map_count + 1) * 
				sizeof (struct  map_info *));
	dyn_maps[dyn_map_count++] = ret;
    }

    free(str);
    return ret;
}

static int cs_iso646_properties P_((charset_t st));
static int cs_iso646_properties(st)
     charset_t st;
{
    int prop = 0;

    /* We know at least invariant part of charset */
    prop |= CS_mapping | CS_printable; 

    return prop;
}

static int cs_onebyte_properties P_((charset_t st));
static int cs_onebyte_properties(st)
     charset_t st;
{
    int prop = 0;

    if (st->map_info)
	prop |= CS_mapping | CS_printable; 

#ifndef ASCII_CTYPE
    /* We know printable characters from locale */
    if (system_charset == st)
	prop |= CS_printable;
#endif

    return prop;
}


static int cs_ascii_properties P_((charset_t st));
static int cs_ascii_properties(st)
     charset_t st;
{
    int prop = 0;

    /* We know at least ascii part of charset */
    prop |= CS_printable | CS_mapping;

    return prop;
}

#if ANSI_C
#define S_(x) static x;
#else
#define S_(x)
#endif

S_(cs_estimate_clip_string cs_estimate_clip_unsupported)
static int cs_estimate_clip_unsupported(str,pos,len,terminal,printable_len)
     CONST struct string *str;
     int pos; 
     int len;      /* UPPER LIMIT */
     screen_info_p terminal;
     struct cs_printable_len *printable_len;
{
    panic("STRING PANIC",__FILE__,__LINE__,"cs_estimate_clip_unsupported",
	  "cs_estimate_clip_unsupported() called",0);

    return -1;
}

int cs_info_set_scan(new_vals,new_setlist,setcount,ptr_94,ptr_96,
			    banks)
     struct charcode_info *new_vals;
     struct setlist       *new_setlist;
     int setcount;
     int *ptr_94; 
     int *ptr_96;
     int banks[ISO2022_BANK_NUM];
{
    int i;

    for (i = 0; i < ISO2022_BANK_NUM; i++)
	banks[i] = -1;

    /* Non ISO2022 charsets there may be only on 94 and one 96 set */

    for (i = 0; i < setcount; i++) {

	switch (new_setlist->sets[i]->type) {
	case iso2022_94x94: 
	case iso2022_94: 
	    if (-1 == *ptr_94)
		*ptr_94 = i;
	    else {
		lib_error(CATGETS(elm_msg_cat, MeSet, MeNoSeveral94,
				  "Charset type %s does not allow several 94 or 94x94 bank specifications"),
			  new_vals->charset_type->type_name);
		return 0; /* Discard bank defination */
	    }
	    break;
	    
	case iso2022_96x96:
	case iso2022_96: 
	    if (-1 == *ptr_96)
		*ptr_96 = i;
	    else {
		lib_error(CATGETS(elm_msg_cat, MeSet, MeNoSeveral96,
				  "Charset type %s does not allow several 96 or 96x96 bank specifications"),
			  new_vals->charset_type->type_name);
		return 0; /* Discard bank defination */
	    }
	    break;		
	}

	if (new_setlist->sets[i]->bank >= 0 &&
	    new_setlist->sets[i]->bank <= ISO2022_BANK_NUM) {
	    if (-1 == banks[new_setlist->sets[i]->bank])
		banks[new_setlist->sets[i]->bank] = i;
	    else {
		lib_error(CATGETS(elm_msg_cat, MeSet, MeNoSeveralBank,
				  "Charset type %s does not allow several bank-G%d specifications"),
			  new_vals->charset_type->type_name,
			  new_setlist->sets[i]->bank);
		return 0; /* Discard bank defination */
	    }
	}
    }

    for (i = setcount; 
	 i < sizeof (new_setlist->sets) / sizeof (new_setlist->sets[0]);
	 i++)
	new_setlist->sets[i] = NULL;
    
    return 1;
}

static int cs_iso2022_info_set_ascii P_((struct charcode_info *new_vals,
					  struct setlist       *new_setlist,
					  int setcount));
static int cs_iso2022_info_set_ascii(new_vals, new_setlist,setcount)
     struct charcode_info *new_vals;
     struct setlist       *new_setlist;
     int setcount;
{       
    int ptr_94 = -1;
    int ptr_96 = -1;
    int banks[ISO2022_BANK_NUM];
    int i;

    if (!cs_info_set_scan(new_vals,new_setlist,setcount,&ptr_94,&ptr_96,
			  banks)) {
	return 0;
    }

    for (i = 0; i < setcount; i++) {
	switch (new_setlist->sets[i]->type) {
	case iso2022_94x94: 
	    lib_error(CATGETS(elm_msg_cat, MeSet, MeNoMultibyte,
			      "Charset type %s does not allow multibyte bank specifications."),
		      new_vals->charset_type->type_name);
	    return 0;
	case iso2022_96x96:
	    lib_error(CATGETS(elm_msg_cat, MeSet, MeNoMultibyte,
			      "Charset type %s does not allow multibyte bank specifications."),
		      new_vals->charset_type->type_name);
	    return 0;
	}
    }

    /* Auto add ASCII bank */
    if (banks[0] == -1 && -1 == ptr_94 &&
	setcount < sizeof (new_setlist->sets) / 
	sizeof (new_setlist->sets[0])) {
	
	new_setlist->sets[setcount++] = ASCII_BANK;
    }
    
    new_vals->iso2022_info = loc_setlist(*new_setlist);
    new_vals->flags       &= ~SET_nodata;

    return 1;
}



static int cs_iso2022_info_set_binary P_((struct charcode_info *new_vals,
					  struct setlist       *new_setlist,
					  int setcount));
static int cs_iso2022_info_set_binary(new_vals, new_setlist,setcount)
     struct charcode_info *new_vals;
     struct setlist       *new_setlist;
     int setcount;
{      
    int ptr_94 = -1;
    int ptr_96 = -1;
    int banks[ISO2022_BANK_NUM];
    int i;

    if (!cs_info_set_scan(new_vals,new_setlist,setcount,&ptr_94,&ptr_96,
			  banks)) {
	return 0;
    }

    for (i = 0; i < setcount; i++) {
	switch (new_setlist->sets[i]->type) {
	case iso2022_94x94: 
	    lib_error(CATGETS(elm_msg_cat, MeSet, MeNoMultibyte,
			      "Charset type %s does not allow multibyte bank specifications."),
		      new_vals->charset_type->type_name);
	    return 0;
	case iso2022_96x96:
	    lib_error(CATGETS(elm_msg_cat, MeSet, MeNoMultibyte,
			      "Charset type %s does not allow multibyte bank specifications."),
		      new_vals->charset_type->type_name);
	    return 0;
	}
    }

    new_vals->iso2022_info = loc_setlist(*new_setlist);
    new_vals->flags       &= ~SET_nodata;

    return 0;
 }




/* --------------------------------------------------------------------- */

struct charset_type cs_iso646    = { "iso646-set",
				     cs_init_binary,
				     cs_free_binary,
				     cs_add_streambyte_to_binary,
				     cs_add_intdata_to_binary,
				     cs_check_length_binary,
				     cs_give_unicode_from_iso646,
				     cs_add_unicodedata_to_iso646,
				     cs_cmp_binary,
				     cs_stream_from_iso646,
				     cs_can_ascii_iso646,
				     cs_streamclip_from_iso646,
				     cs_clip_from_binary,
				     cs_find_pattern_from_iso646,
				     cs_add_streambytes_to_binary,
				     cs_find_iso646,
				     cs_remove_control_iso646, 
				     cs_add_state_to_binary,
				     cs_init_s_binary,
				     cs_free_s_binary,
				     cs_add_streambyte_to_s_binary,
				     cs_soft_reset_s_binary,
				     cs_give_unicode_from_s_iso646,
				     cs_s_binary_same_char,
				     cs_s_generic_printable,
				     cs_s_binary_is_onebyte,
				     cs_iso646_properties,
				     cs_estimate_clip_unsupported,
				     cs_iso2022_info_set_binary,
				     &cs_utf8
};

struct charset_type cs_onebyte   = { "one-byte-map",
				     cs_init_binary,
				     cs_free_binary,
				     cs_add_streambyte_to_binary,
				     cs_add_intdata_to_binary,
				     cs_check_length_binary,
				     cs_give_unicode_from_bytemap,
				     cs_add_unicodedata_to_bytemap,
				     cs_cmp_binary,
				     cs_stream_from_bytemap,
				     cs_can_ascii_bytemap,
				     cs_streamclip_from_bytemap,
				     cs_clip_from_binary,
				     cs_find_pattern_from_bytemap,
				     cs_add_streambytes_to_binary,
				     cs_find_bytemap,
				     cs_remove_control_bytemap,
				     cs_add_state_to_binary,
				     cs_init_s_binary,
				     cs_free_s_binary,
				     cs_add_streambyte_to_s_binary,
				     cs_soft_reset_s_binary,
				     cs_give_unicode_from_s_bytemap,
				     cs_s_binary_same_char,
				     cs_s_generic_printable,
				     cs_s_binary_is_onebyte,
				     cs_onebyte_properties,
				     cs_estimate_clip_unsupported,
				     cs_iso2022_info_set_binary,
				     &cs_iso646
};

struct charset_type cs_ascii   = {   "ascii-set",
				     cs_init_binary,
				     cs_free_binary,
				     cs_add_streambyte_to_binary,
				     cs_add_intdata_to_binary,
				     cs_check_length_binary,
				     cs_give_unicode_from_ascii,
				     cs_add_unicodedata_to_ascii,
				     cs_cmp_binary,
				     cs_stream_from_ascii,
				     cs_can_ascii_ascii,
				     cs_streamclip_from_ascii,
				     cs_clip_from_binary,
				     cs_find_pattern_from_ascii,
				     cs_add_streambytes_to_binary,
				     cs_find_ascii,
				     cs_remove_control_ascii,
				     cs_add_state_to_binary,
				     cs_init_s_binary,
				     cs_free_s_binary,
				     cs_add_streambyte_to_s_binary,
				     cs_soft_reset_s_binary,
				     cs_give_unicode_from_s_ascii,
				     cs_s_binary_same_char,
				     cs_s_generic_printable,
				     cs_s_binary_is_onebyte,
				     cs_ascii_properties,
				     cs_estimate_clip_unsupported,
				     cs_iso2022_info_set_ascii,
				     &cs_onebyte
};

/*
 * Local Variables:
 *  mode:c
 *  c-basic-offset:4
 *  buffer-file-coding-system: iso-8859-1
 * End:
 */



syntax highlighted by Code2HTML, v. 0.9.1