Imported Upstream version 1.22.4
[platform/upstream/groff.git] / src / preproc / preconv / preconv.cpp
1 // -*- C++ -*-
2 /* Copyright (C) 2005-2018 Free Software Foundation, Inc.
3      Written by Werner Lemberg (wl@gnu.org)
4
5 This file is part of groff.
6
7 groff is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 groff is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 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 #include "lib.h"
21
22 #include <assert.h>
23 #include <stdlib.h>
24 #include <errno.h>
25 #include <sys/stat.h>
26 #ifdef HAVE_UCHARDET
27 #include <uchardet/uchardet.h>
28 #endif
29 #include "errarg.h"
30 #include "error.h"
31 #include "localcharset.h"
32 #include "nonposix.h"
33 #include "stringclass.h"
34 #include "lf.h"
35
36 #include <locale.h>
37
38 #if HAVE_ICONV
39 # include <iconv.h>
40 # ifdef WORDS_BIGENDIAN
41 #  define UNICODE "UTF-32BE"
42 # else
43 #  define UNICODE "UTF-32LE"
44 # endif
45 #endif
46
47 #define MAX_VAR_LEN 100
48
49 extern "C" const char *Version_string;
50
51 char default_encoding[MAX_VAR_LEN];
52 char user_encoding[MAX_VAR_LEN];
53 char encoding_string[MAX_VAR_LEN];
54 int debug_flag = 0;
55 int raw_flag = 0;
56
57 struct conversion {
58   const char *from;
59   const char *to;
60 };
61
62 // The official list of MIME tags can be found at
63 //
64 //   http://www.iana.org/assignments/character-sets
65 //
66 // For encodings which don't have a MIME tag we use GNU iconv's encoding
67 // names (which also work with the portable GNU libiconv package).  They
68 // are marked with '*'.
69 //
70 // Encodings specific to XEmacs and Emacs are marked as such; no mark means
71 // that they are used by both Emacs and XEmacs.
72 //
73 // Encodings marked with '--' are special to Emacs, XEmacs, or other
74 // applications and shouldn't be used for data exchange.
75 //
76 // 'Not covered' means that the encoding can be handled neither by GNU iconv
77 // nor by libiconv, or just one of them has support for it.
78 //
79 // A special case is VIQR encoding: Despite of having a MIME tag it is
80 // missing in both libiconv 1.10 and iconv (coming with GNU libc 2.3.6).
81 //
82 // Finally, we add all aliases of GNU iconv for 'ascii', 'latin1', and
83 // 'utf8' to catch those encoding names before iconv is called.
84 //
85 // Note that most entries are commented out -- only a small, (rather)
86 // reliable and stable subset of encodings is recognized (for coding tags)
87 // which are still in greater use today (January 2006).  Most notably, all
88 // Windows-specific encodings are not selected because they lack stability:
89 // Microsoft has changed the mappings instead of creating new versions.
90 //
91 // Please contact the groff list if you find the selection inadequate.
92
93 static const conversion
94 emacs_to_mime[] = {
95   {"ascii",                             "US-ASCII"},    // Emacs
96   {"big5",                              "Big5"},
97   {"chinese-big5",                      "Big5"},        // Emacs
98   {"chinese-euc",                       "GB2312"},      // XEmacs
99   {"chinese-iso-8bit",                  "GB2312"},      // Emacs
100   {"cn-big5",                           "Big5"},
101   {"cn-gb",                             "GB2312"},      // Emacs
102   {"cn-gb-2312",                        "GB2312"},
103   {"cp878",                             "KOI8-R"},      // Emacs
104   {"cp1047",                            "CP1047"},      // EBCDIC
105   {"csascii",                           "US-ASCII"},    // alias
106   {"csisolatin1",                       "ISO-8859-1"},  // alias
107   {"cyrillic-iso-8bit",                 "ISO-8859-5"},  // Emacs
108   {"cyrillic-koi8",                     "KOI8-R"},      // not KOI8!, Emacs
109   {"euc-china",                         "GB2312"},      // Emacs
110   {"euc-cn",                            "GB2312"},      // Emacs
111   {"euc-japan",                         "EUC-JP"},
112   {"euc-japan-1990",                    "EUC-JP"},      // Emacs
113   {"euc-jp",                            "EUC-JP"},
114   {"euc-korea",                         "EUC-KR"},
115   {"euc-kr",                            "EUC-KR"},
116   {"gb2312",                            "GB2312"},
117   {"greek-iso-8bit",                    "ISO-8859-7"},
118   {"iso-10646/utf8",                    "UTF-8"},       // alias
119   {"iso-10646/utf-8",                   "UTF-8"},       // alias
120   {"iso-8859-1",                        "ISO-8859-1"},
121   {"iso-8859-13",                       "ISO-8859-13"}, // Emacs
122   {"iso-8859-15",                       "ISO-8859-15"},
123   {"iso-8859-2",                        "ISO-8859-2"},
124   {"iso-8859-5",                        "ISO-8859-5"},
125   {"iso-8859-7",                        "ISO-8859-7"},
126   {"iso-8859-9",                        "ISO-8859-9"},
127   {"iso-latin-1",                       "ISO-8859-1"},
128   {"iso-latin-2",                       "ISO-8859-2"},  // Emacs
129   {"iso-latin-5",                       "ISO-8859-9"},  // Emacs
130   {"iso-latin-7",                       "ISO-8859-13"}, // Emacs
131   {"iso-latin-9",                       "ISO-8859-15"}, // Emacs
132   {"japanese-iso-8bit",                 "EUC-JP"},      // Emacs
133   {"japanese-euc",                      "EUC-JP"},      // XEmacs
134   {"jis8",                              "EUC-JP"},      // XEmacs
135   {"koi8",                              "KOI8-R"},      // not KOI8!, Emacs
136   {"koi8-r",                            "KOI8-R"},
137   {"korean-euc",                        "EUC-KR"},      // XEmacs
138   {"korean-iso-8bit",                   "EUC-KR"},      // Emacs
139   {"latin1",                            "ISO-8859-1"},  // alias
140   {"latin-0",                           "ISO-8859-15"}, // Emacs
141   {"latin-1",                           "ISO-8859-1"},  // Emacs
142   {"latin-2",                           "ISO-8859-2"},  // Emacs
143   {"latin-5",                           "ISO-8859-9"},  // Emacs
144   {"latin-7",                           "ISO-8859-13"}, // Emacs
145   {"latin-9",                           "ISO-8859-15"}, // Emacs
146   {"mule-utf-16",                       "UTF-16"},      // Emacs
147   {"mule-utf-16be",                     "UTF-16BE"},    // Emacs
148   {"mule-utf-16-be",                    "UTF-16BE"},    // Emacs
149   {"mule-utf-16be-with-signature",      "UTF-16"},      // Emacs, not UTF-16BE
150   {"mule-utf-16le",                     "UTF-16LE"},    // Emacs
151   {"mule-utf-16-le",                    "UTF-16LE"},    // Emacs
152   {"mule-utf-16le-with-signature",      "UTF-16"},      // Emacs, not UTF-16LE
153   {"mule-utf-8",                        "UTF-8"},       // Emacs
154   {"us-ascii",                          "US-ASCII"},    // Emacs
155   {"utf8",                              "UTF-8"},       // alias
156   {"utf-16",                            "UTF-16"},      // Emacs
157   {"utf-16be",                          "UTF-16BE"},    // Emacs
158   {"utf-16-be",                         "UTF-16BE"},    // Emacs
159   {"utf-16be-with-signature",           "UTF-16"},      // Emacs, not UTF-16BE
160   {"utf-16-be-with-signature",          "UTF-16"},      // Emacs, not UTF-16BE
161   {"utf-16le",                          "UTF-16LE"},    // Emacs
162   {"utf-16-le",                         "UTF-16LE"},    // Emacs
163   {"utf-16le-with-signature",           "UTF-16"},      // Emacs, not UTF-16LE
164   {"utf-16-le-with-signature",          "UTF-16"},      // Emacs, not UTF-16LE
165   {"utf-8",                             "UTF-8"},       // Emacs
166
167 //  {"alternativnyj",                   ""},            // ?
168 //  {"arabic-iso-8bit",                 "ISO-8859-6"},  // Emacs
169 //  {"binary",                          ""},            // --
170 //  {"chinese-hz",                      "HZ-GB-2312"},  // Emacs
171 //  {"chinese-iso-7bit",                "ISO-2022-CN"}, // Emacs
172 //  {"chinese-iso-8bit-with-esc",       ""},            // --
173 //  {"compound-text",                   ""},            // --
174 //  {"compound-text-with-extension",    ""},            // --
175 //  {"cp1125",                          "cp1125"},      // *
176 //  {"cp1250",                          "windows-1250"},// Emacs
177 //  {"cp1251",                          "windows-1251"},// Emacs
178 //  {"cp1252",                          "windows-1252"},// Emacs
179 //  {"cp1253",                          "windows-1253"},// Emacs
180 //  {"cp1254",                          "windows-1254"},// Emacs
181 //  {"cp1255",                          "windows-1255"},// Emacs
182 //  {"cp1256",                          "windows-1256"},// Emacs
183 //  {"cp1257",                          "windows-1257"},// Emacs
184 //  {"cp1258",                          "windows-1258"},// Emacs
185 //  {"cp437",                           "cp437"},       // Emacs
186 //  {"cp720",                           ""},            // not covered
187 //  {"cp737",                           "cp737"},       // *, Emacs
188 //  {"cp775",                           "cp775"},       // Emacs
189 //  {"cp850",                           "cp850"},       // Emacs
190 //  {"cp851",                           "cp851"},       // Emacs
191 //  {"cp852",                           "cp852"},       // Emacs
192 //  {"cp855",                           "cp855"},       // Emacs
193 //  {"cp857",                           "cp857"},       // Emacs
194 //  {"cp860",                           "cp860"},       // Emacs
195 //  {"cp861",                           "cp861"},       // Emacs
196 //  {"cp862",                           "cp862"},       // Emacs
197 //  {"cp863",                           "cp863"},       // Emacs
198 //  {"cp864",                           "cp864"},       // Emacs
199 //  {"cp865",                           "cp865"},       // Emacs
200 //  {"cp866",                           "cp866"},       // Emacs
201 //  {"cp866u",                          "cp1125"},      // *, Emacs
202 //  {"cp869",                           "cp869"},       // Emacs
203 //  {"cp874",                           "cp874"},       // *, Emacs
204 //  {"cp932",                           "cp932"},       // *, Emacs
205 //  {"cp936",                           "cp936"},       // Emacs
206 //  {"cp949",                           "cp949"},       // *, Emacs
207 //  {"cp950",                           "cp950"},       // *, Emacs
208 //  {"ctext",                           ""},            // --
209 //  {"ctext-no-compositions",           ""},            // --
210 //  {"ctext-with-extensions",           ""},            // --
211 //  {"cyrillic-alternativnyj",          ""},            // ?, Emacs
212 //  {"cyrillic-iso-8bit-with-esc",      ""},            // --
213 //  {"cyrillic-koi8-t",                 "KOI8-T"},      // *, Emacs
214 //  {"devanagari",                      ""},            // not covered
215 //  {"dos",                             ""},            // --
216 //  {"emacs-mule",                      ""},            // --
217 //  {"euc-jisx0213",                    "EUC-JISX0213"},// *, XEmacs?
218 //  {"euc-jisx0213-with-esc",           ""},            // XEmacs?
219 //  {"euc-taiwan",                      "EUC-TW"},      // *, Emacs
220 //  {"euc-tw",                          "EUC-TW"},      // *, Emacs
221 //  {"georgian-ps",                     "GEORGIAN-PS"}, // *, Emacs
222 //  {"greek-iso-8bit-with-esc",         ""},            // --
223 //  {"hebrew-iso-8bit",                 "ISO-8859-8"},  // Emacs
224 //  {"hebrew-iso-8bit-with-esc",        ""},            // --
225 //  {"hz",                              "HZ-GB-2312"},
226 //  {"hz-gb-2312",                      "HZ-GB-2312"},
227 //  {"in-is13194",                      ""},            // not covered
228 //  {"in-is13194-devanagari",           ""},            // not covered
229 //  {"in-is13194-with-esc",             ""},            // --
230 //  {"iso-2022-7",                      ""},            // XEmacs?
231 //  {"iso-2022-7bit",                   ""},            // --
232 //  {"iso-2022-7bit-lock",              ""},            // --
233 //  {"iso-2022-7bit-lock-ss2",          ""},            // --
234 //  {"iso-2022-7bit-ss2",               ""},            // --
235 //  {"iso-2022-8",                      ""},            // XEmacs?
236 //  {"iso-2022-8bit",                   ""},            // XEmacs?
237 //  {"iso-2022-8bit-lock",              ""},            // XEmacs?
238 //  {"iso-2022-8bit-lock-ss2",          ""},            // XEmacs?
239 //  {"iso-2022-8bit-ss2",               ""},            // --
240 //  {"iso-2022-cjk",                    ""},            // --
241 //  {"iso-2022-cn",                     "ISO-2022-CN"}, // Emacs
242 //  {"iso-2022-cn-ext",                 "ISO-2022-CN-EXT"},// Emacs
243 //  {"iso-2022-int-1",                  ""},            // --
244 //  {"iso-2022-jp",                     "ISO-2022-JP"},
245 //  {"iso-2022-jp-1978-irv",            "ISO-2022-JP"},
246 //  {"iso-2022-jp-2",                   "ISO-2022-JP-2"},
247 //  {"iso-2022-jp-3",                   "ISO-2022-JP-3"},// *, XEmacs?
248 //  {"iso-2022-jp-3-compatible",        ""},            // XEmacs?
249 //  {"iso-2022-jp-3-strict",            "ISO-2022-JP-3"},// *, XEmacs?
250 //  {"iso-2022-kr",                     "ISO-2022-KR"},
251 //  {"iso-2022-lock",                   ""},            // XEmacs?
252 //  {"iso-8859-10",                     "ISO-8859-10"}, // Emacs
253 //  {"iso-8859-11",                     "ISO-8859-11"}, // *, Emacs
254 //  {"iso-8859-14",                     "ISO-8859-14"}, // Emacs
255 //  {"iso-8859-16",                     "ISO-8859-16"},
256 //  {"iso-8859-3",                      "ISO-8859-3"},
257 //  {"iso-8859-4",                      "ISO-8859-4"},
258 //  {"iso-8859-6",                      "ISO-8859-6"},
259 //  {"iso-8859-8",                      "ISO-8859-8"},
260 //  {"iso-8859-8-e",                    "ISO-8859-8"},
261 //  {"iso-8859-8-i",                    "ISO-8859-8"},  // Emacs
262 //  {"iso-latin-10",                    "ISO-8859-16"}, // Emacs
263 //  {"iso-latin-1-with-esc",            ""},            // --
264 //  {"iso-latin-2-with-esc",            ""},            // --
265 //  {"iso-latin-3",                     "ISO-8859-3"},  // Emacs
266 //  {"iso-latin-3-with-esc",            ""},            // --
267 //  {"iso-latin-4",                     "ISO-8859-4"},  // Emacs
268 //  {"iso-latin-4-with-esc",            ""},            // --
269 //  {"iso-latin-5-with-esc",            ""},            // --
270 //  {"iso-latin-6",                     "ISO-8859-10"}, // Emacs
271 //  {"iso-latin-8",                     "ISO-8859-14"}, // Emacs
272 //  {"iso-safe",                                ""},            // --
273 //  {"japanese-iso-7bit-1978-irv",      "ISO-2022-JP"}, // Emacs
274 //  {"japanese-iso-8bit-with-esc",      ""},            // --
275 //  {"japanese-shift-jis",              "Shift_JIS"},   // Emacs
276 //  {"japanese-shift-jisx0213",         ""},            // XEmacs?
277 //  {"jis7",                            "ISO-2022-JP"}, // Xemacs
278 //  {"junet",                           "ISO-2022-JP"},
279 //  {"koi8-t",                          "KOI8-T"},      // *, Emacs
280 //  {"koi8-u",                          "KOI8-U"},      // Emacs
281 //  {"korean-iso-7bit-lock",            "ISO-2022-KR"},
282 //  {"korean-iso-8bit-with-esc",        ""},            // --
283 //  {"lao",                             ""},            // not covered
284 //  {"lao-with-esc",                    ""},            // --
285 //  {"latin-10",                        "ISO-8859-16"}, // Emacs
286 //  {"latin-3",                         "ISO-8859-3"},  // Emacs
287 //  {"latin-4",                         "ISO-8859-4"},  // Emacs
288 //  {"latin-6",                         "ISO-8859-10"}, // Emacs
289 //  {"latin-8",                         "ISO-8859-14"}, // Emacs
290 //  {"mac",                             ""},            // --
291 //  {"mac-roman",                       "MACINTOSH"},   // Emacs
292 //  {"mik",                             ""},            // not covered
293 //  {"next",                            "NEXTSTEP"},    // *, Emacs
294 //  {"no-conversion",                   ""},            // --
295 //  {"old-jis",                         "ISO-2022-JP"},
296 //  {"pt154",                           "PT154"},       // Emacs
297 //  {"raw-text",                        ""},            // --
298 //  {"ruscii",                          "cp1125"},      // *, Emacs
299 //  {"shift-jis",                       "Shift_JIS"},   // XEmacs
300 //  {"shift_jis",                       "Shift_JIS"},
301 //  {"shift_jisx0213",                  "Shift_JISX0213"},// *, XEmacs?
302 //  {"sjis",                            "Shift_JIS"},   // Emacs
303 //  {"tcvn",                            "TCVN"},        // *, Emacs
304 //  {"tcvn-5712",                       "TCVN"},        // *, Emacs
305 //  {"thai-tis620",                     "TIS-620"},
306 //  {"thai-tis620-with-esc",            ""},            // --
307 //  {"th-tis620",                       "TIS-620"},
308 //  {"tibetan",                         ""},            // not covered
309 //  {"tibetan-iso-8bit",                ""},            // not covered
310 //  {"tibetan-iso-8bit-with-esc",       ""},            // --
311 //  {"tis-620",                         "TIS-620"},
312 //  {"tis620",                          "TIS-620"},
313 //  {"undecided",                       ""},            // --
314 //  {"unix",                            ""},            // --
315 //  {"utf-7",                           "UTF-7"},       // Emacs
316 //  {"utf-7-safe",                      ""},            // XEmacs?
317 //  {"utf-8-ws",                        "UTF-8"},       // XEmacs?
318 //  {"vietnamese-tcvn",                 "TCVN"},        // *, Emacs
319 //  {"vietnamese-viqr",                 "VIQR"},        // not covered
320 //  {"vietnamese-viscii",               "VISCII"},
321 //  {"vietnamese-vscii",                ""},            // not covered
322 //  {"viqr",                            "VIQR"},        // not covered
323 //  {"viscii",                          "VISCII"},
324 //  {"vscii",                           ""},            // not covered
325 //  {"windows-037",                     ""},            // not covered
326 //  {"windows-10000",                   ""},            // not covered
327 //  {"windows-10001",                   ""},            // not covered
328 //  {"windows-10006",                   ""},            // not covered
329 //  {"windows-10007",                   ""},            // not covered
330 //  {"windows-10029",                   ""},            // not covered
331 //  {"windows-10079",                   ""},            // not covered
332 //  {"windows-10081",                   ""},            // not covered
333 //  {"windows-1026",                    ""},            // not covered
334 //  {"windows-1200",                    ""},            // not covered
335 //  {"windows-1250",                    "windows-1250"},
336 //  {"windows-1251",                    "windows-1251"},
337 //  {"windows-1252",                    "windows-1252"},
338 //  {"windows-1253",                    "windows-1253"},
339 //  {"windows-1254",                    "windows-1254"},
340 //  {"windows-1255",                    "windows-1255"},
341 //  {"windows-1256",                    "windows-1256"},
342 //  {"windows-1257",                    "windows-1257"},
343 //  {"windows-1258",                    "windows-1258"},
344 //  {"windows-1361",                    "cp1361"},      // *, XEmacs
345 //  {"windows-437",                     "cp437"},       // XEmacs
346 //  {"windows-500",                     ""},            // not covered
347 //  {"windows-708",                     ""},            // not covered
348 //  {"windows-709",                     ""},            // not covered
349 //  {"windows-710",                     ""},            // not covered
350 //  {"windows-720",                     ""},            // not covered
351 //  {"windows-737",                     "cp737"},       // *, XEmacs
352 //  {"windows-775",                     "cp775"},       // XEmacs
353 //  {"windows-850",                     "cp850"},       // XEmacs
354 //  {"windows-852",                     "cp852"},       // XEmacs
355 //  {"windows-855",                     "cp855"},       // XEmacs
356 //  {"windows-857",                     "cp857"},       // XEmacs
357 //  {"windows-860",                     "cp860"},       // XEmacs
358 //  {"windows-861",                     "cp861"},       // XEmacs
359 //  {"windows-862",                     "cp862"},       // XEmacs
360 //  {"windows-863",                     "cp863"},       // XEmacs
361 //  {"windows-864",                     "cp864"},       // XEmacs
362 //  {"windows-865",                     "cp865"},       // XEmacs
363 //  {"windows-866",                     "cp866"},       // XEmacs
364 //  {"windows-869",                     "cp869"},       // XEmacs
365 //  {"windows-874",                     "cp874"},       // XEmacs
366 //  {"windows-875",                     ""},            // not covered
367 //  {"windows-932",                     "cp932"},       // *, XEmacs
368 //  {"windows-936",                     "cp936"},       // XEmacs
369 //  {"windows-949",                     "cp949"},       // *, XEmacs
370 //  {"windows-950",                     "cp950"},       // *, XEmacs
371 //  {"x-ctext",                         ""},            // --
372 //  {"x-ctext-with-extensions",         ""},            // --
373
374   {NULL,                                NULL},
375 };
376
377 // ---------------------------------------------------------
378 // Convert encoding name from emacs to mime.
379 // ---------------------------------------------------------
380 char *
381 emacs2mime(char *emacs_enc)
382 {
383   int emacs_enc_len = strlen(emacs_enc);
384   if (emacs_enc_len > 4
385       && !strcasecmp(emacs_enc + emacs_enc_len - 4, "-dos"))
386     emacs_enc[emacs_enc_len - 4] = 0;
387   if (emacs_enc_len > 4
388       && !strcasecmp(emacs_enc + emacs_enc_len - 4, "-mac"))
389     emacs_enc[emacs_enc_len - 4] = 0;
390   if (emacs_enc_len > 5
391       && !strcasecmp(emacs_enc + emacs_enc_len - 5, "-unix"))
392     emacs_enc[emacs_enc_len - 5] = 0;
393   for (const conversion *table = emacs_to_mime; table->from; table++)
394     if (!strcasecmp(emacs_enc, table->from))
395       return (char *)table->to;
396   return emacs_enc;
397 }
398
399 // ---------------------------------------------------------
400 // Print out Unicode entity if value is greater than 0x7F.
401 // ---------------------------------------------------------
402 inline void
403 unicode_entity(int u)
404 {
405   if (u < 0x80)
406     putchar(u);
407   else {
408     // Handle soft hyphen specially -- it is an input character only,
409     // not a glyph.
410     if (u == 0xAD) {
411       putchar('\\');
412       putchar('%');
413     }
414     else
415       printf("\\[u%04X]", u);
416   }
417 }
418
419 // ---------------------------------------------------------
420 // Conversion functions.  All functions take 'data', which
421 // normally holds the first two lines, and a file pointer.
422 // ---------------------------------------------------------
423
424 // Conversion from ISO-8859-1 (aka Latin-1) to Unicode.
425 void
426 conversion_latin1(FILE *fp, const string &data)
427 {
428   int len = data.length();
429   const unsigned char *ptr = (const unsigned char *)data.contents();
430   for (int i = 0; i < len; i++)
431     unicode_entity(ptr[i]);
432   int c = -1;
433   while ((c = getc(fp)) != EOF)
434     unicode_entity(c);
435 }
436
437 // A future version of groff shall support UTF-8 natively.
438 // In this case, the UTF-8 stuff here in this file will be
439 // moved to the troff program.
440
441 struct utf8 {
442   FILE *fp;
443   unsigned char s[6];
444   enum {
445     FIRST = 0,
446     SECOND,
447     THIRD,
448     FOURTH,
449     FIFTH,
450     SIXTH
451   } byte;
452   int expected_bytes;
453   int invalid_warning;
454   int incomplete_warning;
455   utf8(FILE *);
456   ~utf8();
457   void add(unsigned char);
458   void invalid();
459   void incomplete();
460 };
461
462 utf8::utf8(FILE *f) : fp(f), byte(FIRST), expected_bytes(1),
463                       invalid_warning(1), incomplete_warning(1)
464 {
465   // empty
466 }
467
468 utf8::~utf8()
469 {
470   if (byte != FIRST)
471     incomplete();
472 }
473
474 inline void
475 utf8::add(unsigned char c)
476 {
477   s[byte] = c;
478   if (byte == FIRST) {
479     if (c < 0x80)
480       unicode_entity(c);
481     else if (c < 0xC0)
482       invalid();
483     else if (c < 0xE0) {
484       expected_bytes = 2;
485       byte = SECOND;
486     }
487     else if (c < 0xF0) {
488       expected_bytes = 3;
489       byte = SECOND;
490     }
491     else if (c < 0xF8) {
492       expected_bytes = 4;
493       byte = SECOND;
494     }
495     else if (c < 0xFC) {
496       expected_bytes = 5;
497       byte = SECOND;
498     }
499     else if (c < 0xFE) {
500       expected_bytes = 6;
501       byte = SECOND;
502     }
503     else
504       invalid();
505     return;
506   }
507   if (c < 0x80 || c > 0xBF) {
508     incomplete();
509     add(c);
510     return;
511   }
512   switch (byte) {
513   case FIRST:
514     // can't happen
515     break;
516   case SECOND:
517     if (expected_bytes == 2) {
518       if (s[0] < 0xC2)
519         invalid();
520       else
521         unicode_entity(((s[0] & 0x1F) << 6)
522                        | (s[1] ^ 0x80));
523       byte = FIRST;
524     }
525     else
526       byte = THIRD;
527     break;
528   case THIRD:
529     if (expected_bytes == 3) {
530       if (!(s[0] >= 0xE1 || s[1] >= 0xA0))
531         invalid();
532       else
533         unicode_entity(((s[0] & 0x1F) << 12)
534                        | ((s[1] ^ 0x80) << 6)
535                        | (s[2] ^ 0x80));
536       byte = FIRST;
537     }
538     else
539       byte = FOURTH;
540     break;
541   case FOURTH:
542     // We reject everything greater than 0x10FFFF.
543     if (expected_bytes == 4) {
544       if (!((s[0] >= 0xF1 || s[1] >= 0x90)
545             && (s[0] < 0xF4 || (s[0] == 0xF4 && s[1] < 0x90))))
546         invalid();
547       else
548         unicode_entity(((s[0] & 0x07) << 18)
549                        | ((s[1] ^ 0x80) << 12)
550                        | ((s[2] ^ 0x80) << 6)
551                        | (s[3] ^ 0x80));
552       byte = FIRST;
553     }
554     else
555       byte = FIFTH;
556     break;
557   case FIFTH:
558     if (expected_bytes == 5) {
559       invalid();
560       byte = FIRST;
561     }
562     else
563       byte = SIXTH;
564     break;
565   case SIXTH:
566     invalid();
567     byte = FIRST;
568     break;
569   }
570 }
571
572 void
573 utf8::invalid()
574 {
575   if (debug_flag && invalid_warning) {
576     fprintf(stderr, "  invalid byte(s) found in input stream --\n"
577                     "  each such sequence replaced with 0xFFFD\n");
578     invalid_warning = 0;
579   }
580   unicode_entity(0xFFFD);
581   byte = FIRST;
582 }
583
584 void
585 utf8::incomplete()
586 {
587   if (debug_flag && incomplete_warning) {
588     fprintf(stderr, "  incomplete sequence(s) found in input stream --\n"
589                     "  each such sequence replaced with 0xFFFD\n");
590     incomplete_warning = 0;
591   }
592   unicode_entity(0xFFFD);
593   byte = FIRST;
594 }
595
596 // Conversion from UTF-8 to Unicode.
597 void
598 conversion_utf8(FILE *fp, const string &data)
599 {
600   utf8 u(fp);
601   int len = data.length();
602   const unsigned char *ptr = (const unsigned char *)data.contents();
603   for (int i = 0; i < len; i++)
604     u.add(ptr[i]);
605   int c = -1;
606   while ((c = getc(fp)) != EOF)
607     u.add(c);
608   return;
609 }
610
611 // Conversion from cp1047 (EBCDIC) to UTF-8.
612 void
613 conversion_cp1047(FILE *fp, const string &data)
614 {
615   static unsigned char cp1047[] = {
616     0x00, 0x01, 0x02, 0x03, 0x9C, 0x09, 0x86, 0x7F,     // 0x00
617     0x97, 0x8D, 0x8E, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
618     0x10, 0x11, 0x12, 0x13, 0x9D, 0x85, 0x08, 0x87,     // 0x10
619     0x18, 0x19, 0x92, 0x8F, 0x1C, 0x1D, 0x1E, 0x1F,
620     0x80, 0x81, 0x82, 0x83, 0x84, 0x0A, 0x17, 0x1B,     // 0x20
621     0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x05, 0x06, 0x07,
622     0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x04,     // 0x30
623     0x98, 0x99, 0x9A, 0x9B, 0x14, 0x15, 0x9E, 0x1A,
624     0x20, 0xA0, 0xE2, 0xE4, 0xE0, 0xE1, 0xE3, 0xE5,     // 0x40
625     0xE7, 0xF1, 0xA2, 0x2E, 0x3C, 0x28, 0x2B, 0x7C,
626     0x26, 0xE9, 0xEA, 0xEB, 0xE8, 0xED, 0xEE, 0xEF,     // 0x50
627     0xEC, 0xDF, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0x5E,
628     0x2D, 0x2F, 0xC2, 0xC4, 0xC0, 0xC1, 0xC3, 0xC5,     // 0x60
629     0xC7, 0xD1, 0xA6, 0x2C, 0x25, 0x5F, 0x3E, 0x3F,
630     0xF8, 0xC9, 0xCA, 0xCB, 0xC8, 0xCD, 0xCE, 0xCF,     // 0x70
631     0xCC, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22,
632     0xD8, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,     // 0x80
633     0x68, 0x69, 0xAB, 0xBB, 0xF0, 0xFD, 0xFE, 0xB1,
634     0xB0, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70,     // 0x90
635     0x71, 0x72, 0xAA, 0xBA, 0xE6, 0xB8, 0xC6, 0xA4,
636     0xB5, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,     // 0xA0
637     0x79, 0x7A, 0xA1, 0xBF, 0xD0, 0x5B, 0xDE, 0xAE,
638     0xAC, 0xA3, 0xA5, 0xB7, 0xA9, 0xA7, 0xB6, 0xBC,     // 0xB0
639     0xBD, 0xBE, 0xDD, 0xA8, 0xAF, 0x5D, 0xB4, 0xD7,
640     0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,     // 0xC0
641     0x48, 0x49, 0xAD, 0xF4, 0xF6, 0xF2, 0xF3, 0xF5,
642     0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50,     // 0xD0
643     0x51, 0x52, 0xB9, 0xFB, 0xFC, 0xF9, 0xFA, 0xFF,
644     0x5C, 0xF7, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,     // 0xE0
645     0x59, 0x5A, 0xB2, 0xD4, 0xD6, 0xD2, 0xD3, 0xD5,
646     0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,     // 0xF0
647     0x38, 0x39, 0xB3, 0xDB, 0xDC, 0xD9, 0xDA, 0x9F,
648   };
649   int len = data.length();
650   const unsigned char *ptr = (const unsigned char *)data.contents();
651   for (int i = 0; i < len; i++)
652     unicode_entity(cp1047[ptr[i]]);
653   int c = -1;
654   while ((c = getc(fp)) != EOF)
655     unicode_entity(cp1047[c]);
656 }
657
658 // Locale-sensible conversion.
659 #if HAVE_ICONV
660 void
661 conversion_iconv(FILE *fp, const string &data, char *enc)
662 {
663   iconv_t handle = iconv_open(UNICODE, enc);
664   if (handle == (iconv_t)-1) {
665     if (errno == EINVAL) {
666       error("encoding system '%1' not supported by iconv()", enc);
667       return;
668     }
669     fatal("iconv_open failed");
670   }
671   char inbuf[BUFSIZ];
672   int outbuf[BUFSIZ];
673   char *outptr = (char *)outbuf;
674   size_t outbytes_left = BUFSIZ * sizeof (int);
675   // Handle 'data'.
676   char *inptr = (char *)data.contents();
677   size_t inbytes_left = data.length();
678   char *limit;
679   while (inbytes_left > 0) {
680     size_t status = iconv(handle,
681                           (ICONV_CONST char **)&inptr, &inbytes_left,
682                           &outptr, &outbytes_left);
683     if (status == (size_t)-1) {
684       if (errno == EILSEQ) {
685         // Invalid byte sequence.  XXX
686         inptr++;
687         inbytes_left--;
688       }
689       else if (errno == E2BIG) {
690         // Output buffer is full.
691         limit = (char *)outbuf + BUFSIZ * sizeof (int) - outbytes_left;
692         for (int *ptr = outbuf; (char *)ptr < limit; ptr++)
693           unicode_entity(*ptr);
694         memmove(outbuf, outptr, outbytes_left);
695         outptr = (char *)outbuf + outbytes_left;
696         outbytes_left = BUFSIZ * sizeof (int) - outbytes_left;
697       }
698       else if (errno == EINVAL) {
699         // 'data' ends with partial input sequence.
700         memcpy(inbuf, inptr, inbytes_left);
701         break;
702       }
703     }
704   }
705   // Handle 'fp' and switch to 'inbuf'.
706   size_t read_bytes;
707   char *read_start = inbuf + inbytes_left;
708   while ((read_bytes = fread(read_start, 1, BUFSIZ - inbytes_left, fp)) > 0) {
709     inptr = inbuf;
710     inbytes_left += read_bytes;
711     while (inbytes_left > 0) {
712       size_t status = iconv(handle,
713                             (ICONV_CONST char **)&inptr, &inbytes_left,
714                             &outptr, &outbytes_left);
715       if (status == (size_t)-1) {
716         if (errno == EILSEQ) {
717           // Invalid byte sequence.  XXX
718           inptr++;
719           inbytes_left--;
720         }
721         else if (errno == E2BIG) {
722           // Output buffer is full.
723           limit = (char *)outbuf + BUFSIZ * sizeof (int) - outbytes_left;
724           for (int *ptr = outbuf; (char *)ptr < limit; ptr++)
725             unicode_entity(*ptr);
726           memmove(outbuf, outptr, outbytes_left);
727           outptr = (char *)outbuf + outbytes_left;
728           outbytes_left = BUFSIZ * sizeof (int) - outbytes_left;
729         }
730         else if (errno == EINVAL) {
731           // 'inbuf' ends with partial input sequence.
732           memmove(inbuf, inptr, inbytes_left);
733           break;
734         }
735       }
736     }
737     read_start = inbuf + inbytes_left;
738   }
739   iconv_close(handle);
740   // XXX use ferror?
741   limit = (char *)outbuf + BUFSIZ * sizeof (int) - outbytes_left;
742   for (int *ptr = outbuf; (char *)ptr < limit; ptr++)
743     unicode_entity(*ptr);
744 }
745 #endif /* HAVE_ICONV */
746
747 // ---------------------------------------------------------
748 // Handle Byte Order Mark.
749 //
750 // Since we have a chicken-and-egg problem it's necessary
751 // to handle the BOM manually if it is in the data stream.
752 // As documented in the Unicode book it is very unlikely
753 // that any normal text file (regardless of the encoding)
754 // starts with the bytes which represent a BOM.
755 //
756 // Return the BOM in string 'BOM'; 'data' then starts with
757 // the byte after the BOM.  This function reads (at most)
758 // four bytes from the data stream.
759 //
760 // Return encoding if a BOM is found, NULL otherwise.
761 // ---------------------------------------------------------
762 const char *
763 get_BOM(FILE *fp, string &BOM, string &data)
764 {
765   // The BOM is U+FEFF.  We have thus the following possible
766   // representations.
767   //
768   //   UTF-8: 0xEFBBBF
769   //   UTF-16: 0xFEFF or 0xFFFE
770   //   UTF-32: 0x0000FEFF or 0xFFFE0000
771   static struct {
772     int len;
773     const char *str;
774     const char *name;
775   } BOM_table[] = {
776     {4, "\x00\x00\xFE\xFF", "UTF-32"},
777     {4, "\xFF\xFE\x00\x00", "UTF-32"},
778     {3, "\xEF\xBB\xBF", "UTF-8"},
779     {2, "\xFE\xFF", "UTF-16"},
780     {2, "\xFF\xFE", "UTF-16"},
781   };
782   const int BOM_table_len = sizeof (BOM_table) / sizeof (BOM_table[0]);
783   char BOM_string[4];
784   const char *retval = NULL;
785   int len;
786   for (len = 0; len < 4; len++) {
787     int c = getc(fp);
788     if (c == EOF)
789       break;
790     BOM_string[len] = char(c);
791   }
792   int i;
793   for (i = 0; i < BOM_table_len; i++) {
794     if (BOM_table[i].len <= len
795         && memcmp(BOM_string, BOM_table[i].str, BOM_table[i].len) == 0)
796       break;
797   }
798   int j = 0;
799   if (i < BOM_table_len) {
800     for (; j < BOM_table[i].len; j++)
801       BOM += BOM_string[j];
802     retval = BOM_table[i].name;
803   }
804   for (; j < len; j++)
805     data += BOM_string[j];
806   return retval;
807 }
808
809 // ---------------------------------------------------------
810 // Get first two lines from input stream.
811 //
812 // Return string (allocated with 'new') without zero bytes
813 // or NULL in case no coding tag can occur in the data
814 // (which is stored unmodified in 'data').
815 // ---------------------------------------------------------
816 char *
817 get_tag_lines(FILE *fp, string &data)
818 {
819   int newline_count = 0;
820   int c, prev = -1;
821   // Handle CR, LF, and CRLF as line separators.
822   for (int i = 0; i < data.length(); i++) {
823     c = data[i];
824     if (c == '\n' || c == '\r')
825       newline_count++;
826     if (c == '\n' && prev == '\r')
827       newline_count--;
828     prev = c;
829   }
830   if (newline_count > 1)
831     return NULL;
832   int emit_warning = 1;
833   for (int lines = newline_count; lines < 2; lines++) {
834     while ((c = getc(fp)) != EOF) {
835       if (c == '\0' && debug_flag && emit_warning) {
836         fprintf(stderr,
837                 "  null byte(s) found in input stream --\n"
838                 "  search for coding tag might return false result\n");
839         emit_warning = 0;
840       }
841       data += char(c);
842       if (c == '\n' || c == '\r')
843         break;
844     }
845     // Handle CR, LF, and CRLF as line separators.
846     if (c == '\r') {
847       c = getc(fp);
848       if (c != EOF && c != '\n')
849         ungetc(c, fp);
850       else
851         data += char(c);
852     }
853   }
854   return data.extract();
855 }
856
857 // ---------------------------------------------------------
858 // Check whether C string starts with a comment.
859 //
860 // Return 1 if true, 0 otherwise.
861 // ---------------------------------------------------------
862 int
863 is_comment_line(char *s)
864 {
865   if (!s || !*s)
866     return 0;
867   if (*s == '.' || *s == '\'')
868   {
869     s++;
870     while (*s == ' ' || *s == '\t')
871       s++;
872     if (*s && *s == '\\')
873     {
874       s++;
875       if (*s == '"' || *s == '#')
876         return 1;
877     }
878   }
879   else if (*s == '\\')
880   {
881     s++;
882     if (*s == '#')
883       return 1;
884   }
885   return 0;
886 }
887
888 // ---------------------------------------------------------
889 // Get a value/variable pair from a local variables list
890 // in a C string which look like this:
891 //
892 //   <variable1>: <value1>; <variable2>: <value2>; ...
893 //
894 // Leading and trailing blanks are ignored.  There might be
895 // more than one blank after ':' and ';'.
896 //
897 // Return position of next value/variable pair or NULL if
898 // at end of data.
899 // ---------------------------------------------------------
900 char *
901 get_variable_value_pair(char *d1, char **variable, char **value)
902 {
903   static char var[MAX_VAR_LEN], val[MAX_VAR_LEN];
904   *variable = var;
905   *value = val;
906   while (*d1 == ' ' || *d1 == '\t')
907     d1++;
908   // Get variable.
909   int l = 0;
910   while (l < MAX_VAR_LEN - 1 && *d1 && !strchr(";: \t", *d1))
911     var[l++] = *(d1++);
912   var[l] = 0;
913   // Skip everything until ':', ';', or end of data.
914   while (*d1 && *d1 != ':' && *d1 != ';')
915     d1++;
916   val[0] = 0;
917   if (!*d1)
918     return NULL;
919   if (*d1 == ';')
920     return d1 + 1;
921   d1++;
922   while (*d1 == ' ' || *d1 == '\t')
923     d1++;
924   // Get value.
925   l = 0;
926   while (l < MAX_VAR_LEN - 1 && *d1 && !strchr("; \t", *d1))
927     val[l++] = *(d1++);
928   val[l] = 0;
929   // Skip everything until ';' or end of data.
930   while (*d1 && *d1 != ';')
931     d1++;
932   if (*d1 == ';')
933     return d1 + 1;
934   return NULL;
935 }
936
937 // ---------------------------------------------------------
938 // Check coding tag in the read buffer.
939 //
940 // We search for the following line:
941 //
942 //   <comment> ... -*-<local variables list>-*-
943 //
944 // ('...' might be anything).
945 //
946 // <comment> can be one of the following syntax forms at the
947 // beginning of the line:
948 //
949 //   .\"   .\#   '\"   '\#   \#
950 //
951 // There can be whitespace after the leading '.' or "'".
952 //
953 // The local variables list must occur within the first
954 // comment block at the very beginning of the data stream.
955 //
956 // Within the <local variables list>, we search for
957 //
958 //   coding: <value>
959 //
960 // which specifies the coding system used for the data
961 // stream.
962 //
963 // Return <value> if found, NULL otherwise.
964 //
965 // Note that null bytes in the data are skipped before applying
966 // the algorithm.  This should work even with files encoded as
967 // UTF-16 or UTF-32 (or its siblings) in most cases.
968 //
969 // XXX Add support for tag at the end of buffer.
970 // ---------------------------------------------------------
971 char *
972 check_coding_tag(FILE *fp, string &data)
973 {
974   char *inbuf = get_tag_lines(fp, data);
975   char *lineend;
976   for (char *p = inbuf; is_comment_line(p); p = lineend + 1) {
977     if ((lineend = strchr(p, '\n')) == NULL)
978       break;
979     *lineend = 0;               // switch temporarily to '\0'
980     char *d1 = strstr(p, "-*-");
981     char *d2 = 0;
982     if (d1)
983       d2 = strstr(d1 + 3, "-*-");
984     *lineend = '\n';            // restore newline
985     if (!d1 || !d2)
986       continue;
987     *d2 = 0;                    // switch temporarily to '\0'
988     d1 += 3;
989     while (d1) {
990       char *variable, *value;
991       d1 = get_variable_value_pair(d1, &variable, &value);
992       if (!strcasecmp(variable, "coding")) {
993         *d2 = '-';              // restore '-'
994         free(inbuf);
995         return value;
996       }
997     }
998     *d2 = '-';                  // restore '-'
999   }
1000   free(inbuf);
1001   return NULL;
1002 }
1003
1004 char *
1005 detect_file_encoding(FILE *fp)
1006 {
1007 #ifdef HAVE_UCHARDET
1008   uchardet_t ud = NULL;
1009   struct stat stat_buf;
1010   size_t len, read_bytes;
1011   char *data = NULL;
1012   int res, current_position;
1013   const char *charset;
1014   char *ret = NULL;
1015
1016   current_position = ftell(fp);
1017   /* due to BOM and tag detection we are not at the begining of the file */
1018   rewind(fp);
1019   if (fstat(fileno(fp), &stat_buf) != 0) {
1020     fprintf(stderr, "fstat: %s\n", strerror(errno));
1021     goto end;
1022   }
1023   len = stat_buf.st_size;
1024   if (debug_flag)
1025     fprintf(stderr, "  len: %zu\n", len);  
1026   if (len == 0)
1027     goto end;
1028   data = (char *)calloc(len, 1);
1029   read_bytes = fread(data, 1, len, fp);
1030   if (read_bytes == 0) {
1031     fprintf(stderr, "fread: %s\n", strerror(errno));
1032     goto end;
1033   }
1034   /* We rewind back to the original position */
1035   if (fseek(fp, current_position, SEEK_SET) != 0) {
1036     fprintf(stderr, "Fatal error: fseek: %s\n", strerror(errno));
1037     goto end;
1038   }
1039   ud = uchardet_new();
1040   res = uchardet_handle_data(ud, data, len);
1041   if (res != 0) {
1042     fprintf(stderr, "uchardet_handle_data: %d\n", res);
1043     goto end;
1044   }
1045   if (debug_flag)
1046     fprintf(stderr, "  uchardet read: %zu bytes\n", read_bytes);
1047   uchardet_data_end(ud);
1048   charset = uchardet_get_charset(ud);
1049   if (debug_flag) {
1050     if (charset)
1051        fprintf(stderr, "  charset: %s\n", charset);
1052     else
1053        fprintf(stderr, "  charset is NULL\n");
1054   }
1055   /* uchardet 0.0.1 could return an empty string instead of NULL */
1056   if (charset && *charset) {
1057     ret = (char *)malloc(strlen(charset) + 1);
1058     strcpy(ret, charset);
1059   }
1060
1061 end:
1062   if (ud)
1063      uchardet_delete(ud);
1064   if (data)
1065      free(data);
1066
1067   return ret;
1068 #else /* not HAVE_UCHARDET */
1069   return NULL;
1070 #endif /* not HAVE_UCHARDET */
1071 }
1072
1073 // ---------------------------------------------------------
1074 // Handle an input file.  If filename is '-' handle stdin.
1075 //
1076 // Return 1 on success, 0 otherwise.
1077 // ---------------------------------------------------------
1078 int
1079 do_file(const char *filename)
1080 {
1081   FILE *fp;
1082   string BOM, data;
1083   if (strcmp(filename, "-")) {
1084     if (debug_flag)
1085       fprintf(stderr, "file '%s':\n", filename);
1086     fp = fopen(filename, FOPEN_RB);
1087     if (!fp) {
1088       error("can't open '%1': %2", filename, strerror(errno));
1089       return 0;
1090     }
1091   }
1092   else {
1093     if (debug_flag)
1094       fprintf(stderr, "standard input:\n");
1095     SET_BINARY(fileno(stdin));
1096     fp = stdin;
1097   }
1098   const char *BOM_encoding = get_BOM(fp, BOM, data);
1099   // Determine the encoding.
1100   char *encoding;
1101   int must_free_encoding = 0;
1102   if (user_encoding[0]) {
1103     if (debug_flag) {
1104       fprintf(stderr, "  user-specified encoding '%s', "
1105                       "no search for coding tag\n",
1106                       user_encoding);
1107       if (BOM_encoding && strcmp(BOM_encoding, user_encoding))
1108         fprintf(stderr, "  but BOM in data stream implies encoding '%s'!\n",
1109                         BOM_encoding);
1110     }
1111     encoding = (char *)user_encoding;
1112   }
1113   else if (BOM_encoding) {
1114     if (debug_flag)
1115       fprintf(stderr, "  found BOM, no search for coding tag\n");
1116     encoding = (char *)BOM_encoding;
1117   }
1118   else {
1119     // 'check_coding_tag' returns a pointer to a static array (or NULL).
1120     char *file_encoding = check_coding_tag(fp, data);
1121     if (!file_encoding) {
1122       if (debug_flag)
1123         fprintf(stderr, "  no encoding tag\n");
1124       file_encoding = detect_file_encoding(fp);
1125       if (!file_encoding) {
1126         if (debug_flag)
1127           fprintf(stderr, "  could not detect encoding with uchardet\n");
1128         file_encoding = default_encoding;
1129       }
1130       else
1131         must_free_encoding = 1;
1132     }
1133     else
1134       if (debug_flag)
1135         fprintf(stderr, "  file encoding: '%s'\n", file_encoding);
1136     encoding = file_encoding;
1137   }
1138   strncpy(encoding_string, encoding, MAX_VAR_LEN - 1);
1139   encoding_string[MAX_VAR_LEN - 1] = 0;
1140   if (must_free_encoding)
1141     free(encoding);
1142   encoding = encoding_string;
1143   // Translate from MIME & Emacs encoding names to locale encoding names.
1144   encoding = emacs2mime(encoding_string);
1145   if (encoding[0] == '\0') {
1146     error("encoding '%1' not supported, not a portable encoding",
1147           encoding_string);
1148     return 0;
1149   }
1150   if (debug_flag)
1151     fprintf(stderr, "  encoding used: '%s'\n", encoding);
1152   if (!raw_flag) {
1153     string fn(filename);
1154     fn += '\0';
1155     normalize_for_lf(fn);
1156     printf(".lf 1 %s\n", fn.contents());
1157   }
1158   int success = 1;
1159   // Call converter (converters write to stdout).
1160   if (!strcasecmp(encoding, "ISO-8859-1"))
1161     conversion_latin1(fp, BOM + data);
1162   else if (!strcasecmp(encoding, "UTF-8"))
1163     conversion_utf8(fp, data);
1164   else if (!strcasecmp(encoding, "cp1047"))
1165     conversion_cp1047(fp, BOM + data);
1166   else {
1167 #if HAVE_ICONV
1168     conversion_iconv(fp, BOM + data, encoding);
1169 #else
1170     error("encoding system '%1' not supported", encoding);
1171     success = 0;
1172 #endif /* HAVE_ICONV */
1173   }
1174   if (fp != stdin)
1175     fclose(fp);
1176   return success;
1177 }
1178
1179 // ---------------------------------------------------------
1180 // Print usage.
1181 // ---------------------------------------------------------
1182 void
1183 usage(FILE *stream)
1184 {
1185   fprintf(stream, "usage: %s [ option ] [ files ]\n"
1186                   "\n"
1187                   "-d           show debugging messages\n"
1188                   "-D encoding  specify default encoding\n"
1189                   "-e encoding  specify input encoding\n"
1190                   "-h           print this message\n"
1191                   "-r           don't add .lf requests\n"
1192                   "-v           print version number\n"
1193                   "\n"
1194                   "The default encoding is '%s'.\n",
1195                   program_name, default_encoding);
1196 }
1197
1198 // ---------------------------------------------------------
1199 // Main routine.
1200 // ---------------------------------------------------------
1201 int
1202 main(int argc, char **argv)
1203 {
1204   program_name = argv[0];
1205   // Determine the default encoding.  This must be done before
1206   // getopt() is called since the usage message shows the default
1207   // encoding.
1208   setlocale(LC_ALL, "");
1209   char *locale = getlocale(LC_CTYPE);
1210   if (!locale || !strcmp(locale, "C") || !strcmp(locale, "POSIX"))
1211     strcpy(default_encoding, "latin1");
1212   else {
1213     strncpy(default_encoding, locale_charset(), MAX_VAR_LEN - 1);
1214     default_encoding[MAX_VAR_LEN - 1] = 0;
1215   }
1216
1217   program_name = argv[0];
1218   int opt;
1219   static const struct option long_options[] = {
1220     { "help", no_argument, 0, 'h' },
1221     { "version", no_argument, 0, 'v' },
1222     { NULL, 0, 0, 0 }
1223   };
1224   // Parse the command-line options.
1225   while ((opt = getopt_long(argc, argv,
1226                             "dD:e:hrv", long_options, NULL)) != EOF)
1227     switch (opt) {
1228     case 'v':
1229       printf("GNU preconv (groff) version %s %s iconv support and %s uchardet support\n",
1230              Version_string,
1231 #ifdef HAVE_ICONV
1232              "with",
1233 #else
1234              "without",
1235 #endif /* HAVE_ICONV */
1236 #ifdef HAVE_UCHARDET
1237              "with"
1238 #else
1239              "without"
1240 #endif /* HAVE_UCHARDET */
1241             );
1242       exit(0);
1243       break;
1244     case 'd':
1245       debug_flag = 1;
1246       break;
1247     case 'e':
1248       if (optarg) {
1249         strncpy(user_encoding, optarg, MAX_VAR_LEN - 1);
1250         user_encoding[MAX_VAR_LEN - 1] = 0;
1251       }
1252       else
1253         user_encoding[0] = 0;
1254       break;
1255     case 'D':
1256       if (optarg) {
1257         strncpy(default_encoding, optarg, MAX_VAR_LEN - 1);
1258         default_encoding[MAX_VAR_LEN - 1] = 0;
1259       }
1260       break;
1261     case 'r':
1262       raw_flag = 1;
1263       break;
1264     case 'h':
1265       usage(stdout);
1266       exit(0);
1267       break;
1268     case '?':
1269       usage(stderr);
1270       exit(1);
1271       break;
1272     default:
1273       assert(0);
1274     }
1275   int nbad = 0;
1276   if (debug_flag)
1277     fprintf(stderr, "default encoding: '%s'\n", default_encoding);
1278   if (optind >= argc)
1279     nbad += !do_file("-");
1280   else
1281     for (int i = optind; i < argc; i++)
1282       nbad += !do_file(argv[i]);
1283   if (ferror(stdout) || fflush(stdout) < 0)
1284     fatal("output error");
1285   return nbad != 0;
1286 }
1287
1288 /* end of preconv.cpp */