1 /*
2  * This source file is part of the bstring string library.  This code was
3  * written by Paul Hsieh in 2002-2008, and is covered by the BSD open source
4  * license and the GPL. Refer to the accompanying documentation for details
5  * on usage and license.
6  */
7 
8 /*
9  * bstrlib.c
10  *
11  * This file is the core module for implementing the bstring functions.
12  */
13 
14 #include <stdio.h>
15 #include <stddef.h>
16 #include <stdarg.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <ctype.h>
20 
21 #include <atalk/bstrlib.h>
22 
23 /* Optionally include a mechanism for debugging memory */
24 
25 #if defined(MEMORY_DEBUG) || defined(BSTRLIB_MEMORY_DEBUG)
26 #include "memdbg.h"
27 #endif
28 
29 #ifndef bstr__alloc
30 #define bstr__alloc(x) malloc (x)
31 #endif
32 
33 #ifndef bstr__free
34 #define bstr__free(p) free (p)
35 #endif
36 
37 #ifndef bstr__realloc
38 #define bstr__realloc(p,x) realloc ((p), (x))
39 #endif
40 
41 #ifndef bstr__memcpy
42 #define bstr__memcpy(d,s,l) memcpy ((d), (s), (l))
43 #endif
44 
45 #ifndef bstr__memmove
46 #define bstr__memmove(d,s,l) memmove ((d), (s), (l))
47 #endif
48 
49 #ifndef bstr__memset
50 #define bstr__memset(d,c,l) memset ((d), (c), (l))
51 #endif
52 
53 #ifndef bstr__memcmp
54 #define bstr__memcmp(d,c,l) memcmp ((d), (c), (l))
55 #endif
56 
57 #ifndef bstr__memchr
58 #define bstr__memchr(s,c,l) memchr ((s), (c), (l))
59 #endif
60 
61 /* Just a length safe wrapper for memmove. */
62 
63 #define bBlockCopy(D,S,L) { if ((L) > 0) bstr__memmove ((D),(S),(L)); }
64 
65 /* Compute the snapped size for a given requested size.  By snapping to powers
66    of 2 like this, repeated reallocations are avoided. */
snapUpSize(int i)67 static int snapUpSize (int i) {
68 	if (i < 8) {
69 		i = 8;
70 	} else {
71 		unsigned int j;
72 		j = (unsigned int) i;
73 
74 		j |= (j >>  1);
75 		j |= (j >>  2);
76 		j |= (j >>  4);
77 		j |= (j >>  8);		/* Ok, since int >= 16 bits */
78 #if (UINT_MAX != 0xffff)
79 		j |= (j >> 16);		/* For 32 bit int systems */
80 #if (UINT_MAX > 0xffffffffUL)
81 		j |= (j >> 32);		/* For 64 bit int systems */
82 #endif
83 #endif
84 		/* Least power of two greater than i */
85 		j++;
86 		if ((int) j >= i) i = (int) j;
87 	}
88 	return i;
89 }
90 
91 /*  int balloc (bstring b, int len)
92  *
93  *  Increase the size of the memory backing the bstring b to at least len.
94  */
balloc(bstring b,int olen)95 int balloc (bstring b, int olen) {
96 	int len;
97 	if (b == NULL || b->data == NULL || b->slen < 0 || b->mlen <= 0 ||
98 	    b->mlen < b->slen || olen <= 0) {
99 		return BSTR_ERR;
100 	}
101 
102 	if (olen >= b->mlen) {
103 		unsigned char * x;
104 
105 		if ((len = snapUpSize (olen)) <= b->mlen) return BSTR_OK;
106 
107 		/* Assume probability of a non-moving realloc is 0.125 */
108 		if (7 * b->mlen < 8 * b->slen) {
109 
110 			/* If slen is close to mlen in size then use realloc to reduce
111 			   the memory defragmentation */
112 
113 			reallocStrategy:;
114 
115 			x = (unsigned char *) bstr__realloc (b->data, (size_t) len);
116 			if (x == NULL) {
117 
118 				/* Since we failed, try allocating the tighest possible
119 				   allocation */
120 
121 				if (NULL == (x = (unsigned char *) bstr__realloc (b->data, (size_t) (len = olen)))) {
122 					return BSTR_ERR;
123 				}
124 			}
125 		} else {
126 
127 			/* If slen is not close to mlen then avoid the penalty of copying
128 			   the extra bytes that are allocated, but not considered part of
129 			   the string */
130 
131 			if (NULL == (x = (unsigned char *) bstr__alloc ((size_t) len))) {
132 
133 				/* Perhaps there is no available memory for the two
134 				   allocations to be in memory at once */
135 
136 				goto reallocStrategy;
137 
138 			} else {
139 				if (b->slen) bstr__memcpy ((char *) x, (char *) b->data, (size_t) b->slen);
140 				bstr__free (b->data);
141 			}
142 		}
143 		b->data = x;
144 		b->mlen = len;
145 		b->data[b->slen] = (unsigned char) '\0';
146 	}
147 
148 	return BSTR_OK;
149 }
150 
151 /*  int ballocmin (bstring b, int len)
152  *
153  *  Set the size of the memory backing the bstring b to len or b->slen+1,
154  *  whichever is larger.  Note that repeated use of this function can degrade
155  *  performance.
156  */
ballocmin(bstring b,int len)157 int ballocmin (bstring b, int len) {
158 	unsigned char * s;
159 
160 	if (b == NULL || b->data == NULL || (b->slen+1) < 0 || b->mlen <= 0 ||
161 	    b->mlen < b->slen || len <= 0) {
162 		return BSTR_ERR;
163 	}
164 
165 	if (len < b->slen + 1) len = b->slen + 1;
166 
167 	if (len != b->mlen) {
168 		s = (unsigned char *) bstr__realloc (b->data, (size_t) len);
169 		if (NULL == s) return BSTR_ERR;
170 		s[b->slen] = (unsigned char) '\0';
171 		b->data = s;
172 		b->mlen = len;
173 	}
174 
175 	return BSTR_OK;
176 }
177 
178 /*  bstring bfromcstr (const char * str)
179  *
180  *  Create a bstring which contains the contents of the '\0' terminated char *
181  *  buffer str.
182  */
bfromcstr(const char * str)183 bstring bfromcstr (const char * str) {
184 bstring b;
185 int i;
186 size_t j;
187 
188 	if (str == NULL) return NULL;
189 	j = (strlen) (str);
190 	i = snapUpSize ((int) (j + (2 - (j != 0))));
191 	if (i <= (int) j) return NULL;
192 
193 	b = (bstring) bstr__alloc (sizeof (struct tagbstring));
194 	if (NULL == b) return NULL;
195 	b->slen = (int) j;
196 	if (NULL == (b->data = (unsigned char *) bstr__alloc (b->mlen = i))) {
197 		bstr__free (b);
198 		return NULL;
199 	}
200 
201 	bstr__memcpy (b->data, str, j+1);
202 	return b;
203 }
204 
205 /*  bstring bfromcstralloc (int mlen, const char * str)
206  *
207  *  Create a bstring which contains the contents of the '\0' terminated char *
208  *  buffer str.  The memory buffer backing the string is at least len
209  *  characters in length.
210  */
bfromcstralloc(int mlen,const char * str)211 bstring bfromcstralloc (int mlen, const char * str) {
212 bstring b;
213 int i;
214 size_t j;
215 
216 	if (str == NULL) return NULL;
217 	j = (strlen) (str);
218 	i = snapUpSize ((int) (j + (2 - (j != 0))));
219 	if (i <= (int) j) return NULL;
220 
221 	b = (bstring) bstr__alloc (sizeof (struct tagbstring));
222 	if (b == NULL) return NULL;
223 	b->slen = (int) j;
224 	if (i < mlen) i = mlen;
225 
226 	if (NULL == (b->data = (unsigned char *) bstr__alloc (b->mlen = i))) {
227 		bstr__free (b);
228 		return NULL;
229 	}
230 
231 	bstr__memcpy (b->data, str, j+1);
232 	return b;
233 }
234 
235 /*  bstring blk2bstr (const void * blk, int len)
236  *
237  *  Create a bstring which contains the content of the block blk of length
238  *  len.
239  */
blk2bstr(const void * blk,int len)240 bstring blk2bstr (const void * blk, int len) {
241 bstring b;
242 int i;
243 
244 	if (blk == NULL || len < 0) return NULL;
245 	b = (bstring) bstr__alloc (sizeof (struct tagbstring));
246 	if (b == NULL) return NULL;
247 	b->slen = len;
248 
249 	i = len + (2 - (len != 0));
250 	i = snapUpSize (i);
251 
252 	b->mlen = i;
253 
254 	b->data = (unsigned char *) bstr__alloc ((size_t) b->mlen);
255 	if (b->data == NULL) {
256 		bstr__free (b);
257 		return NULL;
258 	}
259 
260 	if (len > 0) bstr__memcpy (b->data, blk, (size_t) len);
261 	b->data[len] = (unsigned char) '\0';
262 
263 	return b;
264 }
265 
266 /*  char * bstr2cstr (const_bstring s, char z)
267  *
268  *  Create a '\0' terminated char * buffer which is equal to the contents of
269  *  the bstring s, except that any contained '\0' characters are converted
270  *  to the character in z. This returned value should be freed with a
271  *  bcstrfree () call, by the calling application.
272  */
bstr2cstr(const_bstring b,char z)273 char * bstr2cstr (const_bstring b, char z) {
274 int i, l;
275 char * r;
276 
277 	if (b == NULL || b->slen < 0 || b->data == NULL) return NULL;
278 	l = b->slen;
279 	r = (char *) bstr__alloc ((size_t) (l + 1));
280 	if (r == NULL) return r;
281 
282 	for (i=0; i < l; i ++) {
283 		r[i] = (char) ((b->data[i] == '\0') ? z : (char) (b->data[i]));
284 	}
285 
286 	r[l] = (unsigned char) '\0';
287 
288 	return r;
289 }
290 
291 /*  int bcstrfree (char * s)
292  *
293  *  Frees a C-string generated by bstr2cstr ().  This is normally unnecessary
294  *  since it just wraps a call to bstr__free (), however, if bstr__alloc ()
295  *  and bstr__free () have been redefined as a macros within the bstrlib
296  *  module (via defining them in memdbg.h after defining
297  *  BSTRLIB_MEMORY_DEBUG) with some difference in behaviour from the std
298  *  library functions, then this allows a correct way of freeing the memory
299  *  that allows higher level code to be independent from these macro
300  *  redefinitions.
301  */
bcstrfree(char * s)302 int bcstrfree (char * s) {
303 	if (s) {
304 		bstr__free (s);
305 		return BSTR_OK;
306 	}
307 	return BSTR_ERR;
308 }
309 
310 /*  int bconcat (bstring b0, const_bstring b1)
311  *
312  *  Concatenate the bstring b1 to the bstring b0.
313  */
bconcat(bstring b0,const_bstring b1)314 int bconcat (bstring b0, const_bstring b1) {
315 int len, d;
316 bstring aux = (bstring) b1;
317 
318 	if (b0 == NULL || b1 == NULL || b0->data == NULL || b1->data == NULL) return BSTR_ERR;
319 
320 	d = b0->slen;
321 	len = b1->slen;
322 	if ((d | (b0->mlen - d) | len | (d + len)) < 0) return BSTR_ERR;
323 
324 	if (b0->mlen <= d + len + 1) {
325 		ptrdiff_t pd = b1->data - b0->data;
326 		if (0 <= pd && pd < b0->mlen) {
327 			if (NULL == (aux = bstrcpy (b1))) return BSTR_ERR;
328 		}
329 		if (balloc (b0, d + len + 1) != BSTR_OK) {
330 			if (aux != b1) bdestroy (aux);
331 			return BSTR_ERR;
332 		}
333 	}
334 
335 	bBlockCopy (&b0->data[d], &aux->data[0], (size_t) len);
336 	b0->data[d + len] = (unsigned char) '\0';
337 	b0->slen = d + len;
338 	if (aux != b1) bdestroy (aux);
339 	return BSTR_OK;
340 }
341 
342 /*  int bconchar (bstring b, char c)
343 / *
344  *  Concatenate the single character c to the bstring b.
345  */
bconchar(bstring b,char c)346 int bconchar (bstring b, char c) {
347 int d;
348 
349 	if (b == NULL) return BSTR_ERR;
350 	d = b->slen;
351 	if ((d | (b->mlen - d)) < 0 || balloc (b, d + 2) != BSTR_OK) return BSTR_ERR;
352 	b->data[d] = (unsigned char) c;
353 	b->data[d + 1] = (unsigned char) '\0';
354 	b->slen++;
355 	return BSTR_OK;
356 }
357 
358 /*  int bcatcstr (bstring b, const char * s)
359  *
360  *  Concatenate a char * string to a bstring.
361  */
bcatcstr(bstring b,const char * s)362 int bcatcstr (bstring b, const char * s) {
363 char * d;
364 int i, l;
365 
366 	if (b == NULL || b->data == NULL || b->slen < 0 || b->mlen < b->slen
367 	 || b->mlen <= 0 || s == NULL) return BSTR_ERR;
368 
369 	/* Optimistically concatenate directly */
370 	l = b->mlen - b->slen;
371 	d = (char *) &b->data[b->slen];
372 	for (i=0; i < l; i++) {
373 		if ((*d++ = *s++) == '\0') {
374 			b->slen += i;
375 			return BSTR_OK;
376 		}
377 	}
378 	b->slen += i;
379 
380 	/* Need to explicitely resize and concatenate tail */
381 	return bcatblk (b, (const void *) s, (int) strlen (s));
382 }
383 
384 /*  int bcatblk (bstring b, const void * s, int len)
385  *
386  *  Concatenate a fixed length buffer to a bstring.
387  */
bcatblk(bstring b,const void * s,int len)388 int bcatblk (bstring b, const void * s, int len) {
389 int nl;
390 
391 	if (b == NULL || b->data == NULL || b->slen < 0 || b->mlen < b->slen
392 	 || b->mlen <= 0 || s == NULL || len < 0) return BSTR_ERR;
393 
394 	if (0 > (nl = b->slen + len)) return BSTR_ERR; /* Overflow? */
395 	if (b->mlen <= nl && 0 > balloc (b, nl + 1)) return BSTR_ERR;
396 
397 	bBlockCopy (&b->data[b->slen], s, (size_t) len);
398 	b->slen = nl;
399 	b->data[nl] = (unsigned char) '\0';
400 	return BSTR_OK;
401 }
402 
403 /*  bstring bstrcpy (const_bstring b)
404  *
405  *  Create a copy of the bstring b.
406  */
bstrcpy(const_bstring b)407 bstring bstrcpy (const_bstring b) {
408 bstring b0;
409 int i,j;
410 
411 	/* Attempted to copy an invalid string? */
412 	if (b == NULL || b->slen < 0 || b->data == NULL) return NULL;
413 
414 	b0 = (bstring) bstr__alloc (sizeof (struct tagbstring));
415 	if (b0 == NULL) {
416 		/* Unable to allocate memory for string header */
417 		return NULL;
418 	}
419 
420 	i = b->slen;
421 	j = snapUpSize (i + 1);
422 
423 	b0->data = (unsigned char *) bstr__alloc (j);
424 	if (b0->data == NULL) {
425 		j = i + 1;
426 		b0->data = (unsigned char *) bstr__alloc (j);
427 		if (b0->data == NULL) {
428 			/* Unable to allocate memory for string data */
429 			bstr__free (b0);
430 			return NULL;
431 		}
432 	}
433 
434 	b0->mlen = j;
435 	b0->slen = i;
436 
437 	if (i) bstr__memcpy ((char *) b0->data, (char *) b->data, i);
438 	b0->data[b0->slen] = (unsigned char) '\0';
439 
440 	return b0;
441 }
442 
443 /*  int bassign (bstring a, const_bstring b)
444  *
445  *  Overwrite the string a with the contents of string b.
446  */
bassign(bstring a,const_bstring b)447 int bassign (bstring a, const_bstring b) {
448 	if (b == NULL || b->data == NULL || b->slen < 0)
449 		return BSTR_ERR;
450 	if (b->slen != 0) {
451 		if (balloc (a, b->slen) != BSTR_OK) return BSTR_ERR;
452 		bstr__memmove (a->data, b->data, b->slen);
453 	} else {
454 		if (a == NULL || a->data == NULL || a->mlen < a->slen ||
455 		    a->slen < 0 || a->mlen == 0)
456 			return BSTR_ERR;
457 	}
458 	a->data[b->slen] = (unsigned char) '\0';
459 	a->slen = b->slen;
460 	return BSTR_OK;
461 }
462 
463 /*  int bassignmidstr (bstring a, const_bstring b, int left, int len)
464  *
465  *  Overwrite the string a with the middle of contents of string b
466  *  starting from position left and running for a length len.  left and
467  *  len are clamped to the ends of b as with the function bmidstr.
468  */
bassignmidstr(bstring a,const_bstring b,int left,int len)469 int bassignmidstr (bstring a, const_bstring b, int left, int len) {
470 	if (b == NULL || b->data == NULL || b->slen < 0)
471 		return BSTR_ERR;
472 
473 	if (left < 0) {
474 		len += left;
475 		left = 0;
476 	}
477 
478 	if (len > b->slen - left) len = b->slen - left;
479 
480 	if (a == NULL || a->data == NULL || a->mlen < a->slen ||
481 	    a->slen < 0 || a->mlen == 0)
482 		return BSTR_ERR;
483 
484 	if (len > 0) {
485 		if (balloc (a, len) != BSTR_OK) return BSTR_ERR;
486 		bstr__memmove (a->data, b->data + left, len);
487 		a->slen = len;
488 	} else {
489 		a->slen = 0;
490 	}
491 	a->data[a->slen] = (unsigned char) '\0';
492 	return BSTR_OK;
493 }
494 
495 /*  int bassigncstr (bstring a, const char * str)
496  *
497  *  Overwrite the string a with the contents of char * string str.  Note that
498  *  the bstring a must be a well defined and writable bstring.  If an error
499  *  occurs BSTR_ERR is returned however a may be partially overwritten.
500  */
bassigncstr(bstring a,const char * str)501 int bassigncstr (bstring a, const char * str) {
502 int i;
503 size_t len;
504 	if (a == NULL || a->data == NULL || a->mlen < a->slen ||
505 	    a->slen < 0 || a->mlen == 0 || NULL == str)
506 		return BSTR_ERR;
507 
508 	for (i=0; i < a->mlen; i++) {
509 		if ('\0' == (a->data[i] = str[i])) {
510 			a->slen = i;
511 			return BSTR_OK;
512 		}
513 	}
514 
515 	a->slen = i;
516 	len = strlen (str + i);
517 	if (len > INT_MAX || i + len + 1 > INT_MAX ||
518 	    0 > balloc (a, (int) (i + len + 1))) return BSTR_ERR;
519 	bBlockCopy (a->data + i, str + i, (size_t) len + 1);
520 	a->slen += (int) len;
521 	return BSTR_OK;
522 }
523 
524 /*  int bassignblk (bstring a, const void * s, int len)
525  *
526  *  Overwrite the string a with the contents of the block (s, len).  Note that
527  *  the bstring a must be a well defined and writable bstring.  If an error
528  *  occurs BSTR_ERR is returned and a is not overwritten.
529  */
bassignblk(bstring a,const void * s,int len)530 int bassignblk (bstring a, const void * s, int len) {
531 	if (a == NULL || a->data == NULL || a->mlen < a->slen ||
532 	    a->slen < 0 || a->mlen == 0 || NULL == s || len + 1 < 1)
533 		return BSTR_ERR;
534 	if (len + 1 > a->mlen && 0 > balloc (a, len + 1)) return BSTR_ERR;
535 	bBlockCopy (a->data, s, (size_t) len);
536 	a->data[len] = (unsigned char) '\0';
537 	a->slen = len;
538 	return BSTR_OK;
539 }
540 
541 /*  int btrunc (bstring b, int n)
542  *
543  *  Truncate the bstring to at most n characters.
544  */
btrunc(bstring b,int n)545 int btrunc (bstring b, int n) {
546 	if (n < 0 || b == NULL || b->data == NULL || b->mlen < b->slen ||
547 	    b->slen < 0 || b->mlen <= 0) return BSTR_ERR;
548 	if (b->slen > n) {
549 		b->slen = n;
550 		b->data[n] = (unsigned char) '\0';
551 	}
552 	return BSTR_OK;
553 }
554 
555 #define   upcase(c) (toupper ((unsigned char) c))
556 #define downcase(c) (tolower ((unsigned char) c))
557 #define   wspace(c) (isspace ((unsigned char) c))
558 
559 /*  int btoupper (bstring b)
560  *
561  *  Convert contents of bstring to upper case.
562  */
btoupper(bstring b)563 int btoupper (bstring b) {
564 int i, len;
565 	if (b == NULL || b->data == NULL || b->mlen < b->slen ||
566 	    b->slen < 0 || b->mlen <= 0) return BSTR_ERR;
567 	for (i=0, len = b->slen; i < len; i++) {
568 		b->data[i] = (unsigned char) upcase (b->data[i]);
569 	}
570 	return BSTR_OK;
571 }
572 
573 /*  int btolower (bstring b)
574  *
575  *  Convert contents of bstring to lower case.
576  */
btolower(bstring b)577 int btolower (bstring b) {
578 int i, len;
579 	if (b == NULL || b->data == NULL || b->mlen < b->slen ||
580 	    b->slen < 0 || b->mlen <= 0) return BSTR_ERR;
581 	for (i=0, len = b->slen; i < len; i++) {
582 		b->data[i] = (unsigned char) downcase (b->data[i]);
583 	}
584 	return BSTR_OK;
585 }
586 
587 /*  int bstricmp (const_bstring b0, const_bstring b1)
588  *
589  *  Compare two strings without differentiating between case.  The return
590  *  value is the difference of the values of the characters where the two
591  *  strings first differ after lower case transformation, otherwise 0 is
592  *  returned indicating that the strings are equal.  If the lengths are
593  *  different, then a difference from 0 is given, but if the first extra
594  *  character is '\0', then it is taken to be the value UCHAR_MAX+1.
595  */
bstricmp(const_bstring b0,const_bstring b1)596 int bstricmp (const_bstring b0, const_bstring b1) {
597 int i, v, n;
598 
599 	if (bdata (b0) == NULL || b0->slen < 0 ||
600 	    bdata (b1) == NULL || b1->slen < 0) return SHRT_MIN;
601 	if ((n = b0->slen) > b1->slen) n = b1->slen;
602 	else if (b0->slen == b1->slen && b0->data == b1->data) return BSTR_OK;
603 
604 	for (i = 0; i < n; i ++) {
605 		v  = (char) downcase (b0->data[i])
606 		   - (char) downcase (b1->data[i]);
607 		if (0 != v) return v;
608 	}
609 
610 	if (b0->slen > n) {
611 		v = (char) downcase (b0->data[n]);
612 		if (v) return v;
613 		return UCHAR_MAX + 1;
614 	}
615 	if (b1->slen > n) {
616 		v = - (char) downcase (b1->data[n]);
617 		if (v) return v;
618 		return - (int) (UCHAR_MAX + 1);
619 	}
620 	return BSTR_OK;
621 }
622 
623 /*  int bstrnicmp (const_bstring b0, const_bstring b1, int n)
624  *
625  *  Compare two strings without differentiating between case for at most n
626  *  characters.  If the position where the two strings first differ is
627  *  before the nth position, the return value is the difference of the values
628  *  of the characters, otherwise 0 is returned.  If the lengths are different
629  *  and less than n characters, then a difference from 0 is given, but if the
630  *  first extra character is '\0', then it is taken to be the value
631  *  UCHAR_MAX+1.
632  */
bstrnicmp(const_bstring b0,const_bstring b1,int n)633 int bstrnicmp (const_bstring b0, const_bstring b1, int n) {
634 int i, v, m;
635 
636 	if (bdata (b0) == NULL || b0->slen < 0 ||
637 	    bdata (b1) == NULL || b1->slen < 0 || n < 0) return SHRT_MIN;
638 	m = n;
639 	if (m > b0->slen) m = b0->slen;
640 	if (m > b1->slen) m = b1->slen;
641 
642 	if (b0->data != b1->data) {
643 		for (i = 0; i < m; i ++) {
644 			v  = (char) downcase (b0->data[i]);
645 			v -= (char) downcase (b1->data[i]);
646 			if (v != 0) return b0->data[i] - b1->data[i];
647 		}
648 	}
649 
650 	if (n == m || b0->slen == b1->slen) return BSTR_OK;
651 
652 	if (b0->slen > m) {
653 		v = (char) downcase (b0->data[m]);
654 		if (v) return v;
655 		return UCHAR_MAX + 1;
656 	}
657 
658 	v = - (char) downcase (b1->data[m]);
659 	if (v) return v;
660 	return - (int) (UCHAR_MAX + 1);
661 }
662 
663 /*  int biseqcaseless (const_bstring b0, const_bstring b1)
664  *
665  *  Compare two strings for equality without differentiating between case.
666  *  If the strings differ other than in case, 0 is returned, if the strings
667  *  are the same, 1 is returned, if there is an error, -1 is returned.  If
668  *  the length of the strings are different, this function is O(1).  '\0'
669  *  termination characters are not treated in any special way.
670  */
biseqcaseless(const_bstring b0,const_bstring b1)671 int biseqcaseless (const_bstring b0, const_bstring b1) {
672 int i, n;
673 
674 	if (bdata (b0) == NULL || b0->slen < 0 ||
675 	    bdata (b1) == NULL || b1->slen < 0) return BSTR_ERR;
676 	if (b0->slen != b1->slen) return BSTR_OK;
677 	if (b0->data == b1->data || b0->slen == 0) return 1;
678 	for (i=0, n=b0->slen; i < n; i++) {
679 		if (b0->data[i] != b1->data[i]) {
680 			unsigned char c = (unsigned char) downcase (b0->data[i]);
681 			if (c != (unsigned char) downcase (b1->data[i])) return 0;
682 		}
683 	}
684 	return 1;
685 }
686 
687 /*  int bisstemeqcaselessblk (const_bstring b0, const void * blk, int len)
688  *
689  *  Compare beginning of string b0 with a block of memory of length len
690  *  without differentiating between case for equality.  If the beginning of b0
691  *  differs from the memory block other than in case (or if b0 is too short),
692  *  0 is returned, if the strings are the same, 1 is returned, if there is an
693  *  error, -1 is returned.  '\0' characters are not treated in any special
694  *  way.
695  */
bisstemeqcaselessblk(const_bstring b0,const void * blk,int len)696 int bisstemeqcaselessblk (const_bstring b0, const void * blk, int len) {
697 int i;
698 
699 	if (bdata (b0) == NULL || b0->slen < 0 || NULL == blk || len < 0)
700 		return BSTR_ERR;
701 	if (b0->slen < len) return BSTR_OK;
702 	if (b0->data == (const unsigned char *) blk || len == 0) return 1;
703 
704 	for (i = 0; i < len; i ++) {
705 		if (b0->data[i] != ((const unsigned char *) blk)[i]) {
706 			if (downcase (b0->data[i]) !=
707 			    downcase (((const unsigned char *) blk)[i])) return 0;
708 		}
709 	}
710 	return 1;
711 }
712 
713 /*
714  * int bltrimws (bstring b)
715  *
716  * Delete whitespace contiguous from the left end of the string.
717  */
bltrimws(bstring b)718 int bltrimws (bstring b) {
719 int i, len;
720 
721 	if (b == NULL || b->data == NULL || b->mlen < b->slen ||
722 	    b->slen < 0 || b->mlen <= 0) return BSTR_ERR;
723 
724 	for (len = b->slen, i = 0; i < len; i++) {
725 		if (!wspace (b->data[i])) {
726 			return bdelete (b, 0, i);
727 		}
728 	}
729 
730 	b->data[0] = (unsigned char) '\0';
731 	b->slen = 0;
732 	return BSTR_OK;
733 }
734 
735 /*
736  * int brtrimws (bstring b)
737  *
738  * Delete whitespace contiguous from the right end of the string.
739  */
brtrimws(bstring b)740 int brtrimws (bstring b) {
741 int i;
742 
743 	if (b == NULL || b->data == NULL || b->mlen < b->slen ||
744 	    b->slen < 0 || b->mlen <= 0) return BSTR_ERR;
745 
746 	for (i = b->slen - 1; i >= 0; i--) {
747 		if (!wspace (b->data[i])) {
748 			if (b->mlen > i) b->data[i+1] = (unsigned char) '\0';
749 			b->slen = i + 1;
750 			return BSTR_OK;
751 		}
752 	}
753 
754 	b->data[0] = (unsigned char) '\0';
755 	b->slen = 0;
756 	return BSTR_OK;
757 }
758 
759 /*
760  * int btrimws (bstring b)
761  *
762  * Delete whitespace contiguous from both ends of the string.
763  */
btrimws(bstring b)764 int btrimws (bstring b) {
765 int i, j;
766 
767 	if (b == NULL || b->data == NULL || b->mlen < b->slen ||
768 	    b->slen < 0 || b->mlen <= 0) return BSTR_ERR;
769 
770 	for (i = b->slen - 1; i >= 0; i--) {
771 		if (!wspace (b->data[i])) {
772 			if (b->mlen > i) b->data[i+1] = (unsigned char) '\0';
773 			b->slen = i + 1;
774 			for (j = 0; wspace (b->data[j]); j++) {}
775 			return bdelete (b, 0, j);
776 		}
777 	}
778 
779 	b->data[0] = (unsigned char) '\0';
780 	b->slen = 0;
781 	return BSTR_OK;
782 }
783 
784 /*  int biseq (const_bstring b0, const_bstring b1)
785  *
786  *  Compare the string b0 and b1.  If the strings differ, 0 is returned, if
787  *  the strings are the same, 1 is returned, if there is an error, -1 is
788  *  returned.  If the length of the strings are different, this function is
789  *  O(1).  '\0' termination characters are not treated in any special way.
790  */
biseq(const_bstring b0,const_bstring b1)791 int biseq (const_bstring b0, const_bstring b1) {
792 	if (b0 == NULL || b1 == NULL || b0->data == NULL || b1->data == NULL ||
793 		b0->slen < 0 || b1->slen < 0) return BSTR_ERR;
794 	if (b0->slen != b1->slen) return BSTR_OK;
795 	if (b0->data == b1->data || b0->slen == 0) return 1;
796 	return !bstr__memcmp (b0->data, b1->data, b0->slen);
797 }
798 
799 /*  int bisstemeqblk (const_bstring b0, const void * blk, int len)
800  *
801  *  Compare beginning of string b0 with a block of memory of length len for
802  *  equality.  If the beginning of b0 differs from the memory block (or if b0
803  *  is too short), 0 is returned, if the strings are the same, 1 is returned,
804  *  if there is an error, -1 is returned.  '\0' characters are not treated in
805  *  any special way.
806  */
bisstemeqblk(const_bstring b0,const void * blk,int len)807 int bisstemeqblk (const_bstring b0, const void * blk, int len) {
808 int i;
809 
810 	if (bdata (b0) == NULL || b0->slen < 0 || NULL == blk || len < 0)
811 		return BSTR_ERR;
812 	if (b0->slen < len) return BSTR_OK;
813 	if (b0->data == (const unsigned char *) blk || len == 0) return 1;
814 
815 	for (i = 0; i < len; i ++) {
816 		if (b0->data[i] != ((const unsigned char *) blk)[i]) return BSTR_OK;
817 	}
818 	return 1;
819 }
820 
821 /*  int biseqcstr (const_bstring b, const char *s)
822  *
823  *  Compare the bstring b and char * string s.  The C string s must be '\0'
824  *  terminated at exactly the length of the bstring b, and the contents
825  *  between the two must be identical with the bstring b with no '\0'
826  *  characters for the two contents to be considered equal.  This is
827  *  equivalent to the condition that their current contents will be always be
828  *  equal when comparing them in the same format after converting one or the
829  *  other.  If the strings are equal 1 is returned, if they are unequal 0 is
830  *  returned and if there is a detectable error BSTR_ERR is returned.
831  */
biseqcstr(const_bstring b,const char * s)832 int biseqcstr (const_bstring b, const char * s) {
833 int i;
834 	if (b == NULL || s == NULL || b->data == NULL || b->slen < 0) return BSTR_ERR;
835 	for (i=0; i < b->slen; i++) {
836 		if (s[i] == '\0' || b->data[i] != (unsigned char) s[i]) return BSTR_OK;
837 	}
838 	return s[i] == '\0';
839 }
840 
841 /*  int biseqcstrcaseless (const_bstring b, const char *s)
842  *
843  *  Compare the bstring b and char * string s.  The C string s must be '\0'
844  *  terminated at exactly the length of the bstring b, and the contents
845  *  between the two must be identical except for case with the bstring b with
846  *  no '\0' characters for the two contents to be considered equal.  This is
847  *  equivalent to the condition that their current contents will be always be
848  *  equal ignoring case when comparing them in the same format after
849  *  converting one or the other.  If the strings are equal, except for case,
850  *  1 is returned, if they are unequal regardless of case 0 is returned and
851  *  if there is a detectable error BSTR_ERR is returned.
852  */
biseqcstrcaseless(const_bstring b,const char * s)853 int biseqcstrcaseless (const_bstring b, const char * s) {
854 int i;
855 	if (b == NULL || s == NULL || b->data == NULL || b->slen < 0) return BSTR_ERR;
856 	for (i=0; i < b->slen; i++) {
857 		if (s[i] == '\0' ||
858 		    (b->data[i] != (unsigned char) s[i] &&
859 		     downcase (b->data[i]) != (unsigned char) downcase (s[i])))
860 			return BSTR_OK;
861 	}
862 	return s[i] == '\0';
863 }
864 
865 /*  int bstrcmp (const_bstring b0, const_bstring b1)
866  *
867  *  Compare the string b0 and b1.  If there is an error, SHRT_MIN is returned,
868  *  otherwise a value less than or greater than zero, indicating that the
869  *  string pointed to by b0 is lexicographically less than or greater than
870  *  the string pointed to by b1 is returned.  If the the string lengths are
871  *  unequal but the characters up until the length of the shorter are equal
872  *  then a value less than, or greater than zero, indicating that the string
873  *  pointed to by b0 is shorter or longer than the string pointed to by b1 is
874  *  returned.  0 is returned if and only if the two strings are the same.  If
875  *  the length of the strings are different, this function is O(n).  Like its
876  *  standard C library counter part strcmp, the comparison does not proceed
877  *  past any '\0' termination characters encountered.
878  */
bstrcmp(const_bstring b0,const_bstring b1)879 int bstrcmp (const_bstring b0, const_bstring b1) {
880 int i, v, n;
881 
882 	if (b0 == NULL || b1 == NULL || b0->data == NULL || b1->data == NULL ||
883 		b0->slen < 0 || b1->slen < 0) return SHRT_MIN;
884 	n = b0->slen; if (n > b1->slen) n = b1->slen;
885 	if (b0->slen == b1->slen && (b0->data == b1->data || b0->slen == 0))
886 		return BSTR_OK;
887 
888 	for (i = 0; i < n; i ++) {
889 		v = ((char) b0->data[i]) - ((char) b1->data[i]);
890 		if (v != 0) return v;
891 		if (b0->data[i] == (unsigned char) '\0') return BSTR_OK;
892 	}
893 
894 	if (b0->slen > n) return 1;
895 	if (b1->slen > n) return -1;
896 	return BSTR_OK;
897 }
898 
899 /*  int bstrncmp (const_bstring b0, const_bstring b1, int n)
900  *
901  *  Compare the string b0 and b1 for at most n characters.  If there is an
902  *  error, SHRT_MIN is returned, otherwise a value is returned as if b0 and
903  *  b1 were first truncated to at most n characters then bstrcmp was called
904  *  with these new strings are paremeters.  If the length of the strings are
905  *  different, this function is O(n).  Like its standard C library counter
906  *  part strcmp, the comparison does not proceed past any '\0' termination
907  *  characters encountered.
908  */
bstrncmp(const_bstring b0,const_bstring b1,int n)909 int bstrncmp (const_bstring b0, const_bstring b1, int n) {
910 int i, v, m;
911 
912 	if (b0 == NULL || b1 == NULL || b0->data == NULL || b1->data == NULL ||
913 		b0->slen < 0 || b1->slen < 0) return SHRT_MIN;
914 	m = n;
915 	if (m > b0->slen) m = b0->slen;
916 	if (m > b1->slen) m = b1->slen;
917 
918 	if (b0->data != b1->data) {
919 		for (i = 0; i < m; i ++) {
920 			v = ((char) b0->data[i]) - ((char) b1->data[i]);
921 			if (v != 0) return v;
922 			if (b0->data[i] == (unsigned char) '\0') return BSTR_OK;
923 		}
924 	}
925 
926 	if (n == m || b0->slen == b1->slen) return BSTR_OK;
927 
928 	if (b0->slen > m) return 1;
929 	return -1;
930 }
931 
932 /*  bstring bmidstr (const_bstring b, int left, int len)
933  *
934  *  Create a bstring which is the substring of b starting from position left
935  *  and running for a length len (clamped by the end of the bstring b.)  If
936  *  b is detectably invalid, then NULL is returned.  The section described
937  *  by (left, len) is clamped to the boundaries of b.
938  */
bmidstr(const_bstring b,int left,int len)939 bstring bmidstr (const_bstring b, int left, int len) {
940 
941 	if (b == NULL || b->slen < 0 || b->data == NULL) return NULL;
942 
943 	if (left < 0) {
944 		len += left;
945 		left = 0;
946 	}
947 
948 	if (len > b->slen - left) len = b->slen - left;
949 
950 	if (len <= 0) return bfromcstr ("");
951 	return blk2bstr (b->data + left, len);
952 }
953 
954 /*  int bdelete (bstring b, int pos, int len)
955  *
956  *  Removes characters from pos to pos+len-1 inclusive and shifts the tail of
957  *  the bstring starting from pos+len to pos.  len must be positive for this
958  *  call to have any effect.  The section of the string described by (pos,
959  *  len) is clamped to boundaries of the bstring b.
960  */
bdelete(bstring b,int pos,int len)961 int bdelete (bstring b, int pos, int len) {
962 	/* Clamp to left side of bstring */
963 	if (pos < 0) {
964 		len += pos;
965 		pos = 0;
966 	}
967 
968 	if (len < 0 || b == NULL || b->data == NULL || b->slen < 0 ||
969 	    b->mlen < b->slen || b->mlen <= 0)
970 		return BSTR_ERR;
971 	if (len > 0 && pos < b->slen) {
972 		if (pos + len >= b->slen) {
973 			b->slen = pos;
974 		} else {
975 			bBlockCopy ((char *) (b->data + pos),
976 			            (char *) (b->data + pos + len),
977 			            b->slen - (pos+len));
978 			b->slen -= len;
979 		}
980 		b->data[b->slen] = (unsigned char) '\0';
981 	}
982 	return BSTR_OK;
983 }
984 
985 /*  int bdestroy (bstring b)
986  *
987  *  Free up the bstring.  Note that if b is detectably invalid or not writable
988  *  then no action is performed and BSTR_ERR is returned.  Like a freed memory
989  *  allocation, dereferences, writes or any other action on b after it has
990  *  been bdestroyed is undefined.
991  */
bdestroy(bstring b)992 int bdestroy (bstring b) {
993 	if (b == NULL || b->slen < 0 || b->mlen <= 0 || b->mlen < b->slen ||
994 	    b->data == NULL)
995 		return BSTR_ERR;
996 
997 	bstr__free (b->data);
998 
999 	/* In case there is any stale usage, there is one more chance to
1000 	   notice this error. */
1001 
1002 	b->slen = -1;
1003 	b->mlen = -__LINE__;
1004 	b->data = NULL;
1005 
1006 	bstr__free (b);
1007 	return BSTR_OK;
1008 }
1009 
1010 /*  int binstr (const_bstring b1, int pos, const_bstring b2)
1011  *
1012  *  Search for the bstring b2 in b1 starting from position pos, and searching
1013  *  forward.  If it is found then return with the first position where it is
1014  *  found, otherwise return BSTR_ERR.  Note that this is just a brute force
1015  *  string searcher that does not attempt clever things like the Boyer-Moore
1016  *  search algorithm.  Because of this there are many degenerate cases where
1017  *  this can take much longer than it needs to.
1018  */
binstr(const_bstring b1,int pos,const_bstring b2)1019 int binstr (const_bstring b1, int pos, const_bstring b2) {
1020 int j, ii, ll, lf;
1021 unsigned char * d0;
1022 unsigned char c0;
1023 register unsigned char * d1;
1024 register unsigned char c1;
1025 register int i;
1026 
1027 	if (b1 == NULL || b1->data == NULL || b1->slen < 0 ||
1028 	    b2 == NULL || b2->data == NULL || b2->slen < 0) return BSTR_ERR;
1029 	if (b1->slen == pos) return (b2->slen == 0)?pos:BSTR_ERR;
1030 	if (b1->slen < pos || pos < 0) return BSTR_ERR;
1031 	if (b2->slen == 0) return pos;
1032 
1033 	/* No space to find such a string? */
1034 	if ((lf = b1->slen - b2->slen + 1) <= pos) return BSTR_ERR;
1035 
1036 	/* An obvious alias case */
1037 	if (b1->data == b2->data && pos == 0) return 0;
1038 
1039 	i = pos;
1040 
1041 	d0 = b2->data;
1042 	d1 = b1->data;
1043 	ll = b2->slen;
1044 
1045 	/* Peel off the b2->slen == 1 case */
1046 	c0 = d0[0];
1047 	if (1 == ll) {
1048 		for (;i < lf; i++) if (c0 == d1[i]) return i;
1049 		return BSTR_ERR;
1050 	}
1051 
1052 	c1 = c0;
1053 	j = 0;
1054 	lf = b1->slen - 1;
1055 
1056 	ii = -1;
1057 	if (i < lf) do {
1058 		/* Unrolled current character test */
1059 		if (c1 != d1[i]) {
1060 			if (c1 != d1[1+i]) {
1061 				i += 2;
1062 				continue;
1063 			}
1064 			i++;
1065 		}
1066 
1067 		/* Take note if this is the start of a potential match */
1068 		if (0 == j) ii = i;
1069 
1070 		/* Shift the test character down by one */
1071 		j++;
1072 		i++;
1073 
1074 		/* If this isn't past the last character continue */
1075 		if (j < ll) {
1076 			c1 = d0[j];
1077 			continue;
1078 		}
1079 
1080 		N0:;
1081 
1082 		/* If no characters mismatched, then we matched */
1083 		if (i == ii+j) return ii;
1084 
1085 		/* Shift back to the beginning */
1086 		i -= j;
1087 		j  = 0;
1088 		c1 = c0;
1089 	} while (i < lf);
1090 
1091 	/* Deal with last case if unrolling caused a misalignment */
1092 	if (i == lf && ll == j+1 && c1 == d1[i]) goto N0;
1093 
1094 	return BSTR_ERR;
1095 }
1096 
1097 /*  int binstrr (const_bstring b1, int pos, const_bstring b2)
1098  *
1099  *  Search for the bstring b2 in b1 starting from position pos, and searching
1100  *  backward.  If it is found then return with the first position where it is
1101  *  found, otherwise return BSTR_ERR.  Note that this is just a brute force
1102  *  string searcher that does not attempt clever things like the Boyer-Moore
1103  *  search algorithm.  Because of this there are many degenerate cases where
1104  *  this can take much longer than it needs to.
1105  */
binstrr(const_bstring b1,int pos,const_bstring b2)1106 int binstrr (const_bstring b1, int pos, const_bstring b2) {
1107 int j, i, l;
1108 unsigned char * d0, * d1;
1109 
1110 	if (b1 == NULL || b1->data == NULL || b1->slen < 0 ||
1111 	    b2 == NULL || b2->data == NULL || b2->slen < 0) return BSTR_ERR;
1112 	if (b1->slen == pos && b2->slen == 0) return pos;
1113 	if (b1->slen < pos || pos < 0) return BSTR_ERR;
1114 	if (b2->slen == 0) return pos;
1115 
1116 	/* Obvious alias case */
1117 	if (b1->data == b2->data && pos == 0 && b2->slen <= b1->slen) return 0;
1118 
1119 	i = pos;
1120 	if ((l = b1->slen - b2->slen) < 0) return BSTR_ERR;
1121 
1122 	/* If no space to find such a string then snap back */
1123 	if (l + 1 <= i) i = l;
1124 	j = 0;
1125 
1126 	d0 = b2->data;
1127 	d1 = b1->data;
1128 	l  = b2->slen;
1129 
1130 	for (;;) {
1131 		if (d0[j] == d1[i + j]) {
1132 			j ++;
1133 			if (j >= l) return i;
1134 		} else {
1135 			i --;
1136 			if (i < 0) break;
1137 			j=0;
1138 		}
1139 	}
1140 
1141 	return BSTR_ERR;
1142 }
1143 
1144 /*  int binstrcaseless (const_bstring b1, int pos, const_bstring b2)
1145  *
1146  *  Search for the bstring b2 in b1 starting from position pos, and searching
1147  *  forward but without regard to case.  If it is found then return with the
1148  *  first position where it is found, otherwise return BSTR_ERR.  Note that
1149  *  this is just a brute force string searcher that does not attempt clever
1150  *  things like the Boyer-Moore search algorithm.  Because of this there are
1151  *  many degenerate cases where this can take much longer than it needs to.
1152  */
binstrcaseless(const_bstring b1,int pos,const_bstring b2)1153 int binstrcaseless (const_bstring b1, int pos, const_bstring b2) {
1154 int j, i, l, ll;
1155 unsigned char * d0, * d1;
1156 
1157 	if (b1 == NULL || b1->data == NULL || b1->slen < 0 ||
1158 	    b2 == NULL || b2->data == NULL || b2->slen < 0) return BSTR_ERR;
1159 	if (b1->slen == pos) return (b2->slen == 0)?pos:BSTR_ERR;
1160 	if (b1->slen < pos || pos < 0) return BSTR_ERR;
1161 	if (b2->slen == 0) return pos;
1162 
1163 	l = b1->slen - b2->slen + 1;
1164 
1165 	/* No space to find such a string? */
1166 	if (l <= pos) return BSTR_ERR;
1167 
1168 	/* An obvious alias case */
1169 	if (b1->data == b2->data && pos == 0) return BSTR_OK;
1170 
1171 	i = pos;
1172 	j = 0;
1173 
1174 	d0 = b2->data;
1175 	d1 = b1->data;
1176 	ll = b2->slen;
1177 
1178 	for (;;) {
1179 		if (d0[j] == d1[i + j] || downcase (d0[j]) == downcase (d1[i + j])) {
1180 			j ++;
1181 			if (j >= ll) return i;
1182 		} else {
1183 			i ++;
1184 			if (i >= l) break;
1185 			j=0;
1186 		}
1187 	}
1188 
1189 	return BSTR_ERR;
1190 }
1191 
1192 /*  int binstrrcaseless (const_bstring b1, int pos, const_bstring b2)
1193  *
1194  *  Search for the bstring b2 in b1 starting from position pos, and searching
1195  *  backward but without regard to case.  If it is found then return with the
1196  *  first position where it is found, otherwise return BSTR_ERR.  Note that
1197  *  this is just a brute force string searcher that does not attempt clever
1198  *  things like the Boyer-Moore search algorithm.  Because of this there are
1199  *  many degenerate cases where this can take much longer than it needs to.
1200  */
binstrrcaseless(const_bstring b1,int pos,const_bstring b2)1201 int binstrrcaseless (const_bstring b1, int pos, const_bstring b2) {
1202 int j, i, l;
1203 unsigned char * d0, * d1;
1204 
1205 	if (b1 == NULL || b1->data == NULL || b1->slen < 0 ||
1206 	    b2 == NULL || b2->data == NULL || b2->slen < 0) return BSTR_ERR;
1207 	if (b1->slen == pos && b2->slen == 0) return pos;
1208 	if (b1->slen < pos || pos < 0) return BSTR_ERR;
1209 	if (b2->slen == 0) return pos;
1210 
1211 	/* Obvious alias case */
1212 	if (b1->data == b2->data && pos == 0 && b2->slen <= b1->slen) return BSTR_OK;
1213 
1214 	i = pos;
1215 	if ((l = b1->slen - b2->slen) < 0) return BSTR_ERR;
1216 
1217 	/* If no space to find such a string then snap back */
1218 	if (l + 1 <= i) i = l;
1219 	j = 0;
1220 
1221 	d0 = b2->data;
1222 	d1 = b1->data;
1223 	l  = b2->slen;
1224 
1225 	for (;;) {
1226 		if (d0[j] == d1[i + j] || downcase (d0[j]) == downcase (d1[i + j])) {
1227 			j ++;
1228 			if (j >= l) return i;
1229 		} else {
1230 			i --;
1231 			if (i < 0) break;
1232 			j=0;
1233 		}
1234 	}
1235 
1236 	return BSTR_ERR;
1237 }
1238 
1239 
1240 /*  int bstrchrp (const_bstring b, int c, int pos)
1241  *
1242  *  Search for the character c in b forwards from the position pos
1243  *  (inclusive).
1244  */
bstrchrp(const_bstring b,int c,int pos)1245 int bstrchrp (const_bstring b, int c, int pos) {
1246 unsigned char * p;
1247 
1248 	if (b == NULL || b->data == NULL || b->slen <= pos || pos < 0) return BSTR_ERR;
1249 	p = (unsigned char *) bstr__memchr ((b->data + pos), (unsigned char) c, (b->slen - pos));
1250 	if (p) return (int) (p - b->data);
1251 	return BSTR_ERR;
1252 }
1253 
1254 /*  int bstrrchrp (const_bstring b, int c, int pos)
1255  *
1256  *  Search for the character c in b backwards from the position pos in string
1257  *  (inclusive).
1258  */
bstrrchrp(const_bstring b,int c,int pos)1259 int bstrrchrp (const_bstring b, int c, int pos) {
1260 int i;
1261 
1262 	if (b == NULL || b->data == NULL || b->slen <= pos || pos < 0) return BSTR_ERR;
1263 	for (i=pos; i >= 0; i--) {
1264 		if (b->data[i] == (unsigned char) c) return i;
1265 	}
1266 	return BSTR_ERR;
1267 }
1268 
1269 #if !defined (BSTRLIB_AGGRESSIVE_MEMORY_FOR_SPEED_TRADEOFF)
1270 #define LONG_LOG_BITS_QTY (3)
1271 #define LONG_BITS_QTY (1 << LONG_LOG_BITS_QTY)
1272 #define LONG_TYPE unsigned char
1273 
1274 #define CFCLEN ((1 << CHAR_BIT) / LONG_BITS_QTY)
1275 struct charField { LONG_TYPE content[CFCLEN]; };
1276 #define testInCharField(cf,c) ((cf)->content[(c) >> LONG_LOG_BITS_QTY] & (((long)1) << ((c) & (LONG_BITS_QTY-1))))
1277 #define setInCharField(cf,idx) { \
1278 	unsigned int c = (unsigned int) (idx); \
1279 	(cf)->content[c >> LONG_LOG_BITS_QTY] |= (LONG_TYPE) (1ul << (c & (LONG_BITS_QTY-1))); \
1280 }
1281 
1282 #else
1283 
1284 #define CFCLEN (1 << CHAR_BIT)
1285 struct charField { unsigned char content[CFCLEN]; };
1286 #define testInCharField(cf,c) ((cf)->content[(unsigned char) (c)])
1287 #define setInCharField(cf,idx) (cf)->content[(unsigned int) (idx)] = ~0
1288 
1289 #endif
1290 
1291 /* Convert a bstring to charField */
buildCharField(struct charField * cf,const_bstring b)1292 static int buildCharField (struct charField * cf, const_bstring b) {
1293 int i;
1294 	if (b == NULL || b->data == NULL || b->slen <= 0) return BSTR_ERR;
1295 	memset ((void *) cf->content, 0, sizeof (struct charField));
1296 	for (i=0; i < b->slen; i++) {
1297 		setInCharField (cf, b->data[i]);
1298 	}
1299 	return BSTR_OK;
1300 }
1301 
invertCharField(struct charField * cf)1302 static void invertCharField (struct charField * cf) {
1303 int i;
1304 	for (i=0; i < CFCLEN; i++) cf->content[i] = ~cf->content[i];
1305 }
1306 
1307 /* Inner engine for binchr */
binchrCF(const unsigned char * data,int len,int pos,const struct charField * cf)1308 static int binchrCF (const unsigned char * data, int len, int pos, const struct charField * cf) {
1309 int i;
1310 	for (i=pos; i < len; i++) {
1311 		unsigned char c = (unsigned char) data[i];
1312 		if (testInCharField (cf, c)) return i;
1313 	}
1314 	return BSTR_ERR;
1315 }
1316 
1317 /*  int binchr (const_bstring b0, int pos, const_bstring b1);
1318  *
1319  *  Search for the first position in b0 starting from pos or after, in which
1320  *  one of the characters in b1 is found and return it.  If such a position
1321  *  does not exist in b0, then BSTR_ERR is returned.
1322  */
binchr(const_bstring b0,int pos,const_bstring b1)1323 int binchr (const_bstring b0, int pos, const_bstring b1) {
1324 struct charField chrs;
1325 	if (pos < 0 || b0 == NULL || b0->data == NULL ||
1326 	    b0->slen <= pos) return BSTR_ERR;
1327 	if (1 == b1->slen) return bstrchrp (b0, b1->data[0], pos);
1328 	if (0 > buildCharField (&chrs, b1)) return BSTR_ERR;
1329 	return binchrCF (b0->data, b0->slen, pos, &chrs);
1330 }
1331 
1332 /* Inner engine for binchrr */
binchrrCF(const unsigned char * data,int pos,const struct charField * cf)1333 static int binchrrCF (const unsigned char * data, int pos, const struct charField * cf) {
1334 int i;
1335 	for (i=pos; i >= 0; i--) {
1336 		unsigned int c = (unsigned int) data[i];
1337 		if (testInCharField (cf, c)) return i;
1338 	}
1339 	return BSTR_ERR;
1340 }
1341 
1342 /*  int binchrr (const_bstring b0, int pos, const_bstring b1);
1343  *
1344  *  Search for the last position in b0 no greater than pos, in which one of
1345  *  the characters in b1 is found and return it.  If such a position does not
1346  *  exist in b0, then BSTR_ERR is returned.
1347  */
binchrr(const_bstring b0,int pos,const_bstring b1)1348 int binchrr (const_bstring b0, int pos, const_bstring b1) {
1349 struct charField chrs;
1350 	if (pos < 0 || b0 == NULL || b0->data == NULL || b1 == NULL ||
1351 	    b0->slen < pos) return BSTR_ERR;
1352 	if (pos == b0->slen) pos--;
1353 	if (1 == b1->slen) return bstrrchrp (b0, b1->data[0], pos);
1354 	if (0 > buildCharField (&chrs, b1)) return BSTR_ERR;
1355 	return binchrrCF (b0->data, pos, &chrs);
1356 }
1357 
1358 /*  int bninchr (const_bstring b0, int pos, const_bstring b1);
1359  *
1360  *  Search for the first position in b0 starting from pos or after, in which
1361  *  none of the characters in b1 is found and return it.  If such a position
1362  *  does not exist in b0, then BSTR_ERR is returned.
1363  */
bninchr(const_bstring b0,int pos,const_bstring b1)1364 int bninchr (const_bstring b0, int pos, const_bstring b1) {
1365 struct charField chrs;
1366 	if (pos < 0 || b0 == NULL || b0->data == NULL ||
1367 	    b0->slen <= pos) return BSTR_ERR;
1368 	if (buildCharField (&chrs, b1) < 0) return BSTR_ERR;
1369 	invertCharField (&chrs);
1370 	return binchrCF (b0->data, b0->slen, pos, &chrs);
1371 }
1372 
1373 /*  int bninchrr (const_bstring b0, int pos, const_bstring b1);
1374  *
1375  *  Search for the last position in b0 no greater than pos, in which none of
1376  *  the characters in b1 is found and return it.  If such a position does not
1377  *  exist in b0, then BSTR_ERR is returned.
1378  */
bninchrr(const_bstring b0,int pos,const_bstring b1)1379 int bninchrr (const_bstring b0, int pos, const_bstring b1) {
1380 struct charField chrs;
1381 	if (pos < 0 || b0 == NULL || b0->data == NULL ||
1382 	    b0->slen < pos) return BSTR_ERR;
1383 	if (pos == b0->slen) pos--;
1384 	if (buildCharField (&chrs, b1) < 0) return BSTR_ERR;
1385 	invertCharField (&chrs);
1386 	return binchrrCF (b0->data, pos, &chrs);
1387 }
1388 
1389 /*  int bsetstr (bstring b0, int pos, bstring b1, unsigned char fill)
1390  *
1391  *  Overwrite the string b0 starting at position pos with the string b1. If
1392  *  the position pos is past the end of b0, then the character "fill" is
1393  *  appended as necessary to make up the gap between the end of b0 and pos.
1394  *  If b1 is NULL, it behaves as if it were a 0-length string.
1395  */
bsetstr(bstring b0,int pos,const_bstring b1,unsigned char fill)1396 int bsetstr (bstring b0, int pos, const_bstring b1, unsigned char fill) {
1397 int d, newlen;
1398 ptrdiff_t pd;
1399 bstring aux = (bstring) b1;
1400 
1401 	if (pos < 0 || b0 == NULL || b0->slen < 0 || NULL == b0->data ||
1402 	    b0->mlen < b0->slen || b0->mlen <= 0) return BSTR_ERR;
1403 	if (b1 != NULL && (b1->slen < 0 || b1->data == NULL)) return BSTR_ERR;
1404 
1405 	d = pos;
1406 
1407 	/* Aliasing case */
1408 	if (NULL != aux) {
1409 		if ((pd = (ptrdiff_t) (b1->data - b0->data)) >= 0 && pd < (ptrdiff_t) b0->mlen) {
1410 			if (NULL == (aux = bstrcpy (b1))) return BSTR_ERR;
1411 		}
1412 		d += aux->slen;
1413 	}
1414 
1415 	/* Increase memory size if necessary */
1416 	if (balloc (b0, d + 1) != BSTR_OK) {
1417 		if (aux != b1) bdestroy (aux);
1418 		return BSTR_ERR;
1419 	}
1420 
1421 	newlen = b0->slen;
1422 
1423 	/* Fill in "fill" character as necessary */
1424 	if (pos > newlen) {
1425 		bstr__memset (b0->data + b0->slen, (int) fill, (size_t) (pos - b0->slen));
1426 		newlen = pos;
1427 	}
1428 
1429 	/* Copy b1 to position pos in b0. */
1430 	if (aux != NULL) {
1431 		bBlockCopy ((char *) (b0->data + pos), (char *) aux->data, aux->slen);
1432 		if (aux != b1) bdestroy (aux);
1433 	}
1434 
1435 	/* Indicate the potentially increased size of b0 */
1436 	if (d > newlen) newlen = d;
1437 
1438 	b0->slen = newlen;
1439 	b0->data[newlen] = (unsigned char) '\0';
1440 
1441 	return BSTR_OK;
1442 }
1443 
1444 /*  int binsert (bstring b1, int pos, bstring b2, unsigned char fill)
1445  *
1446  *  Inserts the string b2 into b1 at position pos.  If the position pos is
1447  *  past the end of b1, then the character "fill" is appended as necessary to
1448  *  make up the gap between the end of b1 and pos.  Unlike bsetstr, binsert
1449  *  does not allow b2 to be NULL.
1450  */
binsert(bstring b1,int pos,const_bstring b2,unsigned char fill)1451 int binsert (bstring b1, int pos, const_bstring b2, unsigned char fill) {
1452 int d, l;
1453 ptrdiff_t pd;
1454 bstring aux = (bstring) b2;
1455 
1456 	if (pos < 0 || b1 == NULL || b2 == NULL || b1->slen < 0 ||
1457 	    b2->slen < 0 || b1->mlen < b1->slen || b1->mlen <= 0) return BSTR_ERR;
1458 
1459 	/* Aliasing case */
1460 	if ((pd = (ptrdiff_t) (b2->data - b1->data)) >= 0 && pd < (ptrdiff_t) b1->mlen) {
1461 		if (NULL == (aux = bstrcpy (b2))) return BSTR_ERR;
1462 	}
1463 
1464 	/* Compute the two possible end pointers */
1465 	d = b1->slen + aux->slen;
1466 	l = pos + aux->slen;
1467 	if ((d|l) < 0) return BSTR_ERR;
1468 
1469 	if (l > d) {
1470 		/* Inserting past the end of the string */
1471 		if (balloc (b1, l + 1) != BSTR_OK) {
1472 			if (aux != b2) bdestroy (aux);
1473 			return BSTR_ERR;
1474 		}
1475 		bstr__memset (b1->data + b1->slen, (int) fill, (size_t) (pos - b1->slen));
1476 		b1->slen = l;
1477 	} else {
1478 		/* Inserting in the middle of the string */
1479 		if (balloc (b1, d + 1) != BSTR_OK) {
1480 			if (aux != b2) bdestroy (aux);
1481 			return BSTR_ERR;
1482 		}
1483 		bBlockCopy (b1->data + l, b1->data + pos, d - l);
1484 		b1->slen = d;
1485 	}
1486 	bBlockCopy (b1->data + pos, aux->data, aux->slen);
1487 	b1->data[b1->slen] = (unsigned char) '\0';
1488 	if (aux != b2) bdestroy (aux);
1489 	return BSTR_OK;
1490 }
1491 
1492 /*  int breplace (bstring b1, int pos, int len, bstring b2,
1493  *                unsigned char fill)
1494  *
1495  *  Replace a section of a string from pos for a length len with the string b2.
1496  *  fill is used is pos > b1->slen.
1497  */
breplace(bstring b1,int pos,int len,const_bstring b2,unsigned char fill)1498 int breplace (bstring b1, int pos, int len, const_bstring b2,
1499 			  unsigned char fill) {
1500 int pl, ret;
1501 ptrdiff_t pd;
1502 bstring aux = (bstring) b2;
1503 
1504 	if (pos < 0 || len < 0 || (pl = pos + len) < 0 || b1 == NULL ||
1505 	    b2 == NULL || b1->data == NULL || b2->data == NULL ||
1506 	    b1->slen < 0 || b2->slen < 0 || b1->mlen < b1->slen ||
1507 	    b1->mlen <= 0) return BSTR_ERR;
1508 
1509 	/* Straddles the end? */
1510 	if (pl >= b1->slen) {
1511 		if ((ret = bsetstr (b1, pos, b2, fill)) < 0) return ret;
1512 		if (pos + b2->slen < b1->slen) {
1513 			b1->slen = pos + b2->slen;
1514 			b1->data[b1->slen] = (unsigned char) '\0';
1515 		}
1516 		return ret;
1517 	}
1518 
1519 	/* Aliasing case */
1520 	if ((pd = (ptrdiff_t) (b2->data - b1->data)) >= 0 && pd < (ptrdiff_t) b1->slen) {
1521 		if (NULL == (aux = bstrcpy (b2))) return BSTR_ERR;
1522 	}
1523 
1524 	if (aux->slen > len) {
1525 		if (balloc (b1, b1->slen + aux->slen - len) != BSTR_OK) {
1526 			if (aux != b2) bdestroy (aux);
1527 			return BSTR_ERR;
1528 		}
1529 	}
1530 
1531 	if (aux->slen != len) bstr__memmove (b1->data + pos + aux->slen, b1->data + pos + len, b1->slen - (pos + len));
1532 	bstr__memcpy (b1->data + pos, aux->data, aux->slen);
1533 	b1->slen += aux->slen - len;
1534 	b1->data[b1->slen] = (unsigned char) '\0';
1535 	if (aux != b2) bdestroy (aux);
1536 	return BSTR_OK;
1537 }
1538 
1539 /*  int bfindreplace (bstring b, const_bstring find, const_bstring repl,
1540  *                    int pos)
1541  *
1542  *  Replace all occurrences of a find string with a replace string after a
1543  *  given point in a bstring.
1544  */
1545 
1546 typedef int (*instr_fnptr) (const_bstring s1, int pos, const_bstring s2);
1547 
findreplaceengine(bstring b,const_bstring find,const_bstring repl,int pos,instr_fnptr instr)1548 static int findreplaceengine (bstring b, const_bstring find, const_bstring repl, int pos, instr_fnptr instr) {
1549 int i, ret, slen, mlen, delta, acc;
1550 int * d;
1551 int static_d[32];
1552 ptrdiff_t pd;
1553 bstring auxf = (bstring) find;
1554 bstring auxr = (bstring) repl;
1555 
1556 	if (b == NULL || b->data == NULL || find == NULL ||
1557 	    find->data == NULL || repl == NULL || repl->data == NULL ||
1558 	    pos < 0 || find->slen <= 0 || b->mlen < 0 || b->slen > b->mlen ||
1559 	    b->mlen <= 0 || b->slen < 0 || repl->slen < 0) return BSTR_ERR;
1560 	if (pos > b->slen - find->slen) return BSTR_OK;
1561 
1562 	/* Alias with find string */
1563 	pd = (ptrdiff_t) (find->data - b->data);
1564 	if ((ptrdiff_t) (pos - find->slen) < pd && pd < (ptrdiff_t) b->slen) {
1565 		if (NULL == (auxf = bstrcpy (find))) return BSTR_ERR;
1566 	}
1567 
1568 	/* Alias with repl string */
1569 	pd = (ptrdiff_t) (repl->data - b->data);
1570 	if ((ptrdiff_t) (pos - repl->slen) < pd && pd < (ptrdiff_t) b->slen) {
1571 		if (NULL == (auxr = bstrcpy (repl))) {
1572 			if (auxf != find) bdestroy (auxf);
1573 			return BSTR_ERR;
1574 		}
1575 	}
1576 
1577 	delta = auxf->slen - auxr->slen;
1578 
1579 	/* in-place replacement since find and replace strings are of equal
1580 	   length */
1581 	if (delta == 0) {
1582 		while ((pos = instr (b, pos, auxf)) >= 0) {
1583 			bstr__memcpy (b->data + pos, auxr->data, auxr->slen);
1584 			pos += auxf->slen;
1585 		}
1586 		if (auxf != find) bdestroy (auxf);
1587 		if (auxr != repl) bdestroy (auxr);
1588 		return BSTR_OK;
1589 	}
1590 
1591 	/* shrinking replacement since auxf->slen > auxr->slen */
1592 	if (delta > 0) {
1593 		acc = 0;
1594 
1595 		while ((i = instr (b, pos, auxf)) >= 0) {
1596 			if (acc && i > pos)
1597 				bstr__memmove (b->data + pos - acc, b->data + pos, i - pos);
1598 			if (auxr->slen)
1599 				bstr__memcpy (b->data + i - acc, auxr->data, auxr->slen);
1600 			acc += delta;
1601 			pos = i + auxf->slen;
1602 		}
1603 
1604 		if (acc) {
1605 			i = b->slen;
1606 			if (i > pos)
1607 				bstr__memmove (b->data + pos - acc, b->data + pos, i - pos);
1608 			b->slen -= acc;
1609 			b->data[b->slen] = (unsigned char) '\0';
1610 		}
1611 
1612 		if (auxf != find) bdestroy (auxf);
1613 		if (auxr != repl) bdestroy (auxr);
1614 		return BSTR_OK;
1615 	}
1616 
1617 	/* expanding replacement since find->slen < repl->slen.  Its a lot
1618 	   more complicated. */
1619 
1620 	mlen = 32;
1621 	d = (int *) static_d; /* Avoid malloc for trivial cases */
1622 	acc = slen = 0;
1623 
1624 	while ((pos = instr (b, pos, auxf)) >= 0) {
1625 		if (slen + 1 >= mlen) {
1626 			int sl;
1627 			int * t;
1628 			mlen += mlen;
1629 			sl = sizeof (int *) * mlen;
1630 			if (static_d == d) d = NULL;
1631 			if (sl < mlen || NULL == (t = (int *) bstr__realloc (d, sl))) {
1632 				ret = BSTR_ERR;
1633 				goto done;
1634 			}
1635 			if (NULL == d) bstr__memcpy (t, static_d, sizeof (static_d));
1636 			d = t;
1637 		}
1638 		d[slen] = pos;
1639 		slen++;
1640 		acc -= delta;
1641 		pos += auxf->slen;
1642 		if (pos < 0 || acc < 0) {
1643 			ret = BSTR_ERR;
1644 			goto done;
1645 		}
1646 	}
1647 	d[slen] = b->slen;
1648 
1649 	if (BSTR_OK == (ret = balloc (b, b->slen + acc + 1))) {
1650 		b->slen += acc;
1651 		for (i = slen-1; i >= 0; i--) {
1652 			int s, l;
1653 			s = d[i] + auxf->slen;
1654 			l = d[i+1] - s;
1655 			if (l) {
1656 				bstr__memmove (b->data + s + acc, b->data + s, l);
1657 			}
1658 			if (auxr->slen) {
1659 				bstr__memmove (b->data + s + acc - auxr->slen,
1660 				         auxr->data, auxr->slen);
1661 			}
1662 			acc += delta;
1663 		}
1664 		b->data[b->slen] = (unsigned char) '\0';
1665 	}
1666 
1667 	done:;
1668 	if (static_d == d) d = NULL;
1669 	bstr__free (d);
1670 	if (auxf != find) bdestroy (auxf);
1671 	if (auxr != repl) bdestroy (auxr);
1672 	return ret;
1673 }
1674 
1675 /*  int bfindreplace (bstring b, const_bstring find, const_bstring repl,
1676  *                    int pos)
1677  *
1678  *  Replace all occurrences of a find string with a replace string after a
1679  *  given point in a bstring.
1680  */
bfindreplace(bstring b,const_bstring find,const_bstring repl,int pos)1681 int bfindreplace (bstring b, const_bstring find, const_bstring repl, int pos) {
1682 	return findreplaceengine (b, find, repl, pos, binstr);
1683 }
1684 
1685 /*  int bfindreplacecaseless (bstring b, const_bstring find, const_bstring repl,
1686  *                    int pos)
1687  *
1688  *  Replace all occurrences of a find string, ignoring case, with a replace
1689  *  string after a given point in a bstring.
1690  */
bfindreplacecaseless(bstring b,const_bstring find,const_bstring repl,int pos)1691 int bfindreplacecaseless (bstring b, const_bstring find, const_bstring repl, int pos) {
1692 	return findreplaceengine (b, find, repl, pos, binstrcaseless);
1693 }
1694 
1695 /*  int binsertch (bstring b, int pos, int len, unsigned char fill)
1696  *
1697  *  Inserts the character fill repeatedly into b at position pos for a
1698  *  length len.  If the position pos is past the end of b, then the
1699  *  character "fill" is appended as necessary to make up the gap between the
1700  *  end of b and the position pos + len.
1701  */
binsertch(bstring b,int pos,int len,unsigned char fill)1702 int binsertch (bstring b, int pos, int len, unsigned char fill) {
1703 int d, l, i;
1704 
1705 	if (pos < 0 || b == NULL || b->slen < 0 || b->mlen < b->slen ||
1706 	    b->mlen <= 0 || len < 0) return BSTR_ERR;
1707 
1708 	/* Compute the two possible end pointers */
1709 	d = b->slen + len;
1710 	l = pos + len;
1711 	if ((d|l) < 0) return BSTR_ERR;
1712 
1713 	if (l > d) {
1714 		/* Inserting past the end of the string */
1715 		if (balloc (b, l + 1) != BSTR_OK) return BSTR_ERR;
1716 		pos = b->slen;
1717 		b->slen = l;
1718 	} else {
1719 		/* Inserting in the middle of the string */
1720 		if (balloc (b, d + 1) != BSTR_OK) return BSTR_ERR;
1721 		for (i = d - 1; i >= l; i--) {
1722 			b->data[i] = b->data[i - len];
1723 		}
1724 		b->slen = d;
1725 	}
1726 
1727 	for (i=pos; i < l; i++) b->data[i] = fill;
1728 	b->data[b->slen] = (unsigned char) '\0';
1729 	return BSTR_OK;
1730 }
1731 
1732 /*  int bpattern (bstring b, int len)
1733  *
1734  *  Replicate the bstring, b in place, end to end repeatedly until it
1735  *  surpasses len characters, then chop the result to exactly len characters.
1736  *  This function operates in-place.  The function will return with BSTR_ERR
1737  *  if b is NULL or of length 0, otherwise BSTR_OK is returned.
1738  */
bpattern(bstring b,int len)1739 int bpattern (bstring b, int len) {
1740 int i, d;
1741 
1742 	d = blength (b);
1743 	if (d <= 0 || len < 0 || balloc (b, len + 1) != BSTR_OK) return BSTR_ERR;
1744 	if (len > 0) {
1745 		if (d == 1) return bsetstr (b, len, NULL, b->data[0]);
1746 		for (i = d; i < len; i++) b->data[i] = b->data[i - d];
1747 	}
1748 	b->data[len] = (unsigned char) '\0';
1749 	b->slen = len;
1750 	return BSTR_OK;
1751 }
1752 
1753 #define BS_BUFF_SZ (1024)
1754 
1755 /*  int breada (bstring b, bNread readPtr, void * parm)
1756  *
1757  *  Use a finite buffer fread-like function readPtr to concatenate to the
1758  *  bstring b the entire contents of file-like source data in a roughly
1759  *  efficient way.
1760  */
breada(bstring b,bNread readPtr,void * parm)1761 int breada (bstring b, bNread readPtr, void * parm) {
1762 int i, l, n;
1763 
1764 	if (b == NULL || b->mlen <= 0 || b->slen < 0 || b->mlen < b->slen ||
1765 	    b->mlen <= 0 || readPtr == NULL) return BSTR_ERR;
1766 
1767 	i = b->slen;
1768 	for (n=i+16; ; n += ((n < BS_BUFF_SZ) ? n : BS_BUFF_SZ)) {
1769 		if (BSTR_OK != balloc (b, n + 1)) return BSTR_ERR;
1770 		l = (int) readPtr ((void *) (b->data + i), 1, n - i, parm);
1771 		i += l;
1772 		b->slen = i;
1773 		if (i < n) break;
1774 	}
1775 
1776 	b->data[i] = (unsigned char) '\0';
1777 	return BSTR_OK;
1778 }
1779 
1780 /*  bstring bread (bNread readPtr, void * parm)
1781  *
1782  *  Use a finite buffer fread-like function readPtr to create a bstring
1783  *  filled with the entire contents of file-like source data in a roughly
1784  *  efficient way.
1785  */
bread(bNread readPtr,void * parm)1786 bstring bread (bNread readPtr, void * parm) {
1787 bstring buff;
1788 
1789 	if (0 > breada (buff = bfromcstr (""), readPtr, parm)) {
1790 		bdestroy (buff);
1791 		return NULL;
1792 	}
1793 	return buff;
1794 }
1795 
1796 /*  int bassigngets (bstring b, bNgetc getcPtr, void * parm, char terminator)
1797  *
1798  *  Use an fgetc-like single character stream reading function (getcPtr) to
1799  *  obtain a sequence of characters which are concatenated to the end of the
1800  *  bstring b.  The stream read is terminated by the passed in terminator
1801  *  parameter.
1802  *
1803  *  If getcPtr returns with a negative number, or the terminator character
1804  *  (which is appended) is read, then the stream reading is halted and the
1805  *  function returns with a partial result in b.  If there is an empty partial
1806  *  result, 1 is returned.  If no characters are read, or there is some other
1807  *  detectable error, BSTR_ERR is returned.
1808  */
bassigngets(bstring b,bNgetc getcPtr,void * parm,char terminator)1809 int bassigngets (bstring b, bNgetc getcPtr, void * parm, char terminator) {
1810 int c, d, e;
1811 
1812 	if (b == NULL || b->mlen <= 0 || b->slen < 0 || b->mlen < b->slen ||
1813 	    b->mlen <= 0 || getcPtr == NULL) return BSTR_ERR;
1814 	d = 0;
1815 	e = b->mlen - 2;
1816 
1817 	while ((c = getcPtr (parm)) >= 0) {
1818 		if (d > e) {
1819 			b->slen = d;
1820 			if (balloc (b, d + 2) != BSTR_OK) return BSTR_ERR;
1821 			e = b->mlen - 2;
1822 		}
1823 		b->data[d] = (unsigned char) c;
1824 		d++;
1825 		if (c == terminator) break;
1826 	}
1827 
1828 	b->data[d] = (unsigned char) '\0';
1829 	b->slen = d;
1830 
1831 	return d == 0 && c < 0;
1832 }
1833 
1834 /*  int bgetsa (bstring b, bNgetc getcPtr, void * parm, char terminator)
1835  *
1836  *  Use an fgetc-like single character stream reading function (getcPtr) to
1837  *  obtain a sequence of characters which are concatenated to the end of the
1838  *  bstring b.  The stream read is terminated by the passed in terminator
1839  *  parameter.
1840  *
1841  *  If getcPtr returns with a negative number, or the terminator character
1842  *  (which is appended) is read, then the stream reading is halted and the
1843  *  function returns with a partial result concatentated to b.  If there is
1844  *  an empty partial result, 1 is returned.  If no characters are read, or
1845  *  there is some other detectable error, BSTR_ERR is returned.
1846  */
bgetsa(bstring b,bNgetc getcPtr,void * parm,char terminator)1847 int bgetsa (bstring b, bNgetc getcPtr, void * parm, char terminator) {
1848 int c, d, e;
1849 
1850 	if (b == NULL || b->mlen <= 0 || b->slen < 0 || b->mlen < b->slen ||
1851 	    b->mlen <= 0 || getcPtr == NULL) return BSTR_ERR;
1852 	d = b->slen;
1853 	e = b->mlen - 2;
1854 
1855 	while ((c = getcPtr (parm)) >= 0) {
1856 		if (d > e) {
1857 			b->slen = d;
1858 			if (balloc (b, d + 2) != BSTR_OK) return BSTR_ERR;
1859 			e = b->mlen - 2;
1860 		}
1861 		b->data[d] = (unsigned char) c;
1862 		d++;
1863 		if (c == terminator) break;
1864 	}
1865 
1866 	b->data[d] = (unsigned char) '\0';
1867 	b->slen = d;
1868 
1869 	return d == 0 && c < 0;
1870 }
1871 
1872 /*  bstring bgetstream (bNgetc getcPtr, void * parm, char terminator)
1873  *
1874  *  Use an fgetc-like single character stream reading function (getcPtr) to
1875  *  obtain a sequence of characters which are concatenated into a bstring.
1876  *  The stream read is terminated by the passed in terminator function.
1877  *
1878  *  If getcPtr returns with a negative number, or the terminator character
1879  *  (which is appended) is read, then the stream reading is halted and the
1880  *  result obtained thus far is returned.  If no characters are read, or
1881  *  there is some other detectable error, NULL is returned.
1882  */
bgetstream(bNgetc getcPtr,void * parm,char terminator)1883 bstring bgetstream (bNgetc getcPtr, void * parm, char terminator) {
1884 bstring buff;
1885 
1886 	if (0 > bgetsa (buff = bfromcstr (""), getcPtr, parm, terminator) || 0 >= buff->slen) {
1887 		bdestroy (buff);
1888 		buff = NULL;
1889 	}
1890 	return buff;
1891 }
1892 
1893 struct bStream {
1894 	bstring buff;		/* Buffer for over-reads */
1895 	void * parm;		/* The stream handle for core stream */
1896 	bNread readFnPtr;	/* fread compatible fnptr for core stream */
1897 	int isEOF;		/* track file's EOF state */
1898 	int maxBuffSz;
1899 };
1900 
1901 /*  struct bStream * bsopen (bNread readPtr, void * parm)
1902  *
1903  *  Wrap a given open stream (described by a fread compatible function
1904  *  pointer and stream handle) into an open bStream suitable for the bstring
1905  *  library streaming functions.
1906  */
bsopen(bNread readPtr,void * parm)1907 struct bStream * bsopen (bNread readPtr, void * parm) {
1908 struct bStream * s;
1909 
1910 	if (readPtr == NULL) return NULL;
1911 	s = (struct bStream *) bstr__alloc (sizeof (struct bStream));
1912 	if (s == NULL) return NULL;
1913 	s->parm = parm;
1914 	s->buff = bfromcstr ("");
1915 	s->readFnPtr = readPtr;
1916 	s->maxBuffSz = BS_BUFF_SZ;
1917 	s->isEOF = 0;
1918 	return s;
1919 }
1920 
1921 /*  int bsbufflength (struct bStream * s, int sz)
1922  *
1923  *  Set the length of the buffer used by the bStream.  If sz is zero, the
1924  *  length is not set.  This function returns with the previous length.
1925  */
bsbufflength(struct bStream * s,int sz)1926 int bsbufflength (struct bStream * s, int sz) {
1927 int oldSz;
1928 	if (s == NULL || sz < 0) return BSTR_ERR;
1929 	oldSz = s->maxBuffSz;
1930 	if (sz > 0) s->maxBuffSz = sz;
1931 	return oldSz;
1932 }
1933 
bseof(const struct bStream * s)1934 int bseof (const struct bStream * s) {
1935 	if (s == NULL || s->readFnPtr == NULL) return BSTR_ERR;
1936 	return s->isEOF && (s->buff->slen == 0);
1937 }
1938 
1939 /*  void * bsclose (struct bStream * s)
1940  *
1941  *  Close the bStream, and return the handle to the stream that was originally
1942  *  used to open the given stream.
1943  */
bsclose(struct bStream * s)1944 void * bsclose (struct bStream * s) {
1945 void * parm;
1946 	if (s == NULL) return NULL;
1947 	s->readFnPtr = NULL;
1948 	if (s->buff) bdestroy (s->buff);
1949 	s->buff = NULL;
1950 	parm = s->parm;
1951 	s->parm = NULL;
1952 	s->isEOF = 1;
1953 	bstr__free (s);
1954 	return parm;
1955 }
1956 
1957 /*  int bsreadlna (bstring r, struct bStream * s, char terminator)
1958  *
1959  *  Read a bstring terminated by the terminator character or the end of the
1960  *  stream from the bStream (s) and return it into the parameter r.  This
1961  *  function may read additional characters from the core stream that are not
1962  *  returned, but will be retained for subsequent read operations.
1963  */
bsreadlna(bstring r,struct bStream * s,char terminator)1964 int bsreadlna (bstring r, struct bStream * s, char terminator) {
1965 int i, l, ret, rlo;
1966 char * b;
1967 struct tagbstring x;
1968 
1969 	if (s == NULL || s->buff == NULL || r == NULL || r->mlen <= 0 ||
1970 	    r->slen < 0 || r->mlen < r->slen) return BSTR_ERR;
1971 	l = s->buff->slen;
1972 	if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR;
1973 	b = (char *) s->buff->data;
1974 	x.data = (unsigned char *) b;
1975 
1976 	/* First check if the current buffer holds the terminator */
1977 	b[l] = terminator; /* Set sentinel */
1978 	for (i=0; b[i] != terminator; i++) ;
1979 	if (i < l) {
1980 		x.slen = i + 1;
1981 		ret = bconcat (r, &x);
1982 		s->buff->slen = l;
1983 		if (BSTR_OK == ret) bdelete (s->buff, 0, i + 1);
1984 		return BSTR_OK;
1985 	}
1986 
1987 	rlo = r->slen;
1988 
1989 	/* If not then just concatenate the entire buffer to the output */
1990 	x.slen = l;
1991 	if (BSTR_OK != bconcat (r, &x)) return BSTR_ERR;
1992 
1993 	/* Perform direct in-place reads into the destination to allow for
1994 	   the minimum of data-copies */
1995 	for (;;) {
1996 		if (BSTR_OK != balloc (r, r->slen + s->maxBuffSz + 1)) return BSTR_ERR;
1997 		b = (char *) (r->data + r->slen);
1998 		l = (int) s->readFnPtr (b, 1, s->maxBuffSz, s->parm);
1999 		if (l <= 0) {
2000 			r->data[r->slen] = (unsigned char) '\0';
2001 			s->buff->slen = 0;
2002 			s->isEOF = 1;
2003 			/* If nothing was read return with an error message */
2004 			return BSTR_ERR & -(r->slen == rlo);
2005 		}
2006 		b[l] = terminator; /* Set sentinel */
2007 		for (i=0; b[i] != terminator; i++) ;
2008 		if (i < l) break;
2009 		r->slen += l;
2010 	}
2011 
2012 	/* Terminator found, push over-read back to buffer */
2013 	i++;
2014 	r->slen += i;
2015 	s->buff->slen = l - i;
2016 	bstr__memcpy (s->buff->data, b + i, l - i);
2017 	r->data[r->slen] = (unsigned char) '\0';
2018 	return BSTR_OK;
2019 }
2020 
2021 /*  int bsreadlnsa (bstring r, struct bStream * s, bstring term)
2022  *
2023  *  Read a bstring terminated by any character in the term string or the end
2024  *  of the stream from the bStream (s) and return it into the parameter r.
2025  *  This function may read additional characters from the core stream that
2026  *  are not returned, but will be retained for subsequent read operations.
2027  */
bsreadlnsa(bstring r,struct bStream * s,const_bstring term)2028 int bsreadlnsa (bstring r, struct bStream * s, const_bstring term) {
2029 int i, l, ret, rlo;
2030 unsigned char * b;
2031 struct tagbstring x;
2032 struct charField cf;
2033 
2034 	if (s == NULL || s->buff == NULL || r == NULL || term == NULL ||
2035 	    term->data == NULL || r->mlen <= 0 || r->slen < 0 ||
2036 	    r->mlen < r->slen) return BSTR_ERR;
2037 	if (term->slen == 1) return bsreadlna (r, s, term->data[0]);
2038 	if (term->slen < 1 || buildCharField (&cf, term)) return BSTR_ERR;
2039 
2040 	l = s->buff->slen;
2041 	if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR;
2042 	b = (unsigned char *) s->buff->data;
2043 	x.data = b;
2044 
2045 	/* First check if the current buffer holds the terminator */
2046 	b[l] = term->data[0]; /* Set sentinel */
2047 	for (i=0; !testInCharField (&cf, b[i]); i++) ;
2048 	if (i < l) {
2049 		x.slen = i + 1;
2050 		ret = bconcat (r, &x);
2051 		s->buff->slen = l;
2052 		if (BSTR_OK == ret) bdelete (s->buff, 0, i + 1);
2053 		return BSTR_OK;
2054 	}
2055 
2056 	rlo = r->slen;
2057 
2058 	/* If not then just concatenate the entire buffer to the output */
2059 	x.slen = l;
2060 	if (BSTR_OK != bconcat (r, &x)) return BSTR_ERR;
2061 
2062 	/* Perform direct in-place reads into the destination to allow for
2063 	   the minimum of data-copies */
2064 	for (;;) {
2065 		if (BSTR_OK != balloc (r, r->slen + s->maxBuffSz + 1)) return BSTR_ERR;
2066 		b = (unsigned char *) (r->data + r->slen);
2067 		l = (int) s->readFnPtr (b, 1, s->maxBuffSz, s->parm);
2068 		if (l <= 0) {
2069 			r->data[r->slen] = (unsigned char) '\0';
2070 			s->buff->slen = 0;
2071 			s->isEOF = 1;
2072 			/* If nothing was read return with an error message */
2073 			return BSTR_ERR & -(r->slen == rlo);
2074 		}
2075 
2076 		b[l] = term->data[0]; /* Set sentinel */
2077 		for (i=0; !testInCharField (&cf, b[i]); i++) ;
2078 		if (i < l) break;
2079 		r->slen += l;
2080 	}
2081 
2082 	/* Terminator found, push over-read back to buffer */
2083 	i++;
2084 	r->slen += i;
2085 	s->buff->slen = l - i;
2086 	bstr__memcpy (s->buff->data, b + i, l - i);
2087 	r->data[r->slen] = (unsigned char) '\0';
2088 	return BSTR_OK;
2089 }
2090 
2091 /*  int bsreada (bstring r, struct bStream * s, int n)
2092  *
2093  *  Read a bstring of length n (or, if it is fewer, as many bytes as is
2094  *  remaining) from the bStream.  This function may read additional
2095  *  characters from the core stream that are not returned, but will be
2096  *  retained for subsequent read operations.  This function will not read
2097  *  additional characters from the core stream beyond virtual stream pointer.
2098  */
bsreada(bstring r,struct bStream * s,int n)2099 int bsreada (bstring r, struct bStream * s, int n) {
2100 int l, ret, orslen;
2101 char * b;
2102 struct tagbstring x;
2103 
2104 	if (s == NULL || s->buff == NULL || r == NULL || r->mlen <= 0
2105 	 || r->slen < 0 || r->mlen < r->slen || n <= 0) return BSTR_ERR;
2106 
2107 	n += r->slen;
2108 	if (n <= 0) return BSTR_ERR;
2109 
2110 	l = s->buff->slen;
2111 
2112 	orslen = r->slen;
2113 
2114 	if (0 == l) {
2115 		if (s->isEOF) return BSTR_ERR;
2116 		if (r->mlen > n) {
2117 			l = (int) s->readFnPtr (r->data + r->slen, 1, n - r->slen, s->parm);
2118 			if (0 >= l || l > n - r->slen) {
2119 				s->isEOF = 1;
2120 				return BSTR_ERR;
2121 			}
2122 			r->slen += l;
2123 			r->data[r->slen] = (unsigned char) '\0';
2124 			return 0;
2125 		}
2126 	}
2127 
2128 	if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR;
2129 	b = (char *) s->buff->data;
2130 	x.data = (unsigned char *) b;
2131 
2132 	do {
2133 		if (l + r->slen >= n) {
2134 			x.slen = n - r->slen;
2135 			ret = bconcat (r, &x);
2136 			s->buff->slen = l;
2137 			if (BSTR_OK == ret) bdelete (s->buff, 0, x.slen);
2138 			return BSTR_ERR & -(r->slen == orslen);
2139 		}
2140 
2141 		x.slen = l;
2142 		if (BSTR_OK != bconcat (r, &x)) break;
2143 
2144 		l = n - r->slen;
2145 		if (l > s->maxBuffSz) l = s->maxBuffSz;
2146 
2147 		l = (int) s->readFnPtr (b, 1, l, s->parm);
2148 
2149 	} while (l > 0);
2150 	if (l < 0) l = 0;
2151 	if (l == 0) s->isEOF = 1;
2152 	s->buff->slen = l;
2153 	return BSTR_ERR & -(r->slen == orslen);
2154 }
2155 
2156 /*  int bsreadln (bstring r, struct bStream * s, char terminator)
2157  *
2158  *  Read a bstring terminated by the terminator character or the end of the
2159  *  stream from the bStream (s) and return it into the parameter r.  This
2160  *  function may read additional characters from the core stream that are not
2161  *  returned, but will be retained for subsequent read operations.
2162  */
bsreadln(bstring r,struct bStream * s,char terminator)2163 int bsreadln (bstring r, struct bStream * s, char terminator) {
2164 	if (s == NULL || s->buff == NULL || r == NULL || r->mlen <= 0)
2165 		return BSTR_ERR;
2166 	if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR;
2167 	r->slen = 0;
2168 	return bsreadlna (r, s, terminator);
2169 }
2170 
2171 /*  int bsreadlns (bstring r, struct bStream * s, bstring term)
2172  *
2173  *  Read a bstring terminated by any character in the term string or the end
2174  *  of the stream from the bStream (s) and return it into the parameter r.
2175  *  This function may read additional characters from the core stream that
2176  *  are not returned, but will be retained for subsequent read operations.
2177  */
bsreadlns(bstring r,struct bStream * s,const_bstring term)2178 int bsreadlns (bstring r, struct bStream * s, const_bstring term) {
2179 	if (s == NULL || s->buff == NULL || r == NULL || term == NULL
2180 	 || term->data == NULL || r->mlen <= 0) return BSTR_ERR;
2181 	if (term->slen == 1) return bsreadln (r, s, term->data[0]);
2182 	if (term->slen < 1) return BSTR_ERR;
2183 	if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR;
2184 	r->slen = 0;
2185 	return bsreadlnsa (r, s, term);
2186 }
2187 
2188 /*  int bsread (bstring r, struct bStream * s, int n)
2189  *
2190  *  Read a bstring of length n (or, if it is fewer, as many bytes as is
2191  *  remaining) from the bStream.  This function may read additional
2192  *  characters from the core stream that are not returned, but will be
2193  *  retained for subsequent read operations.  This function will not read
2194  *  additional characters from the core stream beyond virtual stream pointer.
2195  */
bsread(bstring r,struct bStream * s,int n)2196 int bsread (bstring r, struct bStream * s, int n) {
2197 	if (s == NULL || s->buff == NULL || r == NULL || r->mlen <= 0
2198 	 || n <= 0) return BSTR_ERR;
2199 	if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR;
2200 	r->slen = 0;
2201 	return bsreada (r, s, n);
2202 }
2203 
2204 /*  int bsunread (struct bStream * s, const_bstring b)
2205  *
2206  *  Insert a bstring into the bStream at the current position.  These
2207  *  characters will be read prior to those that actually come from the core
2208  *  stream.
2209  */
bsunread(struct bStream * s,const_bstring b)2210 int bsunread (struct bStream * s, const_bstring b) {
2211 	if (s == NULL || s->buff == NULL) return BSTR_ERR;
2212 	return binsert (s->buff, 0, b, (unsigned char) '?');
2213 }
2214 
2215 /*  int bspeek (bstring r, const struct bStream * s)
2216  *
2217  *  Return the currently buffered characters from the bStream that will be
2218  *  read prior to reads from the core stream.
2219  */
bspeek(bstring r,const struct bStream * s)2220 int bspeek (bstring r, const struct bStream * s) {
2221 	if (s == NULL || s->buff == NULL) return BSTR_ERR;
2222 	return bassign (r, s->buff);
2223 }
2224 
2225 /*  bstring bjoin (const struct bstrList * bl, const_bstring sep);
2226  *
2227  *  Join the entries of a bstrList into one bstring by sequentially
2228  *  concatenating them with the sep string in between.  If there is an error
2229  *  NULL is returned, otherwise a bstring with the correct result is returned.
2230  */
bjoin(const struct bstrList * bl,const_bstring sep)2231 bstring bjoin (const struct bstrList * bl, const_bstring sep) {
2232 bstring b;
2233 int i, c, v;
2234 
2235 	if (bl == NULL || bl->qty < 0) return NULL;
2236 	if (sep != NULL && (sep->slen < 0 || sep->data == NULL)) return NULL;
2237 
2238 	for (i = 0, c = 1; i < bl->qty; i++) {
2239 		v = bl->entry[i]->slen;
2240 		if (v < 0) return NULL;	/* Invalid input */
2241 		c += v;
2242 		if (c < 0) return NULL;	/* Wrap around ?? */
2243 	}
2244 
2245 	if (sep != NULL) c += (bl->qty - 1) * sep->slen;
2246 
2247 	b = (bstring) bstr__alloc (sizeof (struct tagbstring));
2248 	if (NULL == b) return NULL; /* Out of memory */
2249 	b->data = (unsigned char *) bstr__alloc (c);
2250 	if (b->data == NULL) {
2251 		bstr__free (b);
2252 		return NULL;
2253 	}
2254 
2255 	b->mlen = c;
2256 	b->slen = c-1;
2257 
2258 	for (i = 0, c = 0; i < bl->qty; i++) {
2259 		if (i > 0 && sep != NULL) {
2260 			bstr__memcpy (b->data + c, sep->data, sep->slen);
2261 			c += sep->slen;
2262 		}
2263 		v = bl->entry[i]->slen;
2264 		bstr__memcpy (b->data + c, bl->entry[i]->data, v);
2265 		c += v;
2266 	}
2267 	b->data[c] = (unsigned char) '\0';
2268 	return b;
2269 }
2270 
2271 #define BSSSC_BUFF_LEN (256)
2272 
2273 /*  int bssplitscb (struct bStream * s, const_bstring splitStr,
2274  *	int (* cb) (void * parm, int ofs, const_bstring entry), void * parm)
2275  *
2276  *  Iterate the set of disjoint sequential substrings read from a stream
2277  *  divided by any of the characters in splitStr.  An empty splitStr causes
2278  *  the whole stream to be iterated once.
2279  *
2280  *  Note: At the point of calling the cb function, the bStream pointer is
2281  *  pointed exactly at the position right after having read the split
2282  *  character.  The cb function can act on the stream by causing the bStream
2283  *  pointer to move, and bssplitscb will continue by starting the next split
2284  *  at the position of the pointer after the return from cb.
2285  *
2286  *  However, if the cb causes the bStream s to be destroyed then the cb must
2287  *  return with a negative value, otherwise bssplitscb will continue in an
2288  *  undefined manner.
2289  */
bssplitscb(struct bStream * s,const_bstring splitStr,int (* cb)(void * parm,int ofs,const_bstring entry),void * parm)2290 int bssplitscb (struct bStream * s, const_bstring splitStr,
2291 	int (* cb) (void * parm, int ofs, const_bstring entry), void * parm) {
2292 struct charField chrs;
2293 bstring buff;
2294 int i, p, ret;
2295 
2296 	if (cb == NULL || s == NULL || s->readFnPtr == NULL
2297 	 || splitStr == NULL || splitStr->slen < 0) return BSTR_ERR;
2298 
2299 	if (NULL == (buff = bfromcstr (""))) return BSTR_ERR;
2300 
2301 	if (splitStr->slen == 0) {
2302 		while (bsreada (buff, s, BSSSC_BUFF_LEN) >= 0) ;
2303 		if ((ret = cb (parm, 0, buff)) > 0)
2304 			ret = 0;
2305 	} else {
2306 		buildCharField (&chrs, splitStr);
2307 		ret = p = i = 0;
2308 		for (;;) {
2309 			if (i >= buff->slen) {
2310 				bsreada (buff, s, BSSSC_BUFF_LEN);
2311 				if (i >= buff->slen) {
2312 					if (0 < (ret = cb (parm, p, buff))) ret = 0;
2313 					break;
2314 				}
2315 			}
2316 			if (testInCharField (&chrs, buff->data[i])) {
2317 				struct tagbstring t;
2318 				unsigned char c;
2319 
2320 				blk2tbstr (t, buff->data + i + 1, buff->slen - (i + 1));
2321 				if ((ret = bsunread (s, &t)) < 0) break;
2322 				buff->slen = i;
2323 				c = buff->data[i];
2324 				buff->data[i] = (unsigned char) '\0';
2325 				if ((ret = cb (parm, p, buff)) < 0) break;
2326 				buff->data[i] = c;
2327 				buff->slen = 0;
2328 				p += i + 1;
2329 				i = -1;
2330 			}
2331 			i++;
2332 		}
2333 	}
2334 
2335 	bdestroy (buff);
2336 	return ret;
2337 }
2338 
2339 /*  int bssplitstrcb (struct bStream * s, const_bstring splitStr,
2340  *	int (* cb) (void * parm, int ofs, const_bstring entry), void * parm)
2341  *
2342  *  Iterate the set of disjoint sequential substrings read from a stream
2343  *  divided by the entire substring splitStr.  An empty splitStr causes
2344  *  each character of the stream to be iterated.
2345  *
2346  *  Note: At the point of calling the cb function, the bStream pointer is
2347  *  pointed exactly at the position right after having read the split
2348  *  character.  The cb function can act on the stream by causing the bStream
2349  *  pointer to move, and bssplitscb will continue by starting the next split
2350  *  at the position of the pointer after the return from cb.
2351  *
2352  *  However, if the cb causes the bStream s to be destroyed then the cb must
2353  *  return with a negative value, otherwise bssplitscb will continue in an
2354  *  undefined manner.
2355  */
bssplitstrcb(struct bStream * s,const_bstring splitStr,int (* cb)(void * parm,int ofs,const_bstring entry),void * parm)2356 int bssplitstrcb (struct bStream * s, const_bstring splitStr,
2357 	int (* cb) (void * parm, int ofs, const_bstring entry), void * parm) {
2358 bstring buff;
2359 int i, p, ret;
2360 
2361 	if (cb == NULL || s == NULL || s->readFnPtr == NULL
2362 	 || splitStr == NULL || splitStr->slen < 0) return BSTR_ERR;
2363 
2364 	if (splitStr->slen == 1) return bssplitscb (s, splitStr, cb, parm);
2365 
2366 	if (NULL == (buff = bfromcstr (""))) return BSTR_ERR;
2367 
2368 	if (splitStr->slen == 0) {
2369 		for (i=0; bsreada (buff, s, BSSSC_BUFF_LEN) >= 0; i++) {
2370 			if ((ret = cb (parm, 0, buff)) < 0) {
2371 				bdestroy (buff);
2372 				return ret;
2373 			}
2374 			buff->slen = 0;
2375 		}
2376 		return BSTR_OK;
2377 	} else {
2378 		ret = p = i = 0;
2379 		for (i=p=0;;) {
2380 			if ((ret = binstr (buff, 0, splitStr)) >= 0) {
2381 				struct tagbstring t;
2382 				blk2tbstr (t, buff->data, ret);
2383 				i = ret + splitStr->slen;
2384 				if ((ret = cb (parm, p, &t)) < 0) break;
2385 				p += i;
2386 				bdelete (buff, 0, i);
2387 			} else {
2388 				bsreada (buff, s, BSSSC_BUFF_LEN);
2389 				if (bseof (s)) {
2390 					if ((ret = cb (parm, p, buff)) > 0) ret = 0;
2391 					break;
2392 				}
2393 			}
2394 		}
2395 	}
2396 
2397 	bdestroy (buff);
2398 	return ret;
2399 }
2400 
2401 /*  int bstrListCreate (void)
2402  *
2403  *  Create a bstrList.
2404  */
bstrListCreate(void)2405 struct bstrList * bstrListCreate (void) {
2406 struct bstrList * sl = (struct bstrList *) bstr__alloc (sizeof (struct bstrList));
2407 	if (sl) {
2408 		sl->entry = (bstring *) bstr__alloc (1*sizeof (bstring));
2409 		if (!sl->entry) {
2410 			bstr__free (sl);
2411 			sl = NULL;
2412 		} else {
2413 			sl->qty = 0;
2414 			sl->mlen = 1;
2415 		}
2416 	}
2417 	return sl;
2418 }
2419 
2420 /*  int bstrListDestroy (struct bstrList * sl)
2421  *
2422  *  Destroy a bstrList that has been created by bsplit, bsplits or bstrListCreate.
2423  */
bstrListDestroy(struct bstrList * sl)2424 int bstrListDestroy (struct bstrList * sl) {
2425 int i;
2426 	if (sl == NULL || sl->qty < 0) return BSTR_ERR;
2427 	for (i=0; i < sl->qty; i++) {
2428 		if (sl->entry[i]) {
2429 			bdestroy (sl->entry[i]);
2430 			sl->entry[i] = NULL;
2431 		}
2432 	}
2433 	sl->qty  = -1;
2434 	sl->mlen = -1;
2435 	bstr__free (sl->entry);
2436 	sl->entry = NULL;
2437 	bstr__free (sl);
2438 	return BSTR_OK;
2439 }
2440 
2441 /*  int bstrListAlloc (struct bstrList * sl, int msz)
2442  *
2443  *  Ensure that there is memory for at least msz number of entries for the
2444  *  list.
2445  */
bstrListAlloc(struct bstrList * sl,int msz)2446 int bstrListAlloc (struct bstrList * sl, int msz) {
2447 bstring * l;
2448 int smsz;
2449 size_t nsz;
2450 	if (!sl || msz <= 0 || !sl->entry || sl->qty < 0 || sl->mlen <= 0 || sl->qty > sl->mlen) return BSTR_ERR;
2451 	if (sl->mlen >= msz) return BSTR_OK;
2452 	smsz = snapUpSize (msz);
2453 	nsz = ((size_t) smsz) * sizeof (bstring);
2454 	if (nsz < (size_t) smsz) return BSTR_ERR;
2455 	l = (bstring *) bstr__realloc (sl->entry, nsz);
2456 	if (!l) {
2457 		smsz = msz;
2458 		nsz = ((size_t) smsz) * sizeof (bstring);
2459 		l = (bstring *) bstr__realloc (sl->entry, nsz);
2460 		if (!l) return BSTR_ERR;
2461 	}
2462 	sl->mlen = smsz;
2463 	sl->entry = l;
2464 	return BSTR_OK;
2465 }
2466 
2467 /*  int bstrListAllocMin (struct bstrList * sl, int msz)
2468  *
2469  *  Try to allocate the minimum amount of memory for the list to include at
2470  *  least msz entries or sl->qty whichever is greater.
2471  */
bstrListAllocMin(struct bstrList * sl,int msz)2472 int bstrListAllocMin (struct bstrList * sl, int msz) {
2473 bstring * l;
2474 size_t nsz;
2475 	if (!sl || msz <= 0 || !sl->entry || sl->qty < 0 || sl->mlen <= 0 || sl->qty > sl->mlen) return BSTR_ERR;
2476 	if (msz < sl->qty) msz = sl->qty;
2477 	if (sl->mlen == msz) return BSTR_OK;
2478 	nsz = ((size_t) msz) * sizeof (bstring);
2479 	if (nsz < (size_t) msz) return BSTR_ERR;
2480 	l = (bstring *) bstr__realloc (sl->entry, nsz);
2481 	if (!l) return BSTR_ERR;
2482 	sl->mlen = msz;
2483 	sl->entry = l;
2484 	return BSTR_OK;
2485 }
2486 
2487 /*  int bsplitcb (const_bstring str, unsigned char splitChar, int pos,
2488  *	int (* cb) (void * parm, int ofs, int len), void * parm)
2489  *
2490  *  Iterate the set of disjoint sequential substrings over str divided by the
2491  *  character in splitChar.
2492  *
2493  *  Note: Non-destructive modification of str from within the cb function
2494  *  while performing this split is not undefined.  bsplitcb behaves in
2495  *  sequential lock step with calls to cb.  I.e., after returning from a cb
2496  *  that return a non-negative integer, bsplitcb continues from the position
2497  *  1 character after the last detected split character and it will halt
2498  *  immediately if the length of str falls below this point.  However, if the
2499  *  cb function destroys str, then it *must* return with a negative value,
2500  *  otherwise bsplitcb will continue in an undefined manner.
2501  */
bsplitcb(const_bstring str,unsigned char splitChar,int pos,int (* cb)(void * parm,int ofs,int len),void * parm)2502 int bsplitcb (const_bstring str, unsigned char splitChar, int pos,
2503 	int (* cb) (void * parm, int ofs, int len), void * parm) {
2504 int i, p, ret;
2505 
2506 	if (cb == NULL || str == NULL || pos < 0 || pos > str->slen)
2507 		return BSTR_ERR;
2508 
2509 	p = pos;
2510 	do {
2511 		for (i=p; i < str->slen; i++) {
2512 			if (str->data[i] == splitChar) break;
2513 		}
2514 		if ((ret = cb (parm, p, i - p)) < 0) return ret;
2515 		p = i + 1;
2516 	} while (p <= str->slen);
2517 	return BSTR_OK;
2518 }
2519 
2520 /*  int bsplitscb (const_bstring str, const_bstring splitStr, int pos,
2521  *	int (* cb) (void * parm, int ofs, int len), void * parm)
2522  *
2523  *  Iterate the set of disjoint sequential substrings over str divided by any
2524  *  of the characters in splitStr.  An empty splitStr causes the whole str to
2525  *  be iterated once.
2526  *
2527  *  Note: Non-destructive modification of str from within the cb function
2528  *  while performing this split is not undefined.  bsplitscb behaves in
2529  *  sequential lock step with calls to cb.  I.e., after returning from a cb
2530  *  that return a non-negative integer, bsplitscb continues from the position
2531  *  1 character after the last detected split character and it will halt
2532  *  immediately if the length of str falls below this point.  However, if the
2533  *  cb function destroys str, then it *must* return with a negative value,
2534  *  otherwise bsplitscb will continue in an undefined manner.
2535  */
bsplitscb(const_bstring str,const_bstring splitStr,int pos,int (* cb)(void * parm,int ofs,int len),void * parm)2536 int bsplitscb (const_bstring str, const_bstring splitStr, int pos,
2537 	int (* cb) (void * parm, int ofs, int len), void * parm) {
2538 struct charField chrs;
2539 int i, p, ret;
2540 
2541 	if (cb == NULL || str == NULL || pos < 0 || pos > str->slen
2542 	 || splitStr == NULL || splitStr->slen < 0) return BSTR_ERR;
2543 	if (splitStr->slen == 0) {
2544 		if ((ret = cb (parm, 0, str->slen)) > 0) ret = 0;
2545 		return ret;
2546 	}
2547 
2548 	if (splitStr->slen == 1)
2549 		return bsplitcb (str, splitStr->data[0], pos, cb, parm);
2550 
2551 	buildCharField (&chrs, splitStr);
2552 
2553 	p = pos;
2554 	do {
2555 		for (i=p; i < str->slen; i++) {
2556 			if (testInCharField (&chrs, str->data[i])) break;
2557 		}
2558 		if ((ret = cb (parm, p, i - p)) < 0) return ret;
2559 		p = i + 1;
2560 	} while (p <= str->slen);
2561 	return BSTR_OK;
2562 }
2563 
2564 /*  int bsplitstrcb (const_bstring str, const_bstring splitStr, int pos,
2565  *	int (* cb) (void * parm, int ofs, int len), void * parm)
2566  *
2567  *  Iterate the set of disjoint sequential substrings over str divided by the
2568  *  substring splitStr.  An empty splitStr causes the whole str to be
2569  *  iterated once.
2570  *
2571  *  Note: Non-destructive modification of str from within the cb function
2572  *  while performing this split is not undefined.  bsplitstrcb behaves in
2573  *  sequential lock step with calls to cb.  I.e., after returning from a cb
2574  *  that return a non-negative integer, bsplitscb continues from the position
2575  *  1 character after the last detected split character and it will halt
2576  *  immediately if the length of str falls below this point.  However, if the
2577  *  cb function destroys str, then it *must* return with a negative value,
2578  *  otherwise bsplitscb will continue in an undefined manner.
2579  */
bsplitstrcb(const_bstring str,const_bstring splitStr,int pos,int (* cb)(void * parm,int ofs,int len),void * parm)2580 int bsplitstrcb (const_bstring str, const_bstring splitStr, int pos,
2581 	int (* cb) (void * parm, int ofs, int len), void * parm) {
2582 int i, p, ret;
2583 
2584 	if (cb == NULL || str == NULL || pos < 0 || pos > str->slen
2585 	 || splitStr == NULL || splitStr->slen < 0) return BSTR_ERR;
2586 
2587 	if (0 == splitStr->slen) {
2588 		for (i=pos; i < str->slen; i++) {
2589 			if ((ret = cb (parm, i, 1)) < 0) return ret;
2590 		}
2591 		return BSTR_OK;
2592 	}
2593 
2594 	if (splitStr->slen == 1)
2595 		return bsplitcb (str, splitStr->data[0], pos, cb, parm);
2596 
2597 	for (i=p=pos; i <= str->slen - splitStr->slen; i++) {
2598 		if (0 == bstr__memcmp (splitStr->data, str->data + i, splitStr->slen)) {
2599 			if ((ret = cb (parm, p, i - p)) < 0) return ret;
2600 			i += splitStr->slen;
2601 			p = i;
2602 		}
2603 	}
2604 	if ((ret = cb (parm, p, str->slen - p)) < 0) return ret;
2605 	return BSTR_OK;
2606 }
2607 
2608 struct genBstrList {
2609 	bstring b;
2610 	struct bstrList * bl;
2611 };
2612 
bscb(void * parm,int ofs,int len)2613 static int bscb (void * parm, int ofs, int len) {
2614 struct genBstrList * g = (struct genBstrList *) parm;
2615 	if (g->bl->qty >= g->bl->mlen) {
2616 		int mlen = g->bl->mlen * 2;
2617 		bstring * tbl;
2618 
2619 		while (g->bl->qty >= mlen) {
2620 			if (mlen < g->bl->mlen) return BSTR_ERR;
2621 			mlen += mlen;
2622 		}
2623 
2624 		tbl = (bstring *) bstr__realloc (g->bl->entry, sizeof (bstring) * mlen);
2625 		if (tbl == NULL) return BSTR_ERR;
2626 
2627 		g->bl->entry = tbl;
2628 		g->bl->mlen = mlen;
2629 	}
2630 
2631 	g->bl->entry[g->bl->qty] = bmidstr (g->b, ofs, len);
2632 	g->bl->qty++;
2633 	return BSTR_OK;
2634 }
2635 
2636 /*  struct bstrList * bsplit (const_bstring str, unsigned char splitChar)
2637  *
2638  *  Create an array of sequential substrings from str divided by the character
2639  *  splitChar.
2640  */
bsplit(const_bstring str,unsigned char splitChar)2641 struct bstrList * bsplit (const_bstring str, unsigned char splitChar) {
2642 struct genBstrList g;
2643 
2644 	if (str == NULL || str->data == NULL || str->slen < 0) return NULL;
2645 
2646 	g.bl = (struct bstrList *) bstr__alloc (sizeof (struct bstrList));
2647 	if (g.bl == NULL) return NULL;
2648 	g.bl->mlen = 4;
2649 	g.bl->entry = (bstring *) bstr__alloc (g.bl->mlen * sizeof (bstring));
2650 	if (NULL == g.bl->entry) {
2651 		bstr__free (g.bl);
2652 		return NULL;
2653 	}
2654 
2655 	g.b = (bstring) str;
2656 	g.bl->qty = 0;
2657 	if (bsplitcb (str, splitChar, 0, bscb, &g) < 0) {
2658 		bstrListDestroy (g.bl);
2659 		return NULL;
2660 	}
2661 	return g.bl;
2662 }
2663 
2664 /*  struct bstrList * bsplitstr (const_bstring str, const_bstring splitStr)
2665  *
2666  *  Create an array of sequential substrings from str divided by the entire
2667  *  substring splitStr.
2668  */
bsplitstr(const_bstring str,const_bstring splitStr)2669 struct bstrList * bsplitstr (const_bstring str, const_bstring splitStr) {
2670 struct genBstrList g;
2671 
2672 	if (str == NULL || str->data == NULL || str->slen < 0) return NULL;
2673 
2674 	g.bl = (struct bstrList *) bstr__alloc (sizeof (struct bstrList));
2675 	if (g.bl == NULL) return NULL;
2676 	g.bl->mlen = 4;
2677 	g.bl->entry = (bstring *) bstr__alloc (g.bl->mlen * sizeof (bstring));
2678 	if (NULL == g.bl->entry) {
2679 		bstr__free (g.bl);
2680 		return NULL;
2681 	}
2682 
2683 	g.b = (bstring) str;
2684 	g.bl->qty = 0;
2685 	if (bsplitstrcb (str, splitStr, 0, bscb, &g) < 0) {
2686 		bstrListDestroy (g.bl);
2687 		return NULL;
2688 	}
2689 	return g.bl;
2690 }
2691 
2692 /*  struct bstrList * bsplits (const_bstring str, bstring splitStr)
2693  *
2694  *  Create an array of sequential substrings from str divided by any of the
2695  *  characters in splitStr.  An empty splitStr causes a single entry bstrList
2696  *  containing a copy of str to be returned.
2697  */
bsplits(const_bstring str,const_bstring splitStr)2698 struct bstrList * bsplits (const_bstring str, const_bstring splitStr) {
2699 struct genBstrList g;
2700 
2701 	if (     str == NULL ||      str->slen < 0 ||      str->data == NULL ||
2702 	    splitStr == NULL || splitStr->slen < 0 || splitStr->data == NULL)
2703 		return NULL;
2704 
2705 	g.bl = (struct bstrList *) bstr__alloc (sizeof (struct bstrList));
2706 	if (g.bl == NULL) return NULL;
2707 	g.bl->mlen = 4;
2708 	g.bl->entry = (bstring *) bstr__alloc (g.bl->mlen * sizeof (bstring));
2709 	if (NULL == g.bl->entry) {
2710 		bstr__free (g.bl);
2711 		return NULL;
2712 	}
2713 	g.b = (bstring) str;
2714 	g.bl->qty = 0;
2715 
2716 	if (bsplitscb (str, splitStr, 0, bscb, &g) < 0) {
2717 		bstrListDestroy (g.bl);
2718 		return NULL;
2719 	}
2720 	return g.bl;
2721 }
2722 
2723 #if defined (__TURBOC__) && !defined (__BORLANDC__)
2724 # ifndef BSTRLIB_NOVSNP
2725 #  define BSTRLIB_NOVSNP
2726 # endif
2727 #endif
2728 
2729 /* Give WATCOM C/C++, MSVC some latitude for their non-support of vsnprintf */
2730 #if defined(__WATCOMC__) || defined(_MSC_VER)
2731 #define exvsnprintf(r,b,n,f,a) {r = _vsnprintf (b,n,f,a);}
2732 #else
2733 #ifdef BSTRLIB_NOVSNP
2734 /* This is just a hack.  If you are using a system without a vsnprintf, it is
2735    not recommended that bformat be used at all. */
2736 #define exvsnprintf(r,b,n,f,a) {vsprintf (b,f,a); r = -1;}
2737 #define START_VSNBUFF (256)
2738 #else
2739 
2740 #ifdef __GNUC__
2741 /* Something is making gcc complain about this prototype not being here, so
2742    I've just gone ahead and put it in. */
2743 extern int vsnprintf (char *buf, size_t count, const char *format, va_list arg);
2744 #endif
2745 
2746 #define exvsnprintf(r,b,n,f,a) {r = vsnprintf (b,n,f,a);}
2747 #endif
2748 #endif
2749 
2750 #if !defined (BSTRLIB_NOVSNP)
2751 
2752 #ifndef START_VSNBUFF
2753 #define START_VSNBUFF (16)
2754 #endif
2755 
2756 /* On IRIX vsnprintf returns n-1 when the operation would overflow the target
2757    buffer, WATCOM and MSVC both return -1, while C99 requires that the
2758    returned value be exactly what the length would be if the buffer would be
2759    large enough.  This leads to the idea that if the return value is larger
2760    than n, then changing n to the return value will reduce the number of
2761    iterations required. */
2762 
2763 /*  int bformata (bstring b, const char * fmt, ...)
2764  *
2765  *  After the first parameter, it takes the same parameters as printf (), but
2766  *  rather than outputting results to stdio, it appends the results to
2767  *  a bstring which contains what would have been output. Note that if there
2768  *  is an early generation of a '\0' character, the bstring will be truncated
2769  *  to this end point.
2770  */
bformata(bstring b,const char * fmt,...)2771 int bformata (bstring b, const char * fmt, ...) {
2772 va_list arglist;
2773 bstring buff;
2774 int n, r;
2775 
2776 	if (b == NULL || fmt == NULL || b->data == NULL || b->mlen <= 0
2777 	 || b->slen < 0 || b->slen > b->mlen) return BSTR_ERR;
2778 
2779 	/* Since the length is not determinable beforehand, a search is
2780 	   performed using the truncating "vsnprintf" call (to avoid buffer
2781 	   overflows) on increasing potential sizes for the output result. */
2782 
2783 	if ((n = (int) (2*strlen (fmt))) < START_VSNBUFF) n = START_VSNBUFF;
2784 	if (NULL == (buff = bfromcstralloc (n + 2, ""))) {
2785 		n = 1;
2786 		if (NULL == (buff = bfromcstralloc (n + 2, ""))) return BSTR_ERR;
2787 	}
2788 
2789 	for (;;) {
2790 		va_start (arglist, fmt);
2791 		exvsnprintf (r, (char *) buff->data, n + 1, fmt, arglist);
2792 		va_end (arglist);
2793 
2794 		buff->data[n] = (unsigned char) '\0';
2795 		buff->slen = (int) (strlen) ((char *) buff->data);
2796 
2797 		if (buff->slen < n) break;
2798 
2799 		if (r > n) n = r; else n += n;
2800 
2801 		if (BSTR_OK != balloc (buff, n + 2)) {
2802 			bdestroy (buff);
2803 			return BSTR_ERR;
2804 		}
2805 	}
2806 
2807 	r = bconcat (b, buff);
2808 	bdestroy (buff);
2809 	return r;
2810 }
2811 
2812 /*  int bassignformat (bstring b, const char * fmt, ...)
2813  *
2814  *  After the first parameter, it takes the same parameters as printf (), but
2815  *  rather than outputting results to stdio, it outputs the results to
2816  *  the bstring parameter b. Note that if there is an early generation of a
2817  *  '\0' character, the bstring will be truncated to this end point.
2818  */
bassignformat(bstring b,const char * fmt,...)2819 int bassignformat (bstring b, const char * fmt, ...) {
2820 va_list arglist;
2821 bstring buff;
2822 int n, r;
2823 
2824 	if (b == NULL || fmt == NULL || b->data == NULL || b->mlen <= 0
2825 	 || b->slen < 0 || b->slen > b->mlen) return BSTR_ERR;
2826 
2827 	/* Since the length is not determinable beforehand, a search is
2828 	   performed using the truncating "vsnprintf" call (to avoid buffer
2829 	   overflows) on increasing potential sizes for the output result. */
2830 
2831 	if ((n = (int) (2*strlen (fmt))) < START_VSNBUFF) n = START_VSNBUFF;
2832 	if (NULL == (buff = bfromcstralloc (n + 2, ""))) {
2833 		n = 1;
2834 		if (NULL == (buff = bfromcstralloc (n + 2, ""))) return BSTR_ERR;
2835 	}
2836 
2837 	for (;;) {
2838 		va_start (arglist, fmt);
2839 		exvsnprintf (r, (char *) buff->data, n + 1, fmt, arglist);
2840 		va_end (arglist);
2841 
2842 		buff->data[n] = (unsigned char) '\0';
2843 		buff->slen = (int) (strlen) ((char *) buff->data);
2844 
2845 		if (buff->slen < n) break;
2846 
2847 		if (r > n) n = r; else n += n;
2848 
2849 		if (BSTR_OK != balloc (buff, n + 2)) {
2850 			bdestroy (buff);
2851 			return BSTR_ERR;
2852 		}
2853 	}
2854 
2855 	r = bassign (b, buff);
2856 	bdestroy (buff);
2857 	return r;
2858 }
2859 
2860 /*  bstring bformat (const char * fmt, ...)
2861  *
2862  *  Takes the same parameters as printf (), but rather than outputting results
2863  *  to stdio, it forms a bstring which contains what would have been output.
2864  *  Note that if there is an early generation of a '\0' character, the
2865  *  bstring will be truncated to this end point.
2866  */
bformat(const char * fmt,...)2867 bstring bformat (const char * fmt, ...) {
2868 va_list arglist;
2869 bstring buff;
2870 int n, r;
2871 
2872 	if (fmt == NULL) return NULL;
2873 
2874 	/* Since the length is not determinable beforehand, a search is
2875 	   performed using the truncating "vsnprintf" call (to avoid buffer
2876 	   overflows) on increasing potential sizes for the output result. */
2877 
2878 	if ((n = (int) (2*strlen (fmt))) < START_VSNBUFF) n = START_VSNBUFF;
2879 	if (NULL == (buff = bfromcstralloc (n + 2, ""))) {
2880 		n = 1;
2881 		if (NULL == (buff = bfromcstralloc (n + 2, ""))) return NULL;
2882 	}
2883 
2884 	for (;;) {
2885 		va_start (arglist, fmt);
2886 		exvsnprintf (r, (char *) buff->data, n + 1, fmt, arglist);
2887 		va_end (arglist);
2888 
2889 		buff->data[n] = (unsigned char) '\0';
2890 		buff->slen = (int) (strlen) ((char *) buff->data);
2891 
2892 		if (buff->slen < n) break;
2893 
2894 		if (r > n) n = r; else n += n;
2895 
2896 		if (BSTR_OK != balloc (buff, n + 2)) {
2897 			bdestroy (buff);
2898 			return NULL;
2899 		}
2900 	}
2901 
2902 	return buff;
2903 }
2904 
2905 /*  int bvcformata (bstring b, int count, const char * fmt, va_list arglist)
2906  *
2907  *  The bvcformata function formats data under control of the format control
2908  *  string fmt and attempts to append the result to b.  The fmt parameter is
2909  *  the same as that of the printf function.  The variable argument list is
2910  *  replaced with arglist, which has been initialized by the va_start macro.
2911  *  The size of the output is upper bounded by count.  If the required output
2912  *  exceeds count, the string b is not augmented with any contents and a value
2913  *  below BSTR_ERR is returned.  If a value below -count is returned then it
2914  *  is recommended that the negative of this value be used as an update to the
2915  *  count in a subsequent pass.  On other errors, such as running out of
2916  *  memory, parameter errors or numeric wrap around BSTR_ERR is returned.
2917  *  BSTR_OK is returned when the output is successfully generated and
2918  *  appended to b.
2919  *
2920  *  Note: There is no sanity checking of arglist, and this function is
2921  *  destructive of the contents of b from the b->slen point onward.  If there
2922  *  is an early generation of a '\0' character, the bstring will be truncated
2923  *  to this end point.
2924  */
bvcformata(bstring b,int count,const char * fmt,va_list arg)2925 int bvcformata (bstring b, int count, const char * fmt, va_list arg) {
2926 int n, r, l;
2927 
2928 	if (b == NULL || fmt == NULL || count <= 0 || b->data == NULL
2929 	 || b->mlen <= 0 || b->slen < 0 || b->slen > b->mlen) return BSTR_ERR;
2930 
2931 	if (count > (n = b->slen + count) + 2) return BSTR_ERR;
2932 	if (BSTR_OK != balloc (b, n + 2)) return BSTR_ERR;
2933 
2934 	exvsnprintf (r, (char *) b->data + b->slen, count + 2, fmt, arg);
2935 
2936 	/* Did the operation complete successfully within bounds? */
2937 
2938 	if (n >= (l = b->slen + (int) (strlen) ((const char *) b->data + b->slen))) {
2939 		b->slen = l;
2940 		return BSTR_OK;
2941 	}
2942 
2943 	/* Abort, since the buffer was not large enough.  The return value
2944 	   tries to help set what the retry length should be. */
2945 
2946 	b->data[b->slen] = '\0';
2947 	if (r > count+1) l = r; else {
2948 		l = count+count;
2949 		if (count > l) l = INT_MAX;
2950 	}
2951 	n = -l;
2952 	if (n > BSTR_ERR-1) n = BSTR_ERR-1;
2953 	return n;
2954 }
2955 
2956 #endif
2957