static char rcsid[] = "@(#)$Id: charset.c,v 1.92 2006/06/28 19:21:09 hurtta Exp $";

/******************************************************************************
 *  The Elm (ME+) Mail System  -  $Revision: 1.92 $   $State: Exp $
 *
 *  Author: Kari Hurtta <hurtta+elm@posti.FMI.FI>
 *                      (was hurtta+elm@ozone.FMI.FI)
*****************************************************************************/

#include "headers.h"
#include "s_me.h"
#include "cs_imp.h"
#ifdef USE_DLOPEN
#include "shared_imp.h"
#endif

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

DEBUG_VAR(Debug,__FILE__,"charset");

struct  charset_type * cs_first_type = &cs_unknown;

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

static struct extra_sets {
    struct charcode_info set;
    struct extra_sets    * next;
} * extra_sets = NULL, * extra_sets_tail = NULL;
static int extra_sets_count = 0;

union compare_info {
    CONST char *name;
    struct charcode_info * part;
};

typedef  int compare_set P_((struct charcode_info *set, 
			     union compare_info data));

static struct charcode_info * find_set P_(( compare_set *func,
					    union compare_info data ));

static struct charcode_info * find_set(func,data)
     compare_set *func;
     union compare_info data;
{
    int i;
    struct extra_sets *walk;

#define TEST(ITEM) \
    if (0 != (ITEM.flags & SET_valid) && func(&(ITEM),data)) { \
      DPRINT(Debug,9,(&Debug,"find_set: found=%p (%s)\n",&(ITEM),\
		       ITEM.MIME_name ? ITEM.MIME_name : "<no MIME name>")); \
    return &(ITEM); }

#define SCAN(SET) \
    for (i = 0; SET[i].charset_type; i++) {\
	DPRINT(Debug,72,(&Debug,"find_set: (scan#%d) %p -- flags=%d\n",\
			 i,&(SET[i]),SET[i].flags)); \
	TEST(SET[i]); } 


    DPRINT(Debug,70,(&Debug, 
		     "find_set: precompiled_sets\n"));
    SCAN(precompiled_sets);
    
    DPRINT(Debug,70,(&Debug, "find_set: extra_sets\n"));
    for (walk = extra_sets; walk; walk = walk -> next) {
	DPRINT(Debug,72,(&Debug, "find_set: (walk) %p -- flags=%d\n",
		    &(walk->set),walk->set.flags)); 
	TEST(walk->set);
    }

    DPRINT(Debug,40,(&Debug, "find_set: NOT FOUND\n"));
    return NULL;
}	
#undef SCAN
#undef SET

/*
  Assigned MIB enum Numbers
  -------------------------
  0-2		Reserved
  3-999		Set By Standards Organizations
  1000-1999	Unicode / 10646
  2000-2999	Vendor


  Divide array to 3 classes so memory usage is better ... 

*/


struct MIBenum_class  MIBenum_CLASS[] = {
    { NULL, 0 }                /*    0 -  999 */,
    { NULL, 0 }                /* 1000 - 1999 */,
    { NULL, 0 }                /* 2000 - 2999 */,
}; 

CONST int MIBenum_CLASS_len = 
    sizeof (MIBenum_CLASS) / sizeof (MIBenum_CLASS[0]);


static int array_initialized = 0;

static int add_MIBenum P_((struct charcode_info *ptr));
static int add_MIBenum(ptr)
     struct charcode_info *ptr;
{
    CONST int MIBenum = ptr->MIBenum;
    int MIBenum_class;
    int val;

    if (MIBenum <= 0)
	return 0;
    
    MIBenum_class = MIBenum / MIBenum_class_divide;
    val           = MIBenum % MIBenum_class_divide;

    if (MIBenum_class >= MIBenum_CLASS_len)
	return 0;

    if (MIBenum_CLASS[MIBenum_class].MIBenum_array_len <= val) {
	int X = val+1;
	int i;

	if (X < 100) X = 100;



	MIBenum_CLASS[MIBenum_class].MIBenum_array =
	    safe_realloc (MIBenum_CLASS[MIBenum_class].MIBenum_array,
			  X * sizeof (MIBenum_CLASS[MIBenum_class].MIBenum_array[0])); 

	for (i = MIBenum_CLASS[MIBenum_class].MIBenum_array_len; i < X; i++)
	    MIBenum_CLASS[MIBenum_class].MIBenum_array[i] = NULL;
	
	MIBenum_CLASS[MIBenum_class].MIBenum_array_len = X;
    }

    if (MIBenum_CLASS[MIBenum_class].MIBenum_array[val] &&
	MIBenum_CLASS[MIBenum_class].MIBenum_array[val] != ptr &&
	MIBenum_CLASS[MIBenum_class].MIBenum_array[val] -> flags & SET_valid)
	return 0;

    if (MIBenum_CLASS[MIBenum_class].MIBenum_array[val])
	MIBenum_CLASS[MIBenum_class].MIBenum_array[val] -> MIBenum = 0;

