/*
 * $LynxId: UCAuto.c,v 1.38 2008/07/05 11:42:34 tom Exp $
 *
 *  This file contains code for changing the Linux console mode.
 *  Currently some names for font files are hardwired in here.
 *  You have to change this code if it needs accommodation for your
 *  system (or get the required files...).
 *
 *  Depending on the Display Character Set switched to, and the previous
 *  one as far as it is known, system("setfont ...") and/or output of
 *  escape sequences to switch console mode are done.  Curses will be
 *  temporarily suspended while that happens.
 *
 *  NOTE that the setfont calls will also affect all other virtual consoles.
 *
 *  Any ideas how to do this for other systems?
 */

#include <HTUtils.h>
#include <LYUtils.h>

#include <UCMap.h>
#include <UCDefs.h>
#include <UCAuto.h>
#include <LYGlobalDefs.h>
#include <LYStrings.h>
#include <LYClean.h>
#include <LYLeaks.h>
#include <LYCharSets.h>

#ifdef EXP_CHARTRANS_AUTOSWITCH

#include <HTFile.h>
#include <www_wait.h>

#ifdef LINUX
#include <sysexits.h>		/* EX_DATAERR, etc. */
#endif

#  ifdef CAN_SWITCH_DISPLAY_CHARSET
char *charset_switch_rules;
char *charsets_directory;
int auto_other_display_charset = -1;
int codepages[2];
int real_charsets[2] =
{-1, -1};			/* Non "auto-" charsets for the cps */
int switch_display_charsets;

#  endif

#ifdef HAVE_USE_LEGACY_CODING
static int original_coding = 0;
#endif

#  ifdef __EMX__
/* If we "just include" <os2.h>, BOOLEAN conflicts. */
#  define BOOLEAN OS2_BOOLEAN	/* This file doesn't use it, conflicts */
#  define INCL_VIO		/* I want some Vio functions.. */
#  define INCL_DOSPROCESS	/* TIB PIB. */
#  define INCL_DOSNLS		/* DosQueryCp. */
#  include <os2.h>		/* Misc stuff.. */
#  include <os2thunk.h>		/* 32 bit to 16 bit pointer conv */
#  endif

#ifdef LINUX
typedef enum {
    Is_Unset,
    Is_Set,
    Dunno,
    Dont_Care
} TGen_state_t;

/*
 * List the states the console has been set to via SCS (select character-set).
 */
typedef enum {
    GN_Blat1,			/* Latin-1 */
    GN_0decgraf,		/* VT100 graphics */
    GN_Ucp437,			/* PC -> PC */
    GN_Kuser,			/* user-defined */
    GN_dunno,
    GN_dontCare
} TTransT_t;

static char *T_font_fn = NULL;	/* font filename */
static char *T_umap_fn = NULL;	/* unicode-map filename */

#define NOOUTPUT "2>/dev/null >/dev/null"

/*
 * Return the configured path of the setfont/consolechars program.
 */
static const char *GetSetfontPath(void)
{
    return HTGetProgramPath(ppSETFONT);
}

/*
 * setfont and consolechars have different options and available data.
 */
static BOOL isSetFont(void)
{
    const char *program = GetSetfontPath();
    const char *slash = strrchr(program, '/');
    const char *leaf = (slash ? slash + 1 : program);

    return !strcmp(leaf, "setfont");
}

/*
 * Here are the differences in options which affect lynx:
 */
#define setfont_u()    (isSetFont() ? "-u "  : "--sfm ")
#define setfont_o()    (isSetFont() ? "-o "  : "--old-font-raw ")
#define setfont_ou()   (isSetFont() ? "-ou " : "--old-sfm ")
#define console_font() (isSetFont() ? ""     : "--font ")

/*
 * call_setfont - execute "setfont" command via system()
 * returns:	 0  ok (as far as we know)
 *		-1  error (assume font and umap are not loaded)
 *		 1  error with umap (assume font loaded but umap empty)
 */
