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