* This source file has had its exported symbols prefixed with _al_ or _AL_
* for the Allegro project.
*/
* This source file is part of the _al_bstring string library. This code was
* written by Paul Hsieh in 2002-2008, and is covered by the BSD open source
* license and the GPL. Refer to the accompanying documentation for details
* on usage and license.
*/
* bstrlib.c
*
* This file is the core module for implementing the _al_bstring functions.
*/
#include <stdio.h>
#include <stddef.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "allegro5/allegro.h"
#include "allegro5/internal/bstrlib.h"
#define bstr__alloc(x) al_malloc(x)
#define bstr__free(p) al_free(p)
#define bstr__realloc(p, x) al_realloc((p), (x))
#if defined(MEMORY_DEBUG) || defined(BSTRLIB_MEMORY_DEBUG)
#include "memdbg.h"
#endif
#ifndef bstr__alloc
#define bstr__alloc(x) malloc (x)
#endif
#ifndef bstr__free
#define bstr__free(p) free (p)
#endif
#ifndef bstr__realloc
#define bstr__realloc(p,x) realloc ((p), (x))
#endif
#ifndef bstr__memcpy
#define bstr__memcpy(d,s,l) memcpy ((d), (s), (l))
#endif
#ifndef bstr__memmove
#define bstr__memmove(d,s,l) memmove ((d), (s), (l))
#endif
#ifndef bstr__memset
#define bstr__memset(d,c,l) memset ((d), (c), (l))
#endif
#ifndef bstr__memcmp
#define bstr__memcmp(d,c,l) memcmp ((d), (c), (l))
#endif
#ifndef bstr__memchr
#define bstr__memchr(s,c,l) memchr ((s), (c), (l))
#endif
#define bBlockCopy(D,S,L) { if ((L) > 0) bstr__memmove ((D),(S),(L)); }
of 2 like this, repeated reallocations are avoided. */
static int snapUpSize (int i) {
if (i < 8) {
i = 8;
} else {
unsigned int j;
j = (unsigned int) i;
j |= (j >> 1);
j |= (j >> 2);
j |= (j >> 4);
j |= (j >> 8);
#if (UINT_MAX != 0xffff)
j |= (j >> 16);
#if (UINT_MAX > 0xffffffffUL)
j |= (j >> 32);
#endif
#endif
j++;
if ((int) j >= i) i = (int) j;
}
return i;
}
*
* Increase the size of the memory backing the _al_bstring b to at least len.
*/
int _al_balloc (_al_bstring b, int olen) {
int len;
if (b == NULL || b->data == NULL || b->slen < 0 || b->mlen <= 0 ||
b->mlen < b->slen || olen <= 0) {
return _AL_BSTR_ERR;
}
if (olen >= b->mlen) {
unsigned char * x;
if ((len = snapUpSize (olen)) <= b->mlen) return _AL_BSTR_OK;
if (7 * b->mlen < 8 * b->slen) {
the memory defragmentation */
reallocStrategy:;
x = (unsigned char *) bstr__realloc (b->data, (size_t) len);
if (x == NULL) {
allocation */
if (NULL == (x = (unsigned char *) bstr__realloc (b->data, (size_t) (len = olen)))) {
return _AL_BSTR_ERR;
}
}
} else {
the extra bytes that are allocated, but not considered part of
the string */
if (NULL == (x = (unsigned char *) bstr__alloc ((size_t) len))) {
allocations to be in memory at once */
goto reallocStrategy;
} else {
if (b->slen) bstr__memcpy ((char *) x, (char *) b->data, (size_t) b->slen);
bstr__free (b->data);
}
}
b->data = x;
b->mlen = len;
b->data[b->slen] = (unsigned char) '\0';
}
return _AL_BSTR_OK;
}
*
* Set the size of the memory backing the _al_bstring b to len or b->slen+1,
* whichever is larger. Note that repeated use of this function can degrade
* performance.
*/
int _al_ballocmin (_al_bstring b, int len) {
unsigned char * s;
if (b == NULL || b->data == NULL || (b->slen+1) < 0 || b->mlen <= 0 ||
b->mlen < b->slen || len <= 0) {
return _AL_BSTR_ERR;
}
if (len < b->slen + 1) len = b->slen + 1;
if (len != b->mlen) {
s = (unsigned char *) bstr__realloc (b->data, (size_t) len);
if (NULL == s) return _AL_BSTR_ERR;
s[b->slen] = (unsigned char) '\0';
b->data = s;
b->mlen = len;
}
return _AL_BSTR_OK;
}
*
* Create a _al_bstring which contains the contents of the '\0' terminated char *
* buffer str.
*/
_al_bstring _al_bfromcstr (const char * str) {
_al_bstring b;
int i;
size_t j;
if (str == NULL) return NULL;
j = (strlen) (str);
i = snapUpSize ((int) (j + (2 - (j != 0))));
if (i <= (int) j) return NULL;
b = (_al_bstring) bstr__alloc (sizeof (struct _al_tagbstring));
if (NULL == b) return NULL;
b->slen = (int) j;
if (NULL == (b->data = (unsigned char *) bstr__alloc (b->mlen = i))) {
bstr__free (b);
return NULL;
}
bstr__memcpy (b->data, str, j+1);
return b;
}
*
* Create a _al_bstring which contains the contents of the '\0' terminated char *
* buffer str. The memory buffer backing the string is at least len
* characters in length.
*/
_al_bstring _al_bfromcstralloc (int mlen, const char * str) {
_al_bstring b;
int i;
size_t j;
if (str == NULL) return NULL;
j = (strlen) (str);
i = snapUpSize ((int) (j + (2 - (j != 0))));
if (i <= (int) j) return NULL;
b = (_al_bstring) bstr__alloc (sizeof (struct _al_tagbstring));
if (b == NULL) return NULL;
b->slen = (int) j;
if (i < mlen) i = mlen;
if (NULL == (b->data = (unsigned char *) bstr__alloc (b->mlen = i))) {
bstr__free (b);
return NULL;
}
bstr__memcpy (b->data, str, j+1);
return b;
}
*
* Create a _al_bstring which contains the content of the block blk of length
* len.
*/
_al_bstring _al_blk2bstr (const void * blk, int len) {
_al_bstring b;
int i;
if (blk == NULL || len < 0) return NULL;
b = (_al_bstring) bstr__alloc (sizeof (struct _al_tagbstring));
if (b == NULL) return NULL;
b->slen = len;
i = len + (2 - (len != 0));
i = snapUpSize (i);
b->mlen = i;
b->data = (unsigned char *) bstr__alloc ((size_t) b->mlen);
if (b->data == NULL) {
bstr__free (b);
return NULL;
}
if (len > 0) bstr__memcpy (b->data, blk, (size_t) len);
b->data[len] = (unsigned char) '\0';
return b;
}
*
* Create a '\0' terminated char * buffer which is equal to the contents of
* the _al_bstring s, except that any contained '\0' characters are converted
* to the character in z. This returned value should be freed with a
* _al_bcstrfree () call, by the calling application.
*/
char * _al_bstr2cstr (_al_const_bstring b, char z) {
int i, l;
char * r;
if (b == NULL || b->slen < 0 || b->data == NULL) return NULL;
l = b->slen;
r = (char *) bstr__alloc ((size_t) (l + 1));
if (r == NULL) return r;
for (i=0; i < l; i ++) {
r[i] = (char) ((b->data[i] == '\0') ? z : (char) (b->data[i]));
}
r[l] = (unsigned char) '\0';
return r;
}
*
* Frees a C-string generated by _al_bstr2cstr (). This is normally unnecessary
* since it just wraps a call to bstr__free (), however, if bstr__alloc ()
* and bstr__free () have been redefined as a macros within the bstrlib
* module (via defining them in memdbg.h after defining
* BSTRLIB_MEMORY_DEBUG) with some difference in behaviour from the std
* library functions, then this allows a correct way of freeing the memory
* that allows higher level code to be independent from these macro
* redefinitions.
*/
int _al_bcstrfree (char * s) {
if (s) {
bstr__free (s);
return _AL_BSTR_OK;
}
return _AL_BSTR_ERR;
}
*
* Concatenate the _al_bstring b1 to the _al_bstring b0.
*/
int _al_bconcat (_al_bstring b0, _al_const_bstring b1) {
int len, d;
_al_bstring aux = (_al_bstring) b1;
if (b0 == NULL || b1 == NULL || b0->data == NULL || b1->data == NULL) return _AL_BSTR_ERR;
d = b0->slen;
len = b1->slen;
if ((d | (b0->mlen - d) | len | (d + len)) < 0) return _AL_BSTR_ERR;
if (b0->mlen <= d + len + 1) {
ptrdiff_t pd = b1->data - b0->data;
if (0 <= pd && pd < b0->mlen) {
if (NULL == (aux = _al_bstrcpy (b1))) return _AL_BSTR_ERR;
}
if (_al_balloc (b0, d + len + 1) != _AL_BSTR_OK) {
if (aux != b1) _al_bdestroy (aux);
return _AL_BSTR_ERR;
}
}
bBlockCopy (&b0->data[d], &aux->data[0], (size_t) len);
b0->data[d + len] = (unsigned char) '\0';
b0->slen = d + len;
if (aux != b1) _al_bdestroy (aux);
return _AL_BSTR_OK;
}
/ *
* Concatenate the single character c to the _al_bstring b.
*/
int _al_bconchar (_al_bstring b, char c) {
int d;
if (b == NULL) return _AL_BSTR_ERR;
d = b->slen;
if ((d | (b->mlen - d)) < 0 || _al_balloc (b, d + 2) != _AL_BSTR_OK) return _AL_BSTR_ERR;
b->data[d] = (unsigned char) c;
b->data[d + 1] = (unsigned char) '\0';
b->slen++;
return _AL_BSTR_OK;
}
*
* Concatenate a char * string to a _al_bstring.
*/
int _al_bcatcstr (_al_bstring b, const char * s) {
char * d;
int i, l;
if (b == NULL || b->data == NULL || b->slen < 0 || b->mlen < b->slen
|| b->mlen <= 0 || s == NULL) return _AL_BSTR_ERR;
l = b->mlen - b->slen;
d = (char *) &b->data[b->slen];
for (i=0; i < l; i++) {
if ((*d++ = *s++) == '\0') {
b->slen += i;
return _AL_BSTR_OK;
}
}
b->slen += i;
return _al_bcatblk (b, (const void *) s, (int) strlen (s));
}
*
* Concatenate a fixed length buffer to a _al_bstring.
*/
int _al_bcatblk (_al_bstring b, const void * s, int len) {
int nl;
if (b == NULL || b->data == NULL || b->slen < 0 || b->mlen < b->slen
|| b->mlen <= 0 || s == NULL || len < 0) return _AL_BSTR_ERR;
if (0 > (nl = b->slen + len)) return _AL_BSTR_ERR;
if (b->mlen <= nl && 0 > _al_balloc (b, nl + 1)) return _AL_BSTR_ERR;
bBlockCopy (&b->data[b->slen], s, (size_t) len);
b->slen = nl;
b->data[nl] = (unsigned char) '\0';
return _AL_BSTR_OK;
}
*
* Create a copy of the _al_bstring b.
*/
_al_bstring _al_bstrcpy (_al_const_bstring b) {
_al_bstring b0;
int i,j;
if (b == NULL || b->slen < 0 || b->data == NULL) return NULL;
b0 = (_al_bstring) bstr__alloc (sizeof (struct _al_tagbstring));
if (b0 == NULL) {
return NULL;
}
i = b->slen;
j = snapUpSize (i + 1);
b0->data = (unsigned char *) bstr__alloc (j);
if (b0->data == NULL) {
j = i + 1;
b0->data = (unsigned char *) bstr__alloc (j);
if (b0->data == NULL) {
bstr__free (b0);
return NULL;
}
}
b0->mlen = j;
b0->slen = i;
if (i) bstr__memcpy ((char *) b0->data, (char *) b->data, i);
b0->data[b0->slen] = (unsigned char) '\0';
return b0;
}
*
* Overwrite the string a with the contents of string b.
*/
int _al_bassign (_al_bstring a, _al_const_bstring b) {
if (b == NULL || b->data == NULL || b->slen < 0)
return _AL_BSTR_ERR;
if (b->slen != 0) {
if (_al_balloc (a, b->slen) != _AL_BSTR_OK) return _AL_BSTR_ERR;
bstr__memmove (a->data, b->data, b->slen);
} else {
if (a == NULL || a->data == NULL || a->mlen < a->slen ||
a->slen < 0 || a->mlen == 0)
return _AL_BSTR_ERR;
}
a->data[b->slen] = (unsigned char) '\0';
a->slen = b->slen;
return _AL_BSTR_OK;
}
*
* Overwrite the string a with the middle of contents of string b
* starting from position left and running for a length len. left and
* len are clamped to the ends of b as with the function _al_bmidstr.
*/
int _al_bassignmidstr (_al_bstring a, _al_const_bstring b, int left, int len) {
if (b == NULL || b->data == NULL || b->slen < 0)
return _AL_BSTR_ERR;
if (left < 0) {
len += left;
left = 0;
}
if (len > b->slen - left) len = b->slen - left;
if (a == NULL || a->data == NULL || a->mlen < a->slen ||
a->slen < 0 || a->mlen == 0)
return _AL_BSTR_ERR;
if (len > 0) {
if (_al_balloc (a, len) != _AL_BSTR_OK) return _AL_BSTR_ERR;
bstr__memmove (a->data, b->data + left, len);
a->slen = len;
} else {
a->slen = 0;
}
a->data[a->slen] = (unsigned char) '\0';
return _AL_BSTR_OK;
}
*
* Overwrite the string a with the contents of char * string str. Note that
* the _al_bstring a must be a well defined and writable _al_bstring. If an error
* occurs _AL_BSTR_ERR is returned however a may be partially overwritten.
*/
int _al_bassigncstr (_al_bstring a, const char * str) {
int i;
size_t len;
if (a == NULL || a->data == NULL || a->mlen < a->slen ||
a->slen < 0 || a->mlen == 0 || NULL == str)
return _AL_BSTR_ERR;
for (i=0; i < a->mlen; i++) {
if ('\0' == (a->data[i] = str[i])) {
a->slen = i;
return _AL_BSTR_OK;
}
}
a->slen = i;
len = strlen (str + i);
if (len > INT_MAX || i + len + 1 > INT_MAX ||
0 > _al_balloc (a, (int) (i + len + 1))) return _AL_BSTR_ERR;
bBlockCopy (a->data + i, str + i, (size_t) len + 1);
a->slen += (int) len;
return _AL_BSTR_OK;
}
*
* Overwrite the string a with the contents of the block (s, len). Note that
* the _al_bstring a must be a well defined and writable _al_bstring. If an error
* occurs _AL_BSTR_ERR is returned and a is not overwritten.
*/
int _al_bassignblk (_al_bstring a, const void * s, int len) {
if (a == NULL || a->data == NULL || a->mlen < a->slen ||
a->slen < 0 || a->mlen == 0 || NULL == s || len + 1 < 1)
return _AL_BSTR_ERR;
if (len + 1 > a->mlen && 0 > _al_balloc (a, len + 1)) return _AL_BSTR_ERR;
bBlockCopy (a->data, s, (size_t) len);
a->data[len] = (unsigned char) '\0';
a->slen = len;
return _AL_BSTR_OK;
}
*
* Truncate the _al_bstring to at most n characters.
*/
int _al_btrunc (_al_bstring b, int n) {
if (n < 0 || b == NULL || b->data == NULL || b->mlen < b->slen ||
b->slen < 0 || b->mlen <= 0) return _AL_BSTR_ERR;
if (b->slen > n) {
b->slen = n;
b->data[n] = (unsigned char) '\0';
}
return _AL_BSTR_OK;
}
#define upcase(c) (toupper ((unsigned char) c))
#define downcase(c) (tolower ((unsigned char) c))
#define wspace(c) (isspace ((unsigned char) c))
*
* Convert contents of _al_bstring to upper case.
*/
int _al_btoupper (_al_bstring b) {
int i, len;
if (b == NULL || b->data == NULL || b->mlen < b->slen ||
b->slen < 0 || b->mlen <= 0) return _AL_BSTR_ERR;
for (i=0, len = b->slen; i < len; i++) {
b->data[i] = (unsigned char) upcase (b->data[i]);
}
return _AL_BSTR_OK;
}
*
* Convert contents of _al_bstring to lower case.
*/
int _al_btolower (_al_bstring b) {
int i, len;
if (b == NULL || b->data == NULL || b->mlen < b->slen ||
b->slen < 0 || b->mlen <= 0) return _AL_BSTR_ERR;
for (i=0, len = b->slen; i < len; i++) {
b->data[i] = (unsigned char) downcase (b->data[i]);
}
return _AL_BSTR_OK;
}
*
* Compare two strings without differentiating between case. The return
* value is the difference of the values of the characters where the two
* strings first differ after lower case transformation, otherwise 0 is
* returned indicating that the strings are equal. If the lengths are
* different, then a difference from 0 is given, but if the first extra
* character is '\0', then it is taken to be the value UCHAR_MAX+1.
*/
int _al_bstricmp (_al_const_bstring b0, _al_const_bstring b1) {
int i, v, n;
if (_al_bdata (b0) == NULL || b0->slen < 0 ||
_al_bdata (b1) == NULL || b1->slen < 0) return SHRT_MIN;
if ((n = b0->slen) > b1->slen) n = b1->slen;
else if (b0->slen == b1->slen && b0->data == b1->data) return _AL_BSTR_OK;
for (i = 0; i < n; i ++) {
v = (char) downcase (b0->data[i])
- (char) downcase (b1->data[i]);
if (0 != v) return v;
}
if (b0->slen > n) {
v = (char) downcase (b0->data[n]);
if (v) return v;
return UCHAR_MAX + 1;
}
if (b1->slen > n) {
v = - (char) downcase (b1->data[n]);
if (v) return v;
return - (int) (UCHAR_MAX + 1);
}
return _AL_BSTR_OK;
}
*
* Compare two strings without differentiating between case for at most n
* characters. If the position where the two strings first differ is
* before the nth position, the return value is the difference of the values
* of the characters, otherwise 0 is returned. If the lengths are different
* and less than n characters, then a difference from 0 is given, but if the
* first extra character is '\0', then it is taken to be the value
* UCHAR_MAX+1.
*/
int _al_bstrnicmp (_al_const_bstring b0, _al_const_bstring b1, int n) {
int i, v, m;
if (_al_bdata (b0) == NULL || b0->slen < 0 ||
_al_bdata (b1) == NULL || b1->slen < 0 || n < 0) return SHRT_MIN;
m = n;
if (m > b0->slen) m = b0->slen;
if (m > b1->slen) m = b1->slen;
if (b0->data != b1->data) {
for (i = 0; i < m; i ++) {
v = (char) downcase (b0->data[i]);
v -= (char) downcase (b1->data[i]);
if (v != 0) return b0->data[i] - b1->data[i];
}
}
if (n == m || b0->slen == b1->slen) return _AL_BSTR_OK;
if (b0->slen > m) {
v = (char) downcase (b0->data[m]);
if (v) return v;
return UCHAR_MAX + 1;
}
v = - (char) downcase (b1->data[m]);
if (v) return v;
return - (int) (UCHAR_MAX + 1);
}
*
* Compare two strings for equality without differentiating between case.
* If the strings differ other than in case, 0 is returned, if the strings
* are the same, 1 is returned, if there is an error, -1 is returned. If
* the length of the strings are different, this function is O(1). '\0'
* termination characters are not treated in any special way.
*/
int _al_biseqcaseless (_al_const_bstring b0, _al_const_bstring b1) {
int i, n;
if (_al_bdata (b0) == NULL || b0->slen < 0 ||
_al_bdata (b1) == NULL || b1->slen < 0) return _AL_BSTR_ERR;
if (b0->slen != b1->slen) return _AL_BSTR_OK;
if (b0->data == b1->data || b0->slen == 0) return 1;
for (i=0, n=b0->slen; i < n; i++) {
if (b0->data[i] != b1->data[i]) {
unsigned char c = (unsigned char) downcase (b0->data[i]);
if (c != (unsigned char) downcase (b1->data[i])) return 0;
}
}
return 1;
}
*
* Compare beginning of string b0 with a block of memory of length len
* without differentiating between case for equality. If the beginning of b0
* differs from the memory block other than in case (or if b0 is too short),
* 0 is returned, if the strings are the same, 1 is returned, if there is an
* error, -1 is returned. '\0' characters are not treated in any special
* way.
*/
int _al_bisstemeqcaselessblk (_al_const_bstring b0, const void * blk, int len) {
int i;
if (_al_bdata (b0) == NULL || b0->slen < 0 || NULL == blk || len < 0)
return _AL_BSTR_ERR;
if (b0->slen < len) return _AL_BSTR_OK;
if (b0->data == (const unsigned char *) blk || len == 0) return 1;
for (i = 0; i < len; i ++) {
if (b0->data[i] != ((const unsigned char *) blk)[i]) {
if (downcase (b0->data[i]) !=
downcase (((const unsigned char *) blk)[i])) return 0;
}
}
return 1;
}
* int _al_bltrimws (_al_bstring b)
*
* Delete whitespace contiguous from the left end of the string.
*/
int _al_bltrimws (_al_bstring b) {
int i, len;
if (b == NULL || b->data == NULL || b->mlen < b->slen ||
b->slen < 0 || b->mlen <= 0) return _AL_BSTR_ERR;
for (len = b->slen, i = 0; i < len; i++) {
if (!wspace (b->data[i])) {
return _al_bdelete (b, 0, i);
}
}
b->data[0] = (unsigned char) '\0';
b->slen = 0;
return _AL_BSTR_OK;
}
* int _al_brtrimws (_al_bstring b)
*
* Delete whitespace contiguous from the right end of the string.
*/
int _al_brtrimws (_al_bstring b) {
int i;
if (b == NULL || b->data == NULL || b->mlen < b->slen ||
b->slen < 0 || b->mlen <= 0) return _AL_BSTR_ERR;
for (i = b->slen - 1; i >= 0; i--) {
if (!wspace (b->data[i])) {
if (b->mlen > i) b->data[i+1] = (unsigned char) '\0';
b->slen = i + 1;
return _AL_BSTR_OK;
}
}
b->data[0] = (unsigned char) '\0';
b->slen = 0;
return _AL_BSTR_OK;
}
* int _al_btrimws (_al_bstring b)
*
* Delete whitespace contiguous from both ends of the string.
*/
int _al_btrimws (_al_bstring b) {
int i, j;
if (b == NULL || b->data == NULL || b->mlen < b->slen ||
b->slen < 0 || b->mlen <= 0) return _AL_BSTR_ERR;
for (i = b->slen - 1; i >= 0; i--) {
if (!wspace (b->data[i])) {
if (b->mlen > i) b->data[i+1] = (unsigned char) '\0';
b->slen = i + 1;
for (j = 0; wspace (b->data[j]); j++) {}
return _al_bdelete (b, 0, j);
}
}
b->data[0] = (unsigned char) '\0';
b->slen = 0;
return _AL_BSTR_OK;
}
*
* Compare the string b0 and b1. If the strings differ, 0 is returned, if
* the strings are the same, 1 is returned, if there is an error, -1 is
* returned. If the length of the strings are different, this function is
* O(1). '\0' termination characters are not treated in any special way.
*/
int _al_biseq (_al_const_bstring b0, _al_const_bstring b1) {
if (b0 == NULL || b1 == NULL || b0->data == NULL || b1->data == NULL ||
b0->slen < 0 || b1->slen < 0) return _AL_BSTR_ERR;
if (b0->slen != b1->slen) return _AL_BSTR_OK;
if (b0->data == b1->data || b0->slen == 0) return 1;
return !bstr__memcmp (b0->data, b1->data, b0->slen);
}
*
* Compare beginning of string b0 with a block of memory of length len for
* equality. If the beginning of b0 differs from the memory block (or if b0
* is too short), 0 is returned, if the strings are the same, 1 is returned,
* if there is an error, -1 is returned. '\0' characters are not treated in
* any special way.
*/
int _al_bisstemeqblk (_al_const_bstring b0, const void * blk, int len) {
int i;
if (_al_bdata (b0) == NULL || b0->slen < 0 || NULL == blk || len < 0)
return _AL_BSTR_ERR;
if (b0->slen < len) return _AL_BSTR_OK;
if (b0->data == (const unsigned char *) blk || len == 0) return 1;
for (i = 0; i < len; i ++) {
if (b0->data[i] != ((const unsigned char *) blk)[i]) return _AL_BSTR_OK;
}
return 1;
}
*
* Compare the _al_bstring b and char * string s. The C string s must be '\0'
* terminated at exactly the length of the _al_bstring b, and the contents
* between the two must be identical with the _al_bstring b with no '\0'
* characters for the two contents to be considered equal. This is
* equivalent to the condition that their current contents will be always be
* equal when comparing them in the same format after converting one or the
* other. If the strings are equal 1 is returned, if they are unequal 0 is
* returned and if there is a detectable error _AL_BSTR_ERR is returned.
*/
int _al_biseqcstr (_al_const_bstring b, const char * s) {
int i;
if (b == NULL || s == NULL || b->data == NULL || b->slen < 0) return _AL_BSTR_ERR;
for (i=0; i < b->slen; i++) {
if (s[i] == '\0' || b->data[i] != (unsigned char) s[i]) return _AL_BSTR_OK;
}
return s[i] == '\0';
}
*
* Compare the _al_bstring b and char * string s. The C string s must be '\0'
* terminated at exactly the length of the _al_bstring b, and the contents
* between the two must be identical except for case with the _al_bstring b with
* no '\0' characters for the two contents to be considered equal. This is
* equivalent to the condition that their current contents will be always be
* equal ignoring case when comparing them in the same format after
* converting one or the other. If the strings are equal, except for case,
* 1 is returned, if they are unequal regardless of case 0 is returned and
* if there is a detectable error _AL_BSTR_ERR is returned.
*/
int _al_biseqcstrcaseless (_al_const_bstring b, const char * s) {
int i;
if (b == NULL || s == NULL || b->data == NULL || b->slen < 0) return _AL_BSTR_ERR;
for (i=0; i < b->slen; i++) {
if (s[i] == '\0' ||
(b->data[i] != (unsigned char) s[i] &&
downcase (b->data[i]) != (unsigned char) downcase (s[i])))
return _AL_BSTR_OK;
}
return s[i] == '\0';
}
*
* Compare the string b0 and b1. If there is an error, SHRT_MIN is returned,
* otherwise a value less than or greater than zero, indicating that the
* string pointed to by b0 is lexicographically less than or greater than
* the string pointed to by b1 is returned. If the the string lengths are
* unequal but the characters up until the length of the shorter are equal
* then a value less than, or greater than zero, indicating that the string
* pointed to by b0 is shorter or longer than the string pointed to by b1 is
* returned. 0 is returned if and only if the two strings are the same. If
* the length of the strings are different, this function is O(n). Like its
* standard C library counter part strcmp, the comparison does not proceed
* past any '\0' termination characters encountered.
*/
int _al_bstrcmp (_al_const_bstring b0, _al_const_bstring b1) {
int i, v, n;
if (b0 == NULL || b1 == NULL || b0->data == NULL || b1->data == NULL ||
b0->slen < 0 || b1->slen < 0) return SHRT_MIN;
n = b0->slen; if (n > b1->slen) n = b1->slen;
if (b0->slen == b1->slen && (b0->data == b1->data || b0->slen == 0))
return _AL_BSTR_OK;
for (i = 0; i < n; i ++) {
v = ((char) b0->data[i]) - ((char) b1->data[i]);
if (v != 0) return v;
if (b0->data[i] == (unsigned char) '\0') return _AL_BSTR_OK;
}
if (b0->slen > n) return 1;
if (b1->slen > n) return -1;
return _AL_BSTR_OK;
}
*
* Compare the string b0 and b1 for at most n characters. If there is an
* error, SHRT_MIN is returned, otherwise a value is returned as if b0 and
* b1 were first truncated to at most n characters then _al_bstrcmp was called
* with these new strings are paremeters. If the length of the strings are
* different, this function is O(n). Like its standard C library counter
* part strcmp, the comparison does not proceed past any '\0' termination
* characters encountered.
*/
int _al_bstrncmp (_al_const_bstring b0, _al_const_bstring b1, int n) {
int i, v, m;
if (b0 == NULL || b1 == NULL || b0->data == NULL || b1->data == NULL ||
b0->slen < 0 || b1->slen < 0) return SHRT_MIN;
m = n;
if (m > b0->slen) m = b0->slen;
if (m > b1->slen) m = b1->slen;
if (b0->data != b1->data) {
for (i = 0; i < m; i ++) {
v = ((char) b0->data[i]) - ((char) b1->data[i]);
if (v != 0) return v;
if (b0->data[i] == (unsigned char) '\0') return _AL_BSTR_OK;
}
}
if (n == m || b0->slen == b1->slen) return _AL_BSTR_OK;
if (b0->slen > m) return 1;
return -1;
}
*
* Create a _al_bstring which is the substring of b starting from position left
* and running for a length len (clamped by the end of the _al_bstring b.) If
* b is detectably invalid, then NULL is returned. The section described
* by (left, len) is clamped to the boundaries of b.
*/
_al_bstring _al_bmidstr (_al_const_bstring b, int left, int len) {
if (b == NULL || b->slen < 0 || b->data == NULL) return NULL;
if (left < 0) {
len += left;
left = 0;
}
if (len > b->slen - left) len = b->slen - left;
if (len <= 0) return _al_bfromcstr ("");
return _al_blk2bstr (b->data + left, len);
}
*
* Removes characters from pos to pos+len-1 inclusive and shifts the tail of
* the _al_bstring starting from pos+len to pos. len must be positive for this
* call to have any effect. The section of the string described by (pos,
* len) is clamped to boundaries of the _al_bstring b.
*/
int _al_bdelete (_al_bstring b, int pos, int len) {
if (pos < 0) {
len += pos;
pos = 0;
}
if (len < 0 || b == NULL || b->data == NULL || b->slen < 0 ||
b->mlen < b->slen || b->mlen <= 0)
return _AL_BSTR_ERR;
if (len > 0 && pos < b->slen) {
if (pos + len >= b->slen) {
b->slen = pos;
} else {
bBlockCopy ((char *) (b->data + pos),
(char *) (b->data + pos + len),
b->slen - (pos+len));
b->slen -= len;
}
b->data[b->slen] = (unsigned char) '\0';
}
return _AL_BSTR_OK;
}
*
* Free up the _al_bstring. Note that if b is detectably invalid or not writable
* then no action is performed and _AL_BSTR_ERR is returned. Like a freed memory
* allocation, dereferences, writes or any other action on b after it has
* been bdestroyed is undefined.
*/
int _al_bdestroy (_al_bstring b) {
if (b == NULL || b->slen < 0 || b->mlen <= 0 || b->mlen < b->slen ||
b->data == NULL)
return _AL_BSTR_ERR;
bstr__free (b->data);
notice this error. */
b->slen = -1;
b->mlen = -__LINE__;
b->data = NULL;
bstr__free (b);
return _AL_BSTR_OK;
}
*
* Search for the _al_bstring b2 in b1 starting from position pos, and searching
* forward. If it is found then return with the first position where it is
* found, otherwise return _AL_BSTR_ERR. Note that this is just a brute force
* string searcher that does not attempt clever things like the Boyer-Moore
* search algorithm. Because of this there are many degenerate cases where
* this can take much longer than it needs to.
*/
int _al_binstr (_al_const_bstring b1, int pos, _al_const_bstring b2) {
int j, ii, ll, lf;
unsigned char * d0;
unsigned char c0;
register unsigned char * d1;
register unsigned char c1;
register int i;
if (b1 == NULL || b1->data == NULL || b1->slen < 0 ||
b2 == NULL || b2->data == NULL || b2->slen < 0) return _AL_BSTR_ERR;
if (b1->slen == pos) return (b2->slen == 0)?pos:_AL_BSTR_ERR;
if (b1->slen < pos || pos < 0) return _AL_BSTR_ERR;
if (b2->slen == 0) return pos;
if ((lf = b1->slen - b2->slen + 1) <= pos) return _AL_BSTR_ERR;
if (b1->data == b2->data && pos == 0) return 0;
i = pos;
d0 = b2->data;
d1 = b1->data;
ll = b2->slen;
c0 = d0[0];
if (1 == ll) {
for (;i < lf; i++) if (c0 == d1[i]) return i;
return _AL_BSTR_ERR;
}
c1 = c0;
j = 0;
lf = b1->slen - 1;
ii = -1;
if (i < lf) do {
if (c1 != d1[i]) {
if (c1 != d1[1+i]) {
i += 2;
continue;
}
i++;
}
if (0 == j) ii = i;
j++;
i++;
if (j < ll) {
c1 = d0[j];
continue;
}
N0:;
if (i == ii+j) return ii;
i -= j;
j = 0;
c1 = c0;
} while (i < lf);
if (i == lf && ll == j+1 && c1 == d1[i]) goto N0;
return _AL_BSTR_ERR;
}
*
* Search for the _al_bstring b2 in b1 starting from position pos, and searching
* backward. If it is found then return with the first position where it is
* found, otherwise return _AL_BSTR_ERR. Note that this is just a brute force
* string searcher that does not attempt clever things like the Boyer-Moore
* search algorithm. Because of this there are many degenerate cases where
* this can take much longer than it needs to.
*/
int _al_binstrr (_al_const_bstring b1, int pos, _al_const_bstring b2) {
int j, i, l;
unsigned char * d0, * d1;
if (b1 == NULL || b1->data == NULL || b1->slen < 0 ||
b2 == NULL || b2->data == NULL || b2->slen < 0) return _AL_BSTR_ERR;
if (b1->slen == pos && b2->slen == 0) return pos;
if (b1->slen < pos || pos < 0) return _AL_BSTR_ERR;
if (b2->slen == 0) return pos;
if (b1->data == b2->data && pos == 0 && b2->slen <= b1->slen) return 0;
i = pos;
if ((l = b1->slen - b2->slen) < 0) return _AL_BSTR_ERR;
if (l + 1 <= i) i = l;
j = 0;
d0 = b2->data;
d1 = b1->data;
l = b2->slen;
for (;;) {
if (d0[j] == d1[i + j]) {
j ++;
if (j >= l) return i;
} else {
i --;
if (i < 0) break;
j=0;
}
}
return _AL_BSTR_ERR;
}
*
* Search for the _al_bstring b2 in b1 starting from position pos, and searching
* forward but without regard to case. If it is found then return with the
* first position where it is found, otherwise return _AL_BSTR_ERR. Note that
* this is just a brute force string searcher that does not attempt clever
* things like the Boyer-Moore search algorithm. Because of this there are
* many degenerate cases where this can take much longer than it needs to.
*/
int _al_binstrcaseless (_al_const_bstring b1, int pos, _al_const_bstring b2) {
int j, i, l, ll;
unsigned char * d0, * d1;
if (b1 == NULL || b1->data == NULL || b1->slen < 0 ||
b2 == NULL || b2->data == NULL || b2->slen < 0) return _AL_BSTR_ERR;
if (b1->slen == pos) return (b2->slen == 0)?pos:_AL_BSTR_ERR;
if (b1->slen < pos || pos < 0) return _AL_BSTR_ERR;
if (b2->slen == 0) return pos;
l = b1->slen - b2->slen + 1;
if (l <= pos) return _AL_BSTR_ERR;
if (b1->data == b2->data && pos == 0) return _AL_BSTR_OK;
i = pos;
j = 0;
d0 = b2->data;
d1 = b1->data;
ll = b2->slen;
for (;;) {
if (d0[j] == d1[i + j] || downcase (d0[j]) == downcase (d1[i + j])) {
j ++;
if (j >= ll) return i;
} else {
i ++;
if (i >= l) break;
j=0;
}
}
return _AL_BSTR_ERR;
}
*
* Search for the _al_bstring b2 in b1 starting from position pos, and searching
* backward but without regard to case. If it is found then return with the
* first position where it is found, otherwise return _AL_BSTR_ERR. Note that
* this is just a brute force string searcher that does not attempt clever
* things like the Boyer-Moore search algorithm. Because of this there are
* many degenerate cases where this can take much longer than it needs to.
*/
int _al_binstrrcaseless (_al_const_bstring b1, int pos, _al_const_bstring b2) {
int j, i, l;
unsigned char * d0, * d1;
if (b1 == NULL || b1->data == NULL || b1->slen < 0 ||
b2 == NULL || b2->data == NULL || b2->slen < 0) return _AL_BSTR_ERR;
if (b1->slen == pos && b2->slen == 0) return pos;
if (b1->slen < pos || pos < 0) return _AL_BSTR_ERR;
if (b2->slen == 0) return pos;
if (b1->data == b2->data && pos == 0 && b2->slen <= b1->slen) return _AL_BSTR_OK;
i = pos;
if ((l = b1->slen - b2->slen) < 0) return _AL_BSTR_ERR;
if (l + 1 <= i) i = l;
j = 0;
d0 = b2->data;
d1 = b1->data;
l = b2->slen;
for (;;) {
if (d0[j] == d1[i + j] || downcase (d0[j]) == downcase (d1[i + j])) {
j ++;
if (j >= l) return i;
} else {
i --;
if (i < 0) break;
j=0;
}
}
return _AL_BSTR_ERR;
}
*
* Search for the character c in b forwards from the position pos
* (inclusive).
*/
int _al_bstrchrp (_al_const_bstring b, int c, int pos) {
unsigned char * p;
if (b == NULL || b->data == NULL || b->slen <= pos || pos < 0) return _AL_BSTR_ERR;
p = (unsigned char *) bstr__memchr ((b->data + pos), (unsigned char) c, (b->slen - pos));
if (p) return (int) (p - b->data);
return _AL_BSTR_ERR;
}
*
* Search for the character c in b backwards from the position pos in string
* (inclusive).
*/
int _al_bstrrchrp (_al_const_bstring b, int c, int pos) {
int i;
if (b == NULL || b->data == NULL || b->slen <= pos || pos < 0) return _AL_BSTR_ERR;
for (i=pos; i >= 0; i--) {
if (b->data[i] == (unsigned char) c) return i;
}
return _AL_BSTR_ERR;
}
#if !defined (BSTRLIB_AGGRESSIVE_MEMORY_FOR_SPEED_TRADEOFF)
#define LONG_LOG_BITS_QTY (3)
#define LONG_BITS_QTY (1 << LONG_LOG_BITS_QTY)
#define LONG_TYPE unsigned char
#define CFCLEN ((1 << CHAR_BIT) / LONG_BITS_QTY)
struct charField { LONG_TYPE content[CFCLEN]; };
#define testInCharField(cf,c) ((cf)->content[(c) >> LONG_LOG_BITS_QTY] & (((long)1) << ((c) & (LONG_BITS_QTY-1))))
#define setInCharField(cf,idx) { \
unsigned int c = (unsigned int) (idx); \
(cf)->content[c >> LONG_LOG_BITS_QTY] |= (LONG_TYPE) (1ul << (c & (LONG_BITS_QTY-1))); \
}
#else
#define CFCLEN (1 << CHAR_BIT)
struct charField { unsigned char content[CFCLEN]; };
#define testInCharField(cf,c) ((cf)->content[(unsigned char) (c)])
#define setInCharField(cf,idx) (cf)->content[(unsigned int) (idx)] = ~0
#endif
static int buildCharField (struct charField * cf, _al_const_bstring b) {
int i;
if (b == NULL || b->data == NULL || b->slen <= 0) return _AL_BSTR_ERR;
memset ((void *) cf->content, 0, sizeof (struct charField));
for (i=0; i < b->slen; i++) {
setInCharField (cf, b->data[i]);
}
return _AL_BSTR_OK;
}
static void invertCharField (struct charField * cf) {
int i;
for (i=0; i < CFCLEN; i++) cf->content[i] = ~cf->content[i];
}
static int binchrCF (const unsigned char * data, int len, int pos, const struct charField * cf) {
int i;
for (i=pos; i < len; i++) {
unsigned char c = (unsigned char) data[i];
if (testInCharField (cf, c)) return i;
}
return _AL_BSTR_ERR;
}
*
* Search for the first position in b0 starting from pos or after, in which
* one of the characters in b1 is found and return it. If such a position
* does not exist in b0, then _AL_BSTR_ERR is returned.
*/
int _al_binchr (_al_const_bstring b0, int pos, _al_const_bstring b1) {
struct charField chrs;
if (pos < 0 || b0 == NULL || b0->data == NULL ||
b0->slen <= pos) return _AL_BSTR_ERR;
if (1 == b1->slen) return _al_bstrchrp (b0, b1->data[0], pos);
if (0 > buildCharField (&chrs, b1)) return _AL_BSTR_ERR;
return binchrCF (b0->data, b0->slen, pos, &chrs);
}
static int binchrrCF (const unsigned char * data, int pos, const struct charField * cf) {
int i;
for (i=pos; i >= 0; i--) {
unsigned int c = (unsigned int) data[i];
if (testInCharField (cf, c)) return i;
}
return _AL_BSTR_ERR;
}
*
* Search for the last position in b0 no greater than pos, in which one of
* the characters in b1 is found and return it. If such a position does not
* exist in b0, then _AL_BSTR_ERR is returned.
*/
int _al_binchrr (_al_const_bstring b0, int pos, _al_const_bstring b1) {
struct charField chrs;
if (pos < 0 || b0 == NULL || b0->data == NULL || b1 == NULL ||
b0->slen < pos) return _AL_BSTR_ERR;
if (pos == b0->slen) pos--;
if (1 == b1->slen) return _al_bstrrchrp (b0, b1->data[0], pos);
if (0 > buildCharField (&chrs, b1)) return _AL_BSTR_ERR;
return binchrrCF (b0->data, pos, &chrs);
}
*
* Search for the first position in b0 starting from pos or after, in which
* none of the characters in b1 is found and return it. If such a position
* does not exist in b0, then _AL_BSTR_ERR is returned.
*/
int _al_bninchr (_al_const_bstring b0, int pos, _al_const_bstring b1) {
struct charField chrs;
if (pos < 0 || b0 == NULL || b0->data == NULL ||
b0->slen <= pos) return _AL_BSTR_ERR;
if (buildCharField (&chrs, b1) < 0) return _AL_BSTR_ERR;
invertCharField (&chrs);
return binchrCF (b0->data, b0->slen, pos, &chrs);
}
*
* Search for the last position in b0 no greater than pos, in which none of
* the characters in b1 is found and return it. If such a position does not
* exist in b0, then _AL_BSTR_ERR is returned.
*/
int _al_bninchrr (_al_const_bstring b0, int pos, _al_const_bstring b1) {
struct charField chrs;
if (pos < 0 || b0 == NULL || b0->data == NULL ||
b0->slen < pos) return _AL_BSTR_ERR;
if (pos == b0->slen) pos--;
if (buildCharField (&chrs, b1) < 0) return _AL_BSTR_ERR;
invertCharField (&chrs);
return binchrrCF (b0->data, pos, &chrs);
}
*
* Overwrite the string b0 starting at position pos with the string b1. If
* the position pos is past the end of b0, then the character "fill" is
* appended as necessary to make up the gap between the end of b0 and pos.
* If b1 is NULL, it behaves as if it were a 0-length string.
*/
int _al_bsetstr (_al_bstring b0, int pos, _al_const_bstring b1, unsigned char fill) {
int d, newlen;
ptrdiff_t pd;
_al_bstring aux = (_al_bstring) b1;
if (pos < 0 || b0 == NULL || b0->slen < 0 || NULL == b0->data ||
b0->mlen < b0->slen || b0->mlen <= 0) return _AL_BSTR_ERR;
if (b1 != NULL && (b1->slen < 0 || b1->data == NULL)) return _AL_BSTR_ERR;
d = pos;
if (NULL != aux) {
if ((pd = (ptrdiff_t) (b1->data - b0->data)) >= 0 && pd < (ptrdiff_t) b0->mlen) {
if (NULL == (aux = _al_bstrcpy (b1))) return _AL_BSTR_ERR;
}
d += aux->slen;
}
if (_al_balloc (b0, d + 1) != _AL_BSTR_OK) {
if (aux != b1) _al_bdestroy (aux);
return _AL_BSTR_ERR;
}
newlen = b0->slen;
if (pos > newlen) {
bstr__memset (b0->data + b0->slen, (int) fill, (size_t) (pos - b0->slen));
newlen = pos;
}
if (aux != NULL) {
bBlockCopy ((char *) (b0->data + pos), (char *) aux->data, aux->slen);
if (aux != b1) _al_bdestroy (aux);
}
if (d > newlen) newlen = d;
b0->slen = newlen;
b0->data[newlen] = (unsigned char) '\0';
return _AL_BSTR_OK;
}
*
* Inserts the string b2 into b1 at position pos. If the position pos is
* past the end of b1, then the character "fill" is appended as necessary to
* make up the gap between the end of b1 and pos. Unlike _al_bsetstr, _al_binsert
* does not allow b2 to be NULL.
*/
int _al_binsert (_al_bstring b1, int pos, _al_const_bstring b2, unsigned char fill) {
int d, l;
ptrdiff_t pd;
_al_bstring aux = (_al_bstring) b2;
if (pos < 0 || b1 == NULL || b2 == NULL || b1->slen < 0 ||
b2->slen < 0 || b1->mlen < b1->slen || b1->mlen <= 0) return _AL_BSTR_ERR;
if ((pd = (ptrdiff_t) (b2->data - b1->data)) >= 0 && pd < (ptrdiff_t) b1->mlen) {
if (NULL == (aux = _al_bstrcpy (b2))) return _AL_BSTR_ERR;
}
d = b1->slen + aux->slen;
l = pos + aux->slen;
if ((d|l) < 0) return _AL_BSTR_ERR;
if (l > d) {
if (_al_balloc (b1, l + 1) != _AL_BSTR_OK) {
if (aux != b2) _al_bdestroy (aux);
return _AL_BSTR_ERR;
}
bstr__memset (b1->data + b1->slen, (int) fill, (size_t) (pos - b1->slen));
b1->slen = l;
} else {
if (_al_balloc (b1, d + 1) != _AL_BSTR_OK) {
if (aux != b2) _al_bdestroy (aux);
return _AL_BSTR_ERR;
}
bBlockCopy (b1->data + l, b1->data + pos, d - l);
b1->slen = d;
}
bBlockCopy (b1->data + pos, aux->data, aux->slen);
b1->data[b1->slen] = (unsigned char) '\0';
if (aux != b2) _al_bdestroy (aux);
return _AL_BSTR_OK;
}
* unsigned char fill)
*
* Replace a section of a string from pos for a length len with the string b2.
* fill is used is pos > b1->slen.
*/
int _al_breplace (_al_bstring b1, int pos, int len, _al_const_bstring b2,
unsigned char fill) {
int pl, ret;
ptrdiff_t pd;
_al_bstring aux = (_al_bstring) b2;
if (pos < 0 || len < 0 || (pl = pos + len) < 0 || b1 == NULL ||
b2 == NULL || b1->data == NULL || b2->data == NULL ||
b1->slen < 0 || b2->slen < 0 || b1->mlen < b1->slen ||
b1->mlen <= 0) return _AL_BSTR_ERR;
if (pl >= b1->slen) {
if ((ret = _al_bsetstr (b1, pos, b2, fill)) < 0) return ret;
if (pos + b2->slen < b1->slen) {
b1->slen = pos + b2->slen;
b1->data[b1->slen] = (unsigned char) '\0';
}
return ret;
}
if ((pd = (ptrdiff_t) (b2->data - b1->data)) >= 0 && pd < (ptrdiff_t) b1->slen) {
if (NULL == (aux = _al_bstrcpy (b2))) return _AL_BSTR_ERR;
}
if (aux->slen > len) {
if (_al_balloc (b1, b1->slen + aux->slen - len) != _AL_BSTR_OK) {
if (aux != b2) _al_bdestroy (aux);
return _AL_BSTR_ERR;
}
}
if (aux->slen != len) bstr__memmove (b1->data + pos + aux->slen, b1->data + pos + len, b1->slen - (pos + len));
bstr__memcpy (b1->data + pos, aux->data, aux->slen);
b1->slen += aux->slen - len;
b1->data[b1->slen] = (unsigned char) '\0';
if (aux != b2) _al_bdestroy (aux);
return _AL_BSTR_OK;
}
* int pos)
*
* Replace all occurrences of a find string with a replace string after a
* given point in a _al_bstring.
*/
typedef int (*instr_fnptr) (_al_const_bstring s1, int pos, _al_const_bstring s2);
static int findreplaceengine (_al_bstring b, _al_const_bstring find, _al_const_bstring repl, int pos, instr_fnptr instr) {
int i, ret, slen, mlen, delta, acc;
int * d;
int static_d[32];
ptrdiff_t pd;
_al_bstring auxf = (_al_bstring) find;
_al_bstring auxr = (_al_bstring) repl;
if (b == NULL || b->data == NULL || find == NULL ||
find->data == NULL || repl == NULL || repl->data == NULL ||
pos < 0 || find->slen <= 0 || b->mlen < 0 || b->slen > b->mlen ||
b->mlen <= 0 || b->slen < 0 || repl->slen < 0) return _AL_BSTR_ERR;
if (pos > b->slen - find->slen) return _AL_BSTR_OK;
pd = (ptrdiff_t) (find->data - b->data);
if ((ptrdiff_t) (pos - find->slen) < pd && pd < (ptrdiff_t) b->slen) {
if (NULL == (auxf = _al_bstrcpy (find))) return _AL_BSTR_ERR;
}
pd = (ptrdiff_t) (repl->data - b->data);
if ((ptrdiff_t) (pos - repl->slen) < pd && pd < (ptrdiff_t) b->slen) {
if (NULL == (auxr = _al_bstrcpy (repl))) {
if (auxf != find) _al_bdestroy (auxf);
return _AL_BSTR_ERR;
}
}
delta = auxf->slen - auxr->slen;
length */
if (delta == 0) {
while ((pos = instr (b, pos, auxf)) >= 0) {
bstr__memcpy (b->data + pos, auxr->data, auxr->slen);
pos += auxf->slen;
}
if (auxf != find) _al_bdestroy (auxf);
if (auxr != repl) _al_bdestroy (auxr);
return _AL_BSTR_OK;
}
if (delta > 0) {
acc = 0;
while ((i = instr (b, pos, auxf)) >= 0) {
if (acc && i > pos)
bstr__memmove (b->data + pos - acc, b->data + pos, i - pos);
if (auxr->slen)
bstr__memcpy (b->data + i - acc, auxr->data, auxr->slen);
acc += delta;
pos = i + auxf->slen;
}
if (acc) {
i = b->slen;
if (i > pos)
bstr__memmove (b->data + pos - acc, b->data + pos, i - pos);
b->slen -= acc;
b->data[b->slen] = (unsigned char) '\0';
}
if (auxf != find) _al_bdestroy (auxf);
if (auxr != repl) _al_bdestroy (auxr);
return _AL_BSTR_OK;
}
more complicated. */
mlen = 32;
d = (int *) static_d;
acc = slen = 0;
while ((pos = instr (b, pos, auxf)) >= 0) {
if (slen + 1 >= mlen) {
int sl;
int * t;
mlen += mlen;
sl = sizeof (int *) * mlen;
if (static_d == d) d = NULL;
if (sl < mlen || NULL == (t = (int *) bstr__realloc (d, sl))) {
ret = _AL_BSTR_ERR;
goto done;
}
if (NULL == d) bstr__memcpy (t, static_d, sizeof (static_d));
d = t;
}
d[slen] = pos;
slen++;
acc -= delta;
pos += auxf->slen;
if (pos < 0 || acc < 0) {
ret = _AL_BSTR_ERR;
goto done;
}
}
d[slen] = b->slen;
if (_AL_BSTR_OK == (ret = _al_balloc (b, b->slen + acc + 1))) {
b->slen += acc;
for (i = slen-1; i >= 0; i--) {
int s, l;
s = d[i] + auxf->slen;
l = d[i+1] - s;
if (l) {
bstr__memmove (b->data + s + acc, b->data + s, l);
}
if (auxr->slen) {
bstr__memmove (b->data + s + acc - auxr->slen,
auxr->data, auxr->slen);
}
acc += delta;
}
b->data[b->slen] = (unsigned char) '\0';
}
done:;
if (static_d == d) d = NULL;
bstr__free (d);
if (auxf != find) _al_bdestroy (auxf);
if (auxr != repl) _al_bdestroy (auxr);
return ret;
}
* int pos)
*
* Replace all occurrences of a find string with a replace string after a
* given point in a _al_bstring.
*/
int _al_bfindreplace (_al_bstring b, _al_const_bstring find, _al_const_bstring repl, int pos) {
return findreplaceengine (b, find, repl, pos, _al_binstr);
}
* int pos)
*
* Replace all occurrences of a find string, ignoring case, with a replace
* string after a given point in a _al_bstring.
*/
int _al_bfindreplacecaseless (_al_bstring b, _al_const_bstring find, _al_const_bstring repl, int pos) {
return findreplaceengine (b, find, repl, pos, _al_binstrcaseless);
}
*
* Inserts the character fill repeatedly into b at position pos for a
* length len. If the position pos is past the end of b, then the
* character "fill" is appended as necessary to make up the gap between the
* end of b and the position pos + len.
*/
int _al_binsertch (_al_bstring b, int pos, int len, unsigned char fill) {
int d, l, i;
if (pos < 0 || b == NULL || b->slen < 0 || b->mlen < b->slen ||
b->mlen <= 0 || len < 0) return _AL_BSTR_ERR;
d = b->slen + len;
l = pos + len;
if ((d|l) < 0) return _AL_BSTR_ERR;
if (l > d) {
if (_al_balloc (b, l + 1) != _AL_BSTR_OK) return _AL_BSTR_ERR;
pos = b->slen;
b->slen = l;
} else {
if (_al_balloc (b, d + 1) != _AL_BSTR_OK) return _AL_BSTR_ERR;
for (i = d - 1; i >= l; i--) {
b->data[i] = b->data[i - len];
}
b->slen = d;
}
for (i=pos; i < l; i++) b->data[i] = fill;
b->data[b->slen] = (unsigned char) '\0';
return _AL_BSTR_OK;
}
*
* Replicate the _al_bstring, b in place, end to end repeatedly until it
* surpasses len characters, then chop the result to exactly len characters.
* This function operates in-place. The function will return with _AL_BSTR_ERR
* if b is NULL or of length 0, otherwise _AL_BSTR_OK is returned.
*/
int _al_bpattern (_al_bstring b, int len) {
int i, d;
d = _al_blength (b);
if (d <= 0 || len < 0 || _al_balloc (b, len + 1) != _AL_BSTR_OK) return _AL_BSTR_ERR;
if (len > 0) {
if (d == 1) return _al_bsetstr (b, len, NULL, b->data[0]);
for (i = d; i < len; i++) b->data[i] = b->data[i - d];
}
b->data[len] = (unsigned char) '\0';
b->slen = len;
return _AL_BSTR_OK;
}
#define BS_BUFF_SZ (1024)
*
* Use a finite buffer fread-like function readPtr to concatenate to the
* _al_bstring b the entire contents of file-like source data in a roughly
* efficient way.
*/
int _al_breada (_al_bstring b, _al_bNread readPtr, void * parm) {
int i, l, n;
if (b == NULL || b->mlen <= 0 || b->slen < 0 || b->mlen < b->slen ||
b->mlen <= 0 || readPtr == NULL) return _AL_BSTR_ERR;
i = b->slen;
for (n=i+16; ; n += ((n < BS_BUFF_SZ) ? n : BS_BUFF_SZ)) {
if (_AL_BSTR_OK != _al_balloc (b, n + 1)) return _AL_BSTR_ERR;
l = (int) readPtr ((void *) (b->data + i), 1, n - i, parm);
i += l;
b->slen = i;
if (i < n) break;
}
b->data[i] = (unsigned char) '\0';
return _AL_BSTR_OK;
}
*
* Use a finite buffer fread-like function readPtr to create a _al_bstring
* filled with the entire contents of file-like source data in a roughly
* efficient way.
*/
_al_bstring _al_bread (_al_bNread readPtr, void * parm) {
_al_bstring buff;
if (0 > _al_breada (buff = _al_bfromcstr (""), readPtr, parm)) {
_al_bdestroy (buff);
return NULL;
}
return buff;
}
*
* Use an fgetc-like single character stream reading function (getcPtr) to
* obtain a sequence of characters which are concatenated to the end of the
* _al_bstring b. The stream read is terminated by the passed in terminator
* parameter.
*
* If getcPtr returns with a negative number, or the terminator character
* (which is appended) is read, then the stream reading is halted and the
* function returns with a partial result in b. If there is an empty partial
* result, 1 is returned. If no characters are read, or there is some other
* detectable error, _AL_BSTR_ERR is returned.
*/
int _al_bassigngets (_al_bstring b, _al_bNgetc getcPtr, void * parm, char terminator) {
int c, d, e;
if (b == NULL || b->mlen <= 0 || b->slen < 0 || b->mlen < b->slen ||
b->mlen <= 0 || getcPtr == NULL) return _AL_BSTR_ERR;
d = 0;
e = b->mlen - 2;
while ((c = getcPtr (parm)) >= 0) {
if (d > e) {
b->slen = d;
if (_al_balloc (b, d + 2) != _AL_BSTR_OK) return _AL_BSTR_ERR;
e = b->mlen - 2;
}
b->data[d] = (unsigned char) c;
d++;
if (c == terminator) break;
}
b->data[d] = (unsigned char) '\0';
b->slen = d;
return d == 0 && c < 0;
}
*
* Use an fgetc-like single character stream reading function (getcPtr) to
* obtain a sequence of characters which are concatenated to the end of the
* _al_bstring b. The stream read is terminated by the passed in terminator
* parameter.
*
* If getcPtr returns with a negative number, or the terminator character
* (which is appended) is read, then the stream reading is halted and the
* function returns with a partial result concatentated to b. If there is
* an empty partial result, 1 is returned. If no characters are read, or
* there is some other detectable error, _AL_BSTR_ERR is returned.
*/
int _al_bgetsa (_al_bstring b, _al_bNgetc getcPtr, void * parm, char terminator) {
int c, d, e;
if (b == NULL || b->mlen <= 0 || b->slen < 0 || b->mlen < b->slen ||
b->mlen <= 0 || getcPtr == NULL) return _AL_BSTR_ERR;
d = b->slen;
e = b->mlen - 2;
while ((c = getcPtr (parm)) >= 0) {
if (d > e) {
b->slen = d;
if (_al_balloc (b, d + 2) != _AL_BSTR_OK) return _AL_BSTR_ERR;
e = b->mlen - 2;
}
b->data[d] = (unsigned char) c;
d++;
if (c == terminator) break;
}
b->data[d] = (unsigned char) '\0';
b->slen = d;
return d == 0 && c < 0;
}
*
* Use an fgetc-like single character stream reading function (getcPtr) to
* obtain a sequence of characters which are concatenated into a _al_bstring.
* The stream read is terminated by the passed in terminator function.
*
* If getcPtr returns with a negative number, or the terminator character
* (which is appended) is read, then the stream reading is halted and the
* result obtained thus far is returned. If no characters are read, or
* there is some other detectable error, NULL is returned.
*/
_al_bstring _al_bgets (_al_bNgetc getcPtr, void * parm, char terminator) {
_al_bstring buff;
if (0 > _al_bgetsa (buff = _al_bfromcstr (""), getcPtr, parm, terminator) || 0 >= buff->slen) {
_al_bdestroy (buff);
buff = NULL;
}
return buff;
}
struct _al_bStream {
_al_bstring buff;
void * parm;
_al_bNread readFnPtr;
int isEOF;
int maxBuffSz;
};
*
* Wrap a given open stream (described by a fread compatible function
* pointer and stream handle) into an open _al_bStream suitable for the _al_bstring
* library streaming functions.
*/
struct _al_bStream * _al_bsopen (_al_bNread readPtr, void * parm) {
struct _al_bStream * s;
if (readPtr == NULL) return NULL;
s = (struct _al_bStream *) bstr__alloc (sizeof (struct _al_bStream));
if (s == NULL) return NULL;
s->parm = parm;
s->buff = _al_bfromcstr ("");
s->readFnPtr = readPtr;
s->maxBuffSz = BS_BUFF_SZ;
s->isEOF = 0;
return s;
}
*
* Set the length of the buffer used by the _al_bStream. If sz is zero, the
* length is not set. This function returns with the previous length.
*/
int _al_bsbufflength (struct _al_bStream * s, int sz) {
int oldSz;
if (s == NULL || sz < 0) return _AL_BSTR_ERR;
oldSz = s->maxBuffSz;
if (sz > 0) s->maxBuffSz = sz;
return oldSz;
}
int _al_bseof (const struct _al_bStream * s) {
if (s == NULL || s->readFnPtr == NULL) return _AL_BSTR_ERR;
return s->isEOF && (s->buff->slen == 0);
}
*
* Close the _al_bStream, and return the handle to the stream that was originally
* used to open the given stream.
*/
void * _al_bsclose (struct _al_bStream * s) {
void * parm;
if (s == NULL) return NULL;
s->readFnPtr = NULL;
if (s->buff) _al_bdestroy (s->buff);
s->buff = NULL;
parm = s->parm;
s->parm = NULL;
s->isEOF = 1;
bstr__free (s);
return parm;
}
*
* Read a _al_bstring terminated by the terminator character or the end of the
* stream from the _al_bStream (s) and return it into the parameter r. This
* function may read additional characters from the core stream that are not
* returned, but will be retained for subsequent read operations.
*/
int _al_bsreadlna (_al_bstring r, struct _al_bStream * s, char terminator) {
int i, l, ret, rlo;
char * b;
struct _al_tagbstring x;
if (s == NULL || s->buff == NULL || r == NULL || r->mlen <= 0 ||
r->slen < 0 || r->mlen < r->slen) return _AL_BSTR_ERR;
l = s->buff->slen;
if (_AL_BSTR_OK != _al_balloc (s->buff, s->maxBuffSz + 1)) return _AL_BSTR_ERR;
b = (char *) s->buff->data;
x.data = (unsigned char *) b;
b[l] = terminator;
for (i=0; b[i] != terminator; i++) ;
if (i < l) {
x.slen = i + 1;
ret = _al_bconcat (r, &x);
s->buff->slen = l;
if (_AL_BSTR_OK == ret) _al_bdelete (s->buff, 0, i + 1);
return _AL_BSTR_OK;
}
rlo = r->slen;
x.slen = l;
if (_AL_BSTR_OK != _al_bconcat (r, &x)) return _AL_BSTR_ERR;
the minimum of data-copies */
for (;;) {
if (_AL_BSTR_OK != _al_balloc (r, r->slen + s->maxBuffSz + 1)) return _AL_BSTR_ERR;
b = (char *) (r->data + r->slen);
l = (int) s->readFnPtr (b, 1, s->maxBuffSz, s->parm);
if (l <= 0) {
r->data[r->slen] = (unsigned char) '\0';
s->buff->slen = 0;
s->isEOF = 1;
return _AL_BSTR_ERR & -(r->slen == rlo);
}
b[l] = terminator;
for (i=0; b[i] != terminator; i++) ;
if (i < l) break;
r->slen += l;
}
i++;
r->slen += i;
s->buff->slen = l - i;
bstr__memcpy (s->buff->data, b + i, l - i);
r->data[r->slen] = (unsigned char) '\0';
return _AL_BSTR_OK;
}
*
* Read a _al_bstring terminated by any character in the term string or the end
* of the stream from the _al_bStream (s) and return it into the parameter r.
* This function may read additional characters from the core stream that
* are not returned, but will be retained for subsequent read operations.
*/
int _al_bsreadlnsa (_al_bstring r, struct _al_bStream * s, _al_const_bstring term) {
int i, l, ret, rlo;
unsigned char * b;
struct _al_tagbstring x;
struct charField cf;
if (s == NULL || s->buff == NULL || r == NULL || term == NULL ||
term->data == NULL || r->mlen <= 0 || r->slen < 0 ||
r->mlen < r->slen) return _AL_BSTR_ERR;
if (term->slen == 1) return _al_bsreadlna (r, s, term->data[0]);
if (term->slen < 1 || buildCharField (&cf, term)) return _AL_BSTR_ERR;
l = s->buff->slen;
if (_AL_BSTR_OK != _al_balloc (s->buff, s->maxBuffSz + 1)) return _AL_BSTR_ERR;
b = (unsigned char *) s->buff->data;
x.data = b;
b[l] = term->data[0];
for (i=0; !testInCharField (&cf, b[i]); i++) ;
if (i < l) {
x.slen = i + 1;
ret = _al_bconcat (r, &x);
s->buff->slen = l;
if (_AL_BSTR_OK == ret) _al_bdelete (s->buff, 0, i + 1);
return _AL_BSTR_OK;
}
rlo = r->slen;
x.slen = l;
if (_AL_BSTR_OK != _al_bconcat (r, &x)) return _AL_BSTR_ERR;
the minimum of data-copies */
for (;;) {
if (_AL_BSTR_OK != _al_balloc (r, r->slen + s->maxBuffSz + 1)) return _AL_BSTR_ERR;
b = (unsigned char *) (r->data + r->slen);
l = (int) s->readFnPtr (b, 1, s->maxBuffSz, s->parm);
if (l <= 0) {
r->data[r->slen] = (unsigned char) '\0';
s->buff->slen = 0;
s->isEOF = 1;
return _AL_BSTR_ERR & -(r->slen == rlo);
}
b[l] = term->data[0];
for (i=0; !testInCharField (&cf, b[i]); i++) ;
if (i < l) break;
r->slen += l;
}
i++;
r->slen += i;
s->buff->slen = l - i;
bstr__memcpy (s->buff->data, b + i, l - i);
r->data[r->slen] = (unsigned char) '\0';
return _AL_BSTR_OK;
}
*
* Read a _al_bstring of length n (or, if it is fewer, as many bytes as is
* remaining) from the _al_bStream. This function may read additional
* characters from the core stream that are not returned, but will be
* retained for subsequent read operations. This function will not read
* additional characters from the core stream beyond virtual stream pointer.
*/
int _al_bsreada (_al_bstring r, struct _al_bStream * s, int n) {
int l, ret, orslen;
char * b;
struct _al_tagbstring x;
if (s == NULL || s->buff == NULL || r == NULL || r->mlen <= 0
|| r->slen < 0 || r->mlen < r->slen || n <= 0) return _AL_BSTR_ERR;
n += r->slen;
if (n <= 0) return _AL_BSTR_ERR;
l = s->buff->slen;
orslen = r->slen;
if (0 == l) {
if (s->isEOF) return _AL_BSTR_ERR;
if (r->mlen > n) {
l = (int) s->readFnPtr (r->data + r->slen, 1, n - r->slen, s->parm);
if (0 >= l || l > n - r->slen) {
s->isEOF = 1;
return _AL_BSTR_ERR;
}
r->slen += l;
r->data[r->slen] = (unsigned char) '\0';
return 0;
}
}
if (_AL_BSTR_OK != _al_balloc (s->buff, s->maxBuffSz + 1)) return _AL_BSTR_ERR;
b = (char *) s->buff->data;
x.data = (unsigned char *) b;
do {
if (l + r->slen >= n) {
x.slen = n - r->slen;
ret = _al_bconcat (r, &x);
s->buff->slen = l;
if (_AL_BSTR_OK == ret) _al_bdelete (s->buff, 0, x.slen);
return _AL_BSTR_ERR & -(r->slen == orslen);
}
x.slen = l;
if (_AL_BSTR_OK != _al_bconcat (r, &x)) break;
l = n - r->slen;
if (l > s->maxBuffSz) l = s->maxBuffSz;
l = (int) s->readFnPtr (b, 1, l, s->parm);
} while (l > 0);
if (l < 0) l = 0;
if (l == 0) s->isEOF = 1;
s->buff->slen = l;
return _AL_BSTR_ERR & -(r->slen == orslen);
}
*
* Read a _al_bstring terminated by the terminator character or the end of the
* stream from the _al_bStream (s) and return it into the parameter r. This
* function may read additional characters from the core stream that are not
* returned, but will be retained for subsequent read operations.
*/
int _al_bsreadln (_al_bstring r, struct _al_bStream * s, char terminator) {
if (s == NULL || s->buff == NULL || r == NULL || r->mlen <= 0)
return _AL_BSTR_ERR;
if (_AL_BSTR_OK != _al_balloc (s->buff, s->maxBuffSz + 1)) return _AL_BSTR_ERR;
r->slen = 0;
return _al_bsreadlna (r, s, terminator);
}
*
* Read a _al_bstring terminated by any character in the term string or the end
* of the stream from the _al_bStream (s) and return it into the parameter r.
* This function may read additional characters from the core stream that
* are not returned, but will be retained for subsequent read operations.
*/
int _al_bsreadlns (_al_bstring r, struct _al_bStream * s, _al_const_bstring term) {
if (s == NULL || s->buff == NULL || r == NULL || term == NULL
|| term->data == NULL || r->mlen <= 0) return _AL_BSTR_ERR;
if (term->slen == 1) return _al_bsreadln (r, s, term->data[0]);
if (term->slen < 1) return _AL_BSTR_ERR;
if (_AL_BSTR_OK != _al_balloc (s->buff, s->maxBuffSz + 1)) return _AL_BSTR_ERR;
r->slen = 0;
return _al_bsreadlnsa (r, s, term);
}
*
* Read a _al_bstring of length n (or, if it is fewer, as many bytes as is
* remaining) from the _al_bStream. This function may read additional
* characters from the core stream that are not returned, but will be
* retained for subsequent read operations. This function will not read
* additional characters from the core stream beyond virtual stream pointer.
*/
int _al_bsread (_al_bstring r, struct _al_bStream * s, int n) {
if (s == NULL || s->buff == NULL || r == NULL || r->mlen <= 0
|| n <= 0) return _AL_BSTR_ERR;
if (_AL_BSTR_OK != _al_balloc (s->buff, s->maxBuffSz + 1)) return _AL_BSTR_ERR;
r->slen = 0;
return _al_bsreada (r, s, n);
}
*
* Insert a _al_bstring into the _al_bStream at the current position. These
* characters will be read prior to those that actually come from the core
* stream.
*/
int _al_bsunread (struct _al_bStream * s, _al_const_bstring b) {
if (s == NULL || s->buff == NULL) return _AL_BSTR_ERR;
return _al_binsert (s->buff, 0, b, (unsigned char) '?');
}
*
* Return the currently buffered characters from the _al_bStream that will be
* read prior to reads from the core stream.
*/
int _al_bspeek (_al_bstring r, const struct _al_bStream * s) {
if (s == NULL || s->buff == NULL) return _AL_BSTR_ERR;
return _al_bassign (r, s->buff);
}
*
* Join the entries of a _al_bstrList into one _al_bstring by sequentially
* concatenating them with the sep string in between. If there is an error
* NULL is returned, otherwise a _al_bstring with the correct result is returned.
*/
_al_bstring _al_bjoin (const struct _al_bstrList * bl, _al_const_bstring sep) {
_al_bstring b;
int i, c, v;
if (bl == NULL || bl->qty < 0) return NULL;
if (sep != NULL && (sep->slen < 0 || sep->data == NULL)) return NULL;
for (i = 0, c = 1; i < bl->qty; i++) {
v = bl->entry[i]->slen;
if (v < 0) return NULL;
c += v;
if (c < 0) return NULL;
}
if (sep != NULL) c += (bl->qty - 1) * sep->slen;
b = (_al_bstring) bstr__alloc (sizeof (struct _al_tagbstring));
if (NULL == b) return NULL;
b->data = (unsigned char *) bstr__alloc (c);
if (b->data == NULL) {
bstr__free (b);
return NULL;
}
b->mlen = c;
b->slen = c-1;
for (i = 0, c = 0; i < bl->qty; i++) {
if (i > 0 && sep != NULL) {
bstr__memcpy (b->data + c, sep->data, sep->slen);
c += sep->slen;
}
v = bl->entry[i]->slen;
bstr__memcpy (b->data + c, bl->entry[i]->data, v);
c += v;
}
b->data[c] = (unsigned char) '\0';
return b;
}
#define BSSSC_BUFF_LEN (256)
* int (* cb) (void * parm, int ofs, _al_const_bstring entry), void * parm)
*
* Iterate the set of disjoint sequential substrings read from a stream
* divided by any of the characters in splitStr. An empty splitStr causes
* the whole stream to be iterated once.
*
* Note: At the point of calling the cb function, the _al_bStream pointer is
* pointed exactly at the position right after having read the split
* character. The cb function can act on the stream by causing the _al_bStream
* pointer to move, and _al_bssplitscb will continue by starting the next split
* at the position of the pointer after the return from cb.
*
* However, if the cb causes the _al_bStream s to be destroyed then the cb must
* return with a negative value, otherwise _al_bssplitscb will continue in an
* undefined manner.
*/
int _al_bssplitscb (struct _al_bStream * s, _al_const_bstring splitStr,
int (* cb) (void * parm, int ofs, _al_const_bstring entry), void * parm) {
struct charField chrs;
_al_bstring buff;
int i, p, ret;
if (cb == NULL || s == NULL || s->readFnPtr == NULL
|| splitStr == NULL || splitStr->slen < 0) return _AL_BSTR_ERR;
if (NULL == (buff = _al_bfromcstr (""))) return _AL_BSTR_ERR;
if (splitStr->slen == 0) {
while (_al_bsreada (buff, s, BSSSC_BUFF_LEN) >= 0) ;
if ((ret = cb (parm, 0, buff)) > 0)
ret = 0;
} else {
buildCharField (&chrs, splitStr);
ret = p = i = 0;
for (;;) {
if (i >= buff->slen) {
_al_bsreada (buff, s, BSSSC_BUFF_LEN);
if (i >= buff->slen) {
if (0 < (ret = cb (parm, p, buff))) ret = 0;
break;
}
}
if (testInCharField (&chrs, buff->data[i])) {
struct _al_tagbstring t;
unsigned char c;
_al_blk2tbstr (t, buff->data + i + 1, buff->slen - (i + 1));
if ((ret = _al_bsunread (s, &t)) < 0) break;
buff->slen = i;
c = buff->data[i];
buff->data[i] = (unsigned char) '\0';
if ((ret = cb (parm, p, buff)) < 0) break;
buff->data[i] = c;
buff->slen = 0;
p += i + 1;
i = -1;
}
i++;
}
}
_al_bdestroy (buff);
return ret;
}
* int (* cb) (void * parm, int ofs, _al_const_bstring entry), void * parm)
*
* Iterate the set of disjoint sequential substrings read from a stream
* divided by the entire substring splitStr. An empty splitStr causes
* each character of the stream to be iterated.
*
* Note: At the point of calling the cb function, the _al_bStream pointer is
* pointed exactly at the position right after having read the split
* character. The cb function can act on the stream by causing the _al_bStream
* pointer to move, and _al_bssplitscb will continue by starting the next split
* at the position of the pointer after the return from cb.
*
* However, if the cb causes the _al_bStream s to be destroyed then the cb must
* return with a negative value, otherwise _al_bssplitscb will continue in an
* undefined manner.
*/
int _al_bssplitstrcb (struct _al_bStream * s, _al_const_bstring splitStr,
int (* cb) (void * parm, int ofs, _al_const_bstring entry), void * parm) {
_al_bstring buff;
int i, p, ret;
if (cb == NULL || s == NULL || s->readFnPtr == NULL
|| splitStr == NULL || splitStr->slen < 0) return _AL_BSTR_ERR;
if (splitStr->slen == 1) return _al_bssplitscb (s, splitStr, cb, parm);
if (NULL == (buff = _al_bfromcstr (""))) return _AL_BSTR_ERR;
if (splitStr->slen == 0) {
for (i=0; _al_bsreada (buff, s, BSSSC_BUFF_LEN) >= 0; i++) {
if ((ret = cb (parm, 0, buff)) < 0) {
_al_bdestroy (buff);
return ret;
}
buff->slen = 0;
}
return _AL_BSTR_OK;
} else {
ret = p = i = 0;
for (i=p=0;;) {
if ((ret = _al_binstr (buff, 0, splitStr)) >= 0) {
struct _al_tagbstring t;
_al_blk2tbstr (t, buff->data, ret);
i = ret + splitStr->slen;
if ((ret = cb (parm, p, &t)) < 0) break;
p += i;
_al_bdelete (buff, 0, i);
} else {
_al_bsreada (buff, s, BSSSC_BUFF_LEN);
if (_al_bseof (s)) {
if ((ret = cb (parm, p, buff)) > 0) ret = 0;
break;
}
}
}
}
_al_bdestroy (buff);
return ret;
}
*
* Create a _al_bstrList.
*/
struct _al_bstrList * _al_bstrListCreate (void) {
struct _al_bstrList * sl = (struct _al_bstrList *) bstr__alloc (sizeof (struct _al_bstrList));
if (sl) {
sl->entry = (_al_bstring *) bstr__alloc (1*sizeof (_al_bstring));
if (!sl->entry) {
bstr__free (sl);
sl = NULL;
} else {
sl->qty = 0;
sl->mlen = 1;
}
}
return sl;
}
*
* Destroy a _al_bstrList that has been created by _al_bsplit, _al_bsplits or _al_bstrListCreate.
*/
int _al_bstrListDestroy (struct _al_bstrList * sl) {
int i;
if (sl == NULL || sl->qty < 0) return _AL_BSTR_ERR;
for (i=0; i < sl->qty; i++) {
if (sl->entry[i]) {
_al_bdestroy (sl->entry[i]);
sl->entry[i] = NULL;
}
}
sl->qty = -1;
sl->mlen = -1;
bstr__free (sl->entry);
sl->entry = NULL;
bstr__free (sl);
return _AL_BSTR_OK;
}
*
* Ensure that there is memory for at least msz number of entries for the
* list.
*/
int _al_bstrListAlloc (struct _al_bstrList * sl, int msz) {
_al_bstring * l;
int smsz;
size_t nsz;
if (!sl || msz <= 0 || !sl->entry || sl->qty < 0 || sl->mlen <= 0 || sl->qty > sl->mlen) return _AL_BSTR_ERR;
if (sl->mlen >= msz) return _AL_BSTR_OK;
smsz = snapUpSize (msz);
nsz = ((size_t) smsz) * sizeof (_al_bstring);
if (nsz < (size_t) smsz) return _AL_BSTR_ERR;
l = (_al_bstring *) bstr__realloc (sl->entry, nsz);
if (!l) {
smsz = msz;
nsz = ((size_t) smsz) * sizeof (_al_bstring);
l = (_al_bstring *) bstr__realloc (sl->entry, nsz);
if (!l) return _AL_BSTR_ERR;
}
sl->mlen = smsz;
sl->entry = l;
return _AL_BSTR_OK;
}
*
* Try to allocate the minimum amount of memory for the list to include at
* least msz entries or sl->qty whichever is greater.
*/
int _al_bstrListAllocMin (struct _al_bstrList * sl, int msz) {
_al_bstring * l;
size_t nsz;
if (!sl || msz <= 0 || !sl->entry || sl->qty < 0 || sl->mlen <= 0 || sl->qty > sl->mlen) return _AL_BSTR_ERR;
if (msz < sl->qty) msz = sl->qty;
if (sl->mlen == msz) return _AL_BSTR_OK;
nsz = ((size_t) msz) * sizeof (_al_bstring);
if (nsz < (size_t) msz) return _AL_BSTR_ERR;
l = (_al_bstring *) bstr__realloc (sl->entry, nsz);
if (!l) return _AL_BSTR_ERR;
sl->mlen = msz;
sl->entry = l;
return _AL_BSTR_OK;
}
* int (* cb) (void * parm, int ofs, int len), void * parm)
*
* Iterate the set of disjoint sequential substrings over str divided by the
* character in splitChar.
*
* Note: Non-destructive modification of str from within the cb function
* while performing this split is not undefined. _al_bsplitcb behaves in
* sequential lock step with calls to cb. I.e., after returning from a cb
* that return a non-negative integer, _al_bsplitcb continues from the position
* 1 character after the last detected split character and it will halt
* immediately if the length of str falls below this point. However, if the
* cb function destroys str, then it *must* return with a negative value,
* otherwise _al_bsplitcb will continue in an undefined manner.
*/
int _al_bsplitcb (_al_const_bstring str, unsigned char splitChar, int pos,
int (* cb) (void * parm, int ofs, int len), void * parm) {
int i, p, ret;
if (cb == NULL || str == NULL || pos < 0 || pos > str->slen)
return _AL_BSTR_ERR;
p = pos;
do {
for (i=p; i < str->slen; i++) {
if (str->data[i] == splitChar) break;
}
if ((ret = cb (parm, p, i - p)) < 0) return ret;
p = i + 1;
} while (p <= str->slen);
return _AL_BSTR_OK;
}
* int (* cb) (void * parm, int ofs, int len), void * parm)
*
* Iterate the set of disjoint sequential substrings over str divided by any
* of the characters in splitStr. An empty splitStr causes the whole str to
* be iterated once.
*
* Note: Non-destructive modification of str from within the cb function
* while performing this split is not undefined. _al_bsplitscb behaves in
* sequential lock step with calls to cb. I.e., after returning from a cb
* that return a non-negative integer, _al_bsplitscb continues from the position
* 1 character after the last detected split character and it will halt
* immediately if the length of str falls below this point. However, if the
* cb function destroys str, then it *must* return with a negative value,
* otherwise _al_bsplitscb will continue in an undefined manner.
*/
int _al_bsplitscb (_al_const_bstring str, _al_const_bstring splitStr, int pos,
int (* cb) (void * parm, int ofs, int len), void * parm) {
struct charField chrs;
int i, p, ret;
if (cb == NULL || str == NULL || pos < 0 || pos > str->slen
|| splitStr == NULL || splitStr->slen < 0) return _AL_BSTR_ERR;
if (splitStr->slen == 0) {
if ((ret = cb (parm, 0, str->slen)) > 0) ret = 0;
return ret;
}
if (splitStr->slen == 1)
return _al_bsplitcb (str, splitStr->data[0], pos, cb, parm);
buildCharField (&chrs, splitStr);
p = pos;
do {
for (i=p; i < str->slen; i++) {
if (testInCharField (&chrs, str->data[i])) break;
}
if ((ret = cb (parm, p, i - p)) < 0) return ret;
p = i + 1;
} while (p <= str->slen);
return _AL_BSTR_OK;
}
* int (* cb) (void * parm, int ofs, int len), void * parm)
*
* Iterate the set of disjoint sequential substrings over str divided by the
* substring splitStr. An empty splitStr causes the whole str to be
* iterated once.
*
* Note: Non-destructive modification of str from within the cb function
* while performing this split is not undefined. _al_bsplitstrcb behaves in
* sequential lock step with calls to cb. I.e., after returning from a cb
* that return a non-negative integer, _al_bsplitscb continues from the position
* 1 character after the last detected split character and it will halt
* immediately if the length of str falls below this point. However, if the
* cb function destroys str, then it *must* return with a negative value,
* otherwise _al_bsplitscb will continue in an undefined manner.
*/
int _al_bsplitstrcb (_al_const_bstring str, _al_const_bstring splitStr, int pos,
int (* cb) (void * parm, int ofs, int len), void * parm) {
int i, p, ret;
if (cb == NULL || str == NULL || pos < 0 || pos > str->slen
|| splitStr == NULL || splitStr->slen < 0) return _AL_BSTR_ERR;
if (0 == splitStr->slen) {
for (i=pos; i < str->slen; i++) {
if ((ret = cb (parm, i, 1)) < 0) return ret;
}
return _AL_BSTR_OK;
}
if (splitStr->slen == 1)
return _al_bsplitcb (str, splitStr->data[0], pos, cb, parm);
for (i=p=pos; i <= str->slen - splitStr->slen; i++) {
if (0 == bstr__memcmp (splitStr->data, str->data + i, splitStr->slen)) {
if ((ret = cb (parm, p, i - p)) < 0) return ret;
i += splitStr->slen;
p = i;
}
}
if ((ret = cb (parm, p, str->slen - p)) < 0) return ret;
return _AL_BSTR_OK;
}
struct genBstrList {
_al_bstring b;
struct _al_bstrList * bl;
};
static int bscb (void * parm, int ofs, int len) {
struct genBstrList * g = (struct genBstrList *) parm;
if (g->bl->qty >= g->bl->mlen) {
int mlen = g->bl->mlen * 2;
_al_bstring * tbl;
while (g->bl->qty >= mlen) {
if (mlen < g->bl->mlen) return _AL_BSTR_ERR;
mlen += mlen;
}
tbl = (_al_bstring *) bstr__realloc (g->bl->entry, sizeof (_al_bstring) * mlen);
if (tbl == NULL) return _AL_BSTR_ERR;
g->bl->entry = tbl;
g->bl->mlen = mlen;
}
g->bl->entry[g->bl->qty] = _al_bmidstr (g->b, ofs, len);
g->bl->qty++;
return _AL_BSTR_OK;
}
*
* Create an array of sequential substrings from str divided by the character
* splitChar.
*/
struct _al_bstrList * _al_bsplit (_al_const_bstring str, unsigned char splitChar) {
struct genBstrList g;
if (str == NULL || str->data == NULL || str->slen < 0) return NULL;
g.bl = (struct _al_bstrList *) bstr__alloc (sizeof (struct _al_bstrList));
if (g.bl == NULL) return NULL;
g.bl->mlen = 4;
g.bl->entry = (_al_bstring *) bstr__alloc (g.bl->mlen * sizeof (_al_bstring));
if (NULL == g.bl->entry) {
bstr__free (g.bl);
return NULL;
}
g.b = (_al_bstring) str;
g.bl->qty = 0;
if (_al_bsplitcb (str, splitChar, 0, bscb, &g) < 0) {
_al_bstrListDestroy (g.bl);
return NULL;
}
return g.bl;
}
*
* Create an array of sequential substrings from str divided by the entire
* substring splitStr.
*/
struct _al_bstrList * _al_bsplitstr (_al_const_bstring str, _al_const_bstring splitStr) {
struct genBstrList g;
if (str == NULL || str->data == NULL || str->slen < 0) return NULL;
g.bl = (struct _al_bstrList *) bstr__alloc (sizeof (struct _al_bstrList));
if (g.bl == NULL) return NULL;
g.bl->mlen = 4;
g.bl->entry = (_al_bstring *) bstr__alloc (g.bl->mlen * sizeof (_al_bstring));
if (NULL == g.bl->entry) {
bstr__free (g.bl);
return NULL;
}
g.b = (_al_bstring) str;
g.bl->qty = 0;
if (_al_bsplitstrcb (str, splitStr, 0, bscb, &g) < 0) {
_al_bstrListDestroy (g.bl);
return NULL;
}
return g.bl;
}
*
* Create an array of sequential substrings from str divided by any of the
* characters in splitStr. An empty splitStr causes a single entry _al_bstrList
* containing a copy of str to be returned.
*/
struct _al_bstrList * _al_bsplits (_al_const_bstring str, _al_const_bstring splitStr) {
struct genBstrList g;
if ( str == NULL || str->slen < 0 || str->data == NULL ||
splitStr == NULL || splitStr->slen < 0 || splitStr->data == NULL)
return NULL;
g.bl = (struct _al_bstrList *) bstr__alloc (sizeof (struct _al_bstrList));
if (g.bl == NULL) return NULL;
g.bl->mlen = 4;
g.bl->entry = (_al_bstring *) bstr__alloc (g.bl->mlen * sizeof (_al_bstring));
if (NULL == g.bl->entry) {
bstr__free (g.bl);
return NULL;
}
g.b = (_al_bstring) str;
g.bl->qty = 0;
if (_al_bsplitscb (str, splitStr, 0, bscb, &g) < 0) {
_al_bstrListDestroy (g.bl);
return NULL;
}
return g.bl;
}
#if defined (__TURBOC__) && !defined (__BORLANDC__)
# ifndef BSTRLIB_NOVSNP
# define BSTRLIB_NOVSNP
# endif
#endif
#if defined(__WATCOMC__) || defined(_MSC_VER)
#define exvsnprintf(r,b,n,f,a) {r = _vsnprintf (b,n,f,a);}
#else
#ifdef BSTRLIB_NOVSNP
not recommended that _al_bformat be used at all. */
#define exvsnprintf(r,b,n,f,a) {vsprintf (b,f,a); r = -1;}
#define START_VSNBUFF (256)
#else
#ifdef __GNUC__
I've just gone ahead and put it in. */
* macro that calls compiler builtins sometimes, e.g. Mac OS X 10.6.
*/
extern int vsnprintf (char *buf, size_t count, const char *format, va_list arg);
*/
#endif
#define exvsnprintf(r,b,n,f,a) {r = vsnprintf (b,n,f,a);}
#endif
#endif
#if !defined (BSTRLIB_NOVSNP)
#ifndef START_VSNBUFF
#define START_VSNBUFF (16)
#endif
buffer, WATCOM and MSVC both return -1, while C99 requires that the
returned value be exactly what the length would be if the buffer would be
large enough. This leads to the idea that if the return value is larger
than n, then changing n to the return value will reduce the number of
iterations required. */
*
* After the first parameter, it takes the same parameters as printf (), but
* rather than outputting results to stdio, it appends the results to
* a _al_bstring which contains what would have been output. Note that if there
* is an early generation of a '\0' character, the _al_bstring will be truncated
* to this end point.
*/
int _al_bformata (_al_bstring b, const char * fmt, ...) {
va_list arglist;
_al_bstring buff;
int n, r;
if (b == NULL || fmt == NULL || b->data == NULL || b->mlen <= 0
|| b->slen < 0 || b->slen > b->mlen) return _AL_BSTR_ERR;
performed using the truncating "vsnprintf" call (to avoid buffer
overflows) on increasing potential sizes for the output result. */
if ((n = (int) (2*strlen (fmt))) < START_VSNBUFF) n = START_VSNBUFF;
if (NULL == (buff = _al_bfromcstralloc (n + 2, ""))) {
n = 1;
if (NULL == (buff = _al_bfromcstralloc (n + 2, ""))) return _AL_BSTR_ERR;
}
for (;;) {
va_start (arglist, fmt);
exvsnprintf (r, (char *) buff->data, n + 1, fmt, arglist);
va_end (arglist);
buff->data[n] = (unsigned char) '\0';
buff->slen = (int) (strlen) ((char *) buff->data);
if (buff->slen < n) break;
if (r > n) n = r; else n += n;
if (_AL_BSTR_OK != _al_balloc (buff, n + 2)) {
_al_bdestroy (buff);
return _AL_BSTR_ERR;
}
}
r = _al_bconcat (b, buff);
_al_bdestroy (buff);
return r;
}
*
* After the first parameter, it takes the same parameters as printf (), but
* rather than outputting results to stdio, it outputs the results to
* the _al_bstring parameter b. Note that if there is an early generation of a
* '\0' character, the _al_bstring will be truncated to this end point.
*/
int _al_bassignformat (_al_bstring b, const char * fmt, ...) {
va_list arglist;
_al_bstring buff;
int n, r;
if (b == NULL || fmt == NULL || b->data == NULL || b->mlen <= 0
|| b->slen < 0 || b->slen > b->mlen) return _AL_BSTR_ERR;
performed using the truncating "vsnprintf" call (to avoid buffer
overflows) on increasing potential sizes for the output result. */
if ((n = (int) (2*strlen (fmt))) < START_VSNBUFF) n = START_VSNBUFF;
if (NULL == (buff = _al_bfromcstralloc (n + 2, ""))) {
n = 1;
if (NULL == (buff = _al_bfromcstralloc (n + 2, ""))) return _AL_BSTR_ERR;
}
for (;;) {
va_start (arglist, fmt);
exvsnprintf (r, (char *) buff->data, n + 1, fmt, arglist);
va_end (arglist);
buff->data[n] = (unsigned char) '\0';
buff->slen = (int) (strlen) ((char *) buff->data);
if (buff->slen < n) break;
if (r > n) n = r; else n += n;
if (_AL_BSTR_OK != _al_balloc (buff, n + 2)) {
_al_bdestroy (buff);
return _AL_BSTR_ERR;
}
}
r = _al_bassign (b, buff);
_al_bdestroy (buff);
return r;
}
*
* Takes the same parameters as printf (), but rather than outputting results
* to stdio, it forms a _al_bstring which contains what would have been output.
* Note that if there is an early generation of a '\0' character, the
* _al_bstring will be truncated to this end point.
*/
_al_bstring _al_bformat (const char * fmt, ...) {
va_list arglist;
_al_bstring buff;
int n, r;
if (fmt == NULL) return NULL;
performed using the truncating "vsnprintf" call (to avoid buffer
overflows) on increasing potential sizes for the output result. */
if ((n = (int) (2*strlen (fmt))) < START_VSNBUFF) n = START_VSNBUFF;
if (NULL == (buff = _al_bfromcstralloc (n + 2, ""))) {
n = 1;
if (NULL == (buff = _al_bfromcstralloc (n + 2, ""))) return NULL;
}
for (;;) {
va_start (arglist, fmt);
exvsnprintf (r, (char *) buff->data, n + 1, fmt, arglist);
va_end (arglist);
buff->data[n] = (unsigned char) '\0';
buff->slen = (int) (strlen) ((char *) buff->data);
if (buff->slen < n) break;
if (r > n) n = r; else n += n;
if (_AL_BSTR_OK != _al_balloc (buff, n + 2)) {
_al_bdestroy (buff);
return NULL;
}
}
return buff;
}
*
* The _al_bvcformata function formats data under control of the format control
* string fmt and attempts to append the result to b. The fmt parameter is
* the same as that of the printf function. The variable argument list is
* replaced with arglist, which has been initialized by the va_start macro.
* The size of the output is upper bounded by count. If the required output
* exceeds count, the string b is not augmented with any contents and a value
* below _AL_BSTR_ERR is returned. If a value below -count is returned then it
* is recommended that the negative of this value be used as an update to the
* count in a subsequent pass. On other errors, such as running out of
* memory, parameter errors or numeric wrap around _AL_BSTR_ERR is returned.
* _AL_BSTR_OK is returned when the output is successfully generated and
* appended to b.
*
* Note: There is no sanity checking of arglist, and this function is
* destructive of the contents of b from the b->slen point onward. If there
* is an early generation of a '\0' character, the _al_bstring will be truncated
* to this end point.
*/
int _al_bvcformata (_al_bstring b, int count, const char * fmt, va_list arg) {
int n, r, l;
if (b == NULL || fmt == NULL || count <= 0 || b->data == NULL
|| b->mlen <= 0 || b->slen < 0 || b->slen > b->mlen) return _AL_BSTR_ERR;
if (count > (n = b->slen + count) + 2) return _AL_BSTR_ERR;
if (_AL_BSTR_OK != _al_balloc (b, n + 2)) return _AL_BSTR_ERR;
exvsnprintf (r, (char *) b->data + b->slen, count + 2, fmt, arg);
if (n >= (l = b->slen + (int) (strlen) ((const char *) b->data + b->slen))) {
b->slen = l;
return _AL_BSTR_OK;
}
tries to help set what the retry length should be. */
b->data[b->slen] = '\0';
if (r > count+1) l = r; else {
l = count+count;
if (count > l) l = INT_MAX;
}
n = -l;
if (n > _AL_BSTR_ERR-1) n = _AL_BSTR_ERR-1;
return n;
}
#endif