about summary refs log tree commit diff
path: root/src/multibyte
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2011-02-12 00:22:29 -0500
committerRich Felker <dalias@aerifal.cx>2011-02-12 00:22:29 -0500
commit0b44a0315b47dd8eced9f3b7f31580cf14bbfc01 (patch)
tree6eaef0d8a720fa3da580de87b647fff796fe80b3 /src/multibyte
downloadmusl-0b44a0315b47dd8eced9f3b7f31580cf14bbfc01.tar.gz
musl-0b44a0315b47dd8eced9f3b7f31580cf14bbfc01.tar.xz
musl-0b44a0315b47dd8eced9f3b7f31580cf14bbfc01.zip
initial check-in, version 0.5.0 v0.5.0
Diffstat (limited to 'src/multibyte')
-rw-r--r--src/multibyte/btowc.c7
-rw-r--r--src/multibyte/decode.c47
-rw-r--r--src/multibyte/internal.c60
-rw-r--r--src/multibyte/internal.h61
-rw-r--r--src/multibyte/mblen.c17
-rw-r--r--src/multibyte/mbrlen.c18
-rw-r--r--src/multibyte/mbrtowc.c58
-rw-r--r--src/multibyte/mbsinit.c17
-rw-r--r--src/multibyte/mbsnrtowcs.c61
-rw-r--r--src/multibyte/mbsrtowcs.c121
-rw-r--r--src/multibyte/mbstowcs.c18
-rw-r--r--src/multibyte/mbtowc.c19
-rw-r--r--src/multibyte/wcrtomb.c38
-rw-r--r--src/multibyte/wcsnrtombs.c51
-rw-r--r--src/multibyte/wcsrtombs.c58
-rw-r--r--src/multibyte/wcstombs.c17
-rw-r--r--src/multibyte/wctob.c8
-rw-r--r--src/multibyte/wctomb.c18
18 files changed, 694 insertions, 0 deletions
diff --git a/src/multibyte/btowc.c b/src/multibyte/btowc.c
new file mode 100644
index 00000000..9d2c3b16
--- /dev/null
+++ b/src/multibyte/btowc.c
@@ -0,0 +1,7 @@
+#include <stdio.h>
+#include <wchar.h>
+
+wint_t btowc(int c)
+{
+	return c<128U ? c : EOF;
+}
diff --git a/src/multibyte/decode.c b/src/multibyte/decode.c
new file mode 100644
index 00000000..8d3d3c0b
--- /dev/null
+++ b/src/multibyte/decode.c
@@ -0,0 +1,47 @@
+/* 
+ * This code was written by Rich Felker in 2010; no copyright is claimed.
+ * This code is in the public domain. Attribution is appreciated but
+ * unnecessary.
+ */
+
+#include <stdlib.h>
+#include <inttypes.h>
+#include <wchar.h>
+#include <errno.h>
+
+#include "internal.h"
+
+/* Decodes UTF-8 byte-by-byte. The c argument must be initialized to 0
+ * to begin decoding; when finished it will contain the Unicode scalar
+ * value decoded. Return value is 1 if finished, 0 if in-progress, and
+ * -1 if an invalid sequence was encountered. After an invalid sequence,
+ * the state (in c) automatically resets to 0 if a continuation byte was
+ * expected to facilitate a calling idiom of immediately retrying a
+ * failed decode call after processing the invalid sequence. If the
+ * second try fails, the byte is invalid as a starter as well.
+ *
+ * A trivial usage idiom is:
+ *       while (src<end && (n=decode(dst, *src))>=0) 1[dst+=n]=0, src++;
+ */
+
+int decode(unsigned *c, unsigned b)
+{
+	if (!*c) {
+		if (b < 0x80) {
+			*c = b;
+			return 1;
+		} else if (b-SA >= SB-SA) {
+			*c = FAILSTATE;
+			return -1;
+		}
+		*c = bittab[b-SA];
+		return 0;
+	}
+
+	if (OOB(*c,b)) {
+		*c = 0;
+		return -1;
+	}
+	*c = *c<<6 | b-0x80;
+	return !(*c&(1U<<31));
+}
diff --git a/src/multibyte/internal.c b/src/multibyte/internal.c
new file mode 100644
index 00000000..e9b938dd
--- /dev/null
+++ b/src/multibyte/internal.c
@@ -0,0 +1,60 @@
+/* 
+ * This code was written by Rich Felker in 2010; no copyright is claimed.
+ * This code is in the public domain. Attribution is appreciated but
+ * unnecessary.
+ */
+
+#include <inttypes.h>
+
+#include "internal.h"
+
+#define C(x) ( x<2 ? -1 : ( R(0x80,0xc0) | x ) )
+#define D(x) C((x+16))
+#define E(x) ( ( x==0 ? R(0xa0,0xc0) : \
+                 x==0xd ? R(0x80,0xa0) : \
+                 R(0x80,0xc0) ) \
+             | ( R(0x80,0xc0) >> 6 ) \
+             | x )
+#ifdef I_FAILED_TO_RTFM_RFC3629
+#define F0(x) (( x==0 ? R(0x90,0xc0) : \
+                 R(0x80,0xc0) ) \
+             | ( R(0x80,0xc0) >> 6 ) \
+             | ( R(0x80,0xc0) >> 12 ) \
+             | x )
+#define F8(x) (( x==0 ? R(0xa0,0xc0) : \
+                 R(0x80,0xc0) ) \
+             | ( R(0x80,0xc0) >> 6 ) \
+             | ( R(0x80,0xc0) >> 12 ) \
+             | ( R(0x80,0xc0) >> 18 ) \
+             | x )
+#define FC(x) (( x==0 ? R(0x88,0xc0) : \
+                 R(0x80,0xc0) ) \
+             | ( R(0x80,0xc0) >> 6 ) \
+             | ( R(0x80,0xc0) >> 12 ) \
+             | ( R(0x80,0xc0) >> 18 ) \
+             | ( R(0x80,0xc0) >> 24 ) \
+             | x )
+#define F(x) ( x<8 ? F0(x) : x<12 ? F8((x&3)) : x<14 ? FC((x&1)) : -1 )
+#else
+#define F(x) ( ( x>=5 ? 0 : \
+                 x==0 ? R(0x90,0xc0) : \
+                 x==4 ? R(0x80,0xa0) : \
+                 R(0x80,0xc0) ) \
+             | ( R(0x80,0xc0) >> 6 ) \
+             | ( R(0x80,0xc0) >> 12 ) \
+             | x )
+#endif
+
+const uint32_t bittab[] = {
+	              C(0x2),C(0x3),C(0x4),C(0x5),C(0x6),C(0x7),
+	C(0x8),C(0x9),C(0xa),C(0xb),C(0xc),C(0xd),C(0xe),C(0xf),
+	D(0x0),D(0x1),D(0x2),D(0x3),D(0x4),D(0x5),D(0x6),D(0x7),
+	D(0x8),D(0x9),D(0xa),D(0xb),D(0xc),D(0xd),D(0xe),D(0xf),
+	E(0x0),E(0x1),E(0x2),E(0x3),E(0x4),E(0x5),E(0x6),E(0x7),
+	E(0x8),E(0x9),E(0xa),E(0xb),E(0xc),E(0xd),E(0xe),E(0xf),
+	F(0x0),F(0x1),F(0x2),F(0x3),F(0x4),
+#ifdef I_FAILED_TO_RTFM_RFC3629
+	                                   F(0x5),F(0x6),F(0x7),
+	F(0x8),F(0x9),F(0xa),F(0xb),F(0xc),F(0xd)
+#endif
+};
diff --git a/src/multibyte/internal.h b/src/multibyte/internal.h
new file mode 100644
index 00000000..427519a2
--- /dev/null
+++ b/src/multibyte/internal.h
@@ -0,0 +1,61 @@
+/* 
+ * This code was written by Rich Felker in 2010; no copyright is claimed.
+ * This code is in the public domain. Attribution is appreciated but
+ * unnecessary.
+ */
+
+#define LIBC
+#ifndef LIBC
+/* rename functions not to conflict with libc */
+#ifndef myprefix
+#define myprefix fsmu8_
+#endif
+#define concat2(a,b) a ## b
+#define concat(a,b) concat2(a,b)
+#define prefix(b) concat(myprefix,b)
+
+#undef mblen
+#undef mbrlen
+#undef mbrtowc
+#undef mbsinit
+#undef mbsnrtowcs
+#undef mbsrtowcs
+#undef wcrtomb
+#undef wcsrtombs
+#undef wcstombs
+#undef wctomb
+#define mblen prefix(mblen)
+#define mbrlen prefix(mbrlen)
+#define mbrtowc prefix(mbrtowc)
+#define mbsinit prefix(mbsinit)
+#define mbsnrtowcs prefix(mbsnrtowcs)
+#define mbsrtowcs prefix(mbsrtowcs)
+#define mbstowcs prefix(mbstowcs)
+#define wcrtomb prefix(wcrtomb)
+#define wcsnrtombs prefix(wcsnrtombs)
+#define wcsrtombs prefix(wcsrtombs)
+#define wcstombs prefix(wcstombs)
+#define wctomb prefix(wctomb)
+
+#define bittab prefix(bittab)
+#else
+#define bittab __fsmu8
+#endif
+
+extern const uint32_t bittab[];
+
+/* Upper 6 state bits are a negative integer offset to bound-check next byte */
+/*    equivalent to: ( (b-0x80) | (b+offset) ) & ~0x3f      */
+#define OOB(c,b) (((((b)>>3)-0x10)|(((b)>>3)+((int32_t)(c)>>26))) & ~7)
+
+/* Interval [a,b). Either a must be 80 or b must be c0, lower 3 bits clear. */
+#define R(a,b) ((uint32_t)((a==0x80 ? 0x40-b : -a) << 23))
+#define FAILSTATE R(0x80,0x80)
+
+#ifdef I_FAILED_TO_RTFM_RFC3629
+#define SA 0xc2
+#define SB 0xfe
+#else
+#define SA 0xc2
+#define SB 0xf5
+#endif
diff --git a/src/multibyte/mblen.c b/src/multibyte/mblen.c
new file mode 100644
index 00000000..26d35649
--- /dev/null
+++ b/src/multibyte/mblen.c
@@ -0,0 +1,17 @@
+/* 
+ * This code was written by Rich Felker in 2010; no copyright is claimed.
+ * This code is in the public domain. Attribution is appreciated but
+ * unnecessary.
+ */
+
+#include <stdlib.h>
+#include <inttypes.h>
+#include <wchar.h>
+#include <errno.h>
+
+#include "internal.h"
+
+int mblen(const char *s, size_t n)
+{
+	return mbtowc(0, s, n);
+}
diff --git a/src/multibyte/mbrlen.c b/src/multibyte/mbrlen.c
new file mode 100644
index 00000000..c9a9f033
--- /dev/null
+++ b/src/multibyte/mbrlen.c
@@ -0,0 +1,18 @@
+/* 
+ * This code was written by Rich Felker in 2010; no copyright is claimed.
+ * This code is in the public domain. Attribution is appreciated but
+ * unnecessary.
+ */
+
+#include <stdlib.h>
+#include <inttypes.h>
+#include <wchar.h>
+#include <errno.h>
+
+#include "internal.h"
+
+size_t mbrlen(const char *s, size_t n, mbstate_t *st)
+{
+	static unsigned internal;
+	return mbrtowc(0, s, n, st ? st : (mbstate_t *)&internal);
+}
diff --git a/src/multibyte/mbrtowc.c b/src/multibyte/mbrtowc.c
new file mode 100644
index 00000000..09badebe
--- /dev/null
+++ b/src/multibyte/mbrtowc.c
@@ -0,0 +1,58 @@
+/* 
+ * This code was written by Rich Felker in 2010; no copyright is claimed.
+ * This code is in the public domain. Attribution is appreciated but
+ * unnecessary.
+ */
+
+#include <stdlib.h>
+#include <inttypes.h>
+#include <wchar.h>
+#include <errno.h>
+
+#include "internal.h"
+
+size_t mbrtowc(wchar_t *wc, const char *src, size_t n, mbstate_t *st)
+{
+	static unsigned internal_state;
+	unsigned c;
+	const unsigned char *s = src;
+	const unsigned N = n;
+
+	if (!st) st = (void *)&internal_state;
+	c = *(unsigned *)st;
+	
+	if (!s) {
+		s = "";
+		wc = (void *)&wc;
+		n = 1;
+	} else if (!wc) wc = (void *)&wc;
+
+	if (!n) return -2;
+	if (!c) {
+		if ((unsigned)*s < 0x80) return !!(*wc = *s);
+		if ((unsigned)*s-SA > SB-SA) goto ilseq;
+		c = bittab[*s++-SA]; n--;
+	}
+
+	if (n) {
+		if (OOB(c,*s)) goto ilseq;
+loop:
+		c = c<<6 | *s++-0x80; n--;
+		if (!(c&(1U<<31))) {
+			*(unsigned *)st = 0;
+			*wc = c;
+			return N-n;
+		}
+		if (n) {
+			if ((unsigned)*s-0x80 >= 0x40) goto ilseq;
+			goto loop;
+		}
+	}
+
+	*(unsigned *)st = c;
+	return -2;
+ilseq:
+	*(unsigned *)st = FAILSTATE;
+	errno = EILSEQ;
+	return -1;
+}
diff --git a/src/multibyte/mbsinit.c b/src/multibyte/mbsinit.c
new file mode 100644
index 00000000..d307e5a7
--- /dev/null
+++ b/src/multibyte/mbsinit.c
@@ -0,0 +1,17 @@
+/* 
+ * This code was written by Rich Felker in 2010; no copyright is claimed.
+ * This code is in the public domain. Attribution is appreciated but
+ * unnecessary.
+ */
+
+#include <stdlib.h>
+#include <inttypes.h>
+#include <wchar.h>
+#include <errno.h>
+
+#include "internal.h"
+
+int mbsinit(const mbstate_t *st)
+{
+	return !*(unsigned *)st;
+}
diff --git a/src/multibyte/mbsnrtowcs.c b/src/multibyte/mbsnrtowcs.c
new file mode 100644
index 00000000..c6f0207f
--- /dev/null
+++ b/src/multibyte/mbsnrtowcs.c
@@ -0,0 +1,61 @@
+/* 
+ * This code was written by Rich Felker in 2010; no copyright is claimed.
+ * This code is in the public domain. Attribution is appreciated but
+ * unnecessary.
+ */
+
+#include <stdlib.h>
+#include <inttypes.h>
+#include <wchar.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include "internal.h"
+
+size_t mbsnrtowcs(wchar_t *wcs, const char **src, size_t n, size_t wn, mbstate_t *st)
+{
+	size_t l, cnt=0, n2;
+	wchar_t *ws, wbuf[256];
+	const char *s = *src;
+
+	if (!wcs) ws = wbuf, wn = sizeof wbuf / sizeof *wbuf;
+	else ws = wcs;
+
+	/* making sure output buffer size is at most n/4 will ensure
+	 * that mbsrtowcs never reads more than n input bytes. thus
+	 * we can use mbsrtowcs as long as it's practical.. */
+
+	while ( s && wn && ( (n2=n/4)>=wn || n2>32 ) ) {
+		if (n2>=wn) n2=wn;
+		n -= n2;
+		l = mbsrtowcs(ws, &s, n2, st);
+		if (!(l+1)) {
+			cnt = l;
+			wn = 0;
+			break;
+		}
+		if (ws != wbuf) {
+			ws += l;
+			wn -= l;
+		}
+		cnt += l;
+	}
+	if (s) while (wn && n) {
+		l = mbrtowc(ws, s, n, st);
+		if (l+2<=2) {
+			if (!(l+1)) {
+				cnt = l;
+				break;
+			}
+			/* have to roll back partial character */
+			*(unsigned *)st = 0;
+			break;
+		}
+		s += l; n -= l;
+		/* safe - this loop runs fewer than sizeof(wbuf)/8 times */
+		ws++; wn--;
+		cnt++;
+	}
+	if (wcs) *src = s;
+	return cnt;
+}
diff --git a/src/multibyte/mbsrtowcs.c b/src/multibyte/mbsrtowcs.c
new file mode 100644
index 00000000..e2b43480
--- /dev/null
+++ b/src/multibyte/mbsrtowcs.c
@@ -0,0 +1,121 @@
+/* 
+ * This code was written by Rich Felker in 2010; no copyright is claimed.
+ * This code is in the public domain. Attribution is appreciated but
+ * unnecessary.
+ */
+
+#include <stdlib.h>
+#include <inttypes.h>
+#include <wchar.h>
+#include <errno.h>
+
+#include "internal.h"
+
+size_t mbsrtowcs(wchar_t *ws, const char **src, size_t wn, mbstate_t *st)
+{
+	unsigned c;
+	const unsigned char *s = *src;
+	const wchar_t *wsorig = ws;
+
+	if (!st) st = (void *)&c, c = 0;
+	else c = *(unsigned *)st;
+
+	if (c) {
+		*(unsigned *)st = 0;
+		if (!ws) {
+			wn = 0;
+			goto resume0;
+		}
+		goto resume;
+	}
+
+	if (!ws) for (wn=0;;) {
+		if ((unsigned)*s-SA >= SB-SA) {
+			while (((unsigned)s&3) && (unsigned)*s-1<0x7f) s++, wn++;
+			while (!(( *(uint32_t*)s | *(uint32_t*)s-0x01010101) & 0x80808080)) s+=4, wn+=4;
+			while ((unsigned)*s-1<0x7f) s++, wn++;
+			if (!*s) return wn;
+			if ((unsigned)*s-SA >= SB-SA) goto ilseq2;
+		}
+		c = bittab[*s++-SA];
+		do {
+resume0:
+			if (OOB(c,*s)) goto ilseq2; s++;
+			c <<= 6; if (!(c&(1U<<31))) break;
+#ifdef I_FAILED_TO_RTFM_RFC3629
+			if ((unsigned)*s++-0x80 >= 0x40) goto ilseq2;
+			c <<= 6; if (!(c&(1U<<31))) break;
+			if ((unsigned)*s++-0x80 >= 0x40) goto ilseq2;
+			c <<= 6; if (!(c&(1U<<31))) break;
+#endif
+			if ((unsigned)*s++-0x80 >= 0x40) goto ilseq2;
+			c <<= 6; if (!(c&(1U<<31))) break;
+			if ((unsigned)*s++-0x80 >= 0x40) goto ilseq2;
+		} while (0);
+		wn++; c = 0;
+	}
+
+	while (wn) {
+		if ((unsigned)*s-SA >= SB-SA) {
+			if (wn >= 7) {
+				while (((unsigned)s&3) && (unsigned)*s-1<0x7f) {
+					*ws++ = *s++;
+					wn--;
+				}
+				while (wn>=4 && !(( *(uint32_t*)s | *(uint32_t*)s-0x01010101) & 0x80808080)) {
+					*ws++ = *s++;
+					*ws++ = *s++;
+					*ws++ = *s++;
+					*ws++ = *s++;
+					wn -= 4;
+				}
+			}
+			while (wn && (unsigned)*s-1<0x7f) {
+				*ws++ = *s++;
+				wn--;
+			}
+			if (!wn) break;
+			if (!*s) {
+				*ws = 0;
+				*src = 0;
+				return ws-wsorig;
+			}
+			if ((unsigned)*s-SA >= SB-SA) goto ilseq;
+		}
+		c = bittab[*s++-SA];
+		do {
+resume:
+			if (OOB(c,*s)) goto ilseq;
+			c = (c<<6) | *s++-0x80;
+			if (!(c&(1U<<31))) break;
+
+#ifdef I_FAILED_TO_RTFM_RFC3629
+			if ((unsigned)*s-0x80 >= 0x40) goto ilseq;
+			c = (c<<6) | *s++-0x80;
+			if (!(c&(1U<<31))) break;
+
+			if ((unsigned)*s-0x80 >= 0x40) goto ilseq;
+			c = (c<<6) | *s++-0x80;
+			if (!(c&(1U<<31))) break;
+#endif
+
+			if ((unsigned)*s-0x80 >= 0x40) goto ilseq;
+			c = (c<<6) | *s++-0x80;
+			if (!(c&(1U<<31))) break;
+
+			if ((unsigned)*s-0x80 >= 0x40) goto ilseq;
+			c = (c<<6) | *s++-0x80;
+		} while (0);
+
+		*ws++ = c; wn--; c = 0;
+	}
+	*src = s;
+	return ws-wsorig;
+ilseq:
+	*src = s;
+ilseq2:
+	/* enter permanently failing state */
+	*(unsigned *)st = FAILSTATE;
+	errno = EILSEQ;
+	return -1;
+}
diff --git a/src/multibyte/mbstowcs.c b/src/multibyte/mbstowcs.c
new file mode 100644
index 00000000..23e1d925
--- /dev/null
+++ b/src/multibyte/mbstowcs.c
@@ -0,0 +1,18 @@
+/* 
+ * This code was written by Rich Felker in 2010; no copyright is claimed.
+ * This code is in the public domain. Attribution is appreciated but
+ * unnecessary.
+ */
+
+#include <stdlib.h>
+#include <inttypes.h>
+#include <wchar.h>
+#include <errno.h>
+
+#include "internal.h"
+
+size_t mbstowcs(wchar_t *ws, const char *s, size_t wn)
+{
+	mbstate_t st = { 0 };
+	return mbsrtowcs(ws, (void*)&s, wn, &st);
+}
diff --git a/src/multibyte/mbtowc.c b/src/multibyte/mbtowc.c
new file mode 100644
index 00000000..bdcaeb3c
--- /dev/null
+++ b/src/multibyte/mbtowc.c
@@ -0,0 +1,19 @@
+/* 
+ * This code was written by Rich Felker in 2010; no copyright is claimed.
+ * This code is in the public domain. Attribution is appreciated but
+ * unnecessary.
+ */
+
+#include <stdlib.h>
+#include <inttypes.h>
+#include <wchar.h>
+#include <errno.h>
+
+#include "internal.h"
+
+int mbtowc(wchar_t *wc, const char *s, size_t n)
+{
+	mbstate_t st = { 0 };
+	n = mbrtowc(wc, s, n, &st);
+	return n+2 ? n : -1;
+}
diff --git a/src/multibyte/wcrtomb.c b/src/multibyte/wcrtomb.c
new file mode 100644
index 00000000..36180c8f
--- /dev/null
+++ b/src/multibyte/wcrtomb.c
@@ -0,0 +1,38 @@
+/* 
+ * This code was written by Rich Felker in 2010; no copyright is claimed.
+ * This code is in the public domain. Attribution is appreciated but
+ * unnecessary.
+ */
+
+#include <stdlib.h>
+#include <inttypes.h>
+#include <wchar.h>
+#include <errno.h>
+
+#include "internal.h"
+
+size_t wcrtomb(char *s, wchar_t wc, mbstate_t *st)
+{
+	if (!s) return 1;
+	if ((unsigned)wc < 0x80) {
+		*s = wc;
+		return 1;
+	} else if ((unsigned)wc < 0x800) {
+		*s++ = 0xc0 | (wc>>6);
+		*s = 0x80 | (wc&0x3f);
+		return 2;
+	} else if ((unsigned)wc < 0xd800 || (unsigned)wc-0xe000 < 0x2000) {
+		*s++ = 0xe0 | (wc>>12);
+		*s++ = 0x80 | ((wc>>6)&0x3f);
+		*s = 0x80 | (wc&0x3f);
+		return 3;
+	} else if ((unsigned)wc-0x10000 < 0x100000) {
+		*s++ = 0xf0 | (wc>>18);
+		*s++ = 0x80 | ((wc>>12)&0x3f);
+		*s++ = 0x80 | ((wc>>6)&0x3f);
+		*s = 0x80 | (wc&0x3f);
+		return 4;
+	}
+	errno = EILSEQ;
+	return -1;
+}
diff --git a/src/multibyte/wcsnrtombs.c b/src/multibyte/wcsnrtombs.c
new file mode 100644
index 00000000..666f6f3f
--- /dev/null
+++ b/src/multibyte/wcsnrtombs.c
@@ -0,0 +1,51 @@
+/* 
+ * This code was written by Rich Felker in 2010; no copyright is claimed.
+ * This code is in the public domain. Attribution is appreciated but
+ * unnecessary.
+ */
+
+#include <stdlib.h>
+#include <inttypes.h>
+#include <wchar.h>
+#include <errno.h>
+
+#include "internal.h"
+
+size_t wcsnrtombs(char *dst, const wchar_t **wcs, size_t wn, size_t n, mbstate_t *st)
+{
+	size_t l, cnt=0, n2;
+	char *s, buf[256];
+	const wchar_t *ws = *wcs;
+
+	if (!dst) s = buf, n = sizeof buf;
+	else s = dst;
+
+	while ( n && ( (n2=wn)>=n || n2>32 ) ) {
+		if (n2>=n) n2=n;
+		wn -= n2;
+		l = wcsrtombs(s, &ws, n2, 0);
+		if (!(l+1)) {
+			cnt = l;
+			n = 0;
+			break;
+		}
+		if (s != buf) {
+			s += l;
+			n -= l;
+		}
+		cnt += l;
+	}
+	while (n && wn) {
+		l = wcrtomb(s, *ws, 0);
+		if (!(l+1)) {
+			cnt = l;
+			break;
+		}
+		ws++; wn--;
+		/* safe - this loop runs fewer than sizeof(buf) times */
+		s+=l; n-=l;
+		cnt++;
+	}
+	if (dst) *wcs = ws;
+	return cnt;
+}
diff --git a/src/multibyte/wcsrtombs.c b/src/multibyte/wcsrtombs.c
new file mode 100644
index 00000000..3c48d65b
--- /dev/null
+++ b/src/multibyte/wcsrtombs.c
@@ -0,0 +1,58 @@
+/* 
+ * This code was written by Rich Felker in 2010; no copyright is claimed.
+ * This code is in the public domain. Attribution is appreciated but
+ * unnecessary.
+ */
+
+#include <stdlib.h>
+#include <inttypes.h>
+#include <wchar.h>
+#include <errno.h>
+
+#include "internal.h"
+
+size_t wcsrtombs(char *s, const wchar_t **ws, size_t n, mbstate_t *st)
+{
+	const wchar_t *ws2;
+	char buf[4];
+	size_t N = n, l;
+	if (!s) {
+		for (n=0, ws2=*ws; *ws2; ws2++) {
+			if (*ws2 >= 0x80) {
+				l = wcrtomb(buf, *ws2, 0);
+				if (!(l+1)) return -1;
+				n += l;
+			} else n++;
+		}
+		return n;
+	}
+	while (n>=4 && **ws) {
+		if (**ws >= 0x80) {
+			l = wcrtomb(s, **ws, 0);
+			if (!(l+1)) return -1;
+			s += l;
+			n -= l;
+		} else {
+			*s++ = **ws;
+			n--;
+		}
+		(*ws)++;
+	}
+	while (n && **ws) {
+		if (**ws >= 0x80) {
+			l = wcrtomb(buf, **ws, 0);
+			if (!(l+1)) return -1;
+			if (l>n) return N-n;
+			wcrtomb(s, **ws, 0);
+			s += l;
+			n -= l;
+		} else {
+			*s++ = **ws;
+			n--;
+		}
+		(*ws)++;
+	}
+	if (n) *s = 0;
+	*ws = 0;
+	return N-n;
+}
diff --git a/src/multibyte/wcstombs.c b/src/multibyte/wcstombs.c
new file mode 100644
index 00000000..b9c1b18a
--- /dev/null
+++ b/src/multibyte/wcstombs.c
@@ -0,0 +1,17 @@
+/* 
+ * This code was written by Rich Felker in 2010; no copyright is claimed.
+ * This code is in the public domain. Attribution is appreciated but
+ * unnecessary.
+ */
+
+#include <stdlib.h>
+#include <inttypes.h>
+#include <wchar.h>
+#include <errno.h>
+
+#include "internal.h"
+
+size_t wcstombs(char *s, const wchar_t *ws, size_t n)
+{
+	return wcsrtombs(s, &ws, n, 0);
+}
diff --git a/src/multibyte/wctob.c b/src/multibyte/wctob.c
new file mode 100644
index 00000000..d6353ee1
--- /dev/null
+++ b/src/multibyte/wctob.c
@@ -0,0 +1,8 @@
+#include <stdio.h>
+#include <wchar.h>
+
+int wctob(wint_t c)
+{
+	if (c < 128U) return c;
+	return EOF;
+}
diff --git a/src/multibyte/wctomb.c b/src/multibyte/wctomb.c
new file mode 100644
index 00000000..6910ef37
--- /dev/null
+++ b/src/multibyte/wctomb.c
@@ -0,0 +1,18 @@
+/* 
+ * This code was written by Rich Felker in 2010; no copyright is claimed.
+ * This code is in the public domain. Attribution is appreciated but
+ * unnecessary.
+ */
+
+#include <stdlib.h>
+#include <inttypes.h>
+#include <wchar.h>
+#include <errno.h>
+
+#include "internal.h"
+
+int wctomb(char *s, wchar_t wc)
+{
+	if (!s) return 0;
+	return wcrtomb(s, wc, 0);
+}