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 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
6 * SPDX-License-Identifier: BSD-3-Clause
9 * Copyright (c) 1980, 1993
10 * The Regents of the University of California. All rights reserved.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 #define n_FILE strings
39 #ifndef HAVE_AMALGAMATION
46 (savestr
)(char const *str n_MEMORY_DEBUG_ARGS
)
53 news
= (n_autorec_alloc_from_pool
)(NULL
, size
+1 n_MEMORY_DEBUG_ARGSCALL
);
55 memcpy(news
, str
, size
);
62 (savestrbuf
)(char const *sbuf
, size_t sbuf_len n_MEMORY_DEBUG_ARGS
)
67 news
= (n_autorec_alloc_from_pool
)(NULL
, sbuf_len
+1
68 n_MEMORY_DEBUG_ARGSCALL
);
70 memcpy(news
, sbuf
, sbuf_len
);
77 (savecatsep
)(char const *s1
, char sep
, char const *s2 n_MEMORY_DEBUG_ARGS
)
83 l1
= (s1
!= NULL
) ? strlen(s1
) : 0;
85 news
= (n_autorec_alloc_from_pool
)(NULL
, l1
+ (sep
!= '\0') + l2
+1
86 n_MEMORY_DEBUG_ARGSCALL
);
88 memcpy(news
+ 0, s1
, l1
);
93 memcpy(news
+ l1
, s2
, l2
);
100 * Support routines, auto-reclaimed storage
104 str_concat_csvl(struct str
*self
, ...) /* XXX onepass maybe better here */
112 for (l
= 0; (cs
= va_arg(vl
, char const*)) != NULL
;)
117 self
->s
= n_autorec_alloc(l
+1);
120 for (l
= 0; (cs
= va_arg(vl
, char const*)) != NULL
;) {
125 memcpy(self
->s
+ l
, cs
, i
);
136 (str_concat_cpa
)(struct str
*self
, char const * const *cpa
,
137 char const *sep_o_null n_MEMORY_DEBUG_ARGS
)
140 char const * const *xcpa
;
143 sonl
= (sep_o_null
!= NULL
) ? strlen(sep_o_null
) : 0;
145 for (l
= 0, xcpa
= cpa
; *xcpa
!= NULL
; ++xcpa
)
146 l
+= strlen(*xcpa
) + sonl
;
149 self
->s
= (n_autorec_alloc_from_pool
)(NULL
, l
+1 n_MEMORY_DEBUG_ARGSCALL
);
151 for (l
= 0, xcpa
= cpa
; *xcpa
!= NULL
; ++xcpa
) {
156 memcpy(self
->s
+ l
, *xcpa
, i
);
160 memcpy(self
->s
+ l
, sep_o_null
, sonl
);
170 * Routines that are not related to auto-reclaimed storage follow.
174 n_anyof_buf(char const *template, char const *dat
, size_t len
){
179 while((c
= *template++) != '\0')
180 if(strchr(dat
, c
) != NULL
)
183 while((c
= *template++) != '\0')
184 if(memchr(dat
, c
, len
) != NULL
)
193 n_strsep(char **iolist
, char sep
, bool_t ignore_empty
){
197 for(base
= *iolist
; base
!= NULL
; base
= *iolist
){
198 while(*base
!= '\0' && blankspacechar(*base
))
201 cp
= strchr(base
, sep
);
206 cp
= &base
[strlen(base
)];
208 while(cp
> base
&& blankspacechar(cp
[-1]))
211 if(*base
!= '\0' || !ignore_empty
)
219 n_strsep_esc(char **iolist
, char sep
, bool_t ignore_empty
){
221 bool_t isesc
, anyesc
;
224 for(base
= *iolist
; base
!= NULL
; base
= *iolist
){
225 while((c
= *base
) != '\0' && blankspacechar(c
))
228 for(isesc
= anyesc
= FAL0
, cp
= base
;; ++cp
){
229 if(n_UNLIKELY((c
= *cp
) == '\0')){
240 anyesc
|= (c
== sep
);
244 while(cp
> base
&& blankspacechar(cp
[-1]))
252 for(ins
= cp
= base
;; ++ins
)
253 if((c
= *cp
) == '\\' && cp
[1] == sep
){
256 }else if((*ins
= (++cp
, c
)) == '\0')
261 if(*base
!= '\0' || !ignore_empty
)
269 is_prefix(char const *as1
, char const *as2
) /* TODO arg order */
274 for (; (c
= *as1
) == *as2
&& c
!= '\0'; ++as1
, ++as2
)
282 string_quote(char const *v
) /* TODO too simpleminded (getrawlist(), +++ ..) */
289 for (i
= 0, cp
= v
; (c
= *cp
) != '\0'; ++i
, ++cp
)
290 if (c
== '"' || c
== '\\')
292 rv
= n_autorec_alloc(i
+1);
294 for (i
= 0, cp
= v
; (c
= *cp
) != '\0'; rv
[i
++] = c
, ++cp
)
295 if (c
== '"' || c
== '\\')
303 makelow(char *cp
) /* TODO isn't that crap? --> */
306 #ifdef HAVE_C90AMEND1
307 if (n_mb_cur_max
> 1) {
312 while (*cp
!= '\0') {
313 len
= mbtowc(&wc
, cp
, n_mb_cur_max
);
318 if (wctomb(tp
, wc
) == len
)
319 tp
+= len
, cp
+= len
;
321 *tp
++ = *cp
++; /* <-- at least here */
328 *cp
= tolower((uc_i
)*cp
);
329 while (*cp
++ != '\0');
335 substr(char const *str
, char const *sub
)
337 char const *cp
, *backup
;
342 while (*str
!= '\0' && *cp
!= '\0') {
343 #ifdef HAVE_C90AMEND1
344 if (n_mb_cur_max
> 1) {
348 if ((sz
= mbtowc(&c
, cp
, n_mb_cur_max
)) == -1)
351 if ((sz
= mbtowc(&c2
, str
, n_mb_cur_max
)) == -1)
357 if ((sz
= mbtowc(&c
, backup
, n_mb_cur_max
)) > 0) {
383 return (*cp
== '\0');
387 sstpcpy(char *dst
, char const *src
)
390 while ((*dst
= *src
++) != '\0')
397 (sstrdup
)(char const *cp n_MEMORY_DEBUG_ARGS
)
402 dp
= (cp
== NULL
) ? NULL
: (sbufdup
)(cp
, strlen(cp
) n_MEMORY_DEBUG_ARGSCALL
);
408 (sbufdup
)(char const *cp
, size_t len n_MEMORY_DEBUG_ARGS
)
413 dp
= (n_alloc
)(len
+1 n_MEMORY_DEBUG_ARGSCALL
);
422 n_strscpy(char *dst
, char const *src
, size_t dstsize
){
426 if(n_LIKELY(dstsize
> 0)){
429 if((dst
[rv
] = src
[rv
]) == '\0')
432 }while(--dstsize
> 0);
446 asccasecmp(char const *s1
, char const *s2
)
452 char c1
= *s1
++, c2
= *s2
++;
453 if ((cmp
= lowerconv(c1
) - lowerconv(c2
)) != 0 || c1
== '\0')
461 ascncasecmp(char const *s1
, char const *s2
, size_t sz
)
467 char c1
= *s1
++, c2
= *s2
++;
468 cmp
= (ui8_t
)lowerconv(c1
);
469 cmp
-= (ui8_t
)lowerconv(c2
);
470 if (cmp
!= 0 || c1
== '\0')
478 asccasestr(char const *s1
, char const *s2
)
483 for (c2
= *s2
++, c2
= lowerconv(c2
);;) {
484 if ((c1
= *s1
++) == '\0') {
488 if (lowerconv(c1
) == c2
&& is_asccaseprefix(s2
, s1
)) {
498 is_asccaseprefix(char const *as1
, char const *as2
) /* TODO arg order */
503 for(;; ++as1
, ++as2
){
509 if(c1
!= c2
|| c1
== '\0')
519 is_ascncaseprefix(char const *as1
, char const *as2
, size_t sz
)
525 for(rv
= TRU1
; sz
-- > 0; ++as1
, ++as2
){
531 if(!(rv
= (c1
== c2
)) || c1
== '\0')
542 (n_str_assign_buf
)(struct str
*self
, char const *buf
, uiz_t buflen
543 n_MEMORY_DEBUG_ARGS
){
545 if(buflen
== UIZ_MAX
)
546 buflen
= (buf
== NULL
) ? 0 : strlen(buf
);
548 assert(buflen
== 0 || buf
!= NULL
);
550 if(n_LIKELY(buflen
> 0)){
551 self
->s
= (n_realloc
)(self
->s
, (self
->l
= buflen
) +1
552 n_MEMORY_DEBUG_ARGSCALL
);
553 memcpy(self
->s
, buf
, buflen
);
554 self
->s
[buflen
] = '\0';
562 (n_str_add_buf
)(struct str
*self
, char const *buf
, uiz_t buflen
563 n_MEMORY_DEBUG_ARGS
){
565 if(buflen
== UIZ_MAX
)
566 buflen
= (buf
== NULL
) ? 0 : strlen(buf
);
568 assert(buflen
== 0 || buf
!= NULL
);
571 size_t osl
= self
->l
, nsl
= osl
+ buflen
;
573 self
->s
= (n_realloc
)(self
->s
, (self
->l
= nsl
) +1
574 n_MEMORY_DEBUG_ARGSCALL
);
575 memcpy(self
->s
+ osl
, buf
, buflen
);
583 n_str_trim(struct str
*self
, enum n_str_trim_flags stf
){
590 if((l
= self
->l
) > 0 && (stf
& n_STR_TRIM_FRONT
)){
591 while(spacechar(*cp
)){
596 self
->s
= n_UNCONST(cp
);
599 if(l
> 0 && (stf
& n_STR_TRIM_END
)){
600 for(cp
+= l
-1; spacechar(*cp
); --cp
)
611 n_str_trim_ifs(struct str
*self
, bool_t dodefaults
){
613 char const *ifs
, *cp
;
617 if((l
= self
->l
) == 0)
620 ifs
= ok_vlook(ifs_ws
);
624 /* Check whether we can go fast(er) path */
625 for(i
= 0; (c
= ifs
[i
]) != '\0'; ++i
){
627 case ' ': s
= c
; break;
628 case '\t': t
= c
; break;
629 case '\n': n
= c
; break;
631 /* Need to go the slow path */
632 while(strchr(ifs
, *cp
) != NULL
){
637 self
->s
= n_UNCONST(cp
);
640 for(cp
+= l
-1; strchr(ifs
, *cp
) != NULL
;){
643 /* An uneven number of reverse solidus escapes last WS! */
644 else if(*--cp
== '\\'){
647 for(j
= 1; l
- (uiz_t
)j
> 0 && cp
[-j
] == '\\'; ++j
)
666 /* No ifs-ws? No more data? No trimming */
667 if(l
== 0 || (i
== 0 && !dodefaults
))
677 while((c
= *cp
) != '\0' && (c
== s
|| c
== t
|| c
== n
)){
682 self
->s
= n_UNCONST(cp
);
686 for(cp
+= l
-1; (c
= *cp
) != '\0' && (c
== s
|| c
== t
|| c
== n
);){
689 /* An uneven number of reverse solidus escapes last WS! */
690 else if(*--cp
== '\\'){
693 for(j
= 1; l
- (uiz_t
)j
> 0 && cp
[-j
] == '\\'; ++j
)
709 * struct n_string TODO extend, optimize
713 (n_string_clear
)(struct n_string
*self n_MEMORY_DEBUG_ARGS
){
716 assert(self
!= NULL
);
718 if(self
->s_size
!= 0){
720 (n_free
)(self
->s_dat n_MEMORY_DEBUG_ARGSCALL
);
722 self
->s_len
= self
->s_auto
= self
->s_size
= 0;
730 (n_string_reserve
)(struct n_string
*self
, size_t noof n_MEMORY_DEBUG_ARGS
){
733 assert(self
!= NULL
);
737 if((size_t)SI32_MAX
- n_ALIGN(1) - l
<= noof
)
738 n_panic(_("Memory allocation too large"));
740 if((i
= s
- l
) <= ++noof
){
741 i
+= l
+ (ui32_t
)noof
;
746 self
->s_dat
= (n_realloc
)(self
->s_dat
, i n_MEMORY_DEBUG_ARGSCALL
);
748 char *ndat
= (n_autorec_alloc_from_pool
)(NULL
, i
749 n_MEMORY_DEBUG_ARGSCALL
);
752 memcpy(ndat
, self
->s_dat
, l
);
761 (n_string_resize
)(struct n_string
*self
, size_t nlen n_MEMORY_DEBUG_ARGS
){
763 assert(self
!= NULL
);
765 if(UICMP(z
, SI32_MAX
, <=, nlen
))
766 n_panic(_("Memory allocation too large"));
768 if(self
->s_len
< nlen
)
769 self
= (n_string_reserve
)(self
, nlen n_MEMORY_DEBUG_ARGSCALL
);
770 self
->s_len
= (ui32_t
)nlen
;
776 (n_string_push_buf
)(struct n_string
*self
, char const *buf
, size_t buflen
777 n_MEMORY_DEBUG_ARGS
){
780 assert(self
!= NULL
);
781 assert(buflen
== 0 || buf
!= NULL
);
783 if(buflen
== UIZ_MAX
)
784 buflen
= (buf
== NULL
) ? 0 : strlen(buf
);
789 self
= (n_string_reserve
)(self
, buflen n_MEMORY_DEBUG_ARGSCALL
);
790 memcpy(&self
->s_dat
[i
= self
->s_len
], buf
, buflen
);
791 self
->s_len
= (i
+= (ui32_t
)buflen
);
798 (n_string_push_c
)(struct n_string
*self
, char c n_MEMORY_DEBUG_ARGS
){
801 assert(self
!= NULL
);
803 if(self
->s_len
+ 1 >= self
->s_size
)
804 self
= (n_string_reserve
)(self
, 1 n_MEMORY_DEBUG_ARGSCALL
);
805 self
->s_dat
[self
->s_len
++] = c
;
811 (n_string_unshift_buf
)(struct n_string
*self
, char const *buf
, size_t buflen
812 n_MEMORY_DEBUG_ARGS
){
815 assert(self
!= NULL
);
816 assert(buflen
== 0 || buf
!= NULL
);
818 if(buflen
== UIZ_MAX
)
819 buflen
= (buf
== NULL
) ? 0 : strlen(buf
);
822 self
= (n_string_reserve
)(self
, buflen n_MEMORY_DEBUG_ARGSCALL
);
824 memmove(&self
->s_dat
[buflen
], self
->s_dat
, self
->s_len
);
825 memcpy(self
->s_dat
, buf
, buflen
);
826 self
->s_len
+= (ui32_t
)buflen
;
833 (n_string_unshift_c
)(struct n_string
*self
, char c n_MEMORY_DEBUG_ARGS
){
836 assert(self
!= NULL
);
838 if(self
->s_len
+ 1 >= self
->s_size
)
839 self
= (n_string_reserve
)(self
, 1 n_MEMORY_DEBUG_ARGSCALL
);
841 memmove(&self
->s_dat
[1], self
->s_dat
, self
->s_len
);
849 (n_string_insert_buf
)(struct n_string
*self
, size_t idx
,
850 char const *buf
, size_t buflen n_MEMORY_DEBUG_ARGS
){
853 assert(self
!= NULL
);
854 assert(buflen
== 0 || buf
!= NULL
);
855 assert(idx
<= self
->s_len
);
857 if(buflen
== UIZ_MAX
)
858 buflen
= (buf
== NULL
) ? 0 : strlen(buf
);
861 self
= (n_string_reserve
)(self
, buflen n_MEMORY_DEBUG_ARGSCALL
);
863 memmove(&self
->s_dat
[idx
+ buflen
], &self
->s_dat
[idx
],
865 memcpy(&self
->s_dat
[idx
], buf
, buflen
);
866 self
->s_len
+= (ui32_t
)buflen
;
873 (n_string_insert_c
)(struct n_string
*self
, size_t idx
,
874 char c n_MEMORY_DEBUG_ARGS
){
877 assert(self
!= NULL
);
878 assert(idx
<= self
->s_len
);
880 if(self
->s_len
+ 1 >= self
->s_size
)
881 self
= (n_string_reserve
)(self
, 1 n_MEMORY_DEBUG_ARGSCALL
);
883 memmove(&self
->s_dat
[idx
+ 1], &self
->s_dat
[idx
], self
->s_len
- idx
);
884 self
->s_dat
[idx
] = c
;
891 n_string_cut(struct n_string
*self
, size_t idx
, size_t len
){
894 assert(self
!= NULL
);
895 assert(UIZ_MAX
- idx
> len
);
896 assert(SI32_MAX
>= idx
+ len
);
897 assert(idx
+ len
<= self
->s_len
);
900 memmove(&self
->s_dat
[idx
], &self
->s_dat
[idx
+ len
],
901 (self
->s_len
-= len
) - idx
);
907 (n_string_cp
)(struct n_string
*self n_MEMORY_DEBUG_ARGS
){
911 assert(self
!= NULL
);
913 if(self
->s_size
== 0)
914 self
= (n_string_reserve
)(self
, 1 n_MEMORY_DEBUG_ARGSCALL
);
916 (rv
= self
->s_dat
)[self
->s_len
] = '\0';
922 n_string_cp_const(struct n_string
const *self
){
926 assert(self
!= NULL
);
928 if(self
->s_size
!= 0){
929 ((struct n_string
*)n_UNCONST(self
))->s_dat
[self
->s_len
] = '\0';
942 n_utf8_to_utf32(char const **bdat
, size_t *blen
){
944 char const *cp
, *cpx
;
949 x
= (ui8_t
)*(cp
= *bdat
);
952 if(n_LIKELY(x
<= 0x7Fu
))
954 /* 0xF8, but Unicode guarantees maximum of 0x10FFFFu -> F4 8F BF BF.
955 * Unicode 9.0, 3.9, UTF-8, Table 3-7. Well-Formed UTF-8 Byte Sequences */
956 else if(n_LIKELY(x
> 0xC0u
&& x
<= 0xF4u
)){
957 if(n_LIKELY(x
< 0xE0u
)){
958 if(n_UNLIKELY(l
< 1))
963 }else if(n_LIKELY(x
< 0xF0u
)){
964 if(n_UNLIKELY(l
< 2))
971 /* Second byte constraints */
975 if(n_UNLIKELY(x
< 0xA0u
|| x
> 0xBFu
))
979 if(n_UNLIKELY(x
< 0x80u
|| x
> 0x9Fu
))
983 if(n_UNLIKELY((x
& 0xC0u
) != 0x80u
))
990 if(n_UNLIKELY(l
< 3))
997 /* Second byte constraints */
1001 if(n_UNLIKELY(x
< 0x90u
|| x
> 0xBFu
))
1005 if(n_UNLIKELY((x
& 0xF0u
) != 0x80u
)) /* 80..8F */
1009 if(n_UNLIKELY((x
& 0xC0u
) != 0x80u
))
1017 if(n_UNLIKELY((x
& 0xC0u
) != 0x80u
))
1024 if(n_UNLIKELY((x
& 0xC0u
) != 0x80u
))
1045 n_utf32_to_utf8(ui32_t c
, char *buf
)
1052 ui8_t dec_leader_mask
;
1053 ui8_t dec_leader_val_mask
;
1054 ui8_t dec_bytes_togo
;
1058 {0x00000000, 0x00000000, 0x00, 0, 0x00, 0x00, 0, 0, {0,}},
1059 {0x00000000, 0x0000007F, 0x00, 1, 0x80, 0x7F, 1-1, 1, {0,}},
1060 {0x00000080, 0x000007FF, 0xC0, 2, 0xE0, 0xFF-0xE0, 2-1, 2, {0,}},
1061 /* We assume surrogates are U+D800 - U+DFFF, _cat index 3 */
1062 /* xxx _from_utf32() simply assumes magic code points for surrogates!
1063 * xxx (However, should we ever get yet another surrogate range we
1064 * xxx need to deal with that all over the place anyway? */
1065 {0x00000800, 0x0000FFFF, 0xE0, 3, 0xF0, 0xFF-0xF0, 3-1, 3, {0,}},
1066 {0x00010000, 0x0010FFFF, 0xF0, 4, 0xF8, 0xFF-0xF8, 4-1, 4, {0,}},
1070 if (c
<= _cat
[0].upper_bound
) { catp
+= 0; goto j0
; }
1071 if (c
<= _cat
[1].upper_bound
) { catp
+= 1; goto j1
; }
1072 if (c
<= _cat
[2].upper_bound
) { catp
+= 2; goto j2
; }
1073 if (c
<= _cat
[3].upper_bound
) {
1074 /* Surrogates may not be converted (Compatibility rule C10) */
1075 if (c
>= 0xD800u
&& c
<= 0xDFFFu
)
1080 if (c
<= _cat
[4].upper_bound
) { catp
+= 4; goto j4
; }
1082 c
= 0xFFFDu
; /* Unicode replacement character */
1086 buf
[3] = (char)0x80u
| (char)(c
& 0x3Fu
); c
>>= 6;
1088 buf
[2] = (char)0x80u
| (char)(c
& 0x3Fu
); c
>>= 6;
1090 buf
[1] = (char)0x80u
| (char)(c
& 0x3Fu
); c
>>= 6;
1092 buf
[0] = (char)catp
->enc_leader
| (char)(c
);
1094 buf
[catp
->enc_lval
] = '\0';
1101 * Our iconv(3) wrapper
1105 n_iconv_normalize_name(char const *cset
){
1106 char *cp
, c
, *tcp
, tc
;
1110 /* We need to strip //SUFFIXes off, we want to normalize to all lowercase,
1111 * and we perform some slight content testing, too */
1112 for(any
= FAL0
, cp
= n_UNCONST(cset
); (c
= *cp
) != '\0'; ++cp
){
1113 if(!alnumchar(c
) && !punctchar(c
)){
1114 n_err(_("Invalid character set name %s\n"),
1115 n_shexp_quote_cp(cset
, FAL0
));
1120 else if(upperchar(c
))
1124 if(any
|| c
!= '\0'){
1125 cp
= savestrbuf(cset
, PTR2SIZE(cp
- cset
));
1126 for(tcp
= cp
; (tc
= *tcp
) != '\0'; ++tcp
)
1127 *tcp
= lowerconv(tc
);
1129 if(c
!= '\0' && (n_poption
& n_PO_D_V
))
1130 n_err(_("Stripped off character set suffix: %s -> %s\n"),
1131 n_shexp_quote_cp(cset
, FAL0
), n_shexp_quote_cp(cp
, FAL0
));
1137 return n_UNCONST(cset
);
1141 n_iconv_name_is_ascii(char const *cset
){ /* TODO ctext/su */
1145 /* In MIME preference order */
1146 rv
= (!asccasecmp(cset
, "US-ASCII") || !asccasecmp(cset
, "ASCII") ||
1147 !asccasecmp(cset
, "ANSI_X3.4-1968") ||
1148 !asccasecmp(cset
, "iso-ir-6") ||
1149 !asccasecmp(cset
, "ANSI_X3.4-1986") ||
1150 !asccasecmp(cset
, "ISO_646.irv:1991") ||
1151 !asccasecmp(cset
, "ISO646-US") || !asccasecmp(cset
, "us") ||
1152 !asccasecmp(cset
, "IBM367") || !asccasecmp(cset
, "cp367") ||
1153 !asccasecmp(cset
, "csASCII"));
1160 n_iconv_open(char const *tocode
, char const *fromcode
){
1164 if((!asccasecmp(fromcode
, "unknown-8bit") ||
1165 !asccasecmp(fromcode
, "binary")) &&
1166 (fromcode
= ok_vlook(charset_unknown_8bit
)) == NULL
)
1167 fromcode
= ok_vlook(CHARSET_8BIT_OKEY
);
1169 id
= iconv_open(tocode
, fromcode
);
1171 /* If the encoding names are equal at this point, they are just not
1172 * understood by iconv(), and we cannot sensibly use it in any way. We do
1173 * not perform this as an optimization above since iconv() can otherwise be
1174 * used to check the validity of the input even with identical encoding
1176 if (id
== (iconv_t
)-1 && !asccasecmp(tocode
, fromcode
))
1177 n_err_no
= n_ERR_NONE
;
1183 n_iconv_close(iconv_t cd
){
1187 iconvd
= (iconv_t
)-1;
1192 n_iconv_reset(iconv_t cd
){
1194 iconv(cd
, NULL
, NULL
, NULL
, NULL
);
1198 /* (2012-09-24: export and use it exclusively to isolate prototype problems
1199 * (*inb* is 'char const **' except in POSIX) in a single place.
1200 * GNU libiconv even allows for configuration time const/non-const..
1201 * In the end it's an ugly guess, but we can't do better since make(1) doesn't
1202 * support compiler invocations which bail on error, so no -Werror */
1203 /* Citrus project? */
1204 # if defined _ICONV_H_ && defined __ICONV_F_HIDE_INVALID
1205 /* DragonFly 3.2.1 is special TODO newer DragonFly too, but different */
1207 # define __INBCAST(S) (char ** __restrict__)n_UNCONST(S)
1209 # define __INBCAST(S) (char const **)n_UNCONST(S)
1211 # elif n_OS_SUNOS || n_OS_SOLARIS
1212 # define __INBCAST(S) (char const ** __restrict__)n_UNCONST(S)
1215 # define __INBCAST(S) (char **)n_UNCONST(S)
1219 n_iconv_buf(iconv_t cd
, enum n_iconv_flags icf
,
1220 char const **inb
, size_t *inbleft
, char **outb
, size_t *outbleft
){
1224 if((icf
& n_ICONV_UNIREPL
) && !(n_psonce
& n_PSO_UNICODE
))
1225 icf
&= ~n_ICONV_UNIREPL
;
1230 if((sz
= iconv(cd
, __INBCAST(inb
), inbleft
, outb
, outbleft
)) == 0)
1232 if(sz
!= (size_t)-1){
1233 if(!(icf
& n_ICONV_IGN_NOREVERSE
)){
1240 if((err
= n_err_no
) == n_ERR_2BIG
)
1243 if(!(icf
& n_ICONV_IGN_ILSEQ
) || err
!= n_ERR_ILSEQ
)
1248 if(icf
& n_ICONV_UNIREPL
){
1249 if(*outbleft
>= sizeof(n_unirepl
) -1){
1250 memcpy(*outb
, n_unirepl
, sizeof(n_unirepl
) -1);
1251 *outb
+= sizeof(n_unirepl
) -1;
1252 *outbleft
-= sizeof(n_unirepl
) -1;
1255 }else if(*outbleft
> 0){
1262 }else if(*outbleft
> 0){
1269 n_iconv_err_no
= err
;
1276 n_iconv_str(iconv_t cd
, enum n_iconv_flags icf
,
1277 struct str
*out
, struct str
const *in
, struct str
*in_rest_or_null
){
1278 struct n_string s
, *sp
= &s
;
1285 if(!n_string_get_can_book(il
) || !n_string_get_can_book(out
->l
)){
1291 sp
= n_string_creat(sp
);
1292 sp
= n_string_take_ownership(sp
, out
->s
, out
->l
, 0);
1298 if((nol
= ol
= sp
->s_len
) < il
)
1300 assert(sizeof(sp
->s_len
) == sizeof(ui32_t
));
1306 xnol
= (ui64_t
)(nol
<< 1) - (nol
>> 4);
1307 if(!n_string_can_book(sp
, xnol
)){
1309 if(!n_string_can_book(sp
, xnol
)){
1316 sp
= n_string_resize(sp
, nol
);
1318 ob
= ob_base
= &sp
->s_dat
[ol
];
1320 err
= n_iconv_buf(cd
, icf
, &ib
, &il
, &ob
, &nol
);
1322 sp
= n_string_trunc(sp
, ol
+ PTR2SIZE(ob
- ob_base
));
1323 if(err
== 0 || err
!= n_ERR_2BIG
)
1327 if(in_rest_or_null
!= NULL
){
1328 in_rest_or_null
->s
= n_UNCONST(ib
);
1329 in_rest_or_null
->l
= il
;
1333 out
->s
= n_string_cp(sp
);
1335 sp
= n_string_drop_ownership(sp
);
1336 /* n_string_gut(sp)*/
1343 n_iconv_onetime_cp(enum n_iconv_flags icf
,
1344 char const *tocode
, char const *fromcode
, char const *input
){
1352 tocode
= ok_vlook(ttycharset
);
1353 if(fromcode
== NULL
)
1356 if((icd
= iconv_open(tocode
, fromcode
)) == (iconv_t
)-1)
1359 in
.l
= strlen(in
.s
= n_UNCONST(input
)); /* logical */
1360 out
.s
= NULL
, out
.l
= 0;
1361 if(!n_iconv_str(icd
, icf
, &out
, &in
, NULL
))
1362 rv
= savestrbuf(out
.s
, out
.l
);
1371 #endif /* HAVE_ICONV */