Upgrade Texinfo from 4.8 to 4.13 on the vendor branch
[dragonfly.git] / contrib / texinfo / makeinfo / lang.c
1 /* lang.c -- language-dependent support.
2    $Id: lang.c,v 1.34 2007/12/03 01:38:43 karl Exp $
3
4    Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
5    Free Software Foundation, Inc.
6
7    This program is free software: you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation, either version 3 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
20    Originally written by Karl Heinz Marbaise <kama@hippo.fido.de>.  */
21
22 #include "system.h"
23 #include "cmds.h"
24 #include "files.h"
25 #include "lang.h"
26 #include "makeinfo.h"
27 #include "xml.h"
28
29 #include <assert.h>
30
31 /* Current document encoding.  */
32 encoding_code_type document_encoding_code = no_encoding;
33
34 /* Current language code; default is English.  */
35 language_code_type language_code = en;
36
37 /* Language to use for translations that end up in the output. */
38 char *document_language = "C";
39
40 /* By default, unsupported encoding is an empty string.  */
41 char *unknown_encoding = NULL;
42
43 static iso_map_type asis_map [] = {{NULL, 0, 0}}; /* ASCII, etc. */
44
45 /* Translation table between HTML and ISO Codes.  The last item is
46    hopefully the Unicode. It might be possible that those Unicodes are
47    not correct, cause I didn't check them. kama */
48 static iso_map_type iso8859_1_map [] = {
49   { "nbsp",   0xA0, 0x00A0 },
50   { "iexcl",  0xA1, 0x00A1 },
51   { "cent",   0xA2, 0x00A2 },
52   { "pound",  0xA3, 0x00A3 },
53   { "curren", 0xA4, 0x00A4 },
54   { "yen",    0xA5, 0x00A5 },
55   { "brkbar", 0xA6, 0x00A6 },
56   { "sect",   0xA7, 0x00A7 },
57   { "uml",    0xA8, 0x00A8 },
58   { "copy",   0xA9, 0x00A9 },
59   { "ordf",   0xAA, 0x00AA },
60   { "laquo",  0xAB, 0x00AB },
61   { "not",    0xAC, 0x00AC },
62   { "shy",    0xAD, 0x00AD },
63   { "reg",    0xAE, 0x00AE },
64   { "hibar",  0xAF, 0x00AF },
65   { "deg",    0xB0, 0x00B0 },
66   { "plusmn", 0xB1, 0x00B1 },
67   { "sup2",   0xB2, 0x00B2 },
68   { "sup3",   0xB3, 0x00B3 },
69   { "acute",  0xB4, 0x00B4 },
70   { "micro",  0xB5, 0x00B5 },
71   { "para",   0xB6, 0x00B6 },
72   { "middot", 0xB7, 0x00B7 },
73   { "cedil",  0xB8, 0x00B8 },
74   { "sup1",   0xB9, 0x00B9 },
75   { "ordm",   0xBA, 0x00BA },
76   { "raquo",  0xBB, 0x00BB },
77   { "frac14", 0xBC, 0x00BC },
78   { "frac12", 0xBD, 0x00BD },
79   { "frac34", 0xBE, 0x00BE },
80   { "iquest", 0xBF, 0x00BF },
81   { "Agrave", 0xC0, 0x00C0, "A" },
82   { "Aacute", 0xC1, 0x00C1, "A" },
83   { "Acirc",  0xC2, 0x00C2, "A" },
84   { "Atilde", 0xC3, 0x00C3, "A" },
85   { "Auml",   0xC4, 0x00C4, "A" },
86   { "Aring",  0xC5, 0x00C5, "AA" },
87   { "AElig",  0xC6, 0x00C6, "AE" },
88   { "Ccedil", 0xC7, 0x00C7, "C" },
89   { "Ccedil", 0xC7, 0x00C7, "C" },
90   { "Egrave", 0xC8, 0x00C8, "E" },
91   { "Eacute", 0xC9, 0x00C9, "E" },
92   { "Ecirc",  0xCA, 0x00CA, "E" },
93   { "Euml",   0xCB, 0x00CB, "E" },
94   { "Igrave", 0xCC, 0x00CC, "I" },
95   { "Iacute", 0xCD, 0x00CD, "I" },
96   { "Icirc",  0xCE, 0x00CE, "I" },
97   { "Iuml",   0xCF, 0x00CF, "I" },
98   { "ETH",    0xD0, 0x00D0, "DH" },
99   { "Ntilde", 0xD1, 0x00D1, "N" },
100   { "Ograve", 0xD2, 0x00D2, "O" },
101   { "Oacute", 0xD3, 0x00D3, "O" },
102   { "Ocirc",  0xD4, 0x00D4, "O" },
103   { "Otilde", 0xD5, 0x00D5, "O" },
104   { "Ouml",   0xD6, 0x00D6, "O" },
105   { "times",  0xD7, 0x00D7 },
106   { "Oslash", 0xD8, 0x00D8, "OE" },
107   { "Ugrave", 0xD9, 0x00D9, "U" },
108   { "Uacute", 0xDA, 0x00DA, "U" },
109   { "Ucirc",  0xDB, 0x00DB, "U" },
110   { "Uuml",   0xDC, 0x00DC, "U" },
111   { "Yacute", 0xDD, 0x00DD, "Y" },
112   { "THORN",  0xDE, 0x00DE, "TH" },
113   { "szlig",  0xDF, 0x00DF, "s" },
114   { "agrave", 0xE0, 0x00E0, "a" },
115   { "aacute", 0xE1, 0x00E1, "a" },
116   { "acirc",  0xE2, 0x00E2, "a" },
117   { "atilde", 0xE3, 0x00E3, "a" },
118   { "auml",   0xE4, 0x00E4, "a" },
119   { "aring",  0xE5, 0x00E5, "aa" },
120   { "aelig",  0xE6, 0x00E6, "ae" },
121   { "ccedil", 0xE7, 0x00E7, "c" },
122   { "egrave", 0xE8, 0x00E8, "e" },
123   { "eacute", 0xE9, 0x00E9, "e" },
124   { "ecirc",  0xEA, 0x00EA, "e" },
125   { "euml",   0xEB, 0x00EB, "e" },
126   { "igrave", 0xEC, 0x00EC, "i" },
127   { "iacute", 0xED, 0x00ED, "i" },
128   { "icirc",  0xEE, 0x00EE, "i" },
129   { "iuml",   0xEF, 0x00EF, "i" },
130   { "eth",    0xF0, 0x00F0, "dh" },
131   { "ntilde", 0xF1, 0x00F1, "n" },
132   { "ograve", 0xF2, 0x00F2, "o"},
133   { "oacute", 0xF3, 0x00F3, "o" },
134   { "ocirc",  0xF4, 0x00F4, "o" },
135   { "otilde", 0xF5, 0x00F5, "o" },
136   { "ouml",   0xF6, 0x00F6, "o" },
137   { "divide", 0xF7, 0x00F7 },
138   { "oslash", 0xF8, 0x00F8, "oe" },
139   { "ugrave", 0xF9, 0x00F9, "u" },
140   { "uacute", 0xFA, 0x00FA, "u" },
141   { "ucirc",  0xFB, 0x00FB, "u" },
142   { "uuml",   0xFC, 0x00FC, "u" },
143   { "yacute", 0xFD, 0x00FD, "y" },
144   { "thorn",  0xFE, 0x00FE, "th" },
145   { "yuml",   0xFF, 0x00FF, "y" },
146   { NULL, 0, 0 }
147 };
148
149 \f
150 /* ISO 8859-15, also known as Latin 9, differs from Latin 1 in only a
151    few positions.  http://www.cs.tut.fi/~jkorpela/latin9.html has a good
152    explanation and listing, summarized here.  The names here are
153    abbreviated to fit in a decent line length.
154
155   code position
156   dec   oct   hex   latin1 latin1 name        latin9 latin9 name
157
158   164  0244  0xA4   U+00A4 currency symbol    U+20AC euro sign
159   166  0246  0xA6   U+00A6 broken bar         U+0160 S with caron
160   168  0250  0xA8   U+00A8 diaeresis          U+0161 s with caron
161   180  0264  0xB4   U+00B4 acute accent       U+017D Z with caron
162   184  0270  0xB8   U+00B8 cedilla            U+017E z with caron
163   188  0274  0xBC   U+00BC fraction 1/4       U+0152 ligature OE
164   189  0275  0xBD   U+00BD fraction 1/2       U+0153 ligature oe
165   190  0276  0xBE   U+00BE fraction 3/4       U+0178 Y with diaeresis
166 */
167
168 static iso_map_type iso8859_15_map [] = {
169   { "nbsp",   0xA0, 0x00A0 },
170   { "iexcl",  0xA1, 0x00A1 },
171   { "cent",   0xA2, 0x00A2 },
172   { "pound",  0xA3, 0x00A3 },
173   { "euro",   0xA4, 0x20AC },
174   { "yen",    0xA5, 0x00A5 },
175   { "Scaron", 0xA6, 0x0160, "S" },
176   { "sect",   0xA7, 0x00A7 },
177   { "scaron", 0xA8, 0x0161, "s" },
178   { "copy",   0xA9, 0x00A9 },
179   { "ordf",   0xAA, 0x00AA },
180   { "laquo",  0xAB, 0x00AB },
181   { "not",    0xAC, 0x00AC },
182   { "shy",    0xAD, 0x00AD },
183   { "reg",    0xAE, 0x00AE },
184   { "hibar",  0xAF, 0x00AF },
185   { "deg",    0xB0, 0x00B0 },
186   { "plusmn", 0xB1, 0x00B1 },
187   { "sup2",   0xB2, 0x00B2 },
188   { "sup3",   0xB3, 0x00B3 },
189   { "Zcaron", 0xB4, 0x017D, "Z" },
190   { "micro",  0xB5, 0x00B5 },
191   { "para",   0xB6, 0x00B6 },
192   { "middot", 0xB7, 0x00B7 },
193   { "zcaron", 0xB8, 0x017E, "z" },
194   { "sup1",   0xB9, 0x00B9 },
195   { "ordm",   0xBA, 0x00BA },
196   { "raquo",  0xBB, 0x00BB },
197   { "OElig",  0xBC, 0x0152, "OE" },
198   { "oelig",  0xBD, 0x0153, "oe" },
199   { "Yuml",   0xBE, 0x0178, "y" },
200   { "iquest", 0xBF, 0x00BF },
201   { "Agrave", 0xC0, 0x00C0, "A" },
202   { "Aacute", 0xC1, 0x00C1, "A" },
203   { "Acirc",  0xC2, 0x00C2, "A" },
204   { "Atilde", 0xC3, 0x00C3, "A" },
205   { "Auml",   0xC4, 0x00C4, "A" },
206   { "Aring",  0xC5, 0x00C5, "AA" },
207   { "AElig",  0xC6, 0x00C6, "AE" },
208   { "Ccedil", 0xC7, 0x00C7, "C" },
209   { "Egrave", 0xC8, 0x00C8, "E" },
210   { "Eacute", 0xC9, 0x00C9, "E" },
211   { "Ecirc",  0xCA, 0x00CA, "E" },
212   { "Euml",   0xCB, 0x00CB, "E" },
213   { "Igrave", 0xCC, 0x00CC, "I" },
214   { "Iacute", 0xCD, 0x00CD, "I" },
215   { "Icirc",  0xCE, 0x00CE, "I" },
216   { "Iuml",   0xCF, 0x00CF, "I" },
217   { "ETH",    0xD0, 0x00D0, "DH" },
218   { "Ntilde", 0xD1, 0x00D1, "N" },
219   { "Ograve", 0xD2, 0x00D2, "O" },
220   { "Oacute", 0xD3, 0x00D3, "O" },
221   { "Ocirc",  0xD4, 0x00D4, "O" },
222   { "Otilde", 0xD5, 0x00D5, "O" },
223   { "Ouml",   0xD6, 0x00D6, "O" },
224   { "times",  0xD7, 0x00D7 },
225   { "Oslash", 0xD8, 0x00D8, "OE" },
226   { "Ugrave", 0xD9, 0x00D9, "U" },
227   { "Uacute", 0xDA, 0x00DA, "U" },
228   { "Ucirc",  0xDB, 0x00DB, "U" },
229   { "Uuml",   0xDC, 0x00DC, "U" },
230   { "Yacute", 0xDD, 0x00DD, "Y" },
231   { "THORN",  0xDE, 0x00DE, "TH" },
232   { "szlig",  0xDF, 0x00DF, "s" },
233   { "agrave", 0xE0, 0x00E0, "a" },
234   { "aacute", 0xE1, 0x00E1, "a" },
235   { "acirc",  0xE2, 0x00E2, "a" },
236   { "atilde", 0xE3, 0x00E3, "a" },
237   { "auml",   0xE4, 0x00E4, "a" },
238   { "aring",  0xE5, 0x00E5, "aa" },
239   { "aelig",  0xE6, 0x00E6, "ae" },
240   { "ccedil", 0xE7, 0x00E7, "c" },
241   { "egrave", 0xE8, 0x00E8, "e" },
242   { "eacute", 0xE9, 0x00E9, "e" },
243   { "ecirc",  0xEA, 0x00EA, "e" },
244   { "euml",   0xEB, 0x00EB, "e" },
245   { "igrave", 0xEC, 0x00EC, "i" },
246   { "iacute", 0xED, 0x00ED, "i" },
247   { "icirc",  0xEE, 0x00EE, "i" },
248   { "iuml",   0xEF, 0x00EF, "i" },
249   { "eth",    0xF0, 0x00F0, "d" },
250   { "ntilde", 0xF1, 0x00F1, "n" },
251   { "ograve", 0xF2, 0x00F2, "o" },
252   { "oacute", 0xF3, 0x00F3, "o" },
253   { "ocirc",  0xF4, 0x00F4, "o" },
254   { "otilde", 0xF5, 0x00F5, "o" },
255   { "ouml",   0xF6, 0x00F6, "o" },
256   { "divide", 0xF7, 0x00F7 },
257   { "oslash", 0xF8, 0x00F8, "oe" },
258   { "ugrave", 0xF9, 0x00F9, "u" },
259   { "uacute", 0xFA, 0x00FA, "u" },
260   { "ucirc",  0xFB, 0x00FB, "u" },
261   { "uuml",   0xFC, 0x00FC, "u" },
262   { "yacute", 0xFD, 0x00FD, "y" },
263   { "thorn",  0xFE, 0x00FE, "th" },
264   { "yuml",   0xFF, 0x00FF, "y" },
265   { NULL, 0, 0 }
266 };
267
268
269 \f
270 /* Date: Mon, 31 Mar 2003 00:19:28 +0200
271    From: Wojciech Polak <polak@gnu.org>
272 ...
273  * Primary Polish site for ogonki is http://www.agh.edu.pl/ogonki/,
274    but it's only in Polish language (it has some interesting links).
275
276  * A general site about ISO 8859-2 at http://nl.ijs.si/gnusl/cee/iso8859-2.html
277
278  * ISO 8859-2 Character Set at http://nl.ijs.si/gnusl/cee/charset.html
279    This site provides almost all information about iso-8859-2,
280    including the character table!!! (must see!)
281
282  * ISO 8859-2 and even HTML entities !!! (must see!)
283    88592.txt in the GNU enscript distribution
284
285  * (minor) http://www.agh.edu.pl/ogonki/plchars.html
286    One more table, this time it includes even information about Polish
287    characters in Unicode.
288 */
289
290 static iso_map_type iso8859_2_map [] = {
291   { "nbsp",     0xA0, 0x00A0 }, /* NO-BREAK SPACE */
292   { "", 0xA1, 0x0104, "A" }, /* LATIN CAPITAL LETTER A WITH OGONEK */
293   { "", 0xA2, 0x02D8 }, /* BREVE */
294   { "", 0xA3, 0x0141, "L" }, /* LATIN CAPITAL LETTER L WITH STROKE */
295   { "curren",   0xA4, 0x00A4 }, /* CURRENCY SIGN */
296   { "", 0xA5, 0x013D, "L" }, /* LATIN CAPITAL LETTER L WITH CARON */
297   { "", 0xA6, 0x015A, "S" }, /* LATIN CAPITAL LETTER S WITH ACUTE */
298   { "sect",     0xA7, 0x00A7 }, /* SECTION SIGN */
299   { "uml",      0xA8, 0x00A8 }, /* DIAERESIS */
300   { "#xa9",     0xA9, 0x0160, "S" }, /* LATIN CAPITAL LETTER S WITH CARON */
301   { "", 0xAA, 0x015E, "S" }, /* LATIN CAPITAL LETTER S WITH CEDILLA */
302   { "", 0xAB, 0x0164, "T" }, /* LATIN CAPITAL LETTER T WITH CARON */
303   { "", 0xAC, 0x0179, "Z" }, /* LATIN CAPITAL LETTER Z WITH ACUTE */
304   { "shy",      0xAD, 0x00AD }, /* SOFT HYPHEN */
305   { "", 0xAE, 0x017D, "Z" }, /* LATIN CAPITAL LETTER Z WITH CARON */
306   { "", 0xAF, 0x017B, "Z" }, /* LATIN CAPITAL LETTER Z WITH DOT ABOVE */
307   { "deg",      0xB0, 0x00B0 }, /* DEGREE SIGN */
308   { "", 0xB1, 0x0105, "a" }, /* LATIN SMALL LETTER A WITH OGONEK */
309   { "", 0xB2, 0x02DB }, /* OGONEK */
310   { "", 0xB3, 0x0142, "l" }, /* LATIN SMALL LETTER L WITH STROKE */
311   { "acute",    0xB4, 0x00B4 }, /* ACUTE ACCENT */
312   { "", 0xB5, 0x013E, "l" }, /* LATIN SMALL LETTER L WITH CARON */
313   { "", 0xB6, 0x015B, "s" }, /* LATIN SMALL LETTER S WITH ACUTE */
314   { "", 0xB7, 0x02C7 }, /* CARON (Mandarin Chinese third tone) */
315   { "cedil",    0xB8, 0x00B8 }, /* CEDILLA */
316   { "", 0xB9, 0x0161, "s" }, /* LATIN SMALL LETTER S WITH CARON */
317   { "", 0xBA, 0x015F, "s" }, /* LATIN SMALL LETTER S WITH CEDILLA */
318   { "", 0xBB, 0x0165, "t" }, /* LATIN SMALL LETTER T WITH CARON */
319   { "", 0xBC, 0x017A, "z" }, /* LATIN SMALL LETTER Z WITH ACUTE */
320   { "", 0xBD, 0x02DD }, /* DOUBLE ACUTE ACCENT */
321   { "", 0xBE, 0x017E, "z" }, /* LATIN SMALL LETTER Z WITH CARON */
322   { "", 0xBF, 0x017C, "z" }, /* LATIN SMALL LETTER Z WITH DOT ABOVE */
323   { "", 0xC0, 0x0154, "R" }, /* LATIN CAPITAL LETTER R WITH ACUTE */
324   { "Aacute",   0xC1, 0x00C1, "A" }, /* LATIN CAPITAL LETTER A WITH ACUTE */
325   { "Acirc",    0xC2, 0x00C2, "A" }, /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX */
326   { "", 0xC3, 0x0102, "A" }, /* LATIN CAPITAL LETTER A WITH BREVE */
327   { "Auml",     0xC4, 0x00C4, "A" }, /* LATIN CAPITAL LETTER A WITH DIAERESIS */
328   { "", 0xC5, 0x0139, "L" }, /* LATIN CAPITAL LETTER L WITH ACUTE */
329   { "", 0xC6, 0x0106, "C" }, /* LATIN CAPITAL LETTER C WITH ACUTE */
330   { "Ccedil",   0xC7, 0x00C7, "C" }, /* LATIN CAPITAL LETTER C WITH CEDILLA */
331   { "", 0xC8, 0x010C, "C" }, /* LATIN CAPITAL LETTER C WITH CARON */
332   { "Eacute",   0xC9, 0x00C9, "E" }, /* LATIN CAPITAL LETTER E WITH ACUTE */
333   { "", 0xCA, 0x0118, "E" }, /* LATIN CAPITAL LETTER E WITH OGONEK */
334   { "Euml",     0xCB, 0x00CB, "E" }, /* LATIN CAPITAL LETTER E WITH DIAERESIS */
335   { "", 0xCC, 0x011A, "E" }, /* LATIN CAPITAL LETTER E WITH CARON */
336   { "Iacute",   0xCD, 0x00CD, "I" }, /* LATIN CAPITAL LETTER I WITH ACUTE */
337   { "Icirc",    0xCE, 0x00CE, "I" }, /* LATIN CAPITAL LETTER I WITH CIRCUMFLEX */
338   { "", 0xCF, 0x010E, "D" }, /* LATIN CAPITAL LETTER D WITH CARON */
339   { "ETH",      0xD0, 0x0110, "D" }, /* LATIN CAPITAL LETTER D WITH STROKE */
340   { "", 0xD1, 0x0143, "N" }, /* LATIN CAPITAL LETTER N WITH ACUTE */
341   { "", 0xD2, 0x0147, "N" }, /* LATIN CAPITAL LETTER N WITH CARON */
342   { "Oacute",   0xD3, 0x00D3, "O" }, /* LATIN CAPITAL LETTER O WITH ACUTE */
343   { "Ocirc",    0xD4, 0x00D4, "O" }, /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX */
344   { "", 0xD5, 0x0150, "O" }, /* LATIN CAPITAL LETTER O WITH DOUBLE ACUTE */
345   { "Ouml",     0xD6, 0x00D6, "O" }, /* LATIN CAPITAL LETTER O WITH DIAERESIS */
346   { "times",    0xD7, 0x00D7 }, /* MULTIPLICATION SIGN */
347   { "", 0xD8, 0x0158, "R" }, /* LATIN CAPITAL LETTER R WITH CARON */
348   { "", 0xD9, 0x016E, "U" }, /* LATIN CAPITAL LETTER U WITH RING ABOVE */
349   { "Uacute",   0xDA, 0x00DA, "U" }, /* LATIN CAPITAL LETTER U WITH ACUTE */
350   { "", 0xDB, 0x0170, "U" }, /* LATIN CAPITAL LETTER U WITH DOUBLE ACUTE */
351   { "Uuml",     0xDC, 0x00DC, "U" }, /* LATIN CAPITAL LETTER U WITH DIAERESIS */
352   { "Yacute",   0xDD, 0x00DD, "Y" }, /* LATIN CAPITAL LETTER Y WITH ACUTE */
353   { "", 0xDE, 0x0162, "T" }, /* LATIN CAPITAL LETTER T WITH CEDILLA */
354   { "szlig",    0xDF, 0x00DF, "ss" }, /* LATIN SMALL LETTER SHARP S (German) */
355   { "", 0xE0, 0x0155, "s" }, /* LATIN SMALL LETTER R WITH ACUTE */
356   { "aacute",   0xE1, 0x00E1, "a" }, /* LATIN SMALL LETTER A WITH ACUTE */
357   { "acirc",    0xE2, 0x00E2, "a" }, /* LATIN SMALL LETTER A WITH CIRCUMFLEX */
358   { "", 0xE3, 0x0103, "a" }, /* LATIN SMALL LETTER A WITH BREVE */
359   { "auml",     0xE4, 0x00E4, "a" }, /* LATIN SMALL LETTER A WITH DIAERESIS */
360   { "", 0xE5, 0x013A, "l" }, /* LATIN SMALL LETTER L WITH ACUTE */
361   { "", 0xE6, 0x0107, "c" }, /* LATIN SMALL LETTER C WITH ACUTE */
362   { "ccedil",   0xE7, 0x00E7, "c" }, /* LATIN SMALL LETTER C WITH CEDILLA */
363   { "", 0xE8, 0x010D, "c" }, /* LATIN SMALL LETTER C WITH CARON */
364   { "eacute",   0xE9, 0x00E9, "e" }, /* LATIN SMALL LETTER E WITH ACUTE */
365   { "", 0xEA, 0x0119, "e" }, /* LATIN SMALL LETTER E WITH OGONEK */
366   { "euml",     0xEB, 0x00EB, "e" }, /* LATIN SMALL LETTER E WITH DIAERESIS */
367   { "", 0xEC, 0x011B, "e" }, /* LATIN SMALL LETTER E WITH CARON */
368   { "iacute",   0xED, 0x00ED, "i" }, /* LATIN SMALL LETTER I WITH ACUTE */
369   { "icirc",    0xEE, 0x00EE, "i" }, /* LATIN SMALL LETTER I WITH CIRCUMFLEX */
370   { "", 0xEF, 0x010F, "d" }, /* LATIN SMALL LETTER D WITH CARON */
371   { "", 0xF0, 0x0111, "d" }, /* LATIN SMALL LETTER D WITH STROKE */
372   { "", 0xF1, 0x0144, "n" }, /* LATIN SMALL LETTER N WITH ACUTE */
373   { "", 0xF2, 0x0148, "n" }, /* LATIN SMALL LETTER N WITH CARON */
374   { "oacute",   0xF3, 0x00F3, "o" }, /* LATIN SMALL LETTER O WITH ACUTE */
375   { "ocirc",    0xF4, 0x00F4, "o" }, /* LATIN SMALL LETTER O WITH CIRCUMFLEX */
376   { "", 0xF5, 0x0151, "o" }, /* LATIN SMALL LETTER O WITH DOUBLE ACUTE */
377   { "ouml",     0xF6, 0x00F6, "o" }, /* LATIN SMALL LETTER O WITH DIAERESIS */
378   { "divide",   0xF7, 0x00F7 }, /* DIVISION SIGN */
379   { "", 0xF8, 0x0159, "r" }, /* LATIN SMALL LETTER R WITH CARON */
380   { "", 0xF9, 0x016F, "u" }, /* LATIN SMALL LETTER U WITH RING ABOVE */
381   { "uacute",   0xFA, 0x00FA, "u" }, /* LATIN SMALL LETTER U WITH ACUTE */
382   { "", 0xFB, 0x0171, "u" }, /* LATIN SMALL LETTER U WITH DOUBLE ACUTE */
383   { "uuml",     0xFC, 0x00FC, "u" }, /* LATIN SMALL LETTER U WITH DIAERESIS */
384   { "yacute",   0xFD, 0x00FD, "y" }, /* LATIN SMALL LETTER Y WITH ACUTE */
385   { "", 0xFE, 0x0163, "t" }, /* LATIN SMALL LETTER T WITH CEDILLA */
386   { "", 0xFF, 0x02D9 }, /* DOT ABOVE (Mandarin Chinese light tone) */
387   { NULL, 0, 0 }
388 };
389
390 /* Common map for koi8-u, koi8-r */
391 static iso_map_type koi8_map [] = {
392   { "", 0xa3, 0x0415, "io"}, /* CYRILLIC SMALL LETTER IO */
393   { "", 0xa4, 0x0454, "ie"}, /* CYRILLIC SMALL LETTER UKRAINIAN IE */
394   { "", 0xa6, 0x0456, "i"}, /* CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I */
395   { "", 0xa7, 0x0457, "yi"}, /* CYRILLIC SMALL LETTER YI */
396
397   { "", 0xb3, 0x04d7, "IO"}, /* CYRILLIC CAPITAL LETTER IO */
398   { "", 0xb4, 0x0404, "IE"}, /* CYRILLIC CAPITAL LETTER UKRAINIAN IE */
399   { "", 0xb6, 0x0406, "I"},  /* CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I */
400   { "", 0xb7, 0x0407, "YI"}, /* CYRILLIC CAPITAL LETTER YI */
401 /* { "", 0xbf, 0x}, / * CYRILLIC COPYRIGHT SIGN */ 
402   { "", 0xc0, 0x042e, "yu"}, /* CYRILLIC SMALL LETTER YU */        
403   { "", 0xc1, 0x0430, "a"}, /* CYRILLIC SMALL LETTER A */         
404   { "", 0xc2, 0x0431, "b"}, /* CYRILLIC SMALL LETTER BE */        
405   { "", 0xc3, 0x0446, "c"}, /* CYRILLIC SMALL LETTER TSE */        
406   { "", 0xc4, 0x0434, "d"}, /* CYRILLIC SMALL LETTER DE */        
407   { "", 0xc5, 0x0435, "e"}, /* CYRILLIC SMALL LETTER IE */        
408   { "", 0xc6, 0x0444, "f"}, /* CYRILLIC SMALL LETTER EF */        
409   { "", 0xc7, 0x0433, "g"}, /* CYRILLIC SMALL LETTER GHE */       
410   { "", 0xc8, 0x0445, "h"}, /* CYRILLIC SMALL LETTER HA */        
411   { "", 0xc9, 0x0438, "i"}, /* CYRILLIC SMALL LETTER I */         
412   { "", 0xca, 0x0439, "i"}, /* CYRILLIC SMALL LETTER SHORT I */   
413   { "", 0xcb, 0x043a, "k"}, /* CYRILLIC SMALL LETTER KA */        
414   { "", 0xcc, 0x043b, "l"}, /* CYRILLIC SMALL LETTER EL */        
415   { "", 0xcd, 0x043c, "m"}, /* CYRILLIC SMALL LETTER EM */        
416   { "", 0xce, 0x043d, "n"}, /* CYRILLIC SMALL LETTER EN */        
417   { "", 0xcf, 0x043e, "o"}, /* CYRILLIC SMALL LETTER O */         
418   { "", 0xd0, 0x043f, "p"}, /* CYRILLIC SMALL LETTER PE */        
419   { "", 0xd1, 0x044f, "ya"}, /* CYRILLIC SMALL LETTER YA */        
420   { "", 0xd2, 0x0440, "r"}, /* CYRILLIC SMALL LETTER ER */        
421   { "", 0xd3, 0x0441, "s"}, /* CYRILLIC SMALL LETTER ES */        
422   { "", 0xd4, 0x0442, "t"}, /* CYRILLIC SMALL LETTER TE */        
423   { "", 0xd5, 0x0443, "u"}, /* CYRILLIC SMALL LETTER U */         
424   { "", 0xd6, 0x0436, "zh"}, /* CYRILLIC SMALL LETTER ZHE */       
425   { "", 0xd7, 0x0432, "v"}, /* CYRILLIC SMALL LETTER VE */        
426   { "", 0xd8, 0x044c, "x"}, /* CYRILLIC SMALL LETTER SOFT SIGN */ 
427   { "", 0xd9, 0x044b, "y"}, /* CYRILLIC SMALL LETTER YERU */      
428   { "", 0xda, 0x0437, "z"}, /* CYRILLIC SMALL LETTER ZE */        
429   { "", 0xdb, 0x0448, "sh"}, /* CYRILLIC SMALL LETTER SHA */       
430   { "", 0xdc, 0x044d, "e"}, /* CYRILLIC SMALL LETTER E */         
431   { "", 0xdd, 0x0449, "shch"}, /* CYRILLIC SMALL LETTER SHCHA */     
432   { "", 0xde, 0x0447, "ch"}, /* CYRILLIC SMALL LETTER CHA */       
433   { "", 0xdf, 0x044a, "w"}, /* CYRILLIC SMALL LETTER HARD SIGN */ 
434   { "", 0xe0, 0x042d, "YU"}, /* CYRILLIC CAPITAL LETTER YU */       
435   { "", 0xe1, 0x0410, "A"}, /* CYRILLIC CAPITAL LETTER A */        
436   { "", 0xe2, 0x0411, "B"}, /* CYRILLIC CAPITAL LETTER BE */         
437   { "", 0xe3, 0x0426, "C"}, /* CYRILLIC CAPITAL LETTER TSE */        
438   { "", 0xe4, 0x0414, "D"}, /* CYRILLIC CAPITAL LETTER DE */        
439   { "", 0xe5, 0x0415, "E"}, /* CYRILLIC CAPITAL LETTER IE */        
440   { "", 0xe6, 0x0424, "F"}, /* CYRILLIC CAPITAL LETTER EF */        
441   { "", 0xe7, 0x0413, "G"}, /* CYRILLIC CAPITAL LETTER GHE */        
442   { "", 0xe8, 0x0425, "H"}, /* CYRILLIC CAPITAL LETTER HA */        
443   { "", 0xe9, 0x0418, "I"}, /* CYRILLIC CAPITAL LETTER I */        
444   { "", 0xea, 0x0419, "I"}, /* CYRILLIC CAPITAL LETTER SHORT I */  
445   { "", 0xeb, 0x041a, "K"}, /* CYRILLIC CAPITAL LETTER KA */        
446   { "", 0xec, 0x041b, "L"}, /* CYRILLIC CAPITAL LETTER EL */        
447   { "", 0xed, 0x041c, "M"}, /* CYRILLIC CAPITAL LETTER EM */        
448   { "", 0xee, 0x041d, "N"}, /* CYRILLIC CAPITAL LETTER EN */        
449   { "", 0xef, 0x041e, "O"}, /* CYRILLIC CAPITAL LETTER O */        
450   { "", 0xf0, 0x041f, "P"}, /* CYRILLIC CAPITAL LETTER PE */        
451   { "", 0xf1, 0x042f, "YA"}, /* CYRILLIC CAPITAL LETTER YA */       
452   { "", 0xf2, 0x0420, "R"}, /* CYRILLIC CAPITAL LETTER ER */        
453   { "", 0xf3, 0x0421, "S"}, /* CYRILLIC CAPITAL LETTER ES */        
454   { "", 0xf4, 0x0422, "T"}, /* CYRILLIC CAPITAL LETTER TE */        
455   { "", 0xf5, 0x0423, "U"}, /* CYRILLIC CAPITAL LETTER U */        
456   { "", 0xf6, 0x0416, "ZH"}, /* CYRILLIC CAPITAL LETTER ZHE */       
457   { "", 0xf7, 0x0412, "V"}, /* CYRILLIC CAPITAL LETTER VE */        
458   { "", 0xf8, 0x042c, "X"}, /* CYRILLIC CAPITAL LETTER SOFT SIGN */
459   { "", 0xf9, 0x042b, "Y"}, /* CYRILLIC CAPITAL LETTER YERU */     
460   { "", 0xfa, 0x0417, "Z"}, /* CYRILLIC CAPITAL LETTER ZE */        
461   { "", 0xfb, 0x0428, "SH"}, /* CYRILLIC CAPITAL LETTER SHA */       
462   { "", 0xfc, 0x042d, "E"}, /* CYRILLIC CAPITAL LETTER E */        
463   { "", 0xfd, 0x0429, "SHCH"}, /* CYRILLIC CAPITAL LETTER SHCHA */     
464   { "", 0xfe, 0x0427, "CH"}, /* CYRILLIC CAPITAL LETTER CHE */      
465   { "", 0xff, 0x042a, "W"}, /* CYRILLIC CAPITAL LETTER HARD SIGN */
466   { NULL, 0, 0 }
467 };            
468
469 encoding_type encoding_table[] = {
470   { no_encoding, "(no encoding)", NULL },
471   { US_ASCII,    "US-ASCII",    asis_map },
472   { ISO_8859_1,  "iso-8859-1",  (iso_map_type *) iso8859_1_map },
473   { ISO_8859_2,  "iso-8859-2",  (iso_map_type *) iso8859_2_map },
474   { ISO_8859_3,  "iso-8859-3",  NULL },
475   { ISO_8859_4,  "iso-8859-4",  NULL },
476   { ISO_8859_5,  "iso-8859-5",  NULL },
477   { ISO_8859_6,  "iso-8859-6",  NULL },
478   { ISO_8859_7,  "iso-8859-7",  NULL },
479   { ISO_8859_8,  "iso-8859-8",  NULL },
480   { ISO_8859_9,  "iso-8859-9",  NULL },
481   { ISO_8859_10, "iso-8859-10", NULL },
482   { ISO_8859_11, "iso-8859-11", NULL },
483   { ISO_8859_12, "iso-8859-12", NULL },
484   { ISO_8859_13, "iso-8859-13", NULL },
485   { ISO_8859_14, "iso-8859-14", NULL },
486   { ISO_8859_15, "iso-8859-15", (iso_map_type *) iso8859_15_map },
487   { KOI8_R,      "koi8-r",      (iso_map_type *) koi8_map },
488   { KOI8_U,      "koi8-u",      (iso_map_type *) koi8_map },
489   { UTF_8,       "utf-8",       asis_map },  /* specific code for this below */
490   { last_encoding_code, NULL, NULL }
491 };
492
493 \f
494 /* List of HTML entities.  */
495 static struct { const char *html; unsigned int unicode; } unicode_map[] = {
496 /* Extracted from http://www.w3.org/TR/html401/sgml/entities.html through
497    sed -n -e 's|<!ENTITY \([^ ][^ ]*\) *CDATA "[&]#\([0-9][0-9]*\);".*|  { "\1", \2 },|p'
498    | LC_ALL=C sort -k2  */
499   { "AElig",     198 },
500   { "Aacute",    193 },
501   { "Acirc",     194 },
502   { "Agrave",    192 },
503   { "Alpha",     913 },
504   { "Aring",     197 },
505   { "Atilde",    195 },
506   { "Auml",      196 },
507   { "Beta",      914 },
508   { "Ccedil",    199 },
509   { "Chi",       935 },
510   { "Dagger",   8225 },
511   { "Delta",     916 },
512   { "ETH",       208 },
513   { "Eacute",    201 },
514   { "Ecirc",     202 },
515   { "Egrave",    200 },
516   { "Epsilon",   917 },
517   { "Eta",       919 },
518   { "Euml",      203 },
519   { "Gamma",     915 },
520   { "Iacute",    205 },
521   { "Icirc",     206 },
522   { "Igrave",    204 },
523   { "Iota",      921 },
524   { "Iuml",      207 },
525   { "Kappa",     922 },
526   { "Lambda",    923 },
527   { "Mu",        924 },
528   { "Ntilde",    209 },
529   { "Nu",        925 },
530   { "OElig",     338 },
531   { "Oacute",    211 },
532   { "Ocirc",     212 },
533   { "Ograve",    210 },
534   { "Omega",     937 },
535   { "Omicron",   927 },
536   { "Oslash",    216 },
537   { "Otilde",    213 },
538   { "Ouml",      214 },
539   { "Phi",       934 },
540   { "Pi",        928 },
541   { "Prime",    8243 },
542   { "Psi",       936 },
543   { "Rho",       929 },
544   { "Scaron",    352 },
545   { "Sigma",     931 },
546   { "THORN",     222 },
547   { "Tau",       932 },
548   { "Theta",     920 },
549   { "Uacute",    218 },
550   { "Ucirc",     219 },
551   { "Ugrave",    217 },
552   { "Upsilon",   933 },
553   { "Uuml",      220 },
554   { "Xi",        926 },
555   { "Yacute",    221 },
556   { "Yuml",      376 },
557   { "Zeta",      918 },
558   { "aacute",    225 },
559   { "acirc",     226 },
560   { "acute",     180 },
561   { "aelig",     230 },
562   { "agrave",    224 },
563   { "alefsym",  8501 },
564   { "alpha",     945 },
565   { "amp",        38 },
566   { "and",      8743 },
567   { "ang",      8736 },
568   { "aring",     229 },
569   { "asymp",    8776 },
570   { "atilde",    227 },
571   { "auml",      228 },
572   { "bdquo",    8222 },
573   { "beta",      946 },
574   { "brvbar",    166 },
575   { "bull",     8226 },
576   { "cap",      8745 },
577   { "ccedil",    231 },
578   { "cedil",     184 },
579   { "cent",      162 },
580   { "chi",       967 },
581   { "circ",      710 },
582   { "clubs",    9827 },
583   { "cong",     8773 },
584   { "copy",      169 },
585   { "crarr",    8629 },
586   { "cup",      8746 },
587   { "curren",    164 },
588   { "dArr",     8659 },
589   { "dagger",   8224 },
590   { "darr",     8595 },
591   { "deg",       176 },
592   { "delta",     948 },
593   { "diams",    9830 },
594   { "divide",    247 },
595   { "eacute",    233 },
596   { "ecirc",     234 },
597   { "egrave",    232 },
598   { "empty",    8709 },
599   { "emsp",     8195 },
600   { "ensp",     8194 },
601   { "epsilon",   949 },
602   { "equiv",    8801 },
603   { "eta",       951 },
604   { "eth",       240 },
605   { "euml",      235 },
606   { "euro",     8364 },
607   { "exist",    8707 },
608   { "fnof",      402 },
609   { "forall",   8704 },
610   { "frac12",    189 },
611   { "frac14",    188 },
612   { "frac34",    190 },
613   { "frasl",    8260 },
614   { "gamma",     947 },
615   { "ge",       8805 },
616   { "gt",         62 },
617   { "hArr",     8660 },
618   { "harr",     8596 },
619   { "hearts",   9829 },
620   { "hellip",   8230 },
621   { "iacute",    237 },
622   { "icirc",     238 },
623   { "iexcl",     161 },
624   { "igrave",    236 },
625   { "image",    8465 },
626   { "infin",    8734 },
627   { "int",      8747 },
628   { "iota",      953 },
629   { "iquest",    191 },
630   { "isin",     8712 },
631   { "iuml",      239 },
632   { "kappa",     954 },
633   { "lArr",     8656 },
634   { "lambda",    955 },
635   { "lang",     9001 },
636   { "laquo",     171 },
637   { "larr",     8592 },
638   { "lceil",    8968 },
639   { "ldquo",    8220 },
640   { "le",       8804 },
641   { "lfloor",   8970 },
642   { "lowast",   8727 },
643   { "loz",      9674 },
644   { "lrm",      8206 },
645   { "lsaquo",   8249 },
646   { "lsquo",    8216 },
647   { "lt",         60 },
648   { "macr",      175 },
649   { "mdash",    8212 },
650   { "micro",     181 },
651   { "middot",    183 },
652   { "minus",    8722 },
653   { "mu",        956 },
654   { "nabla",    8711 },
655   { "nbsp",      160 },
656   { "ndash",    8211 },
657   { "ne",       8800 },
658   { "ni",       8715 },
659   { "not",       172 },
660   { "notin",    8713 },
661   { "nsub",     8836 },
662   { "ntilde",    241 },
663   { "nu",        957 },
664   { "oacute",    243 },
665   { "ocirc",     244 },
666   { "oelig",     339 },
667   { "ograve",    242 },
668   { "oline",    8254 },
669   { "omega",     969 },
670   { "omicron",   959 },
671   { "oplus",    8853 },
672   { "or",       8744 },
673   { "ordf",      170 },
674   { "ordm",      186 },
675   { "oslash",    248 },
676   { "otilde",    245 },
677   { "otimes",   8855 },
678   { "ouml",      246 },
679   { "para",      182 },
680   { "part",     8706 },
681   { "permil",   8240 },
682   { "perp",     8869 },
683   { "phi",       966 },
684   { "pi",        960 },
685   { "piv",       982 },
686   { "plusmn",    177 },
687   { "pound",     163 },
688   { "prime",    8242 },
689   { "prod",     8719 },
690   { "prop",     8733 },
691   { "psi",       968 },
692   { "quot",       34 },
693   { "rArr",     8658 },
694   { "radic",    8730 },
695   { "rang",     9002 },
696   { "raquo",     187 },
697   { "rarr",     8594 },
698   { "rceil",    8969 },
699   { "rdquo",    8221 },
700   { "real",     8476 },
701   { "reg",       174 },
702   { "rfloor",   8971 },
703   { "rho",       961 },
704   { "rlm",      8207 },
705   { "rsaquo",   8250 },
706   { "rsquo",    8217 },
707   { "sbquo",    8218 },
708   { "scaron",    353 },
709   { "sdot",     8901 },
710   { "sect",      167 },
711   { "shy",       173 },
712   { "sigma",     963 },
713   { "sigmaf",    962 },
714   { "sim",      8764 },
715   { "spades",   9824 },
716   { "sub",      8834 },
717   { "sube",     8838 },
718   { "sum",      8721 },
719   { "sup",      8835 },
720   { "sup1",      185 },
721   { "sup2",      178 },
722   { "sup3",      179 },
723   { "supe",     8839 },
724   { "szlig",     223 },
725   { "tau",       964 },
726   { "there4",   8756 },
727   { "theta",     952 },
728   { "thetasym",  977 },
729   { "thinsp",   8201 },
730   { "thorn",     254 },
731   { "tilde",     732 },
732   { "times",     215 },
733   { "trade",    8482 },
734   { "uArr",     8657 },
735   { "uacute",    250 },
736   { "uarr",     8593 },
737   { "ucirc",     251 },
738   { "ugrave",    249 },
739   { "uml",       168 },
740   { "upsih",     978 },
741   { "upsilon",   965 },
742   { "uuml",      252 },
743   { "weierp",   8472 },
744   { "xi",        958 },
745   { "yacute",    253 },
746   { "yen",       165 },
747   { "yuml",      255 },
748   { "zeta",      950 },
749   { "zwj",      8205 },
750   { "zwnj",     8204 }
751 };
752  
753
754 \f
755 /* To update this list of language codes, download the current data from
756    http://www.loc.gov/standards/iso639-2; specifically,
757    http://www.loc.gov/standards/iso639-2/ISO-639-2_values_8bits-8559-1.txt.
758    Run cut -d\| -f 3,4 <ISO-639-2_values_8bits-8559-1.txt | sort -u >/tmp/639.2
759    
760    Extract the C table below to a file, say /tmp/lc, and run:
761    cut -c 10- /tmp/lc| sed -e 's/", "/|/' -e 's,".*,,'>/tmp/lang.2
762    
763    Then: comm -3 /tmp/639.2 /tmp/lang.c.
764    
765    Also update the enum in lang.h.  */
766
767 language_type language_table[] = {
768   { aa, "aa", "Afar" },
769   { ab, "ab", "Abkhazian" },
770   { ae, "ae", "Avestan" },
771   { af, "af", "Afrikaans" },
772   { ak, "ak", "Akan" },
773   { am, "am", "Amharic" },
774   { an, "an", "Aragonese" },
775   { ar, "ar", "Arabic" },
776   { as, "as", "Assamese" },
777   { av, "av", "Avaric" },
778   { ay, "ay", "Aymara" },
779   { az, "az", "Azerbaijani" },
780   { ba, "ba", "Bashkir" },
781   { be, "be", "Belarusian" },
782   { bg, "bg", "Bulgarian" },
783   { bh, "bh", "Bihari" },
784   { bi, "bi", "Bislama" },
785   { bm, "bm", "Bambara" },
786   { bn, "bn", "Bengali" },
787   { bo, "bo", "Tibetan" },
788   { br, "br", "Breton" },
789   { bs, "bs", "Bosnian" },
790   { ca, "ca", "Catalan; Valencian" },
791   { ce, "ce", "Chechen" },
792   { ch, "ch", "Chamorro" },
793   { co, "co", "Corsican" },
794   { cr, "cr", "Cree" },
795   { cs, "cs", "Czech" },
796   { cu, "cu", "Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic" },
797   { cv, "cv", "Chuvash" },
798   { cy, "cy", "Welsh" },
799   { da, "da", "Danish" },
800   { de, "de", "German" },
801   { dv, "dv", "Divehi; Dhivehi; Maldivian" },
802   { dz, "dz", "Dzongkha" },
803   { ee, "ee", "Ewe" },
804   { el, "el", "Greek, Modern (1453-)" },
805   { en, "en", "English" },
806   { eo, "eo", "Esperanto" },
807   { es, "es", "Spanish; Castilian" },
808   { et, "et", "Estonian" },
809   { eu, "eu", "Basque" },
810   { fa, "fa", "Persian" },
811   { ff, "ff", "Fulah" },
812   { fi, "fi", "Finnish" },
813   { fj, "fj", "Fijian" },
814   { fo, "fo", "Faroese" },
815   { fr, "fr", "French" },
816   { fy, "fy", "Western Frisian" },
817   { ga, "ga", "Irish" },
818   { gd, "gd", "Gaelic; Scottish Gaelic" },
819   { gl, "gl", "Galician" },
820   { gn, "gn", "Guarani" },
821   { gu, "gu", "Gujarati" },
822   { gv, "gv", "Manx" },
823   { ha, "ha", "Hausa" },
824   { he, "he", "Hebrew" } /* (formerly iw) */,
825   { hi, "hi", "Hindi" },
826   { ho, "ho", "Hiri Motu" },
827   { hr, "hr", "Croatian" },
828   { ht, "ht", "Haitian; Haitian Creole" },
829   { hu, "hu", "Hungarian" },
830   { hy, "hy", "Armenian" },
831   { hz, "hz", "Herero" },
832   { ia, "ia", "Interlingua (International Auxiliary Language Association)" },
833   { id, "id", "Indonesian" } /* (formerly in) */,
834   { ie, "ie", "Interlingue" },
835   { ig, "ig", "Igbo" },
836   { ii, "ii", "Sichuan Yi" },
837   { ik, "ik", "Inupiaq" },
838   { io, "io", "Ido" },
839   { is, "is", "Icelandic" },
840   { it, "it", "Italian" },
841   { iu, "iu", "Inuktitut" },
842   { ja, "ja", "Japanese" },
843   { jv, "jv", "Javanese" },
844   { ka, "ka", "Georgian" },
845   { kg, "kg", "Kongo" },
846   { ki, "ki", "Kikuyu; Gikuyu" },
847   { kj, "kj", "Kuanyama; Kwanyama" },
848   { kk, "kk", "Kazakh" },
849   { kl, "kl", "Kalaallisut; Greenlandic" },
850   { km, "km", "Central Khmer" },
851   { kn, "kn", "Kannada" },
852   { ko, "ko", "Korean" },
853   { kr, "kr", "Kanuri" },
854   { ks, "ks", "Kashmiri" },
855   { ku, "ku", "Kurdish" },
856   { kv, "kv", "Komi" },
857   { kw, "kw", "Cornish" },
858   { ky, "ky", "Kirghiz; Kyrgyz" },
859   { la, "la", "Latin" },
860   { lb, "lb", "Luxembourgish; Letzeburgesch" },
861   { lg, "lg", "Ganda" },
862   { li, "li", "Limburgan; Limburger; Limburgish" },
863   { ln, "ln", "Lingala" },
864   { lo, "lo", "Lao" },
865   { lt, "lt", "Lithuanian" },
866   { lu, "lu", "Luba-Katanga" },
867   { lv, "lv", "Latvian" },
868   { mg, "mg", "Malagasy" },
869   { mh, "mh", "Marshallese" },
870   { mi, "mi", "Maori" },
871   { mk, "mk", "Macedonian" },
872   { ml, "ml", "Malayalam" },
873   { mn, "mn", "Mongolian" },
874   { mo, "mo", "Moldavian" },
875   { mr, "mr", "Marathi" },
876   { ms, "ms", "Malay" },
877   { mt, "mt", "Maltese" },
878   { my, "my", "Burmese" },
879   { na, "na", "Nauru" },
880   { nb, "nb", "BokmÃ¥l, Norwegian; Norwegian BokmÃ¥l" },
881   { nd, "nd", "Ndebele, North; North Ndebele" },
882   { ne, "ne", "Nepali" },
883   { ng, "ng", "Ndonga" },
884   { nl, "nl", "Dutch; Flemish" },
885   { nn, "nn", "Norwegian Nynorsk; Nynorsk, Norwegian" },
886   { no, "no", "Norwegian" },
887   { nr, "nr", "Ndebele, South; South Ndebele" },
888   { nv, "nv", "Navajo; Navaho" },
889   { ny, "ny", "Chichewa; Chewa; Nyanja" },
890   { oc, "oc", "Occitan (post 1500); Provençal" },
891   { oj, "oj", "Ojibwa" },
892   { om, "om", "Oromo" },
893   { or, "or", "Oriya" },
894   { os, "os", "Ossetian; Ossetic" },
895   { pa, "pa", "Panjabi; Punjabi" },
896   { pi, "pi", "Pali" },
897   { pl, "pl", "Polish" },
898   { ps, "ps", "Pushto" },
899   { pt, "pt", "Portuguese" },
900   { qu, "qu", "Quechua" },
901   { rm, "rm", "Romansh" },
902   { rn, "rn", "Rundi" },
903   { ro, "ro", "Romanian" },
904   { ru, "ru", "Russian" },
905   { rw, "rw", "Kinyarwanda" },
906   { sa, "sa", "Sanskrit" },
907   { sc, "sc", "Sardinian" },
908   { sd, "sd", "Sindhi" },
909   { se, "se", "Northern Sami" },
910   { sg, "sg", "Sango" },
911   { si, "si", "Sinhala; Sinhalese" },
912   { sk, "sk", "Slovak" },
913   { sl, "sl", "Slovenian" },
914   { sm, "sm", "Samoan" },
915   { sn, "sn", "Shona" },
916   { so, "so", "Somali" },
917   { sq, "sq", "Albanian" },
918   { sr, "sr", "Serbian" },
919   { ss, "ss", "Swati" },
920   { st, "st", "Sotho, Souther" },
921   { su, "su", "Sundanese" },
922   { sv, "sv", "Swedish" },
923   { sw, "sw", "Swahili" },
924   { ta, "ta", "Tamil" },
925   { te, "te", "Telugu" },
926   { tg, "tg", "Tajik" },
927   { th, "th", "Thai" },
928   { ti, "ti", "Tigrinya" },
929   { tk, "tk", "Turkmen" },
930   { tl, "tl", "Tagalog" },
931   { tn, "tn", "Tswana" },
932   { to, "to", "Tonga (Tonga Islands)" },
933   { tr, "tr", "Turkish" },
934   { ts, "ts", "Tsonga" },
935   { tt, "tt", "Tatar" },
936   { tw, "tw", "Twi" },
937   { ty, "ty", "Tahitian" },
938   { ug, "ug", "Uighur; Uyghur" },
939   { uk, "uk", "Ukrainian" },
940   { ur, "ur", "Urdu" },
941   { uz, "uz", "Uzbek" },
942   { ve, "ve", "Venda" },
943   { vi, "vi", "Vietnamese" },
944   { vo, "vo", "Volapük" },
945   { wa, "wa", "Walloon" },
946   { wo, "wo", "Wolof" },
947   { xh, "xh", "Xhosa" },
948   { yi, "yi", "Yiddish" } /* (formerly ji) */,
949   { yo, "yo", "Yoruba" },
950   { za, "za", "Zhuang; Chuang" },
951   { zh, "zh", "Chinese" },
952   { zu, "zu", "Zulu" },
953   { last_language_code, NULL, NULL }
954 };
955
956 static const char *
957 cm_search_iso_map_char (byte_t ch)
958 {
959   int i;
960   iso_map_type *iso = encoding_table[document_encoding_code].isotab;
961
962   /* If no conversion table for this encoding, quit.  */
963   if (!iso)
964     return NULL;
965   
966   for (i = 0; iso[i].html; i++)
967     if (iso[i].bytecode == ch)
968       return iso[i].translit;
969
970   return NULL;
971 }
972
973 const char *
974 lang_transliterate_char (byte_t ch)
975 {
976   if (transliterate_file_names
977       && document_encoding_code != no_encoding)
978     return cm_search_iso_map_char (ch);
979   return NULL;
980 }       
981
982
983 \f
984 /* Given a language code LL_CODE, return a "default" country code (in
985    new memory).  We use the same table as gettext, and return LL_CODE
986    uppercased in the absence of any better possibility, with a warning.
987    (gettext silently defaults to the C locale, but we want to give users
988    a shot at fixing ambiguities.)  We also return en_US for en, while
989    gettext does not.  */
990
991 #define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
992
993 static char *
994 default_country_for_lang (const char *ll_code)
995 {
996   /* This table comes from msginit.c in gettext (0.16.1).  It is
997      intentionally copied verbatim even though the format is not the
998      most convenient for our purposes, to make updates simple.  */
999   static const char *locales_with_principal_territory[] = {
1000                 /* Language     Main territory */
1001     "ace_ID",   /* Achinese     Indonesia */
1002     "af_ZA",    /* Afrikaans    South Africa */
1003     "ak_GH",    /* Akan         Ghana */
1004     "am_ET",    /* Amharic      Ethiopia */
1005     "an_ES",    /* Aragonese    Spain */
1006     "ang_GB",   /* Old English  Britain */
1007     "as_IN",    /* Assamese     India */
1008     "av_RU",    /* Avaric       Russia */
1009     "awa_IN",   /* Awadhi       India */
1010     "az_AZ",    /* Azerbaijani  Azerbaijan */
1011     "bad_CF",   /* Banda        Central African Republic */
1012     "ban_ID",   /* Balinese     Indonesia */
1013     "be_BY",    /* Belarusian   Belarus */
1014     "bem_ZM",   /* Bemba        Zambia */
1015     "bg_BG",    /* Bulgarian    Bulgaria */
1016     "bho_IN",   /* Bhojpuri     India */
1017     "bik_PH",   /* Bikol        Philippines */
1018     "bin_NG",   /* Bini         Nigeria */
1019     "bm_ML",    /* Bambara      Mali */
1020     "bn_IN",    /* Bengali      India */
1021     "bo_CN",    /* Tibetan      China */
1022     "br_FR",    /* Breton       France */
1023     "bs_BA",    /* Bosnian      Bosnia */
1024     "btk_ID",   /* Batak        Indonesia */
1025     "bug_ID",   /* Buginese     Indonesia */
1026     "ca_ES",    /* Catalan      Spain */
1027     "ce_RU",    /* Chechen      Russia */
1028     "ceb_PH",   /* Cebuano      Philippines */
1029     "co_FR",    /* Corsican     France */
1030     "cr_CA",    /* Cree         Canada */
1031     "cs_CZ",    /* Czech        Czech Republic */
1032     "csb_PL",   /* Kashubian    Poland */
1033     "cy_GB",    /* Welsh        Britain */
1034     "da_DK",    /* Danish       Denmark */
1035     "de_DE",    /* German       Germany */
1036     "din_SD",   /* Dinka        Sudan */
1037     "doi_IN",   /* Dogri        India */
1038     "dv_MV",    /* Divehi       Maldives */
1039     "dz_BT",    /* Dzongkha     Bhutan */
1040     "ee_GH",    /* Ã‰wé                Ghana */
1041     "el_GR",    /* Greek        Greece */
1042     /* Don't put "en_GB" or "en_US" here.  That would be asking for fruitless
1043        political discussion.  */
1044     "es_ES",    /* Spanish      Spain */
1045     "et_EE",    /* Estonian     Estonia */
1046     "fa_IR",    /* Persian      Iran */
1047     "fi_FI",    /* Finnish      Finland */
1048     "fil_PH",   /* Filipino     Philippines */
1049     "fj_FJ",    /* Fijian       Fiji */
1050     "fo_FO",    /* Faroese      Faeroe Islands */
1051     "fon_BJ",   /* Fon          Benin */
1052     "fr_FR",    /* French       France */
1053     "fy_NL",    /* Western Frisian      Netherlands */
1054     "ga_IE",    /* Irish        Ireland */
1055     "gd_GB",    /* Scots        Britain */
1056     "gon_IN",   /* Gondi        India */
1057     "gsw_CH",   /* Swiss German Switzerland */
1058     "gu_IN",    /* Gujarati     India */
1059     "he_IL",    /* Hebrew       Israel */
1060     "hi_IN",    /* Hindi        India */
1061     "hil_PH",   /* Hiligaynon   Philippines */
1062     "hr_HR",    /* Croatian     Croatia */
1063     "ht_HT",    /* Haitian      Haiti */
1064     "hu_HU",    /* Hungarian    Hungary */
1065     "hy_AM",    /* Armenian     Armenia */
1066     "id_ID",    /* Indonesian   Indonesia */
1067     "ig_NG",    /* Igbo         Nigeria */
1068     "ii_CN",    /* Sichuan Yi   China */
1069     "ilo_PH",   /* Iloko        Philippines */
1070     "is_IS",    /* Icelandic    Iceland */
1071     "it_IT",    /* Italian      Italy */
1072     "ja_JP",    /* Japanese     Japan */
1073     "jab_NG",   /* Hyam         Nigeria */
1074     "jv_ID",    /* Javanese     Indonesia */
1075     "ka_GE",    /* Georgian     Georgia */
1076     "kab_DZ",   /* Kabyle       Algeria */
1077     "kaj_NG",   /* Jju          Nigeria */
1078     "kam_KE",   /* Kamba        Kenya */
1079     "kmb_AO",   /* Kimbundu     Angola */
1080     "kcg_NG",   /* Tyap         Nigeria */
1081     "kdm_NG",   /* Kagoma       Nigeria */
1082     "kg_CD",    /* Kongo        Democratic Republic of Congo */
1083     "kk_KZ",    /* Kazakh       Kazakhstan */
1084     "kl_GL",    /* Kalaallisut  Greenland */
1085     "km_KH",    /* Khmer        Cambodia */
1086     "kn_IN",    /* Kannada      India */
1087     "ko_KR",    /* Korean       Korea (South) */
1088     "kok_IN",   /* Konkani      India */
1089     "kr_NG",    /* Kanuri       Nigeria */
1090     "kru_IN",   /* Kurukh       India */
1091     "lg_UG",    /* Ganda        Uganda */
1092     "li_BE",    /* Limburgish   Belgium */
1093     "lo_LA",    /* Laotian      Laos */
1094     "lt_LT",    /* Lithuanian   Lithuania */
1095     "lu_CD",    /* Luba-Katanga Democratic Republic of Congo */
1096     "lua_CD",   /* Luba-Lulua   Democratic Republic of Congo */
1097     "luo_KE",   /* Luo          Kenya */
1098     "lv_LV",    /* Latvian      Latvia */
1099     "mad_ID",   /* Madurese     Indonesia */
1100     "mag_IN",   /* Magahi       India */
1101     "mai_IN",   /* Maithili     India */
1102     "mak_ID",   /* Makasar      Indonesia */
1103     "man_ML",   /* Mandingo     Mali */
1104     "men_SL",   /* Mende        Sierra Leone */
1105     "mg_MG",    /* Malagasy     Madagascar */
1106     "min_ID",   /* Minangkabau  Indonesia */
1107     "mk_MK",    /* Macedonian   Macedonia */
1108     "ml_IN",    /* Malayalam    India */
1109     "mn_MN",    /* Mongolian    Mongolia */
1110     "mni_IN",   /* Manipuri     India */
1111     "mos_BF",   /* Mossi        Burkina Faso */
1112     "mr_IN",    /* Marathi      India */
1113     "ms_MY",    /* Malay        Malaysia */
1114     "mt_MT",    /* Maltese      Malta */
1115     "mwr_IN",   /* Marwari      India */
1116     "my_MM",    /* Burmese      Myanmar */
1117     "na_NR",    /* Nauru        Nauru */
1118     "nah_MX",   /* Nahuatl      Mexico */
1119     "nap_IT",   /* Neapolitan   Italy */
1120     "nb_NO",    /* Norwegian BokmÃ¥l    Norway */
1121     "nds_DE",   /* Low Saxon    Germany */
1122     "ne_NP",    /* Nepali       Nepal */
1123     "nl_NL",    /* Dutch        Netherlands */
1124     "nn_NO",    /* Norwegian Nynorsk    Norway */
1125     "no_NO",    /* Norwegian    Norway */
1126     "nr_ZA",    /* South Ndebele        South Africa */
1127     "nso_ZA",   /* Northern Sotho       South Africa */
1128     "nym_TZ",   /* Nyamwezi     Tanzania */
1129     "nyn_UG",   /* Nyankole     Uganda */
1130     "oc_FR",    /* Occitan      France */
1131     "oj_CA",    /* Ojibwa       Canada */
1132     "or_IN",    /* Oriya        India */
1133     "pa_IN",    /* Punjabi      India */
1134     "pag_PH",   /* Pangasinan   Philippines */
1135     "pam_PH",   /* Pampanga     Philippines */
1136     "pbb_CO",   /* Páez                Colombia */
1137     "pl_PL",    /* Polish       Poland */
1138     "ps_AF",    /* Pashto       Afghanistan */
1139     "pt_PT",    /* Portuguese   Portugal */
1140     "raj_IN",   /* Rajasthani   India */
1141     "rm_CH",    /* Rhaeto-Roman Switzerland */
1142     "rn_BI",    /* Kirundi      Burundi */
1143     "ro_RO",    /* Romanian     Romania */
1144     "ru_RU",    /* Russian      Russia */
1145     "sa_IN",    /* Sanskrit     India */
1146     "sas_ID",   /* Sasak        Indonesia */
1147     "sat_IN",   /* Santali      India */
1148     "sc_IT",    /* Sardinian    Italy */
1149     "scn_IT",   /* Sicilian     Italy */
1150     "sg_CF",    /* Sango        Central African Republic */
1151     "shn_MM",   /* Shan         Myanmar */
1152     "si_LK",    /* Sinhala      Sri Lanka */
1153     "sid_ET",   /* Sidamo       Ethiopia */
1154     "sk_SK",    /* Slovak       Slovakia */
1155     "sl_SI",    /* Slovenian    Slovenia */
1156     "so_SO",    /* Somali       Somalia */
1157     "sq_AL",    /* Albanian     Albania */
1158     "sr_RS",    /* Serbian      Serbia */
1159     "sr_YU",    /* Serbian      Yugoslavia */
1160     "srr_SN",   /* Serer        Senegal */
1161     "suk_TZ",   /* Sukuma       Tanzania */
1162     "sus_GN",   /* Susu         Guinea */
1163     "sv_SE",    /* Swedish      Sweden */
1164     "te_IN",    /* Telugu       India */
1165     "tem_SL",   /* Timne        Sierra Leone */
1166     "tet_ID",   /* Tetum        Indonesia */
1167     "tg_TJ",    /* Tajik        Tajikistan */
1168     "th_TH",    /* Thai         Thailand */
1169     "tiv_NG",   /* Tiv          Nigeria */
1170     "tk_TM",    /* Turkmen      Turkmenistan */
1171     "tl_PH",    /* Tagalog      Philippines */
1172     "to_TO",    /* Tonga        Tonga */
1173     "tr_TR",    /* Turkish      Turkey */
1174     "tum_MW",   /* Tumbuka      Malawi */
1175     "uk_UA",    /* Ukrainian    Ukraine */
1176     "umb_AO",   /* Umbundu      Angola */
1177     "ur_PK",    /* Urdu         Pakistan */
1178     "uz_UZ",    /* Uzbek        Uzbekistan */
1179     "ve_ZA",    /* Venda        South Africa */
1180     "vi_VN",    /* Vietnamese   Vietnam */
1181     "wa_BE",    /* Walloon      Belgium */
1182     "wal_ET",   /* Walamo       Ethiopia */
1183     "war_PH",   /* Waray        Philippines */
1184     "wen_DE",   /* Sorbian      Germany */
1185     "yao_MW",   /* Yao          Malawi */
1186     "zap_MX"    /* Zapotec      Mexico */
1187   };
1188   int c;
1189   int cc_len;
1190   int ll_len = strlen (ll_code);
1191   char *cc_code = xmalloc (ll_len + 1 + 1);
1192   int principal_len = SIZEOF (locales_with_principal_territory);
1193   
1194   strcpy (cc_code, ll_code);
1195   strcat (cc_code, "_");
1196   cc_len = ll_len + 1;
1197   
1198   for (c = 0; c < principal_len; c++)
1199     {
1200       const char *principal_locale = locales_with_principal_territory[c];
1201       if (strncmp (principal_locale, cc_code, cc_len) == 0)
1202         {
1203           const char *underscore = strchr (principal_locale, '_');
1204           /* should always be there, but in case ... */
1205           free (cc_code);
1206           cc_code = xstrdup (underscore ? underscore + 1 : principal_locale);
1207           break;
1208         }
1209     }
1210   
1211   /* If we didn't find one to copy, warn and duplicate.  */
1212   if (c == principal_len)
1213     {
1214       if (mbscasecmp (ll_code, "en") == 0)
1215         cc_code = xstrdup ("en_US");
1216       else
1217         {
1218           warning (_("no default territory known for language `%s'"), ll_code);
1219           for (c = 0; c < strlen (ll_code); c++)
1220             cc_code[c] = toupper (ll_code[c]);
1221           cc_code[c] = 0;
1222           /* We're probably wasting a byte, oops.  */
1223         }
1224     }
1225   
1226   return cc_code;
1227 }
1228
1229
1230 \f
1231 /* @documentlanguage ARG.  We set the global `document_language' (which
1232    `getdocumenttext' uses) to the gettextish string, and 
1233    `language_code' to the corresponding enum value.  */
1234
1235 void
1236 cm_documentlanguage (void)
1237 {
1238   language_code_type c;
1239   char *lang_arg, *ll_part, *cc_part, *locale_string;
1240   char *underscore;
1241
1242   /* Read the line with the language code on it.  */
1243   get_rest_of_line (0, &lang_arg);
1244
1245   /* What we're passed might be either just a language code LL (we'll 
1246      also need it with the usual _CC appended, if there is one), or a
1247      locale name as used by gettext, LL_CC (we'll also need its
1248      constituents separately).  */
1249   underscore = strchr (lang_arg, '_');
1250   if (underscore)
1251     {
1252       ll_part = substring (lang_arg, underscore);
1253       cc_part = xstrdup (underscore + 1);
1254       locale_string = xstrdup (lang_arg);
1255     }
1256   else
1257     {
1258       ll_part = xstrdup (lang_arg);
1259       cc_part = default_country_for_lang (ll_part);
1260       locale_string = xmalloc (strlen (ll_part) + 1 + strlen (cc_part) + 1);
1261       strcpy (locale_string, ll_part);
1262       strcat (locale_string, "_");
1263       strcat (locale_string, cc_part);
1264     }
1265
1266   /* Done with analysis of user-supplied string.  */
1267   free (lang_arg);
1268   
1269   /* Linear search is fine these days.  */
1270   for (c = aa; c != last_language_code; c++)
1271     {
1272       if (strcmp (ll_part, language_table[c].abbrev) == 0)
1273         { /* Set current language code.  */
1274           language_code = c;
1275           break;
1276         }
1277     }
1278
1279   /* If we didn't find this code, complain.  */
1280   if (c == last_language_code)
1281     warning (_("%s is not a valid ISO 639 language code"), ll_part);
1282
1283   /* Set the language our `getdocumenttext' function uses for
1284      translating document strings.  */
1285   document_language = xstrdup (locale_string);
1286   free (locale_string);
1287   
1288   if (xml && !docbook)
1289     { /* According to http://www.opentag.com/xfaq_lang.htm, xml:lang
1290          takes an ISO 639 language code, optionally followed by a dash
1291          (not underscore) and an ISO 3166 country code.  So we have
1292          to make another version with - instead of _.  */
1293       char *xml_locale = xmalloc (strlen (ll_part) + 1 + strlen (cc_part) + 1);
1294       strcpy (xml_locale, ll_part);
1295       strcat (xml_locale, "-");
1296       strcat (xml_locale, cc_part);
1297       xml_insert_element_with_attribute (DOCUMENTLANGUAGE, START,
1298                                          "xml:lang=\"%s\"", xml_locale);
1299       xml_insert_element (DOCUMENTLANGUAGE, END);
1300       free (xml_locale);
1301     }
1302
1303   free (ll_part);
1304   free (cc_part);
1305 }
1306
1307
1308 \f
1309 /* Search through the encoding table for the given character, returning
1310    its equivalent.  */
1311
1312 static int
1313 cm_search_iso_map (char *html)
1314 {
1315   if (document_encoding_code == UTF_8)
1316     {
1317       /* Binary search in unicode_map.  */
1318       size_t low = 0;
1319       size_t high = sizeof (unicode_map) / sizeof (unicode_map[0]);
1320
1321       /* At each loop iteration, low < high; for indices < low the values are
1322          smaller than HTML; for indices >= high the values are greater than HTML.
1323          So, if HTML occurs in the list, it is at  low <= position < high.  */
1324       do
1325         {
1326           size_t mid = low + (high - low) / 2; /* low <= mid < high */
1327           int cmp = strcmp (unicode_map[mid].html, html);
1328
1329           if (cmp < 0)
1330             low = mid + 1;
1331           else if (cmp > 0)
1332             high = mid;
1333           else /* cmp == 0 */
1334             return unicode_map[mid].unicode;
1335         }
1336       while (low < high);
1337
1338       return -1;
1339     }
1340   else
1341     {
1342       int i;
1343       iso_map_type *iso = encoding_table[document_encoding_code].isotab;
1344
1345       /* If no conversion table for this encoding, quit.  */
1346       if (!iso)
1347         return -1;
1348
1349       for (i = 0; iso[i].html; i++)
1350         {
1351           if (strcmp (html, iso[i].html) == 0)
1352             return i;
1353         }
1354
1355       return -1;
1356     }
1357 }
1358
1359
1360 /* @documentencoding.  Set the translation table.  */
1361
1362 void
1363 cm_documentencoding (void)
1364 {
1365   if (!handling_delayed_writes)
1366     {
1367       encoding_code_type enc;
1368       char *enc_arg;
1369
1370       /* This is ugly and probably needs to apply to other commands'
1371          argument parsing as well.  When we're doing @documentencoding,
1372          we're generally in the frontmatter of the document, and so the.
1373          expansion in html/xml/docbook would generally be the empty string.
1374          (Because those modes wait until the first normal text of the
1375          document to start outputting.)  The result would thus be a warning
1376          "unrecognized encoding name `'".  Sigh.  */
1377       int save_html = html;
1378       int save_xml = xml;
1379
1380       html = 0;
1381       xml = 0;
1382       get_rest_of_line (1, &enc_arg);
1383       html = save_html;
1384       xml = save_xml;
1385
1386       /* See if we have this encoding.  */
1387       for (enc = no_encoding+1; enc != last_encoding_code; enc++)
1388         {
1389           if (mbscasecmp (enc_arg, encoding_table[enc].encname) == 0)
1390             {
1391               document_encoding_code = enc;
1392               break;
1393             }
1394         }
1395
1396       /* If we didn't find this code, complain.  */
1397       if (enc == last_encoding_code)
1398         {
1399           warning (_("unrecognized encoding name `%s'"), enc_arg);
1400           /* Let the previous one go.  */
1401           if (unknown_encoding && *unknown_encoding)
1402             free (unknown_encoding);
1403           unknown_encoding = xstrdup (enc_arg);
1404         }
1405
1406       else if (encoding_table[document_encoding_code].isotab == NULL)
1407         warning (_("sorry, encoding `%s' not supported"), enc_arg);
1408
1409       free (enc_arg);
1410     }
1411   else if (xml)
1412     {
1413       char *encoding = current_document_encoding ();
1414
1415       if (encoding && *encoding)
1416         {
1417           insert_string (" encoding=\"");
1418           insert_string (encoding);
1419           insert_string ("\"");
1420         }
1421
1422       free (encoding);
1423     }
1424 }
1425
1426 char *
1427 current_document_encoding (void)
1428 {
1429   if (document_encoding_code != no_encoding)
1430     return xstrdup (encoding_table[document_encoding_code].encname);
1431   else if (unknown_encoding && *unknown_encoding)
1432     return xstrdup (unknown_encoding);
1433   else
1434     return xstrdup ("");
1435 }
1436
1437
1438 /* Add RC per the current encoding.  */
1439
1440 static void
1441 add_encoded_char_from_code (int rc)
1442 {
1443   if (document_encoding_code == UTF_8)
1444     {
1445       if (rc < 0x80)
1446         add_char (rc);
1447       else if (rc < 0x800)
1448         {
1449           add_char (0xc0 | (rc >> 6));
1450           add_char (0x80 | (rc & 0x3f));
1451         }
1452       else if (rc < 0x10000)
1453         {
1454           add_char (0xe0 | (rc >> 12));
1455           add_char (0x80 | ((rc >> 6) & 0x3f));
1456           add_char (0x80 | (rc & 0x3f));
1457         }
1458       else
1459         {
1460           add_char (0xf0 | (rc >> 18));
1461           add_char (0x80 | ((rc >> 12) & 0x3f));
1462           add_char (0x80 | ((rc >> 6) & 0x3f));
1463           add_char (0x80 | (rc & 0x3f));
1464         }
1465     }
1466   else
1467     add_char (encoding_table[document_encoding_code].isotab[rc].bytecode);
1468 }
1469
1470
1471 /* If html or xml output, add &HTML_STR; to the output.  If not html and
1472    the user requested encoded output, add the real 8-bit character
1473    corresponding to HTML_STR from the translation tables.  Otherwise,
1474    add INFO_STR.  */
1475
1476 static void
1477 add_encoded_char (char *html_str, char *info_str)
1478 {
1479   if (html)
1480     add_word_args ("&%s;", html_str);
1481   else if (xml)
1482     xml_insert_entity (html_str);
1483   else if (enable_encoding && document_encoding_code != no_encoding)
1484     {
1485       /* Look for HTML_STR in the current translation table.  */
1486       int rc = cm_search_iso_map (html_str);
1487       if (rc >= 0)
1488         /* We found it, add the real character.  */
1489         add_encoded_char_from_code (rc);
1490       else
1491         { /* We didn't find it, that seems bad.  */
1492           warning (_("invalid encoded character `%s'"), html_str);
1493           add_word (info_str);
1494         }
1495     }
1496   else
1497     add_word (info_str);
1498 }
1499
1500
1501 \f
1502 /* Output an accent for HTML or XML. */
1503
1504 static void
1505 cm_accent_generic_html (int arg, int start, int end, char *html_supported,
1506     int single, int html_solo_standalone, char *html_solo)
1507 {
1508   static int valid_html_accent; /* yikes */
1509
1510   if (arg == START)
1511     { /* If HTML has good support for this character, use it.  */
1512       if (strchr (html_supported, curchar ()))
1513         { /* Yes; start with an ampersand.  The character itself
1514              will be added later in read_command (makeinfo.c).  */
1515           int saved_escape_html = escape_html;
1516           escape_html = 0;
1517           valid_html_accent = 1;
1518           add_char ('&');
1519           escape_html = saved_escape_html;
1520         }
1521       else
1522         { /* @dotless{i} is not listed in html_supported but HTML entities
1523              starting with `i' can be used, such as &icirc;.  */
1524           int save_input_text_offset = input_text_offset;
1525           char *accent_contents;
1526
1527           get_until_in_braces ("\n", &accent_contents);
1528           canon_white (accent_contents);
1529
1530           if (strstr (accent_contents, "@dotless{i"))
1531             {
1532               add_word_args ("&%c", accent_contents[9]);
1533               valid_html_accent = 1;
1534             }
1535           else
1536             {
1537               /* Search for @dotless{} wasn't successful, so rewind.  */
1538               input_text_offset = save_input_text_offset;
1539               valid_html_accent = 0;
1540               if (html_solo_standalone)
1541                 { /* No special HTML support, so produce standalone char.  */
1542                   if (xml)
1543                     xml_insert_entity (html_solo);
1544                   else
1545                     add_word_args ("&%s;", html_solo);
1546                 }
1547               else
1548                 /* If the html_solo does not exist as standalone character
1549                    (namely &circ; &grave; &tilde;), then we use
1550                    the single character version instead.  */
1551                 add_char (single);
1552             }
1553
1554           free (accent_contents);
1555         }
1556     }
1557   else if (arg == END)
1558     { /* Only if we saw a valid_html_accent can we use the full
1559          HTML accent (umlaut, grave ...).  */
1560       if (valid_html_accent)
1561         {
1562           add_word (html_solo);
1563           add_char (';');
1564         }
1565     }
1566 }
1567
1568
1569 /* If END is zero, there is nothing in the paragraph to accent.  This
1570    can happen when we're in a menu with an accent command and
1571    --no-headers is given, so the base character is not added.  In this
1572    case we're not producing any output anyway, so just forget it.
1573    Otherwise, produce the ASCII version of the accented char.  */
1574 static void
1575 cm_accent_generic_no_headers (int arg, int start, int end, int single,
1576     char *html_solo)
1577 {
1578   if (arg == END && end > 0)
1579     {
1580       if (no_encoding)
1581         add_char (single);
1582       else
1583         {
1584           int rc;
1585           char *buffer = xmalloc (1 + strlen (html_solo) + 1);
1586           assert (end > 0);
1587           buffer[0] = output_paragraph[end - 1];
1588           buffer[1] = 0;
1589           strcat (buffer, html_solo);
1590
1591           rc = cm_search_iso_map (buffer);
1592           if (rc >= 0)
1593             {
1594               /* Here we replace the character which has
1595                  been inserted in read_command with
1596                  the value we have found in the conversion table.  */
1597               output_paragraph_offset--;
1598               add_encoded_char_from_code (rc);
1599             }
1600           else
1601             { /* If we didn't find a translation for this character,
1602                  put the single instead. E.g., &Xuml; does not exist so X&uml;
1603                  should be produced. */
1604               /* When the below warning is issued, an author has nothing
1605                  wrong in their document, let alone anything ``fixable''
1606                  on their side.  So it is commented out for now.  */
1607               /* warning (_("%s is an invalid ISO code, using %c"),
1608                        buffer, single); */
1609               add_char (single);
1610             }
1611
1612           free (buffer);
1613         }
1614     }
1615 }
1616
1617
1618 \f
1619 /* Accent commands that take explicit arguments and don't have any
1620    special HTML support.  */
1621
1622 void
1623 cm_accent (int arg)
1624 {
1625   int old_escape_html = escape_html;
1626   escape_html = 0;
1627   if (arg == START)
1628     {
1629       /* Must come first to avoid ambiguity with overdot.  */
1630       if (strcmp (command, "udotaccent") == 0)      /* underdot */
1631         add_char ('.');
1632     }
1633   else if (arg == END)
1634     {
1635       if (strcmp (command, "=") == 0)               /* macron */
1636         add_word ((html || xml) ? "&macr;" : "=");
1637       else if (strcmp (command, "H") == 0)          /* Hungarian umlaut */
1638         add_word ("''");
1639       else if (strcmp (command, "dotaccent") == 0)  /* overdot */
1640         add_meta_char ('.');
1641       else if (strcmp (command, "ringaccent") == 0) /* ring */
1642         add_char ('*');
1643       else if (strcmp (command, "tieaccent") == 0)  /* long tie */
1644         add_char ('[');
1645       else if (strcmp (command, "u") == 0)          /* breve */
1646         add_char ('(');
1647       else if (strcmp (command, "ubaraccent") == 0) /* underbar */
1648         add_char ('_');
1649       else if (strcmp (command, "v") == 0)          /* hacek/check */
1650         add_word ((html || xml) ? "&lt;" : "<");
1651     }
1652   escape_html = old_escape_html;
1653 }
1654
1655 /* Common routine for the accent characters that have support in HTML.
1656    If the character being accented is in the HTML_SUPPORTED set, then
1657    produce &CHTML_SOLO;, for example, &Auml; for an A-umlaut.  If not in
1658    HTML_SUPPORTED, just produce &HTML_SOLO;X for the best we can do with
1659    at an X-umlaut.  If not producing HTML, just use SINGLE, a
1660    character such as " which is the best plain text representation we
1661    can manage.  If HTML_SOLO_STANDALONE is nonzero the given HTML_SOLO
1662    exists as valid standalone character in HTML, e.g., &uml;.  */
1663
1664 static void
1665 cm_accent_generic (int arg, int start, int end, char *html_supported,
1666     int single, int html_solo_standalone, char *html_solo)
1667 {
1668   /* Accentuating space characters makes no sense, so issue a warning.  */
1669   if (arg == START && isspace (input_text[input_text_offset]))
1670     warning ("Accent command `@%s' must not be followed by whitespace",
1671         command);
1672
1673   if (html || xml)
1674     cm_accent_generic_html (arg, start, end, html_supported,
1675                             single, html_solo_standalone, html_solo);
1676   else if (no_headers)
1677     cm_accent_generic_no_headers (arg, start, end, single, html_solo);
1678   else if (arg == END)
1679     {
1680       if (enable_encoding)
1681         /* use 8-bit if available */
1682         cm_accent_generic_no_headers (arg, start, end, single, html_solo);
1683       else
1684         /* use regular character */
1685         add_char (single);
1686     }
1687 }
1688
1689 void
1690 cm_accent_umlaut (int arg, int start, int end)
1691 {
1692   cm_accent_generic (arg, start, end, "aouAOUEeIiy", '"', 1, "uml");
1693 }
1694
1695 void
1696 cm_accent_acute (int arg, int start, int end)
1697 {
1698   cm_accent_generic (arg, start, end, "AEIOUYaeiouy", '\'', 1, "acute");
1699 }
1700
1701 void
1702 cm_accent_cedilla (int arg, int start, int end)
1703 {
1704   cm_accent_generic (arg, start, end, "Cc", ',', 1, "cedil");
1705 }
1706
1707 void
1708 cm_accent_hat (int arg, int start, int end)
1709 {
1710   cm_accent_generic (arg, start, end, "AEIOUaeiou", '^', 0, "circ");
1711 }
1712
1713 void
1714 cm_accent_grave (int arg, int start, int end)
1715 {
1716   cm_accent_generic (arg, start, end, "AEIOUaeiou", '`', 0, "grave");
1717 }
1718
1719 void
1720 cm_accent_tilde (int arg, int start, int end)
1721 {
1722   cm_accent_generic (arg, start, end, "ANOano", '~', 0, "tilde");
1723 }
1724
1725
1726 \f
1727 /* Non-English letters/characters that don't insert themselves.  */
1728 void
1729 cm_special_char (int arg)
1730 {
1731   int old_escape_html = escape_html;
1732   escape_html = 0;
1733
1734   if (arg == START)
1735     {
1736       if ((*command == 'L' || *command == 'l'
1737            || *command == 'O' || *command == 'o')
1738           && command[1] == 0)
1739         { /* Lslash lslash Oslash oslash.
1740              Lslash and lslash aren't supported in HTML.  */
1741           if (command[0] == 'O')
1742             add_encoded_char ("Oslash", "/O");
1743           else if (command[0] == 'o')
1744             add_encoded_char ("oslash", "/o");
1745           else
1746             add_word_args ("/%c", command[0]);
1747         }
1748       else if (strcmp (command, "exclamdown") == 0)
1749         add_encoded_char ("iexcl", "!");
1750       else if (strcmp (command, "questiondown") == 0)
1751         add_encoded_char ("iquest", "?");
1752       else if (strcmp (command, "euro") == 0)
1753         /* http://www.cs.tut.fi/~jkorpela/html/euro.html suggests that
1754            &euro; degrades best in old browsers.  */
1755         add_encoded_char ("euro", "Euro ");
1756       else if (strcmp (command, "pounds") == 0)
1757         add_encoded_char ("pound" , "#");
1758       else if (strcmp (command, "ordf") == 0)
1759         add_encoded_char ("ordf" , "a");
1760       else if (strcmp (command, "ordm") == 0)
1761         add_encoded_char ("ordm" , "o");
1762       else if (strcmp (command, "textdegree") == 0)
1763         add_encoded_char ("deg" , "o");
1764       else if (strcmp (command, "AE") == 0)
1765         add_encoded_char ("AElig", command);
1766       else if (strcmp (command, "ae") == 0)
1767         add_encoded_char ("aelig",  command);
1768       else if (strcmp (command, "OE") == 0)
1769         add_encoded_char ("OElig", command);
1770       else if (strcmp (command, "oe") == 0)
1771         add_encoded_char ("oelig", command);
1772       else if (strcmp (command, "AA") == 0)
1773         add_encoded_char ("Aring", command);
1774       else if (strcmp (command, "aa") == 0)
1775         add_encoded_char ("aring", command);
1776       else if (strcmp (command, "ss") == 0)
1777         add_encoded_char ("szlig", command);
1778       else if (strcmp (command, "guillemetleft") == 0
1779                || strcmp (command, "guillemotleft") == 0)
1780         add_encoded_char ("laquo", "<<");
1781       else if (strcmp (command, "guillemetright") == 0
1782                || strcmp (command, "guillemotright") == 0)
1783         add_encoded_char ("raquo", ">>");
1784       else
1785         line_error ("cm_special_char internal error: command=@%s", command);
1786     }
1787   escape_html = old_escape_html;
1788 }
1789
1790 /* Dotless i or j.  */
1791 void
1792 cm_dotless (int arg, int start, int end)
1793 {
1794   if (arg == END)
1795     {
1796       xml_no_para --;
1797       if (output_paragraph[start] != 'i' && output_paragraph[start] != 'j')
1798         /* This error message isn't perfect if the argument is multiple
1799            characters, but it doesn't seem worth getting right.  */
1800         line_error (_("%c%s expects `i' or `j' as argument, not `%c'"),
1801                     COMMAND_PREFIX, command, output_paragraph[start]);
1802
1803       else if (end - start != 1)
1804         line_error (_("%c%s expects a single character `i' or `j' as argument"),
1805                     COMMAND_PREFIX, command);
1806
1807       /* We've already inserted the `i' or `j', so nothing to do.  */
1808     }
1809   else
1810     xml_no_para ++;
1811 }