1 /*
2  * $LynxId: UCAuto.c,v 1.51 2013/05/04 13:14:39 tom Exp $
3  *
4  *  This file contains code for changing the Linux console mode.
5  *  Currently some names for font files are hardwired in here.
6  *  You have to change this code if it needs accommodation for your
7  *  system (or get the required files...).
8  *
9  *  Depending on the Display Character Set switched to, and the previous
10  *  one as far as it is known, system("setfont ...") and/or output of
11  *  escape sequences to switch console mode are done.  Curses will be
12  *  temporarily suspended while that happens.
13  *
14  *  NOTE that the setfont calls will also affect all other virtual consoles.
15  *
16  *  Any ideas how to do this for other systems?
17  */
18 
19 #include <HTUtils.h>
20 #include <LYUtils.h>
21 
22 #include <UCMap.h>
23 #include <UCDefs.h>
24 #include <UCAuto.h>
25 #include <LYGlobalDefs.h>
26 #include <LYStrings.h>
27 #include <LYClean.h>
28 #include <LYLeaks.h>
29 #include <LYCharSets.h>
30 
31 #ifdef EXP_CHARTRANS_AUTOSWITCH
32 
33 #include <HTFile.h>
34 #include <www_wait.h>
35 
36 #ifdef LINUX
37 #include <sysexits.h>		/* EX_DATAERR, etc. */
38 #endif
39 
40 #  ifdef CAN_SWITCH_DISPLAY_CHARSET
41 char *charset_switch_rules;
42 char *charsets_directory;
43 int auto_other_display_charset = -1;
44 int codepages[2];
45 int real_charsets[2] =
46 {-1, -1};			/* Non "auto-" charsets for the cps */
47 int switch_display_charsets;
48 
49 #  endif
50 
51 #ifdef HAVE_USE_LEGACY_CODING
52 static int original_coding = 0;
53 #endif
54 
55 #  ifdef __EMX__
56 /* If we "just include" <os2.h>, BOOLEAN conflicts. */
57 #  define BOOLEAN OS2_BOOLEAN	/* This file doesn't use it, conflicts */
58 #  define INCL_VIO		/* I want some Vio functions.. */
59 #  define INCL_DOSPROCESS	/* TIB PIB. */
60 #  define INCL_DOSNLS		/* DosQueryCp. */
61 #  include <os2.h>		/* Misc stuff.. */
62 #  include <os2thunk.h>		/* 32 bit to 16 bit pointer conv */
63 #  endif
64 
65 #ifdef LINUX
66 typedef enum {
67     Is_Unset,
68     Is_Set,
69     Dunno,
70     Dont_Care
71 } TGen_state_t;
72 
73 /*
74  * List the states the console has been set to via SCS (select character-set).
75  */
76 typedef enum {
77     GN_Blat1,			/* Latin-1 */
78     GN_Ucp437,			/* PC -> PC */
79     GN_Kuser,			/* user-defined */
80     GN_dunno,
81     GN_dontCare
82 } TTransT_t;
83 
84 static char *T_font_fn = NULL;	/* font filename */
85 static char *T_umap_fn = NULL;	/* unicode-map filename */
86 
87 #define NOOUTPUT "2>/dev/null >/dev/null"
88 
89 /*
90  * Return the configured path of the setfont/consolechars program.
91  */
GetSetfontPath(void)92 static const char *GetSetfontPath(void)
93 {
94     return HTGetProgramPath(ppSETFONT);
95 }
96 
97 /*
98  * setfont and consolechars have different options and available data.
99  */
isSetFont(void)100 static BOOL isSetFont(void)
101 {
102     const char *program = GetSetfontPath();
103     const char *slash = strrchr(program, '/');
104     const char *leaf = (slash ? slash + 1 : program);
105 
106     return (BOOL) !strcmp(leaf, "setfont");
107 }
108 
109 /*
110  * Here are the differences in options which affect lynx:
111  */
112 #define setfont_u()    (isSetFont() ? "-u "  : "--sfm ")
113 #define setfont_o()    (isSetFont() ? "-o "  : "--old-font-raw ")
114 #define setfont_ou()   (isSetFont() ? "-ou " : "--old-sfm ")
115 #define console_font() (isSetFont() ? ""     : "--font ")
116 
117 /*
118  * call_setfont - execute "setfont" command via system()
119  * returns:	 0  ok (as far as we know)
120  *		-1  error (assume font and umap are not loaded)
121  *		 1  error with umap (assume font loaded but umap empty)
122  */
call_setfont(const char * font,const char * fnsuffix,const char * umap)123 static int call_setfont(const char *font,
124 			const char *fnsuffix,
125 			const char *umap)
126 {
127     const char *program = GetSetfontPath();
128     char *T_setfont_cmd = NULL;
129     int rv;
130 
131     /*
132      * console-data package has only a few unicode maps.
133      */
134     if (!isSetFont())
135 	umap = 0;
136 
137     if ((font && T_font_fn && !strcmp(font, T_font_fn))
138 	&& (umap && T_umap_fn && !strcmp(umap, T_umap_fn))) {
139 	/*
140 	 * No need to repeat.
141 	 */
142 	return 0;
143     }
144     if (font)
145 	StrAllocCopy(T_font_fn, font);
146     if (umap)
147 	StrAllocCopy(T_umap_fn, umap);
148 
149     if (!*fnsuffix)
150 	fnsuffix = "";
151 
152     if (non_empty(umap) && non_empty(font)) {
153 	HTSprintf0(&T_setfont_cmd, "%s %s%s%s %s%s %s",
154 		   program,
155 		   console_font(), font, fnsuffix,
156 		   setfont_u(), umap,
157 		   NOOUTPUT);
158     } else if (non_empty(font)) {
159 	HTSprintf0(&T_setfont_cmd, "%s %s%s%s %s",
160 		   program,
161 		   console_font(), font, fnsuffix,
162 		   NOOUTPUT);
163     } else if (non_empty(umap)) {
164 	HTSprintf0(&T_setfont_cmd, "%s %s%s %s",
165 		   program,
166 		   setfont_u(), umap,
167 		   NOOUTPUT);
168     }
169 
170     if (T_setfont_cmd) {
171 	CTRACE((tfp, "Changing font: '%s'\n", T_setfont_cmd));
172 	rv = LYSystem(T_setfont_cmd);
173 	FREE(T_setfont_cmd);
174 	if (rv) {
175 	    CTRACE((tfp, "call_setfont: system returned %d (0x%x)!\n",
176 		    rv, rv));
177 	    if (rv == -1 || WIFSIGNALED(rv) || !WIFEXITED(rv)) {
178 		return -1;
179 	    } else if ((WEXITSTATUS(rv) == EX_DATAERR ||
180 			WEXITSTATUS(rv) == EX_NOINPUT) &&
181 		       non_empty(umap)) {
182 		/*
183 		 * Check if the font was loaded ok but something was wrong with
184 		 * the umap file.
185 		 */
186 		return 1;
187 	    } else {
188 		return -1;
189 	    }
190 	}
191     }
192     return 0;
193 }
194 
write_esc(const char * p)195 static void write_esc(const char *p)
196 {
197     int fd = open("/dev/tty", O_WRONLY);
198 
199     if (fd >= 0) {
200 	IGNORE_RC(write(fd, p, strlen(p)));
201 	close(fd);
202     }
203 }
204 
nonempty_file(const char * p)205 static int nonempty_file(const char *p)
206 {
207     struct stat sb;
208 
209     return (stat(p, &sb) == 0 &&
210 	    S_ISREG(sb.st_mode) &&
211 	    (sb.st_size != 0));
212 }
213 
on_console(void)214 static BOOL on_console(void)
215 {
216     if ((x_display != NULL) ||
217 	LYgetXDisplay() != NULL) {
218 	/*
219 	 * We won't do anything in an xterm.  Better that way...
220 	 */
221 	return FALSE;
222     }
223     return TRUE;
224 }
225 
226 /*
227  * This is the thing that actually gets called from display_page().
228  */
UCChangeTerminalCodepage(int newcs,LYUCcharset * p)229 void UCChangeTerminalCodepage(int newcs,
230 			      LYUCcharset *p)
231 {
232     const char *program = GetSetfontPath();
233     static int lastcs = -1;
234     static const char *lastname = NULL;
235     static TTransT_t lastTransT = GN_dunno;
236     static TGen_state_t lastUtf = Dunno;
237     static TGen_state_t lastHasUmap = Dunno;
238 
239     static char *old_font = NULL;
240     static char *old_umap = NULL;
241 
242     const char *name;
243     TTransT_t TransT = GN_dunno;
244     TGen_state_t Utf = Dunno;
245     TGen_state_t HasUmap = Dunno;
246 
247     char *tmpbuf1 = NULL;
248     char *tmpbuf2 = NULL;
249     int status = 0;
250 
251     if (!on_console())
252 	return;
253 
254 #ifdef HAVE_USE_LEGACY_CODING
255     if (newcs < 0) {
256 	use_legacy_coding(original_coding);
257     } else {
258 	original_coding = use_legacy_coding(2);
259     }
260 #endif
261 
262     /*
263      * Restore the original character set.
264      */
265     if (newcs < 0 || p == 0) {
266 	if (non_empty(old_font) &&
267 	    non_empty(old_umap)) {
268 
269 	    if (nonempty_file(old_font)) {
270 		if (nonempty_file(old_umap)) {
271 		    HTSprintf0(&tmpbuf1, "%s %s%s %s%s %s",
272 			       program,
273 			       console_font(), old_font,
274 			       setfont_u(), old_umap,
275 			       NOOUTPUT);
276 		} else {
277 		    HTSprintf0(&tmpbuf1, "%s %s%s %s",
278 			       program,
279 			       console_font(), old_font,
280 			       NOOUTPUT);
281 		}
282 		CTRACE((tfp, "Restoring font: '%s'\n", tmpbuf1));
283 		status = LYSystem(tmpbuf1);
284 		if (status != 0) {
285 		    CTRACE((tfp, "...system returned %d (0x%x)\n", status, status));
286 		}
287 		FREE(tmpbuf1);
288 	    }
289 	}
290 	if (newcs < 0 && p == 0) {
291 	    if (old_font) {
292 		(void) LYRemoveTemp(old_font);
293 		FREE(old_font);
294 	    }
295 	    if (old_umap) {
296 		(void) LYRemoveTemp(old_umap);
297 		FREE(old_umap);
298 	    }
299 	    if (status == 0) {
300 		FREE(T_font_fn);
301 		FREE(T_umap_fn);
302 	    }
303 	}
304 	return;
305     } else if (lastcs < 0 && old_umap == 0 && old_font == 0) {
306 	FILE *fp1;
307 	FILE *fp2 = NULL;
308 
309 	if ((old_font = typecallocn(char, LY_MAXPATH)) != 0)
310 	      old_umap = typecallocn(char, LY_MAXPATH);
311 
312 	if (old_font == NULL)
313 	    outofmem(__FILE__, "UCChangeTerminalCodepage");
314 
315 	assert(old_font != NULL);
316 
317 	if ((fp1 = LYOpenTemp(old_font, ".fnt", BIN_W)) != 0)
318 	    fp2 = LYOpenTemp(old_umap, ".uni", BIN_W);
319 
320 	if (fp1 && fp2) {
321 	    size_t nlen;
322 	    int rv;
323 
324 	    HTSprintf0(&tmpbuf1, "%s %s%s %s%s %s",
325 		       program,
326 		       setfont_o(), old_font,
327 		       setfont_ou(), old_umap,
328 		       NOOUTPUT);
329 
330 	    CTRACE((tfp, "Saving font: '%s'\n", tmpbuf1));
331 	    rv = LYSystem(tmpbuf1);
332 	    if (rv != 0) {
333 		CTRACE((tfp, "...system returned %d (0x%x)\n", rv, rv));
334 	    }
335 	    FREE(tmpbuf1);
336 	    LYCloseTempFP(fp1);
337 	    LYCloseTempFP(fp2);
338 
339 	    /* free up a few bytes */
340 	    if ((nlen = strlen(old_font) + 1) < LY_MAXPATH)
341 		old_font = typeRealloc(char, old_font, nlen);
342 
343 	    if ((nlen = strlen(old_umap) + 1) < LY_MAXPATH)
344 		old_umap = typeRealloc(char, old_umap, nlen);
345 	} else {
346 	    if (fp1)
347 		(void) LYRemoveTemp(old_font);
348 	    FREE(old_font);
349 	    FREE(old_umap);
350 	}
351     }
352 
353     name = p->MIMEname;
354 
355     /*
356      * Font sizes are currently hardwired here.
357      */
358 #define SUFF1 ".f16"
359 #define SUFF2 "-16.psf"
360 #define SUFF3 "-8x16"
361 #define SUFF4 "8x16"
362 #define SUFF5 ".cp -16"
363 #define SUFF6 "_8x16"
364 
365     /* NOTE: `!!umap not in kbd!!' comments below means that the *.uni file
366      * is not found in kbd package.  Reference Debian Package: kbd-data,
367      * Version: 0.96a-14.  They should be located elsewhere or generated.
368      * Also some cpNNN fonts used below are not in the kbd-data.  - kw
369      */
370 
371     if (!StrNCmp(name, "iso-8859-1", 10) &&
372 	(!name[10] || !isdigit(UCH(name[10])))) {
373 	if ((lastHasUmap == Is_Set) && !strcmp(lastname, "cp850")) {
374 	    /*
375 	     * cp850 already contains all latin1 characters.
376 	     */
377 	    if (lastTransT != GN_Blat1) {
378 		TransT = GN_Blat1;
379 	    }
380 	} else {
381 	    /*
382 	     * "setfont lat1u-16.psf -u lat1u.uni"
383 	     */
384 	    status = call_setfont("lat1u", SUFF2, "lat1u.uni");
385 	    HasUmap = Is_Set;
386 	    if (lastTransT != GN_Blat1) {
387 		TransT = GN_Blat1;
388 	    }
389 	}
390 	Utf = Is_Unset;
391     } else if (!strcmp(name, "iso-8859-2")) {
392 	/*
393 	 * "setfont iso02.f16 -u iso02.uni"
394 	 */
395 	status = call_setfont("iso02", SUFF1, "iso02.uni");
396 	TransT = GN_Kuser;
397 	HasUmap = Is_Set;
398 	Utf = Is_Unset;
399     } else if (!strcmp(name, "iso-8859-15")) {
400 	/*
401 	 * "setfont lat0-16.psf"
402 	 */
403 	status = call_setfont("lat0", SUFF2, NULL);
404 	TransT = GN_Blat1;	/* bogus! */
405 	HasUmap = Dunno;	/* distributed lat0 files have bogus map data! */
406 	Utf = Is_Unset;
407     } else if (!StrNCmp(name, "iso-8859-", 9)) {
408 	if (strlen(name) <= 10 || !isdigit(UCH(name[10])))
409 	    HTSprintf0(&tmpbuf1, "iso0%s", &name[9]);
410 	else
411 	    HTSprintf0(&tmpbuf1, "iso%s", &name[9]);
412 	HTSprintf0(&tmpbuf2, "%s.uni", tmpbuf1);
413 	/*
414 	 * "setfont iso0N.f16 -u iso0N.uni"
415 	 */
416 	status = call_setfont(tmpbuf1, SUFF1, tmpbuf2);
417 	FREE(tmpbuf1);
418 	FREE(tmpbuf2);
419 	TransT = GN_Kuser;
420 	HasUmap = Is_Set;
421 	Utf = Is_Unset;
422     } else if (!strcmp(name, "koi8-r")) {
423 	/*
424 	 * "setfont koi8-8x16"
425 	 * !!umap not in kbd!!
426 	 */
427 	status = call_setfont("koi8", SUFF3, "koi8r.uni");
428 	TransT = GN_Kuser;
429 	HasUmap = Is_Set;
430 	Utf = Is_Unset;
431     } else if (!strcmp(name, "koi8-u")) {
432 	/*
433 	 * "setfont koi8u_8x16"
434 	 * !!umap not in kbd!!
435 	 */
436 	status = call_setfont("koi8u", SUFF6, "koi8u.uni");
437 	TransT = GN_Kuser;
438 	HasUmap = Is_Set;
439 	Utf = Is_Unset;
440     } else if (!strcmp(name, "cp437")) {
441 	/*
442 	 * "setfont default8x16 -u cp437.uni"
443 	 */
444 	status = call_setfont("default", SUFF4, "cp437.uni");
445 	if (lastTransT == GN_Kuser || lastTransT == GN_Ucp437)
446 	    TransT = GN_dontCare;
447 	else
448 	    TransT = GN_Ucp437;
449 	HasUmap = Is_Set;
450 	Utf = Is_Unset;
451     } else if (!strcmp(name, "cp850")) {
452 	/*
453 	 * "setfont cp850-8x16 -u cp850.uni"
454 	 * !!umap not in kbd!!
455 	 */
456 	status = call_setfont("cp850", SUFF3, "cp850.uni");
457 	TransT = GN_Kuser;
458 	HasUmap = Is_Set;
459 	Utf = Is_Unset;
460     } else if (!strcmp(name, "cp866") ||
461 	       !strcmp(name, "cp852") ||
462 	       !strcmp(name, "cp862")) {	/* MS-Kermit has these files */
463 	HTSprintf0(&tmpbuf2, "%s.uni", name);
464 	/*
465 	 * "setfont cpNNN.f16"
466 	 * !!umap not in kbd!!
467 	 */
468 	status = call_setfont(name, SUFF1, tmpbuf2);
469 	FREE(tmpbuf2);
470 	TransT = GN_Kuser;
471 	HasUmap = Is_Set;
472 	Utf = Is_Unset;
473     } else if (!strcmp(name, "cp737")) {
474 	/*
475 	 * "setfont cp737.cp"
476 	 * !!umap not in kbd!!
477 	 */
478 	if (isSetFont()) {
479 	    status = call_setfont("737", SUFF5, "cp737.uni");
480 	} else {
481 	    status = call_setfont("greek", "", "cp737.uni");
482 	}
483 	TransT = GN_Kuser;
484 	HasUmap = Is_Set;
485 	Utf = Is_Unset;
486     } else if (!strcmp(name, "cp857")) {
487 	status = call_setfont("cp857", SUFF3, "cp857.uni");
488 	TransT = GN_Kuser;
489 	HasUmap = Is_Set;
490 	Utf = Is_Unset;
491     } else if (!strcmp(name, "x-transparent")) {
492 	Utf = Dont_Care;
493     } else if (!strcmp(name, "us-ascii")) {
494 	Utf = Dont_Care;
495     } else if (!StrNCmp(name, "mnem", 4)) {
496 	Utf = Dont_Care;
497     }
498 
499     if (status == 1)
500 	HasUmap = Is_Unset;
501     else if (status < 0) {
502 	if (HasUmap == Is_Set)
503 	    HasUmap = Dunno;
504 	name = "unknown-8bit";
505     }
506 
507     if (TransT != lastTransT) {
508 	if (TransT == GN_Blat1) {
509 	    /*
510 	     * Switch Linux console to lat1 table.
511 	     */
512 	    write_esc("\033(B");
513 	} else if (TransT == GN_Ucp437) {
514 	    /*
515 	     * Switch Linux console to 437 table?
516 	     */
517 	    write_esc("\033(U");
518 	} else if (TransT == GN_Kuser) {
519 	    /*
520 	     * Switch Linux console to user table.
521 	     */
522 	    write_esc("\033(K");
523 	}
524 	if (TransT != GN_dunno && TransT != GN_dontCare) {
525 	    lastTransT = TransT;
526 	}
527     }
528 
529     if (HasUmap != Dont_Care && HasUmap != Dunno)
530 	lastHasUmap = HasUmap;
531 
532     if (p->enc == UCT_ENC_UTF8) {
533 	if (lastUtf != Is_Set) {
534 	    Utf = Is_Set;
535 	    /*
536 	     * Turn Linux console UTF8 mode ON.
537 	     */
538 	    write_esc("\033%G");
539 	    lastUtf = Utf;
540 	}
541 	return;
542     } else if (lastUtf == Is_Set && Utf != Dont_Care) {
543 	Utf = Is_Unset;
544 	/*
545 	 * Turn Linux console UTF8 mode OFF.
546 	 */
547 	write_esc("\033%@");
548 	lastUtf = Utf;
549     }
550 
551     if (Utf != Dont_Care && Utf != Dunno)
552 	lastUtf = Utf;
553 
554     lastcs = newcs;
555     lastname = name;
556 }
557 
558 #else /* Not LINUX: */
559 /*
560  * This is the thing that actually gets called from display_page().
561  */
UCChangeTerminalCodepage(int newcs,LYUCcharset * p)562 void UCChangeTerminalCodepage(int newcs,
563 			      LYUCcharset *p)
564 {
565 #ifdef __EMX__
566     int res = 0;
567 
568 #ifdef HAVE_USE_LEGACY_CODING
569     if (newcs < 0) {
570 	use_legacy_coding(original_coding);
571     } else {
572 	original_coding = use_legacy_coding(2);
573     }
574 #endif
575 
576     if (newcs < 0)
577 	newcs = auto_display_charset;
578     res = Switch_Display_Charset(newcs, SWITCH_DISPLAY_CHARSET_REALLY);
579     CTRACE((tfp,
580 	    "UCChangeTerminalCodepage: Switch_Display_Charset(%d) returned %d\n",
581 	    newcs, res));
582 #else
583     CTRACE((tfp, "UCChangeTerminalCodepage: Called, but not implemented!"));
584 #endif
585 }
586 #endif /* LINUX */
587 
588 #ifdef CAN_SWITCH_DISPLAY_CHARSET
589 
Find_Best_Display_Charset(int ord)590 int Find_Best_Display_Charset(int ord)
591 {
592     const char *name = LYCharSet_UC[ord].MIMEname;
593     char *s = charset_switch_rules, *r;
594     char buf[160];
595     static int lowercase;
596     int n = strlen(name), source = 1;
597 
598     if (!s || !n)
599 	return ord;
600     if (!lowercase++)
601 	LYLowerCase(charset_switch_rules);
602     while (1) {
603 	while (*s && strchr(" \t,", *s))
604 	    s++;		/* Go to start of a name or ':' */
605 	if (!*s && source)
606 	    return ord;		/* OK to find nothing */
607 	if (!*s) {
608 	    sprintf(buf,
609 		    gettext("No destination for '%.80s' in CHARSET_SWITCH_RULES"),
610 		    name);
611 	    HTInfoMsg(buf);
612 	    return ord;
613 	}
614 	if (*s == ':') {
615 	    /* Before the replacement name */
616 	    while (*s && strchr(" \t:", *s))
617 		s++;		/* Go to the replacement */
618 	    /* At start of the replacement name */
619 	    r = s;
620 	    while (*s && !strchr(" \t,:", *s))
621 		s++;		/* Skip the replacement */
622 	    if (source)
623 		continue;
624 	    break;
625 	}
626 	/* At start of the source name */
627 	if (source && !strnicmp(name, s, n) && strchr(" \t,", s[n])) {	/* Found! */
628 	    source = 0;
629 	    s += n;
630 	    continue;		/* Look for the replacement */
631 	}
632 	while (*s && !strchr(" \t,:", *s))
633 	    s++;		/* Skip the other source names */
634     }
635     /* Here r point to the replacement, s to the end of the replacement. */
636     if (s >= r + sizeof(buf)) {
637 	HTInfoMsg(gettext("Charset name in CHARSET_SWITCH_RULES too long"));
638 	return ord;
639     }
640     LYStrNCpy(buf, r, s - r);
641     n = UCGetLYhndl_byMIME(buf);
642     if (n < 0) {
643 	sprintf(buf,
644 		gettext("Unknown charset name '%.*s' in CHARSET_SWITCH_RULES"),
645 		s - r, r);
646 	HTInfoMsg(buf);
647 	return ord;
648     }
649     return n;
650 }
651 
652 #  ifdef __EMX__
653 /* Switch display for the best fit for LYCharSet_UC[ord].
654    If really is MAYBE, the switch is tentative only, another switch may happen
655    before the actual display.
656 
657    Returns the charset we switched to.  */
_Switch_Display_Charset(int ord,enum switch_display_charset_t really)658 static int _Switch_Display_Charset(int ord, enum switch_display_charset_t really)
659 {
660     const char *name;
661     unsigned short cp;
662     static int font_loaded_for = -1, old_h, old_w;
663     int rc, ord1;
664     UCHAR msgbuf[MAXPATHLEN + 80];
665 
666     CTRACE((tfp, "_Switch_Display_Charset(cp=%d, really=%d).\n", ord, really));
667     /* Do not trust current_char_set unless REALLY, we fake it if MAYBE! */
668     if (ord == current_char_set && really == SWITCH_DISPLAY_CHARSET_MAYBE)
669 	return ord;
670     if (ord == auto_other_display_charset
671 	|| ord == auto_display_charset || ord == font_loaded_for) {
672 	if (really == SWITCH_DISPLAY_CHARSET_MAYBE)
673 	    return ord;		/* Report success, to avoid flicker, switch later */
674     } else			/* Currently supports only koi8-r to cp866 translation */
675 	ord = Find_Best_Display_Charset(ord);
676 
677     /* Ignore sizechange unless the font is loaded */
678     if (ord != font_loaded_for && really == SWITCH_DISPLAY_CHARSET_RESIZE)
679 	return ord;
680 
681     if (ord == real_charsets[0] || ord == real_charsets[1]) {
682 	ord1 = (ord == real_charsets[1]
683 		? auto_other_display_charset : auto_display_charset);
684 	if (really == SWITCH_DISPLAY_CHARSET_MAYBE)
685 	    return ord;		/* Can switch later, report success to avoid flicker */
686     } else
687 	ord1 = ord;
688     if (ord == current_char_set && really == SWITCH_DISPLAY_CHARSET_MAYBE)
689 	return ord;
690 
691     name = LYCharSet_UC[ord1].MIMEname;
692     if (ord1 == auto_other_display_charset || ord1 == auto_display_charset) {
693       retry:
694 	rc = VioSetCp(0, codepages[ord1 == auto_other_display_charset], 0);
695 	if (rc == 0)
696 	    goto report;
697       err:
698 	sprintf(msgbuf, gettext("Can't change to '%s': err=%#x=%d"), name, rc, rc);
699 	HTInfoMsg(msgbuf);
700 	return -1;
701     }
702 
703     /* Not a "prepared" codepage.  Need to load the user font. */
704     if (charsets_directory) {
705 	TIB *tib;		/* Can't load font in a windowed-VIO */
706 	PIB *pib;
707 	VIOFONTINFO f[2];
708 	VIOFONTINFO *font;
709 	UCHAR b[1 << 17];
710 	UCHAR *buf = b;
711 	UCHAR fnamebuf[MAXPATHLEN];
712 	FILE *file;
713 	APIRET rc;
714 	long i, j;
715 
716 	/* 0 means a FS protected-mode session */
717 	if (font_loaded_for == -1	/* Did not try it yet */
718 	    && (DosGetInfoBlocks(&tib, &pib) || pib->pib_ultype != 0)) {
719 	    ord = ord1 = auto_display_charset;
720 	    goto retry;
721 	}
722 	/* Should not cross 64K boundaries: */
723 	font = f;
724 	if (((((ULONG) (char *) f) + sizeof(*font)) & 0xFFFF) < sizeof(*font))
725 	    font++;
726 	if (((ULONG) buf) & 0xFFFF)
727 	    buf += 0x10000 - (((ULONG) buf) & 0xFFFF);
728 	font->cb = sizeof(*font);	/* How large is this structure */
729 	font->type = 0;		/* Not the BIOS, the loaded font. */
730 	font->cbData = 65535;	/* How large is my buffer? */
731 	font->pbData = _emx_32to16(buf);	/* Wants an 16:16 pointer */
732 
733 	rc = VioGetFont(font, 0);	/* Retrieve data for current font */
734 	if (rc) {
735 	    sprintf(msgbuf,
736 		    gettext("Can't fetch current font info: err=%#x=%d"), rc, rc);
737 	    HTInfoMsg(msgbuf);
738 	    ord = ord1 = auto_display_charset;
739 	    goto retry;
740 	}
741 	if (ord1 == font_loaded_for
742 	    && old_h == font->cyCell && old_w == font->cxCell) {
743 	    /* The same as the previous font */
744 	    if ((rc = VioSetCp(0, -1, 0)))	/* -1: User font */
745 		goto err;
746 	    goto report;
747 	}
748 	sprintf(fnamebuf, "%s/%dx%d/%s.fnt",
749 		charsets_directory, font->cyCell, font->cxCell, name);
750 	file = fopen(fnamebuf, BIN_R);
751 	if (!file) {
752 	    sprintf(msgbuf, gettext("Can't open font file '%s'"), fnamebuf);
753 	    HTInfoMsg(msgbuf);
754 	    ord = ord1 = auto_display_charset;
755 	    goto retry;
756 	}
757 	i = ftell(file);
758 	fseek(file, 0, SEEK_END);
759 	if (ftell(file) - i != font->cbData) {
760 	    fclose(file);
761 	    sprintf(msgbuf, gettext("Mismatch of size of font file '%s'"), fnamebuf);
762 	    HTAlert(msgbuf);
763 	    ord = ord1 = auto_display_charset;
764 	    goto retry;
765 	}
766 	fseek(file, i, SEEK_SET);
767 	fread(buf, 1, font->cbData, file);
768 	fclose(file);
769 	rc = VioSetFont(font, 0);	/* Put it all back.. */
770 	if (rc) {
771 	    sprintf(msgbuf, gettext("Can't set font: err=%#x=%d"), rc, rc);
772 	    HTInfoMsg(msgbuf);
773 	    ord = ord1 = auto_display_charset;
774 	    font_loaded_for = -1;
775 	    goto retry;
776 	}
777 	font_loaded_for = ord1;
778 	old_h = font->cyCell;
779 	old_w = font->cxCell;
780     } else {
781 	ord = ord1 = auto_display_charset;
782 	goto retry;
783     }
784   report:
785     CTRACE((tfp, "Display font set to '%s'.\n", name));
786     return ord;
787 }
788 #  endif /* __EMX__ */
789 
Switch_Display_Charset(const int ord,const enum switch_display_charset_t really)790 int Switch_Display_Charset(const int ord, const enum switch_display_charset_t really)
791 {
792     int prev = current_char_set;
793     int res;
794     static int repeated;
795 
796     if (!switch_display_charsets)
797 	return 0;
798     res = _Switch_Display_Charset(ord, really);
799     if (res < 0 || prev == res)	/* No change */
800 	return 0;
801     /* Register the change */
802     current_char_set = res;
803     HTMLUseCharacterSet(current_char_set);
804     return 1;
805 }
806 #endif /* CAN_SWITCH_DISPLAY_CHARSET */
807 
808 #else /* EXP_CHARTRANS_AUTOSWITCH not defined: */
809 /*
810  * This is the thing that actually gets called from display_page().
811  */
UCChangeTerminalCodepage(int newcs GCC_UNUSED,LYUCcharset * p GCC_UNUSED)812 void UCChangeTerminalCodepage(int newcs GCC_UNUSED,
813 			      LYUCcharset *p GCC_UNUSED)
814 {
815     CTRACE((tfp, "UCChangeTerminalCodepage: Called, but not implemented!"));
816 }
817 #endif /* EXP_CHARTRANS_AUTOSWITCH */
818