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