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