nail.h: resort a bit
[s-mailx.git] / strings.c
blob3e31ebedeba28d8de8a7b7e949b2625650323d78
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Auto-reclaimed string allocation and support routines that build on top of
3 *@ them. Strings handed out by those are reclaimed at the top of the command
4 *@ loop each time, so they need not be freed.
5 *@ And below this series we do collect all other plain string support routines
6 *@ in here, including those which use normal heap memory.
8 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
9 * Copyright (c) 2012 - 2013 Steffen "Daode" Nurpmeso <sdaoden@users.sf.net>.
12 * Copyright (c) 1980, 1993
13 * The Regents of the University of California. All rights reserved.
15 * Redistribution and use in source and binary forms, with or without
16 * modification, are permitted provided that the following conditions
17 * are met:
18 * 1. Redistributions of source code must retain the above copyright
19 * notice, this list of conditions and the following disclaimer.
20 * 2. Redistributions in binary form must reproduce the above copyright
21 * notice, this list of conditions and the following disclaimer in the
22 * documentation and/or other materials provided with the distribution.
23 * 3. All advertising materials mentioning features or use of this software
24 * must display the following acknowledgement:
25 * This product includes software developed by the University of
26 * California, Berkeley and its contributors.
27 * 4. Neither the name of the University nor the names of its contributors
28 * may be used to endorse or promote products derived from this software
29 * without specific prior written permission.
31 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
32 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
33 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
34 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
35 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
36 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
37 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
38 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
39 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
40 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
41 * SUCH DAMAGE.
44 #include "nail.h"
46 #include <ctype.h>
49 * Allocate SBUFFER_SIZE chunks and keep them in a singly linked list, but
50 * release all except the first two in sreset(), because other allocations are
51 * performed and the underlaying allocator should have the possibility to
52 * reorder stuff and possibly even madvise(2), so that S-nail(1) integrates
53 * neatly into the system.
54 * To relax stuff further, especially in non-interactive, i.e., send mode, do
55 * not even allocate the first buffer, but let that be a builtin DATA section
56 * one that is rather small, yet sufficient for send mode to *never* even
57 * perform a single dynamic allocation (from our stringdope point of view).
60 union __align__ {
61 char *cp;
62 size_t sz;
63 ul_it ul;
65 #define SALIGN (sizeof(union __align__) - 1)
67 CTA(ISPOW2(SALIGN + 1));
69 struct b_base {
70 struct buffer *_next;
71 char *_bot; /* For spreserve() */
72 char *_max; /* Max usable byte */
73 char *_caster; /* NULL if full */
76 /* Single instance builtin buffer, DATA */
77 struct b_bltin {
78 struct b_base b_base;
79 char b_buf[SBUFFER_BUILTIN - sizeof(struct b_base)];
81 #define SBLTIN_SIZE SIZEOF_FIELD(struct b_bltin, b_buf)
83 /* Dynamically allocated buffers */
84 struct b_dyn {
85 struct b_base b_base;
86 char b_buf[SBUFFER_SIZE - sizeof(struct b_base)];
88 #define SDYN_SIZE SIZEOF_FIELD(struct b_dyn, b_buf)
90 struct buffer {
91 struct b_base b;
92 char b_buf[VFIELD_SIZE(SALIGN + 1)];
95 static struct b_bltin _builtin_buf;
96 static struct buffer *_buf_head, *_buf_list, *_buf_server;
98 #ifdef HAVE_ASSERTS
99 size_t _all_cnt, _all_cycnt, _all_cycnt_max,
100 _all_size, _all_cysize, _all_cysize_max, _all_min, _all_max, _all_wast,
101 _all_bufcnt, _all_cybufcnt, _all_cybufcnt_max,
102 _all_resetreqs, _all_resets;
103 #endif
106 * Allocate size more bytes of space and return the address of the
107 * first byte to the caller. An even number of bytes are always
108 * allocated so that the space will always be on a word boundary.
110 void *
111 salloc(size_t size)
113 #ifdef HAVE_ASSERTS
114 size_t orig_size = size;
115 #endif
116 union {struct buffer *b; char *cp;} u;
117 char *x, *y, *z;
119 if (size == 0)
120 ++size;
121 size += SALIGN;
122 size &= ~SALIGN;
124 #ifdef HAVE_ASSERTS
125 ++_all_cnt;
126 ++_all_cycnt;
127 _all_cycnt_max = MAX(_all_cycnt_max, _all_cycnt);
128 _all_size += size;
129 _all_cysize += size;
130 _all_cysize_max = MAX(_all_cysize_max, _all_cysize);
131 _all_min = _all_max == 0 ? size : MIN(_all_min, size);
132 _all_max = MAX(_all_max, size);
133 _all_wast += size - orig_size;
134 #endif
136 if ((u.b = _buf_server) != NULL)
137 goto jumpin;
138 jredo:
139 for (u.b = _buf_head; u.b != NULL; u.b = u.b->b._next) {
140 jumpin: x = u.b->b._caster;
141 if (x == NULL) {
142 if (u.b == _buf_server) {
143 if (u.b == _buf_head &&
144 (u.b = _buf_head->b._next)
145 != NULL) {
146 _buf_server = u.b;
147 goto jumpin;
149 _buf_server = NULL;
150 goto jredo;
152 continue;
154 y = x + size;
155 z = u.b->b._max;
156 if (y <= z) {
158 * Alignment is the one thing, the other is what is
159 * usually allocated, and here about 40 bytes seems to
160 * be a good cut to avoid non-usable non-NULL casters
162 u.b->b._caster = (y + 42+16 >= z) ? NULL : y;
163 u.cp = x;
164 goto jleave;
168 if (_buf_head == NULL) {
169 struct b_bltin *b = &_builtin_buf;
170 b->b_base._max = b->b_buf + sizeof(b->b_buf) - 1;
171 _buf_head = (struct buffer*)b;
172 u.b = _buf_head;
173 } else {
174 #ifdef HAVE_ASSERTS
175 ++_all_bufcnt;
176 ++_all_cybufcnt;
177 _all_cybufcnt_max = MAX(_all_cybufcnt_max, _all_cybufcnt);
178 #endif
179 u.b = (struct buffer*)smalloc(sizeof(struct b_dyn));
180 u.b->b._max = u.b->b_buf + SDYN_SIZE - 1;
182 if (_buf_list != NULL)
183 _buf_list->b._next = u.b;
184 _buf_server = _buf_list = u.b;
185 u.b->b._next = NULL;
186 u.b->b._caster = (u.b->b._bot = u.b->b_buf) + size;
187 u.cp = u.b->b._bot;
188 jleave:
189 return u.cp;
192 void *
193 csalloc(size_t nmemb, size_t size)
195 void *vp;
197 size *= nmemb;
198 vp = salloc(size);
199 memset(vp, 0, size);
200 return (vp);
204 * Reset the string area to be empty.
205 * Called to free all strings allocated since last reset.
207 void
208 sreset(void)
210 struct buffer *bh;
212 #ifdef HAVE_ASSERTS
213 ++_all_resetreqs;
214 #endif
215 if (noreset)
216 goto jleave;
218 #ifdef HAVE_ASSERTS
219 _all_cycnt = _all_cysize = 0;
220 _all_cybufcnt = (_buf_head != NULL && _buf_head->b._next != NULL);
221 ++_all_resets;
222 #endif
224 if ((bh = _buf_head) != NULL) {
225 struct buffer *b = bh;
226 b->b._caster = b->b._bot;
227 #ifdef HAVE_ASSERTS
228 memset(b->b._caster, 0377,
229 (size_t)(b->b._max - b->b._caster));
230 #endif
231 _buf_server = b;
232 if ((bh = bh->b._next) != NULL) {
233 b = bh;
234 b->b._caster = b->b._bot;
235 #ifdef HAVE_ASSERTS
236 memset(b->b._caster, 0377,
237 (size_t)(b->b._max - b->b._caster));
238 #endif
239 for (bh = bh->b._next; bh != NULL;) {
240 struct buffer *b2 = bh->b._next;
241 free(bh);
242 bh = b2;
245 _buf_list = b;
246 b->b._next = NULL;
249 #ifdef HAVE_ASSERTS
250 smemreset();
251 #endif
252 jleave: ;
256 * Make the string area permanent.
257 * Meant to be called in main, after initialization.
259 void
260 spreserve(void)
262 struct buffer *b;
264 for (b = _buf_head; b != NULL; b = b->b._next)
265 b->b._bot = b->b._caster;
268 #ifdef HAVE_ASSERTS
270 sstats(void *v)
272 (void)v;
273 printf("String usage statistics (cycle means one sreset() cycle):\n"
274 " Buffer allocs ever/max simultan. : %lu/%lu\n"
275 " Buffer size of builtin(1)/dynamic: %lu/%lu\n"
276 " Overall alloc count/bytes : %lu/%lu\n"
277 " Alloc bytes min/max/align wastage: %lu/%lu/%lu\n"
278 " sreset() cycles : %lu (%lu performed)\n"
279 " Cycle maximums: alloc count/bytes: %lu/%lu\n",
280 (ul_it)_all_bufcnt, (ul_it)_all_cybufcnt_max,
281 (ul_it)SBLTIN_SIZE, (ul_it)SDYN_SIZE,
282 (ul_it)_all_cnt, (ul_it)_all_size,
283 (ul_it)_all_min, (ul_it)_all_max, (ul_it)_all_wast,
284 (ul_it)_all_resetreqs, (ul_it)_all_resets,
285 (ul_it)_all_cycnt_max, (ul_it)_all_cysize_max);
286 return (0);
288 #endif
291 * Return a pointer to a dynamic copy of the argument.
293 char *
294 savestr(const char *str)
296 size_t size = strlen(str) + 1;
297 char *news = salloc(size);
298 memcpy(news, str, size);
299 return (news);
303 * Return new string copy of a non-terminated argument.
305 char *
306 savestrbuf(const char *sbuf, size_t sbuf_len)
308 char *news = salloc(sbuf_len + 1);
309 memcpy(news, sbuf, sbuf_len);
310 news[sbuf_len] = 0;
311 return (news);
315 * Make a copy of new argument incorporating old one.
317 char *
318 save2str(const char *str, const char *old)
320 size_t newsize = strlen(str) + 1, oldsize = old ? strlen(old) + 1 : 0;
321 char *news = salloc(newsize + oldsize);
322 if (oldsize) {
323 memcpy(news, old, oldsize);
324 news[oldsize - 1] = ' ';
326 memcpy(news + oldsize, str, newsize);
327 return (news);
330 char *
331 savecat(char const *s1, char const *s2)
333 size_t l1 = strlen(s1), l2 = strlen(s2);
334 char *news = salloc(l1 + l2 + 1);
335 memcpy(news + 0, s1, l1);
336 memcpy(news + l1, s2, l2);
337 news[l1 + l2] = '\0';
338 return (news);
342 * Support routines, auto-reclaimed storage
345 char *
346 i_strdup(char const *src)
348 size_t sz;
349 char *dest;
351 sz = strlen(src) + 1;
352 dest = salloc(sz);
353 i_strcpy(dest, src, sz);
354 return (dest);
357 char *
358 protbase(char const *cp)
360 char *n = salloc(strlen(cp) + 1), *np = n;
362 /* Just ignore the `is-system-mailbox' prefix XXX */
363 if (cp[0] == '%' && cp[1] == ':')
364 cp += 2;
366 while (*cp) {
367 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
368 *np++ = *cp++;
369 *np++ = *cp++;
370 *np++ = *cp++;
371 } else if (cp[0] == '/')
372 break;
373 else
374 *np++ = *cp++;
376 *np = '\0';
377 return n;
380 char *
381 urlxenc(char const *cp) /* XXX */
383 char *n, *np;
385 np = n = salloc(strlen(cp) * 3 + 1);
386 while (*cp) {
387 if (alnumchar(*cp) || *cp == '_' || *cp == '@' ||
388 (np > n && (*cp == '.' || *cp == '-' ||
389 *cp == ':')))
390 *np++ = *cp;
391 else {
392 *np++ = '%';
393 *np++ = Hexchar((*cp&0xf0) >> 4);
394 *np++ = Hexchar(*cp&0x0f);
396 cp++;
398 *np = '\0';
399 return n;
402 char *
403 urlxdec(char const *cp) /* XXX */
405 char *n, *np;
407 np = n = salloc(strlen(cp) + 1);
408 while (*cp) {
409 if (cp[0] == '%' && cp[1] && cp[2]) {
410 *np = (int)(cp[1]>'9'?cp[1]-'A'+10:cp[1]-'0') << 4;
411 *np++ |= cp[2]>'9'?cp[2]-'A'+10:cp[2]-'0';
412 cp += 3;
413 } else
414 *np++ = *cp++;
416 *np = '\0';
417 return (n);
420 struct str *
421 str_concat_csvl(struct str *self, ...) /* XXX onepass maybe better here */
423 va_list vl;
424 size_t l;
425 char const *cs;
427 va_start(vl, self);
428 for (l = 0; (cs = va_arg(vl, char const*)) != NULL;)
429 l += strlen(cs);
430 va_end(vl);
432 self->l = l;
433 self->s = salloc(l + 1);
435 va_start(vl, self);
436 for (l = 0; (cs = va_arg(vl, char const*)) != NULL;) {
437 size_t i = strlen(cs);
438 memcpy(self->s + l, cs, i);
439 l += i;
441 self->s[l] = '\0';
442 va_end(vl);
443 return self;
446 struct str *
447 str_concat_cpa(struct str *self, char const *const*cpa, char const *sep_o_null)
449 size_t sonl, l;
450 char const *const*xcpa;
452 sonl = (sep_o_null != NULL) ? strlen(sep_o_null) : 0;
454 for (l = 0, xcpa = cpa; *xcpa != NULL; ++xcpa)
455 l += strlen(*xcpa) + sonl;
457 self->l = l;
458 self->s = salloc(l + 1);
460 for (l = 0, xcpa = cpa; *xcpa != NULL; ++xcpa) {
461 size_t i = strlen(*xcpa);
462 memcpy(self->s + l, *xcpa, i);
463 l += i;
464 if (sonl > 0) {
465 memcpy(self->s + l, sep_o_null, sonl);
466 l += sonl;
469 self->s[l] = '\0';
470 return self;
474 * Routines that are not related to auto-reclaimed storage follow.
477 uc_it const class_char[] = {
478 /* 000 nul 001 soh 002 stx 003 etx 004 eot 005 enq 006 ack 007 bel */
479 C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,
480 /* 010 bs 011 ht 012 nl 013 vt 014 np 015 cr 016 so 017 si */
481 C_CNTRL,C_BLANK,C_WHITE,C_SPACE,C_SPACE,C_SPACE,C_CNTRL,C_CNTRL,
482 /* 020 dle 021 dc1 022 dc2 023 dc3 024 dc4 025 nak 026 syn 027 etb */
483 C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,
484 /* 030 can 031 em 032 sub 033 esc 034 fs 035 gs 036 rs 037 us */
485 C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,
486 /* 040 sp 041 ! 042 " 043 # 044 $ 045 % 046 & 047 ' */
487 C_BLANK,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,
488 /* 050 ( 051 ) 052 * 053 + 054 , 055 - 056 . 057 / */
489 C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,
490 /* 060 0 061 1 062 2 063 3 064 4 065 5 066 6 067 7 */
491 C_OCTAL,C_OCTAL,C_OCTAL,C_OCTAL,C_OCTAL,C_OCTAL,C_OCTAL,C_OCTAL,
492 /* 070 8 071 9 072 : 073 ; 074 < 075 = 076 > 077 ? */
493 C_DIGIT,C_DIGIT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,
494 /* 100 @ 101 A 102 B 103 C 104 D 105 E 106 F 107 G */
495 C_PUNCT,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,
496 /* 110 H 111 I 112 J 113 K 114 L 115 M 116 N 117 O */
497 C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,
498 /* 120 P 121 Q 122 R 123 S 124 T 125 U 126 V 127 W */
499 C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,
500 /* 130 X 131 Y 132 Z 133 [ 134 \ 135 ] 136 ^ 137 _ */
501 C_UPPER,C_UPPER,C_UPPER,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,
502 /* 140 ` 141 a 142 b 143 c 144 d 145 e 146 f 147 g */
503 C_PUNCT,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,
504 /* 150 h 151 i 152 j 153 k 154 l 155 m 156 n 157 o */
505 C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,
506 /* 160 p 161 q 162 r 163 s 164 t 165 u 166 v 167 w */
507 C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,
508 /* 170 x 171 y 172 z 173 { 174 | 175 } 176 ~ 177 del */
509 C_LOWER,C_LOWER,C_LOWER,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_CNTRL
513 anyof(char const *s1, char const *s2)
515 for (; *s1 != '\0'; ++s1)
516 if (strchr(s2, *s1))
517 break;
518 return (*s1 != '\0');
521 ui_it
522 strhash(char const *name)
524 ui_it h = 0;
526 while (*name != '\0') {
527 h *= 33;
528 h += *name++;
530 return h;
533 char *
534 strcomma(char **iolist, int ignore_empty)
536 char *base, *cp;
538 for (base = *iolist; base != NULL; base = *iolist) {
539 while (*base != '\0' && blankspacechar(*base))
540 ++base;
541 cp = strchr(base, ',');
542 if (cp != NULL)
543 *iolist = cp + 1;
544 else {
545 *iolist = NULL;
546 cp = base + strlen(base);
548 while (cp > base && blankspacechar(cp[-1]))
549 --cp;
550 *cp = '\0';
551 if (*base != '\0' || ! ignore_empty)
552 break;
554 return (base);
557 void
558 i_strcpy(char *dest, const char *src, size_t size)
560 if (size > 0) {
561 for (;; ++dest, ++src)
562 if ((*dest = lowerconv(*src)) == '\0') {
563 break;
564 } else if (--size == 0) {
565 *dest = '\0';
566 break;
572 is_prefix(char const *as1, char const *as2)
574 char c;
575 for (; (c = *as1) == *as2 && c != '\0'; ++as1, ++as2)
576 if ((c = *as2) == '\0')
577 break;
578 return (c == '\0');
581 char const *
582 last_at_before_slash(char const *sp)
584 char const *cp;
585 char c;
587 for (cp = sp; (c = *cp) != '\0'; ++cp)
588 if (c == '/')
589 break;
590 while (cp > sp && *--cp != '@')
592 return (*cp == '@' ? cp : NULL);
595 void
596 makelow(char *cp) /* TODO isn't that crap? --> */
598 #if defined HAVE_MBTOWC && defined HAVE_WCTYPE_H
599 if (mb_cur_max > 1) {
600 char *tp = cp;
601 wchar_t wc;
602 int len;
604 while (*cp) {
605 len = mbtowc(&wc, cp, mb_cur_max);
606 if (len < 0)
607 *tp++ = *cp++;
608 else {
609 wc = towlower(wc);
610 if (wctomb(tp, wc) == len)
611 tp += len, cp += len;
612 else
613 *tp++ = *cp++; /* <-- at least here */
616 } else
617 #endif
620 *cp = tolower((uc_it)*cp);
621 while (*cp++);
626 substr(char const *str, char const *sub)
628 char const *cp, *backup;
630 cp = sub;
631 backup = str;
632 while (*str && *cp) {
633 #if defined HAVE_MBTOWC && defined HAVE_WCTYPE_H
634 if (mb_cur_max > 1) {
635 wchar_t c, c2;
636 int sz;
638 if ((sz = mbtowc(&c, cp, mb_cur_max)) < 0)
639 goto singlebyte;
640 cp += sz;
641 if ((sz = mbtowc(&c2, str, mb_cur_max)) < 0)
642 goto singlebyte;
643 str += sz;
644 c = towupper(c);
645 c2 = towupper(c2);
646 if (c != c2) {
647 if ((sz = mbtowc(&c, backup, mb_cur_max)) > 0) {
648 backup += sz;
649 str = backup;
650 } else
651 str = ++backup;
652 cp = sub;
654 } else
655 #endif
657 int c, c2;
659 #if defined HAVE_MBTOWC && defined HAVE_WCTYPE_H
660 singlebyte:
661 #endif
662 c = *cp++ & 0377;
663 if (islower(c))
664 c = toupper(c);
665 c2 = *str++ & 0377;
666 if (islower(c2))
667 c2 = toupper(c2);
668 if (c != c2) {
669 str = ++backup;
670 cp = sub;
674 return *cp == '\0';
677 #ifndef HAVE_SNPRINTF
679 snprintf(char *str, size_t size, const char *format, ...) /* XXX DANGER! */
681 va_list ap;
682 int ret;
684 va_start(ap, format);
685 ret = vsprintf(str, format, ap);
686 va_end(ap);
687 if (ret < 0)
688 ret = strlen(str);
689 return ret;
691 #endif
693 char *
694 sstpcpy(char *dst, char const *src)
696 while ((*dst = *src++) != '\0')
697 dst++;
698 return (dst);
701 char *
702 (sstrdup)(char const *cp SMALLOC_DEBUG_ARGS)
704 char *dp = NULL;
706 if (cp != NULL) {
707 size_t l = strlen(cp) + 1;
708 dp = (smalloc)(l SMALLOC_DEBUG_ARGSCALL);
709 memcpy(dp, cp, l);
711 return dp;
714 char *
715 (sbufdup)(char const *cp, size_t len SMALLOC_DEBUG_ARGS)
717 char *dp = NULL;
719 dp = (smalloc)(len + 1 SMALLOC_DEBUG_ARGSCALL);
720 if (cp != NULL)
721 memcpy(dp, cp, len);
722 dp[len] = '\0';
723 return dp;
727 asccasecmp(char const *s1, char const *s2)
729 int cmp;
731 for (;;) {
732 char c1 = *s1++, c2 = *s2++;
733 if ((cmp = lowerconv(c1) - lowerconv(c2)) != 0 || c1 == '\0')
734 break;
736 return cmp;
740 ascncasecmp(char const *s1, char const *s2, size_t sz)
742 int cmp = 0;
744 while (sz-- > 0) {
745 char c1 = *s1++, c2 = *s2++;
746 if ((cmp = lowerconv(c1) - lowerconv(c2)) != 0 || c1 == '\0')
747 break;
749 return cmp;
752 char const *
753 asccasestr(char const *haystack, char const *xneedle)
755 char *needle = NULL, *NEEDLE;
756 size_t i, sz;
758 sz = strlen(xneedle);
759 if (sz == 0)
760 goto jleave;
762 needle = ac_alloc(sz);
763 NEEDLE = ac_alloc(sz);
764 for (i = 0; i < sz; i++) {
765 needle[i] = lowerconv(xneedle[i]);
766 NEEDLE[i] = upperconv(xneedle[i]);
769 while (*haystack) {
770 if (*haystack == *needle || *haystack == *NEEDLE) {
771 for (i = 1; i < sz; i++)
772 if (haystack[i] != needle[i] &&
773 haystack[i] != NEEDLE[i])
774 break;
775 if (i == sz)
776 goto jleave;
778 haystack++;
780 haystack = NULL;
781 jleave:
782 if (needle != NULL) {
783 ac_free(NEEDLE);
784 ac_free(needle);
786 return haystack;
789 bool_t
790 is_asccaseprefix(char const *as1, char const *as2)
792 bool_t rv = FAL0;
794 for (;; ++as1, ++as2) {
795 char c1 = lowerconv(*as1), c2 = lowerconv(*as2);
796 if ((rv = (c1 == '\0')))
797 break;
798 if (c1 != c2 || c2 == '\0')
799 break;
801 return rv;
804 struct str *
805 (n_str_dup)(struct str *self, struct str const *t SMALLOC_DEBUG_ARGS)
807 if (t != NULL && t->l > 0) {
808 self->l = t->l;
809 self->s = (srealloc)(self->s, t->l + 1 SMALLOC_DEBUG_ARGSCALL);
810 memcpy(self->s, t->s, t->l);
811 } else
812 self->l = 0;
813 return self;
816 struct str *
817 (n_str_add_buf)(struct str *self, char const *buf, size_t buflen
818 SMALLOC_DEBUG_ARGS)
820 if (buflen != 0) {
821 size_t sl = self->l;
822 self->l = sl + buflen;
823 self->s = (srealloc)(self->s, self->l+1 SMALLOC_DEBUG_ARGSCALL);
824 memcpy(self->s + sl, buf, buflen);
826 return self;
829 #ifdef HAVE_ICONV
830 static void _ic_toupper(char *dest, char const *src);
831 static void _ic_stripdash(char *p);
833 static void
834 _ic_toupper(char *dest, const char *src)
837 *dest++ = upperconv(*src);
838 while (*src++);
841 static void
842 _ic_stripdash(char *p)
844 char *q = p;
847 if (*(q = p) != '-')
848 q++;
849 while (*p++);
852 iconv_t
853 n_iconv_open(char const *tocode, char const *fromcode)
855 iconv_t id;
856 char *t, *f;
858 if ((id = iconv_open(tocode, fromcode)) != (iconv_t)-1)
859 return id;
862 * Remove the "iso-" prefixes for Solaris.
864 if (ascncasecmp(tocode, "iso-", 4) == 0)
865 tocode += 4;
866 else if (ascncasecmp(tocode, "iso", 3) == 0)
867 tocode += 3;
868 if (ascncasecmp(fromcode, "iso-", 4) == 0)
869 fromcode += 4;
870 else if (ascncasecmp(fromcode, "iso", 3) == 0)
871 fromcode += 3;
872 if (*tocode == '\0' || *fromcode == '\0')
873 return (iconv_t) -1;
874 if ((id = iconv_open(tocode, fromcode)) != (iconv_t)-1)
875 return id;
877 * Solaris prefers upper-case charset names. Don't ask...
879 t = salloc(strlen(tocode) + 1);
880 _ic_toupper(t, tocode);
881 f = salloc(strlen(fromcode) + 1);
882 _ic_toupper(f, fromcode);
883 if ((id = iconv_open(t, f)) != (iconv_t)-1)
884 return id;
886 * Strip dashes for UnixWare.
888 _ic_stripdash(t);
889 _ic_stripdash(f);
890 if ((id = iconv_open(t, f)) != (iconv_t)-1)
891 return id;
893 * Add your vendor's sillynesses here.
897 * If the encoding names are equal at this point, they
898 * are just not understood by iconv(), and we cannot
899 * sensibly use it in any way. We do not perform this
900 * as an optimization above since iconv() can otherwise
901 * be used to check the validity of the input even with
902 * identical encoding names.
904 if (strcmp(t, f) == 0)
905 errno = 0;
906 return (iconv_t)-1;
909 void
910 n_iconv_close(iconv_t cd)
912 iconv_close(cd);
913 if (cd == iconvd)
914 iconvd = (iconv_t)-1;
917 void
918 n_iconv_reset(iconv_t cd)
920 (void)iconv(cd, NULL, NULL, NULL, NULL);
924 * (2012-09-24: export and use it exclusively to isolate prototype problems
925 * (*inb* is 'char const **' except in POSIX) in a single place.
926 * GNU libiconv even allows for configuration time const/non-const..
927 * In the end it's an ugly guess, but we can't do better since make(1) doesn't
928 * support compiler invocations which bail on error, so no -Werror.
930 /* Citrus project? */
931 # if defined _ICONV_H_ && defined __ICONV_F_HIDE_INVALID
932 /* DragonFly 3.2.1 is special */
933 # ifdef __DragonFly__
934 # define __INBCAST(S) (char ** __restrict__)UNCONST(S)
935 # else
936 # define __INBCAST(S) (char const **)UNCONST(S)
937 # endif
938 # endif
939 # ifndef __INBCAST
940 # define __INBCAST(S) (char **)UNCONST(S)
941 # endif
944 n_iconv_buf(iconv_t cd, char const **inb, size_t *inbleft,/*XXX redo iconv use*/
945 char **outb, size_t *outbleft, bool_t skipilseq)
947 int err = 0;
949 for (;;) {
950 size_t sz = iconv(cd, __INBCAST(inb), inbleft, outb, outbleft);
951 if (sz != (size_t)-1)
952 break;
953 err = errno;
954 if (! skipilseq || err != EILSEQ)
955 break;
956 if (*inbleft > 0) {
957 ++(*inb);
958 --(*inbleft);
959 } else if (*outbleft > 0) {
960 **outb = '\0';
961 break;
963 if (*outbleft > 0/* TODO 0xFFFD 2*/) {
964 /* TODO 0xFFFD (*outb)[0] = '[';
965 * TODO (*outb)[1] = '?';
966 * TODO 0xFFFD (*outb)[2] = ']';
967 * TODO (*outb) += 3;
968 * TODO (*outbleft) -= 3; */
969 *(*outb)++ = '?';
970 --*outbleft;
971 } else {
972 err = E2BIG;
973 break;
975 err = 0;
977 return err;
979 # undef __INBCAST
982 n_iconv_str(iconv_t cd, struct str *out, struct str const *in,
983 struct str *in_rest_or_null, bool_t skipilseq)
985 int err = 0;
986 char *obb = out->s, *ob;
987 char const *ib;
988 size_t olb = out->l, ol, il;
990 ol = in->l;
991 ol = (ol << 1) - (ol >> 4);
992 if (olb < ol) {
993 olb = ol;
994 goto jrealloc;
997 for (;;) {
998 ib = in->s;
999 il = in->l;
1000 ob = obb;
1001 ol = olb;
1002 err = n_iconv_buf(cd, &ib, &il, &ob, &ol, skipilseq);
1003 if (err == 0 || err != E2BIG)
1004 break;
1005 err = 0;
1006 olb += in->l;
1007 jrealloc: obb = srealloc(obb, olb);
1010 if (in_rest_or_null != NULL) {
1011 in_rest_or_null->s = UNCONST(ib);
1012 in_rest_or_null->l = il;
1014 out->s = obb;
1015 out->l = olb - ol;
1016 return err;
1018 #endif /* HAVE_ICONV */