1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Auto-reclaimed string allocation and support routines that build on top of
3 *@ them. Strings handed out by those are reclaimed at the top of the command
4 *@ loop each time, so they need not be freed.
5 *@ And below this series we do collect all other plain string support routines
6 *@ in here, including those which use normal heap memory.
8 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
9 * Copyright (c) 2012 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
12 * Copyright (c) 1980, 1993
13 * The Regents of the University of California. All rights reserved.
15 * Redistribution and use in source and binary forms, with or without
16 * modification, are permitted provided that the following conditions
18 * 1. Redistributions of source code must retain the above copyright
19 * notice, this list of conditions and the following disclaimer.
20 * 2. Redistributions in binary form must reproduce the above copyright
21 * notice, this list of conditions and the following disclaimer in the
22 * documentation and/or other materials provided with the distribution.
23 * 3. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40 #define n_FILE strings
42 #ifndef HAVE_AMALGAMATION
48 /* In debug mode the "string dope" allocations are enwrapped in canaries, just
49 * as we do with our normal memory allocator */
51 # define _SHOPE_SIZE (2u * 8 * sizeof(char) + sizeof(struct schunk))
53 CTA(sizeof(char) == sizeof(ui8_t
));
68 #endif /* HAVE_DEBUG */
75 #define SALIGN (sizeof(union __align__) - 1)
77 CTA(ISPOW2(SALIGN
+ 1));
81 char *_bot
; /* For spreserve() */
82 char *_relax
; /* If !NULL, used by srelax() instead of ._bot */
83 char *_max
; /* Max usable byte */
84 char *_caster
; /* NULL if full */
87 /* Single instance builtin buffer. Room for anything, most of the time */
90 char b_buf
[SBUFFER_BUILTIN
- sizeof(struct b_base
)];
92 #define SBLTIN_SIZE SIZEOF_FIELD(struct b_bltin, b_buf)
94 /* Dynamically allocated buffers to overcome shortage, always released again
95 * once the command loop ticks (without relaxation or during PS_SOURCING) */
98 char b_buf
[SBUFFER_SIZE
- sizeof(struct b_base
)];
100 #define SDYN_SIZE SIZEOF_FIELD(struct b_dyn, b_buf)
102 /* The multiplexer of the several real b_* */
105 char b_buf
[VFIELD_SIZE(SALIGN
+ 1)];
108 /* Requests that exceed SDYN_SIZE-1 and thus cannot be handled by string dope
109 * are always served by the normal memory allocator (which panics if memory
110 * cannot be served). Note such an allocation has not yet occurred, it is only
111 * included as a security fallback bypass */
113 struct hugebuf
*hb_next
;
114 char hb_buf
[VFIELD_SIZE(SALIGN
+ 1)];
117 static struct b_bltin _builtin_buf
;
118 static struct buffer
*_buf_head
, *_buf_list
, *_buf_server
, *_buf_relax
;
119 static size_t _relax_recur_no
;
120 static struct hugebuf
*_huge_list
;
122 static size_t _all_cnt
, _all_cycnt
, _all_cycnt_max
,
123 _all_size
, _all_cysize
, _all_cysize_max
, _all_min
,
125 _all_bufcnt
, _all_cybufcnt
, _all_cybufcnt_max
,
126 _all_resetreqs
, _all_resets
;
129 /* sreset() / srelax() release a buffer, check the canaries of all chunks */
131 static void _salloc_bcheck(struct buffer
*b
);
136 _salloc_bcheck(struct buffer
*b
)
141 pmax
.cp
= (b
->b
._caster
== NULL
) ? b
->b
._max
: b
->b
._caster
;
144 while (pp
.cp
< pmax
.cp
) {
151 pp
.cp
+= c
->full_size
;
156 if (x
.ui8p
[0] != 0xDE) i
|= 1<<0;
157 if (x
.ui8p
[1] != 0xAA) i
|= 1<<1;
158 if (x
.ui8p
[2] != 0x55) i
|= 1<<2;
159 if (x
.ui8p
[3] != 0xAD) i
|= 1<<3;
160 if (x
.ui8p
[4] != 0xBE) i
|= 1<<4;
161 if (x
.ui8p
[5] != 0x55) i
|= 1<<5;
162 if (x
.ui8p
[6] != 0xAA) i
|= 1<<6;
163 if (x
.ui8p
[7] != 0xEF) i
|= 1<<7;
165 n_alert("sdope %p: corrupt lower canary: 0x%02X, size %u: %s, line %u",
166 ux
, i
, c
->usr_size
, c
->file
, c
->line
);
167 x
.cp
+= 8 + c
->usr_size
;
170 if (x
.ui8p
[0] != 0xDE) i
|= 1<<0;
171 if (x
.ui8p
[1] != 0xAA) i
|= 1<<1;
172 if (x
.ui8p
[2] != 0x55) i
|= 1<<2;
173 if (x
.ui8p
[3] != 0xAD) i
|= 1<<3;
174 if (x
.ui8p
[4] != 0xBE) i
|= 1<<4;
175 if (x
.ui8p
[5] != 0x55) i
|= 1<<5;
176 if (x
.ui8p
[6] != 0xAA) i
|= 1<<6;
177 if (x
.ui8p
[7] != 0xEF) i
|= 1<<7;
179 n_alert("sdope %p: corrupt upper canary: 0x%02X, size %u: %s, line %u",
180 ux
, i
, c
->usr_size
, c
->file
, c
->line
);
187 (salloc
)(size_t size SALLOC_DEBUG_ARGS
)
189 DBG( size_t orig_size
= size
; )
190 union {struct buffer
*b
; struct hugebuf
*hb
; char *cp
;} u
;
202 _all_cycnt_max
= MAX(_all_cycnt_max
, _all_cycnt
);
205 _all_cysize_max
= MAX(_all_cysize_max
, _all_cysize
);
206 _all_min
= (_all_max
== 0) ? size
: MIN(_all_min
, size
);
207 _all_max
= MAX(_all_max
, size
);
208 _all_wast
+= size
- orig_size
;
212 if (size
>= SDYN_SIZE
- 1)
213 n_alert("salloc() of %" PRIuZ
" bytes from \"%s\", line %d",
214 size
, mdbg_file
, mdbg_line
);
217 /* Huge allocations are special */
218 if (UNLIKELY(size
>= SDYN_SIZE
- 1))
221 /* Search for a buffer with enough free space to serve request */
222 if ((u
.b
= _buf_server
) != NULL
)
225 for (u
.b
= _buf_head
; u
.b
!= NULL
; u
.b
= u
.b
->b
._next
) {
229 if (u
.b
== _buf_server
) {
230 if (u
.b
== _buf_head
&& (u
.b
= _buf_head
->b
._next
) != NULL
) {
241 if (PTRCMP(y
, <=, z
)) {
242 /* Alignment is the one thing, the other is what is usually allocated,
243 * and here about 40 bytes seems to be a good cut to avoid non-usable
244 * non-NULL casters. However, because of _salloc_bcheck(), we may not
245 * set ._caster to NULL because then it would check all chunks up to
246 * ._max, which surely doesn't work; speed is no issue with DEBUG */
247 u
.b
->b
._caster
= NDBG( PTRCMP(y
+ 42 + 16, >=, z
) ? NULL
: ) y
;
253 /* Need a new buffer */
254 if (_buf_head
== NULL
) {
255 struct b_bltin
*b
= &_builtin_buf
;
256 b
->b_base
._max
= b
->b_buf
+ SBLTIN_SIZE
- 1;
257 _buf_head
= (struct buffer
*)b
;
263 _all_cybufcnt_max
= MAX(_all_cybufcnt_max
, _all_cybufcnt
);
265 u
.b
= smalloc(sizeof(struct b_dyn
));
266 u
.b
->b
._max
= u
.b
->b_buf
+ SDYN_SIZE
- 1;
268 if (_buf_list
!= NULL
)
269 _buf_list
->b
._next
= u
.b
;
270 _buf_server
= _buf_list
= u
.b
;
272 u
.b
->b
._caster
= (u
.b
->b
._bot
= u
.b
->b_buf
) + size
;
273 u
.b
->b
._relax
= NULL
;
277 /* Encapsulate user chunk in debug canaries */
285 xc
->file
= mdbg_file
;
286 xc
->line
= mdbg_line
;
287 xc
->usr_size
= (ui16_t
)orig_size
;
288 xc
->full_size
= (ui16_t
)size
;
290 xl
.ui8p
[0]=0xDE; xl
.ui8p
[1]=0xAA; xl
.ui8p
[2]=0x55; xl
.ui8p
[3]=0xAD;
291 xl
.ui8p
[4]=0xBE; xl
.ui8p
[5]=0x55; xl
.ui8p
[6]=0xAA; xl
.ui8p
[7]=0xEF;
295 xu
.ui8p
[0]=0xDE; xu
.ui8p
[1]=0xAA; xu
.ui8p
[2]=0x55; xu
.ui8p
[3]=0xAD;
296 xu
.ui8p
[4]=0xBE; xu
.ui8p
[5]=0x55; xu
.ui8p
[6]=0xAA; xu
.ui8p
[7]=0xEF;
303 u
.hb
= smalloc(sizeof(*u
.hb
) - VFIELD_SIZEOF(struct hugebuf
, hb_buf
) +
305 u
.hb
->hb_next
= _huge_list
;
312 (csalloc
)(size_t nmemb
, size_t size SALLOC_DEBUG_ARGS
)
318 vp
= (salloc
)(size SALLOC_DEBUG_ARGSCALL
);
325 sreset(bool_t only_if_relaxed
)
327 struct buffer
*blh
, *bh
;
330 DBG( ++_all_resetreqs
; )
332 /* Reset relaxation after any jump is a MUST */
333 if (_relax_recur_no
> 0)
337 if (only_if_relaxed
&& _relax_recur_no
== 0)
341 _all_cycnt
= _all_cysize
= 0;
342 _all_cybufcnt
= (_buf_head
!= NULL
&& _buf_head
->b
._next
!= NULL
);
346 /* Reset relaxation after jump */
347 if (_relax_recur_no
> 0) {
349 assert(_relax_recur_no
== 0);
353 if ((bh
= _buf_head
) != NULL
) {
355 struct buffer
*x
= bh
;
357 DBG( _salloc_bcheck(x
); )
359 /* Give away all buffers that are not covered by sreset().
360 * _buf_head is builtin and thus cannot be free()d */
361 if (blh
!= NULL
&& x
->b
._bot
== x
->b_buf
) {
366 x
->b
._caster
= x
->b
._bot
;
368 DBG( memset(x
->b
._caster
, 0377,
369 PTR2SIZE(x
->b
._max
- x
->b
._caster
)); )
371 } while (bh
!= NULL
);
373 _buf_server
= _buf_head
;
378 while (_huge_list
!= NULL
) {
379 struct hugebuf
*hb
= _huge_list
;
380 _huge_list
= hb
->hb_next
;
395 if (_relax_recur_no
++ == 0) {
396 for (b
= _buf_head
; b
!= NULL
; b
= b
->b
._next
)
397 b
->b
._relax
= b
->b
._caster
;
398 _buf_relax
= _buf_server
;
409 assert(_relax_recur_no
> 0);
411 if (--_relax_recur_no
== 0) {
412 for (b
= _buf_head
; b
!= NULL
; b
= b
->b
._next
) {
413 DBG( _salloc_bcheck(b
); )
414 b
->b
._caster
= (b
->b
._relax
!= NULL
) ? b
->b
._relax
: b
->b
._bot
;
422 n_err("srelax_rele(): recursion >0!\n");
430 /* The purpose of relaxation is only that it is possible to reset the
431 * casters, *not* to give back memory to the system. We are presumably in
432 * an iteration over all messages of a mailbox, and it'd be quite
433 * counterproductive to give the system allocator a chance to waste time */
437 assert(_relax_recur_no
> 0);
439 if (_relax_recur_no
== 1) {
440 for (b
= _buf_head
; b
!= NULL
; b
= b
->b
._next
) {
441 DBG( _salloc_bcheck(b
); )
442 b
->b
._caster
= (b
->b
._relax
!= NULL
) ? b
->b
._relax
: b
->b
._bot
;
443 DBG( memset(b
->b
._caster
, 0377, PTR2SIZE(b
->b
._max
- b
->b
._caster
)); )
455 for (b
= _buf_head
; b
!= NULL
; b
= b
->b
._next
)
456 b
->b
._bot
= b
->b
._caster
;
468 excess
= (_all_cybufcnt_max
* SDYN_SIZE
) + SBLTIN_SIZE
;
469 excess
= (excess
>= _all_cysize_max
) ? 0 : _all_cysize_max
- excess
;
471 printf("String usage statistics (cycle means one sreset() cycle):\n"
472 " Buffer allocs ever/max a time : %" PRIuZ
"/%" PRIuZ
"\n"
473 " .. size of the builtin/dynamic: %" PRIuZ
"/%" PRIuZ
"\n"
474 " Overall alloc count/bytes : %" PRIuZ
"/%" PRIuZ
"\n"
475 " .. bytes min/max/align wastage: %" PRIuZ
"/%" PRIuZ
"/%" PRIuZ
"\n"
476 " sreset() cycles : %" PRIuZ
" (%" PRIuZ
" performed)\n"
477 " Cycle max.: alloc count/bytes : %" PRIuZ
"/%" PRIuZ
"+%" PRIuZ
"\n",
478 _all_bufcnt
, _all_cybufcnt_max
,
479 SBLTIN_SIZE
, SDYN_SIZE
,
481 _all_min
, _all_max
, _all_wast
,
482 _all_resetreqs
, _all_resets
,
483 _all_cycnt_max
, _all_cysize_max
, excess
);
490 (savestr
)(char const *str SALLOC_DEBUG_ARGS
)
496 size
= strlen(str
) +1;
497 news
= (salloc
)(size SALLOC_DEBUG_ARGSCALL
);
498 memcpy(news
, str
, size
);
504 (savestrbuf
)(char const *sbuf
, size_t sbuf_len SALLOC_DEBUG_ARGS
)
509 news
= (salloc
)(sbuf_len
+1 SALLOC_DEBUG_ARGSCALL
);
510 memcpy(news
, sbuf
, sbuf_len
);
517 (savecatsep
)(char const *s1
, char sep
, char const *s2 SALLOC_DEBUG_ARGS
)
523 l1
= (s1
!= NULL
) ? strlen(s1
) : 0;
525 news
= (salloc
)(l1
+ (sep
!= '\0') + l2
+1 SALLOC_DEBUG_ARGSCALL
);
527 memcpy(news
+ 0, s1
, l1
);
531 memcpy(news
+ l1
, s2
, l2
);
532 news
[l1
+ l2
] = '\0';
538 * Support routines, auto-reclaimed storage
542 (i_strdup
)(char const *src SALLOC_DEBUG_ARGS
)
549 dest
= (salloc
)(sz SALLOC_DEBUG_ARGSCALL
);
550 i_strcpy(dest
, src
, sz
);
556 str_concat_csvl(struct str
*self
, ...) /* XXX onepass maybe better here */
564 for (l
= 0; (cs
= va_arg(vl
, char const*)) != NULL
;)
569 self
->s
= salloc(l
+1);
572 for (l
= 0; (cs
= va_arg(vl
, char const*)) != NULL
;) {
573 size_t i
= strlen(cs
);
574 memcpy(self
->s
+ l
, cs
, i
);
584 (str_concat_cpa
)(struct str
*self
, char const * const *cpa
,
585 char const *sep_o_null SALLOC_DEBUG_ARGS
)
588 char const * const *xcpa
;
591 sonl
= (sep_o_null
!= NULL
) ? strlen(sep_o_null
) : 0;
593 for (l
= 0, xcpa
= cpa
; *xcpa
!= NULL
; ++xcpa
)
594 l
+= strlen(*xcpa
) + sonl
;
597 self
->s
= (salloc
)(l
+1 SALLOC_DEBUG_ARGSCALL
);
599 for (l
= 0, xcpa
= cpa
; *xcpa
!= NULL
; ++xcpa
) {
600 size_t i
= strlen(*xcpa
);
601 memcpy(self
->s
+ l
, *xcpa
, i
);
604 memcpy(self
->s
+ l
, sep_o_null
, sonl
);
614 * Routines that are not related to auto-reclaimed storage follow.
618 anyof(char const *s1
, char const *s2
)
621 for (; *s1
!= '\0'; ++s1
)
622 if (strchr(s2
, *s1
) != NULL
)
625 return (*s1
!= '\0');
629 n_strsep(char **iolist
, char sep
, bool_t ignore_empty
)
634 for (base
= *iolist
; base
!= NULL
; base
= *iolist
) {
635 while (*base
!= '\0' && blankspacechar(*base
))
637 cp
= strchr(base
, sep
);
642 cp
= base
+ strlen(base
);
644 while (cp
> base
&& blankspacechar(cp
[-1]))
647 if (*base
!= '\0' || !ignore_empty
)
655 i_strcpy(char *dest
, char const *src
, size_t size
)
659 for (;; ++dest
, ++src
)
660 if ((*dest
= lowerconv(*src
)) == '\0') {
662 } else if (--size
== 0) {
671 is_prefix(char const *as1
, char const *as2
)
676 for (; (c
= *as1
) == *as2
&& c
!= '\0'; ++as1
, ++as2
)
684 string_quote(char const *v
) /* TODO too simpleminded (getrawlist(), +++ ..) */
691 for (i
= 0, cp
= v
; (c
= *cp
) != '\0'; ++i
, ++cp
)
692 if (c
== '"' || c
== '\\')
696 for (i
= 0, cp
= v
; (c
= *cp
) != '\0'; rv
[i
++] = c
, ++cp
)
697 if (c
== '"' || c
== '\\')
705 laststring(char *linebuf
, bool_t
*needs_list
, bool_t strip
)
707 char *cp
, *p
, quoted
;
710 /* Anything to do at all? */
711 if (*(cp
= linebuf
) == '\0')
713 cp
+= strlen(linebuf
) -1;
715 /* Strip away trailing blanks */
716 while (whitechar(*cp
) && cp
> linebuf
)
722 /* Now search for the BOS of the "last string" */
724 if (quoted
== '\'' || quoted
== '"') {
730 while (cp
> linebuf
) {
735 } else if (!whitechar(*cp
))
737 if (cp
== linebuf
|| cp
[-1] != '\\') {
738 /* When in whitespace mode, WS prefix doesn't belong */
743 /* Expand the escaped quote character */
744 for (p
= --cp
; (p
[0] = p
[1]) != '\0'; ++p
)
747 if (strip
&& quoted
!= ' ' && *cp
== quoted
)
748 for (p
= cp
; (p
[0] = p
[1]) != '\0'; ++p
)
751 /* The "last string" has been skipped over, but still, try to step backwards
752 * until we are at BOS or see whitespace, so as to make possible things like
753 * "? copy +'x y.mbox'" or even "? copy +x\ y.mbox" */
754 while (cp
> linebuf
) {
756 if (whitechar(*cp
)) {
759 /* We can furtherly release our callees if we now decide wether the
760 * remaining non-"last string" line content contains non-WS */
761 while (--p
>= linebuf
)
770 if (cp
!= NULL
&& *cp
== '\0')
772 *needs_list
= (cp
!= linebuf
&& *linebuf
!= '\0');
783 makelow(char *cp
) /* TODO isn't that crap? --> */
786 #ifdef HAVE_C90AMEND1
787 if (mb_cur_max
> 1) {
792 while (*cp
!= '\0') {
793 len
= mbtowc(&wc
, cp
, mb_cur_max
);
798 if (wctomb(tp
, wc
) == len
)
799 tp
+= len
, cp
+= len
;
801 *tp
++ = *cp
++; /* <-- at least here */
808 *cp
= tolower((uc_i
)*cp
);
809 while (*cp
++ != '\0');
815 substr(char const *str
, char const *sub
)
817 char const *cp
, *backup
;
822 while (*str
!= '\0' && *cp
!= '\0') {
823 #ifdef HAVE_C90AMEND1
824 if (mb_cur_max
> 1) {
828 if ((sz
= mbtowc(&c
, cp
, mb_cur_max
)) == -1)
831 if ((sz
= mbtowc(&c2
, str
, mb_cur_max
)) == -1)
837 if ((sz
= mbtowc(&c
, backup
, mb_cur_max
)) > 0) {
863 return (*cp
== '\0');
867 sstpcpy(char *dst
, char const *src
)
870 while ((*dst
= *src
++) != '\0')
877 (sstrdup
)(char const *cp SMALLOC_DEBUG_ARGS
)
882 dp
= (cp
== NULL
) ? NULL
: (sbufdup
)(cp
, strlen(cp
) SMALLOC_DEBUG_ARGSCALL
);
888 (sbufdup
)(char const *cp
, size_t len SMALLOC_DEBUG_ARGS
)
893 dp
= (smalloc
)(len
+1 SMALLOC_DEBUG_ARGSCALL
);
902 n_strscpy(char *dst
, char const *src
, size_t dstsize
){
906 if(LIKELY(dstsize
> 0)){
909 if((dst
[rv
] = src
[rv
]) == '\0')
912 }while(--dstsize
> 0);
926 asccasecmp(char const *s1
, char const *s2
)
932 char c1
= *s1
++, c2
= *s2
++;
933 if ((cmp
= lowerconv(c1
) - lowerconv(c2
)) != 0 || c1
== '\0')
941 ascncasecmp(char const *s1
, char const *s2
, size_t sz
)
947 char c1
= *s1
++, c2
= *s2
++;
948 cmp
= (ui8_t
)lowerconv(c1
);
949 cmp
-= (ui8_t
)lowerconv(c2
);
950 if (cmp
!= 0 || c1
== '\0')
958 asccasestr(char const *s1
, char const *s2
)
963 for (c2
= *s2
++, c2
= lowerconv(c2
);;) {
964 if ((c1
= *s1
++) == '\0') {
968 if (lowerconv(c1
) == c2
&& is_asccaseprefix(s1
, s2
)) {
978 is_asccaseprefix(char const *as1
, char const *as2
)
983 for (;; ++as1
, ++as2
) {
984 char c1
= lowerconv(*as1
), c2
= lowerconv(*as2
);
986 if ((rv
= (c2
== '\0')))
996 (n_str_dup
)(struct str
*self
, struct str
const *t SMALLOC_DEBUG_ARGS
)
999 if (t
!= NULL
&& t
->l
> 0) {
1001 self
->s
= (srealloc
)(self
->s
, t
->l
+1 SMALLOC_DEBUG_ARGSCALL
);
1002 memcpy(self
->s
, t
->s
, t
->l
);
1003 self
->s
[t
->l
] = '\0';
1011 (n_str_add_buf
)(struct str
*self
, char const *buf
, size_t buflen
1016 size_t sl
= self
->l
;
1017 self
->l
= sl
+ buflen
;
1018 self
->s
= (srealloc
)(self
->s
, self
->l
+1 SMALLOC_DEBUG_ARGSCALL
);
1019 memcpy(self
->s
+ sl
, buf
, buflen
);
1020 self
->s
[self
->l
] = '\0';
1030 #ifdef HAVE_NATCH_CHAR
1032 n_utf8_to_utf32(char const **bdat
, size_t *blen
) /* TODO check false UTF8 */
1046 if ((x
& 0xE0) == 0xC0) {
1051 } else if ((x
& 0xF0) == 0xE0) {
1085 #endif /* HAVE_NATCH_CHAR */
1087 #ifdef HAVE_FILTER_HTML_TAGSOUP
1089 n_utf32_to_utf8(ui32_t c
, char *buf
)
1096 ui8_t dec_leader_mask
;
1097 ui8_t dec_leader_val_mask
;
1098 ui8_t dec_bytes_togo
;
1102 {0x00000000, 0x00000000, 0x00, 0, 0x00, 0x00, 0, 0, {0,}},
1103 {0x00000000, 0x0000007F, 0x00, 1, 0x80, 0x7F, 1-1, 1, {0,}},
1104 {0x00000080, 0x000007FF, 0xC0, 2, 0xE0, 0xFF-0xE0, 2-1, 2, {0,}},
1105 /* We assume surrogates are U+D800 - U+DFFF, _cat index 3 */
1106 /* xxx _from_utf32() simply assumes magic code points for surrogates!
1107 * xxx (However, should we ever get yet another surrogate range we
1108 * xxx need to deal with that all over the place anyway? */
1109 {0x00000800, 0x0000FFFF, 0xE0, 3, 0xF0, 0xFF-0xF0, 3-1, 3, {0,}},
1110 {0x00010000, 0x001FFFFF, 0xF0, 4, 0xF8, 0xFF-0xF8, 4-1, 4, {0,}},
1114 if (c
<= _cat
[0].upper_bound
) { catp
+= 0; goto j0
; }
1115 if (c
<= _cat
[1].upper_bound
) { catp
+= 1; goto j1
; }
1116 if (c
<= _cat
[2].upper_bound
) { catp
+= 2; goto j2
; }
1117 if (c
<= _cat
[3].upper_bound
) {
1118 /* Surrogates may not be converted (Compatibility rule C10) */
1119 if (c
>= 0xD800u
&& c
<= 0xDFFFu
)
1124 if (c
<= _cat
[4].upper_bound
) { catp
+= 4; goto j4
; }
1126 c
= 0xFFFDu
; /* Unicode replacement character */
1130 buf
[3] = (char)0x80 | (char)(c
& 0x3F); c
>>= 6;
1132 buf
[2] = (char)0x80 | (char)(c
& 0x3F); c
>>= 6;
1134 buf
[1] = (char)0x80 | (char)(c
& 0x3F); c
>>= 6;
1136 buf
[0] = (char)catp
->enc_leader
| (char)(c
);
1138 buf
[catp
->enc_lval
] = '\0';
1143 #endif /* HAVE_FILTER_HTML_TAGSOUP */
1146 * Our iconv(3) wrapper
1150 static void _ic_toupper(char *dest
, char const *src
);
1151 static void _ic_stripdash(char *p
);
1154 _ic_toupper(char *dest
, char const *src
)
1158 *dest
++ = upperconv(*src
);
1159 while (*src
++ != '\0');
1164 _ic_stripdash(char *p
)
1170 if (*(q
= p
) != '-')
1172 while (*p
++ != '\0');
1177 n_iconv_open(char const *tocode
, char const *fromcode
)
1183 if (!asccasecmp(fromcode
, "unknown-8bit") &&
1184 (fromcode
= ok_vlook(charset_unknown_8bit
)) == NULL
)
1185 fromcode
= charset_get_8bit();
1187 if ((id
= iconv_open(tocode
, fromcode
)) != (iconv_t
)-1)
1190 /* Remove the "iso-" prefixes for Solaris */
1191 if (!ascncasecmp(tocode
, "iso-", 4))
1193 else if (!ascncasecmp(tocode
, "iso", 3))
1195 if (!ascncasecmp(fromcode
, "iso-", 4))
1197 else if (!ascncasecmp(fromcode
, "iso", 3))
1199 if (*tocode
== '\0' || *fromcode
== '\0') {
1203 if ((id
= iconv_open(tocode
, fromcode
)) != (iconv_t
)-1)
1206 /* Solaris prefers upper-case charset names. Don't ask... */
1207 t
= salloc(strlen(tocode
) +1);
1208 _ic_toupper(t
, tocode
);
1209 f
= salloc(strlen(fromcode
) +1);
1210 _ic_toupper(f
, fromcode
);
1211 if ((id
= iconv_open(t
, f
)) != (iconv_t
)-1)
1214 /* Strip dashes for UnixWare */
1217 if ((id
= iconv_open(t
, f
)) != (iconv_t
)-1)
1220 /* Add your vendor's sillynesses here */
1222 /* If the encoding names are equal at this point, they are just not
1223 * understood by iconv(), and we cannot sensibly use it in any way. We do
1224 * not perform this as an optimization above since iconv() can otherwise be
1225 * used to check the validity of the input even with identical encoding
1235 n_iconv_close(iconv_t cd
)
1240 iconvd
= (iconv_t
)-1;
1245 n_iconv_reset(iconv_t cd
)
1248 iconv(cd
, NULL
, NULL
, NULL
, NULL
);
1252 /* (2012-09-24: export and use it exclusively to isolate prototype problems
1253 * (*inb* is 'char const **' except in POSIX) in a single place.
1254 * GNU libiconv even allows for configuration time const/non-const..
1255 * In the end it's an ugly guess, but we can't do better since make(1) doesn't
1256 * support compiler invocations which bail on error, so no -Werror */
1257 /* Citrus project? */
1258 # if defined _ICONV_H_ && defined __ICONV_F_HIDE_INVALID
1259 /* DragonFly 3.2.1 is special TODO newer DragonFly too, but different */
1261 # define __INBCAST(S) (char ** __restrict__)UNCONST(S)
1263 # define __INBCAST(S) (char const **)UNCONST(S)
1265 # elif OS_SUNOS || OS_SOLARIS
1266 # define __INBCAST(S) (char const ** __restrict__)UNCONST(S)
1269 # define __INBCAST(S) (char **)UNCONST(S)
1273 n_iconv_buf(iconv_t cd
, char const **inb
, size_t *inbleft
,/*XXX redo iconv use*/
1274 char **outb
, size_t *outbleft
, bool_t skipilseq
)
1280 size_t sz
= iconv(cd
, __INBCAST(inb
), inbleft
, outb
, outbleft
);
1281 if (sz
!= (size_t)-1)
1284 if (!skipilseq
|| err
!= EILSEQ
)
1289 } else if (*outbleft
> 0) {
1293 if (*outbleft
> 0/* TODO 0xFFFD 2*/) {
1294 /* TODO 0xFFFD (*outb)[0] = '[';
1295 * TODO (*outb)[1] = '?';
1296 * TODO 0xFFFD (*outb)[2] = ']';
1297 * TODO (*outb) += 3;
1298 * TODO (*outbleft) -= 3; */
1313 n_iconv_str(iconv_t cd
, struct str
*out
, struct str
const *in
,
1314 struct str
*in_rest_or_null
, bool_t skipilseq
)
1327 ol
= (ol
<< 1) - (ol
>> 4);
1338 err
= n_iconv_buf(cd
, &ib
, &il
, &ob
, &ol
, skipilseq
);
1339 if (err
== 0 || err
!= E2BIG
)
1344 obb
= srealloc(obb
, olb
+1);
1347 if (in_rest_or_null
!= NULL
) {
1348 in_rest_or_null
->s
= UNCONST(ib
);
1349 in_rest_or_null
->l
= il
;
1352 out
->s
[out
->l
= olb
- ol
] = '\0';
1356 #endif /* HAVE_ICONV */