static int call_setfont(const char *font,
			const char *fnsuffix,
			const char *umap)
{
    const char *program = GetSetfontPath();
    char *T_setfont_cmd = NULL;
    int rv;

    /*
     * console-data package has only a few unicode maps.
     */
    if (!isSetFont())
	umap = 0;

    if ((font && T_font_fn && !strcmp(font, T_font_fn))
	&& (umap && T_umap_fn && !strcmp(umap, T_umap_fn))) {
	/*
	 * No need to repeat.
	 */
	return 0;
    }
    if (font)
	StrAllocCopy(T_font_fn, font);
    if (umap)
	StrAllocCopy(T_umap_fn, umap);

    if (!*fnsuffix)
	fnsuffix = "";

    if (non_empty(umap) && non_empty(font)) {
	HTSprintf0(&T_setfont_cmd, "%s %s%s%s %s%s %s",
		   program,
		   console_font(), font, fnsuffix,
		   setfont_u(), umap,
		   NOOUTPUT);
    } else if (non_empty(font)) {
	HTSprintf0(&T_setfont_cmd, "%s %s%s%s %s",
		   program,
		   console_font(), font, fnsuffix,
		   NOOUTPUT);
    } else if (non_empty(umap)) {
	HTSprintf0(&T_setfont_cmd, "%s %s%s %s",
		   program,
		   setfont_u(), umap,
		   NOOUTPUT);
    }

    if (T_setfont_cmd) {
	CTRACE((tfp, "Changing font: '%s'\n", T_setfont_cmd));
	rv = LYSystem(T_setfont_cmd);
	FREE(T_setfont_cmd);
	if (rv) {
	    CTRACE((tfp, "call_setfont: system returned %d (0x%x)!\n",
		    rv, rv));
	    if (rv == -1 || WIFSIGNALED(rv) || !WIFEXITED(rv)) {
		return -1;
	    } else if ((WEXITSTATUS(rv) == EX_DATAERR ||
			WEXITSTATUS(rv) == EX_NOINPUT) &&
		       non_empty(umap)) {
		/*
		 * Check if the font was loaded ok but something was wrong with
		 * the umap file.
		 */
		return 1;
	    } else {
		return -1;
	    }
	}
    }
    return 0;
}

static void write_esc(const char *p)
{
    int fd = open("/dev/tty", O_WRONLY);

    if (fd >= 0) {
	write(fd, p, strlen(p));
	close(fd);
    }
}

static int nonempty_file(const char *p)
{
    struct stat sb;

    return (stat(p, &sb) == 0 &&
	    S_ISREG(sb.st_mode) &&
	    (sb.st_size != 0));
}

static BOOL on_console(void)
{
    if ((x_display != NULL) ||
	LYgetXDisplay() != NULL) {
	/*
	 * We won't do anything in an xterm.  Better that way...
	 */
	return FALSE;
    }
    return TRUE;
}

/*
 * This is the thing that actually gets called from display_page().
 */
