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