    MIBenum_CLASS[MIBenum_class].MIBenum_array[val] = ptr; 
    
    return 1;
}

static void add_builtin_sets P_((void));
static void add_builtin_sets(void)
{
    int i;

    for (i = 0; precompiled_sets[i].charset_type; i++) {
	if (precompiled_sets[i].MIBenum) {
	    DPRINT(Debug,49,(&Debug,"(#%d) Setting MIBEnum for %s (%d)\n",
			     i,
			     precompiled_sets[i].MIME_name ? 
			     precompiled_sets[i].MIME_name  :
			     "<no MIME name>",
			     precompiled_sets[i].MIBenum));

	    if (!add_MIBenum(& (precompiled_sets[i]))) {
		panic("CHARSET PANIC",__FILE__,__LINE__,"add_builtin_set",
		      "Duplicate or bad MIBenum on builtin set",0);
	    }			 
	}
    }
    array_initialized++;
}

static struct charcode_info * add_set P_((struct charcode_info s));
static struct charcode_info * add_set(s)
     struct charcode_info s;
{
    struct extra_sets *tmp = safe_malloc( sizeof (struct extra_sets));
    
    if (CS_charset_magic  != s.magic)
	panic("STRING PANIC",__FILE__,__LINE__,"add_set",
	      "Bad magic number",0);

    tmp->set  = s;
    tmp->next = NULL;

    if (!array_initialized)
	add_builtin_sets();

    if (extra_sets_tail)
	extra_sets_tail->next = tmp;    
    extra_sets_tail = tmp;    
    if (!extra_sets)
	extra_sets = tmp;
    
    extra_sets_count++;

    if (tmp->set.MIME_name) {
	DPRINT(Debug,1,(&Debug, 
			"charset: Adding charset %s (type %s)\n",
		  tmp->set.MIME_name,
		  tmp->set.charset_type->type_name));	
    }

    if (tmp->set.MIBenum && !add_MIBenum(& (tmp->set))) {
	lib_error(CATGETS(elm_msg_cat, MeSet,
			  MeDuplicateMibEnum,
			  "Charset %s have duplicate or bad MIBenum %d"),
		  tmp->set.MIME_name ? tmp->set.MIME_name : "<no MIME name>",
		  tmp->set.MIBenum);

	tmp->set.MIBenum = 0;   /* Discard MIBEnum */
	

    }

    return &(tmp->set);
}

#if defined(WCHAR) && defined(__STDC_ISO_10646__)

void change_system_charset_1 P_((void));

void change_system_charset()
{
    char ** cs; 
    int i;


    if (system_charset->charset_type == &cs_unknown) {
	DPRINT(Debug,9,(&Debug,"change_system_charset: System charset is already type unknown\n"));
	return;
    }

    if (!system_charset->MIME_name) {
	DPRINT(Debug,9,(&Debug,"change_system_charset: System charset have no MIME name\n"));
	return;
    }

    cs = give_dt_path_as_elems(&locale_charsets,"locale-charsets");
    if (!cs) {
	DPRINT(Debug,9,(&Debug,
			"change_system_charset:  No change: locale-charsets list empty\n"));
	return;
    }

    for (i = 0; cs[i]; i++) {
	if (0 == istrcmp(cs[i],system_charset->MIME_name)) {
	    DPRINT(Debug,9,(&Debug,
			    "change_system_charset: Found system charset %s from list, item #%d\n",
			    system_charset->MIME_name,i));
	    break;
	}
    }

    if (!cs[i]) {
	DPRINT(Debug,9,(&Debug,
			"change_system_charset:  No change\n"));
	return;
    }

    change_system_charset_1();
}

void change_system_charset_1() {
    struct charcode_info new_vals;
    charset_t x;


    new_vals = *system_charset;
    new_vals.charset_type = &cs_unknown;
    new_vals.map_info     = NULL;
    new_vals.flags       |= SET_nodata;

    system_charset->flags &=  ~SET_valid;

    x = add_set(new_vals);

    DPRINT(Debug,9,(&Debug,
		    "change_system_charset_1: Changed system charset %s (type %s) to type unknown\n",
		    system_charset->MIME_name,
		    system_charset->charset_type->type_name));
    
    system_charset  = x;
}
#endif

static int same_name P_((struct charcode_info *set, union compare_info data));
static int same_name (set,data)
     struct charcode_info *set;
     union compare_info data;
{
    DPRINT(Debug,71,(&Debug, 
		     "same_name -- set=%p (MIME_name=%s,codeset=%s,data.name=%s)\n",
		     set,
		     set->MIME_name ? set->MIME_name : "<NULL>",
		     set->codeset   ? set->codeset : "<NULL>",
		     data.name));
    if (set->codeset &&
	0 == istrcmp(data.name,set->codeset))
	return 1;
    
    if (!set->MIME_name)
	return 0;
    return 0 == istrcmp(data.name,set->MIME_name);
}

struct charset_alias *  charset_alias_list  = NULL;
int charset_alias_count = 0;

