static char rcsid[] = "@(#)$Id: terminal.c,v 1.6.44.1 2007/08/25 07:45:27 hurtta Exp $";

/******************************************************************************
 *  The Elm (ME+) Mail System  -  $Revision: 1.6.44.1 $   $State: Exp $
 *
 *  Author: Kari Hurtta <hurtta+elm@posti.FMI.FI> 
 *      or  Kari Hurtta <elm@elmme-mailer.org>
 *****************************************************************************/

#include "def_misc.h"

#include "s_me.h"

#include "cs_imp.h"
#include "cs_terminal.h"

DEBUG_VAR(Debug,__FILE__,"charset");

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


static char *us2s P_((unsigned char *str));
static char *us2s(str)
     unsigned char *str;
{
    return (char *)str;
}

static unsigned char * s2us P_((char *str));
static unsigned char * s2us(str)
     char *str;
{
    return (unsigned char *)str;
}



enum terminal_flags { terminal_bad_flag    = -1,  
		      terminal_xterm_title = 1,
		      terminal_xwsh_title  = 2
};

enum terminal_map_keyword { terminal_bad = -1, terminal_iso2022         = 0,
                            terminal_private = 1, terminal_iso2022_mb   = 2,
			    terminal_flag    = 3, terminal_iso2022_like = 4,
			    terminal_dw
};


struct terminal_map_item {
    char                    * match;
    enum terminal_map_keyword keyword;
    union {
	struct cset {
	    charset_t  charset;  
	    int        is_def;
	} iso2022;
        struct privset {
	    struct term_cond  * condition;
            charset_t           charset;
            unsigned char *     bytes;
	} private;
	enum terminal_flags flag;
	struct cset2 {
	    charset_t           charset;
	    struct setlist    * iso2022_info;   
	} iso2022_like;
        char *                  dw_charset;
    } value;
};

static void strXcat P_((char **ret,char *val, 
			int maybe_signal, char *buffer, int size));
static void strXcat(ret,val,maybe_signal,buffer,size)
     char **ret;
     char *val; 
     int maybe_signal; 
     char *buffer; 
     int size;
{
    
    if (maybe_signal) {
	if (!*ret)
	    *ret = strfcpy(buffer,val,size);
	else
	    *ret = strfcat(buffer,val,size);
    } else
	*ret = strmcat(*ret,val);
}


static enum terminal_map_keyword get_keyword P_((const char *word));

static enum terminal_map_keyword get_keyword(word)
     CONST char *word;
{
    if (0 == istrcmp(word,"ISO-2022"))
	return terminal_iso2022;
    if (0 == istrcmp(word,"ISO-2022-LIKE"))
	return terminal_iso2022_like;
    if (0 == istrcmp(word,"ISO-2022/DW"))
	return terminal_iso2022_mb;
    if (0 == istrcmp(word,"PRIVATE"))
	return terminal_private;
    if (0 == istrcmp(word,"FLAG"))
	return terminal_flag;
    if (0 == istrcmp(word,"DW"))
	return terminal_dw;

    
    return terminal_bad;
}

static char * sStr P_((unsigned char *p));
static char * sStr (p)
     unsigned char *p;
{
    return (char *)p;
}

struct term_cond {
    enum tc_type { tt_value, tt_simple_var, tt_equal } X;


    struct term_cond        * p1;
    struct term_cond        * p2;

    char                    * value;

};

static void zero_term_cond P_((struct term_cond *X));
static void zero_term_cond(X)
     struct term_cond *X;
{
    X->X      = tt_value;
    X->p1     = NULL;
    X->p2     = NULL;
    X->value  = NULL;
}

static struct term_cond * copy_term_cond P_((struct term_cond *ptr));
static struct term_cond * copy_term_cond(ptr)
     struct term_cond *ptr;
{
    struct term_cond * ret = safe_malloc(sizeof (*ret));

    zero_term_cond(ret);

    ret->X = ptr->X;

    if (ptr->p1)
	ret->p1 = copy_term_cond(ptr->p1);
    if (ptr->p2)
	ret->p2 = copy_term_cond(ptr->p2);

    if (ptr->value)
	ret->value = safe_strdup(ptr->value);

    return ret;
}

static void free_term_cond P_((struct term_cond **X));
static void free_term_cond(X)
     struct term_cond **X;
{
    struct term_cond *A = *X; 
    if (A->p1)
	free_term_cond(&(A->p1));
    if (A->p2)
	free_term_cond(&(A->p2));

    if (A->value) {
	free(A->value);
	A->value = NULL;
    }

    free(A);
    A = NULL;
	
    *X = A;
}

static int match_term_cond P_((struct term_cond *A, struct term_cond *B));
static int match_term_cond(A,B)
     struct term_cond *A;
     struct term_cond *B;
{  
    if (A->p1 && B->p1) {
	if (! match_term_cond(A->p1,B->p1))
	    return 0;
    } else if (A->p1 || B->p1) 
	return 0;

    if (A->p2 && B->p2) {
	if (! match_term_cond(A->p2,B->p2))
	    return 0;
    } else if (A->p2 || B->p2) 
	return 0;


    if (A->value && B->value) {
	if (0 != strcmp(A->value,B->value))
	    return 0;
    } else if (A->value || B->value)
	return 0;

    return 1;
}



enum tc_token_type { tc_left = '[', tc_rigth = ']', tc_equal = '=',
		     tc_quoted = '"', tc_plain = 'a', tc_simple_var = '$',
		     tc_none = 0 };

static enum tc_token_type tc_get_token P_((char **value, char **ptr));
static enum tc_token_type tc_get_token (value,ptr)
     char **value;
     char **ptr;
{
    enum tc_token_type r = tc_none;
    char *C = *ptr;

    *value = NULL;

    while (*C && whitespace (*C)) /* skip leading whitespace */
	C++;

    switch (*C) {
	int L;
    case '\0':    goto fail;
    case '[':     r = tc_left; C++;  break;
    case ']':     r = tc_rigth; C++; break;
    case '=':     r =  tc_equal; C++; break;
    case '"':
	L = len_next_part(C);
	*value = dequote_opt(C,L);
	C += L;
	r = tc_quoted;
	break;
    case '$':	
	L = strcspn(C+1," []=\"$/");
	if (1 == L) {
	    r = tc_none;
	    goto fail;
	}
	*value = safe_malloc(L+1);
	memcpy(*value,C+1,L);
	(*value)[L] = '\0';
	C += L +1;
	r = tc_simple_var;
	break;
    default:
	L = strcspn(C," []=\"$");
	*value = safe_malloc(L+1);
	memcpy(*value,C,L);
	(*value)[L] = '\0';
	C += L;
	r = tc_plain;
	break;
    }

 fail:
    *ptr = C;
    return r;
}

