Move mailbox/folder handling to new folder.c
[s-mailx.git] / strings.c
blob4a554c3e05d98373abb6bb216bd7ade5091dbc02
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 - 2015 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. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
39 #undef n_FILE
40 #define n_FILE strings
42 #ifndef HAVE_AMALGAMATION
43 # include "nail.h"
44 #endif
46 #include <ctype.h>
48 /* In debug mode the "string dope" allocations are enwrapped in canaries, just
49 * as we do with our normal memory allocator */
50 #ifdef HAVE_DEBUG
51 # define _SHOPE_SIZE (2u * 8 * sizeof(char) + sizeof(struct schunk))
53 CTA(sizeof(char) == sizeof(ui8_t));
55 struct schunk {
56 char const *file;
57 ui32_t line;
58 ui16_t usr_size;
59 ui16_t full_size;
62 union sptr {
63 void *p;
64 struct schunk *c;
65 char *cp;
66 ui8_t *ui8p;
68 #endif /* HAVE_DEBUG */
70 union __align__ {
71 char *cp;
72 size_t sz;
73 ul_i ul;
75 #define SALIGN (sizeof(union __align__) - 1)
77 CTA(ISPOW2(SALIGN + 1));
79 struct b_base {
80 struct buffer *_next;
81 char *_bot; /* For spreserve() */
82 char *_relax; /* If !NULL, used by srelax() instead of ._bot */
83 char *_max; /* Max usable byte */
84 char *_caster; /* NULL if full */
87 /* Single instance builtin buffer. Room for anything, most of the time */
88 struct b_bltin {
89 struct b_base b_base;
90 char b_buf[SBUFFER_BUILTIN - sizeof(struct b_base)];
92 #define SBLTIN_SIZE SIZEOF_FIELD(struct b_bltin, b_buf)
94 /* Dynamically allocated buffers to overcome shortage, always released again
95 * once the command loop ticks (without relaxation or during PS_SOURCING) */
96 struct b_dyn {
97 struct b_base b_base;
98 char b_buf[SBUFFER_SIZE - sizeof(struct b_base)];
100 #define SDYN_SIZE SIZEOF_FIELD(struct b_dyn, b_buf)
102 /* The multiplexer of the several real b_* */
103 struct buffer {
104 struct b_base b;
105 char b_buf[VFIELD_SIZE(SALIGN + 1)];
108 /* Requests that exceed SDYN_SIZE-1 and thus cannot be handled by string dope
109 * are always served by the normal memory allocator (which panics if memory
110 * cannot be served). Note such an allocation has not yet occurred, it is only
111 * included as a security fallback bypass */
112 struct hugebuf {
113 struct hugebuf *hb_next;
114 char hb_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 static size_t _relax_recur_no;
120 static struct hugebuf *_huge_list;
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;
139 /*NYD2_ENTER;*/
141 pmax.cp = (b->b._caster == NULL) ? b->b._max : b->b._caster;
142 pp.cp = b->b._bot;
144 while (pp.cp < pmax.cp) {
145 struct schunk *c;
146 union sptr x;
147 void *ux;
148 ui8_t i;
150 c = pp.c;
151 pp.cp += c->full_size;
152 x.p = c + 1;
153 ux = x.cp + 8;
155 i = 0;
156 if (x.ui8p[0] != 0xDE) i |= 1<<0;
157 if (x.ui8p[1] != 0xAA) i |= 1<<1;
158 if (x.ui8p[2] != 0x55) i |= 1<<2;
159 if (x.ui8p[3] != 0xAD) i |= 1<<3;
160 if (x.ui8p[4] != 0xBE) i |= 1<<4;
161 if (x.ui8p[5] != 0x55) i |= 1<<5;
162 if (x.ui8p[6] != 0xAA) i |= 1<<6;
163 if (x.ui8p[7] != 0xEF) i |= 1<<7;
164 if (i != 0)
165 n_alert("sdope %p: corrupt lower canary: 0x%02X, size %u: %s, line %u",
166 ux, i, c->usr_size, c->file, c->line);
167 x.cp += 8 + c->usr_size;
169 i = 0;
170 if (x.ui8p[0] != 0xDE) i |= 1<<0;
171 if (x.ui8p[1] != 0xAA) i |= 1<<1;
172 if (x.ui8p[2] != 0x55) i |= 1<<2;
173 if (x.ui8p[3] != 0xAD) i |= 1<<3;
174 if (x.ui8p[4] != 0xBE) i |= 1<<4;
175 if (x.ui8p[5] != 0x55) i |= 1<<5;
176 if (x.ui8p[6] != 0xAA) i |= 1<<6;
177 if (x.ui8p[7] != 0xEF) i |= 1<<7;
178 if (i != 0)
179 n_alert("sdope %p: corrupt upper canary: 0x%02X, size %u: %s, line %u",
180 ux, i, c->usr_size, c->file, c->line);
182 /*NYD2_LEAVE;*/
184 #endif
186 FL void *
187 (salloc)(size_t size SALLOC_DEBUG_ARGS)
189 DBG( size_t orig_size = size; )
190 union {struct buffer *b; struct hugebuf *hb; char *cp;} u;
191 char *x, *y, *z;
192 NYD2_ENTER;
194 if (size == 0)
195 ++size;
196 size += SALIGN;
197 size &= ~SALIGN;
199 #ifdef HAVE_DEBUG
200 ++_all_cnt;
201 ++_all_cycnt;
202 _all_cycnt_max = MAX(_all_cycnt_max, _all_cycnt);
203 _all_size += size;
204 _all_cysize += size;
205 _all_cysize_max = MAX(_all_cysize_max, _all_cysize);
206 _all_min = (_all_max == 0) ? size : MIN(_all_min, size);
207 _all_max = MAX(_all_max, size);
208 _all_wast += size - orig_size;
210 size += _SHOPE_SIZE;
212 if (size >= SDYN_SIZE - 1)
213 n_alert("salloc() of %" PRIuZ " bytes from \"%s\", line %d",
214 size, mdbg_file, mdbg_line);
215 #endif
217 /* Huge allocations are special */
218 if (UNLIKELY(size >= SDYN_SIZE - 1))
219 goto jhuge;
221 /* Search for a buffer with enough free space to serve request */
222 if ((u.b = _buf_server) != NULL)
223 goto jumpin;
224 jredo:
225 for (u.b = _buf_head; u.b != NULL; u.b = u.b->b._next) {
226 jumpin:
227 x = u.b->b._caster;
228 if (x == NULL) {
229 if (u.b == _buf_server) {
230 if (u.b == _buf_head && (u.b = _buf_head->b._next) != NULL) {
231 _buf_server = u.b;
232 goto jumpin;
234 _buf_server = NULL;
235 goto jredo;
237 continue;
239 y = x + size;
240 z = u.b->b._max;
241 if (PTRCMP(y, <=, z)) {
242 /* Alignment is the one thing, the other is what is usually allocated,
243 * and here about 40 bytes seems to be a good cut to avoid non-usable
244 * non-NULL casters. However, because of _salloc_bcheck(), we may not
245 * set ._caster to NULL because then it would check all chunks up to
246 * ._max, which surely doesn't work; speed is no issue with DEBUG */
247 u.b->b._caster = NDBG( PTRCMP(y + 42 + 16, >=, z) ? NULL : ) y;
248 u.cp = x;
249 goto jleave;
253 /* Need a new buffer */
254 if (_buf_head == NULL) {
255 struct b_bltin *b = &_builtin_buf;
256 b->b_base._max = b->b_buf + SBLTIN_SIZE - 1;
257 _buf_head = (struct buffer*)b;
258 u.b = _buf_head;
259 } else {
260 #ifdef HAVE_DEBUG
261 ++_all_bufcnt;
262 ++_all_cybufcnt;
263 _all_cybufcnt_max = MAX(_all_cybufcnt_max, _all_cybufcnt);
264 #endif
265 u.b = smalloc(sizeof(struct b_dyn));
266 u.b->b._max = u.b->b_buf + SDYN_SIZE - 1;
268 if (_buf_list != NULL)
269 _buf_list->b._next = u.b;
270 _buf_server = _buf_list = u.b;
271 u.b->b._next = NULL;
272 u.b->b._caster = (u.b->b._bot = u.b->b_buf) + size;
273 u.b->b._relax = NULL;
274 u.cp = u.b->b._bot;
276 jleave:
277 /* Encapsulate user chunk in debug canaries */
278 #ifdef HAVE_DEBUG
280 union sptr xl, xu;
281 struct schunk *xc;
283 xl.p = u.cp;
284 xc = xl.c;
285 xc->file = mdbg_file;
286 xc->line = mdbg_line;
287 xc->usr_size = (ui16_t)orig_size;
288 xc->full_size = (ui16_t)size;
289 xl.p = xc + 1;
290 xl.ui8p[0]=0xDE; xl.ui8p[1]=0xAA; xl.ui8p[2]=0x55; xl.ui8p[3]=0xAD;
291 xl.ui8p[4]=0xBE; xl.ui8p[5]=0x55; xl.ui8p[6]=0xAA; xl.ui8p[7]=0xEF;
292 u.cp = xl.cp + 8;
293 xu.p = u.cp;
294 xu.cp += orig_size;
295 xu.ui8p[0]=0xDE; xu.ui8p[1]=0xAA; xu.ui8p[2]=0x55; xu.ui8p[3]=0xAD;
296 xu.ui8p[4]=0xBE; xu.ui8p[5]=0x55; xu.ui8p[6]=0xAA; xu.ui8p[7]=0xEF;
298 #endif
299 NYD2_LEAVE;
300 return u.cp;
302 jhuge:
303 u.hb = smalloc(sizeof(*u.hb) - VFIELD_SIZEOF(struct hugebuf, hb_buf) +
304 size +1);
305 u.hb->hb_next = _huge_list;
306 _huge_list = u.hb;
307 u.cp = u.hb->hb_buf;
308 goto jleave;
311 FL void *
312 (csalloc)(size_t nmemb, size_t size SALLOC_DEBUG_ARGS)
314 void *vp;
315 NYD2_ENTER;
317 size *= nmemb;
318 vp = (salloc)(size SALLOC_DEBUG_ARGSCALL);
319 memset(vp, 0, size);
320 NYD2_LEAVE;
321 return vp;
324 FL void
325 sreset(bool_t only_if_relaxed)
327 struct buffer *blh, *bh;
328 NYD_ENTER;
330 DBG( ++_all_resetreqs; )
331 if (noreset) {
332 /* Reset relaxation after any jump is a MUST */
333 if (_relax_recur_no > 0)
334 srelax_rele();
335 goto jleave;
337 if (only_if_relaxed && _relax_recur_no == 0)
338 goto jleave;
340 #ifdef HAVE_DEBUG
341 _all_cycnt = _all_cysize = 0;
342 _all_cybufcnt = (_buf_head != NULL && _buf_head->b._next != NULL);
343 ++_all_resets;
344 #endif
346 /* Reset relaxation after jump */
347 if (_relax_recur_no > 0) {
348 srelax_rele();
349 assert(_relax_recur_no == 0);
352 blh = NULL;
353 if ((bh = _buf_head) != NULL) {
354 do {
355 struct buffer *x = bh;
356 bh = x->b._next;
357 DBG( _salloc_bcheck(x); )
359 /* Give away all buffers that are not covered by sreset().
360 * _buf_head is builtin and thus cannot be free()d */
361 if (blh != NULL && x->b._bot == x->b_buf) {
362 blh->b._next = bh;
363 free(x);
364 } else {
365 blh = x;
366 x->b._caster = x->b._bot;
367 x->b._relax = NULL;
368 DBG( memset(x->b._caster, 0377,
369 PTR2SIZE(x->b._max - x->b._caster)); )
371 } while (bh != NULL);
373 _buf_server = _buf_head;
374 _buf_list = blh;
375 _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 if (_relax_recur_no++ == 0) {
396 for (b = _buf_head; b != NULL; b = b->b._next)
397 b->b._relax = b->b._caster;
398 _buf_relax = _buf_server;
400 NYD_LEAVE;
403 FL void
404 srelax_rele(void)
406 struct buffer *b;
407 NYD_ENTER;
409 assert(_relax_recur_no > 0);
411 if (--_relax_recur_no == 0) {
412 for (b = _buf_head; 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;
418 _buf_relax = NULL;
420 #ifdef HAVE_DEVEL
421 else
422 n_err("srelax_rele(): recursion >0!\n");
423 #endif
424 NYD_LEAVE;
427 FL void
428 srelax(void)
430 /* The purpose of relaxation is only that it is possible to reset the
431 * casters, *not* to give back memory to the system. We are presumably in
432 * an iteration over all messages of a mailbox, and it'd be quite
433 * counterproductive to give the system allocator a chance to waste time */
434 struct buffer *b;
435 NYD_ENTER;
437 assert(_relax_recur_no > 0);
439 if (_relax_recur_no == 1) {
440 for (b = _buf_head; b != NULL; b = b->b._next) {
441 DBG( _salloc_bcheck(b); )
442 b->b._caster = (b->b._relax != NULL) ? b->b._relax : b->b._bot;
443 DBG( memset(b->b._caster, 0377, PTR2SIZE(b->b._max - b->b._caster)); )
446 NYD_LEAVE;
449 FL void
450 spreserve(void)
452 struct buffer *b;
453 NYD_ENTER;
455 for (b = _buf_head; b != NULL; b = b->b._next)
456 b->b._bot = b->b._caster;
457 NYD_LEAVE;
460 #ifdef HAVE_DEBUG
461 FL int
462 c_sstats(void *v)
464 size_t excess;
465 NYD_ENTER;
466 UNUSED(v);
468 excess = (_all_cybufcnt_max * SDYN_SIZE) + SBLTIN_SIZE;
469 excess = (excess >= _all_cysize_max) ? 0 : _all_cysize_max - excess;
471 printf("String usage statistics (cycle means one sreset() cycle):\n"
472 " Buffer allocs ever/max a time : %" PRIuZ "/%" PRIuZ "\n"
473 " .. size of the builtin/dynamic: %" PRIuZ "/%" PRIuZ "\n"
474 " Overall alloc count/bytes : %" PRIuZ "/%" PRIuZ "\n"
475 " .. bytes min/max/align wastage: %" PRIuZ "/%" PRIuZ "/%" PRIuZ "\n"
476 " sreset() cycles : %" PRIuZ " (%" PRIuZ " performed)\n"
477 " Cycle max.: alloc count/bytes : %" PRIuZ "/%" PRIuZ "+%" PRIuZ "\n",
478 _all_bufcnt, _all_cybufcnt_max,
479 SBLTIN_SIZE, SDYN_SIZE,
480 _all_cnt, _all_size,
481 _all_min, _all_max, _all_wast,
482 _all_resetreqs, _all_resets,
483 _all_cycnt_max, _all_cysize_max, excess);
484 NYD_LEAVE;
485 return 0;
487 #endif
489 FL char *
490 (savestr)(char const *str SALLOC_DEBUG_ARGS)
492 size_t size;
493 char *news;
494 NYD_ENTER;
496 size = strlen(str) +1;
497 news = (salloc)(size SALLOC_DEBUG_ARGSCALL);
498 memcpy(news, str, size);
499 NYD_LEAVE;
500 return news;
503 FL char *
504 (savestrbuf)(char const *sbuf, size_t sbuf_len SALLOC_DEBUG_ARGS)
506 char *news;
507 NYD_ENTER;
509 news = (salloc)(sbuf_len +1 SALLOC_DEBUG_ARGSCALL);
510 memcpy(news, sbuf, sbuf_len);
511 news[sbuf_len] = 0;
512 NYD_LEAVE;
513 return news;
516 FL char *
517 (savecatsep)(char const *s1, char sep, char const *s2 SALLOC_DEBUG_ARGS)
519 size_t l1, l2;
520 char *news;
521 NYD_ENTER;
523 l1 = (s1 != NULL) ? strlen(s1) : 0;
524 l2 = strlen(s2);
525 news = (salloc)(l1 + (sep != '\0') + l2 +1 SALLOC_DEBUG_ARGSCALL);
526 if (l1 > 0) {
527 memcpy(news + 0, s1, l1);
528 if (sep != '\0')
529 news[l1++] = sep;
531 memcpy(news + l1, s2, l2);
532 news[l1 + l2] = '\0';
533 NYD_LEAVE;
534 return news;
538 * Support routines, auto-reclaimed storage
541 FL char *
542 (i_strdup)(char const *src SALLOC_DEBUG_ARGS)
544 size_t sz;
545 char *dest;
546 NYD_ENTER;
548 sz = strlen(src) +1;
549 dest = (salloc)(sz SALLOC_DEBUG_ARGSCALL);
550 i_strcpy(dest, src, sz);
551 NYD_LEAVE;
552 return dest;
555 FL struct str *
556 str_concat_csvl(struct str *self, ...) /* XXX onepass maybe better here */
558 va_list vl;
559 size_t l;
560 char const *cs;
561 NYD_ENTER;
563 va_start(vl, self);
564 for (l = 0; (cs = va_arg(vl, char const*)) != NULL;)
565 l += strlen(cs);
566 va_end(vl);
568 self->l = l;
569 self->s = salloc(l +1);
571 va_start(vl, self);
572 for (l = 0; (cs = va_arg(vl, char const*)) != NULL;) {
573 size_t i = strlen(cs);
574 memcpy(self->s + l, cs, i);
575 l += i;
577 self->s[l] = '\0';
578 va_end(vl);
579 NYD_LEAVE;
580 return self;
583 FL struct str *
584 (str_concat_cpa)(struct str *self, char const * const *cpa,
585 char const *sep_o_null SALLOC_DEBUG_ARGS)
587 size_t sonl, l;
588 char const * const *xcpa;
589 NYD_ENTER;
591 sonl = (sep_o_null != NULL) ? strlen(sep_o_null) : 0;
593 for (l = 0, xcpa = cpa; *xcpa != NULL; ++xcpa)
594 l += strlen(*xcpa) + sonl;
596 self->l = l;
597 self->s = (salloc)(l +1 SALLOC_DEBUG_ARGSCALL);
599 for (l = 0, xcpa = cpa; *xcpa != NULL; ++xcpa) {
600 size_t i = strlen(*xcpa);
601 memcpy(self->s + l, *xcpa, i);
602 l += i;
603 if (sonl > 0) {
604 memcpy(self->s + l, sep_o_null, sonl);
605 l += sonl;
608 self->s[l] = '\0';
609 NYD_LEAVE;
610 return self;
614 * Routines that are not related to auto-reclaimed storage follow.
617 FL int
618 anyof(char const *s1, char const *s2)
620 NYD2_ENTER;
621 for (; *s1 != '\0'; ++s1)
622 if (strchr(s2, *s1) != NULL)
623 break;
624 NYD2_LEAVE;
625 return (*s1 != '\0');
628 FL char *
629 n_strsep(char **iolist, char sep, bool_t ignore_empty)
631 char *base, *cp;
632 NYD2_ENTER;
634 for (base = *iolist; base != NULL; base = *iolist) {
635 while (*base != '\0' && blankspacechar(*base))
636 ++base;
637 cp = strchr(base, sep);
638 if (cp != NULL)
639 *iolist = cp + 1;
640 else {
641 *iolist = NULL;
642 cp = base + strlen(base);
644 while (cp > base && blankspacechar(cp[-1]))
645 --cp;
646 *cp = '\0';
647 if (*base != '\0' || !ignore_empty)
648 break;
650 NYD2_LEAVE;
651 return base;
654 FL char *
655 n_strescsep(char **iolist, char sep, bool_t ignore_empty){
656 char *cp, c, *base;
657 bool_t isesc, anyesc;
658 NYD2_ENTER;
660 assert(sep != '\0');
662 for(base = *iolist; base != NULL; base = *iolist){
663 while((c = *base) != '\0' && blankspacechar(c))
664 ++base;
666 for(isesc = anyesc = FAL0, cp = base;; ++cp){
667 if(UNLIKELY((c = *cp) == '\0')){
668 *iolist = NULL;
669 break;
670 }else if(!isesc){
671 if(c == sep){
672 *iolist = cp + 1;
673 break;
675 isesc = (c == '\\');
676 }else{
677 isesc = FAL0;
678 anyesc |= (c == sep);
682 while(cp > base && blankspacechar(cp[-1]))
683 --cp;
684 *cp = '\0';
686 if(*base != '\0'){
687 if(anyesc){
688 char *ins;
690 for(ins = cp = base;; ++ins)
691 if((c = *cp) == '\\' && cp[1] == sep){
692 *ins = sep;
693 cp += 2;
694 }else if((*ins = (++cp, c)) == '\0')
695 break;
697 break;
699 if(!ignore_empty)
700 break;
702 NYD2_LEAVE;
703 return base;
706 FL void
707 i_strcpy(char *dest, char const *src, size_t size)
709 NYD2_ENTER;
710 if (size > 0) {
711 for (;; ++dest, ++src)
712 if ((*dest = lowerconv(*src)) == '\0') {
713 break;
714 } else if (--size == 0) {
715 *dest = '\0';
716 break;
719 NYD2_LEAVE;
722 FL int
723 is_prefix(char const *as1, char const *as2)
725 char c;
726 NYD2_ENTER;
728 for (; (c = *as1) == *as2 && c != '\0'; ++as1, ++as2)
729 if (*as2 == '\0')
730 break;
731 NYD2_LEAVE;
732 return (c == '\0');
735 FL char *
736 string_quote(char const *v) /* TODO too simpleminded (getrawlist(), +++ ..) */
738 char const *cp;
739 size_t i;
740 char c, *rv;
741 NYD2_ENTER;
743 for (i = 0, cp = v; (c = *cp) != '\0'; ++i, ++cp)
744 if (c == '"' || c == '\\')
745 ++i;
746 rv = salloc(i +1);
748 for (i = 0, cp = v; (c = *cp) != '\0'; rv[i++] = c, ++cp)
749 if (c == '"' || c == '\\')
750 rv[i++] = '\\';
751 rv[i] = '\0';
752 NYD2_LEAVE;
753 return rv;
756 FL char *
757 laststring(char *linebuf, bool_t *needs_list, bool_t strip)
759 char *cp, *p, quoted;
760 NYD_ENTER;
762 /* Anything to do at all? */
763 if (*(cp = linebuf) == '\0')
764 goto jnull;
765 cp += strlen(linebuf) -1;
767 /* Strip away trailing blanks */
768 while (spacechar(*cp) && cp > linebuf)
769 --cp;
770 cp[1] = '\0';
771 if (cp == linebuf)
772 goto jleave;
774 /* Now search for the BOS of the "last string" */
775 quoted = *cp;
776 if (quoted == '\'' || quoted == '"') {
777 if (strip)
778 *cp = '\0';
779 } else
780 quoted = ' ';
782 while (cp > linebuf) {
783 --cp;
784 if (quoted != ' ') {
785 if (*cp != quoted)
786 continue;
787 } else if (!spacechar(*cp))
788 continue;
789 if (cp == linebuf || cp[-1] != '\\') {
790 /* When in whitespace mode, WS prefix doesn't belong */
791 if (quoted == ' ')
792 ++cp;
793 break;
795 /* Expand the escaped quote character */
796 for (p = --cp; (p[0] = p[1]) != '\0'; ++p)
799 if (strip && quoted != ' ' && *cp == quoted)
800 for (p = cp; (p[0] = p[1]) != '\0'; ++p)
803 /* The "last string" has been skipped over, but still, try to step backwards
804 * until we are at BOS or see whitespace, so as to make possible things like
805 * "? copy +'x y.mbox'" or even "? copy +x\ y.mbox" */
806 while (cp > linebuf) {
807 --cp;
808 if (spacechar(*cp)) {
809 p = cp;
810 *cp++ = '\0';
811 /* We can furtherly release our callees if we now decide wether the
812 * remaining non-"last string" line content contains non-WS */
813 while (--p >= linebuf)
814 if (!spacechar(*p))
815 goto jleave;
816 linebuf = cp;
817 break;
821 jleave:
822 if (cp != NULL && *cp == '\0')
823 goto jnull;
824 *needs_list = (cp != linebuf && *linebuf != '\0');
825 j_leave:
826 NYD_LEAVE;
827 return cp;
828 jnull:
829 *needs_list = FAL0;
830 cp = NULL;
831 goto j_leave;
834 FL void
835 makelow(char *cp) /* TODO isn't that crap? --> */
837 NYD_ENTER;
838 #ifdef HAVE_C90AMEND1
839 if (mb_cur_max > 1) {
840 char *tp = cp;
841 wchar_t wc;
842 int len;
844 while (*cp != '\0') {
845 len = mbtowc(&wc, cp, mb_cur_max);
846 if (len < 0)
847 *tp++ = *cp++;
848 else {
849 wc = towlower(wc);
850 if (wctomb(tp, wc) == len)
851 tp += len, cp += len;
852 else
853 *tp++ = *cp++; /* <-- at least here */
856 } else
857 #endif
860 *cp = tolower((uc_i)*cp);
861 while (*cp++ != '\0');
863 NYD_LEAVE;
866 FL bool_t
867 substr(char const *str, char const *sub)
869 char const *cp, *backup;
870 NYD_ENTER;
872 cp = sub;
873 backup = str;
874 while (*str != '\0' && *cp != '\0') {
875 #ifdef HAVE_C90AMEND1
876 if (mb_cur_max > 1) {
877 wchar_t c, c2;
878 int sz;
880 if ((sz = mbtowc(&c, cp, mb_cur_max)) == -1)
881 goto Jsinglebyte;
882 cp += sz;
883 if ((sz = mbtowc(&c2, str, mb_cur_max)) == -1)
884 goto Jsinglebyte;
885 str += sz;
886 c = towupper(c);
887 c2 = towupper(c2);
888 if (c != c2) {
889 if ((sz = mbtowc(&c, backup, mb_cur_max)) > 0) {
890 backup += sz;
891 str = backup;
892 } else
893 str = ++backup;
894 cp = sub;
896 } else
897 Jsinglebyte:
898 #endif
900 int c, c2;
902 c = *cp++ & 0377;
903 if (islower(c))
904 c = toupper(c);
905 c2 = *str++ & 0377;
906 if (islower(c2))
907 c2 = toupper(c2);
908 if (c != c2) {
909 str = ++backup;
910 cp = sub;
914 NYD_LEAVE;
915 return (*cp == '\0');
918 FL char *
919 sstpcpy(char *dst, char const *src)
921 NYD2_ENTER;
922 while ((*dst = *src++) != '\0')
923 ++dst;
924 NYD2_LEAVE;
925 return dst;
928 FL char *
929 (sstrdup)(char const *cp SMALLOC_DEBUG_ARGS)
931 char *dp;
932 NYD2_ENTER;
934 dp = (cp == NULL) ? NULL : (sbufdup)(cp, strlen(cp) SMALLOC_DEBUG_ARGSCALL);
935 NYD2_LEAVE;
936 return dp;
939 FL char *
940 (sbufdup)(char const *cp, size_t len SMALLOC_DEBUG_ARGS)
942 char *dp = NULL;
943 NYD2_ENTER;
945 dp = (smalloc)(len +1 SMALLOC_DEBUG_ARGSCALL);
946 if (cp != NULL)
947 memcpy(dp, cp, len);
948 dp[len] = '\0';
949 NYD2_LEAVE;
950 return dp;
953 FL ssize_t
954 n_strscpy(char *dst, char const *src, size_t dstsize){
955 ssize_t rv;
956 NYD2_ENTER;
958 if(LIKELY(dstsize > 0)){
959 rv = 0;
961 if((dst[rv] = src[rv]) == '\0')
962 goto jleave;
963 ++rv;
964 }while(--dstsize > 0);
965 dst[--rv] = '\0';
967 #ifdef HAVE_DEVEL
968 else
969 assert(dstsize > 0);
970 #endif
971 rv = -1;
972 jleave:
973 NYD2_LEAVE;
974 return rv;
977 FL int
978 asccasecmp(char const *s1, char const *s2)
980 int cmp;
981 NYD2_ENTER;
983 for (;;) {
984 char c1 = *s1++, c2 = *s2++;
985 if ((cmp = lowerconv(c1) - lowerconv(c2)) != 0 || c1 == '\0')
986 break;
988 NYD2_LEAVE;
989 return cmp;
992 FL int
993 ascncasecmp(char const *s1, char const *s2, size_t sz)
995 int cmp = 0;
996 NYD2_ENTER;
998 while (sz-- > 0) {
999 char c1 = *s1++, c2 = *s2++;
1000 cmp = (ui8_t)lowerconv(c1);
1001 cmp -= (ui8_t)lowerconv(c2);
1002 if (cmp != 0 || c1 == '\0')
1003 break;
1005 NYD2_LEAVE;
1006 return cmp;
1009 FL char const *
1010 asccasestr(char const *s1, char const *s2)
1012 char c2, c1;
1013 NYD2_ENTER;
1015 for (c2 = *s2++, c2 = lowerconv(c2);;) {
1016 if ((c1 = *s1++) == '\0') {
1017 s1 = NULL;
1018 break;
1020 if (lowerconv(c1) == c2 && is_asccaseprefix(s1, s2)) {
1021 --s1;
1022 break;
1025 NYD2_LEAVE;
1026 return s1;
1029 FL bool_t
1030 is_asccaseprefix(char const *as1, char const *as2)
1032 bool_t rv = FAL0;
1033 NYD2_ENTER;
1035 for (;; ++as1, ++as2) {
1036 char c1 = lowerconv(*as1), c2 = lowerconv(*as2);
1038 if ((rv = (c2 == '\0')))
1039 break;
1040 if (c1 != c2)
1041 break;
1043 NYD2_LEAVE;
1044 return rv;
1047 FL struct str *
1048 (n_str_dup)(struct str *self, struct str const *t SMALLOC_DEBUG_ARGS)
1050 NYD_ENTER;
1051 if (t != NULL && t->l > 0) {
1052 self->l = t->l;
1053 self->s = (srealloc)(self->s, t->l +1 SMALLOC_DEBUG_ARGSCALL);
1054 memcpy(self->s, t->s, t->l);
1055 self->s[t->l] = '\0';
1056 } else
1057 self->l = 0;
1058 NYD_LEAVE;
1059 return self;
1062 FL struct str *
1063 (n_str_add_buf)(struct str *self, char const *buf, size_t buflen
1064 SMALLOC_DEBUG_ARGS)
1066 NYD_ENTER;
1067 if (buflen != 0) {
1068 size_t sl = self->l;
1069 self->l = sl + buflen;
1070 self->s = (srealloc)(self->s, self->l +1 SMALLOC_DEBUG_ARGSCALL);
1071 memcpy(self->s + sl, buf, buflen);
1072 self->s[self->l] = '\0';
1074 NYD_LEAVE;
1075 return self;
1079 * struct n_string TODO extend, optimize
1082 FL struct n_string *
1083 (n_string_clear)(struct n_string *self SMALLOC_DEBUG_ARGS){
1084 NYD_ENTER;
1086 assert(self != NULL);
1088 if(self->s_size != 0){
1089 if(!self->s_auto){
1090 #ifdef HAVE_DEBUG
1091 sfree(self->s_dat SMALLOC_DEBUG_ARGSCALL);
1092 #else
1093 free(self->s_dat);
1094 #endif
1096 self->s_len = self->s_auto = self->s_size = 0;
1097 self->s_dat = NULL;
1099 NYD_LEAVE;
1100 return self;
1103 FL struct n_string *
1104 (n_string_reserve)(struct n_string *self, size_t noof SMALLOC_DEBUG_ARGS){
1105 ui32_t i, l, s;
1106 NYD_ENTER;
1108 assert(self != NULL);
1110 s = self->s_size;
1111 l = self->s_len;
1112 #if 0 /* FIXME memory alloc too large */
1113 if(SI32_MAX - n_ALIGN(1) - l <= noof)
1114 n_panic(_("Memory allocation too large"));
1115 #endif
1117 if((i = s - l) <= noof){
1118 i += 1 + l + (ui32_t)noof;
1119 i = n_ALIGN(i);
1120 self->s_size = i -1;
1122 if(!self->s_auto)
1123 self->s_dat = (srealloc)(self->s_dat, i SMALLOC_DEBUG_ARGSCALL);
1124 else{
1125 char *ndat = (salloc)(i SALLOC_DEBUG_ARGSCALL);
1127 if(l > 0)
1128 memcpy(ndat, self->s_dat, l);
1129 self->s_dat = ndat;
1132 NYD_LEAVE;
1133 return self;
1136 FL struct n_string *
1137 (n_string_push_buf)(struct n_string *self, char const *buf, size_t buflen
1138 SMALLOC_DEBUG_ARGS){
1139 NYD_ENTER;
1141 assert(self != NULL);
1142 assert(buflen == 0 || buf != NULL);
1144 if(buflen == UIZ_MAX)
1145 buflen = (buf == NULL) ? 0 : strlen(buf);
1147 if(buflen > 0){
1148 ui32_t i;
1150 self = (n_string_reserve)(self, buflen SMALLOC_DEBUG_ARGSCALL);
1151 memcpy(self->s_dat + (i = self->s_len), buf, buflen);
1152 self->s_len = (i += (ui32_t)buflen);
1154 NYD_LEAVE;
1155 return self;
1158 FL struct n_string *
1159 (n_string_push_c)(struct n_string *self, char c SMALLOC_DEBUG_ARGS){
1160 NYD_ENTER;
1162 assert(self != NULL);
1164 if(self->s_len + 1 >= self->s_size)
1165 self = (n_string_reserve)(self, 1 SMALLOC_DEBUG_ARGSCALL);
1166 self->s_dat[self->s_len++] = c;
1167 NYD_LEAVE;
1168 return self;
1171 FL struct n_string *
1172 (n_string_unshift_buf)(struct n_string *self, char const *buf, size_t buflen
1173 SMALLOC_DEBUG_ARGS){
1174 NYD_ENTER;
1176 assert(self != NULL);
1177 assert(buflen == 0 || buf != NULL);
1179 if(buflen == UIZ_MAX)
1180 buflen = (buf == NULL) ? 0 : strlen(buf);
1182 if(buflen > 0){
1183 self = (n_string_reserve)(self, buflen SMALLOC_DEBUG_ARGSCALL);
1184 if(self->s_len > 0)
1185 memmove(self->s_dat + buflen, self->s_dat, self->s_len);
1186 memcpy(self->s_dat, buf, buflen);
1187 self->s_len += (ui32_t)buflen;
1189 NYD_LEAVE;
1190 return self;
1193 FL struct n_string *
1194 (n_string_unshift_c)(struct n_string *self, char c SMALLOC_DEBUG_ARGS){
1195 NYD_ENTER;
1197 assert(self != NULL);
1199 if(self->s_len + 1 >= self->s_size)
1200 self = (n_string_reserve)(self, 1 SMALLOC_DEBUG_ARGSCALL);
1201 if(self->s_len > 0)
1202 memmove(self->s_dat + 1, self->s_dat, self->s_len);
1203 self->s_dat[0] = c;
1204 ++self->s_len;
1205 NYD_LEAVE;
1206 return self;
1209 FL char *
1210 (n_string_cp)(struct n_string *self SMALLOC_DEBUG_ARGS){
1211 char *rv;
1212 NYD2_ENTER;
1214 assert(self != NULL);
1216 if(self->s_size == 0)
1217 self = (n_string_reserve)(self, 1 SMALLOC_DEBUG_ARGSCALL);
1219 (rv = self->s_dat)[self->s_len] = '\0';
1220 NYD2_LEAVE;
1221 return rv;
1224 FL char const *
1225 n_string_cp_const(struct n_string const *self){
1226 char const *rv;
1227 NYD2_ENTER;
1229 assert(self != NULL);
1231 if(self->s_size != 0){
1232 ((struct n_string*)UNCONST(self))->s_dat[self->s_len] = '\0';
1233 rv = self->s_dat;
1234 }else
1235 rv = "";
1236 NYD2_LEAVE;
1237 return rv;
1241 * UTF-8
1244 #ifdef HAVE_NATCH_CHAR
1245 FL ui32_t
1246 n_utf8_to_utf32(char const **bdat, size_t *blen) /* TODO check false UTF8 */
1248 char const *cp;
1249 size_t l;
1250 ui32_t c, x;
1251 NYD2_ENTER;
1253 cp = *bdat;
1254 l = *blen - 1;
1255 x = (ui8_t)*cp++;
1257 if (x <= 0x7F)
1258 c = x;
1259 else {
1260 if ((x & 0xE0) == 0xC0) {
1261 if (l < 1)
1262 goto jerr;
1263 l -= 1;
1264 c = x & ~0xC0;
1265 } else if ((x & 0xF0) == 0xE0) {
1266 if (l < 2)
1267 goto jerr;
1268 l -= 2;
1269 c = x & ~0xE0;
1270 c <<= 6;
1271 x = (ui8_t)*cp++;
1272 c |= x & 0x7F;
1273 } else {
1274 if (l < 3)
1275 goto jerr;
1276 l -= 3;
1277 c = x & ~0xF0;
1278 c <<= 6;
1279 x = (ui8_t)*cp++;
1280 c |= x & 0x7F;
1281 c <<= 6;
1282 x = (ui8_t)*cp++;
1283 c |= x & 0x7F;
1285 c <<= 6;
1286 x = (ui8_t)*cp++;
1287 c |= x & 0x7F;
1290 jleave:
1291 *bdat = cp;
1292 *blen = l;
1293 NYD2_LEAVE;
1294 return c;
1295 jerr:
1296 c = UI32_MAX;
1297 goto jleave;
1299 #endif /* HAVE_NATCH_CHAR */
1301 #ifdef HAVE_FILTER_HTML_TAGSOUP
1302 FL size_t
1303 n_utf32_to_utf8(ui32_t c, char *buf)
1305 struct {
1306 ui32_t lower_bound;
1307 ui32_t upper_bound;
1308 ui8_t enc_leader;
1309 ui8_t enc_lval;
1310 ui8_t dec_leader_mask;
1311 ui8_t dec_leader_val_mask;
1312 ui8_t dec_bytes_togo;
1313 ui8_t cat_index;
1314 ui8_t __dummy[2];
1315 } const _cat[] = {
1316 {0x00000000, 0x00000000, 0x00, 0, 0x00, 0x00, 0, 0, {0,}},
1317 {0x00000000, 0x0000007F, 0x00, 1, 0x80, 0x7F, 1-1, 1, {0,}},
1318 {0x00000080, 0x000007FF, 0xC0, 2, 0xE0, 0xFF-0xE0, 2-1, 2, {0,}},
1319 /* We assume surrogates are U+D800 - U+DFFF, _cat index 3 */
1320 /* xxx _from_utf32() simply assumes magic code points for surrogates!
1321 * xxx (However, should we ever get yet another surrogate range we
1322 * xxx need to deal with that all over the place anyway? */
1323 {0x00000800, 0x0000FFFF, 0xE0, 3, 0xF0, 0xFF-0xF0, 3-1, 3, {0,}},
1324 {0x00010000, 0x0010FFFF, 0xF0, 4, 0xF8, 0xFF-0xF8, 4-1, 4, {0,}},
1325 }, *catp = _cat;
1326 size_t l;
1328 if (c <= _cat[0].upper_bound) { catp += 0; goto j0; }
1329 if (c <= _cat[1].upper_bound) { catp += 1; goto j1; }
1330 if (c <= _cat[2].upper_bound) { catp += 2; goto j2; }
1331 if (c <= _cat[3].upper_bound) {
1332 /* Surrogates may not be converted (Compatibility rule C10) */
1333 if (c >= 0xD800u && c <= 0xDFFFu)
1334 goto jerr;
1335 catp += 3;
1336 goto j3;
1338 if (c <= _cat[4].upper_bound) { catp += 4; goto j4; }
1339 jerr:
1340 c = 0xFFFDu; /* Unicode replacement character */
1341 catp += 3;
1342 goto j3;
1344 buf[3] = (char)0x80 | (char)(c & 0x3F); c >>= 6;
1346 buf[2] = (char)0x80 | (char)(c & 0x3F); c >>= 6;
1348 buf[1] = (char)0x80 | (char)(c & 0x3F); c >>= 6;
1350 buf[0] = (char)catp->enc_leader | (char)(c);
1352 buf[catp->enc_lval] = '\0';
1353 l = catp->enc_lval;
1354 NYD2_LEAVE;
1355 return l;
1357 #endif /* HAVE_FILTER_HTML_TAGSOUP */
1360 * Our iconv(3) wrapper
1362 #ifdef HAVE_ICONV
1364 static void _ic_toupper(char *dest, char const *src);
1365 static void _ic_stripdash(char *p);
1367 static void
1368 _ic_toupper(char *dest, char const *src)
1370 NYD2_ENTER;
1372 *dest++ = upperconv(*src);
1373 while (*src++ != '\0');
1374 NYD2_LEAVE;
1377 static void
1378 _ic_stripdash(char *p)
1380 char *q = p;
1381 NYD2_ENTER;
1384 if (*(q = p) != '-')
1385 ++q;
1386 while (*p++ != '\0');
1387 NYD2_LEAVE;
1390 FL iconv_t
1391 n_iconv_open(char const *tocode, char const *fromcode)
1393 iconv_t id;
1394 char *t, *f;
1395 NYD_ENTER;
1397 if ((!asccasecmp(fromcode, "unknown-8bit") ||
1398 !asccasecmp(fromcode, "binary")) &&
1399 (fromcode = ok_vlook(charset_unknown_8bit)) == NULL)
1400 fromcode = charset_get_8bit();
1402 if ((id = iconv_open(tocode, fromcode)) != (iconv_t)-1)
1403 goto jleave;
1405 /* Remove the "iso-" prefixes for Solaris */
1406 if (!ascncasecmp(tocode, "iso-", 4))
1407 tocode += 4;
1408 else if (!ascncasecmp(tocode, "iso", 3))
1409 tocode += 3;
1410 if (!ascncasecmp(fromcode, "iso-", 4))
1411 fromcode += 4;
1412 else if (!ascncasecmp(fromcode, "iso", 3))
1413 fromcode += 3;
1414 if (*tocode == '\0' || *fromcode == '\0') {
1415 id = (iconv_t)-1;
1416 goto jleave;
1418 if ((id = iconv_open(tocode, fromcode)) != (iconv_t)-1)
1419 goto jleave;
1421 /* Solaris prefers upper-case charset names. Don't ask... */
1422 t = salloc(strlen(tocode) +1);
1423 _ic_toupper(t, tocode);
1424 f = salloc(strlen(fromcode) +1);
1425 _ic_toupper(f, fromcode);
1426 if ((id = iconv_open(t, f)) != (iconv_t)-1)
1427 goto jleave;
1429 /* Strip dashes for UnixWare */
1430 _ic_stripdash(t);
1431 _ic_stripdash(f);
1432 if ((id = iconv_open(t, f)) != (iconv_t)-1)
1433 goto jleave;
1435 /* Add your vendor's sillynesses here */
1437 /* If the encoding names are equal at this point, they are just not
1438 * understood by iconv(), and we cannot sensibly use it in any way. We do
1439 * not perform this as an optimization above since iconv() can otherwise be
1440 * used to check the validity of the input even with identical encoding
1441 * names */
1442 if (!strcmp(t, f))
1443 errno = 0;
1444 jleave:
1445 NYD_LEAVE;
1446 return id;
1449 FL void
1450 n_iconv_close(iconv_t cd)
1452 NYD_ENTER;
1453 iconv_close(cd);
1454 if (cd == iconvd)
1455 iconvd = (iconv_t)-1;
1456 NYD_LEAVE;
1459 FL void
1460 n_iconv_reset(iconv_t cd)
1462 NYD_ENTER;
1463 iconv(cd, NULL, NULL, NULL, NULL);
1464 NYD_LEAVE;
1467 /* (2012-09-24: export and use it exclusively to isolate prototype problems
1468 * (*inb* is 'char const **' except in POSIX) in a single place.
1469 * GNU libiconv even allows for configuration time const/non-const..
1470 * In the end it's an ugly guess, but we can't do better since make(1) doesn't
1471 * support compiler invocations which bail on error, so no -Werror */
1472 /* Citrus project? */
1473 # if defined _ICONV_H_ && defined __ICONV_F_HIDE_INVALID
1474 /* DragonFly 3.2.1 is special TODO newer DragonFly too, but different */
1475 # if OS_DRAGONFLY
1476 # define __INBCAST(S) (char ** __restrict__)UNCONST(S)
1477 # else
1478 # define __INBCAST(S) (char const **)UNCONST(S)
1479 # endif
1480 # elif OS_SUNOS || OS_SOLARIS
1481 # define __INBCAST(S) (char const ** __restrict__)UNCONST(S)
1482 # endif
1483 # ifndef __INBCAST
1484 # define __INBCAST(S) (char **)UNCONST(S)
1485 # endif
1487 FL int
1488 n_iconv_buf(iconv_t cd, char const **inb, size_t *inbleft,/*XXX redo iconv use*/
1489 char **outb, size_t *outbleft, bool_t skipilseq)
1491 int err = 0;
1492 NYD2_ENTER;
1494 for (;;) {
1495 size_t sz = iconv(cd, __INBCAST(inb), inbleft, outb, outbleft);
1496 if (sz != (size_t)-1)
1497 break;
1498 err = errno;
1499 if (!skipilseq || err != EILSEQ)
1500 break;
1501 if (*inbleft > 0) {
1502 ++(*inb);
1503 --(*inbleft);
1504 } else if (*outbleft > 0) {
1505 **outb = '\0';
1506 break;
1508 if (*outbleft > 0/* TODO 0xFFFD 2*/) {
1509 /* TODO 0xFFFD (*outb)[0] = '[';
1510 * TODO (*outb)[1] = '?';
1511 * TODO 0xFFFD (*outb)[2] = ']';
1512 * TODO (*outb) += 3;
1513 * TODO (*outbleft) -= 3; */
1514 *(*outb)++ = '?';
1515 --*outbleft;
1516 } else {
1517 err = E2BIG;
1518 break;
1520 err = 0;
1522 NYD2_LEAVE;
1523 return err;
1525 # undef __INBCAST
1527 FL int
1528 n_iconv_str(iconv_t cd, struct str *out, struct str const *in,
1529 struct str *in_rest_or_null, bool_t skipilseq)
1531 int err;
1532 char *obb, *ob;
1533 char const *ib;
1534 size_t olb, ol, il;
1535 NYD2_ENTER;
1537 err = 0;
1538 obb = out->s;
1539 olb = out->l;
1540 ol = in->l;
1542 ol = (ol << 1) - (ol >> 4);
1543 if (olb <= ol) {
1544 olb = ol;
1545 goto jrealloc;
1548 for (;;) {
1549 ib = in->s;
1550 il = in->l;
1551 ob = obb;
1552 ol = olb;
1553 err = n_iconv_buf(cd, &ib, &il, &ob, &ol, skipilseq);
1554 if (err == 0 || err != E2BIG)
1555 break;
1556 err = 0;
1557 olb += in->l;
1558 jrealloc:
1559 obb = srealloc(obb, olb +1);
1562 if (in_rest_or_null != NULL) {
1563 in_rest_or_null->s = UNCONST(ib);
1564 in_rest_or_null->l = il;
1566 out->s = obb;
1567 out->s[out->l = olb - ol] = '\0';
1568 NYD2_LEAVE;
1569 return err;
1571 #endif /* HAVE_ICONV */
1573 /* s-it-mode */