Many: unite many HAVE_ to HAVE_C90AMEND1, plus
[s-mailx.git] / strings.c
blob112600d1ce4c359fca26ae84ad1232fa69c923e8
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).
62 union __align__ {
63 char *cp;
64 size_t sz;
65 ul_it ul;
67 #define SALIGN (sizeof(union __align__) - 1)
69 CTA(ISPOW2(SALIGN + 1));
71 struct b_base {
72 struct buffer *_next;
73 char *_bot; /* For spreserve() */
74 char *_relax; /* If !NULL, used by srelax() instead of ._bot */
75 char *_max; /* Max usable byte */
76 char *_caster; /* NULL if full */
79 /* Single instance builtin buffer, DATA */
80 struct b_bltin {
81 struct b_base b_base;
82 char b_buf[SBUFFER_BUILTIN - sizeof(struct b_base)];
84 #define SBLTIN_SIZE SIZEOF_FIELD(struct b_bltin, b_buf)
86 /* Dynamically allocated buffers */
87 struct b_dyn {
88 struct b_base b_base;
89 char b_buf[SBUFFER_SIZE - sizeof(struct b_base)];
91 #define SDYN_SIZE SIZEOF_FIELD(struct b_dyn, b_buf)
93 struct buffer {
94 struct b_base b;
95 char b_buf[VFIELD_SIZE(SALIGN + 1)];
98 static struct b_bltin _builtin_buf;
99 static struct buffer *_buf_head, *_buf_list, *_buf_server, *_buf_relax;
101 #ifdef HAVE_DEBUG
102 size_t _all_cnt, _all_cycnt, _all_cycnt_max,
103 _all_size, _all_cysize, _all_cysize_max, _all_min, _all_max, _all_wast,
104 _all_bufcnt, _all_cybufcnt, _all_cybufcnt_max,
105 _all_resetreqs, _all_resets;
106 #endif
108 FL void *
109 salloc(size_t size)
111 #ifdef HAVE_DEBUG
112 size_t orig_size = size;
113 #endif
114 union {struct buffer *b; char *cp;} u;
115 char *x, *y, *z;
117 if (size == 0)
118 ++size;
119 size += SALIGN;
120 size &= ~SALIGN;
122 #ifdef HAVE_DEBUG
123 ++_all_cnt;
124 ++_all_cycnt;
125 _all_cycnt_max = MAX(_all_cycnt_max, _all_cycnt);
126 _all_size += size;
127 _all_cysize += size;
128 _all_cysize_max = MAX(_all_cysize_max, _all_cysize);
129 _all_min = (_all_max == 0) ? size : MIN(_all_min, size);
130 _all_max = MAX(_all_max, size);
131 _all_wast += size - orig_size;
132 #endif
134 if ((u.b = _buf_server) != NULL)
135 goto jumpin;
136 jredo:
137 for (u.b = _buf_head; u.b != NULL; u.b = u.b->b._next) {
138 jumpin:
139 x = u.b->b._caster;
140 if (x == NULL) {
141 if (u.b == _buf_server) {
142 if (u.b == _buf_head && (u.b = _buf_head->b._next) != NULL) {
143 _buf_server = u.b;
144 goto jumpin;
146 _buf_server = NULL;
147 goto jredo;
149 continue;
151 y = x + size;
152 z = u.b->b._max;
153 if (y <= z) {
154 /* Alignment is the one thing, the other is what is
155 * usually allocated, and here about 40 bytes seems to
156 * be a good cut to avoid non-usable non-NULL casters */
157 u.b->b._caster = PTRCMP(y + 42 + 16, >=, z) ? NULL : y;
158 u.cp = x;
159 goto jleave;
163 if (_buf_head == NULL) {
164 struct b_bltin *b = &_builtin_buf;
165 b->b_base._max = b->b_buf + sizeof(b->b_buf) - 1;
166 _buf_head = (struct buffer*)b;
167 u.b = _buf_head;
168 } else {
169 #ifdef HAVE_DEBUG
170 ++_all_bufcnt;
171 ++_all_cybufcnt;
172 _all_cybufcnt_max = MAX(_all_cybufcnt_max, _all_cybufcnt);
173 #endif
174 u.b = smalloc(sizeof(struct b_dyn));
175 u.b->b._max = u.b->b_buf + SDYN_SIZE - 1;
177 if (_buf_list != NULL)
178 _buf_list->b._next = u.b;
179 _buf_server = _buf_list = u.b;
180 u.b->b._next = NULL;
181 u.b->b._caster = (u.b->b._bot = u.b->b_buf) + size;
182 u.b->b._relax = NULL;
183 u.cp = u.b->b._bot;
184 jleave:
185 return u.cp;
188 FL void *
189 csalloc(size_t nmemb, size_t size)
191 void *vp;
193 size *= nmemb;
194 vp = salloc(size);
195 memset(vp, 0, size);
196 return (vp);
199 FL void
200 sreset(bool_t only_if_relaxed)
202 struct buffer *bh;
204 #ifdef HAVE_DEBUG
205 ++_all_resetreqs;
206 #endif
207 if (noreset || (only_if_relaxed && _buf_relax == NULL))
208 goto jleave;
210 #ifdef HAVE_DEBUG
211 _all_cycnt = _all_cysize = 0;
212 _all_cybufcnt = (_buf_head != NULL && _buf_head->b._next != NULL);
213 ++_all_resets;
214 #endif
216 if ((bh = _buf_head) != NULL) {
217 struct buffer *b = bh;
218 b->b._caster = b->b._bot;
219 b->b._relax = NULL;
220 #ifdef HAVE_DEBUG
221 memset(b->b._caster, 0377, PTR2SIZE(b->b._max - b->b._caster));
222 #endif
223 _buf_server = b;
224 if ((bh = bh->b._next) != NULL) {
225 b = bh;
226 b->b._caster = b->b._bot;
227 b->b._relax = NULL;
228 #ifdef HAVE_DEBUG
229 memset(b->b._caster, 0377, PTR2SIZE(b->b._max - b->b._caster));
230 #endif
231 for (bh = bh->b._next; bh != NULL;) {
232 struct buffer *b2 = bh->b._next;
233 free(bh);
234 bh = b2;
237 _buf_list = b;
238 b->b._next = NULL;
239 _buf_relax = NULL;
242 #ifdef HAVE_DEBUG
243 smemreset();
244 #endif
245 jleave:
249 FL void
250 spreserve(void)
252 struct buffer *b;
254 for (b = _buf_head; b != NULL; b = b->b._next)
255 b->b._bot = b->b._caster;
258 FL void
259 srelax_hold(void)
261 struct buffer *b;
263 assert(_buf_relax == NULL);
265 for (b = _buf_head; b != NULL; b = b->b._next)
266 b->b._relax = b->b._caster;
267 _buf_relax = _buf_server;
268 assert(_buf_relax != NULL);
271 FL void
272 srelax_rele(void)
274 struct buffer *b;
276 assert(_buf_relax != NULL);
278 for (b = _buf_relax; b != NULL; b = b->b._next) {
279 b->b._caster = (b->b._relax != NULL) ? b->b._relax : b->b._bot;
280 b->b._relax = NULL;
282 _buf_relax = NULL;
285 FL void
286 srelax(void)
288 /* The purpose of relaxation is only that it is possible to reset the
289 * casters, *not* to give back memory to the system. We are presumably in
290 * an iteration over all messages of a mailbox, and it'd be quite
291 * counterproductive to give the system allocator a chance to waste time */
292 struct buffer *b;
294 assert(_buf_relax != NULL);
296 for (b = _buf_relax; b != NULL; b = b->b._next) {
297 b->b._caster = (b->b._relax != NULL) ? b->b._relax : b->b._bot;
298 #ifdef HAVE_DEBUG
299 memset(b->b._caster, 0377, PTR2SIZE(b->b._max - b->b._caster));
300 #endif
304 #ifdef HAVE_DEBUG
305 FL int
306 c_sstats(void *v)
308 size_t excess;
309 UNUSED(v);
311 excess = (_all_cybufcnt_max * SDYN_SIZE) + SBLTIN_SIZE;
312 excess = (excess >= _all_cysize_max) ? 0 : _all_cysize_max - excess;
314 printf("String usage statistics (cycle means one sreset() cycle):\n"
315 " Buffer allocs ever/max simultan. : %lu/%lu\n"
316 " Buffer size of builtin(1)/dynamic: %lu/%lu\n"
317 " Overall alloc count/bytes : %lu/%lu\n"
318 " Alloc bytes min/max/align wastage: %lu/%lu/%lu\n"
319 " sreset() cycles : %lu (%lu performed)\n"
320 " Cycle maximums: alloc count/bytes: %lu/%lu+%lu\n",
321 (ul_it)_all_bufcnt, (ul_it)_all_cybufcnt_max,
322 (ul_it)SBLTIN_SIZE, (ul_it)SDYN_SIZE,
323 (ul_it)_all_cnt, (ul_it)_all_size,
324 (ul_it)_all_min, (ul_it)_all_max, (ul_it)_all_wast,
325 (ul_it)_all_resetreqs, (ul_it)_all_resets,
326 (ul_it)_all_cycnt_max, (ul_it)_all_cysize_max, (ul_it)excess);
327 return 0;
329 #endif
332 * Return a pointer to a dynamic copy of the argument.
334 FL char *
335 savestr(const char *str)
337 size_t size = strlen(str) + 1;
338 char *news = salloc(size);
339 memcpy(news, str, size);
340 return (news);
344 * Return new string copy of a non-terminated argument.
346 FL char *
347 savestrbuf(const char *sbuf, size_t sbuf_len)
349 char *news = salloc(sbuf_len + 1);
350 memcpy(news, sbuf, sbuf_len);
351 news[sbuf_len] = 0;
352 return (news);
356 * Make a copy of new argument incorporating old one.
358 FL char *
359 save2str(const char *str, const char *old)
361 size_t newsize = strlen(str) + 1, oldsize = old ? strlen(old) + 1 : 0;
362 char *news = salloc(newsize + oldsize);
363 if (oldsize) {
364 memcpy(news, old, oldsize);
365 news[oldsize - 1] = ' ';
367 memcpy(news + oldsize, str, newsize);
368 return (news);
371 FL char *
372 savecat(char const *s1, char const *s2)
374 size_t l1 = strlen(s1), l2 = strlen(s2);
375 char *news = salloc(l1 + l2 + 1);
376 memcpy(news + 0, s1, l1);
377 memcpy(news + l1, s2, l2);
378 news[l1 + l2] = '\0';
379 return (news);
383 * Support routines, auto-reclaimed storage
386 FL char *
387 i_strdup(char const *src)
389 size_t sz;
390 char *dest;
392 sz = strlen(src) + 1;
393 dest = salloc(sz);
394 i_strcpy(dest, src, sz);
395 return (dest);
398 FL char *
399 protbase(char const *cp)
401 char *n = salloc(strlen(cp) + 1), *np = n;
403 /* Just ignore the `is-system-mailbox' prefix XXX */
404 if (cp[0] == '%' && cp[1] == ':')
405 cp += 2;
407 while (*cp) {
408 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
409 *np++ = *cp++;
410 *np++ = *cp++;
411 *np++ = *cp++;
412 } else if (cp[0] == '/')
413 break;
414 else
415 *np++ = *cp++;
417 *np = '\0';
418 return n;
421 FL char *
422 urlxenc(char const *cp) /* XXX */
424 char *n, *np;
426 np = n = salloc(strlen(cp) * 3 + 1);
427 while (*cp) {
428 if (alnumchar(*cp) || *cp == '_' || *cp == '@' ||
429 (np > n && (*cp == '.' || *cp == '-' ||
430 *cp == ':')))
431 *np++ = *cp;
432 else {
433 *np++ = '%';
434 *np++ = Hexchar((*cp&0xf0) >> 4);
435 *np++ = Hexchar(*cp&0x0f);
437 cp++;
439 *np = '\0';
440 return n;
443 FL char *
444 urlxdec(char const *cp) /* XXX */
446 char *n, *np;
448 np = n = salloc(strlen(cp) + 1);
449 while (*cp) {
450 if (cp[0] == '%' && cp[1] && cp[2]) {
451 *np = (int)(cp[1]>'9'?cp[1]-'A'+10:cp[1]-'0') << 4;
452 *np++ |= cp[2]>'9'?cp[2]-'A'+10:cp[2]-'0';
453 cp += 3;
454 } else
455 *np++ = *cp++;
457 *np = '\0';
458 return (n);
461 FL struct str *
462 str_concat_csvl(struct str *self, ...) /* XXX onepass maybe better here */
464 va_list vl;
465 size_t l;
466 char const *cs;
468 va_start(vl, self);
469 for (l = 0; (cs = va_arg(vl, char const*)) != NULL;)
470 l += strlen(cs);
471 va_end(vl);
473 self->l = l;
474 self->s = salloc(l + 1);
476 va_start(vl, self);
477 for (l = 0; (cs = va_arg(vl, char const*)) != NULL;) {
478 size_t i = strlen(cs);
479 memcpy(self->s + l, cs, i);
480 l += i;
482 self->s[l] = '\0';
483 va_end(vl);
484 return self;
487 FL struct str *
488 str_concat_cpa(struct str *self, char const *const*cpa, char const *sep_o_null)
490 size_t sonl, l;
491 char const *const*xcpa;
493 sonl = (sep_o_null != NULL) ? strlen(sep_o_null) : 0;
495 for (l = 0, xcpa = cpa; *xcpa != NULL; ++xcpa)
496 l += strlen(*xcpa) + sonl;
498 self->l = l;
499 self->s = salloc(l + 1);
501 for (l = 0, xcpa = cpa; *xcpa != NULL; ++xcpa) {
502 size_t i = strlen(*xcpa);
503 memcpy(self->s + l, *xcpa, i);
504 l += i;
505 if (sonl > 0) {
506 memcpy(self->s + l, sep_o_null, sonl);
507 l += sonl;
510 self->s[l] = '\0';
511 return self;
515 * Routines that are not related to auto-reclaimed storage follow.
518 uc_it const class_char[] = {
519 /* 000 nul 001 soh 002 stx 003 etx 004 eot 005 enq 006 ack 007 bel */
520 C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,
521 /* 010 bs 011 ht 012 nl 013 vt 014 np 015 cr 016 so 017 si */
522 C_CNTRL,C_BLANK,C_WHITE,C_SPACE,C_SPACE,C_SPACE,C_CNTRL,C_CNTRL,
523 /* 020 dle 021 dc1 022 dc2 023 dc3 024 dc4 025 nak 026 syn 027 etb */
524 C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,
525 /* 030 can 031 em 032 sub 033 esc 034 fs 035 gs 036 rs 037 us */
526 C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,C_CNTRL,
527 /* 040 sp 041 ! 042 " 043 # 044 $ 045 % 046 & 047 ' */
528 C_BLANK,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,
529 /* 050 ( 051 ) 052 * 053 + 054 , 055 - 056 . 057 / */
530 C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,
531 /* 060 0 061 1 062 2 063 3 064 4 065 5 066 6 067 7 */
532 C_OCTAL,C_OCTAL,C_OCTAL,C_OCTAL,C_OCTAL,C_OCTAL,C_OCTAL,C_OCTAL,
533 /* 070 8 071 9 072 : 073 ; 074 < 075 = 076 > 077 ? */
534 C_DIGIT,C_DIGIT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,
535 /* 100 @ 101 A 102 B 103 C 104 D 105 E 106 F 107 G */
536 C_PUNCT,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,
537 /* 110 H 111 I 112 J 113 K 114 L 115 M 116 N 117 O */
538 C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,
539 /* 120 P 121 Q 122 R 123 S 124 T 125 U 126 V 127 W */
540 C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,C_UPPER,
541 /* 130 X 131 Y 132 Z 133 [ 134 \ 135 ] 136 ^ 137 _ */
542 C_UPPER,C_UPPER,C_UPPER,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,
543 /* 140 ` 141 a 142 b 143 c 144 d 145 e 146 f 147 g */
544 C_PUNCT,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,
545 /* 150 h 151 i 152 j 153 k 154 l 155 m 156 n 157 o */
546 C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,
547 /* 160 p 161 q 162 r 163 s 164 t 165 u 166 v 167 w */
548 C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,C_LOWER,
549 /* 170 x 171 y 172 z 173 { 174 | 175 } 176 ~ 177 del */
550 C_LOWER,C_LOWER,C_LOWER,C_PUNCT,C_PUNCT,C_PUNCT,C_PUNCT,C_CNTRL
553 FL int
554 anyof(char const *s1, char const *s2)
556 for (; *s1 != '\0'; ++s1)
557 if (strchr(s2, *s1))
558 break;
559 return (*s1 != '\0');
562 FL ui_it
563 strhash(char const *name)
565 ui_it h = 0;
567 while (*name != '\0') {
568 h *= 33;
569 h += *name++;
571 return h;
574 FL char *
575 strcomma(char **iolist, int ignore_empty)
577 char *base, *cp;
579 for (base = *iolist; base != NULL; base = *iolist) {
580 while (*base != '\0' && blankspacechar(*base))
581 ++base;
582 cp = strchr(base, ',');
583 if (cp != NULL)
584 *iolist = cp + 1;
585 else {
586 *iolist = NULL;
587 cp = base + strlen(base);
589 while (cp > base && blankspacechar(cp[-1]))
590 --cp;
591 *cp = '\0';
592 if (*base != '\0' || ! ignore_empty)
593 break;
595 return (base);
598 FL void
599 i_strcpy(char *dest, const char *src, size_t size)
601 if (size > 0) {
602 for (;; ++dest, ++src)
603 if ((*dest = lowerconv(*src)) == '\0') {
604 break;
605 } else if (--size == 0) {
606 *dest = '\0';
607 break;
612 FL int
613 is_prefix(char const *as1, char const *as2)
615 char c;
616 for (; (c = *as1) == *as2 && c != '\0'; ++as1, ++as2)
617 if (*as2 == '\0')
618 break;
619 return (c == '\0');
622 FL char const *
623 last_at_before_slash(char const *sp)
625 char const *cp;
626 char c;
628 for (cp = sp; (c = *cp) != '\0'; ++cp)
629 if (c == '/')
630 break;
631 while (cp > sp && *--cp != '@')
633 return (*cp == '@' ? cp : NULL);
636 FL char *
637 laststring(char *linebuf, bool_t *needs_list, bool_t strip)
639 char *cp, *p, quoted;
641 /* Anything to do at all? */
642 if (*(cp = linebuf) == '\0')
643 goto jnull;
644 cp += strlen(linebuf) - 1;
646 /* Strip away trailing blanks */
647 while (whitechar(*cp) && PTRCMP(cp, >, linebuf))
648 --cp;
649 cp[1] = '\0';
650 if (cp == linebuf)
651 goto jleave;
653 /* Now search for the BOS of the "last string" */
654 quoted = *cp;
655 if (quoted == '\'' || quoted == '"') {
656 if (strip)
657 *cp = '\0';
658 } else
659 quoted = ' ';
661 while (PTRCMP(cp, >, linebuf)) {
662 --cp;
663 if (quoted != ' ') {
664 if (*cp != quoted)
665 continue;
666 } else if (!whitechar(*cp))
667 continue;
668 if (PTRCMP(cp, ==, linebuf) || cp[-1] != '\\') {
669 /* When in whitespace mode, WS prefix doesn't belong */
670 if (quoted == ' ')
671 ++cp;
672 break;
674 /* Expand the escaped quote character */
675 for (p = --cp; (p[0] = p[1]) != '\0'; ++p)
678 if (strip && quoted != ' ' && *cp == quoted)
679 for (p = cp; (p[0] = p[1]) != '\0'; ++p)
682 /* The "last string" has been skipped over, but still, try to step backwards
683 * until we are at BOS or see whitespace, so as to make possible things like
684 * "? copy +'x y.mbox'" or even "? copy +x\ y.mbox" */
685 while (PTRCMP(cp, >, linebuf)) {
686 --cp;
687 if (whitechar(*cp)) {
688 p = cp;
689 *cp++ = '\0';
690 /* We can furtherly release our callees if we now decide wether the
691 * remaining non-"last string" line content contains non-WS */
692 while (PTRCMP(--p, >=, linebuf))
693 if (!whitechar(*p))
694 goto jleave;
695 linebuf = cp;
696 break;
700 jleave:
701 if (cp != NULL && *cp == '\0')
702 goto jnull;
703 *needs_list = (cp != linebuf && *linebuf != '\0');
704 j_leave:
705 return cp;
706 jnull:
707 *needs_list = FAL0;
708 cp = NULL;
709 goto j_leave;
712 FL void
713 makelow(char *cp) /* TODO isn't that crap? --> */
715 #ifdef HAVE_C90AMEND1
716 if (mb_cur_max > 1) {
717 char *tp = cp;
718 wchar_t wc;
719 int len;
721 while (*cp) {
722 len = mbtowc(&wc, cp, mb_cur_max);
723 if (len < 0)
724 *tp++ = *cp++;
725 else {
726 wc = towlower(wc);
727 if (wctomb(tp, wc) == len)
728 tp += len, cp += len;
729 else
730 *tp++ = *cp++; /* <-- at least here */
733 } else
734 #endif
737 *cp = tolower((uc_it)*cp);
738 while (*cp++);
742 FL int
743 substr(char const *str, char const *sub)
745 char const *cp, *backup;
747 cp = sub;
748 backup = str;
749 while (*str && *cp) {
750 #ifdef HAVE_C90AMEND1
751 if (mb_cur_max > 1) {
752 wchar_t c, c2;
753 int sz;
755 if ((sz = mbtowc(&c, cp, mb_cur_max)) < 0)
756 goto singlebyte;
757 cp += sz;
758 if ((sz = mbtowc(&c2, str, mb_cur_max)) < 0)
759 goto singlebyte;
760 str += sz;
761 c = towupper(c);
762 c2 = towupper(c2);
763 if (c != c2) {
764 if ((sz = mbtowc(&c, backup, mb_cur_max)) > 0) {
765 backup += sz;
766 str = backup;
767 } else
768 str = ++backup;
769 cp = sub;
771 } else
772 #endif
774 int c, c2;
776 #ifdef HAVE_C90AMEND1
777 singlebyte:
778 #endif
779 c = *cp++ & 0377;
780 if (islower(c))
781 c = toupper(c);
782 c2 = *str++ & 0377;
783 if (islower(c2))
784 c2 = toupper(c2);
785 if (c != c2) {
786 str = ++backup;
787 cp = sub;
791 return *cp == '\0';
794 #ifndef HAVE_SNPRINTF
795 FL int
796 snprintf(char *str, size_t size, const char *format, ...) /* XXX DANGER! */
798 va_list ap;
799 int ret;
801 va_start(ap, format);
802 ret = vsprintf(str, format, ap);
803 va_end(ap);
804 if (ret < 0)
805 ret = strlen(str);
806 return ret;
808 #endif
810 FL char *
811 sstpcpy(char *dst, char const *src)
813 while ((*dst = *src++) != '\0')
814 dst++;
815 return (dst);
818 FL char *
819 (sstrdup)(char const *cp SMALLOC_DEBUG_ARGS)
821 char *dp = NULL;
823 if (cp != NULL) {
824 size_t l = strlen(cp) + 1;
825 dp = (smalloc)(l SMALLOC_DEBUG_ARGSCALL);
826 memcpy(dp, cp, l);
828 return dp;
831 #ifdef notyet
832 FL char *
833 (sbufdup)(char const *cp, size_t len SMALLOC_DEBUG_ARGS)
835 char *dp = NULL;
837 dp = (smalloc)(len + 1 SMALLOC_DEBUG_ARGSCALL);
838 if (cp != NULL)
839 memcpy(dp, cp, len);
840 dp[len] = '\0';
841 return dp;
843 #endif
845 FL char *
846 n_strlcpy(char *dst, const char *src, size_t len)
848 assert(len > 0);
850 dst = strncpy(dst, src, len);
851 dst[len - 1] = '\0';
852 return dst;
855 FL int
856 asccasecmp(char const *s1, char const *s2)
858 int cmp;
860 for (;;) {
861 char c1 = *s1++, c2 = *s2++;
862 if ((cmp = lowerconv(c1) - lowerconv(c2)) != 0 || c1 == '\0')
863 break;
865 return cmp;
868 FL int
869 ascncasecmp(char const *s1, char const *s2, size_t sz)
871 int cmp = 0;
873 while (sz-- > 0) {
874 char c1 = *s1++, c2 = *s2++;
875 cmp = (ui8_t)lowerconv(c1);
876 cmp -= (ui8_t)lowerconv(c2);
877 if (cmp != 0 || c1 == '\0')
878 break;
880 return cmp;
883 FL char const *
884 asccasestr(char const *haystack, char const *xneedle)
886 char *needle = NULL, *NEEDLE;
887 size_t i, sz;
889 sz = strlen(xneedle);
890 if (sz == 0)
891 goto jleave;
893 needle = ac_alloc(sz);
894 NEEDLE = ac_alloc(sz);
895 for (i = 0; i < sz; i++) {
896 needle[i] = lowerconv(xneedle[i]);
897 NEEDLE[i] = upperconv(xneedle[i]);
900 while (*haystack) {
901 if (*haystack == *needle || *haystack == *NEEDLE) {
902 for (i = 1; i < sz; i++)
903 if (haystack[i] != needle[i] &&
904 haystack[i] != NEEDLE[i])
905 break;
906 if (i == sz)
907 goto jleave;
909 haystack++;
911 haystack = NULL;
912 jleave:
913 if (needle != NULL) {
914 ac_free(NEEDLE);
915 ac_free(needle);
917 return haystack;
920 FL bool_t
921 is_asccaseprefix(char const *as1, char const *as2)
923 bool_t rv = FAL0;
925 for (;; ++as1, ++as2) {
926 char c1 = lowerconv(*as1), c2 = lowerconv(*as2);
927 if ((rv = (c1 == '\0')))
928 break;
929 if (c1 != c2 || c2 == '\0')
930 break;
932 return rv;
935 FL struct str *
936 (n_str_dup)(struct str *self, struct str const *t SMALLOC_DEBUG_ARGS)
938 if (t != NULL && t->l > 0) {
939 self->l = t->l;
940 self->s = (srealloc)(self->s, t->l + 1 SMALLOC_DEBUG_ARGSCALL);
941 memcpy(self->s, t->s, t->l);
942 } else
943 self->l = 0;
944 return self;
947 FL struct str *
948 (n_str_add_buf)(struct str *self, char const *buf, size_t buflen
949 SMALLOC_DEBUG_ARGS)
951 if (buflen != 0) {
952 size_t sl = self->l;
953 self->l = sl + buflen;
954 self->s = (srealloc)(self->s, self->l+1 SMALLOC_DEBUG_ARGSCALL);
955 memcpy(self->s + sl, buf, buflen);
957 return self;
960 #ifdef HAVE_ICONV
961 static void _ic_toupper(char *dest, char const *src);
962 static void _ic_stripdash(char *p);
964 static void
965 _ic_toupper(char *dest, const char *src)
968 *dest++ = upperconv(*src);
969 while (*src++);
972 static void
973 _ic_stripdash(char *p)
975 char *q = p;
978 if (*(q = p) != '-')
979 q++;
980 while (*p++);
983 FL iconv_t
984 n_iconv_open(char const *tocode, char const *fromcode)
986 iconv_t id;
987 char *t, *f;
989 if ((id = iconv_open(tocode, fromcode)) != (iconv_t)-1)
990 return id;
993 * Remove the "iso-" prefixes for Solaris.
995 if (ascncasecmp(tocode, "iso-", 4) == 0)
996 tocode += 4;
997 else if (ascncasecmp(tocode, "iso", 3) == 0)
998 tocode += 3;
999 if (ascncasecmp(fromcode, "iso-", 4) == 0)
1000 fromcode += 4;
1001 else if (ascncasecmp(fromcode, "iso", 3) == 0)
1002 fromcode += 3;
1003 if (*tocode == '\0' || *fromcode == '\0')
1004 return (iconv_t) -1;
1005 if ((id = iconv_open(tocode, fromcode)) != (iconv_t)-1)
1006 return id;
1008 * Solaris prefers upper-case charset names. Don't ask...
1010 t = salloc(strlen(tocode) + 1);
1011 _ic_toupper(t, tocode);
1012 f = salloc(strlen(fromcode) + 1);
1013 _ic_toupper(f, fromcode);
1014 if ((id = iconv_open(t, f)) != (iconv_t)-1)
1015 return id;
1017 * Strip dashes for UnixWare.
1019 _ic_stripdash(t);
1020 _ic_stripdash(f);
1021 if ((id = iconv_open(t, f)) != (iconv_t)-1)
1022 return id;
1024 * Add your vendor's sillynesses here.
1028 * If the encoding names are equal at this point, they
1029 * are just not understood by iconv(), and we cannot
1030 * sensibly use it in any way. We do not perform this
1031 * as an optimization above since iconv() can otherwise
1032 * be used to check the validity of the input even with
1033 * identical encoding names.
1035 if (strcmp(t, f) == 0)
1036 errno = 0;
1037 return (iconv_t)-1;
1040 FL void
1041 n_iconv_close(iconv_t cd)
1043 iconv_close(cd);
1044 if (cd == iconvd)
1045 iconvd = (iconv_t)-1;
1048 #ifdef notyet
1049 FL void
1050 n_iconv_reset(iconv_t cd)
1052 (void)iconv(cd, NULL, NULL, NULL, NULL);
1054 #endif
1057 * (2012-09-24: export and use it exclusively to isolate prototype problems
1058 * (*inb* is 'char const **' except in POSIX) in a single place.
1059 * GNU libiconv even allows for configuration time const/non-const..
1060 * In the end it's an ugly guess, but we can't do better since make(1) doesn't
1061 * support compiler invocations which bail on error, so no -Werror.
1063 /* Citrus project? */
1064 # if defined _ICONV_H_ && defined __ICONV_F_HIDE_INVALID
1065 /* DragonFly 3.2.1 is special */
1066 # ifdef __DragonFly__
1067 # define __INBCAST(S) (char ** __restrict__)UNCONST(S)
1068 # else
1069 # define __INBCAST(S) (char const **)UNCONST(S)
1070 # endif
1071 # endif
1072 # ifndef __INBCAST
1073 # define __INBCAST(S) (char **)UNCONST(S)
1074 # endif
1076 FL int
1077 n_iconv_buf(iconv_t cd, char const **inb, size_t *inbleft,/*XXX redo iconv use*/
1078 char **outb, size_t *outbleft, bool_t skipilseq)
1080 int err = 0;
1082 for (;;) {
1083 size_t sz = iconv(cd, __INBCAST(inb), inbleft, outb, outbleft);
1084 if (sz != (size_t)-1)
1085 break;
1086 err = errno;
1087 if (! skipilseq || err != EILSEQ)
1088 break;
1089 if (*inbleft > 0) {
1090 ++(*inb);
1091 --(*inbleft);
1092 } else if (*outbleft > 0) {
1093 **outb = '\0';
1094 break;
1096 if (*outbleft > 0/* TODO 0xFFFD 2*/) {
1097 /* TODO 0xFFFD (*outb)[0] = '[';
1098 * TODO (*outb)[1] = '?';
1099 * TODO 0xFFFD (*outb)[2] = ']';
1100 * TODO (*outb) += 3;
1101 * TODO (*outbleft) -= 3; */
1102 *(*outb)++ = '?';
1103 --*outbleft;
1104 } else {
1105 err = E2BIG;
1106 break;
1108 err = 0;
1110 return err;
1112 # undef __INBCAST
1114 FL int
1115 n_iconv_str(iconv_t cd, struct str *out, struct str const *in,
1116 struct str *in_rest_or_null, bool_t skipilseq)
1118 int err = 0;
1119 char *obb = out->s, *ob;
1120 char const *ib;
1121 size_t olb = out->l, ol, il;
1123 ol = in->l;
1124 ol = (ol << 1) - (ol >> 4);
1125 if (olb < ol) {
1126 olb = ol;
1127 goto jrealloc;
1130 for (;;) {
1131 ib = in->s;
1132 il = in->l;
1133 ob = obb;
1134 ol = olb;
1135 err = n_iconv_buf(cd, &ib, &il, &ob, &ol, skipilseq);
1136 if (err == 0 || err != E2BIG)
1137 break;
1138 err = 0;
1139 olb += in->l;
1140 jrealloc: obb = srealloc(obb, olb);
1143 if (in_rest_or_null != NULL) {
1144 in_rest_or_null->s = UNCONST(ib);
1145 in_rest_or_null->l = il;
1147 out->s = obb;
1148 out->l = olb - ol;
1149 return err;
1151 #endif /* HAVE_ICONV */
1153 /* vim:set fenc=utf-8:s-it-mode (TODO only partial true) */