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