static int locate_charset_alias P_((const char * name));
static int locate_charset_alias(name)
     CONST char * name;
{
    int i;

    for (i = 0; i < charset_alias_count; i++)
	if (0 == istrcmp(charset_alias_list[i].name,name))
	    return i;
    return -1;
}

static int add_charset_alias P_((const char * name, charset_t set));
static int add_charset_alias(name,set)
     CONST char * name;
     charset_t set;
{
    charset_alias_list = safe_realloc(charset_alias_list,
				      (charset_alias_count +1) *
				      sizeof (struct charset_alias));
    charset_alias_list[charset_alias_count].name = safe_strdup(name);
    charset_alias_list[charset_alias_count].set = set;
    return charset_alias_count++;
}

charset_t codeset_name_to_charset (name)
     CONST char *name;
{
    charset_t ret;
    union compare_info D;
    D.name =  name;
    
    ret = find_set(same_name,D);

    return ret;
}

charset_t MIME_name_to_charset (name,flag)
     CONST char *name;
     int flag;
{
    int create          = flag & CHARSET_create;
    CONST int noautodef = flag & CHARSET_noautodef;
    int i;
    charset_t ret;
    struct charcode_info new;
    int do_ascii = 0;

    int do_cs = 0;
    struct  charset_type * cs_type = NULL;
    struct  map_info     * cs_map  = NULL;
    uint16               MIBenum   = 0;

    union compare_info D;
    D.name =  name;
    
    ret = find_set(same_name,D);

    if (!ret && 0 == istrcmp(name,"US-ASCII")) {
	DPRINT(Debug,7,(&Debug, 
		   "MIME_name_to_charset: US-ASCII not found -- using builting constant...\n"));
	ret = ASCII_SET;
    }

    if (!ret && 0 == istrcmp(name,"UTF-8")) {
	DPRINT(Debug,7,(&Debug, 
		   "MIME_name_to_charset: UTF-8 not found -- using builting constant...\n"));
	ret = UTF8_ENCODING;  /* This is NOT from precompiled_sets[..] */ 
    }

    if (ret && &cs_unknown == ret->charset_type &&
	0 == (charset_properties(ret) & CS_mapping)) {
	DPRINT(Debug,9,(&Debug, "MIME_name_to_charset(%s), found=%p as unknown\n",
			name,ret));
	
#ifdef USE_DLOPEN
	if (!noautodef &&
	    cs_auto_charset(name,&cs_type,&cs_map)) {
	    DPRINT(Debug,9,(&Debug, 
			    "MIME_name_to_charset: Using autocreate instead... (MIBenum=%d)\n",
			    (int) ret->MIBenum));
	    do_cs = 1;
	    create = 1;

	    ret-> flags &=  ~SET_valid;    /* Invalidate result so that 
					      autocreated set is found instead
					   */
	    MIBenum      = ret->MIBenum;

	    goto create_new;
	}
#endif
    }
   
    if (ret) {
	DPRINT(Debug,9,(&Debug, "MIME_name_to_charset(%s), found=%p",
			name,ret));

    found_it:
	if (ret->codeset) {
	    DPRINT(Debug,9,(&Debug, ", codeset=%s",ret->codeset));
	}
	DPRINT(Debug,9,(&Debug, "\n",ret->codeset));

	if (!ret->MIME_name && create) {
	    CONST char * X =  ret->codeset ? ret->codeset : name;   /* Should be unnecessary */
	    DPRINT(Debug,1,(&Debug,"*** No MIME name for charset %s, using %s as MIME name\n",
			    name,X));
	    ret->MIME_name = safe_strdup(X);
	}

	if (!ret->MIME_name) {
	    DPRINT(Debug,1,(&Debug,"*** No MIME name for charset %s (codeset %s)\n",
			    name,ret->codeset ? ret->codeset : "<?>"));

	    lib_error(CATGETS(elm_msg_cat, MeSet,
			      MeNoMIMEname,
			      "Charset %s exists, but that is not suitable name for MIME use"),
		      name);
	    return NULL;
	}

	/* Initialize associated map if not done already ... */       
	if (ret->map_info && !ret->map_info->map_initialized)
	    ret->map_info->map_init_it(ret->map_info);

	return ret;
    }

    /* Check aliases ... */
    if (0 <= (i = locate_charset_alias(name)) &&
	(ret = charset_alias_list[i].set) &&
	ret->MIME_name &&
	/* Ignore aliases which point invalidated charsets.
	   These charsets may me invalidated by setting
	   explicit compatcharsets on user's elmrc
	*/
	(0 != (ret-> flags & SET_valid))) {

	DPRINT(Debug,9,(&Debug,
			"MIME_name_to_charset(%s), found=%p (alias of %s)",
			name,ret,
			ret->MIME_name ? ret->MIME_name : "<no MIME name>"));	
	goto found_it;

    }

    if (!ret && 0 == strincmp(name,"ISO-8859-",9) &&
	0 < (i = atoi(name+9))) {
	DPRINT(Debug,7,(&Debug, "MIME_name_to_charset: ISO-8859-X charset, "));
	if (noautodef) {
	    DPRINT(Debug,7,(&Debug, "CHARSET_noautodef set\n"));
	} else if (auto_iso_8859) {
	    do_ascii = 1;
	    create = 1;
	    DPRINT(Debug,7,(&Debug, " autocreating (as subtype of builtin ASCII constant)\n"));
	} else {
	    DPRINT(Debug,7,(&Debug, "auto-iso-8859 is not set.\n"));
	}
    }

#ifdef USE_DLOPEN
    if (!ret && !noautodef &&
	cs_auto_charset(name,&cs_type,&cs_map)) {
	do_cs = 1;
	create = 1;
	DPRINT(Debug,7,(&Debug, 
			"MIME_name_to_charset: %s charset, autocreating\n",
			name));
    }
#endif

    if (!create) {
	DPRINT(Debug,12,(&Debug,"MIME_name_to_charset(%s), not found\n",
			 name));
	return NULL;
    }    

#ifdef USE_DLOPEN
 create_new:
#endif

    new.magic          = CS_charset_magic;
    new.flags          = SET_nodata|SET_valid;
    new.map_info       = NULL;
    new.MIME_name      = safe_strdup(name);

    if (do_cs) {
	new.map_info       = cs_map;
	new.charset_type   = cs_type;
	new.subtype        = NULL;
    } else if (do_ascii) {
	new.charset_type   = &cs_ascii;
	new.subtype        = ASCII_SET;
    } else {
	new.charset_type   = &cs_unknown;
	new.subtype        = NULL;
    }
    new.iso2022_info   = NULL;
    new.MIBenum        = MIBenum;
    new.codeset        = NULL;

    ret = add_set(new);

    DPRINT(Debug,12,(&Debug,"MIME_name_to_charset(%s), adding=%p\n",
		     name,ret));

    return ret;
}