static struct term_cond * tc_get_expression P_((char              **ptr));
static struct term_cond * tc_get_expression(ptr)     
     char              **ptr;
{
    struct term_cond *r  = NULL;
    char *value = NULL;

    switch (tc_get_token(&value,ptr)) {
    case tc_plain:
	lib_error(CATGETS(elm_msg_cat, MeSet, MeTreatedAsQuoted,
			  "Unregonized token %s treated as quoted value"),
		  value); 
	/* FALLTHRU */
    case tc_quoted:
	r = safe_malloc(sizeof (*r));
	zero_term_cond(r);
	r->value = value;
	r->X     = tt_value;
	value = NULL;
	break;
    case tc_simple_var:
	r = safe_malloc(sizeof (*r));
	zero_term_cond(r);
	r->value = value;
	r->X     = tt_simple_var;
	value = NULL;
	break;

    }



    return r;
}

static struct term_cond * parse_condition P_((char              **ptr));
static struct term_cond * parse_condition(ptr)     
     char              **ptr;
{
    struct term_cond *r  = NULL;
    struct term_cond *r1 = NULL;
    struct term_cond *r2 = NULL;

    char *value = NULL;

    if (tc_left != tc_get_token(&value,ptr))
	goto err;
    r1 = tc_get_expression(ptr);
    if (!r1)
	goto err;
    if (tc_equal != tc_get_token(&value,ptr))
	goto err;
    r2 = tc_get_expression(ptr);
    if (!r2)
	goto err;
    if (tc_rigth != tc_get_token(&value,ptr))
	goto err;

    r = safe_malloc(sizeof (*r));
    zero_term_cond(r);

    r->X = tt_equal;
    r->p1 = r1;
    r->p2 = r2;

    r1 = NULL; r2 = NULL;

 err:
    if (value)
	free(value);
    if (r1) 
	free_term_cond(&r1);
    if (r2) 
	free_term_cond(&r2);

    return r;
}

static void tc_dump_expression P_((FILE *f, struct term_cond * C));

static char * tc_eval_expression P_((struct term_cond * C));
static char * tc_eval_expression(C)
     struct term_cond * C;
{
    char * r = NULL;
    

    switch (C->X) {
	char buffer[1024];
	int v;
    case tt_value:
	/* expand_path converts $VAR between / characters */
	v = expand_path(buffer,C->value,sizeof buffer);
	if (v > 0) {
	    DPRINT(Debug,9,(&Debug,"tc_eval_expression : \"%s\" expanded\n",
			    C->value));
	    r = safe_strdup(buffer);
	} else if (v == 0) {
	    DPRINT(Debug,9,(&Debug,"tc_eval_expression : \"%s\" copied\n",
			    C->value));
	    r = safe_strdup(C->value);       
	} else {
	    DPRINT(Debug,9,(&Debug,"tc_eval_expression : \"%s\" failed\n",
			    C->value));
	}

	DPRINT(Debug,9,(&Debug,"tc_eval_expression \"%s\" = %s\n",
			C->value,
			r ? r : "NULL"));

	break;
    case tt_simple_var:
	r = getenv(C->value);
	if (r)
	    r = safe_strdup(r);

	DPRINT(Debug,9,(&Debug,"tc_eval_expression $%s = %s\n",
			C->value,
			r ? r : "NULL"));

	break;
    default:
	DPRINT(Debug,8,(&Debug,"tc_eval_expression %d (bad)\n",C->X));
    }

    return r;
}

static void dump_condition P_((FILE *f, struct term_cond * C));

static int eval_condition P_((struct term_cond * C));
static int eval_condition(C)
     struct term_cond * C;
{
    int r = 0;
    char * v1 = NULL;
    char * v2 = NULL;

#ifdef DEBUG
    if (Debug.active >= 8) {

	FILE *F;

	DPRINT(Debug,8,(&Debug,"eval_condition   "));
	F = debug_to_FILE(&Debug);
	if (F) {
	    dump_condition(F,C);

	    fputc('\n',F);
	    fclose(F);
	}	
    } 
#endif

    if (C->p1)
	v1 = tc_eval_expression(C->p1);
    if (C->p2)
	v2 = tc_eval_expression(C->p2);


    switch (C->X) {
    case tt_equal:
	
	if (!v1 || !v2)
	    break;
	r = 0 == strcmp(v1,v2);

	DPRINT(Debug,8,(&Debug,"eval_condition  \"%s\" = \"%s\"\n",v1,v2));

	break;
    }

    if (v1)
	free(v1);
    if (v2)
	free(v2);
    DPRINT(Debug,8,(&Debug,"eval_condition=%d\n",r));
    return r;
}

static void tc_dump_expression(f,C)
     FILE *f;
     struct term_cond * C;
{
    switch (C->X) {
    case tt_value:
	elm_fprintf(f,FRM("%Q"),C->value);
	break;
    case tt_simple_var:
	fprintf(f,"$%s",C->value);
	break;
    default:
	fputc('?',f);
    }
}

static void dump_condition(f,C)
     FILE *f;
     struct term_cond * C;
{
    fputc('[',f);
    fputc(' ',f);
    
    if (C->p1)
	tc_dump_expression(f,C->p1);

    if (tt_equal == C->X)
	fputc('=',f);
    else
	fputc('?',f);

    if (C->p2)
	tc_dump_expression(f,C->p2);

    fputc(' ',f);
    fputc(']',f);
}

static struct {
    char                *flag;
    enum terminal_flags  flag_value;
} terminal_flag_list[] = {
    { "xterm-title" , terminal_xterm_title },
    { "xwsh-title"  , terminal_xwsh_title }

};
    

