1 /*
2 * translat.c: Stuff for handling different encodings
3 * and a digraph entry facility. Support an international IRC!
4 *
5 * <subliminal message> you start using utf-8 and
6 * discard all legacy encodings</subliminal message>
7 *
8 * Joel Yliluoma.
9 */
10
11 #include "irc.h"
12 IRCII_RCSID("@(#)$eterna: translat.c,v 1.49 2020/11/17 08:11:57 mrg Exp $");
13
14 #ifdef HAVE_ICONV_H
15 #include <iconv.h>
16 #endif /* HAVE_ICONV_H */
17
18 #include "vars.h"
19 #include "translat.h"
20 #include "ircaux.h"
21 #include "window.h"
22 #include "screen.h"
23 #include "output.h"
24 #include "edit.h"
25
26 static u_char my_getarg(u_char **);
27
28 static char digraph_changed = 0;
29
30 #ifdef HAVE_ICONV_OPEN
31 static u_char *irc_encoding = NULL;
32 static u_char *display_encoding = NULL;
33 static u_char *input_encoding = NULL;
34 #endif /* HAVE_ICONV_OPEN */
35
36
37 /*
38 * dig_table_lo[] and dig_table_hi[] contain the character pair that
39 * will result in the digraph in dig_table_di[]. To avoid searching
40 * both tables, I take the lower character of the pair, and only
41 * search dig_table_lo[]. Thus, dig_table_lo[] must always contain
42 * the lower character of the pair.
43 *
44 * The digraph tables are based on those in the excellent editor Elvis,
45 * with some additions for those, like me, who are used to VT320 or
46 * VT420 terminals.
47 */
48
49 #define DIG_TABLE_SIZE 256
50
51 #define DiLo(x) x,
52 #define DiHi(x)
53 #define DiDi(x)
54
55 /*
56 * Digraph tables. Note that, when adding a new digraph, the character
57 * of the pair with the lowest value, *must* be in the DiLo column.
58 * The higher of the pair goes in DiHi, and the digraph itself in DiDi.
59 */
60
61 static u_char dig_table_lo[DIG_TABLE_SIZE] =
62 {
63 #include "digraph.inc"
64 0
65 };
66
67
68 #undef DiLo
69 #undef DiHi
70 #undef DiDi
71 #define DiLo(x)
72 #define DiHi(x) x,
73 #define DiDi(x)
74
75 static u_char dig_table_hi[DIG_TABLE_SIZE] =
76 {
77 #include "digraph.inc"
78 0
79 };
80
81
82 #undef DiLo
83 #undef DiHi
84 #undef DiDi
85 #define DiLo(x)
86 #define DiHi(x)
87 #define DiDi(x) x,
88
89 static u_char dig_table_di[DIG_TABLE_SIZE] =
90 {
91 #include "digraph.inc"
92 0
93 };
94
95
96 /*
97 * enter_digraph: The BIND function ENTER_DIGRAPH.
98 */
99 void
enter_digraph(u_int key,u_char * str)100 enter_digraph(u_int key, u_char *str)
101 {
102
103 set_digraph_hit(1); /* Just stuff away first character. */
104 }
105
106 /*
107 * get_digraph: Called by edit_char() when a digraph entry is activated.
108 * Looks up a digraph given u_char c1 and the global u_char
109 * get_current_screen()->digraph_hit.
110 */
111 u_char
get_digraph(u_int ic1)112 get_digraph(u_int ic1)
113 {
114 int i = 0;
115 u_char c,
116 c2 = get_digraph_first(),
117 c1 = (u_char)ic1;
118
119 set_digraph_hit(0);
120 if (c1 > c2) /* Make sure we have the lowest one in c1. */
121 c = c1, c1 = c2, c2 = c;
122 while (dig_table_lo[i])
123 { /* Find digraph and return it. */
124 if ((dig_table_lo[i] == c1) && (dig_table_hi[i] == c2))
125 return dig_table_di[i];
126 i++;
127 }
128 return 0; /* Failed lookup. */
129 }
130
131 /*
132 * digraph: The /DIGRAPH command with facilities.
133 * This routine is *NOT* finished yet.
134 */
135
136 void
digraph(u_char * command,u_char * args,u_char * subargs)137 digraph(u_char *command, u_char *args, u_char *subargs)
138 {
139 u_char *arg;
140 u_char c1,
141 c2 = '\0',
142 c3 = '\0';
143 int i;
144 size_t len;
145
146 if ((arg = next_arg(args, &args)) && (*arg == '-'))
147 {
148 u_char *cmd = NULL;
149
150 arg++;
151 if ((len = my_strlen(arg)) == 0)
152 {
153 say("Unknown or missing flag.");
154 return;
155 }
156 malloc_strcpy(&cmd, arg);
157 lower(cmd);
158 if (my_strncmp(cmd, "add", len) == 0)
159 {
160 /*
161 * Add a digraph to the table.
162 * I *know*. This *is* a kludge.
163 */
164 if ((i = my_strlen(dig_table_lo)) ==
165 DIG_TABLE_SIZE - 1)
166 say("Sorry, digraph table full.");
167 else
168 {
169 while ((c1 = my_getarg(&args)) &&
170 (c2 = my_getarg(&args)) &&
171 (c3 = my_getarg(&args)))
172 {
173 /* Pass c1 to get_digraph() */
174 set_digraph_first(c1);
175 if (get_digraph(c2) == 0)
176 {
177 dig_table_di[i] = c3;
178 /* Make sure c1 <= c2 */
179 if (c1 > c2)
180 c3 = c1, c1 = c2, c2 = c3;
181 dig_table_lo[i] = c1;
182 dig_table_hi[i] = c2;
183 i++;
184 dig_table_lo[i] =
185 dig_table_hi[i] =
186 dig_table_di[i] = '\0';
187 digraph_changed = 1;
188 say("Digraph added to table.");
189 }
190 else
191 {
192 say("Digraph already defined in table.");
193 break;
194 }
195 }
196 if (!c2 || !c3)
197 say("Unknown or missing argument.");
198 }
199
200 }
201 else if (my_strncmp(cmd, "remove", len) == 0)
202 {
203
204 /* Remove a digraph from the table. */
205 if ((i = my_strlen(dig_table_lo)) == 0)
206 say("Digraph table is already empty.");
207 else
208 {
209 if ((c1 = my_getarg(&args)) &&
210 (c2 = my_getarg(&args)))
211 {
212 i = 0;
213 if (c1 > c2)
214 c3 = c1, c1 = c2, c2 = c3;
215 while (dig_table_lo[i])
216 {
217 if ((dig_table_lo[i] == c1) &&
218 (dig_table_hi[i] == c2))
219 /*
220 * FIXME: strcpy() is not guaranteed for
221 * overlapping copying, but this one
222 * is high -> low. Ought to be fixed.
223 */
224 /* re-indent this block - phone, jan 1993. */
225 {
226 my_strcpy(dig_table_lo + i, dig_table_lo + i + 1);
227 my_strcpy(dig_table_hi + i, dig_table_hi + i + 1);
228 my_strcpy(dig_table_di + i, dig_table_di + i + 1);
229 digraph_changed = 1;
230 put_it("Digraph removed from table.");
231 return;
232 }
233 /* much better */
234 i++;
235 }
236 say("Digraph not found.");
237 }
238 }
239 }
240 else if (my_strncmp(cmd, "clear", len) == 0)
241 {
242
243 /* Clear digraph table. */
244 dig_table_lo[0] = dig_table_hi[0] = dig_table_di[0] = '\0';
245 digraph_changed = 1;
246 say("Digraph table cleared.");
247
248 }
249 else
250 say("Unknown flag.");
251 }
252 else
253 {
254
255 /* Display digraph table. */
256 u_char buffer1[8];
257 u_char buffer2[192];
258
259 say("Digraph table:");
260 buffer2[0] = '\0';
261 i = 0;
262 while(dig_table_lo[i])
263 {
264 snprintf(CP(buffer1), sizeof buffer1, "%c%c %c ", dig_table_lo[i],
265 dig_table_hi[i], dig_table_di[i]);
266 my_strcat(buffer2, buffer1);
267 if ((++i % 10) == 0)
268 {
269 put_it("%s", CP(buffer2));
270 buffer2[0] = '\0';
271 }
272 }
273 if (buffer2[0])
274 put_it("%s", CP(buffer2));
275 snprintf(CP(buffer2), sizeof buffer2, "%d digraphs listed.", i);
276 say("%s", CP(buffer2));
277 }
278 }
279
280 static u_char
my_getarg(u_char ** args)281 my_getarg(u_char **args)
282 {
283 u_char *arg;
284
285 arg = next_arg(*args, args);
286 if (!args || !*args || !arg)
287 return '\0';
288 /* Don't trust isdigit() with 8 bits. */
289 if ((*arg <= '9') && (*arg >= '0'))
290 {
291 u_char i = *arg & 0x0f;
292 while ( *(++arg) )
293 i = (i * 10) + (*arg & 0x0f);
294 return i;
295 }
296 else if ( (*arg == '!') && (*(arg + 1)) )
297 return *(arg + 1) | 0x80;
298 return *arg;
299 }
300
301 void
save_digraphs(FILE * fp)302 save_digraphs(FILE *fp)
303 {
304 if (digraph_changed)
305 {
306
307 int i = 0;
308 char *command = "\nDIGRAPH -ADD ";
309
310 fprintf(fp, "DIGRAPH -CLEAR");
311 fprintf(fp, "%s", command);
312 while(1)
313 {
314 fprintf(fp, "%d %d %d ", dig_table_lo[i],
315 dig_table_hi[i], dig_table_di[i]);
316 if (!dig_table_lo[++i])
317 break;
318 if (!(i % 5))
319 fprintf(fp, "%s", command);
320 }
321 fputc('\n', fp);
322
323 }
324 }
325
326 char
displayable_unival(unsigned unival,iconv_t conv_out)327 displayable_unival(unsigned unival, iconv_t conv_out)
328 {
329 /* First rule out control characters */
330 if (unival < 0x20 ||
331 (unival >= 0x80 && unival < 0xA0) ||
332 (unival == 0x7F) ||
333 (unival >= 0xFFF0 && unival <= 0xFFFF))
334 return 0;
335
336 /* Range 0x80..0x9F is used in some character sets (such as cp850),
337 * but they are assigned different positions in unicode.
338 * The univals we handle here are unicode positions.
339 * In unicode, 0x80..0x9F are not used because some
340 * american programs might still blindly assume
341 * 7-bitness and take those as control characters.
342 * 0x7F is delete/backspace.
343 * 0xFFF0..0xFFFF is the unicode control range.
344 * It contains a signature token, an illegal
345 * character token and so on.
346 */
347
348 #ifdef HAVE_ICONV_OPEN
349 if (conv_out)
350 {
351 u_char utfbuf[8], *utfptr;
352 u_char outbuf[256], *outptr;
353 size_t utfspace, outspace;
354 size_t retval;
355
356 /* Now sequence the character to buffer
357 * and let iconv say whether it can displayed.
358 */
359 utf8_sequence(unival, utfbuf);
360
361 utfptr = utfbuf;
362 outptr = outbuf;
363 utfspace = my_strlen(utfbuf);
364 outspace = sizeof outbuf;
365
366 /* reset the converter */
367 iconv(conv_out, NULL, 0, NULL, 0);
368
369 /* *outptr = '\0'; */
370 retval = iconv(conv_out,
371 (iconv_const char**)(void *)&utfptr, &utfspace,
372 (char **)(void *)&outptr, &outspace);
373
374 /*
375 *outptr = '\0';
376 fprintf(stderr, "CHK: '%s' -> '%s', retval=%d, errno=%d\n",
377 utfbuf, outbuf,
378 retval, errno);
379 */
380 return retval != (size_t)-1;
381 }
382 #endif /* HAVE_ICONV_OPEN */
383 return 1;
384 }
385
386 unsigned
calc_unival_width(unsigned unival)387 calc_unival_width(unsigned unival)
388 {
389 /* FIXME: Should we use some kind of database here?
390 * FIXME: Combining marks support is completely untested
391 */
392
393 /* chinese, japanese, korean */
394 if (unival >= 0x3000 && unival < 0xFF00)
395 return 2;
396 /* combining diacritical marks */
397 if (unival >= 0x0300 && unival < 0x0400)
398 return 0;
399 /* combining diacritical marks for symbols */
400 if (unival >= 0x20D0 && unival < 0x2100)
401 return 0;
402 /* combining half-marks */
403 if (unival >= 0xFE20 && unival < 0xFE30)
404 return 0;
405 /* everything else */
406 return 1;
407 }
408
409 /* Returns the number of bytes taken by the given utf-8 code */
410 unsigned
calc_unival_length(const u_char * str)411 calc_unival_length(const u_char *str)
412 {
413 static const char sizes[16] = {
414 /*
415 * 1-byte (0..7F):
416 * 0 1 2 3 4 5 6 7
417 */
418 1, 1, 1, 1, 1, 1, 1, 1,
419 /*
420 * invalid:
421 * 8 9 A B (they can not begin a sequence), but give
422 * them a length so that processing continues.
423 */
424 1, 1, 1, 1,
425 /*
426 * 2-byte (80..7FF):
427 * C D
428 */
429 2, 2,
430 /*
431 * 3-byte (800..FFFF):
432 * E
433 */
434 3,
435 /*
436 * 4-byte (10000..1FFFFF):
437 * F
438 */
439 4
440 };
441
442 /*
443 *
444 * If utf8 is some day extended to use 5-byte
445 * codings, you need to double the sizes[] size
446 * and shift str by 3 instead of 4.
447 * You'd also need to modify
448 * utf8_sequence() and calc_unival().
449 *
450 * Today, it seems unlikely that these encodings
451 * will be needed in practical applications such as
452 * an irc client. Many programs (such as Microsoft IE)
453 * don't even support 4-byte encodings.
454 * 2-3 -byte encodings are in daily use everywhere.
455 */
456
457 return sizes[*str >> 4];
458 }
459
460 unsigned
calc_unival(const u_char * utfbuf)461 calc_unival(const u_char *utfbuf)
462 {
463 /* This function does the reverse of utf8_sequence(). */
464 switch (calc_unival_length(utfbuf))
465 {
466 case 1:
467 default:
468 return ((utfbuf[0] & 127));
469 case 2:
470 return ((utfbuf[0] & 31) << 6)
471 | ((utfbuf[1] & 63));
472 case 3:
473 return ((utfbuf[0] & 15) << 12)
474 | ((utfbuf[1] & 63) << 6)
475 | ((utfbuf[2] & 63));
476 case 4:
477 return ((utfbuf[0] & 7) << 16)
478 | ((utfbuf[1] & 63) << 12)
479 | ((utfbuf[2] & 63) << 6)
480 | ((utfbuf[3] & 63));
481 }
482 }
483
484 /*
485 * This function does the reverse of calc_unival().
486 *
487 * The output buffer should have 5 bytes of space.
488 */
489 void
utf8_sequence(unsigned unival,u_char * utfbuf)490 utf8_sequence(unsigned unival, u_char *utfbuf)
491 {
492 u_char *utfptr = utfbuf;
493 if (unival < 0x80) /* <=7 bits */
494 *utfptr++ = (u_char)unival;
495 else
496 {
497 if (unival < 0x800) /* <=11 bits */
498 *utfptr++ = (u_char)(0xC0 + (unival>>6));
499 else
500 {
501 if (unival < 0x10000) /* <=16 bits */
502 *utfptr++ = (u_char)(0xE0 + (unival>>12));
503 else /* <=21 bits */
504 {
505 *utfptr++ = (u_char)(0xF0 + (unival>>18));
506 *utfptr++ = (u_char)(0x80 + ((unival>>12)&63));
507 }
508 *utfptr++ = (u_char)(0x80 + ((unival>>6)&63));
509 }
510 *utfptr++ = (u_char)(0x80 + (unival&63));
511 }
512 /* Last put a zero-terminator. */
513 *utfptr = '\0';
514 /*
515 fprintf(stderr, "utf8-seq %X: %02X %02X (%s)\n",
516 unival, utfbuf[0], utfbuf[1], utfbuf);
517 */
518 }
519
520 void
mbdata_init(struct mb_data * d,const char * enc)521 mbdata_init(struct mb_data *d, const char *enc)
522 {
523 memset(d, 0, sizeof(*d));
524
525 #ifdef HAVE_ICONV_OPEN
526 d->enc = enc;
527 if (!d->conv_in && !d->conv_out && d->enc && current_display_encoding())
528 {
529 /* New encoding, reinitialize converters */
530
531 if (!d->conv_in)
532 d->conv_in = iconv_open("UTF-8", d->enc);
533 if (!d->conv_out)
534 d->conv_out = iconv_open(CP(current_display_encoding()), "UTF-8");
535
536 if (d->conv_in == (iconv_t)(-1))
537 {
538 iconv_close(d->conv_in);
539 d->conv_in = NULL;
540 }
541 if (d->conv_out == (iconv_t)(-1))
542 {
543 iconv_close(d->conv_out);
544 d->conv_out = NULL;
545 }
546 }
547 #endif /* HAVE_ICONV_OPEN */
548 }
549
550 void
mbdata_done(struct mb_data * d)551 mbdata_done(struct mb_data* d)
552 {
553 #ifdef HAVE_ICONV_OPEN
554 if (d->conv_in)
555 iconv_close(d->conv_in);
556 if (d->conv_out)
557 iconv_close(d->conv_out);
558 #endif /* HAVE_ICONV_OPEN */
559 memset(d, 0, sizeof(*d));
560 }
561
562 /*
563 * decode_mb() - decode a multibyte sequence.
564 *
565 * ptr - Source, encoded in whatever
566 * dest - Target, encoded in utf-8 - NULL is allowed
567 * destlen - Length of dest buffer
568 * data - Populated with data
569 */
570 void
decode_mb(u_char * ptr,u_char * dest,size_t destlen,mb_data * data)571 decode_mb(u_char *ptr, u_char *dest, size_t destlen, mb_data *data)
572 {
573 #ifdef HAVE_ICONV_OPEN
574 /* If iconv has now been initialized, use it. */
575 if (data->conv_in && data->conv_out)
576 {
577 /*
578 * Task:
579 * Eat input byte by byte
580 * Until either
581 * - the input is exhausted
582 * - conv_in creates a character
583 * When conv_in creates a character,
584 * - feed the character to conv_out
585 * - if conv_out says dame desu yo
586 * - we have an invalid character
587 * - otherwise, analyze the unicode value
588 * - For values 0000..001F: add 40, invert (invalid)
589 * - For values 0080..009F: dec 40, invert (invalid)
590 * - For values 3000..FEFF: (CJK) width=2
591 */
592 u_char utfbuf[8], *utfptr = utfbuf;
593 size_t utfspace = sizeof(utfbuf);
594 unsigned unival;
595 int error = 0;
596 size_t retval = 0;
597
598 data->input_bytes = 0;
599 data->output_bytes = 0;
600 data->num_columns = 0;
601
602 *utfptr = '\0';
603
604 while (*ptr != '\0')
605 {
606 unsigned gave;
607 size_t is = 1;
608
609 retry:
610 gave = is;
611 retval = iconv(data->conv_in,
612 (iconv_const char**)(void *)&ptr, &is,
613 (char **)(void *)&utfptr, &utfspace);
614
615 data->input_bytes += gave-is;
616
617 if (retval == (size_t)-1)
618 {
619 switch (errno)
620 {
621 case EINVAL:
622 /* We didn't give enough bytes. Must give more */
623 is = gave;
624 if (ptr[is] != '\0')
625 {
626 is++;
627 goto retry;
628 }
629 /* It's an undecodable input. */
630 error = 1;
631 data->input_bytes = 1;
632 ptr++;
633 goto endloop;
634 case EILSEQ:
635 if (*ptr != '\0')
636 {
637 ptr++;
638 data->input_bytes++;
639 // add some indication '?' char?
640 }
641 /* Ignore invalid byte, continue loop. */
642 error = 1;
643 continue;
644 default:
645 error = 1;
646 goto endloop;
647 }
648 }
649
650 if (utfptr > utfbuf)
651 {
652 /* An UTF-8 character was created! */
653 data->output_bytes += utfptr - utfbuf;
654 *utfptr = '\0';
655 endloop:
656 break;
657 }
658 break;
659 }
660
661 if (data->output_bytes == 0 && !error)
662 {
663 /* Nothing was produced, no errors. */
664 return;
665 }
666
667 unival = 0;
668
669 if (data->output_bytes > 0)
670 {
671 /* Calculate the unicode value of the utf8 character */
672 unival = calc_unival(utfbuf);
673 }
674
675 if (!displayable_unival(unival, data->conv_out))
676 {
677 /* The character could not be expressed in display encoding
678 * or would be a control character
679 */
680 data->num_columns = data->input_bytes;
681 data->output_bytes = data->input_bytes;
682 if (data->output_bytes > 0)
683 data->output_bytes += 2;
684 if (dest)
685 {
686 unsigned n = data->input_bytes;
687 if (n > 0)
688 {
689 ptr -= n;
690 *dest++ = REV_TOG;
691 destlen--;
692 /* we assume ascii always works */
693 while (n-- > 0)
694 {
695 *dest++ = (*ptr++ & 127) | 64;
696 destlen--;
697 }
698 *dest++ = REV_TOG;
699 destlen--;
700 }
701 }
702 return;
703 }
704
705 data->num_columns = calc_unival_width(unival);
706
707 if (dest)
708 {
709 memcpy(dest, utfbuf, data->output_bytes);
710 destlen -= data->output_bytes;
711 }
712 return;
713 }
714 #endif /* HAVE_ICONV_OPEN */
715 /* No usable iconv (maybe csets were invalid), assume ISO-8859-1 in */
716 data->input_bytes = 1;
717 data->num_columns = 1;
718
719 if (!displayable_unival(*ptr, NULL))
720 {
721 data->output_bytes = 3;
722 if (dest)
723 {
724 *dest++ = REV_TOG;
725 *dest++ = (*ptr & 127) | 64;
726 *dest++ = REV_TOG;
727 destlen -= 3;
728 }
729 }
730 else
731 {
732 unsigned unival = *ptr;
733
734 if (unival < 0x80)
735 data->output_bytes = 1;
736 else if (unival < 0x800)
737 data->output_bytes = 2;
738 else if (unival < 0x10000)
739 data->output_bytes = 3;
740 else
741 data->output_bytes = 4;
742 if (dest && destlen >= 5)
743 utf8_sequence(unival, dest);
744 }
745 }
746
747 void
set_irc_encoding(u_char * enc)748 set_irc_encoding(u_char *enc)
749 {
750 #ifdef HAVE_ICONV_OPEN
751 iconv_t test;
752
753 if (!enc) {
754 malloc_strcpy(&irc_encoding, UP("ISO-8859-1"));
755 return;
756 }
757
758 test = iconv_open("UTF-8", CP(enc));
759 if (test != NULL && test != (iconv_t)(-1))
760 {
761 iconv_close(test);
762 malloc_strcpy(&irc_encoding, enc);
763 }
764 else
765 say("IRC_ENCODING value %s is not supported by this system", enc);
766 #else
767 say("IRC_ENCODING has no effect - this version was compiled without iconv support");
768 #endif /* HAVE_ICONV_OPEN */
769 }
770
771 void
set_display_encoding(u_char * enc)772 set_display_encoding(u_char *enc)
773 {
774 #ifdef HAVE_ICONV_OPEN
775 iconv_t test;
776
777 if (!enc) {
778 malloc_strcpy(&display_encoding, UP("ISO-8859-1"));
779 return;
780 }
781
782 test = iconv_open(CP(enc), "UTF-8");
783 if (test != NULL && test != (iconv_t)(-1))
784 {
785 iconv_close(test);
786 malloc_strcpy(&display_encoding, enc);
787 }
788 else
789 say("DISPLAY_ENCODING value %s is not supported by this system", enc);
790 #else
791 say("DISPLAY_ENCODING has no effect - this version was compiled without iconv support");
792 #endif /* HAVE_ICONV_OPEN */
793 }
794
795 void
set_input_encoding(u_char * enc)796 set_input_encoding(u_char *enc)
797 {
798 #ifdef HAVE_ICONV_OPEN
799 iconv_t test;
800
801 if (!enc) {
802 malloc_strcpy(&input_encoding, UP("ISO-8859-1"));
803 return;
804 }
805
806 test = iconv_open("UTF-8", CP(enc));
807
808 if (test != NULL && test != (iconv_t)(-1))
809 {
810 iconv_close(test);
811 malloc_strcpy(&input_encoding, enc);
812 }
813 else
814 say("INPUT_ENCODING value %s is not supported by this system", enc);
815 #else
816 say("INPUT_ENCODING has no effect - this version was compiled without iconv support");
817 #endif /* HAVE_ICONV_OPEN */
818 }
819
820 #ifdef HAVE_ICONV_OPEN
821 u_char *
current_irc_encoding(void)822 current_irc_encoding(void)
823 {
824 return irc_encoding;
825 }
826
827 u_char *
current_display_encoding(void)828 current_display_encoding(void)
829 {
830 return display_encoding;
831 }
832
833 u_char *
current_input_encoding(void)834 current_input_encoding(void)
835 {
836 return input_encoding;
837 }
838 #endif /* HAVE_ICONV_OPEN */
839