char * dequote_opt(source,len)
     CONST char *source; 
     int len;
{
    int size = len + 1;
    int ptr = 0;
    char * store = safe_malloc(size);
    CONST char *p;
    int q = 0;
    
    for (p = source; p - source < len && ptr < size-1; p++) {
	switch(*p) {
	case '"':
	    q = !q;
	    break;
	case '\\':
	    if (q) {
		p++;
		if (*p != '\0')
		    store[ptr++] = *p;
		else {
		    lib_error(CATGETS(elm_msg_cat, MeSet, 
				      MeTrailingBackslash,
				      "Trailing backslash (\\): %.20s..."),
			      source);
		}
		break;
	    }
	    /* FALLTHRU */
	default:
	    if (q || *p != ' ')
		store[ptr++] = *p;
	    break;
	}    
    }
    store[ptr] = '\0';

    if (q)
	lib_error(CATGETS(elm_msg_cat, MeSet, 
			  MeUnbalancedQuote,
			  "Unbalanced quote (\"): %.20s..."),
		  source);


    DPRINT(Debug,20,(&Debug, 
		    "dequote_opt(%.*s (len=%d))=%s\n",
		    len,source,len,store));

    return store;
}

/* 0 == nothinh load, 1 == defination, 2 == alias only */
int load_charset_map_info(buffer,data)
     charset_t *buffer; 
     CONST char *data;
{
    char * temp = safe_strdup(data);
    char *c;
    char *store,*opt;
    struct charcode_info new_vals;
    int reallocation_needed = 0;
    char ** alias_vector = NULL;
    int alias_count      = 0;
    struct setlist     new_setlist;
    int         setcount          = 0;
    int         define_as_codeset = 0;
    int ret = 0;

    iso2022_clear_setlist(&new_setlist);

    if (0 == strncmp(data,"codeset=",8)) {
	store = NULL;

	*buffer = NULL;

	reallocation_needed   = 0;   /* codeset= code will set this */
	new_vals.magic        = CS_charset_magic;
	new_vals.charset_type = &cs_unknown;
	new_vals.map_info     = NULL;
	new_vals.flags        = SET_valid;
	new_vals.MIME_name    = NULL;
	new_vals.subtype      = NULL;
	new_vals.iso2022_info = NULL;

	new_vals.MIBenum        = 0;
	new_vals.codeset        = NULL;

	define_as_codeset = 1;

	c = temp;
	goto codeset_hook;
    }


    c = qstrpbrk(temp,";");
    if (!c) {
	*buffer = NULL;
	
	DPRINT(Debug,7,(&Debug, 
		     "load_charset_map_info(\"%s\") = 0 (FAIL)\n", 
		     data));
	free(temp);
	return 0;
    }
    store = dequote_opt(temp,c - temp);
    DPRINT(Debug,11,(&Debug, 
		 "load_charset_map_info: charset=%s\n", 
		 store));

    DPRINT(Debug,11,(&Debug, 
		 "load_charset_map_info: info=%s\n", c));

    *buffer = MIME_name_to_charset(store,0);
    if (!*buffer) {
	reallocation_needed   = 1;
	new_vals.magic        = CS_charset_magic;
	new_vals.charset_type = &cs_unknown;
	new_vals.map_info     = NULL;
	new_vals.flags        = SET_valid;
	new_vals.MIME_name    = safe_strdup(store);
	new_vals.subtype      = NULL;
	new_vals.iso2022_info = NULL;

	new_vals.MIBenum        = 0;
	new_vals.codeset        = NULL;

    } else 
	new_vals = **buffer;
    
    c++;

    if ('\0' == *c || 0 == strcmp(c,"!")) {
	DPRINT(Debug,9,(&Debug,"charset: %s: no data\n",store));
	new_vals.flags       |= SET_nodata;
    } else { 
	int i;
	char * WALK;
       
	if (0 <= (i = locate_charset_alias(store)) &&
	    charset_alias_list[i].set == *buffer) {
	    DPRINT(Debug,1,(&Debug,
			    "DANGER: Defining charset %s which is also alias of %s\n",
			    store,
			    charset_alias_list[i].set->MIME_name ?
			    charset_alias_list[i].set->MIME_name :
			    "<no MIME name>"));
	    alias_vector = safe_realloc(alias_vector,
					(alias_count+1) *
					sizeof (char *));
	    alias_vector[alias_count++] = safe_strdup(store);

	    lib_error(CATGETS(elm_msg_cat, MeSet,
			      MeTryingCharsetAlias,
			      "Trying redefine alias %s as charset"),
		      store);
	}

    codeset_hook:
	WALK = NULL;

	for (opt = mime_parse_content_opts(c, &WALK); 
	     opt; 
	     opt = mime_parse_content_opts(NULL, &WALK)) {
	    char * q = strchr(opt,'=');
	    
	    DPRINT(Debug,20,(&Debug, "mime_parse_content_opts gives: %s\n",
			     opt));

	    if (q) {
		char *val; 
		*q++ = '\0';
		val = dequote_opt(q,strlen(q));

		if (0 == strcmp(opt,"codeset")) {		
		    charset_t X = codeset_name_to_charset(val);

		    if (X && X != *buffer && !define_as_codeset) {
			lib_error(CATGETS(elm_msg_cat, MeSet, 
					  MeCodesetRedefined,
					  "Codeset %s redefined -- invalidating old charset %s"),
				  X->codeset ? X->codeset : val,
				  X->MIME_name ? X->MIME_name : val);
			X -> flags &=  ~SET_valid;
		    }
		    
		    if (define_as_codeset) {
			*buffer = X;
			if (X) {
			    new_vals = *X;
			    free(val); val = NULL;
			} else {
			    reallocation_needed   = 1;
			    new_vals.codeset = val; val = NULL;
			}
			define_as_codeset = 0;
		    } else {
			reallocation_needed   = 1;
			new_vals.codeset = val; val = NULL;
		    }
		} else if (0 == strcmp(opt,"MIBenum")) {
  
		    new_vals.MIBenum = atoi(val);

		    if (new_vals.MIBenum < 1) {
			lib_error(CATGETS(elm_msg_cat, MeSet, 
					  MeBadMIBenum,
					  "Bad MIBenum %s"),
				  val);						
			new_vals.MIBenum = 0;
		    }

		    free(val); val = NULL;

		} else if (0 == strcmp(opt,"MIME-subset")) {
		    reallocation_needed = 1;
		    new_vals.flags  &= ~SET_nodata;

		    new_vals.subtype  = MIME_name_to_charset(val,0);
		    if (!new_vals.subtype) {
			lib_error(CATGETS(elm_msg_cat, MeSet, 
					  MeUnknownMIMEsubset,
					  "Unknown MIME-subset %s"),
				  val);				  
		    } 
		    
		    if (0 == istrcmp(val,"US-ASCII") &&
			&cs_unknown == new_vals.charset_type)
			new_vals.charset_type = &cs_ascii;
		    if (0 == istrcmp(val,"INVARIANT") &&
			&cs_unknown == new_vals.charset_type)
			new_vals.charset_type = &cs_iso646;
		    free(val); val = NULL;

		} else if (0 == strcmp(opt,"map")) {
		    struct  map_info * r;
		    if (new_vals.charset_type == &cs_unknown) {
			lib_error(CATGETS(elm_msg_cat, MeSet,
                                          MeCharsetTypeBeforeMap,
                                          "Charset type must be given before map -parameter"));
		    }
		    r = new_vals.charset_type->find_map_type(val);
		    if (!r) {
			lib_error(CATGETS(elm_msg_cat, MeSet,
                                          MeUnknownMap,
					  "No map %s on charset type %s"),
				  val,new_vals.charset_type->type_name);
		    } else {
			reallocation_needed = 1;
			new_vals.flags     &= ~SET_nodata;
			new_vals.map_info   = r;
		    }
		    free(val); /* ?? */ val = NULL;

		} else if (0 == strcmp(opt,"type")) {
		    struct  charset_type * walk;

		    for (walk = cs_first_type; walk; walk = walk -> next_type)
			if (0 == strcmp(val,walk->type_name))
			    break;
#ifdef USE_DLOPEN
		    if (!walk)
			walk = find_shared_CS_type(val);
#endif		    
		    if (!walk) {
			lib_error(CATGETS(elm_msg_cat, MeSet,
                                          MeUnknownCharsetType,
                                          "Unknown charset type %s"),
                                  val);
			walk = &cs_unknown;
		    }	
		    reallocation_needed   = 1;
		    new_vals.flags       &= ~SET_nodata;
		    new_vals.charset_type = walk;

		    free(val); val = NULL;
		} else if (0 == strcmp(opt,"alias")) {

		    if (new_vals.codeset &&
			0 == istrcmp(new_vals.codeset,val)) {
			DPRINT(Debug,1,(&Debug,
					"charset: Ignoring alias=\"%s\" for codeset=\"%s\"\n",
					val,new_vals.codeset));
			free(val); val = NULL;

		    } else if (new_vals.MIME_name &&
			0 == istrcmp(new_vals.MIME_name,val)) {
			DPRINT(Debug,1,(&Debug,
					"charset: Ignoring alias=\"%s\" for charset %s\n",
					val,new_vals.MIME_name));
			free(val); val = NULL;

					
		    } else {
			int i = locate_charset_alias(val);
			charset_t p1;
			
			if (i < 0 &&
			    (p1 = codeset_name_to_charset(val))) {

			    lib_error(CATGETS(elm_msg_cat, MeSet,
					      MeTryingAliasCodeset,
					      "Trying redefine codeset %s as alias"),
				      p1->codeset ? p1->codeset : 
				      p1->MIME_name ? p1->MIME_name :
				      val);
			    free(val); val = NULL;
			} else {
			    alias_vector = safe_realloc(alias_vector,
							(alias_count+1) *
							sizeof (char *));
			    alias_vector[alias_count++] = val; val = NULL;
			}
		    }
		} else if (parse_iso2022_specification(opt,val,
						       &setcount,
						       &new_setlist)) {
		    /* none */
		    free(val); /* ?? */ val = NULL;
		} else {
		    lib_error(CATGETS(elm_msg_cat, MeSet, 
				      MeUnsupportedCharsetOption,
				      "Unsupported charset option %s (value %.20s...)"),
			      opt,val);				  
		    free(val); val = NULL;
		}
	    } else {
		lib_error(CATGETS(elm_msg_cat, MeSet, 
				  MeUnsupportedCharsetOptionNoValue,
				  "Unsupported charset option %s (no value)"),
			  opt);				  
	    }
	}
    }

    if (setcount > 0) {

	if (new_vals.iso2022_info)
	    iso2022_merge_old_info(&new_setlist,setcount,new_vals.iso2022_info);

	if (new_vals.charset_type->cs_iso2022_info_set_it(&new_vals,&new_setlist,
							  setcount)) {
	    reallocation_needed   = 1;
	    new_vals.flags       &= ~SET_nodata;

	} else
	    goto iso2022_fail; /* Discard bank defination */


	/* TODO: Other processing? */

    }
 iso2022_fail:

    if (reallocation_needed) {
	/* Reset incompatble map types ... */
	if (new_vals.map_info && 
	    new_vals.map_info->map_type != new_vals.charset_type) {
	    DPRINT(Debug,1,(&Debug,
		      "charset: Resetting incompatible map %s (type %s) from charset %s (type %s)\n",
		      new_vals.map_info->map_name,
		      new_vals.map_info->map_type->type_name,
		      new_vals.MIME_name ? 
		      new_vals.MIME_name : "<no MIME name>",
		      new_vals.charset_type->type_name));
		   new_vals.map_info = NULL;
	}
	
	if (!new_vals.map_info && &cs_onebyte == new_vals.charset_type) {
	    lib_error(CATGETS(elm_msg_cat, MeSet, MeNeedsMapParamater,
			      "Charset type %s requires map parameter"),
		      new_vals.charset_type->type_name);

	    if (new_vals.subtype == ASCII_SET) {
		DPRINT(Debug,1,(&Debug,
			  "charset: Charset %s: setting type to %s instead of %s\n",
			  (*buffer)->MIME_name ?
			  (*buffer)->MIME_name : "<no MIME name>",
			  cs_ascii.type_name,
			  new_vals.charset_type->type_name));
		new_vals.charset_type = &cs_ascii;
	    }
	    if (new_vals.subtype == INVARIANT_SET) {
		DPRINT(Debug,1,(&Debug,
			  "charset: Charset %s: setting type to %s instead of %s\n",
			  (*buffer)->MIME_name ?
			  (*buffer)->MIME_name : "<no MIME name>",
			  cs_iso646.type_name,
			  new_vals.charset_type->type_name));
		new_vals.charset_type = &cs_iso646;
	    }
	}

	if (*buffer) {
	    (*buffer) -> flags  &= ~SET_valid;
	    DPRINT(Debug,1,(&Debug,
		      "charset: Invalidating charset %s (and regenerating)\n",
		      (*buffer)->MIME_name ? 
		      (*buffer)->MIME_name : "<no MIME name>"));	
	}
	*buffer = add_set(new_vals);
	DPRINT(Debug,9,(&Debug,
			"charset: Adding "));

	if ((*buffer)->MIME_name) {
	    DPRINT(Debug,9,(&Debug,
			    "charset %s ",
			    (*buffer)->MIME_name));
	}
	if ((*buffer)->codeset) {
	    DPRINT(Debug,9,(&Debug,
			    "codeset %s ",
			    (*buffer)->codeset));
	}
	DPRINT(Debug,9,(&Debug,
			"(type %s) = %p\n",
			(*buffer)->charset_type->type_name,
			*buffer));
    }

    if (alias_vector) {
	int i;
	for (i = 0; i < alias_count; i++) {
	    int j =  locate_charset_alias(alias_vector[i]);
	    if (j < 0)
		j = add_charset_alias(alias_vector[i],*buffer);

	    /* In case of we redefine alias ... */
	    /* This is BUGGY on case of multiple aliases */ 
	    charset_alias_list[j].set->flags &= ~SET_havealias;

	    (*buffer) -> flags  |= SET_havealias;

	    DPRINT(Debug,9,(&Debug,
			    "load_charset_map_info: Setting %s as alias of %s\n",
			    charset_alias_list[j].name,
			    (*buffer)->MIME_name ?
			    (*buffer)->MIME_name : "<no MIME name>"));

	    charset_alias_list[j].set = *buffer;

	    free(alias_vector[i]);
	    alias_vector[i] = NULL;
	}
	free(alias_vector);
	
	ret = 2;
    }


    if (reallocation_needed)
	ret = 1;

    DPRINT(Debug,9,(&Debug,
		    "load_charset_map_info(\"%s\") = %d (SUCCEED)\n", 
		    data,ret));    
    free(temp);
    if (store)
	free(store);
    return ret;    
}



