static char rcsid[] = "@(#)$Id: string.c,v 1.50.18.1 2007/08/25 06:35:47 hurtta Exp $";

/******************************************************************************
 *  The Elm (ME+) Mail System  -  $Revision: 1.50.18.1 $   $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

DEBUG_VAR(Debug,__FILE__,"charset");

static unsigned char *s2us P_((char *str));
static unsigned char *s2us(str) 
     char *str;
{
    return (unsigned char *)str;
}
static char *us2s P_((unsigned char *str));
static char *us2s(str) 
     unsigned char *str;
{
    return (char *)str;
}
static CONST char *cus2s P_((const unsigned char *str));
static CONST char *cus2s(str) 
     CONST unsigned char *str;
{
    return (CONST char *)str;
}

#define INVALID_MAP(cs) ((cs)->map_info && \
   (cs)->map_info->map_type != (cs)->charset_type)



int verify_string(str)
     struct string *str;
{
     
     if (!str) {
	 DPRINT(Debug,1,(&Debug,"verify_string=0:  FAILURE  str = NULL\n"));
	 return 0;
     }

     if (!str->string_type) {
	 DPRINT(Debug,1,(&Debug,
			 "verify_string=0:  FAILURE  str->string_type = NULL  (str=%p)\n",
			 str));
	  return 0;
     }

     if (CS_charset_magic != str->string_type->magic) {
	 DPRINT(Debug,1,(&Debug,
			 "verify_string=0:  FAILURE  Bad magic  (str=%p)\n",
			 str));
       return 0;
     }

     if (INVALID_MAP(str->string_type)) {
	 DPRINT(Debug,1,(&Debug,
			 "verify_string=0:  FAILURE  Bad map  (str=%p)\n",
			 str));
       return 0;
     }


     return 1;
}

static struct string * malloc_string P_((charset_t set));
static struct string * malloc_string(set) 
     charset_t set;
{
    struct string * res = safe_malloc(sizeof (struct string));

    /* defined in hdrs/defs.h */
    bzero((void *)res,sizeof (struct string));

     if (CS_charset_magic != set->magic) 
	panic("STRING PANIC",__FILE__,__LINE__,"malloc_string",
	      "Bad magic number (set)",0);

    if (INVALID_MAP(set)) 
	panic("STRING PANIC",__FILE__,__LINE__,"malloc_string",
	      "Bad map (set)",0);


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

    res->string_type = set;
    res->p = safe_malloc(sizeof (struct str_private_data));
    /* defined in hdrs/defs.h */
    bzero((void *)res->p,sizeof (struct str_private_data));
    res->p->magic             = CS_str_magic;
    res->p->len               = 0;
    res->p->state             = NULL;
    res->p->private_flag      = 0;     /* Mostly not used */
    res->p->lang              = NULL;

    res->string_type->charset_type->cs_init_it(res);
    return res;
}

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

struct string * new_string(set)
     charset_t set;
{
    struct string *res;

    DPRINT(Debug,60,(&Debug,"new_string(set=%p '%s')\n",
		     set,
		     set->MIME_name ? set->MIME_name : "<none>"));

    res = malloc_string(set);
    
    DPRINT(Debug,60,(&Debug,"new_string=%p  (type=%p '%s')\n",
		     res,
		     res->string_type->charset_type,
		     res->string_type->charset_type->type_name));

    return res;
}

struct string * new_langstring(set,lang)
     charset_t set; 
     char *lang;
{
    struct string *res;

    DPRINT(Debug,60,(&Debug,"new_langstring(set=%p '%s', lang=%s)\n",
		     set,
		     set->MIME_name ? set->MIME_name : "<none>",
		     lang));

    res = malloc_string(set);
    res->p->lang   = safe_strdup(lang);

    DPRINT(Debug,60,(&Debug,"new_langstring=%p  (type=%p '%s')\n",
		     res,
		     res->string_type->charset_type,
		     res->string_type->charset_type->type_name));

    return res;
}

struct string * new_string2 P_((charset_t set, 
				CONST unsigned char *data));
struct string * new_string2(set,data)
     charset_t set; 
     CONST unsigned char *data;
{
    struct string * res;
    int ERRORS = 0;

    DPRINT(Debug,60,(&Debug,
	       "new_string2: set=%p '%s'\n",
	       set,
	       set->MIME_name ? set->MIME_name : "<none>"));
    if (set == display_charset || set == system_charset) {
	DPRINT(Debug,61,(&Debug,
			 "new_string2 -- data=%s\n",data));
    }

    res = malloc_string(set);
    res->string_type->charset_type->
	cs_add_streambytes_to_it(res,
				 strlen(cus2s(data)),
				 data,&ERRORS);

    DPRINT(Debug,60,(&Debug,
		     "new_string2=%p  (type=%p '%s'): len=%d  ERRORS=%d\n",
		     res,
		     res->string_type->charset_type,
		     res->string_type->charset_type->type_name,
		     res->p->len,
		     ERRORS));
    return res;
}

void free_string(str)
     struct string **str;
{

    if (CS_charset_magic != (*str)->string_type->magic) 
	panic("STRING PANIC",__FILE__,__LINE__,"free_string",
	      "Bad magic number (string type)",0);

    if (INVALID_MAP((*str)->string_type))
	panic("STRING PANIC",__FILE__,__LINE__,"free_string",
	      "Bad map (string type)",0);


    DPRINT(Debug,60,(&Debug,"free_string(*str=%p) ('%s'; type=%p '%s')\n",
		     (*str),
		     (*str)->string_type->MIME_name ? 
		     (*str)->string_type->MIME_name : "<none>",
		     (*str)->string_type->charset_type,
		     (*str)->string_type->charset_type->type_name));
    
    if ((*str)->p->magic  != CS_str_magic)
	panic("STRING PANIC",__FILE__,__LINE__,"free_string",
	      "Bad magic number (str_private_data)",0);

    if ((*str)->p->state && 
	(*str)->p->state->charset != (*str)->string_type)
	panic("STRING PANIC",__FILE__,__LINE__,"free_string",
	      "Bad magic number (state charset)",0);

    (*str)->string_type->charset_type->cs_free_it(*str);

    if ((*str)->p->state) {
	free_state_internal(&((*str)->p->state));		
    }

    if ((*str)->p->lang) {
	free((*str)->p->lang);
	(*str)->p->lang = NULL;
    }

    free((*str)->p);
    (*str)->p = NULL;
    (*str)->string_type = NULL;

    free(*str);
    *str = NULL;
}

CONST char * get_string_lang(str)
     CONST struct string *str;
{
    if (CS_charset_magic != str->string_type->magic) 
	panic("STRING PANIC",__FILE__,__LINE__,"get_string_lang",
	    "Bad magic number (string type)",0);

    if (str->p->magic  != CS_str_magic)
	panic("STRING PANIC",__FILE__,__LINE__,"get_string_lang",
	      "Bad magic number (str_private_data)",0);

    return str->p->lang;
}

CONST char * get_string_MIME_name(str)
     CONST struct string *str;
{
    if (CS_charset_magic != str->string_type->magic) 
	panic("STRING PANIC",__FILE__,__LINE__,"get_string_MIME_name",
	      "Bad magic number (string type)",0);

    return str->string_type->MIME_name;
}

int add_streambyte_to_string(str,ch)
     struct string *str; 
     int ch;
{
    int r;

    if (CS_charset_magic != str->string_type->magic) 
	panic("STRING PANIC",__FILE__,__LINE__,"add_streambyte_to_string",
	      "Bad magic number (string type)",0);

    if (INVALID_MAP(str->string_type))
	panic("STRING PANIC",__FILE__,__LINE__,"add_streambyte_to_string",
	      "Bad map (string type)",0);


    if (str->p->magic  != CS_str_magic)
	panic("STRING PANIC",__FILE__,__LINE__,"add_streambyte_to_string",
	      "Bad magic number (str_private_data)",0);

    if (str->p->state && 
	str->p->state->charset != str->string_type)
	panic("STRING PANIC",__FILE__,__LINE__,"add_streambyte_to_string",
	      "Bad magic number (state charset)",0);

    DPRINT(Debug,60,(&Debug,
		     "add_streambyte_to_string: str=%p ('%s'; type=%p '%s')\n",
		     str,	       
		     str->string_type->MIME_name ? 
		     str->string_type->MIME_name : "<none>",
		     str->string_type->charset_type,
		     str->string_type->charset_type->type_name));

    if (str->string_type == display_charset || 
	str->string_type == system_charset) {
	DPRINT(Debug,61,(&Debug,
			 "add_streambyte_to_string -- ch=%d '%c'\n",ch,ch));    
    } else {
	DPRINT(Debug,61,(&Debug,
			 "add_streambyte_to_string -- ch=%d\n",ch));    
    }


    r = str->string_type->charset_type->cs_add_streambyte_to_it(str,ch);
    
    DPRINT(Debug,60,(&Debug,
		     "add_streambyte_to_string=%d\n",
		     r));

    return r;
}

