]> code.citadel.org Git - citadel.git/blob - libcitadel/lib/stringbuf.c
* add VALGRIND flag to debian debug flag to make valgrind produce less noise in the...
[citadel.git] / libcitadel / lib / stringbuf.c
1 #include <ctype.h>
2 #include <errno.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <string.h>
6 #include <stdio.h>
7 #include <sys/select.h>
8 #include <fcntl.h>
9 #define SHOW_ME_VAPPEND_PRINTF
10 #include <stdarg.h>
11 #include "libcitadel.h"
12
13
14 struct StrBuf {
15         char *buf;
16         long BufSize;
17         long BufUsed;
18         int ConstBuf;
19 };
20
21
22 inline const char *ChrPtr(const StrBuf *Str)
23 {
24         if (Str == NULL)
25                 return "";
26         return Str->buf;
27 }
28
29 inline int StrLength(const StrBuf *Str)
30 {
31         return (Str != NULL) ? Str->BufUsed : 0;
32 }
33
34 StrBuf* NewStrBuf(void)
35 {
36         StrBuf *NewBuf;
37
38         NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
39         NewBuf->buf = (char*) malloc(SIZ);
40         NewBuf->buf[0] = '\0';
41         NewBuf->BufSize = SIZ;
42         NewBuf->BufUsed = 0;
43         NewBuf->ConstBuf = 0;
44         return NewBuf;
45 }
46
47
48 StrBuf* NewStrBufPlain(const char* ptr, int nChars)
49 {
50         StrBuf *NewBuf;
51         size_t Siz = SIZ;
52         size_t CopySize;
53
54         NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
55         if (nChars < 0)
56                 CopySize = strlen((ptr != NULL)?ptr:"");
57         else
58                 CopySize = nChars;
59
60         while (Siz <= CopySize)
61                 Siz *= 2;
62
63         NewBuf->buf = (char*) malloc(Siz);
64         NewBuf->BufSize = Siz;
65         if (ptr != NULL) {
66                 memcpy(NewBuf->buf, ptr, CopySize);
67                 NewBuf->buf[CopySize] = '\0';
68                 NewBuf->BufUsed = CopySize;
69         }
70         else {
71                 NewBuf->buf[0] = '\0';
72                 NewBuf->BufUsed = 0;
73         }
74         NewBuf->ConstBuf = 0;
75         return NewBuf;
76 }
77
78
79 StrBuf* _NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant)
80 {
81         StrBuf *NewBuf;
82
83         NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
84         NewBuf->buf = (char*) StringConstant;
85         NewBuf->BufSize = SizeOfStrConstant;
86         NewBuf->BufUsed = SizeOfStrConstant;
87         NewBuf->ConstBuf = 1;
88         return NewBuf;
89 }
90
91
92 static int IncreaseBuf(StrBuf *Buf, int KeepOriginal, int DestSize)
93 {
94         char *NewBuf;
95         size_t NewSize = Buf->BufSize * 2;
96
97         if (Buf->ConstBuf)
98                 return -1;
99                 
100         if (DestSize > 0)
101                 while (NewSize < DestSize)
102                         NewSize *= 2;
103
104         NewBuf= (char*) malloc(NewSize);
105         if (KeepOriginal)
106         {
107                 memcpy(NewBuf, Buf->buf, Buf->BufUsed);
108         }
109         else
110         {
111                 NewBuf[0] = '\0';
112                 Buf->BufUsed = 0;
113         }
114         free (Buf->buf);
115         Buf->buf = NewBuf;
116         Buf->BufSize *= 2;
117         return Buf->BufSize;
118 }
119
120 int FlushStrBuf(StrBuf *buf)
121 {
122         if (buf->ConstBuf)
123                 return -1;       
124         buf->buf[0] ='\0';
125         buf->BufUsed = 0;
126         return 0;
127 }
128
129 void FreeStrBuf (StrBuf **FreeMe)
130 {
131         if (*FreeMe == NULL)
132                 return;
133         if (!(*FreeMe)->ConstBuf) 
134                 free((*FreeMe)->buf);
135         free(*FreeMe);
136         *FreeMe = NULL;
137 }
138
139 void HFreeStrBuf (void *VFreeMe)
140 {
141         StrBuf *FreeMe = (StrBuf*)VFreeMe;
142         if (FreeMe == NULL)
143                 return;
144         if (!FreeMe->ConstBuf) 
145                 free(FreeMe->buf);
146         free(FreeMe);
147 }
148
149 long StrTol(const StrBuf *Buf)
150 {
151         if(Buf->BufUsed > 0)
152                 return atol(Buf->buf);
153         else
154                 return 0;
155 }
156
157 int StrToi(const StrBuf *Buf)
158 {
159         if(Buf->BufUsed > 0)
160                 return atoi(Buf->buf);
161         else
162                 return 0;
163 }
164
165 int StrBufPlain(StrBuf *Buf, const char* ptr, int nChars)
166 {
167         size_t Siz = Buf->BufSize;
168         size_t CopySize;
169
170         if (nChars < 0)
171                 CopySize = strlen(ptr);
172         else
173                 CopySize = nChars;
174
175         while (Siz <= CopySize)
176                 Siz *= 2;
177
178         if (Siz != Buf->BufSize)
179                 IncreaseBuf(Buf, 0, Siz);
180         memcpy(Buf->buf, ptr, CopySize);
181         Buf->buf[CopySize] = '\0';
182         Buf->BufUsed = CopySize;
183         Buf->ConstBuf = 0;
184         return CopySize;
185 }
186
187 void StrBufAppendBuf(StrBuf *Buf, const StrBuf *AppendBuf, size_t Offset)
188 {
189         if ((AppendBuf == NULL) || (Buf == NULL))
190                 return;
191
192         if (Buf->BufSize - Offset < AppendBuf->BufUsed + Buf->BufUsed)
193                 IncreaseBuf(Buf, 
194                             (Buf->BufUsed > 0), 
195                             AppendBuf->BufUsed + Buf->BufUsed);
196
197         memcpy(Buf->buf + Buf->BufUsed, 
198                AppendBuf->buf + Offset, 
199                AppendBuf->BufUsed - Offset);
200         Buf->BufUsed += AppendBuf->BufUsed - Offset;
201         Buf->buf[Buf->BufUsed] = '\0';
202 }
203
204
205 void StrBufAppendBufPlain(StrBuf *Buf, const char *AppendBuf, long AppendSize, size_t Offset)
206 {
207         long aps;
208
209         if ((AppendBuf == NULL) || (Buf == NULL))
210                 return;
211
212         if (AppendSize < 0 )
213                 aps = strlen(AppendBuf + Offset);
214         else
215                 aps = AppendSize - Offset;
216
217         if (Buf->BufSize < Buf->BufUsed + aps)
218                 IncreaseBuf(Buf, (Buf->BufUsed > 0), Buf->BufUsed + aps);
219
220         memcpy(Buf->buf + Buf->BufUsed, 
221                AppendBuf + Offset, 
222                aps);
223         Buf->BufUsed += aps;
224         Buf->buf[Buf->BufUsed] = '\0';
225 }
226
227
228 inline int StrBufNum_tokens(const StrBuf *source, char tok)
229 {
230         return num_tokens(source->buf, tok);
231 }
232
233
234 int StrBufSub(StrBuf *dest, const StrBuf *Source, size_t Offset, size_t nChars)
235 {
236         size_t NCharsRemain;
237         if (Offset > Source->BufUsed)
238         {
239                 FlushStrBuf(dest);
240                 return 0;
241         }
242         if (Offset + nChars < Source->BufUsed)
243         {
244                 if (nChars > dest->BufSize)
245                         IncreaseBuf(dest, 0, nChars + 1);
246                 memcpy(dest->buf, Source->buf + Offset, nChars);
247                 dest->BufUsed = nChars;
248                 dest->buf[dest->BufUsed] = '\0';
249                 return nChars;
250         }
251         NCharsRemain = Source->BufUsed - Offset;
252         if (NCharsRemain > dest->BufSize)
253                 IncreaseBuf(dest, 0, NCharsRemain + 1);
254         memcpy(dest->buf, Source->buf + Offset, NCharsRemain);
255         dest->BufUsed = NCharsRemain;
256         dest->buf[dest->BufUsed] = '\0';
257         return NCharsRemain;
258 }
259
260
261 void StrBufVAppendPrintf(StrBuf *Buf, const char *format, va_list ap)
262 {
263         size_t nWritten = Buf->BufSize + 1;
264         size_t Offset = Buf->BufUsed;
265         size_t newused = Offset + nWritten;
266         
267         while (newused >= Buf->BufSize) {
268                 nWritten = vsnprintf(Buf->buf + Offset, 
269                                      Buf->BufSize - Offset, 
270                                      format, ap);
271                 newused = Offset + nWritten;
272                 if (newused >= Buf->BufSize)
273                         IncreaseBuf(Buf, 1, 0);
274                 else
275                         Buf->BufUsed = Offset + nWritten ;
276
277         }
278 }
279
280 void StrBufPrintf(StrBuf *Buf, const char *format, ...)
281 {
282         size_t nWritten = Buf->BufSize + 1;
283         va_list arg_ptr;
284         
285         while (nWritten >= Buf->BufSize) {
286                 va_start(arg_ptr, format);
287                 nWritten = vsnprintf(Buf->buf, Buf->BufSize, format, arg_ptr);
288                 va_end(arg_ptr);
289                 Buf->BufUsed = nWritten ;
290                 if (nWritten >= Buf->BufSize)
291                         IncreaseBuf(Buf, 0, 0);
292         }
293 }
294
295
296 /**
297  * \brief a string tokenizer
298  * \param dest Destination StringBuffer
299  * \param Source StringBuffer to read into
300  * \param separator tokenizer param
301  * \returns -1 if not found, else length of token.
302  */
303 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
304 {
305         const char *s, *e;              //* source * /
306         int len = 0;                    //* running total length of extracted string * /
307         int current_token = 0;          //* token currently being processed * /
308
309         if ((Source == NULL) || (Source->BufUsed ==0)) {
310                 return(-1);
311         }
312         s = Source->buf;
313         e = s + Source->BufUsed;
314         if (dest == NULL) {
315                 return(-1);
316         }
317
318         //cit_backtrace();
319         //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
320         dest->buf[0] = '\0';
321         dest->BufUsed = 0;
322
323         while ((s<e) && !IsEmptyStr(s)) {
324                 if (*s == separator) {
325                         ++current_token;
326                 }
327                 if (len >= dest->BufSize)
328                         if (!IncreaseBuf(dest, 1, -1))
329                                 break;
330                 if ( (current_token == parmnum) && 
331                      (*s != separator)) {
332                         dest->buf[len] = *s;
333                         ++len;
334                 }
335                 else if (current_token > parmnum) {
336                         break;
337                 }
338                 ++s;
339         }
340         
341         dest->buf[len] = '\0';
342         dest->BufUsed = len;
343                 
344         if (current_token < parmnum) {
345                 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
346                 return(-1);
347         }
348         //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
349         return(len);
350 }
351
352
353 /*
354  * extract_int()  -  extract an int parm w/o supplying a buffer
355  */
356 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
357 {
358         StrBuf tmp;
359         char buf[64];
360         
361         tmp.buf = buf;
362         buf[0] = '\0';
363         tmp.BufSize = 64;
364         tmp.BufUsed = 0;
365         if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
366                 return(atoi(buf));
367         else
368                 return 0;
369 }
370
371 /*
372  * extract_long()  -  extract an long parm w/o supplying a buffer
373  */
374 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
375 {
376         StrBuf tmp;
377         char buf[64];
378         
379         tmp.buf = buf;
380         buf[0] = '\0';
381         tmp.BufSize = 64;
382         tmp.BufUsed = 0;
383         if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
384                 return(atoi(buf));
385         else
386                 return 0;
387 }
388
389
390 /*
391  * extract_unsigned_long() - extract an unsigned long parm
392  */
393 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
394 {
395         StrBuf tmp;
396         char buf[64];
397         
398         tmp.buf = buf;
399         buf[0] = '\0';
400         tmp.BufSize = 64;
401         tmp.BufUsed = 0;
402         if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
403                 return(atoi(buf));
404         else 
405                 return 0;
406 }
407
408
409
410 /**
411  * \brief Input binary data from socket
412  * \param buf the buffer to get the input to
413  * \param bytes the maximal number of bytes to read
414  */
415 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
416 {
417         int len, rlen, slen;
418
419         if (!append)
420                 FlushStrBuf(buf);
421
422         slen = len = buf->BufUsed;
423         while (1) {
424                 rlen = read(*fd, &buf->buf[len], 1);
425                 if (rlen < 1) {
426                         *Error = strerror(errno);
427                         
428                         close(*fd);
429                         *fd = -1;
430                         
431                         return -1;
432                 }
433                 if (buf->buf[len] == '\n')
434                         break;
435                 if (buf->buf[len] != '\r')
436                         len ++;
437                 if (!(len < buf->BufSize)) {
438                         buf->BufUsed = len;
439                         buf->buf[len+1] = '\0';
440                         IncreaseBuf(buf, 1, -1);
441                 }
442         }
443         buf->BufUsed = len;
444         buf->buf[len] = '\0';
445         return len - slen;
446 }
447
448 /**
449  * \brief Input binary data from socket
450  * \param buf the buffer to get the input to
451  * \param bytes the maximal number of bytes to read
452  */
453 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
454 {
455         fd_set wset;
456         int fdflags;
457         int len, rlen, slen;
458         int nRead = 0;
459         char *ptr;
460
461         if ((Buf == NULL) || (*fd == -1))
462                 return -1;
463         if (!append)
464                 FlushStrBuf(Buf);
465         if (Buf->BufUsed + nBytes > Buf->BufSize)
466                 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
467
468         ptr = Buf->buf + Buf->BufUsed;
469
470         slen = len = Buf->BufUsed;
471
472         fdflags = fcntl(*fd, F_GETFL);
473
474         while (nRead < nBytes) {
475                if ((fdflags & O_NONBLOCK) == O_NONBLOCK) {
476                         FD_ZERO(&wset);
477                         FD_SET(*fd, &wset);
478                         if (select(*fd + 1, NULL, &wset, NULL, NULL) == -1) {
479                                 *Error = strerror(errno);
480                                 return -1;
481                         }
482                 }
483
484                 if ((rlen = read(*fd, 
485                                  ptr,
486                                  nBytes - nRead)) == -1) {
487                         close(*fd);
488                         *fd = -1;
489                         *Error = strerror(errno);
490                         return rlen;
491                 }
492                 nRead += rlen;
493                 Buf->BufUsed += rlen;
494         }
495         Buf->buf[Buf->BufUsed] = '\0';
496         return nRead;
497 }
498
499 void StrBufCutLeft(StrBuf *Buf, int nChars)
500 {
501         if (nChars >= Buf->BufUsed) {
502                 FlushStrBuf(Buf);
503                 return;
504         }
505         memmove(Buf->buf, Buf->buf + nChars, Buf->BufUsed - nChars);
506         Buf->BufUsed -= nChars;
507         Buf->buf[Buf->BufUsed] = '\0';
508 }
509
510 void StrBufCutRight(StrBuf *Buf, int nChars)
511 {
512         if (nChars >= Buf->BufUsed) {
513                 FlushStrBuf(Buf);
514                 return;
515         }
516         Buf->BufUsed -= nChars;
517         Buf->buf[Buf->BufUsed] = '\0';
518 }
519
520
521 /*
522  * string conversion function
523  */
524 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source) 
525 {
526         int a, b, len;
527         char hex[3];
528
529         if (target != NULL)
530                 FlushStrBuf(target);
531
532         if (source == NULL ||target == NULL)
533         {
534                 return;
535         }
536
537         len = source->BufUsed;
538         for (a = 0; a < len; ++a) {
539                 if (target->BufUsed >= target->BufSize)
540                         IncreaseBuf(target, 1, -1);
541
542                 if (source->buf[a] == '=') {
543                         hex[0] = source->buf[a + 1];
544                         hex[1] = source->buf[a + 2];
545                         hex[2] = 0;
546                         b = 0;
547                         sscanf(hex, "%02x", &b);
548                         target->buf[target->BufUsed] = b;
549                         target->buf[++target->BufUsed] = 0;
550                         a += 2;
551                 }
552                 else {
553                         target->buf[target->BufUsed] = source->buf[a];
554                         target->buf[++target->BufUsed] = 0;
555                 }
556         }
557 }
558
559
560 /*
561  * string conversion function
562  */
563 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source) 
564 {
565         int i, len;
566
567         if (target != NULL)
568                 FlushStrBuf(target);
569
570         if (source == NULL ||target == NULL)
571         {
572                 return;
573         }
574
575         len = source->BufUsed;
576         for (i=0; i<len; ++i) {
577                 if (target->BufUsed + 4 >= target->BufSize)
578                         IncreaseBuf(target, 1, -1);
579                 if ( (isalnum(source->buf[i])) || 
580                      (source->buf[i]=='-') || 
581                      (source->buf[i]=='_') ) {
582                         target->buf[target->BufUsed++] = source->buf[i];
583                 }
584                 else {
585                         sprintf(&target->buf[target->BufUsed], 
586                                 "=%02X", 
587                                 (0xFF &source->buf[i]));
588                         target->BufUsed += 3;
589                 }
590         }
591         target->buf[target->BufUsed + 1] = '\0';
592 }
593
594 /*
595  * \brief uses the same calling syntax as compress2(), but it
596  * creates a stream compatible with HTTP "Content-encoding: gzip"
597  */
598 #ifdef HAVE_ZLIB
599 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
600 #define OS_CODE 0x03    /*< unix */
601 int ZEXPORT compress_gzip(Bytef * dest,         /*< compressed buffer*/
602                           size_t * destLen,     /*< length of the compresed data */
603                           const Bytef * source, /*< source to encode */
604                           uLong sourceLen,      /*< length of source to encode */
605                           int level)            /*< compression level */
606 {
607         const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
608
609         /* write gzip header */
610         snprintf((char *) dest, *destLen, 
611                  "%c%c%c%c%c%c%c%c%c%c",
612                  gz_magic[0], gz_magic[1], Z_DEFLATED,
613                  0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
614                  OS_CODE);
615
616         /* normal deflate */
617         z_stream stream;
618         int err;
619         stream.next_in = (Bytef *) source;
620         stream.avail_in = (uInt) sourceLen;
621         stream.next_out = dest + 10L;   // after header
622         stream.avail_out = (uInt) * destLen;
623         if ((uLong) stream.avail_out != *destLen)
624                 return Z_BUF_ERROR;
625
626         stream.zalloc = (alloc_func) 0;
627         stream.zfree = (free_func) 0;
628         stream.opaque = (voidpf) 0;
629
630         err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
631                            DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
632         if (err != Z_OK)
633                 return err;
634
635         err = deflate(&stream, Z_FINISH);
636         if (err != Z_STREAM_END) {
637                 deflateEnd(&stream);
638                 return err == Z_OK ? Z_BUF_ERROR : err;
639         }
640         *destLen = stream.total_out + 10L;
641
642         /* write CRC and Length */
643         uLong crc = crc32(0L, source, sourceLen);
644         int n;
645         for (n = 0; n < 4; ++n, ++*destLen) {
646                 dest[*destLen] = (int) (crc & 0xff);
647                 crc >>= 8;
648         }
649         uLong len = stream.total_in;
650         for (n = 0; n < 4; ++n, ++*destLen) {
651                 dest[*destLen] = (int) (len & 0xff);
652                 len >>= 8;
653         }
654         err = deflateEnd(&stream);
655         return err;
656 }
657 #endif
658
659
660 /**
661  * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
662  */
663 int CompressBuffer(StrBuf *Buf)
664 {
665 #ifdef HAVE_ZLIB
666         char *compressed_data = NULL;
667         size_t compressed_len, bufsize;
668         
669         bufsize = compressed_len = ((Buf->BufUsed * 101) / 100) + 100;
670         compressed_data = malloc(compressed_len);
671         
672         if (compress_gzip((Bytef *) compressed_data,
673                           &compressed_len,
674                           (Bytef *) Buf->buf,
675                           (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
676                 if (!ConstBuf)
677                         free(Buf->buf);
678                 Buf->buf = compressed_data;
679                 Buf->BufUsed = compressed_len;
680                 Buf->BufSize = bufsize;
681                 return 1;
682         } else {
683                 free(compressed_data);
684         }
685 #endif  /* HAVE_ZLIB */
686         return 0;
687 }
688
689 int StrBufDecodeBase64(StrBuf *Buf)
690 {
691         char *xferbuf;
692         size_t siz;
693         if (Buf == NULL) return -1;
694
695         xferbuf = (char*) malloc(Buf->BufSize);
696         siz = CtdlDecodeBase64(xferbuf,
697                                Buf->buf,
698                                Buf->BufUsed);
699         free(Buf->buf);
700         Buf->buf = xferbuf;
701         Buf->BufUsed = siz;
702         return siz;
703 }
704
705
706 /*   
707  * remove escaped strings from i.e. the url string (like %20 for blanks)
708  */
709 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
710 {
711         int a, b;
712         char hex[3];
713         long len;
714
715         while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
716                 Buf->buf[Buf->BufUsed - 1] = '\0';
717                 Buf->BufUsed --;
718         }
719
720         a = 0; 
721         while (a < Buf->BufUsed) {
722                 if (Buf->buf[a] == '+')
723                         Buf->buf[a] = ' ';
724                 else if (Buf->buf[a] == '%') {
725                         /* don't let % chars through, rather truncate the input. */
726                         if (a + 2 > Buf->BufUsed) {
727                                 Buf->buf[a] = '\0';
728                                 Buf->BufUsed = a;
729                         }
730                         else {                  
731                                 hex[0] = Buf->buf[a + 1];
732                                 hex[1] = Buf->buf[a + 2];
733                                 hex[2] = 0;
734                                 b = 0;
735                                 sscanf(hex, "%02x", &b);
736                                 Buf->buf[a] = (char) b;
737                                 len = Buf->BufUsed - a - 2;
738                                 if (len > 0)
739                                         memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
740                         
741                                 Buf->BufUsed -=2;
742                         }
743                 }
744                 a++;
745         }
746         return a;
747 }
748
749
750 /**
751  * \brief       RFC2047-encode a header field if necessary.
752  *              If no non-ASCII characters are found, the string
753  *              will be copied verbatim without encoding.
754  *
755  * \param       target          Target buffer.
756  * \param       source          Source string to be encoded.
757  * \returns     encoded length; -1 if non success.
758  */
759 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
760 {
761         const char headerStr[] = "=?UTF-8?Q?";
762         int need_to_encode = 0;
763         int i = 0;
764         unsigned char ch;
765
766         if ((source == NULL) || 
767             (target == NULL))
768             return -1;
769
770         while ((i < source->BufUsed) &&
771                (!IsEmptyStr (&source->buf[i])) &&
772                (need_to_encode == 0)) {
773                 if (((unsigned char) source->buf[i] < 32) || 
774                     ((unsigned char) source->buf[i] > 126)) {
775                         need_to_encode = 1;
776                 }
777                 i++;
778         }
779
780         if (!need_to_encode) {
781                 if (*target == NULL) {
782                         *target = NewStrBufPlain(source->buf, source->BufUsed);
783                 }
784                 else {
785                         FlushStrBuf(*target);
786                         StrBufAppendBuf(*target, source, 0);
787                 }
788                 return (*target)->BufUsed;
789         }
790         if (*target == NULL)
791                 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
792         else if (sizeof(headerStr) + source->BufUsed > (*target)->BufSize)
793                 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
794         memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
795         (*target)->BufUsed = sizeof(headerStr) - 1;
796         for (i=0; (i < source->BufUsed); ++i) {
797                 if ((*target)->BufUsed + 4 > (*target)->BufSize)
798                         IncreaseBuf(*target, 1, 0);
799                 ch = (unsigned char) source->buf[i];
800                 if ((ch < 32) || (ch > 126) || (ch == 61)) {
801                         sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
802                         (*target)->BufUsed += 3;
803                 }
804                 else {
805                         (*target)->buf[(*target)->BufUsed] = ch;
806                         (*target)->BufUsed++;
807                 }
808         }
809         
810         if ((*target)->BufUsed + 4 > (*target)->BufSize)
811                 IncreaseBuf(*target, 1, 0);
812
813         (*target)->buf[(*target)->BufUsed++] = '?';
814         (*target)->buf[(*target)->BufUsed++] = '=';
815         (*target)->buf[(*target)->BufUsed] = '\0';
816         return (*target)->BufUsed;;
817 }
818
819 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
820 {
821         long i;
822         if (buf == NULL)
823                 return;
824         for (i=0; i<buf->BufUsed; i++)
825                 if (buf->buf[i] == search)
826                         buf->buf[i] = replace;
827
828 }