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