1 /*-
2  * Copyright (c) 2006, 2007, 2008
3  *	Thorsten Glaser <tg@mirbsd.org>
4  *
5  * Provided that these terms and disclaimer and all copyright notices
6  * are retained or reproduced in an accompanying document, permission
7  * is granted to deal in this work without restriction, including un-
8  * limited rights to use, publicly perform, distribute, sell, modify,
9  * merge, give away, or sublicence.
10  *
11  * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
12  * the utmost extent permitted by applicable law, neither express nor
13  * implied; without malicious intent or gross negligence. In no event
14  * may a licensor, author or contributor be held liable for indirect,
15  * direct, other damage, loss, or other issues arising in any way out
16  * of dealing in the work, even if advised of the possibility of such
17  * damage or existence of a defect, except proven that it results out
18  * of said person's immediate fault when using the work as intended.
19  *
20  * The author reserves the right to steward the OPTU encoding forms.
21  */
22 
23 #include <errno.h>
24 #include <wchar.h>
25 
26 __RCSID("$MirOS: src/lib/libc/i18n/wcsrtombs.c,v 1.10 2014/02/09 22:35:53 tg Exp $");
27 
28 #ifdef WCSNRTOMBS
29 size_t
wcsnrtombs(char * dst,const wchar_t ** src,size_t max,size_t len,mbstate_t * ps)30 wcsnrtombs(char *dst, const wchar_t **src, size_t max, size_t len, mbstate_t *ps)
31 #else
32 size_t
33 wcsrtombs(char *dst, const wchar_t **src, size_t len, mbstate_t *ps)
34 #endif
35 {
36 	static mbstate_t internal_mbstate = { 0, 0 };	/* if ps == NULL */
37 	const wchar_t *s = *src;
38 	unsigned char *d = (unsigned char *)dst;
39 	wint_t wc /* shut up gcc */ = 0;
40 	uint8_t count;
41 
42 	/* make sure we can at least write one output byte */
43 	if ((dst != NULL) && (len == 0))
44 		return (0);
45 
46 	if (__predict_false(ps == NULL))
47 		ps = &internal_mbstate;
48 
49 	if ((count = ps->count)) {
50 		wc = ps->value;
51 		/* process remnants */
52 		goto process_state;
53 	}
54 
55  process_firstbyte:
56 	/* count is zero here; devour an input wide character */
57 #ifdef WCSNRTOMBS
58 	if (s >= (*src + max))
59 		goto empty_buf;
60 #endif
61 	wc = *s++;
62 	/* create the first output byte and state information from it */
63 	if (__predict_false(wc > WCHAR_MAX)) {
64 		errno = EILSEQ;
65 		return ((size_t)(-1));
66 	} else if (__predict_true(wc < 0x80 || iswoctet(wc))) {
67 		if (dst != NULL)
68 			*d = wc & 0xFF;
69 		/* count is already 0 */
70 	} else if (wc < 0x0800) {
71 		if (dst != NULL)
72 			*d = (wc >> 6) | 0xC0;
73 		count = 1;
74 	} else {
75 		if (dst != NULL)
76 			*d = (wc >> 12) | 0xE0;
77 		count = 2;
78 	}
79 	/* account the output byte, except if it's the terminating NUL */
80 	if (wc > 0)
81 		++d;
82 	/* at this point, we have written an output byte */
83 	--len;
84 
85  process_state:
86 	/* entering with len >= 0, count + wc containing state info */
87 	while (__predict_false((count > 0) && ((len > 0) || (dst == NULL)))) {
88 		--count;
89 		if (dst != NULL)
90 			*d = ((wc >> (6 * count)) & 0x3F) | 0x80;
91 		++d;
92 		--len;
93 	}
94 
95 	/* here: either len == 0 or count == 0 (or both!) */
96 	if (__predict_false(wc == 0))
97 		/* last character was a terminating NUL, count == 0 */
98 		s = NULL;
99 	else if (__predict_true(count == 0))
100 		/* so, if there's still output space left or we ignore it */
101 		if ((len > 0) || (dst == NULL))
102 			/* loop on to the next full input wide character */
103 			goto process_firstbyte;
104 
105 #ifdef WCSNRTOMBS
106  empty_buf:
107 #endif
108 	if (dst != NULL) {
109 		*src = s;
110 		/* save state information for restarting */
111 		ps->count = count;
112 		ps->value = wc;
113 	}
114 	return ((char *)d - dst);
115 }
116