2 * This source file is part of the bstring string library. This code was
3 * written by Paul Hsieh in 2002-2008, and is covered by the BSD open source
4 * license and the GPL. Refer to the accompanying documentation for details
5 * on usage and license.
11 * This file is not necessarily part of the core bstring library itself, but
12 * is just an auxilliary module which includes miscellaneous or trivial
24 /* bstring bTail (bstring b, int n)
26 * Return with a string of the last n characters of b.
28 bstring
bTail (bstring b
, int n
) {
29 if (b
== NULL
|| n
< 0 || (b
->mlen
< b
->slen
&& b
->mlen
> 0)) return NULL
;
30 if (n
>= b
->slen
) return bstrcpy (b
);
31 return bmidstr (b
, b
->slen
- n
, n
);
34 /* bstring bHead (bstring b, int n)
36 * Return with a string of the first n characters of b.
38 bstring
bHead (bstring b
, int n
) {
39 if (b
== NULL
|| n
< 0 || (b
->mlen
< b
->slen
&& b
->mlen
> 0)) return NULL
;
40 if (n
>= b
->slen
) return bstrcpy (b
);
41 return bmidstr (b
, 0, n
);
44 /* int bFill (bstring a, char c, int len)
46 * Fill a given bstring with the character in parameter c, for a length n.
48 int bFill (bstring b
, char c
, int len
) {
49 if (b
== NULL
|| len
< 0 || (b
->mlen
< b
->slen
&& b
->mlen
> 0)) return -__LINE__
;
51 return bsetstr (b
, len
, NULL
, c
);
54 /* int bReplicate (bstring b, int n)
56 * Replicate the contents of b end to end n times and replace it in b.
58 int bReplicate (bstring b
, int n
) {
59 return bpattern (b
, n
* b
->slen
);
62 /* int bReverse (bstring b)
64 * Reverse the contents of b in place.
66 int bReverse (bstring b
) {
70 if (b
== NULL
|| b
->slen
< 0 || b
->mlen
< b
->slen
) return -__LINE__
;
73 m
= ((unsigned)n
) >> 1;
75 for (i
=0; i
< m
; i
++) {
77 b
->data
[n
- i
] = b
->data
[i
];
84 /* int bInsertChrs (bstring b, int pos, int len, unsigned char c, unsigned char fill)
86 * Insert a repeated sequence of a given character into the string at
87 * position pos for a length len.
89 int bInsertChrs (bstring b
, int pos
, int len
, unsigned char c
, unsigned char fill
) {
90 if (b
== NULL
|| b
->slen
< 0 || b
->mlen
< b
->slen
|| pos
< 0 || len
<= 0) return -__LINE__
;
93 && 0 > bsetstr (b
, pos
, NULL
, fill
)) return -__LINE__
;
95 if (0 > balloc (b
, b
->slen
+ len
)) return -__LINE__
;
96 if (pos
< b
->slen
) memmove (b
->data
+ pos
+ len
, b
->data
+ pos
, b
->slen
- pos
);
97 memset (b
->data
+ pos
, c
, len
);
99 b
->data
[b
->slen
] = (unsigned char) '\0';
103 /* int bJustifyLeft (bstring b, int space)
105 * Left justify a string.
107 int bJustifyLeft (bstring b
, int space
) {
109 unsigned char c
= (unsigned char) space
;
111 if (b
== NULL
|| b
->slen
< 0 || b
->mlen
< b
->slen
) return -__LINE__
;
112 if (space
!= (int) c
) return BSTR_OK
;
114 for (s
=j
=i
=0; i
< b
->slen
; i
++) {
116 s
= c
!= (b
->data
[j
] = b
->data
[i
]);
119 if (j
> 0 && b
->data
[j
-1] == c
) j
--;
121 b
->data
[j
] = (unsigned char) '\0';
126 /* int bJustifyRight (bstring b, int width, int space)
128 * Right justify a string to within a given width.
130 int bJustifyRight (bstring b
, int width
, int space
) {
132 if (width
<= 0) return -__LINE__
;
133 if (0 > (ret
= bJustifyLeft (b
, space
))) return ret
;
134 if (b
->slen
<= width
)
135 return bInsertChrs (b
, 0, width
- b
->slen
, (unsigned char) space
, (unsigned char) space
);
139 /* int bJustifyCenter (bstring b, int width, int space)
141 * Center a string's non-white space characters to within a given width by
142 * inserting whitespaces at the beginning.
144 int bJustifyCenter (bstring b
, int width
, int space
) {
146 if (width
<= 0) return -__LINE__
;
147 if (0 > (ret
= bJustifyLeft (b
, space
))) return ret
;
148 if (b
->slen
<= width
)
149 return bInsertChrs (b
, 0, (width
- b
->slen
+ 1) >> 1, (unsigned char) space
, (unsigned char) space
);
153 /* int bJustifyMargin (bstring b, int width, int space)
155 * Stretch a string to flush against left and right margins by evenly
156 * distributing additional white space between words. If the line is too
157 * long to be margin justified, it is left justified.
159 int bJustifyMargin (bstring b
, int width
, int space
) {
160 struct bstrList
* sl
;
163 if (b
== NULL
|| b
->slen
< 0 || b
->mlen
== 0 || b
->mlen
< b
->slen
) return -__LINE__
;
164 if (NULL
== (sl
= bsplit (b
, (unsigned char) space
))) return -__LINE__
;
165 for (l
=c
=i
=0; i
< sl
->qty
; i
++) {
166 if (sl
->entry
[i
]->slen
> 0) {
168 l
+= sl
->entry
[i
]->slen
;
172 if (l
+ c
>= width
|| c
< 2) {
173 bstrListDestroy (sl
);
174 return bJustifyLeft (b
, space
);
178 for (i
=0; i
< sl
->qty
; i
++) {
179 if (sl
->entry
[i
]->slen
> 0) {
181 int s
= (width
- l
+ (c
/ 2)) / c
;
182 bInsertChrs (b
, b
->slen
, s
, (unsigned char) space
, (unsigned char) space
);
185 bconcat (b
, sl
->entry
[i
]);
191 bstrListDestroy (sl
);
195 static size_t readNothing (void *buff
, size_t elsize
, size_t nelem
, void *parm
) {
200 return 0; /* Immediately indicate EOF. */
203 /* struct bStream * bsFromBstr (const_bstring b);
205 * Create a bStream whose contents are a copy of the bstring passed in.
206 * This allows the use of all the bStream APIs with bstrings.
208 struct bStream
* bsFromBstr (const_bstring b
) {
209 struct bStream
* s
= bsopen ((bNread
) readNothing
, NULL
);
210 bsunread (s
, b
); /* Push the bstring data into the empty bStream. */
214 static size_t readRef (void *buff
, size_t elsize
, size_t nelem
, void *parm
) {
215 struct tagbstring
* t
= (struct tagbstring
*) parm
;
216 size_t tsz
= elsize
* nelem
;
218 if (tsz
> (size_t) t
->slen
) tsz
= (size_t) t
->slen
;
220 memcpy (buff
, t
->data
, tsz
);
221 t
->slen
-= (int) tsz
;
228 /* The "by reference" version of the above function. This function puts
229 * a number of restrictions on the call site (the passed in struct
230 * tagbstring *will* be modified by this function, and the source data
231 * must remain alive and constant for the lifetime of the bStream).
232 * Hence it is not presented as an extern.
234 static struct bStream
* bsFromBstrRef (struct tagbstring
* t
) {
236 return bsopen ((bNread
) readRef
, t
);
239 /* char * bStr2NetStr (const_bstring b)
241 * Convert a bstring to a netstring. See
242 * http://cr.yp.to/proto/netstrings.txt for a description of netstrings.
243 * Note: 1) The value returned should be freed with a call to bcstrfree() at
244 * the point when it will no longer be referenced to avoid a memory
246 * 2) If the returned value is non-NULL, then it also '\0' terminated
247 * in the character position one past the "," terminator.
249 char * bStr2NetStr (const_bstring b
) {
250 char strnum
[sizeof (b
->slen
) * 3 + 1];
252 unsigned char * buff
;
254 if (b
== NULL
|| b
->data
== NULL
|| b
->slen
< 0) return NULL
;
255 sprintf (strnum
, "%d:", b
->slen
);
256 if (NULL
== (s
= bfromcstr (strnum
))
257 || bconcat (s
, b
) == BSTR_ERR
|| bconchar (s
, (char) ',') == BSTR_ERR
) {
262 bcstrfree ((char *) s
);
263 return (char *) buff
;
266 /* bstring bNetStr2Bstr (const char * buf)
268 * Convert a netstring to a bstring. See
269 * http://cr.yp.to/proto/netstrings.txt for a description of netstrings.
270 * Note that the terminating "," *must* be present, however a following '\0'
273 bstring
bNetStr2Bstr (const char * buff
) {
276 if (buff
== NULL
) return NULL
;
278 for (i
=0; buff
[i
] != ':'; i
++) {
279 unsigned int v
= buff
[i
] - '0';
280 if (v
> 9 || x
> ((INT_MAX
- (signed int)v
) / 10)) return NULL
;
284 /* This thing has to be properly terminated */
285 if (buff
[i
+ 1 + x
] != ',') return NULL
;
287 if (NULL
== (b
= bfromcstr (""))) return NULL
;
288 if (balloc (b
, x
+ 1) != BSTR_OK
) {
292 memcpy (b
->data
, buff
+ i
+ 1, x
);
293 b
->data
[x
] = (unsigned char) '\0';
298 static char b64ETable
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
300 /* bstring bBase64Encode (const_bstring b)
302 * Generate a base64 encoding. See: RFC1341
304 bstring
bBase64Encode (const_bstring b
) {
305 int i
, c0
, c1
, c2
, c3
;
308 if (b
== NULL
|| b
->slen
< 0 || b
->data
== NULL
) return NULL
;
310 out
= bfromcstr ("");
311 for (i
=0; i
+ 2 < b
->slen
; i
+= 3) {
312 if (i
&& ((i
% 57) == 0)) {
313 if (bconchar (out
, (char) '\015') < 0 || bconchar (out
, (char) '\012') < 0) {
318 c0
= b
->data
[i
] >> 2;
319 c1
= ((b
->data
[i
] << 4) |
320 (b
->data
[i
+1] >> 4)) & 0x3F;
321 c2
= ((b
->data
[i
+1] << 2) |
322 (b
->data
[i
+2] >> 6)) & 0x3F;
323 c3
= b
->data
[i
+2] & 0x3F;
324 if (bconchar (out
, b64ETable
[c0
]) < 0 ||
325 bconchar (out
, b64ETable
[c1
]) < 0 ||
326 bconchar (out
, b64ETable
[c2
]) < 0 ||
327 bconchar (out
, b64ETable
[c3
]) < 0) {
333 if (i
&& ((i
% 57) == 0)) {
334 if (bconchar (out
, (char) '\015') < 0 || bconchar (out
, (char) '\012') < 0) {
340 switch (i
+ 2 - b
->slen
) {
341 case 0: c0
= b
->data
[i
] >> 2;
342 c1
= ((b
->data
[i
] << 4) |
343 (b
->data
[i
+1] >> 4)) & 0x3F;
344 c2
= (b
->data
[i
+1] << 2) & 0x3F;
345 if (bconchar (out
, b64ETable
[c0
]) < 0 ||
346 bconchar (out
, b64ETable
[c1
]) < 0 ||
347 bconchar (out
, b64ETable
[c2
]) < 0 ||
348 bconchar (out
, (char) '=') < 0) {
353 case 1: c0
= b
->data
[i
] >> 2;
354 c1
= (b
->data
[i
] << 4) & 0x3F;
355 if (bconchar (out
, b64ETable
[c0
]) < 0 ||
356 bconchar (out
, b64ETable
[c1
]) < 0 ||
357 bconchar (out
, (char) '=') < 0 ||
358 bconchar (out
, (char) '=') < 0) {
372 static int base64DecodeSymbol (unsigned char alpha
) {
373 if ((alpha
>= 'A') && (alpha
<= 'Z')) return (int)(alpha
- 'A');
374 else if ((alpha
>= 'a') && (alpha
<= 'z'))
375 return 26 + (int)(alpha
- 'a');
376 else if ((alpha
>= '0') && (alpha
<= '9'))
377 return 52 + (int)(alpha
- '0');
378 else if (alpha
== '+') return 62;
379 else if (alpha
== '/') return 63;
380 else if (alpha
== '=') return B64_PAD
;
384 /* bstring bBase64DecodeEx (const_bstring b, int * boolTruncError)
386 * Decode a base64 block of data. All MIME headers are assumed to have been
387 * removed. See: RFC1341
389 bstring
bBase64DecodeEx (const_bstring b
, int * boolTruncError
) {
391 unsigned char c0
, c1
, c2
;
394 if (b
== NULL
|| b
->slen
< 0 || b
->data
== NULL
) return NULL
;
395 if (boolTruncError
) *boolTruncError
= 0;
396 out
= bfromcstr ("");
400 if (i
>= b
->slen
) return out
;
401 if (b
->data
[i
] == '=') { /* Bad "too early" truncation */
402 if (boolTruncError
) {
409 v
= base64DecodeSymbol (b
->data
[i
]);
412 c0
= (unsigned char) (v
<< 2);
414 if (i
>= b
->slen
|| b
->data
[i
] == '=') { /* Bad "too early" truncation */
415 if (boolTruncError
) {
422 v
= base64DecodeSymbol (b
->data
[i
]);
425 c0
|= (unsigned char) (v
>> 4);
426 c1
= (unsigned char) (v
<< 4);
429 if (boolTruncError
) {
436 if (b
->data
[i
] == '=') {
438 if (i
>= b
->slen
|| b
->data
[i
] != '=' || bconchar (out
, c0
) < 0) {
439 if (boolTruncError
) {
443 bdestroy (out
); /* Missing "=" at the end. */
448 v
= base64DecodeSymbol (b
->data
[i
]);
451 c1
|= (unsigned char) (v
>> 2);
452 c2
= (unsigned char) (v
<< 6);
455 if (boolTruncError
) {
462 if (b
->data
[i
] == '=') {
463 if (bconchar (out
, c0
) < 0 || bconchar (out
, c1
) < 0) {
464 if (boolTruncError
) {
471 if (boolTruncError
) *boolTruncError
= 0;
474 v
= base64DecodeSymbol (b
->data
[i
]);
477 c2
|= (unsigned char) (v
);
478 if (bconchar (out
, c0
) < 0 ||
479 bconchar (out
, c1
) < 0 ||
480 bconchar (out
, c2
) < 0) {
481 if (boolTruncError
) {
482 *boolTruncError
= -1;
491 #define UU_DECODE_BYTE(b) (((b) == (signed int)'`') ? 0 : (b) - (signed int)' ')
498 #define UU_MAX_LINELEN 45
500 static int bUuDecLine (void * parm
, int ofs
, int len
) {
501 struct bUuInOut
* io
= (struct bUuInOut
*) parm
;
504 int i
, llen
, otlen
, ret
, c0
, c1
, c2
, c3
, d0
, d1
, d2
, d3
;
506 if (len
== 0) return 0;
507 llen
= UU_DECODE_BYTE (s
->data
[ofs
]);
512 if (((unsigned) llen
) > UU_MAX_LINELEN
) { ret
= -__LINE__
;
518 for (i
=1; i
< s
->slen
&& t
->slen
< llen
;i
+= 4) {
519 unsigned char outoctet
[3];
520 c0
= UU_DECODE_BYTE (d0
= (int) bchare (s
, i
+ofs
+0, ' ' - 1));
521 c1
= UU_DECODE_BYTE (d1
= (int) bchare (s
, i
+ofs
+1, ' ' - 1));
522 c2
= UU_DECODE_BYTE (d2
= (int) bchare (s
, i
+ofs
+2, ' ' - 1));
523 c3
= UU_DECODE_BYTE (d3
= (int) bchare (s
, i
+ofs
+3, ' ' - 1));
525 if (((unsigned) (c0
|c1
) >= 0x40)) { if (!ret
) ret
= -__LINE__
;
526 if (d0
> 0x60 || (d0
< (' ' - 1) && !isspace (d0
)) ||
527 d1
> 0x60 || (d1
< (' ' - 1) && !isspace (d1
))) {
533 outoctet
[0] = (unsigned char) ((c0
<< 2) | ((unsigned) c1
>> 4));
534 if (t
->slen
+1 >= llen
) {
535 if (0 > bconchar (t
, (char) outoctet
[0])) return -__LINE__
;
538 if ((unsigned) c2
>= 0x40) { if (!ret
) ret
= -__LINE__
;
539 if (d2
> 0x60 || (d2
< (' ' - 1) && !isspace (d2
))) {
545 outoctet
[1] = (unsigned char) ((c1
<< 4) | ((unsigned) c2
>> 2));
546 if (t
->slen
+2 >= llen
) {
547 if (0 > bcatblk (t
, outoctet
, 2)) return -__LINE__
;
550 if ((unsigned) c3
>= 0x40) { if (!ret
) ret
= -__LINE__
;
551 if (d3
> 0x60 || (d3
< (' ' - 1) && !isspace (d3
))) {
557 outoctet
[2] = (unsigned char) ((c2
<< 6) | ((unsigned) c3
));
558 if (0 > bcatblk (t
, outoctet
, 3)) return -__LINE__
;
560 if (t
->slen
< llen
) { if (0 == ret
) ret
= -__LINE__
;
564 if (ret
&& io
->badlines
) {
571 /* bstring bUuDecodeEx (const_bstring src, int * badlines)
573 * Performs a UUDecode of a block of data. If there are errors in the
574 * decoding, they are counted up and returned in "badlines", if badlines is
575 * not NULL. It is assumed that the "begin" and "end" lines have already
576 * been stripped off. The potential security problem of writing the
577 * filename in the begin line is something that is beyond the scope of a
582 #pragma warning(disable:4204)
585 bstring
bUuDecodeEx (const_bstring src
, int * badlines
) {
591 if (!src
) return NULL
;
592 t
= *src
; /* Short lifetime alias to header of src */
593 s
= bsFromBstrRef (&t
); /* t is undefined after this */
595 d
= bsUuDecode (s
, badlines
);
596 b
= bfromcstralloc (256, "");
597 if (NULL
== b
|| 0 > bsread (b
, d
, INT_MAX
)) {
608 struct bStream
* sInp
;
611 static size_t bsUuDecodePart (void *buff
, size_t elsize
, size_t nelem
, void *parm
) {
612 static struct tagbstring eol
= bsStatic ("\r\n");
613 struct bsUuCtx
* luuCtx
= (struct bsUuCtx
*) parm
;
617 if (NULL
== buff
|| NULL
== parm
) return 0;
618 tsz
= elsize
* nelem
;
620 CheckInternalBuffer
:;
621 /* If internal buffer has sufficient data, just output it */
622 if (((size_t) luuCtx
->io
.dst
->slen
) > tsz
) {
623 memcpy (buff
, luuCtx
->io
.dst
->data
, tsz
);
624 bdelete (luuCtx
->io
.dst
, 0, (int) tsz
);
629 if (0 <= (l
= binchr (luuCtx
->io
.src
, 0, &eol
))) {
632 bstring s
= luuCtx
->io
.src
;
637 bmid2tbstr (t
, s
, ol
, l
- ol
);
638 lret
= bUuDecLine (&luuCtx
->io
, 0, t
.slen
);
645 if (((size_t) luuCtx
->io
.dst
->slen
) > tsz
) break;
646 l
= binchr (s
, ol
, &eol
);
647 } while (BSTR_ERR
!= l
);
650 goto CheckInternalBuffer
;
653 if (BSTR_ERR
!= bsreada (luuCtx
->io
.src
, luuCtx
->sInp
, bsbufflength (luuCtx
->sInp
, BSTR_BS_BUFF_LENGTH_GET
))) {
657 bUuDecLine (&luuCtx
->io
, 0, luuCtx
->io
.src
->slen
);
660 /* Output any lingering data that has been translated */
661 if (((size_t) luuCtx
->io
.dst
->slen
) > 0) {
662 if (((size_t) luuCtx
->io
.dst
->slen
) > tsz
) goto CheckInternalBuffer
;
663 memcpy (buff
, luuCtx
->io
.dst
->data
, luuCtx
->io
.dst
->slen
);
664 tsz
= luuCtx
->io
.dst
->slen
/ elsize
;
665 luuCtx
->io
.dst
->slen
= 0;
666 if (tsz
> 0) return tsz
;
669 /* Deallocate once EOF becomes triggered */
670 bdestroy (luuCtx
->io
.dst
);
671 bdestroy (luuCtx
->io
.src
);
676 /* bStream * bsUuDecode (struct bStream * sInp, int * badlines)
678 * Creates a bStream which performs the UUDecode of an an input stream. If
679 * there are errors in the decoding, they are counted up and returned in
680 * "badlines", if badlines is not NULL. It is assumed that the "begin" and
681 * "end" lines have already been stripped off. The potential security
682 * problem of writing the filename in the begin line is something that is
683 * beyond the scope of a portable library.
686 struct bStream
* bsUuDecode (struct bStream
* sInp
, int * badlines
) {
687 struct bsUuCtx
* luuCtx
= (struct bsUuCtx
*) malloc (sizeof (struct bsUuCtx
));
688 struct bStream
* sOut
;
690 if (NULL
== luuCtx
) return NULL
;
692 luuCtx
->io
.src
= bfromcstr ("");
693 luuCtx
->io
.dst
= bfromcstr ("");
694 if (NULL
== luuCtx
->io
.dst
|| NULL
== luuCtx
->io
.src
) {
695 CleanUpFailureToAllocate
:;
696 bdestroy (luuCtx
->io
.dst
);
697 bdestroy (luuCtx
->io
.src
);
701 luuCtx
->io
.badlines
= badlines
;
702 if (badlines
) *badlines
= 0;
706 sOut
= bsopen ((bNread
) bsUuDecodePart
, luuCtx
);
707 if (NULL
== sOut
) goto CleanUpFailureToAllocate
;
711 #define UU_ENCODE_BYTE(b) (char) (((b) == 0) ? '`' : ((b) + ' '))
713 /* bstring bUuEncode (const_bstring src)
715 * Performs a UUEncode of a block of data. The "begin" and "end" lines are
718 bstring
bUuEncode (const_bstring src
) {
721 unsigned int c0
, c1
, c2
;
722 if (src
== NULL
|| src
->slen
< 0 || src
->data
== NULL
) return NULL
;
723 if ((out
= bfromcstr ("")) == NULL
) return NULL
;
724 for (i
=0; i
< src
->slen
; i
+= UU_MAX_LINELEN
) {
725 if ((jm
= i
+ UU_MAX_LINELEN
) > src
->slen
) jm
= src
->slen
;
726 if (bconchar (out
, UU_ENCODE_BYTE (jm
- i
)) < 0) {
730 for (j
= i
; j
< jm
; j
+= 3) {
731 c0
= (unsigned int) bchar (src
, j
);
732 c1
= (unsigned int) bchar (src
, j
+ 1);
733 c2
= (unsigned int) bchar (src
, j
+ 2);
734 if (bconchar (out
, UU_ENCODE_BYTE ( (c0
& 0xFC) >> 2)) < 0 ||
735 bconchar (out
, UU_ENCODE_BYTE (((c0
& 0x03) << 4) | ((c1
& 0xF0) >> 4))) < 0 ||
736 bconchar (out
, UU_ENCODE_BYTE (((c1
& 0x0F) << 2) | ((c2
& 0xC0) >> 6))) < 0 ||
737 bconchar (out
, UU_ENCODE_BYTE ( (c2
& 0x3F))) < 0) {
742 if (bconchar (out
, (char) '\r') < 0 || bconchar (out
, (char) '\n') < 0) {
751 /* bstring bYEncode (const_bstring src)
753 * Performs a YEncode of a block of data. No header or tail info is
754 * appended. See: http://www.yenc.org/whatis.htm and
755 * http://www.yenc.org/yenc-draft.1.3.txt
757 bstring
bYEncode (const_bstring src
) {
762 if (src
== NULL
|| src
->slen
< 0 || src
->data
== NULL
) return NULL
;
763 if ((out
= bfromcstr ("")) == NULL
) return NULL
;
764 for (i
=0; i
< src
->slen
; i
++) {
765 c
= (unsigned char)(src
->data
[i
] + 42);
766 if (c
== '=' || c
== '\0' || c
== '\r' || c
== '\n') {
767 if (0 > bconchar (out
, (char) '=')) {
771 c
+= (unsigned char) 64;
773 if (0 > bconchar (out
, c
)) {
781 /* bstring bYDecode (const_bstring src)
783 * Performs a YDecode of a block of data. See:
784 * http://www.yenc.org/whatis.htm and http://www.yenc.org/yenc-draft.1.3.txt
786 #define MAX_OB_LEN (64)
788 bstring
bYDecode (const_bstring src
) {
792 unsigned char octetbuff
[MAX_OB_LEN
];
795 if (src
== NULL
|| src
->slen
< 0 || src
->data
== NULL
) return NULL
;
796 if ((out
= bfromcstr ("")) == NULL
) return NULL
;
800 for (i
=0; i
< src
->slen
; i
++) {
801 if ('=' == (c
= src
->data
[i
])) { /* The = escape mode */
803 if (i
>= src
->slen
) {
807 c
= (unsigned char) (src
->data
[i
] - 64);
814 /* Extraneous CR/LFs are to be ignored. */
815 if (c
== '\r' || c
== '\n') continue;
818 octetbuff
[obl
] = (unsigned char) ((int) c
- 42);
821 if (obl
>= MAX_OB_LEN
) {
822 if (0 > bcatblk (out
, octetbuff
, obl
)) {
830 if (0 > bcatblk (out
, octetbuff
, obl
)) {
837 /* bstring bStrfTime (const char * fmt, const struct tm * timeptr)
839 * Takes a format string that is compatible with strftime and a struct tm
840 * pointer, formats the time according to the format string and outputs
841 * the bstring as a result. Note that if there is an early generation of a
842 * '\0' character, the bstring will be truncated to this end point.
844 bstring
bStrfTime (const char * fmt
, const struct tm
* timeptr
) {
845 #if defined (__TURBOC__) && !defined (__BORLANDC__)
846 static struct tagbstring ns
= bsStatic ("bStrfTime Not supported");
855 if (fmt
== NULL
) return NULL
;
857 /* Since the length is not determinable beforehand, a search is
858 performed using the truncating "strftime" call on increasing
859 potential sizes for the output result. */
861 if ((n
= (int) (2*strlen (fmt
))) < 16) n
= 16;
862 buff
= bfromcstralloc (n
+2, "");
865 if (BSTR_OK
!= balloc (buff
, n
+ 2)) {
870 r
= strftime ((char *) buff
->data
, n
+ 1, fmt
, timeptr
);
873 buff
->slen
= (int) r
;
884 /* int bSetCstrChar (bstring a, int pos, char c)
886 * Sets the character at position pos to the character c in the bstring a.
887 * If the character c is NUL ('\0') then the string is truncated at this
888 * point. Note: this does not enable any other '\0' character in the bstring
889 * as terminator indicator for the string. pos must be in the position
890 * between 0 and b->slen inclusive, otherwise BSTR_ERR will be returned.
892 int bSetCstrChar (bstring b
, int pos
, char c
) {
893 if (NULL
== b
|| b
->mlen
<= 0 || b
->slen
< 0 || b
->mlen
< b
->slen
)
895 if (pos
< 0 || pos
> b
->slen
) return BSTR_ERR
;
897 if (pos
== b
->slen
) {
898 if ('\0' != c
) return bconchar (b
, c
);
902 b
->data
[pos
] = (unsigned char) c
;
903 if ('\0' == c
) b
->slen
= pos
;
908 /* int bSetChar (bstring b, int pos, char c)
910 * Sets the character at position pos to the character c in the bstring a.
911 * The string is not truncated if the character c is NUL ('\0'). pos must
912 * be in the position between 0 and b->slen inclusive, otherwise BSTR_ERR
915 int bSetChar (bstring b
, int pos
, char c
) {
916 if (NULL
== b
|| b
->mlen
<= 0 || b
->slen
< 0 || b
->mlen
< b
->slen
)
918 if (pos
< 0 || pos
> b
->slen
) return BSTR_ERR
;
920 if (pos
== b
->slen
) {
921 return bconchar (b
, c
);
924 b
->data
[pos
] = (unsigned char) c
;
928 #define INIT_SECURE_INPUT_LENGTH (256)
930 /* bstring bSecureInput (int maxlen, int termchar,
931 * bNgetc vgetchar, void * vgcCtx)
933 * Read input from an abstracted input interface, for a length of at most
934 * maxlen characters. If maxlen <= 0, then there is no length limit put
935 * on the input. The result is terminated early if vgetchar() return EOF
936 * or the user specified value termchar.
939 bstring
bSecureInput (int maxlen
, int termchar
, bNgetc vgetchar
, void * vgcCtx
) {
943 if (!vgetchar
) return NULL
;
945 b
= bfromcstralloc (INIT_SECURE_INPUT_LENGTH
, "");
946 if ((c
= UCHAR_MAX
+ 1) == termchar
) c
++;
949 if (termchar
== c
|| (maxlen
> 0 && i
>= maxlen
)) c
= EOF
;
950 else c
= vgetchar (vgcCtx
);
954 if (i
+1 >= b
->mlen
) {
956 /* Double size, but deal with unusual case of numeric
959 if ((m
= b
->mlen
<< 1) <= b
->mlen
&&
960 (m
= b
->mlen
+ 1024) <= b
->mlen
&&
961 (m
= b
->mlen
+ 16) <= b
->mlen
&&
962 (m
= b
->mlen
+ 1) <= b
->mlen
) t
= NULL
;
963 else t
= bfromcstralloc (m
, "");
965 if (t
) memcpy (t
->data
, b
->data
, i
);
966 bSecureDestroy (b
); /* Cleanse previous buffer */
971 b
->data
[i
] = (unsigned char) c
;
975 b
->data
[i
] = (unsigned char) '\0';
979 #define BWS_BUFF_SZ (1024)
981 struct bwriteStream
{
982 bstring buff
; /* Buffer for underwrites */
983 void * parm
; /* The stream handle for core stream */
984 bNwrite writeFn
; /* fwrite work-a-like fnptr for core stream */
985 int isEOF
; /* track stream's EOF state */
989 /* struct bwriteStream * bwsOpen (bNwrite writeFn, void * parm)
991 * Wrap a given open stream (described by a fwrite work-a-like function
992 * pointer and stream handle) into an open bwriteStream suitable for write
993 * streaming functions.
995 struct bwriteStream
* bwsOpen (bNwrite writeFn
, void * parm
) {
996 struct bwriteStream
* ws
;
998 if (NULL
== writeFn
) return NULL
;
999 ws
= (struct bwriteStream
*) malloc (sizeof (struct bwriteStream
));
1001 if (NULL
== (ws
->buff
= bfromcstr (""))) {
1006 ws
->writeFn
= writeFn
;
1008 ws
->minBuffSz
= BWS_BUFF_SZ
;
1014 #define internal_bwswriteout(ws,b) { \
1015 if ((b)->slen > 0) { \
1016 if (1 != (ws->writeFn ((b)->data, (b)->slen, 1, ws->parm))) { \
1023 /* int bwsWriteFlush (struct bwriteStream * ws)
1025 * Force any pending data to be written to the core stream.
1027 int bwsWriteFlush (struct bwriteStream
* ws
) {
1028 if (NULL
== ws
|| ws
->isEOF
|| 0 >= ws
->minBuffSz
||
1029 NULL
== ws
->writeFn
|| NULL
== ws
->buff
) return BSTR_ERR
;
1030 internal_bwswriteout (ws
, ws
->buff
);
1035 /* int bwsWriteBstr (struct bwriteStream * ws, const_bstring b)
1037 * Send a bstring to a bwriteStream. If the stream is at EOF BSTR_ERR is
1038 * returned. Note that there is no deterministic way to determine the exact
1039 * cut off point where the core stream stopped accepting data.
1041 int bwsWriteBstr (struct bwriteStream
* ws
, const_bstring b
) {
1042 struct tagbstring t
;
1045 if (NULL
== ws
|| NULL
== b
|| NULL
== ws
->buff
||
1046 ws
->isEOF
|| 0 >= ws
->minBuffSz
|| NULL
== ws
->writeFn
)
1049 /* Buffer prepacking optimization */
1050 if (b
->slen
> 0 && ws
->buff
->mlen
- ws
->buff
->slen
> b
->slen
) {
1051 static struct tagbstring empty
= bsStatic ("");
1052 if (0 > bconcat (ws
->buff
, b
)) return BSTR_ERR
;
1053 return bwsWriteBstr (ws
, &empty
);
1056 if (0 > (l
= ws
->minBuffSz
- ws
->buff
->slen
)) {
1057 internal_bwswriteout (ws
, ws
->buff
);
1062 if (b
->slen
< l
) return bconcat (ws
->buff
, b
);
1064 if (0 > bcatblk (ws
->buff
, b
->data
, l
)) return BSTR_ERR
;
1065 internal_bwswriteout (ws
, ws
->buff
);
1068 bmid2tbstr (t
, (bstring
) b
, l
, b
->slen
);
1070 if (t
.slen
>= ws
->minBuffSz
) {
1071 internal_bwswriteout (ws
, &t
);
1075 return bassign (ws
->buff
, &t
);
1078 /* int bwsWriteBlk (struct bwriteStream * ws, void * blk, int len)
1080 * Send a block of data a bwriteStream. If the stream is at EOF BSTR_ERR is
1083 int bwsWriteBlk (struct bwriteStream
* ws
, void * blk
, int len
) {
1084 struct tagbstring t
;
1085 if (NULL
== blk
|| len
< 0) return BSTR_ERR
;
1086 blk2tbstr (t
, blk
, len
);
1087 return bwsWriteBstr (ws
, &t
);
1090 /* int bwsIsEOF (const struct bwriteStream * ws)
1092 * Returns 0 if the stream is currently writable, 1 if the core stream has
1093 * responded by not accepting the previous attempted write.
1095 int bwsIsEOF (const struct bwriteStream
* ws
) {
1096 if (NULL
== ws
|| NULL
== ws
->buff
|| 0 > ws
->minBuffSz
||
1097 NULL
== ws
->writeFn
) return BSTR_ERR
;
1101 /* int bwsBuffLength (struct bwriteStream * ws, int sz)
1103 * Set the length of the buffer used by the bwsStream. If sz is zero, the
1104 * length is not set. This function returns with the previous length.
1106 int bwsBuffLength (struct bwriteStream
* ws
, int sz
) {
1108 if (ws
== NULL
|| sz
< 0) return BSTR_ERR
;
1109 oldSz
= ws
->minBuffSz
;
1110 if (sz
> 0) ws
->minBuffSz
= sz
;
1114 /* void * bwsClose (struct bwriteStream * s)
1116 * Close the bwriteStream, and return the handle to the stream that was
1117 * originally used to open the given stream. Note that even if the stream
1118 * is at EOF it still needs to be closed with a call to bwsClose.
1120 void * bwsClose (struct bwriteStream
* ws
) {
1122 if (NULL
== ws
|| NULL
== ws
->buff
|| 0 >= ws
->minBuffSz
||
1123 NULL
== ws
->writeFn
) return NULL
;
1129 bstrFree (ws
->buff
);