Use new vok_ interface
[s-mailx.git] / strings.c
blob63e6ee57b63432e784b0638b3fabb1d937ac54e5
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 #ifndef HAVE_AMALGAMATION
45 # include "nail.h"
46 #endif
48 #include <ctype.h>
51 * Allocate SBUFFER_SIZE chunks and keep them in a singly linked list, but
52 * release all except the first two in sreset(), because other allocations are
53 * performed and the underlaying allocator should have the possibility to
54 * reorder stuff and possibly even madvise(2), so that S-nail(1) integrates
55 * 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.
63 #ifdef HAVE_DEBUG
64 # define _SHOPE_SIZE (2 * 8 * sizeof(char) + sizeof(struct schunk))
66 CTA(sizeof(char) == sizeof(ui8_t));
68 struct schunk {
69 char const *file;
70 ui32_t line;
71 ui16_t usr_size;
72 ui16_t full_size;
75 union sptr {
76 void *p;
77 struct schunk *c;
78 char *cp;
79 ui8_t *ui8p;
81 #endif /* HAVE_DEBUG */
83 union __align__ {
84 char *cp;
85 size_t sz;
86 ul_it ul;
88 #define SALIGN (sizeof(union __align__) - 1)
90 CTA(ISPOW2(SALIGN + 1));
92 struct b_base {
93 struct buffer *_next;
94 char *_bot; /* For spreserve() */
95 char *_relax; /* If !NULL, used by srelax() instead of ._bot */
96 char *_max; /* Max usable byte */
97 char *_caster; /* NULL if full */
100 /* Single instance builtin buffer, DATA */
101 struct b_bltin {
102 struct b_base b_base;
103 char b_buf[SBUFFER_BUILTIN - sizeof(struct b_base)];
105 #define SBLTIN_SIZE SIZEOF_FIELD(struct b_bltin, b_buf)
107 /* Dynamically allocated buffers */
108 struct b_dyn {
109 struct b_base b_base;
110 char b_buf[SBUFFER_SIZE - sizeof(struct b_base)];
112 #define SDYN_SIZE SIZEOF_FIELD(struct b_dyn, b_buf)
114 struct buffer {
115 struct b_base b;
116 char b_buf[VFIELD_SIZE(SALIGN + 1)];
119 static struct b_bltin _builtin_buf;
120 static struct buffer *_buf_head, *_buf_list, *_buf_server, *_buf_relax;
121 #ifdef HAVE_DEBUG
122 static size_t _all_cnt, _all_cycnt, _all_cycnt_max,
123 _all_size, _all_cysize, _all_cysize_max, _all_min,
124 _all_max, _all_wast,
125 _all_bufcnt, _all_cybufcnt, _all_cybufcnt_max,
126 _all_resetreqs, _all_resets;
127 #endif
129 /* sreset() / srelax() release a buffer, check the canaries of all chunks */
130 #ifdef HAVE_DEBUG
131 static void _salloc_bcheck(struct buffer *b);
132 #endif
134 #ifdef HAVE_DEBUG
135 static void
136 _salloc_bcheck(struct buffer *b)
138 union sptr pmax, pp;
140 pmax.cp = (b->b._caster == NULL) ? b->b._max : b->b._caster;
141 pp.cp = b->b._bot;
143 while (PTRCMP(pp.cp, <, pmax.cp)) {
144 struct schunk *c;
145 union sptr x;
146 void *ux;
147 ui8_t i;
149 c = pp.c;
150 pp.cp += c->full_size;
151 x.p = c + 1;
152 ux = x.cp + 8;
154 i = 0;
155 if (x.ui8p[0] != 0xDE) i |= 1<<0;
156 if (x.ui8p[1] != 0xAA) i |= 1<<1;
157 if (x.ui8p[2] != 0x55) i |= 1<<2;
158 if (x.ui8p[3] != 0xAD) i |= 1<<3;
159 if (x.ui8p[4] != 0xBE) i |= 1<<4;
160 if (x.ui8p[5] != 0x55) i |= 1<<5;
161 if (x.ui8p[6] != 0xAA) i |= 1<<6;
162 if (x.ui8p[7] != 0xEF) i |= 1<<7;
163 if (i != 0)
164 warn("sdope %p: corrupted lower canary: 0x%02X, size %u: %s, line %u",
165 ux, i, c->usr_size, c->file, c->line);
166 x.cp += 8 + c->usr_size;
168 i = 0;
169 if (x.ui8p[0] != 0xDE) i |= 1<<0;
170 if (x.ui8p[1] != 0xAA) i |= 1<<1;
171 if (x.ui8p[2] != 0x55) i |= 1<<2;
172 if (x.ui8p[3] != 0xAD) i |= 1<<3;
173 if (x.ui8p[4] != 0xBE) i |= 1<<4;
174 if (x.ui8p[5] != 0x55) i |= 1<<5;
175 if (x.ui8p[6] != 0xAA) i |= 1<<6;
176 if (x.ui8p[7] != 0xEF) i |= 1<<7;
177 if (i != 0)
178 warn("sdope %p: corrupted upper canary: 0x%02X, size %u: %s, line %u",
179 ux, i, c->usr_size, c->file, c->line);
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;
191 if (size == 0)
192 ++size;
193 size += SALIGN;
194 size &= ~SALIGN;
196 #ifdef HAVE_DEBUG
197 ++_all_cnt;
198 ++_all_cycnt;
199 _all_cycnt_max = MAX(_all_cycnt_max, _all_cycnt);
200 _all_size += size;
201 _all_cysize += size;
202 _all_cysize_max = MAX(_all_cysize_max, _all_cysize);
203 _all_min = (_all_max == 0) ? size : MIN(_all_min, size);
204 _all_max = MAX(_all_max, size);
205 _all_wast += size - orig_size;
207 size += _SHOPE_SIZE;
208 #endif
210 /* Search for a buffer with enough free space to serve request */
211 if ((u.b = _buf_server) != NULL)
212 goto jumpin;
213 jredo:
214 for (u.b = _buf_head; u.b != NULL; u.b = u.b->b._next) {
215 jumpin:
216 x = u.b->b._caster;
217 if (x == NULL) {
218 if (u.b == _buf_server) {
219 if (u.b == _buf_head && (u.b = _buf_head->b._next) != NULL) {
220 _buf_server = u.b;
221 goto jumpin;
223 _buf_server = NULL;
224 goto jredo;
226 continue;
228 y = x + size;
229 z = u.b->b._max;
230 if (PTRCMP(y, <=, z)) {
231 /* Alignment is the one thing, the other is what is usually allocated,
232 * and here about 40 bytes seems to be a good cut to avoid non-usable
233 * non-NULL casters. However, because of _salloc_bcheck(), we may not
234 * set ._caster to NULL because then it would check all chunks up to
235 * ._max, which surely doesn't work; speed is no issue with DEBUG */
236 u.b->b._caster = NDBG( PTRCMP(y + 42 + 16, >=, z) ? NULL : ) y;
237 u.cp = x;
238 goto jleave;
242 /* Need a new buffer */
243 if (_buf_head == NULL) {
244 struct b_bltin *b = &_builtin_buf;
245 b->b_base._max = b->b_buf + sizeof(b->b_buf) - 1;
246 _buf_head = (struct buffer*)b;
247 u.b = _buf_head;
248 } else {
249 #ifdef HAVE_DEBUG
250 ++_all_bufcnt;
251 ++_all_cybufcnt;
252 _all_cybufcnt_max = MAX(_all_cybufcnt_max, _all_cybufcnt);
253 #endif
254 u.b = smalloc(sizeof(struct b_dyn));
255 u.b->b._max = u.b->b_buf + SDYN_SIZE - 1;
257 if (_buf_list != NULL)
258 _buf_list->b._next = u.b;
259 _buf_server = _buf_list = u.b;
260 u.b->b._next = NULL;
261 u.b->b._caster = (u.b->b._bot = u.b->b_buf) + size;
262 u.b->b._relax = NULL;
263 u.cp = u.b->b._bot;
265 jleave:
266 /* Encapsulate user chunk in debug canaries */
267 #ifdef HAVE_DEBUG
269 union sptr xl, xu;
270 struct schunk *xc;
272 xl.p = u.cp;
273 xc = xl.c;
274 xc->file = mdbg_file;
275 xc->line = mdbg_line;
276 xc->usr_size = (ui16_t)orig_size;
277 xc->full_size = (ui16_t)size;
278 xl.p = xc + 1;
279 xl.ui8p[0]=0xDE; xl.ui8p[1]=0xAA; xl.ui8p[2]=0x55; xl.ui8p[3]=0xAD;
280 xl.ui8p[4]=0xBE; xl.ui8p[5]=0x55; xl.ui8p[6]=0xAA; xl.ui8p[7]=0xEF;
281 u.cp = xl.cp + 8;
282 xu.p = u.cp;
283 xu.cp += orig_size;
284 xu.ui8p[0]=0xDE; xu.ui8p[1]=0xAA; xu.ui8p[2]=0x55; xu.ui8p[3]=0xAD;
285 xu.ui8p[4]=0xBE; xu.ui8p[5]=0x55; xu.ui8p[6]=0xAA; xu.ui8p[7]=0xEF;
287 #endif
288 return u.cp;
291 FL void *
292 (csalloc)(size_t nmemb, size_t size SALLOC_DEBUG_ARGS)
294 void *vp;
296 size *= nmemb;
297 vp = (salloc)(size SALLOC_DEBUG_ARGSCALL);
298 memset(vp, 0, size);
299 return vp;
302 FL void
303 sreset(bool_t only_if_relaxed)
305 struct buffer *bh;
307 DBG( ++_all_resetreqs; )
308 if (noreset || (only_if_relaxed && _buf_relax == NULL))
309 goto jleave;
311 #ifdef HAVE_DEBUG
312 _all_cycnt = _all_cysize = 0;
313 _all_cybufcnt = (_buf_head != NULL && _buf_head->b._next != NULL);
314 ++_all_resets;
315 #endif
317 if ((bh = _buf_head) != NULL) {
318 struct buffer *b = bh;
319 DBG( _salloc_bcheck(b); )
320 b->b._caster = b->b._bot;
321 b->b._relax = NULL;
322 DBG( memset(b->b._caster, 0377, PTR2SIZE(b->b._max - b->b._caster)); )
323 _buf_server = b;
325 if ((bh = bh->b._next) != NULL) {
326 b = bh;
327 DBG( _salloc_bcheck(b); )
328 b->b._caster = b->b._bot;
329 b->b._relax = NULL;
330 DBG( memset(b->b._caster, 0377, PTR2SIZE(b->b._max - b->b._caster)); )
332 for (bh = bh->b._next; bh != NULL;) {
333 struct buffer *b2 = bh->b._next;
334 DBG( _salloc_bcheck(bh); )
335 free(bh);
336 bh = b2;
339 _buf_list = b;
340 b->b._next = NULL;
341 _buf_relax = NULL;
344 DBG( smemreset(); )
345 jleave:
349 FL void
350 srelax_hold(void)
352 struct buffer *b;
354 assert(_buf_relax == NULL);
356 for (b = _buf_head; b != NULL; b = b->b._next)
357 b->b._relax = b->b._caster;
358 _buf_relax = _buf_server;
359 assert(_buf_relax != NULL);
362 FL void
363 srelax_rele(void)
365 struct buffer *b;
367 assert(_buf_relax != NULL);
369 for (b = _buf_relax; b != NULL; b = b->b._next) {
370 DBG( _salloc_bcheck(b); )
371 b->b._caster = (b->b._relax != NULL) ? b->b._relax : b->b._bot;
372 b->b._relax = NULL;
374 _buf_relax = NULL;
377 FL void
378 srelax(void)
380 /* The purpose of relaxation is only that it is possible to reset the
381 * casters, *not* to give back memory to the system. We are presumably in
382 * an iteration over all messages of a mailbox, and it'd be quite
383 * counterproductive to give the system allocator a chance to waste time */
384 struct buffer *b;
386 assert(_buf_relax != NULL);
388 for (b = _buf_relax; b != NULL; b = b->b._next) {
389 DBG( _salloc_bcheck(b); )
390 b->b._caster = (b->b._relax != NULL) ? b->b._relax : b->b._bot;
391 DBG( memset(b->b._caster, 0377, PTR2SIZE(b->b._max - b->b._caster)); )
395 FL void
396 spreserve(void)
398 struct buffer *b;
400 for (b = _buf_head; b != NULL; b = b->b._next)
401 b->b._bot = b->b._caster;
404 #ifdef HAVE_DEBUG
405 FL int
406 c_sstats(void *v)
408 size_t excess;
409 UNUSED(v);
411 excess = (_all_cybufcnt_max * SDYN_SIZE) + SBLTIN_SIZE;
412 excess = (excess >= _all_cysize_max) ? 0 : _all_cysize_max - excess;
414 printf("String usage statistics (cycle means one sreset() cycle):\n"
415 " Buffer allocs ever/max simultan. : %lu/%lu\n"
416 " Buffer size of builtin(1)/dynamic: %lu/%lu\n"
417 " Overall alloc count/bytes : %lu/%lu\n"
418 " Alloc bytes min/max/align wastage: %lu/%lu/%lu\n"
419 " sreset() cycles : %lu (%lu performed)\n"
420 " Cycle maximums: alloc count/bytes: %lu/%lu+%lu\n",
421 (ul_it)_all_bufcnt, (ul_it)_all_cybufcnt_max,
422 (ul_it)SBLTIN_SIZE, (ul_it)SDYN_SIZE,
423 (ul_it)_all_cnt, (ul_it)_all_size,
424 (ul_it)_all_min, (ul_it)_all_max, (ul_it)_all_wast,
425 (ul_it)_all_resetreqs, (ul_it)_all_resets,
426 (ul_it)_all_cycnt_max, (ul_it)_all_cysize_max, (ul_it)excess);
427 return 0;
429 #endif
431 FL char *
432 (savestr)(const char *str SALLOC_DEBUG_ARGS)
434 size_t size;
435 char *news;
437 size = strlen(str) + 1;
438 news = (salloc)(size SALLOC_DEBUG_ARGSCALL);
439 memcpy(news, str, size);
440 return news;
443 FL char *
444 (savestrbuf)(const char *sbuf, size_t sbuf_len SALLOC_DEBUG_ARGS)
446 char *news;
448 news = (salloc)(sbuf_len + 1 SALLOC_DEBUG_ARGSCALL);
449 memcpy(news, sbuf, sbuf_len);
450 news[sbuf_len] = 0;
451 return news;
454 FL char *
455 (save2str)(const char *str, const char *old SALLOC_DEBUG_ARGS)
457 size_t newsize, oldsize;
458 char *news;
460 newsize = strlen(str) + 1;
461 oldsize = (old != NULL) ? strlen(old) + 1 : 0;
462 news = (salloc)(newsize + oldsize SALLOC_DEBUG_ARGSCALL);
463 if (oldsize) {
464 memcpy(news, old, oldsize);
465 news[oldsize - 1] = ' ';
467 memcpy(news + oldsize, str, newsize);
468 return news;
471 FL char *
472 (savecat)(char const *s1, char const *s2 SALLOC_DEBUG_ARGS)
474 size_t l1, l2;
475 char *news;
477 l1 = strlen(s1);
478 l2 = strlen(s2);
479 news = (salloc)(l1 + l2 + 1 SALLOC_DEBUG_ARGSCALL);
480 memcpy(news + 0, s1, l1);
481 memcpy(news + l1, s2, l2);
482 news[l1 + l2] = '\0';
483 return news;
487 * Support routines, auto-reclaimed storage
490 FL char *
491 (i_strdup)(char const *src SALLOC_DEBUG_ARGS)
493 size_t sz;
494 char *dest;
496 sz = strlen(src) + 1;
497 dest = (salloc)(sz SALLOC_DEBUG_ARGSCALL);
498 i_strcpy(dest, src, sz);
499 return dest;
502 FL char *
503 (protbase)(char const *cp SALLOC_DEBUG_ARGS)
505 char *n, *np;
507 np = n = (salloc)(strlen(cp) + 1 SALLOC_DEBUG_ARGSCALL);
509 /* Just ignore the `is-system-mailbox' prefix XXX */
510 if (cp[0] == '%' && cp[1] == ':')
511 cp += 2;
513 while (*cp != '\0') {
514 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
515 *np++ = *cp++;
516 *np++ = *cp++;
517 *np++ = *cp++;
518 } else if (cp[0] == '/')
519 break;
520 else
521 *np++ = *cp++;
523 *np = '\0';
524 return n;
527 FL char *
528 (urlxenc)(char const *cp SALLOC_DEBUG_ARGS) /* XXX */
530 char *n, *np;
532 np = n = (salloc)(strlen(cp) * 3 + 1 SALLOC_DEBUG_ARGSCALL);
534 while (*cp != '\0') {
535 if (alnumchar(*cp) || *cp == '_' || *cp == '@' ||
536 (PTRCMP(np, >, n) && (*cp == '.' || *cp == '-' || *cp == ':')))
537 *np++ = *cp;
538 else {
539 *np++ = '%';
540 *np++ = Hexchar((*cp & 0xf0) >> 4);
541 *np++ = Hexchar(*cp & 0x0f);
543 cp++;
545 *np = '\0';
546 return n;
549 FL char *
550 (urlxdec)(char const *cp SALLOC_DEBUG_ARGS) /* XXX */
552 char *n, *np;
554 np = n = (salloc)(strlen(cp) + 1 SALLOC_DEBUG_ARGSCALL);
556 while (*cp != '\0') {
557 if (cp[0] == '%' && cp[1] != '\0' && cp[2] != '\0') {
558 *np = (int)(cp[1] > '9' ? cp[1] - 'A' + 10 : cp[1] - '0') << 4;
559 *np++ |= cp[2] > '9' ? cp[2] - 'A' + 10 : cp[2] - '0';
560 cp += 3;
561 } else
562 *np++ = *cp++;
564 *np = '\0';
565 return n;
568 FL struct str *
569 str_concat_csvl(struct str *self, ...) /* XXX onepass maybe better here */
571 va_list vl;
572 size_t l;
573 char const *cs;
575 va_start(vl, self);
576 for (l = 0; (cs = va_arg(vl, char const*)) != NULL;)
577 l += strlen(cs);
578 va_end(vl);
580 self->l = l;
581 self->s = salloc(l + 1);
583 va_start(vl, self);
584 for (l = 0; (cs = va_arg(vl, char const*)) != NULL;) {
585 size_t i = strlen(cs);
586 memcpy(self->s + l, cs, i);
587 l += i;
589 self->s[l] = '\0';
590 va_end(vl);
591 return self;
594 FL struct str *
595 (str_concat_cpa)(struct str *self, char const * const *cpa,
596 char const *sep_o_null SALLOC_DEBUG_ARGS)
598 size_t sonl, l;
599 char const * const *xcpa;
601 sonl = (sep_o_null != NULL) ? strlen(sep_o_null) : 0;
603 for (l = 0, xcpa = cpa; *xcpa != NULL; ++xcpa)
604 l += strlen(*xcpa) + sonl;
606 self->l = l;
607 self->s = (salloc)(l + 1 SALLOC_DEBUG_ARGSCALL);
609 for (l = 0, xcpa = cpa; *xcpa != NULL; ++xcpa) {
610 size_t i = strlen(*xcpa);
611 memcpy(self->s + l, *xcpa, i);
612 l += i;
613 if (sonl > 0) {
614 memcpy(self->s + l, sep_o_null, sonl);
615 l += sonl;
618 self->s[l] = '\0';
619 return self;
623 * Routines that are not related to auto-reclaimed storage follow.
626 FL int
627 anyof(char const *s1, char const *s2)
629 for (; *s1 != '\0'; ++s1)
630 if (strchr(s2, *s1))
631 break;
632 return (*s1 != '\0');
635 FL ui_it
636 strhash(char const *name)
638 ui_it h = 0;
640 while (*name != '\0') {
641 h *= 33;
642 h += *name++;
644 return h;
647 FL char *
648 n_strsep(char **iolist, char sep, bool_t ignore_empty)
650 char *base, *cp;
652 for (base = *iolist; base != NULL; base = *iolist) {
653 while (*base != '\0' && blankspacechar(*base))
654 ++base;
655 cp = strchr(base, sep);
656 if (cp != NULL)
657 *iolist = cp + 1;
658 else {
659 *iolist = NULL;
660 cp = base + strlen(base);
662 while (cp > base && blankspacechar(cp[-1]))
663 --cp;
664 *cp = '\0';
665 if (*base != '\0' || !ignore_empty)
666 break;
668 return base;
671 FL void
672 i_strcpy(char *dest, const char *src, size_t size)
674 if (size > 0) {
675 for (;; ++dest, ++src)
676 if ((*dest = lowerconv(*src)) == '\0') {
677 break;
678 } else if (--size == 0) {
679 *dest = '\0';
680 break;
685 FL int
686 is_prefix(char const *as1, char const *as2)
688 char c;
689 for (; (c = *as1) == *as2 && c != '\0'; ++as1, ++as2)
690 if (*as2 == '\0')
691 break;
692 return (c == '\0');
695 FL char const *
696 last_at_before_slash(char const *sp)
698 char const *cp;
699 char c;
701 for (cp = sp; (c = *cp) != '\0'; ++cp)
702 if (c == '/')
703 break;
704 while (cp > sp && *--cp != '@')
706 return (*cp == '@' ? cp : NULL);
709 FL char *
710 laststring(char *linebuf, bool_t *needs_list, bool_t strip)
712 char *cp, *p, quoted;
714 /* Anything to do at all? */
715 if (*(cp = linebuf) == '\0')
716 goto jnull;
717 cp += strlen(linebuf) - 1;
719 /* Strip away trailing blanks */
720 while (whitechar(*cp) && PTRCMP(cp, >, linebuf))
721 --cp;
722 cp[1] = '\0';
723 if (cp == linebuf)
724 goto jleave;
726 /* Now search for the BOS of the "last string" */
727 quoted = *cp;
728 if (quoted == '\'' || quoted == '"') {
729 if (strip)
730 *cp = '\0';
731 } else
732 quoted = ' ';
734 while (PTRCMP(cp, >, linebuf)) {
735 --cp;
736 if (quoted != ' ') {
737 if (*cp != quoted)
738 continue;
739 } else if (!whitechar(*cp))
740 continue;
741 if (PTRCMP(cp, ==, linebuf) || cp[-1] != '\\') {
742 /* When in whitespace mode, WS prefix doesn't belong */
743 if (quoted == ' ')
744 ++cp;
745 break;
747 /* Expand the escaped quote character */
748 for (p = --cp; (p[0] = p[1]) != '\0'; ++p)
751 if (strip && quoted != ' ' && *cp == quoted)
752 for (p = cp; (p[0] = p[1]) != '\0'; ++p)
755 /* The "last string" has been skipped over, but still, try to step backwards
756 * until we are at BOS or see whitespace, so as to make possible things like
757 * "? copy +'x y.mbox'" or even "? copy +x\ y.mbox" */
758 while (PTRCMP(cp, >, linebuf)) {
759 --cp;
760 if (whitechar(*cp)) {
761 p = cp;
762 *cp++ = '\0';
763 /* We can furtherly release our callees if we now decide wether the
764 * remaining non-"last string" line content contains non-WS */
765 while (PTRCMP(--p, >=, linebuf))
766 if (!whitechar(*p))
767 goto jleave;
768 linebuf = cp;
769 break;
773 jleave:
774 if (cp != NULL && *cp == '\0')
775 goto jnull;
776 *needs_list = (cp != linebuf && *linebuf != '\0');
777 j_leave:
778 return cp;
779 jnull:
780 *needs_list = FAL0;
781 cp = NULL;
782 goto j_leave;
785 FL void
786 makelow(char *cp) /* TODO isn't that crap? --> */
788 #ifdef HAVE_C90AMEND1
789 if (mb_cur_max > 1) {
790 char *tp = cp;
791 wchar_t wc;
792 int len;
794 while (*cp) {
795 len = mbtowc(&wc, cp, mb_cur_max);
796 if (len < 0)
797 *tp++ = *cp++;
798 else {
799 wc = towlower(wc);
800 if (wctomb(tp, wc) == len)
801 tp += len, cp += len;
802 else
803 *tp++ = *cp++; /* <-- at least here */
806 } else
807 #endif
810 *cp = tolower((uc_it)*cp);
811 while (*cp++);
815 FL int
816 substr(char const *str, char const *sub)
818 char const *cp, *backup;
820 cp = sub;
821 backup = str;
822 while (*str && *cp) {
823 #ifdef HAVE_C90AMEND1
824 if (mb_cur_max > 1) {
825 wchar_t c, c2;
826 int sz;
828 if ((sz = mbtowc(&c, cp, mb_cur_max)) < 0)
829 goto singlebyte;
830 cp += sz;
831 if ((sz = mbtowc(&c2, str, mb_cur_max)) < 0)
832 goto singlebyte;
833 str += sz;
834 c = towupper(c);
835 c2 = towupper(c2);
836 if (c != c2) {
837 if ((sz = mbtowc(&c, backup, mb_cur_max)) > 0) {
838 backup += sz;
839 str = backup;
840 } else
841 str = ++backup;
842 cp = sub;
844 } else
845 #endif
847 int c, c2;
849 #ifdef HAVE_C90AMEND1
850 singlebyte:
851 #endif
852 c = *cp++ & 0377;
853 if (islower(c))
854 c = toupper(c);
855 c2 = *str++ & 0377;
856 if (islower(c2))
857 c2 = toupper(c2);
858 if (c != c2) {
859 str = ++backup;
860 cp = sub;
864 return *cp == '\0';
867 #ifndef HAVE_SNPRINTF
868 FL int
869 snprintf(char *str, size_t size, const char *format, ...) /* XXX DANGER! */
871 va_list ap;
872 int ret;
874 va_start(ap, format);
875 ret = vsprintf(str, format, ap);
876 va_end(ap);
877 if (ret < 0)
878 ret = strlen(str);
879 return ret;
881 #endif
883 FL char *
884 sstpcpy(char *dst, char const *src)
886 while ((*dst = *src++) != '\0')
887 dst++;
888 return (dst);
891 FL char *
892 (sstrdup)(char const *cp SMALLOC_DEBUG_ARGS)
894 char *dp = NULL;
896 if (cp != NULL) {
897 size_t l = strlen(cp) + 1;
898 dp = (smalloc)(l SMALLOC_DEBUG_ARGSCALL);
899 memcpy(dp, cp, l);
901 return dp;
904 FL char *
905 (sbufdup)(char const *cp, size_t len SMALLOC_DEBUG_ARGS)
907 char *dp = NULL;
909 dp = (smalloc)(len + 1 SMALLOC_DEBUG_ARGSCALL);
910 if (cp != NULL)
911 memcpy(dp, cp, len);
912 dp[len] = '\0';
913 return dp;
916 FL char *
917 n_strlcpy(char *dst, const char *src, size_t len)
919 assert(len > 0);
921 dst = strncpy(dst, src, len);
922 dst[len - 1] = '\0';
923 return dst;
926 FL int
927 asccasecmp(char const *s1, char const *s2)
929 int cmp;
931 for (;;) {
932 char c1 = *s1++, c2 = *s2++;
933 if ((cmp = lowerconv(c1) - lowerconv(c2)) != 0 || c1 == '\0')
934 break;
936 return cmp;
939 FL int
940 ascncasecmp(char const *s1, char const *s2, size_t sz)
942 int cmp = 0;
944 while (sz-- > 0) {
945 char c1 = *s1++, c2 = *s2++;
946 cmp = (ui8_t)lowerconv(c1);
947 cmp -= (ui8_t)lowerconv(c2);
948 if (cmp != 0 || c1 == '\0')
949 break;
951 return cmp;
954 FL char const *
955 asccasestr(char const *haystack, char const *xneedle)
957 char *needle = NULL, *NEEDLE;
958 size_t i, sz;
960 sz = strlen(xneedle);
961 if (sz == 0)
962 goto jleave;
964 needle = ac_alloc(sz);
965 NEEDLE = ac_alloc(sz);
966 for (i = 0; i < sz; i++) {
967 needle[i] = lowerconv(xneedle[i]);
968 NEEDLE[i] = upperconv(xneedle[i]);
971 while (*haystack) {
972 if (*haystack == *needle || *haystack == *NEEDLE) {
973 for (i = 1; i < sz; i++)
974 if (haystack[i] != needle[i] &&
975 haystack[i] != NEEDLE[i])
976 break;
977 if (i == sz)
978 goto jleave;
980 haystack++;
982 haystack = NULL;
983 jleave:
984 if (needle != NULL) {
985 ac_free(NEEDLE);
986 ac_free(needle);
988 return haystack;
991 FL bool_t
992 is_asccaseprefix(char const *as1, char const *as2)
994 bool_t rv = FAL0;
996 for (;; ++as1, ++as2) {
997 char c1 = lowerconv(*as1), c2 = lowerconv(*as2);
998 if ((rv = (c1 == '\0')))
999 break;
1000 if (c1 != c2 || c2 == '\0')
1001 break;
1003 return rv;
1006 FL struct str *
1007 (n_str_dup)(struct str *self, struct str const *t SMALLOC_DEBUG_ARGS)
1009 if (t != NULL && t->l > 0) {
1010 self->l = t->l;
1011 self->s = (srealloc)(self->s, t->l + 1 SMALLOC_DEBUG_ARGSCALL);
1012 memcpy(self->s, t->s, t->l);
1013 } else
1014 self->l = 0;
1015 return self;
1018 FL struct str *
1019 (n_str_add_buf)(struct str *self, char const *buf, size_t buflen
1020 SMALLOC_DEBUG_ARGS)
1022 if (buflen != 0) {
1023 size_t sl = self->l;
1024 self->l = sl + buflen;
1025 self->s = (srealloc)(self->s, self->l+1 SMALLOC_DEBUG_ARGSCALL);
1026 memcpy(self->s + sl, buf, buflen);
1028 return self;
1031 #ifdef HAVE_ICONV
1032 static void _ic_toupper(char *dest, char const *src);
1033 static void _ic_stripdash(char *p);
1035 static void
1036 _ic_toupper(char *dest, const char *src)
1039 *dest++ = upperconv(*src);
1040 while (*src++);
1043 static void
1044 _ic_stripdash(char *p)
1046 char *q = p;
1049 if (*(q = p) != '-')
1050 q++;
1051 while (*p++);
1054 FL iconv_t
1055 n_iconv_open(char const *tocode, char const *fromcode)
1057 iconv_t id;
1058 char *t, *f;
1060 if ((id = iconv_open(tocode, fromcode)) != (iconv_t)-1)
1061 return id;
1064 * Remove the "iso-" prefixes for Solaris.
1066 if (ascncasecmp(tocode, "iso-", 4) == 0)
1067 tocode += 4;
1068 else if (ascncasecmp(tocode, "iso", 3) == 0)
1069 tocode += 3;
1070 if (ascncasecmp(fromcode, "iso-", 4) == 0)
1071 fromcode += 4;
1072 else if (ascncasecmp(fromcode, "iso", 3) == 0)
1073 fromcode += 3;
1074 if (*tocode == '\0' || *fromcode == '\0')
1075 return (iconv_t) -1;
1076 if ((id = iconv_open(tocode, fromcode)) != (iconv_t)-1)
1077 return id;
1079 * Solaris prefers upper-case charset names. Don't ask...
1081 t = salloc(strlen(tocode) + 1);
1082 _ic_toupper(t, tocode);
1083 f = salloc(strlen(fromcode) + 1);
1084 _ic_toupper(f, fromcode);
1085 if ((id = iconv_open(t, f)) != (iconv_t)-1)
1086 return id;
1088 * Strip dashes for UnixWare.
1090 _ic_stripdash(t);
1091 _ic_stripdash(f);
1092 if ((id = iconv_open(t, f)) != (iconv_t)-1)
1093 return id;
1095 * Add your vendor's sillynesses here.
1099 * If the encoding names are equal at this point, they
1100 * are just not understood by iconv(), and we cannot
1101 * sensibly use it in any way. We do not perform this
1102 * as an optimization above since iconv() can otherwise
1103 * be used to check the validity of the input even with
1104 * identical encoding names.
1106 if (strcmp(t, f) == 0)
1107 errno = 0;
1108 return (iconv_t)-1;
1111 FL void
1112 n_iconv_close(iconv_t cd)
1114 iconv_close(cd);
1115 if (cd == iconvd)
1116 iconvd = (iconv_t)-1;
1119 #ifdef notyet
1120 FL void
1121 n_iconv_reset(iconv_t cd)
1123 (void)iconv(cd, NULL, NULL, NULL, NULL);
1125 #endif
1128 * (2012-09-24: export and use it exclusively to isolate prototype problems
1129 * (*inb* is 'char const **' except in POSIX) in a single place.
1130 * GNU libiconv even allows for configuration time const/non-const..
1131 * In the end it's an ugly guess, but we can't do better since make(1) doesn't
1132 * support compiler invocations which bail on error, so no -Werror.
1134 /* Citrus project? */
1135 # if defined _ICONV_H_ && defined __ICONV_F_HIDE_INVALID
1136 /* DragonFly 3.2.1 is special */
1137 # ifdef __DragonFly__
1138 # define __INBCAST(S) (char ** __restrict__)UNCONST(S)
1139 # else
1140 # define __INBCAST(S) (char const **)UNCONST(S)
1141 # endif
1142 # endif
1143 # ifndef __INBCAST
1144 # define __INBCAST(S) (char **)UNCONST(S)
1145 # endif
1147 FL int
1148 n_iconv_buf(iconv_t cd, char const **inb, size_t *inbleft,/*XXX redo iconv use*/
1149 char **outb, size_t *outbleft, bool_t skipilseq)
1151 int err = 0;
1153 for (;;) {
1154 size_t sz = iconv(cd, __INBCAST(inb), inbleft, outb, outbleft);
1155 if (sz != (size_t)-1)
1156 break;
1157 err = errno;
1158 if (! skipilseq || err != EILSEQ)
1159 break;
1160 if (*inbleft > 0) {
1161 ++(*inb);
1162 --(*inbleft);
1163 } else if (*outbleft > 0) {
1164 **outb = '\0';
1165 break;
1167 if (*outbleft > 0/* TODO 0xFFFD 2*/) {
1168 /* TODO 0xFFFD (*outb)[0] = '[';
1169 * TODO (*outb)[1] = '?';
1170 * TODO 0xFFFD (*outb)[2] = ']';
1171 * TODO (*outb) += 3;
1172 * TODO (*outbleft) -= 3; */
1173 *(*outb)++ = '?';
1174 --*outbleft;
1175 } else {
1176 err = E2BIG;
1177 break;
1179 err = 0;
1181 return err;
1183 # undef __INBCAST
1185 FL int
1186 n_iconv_str(iconv_t cd, struct str *out, struct str const *in,
1187 struct str *in_rest_or_null, bool_t skipilseq)
1189 int err = 0;
1190 char *obb = out->s, *ob;
1191 char const *ib;
1192 size_t olb = out->l, ol, il;
1194 ol = in->l;
1195 ol = (ol << 1) - (ol >> 4);
1196 if (olb < ol) {
1197 olb = ol;
1198 goto jrealloc;
1201 for (;;) {
1202 ib = in->s;
1203 il = in->l;
1204 ob = obb;
1205 ol = olb;
1206 err = n_iconv_buf(cd, &ib, &il, &ob, &ol, skipilseq);
1207 if (err == 0 || err != E2BIG)
1208 break;
1209 err = 0;
1210 olb += in->l;
1211 jrealloc: obb = srealloc(obb, olb);
1214 if (in_rest_or_null != NULL) {
1215 in_rest_or_null->s = UNCONST(ib);
1216 in_rest_or_null->l = il;
1218 out->s = obb;
1219 out->l = olb - ol;
1220 return err;
1222 #endif /* HAVE_ICONV */
1224 /* vim:set fenc=utf-8:s-it-mode (TODO only partial true) */