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 * or Kari Hurtta *****************************************************************************/ #include "def_misc.h" #include "s_me.h" #include "cs_imp.h" #include "cs_terminal.h" DEBUG_VAR(Debug,__FILE__,"charset"); #include #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 : "", 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 : "", 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 : "", 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("# ",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 : "", 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 : "", 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 : "", 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 : "", 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 : "", 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 : "")); (*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 : ""); 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 : ""); 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: */