make-release.inc: fix substr, that was too long
[s-mailx.git] / strings.c
blobf646182030a1d486fd89efaf2e70ec51826aa8e9
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);
52 news = (n_autorec_alloc_from_pool)(NULL, size +1 n_MEMORY_DEBUG_ARGSCALL);
53 if(size > 0)
54 memcpy(news, str, size);
55 news[size] = '\0';
56 NYD_LEAVE;
57 return news;
60 FL char *
61 (savestrbuf)(char const *sbuf, size_t sbuf_len n_MEMORY_DEBUG_ARGS)
63 char *news;
64 NYD_ENTER;
66 news = (n_autorec_alloc_from_pool)(NULL, sbuf_len +1
67 n_MEMORY_DEBUG_ARGSCALL);
68 if(sbuf_len > 0)
69 memcpy(news, sbuf, sbuf_len);
70 news[sbuf_len] = 0;
71 NYD_LEAVE;
72 return news;
75 FL char *
76 (savecatsep)(char const *s1, char sep, char const *s2 n_MEMORY_DEBUG_ARGS)
78 size_t l1, l2;
79 char *news;
80 NYD_ENTER;
82 l1 = (s1 != NULL) ? strlen(s1) : 0;
83 l2 = strlen(s2);
84 news = (n_autorec_alloc_from_pool)(NULL, l1 + (sep != '\0') + l2 +1
85 n_MEMORY_DEBUG_ARGSCALL);
86 if (l1 > 0) {
87 memcpy(news + 0, s1, l1);
88 if (sep != '\0')
89 news[l1++] = sep;
91 if(l2 > 0)
92 memcpy(news + l1, s2, l2);
93 news[l1 + l2] = '\0';
94 NYD_LEAVE;
95 return news;
99 * Support routines, auto-reclaimed storage
102 FL char *
103 (i_strdup)(char const *src n_MEMORY_DEBUG_ARGS)
105 size_t sz;
106 char *dest;
107 NYD_ENTER;
109 sz = strlen(src);
110 dest = (n_autorec_alloc_from_pool)(NULL, sz +1 n_MEMORY_DEBUG_ARGSCALL);
111 if(sz > 0)
112 i_strcpy(dest, src, sz);
113 dest[sz] = '\0';
114 NYD_LEAVE;
115 return dest;
118 FL struct str *
119 str_concat_csvl(struct str *self, ...) /* XXX onepass maybe better here */
121 va_list vl;
122 size_t l;
123 char const *cs;
124 NYD_ENTER;
126 va_start(vl, self);
127 for (l = 0; (cs = va_arg(vl, char const*)) != NULL;)
128 l += strlen(cs);
129 va_end(vl);
131 self->l = l;
132 self->s = salloc(l +1);
134 va_start(vl, self);
135 for (l = 0; (cs = va_arg(vl, char const*)) != NULL;) {
136 size_t i;
138 i = strlen(cs);
139 if(i > 0){
140 memcpy(self->s + l, cs, i);
141 l += i;
144 self->s[l] = '\0';
145 va_end(vl);
146 NYD_LEAVE;
147 return self;
150 FL struct str *
151 (str_concat_cpa)(struct str *self, char const * const *cpa,
152 char const *sep_o_null n_MEMORY_DEBUG_ARGS)
154 size_t sonl, l;
155 char const * const *xcpa;
156 NYD_ENTER;
158 sonl = (sep_o_null != NULL) ? strlen(sep_o_null) : 0;
160 for (l = 0, xcpa = cpa; *xcpa != NULL; ++xcpa)
161 l += strlen(*xcpa) + sonl;
163 self->l = l;
164 self->s = (n_autorec_alloc_from_pool)(NULL, l +1 n_MEMORY_DEBUG_ARGSCALL);
166 for (l = 0, xcpa = cpa; *xcpa != NULL; ++xcpa) {
167 size_t i;
169 i = strlen(*xcpa);
170 if(i > 0){
171 memcpy(self->s + l, *xcpa, i);
172 l += i;
174 if (sonl > 0) {
175 memcpy(self->s + l, sep_o_null, sonl);
176 l += sonl;
179 self->s[l] = '\0';
180 NYD_LEAVE;
181 return self;
185 * Routines that are not related to auto-reclaimed storage follow.
188 FL bool_t
189 n_anyof_buf(char const *template, char const *dat, size_t len){
190 char c;
191 NYD2_ENTER;
193 if(len == UIZ_MAX){
194 while((c = *template++) != '\0')
195 if(strchr(dat, c) != NULL)
196 break;
197 }else if(len > 0){
198 while((c = *template++) != '\0')
199 if(memchr(dat, c, len) != NULL)
200 break;
201 }else
202 c = '\0';
203 NYD2_LEAVE;
204 return (c != '\0');
207 FL char *
208 n_strsep(char **iolist, char sep, bool_t ignore_empty)
210 char *base, *cp;
211 NYD2_ENTER;
213 for (base = *iolist; base != NULL; base = *iolist) {
214 while (*base != '\0' && blankspacechar(*base))
215 ++base;
216 cp = strchr(base, sep);
217 if (cp != NULL)
218 *iolist = cp + 1;
219 else {
220 *iolist = NULL;
221 cp = base + strlen(base);
223 while (cp > base && blankspacechar(cp[-1]))
224 --cp;
225 *cp = '\0';
226 if (*base != '\0' || !ignore_empty)
227 break;
229 NYD2_LEAVE;
230 return base;
233 FL void
234 i_strcpy(char *dest, char const *src, size_t size)
236 NYD2_ENTER;
237 if (size > 0) {
238 for (;; ++dest, ++src)
239 if ((*dest = lowerconv(*src)) == '\0') {
240 break;
241 } else if (--size == 0) {
242 *dest = '\0';
243 break;
246 NYD2_LEAVE;
249 FL bool_t
250 is_prefix(char const *as1, char const *as2) /* TODO arg order */
252 char c;
253 NYD2_ENTER;
255 for (; (c = *as1) == *as2 && c != '\0'; ++as1, ++as2)
256 if (*as2 == '\0')
257 break;
258 NYD2_LEAVE;
259 return (c == '\0');
262 FL char *
263 string_quote(char const *v) /* TODO too simpleminded (getrawlist(), +++ ..) */
265 char const *cp;
266 size_t i;
267 char c, *rv;
268 NYD2_ENTER;
270 for (i = 0, cp = v; (c = *cp) != '\0'; ++i, ++cp)
271 if (c == '"' || c == '\\')
272 ++i;
273 rv = salloc(i +1);
275 for (i = 0, cp = v; (c = *cp) != '\0'; rv[i++] = c, ++cp)
276 if (c == '"' || c == '\\')
277 rv[i++] = '\\';
278 rv[i] = '\0';
279 NYD2_LEAVE;
280 return rv;
283 FL char *
284 laststring(char *linebuf, bool_t *needs_list, bool_t strip)
286 char *cp, *p, quoted;
287 NYD_ENTER;
289 /* Anything to do at all? */
290 if (*(cp = linebuf) == '\0')
291 goto jnull;
292 cp += strlen(linebuf) -1;
294 /* Strip away trailing blanks */
295 while (spacechar(*cp) && cp > linebuf)
296 --cp;
297 cp[1] = '\0';
298 if (cp == linebuf)
299 goto jleave;
301 /* Now search for the BOS of the "last string" */
302 quoted = *cp;
303 if (quoted == '\'' || quoted == '"') {
304 if (strip)
305 *cp = '\0';
306 } else
307 quoted = ' ';
309 while (cp > linebuf) {
310 --cp;
311 if (quoted != ' ') {
312 if (*cp != quoted)
313 continue;
314 } else if (!spacechar(*cp))
315 continue;
316 if (cp == linebuf || cp[-1] != '\\') {
317 /* When in whitespace mode, WS prefix doesn't belong */
318 if (quoted == ' ')
319 ++cp;
320 break;
322 /* Expand the escaped quote character */
323 for (p = --cp; (p[0] = p[1]) != '\0'; ++p)
326 if (strip && quoted != ' ' && *cp == quoted)
327 for (p = cp; (p[0] = p[1]) != '\0'; ++p)
330 /* The "last string" has been skipped over, but still, try to step backwards
331 * until we are at BOS or see whitespace, so as to make possible things like
332 * "? copy +'x y.mbox'" or even "? copy +x\ y.mbox" */
333 while (cp > linebuf) {
334 --cp;
335 if (spacechar(*cp)) {
336 p = cp;
337 *cp++ = '\0';
338 /* We can furtherly release our callees if we now decide whether the
339 * remaining non-"last string" line content contains non-WS */
340 while (--p >= linebuf)
341 if (!spacechar(*p))
342 goto jleave;
343 linebuf = cp;
344 break;
348 jleave:
349 if (cp != NULL && *cp == '\0')
350 goto jnull;
351 *needs_list = (cp != linebuf && *linebuf != '\0');
352 j_leave:
353 NYD_LEAVE;
354 return cp;
355 jnull:
356 *needs_list = FAL0;
357 cp = NULL;
358 goto j_leave;
361 FL void
362 makelow(char *cp) /* TODO isn't that crap? --> */
364 NYD_ENTER;
365 #ifdef HAVE_C90AMEND1
366 if (n_mb_cur_max > 1) {
367 char *tp = cp;
368 wchar_t wc;
369 int len;
371 while (*cp != '\0') {
372 len = mbtowc(&wc, cp, n_mb_cur_max);
373 if (len < 0)
374 *tp++ = *cp++;
375 else {
376 wc = towlower(wc);
377 if (wctomb(tp, wc) == len)
378 tp += len, cp += len;
379 else
380 *tp++ = *cp++; /* <-- at least here */
383 } else
384 #endif
387 *cp = tolower((uc_i)*cp);
388 while (*cp++ != '\0');
390 NYD_LEAVE;
393 FL bool_t
394 substr(char const *str, char const *sub)
396 char const *cp, *backup;
397 NYD_ENTER;
399 cp = sub;
400 backup = str;
401 while (*str != '\0' && *cp != '\0') {
402 #ifdef HAVE_C90AMEND1
403 if (n_mb_cur_max > 1) {
404 wchar_t c, c2;
405 int sz;
407 if ((sz = mbtowc(&c, cp, n_mb_cur_max)) == -1)
408 goto Jsinglebyte;
409 cp += sz;
410 if ((sz = mbtowc(&c2, str, n_mb_cur_max)) == -1)
411 goto Jsinglebyte;
412 str += sz;
413 c = towupper(c);
414 c2 = towupper(c2);
415 if (c != c2) {
416 if ((sz = mbtowc(&c, backup, n_mb_cur_max)) > 0) {
417 backup += sz;
418 str = backup;
419 } else
420 str = ++backup;
421 cp = sub;
423 } else
424 Jsinglebyte:
425 #endif
427 int c, c2;
429 c = *cp++ & 0377;
430 if (islower(c))
431 c = toupper(c);
432 c2 = *str++ & 0377;
433 if (islower(c2))
434 c2 = toupper(c2);
435 if (c != c2) {
436 str = ++backup;
437 cp = sub;
441 NYD_LEAVE;
442 return (*cp == '\0');
445 FL char *
446 sstpcpy(char *dst, char const *src)
448 NYD2_ENTER;
449 while ((*dst = *src++) != '\0')
450 ++dst;
451 NYD2_LEAVE;
452 return dst;
455 FL char *
456 (sstrdup)(char const *cp n_MEMORY_DEBUG_ARGS)
458 char *dp;
459 NYD2_ENTER;
461 dp = (cp == NULL) ? NULL : (sbufdup)(cp, strlen(cp) n_MEMORY_DEBUG_ARGSCALL);
462 NYD2_LEAVE;
463 return dp;
466 FL char *
467 (sbufdup)(char const *cp, size_t len n_MEMORY_DEBUG_ARGS)
469 char *dp = NULL;
470 NYD2_ENTER;
472 dp = (n_alloc)(len +1 n_MEMORY_DEBUG_ARGSCALL);
473 if (cp != NULL)
474 memcpy(dp, cp, len);
475 dp[len] = '\0';
476 NYD2_LEAVE;
477 return dp;
480 FL ssize_t
481 n_strscpy(char *dst, char const *src, size_t dstsize){
482 ssize_t rv;
483 NYD2_ENTER;
485 if(n_LIKELY(dstsize > 0)){
486 rv = 0;
488 if((dst[rv] = src[rv]) == '\0')
489 goto jleave;
490 ++rv;
491 }while(--dstsize > 0);
492 dst[--rv] = '\0';
494 #ifdef HAVE_DEVEL
495 else
496 assert(dstsize > 0);
497 #endif
498 rv = -1;
499 jleave:
500 NYD2_LEAVE;
501 return rv;
504 FL int
505 asccasecmp(char const *s1, char const *s2)
507 int cmp;
508 NYD2_ENTER;
510 for (;;) {
511 char c1 = *s1++, c2 = *s2++;
512 if ((cmp = lowerconv(c1) - lowerconv(c2)) != 0 || c1 == '\0')
513 break;
515 NYD2_LEAVE;
516 return cmp;
519 FL int
520 ascncasecmp(char const *s1, char const *s2, size_t sz)
522 int cmp = 0;
523 NYD2_ENTER;
525 while (sz-- > 0) {
526 char c1 = *s1++, c2 = *s2++;
527 cmp = (ui8_t)lowerconv(c1);
528 cmp -= (ui8_t)lowerconv(c2);
529 if (cmp != 0 || c1 == '\0')
530 break;
532 NYD2_LEAVE;
533 return cmp;
536 FL char const *
537 asccasestr(char const *s1, char const *s2)
539 char c2, c1;
540 NYD2_ENTER;
542 for (c2 = *s2++, c2 = lowerconv(c2);;) {
543 if ((c1 = *s1++) == '\0') {
544 s1 = NULL;
545 break;
547 if (lowerconv(c1) == c2 && is_asccaseprefix(s2, s1)) {
548 --s1;
549 break;
552 NYD2_LEAVE;
553 return s1;
556 FL bool_t
557 is_asccaseprefix(char const *as1, char const *as2) /* TODO arg order */
559 char c1, c2;
560 NYD2_ENTER;
562 for(;; ++as1, ++as2){
563 c1 = *as1;
564 c1 = lowerconv(c1);
565 c2 = *as2;
566 c2 = lowerconv(c2);
568 if(c1 != c2 || c1 == '\0')
569 break;
570 if(c2 == '\0')
571 break;
573 NYD2_LEAVE;
574 return (c1 == '\0');
577 FL bool_t
578 is_ascncaseprefix(char const *as1, char const *as2, size_t sz)
580 char c1, c2;
581 bool_t rv;
582 NYD2_ENTER;
584 for(rv = TRU1; sz-- > 0; ++as1, ++as2){
585 c1 = *as1;
586 c1 = lowerconv(c1);
587 c2 = *as2;
588 c2 = lowerconv(c2);
590 if(!(rv = (c1 == c2)) || c1 == '\0')
591 break;
592 if(c2 == '\0')
593 break;
595 NYD2_LEAVE;
596 return rv;
600 FL struct str *
601 (n_str_assign_buf)(struct str *self, char const *buf, uiz_t buflen
602 n_MEMORY_DEBUG_ARGS){
603 NYD_ENTER;
604 if(buflen == UIZ_MAX)
605 buflen = (buf == NULL) ? 0 : strlen(buf);
607 assert(buflen == 0 || buf != NULL);
609 if(n_LIKELY(buflen > 0)){
610 self->s = (n_realloc)(self->s, (self->l = buflen) +1
611 n_MEMORY_DEBUG_ARGSCALL);
612 memcpy(self->s, buf, buflen);
613 self->s[buflen] = '\0';
614 }else
615 self->l = 0;
616 NYD_LEAVE;
617 return self;
620 FL struct str *
621 (n_str_add_buf)(struct str *self, char const *buf, uiz_t buflen
622 n_MEMORY_DEBUG_ARGS){
623 NYD_ENTER;
624 if(buflen == UIZ_MAX)
625 buflen = (buf == NULL) ? 0 : strlen(buf);
627 assert(buflen == 0 || buf != NULL);
629 if(buflen > 0) {
630 size_t osl = self->l, nsl = osl + buflen;
632 self->s = (n_realloc)(self->s, (self->l = nsl) +1
633 n_MEMORY_DEBUG_ARGSCALL);
634 memcpy(self->s + osl, buf, buflen);
635 self->s[nsl] = '\0';
637 NYD_LEAVE;
638 return self;
641 FL struct str *
642 n_str_trim(struct str *self){
643 size_t l;
644 char const *cp;
645 NYD2_ENTER;
647 cp = self->s;
649 if((l = self->l) > 0){
650 while(spacechar(*cp)){
651 ++cp;
652 if(--l == 0)
653 break;
655 self->s = n_UNCONST(cp);
658 if(l > 0){
659 for(cp += l -1; spacechar(*cp); --cp)
660 if(--l == 0)
661 break;
663 self->l = l;
665 NYD2_LEAVE;
666 return self;
669 FL struct str *
670 n_str_trim_ifs(struct str *self, bool_t dodefaults){
671 char s, t, n, c;
672 char const *ifs, *cp;
673 size_t l, i;
674 NYD2_ENTER;
676 if((l = self->l) == 0)
677 goto jleave;
679 ifs = ok_vlook(ifs_ws);
680 cp = self->s;
681 s = t = n = '\0';
683 /* Check whether we can go fast(er) path */
684 for(i = 0; (c = ifs[i]) != '\0'; ++i){
685 switch(c){
686 case ' ': s = c; break;
687 case '\t': t = c; break;
688 case '\n': n = c; break;
689 default:
690 /* Need to go the slow path */
691 while(strchr(ifs, *cp) != NULL){
692 ++cp;
693 if(--l == 0)
694 break;
696 self->s = n_UNCONST(cp);
698 if(l > 0){
699 for(cp += l -1; strchr(ifs, *cp) != NULL;){
700 if(--l == 0)
701 break;
702 /* An uneven number of reverse solidus escapes last WS! */
703 else if(*--cp == '\\'){
704 siz_t j;
706 for(j = 1; l - (uiz_t)j > 0 && cp[-j] == '\\'; ++j)
708 if(j & 1){
709 ++l;
710 break;
715 self->l = l;
717 if(!dodefaults)
718 goto jleave;
719 cp = self->s;
720 ++i;
721 break;
725 /* No ifs-ws? No more data? No trimming */
726 if(l == 0 || (i == 0 && !dodefaults))
727 goto jleave;
729 if(dodefaults){
730 s = ' ';
731 t = '\t';
732 n = '\n';
735 if(l > 0){
736 while((c = *cp) != '\0' && (c == s || c == t || c == n)){
737 ++cp;
738 if(--l == 0)
739 break;
741 self->s = n_UNCONST(cp);
744 if(l > 0){
745 for(cp += l -1; (c = *cp) != '\0' && (c == s || c == t || c == n);){
746 if(--l == 0)
747 break;
748 /* An uneven number of reverse solidus escapes last WS! */
749 else if(*--cp == '\\'){
750 siz_t j;
752 for(j = 1; l - (uiz_t)j > 0 && cp[-j] == '\\'; ++j)
754 if(j & 1){
755 ++l;
756 break;
761 self->l = l;
762 jleave:
763 NYD2_LEAVE;
764 return self;
768 * struct n_string TODO extend, optimize
771 FL struct n_string *
772 (n_string_clear)(struct n_string *self n_MEMORY_DEBUG_ARGS){
773 NYD_ENTER;
775 assert(self != NULL);
777 if(self->s_size != 0){
778 if(!self->s_auto){
779 (n_free)(self->s_dat n_MEMORY_DEBUG_ARGSCALL);
781 self->s_len = self->s_auto = self->s_size = 0;
782 self->s_dat = NULL;
784 NYD_LEAVE;
785 return self;
788 FL struct n_string *
789 (n_string_reserve)(struct n_string *self, size_t noof n_MEMORY_DEBUG_ARGS){
790 ui32_t i, l, s;
791 NYD_ENTER;
793 assert(self != NULL);
795 s = self->s_size;
796 l = self->s_len;
797 #if 0 /* FIXME memory alloc too large */
798 if(SI32_MAX - n_ALIGN(1) - l <= noof)
799 n_panic(_("Memory allocation too large"));
800 #endif
802 if((i = s - l) <= ++noof){
803 i += l + (ui32_t)noof;
804 i = n_ALIGN(i);
805 self->s_size = i -1;
807 if(!self->s_auto)
808 self->s_dat = (n_realloc)(self->s_dat, i n_MEMORY_DEBUG_ARGSCALL);
809 else{
810 char *ndat = (n_autorec_alloc_from_pool)(NULL, i
811 n_MEMORY_DEBUG_ARGSCALL);
813 if(l > 0)
814 memcpy(ndat, self->s_dat, l);
815 self->s_dat = ndat;
818 NYD_LEAVE;
819 return self;
822 FL struct n_string *
823 (n_string_resize)(struct n_string *self, size_t nlen n_MEMORY_DEBUG_ARGS){
824 NYD_ENTER;
826 assert(self != NULL);
827 #if 0 /* FIXME memory alloc too large */
828 if(SI32_MAX - n_ALIGN(1) - l <= noof)
829 n_panic(_("Memory allocation too large"));
830 #endif
832 if(self->s_len < nlen)
833 self = (n_string_reserve)(self, nlen n_MEMORY_DEBUG_ARGSCALL);
834 self->s_len = (ui32_t)nlen;
835 NYD_LEAVE;
836 return self;
839 FL struct n_string *
840 (n_string_push_buf)(struct n_string *self, char const *buf, size_t buflen
841 n_MEMORY_DEBUG_ARGS){
842 NYD_ENTER;
844 assert(self != NULL);
845 assert(buflen == 0 || buf != NULL);
847 if(buflen == UIZ_MAX)
848 buflen = (buf == NULL) ? 0 : strlen(buf);
850 if(buflen > 0){
851 ui32_t i;
853 self = (n_string_reserve)(self, buflen n_MEMORY_DEBUG_ARGSCALL);
854 memcpy(&self->s_dat[i = self->s_len], buf, buflen);
855 self->s_len = (i += (ui32_t)buflen);
857 NYD_LEAVE;
858 return self;
861 FL struct n_string *
862 (n_string_push_c)(struct n_string *self, char c n_MEMORY_DEBUG_ARGS){
863 NYD_ENTER;
865 assert(self != NULL);
867 if(self->s_len + 1 >= self->s_size)
868 self = (n_string_reserve)(self, 1 n_MEMORY_DEBUG_ARGSCALL);
869 self->s_dat[self->s_len++] = c;
870 NYD_LEAVE;
871 return self;
874 FL struct n_string *
875 (n_string_unshift_buf)(struct n_string *self, char const *buf, size_t buflen
876 n_MEMORY_DEBUG_ARGS){
877 NYD_ENTER;
879 assert(self != NULL);
880 assert(buflen == 0 || buf != NULL);
882 if(buflen == UIZ_MAX)
883 buflen = (buf == NULL) ? 0 : strlen(buf);
885 if(buflen > 0){
886 self = (n_string_reserve)(self, buflen n_MEMORY_DEBUG_ARGSCALL);
887 if(self->s_len > 0)
888 memmove(&self->s_dat[buflen], self->s_dat, self->s_len);
889 memcpy(self->s_dat, buf, buflen);
890 self->s_len += (ui32_t)buflen;
892 NYD_LEAVE;
893 return self;
896 FL struct n_string *
897 (n_string_unshift_c)(struct n_string *self, char c n_MEMORY_DEBUG_ARGS){
898 NYD_ENTER;
900 assert(self != NULL);
902 if(self->s_len + 1 >= self->s_size)
903 self = (n_string_reserve)(self, 1 n_MEMORY_DEBUG_ARGSCALL);
904 if(self->s_len > 0)
905 memmove(&self->s_dat[1], self->s_dat, self->s_len);
906 self->s_dat[0] = c;
907 ++self->s_len;
908 NYD_LEAVE;
909 return self;
912 FL struct n_string *
913 (n_string_insert_buf)(struct n_string *self, size_t idx,
914 char const *buf, size_t buflen n_MEMORY_DEBUG_ARGS){
915 NYD_ENTER;
917 assert(self != NULL);
918 assert(buflen == 0 || buf != NULL);
919 assert(idx <= self->s_len);
921 if(buflen == UIZ_MAX)
922 buflen = (buf == NULL) ? 0 : strlen(buf);
924 if(buflen > 0){
925 self = (n_string_reserve)(self, buflen n_MEMORY_DEBUG_ARGSCALL);
926 if(self->s_len > 0)
927 memmove(&self->s_dat[idx + buflen], &self->s_dat[idx],
928 self->s_len - idx);
929 memcpy(&self->s_dat[idx], buf, buflen);
930 self->s_len += (ui32_t)buflen;
932 NYD_LEAVE;
933 return self;
936 FL struct n_string *
937 (n_string_insert_c)(struct n_string *self, size_t idx,
938 char c n_MEMORY_DEBUG_ARGS){
939 NYD_ENTER;
941 assert(self != NULL);
942 assert(idx <= self->s_len);
944 if(self->s_len + 1 >= self->s_size)
945 self = (n_string_reserve)(self, 1 n_MEMORY_DEBUG_ARGSCALL);
946 if(self->s_len > 0)
947 memmove(&self->s_dat[idx + 1], &self->s_dat[idx], self->s_len - idx);
948 self->s_dat[idx] = c;
949 ++self->s_len;
950 NYD_LEAVE;
951 return self;
954 FL struct n_string *
955 n_string_cut(struct n_string *self, size_t idx, size_t len){
956 NYD_ENTER;
958 assert(self != NULL);
959 assert(UIZ_MAX - idx > len);
960 assert(SI32_MAX >= idx + len);
961 assert(idx + len <= self->s_len);
963 if(len > 0)
964 memmove(&self->s_dat[idx], &self->s_dat[idx + len],
965 (self->s_len -= len) - idx);
966 NYD_LEAVE;
967 return self;
970 FL char *
971 (n_string_cp)(struct n_string *self n_MEMORY_DEBUG_ARGS){
972 char *rv;
973 NYD2_ENTER;
975 assert(self != NULL);
977 if(self->s_size == 0)
978 self = (n_string_reserve)(self, 1 n_MEMORY_DEBUG_ARGSCALL);
980 (rv = self->s_dat)[self->s_len] = '\0';
981 NYD2_LEAVE;
982 return rv;
985 FL char const *
986 n_string_cp_const(struct n_string const *self){
987 char const *rv;
988 NYD2_ENTER;
990 assert(self != NULL);
992 if(self->s_size != 0){
993 ((struct n_string*)n_UNCONST(self))->s_dat[self->s_len] = '\0';
994 rv = self->s_dat;
995 }else
996 rv = n_empty;
997 NYD2_LEAVE;
998 return rv;
1002 * UTF-8
1005 FL ui32_t
1006 n_utf8_to_utf32(char const **bdat, size_t *blen) /* TODO check false UTF8 */
1008 char const *cp;
1009 size_t l;
1010 ui32_t c, x;
1011 NYD2_ENTER;
1013 cp = *bdat;
1014 l = *blen - 1;
1015 x = (ui8_t)*cp++;
1017 if (x <= 0x7Fu)
1018 c = x;
1019 else {
1020 if ((x & 0xE0u) == 0xC0u) {
1021 if (l < 1)
1022 goto jerr;
1023 l -= 1;
1024 c = x & ~0xC0u;
1025 } else if ((x & 0xF0u) == 0xE0u) {
1026 if (l < 2)
1027 goto jerr;
1028 l -= 2;
1029 c = x & ~0xE0u;
1030 c <<= 6;
1031 x = (ui8_t)*cp++;
1032 c |= x & 0x7Fu;
1033 } else {
1034 if (l < 3)
1035 goto jerr;
1036 l -= 3;
1037 c = x & ~0xF0u;
1038 c <<= 6;
1039 x = (ui8_t)*cp++;
1040 c |= x & 0x7Fu;
1041 c <<= 6;
1042 x = (ui8_t)*cp++;
1043 c |= x & 0x7Fu;
1045 c <<= 6;
1046 x = (ui8_t)*cp++;
1047 c |= x & 0x7Fu;
1050 jleave:
1051 *bdat = cp;
1052 *blen = l;
1053 NYD2_LEAVE;
1054 return c;
1055 jerr:
1056 c = UI32_MAX;
1057 goto jleave;
1060 FL size_t
1061 n_utf32_to_utf8(ui32_t c, char *buf)
1063 struct {
1064 ui32_t lower_bound;
1065 ui32_t upper_bound;
1066 ui8_t enc_leader;
1067 ui8_t enc_lval;
1068 ui8_t dec_leader_mask;
1069 ui8_t dec_leader_val_mask;
1070 ui8_t dec_bytes_togo;
1071 ui8_t cat_index;
1072 ui8_t __dummy[2];
1073 } const _cat[] = {
1074 {0x00000000, 0x00000000, 0x00, 0, 0x00, 0x00, 0, 0, {0,}},
1075 {0x00000000, 0x0000007F, 0x00, 1, 0x80, 0x7F, 1-1, 1, {0,}},
1076 {0x00000080, 0x000007FF, 0xC0, 2, 0xE0, 0xFF-0xE0, 2-1, 2, {0,}},
1077 /* We assume surrogates are U+D800 - U+DFFF, _cat index 3 */
1078 /* xxx _from_utf32() simply assumes magic code points for surrogates!
1079 * xxx (However, should we ever get yet another surrogate range we
1080 * xxx need to deal with that all over the place anyway? */
1081 {0x00000800, 0x0000FFFF, 0xE0, 3, 0xF0, 0xFF-0xF0, 3-1, 3, {0,}},
1082 {0x00010000, 0x0010FFFF, 0xF0, 4, 0xF8, 0xFF-0xF8, 4-1, 4, {0,}},
1083 }, *catp = _cat;
1084 size_t l;
1086 if (c <= _cat[0].upper_bound) { catp += 0; goto j0; }
1087 if (c <= _cat[1].upper_bound) { catp += 1; goto j1; }
1088 if (c <= _cat[2].upper_bound) { catp += 2; goto j2; }
1089 if (c <= _cat[3].upper_bound) {
1090 /* Surrogates may not be converted (Compatibility rule C10) */
1091 if (c >= 0xD800u && c <= 0xDFFFu)
1092 goto jerr;
1093 catp += 3;
1094 goto j3;
1096 if (c <= _cat[4].upper_bound) { catp += 4; goto j4; }
1097 jerr:
1098 c = 0xFFFDu; /* Unicode replacement character */
1099 catp += 3;
1100 goto j3;
1102 buf[3] = (char)0x80u | (char)(c & 0x3Fu); c >>= 6;
1104 buf[2] = (char)0x80u | (char)(c & 0x3Fu); c >>= 6;
1106 buf[1] = (char)0x80u | (char)(c & 0x3Fu); c >>= 6;
1108 buf[0] = (char)catp->enc_leader | (char)(c);
1110 buf[catp->enc_lval] = '\0';
1111 l = catp->enc_lval;
1112 NYD2_LEAVE;
1113 return l;
1117 * Our iconv(3) wrapper
1120 #ifdef HAVE_ICONV
1121 FL iconv_t
1122 n_iconv_open(char const *tocode, char const *fromcode){
1123 iconv_t id;
1124 NYD_ENTER;
1126 if((!asccasecmp(fromcode, "unknown-8bit") ||
1127 !asccasecmp(fromcode, "binary")) &&
1128 (fromcode = ok_vlook(charset_unknown_8bit)) == NULL)
1129 fromcode = ok_vlook(CHARSET_8BIT_OKEY);
1131 id = iconv_open(tocode, fromcode);
1133 /* If the encoding names are equal at this point, they are just not
1134 * understood by iconv(), and we cannot sensibly use it in any way. We do
1135 * not perform this as an optimization above since iconv() can otherwise be
1136 * used to check the validity of the input even with identical encoding
1137 * names */
1138 if (id == (iconv_t)-1 && !asccasecmp(tocode, fromcode))
1139 n_err_no = n_ERR_NONE;
1140 NYD_LEAVE;
1141 return id;
1144 FL void
1145 n_iconv_close(iconv_t cd){
1146 NYD_ENTER;
1147 iconv_close(cd);
1148 if(cd == iconvd)
1149 iconvd = (iconv_t)-1;
1150 NYD_LEAVE;
1153 FL void
1154 n_iconv_reset(iconv_t cd){
1155 NYD_ENTER;
1156 iconv(cd, NULL, NULL, NULL, NULL);
1157 NYD_LEAVE;
1160 /* (2012-09-24: export and use it exclusively to isolate prototype problems
1161 * (*inb* is 'char const **' except in POSIX) in a single place.
1162 * GNU libiconv even allows for configuration time const/non-const..
1163 * In the end it's an ugly guess, but we can't do better since make(1) doesn't
1164 * support compiler invocations which bail on error, so no -Werror */
1165 /* Citrus project? */
1166 # if defined _ICONV_H_ && defined __ICONV_F_HIDE_INVALID
1167 /* DragonFly 3.2.1 is special TODO newer DragonFly too, but different */
1168 # if n_OS_DRAGONFLY
1169 # define __INBCAST(S) (char ** __restrict__)n_UNCONST(S)
1170 # else
1171 # define __INBCAST(S) (char const **)n_UNCONST(S)
1172 # endif
1173 # elif n_OS_SUNOS || n_OS_SOLARIS
1174 # define __INBCAST(S) (char const ** __restrict__)n_UNCONST(S)
1175 # endif
1176 # ifndef __INBCAST
1177 # define __INBCAST(S) (char **)n_UNCONST(S)
1178 # endif
1180 FL int
1181 n_iconv_buf(iconv_t cd, enum n_iconv_flags icf,
1182 char const **inb, size_t *inbleft, char **outb, size_t *outbleft){
1183 int err;
1184 NYD2_ENTER;
1186 if((icf & n_ICONV_UNIREPL) && !(n_psonce & n_PSO_UNICODE))
1187 icf &= ~n_ICONV_UNIREPL;
1189 for(;;){
1190 size_t sz;
1192 sz = iconv(cd, __INBCAST(inb), inbleft, outb, outbleft);
1193 if(sz > 0 && !(icf & n_ICONV_IGN_NOREVERSE)){
1194 err = n_ERR_NOENT;
1195 goto jleave;
1197 if(sz != (size_t)-1)
1198 break;
1200 err = n_err_no;
1201 if(!(icf & n_ICONV_IGN_ILSEQ) || err != n_ERR_ILSEQ)
1202 goto jleave;
1203 if(*inbleft > 0){
1204 ++(*inb);
1205 --(*inbleft);
1206 if(icf & n_ICONV_UNIREPL){
1207 if(*outbleft >= sizeof(n_unirepl) -1){
1208 memcpy(*outb, n_unirepl, sizeof(n_unirepl) -1);
1209 *outb += sizeof(n_unirepl) -1;
1210 *outbleft -= sizeof(n_unirepl) -1;
1211 continue;
1213 }else if(*outbleft > 0){
1214 *(*outb)++ = '?';
1215 --*outbleft;
1216 continue;
1218 err = E2BIG;
1219 goto jleave;
1220 }else if(*outbleft > 0){
1221 **outb = '\0';
1222 goto jleave;
1225 err = 0;
1226 jleave:
1227 NYD2_LEAVE;
1228 return err;
1230 # undef __INBCAST
1232 FL int
1233 n_iconv_str(iconv_t cd, enum n_iconv_flags icf,
1234 struct str *out, struct str const *in, struct str *in_rest_or_null)
1236 int err;
1237 char *obb, *ob;
1238 char const *ib;
1239 size_t olb, ol, il;
1240 NYD2_ENTER;
1242 obb = out->s;
1243 olb = out->l;
1244 ol = in->l;
1246 ol = (ol << 1) - (ol >> 4);
1247 if (olb <= ol) {
1248 olb = ol;
1249 goto jrealloc;
1252 for (;;) {
1253 ib = in->s;
1254 il = in->l;
1255 ob = obb;
1256 ol = olb;
1257 if((err = n_iconv_buf(cd, icf, &ib, &il, &ob, &ol)) == 0 || err != E2BIG)
1258 break;
1259 olb += in->l;
1260 jrealloc:
1261 obb = n_realloc(obb, olb +1);
1264 if (in_rest_or_null != NULL) {
1265 in_rest_or_null->s = n_UNCONST(ib);
1266 in_rest_or_null->l = il;
1268 out->s = obb;
1269 out->s[out->l = olb - ol] = '\0';
1270 NYD2_LEAVE;
1271 return err;
1274 FL char *
1275 n_iconv_onetime_cp(enum n_iconv_flags icf,
1276 char const *tocode, char const *fromcode, char const *input){
1277 struct str out, in;
1278 iconv_t icd;
1279 char *rv;
1280 NYD2_ENTER;
1282 rv = NULL;
1283 if(tocode == NULL)
1284 tocode = ok_vlook(ttycharset);
1285 if(fromcode == NULL)
1286 fromcode = "utf-8";
1288 if((icd = iconv_open(tocode, fromcode)) == (iconv_t)-1)
1289 goto jleave;
1291 in.l = strlen(in.s = n_UNCONST(input)); /* logical */
1292 out.s = NULL, out.l = 0;
1293 if(!n_iconv_str(icd, icf, &out, &in, NULL))
1294 rv = savestrbuf(out.s, out.l);
1295 if(out.s != NULL)
1296 free(out.s);
1298 iconv_close(icd);
1299 jleave:
1300 NYD2_LEAVE;
1301 return rv;
1303 #endif /* HAVE_ICONV */
1305 /* s-it-mode */