72 |
* distribution; send me updates for languages I've missed. |
* distribution; send me updates for languages I've missed. |
73 |
* |
* |
74 |
* Explicit encodings in the directory name (e.g. de_DE.UTF-8) override this |
* Explicit encodings in the directory name (e.g. de_DE.UTF-8) override this |
75 |
* table. TODO: Implement this. |
* table. |
76 |
*/ |
*/ |
77 |
static struct { |
struct directory_entry { |
78 |
const char *lang_dir; |
const char *lang_dir; |
79 |
const char *source_encoding; |
const char *source_encoding; |
80 |
const char *standard_output_encoding; |
const char *standard_output_encoding; |
81 |
} directory_table[] = { |
}; |
82 |
|
|
83 |
|
static struct directory_entry directory_table[] = { |
84 |
{ "C", "ISO-8859-1", "ANSI_X3.4-1968" }, /* English */ |
{ "C", "ISO-8859-1", "ANSI_X3.4-1968" }, /* English */ |
85 |
{ "POSIX", "ISO-8859-1", "ANSI_X3.4-1968" }, /* English */ |
{ "POSIX", "ISO-8859-1", "ANSI_X3.4-1968" }, /* English */ |
86 |
{ "da", "ISO-8859-1", "ISO-8859-1" }, /* Danish */ |
{ "da", "ISO-8859-1", "ISO-8859-1" }, /* Danish */ |
109 |
{ "ru", "KOI8-R", "KOI8-R" }, /* Russian */ |
{ "ru", "KOI8-R", "KOI8-R" }, /* Russian */ |
110 |
#endif /* MULTIBYTE_GROFF */ |
#endif /* MULTIBYTE_GROFF */ |
111 |
|
|
112 |
{ NULL, NULL, "NULL" } }; |
{ NULL, NULL, "NULL" } |
113 |
|
}; |
114 |
|
|
115 |
/* The default groff terminal output device to be used is determined based |
/* The default groff terminal output device to be used is determined based |
116 |
* on nl_langinfo(CODESET), which returns the character set used by the |
* on nl_langinfo(CODESET), which returns the character set used by the |
117 |
* current locale. |
* current locale. |
118 |
*/ |
*/ |
119 |
static struct { |
struct charset_entry { |
120 |
const char *locale_charset; |
const char *locale_charset; |
121 |
const char *default_device; |
const char *default_device; |
122 |
} charset_table[] = { |
}; |
123 |
|
|
124 |
|
static struct charset_entry charset_table[] = { |
125 |
{ "ANSI_X3.4-1968", "ascii" }, |
{ "ANSI_X3.4-1968", "ascii" }, |
126 |
{ "ISO-8859-1", "latin1" }, |
{ "ISO-8859-1", "latin1" }, |
127 |
{ "UTF-8", "utf8" }, |
{ "UTF-8", "utf8" }, |
130 |
{ "EUC-JP", "nippon" }, |
{ "EUC-JP", "nippon" }, |
131 |
#endif /* MULTIBYTE_GROFF */ |
#endif /* MULTIBYTE_GROFF */ |
132 |
|
|
133 |
{ NULL, NULL } }; |
{ NULL, NULL } |
134 |
|
}; |
135 |
|
|
136 |
static const char *fallback_default_device = |
static const char *fallback_default_device = |
137 |
#ifdef MULTIBYTE_GROFF |
#ifdef MULTIBYTE_GROFF |
148 |
* straight through so is (probably ...) encoding-agnostic. If this encoding |
* straight through so is (probably ...) encoding-agnostic. If this encoding |
149 |
* does not match the source encoding, an iconv pipe is used (if available) |
* does not match the source encoding, an iconv pipe is used (if available) |
150 |
* to perform recoding. |
* to perform recoding. |
|
* |
|
|
* Setting less_charset to latin1 tells the less pager that characters |
|
|
* between 0xA0 and 0xFF are displayable, not that its input is encoded in |
|
|
* ISO-8859-1. TODO: Perhaps using LESSCHARDEF would be better. |
|
151 |
*/ |
*/ |
152 |
static struct { |
struct device_entry { |
153 |
const char *roff_device; |
const char *roff_device; |
154 |
const char *roff_encoding; |
const char *roff_encoding; |
155 |
const char *less_charset; |
const char *output_encoding; |
156 |
} device_table[] = { |
}; |
157 |
{ "ascii", "ISO-8859-1", "ascii" }, |
|
158 |
{ "latin1", "ISO-8859-1", "latin1" }, |
static struct device_entry device_table[] = { |
159 |
{ "utf8", "ISO-8859-1", "utf-8" }, |
{ "ascii", "ISO-8859-1", "ANSI_X3.4-1968" }, |
160 |
|
{ "latin1", "ISO-8859-1", "ISO-8859-1" }, |
161 |
|
{ "utf8", "ISO-8859-1", "UTF-8" }, |
162 |
|
|
163 |
#ifdef MULTIBYTE_GROFF |
#ifdef MULTIBYTE_GROFF |
164 |
{ "ascii8", NULL, "latin1" }, |
{ "ascii8", NULL, NULL }, |
165 |
{ "nippon", "EUC-JP", "ja" }, |
{ "nippon", "EUC-JP", "EUC-JP" }, |
166 |
#endif /* MULTIBYTE_GROFF */ |
#endif /* MULTIBYTE_GROFF */ |
167 |
|
|
168 |
{ NULL, NULL, NULL } }; |
{ NULL, NULL, NULL } |
169 |
|
}; |
170 |
|
|
171 |
static const char *fallback_roff_encoding = "ISO-8859-1"; |
static const char *fallback_roff_encoding = "ISO-8859-1"; |
172 |
static const char *fallback_less_charset = "latin1"; |
|
173 |
|
/* Setting less_charset to iso8859 tells the less pager that characters |
174 |
|
* between 0xA0 and 0xFF are displayable, not that its input is encoded in |
175 |
|
* ISO-8859-*. TODO: Perhaps using LESSCHARDEF would be better. |
176 |
|
*/ |
177 |
|
struct less_charset_entry { |
178 |
|
const char *locale_charset; |
179 |
|
const char *less_charset; |
180 |
|
}; |
181 |
|
|
182 |
|
static struct less_charset_entry less_charset_table[] = { |
183 |
|
{ "ANSI_X3.4-1968", "ascii" }, |
184 |
|
{ "ISO-8859-1", "iso8859" }, |
185 |
|
{ "UTF-8", "utf-8" }, |
186 |
|
|
187 |
|
#ifdef MULTIBYTE_GROFF |
188 |
|
{ "EUC-JP", "ja" }, |
189 |
|
{ "KOI8-R", "koi8-r" }, |
190 |
|
#endif /* MULTIBYTE_GROFF */ |
191 |
|
|
192 |
|
{ NULL, NULL } |
193 |
|
}; |
194 |
|
|
195 |
|
static const char *fallback_less_charset = "iso8859"; |
196 |
|
|
197 |
|
|
198 |
/* Return the assumed encoding of the source man page, based on the |
/* Return the assumed encoding of the source man page, based on the |
303 |
return NULL; |
return NULL; |
304 |
} |
} |
305 |
|
|
306 |
|
/* Can we take this input encoding and produce this output encoding, perhaps |
307 |
|
* with the help of some iconv pipes? */ |
308 |
|
static int compatible_encodings (const char *input, const char *output) |
309 |
|
{ |
310 |
|
#ifdef MULTIBYTE_GROFF |
311 |
|
/* NULL output means that its encoding is passed through from the |
312 |
|
* input, so it must be compatible. |
313 |
|
*/ |
314 |
|
if (!output) |
315 |
|
return 1; |
316 |
|
#endif /* MULTIBYTE_GROFF */ |
317 |
|
|
318 |
|
if (STREQ (input, output)) |
319 |
|
return 1; |
320 |
|
|
321 |
|
/* If the input is ASCII, recoding should be easy. Try it. */ |
322 |
|
if (STREQ (input, "ANSI_X3.4-1968")) |
323 |
|
return 1; |
324 |
|
|
325 |
|
/* If the input is UTF-8, it's either a simple recoding of whatever |
326 |
|
* we want or else it probably won't work at all no matter what we |
327 |
|
* do. We might as well try it for now. |
328 |
|
*/ |
329 |
|
if (STREQ (input, "UTF-8")) |
330 |
|
return 1; |
331 |
|
|
332 |
|
#ifdef MULTIBYTE_GROFF |
333 |
|
/* Special case for ja_JP.UTF-8, which takes UTF-8 input recoded |
334 |
|
* from EUC-JP and produces UTF-8 output. This is rather filthy. |
335 |
|
*/ |
336 |
|
if (STREQ (input, "EUC-JP") && STREQ (output, "UTF-8")) |
337 |
|
return 1; |
338 |
|
#endif /* MULTIBYTE_GROFF */ |
339 |
|
|
340 |
|
return 0; |
341 |
|
} |
342 |
|
|
343 |
/* Return the default groff device for the given character set. This may be |
/* Return the default groff device for the given character set. This may be |
344 |
* overridden by the user. |
* overridden by the user. The page's source encoding is needed to ensure |
345 |
|
* that the device is compatible: consider ru_RU.UTF-8, which needs ascii8 |
346 |
|
* and a trailing iconv pipe to recode to UTF-8. |
347 |
|
* |
348 |
|
* All this encoding compatibility stuff feels like a slightly nasty hack, |
349 |
|
* but I haven't yet come up with a cleaner way to do it. |
350 |
*/ |
*/ |
351 |
const char *get_default_device (const char *locale_charset) |
const char *get_default_device (const char *locale_charset, |
352 |
|
const char *source_encoding) |
353 |
{ |
{ |
354 |
int i; |
int i; |
355 |
|
|
356 |
if (!locale_charset) |
if (!locale_charset || !source_encoding) |
357 |
return fallback_default_device; |
return fallback_default_device; |
358 |
|
|
359 |
for (i = 0; charset_table[i].locale_charset; ++i) { |
for (i = 0; charset_table[i].locale_charset; ++i) { |
360 |
if (STREQ (charset_table[i].locale_charset, locale_charset)) |
const struct charset_entry *entry = &charset_table[i]; |
361 |
return charset_table[i].default_device; |
if (STREQ (entry->locale_charset, locale_charset)) { |
362 |
|
const char *roff_encoding = |
363 |
|
get_roff_encoding (entry->default_device); |
364 |
|
if (compatible_encodings (source_encoding, |
365 |
|
roff_encoding)) |
366 |
|
return entry->default_device; |
367 |
|
} |
368 |
} |
} |
369 |
|
|
370 |
return fallback_default_device; |
return fallback_default_device; |
378 |
{ |
{ |
379 |
int i; |
int i; |
380 |
int found = 0; |
int found = 0; |
381 |
const char *roff_encoding = NULL, *less_charset = NULL; |
const char *roff_encoding = NULL; |
382 |
|
|
383 |
for (i = 0; device_table[i].roff_device; ++i) { |
for (i = 0; device_table[i].roff_device; ++i) { |
384 |
if (STREQ (device_table[i].roff_device, device)) { |
if (STREQ (device_table[i].roff_device, device)) { |
385 |
found = 1; |
found = 1; |
386 |
roff_encoding = device_table[i].roff_encoding; |
roff_encoding = device_table[i].roff_encoding; |
|
less_charset = device_table[i].less_charset; |
|
387 |
break; |
break; |
388 |
} |
} |
389 |
} |
} |
390 |
|
|
391 |
if (!found) { |
if (!found) |
|
less_charset = fallback_less_charset; |
|
392 |
roff_encoding = fallback_roff_encoding; |
roff_encoding = fallback_roff_encoding; |
|
} |
|
393 |
|
|
394 |
#ifdef MULTIBYTE_GROFF |
#ifdef MULTIBYTE_GROFF |
395 |
/* An ugly special case is needed here. The utf8 device normally |
/* An ugly special case is needed here. The utf8 device normally |
405 |
} |
} |
406 |
#endif /* MULTIBYTE_GROFF */ |
#endif /* MULTIBYTE_GROFF */ |
407 |
|
|
|
putenv (strappend (NULL, "LESSCHARSET=", less_charset, NULL)); |
|
408 |
return roff_encoding; |
return roff_encoding; |
409 |
} |
} |
410 |
|
|
411 |
|
/* Find the output encoding that this device will produce, or NULL if it |
412 |
|
* will simply pass through the input encoding. |
413 |
|
*/ |
414 |
|
const char *get_output_encoding (const char *device) |
415 |
|
{ |
416 |
|
int i; |
417 |
|
|
418 |
|
for (i = 0; device_table[i].roff_device; ++i) |
419 |
|
if (STREQ (device_table[i].roff_device, device)) |
420 |
|
return device_table[i].output_encoding; |
421 |
|
|
422 |
|
return NULL; |
423 |
|
} |
424 |
|
|
425 |
|
/* Return the value of LESSCHARSET appropriate for this locale. */ |
426 |
|
const char *get_less_charset (const char *locale_charset) |
427 |
|
{ |
428 |
|
int i; |
429 |
|
|
430 |
|
for (i = 0; less_charset_table[i].locale_charset; ++i) { |
431 |
|
const struct less_charset_entry *entry = |
432 |
|
&less_charset_table[i]; |
433 |
|
if (STREQ (entry->locale_charset, locale_charset)) |
434 |
|
return entry->less_charset; |
435 |
|
} |
436 |
|
|
437 |
|
return fallback_less_charset; |
438 |
|
} |