int charset_superset_of(charset,subset)
     charset_t charset,subset;
{
    int res = 0;
    int loop = 0;
    charset_t p;
    
    DPRINT(Debug,6,(&Debug, 
		    "charset_superset_of(%p (%s),%p (%s))\n",
	      charset,
	      charset->MIME_name ? charset->MIME_name :"<no MIME name>",
	      subset,subset->MIME_name ? subset->MIME_name :"<no MIME name>"));

    for (p = charset; p; p = p->subtype) {
	DPRINT(Debug,6,(&Debug, "   subset: %p (%s)\n",
			p,
			p->MIME_name ? p->MIME_name :"<no MIME name>"));
			

	if (p == subset) {
	    DPRINT(Debug,6,(&Debug, "   subset found\n"));
	    res = 1;
	}

	if (p->subtype == charset) {
	    DPRINT(Debug,6,(&Debug, "   loop found\n"));

	    lib_error(CATGETS(elm_msg_cat, MeSet, MeCharsetSubsetLoop,
			      "Charset subset defination loop detected for %s"),
		      charset->MIME_name ? charset->MIME_name : "<no MIME name>");
	    break;
	}



	if (loop++ > 20) {   
	    /* Catch other loop which do not go tgrough original set */
	    lib_error(CATGETS(elm_msg_cat, MeSet, MeCharsetSubsetLoop,
			      "Charset subset defination loop detected for %s"),
		      p->MIME_name ? p->MIME_name : "<no MIME name>");
	    break;
	}
    }


    DPRINT(Debug,6,(&Debug, 
		    "charset_superset_of=%d\n",
		    res));
    return res;
}

