1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ String support routines.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 #define n_FILE strings
38 #ifndef HAVE_AMALGAMATION
45 (savestr
)(char const *str SALLOC_DEBUG_ARGS
)
51 size
= strlen(str
) +1;
52 news
= (salloc
)(size SALLOC_DEBUG_ARGSCALL
);
53 memcpy(news
, str
, size
);
59 (savestrbuf
)(char const *sbuf
, size_t sbuf_len SALLOC_DEBUG_ARGS
)
64 news
= (salloc
)(sbuf_len
+1 SALLOC_DEBUG_ARGSCALL
);
65 memcpy(news
, sbuf
, sbuf_len
);
72 (savecatsep
)(char const *s1
, char sep
, char const *s2 SALLOC_DEBUG_ARGS
)
78 l1
= (s1
!= NULL
) ? strlen(s1
) : 0;
80 news
= (salloc
)(l1
+ (sep
!= '\0') + l2
+1 SALLOC_DEBUG_ARGSCALL
);
82 memcpy(news
+ 0, s1
, l1
);
86 memcpy(news
+ l1
, s2
, l2
);
93 * Support routines, auto-reclaimed storage
97 (i_strdup
)(char const *src SALLOC_DEBUG_ARGS
)
104 dest
= (salloc
)(sz SALLOC_DEBUG_ARGSCALL
);
105 i_strcpy(dest
, src
, sz
);
111 str_concat_csvl(struct str
*self
, ...) /* XXX onepass maybe better here */
119 for (l
= 0; (cs
= va_arg(vl
, char const*)) != NULL
;)
124 self
->s
= salloc(l
+1);
127 for (l
= 0; (cs
= va_arg(vl
, char const*)) != NULL
;) {
128 size_t i
= strlen(cs
);
129 memcpy(self
->s
+ l
, cs
, i
);
139 (str_concat_cpa
)(struct str
*self
, char const * const *cpa
,
140 char const *sep_o_null SALLOC_DEBUG_ARGS
)
143 char const * const *xcpa
;
146 sonl
= (sep_o_null
!= NULL
) ? strlen(sep_o_null
) : 0;
148 for (l
= 0, xcpa
= cpa
; *xcpa
!= NULL
; ++xcpa
)
149 l
+= strlen(*xcpa
) + sonl
;
152 self
->s
= (salloc
)(l
+1 SALLOC_DEBUG_ARGSCALL
);
154 for (l
= 0, xcpa
= cpa
; *xcpa
!= NULL
; ++xcpa
) {
155 size_t i
= strlen(*xcpa
);
156 memcpy(self
->s
+ l
, *xcpa
, i
);
159 memcpy(self
->s
+ l
, sep_o_null
, sonl
);
169 * Routines that are not related to auto-reclaimed storage follow.
173 anyof(char const *s1
, char const *s2
)
176 for (; *s1
!= '\0'; ++s1
)
177 if (strchr(s2
, *s1
) != NULL
)
180 return (*s1
!= '\0');
184 n_strsep(char **iolist
, char sep
, bool_t ignore_empty
)
189 for (base
= *iolist
; base
!= NULL
; base
= *iolist
) {
190 while (*base
!= '\0' && blankspacechar(*base
))
192 cp
= strchr(base
, sep
);
197 cp
= base
+ strlen(base
);
199 while (cp
> base
&& blankspacechar(cp
[-1]))
202 if (*base
!= '\0' || !ignore_empty
)
210 n_strescsep(char **iolist
, char sep
, bool_t ignore_empty
){
212 bool_t isesc
, anyesc
;
217 for(base
= *iolist
; base
!= NULL
; base
= *iolist
){
218 while((c
= *base
) != '\0' && blankspacechar(c
))
221 for(isesc
= anyesc
= FAL0
, cp
= base
;; ++cp
){
222 if(UNLIKELY((c
= *cp
) == '\0')){
233 anyesc
|= (c
== sep
);
237 while(cp
> base
&& blankspacechar(cp
[-1]))
245 for(ins
= cp
= base
;; ++ins
)
246 if((c
= *cp
) == '\\' && cp
[1] == sep
){
249 }else if((*ins
= (++cp
, c
)) == '\0')
262 i_strcpy(char *dest
, char const *src
, size_t size
)
266 for (;; ++dest
, ++src
)
267 if ((*dest
= lowerconv(*src
)) == '\0') {
269 } else if (--size
== 0) {
278 is_prefix(char const *as1
, char const *as2
)
283 for (; (c
= *as1
) == *as2
&& c
!= '\0'; ++as1
, ++as2
)
291 string_quote(char const *v
) /* TODO too simpleminded (getrawlist(), +++ ..) */
298 for (i
= 0, cp
= v
; (c
= *cp
) != '\0'; ++i
, ++cp
)
299 if (c
== '"' || c
== '\\')
303 for (i
= 0, cp
= v
; (c
= *cp
) != '\0'; rv
[i
++] = c
, ++cp
)
304 if (c
== '"' || c
== '\\')
312 laststring(char *linebuf
, bool_t
*needs_list
, bool_t strip
)
314 char *cp
, *p
, quoted
;
317 /* Anything to do at all? */
318 if (*(cp
= linebuf
) == '\0')
320 cp
+= strlen(linebuf
) -1;
322 /* Strip away trailing blanks */
323 while (spacechar(*cp
) && cp
> linebuf
)
329 /* Now search for the BOS of the "last string" */
331 if (quoted
== '\'' || quoted
== '"') {
337 while (cp
> linebuf
) {
342 } else if (!spacechar(*cp
))
344 if (cp
== linebuf
|| cp
[-1] != '\\') {
345 /* When in whitespace mode, WS prefix doesn't belong */
350 /* Expand the escaped quote character */
351 for (p
= --cp
; (p
[0] = p
[1]) != '\0'; ++p
)
354 if (strip
&& quoted
!= ' ' && *cp
== quoted
)
355 for (p
= cp
; (p
[0] = p
[1]) != '\0'; ++p
)
358 /* The "last string" has been skipped over, but still, try to step backwards
359 * until we are at BOS or see whitespace, so as to make possible things like
360 * "? copy +'x y.mbox'" or even "? copy +x\ y.mbox" */
361 while (cp
> linebuf
) {
363 if (spacechar(*cp
)) {
366 /* We can furtherly release our callees if we now decide wether the
367 * remaining non-"last string" line content contains non-WS */
368 while (--p
>= linebuf
)
377 if (cp
!= NULL
&& *cp
== '\0')
379 *needs_list
= (cp
!= linebuf
&& *linebuf
!= '\0');
390 makelow(char *cp
) /* TODO isn't that crap? --> */
393 #ifdef HAVE_C90AMEND1
394 if (mb_cur_max
> 1) {
399 while (*cp
!= '\0') {
400 len
= mbtowc(&wc
, cp
, mb_cur_max
);
405 if (wctomb(tp
, wc
) == len
)
406 tp
+= len
, cp
+= len
;
408 *tp
++ = *cp
++; /* <-- at least here */
415 *cp
= tolower((uc_i
)*cp
);
416 while (*cp
++ != '\0');
422 substr(char const *str
, char const *sub
)
424 char const *cp
, *backup
;
429 while (*str
!= '\0' && *cp
!= '\0') {
430 #ifdef HAVE_C90AMEND1
431 if (mb_cur_max
> 1) {
435 if ((sz
= mbtowc(&c
, cp
, mb_cur_max
)) == -1)
438 if ((sz
= mbtowc(&c2
, str
, mb_cur_max
)) == -1)
444 if ((sz
= mbtowc(&c
, backup
, mb_cur_max
)) > 0) {
470 return (*cp
== '\0');
474 sstpcpy(char *dst
, char const *src
)
477 while ((*dst
= *src
++) != '\0')
484 (sstrdup
)(char const *cp SMALLOC_DEBUG_ARGS
)
489 dp
= (cp
== NULL
) ? NULL
: (sbufdup
)(cp
, strlen(cp
) SMALLOC_DEBUG_ARGSCALL
);
495 (sbufdup
)(char const *cp
, size_t len SMALLOC_DEBUG_ARGS
)
500 dp
= (smalloc
)(len
+1 SMALLOC_DEBUG_ARGSCALL
);
509 n_strscpy(char *dst
, char const *src
, size_t dstsize
){
513 if(LIKELY(dstsize
> 0)){
516 if((dst
[rv
] = src
[rv
]) == '\0')
519 }while(--dstsize
> 0);
533 asccasecmp(char const *s1
, char const *s2
)
539 char c1
= *s1
++, c2
= *s2
++;
540 if ((cmp
= lowerconv(c1
) - lowerconv(c2
)) != 0 || c1
== '\0')
548 ascncasecmp(char const *s1
, char const *s2
, size_t sz
)
554 char c1
= *s1
++, c2
= *s2
++;
555 cmp
= (ui8_t
)lowerconv(c1
);
556 cmp
-= (ui8_t
)lowerconv(c2
);
557 if (cmp
!= 0 || c1
== '\0')
565 asccasestr(char const *s1
, char const *s2
)
570 for (c2
= *s2
++, c2
= lowerconv(c2
);;) {
571 if ((c1
= *s1
++) == '\0') {
575 if (lowerconv(c1
) == c2
&& is_asccaseprefix(s1
, s2
)) {
585 is_asccaseprefix(char const *as1
, char const *as2
)
590 for (;; ++as1
, ++as2
) {
591 char c1
= lowerconv(*as1
), c2
= lowerconv(*as2
);
593 if ((rv
= (c2
== '\0')))
603 (n_str_assign_buf
)(struct str
*self
, char const *buf
, uiz_t buflen
606 if(buflen
== UIZ_MAX
)
607 buflen
= (buf
== NULL
) ? 0 : strlen(buf
);
609 assert(buflen
== 0 || buf
!= NULL
);
611 if(LIKELY(buflen
> 0)){
612 self
->s
= (srealloc
)(self
->s
, (self
->l
= buflen
) +1
613 SMALLOC_DEBUG_ARGSCALL
);
614 memcpy(self
->s
, buf
, buflen
);
615 self
->s
[buflen
] = '\0';
623 (n_str_add_buf
)(struct str
*self
, char const *buf
, uiz_t buflen
626 if(buflen
== UIZ_MAX
)
627 buflen
= (buf
== NULL
) ? 0 : strlen(buf
);
629 assert(buflen
== 0 || buf
!= NULL
);
632 size_t osl
= self
->l
, nsl
= osl
+ buflen
;
634 self
->s
= (srealloc
)(self
->s
, (self
->l
= nsl
) +1 SMALLOC_DEBUG_ARGSCALL
);
635 memcpy(self
->s
+ osl
, buf
, buflen
);
643 * struct n_string TODO extend, optimize
647 (n_string_clear
)(struct n_string
*self SMALLOC_DEBUG_ARGS
){
650 assert(self
!= NULL
);
652 if(self
->s_size
!= 0){
654 #ifdef HAVE_MEMORY_DEBUG
655 sfree(self
->s_dat SMALLOC_DEBUG_ARGSCALL
);
660 self
->s_len
= self
->s_auto
= self
->s_size
= 0;
668 (n_string_reserve
)(struct n_string
*self
, size_t noof SMALLOC_DEBUG_ARGS
){
672 assert(self
!= NULL
);
676 #if 0 /* FIXME memory alloc too large */
677 if(SI32_MAX
- n_ALIGN(1) - l
<= noof
)
678 n_panic(_("Memory allocation too large"));
681 if((i
= s
- l
) <= noof
){
682 i
+= 1 + l
+ (ui32_t
)noof
;
687 self
->s_dat
= (srealloc
)(self
->s_dat
, i SMALLOC_DEBUG_ARGSCALL
);
689 char *ndat
= (salloc
)(i SALLOC_DEBUG_ARGSCALL
);
692 memcpy(ndat
, self
->s_dat
, l
);
701 (n_string_push_buf
)(struct n_string
*self
, char const *buf
, size_t buflen
705 assert(self
!= NULL
);
706 assert(buflen
== 0 || buf
!= NULL
);
708 if(buflen
== UIZ_MAX
)
709 buflen
= (buf
== NULL
) ? 0 : strlen(buf
);
714 self
= (n_string_reserve
)(self
, buflen SMALLOC_DEBUG_ARGSCALL
);
715 memcpy(self
->s_dat
+ (i
= self
->s_len
), buf
, buflen
);
716 self
->s_len
= (i
+= (ui32_t
)buflen
);
723 (n_string_push_c
)(struct n_string
*self
, char c SMALLOC_DEBUG_ARGS
){
726 assert(self
!= NULL
);
728 if(self
->s_len
+ 1 >= self
->s_size
)
729 self
= (n_string_reserve
)(self
, 1 SMALLOC_DEBUG_ARGSCALL
);
730 self
->s_dat
[self
->s_len
++] = c
;
736 (n_string_unshift_buf
)(struct n_string
*self
, char const *buf
, size_t buflen
740 assert(self
!= NULL
);
741 assert(buflen
== 0 || buf
!= NULL
);
743 if(buflen
== UIZ_MAX
)
744 buflen
= (buf
== NULL
) ? 0 : strlen(buf
);
747 self
= (n_string_reserve
)(self
, buflen SMALLOC_DEBUG_ARGSCALL
);
749 memmove(self
->s_dat
+ buflen
, self
->s_dat
, self
->s_len
);
750 memcpy(self
->s_dat
, buf
, buflen
);
751 self
->s_len
+= (ui32_t
)buflen
;
758 (n_string_unshift_c
)(struct n_string
*self
, char c SMALLOC_DEBUG_ARGS
){
761 assert(self
!= NULL
);
763 if(self
->s_len
+ 1 >= self
->s_size
)
764 self
= (n_string_reserve
)(self
, 1 SMALLOC_DEBUG_ARGSCALL
);
766 memmove(self
->s_dat
+ 1, self
->s_dat
, self
->s_len
);
774 (n_string_cp
)(struct n_string
*self SMALLOC_DEBUG_ARGS
){
778 assert(self
!= NULL
);
780 if(self
->s_size
== 0)
781 self
= (n_string_reserve
)(self
, 1 SMALLOC_DEBUG_ARGSCALL
);
783 (rv
= self
->s_dat
)[self
->s_len
] = '\0';
789 n_string_cp_const(struct n_string
const *self
){
793 assert(self
!= NULL
);
795 if(self
->s_size
!= 0){
796 ((struct n_string
*)UNCONST(self
))->s_dat
[self
->s_len
] = '\0';
808 #ifdef HAVE_NATCH_CHAR
810 n_utf8_to_utf32(char const **bdat
, size_t *blen
) /* TODO check false UTF8 */
824 if ((x
& 0xE0) == 0xC0) {
829 } else if ((x
& 0xF0) == 0xE0) {
863 #endif /* HAVE_NATCH_CHAR */
865 #ifdef HAVE_FILTER_HTML_TAGSOUP
867 n_utf32_to_utf8(ui32_t c
, char *buf
)
874 ui8_t dec_leader_mask
;
875 ui8_t dec_leader_val_mask
;
876 ui8_t dec_bytes_togo
;
880 {0x00000000, 0x00000000, 0x00, 0, 0x00, 0x00, 0, 0, {0,}},
881 {0x00000000, 0x0000007F, 0x00, 1, 0x80, 0x7F, 1-1, 1, {0,}},
882 {0x00000080, 0x000007FF, 0xC0, 2, 0xE0, 0xFF-0xE0, 2-1, 2, {0,}},
883 /* We assume surrogates are U+D800 - U+DFFF, _cat index 3 */
884 /* xxx _from_utf32() simply assumes magic code points for surrogates!
885 * xxx (However, should we ever get yet another surrogate range we
886 * xxx need to deal with that all over the place anyway? */
887 {0x00000800, 0x0000FFFF, 0xE0, 3, 0xF0, 0xFF-0xF0, 3-1, 3, {0,}},
888 {0x00010000, 0x0010FFFF, 0xF0, 4, 0xF8, 0xFF-0xF8, 4-1, 4, {0,}},
892 if (c
<= _cat
[0].upper_bound
) { catp
+= 0; goto j0
; }
893 if (c
<= _cat
[1].upper_bound
) { catp
+= 1; goto j1
; }
894 if (c
<= _cat
[2].upper_bound
) { catp
+= 2; goto j2
; }
895 if (c
<= _cat
[3].upper_bound
) {
896 /* Surrogates may not be converted (Compatibility rule C10) */
897 if (c
>= 0xD800u
&& c
<= 0xDFFFu
)
902 if (c
<= _cat
[4].upper_bound
) { catp
+= 4; goto j4
; }
904 c
= 0xFFFDu
; /* Unicode replacement character */
908 buf
[3] = (char)0x80 | (char)(c
& 0x3F); c
>>= 6;
910 buf
[2] = (char)0x80 | (char)(c
& 0x3F); c
>>= 6;
912 buf
[1] = (char)0x80 | (char)(c
& 0x3F); c
>>= 6;
914 buf
[0] = (char)catp
->enc_leader
| (char)(c
);
916 buf
[catp
->enc_lval
] = '\0';
921 #endif /* HAVE_FILTER_HTML_TAGSOUP */
924 * Our iconv(3) wrapper
928 static void _ic_toupper(char *dest
, char const *src
);
929 static void _ic_stripdash(char *p
);
932 _ic_toupper(char *dest
, char const *src
)
936 *dest
++ = upperconv(*src
);
937 while (*src
++ != '\0');
942 _ic_stripdash(char *p
)
950 while (*p
++ != '\0');
955 n_iconv_open(char const *tocode
, char const *fromcode
)
961 if ((!asccasecmp(fromcode
, "unknown-8bit") ||
962 !asccasecmp(fromcode
, "binary")) &&
963 (fromcode
= ok_vlook(charset_unknown_8bit
)) == NULL
)
964 fromcode
= charset_get_8bit();
966 if ((id
= iconv_open(tocode
, fromcode
)) != (iconv_t
)-1)
969 /* Remove the "iso-" prefixes for Solaris */
970 if (!ascncasecmp(tocode
, "iso-", 4))
972 else if (!ascncasecmp(tocode
, "iso", 3))
974 if (!ascncasecmp(fromcode
, "iso-", 4))
976 else if (!ascncasecmp(fromcode
, "iso", 3))
978 if (*tocode
== '\0' || *fromcode
== '\0') {
982 if ((id
= iconv_open(tocode
, fromcode
)) != (iconv_t
)-1)
985 /* Solaris prefers upper-case charset names. Don't ask... */
986 t
= salloc(strlen(tocode
) +1);
987 _ic_toupper(t
, tocode
);
988 f
= salloc(strlen(fromcode
) +1);
989 _ic_toupper(f
, fromcode
);
990 if ((id
= iconv_open(t
, f
)) != (iconv_t
)-1)
993 /* Strip dashes for UnixWare */
996 if ((id
= iconv_open(t
, f
)) != (iconv_t
)-1)
999 /* Add your vendor's sillynesses here */
1001 /* If the encoding names are equal at this point, they are just not
1002 * understood by iconv(), and we cannot sensibly use it in any way. We do
1003 * not perform this as an optimization above since iconv() can otherwise be
1004 * used to check the validity of the input even with identical encoding
1014 n_iconv_close(iconv_t cd
)
1019 iconvd
= (iconv_t
)-1;
1024 n_iconv_reset(iconv_t cd
)
1027 iconv(cd
, NULL
, NULL
, NULL
, NULL
);
1031 /* (2012-09-24: export and use it exclusively to isolate prototype problems
1032 * (*inb* is 'char const **' except in POSIX) in a single place.
1033 * GNU libiconv even allows for configuration time const/non-const..
1034 * In the end it's an ugly guess, but we can't do better since make(1) doesn't
1035 * support compiler invocations which bail on error, so no -Werror */
1036 /* Citrus project? */
1037 # if defined _ICONV_H_ && defined __ICONV_F_HIDE_INVALID
1038 /* DragonFly 3.2.1 is special TODO newer DragonFly too, but different */
1040 # define __INBCAST(S) (char ** __restrict__)UNCONST(S)
1042 # define __INBCAST(S) (char const **)UNCONST(S)
1044 # elif OS_SUNOS || OS_SOLARIS
1045 # define __INBCAST(S) (char const ** __restrict__)UNCONST(S)
1048 # define __INBCAST(S) (char **)UNCONST(S)
1052 n_iconv_buf(iconv_t cd
, char const **inb
, size_t *inbleft
,/*XXX redo iconv use*/
1053 char **outb
, size_t *outbleft
, bool_t skipilseq
)
1059 size_t sz
= iconv(cd
, __INBCAST(inb
), inbleft
, outb
, outbleft
);
1060 if (sz
!= (size_t)-1)
1063 if (!skipilseq
|| err
!= EILSEQ
)
1068 } else if (*outbleft
> 0) {
1072 if (*outbleft
> 0/* TODO 0xFFFD 2*/) {
1073 /* TODO 0xFFFD (*outb)[0] = '[';
1074 * TODO (*outb)[1] = '?';
1075 * TODO 0xFFFD (*outb)[2] = ']';
1076 * TODO (*outb) += 3;
1077 * TODO (*outbleft) -= 3; */
1092 n_iconv_str(iconv_t cd
, struct str
*out
, struct str
const *in
,
1093 struct str
*in_rest_or_null
, bool_t skipilseq
)
1106 ol
= (ol
<< 1) - (ol
>> 4);
1117 err
= n_iconv_buf(cd
, &ib
, &il
, &ob
, &ol
, skipilseq
);
1118 if (err
== 0 || err
!= E2BIG
)
1123 obb
= srealloc(obb
, olb
+1);
1126 if (in_rest_or_null
!= NULL
) {
1127 in_rest_or_null
->s
= UNCONST(ib
);
1128 in_rest_or_null
->l
= il
;
1131 out
->s
[out
->l
= olb
- ol
] = '\0';
1135 #endif /* HAVE_ICONV */