Add *{smime,ssl}-ca-flags*..
[s-mailx.git] / strings.c
blob851b86bb961ead85594bb04cf066920e1ae1587f
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 - 2017 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
6 */
7 /*
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
13 * are met:
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
33 * SUCH DAMAGE.
35 #undef n_FILE
36 #define n_FILE strings
38 #ifndef HAVE_AMALGAMATION
39 # include "nail.h"
40 #endif
42 #include <ctype.h>
44 FL char *
45 (savestr)(char const *str n_MEMORY_DEBUG_ARGS)
47 size_t size;
48 char *news;
49 NYD_ENTER;
51 size = strlen(str) +1;
52 news = (n_autorec_alloc)(NULL, size n_MEMORY_DEBUG_ARGSCALL);
53 memcpy(news, str, size);
54 NYD_LEAVE;
55 return news;
58 FL char *
59 (savestrbuf)(char const *sbuf, size_t sbuf_len n_MEMORY_DEBUG_ARGS)
61 char *news;
62 NYD_ENTER;
64 news = (n_autorec_alloc)(NULL, sbuf_len +1 n_MEMORY_DEBUG_ARGSCALL);
65 memcpy(news, sbuf, sbuf_len);
66 news[sbuf_len] = 0;
67 NYD_LEAVE;
68 return news;
71 FL char *
72 (savecatsep)(char const *s1, char sep, char const *s2 n_MEMORY_DEBUG_ARGS)
74 size_t l1, l2;
75 char *news;
76 NYD_ENTER;
78 l1 = (s1 != NULL) ? strlen(s1) : 0;
79 l2 = strlen(s2);
80 news = (n_autorec_alloc)(NULL, l1 + (sep != '\0') + l2 +1
81 n_MEMORY_DEBUG_ARGSCALL);
82 if (l1 > 0) {
83 memcpy(news + 0, s1, l1);
84 if (sep != '\0')
85 news[l1++] = sep;
87 memcpy(news + l1, s2, l2);
88 news[l1 + l2] = '\0';
89 NYD_LEAVE;
90 return news;
94 * Support routines, auto-reclaimed storage
97 FL char *
98 (i_strdup)(char const *src n_MEMORY_DEBUG_ARGS)
100 size_t sz;
101 char *dest;
102 NYD_ENTER;
104 sz = strlen(src) +1;
105 dest = (n_autorec_alloc)(NULL, sz n_MEMORY_DEBUG_ARGSCALL);
106 i_strcpy(dest, src, sz);
107 NYD_LEAVE;
108 return dest;
111 FL struct str *
112 str_concat_csvl(struct str *self, ...) /* XXX onepass maybe better here */
114 va_list vl;
115 size_t l;
116 char const *cs;
117 NYD_ENTER;
119 va_start(vl, self);
120 for (l = 0; (cs = va_arg(vl, char const*)) != NULL;)
121 l += strlen(cs);
122 va_end(vl);
124 self->l = l;
125 self->s = salloc(l +1);
127 va_start(vl, self);
128 for (l = 0; (cs = va_arg(vl, char const*)) != NULL;) {
129 size_t i = strlen(cs);
130 memcpy(self->s + l, cs, i);
131 l += i;
133 self->s[l] = '\0';
134 va_end(vl);
135 NYD_LEAVE;
136 return self;
139 FL struct str *
140 (str_concat_cpa)(struct str *self, char const * const *cpa,
141 char const *sep_o_null n_MEMORY_DEBUG_ARGS)
143 size_t sonl, l;
144 char const * const *xcpa;
145 NYD_ENTER;
147 sonl = (sep_o_null != NULL) ? strlen(sep_o_null) : 0;
149 for (l = 0, xcpa = cpa; *xcpa != NULL; ++xcpa)
150 l += strlen(*xcpa) + sonl;
152 self->l = l;
153 self->s = (n_autorec_alloc)(NULL, l +1 n_MEMORY_DEBUG_ARGSCALL);
155 for (l = 0, xcpa = cpa; *xcpa != NULL; ++xcpa) {
156 size_t i = strlen(*xcpa);
157 memcpy(self->s + l, *xcpa, i);
158 l += i;
159 if (sonl > 0) {
160 memcpy(self->s + l, sep_o_null, sonl);
161 l += sonl;
164 self->s[l] = '\0';
165 NYD_LEAVE;
166 return self;
170 * Routines that are not related to auto-reclaimed storage follow.
173 FL bool_t
174 n_anyof_buf(char const *template, char const *dat, size_t len){
175 char c;
176 NYD2_ENTER;
178 if(len == UIZ_MAX){
179 while((c = *template++) != '\0')
180 if(strchr(dat, c) != NULL)
181 break;
182 }else if(len > 0){
183 while((c = *template++) != '\0')
184 if(memchr(dat, c, len) != NULL)
185 break;
186 }else
187 c = '\0';
188 NYD2_LEAVE;
189 return (c != '\0');
192 FL char *
193 n_strsep(char **iolist, char sep, bool_t ignore_empty)
195 char *base, *cp;
196 NYD2_ENTER;
198 for (base = *iolist; base != NULL; base = *iolist) {
199 while (*base != '\0' && blankspacechar(*base))
200 ++base;
201 cp = strchr(base, sep);
202 if (cp != NULL)
203 *iolist = cp + 1;
204 else {
205 *iolist = NULL;
206 cp = base + strlen(base);
208 while (cp > base && blankspacechar(cp[-1]))
209 --cp;
210 *cp = '\0';
211 if (*base != '\0' || !ignore_empty)
212 break;
214 NYD2_LEAVE;
215 return base;
218 FL void
219 i_strcpy(char *dest, char const *src, size_t size)
221 NYD2_ENTER;
222 if (size > 0) {
223 for (;; ++dest, ++src)
224 if ((*dest = lowerconv(*src)) == '\0') {
225 break;
226 } else if (--size == 0) {
227 *dest = '\0';
228 break;
231 NYD2_LEAVE;
234 FL int
235 is_prefix(char const *as1, char const *as2) /* TODO arg order */
237 char c;
238 NYD2_ENTER;
240 for (; (c = *as1) == *as2 && c != '\0'; ++as1, ++as2)
241 if (*as2 == '\0')
242 break;
243 NYD2_LEAVE;
244 return (c == '\0');
247 FL char *
248 string_quote(char const *v) /* TODO too simpleminded (getrawlist(), +++ ..) */
250 char const *cp;
251 size_t i;
252 char c, *rv;
253 NYD2_ENTER;
255 for (i = 0, cp = v; (c = *cp) != '\0'; ++i, ++cp)
256 if (c == '"' || c == '\\')
257 ++i;
258 rv = salloc(i +1);
260 for (i = 0, cp = v; (c = *cp) != '\0'; rv[i++] = c, ++cp)
261 if (c == '"' || c == '\\')
262 rv[i++] = '\\';
263 rv[i] = '\0';
264 NYD2_LEAVE;
265 return rv;
268 FL char *
269 laststring(char *linebuf, bool_t *needs_list, bool_t strip)
271 char *cp, *p, quoted;
272 NYD_ENTER;
274 /* Anything to do at all? */
275 if (*(cp = linebuf) == '\0')
276 goto jnull;
277 cp += strlen(linebuf) -1;
279 /* Strip away trailing blanks */
280 while (spacechar(*cp) && cp > linebuf)
281 --cp;
282 cp[1] = '\0';
283 if (cp == linebuf)
284 goto jleave;
286 /* Now search for the BOS of the "last string" */
287 quoted = *cp;
288 if (quoted == '\'' || quoted == '"') {
289 if (strip)
290 *cp = '\0';
291 } else
292 quoted = ' ';
294 while (cp > linebuf) {
295 --cp;
296 if (quoted != ' ') {
297 if (*cp != quoted)
298 continue;
299 } else if (!spacechar(*cp))
300 continue;
301 if (cp == linebuf || cp[-1] != '\\') {
302 /* When in whitespace mode, WS prefix doesn't belong */
303 if (quoted == ' ')
304 ++cp;
305 break;
307 /* Expand the escaped quote character */
308 for (p = --cp; (p[0] = p[1]) != '\0'; ++p)
311 if (strip && quoted != ' ' && *cp == quoted)
312 for (p = cp; (p[0] = p[1]) != '\0'; ++p)
315 /* The "last string" has been skipped over, but still, try to step backwards
316 * until we are at BOS or see whitespace, so as to make possible things like
317 * "? copy +'x y.mbox'" or even "? copy +x\ y.mbox" */
318 while (cp > linebuf) {
319 --cp;
320 if (spacechar(*cp)) {
321 p = cp;
322 *cp++ = '\0';
323 /* We can furtherly release our callees if we now decide whether the
324 * remaining non-"last string" line content contains non-WS */
325 while (--p >= linebuf)
326 if (!spacechar(*p))
327 goto jleave;
328 linebuf = cp;
329 break;
333 jleave:
334 if (cp != NULL && *cp == '\0')
335 goto jnull;
336 *needs_list = (cp != linebuf && *linebuf != '\0');
337 j_leave:
338 NYD_LEAVE;
339 return cp;
340 jnull:
341 *needs_list = FAL0;
342 cp = NULL;
343 goto j_leave;
346 FL void
347 makelow(char *cp) /* TODO isn't that crap? --> */
349 NYD_ENTER;
350 #ifdef HAVE_C90AMEND1
351 if (n_mb_cur_max > 1) {
352 char *tp = cp;
353 wchar_t wc;
354 int len;
356 while (*cp != '\0') {
357 len = mbtowc(&wc, cp, n_mb_cur_max);
358 if (len < 0)
359 *tp++ = *cp++;
360 else {
361 wc = towlower(wc);
362 if (wctomb(tp, wc) == len)
363 tp += len, cp += len;
364 else
365 *tp++ = *cp++; /* <-- at least here */
368 } else
369 #endif
372 *cp = tolower((uc_i)*cp);
373 while (*cp++ != '\0');
375 NYD_LEAVE;
378 FL bool_t
379 substr(char const *str, char const *sub)
381 char const *cp, *backup;
382 NYD_ENTER;
384 cp = sub;
385 backup = str;
386 while (*str != '\0' && *cp != '\0') {
387 #ifdef HAVE_C90AMEND1
388 if (n_mb_cur_max > 1) {
389 wchar_t c, c2;
390 int sz;
392 if ((sz = mbtowc(&c, cp, n_mb_cur_max)) == -1)
393 goto Jsinglebyte;
394 cp += sz;
395 if ((sz = mbtowc(&c2, str, n_mb_cur_max)) == -1)
396 goto Jsinglebyte;
397 str += sz;
398 c = towupper(c);
399 c2 = towupper(c2);
400 if (c != c2) {
401 if ((sz = mbtowc(&c, backup, n_mb_cur_max)) > 0) {
402 backup += sz;
403 str = backup;
404 } else
405 str = ++backup;
406 cp = sub;
408 } else
409 Jsinglebyte:
410 #endif
412 int c, c2;
414 c = *cp++ & 0377;
415 if (islower(c))
416 c = toupper(c);
417 c2 = *str++ & 0377;
418 if (islower(c2))
419 c2 = toupper(c2);
420 if (c != c2) {
421 str = ++backup;
422 cp = sub;
426 NYD_LEAVE;
427 return (*cp == '\0');
430 FL char *
431 sstpcpy(char *dst, char const *src)
433 NYD2_ENTER;
434 while ((*dst = *src++) != '\0')
435 ++dst;
436 NYD2_LEAVE;
437 return dst;
440 FL char *
441 (sstrdup)(char const *cp n_MEMORY_DEBUG_ARGS)
443 char *dp;
444 NYD2_ENTER;
446 dp = (cp == NULL) ? NULL : (sbufdup)(cp, strlen(cp) n_MEMORY_DEBUG_ARGSCALL);
447 NYD2_LEAVE;
448 return dp;
451 FL char *
452 (sbufdup)(char const *cp, size_t len n_MEMORY_DEBUG_ARGS)
454 char *dp = NULL;
455 NYD2_ENTER;
457 dp = (n_alloc)(len +1 n_MEMORY_DEBUG_ARGSCALL);
458 if (cp != NULL)
459 memcpy(dp, cp, len);
460 dp[len] = '\0';
461 NYD2_LEAVE;
462 return dp;
465 FL ssize_t
466 n_strscpy(char *dst, char const *src, size_t dstsize){
467 ssize_t rv;
468 NYD2_ENTER;
470 if(n_LIKELY(dstsize > 0)){
471 rv = 0;
473 if((dst[rv] = src[rv]) == '\0')
474 goto jleave;
475 ++rv;
476 }while(--dstsize > 0);
477 dst[--rv] = '\0';
479 #ifdef HAVE_DEVEL
480 else
481 assert(dstsize > 0);
482 #endif
483 rv = -1;
484 jleave:
485 NYD2_LEAVE;
486 return rv;
489 FL int
490 asccasecmp(char const *s1, char const *s2)
492 int cmp;
493 NYD2_ENTER;
495 for (;;) {
496 char c1 = *s1++, c2 = *s2++;
497 if ((cmp = lowerconv(c1) - lowerconv(c2)) != 0 || c1 == '\0')
498 break;
500 NYD2_LEAVE;
501 return cmp;
504 FL int
505 ascncasecmp(char const *s1, char const *s2, size_t sz)
507 int cmp = 0;
508 NYD2_ENTER;
510 while (sz-- > 0) {
511 char c1 = *s1++, c2 = *s2++;
512 cmp = (ui8_t)lowerconv(c1);
513 cmp -= (ui8_t)lowerconv(c2);
514 if (cmp != 0 || c1 == '\0')
515 break;
517 NYD2_LEAVE;
518 return cmp;
521 FL char const *
522 asccasestr(char const *s1, char const *s2)
524 char c2, c1;
525 NYD2_ENTER;
527 for (c2 = *s2++, c2 = lowerconv(c2);;) {
528 if ((c1 = *s1++) == '\0') {
529 s1 = NULL;
530 break;
532 if (lowerconv(c1) == c2 && is_asccaseprefix(s2, s1)) {
533 --s1;
534 break;
537 NYD2_LEAVE;
538 return s1;
541 FL bool_t
542 is_asccaseprefix(char const *as1, char const *as2) /* TODO arg order */
544 char c1, c2;
545 NYD2_ENTER;
547 for(;; ++as1, ++as2){
548 c1 = *as1;
549 c1 = lowerconv(c1);
550 c2 = *as2;
551 c2 = lowerconv(c2);
553 if(c1 != c2 || c1 == '\0')
554 break;
555 if(c2 == '\0')
556 break;
558 NYD2_LEAVE;
559 return (c1 == '\0');
562 FL bool_t
563 is_ascncaseprefix(char const *as1, char const *as2, size_t sz)
565 char c1, c2;
566 bool_t rv;
567 NYD2_ENTER;
569 for(rv = TRU1; sz-- > 0; ++as1, ++as2){
570 c1 = *as1;
571 c1 = lowerconv(c1);
572 c2 = *as2;
573 c2 = lowerconv(c2);
575 if(!(rv = (c1 == c2)) || c1 == '\0')
576 break;
577 if(c2 == '\0')
578 break;
580 NYD2_LEAVE;
581 return rv;
585 FL struct str *
586 (n_str_assign_buf)(struct str *self, char const *buf, uiz_t buflen
587 n_MEMORY_DEBUG_ARGS){
588 NYD_ENTER;
589 if(buflen == UIZ_MAX)
590 buflen = (buf == NULL) ? 0 : strlen(buf);
592 assert(buflen == 0 || buf != NULL);
594 if(n_LIKELY(buflen > 0)){
595 self->s = (n_realloc)(self->s, (self->l = buflen) +1
596 n_MEMORY_DEBUG_ARGSCALL);
597 memcpy(self->s, buf, buflen);
598 self->s[buflen] = '\0';
599 }else
600 self->l = 0;
601 NYD_LEAVE;
602 return self;
605 FL struct str *
606 (n_str_add_buf)(struct str *self, char const *buf, uiz_t buflen
607 n_MEMORY_DEBUG_ARGS){
608 NYD_ENTER;
609 if(buflen == UIZ_MAX)
610 buflen = (buf == NULL) ? 0 : strlen(buf);
612 assert(buflen == 0 || buf != NULL);
614 if(buflen > 0) {
615 size_t osl = self->l, nsl = osl + buflen;
617 self->s = (n_realloc)(self->s, (self->l = nsl) +1
618 n_MEMORY_DEBUG_ARGSCALL);
619 memcpy(self->s + osl, buf, buflen);
620 self->s[nsl] = '\0';
622 NYD_LEAVE;
623 return self;
627 * struct n_string TODO extend, optimize
630 FL struct n_string *
631 (n_string_clear)(struct n_string *self n_MEMORY_DEBUG_ARGS){
632 NYD_ENTER;
634 assert(self != NULL);
636 if(self->s_size != 0){
637 if(!self->s_auto){
638 (n_free)(self->s_dat n_MEMORY_DEBUG_ARGSCALL);
640 self->s_len = self->s_auto = self->s_size = 0;
641 self->s_dat = NULL;
643 NYD_LEAVE;
644 return self;
647 FL struct n_string *
648 (n_string_reserve)(struct n_string *self, size_t noof n_MEMORY_DEBUG_ARGS){
649 ui32_t i, l, s;
650 NYD_ENTER;
652 assert(self != NULL);
654 s = self->s_size;
655 l = self->s_len;
656 #if 0 /* FIXME memory alloc too large */
657 if(SI32_MAX - n_ALIGN(1) - l <= noof)
658 n_panic(_("Memory allocation too large"));
659 #endif
661 if((i = s - l) <= ++noof){
662 i += l + (ui32_t)noof;
663 i = n_ALIGN(i);
664 self->s_size = i -1;
666 if(!self->s_auto)
667 self->s_dat = (n_realloc)(self->s_dat, i n_MEMORY_DEBUG_ARGSCALL);
668 else{
669 char *ndat = (n_autorec_alloc)(NULL, i n_MEMORY_DEBUG_ARGSCALL);
671 if(l > 0)
672 memcpy(ndat, self->s_dat, l);
673 self->s_dat = ndat;
676 NYD_LEAVE;
677 return self;
680 FL struct n_string *
681 (n_string_resize)(struct n_string *self, size_t nlen n_MEMORY_DEBUG_ARGS){
682 NYD_ENTER;
684 assert(self != NULL);
685 #if 0 /* FIXME memory alloc too large */
686 if(SI32_MAX - n_ALIGN(1) - l <= noof)
687 n_panic(_("Memory allocation too large"));
688 #endif
690 if(self->s_len < nlen)
691 self = (n_string_reserve)(self, nlen n_MEMORY_DEBUG_ARGSCALL);
692 self->s_len = (ui32_t)nlen;
693 NYD_LEAVE;
694 return self;
697 FL struct n_string *
698 (n_string_push_buf)(struct n_string *self, char const *buf, size_t buflen
699 n_MEMORY_DEBUG_ARGS){
700 NYD_ENTER;
702 assert(self != NULL);
703 assert(buflen == 0 || buf != NULL);
705 if(buflen == UIZ_MAX)
706 buflen = (buf == NULL) ? 0 : strlen(buf);
708 if(buflen > 0){
709 ui32_t i;
711 self = (n_string_reserve)(self, buflen n_MEMORY_DEBUG_ARGSCALL);
712 memcpy(&self->s_dat[i = self->s_len], buf, buflen);
713 self->s_len = (i += (ui32_t)buflen);
715 NYD_LEAVE;
716 return self;
719 FL struct n_string *
720 (n_string_push_c)(struct n_string *self, char c n_MEMORY_DEBUG_ARGS){
721 NYD_ENTER;
723 assert(self != NULL);
725 if(self->s_len + 1 >= self->s_size)
726 self = (n_string_reserve)(self, 1 n_MEMORY_DEBUG_ARGSCALL);
727 self->s_dat[self->s_len++] = c;
728 NYD_LEAVE;
729 return self;
732 FL struct n_string *
733 (n_string_unshift_buf)(struct n_string *self, char const *buf, size_t buflen
734 n_MEMORY_DEBUG_ARGS){
735 NYD_ENTER;
737 assert(self != NULL);
738 assert(buflen == 0 || buf != NULL);
740 if(buflen == UIZ_MAX)
741 buflen = (buf == NULL) ? 0 : strlen(buf);
743 if(buflen > 0){
744 self = (n_string_reserve)(self, buflen n_MEMORY_DEBUG_ARGSCALL);
745 if(self->s_len > 0)
746 memmove(&self->s_dat[buflen], self->s_dat, self->s_len);
747 memcpy(self->s_dat, buf, buflen);
748 self->s_len += (ui32_t)buflen;
750 NYD_LEAVE;
751 return self;
754 FL struct n_string *
755 (n_string_unshift_c)(struct n_string *self, char c n_MEMORY_DEBUG_ARGS){
756 NYD_ENTER;
758 assert(self != NULL);
760 if(self->s_len + 1 >= self->s_size)
761 self = (n_string_reserve)(self, 1 n_MEMORY_DEBUG_ARGSCALL);
762 if(self->s_len > 0)
763 memmove(&self->s_dat[1], self->s_dat, self->s_len);
764 self->s_dat[0] = c;
765 ++self->s_len;
766 NYD_LEAVE;
767 return self;
770 FL struct n_string *
771 (n_string_insert_buf)(struct n_string *self, size_t idx,
772 char const *buf, size_t buflen n_MEMORY_DEBUG_ARGS){
773 NYD_ENTER;
775 assert(self != NULL);
776 assert(buflen == 0 || buf != NULL);
777 assert(idx <= self->s_len);
779 if(buflen == UIZ_MAX)
780 buflen = (buf == NULL) ? 0 : strlen(buf);
782 if(buflen > 0){
783 self = (n_string_reserve)(self, buflen n_MEMORY_DEBUG_ARGSCALL);
784 if(self->s_len > 0)
785 memmove(&self->s_dat[idx + buflen], &self->s_dat[idx],
786 self->s_len - idx);
787 memcpy(&self->s_dat[idx], buf, buflen);
788 self->s_len += (ui32_t)buflen;
790 NYD_LEAVE;
791 return self;
794 FL struct n_string *
795 (n_string_insert_c)(struct n_string *self, size_t idx,
796 char c n_MEMORY_DEBUG_ARGS){
797 NYD_ENTER;
799 assert(self != NULL);
800 assert(idx <= self->s_len);
802 if(self->s_len + 1 >= self->s_size)
803 self = (n_string_reserve)(self, 1 n_MEMORY_DEBUG_ARGSCALL);
804 if(self->s_len > 0)
805 memmove(&self->s_dat[idx + 1], &self->s_dat[idx], self->s_len - idx);
806 self->s_dat[idx] = c;
807 ++self->s_len;
808 NYD_LEAVE;
809 return self;
812 FL struct n_string *
813 n_string_cut(struct n_string *self, size_t idx, size_t len){
814 NYD_ENTER;
816 assert(self != NULL);
817 assert(UIZ_MAX - idx > len);
818 assert(SI32_MAX >= idx + len);
819 assert(idx + len <= self->s_len);
821 if(len > 0)
822 memmove(&self->s_dat[idx], &self->s_dat[idx + len],
823 (self->s_len -= len) - idx);
824 NYD_LEAVE;
825 return self;
828 FL char *
829 (n_string_cp)(struct n_string *self n_MEMORY_DEBUG_ARGS){
830 char *rv;
831 NYD2_ENTER;
833 assert(self != NULL);
835 if(self->s_size == 0)
836 self = (n_string_reserve)(self, 1 n_MEMORY_DEBUG_ARGSCALL);
838 (rv = self->s_dat)[self->s_len] = '\0';
839 NYD2_LEAVE;
840 return rv;
843 FL char const *
844 n_string_cp_const(struct n_string const *self){
845 char const *rv;
846 NYD2_ENTER;
848 assert(self != NULL);
850 if(self->s_size != 0){
851 ((struct n_string*)n_UNCONST(self))->s_dat[self->s_len] = '\0';
852 rv = self->s_dat;
853 }else
854 rv = n_empty;
855 NYD2_LEAVE;
856 return rv;
860 * UTF-8
863 FL ui32_t
864 n_utf8_to_utf32(char const **bdat, size_t *blen) /* TODO check false UTF8 */
866 char const *cp;
867 size_t l;
868 ui32_t c, x;
869 NYD2_ENTER;
871 cp = *bdat;
872 l = *blen - 1;
873 x = (ui8_t)*cp++;
875 if (x <= 0x7Fu)
876 c = x;
877 else {
878 if ((x & 0xE0u) == 0xC0u) {
879 if (l < 1)
880 goto jerr;
881 l -= 1;
882 c = x & ~0xC0u;
883 } else if ((x & 0xF0u) == 0xE0u) {
884 if (l < 2)
885 goto jerr;
886 l -= 2;
887 c = x & ~0xE0u;
888 c <<= 6;
889 x = (ui8_t)*cp++;
890 c |= x & 0x7Fu;
891 } else {
892 if (l < 3)
893 goto jerr;
894 l -= 3;
895 c = x & ~0xF0u;
896 c <<= 6;
897 x = (ui8_t)*cp++;
898 c |= x & 0x7Fu;
899 c <<= 6;
900 x = (ui8_t)*cp++;
901 c |= x & 0x7Fu;
903 c <<= 6;
904 x = (ui8_t)*cp++;
905 c |= x & 0x7Fu;
908 jleave:
909 *bdat = cp;
910 *blen = l;
911 NYD2_LEAVE;
912 return c;
913 jerr:
914 c = UI32_MAX;
915 goto jleave;
918 FL size_t
919 n_utf32_to_utf8(ui32_t c, char *buf)
921 struct {
922 ui32_t lower_bound;
923 ui32_t upper_bound;
924 ui8_t enc_leader;
925 ui8_t enc_lval;
926 ui8_t dec_leader_mask;
927 ui8_t dec_leader_val_mask;
928 ui8_t dec_bytes_togo;
929 ui8_t cat_index;
930 ui8_t __dummy[2];
931 } const _cat[] = {
932 {0x00000000, 0x00000000, 0x00, 0, 0x00, 0x00, 0, 0, {0,}},
933 {0x00000000, 0x0000007F, 0x00, 1, 0x80, 0x7F, 1-1, 1, {0,}},
934 {0x00000080, 0x000007FF, 0xC0, 2, 0xE0, 0xFF-0xE0, 2-1, 2, {0,}},
935 /* We assume surrogates are U+D800 - U+DFFF, _cat index 3 */
936 /* xxx _from_utf32() simply assumes magic code points for surrogates!
937 * xxx (However, should we ever get yet another surrogate range we
938 * xxx need to deal with that all over the place anyway? */
939 {0x00000800, 0x0000FFFF, 0xE0, 3, 0xF0, 0xFF-0xF0, 3-1, 3, {0,}},
940 {0x00010000, 0x0010FFFF, 0xF0, 4, 0xF8, 0xFF-0xF8, 4-1, 4, {0,}},
941 }, *catp = _cat;
942 size_t l;
944 if (c <= _cat[0].upper_bound) { catp += 0; goto j0; }
945 if (c <= _cat[1].upper_bound) { catp += 1; goto j1; }
946 if (c <= _cat[2].upper_bound) { catp += 2; goto j2; }
947 if (c <= _cat[3].upper_bound) {
948 /* Surrogates may not be converted (Compatibility rule C10) */
949 if (c >= 0xD800u && c <= 0xDFFFu)
950 goto jerr;
951 catp += 3;
952 goto j3;
954 if (c <= _cat[4].upper_bound) { catp += 4; goto j4; }
955 jerr:
956 c = 0xFFFDu; /* Unicode replacement character */
957 catp += 3;
958 goto j3;
960 buf[3] = (char)0x80u | (char)(c & 0x3Fu); c >>= 6;
962 buf[2] = (char)0x80u | (char)(c & 0x3Fu); c >>= 6;
964 buf[1] = (char)0x80u | (char)(c & 0x3Fu); c >>= 6;
966 buf[0] = (char)catp->enc_leader | (char)(c);
968 buf[catp->enc_lval] = '\0';
969 l = catp->enc_lval;
970 NYD2_LEAVE;
971 return l;
975 * Our iconv(3) wrapper
977 #ifdef HAVE_ICONV
979 static void _ic_toupper(char *dest, char const *src);
980 static void _ic_stripdash(char *p);
982 static void
983 _ic_toupper(char *dest, char const *src)
985 NYD2_ENTER;
987 *dest++ = upperconv(*src);
988 while (*src++ != '\0');
989 NYD2_LEAVE;
992 static void
993 _ic_stripdash(char *p)
995 char *q = p;
996 NYD2_ENTER;
999 if (*(q = p) != '-')
1000 ++q;
1001 while (*p++ != '\0');
1002 NYD2_LEAVE;
1005 FL iconv_t
1006 n_iconv_open(char const *tocode, char const *fromcode)
1008 iconv_t id;
1009 char *t, *f;
1010 NYD_ENTER;
1012 if ((!asccasecmp(fromcode, "unknown-8bit") ||
1013 !asccasecmp(fromcode, "binary")) &&
1014 (fromcode = ok_vlook(charset_unknown_8bit)) == NULL)
1015 fromcode = ok_vlook(CHARSET_8BIT_OKEY);
1017 if ((id = iconv_open(tocode, fromcode)) != (iconv_t)-1)
1018 goto jleave;
1020 /* Remove the "iso-" prefixes for Solaris */
1021 if (!ascncasecmp(tocode, "iso-", 4))
1022 tocode += 4;
1023 else if (!ascncasecmp(tocode, "iso", 3))
1024 tocode += 3;
1025 if (!ascncasecmp(fromcode, "iso-", 4))
1026 fromcode += 4;
1027 else if (!ascncasecmp(fromcode, "iso", 3))
1028 fromcode += 3;
1029 if (*tocode == '\0' || *fromcode == '\0') {
1030 id = (iconv_t)-1;
1031 goto jleave;
1033 if ((id = iconv_open(tocode, fromcode)) != (iconv_t)-1)
1034 goto jleave;
1036 /* Solaris prefers upper-case charset names. Don't ask... */
1037 t = salloc(strlen(tocode) +1);
1038 _ic_toupper(t, tocode);
1039 f = salloc(strlen(fromcode) +1);
1040 _ic_toupper(f, fromcode);
1041 if ((id = iconv_open(t, f)) != (iconv_t)-1)
1042 goto jleave;
1044 /* Strip dashes for UnixWare */
1045 _ic_stripdash(t);
1046 _ic_stripdash(f);
1047 if ((id = iconv_open(t, f)) != (iconv_t)-1)
1048 goto jleave;
1050 /* Add your vendor's sillynesses here */
1052 /* If the encoding names are equal at this point, they are just not
1053 * understood by iconv(), and we cannot sensibly use it in any way. We do
1054 * not perform this as an optimization above since iconv() can otherwise be
1055 * used to check the validity of the input even with identical encoding
1056 * names */
1057 if (!strcmp(t, f))
1058 errno = 0;
1059 jleave:
1060 NYD_LEAVE;
1061 return id;
1064 FL void
1065 n_iconv_close(iconv_t cd)
1067 NYD_ENTER;
1068 iconv_close(cd);
1069 if (cd == iconvd)
1070 iconvd = (iconv_t)-1;
1071 NYD_LEAVE;
1074 FL void
1075 n_iconv_reset(iconv_t cd)
1077 NYD_ENTER;
1078 iconv(cd, NULL, NULL, NULL, NULL);
1079 NYD_LEAVE;
1082 /* (2012-09-24: export and use it exclusively to isolate prototype problems
1083 * (*inb* is 'char const **' except in POSIX) in a single place.
1084 * GNU libiconv even allows for configuration time const/non-const..
1085 * In the end it's an ugly guess, but we can't do better since make(1) doesn't
1086 * support compiler invocations which bail on error, so no -Werror */
1087 /* Citrus project? */
1088 # if defined _ICONV_H_ && defined __ICONV_F_HIDE_INVALID
1089 /* DragonFly 3.2.1 is special TODO newer DragonFly too, but different */
1090 # if n_OS_DRAGONFLY
1091 # define __INBCAST(S) (char ** __restrict__)n_UNCONST(S)
1092 # else
1093 # define __INBCAST(S) (char const **)n_UNCONST(S)
1094 # endif
1095 # elif n_OS_SUNOS || n_OS_SOLARIS
1096 # define __INBCAST(S) (char const ** __restrict__)n_UNCONST(S)
1097 # endif
1098 # ifndef __INBCAST
1099 # define __INBCAST(S) (char **)n_UNCONST(S)
1100 # endif
1102 FL int
1103 n_iconv_buf(iconv_t cd, enum n_iconv_flags icf,
1104 char const **inb, size_t *inbleft, char **outb, size_t *outbleft){
1105 int err;
1106 NYD2_ENTER;
1108 if((icf & n_ICONV_UNIREPL) && !(n_psonce & n_PSO_UNICODE))
1109 icf &= ~n_ICONV_UNIREPL;
1111 for(;;){
1112 size_t sz;
1114 sz = iconv(cd, __INBCAST(inb), inbleft, outb, outbleft);
1115 if(sz > 0 && !(icf & n_ICONV_IGN_NOREVERSE)){
1116 err = ENOENT;
1117 goto jleave;
1119 if(sz != (size_t)-1)
1120 break;
1122 err = errno;
1123 if(!(icf & n_ICONV_IGN_ILSEQ) || err != EILSEQ)
1124 goto jleave;
1125 if(*inbleft > 0){
1126 ++(*inb);
1127 --(*inbleft);
1128 if(icf & n_ICONV_UNIREPL){
1129 if(*outbleft >= sizeof(n_unirepl) -1){
1130 memcpy(*outb, n_unirepl, sizeof(n_unirepl) -1);
1131 *outb += sizeof(n_unirepl) -1;
1132 *outbleft -= sizeof(n_unirepl) -1;
1133 continue;
1135 }else if(*outbleft > 0){
1136 *(*outb)++ = '?';
1137 --*outbleft;
1138 continue;
1140 err = E2BIG;
1141 goto jleave;
1142 }else if(*outbleft > 0){
1143 **outb = '\0';
1144 goto jleave;
1147 err = 0;
1148 jleave:
1149 NYD2_LEAVE;
1150 return err;
1152 # undef __INBCAST
1154 FL int
1155 n_iconv_str(iconv_t cd, enum n_iconv_flags icf,
1156 struct str *out, struct str const *in, struct str *in_rest_or_null)
1158 int err;
1159 char *obb, *ob;
1160 char const *ib;
1161 size_t olb, ol, il;
1162 NYD2_ENTER;
1164 err = 0;
1165 obb = out->s;
1166 olb = out->l;
1167 ol = in->l;
1169 ol = (ol << 1) - (ol >> 4);
1170 if (olb <= ol) {
1171 olb = ol;
1172 goto jrealloc;
1175 for (;;) {
1176 ib = in->s;
1177 il = in->l;
1178 ob = obb;
1179 ol = olb;
1180 if((err = n_iconv_buf(cd, icf, &ib, &il, &ob, &ol)) == 0 || err != E2BIG)
1181 break;
1182 err = 0;
1183 olb += in->l;
1184 jrealloc:
1185 obb = n_realloc(obb, olb +1);
1188 if (in_rest_or_null != NULL) {
1189 in_rest_or_null->s = n_UNCONST(ib);
1190 in_rest_or_null->l = il;
1192 out->s = obb;
1193 out->s[out->l = olb - ol] = '\0';
1194 NYD2_LEAVE;
1195 return err;
1198 FL char *
1199 n_iconv_onetime_cp(enum n_iconv_flags icf,
1200 char const *tocode, char const *fromcode, char const *input){
1201 struct str out, in;
1202 iconv_t icd;
1203 char *rv;
1204 NYD2_ENTER;
1206 rv = NULL;
1207 if(tocode == NULL)
1208 tocode = ok_vlook(ttycharset);
1209 if(fromcode == NULL)
1210 fromcode = "utf-8";
1212 if((icd = iconv_open(tocode, fromcode)) == (iconv_t)-1)
1213 goto jleave;
1215 in.l = strlen(in.s = n_UNCONST(input)); /* logical */
1216 out.s = NULL, out.l = 0;
1217 if(!n_iconv_str(icd, icf, &out, &in, NULL))
1218 rv = savestrbuf(out.s, out.l);
1219 if(out.s != NULL)
1220 free(out.s);
1222 iconv_close(icd);
1223 jleave:
1224 NYD2_LEAVE;
1225 return rv;
1227 #endif /* HAVE_ICONV */
1229 /* s-it-mode */