mk-conf.sh: adjust fixed CONFIG=urations for mandatory options
[s-mailx.git] / strings.c
blobabe0b18423df4116182fae319ef22e7e90fd197c
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.
56 * To relax stuff further, especially in non-interactive, i.e., send mode, do
57 * not even allocate the first buffer, but let that be a builtin DATA section
58 * one that is rather small, yet sufficient for send mode to *never* even
59 * perform a single dynamic allocation (from our stringdope point of view).
60 * Encapsulate user chunks with canaries if HAVE_DEBUG.
62 * To deal with huge allocations which would overflow those buffers we offer
63 * a simple bypass that relies upon smalloc() (which panic()s as necessary) */
65 #ifdef HAVE_DEBUG
66 # define _SHOPE_SIZE (2u * 8 * sizeof(char) + sizeof(struct schunk))
68 CTA(sizeof(char) == sizeof(ui8_t));
70 struct schunk {
71 char const *file;
72 ui32_t line;
73 ui16_t usr_size;
74 ui16_t full_size;
77 union sptr {
78 void *p;
79 struct schunk *c;
80 char *cp;
81 ui8_t *ui8p;
83 #endif /* HAVE_DEBUG */
85 union __align__ {
86 char *cp;
87 size_t sz;
88 ul_it ul;
90 #define SALIGN (sizeof(union __align__) - 1)
92 CTA(ISPOW2(SALIGN + 1));
94 struct b_base {
95 struct buffer *_next;
96 char *_bot; /* For spreserve() */
97 char *_relax; /* If !NULL, used by srelax() instead of ._bot */
98 char *_max; /* Max usable byte */
99 char *_caster; /* NULL if full */
102 /* Single instance builtin buffer, DATA */
103 struct b_bltin {
104 struct b_base b_base;
105 char b_buf[SBUFFER_BUILTIN - sizeof(struct b_base)];
107 #define SBLTIN_SIZE SIZEOF_FIELD(struct b_bltin, b_buf)
109 /* Dynamically allocated buffers */
110 struct b_dyn {
111 struct b_base b_base;
112 char b_buf[SBUFFER_SIZE - sizeof(struct b_base)];
114 #define SDYN_SIZE SIZEOF_FIELD(struct b_dyn, b_buf)
116 struct buffer {
117 struct b_base b;
118 char b_buf[VFIELD_SIZE(SALIGN + 1)];
121 struct hugebuf {
122 struct hugebuf *hb_next;
123 char hb_buf[VFIELD_SIZE(SALIGN + 1)];
126 static struct b_bltin _builtin_buf;
127 static struct buffer *_buf_head, *_buf_list, *_buf_server, *_buf_relax;
128 static struct hugebuf *_huge_list;
129 #ifdef HAVE_DEBUG
130 static size_t _all_cnt, _all_cycnt, _all_cycnt_max,
131 _all_size, _all_cysize, _all_cysize_max, _all_min,
132 _all_max, _all_wast,
133 _all_bufcnt, _all_cybufcnt, _all_cybufcnt_max,
134 _all_resetreqs, _all_resets;
135 #endif
137 /* sreset() / srelax() release a buffer, check the canaries of all chunks */
138 #ifdef HAVE_DEBUG
139 static void _salloc_bcheck(struct buffer *b);
140 #endif
142 #ifdef HAVE_DEBUG
143 static void
144 _salloc_bcheck(struct buffer *b)
146 union sptr pmax, pp;
147 /*NYD2_ENTER;*/
149 pmax.cp = (b->b._caster == NULL) ? b->b._max : b->b._caster;
150 pp.cp = b->b._bot;
152 while (pp.cp < pmax.cp) {
153 struct schunk *c;
154 union sptr x;
155 void *ux;
156 ui8_t i;
158 c = pp.c;
159 pp.cp += c->full_size;
160 x.p = c + 1;
161 ux = x.cp + 8;
163 i = 0;
164 if (x.ui8p[0] != 0xDE) i |= 1<<0;
165 if (x.ui8p[1] != 0xAA) i |= 1<<1;
166 if (x.ui8p[2] != 0x55) i |= 1<<2;
167 if (x.ui8p[3] != 0xAD) i |= 1<<3;
168 if (x.ui8p[4] != 0xBE) i |= 1<<4;
169 if (x.ui8p[5] != 0x55) i |= 1<<5;
170 if (x.ui8p[6] != 0xAA) i |= 1<<6;
171 if (x.ui8p[7] != 0xEF) i |= 1<<7;
172 if (i != 0)
173 alert("sdope %p: corrupt lower canary: 0x%02X, size %u: %s, line %u",
174 ux, i, c->usr_size, c->file, c->line);
175 x.cp += 8 + c->usr_size;
177 i = 0;
178 if (x.ui8p[0] != 0xDE) i |= 1<<0;
179 if (x.ui8p[1] != 0xAA) i |= 1<<1;
180 if (x.ui8p[2] != 0x55) i |= 1<<2;
181 if (x.ui8p[3] != 0xAD) i |= 1<<3;
182 if (x.ui8p[4] != 0xBE) i |= 1<<4;
183 if (x.ui8p[5] != 0x55) i |= 1<<5;
184 if (x.ui8p[6] != 0xAA) i |= 1<<6;
185 if (x.ui8p[7] != 0xEF) i |= 1<<7;
186 if (i != 0)
187 alert("sdope %p: corrupt upper canary: 0x%02X, size %u: %s, line %u",
188 ux, i, c->usr_size, c->file, c->line);
190 /*NYD2_LEAVE;*/
192 #endif
194 FL void *
195 (salloc)(size_t size SALLOC_DEBUG_ARGS)
197 DBG( size_t orig_size = size; )
198 union {struct buffer *b; struct hugebuf *hb; char *cp;} u;
199 char *x, *y, *z;
200 NYD2_ENTER;
202 if (size == 0)
203 ++size;
204 size += SALIGN;
205 size &= ~SALIGN;
207 #ifdef HAVE_DEBUG
208 ++_all_cnt;
209 ++_all_cycnt;
210 _all_cycnt_max = MAX(_all_cycnt_max, _all_cycnt);
211 _all_size += size;
212 _all_cysize += size;
213 _all_cysize_max = MAX(_all_cysize_max, _all_cysize);
214 _all_min = (_all_max == 0) ? size : MIN(_all_min, size);
215 _all_max = MAX(_all_max, size);
216 _all_wast += size - orig_size;
218 size += _SHOPE_SIZE;
220 if (size >= SDYN_SIZE - 1)
221 alert("salloc() of %" ZFMT " bytes from `%s', line %d\n",
222 size, mdbg_file, mdbg_line);
223 #endif
225 /* Huge allocations are special */
226 if (UNLIKELY(size >= SDYN_SIZE - 1))
227 goto jhuge;
229 /* Search for a buffer with enough free space to serve request */
230 if ((u.b = _buf_server) != NULL)
231 goto jumpin;
232 jredo:
233 for (u.b = _buf_head; u.b != NULL; u.b = u.b->b._next) {
234 jumpin:
235 x = u.b->b._caster;
236 if (x == NULL) {
237 if (u.b == _buf_server) {
238 if (u.b == _buf_head && (u.b = _buf_head->b._next) != NULL) {
239 _buf_server = u.b;
240 goto jumpin;
242 _buf_server = NULL;
243 goto jredo;
245 continue;
247 y = x + size;
248 z = u.b->b._max;
249 if (PTRCMP(y, <=, z)) {
250 /* Alignment is the one thing, the other is what is usually allocated,
251 * and here about 40 bytes seems to be a good cut to avoid non-usable
252 * non-NULL casters. However, because of _salloc_bcheck(), we may not
253 * set ._caster to NULL because then it would check all chunks up to
254 * ._max, which surely doesn't work; speed is no issue with DEBUG */
255 u.b->b._caster = NDBG( PTRCMP(y + 42 + 16, >=, z) ? NULL : ) y;
256 u.cp = x;
257 goto jleave;
261 /* Need a new buffer */
262 if (_buf_head == NULL) {
263 struct b_bltin *b = &_builtin_buf;
264 b->b_base._max = b->b_buf + SBLTIN_SIZE - 1;
265 _buf_head = (struct buffer*)b;
266 u.b = _buf_head;
267 } else {
268 #ifdef HAVE_DEBUG
269 ++_all_bufcnt;
270 ++_all_cybufcnt;
271 _all_cybufcnt_max = MAX(_all_cybufcnt_max, _all_cybufcnt);
272 #endif
273 u.b = smalloc(sizeof(struct b_dyn));
274 u.b->b._max = u.b->b_buf + SDYN_SIZE - 1;
276 if (_buf_list != NULL)
277 _buf_list->b._next = u.b;
278 _buf_server = _buf_list = u.b;
279 u.b->b._next = NULL;
280 u.b->b._caster = (u.b->b._bot = u.b->b_buf) + size;
281 u.b->b._relax = NULL;
282 u.cp = u.b->b._bot;
284 jleave:
285 /* Encapsulate user chunk in debug canaries */
286 #ifdef HAVE_DEBUG
288 union sptr xl, xu;
289 struct schunk *xc;
291 xl.p = u.cp;
292 xc = xl.c;
293 xc->file = mdbg_file;
294 xc->line = mdbg_line;
295 xc->usr_size = (ui16_t)orig_size;
296 xc->full_size = (ui16_t)size;
297 xl.p = xc + 1;
298 xl.ui8p[0]=0xDE; xl.ui8p[1]=0xAA; xl.ui8p[2]=0x55; xl.ui8p[3]=0xAD;
299 xl.ui8p[4]=0xBE; xl.ui8p[5]=0x55; xl.ui8p[6]=0xAA; xl.ui8p[7]=0xEF;
300 u.cp = xl.cp + 8;
301 xu.p = u.cp;
302 xu.cp += orig_size;
303 xu.ui8p[0]=0xDE; xu.ui8p[1]=0xAA; xu.ui8p[2]=0x55; xu.ui8p[3]=0xAD;
304 xu.ui8p[4]=0xBE; xu.ui8p[5]=0x55; xu.ui8p[6]=0xAA; xu.ui8p[7]=0xEF;
306 #endif
307 NYD2_LEAVE;
308 return u.cp;
310 jhuge:
311 u.hb = smalloc(sizeof(*u.hb) - VFIELD_SIZEOF(struct hugebuf, hb_buf) +
312 size +1);
313 u.hb->hb_next = _huge_list;
314 _huge_list = u.hb;
315 u.cp = u.hb->hb_buf;
316 goto jleave;
319 FL void *
320 (csalloc)(size_t nmemb, size_t size SALLOC_DEBUG_ARGS)
322 void *vp;
323 NYD2_ENTER;
325 size *= nmemb;
326 vp = (salloc)(size SALLOC_DEBUG_ARGSCALL);
327 memset(vp, 0, size);
328 NYD2_LEAVE;
329 return vp;
332 FL void
333 sreset(bool_t only_if_relaxed)
335 struct buffer *bh;
336 NYD_ENTER;
338 DBG( ++_all_resetreqs; )
339 if (noreset || (only_if_relaxed && _buf_relax == NULL))
340 goto jleave;
342 #ifdef HAVE_DEBUG
343 _all_cycnt = _all_cysize = 0;
344 _all_cybufcnt = (_buf_head != NULL && _buf_head->b._next != NULL);
345 ++_all_resets;
346 #endif
348 if ((bh = _buf_head) != NULL) {
349 struct buffer *b = bh;
350 DBG( _salloc_bcheck(b); )
351 b->b._caster = b->b._bot;
352 b->b._relax = NULL;
353 DBG( memset(b->b._caster, 0377, PTR2SIZE(b->b._max - b->b._caster)); )
354 _buf_server = b;
356 if ((bh = bh->b._next) != NULL) {
357 b = bh;
358 DBG( _salloc_bcheck(b); )
359 b->b._caster = b->b._bot;
360 b->b._relax = NULL;
361 DBG( memset(b->b._caster, 0377, PTR2SIZE(b->b._max - b->b._caster)); )
363 for (bh = bh->b._next; bh != NULL;) {
364 struct buffer *b2 = bh->b._next;
365 DBG( _salloc_bcheck(bh); )
366 free(bh);
367 bh = b2;
370 _buf_list = b;
371 b->b._next = NULL;
372 _buf_relax = NULL;
375 /* We'll only free huge allocations when not in relaxed mode, because they
376 * know nothing about relaxation (as above) */
377 if (_buf_relax == NULL)
378 while (_huge_list != NULL) {
379 struct hugebuf *hb = _huge_list;
380 _huge_list = hb->hb_next;
381 free(hb);
384 DBG( smemreset(); )
385 jleave:
386 NYD_LEAVE;
389 FL void
390 srelax_hold(void)
392 struct buffer *b;
393 NYD_ENTER;
395 assert(_buf_relax == NULL);
397 for (b = _buf_head; b != NULL; b = b->b._next)
398 b->b._relax = b->b._caster;
399 _buf_relax = _buf_server;
400 assert(_buf_relax != NULL);
401 NYD_LEAVE;
404 FL void
405 srelax_rele(void)
407 struct buffer *b;
408 NYD_ENTER;
410 assert(_buf_relax != NULL);
412 for (b = _buf_relax; b != NULL; b = b->b._next) {
413 DBG( _salloc_bcheck(b); )
414 b->b._caster = (b->b._relax != NULL) ? b->b._relax : b->b._bot;
415 b->b._relax = NULL;
417 _buf_relax = NULL;
418 NYD_LEAVE;
421 FL void
422 srelax(void)
424 /* The purpose of relaxation is only that it is possible to reset the
425 * casters, *not* to give back memory to the system. We are presumably in
426 * an iteration over all messages of a mailbox, and it'd be quite
427 * counterproductive to give the system allocator a chance to waste time */
428 struct buffer *b;
429 NYD_ENTER;
431 assert(_buf_relax != NULL);
433 for (b = _buf_relax; b != NULL; b = b->b._next) {
434 DBG( _salloc_bcheck(b); )
435 b->b._caster = (b->b._relax != NULL) ? b->b._relax : b->b._bot;
436 DBG( memset(b->b._caster, 0377, PTR2SIZE(b->b._max - b->b._caster)); )
438 NYD_LEAVE;
441 FL void
442 spreserve(void)
444 struct buffer *b;
445 NYD_ENTER;
447 for (b = _buf_head; b != NULL; b = b->b._next)
448 b->b._bot = b->b._caster;
449 NYD_LEAVE;
452 #ifdef HAVE_DEBUG
453 FL int
454 c_sstats(void *v)
456 size_t excess;
457 NYD_ENTER;
458 UNUSED(v);
460 excess = (_all_cybufcnt_max * SDYN_SIZE) + SBLTIN_SIZE;
461 excess = (excess >= _all_cysize_max) ? 0 : _all_cysize_max - excess;
463 printf("String usage statistics (cycle means one sreset() cycle):\n"
464 " Buffer allocs ever/max simultan. : %lu/%lu\n"
465 " Buffer size of builtin(1)/dynamic: %lu/%lu\n"
466 " Overall alloc count/bytes : %lu/%lu\n"
467 " Alloc bytes min/max/align wastage: %lu/%lu/%lu\n"
468 " sreset() cycles : %lu (%lu performed)\n"
469 " Cycle maximums: alloc count/bytes: %lu/%lu+%lu\n",
470 (ul_it)_all_bufcnt, (ul_it)_all_cybufcnt_max,
471 (ul_it)SBLTIN_SIZE, (ul_it)SDYN_SIZE,
472 (ul_it)_all_cnt, (ul_it)_all_size,
473 (ul_it)_all_min, (ul_it)_all_max, (ul_it)_all_wast,
474 (ul_it)_all_resetreqs, (ul_it)_all_resets,
475 (ul_it)_all_cycnt_max, (ul_it)_all_cysize_max, (ul_it)excess);
476 NYD_LEAVE;
477 return 0;
479 #endif
481 FL char *
482 (savestr)(char const *str SALLOC_DEBUG_ARGS)
484 size_t size;
485 char *news;
486 NYD_ENTER;
488 size = strlen(str) +1;
489 news = (salloc)(size SALLOC_DEBUG_ARGSCALL);
490 memcpy(news, str, size);
491 NYD_LEAVE;
492 return news;
495 FL char *
496 (savestrbuf)(char const *sbuf, size_t sbuf_len SALLOC_DEBUG_ARGS)
498 char *news;
499 NYD_ENTER;
501 news = (salloc)(sbuf_len +1 SALLOC_DEBUG_ARGSCALL);
502 memcpy(news, sbuf, sbuf_len);
503 news[sbuf_len] = 0;
504 NYD_LEAVE;
505 return news;
508 FL char *
509 (save2str)(char const *str, char const *old SALLOC_DEBUG_ARGS)
511 size_t newsize, oldsize;
512 char *news;
513 NYD_ENTER;
515 newsize = strlen(str) +1;
516 oldsize = (old != NULL) ? strlen(old) + 1 : 0;
517 news = (salloc)(newsize + oldsize SALLOC_DEBUG_ARGSCALL);
518 if (oldsize) {
519 memcpy(news, old, oldsize);
520 news[oldsize - 1] = ' ';
522 memcpy(news + oldsize, str, newsize);
523 NYD_LEAVE;
524 return news;
527 FL char *
528 (savecat)(char const *s1, char const *s2 SALLOC_DEBUG_ARGS)
530 size_t l1, l2;
531 char *news;
532 NYD_ENTER;
534 l1 = strlen(s1);
535 l2 = strlen(s2);
536 news = (salloc)(l1 + l2 +1 SALLOC_DEBUG_ARGSCALL);
537 memcpy(news + 0, s1, l1);
538 memcpy(news + l1, s2, l2);
539 news[l1 + l2] = '\0';
540 NYD_LEAVE;
541 return news;
545 * Support routines, auto-reclaimed storage
548 FL char *
549 (i_strdup)(char const *src SALLOC_DEBUG_ARGS)
551 size_t sz;
552 char *dest;
553 NYD_ENTER;
555 sz = strlen(src) +1;
556 dest = (salloc)(sz SALLOC_DEBUG_ARGSCALL);
557 i_strcpy(dest, src, sz);
558 NYD_LEAVE;
559 return dest;
562 FL char *
563 (protbase)(char const *cp SALLOC_DEBUG_ARGS) /* TODO obsolete */
565 char *n, *np;
566 NYD_ENTER;
568 np = n = (salloc)(strlen(cp) +1 SALLOC_DEBUG_ARGSCALL);
570 /* Just ignore the `is-system-mailbox' prefix XXX */
571 if (cp[0] == '%' && cp[1] == ':')
572 cp += 2;
574 while (*cp != '\0') {
575 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
576 *np++ = *cp++;
577 *np++ = *cp++;
578 *np++ = *cp++;
579 } else if (cp[0] == '/')
580 break;
581 else
582 *np++ = *cp++;
584 *np = '\0';
585 NYD_LEAVE;
586 return n;
589 FL struct str *
590 str_concat_csvl(struct str *self, ...) /* XXX onepass maybe better here */
592 va_list vl;
593 size_t l;
594 char const *cs;
595 NYD_ENTER;
597 va_start(vl, self);
598 for (l = 0; (cs = va_arg(vl, char const*)) != NULL;)
599 l += strlen(cs);
600 va_end(vl);
602 self->l = l;
603 self->s = salloc(l +1);
605 va_start(vl, self);
606 for (l = 0; (cs = va_arg(vl, char const*)) != NULL;) {
607 size_t i = strlen(cs);
608 memcpy(self->s + l, cs, i);
609 l += i;
611 self->s[l] = '\0';
612 va_end(vl);
613 NYD_LEAVE;
614 return self;
617 #ifdef HAVE_SPAM
618 FL struct str *
619 (str_concat_cpa)(struct str *self, char const * const *cpa,
620 char const *sep_o_null SALLOC_DEBUG_ARGS)
622 size_t sonl, l;
623 char const * const *xcpa;
624 NYD_ENTER;
626 sonl = (sep_o_null != NULL) ? strlen(sep_o_null) : 0;
628 for (l = 0, xcpa = cpa; *xcpa != NULL; ++xcpa)
629 l += strlen(*xcpa) + sonl;
631 self->l = l;
632 self->s = (salloc)(l +1 SALLOC_DEBUG_ARGSCALL);
634 for (l = 0, xcpa = cpa; *xcpa != NULL; ++xcpa) {
635 size_t i = strlen(*xcpa);
636 memcpy(self->s + l, *xcpa, i);
637 l += i;
638 if (sonl > 0) {
639 memcpy(self->s + l, sep_o_null, sonl);
640 l += sonl;
643 self->s[l] = '\0';
644 NYD_LEAVE;
645 return self;
647 #endif
650 * Routines that are not related to auto-reclaimed storage follow.
653 FL int
654 anyof(char const *s1, char const *s2)
656 NYD2_ENTER;
657 for (; *s1 != '\0'; ++s1)
658 if (strchr(s2, *s1) != NULL)
659 break;
660 NYD2_LEAVE;
661 return (*s1 != '\0');
664 FL char *
665 n_strsep(char **iolist, char sep, bool_t ignore_empty)
667 char *base, *cp;
668 NYD2_ENTER;
670 for (base = *iolist; base != NULL; base = *iolist) {
671 while (*base != '\0' && blankspacechar(*base))
672 ++base;
673 cp = strchr(base, sep);
674 if (cp != NULL)
675 *iolist = cp + 1;
676 else {
677 *iolist = NULL;
678 cp = base + strlen(base);
680 while (cp > base && blankspacechar(cp[-1]))
681 --cp;
682 *cp = '\0';
683 if (*base != '\0' || !ignore_empty)
684 break;
686 NYD2_LEAVE;
687 return base;
690 FL void
691 i_strcpy(char *dest, char const *src, size_t size)
693 NYD2_ENTER;
694 if (size > 0) {
695 for (;; ++dest, ++src)
696 if ((*dest = lowerconv(*src)) == '\0') {
697 break;
698 } else if (--size == 0) {
699 *dest = '\0';
700 break;
703 NYD2_LEAVE;
706 FL int
707 is_prefix(char const *as1, char const *as2)
709 char c;
710 NYD2_ENTER;
712 for (; (c = *as1) == *as2 && c != '\0'; ++as1, ++as2)
713 if (*as2 == '\0')
714 break;
715 NYD2_LEAVE;
716 return (c == '\0');
719 FL char *
720 laststring(char *linebuf, bool_t *needs_list, bool_t strip)
722 char *cp, *p, quoted;
723 NYD_ENTER;
725 /* Anything to do at all? */
726 if (*(cp = linebuf) == '\0')
727 goto jnull;
728 cp += strlen(linebuf) -1;
730 /* Strip away trailing blanks */
731 while (whitechar(*cp) && cp > linebuf)
732 --cp;
733 cp[1] = '\0';
734 if (cp == linebuf)
735 goto jleave;
737 /* Now search for the BOS of the "last string" */
738 quoted = *cp;
739 if (quoted == '\'' || quoted == '"') {
740 if (strip)
741 *cp = '\0';
742 } else
743 quoted = ' ';
745 while (cp > linebuf) {
746 --cp;
747 if (quoted != ' ') {
748 if (*cp != quoted)
749 continue;
750 } else if (!whitechar(*cp))
751 continue;
752 if (cp == linebuf || cp[-1] != '\\') {
753 /* When in whitespace mode, WS prefix doesn't belong */
754 if (quoted == ' ')
755 ++cp;
756 break;
758 /* Expand the escaped quote character */
759 for (p = --cp; (p[0] = p[1]) != '\0'; ++p)
762 if (strip && quoted != ' ' && *cp == quoted)
763 for (p = cp; (p[0] = p[1]) != '\0'; ++p)
766 /* The "last string" has been skipped over, but still, try to step backwards
767 * until we are at BOS or see whitespace, so as to make possible things like
768 * "? copy +'x y.mbox'" or even "? copy +x\ y.mbox" */
769 while (cp > linebuf) {
770 --cp;
771 if (whitechar(*cp)) {
772 p = cp;
773 *cp++ = '\0';
774 /* We can furtherly release our callees if we now decide wether the
775 * remaining non-"last string" line content contains non-WS */
776 while (--p >= linebuf)
777 if (!whitechar(*p))
778 goto jleave;
779 linebuf = cp;
780 break;
784 jleave:
785 if (cp != NULL && *cp == '\0')
786 goto jnull;
787 *needs_list = (cp != linebuf && *linebuf != '\0');
788 j_leave:
789 NYD_LEAVE;
790 return cp;
791 jnull:
792 *needs_list = FAL0;
793 cp = NULL;
794 goto j_leave;
797 FL void
798 makelow(char *cp) /* TODO isn't that crap? --> */
800 NYD_ENTER;
801 #ifdef HAVE_C90AMEND1
802 if (mb_cur_max > 1) {
803 char *tp = cp;
804 wchar_t wc;
805 int len;
807 while (*cp != '\0') {
808 len = mbtowc(&wc, cp, mb_cur_max);
809 if (len < 0)
810 *tp++ = *cp++;
811 else {
812 wc = towlower(wc);
813 if (wctomb(tp, wc) == len)
814 tp += len, cp += len;
815 else
816 *tp++ = *cp++; /* <-- at least here */
819 } else
820 #endif
823 *cp = tolower((uc_it)*cp);
824 while (*cp++ != '\0');
826 NYD_LEAVE;
829 FL bool_t
830 substr(char const *str, char const *sub)
832 char const *cp, *backup;
833 NYD_ENTER;
835 cp = sub;
836 backup = str;
837 while (*str != '\0' && *cp != '\0') {
838 #ifdef HAVE_C90AMEND1
839 if (mb_cur_max > 1) {
840 wchar_t c, c2;
841 int sz;
843 if ((sz = mbtowc(&c, cp, mb_cur_max)) == -1)
844 goto Jsinglebyte;
845 cp += sz;
846 if ((sz = mbtowc(&c2, str, mb_cur_max)) == -1)
847 goto Jsinglebyte;
848 str += sz;
849 c = towupper(c);
850 c2 = towupper(c2);
851 if (c != c2) {
852 if ((sz = mbtowc(&c, backup, mb_cur_max)) > 0) {
853 backup += sz;
854 str = backup;
855 } else
856 str = ++backup;
857 cp = sub;
859 } else
860 Jsinglebyte:
861 #endif
863 int c, c2;
865 c = *cp++ & 0377;
866 if (islower(c))
867 c = toupper(c);
868 c2 = *str++ & 0377;
869 if (islower(c2))
870 c2 = toupper(c2);
871 if (c != c2) {
872 str = ++backup;
873 cp = sub;
877 NYD_LEAVE;
878 return (*cp == '\0');
881 #ifndef HAVE_SNPRINTF
882 FL int
883 snprintf(char *str, size_t size, char const *format, ...) /* XXX DANGER! */
885 va_list ap;
886 int ret;
887 NYD2_ENTER;
889 va_start(ap, format);
890 ret = vsprintf(str, format, ap);
891 va_end(ap);
892 if (ret < 0)
893 ret = strlen(str);
894 NYD2_LEAVE;
895 return ret;
897 #endif
899 FL char *
900 sstpcpy(char *dst, char const *src)
902 NYD2_ENTER;
903 while ((*dst = *src++) != '\0')
904 ++dst;
905 NYD2_LEAVE;
906 return dst;
909 FL char *
910 (sstrdup)(char const *cp SMALLOC_DEBUG_ARGS)
912 char *dp;
913 NYD2_ENTER;
915 dp = (cp == NULL) ? NULL : (sbufdup)(cp, strlen(cp) SMALLOC_DEBUG_ARGSCALL);
916 NYD2_LEAVE;
917 return dp;
920 FL char *
921 (sbufdup)(char const *cp, size_t len SMALLOC_DEBUG_ARGS)
923 char *dp = NULL;
924 NYD2_ENTER;
926 dp = (smalloc)(len +1 SMALLOC_DEBUG_ARGSCALL);
927 if (cp != NULL)
928 memcpy(dp, cp, len);
929 dp[len] = '\0';
930 NYD2_LEAVE;
931 return dp;
934 FL char *
935 n_strlcpy(char *dst, char const *src, size_t len)
937 NYD2_ENTER;
939 assert(len > 0);
941 dst = strncpy(dst, src, len);
942 dst[len -1] = '\0';
943 NYD2_LEAVE;
944 return dst;
947 FL int
948 asccasecmp(char const *s1, char const *s2)
950 int cmp;
951 NYD2_ENTER;
953 for (;;) {
954 char c1 = *s1++, c2 = *s2++;
955 if ((cmp = lowerconv(c1) - lowerconv(c2)) != 0 || c1 == '\0')
956 break;
958 NYD2_LEAVE;
959 return cmp;
962 FL int
963 ascncasecmp(char const *s1, char const *s2, size_t sz)
965 int cmp = 0;
966 NYD2_ENTER;
968 while (sz-- > 0) {
969 char c1 = *s1++, c2 = *s2++;
970 cmp = (ui8_t)lowerconv(c1);
971 cmp -= (ui8_t)lowerconv(c2);
972 if (cmp != 0 || c1 == '\0')
973 break;
975 NYD2_LEAVE;
976 return cmp;
979 FL bool_t
980 is_asccaseprefix(char const *as1, char const *as2)
982 bool_t rv = FAL0;
983 NYD2_ENTER;
985 for (;; ++as1, ++as2) {
986 char c1 = lowerconv(*as1), c2 = lowerconv(*as2);
987 if ((rv = (c1 == '\0')))
988 break;
989 if (c1 != c2 || c2 == '\0')
990 break;
992 NYD2_LEAVE;
993 return rv;
996 #ifdef HAVE_IMAP
997 FL char const *
998 asccasestr(char const *haystack, char const *xneedle)
1000 char *needle = NULL, *NEEDLE;
1001 size_t i, sz;
1002 NYD2_ENTER;
1004 sz = strlen(xneedle);
1005 if (sz == 0)
1006 goto jleave;
1008 needle = ac_alloc(sz);
1009 NEEDLE = ac_alloc(sz);
1010 for (i = 0; i < sz; i++) {
1011 needle[i] = lowerconv(xneedle[i]);
1012 NEEDLE[i] = upperconv(xneedle[i]);
1015 while (*haystack != '\0') {
1016 if (*haystack == *needle || *haystack == *NEEDLE) {
1017 for (i = 1; i < sz; ++i)
1018 if (haystack[i] != needle[i] && haystack[i] != NEEDLE[i])
1019 break;
1020 if (i == sz)
1021 goto jleave;
1023 ++haystack;
1025 haystack = NULL;
1026 jleave:
1027 if (needle != NULL) {
1028 ac_free(NEEDLE);
1029 ac_free(needle);
1031 NYD2_LEAVE;
1032 return haystack;
1034 #endif
1036 FL struct str *
1037 (n_str_dup)(struct str *self, struct str const *t SMALLOC_DEBUG_ARGS)
1039 NYD_ENTER;
1040 if (t != NULL && t->l > 0) {
1041 self->l = t->l;
1042 self->s = (srealloc)(self->s, t->l +1 SMALLOC_DEBUG_ARGSCALL);
1043 memcpy(self->s, t->s, t->l +1);
1044 } else
1045 self->l = 0;
1046 NYD_LEAVE;
1047 return self;
1050 FL struct str *
1051 (n_str_add_buf)(struct str *self, char const *buf, size_t buflen
1052 SMALLOC_DEBUG_ARGS)
1054 NYD_ENTER;
1055 if (buflen != 0) {
1056 size_t sl = self->l;
1057 self->l = sl + buflen;
1058 self->s = (srealloc)(self->s, self->l +1 SMALLOC_DEBUG_ARGSCALL);
1059 memcpy(self->s + sl, buf, buflen);
1060 self->s[self->l] = '\0';
1062 NYD_LEAVE;
1063 return self;
1067 * Our iconv(3) wrapper
1069 #ifdef HAVE_ICONV
1071 static void _ic_toupper(char *dest, char const *src);
1072 static void _ic_stripdash(char *p);
1074 static void
1075 _ic_toupper(char *dest, char const *src)
1077 NYD2_ENTER;
1079 *dest++ = upperconv(*src);
1080 while (*src++ != '\0');
1081 NYD2_LEAVE;
1084 static void
1085 _ic_stripdash(char *p)
1087 char *q = p;
1088 NYD2_ENTER;
1091 if (*(q = p) != '-')
1092 ++q;
1093 while (*p++ != '\0');
1094 NYD2_LEAVE;
1097 FL iconv_t
1098 n_iconv_open(char const *tocode, char const *fromcode)
1100 iconv_t id;
1101 char *t, *f;
1102 NYD_ENTER;
1104 if ((id = iconv_open(tocode, fromcode)) != (iconv_t)-1)
1105 goto jleave;
1107 /* Remove the "iso-" prefixes for Solaris */
1108 if (!ascncasecmp(tocode, "iso-", 4))
1109 tocode += 4;
1110 else if (!ascncasecmp(tocode, "iso", 3))
1111 tocode += 3;
1112 if (!ascncasecmp(fromcode, "iso-", 4))
1113 fromcode += 4;
1114 else if (!ascncasecmp(fromcode, "iso", 3))
1115 fromcode += 3;
1116 if (*tocode == '\0' || *fromcode == '\0') {
1117 id = (iconv_t)-1;
1118 goto jleave;
1120 if ((id = iconv_open(tocode, fromcode)) != (iconv_t)-1)
1121 goto jleave;
1123 /* Solaris prefers upper-case charset names. Don't ask... */
1124 t = salloc(strlen(tocode) +1);
1125 _ic_toupper(t, tocode);
1126 f = salloc(strlen(fromcode) +1);
1127 _ic_toupper(f, fromcode);
1128 if ((id = iconv_open(t, f)) != (iconv_t)-1)
1129 goto jleave;
1131 /* Strip dashes for UnixWare */
1132 _ic_stripdash(t);
1133 _ic_stripdash(f);
1134 if ((id = iconv_open(t, f)) != (iconv_t)-1)
1135 goto jleave;
1137 /* Add your vendor's sillynesses here */
1139 /* If the encoding names are equal at this point, they are just not
1140 * understood by iconv(), and we cannot sensibly use it in any way. We do
1141 * not perform this as an optimization above since iconv() can otherwise be
1142 * used to check the validity of the input even with identical encoding
1143 * names */
1144 if (!strcmp(t, f))
1145 errno = 0;
1146 jleave:
1147 NYD_LEAVE;
1148 return id;
1151 FL void
1152 n_iconv_close(iconv_t cd)
1154 NYD_ENTER;
1155 iconv_close(cd);
1156 if (cd == iconvd)
1157 iconvd = (iconv_t)-1;
1158 NYD_LEAVE;
1161 #ifdef notyet
1162 FL void
1163 n_iconv_reset(iconv_t cd)
1165 NYD_ENTER;
1166 iconv(cd, NULL, NULL, NULL, NULL);
1167 NYD_LEAVE;
1169 #endif
1171 /* (2012-09-24: export and use it exclusively to isolate prototype problems
1172 * (*inb* is 'char const **' except in POSIX) in a single place.
1173 * GNU libiconv even allows for configuration time const/non-const..
1174 * In the end it's an ugly guess, but we can't do better since make(1) doesn't
1175 * support compiler invocations which bail on error, so no -Werror */
1176 /* Citrus project? */
1177 # if defined _ICONV_H_ && defined __ICONV_F_HIDE_INVALID
1178 /* DragonFly 3.2.1 is special TODO newer DragonFly too, but different */
1179 # ifdef __DragonFly__
1180 # define __INBCAST(S) (char ** __restrict__)UNCONST(S)
1181 # else
1182 # define __INBCAST(S) (char const **)UNCONST(S)
1183 # endif
1184 # endif
1185 # ifndef __INBCAST
1186 # define __INBCAST(S) (char **)UNCONST(S)
1187 # endif
1189 FL int
1190 n_iconv_buf(iconv_t cd, char const **inb, size_t *inbleft,/*XXX redo iconv use*/
1191 char **outb, size_t *outbleft, bool_t skipilseq)
1193 int err = 0;
1194 NYD2_ENTER;
1196 for (;;) {
1197 size_t sz = iconv(cd, __INBCAST(inb), inbleft, outb, outbleft);
1198 if (sz != (size_t)-1)
1199 break;
1200 err = errno;
1201 if (!skipilseq || err != EILSEQ)
1202 break;
1203 if (*inbleft > 0) {
1204 ++(*inb);
1205 --(*inbleft);
1206 } else if (*outbleft > 0) {
1207 **outb = '\0';
1208 break;
1210 if (*outbleft > 0/* TODO 0xFFFD 2*/) {
1211 /* TODO 0xFFFD (*outb)[0] = '[';
1212 * TODO (*outb)[1] = '?';
1213 * TODO 0xFFFD (*outb)[2] = ']';
1214 * TODO (*outb) += 3;
1215 * TODO (*outbleft) -= 3; */
1216 *(*outb)++ = '?';
1217 --*outbleft;
1218 } else {
1219 err = E2BIG;
1220 break;
1222 err = 0;
1224 NYD2_LEAVE;
1225 return err;
1227 # undef __INBCAST
1229 FL int
1230 n_iconv_str(iconv_t cd, struct str *out, struct str const *in,
1231 struct str *in_rest_or_null, bool_t skipilseq)
1233 int err;
1234 char *obb, *ob;
1235 char const *ib;
1236 size_t olb, ol, il;
1237 NYD2_ENTER;
1239 err = 0;
1240 obb = out->s;
1241 olb = out->l;
1242 ol = in->l;
1244 ol = (ol << 1) - (ol >> 4);
1245 if (olb < ol) {
1246 olb = ol;
1247 goto jrealloc;
1250 for (;;) {
1251 ib = in->s;
1252 il = in->l;
1253 ob = obb;
1254 ol = olb;
1255 err = n_iconv_buf(cd, &ib, &il, &ob, &ol, skipilseq);
1256 if (err == 0 || err != E2BIG)
1257 break;
1258 err = 0;
1259 olb += in->l;
1260 jrealloc:
1261 obb = srealloc(obb, olb);
1264 if (in_rest_or_null != NULL) {
1265 in_rest_or_null->s = UNCONST(ib);
1266 in_rest_or_null->l = il;
1268 out->s = obb;
1269 out->l = olb - ol;
1270 NYD2_LEAVE;
1271 return err;
1273 #endif /* HAVE_ICONV */
1275 /* s-it-mode */