int add_streambytes_to_string(str,count,data, errors)
     struct string *str; 
     int count; 
     CONST unsigned char *data;
     int *errors;
{
    int r;

    int ERRORS = 0;

    if (CS_charset_magic != str->string_type->magic) 
	panic("STRING PANIC",__FILE__,__LINE__,"add_streambytes_to_string",
	      "Bad magic number (string type)",0);

    if (INVALID_MAP(str->string_type))
	panic("STRING PANIC",__FILE__,__LINE__,"add_streambytes_to_string",
	      "Bad map (string type)",0);

    if (str->p->magic  != CS_str_magic)
	panic("STRING PANIC",__FILE__,__LINE__,"add_streambytes_to_string",
	      "Bad magic number (str_private_data)",0);

    if (str->p->state && 
	str->p->state->charset != str->string_type)
	panic("STRING PANIC",__FILE__,__LINE__,"add_streambytes_to_string",
	      "Bad magic number (state charset)",0);

    DPRINT(Debug,60,(&Debug,
		     "add_streambytes_to_string: str=%p, count=%d, data=%p ('%s'; type=%p '%s')\n",
		     str,count,data,	       
		     str->string_type->MIME_name ? 
		     str->string_type->MIME_name : "<none>",
		     str->string_type->charset_type,
		     str->string_type->charset_type->type_name));

    if (str->string_type == display_charset || 
	str->string_type == system_charset) {
	DPRINT(Debug,61,(&Debug,
			 "add_streambytes_to_string -- data='%.*s'\n",
			 count,data));    
    } 

    r = str->string_type->charset_type->cs_add_streambytes_to_it(str, count,data, &ERRORS);
    
    if (errors)
	*errors = ERRORS;

    DPRINT(Debug,60,(&Debug,"add_streambytes_to_string=%d ERRORS=%d\n",
		     r,ERRORS));

    return r;
}

static CONST unsigned char * csUs P_((const char *str));
static CONST unsigned char * csUs(str)
     CONST char *str;
{
    return (CONST unsigned char *)str;
}

static CONST char * cUss P_((const unsigned char *str));
static CONST char * cUss(str)
     CONST unsigned char *str;
{
    return (CONST char *)str;
}

static void add_name_to_string P_((struct string *str,
				   CONST unsigned char *ascii));

static void add_name_to_string(str,ascii)
     struct string *str; 
     CONST unsigned char *ascii;
{
    int l = strlen(cUss(ascii));
    int i;
    uint16 * vector = safe_malloc ( (l) * sizeof (uint16));

    /* Ascii and UNICODE have save codes ... */
    
    for (i = 0; i < l ; i++)
	/* ascii string is unsigned ... */
	if (ascii[i] < 128) 
	    vector[i] = ascii[i];
	else
	    vector[i] = '?';
    
    str->string_type->charset_type->cs_add_unicodedata_to_it(str,l,vector);
    free(vector);
}

void add_unicode_to_string(str,len,vector)
     struct string *str;
     int len;
     CONST uint16 *vector;
{
    if (CS_charset_magic != str->string_type->magic) 
	panic("STRING PANIC",__FILE__,__LINE__,"add_unicode_to_string",
	      "Bad magic number (string type)",0);

    if (INVALID_MAP(str->string_type))
	panic("STRING PANIC",__FILE__,__LINE__,"add_unicode_to_string",
	      "Bad map (string type)",0);

    if (str->p->magic  != CS_str_magic)
	panic("STRING PANIC",__FILE__,__LINE__,"add_unicode_to_string",
	      "Bad magic number (str_private_data)",0);

    if (len < 0)
	panic("STRING PANIC",__FILE__,__LINE__,"add_unicode_to_string",
	      "Bad vector len",0);

    DPRINT(Debug,60,(&Debug,
		     "add_unicode_to_string(%p,len=%d)  ('%s'; type=%p '%s')\n",
		     str,len,
		     str->string_type->MIME_name ? 
		     str->string_type->MIME_name : "<none>",
		     str->string_type->charset_type,
		     str->string_type->charset_type->type_name));

    str->string_type->charset_type->cs_add_unicodedata_to_it(str,len,vector);
    
    DPRINT(Debug,60,(&Debug,
		     "add_unicode_to_string: len = %d\n",str->p->len));	     
}

struct string * cat_strings(str1,str2,printind)
     CONST struct string *str1; 
     CONST struct string *str2;
     int printind;
{
    struct string *ret = NULL;

    if (CS_charset_magic != str1->string_type->magic) 
	panic("STRING PANIC",__FILE__,__LINE__,"cat_strings",
	      "Bad magic number (string type)",0);

    if (CS_charset_magic != str2->string_type->magic) 
	panic("STRING PANIC",__FILE__,__LINE__,"cat_strings",
	      "Bad magic number (string type)",0);

    if (INVALID_MAP(str1->string_type))
	panic("STRING PANIC",__FILE__,__LINE__,"cat_strings",
	      "Bad map (string type)",0);

    if (INVALID_MAP(str2->string_type))
	panic("STRING PANIC",__FILE__,__LINE__,"cat_strings",
	      "Bad map (string type)",0);


    if (str1->p->magic  != CS_str_magic)
	panic("STRING PANIC",__FILE__,__LINE__,"cat_strings",
	      "Bad magic number (str_private_data)",0);

    if (str2->p->magic  != CS_str_magic)
	panic("STRING PANIC",__FILE__,__LINE__,"cat_strings",
	      "Bad magic number (str_private_data)",0);

    DPRINT(Debug,60,(&Debug,
		     "cat_strings(%p,%p) ('%s'; type=%p '%s', len=%d), ('%s'; type=%p '%s', len=%d)\n",
	       str1,str2,
	       str1->string_type->MIME_name ? 
	       str1->string_type->MIME_name : "<none>",
	       str1->string_type->charset_type,
	       str1->string_type->charset_type->type_name,
	       str1->p->len,
	       str2->string_type->MIME_name ? 
	       str2->string_type->MIME_name : "<none>",
	       str2->string_type->charset_type,
	       str2->string_type->charset_type->type_name,
	       str2->p->len));

    /* Check lengths */
    str1->string_type->charset_type->cs_check_length_it(str1);
    str2->string_type->charset_type->cs_check_length_it(str2);

    /* Either one is empty, produce other,
       but not if other is not printable and printind is set.
     */
    if (str1->p->len == 0 &&
	(!printind || 
	 0 != (charset_properties(str2 -> string_type) & CS_mapping))) {
	ret = malloc_string(str2 -> string_type);
	ret->string_type->charset_type->cs_add_intdata_to_it(ret,str2);

	if (str2->p->lang) {
	    ret->p->lang = safe_strdup(str2->p->lang);
	}

    } else if (str2->p->len == 0 &&
	       (!printind || 
		0 != (charset_properties(str1 -> string_type) & CS_mapping))) {
	ret = malloc_string(str1 -> string_type);
	ret->string_type->charset_type->cs_add_intdata_to_it(ret,str1);

	if (str1->p->lang) {
	    ret->p->lang = safe_strdup(str1->p->lang);
	}

    } else if (str1 -> string_type == str2 -> string_type) {
	/* 1) Same CHARSET */

	ret = malloc_string(str1 -> string_type);

	ret->string_type->charset_type->cs_add_intdata_to_it(ret,str1);
	ret->string_type->charset_type->cs_add_intdata_to_it(ret,str2);

	if (str1->p->lang && str2->p->lang &&
	    0 == strcmp(str1->p->lang, str2->p->lang)) {
	    ret->p->lang = safe_strdup(str1->p->lang);
	}

    } else if (str1 -> string_type -> charset_type == 
	       str2 -> string_type -> charset_type &&
	       charset_superset_of(str1 -> string_type,str2 -> string_type)) {
	/* 2)  CHARSET of str1 is superset of CHARSET of str2 */

	ret = malloc_string(str1 -> string_type);

	ret->string_type->charset_type->cs_add_intdata_to_it(ret,str1);
	ret->string_type->charset_type->cs_add_intdata_to_it(ret,str2);

	if (str1->p->lang && str2->p->lang &&
	    0 == strcmp(str1->p->lang, str2->p->lang)) {
	    ret->p->lang = safe_strdup(str1->p->lang);
	}

    } else if (str1 -> string_type -> charset_type == 
	       str2 -> string_type -> charset_type &&
	       charset_superset_of(str2 -> string_type,str1 -> string_type)) {
	/* 3)  CHARSET of str2 is superset of CHARSET of str1 */

	ret = malloc_string(str2 -> string_type);

	ret->string_type->charset_type->cs_add_intdata_to_it(ret,str1);
	ret->string_type->charset_type->cs_add_intdata_to_it(ret,str2);
	
	if (str1->p->lang && str2->p->lang &&
	    0 == strcmp(str1->p->lang, str2->p->lang)) {
	    ret->p->lang = safe_strdup(str1->p->lang);
	}

    } else if (printind &&
	       0 != (charset_properties(str1 -> string_type) & CS_mapping) &&
	       0 == (charset_properties(str2 -> string_type) & CS_mapping) &&
	       str2 -> string_type -> MIME_name) {
	/* Add just str1 and indicate that 2 is not mappable */
	
	ret = malloc_string(str1 -> string_type);

	ret->string_type->charset_type->cs_add_intdata_to_it(ret,str1);
	add_name_to_string(ret,csUs("[?"));
	add_name_to_string(ret,csUs(str2 -> string_type -> MIME_name));
	add_name_to_string(ret,csUs("?]"));
    } else if (printind &&
	       0 != (charset_properties(str2 -> string_type) & CS_mapping) &&
	       0 == (charset_properties(str1 -> string_type) & CS_mapping) &&
	       str1 -> string_type -> MIME_name) {
	/* Add just str2 and indicate that 1 is not mappable */
	
	ret = malloc_string(str2 -> string_type);

	add_name_to_string(ret,csUs("[?"));
	add_name_to_string(ret,csUs(str2 -> string_type -> MIME_name));
	add_name_to_string(ret,csUs("?]"));
	ret->string_type->charset_type->cs_add_intdata_to_it(ret,str2);

    } else if (0 == str1->p->len && 0 == str2->p->len) {
	/* Avoid mallocing null vector */
	ret = malloc_string(display_charset);
    } else {
	/* 4) Unrelated CHARSETs -- so we try use display charset or UTF-8
	   instead 
	*/
	int i;
	int l = 0;
 	uint16 * vector = safe_malloc ( (str1->p->len + str2->p->len) *
					sizeof (uint16));

	charset_t target_set = MIME_name_to_charset("UTF-8",0);

	if (!target_set)
	    panic("CHARSET PANIC",__FILE__,__LINE__,"cat_strings",
		  "UTF-8 not found",0);

	/* If display charset is superset of charset of strings
	   use it 
	*/
	
	if (0 != (charset_properties(display_charset) & CS_mapping) &&
	    ( charset_superset_of(display_charset, str1 -> string_type) &&
	      charset_superset_of(display_charset, str2 -> string_type)
	      ||
	      0 != (charset_properties(display_charset) & CS_universal_set)
	      ))
	    target_set = display_charset;

	if (0 == (charset_properties(target_set) & CS_mapping)) {
	    DPRINT(Debug,5,(&Debug,
		       "cat_strings: selected charset do not have mapping info, using ASCII as target set\n"));
	    target_set = ASCII_SET;
	}

	ret = malloc_string(target_set);

	for (i = 0; i < str1->p->len; i++) {
	    int found;
	    uint16 c = 
		str1->string_type->charset_type->
		cs_give_unicode_from_it(str1,i,&found);
	    vector[l++] = c;
	}

	for (i = 0; i < str2->p->len; i++) {
	    int found;
	    uint16 c = 
		str2->string_type->charset_type->
		cs_give_unicode_from_it(str2,i,&found);
	    vector[l++] = c;
	}

	ret->string_type->charset_type->cs_add_unicodedata_to_it(ret,l,vector);
	free(vector);

	if (str1->p->lang && str2->p->lang &&
	    0 == strcmp(str1->p->lang, str2->p->lang)) {
	    ret->p->lang = safe_strdup(str1->p->lang);
	}

    }

    DPRINT(Debug,60,(&Debug,
		     "cat_strings=%p  ('%s'; type=%p '%s'): len=%d\n",
		     ret,
		     ret->string_type->MIME_name ? 
		     ret->string_type->MIME_name : "<none>",
		     ret->string_type->charset_type,
		     ret->string_type->charset_type->type_name,
		     ret->p->len));

    return ret;
}

