Add note about reboot before 'make upgrade' step.
[dragonfly.git] / contrib / nvi / common / conv.c
blob7803cec9922e0c789037a6ae33a057b4d6ca23da
1 /*-
2 * Copyright (c) 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 * Copyright (c) 1993, 1994, 1995, 1996
5 * Keith Bostic. All rights reserved.
6 * Copyright (c) 2011, 2012
7 * Zhihao Yuan. All rights reserved.
9 * See the LICENSE file for redistribution information.
12 #include "config.h"
14 #ifndef lint
15 static const char sccsid[] = "$Id: conv.c,v 2.39 2013/07/01 23:28:13 zy Exp $";
16 #endif /* not lint */
18 #include <sys/types.h>
19 #include <sys/queue.h>
20 #include <sys/time.h>
22 #include <bitstring.h>
23 #include <errno.h>
24 #include <limits.h>
25 #include <langinfo.h>
26 #include <locale.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <strings.h>
31 #include <unistd.h>
33 #include "common.h"
36 * codeset --
37 * Get the locale encoding.
39 * PUBLIC: char * codeset __P((void));
41 char *
42 codeset(void) {
43 static char *cs;
45 if (cs == NULL)
46 cs = nl_langinfo(CODESET);
47 return cs;
50 #ifdef USE_WIDECHAR
51 static int
52 raw2int(SCR *sp, const char * str, ssize_t len, CONVWIN *cw,
53 size_t *tolen, CHAR_T **dst)
55 int i;
56 CHAR_T **tostr = &cw->bp1.wc;
57 size_t *blen = &cw->blen1;
59 BINC_RETW(NULL, *tostr, *blen, len);
61 *tolen = len;
62 for (i = 0; i < len; ++i)
63 (*tostr)[i] = (u_char) str[i];
65 *dst = cw->bp1.wc;
67 return 0;
70 #define CONV_BUFFER_SIZE 512
71 /* fill the buffer with codeset encoding of string pointed to by str
72 * left has the number of bytes left in str and is adjusted
73 * len contains the number of bytes put in the buffer
75 #ifdef USE_ICONV
76 #define CONVERT(str, left, src, len) \
77 do { \
78 size_t outleft; \
79 char *bp = buffer; \
80 outleft = CONV_BUFFER_SIZE; \
81 errno = 0; \
82 if (iconv(id, (iconv_src_t)&str, &left, &bp, &outleft) == -1 && \
83 errno != E2BIG) \
84 goto err; \
85 if ((len = CONV_BUFFER_SIZE - outleft) == 0) { \
86 error = -left; \
87 goto err; \
88 } \
89 src = buffer; \
90 } while (0)
92 #define IC_RESET() \
93 do { \
94 if (id != (iconv_t)-1) \
95 iconv(id, NULL, NULL, NULL, NULL); \
96 } while(0)
97 #else
98 #define CONVERT(str, left, src, len)
99 #define IC_RESET()
100 #endif
102 static int
103 default_char2int(SCR *sp, const char * str, ssize_t len, CONVWIN *cw,
104 size_t *tolen, CHAR_T **dst, iconv_t id)
106 size_t i = 0, j;
107 CHAR_T **tostr = &cw->bp1.wc;
108 size_t *blen = &cw->blen1;
109 mbstate_t mbs;
110 size_t n;
111 ssize_t nlen = len;
112 char *src = (char *)str;
113 #ifdef USE_ICONV
114 char buffer[CONV_BUFFER_SIZE];
115 #endif
116 size_t left = len;
117 int error = 1;
119 BZERO(&mbs, 1);
120 BINC_RETW(NULL, *tostr, *blen, nlen);
122 #ifdef USE_ICONV
123 if (id != (iconv_t)-1)
124 CONVERT(str, left, src, len);
125 #endif
127 for (i = 0, j = 0; j < len; ) {
128 n = mbrtowc((*tostr)+i, src+j, len-j, &mbs);
129 /* NULL character converted */
130 if (n == -2) error = -(len-j);
131 if (n == -1 || n == -2) goto err;
132 if (n == 0) n = 1;
133 j += n;
134 if (++i >= *blen) {
135 nlen += 256;
136 BINC_RETW(NULL, *tostr, *blen, nlen);
138 if (id != (iconv_t)-1 && j == len && left) {
139 CONVERT(str, left, src, len);
140 j = 0;
144 error = 0;
145 err:
146 *tolen = i;
147 *dst = cw->bp1.wc;
148 IC_RESET();
150 return error;
153 static int
154 fe_char2int(SCR *sp, const char * str, ssize_t len, CONVWIN *cw,
155 size_t *tolen, CHAR_T **dst)
157 return default_char2int(sp, str, len, cw, tolen, dst,
158 sp->conv.id[IC_FE_CHAR2INT]);
161 static int
162 ie_char2int(SCR *sp, const char * str, ssize_t len, CONVWIN *cw,
163 size_t *tolen, CHAR_T **dst)
165 return default_char2int(sp, str, len, cw, tolen, dst,
166 sp->conv.id[IC_IE_CHAR2INT]);
169 static int
170 cs_char2int(SCR *sp, const char * str, ssize_t len, CONVWIN *cw,
171 size_t *tolen, CHAR_T **dst)
173 return default_char2int(sp, str, len, cw, tolen, dst,
174 (iconv_t)-1);
177 static int
178 int2raw(SCR *sp, const CHAR_T * str, ssize_t len, CONVWIN *cw,
179 size_t *tolen, char **dst)
181 int i;
182 char **tostr = &cw->bp1.c;
183 size_t *blen = &cw->blen1;
185 BINC_RETC(NULL, *tostr, *blen, len);
187 *tolen = len;
188 for (i = 0; i < len; ++i)
189 (*tostr)[i] = str[i];
191 *dst = cw->bp1.c;
193 return 0;
196 static int
197 default_int2char(SCR *sp, const CHAR_T * str, ssize_t len, CONVWIN *cw,
198 size_t *tolen, char **pdst, iconv_t id)
200 size_t i, j, offset = 0;
201 char **tostr = &cw->bp1.c;
202 size_t *blen = &cw->blen1;
203 mbstate_t mbs;
204 size_t n;
205 ssize_t nlen = len + MB_CUR_MAX;
206 char *dst;
207 size_t buflen;
208 #ifdef USE_ICONV
209 char buffer[CONV_BUFFER_SIZE];
210 #endif
211 int error = 1;
213 /* convert first len bytes of buffer and append it to cw->bp
214 * len is adjusted => 0
215 * offset contains the offset in cw->bp and is adjusted
216 * cw->bp is grown as required
218 #ifdef USE_ICONV
219 #define CONVERT2(_buffer, lenp, cw, offset) \
220 do { \
221 char *bp = _buffer; \
222 int ret; \
223 do { \
224 size_t outleft = cw->blen1 - offset; \
225 char *obp = cw->bp1.c + offset; \
226 if (cw->blen1 < offset + MB_CUR_MAX) { \
227 nlen += 256; \
228 BINC_RETC(NULL, cw->bp1.c, cw->blen1, nlen); \
230 errno = 0; \
231 ret = iconv(id, (iconv_src_t)&bp, lenp, &obp, &outleft); \
232 if (ret == -1 && errno != E2BIG) \
233 goto err; \
234 offset = cw->blen1 - outleft; \
235 } while (ret != 0); \
236 } while (0)
237 #else
238 #define CONVERT2(_buffer, lenp, cw, offset)
239 #endif
242 BZERO(&mbs, 1);
243 BINC_RETC(NULL, *tostr, *blen, nlen);
244 dst = *tostr; buflen = *blen;
246 #ifdef USE_ICONV
247 if (id != (iconv_t)-1) {
248 dst = buffer; buflen = CONV_BUFFER_SIZE;
250 #endif
252 for (i = 0, j = 0; i < len; ++i) {
253 n = wcrtomb(dst+j, str[i], &mbs);
254 if (n == -1) goto err;
255 j += n;
256 if (buflen < j + MB_CUR_MAX) {
257 if (id != (iconv_t)-1) {
258 CONVERT2(buffer, &j, cw, offset);
259 } else {
260 nlen += 256;
261 BINC_RETC(NULL, *tostr, *blen, nlen);
262 dst = *tostr; buflen = *blen;
267 n = wcrtomb(dst+j, L'\0', &mbs);
268 j += n - 1; /* don't count NUL at the end */
269 *tolen = j;
271 if (id != (iconv_t)-1) {
272 CONVERT2(buffer, &j, cw, offset);
273 CONVERT2(NULL, NULL, cw, offset); /* back to the initial state */
274 *tolen = offset;
277 error = 0;
278 err:
279 if (error)
280 *tolen = j;
281 *pdst = cw->bp1.c;
282 IC_RESET();
284 return error;
287 static int
288 fe_int2char(SCR *sp, const CHAR_T * str, ssize_t len, CONVWIN *cw,
289 size_t *tolen, char **dst)
291 return default_int2char(sp, str, len, cw, tolen, dst,
292 sp->conv.id[IC_FE_INT2CHAR]);
295 static int
296 cs_int2char(SCR *sp, const CHAR_T * str, ssize_t len, CONVWIN *cw,
297 size_t *tolen, char **dst)
299 return default_int2char(sp, str, len, cw, tolen, dst,
300 (iconv_t)-1);
303 #endif
306 * conv_init --
307 * Initialize the iconv environment.
309 * PUBLIC: void conv_init __P((SCR *, SCR *));
311 void
312 conv_init(SCR *orig, SCR *sp)
314 int i;
316 if (orig == NULL)
317 setlocale(LC_ALL, "");
318 if (orig != NULL)
319 BCOPY(&orig->conv, &sp->conv, 1);
320 #ifdef USE_WIDECHAR
321 else {
322 char *ctype = setlocale(LC_CTYPE, NULL);
325 * XXX
326 * This hack fixes the libncursesw issue on FreeBSD.
328 if (!strcmp(ctype, "ko_KR.CP949"))
329 setlocale(LC_CTYPE, "ko_KR.eucKR");
330 else if (!strcmp(ctype, "zh_CN.GB2312"))
331 setlocale(LC_CTYPE, "zh_CN.eucCN");
332 else if (!strcmp(ctype, "zh_CN.GBK"))
333 setlocale(LC_CTYPE, "zh_CN.GB18030");
336 * Switch to 8bit mode if locale is C;
337 * LC_CTYPE should be reseted to C if unmatched.
339 if (!strcmp(ctype, "C") || !strcmp(ctype, "POSIX")) {
340 sp->conv.sys2int = sp->conv.file2int = raw2int;
341 sp->conv.int2sys = sp->conv.int2file = int2raw;
342 sp->conv.input2int = raw2int;
343 } else {
344 sp->conv.sys2int = cs_char2int;
345 sp->conv.int2sys = cs_int2char;
346 sp->conv.file2int = fe_char2int;
347 sp->conv.int2file = fe_int2char;
348 sp->conv.input2int = ie_char2int;
350 #ifdef USE_ICONV
351 o_set(sp, O_INPUTENCODING, OS_STRDUP, codeset(), 0);
352 #endif
354 #endif
356 /* iconv descriptors must be distinct to screens. */
357 for (i = 0; i <= IC_IE_TO_UTF16; ++i)
358 sp->conv.id[i] = (iconv_t)-1;
359 #ifdef USE_ICONV
360 conv_enc(sp, O_INPUTENCODING, 0);
361 #endif
365 * conv_enc --
366 * Convert file/input encoding.
368 * PUBLIC: int conv_enc __P((SCR *, int, char *));
371 conv_enc(SCR *sp, int option, char *enc)
373 #if defined(USE_WIDECHAR) && defined(USE_ICONV)
374 iconv_t *c2w, *w2c;
376 switch (option) {
377 case O_FILEENCODING:
378 c2w = sp->conv.id + IC_FE_CHAR2INT;
379 w2c = sp->conv.id + IC_FE_INT2CHAR;
380 if (!enc) enc = O_STR(sp, O_FILEENCODING);
381 if (*c2w != (iconv_t)-1)
382 iconv_close(*c2w);
383 if (*w2c != (iconv_t)-1)
384 iconv_close(*w2c);
385 if (strcasecmp(codeset(), enc)) {
386 if ((*c2w = iconv_open(codeset(), enc)) == (iconv_t)-1)
387 goto err;
388 if ((*w2c = iconv_open(enc, codeset())) == (iconv_t)-1)
389 goto err;
390 } else *c2w = *w2c = (iconv_t)-1;
391 break;
392 case O_INPUTENCODING:
393 c2w = sp->conv.id + IC_IE_CHAR2INT;
394 w2c = sp->conv.id + IC_IE_TO_UTF16;
395 if (!enc) enc = O_STR(sp, O_INPUTENCODING);
396 if (*c2w != (iconv_t)-1)
397 iconv_close(*c2w);
398 if (*w2c != (iconv_t)-1)
399 iconv_close(*w2c);
400 if (strcasecmp(codeset(), enc)) {
401 if ((*c2w = iconv_open(codeset(), enc)) == (iconv_t)-1)
402 goto err;
403 } else *c2w = (iconv_t)-1;
404 /* UTF-16 can not be locale and can not be inputed. */
405 if ((*w2c = iconv_open("utf-16be", enc)) == (iconv_t)-1)
406 goto err;
407 break;
410 F_CLR(sp, SC_CONV_ERROR);
411 F_SET(sp, SC_SCR_REFORMAT);
413 return 0;
414 err:
415 #endif
416 switch (option) {
417 case O_FILEENCODING:
418 msgq(sp, M_ERR,
419 "321|File encoding conversion not supported");
420 break;
421 case O_INPUTENCODING:
422 msgq(sp, M_ERR,
423 "322|Input encoding conversion not supported");
424 break;
426 return 1;
430 * conv_end --
431 * Close the iconv descriptors, release the buffer.
433 * PUBLIC: void conv_end __P((SCR *));
435 void
436 conv_end(SCR *sp)
438 #if defined(USE_WIDECHAR) && defined(USE_ICONV)
439 int i;
440 for (i = 0; i <= IC_IE_TO_UTF16; ++i)
441 if (sp->conv.id[i] != (iconv_t)-1)
442 iconv_close(sp->conv.id[i]);
443 if (sp->cw.bp1.c != NULL)
444 free(sp->cw.bp1.c);
445 #endif