Home | History | Annotate | Line # | Download | only in modules
      1 /*	$NetBSD: citrus_utf7.c,v 1.9 2026/06/30 23:18:22 riastradh Exp $	*/
      2 
      3 /*-
      4  * Copyright (c)2004, 2005 Citrus Project,
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26  * SUCH DAMAGE.
     27  *
     28  */
     29 
     30 #include <sys/cdefs.h>
     31 #if defined(LIB_SCCS) && !defined(lint)
     32 __RCSID("$NetBSD: citrus_utf7.c,v 1.9 2026/06/30 23:18:22 riastradh Exp $");
     33 #endif /* LIB_SCCS and not lint */
     34 
     35 #include <assert.h>
     36 #include <errno.h>
     37 #include <string.h>
     38 #include <stdio.h>
     39 #include <stdint.h>
     40 #include <stdlib.h>
     41 #include <limits.h>
     42 #include <wchar.h>
     43 
     44 #include "citrus_namespace.h"
     45 #include "citrus_types.h"
     46 #include "citrus_module.h"
     47 #include "citrus_ctype.h"
     48 #include "citrus_stdenc.h"
     49 #include "citrus_utf7.h"
     50 
     51 /* ----------------------------------------------------------------------
     52  * private stuffs used by templates
     53  */
     54 
     55 typedef struct {
     56 	uint16_t	cell[0x80];
     57 #define	EI_MASK		UINT16_C(0xff)
     58 #define EI_DIRECT	UINT16_C(0x100)
     59 #define EI_OPTION	UINT16_C(0x200)
     60 #define EI_SPACE	UINT16_C(0x400)
     61 } _UTF7EncodingInfo;
     62 
     63 typedef struct {
     64 	unsigned int
     65 		mode: 1,	/* whether base64 mode */
     66 		bits: 4,	/* need to hold 0 - 15 */
     67 		cache: 22,	/* 22 = BASE64_BIT + UTF16_BIT */
     68 		surrogate: 1;	/* whether surrogate pair or not */
     69 	unsigned char chlen;
     70 	char ch[4]; /* BASE64_IN, 3 * 6 = 18, most closed to UTF16_BIT */
     71 } _UTF7State;
     72 
     73 __CTASSERT(__arraycount(((_UTF7State *)0)->ch) <= CHAR_MAX);
     74 
     75 typedef struct {
     76 	_UTF7EncodingInfo	ei;
     77 	struct {
     78 		/* for future multi-locale facility */
     79 		_UTF7State	s_mblen;
     80 		_UTF7State	s_mbrlen;
     81 		_UTF7State	s_mbrtowc;
     82 		_UTF7State	s_mbtowc;
     83 		_UTF7State	s_mbsrtowcs;
     84 		_UTF7State	s_mbsnrtowcs;
     85 		_UTF7State	s_wcrtomb;
     86 		_UTF7State	s_wcsrtombs;
     87 		_UTF7State	s_wcsnrtombs;
     88 		_UTF7State	s_wctomb;
     89 	} states;
     90 } _UTF7CTypeInfo;
     91 
     92 #define	_CEI_TO_EI(_cei_)		(&(_cei_)->ei)
     93 #define	_CEI_TO_STATE(_cei_, _func_)	(_cei_)->states.s_##_func_
     94 
     95 #define	_FUNCNAME(m)			_citrus_UTF7_##m
     96 #define	_ENCODING_INFO			_UTF7EncodingInfo
     97 #define	_CTYPE_INFO			_UTF7CTypeInfo
     98 #define	_ENCODING_STATE			_UTF7State
     99 #define	_ENCODING_MB_CUR_MAX(_ei_)		4
    100 #define	_ENCODING_IS_STATE_DEPENDENT		1
    101 #define	_STATE_NEEDS_EXPLICIT_INIT(_ps_)	0
    102 
    103 static __inline void
    104 /*ARGSUSED*/
    105 _citrus_UTF7_init_state(_UTF7EncodingInfo * __restrict ei,
    106 	_UTF7State * __restrict s)
    107 {
    108 	/* ei appears to be unused */
    109 	_DIAGASSERT(s != NULL);
    110 
    111 	memset((void *)s, 0, sizeof(*s));
    112 }
    113 
    114 static __inline void
    115 /*ARGSUSED*/
    116 _citrus_UTF7_pack_state(_UTF7EncodingInfo * __restrict ei,
    117 	void *__restrict pspriv, const _UTF7State * __restrict s)
    118 {
    119 	/* ei seem to be unused */
    120 	_DIAGASSERT(pspriv != NULL);
    121 	_DIAGASSERT(s != NULL);
    122 
    123 	memcpy(pspriv, (const void *)s, sizeof(*s));
    124 }
    125 
    126 static __inline void
    127 /*ARGSUSED*/
    128 _citrus_UTF7_unpack_state(_UTF7EncodingInfo * __restrict ei,
    129 	_UTF7State * __restrict s, const void * __restrict pspriv)
    130 {
    131 	/* ei seem to be unused */
    132 	_DIAGASSERT(s != NULL);
    133 	_DIAGASSERT(pspriv != NULL);
    134 
    135 	memcpy((void *)s, pspriv, sizeof(*s));
    136 }
    137 
    138 static const char base64[] =
    139 	"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    140 	"abcdefghijklmnopqrstuvwxyz"
    141 	"0123456789+/";
    142 
    143 static const char direct[] =
    144 	"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    145 	"abcdefghijklmnopqrstuvwxyz"
    146 	"0123456789(),-./:?";
    147 
    148 static const char option[] = "!\"#$%&';<=>@[]^_`{|}";
    149 static const char spaces[] = " \t\r\n";
    150 
    151 #define	BASE64_BIT	6
    152 #define	UTF16_BIT	16
    153 
    154 #define	BASE64_MAX	0x3f
    155 #define	UTF16_MAX	UINT16_C(0xffff)
    156 #define	UTF32_MAX	UINT32_C(0x10ffff)
    157 
    158 #define	BASE64_IN	'+'
    159 #define	BASE64_OUT	'-'
    160 
    161 #define	SHIFT7BIT(c)	((c) >> 7)
    162 #define	ISSPECIAL(c)	((c) == '\0' || (c) == BASE64_IN)
    163 
    164 #define	FINDLEN(ei, c) \
    165 	(SHIFT7BIT((c)) ? -1 : (((ei)->cell[(c)] & EI_MASK) - 1))
    166 
    167 #define	ISDIRECT(ei, c)	(!SHIFT7BIT((c)) && (ISSPECIAL((c)) || \
    168 	ei->cell[(c)] & (EI_DIRECT | EI_OPTION | EI_SPACE)))
    169 
    170 #define	ISSAFE(ei, c)	(!SHIFT7BIT((c)) && (ISSPECIAL((c)) || \
    171 	(c < 0x80 && ei->cell[(c)] & (EI_DIRECT | EI_SPACE))))
    172 
    173 /* surrogate pair */
    174 #define	SRG_BASE	UINT32_C(0x10000)
    175 #define	HISRG_MIN	UINT16_C(0xd800)
    176 #define	HISRG_MAX	UINT16_C(0xdbff)
    177 #define	LOSRG_MIN	UINT16_C(0xdc00)
    178 #define	LOSRG_MAX	UINT16_C(0xdfff)
    179 
    180 static int
    181 _citrus_UTF7_mbtoutf16(_UTF7EncodingInfo * __restrict ei,
    182 	uint16_t * __restrict u16, const char ** __restrict s, size_t n,
    183 	_UTF7State * __restrict psenc, size_t * __restrict nresult)
    184 {
    185 	_UTF7State sv;
    186 	const char *s0;
    187 	int i, done, len;
    188 
    189 	_DIAGASSERT(ei != NULL);
    190 	_DIAGASSERT(s != NULL && *s != NULL);
    191 	_DIAGASSERT(psenc != NULL);
    192 
    193 	s0 = *s;
    194 	sv = *psenc;
    195 
    196 	for (i = 0, done = 0; done == 0; i++) {
    197 		_DIAGASSERT(i < __arraycount(psenc->ch));
    198 		_DIAGASSERT(i <= psenc->chlen);
    199 		_DIAGASSERT(psenc->chlen <= __arraycount(psenc->ch));
    200 		if (i == psenc->chlen) {
    201 			if (n-- < 1) {
    202 				*nresult = (size_t)-2;
    203 				*s = s0;
    204 				sv.chlen = psenc->chlen;
    205 				*psenc = sv;
    206 				return 0;
    207 			}
    208 			_DIAGASSERT(psenc->chlen < __arraycount(psenc->ch));
    209 			psenc->ch[psenc->chlen++] = *s0++;
    210 		}
    211 		if (SHIFT7BIT((int)psenc->ch[i]))
    212 			goto ilseq;
    213 		if (!psenc->mode) {
    214 			if (psenc->bits > 0 || psenc->cache > 0)
    215 				return EINVAL;
    216 			if (psenc->ch[i] == BASE64_IN) {
    217 				psenc->mode = 1;
    218 			} else {
    219 				if (!ISDIRECT(ei, (int)psenc->ch[i]))
    220 					goto ilseq;
    221 				*u16 = (uint16_t)psenc->ch[i];
    222 				done = 1;
    223 				continue;
    224 			}
    225 		} else {
    226 			if (psenc->ch[i] == BASE64_OUT && psenc->cache == 0) {
    227 				psenc->mode = 0;
    228 				*u16 = (uint16_t)BASE64_IN;
    229 				done = 1;
    230 				continue;
    231 			}
    232 			len = FINDLEN(ei, (int)psenc->ch[i]);
    233 			if (len < 0) {
    234 				if (psenc->bits >= BASE64_BIT)
    235 					return EINVAL;
    236 				psenc->mode = 0;
    237 				psenc->bits = psenc->cache = 0;
    238 				if (psenc->ch[i] != BASE64_OUT) {
    239 					if (!ISDIRECT(ei, (int)psenc->ch[i]))
    240 						goto ilseq;
    241 					*u16 = (uint16_t)psenc->ch[i];
    242 					done = 1;
    243 				}
    244 			} else {
    245 				psenc->cache =
    246 				    (psenc->cache << BASE64_BIT) | len;
    247 				switch (psenc->bits) {
    248 				case 0: case 2: case 4: case 6: case 8:
    249 					psenc->bits += BASE64_BIT;
    250 					break;
    251 				case 10: case 12: case 14:
    252 					psenc->bits -= (UTF16_BIT - BASE64_BIT);
    253 					*u16 = (psenc->cache >> psenc->bits)
    254 					    & UTF16_MAX;
    255 					done = 1;
    256 					break;
    257 				default:
    258 					return EINVAL;
    259 				}
    260 			}
    261 		}
    262 	}
    263 
    264 	if (psenc->chlen > i)
    265 		return EINVAL;
    266 	psenc->chlen = 0;
    267 	*nresult = (size_t)((*u16 == 0) ? 0 : s0 - *s);
    268 	*s = s0;
    269 
    270 	return 0;
    271 
    272 ilseq:
    273 	*nresult = (size_t)-1;
    274 	return EILSEQ;
    275 }
    276 
    277 static int
    278 _citrus_UTF7_mbrtowc_priv(_UTF7EncodingInfo * __restrict ei,
    279 	wchar_t * __restrict pwc, const char ** __restrict s, size_t n,
    280 	_UTF7State * __restrict psenc, size_t * __restrict nresult)
    281 {
    282 	const char *s0;
    283 	uint32_t u32;
    284 	uint16_t hi, lo;
    285 	size_t siz, nr;
    286 	int err;
    287 
    288 	_DIAGASSERT(ei != NULL);
    289 	/* pwc may be null */
    290 	_DIAGASSERT(s != NULL);
    291 	_DIAGASSERT(psenc != NULL);
    292 
    293 	if (*s == NULL) {
    294 		_citrus_UTF7_init_state(ei, psenc);
    295 		*nresult = (size_t)_ENCODING_IS_STATE_DEPENDENT;
    296 		return 0;
    297 	}
    298 	s0 = *s;
    299 	if (psenc->surrogate) {
    300 		hi = (psenc->cache >> 2) & UTF16_MAX;
    301 		if (hi < HISRG_MIN || hi > HISRG_MAX)
    302 			return EINVAL;
    303 		siz = 0;
    304 	} else {
    305 		err = _citrus_UTF7_mbtoutf16(ei, &hi, &s0, n, psenc, &nr);
    306 		if (nr == (size_t)-1 || nr == (size_t)-2) {
    307 			*nresult = nr;
    308 			return err;
    309 		}
    310 		if (err != 0)
    311 			return err;
    312 		n -= nr;
    313 		siz = nr;
    314 		if (hi < HISRG_MIN || hi > HISRG_MAX) {
    315 			u32 = (uint32_t)hi;
    316 			goto done;
    317 		}
    318 		psenc->surrogate = 1;
    319 	}
    320 	err = _citrus_UTF7_mbtoutf16(ei, &lo, &s0, n, psenc, &nr);
    321 	if (nr == (size_t)-1 || nr == (size_t)-2) {
    322 		*nresult = nr;
    323 		return err;
    324 	}
    325 	if (err != 0)
    326 		return err;
    327 	hi -= HISRG_MIN;
    328 	lo -= LOSRG_MIN;
    329 	u32 = (hi << 10 | lo) + SRG_BASE;
    330 	siz += nr;
    331 done:
    332 	*s = s0;
    333 	if (pwc != NULL)
    334 		*pwc = (wchar_t)u32;
    335 	if (u32 == (uint32_t)0) {
    336 		*nresult = (size_t)0;
    337 		_citrus_UTF7_init_state(ei, psenc);
    338 	} else {
    339 		*nresult = siz;
    340 		psenc->surrogate = 0;
    341 	}
    342 	return err;
    343 }
    344 
    345 static int
    346 _citrus_UTF7_utf16tomb(_UTF7EncodingInfo * __restrict ei,
    347 	char * __restrict s, size_t n, uint16_t u16,
    348 	_UTF7State * __restrict psenc, size_t * __restrict nresult)
    349 {
    350 	int bits, i;
    351 
    352 	_DIAGASSERT(ei != NULL);
    353 	_DIAGASSERT(psenc != NULL);
    354 
    355 	if (psenc->chlen != 0 || psenc->bits > BASE64_BIT)
    356 		return EINVAL;
    357 
    358 	if (ISSAFE(ei, u16)) {
    359 		if (psenc->mode) {
    360 			if (psenc->bits > 0) {
    361 				bits = BASE64_BIT - psenc->bits;
    362 				i = (psenc->cache << bits) & BASE64_MAX;
    363 				_DIAGASSERT(psenc->chlen <
    364 				    __arraycount(psenc->ch));
    365 				psenc->ch[psenc->chlen++] = base64[i];
    366 				psenc->bits = psenc->cache = 0;
    367 			}
    368 			if (u16 == BASE64_OUT || FINDLEN(ei, u16) >= 0) {
    369 				_DIAGASSERT(psenc->chlen <
    370 				    __arraycount(psenc->ch));
    371 				psenc->ch[psenc->chlen++] = BASE64_OUT;
    372 			}
    373 			psenc->mode = 0;
    374 		}
    375 		if (psenc->bits != 0)
    376 			return EINVAL;
    377 		_DIAGASSERT(psenc->chlen < __arraycount(psenc->ch));
    378 		psenc->ch[psenc->chlen++] = (char)u16;
    379 		if (u16 == BASE64_IN) {
    380 			_DIAGASSERT(psenc->chlen < __arraycount(psenc->ch));
    381 			psenc->ch[psenc->chlen++] = BASE64_OUT;
    382 		}
    383 	} else {
    384 		if (!psenc->mode) {
    385 			if (psenc->bits > 0)
    386 				return EINVAL;
    387 			_DIAGASSERT(psenc->chlen < __arraycount(psenc->ch));
    388 			psenc->ch[psenc->chlen++] = BASE64_IN;
    389 			psenc->mode = 1;
    390 		}
    391 		psenc->cache = (psenc->cache << UTF16_BIT) | u16;
    392 		bits = UTF16_BIT + psenc->bits;
    393 		psenc->bits = bits % BASE64_BIT;
    394 		while ((bits -= BASE64_BIT) >= 0) {
    395 			i = (psenc->cache >> bits) & BASE64_MAX;
    396 			_DIAGASSERT(psenc->chlen < __arraycount(psenc->ch));
    397 			psenc->ch[psenc->chlen++] = base64[i];
    398 		}
    399 	}
    400 	if (n < (size_t)psenc->chlen)
    401 		return E2BIG;
    402 	_DIAGASSERT(psenc->chlen <= sizeof(psenc->ch));
    403 	memcpy(s, psenc->ch, psenc->chlen);
    404 	*nresult = psenc->chlen;
    405 	psenc->chlen = 0;
    406 
    407 	return 0;
    408 }
    409 
    410 static int
    411 _citrus_UTF7_wcrtomb_priv(_UTF7EncodingInfo * __restrict ei,
    412 	char * __restrict s, size_t n, wchar_t wchar,
    413 	_UTF7State * __restrict psenc, size_t * __restrict nresult)
    414 {
    415 	uint32_t u32;
    416 	uint16_t u16[2];
    417 	int err, len, i;
    418 	size_t siz, nr;
    419 	char buf[8], *bufp = buf;
    420 	_UTF7State psenc_save = *psenc;
    421 
    422 	_DIAGASSERT(ei != NULL);
    423 	_DIAGASSERT(s != NULL);
    424 	_DIAGASSERT(psenc != NULL);
    425 	_DIAGASSERT(nresult != NULL);
    426 
    427 	u32 = (uint32_t)wchar;
    428 	if (u32 <= UTF16_MAX) {
    429 		u16[0] = (uint16_t)u32;
    430 		len = 1;
    431 	} else if (u32 <= UTF32_MAX) {
    432 		u32 -= SRG_BASE;
    433 		u16[0] = (u32 >> 10) + HISRG_MIN;
    434 		u16[1] = ((uint16_t)(u32 & UINT32_C(0x3ff))) + LOSRG_MIN;
    435 		len = 2;
    436 	} else {
    437 		*nresult = (size_t)-1;
    438 		return EILSEQ;
    439 	}
    440 	siz = 0;
    441 	if (n > sizeof(buf))	/* paranoia */
    442 		n = sizeof(buf);
    443 	for (i = 0; i < len; ++i) {
    444 		err = _citrus_UTF7_utf16tomb(ei, bufp, n, u16[i], psenc, &nr);
    445 		if (err != 0) {
    446 			*nresult = (size_t)-1;
    447 			*psenc = psenc_save; /* restore state */
    448 			return err;
    449 		}
    450 		bufp += nr;
    451 		n -= nr;
    452 		siz += nr;
    453 	}
    454 	_DIAGASSERT(siz <= sizeof(buf));
    455 	memcpy(s, buf, siz);
    456 	*nresult = siz;
    457 
    458 	return 0;
    459 }
    460 
    461 static int
    462 /* ARGSUSED */
    463 _citrus_UTF7_put_state_reset(_UTF7EncodingInfo * __restrict ei,
    464 	char * __restrict s, size_t n, _UTF7State * __restrict psenc,
    465 	size_t * __restrict nresult)
    466 {
    467 	int bits, pos;
    468 
    469 	_DIAGASSERT(ei != NULL);
    470 	_DIAGASSERT(s != NULL);
    471 	_DIAGASSERT(psenc != NULL);
    472 	_DIAGASSERT(nresult != NULL);
    473 
    474 	if (psenc->chlen != 0 || psenc->bits > BASE64_BIT || psenc->surrogate)
    475 		return EINVAL;
    476 
    477 	if (psenc->mode) {
    478 		if (psenc->bits > 0) {
    479 			if (n-- < 1)
    480 				return E2BIG;
    481 			bits = BASE64_BIT - psenc->bits;
    482 			pos = (psenc->cache << bits) & BASE64_MAX;
    483 			_DIAGASSERT(psenc->chlen < __arraycount(psenc->ch));
    484 			psenc->ch[psenc->chlen++] = base64[pos];
    485 			_DIAGASSERT(psenc->chlen < __arraycount(psenc->ch));
    486 			psenc->ch[psenc->chlen++] = BASE64_OUT;
    487 			psenc->bits = psenc->cache = 0;
    488 		}
    489 		psenc->mode = 0;
    490 	}
    491 	if (psenc->bits != 0)
    492 		return EINVAL;
    493 	if (n-- < 1)
    494 		return E2BIG;
    495 
    496 	_DIAGASSERT(n >= psenc->chlen);
    497 	*nresult = (size_t)psenc->chlen;
    498 	_DIAGASSERT(psenc->chlen <= sizeof(psenc->ch));
    499 	if (psenc->chlen > 0) {
    500 		memcpy(s, psenc->ch, psenc->chlen);
    501 		psenc->chlen = 0;
    502 	}
    503 
    504 	return 0;
    505 }
    506 
    507 static __inline int
    508 /*ARGSUSED*/
    509 _citrus_UTF7_stdenc_wctocs(_UTF7EncodingInfo * __restrict ei,
    510 			   _csid_t * __restrict csid,
    511 			   _index_t * __restrict idx, wchar_t wc)
    512 {
    513 	/* ei seem to be unused */
    514 	_DIAGASSERT(csid != NULL);
    515 	_DIAGASSERT(idx != NULL);
    516 
    517 	*csid = 0;
    518 	*idx = (_index_t)wc;
    519 
    520 	return 0;
    521 }
    522 
    523 static __inline int
    524 /*ARGSUSED*/
    525 _citrus_UTF7_stdenc_cstowc(_UTF7EncodingInfo * __restrict ei,
    526 			   wchar_t * __restrict wc,
    527 			   _csid_t csid, _index_t idx)
    528 {
    529 	/* ei seem to be unused */
    530 	_DIAGASSERT(wc != NULL);
    531 
    532 	if (csid != 0)
    533 		return EILSEQ;
    534 	*wc = (wchar_t)idx;
    535 
    536 	return 0;
    537 }
    538 
    539 static __inline int
    540 /*ARGSUSED*/
    541 _citrus_UTF7_stdenc_get_state_desc_generic(_UTF7EncodingInfo * __restrict ei,
    542 					   _UTF7State * __restrict psenc,
    543 					   int * __restrict rstate)
    544 {
    545 
    546 	if (psenc->chlen == 0)
    547 		*rstate = _STDENC_SDGEN_INITIAL;
    548 	else
    549 		*rstate = _STDENC_SDGEN_INCOMPLETE_CHAR;
    550 
    551 	return 0;
    552 }
    553 
    554 static void
    555 /*ARGSUSED*/
    556 _citrus_UTF7_encoding_module_uninit(_UTF7EncodingInfo *ei)
    557 {
    558 	/* ei seems to be unused */
    559 }
    560 
    561 static int
    562 /*ARGSUSED*/
    563 _citrus_UTF7_encoding_module_init(_UTF7EncodingInfo * __restrict ei,
    564 				  const void * __restrict var, size_t lenvar)
    565 {
    566 	const char *s;
    567 
    568 	_DIAGASSERT(ei != NULL);
    569 	/* var may be null */
    570 
    571 	memset(ei, 0, sizeof(*ei));
    572 
    573 #define FILL(str, flag)				\
    574 do {						\
    575 	for (s = str; *s != '\0'; s++)		\
    576 		ei->cell[*s & 0x7f] |= flag;	\
    577 } while (0)
    578 
    579 	FILL(base64, (s - base64) + 1);
    580 	FILL(direct, EI_DIRECT);
    581 	FILL(option, EI_OPTION);
    582 	FILL(spaces, EI_SPACE);
    583 
    584 	return 0;
    585 }
    586 
    587 /* ----------------------------------------------------------------------
    588  * public interface for ctype
    589  */
    590 
    591 _CITRUS_CTYPE_DECLS(UTF7);
    592 _CITRUS_CTYPE_DEF_OPS(UTF7);
    593 
    594 #include "citrus_ctype_template.h"
    595 
    596 /* ----------------------------------------------------------------------
    597  * public interface for stdenc
    598  */
    599 
    600 _CITRUS_STDENC_DECLS(UTF7);
    601 _CITRUS_STDENC_DEF_OPS(UTF7);
    602 
    603 #include "citrus_stdenc_template.h"
    604