/* * This source file is part of the bstring string library. This code was * written by Paul Hsieh in 2002-2015, and is covered by the BSD open source * license and the GPL. Refer to the accompanying documentation for details * on usage and license. */ /* * bstraux.c * * This file is not necessarily part of the core bstring library itself, but * is just an auxilliary module which includes miscellaneous or trivial * functions. */ #if defined (_MSC_VER) # define _CRT_SECURE_NO_WARNINGS #endif #include #include #include #include #include #include "bstrlib.h" #include "bstraux.h" #ifndef UNUSED #define UNUSED(x) (void)(x) #endif /* bstring bTail (bstring b, int n) * * Return with a string of the last n characters of b. */ bstring bTail (bstring b, int n) { if (b == NULL || n < 0 || (b->mlen < b->slen && b->mlen > 0)) return NULL; if (n >= b->slen) return bstrcpy (b); return bmidstr (b, b->slen - n, n); } /* bstring bHead (bstring b, int n) * * Return with a string of the first n characters of b. */ bstring bHead (bstring b, int n) { if (b == NULL || n < 0 || (b->mlen < b->slen && b->mlen > 0)) return NULL; if (n >= b->slen) return bstrcpy (b); return bmidstr (b, 0, n); } /* int bFill (bstring a, char c, int len) * * Fill a given bstring with the character in parameter c, for a length n. */ int bFill (bstring b, char c, int len) { if (b == NULL || len < 0 || (b->mlen < b->slen && b->mlen > 0)) return -__LINE__; b->slen = 0; return bsetstr (b, len, NULL, c); } /* int bReplicate (bstring b, int n) * * Replicate the contents of b end to end n times and replace it in b. */ int bReplicate (bstring b, int n) { return bpattern (b, n * b->slen); } /* int bReverse (bstring b) * * Reverse the contents of b in place. */ int bReverse (bstring b) { int i, n, m; unsigned char t; if (b == NULL || b->slen < 0 || b->mlen < b->slen) return -__LINE__; n = b->slen; if (2 <= n) { m = ((unsigned)n) >> 1; n--; for (i=0; i < m; i++) { t = b->data[n - i]; b->data[n - i] = b->data[i]; b->data[i] = t; } } return 0; } /* int bInsertChrs (bstring b, int pos, int len, unsigned char c, unsigned char fill) * * Insert a repeated sequence of a given character into the string at * position pos for a length len. */ int bInsertChrs (bstring b, int pos, int len, unsigned char c, unsigned char fill) { if (b == NULL || b->slen < 0 || b->mlen < b->slen || pos < 0 || len <= 0) return -__LINE__; if (pos > b->slen && 0 > bsetstr (b, pos, NULL, fill)) return -__LINE__; if (0 > balloc (b, b->slen + len)) return -__LINE__; if (pos < b->slen) memmove (b->data + pos + len, b->data + pos, b->slen - pos); memset (b->data + pos, c, len); b->slen += len; b->data[b->slen] = (unsigned char) '\0'; return BSTR_OK; } /* int bJustifyLeft (bstring b, int space) * * Left justify a string. */ int bJustifyLeft (bstring b, int space) { int j, i, s, t; unsigned char c = (unsigned char) space; if (b == NULL || b->slen < 0 || b->mlen < b->slen) return -__LINE__; if (space != (int) c) return BSTR_OK; for (s=j=i=0; i < b->slen; i++) { t = s; s = c != (b->data[j] = b->data[i]); j += (t|s); } if (j > 0 && b->data[j-1] == c) j--; b->data[j] = (unsigned char) '\0'; b->slen = j; return BSTR_OK; } /* int bJustifyRight (bstring b, int width, int space) * * Right justify a string to within a given width. */ int bJustifyRight (bstring b, int width, int space) { int ret; if (width <= 0) return -__LINE__; if (0 > (ret = bJustifyLeft (b, space))) return ret; if (b->slen <= width) return bInsertChrs (b, 0, width - b->slen, (unsigned char) space, (unsigned char) space); return BSTR_OK; } /* int bJustifyCenter (bstring b, int width, int space) * * Center a string's non-white space characters to within a given width by * inserting whitespaces at the beginning. */ int bJustifyCenter (bstring b, int width, int space) { int ret; if (width <= 0) return -__LINE__; if (0 > (ret = bJustifyLeft (b, space))) return ret; if (b->slen <= width) return bInsertChrs (b, 0, (width - b->slen + 1) >> 1, (unsigned char) space, (unsigned char) space); return BSTR_OK; } /* int bJustifyMargin (bstring b, int width, int space) * * Stretch a string to flush against left and right margins by evenly * distributing additional white space between words. If the line is too * long to be margin justified, it is left justified. */ int bJustifyMargin (bstring b, int width, int space) { struct bstrList * sl; int i, l, c; if (b == NULL || b->slen < 0 || b->mlen == 0 || b->mlen < b->slen) return -__LINE__; if (NULL == (sl = bsplit (b, (unsigned char) space))) return -__LINE__; for (l=c=i=0; i < sl->qty; i++) { if (sl->entry[i]->slen > 0) { c ++; l += sl->entry[i]->slen; } } if (l + c >= width || c < 2) { bstrListDestroy (sl); return bJustifyLeft (b, space); } b->slen = 0; for (i=0; i < sl->qty; i++) { if (sl->entry[i]->slen > 0) { if (b->slen > 0) { int s = (width - l + (c / 2)) / c; bInsertChrs (b, b->slen, s, (unsigned char) space, (unsigned char) space); l += s; } bconcat (b, sl->entry[i]); c--; if (c <= 0) break; } } bstrListDestroy (sl); return BSTR_OK; } static size_t readNothing (void *buff, size_t elsize, size_t nelem, void *parm) { UNUSED(buff); UNUSED(elsize); UNUSED(nelem); UNUSED(parm); return 0; /* Immediately indicate EOF. */ } /* struct bStream * bsFromBstr (const_bstring b); * * Create a bStream whose contents are a copy of the bstring passed in. * This allows the use of all the bStream APIs with bstrings. */ struct bStream * bsFromBstr (const_bstring b) { struct bStream * s = bsopen ((bNread) readNothing, NULL); bsunread (s, b); /* Push the bstring data into the empty bStream. */ return s; } static size_t readRef (void *buff, size_t elsize, size_t nelem, void *parm) { struct tagbstring * t = (struct tagbstring *) parm; size_t tsz = elsize * nelem; if (tsz > (size_t) t->slen) tsz = (size_t) t->slen; if (tsz > 0) { memcpy (buff, t->data, tsz); t->slen -= (int) tsz; t->data += tsz; return tsz / elsize; } return 0; } /* The "by reference" version of the above function. This function puts * a number of restrictions on the call site (the passed in struct * tagbstring *will* be modified by this function, and the source data * must remain alive and constant for the lifetime of the bStream). * Hence it is not presented as an extern. */ static struct bStream * bsFromBstrRef (struct tagbstring * t) { if (!t) return NULL; return bsopen ((bNread) readRef, t); } /* char * bStr2NetStr (const_bstring b) * * Convert a bstring to a netstring. See * http://cr.yp.to/proto/netstrings.txt for a description of netstrings. * Note: 1) The value returned should be freed with a call to bcstrfree() at * the point when it will no longer be referenced to avoid a memory * leak. * 2) If the returned value is non-NULL, then it also '\0' terminated * in the character position one past the "," terminator. */ char * bStr2NetStr (const_bstring b) { char strnum[sizeof (b->slen) * 3 + 1]; bstring s; unsigned char * buff; if (b == NULL || b->data == NULL || b->slen < 0) return NULL; sprintf (strnum, "%d:", b->slen); if (NULL == (s = bfromcstr (strnum)) || bconcat (s, b) == BSTR_ERR || bconchar (s, (char) ',') == BSTR_ERR) { bdestroy (s); return NULL; } buff = s->data; bcstrfree ((char *) s); return (char *) buff; } /* bstring bNetStr2Bstr (const char * buf) * * Convert a netstring to a bstring. See * http://cr.yp.to/proto/netstrings.txt for a description of netstrings. * Note that the terminating "," *must* be present, however a following '\0' * is *not* required. */ bstring bNetStr2Bstr (const char * buff) { int i, x; bstring b; if (buff == NULL) return NULL; x = 0; for (i=0; buff[i] != ':'; i++) { unsigned int v = buff[i] - '0'; if (v > 9 || x > ((INT_MAX - (signed int)v) / 10)) return NULL; x = (x * 10) + v; } /* This thing has to be properly terminated */ if (buff[i + 1 + x] != ',') return NULL; if (NULL == (b = bfromcstr (""))) return NULL; if (balloc (b, x + 1) != BSTR_OK) { bdestroy (b); return NULL; } memcpy (b->data, buff + i + 1, x); b->data[x] = (unsigned char) '\0'; b->slen = x; return b; } static char b64ETable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; /* bstring bBase64Encode (const_bstring b) * * Generate a base64 encoding. See: RFC1341 */ bstring bBase64Encode (const_bstring b) { int i, c0, c1, c2, c3; bstring out; if (b == NULL || b->slen < 0 || b->data == NULL) return NULL; out = bfromcstr (""); for (i=0; i + 2 < b->slen; i += 3) { if (i && ((i % 57) == 0)) { if (bconchar (out, (char) '\015') < 0 || bconchar (out, (char) '\012') < 0) { bdestroy (out); return NULL; } } c0 = b->data[i] >> 2; c1 = ((b->data[i] << 4) | (b->data[i+1] >> 4)) & 0x3F; c2 = ((b->data[i+1] << 2) | (b->data[i+2] >> 6)) & 0x3F; c3 = b->data[i+2] & 0x3F; if (bconchar (out, b64ETable[c0]) < 0 || bconchar (out, b64ETable[c1]) < 0 || bconchar (out, b64ETable[c2]) < 0 || bconchar (out, b64ETable[c3]) < 0) { bdestroy (out); return NULL; } } if (i && ((i % 57) == 0)) { if (bconchar (out, (char) '\015') < 0 || bconchar (out, (char) '\012') < 0) { bdestroy (out); return NULL; } } switch (i + 2 - b->slen) { case 0: c0 = b->data[i] >> 2; c1 = ((b->data[i] << 4) | (b->data[i+1] >> 4)) & 0x3F; c2 = (b->data[i+1] << 2) & 0x3F; if (bconchar (out, b64ETable[c0]) < 0 || bconchar (out, b64ETable[c1]) < 0 || bconchar (out, b64ETable[c2]) < 0 || bconchar (out, (char) '=') < 0) { bdestroy (out); return NULL; } break; case 1: c0 = b->data[i] >> 2; c1 = (b->data[i] << 4) & 0x3F; if (bconchar (out, b64ETable[c0]) < 0 || bconchar (out, b64ETable[c1]) < 0 || bconchar (out, (char) '=') < 0 || bconchar (out, (char) '=') < 0) { bdestroy (out); return NULL; } break; case 2: break; } return out; } #define B64_PAD (-2) #define B64_ERR (-1) static int base64DecodeSymbol (unsigned char alpha) { if ((alpha >= 'A') && (alpha <= 'Z')) return (int)(alpha - 'A'); else if ((alpha >= 'a') && (alpha <= 'z')) return 26 + (int)(alpha - 'a'); else if ((alpha >= '0') && (alpha <= '9')) return 52 + (int)(alpha - '0'); else if (alpha == '+') return 62; else if (alpha == '/') return 63; else if (alpha == '=') return B64_PAD; else return B64_ERR; } /* bstring bBase64DecodeEx (const_bstring b, int * boolTruncError) * * Decode a base64 block of data. All MIME headers are assumed to have been * removed. See: RFC1341 */ bstring bBase64DecodeEx (const_bstring b, int * boolTruncError) { int i, v; unsigned char c0, c1, c2; bstring out; if (b == NULL || b->slen < 0 || b->data == NULL) return NULL; if (boolTruncError) *boolTruncError = 0; out = bfromcstr (""); i = 0; for (;;) { do { if (i >= b->slen) return out; if (b->data[i] == '=') { /* Bad "too early" truncation */ if (boolTruncError) { *boolTruncError = 1; return out; } bdestroy (out); return NULL; } v = base64DecodeSymbol (b->data[i]); i++; } while (v < 0); c0 = (unsigned char) (v << 2); do { if (i >= b->slen || b->data[i] == '=') { /* Bad "too early" truncation */ if (boolTruncError) { *boolTruncError = 1; return out; } bdestroy (out); return NULL; } v = base64DecodeSymbol (b->data[i]); i++; } while (v < 0); c0 |= (unsigned char) (v >> 4); c1 = (unsigned char) (v << 4); do { if (i >= b->slen) { if (boolTruncError) { *boolTruncError = 1; return out; } bdestroy (out); return NULL; } if (b->data[i] == '=') { i++; if (i >= b->slen || b->data[i] != '=' || bconchar (out, c0) < 0) { if (boolTruncError) { *boolTruncError = 1; return out; } bdestroy (out); /* Missing "=" at the end. */ return NULL; } return out; } v = base64DecodeSymbol (b->data[i]); i++; } while (v < 0); c1 |= (unsigned char) (v >> 2); c2 = (unsigned char) (v << 6); do { if (i >= b->slen) { if (boolTruncError) { *boolTruncError = 1; return out; } bdestroy (out); return NULL; } if (b->data[i] == '=') { if (bconchar (out, c0) < 0 || bconchar (out, c1) < 0) { if (boolTruncError) { *boolTruncError = 1; return out; } bdestroy (out); return NULL; } if (boolTruncError) *boolTruncError = 0; return out; } v = base64DecodeSymbol (b->data[i]); i++; } while (v < 0); c2 |= (unsigned char) (v); if (bconchar (out, c0) < 0 || bconchar (out, c1) < 0 || bconchar (out, c2) < 0) { if (boolTruncError) { *boolTruncError = -1; return out; } bdestroy (out); return NULL; } } } #define UU_DECODE_BYTE(b) (((b) == (signed int)'`') ? 0 : (b) - (signed int)' ') struct bUuInOut { bstring src, dst; int * badlines; }; #define UU_MAX_LINELEN 45 static int bUuDecLine (void * parm, int ofs, int len) { struct bUuInOut * io = (struct bUuInOut *) parm; bstring s = io->src; bstring t = io->dst; int i, llen, otlen, ret, c0, c1, c2, c3, d0, d1, d2, d3; if (len == 0) return 0; llen = UU_DECODE_BYTE (s->data[ofs]); ret = 0; otlen = t->slen; if (((unsigned) llen) > UU_MAX_LINELEN) { ret = -__LINE__; goto bl; } llen += t->slen; for (i=1; i < s->slen && t->slen < llen;i += 4) { unsigned char outoctet[3]; c0 = UU_DECODE_BYTE (d0 = (int) bchare (s, i+ofs+0, ' ' - 1)); c1 = UU_DECODE_BYTE (d1 = (int) bchare (s, i+ofs+1, ' ' - 1)); c2 = UU_DECODE_BYTE (d2 = (int) bchare (s, i+ofs+2, ' ' - 1)); c3 = UU_DECODE_BYTE (d3 = (int) bchare (s, i+ofs+3, ' ' - 1)); if (((unsigned) (c0|c1) >= 0x40)) { if (!ret) ret = -__LINE__; if (d0 > 0x60 || (d0 < (' ' - 1) && !isspace (d0)) || d1 > 0x60 || (d1 < (' ' - 1) && !isspace (d1))) { t->slen = otlen; goto bl; } c0 = c1 = 0; } outoctet[0] = (unsigned char) ((c0 << 2) | ((unsigned) c1 >> 4)); if (t->slen+1 >= llen) { if (0 > bconchar (t, (char) outoctet[0])) return -__LINE__; break; } if ((unsigned) c2 >= 0x40) { if (!ret) ret = -__LINE__; if (d2 > 0x60 || (d2 < (' ' - 1) && !isspace (d2))) { t->slen = otlen; goto bl; } c2 = 0; } outoctet[1] = (unsigned char) ((c1 << 4) | ((unsigned) c2 >> 2)); if (t->slen+2 >= llen) { if (0 > bcatblk (t, outoctet, 2)) return -__LINE__; break; } if ((unsigned) c3 >= 0x40) { if (!ret) ret = -__LINE__; if (d3 > 0x60 || (d3 < (' ' - 1) && !isspace (d3))) { t->slen = otlen; goto bl; } c3 = 0; } outoctet[2] = (unsigned char) ((c2 << 6) | ((unsigned) c3)); if (0 > bcatblk (t, outoctet, 3)) return -__LINE__; } if (t->slen < llen) { if (0 == ret) ret = -__LINE__; t->slen = otlen; } bl:; if (ret && io->badlines) { (*io->badlines)++; return 0; } return ret; } /* bstring bUuDecodeEx (const_bstring src, int * badlines) * * Performs a UUDecode of a block of data. If there are errors in the * decoding, they are counted up and returned in "badlines", if badlines is * not NULL. It is assumed that the "begin" and "end" lines have already * been stripped off. The potential security problem of writing the * filename in the begin line is something that is beyond the scope of a * portable library. */ #ifdef _MSC_VER #pragma warning(disable:4204) #endif bstring bUuDecodeEx (const_bstring src, int * badlines) { struct tagbstring t; struct bStream * s; struct bStream * d; bstring b; if (!src) return NULL; t = *src; /* Short lifetime alias to header of src */ s = bsFromBstrRef (&t); /* t is undefined after this */ if (!s) return NULL; d = bsUuDecode (s, badlines); b = bfromcstralloc (256, ""); if (NULL == b || 0 > bsread (b, d, INT_MAX)) { bdestroy (b); b = NULL; } bsclose (d); bsclose (s); return b; } struct bsUuCtx { struct bUuInOut io; struct bStream * sInp; }; static size_t bsUuDecodePart (void *buff, size_t elsize, size_t nelem, void *parm) { static struct tagbstring eol = bsStatic ("\r\n"); struct bsUuCtx * luuCtx = (struct bsUuCtx *) parm; size_t tsz; int l, lret; if (NULL == buff || NULL == parm) return 0; tsz = elsize * nelem; CheckInternalBuffer:; /* If internal buffer has sufficient data, just output it */ if (((size_t) luuCtx->io.dst->slen) > tsz) { memcpy (buff, luuCtx->io.dst->data, tsz); bdelete (luuCtx->io.dst, 0, (int) tsz); return nelem; } DecodeMore:; if (0 <= (l = binchr (luuCtx->io.src, 0, &eol))) { int ol = 0; struct tagbstring t; bstring s = luuCtx->io.src; luuCtx->io.src = &t; do { if (l > ol) { bmid2tbstr (t, s, ol, l - ol); lret = bUuDecLine (&luuCtx->io, 0, t.slen); if (0 > lret) { luuCtx->io.src = s; goto Done; } } ol = l + 1; if (((size_t) luuCtx->io.dst->slen) > tsz) break; l = binchr (s, ol, &eol); } while (BSTR_ERR != l); bdelete (s, 0, ol); luuCtx->io.src = s; goto CheckInternalBuffer; } if (BSTR_ERR != bsreada (luuCtx->io.src, luuCtx->sInp, bsbufflength (luuCtx->sInp, BSTR_BS_BUFF_LENGTH_GET))) { goto DecodeMore; } bUuDecLine (&luuCtx->io, 0, luuCtx->io.src->slen); Done:; /* Output any lingering data that has been translated */ if (((size_t) luuCtx->io.dst->slen) > 0) { if (((size_t) luuCtx->io.dst->slen) > tsz) goto CheckInternalBuffer; memcpy (buff, luuCtx->io.dst->data, luuCtx->io.dst->slen); tsz = luuCtx->io.dst->slen / elsize; luuCtx->io.dst->slen = 0; if (tsz > 0) return tsz; } /* Deallocate once EOF becomes triggered */ bdestroy (luuCtx->io.dst); bdestroy (luuCtx->io.src); free (luuCtx); return 0; } /* bStream * bsUuDecode (struct bStream * sInp, int * badlines) * * Creates a bStream which performs the UUDecode of an an input stream. If * there are errors in the decoding, they are counted up and returned in * "badlines", if badlines is not NULL. It is assumed that the "begin" and * "end" lines have already been stripped off. The potential security * problem of writing the filename in the begin line is something that is * beyond the scope of a portable library. */ struct bStream * bsUuDecode (struct bStream * sInp, int * badlines) { struct bsUuCtx * luuCtx = (struct bsUuCtx *) malloc (sizeof (struct bsUuCtx)); struct bStream * sOut; if (NULL == luuCtx) return NULL; luuCtx->io.src = bfromcstr (""); luuCtx->io.dst = bfromcstr (""); if (NULL == luuCtx->io.dst || NULL == luuCtx->io.src) { CleanUpFailureToAllocate:; bdestroy (luuCtx->io.dst); bdestroy (luuCtx->io.src); free (luuCtx); return NULL; } luuCtx->io.badlines = badlines; if (badlines) *badlines = 0; luuCtx->sInp = sInp; sOut = bsopen ((bNread) bsUuDecodePart, luuCtx); if (NULL == sOut) goto CleanUpFailureToAllocate; return sOut; } #define UU_ENCODE_BYTE(b) (char) (((b) == 0) ? '`' : ((b) + ' ')) /* bstring bUuEncode (const_bstring src) * * Performs a UUEncode of a block of data. The "begin" and "end" lines are * not appended. */ bstring bUuEncode (const_bstring src) { bstring out; int i, j, jm; unsigned int c0, c1, c2; if (src == NULL || src->slen < 0 || src->data == NULL) return NULL; if ((out = bfromcstr ("")) == NULL) return NULL; for (i=0; i < src->slen; i += UU_MAX_LINELEN) { if ((jm = i + UU_MAX_LINELEN) > src->slen) jm = src->slen; if (bconchar (out, UU_ENCODE_BYTE (jm - i)) < 0) { bstrFree (out); break; } for (j = i; j < jm; j += 3) { c0 = (unsigned int) bchar (src, j ); c1 = (unsigned int) bchar (src, j + 1); c2 = (unsigned int) bchar (src, j + 2); if (bconchar (out, UU_ENCODE_BYTE ( (c0 & 0xFC) >> 2)) < 0 || bconchar (out, UU_ENCODE_BYTE (((c0 & 0x03) << 4) | ((c1 & 0xF0) >> 4))) < 0 || bconchar (out, UU_ENCODE_BYTE (((c1 & 0x0F) << 2) | ((c2 & 0xC0) >> 6))) < 0 || bconchar (out, UU_ENCODE_BYTE ( (c2 & 0x3F))) < 0) { bstrFree (out); goto End; } } if (bconchar (out, (char) '\r') < 0 || bconchar (out, (char) '\n') < 0) { bstrFree (out); break; } } End:; return out; } /* bstring bYEncode (const_bstring src) * * Performs a YEncode of a block of data. No header or tail info is * appended. See: http://www.yenc.org/whatis.htm and * http://www.yenc.org/yenc-draft.1.3.txt */ bstring bYEncode (const_bstring src) { int i; bstring out; unsigned char c; if (src == NULL || src->slen < 0 || src->data == NULL) return NULL; if ((out = bfromcstr ("")) == NULL) return NULL; for (i=0; i < src->slen; i++) { c = (unsigned char)(src->data[i] + 42); if (c == '=' || c == '\0' || c == '\r' || c == '\n') { if (0 > bconchar (out, (char) '=')) { bdestroy (out); return NULL; } c += (unsigned char) 64; } if (0 > bconchar (out, c)) { bdestroy (out); return NULL; } } return out; } /* bstring bYDecode (const_bstring src) * * Performs a YDecode of a block of data. See: * http://www.yenc.org/whatis.htm and http://www.yenc.org/yenc-draft.1.3.txt */ #define MAX_OB_LEN (64) bstring bYDecode (const_bstring src) { int i; bstring out; unsigned char c; unsigned char octetbuff[MAX_OB_LEN]; int obl; if (src == NULL || src->slen < 0 || src->data == NULL) return NULL; if ((out = bfromcstr ("")) == NULL) return NULL; obl = 0; for (i=0; i < src->slen; i++) { if ('=' == (c = src->data[i])) { /* The = escape mode */ i++; if (i >= src->slen) { bdestroy (out); return NULL; } c = (unsigned char) (src->data[i] - 64); } else { if ('\0' == c) { bdestroy (out); return NULL; } /* Extraneous CR/LFs are to be ignored. */ if (c == '\r' || c == '\n') continue; } octetbuff[obl] = (unsigned char) ((int) c - 42); obl++; if (obl >= MAX_OB_LEN) { if (0 > bcatblk (out, octetbuff, obl)) { bdestroy (out); return NULL; } obl = 0; } } if (0 > bcatblk (out, octetbuff, obl)) { bdestroy (out); out = NULL; } return out; } /* int bSGMLEncode (bstring b) * * Change the string into a version that is quotable in SGML (HTML, XML). */ int bSGMLEncode (bstring b) { static struct tagbstring fr[4][2] = { { bsStatic("&"), bsStatic("&") }, { bsStatic("\""), bsStatic(""") }, { bsStatic("<"), bsStatic("<") }, { bsStatic(">"), bsStatic(">") } }; int i; for (i = 0; i < 4; i++) { int ret = bfindreplace (b, &fr[i][0], &fr[i][1], 0); if (0 > ret) return ret; } return 0; } /* bstring bStrfTime (const char * fmt, const struct tm * timeptr) * * Takes a format string that is compatible with strftime and a struct tm * pointer, formats the time according to the format string and outputs * the bstring as a result. Note that if there is an early generation of a * '\0' character, the bstring will be truncated to this end point. */ bstring bStrfTime (const char * fmt, const struct tm * timeptr) { #if defined (__TURBOC__) && !defined (__BORLANDC__) static struct tagbstring ns = bsStatic ("bStrfTime Not supported"); fmt = fmt; timeptr = timeptr; return &ns; #else bstring buff; int n; size_t r; if (fmt == NULL) return NULL; /* Since the length is not determinable beforehand, a search is performed using the truncating "strftime" call on increasing potential sizes for the output result. */ if ((n = (int) (2*strlen (fmt))) < 16) n = 16; buff = bfromcstralloc (n+2, ""); for (;;) { if (BSTR_OK != balloc (buff, n + 2)) { bdestroy (buff); return NULL; } r = strftime ((char *) buff->data, n + 1, fmt, timeptr); if (r > 0) { buff->slen = (int) r; break; } n += n; } return buff; #endif } /* int bSetCstrChar (bstring a, int pos, char c) * * Sets the character at position pos to the character c in the bstring a. * If the character c is NUL ('\0') then the string is truncated at this * point. Note: this does not enable any other '\0' character in the bstring * as terminator indicator for the string. pos must be in the position * between 0 and b->slen inclusive, otherwise BSTR_ERR will be returned. */ int bSetCstrChar (bstring b, int pos, char c) { if (NULL == b || b->mlen <= 0 || b->slen < 0 || b->mlen < b->slen) return BSTR_ERR; if (pos < 0 || pos > b->slen) return BSTR_ERR; if (pos == b->slen) { if ('\0' != c) return bconchar (b, c); return 0; } b->data[pos] = (unsigned char) c; if ('\0' == c) b->slen = pos; return 0; } /* int bSetChar (bstring b, int pos, char c) * * Sets the character at position pos to the character c in the bstring a. * The string is not truncated if the character c is NUL ('\0'). pos must * be in the position between 0 and b->slen inclusive, otherwise BSTR_ERR * will be returned. */ int bSetChar (bstring b, int pos, char c) { if (NULL == b || b->mlen <= 0 || b->slen < 0 || b->mlen < b->slen) return BSTR_ERR; if (pos < 0 || pos > b->slen) return BSTR_ERR; if (pos == b->slen) { return bconchar (b, c); } b->data[pos] = (unsigned char) c; return 0; } #define INIT_SECURE_INPUT_LENGTH (256) /* bstring bSecureInput (int maxlen, int termchar, * bNgetc vgetchar, void * vgcCtx) * * Read input from an abstracted input interface, for a length of at most * maxlen characters. If maxlen <= 0, then there is no length limit put * on the input. The result is terminated early if vgetchar() return EOF * or the user specified value termchar. * */ bstring bSecureInput (int maxlen, int termchar, bNgetc vgetchar, void * vgcCtx) { int i, m, c; bstring b, t; if (!vgetchar) return NULL; b = bfromcstralloc (INIT_SECURE_INPUT_LENGTH, ""); if ((c = UCHAR_MAX + 1) == termchar) c++; for (i=0; ; i++) { if (termchar == c || (maxlen > 0 && i >= maxlen)) break; c = vgetchar (vgcCtx); if (EOF == c) break; if (i+1 >= b->mlen) { /* Double size, and deal with numeric overflows */ if (b->mlen <= INT_MAX / 2) m = b->mlen << 1; else if (b->mlen <= INT_MAX - 1024) m = b->mlen + 1024; else if (b->mlen <= INT_MAX - 16) m = b->mlen + 16; else if (b->mlen <= INT_MAX - 1) m = b->mlen + 1; else { bSecureDestroy (b); /* Cleanse partial buffer */ return NULL; } t = bfromcstrrangealloc (b->mlen + 1, m, ""); if (t) memcpy (t->data, b->data, i); bSecureDestroy (b); /* Cleanse previous buffer */ b = t; if (!b) return b; } b->data[i] = (unsigned char) c; } b->slen = i; b->data[i] = (unsigned char) '\0'; return b; } #define BWS_BUFF_SZ (1024) struct bwriteStream { bstring buff; /* Buffer for underwrites */ void * parm; /* The stream handle for core stream */ bNwrite writeFn; /* fwrite work-a-like fnptr for core stream */ int isEOF; /* track stream's EOF state */ int minBuffSz; }; /* struct bwriteStream * bwsOpen (bNwrite writeFn, void * parm) * * Wrap a given open stream (described by a fwrite work-a-like function * pointer and stream handle) into an open bwriteStream suitable for write * streaming functions. */ struct bwriteStream * bwsOpen (bNwrite writeFn, void * parm) { struct bwriteStream * ws; if (NULL == writeFn) return NULL; ws = (struct bwriteStream *) malloc (sizeof (struct bwriteStream)); if (ws) { if (NULL == (ws->buff = bfromcstr (""))) { free (ws); ws = NULL; } else { ws->parm = parm; ws->writeFn = writeFn; ws->isEOF = 0; ws->minBuffSz = BWS_BUFF_SZ; } } return ws; } #define internal_bwswriteout(ws,b) { \ if ((b)->slen > 0) { \ if (1 != (ws->writeFn ((b)->data, (b)->slen, 1, ws->parm))) { \ ws->isEOF = 1; \ return BSTR_ERR; \ } \ } \ } /* int bwsWriteFlush (struct bwriteStream * ws) * * Force any pending data to be written to the core stream. */ int bwsWriteFlush (struct bwriteStream * ws) { if (NULL == ws || ws->isEOF || 0 >= ws->minBuffSz || NULL == ws->writeFn || NULL == ws->buff) return BSTR_ERR; internal_bwswriteout (ws, ws->buff); ws->buff->slen = 0; return 0; } /* int bwsWriteBstr (struct bwriteStream * ws, const_bstring b) * * Send a bstring to a bwriteStream. If the stream is at EOF BSTR_ERR is * returned. Note that there is no deterministic way to determine the exact * cut off point where the core stream stopped accepting data. */ int bwsWriteBstr (struct bwriteStream * ws, const_bstring b) { struct tagbstring t; int l; if (NULL == ws || NULL == b || NULL == ws->buff || ws->isEOF || 0 >= ws->minBuffSz || NULL == ws->writeFn) return BSTR_ERR; /* Buffer prepacking optimization */ if (b->slen > 0 && ws->buff->mlen - ws->buff->slen > b->slen) { static struct tagbstring empty = bsStatic (""); if (0 > bconcat (ws->buff, b)) return BSTR_ERR; return bwsWriteBstr (ws, &empty); } if (0 > (l = ws->minBuffSz - ws->buff->slen)) { internal_bwswriteout (ws, ws->buff); ws->buff->slen = 0; l = ws->minBuffSz; } if (b->slen < l) return bconcat (ws->buff, b); if (0 > bcatblk (ws->buff, b->data, l)) return BSTR_ERR; internal_bwswriteout (ws, ws->buff); ws->buff->slen = 0; bmid2tbstr (t, (bstring) b, l, b->slen); if (t.slen >= ws->minBuffSz) { internal_bwswriteout (ws, &t); return 0; } return bassign (ws->buff, &t); } /* int bwsWriteBlk (struct bwriteStream * ws, void * blk, int len) * * Send a block of data a bwriteStream. If the stream is at EOF BSTR_ERR is * returned. */ int bwsWriteBlk (struct bwriteStream * ws, void * blk, int len) { struct tagbstring t; if (NULL == blk || len < 0) return BSTR_ERR; blk2tbstr (t, blk, len); return bwsWriteBstr (ws, &t); } /* int bwsIsEOF (const struct bwriteStream * ws) * * Returns 0 if the stream is currently writable, 1 if the core stream has * responded by not accepting the previous attempted write. */ int bwsIsEOF (const struct bwriteStream * ws) { if (NULL == ws || NULL == ws->buff || 0 > ws->minBuffSz || NULL == ws->writeFn) return BSTR_ERR; return ws->isEOF; } /* int bwsBuffLength (struct bwriteStream * ws, int sz) * * Set the length of the buffer used by the bwsStream. If sz is zero, the * length is not set. This function returns with the previous length. */ int bwsBuffLength (struct bwriteStream * ws, int sz) { int oldSz; if (ws == NULL || sz < 0) return BSTR_ERR; oldSz = ws->minBuffSz; if (sz > 0) ws->minBuffSz = sz; return oldSz; } /* void * bwsClose (struct bwriteStream * s) * * Close the bwriteStream, and return the handle to the stream that was * originally used to open the given stream. Note that even if the stream * is at EOF it still needs to be closed with a call to bwsClose. */ void * bwsClose (struct bwriteStream * ws) { void * parm; if (NULL == ws || NULL == ws->buff || 0 >= ws->minBuffSz || NULL == ws->writeFn) return NULL; bwsWriteFlush (ws); parm = ws->parm; ws->parm = NULL; ws->minBuffSz = -1; ws->writeFn = NULL; bstrFree (ws->buff); free (ws); return parm; }