Fix typo
[prads.git] / src / bstraux.c
blob0e3522ab5dee3c01a411d9a78ee344084e2a6bb1
1 /*
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.
6 */
8 /*
9 * bstraux.c
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
13 * functions.
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <limits.h>
20 #include <ctype.h>
21 #include "bstrlib.h"
22 #include "bstraux.h"
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__;
50 b->slen = 0;
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) {
67 int i, n, m;
68 unsigned char t;
70 if (b == NULL || b->slen < 0 || b->mlen < b->slen) return -__LINE__;
71 n = b->slen;
72 if (2 <= n) {
73 m = ((unsigned)n) >> 1;
74 n--;
75 for (i=0; i < m; i++) {
76 t = b->data[n - i];
77 b->data[n - i] = b->data[i];
78 b->data[i] = t;
81 return 0;
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__;
92 if (pos > b->slen
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);
98 b->slen += len;
99 b->data[b->slen] = (unsigned char) '\0';
100 return BSTR_OK;
103 /* int bJustifyLeft (bstring b, int space)
105 * Left justify a string.
107 int bJustifyLeft (bstring b, int space) {
108 int j, i, s, t;
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++) {
115 t = s;
116 s = c != (b->data[j] = b->data[i]);
117 j += (t|s);
119 if (j > 0 && b->data[j-1] == c) j--;
121 b->data[j] = (unsigned char) '\0';
122 b->slen = j;
123 return BSTR_OK;
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) {
131 int ret;
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);
136 return BSTR_OK;
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) {
145 int ret;
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);
150 return BSTR_OK;
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;
161 int i, l, c;
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) {
167 c ++;
168 l += sl->entry[i]->slen;
172 if (l + c >= width || c < 2) {
173 bstrListDestroy (sl);
174 return bJustifyLeft (b, space);
177 b->slen = 0;
178 for (i=0; i < sl->qty; i++) {
179 if (sl->entry[i]->slen > 0) {
180 if (b->slen > 0) {
181 int s = (width - l + (c / 2)) / c;
182 bInsertChrs (b, b->slen, s, (unsigned char) space, (unsigned char) space);
183 l += s;
185 bconcat (b, sl->entry[i]);
186 c--;
187 if (c <= 0) break;
191 bstrListDestroy (sl);
192 return BSTR_OK;
195 static size_t readNothing (void *buff, size_t elsize, size_t nelem, void *parm) {
196 buff = buff;
197 elsize = elsize;
198 nelem = nelem;
199 parm = 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. */
211 return s;
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;
219 if (tsz > 0) {
220 memcpy (buff, t->data, tsz);
221 t->slen -= (int) tsz;
222 t->data += tsz;
223 return tsz / elsize;
225 return 0;
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) {
235 if (!t) return NULL;
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
245 * leak.
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];
251 bstring s;
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) {
258 bdestroy (s);
259 return NULL;
261 buff = s->data;
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'
271 * is *not* required.
273 bstring bNetStr2Bstr (const char * buff) {
274 int i, x;
275 bstring b;
276 if (buff == NULL) return NULL;
277 x = 0;
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;
281 x = (x * 10) + v;
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) {
289 bdestroy (b);
290 return NULL;
292 memcpy (b->data, buff + i + 1, x);
293 b->data[x] = (unsigned char) '\0';
294 b->slen = x;
295 return b;
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;
306 bstring out;
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) {
314 bdestroy (out);
315 return NULL;
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) {
328 bdestroy (out);
329 return NULL;
333 if (i && ((i % 57) == 0)) {
334 if (bconchar (out, (char) '\015') < 0 || bconchar (out, (char) '\012') < 0) {
335 bdestroy (out);
336 return NULL;
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) {
349 bdestroy (out);
350 return NULL;
352 break;
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) {
359 bdestroy (out);
360 return NULL;
362 break;
363 case 2: break;
366 return out;
369 #define B64_PAD (-2)
370 #define B64_ERR (-1)
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;
381 else return B64_ERR;
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) {
390 int i, v;
391 unsigned char c0, c1, c2;
392 bstring out;
394 if (b == NULL || b->slen < 0 || b->data == NULL) return NULL;
395 if (boolTruncError) *boolTruncError = 0;
396 out = bfromcstr ("");
397 i = 0;
398 for (;;) {
399 do {
400 if (i >= b->slen) return out;
401 if (b->data[i] == '=') { /* Bad "too early" truncation */
402 if (boolTruncError) {
403 *boolTruncError = 1;
404 return out;
406 bdestroy (out);
407 return NULL;
409 v = base64DecodeSymbol (b->data[i]);
410 i++;
411 } while (v < 0);
412 c0 = (unsigned char) (v << 2);
413 do {
414 if (i >= b->slen || b->data[i] == '=') { /* Bad "too early" truncation */
415 if (boolTruncError) {
416 *boolTruncError = 1;
417 return out;
419 bdestroy (out);
420 return NULL;
422 v = base64DecodeSymbol (b->data[i]);
423 i++;
424 } while (v < 0);
425 c0 |= (unsigned char) (v >> 4);
426 c1 = (unsigned char) (v << 4);
427 do {
428 if (i >= b->slen) {
429 if (boolTruncError) {
430 *boolTruncError = 1;
431 return out;
433 bdestroy (out);
434 return NULL;
436 if (b->data[i] == '=') {
437 i++;
438 if (i >= b->slen || b->data[i] != '=' || bconchar (out, c0) < 0) {
439 if (boolTruncError) {
440 *boolTruncError = 1;
441 return out;
443 bdestroy (out); /* Missing "=" at the end. */
444 return NULL;
446 return out;
448 v = base64DecodeSymbol (b->data[i]);
449 i++;
450 } while (v < 0);
451 c1 |= (unsigned char) (v >> 2);
452 c2 = (unsigned char) (v << 6);
453 do {
454 if (i >= b->slen) {
455 if (boolTruncError) {
456 *boolTruncError = 1;
457 return out;
459 bdestroy (out);
460 return NULL;
462 if (b->data[i] == '=') {
463 if (bconchar (out, c0) < 0 || bconchar (out, c1) < 0) {
464 if (boolTruncError) {
465 *boolTruncError = 1;
466 return out;
468 bdestroy (out);
469 return NULL;
471 if (boolTruncError) *boolTruncError = 0;
472 return out;
474 v = base64DecodeSymbol (b->data[i]);
475 i++;
476 } while (v < 0);
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;
483 return out;
485 bdestroy (out);
486 return NULL;
491 #define UU_DECODE_BYTE(b) (((b) == (signed int)'`') ? 0 : (b) - (signed int)' ')
493 struct bUuInOut {
494 bstring src, dst;
495 int * badlines;
498 #define UU_MAX_LINELEN 45
500 static int bUuDecLine (void * parm, int ofs, int len) {
501 struct bUuInOut * io = (struct bUuInOut *) parm;
502 bstring s = io->src;
503 bstring t = io->dst;
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]);
508 ret = 0;
510 otlen = t->slen;
512 if (((unsigned) llen) > UU_MAX_LINELEN) { ret = -__LINE__;
513 goto bl;
516 llen += t->slen;
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))) {
528 t->slen = otlen;
529 goto bl;
531 c0 = c1 = 0;
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__;
536 break;
538 if ((unsigned) c2 >= 0x40) { if (!ret) ret = -__LINE__;
539 if (d2 > 0x60 || (d2 < (' ' - 1) && !isspace (d2))) {
540 t->slen = otlen;
541 goto bl;
543 c2 = 0;
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__;
548 break;
550 if ((unsigned) c3 >= 0x40) { if (!ret) ret = -__LINE__;
551 if (d3 > 0x60 || (d3 < (' ' - 1) && !isspace (d3))) {
552 t->slen = otlen;
553 goto bl;
555 c3 = 0;
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__;
561 t->slen = otlen;
563 bl:;
564 if (ret && io->badlines) {
565 (*io->badlines)++;
566 return 0;
568 return ret;
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
578 * portable library.
581 #ifdef _MSC_VER
582 #pragma warning(disable:4204)
583 #endif
585 bstring bUuDecodeEx (const_bstring src, int * badlines) {
586 struct tagbstring t;
587 struct bStream * s;
588 struct bStream * d;
589 bstring b;
591 if (!src) return NULL;
592 t = *src; /* Short lifetime alias to header of src */
593 s = bsFromBstrRef (&t); /* t is undefined after this */
594 if (!s) return NULL;
595 d = bsUuDecode (s, badlines);
596 b = bfromcstralloc (256, "");
597 if (NULL == b || 0 > bsread (b, d, INT_MAX)) {
598 bdestroy (b);
599 bsclose (d);
600 bsclose (s);
601 return NULL;
603 return b;
606 struct bsUuCtx {
607 struct bUuInOut io;
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;
614 size_t tsz;
615 int l, lret;
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);
625 return nelem;
628 DecodeMore:;
629 if (0 <= (l = binchr (luuCtx->io.src, 0, &eol))) {
630 int ol = 0;
631 struct tagbstring t;
632 bstring s = luuCtx->io.src;
633 luuCtx->io.src = &t;
635 do {
636 if (l > ol) {
637 bmid2tbstr (t, s, ol, l - ol);
638 lret = bUuDecLine (&luuCtx->io, 0, t.slen);
639 if (0 > lret) {
640 luuCtx->io.src = s;
641 goto Done;
644 ol = l + 1;
645 if (((size_t) luuCtx->io.dst->slen) > tsz) break;
646 l = binchr (s, ol, &eol);
647 } while (BSTR_ERR != l);
648 bdelete (s, 0, ol);
649 luuCtx->io.src = s;
650 goto CheckInternalBuffer;
653 if (BSTR_ERR != bsreada (luuCtx->io.src, luuCtx->sInp, bsbufflength (luuCtx->sInp, BSTR_BS_BUFF_LENGTH_GET))) {
654 goto DecodeMore;
657 bUuDecLine (&luuCtx->io, 0, luuCtx->io.src->slen);
659 Done:;
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);
672 free (luuCtx);
673 return 0;
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);
698 free (luuCtx);
699 return NULL;
701 luuCtx->io.badlines = badlines;
702 if (badlines) *badlines = 0;
704 luuCtx->sInp = sInp;
706 sOut = bsopen ((bNread) bsUuDecodePart, luuCtx);
707 if (NULL == sOut) goto CleanUpFailureToAllocate;
708 return sOut;
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
716 * not appended.
718 bstring bUuEncode (const_bstring src) {
719 bstring out;
720 int i, j, jm;
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) {
727 bstrFree (out);
728 break;
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) {
738 bstrFree (out);
739 goto End;
742 if (bconchar (out, (char) '\r') < 0 || bconchar (out, (char) '\n') < 0) {
743 bstrFree (out);
744 break;
747 End:;
748 return out;
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) {
758 int i;
759 bstring out;
760 unsigned char c;
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) '=')) {
768 bdestroy (out);
769 return NULL;
771 c += (unsigned char) 64;
773 if (0 > bconchar (out, c)) {
774 bdestroy (out);
775 return NULL;
778 return out;
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) {
789 int i;
790 bstring out;
791 unsigned char c;
792 unsigned char octetbuff[MAX_OB_LEN];
793 int obl;
795 if (src == NULL || src->slen < 0 || src->data == NULL) return NULL;
796 if ((out = bfromcstr ("")) == NULL) return NULL;
798 obl = 0;
800 for (i=0; i < src->slen; i++) {
801 if ('=' == (c = src->data[i])) { /* The = escape mode */
802 i++;
803 if (i >= src->slen) {
804 bdestroy (out);
805 return NULL;
807 c = (unsigned char) (src->data[i] - 64);
808 } else {
809 if ('\0' == c) {
810 bdestroy (out);
811 return NULL;
814 /* Extraneous CR/LFs are to be ignored. */
815 if (c == '\r' || c == '\n') continue;
818 octetbuff[obl] = (unsigned char) ((int) c - 42);
819 obl++;
821 if (obl >= MAX_OB_LEN) {
822 if (0 > bcatblk (out, octetbuff, obl)) {
823 bdestroy (out);
824 return NULL;
826 obl = 0;
830 if (0 > bcatblk (out, octetbuff, obl)) {
831 bdestroy (out);
832 out = NULL;
834 return out;
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");
847 fmt = fmt;
848 timeptr = timeptr;
849 return &ns;
850 #else
851 bstring buff;
852 int n;
853 size_t r;
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, "");
864 for (;;) {
865 if (BSTR_OK != balloc (buff, n + 2)) {
866 bdestroy (buff);
867 return NULL;
870 r = strftime ((char *) buff->data, n + 1, fmt, timeptr);
872 if (r > 0) {
873 buff->slen = (int) r;
874 break;
877 n += n;
880 return buff;
881 #endif
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)
894 return BSTR_ERR;
895 if (pos < 0 || pos > b->slen) return BSTR_ERR;
897 if (pos == b->slen) {
898 if ('\0' != c) return bconchar (b, c);
899 return 0;
902 b->data[pos] = (unsigned char) c;
903 if ('\0' == c) b->slen = pos;
905 return 0;
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
913 * will be returned.
915 int bSetChar (bstring b, int pos, char c) {
916 if (NULL == b || b->mlen <= 0 || b->slen < 0 || b->mlen < b->slen)
917 return BSTR_ERR;
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;
925 return 0;
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) {
940 int i, m, c;
941 bstring b, t;
943 if (!vgetchar) return NULL;
945 b = bfromcstralloc (INIT_SECURE_INPUT_LENGTH, "");
946 if ((c = UCHAR_MAX + 1) == termchar) c++;
948 for (i=0; ; i++) {
949 if (termchar == c || (maxlen > 0 && i >= maxlen)) c = EOF;
950 else c = vgetchar (vgcCtx);
952 if (EOF == c) break;
954 if (i+1 >= b->mlen) {
956 /* Double size, but deal with unusual case of numeric
957 overflows */
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 */
967 b = t;
968 if (!b) return b;
971 b->data[i] = (unsigned char) c;
974 b->slen = i;
975 b->data[i] = (unsigned char) '\0';
976 return b;
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 */
986 int minBuffSz;
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));
1000 if (ws) {
1001 if (NULL == (ws->buff = bfromcstr (""))) {
1002 free (ws);
1003 ws = NULL;
1004 } else {
1005 ws->parm = parm;
1006 ws->writeFn = writeFn;
1007 ws->isEOF = 0;
1008 ws->minBuffSz = BWS_BUFF_SZ;
1011 return ws;
1014 #define internal_bwswriteout(ws,b) { \
1015 if ((b)->slen > 0) { \
1016 if (1 != (ws->writeFn ((b)->data, (b)->slen, 1, ws->parm))) { \
1017 ws->isEOF = 1; \
1018 return BSTR_ERR; \
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);
1031 ws->buff->slen = 0;
1032 return 0;
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;
1043 int l;
1045 if (NULL == ws || NULL == b || NULL == ws->buff ||
1046 ws->isEOF || 0 >= ws->minBuffSz || NULL == ws->writeFn)
1047 return BSTR_ERR;
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);
1058 ws->buff->slen = 0;
1059 l = ws->minBuffSz;
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);
1066 ws->buff->slen = 0;
1068 bmid2tbstr (t, (bstring) b, l, b->slen);
1070 if (t.slen >= ws->minBuffSz) {
1071 internal_bwswriteout (ws, &t);
1072 return 0;
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
1081 * returned.
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;
1098 return ws->isEOF;
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) {
1107 int oldSz;
1108 if (ws == NULL || sz < 0) return BSTR_ERR;
1109 oldSz = ws->minBuffSz;
1110 if (sz > 0) ws->minBuffSz = sz;
1111 return oldSz;
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) {
1121 void * parm;
1122 if (NULL == ws || NULL == ws->buff || 0 >= ws->minBuffSz ||
1123 NULL == ws->writeFn) return NULL;
1124 bwsWriteFlush (ws);
1125 parm = ws->parm;
1126 ws->parm = NULL;
1127 ws->minBuffSz = -1;
1128 ws->writeFn = NULL;
1129 bstrFree (ws->buff);
1130 free (ws);
1131 return parm;