run_editor(): also compare size when deciding "has changed"
[s-mailx.git] / strings.c
blob1c4557f11c0caf58a3830668946a96f727dca13d
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 (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;
210 if (size >= 2048)
211 alert("salloc() of %" ZFMT " bytes from `%s', line %u\n",
212 size, mdbg_file, mdbg_line);
213 #endif
215 /* Search for a buffer with enough free space to serve request */
216 if ((u.b = _buf_server) != NULL)
217 goto jumpin;
218 jredo:
219 for (u.b = _buf_head; u.b != NULL; u.b = u.b->b._next) {
220 jumpin:
221 x = u.b->b._caster;
222 if (x == NULL) {
223 if (u.b == _buf_server) {
224 if (u.b == _buf_head && (u.b = _buf_head->b._next) != NULL) {
225 _buf_server = u.b;
226 goto jumpin;
228 _buf_server = NULL;
229 goto jredo;
231 continue;
233 y = x + size;
234 z = u.b->b._max;
235 if (PTRCMP(y, <=, z)) {
236 /* Alignment is the one thing, the other is what is usually allocated,
237 * and here about 40 bytes seems to be a good cut to avoid non-usable
238 * non-NULL casters. However, because of _salloc_bcheck(), we may not
239 * set ._caster to NULL because then it would check all chunks up to
240 * ._max, which surely doesn't work; speed is no issue with DEBUG */
241 u.b->b._caster = NDBG( PTRCMP(y + 42 + 16, >=, z) ? NULL : ) y;
242 u.cp = x;
243 goto jleave;
247 /* Need a new buffer */
248 if (_buf_head == NULL) {
249 struct b_bltin *b = &_builtin_buf;
250 b->b_base._max = b->b_buf + sizeof(b->b_buf) - 1;
251 _buf_head = (struct buffer*)b;
252 u.b = _buf_head;
253 } else {
254 #ifdef HAVE_DEBUG
255 ++_all_bufcnt;
256 ++_all_cybufcnt;
257 _all_cybufcnt_max = MAX(_all_cybufcnt_max, _all_cybufcnt);
258 #endif
259 u.b = smalloc(sizeof(struct b_dyn));
260 u.b->b._max = u.b->b_buf + SDYN_SIZE - 1;
262 if (_buf_list != NULL)
263 _buf_list->b._next = u.b;
264 _buf_server = _buf_list = u.b;
265 u.b->b._next = NULL;
266 u.b->b._caster = (u.b->b._bot = u.b->b_buf) + size;
267 u.b->b._relax = NULL;
268 u.cp = u.b->b._bot;
270 jleave:
271 /* Encapsulate user chunk in debug canaries */
272 #ifdef HAVE_DEBUG
274 union sptr xl, xu;
275 struct schunk *xc;
277 xl.p = u.cp;
278 xc = xl.c;
279 xc->file = mdbg_file;
280 xc->line = mdbg_line;
281 xc->usr_size = (ui16_t)orig_size;
282 xc->full_size = (ui16_t)size;
283 xl.p = xc + 1;
284 xl.ui8p[0]=0xDE; xl.ui8p[1]=0xAA; xl.ui8p[2]=0x55; xl.ui8p[3]=0xAD;
285 xl.ui8p[4]=0xBE; xl.ui8p[5]=0x55; xl.ui8p[6]=0xAA; xl.ui8p[7]=0xEF;
286 u.cp = xl.cp + 8;
287 xu.p = u.cp;
288 xu.cp += orig_size;
289 xu.ui8p[0]=0xDE; xu.ui8p[1]=0xAA; xu.ui8p[2]=0x55; xu.ui8p[3]=0xAD;
290 xu.ui8p[4]=0xBE; xu.ui8p[5]=0x55; xu.ui8p[6]=0xAA; xu.ui8p[7]=0xEF;
292 #endif
293 NYD_LEAVE;
294 return u.cp;
297 FL void *
298 (csalloc)(size_t nmemb, size_t size SALLOC_DEBUG_ARGS)
300 void *vp;
301 NYD_ENTER;
303 size *= nmemb;
304 vp = (salloc)(size SALLOC_DEBUG_ARGSCALL);
305 memset(vp, 0, size);
306 NYD_LEAVE;
307 return vp;
310 FL void
311 sreset(bool_t only_if_relaxed)
313 struct buffer *bh;
314 NYD_ENTER;
316 DBG( ++_all_resetreqs; )
317 if (noreset || (only_if_relaxed && _buf_relax == NULL))
318 goto jleave;
320 #ifdef HAVE_DEBUG
321 _all_cycnt = _all_cysize = 0;
322 _all_cybufcnt = (_buf_head != NULL && _buf_head->b._next != NULL);
323 ++_all_resets;
324 #endif
326 if ((bh = _buf_head) != NULL) {
327 struct buffer *b = bh;
328 DBG( _salloc_bcheck(b); )
329 b->b._caster = b->b._bot;
330 b->b._relax = NULL;
331 DBG( memset(b->b._caster, 0377, PTR2SIZE(b->b._max - b->b._caster)); )
332 _buf_server = b;
334 if ((bh = bh->b._next) != NULL) {
335 b = bh;
336 DBG( _salloc_bcheck(b); )
337 b->b._caster = b->b._bot;
338 b->b._relax = NULL;
339 DBG( memset(b->b._caster, 0377, PTR2SIZE(b->b._max - b->b._caster)); )
341 for (bh = bh->b._next; bh != NULL;) {
342 struct buffer *b2 = bh->b._next;
343 DBG( _salloc_bcheck(bh); )
344 free(bh);
345 bh = b2;
348 _buf_list = b;
349 b->b._next = NULL;
350 _buf_relax = NULL;
353 DBG( smemreset(); )
354 jleave:
355 NYD_LEAVE;
358 FL void
359 srelax_hold(void)
361 struct buffer *b;
362 NYD_ENTER;
364 assert(_buf_relax == NULL);
366 for (b = _buf_head; b != NULL; b = b->b._next)
367 b->b._relax = b->b._caster;
368 _buf_relax = _buf_server;
369 assert(_buf_relax != NULL);
370 NYD_LEAVE;
373 FL void
374 srelax_rele(void)
376 struct buffer *b;
377 NYD_ENTER;
379 assert(_buf_relax != NULL);
381 for (b = _buf_relax; b != NULL; b = b->b._next) {
382 DBG( _salloc_bcheck(b); )
383 b->b._caster = (b->b._relax != NULL) ? b->b._relax : b->b._bot;
384 b->b._relax = NULL;
386 _buf_relax = NULL;
387 NYD_LEAVE;
390 FL void
391 srelax(void)
393 /* The purpose of relaxation is only that it is possible to reset the
394 * casters, *not* to give back memory to the system. We are presumably in
395 * an iteration over all messages of a mailbox, and it'd be quite
396 * counterproductive to give the system allocator a chance to waste time */
397 struct buffer *b;
398 NYD_ENTER;
400 assert(_buf_relax != NULL);
402 for (b = _buf_relax; b != NULL; b = b->b._next) {
403 DBG( _salloc_bcheck(b); )
404 b->b._caster = (b->b._relax != NULL) ? b->b._relax : b->b._bot;
405 DBG( memset(b->b._caster, 0377, PTR2SIZE(b->b._max - b->b._caster)); )
407 NYD_LEAVE;
410 FL void
411 spreserve(void)
413 struct buffer *b;
414 NYD_ENTER;
416 for (b = _buf_head; b != NULL; b = b->b._next)
417 b->b._bot = b->b._caster;
418 NYD_LEAVE;
421 #ifdef HAVE_DEBUG
422 FL int
423 c_sstats(void *v)
425 size_t excess;
426 NYD_ENTER;
427 UNUSED(v);
429 excess = (_all_cybufcnt_max * SDYN_SIZE) + SBLTIN_SIZE;
430 excess = (excess >= _all_cysize_max) ? 0 : _all_cysize_max - excess;
432 printf("String usage statistics (cycle means one sreset() cycle):\n"
433 " Buffer allocs ever/max simultan. : %lu/%lu\n"
434 " Buffer size of builtin(1)/dynamic: %lu/%lu\n"
435 " Overall alloc count/bytes : %lu/%lu\n"
436 " Alloc bytes min/max/align wastage: %lu/%lu/%lu\n"
437 " sreset() cycles : %lu (%lu performed)\n"
438 " Cycle maximums: alloc count/bytes: %lu/%lu+%lu\n",
439 (ul_it)_all_bufcnt, (ul_it)_all_cybufcnt_max,
440 (ul_it)SBLTIN_SIZE, (ul_it)SDYN_SIZE,
441 (ul_it)_all_cnt, (ul_it)_all_size,
442 (ul_it)_all_min, (ul_it)_all_max, (ul_it)_all_wast,
443 (ul_it)_all_resetreqs, (ul_it)_all_resets,
444 (ul_it)_all_cycnt_max, (ul_it)_all_cysize_max, (ul_it)excess);
445 NYD_LEAVE;
446 return 0;
448 #endif
450 FL char *
451 (savestr)(char const *str SALLOC_DEBUG_ARGS)
453 size_t size;
454 char *news;
455 NYD_ENTER;
457 size = strlen(str) +1;
458 news = (salloc)(size SALLOC_DEBUG_ARGSCALL);
459 memcpy(news, str, size);
460 NYD_LEAVE;
461 return news;
464 FL char *
465 (savestrbuf)(char const *sbuf, size_t sbuf_len SALLOC_DEBUG_ARGS)
467 char *news;
468 NYD_ENTER;
470 news = (salloc)(sbuf_len +1 SALLOC_DEBUG_ARGSCALL);
471 memcpy(news, sbuf, sbuf_len);
472 news[sbuf_len] = 0;
473 NYD_LEAVE;
474 return news;
477 FL char *
478 (save2str)(char const *str, char const *old SALLOC_DEBUG_ARGS)
480 size_t newsize, oldsize;
481 char *news;
482 NYD_ENTER;
484 newsize = strlen(str) +1;
485 oldsize = (old != NULL) ? strlen(old) + 1 : 0;
486 news = (salloc)(newsize + oldsize SALLOC_DEBUG_ARGSCALL);
487 if (oldsize) {
488 memcpy(news, old, oldsize);
489 news[oldsize - 1] = ' ';
491 memcpy(news + oldsize, str, newsize);
492 NYD_LEAVE;
493 return news;
496 FL char *
497 (savecat)(char const *s1, char const *s2 SALLOC_DEBUG_ARGS)
499 size_t l1, l2;
500 char *news;
501 NYD_ENTER;
503 l1 = strlen(s1);
504 l2 = strlen(s2);
505 news = (salloc)(l1 + l2 +1 SALLOC_DEBUG_ARGSCALL);
506 memcpy(news + 0, s1, l1);
507 memcpy(news + l1, s2, l2);
508 news[l1 + l2] = '\0';
509 NYD_LEAVE;
510 return news;
514 * Support routines, auto-reclaimed storage
517 FL char *
518 (i_strdup)(char const *src SALLOC_DEBUG_ARGS)
520 size_t sz;
521 char *dest;
522 NYD_ENTER;
524 sz = strlen(src) +1;
525 dest = (salloc)(sz SALLOC_DEBUG_ARGSCALL);
526 i_strcpy(dest, src, sz);
527 NYD_LEAVE;
528 return dest;
531 FL char *
532 (protbase)(char const *cp SALLOC_DEBUG_ARGS) /* TODO obsolete */
534 char *n, *np;
535 NYD_ENTER;
537 np = n = (salloc)(strlen(cp) +1 SALLOC_DEBUG_ARGSCALL);
539 /* Just ignore the `is-system-mailbox' prefix XXX */
540 if (cp[0] == '%' && cp[1] == ':')
541 cp += 2;
543 while (*cp != '\0') {
544 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
545 *np++ = *cp++;
546 *np++ = *cp++;
547 *np++ = *cp++;
548 } else if (cp[0] == '/')
549 break;
550 else
551 *np++ = *cp++;
553 *np = '\0';
554 NYD_LEAVE;
555 return n;
558 FL char *
559 (urlxenc)(char const *cp, bool_t ispath SALLOC_DEBUG_ARGS) /* XXX (->URL) */
561 char *n, *np, c1, c2;
562 NYD_ENTER;
564 np = n = (salloc)(strlen(cp) * 3 +1 SALLOC_DEBUG_ARGSCALL);
566 for (; (c1 = *cp) != '\0'; ++cp) {
567 /* RFC 3986, 2.3 Unreserved Characters:
568 * ALPHA / DIGIT / "-" / "." / "_" / "~"
569 * However add a special is[file]path mode for file-system friendliness */
570 if (alnumchar(c1) || c1 == '_')
571 *np++ = c1;
572 else if (!ispath) {
573 if (c1 != '-' && c1 != '.' && c1 != '~')
574 goto jesc;
575 *np++ = c1;
576 } else if (PTRCMP(np, >, n) && (*cp == '-' || *cp == '.')) /* XXX imap */
577 *np++ = c1;
578 else {
579 jesc:
580 np[0] = '%';
581 c2 = c1 & 0x0F;
582 c2 += (c2 > 9) ? 'A' - 10 : '0';
583 np[2] = c2;
584 c1 = (ui8_t)(c1 & 0xF0) >> 4;
585 c1 += (c1 > 9) ? 'A' - 10 : '0';
586 np[1] = c1;
587 np += 3;
590 *np = '\0';
591 NYD_LEAVE;
592 return n;
595 FL char *
596 (urlxdec)(char const *cp SALLOC_DEBUG_ARGS) /* XXX (->URL (yet auxlily.c)) */
598 char *n, *np, c1, c2;
599 NYD_ENTER;
601 np = n = (salloc)(strlen(cp) +1 SALLOC_DEBUG_ARGSCALL);
603 while (*cp != '\0') {
604 if (cp[0] == '%' && (c1 = cp[1]) != '\0' && (c2 = cp[2]) != '\0') {
605 c1 -= (c1 <= '9') ? '0' : 'A' - 10;
606 c1 <<= 4;
607 c2 -= (c2 <= '9') ? '0' : 'A' - 10;
608 *np = c1;
609 *np++ |= c2;
610 cp += 3;
611 } else
612 *np++ = *cp++;
614 *np = '\0';
615 NYD_LEAVE;
616 return n;
619 FL struct str *
620 str_concat_csvl(struct str *self, ...) /* XXX onepass maybe better here */
622 va_list vl;
623 size_t l;
624 char const *cs;
625 NYD_ENTER;
627 va_start(vl, self);
628 for (l = 0; (cs = va_arg(vl, char const*)) != NULL;)
629 l += strlen(cs);
630 va_end(vl);
632 self->l = l;
633 self->s = salloc(l +1);
635 va_start(vl, self);
636 for (l = 0; (cs = va_arg(vl, char const*)) != NULL;) {
637 size_t i = strlen(cs);
638 memcpy(self->s + l, cs, i);
639 l += i;
641 self->s[l] = '\0';
642 va_end(vl);
643 NYD_LEAVE;
644 return self;
647 #ifdef HAVE_SPAM
648 FL struct str *
649 (str_concat_cpa)(struct str *self, char const * const *cpa,
650 char const *sep_o_null SALLOC_DEBUG_ARGS)
652 size_t sonl, l;
653 char const * const *xcpa;
654 NYD_ENTER;
656 sonl = (sep_o_null != NULL) ? strlen(sep_o_null) : 0;
658 for (l = 0, xcpa = cpa; *xcpa != NULL; ++xcpa)
659 l += strlen(*xcpa) + sonl;
661 self->l = l;
662 self->s = (salloc)(l +1 SALLOC_DEBUG_ARGSCALL);
664 for (l = 0, xcpa = cpa; *xcpa != NULL; ++xcpa) {
665 size_t i = strlen(*xcpa);
666 memcpy(self->s + l, *xcpa, i);
667 l += i;
668 if (sonl > 0) {
669 memcpy(self->s + l, sep_o_null, sonl);
670 l += sonl;
673 self->s[l] = '\0';
674 NYD_LEAVE;
675 return self;
677 #endif
680 * Routines that are not related to auto-reclaimed storage follow.
683 FL int
684 anyof(char const *s1, char const *s2)
686 NYD_ENTER;
687 for (; *s1 != '\0'; ++s1)
688 if (strchr(s2, *s1) != NULL)
689 break;
690 NYD_LEAVE;
691 return (*s1 != '\0');
694 FL char *
695 n_strsep(char **iolist, char sep, bool_t ignore_empty)
697 char *base, *cp;
698 NYD_ENTER;
700 for (base = *iolist; base != NULL; base = *iolist) {
701 while (*base != '\0' && blankspacechar(*base))
702 ++base;
703 cp = strchr(base, sep);
704 if (cp != NULL)
705 *iolist = cp + 1;
706 else {
707 *iolist = NULL;
708 cp = base + strlen(base);
710 while (cp > base && blankspacechar(cp[-1]))
711 --cp;
712 *cp = '\0';
713 if (*base != '\0' || !ignore_empty)
714 break;
716 NYD_LEAVE;
717 return base;
720 FL void
721 i_strcpy(char *dest, char const *src, size_t size)
723 NYD_ENTER;
724 if (size > 0) {
725 for (;; ++dest, ++src)
726 if ((*dest = lowerconv(*src)) == '\0') {
727 break;
728 } else if (--size == 0) {
729 *dest = '\0';
730 break;
733 NYD_LEAVE;
736 FL int
737 is_prefix(char const *as1, char const *as2)
739 char c;
740 NYD_ENTER;
742 for (; (c = *as1) == *as2 && c != '\0'; ++as1, ++as2)
743 if (*as2 == '\0')
744 break;
745 NYD_LEAVE;
746 return (c == '\0');
749 FL char const *
750 last_at_before_slash(char const *sp)/* XXX (->URL (yet auxlily.c) / obsolete) */
752 char const *cp;
753 char c;
754 NYD_ENTER;
756 for (cp = sp; (c = *cp) != '\0'; ++cp)
757 if (c == '/')
758 break;
759 while (cp > sp && *--cp != '@')
761 if (*cp != '@')
762 cp = NULL;
763 NYD_LEAVE;
764 return cp;
767 FL char *
768 laststring(char *linebuf, bool_t *needs_list, bool_t strip)
770 char *cp, *p, quoted;
771 NYD_ENTER;
773 /* Anything to do at all? */
774 if (*(cp = linebuf) == '\0')
775 goto jnull;
776 cp += strlen(linebuf) -1;
778 /* Strip away trailing blanks */
779 while (whitechar(*cp) && cp > linebuf)
780 --cp;
781 cp[1] = '\0';
782 if (cp == linebuf)
783 goto jleave;
785 /* Now search for the BOS of the "last string" */
786 quoted = *cp;
787 if (quoted == '\'' || quoted == '"') {
788 if (strip)
789 *cp = '\0';
790 } else
791 quoted = ' ';
793 while (cp > linebuf) {
794 --cp;
795 if (quoted != ' ') {
796 if (*cp != quoted)
797 continue;
798 } else if (!whitechar(*cp))
799 continue;
800 if (cp == linebuf || cp[-1] != '\\') {
801 /* When in whitespace mode, WS prefix doesn't belong */
802 if (quoted == ' ')
803 ++cp;
804 break;
806 /* Expand the escaped quote character */
807 for (p = --cp; (p[0] = p[1]) != '\0'; ++p)
810 if (strip && quoted != ' ' && *cp == quoted)
811 for (p = cp; (p[0] = p[1]) != '\0'; ++p)
814 /* The "last string" has been skipped over, but still, try to step backwards
815 * until we are at BOS or see whitespace, so as to make possible things like
816 * "? copy +'x y.mbox'" or even "? copy +x\ y.mbox" */
817 while (cp > linebuf) {
818 --cp;
819 if (whitechar(*cp)) {
820 p = cp;
821 *cp++ = '\0';
822 /* We can furtherly release our callees if we now decide wether the
823 * remaining non-"last string" line content contains non-WS */
824 while (--p >= linebuf)
825 if (!whitechar(*p))
826 goto jleave;
827 linebuf = cp;
828 break;
832 jleave:
833 if (cp != NULL && *cp == '\0')
834 goto jnull;
835 *needs_list = (cp != linebuf && *linebuf != '\0');
836 j_leave:
837 NYD_LEAVE;
838 return cp;
839 jnull:
840 *needs_list = FAL0;
841 cp = NULL;
842 goto j_leave;
845 FL void
846 makelow(char *cp) /* TODO isn't that crap? --> */
848 NYD_ENTER;
849 #ifdef HAVE_C90AMEND1
850 if (mb_cur_max > 1) {
851 char *tp = cp;
852 wchar_t wc;
853 int len;
855 while (*cp != '\0') {
856 len = mbtowc(&wc, cp, mb_cur_max);
857 if (len < 0)
858 *tp++ = *cp++;
859 else {
860 wc = towlower(wc);
861 if (wctomb(tp, wc) == len)
862 tp += len, cp += len;
863 else
864 *tp++ = *cp++; /* <-- at least here */
867 } else
868 #endif
871 *cp = tolower((uc_it)*cp);
872 while (*cp++ != '\0');
874 NYD_LEAVE;
877 FL bool_t
878 substr(char const *str, char const *sub)
880 char const *cp, *backup;
881 NYD_ENTER;
883 cp = sub;
884 backup = str;
885 while (*str != '\0' && *cp != '\0') {
886 #ifdef HAVE_C90AMEND1
887 if (mb_cur_max > 1) {
888 wchar_t c, c2;
889 int sz;
891 if ((sz = mbtowc(&c, cp, mb_cur_max)) == -1)
892 goto Jsinglebyte;
893 cp += sz;
894 if ((sz = mbtowc(&c2, str, mb_cur_max)) == -1)
895 goto Jsinglebyte;
896 str += sz;
897 c = towupper(c);
898 c2 = towupper(c2);
899 if (c != c2) {
900 if ((sz = mbtowc(&c, backup, mb_cur_max)) > 0) {
901 backup += sz;
902 str = backup;
903 } else
904 str = ++backup;
905 cp = sub;
907 } else
908 Jsinglebyte:
909 #endif
911 int c, c2;
913 c = *cp++ & 0377;
914 if (islower(c))
915 c = toupper(c);
916 c2 = *str++ & 0377;
917 if (islower(c2))
918 c2 = toupper(c2);
919 if (c != c2) {
920 str = ++backup;
921 cp = sub;
925 NYD_LEAVE;
926 return (*cp == '\0');
929 #ifndef HAVE_SNPRINTF
930 FL int
931 snprintf(char *str, size_t size, char const *format, ...) /* XXX DANGER! */
933 va_list ap;
934 int ret;
935 NYD_ENTER;
937 va_start(ap, format);
938 ret = vsprintf(str, format, ap);
939 va_end(ap);
940 if (ret < 0)
941 ret = strlen(str);
942 NYD_LEAVE;
943 return ret;
945 #endif
947 FL char *
948 sstpcpy(char *dst, char const *src)
950 NYD_ENTER;
951 while ((*dst = *src++) != '\0')
952 ++dst;
953 NYD_LEAVE;
954 return dst;
957 FL char *
958 (sstrdup)(char const *cp SMALLOC_DEBUG_ARGS)
960 char *dp;
961 NYD_ENTER;
963 dp = (cp == NULL) ? NULL : (sbufdup)(cp, strlen(cp) SMALLOC_DEBUG_ARGSCALL);
964 NYD_LEAVE;
965 return dp;
968 FL char *
969 (sbufdup)(char const *cp, size_t len SMALLOC_DEBUG_ARGS)
971 char *dp = NULL;
972 NYD_ENTER;
974 dp = (smalloc)(len +1 SMALLOC_DEBUG_ARGSCALL);
975 if (cp != NULL)
976 memcpy(dp, cp, len);
977 dp[len] = '\0';
978 NYD_LEAVE;
979 return dp;
982 FL char *
983 n_strlcpy(char *dst, char const *src, size_t len)
985 NYD_ENTER;
987 assert(len > 0);
989 dst = strncpy(dst, src, len);
990 dst[len -1] = '\0';
991 NYD_LEAVE;
992 return dst;
995 FL int
996 asccasecmp(char const *s1, char const *s2)
998 int cmp;
999 NYD_ENTER;
1001 for (;;) {
1002 char c1 = *s1++, c2 = *s2++;
1003 if ((cmp = lowerconv(c1) - lowerconv(c2)) != 0 || c1 == '\0')
1004 break;
1006 NYD_LEAVE;
1007 return cmp;
1010 FL int
1011 ascncasecmp(char const *s1, char const *s2, size_t sz)
1013 int cmp = 0;
1014 NYD_ENTER;
1016 while (sz-- > 0) {
1017 char c1 = *s1++, c2 = *s2++;
1018 cmp = (ui8_t)lowerconv(c1);
1019 cmp -= (ui8_t)lowerconv(c2);
1020 if (cmp != 0 || c1 == '\0')
1021 break;
1023 NYD_LEAVE;
1024 return cmp;
1027 FL bool_t
1028 is_asccaseprefix(char const *as1, char const *as2)
1030 bool_t rv = FAL0;
1031 NYD_ENTER;
1033 for (;; ++as1, ++as2) {
1034 char c1 = lowerconv(*as1), c2 = lowerconv(*as2);
1035 if ((rv = (c1 == '\0')))
1036 break;
1037 if (c1 != c2 || c2 == '\0')
1038 break;
1040 NYD_LEAVE;
1041 return rv;
1044 #ifdef HAVE_IMAP
1045 FL char const *
1046 asccasestr(char const *haystack, char const *xneedle)
1048 char *needle = NULL, *NEEDLE;
1049 size_t i, sz;
1050 NYD_ENTER;
1052 sz = strlen(xneedle);
1053 if (sz == 0)
1054 goto jleave;
1056 needle = ac_alloc(sz);
1057 NEEDLE = ac_alloc(sz);
1058 for (i = 0; i < sz; i++) {
1059 needle[i] = lowerconv(xneedle[i]);
1060 NEEDLE[i] = upperconv(xneedle[i]);
1063 while (*haystack != '\0') {
1064 if (*haystack == *needle || *haystack == *NEEDLE) {
1065 for (i = 1; i < sz; ++i)
1066 if (haystack[i] != needle[i] && haystack[i] != NEEDLE[i])
1067 break;
1068 if (i == sz)
1069 goto jleave;
1071 ++haystack;
1073 haystack = NULL;
1074 jleave:
1075 if (needle != NULL) {
1076 ac_free(NEEDLE);
1077 ac_free(needle);
1079 NYD_LEAVE;
1080 return haystack;
1082 #endif
1084 FL struct str *
1085 (n_str_dup)(struct str *self, struct str const *t SMALLOC_DEBUG_ARGS)
1087 NYD_ENTER;
1088 if (t != NULL && t->l > 0) {
1089 self->l = t->l;
1090 self->s = (srealloc)(self->s, t->l +1 SMALLOC_DEBUG_ARGSCALL);
1091 memcpy(self->s, t->s, t->l +1);
1092 } else
1093 self->l = 0;
1094 NYD_LEAVE;
1095 return self;
1098 FL struct str *
1099 (n_str_add_buf)(struct str *self, char const *buf, size_t buflen
1100 SMALLOC_DEBUG_ARGS)
1102 NYD_ENTER;
1103 if (buflen != 0) {
1104 size_t sl = self->l;
1105 self->l = sl + buflen;
1106 self->s = (srealloc)(self->s, self->l +1 SMALLOC_DEBUG_ARGSCALL);
1107 memcpy(self->s + sl, buf, buflen);
1108 self->s[self->l] = '\0';
1110 NYD_LEAVE;
1111 return self;
1115 * Our iconv(3) wrapper
1117 #ifdef HAVE_ICONV
1119 static void _ic_toupper(char *dest, char const *src);
1120 static void _ic_stripdash(char *p);
1122 static void
1123 _ic_toupper(char *dest, char const *src)
1125 NYD_ENTER;
1127 *dest++ = upperconv(*src);
1128 while (*src++ != '\0');
1129 NYD_LEAVE;
1132 static void
1133 _ic_stripdash(char *p)
1135 char *q = p;
1136 NYD_ENTER;
1139 if (*(q = p) != '-')
1140 ++q;
1141 while (*p++ != '\0');
1142 NYD_LEAVE;
1145 FL iconv_t
1146 n_iconv_open(char const *tocode, char const *fromcode)
1148 iconv_t id;
1149 char *t, *f;
1150 NYD_ENTER;
1152 if ((id = iconv_open(tocode, fromcode)) != (iconv_t)-1)
1153 goto jleave;
1155 /* Remove the "iso-" prefixes for Solaris */
1156 if (!ascncasecmp(tocode, "iso-", 4))
1157 tocode += 4;
1158 else if (!ascncasecmp(tocode, "iso", 3))
1159 tocode += 3;
1160 if (!ascncasecmp(fromcode, "iso-", 4))
1161 fromcode += 4;
1162 else if (!ascncasecmp(fromcode, "iso", 3))
1163 fromcode += 3;
1164 if (*tocode == '\0' || *fromcode == '\0') {
1165 id = (iconv_t)-1;
1166 goto jleave;
1168 if ((id = iconv_open(tocode, fromcode)) != (iconv_t)-1)
1169 goto jleave;
1171 /* Solaris prefers upper-case charset names. Don't ask... */
1172 t = salloc(strlen(tocode) +1);
1173 _ic_toupper(t, tocode);
1174 f = salloc(strlen(fromcode) +1);
1175 _ic_toupper(f, fromcode);
1176 if ((id = iconv_open(t, f)) != (iconv_t)-1)
1177 goto jleave;
1179 /* Strip dashes for UnixWare */
1180 _ic_stripdash(t);
1181 _ic_stripdash(f);
1182 if ((id = iconv_open(t, f)) != (iconv_t)-1)
1183 goto jleave;
1185 /* Add your vendor's sillynesses here */
1187 /* If the encoding names are equal at this point, they are just not
1188 * understood by iconv(), and we cannot sensibly use it in any way. We do
1189 * not perform this as an optimization above since iconv() can otherwise be
1190 * used to check the validity of the input even with identical encoding
1191 * names */
1192 if (!strcmp(t, f))
1193 errno = 0;
1194 jleave:
1195 NYD_LEAVE;
1196 return id;
1199 FL void
1200 n_iconv_close(iconv_t cd)
1202 NYD_ENTER;
1203 iconv_close(cd);
1204 if (cd == iconvd)
1205 iconvd = (iconv_t)-1;
1206 NYD_LEAVE;
1209 #ifdef notyet
1210 FL void
1211 n_iconv_reset(iconv_t cd)
1213 NYD_ENTER;
1214 iconv(cd, NULL, NULL, NULL, NULL);
1215 NYD_LEAVE;
1217 #endif
1219 /* (2012-09-24: export and use it exclusively to isolate prototype problems
1220 * (*inb* is 'char const **' except in POSIX) in a single place.
1221 * GNU libiconv even allows for configuration time const/non-const..
1222 * In the end it's an ugly guess, but we can't do better since make(1) doesn't
1223 * support compiler invocations which bail on error, so no -Werror */
1224 /* Citrus project? */
1225 # if defined _ICONV_H_ && defined __ICONV_F_HIDE_INVALID
1226 /* DragonFly 3.2.1 is special TODO newer DragonFly too, but different */
1227 # ifdef __DragonFly__
1228 # define __INBCAST(S) (char ** __restrict__)UNCONST(S)
1229 # else
1230 # define __INBCAST(S) (char const **)UNCONST(S)
1231 # endif
1232 # endif
1233 # ifndef __INBCAST
1234 # define __INBCAST(S) (char **)UNCONST(S)
1235 # endif
1237 FL int
1238 n_iconv_buf(iconv_t cd, char const **inb, size_t *inbleft,/*XXX redo iconv use*/
1239 char **outb, size_t *outbleft, bool_t skipilseq)
1241 int err = 0;
1242 NYD_ENTER;
1244 for (;;) {
1245 size_t sz = iconv(cd, __INBCAST(inb), inbleft, outb, outbleft);
1246 if (sz != (size_t)-1)
1247 break;
1248 err = errno;
1249 if (!skipilseq || err != EILSEQ)
1250 break;
1251 if (*inbleft > 0) {
1252 ++(*inb);
1253 --(*inbleft);
1254 } else if (*outbleft > 0) {
1255 **outb = '\0';
1256 break;
1258 if (*outbleft > 0/* TODO 0xFFFD 2*/) {
1259 /* TODO 0xFFFD (*outb)[0] = '[';
1260 * TODO (*outb)[1] = '?';
1261 * TODO 0xFFFD (*outb)[2] = ']';
1262 * TODO (*outb) += 3;
1263 * TODO (*outbleft) -= 3; */
1264 *(*outb)++ = '?';
1265 --*outbleft;
1266 } else {
1267 err = E2BIG;
1268 break;
1270 err = 0;
1272 NYD_LEAVE;
1273 return err;
1275 # undef __INBCAST
1277 FL int
1278 n_iconv_str(iconv_t cd, struct str *out, struct str const *in,
1279 struct str *in_rest_or_null, bool_t skipilseq)
1281 int err;
1282 char *obb, *ob;
1283 char const *ib;
1284 size_t olb, ol, il;
1285 NYD_ENTER;
1287 err = 0;
1288 obb = out->s;
1289 olb = out->l;
1290 ol = in->l;
1292 ol = (ol << 1) - (ol >> 4);
1293 if (olb < ol) {
1294 olb = ol;
1295 goto jrealloc;
1298 for (;;) {
1299 ib = in->s;
1300 il = in->l;
1301 ob = obb;
1302 ol = olb;
1303 err = n_iconv_buf(cd, &ib, &il, &ob, &ol, skipilseq);
1304 if (err == 0 || err != E2BIG)
1305 break;
1306 err = 0;
1307 olb += in->l;
1308 jrealloc:
1309 obb = srealloc(obb, olb);
1312 if (in_rest_or_null != NULL) {
1313 in_rest_or_null->s = UNCONST(ib);
1314 in_rest_or_null->l = il;
1316 out->s = obb;
1317 out->l = olb - ol;
1318 NYD_LEAVE;
1319 return err;
1321 #endif /* HAVE_ICONV */
1323 /* vim:set fenc=utf-8:s-it-mode */