int charset_ok_p(ptr)
     charset_t ptr;
{
    int r;
    charset_t ascii_ptr = MIME_name_to_charset("US-ASCII",0);

    if (!ascii_ptr)
	panic("CHARSET PANIC",__FILE__,__LINE__,"charset_ok_p",
	      "US-ASCII not found",0);
    r = charset_superset_of(ptr,ascii_ptr);

    if (r &&  &cs_ascii != ptr->charset_type) {
	DPRINT(Debug,6,(&Debug, 
			"charset_ok_p: charset %p (%s) is superset of ascii %p (%s) but is not type cs_ascii (type = %p)\n",
			ptr,
			ptr->MIME_name ? ptr->MIME_name :"<no MIME name>",
			ascii_ptr,
			ascii_ptr->MIME_name ? ascii_ptr->MIME_name :"<no MIME name>",
			ptr->charset_type));
    }

    if (!r && &cs_ascii == ptr->charset_type) {    /* Hack !! */
	DPRINT(Debug,6,(&Debug, 
			"charset_ok_p: charset %p (%s) is not superset of ascii %p (%s) but is type cs_ascii (type = %p), returning OK\n",
			ptr,
			ptr->MIME_name ? ptr->MIME_name :"<no MIME name>",
			ascii_ptr,
			ascii_ptr->MIME_name ? ascii_ptr->MIME_name :"<no MIME name>"));
	r = 1;
    }

    return r;
}