int string_len(str)
     CONST struct string *str;
{
    if (CS_charset_magic != str->string_type->magic) 
	panic("STRING PANIC",__FILE__,__LINE__,"string_len",
	      "Bad magic number (string type)",0);

    if (INVALID_MAP(str->string_type))
	panic("STRING PANIC",__FILE__,__LINE__,"string_len",
	      "Bad map (string type)",0);


    if (str->p->magic  != CS_str_magic)
	panic("STRING PANIC",__FILE__,__LINE__,"string_len",
	      "Bad magic number (str_private_data)",0);

    /* Check length */
    str->string_type->charset_type->cs_check_length_it(str);

    DPRINT(Debug,60,(&Debug,"string_len(%p)=%d ('%s'; type=%p '%s')\n",
		     str,str->p->len,
		     str->string_type->MIME_name ? 
		     str->string_type->MIME_name : "<none>",
		     str->string_type->charset_type,
		     str->string_type->charset_type->type_name));
    return str->p->len;
}

int string_cmp(str1,str2,unknown_val)
     CONST struct string *str1; 
     CONST struct string *str2;
     int unknown_val;
{
    int ret = 0;
    int not_found = 0;
    int i;
    int str1_len;
    int str2_len;
    cs_give_unicode_from_string *str1_get_unicode;
    cs_give_unicode_from_string *str2_get_unicode;

    if (CS_charset_magic != str1->string_type->magic) 
	panic("STRING PANIC",__FILE__,__LINE__,"string_cmp",
	      "Bad magic number (string type)",0);

    if (CS_charset_magic != str2->string_type->magic) 
	panic("STRING PANIC",__FILE__,__LINE__,"string_cmp",
	      "Bad magic number (string type)",0);

    if (INVALID_MAP(str1->string_type))
	panic("STRING PANIC",__FILE__,__LINE__,"string_cmp",
	      "Bad map (string type)",0);

    if (INVALID_MAP(str2->string_type))
	panic("STRING PANIC",__FILE__,__LINE__,"string_cmp",
	      "Bad map (string type)",0);

    if (str1->p->magic  != CS_str_magic)
	panic("STRING PANIC",__FILE__,__LINE__,"string_cmp",
	      "Bad magic number (str_private_data)",0);

    if (str2->p->magic  != CS_str_magic)
	panic("STRING PANIC",__FILE__,__LINE__,"string_cmp",
	      "Bad magic number (str_private_data)",0);


    DPRINT(Debug,60,(&Debug,
		     "string_cmp(%p,%p,unknown_val=%d) ('%s'; type=%p '%s', len=%d), ('%s'; type=%p '%s', len=%d)\n",
		     str1,str2,unknown_val,
		     str1->string_type->MIME_name ? 
		     str1->string_type->MIME_name : "<none>",
		     str1->string_type->charset_type,
		     str1->string_type->charset_type->type_name,
		     str1->p->len,
		     str2->string_type->MIME_name ? 
		     str2->string_type->MIME_name : "<none>",
		     str2->string_type->charset_type,
		     str2->string_type->charset_type->type_name,
		     str2->p->len));

    /* Check lengths -- do compressions and so on */
    str1->string_type->charset_type->cs_check_length_it(str1);
    str2->string_type->charset_type->cs_check_length_it(str2);


    str1_len = str1->p->len;
    str2_len = str2->p->len;

    str1_get_unicode = str1->string_type->charset_type->cs_give_unicode_from_it;
    str2_get_unicode = str2->string_type->charset_type->cs_give_unicode_from_it;
    /* Make unicode compare first to make comparision to stable */

    for (i = 0; i < str1_len && i < str2_len; i++) {
	int found1,found2;
	uint16 c1 = str1_get_unicode(str1,i,&found1);
	uint16 c2 = str2_get_unicode(str2,i,&found2);

	if (!found1 || !found2) {
	    ret = 0;
	    not_found = 1;
	    break;
	} else if (c1 < c2) 
	    ret = -1;
	else if (c1 > c2)
	    ret = 1;

	if (ret != 0)	    
	    break;
    }

    DPRINT(Debug,61,(&Debug,
		     "string_cmp: i=%d, str1 len=%d, str2 len=%d, not_found=%d\n",
		     i,str1->p->len,str2->p->len,not_found));

    if (!not_found) {
	if (0 == ret && i < str1->p->len)
	    ret = 1;
	if (0 == ret && i < str2->p->len)
	    ret = -1;
	
	DPRINT(Debug,61,(&Debug,
		   "string_cmp: unicode cmp ret=%d\n",ret)); 
    }

    if (0 == ret && str1 -> string_type == str2 -> string_type) {
	ret = str1 -> string_type->charset_type->cs_cmp_it(str1,str2);
	DPRINT(Debug,61,(&Debug,
			 "string_cmp: cs_cmp_it ret=%d\n",ret)); 
	
	if (ret < 0)
	    ret = -1;
	else if (ret > 0)
	    ret = 1;

    } else if (0 == ret && not_found) {
	DPRINT(Debug,61,(&Debug,"string_cmp: Unable to compare values!\n"));
	ret = unknown_val;
    }

    DPRINT(Debug,60,(&Debug,"string_cmp=%d\n",ret));
    return ret;
}

struct string * dup_string(str)
     CONST struct string *str;
{
    struct string *ret;

    if (str->p->magic  != CS_str_magic)
	panic("STRING PANIC",__FILE__,__LINE__,"dup_string",
	      "Bad magic number (str_private_data)",0);
    
    DPRINT(Debug,60,(&Debug,"dup_string: str=%p ('%s'; type=%p '%s')\n",
		     str,
		     str->string_type->MIME_name ? 
		     str->string_type->MIME_name : "<none>",
		     str->string_type->charset_type,
		     str->string_type->charset_type->type_name));

    ret = malloc_string(str -> string_type);
    ret->string_type->charset_type->cs_add_intdata_to_it(ret,str);

    if (str->p->lang) {
	ret->p->lang = safe_strdup(str->p->lang);
    }


    DPRINT(Debug,60,(&Debug,"dup_string=%p ('%s'; type=%p '%s')\n",
		     ret,
		     ret->string_type->MIME_name ? 
		     ret->string_type->MIME_name : "<none>",
		     ret->string_type->charset_type,
		     ret->string_type->charset_type->type_name));

    return ret;
}

struct string * convert_string(set,str,printind)
     charset_t set;
     CONST struct string *str;
     int printind;
{
    struct string *ret = malloc_string(set);

    if (CS_charset_magic != str->string_type->magic) 
	panic("STRING PANIC",__FILE__,__LINE__,"convert_string",
	      "Bad magic number (string type)",0);

    if (INVALID_MAP(str->string_type))
	panic("STRING PANIC",__FILE__,__LINE__,"convert_string",
	      "Bad map (string type)",0);
	
    if (str->p->magic  != CS_str_magic)
	panic("STRING PANIC",__FILE__,__LINE__,"convert_string",
	      "Bad magic number (str_private_data)",0);

    DPRINT(Debug,60,(&Debug,
		     "convert_string(set=%p '%s',str=%p)  ('%s', type=%p '%s', len=%d)\n",
		     set,
		     set->MIME_name ? set->MIME_name : "<none>",
		     str,
		     str->string_type->MIME_name ? 
		     str->string_type->MIME_name : "<none>",
		     str->string_type->charset_type,
		     str->string_type->charset_type->type_name,
		     str->p->len));
    
    /* Check lengths -- do compressions and so on */
    str->string_type->charset_type->cs_check_length_it(str);

    if (0 == str->p->len) {
	/* 0) Empty string is always convertable
	      because nothing need to be added to result
	 */

    } else if (set == str->string_type) {
	/* 1) Same charset */
	ret->string_type->charset_type->cs_add_intdata_to_it(ret,str);

	if (str->p->lang) {
	    ret->p->lang = safe_strdup(str->p->lang);
	}


    } else if (printind &&
	       0 != (charset_properties(set) & CS_mapping) &&
	       0 == (charset_properties(str -> string_type) & CS_mapping) &&
	       str -> string_type -> MIME_name) {
	/* Just indicate that str is not mappable */
	
	add_name_to_string(ret,csUs("[?"));
	add_name_to_string(ret,csUs(str -> string_type -> MIME_name));
	add_name_to_string(ret,csUs("?]"));

    } else {
	/* 2) Different charset */
	int i;
	int l = 0;

	uint16 * vector = safe_malloc ( (str->p->len) * sizeof (uint16));
	for (i = 0; i < str->p->len; i++) {
	    int found;
	    uint16 c = 
		str->string_type->charset_type->
		cs_give_unicode_from_it(str,i,&found);
	    vector[l++] = c;
	}

	ret->string_type->charset_type->cs_add_unicodedata_to_it(ret,l,vector);
	free(vector);

	if (str->p->lang) {
	    ret->p->lang = safe_strdup(str->p->lang);
	}

    }

    DPRINT(Debug,60,(&Debug,
		     "convert_string=%p  ('%s'; type=%p '%s'): len=%d\n",
		     ret,
		     ret->string_type->MIME_name ? 
		     ret->string_type->MIME_name : "<none>",
		     ret->string_type->charset_type,
		     ret->string_type->charset_type->type_name,
		     ret->p->len));

    return ret;
}

