1 /*
2 * binkleyforce -- unix FTN mailer project
3 *
4 * Copyright (c) 1998-2000 Alexander Belkin, 2:5020/1398.11
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * $Id: u_string.c,v 1.1.1.1 2004/09/09 09:52:39 kstepanenkov Exp $
12 */
13
14 #ifndef TEST
15 #include "includes.h"
16 #include "confread.h"
17 #include "logger.h"
18 #include "util.h"
19 #else
20 #include <stdio.h>
21 #include <stdarg.h>
22 #define TRUE 1
23 #define FALSE 0
24 #define bool int
25 #define xmalloc malloc
26 #define xrealloc realloc
27 #define ASSERT
28 #endif
29
30 /*
31 * Maximum number of quoted-printable strings we will store together
32 */
33 #define PRINTABLE_MAXITEMS 5
34
35 /*
36 * Cyclic buffer for quoted-printable strings
37 */
38 static char *printable_storage[PRINTABLE_MAXITEMS];
39
40 /*
41 * Position of next entry to use in printable_storage[]
42 */
43 static int printable_pos = -1;
44
45
46 /*****************************************************************************
47 * Allocate memory for the copy of a string pointed by src
48 *
49 * Arguments:
50 * src pointer to the null-terminated string
51 *
52 * Return value:
53 * Pointer to the resulting string (must be freed)
54 */
xstrcpy(const char * src)55 char *xstrcpy(const char *src)
56 {
57 char *tmp;
58
59 if( !src )
60 return NULL;
61
62 tmp = xmalloc(strlen(src)+1);
63 strcpy(tmp, src);
64
65 return tmp;
66 }
67
68 /*****************************************************************************
69 * Append string pointed by add to the string src, allocate memory for result
70 *
71 * Arguments:
72 * src pointer to the null-terminated string (will be freed)
73 * add this one will be appended to the src
74 *
75 * Return value:
76 * Pointer to the resulting string (must be freed)
77 */
xstrcat(char * src,const char * add)78 char *xstrcat(char *src, const char *add)
79 {
80 char *tmp;
81 size_t size;
82
83 if( !add || *add == '\0' )
84 return src;
85
86 size = (src ? strlen(src) : 0) + strlen(add);
87 tmp = (char*)xmalloc(size+1);
88
89 if( src )
90 {
91 strcpy(tmp, src);
92 free(src);
93 } else
94 *tmp = '\0';
95
96 strcat(tmp, add);
97
98 return tmp;
99 }
100
101 /*****************************************************************************
102 * Copy a string to the buffer. Checks for the destination buffer overflow and
103 * terminating '\0' character.
104 *
105 * Arguments:
106 * dst pointer to the destination buffer
107 * src string to copy from
108 * len destination buffer size
109 *
110 * Return value:
111 * Pointer to the resulting string
112 */
strnxcpy(char * dst,const char * src,size_t len)113 char *strnxcpy(char *dst, const char *src, size_t len)
114 {
115 ASSERT(src != NULL && dst != NULL && len >= 0);
116
117 dst[len - 1] = 0;
118
119 return strncpy(dst, src, len - 1);
120 }
121
122 /*****************************************************************************
123 * Add one string to the end of another. Checks for destination buffer
124 * overflow and terminating '\0' character.
125 *
126 * Arguments:
127 * dst pointer to the destination buffer (append to the string
128 * stored in it)
129 * src append this string
130 * len destination buffer size
131 *
132 * Return value:
133 * Pointer to the resulting string
134 */
strnxcat(char * dst,const char * src,size_t len)135 char *strnxcat(char *dst, const char *src, size_t len)
136 {
137 int pos;
138
139 ASSERT(src != NULL && dst != NULL && len >= 0);
140
141 pos = strlen(dst);
142
143 return strnxcpy(dst + pos, src, len - pos);
144 }
145
146 /*****************************************************************************
147 * Get next substring from the string.
148 *
149 * Arguments:
150 * str pointer to a null-terminated string
151 * next points to the pointer where you want store address of
152 * the next substring for next calls
153 * delim delimiter characters, if NULL than the isspace() call used
154 * to check for the delimiter characters
155 * quoted ignore spaces in the quoted substrings
156 *
157 * Return value:
158 * Pointer to the new substring or NULL if no more available
159 */
string_token(char * str,char ** next,const char * delim,int quoted)160 char *string_token(char *str, char **next, const char *delim, int quoted)
161 {
162 char *yield;
163 char *p;
164
165 ASSERT(next != NULL);
166
167 yield = str ? str : *next;
168
169 if( yield )
170 {
171 if( delim && *delim )
172 while( *yield && strchr(delim, *yield) ) yield++;
173 else
174 while( isspace(*yield) ) yield++;
175
176 if( *yield )
177 {
178 if( quoted && *yield == '"' )
179 {
180 p = ++yield;
181 while( *p && *p != '"' ) p++;
182 }
183 else
184 {
185 p = yield;
186 if( delim && *delim )
187 while( *p && !strchr(delim, *p) ) p++;
188 else
189 while( *p && !isspace(*p) ) p++;
190 }
191 if( *p )
192 {
193 *p = '\0';
194 *next = p+1;
195 } else
196 *next = NULL;
197 } else
198 yield = *next = NULL;
199 }
200
201 return yield;
202 }
203
204 /*****************************************************************************
205 * Remove trailing CR and LF characters
206 *
207 * Arguments:
208 * str pointer to the null-terminated string
209 *
210 * Return value:
211 * Pointer to the string
212 */
string_chomp(char * str)213 char *string_chomp(char *str)
214 {
215 char *p;
216
217 ASSERT(str != NULL);
218
219 if( str && *str )
220 {
221 p = str + strlen(str + 1);
222 if( *p == '\n' )
223 *p-- = '\0';
224 if( *p == '\r' && p >= str )
225 *p = '\0';
226 }
227
228 return str;
229 }
230
231 /*****************************************************************************
232 * Find the firtst occurance of substring into the string
233 *
234 * Arguments:
235 * substr pointer to the null-terminated substring
236 * string pointer to the null-terminated string
237 *
238 * Return value:
239 * Pointer to the first occurrence of the substring in the string,
240 * and NULL if substring is not found
241 */
string_casestr(const char * string,const char * substr)242 const char *string_casestr(const char *string, const char *substr)
243 {
244 size_t subln = strlen(substr);
245 size_t strln = strlen(string);
246
247 while( *string && strln >= subln )
248 {
249 if( !strncasecmp(substr, string, subln) )
250 return string;
251
252 ++string;
253 --strln;
254 }
255
256 return NULL;
257 }
258
259 /*****************************************************************************
260 * Find character in the string (It is like strchr(), but case insensitive)
261 *
262 * Arguments:
263 * str pointer to the null-terminated string
264 * ch character you want to find
265 *
266 * Return value:
267 * Pointer to the first occurrence of the character in the string,
268 * and NULL if character is not found
269 */
string_casechr(const char * str,int ch)270 const char *string_casechr(const char *str, int ch)
271 {
272 ch = tolower(ch);
273
274 while( *str )
275 {
276 if( tolower(*str) == ch )
277 return str;
278
279 ++str;
280 }
281
282 return NULL;
283 }
284
285 /*****************************************************************************
286 * Convert string to the upper case
287 *
288 * Arguments:
289 * str pointer to the null-terminated string
290 *
291 * Return value:
292 * Pointer to the string
293 */
string_toupper(char * str)294 char *string_toupper(char *str)
295 {
296 char *p;
297
298 ASSERT(str != NULL);
299
300 for( p = str; *p; p++ ) *p = toupper(*p);
301
302 return(str);
303 }
304
305 /*****************************************************************************
306 * Convert string to the lower case
307 *
308 * Arguments:
309 * str pointer to the null-terminated string
310 *
311 * Return value:
312 * Pointer to the string
313 */
string_tolower(char * str)314 char *string_tolower(char *str)
315 {
316 char *p;
317
318 ASSERT(str != NULL);
319
320 for( p = str; *p; p++ ) *p = tolower(*p);
321
322 return(str);
323 }
324
325 /*****************************************************************************
326 * Check wheather string doesn't contain lower case characters
327 *
328 * Arguments:
329 * str pointer to the null-terminated string
330 *
331 * Return value:
332 * Return TRUE if string conforms our requirements, and FALSE if not
333 */
string_isupper(const char * str)334 bool string_isupper(const char *str)
335 {
336 const char *p;
337
338 ASSERT(str != NULL);
339
340 for( p = str; *p; p++ )
341 {
342 if( isalpha(*p) && islower(*p) ) break;
343 }
344
345 return (*p == '\0') ? TRUE : FALSE;
346 }
347
348 /*****************************************************************************
349 * Check wheather string doesn't contain upper case characters
350 *
351 * Arguments:
352 * str pointer to the null-terminated string
353 *
354 * Return value:
355 * Return TRUE if string conforms our requirements, and FALSE if not
356 */
string_islower(const char * str)357 bool string_islower(const char *str)
358 {
359 const char *p;
360
361 ASSERT(str != NULL);
362
363 for( p = str; *p; p++ )
364 {
365 if( isalpha(*p) && isupper(*p) ) break;
366 }
367
368 return (*p == '\0') ? TRUE : FALSE;
369 }
370
371 /*****************************************************************************
372 * Remove spaces at the end of string
373 *
374 * Arguments:
375 * str pointer to the null-terminated string
376 *
377 * Return value:
378 * Pointer to the string
379 */
string_trimright(char * str)380 char *string_trimright(char *str)
381 {
382 char *p;
383
384 ASSERT(str != NULL);
385
386 if( str && *str )
387 {
388 p = str + strlen(str+1);
389 while( p >= str && isspace(*p) ) *p-- = '\0';
390 }
391
392 return str;
393 }
394
395 /*****************************************************************************
396 * Remove spaces at the begining of string
397 *
398 * Arguments:
399 * str pointer to the null-terminated string
400 *
401 * Return value:
402 * pointer to the string
403 */
string_trimleft(char * str)404 char *string_trimleft(char *str)
405 {
406 char *p;
407
408 ASSERT(str != NULL);
409
410 if( str && *str )
411 {
412 p = str;
413 while( isspace(*p) ) p++;
414 if( p > str )
415 memmove(str, p, strlen(p)+1);
416 }
417
418 return str;
419 }
420
421 /*****************************************************************************
422 * Remove white spaces at the begining and at the end of string
423 *
424 * Arguments:
425 * str pointer to the null-terminated string
426 *
427 * Return value:
428 * pointer to the string
429 */
string_trimboth(char * str)430 char *string_trimboth(char *str)
431 {
432 char *p;
433
434 ASSERT(str != NULL);
435
436 if( str && *str )
437 {
438 /* Remove leading spaces */
439 p = str;
440 while( isspace(*p) ) p++;
441 if( p > str )
442 memmove(str, p, strlen(p)+1);
443
444 /* Remove trailing spaces */
445 p = str + strlen(str+1);
446 while( p >= str && isspace(*p) ) *p-- = '\0';
447 }
448
449 return str;
450 }
451
452 /*****************************************************************************
453 * Quote all unprintable characters in the buffer (e.g. "\xff\xfe")
454 *
455 * Arguments:
456 * buffer pointer to the buffer to process
457 * buflen buffer size
458 *
459 * Return value:
460 * Pointer to the quoted-printable string (must be freed)
461 */
string_printable_buffer(const char * buffer,size_t buflen)462 char *string_printable_buffer(const char *buffer, size_t buflen)
463 {
464 char *dest, *p;
465 const char *bp;
466 int nonprintcount = 0;
467 size_t pos = 0;
468
469 for( bp = buffer, pos = 0; pos < buflen; bp++, pos++ )
470 {
471 if( !isprint((unsigned char)*bp) )
472 nonprintcount++;
473 }
474
475 dest = xmalloc(buflen + nonprintcount * 4 + 1);
476
477 if( nonprintcount == 0 )
478 {
479 memcpy(dest, buffer, buflen);
480 dest[buflen] = '\0';
481 }
482 else
483 {
484 p = dest;
485 for( pos = 0; pos < buflen; buffer++, pos++ )
486 {
487 if( !isprint((unsigned char)*buffer) )
488 {
489 sprintf(p, "\\x%02x", (unsigned char)*buffer);
490 p += 4;
491 } else
492 *p++ = *buffer;
493 }
494 *p = '\0';
495 }
496
497 return dest;
498 }
499
500 /*****************************************************************************
501 * Quote all unprintable characters in the string (e.g. "\xff\xfe")
502 *
503 * Arguments:
504 * str pointer to the null-terminated string
505 *
506 * Return value:
507 * Pointer to the quoted-printable string. Memory allocated for the
508 * resulting string will be freed automatically. So don't use more
509 * than last PRINTABLE_MAXITEMS pointers returned by this function.
510 */
string_printable(const char * str)511 const char *string_printable(const char *str)
512 {
513 char *p;
514 const char *q;
515 int nonprintcount = 0;
516 size_t pos = 0;
517 size_t len = 0;
518
519 if( printable_pos == -1 )
520 {
521 memset(printable_storage, '\0', sizeof(printable_storage));
522 printable_pos = 0;
523 }
524
525 if( str == NULL ) return "(null)";
526
527 len = strlen(str);
528
529 for( q = str, pos = 0; pos < len; q++, pos++ )
530 {
531 if( iscntrl((unsigned char)*q) || !isprint((unsigned char)*q) )
532 nonprintcount++;
533 }
534
535 if( !nonprintcount )
536 return str;
537
538 if( printable_pos >= PRINTABLE_MAXITEMS )
539 printable_pos = 0;
540
541 if( printable_storage[printable_pos] )
542 free(printable_storage[printable_pos]);
543
544 p = printable_storage[printable_pos] = xmalloc(len + nonprintcount * 4 + 1);
545
546 for( pos = 0; pos < len; str++, pos++ )
547 {
548 if( iscntrl((unsigned char)*str) || !isprint((unsigned char)*str) )
549 {
550 sprintf(p, "\\x%02x", (unsigned char)*str);
551 p += 4;
552 } else
553 *p++ = *str;
554 }
555 *p = '\0';
556
557 return printable_storage[printable_pos++];
558 }
559
560 /*****************************************************************************
561 * Replace all chracters 'oldchar' by the 'newchar'
562 *
563 * Arguments:
564 * str pointer to the null-terminated string
565 * oldchar old chracter
566 * newchar character to put istead of 'oldchar'
567 *
568 * Return value:
569 * Pointer to the string
570 */
string_replchar(char * str,char oldchar,char newchar)571 char *string_replchar(char *str, char oldchar, char newchar)
572 {
573 char *p = str;
574
575 while( *p )
576 {
577 if( *p == oldchar ) *p = newchar;
578 ++p;
579 }
580
581 return str;
582 }
583
584 /*****************************************************************************
585 * Devide string on substrings and put pointers to them into the
586 * dest[] array.
587 *
588 * Arguments:
589 * dest destination array where we should put substrings
590 * items number of entries in dest[]
591 * str string to parse (will be destroyed)
592 * separator separator character
593 *
594 * Return value:
595 * Number of substrings in the dest[]
596 */
string_parse(char ** dest,int items,char * str,int separator)597 int string_parse(char **dest, int items, char *str, int separator)
598 {
599 int count = 0;
600 char *p = str;
601
602 dest[count++] = str;
603
604 while( *p && count < items )
605 {
606 if( *((unsigned char *)p) == separator )
607 {
608 *p++ = '\0';
609 dest[count++] = p;
610 } else
611 ++p;
612 }
613
614 return count;
615 }
616
617 /*****************************************************************************
618 * Devide string on substrings separated by white-space characters and put
619 * pointers to them into the dest[] array. Also it will ignore spaces in
620 * quoted strings (quote characters will be removed)
621 *
622 * Arguments:
623 * dest destination array where we should put substrings
624 * items number of entries in dest[]
625 * str string to parse (will be destroyed)
626 *
627 * Return value:
628 * Number of substrings in the dest[]
629 */
string_parse_regular(char ** dest,int items,char * str)630 int string_parse_regular(char **dest, int items, char *str)
631 {
632 int count = 0;
633 char *p = str;
634
635 while( *p )
636 {
637 while( *p && isspace(*p) )
638 ++p;
639
640 if( *((unsigned char *)p) == '"' && *++p )
641 {
642 dest[count++] = p;
643 while( *p && *((unsigned char *)p) != '"' ) p++;
644 }
645 else if( *p )
646 {
647 dest[count++] = p;
648 while( *p && !isspace(*p) ) p++;
649 }
650
651 if( *p && count < items )
652 *p++ = '\0';
653 else
654 break;
655 }
656
657 return count;
658 }
659
660 /*****************************************************************************
661 * Replace all substrings 'find' by the 'repl' in the string 'str'
662 *
663 * Arguments:
664 * str null-terminated string to process (will be unchanged)
665 * find substring to find
666 * repl replace substring
667 *
668 * Return value:
669 * Pointer to the new string (must be freed)
670 */
string_translate(const char * str,const char * find,const char * repl)671 char *string_translate(const char *str, const char *find, const char *repl)
672 {
673 size_t sz_find = strlen(find);
674 size_t sz_repl = strlen(repl);
675 size_t sz_dest = strlen(str);
676 char *dest, *p;
677
678 p = dest = xstrcpy(str);
679
680 if( !sz_find ) return dest;
681
682 while( *p )
683 {
684 if( memcmp(p, find, sz_find) == 0 )
685 {
686 size_t offset = p - dest;
687 size_t newsize = sz_dest + (sz_repl - sz_find);
688
689 if( newsize > sz_dest )
690 dest = xrealloc(dest, newsize+1);
691
692 if( sz_repl > sz_find )
693 memmove(dest + offset + (sz_repl - sz_find), dest + offset, sz_dest - offset + 1);
694 else if( sz_repl < sz_find )
695 memmove(dest + offset, dest + offset + (sz_find - sz_repl), sz_dest - offset + 1);
696
697 memcpy(dest + offset, repl, sz_repl);
698
699 sz_dest = newsize;
700 p = dest + offset + sz_repl;
701 } else
702 ++p;
703 }
704
705 return dest;
706 }
707
708 /*****************************************************************************
709 * Convert size into human readable format (e.g. 100,256Kb or 20.7Mb)
710 *
711 * Arguments:
712 * buffer pointer to the buffer for resulting string
713 * size size in bytes that you want to print
714 *
715 * Return value:
716 * Pointer to the string
717 */
string_humansize(char * buffer,size_t size)718 char *string_humansize(char *buffer, size_t size)
719 {
720 if( size < 1024 )
721 sprintf(buffer, "%ldb", (long)size);
722 else if( size < 100*1024 )
723 sprintf(buffer, "%.1fK", ((double)size)/1024.0);
724 else if( size < 1000*1024 )
725 sprintf(buffer, "%ldK", (long)(size/1024));
726 else if( size < 100*1024*1024 )
727 sprintf(buffer, "%.1fM", ((double)size)/(1024.0*1024.0));
728 else if( size < 1000*1024*1024 )
729 sprintf(buffer, "%ldM", (long)(size/(1024*1024)));
730 else
731 sprintf(buffer, "%.1fG", ((double)size)/(1024.0*1024.0*1024.0));
732
733 return string_replchar(buffer, ',', '.');
734 }
735
736 /*****************************************************************************
737 * Interpret escape sequence (\xNN, \NNN, \r, \n, etc.)
738 *
739 * Arguments:
740 * pptr points a pointer to the first character after '\'
741 *
742 * Return value:
743 * Value of the escaped character
744 */
string_get_escape(char ** pptr)745 int string_get_escape(char **pptr)
746 {
747 int ch = 0;
748 static const char *hexdigits = "0123456789abcdef";
749 char *p = *pptr;
750
751 if( *p == 'x' )
752 {
753 const char *q;
754
755 if( (q = string_casechr(hexdigits, *(++p))) )
756 {
757 ch = q - hexdigits;
758 if( (q = string_casechr(hexdigits, *(++p))) )
759 {
760 ch = ch * 16 + (q - hexdigits);
761 }
762 }
763 }
764 else if( *p == '0' || *p == '1' || *p == '2' || *p == '3' )
765 {
766 ch = *(p++) - '0';
767 if( isdigit(*p) && *p != '8' && *p != '9' )
768 {
769 ch = ch * 8 + (*(p++) - '0');
770 if( isdigit(*p) && *p != '8' && *p != '9' )
771 {
772 ch = ch * 8 + (*p - '0');
773 }
774 }
775 }
776 else switch(*p) {
777 case 'a':
778 ch = '\a'; break;
779 case 'b':
780 ch = '\b'; break;
781 case 'f':
782 ch = '\f'; break;
783 case 'n':
784 ch = '\n'; break;
785 case 'r':
786 ch = '\r'; break;
787 case 't':
788 ch = '\t'; break;
789 case '\\':
790 ch = '\\'; break;
791 default:
792 ch = *p;
793 }
794
795 *pptr = p + 1;
796
797 return (ch > 0) ? (ch & 0xff) : -1;
798 }
799
string_dequote(char * dst,char * src)800 int string_dequote(char *dst, char *src)
801 {
802 char *d = dst;
803 char *s = src;
804 int ch;
805
806 while( *s )
807 {
808 if( s[0] == '\\' && s[1] )
809 {
810 ++s;
811 ch = string_get_escape(&s);
812 if( ch != -1 )
813 *d++ = ch;
814 }
815 else
816 *d++ = *s++;
817 }
818
819 *d = '\0';
820
821 return 0;
822 }
823
string_concat(const char * str,...)824 char *string_concat(const char *str, ...)
825 {
826 va_list args;
827 size_t yield_len;
828 char *yield_ptr;
829 char *yield;
830 char *p;
831
832 /*
833 * Calculate total length of yielding string
834 */
835 yield_len = strlen(str);
836 va_start(args, str);
837 while( (p = va_arg(args, char *)) )
838 yield_len += strlen(p);
839 va_end(args);
840
841 yield = xmalloc(yield_len + 1);
842 strncpy(yield, str, yield_len);
843 yield[yield_len] = '\0';
844 yield_ptr = yield + strlen(yield);
845
846 va_start(args, str);
847 while( (p = va_arg(args, char *)) )
848 {
849 strcpy(yield_ptr, p);
850 yield_ptr += strlen(p);
851 }
852 va_end(args);
853
854 return yield;
855 }
856
string_bin_to_hex(char * string,const char * binptr,int binlen)857 void string_bin_to_hex(char *string, const char *binptr, int binlen)
858 {
859 static const char *hexdigits = "0123456789abcdef";
860 int i;
861
862 for( i = 0; i < binlen; i++ )
863 {
864 *string++ = hexdigits[(*binptr >> 4) & 0x0f];
865 *string++ = hexdigits[(*binptr ) & 0x0f];
866 ++binptr;
867 }
868 *string = '\0';
869 }
870
string_hex_to_bin(char * binptr,const char * string)871 int string_hex_to_bin(char *binptr, const char *string)
872 {
873 static const char *hexdigits = "0123456789abcdef";
874 int len = (int)strlen(string);
875 int i, val;
876 const char *p;
877 char *dest = binptr;
878
879 for( i = 0; 2*i < len; i++ )
880 {
881 if( (p = string_casechr(hexdigits, *(string++))) )
882 {
883 val = (int)(p - hexdigits);
884 if( (p = string_casechr(hexdigits, *(string++))) )
885 {
886 val = val * 16 + (int)(p - hexdigits);
887 *dest++ = (unsigned char)(val & 0xff);
888 }
889 else
890 return 0;
891 }
892 else
893 return 0;
894 }
895
896 return (int)(dest - binptr);
897 }
898
string_is_empty(const char * string)899 bool string_is_empty(const char *string)
900 {
901 const char *p;
902
903 for( p = string; *p; p++ )
904 if( !isspace((int)(unsigned char)*p) )
905 return FALSE;
906
907 return TRUE;
908 }
909
910 #ifdef TEST
main(void)911 int main(void)
912 {
913 char *hexstr = "0406ff2354124e6a9b2f6ed6ff00411287";
914 char binbuf[64];
915 int binlen;
916 char newhex[64];
917
918 /*
919 * string_hex_to_bin() and string_bin_to_hex()
920 */
921 printf("*** Checking string_hex_to_bin() and string_bin_to_hex()\n");
922 printf("Original string : \"%s\"\n", hexstr);
923 binlen = string_hex_to_bin(binbuf, hexstr);
924 string_bin_to_hex(newhex, binbuf, binlen);
925 printf("Resulting string : \"%s\"\n", newhex);
926 if( strcmp(hexstr, newhex) )
927 printf("<<< ERROR!!! >>>\n");
928
929 return 0;
930 }
931 #endif /* TEST */
932
933