charsets.c revision 04b94745
1/* $XTermId: charsets.c,v 1.126 2024/05/22 00:27:53 tom Exp $ */
2
3/*
4 * Copyright 1998-2023,2024 by Thomas E. Dickey
5 *
6 *                         All Rights Reserved
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the
10 * "Software"), to deal in the Software without restriction, including
11 * without limitation the rights to use, copy, modify, merge, publish,
12 * distribute, sublicense, and/or sell copies of the Software, and to
13 * permit persons to whom the Software is furnished to do so, subject to
14 * the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22 * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
23 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 *
27 * Except as contained in this notice, the name(s) of the above copyright
28 * holders shall not be used in advertising or otherwise to promote the
29 * sale, use or other dealings in this Software without prior written
30 * authorization.
31 *
32 */
33
34#include <assert.h>
35#include <X11/keysym.h>
36
37#include <xterm.h>
38#include <data.h>
39#include <charsets.h>
40#include <fontutils.h>
41
42/*
43 * This module performs translation as needed to support the DEC VT220 national
44 * replacement character sets as well as supplementary character sets (aka
45 * code-pages) introduced in VT320, etc.
46 *
47 * We assume that xterm's font is based on the ISO 8859-1 (Latin 1) character
48 * set, which is almost the same as the DEC multinational character set.  Glyph
49 * positions 0-31 have to be the DEC graphic characters, though.
50 *
51 * References:
52 *	"VT220 Programmer Pocket Guide" EK-VT220-HR-002 (2nd ed., 1984), which
53 *		contains character charts for the national character sets.
54 *	"VT330/VT340 Programmer Reference Manual Volume 1: Text Programming"
55 *		EK-VT3XX-TP-001 (1st ed, 1987), which contains a table (2-1)
56 *		listing the glyphs which are mapped from the multinational
57 *		character set to the national character set.
58 *
59 * The latter reference, though easier to read, has a few errors and omissions.
60 */
61
62#define HandleUPSS(charset) \
63    if (charset == nrc_DEC_UPSS) { \
64	charset = screen->gsets_upss; \
65	if (screen->vtXX_level >= 5) { \
66	    /* EMPTY */ ; \
67	} else if (screen->vtXX_level >= 3) { \
68	    if (charset != nrc_DEC_Supp) \
69		charset = nrc_ISO_Latin_1_Supp; \
70	} else if (screen->vtXX_level < 2) { \
71	    charset = nrc_ASCII; \
72	} \
73    }
74
75static Boolean
76isSevenBit(DECNRCM_codes cs)
77{
78    Boolean result = False;
79
80    switch (cs) {
81    case nrc_ISO_Greek_Supp:
82    case nrc_ISO_Hebrew_Supp:
83    case nrc_ISO_Latin_1_Supp:
84    case nrc_ISO_Latin_2_Supp:
85    case nrc_ISO_Latin_5_Supp:
86    case nrc_ISO_Latin_Cyrillic:
87    case nrc_DEC_UPSS:
88	break;
89	/* VT100 character sets */
90    case nrc_ASCII:
91    case nrc_British:
92    case nrc_DEC_Alt_Chars:
93    case nrc_DEC_Spec_Graphic:
94	/* VT220 character sets */
95    case nrc_DEC_Alt_Graphics:
96    case nrc_DEC_Supp:
97	/* VT320 character sets */
98    case nrc_DEC_Supp_Graphic:
99    case nrc_DEC_Technical:
100	/* NRCS character sets (VT320 to VT520) */
101    case nrc_British_Latin_1:
102    case nrc_Dutch:
103    case nrc_Finnish2:
104    case nrc_Finnish:
105    case nrc_French2:
106    case nrc_French:
107    case nrc_French_Canadian2:
108    case nrc_French_Canadian:
109    case nrc_German:
110    case nrc_Greek:
111    case nrc_Hebrew:
112    case nrc_Italian:
113    case nrc_Norwegian_Danish2:
114    case nrc_Norwegian_Danish3:
115    case nrc_Norwegian_Danish:
116    case nrc_Portugese:
117    case nrc_Russian:
118    case nrc_SCS_NRCS:
119    case nrc_Spanish:
120    case nrc_Swedish2:
121    case nrc_Swedish:
122    case nrc_Swiss:
123    case nrc_Turkish:
124	/* other DEC character sets */
125    case nrc_DEC_Cyrillic:
126    case nrc_DEC_Greek_Supp:
127    case nrc_DEC_Hebrew_Supp:
128    case nrc_DEC_Turkish_Supp:
129	result = True;
130	break;
131    case nrc_Unknown:
132	break;
133    }
134    return result;
135}
136
137/*
138 * Translate an input keysym to the corresponding NRC keysym.
139 */
140unsigned
141xtermCharSetIn(XtermWidget xw, unsigned code, DECNRCM_codes charset)
142{
143    TScreen *screen = TScreenOf(xw);
144#define MAP(to, from) case from: code = to; break;
145
146#if OPT_WIDE_CHARS
147#define UNI(to, from) case from: if (screen->utf8_nrc_mode) code = to; break;
148#else
149#define UNI(to, from) case from: break;
150#endif
151
152#define XXX(to, from)		/* no defined mapping to 0..255 */
153
154    TRACE(("CHARSET-IN GL=%s(G%d) GR=%s(G%d) SS%d\n\t%s\n",
155	   visibleScsCode(screen->gsets[screen->curgl]), screen->curgl,
156	   visibleScsCode(screen->gsets[screen->curgr]), screen->curgr,
157	   screen->curss,
158	   visibleUChar(code)));
159
160    HandleUPSS(charset);
161
162    switch (charset) {
163    case nrc_British:		/* United Kingdom set (or Latin 1)      */
164	if (code == XK_sterling)
165	    code = 0x23;
166	code &= 0x7f;
167	break;
168
169    case nrc_DEC_Alt_Chars:
170    case nrc_DEC_Alt_Graphics:
171    case nrc_ASCII:
172	break;
173
174    case nrc_DEC_Spec_Graphic:
175	break;
176
177    case nrc_DEC_Supp:
178	map_DEC_Supp_Graphic(code, code &= 0x7f);
179	break;
180
181    case nrc_DEC_Supp_Graphic:
182	map_DEC_Supp_Graphic(code, code |= 0x80);
183	break;
184
185    case nrc_DEC_Technical:
186	map_DEC_Technical(code);
187	break;
188
189    case nrc_Dutch:
190	map_NRCS_Dutch(code);
191	break;
192
193    case nrc_Finnish:
194    case nrc_Finnish2:
195	map_NRCS_Finnish(code);
196	break;
197
198    case nrc_French:
199    case nrc_French2:
200	map_NRCS_French(code);
201	break;
202
203    case nrc_French_Canadian:
204	map_NRCS_French_Canadian(code);
205	break;
206
207    case nrc_German:
208	map_NRCS_German(code);
209	break;
210
211    case nrc_Greek:
212	map_NRCS_Greek(code);	/* FIXME - ELOT? */
213	break;
214
215    case nrc_DEC_Greek_Supp:
216	map_DEC_Greek_Supp(code);
217	break;
218
219    case nrc_ISO_Greek_Supp:
220	map_ISO_Greek_Supp(code);
221	break;
222
223    case nrc_DEC_Hebrew_Supp:
224	map_DEC_Hebrew_Supp(code);
225	break;
226
227    case nrc_Hebrew:
228	map_NRCS_Hebrew(code);
229	break;
230
231    case nrc_ISO_Hebrew_Supp:
232	map_ISO_Hebrew(code);
233	break;
234
235    case nrc_Italian:
236	map_NRCS_Italian(code);
237	break;
238
239    case nrc_ISO_Latin_2_Supp:
240	map_ISO_Latin_2(code);
241	break;
242
243    case nrc_ISO_Latin_5_Supp:
244	map_ISO_Latin_5(code);
245	break;
246
247    case nrc_ISO_Latin_Cyrillic:
248	map_ISO_Latin_Cyrillic(code);
249	break;
250
251    case nrc_Norwegian_Danish:
252    case nrc_Norwegian_Danish2:
253    case nrc_Norwegian_Danish3:
254	map_NRCS_Norwegian_Danish(code);
255	break;
256
257    case nrc_Portugese:
258	map_NRCS_Portuguese(code);
259	break;
260
261    case nrc_SCS_NRCS:		/* vt5xx - Serbo/Croatian */
262	/* FIXME */
263	break;
264
265    case nrc_Spanish:
266	map_NRCS_Spanish(code);
267	break;
268
269    case nrc_Swedish2:
270    case nrc_Swedish:
271	map_NRCS_Swedish(code);
272	break;
273
274    case nrc_Swiss:
275	map_NRCS_Swiss(code);
276	break;
277
278    case nrc_Turkish:
279	map_NRCS_Turkish(code);
280	break;
281
282    case nrc_DEC_Turkish_Supp:
283	map_DEC_Turkish_Supp(code);
284	break;
285
286    case nrc_DEC_Cyrillic:
287	map_DEC_Cyrillic(code);
288	break;
289
290    case nrc_ISO_Latin_1_Supp:
291    case nrc_British_Latin_1:
292    case nrc_Russian:
293    case nrc_French_Canadian2:
294    case nrc_Unknown:
295    case nrc_DEC_UPSS:
296    default:			/* any character sets we don't recognize */
297	break;
298    }
299    code &= 0x7f;		/* NRC in any case is 7-bit */
300    TRACE(("->\t%s\n",
301	   visibleUChar(code)));
302    return code;
303#undef MAP
304#undef UNI
305#undef XXX
306}
307
308/*
309 * Translate a string to the display form.  This assumes the font has the
310 * DEC graphic characters in cells 0-31, and otherwise is ISO-8859-1.
311 */
312Cardinal
313xtermCharSetOut(XtermWidget xw, Cardinal length, DECNRCM_codes leftset)
314{
315    IChar *buf = xw->work.write_text;
316    IChar *ptr = buf + length;
317    IChar *s;
318    TScreen *screen = TScreenOf(xw);
319    Cardinal count = 0;
320    DECNRCM_codes rightset = screen->gsets[(int) (screen->curgr)];
321#if OPT_DEC_RECTOPS
322    int sums = 0;
323#endif
324
325#define MAP(from, to) case from: chr = to; break;
326
327#if OPT_WIDE_CHARS
328#define UNI(from, to) case from: if (screen->utf8_nrc_mode) chr = to; break;
329#define XXX(from, to) UNI(from, to)
330#else
331#define UNI(old, new) case new: chr = old; break;
332#define XXX(from, to)		/* nothing */
333#endif
334
335    TRACE(("CHARSET-OUT GL=%s(G%d) GR=%s(G%d) SS%d\n\t%s\n",
336	   visibleScsCode(leftset), screen->curgl,
337	   visibleScsCode(rightset), screen->curgr,
338	   screen->curss,
339	   visibleIChars(buf, (size_t) length)));
340
341    assert(length != 0);
342#if OPT_DEC_RECTOPS
343    if (length != 0 && length > xw->work.sizeof_sums) {
344	xw->work.sizeof_sums += length + 80;
345	xw->work.buffer_sums = realloc(xw->work.buffer_sums,
346				       xw->work.sizeof_sums);
347	xw->work.buffer_sets = realloc(xw->work.buffer_sets,
348				       xw->work.sizeof_sums);
349    }
350    xw->work.write_sums = xw->work.buffer_sums;
351#endif
352
353    for (s = buf; s < ptr; ++s) {
354	int eight = CharOf(E2A(*s));
355	int seven = eight & 0x7f;
356	DECNRCM_codes cs = (eight >= 128) ? rightset : leftset;
357	int chr = eight;
358
359	HandleUPSS(cs);
360
361#if OPT_DEC_RECTOPS
362	if (xw->work.buffer_sums != NULL && xw->work.buffer_sets != NULL) {
363	    xw->work.buffer_sums[sums] = (Char) ((eight < 32 || eight > 255)
364						 ? ANSI_ESC
365						 : eight);
366	    xw->work.buffer_sets[sums] = cs;
367	    ++sums;
368	}
369#endif
370	count++;
371#if OPT_WIDE_CHARS
372	/*
373	 * This is only partly right - prevent inadvertent remapping of
374	 * the replacement character and other non-8bit codes into bogus
375	 * 8bit codes.
376	 */
377	if (screen->utf8_mode || screen->utf8_nrc_mode) {
378	    if (*s > 255)
379		continue;
380	}
381#endif
382	if (*s < 32)
383	    continue;
384
385	switch (cs) {
386	case nrc_DEC_UPSS:
387	    break;
388
389	case nrc_ISO_Latin_1_Supp:
390	case nrc_British_Latin_1:
391	case nrc_British:	/* United Kingdom set (or Latin 1)      */
392	    if ((xw->flags & NATIONAL)
393		|| (screen->vtXX_level <= 1)) {
394		if ((xw->flags & NATIONAL)) {
395		    chr = seven;
396		}
397		if (chr == 0x23) {
398		    chr = XTERM_POUND;
399#if OPT_WIDE_CHARS
400		    if (screen->utf8_nrc_mode) {
401			chr = 0xa3;
402		    }
403#endif
404		}
405	    }
406	    break;
407
408	case nrc_DEC_Alt_Chars:
409	case nrc_DEC_Alt_Graphics:
410	case nrc_ASCII:
411	    break;
412
413	case nrc_DEC_Spec_Graphic:
414	    if (seven > 0x5f && seven <= 0x7e) {
415#if OPT_WIDE_CHARS
416		if (screen->utf8_mode || screen->utf8_nrc_mode)
417		    chr = (int) dec2ucs(screen, (unsigned) (seven - 0x5f));
418		else
419#endif
420		    chr = seven - 0x5f;
421	    } else if (chr == 0x5f) {
422		chr = 0;
423	    } else {
424		chr = seven;
425	    }
426	    break;
427
428	case nrc_DEC_Supp:
429	case nrc_DEC_Supp_Graphic:
430	    map_DEC_Supp_Graphic(chr = seven, chr = eight);
431	    break;
432
433	case nrc_DEC_Technical:
434	    map_DEC_Technical(chr = seven);
435	    break;
436
437	case nrc_Dutch:
438	    map_NRCS_Dutch(chr = seven);
439	    break;
440
441	case nrc_Finnish:
442	case nrc_Finnish2:
443	    map_NRCS_Finnish(chr = seven);
444	    break;
445
446	case nrc_French:
447	case nrc_French2:
448	    map_NRCS_French(chr = seven);
449	    break;
450
451	case nrc_French_Canadian:
452	case nrc_French_Canadian2:
453	    map_NRCS_French_Canadian(chr = seven);
454	    break;
455
456	case nrc_German:
457	    map_NRCS_German(chr = seven);
458	    break;
459
460	case nrc_Greek:
461	    map_NRCS_Greek(chr = seven);	/* FIXME - ELOT? */
462	    break;
463
464	case nrc_DEC_Greek_Supp:
465	    map_DEC_Greek_Supp(chr = seven);
466	    break;
467
468	case nrc_ISO_Greek_Supp:
469	    map_ISO_Greek_Supp(chr = seven);
470	    break;
471
472	case nrc_DEC_Hebrew_Supp:
473	    map_DEC_Hebrew_Supp(chr = seven);
474	    break;
475
476	case nrc_Hebrew:
477	    map_NRCS_Hebrew(chr = seven);
478	    break;
479
480	case nrc_ISO_Hebrew_Supp:
481	    map_ISO_Hebrew(chr = seven);
482	    break;
483
484	case nrc_Italian:
485	    map_NRCS_Italian(chr = seven);
486	    break;
487
488	case nrc_ISO_Latin_2_Supp:
489	    map_ISO_Latin_2(chr = seven);
490	    break;
491
492	case nrc_ISO_Latin_5_Supp:
493	    map_ISO_Latin_5(chr = seven);
494	    break;
495
496	case nrc_ISO_Latin_Cyrillic:
497	    map_ISO_Latin_Cyrillic(chr = seven);
498	    break;
499
500	case nrc_Norwegian_Danish:
501	case nrc_Norwegian_Danish2:
502	case nrc_Norwegian_Danish3:
503	    map_NRCS_Norwegian_Danish(chr = seven);
504	    break;
505
506	case nrc_Portugese:
507	    map_NRCS_Portuguese(chr = seven);
508	    break;
509
510	case nrc_SCS_NRCS:	/* vt5xx - Serbo/Croatian */
511	    /* FIXME */
512	    break;
513
514	case nrc_Spanish:
515	    map_NRCS_Spanish(chr = seven);
516	    break;
517
518	case nrc_Swedish2:
519	case nrc_Swedish:
520	    map_NRCS_Swedish(chr = seven);
521	    break;
522
523	case nrc_Swiss:
524	    map_NRCS_Swiss(chr = seven);
525	    break;
526
527	case nrc_Turkish:
528	    map_NRCS_Turkish(chr = seven);
529	    break;
530
531	case nrc_DEC_Turkish_Supp:
532	    map_DEC_Turkish_Supp(chr = seven);
533	    break;
534
535	case nrc_DEC_Cyrillic:
536	    map_DEC_Cyrillic(chr = seven);
537	    break;
538
539	case nrc_Russian:
540	case nrc_Unknown:
541	default:		/* any character sets we don't recognize */
542	    break;
543	}
544	/*
545	 * The state machine already treated DEL as a nonprinting and
546	 * nonspacing character.  If we have DEL now, remove it.
547	 */
548	if (chr == ANSI_DEL && isSevenBit(cs)) {
549	    IChar *s1;
550	    --ptr;
551	    for (s1 = s; s1 < ptr; ++s1) {
552		s1[0] = s1[1];
553	    }
554	    --count;
555#if OPT_DEC_RECTOPS
556	    --sums;
557#endif
558	} else {
559	    if (eight >= 128 && chr < 128 && chr > 32)
560		chr |= 128;
561	    *s = (IChar) A2E(chr);
562	}
563    }
564    TRACE(("%d\t%s\n",
565	   count,
566	   visibleIChars(buf, (size_t) length)));
567    return count;
568#undef MAP
569#undef UNI
570#undef XXX
571}
572
573#if OPT_DEC_RECTOPS
574/*
575 * Given a mapped character, e.g., a Unicode value returned by xtermCharSetIn,
576 * match it against the current GL/GR selection and return the corresponding
577 * DEC internal character-set code for DECRQCRA.
578 *
579 * A hardware terminal presumably stores the original and mapped characters,
580 * as well as the character set which was selected at that time  Doing that
581 * in xterm adds a couple of bytes to every cell.
582 */
583int
584xtermCharSetDec(XtermWidget xw, IChar chr, DECNRCM_codes cs)
585{
586#define MAP(from, to) case from: result = to; break;
587
588#define DFTMAP()      result = (actual | 0x80)
589#define DFT_94(chr)   result = ((actual) & 0x7f)
590#define DFT_96(chr)   result = ((actual) | 0x80)
591
592#if OPT_WIDE_CHARS
593#define UNI(from, to) case from: if (screen->utf8_nrc_mode) result = to; break;
594#define XXX(from, to) UNI(from, to)
595#else
596#define UNI(old, new) case new: result = old; break;
597#define XXX(from, to)		/* nothing */
598#endif
599
600    int result;
601
602    if (chr < 0x20
603#if OPT_WIDE_CHARS
604	|| chr > 0xff
605#endif
606	) {
607	result = ANSI_ESC;
608    } else {
609	Boolean isSeven = isSevenBit(cs);
610	TScreen *screen = TScreenOf(xw);
611
612	result = -1;
613
614	HandleUPSS(cs);
615
616	if (chr == 0xa0 && isSeven) {
617	    result = ANSI_ESC;
618	} else if (chr == ANSI_SPA && isSeven) {
619	    result = ANSI_SPA;
620	} else if ((chr == ANSI_DEL || chr == 0xff) && isSeven) {
621	    result = 0;
622	} else {
623	    int actual = (int) chr;
624	    chr &= 0x7f;
625
626	    switch (cs) {
627	    case nrc_DEC_Alt_Chars:
628	    case nrc_DEC_Alt_Graphics:
629	    case nrc_ASCII:
630		result = (int) chr;
631		break;
632
633	    case nrc_British:
634		if (chr >= 0xa0 && chr < 0xff) {
635		    if (chr == 0x23)
636			chr = 0xA3;
637		    result = (int) chr;
638		}
639		break;
640
641	    case nrc_DEC_Cyrillic:
642		unmap_DEC_Cyrillic(chr, DFT_94(chr));
643		break;
644
645	    case nrc_DEC_Spec_Graphic:
646		unmap_DEC_Spec_Graphic(chr, DFT_94(chr));
647		break;
648
649	    case nrc_DEC_Supp:
650		/* FALLTHRU */
651	    case nrc_DEC_Supp_Graphic:
652		unmap_DEC_Supp_Graphic(chr, DFTMAP());
653		break;
654
655	    case nrc_DEC_Technical:
656		unmap_DEC_Technical(chr, DFTMAP());
657		break;
658
659	    case nrc_Dutch:
660		unmap_NRCS_Dutch(chr, DFT_94(chr));
661		break;
662
663	    case nrc_Finnish:
664	    case nrc_Finnish2:
665		unmap_NRCS_Finnish(chr, DFT_94(chr));
666		break;
667
668	    case nrc_French:
669	    case nrc_French2:
670		unmap_NRCS_French(chr, DFT_94(chr));
671		break;
672
673	    case nrc_French_Canadian:
674	    case nrc_French_Canadian2:
675		unmap_NRCS_French_Canadian(chr, DFT_94(chr));
676		break;
677
678	    case nrc_German:
679		unmap_NRCS_German(chr, DFT_94(chr));
680		break;
681
682	    case nrc_Greek:
683		unmap_NRCS_Greek(chr, DFT_94(chr));
684		break;
685
686	    case nrc_DEC_Greek_Supp:
687		unmap_DEC_Greek_Supp(chr, DFTMAP());
688		break;
689
690	    case nrc_ISO_Greek_Supp:
691		unmap_ISO_Greek_Supp(chr, DFTMAP());
692		break;
693
694	    case nrc_DEC_Hebrew_Supp:
695		unmap_DEC_Hebrew_Supp(chr, DFTMAP());
696		break;
697
698	    case nrc_Hebrew:
699		unmap_NRCS_Hebrew(chr, DFT_94(chr));
700		break;
701
702	    case nrc_ISO_Hebrew_Supp:
703		unmap_ISO_Hebrew(chr, DFTMAP());
704		break;
705
706	    case nrc_Italian:
707		unmap_NRCS_Italian(chr, DFT_94(chr));
708		break;
709
710	    case nrc_ISO_Latin_1_Supp:
711		unmap_ISO_Latin_1(chr, DFTMAP());
712		break;
713
714	    case nrc_ISO_Latin_2_Supp:
715		unmap_ISO_Latin_2(chr, DFTMAP());
716		break;
717
718	    case nrc_ISO_Latin_5_Supp:
719		unmap_ISO_Latin_5(chr, DFTMAP());
720		break;
721
722	    case nrc_ISO_Latin_Cyrillic:
723		unmap_ISO_Latin_Cyrillic(chr, DFTMAP());
724		break;
725
726	    case nrc_Norwegian_Danish:
727	    case nrc_Norwegian_Danish2:
728	    case nrc_Norwegian_Danish3:
729		unmap_NRCS_Norwegian_Danish(chr, DFT_94(chr));
730		break;
731
732	    case nrc_Portugese:
733		unmap_NRCS_Portuguese(chr, DFT_94(chr));
734		break;
735
736	    case nrc_Spanish:
737		unmap_NRCS_Spanish(chr, DFT_94(chr));
738		break;
739
740	    case nrc_Swedish:
741	    case nrc_Swedish2:
742		unmap_NRCS_Swedish(chr, DFT_94(chr));
743		break;
744
745	    case nrc_Swiss:
746		unmap_NRCS_Swiss(chr, DFT_94(chr));
747		break;
748
749	    case nrc_DEC_Turkish_Supp:
750		unmap_DEC_Turkish_Supp(chr, DFTMAP());
751		break;
752
753	    case nrc_Turkish:
754		unmap_NRCS_Turkish(chr, DFT_94(chr));
755		break;
756
757	    case nrc_British_Latin_1:
758	    case nrc_SCS_NRCS:
759	    case nrc_Russian:
760	    case nrc_Unknown:
761	    case nrc_DEC_UPSS:
762	    default:		/* anything we cannot unmap */
763		break;
764	    }
765	    if (result < 0) {
766		if (isSeven) {
767		    DFT_94(chr);
768		} else {
769		    DFT_96(chr);
770		}
771	    }
772	}
773    }
774    return result;
775#undef MAP
776#undef UNI
777#undef XXX
778}
779#endif /* OPT_DEC_RECTOPS */
780