unsigned char *stream_from_string(str, printable,terminal)
     CONST struct string *str;
     int printable;
     screen_info_p terminal; 
{
    int reslen;
    unsigned char * ret;
    char *ret0 = NULL;
    
    if (CS_charset_magic != str->string_type->magic) 
	panic("STRING PANIC",__FILE__,__LINE__,"stream_from_string",
	      "Bad magic number (string type)",0);

    if (INVALID_MAP(str->string_type))
	panic("STRING PANIC",__FILE__,__LINE__,"stream_from_string",
	      "Bad map (string type)",0);

    if (str->p->magic  != CS_str_magic)
	panic("STRING PANIC",__FILE__,__LINE__,"stream_from_string",
	      "Bad magic number (str_private_data)",0);
    
    DPRINT(Debug,60,(&Debug,"stream_from_string: ret=%p ('%s'; type=%p '%s')\n",
		     str,
		     str->string_type->MIME_name ? 
		     str->string_type->MIME_name : "<none>",
		     str->string_type->charset_type,
		     str->string_type->charset_type->type_name));

    if (terminal && str->string_type->iso2022_info) 
	ret0 = terminal_charset_post_init(terminal,
					  str->string_type->iso2022_info);


    /* Catenate order is ret == ret0 + ret */
    	    
    ret = str->string_type->charset_type->cs_stream_from_it(str,
							    printable,
							    terminal,
							    &reslen);
    DPRINT(Debug,60,(&Debug,
		     "stream_from_string -- reslen=%d\n",reslen));

    if (str->string_type == display_charset) {
	DPRINT(Debug,61,(&Debug,
			 "stream_from_string -- ret='%s'%s\n",
			 ret,
			 ret0 ? " (without post init)" : ""));
    }


    if (ret0) {
	int i;
	DPRINT(Debug,8,(&Debug,"Post init terminal charset %s (",
			str->string_type->MIME_name ? 
			str->string_type->MIME_name : 
			"<no MIME name>"));
	for (i = 0; ret0[i]; i++) {
	    DPRINT(Debug,8,(&Debug,"%s%02X",
			    i ? " " : "", ret0[i]));
	}
	DPRINT(Debug,8,(&Debug,")\n"));

	/* Catenate order is ret == ret0 + ret */

	ret0 = strmcat(ret0,us2s(ret));
	free(ret);
	ret = s2us(ret0);
    }

    DPRINT(Debug,60,(&Debug,"stream_from_string=%p\n",
	       ret));

    return ret;
}

/* result is malloced */
void bytestream_from_string(str,res,reslen)
     CONST struct string *str;
     char **res;
     int *reslen;
{
    if (CS_charset_magic != str->string_type->magic) 
	panic("STRING PANIC",__FILE__,__LINE__,"bytestream_from_string",
	      "Bad magic number (string type)",0);

    if (INVALID_MAP(str->string_type))
	panic("STRING PANIC",__FILE__,__LINE__,"bytestream_from_string",
	      "Bad map (string type)",0);
	
    if (str->p->magic  != CS_str_magic)
	panic("STRING PANIC",__FILE__,__LINE__,"bytestream_from_string",
	      "Bad magic number (str_private_data)",0);
    
    DPRINT(Debug,60,(&Debug,
		     "bytestream_from_string: str=%p ('%s'; type=%p '%s')\n",
		     str,
		     str->string_type->MIME_name ? 
		     str->string_type->MIME_name : "<none>",
		     str->string_type->charset_type,
		     str->string_type->charset_type->type_name));


    *res = us2s(str->string_type->charset_type->cs_stream_from_it(str,0,NULL,
								  reslen));

    DPRINT(Debug,60,(&Debug,
		     "bytestream_from_string: *res=%p, reslen=%d\n",
		     *res,reslen));
    
}


unsigned char *streamclip_from_string(str,pos,len,terminal,printable_len)
     CONST struct string *str;
     int *pos; 
     int len; 
     screen_info_p terminal;   
     struct cs_printable_len *printable_len;
{
    int p = *pos;
    unsigned char * ret;
    char *ret0 = NULL;

    if (CS_charset_magic != str->string_type->magic) 
	panic("STRING PANIC",__FILE__,__LINE__,"streamclip_from_string",
	      "Bad magic number (string type)",0);

    if (INVALID_MAP(str->string_type))
	panic("STRING PANIC",__FILE__,__LINE__,"streamclip_from_string",
	      "Bad map (string type)",0);

    if (str->p->magic  != CS_str_magic)
	panic("STRING PANIC",__FILE__,__LINE__,"streamclip_from_string",
	      "Bad magic number (str_private_data)",0);
	
    if (printable_len && !terminal)
	panic("STRING PANIC",__FILE__,__LINE__,"streamclip_from_string",
	      "printable_len argument requires terminal argument",0);
	
    if (printable_len && 
	0 == ( CS_printable_len & str->string_type->charset_type->cs_it_properties(str->string_type)))
	panic("STRING PANIC",__FILE__,__LINE__,"streamclip_from_string",
	      "Charset does not support printable_len argument",0);


    if (terminal && str->string_type->iso2022_info) 
	ret0 = terminal_charset_post_init(terminal,
					  str->string_type->iso2022_info);

    /* Catenate order is ret == ret0 + ret */
     
    DPRINT(Debug,60,(&Debug,
		     "streamclip_from_string(%p,%d,%d)    ('%s'; type=%p '%s')\n",
		     str,p,len,
		     str->string_type->MIME_name ? 
		     str->string_type->MIME_name : "<none>",
		     str->string_type->charset_type,
		     str->string_type->charset_type->type_name));
    
    ret = 
	str->string_type->charset_type->cs_streamclip_from_it(str,pos,len,
							      terminal,
							      printable_len);

    if (str->string_type == display_charset) {
	DPRINT(Debug,61,(&Debug,
			 "streamclip_from_string -- ret='%s'%s\n",
			 ret,
			 ret0 ? " (without post init)" : ""));
    }

    if (ret0) {
	int i;
	DPRINT(Debug,8,(&Debug,
			"Post init terminal charset %s (",
			str->string_type->MIME_name ? 
			str->string_type->MIME_name : 
			"<no MIME name>"));
	for (i = 0; ret0[i]; i++) {
	    DPRINT(Debug,8,(&Debug,"%s%02X",
			    i ? " " : "", ret0[i]));
	}
	DPRINT(Debug,8,(&Debug,")\n"));

	/* Catenate order is ret == ret0 + ret */

	ret0 = strmcat(ret0,us2s(ret));
	free(ret);
	ret = s2us(ret0);
    }
    
    DPRINT(Debug,60,(&Debug,
		     "streamclip_from_string [%d(..%d)] =%p ('%s'; type=%p '%s')",
		     p,*pos,ret,
		     str->string_type->MIME_name ? 
		     str->string_type->MIME_name : "<none>",
		     str->string_type->charset_type,
		     str->string_type->charset_type->type_name));
    
    if (printable_len) {
	DPRINT(Debug,60,(&Debug,
			 " printable_len: max_len=%d ret_len=%d",
			 printable_len->max_len,
			 printable_len->ret_len));
    }
    DPRINT(Debug,60,(&Debug,"\n"));

    return ret;
}

