Merge branch 'topic/nyd'
[s-mailx.git] / strings.c
blobb58ea7f405e2d39c6e8948fc44e7f1196ea08b8e
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 - 2014 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 #ifndef HAVE_AMALGAMATION
45 # include "nail.h"
46 #endif
48 #include <ctype.h>
50 /* Allocate SBUFFER_SIZE chunks and keep them in a singly linked list, but
51 * release all except the first two in sreset(), because other allocations are
52 * performed and the underlaying allocator should have the possibility to
53 * reorder stuff and possibly even madvise(2), so that S-nail(1) integrates
54 * neatly into the system.
55 * To relax stuff further, especially in non-interactive, i.e., send mode, do
56 * not even allocate the first buffer, but let that be a builtin DATA section
57 * one that is rather small, yet sufficient for send mode to *never* even
58 * perform a single dynamic allocation (from our stringdope point of view).
59 * Encapsulate user chunks with canaries if HAVE_DEBUG */
61 #ifdef HAVE_DEBUG
62 # define _SHOPE_SIZE (2u * 8 * sizeof(char) + sizeof(struct schunk))
64 CTA(sizeof(char) == sizeof(ui8_t));
66 struct schunk {
67 char const *file;
68 ui32_t line;
69 ui16_t usr_size;
70 ui16_t full_size;
73 union sptr {
74 void *p;
75 struct schunk *c;
76 char *cp;
77 ui8_t *ui8p;
79 #endif /* HAVE_DEBUG */
81 union __align__ {
82 char *cp;
83 size_t sz;
84 ul_it ul;
86 #define SALIGN (sizeof(union __align__) - 1)
88 CTA(ISPOW2(SALIGN + 1));
90 struct b_base {
91 struct buffer *_next;
92 char *_bot; /* For spreserve() */
93 char *_relax; /* If !NULL, used by srelax() instead of ._bot */
94 char *_max; /* Max usable byte */
95 char *_caster; /* NULL if full */
98 /* Single instance builtin buffer, DATA */
99 struct b_bltin {
100 struct b_base b_base;
101 char b_buf[SBUFFER_BUILTIN - sizeof(struct b_base)];
103 #define SBLTIN_SIZE SIZEOF_FIELD(struct b_bltin, b_buf)
105 /* Dynamically allocated buffers */
106 struct b_dyn {
107 struct b_base b_base;
108 char b_buf[SBUFFER_SIZE - sizeof(struct b_base)];
110 #define SDYN_SIZE SIZEOF_FIELD(struct b_dyn, b_buf)
112 struct buffer {
113 struct b_base b;
114 char b_buf[VFIELD_SIZE(SALIGN + 1)];
117 static struct b_bltin _builtin_buf;
118 static struct buffer *_buf_head, *_buf_list, *_buf_server, *_buf_relax;
119 #ifdef HAVE_DEBUG
120 static size_t _all_cnt, _all_cycnt, _all_cycnt_max,
121 _all_size, _all_cysize, _all_cysize_max, _all_min,
122 _all_max, _all_wast,
123 _all_bufcnt, _all_cybufcnt, _all_cybufcnt_max,
124 _all_resetreqs, _all_resets;
125 #endif
127 /* sreset() / srelax() release a buffer, check the canaries of all chunks */
128 #ifdef HAVE_DEBUG
129 static void _salloc_bcheck(struct buffer *b);
130 #endif
132 #ifdef HAVE_DEBUG
133 static void
134 _salloc_bcheck(struct buffer *b)
136 union sptr pmax, pp;
137 /*NYD_ENTER;*/
139 pmax.cp = (b->b._caster == NULL) ? b->b._max : b->b._caster;
140 pp.cp = b->b._bot;
142 while (PTRCMP(pp.cp, <, pmax.cp)) {
143 struct schunk *c;
144 union sptr x;
145 void *ux;
146 ui8_t i;
148 c = pp.c;
149 pp.cp += c->full_size;
150 x.p = c + 1;
151 ux = x.cp + 8;
153 i = 0;
154 if (x.ui8p[0] != 0xDE) i |= 1<<0;
155 if (x.ui8p[1] != 0xAA) i |= 1<<1;
156 if (x.ui8p[2] != 0x55) i |= 1<<2;
157 if (x.ui8p[3] != 0xAD) i |= 1<<3;
158 if (x.ui8p[4] != 0xBE) i |= 1<<4;
159 if (x.ui8p[5] != 0x55) i |= 1<<5;
160 if (x.ui8p[6] != 0xAA) i |= 1<<6;
161 if (x.ui8p[7] != 0xEF) i |= 1<<7;
162 if (i != 0)
163 alert("sdope %p: corrupt lower canary: 0x%02X, size %u: %s, line %u",
164 ux, i, c->usr_size, c->file, c->line);
165 x.cp += 8 + c->usr_size;
167 i = 0;
168 if (x.ui8p[0] != 0xDE) i |= 1<<0;
169 if (x.ui8p[1] != 0xAA) i |= 1<<1;
170 if (x.ui8p[2] != 0x55) i |= 1<<2;
171 if (x.ui8p[3] != 0xAD) i |= 1<<3;
172 if (x.ui8p[4] != 0xBE) i |= 1<<4;
173 if (x.ui8p[5] != 0x55) i |= 1<<5;
174 if (x.ui8p[6] != 0xAA) i |= 1<<6;
175 if (x.ui8p[7] != 0xEF) i |= 1<<7;
176 if (i != 0)
177 alert("sdope %p: corrupt upper canary: 0x%02X, size %u: %s, line %u",
178 ux, i, c->usr_size, c->file, c->line);
180 /*NYD_LEAVE;*/
182 #endif
184 FL void *
185 (salloc)(size_t size SALLOC_DEBUG_ARGS)
187 DBG( size_t orig_size = size; )
188 union {struct buffer *b; char *cp;} u;
189 char *x, *y, *z;
190 NYD_ENTER;
192 if (size == 0)
193 ++size;
194 size += SALIGN;
195 size &= ~SALIGN;
197 #ifdef HAVE_DEBUG
198 ++_all_cnt;
199 ++_all_cycnt;
200 _all_cycnt_max = MAX(_all_cycnt_max, _all_cycnt);
201 _all_size += size;
202 _all_cysize += size;
203 _all_cysize_max = MAX(_all_cysize_max, _all_cysize);
204 _all_min = (_all_max == 0) ? size : MIN(_all_min, size);
205 _all_max = MAX(_all_max, size);
206 _all_wast += size - orig_size;
208 size += _SHOPE_SIZE;
209 #endif
211 /* Search for a buffer with enough free space to serve request */
212 if ((u.b = _buf_server) != NULL)
213 goto jumpin;
214 jredo:
215 for (u.b = _buf_head; u.b != NULL; u.b = u.b->b._next) {
216 jumpin:
217 x = u.b->b._caster;
218 if (x == NULL) {
219 if (u.b == _buf_server) {
220 if (u.b == _buf_head && (u.b = _buf_head->b._next) != NULL) {
221 _buf_server = u.b;
222 goto jumpin;
224 _buf_server = NULL;
225 goto jredo;
227 continue;
229 y = x + size;
230 z = u.b->b._max;
231 if (PTRCMP(y, <=, z)) {
232 /* Alignment is the one thing, the other is what is usually allocated,
233 * and here about 40 bytes seems to be a good cut to avoid non-usable
234 * non-NULL casters. However, because of _salloc_bcheck(), we may not
235 * set ._caster to NULL because then it would check all chunks up to
236 * ._max, which surely doesn't work; speed is no issue with DEBUG */
237 u.b->b._caster = NDBG( PTRCMP(y + 42 + 16, >=, z) ? NULL : ) y;
238 u.cp = x;
239 goto jleave;
243 /* Need a new buffer */
244 if (_buf_head == NULL) {
245 struct b_bltin *b = &_builtin_buf;
246 b->b_base._max = b->b_buf + sizeof(b->b_buf) - 1;
247 _buf_head = (struct buffer*)b;
248 u.b = _buf_head;
249 } else {
250 #ifdef HAVE_DEBUG
251 ++_all_bufcnt;
252 ++_all_cybufcnt;
253 _all_cybufcnt_max = MAX(_all_cybufcnt_max, _all_cybufcnt);
254 #endif
255 u.b = smalloc(sizeof(struct b_dyn));
256 u.b->b._max = u.b->b_buf + SDYN_SIZE - 1;
258 if (_buf_list != NULL)
259 _buf_list->b._next = u.b;
260 _buf_server = _buf_list = u.b;
261 u.b->b._next = NULL;
262 u.b->b._caster = (u.b->b._bot = u.b->b_buf) + size;
263 u.b->b._relax = NULL;
264 u.cp = u.b->b._bot;
266 jleave:
267 /* Encapsulate user chunk in debug canaries */
268 #ifdef HAVE_DEBUG
270 union sptr xl, xu;
271 struct schunk *xc;
273 xl.p = u.cp;
274 xc = xl.c;
275 xc->file = mdbg_file;
276 xc->line = mdbg_line;
277 xc->usr_size = (ui16_t)orig_size;
278 xc->full_size = (ui16_t)size;
279 xl.p = xc + 1;
280 xl.ui8p[0]=0xDE; xl.ui8p[1]=0xAA; xl.ui8p[2]=0x55; xl.ui8p[3]=0xAD;
281 xl.ui8p[4]=0xBE; xl.ui8p[5]=0x55; xl.ui8p[6]=0xAA; xl.ui8p[7]=0xEF;
282 u.cp = xl.cp + 8;
283 xu.p = u.cp;
284 xu.cp += orig_size;
285 xu.ui8p[0]=0xDE; xu.ui8p[1]=0xAA; xu.ui8p[2]=0x55; xu.ui8p[3]=0xAD;
286 xu.ui8p[4]=0xBE; xu.ui8p[5]=0x55; xu.ui8p[6]=0xAA; xu.ui8p[7]=0xEF;
288 #endif
289 NYD_LEAVE;
290 return u.cp;
293 FL void *
294 (csalloc)(size_t nmemb, size_t size SALLOC_DEBUG_ARGS)
296 void *vp;
297 NYD_ENTER;
299 size *= nmemb;
300 vp = (salloc)(size SALLOC_DEBUG_ARGSCALL);
301 memset(vp, 0, size);
302 NYD_LEAVE;
303 return vp;
306 FL void
307 sreset(bool_t only_if_relaxed)
309 struct buffer *bh;
310 NYD_ENTER;
312 DBG( ++_all_resetreqs; )
313 if (noreset || (only_if_relaxed && _buf_relax == NULL))
314 goto jleave;
316 #ifdef HAVE_DEBUG
317 _all_cycnt = _all_cysize = 0;
318 _all_cybufcnt = (_buf_head != NULL && _buf_head->b._next != NULL);
319 ++_all_resets;
320 #endif
322 if ((bh = _buf_head) != NULL) {
323 struct buffer *b = bh;
324 DBG( _salloc_bcheck(b); )
325 b->b._caster = b->b._bot;
326 b->b._relax = NULL;
327 DBG( memset(b->b._caster, 0377, PTR2SIZE(b->b._max - b->b._caster)); )
328 _buf_server = b;
330 if ((bh = bh->b._next) != NULL) {
331 b = bh;
332 DBG( _salloc_bcheck(b); )
333 b->b._caster = b->b._bot;
334 b->b._relax = NULL;
335 DBG( memset(b->b._caster, 0377, PTR2SIZE(b->b._max - b->b._caster)); )
337 for (bh = bh->b._next; bh != NULL;) {
338 struct buffer *b2 = bh->b._next;
339 DBG( _salloc_bcheck(bh); )
340 free(bh);
341 bh = b2;
344 _buf_list = b;
345 b->b._next = NULL;
346 _buf_relax = NULL;
349 DBG( smemreset(); )
350 jleave:
351 NYD_LEAVE;
354 FL void
355 srelax_hold(void)
357 struct buffer *b;
358 NYD_ENTER;
360 assert(_buf_relax == NULL);
362 for (b = _buf_head; b != NULL; b = b->b._next)
363 b->b._relax = b->b._caster;
364 _buf_relax = _buf_server;
365 assert(_buf_relax != NULL);
366 NYD_LEAVE;
369 FL void
370 srelax_rele(void)
372 struct buffer *b;
373 NYD_ENTER;
375 assert(_buf_relax != NULL);
377 for (b = _buf_relax; b != NULL; b = b->b._next) {
378 DBG( _salloc_bcheck(b); )
379 b->b._caster = (b->b._relax != NULL) ? b->b._relax : b->b._bot;
380 b->b._relax = NULL;
382 _buf_relax = NULL;
383 NYD_LEAVE;
386 FL void
387 srelax(void)
389 /* The purpose of relaxation is only that it is possible to reset the
390 * casters, *not* to give back memory to the system. We are presumably in
391 * an iteration over all messages of a mailbox, and it'd be quite
392 * counterproductive to give the system allocator a chance to waste time */
393 struct buffer *b;
394 NYD_ENTER;
396 assert(_buf_relax != NULL);
398 for (b = _buf_relax; b != NULL; b = b->b._next) {
399 DBG( _salloc_bcheck(b); )
400 b->b._caster = (b->b._relax != NULL) ? b->b._relax : b->b._bot;
401 DBG( memset(b->b._caster, 0377, PTR2SIZE(b->b._max - b->b._caster)); )
403 NYD_LEAVE;
406 FL void
407 spreserve(void)
409 struct buffer *b;
410 NYD_ENTER;
412 for (b = _buf_head; b != NULL; b = b->b._next)
413 b->b._bot = b->b._caster;
414 NYD_LEAVE;
417 #ifdef HAVE_DEBUG
418 FL int
419 c_sstats(void *v)
421 size_t excess;
422 NYD_ENTER;
423 UNUSED(v);
425 excess = (_all_cybufcnt_max * SDYN_SIZE) + SBLTIN_SIZE;
426 excess = (excess >= _all_cysize_max) ? 0 : _all_cysize_max - excess;
428 printf("String usage statistics (cycle means one sreset() cycle):\n"
429 " Buffer allocs ever/max simultan. : %lu/%lu\n"
430 " Buffer size of builtin(1)/dynamic: %lu/%lu\n"
431 " Overall alloc count/bytes : %lu/%lu\n"
432 " Alloc bytes min/max/align wastage: %lu/%lu/%lu\n"
433 " sreset() cycles : %lu (%lu performed)\n"
434 " Cycle maximums: alloc count/bytes: %lu/%lu+%lu\n",
435 (ul_it)_all_bufcnt, (ul_it)_all_cybufcnt_max,
436 (ul_it)SBLTIN_SIZE, (ul_it)SDYN_SIZE,
437 (ul_it)_all_cnt, (ul_it)_all_size,
438 (ul_it)_all_min, (ul_it)_all_max, (ul_it)_all_wast,
439 (ul_it)_all_resetreqs, (ul_it)_all_resets,
440 (ul_it)_all_cycnt_max, (ul_it)_all_cysize_max, (ul_it)excess);
441 NYD_LEAVE;
442 return 0;
444 #endif
446 FL char *
447 (savestr)(char const *str SALLOC_DEBUG_ARGS)
449 size_t size;
450 char *news;
451 NYD_ENTER;
453 size = strlen(str) + 1;
454 news = (salloc)(size SALLOC_DEBUG_ARGSCALL);
455 memcpy(news, str, size);
456 NYD_LEAVE;
457 return news;
460 FL char *
461 (savestrbuf)(char const *sbuf, size_t sbuf_len SALLOC_DEBUG_ARGS)
463 char *news;
464 NYD_ENTER;
466 news = (salloc)(sbuf_len + 1 SALLOC_DEBUG_ARGSCALL);
467 memcpy(news, sbuf, sbuf_len);
468 news[sbuf_len] = 0;
469 NYD_LEAVE;
470 return news;
473 FL char *
474 (save2str)(char const *str, char const *old SALLOC_DEBUG_ARGS)
476 size_t newsize, oldsize;
477 char *news;
478 NYD_ENTER;
480 newsize = strlen(str) + 1;
481 oldsize = (old != NULL) ? strlen(old) + 1 : 0;
482 news = (salloc)(newsize + oldsize SALLOC_DEBUG_ARGSCALL);
483 if (oldsize) {
484 memcpy(news, old, oldsize);
485 news[oldsize - 1] = ' ';
487 memcpy(news + oldsize, str, newsize);
488 NYD_LEAVE;
489 return news;
492 FL char *
493 (savecat)(char const *s1, char const *s2 SALLOC_DEBUG_ARGS)
495 size_t l1, l2;
496 char *news;
497 NYD_ENTER;
499 l1 = strlen(s1);
500 l2 = strlen(s2);
501 news = (salloc)(l1 + l2 + 1 SALLOC_DEBUG_ARGSCALL);
502 memcpy(news + 0, s1, l1);
503 memcpy(news + l1, s2, l2);
504 news[l1 + l2] = '\0';
505 NYD_LEAVE;
506 return news;
510 * Support routines, auto-reclaimed storage
513 FL char *
514 (i_strdup)(char const *src SALLOC_DEBUG_ARGS)
516 size_t sz;
517 char *dest;
518 NYD_ENTER;
520 sz = strlen(src) + 1;
521 dest = (salloc)(sz SALLOC_DEBUG_ARGSCALL);
522 i_strcpy(dest, src, sz);
523 NYD_LEAVE;
524 return dest;
527 FL char *
528 (protbase)(char const *cp SALLOC_DEBUG_ARGS)
530 char *n, *np;
531 NYD_ENTER;
533 np = n = (salloc)(strlen(cp) + 1 SALLOC_DEBUG_ARGSCALL);
535 /* Just ignore the `is-system-mailbox' prefix XXX */
536 if (cp[0] == '%' && cp[1] == ':')
537 cp += 2;
539 while (*cp != '\0') {
540 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
541 *np++ = *cp++;
542 *np++ = *cp++;
543 *np++ = *cp++;
544 } else if (cp[0] == '/')
545 break;
546 else
547 *np++ = *cp++;
549 *np = '\0';
550 NYD_LEAVE;
551 return n;
554 FL char *
555 (urlxenc)(char const *cp SALLOC_DEBUG_ARGS) /* XXX */
557 char *n, *np;
558 NYD_ENTER;
560 np = n = (salloc)(strlen(cp) * 3 + 1 SALLOC_DEBUG_ARGSCALL);
562 while (*cp != '\0') {
563 if (alnumchar(*cp) || *cp == '_' || *cp == '@' ||
564 (PTRCMP(np, >, n) && (*cp == '.' || *cp == '-' || *cp == ':')))
565 *np++ = *cp;
566 else {
567 *np++ = '%';
568 *np++ = Hexchar((*cp & 0xf0) >> 4);
569 *np++ = Hexchar(*cp & 0x0f);
571 cp++;
573 *np = '\0';
574 NYD_LEAVE;
575 return n;
578 FL char *
579 (urlxdec)(char const *cp SALLOC_DEBUG_ARGS) /* XXX */
581 char *n, *np;
582 NYD_ENTER;
584 np = n = (salloc)(strlen(cp) + 1 SALLOC_DEBUG_ARGSCALL);
586 while (*cp != '\0') {
587 if (cp[0] == '%' && cp[1] != '\0' && cp[2] != '\0') {
588 *np = (int)(cp[1] > '9' ? cp[1] - 'A' + 10 : cp[1] - '0') << 4;
589 *np++ |= cp[2] > '9' ? cp[2] - 'A' + 10 : cp[2] - '0';
590 cp += 3;
591 } else
592 *np++ = *cp++;
594 *np = '\0';
595 NYD_LEAVE;
596 return n;
599 FL struct str *
600 str_concat_csvl(struct str *self, ...) /* XXX onepass maybe better here */
602 va_list vl;
603 size_t l;
604 char const *cs;
605 NYD_ENTER;
607 va_start(vl, self);
608 for (l = 0; (cs = va_arg(vl, char const*)) != NULL;)
609 l += strlen(cs);
610 va_end(vl);
612 self->l = l;
613 self->s = salloc(l + 1);
615 va_start(vl, self);
616 for (l = 0; (cs = va_arg(vl, char const*)) != NULL;) {
617 size_t i = strlen(cs);
618 memcpy(self->s + l, cs, i);
619 l += i;
621 self->s[l] = '\0';
622 va_end(vl);
623 NYD_LEAVE;
624 return self;
627 FL struct str *
628 (str_concat_cpa)(struct str *self, char const * const *cpa,
629 char const *sep_o_null SALLOC_DEBUG_ARGS)
631 size_t sonl, l;
632 char const * const *xcpa;
633 NYD_ENTER;
635 sonl = (sep_o_null != NULL) ? strlen(sep_o_null) : 0;
637 for (l = 0, xcpa = cpa; *xcpa != NULL; ++xcpa)
638 l += strlen(*xcpa) + sonl;
640 self->l = l;
641 self->s = (salloc)(l + 1 SALLOC_DEBUG_ARGSCALL);
643 for (l = 0, xcpa = cpa; *xcpa != NULL; ++xcpa) {
644 size_t i = strlen(*xcpa);
645 memcpy(self->s + l, *xcpa, i);
646 l += i;
647 if (sonl > 0) {
648 memcpy(self->s + l, sep_o_null, sonl);
649 l += sonl;
652 self->s[l] = '\0';
653 NYD_LEAVE;
654 return self;
658 * Routines that are not related to auto-reclaimed storage follow.
661 FL int
662 anyof(char const *s1, char const *s2)
664 NYD_ENTER;
665 for (; *s1 != '\0'; ++s1)
666 if (strchr(s2, *s1) != NULL)
667 break;
668 NYD_LEAVE;
669 return (*s1 != '\0');
672 FL char *
673 n_strsep(char **iolist, char sep, bool_t ignore_empty)
675 char *base, *cp;
676 NYD_ENTER;
678 for (base = *iolist; base != NULL; base = *iolist) {
679 while (*base != '\0' && blankspacechar(*base))
680 ++base;
681 cp = strchr(base, sep);
682 if (cp != NULL)
683 *iolist = cp + 1;
684 else {
685 *iolist = NULL;
686 cp = base + strlen(base);
688 while (cp > base && blankspacechar(cp[-1]))
689 --cp;
690 *cp = '\0';
691 if (*base != '\0' || !ignore_empty)
692 break;
694 NYD_LEAVE;
695 return base;
698 FL void
699 i_strcpy(char *dest, char const *src, size_t size)
701 NYD_ENTER;
702 if (size > 0) {
703 for (;; ++dest, ++src)
704 if ((*dest = lowerconv(*src)) == '\0') {
705 break;
706 } else if (--size == 0) {
707 *dest = '\0';
708 break;
711 NYD_LEAVE;
714 FL int
715 is_prefix(char const *as1, char const *as2)
717 char c;
718 NYD_ENTER;
720 for (; (c = *as1) == *as2 && c != '\0'; ++as1, ++as2)
721 if (*as2 == '\0')
722 break;
723 NYD_LEAVE;
724 return (c == '\0');
727 FL char const *
728 last_at_before_slash(char const *sp)
730 char const *cp;
731 char c;
732 NYD_ENTER;
734 for (cp = sp; (c = *cp) != '\0'; ++cp)
735 if (c == '/')
736 break;
737 while (cp > sp && *--cp != '@')
739 if (*cp != '@')
740 cp = NULL;
741 NYD_LEAVE;
742 return cp;
745 FL char *
746 laststring(char *linebuf, bool_t *needs_list, bool_t strip)
748 char *cp, *p, quoted;
749 NYD_ENTER;
751 /* Anything to do at all? */
752 if (*(cp = linebuf) == '\0')
753 goto jnull;
754 cp += strlen(linebuf) - 1;
756 /* Strip away trailing blanks */
757 while (whitechar(*cp) && PTRCMP(cp, >, linebuf))
758 --cp;
759 cp[1] = '\0';
760 if (cp == linebuf)
761 goto jleave;
763 /* Now search for the BOS of the "last string" */
764 quoted = *cp;
765 if (quoted == '\'' || quoted == '"') {
766 if (strip)
767 *cp = '\0';
768 } else
769 quoted = ' ';
771 while (PTRCMP(cp, >, linebuf)) {
772 --cp;
773 if (quoted != ' ') {
774 if (*cp != quoted)
775 continue;
776 } else if (!whitechar(*cp))
777 continue;
778 if (PTRCMP(cp, ==, linebuf) || cp[-1] != '\\') {
779 /* When in whitespace mode, WS prefix doesn't belong */
780 if (quoted == ' ')
781 ++cp;
782 break;
784 /* Expand the escaped quote character */
785 for (p = --cp; (p[0] = p[1]) != '\0'; ++p)
788 if (strip && quoted != ' ' && *cp == quoted)
789 for (p = cp; (p[0] = p[1]) != '\0'; ++p)
792 /* The "last string" has been skipped over, but still, try to step backwards
793 * until we are at BOS or see whitespace, so as to make possible things like
794 * "? copy +'x y.mbox'" or even "? copy +x\ y.mbox" */
795 while (PTRCMP(cp, >, linebuf)) {
796 --cp;
797 if (whitechar(*cp)) {
798 p = cp;
799 *cp++ = '\0';
800 /* We can furtherly release our callees if we now decide wether the
801 * remaining non-"last string" line content contains non-WS */
802 while (PTRCMP(--p, >=, linebuf))
803 if (!whitechar(*p))
804 goto jleave;
805 linebuf = cp;
806 break;
810 jleave:
811 if (cp != NULL && *cp == '\0')
812 goto jnull;
813 *needs_list = (cp != linebuf && *linebuf != '\0');
814 j_leave:
815 NYD_LEAVE;
816 return cp;
817 jnull:
818 *needs_list = FAL0;
819 cp = NULL;
820 goto j_leave;
823 FL void
824 makelow(char *cp) /* TODO isn't that crap? --> */
826 NYD_ENTER;
827 #ifdef HAVE_C90AMEND1
828 if (mb_cur_max > 1) {
829 char *tp = cp;
830 wchar_t wc;
831 int len;
833 while (*cp) {
834 len = mbtowc(&wc, cp, mb_cur_max);
835 if (len < 0)
836 *tp++ = *cp++;
837 else {
838 wc = towlower(wc);
839 if (wctomb(tp, wc) == len)
840 tp += len, cp += len;
841 else
842 *tp++ = *cp++; /* <-- at least here */
845 } else
846 #endif
849 *cp = tolower((uc_it)*cp);
850 while (*cp++);
852 NYD_LEAVE;
855 FL int
856 substr(char const *str, char const *sub)
858 char const *cp, *backup;
859 NYD_ENTER;
861 cp = sub;
862 backup = str;
863 while (*str && *cp) {
864 #ifdef HAVE_C90AMEND1
865 if (mb_cur_max > 1) {
866 wchar_t c, c2;
867 int sz;
869 if ((sz = mbtowc(&c, cp, mb_cur_max)) < 0)
870 goto Jsinglebyte;
871 cp += sz;
872 if ((sz = mbtowc(&c2, str, mb_cur_max)) < 0)
873 goto Jsinglebyte;
874 str += sz;
875 c = towupper(c);
876 c2 = towupper(c2);
877 if (c != c2) {
878 if ((sz = mbtowc(&c, backup, mb_cur_max)) > 0) {
879 backup += sz;
880 str = backup;
881 } else
882 str = ++backup;
883 cp = sub;
885 } else
886 Jsinglebyte:
887 #endif
889 int c, c2;
891 c = *cp++ & 0377;
892 if (islower(c))
893 c = toupper(c);
894 c2 = *str++ & 0377;
895 if (islower(c2))
896 c2 = toupper(c2);
897 if (c != c2) {
898 str = ++backup;
899 cp = sub;
903 NYD_LEAVE;
904 return (*cp == '\0');
907 #ifndef HAVE_SNPRINTF
908 FL int
909 snprintf(char *str, size_t size, char const *format, ...) /* XXX DANGER! */
911 va_list ap;
912 int ret;
913 NYD_ENTER;
915 va_start(ap, format);
916 ret = vsprintf(str, format, ap);
917 va_end(ap);
918 if (ret < 0)
919 ret = strlen(str);
920 NYD_LEAVE;
921 return ret;
923 #endif
925 FL char *
926 sstpcpy(char *dst, char const *src)
928 NYD_ENTER;
929 while ((*dst = *src++) != '\0')
930 ++dst;
931 NYD_LEAVE;
932 return dst;
935 FL char *
936 (sstrdup)(char const *cp SMALLOC_DEBUG_ARGS)
938 char *dp = NULL;
939 NYD_ENTER;
941 if (cp != NULL) {
942 size_t l = strlen(cp) + 1;
943 dp = (smalloc)(l SMALLOC_DEBUG_ARGSCALL);
944 memcpy(dp, cp, l);
946 NYD_LEAVE;
947 return dp;
950 FL char *
951 (sbufdup)(char const *cp, size_t len SMALLOC_DEBUG_ARGS)
953 char *dp = NULL;
954 NYD_ENTER;
956 dp = (smalloc)(len + 1 SMALLOC_DEBUG_ARGSCALL);
957 if (cp != NULL)
958 memcpy(dp, cp, len);
959 dp[len] = '\0';
960 NYD_LEAVE;
961 return dp;
964 FL char *
965 n_strlcpy(char *dst, char const *src, size_t len)
967 NYD_ENTER;
969 assert(len > 0);
971 dst = strncpy(dst, src, len);
972 dst[len - 1] = '\0';
973 NYD_LEAVE;
974 return dst;
977 FL int
978 asccasecmp(char const *s1, char const *s2)
980 int cmp;
981 NYD_ENTER;
983 for (;;) {
984 char c1 = *s1++, c2 = *s2++;
985 if ((cmp = lowerconv(c1) - lowerconv(c2)) != 0 || c1 == '\0')
986 break;
988 NYD_LEAVE;
989 return cmp;
992 FL int
993 ascncasecmp(char const *s1, char const *s2, size_t sz)
995 int cmp = 0;
996 NYD_ENTER;
998 while (sz-- > 0) {
999 char c1 = *s1++, c2 = *s2++;
1000 cmp = (ui8_t)lowerconv(c1);
1001 cmp -= (ui8_t)lowerconv(c2);
1002 if (cmp != 0 || c1 == '\0')
1003 break;
1005 NYD_LEAVE;
1006 return cmp;
1009 FL char const *
1010 asccasestr(char const *haystack, char const *xneedle)
1012 char *needle = NULL, *NEEDLE;
1013 size_t i, sz;
1014 NYD_ENTER;
1016 sz = strlen(xneedle);
1017 if (sz == 0)
1018 goto jleave;
1020 needle = ac_alloc(sz);
1021 NEEDLE = ac_alloc(sz);
1022 for (i = 0; i < sz; i++) {
1023 needle[i] = lowerconv(xneedle[i]);
1024 NEEDLE[i] = upperconv(xneedle[i]);
1027 while (*haystack) {
1028 if (*haystack == *needle || *haystack == *NEEDLE) {
1029 for (i = 1; i < sz; i++)
1030 if (haystack[i] != needle[i] &&
1031 haystack[i] != NEEDLE[i])
1032 break;
1033 if (i == sz)
1034 goto jleave;
1036 haystack++;
1038 haystack = NULL;
1039 jleave:
1040 if (needle != NULL) {
1041 ac_free(NEEDLE);
1042 ac_free(needle);
1044 NYD_LEAVE;
1045 return haystack;
1048 FL bool_t
1049 is_asccaseprefix(char const *as1, char const *as2)
1051 bool_t rv = FAL0;
1052 NYD_ENTER;
1054 for (;; ++as1, ++as2) {
1055 char c1 = lowerconv(*as1), c2 = lowerconv(*as2);
1056 if ((rv = (c1 == '\0')))
1057 break;
1058 if (c1 != c2 || c2 == '\0')
1059 break;
1061 NYD_LEAVE;
1062 return rv;
1065 FL struct str *
1066 (n_str_dup)(struct str *self, struct str const *t SMALLOC_DEBUG_ARGS)
1068 NYD_ENTER;
1069 if (t != NULL && t->l > 0) {
1070 self->l = t->l;
1071 self->s = (srealloc)(self->s, t->l + 1 SMALLOC_DEBUG_ARGSCALL);
1072 memcpy(self->s, t->s, t->l);
1073 } else
1074 self->l = 0;
1075 NYD_LEAVE;
1076 return self;
1079 FL struct str *
1080 (n_str_add_buf)(struct str *self, char const *buf, size_t buflen
1081 SMALLOC_DEBUG_ARGS)
1083 NYD_ENTER;
1084 if (buflen != 0) {
1085 size_t sl = self->l;
1086 self->l = sl + buflen;
1087 self->s = (srealloc)(self->s, self->l+1 SMALLOC_DEBUG_ARGSCALL);
1088 memcpy(self->s + sl, buf, buflen);
1090 NYD_LEAVE;
1091 return self;
1095 * Our iconv(3) wrapper
1097 #ifdef HAVE_ICONV
1099 static void _ic_toupper(char *dest, char const *src);
1100 static void _ic_stripdash(char *p);
1102 static void
1103 _ic_toupper(char *dest, char const *src)
1105 NYD_ENTER;
1107 *dest++ = upperconv(*src);
1108 while (*src++);
1109 NYD_LEAVE;
1112 static void
1113 _ic_stripdash(char *p)
1115 char *q = p;
1116 NYD_ENTER;
1119 if (*(q = p) != '-')
1120 ++q;
1121 while (*p++ != '\0');
1122 NYD_LEAVE;
1125 FL iconv_t
1126 n_iconv_open(char const *tocode, char const *fromcode)
1128 iconv_t id;
1129 char *t, *f;
1130 NYD_ENTER;
1132 if ((id = iconv_open(tocode, fromcode)) != (iconv_t)-1)
1133 goto jleave;
1135 /* Remove the "iso-" prefixes for Solaris */
1136 if (ascncasecmp(tocode, "iso-", 4) == 0)
1137 tocode += 4;
1138 else if (ascncasecmp(tocode, "iso", 3) == 0)
1139 tocode += 3;
1140 if (ascncasecmp(fromcode, "iso-", 4) == 0)
1141 fromcode += 4;
1142 else if (ascncasecmp(fromcode, "iso", 3) == 0)
1143 fromcode += 3;
1144 if (*tocode == '\0' || *fromcode == '\0') {
1145 id = (iconv_t)-1;
1146 goto jleave;
1148 if ((id = iconv_open(tocode, fromcode)) != (iconv_t)-1)
1149 goto jleave;
1151 /* Solaris prefers upper-case charset names. Don't ask... */
1152 t = salloc(strlen(tocode) + 1);
1153 _ic_toupper(t, tocode);
1154 f = salloc(strlen(fromcode) + 1);
1155 _ic_toupper(f, fromcode);
1156 if ((id = iconv_open(t, f)) != (iconv_t)-1)
1157 goto jleave;
1159 /* Strip dashes for UnixWare */
1160 _ic_stripdash(t);
1161 _ic_stripdash(f);
1162 if ((id = iconv_open(t, f)) != (iconv_t)-1)
1163 goto jleave;
1165 /* Add your vendor's sillynesses here */
1167 /* If the encoding names are equal at this point, they are just not
1168 * understood by iconv(), and we cannot sensibly use it in any way. We do
1169 * not perform this as an optimization above since iconv() can otherwise be
1170 * used to check the validity of the input even with identical encoding
1171 * names */
1172 if (!strcmp(t, f))
1173 errno = 0;
1174 jleave:
1175 NYD_LEAVE;
1176 return id;
1179 FL void
1180 n_iconv_close(iconv_t cd)
1182 NYD_ENTER;
1183 iconv_close(cd);
1184 if (cd == iconvd)
1185 iconvd = (iconv_t)-1;
1186 NYD_LEAVE;
1189 #ifdef notyet
1190 FL void
1191 n_iconv_reset(iconv_t cd)
1193 NYD_ENTER;
1194 (void)iconv(cd, NULL, NULL, NULL, NULL);
1195 NYD_LEAVE;
1197 #endif
1199 /* (2012-09-24: export and use it exclusively to isolate prototype problems
1200 * (*inb* is 'char const **' except in POSIX) in a single place.
1201 * GNU libiconv even allows for configuration time const/non-const..
1202 * In the end it's an ugly guess, but we can't do better since make(1) doesn't
1203 * support compiler invocations which bail on error, so no -Werror */
1204 /* Citrus project? */
1205 # if defined _ICONV_H_ && defined __ICONV_F_HIDE_INVALID
1206 /* DragonFly 3.2.1 is special */
1207 # ifdef __DragonFly__
1208 # define __INBCAST(S) (char ** __restrict__)UNCONST(S)
1209 # else
1210 # define __INBCAST(S) (char const **)UNCONST(S)
1211 # endif
1212 # endif
1213 # ifndef __INBCAST
1214 # define __INBCAST(S) (char **)UNCONST(S)
1215 # endif
1217 FL int
1218 n_iconv_buf(iconv_t cd, char const **inb, size_t *inbleft,/*XXX redo iconv use*/
1219 char **outb, size_t *outbleft, bool_t skipilseq)
1221 int err = 0;
1222 NYD_ENTER;
1224 for (;;) {
1225 size_t sz = iconv(cd, __INBCAST(inb), inbleft, outb, outbleft);
1226 if (sz != (size_t)-1)
1227 break;
1228 err = errno;
1229 if (!skipilseq || err != EILSEQ)
1230 break;
1231 if (*inbleft > 0) {
1232 ++(*inb);
1233 --(*inbleft);
1234 } else if (*outbleft > 0) {
1235 **outb = '\0';
1236 break;
1238 if (*outbleft > 0/* TODO 0xFFFD 2*/) {
1239 /* TODO 0xFFFD (*outb)[0] = '[';
1240 * TODO (*outb)[1] = '?';
1241 * TODO 0xFFFD (*outb)[2] = ']';
1242 * TODO (*outb) += 3;
1243 * TODO (*outbleft) -= 3; */
1244 *(*outb)++ = '?';
1245 --*outbleft;
1246 } else {
1247 err = E2BIG;
1248 break;
1250 err = 0;
1252 NYD_LEAVE;
1253 return err;
1255 # undef __INBCAST
1257 FL int
1258 n_iconv_str(iconv_t cd, struct str *out, struct str const *in,
1259 struct str *in_rest_or_null, bool_t skipilseq)
1261 int err = 0;
1262 char *obb = out->s, *ob;
1263 char const *ib;
1264 size_t olb = out->l, ol, il;
1265 NYD_ENTER;
1267 ol = in->l;
1268 ol = (ol << 1) - (ol >> 4);
1269 if (olb < ol) {
1270 olb = ol;
1271 goto jrealloc;
1274 for (;;) {
1275 ib = in->s;
1276 il = in->l;
1277 ob = obb;
1278 ol = olb;
1279 err = n_iconv_buf(cd, &ib, &il, &ob, &ol, skipilseq);
1280 if (err == 0 || err != E2BIG)
1281 break;
1282 err = 0;
1283 olb += in->l;
1284 jrealloc:
1285 obb = srealloc(obb, olb);
1288 if (in_rest_or_null != NULL) {
1289 in_rest_or_null->s = UNCONST(ib);
1290 in_rest_or_null->l = il;
1292 out->s = obb;
1293 out->l = olb - ol;
1294 NYD_LEAVE;
1295 return err;
1297 #endif /* HAVE_ICONV */
1299 /* vim:set fenc=utf-8:s-it-mode */