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 - 2013 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. All advertising materials mentioning features or use of this software
24 * must display the following acknowledgement:
25 * This product includes software developed by the University of
26 * California, Berkeley and its contributors.
27 * 4. Neither the name of the University nor the names of its contributors
28 * may be used to endorse or promote products derived from this software
29 * without specific prior written permission.
31 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
32 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
33 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
34 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
35 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
36 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
37 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
38 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
39 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
40 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
49 * Allocate SBUFFER_SIZE chunks and keep them in a singly linked list, but
50 * release all except the first two in sreset(), because other allocations are
51 * performed and the underlaying allocator should have the possibility to
52 * reorder stuff and possibly even madvise(2), so that S-nail(1) integrates
53 * neatly into the system.
54 * To relax stuff further, especially in non-interactive, i.e., send mode, do
55 * not even allocate the first buffer, but let that be a builtin DATA section
56 * one that is rather small, yet sufficient for send mode to *never* even
57 * perform a single dynamic allocation (from our stringdope point of view).
65 #define SALIGN (sizeof(union __align__) - 1)
67 CTA(ISPOW2(SALIGN
+ 1));
71 char *_bot
; /* For spreserve() */
72 char *_max
; /* Max usable byte */
73 char *_caster
; /* NULL if full */
76 /* Single instance builtin buffer, DATA */
79 char b_buf
[SBUFFER_BUILTIN
- sizeof(struct b_base
)];
81 #define SBLTIN_SIZE SIZEOF_FIELD(struct b_bltin, b_buf)
83 /* Dynamically allocated buffers */
86 char b_buf
[SBUFFER_SIZE
- sizeof(struct b_base
)];
88 #define SDYN_SIZE SIZEOF_FIELD(struct b_dyn, b_buf)
92 char b_buf
[VFIELD_SIZE(SALIGN
+ 1)];
95 static struct b_bltin _builtin_buf
;
96 static struct buffer
*_buf_head
, *_buf_list
, *_buf_server
;
99 size_t _all_cnt
, _all_cycnt
, _all_cycnt_max
,
100 _all_size
, _all_cysize
, _all_cysize_max
, _all_min
, _all_max
, _all_wast
,
101 _all_bufcnt
, _all_cybufcnt
, _all_cybufcnt_max
,
102 _all_resetreqs
, _all_resets
;
106 * Allocate size more bytes of space and return the address of the
107 * first byte to the caller. An even number of bytes are always
108 * allocated so that the space will always be on a word boundary.
114 size_t orig_size
= size
;
116 union {struct buffer
*b
; char *cp
;} u
;
127 _all_cycnt_max
= MAX(_all_cycnt_max
, _all_cycnt
);
130 _all_cysize_max
= MAX(_all_cysize_max
, _all_cysize
);
131 _all_min
= _all_max
== 0 ? size
: MIN(_all_min
, size
);
132 _all_max
= MAX(_all_max
, size
);
133 _all_wast
+= size
- orig_size
;
136 if ((u
.b
= _buf_server
) != NULL
)
139 for (u
.b
= _buf_head
; u
.b
!= NULL
; u
.b
= u
.b
->b
._next
) {
140 jumpin
: x
= u
.b
->b
._caster
;
142 if (u
.b
== _buf_server
) {
143 if (u
.b
== _buf_head
&&
144 (u
.b
= _buf_head
->b
._next
)
158 * Alignment is the one thing, the other is what is
159 * usually allocated, and here about 40 bytes seems to
160 * be a good cut to avoid non-usable non-NULL casters
162 u
.b
->b
._caster
= (y
+ 42+16 >= z
) ? NULL
: y
;
168 if (_buf_head
== NULL
) {
169 struct b_bltin
*b
= &_builtin_buf
;
170 b
->b_base
._max
= b
->b_buf
+ sizeof(b
->b_buf
) - 1;
171 _buf_head
= (struct buffer
*)b
;
177 _all_cybufcnt_max
= MAX(_all_cybufcnt_max
, _all_cybufcnt
);
179 u
.b
= (struct buffer
*)smalloc(sizeof(struct b_dyn
));
180 u
.b
->b
._max
= u
.b
->b_buf
+ SDYN_SIZE
- 1;
182 if (_buf_list
!= NULL
)
183 _buf_list
->b
._next
= u
.b
;
184 _buf_server
= _buf_list
= u
.b
;
186 u
.b
->b
._caster
= (u
.b
->b
._bot
= u
.b
->b_buf
) + size
;
193 csalloc(size_t nmemb
, size_t size
)
204 * Reset the string area to be empty.
205 * Called to free all strings allocated since last reset.
219 _all_cycnt
= _all_cysize
= 0;
220 _all_cybufcnt
= (_buf_head
!= NULL
&& _buf_head
->b
._next
!= NULL
);
224 if ((bh
= _buf_head
) != NULL
) {
225 struct buffer
*b
= bh
;
226 b
->b
._caster
= b
->b
._bot
;
228 memset(b
->b
._caster
, 0377,
229 (size_t)(b
->b
._max
- b
->b
._caster
));
232 if ((bh
= bh
->b
._next
) != NULL
) {
234 b
->b
._caster
= b
->b
._bot
;
236 memset(b
->b
._caster
, 0377,
237 (size_t)(b
->b
._max
- b
->b
._caster
));
239 for (bh
= bh
->b
._next
; bh
!= NULL
;) {
240 struct buffer
*b2
= bh
->b
._next
;
256 * Make the string area permanent.
257 * Meant to be called in main, after initialization.
264 for (b
= _buf_head
; b
!= NULL
; b
= b
->b
._next
)
265 b
->b
._bot
= b
->b
._caster
;
273 printf("String usage statistics (cycle means one sreset() cycle):\n"
274 " Buffer allocs ever/max simultan. : %lu/%lu\n"
275 " Buffer size of builtin(1)/dynamic: %lu/%lu\n"
276 " Overall alloc count/bytes : %lu/%lu\n"
277 " Alloc bytes min/max/align wastage: %lu/%lu/%lu\n"
278 " sreset() cycles : %lu (%lu performed)\n"
279 " Cycle maximums: alloc count/bytes: %lu/%lu\n",
280 (ul_it
)_all_bufcnt
, (ul_it
)_all_cybufcnt_max
,
281 (ul_it
)SBLTIN_SIZE
, (ul_it
)SDYN_SIZE
,
282 (ul_it
)_all_cnt
, (ul_it
)_all_size
,
283 (ul_it
)_all_min
, (ul_it
)_all_max
, (ul_it
)_all_wast
,
284 (ul_it
)_all_resetreqs
, (ul_it
)_all_resets
,
285 (ul_it
)_all_cycnt_max
, (ul_it
)_all_cysize_max
);
291 * Return a pointer to a dynamic copy of the argument.
294 savestr(const char *str
)
296 size_t size
= strlen(str
) + 1;
297 char *news
= salloc(size
);
298 memcpy(news
, str
, size
);
303 * Return new string copy of a non-terminated argument.
306 savestrbuf(const char *sbuf
, size_t sbuf_len
)
308 char *news
= salloc(sbuf_len
+ 1);
309 memcpy(news
, sbuf
, sbuf_len
);
315 * Make a copy of new argument incorporating old one.
318 save2str(const char *str
, const char *old
)
320 size_t newsize
= strlen(str
) + 1, oldsize
= old
? strlen(old
) + 1 : 0;
321 char *news
= salloc(newsize
+ oldsize
);
323 memcpy(news
, old
, oldsize
);
324 news
[oldsize
- 1] = ' ';
326 memcpy(news
+ oldsize
, str
, newsize
);
331 savecat(char const *s1
, char const *s2
)
333 size_t l1
= strlen(s1
), l2
= strlen(s2
);
334 char *news
= salloc(l1
+ l2
+ 1);
335 memcpy(news
+ 0, s1
, l1
);
336 memcpy(news
+ l1
, s2
, l2
);
337 news
[l1
+ l2
] = '\0';
342 * Support routines, auto-reclaimed storage
346 i_strdup(char const *src
)
351 sz
= strlen(src
) + 1;
353 i_strcpy(dest
, src
, sz
);
358 protbase(char const *cp
)
360 char *n
= salloc(strlen(cp
) + 1), *np
= n
;
362 /* Just ignore the `is-system-mailbox' prefix XXX */
363 if (cp
[0] == '%' && cp
[1] == ':')
367 if (cp
[0] == ':' && cp
[1] == '/' && cp
[2] == '/') {
371 } else if (cp
[0] == '/')
381 urlxenc(char const *cp
) /* XXX */
385 np
= n
= salloc(strlen(cp
) * 3 + 1);
387 if (alnumchar(*cp
) || *cp
== '_' || *cp
== '@' ||
388 (np
> n
&& (*cp
== '.' || *cp
== '-' ||
393 *np
++ = Hexchar((*cp
&0xf0) >> 4);
394 *np
++ = Hexchar(*cp
&0x0f);
403 urlxdec(char const *cp
) /* XXX */
407 np
= n
= salloc(strlen(cp
) + 1);
409 if (cp
[0] == '%' && cp
[1] && cp
[2]) {
410 *np
= (int)(cp
[1]>'9'?cp
[1]-'A'+10:cp
[1]-'0') << 4;
411 *np
++ |= cp
[2]>'9'?cp
[2]-'A'+10:cp
[2]-'0';
421 str_concat_csvl(struct str
*self
, ...) /* XXX onepass maybe better here */
428 for (l
= 0; (cs
= va_arg(vl
, char const*)) != NULL
;)
433 self
->s
= salloc(l
+ 1);
436 for (l
= 0; (cs
= va_arg(vl
, char const*)) != NULL
;) {
437 size_t i
= strlen(cs
);
438 memcpy(self
->s
+ l
, cs
, i
);
447 str_concat_cpa(struct str
*self
, char const *const*cpa
, char const *sep_o_null
)
450 char const *const*xcpa
;
452 sonl
= (sep_o_null
!= NULL
) ? strlen(sep_o_null
) : 0;
454 for (l
= 0, xcpa
= cpa
; *xcpa
!= NULL
; ++xcpa
)
455 l
+= strlen(*xcpa
) + sonl
;
458 self
->s
= salloc(l
+ 1);
460 for (l
= 0, xcpa
= cpa
; *xcpa
!= NULL
; ++xcpa
) {
461 size_t i
= strlen(*xcpa
);
462 memcpy(self
->s
+ l
, *xcpa
, i
);
465 memcpy(self
->s
+ l
, sep_o_null
, sonl
);
474 * Routines that are not related to auto-reclaimed storage follow.
477 uc_it
const class_char
[] = {
478 /* 000 nul 001 soh 002 stx 003 etx 004 eot 005 enq 006 ack 007 bel */
479 C_CNTRL
,C_CNTRL
,C_CNTRL
,C_CNTRL
,C_CNTRL
,C_CNTRL
,C_CNTRL
,C_CNTRL
,
480 /* 010 bs 011 ht 012 nl 013 vt 014 np 015 cr 016 so 017 si */
481 C_CNTRL
,C_BLANK
,C_WHITE
,C_SPACE
,C_SPACE
,C_SPACE
,C_CNTRL
,C_CNTRL
,
482 /* 020 dle 021 dc1 022 dc2 023 dc3 024 dc4 025 nak 026 syn 027 etb */
483 C_CNTRL
,C_CNTRL
,C_CNTRL
,C_CNTRL
,C_CNTRL
,C_CNTRL
,C_CNTRL
,C_CNTRL
,
484 /* 030 can 031 em 032 sub 033 esc 034 fs 035 gs 036 rs 037 us */
485 C_CNTRL
,C_CNTRL
,C_CNTRL
,C_CNTRL
,C_CNTRL
,C_CNTRL
,C_CNTRL
,C_CNTRL
,
486 /* 040 sp 041 ! 042 " 043 # 044 $ 045 % 046 & 047 ' */
487 C_BLANK
,C_PUNCT
,C_PUNCT
,C_PUNCT
,C_PUNCT
,C_PUNCT
,C_PUNCT
,C_PUNCT
,
488 /* 050 ( 051 ) 052 * 053 + 054 , 055 - 056 . 057 / */
489 C_PUNCT
,C_PUNCT
,C_PUNCT
,C_PUNCT
,C_PUNCT
,C_PUNCT
,C_PUNCT
,C_PUNCT
,
490 /* 060 0 061 1 062 2 063 3 064 4 065 5 066 6 067 7 */
491 C_OCTAL
,C_OCTAL
,C_OCTAL
,C_OCTAL
,C_OCTAL
,C_OCTAL
,C_OCTAL
,C_OCTAL
,
492 /* 070 8 071 9 072 : 073 ; 074 < 075 = 076 > 077 ? */
493 C_DIGIT
,C_DIGIT
,C_PUNCT
,C_PUNCT
,C_PUNCT
,C_PUNCT
,C_PUNCT
,C_PUNCT
,
494 /* 100 @ 101 A 102 B 103 C 104 D 105 E 106 F 107 G */
495 C_PUNCT
,C_UPPER
,C_UPPER
,C_UPPER
,C_UPPER
,C_UPPER
,C_UPPER
,C_UPPER
,
496 /* 110 H 111 I 112 J 113 K 114 L 115 M 116 N 117 O */
497 C_UPPER
,C_UPPER
,C_UPPER
,C_UPPER
,C_UPPER
,C_UPPER
,C_UPPER
,C_UPPER
,
498 /* 120 P 121 Q 122 R 123 S 124 T 125 U 126 V 127 W */
499 C_UPPER
,C_UPPER
,C_UPPER
,C_UPPER
,C_UPPER
,C_UPPER
,C_UPPER
,C_UPPER
,
500 /* 130 X 131 Y 132 Z 133 [ 134 \ 135 ] 136 ^ 137 _ */
501 C_UPPER
,C_UPPER
,C_UPPER
,C_PUNCT
,C_PUNCT
,C_PUNCT
,C_PUNCT
,C_PUNCT
,
502 /* 140 ` 141 a 142 b 143 c 144 d 145 e 146 f 147 g */
503 C_PUNCT
,C_LOWER
,C_LOWER
,C_LOWER
,C_LOWER
,C_LOWER
,C_LOWER
,C_LOWER
,
504 /* 150 h 151 i 152 j 153 k 154 l 155 m 156 n 157 o */
505 C_LOWER
,C_LOWER
,C_LOWER
,C_LOWER
,C_LOWER
,C_LOWER
,C_LOWER
,C_LOWER
,
506 /* 160 p 161 q 162 r 163 s 164 t 165 u 166 v 167 w */
507 C_LOWER
,C_LOWER
,C_LOWER
,C_LOWER
,C_LOWER
,C_LOWER
,C_LOWER
,C_LOWER
,
508 /* 170 x 171 y 172 z 173 { 174 | 175 } 176 ~ 177 del */
509 C_LOWER
,C_LOWER
,C_LOWER
,C_PUNCT
,C_PUNCT
,C_PUNCT
,C_PUNCT
,C_CNTRL
513 anyof(char const *s1
, char const *s2
)
515 for (; *s1
!= '\0'; ++s1
)
518 return (*s1
!= '\0');
522 strhash(char const *name
)
526 while (*name
!= '\0') {
534 strcomma(char **iolist
, int ignore_empty
)
538 for (base
= *iolist
; base
!= NULL
; base
= *iolist
) {
539 while (*base
!= '\0' && blankspacechar(*base
))
541 cp
= strchr(base
, ',');
546 cp
= base
+ strlen(base
);
548 while (cp
> base
&& blankspacechar(cp
[-1]))
551 if (*base
!= '\0' || ! ignore_empty
)
558 i_strcpy(char *dest
, const char *src
, size_t size
)
561 for (;; ++dest
, ++src
)
562 if ((*dest
= lowerconv(*src
)) == '\0') {
564 } else if (--size
== 0) {
572 is_prefix(char const *as1
, char const *as2
)
575 for (; (c
= *as1
) == *as2
&& c
!= '\0'; ++as1
, ++as2
)
576 if ((c
= *as2
) == '\0')
582 last_at_before_slash(char const *sp
)
587 for (cp
= sp
; (c
= *cp
) != '\0'; ++cp
)
590 while (cp
> sp
&& *--cp
!= '@')
592 return (*cp
== '@' ? cp
: NULL
);
596 makelow(char *cp
) /* TODO isn't that crap? --> */
598 #if defined HAVE_MBTOWC && defined HAVE_WCTYPE_H
599 if (mb_cur_max
> 1) {
605 len
= mbtowc(&wc
, cp
, mb_cur_max
);
610 if (wctomb(tp
, wc
) == len
)
611 tp
+= len
, cp
+= len
;
613 *tp
++ = *cp
++; /* <-- at least here */
620 *cp
= tolower((uc_it
)*cp
);
626 substr(char const *str
, char const *sub
)
628 char const *cp
, *backup
;
632 while (*str
&& *cp
) {
633 #if defined HAVE_MBTOWC && defined HAVE_WCTYPE_H
634 if (mb_cur_max
> 1) {
638 if ((sz
= mbtowc(&c
, cp
, mb_cur_max
)) < 0)
641 if ((sz
= mbtowc(&c2
, str
, mb_cur_max
)) < 0)
647 if ((sz
= mbtowc(&c
, backup
, mb_cur_max
)) > 0) {
659 #if defined HAVE_MBTOWC && defined HAVE_WCTYPE_H
677 #ifndef HAVE_SNPRINTF
679 snprintf(char *str
, size_t size
, const char *format
, ...) /* XXX DANGER! */
684 va_start(ap
, format
);
685 ret
= vsprintf(str
, format
, ap
);
694 sstpcpy(char *dst
, char const *src
)
696 while ((*dst
= *src
++) != '\0')
702 (sstrdup
)(char const *cp SMALLOC_DEBUG_ARGS
)
707 size_t l
= strlen(cp
) + 1;
708 dp
= (smalloc
)(l SMALLOC_DEBUG_ARGSCALL
);
715 (sbufdup
)(char const *cp
, size_t len SMALLOC_DEBUG_ARGS
)
719 dp
= (smalloc
)(len
+ 1 SMALLOC_DEBUG_ARGSCALL
);
727 asccasecmp(char const *s1
, char const *s2
)
732 char c1
= *s1
++, c2
= *s2
++;
733 if ((cmp
= lowerconv(c1
) - lowerconv(c2
)) != 0 || c1
== '\0')
740 ascncasecmp(char const *s1
, char const *s2
, size_t sz
)
745 char c1
= *s1
++, c2
= *s2
++;
746 if ((cmp
= lowerconv(c1
) - lowerconv(c2
)) != 0 || c1
== '\0')
753 asccasestr(char const *haystack
, char const *xneedle
)
755 char *needle
= NULL
, *NEEDLE
;
758 sz
= strlen(xneedle
);
762 needle
= ac_alloc(sz
);
763 NEEDLE
= ac_alloc(sz
);
764 for (i
= 0; i
< sz
; i
++) {
765 needle
[i
] = lowerconv(xneedle
[i
]);
766 NEEDLE
[i
] = upperconv(xneedle
[i
]);
770 if (*haystack
== *needle
|| *haystack
== *NEEDLE
) {
771 for (i
= 1; i
< sz
; i
++)
772 if (haystack
[i
] != needle
[i
] &&
773 haystack
[i
] != NEEDLE
[i
])
782 if (needle
!= NULL
) {
790 is_asccaseprefix(char const *as1
, char const *as2
)
794 for (;; ++as1
, ++as2
) {
795 char c1
= lowerconv(*as1
), c2
= lowerconv(*as2
);
796 if ((rv
= (c1
== '\0')))
798 if (c1
!= c2
|| c2
== '\0')
805 (n_str_dup
)(struct str
*self
, struct str
const *t SMALLOC_DEBUG_ARGS
)
807 if (t
!= NULL
&& t
->l
> 0) {
809 self
->s
= (srealloc
)(self
->s
, t
->l
+ 1 SMALLOC_DEBUG_ARGSCALL
);
810 memcpy(self
->s
, t
->s
, t
->l
);
817 (n_str_add_buf
)(struct str
*self
, char const *buf
, size_t buflen
822 self
->l
= sl
+ buflen
;
823 self
->s
= (srealloc
)(self
->s
, self
->l
+1 SMALLOC_DEBUG_ARGSCALL
);
824 memcpy(self
->s
+ sl
, buf
, buflen
);
830 static void _ic_toupper(char *dest
, char const *src
);
831 static void _ic_stripdash(char *p
);
834 _ic_toupper(char *dest
, const char *src
)
837 *dest
++ = upperconv(*src
);
842 _ic_stripdash(char *p
)
853 n_iconv_open(char const *tocode
, char const *fromcode
)
858 if ((id
= iconv_open(tocode
, fromcode
)) != (iconv_t
)-1)
862 * Remove the "iso-" prefixes for Solaris.
864 if (ascncasecmp(tocode
, "iso-", 4) == 0)
866 else if (ascncasecmp(tocode
, "iso", 3) == 0)
868 if (ascncasecmp(fromcode
, "iso-", 4) == 0)
870 else if (ascncasecmp(fromcode
, "iso", 3) == 0)
872 if (*tocode
== '\0' || *fromcode
== '\0')
874 if ((id
= iconv_open(tocode
, fromcode
)) != (iconv_t
)-1)
877 * Solaris prefers upper-case charset names. Don't ask...
879 t
= salloc(strlen(tocode
) + 1);
880 _ic_toupper(t
, tocode
);
881 f
= salloc(strlen(fromcode
) + 1);
882 _ic_toupper(f
, fromcode
);
883 if ((id
= iconv_open(t
, f
)) != (iconv_t
)-1)
886 * Strip dashes for UnixWare.
890 if ((id
= iconv_open(t
, f
)) != (iconv_t
)-1)
893 * Add your vendor's sillynesses here.
897 * If the encoding names are equal at this point, they
898 * are just not understood by iconv(), and we cannot
899 * sensibly use it in any way. We do not perform this
900 * as an optimization above since iconv() can otherwise
901 * be used to check the validity of the input even with
902 * identical encoding names.
904 if (strcmp(t
, f
) == 0)
910 n_iconv_close(iconv_t cd
)
914 iconvd
= (iconv_t
)-1;
918 n_iconv_reset(iconv_t cd
)
920 (void)iconv(cd
, NULL
, NULL
, NULL
, NULL
);
924 * (2012-09-24: export and use it exclusively to isolate prototype problems
925 * (*inb* is 'char const **' except in POSIX) in a single place.
926 * GNU libiconv even allows for configuration time const/non-const..
927 * In the end it's an ugly guess, but we can't do better since make(1) doesn't
928 * support compiler invocations which bail on error, so no -Werror.
930 /* Citrus project? */
931 # if defined _ICONV_H_ && defined __ICONV_F_HIDE_INVALID
932 /* DragonFly 3.2.1 is special */
933 # ifdef __DragonFly__
934 # define __INBCAST(S) (char ** __restrict__)UNCONST(S)
936 # define __INBCAST(S) (char const **)UNCONST(S)
940 # define __INBCAST(S) (char **)UNCONST(S)
944 n_iconv_buf(iconv_t cd
, char const **inb
, size_t *inbleft
,/*XXX redo iconv use*/
945 char **outb
, size_t *outbleft
, bool_t skipilseq
)
950 size_t sz
= iconv(cd
, __INBCAST(inb
), inbleft
, outb
, outbleft
);
951 if (sz
!= (size_t)-1)
954 if (! skipilseq
|| err
!= EILSEQ
)
959 } else if (*outbleft
> 0) {
963 if (*outbleft
> 0/* TODO 0xFFFD 2*/) {
964 /* TODO 0xFFFD (*outb)[0] = '[';
965 * TODO (*outb)[1] = '?';
966 * TODO 0xFFFD (*outb)[2] = ']';
968 * TODO (*outbleft) -= 3; */
982 n_iconv_str(iconv_t cd
, struct str
*out
, struct str
const *in
,
983 struct str
*in_rest_or_null
, bool_t skipilseq
)
986 char *obb
= out
->s
, *ob
;
988 size_t olb
= out
->l
, ol
, il
;
991 ol
= (ol
<< 1) - (ol
>> 4);
1002 err
= n_iconv_buf(cd
, &ib
, &il
, &ob
, &ol
, skipilseq
);
1003 if (err
== 0 || err
!= E2BIG
)
1007 jrealloc
: obb
= srealloc(obb
, olb
);
1010 if (in_rest_or_null
!= NULL
) {
1011 in_rest_or_null
->s
= UNCONST(ib
);
1012 in_rest_or_null
->l
= il
;
1018 #endif /* HAVE_ICONV */