int estimate_clip_string(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;
{
    int ret;

    if (CS_charset_magic != str->string_type->magic) 
	panic("STRING PANIC",__FILE__,__LINE__,"estimate_clip_string",
	      "Bad magic number (string type)",0);
    
    if (INVALID_MAP(str->string_type))
	panic("STRING PANIC",__FILE__,__LINE__,"estimate_clip_string",
	      "Bad map (string type)",0);


    if (str->p->magic  != CS_str_magic)
	panic("STRING PANIC",__FILE__,__LINE__,"estimate_clip_string",
	      "Bad magic number (str_private_data)",0);


    if (0 == ( CS_printable_len & 
	       str->string_type->charset_type->cs_it_properties(str->string_type)))
	panic("STRING PANIC",__FILE__,__LINE__,"estimate_clip_string",
	      "Charset does not support function",0);

    if (!printable_len || !terminal)
	panic("STRING PANIC",__FILE__,__LINE__,"estimate_clip_string",
	      "printable_len and terminal required",0);

    
    ret = 
	str->string_type->charset_type->cs_estimate_clip_it(str,pos,len,
							    terminal,
							    printable_len);

    
    DPRINT(Debug,60,(&Debug,
		     "estimate_clip_it(%p,%d,%d,...) =%d ('%s'; type=%p '%s')",
		     str,pos,len,ret,
		     str->string_type->MIME_name ? 
		     str->string_type->MIME_name : "<none>",
		     str->string_type->charset_type,
		     str->string_type->charset_type->type_name));

    DPRINT(Debug,60,(&Debug,
		     " printable_len: max_len=%d ret_len=%d\n",
		     printable_len->max_len,
		     printable_len->ret_len));
    
    return  ret;
}

struct string *clip_from_string(str,pos,len)
     CONST struct string *str;
     int *pos; 
     int len;
{
    struct string *ret = malloc_string(str->string_type);
    
    if (str->p->magic  != CS_str_magic)
	panic("STRING PANIC",__FILE__,__LINE__,"clip_from_string",
	      "Bad magic number (str_private_data)",0);

    DPRINT(Debug,60,(&Debug,
		     "clip_from_string(%p,%d,%d)  ('%s'; type=%p '%s')\n",
		     str,*pos,len,
		     str->string_type->MIME_name ? 
		     str->string_type->MIME_name : "<none>",
		     str->string_type->charset_type,
		     str->string_type->charset_type->type_name));

    /* It is assumed that compressions are done
       .. ie string_length() function
       is called 
    */

    str->string_type->charset_type->cs_clip_from_it(ret,str,pos,len);

    DPRINT(Debug,60,(&Debug,
		     "clip_from_string=%p  ('%s'; type=%p '%s'): len=%d\n",
		     ret,
		     ret->string_type->MIME_name ? 
		     ret->string_type->MIME_name : "<none>",
		     ret->string_type->charset_type,
		     ret->string_type->charset_type->type_name,
		     ret->p->len));
    
    return ret;
}

struct string *ascify_string(str)
     CONST struct string *str;
{
    int can_ascii;
    charset_t ascii_ptr = MIME_name_to_charset("US-ASCII",0);
    struct string *ret;

    if (!ascii_ptr)
	panic("STRING PANIC",__FILE__,__LINE__,"ascify_string",
	      "US-ASCII not found",0);

    if (CS_charset_magic != str->string_type->magic) 
	panic("STRING PANIC",__FILE__,__LINE__,"ascify_string",
	      "Bad magic number (string type)",0);

    if (INVALID_MAP(str->string_type))
	panic("STRING PANIC",__FILE__,__LINE__,"ascify_string",
	      "Bad map (string type)",0);


    if (str->p->magic  != CS_str_magic)
	panic("STRING PANIC",__FILE__,__LINE__,"ascify_string",
	      "Bad magic number (str_private_data)",0);

        DPRINT(Debug,60,(&Debug,
			 "ascify_string(%p) ('%s'; type=%p '%s', len=%d)\n",
			 str,
			 str->string_type->MIME_name ? 
			 str->string_type->MIME_name : "<none>",
			 str->string_type->charset_type,
			 str->string_type->charset_type->type_name,
			 str->p->len));

    /* Check lengths -- do compressions and so on */
    str->string_type->charset_type->cs_check_length_it(str);

    can_ascii = str->string_type->charset_type->cs_can_ascii_it(str);

    if (can_ascii) {
	int i;
	int l = 0;
	uint16 * vector = safe_malloc ( (str->p->len) * sizeof (uint16));

	ret = malloc_string(ascii_ptr);
	

	for (i = 0; i < str->p->len; i++) {
	    int found;
	    uint16 c = 
		str->string_type->charset_type->
		cs_give_unicode_from_it(str,i,&found);
	    vector[l++] = c;
	}
	
	ret->string_type->charset_type->cs_add_unicodedata_to_it(ret,l,vector);
	free(vector);
    } else {
	ret = malloc_string(str -> string_type);
	ret->string_type->charset_type->cs_add_intdata_to_it(ret,str);   
    }

    if (str->p->lang) {
	ret->p->lang = safe_strdup(str->p->lang);
    }

    DPRINT(Debug,60,(&Debug,
		     "ascify_string=%p  ('%s'; type=%p '%s'): len=%d\n",
		     ret,
		     ret->string_type->MIME_name ? 
		     ret->string_type->MIME_name : "<none>",
		     ret->string_type->charset_type,
		     ret->string_type->charset_type->type_name,
		     ret->p->len));

    return ret;
}

int can_ascii_string(str)
     CONST struct string *str;
{
    int ret;
    
    if (CS_charset_magic != str->string_type->magic) 
	panic("STRING PANIC",__FILE__,__LINE__,"can_ascii_string",
	      "Bad magic number (string type)",0);

    if (INVALID_MAP(str->string_type))    
	panic("STRING PANIC",__FILE__,__LINE__,"can_ascii_string",
	      "Bad map (string type)",0);

    if (str->p->magic  != CS_str_magic)
	panic("STRING PANIC",__FILE__,__LINE__,"can_ascii_string",
	      "Bad magic number (str_private_data)",0);
    
    ret= str->string_type->charset_type->cs_can_ascii_it(str);
    
    DPRINT(Debug,60,(&Debug,
		     "can_ascii_string(%p)=%d ('%s'; type=%p '%s', len=%d)\n",
		     str,ret,
		     str->string_type->MIME_name ? 
		     str->string_type->MIME_name : "<none>",
		     str->string_type->charset_type,
		     str->string_type->charset_type->type_name,
		     str->p->len));
    
    return ret;
}

void add_ascii_to_string(str,ascii)
     struct string *str; 
     CONST unsigned char *ascii;
{

    if (CS_charset_magic != str->string_type->magic) 
	panic("STRING PANIC",__FILE__,__LINE__,"add_ascii_to_string",
	      "Bad magic number (string type)",0);

    if (INVALID_MAP(str->string_type))    
	panic("STRING PANIC",__FILE__,__LINE__,"add_ascii_to_string",
	      "Bad map (string type)",0);

    if (str->p->magic  != CS_str_magic)
	panic("STRING PANIC",__FILE__,__LINE__,"add_ascii_to_string",
	      "Bad magic number (str_private_data)",0);

    DPRINT(Debug,60,(&Debug,
		     "add_ascii_to_string(%p)  ('%s'; type=%p '%s')\n",
		     str,
		     str->string_type->MIME_name ? 
		     str->string_type->MIME_name : "<none>",
		     str->string_type->charset_type,
		     str->string_type->charset_type->type_name));
    
    DPRINT(Debug,61,(&Debug,
		     "add_ascii_to_string -- ascii=%s\n",ascii));

    add_name_to_string(str,ascii);

    DPRINT(Debug,60,(&Debug,
		     "add_ascii_to_string: len = %d\n",str->p->len));	   
}

void fill_ascii_to_string(str,count,ascii)
     struct string *str; 
     int count; 
     unsigned int ascii;
{
    int i;
    uint16 * vector;

    if (CS_charset_magic != str->string_type->magic) 
	panic("STRING PANIC",__FILE__,__LINE__,"fill_ascii_to_string",
	      "Bad magic number (string type)",0);

    if (INVALID_MAP(str->string_type))    
	panic("STRING PANIC",__FILE__,__LINE__,"fill_ascii_to_string",
	      "Bad map (string type)",0);

    if (count < 1)
	panic("STRING PANIC",__FILE__,__LINE__,"fill_ascii_to_string",
	      "bad count",0);

    if (str->p->magic  != CS_str_magic)
	panic("STRING PANIC",__FILE__,__LINE__,"fill_ascii_to_string",
	      "Bad magic number (str_private_data)",0);

    if (str->p->state) {
	if (str->p->state->charset != str->string_type)
	    panic("STRING PANIC",__FILE__,__LINE__,"fill_ascii_to_string",
		  "Bad magic number (state charset)",0);
	free_state_internal(&(str->p->state));
    }

    vector = safe_malloc ( count * sizeof (uint16));

    /* Ascii and UNICODE have save codes ... */
    
    for (i = 0; i < count ; i++)
	/* ascii is unsigned ... */
	if (ascii < 128) 
	    vector[i] = ascii;
	else
	    vector[i] = '?';  /* XXX */


    str->string_type->charset_type->cs_add_unicodedata_to_it(str,count,vector);
    free(vector);

    DPRINT(Debug,60,(&Debug,
		     "fill_ascii_to_string(%p,%d,%d)  ('%s'; type=%p '%s'): len=%d\n",
		     str,count,ascii,
		     str->string_type->MIME_name ? 
		     str->string_type->MIME_name : "<none>",
		     str->string_type->charset_type,
		     str->string_type->charset_type->type_name,
		     str->p->len));
}

struct string *collapse_spaces_from_string(str)
     const struct string *str;
{
    struct  charset_type  * cstype;

    struct string *ret;
    int i, j = 0;

    if (CS_charset_magic != str->string_type->magic) 
	panic("STRING PANIC",__FILE__,__LINE__,"collapse_spaces_from_string",
	      "Bad magic number (string type)",0);

    cstype = str->string_type->charset_type;

    if (str->p->magic  != CS_str_magic)
	panic("STRING PANIC",__FILE__,__LINE__,"collapse_spaces_from_string",
	      "Bad magic number (str_private_data)",0);


    DPRINT(Debug,60,(&Debug,
		     "collapse_spaces_from_string(%p) ('%s'; type=%p '%s', len=%d)\n",
		     str,
		     str->string_type->MIME_name ? 
		     str->string_type->MIME_name : "<none>",
		     cstype,
		     cstype->type_name,
		     str->p->len));


    /* Check lengths -- do compressions and so on */
    str->string_type->charset_type->cs_check_length_it(str);

    ret = malloc_string(str->string_type);

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

	int is_space = 0;
	int POS;
	int len;

	/* Scan to next space */
	for (j = i; j < str->p->len; j++) {

	    int found;
	    
	    uint16 c = cstype->cs_give_unicode_from_it(str,j,&found);
	    	    
	    if (!found) {
		DPRINT(Debug,61,(&Debug,
				 "collapse_spaces_from_string -- failing at %d -- no unicode\n",
				 i));
	    } else
		is_space = unicode_ch(c,UOP_space);

	    if (is_space)
		break;

	}

	POS = i;
	len = j-i;	
	
	if (len > 0) {
	    struct string *tmp = malloc_string(str->string_type);

	    DPRINT(Debug,61,(&Debug,
			     "collapse_spaces_from_string: clipping no space from %d -- len %d\n",
			     POS,len));
	    
	    cstype->cs_clip_from_it(tmp,str,&POS,len);
	    cstype->cs_add_intdata_to_it(ret,tmp);
	    free_string(&tmp);
	    
	}

	/* Skip over space */
	if (is_space) {
	    uint16 SPACE_val = 0x0020;

	    cstype->cs_add_unicodedata_to_it(ret,1,&SPACE_val);

	    for (; j < str->p->len; j++) {
		
		int found;
		
		uint16 c = cstype->cs_give_unicode_from_it(str,j,&found);
		
		if (!found) {
		    DPRINT(Debug,61,(&Debug,
				 "collapse_spaces_from_string -- failing at %d -- no unicode\n",
				     j));
		} else
		    is_space = unicode_ch(c,UOP_space);
		
		if (!is_space)
		    break;
	    }			       		
	}
    }


    DPRINT(Debug,60,(&Debug,
		     "collapse_spaces_from_string=%p  ('%s'; type=%p '%s'): len=%d\n",
		     ret,
		     ret->string_type->MIME_name ? 
		     ret->string_type->MIME_name : "<none>",
		     ret->string_type->charset_type,
		     ret->string_type->charset_type->type_name,
		     ret->p->len));
    
    return ret;
}




