summaryrefslogtreecommitdiff
path: root/cmd/wmii/utf.c
blob: 48e2a6da2ff7bfe06203260b9787116ffd96860f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
/* Public Domain --Kris Maglione */
#include "dat.h"
#include <errno.h>
#include <iconv.h>
#include <langinfo.h>
#include <string.h>
#include "fns.h"

char*
toutf8n(const char *str, size_t nstr) {
	static iconv_t cd;
	static bool haveiconv;
	char *buf, *pos;
	size_t nbuf, bsize;

	if(cd == nil) {
		cd = iconv_open("UTF-8", nl_langinfo(CODESET));
		if((long)cd == -1)
			warning("Can't convert from local character encoding to UTF-8");
		else
			haveiconv = true;
	}
	if(!haveiconv) {
		buf = emalloc(nstr+1);
		memcpy(buf, str, nstr);
		buf[nstr+1] = '\0';
		return buf;
	}

	iconv(cd, nil, nil, nil, nil);

	bsize = (nstr+1) << 1;
	buf = emalloc(bsize);
	pos = buf;
	nbuf = bsize-1;
	/* The (void*) cast is because, while the BSDs declare:
	 * size_t iconv(iconv_t, const char**, size_t*, char**, size_t*),
	 * GNU/Linux and POSIX declare:
	 * size_t iconv(iconv_t, char**, size_t*, char**, size_t*).
	 * This just happens to be safer than declaring our own
	 * prototype.
	 */
	while(iconv(cd, (void*)&str, &nstr, &pos, &nbuf) == -1)
		if(errno == E2BIG) {
			bsize <<= 1;
			nbuf = pos - buf;
			buf = erealloc(buf, bsize);
			pos = buf + nbuf;
			nbuf = bsize - nbuf - 1;
		}else
			break;
	*pos++ = '\0';
	return erealloc(buf, pos-buf);
}

char*
toutf8(const char *str) {
	return toutf8n(str, strlen(str));
}