1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: string.c 910 2008-01-14 22:28:38Z hubert@u.washington.edu $";
6 * ========================================================================
7 * Copyright 2006-2008 University of Washington
8 * Copyright 2013-2016 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 /*======================================================================
21 Misc extra and useful string functions
22 - rplstr replace a substring with another string
23 - sqzspaces Squeeze out the extra blanks in a string
24 - sqznewlines Squeeze out \n and \r.
25 - removing_trailing_white_space
26 - short_str Replace part of string with ... for display
27 - removing_leading_white_space
28 Remove leading or trailing white space
29 - removing_double_quotes
30 Remove surrounding double quotes
32 both of above plus convert to lower case
34 return pointer to first non-white-space char
36 return pointer to first white-space char
37 - srchstr Search a string for first occurrence of a sub string
38 - srchrstr Search a string for last occurrence of a sub string
39 - strindex Replacement for strchr/index
40 - strrindex Replacement for strrchr/rindex
41 - sstrncpy Copy one string onto another, advancing dest'n pointer
42 - istrncpy Copy n chars between bufs, making ctrl chars harmless
43 - month_abbrev Return three letter abbreviations for months
44 - month_num Calculate month number from month/year string
45 - cannon_date Formalize format of a some what formatted date
46 - repeat_char Returns a string n chars long
47 - fold Inserts newlines for folding at whitespace.
48 - byte_string Format number of bytes with Kb, Mb, Gb or bytes
49 - enth-string Format number i.e. 1: 1st, 983: 983rd....
50 - string_to_cstring Convert string to C-style constant string with \'s
51 - cstring_to_hexstring Convert cstring to hex string
52 - cstring_to_string Convert C-style string to string
53 - add_backslash_escapes Escape / and \ with \
54 - remove_backslash_escapes Undo the \ escaping, and stop string at /.
58 #include "../pith/headers.h"
59 #include "../pith/string.h"
60 #include "../pith/state.h"
61 #include "../pith/conf.h"
62 #include "../pith/escapes.h"
63 #include "../pith/util.h"
66 void char_to_octal_triple(int, char *);
67 char *dollar_escape_dollars(char *);
68 void convert_string_to_utf8(char *, int);
71 /*----------------------------------------------------------------------
72 Replace dl characters in one string with another given string
74 args: os -- pointer into output string
75 oslen -- size of output string starting at os
76 dl -- the number of character to delete starting at os
77 is -- The string to insert
79 Result: returns pointer in originl string to end of string just inserted
82 rplstr(char *os
, size_t oslen
, int dl
, char *is
)
92 /* can't delete more characters than there are */
100 diff
= (x2
- is
) - dl
;
102 if(diff
< 0){ /* String shrinks */
105 for(x2
= is
; *x2
; *x3
++ = *x2
++) /* copy new string in */
108 for(x2
= x3
- diff
; *x2
; *x3
++ = *x2
++) /* shift for delete */
111 *x3
= *x2
; /* the null */
113 else{ /* String grows */
114 /* make room for insert */
116 if(x3
>= os
+ oslen
) /* just protecting ourselves */
119 for(; x3
>= os
+ (x2
- is
); *x3
-- = *x1
--); /* shift*/
123 for(x1
= os
, x2
= is
; *x2
; *x1
++ = *x2
++)
136 /*----------------------------------------------------------------------
138 ----------------------------------------------------------------------*/
140 sqzspaces(char *string
)
144 while((*string
= *p
++) != '\0') /* while something to copy */
145 if(!isspace((unsigned char)*string
)) /* only really copy if non-blank */
151 /*----------------------------------------------------------------------
152 Squeeze out CR's and LF's
153 ----------------------------------------------------------------------*/
155 sqznewlines(char *string
)
159 while((*string
= *p
++) != '\0') /* while something to copy */
160 if(*string
!= '\r' && *string
!= '\n') /* only copy if non-newline */
166 /*----------------------------------------------------------------------
167 Remove leading white space from a string in place
169 Args: string -- string to remove space from
172 removing_leading_white_space(char *string
)
176 if(!string
|| !*string
|| !isspace(*string
))
179 for(p
= string
; *p
; p
++) /* find the first non-blank */
180 if(!isspace((unsigned char) *p
)){
181 while((*string
++ = *p
++) != '\0') /* copy back from there... */
188 /* replace_embedded_tabs_by_space
189 replace any tab by only one space, when we do not want to see them
190 in the from or subject field.
193 replace_tabs_by_space(char *orig
)
197 for(s
= orig
; s
!= NULL
&& *s
!= '\0' ; s
++)
198 if(*s
== '\t') *s
= ' ';
202 /*----------------------------------------------------------------------
203 Remove trailing white space from a string in place
205 Args: string -- string to remove space from
208 removing_trailing_white_space(char *string
)
212 if(!string
|| !*string
)
215 for(; *string
; string
++) /* remember start of whitespace */
216 p
= (!isspace((unsigned char)*string
)) ? NULL
: (!p
) ? string
: p
;
218 if(p
) /* if whitespace, blast it */
224 removing_leading_and_trailing_white_space(char *string
)
226 register char *p
, *q
= NULL
;
228 if(!string
|| !*string
)
231 for(p
= string
; *p
; p
++) /* find the first non-blank */
232 if(!isspace((unsigned char)*p
)){
233 if(p
== string
){ /* don't have to copy in this case */
234 for(; *string
; string
++)
235 q
= (!isspace((unsigned char)*string
)) ? NULL
: (!q
) ? string
: q
;
238 for(; (*string
= *p
++) != '\0'; string
++)
239 q
= (!isspace((unsigned char)*string
)) ? NULL
: (!q
) ? string
: q
;
253 /*----------------------------------------------------------------------
254 Remove one set of double quotes surrounding string in place
255 Returns 1 if quotes were removed
257 Args: string -- string to remove quotes from
260 removing_double_quotes(char *string
)
265 if(string
&& string
[0] == '"' && string
[1] != '\0'){
266 p
= string
+ strlen(string
) - 1;
270 for(p
= string
; *p
; p
++)
280 /*----------------------------------------------------------------------
281 return a pointer to first non-whitespace char in string
283 Args: string -- string to scan
286 skip_white_space(char *string
)
288 while(*string
&& isspace((unsigned char) *string
))
296 /*----------------------------------------------------------------------
297 return a pointer to first whitespace char in string
299 Args: string -- string to scan
302 skip_to_white_space(char *string
)
304 while(*string
&& !isspace((unsigned char) *string
))
312 /*----------------------------------------------------------------------
313 Remove quotes from a string in place
315 Args: string -- string to remove quotes from
316 Rreturns: string passed us, but with quotes gone
319 removing_quotes(char *string
)
323 if(*(p
= q
= string
) == '\"'){
326 if(*q
== '\"' || *q
== '\\')
328 while((*p
++ = *q
++) != '\0');
336 /*---------------------------------------------------
337 Remove leading whitespace, trailing whitespace and convert
340 Args: s, -- The string to clean
342 Result: the cleaned string
345 strclean(char *string
)
347 char *s
= string
, *sc
= NULL
, *p
= NULL
;
349 for(; *s
; s
++){ /* single pass */
350 if(!isspace((unsigned char)*s
)){
351 p
= NULL
; /* not start of blanks */
352 if(!sc
) /* first non-blank? */
353 sc
= string
; /* start copying */
355 else if(!p
) /* it's OK if sc == NULL */
356 p
= sc
; /* start of blanks? */
358 if(sc
) /* if copying, copy */
359 *sc
++ = isupper((unsigned char)(*s
))
360 ? (unsigned char)tolower((unsigned char)(*s
))
361 : (unsigned char)(*s
);
364 if(p
) /* if ending blanks */
365 *p
= '\0'; /* tie off beginning */
366 else if(!sc
) /* never saw a non-blank */
367 *string
= '\0'; /* so tie whole thing off */
374 * Returns a pointer to a short version of the string.
375 * If src is not longer than wid, pointer points to src.
376 * If longer than wid, a version which is wid long is made in
377 * buf and the pointer points there.
379 * Wid refers to UTF-8 screen width, not strlen width.
381 * Args src -- The string to be shortened
382 * buf -- A place to put the short version
383 * wid -- Desired width of shortened string
384 * where -- Where should the dots be in the shortened string. Can be
385 * FrontDots, MidDots, EndDots.
387 * FrontDots ...stuvwxyz
388 * EndDots abcdefgh...
389 * MidDots abcd...wxyz
392 short_str(char *src
, char *buf
, size_t buflen
, int wid
, WhereDots where
)
395 unsigned alen
, first
, second
;
402 else if((alen
= utf8_width(src
)) <= wid
)
408 strncpy(buf
, "....", buflen
);
417 * first == length of preellipsis text
418 * second == length of postellipsis text
420 if(where
== FrontDots
){
424 else if(where
== MidDots
){
426 second
= wid
- 3 - first
;
428 else if(where
== EndDots
){
435 q
+= utf8_to_width(q
, src
, buflen
, first
, &got_width
);
436 if(got_width
!= first
){
440 while(got_width
< first
&& buflen
-(q
-buf
) > 0)
445 if(buflen
- (q
-buf
) > 3){
446 strncpy(q
, "...", buflen
- (q
-buf
));
447 buf
[buflen
-1] = '\0';
454 p
= utf8_count_back_width(src
, src
+strlen(src
), second
, &got_width
);
455 if(buflen
- (q
-buf
) > strlen(p
)){
456 strncpy(q
, p
, buflen
- (q
-buf
));
457 buf
[buflen
-1] = '\0';
462 if(buflen
- (q
-buf
) > 0)
465 buf
[buflen
-1] = '\0';
474 /*----------------------------------------------------------------------
475 Search one string for another
477 Args: haystack -- The string to search in, the larger string
478 needle -- The string to search for, the smaller string
480 Search for first occurrence of needle in the haystack, and return a pointer
481 into the string haystack when it is found. The text we are dealing with is
482 UTF-8. We'd like the search to be case-independent but we're not sure what
483 that means for UTF-8. We're not even sure what matching means. We're not going
484 to worry about composed characters and canonical forms and anything like that
485 for now. Instead, we'll do the case-independent thing for ascii but exact
486 equality for the rest of the character space.
489 srchstr(char *haystack
, char *needle
)
493 #define CMPNOCASE(x, y) (((isascii((unsigned char) (x)) && isupper((unsigned char) (x))) \
494 ? tolower((unsigned char) (x)) \
495 : (unsigned char) (x)) \
496 == ((isascii((unsigned char) (y)) && isupper((unsigned char) (y))) \
497 ? tolower((unsigned char) (y)) \
498 : (unsigned char) (y)))
500 if(needle
&& haystack
)
501 for(; *haystack
; haystack
++)
502 for(p
= needle
, q
= haystack
; ; p
++, q
++){
504 return(haystack
); /* winner! */
506 return(NULL
); /* len(needle) > len(haystack)! */
507 else if(*p
!= *q
&& !CMPNOCASE(*p
, *q
))
516 /*----------------------------------------------------------------------
517 Search one string for another, from right
519 Args: is -- The string to search in, the larger string
520 ss -- The string to search for, the smaller string
522 Search for last occurrence of ss in the is, and return a pointer
523 into the string is when it is found. The search is case indepedent.
527 srchrstr(register char *is
, register char *ss
)
529 register char *sx
, *sy
;
534 if(is
== NULL
|| ss
== NULL
)
537 if(strlen(ss
) > sizeof(temp
) - 2)
538 ss_store
= (char *)fs_get(strlen(ss
) + 1);
542 for(sx
= ss
, sy
= ss_store
; *sx
!= '\0' ; sx
++, sy
++)
543 *sy
= isupper((unsigned char)(*sx
))
544 ? (unsigned char)tolower((unsigned char)(*sx
))
545 : (unsigned char)(*sx
);
549 is
= is
+ strlen(is
) - strlen(ss_store
);
551 while(is
>= begin_is
){
552 for(sx
= is
, sy
= ss_store
;
554 || ((isupper((unsigned char)(*sx
))
555 ? (unsigned char)tolower((unsigned char)(*sx
))
556 : (unsigned char)(*sx
)) == (unsigned char)(*sy
))) && *sy
;
569 fs_give((void **)&ss_store
);
576 /*----------------------------------------------------------------------
577 A replacement for strchr or index ...
579 Returns a pointer to the first occurrence of the character
580 'ch' in the specified string or NULL if it doesn't occur
582 ....so we don't have to worry if it's there or not. We bring our own.
583 If we really care about efficiency and think the local one is more
584 efficient the local one can be used, but most of the things that take
585 a long time are in the c-client and not in pine.
588 strindex(char *buffer
, int ch
)
593 while (*buffer
++ != '\0');
599 /* Returns a pointer to the last occurrence of the character
600 * 'ch' in the specified string or NULL if it doesn't occur
603 strrindex(char *buffer
, int ch
)
605 char *address
= NULL
;
610 while (*buffer
++ != '\0');
615 /*----------------------------------------------------------------------
616 copy at most n chars of the UTF-8 source string onto the destination string
617 returning pointer to start of destination and converting any undisplayable
618 characters to harmless character equivalents.
621 iutf8ncpy(char *d
, char *s
, int n
)
629 * BUG: this needs to get improved to actually count the
630 * character "cell" positions. For now, at least don't break
631 * a multi-byte character.
633 for(i
= 0; i
< n
&& (d
[i
] = *s
) != '\0'; s
++, i
++)
634 if((unsigned char)(*s
) < 0x80 && FILTER_THIS(*s
)){
637 d
[++i
] = (*s
== 0x7f) ? '?' : *s
+ '@';
641 break; /* don't fit */
645 /* multi-byte character */
646 if((*s
& 0xE0) == 0xC0){
648 if(((d
[++i
] = *++s
) & 0xC0) != 0x80){
650 break; /* bogus utf-8 */
655 break; /* too long */
658 else if((*s
& 0xF0) == 0xE0){
660 if(!(((d
[++i
] = *++s
) & 0xC0) == 0x80
661 && ((d
[++i
] = *++s
) & 0xC0) == 0x80)){
663 break; /* bogus utf-8 */
668 break; /* won't fit */
671 else if((*s
& 0xF8) == 0xF0){
673 if(!(((d
[++i
] = *++s
) & 0xC0) == 0x80
674 && ((d
[++i
] = *++s
) & 0xC0) == 0x80
675 && ((d
[++i
] = *++s
) & 0xC0) == 0x80)){
677 break; /* bogus utf-8 */
682 break; /* won't fit */
685 else if((*s
& 0xFC) == 0xF8){
687 if(!(((d
[++i
] = *++s
) & 0xC0) == 0x80
688 && ((d
[++i
] = *++s
) & 0xC0) == 0x80
689 && ((d
[++i
] = *++s
) & 0xC0) == 0x80
690 && ((d
[++i
] = *++s
) & 0xC0) == 0x80)){
692 break; /* bogus utf-8 */
697 break; /* won't fit */
700 else if((*s
& 0xFE) == 0xFC){
702 if(!(((d
[++i
] = *++s
) & 0xC0) == 0x80
703 && ((d
[++i
] = *++s
) & 0xC0) == 0x80
704 && ((d
[++i
] = *++s
) & 0xC0) == 0x80
705 && ((d
[++i
] = *++s
) & 0xC0) == 0x80
706 && ((d
[++i
] = *++s
) & 0xC0) == 0x80)){
708 break; /* bogus utf-8 */
713 break; /* won't fit */
718 break; /* don't fit */
726 /*----------------------------------------------------------------------
727 copy at most n chars of the source string onto the destination string
728 returning pointer to start of destination and converting any undisplayable
729 characters to harmless character equivalents.
732 istrncpy(char *d
, char *s
, int n
)
741 if(*s
&& (unsigned char)(*s
) < 0x80 && FILTER_THIS(*s
)
742 && !(*(s
+1) && *s
== ESCAPE
&& match_escapes(s
+1))){
744 c
= (unsigned char) *s
;
745 *d
++ = c
>= 0x80 ? '~' : '^';
749 *d
= (c
== 0x7f) ? '?' : (c
& 0x1f) + '@';
757 while(n
> 0 && *d
++);
764 convert_string_to_utf8(char *buf
, int bufsize
)
767 if(strucmp("UTF-8", ps_global
->display_charmap
) &&
768 (s
= convert_to_utf8(buf
, ps_global
->display_charmap
, 0)) != NULL
){
769 strncpy(buf
, s
, bufsize
);
770 buf
[bufsize
-1] = '\0';
771 fs_give((void **)&s
);
777 char *xdays
[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL
};
780 month_abbrev(int month_num
)
782 static char *xmonths
[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
783 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL
};
784 if(month_num
< 1 || month_num
> 12)
786 return(xmonths
[month_num
- 1]);
790 month_abbrev_locale(int month_num
)
792 #ifndef DISABLE_LOCALE_DATES
793 if(F_OFF(F_DISABLE_INDEX_LOCALE_DATES
, ps_global
)){
794 if(month_num
< 1 || month_num
> 12)
797 static char buf
[120];
800 memset(&tm
, 0, sizeof(tm
));
802 tm
.tm_mon
= month_num
-1;
803 our_strftime(buf
, sizeof(buf
), "%b", &tm
);
804 convert_string_to_utf8(buf
, sizeof(buf
));
807 * If it is all digits, then use the English
808 * words instead. Look for
810 * "<digit><digit>" or
813 if((buf
[0] && !(buf
[0] & 0x80)
814 && isdigit((unsigned char)buf
[0]) && !buf
[1])
816 (buf
[0] && !(buf
[0] & 0x80)
817 && (isdigit((unsigned char)buf
[0]) || buf
[0] == ' ')
818 && buf
[1] && !(buf
[1] & 0x80)
819 && isdigit((unsigned char)buf
[1]) && !buf
[2]))
820 return(month_abbrev(month_num
));
823 * If buf[0] is a digit then assume that there should be a leading
824 * space if it leads off with a single digit.
826 if(buf
[0] && !(buf
[0] & 0x80) && isdigit((unsigned char) buf
[0])
827 && !(buf
[1] && !(buf
[1] & 0x80) && isdigit((unsigned char) buf
[1]))){
830 /* insert space at start of buf */
831 p
= buf
+strlen(buf
) + 1;
832 if(p
> buf
+ sizeof(buf
) - 1)
833 p
= buf
+ sizeof(buf
) - 1;
845 return(month_abbrev(month_num
));
846 #else /* DISABLE_LOCALE_DATES */
847 return(month_abbrev(month_num
));
848 #endif /* DISABLE_LOCALE_DATES */
852 month_name(int month_num
)
854 static char *months
[] = {"January", "February", "March", "April",
855 "May", "June", "July", "August", "September", "October",
856 "November", "December", NULL
};
857 if(month_num
< 1 || month_num
> 12)
859 return(months
[month_num
- 1]);
863 month_name_locale(int month_num
)
865 #ifndef DISABLE_LOCALE_DATES
866 if(F_OFF(F_DISABLE_INDEX_LOCALE_DATES
, ps_global
)){
867 if(month_num
< 1 || month_num
> 12)
870 static char buf
[120];
873 memset(&tm
, 0, sizeof(tm
));
875 tm
.tm_mon
= month_num
-1;
876 our_strftime(buf
, sizeof(buf
), "%B", &tm
);
877 convert_string_to_utf8(buf
, sizeof(buf
));
882 return(month_name(month_num
));
883 #else /* DISABLE_LOCALE_DATES */
884 return(month_name(month_num
));
885 #endif /* DISABLE_LOCALE_DATES */
890 day_abbrev(int day_of_week
)
892 if(day_of_week
< 0 || day_of_week
> 6)
894 return(xdays
[day_of_week
]);
898 day_abbrev_locale(int day_of_week
)
900 #ifndef DISABLE_LOCALE_DATES
901 if(F_OFF(F_DISABLE_INDEX_LOCALE_DATES
, ps_global
)){
902 if(day_of_week
< 0 || day_of_week
> 6)
905 static char buf
[120];
908 memset(&tm
, 0, sizeof(tm
));
909 tm
.tm_wday
= day_of_week
;
910 our_strftime(buf
, sizeof(buf
), "%a", &tm
);
911 convert_string_to_utf8(buf
, sizeof(buf
));
916 return(day_abbrev(day_of_week
));
917 #else /* DISABLE_LOCALE_DATES */
918 return(day_abbrev(day_of_week
));
919 #endif /* DISABLE_LOCALE_DATES */
923 day_name(int day_of_week
)
925 static char *days
[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
926 "Thursday", "Friday", "Saturday", NULL
};
927 if(day_of_week
< 0 || day_of_week
> 6)
929 return(days
[day_of_week
]);
933 day_name_locale(int day_of_week
)
935 #ifndef DISABLE_LOCALE_DATES
936 if(F_OFF(F_DISABLE_INDEX_LOCALE_DATES
, ps_global
)){
937 if(day_of_week
< 0 || day_of_week
> 6)
940 static char buf
[120];
943 memset(&tm
, 0, sizeof(tm
));
944 tm
.tm_wday
= day_of_week
;
945 our_strftime(buf
, sizeof(buf
), "%A", &tm
);
946 convert_string_to_utf8(buf
, sizeof(buf
));
951 return(day_name(day_of_week
));
952 #else /* DISABLE_LOCALE_DATES */
953 return(day_name(day_of_week
));
954 #endif /* DISABLE_LOCALE_DATES */
959 our_strftime(char *dst
, size_t dst_size
, char *format
, struct tm
*tm
)
962 LPTSTR lptbuf
, lptformat
;
965 lptbuf
= (LPTSTR
) fs_get(dst_size
* sizeof(TCHAR
));
967 lptformat
= utf8_to_lptstr((LPSTR
) format
);
969 _tcsftime(lptbuf
, dst_size
, lptformat
, tm
);
970 u
= lptstr_to_utf8(lptbuf
);
972 strncpy(dst
, u
, dst_size
);
973 dst
[dst_size
-1] = '\0';
974 fs_give((void **) &u
);
979 return(strftime(dst
, dst_size
, format
, tm
));
984 /*----------------------------------------------------------------------
985 Return month number of month named in string
987 Args: s -- string with 3 letter month abbreviation of form mmm-yyyy
989 Result: Returns month number with January, year 1900, 2000... being 0;
990 -1 if no month/year is matched
995 int month
= -1, year
;
998 if(F_ON(F_PRUNE_USES_ISO
,ps_global
)){
1002 if(s
&& strlen(s
) > 4 && s
[4] == '-'){
1011 for(i
= 0; i
< 12; i
++){
1012 digmon
[0] = ((i
+1) < 10) ? '0' : '1';
1013 digmon
[1] = '0' + (i
+1) % 10;
1015 if(strcmp(digmon
, p
) == 0)
1022 month
= year
* 12 + i
;
1026 if(s
&& strlen(s
) > 3 && s
[3] == '-'){
1027 for(i
= 0; i
< 12; i
++){
1028 if(struncmp(month_abbrev(i
+1), s
, 3) == 0)
1039 month
= year
* 12 + i
;
1048 * Structure containing all knowledge of symbolic time zones.
1049 * To add support for a given time zone, add it here, but make sure
1050 * the zone name is in upper case.
1058 {"PST", 3, -8, 0}, /* Pacific Standard */
1059 {"PDT", 3, -7, 0}, /* Pacific Daylight */
1060 {"MST", 3, -7, 0}, /* Mountain Standard */
1061 {"MDT", 3, -6, 0}, /* Mountain Daylight */
1062 {"CST", 3, -6, 0}, /* Central Standard */
1063 {"CDT", 3, -5, 0}, /* Central Daylight */
1064 {"EST", 3, -5, 0}, /* Eastern Standard */
1065 {"EDT", 3, -4, 0}, /* Eastern Daylight */
1066 {"JST", 3, 9, 0}, /* Japan Standard */
1067 {"GMT", 3, 0, 0}, /* Universal Time */
1068 {"UT", 2, 0, 0}, /* Universal Time */
1069 #ifdef IST_MEANS_ISREAL
1070 {"IST", 3, 2, 0}, /* Israel Standard */
1072 #ifdef IST_MEANS_INDIA
1073 {"IST", 3, 5, 30}, /* India Standard */
1079 /*----------------------------------------------------------------------
1080 Parse date in or near RFC-822 format into the date structure
1082 Args: given_date -- The input string to parse
1083 d -- Pointer to a struct date to place the result in
1087 The following date formats are accepted:
1088 WKDAY DD MM YY HH:MM:SS ZZ
1089 DD MM YY HH:MM:SS ZZ
1090 WKDAY DD MM HH:MM:SS YY ZZ
1091 DD MM HH:MM:SS YY ZZ
1092 DD MM WKDAY HH:MM:SS YY ZZ
1093 DD MM WKDAY YY MM HH:MM:SS ZZ
1095 All leading, intervening and trailing spaces tabs and commas are ignored.
1096 The prefered formats are the first or second ones. If a field is unparsable
1097 it's value is left as -1.
1101 parse_date(char *given_date
, struct date
*d
)
1113 d
->hours_off_gmt
= -1;
1114 d
->min_off_gmt
= -1;
1116 if(given_date
== NULL
)
1120 while(*p
&& isspace((unsigned char)*p
))
1123 /* Start with weekday? */
1124 if((q
=strchr(p
, ',')) != NULL
){
1128 for(i
= xdays
; *i
!= NULL
; i
++)
1129 if(strucmp(p
, *i
) == 0) /* Match first 3 letters */
1135 /* Started with week day */
1136 d
->wkday
= i
- xdays
;
1141 while(*p
&& isspace((unsigned char)*p
))
1144 else if((q
=strchr(p
, ' ')) != NULL
&& q
- p
== 3){
1146 for(i
= xdays
; *i
!= NULL
; i
++)
1147 if(strucmp(p
, *i
) == 0) /* Match first 3 letters */
1153 /* Started with week day */
1154 d
->wkday
= i
- xdays
;
1156 while(*p
&& isspace((unsigned char)*p
))
1161 if(isdigit((unsigned char)*p
)) {
1163 while(*p
&& isdigit((unsigned char)*p
))
1165 while(*p
&& (*p
== '-' || *p
== ',' || isspace((unsigned char)*p
)))
1168 for(month
= 1; month
<= 12; month
++)
1169 if(struncmp(p
, month_abbrev(month
), 3) == 0)
1175 /* Move over month, (or whatever is there) */
1176 while(*p
&& !isspace((unsigned char)*p
) && *p
!= ',' && *p
!= '-')
1178 while(*p
&& (isspace((unsigned char)*p
) || *p
== ',' || *p
== '-'))
1181 /* Check again for day */
1182 if(isdigit((unsigned char)*p
) && d
->day
== -1) {
1184 while(*p
&& isdigit((unsigned char)*p
))
1186 while(*p
&& (*p
== '-' || *p
== ',' || isspace((unsigned char)*p
)))
1190 /*-- Check for time --*/
1191 for(q
= p
; *q
&& isdigit((unsigned char)*q
); q
++);
1193 /* It's the time (out of place) */
1195 while(*p
&& *p
!= ':' && !isspace((unsigned char)*p
))
1199 d
->minute
= atoi(p
);
1200 while(*p
&& *p
!= ':' && !isspace((unsigned char)*p
))
1204 while(*p
&& !isspace((unsigned char)*p
))
1208 while(*p
&& isspace((unsigned char)*p
))
1213 /* Get the year 0-49 is 2000-2049; 50-100 is 1950-1999 and
1214 101-9999 is 101-9999 */
1215 if(isdigit((unsigned char)*p
)) {
1219 else if(d
->year
< 100)
1221 while(*p
&& isdigit((unsigned char)*p
))
1223 while(*p
&& (*p
== '-' || *p
== ',' || isspace((unsigned char)*p
)))
1226 /* Something wierd, skip it and try to resynch */
1227 while(*p
&& !isspace((unsigned char)*p
) && *p
!= ',' && *p
!= '-')
1229 while(*p
&& (isspace((unsigned char)*p
) || *p
== ',' || *p
== '-'))
1233 /*-- Now get hours minutes, seconds and ignore tenths --*/
1234 for(q
= p
; *q
&& isdigit((unsigned char)*q
); q
++);
1235 if(*q
== ':' && d
->hour
== -1) {
1237 while(*p
&& *p
!= ':' && !isspace((unsigned char)*p
))
1241 d
->minute
= atoi(p
);
1242 while(*p
&& *p
!= ':' && !isspace((unsigned char)*p
))
1247 while(*p
&& !isspace((unsigned char)*p
))
1252 while(*p
&& isspace((unsigned char)*p
))
1256 /*-- The time zone --*/
1257 d
->hours_off_gmt
= 0;
1260 if((*p
== '+' || *p
== '-')
1261 && isdigit((unsigned char)p
[1])
1262 && isdigit((unsigned char)p
[2])
1263 && isdigit((unsigned char)p
[3])
1264 && isdigit((unsigned char)p
[4])
1265 && !isdigit((unsigned char)p
[5])) {
1267 d
->min_off_gmt
= d
->hours_off_gmt
= (*p
== '+' ? 1 : -1);
1272 d
->hours_off_gmt
*= atoi(tmp
);
1276 d
->min_off_gmt
*= atoi(tmp
);
1278 for(n
= 0; known_zones
[n
].zone
; n
++)
1279 if(struncmp(p
, known_zones
[n
].zone
, known_zones
[n
].len
) == 0){
1280 d
->hours_off_gmt
= (int) known_zones
[n
].hour_offset
;
1281 d
->min_off_gmt
= (int) known_zones
[n
].min_offset
;
1293 * Not sure why we aren't just using this from the gitgo, but
1294 * since not sure will just use it to repair wkday.
1296 if(mail_parse_date(&elt
, (unsigned char *) given_date
)){
1297 t
= mail_longdate(&elt
);
1301 d
->wkday
= tm
->tm_wday
;
1308 convert_date_to_local(char *date
)
1312 static char datebuf
[26];
1314 ltime
= date_to_local_time_t(date
);
1315 if(ltime
== (time_t) -1)
1318 tm
= localtime(<ime
);
1323 snprintf(datebuf
, sizeof(datebuf
), "%.3s, %d %.3s %d %02d:%02d:%02d",
1324 day_abbrev(tm
->tm_wday
), tm
->tm_mday
, month_abbrev(tm
->tm_mon
+1),
1325 tm
->tm_year
+1900, tm
->tm_hour
, tm
->tm_min
, tm
->tm_sec
);
1332 date_to_local_time_t(char *date
)
1335 struct tm theirtime
;
1337 static int zone
= 1000000; /* initialize timezone offset */
1340 if(zone
== 1000000){
1346 /* find difference between gmtime and localtime, from c-client do_date */
1347 now
= time((time_t *) 0);
1348 if(now
!= (time_t) -1){
1351 zone
= tm
->tm_hour
* 60 + tm
->tm_min
; /* minutes */
1352 julian
= tm
->tm_yday
;
1354 tm
= localtime(&now
);
1355 dst
= tm
->tm_isdst
; /* for converting back to our time */
1357 zone
= tm
->tm_hour
* 60 + tm
->tm_min
- zone
;
1358 if((julian
= tm
->tm_yday
- julian
) != 0)
1359 zone
+= ((julian
< 0) == (abs(julian
) == 1)) ? -24*60 : 24*60;
1361 zone
*= 60; /* change to seconds */
1366 parse_date(date
, &d
);
1368 /* put d into struct tm so we can use mktime */
1369 memset(&theirtime
, 0, sizeof(theirtime
));
1370 theirtime
.tm_year
= d
.year
- 1900;
1371 theirtime
.tm_mon
= d
.month
- 1;
1372 theirtime
.tm_mday
= d
.day
;
1373 theirtime
.tm_hour
= d
.hour
- d
.hours_off_gmt
;
1374 theirtime
.tm_min
= d
.minute
- d
.min_off_gmt
;
1375 theirtime
.tm_sec
= d
.sec
;
1377 theirtime
.tm_isdst
= dst
;
1379 ourtime
= mktime(&theirtime
); /* still theirtime, actually */
1381 /* convert to the time we want to show */
1382 if(ourtime
!= (time_t) -1)
1389 /*----------------------------------------------------------------------
1390 Create a little string of blanks of the specified length.
1391 Max n is MAX_SCREEN_COLS. Can use up to e repeat_char results at once.
1394 repeat_char(int n
, int c
)
1396 static char bb
[3][MAX_SCREEN_COLS
+1];
1397 static int whichbb
= 0;
1400 whichbb
= (whichbb
+ 1) % 3;
1403 if(n
> sizeof(bb
[0]))
1404 n
= sizeof(bb
[0]) - 1;
1406 bb
[whichbb
][n
--] = '\0';
1408 bb
[whichbb
][n
--] = c
;
1410 return(bb
[whichbb
]);
1414 /*----------------------------------------------------------------------
1415 Format number as amount of bytes, appending Kb, Mb, Gb, bytes
1417 Args: bytes -- number of bytes to format
1419 Returns pointer to static string. The numbers are divided to produce a
1420 nice string with precision of about 2-4 digits
1423 byte_string(long int bytes
)
1426 char *abbrevs
= "GMK";
1427 long i
, ones
, tenths
;
1428 static char string
[10];
1434 strncpy(string
, "0 bytes", sizeof(string
));
1435 string
[sizeof(string
)-1] = '\0';
1438 for(a
= abbrevs
, i
= 1000000000; i
>= 1; i
/= 1000, a
++) {
1441 if(ones
< 10L && i
> 10L)
1442 tenths
= (bytes
- (ones
* i
)) / (i
/ 10L);
1447 aa
[0] = *a
; aa
[1] = '\0';
1450 snprintf(string
, sizeof(string
), "%ld%s%s", ones
, aa
, *a
? "B" : "bytes");
1452 snprintf(string
, sizeof(string
), "%ld.%ld%s%s", ones
, tenths
, aa
, *a
? "B" : "bytes");
1460 /*----------------------------------------------------------------------
1461 Print a string corresponding to the number given:
1462 1st, 2nd, 3rd, 105th, 92342nd....
1468 static char enth
[10];
1475 if( (i
% 100 ) == 11)
1476 snprintf(enth
, sizeof(enth
),"%dth", i
);
1478 snprintf(enth
, sizeof(enth
),"%dst", i
);
1482 if ((i
% 100) == 12)
1483 snprintf(enth
, sizeof(enth
), "%dth",i
);
1485 snprintf(enth
, sizeof(enth
), "%dnd",i
);
1489 if(( i
% 100) == 13)
1490 snprintf(enth
, sizeof(enth
), "%dth",i
);
1492 snprintf(enth
, sizeof(enth
), "%drd",i
);
1496 snprintf(enth
, sizeof(enth
),"%dth",i
);
1504 * Inserts newlines for folding at whitespace.
1506 * Args src -- The source text.
1507 * width -- Approximately where the fold should happen.
1508 * maxwidth -- Maximum width we want to fold at.
1509 * first_indent -- String to use as indent on first line.
1510 * indent -- String to use as indent for subsequent folded lines.
1511 * flags -- FLD_CRLF End of line is \r\n instead of \n.
1512 * FLD_PWS PreserveWhiteSpace when folding. This is
1513 * for vcard folding where CRLF SPACE is
1514 * removed when unfolding, so we need to
1515 * leave the space in. With rfc2822 unfolding
1516 * only the CRLF is removed when unfolding.
1518 * Returns An allocated string which caller should free.
1521 fold(char *src
, int width
, int maxwidth
, char *first_indent
, char *indent
, unsigned int flags
)
1523 char *next_piece
, *res
, *p
;
1524 int i
, len
= 0, starting_point
, winner
, eol
, this_width
;
1525 int indent1
= 0, /* width of first_indent */
1526 indent2
= 0, /* width of indent */
1527 nbindent2
= 0, /* number of bytes in indent */
1528 nb
= 0; /* number of bytes needed */
1529 int cr
, preserve_ws
;
1531 char *endptr
= NULL
;
1532 unsigned shorter
, longer
;
1535 cr
= (flags
& FLD_CRLF
);
1536 preserve_ws
= (flags
& FLD_PWS
);
1539 indent2
= (int) utf8_width(indent
);
1540 nbindent2
= strlen(indent
);
1544 indent1
= (int) utf8_width(first_indent
);
1545 nb
= strlen(first_indent
);
1555 * We can't tell how much space is going to be needed without actually
1556 * passing through the data to see.
1558 while(next_piece
&& *next_piece
){
1559 if(next_piece
!= src
&& indent2
){
1564 this_width
= (int) utf8_width(next_piece
);
1565 if(this_width
+ len
<= width
){
1566 nb
+= (strlen(next_piece
) + eol
);
1570 starting_point
= width
- len
; /* space left on this line */
1571 /* find a good folding spot */
1574 winner
== -1 && (starting_point
- i
> 5 || i
< maxwidth
- width
);
1577 if((shorter
=starting_point
-i
) > 5){
1578 endptr
= utf8_count_forw_width(next_piece
, shorter
, &got_width
);
1579 if(endptr
&& got_width
== shorter
&& isspace((unsigned char) *endptr
))
1580 winner
= (int) shorter
;
1584 && (longer
=starting_point
+i
) && i
< maxwidth
- width
){
1585 endptr
= utf8_count_forw_width(next_piece
, longer
, &got_width
);
1586 if(endptr
&& got_width
== longer
&& isspace((unsigned char) *endptr
))
1587 winner
= (int) longer
;
1591 if(winner
== -1 && (flags
& FLD_NEXTSPC
)){
1592 for(i
= starting_point
; winner
== -1 && i
<= strlen(next_piece
) != '\0' && i
< 512; i
++){
1593 endptr
= utf8_count_forw_width(next_piece
, i
, &got_width
);
1594 if(endptr
&& got_width
== i
&& isspace((unsigned char) *endptr
))
1598 winner
= got_width
< 512 ? got_width
: 512;
1603 if(winner
== -1){ /* if no good folding spot, fold at width */
1604 winner
= starting_point
;
1609 endptr
= utf8_count_forw_width(next_piece
, (unsigned) winner
, &got_width
);
1610 winner
= (int) got_width
;
1613 nb
+= ((endptr
- next_piece
) + eol
);
1614 next_piece
= endptr
;
1615 if(!preserve_ws
&& isspace((unsigned char) next_piece
[0]))
1622 res
= (char *) fs_get((nb
+1) * sizeof(char));
1624 sstrncpy(&p
, first_indent
, nb
+1-(p
-res
));
1628 while(next_piece
&& *next_piece
){
1629 if(next_piece
!= src
&& indent2
){
1630 sstrncpy(&p
, indent
, nb
+1-(p
-res
));
1634 this_width
= (int) utf8_width(next_piece
);
1635 if(this_width
+ len
<= width
){
1636 sstrncpy(&p
, next_piece
, nb
+1-(p
-res
));
1637 if(cr
&& p
-res
< nb
+1)
1646 starting_point
= width
- len
; /* space left on this line */
1647 /* find a good folding spot */
1650 winner
== -1 && (starting_point
- i
> 5 || i
< maxwidth
- width
);
1653 if((shorter
=starting_point
-i
) > 5){
1654 endptr
= utf8_count_forw_width(next_piece
, shorter
, &got_width
);
1655 if(endptr
&& got_width
== shorter
&& isspace((unsigned char) *endptr
))
1656 winner
= (int) shorter
;
1660 && (longer
=starting_point
+i
) && i
< maxwidth
- width
){
1661 endptr
= utf8_count_forw_width(next_piece
, longer
, &got_width
);
1662 if(endptr
&& got_width
== longer
&& isspace((unsigned char) *endptr
))
1663 winner
= (int) longer
;
1667 if(winner
== -1 && (flags
& FLD_NEXTSPC
)){
1668 for(i
= starting_point
; winner
== -1 && i
<= strlen(next_piece
) && i
< 512; i
++){
1669 endptr
= utf8_count_forw_width(next_piece
, i
, &got_width
);
1670 if(endptr
&& got_width
== i
&& isspace((unsigned char) *endptr
))
1674 winner
= got_width
< 512 ? got_width
: 512;
1679 if(winner
== -1){ /* if no good folding spot, fold at width */
1680 winner
= starting_point
;
1685 endptr
= utf8_count_forw_width(next_piece
, (unsigned) winner
, &got_width
);
1686 winner
= (int) got_width
;
1690 save_char
= *endptr
;
1692 sstrncpy(&p
, next_piece
, nb
+1-(p
-res
));
1693 *endptr
= save_char
;
1694 next_piece
= endptr
;
1697 if(cr
&& p
-res
< nb
+1)
1703 if(!preserve_ws
&& isspace((unsigned char) next_piece
[0]))
1711 if(cr
&& p
-res
< nb
+1)
1728 * strsquish - fancifies a string into the given buffer if it's too
1729 * long to fit in the given width
1732 strsquish(char *buf
, size_t buflen
, char *src
, int width
)
1735 * Replace strsquish() with calls to short_str().
1738 return(short_str(src
, buf
, buflen
, width
, MidDots
));
1740 return(short_str(src
, buf
, buflen
, width
, FrontDots
));
1745 long2string(long int l
)
1747 static char string
[20];
1749 snprintf(string
, sizeof(string
), "%ld", l
);
1755 ulong2string(unsigned long int l
)
1757 static char string
[20];
1759 snprintf(string
, sizeof(string
), "%lu", l
);
1767 static char string
[20];
1769 snprintf(string
, sizeof(string
), "%d", i
);
1775 * strtoval - convert the given string to a positive integer.
1778 strtoval(char *s
, int *val
, int minmum
, int maxmum
, int otherok
, char *errbuf
,
1779 size_t errbuflen
, char *varname
)
1782 char *p
= s
, *errstr
= NULL
;
1784 removing_leading_and_trailing_white_space(p
);
1786 if(isdigit((unsigned char) *p
)){
1787 i
= (i
* 10) + (*p
- '0');
1789 else if(*p
== '-' && i
== 0){
1793 snprintf(errstr
= errbuf
, errbuflen
,
1794 "Non-numeric value ('%c' in \"%.8s\") in %s. Using \"%d\"",
1795 *p
, s
, varname
, *val
);
1801 /* range describes acceptable values */
1802 if(maxmum
> minmum
&& (i
< minmum
|| i
> maxmum
) && i
!= otherok
)
1803 snprintf(errstr
= errbuf
, errbuflen
,
1804 "%s of %d not supported (M%s %d). Using \"%d\"",
1805 varname
, i
, (i
> maxmum
) ? "ax" : "in",
1806 (i
> maxmum
) ? maxmum
: minmum
, *val
);
1807 /* range describes unacceptable values */
1808 else if(minmum
> maxmum
&& !(i
< maxmum
|| i
> minmum
))
1809 snprintf(errstr
= errbuf
, errbuflen
, "%s of %d not supported. Using \"%d\"",
1819 * strtolval - convert the given string to a positive _long_ integer.
1822 strtolval(char *s
, long int *val
, long int minmum
, long int maxmum
, long int otherok
,
1823 char *errbuf
, size_t errbuflen
, char *varname
)
1825 long i
= 0, neg
= 1L;
1826 char *p
= s
, *errstr
= NULL
;
1828 removing_leading_and_trailing_white_space(p
);
1830 if(isdigit((unsigned char) *p
)){
1831 i
= (i
* 10L) + (*p
- '0');
1833 else if(*p
== '-' && i
== 0L){
1837 snprintf(errstr
= errbuf
, errbuflen
,
1838 "Non-numeric value ('%c' in \"%.8s\") in %s. Using \"%ld\"",
1839 *p
, s
, varname
, *val
);
1845 /* range describes acceptable values */
1846 if(maxmum
> minmum
&& (i
< minmum
|| i
> maxmum
) && i
!= otherok
)
1847 snprintf(errstr
= errbuf
, errbuflen
,
1848 "%s of %ld not supported (M%s %ld). Using \"%ld\"",
1849 varname
, i
, (i
> maxmum
) ? "ax" : "in",
1850 (i
> maxmum
) ? maxmum
: minmum
, *val
);
1851 /* range describes unacceptable values */
1852 else if(minmum
> maxmum
&& !(i
< maxmum
|| i
> minmum
))
1853 snprintf(errstr
= errbuf
, errbuflen
, "%s of %ld not supported. Using \"%ld\"",
1863 * Function to parse the given string into two space-delimited fields
1864 * Quotes may be used to surround labels or values with spaces in them.
1865 * Backslash negates the special meaning of a quote.
1866 * Unescaping of backslashes only happens if the pair member is quoted,
1867 * this provides for backwards compatibility.
1869 * Args -- string -- the source string
1870 * label -- the first half of the string, a return value
1871 * value -- the last half of the string, a return value
1872 * firstws -- if set, the halves are delimited by the first unquoted
1873 * whitespace, else by the last unquoted whitespace
1874 * strip_internal_label_quotes -- unescaped quotes in the middle of the label
1875 * are removed. This is useful for vars
1876 * like display-filters and url-viewers
1877 * which may require quoting of an arg
1878 * inside of a _TOKEN_.
1881 get_pair(char *string
, char **label
, char **value
, int firstws
, int strip_internal_label_quotes
)
1883 char *p
, *q
, *tmp
, *token
= NULL
;
1886 *label
= *value
= NULL
;
1889 * This for loop just finds the beginning of the value. If firstws
1890 * is set, then it begins after the first whitespace. Otherwise, it begins
1891 * after the last whitespace. Quoted whitespace doesn't count as
1892 * whitespace. If there is no unquoted whitespace, then there is no
1893 * label, there's just a value.
1895 for(p
= string
; p
&& *p
;){
1896 if(*p
== '"') /* quoted label? */
1897 quoted
= (quoted
) ? 0 : 1;
1899 if(*p
== '\\' && *(p
+1) == '"') /* escaped quote? */
1900 p
++; /* skip it... */
1902 if(isspace((unsigned char)*p
) && !quoted
){ /* if space, */
1903 while(*++p
&& isspace((unsigned char)*p
)) /* move past it */
1906 if(!firstws
|| !token
)
1907 token
= p
; /* remember start of text */
1913 if(token
){ /* copy label */
1914 *label
= p
= (char *)fs_get(((token
- string
) + 1) * sizeof(char));
1916 /* make a copy of the string */
1917 tmp
= (char *)fs_get(((token
- string
) + 1) * sizeof(char));
1918 strncpy(tmp
, string
, token
- string
);
1919 tmp
[token
-string
] = '\0';
1921 removing_leading_and_trailing_white_space(tmp
);
1922 quoted
= removing_double_quotes(tmp
);
1924 for(q
= tmp
; *q
; q
++){
1925 if(quoted
&& *q
== '\\' && (*(q
+1) == '"' || *(q
+1) == '\\'))
1927 else if(!(strip_internal_label_quotes
&& *q
== '"'))
1931 *p
= '\0'; /* tie off label */
1932 fs_give((void **)&tmp
);
1934 fs_give((void **)label
);
1939 if(token
){ /* copy value */
1940 *value
= p
= (char *)fs_get((strlen(token
) + 1) * sizeof(char));
1942 tmp
= cpystr(token
);
1943 removing_leading_and_trailing_white_space(tmp
);
1944 quoted
= removing_double_quotes(tmp
);
1946 for(q
= tmp
; *q
; q
++){
1947 if(quoted
&& *q
== '\\' && (*(q
+1) == '"' || *(q
+1) == '\\'))
1953 *p
= '\0'; /* tie off value */
1954 fs_give((void **)&tmp
);
1960 * This is sort of the inverse of get_pair.
1962 * Args -- label -- the first half of the string
1963 * value -- the last half of the string
1965 * Returns -- an allocated string which is "label" SPACE "value"
1967 * Label and value are quoted separately. If quoting is needed (they contain
1968 * whitespace) then backslash escaping is done inside the quotes for
1969 * " and for \. If quoting is not needed, no escaping is done.
1972 put_pair(char *label
, char *value
)
1974 char *result
, *lab
= label
, *val
= value
;
1978 lab
= quote_if_needed(label
);
1981 val
= quote_if_needed(value
);
1983 l
= strlen(lab
) + strlen(val
) +1;
1984 result
= (char *) fs_get((l
+1) * sizeof(char));
1986 snprintf(result
, l
+1, "%s%s%s",
1988 (lab
&& lab
[0] && val
&& val
[0]) ? " " : "",
1991 if(lab
&& lab
!= label
)
1992 fs_give((void **)&lab
);
1993 if(val
&& val
!= value
)
1994 fs_give((void **)&val
);
2001 * This is for put_pair type uses. It returns either an allocated
2002 * string which is the quoted src string or it returns a pointer to
2003 * the src string if no quoting is needed.
2006 quote_if_needed(char *src
)
2008 char *result
= src
, *qsrc
= NULL
;
2012 if(strpbrk(src
, " \t") != NULL
)
2013 qsrc
= add_escapes(src
, "\\\"", '\\', "", "");
2016 fs_give((void **)&qsrc
);
2022 result
= (char *) fs_get((l
+1) * sizeof(char));
2023 snprintf(result
, l
+1, "\"%s\"", qsrc
);
2024 fs_give((void **)&qsrc
);
2033 * Convert a 1, 2, or 3-digit octal string into an 8-bit character.
2034 * Only the first three characters of s will be used, and it is ok not
2035 * to null-terminate it.
2038 read_octal(char **s
)
2043 for(j
= 0; j
< 3 && **s
>= '0' && **s
< '8' ; (*s
)++, j
++)
2044 i
= (i
* 8) + (int)(unsigned char)**s
- '0';
2051 * Convert two consecutive HEX digits to an integer. First two
2052 * chars pointed to by "s" MUST already be tested for hexness.
2062 * Given a character c, put the 3-digit ascii octal value of that char
2063 * in the 2nd argument, which must be at least 3 in length.
2066 char_to_octal_triple(int c
, char *octal
)
2070 octal
[2] = (c
% 8) + '0';
2072 octal
[1] = (c
% 8) + '0';
2079 * Convert in memory string s to a C-style string, with backslash escapes
2080 * like they're used in C character constants.
2081 * Also convert leading spaces because read_pinerc deletes those
2084 * Returns allocated C string version of s.
2087 string_to_cstring(char *s
)
2090 int n
, i
, all_space_so_far
= 1;
2096 b
= (char *)fs_get((n
+1) * sizeof(char));
2103 all_space_so_far
= 0;
2107 * The output string may overflow the output buffer.
2111 fs_resize((void **)&b
, (n
+1) * sizeof(char));
2153 if(all_space_so_far
){ /* use octal output */
2155 char_to_octal_triple(*s
, p
);
2166 if(*s
>= SPACE
&& *s
< '~' && *s
!= '\"' && *s
!= '$'){
2170 else{ /* use octal output */
2172 char_to_octal_triple(*s
, p
);
2190 * Convert C-style string, with backslash escapes, into a hex string, two
2191 * hex digits per character.
2193 * Returns allocated hexstring version of s.
2196 cstring_to_hexstring(char *s
)
2205 b
= (char *)fs_get((n
+1) * sizeof(char));
2213 * The output string may overflow the output buffer.
2217 fs_resize((void **)&b
, (n
+1) * sizeof(char));
2301 case 0: /* reached end of s too early */
2315 else if(isxdigit((unsigned char)*s
)){
2350 * Convert C-style string, with backslash escapes, into a regular string.
2351 * Result goes in dst, which should be as big as src.
2355 cstring_to_string(char *src
, char *dst
)
2425 case 0: /* reached end of s too early */
2436 else if(isxdigit((unsigned char)*src
)){
2449 c
= read_octal(&src
);
2463 * Quotes /'s and \'s with \
2465 * Args: src -- The source string.
2467 * Returns: A string with backslash quoting added. Any / in the string is
2468 * replaced with \/ and any \ is replaced with \\, and any
2469 * " is replaced with \".
2471 * The caller is responsible for freeing the memory allocated for the answer.
2474 add_backslash_escapes(char *src
)
2476 return(add_escapes(src
, "/\\\"", '\\', "", ""));
2481 * Undoes backslash quoting of source string.
2483 * Args: src -- The source string.
2485 * Returns: A string with backslash quoting removed or NULL. The string starts
2486 * at src and goes until the end of src or until a / is reached. The
2487 * / is not included in the string. /'s may be quoted by preceding
2488 * them with a backslash (\) and \'s may also be quoted by
2489 * preceding them with a \. In fact, \ quotes any character.
2490 * Not quite, \nnn is octal escape, \xXX is hex escape.
2492 * The caller is responsible for freeing the memory allocated for the answer.
2495 remove_backslash_escapes(char *src
)
2497 char *ans
= NULL
, *q
, *p
;
2501 p
= q
= (char *)fs_get(strlen(src
) + 1);
2508 if(isdigit((unsigned char)*src
))
2509 *p
++ = (char)read_octal(&src
);
2510 else if((*src
== 'x' || *src
== 'X') &&
2511 *(src
+1) && *(src
+2) && isxpair(src
+1)){
2512 *p
++ = (char)read_hex(src
+1);
2535 fs_give((void **)&q
);
2543 * Quote values for viewer-hdr-colors. We quote backslash, comma, and slash.
2544 * Also replaces $ with $$.
2546 * Args: src -- The source string.
2548 * Returns: A string with backslash quoting added.
2550 * The caller is responsible for freeing the memory allocated for the answer.
2553 add_viewerhdr_escapes(char *src
)
2555 char *tmp
, *ans
= NULL
;
2557 tmp
= add_escapes(src
, "/\\", '\\', ",", "");
2560 ans
= dollar_escape_dollars(tmp
);
2561 fs_give((void **) &tmp
);
2569 * Quote dollar sign by preceding it with another dollar sign. We use $$
2570 * instead of \$ so that it will work for both PC-Pine and unix.
2572 * Args: src -- The source string.
2574 * Returns: A string with $$ quoting added.
2576 * The caller is responsible for freeing the memory allocated for the answer.
2579 dollar_escape_dollars(char *src
)
2581 return(add_escapes(src
, "$", '$', "", ""));
2586 * This adds the quoting for vcard backslash quoting.
2587 * That is, commas are backslashed, backslashes are backslashed,
2588 * semicolons are backslashed, and CRLFs are \n'd.
2589 * This is thought to be correct for draft-ietf-asid-mime-vcard-06.txt, Apr 98.
2592 vcard_escape(char *src
)
2596 q
= add_escapes(src
, ";,\\", '\\', "", "");
2598 /* now do CRLF -> \n in place */
2599 for(p
= q
; *p
!= '\0'; p
++)
2600 if(*p
== '\r' && *(p
+1) == '\n'){
2611 * This undoes the vcard backslash quoting.
2613 * In particular, it turns \n into newline, \, into ',', \\ into \, \; -> ;.
2614 * In fact, \<anything_else> is also turned into <anything_else>. The ID
2615 * isn't clear on this.
2617 * The caller is responsible for freeing the memory allocated for the answer.
2620 vcard_unescape(char *src
)
2622 char *ans
= NULL
, *p
;
2626 p
= ans
= (char *)fs_get(strlen(src
) + 1);
2632 if(*src
== 'n' || *src
== 'N'){
2659 * Turn folded lines into long lines in place.
2661 * CRLF whitespace sequences are removed, the space is not preserved.
2664 vcard_unfold(char *string
)
2668 while(*string
) /* while something to copy */
2669 if(*string
== '\r' &&
2670 *(string
+1) == '\n' &&
2671 (*(string
+2) == SPACE
|| *(string
+2) == TAB
))
2681 * Quote specified chars with escape char.
2683 * Args: src -- The source string.
2684 * quote_these_chars -- Array of chars to quote
2685 * quoting_char -- The quoting char to be used (e.g., \)
2686 * hex_these_chars -- Array of chars to hex escape
2687 * hex_these_quoted_chars -- Array of chars to hex escape if they are
2688 * already quoted with quoting_char (that is,
2689 * turn \, into hex comma)
2691 * Returns: An allocated copy of string with quoting added.
2692 * The caller is responsible for freeing the memory allocated for the answer.
2695 add_escapes(char *src
, char *quote_these_chars
, int quoting_char
,
2696 char *hex_these_chars
, char *hex_these_quoted_chars
)
2700 if(!quote_these_chars
)
2701 alpine_panic("bad arg to add_escapes");
2704 char *q
, *p
, *qchar
;
2706 p
= q
= (char *)fs_get(2*strlen(src
) + 1);
2709 if(*src
== quoting_char
)
2710 for(qchar
= hex_these_quoted_chars
; *qchar
!= '\0'; qchar
++)
2711 if(*(src
+1) == *qchar
)
2714 if(*src
== quoting_char
&& *qchar
){
2715 src
++; /* skip quoting_char */
2719 src
++; /* skip quoted char */
2722 for(qchar
= quote_these_chars
; *qchar
!= '\0'; qchar
++)
2726 if(*qchar
){ /* *src is a char to be quoted */
2727 *p
++ = quoting_char
;
2731 for(qchar
= hex_these_chars
; *qchar
!= '\0'; qchar
++)
2735 if(*qchar
){ /* *src is a char to be escaped */
2741 else /* a regular char */
2751 fs_give((void **)&q
);
2759 * Copy a string enclosed in "" without fixing \" or \\. Skip past \"
2760 * but copy it as is, removing only the enclosing quotes.
2763 copy_quoted_string_asis(char *src
)
2766 int done
= 0, quotes
= 0;
2769 p
= q
= (char *)fs_get(strlen(src
) + 1);
2781 case BSLASH
: /* don't count \" as a quote, just copy */
2782 if(*(src
+1) == QUOTE
){
2800 fs_give((void **)&q
);
2821 * isxpair -- return true if the first two chars in string are
2822 * hexidecimal characters
2827 return(isxdigit((unsigned char) *s
) && isxdigit((unsigned char) *(s
+1)));
2835 * * * * * * * something to help managing lists of strings * * * * * * * *
2840 new_strlist(char *name
)
2842 STRLIST_S
*sp
= (STRLIST_S
*) fs_get(sizeof(STRLIST_S
));
2843 memset(sp
, 0, sizeof(STRLIST_S
));
2845 sp
->name
= cpystr(name
);
2852 copy_strlist(STRLIST_S
*src
)
2854 STRLIST_S
*ret
= NULL
, *sl
, *ss
, *new_sl
;
2858 for(sl
= src
; sl
; sl
= sl
->next
){
2859 new_sl
= (STRLIST_S
*) fs_get(sizeof(*new_sl
));
2860 memset((void *) new_sl
, 0, sizeof(*new_sl
));
2862 new_sl
->name
= cpystr(sl
->name
);
2880 * Add the second list to the end of the first.
2883 combine_strlists(STRLIST_S
**first
, STRLIST_S
*second
)
2892 for(sl
= *first
; sl
->next
; sl
= sl
->next
)
2904 free_strlist(STRLIST_S
**strp
)
2908 free_strlist(&(*strp
)->next
);
2911 fs_give((void **) &(*strp
)->name
);
2913 fs_give((void **) strp
);