struct string *skip_ascii_head_from_string(str,ascii,ignore_case)
     CONST struct string *str;
     CONST unsigned char *ascii;
     int ignore_case;
{
    struct string *ret = malloc_string(str->string_type);
    int X = 0;
    int x;

    if (CS_charset_magic != str->string_type->magic) 
	panic("STRING PANIC",__FILE__,__LINE__,"skip_ascii_head_from_string",
	      "Bad magic number (string type)",0);

    if (INVALID_MAP(str->string_type))    
	panic("STRING PANIC",__FILE__,__LINE__,"skip_ascii_head_from_string",
	      "Bad map (string type)",0);

    if (str->p->magic  != CS_str_magic)
	panic("STRING PANIC",__FILE__,__LINE__,"skip_ascii_head_from_string",
	      "Bad magic number (str_private_data)",0);

    DPRINT(Debug,60,(&Debug,
		     "skip_ascii_head_from_string(%p) ('%s'; type=%p '%s', len=%d)\n",
		     str,
		     str->string_type->MIME_name ? 
		     str->string_type->MIME_name : "<none>",
		     str->string_type->charset_type,
		     str->string_type->charset_type->type_name,
		     str->p->len));
    DPRINT(Debug,60,(&Debug,
		     "skip_ascii_head_from_string -- ascii=%s\n",ascii));
    
    /* Check lengths -- do compressions and so on */
    str->string_type->charset_type->cs_check_length_it(str);

    while(1) {
	int i;
	CONST unsigned char * p = ascii;

	DPRINT(Debug,61,(&Debug,
			 "skip_ascii_head_from_string -- starting pos %d\n",
			 X));


	for (i = X;  
	     /* ascii string is unsigned ... */
	     *p && *p < 128 && i < str->p->len; 
	     i++, p++) {

	    int found;
	    uint16 c =
		str->string_type->charset_type->
		cs_give_unicode_from_it(str,i,&found);
	    unsigned char c1 = *p;

	    if (!found) {
		DPRINT(Debug,61,(&Debug,
				 "skip_ascii_head_from_string -- failing at %d -- no unicode\n",
				 i));
		break;
	    }
	    if (ignore_case) {
		c = unicode_ch(c,UOP_lowercase);
		if (!c) {	
		    DPRINT(Debug,61,(&Debug,
				     "skip_ascii_head_from_string -- failing at %d -- no lowercasing\n",
				     i));
		    break;
		}
	    }

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

	    if (c1 != c) {
		DPRINT(Debug,61,(&Debug,
				 "skip_ascii_head_from_string -- failing at %d -- mismatch %02X (%c) <> %04X\n",
				 i,c1,c1,c));
		break;
	    }
	}
	if (*p) {
	    DPRINT(Debug,61,(&Debug,
			     "skip_ascii_head_from_string -- hit end at %d\n",
			     i));
	    break;
	}
       
	/* Try next sequence */
	X = i;
	DPRINT(Debug,61,(&Debug,
			 "skip_ascii_head_from_string --  got %d\n",
			 X));
    }	    

    x = X;
    str->string_type->charset_type->cs_clip_from_it(ret,str,&X,str->p->len);

    DPRINT(Debug,60,(&Debug,
		     "skip_ascii_head_from_string=%p  ('%s'; type=%p '%s'): len=%d (clip=%d..%d)\n",
		     ret,
		     ret->string_type->MIME_name ? 
		     ret->string_type->MIME_name : "<none>",
		     ret->string_type->charset_type,
		     ret->string_type->charset_type->type_name,
		     ret->p->len,x,X));

    return ret;
}

/* WARNING:  get_word_from_string does everything via Unicode
 *           may lose information on some situations
 */
             
int get_word_from_string(str,word,pos,flags,ascii_delim, found_delim)
     CONST struct string *str;
     struct string **word;
     int *pos;
     int flags;
     CONST unsigned char *ascii_delim;
     uint16 *found_delim;
{
    int lowercase = flags & GWF_lowercase;
    int trim      = flags & GWF_trim_space;
    
    int X = *pos;
    int last_X;
    int X1;

    uint16 *data = NULL;
    int dx = 0;
    int ret = 0;

    if (CS_charset_magic != str->string_type->magic) 
	panic("STRING PANIC",__FILE__,__LINE__,"get_word_from_string",
	      "Bad magic number (string type)",0);

    if (INVALID_MAP(str->string_type))    
	panic("STRING PANIC",__FILE__,__LINE__,"get_word_from_string",
	      "Bad map (string type)",0);

    if (str->p->magic  != CS_str_magic)
	panic("STRING PANIC",__FILE__,__LINE__,"get_word_from_string",
	      "Bad magic number (str_private_data)",0);

    *word = malloc_string(str->string_type);

    DPRINT(Debug,60,(&Debug,
		     "get_word_from_string(%p) ('%s'; type=%p '%s', len=%d), *pos=%d\n",
		     str,
		     str->string_type->MIME_name ? 
		     str->string_type->MIME_name : "<none>",
		     str->string_type->charset_type,
		     str->string_type->charset_type->type_name,
		     str->p->len,
		     *pos));

    if (found_delim)
	*found_delim = 0;

    /* Check lengths -- do compressions and so on */
    str->string_type->charset_type->cs_check_length_it(str);

    if (str->p->len < 1)
	goto fail;

    /* SKIP delim */
    while (X < str->p->len) {
	int found;
	int sp;
       
	uint16 c = str->string_type->charset_type->
	    cs_give_unicode_from_it(str,X,&found);

	/* ascii string is unsigned ... */
	CONST unsigned char *y = NULL;

	if (!found) {
	    DPRINT(Debug,57,(&Debug,
			     "get_word_from_string -- failing at %d -- no unicode\n",
			     X));

	    break;
	}

	/* Is space?    (returns 0 or original character) */
	sp = unicode_ch(c,UOP_space);
	if (sp) {
	    if (trim) {
		X++;
		continue;
	    }
		
	    c = 0x0020;    /* Treat all unicode space characters as ascii space */
	}

	for (y = ascii_delim; *y; y++)
	    if (*y == c || *y == sp)
		break;
	    
	/* Delim not found ? */
	if (! *y)
	    break;

	X++;
    }

    X1 = X;    
    data = safe_malloc(str->p->len * sizeof (data[0]));
    dx = 0;

    last_X = 0;
    /* Read first word */
    while (X < str->p->len) {
	int found;
	uint16 c = str->string_type->charset_type->
	    cs_give_unicode_from_it(str,X,&found);

	int sp;
	/* ascii string is unsigned ... */
	CONST unsigned char *y = NULL;

	if (!found) {
	    DPRINT(Debug,57,(&Debug,
			     "get_word_from_string -- failing at %d -- no unicode\n",
			     X));

	    ret = 0;
	    goto fail;
	}

	/* Is space?    (returns 0 or original character) */
	sp = unicode_ch(c,UOP_space);
	if (sp)
	    c = 0x0020;    /* Treat all unicode space characters as ascii space */

	for (y = ascii_delim; *y; y++)
	    if (*y == c || *y == sp)
		break;

	/* Delim found */
	if (*y) {
	    if (found_delim)
		*found_delim = c;
	    break;
	}
	data[dx++] = lowercase ? unicode_ch(c,UOP_lowercase) : c;

	if (!sp)
	    last_X = dx;

	X++;
	
    }

    if (trim && last_X < dx)
	dx = last_X;

    (*word)->string_type->charset_type->cs_add_unicodedata_to_it(*word,dx,data);

    ret = X -*pos;
    *pos = X;

 fail:
    if (data)
	free(data);

    DPRINT(Debug,58,(&Debug,
		     "get_word_from_string=%d, *pos=%d",ret,*pos));

    if (found_delim) {
	DPRINT(Debug,60,(&Debug,", *found_delim=%04x",*found_delim));
    }

    DPRINT(Debug,58,(&Debug,"\n"));

    return ret;
}