void UCChangeTerminalCodepage(int newcs,
			      LYUCcharset *p)
{
    const char *program = GetSetfontPath();
    static int lastcs = -1;
    static const char *lastname = NULL;
    static TTransT_t lastTransT = GN_dunno;
    static TGen_state_t lastUtf = Dunno;
    static TGen_state_t lastHasUmap = Dunno;

    static char *old_font = NULL;
    static char *old_umap = NULL;

    const char *name;
    TTransT_t TransT = GN_dunno;
    TGen_state_t Utf = Dunno;
    TGen_state_t HasUmap = Dunno;

    char *tmpbuf1 = NULL;
    char *tmpbuf2 = NULL;
    int status = 0;

    if (!on_console())
	return;

#ifdef HAVE_USE_LEGACY_CODING
    if (newcs < 0) {
	use_legacy_coding(original_coding);
    } else {
	original_coding = use_legacy_coding(2);
    }
#endif

    /*
     * Restore the original character set.
     */
    if (newcs < 0 || p == 0) {
	if (non_empty(old_font) &&
	    non_empty(old_umap)) {

	    if (nonempty_file(old_font)) {
		if (nonempty_file(old_umap)) {
		    HTSprintf0(&tmpbuf1, "%s %s%s %s%s %s",
			       program,
			       console_font(), old_font,
			       setfont_u(), old_umap,
			       NOOUTPUT);
		} else {
		    HTSprintf0(&tmpbuf1, "%s %s%s %s",
			       program,
			       console_font(), old_font,
			       NOOUTPUT);
		}
		CTRACE((tfp, "Restoring font: '%s'\n", tmpbuf1));
		status = LYSystem(tmpbuf1);
		if (status != 0) {
		    CTRACE((tfp, "...system returned %d (0x%x)\n", status, status));
		}
		FREE(tmpbuf1);
	    }
	}
	if (newcs < 0 && p == 0) {
	    if (old_font) {
		LYRemoveTemp(old_font);
		FREE(old_font);
	    }
	    if (old_umap) {
		LYRemoveTemp(old_umap);
		FREE(old_umap);
	    }
	    if (status == 0) {
		FREE(T_font_fn);
		FREE(T_umap_fn);
	    }
	}
	return;
    } else if (lastcs < 0 && old_umap == 0 && old_font == 0) {
	FILE *fp1;
	FILE *fp2 = NULL;

	if ((old_font = typecallocn(char, LY_MAXPATH)) != 0)
	      old_umap = typecallocn(char, LY_MAXPATH);

	if ((fp1 = LYOpenTemp(old_font, ".fnt", BIN_W)) != 0)
	    fp2 = LYOpenTemp(old_umap, ".uni", BIN_W);

	if (fp1 && fp2) {
	    size_t nlen;
	    int rv;

	    HTSprintf0(&tmpbuf1, "%s %s%s %s%s %s",
		       program,
		       setfont_o(), old_font,
		       setfont_ou(), old_umap,
		       NOOUTPUT);

	    CTRACE((tfp, "Saving font: '%s'\n", tmpbuf1));
	    rv = LYSystem(tmpbuf1);
	    if (rv != 0) {
		CTRACE((tfp, "...system returned %d (0x%x)\n", rv, rv));
	    }
	    FREE(tmpbuf1);
	    LYCloseTempFP(fp1);
	    LYCloseTempFP(fp2);

	    /* free up a few bytes */
	    if ((nlen = strlen(old_font) + 1) < LY_MAXPATH)
		old_font = typeRealloc(char, old_font, nlen);

	    if ((nlen = strlen(old_umap) + 1) < LY_MAXPATH)
		old_umap = typeRealloc(char, old_umap, nlen);
	} else {
	    if (fp1)
		LYRemoveTemp(old_font);
	    if (fp2)
		LYRemoveTemp(old_umap);
	    FREE(old_font);
	    FREE(old_umap);
	}
    }

    name = p->MIMEname;

    /*
     * Font sizes are currently hardwired here.
     */
#define SUFF1 ".f16"
#define SUFF2 "-16.psf"
#define SUFF3 "-8x16"
#define SUFF4 "8x16"
#define SUFF5 ".cp -16"
#define SUFF6 "_8x16"

    /* NOTE: `!!umap not in kbd!!' comments below means that the *.uni file
     * is not found in kbd package.  Reference Debian Package: kbd-data,
     * Version: 0.96a-14.  They should be located elsewhere or generated.
     * Also some cpNNN fonts used below are not in the kbd-data.  - kw
     */

    if (!strncmp(name, "iso-8859-1", 10) &&
	(!name[10] || !isdigit(UCH(name[10])))) {
	if ((lastHasUmap == Is_Set) && !strcmp(lastname, "cp850")) {
	    /*
	     * cp850 already contains all latin1 characters.
	     */
	    if (lastTransT != GN_Blat1) {
		TransT = GN_Blat1;
	    }
	} else {
	    /*
	     * "setfont lat1u-16.psf -u lat1u.uni"
	     */
	    status = call_setfont("lat1u", SUFF2, "lat1u.uni");
	    HasUmap = Is_Set;
	    if (lastTransT != GN_Blat1) {
		TransT = GN_Blat1;
	    }
	}
	Utf = Is_Unset;
    } else if (!strcmp(name, "iso-8859-2")) {
	/*
	 * "setfont iso02.f16 -u iso02.uni"
	 */
	status = call_setfont("iso02", SUFF1, "iso02.uni");
	TransT = GN_Kuser;
	HasUmap = Is_Set;
	Utf = Is_Unset;
    } else if (!strcmp(name, "iso-8859-15")) {
	/*
	 * "setfont lat0-16.psf"
	 */
	status = call_setfont("lat0", SUFF2, NULL);
	TransT = GN_Blat1;	/* bogus! */
	HasUmap = Dunno;	/* distributed lat0 files have bogus map data! */
	Utf = Is_Unset;
    } else if (!strncmp(name, "iso-8859-", 9)) {
	if (strlen(name) <= 10 || !isdigit(UCH(name[10])))
	    HTSprintf0(&tmpbuf1, "iso0%s", &name[9]);
	else
	    HTSprintf0(&tmpbuf1, "iso%s", &name[9]);
	HTSprintf0(&tmpbuf2, "%s.uni", tmpbuf1);
	/*
	 * "setfont iso0N.f16 -u iso0N.uni"
	 */
	status = call_setfont(tmpbuf1, SUFF1, tmpbuf2);
	FREE(tmpbuf1);
	FREE(tmpbuf2);
	TransT = GN_Kuser;
	HasUmap = Is_Set;
	Utf = Is_Unset;
    } else if (!strcmp(name, "koi8-r")) {
	/*
	 * "setfont koi8-8x16"
	 * !!umap not in kbd!!
	 */
	status = call_setfont("koi8", SUFF3, "koi8r.uni");
	TransT = GN_Kuser;
	HasUmap = Is_Set;
	Utf = Is_Unset;
    } else if (!strcmp(name, "koi8-u")) {
	/*
	 * "setfont koi8u_8x16"
	 * !!umap not in kbd!!
	 */
	status = call_setfont("koi8u", SUFF6, "koi8u.uni");
	TransT = GN_Kuser;
	HasUmap = Is_Set;
	Utf = Is_Unset;
    } else if (!strcmp(name, "cp437")) {
	/*
	 * "setfont default8x16 -u cp437.uni"
	 */
	status = call_setfont("default", SUFF4, "cp437.uni");
	if (lastTransT == GN_Kuser || lastTransT == GN_Ucp437)
	    TransT = GN_dontCare;
	else
	    TransT = GN_Ucp437;
	HasUmap = Is_Set;
	Utf = Is_Unset;
    } else if (!strcmp(name, "cp850")) {
	/*
	 * "setfont cp850-8x16 -u cp850.uni"
	 * !!umap not in kbd!!
	 */
	status = call_setfont("cp850", SUFF3, "cp850.uni");
	TransT = GN_Kuser;
	HasUmap = Is_Set;
	Utf = Is_Unset;
    } else if (!strcmp(name, "cp866") ||
	       !strcmp(name, "cp852") ||
	       !strcmp(name, "cp862")) {	/* MS-Kermit has these files */
	HTSprintf0(&tmpbuf2, "%s.uni", name);
	/*
	 * "setfont cpNNN.f16"
	 * !!umap not in kbd!!
	 */
	status = call_setfont(name, SUFF1, tmpbuf2);
	FREE(tmpbuf2);
	TransT = GN_Kuser;
	HasUmap = Is_Set;
	Utf = Is_Unset;
    } else if (!strcmp(name, "cp737")) {
	/*
	 * "setfont cp737.cp"
	 * !!umap not in kbd!!
	 */
	if (isSetFont()) {
	    status = call_setfont("737", SUFF5, "cp737.uni");
	} else {
	    status = call_setfont("greek", "", "cp737.uni");
	}
	TransT = GN_Kuser;
	HasUmap = Is_Set;
	Utf = Is_Unset;
    } else if (!strcmp(name, "cp857")) {
	status = call_setfont("cp857", SUFF3, "cp857.uni");
	TransT = GN_Kuser;
	HasUmap = Is_Set;
	Utf = Is_Unset;
    } else if (!strcmp(name, "x-transparent")) {
	Utf = Dont_Care;
    } else if (!strcmp(name, "us-ascii")) {
	Utf = Dont_Care;
    } else if (!strncmp(name, "mnem", 4)) {
	Utf = Dont_Care;
    }

    if (status == 1)
	HasUmap = Is_Unset;
    else if (status < 0) {
	if (HasUmap == Is_Set)
	    HasUmap = Dunno;
	name = "unknown-8bit";
    }

    if (TransT != lastTransT) {
	if (TransT == GN_Blat1) {
	    /*
	     * Switch Linux console to lat1 table.
	     */
	    write_esc("\033(B");
	} else if (TransT == GN_0decgraf) {
	    write_esc("\033(0");
	} else if (TransT == GN_Ucp437) {
	    /*
	     * Switch Linux console to 437 table?
	     */
	    write_esc("\033(U");
	} else if (TransT == GN_Kuser) {
	    /*
	     * Switch Linux console to user table.
	     */
	    write_esc("\033(K");
	}
	if (TransT != GN_dunno && TransT != GN_dontCare) {
	    lastTransT = TransT;
	} else {
	    TransT = lastTransT;
	}
    }

    if (HasUmap != Dont_Care && HasUmap != Dunno)
	lastHasUmap = HasUmap;

    if (p->enc == UCT_ENC_UTF8) {
	if (lastUtf != Is_Set) {
	    Utf = Is_Set;
	    /*
	     * Turn Linux console UTF8 mode ON.
	     */
	    write_esc("\033%G");
	    lastUtf = Utf;
	}
	return;
    } else if (lastUtf == Is_Set && Utf != Dont_Care) {
	Utf = Is_Unset;
	/*
	 * Turn Linux console UTF8 mode OFF.
	 */
	write_esc("\033%@");
	lastUtf = Utf;
    }

    if (Utf != Dont_Care && Utf != Dunno)
	lastUtf = Utf;

    lastcs = newcs;
    lastname = name;
}

