2 * ========================================================================
3 * Copyright 2013-2022 Eduardo Chappa
4 * Copyright 2006-2008 University of Washington
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
15 /*======================================================================
17 Misc extra and useful string functions
18 - rplstr replace a substring with another string
19 - sqzspaces Squeeze out the extra blanks in a string
20 - sqznewlines Squeeze out \n and \r.
21 - removing_trailing_white_space
22 - short_str Replace part of string with ... for display
23 - removing_leading_white_space
24 Remove leading or trailing white space
25 - removing_double_quotes
26 Remove surrounding double quotes
28 both of above plus convert to lower case
30 return pointer to first non-white-space char
32 return pointer to first white-space char
33 - srchstr Search a string for first occurrence of a sub string
34 - srchrstr Search a string for last occurrence of a sub string
35 - strindex Replacement for strchr/index
36 - strrindex Replacement for strrchr/rindex
37 - sstrncpy Copy one string onto another, advancing dest'n pointer
38 - istrncpy Copy n chars between bufs, making ctrl chars harmless
39 - month_abbrev Return three letter abbreviations for months
40 - month_num Calculate month number from month/year string
41 - cannon_date Formalize format of a some what formatted date
42 - repeat_char Returns a string n chars long
43 - fold Inserts newlines for folding at whitespace.
44 - byte_string Format number of bytes with Kb, Mb, Gb or bytes
45 - enth-string Format number i.e. 1: 1st, 983: 983rd....
46 - string_to_cstring Convert string to C-style constant string with \'s
47 - cstring_to_hexstring Convert cstring to hex string
48 - cstring_to_string Convert C-style string to string
49 - add_backslash_escapes Escape / and \ with \
50 - remove_backslash_escapes Undo the \ escaping, and stop string at /.
54 #include "../pith/headers.h"
55 #include "../pith/string.h"
56 #include "../pith/state.h"
57 #include "../pith/conf.h"
58 #include "../pith/escapes.h"
59 #include "../pith/util.h"
62 void char_to_octal_triple(int, char *);
63 char *dollar_escape_dollars(char *);
64 void convert_string_to_utf8(char *, int);
67 /*----------------------------------------------------------------------
68 Replace dl characters in one string with another given string
70 args: os -- pointer into output string
71 oslen -- size of output string starting at os
72 dl -- the number of character to delete starting at os
73 is -- The string to insert
75 Result: returns pointer in originl string to end of string just inserted
78 rplstr(char *os
, size_t oslen
, int dl
, char *is
)
88 /* can't delete more characters than there are */
96 diff
= (x2
- is
) - dl
;
98 if(diff
< 0){ /* String shrinks */
101 for(x2
= is
; *x2
; *x3
++ = *x2
++) /* copy new string in */
104 for(x2
= x3
- diff
; *x2
; *x3
++ = *x2
++) /* shift for delete */
107 *x3
= *x2
; /* the null */
109 else{ /* String grows */
110 /* make room for insert */
112 if(x3
>= os
+ oslen
) /* just protecting ourselves */
115 for(; x3
>= os
+ (x2
- is
); *x3
-- = *x1
--); /* shift*/
119 for(x1
= os
, x2
= is
; *x2
; *x1
++ = *x2
++)
132 /*----------------------------------------------------------------------
134 ----------------------------------------------------------------------*/
136 sqzspaces(char *string
)
140 while((*string
= *p
++) != '\0') /* while something to copy */
141 if(!isspace((unsigned char)*string
)) /* only really copy if non-blank */
147 /*----------------------------------------------------------------------
148 Squeeze out CR's and LF's
149 ----------------------------------------------------------------------*/
151 sqznewlines(char *string
)
155 while((*string
= *p
++) != '\0') /* while something to copy */
156 if(*string
!= '\r' && *string
!= '\n') /* only copy if non-newline */
162 /*----------------------------------------------------------------------
163 Remove leading white space from a string in place
165 Args: string -- string to remove space from
168 removing_leading_white_space(char *string
)
172 if(!string
|| !*string
|| !isspace(*string
))
175 for(p
= string
; *p
; p
++) /* find the first non-blank */
176 if(!isspace((unsigned char) *p
)){
177 while((*string
++ = *p
++) != '\0') /* copy back from there... */
184 /* replace_embedded_tabs_by_space
185 replace any tab by only one space, when we do not want to see them
186 in the from or subject field.
189 replace_tabs_by_space(char *orig
)
193 for(s
= orig
; s
!= NULL
&& *s
!= '\0' ; s
++)
194 if(*s
== '\t') *s
= ' ';
198 /*----------------------------------------------------------------------
199 Remove trailing white space from a string in place
201 Args: string -- string to remove space from
204 removing_trailing_white_space(char *string
)
208 if(!string
|| !*string
)
211 for(; *string
; string
++) /* remember start of whitespace */
212 p
= (!isspace((unsigned char)*string
)) ? NULL
: (!p
) ? string
: p
;
214 if(p
) /* if whitespace, blast it */
220 removing_leading_and_trailing_white_space(char *string
)
222 register char *p
, *q
= NULL
;
224 if(!string
|| !*string
)
227 for(p
= string
; *p
; p
++) /* find the first non-blank */
228 if(!isspace((unsigned char)*p
)){
229 if(p
== string
){ /* don't have to copy in this case */
230 for(; *string
; string
++)
231 q
= (!isspace((unsigned char)*string
)) ? NULL
: (!q
) ? string
: q
;
234 for(; (*string
= *p
++) != '\0'; string
++)
235 q
= (!isspace((unsigned char)*string
)) ? NULL
: (!q
) ? string
: q
;
249 /*----------------------------------------------------------------------
250 Remove one set of double quotes surrounding string in place
251 Returns 1 if quotes were removed
253 Args: string -- string to remove quotes from
256 removing_double_quotes(char *string
)
261 if(string
&& string
[0] == '"' && string
[1] != '\0'){
262 p
= string
+ strlen(string
) - 1;
266 for(p
= string
; *p
; p
++)
276 /*----------------------------------------------------------------------
277 return a pointer to first non-whitespace char in string
279 Args: string -- string to scan
282 skip_white_space(char *string
)
284 while(*string
&& isspace((unsigned char) *string
))
292 /*----------------------------------------------------------------------
293 return a pointer to first whitespace char in string
295 Args: string -- string to scan
298 skip_to_white_space(char *string
)
300 while(*string
&& !isspace((unsigned char) *string
))
308 /*----------------------------------------------------------------------
309 Remove quotes from a string in place
311 Args: string -- string to remove quotes from
312 Rreturns: string passed us, but with quotes gone
315 removing_quotes(char *string
)
319 if(*(p
= q
= string
) == '\"'){
322 if(*q
== '\"' || *q
== '\\')
324 while((*p
++ = *q
++) != '\0');
332 /*---------------------------------------------------
333 Remove leading whitespace, trailing whitespace and convert
336 Args: s, -- The string to clean
338 Result: the cleaned string
341 strclean(char *string
)
343 char *s
= string
, *sc
= NULL
, *p
= NULL
;
345 for(; *s
; s
++){ /* single pass */
346 if(!isspace((unsigned char)*s
)){
347 p
= NULL
; /* not start of blanks */
348 if(!sc
) /* first non-blank? */
349 sc
= string
; /* start copying */
351 else if(!p
) /* it's OK if sc == NULL */
352 p
= sc
; /* start of blanks? */
354 if(sc
) /* if copying, copy */
355 *sc
++ = isupper((unsigned char)(*s
))
356 ? (unsigned char)tolower((unsigned char)(*s
))
357 : (unsigned char)(*s
);
360 if(p
) /* if ending blanks */
361 *p
= '\0'; /* tie off beginning */
362 else if(!sc
) /* never saw a non-blank */
363 *string
= '\0'; /* so tie whole thing off */
370 * Returns a pointer to a short version of the string.
371 * If src is not longer than wid, pointer points to src.
372 * If longer than wid, a version which is wid long is made in
373 * buf and the pointer points there.
375 * Wid refers to UTF-8 screen width, not strlen width.
377 * Args src -- The string to be shortened
378 * buf -- A place to put the short version
379 * wid -- Desired width of shortened string
380 * where -- Where should the dots be in the shortened string. Can be
381 * FrontDots, MidDots, EndDots.
383 * FrontDots ...stuvwxyz
384 * EndDots abcdefgh...
385 * MidDots abcd...wxyz
388 short_str(char *src
, char *buf
, size_t buflen
, int wid
, WhereDots where
)
391 unsigned alen
, first
= 0, second
= 0;
398 else if((alen
= utf8_width(src
)) <= wid
)
404 strncpy(buf
, "....", buflen
);
413 * first == length of preellipsis text
414 * second == length of postellipsis text
416 if(where
== FrontDots
){
420 else if(where
== MidDots
){
422 second
= wid
- 3 - first
;
424 else if(where
== EndDots
){
431 q
+= utf8_to_width(q
, src
, buflen
, first
, &got_width
);
432 if(got_width
!= first
){
436 while(got_width
< first
&& buflen
-(q
-buf
) > 0)
441 if(buflen
- (q
-buf
) > 3){
442 strncpy(q
, "...", buflen
- (q
-buf
));
443 buf
[buflen
-1] = '\0';
450 p
= utf8_count_back_width(src
, src
+strlen(src
), second
, &got_width
);
451 if(buflen
- (q
-buf
) > strlen(p
)){
452 strncpy(q
, p
, buflen
- (q
-buf
));
453 buf
[buflen
-1] = '\0';
458 if(buflen
- (q
-buf
) > 0)
461 buf
[buflen
-1] = '\0';
470 /*----------------------------------------------------------------------
471 Search one string for another
473 Args: haystack -- The string to search in, the larger string
474 needle -- The string to search for, the smaller string
476 Search for first occurrence of needle in the haystack, and return a pointer
477 into the string haystack when it is found. The text we are dealing with is
478 UTF-8. We'd like the search to be case-independent but we're not sure what
479 that means for UTF-8. We're not even sure what matching means. We're not going
480 to worry about composed characters and canonical forms and anything like that
481 for now. Instead, we'll do the case-independent thing for ascii but exact
482 equality for the rest of the character space.
485 srchstr(char *haystack
, char *needle
)
489 #define CMPNOCASE(x, y) (((isascii((unsigned char) (x)) && isupper((unsigned char) (x))) \
490 ? tolower((unsigned char) (x)) \
491 : (unsigned char) (x)) \
492 == ((isascii((unsigned char) (y)) && isupper((unsigned char) (y))) \
493 ? tolower((unsigned char) (y)) \
494 : (unsigned char) (y)))
496 if(needle
&& haystack
)
497 for(; *haystack
; haystack
++)
498 for(p
= needle
, q
= haystack
; ; p
++, q
++){
500 return(haystack
); /* winner! */
502 return(NULL
); /* len(needle) > len(haystack)! */
503 else if(*p
!= *q
&& !CMPNOCASE(*p
, *q
))
512 /*----------------------------------------------------------------------
513 Search one string for another, from right
515 Args: is -- The string to search in, the larger string
516 ss -- The string to search for, the smaller string
518 Search for last occurrence of ss in the is, and return a pointer
519 into the string is when it is found. The search is case independent.
523 srchrstr(register char *is
, register char *ss
)
525 register char *sx
, *sy
;
530 if(is
== NULL
|| ss
== NULL
)
533 if(strlen(ss
) > sizeof(temp
) - 2)
534 ss_store
= (char *)fs_get(strlen(ss
) + 1);
538 for(sx
= ss
, sy
= ss_store
; *sx
!= '\0' ; sx
++, sy
++)
539 *sy
= isupper((unsigned char)(*sx
))
540 ? (unsigned char)tolower((unsigned char)(*sx
))
541 : (unsigned char)(*sx
);
545 is
= is
+ strlen(is
) - strlen(ss_store
);
547 while(is
>= begin_is
){
548 for(sx
= is
, sy
= ss_store
;
550 || ((isupper((unsigned char)(*sx
))
551 ? (unsigned char)tolower((unsigned char)(*sx
))
552 : (unsigned char)(*sx
)) == (unsigned char)(*sy
))) && *sy
;
565 fs_give((void **)&ss_store
);
572 /*----------------------------------------------------------------------
573 A replacement for strchr or index ...
575 Returns a pointer to the first occurrence of the character
576 'ch' in the specified string or NULL if it doesn't occur
578 ....so we don't have to worry if it's there or not. We bring our own.
579 If we really care about efficiency and think the local one is more
580 efficient the local one can be used, but most of the things that take
581 a long time are in the c-client and not in pine.
584 strindex(char *buffer
, int ch
)
589 while (*buffer
++ != '\0');
595 /* Returns a pointer to the last occurrence of the character
596 * 'ch' in the specified string or NULL if it doesn't occur
599 strrindex(char *buffer
, int ch
)
601 char *address
= NULL
;
606 while (*buffer
++ != '\0');
611 /*----------------------------------------------------------------------
612 copy at most n chars of the UTF-8 source string onto the destination string
613 returning pointer to start of destination and converting any undisplayable
614 characters to harmless character equivalents.
617 iutf8ncpy(char *d
, char *s
, int n
)
625 * BUG: this needs to get improved to actually count the
626 * character "cell" positions. For now, at least don't break
627 * a multi-byte character.
629 for(i
= 0; i
< n
&& (d
[i
] = *s
) != '\0'; s
++, i
++)
630 if((unsigned char)(*s
) < 0x80 && FILTER_THIS(*s
)){
633 d
[++i
] = (*s
== 0x7f) ? '?' : *s
+ '@';
637 break; /* don't fit */
641 /* multi-byte character */
642 if((*s
& 0xE0) == 0xC0){
644 if(((d
[++i
] = *++s
) & 0xC0) != 0x80){
646 break; /* bogus utf-8 */
651 break; /* too long */
654 else if((*s
& 0xF0) == 0xE0){
656 if(!(((d
[++i
] = *++s
) & 0xC0) == 0x80
657 && ((d
[++i
] = *++s
) & 0xC0) == 0x80)){
659 break; /* bogus utf-8 */
664 break; /* won't fit */
667 else if((*s
& 0xF8) == 0xF0){
669 if(!(((d
[++i
] = *++s
) & 0xC0) == 0x80
670 && ((d
[++i
] = *++s
) & 0xC0) == 0x80
671 && ((d
[++i
] = *++s
) & 0xC0) == 0x80)){
673 break; /* bogus utf-8 */
678 break; /* won't fit */
681 else if((*s
& 0xFC) == 0xF8){
683 if(!(((d
[++i
] = *++s
) & 0xC0) == 0x80
684 && ((d
[++i
] = *++s
) & 0xC0) == 0x80
685 && ((d
[++i
] = *++s
) & 0xC0) == 0x80
686 && ((d
[++i
] = *++s
) & 0xC0) == 0x80)){
688 break; /* bogus utf-8 */
693 break; /* won't fit */
696 else if((*s
& 0xFE) == 0xFC){
698 if(!(((d
[++i
] = *++s
) & 0xC0) == 0x80
699 && ((d
[++i
] = *++s
) & 0xC0) == 0x80
700 && ((d
[++i
] = *++s
) & 0xC0) == 0x80
701 && ((d
[++i
] = *++s
) & 0xC0) == 0x80
702 && ((d
[++i
] = *++s
) & 0xC0) == 0x80)){
704 break; /* bogus utf-8 */
709 break; /* won't fit */
714 break; /* don't fit */
722 /*----------------------------------------------------------------------
723 copy at most n chars of the source string onto the destination string
724 returning pointer to start of destination and converting any undisplayable
725 characters to harmless character equivalents.
728 istrncpy(char *d
, char *s
, int n
)
737 if(*s
&& (unsigned char)(*s
) < 0x80 && FILTER_THIS(*s
)
738 && !(*(s
+1) && *s
== ESCAPE
&& match_escapes(s
+1))){
740 c
= (unsigned char) *s
;
741 *d
++ = c
>= 0x80 ? '~' : '^';
745 *d
= (c
== 0x7f) ? '?' : (c
& 0x1f) + '@';
753 while(n
> 0 && *d
++);
760 convert_string_to_utf8(char *buf
, int bufsize
)
763 if(strucmp("UTF-8", ps_global
->display_charmap
) &&
764 (s
= convert_to_utf8(buf
, ps_global
->display_charmap
, 0)) != NULL
){
765 strncpy(buf
, s
, bufsize
);
766 buf
[bufsize
-1] = '\0';
767 fs_give((void **)&s
);
773 char *xdays
[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL
};
776 month_abbrev(int month_num
)
778 static char *xmonths
[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
779 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL
};
780 if(month_num
< 1 || month_num
> 12)
782 return(xmonths
[month_num
- 1]);
786 month_abbrev_locale(int month_num
)
788 #ifndef DISABLE_LOCALE_DATES
789 if(F_OFF(F_DISABLE_INDEX_LOCALE_DATES
, ps_global
)){
790 if(month_num
< 1 || month_num
> 12)
793 static char buf
[120];
796 memset(&tm
, 0, sizeof(tm
));
798 tm
.tm_mon
= month_num
-1;
799 our_strftime(buf
, sizeof(buf
), "%b", &tm
);
800 convert_string_to_utf8(buf
, sizeof(buf
));
803 * If it is all digits, then use the English
804 * words instead. Look for
806 * "<digit><digit>" or
809 if((buf
[0] && !(buf
[0] & 0x80)
810 && isdigit((unsigned char)buf
[0]) && !buf
[1])
812 (buf
[0] && !(buf
[0] & 0x80)
813 && (isdigit((unsigned char)buf
[0]) || buf
[0] == ' ')
814 && buf
[1] && !(buf
[1] & 0x80)
815 && isdigit((unsigned char)buf
[1]) && !buf
[2]))
816 return(month_abbrev(month_num
));
819 * If buf[0] is a digit then assume that there should be a leading
820 * space if it leads off with a single digit.
822 if(buf
[0] && !(buf
[0] & 0x80) && isdigit((unsigned char) buf
[0])
823 && !(buf
[1] && !(buf
[1] & 0x80) && isdigit((unsigned char) buf
[1]))){
826 /* insert space at start of buf */
827 p
= buf
+strlen(buf
) + 1;
828 if(p
> buf
+ sizeof(buf
) - 1)
829 p
= buf
+ sizeof(buf
) - 1;
841 return(month_abbrev(month_num
));
842 #else /* DISABLE_LOCALE_DATES */
843 return(month_abbrev(month_num
));
844 #endif /* DISABLE_LOCALE_DATES */
848 month_name(int month_num
)
850 static char *months
[] = {"January", "February", "March", "April",
851 "May", "June", "July", "August", "September", "October",
852 "November", "December", NULL
};
853 if(month_num
< 1 || month_num
> 12)
855 return(months
[month_num
- 1]);
859 month_name_locale(int month_num
)
861 #ifndef DISABLE_LOCALE_DATES
862 if(F_OFF(F_DISABLE_INDEX_LOCALE_DATES
, ps_global
)){
863 if(month_num
< 1 || month_num
> 12)
866 static char buf
[120];
869 memset(&tm
, 0, sizeof(tm
));
871 tm
.tm_mon
= month_num
-1;
872 our_strftime(buf
, sizeof(buf
), "%B", &tm
);
873 convert_string_to_utf8(buf
, sizeof(buf
));
878 return(month_name(month_num
));
879 #else /* DISABLE_LOCALE_DATES */
880 return(month_name(month_num
));
881 #endif /* DISABLE_LOCALE_DATES */
886 day_abbrev(int day_of_week
)
888 if(day_of_week
< 0 || day_of_week
> 6)
890 return(xdays
[day_of_week
]);
894 day_abbrev_locale(int day_of_week
)
896 #ifndef DISABLE_LOCALE_DATES
897 if(F_OFF(F_DISABLE_INDEX_LOCALE_DATES
, ps_global
)){
898 if(day_of_week
< 0 || day_of_week
> 6)
901 static char buf
[120];
904 memset(&tm
, 0, sizeof(tm
));
905 tm
.tm_wday
= day_of_week
;
906 our_strftime(buf
, sizeof(buf
), "%a", &tm
);
907 convert_string_to_utf8(buf
, sizeof(buf
));
912 return(day_abbrev(day_of_week
));
913 #else /* DISABLE_LOCALE_DATES */
914 return(day_abbrev(day_of_week
));
915 #endif /* DISABLE_LOCALE_DATES */
919 day_name(int day_of_week
)
921 static char *days
[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
922 "Thursday", "Friday", "Saturday", NULL
};
923 if(day_of_week
< 0 || day_of_week
> 6)
925 return(days
[day_of_week
]);
929 day_name_locale(int day_of_week
)
931 #ifndef DISABLE_LOCALE_DATES
932 if(F_OFF(F_DISABLE_INDEX_LOCALE_DATES
, ps_global
)){
933 if(day_of_week
< 0 || day_of_week
> 6)
936 static char buf
[120];
939 memset(&tm
, 0, sizeof(tm
));
940 tm
.tm_wday
= day_of_week
;
941 our_strftime(buf
, sizeof(buf
), "%A", &tm
);
942 convert_string_to_utf8(buf
, sizeof(buf
));
947 return(day_name(day_of_week
));
948 #else /* DISABLE_LOCALE_DATES */
949 return(day_name(day_of_week
));
950 #endif /* DISABLE_LOCALE_DATES */
955 our_strftime(char *dst
, size_t dst_size
, char *format
, struct tm
*tm
)
958 LPTSTR lptbuf
, lptformat
;
961 lptbuf
= (LPTSTR
) fs_get(dst_size
* sizeof(TCHAR
));
963 lptformat
= utf8_to_lptstr((LPSTR
) format
);
965 _tcsftime(lptbuf
, dst_size
, lptformat
, tm
);
966 u
= lptstr_to_utf8(lptbuf
);
968 strncpy(dst
, u
, dst_size
);
969 dst
[dst_size
-1] = '\0';
970 fs_give((void **) &u
);
975 return(strftime(dst
, dst_size
, format
, tm
));
980 /*----------------------------------------------------------------------
981 Return month number of month named in string
983 Args: s -- string with 3 letter month abbreviation of form mmm-yyyy
985 Result: Returns month number with January, year 1900, 2000... being 0;
986 -1 if no month/year is matched
991 int month
= -1, year
;
994 if(F_ON(F_PRUNE_USES_ISO
,ps_global
)){
998 if(s
&& strlen(s
) > 4 && s
[4] == '-'){
1007 for(i
= 0; i
< 12; i
++){
1008 digmon
[0] = ((i
+1) < 10) ? '0' : '1';
1009 digmon
[1] = '0' + (i
+1) % 10;
1011 if(strcmp(digmon
, p
) == 0)
1018 month
= year
* 12 + i
;
1022 if(s
&& strlen(s
) > 3 && s
[3] == '-'){
1023 for(i
= 0; i
< 12; i
++){
1024 if(struncmp(month_abbrev(i
+1), s
, 3) == 0)
1035 month
= year
* 12 + i
;
1044 * Structure containing all knowledge of symbolic time zones.
1045 * To add support for a given time zone, add it here, but make sure
1046 * the zone name is in upper case.
1054 {"PST", 3, -8, 0}, /* Pacific Standard */
1055 {"PDT", 3, -7, 0}, /* Pacific Daylight */
1056 {"MST", 3, -7, 0}, /* Mountain Standard */
1057 {"MDT", 3, -6, 0}, /* Mountain Daylight */
1058 {"CST", 3, -6, 0}, /* Central Standard */
1059 {"CDT", 3, -5, 0}, /* Central Daylight */
1060 {"EST", 3, -5, 0}, /* Eastern Standard */
1061 {"EDT", 3, -4, 0}, /* Eastern Daylight */
1062 {"GMT", 3, 0, 0}, /* Universal Time */
1063 {"UT", 2, 0, 0}, /* Universal Time */
1064 {"WET", 3, 0, 0}, /* Western Europe Standard */
1065 {"WEST", 4, 1, 0}, /* Western Europe Daylight */
1066 {"CET", 3, 1, 0}, /* Central Europe Standard */
1067 {"CEST", 4, 2, 0}, /* Central Europe Daylight */
1068 {"EET", 3, 2, 0}, /* Eastern Europe Standard */
1069 {"EEST", 4, 3, 0}, /* Eastern Europe Daylight */
1070 {"FET", 3, 3, 0}, /* Further Eastern Europe Standard */
1071 {"MSK", 3, 3, 0}, /* Moscow Standard Time */
1072 {"BST", 3, 1, 0}, /* Brittish Summer Time */
1073 {"JST", 3, 9, 0}, /* Japan Standard */
1074 #ifdef IST_MEANS_ISRAEL
1075 {"IST", 3, 2, 0}, /* Israel Standard */
1077 #ifdef IST_MEANS_INDIA
1078 {"IST", 3, 5, 30}, /* India Standard */
1080 #ifdef IST_MEANS_IRISH
1081 {"IST", 3, 1, 0}, /* Irish Standard */
1088 /*----------------------------------------------------------------------
1089 Parse date in or near RFC-822 format into the date structure
1091 Args: given_date -- The input string to parse
1092 d -- Pointer to a struct date to place the result in
1096 The following date formats are accepted:
1097 WKDAY DD MM YY HH:MM:SS ZZ
1098 DD MM YY HH:MM:SS ZZ
1099 WKDAY DD MM HH:MM:SS YY ZZ
1100 DD MM HH:MM:SS YY ZZ
1101 DD MM WKDAY HH:MM:SS YY ZZ
1102 DD MM WKDAY YY MM HH:MM:SS ZZ
1104 All leading, intervening and trailing spaces tabs and commas are ignored.
1105 The preferred formats are the first or second ones. If a field is unparsable
1106 it's value is left as -1.
1110 parse_date(char *given_date
, struct date
*d
)
1122 d
->hours_off_gmt
= -1;
1123 d
->min_off_gmt
= -1;
1125 if(given_date
== NULL
)
1129 while(*p
&& isspace((unsigned char)*p
))
1132 /* Start with weekday? */
1133 if((q
=strchr(p
, ',')) != NULL
){
1137 for(i
= xdays
; *i
!= NULL
; i
++)
1138 if(strucmp(p
, *i
) == 0) /* Match first 3 letters */
1144 /* Started with week day */
1145 d
->wkday
= i
- xdays
;
1150 while(*p
&& isspace((unsigned char)*p
))
1153 else if((q
=strchr(p
, ' ')) != NULL
&& q
- p
== 3){
1155 for(i
= xdays
; *i
!= NULL
; i
++)
1156 if(strucmp(p
, *i
) == 0) /* Match first 3 letters */
1162 /* Started with week day */
1163 d
->wkday
= i
- xdays
;
1165 while(*p
&& isspace((unsigned char)*p
))
1170 if(isdigit((unsigned char)*p
)) {
1172 while(*p
&& isdigit((unsigned char)*p
))
1174 while(*p
&& (*p
== '-' || *p
== ',' || isspace((unsigned char)*p
)))
1177 for(month
= 1; month
<= 12; month
++)
1178 if(struncmp(p
, month_abbrev(month
), 3) == 0)
1184 /* Move over month, (or whatever is there) */
1185 while(*p
&& !isspace((unsigned char)*p
) && *p
!= ',' && *p
!= '-')
1187 while(*p
&& (isspace((unsigned char)*p
) || *p
== ',' || *p
== '-'))
1190 /* Check again for day */
1191 if(isdigit((unsigned char)*p
) && d
->day
== -1) {
1193 while(*p
&& isdigit((unsigned char)*p
))
1195 while(*p
&& (*p
== '-' || *p
== ',' || isspace((unsigned char)*p
)))
1199 /*-- Check for time --*/
1200 for(q
= p
; *q
&& isdigit((unsigned char)*q
); q
++);
1202 /* It's the time (out of place) */
1204 while(*p
&& *p
!= ':' && !isspace((unsigned char)*p
))
1208 d
->minute
= atoi(p
);
1209 while(*p
&& *p
!= ':' && !isspace((unsigned char)*p
))
1213 while(*p
&& !isspace((unsigned char)*p
))
1217 while(*p
&& isspace((unsigned char)*p
))
1222 /* Get the year 0-49 is 2000-2049; 50-100 is 1950-1999 and
1223 101-9999 is 101-9999 */
1224 if(isdigit((unsigned char)*p
)) {
1228 else if(d
->year
< 100)
1230 while(*p
&& isdigit((unsigned char)*p
))
1232 while(*p
&& (*p
== '-' || *p
== ',' || isspace((unsigned char)*p
)))
1235 /* Something weird, skip it and try to resynch */
1236 while(*p
&& !isspace((unsigned char)*p
) && *p
!= ',' && *p
!= '-')
1238 while(*p
&& (isspace((unsigned char)*p
) || *p
== ',' || *p
== '-'))
1242 /*-- Now get hours minutes, seconds and ignore tenths --*/
1243 for(q
= p
; *q
&& isdigit((unsigned char)*q
); q
++);
1244 if(*q
== ':' && d
->hour
== -1) {
1246 while(*p
&& *p
!= ':' && !isspace((unsigned char)*p
))
1250 d
->minute
= atoi(p
);
1251 while(*p
&& *p
!= ':' && !isspace((unsigned char)*p
))
1256 while(*p
&& !isspace((unsigned char)*p
))
1261 while(*p
&& isspace((unsigned char)*p
))
1265 /*-- The time zone --*/
1266 d
->hours_off_gmt
= 0;
1269 if((*p
== '+' || *p
== '-')
1270 && isdigit((unsigned char)p
[1])
1271 && isdigit((unsigned char)p
[2])
1272 && isdigit((unsigned char)p
[3])
1273 && isdigit((unsigned char)p
[4])
1274 && !isdigit((unsigned char)p
[5])) {
1276 d
->min_off_gmt
= d
->hours_off_gmt
= (*p
== '+' ? 1 : -1);
1281 d
->hours_off_gmt
*= atoi(tmp
);
1285 d
->min_off_gmt
*= atoi(tmp
);
1287 for(n
= 0; known_zones
[n
].zone
; n
++)
1288 if(struncmp(p
, known_zones
[n
].zone
, known_zones
[n
].len
) == 0){
1289 d
->hours_off_gmt
= (int) known_zones
[n
].hour_offset
;
1290 d
->min_off_gmt
= (int) known_zones
[n
].min_offset
;
1302 * Not sure why we aren't just using this from the gitgo, but
1303 * since not sure will just use it to repair wkday.
1305 if(mail_parse_date(&elt
, (unsigned char *) given_date
)){
1306 t
= mail_longdate(&elt
);
1310 d
->wkday
= tm
->tm_wday
;
1317 convert_date_to_local(char *date
)
1321 static char datebuf
[30];
1323 ltime
= date_to_local_time_t(date
);
1324 if(ltime
== (time_t) -1)
1327 tm
= localtime(<ime
);
1332 snprintf(datebuf
, sizeof(datebuf
), "%.3s, %d %.3s %d %02d:%02d:%02d",
1333 day_abbrev(tm
->tm_wday
), tm
->tm_mday
, month_abbrev(tm
->tm_mon
+1),
1334 tm
->tm_year
+1900, tm
->tm_hour
, tm
->tm_min
, tm
->tm_sec
);
1341 timezone_offset_to_gmt(int *dst
)
1348 /* find difference between gmtime and localtime, from c-client do_date */
1349 now
= time((time_t *) 0);
1350 if(now
!= (time_t) -1){
1353 offset
= tm
->tm_hour
* 60 + tm
->tm_min
; /* minutes */
1354 julian
= tm
->tm_yday
;
1356 tm
= localtime(&now
);
1357 *dst
= tm
->tm_isdst
; /* for converting back to our time */
1359 offset
= tm
->tm_hour
* 60 + tm
->tm_min
- offset
;
1360 if((julian
= tm
->tm_yday
- julian
) != 0)
1361 offset
+= ((julian
< 0) == (abs(julian
) == 1)) ? -24*60 : 24*60;
1363 offset
*= 60; /* change to seconds */
1370 date_to_local_time_t(char *date
)
1373 struct tm theirtime
;
1375 static int zone
= 1000000; /* initialize timezone offset */
1379 zone
= timezone_offset_to_gmt(&dst
);
1381 parse_date(date
, &d
);
1383 /* put d into struct tm so we can use mktime */
1384 memset(&theirtime
, 0, sizeof(theirtime
));
1385 theirtime
.tm_year
= d
.year
- 1900;
1386 theirtime
.tm_mon
= d
.month
- 1;
1387 theirtime
.tm_mday
= d
.day
;
1388 theirtime
.tm_hour
= d
.hour
- d
.hours_off_gmt
;
1389 theirtime
.tm_min
= d
.minute
- d
.min_off_gmt
;
1390 theirtime
.tm_sec
= d
.sec
;
1392 theirtime
.tm_isdst
= dst
;
1394 ourtime
= mktime(&theirtime
); /* still theirtime, actually */
1396 /* convert to the time we want to show */
1397 if(ourtime
!= (time_t) -1)
1404 /*----------------------------------------------------------------------
1405 Create a little string of blanks of the specified length.
1406 Max n is MAX_SCREEN_COLS. Can use up to e repeat_char results at once.
1409 repeat_char(int n
, int c
)
1411 static char bb
[3][MAX_SCREEN_COLS
+1];
1412 static int whichbb
= 0;
1415 whichbb
= (whichbb
+ 1) % 3;
1418 if(n
> sizeof(bb
[0]))
1419 n
= sizeof(bb
[0]) - 1;
1421 bb
[whichbb
][n
--] = '\0';
1423 bb
[whichbb
][n
--] = c
;
1425 return(bb
[whichbb
]);
1429 /*----------------------------------------------------------------------
1430 Format number as amount of bytes, appending Kb, Mb, Gb, bytes
1432 Args: bytes -- number of bytes to format
1434 Returns pointer to static string. The numbers are divided to produce a
1435 nice string with precision of about 2-4 digits
1438 byte_string(long int bytes
)
1441 char *abbrevs
= "GMK";
1442 long i
, ones
, tenths
;
1443 static char string
[50];
1449 strncpy(string
, "0 bytes", sizeof(string
));
1450 string
[sizeof(string
)-1] = '\0';
1453 for(a
= abbrevs
, i
= 1000000000; i
>= 1; i
/= 1000, a
++) {
1456 if(ones
< 10L && i
> 10L)
1457 tenths
= (bytes
- (ones
* i
)) / (i
/ 10L);
1462 aa
[0] = *a
; aa
[1] = '\0';
1465 snprintf(string
, sizeof(string
), "%ld%s%s", ones
, aa
, *a
? "B" : "bytes");
1467 snprintf(string
, sizeof(string
), "%ld.%ld%s%s", ones
, tenths
, aa
, *a
? "B" : "bytes");
1475 /*----------------------------------------------------------------------
1476 Print a string corresponding to the number given:
1477 1st, 2nd, 3rd, 105th, 92342nd....
1483 static char enth
[10];
1490 if( (i
% 100 ) == 11)
1491 snprintf(enth
, sizeof(enth
),"%dth", i
);
1493 snprintf(enth
, sizeof(enth
),"%dst", i
);
1497 if ((i
% 100) == 12)
1498 snprintf(enth
, sizeof(enth
), "%dth",i
);
1500 snprintf(enth
, sizeof(enth
), "%dnd",i
);
1504 if(( i
% 100) == 13)
1505 snprintf(enth
, sizeof(enth
), "%dth",i
);
1507 snprintf(enth
, sizeof(enth
), "%drd",i
);
1511 snprintf(enth
, sizeof(enth
),"%dth",i
);
1519 * Inserts newlines for folding at whitespace.
1521 * Args src -- The source text.
1522 * width -- Approximately where the fold should happen.
1523 * maxwidth -- Maximum width we want to fold at.
1524 * first_indent -- String to use as indent on first line.
1525 * indent -- String to use as indent for subsequent folded lines.
1526 * flags -- FLD_CRLF End of line is \r\n instead of \n.
1527 * FLD_PWS PreserveWhiteSpace when folding. This is
1528 * for vcard folding where CRLF SPACE is
1529 * removed when unfolding, so we need to
1530 * leave the space in. With rfc2822 unfolding
1531 * only the CRLF is removed when unfolding.
1533 * Returns An allocated string which caller should free.
1536 fold(char *src
, int width
, int maxwidth
, char *first_indent
, char *indent
, unsigned int flags
)
1538 char *next_piece
, *res
, *p
;
1539 int i
, len
= 0, starting_point
, winner
, eol
, this_width
;
1540 int indent1
= 0, /* width of first_indent */
1541 indent2
= 0, /* width of indent */
1542 nbindent2
= 0, /* number of bytes in indent */
1543 nb
= 0; /* number of bytes needed */
1544 int cr
, preserve_ws
;
1546 char *endptr
= NULL
;
1547 unsigned shorter
, longer
;
1550 cr
= (flags
& FLD_CRLF
);
1551 preserve_ws
= (flags
& FLD_PWS
);
1554 indent2
= (int) utf8_width(indent
);
1555 nbindent2
= strlen(indent
);
1559 indent1
= (int) utf8_width(first_indent
);
1560 nb
= strlen(first_indent
);
1570 * We can't tell how much space is going to be needed without actually
1571 * passing through the data to see.
1573 while(next_piece
&& *next_piece
){
1574 if(next_piece
!= src
&& indent2
){
1579 this_width
= (int) utf8_width(next_piece
);
1580 if(this_width
+ len
<= width
){
1581 nb
+= (strlen(next_piece
) + eol
);
1585 starting_point
= width
- len
; /* space left on this line */
1586 /* find a good folding spot */
1589 winner
== -1 && (starting_point
- i
> 5 || i
< maxwidth
- width
);
1592 if((shorter
=starting_point
-i
) > 5){
1593 endptr
= utf8_count_forw_width(next_piece
, shorter
, &got_width
);
1594 if(endptr
&& got_width
== shorter
&& isspace((unsigned char) *endptr
))
1595 winner
= (int) shorter
;
1599 && (longer
=starting_point
+i
) && i
< maxwidth
- width
){
1600 endptr
= utf8_count_forw_width(next_piece
, longer
, &got_width
);
1601 if(endptr
&& got_width
== longer
&& isspace((unsigned char) *endptr
))
1602 winner
= (int) longer
;
1606 if(winner
== -1 && (flags
& FLD_NEXTSPC
)){
1607 for(i
= starting_point
; winner
== -1 && (i
<= strlen(next_piece
)) != '\0' && i
< 512; i
++){
1608 endptr
= utf8_count_forw_width(next_piece
, i
, &got_width
);
1609 if(endptr
&& got_width
== i
&& isspace((unsigned char) *endptr
))
1613 winner
= got_width
< 512 ? got_width
: 512;
1618 if(winner
== -1){ /* if no good folding spot, fold at width */
1619 winner
= starting_point
;
1624 endptr
= utf8_count_forw_width(next_piece
, (unsigned) winner
, &got_width
);
1625 winner
= (int) got_width
;
1628 nb
+= ((endptr
- next_piece
) + eol
);
1629 next_piece
= endptr
;
1630 if(!preserve_ws
&& isspace((unsigned char) next_piece
[0]))
1637 res
= (char *) fs_get((nb
+1) * sizeof(char));
1639 sstrncpy(&p
, first_indent
, nb
+1-(p
-res
));
1643 while(next_piece
&& *next_piece
){
1644 if(next_piece
!= src
&& indent2
){
1645 sstrncpy(&p
, indent
, nb
+1-(p
-res
));
1649 this_width
= (int) utf8_width(next_piece
);
1650 if(this_width
+ len
<= width
){
1651 sstrncpy(&p
, next_piece
, nb
+1-(p
-res
));
1652 if(cr
&& p
-res
< nb
+1)
1661 starting_point
= width
- len
; /* space left on this line */
1662 /* find a good folding spot */
1665 winner
== -1 && (starting_point
- i
> 5 || i
< maxwidth
- width
);
1668 if((shorter
=starting_point
-i
) > 5){
1669 endptr
= utf8_count_forw_width(next_piece
, shorter
, &got_width
);
1670 if(endptr
&& got_width
== shorter
&& isspace((unsigned char) *endptr
))
1671 winner
= (int) shorter
;
1675 && (longer
=starting_point
+i
) && i
< maxwidth
- width
){
1676 endptr
= utf8_count_forw_width(next_piece
, longer
, &got_width
);
1677 if(endptr
&& got_width
== longer
&& isspace((unsigned char) *endptr
))
1678 winner
= (int) longer
;
1682 if(winner
== -1 && (flags
& FLD_NEXTSPC
)){
1683 for(i
= starting_point
; winner
== -1 && i
<= strlen(next_piece
) && i
< 512; i
++){
1684 endptr
= utf8_count_forw_width(next_piece
, i
, &got_width
);
1685 if(endptr
&& got_width
== i
&& isspace((unsigned char) *endptr
))
1689 winner
= got_width
< 512 ? got_width
: 512;
1694 if(winner
== -1){ /* if no good folding spot, fold at width */
1695 winner
= starting_point
;
1700 endptr
= utf8_count_forw_width(next_piece
, (unsigned) winner
, &got_width
);
1701 winner
= (int) got_width
;
1705 save_char
= *endptr
;
1707 sstrncpy(&p
, next_piece
, nb
+1-(p
-res
));
1708 *endptr
= save_char
;
1709 next_piece
= endptr
;
1712 if(cr
&& p
-res
< nb
+1)
1718 if(!preserve_ws
&& isspace((unsigned char) next_piece
[0]))
1726 if(cr
&& p
-res
< nb
+1)
1743 * strsquish - fancifies a string into the given buffer if it's too
1744 * long to fit in the given width
1747 strsquish(char *buf
, size_t buflen
, char *src
, int width
)
1750 * Replace strsquish() with calls to short_str().
1753 return(short_str(src
, buf
, buflen
, width
, MidDots
));
1755 return(short_str(src
, buf
, buflen
, width
, FrontDots
));
1760 long2string(long int l
)
1762 static char string
[20];
1764 snprintf(string
, sizeof(string
), "%ld", l
);
1770 ulong2string(unsigned long int l
)
1772 static char string
[20];
1774 snprintf(string
, sizeof(string
), "%lu", l
);
1782 static char string
[20];
1784 snprintf(string
, sizeof(string
), "%d", i
);
1790 * strtoval - convert the given string to a positive integer.
1793 strtoval(char *s
, int *val
, int minmum
, int maxmum
, int otherok
, char *errbuf
,
1794 size_t errbuflen
, char *varname
)
1797 char *p
= s
, *errstr
= NULL
;
1799 removing_leading_and_trailing_white_space(p
);
1801 if(isdigit((unsigned char) *p
)){
1802 i
= (i
* 10) + (*p
- '0');
1804 else if(*p
== '-' && i
== 0){
1808 snprintf(errstr
= errbuf
, errbuflen
,
1809 "Non-numeric value ('%c' in \"%.8s\") in %s. Using \"%d\"",
1810 *p
, s
, varname
, *val
);
1816 /* range describes acceptable values */
1817 if(maxmum
> minmum
&& (i
< minmum
|| i
> maxmum
) && i
!= otherok
)
1818 snprintf(errstr
= errbuf
, errbuflen
,
1819 "%s of %d not supported (M%s %d). Using \"%d\"",
1820 varname
, i
, (i
> maxmum
) ? "ax" : "in",
1821 (i
> maxmum
) ? maxmum
: minmum
, *val
);
1822 /* range describes unacceptable values */
1823 else if(minmum
> maxmum
&& !(i
< maxmum
|| i
> minmum
))
1824 snprintf(errstr
= errbuf
, errbuflen
, "%s of %d not supported. Using \"%d\"",
1834 * strtolval - convert the given string to a positive _long_ integer.
1837 strtolval(char *s
, long int *val
, long int minmum
, long int maxmum
, long int otherok
,
1838 char *errbuf
, size_t errbuflen
, char *varname
)
1840 long i
= 0, neg
= 1L;
1841 char *p
= s
, *errstr
= NULL
;
1843 removing_leading_and_trailing_white_space(p
);
1845 if(isdigit((unsigned char) *p
)){
1846 i
= (i
* 10L) + (*p
- '0');
1848 else if(*p
== '-' && i
== 0L){
1852 snprintf(errstr
= errbuf
, errbuflen
,
1853 "Non-numeric value ('%c' in \"%.8s\") in %s. Using \"%ld\"",
1854 *p
, s
, varname
, *val
);
1860 /* range describes acceptable values */
1861 if(maxmum
> minmum
&& (i
< minmum
|| i
> maxmum
) && i
!= otherok
)
1862 snprintf(errstr
= errbuf
, errbuflen
,
1863 "%s of %ld not supported (M%s %ld). Using \"%ld\"",
1864 varname
, i
, (i
> maxmum
) ? "ax" : "in",
1865 (i
> maxmum
) ? maxmum
: minmum
, *val
);
1866 /* range describes unacceptable values */
1867 else if(minmum
> maxmum
&& !(i
< maxmum
|| i
> minmum
))
1868 snprintf(errstr
= errbuf
, errbuflen
, "%s of %ld not supported. Using \"%ld\"",
1878 * Function to parse the given string into two space-delimited fields
1879 * Quotes may be used to surround labels or values with spaces in them.
1880 * Backslash negates the special meaning of a quote.
1881 * Unescaping of backslashes only happens if the pair member is quoted,
1882 * this provides for backwards compatibility.
1884 * Args -- string -- the source string
1885 * label -- the first half of the string, a return value
1886 * value -- the last half of the string, a return value
1887 * firstws -- if set, the halves are delimited by the first unquoted
1888 * whitespace, else by the last unquoted whitespace
1889 * strip_internal_label_quotes -- unescaped quotes in the middle of the label
1890 * are removed. This is useful for vars
1891 * like display-filters and url-viewers
1892 * which may require quoting of an arg
1893 * inside of a _TOKEN_.
1896 get_pair(char *string
, char **label
, char **value
, int firstws
, int strip_internal_label_quotes
)
1898 char *p
, *q
, *tmp
, *token
= NULL
;
1901 *label
= *value
= NULL
;
1904 * This for loop just finds the beginning of the value. If firstws
1905 * is set, then it begins after the first whitespace. Otherwise, it begins
1906 * after the last whitespace. Quoted whitespace doesn't count as
1907 * whitespace. If there is no unquoted whitespace, then there is no
1908 * label, there's just a value.
1910 for(p
= string
; p
&& *p
;){
1911 if(*p
== '"') /* quoted label? */
1912 quoted
= (quoted
) ? 0 : 1;
1914 if(*p
== '\\' && *(p
+1) == '"') /* escaped quote? */
1915 p
++; /* skip it... */
1917 if(isspace((unsigned char)*p
) && !quoted
){ /* if space, */
1918 while(*++p
&& isspace((unsigned char)*p
)) /* move past it */
1921 if(!firstws
|| !token
)
1922 token
= p
; /* remember start of text */
1928 if(token
){ /* copy label */
1929 *label
= p
= (char *)fs_get(((token
- string
) + 1) * sizeof(char));
1931 /* make a copy of the string */
1932 tmp
= (char *)fs_get(((token
- string
) + 1) * sizeof(char));
1933 strncpy(tmp
, string
, token
- string
);
1934 tmp
[token
-string
] = '\0';
1936 removing_leading_and_trailing_white_space(tmp
);
1937 quoted
= removing_double_quotes(tmp
);
1939 for(q
= tmp
; *q
; q
++){
1940 if(quoted
&& *q
== '\\' && (*(q
+1) == '"' || *(q
+1) == '\\'))
1942 else if(!(strip_internal_label_quotes
&& *q
== '"'))
1946 *p
= '\0'; /* tie off label */
1947 fs_give((void **)&tmp
);
1949 fs_give((void **)label
);
1954 if(token
){ /* copy value */
1955 *value
= p
= (char *)fs_get((strlen(token
) + 1) * sizeof(char));
1957 tmp
= cpystr(token
);
1958 removing_leading_and_trailing_white_space(tmp
);
1959 quoted
= removing_double_quotes(tmp
);
1961 for(q
= tmp
; *q
; q
++){
1962 if(quoted
&& *q
== '\\' && (*(q
+1) == '"' || *(q
+1) == '\\'))
1968 *p
= '\0'; /* tie off value */
1969 fs_give((void **)&tmp
);
1975 * This is sort of the inverse of get_pair.
1977 * Args -- label -- the first half of the string
1978 * value -- the last half of the string
1980 * Returns -- an allocated string which is "label" SPACE "value"
1982 * Label and value are quoted separately. If quoting is needed (they contain
1983 * whitespace) then backslash escaping is done inside the quotes for
1984 * " and for \. If quoting is not needed, no escaping is done.
1987 put_pair(char *label
, char *value
)
1989 char *result
, *lab
= label
, *val
= value
;
1993 lab
= quote_if_needed(label
);
1996 val
= quote_if_needed(value
);
1998 l
= strlen(lab
) + strlen(val
) +1;
1999 result
= (char *) fs_get((l
+1) * sizeof(char));
2001 snprintf(result
, l
+1, "%s%s%s",
2003 (lab
&& lab
[0] && val
&& val
[0]) ? " " : "",
2006 if(lab
&& lab
!= label
)
2007 fs_give((void **)&lab
);
2008 if(val
&& val
!= value
)
2009 fs_give((void **)&val
);
2016 * This is for put_pair type uses. It returns either an allocated
2017 * string which is the quoted src string or it returns a pointer to
2018 * the src string if no quoting is needed.
2021 quote_if_needed(char *src
)
2023 char *result
= src
, *qsrc
= NULL
;
2027 if(strpbrk(src
, " \t") != NULL
)
2028 qsrc
= add_escapes(src
, "\\\"", '\\', "", "");
2031 fs_give((void **)&qsrc
);
2037 result
= (char *) fs_get((l
+1) * sizeof(char));
2038 snprintf(result
, l
+1, "\"%s\"", qsrc
);
2039 fs_give((void **)&qsrc
);
2048 * Convert a 1, 2, or 3-digit octal string into an 8-bit character.
2049 * Only the first three characters of s will be used, and it is ok not
2050 * to null-terminate it.
2053 read_octal(char **s
)
2058 for(j
= 0; j
< 3 && **s
>= '0' && **s
< '8' ; (*s
)++, j
++)
2059 i
= (i
* 8) + (int)(unsigned char)**s
- '0';
2066 * Convert two consecutive HEX digits to an integer. First two
2067 * chars pointed to by "s" MUST already be tested for hexness.
2077 * Given a character c, put the 3-digit ascii octal value of that char
2078 * in the 2nd argument, which must be at least 3 in length.
2081 char_to_octal_triple(int c
, char *octal
)
2085 octal
[2] = (c
% 8) + '0';
2087 octal
[1] = (c
% 8) + '0';
2094 * Convert in memory string s to a C-style string, with backslash escapes
2095 * like they're used in C character constants.
2096 * Also convert leading spaces because read_pinerc deletes those
2099 * Returns allocated C string version of s.
2102 string_to_cstring(char *s
)
2105 int n
, i
, all_space_so_far
= 1;
2111 b
= (char *)fs_get((n
+1) * sizeof(char));
2118 all_space_so_far
= 0;
2122 * The output string may overflow the output buffer.
2126 fs_resize((void **)&b
, (n
+1) * sizeof(char));
2168 if(all_space_so_far
){ /* use octal output */
2170 char_to_octal_triple(*s
, p
);
2181 if(*s
>= SPACE
&& *s
< '~' && *s
!= '\"' && *s
!= '$'){
2185 else{ /* use octal output */
2187 char_to_octal_triple(*s
, p
);
2205 * Convert C-style string, with backslash escapes, into a hex string, two
2206 * hex digits per character.
2208 * Returns allocated hexstring version of s.
2211 cstring_to_hexstring(char *s
)
2220 b
= (char *)fs_get((n
+1) * sizeof(char));
2228 * The output string may overflow the output buffer.
2232 fs_resize((void **)&b
, (n
+1) * sizeof(char));
2316 case 0: /* reached end of s too early */
2330 else if(isxdigit((unsigned char)*s
)){
2365 * Convert C-style string, with backslash escapes, into a regular string.
2366 * Result goes in dst, which should be as big as src.
2370 cstring_to_string(char *src
, char *dst
)
2440 case 0: /* reached end of s too early */
2451 else if(isxdigit((unsigned char)*src
)){
2464 c
= read_octal(&src
);
2478 * Quotes /'s and \'s with \
2480 * Args: src -- The source string.
2482 * Returns: A string with backslash quoting added. Any / in the string is
2483 * replaced with \/ and any \ is replaced with \\, and any
2484 * " is replaced with \".
2486 * The caller is responsible for freeing the memory allocated for the answer.
2489 add_backslash_escapes(char *src
)
2491 return(add_escapes(src
, "/\\\"", '\\', "", ""));
2496 * Undoes backslash quoting of source string.
2498 * Args: src -- The source string.
2500 * Returns: A string with backslash quoting removed or NULL. The string starts
2501 * at src and goes until the end of src or until a / is reached. The
2502 * / is not included in the string. /'s may be quoted by preceding
2503 * them with a backslash (\) and \'s may also be quoted by
2504 * preceding them with a \. In fact, \ quotes any character.
2505 * Not quite, \nnn is octal escape, \xXX is hex escape.
2507 * The caller is responsible for freeing the memory allocated for the answer.
2510 remove_backslash_escapes(char *src
)
2512 char *ans
= NULL
, *q
, *p
;
2516 p
= q
= (char *)fs_get(strlen(src
) + 1);
2523 if(isdigit((unsigned char)*src
))
2524 *p
++ = (char)read_octal(&src
);
2525 else if((*src
== 'x' || *src
== 'X') &&
2526 *(src
+1) && *(src
+2) && isxpair(src
+1)){
2527 *p
++ = (char)read_hex(src
+1);
2550 fs_give((void **)&q
);
2558 * Quote values for viewer-hdr-colors. We quote backslash, comma, and slash.
2559 * Also replaces $ with $$.
2561 * Args: src -- The source string.
2563 * Returns: A string with backslash quoting added.
2565 * The caller is responsible for freeing the memory allocated for the answer.
2568 add_viewerhdr_escapes(char *src
)
2570 char *tmp
, *ans
= NULL
;
2572 tmp
= add_escapes(src
, "/\\", '\\', ",", "");
2575 ans
= dollar_escape_dollars(tmp
);
2576 fs_give((void **) &tmp
);
2584 * Quote dollar sign by preceding it with another dollar sign. We use $$
2585 * instead of \$ so that it will work for both PC-Pine and unix.
2587 * Args: src -- The source string.
2589 * Returns: A string with $$ quoting added.
2591 * The caller is responsible for freeing the memory allocated for the answer.
2594 dollar_escape_dollars(char *src
)
2596 return(add_escapes(src
, "$", '$', "", ""));
2601 * This adds the quoting for vcard backslash quoting.
2602 * That is, commas are backslashed, backslashes are backslashed,
2603 * semicolons are backslashed, and CRLFs are \n'd.
2604 * This is thought to be correct for draft-ietf-asid-mime-vcard-06.txt, Apr 98.
2607 vcard_escape(char *src
)
2611 q
= add_escapes(src
, ";,\\", '\\', "", "");
2613 /* now do CRLF -> \n in place */
2614 for(p
= q
; *p
!= '\0'; p
++)
2615 if(*p
== '\r' && *(p
+1) == '\n'){
2626 * This undoes the vcard backslash quoting.
2628 * In particular, it turns \n into newline, \, into ',', \\ into \, \; -> ;.
2629 * In fact, \<anything_else> is also turned into <anything_else>. The ID
2630 * isn't clear on this.
2632 * The caller is responsible for freeing the memory allocated for the answer.
2635 vcard_unescape(char *src
)
2637 char *ans
= NULL
, *p
;
2641 p
= ans
= (char *)fs_get(strlen(src
) + 1);
2647 if(*src
== 'n' || *src
== 'N'){
2674 * Turn folded lines into long lines in place.
2676 * CRLF whitespace sequences are removed, the space is not preserved.
2679 vcard_unfold(char *string
)
2683 while(*string
) /* while something to copy */
2684 if(*string
== '\r' &&
2685 *(string
+1) == '\n' &&
2686 (*(string
+2) == SPACE
|| *(string
+2) == TAB
))
2696 * Quote specified chars with escape char.
2698 * Args: src -- The source string.
2699 * quote_these_chars -- Array of chars to quote
2700 * quoting_char -- The quoting char to be used (e.g., \)
2701 * hex_these_chars -- Array of chars to hex escape
2702 * hex_these_quoted_chars -- Array of chars to hex escape if they are
2703 * already quoted with quoting_char (that is,
2704 * turn \, into hex comma)
2706 * Returns: An allocated copy of string with quoting added.
2707 * The caller is responsible for freeing the memory allocated for the answer.
2710 add_escapes(char *src
, char *quote_these_chars
, int quoting_char
,
2711 char *hex_these_chars
, char *hex_these_quoted_chars
)
2715 if(!quote_these_chars
)
2716 alpine_panic("bad arg to add_escapes");
2719 char *q
, *p
, *qchar
;
2721 p
= q
= (char *)fs_get(2*strlen(src
) + 1);
2724 if(*src
== quoting_char
)
2725 for(qchar
= hex_these_quoted_chars
; *qchar
!= '\0'; qchar
++)
2726 if(*(src
+1) == *qchar
)
2729 if(*src
== quoting_char
&& *qchar
){
2730 src
++; /* skip quoting_char */
2734 src
++; /* skip quoted char */
2737 for(qchar
= quote_these_chars
; *qchar
!= '\0'; qchar
++)
2741 if(*qchar
){ /* *src is a char to be quoted */
2742 *p
++ = quoting_char
;
2746 for(qchar
= hex_these_chars
; *qchar
!= '\0'; qchar
++)
2750 if(*qchar
){ /* *src is a char to be escaped */
2756 else /* a regular char */
2766 fs_give((void **)&q
);
2774 * Copy a string enclosed in "" without fixing \" or \\. Skip past \"
2775 * but copy it as is, removing only the enclosing quotes.
2778 copy_quoted_string_asis(char *src
)
2781 int done
= 0, quotes
= 0;
2784 p
= q
= (char *)fs_get(strlen(src
) + 1);
2796 case BSLASH
: /* don't count \" as a quote, just copy */
2797 if(*(src
+1) == QUOTE
){
2815 fs_give((void **)&q
);
2836 * isxpair -- return true if the first two chars in string are
2837 * hexadecimal characters
2842 return(isxdigit((unsigned char) *s
) && isxdigit((unsigned char) *(s
+1)));
2850 * * * * * * * something to help managing lists of strings * * * * * * * *
2855 new_strlist(char *name
)
2857 return new_strlist_auth(name
, NULL
, '\0');
2861 new_strlist_auth(char *name
, char *authtype
, char sep
)
2863 STRLIST_S
*sp
= (STRLIST_S
*) fs_get(sizeof(STRLIST_S
));
2864 int len
= authtype
? strlen(authtype
) : 0;
2865 int offset
= authtype
? 1 : 0;
2867 memset(sp
, 0, sizeof(STRLIST_S
));
2869 sp
->name
= fs_get(strlen(name
) + len
+ offset
+ 1);
2870 sprintf(sp
->name
, "%s%s%s", authtype
? authtype
: "",
2871 authtype
? " " : "", name
);
2872 if(authtype
!= NULL
) sp
->name
[len
] = sep
;
2879 copy_strlist(STRLIST_S
*src
)
2881 STRLIST_S
*ret
= NULL
, *sl
, *ss
, *new_sl
;
2885 for(sl
= src
; sl
; sl
= sl
->next
){
2886 new_sl
= (STRLIST_S
*) fs_get(sizeof(*new_sl
));
2887 memset((void *) new_sl
, 0, sizeof(*new_sl
));
2889 new_sl
->name
= cpystr(sl
->name
);
2907 * Add the second list to the end of the first.
2910 combine_strlists(STRLIST_S
**first
, STRLIST_S
*second
)
2919 for(sl
= *first
; sl
->next
; sl
= sl
->next
)
2931 free_strlist(STRLIST_S
**strp
)
2935 free_strlist(&(*strp
)->next
);
2938 fs_give((void **) &(*strp
)->name
);
2940 fs_give((void **) strp
);
2945 convert_decimal_to_roman (char *rn
, size_t len
, long n
, char l
)
2952 if(n
>= 4000L || n
<= 0L)
2955 symbols
[0] = l
+ 'm' - 'i';
2956 symbols
[1] = l
+ 'd' - 'i';
2957 symbols
[2] = l
+ 'c' - 'i';
2958 symbols
[3] = l
+ 'l' - 'i';
2959 symbols
[4] = l
+ 'x' - 'i';
2960 symbols
[5] = l
+ 'v' - 'i';
2964 amo
[0] = n
/1000; n
-= amo
[0]*1000;
2965 amo
[1] = n
/500; n
-= amo
[1]*500;
2966 amo
[2] = n
/100; n
-= amo
[2]*100;
2967 amo
[3] = n
/50; n
-= amo
[3]*50;
2968 amo
[4] = n
/10; n
-= amo
[4]*10;
2969 amo
[5] = n
/5; n
-= amo
[5]*5;
2971 amo
[7] = 0; /* make valgrind happy */
2973 for(i
= 0, j
= 0; i
< len
&& j
< strlen(symbols
); j
++){
2976 for(k
= 0; k
< amo
[j
]; k
++)
2977 rn
[i
++] = symbols
[j
];
2981 rn
[i
++] = symbols
[j
];
2982 rn
[i
++] = symbols
[j
-1];
2984 rn
[i
++] = symbols
[j
];
2985 rn
[i
++] = symbols
[j
-2];
2989 if(i
< len
) rn
[i
] = '\0';
2994 convert_decimal_to_alpha (char *rn
, size_t len
, long n
, char l
)
3004 for(i
= 0; i
< sizeof(amo
) && n
> 0; i
++){
3006 n
= (n
- amo
[i
])/26;
3010 for(i
= 0; i
< len
&& amo
[i
] >= 0; i
++)
3011 rn
[i
] = l
+ amo
[i
] - 1;
3012 if(i
< len
) rn
[i
] = '\0';
3017 remove_quotes(unsigned char *name
)
3019 unsigned char *s
, *bos
, *eos
;
3020 int startquote
, endquote
;
3022 startquote
= endquote
= 0;
3024 for(s
= name
; s
&& *s
; s
++){
3025 endquote
= startquote
&& (*s
== '"') ? 1 : 0;
3028 if(startquote
== 0){
3034 if(startquote
&& endquote
){
3035 if(bos
== name
&& eos
[1] == '\0'){
3036 for(s
= name
+ 1; *s
&& s
< eos
; s
++)
3040 startquote
= endquote
= 0;