int locate_unicode_from_string(str,unicode)
     CONST struct string *str;
     int unicode;
{
    /* NOTE: We can not trim string !!! */
    int i;

    if (CS_charset_magic != str->string_type->magic) 
	panic("STRING PANIC",__FILE__,__LINE__,"locate_unicode_from_string",
	      "Bad magic number (string type)",0);
    
    if (INVALID_MAP(str->string_type))    
	panic("STRING PANIC",__FILE__,__LINE__,"locate_unicode_from_string",
	      "Bad map (string type)",0);

    if (str->p->magic  != CS_str_magic)
	panic("STRING PANIC",__FILE__,__LINE__,"locate_unicode_from_string",
	      "Bad magic number (str_private_data)",0);

    DPRINT(Debug,60,(&Debug,
		     "locate_unicode_from_string(%p) ('%s'; type=%p '%s', len=%d)\n",
		     str,
		     str->string_type->MIME_name ? 
		     str->string_type->MIME_name : "<none>",
		     str->string_type->charset_type,
		     str->string_type->charset_type->type_name,
		     str->p->len));



    for (i = 0; i < str->p->len; i++) {
	int found;
	uint16 c = str->string_type->charset_type->
	    cs_give_unicode_from_it(str,i,&found);

	if (found && c == unicode) {
	    DPRINT(Debug,60,(&Debug,
			     "locate_unicode_from_string=%d\n",i));
	    return i;
	}
    }
    
    DPRINT(Debug,60,(&Debug,
		     "locate_unicode_from_string=-1 (not found)\n"));
    return -1;
}

int find_pattern_from_string(str,pattern,ignore_case)
     CONST struct string *str;
     CONST struct string *pattern;
     int ignore_case;
{
    int ret = -1;

    if (CS_charset_magic != str->string_type->magic) 
	panic("STRING PANIC",__FILE__,__LINE__,"find_pattern_from_string",
	      "Bad magic number (string type)",0);

    if (CS_charset_magic != pattern->string_type->magic) 
	panic("STRING PANIC",__FILE__,__LINE__,"find_pattern_from_string",
	      "Bad magic number (string type)",0);

    if (INVALID_MAP(str->string_type))    
	panic("STRING PANIC",__FILE__,__LINE__,"find_pattern_from_string",
	      "Bad map (string type)",0);

    if (INVALID_MAP(pattern->string_type))    
	panic("STRING PANIC",__FILE__,__LINE__,"find_pattern_from_string",
	      "Bad map (string type)",0);

    if (str->p->magic  != CS_str_magic)
	panic("STRING PANIC",__FILE__,__LINE__,"find_pattern_from_string",
	      "Bad magic number (str_private_data)",0);

    if (pattern->p->magic  != CS_str_magic)
	panic("STRING PANIC",__FILE__,__LINE__,"find_pattern_from_string",
	      "Bad magic number (str_private_data)",0);

    DPRINT(Debug,60,(&Debug,
		     "find_pattern_from_string(%p,%p,%d) ('%s'; type=%p '%s', len=%d), ('%s'; type=%p '%s', len=%d)\n",
		     str,pattern,ignore_case,
		     str->string_type->MIME_name ? 
		     str->string_type->MIME_name : "<none>",
		     str->string_type->charset_type,
		     str->string_type->charset_type->type_name,
		     str->p->len,
		     pattern->string_type->MIME_name ? 
		     pattern->string_type->MIME_name : "<none>",
		     pattern->string_type->charset_type,
		     pattern->string_type->charset_type->type_name,
		     pattern->p->len));

    /* Check lengths -- do compressions and so on */
    str->string_type->charset_type->cs_check_length_it(str);
    pattern->string_type->charset_type->cs_check_length_it(pattern);

    
    if (0 == str->p->len) {
	/* Nothing can be found from empty string */
	
	DPRINT(Debug,63,(&Debug,
			 "cs_find_pattern_from_string=0  -- empty string\n"));
	ret = 0;
    } else if (0 == pattern->p->len) {
	/* Empty pattern can found from anywhere */
	
	DPRINT(Debug,63,(&Debug,
			 "cs_find_pattern_from_string=1  -- empty pattern\n"));
	ret = 1;
    } else if (str -> string_type == pattern -> string_type)
	ret = str->string_type->charset_type->
	    cs_find_pattern_from_it(str,pattern,ignore_case);

    /* -1 indicates that unicode values should be used on comparision ... */

    if (ret < 0) {
	int l1 = 0;
	uint16 * vector1 = safe_malloc ( (str->p->len) * sizeof (uint16));
	int l2 = 0;
	uint16 * vector2 = safe_malloc ( (pattern->p->len) * sizeof (uint16));
	int i,j;
	
	ret = 0;
	
	for (i = 0;  i < str->p->len; i++) {
	    int found;
            uint16 c =
                str->string_type->charset_type->
		cs_give_unicode_from_it(str,i,&found);

	    /* TODO: Should lowercase ANY unicode value ... */

	    if (!found)
		c = MAPPING_NONE;
	    else if (ignore_case) {
		c = unicode_ch(c,UOP_lowercase);
		if (!c)
		    c = MAPPING_NONE;
	    }
	    vector1[l1++] = c;
	}

	for (i = 0;  i < pattern->p->len; i++) {
	    int found;
            uint16 c =
                pattern->string_type->charset_type->
		cs_give_unicode_from_it(pattern,i,&found);

	    if (!found)
		c = MAPPING_NONE;
	    else if (ignore_case) { 
		c = unicode_ch(c,UOP_lowercase);
		if (!c)
		    c = MAPPING_NONE;
	    }
	    vector2[l2++] = c;
	}

	for (i = 0; i < l1; ) {
	    CONST int s = i + 1;
	    for (j = 0; j < l2 && i < l1; j++,i++) {
		if (vector1[i] != vector2[j])
		    break;
		/* MAPPING_NONE matches newer ... */
		if (MAPPING_NONE == vector1[i] ||
		    MAPPING_NONE == vector2[j])
		    break;
	    }
	    if (j >= l2) {
		DPRINT(Debug,63,(&Debug,
				 "cs_find_pattern_from_string=1 MATCH\n"));

		ret = 1;
		break;
	    }
	    i = s;
	}	     

	if (!ret) {
	    DPRINT(Debug,63,(&Debug,
			     "cs_find_pattern_from_string=0 NO MATCH\n"));
	}
	
	free(vector1);
	free(vector2);
    }
								      
    DPRINT(Debug,60,(&Debug,
		     "find_pattern_from_string=%d\n",ret));

    return ret;
}


static int string_match_part P_((const struct string * name, int name_X,
				 const struct string * pat, int pat_X));

static int string_match_part(name,name_X,pat,pat_X)
     CONST struct string * name;
     int name_X;
     CONST struct string * pat;
     int pat_X;
{
    int r = 1;


    /* It is assumed that compressions are already done ... */

    while (pat_X < pat->p->len) {
	int pat_found = 0;
	uint16 code_pat  = 
	    pat->string_type->charset_type->
	    cs_give_unicode_from_it(pat,
				    pat_X,
				    &pat_found);
	
	if (pat_found && 
	    0x002A /* '*' */ == code_pat) {
	    
	    if (name_X < name->p->len) {
		if (string_match_part(name,name_X,pat,pat_X+1)) {
		    /* Tail matches */
		
		    goto succeed;
		}
		/* Try match to next starting position */
		
		name_X++;
		
	    } else {
		/* '*' matches to empty string */
		pat_X++;
	    }	    
	} else if (name_X < name->p->len) {
	    int name_found = 0;
	    uint16 code_name = 
		name->string_type->charset_type->
		cs_give_unicode_from_it(name,
					name_X,
					&name_found);
	    
	    
	    if (pat_found &&
		0x003F /* '?' */ == code_pat) {

		/* Accept any character */
		name_X++;
		pat_X++;
		
	    } else if (pat_found && name_found &&
		       code_pat == code_name) {
		
		/* Accept character */
		name_X++;
		pat_X++;
	    } else {
		r = 0;

		DPRINT(Debug,55,(&Debug,
			   "-- Match fail on pat_X=%d (%04X) name_X=%d (%04X)\n",
			   pat_X,code_pat,name_X,code_name));
		break;
	    }		
	} else {
	    r = 0;
	    DPRINT(Debug,55,(&Debug,
			     "-- Match fail on pat_X=%d name_X=%d (end of name)\n",
			     pat_X,name_X));
	    break;	    
	}  
    }
    
    if (name_X != name->p->len ||
	pat_X != pat->p->len) {

	DPRINT(Debug,55,(&Debug,
			 "-- Match fail on pat_X=%d name_X=%d (failure?)\n",
			 pat_X,name_X));

	r = 0;
    }
 succeed:
    return r;
}

/* return 1 on succeed and 0 on failure */
int string_match(name,pat)
     CONST struct string * name;
     CONST struct string * pat;
{
    int r = 0;

    if (CS_charset_magic != name->string_type->magic) 
	panic("STRING PANIC",__FILE__,__LINE__,"string_match",
	      "Bad magic number (string type)",0);

    if (CS_charset_magic != pat->string_type->magic) 
	panic("STRING PANIC",__FILE__,__LINE__,"string_match",
	      "Bad magic number (string type)",0);

    if (INVALID_MAP(name->string_type))    
	panic("STRING PANIC",__FILE__,__LINE__,"string_match",
	      "Bad map (string type)",0);

    if (INVALID_MAP(pat->string_type))    
	panic("STRING PANIC",__FILE__,__LINE__,"string_match",
	      "Bad map (string type)",0);

    if (name->p->magic  != CS_str_magic)
	panic("STRING PANIC",__FILE__,__LINE__,"string_match",
	      "Bad magic number (str_private_data)",0);
    if (pat->p->magic  != CS_str_magic)
	panic("STRING PANIC",__FILE__,__LINE__,"string_match",
	      "Bad magic number (str_private_data)",0);

    DPRINT(Debug,60,(&Debug,
		     "string_match(%p,%p) ('%s'; type=%p '%s', len=%d), ('%s'; type=%p '%s', len=%d)\n",
		     name,pat,
		     name->string_type->MIME_name ? 
		     name->string_type->MIME_name : "<none>",
		     name->string_type->charset_type,
		     name->string_type->charset_type->type_name,
		     name->p->len,
		     pat->string_type->MIME_name ? 
		     pat->string_type->MIME_name : "<none>",
		     pat->string_type->charset_type,
		     pat->string_type->charset_type->type_name,
		     pat->p->len));

    /* Check lengths -- do compressions and so on */
    name->string_type->charset_type->cs_check_length_it(name);
    pat->string_type->charset_type->cs_check_length_it(pat);

    r = string_match_part(name,0,pat,0);
    
    DPRINT(Debug,60,(&Debug,
		     "string_match=%d\n",r));

    return r;
}


