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