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 - 2014 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
44 #ifndef HAVE_AMALGAMATION
50 /* Allocate SBUFFER_SIZE chunks and keep them in a singly linked list, but
51 * release all except the first two in sreset(), because other allocations are
52 * performed and the underlaying allocator should have the possibility to
53 * reorder stuff and possibly even madvise(2), so that S-nail(1) integrates
54 * neatly into the system.
55 * To relax stuff further, especially in non-interactive, i.e., send mode, do
56 * not even allocate the first buffer, but let that be a builtin DATA section
57 * one that is rather small, yet sufficient for send mode to *never* even
58 * perform a single dynamic allocation (from our stringdope point of view).
59 * Encapsulate user chunks with canaries if HAVE_DEBUG */
62 # define _SHOPE_SIZE (2u * 8 * sizeof(char) + sizeof(struct schunk))
64 CTA(sizeof(char) == sizeof(ui8_t
));
79 #endif /* HAVE_DEBUG */
86 #define SALIGN (sizeof(union __align__) - 1)
88 CTA(ISPOW2(SALIGN
+ 1));
92 char *_bot
; /* For spreserve() */
93 char *_relax
; /* If !NULL, used by srelax() instead of ._bot */
94 char *_max
; /* Max usable byte */
95 char *_caster
; /* NULL if full */
98 /* Single instance builtin buffer, DATA */
100 struct b_base b_base
;
101 char b_buf
[SBUFFER_BUILTIN
- sizeof(struct b_base
)];
103 #define SBLTIN_SIZE SIZEOF_FIELD(struct b_bltin, b_buf)
105 /* Dynamically allocated buffers */
107 struct b_base b_base
;
108 char b_buf
[SBUFFER_SIZE
- sizeof(struct b_base
)];
110 #define SDYN_SIZE SIZEOF_FIELD(struct b_dyn, b_buf)
114 char b_buf
[VFIELD_SIZE(SALIGN
+ 1)];
117 static struct b_bltin _builtin_buf
;
118 static struct buffer
*_buf_head
, *_buf_list
, *_buf_server
, *_buf_relax
;
120 static size_t _all_cnt
, _all_cycnt
, _all_cycnt_max
,
121 _all_size
, _all_cysize
, _all_cysize_max
, _all_min
,
123 _all_bufcnt
, _all_cybufcnt
, _all_cybufcnt_max
,
124 _all_resetreqs
, _all_resets
;
127 /* sreset() / srelax() release a buffer, check the canaries of all chunks */
129 static void _salloc_bcheck(struct buffer
*b
);
134 _salloc_bcheck(struct buffer
*b
)
139 pmax
.cp
= (b
->b
._caster
== NULL
) ? b
->b
._max
: b
->b
._caster
;
142 while (pp
.cp
< pmax
.cp
) {
149 pp
.cp
+= c
->full_size
;
154 if (x
.ui8p
[0] != 0xDE) i
|= 1<<0;
155 if (x
.ui8p
[1] != 0xAA) i
|= 1<<1;
156 if (x
.ui8p
[2] != 0x55) i
|= 1<<2;
157 if (x
.ui8p
[3] != 0xAD) i
|= 1<<3;
158 if (x
.ui8p
[4] != 0xBE) i
|= 1<<4;
159 if (x
.ui8p
[5] != 0x55) i
|= 1<<5;
160 if (x
.ui8p
[6] != 0xAA) i
|= 1<<6;
161 if (x
.ui8p
[7] != 0xEF) i
|= 1<<7;
163 alert("sdope %p: corrupt lower canary: 0x%02X, size %u: %s, line %u",
164 ux
, i
, c
->usr_size
, c
->file
, c
->line
);
165 x
.cp
+= 8 + c
->usr_size
;
168 if (x
.ui8p
[0] != 0xDE) i
|= 1<<0;
169 if (x
.ui8p
[1] != 0xAA) i
|= 1<<1;
170 if (x
.ui8p
[2] != 0x55) i
|= 1<<2;
171 if (x
.ui8p
[3] != 0xAD) i
|= 1<<3;
172 if (x
.ui8p
[4] != 0xBE) i
|= 1<<4;
173 if (x
.ui8p
[5] != 0x55) i
|= 1<<5;
174 if (x
.ui8p
[6] != 0xAA) i
|= 1<<6;
175 if (x
.ui8p
[7] != 0xEF) i
|= 1<<7;
177 alert("sdope %p: corrupt upper canary: 0x%02X, size %u: %s, line %u",
178 ux
, i
, c
->usr_size
, c
->file
, c
->line
);
185 (salloc
)(size_t size SALLOC_DEBUG_ARGS
)
187 DBG( size_t orig_size
= size
; )
188 union {struct buffer
*b
; char *cp
;} u
;
200 _all_cycnt_max
= MAX(_all_cycnt_max
, _all_cycnt
);
203 _all_cysize_max
= MAX(_all_cysize_max
, _all_cysize
);
204 _all_min
= (_all_max
== 0) ? size
: MIN(_all_min
, size
);
205 _all_max
= MAX(_all_max
, size
);
206 _all_wast
+= size
- orig_size
;
211 alert("salloc() of %" ZFMT
" bytes from `%s', line %u\n",
212 size
, mdbg_file
, mdbg_line
);
215 /* Search for a buffer with enough free space to serve request */
216 if ((u
.b
= _buf_server
) != NULL
)
219 for (u
.b
= _buf_head
; u
.b
!= NULL
; u
.b
= u
.b
->b
._next
) {
223 if (u
.b
== _buf_server
) {
224 if (u
.b
== _buf_head
&& (u
.b
= _buf_head
->b
._next
) != NULL
) {
235 if (PTRCMP(y
, <=, z
)) {
236 /* Alignment is the one thing, the other is what is usually allocated,
237 * and here about 40 bytes seems to be a good cut to avoid non-usable
238 * non-NULL casters. However, because of _salloc_bcheck(), we may not
239 * set ._caster to NULL because then it would check all chunks up to
240 * ._max, which surely doesn't work; speed is no issue with DEBUG */
241 u
.b
->b
._caster
= NDBG( PTRCMP(y
+ 42 + 16, >=, z
) ? NULL
: ) y
;
247 /* Need a new buffer */
248 if (_buf_head
== NULL
) {
249 struct b_bltin
*b
= &_builtin_buf
;
250 b
->b_base
._max
= b
->b_buf
+ sizeof(b
->b_buf
) - 1;
251 _buf_head
= (struct buffer
*)b
;
257 _all_cybufcnt_max
= MAX(_all_cybufcnt_max
, _all_cybufcnt
);
259 u
.b
= smalloc(sizeof(struct b_dyn
));
260 u
.b
->b
._max
= u
.b
->b_buf
+ SDYN_SIZE
- 1;
262 if (_buf_list
!= NULL
)
263 _buf_list
->b
._next
= u
.b
;
264 _buf_server
= _buf_list
= u
.b
;
266 u
.b
->b
._caster
= (u
.b
->b
._bot
= u
.b
->b_buf
) + size
;
267 u
.b
->b
._relax
= NULL
;
271 /* Encapsulate user chunk in debug canaries */
279 xc
->file
= mdbg_file
;
280 xc
->line
= mdbg_line
;
281 xc
->usr_size
= (ui16_t
)orig_size
;
282 xc
->full_size
= (ui16_t
)size
;
284 xl
.ui8p
[0]=0xDE; xl
.ui8p
[1]=0xAA; xl
.ui8p
[2]=0x55; xl
.ui8p
[3]=0xAD;
285 xl
.ui8p
[4]=0xBE; xl
.ui8p
[5]=0x55; xl
.ui8p
[6]=0xAA; xl
.ui8p
[7]=0xEF;
289 xu
.ui8p
[0]=0xDE; xu
.ui8p
[1]=0xAA; xu
.ui8p
[2]=0x55; xu
.ui8p
[3]=0xAD;
290 xu
.ui8p
[4]=0xBE; xu
.ui8p
[5]=0x55; xu
.ui8p
[6]=0xAA; xu
.ui8p
[7]=0xEF;
298 (csalloc
)(size_t nmemb
, size_t size SALLOC_DEBUG_ARGS
)
304 vp
= (salloc
)(size SALLOC_DEBUG_ARGSCALL
);
311 sreset(bool_t only_if_relaxed
)
316 DBG( ++_all_resetreqs
; )
317 if (noreset
|| (only_if_relaxed
&& _buf_relax
== NULL
))
321 _all_cycnt
= _all_cysize
= 0;
322 _all_cybufcnt
= (_buf_head
!= NULL
&& _buf_head
->b
._next
!= NULL
);
326 if ((bh
= _buf_head
) != NULL
) {
327 struct buffer
*b
= bh
;
328 DBG( _salloc_bcheck(b
); )
329 b
->b
._caster
= b
->b
._bot
;
331 DBG( memset(b
->b
._caster
, 0377, PTR2SIZE(b
->b
._max
- b
->b
._caster
)); )
334 if ((bh
= bh
->b
._next
) != NULL
) {
336 DBG( _salloc_bcheck(b
); )
337 b
->b
._caster
= b
->b
._bot
;
339 DBG( memset(b
->b
._caster
, 0377, PTR2SIZE(b
->b
._max
- b
->b
._caster
)); )
341 for (bh
= bh
->b
._next
; bh
!= NULL
;) {
342 struct buffer
*b2
= bh
->b
._next
;
343 DBG( _salloc_bcheck(bh
); )
364 assert(_buf_relax
== NULL
);
366 for (b
= _buf_head
; b
!= NULL
; b
= b
->b
._next
)
367 b
->b
._relax
= b
->b
._caster
;
368 _buf_relax
= _buf_server
;
369 assert(_buf_relax
!= NULL
);
379 assert(_buf_relax
!= NULL
);
381 for (b
= _buf_relax
; b
!= NULL
; b
= b
->b
._next
) {
382 DBG( _salloc_bcheck(b
); )
383 b
->b
._caster
= (b
->b
._relax
!= NULL
) ? b
->b
._relax
: b
->b
._bot
;
393 /* The purpose of relaxation is only that it is possible to reset the
394 * casters, *not* to give back memory to the system. We are presumably in
395 * an iteration over all messages of a mailbox, and it'd be quite
396 * counterproductive to give the system allocator a chance to waste time */
400 assert(_buf_relax
!= NULL
);
402 for (b
= _buf_relax
; b
!= NULL
; b
= b
->b
._next
) {
403 DBG( _salloc_bcheck(b
); )
404 b
->b
._caster
= (b
->b
._relax
!= NULL
) ? b
->b
._relax
: b
->b
._bot
;
405 DBG( memset(b
->b
._caster
, 0377, PTR2SIZE(b
->b
._max
- b
->b
._caster
)); )
416 for (b
= _buf_head
; b
!= NULL
; b
= b
->b
._next
)
417 b
->b
._bot
= b
->b
._caster
;
429 excess
= (_all_cybufcnt_max
* SDYN_SIZE
) + SBLTIN_SIZE
;
430 excess
= (excess
>= _all_cysize_max
) ? 0 : _all_cysize_max
- excess
;
432 printf("String usage statistics (cycle means one sreset() cycle):\n"
433 " Buffer allocs ever/max simultan. : %lu/%lu\n"
434 " Buffer size of builtin(1)/dynamic: %lu/%lu\n"
435 " Overall alloc count/bytes : %lu/%lu\n"
436 " Alloc bytes min/max/align wastage: %lu/%lu/%lu\n"
437 " sreset() cycles : %lu (%lu performed)\n"
438 " Cycle maximums: alloc count/bytes: %lu/%lu+%lu\n",
439 (ul_it
)_all_bufcnt
, (ul_it
)_all_cybufcnt_max
,
440 (ul_it
)SBLTIN_SIZE
, (ul_it
)SDYN_SIZE
,
441 (ul_it
)_all_cnt
, (ul_it
)_all_size
,
442 (ul_it
)_all_min
, (ul_it
)_all_max
, (ul_it
)_all_wast
,
443 (ul_it
)_all_resetreqs
, (ul_it
)_all_resets
,
444 (ul_it
)_all_cycnt_max
, (ul_it
)_all_cysize_max
, (ul_it
)excess
);
451 (savestr
)(char const *str SALLOC_DEBUG_ARGS
)
457 size
= strlen(str
) +1;
458 news
= (salloc
)(size SALLOC_DEBUG_ARGSCALL
);
459 memcpy(news
, str
, size
);
465 (savestrbuf
)(char const *sbuf
, size_t sbuf_len SALLOC_DEBUG_ARGS
)
470 news
= (salloc
)(sbuf_len
+1 SALLOC_DEBUG_ARGSCALL
);
471 memcpy(news
, sbuf
, sbuf_len
);
478 (save2str
)(char const *str
, char const *old SALLOC_DEBUG_ARGS
)
480 size_t newsize
, oldsize
;
484 newsize
= strlen(str
) +1;
485 oldsize
= (old
!= NULL
) ? strlen(old
) + 1 : 0;
486 news
= (salloc
)(newsize
+ oldsize SALLOC_DEBUG_ARGSCALL
);
488 memcpy(news
, old
, oldsize
);
489 news
[oldsize
- 1] = ' ';
491 memcpy(news
+ oldsize
, str
, newsize
);
497 (savecat
)(char const *s1
, char const *s2 SALLOC_DEBUG_ARGS
)
505 news
= (salloc
)(l1
+ l2
+1 SALLOC_DEBUG_ARGSCALL
);
506 memcpy(news
+ 0, s1
, l1
);
507 memcpy(news
+ l1
, s2
, l2
);
508 news
[l1
+ l2
] = '\0';
514 * Support routines, auto-reclaimed storage
518 (i_strdup
)(char const *src SALLOC_DEBUG_ARGS
)
525 dest
= (salloc
)(sz SALLOC_DEBUG_ARGSCALL
);
526 i_strcpy(dest
, src
, sz
);
532 (protbase
)(char const *cp SALLOC_DEBUG_ARGS
) /* TODO obsolete */
537 np
= n
= (salloc
)(strlen(cp
) +1 SALLOC_DEBUG_ARGSCALL
);
539 /* Just ignore the `is-system-mailbox' prefix XXX */
540 if (cp
[0] == '%' && cp
[1] == ':')
543 while (*cp
!= '\0') {
544 if (cp
[0] == ':' && cp
[1] == '/' && cp
[2] == '/') {
548 } else if (cp
[0] == '/')
559 (urlxenc
)(char const *cp SALLOC_DEBUG_ARGS
) /* XXX (->URL (yet auxlily.c)) */
564 np
= n
= (salloc
)(strlen(cp
) * 3 +1 SALLOC_DEBUG_ARGSCALL
);
566 while (*cp
!= '\0') {
567 if (alnumchar(*cp
) || *cp
== '_' || *cp
== '@' ||
568 (PTRCMP(np
, >, n
) && (*cp
== '.' || *cp
== '-' || *cp
== ':')))
572 *np
++ = Hexchar((*cp
& 0xf0) >> 4);
573 *np
++ = Hexchar(*cp
& 0x0f);
583 (urlxdec
)(char const *cp SALLOC_DEBUG_ARGS
) /* XXX (->URL (yet auxlily.c)) */
588 np
= n
= (salloc
)(strlen(cp
) +1 SALLOC_DEBUG_ARGSCALL
);
590 while (*cp
!= '\0') {
591 if (cp
[0] == '%' && cp
[1] != '\0' && cp
[2] != '\0') {
592 *np
= (int)(cp
[1] > '9' ? cp
[1] - 'A' + 10 : cp
[1] - '0') << 4;
593 *np
++ |= cp
[2] > '9' ? cp
[2] - 'A' + 10 : cp
[2] - '0';
604 str_concat_csvl(struct str
*self
, ...) /* XXX onepass maybe better here */
612 for (l
= 0; (cs
= va_arg(vl
, char const*)) != NULL
;)
617 self
->s
= salloc(l
+1);
620 for (l
= 0; (cs
= va_arg(vl
, char const*)) != NULL
;) {
621 size_t i
= strlen(cs
);
622 memcpy(self
->s
+ l
, cs
, i
);
632 (str_concat_cpa
)(struct str
*self
, char const * const *cpa
,
633 char const *sep_o_null SALLOC_DEBUG_ARGS
)
636 char const * const *xcpa
;
639 sonl
= (sep_o_null
!= NULL
) ? strlen(sep_o_null
) : 0;
641 for (l
= 0, xcpa
= cpa
; *xcpa
!= NULL
; ++xcpa
)
642 l
+= strlen(*xcpa
) + sonl
;
645 self
->s
= (salloc
)(l
+1 SALLOC_DEBUG_ARGSCALL
);
647 for (l
= 0, xcpa
= cpa
; *xcpa
!= NULL
; ++xcpa
) {
648 size_t i
= strlen(*xcpa
);
649 memcpy(self
->s
+ l
, *xcpa
, i
);
652 memcpy(self
->s
+ l
, sep_o_null
, sonl
);
662 * Routines that are not related to auto-reclaimed storage follow.
666 anyof(char const *s1
, char const *s2
)
669 for (; *s1
!= '\0'; ++s1
)
670 if (strchr(s2
, *s1
) != NULL
)
673 return (*s1
!= '\0');
677 n_strsep(char **iolist
, char sep
, bool_t ignore_empty
)
682 for (base
= *iolist
; base
!= NULL
; base
= *iolist
) {
683 while (*base
!= '\0' && blankspacechar(*base
))
685 cp
= strchr(base
, sep
);
690 cp
= base
+ strlen(base
);
692 while (cp
> base
&& blankspacechar(cp
[-1]))
695 if (*base
!= '\0' || !ignore_empty
)
703 i_strcpy(char *dest
, char const *src
, size_t size
)
707 for (;; ++dest
, ++src
)
708 if ((*dest
= lowerconv(*src
)) == '\0') {
710 } else if (--size
== 0) {
719 is_prefix(char const *as1
, char const *as2
)
724 for (; (c
= *as1
) == *as2
&& c
!= '\0'; ++as1
, ++as2
)
732 last_at_before_slash(char const *sp
)/* XXX (->URL (yet auxlily.c) / obsolete) */
738 for (cp
= sp
; (c
= *cp
) != '\0'; ++cp
)
741 while (cp
> sp
&& *--cp
!= '@')
750 laststring(char *linebuf
, bool_t
*needs_list
, bool_t strip
)
752 char *cp
, *p
, quoted
;
755 /* Anything to do at all? */
756 if (*(cp
= linebuf
) == '\0')
758 cp
+= strlen(linebuf
) -1;
760 /* Strip away trailing blanks */
761 while (whitechar(*cp
) && cp
> linebuf
)
767 /* Now search for the BOS of the "last string" */
769 if (quoted
== '\'' || quoted
== '"') {
775 while (cp
> linebuf
) {
780 } else if (!whitechar(*cp
))
782 if (cp
== linebuf
|| cp
[-1] != '\\') {
783 /* When in whitespace mode, WS prefix doesn't belong */
788 /* Expand the escaped quote character */
789 for (p
= --cp
; (p
[0] = p
[1]) != '\0'; ++p
)
792 if (strip
&& quoted
!= ' ' && *cp
== quoted
)
793 for (p
= cp
; (p
[0] = p
[1]) != '\0'; ++p
)
796 /* The "last string" has been skipped over, but still, try to step backwards
797 * until we are at BOS or see whitespace, so as to make possible things like
798 * "? copy +'x y.mbox'" or even "? copy +x\ y.mbox" */
799 while (cp
> linebuf
) {
801 if (whitechar(*cp
)) {
804 /* We can furtherly release our callees if we now decide wether the
805 * remaining non-"last string" line content contains non-WS */
806 while (--p
>= linebuf
)
815 if (cp
!= NULL
&& *cp
== '\0')
817 *needs_list
= (cp
!= linebuf
&& *linebuf
!= '\0');
828 makelow(char *cp
) /* TODO isn't that crap? --> */
831 #ifdef HAVE_C90AMEND1
832 if (mb_cur_max
> 1) {
837 while (*cp
!= '\0') {
838 len
= mbtowc(&wc
, cp
, mb_cur_max
);
843 if (wctomb(tp
, wc
) == len
)
844 tp
+= len
, cp
+= len
;
846 *tp
++ = *cp
++; /* <-- at least here */
853 *cp
= tolower((uc_it
)*cp
);
854 while (*cp
++ != '\0');
860 substr(char const *str
, char const *sub
)
862 char const *cp
, *backup
;
867 while (*str
!= '\0' && *cp
!= '\0') {
868 #ifdef HAVE_C90AMEND1
869 if (mb_cur_max
> 1) {
873 if ((sz
= mbtowc(&c
, cp
, mb_cur_max
)) == -1)
876 if ((sz
= mbtowc(&c2
, str
, mb_cur_max
)) == -1)
882 if ((sz
= mbtowc(&c
, backup
, mb_cur_max
)) > 0) {
908 return (*cp
== '\0');
911 #ifndef HAVE_SNPRINTF
913 snprintf(char *str
, size_t size
, char const *format
, ...) /* XXX DANGER! */
919 va_start(ap
, format
);
920 ret
= vsprintf(str
, format
, ap
);
930 sstpcpy(char *dst
, char const *src
)
933 while ((*dst
= *src
++) != '\0')
940 (sstrdup
)(char const *cp SMALLOC_DEBUG_ARGS
)
945 dp
= (cp
== NULL
) ? NULL
: (sbufdup
)(cp
, strlen(cp
) SMALLOC_DEBUG_ARGSCALL
);
951 (sbufdup
)(char const *cp
, size_t len SMALLOC_DEBUG_ARGS
)
956 dp
= (smalloc
)(len
+1 SMALLOC_DEBUG_ARGSCALL
);
965 n_strlcpy(char *dst
, char const *src
, size_t len
)
971 dst
= strncpy(dst
, src
, len
);
978 asccasecmp(char const *s1
, char const *s2
)
984 char c1
= *s1
++, c2
= *s2
++;
985 if ((cmp
= lowerconv(c1
) - lowerconv(c2
)) != 0 || c1
== '\0')
993 ascncasecmp(char const *s1
, char const *s2
, size_t sz
)
999 char c1
= *s1
++, c2
= *s2
++;
1000 cmp
= (ui8_t
)lowerconv(c1
);
1001 cmp
-= (ui8_t
)lowerconv(c2
);
1002 if (cmp
!= 0 || c1
== '\0')
1010 asccasestr(char const *haystack
, char const *xneedle
)
1012 char *needle
= NULL
, *NEEDLE
;
1016 sz
= strlen(xneedle
);
1020 needle
= ac_alloc(sz
);
1021 NEEDLE
= ac_alloc(sz
);
1022 for (i
= 0; i
< sz
; i
++) {
1023 needle
[i
] = lowerconv(xneedle
[i
]);
1024 NEEDLE
[i
] = upperconv(xneedle
[i
]);
1027 while (*haystack
!= '\0') {
1028 if (*haystack
== *needle
|| *haystack
== *NEEDLE
) {
1029 for (i
= 1; i
< sz
; ++i
)
1030 if (haystack
[i
] != needle
[i
] && haystack
[i
] != NEEDLE
[i
])
1039 if (needle
!= NULL
) {
1048 is_asccaseprefix(char const *as1
, char const *as2
)
1053 for (;; ++as1
, ++as2
) {
1054 char c1
= lowerconv(*as1
), c2
= lowerconv(*as2
);
1055 if ((rv
= (c1
== '\0')))
1057 if (c1
!= c2
|| c2
== '\0')
1065 (n_str_dup
)(struct str
*self
, struct str
const *t SMALLOC_DEBUG_ARGS
)
1068 if (t
!= NULL
&& t
->l
> 0) {
1070 self
->s
= (srealloc
)(self
->s
, t
->l
+1 SMALLOC_DEBUG_ARGSCALL
);
1071 memcpy(self
->s
, t
->s
, t
->l
+1);
1079 (n_str_add_buf
)(struct str
*self
, char const *buf
, size_t buflen
1084 size_t sl
= self
->l
;
1085 self
->l
= sl
+ buflen
;
1086 self
->s
= (srealloc
)(self
->s
, self
->l
+1 SMALLOC_DEBUG_ARGSCALL
);
1087 memcpy(self
->s
+ sl
, buf
, buflen
);
1088 self
->s
[self
->l
] = '\0';
1095 * Our iconv(3) wrapper
1099 static void _ic_toupper(char *dest
, char const *src
);
1100 static void _ic_stripdash(char *p
);
1103 _ic_toupper(char *dest
, char const *src
)
1107 *dest
++ = upperconv(*src
);
1108 while (*src
++ != '\0');
1113 _ic_stripdash(char *p
)
1119 if (*(q
= p
) != '-')
1121 while (*p
++ != '\0');
1126 n_iconv_open(char const *tocode
, char const *fromcode
)
1132 if ((id
= iconv_open(tocode
, fromcode
)) != (iconv_t
)-1)
1135 /* Remove the "iso-" prefixes for Solaris */
1136 if (!ascncasecmp(tocode
, "iso-", 4))
1138 else if (!ascncasecmp(tocode
, "iso", 3))
1140 if (!ascncasecmp(fromcode
, "iso-", 4))
1142 else if (!ascncasecmp(fromcode
, "iso", 3))
1144 if (*tocode
== '\0' || *fromcode
== '\0') {
1148 if ((id
= iconv_open(tocode
, fromcode
)) != (iconv_t
)-1)
1151 /* Solaris prefers upper-case charset names. Don't ask... */
1152 t
= salloc(strlen(tocode
) +1);
1153 _ic_toupper(t
, tocode
);
1154 f
= salloc(strlen(fromcode
) +1);
1155 _ic_toupper(f
, fromcode
);
1156 if ((id
= iconv_open(t
, f
)) != (iconv_t
)-1)
1159 /* Strip dashes for UnixWare */
1162 if ((id
= iconv_open(t
, f
)) != (iconv_t
)-1)
1165 /* Add your vendor's sillynesses here */
1167 /* If the encoding names are equal at this point, they are just not
1168 * understood by iconv(), and we cannot sensibly use it in any way. We do
1169 * not perform this as an optimization above since iconv() can otherwise be
1170 * used to check the validity of the input even with identical encoding
1180 n_iconv_close(iconv_t cd
)
1185 iconvd
= (iconv_t
)-1;
1191 n_iconv_reset(iconv_t cd
)
1194 iconv(cd
, NULL
, NULL
, NULL
, NULL
);
1199 /* (2012-09-24: export and use it exclusively to isolate prototype problems
1200 * (*inb* is 'char const **' except in POSIX) in a single place.
1201 * GNU libiconv even allows for configuration time const/non-const..
1202 * In the end it's an ugly guess, but we can't do better since make(1) doesn't
1203 * support compiler invocations which bail on error, so no -Werror */
1204 /* Citrus project? */
1205 # if defined _ICONV_H_ && defined __ICONV_F_HIDE_INVALID
1206 /* DragonFly 3.2.1 is special TODO newer DragonFly too, but different */
1207 # ifdef __DragonFly__
1208 # define __INBCAST(S) (char ** __restrict__)UNCONST(S)
1210 # define __INBCAST(S) (char const **)UNCONST(S)
1214 # define __INBCAST(S) (char **)UNCONST(S)
1218 n_iconv_buf(iconv_t cd
, char const **inb
, size_t *inbleft
,/*XXX redo iconv use*/
1219 char **outb
, size_t *outbleft
, bool_t skipilseq
)
1225 size_t sz
= iconv(cd
, __INBCAST(inb
), inbleft
, outb
, outbleft
);
1226 if (sz
!= (size_t)-1)
1229 if (!skipilseq
|| err
!= EILSEQ
)
1234 } else if (*outbleft
> 0) {
1238 if (*outbleft
> 0/* TODO 0xFFFD 2*/) {
1239 /* TODO 0xFFFD (*outb)[0] = '[';
1240 * TODO (*outb)[1] = '?';
1241 * TODO 0xFFFD (*outb)[2] = ']';
1242 * TODO (*outb) += 3;
1243 * TODO (*outbleft) -= 3; */
1258 n_iconv_str(iconv_t cd
, struct str
*out
, struct str
const *in
,
1259 struct str
*in_rest_or_null
, bool_t skipilseq
)
1272 ol
= (ol
<< 1) - (ol
>> 4);
1283 err
= n_iconv_buf(cd
, &ib
, &il
, &ob
, &ol
, skipilseq
);
1284 if (err
== 0 || err
!= E2BIG
)
1289 obb
= srealloc(obb
, olb
);
1292 if (in_rest_or_null
!= NULL
) {
1293 in_rest_or_null
->s
= UNCONST(ib
);
1294 in_rest_or_null
->l
= il
;
1301 #endif /* HAVE_ICONV */
1303 /* vim:set fenc=utf-8:s-it-mode */