struct terminal_map_item * load_terminal_map(filename,errors)
     CONST char *filename;
     int *errors;
{
    struct terminal_map_item *result;
    int result_len = 0;
    FILE * f;
    int max_result = 0;
    int c;
    char * buf = NULL;
    int err = can_open(filename,"r");

    if (err) {
	DPRINT(Debug,2,(&Debug,"load_terminal_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_terminal_map: %s: %s\n",
			filename,error_description(err)));
	return NULL;
    }

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

    DPRINT(Debug,10,(&Debug,"load_terminal_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 (result[0]));
    
    /* malloc_gets reallocates buf and do not return \n.
       It returns -1 if line is longer than given limit (32000)
       and -2 is returned on error  (buffer may still be alloced)
    */

    while (result_len < max_result &&
	   !feof(f) && !ferror(f)) {
	int l1 = malloc_gets(&buf,32000,f);
	char * c = buf,*c1,*c2;
	enum terminal_map_keyword kw;

	if (-1 == l1) {
	    lib_error(CATGETS(elm_msg_cat, MeSet, MeTooLongLine,
			      "%.30s: Too long line: %.30s..."),
		      filename,buf ? buf : "???");
	    (*errors) ++;
	    goto OUT;

	} else if (-2 == l1) {
	    int err = errno;
	    
	    lib_error(CATGETS(elm_msg_cat, MeSet, MeReadError,
			  "%.30s: Reading error: %s"),
		      filename,error_description(err));
	    (*errors) ++;	    

	} else if (0 == l1)
	    continue;

	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;	    
	}
	    

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

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

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

	if (terminal_bad == kw) {
	    lib_error(CATGETS(elm_msg_cat, MeSet, MeBadLine,
			      "%.30s: Bad line: %.30s..."),
		      filename,buf);
	    (*errors) ++;
	    break;	    
	}
	
	switch(kw) {
	    char * c3;
	    int size;
	    int i;
	case terminal_dw:
	    result[result_len].keyword = kw;
	    result[result_len].value.dw_charset = safe_strdup(c2);
	    result[result_len].match = safe_strdup(c);
	    result_len++;
	    break;

	case terminal_iso2022:
	case terminal_iso2022_mb:
	    result[result_len].keyword = kw;
	    
	    if (load_charset_map_info(&result[result_len].
				      value.iso2022.charset,c2))
		result[result_len].value.iso2022.is_def = 
		    ((result[result_len].value.iso2022.charset->flags &
		      SET_nodata) != 0);
	    else {
		result[result_len].value.iso2022.charset = 
		    MIME_name_to_charset(c2,0); 
		result[result_len].value.iso2022.is_def = 0;
	    }
	    
	    if (!result[result_len].value.iso2022.charset) {
		lib_error(CATGETS(elm_msg_cat, MeSet, 
				  MeUnknownTerminalCharset,
				  "Unknown terminal charset %s (file %s)"),
			  c2,filename);
		(*errors) ++;
		continue;
	    }

	    if (!result[result_len].value.iso2022.charset->map_info) {
		lib_error(CATGETS(elm_msg_cat, MeSet, 
				  MeCharsetNeedsMap,
				  "Charset %s needs map= defination (file %s)"),
			  result[result_len].value.iso2022.charset->MIME_name ?
			  result[result_len].value.iso2022.charset->MIME_name :
			  "<no MIME name>",
			  filename);
		(*errors) ++;
		continue;
	    }

	    if (!result[result_len].value.iso2022.charset->iso2022_info) {
		lib_error(CATGETS(elm_msg_cat, MeSet, 
				  MeCharsetNeedsIso2022,
				  "Charset %s needs iso2022 defination (file %s)"),
			  result[result_len].value.iso2022.charset->MIME_name ?
			  result[result_len].value.iso2022.charset->MIME_name :
			  "<no MIME name>",
			  filename);
		(*errors) ++;
		continue;
	    }

	    if (terminal_iso2022_mb == result[result_len].keyword &&
		0 == (CS_printable_len & 
		      charset_properties(result[result_len].value.iso2022.
					 charset))) {
		lib_error(CATGETS(elm_msg_cat, MeSet, 
				  MeCharsetNotMB,
				  "Charset %s does not support /DW flag (file %s)"),
			  result[result_len].value.iso2022.charset->MIME_name ?
			  result[result_len].value.iso2022.charset->MIME_name :
			  "<no MIME name>",
			  filename);
		(*errors) ++;
		continue;

	    }

	    result[result_len].match = safe_strdup(c);
	    result_len++;
	    break;
	case terminal_iso2022_like: {
	    struct setlist     new_setlist;
	    int         setcount          = 0;

	    char * opt;
	    char * WALK;
	
	    iso2022_clear_setlist(&new_setlist);

	    result[result_len].keyword = kw;

	    c3 = strpbrk(c2," \t");

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

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

	    result[result_len].value.iso2022_like.charset = 
		MIME_name_to_charset(c2,0); 
	    if (!result[result_len].value.iso2022_like.charset) {
		lib_error(CATGETS(elm_msg_cat, MeSet, 
				  MeUnknownTerminalCharset,
				  "Unknown terminal charset %s (file %s)"),
			  c2,filename);
		(*errors) ++;
		continue;
	    }

	    if (';' == *c3)
		c3++;

	    for (opt = mime_parse_content_opts(c3, &WALK); 
		 opt; 
		 opt = mime_parse_content_opts(NULL, &WALK)) {
		
		char * q = strchr(opt,'=');
		char *val; 

		if (!q) {
		    lib_error(CATGETS(elm_msg_cat, MeSet, MeBadLine,
				      "%.30s: Bad line: %.30s..."),
			      filename,buf);
		    (*errors) ++;
		    continue;    		    
		}
		*q++ = '\0';
		val = dequote_opt(q,strlen(q));
		
		if (! parse_iso2022_specification(opt,val,
						  &setcount,
						  &new_setlist)) {
		    lib_error(CATGETS(elm_msg_cat, MeSet, MeBadLine,
				      "%.30s: Bad line: %.30s..."),
			      filename,buf);
		    (*errors) ++;
		}

		free(val);
	    }
	    
	    if (0 == setcount) {
		lib_error(CATGETS(elm_msg_cat, MeSet, MeBadLine,
				  "%.30s: Bad line: %.30s..."),
			  filename,buf);
		(*errors) ++;
		continue;
	    }
		
	    result[result_len].value.iso2022_like.iso2022_info =
		loc_setlist(new_setlist);
	    result[result_len].match = safe_strdup(c);
	    result_len++;

	}
	    break;
	case terminal_private:
	    result[result_len].keyword = kw;

	    c3 = strpbrk(c2," \t");

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

	    while (*c3 && whitespace (*c3)) /* skip leading whitespace */
		c3++;
	    if (!*c3) {
		lib_error(CATGETS(elm_msg_cat, MeSet, MeBadLine,
				  "%.30s: Bad line: %.30s..."),
			  filename,buf);
		(*errors) ++;
		continue;    
	    }
	    result[result_len].value.private.condition         = NULL;

	    if ('[' == *c3) {

		result[result_len].value.private.condition = parse_condition(&c3);
		if (!result[result_len].value.private.condition)
		    goto err_3;

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

	    result[result_len].value.private.charset = 
		MIME_name_to_charset(c2,0); 
	    if (!result[result_len].value.private.charset) {
		lib_error(CATGETS(elm_msg_cat, MeSet, 
				  MeUnknownTerminalCharset,
				  "Unknown terminal charset %s (file %s)"),
			  c2,filename);
		(*errors) ++;
		continue;
	    }
	    size = strlen(c3);
	    result[result_len].value.private.bytes = safe_malloc(size+1);

	    if (!parse_gen_spec(c3,result[result_len].
				value.private.bytes,
				size)) {
		(*errors) ++;
		continue;
	    }
	    result[result_len].value.private.bytes[size] = '\0';
	    result[result_len].match = safe_strdup(c);
	    result_len++;
	    break;
	case terminal_flag:
	    result[result_len].keyword = kw;
	    result[result_len].value.flag    = terminal_bad_flag;

	    for (i = 0; 
		 i < sizeof terminal_flag_list / sizeof (terminal_flag_list[0]);
		 i++) {
		if (0 == strcmp(terminal_flag_list[i].flag,c2)) {
		    result[result_len].value.flag   = 
			terminal_flag_list[i].flag_value;
		    goto ok1;
		}
	    }

	    lib_error(CATGETS(elm_msg_cat, MeSet, 
			      MeUnknownTerminalFlag,
			      "Unknown terminal flag %s (file %s)"),
		      c2,filename);
	    (*errors) ++;
	    continue;
	    
	ok1:
	    result[result_len].match = safe_strdup(c);
	    result_len++;
	    break;
	}
    }

    if (ferror(f)) {
	int err = errno;

	lib_error(CATGETS(elm_msg_cat, MeSet, MeReadError,
			  "%.30s: Reading error: %s"),
		  filename,error_description(err));
	(*errors) ++;
    }

 OUT:
    if (buf) {
	free(buf);
    }

    result[result_len].match   = NULL;
    result[result_len].keyword = terminal_bad;


    if (!feof(f)) {
	DPRINT(Debug,3,(&Debug,
			"load_terminal_map: %s, All results not readed\n",
			filename));
    }

    fclose(f);

    

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

static int name_ok P_((CONST char *name));
static int name_ok(name)
     CONST char *name;
{
    if ('\0' == *name)
	return 0;

    return strlen(name) == strspn(name,"ABCDEFGHIJKLMNOPQRSTUVXYZ-0123456789");
}

void dump_terminal_map(f,map)
     FILE *f; 
     struct terminal_map_item *map;
{
    struct terminal_map_item *ptr;
    
    for (ptr = map; ptr && ptr->match; ptr++) {
	switch(ptr->keyword) {
	    char *s;
	    int i;

	case terminal_dw:
	    fputs(ptr->match,f);
	    fputc('\t',f);
	    fputs("DW",f);
	    fputc('\t',f);
	    fputs(ptr->value.dw_charset,f);
	    break;

	case terminal_iso2022:
	case terminal_iso2022_mb:
	    fputs(ptr->match,f);
	    fputc('\t',f);
	    if (terminal_iso2022_mb == ptr->keyword)
		fputs("ISO-2022/DW",f);
	    else
		fputs("ISO-2022",f);
	    fputc('\t',f);
	    if (ptr->value.iso2022.is_def)
		dump_map_info(f,ptr->value.iso2022.charset);
	    else {
		if (!ptr->value.iso2022.charset->MIME_name) {
		    fputs("# <No MIME name> ",f);
		} else if (name_ok(ptr->value.iso2022.charset->MIME_name)) {
		    fputs(ptr->value.iso2022.charset->MIME_name,f);
		} else {
		    elm_fprintf(f,FRM("%Q;!"),
				ptr->value.iso2022.charset->MIME_name);
		}
	    }
	    break;

	case terminal_iso2022_like:
	    fputs(ptr->match,f);
	    fputc('\t',f);
	    fputs("ISO-2022-LIKE",f);
	    fputc('\t',f);
	    fputs(ptr->value.iso2022_like.charset->MIME_name,f);
	    fputc('\t',f);
	    print_setlist(f,ptr->value.iso2022_like.iso2022_info);
	    break;

	case terminal_private:
	    fputs(ptr->match,f);
	    fputc('\t',f);
	    fputs("PRIVATE",f);
	    fputc('\t',f);
	    fputs(ptr->value.private.charset->MIME_name,f);
	    if (ptr->value.private.condition) {
		fputc('\t',f);
		dump_condition(f,ptr->value.private.condition);
	    }
	    fputc('\t',f);
	    s = iso2022_codestr(ptr->value.private.bytes,
				strlen(sStr(ptr->value.private.bytes)));
	    if (s) {
		fputs(s,f);
		free(s);
	    }
	    break;
	case terminal_flag:
	    fputs(ptr->match,f);
	    fputc('\t',f);
	    fputs("FLAG",f);
	    fputc('\t',f);
	    for (i = 0; 
		 i < sizeof terminal_flag_list / sizeof (terminal_flag_list[0]);
		 i++) {
		if (ptr->value.flag ==  terminal_flag_list[i].flag_value) {
		    fputs(terminal_flag_list[i].flag,f);
		    break;
		}
	    }
	    break;
	}
	fputc('\n',f);
    }
}

static int match_item P_((struct terminal_map_item *a, 
			  struct terminal_map_item *b));
static int match_item(a,b)
     struct terminal_map_item *a;
     struct terminal_map_item*b;
{
    if (a->keyword != b->keyword)
	return 0;
    if (0 != strcmp(a->match,b->match))
	return 0;

    switch(a->keyword) {

    case terminal_dw:
	if (0 != strcmp(a->value.dw_charset,
			a->value.dw_charset))
	    return 0;
	return 1;

    case terminal_iso2022:
    case terminal_iso2022_mb:
	
	if (a->value.iso2022.is_def !=
	    b->value.iso2022.is_def)
	    return 0;

	if (a->value.iso2022.charset ==
	    b->value.iso2022.charset)
	    return 1;

	if (a->value.iso2022.charset->MIME_name &&
	    b->value.iso2022.charset->MIME_name) {
	    if (0 == strcmp(a->value.iso2022.charset->MIME_name,
			    b->value.iso2022.charset->MIME_name))
		return 1;
	    else
		return 0;
	}

	if (a->value.iso2022.charset->codeset &&
	    b->value.iso2022.charset->codeset &&
	    0 == strcmp(a->value.iso2022.charset->codeset,
			b->value.iso2022.charset->codeset))
	    return 1;
	return 0;

    case terminal_iso2022_like:

	if (a->value.iso2022_like.charset !=
	    b->value.iso2022_like.charset)
	    return 0;

	if (a->value.iso2022_like.iso2022_info !=
	    b->value.iso2022_like.iso2022_info)
	    return 0;
	return 1;

    case terminal_private:
	if (a->value.private.charset !=
	    b->value.private.charset)
	    return 0;


	if (a->value.private.condition &&
	    b->value.private.condition) {
	    if (! match_term_cond(a->value.private.condition,
				  b->value.private.condition))
		return 0;
	} else if (a->value.private.condition ||
		   b->value.private.condition)
	    return 0;
	
	return 1;

    case terminal_flag:	
	if (a->value.flag != b->value.flag)
	    return 0;
	return 1;       
    }

    panic("CHARSET PANIC",__FILE__,__LINE__,"match_item",
	  "Unsupported type",0);


    return 0;

}

void change_terminal_map(map,new) 
     struct terminal_map_item **map;
     struct terminal_map_item *new;
{
    int count = 0;

    struct terminal_map_item *ptr;

    for (ptr = *map; ptr && ptr->match; ptr++)
	count++;

    for (ptr = new; ptr && ptr->match; ptr++)
	count++;

    if (!count)
	return;

    if (!*map) {
	*map = safe_malloc((count+1) * sizeof ((*map)[0]));
	(*map)->match   = NULL;
	
    } else
	*map = safe_realloc(*map, (count+1) * sizeof ((*map)[0]));

   for (ptr = new; ptr && ptr->match; ptr++) {
	struct terminal_map_item *ptr2;

	for (ptr2 = *map; ptr2->match; ptr2++) {
	    if (match_item(ptr,ptr2)) {
		goto set_it;
	    }
	}

	if (ptr2 > (*map) + count)
	    panic("CHARSET PANIC",__FILE__,__LINE__,"change_terminal_map",
		  "Overflow",0);
	
	ptr2->match    = safe_strdup(ptr->match);
	ptr2->keyword  = ptr->keyword;

	bzero((void *)(& ptr2->value), sizeof (ptr2->value));

	(ptr2+1)->match   = NULL;
	


    set_it:
	
	switch(ptr2->keyword) {
	case terminal_dw:
	    ptr2->value.dw_charset = 
		strmcpy(ptr2->value.dw_charset,
			ptr->value.dw_charset);
	    break;
	    
	case terminal_iso2022:
	case terminal_iso2022_mb:

	    ptr2->value.iso2022 = ptr->value.iso2022;
	    break;

	case terminal_iso2022_like:
	    ptr2->value.iso2022_like = ptr->value.iso2022_like;
	    break;

	case terminal_private:
	    ptr2->value.private.charset = ptr->value.private.charset;
	    
	    ptr2->value.private.bytes =
		s2us(strmcpy(us2s(ptr2->value.private.bytes),
			     us2s(ptr->value.private.bytes)));
	    if (ptr2->value.private.condition)
		free_term_cond(& (ptr2->value.private.condition));
	    
	    if (ptr->value.private.condition)
		ptr2->value.private.condition =
		    copy_term_cond(ptr->value.private.condition);
	    
	    break;

	case terminal_flag:
	    ptr2->value.flag = ptr->value.flag;
	    break;

	default:
	    panic("CHARSET PANIC",__FILE__,__LINE__,"change_terminal_map",
		  "unsupported type",0);
		

	}	
    }

}

void free_terminal_map(map) 
     struct terminal_map_item **map;
{

    struct terminal_map_item *ptr;

    for (ptr = *map; ptr && ptr->match; ptr++) {

	switch(ptr->keyword) {
	case terminal_dw:
	    if (ptr->value.dw_charset)
		free(ptr->value.dw_charset);
	    break;

	case terminal_iso2022:
	case terminal_iso2022_mb:
	    break;

	case terminal_iso2022_like:
	    break;
	    
	case terminal_private:
	    if (ptr->value.private.bytes)
		free(ptr->value.private.bytes);
	    ptr->value.private.bytes = NULL;

	    if (ptr->value.private.condition)
		free_term_cond(& (ptr->value.private.condition));
	    
	    break;

	case terminal_flag:
	    break;
     
	default:
	    panic("CHARSET PANIC",__FILE__,__LINE__,"free_terminal_map",
		  "unsupported type",0);
	}
    }
    
    free(*map);
    *map = NULL;
}


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

/* Hopefully quite safe to be called from signal handler */
static struct terminal_map_item * loc_info P_((const char *terminal, 
					       charset_t set,
					       int only_switch));

static struct terminal_map_item * loc_info(terminal,set, only_switch)
     CONST char *terminal; 
     charset_t set;
     int only_switch;
{
    int i;
    
    SIGDPRINT(Debug,9,(&Debug,"** Looking terminal %s (set %s)%s\n",
		       terminal,
		       set->MIME_name ? set->MIME_name : "<no MIME name>",
		       only_switch ? " charset switches only" : ""));

    for (i =  0; i < 2; i++) {
	struct terminal_map_item *ptr = user_terminal_map;

	if (i)
	    ptr = system_terminal_map;

	while (ptr && ptr->match) {
	    char * c = NULL;

	    if (0 == strcmp(ptr->match,terminal) ||
		( (c = strchr(ptr->match,'*')) &&
		  *(c+1) == '\0' &&
		  0 == strncmp(ptr->match,terminal,c - ptr->match))) {

		if (ptr->keyword == terminal_iso2022 &&
		    match_charset_name(ptr->value.iso2022.charset,set)) {
		    SIGDPRINT(Debug,9,(&Debug,"-- Found ISO2022 match %s\n",
				       ptr->match));

		    return ptr;	    
		}

		if (ptr->keyword == terminal_iso2022_mb &&
		    match_charset_name(ptr->value.iso2022.charset,set)) {
		    
		    SIGDPRINT(Debug,9,(&Debug,"-- Found ISO2022 MB match %s\n",
				       ptr->match));

		    return ptr;	    
		}

		if (ptr->keyword == terminal_iso2022_like &&
		    match_charset_name(ptr->value.iso2022_like.charset,set)) {
		    
		    SIGDPRINT(Debug,9,(&Debug,"-- Found ISO2022-LIKE match %s\n",
				       ptr->match));

		    return ptr;	    
		}

		if (ptr->keyword == terminal_private &&
		    match_charset_name(ptr->value.private.charset, set)) {
		    if (ptr->value.private.condition) {
			if (!eval_condition(ptr->value.private.condition)) {
			    goto no_this;
			}
		    }

		    SIGDPRINT(Debug,9,(&Debug,"-- Found provate match %s\n",
				       ptr->match));

		    return ptr;	    
		}

		if (ptr->keyword == terminal_dw && set->MIME_name &&
		    0 == istrcmp(set->MIME_name,ptr->value.dw_charset)) {

		    if (only_switch) {

			SIGDPRINT(Debug,9,(&Debug,"-- Found DW match %s (ignored)\n",
					   ptr->match));
			
			goto no_this;

		    }
		    SIGDPRINT(Debug,9,(&Debug,"-- Found DW match %s\n",
				       ptr->match));
			
		    return ptr;
		}

	    }
	no_this:
	    ptr++;
	}
    }
    return NULL;
}

void terminal_can_switch(terminal,storage,n,max)
     CONST char *terminal;
     charset_t *storage;
     int *n;
     int max;
{

    int i;
    struct terminal_map_item * back;

    /* Can not switch to other sets if can not
       switch back to display_charset
    */
    back = loc_info(terminal,display_charset,1);
    if (!back)
	return;


    for (i =  0; i < 2; i++) {
	struct terminal_map_item *ptr = user_terminal_map;
	
	if (i)
	    ptr = system_terminal_map;
	
	while (ptr && ptr->match && *n < max) {
	    char * c = NULL;
	    int j;

	    SIGDPRINT(Debug,9,(&Debug,"-- Looking %s ...\n",
			       ptr->match));

	    if (0 == strcmp(ptr->match,terminal) ||
		( (c = strchr(ptr->match,'*')) &&
		  *(c+1) == '\0' &&
		  0 == strncmp(ptr->match,terminal,c - ptr->match))) {
	
		switch (ptr->keyword) {
		case terminal_iso2022:
		case terminal_iso2022_mb:
		    SIGDPRINT(Debug,8,(&Debug,
				       "-- Adding %s (%p) to possible sets\n",
				       ptr->value.iso2022.charset->MIME_name ?
				       ptr->value.iso2022.charset->MIME_name :
				       "<no MIME name>",
				       ptr->value.iso2022.charset
				       ));
		    storage[(*n)++] = ptr->value.iso2022.charset;
		    break;

		case terminal_iso2022_like:
		    SIGDPRINT(Debug,8,(&Debug,
				       "-- Adding %s (%p) to possible sets\n",
				       ptr->value.iso2022_like.charset->MIME_name ?
				       ptr->value.iso2022_like.charset->MIME_name :
				       "<no MIME name>",
				       ptr->value.iso2022_like.charset
				       ));
		    storage[(*n)++] = ptr->value.iso2022_like.charset;
		    break;

		case terminal_private:
		    if (ptr->value.private.condition) {
			if (!eval_condition(ptr->value.private.condition)) {
			    SIGDPRINT(Debug,8,(&Debug,
					       "-- NOT adding %s (%p) to possible sets (condition fail)\n",
					       ptr->value.private.charset->MIME_name ?
					       ptr->value.private.charset->MIME_name :
					       "<no MIME name>",
					       ptr->value.private.charset
					       ));

			    goto no_this;
			}
		    }

		    SIGDPRINT(Debug,8,(&Debug,
				       "-- Adding %s (%p) to possible sets\n",
				       ptr->value.private.charset->MIME_name ?
				       ptr->value.private.charset->MIME_name :
				       "<no MIME name>",
				       ptr->value.private.charset
				       ));

		    storage[(*n)++] = ptr->value.private.charset;
		    break;

		}

		for (j = 0; j < (*n) -1; j++)
		    if (storage[j] == storage[(*n)-1]) {
			SIGDPRINT(Debug,8,(&Debug,
					   "-- Removing duplicate %s from possible sets\n",
					   storage[(*n)-1]->MIME_name ?
					   storage[(*n)-1]->MIME_name :
					   "<no MIME name>"));
			(*n)--;  /* Remove duplicate */  
			break;
		    }
	    }
	no_this:
	    ptr++;
	}
    }
}

int terminal_can_switch_to(terminal,set,silent)
     CONST char *terminal; 
     charset_t set;
     int silent;
{

    struct terminal_map_item * a = loc_info(terminal,set,1);

    if (!a) {
	if (!silent)
	    lib_error(CATGETS(elm_msg_cat, MeSet, 
			      MeNoInformationToSwitchTerminal,
			      "No information to switch %s terminal to %s charset!"), 
		      terminal,
		      set->MIME_name ? set->MIME_name : "<no MIME name>");
		      
	return 0;
    }
    
    a = loc_info(terminal,display_charset,1);

    if (!a) {
	if (!silent)
	    lib_error(CATGETS(elm_msg_cat, MeSet, 
			      MeNoInformationToSwitchBack,
			      "No information to switch %s terminal back to %s charset!"), 
		      terminal,
		      display_charset->MIME_name ? display_charset->MIME_name :
		      "<no MIME name>");
	return 0;
    }
    return 1;
}



/* Returns first flags found for terminal from list or terminal_bad_flag, 
   flags[] is terminated with terminal_bad_flag

   Hopefully quite safe to be called from signal handler 
*/
static enum terminal_flags find_terminal_flag P_((const char *terminal,
						  enum terminal_flags flags[]));

static enum terminal_flags find_terminal_flag(terminal,flags)
     CONST char *terminal;
     enum terminal_flags flags[];
{

    int i,j;

    SIGDPRINT(Debug,9,(&Debug,"** Looking terminal %s (flags",
		       terminal));
    for (j = 0; terminal_bad_flag != flags[j]; j++) {
	SIGDPRINT(Debug,9,(&Debug," %d",flags[j]));
    }
    SIGDPRINT(Debug,9,(&Debug,")\n"));
    
    for (i =  0; i < 2; i++) {
	struct terminal_map_item *ptr = user_terminal_map;

	if (i)
	    ptr = system_terminal_map;

	while (ptr && ptr->match) {
	    char * c = NULL;

	    if (0 == strcmp(ptr->match,terminal) ||
		( (c = strchr(ptr->match,'*')) &&
		  *(c+1) == '\0' &&
		  0 == strncmp(ptr->match,terminal,c - ptr->match))) {


		if (ptr->keyword == terminal_flag) {
		    int j;

		    for (j = 0; terminal_bad_flag != flags[j]; j++) {

			if (ptr->value.flag == flags[j]) {
			    SIGDPRINT(Debug,9,(&Debug,
					       "-- Found flag match %s (flag %d)\n",
					       ptr->match,flags[j]));

			    return flags[j];
			}

		    }		   
		}

	    }
	    ptr++;
	}
    }

    return terminal_bad_flag; 
}


/* May be called from signal handler -- on that situation use
   buffer specified as argument -- otherwise result is malloced
*/
char * terminal_set_title(terminal,window_title, icon_title,buffer,size)
     char *terminal; 
     char *window_title;
     char *icon_title;
     char * buffer; 
     int size;
{
    /* window_title and icon_title are assumed to be US-ASCII ... */

    int maybe_signal = buffer != NULL;

    char *ret = NULL;

    static enum terminal_flags methods[] = {
	terminal_xterm_title,
	terminal_xwsh_title,
	terminal_bad_flag
    };

    enum terminal_flags method = find_terminal_flag(terminal,methods);

    if (maybe_signal) {
	SIGDPRINT(Debug,8,(&Debug,
			   "terminal_set_title: buffer=%p size=%d\n",
			   buffer,size));
    }


    switch (method) {
    case terminal_xterm_title:
	if (window_title == icon_title) {  /* Optimize */

	    if (window_title) {
		strXcat(&ret,"\033]0;",
			maybe_signal,buffer,size);
		strXcat(&ret,window_title,
			maybe_signal,buffer,size);
		strXcat(&ret,"\007",
			maybe_signal,buffer,size);
	    }
	} else {
	    if (icon_title) {
		strXcat(&ret,"\033]1;",
			maybe_signal,buffer,size);
		strXcat(&ret,icon_title,
			maybe_signal,buffer,size);
		strXcat(&ret,"\007",
			maybe_signal,buffer,size);
	    }

	    if (window_title) {
		strXcat(&ret,"\033]2;",
			maybe_signal,buffer,size);
		strXcat(&ret,window_title,
			maybe_signal,buffer,size);
		strXcat(&ret,"\007",
			maybe_signal,buffer,size);
	    }

	}
	break;
    case terminal_xwsh_title:

	if (icon_title) {
	    strXcat(&ret,"\033P3.y",
		    maybe_signal,buffer,size);
	    strXcat(&ret,icon_title,
		    maybe_signal,buffer,size);
	    strXcat(&ret,"\033\\",
		    maybe_signal,buffer,size);
	}

	if (window_title) {
	    strXcat(&ret,"\033P1.y",
		    maybe_signal,buffer,size);
	    strXcat(&ret,window_title,
		    maybe_signal,buffer,size);
	    strXcat(&ret,"\033\\",
		    maybe_signal,buffer,size);
	}

    case terminal_bad_flag:
	SIGDPRINT(Debug,8,(&Debug,
			   "terminal_set_title: No way to set title for %s\n",
			   terminal));
	break;
    }


    if (maybe_signal && ret && ret != buffer)
	panic("STRING PANIC",__FILE__,__LINE__,
	      "terminal_set_title",
	      "buffer != ret when called from signal",1);
    
    return ret;
}


void terminal_change_system_charset(terminal)
     char *terminal; 
{
    struct terminal_map_item *    a = loc_info(terminal,system_charset,0);

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

#if defined(WCHAR) && defined(__STDC_ISO_10646__)
    if (a && a->keyword == terminal_dw) {
	change_system_charset_1();
    }
#endif

}

static void change_helper_2 P_((char **ret,struct iso2022_setid ID,
				screen_info_p terminal_info,   
				char * buffer,
				int size, 
				int maybe_signal,
				int setpos
				));

static void change_helper_2(ret,ID,terminal_info,buffer,size,maybe_signal,setpos)
     char **ret;
     struct iso2022_setid ID;
     screen_info_p terminal_info;   
     char * buffer; 
     int size;
     int maybe_signal;
     int setpos;
{   
    if (maybe_signal) { 
	if (!*ret)
	    *ret = 
		iso2022_change_helper_1(terminal_info,ID,
					setpos, 
					buffer,size);
	else {
	    char buffer1[100];
	    char *c = 
		iso2022_change_helper_1(terminal_info,ID,
					setpos, 
					buffer1,sizeof buffer1);
	    if (c) 
		*ret = strfcat(buffer,c,size);
	}

    } else {
	char *c = 
	    iso2022_change_helper_1(terminal_info,ID,
				    setpos, 
				    NULL,0);
	if (c) {
	    *ret = strmcat(*ret,c);
	    free(c);
	}
    }
}


static void change_helper_3 P_((char **RET,struct setlist * iso2022_info,
				screen_info_p terminal_info,   
				char * buffer,
				int size, 
				int maybe_signal,
				int MB));

static void change_helper_3(RET,iso2022_info,terminal_info,buffer,size,
			    maybe_signal,MB)
     char **RET;
     struct setlist * iso2022_info;
     screen_info_p terminal_info;   
     char * buffer;
     int size; 
     int maybe_signal;
     int MB;
{
    char * ret = *RET;
    int i;    
	    
    if (bank_unspecified != iso2022_info->initial_L) {
	terminal_info->current_L = iso2022_info->initial_L;

	if (maybe_signal) {
	    if (!ret)
		ret = lock_shift(0,terminal_info->current_L,buffer,
				 size);
	    else {
		char buffer1[10];
		char * c = lock_shift(0,terminal_info->current_L,
				      buffer1,sizeof buffer1);
		ret = strfcat(buffer,c,size);
	    }
		    
	} else {
	    char *c = lock_shift(0,terminal_info->current_L,NULL,0);
	    ret = strmcat(ret,c);
	    free(c);
	}	       
    }
    
    if (bank_unspecified != iso2022_info->initial_R) {
	terminal_info->current_R = iso2022_info->initial_R;
		
	if (maybe_signal) {
	    if (!ret)
		ret = lock_shift(1,terminal_info->current_R,
				 buffer,size);
	    else {
		char buffer1[10];
		char *c =  lock_shift(1,terminal_info->current_R,
				      buffer1,sizeof buffer1);
		ret = strfcpy(buffer,c,size);
	    }
	} else {
	    char *c = lock_shift(1,terminal_info->current_R,NULL,0);
	    ret = strmcat(ret,c);
	    free(c);
	}
    }
	    
    for (i = 0; 
	 i < sizeof (iso2022_info->initial_bank) / 
	     sizeof (iso2022_info->initial_bank[0]);
	 i++) {
	int x = iso2022_info->initial_bank[i];
		
	if (-1 != x) {
	    int n;
	    struct iso2022_setid ID;

	    if (x < 0 ||
		x >= sizeof (iso2022_info->sets)  /
			sizeof (iso2022_info->sets[0]) ||
			! iso2022_info->sets[x]) 
		panic("STRING PANIC",__FILE__,__LINE__,
		      "change_helper_3",
		      "Bad initial_bank (set)",
		      maybe_signal);
	       
	    ID = * (iso2022_info->sets[x]);
		    
	    if (ID.bank != i)
		panic("STRING PANIC",__FILE__,__LINE__,
		      "change_helper_3",
		      "Bad initial_bank (bank number)",
		      maybe_signal);
		    
	    n = set_initial_bank(&ret,ID,terminal_info,buffer,size,
				 maybe_signal);

	    /* Do not support variable width inside of bank */
	    if (MB &&
		(iso2022_94x94 == ID.type ||
		 iso2022_96x96 == ID.type))
		terminal_info->width[n]  = 2;
	    else
		terminal_info->width[n]  = 1;	  
	    
	}
    }

#ifdef WCHAR
    terminal_info->wcwidth       = MB;
#endif

    for (i = 0; 
	 i < sizeof (iso2022_info->sets)  / 
	     sizeof (iso2022_info->sets[0]) &&
	     iso2022_info->sets[i];
	 i++) {
	int setpos;
	struct iso2022_setid ID = * (iso2022_info->sets[i]);

	/* iso2022_give_setpos does iso2022_setid_select_bank() */
	setpos = iso2022_give_setpos(&ID,terminal_info);

	if (-1 == setpos) {
	    if ((terminal_info)->set_count >=
			sizeof ((terminal_info)->sets) /
			sizeof ((terminal_info)->sets[0]))
			panic("STRING PANIC",__FILE__,__LINE__,
			      "change_helper_3",
			      "Too many iso 2022 sets on display charset",
			      maybe_signal);

		    setpos = terminal_info->set_count++;
		    terminal_info->sets[setpos] = ID;
	}

	change_helper_2(&ret,ID,terminal_info,buffer,size,
			maybe_signal,setpos);
		
	/* Do not support variable width inside of bank */
	if (MB &&
	    (iso2022_94x94 == ID.type ||
	     iso2022_96x96 == ID.type))
	    terminal_info->width[setpos]  = 2;
	else
	    terminal_info->width[setpos]  = 1;
    }

    debug_display_settings(terminal_info,maybe_signal,0);
	   
    *RET = ret;
}



char * terminal_set_info (terminal,set,terminal_info)
     char *terminal; 
     charset_t set;
     screen_info_p terminal_info;   
{
    char * ret = NULL;
    struct terminal_map_item * a;

    if (!terminal_info) {
	panic("STRING PANIC",__FILE__,__LINE__,
	      "terminal_set_info",
	      "terminal_info not set",0);
    } 
    
    if (DISPLAY_STATE_magic != terminal_info->magic)
	panic("STRING PANIC",__FILE__,__LINE__,
	      "terminal_set_info",
	      "Bad terminal_info (bad magic)",
	      0);

    /* We do not reset bank settings on here */
#ifdef WCHAR
    terminal_info->wcwidth             = 0;
#endif

    a = loc_info(terminal,set,0);
    if (!a)
	return ret;

    switch (a->keyword) {
	case terminal_dw: {
	    static int warned = 0;
	    
#ifdef WCHAR
	    if (&cs_unknown  != set->charset_type ||
		0 == (CS_printable_len & charset_properties(set))) {
		
		if (!warned)
		    lib_error(CATGETS(elm_msg_cat, MeSet, 
				      MeUnsupportedDW,
				      "Double wide characters are not supported"));
		
		warned = 1;
		return NULL;
	    }
	 
	    terminal_info->wcwidth             = 1;

#else
		
	    if (!warned)
		lib_error(CATGETS(elm_msg_cat, MeSet, 
				  MeUnsupportedDW,
				  "Double wide characters are not supported"));
	    
	    warned = 1;	    
#endif	   
	}
	    break;

    case terminal_iso2022_mb: {
	static int warned = 0;

	if (0 == (CS_printable_len & charset_properties(set))) {
		
	    if (!warned)
		lib_error(CATGETS(elm_msg_cat, MeSet, 
				  MeUnsupportedDW,
				  "Double wide characters are not supported"));
	    
	    warned = 1;
	    return ret;
	}

	if (set->iso2022_info)
	  change_helper_3(&ret,set->iso2022_info,terminal_info,NULL,0,0,1);

#ifdef WCHAR
	terminal_info->wcwidth             = 1;
#endif
    }
	break;
    }


#ifdef WCHAR
    SIGDPRINT(Debug,8,(&Debug,
		       "terminal_set_info: wcwidth=%d\n",
		       terminal_info->wcwidth));
#endif    



    return ret;
}




/* May be called from signal handler -- on that situation use
   buffer specified as argument -- otherwise result is malloced
*/
char * terminal_switch_to(terminal,set,terminal_info,buffer,size,silent)
     char *terminal; 
     charset_t set;
     screen_info_p terminal_info;   
     char * buffer; 
     int size;
     int silent;
{
    int maybe_signal = buffer != NULL;

    char *ret = NULL;
    struct terminal_map_item * a = loc_info(terminal,set,1);

    if (!terminal_info) {
	panic("STRING PANIC",__FILE__,__LINE__,
	      "terminal_switch_to",
	      "terminal_info not set",maybe_signal);
    } 
    
    if (DISPLAY_STATE_magic != (terminal_info)->magic)
	panic("STRING PANIC",__FILE__,__LINE__,
	      "terminal_switch_to",
	      "Bad terminal_info (bad magic)",
	      maybe_signal);
    
   
    if (!a) {
	return NULL;
    }


    if (maybe_signal) {
	SIGDPRINT(Debug,8,(&Debug,
			   "terminal_switch_to: buffer=%p size=%d\n",
			   buffer,size));
    }

    if ((terminal_info)->set_count > 0 &&     
	(terminal_info)->sets[0].type == iso2022_other) {
	SIGDPRINT(Debug,8,(&Debug,
			   "Terminal was on no ISO2022 mode ... returning\n"));
	if (maybe_signal)
	    ret = iso2022_setid_stream(return_to_iso2022,buffer,size);
	else
	    ret = iso2022_setid_stream(return_to_iso2022,NULL,0);
    }
    
    reset_display_settings(terminal_info);

    switch(a->keyword) {
	int mb;
    case terminal_iso2022:
    case terminal_iso2022_mb:
	if (set->iso2022_info) {
	    SIGDPRINT(Debug,8,(&Debug,
			       "Using ISO 2022 code for assignment\n"));
	    
	    mb = 0;
	    
	    if (a->keyword == terminal_iso2022_mb) {	    
		if (0 != (CS_printable_len & charset_properties(set)))
		    mb = 1;
		else if (!silent) {		    
		    static int warned = 0;
		    
		    if (!warned)
			lib_error(CATGETS(elm_msg_cat, MeSet, 
					  MeUnsupportedDW,
					  "Double wide characters are not supported"));
		    
		    warned = 1;
		} 
	    }
	    
	    change_helper_3(&ret,set->iso2022_info,terminal_info,
			    buffer,size,maybe_signal,
			    mb);
	} else {
	    SIGDPRINT(Debug,1,(&Debug,
			       "No ISO 2022 code for changing charset!\n"));

	}
	break;
    
    case terminal_iso2022_like:

	SIGDPRINT(Debug,8,(&Debug,
			   "Using ISO 2022 LIKE code for assignment\n"));
	
	change_helper_3(&ret,a->value.iso2022_like.iso2022_info,
			terminal_info,
			buffer,size,maybe_signal,0);
	
	break;

    case terminal_private:
	SIGDPRINT(Debug,8,(&Debug,
			   "Using terminal private code for assignment\n"));
	
	
	strXcat(&ret,sStr(a->value.private.bytes),
		maybe_signal,buffer,size);

	break;
	

    }

#ifdef WCHAR
    SIGDPRINT(Debug,8,(&Debug,
		       "terminal_switch_to: wcwidth=%d\n",
		       (terminal_info)->wcwidth));
#endif

    if (maybe_signal && ret && ret != buffer)
	panic("STRING PANIC",__FILE__,__LINE__,
	      "terminal_switch_to",
	      "buffer != ret when called from signal",1);
    
    return ret;
}



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



syntax highlighted by Code2HTML, v. 0.9.1