#else /* Not LINUX: */
/*
 * This is the thing that actually gets called from display_page().
 */
void UCChangeTerminalCodepage(int newcs,
			      LYUCcharset *p)
{
#ifdef __EMX__
    int res = 0;

#ifdef HAVE_USE_LEGACY_CODING
    if (newcs < 0) {
	use_legacy_coding(original_coding);
    } else {
	original_coding = use_legacy_coding(2);
    }
#endif

    if (newcs < 0)
	newcs = auto_display_charset;
    res = Switch_Display_Charset(newcs, SWITCH_DISPLAY_CHARSET_REALLY);
    CTRACE((tfp,
	    "UCChangeTerminalCodepage: Switch_Display_Charset(%d) returned %d\n",
	    newcs, res));
#else
    CTRACE((tfp, "UCChangeTerminalCodepage: Called, but not implemented!"));
#endif
}
#endif /* LINUX */

#ifdef CAN_SWITCH_DISPLAY_CHARSET

int Find_Best_Display_Charset(int ord)
{
    const char *name = LYCharSet_UC[ord].MIMEname;
    char *s = charset_switch_rules, *r;
    char buf[160];
    static int lowercase;
    int n = strlen(name), source = 1;

    if (!s || !n)
	return ord;
    if (!lowercase++)
	LYLowerCase(charset_switch_rules);
    while (1) {
	while (*s && strchr(" \t,", *s))
	    s++;		/* Go to start of a name or ':' */
	if (!*s && source)
	    return ord;		/* OK to find nothing */
	if (!*s) {
	    sprintf(buf, "No destination for '%.80s' in CHARSET_SWITCH_RULES",
		    name);
	    HTInfoMsg(buf);
	    return ord;
	}
	if (*s == ':') {
	    /* Before the replacement name */
	    while (*s && strchr(" \t:", *s))
		s++;		/* Go to the replacement */
	    /* At start of the replacement name */
	    r = s;
	    while (*s && !strchr(" \t,:", *s))
		s++;		/* Skip the replacement */
	    if (source)
		continue;
	    break;
	}
	/* At start of the source name */
	if (source && !strnicmp(name, s, n) && strchr(" \t,", s[n])) {	/* Found! */
	    source = 0;
	    s += n;
	    continue;		/* Look for the replacement */
	}
	while (*s && !strchr(" \t,:", *s))
	    s++;		/* Skip the other source names */
    }
    /* Here r point to the replacement, s to the end of the replacement. */
    if (s >= r + sizeof(buf)) {
	HTInfoMsg(gettext("Charset name in CHARSET_SWITCH_RULES too long"));
	return ord;
    }
    strncpy(buf, r, s - r);
    buf[s - r] = '\0';
    n = UCGetLYhndl_byMIME(buf);
    if (n < 0) {
	sprintf(buf, "Unknown charset name '%.*s' in CHARSET_SWITCH_RULES",
		s - r, r);
	HTInfoMsg(buf);
	return ord;
    }
    return n;
}

