1041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner/*
2041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner * This code was written by Rich Felker in 2010; no copyright is claimed.
3041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner * This code is in the public domain. Attribution is appreciated but
4041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner * unnecessary.
5041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner */
6041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner
7041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner#include <wchar.h>
8041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner#include <errno.h>
9041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner#include "internal.h"
10041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner
11041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turnersize_t mbrtowc(wchar_t *restrict wc, const char *restrict src, size_t n, mbstate_t *restrict st)
12041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner{
13041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner	static unsigned internal_state;
14041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner	unsigned c;
15041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner	const unsigned char *s = (const void *)src;
16041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner	const unsigned N = n;
17041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner
18041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner	if (!st) st = (void *)&internal_state;
19041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner	c = *(unsigned *)st;
20041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner
21041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner	if (!s) {
22041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner		if (c) goto ilseq;
23041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner		return 0;
24041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner	} else if (!wc) wc = (void *)&wc;
25041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner
26041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner	if (!n) return -2;
27041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner	if (!c) {
28041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner		if (*s < 0x80) return !!(*wc = *s);
29041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner		if (*s-SA > SB-SA) goto ilseq;
30041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner		c = bittab[*s++-SA]; n--;
31041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner	}
32041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner
33041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner	if (n) {
34041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner		if (OOB(c,*s)) goto ilseq;
35041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turnerloop:
36041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner		c = c<<6 | *s++-0x80; n--;
37041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner		if (!(c&(1U<<31))) {
38041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner			*(unsigned *)st = 0;
39041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner			*wc = c;
40041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner			return N-n;
41041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner		}
42041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner		if (n) {
43041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner			if (*s-0x80u >= 0x40) goto ilseq;
44041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner			goto loop;
45041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner		}
46041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner	}
47041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner
48041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner	*(unsigned *)st = c;
49041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner	return -2;
50041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turnerilseq:
51041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner	*(unsigned *)st = 0;
52041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner	errno = EILSEQ;
53041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner	return -1;
54041656818eb2625982d4b55d176468a4bd07fb32David 'Digit' Turner}
55