uint16 give_unicode_from_string(str,pos)
     CONST struct string *str;
     int pos;
{
    int found;
    uint16 ret;

    if (CS_charset_magic != str->string_type->magic) 
	panic("STRING PANIC",__FILE__,__LINE__,"give_unicode_from_string",
	      "Bad magic number (string type)",0);

    if (INVALID_MAP(str->string_type))    
	panic("STRING PANIC",__FILE__,__LINE__,"give_unicode_from_string",
	      "Bad map (string type)",0);

    if (str->p->magic  != CS_str_magic)
	panic("STRING PANIC",__FILE__,__LINE__,"give_unicode_from_string",
	      "Bad magic number (str_private_data)",0);

    /* It is assumed that compressions are done
       .. ie string_length() function
       is called 
    */

    ret = str->string_type->charset_type->
	cs_give_unicode_from_it(str,pos, &found);

    DPRINT(Debug,60,(&Debug,
		     "give_unicode_from_string(%p,%d)=%d  ('%s'; type=%p '%s')%s\n",
		     str,pos,ret,
		     str->string_type->MIME_name ? 
		     str->string_type->MIME_name : "<none>",
		     str->string_type->charset_type,
		     str->string_type->charset_type->type_name,
		     found ? "" : " [NOT FOUND]"));
    
    return ret;
}

void remove_control(str)
     CONST struct string *str;
{

    if (CS_charset_magic != str->string_type->magic) 
	panic("STRING PANIC",__FILE__,__LINE__,"remove_control",
	      "Bad magic number (string type)",0);

    if (INVALID_MAP(str->string_type))    
	panic("STRING PANIC",__FILE__,__LINE__,"remove_control",
	      "Bad map (string type)",0);

    if (str->p->magic  != CS_str_magic)
	panic("STRING PANIC",__FILE__,__LINE__,"remove_control",
	      "Bad magic number (str_private_data)",0);

    if (str->p->state) {
	if (str->p->state->charset != str->string_type)
	    panic("STRING PANIC",__FILE__,__LINE__,"remove_control",
		  "Bad magic number (state charset)",0);
	free_state_internal(&(str->p->state));
    }

    DPRINT(Debug,60,(&Debug,
		     "remove_control(%p)   ('%s'; type=%p '%s')\n",
		     str,
		     str->string_type->MIME_name ? 
		     str->string_type->MIME_name : "<none>",
		     str->string_type->charset_type,
		     str->string_type->charset_type->type_name));
    
    str->string_type->charset_type->cs_remove_control_it(str);	
}

void add_state_to_string(str,ch)
     struct string *str; 
     struct charset_state *ch;
{
    if (CS_charset_magic != str->string_type->magic) 
	panic("STRING PANIC",__FILE__,__LINE__,"add_state_to_string",
	      "Bad magic number (string type)",0);
    
    if (CS_charset_magic != ch->charset->magic) 
	panic("STRING PANIC",__FILE__,__LINE__,"add_state_to_string",
	      "Bad magic number (charset)",0);

    if (INVALID_MAP(str->string_type))    
	panic("STRING PANIC",__FILE__,__LINE__,"add_state_to_string",
	      "Bad map (string type)",0);

    if (INVALID_MAP(ch->charset))    
	panic("STRING PANIC",__FILE__,__LINE__,"add_state_to_string",
	      "Bad map (charset)",0);


    if (str->p->magic  != CS_str_magic)
	panic("STRING PANIC",__FILE__,__LINE__,"add_state_to_string",
	      "Bad magic number (str_private_data)",0);

    if (str->p->state) {
	if (str->p->state->charset != str->string_type)
	    panic("STRING PANIC",__FILE__,__LINE__,"add_state_to_string",
		  "Bad magic number (state charset)",0);
	free_state_internal(&(str->p->state));
    }

    DPRINT(Debug,60,(&Debug,
		     "add_state_to_it(%p,%p)   ('%s'; type=%p '%s', '%s'; type=%p '%s')\n",
		     str,ch,
		     str->string_type->MIME_name ? 
		     str->string_type->MIME_name : "<none>",
		     str->string_type->charset_type,
		     str->string_type->charset_type->type_name,
		     ch->charset->MIME_name ? 
		     ch->charset->MIME_name : "<none>",
		     ch->charset->charset_type,
		     ch->charset->charset_type->type_name));

    if (ch->charset == str->string_type)
	str->string_type->charset_type->cs_add_state_to_it(str,ch);
    else {
	int found;
	uint16 val = 
	    ch->charset->charset_type->cs_give_unicode_from_s_it(ch,&found);
	
	str->string_type->charset_type->
	    cs_add_unicodedata_to_it(str,1,&val);
    }
}

int charset_properties(ptr)
     charset_t ptr;
{
    int prop = 0;

    if (CS_charset_magic != ptr->magic) 
 	panic("STRING PANIC",__FILE__,__LINE__,"charset_properties",
	      "Bad magic number (charset)",0);

    if (INVALID_MAP(ptr))    
 	panic("STRING PANIC",__FILE__,__LINE__,"charset_properties",
	      "Bad map (charset)",0);


    prop = ptr->charset_type->cs_it_properties(ptr);

    DPRINT(Debug,11,(&Debug, 
		    "charset_properties(%p '%s'; type=%p)=%d\n",
		    ptr,
		    ptr->MIME_name ? ptr->MIME_name : "<none>",
		    ptr->charset_type,
		    prop));
    return prop;
}

/* bad_pos is -1 if string is OK */
long string_to_long(str,bad_pos)
     CONST struct string *str; 
     int *bad_pos;
{
    long val = 0L;
    int found;
    int pos = 0;
    uint16 c;
    int plus_minus = 1;
    
    if (CS_charset_magic != str->string_type->magic) 
	panic("STRING PANIC",__FILE__,__LINE__,"string_to_long",
	      "Bad magic number (string type)",0);
    
    if (INVALID_MAP(str->string_type))    
	panic("STRING PANIC",__FILE__,__LINE__,"string_to_long",
	      "Bad magic number (string type)",0);

    if (str->p->magic  != CS_str_magic)
	panic("STRING PANIC",__FILE__,__LINE__,"string_to_long",
	      "Bad magic number (str_private_data)",0);

    *bad_pos = -1;
    
    pos = 0;

    if (str->p->len < 1)
	goto fail;

    c = str->string_type->charset_type->
	cs_give_unicode_from_it(str,pos,&found);

    if (!found) 
	goto fail;

    if (0x002B /* + */ == c) {
	plus_minus = 1;
	pos++;
	
    } else if (0x002D /* - */ == c) {
	plus_minus = -1;
	pos++;
    }


    while (pos < str->p->len) {
	int num;
	long l1;

	c = str->string_type->charset_type->
	    cs_give_unicode_from_it(str,pos,&found);
	if (!found) 
	    goto fail;

	if (c < 0x0030  /* 0 */ ||
	    c > 0x0039  /* 9 */)
	    goto fail;

	num = c - 0x0030;

	l1 = val * 10 + num;
	if (l1 < val) {   /* Overflow */
	    
	    goto fail;
	}
	val = l1;	
	pos++;
    }

    val = val * plus_minus;

    DPRINT(Debug,30,(&Debug,
		     "string_to_long: %S gives value %ld\n",
		     str,val));

    return val;

 fail:
    *bad_pos = pos;

    val = val * plus_minus;

    DPRINT(Debug,30,(&Debug,
		     "string_to_long: %S gives value %ld, error on position %d\n",
		     str,val,pos));

    return val;
}

/* Return 1 is matches, otherwise 0 */
int string_matches_ascii(str,ascii)
     CONST struct string *str;
     CONST unsigned char *ascii;
{
    int i;

    if (CS_charset_magic != str->string_type->magic) 
	panic("STRING PANIC",__FILE__,__LINE__,"string_matches_ascii",
	      "Bad magic number (string type)",0);
    
    if (INVALID_MAP(str->string_type))    
	panic("STRING PANIC",__FILE__,__LINE__,"string_matches_ascii",
	      "Bad map (string type)",0);


    if (str->p->magic  != CS_str_magic)
	panic("STRING PANIC",__FILE__,__LINE__,"string_matches_ascii",
	      "Bad magic number (str_private_data)",0);

    for (i = 0; i < str->p->len; i++) {
	uint16 c;
	int found;

	c = str->string_type->charset_type->
	    cs_give_unicode_from_it(str,i,&found);

	if (!found) {
	    DPRINT(Debug,30,(&Debug,
			     "string_matches_ascii=0 on pos %d (unicode not found)\n",
			     i));
	    return 0;
	}
	
	if (!ascii[i]) {
	    DPRINT(Debug,30,(&Debug,
			     "string_matches_ascii=0 on pos %d (ascii string ended)\n",
			     i));
	    return 0;
	}

	if (c != ascii[i]) {
	    DPRINT(Debug,30,(&Debug,
			     "string_matches_ascii=0 on pos %d (mismatch)\n",
			     i));
	    return 0;
	    
	}
    }

    if (ascii[i]) {
	DPRINT(Debug,30,(&Debug,
			 "string_matches_ascii=0 on pos %d (ascii string not ended)\n",
			 i));
	return 0;
    }

    DPRINT(Debug,30,(&Debug,
		     "string_matches_ascii=1 on pos %d (matches)\n",
		     i));
    return 1;
}

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



syntax highlighted by Code2HTML, v. 0.9.1