#  ifdef __EMX__
/* Switch display for the best fit for LYCharSet_UC[ord].
   If really is MAYBE, the switch is tentative only, another switch may happen
   before the actual display.

   Returns the charset we switched to.  */
static int _Switch_Display_Charset(int ord, enum switch_display_charset_t really)
{
    const char *name;
    unsigned short cp;
    static int font_loaded_for = -1, old_h, old_w;
    int rc, ord1;
    UCHAR msgbuf[MAXPATHLEN + 80];

    CTRACE((tfp, "_Switch_Display_Charset(cp=%d, really=%d).\n", ord, really));
    /* Do not trust current_char_set unless REALLY, we fake it if MAYBE! */
    if (ord == current_char_set && really == SWITCH_DISPLAY_CHARSET_MAYBE)
	return ord;
    if (ord == auto_other_display_charset
	|| ord == auto_display_charset || ord == font_loaded_for) {
	if (really == SWITCH_DISPLAY_CHARSET_MAYBE)
	    return ord;		/* Report success, to avoid flicker, switch later */
    } else			/* Currently supports only koi8-r to cp866 translation */
	ord = Find_Best_Display_Charset(ord);

    /* Ignore sizechange unless the font is loaded */
    if (ord != font_loaded_for && really == SWITCH_DISPLAY_CHARSET_RESIZE)
	return ord;

    if (ord == real_charsets[0] || ord == real_charsets[1]) {
	ord1 = (ord == real_charsets[1]
		? auto_other_display_charset : auto_display_charset);
	if (really == SWITCH_DISPLAY_CHARSET_MAYBE)
	    return ord;		/* Can switch later, report success to avoid flicker */
    } else
	ord1 = ord;
    if (ord == current_char_set && really == SWITCH_DISPLAY_CHARSET_MAYBE)
	return ord;

    name = LYCharSet_UC[ord1].MIMEname;
    if (ord1 == auto_other_display_charset || ord1 == auto_display_charset) {
      retry:
	rc = VioSetCp(0, codepages[ord1 == auto_other_display_charset], 0);
	if (rc == 0)
	    goto report;
      err:
	sprintf(msgbuf, "Can't change to '%s': err=%#x=%d", name, rc, rc);
	HTInfoMsg(msgbuf);
	return -1;
    }

    /* Not a "prepared" codepage.  Need to load the user font. */
    if (charsets_directory) {
	TIB *tib;		/* Can't load font in a windowed-VIO */
	PIB *pib;
	VIOFONTINFO f[2];
	VIOFONTINFO *font;
	UCHAR b[1 << 17];
	UCHAR *buf = b;
	UCHAR fnamebuf[MAXPATHLEN];
	FILE *file;
	APIRET rc;
	long i, j;

	/* 0 means a FS protected-mode session */
	if (font_loaded_for == -1	/* Did not try it yet */
	    && (DosGetInfoBlocks(&tib, &pib) || pib->pib_ultype != 0)) {
	    ord = ord1 = auto_display_charset;
	    goto retry;
	}
	/* Should not cross 64K boundaries: */
	font = f;
	if (((((ULONG) (char *) f) + sizeof(*font)) & 0xFFFF) < sizeof(*font))
	    font++;
	if (((ULONG) buf) & 0xFFFF)
	    buf += 0x10000 - (((ULONG) buf) & 0xFFFF);
	font->cb = sizeof(*font);	/* How large is this structure */
	font->type = 0;		/* Not the BIOS, the loaded font. */
	font->cbData = 65535;	/* How large is my buffer? */
	font->pbData = _emx_32to16(buf);	/* Wants an 16:16 pointer */

	rc = VioGetFont(font, 0);	/* Retrieve data for current font */
	if (rc) {
	    sprintf(msgbuf, "Can't fetch current font info: err=%#x=%d", rc, rc);
	    HTInfoMsg(msgbuf);
	    ord = ord1 = auto_display_charset;
	    goto retry;
	}
	if (ord1 == font_loaded_for
	    && old_h == font->cyCell && old_w == font->cxCell) {
	    /* The same as the previous font */
	    if ((rc = VioSetCp(0, -1, 0)))	/* -1: User font */
		goto err;
	    goto report;
	}
	sprintf(fnamebuf, "%s/%dx%d/%s.fnt",
		charsets_directory, font->cyCell, font->cxCell, name);
	file = fopen(fnamebuf, BIN_R);
	if (!file) {
	    sprintf(msgbuf, "Can't open font file '%s'", fnamebuf);
	    HTInfoMsg(msgbuf);
	    ord = ord1 = auto_display_charset;
	    goto retry;
	}
	i = ftell(file);
	fseek(file, 0, SEEK_END);
	if (ftell(file) - i != font->cbData) {
	    fclose(file);
	    sprintf(msgbuf, "Mismatch of size of font file '%s'", fnamebuf);
	    HTAlert(msgbuf);
	    ord = ord1 = auto_display_charset;
	    goto retry;
	}
	fseek(file, i, SEEK_SET);
	fread(buf, 1, font->cbData, file);
	fclose(file);
	rc = VioSetFont(font, 0);	/* Put it all back.. */
	if (rc) {
	    sprintf(msgbuf, "Can't set font: err=%#x=%d", rc, rc);
	    HTInfoMsg(msgbuf);
	    ord = ord1 = auto_display_charset;
	    font_loaded_for = -1;
	    goto retry;
	}
	font_loaded_for = ord1;
	old_h = font->cyCell;
	old_w = font->cxCell;
    } else {
	ord = ord1 = auto_display_charset;
	goto retry;
    }
  report:
    CTRACE((tfp, "Display font set to '%s'.\n", name));
    return ord;
}
#  endif			/* __EMX__ */

int Switch_Display_Charset(const int ord, const enum switch_display_charset_t really)
{
    int prev = current_char_set;
    int res;
    static int repeated;

    if (!switch_display_charsets)
	return 0;
    res = _Switch_Display_Charset(ord, really);
    if (res < 0 || prev == res)	/* No change */
	return 0;
    /* Register the change */
    current_char_set = res;
    HTMLUseCharacterSet(current_char_set);
    return 1;
}
#endif /* CAN_SWITCH_DISPLAY_CHARSET */

#else /* EXP_CHARTRANS_AUTOSWITCH not defined: */
/*
 * This is the thing that actually gets called from display_page().
 */
void UCChangeTerminalCodepage(int newcs GCC_UNUSED,
			      LYUCcharset *p GCC_UNUSED)
{
    CTRACE((tfp, "UCChangeTerminalCodepage: Called, but not implemented!"));
}
#endif /* EXP_CHARTRANS_AUTOSWITCH */
