Fix typo introduced in IMAP reinstantiation
[s-mailx.git] / strings.c
blob3ed372dc793e64f6f726facdde47ac5f8d42dc55
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_from_pool)(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_from_pool)(NULL, sbuf_len +1
65 n_MEMORY_DEBUG_ARGSCALL);
66 memcpy(news, sbuf, sbuf_len);
67 news[sbuf_len] = 0;
68 NYD_LEAVE;
69 return news;
72 FL char *
73 (savecatsep)(char const *s1, char sep, char const *s2 n_MEMORY_DEBUG_ARGS)
75 size_t l1, l2;
76 char *news;
77 NYD_ENTER;
79 l1 = (s1 != NULL) ? strlen(s1) : 0;
80 l2 = strlen(s2);
81 news = (n_autorec_alloc_from_pool)(NULL, l1 + (sep != '\0') + l2 +1
82 n_MEMORY_DEBUG_ARGSCALL);
83 if (l1 > 0) {
84 memcpy(news + 0, s1, l1);
85 if (sep != '\0')
86 news[l1++] = sep;
88 memcpy(news + l1, s2, l2);
89 news[l1 + l2] = '\0';
90 NYD_LEAVE;
91 return news;
95 * Support routines, auto-reclaimed storage
98 FL char *
99 (i_strdup)(char const *src n_MEMORY_DEBUG_ARGS)
101 size_t sz;
102 char *dest;
103 NYD_ENTER;
105 sz = strlen(src) +1;
106 dest = (n_autorec_alloc_from_pool)(NULL, sz n_MEMORY_DEBUG_ARGSCALL);
107 i_strcpy(dest, src, sz);
108 NYD_LEAVE;
109 return dest;
112 FL struct str *
113 str_concat_csvl(struct str *self, ...) /* XXX onepass maybe better here */
115 va_list vl;
116 size_t l;
117 char const *cs;
118 NYD_ENTER;
120 va_start(vl, self);
121 for (l = 0; (cs = va_arg(vl, char const*)) != NULL;)
122 l += strlen(cs);
123 va_end(vl);
125 self->l = l;
126 self->s = salloc(l +1);
128 va_start(vl, self);
129 for (l = 0; (cs = va_arg(vl, char const*)) != NULL;) {
130 size_t i = strlen(cs);
131 memcpy(self->s + l, cs, i);
132 l += i;
134 self->s[l] = '\0';
135 va_end(vl);
136 NYD_LEAVE;
137 return self;
140 FL struct str *
141 (str_concat_cpa)(struct str *self, char const * const *cpa,
142 char const *sep_o_null n_MEMORY_DEBUG_ARGS)
144 size_t sonl, l;
145 char const * const *xcpa;
146 NYD_ENTER;
148 sonl = (sep_o_null != NULL) ? strlen(sep_o_null) : 0;
150 for (l = 0, xcpa = cpa; *xcpa != NULL; ++xcpa)
151 l += strlen(*xcpa) + sonl;
153 self->l = l;
154 self->s = (n_autorec_alloc_from_pool)(NULL, l +1 n_MEMORY_DEBUG_ARGSCALL);
156 for (l = 0, xcpa = cpa; *xcpa != NULL; ++xcpa) {
157 size_t i = strlen(*xcpa);
158 memcpy(self->s + l, *xcpa, i);
159 l += i;
160 if (sonl > 0) {
161 memcpy(self->s + l, sep_o_null, sonl);
162 l += sonl;
165 self->s[l] = '\0';
166 NYD_LEAVE;
167 return self;
171 * Routines that are not related to auto-reclaimed storage follow.
174 FL bool_t
175 n_anyof_buf(char const *template, char const *dat, size_t len){
176 char c;
177 NYD2_ENTER;
179 if(len == UIZ_MAX){
180 while((c = *template++) != '\0')
181 if(strchr(dat, c) != NULL)
182 break;
183 }else if(len > 0){
184 while((c = *template++) != '\0')
185 if(memchr(dat, c, len) != NULL)
186 break;
187 }else
188 c = '\0';
189 NYD2_LEAVE;
190 return (c != '\0');
193 FL char *
194 n_strsep(char **iolist, char sep, bool_t ignore_empty)
196 char *base, *cp;
197 NYD2_ENTER;
199 for (base = *iolist; base != NULL; base = *iolist) {
200 while (*base != '\0' && blankspacechar(*base))
201 ++base;
202 cp = strchr(base, sep);
203 if (cp != NULL)
204 *iolist = cp + 1;
205 else {
206 *iolist = NULL;
207 cp = base + strlen(base);
209 while (cp > base && blankspacechar(cp[-1]))
210 --cp;
211 *cp = '\0';
212 if (*base != '\0' || !ignore_empty)
213 break;
215 NYD2_LEAVE;
216 return base;
219 FL void
220 i_strcpy(char *dest, char const *src, size_t size)
222 NYD2_ENTER;
223 if (size > 0) {
224 for (;; ++dest, ++src)
225 if ((*dest = lowerconv(*src)) == '\0') {
226 break;
227 } else if (--size == 0) {
228 *dest = '\0';
229 break;
232 NYD2_LEAVE;
235 FL bool_t
236 is_prefix(char const *as1, char const *as2) /* TODO arg order */
238 char c;
239 NYD2_ENTER;
241 for (; (c = *as1) == *as2 && c != '\0'; ++as1, ++as2)
242 if (*as2 == '\0')
243 break;
244 NYD2_LEAVE;
245 return (c == '\0');
248 FL char *
249 string_quote(char const *v) /* TODO too simpleminded (getrawlist(), +++ ..) */
251 char const *cp;
252 size_t i;
253 char c, *rv;
254 NYD2_ENTER;
256 for (i = 0, cp = v; (c = *cp) != '\0'; ++i, ++cp)
257 if (c == '"' || c == '\\')
258 ++i;
259 rv = salloc(i +1);
261 for (i = 0, cp = v; (c = *cp) != '\0'; rv[i++] = c, ++cp)
262 if (c == '"' || c == '\\')
263 rv[i++] = '\\';
264 rv[i] = '\0';
265 NYD2_LEAVE;
266 return rv;
269 FL char *
270 laststring(char *linebuf, bool_t *needs_list, bool_t strip)
272 char *cp, *p, quoted;
273 NYD_ENTER;
275 /* Anything to do at all? */
276 if (*(cp = linebuf) == '\0')
277 goto jnull;
278 cp += strlen(linebuf) -1;
280 /* Strip away trailing blanks */
281 while (spacechar(*cp) && cp > linebuf)
282 --cp;
283 cp[1] = '\0';
284 if (cp == linebuf)
285 goto jleave;
287 /* Now search for the BOS of the "last string" */
288 quoted = *cp;
289 if (quoted == '\'' || quoted == '"') {
290 if (strip)
291 *cp = '\0';
292 } else
293 quoted = ' ';
295 while (cp > linebuf) {
296 --cp;
297 if (quoted != ' ') {
298 if (*cp != quoted)
299 continue;
300 } else if (!spacechar(*cp))
301 continue;
302 if (cp == linebuf || cp[-1] != '\\') {
303 /* When in whitespace mode, WS prefix doesn't belong */
304 if (quoted == ' ')
305 ++cp;
306 break;
308 /* Expand the escaped quote character */
309 for (p = --cp; (p[0] = p[1]) != '\0'; ++p)
312 if (strip && quoted != ' ' && *cp == quoted)
313 for (p = cp; (p[0] = p[1]) != '\0'; ++p)
316 /* The "last string" has been skipped over, but still, try to step backwards
317 * until we are at BOS or see whitespace, so as to make possible things like
318 * "? copy +'x y.mbox'" or even "? copy +x\ y.mbox" */
319 while (cp > linebuf) {
320 --cp;
321 if (spacechar(*cp)) {
322 p = cp;
323 *cp++ = '\0';
324 /* We can furtherly release our callees if we now decide whether the
325 * remaining non-"last string" line content contains non-WS */
326 while (--p >= linebuf)
327 if (!spacechar(*p))
328 goto jleave;
329 linebuf = cp;
330 break;
334 jleave:
335 if (cp != NULL && *cp == '\0')
336 goto jnull;
337 *needs_list = (cp != linebuf && *linebuf != '\0');
338 j_leave:
339 NYD_LEAVE;
340 return cp;
341 jnull:
342 *needs_list = FAL0;
343 cp = NULL;
344 goto j_leave;
347 FL void
348 makelow(char *cp) /* TODO isn't that crap? --> */
350 NYD_ENTER;
351 #ifdef HAVE_C90AMEND1
352 if (n_mb_cur_max > 1) {
353 char *tp = cp;
354 wchar_t wc;
355 int len;
357 while (*cp != '\0') {
358 len = mbtowc(&wc, cp, n_mb_cur_max);
359 if (len < 0)
360 *tp++ = *cp++;
361 else {
362 wc = towlower(wc);
363 if (wctomb(tp, wc) == len)
364 tp += len, cp += len;
365 else
366 *tp++ = *cp++; /* <-- at least here */
369 } else
370 #endif
373 *cp = tolower((uc_i)*cp);
374 while (*cp++ != '\0');
376 NYD_LEAVE;
379 FL bool_t
380 substr(char const *str, char const *sub)
382 char const *cp, *backup;
383 NYD_ENTER;
385 cp = sub;
386 backup = str;
387 while (*str != '\0' && *cp != '\0') {
388 #ifdef HAVE_C90AMEND1
389 if (n_mb_cur_max > 1) {
390 wchar_t c, c2;
391 int sz;
393 if ((sz = mbtowc(&c, cp, n_mb_cur_max)) == -1)
394 goto Jsinglebyte;
395 cp += sz;
396 if ((sz = mbtowc(&c2, str, n_mb_cur_max)) == -1)
397 goto Jsinglebyte;
398 str += sz;
399 c = towupper(c);
400 c2 = towupper(c2);
401 if (c != c2) {
402 if ((sz = mbtowc(&c, backup, n_mb_cur_max)) > 0) {
403 backup += sz;
404 str = backup;
405 } else
406 str = ++backup;
407 cp = sub;
409 } else
410 Jsinglebyte:
411 #endif
413 int c, c2;
415 c = *cp++ & 0377;
416 if (islower(c))
417 c = toupper(c);
418 c2 = *str++ & 0377;
419 if (islower(c2))
420 c2 = toupper(c2);
421 if (c != c2) {
422 str = ++backup;
423 cp = sub;
427 NYD_LEAVE;
428 return (*cp == '\0');
431 FL char *
432 sstpcpy(char *dst, char const *src)
434 NYD2_ENTER;
435 while ((*dst = *src++) != '\0')
436 ++dst;
437 NYD2_LEAVE;
438 return dst;
441 FL char *
442 (sstrdup)(char const *cp n_MEMORY_DEBUG_ARGS)
444 char *dp;
445 NYD2_ENTER;
447 dp = (cp == NULL) ? NULL : (sbufdup)(cp, strlen(cp) n_MEMORY_DEBUG_ARGSCALL);
448 NYD2_LEAVE;
449 return dp;
452 FL char *
453 (sbufdup)(char const *cp, size_t len n_MEMORY_DEBUG_ARGS)
455 char *dp = NULL;
456 NYD2_ENTER;
458 dp = (n_alloc)(len +1 n_MEMORY_DEBUG_ARGSCALL);
459 if (cp != NULL)
460 memcpy(dp, cp, len);
461 dp[len] = '\0';
462 NYD2_LEAVE;
463 return dp;
466 FL ssize_t
467 n_strscpy(char *dst, char const *src, size_t dstsize){
468 ssize_t rv;
469 NYD2_ENTER;
471 if(n_LIKELY(dstsize > 0)){
472 rv = 0;
474 if((dst[rv] = src[rv]) == '\0')
475 goto jleave;
476 ++rv;
477 }while(--dstsize > 0);
478 dst[--rv] = '\0';
480 #ifdef HAVE_DEVEL
481 else
482 assert(dstsize > 0);
483 #endif
484 rv = -1;
485 jleave:
486 NYD2_LEAVE;
487 return rv;
490 FL int
491 asccasecmp(char const *s1, char const *s2)
493 int cmp;
494 NYD2_ENTER;
496 for (;;) {
497 char c1 = *s1++, c2 = *s2++;
498 if ((cmp = lowerconv(c1) - lowerconv(c2)) != 0 || c1 == '\0')
499 break;
501 NYD2_LEAVE;
502 return cmp;
505 FL int
506 ascncasecmp(char const *s1, char const *s2, size_t sz)
508 int cmp = 0;
509 NYD2_ENTER;
511 while (sz-- > 0) {
512 char c1 = *s1++, c2 = *s2++;
513 cmp = (ui8_t)lowerconv(c1);
514 cmp -= (ui8_t)lowerconv(c2);
515 if (cmp != 0 || c1 == '\0')
516 break;
518 NYD2_LEAVE;
519 return cmp;
522 FL char const *
523 asccasestr(char const *s1, char const *s2)
525 char c2, c1;
526 NYD2_ENTER;
528 for (c2 = *s2++, c2 = lowerconv(c2);;) {
529 if ((c1 = *s1++) == '\0') {
530 s1 = NULL;
531 break;
533 if (lowerconv(c1) == c2 && is_asccaseprefix(s2, s1)) {
534 --s1;
535 break;
538 NYD2_LEAVE;
539 return s1;
542 FL bool_t
543 is_asccaseprefix(char const *as1, char const *as2) /* TODO arg order */
545 char c1, c2;
546 NYD2_ENTER;
548 for(;; ++as1, ++as2){
549 c1 = *as1;
550 c1 = lowerconv(c1);
551 c2 = *as2;
552 c2 = lowerconv(c2);
554 if(c1 != c2 || c1 == '\0')
555 break;
556 if(c2 == '\0')
557 break;
559 NYD2_LEAVE;
560 return (c1 == '\0');
563 FL bool_t
564 is_ascncaseprefix(char const *as1, char const *as2, size_t sz)
566 char c1, c2;
567 bool_t rv;
568 NYD2_ENTER;
570 for(rv = TRU1; sz-- > 0; ++as1, ++as2){
571 c1 = *as1;
572 c1 = lowerconv(c1);
573 c2 = *as2;
574 c2 = lowerconv(c2);
576 if(!(rv = (c1 == c2)) || c1 == '\0')
577 break;
578 if(c2 == '\0')
579 break;
581 NYD2_LEAVE;
582 return rv;
586 FL struct str *
587 (n_str_assign_buf)(struct str *self, char const *buf, uiz_t buflen
588 n_MEMORY_DEBUG_ARGS){
589 NYD_ENTER;
590 if(buflen == UIZ_MAX)
591 buflen = (buf == NULL) ? 0 : strlen(buf);
593 assert(buflen == 0 || buf != NULL);
595 if(n_LIKELY(buflen > 0)){
596 self->s = (n_realloc)(self->s, (self->l = buflen) +1
597 n_MEMORY_DEBUG_ARGSCALL);
598 memcpy(self->s, buf, buflen);
599 self->s[buflen] = '\0';
600 }else
601 self->l = 0;
602 NYD_LEAVE;
603 return self;
606 FL struct str *
607 (n_str_add_buf)(struct str *self, char const *buf, uiz_t buflen
608 n_MEMORY_DEBUG_ARGS){
609 NYD_ENTER;
610 if(buflen == UIZ_MAX)
611 buflen = (buf == NULL) ? 0 : strlen(buf);
613 assert(buflen == 0 || buf != NULL);
615 if(buflen > 0) {
616 size_t osl = self->l, nsl = osl + buflen;
618 self->s = (n_realloc)(self->s, (self->l = nsl) +1
619 n_MEMORY_DEBUG_ARGSCALL);
620 memcpy(self->s + osl, buf, buflen);
621 self->s[nsl] = '\0';
623 NYD_LEAVE;
624 return self;
627 FL struct str *
628 n_str_trim(struct str *self){
629 size_t l;
630 char const *cp;
631 NYD2_ENTER;
633 cp = self->s;
635 if((l = self->l) > 0){
636 while(spacechar(*cp)){
637 ++cp;
638 if(--l == 0)
639 break;
641 self->s = n_UNCONST(cp);
644 if(l > 0){
645 for(cp += l -1; spacechar(*cp); --cp)
646 if(--l == 0)
647 break;
649 self->l = l;
651 NYD2_LEAVE;
652 return self;
655 FL struct str *
656 n_str_trim_ifs(struct str *self, bool_t dodefaults){
657 char s, t, n, c;
658 char const *ifs, *cp;
659 size_t l, i;
660 NYD2_ENTER;
662 if((l = self->l) == 0)
663 goto jleave;
665 ifs = ok_vlook(ifs_ws);
666 cp = self->s;
667 s = t = n = '\0';
669 /* Check whether we can go fast(er) path */
670 for(i = 0; (c = ifs[i]) != '\0'; ++i){
671 switch(c){
672 case ' ': s = c; break;
673 case '\t': t = c; break;
674 case '\n': n = c; break;
675 default:
676 /* Need to go the slow path */
677 while(strchr(ifs, *cp) != NULL){
678 ++cp;
679 if(--l == 0)
680 break;
682 self->s = n_UNCONST(cp);
684 if(l > 0){
685 for(cp += l -1; strchr(ifs, *cp) != NULL;){
686 if(--l == 0)
687 break;
688 /* An uneven number of reverse solidus escapes last WS! */
689 else if(*--cp == '\\'){
690 siz_t j;
692 for(j = 1; l - (uiz_t)j > 0 && cp[-j] == '\\'; ++j)
694 if(j & 1){
695 ++l;
696 break;
701 self->l = l;
703 if(!dodefaults)
704 goto jleave;
705 cp = self->s;
706 ++i;
707 break;
711 /* No ifs-ws? No more data? No trimming */
712 if(l == 0 || (i == 0 && !dodefaults))
713 goto jleave;
715 if(dodefaults){
716 s = ' ';
717 t = '\t';
718 n = '\n';
721 if(l > 0){
722 while((c = *cp) != '\0' && (c == s || c == t || c == n)){
723 ++cp;
724 if(--l == 0)
725 break;
727 self->s = n_UNCONST(cp);
730 if(l > 0){
731 for(cp += l -1; (c = *cp) != '\0' && (c == s || c == t || c == n);){
732 if(--l == 0)
733 break;
734 /* An uneven number of reverse solidus escapes last WS! */
735 else if(*--cp == '\\'){
736 siz_t j;
738 for(j = 1; l - (uiz_t)j > 0 && cp[-j] == '\\'; ++j)
740 if(j & 1){
741 ++l;
742 break;
747 self->l = l;
748 jleave:
749 NYD2_LEAVE;
750 return self;
754 * struct n_string TODO extend, optimize
757 FL struct n_string *
758 (n_string_clear)(struct n_string *self n_MEMORY_DEBUG_ARGS){
759 NYD_ENTER;
761 assert(self != NULL);
763 if(self->s_size != 0){
764 if(!self->s_auto){
765 (n_free)(self->s_dat n_MEMORY_DEBUG_ARGSCALL);
767 self->s_len = self->s_auto = self->s_size = 0;
768 self->s_dat = NULL;
770 NYD_LEAVE;
771 return self;
774 FL struct n_string *
775 (n_string_reserve)(struct n_string *self, size_t noof n_MEMORY_DEBUG_ARGS){
776 ui32_t i, l, s;
777 NYD_ENTER;
779 assert(self != NULL);
781 s = self->s_size;
782 l = self->s_len;
783 #if 0 /* FIXME memory alloc too large */
784 if(SI32_MAX - n_ALIGN(1) - l <= noof)
785 n_panic(_("Memory allocation too large"));
786 #endif
788 if((i = s - l) <= ++noof){
789 i += l + (ui32_t)noof;
790 i = n_ALIGN(i);
791 self->s_size = i -1;
793 if(!self->s_auto)
794 self->s_dat = (n_realloc)(self->s_dat, i n_MEMORY_DEBUG_ARGSCALL);
795 else{
796 char *ndat = (n_autorec_alloc_from_pool)(NULL, i
797 n_MEMORY_DEBUG_ARGSCALL);
799 if(l > 0)
800 memcpy(ndat, self->s_dat, l);
801 self->s_dat = ndat;
804 NYD_LEAVE;
805 return self;
808 FL struct n_string *
809 (n_string_resize)(struct n_string *self, size_t nlen n_MEMORY_DEBUG_ARGS){
810 NYD_ENTER;
812 assert(self != NULL);
813 #if 0 /* FIXME memory alloc too large */
814 if(SI32_MAX - n_ALIGN(1) - l <= noof)
815 n_panic(_("Memory allocation too large"));
816 #endif
818 if(self->s_len < nlen)
819 self = (n_string_reserve)(self, nlen n_MEMORY_DEBUG_ARGSCALL);
820 self->s_len = (ui32_t)nlen;
821 NYD_LEAVE;
822 return self;
825 FL struct n_string *
826 (n_string_push_buf)(struct n_string *self, char const *buf, size_t buflen
827 n_MEMORY_DEBUG_ARGS){
828 NYD_ENTER;
830 assert(self != NULL);
831 assert(buflen == 0 || buf != NULL);
833 if(buflen == UIZ_MAX)
834 buflen = (buf == NULL) ? 0 : strlen(buf);
836 if(buflen > 0){
837 ui32_t i;
839 self = (n_string_reserve)(self, buflen n_MEMORY_DEBUG_ARGSCALL);
840 memcpy(&self->s_dat[i = self->s_len], buf, buflen);
841 self->s_len = (i += (ui32_t)buflen);
843 NYD_LEAVE;
844 return self;
847 FL struct n_string *
848 (n_string_push_c)(struct n_string *self, char c n_MEMORY_DEBUG_ARGS){
849 NYD_ENTER;
851 assert(self != NULL);
853 if(self->s_len + 1 >= self->s_size)
854 self = (n_string_reserve)(self, 1 n_MEMORY_DEBUG_ARGSCALL);
855 self->s_dat[self->s_len++] = c;
856 NYD_LEAVE;
857 return self;
860 FL struct n_string *
861 (n_string_unshift_buf)(struct n_string *self, char const *buf, size_t buflen
862 n_MEMORY_DEBUG_ARGS){
863 NYD_ENTER;
865 assert(self != NULL);
866 assert(buflen == 0 || buf != NULL);
868 if(buflen == UIZ_MAX)
869 buflen = (buf == NULL) ? 0 : strlen(buf);
871 if(buflen > 0){
872 self = (n_string_reserve)(self, buflen n_MEMORY_DEBUG_ARGSCALL);
873 if(self->s_len > 0)
874 memmove(&self->s_dat[buflen], self->s_dat, self->s_len);
875 memcpy(self->s_dat, buf, buflen);
876 self->s_len += (ui32_t)buflen;
878 NYD_LEAVE;
879 return self;
882 FL struct n_string *
883 (n_string_unshift_c)(struct n_string *self, char c n_MEMORY_DEBUG_ARGS){
884 NYD_ENTER;
886 assert(self != NULL);
888 if(self->s_len + 1 >= self->s_size)
889 self = (n_string_reserve)(self, 1 n_MEMORY_DEBUG_ARGSCALL);
890 if(self->s_len > 0)
891 memmove(&self->s_dat[1], self->s_dat, self->s_len);
892 self->s_dat[0] = c;
893 ++self->s_len;
894 NYD_LEAVE;
895 return self;
898 FL struct n_string *
899 (n_string_insert_buf)(struct n_string *self, size_t idx,
900 char const *buf, size_t buflen n_MEMORY_DEBUG_ARGS){
901 NYD_ENTER;
903 assert(self != NULL);
904 assert(buflen == 0 || buf != NULL);
905 assert(idx <= self->s_len);
907 if(buflen == UIZ_MAX)
908 buflen = (buf == NULL) ? 0 : strlen(buf);
910 if(buflen > 0){
911 self = (n_string_reserve)(self, buflen n_MEMORY_DEBUG_ARGSCALL);
912 if(self->s_len > 0)
913 memmove(&self->s_dat[idx + buflen], &self->s_dat[idx],
914 self->s_len - idx);
915 memcpy(&self->s_dat[idx], buf, buflen);
916 self->s_len += (ui32_t)buflen;
918 NYD_LEAVE;
919 return self;
922 FL struct n_string *
923 (n_string_insert_c)(struct n_string *self, size_t idx,
924 char c n_MEMORY_DEBUG_ARGS){
925 NYD_ENTER;
927 assert(self != NULL);
928 assert(idx <= self->s_len);
930 if(self->s_len + 1 >= self->s_size)
931 self = (n_string_reserve)(self, 1 n_MEMORY_DEBUG_ARGSCALL);
932 if(self->s_len > 0)
933 memmove(&self->s_dat[idx + 1], &self->s_dat[idx], self->s_len - idx);
934 self->s_dat[idx] = c;
935 ++self->s_len;
936 NYD_LEAVE;
937 return self;
940 FL struct n_string *
941 n_string_cut(struct n_string *self, size_t idx, size_t len){
942 NYD_ENTER;
944 assert(self != NULL);
945 assert(UIZ_MAX - idx > len);
946 assert(SI32_MAX >= idx + len);
947 assert(idx + len <= self->s_len);
949 if(len > 0)
950 memmove(&self->s_dat[idx], &self->s_dat[idx + len],
951 (self->s_len -= len) - idx);
952 NYD_LEAVE;
953 return self;
956 FL char *
957 (n_string_cp)(struct n_string *self n_MEMORY_DEBUG_ARGS){
958 char *rv;
959 NYD2_ENTER;
961 assert(self != NULL);
963 if(self->s_size == 0)
964 self = (n_string_reserve)(self, 1 n_MEMORY_DEBUG_ARGSCALL);
966 (rv = self->s_dat)[self->s_len] = '\0';
967 NYD2_LEAVE;
968 return rv;
971 FL char const *
972 n_string_cp_const(struct n_string const *self){
973 char const *rv;
974 NYD2_ENTER;
976 assert(self != NULL);
978 if(self->s_size != 0){
979 ((struct n_string*)n_UNCONST(self))->s_dat[self->s_len] = '\0';
980 rv = self->s_dat;
981 }else
982 rv = n_empty;
983 NYD2_LEAVE;
984 return rv;
988 * UTF-8
991 FL ui32_t
992 n_utf8_to_utf32(char const **bdat, size_t *blen) /* TODO check false UTF8 */
994 char const *cp;
995 size_t l;
996 ui32_t c, x;
997 NYD2_ENTER;
999 cp = *bdat;
1000 l = *blen - 1;
1001 x = (ui8_t)*cp++;
1003 if (x <= 0x7Fu)
1004 c = x;
1005 else {
1006 if ((x & 0xE0u) == 0xC0u) {
1007 if (l < 1)
1008 goto jerr;
1009 l -= 1;
1010 c = x & ~0xC0u;
1011 } else if ((x & 0xF0u) == 0xE0u) {
1012 if (l < 2)
1013 goto jerr;
1014 l -= 2;
1015 c = x & ~0xE0u;
1016 c <<= 6;
1017 x = (ui8_t)*cp++;
1018 c |= x & 0x7Fu;
1019 } else {
1020 if (l < 3)
1021 goto jerr;
1022 l -= 3;
1023 c = x & ~0xF0u;
1024 c <<= 6;
1025 x = (ui8_t)*cp++;
1026 c |= x & 0x7Fu;
1027 c <<= 6;
1028 x = (ui8_t)*cp++;
1029 c |= x & 0x7Fu;
1031 c <<= 6;
1032 x = (ui8_t)*cp++;
1033 c |= x & 0x7Fu;
1036 jleave:
1037 *bdat = cp;
1038 *blen = l;
1039 NYD2_LEAVE;
1040 return c;
1041 jerr:
1042 c = UI32_MAX;
1043 goto jleave;
1046 FL size_t
1047 n_utf32_to_utf8(ui32_t c, char *buf)
1049 struct {
1050 ui32_t lower_bound;
1051 ui32_t upper_bound;
1052 ui8_t enc_leader;
1053 ui8_t enc_lval;
1054 ui8_t dec_leader_mask;
1055 ui8_t dec_leader_val_mask;
1056 ui8_t dec_bytes_togo;
1057 ui8_t cat_index;
1058 ui8_t __dummy[2];
1059 } const _cat[] = {
1060 {0x00000000, 0x00000000, 0x00, 0, 0x00, 0x00, 0, 0, {0,}},
1061 {0x00000000, 0x0000007F, 0x00, 1, 0x80, 0x7F, 1-1, 1, {0,}},
1062 {0x00000080, 0x000007FF, 0xC0, 2, 0xE0, 0xFF-0xE0, 2-1, 2, {0,}},
1063 /* We assume surrogates are U+D800 - U+DFFF, _cat index 3 */
1064 /* xxx _from_utf32() simply assumes magic code points for surrogates!
1065 * xxx (However, should we ever get yet another surrogate range we
1066 * xxx need to deal with that all over the place anyway? */
1067 {0x00000800, 0x0000FFFF, 0xE0, 3, 0xF0, 0xFF-0xF0, 3-1, 3, {0,}},
1068 {0x00010000, 0x0010FFFF, 0xF0, 4, 0xF8, 0xFF-0xF8, 4-1, 4, {0,}},
1069 }, *catp = _cat;
1070 size_t l;
1072 if (c <= _cat[0].upper_bound) { catp += 0; goto j0; }
1073 if (c <= _cat[1].upper_bound) { catp += 1; goto j1; }
1074 if (c <= _cat[2].upper_bound) { catp += 2; goto j2; }
1075 if (c <= _cat[3].upper_bound) {
1076 /* Surrogates may not be converted (Compatibility rule C10) */
1077 if (c >= 0xD800u && c <= 0xDFFFu)
1078 goto jerr;
1079 catp += 3;
1080 goto j3;
1082 if (c <= _cat[4].upper_bound) { catp += 4; goto j4; }
1083 jerr:
1084 c = 0xFFFDu; /* Unicode replacement character */
1085 catp += 3;
1086 goto j3;
1088 buf[3] = (char)0x80u | (char)(c & 0x3Fu); c >>= 6;
1090 buf[2] = (char)0x80u | (char)(c & 0x3Fu); c >>= 6;
1092 buf[1] = (char)0x80u | (char)(c & 0x3Fu); c >>= 6;
1094 buf[0] = (char)catp->enc_leader | (char)(c);
1096 buf[catp->enc_lval] = '\0';
1097 l = catp->enc_lval;
1098 NYD2_LEAVE;
1099 return l;
1103 * Our iconv(3) wrapper
1106 #ifdef HAVE_ICONV
1107 FL iconv_t
1108 n_iconv_open(char const *tocode, char const *fromcode){
1109 iconv_t id;
1110 NYD_ENTER;
1112 if((!asccasecmp(fromcode, "unknown-8bit") ||
1113 !asccasecmp(fromcode, "binary")) &&
1114 (fromcode = ok_vlook(charset_unknown_8bit)) == NULL)
1115 fromcode = ok_vlook(CHARSET_8BIT_OKEY);
1117 id = iconv_open(tocode, fromcode);
1119 /* If the encoding names are equal at this point, they are just not
1120 * understood by iconv(), and we cannot sensibly use it in any way. We do
1121 * not perform this as an optimization above since iconv() can otherwise be
1122 * used to check the validity of the input even with identical encoding
1123 * names */
1124 if (id == (iconv_t)-1 && !asccasecmp(tocode, fromcode))
1125 n_err_no = n_ERR_NONE;
1126 NYD_LEAVE;
1127 return id;
1130 FL void
1131 n_iconv_close(iconv_t cd){
1132 NYD_ENTER;
1133 iconv_close(cd);
1134 if(cd == iconvd)
1135 iconvd = (iconv_t)-1;
1136 NYD_LEAVE;
1139 FL void
1140 n_iconv_reset(iconv_t cd){
1141 NYD_ENTER;
1142 iconv(cd, NULL, NULL, NULL, NULL);
1143 NYD_LEAVE;
1146 /* (2012-09-24: export and use it exclusively to isolate prototype problems
1147 * (*inb* is 'char const **' except in POSIX) in a single place.
1148 * GNU libiconv even allows for configuration time const/non-const..
1149 * In the end it's an ugly guess, but we can't do better since make(1) doesn't
1150 * support compiler invocations which bail on error, so no -Werror */
1151 /* Citrus project? */
1152 # if defined _ICONV_H_ && defined __ICONV_F_HIDE_INVALID
1153 /* DragonFly 3.2.1 is special TODO newer DragonFly too, but different */
1154 # if n_OS_DRAGONFLY
1155 # define __INBCAST(S) (char ** __restrict__)n_UNCONST(S)
1156 # else
1157 # define __INBCAST(S) (char const **)n_UNCONST(S)
1158 # endif
1159 # elif n_OS_SUNOS || n_OS_SOLARIS
1160 # define __INBCAST(S) (char const ** __restrict__)n_UNCONST(S)
1161 # endif
1162 # ifndef __INBCAST
1163 # define __INBCAST(S) (char **)n_UNCONST(S)
1164 # endif
1166 FL int
1167 n_iconv_buf(iconv_t cd, enum n_iconv_flags icf,
1168 char const **inb, size_t *inbleft, char **outb, size_t *outbleft){
1169 int err;
1170 NYD2_ENTER;
1172 if((icf & n_ICONV_UNIREPL) && !(n_psonce & n_PSO_UNICODE))
1173 icf &= ~n_ICONV_UNIREPL;
1175 for(;;){
1176 size_t sz;
1178 sz = iconv(cd, __INBCAST(inb), inbleft, outb, outbleft);
1179 if(sz > 0 && !(icf & n_ICONV_IGN_NOREVERSE)){
1180 err = n_ERR_NOENT;
1181 goto jleave;
1183 if(sz != (size_t)-1)
1184 break;
1186 err = n_err_no;
1187 if(!(icf & n_ICONV_IGN_ILSEQ) || err != n_ERR_ILSEQ)
1188 goto jleave;
1189 if(*inbleft > 0){
1190 ++(*inb);
1191 --(*inbleft);
1192 if(icf & n_ICONV_UNIREPL){
1193 if(*outbleft >= sizeof(n_unirepl) -1){
1194 memcpy(*outb, n_unirepl, sizeof(n_unirepl) -1);
1195 *outb += sizeof(n_unirepl) -1;
1196 *outbleft -= sizeof(n_unirepl) -1;
1197 continue;
1199 }else if(*outbleft > 0){
1200 *(*outb)++ = '?';
1201 --*outbleft;
1202 continue;
1204 err = E2BIG;
1205 goto jleave;
1206 }else if(*outbleft > 0){
1207 **outb = '\0';
1208 goto jleave;
1211 err = 0;
1212 jleave:
1213 NYD2_LEAVE;
1214 return err;
1216 # undef __INBCAST
1218 FL int
1219 n_iconv_str(iconv_t cd, enum n_iconv_flags icf,
1220 struct str *out, struct str const *in, struct str *in_rest_or_null)
1222 int err;
1223 char *obb, *ob;
1224 char const *ib;
1225 size_t olb, ol, il;
1226 NYD2_ENTER;
1228 err = 0;
1229 obb = out->s;
1230 olb = out->l;
1231 ol = in->l;
1233 ol = (ol << 1) - (ol >> 4);
1234 if (olb <= ol) {
1235 olb = ol;
1236 goto jrealloc;
1239 for (;;) {
1240 ib = in->s;
1241 il = in->l;
1242 ob = obb;
1243 ol = olb;
1244 if((err = n_iconv_buf(cd, icf, &ib, &il, &ob, &ol)) == 0 || err != E2BIG)
1245 break;
1246 err = 0;
1247 olb += in->l;
1248 jrealloc:
1249 obb = n_realloc(obb, olb +1);
1252 if (in_rest_or_null != NULL) {
1253 in_rest_or_null->s = n_UNCONST(ib);
1254 in_rest_or_null->l = il;
1256 out->s = obb;
1257 out->s[out->l = olb - ol] = '\0';
1258 NYD2_LEAVE;
1259 return err;
1262 FL char *
1263 n_iconv_onetime_cp(enum n_iconv_flags icf,
1264 char const *tocode, char const *fromcode, char const *input){
1265 struct str out, in;
1266 iconv_t icd;
1267 char *rv;
1268 NYD2_ENTER;
1270 rv = NULL;
1271 if(tocode == NULL)
1272 tocode = ok_vlook(ttycharset);
1273 if(fromcode == NULL)
1274 fromcode = "utf-8";
1276 if((icd = iconv_open(tocode, fromcode)) == (iconv_t)-1)
1277 goto jleave;
1279 in.l = strlen(in.s = n_UNCONST(input)); /* logical */
1280 out.s = NULL, out.l = 0;
1281 if(!n_iconv_str(icd, icf, &out, &in, NULL))
1282 rv = savestrbuf(out.s, out.l);
1283 if(out.s != NULL)
1284 free(out.s);
1286 iconv_close(icd);
1287 jleave:
1288 NYD2_LEAVE;
1289 return rv;
1291 #endif /* HAVE_ICONV */
1293 /* s-it-mode */