struct charset_map_item  * load_charset_map(filename,errors) 
     CONST char *filename;
     int *errors;
{
    struct charset_map_item *result;
    int result_len = 0;
    FILE * f;
    int max_result = 0;
    int c,l1;
    char buf[LONG_STRING];
    int err = can_open(filename,"r");

    if (!array_initialized)
	add_builtin_sets();

    if(err) {
	DPRINT(Debug,2,(&Debug, 
			"load_charset_map: %s: %s (can_open)\n",
			filename,error_description(err)));
	return NULL;    
    }

    f = fopen(filename,"r");
    if (!f) {
	int err = errno;
	DPRINT(Debug,2,(&Debug, 
			"load_charset_map: %s: %s\n",
			filename,error_description(err)));
	return NULL;
    }

    while(EOF != (c = fgetc(f)))
	if ('\n' == c)
	    max_result++;

    DPRINT(Debug,11,(&Debug, 
		     "load_charset_map: %s, max_result=%d\n",
		     filename,max_result));

    if (!max_result) {
	fclose(f);
	return NULL;
    }
    rewind(f);

    result = safe_malloc((max_result +1) * sizeof (struct charset_map_item));

    while (result_len < max_result &&
	   (l1 = mail_gets(buf,sizeof buf, f)) > 0) {
	char * c = buf,*c1;
	
	if ('\n' == buf[l1 -1]) 
	    buf[l1 - 1] = '\0';
	else {
	    lib_error(CATGETS(elm_msg_cat, MeSet, MeTooLongLine,
			      "%.30s: Too long line: %.30s..."),
		      filename,buf);
	    (*errors) ++;
	    break;
	}
	l1--;

	while (l1-- > 0 && whitespace(buf[l1]))
	    buf[l1] = '\0';
	
	c = buf;
	while (*c && whitespace (*c)) /* skip leading whitespace */
	    c++;
	if ('#' == *c)
	    continue;
	if (!*c)
	    continue;

	c1 = strpbrk(c," \t");

	if (!c1) {
	    lib_error(CATGETS(elm_msg_cat, MeSet, MeBadLine,
			      "%.30s: Bad line: %.30s..."),
		      filename,buf);
	    (*errors) ++;
	    break;	    
	}
	*c1 = '\0';

	c1++;

	while (*c1 && whitespace (*c1)) /* skip leading whitespace */
	    c1++;
	if (!*c1) {
	    lib_error(CATGETS(elm_msg_cat, MeSet, MeBadLine,
			      "%.30s: Bad line: %.30s..."),
		      filename,buf);
	    (*errors) ++;
	    break;	    
	}

	result[result_len].match   = NULL;
	result[result_len].charset = NULL;
	result[result_len].is_def  = 0;

	if (0 == strcmp(c,"-")) {
	    result[result_len].match = NULL;
	    result[result_len].is_def = 
		load_charset_map_info(&result[result_len].charset,c1);

	    if (!result[result_len].is_def) {
		lib_error(CATGETS(elm_msg_cat, MeSet, MeNotCharsetDeclaration,
				  "%.30s: Not charset declaration: %.30s..."),
			  filename,c1);
		(*errors) ++;
		break;	    
	    }
	} else {
	    result[result_len].match = safe_strdup(c);

	    if (0 == strcmp(c1,"(unsupported)")) {
		/* Adding nameless unsupported charset */
		struct charcode_info new;
		charset_t ret;

		new.magic          = CS_charset_magic;
		new.flags          = 0;
		new.map_info       = NULL;
		new.MIME_name      = NULL;
		new.charset_type   = &cs_unknown;
		new.subtype        = NULL;
		new.iso2022_info   = NULL;

		new.MIBenum        = 0;
		new.codeset        = NULL;

		ret = add_set(new);

		DPRINT(Debug,11,(&Debug,"Adding (unsupported) =%p\n",
				 ret));

		result[result_len].charset = ret;
	    } else {
		result[result_len].is_def = 
		    load_charset_map_info(&result[result_len].charset,c1);

		if (! result[result_len].is_def  && 
		    ! result[result_len].charset)
		    result[result_len].charset = 
			MIME_name_to_charset(c1,CHARSET_create); 
	    
	    } 
	}
	DPRINT(Debug,11,(&Debug,"Added map[%d] %-15s   %-15s  (flags %X)\n",
			 result_len,
			 result[result_len].match ?
			 result[result_len].match : "- (define)",
			 result[result_len].charset->MIME_name ?
			 result[result_len].charset->MIME_name :
			 "(unsupported -- no MIME name)",
			 result[result_len].charset->flags));
	result_len++;


    }
    result[result_len].match   = NULL;
    result[result_len].charset = NULL;

    fclose(f);

    DPRINT(Debug,11,(&Debug,"load_charset_map: %s, result_len=%d\n",
	       filename,result_len));
    
    return result;
}


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


syntax highlighted by Code2HTML, v. 0.9.1