xref: /netbsd-src/lib/libc/citrus/modules/citrus_utf7.c (revision 388550b026d49b7f7b7480b1113bf82bb8d6a480)
1*388550b0Srillig /*	$NetBSD: citrus_utf7.c,v 1.7 2022/04/19 20:32:14 rillig Exp $	*/
2fe05f588Stnozaki 
3fe05f588Stnozaki /*-
4fe05f588Stnozaki  * Copyright (c)2004, 2005 Citrus Project,
5fe05f588Stnozaki  * All rights reserved.
6fe05f588Stnozaki  *
7fe05f588Stnozaki  * Redistribution and use in source and binary forms, with or without
8fe05f588Stnozaki  * modification, are permitted provided that the following conditions
9fe05f588Stnozaki  * are met:
10fe05f588Stnozaki  * 1. Redistributions of source code must retain the above copyright
11fe05f588Stnozaki  *    notice, this list of conditions and the following disclaimer.
12fe05f588Stnozaki  * 2. Redistributions in binary form must reproduce the above copyright
13fe05f588Stnozaki  *    notice, this list of conditions and the following disclaimer in the
14fe05f588Stnozaki  *    documentation and/or other materials provided with the distribution.
15fe05f588Stnozaki  *
16fe05f588Stnozaki  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17fe05f588Stnozaki  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18fe05f588Stnozaki  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19fe05f588Stnozaki  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20fe05f588Stnozaki  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21fe05f588Stnozaki  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22fe05f588Stnozaki  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23fe05f588Stnozaki  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24fe05f588Stnozaki  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25fe05f588Stnozaki  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26fe05f588Stnozaki  * SUCH DAMAGE.
27fe05f588Stnozaki  *
28fe05f588Stnozaki  */
29fe05f588Stnozaki 
30fe05f588Stnozaki #include <sys/cdefs.h>
31fe05f588Stnozaki #if defined(LIB_SCCS) && !defined(lint)
32*388550b0Srillig __RCSID("$NetBSD: citrus_utf7.c,v 1.7 2022/04/19 20:32:14 rillig Exp $");
33fe05f588Stnozaki #endif /* LIB_SCCS and not lint */
34fe05f588Stnozaki 
35fe05f588Stnozaki #include <assert.h>
36fe05f588Stnozaki #include <errno.h>
37fe05f588Stnozaki #include <string.h>
38fe05f588Stnozaki #include <stdio.h>
39fe05f588Stnozaki #include <stdint.h>
40fe05f588Stnozaki #include <stdlib.h>
41fe05f588Stnozaki #include <limits.h>
42fe05f588Stnozaki #include <wchar.h>
43fe05f588Stnozaki 
44fe05f588Stnozaki #include "citrus_namespace.h"
45fe05f588Stnozaki #include "citrus_types.h"
46fe05f588Stnozaki #include "citrus_module.h"
47fe05f588Stnozaki #include "citrus_ctype.h"
48fe05f588Stnozaki #include "citrus_stdenc.h"
49fe05f588Stnozaki #include "citrus_utf7.h"
50fe05f588Stnozaki 
51fe05f588Stnozaki /* ----------------------------------------------------------------------
52fe05f588Stnozaki  * private stuffs used by templates
53fe05f588Stnozaki  */
54fe05f588Stnozaki 
55fe05f588Stnozaki typedef struct {
56fe05f588Stnozaki 	uint16_t	cell[0x80];
57fe05f588Stnozaki #define	EI_MASK		UINT16_C(0xff)
58fe05f588Stnozaki #define EI_DIRECT	UINT16_C(0x100)
59fe05f588Stnozaki #define EI_OPTION	UINT16_C(0x200)
60fe05f588Stnozaki #define EI_SPACE	UINT16_C(0x400)
61fe05f588Stnozaki } _UTF7EncodingInfo;
62fe05f588Stnozaki 
63fe05f588Stnozaki typedef struct {
64fe05f588Stnozaki 	unsigned int
65fe05f588Stnozaki 		mode: 1,	/* whether base64 mode */
66fe05f588Stnozaki 		bits: 4,	/* need to hold 0 - 15 */
67fe05f588Stnozaki 		cache: 22,	/* 22 = BASE64_BIT + UTF16_BIT */
686e2609d6Stnozaki 		surrogate: 1;	/* whether surrogate pair or not */
696e2609d6Stnozaki 	int chlen;
70fe05f588Stnozaki 	char ch[4]; /* BASE64_IN, 3 * 6 = 18, most closed to UTF16_BIT */
71fe05f588Stnozaki } _UTF7State;
72fe05f588Stnozaki 
73fe05f588Stnozaki typedef struct {
74fe05f588Stnozaki 	_UTF7EncodingInfo	ei;
75fe05f588Stnozaki 	struct {
76fe05f588Stnozaki 		/* for future multi-locale facility */
77fe05f588Stnozaki 		_UTF7State	s_mblen;
78fe05f588Stnozaki 		_UTF7State	s_mbrlen;
79fe05f588Stnozaki 		_UTF7State	s_mbrtowc;
80fe05f588Stnozaki 		_UTF7State	s_mbtowc;
81fe05f588Stnozaki 		_UTF7State	s_mbsrtowcs;
8285a67e61Sjoerg 		_UTF7State	s_mbsnrtowcs;
83fe05f588Stnozaki 		_UTF7State	s_wcrtomb;
84fe05f588Stnozaki 		_UTF7State	s_wcsrtombs;
8585a67e61Sjoerg 		_UTF7State	s_wcsnrtombs;
86fe05f588Stnozaki 		_UTF7State	s_wctomb;
87fe05f588Stnozaki 	} states;
88fe05f588Stnozaki } _UTF7CTypeInfo;
89fe05f588Stnozaki 
90fe05f588Stnozaki #define	_CEI_TO_EI(_cei_)		(&(_cei_)->ei)
91fe05f588Stnozaki #define	_CEI_TO_STATE(_cei_, _func_)	(_cei_)->states.s_##_func_
92fe05f588Stnozaki 
93fe05f588Stnozaki #define	_FUNCNAME(m)			_citrus_UTF7_##m
94fe05f588Stnozaki #define	_ENCODING_INFO			_UTF7EncodingInfo
95fe05f588Stnozaki #define	_CTYPE_INFO			_UTF7CTypeInfo
96fe05f588Stnozaki #define	_ENCODING_STATE			_UTF7State
97fe05f588Stnozaki #define	_ENCODING_MB_CUR_MAX(_ei_)		4
98fe05f588Stnozaki #define	_ENCODING_IS_STATE_DEPENDENT		1
99fe05f588Stnozaki #define	_STATE_NEEDS_EXPLICIT_INIT(_ps_)	0
100fe05f588Stnozaki 
101fe05f588Stnozaki static __inline void
102fe05f588Stnozaki /*ARGSUSED*/
_citrus_UTF7_init_state(_UTF7EncodingInfo * __restrict ei,_UTF7State * __restrict s)103fe05f588Stnozaki _citrus_UTF7_init_state(_UTF7EncodingInfo * __restrict ei,
104fe05f588Stnozaki 	_UTF7State * __restrict s)
105fe05f588Stnozaki {
106fe05f588Stnozaki 	/* ei appears to be unused */
107fe05f588Stnozaki 	_DIAGASSERT(s != NULL);
108fe05f588Stnozaki 
109fe05f588Stnozaki 	memset((void *)s, 0, sizeof(*s));
110fe05f588Stnozaki }
111fe05f588Stnozaki 
112fe05f588Stnozaki static __inline void
113fe05f588Stnozaki /*ARGSUSED*/
_citrus_UTF7_pack_state(_UTF7EncodingInfo * __restrict ei,void * __restrict pspriv,const _UTF7State * __restrict s)114fe05f588Stnozaki _citrus_UTF7_pack_state(_UTF7EncodingInfo * __restrict ei,
115fe05f588Stnozaki 	void *__restrict pspriv, const _UTF7State * __restrict s)
116fe05f588Stnozaki {
117fe05f588Stnozaki 	/* ei seem to be unused */
118fe05f588Stnozaki 	_DIAGASSERT(pspriv != NULL);
119fe05f588Stnozaki 	_DIAGASSERT(s != NULL);
120fe05f588Stnozaki 
121fe05f588Stnozaki 	memcpy(pspriv, (const void *)s, sizeof(*s));
122fe05f588Stnozaki }
123fe05f588Stnozaki 
124fe05f588Stnozaki static __inline void
125fe05f588Stnozaki /*ARGSUSED*/
_citrus_UTF7_unpack_state(_UTF7EncodingInfo * __restrict ei,_UTF7State * __restrict s,const void * __restrict pspriv)126fe05f588Stnozaki _citrus_UTF7_unpack_state(_UTF7EncodingInfo * __restrict ei,
127fe05f588Stnozaki 	_UTF7State * __restrict s, const void * __restrict pspriv)
128fe05f588Stnozaki {
129fe05f588Stnozaki 	/* ei seem to be unused */
130fe05f588Stnozaki 	_DIAGASSERT(s != NULL);
131fe05f588Stnozaki 	_DIAGASSERT(pspriv != NULL);
132fe05f588Stnozaki 
133fe05f588Stnozaki 	memcpy((void *)s, pspriv, sizeof(*s));
134fe05f588Stnozaki }
135fe05f588Stnozaki 
136fe05f588Stnozaki static const char base64[] =
137fe05f588Stnozaki 	"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
138fe05f588Stnozaki 	"abcdefghijklmnopqrstuvwxyz"
139fe05f588Stnozaki 	"0123456789+/";
140fe05f588Stnozaki 
141fe05f588Stnozaki static const char direct[] =
142fe05f588Stnozaki 	"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
143fe05f588Stnozaki 	"abcdefghijklmnopqrstuvwxyz"
144fe05f588Stnozaki 	"0123456789(),-./:?";
145fe05f588Stnozaki 
146fe05f588Stnozaki static const char option[] = "!\"#$%&';<=>@[]^_`{|}";
147fe05f588Stnozaki static const char spaces[] = " \t\r\n";
148fe05f588Stnozaki 
149fe05f588Stnozaki #define	BASE64_BIT	6
150fe05f588Stnozaki #define	UTF16_BIT	16
151fe05f588Stnozaki 
152fe05f588Stnozaki #define	BASE64_MAX	0x3f
153fe05f588Stnozaki #define	UTF16_MAX	UINT16_C(0xffff)
154fe05f588Stnozaki #define	UTF32_MAX	UINT32_C(0x10ffff)
155fe05f588Stnozaki 
156fe05f588Stnozaki #define	BASE64_IN	'+'
157fe05f588Stnozaki #define	BASE64_OUT	'-'
158fe05f588Stnozaki 
159fe05f588Stnozaki #define	SHIFT7BIT(c)	((c) >> 7)
160fe05f588Stnozaki #define	ISSPECIAL(c)	((c) == '\0' || (c) == BASE64_IN)
161fe05f588Stnozaki 
162fe05f588Stnozaki #define	FINDLEN(ei, c) \
163fe05f588Stnozaki 	(SHIFT7BIT((c)) ? -1 : (((ei)->cell[(c)] & EI_MASK) - 1))
164fe05f588Stnozaki 
165fe05f588Stnozaki #define	ISDIRECT(ei, c)	(!SHIFT7BIT((c)) && (ISSPECIAL((c)) || \
166fe05f588Stnozaki 	ei->cell[(c)] & (EI_DIRECT | EI_OPTION | EI_SPACE)))
167fe05f588Stnozaki 
168fe05f588Stnozaki #define	ISSAFE(ei, c)	(!SHIFT7BIT((c)) && (ISSPECIAL((c)) || \
16915cc8e46Schristos 	(c < 0x80 && ei->cell[(c)] & (EI_DIRECT | EI_SPACE))))
170fe05f588Stnozaki 
171fe05f588Stnozaki /* surrogate pair */
172fe05f588Stnozaki #define	SRG_BASE	UINT32_C(0x10000)
173fe05f588Stnozaki #define	HISRG_MIN	UINT16_C(0xd800)
174fe05f588Stnozaki #define	HISRG_MAX	UINT16_C(0xdbff)
175fe05f588Stnozaki #define	LOSRG_MIN	UINT16_C(0xdc00)
176fe05f588Stnozaki #define	LOSRG_MAX	UINT16_C(0xdfff)
177fe05f588Stnozaki 
178fe05f588Stnozaki static int
_citrus_UTF7_mbtoutf16(_UTF7EncodingInfo * __restrict ei,uint16_t * __restrict u16,const char ** __restrict s,size_t n,_UTF7State * __restrict psenc,size_t * __restrict nresult)179f264ea3aStnozaki _citrus_UTF7_mbtoutf16(_UTF7EncodingInfo * __restrict ei,
180fe05f588Stnozaki 	uint16_t * __restrict u16, const char ** __restrict s, size_t n,
181fe05f588Stnozaki 	_UTF7State * __restrict psenc, size_t * __restrict nresult)
182fe05f588Stnozaki {
1836e2609d6Stnozaki 	_UTF7State sv;
184fe05f588Stnozaki 	const char *s0;
185fe05f588Stnozaki 	int i, done, len;
186fe05f588Stnozaki 
187fe05f588Stnozaki 	_DIAGASSERT(ei != NULL);
188fe05f588Stnozaki 	_DIAGASSERT(s != NULL && *s != NULL);
189fe05f588Stnozaki 	_DIAGASSERT(psenc != NULL);
190fe05f588Stnozaki 
191fe05f588Stnozaki 	s0 = *s;
1926e2609d6Stnozaki 	sv = *psenc;
193fe05f588Stnozaki 
194fe05f588Stnozaki 	for (i = 0, done = 0; done == 0; i++) {
1956e2609d6Stnozaki 		_DIAGASSERT(i <= psenc->chlen);
1966e2609d6Stnozaki 		if (i == psenc->chlen) {
197fe05f588Stnozaki 			if (n-- < 1) {
198fe05f588Stnozaki 				*nresult = (size_t)-2;
199fe05f588Stnozaki 				*s = s0;
2006e2609d6Stnozaki 				sv.chlen = psenc->chlen;
2016e2609d6Stnozaki 				*psenc = sv;
202f264ea3aStnozaki 				return 0;
203fe05f588Stnozaki 			}
2046e2609d6Stnozaki 			psenc->ch[psenc->chlen++] = *s0++;
205fe05f588Stnozaki 		}
206fe05f588Stnozaki 		if (SHIFT7BIT((int)psenc->ch[i]))
207fe05f588Stnozaki 			goto ilseq;
2086e2609d6Stnozaki 		if (!psenc->mode) {
2096e2609d6Stnozaki 			if (psenc->bits > 0 || psenc->cache > 0)
210f264ea3aStnozaki 				return EINVAL;
211fe05f588Stnozaki 			if (psenc->ch[i] == BASE64_IN) {
2126e2609d6Stnozaki 				psenc->mode = 1;
213fe05f588Stnozaki 			} else {
214fe05f588Stnozaki 				if (!ISDIRECT(ei, (int)psenc->ch[i]))
215fe05f588Stnozaki 					goto ilseq;
216fe05f588Stnozaki 				*u16 = (uint16_t)psenc->ch[i];
217fe05f588Stnozaki 				done = 1;
218fe05f588Stnozaki 				continue;
219fe05f588Stnozaki 			}
220fe05f588Stnozaki 		} else {
2216e2609d6Stnozaki 			if (psenc->ch[i] == BASE64_OUT && psenc->cache == 0) {
2226e2609d6Stnozaki 				psenc->mode = 0;
223fe05f588Stnozaki 				*u16 = (uint16_t)BASE64_IN;
224fe05f588Stnozaki 				done = 1;
225fe05f588Stnozaki 				continue;
226fe05f588Stnozaki 			}
227fe05f588Stnozaki 			len = FINDLEN(ei, (int)psenc->ch[i]);
228fe05f588Stnozaki 			if (len < 0) {
2296e2609d6Stnozaki 				if (psenc->bits >= BASE64_BIT)
230f264ea3aStnozaki 					return EINVAL;
2316e2609d6Stnozaki 				psenc->mode = 0;
2326e2609d6Stnozaki 				psenc->bits = psenc->cache = 0;
233fe05f588Stnozaki 				if (psenc->ch[i] != BASE64_OUT) {
234fe05f588Stnozaki 					if (!ISDIRECT(ei, (int)psenc->ch[i]))
235fe05f588Stnozaki 						goto ilseq;
236fe05f588Stnozaki 					*u16 = (uint16_t)psenc->ch[i];
237fe05f588Stnozaki 					done = 1;
238fe05f588Stnozaki 				}
239fe05f588Stnozaki 			} else {
240f264ea3aStnozaki 				psenc->cache =
241f264ea3aStnozaki 				    (psenc->cache << BASE64_BIT) | len;
2426e2609d6Stnozaki 				switch (psenc->bits) {
243fe05f588Stnozaki 				case 0: case 2: case 4: case 6: case 8:
2446e2609d6Stnozaki 					psenc->bits += BASE64_BIT;
245fe05f588Stnozaki 					break;
246fe05f588Stnozaki 				case 10: case 12: case 14:
2476e2609d6Stnozaki 					psenc->bits -= (UTF16_BIT - BASE64_BIT);
2486e2609d6Stnozaki 					*u16 = (psenc->cache >> psenc->bits)
249fe05f588Stnozaki 					    & UTF16_MAX;
250fe05f588Stnozaki 					done = 1;
251fe05f588Stnozaki 					break;
252fe05f588Stnozaki 				default:
253f264ea3aStnozaki 					return EINVAL;
254fe05f588Stnozaki 				}
255fe05f588Stnozaki 			}
256fe05f588Stnozaki 		}
257fe05f588Stnozaki 	}
258fe05f588Stnozaki 
2596e2609d6Stnozaki 	if (psenc->chlen > i)
260f264ea3aStnozaki 		return EINVAL;
2616e2609d6Stnozaki 	psenc->chlen = 0;
262fe05f588Stnozaki 	*nresult = (size_t)((*u16 == 0) ? 0 : s0 - *s);
263fe05f588Stnozaki 	*s = s0;
264fe05f588Stnozaki 
265f264ea3aStnozaki 	return 0;
266fe05f588Stnozaki 
267fe05f588Stnozaki ilseq:
268fe05f588Stnozaki 	*nresult = (size_t)-1;
269f264ea3aStnozaki 	return EILSEQ;
270fe05f588Stnozaki }
271fe05f588Stnozaki 
272fe05f588Stnozaki static int
_citrus_UTF7_mbrtowc_priv(_UTF7EncodingInfo * __restrict ei,wchar_t * __restrict pwc,const char ** __restrict s,size_t n,_UTF7State * __restrict psenc,size_t * __restrict nresult)273fe05f588Stnozaki _citrus_UTF7_mbrtowc_priv(_UTF7EncodingInfo * __restrict ei,
274fe05f588Stnozaki 	wchar_t * __restrict pwc, const char ** __restrict s, size_t n,
275fe05f588Stnozaki 	_UTF7State * __restrict psenc, size_t * __restrict nresult)
276fe05f588Stnozaki {
277f264ea3aStnozaki 	const char *s0;
278fe05f588Stnozaki 	uint32_t u32;
279fe05f588Stnozaki 	uint16_t hi, lo;
280f264ea3aStnozaki 	size_t siz, nr;
281fe05f588Stnozaki 	int err;
282fe05f588Stnozaki 
283fe05f588Stnozaki 	_DIAGASSERT(ei != NULL);
284fe05f588Stnozaki 	/* pwc may be null */
285fe05f588Stnozaki 	_DIAGASSERT(s != NULL);
286fe05f588Stnozaki 	_DIAGASSERT(psenc != NULL);
287fe05f588Stnozaki 
288fe05f588Stnozaki 	if (*s == NULL) {
289fe05f588Stnozaki 		_citrus_UTF7_init_state(ei, psenc);
290fe05f588Stnozaki 		*nresult = (size_t)_ENCODING_IS_STATE_DEPENDENT;
291f264ea3aStnozaki 		return 0;
292fe05f588Stnozaki 	}
293f264ea3aStnozaki 	s0 = *s;
2946e2609d6Stnozaki 	if (psenc->surrogate) {
2956e2609d6Stnozaki 		hi = (psenc->cache >> 2) & UTF16_MAX;
296f264ea3aStnozaki 		if (hi < HISRG_MIN || hi > HISRG_MAX)
297f264ea3aStnozaki 			return EINVAL;
298fe05f588Stnozaki 		siz = 0;
299fe05f588Stnozaki 	} else {
300f264ea3aStnozaki 		err = _citrus_UTF7_mbtoutf16(ei, &hi, &s0, n, psenc, &nr);
301f264ea3aStnozaki 		if (nr == (size_t)-1 || nr == (size_t)-2) {
302f264ea3aStnozaki 			*nresult = nr;
303f264ea3aStnozaki 			return err;
304fe05f588Stnozaki 		}
305f264ea3aStnozaki 		if (err != 0)
306f264ea3aStnozaki 			return err;
307f264ea3aStnozaki 		n -= nr;
308f264ea3aStnozaki 		siz = nr;
309f264ea3aStnozaki 		if (hi < HISRG_MIN || hi > HISRG_MAX) {
310fe05f588Stnozaki 			u32 = (uint32_t)hi;
311f264ea3aStnozaki 			goto done;
312f264ea3aStnozaki 		}
313f264ea3aStnozaki 		psenc->surrogate = 1;
314f264ea3aStnozaki 	}
315f264ea3aStnozaki 	err = _citrus_UTF7_mbtoutf16(ei, &lo, &s0, n, psenc, &nr);
316f264ea3aStnozaki 	if (nr == (size_t)-1 || nr == (size_t)-2) {
317f264ea3aStnozaki 		*nresult = nr;
318f264ea3aStnozaki 		return err;
319f264ea3aStnozaki 	}
320f264ea3aStnozaki 	if (err != 0)
321f264ea3aStnozaki 		return err;
322fe05f588Stnozaki 	hi -= HISRG_MIN;
323fe05f588Stnozaki 	lo -= LOSRG_MIN;
324fe05f588Stnozaki 	u32 = (hi << 10 | lo) + SRG_BASE;
325f264ea3aStnozaki 	siz += nr;
326f264ea3aStnozaki done:
327f264ea3aStnozaki 	*s = s0;
328fe05f588Stnozaki 	if (pwc != NULL)
329fe05f588Stnozaki 		*pwc = (wchar_t)u32;
330f264ea3aStnozaki 	if (u32 == (uint32_t)0) {
331f264ea3aStnozaki 		*nresult = (size_t)0;
332f264ea3aStnozaki 		_citrus_UTF7_init_state(ei, psenc);
333f264ea3aStnozaki 	} else {
334f264ea3aStnozaki 		*nresult = siz;
335f264ea3aStnozaki 		psenc->surrogate = 0;
336f264ea3aStnozaki 	}
337f264ea3aStnozaki 	return err;
338fe05f588Stnozaki }
339fe05f588Stnozaki 
340f264ea3aStnozaki static int
_citrus_UTF7_utf16tomb(_UTF7EncodingInfo * __restrict ei,char * __restrict s,size_t n,uint16_t u16,_UTF7State * __restrict psenc,size_t * __restrict nresult)341f264ea3aStnozaki _citrus_UTF7_utf16tomb(_UTF7EncodingInfo * __restrict ei,
342f264ea3aStnozaki 	char * __restrict s, size_t n, uint16_t u16,
343f264ea3aStnozaki 	_UTF7State * __restrict psenc, size_t * __restrict nresult)
344fe05f588Stnozaki {
345fe05f588Stnozaki 	int bits, i;
346fe05f588Stnozaki 
347fe05f588Stnozaki 	_DIAGASSERT(ei != NULL);
348fe05f588Stnozaki 	_DIAGASSERT(psenc != NULL);
349fe05f588Stnozaki 
3506e2609d6Stnozaki 	if (psenc->chlen != 0 || psenc->bits > BASE64_BIT)
351f264ea3aStnozaki 		return EINVAL;
352fe05f588Stnozaki 
353fe05f588Stnozaki 	if (ISSAFE(ei, u16)) {
3546e2609d6Stnozaki 		if (psenc->mode) {
3556e2609d6Stnozaki 			if (psenc->bits > 0) {
3566e2609d6Stnozaki 				bits = BASE64_BIT - psenc->bits;
3576e2609d6Stnozaki 				i = (psenc->cache << bits) & BASE64_MAX;
3586e2609d6Stnozaki 				psenc->ch[psenc->chlen++] = base64[i];
3596e2609d6Stnozaki 				psenc->bits = psenc->cache = 0;
360fe05f588Stnozaki 			}
361fe05f588Stnozaki 			if (u16 == BASE64_OUT || FINDLEN(ei, u16) >= 0)
3626e2609d6Stnozaki 				psenc->ch[psenc->chlen++] = BASE64_OUT;
3636e2609d6Stnozaki 			psenc->mode = 0;
364fe05f588Stnozaki 		}
3656e2609d6Stnozaki 		if (psenc->bits != 0)
366f264ea3aStnozaki 			return EINVAL;
3676e2609d6Stnozaki 		psenc->ch[psenc->chlen++] = (char)u16;
368fe05f588Stnozaki 		if (u16 == BASE64_IN)
3696e2609d6Stnozaki 			psenc->ch[psenc->chlen++] = BASE64_OUT;
370fe05f588Stnozaki 	} else {
3716e2609d6Stnozaki 		if (!psenc->mode) {
3726e2609d6Stnozaki 			if (psenc->bits > 0)
373f264ea3aStnozaki 				return EINVAL;
3746e2609d6Stnozaki 			psenc->ch[psenc->chlen++] = BASE64_IN;
3756e2609d6Stnozaki 			psenc->mode = 1;
376fe05f588Stnozaki 		}
3776e2609d6Stnozaki 		psenc->cache = (psenc->cache << UTF16_BIT) | u16;
3786e2609d6Stnozaki 		bits = UTF16_BIT + psenc->bits;
3796e2609d6Stnozaki 		psenc->bits = bits % BASE64_BIT;
380fe05f588Stnozaki 		while ((bits -= BASE64_BIT) >= 0) {
3816e2609d6Stnozaki 			i = (psenc->cache >> bits) & BASE64_MAX;
3826e2609d6Stnozaki 			psenc->ch[psenc->chlen++] = base64[i];
383fe05f588Stnozaki 		}
384fe05f588Stnozaki 	}
385f264ea3aStnozaki 	memcpy(s, psenc->ch, psenc->chlen);
386f264ea3aStnozaki 	*nresult = psenc->chlen;
387f264ea3aStnozaki 	psenc->chlen = 0;
388fe05f588Stnozaki 
389f264ea3aStnozaki 	return 0;
390fe05f588Stnozaki }
391fe05f588Stnozaki 
392fe05f588Stnozaki static int
_citrus_UTF7_wcrtomb_priv(_UTF7EncodingInfo * __restrict ei,char * __restrict s,size_t n,wchar_t wchar,_UTF7State * __restrict psenc,size_t * __restrict nresult)393fe05f588Stnozaki _citrus_UTF7_wcrtomb_priv(_UTF7EncodingInfo * __restrict ei,
394fe05f588Stnozaki 	char * __restrict s, size_t n, wchar_t wchar,
395fe05f588Stnozaki 	_UTF7State * __restrict psenc, size_t * __restrict nresult)
396fe05f588Stnozaki {
397fe05f588Stnozaki 	uint32_t u32;
398fe05f588Stnozaki 	uint16_t u16[2];
399fe05f588Stnozaki 	int err, len, i;
400f264ea3aStnozaki 	size_t siz, nr;
401fe05f588Stnozaki 
402fe05f588Stnozaki 	_DIAGASSERT(ei != NULL);
403fe05f588Stnozaki 	_DIAGASSERT(s != NULL);
404fe05f588Stnozaki 	_DIAGASSERT(psenc != NULL);
405f264ea3aStnozaki 	_DIAGASSERT(nresult != NULL);
406fe05f588Stnozaki 
407fe05f588Stnozaki 	u32 = (uint32_t)wchar;
408fe05f588Stnozaki 	if (u32 <= UTF16_MAX) {
409fe05f588Stnozaki 		u16[0] = (uint16_t)u32;
410fe05f588Stnozaki 		len = 1;
411fe05f588Stnozaki 	} else if (u32 <= UTF32_MAX) {
412fe05f588Stnozaki 		u32 -= SRG_BASE;
413fe05f588Stnozaki 		u16[0] = (u32 >> 10) + HISRG_MIN;
414fe05f588Stnozaki 		u16[1] = ((uint16_t)(u32 & UINT32_C(0x3ff))) + LOSRG_MIN;
415fe05f588Stnozaki 		len = 2;
416fe05f588Stnozaki 	} else {
417fe05f588Stnozaki 		*nresult = (size_t)-1;
418f264ea3aStnozaki 		return EILSEQ;
419fe05f588Stnozaki 	}
420f264ea3aStnozaki 	siz = 0;
421f264ea3aStnozaki 	for (i = 0; i < len; ++i) {
422f264ea3aStnozaki 		err = _citrus_UTF7_utf16tomb(ei, s, n, u16[i], psenc, &nr);
423f264ea3aStnozaki 		if (err != 0)
424f264ea3aStnozaki 			return err; /* XXX: state has been modified */
425f264ea3aStnozaki 		s += nr;
426f264ea3aStnozaki 		n -= nr;
427f264ea3aStnozaki 		siz += nr;
428f264ea3aStnozaki 	}
429f264ea3aStnozaki 	*nresult = siz;
430fe05f588Stnozaki 
431f264ea3aStnozaki 	return 0;
432fe05f588Stnozaki }
433fe05f588Stnozaki 
434fe05f588Stnozaki static int
435fe05f588Stnozaki /* ARGSUSED */
_citrus_UTF7_put_state_reset(_UTF7EncodingInfo * __restrict ei,char * __restrict s,size_t n,_UTF7State * __restrict psenc,size_t * __restrict nresult)436fe05f588Stnozaki _citrus_UTF7_put_state_reset(_UTF7EncodingInfo * __restrict ei,
437fe05f588Stnozaki 	char * __restrict s, size_t n, _UTF7State * __restrict psenc,
438fe05f588Stnozaki 	size_t * __restrict nresult)
439fe05f588Stnozaki {
440fe05f588Stnozaki 	int bits, pos;
441fe05f588Stnozaki 
442fe05f588Stnozaki 	_DIAGASSERT(ei != NULL);
443fe05f588Stnozaki 	_DIAGASSERT(s != NULL);
444fe05f588Stnozaki 	_DIAGASSERT(psenc != NULL);
445fe05f588Stnozaki 	_DIAGASSERT(nresult != NULL);
446fe05f588Stnozaki 
4476e2609d6Stnozaki 	if (psenc->chlen != 0 || psenc->bits > BASE64_BIT || psenc->surrogate)
448f264ea3aStnozaki 		return EINVAL;
449fe05f588Stnozaki 
4506e2609d6Stnozaki 	if (psenc->mode) {
4516e2609d6Stnozaki 		if (psenc->bits > 0) {
452fe05f588Stnozaki 			if (n-- < 1)
453f264ea3aStnozaki 				return E2BIG;
4546e2609d6Stnozaki 			bits = BASE64_BIT - psenc->bits;
4556e2609d6Stnozaki 			pos = (psenc->cache << bits) & BASE64_MAX;
4566e2609d6Stnozaki 			psenc->ch[psenc->chlen++] = base64[pos];
4576e2609d6Stnozaki 			psenc->ch[psenc->chlen++] = BASE64_OUT;
4586e2609d6Stnozaki 			psenc->bits = psenc->cache = 0;
459fe05f588Stnozaki 		}
4606e2609d6Stnozaki 		psenc->mode = 0;
461fe05f588Stnozaki 	}
4626e2609d6Stnozaki 	if (psenc->bits != 0)
463f264ea3aStnozaki 		return EINVAL;
464fe05f588Stnozaki 	if (n-- < 1)
465f264ea3aStnozaki 		return E2BIG;
466fe05f588Stnozaki 
4676e2609d6Stnozaki 	_DIAGASSERT(n >= psenc->chlen);
4686e2609d6Stnozaki 	*nresult = (size_t)psenc->chlen;
4696e2609d6Stnozaki 	if (psenc->chlen > 0) {
4706e2609d6Stnozaki 		memcpy(s, psenc->ch, psenc->chlen);
4716e2609d6Stnozaki 		psenc->chlen = 0;
472fe05f588Stnozaki 	}
473fe05f588Stnozaki 
474f264ea3aStnozaki 	return 0;
475fe05f588Stnozaki }
476fe05f588Stnozaki 
477fe05f588Stnozaki static __inline int
478fe05f588Stnozaki /*ARGSUSED*/
_citrus_UTF7_stdenc_wctocs(_UTF7EncodingInfo * __restrict ei,_csid_t * __restrict csid,_index_t * __restrict idx,wchar_t wc)479fe05f588Stnozaki _citrus_UTF7_stdenc_wctocs(_UTF7EncodingInfo * __restrict ei,
480fe05f588Stnozaki 			   _csid_t * __restrict csid,
481fe05f588Stnozaki 			   _index_t * __restrict idx, wchar_t wc)
482fe05f588Stnozaki {
483fe05f588Stnozaki 	/* ei seem to be unused */
484fe05f588Stnozaki 	_DIAGASSERT(csid != NULL);
485fe05f588Stnozaki 	_DIAGASSERT(idx != NULL);
486fe05f588Stnozaki 
487fe05f588Stnozaki 	*csid = 0;
488fe05f588Stnozaki 	*idx = (_index_t)wc;
489fe05f588Stnozaki 
490f264ea3aStnozaki 	return 0;
491fe05f588Stnozaki }
492fe05f588Stnozaki 
493fe05f588Stnozaki static __inline int
494fe05f588Stnozaki /*ARGSUSED*/
_citrus_UTF7_stdenc_cstowc(_UTF7EncodingInfo * __restrict ei,wchar_t * __restrict wc,_csid_t csid,_index_t idx)495fe05f588Stnozaki _citrus_UTF7_stdenc_cstowc(_UTF7EncodingInfo * __restrict ei,
496fe05f588Stnozaki 			   wchar_t * __restrict wc,
497fe05f588Stnozaki 			   _csid_t csid, _index_t idx)
498fe05f588Stnozaki {
499fe05f588Stnozaki 	/* ei seem to be unused */
500fe05f588Stnozaki 	_DIAGASSERT(wc != NULL);
501fe05f588Stnozaki 
502fe05f588Stnozaki 	if (csid != 0)
503f264ea3aStnozaki 		return EILSEQ;
504fe05f588Stnozaki 	*wc = (wchar_t)idx;
505fe05f588Stnozaki 
506f264ea3aStnozaki 	return 0;
507fe05f588Stnozaki }
508fe05f588Stnozaki 
5091beef8feStshiozak static __inline int
5101beef8feStshiozak /*ARGSUSED*/
_citrus_UTF7_stdenc_get_state_desc_generic(_UTF7EncodingInfo * __restrict ei,_UTF7State * __restrict psenc,int * __restrict rstate)5111beef8feStshiozak _citrus_UTF7_stdenc_get_state_desc_generic(_UTF7EncodingInfo * __restrict ei,
5121beef8feStshiozak 					   _UTF7State * __restrict psenc,
5131beef8feStshiozak 					   int * __restrict rstate)
5141beef8feStshiozak {
5151beef8feStshiozak 
5161beef8feStshiozak 	if (psenc->chlen == 0)
5171beef8feStshiozak 		*rstate = _STDENC_SDGEN_INITIAL;
5181beef8feStshiozak 	else
5191beef8feStshiozak 		*rstate = _STDENC_SDGEN_INCOMPLETE_CHAR;
5201beef8feStshiozak 
5211beef8feStshiozak 	return 0;
5221beef8feStshiozak }
5231beef8feStshiozak 
524fe05f588Stnozaki static void
525fe05f588Stnozaki /*ARGSUSED*/
_citrus_UTF7_encoding_module_uninit(_UTF7EncodingInfo * ei)526fe05f588Stnozaki _citrus_UTF7_encoding_module_uninit(_UTF7EncodingInfo *ei)
527fe05f588Stnozaki {
528fe05f588Stnozaki 	/* ei seems to be unused */
529fe05f588Stnozaki }
530fe05f588Stnozaki 
531fe05f588Stnozaki static int
532fe05f588Stnozaki /*ARGSUSED*/
_citrus_UTF7_encoding_module_init(_UTF7EncodingInfo * __restrict ei,const void * __restrict var,size_t lenvar)533fe05f588Stnozaki _citrus_UTF7_encoding_module_init(_UTF7EncodingInfo * __restrict ei,
534fe05f588Stnozaki 				  const void * __restrict var, size_t lenvar)
535fe05f588Stnozaki {
536fe05f588Stnozaki 	const char *s;
537fe05f588Stnozaki 
538fe05f588Stnozaki 	_DIAGASSERT(ei != NULL);
539fe05f588Stnozaki 	/* var may be null */
540fe05f588Stnozaki 
541fe05f588Stnozaki 	memset(ei, 0, sizeof(*ei));
542fe05f588Stnozaki 
543fe05f588Stnozaki #define FILL(str, flag)				\
544fe05f588Stnozaki do {						\
545fe05f588Stnozaki 	for (s = str; *s != '\0'; s++)		\
546fe05f588Stnozaki 		ei->cell[*s & 0x7f] |= flag;	\
547*388550b0Srillig } while (0)
548fe05f588Stnozaki 
549fe05f588Stnozaki 	FILL(base64, (s - base64) + 1);
550fe05f588Stnozaki 	FILL(direct, EI_DIRECT);
551fe05f588Stnozaki 	FILL(option, EI_OPTION);
552fe05f588Stnozaki 	FILL(spaces, EI_SPACE);
553fe05f588Stnozaki 
554f264ea3aStnozaki 	return 0;
555fe05f588Stnozaki }
556fe05f588Stnozaki 
557fe05f588Stnozaki /* ----------------------------------------------------------------------
558fe05f588Stnozaki  * public interface for ctype
559fe05f588Stnozaki  */
560fe05f588Stnozaki 
561fe05f588Stnozaki _CITRUS_CTYPE_DECLS(UTF7);
562fe05f588Stnozaki _CITRUS_CTYPE_DEF_OPS(UTF7);
563fe05f588Stnozaki 
564fe05f588Stnozaki #include "citrus_ctype_template.h"
565fe05f588Stnozaki 
566fe05f588Stnozaki /* ----------------------------------------------------------------------
567fe05f588Stnozaki  * public interface for stdenc
568fe05f588Stnozaki  */
569fe05f588Stnozaki 
570fe05f588Stnozaki _CITRUS_STDENC_DECLS(UTF7);
571fe05f588Stnozaki _CITRUS_STDENC_DEF_OPS(UTF7);
572fe05f588Stnozaki 
573fe05f588Stnozaki #include "citrus_stdenc_template.h"
574