Add support for tab-completion when selecting by rule
[alpine.git] / pith / string.c
blob2ee82be08f073df4e028c34844d578440e5c198e
1 /*
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 /*======================================================================
16 string.c
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
27 - strclean
28 both of above plus convert to lower case
29 - skip_white_space
30 return pointer to first non-white-space char
31 - skip_to_white_space
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 /.
52 ====*/
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
76 ---*/
77 char *
78 rplstr(char *os, size_t oslen, int dl, char *is)
80 char *x1, *x2, *x3;
81 int diff;
83 if(os == NULL)
84 return(NULL);
86 x1 = os + strlen(os);
88 /* can't delete more characters than there are */
89 if(dl > x1 - os)
90 dl = x1 - os;
92 x2 = is;
93 if(is != NULL)
94 x2 = is + strlen(is);
96 diff = (x2 - is) - dl;
98 if(diff < 0){ /* String shrinks */
99 x3 = os;
100 if(is != NULL)
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 */
111 x3 = x1 + diff;
112 if(x3 >= os + oslen) /* just protecting ourselves */
113 x3 = os + oslen - 1;
115 for(; x3 >= os + (x2 - is); *x3-- = *x1--); /* shift*/
118 if(is != NULL)
119 for(x1 = os, x2 = is; *x2 ; *x1++ = *x2++)
122 while(*x3) x3++;
125 os[oslen-1] = '\0';
127 return(x3);
132 /*----------------------------------------------------------------------
133 Squeeze out blanks
134 ----------------------------------------------------------------------*/
135 void
136 sqzspaces(char *string)
138 char *p = string;
140 while((*string = *p++) != '\0') /* while something to copy */
141 if(!isspace((unsigned char)*string)) /* only really copy if non-blank */
142 string++;
147 /*----------------------------------------------------------------------
148 Squeeze out CR's and LF's
149 ----------------------------------------------------------------------*/
150 void
151 sqznewlines(char *string)
153 char *p = string;
155 while((*string = *p++) != '\0') /* while something to copy */
156 if(*string != '\r' && *string != '\n') /* only copy if non-newline */
157 string++;
162 /*----------------------------------------------------------------------
163 Remove leading white space from a string in place
165 Args: string -- string to remove space from
166 ----*/
167 void
168 removing_leading_white_space(char *string)
170 register char *p;
172 if(!string || !*string || !isspace(*string))
173 return;
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... */
180 return;
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.
188 void
189 replace_tabs_by_space(char *orig)
191 char *s;
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
202 ----*/
203 void
204 removing_trailing_white_space(char *string)
206 char *p = NULL;
208 if(!string || !*string)
209 return;
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 */
215 *p = '\0';
219 void
220 removing_leading_and_trailing_white_space(char *string)
222 register char *p, *q = NULL;
224 if(!string || !*string)
225 return;
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;
233 else{
234 for(; (*string = *p++) != '\0'; string++)
235 q = (!isspace((unsigned char)*string)) ? NULL : (!q) ? string : q;
238 if(q)
239 *q = '\0';
241 return;
244 if(*string != '\0')
245 *string = '\0';
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
254 ----*/
256 removing_double_quotes(char *string)
258 register char *p;
259 int ret = 0;
261 if(string && string[0] == '"' && string[1] != '\0'){
262 p = string + strlen(string) - 1;
263 if(*p == '"'){
264 ret++;
265 *p = '\0';
266 for(p = string; *p; p++)
267 *p = *(p+1);
271 return(ret);
276 /*----------------------------------------------------------------------
277 return a pointer to first non-whitespace char in string
279 Args: string -- string to scan
280 ----*/
281 char *
282 skip_white_space(char *string)
284 while(*string && isspace((unsigned char) *string))
285 string++;
287 return(string);
292 /*----------------------------------------------------------------------
293 return a pointer to first whitespace char in string
295 Args: string -- string to scan
296 ----*/
297 char *
298 skip_to_white_space(char *string)
300 while(*string && !isspace((unsigned char) *string))
301 string++;
303 return(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
313 ----*/
314 char *
315 removing_quotes(char *string)
317 char *p, *q;
319 if(*(p = q = string) == '\"'){
320 q++;
322 if(*q == '\"' || *q == '\\')
323 q++;
324 while((*p++ = *q++) != '\0');
327 return(string);
332 /*---------------------------------------------------
333 Remove leading whitespace, trailing whitespace and convert
334 to lowercase
336 Args: s, -- The string to clean
338 Result: the cleaned string
339 ----*/
340 char *
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 */
365 return(string);
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
387 char *
388 short_str(char *src, char *buf, size_t buflen, int wid, WhereDots where)
390 char *ans;
391 unsigned alen, first = 0, second = 0;
393 if(wid <= 0){
394 ans = buf;
395 if(buflen > 0)
396 buf[0] = '\0';
398 else if((alen = utf8_width(src)) <= wid)
399 ans = src;
400 else{
401 ans = buf;
402 if(wid < 5){
403 if(buflen > wid){
404 strncpy(buf, "....", buflen);
405 buf[wid] = '\0';
408 else{
409 char *q;
410 unsigned got_width;
413 * first == length of preellipsis text
414 * second == length of postellipsis text
416 if(where == FrontDots){
417 first = 0;
418 second = wid - 3;
420 else if(where == MidDots){
421 first = (wid - 3)/2;
422 second = wid - 3 - first;
424 else if(where == EndDots){
425 first = wid - 3;
426 second = 0;
429 q = buf;
430 if(first > 0){
431 q += utf8_to_width(q, src, buflen, first, &got_width);
432 if(got_width != first){
433 if(second)
434 second++;
435 else
436 while(got_width < first && buflen-(q-buf) > 0)
437 *q++ = '.';
441 if(buflen - (q-buf) > 3){
442 strncpy(q, "...", buflen - (q-buf));
443 buf[buflen-1] = '\0';
444 q += strlen(q);
447 if(second > 0){
448 char *p;
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';
454 q += strlen(q);
458 if(buflen - (q-buf) > 0)
459 *q = '\0';
461 buf[buflen-1] = '\0';
465 return(ans);
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.
483 ----*/
484 char *
485 srchstr(char *haystack, char *needle)
487 char *p, *q;
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++){
499 if(!*p)
500 return(haystack); /* winner! */
501 else if(!*q)
502 return(NULL); /* len(needle) > len(haystack)! */
503 else if(*p != *q && !CMPNOCASE(*p, *q))
504 break;
507 return(NULL);
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.
520 ----*/
522 char *
523 srchrstr(register char *is, register char *ss)
525 register char *sx, *sy;
526 char *ss_store, *rv;
527 char *begin_is;
528 char temp[251];
530 if(is == NULL || ss == NULL)
531 return(NULL);
533 if(strlen(ss) > sizeof(temp) - 2)
534 ss_store = (char *)fs_get(strlen(ss) + 1);
535 else
536 ss_store = temp;
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);
542 *sy = *sx;
544 begin_is = is;
545 is = is + strlen(is) - strlen(ss_store);
546 rv = NULL;
547 while(is >= begin_is){
548 for(sx = is, sy = ss_store;
549 ((*sx == *sy)
550 || ((isupper((unsigned char)(*sx))
551 ? (unsigned char)tolower((unsigned char)(*sx))
552 : (unsigned char)(*sx)) == (unsigned char)(*sy))) && *sy;
553 sx++, sy++)
556 if(!*sy){
557 rv = is;
558 break;
561 is--;
564 if(ss_store != temp)
565 fs_give((void **)&ss_store);
567 return(rv);
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.
582 ----*/
583 char *
584 strindex(char *buffer, int ch)
587 if(*buffer == ch)
588 return(buffer);
589 while (*buffer++ != '\0');
591 return(NULL);
595 /* Returns a pointer to the last occurrence of the character
596 * 'ch' in the specified string or NULL if it doesn't occur
598 char *
599 strrindex(char *buffer, int ch)
601 char *address = NULL;
604 if(*buffer == ch)
605 address = buffer;
606 while (*buffer++ != '\0');
607 return(address);
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.
615 ----*/
616 char *
617 iutf8ncpy(char *d, char *s, int n)
619 register int i;
621 if(!d || !s)
622 return(NULL);
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)){
631 if(i+1 < n){
632 d[i] = '^';
633 d[++i] = (*s == 0x7f) ? '?' : *s + '@';
635 else{
636 d[i] = '\0';
637 break; /* don't fit */
640 else if(*s & 0x80){
641 /* multi-byte character */
642 if((*s & 0xE0) == 0xC0){
643 if(i+1 < n){
644 if(((d[++i] = *++s) & 0xC0) != 0x80){
645 d[i] = '\0';
646 break; /* bogus utf-8 */
649 else{
650 d[i] = '\0';
651 break; /* too long */
654 else if((*s & 0xF0) == 0xE0){
655 if(i+2 < n){
656 if(!(((d[++i] = *++s) & 0xC0) == 0x80
657 && ((d[++i] = *++s) & 0xC0) == 0x80)){
658 d[i] = '\0';
659 break; /* bogus utf-8 */
662 else{
663 d[i] = '\0';
664 break; /* won't fit */
667 else if((*s & 0xF8) == 0xF0){
668 if(i+3 < n){
669 if(!(((d[++i] = *++s) & 0xC0) == 0x80
670 && ((d[++i] = *++s) & 0xC0) == 0x80
671 && ((d[++i] = *++s) & 0xC0) == 0x80)){
672 d[i] = '\0';
673 break; /* bogus utf-8 */
676 else{
677 d[i] = '\0';
678 break; /* won't fit */
681 else if((*s & 0xFC) == 0xF8){
682 if(i+4 < n){
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)){
687 d[i] = '\0';
688 break; /* bogus utf-8 */
691 else{
692 d[i] = '\0';
693 break; /* won't fit */
696 else if((*s & 0xFE) == 0xFC){
697 if(i+5 < n){
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)){
703 d[i] = '\0';
704 break; /* bogus utf-8 */
707 else{
708 d[i] = '\0';
709 break; /* won't fit */
712 else{
713 d[i] = '\0';
714 break; /* don't fit */
718 return(d);
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.
726 ----*/
727 char *
728 istrncpy(char *d, char *s, int n)
730 char *rv = d;
731 unsigned char c;
733 if(!d || !s)
734 return(NULL);
737 if(*s && (unsigned char)(*s) < 0x80 && FILTER_THIS(*s)
738 && !(*(s+1) && *s == ESCAPE && match_escapes(s+1))){
739 if(n-- > 0){
740 c = (unsigned char) *s;
741 *d++ = c >= 0x80 ? '~' : '^';
743 if(n-- > 0){
744 s++;
745 *d = (c == 0x7f) ? '?' : (c & 0x1f) + '@';
749 else{
750 if(n-- > 0)
751 *d = *s++;
753 while(n > 0 && *d++);
755 return(rv);
759 void
760 convert_string_to_utf8(char *buf, int bufsize)
762 char *s;
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};
775 char *
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)
781 return("xxx");
782 return(xmonths[month_num - 1]);
785 char *
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)
791 return("xxx");
792 else{
793 static char buf[120];
794 struct tm tm;
796 memset(&tm, 0, sizeof(tm));
797 tm.tm_year = 107;
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
805 * "<digit>"
806 * "<digit><digit>" or
807 * "<space><digit>"
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]))){
824 char *p;
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;
831 for(; p > buf; p--)
832 *p = *(p-1);
834 buf[0] = ' ';
837 return(buf);
840 else
841 return(month_abbrev(month_num));
842 #else /* DISABLE_LOCALE_DATES */
843 return(month_abbrev(month_num));
844 #endif /* DISABLE_LOCALE_DATES */
847 char *
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)
854 return("");
855 return(months[month_num - 1]);
858 char *
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)
864 return("");
865 else{
866 static char buf[120];
867 struct tm tm;
869 memset(&tm, 0, sizeof(tm));
870 tm.tm_year = 107;
871 tm.tm_mon = month_num-1;
872 our_strftime(buf, sizeof(buf), "%B", &tm);
873 convert_string_to_utf8(buf, sizeof(buf));
874 return(buf);
877 else
878 return(month_name(month_num));
879 #else /* DISABLE_LOCALE_DATES */
880 return(month_name(month_num));
881 #endif /* DISABLE_LOCALE_DATES */
885 char *
886 day_abbrev(int day_of_week)
888 if(day_of_week < 0 || day_of_week > 6)
889 return("???");
890 return(xdays[day_of_week]);
893 char *
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)
899 return("???");
900 else{
901 static char buf[120];
902 struct tm tm;
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));
908 return(buf);
911 else
912 return(day_abbrev(day_of_week));
913 #else /* DISABLE_LOCALE_DATES */
914 return(day_abbrev(day_of_week));
915 #endif /* DISABLE_LOCALE_DATES */
918 char *
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)
924 return("");
925 return(days[day_of_week]);
928 char *
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)
934 return("");
935 else{
936 static char buf[120];
937 struct tm tm;
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));
943 return(buf);
946 else
947 return(day_name(day_of_week));
948 #else /* DISABLE_LOCALE_DATES */
949 return(day_name(day_of_week));
950 #endif /* DISABLE_LOCALE_DATES */
954 size_t
955 our_strftime(char *dst, size_t dst_size, char *format, struct tm *tm)
957 #ifdef _WINDOWS
958 LPTSTR lptbuf, lptformat;
959 char *u;
961 lptbuf = (LPTSTR) fs_get(dst_size * sizeof(TCHAR));
962 lptbuf[0] = '\0';
963 lptformat = utf8_to_lptstr((LPSTR) format);
965 _tcsftime(lptbuf, dst_size, lptformat, tm);
966 u = lptstr_to_utf8(lptbuf);
967 if(u){
968 strncpy(dst, u, dst_size);
969 dst[dst_size-1] = '\0';
970 fs_give((void **) &u);
973 return(strlen(dst));
974 #else
975 return(strftime(dst, dst_size, format, tm));
976 #endif
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
987 ----*/
989 month_num(char *s)
991 int month = -1, year;
992 int i;
994 if(F_ON(F_PRUNE_USES_ISO,ps_global)){
995 char save, *p;
996 char digmon[3];
998 if(s && strlen(s) > 4 && s[4] == '-'){
999 save = s[4];
1000 s[4] = '\0';
1001 year = atoi(s);
1002 s[4] = save;
1003 if(year == 0)
1004 return(-1);
1006 p = s + 5;
1007 for(i = 0; i < 12; i++){
1008 digmon[0] = ((i+1) < 10) ? '0' : '1';
1009 digmon[1] = '0' + (i+1) % 10;
1010 digmon[2] = '\0';
1011 if(strcmp(digmon, p) == 0)
1012 break;
1015 if(i == 12)
1016 return(-1);
1018 month = year * 12 + i;
1021 else{
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)
1025 break;
1028 if(i == 12)
1029 return(-1);
1031 year = atoi(s + 4);
1032 if(year == 0)
1033 return(-1);
1035 month = year * 12 + i;
1039 return(month);
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.
1048 static struct {
1049 char *zone;
1050 short len,
1051 hour_offset,
1052 min_offset;
1053 } known_zones[] = {
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 */
1076 #else
1077 #ifdef IST_MEANS_INDIA
1078 {"IST", 3, 5, 30}, /* India Standard */
1079 #else
1080 #ifdef IST_MEANS_IRISH
1081 {"IST", 3, 1, 0}, /* Irish Standard */
1082 #endif
1083 #endif
1084 #endif
1085 {NULL, 0, 0},
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
1094 Returns nothing
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.
1108 ----*/
1109 void
1110 parse_date(char *given_date, struct date *d)
1112 char *p, **i, *q;
1113 int month, n;
1115 d->sec = -1;
1116 d->minute= -1;
1117 d->hour = -1;
1118 d->day = -1;
1119 d->month = -1;
1120 d->year = -1;
1121 d->wkday = -1;
1122 d->hours_off_gmt = -1;
1123 d->min_off_gmt = -1;
1125 if(given_date == NULL)
1126 return;
1128 p = given_date;
1129 while(*p && isspace((unsigned char)*p))
1130 p++;
1132 /* Start with weekday? */
1133 if((q=strchr(p, ',')) != NULL){
1135 if(q - p == 3){
1136 *q = '\0';
1137 for(i = xdays; *i != NULL; i++)
1138 if(strucmp(p, *i) == 0) /* Match first 3 letters */
1139 break;
1141 *q = ',';
1143 if(*i != NULL) {
1144 /* Started with week day */
1145 d->wkday = i - xdays;
1149 p = q+1;
1150 while(*p && isspace((unsigned char)*p))
1151 p++;
1153 else if((q=strchr(p, ' ')) != NULL && q - p == 3){
1154 *q = '\0';
1155 for(i = xdays; *i != NULL; i++)
1156 if(strucmp(p, *i) == 0) /* Match first 3 letters */
1157 break;
1159 *q = ' ';
1161 if(*i != NULL) {
1162 /* Started with week day */
1163 d->wkday = i - xdays;
1164 p = q+1;
1165 while(*p && isspace((unsigned char)*p))
1166 p++;
1170 if(isdigit((unsigned char)*p)) {
1171 d->day = atoi(p);
1172 while(*p && isdigit((unsigned char)*p))
1173 p++;
1174 while(*p && (*p == '-' || *p == ',' || isspace((unsigned char)*p)))
1175 p++;
1177 for(month = 1; month <= 12; month++)
1178 if(struncmp(p, month_abbrev(month), 3) == 0)
1179 break;
1180 if(month < 13) {
1181 d->month = month;
1184 /* Move over month, (or whatever is there) */
1185 while(*p && !isspace((unsigned char)*p) && *p != ',' && *p != '-')
1186 p++;
1187 while(*p && (isspace((unsigned char)*p) || *p == ',' || *p == '-'))
1188 p++;
1190 /* Check again for day */
1191 if(isdigit((unsigned char)*p) && d->day == -1) {
1192 d->day = atoi(p);
1193 while(*p && isdigit((unsigned char)*p))
1194 p++;
1195 while(*p && (*p == '-' || *p == ',' || isspace((unsigned char)*p)))
1196 p++;
1199 /*-- Check for time --*/
1200 for(q = p; *q && isdigit((unsigned char)*q); q++);
1201 if(*q == ':') {
1202 /* It's the time (out of place) */
1203 d->hour = atoi(p);
1204 while(*p && *p != ':' && !isspace((unsigned char)*p))
1205 p++;
1206 if(*p == ':') {
1207 p++;
1208 d->minute = atoi(p);
1209 while(*p && *p != ':' && !isspace((unsigned char)*p))
1210 p++;
1211 if(*p == ':') {
1212 d->sec = atoi(p);
1213 while(*p && !isspace((unsigned char)*p))
1214 p++;
1217 while(*p && isspace((unsigned char)*p))
1218 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)) {
1225 d->year = atoi(p);
1226 if(d->year < 50)
1227 d->year += 2000;
1228 else if(d->year < 100)
1229 d->year += 1900;
1230 while(*p && isdigit((unsigned char)*p))
1231 p++;
1232 while(*p && (*p == '-' || *p == ',' || isspace((unsigned char)*p)))
1233 p++;
1234 } else {
1235 /* Something weird, skip it and try to resynch */
1236 while(*p && !isspace((unsigned char)*p) && *p != ',' && *p != '-')
1237 p++;
1238 while(*p && (isspace((unsigned char)*p) || *p == ',' || *p == '-'))
1239 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) {
1245 d->hour = atoi(p);
1246 while(*p && *p != ':' && !isspace((unsigned char)*p))
1247 p++;
1248 if(*p == ':') {
1249 p++;
1250 d->minute = atoi(p);
1251 while(*p && *p != ':' && !isspace((unsigned char)*p))
1252 p++;
1253 if(*p == ':') {
1254 p++;
1255 d->sec = atoi(p);
1256 while(*p && !isspace((unsigned char)*p))
1257 p++;
1261 while(*p && isspace((unsigned char)*p))
1262 p++;
1265 /*-- The time zone --*/
1266 d->hours_off_gmt = 0;
1267 d->min_off_gmt = 0;
1268 if(*p) {
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])) {
1275 char tmp[3];
1276 d->min_off_gmt = d->hours_off_gmt = (*p == '+' ? 1 : -1);
1277 p++;
1278 tmp[0] = *p++;
1279 tmp[1] = *p++;
1280 tmp[2] = '\0';
1281 d->hours_off_gmt *= atoi(tmp);
1282 tmp[0] = *p++;
1283 tmp[1] = *p++;
1284 tmp[2] = '\0';
1285 d->min_off_gmt *= atoi(tmp);
1286 } else {
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;
1291 break;
1296 if(d->wkday == -1){
1297 MESSAGECACHE elt;
1298 struct tm *tm;
1299 time_t t;
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);
1307 tm = localtime(&t);
1309 if(tm)
1310 d->wkday = tm->tm_wday;
1316 char *
1317 convert_date_to_local(char *date)
1319 struct tm *tm;
1320 time_t ltime;
1321 static char datebuf[30];
1323 ltime = date_to_local_time_t(date);
1324 if(ltime == (time_t) -1)
1325 return(date);
1327 tm = localtime(&ltime);
1329 if(tm == NULL)
1330 return(date);
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);
1336 return(datebuf);
1341 timezone_offset_to_gmt(int *dst)
1343 int julian, offset;
1344 struct tm *tm;
1345 time_t now;
1347 offset = 0;
1348 /* find difference between gmtime and localtime, from c-client do_date */
1349 now = time((time_t *) 0);
1350 if(now != (time_t) -1){
1351 tm = gmtime(&now);
1352 if(tm != NULL){
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 */
1366 return offset;
1369 time_t
1370 date_to_local_time_t(char *date)
1372 time_t ourtime;
1373 struct tm theirtime;
1374 struct date d;
1375 static int zone = 1000000; /* initialize timezone offset */
1376 static int dst;
1378 if(zone == 1000000)
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)
1398 ourtime += zone;
1400 return(ourtime);
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.
1407 ----*/
1408 char *
1409 repeat_char(int n, int c)
1411 static char bb[3][MAX_SCREEN_COLS+1];
1412 static int whichbb = 0;
1413 char *b;
1415 whichbb = (whichbb + 1) % 3;
1416 b = bb[whichbb];
1418 if(n > sizeof(bb[0]))
1419 n = sizeof(bb[0]) - 1;
1421 bb[whichbb][n--] = '\0';
1422 while(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
1436 ----*/
1437 char *
1438 byte_string(long int bytes)
1440 char *a, aa[5];
1441 char *abbrevs = "GMK";
1442 long i, ones, tenths;
1443 static char string[50];
1445 ones = 0L;
1446 tenths = 0L;
1448 if(bytes == 0L){
1449 strncpy(string, "0 bytes", sizeof(string));
1450 string[sizeof(string)-1] = '\0';
1452 else {
1453 for(a = abbrevs, i = 1000000000; i >= 1; i /= 1000, a++) {
1454 if(bytes > i) {
1455 ones = bytes/i;
1456 if(ones < 10L && i > 10L)
1457 tenths = (bytes - (ones * i)) / (i / 10L);
1458 break;
1462 aa[0] = *a; aa[1] = '\0';
1464 if(tenths == 0)
1465 snprintf(string, sizeof(string), "%ld%s%s", ones, aa, *a ? "B" : "bytes");
1466 else
1467 snprintf(string, sizeof(string), "%ld.%ld%s%s", ones, tenths, aa, *a ? "B" : "bytes");
1470 return(string);
1475 /*----------------------------------------------------------------------
1476 Print a string corresponding to the number given:
1477 1st, 2nd, 3rd, 105th, 92342nd....
1478 ----*/
1480 char *
1481 enth_string(int i)
1483 static char enth[10];
1485 enth[0] = '\0';
1487 switch (i % 10) {
1489 case 1:
1490 if( (i % 100 ) == 11)
1491 snprintf(enth, sizeof(enth),"%dth", i);
1492 else
1493 snprintf(enth, sizeof(enth),"%dst", i);
1494 break;
1496 case 2:
1497 if ((i % 100) == 12)
1498 snprintf(enth, sizeof(enth), "%dth",i);
1499 else
1500 snprintf(enth, sizeof(enth), "%dnd",i);
1501 break;
1503 case 3:
1504 if(( i % 100) == 13)
1505 snprintf(enth, sizeof(enth), "%dth",i);
1506 else
1507 snprintf(enth, sizeof(enth), "%drd",i);
1508 break;
1510 default:
1511 snprintf(enth, sizeof(enth),"%dth",i);
1512 break;
1514 return(enth);
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.
1535 char *
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;
1545 char save_char;
1546 char *endptr = NULL;
1547 unsigned shorter, longer;
1548 unsigned got_width;
1550 cr = (flags & FLD_CRLF);
1551 preserve_ws = (flags & FLD_PWS);
1553 if(indent){
1554 indent2 = (int) utf8_width(indent);
1555 nbindent2 = strlen(indent);
1558 if(first_indent){
1559 indent1 = (int) utf8_width(first_indent);
1560 nb = strlen(first_indent);
1563 len = indent1;
1564 next_piece = src;
1565 eol = cr ? 2 : 1;
1566 if(!src || !*src)
1567 nb += eol;
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){
1575 len += indent2;
1576 nb += nbindent2;
1579 this_width = (int) utf8_width(next_piece);
1580 if(this_width + len <= width){
1581 nb += (strlen(next_piece) + eol);
1582 break;
1584 else{ /* fold it */
1585 starting_point = width - len; /* space left on this line */
1586 /* find a good folding spot */
1587 winner = -1;
1588 for(i = 0;
1589 winner == -1 && (starting_point - i > 5 || i < maxwidth - width);
1590 i++){
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;
1598 if(winner == -1
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))
1610 winner = (int) i;
1612 if(winner == -1){
1613 winner = got_width < 512 ? got_width : 512;
1614 endptr = NULL;
1618 if(winner == -1){ /* if no good folding spot, fold at width */
1619 winner = starting_point;
1620 endptr = NULL;
1623 if(endptr == NULL){
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]))
1631 next_piece++;
1634 len = 0;
1637 res = (char *) fs_get((nb+1) * sizeof(char));
1638 p = res;
1639 sstrncpy(&p, first_indent, nb+1-(p-res));
1640 len = indent1;
1641 next_piece = src;
1643 while(next_piece && *next_piece){
1644 if(next_piece != src && indent2){
1645 sstrncpy(&p, indent, nb+1-(p-res));
1646 len += indent2;
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)
1653 *p++ = '\r';
1655 if(p-res < nb+1)
1656 *p++ = '\n';
1658 break;
1660 else{ /* fold it */
1661 starting_point = width - len; /* space left on this line */
1662 /* find a good folding spot */
1663 winner = -1;
1664 for(i = 0;
1665 winner == -1 && (starting_point - i > 5 || i < maxwidth - width);
1666 i++){
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;
1674 if(winner == -1
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))
1686 winner = (int) i;
1688 if(winner == -1){
1689 winner = got_width < 512 ? got_width : 512;
1690 endptr = NULL;
1694 if(winner == -1){ /* if no good folding spot, fold at width */
1695 winner = starting_point;
1696 endptr = NULL;
1699 if(endptr == NULL){
1700 endptr = utf8_count_forw_width(next_piece, (unsigned) winner, &got_width);
1701 winner = (int) got_width;
1704 if(endptr){
1705 save_char = *endptr;
1706 *endptr = '\0';
1707 sstrncpy(&p, next_piece, nb+1-(p-res));
1708 *endptr = save_char;
1709 next_piece = endptr;
1712 if(cr && p-res < nb+1)
1713 *p++ = '\r';
1715 if(p-res < nb+1)
1716 *p++ = '\n';
1718 if(!preserve_ws && isspace((unsigned char) next_piece[0]))
1719 next_piece++;
1722 len = 0;
1725 if(!src || !*src){
1726 if(cr && p-res < nb+1)
1727 *p++ = '\r';
1729 if(p-res < nb+1)
1730 *p++ = '\n';
1733 if(p-res < nb+1)
1734 *p = '\0';
1736 res[nb] = '\0';
1738 return(res);
1743 * strsquish - fancifies a string into the given buffer if it's too
1744 * long to fit in the given width
1746 char *
1747 strsquish(char *buf, size_t buflen, char *src, int width)
1750 * Replace strsquish() with calls to short_str().
1752 if(width > 14)
1753 return(short_str(src, buf, buflen, width, MidDots));
1754 else
1755 return(short_str(src, buf, buflen, width, FrontDots));
1759 char *
1760 long2string(long int l)
1762 static char string[20];
1764 snprintf(string, sizeof(string), "%ld", l);
1765 return(string);
1769 char *
1770 ulong2string(unsigned long int l)
1772 static char string[20];
1774 snprintf(string, sizeof(string), "%lu", l);
1775 return(string);
1779 char *
1780 int2string(int i)
1782 static char string[20];
1784 snprintf(string, sizeof(string), "%d", i);
1785 return(string);
1790 * strtoval - convert the given string to a positive integer.
1792 char *
1793 strtoval(char *s, int *val, int minmum, int maxmum, int otherok, char *errbuf,
1794 size_t errbuflen, char *varname)
1796 int i = 0, neg = 1;
1797 char *p = s, *errstr = NULL;
1799 removing_leading_and_trailing_white_space(p);
1800 for(; *p; p++)
1801 if(isdigit((unsigned char) *p)){
1802 i = (i * 10) + (*p - '0');
1804 else if(*p == '-' && i == 0){
1805 neg = -1;
1807 else{
1808 snprintf(errstr = errbuf, errbuflen,
1809 "Non-numeric value ('%c' in \"%.8s\") in %s. Using \"%d\"",
1810 *p, s, varname, *val);
1811 return(errbuf);
1814 i *= neg;
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\"",
1825 varname, i, *val);
1826 else
1827 *val = i;
1829 return(errstr);
1834 * strtolval - convert the given string to a positive _long_ integer.
1836 char *
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);
1844 for(; *p; p++)
1845 if(isdigit((unsigned char) *p)){
1846 i = (i * 10L) + (*p - '0');
1848 else if(*p == '-' && i == 0L){
1849 neg = -1L;
1851 else{
1852 snprintf(errstr = errbuf, errbuflen,
1853 "Non-numeric value ('%c' in \"%.8s\") in %s. Using \"%ld\"",
1854 *p, s, varname, *val);
1855 return(errbuf);
1858 i *= neg;
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\"",
1869 varname, i, *val);
1870 else
1871 *val = i;
1873 return(errstr);
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_.
1895 void
1896 get_pair(char *string, char **label, char **value, int firstws, int strip_internal_label_quotes)
1898 char *p, *q, *tmp, *token = NULL;
1899 int quoted = 0;
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 */
1924 else
1925 p++;
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) == '\\'))
1941 *p++ = *++q;
1942 else if(!(strip_internal_label_quotes && *q == '"'))
1943 *p++ = *q;
1946 *p = '\0'; /* tie off label */
1947 fs_give((void **)&tmp);
1948 if(*label == NULL)
1949 fs_give((void **)label);
1951 else
1952 token = string;
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) == '\\'))
1963 *p++ = *++q;
1964 else
1965 *p++ = *q;
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.
1986 char *
1987 put_pair(char *label, char *value)
1989 char *result, *lab = label, *val = value;
1990 size_t l;
1992 if(label && *label)
1993 lab = quote_if_needed(label);
1995 if(value && *value)
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",
2002 lab ? lab : "",
2003 (lab && lab[0] && val && val[0]) ? " " : "",
2004 val ? val : "");
2006 if(lab && lab != label)
2007 fs_give((void **)&lab);
2008 if(val && val != value)
2009 fs_give((void **)&val);
2011 return(result);
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.
2020 char *
2021 quote_if_needed(char *src)
2023 char *result = src, *qsrc = NULL;
2025 if(src && *src){
2026 /* need quoting? */
2027 if(strpbrk(src, " \t") != NULL)
2028 qsrc = add_escapes(src, "\\\"", '\\', "", "");
2030 if(qsrc && !*qsrc)
2031 fs_give((void **)&qsrc);
2033 if(qsrc){
2034 size_t l;
2036 l = strlen(qsrc)+2;
2037 result = (char *) fs_get((l+1) * sizeof(char));
2038 snprintf(result, l+1, "\"%s\"", qsrc);
2039 fs_give((void **)&qsrc);
2043 return(result);
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)
2055 register int i, j;
2057 i = 0;
2058 for(j = 0; j < 3 && **s >= '0' && **s < '8' ; (*s)++, j++)
2059 i = (i * 8) + (int)(unsigned char)**s - '0';
2061 return(i);
2066 * Convert two consecutive HEX digits to an integer. First two
2067 * chars pointed to by "s" MUST already be tested for hexness.
2070 read_hex(char *s)
2072 return(X2C(s));
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.
2080 void
2081 char_to_octal_triple(int c, char *octal)
2083 c &= 0xff;
2085 octal[2] = (c % 8) + '0';
2086 c /= 8;
2087 octal[1] = (c % 8) + '0';
2088 c /= 8;
2089 octal[0] = c + '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
2097 * if not quoted.
2099 * Returns allocated C string version of s.
2101 char *
2102 string_to_cstring(char *s)
2104 char *b, *p;
2105 int n, i, all_space_so_far = 1;
2107 if(!s)
2108 return(cpystr(""));
2110 n = 20;
2111 b = (char *)fs_get((n+1) * sizeof(char));
2112 p = b;
2113 *p = '\0';
2114 i = 0;
2116 while(*s){
2117 if(*s != SPACE)
2118 all_space_so_far = 0;
2120 if(i + 4 > n){
2122 * The output string may overflow the output buffer.
2123 * Make more room.
2125 n += 20;
2126 fs_resize((void **)&b, (n+1) * sizeof(char));
2127 p = &b[i];
2129 else{
2130 switch(*s){
2131 case '\n':
2132 *p++ = '\\';
2133 *p++ = 'n';
2134 i += 2;
2135 break;
2137 case '\r':
2138 *p++ = '\\';
2139 *p++ = 'r';
2140 i += 2;
2141 break;
2143 case '\t':
2144 *p++ = '\\';
2145 *p++ = 't';
2146 i += 2;
2147 break;
2149 case '\b':
2150 *p++ = '\\';
2151 *p++ = 'b';
2152 i += 2;
2153 break;
2155 case '\f':
2156 *p++ = '\\';
2157 *p++ = 'f';
2158 i += 2;
2159 break;
2161 case '\\':
2162 *p++ = '\\';
2163 *p++ = '\\';
2164 i += 2;
2165 break;
2167 case SPACE:
2168 if(all_space_so_far){ /* use octal output */
2169 *p++ = '\\';
2170 char_to_octal_triple(*s, p);
2171 p += 3;
2172 i += 4;
2173 break;
2175 else{
2176 /* fall through */
2180 default:
2181 if(*s >= SPACE && *s < '~' && *s != '\"' && *s != '$'){
2182 *p++ = *s;
2183 i++;
2185 else{ /* use octal output */
2186 *p++ = '\\';
2187 char_to_octal_triple(*s, p);
2188 p += 3;
2189 i += 4;
2192 break;
2195 s++;
2199 *p = '\0';
2200 return(b);
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.
2210 char *
2211 cstring_to_hexstring(char *s)
2213 char *b, *p;
2214 int n, i, c;
2216 if(!s)
2217 return(cpystr(""));
2219 n = 20;
2220 b = (char *)fs_get((n+1) * sizeof(char));
2221 p = b;
2222 *p = '\0';
2223 i = 0;
2225 while(*s){
2226 if(i + 2 > n){
2228 * The output string may overflow the output buffer.
2229 * Make more room.
2231 n += 20;
2232 fs_resize((void **)&b, (n+1) * sizeof(char));
2233 p = &b[i];
2235 else{
2236 if(*s == '\\'){
2237 s++;
2238 switch(*s){
2239 case 'n':
2240 c = '\n';
2241 C2XPAIR(c, p);
2242 i += 2;
2243 s++;
2244 break;
2246 case 'r':
2247 c = '\r';
2248 C2XPAIR(c, p);
2249 i += 2;
2250 s++;
2251 break;
2253 case 't':
2254 c = '\t';
2255 C2XPAIR(c, p);
2256 i += 2;
2257 s++;
2258 break;
2260 case 'v':
2261 c = '\v';
2262 C2XPAIR(c, p);
2263 i += 2;
2264 s++;
2265 break;
2267 case 'b':
2268 c = '\b';
2269 C2XPAIR(c, p);
2270 i += 2;
2271 s++;
2272 break;
2274 case 'f':
2275 c = '\f';
2276 C2XPAIR(c, p);
2277 i += 2;
2278 s++;
2279 break;
2281 case 'a':
2282 c = '\007';
2283 C2XPAIR(c, p);
2284 i += 2;
2285 s++;
2286 break;
2288 case '\\':
2289 c = '\\';
2290 C2XPAIR(c, p);
2291 i += 2;
2292 s++;
2293 break;
2295 case '?':
2296 c = '?';
2297 C2XPAIR(c, p);
2298 i += 2;
2299 s++;
2300 break;
2302 case '\'':
2303 c = '\'';
2304 C2XPAIR(c, p);
2305 i += 2;
2306 s++;
2307 break;
2309 case '\"':
2310 c = '\"';
2311 C2XPAIR(c, p);
2312 i += 2;
2313 s++;
2314 break;
2316 case 0: /* reached end of s too early */
2317 c = 0;
2318 C2XPAIR(c, p);
2319 i += 2;
2320 s++;
2321 break;
2323 /* hex number */
2324 case 'x':
2325 s++;
2326 if(isxpair(s)){
2327 c = X2C(s);
2328 s += 2;
2330 else if(isxdigit((unsigned char)*s)){
2331 c = XDIGIT2C(*s);
2332 s++;
2334 else
2335 c = 0;
2337 C2XPAIR(c, p);
2338 i += 2;
2340 break;
2342 /* octal number */
2343 default:
2344 c = read_octal(&s);
2345 C2XPAIR(c, p);
2346 i += 2;
2348 break;
2351 else{
2352 C2XPAIR(*s, p);
2353 i += 2;
2354 s++;
2359 *p = '\0';
2360 return(b);
2365 * Convert C-style string, with backslash escapes, into a regular string.
2366 * Result goes in dst, which should be as big as src.
2369 void
2370 cstring_to_string(char *src, char *dst)
2372 char *p;
2373 int c;
2375 dst[0] = '\0';
2376 if(!src)
2377 return;
2379 p = dst;
2381 while(*src){
2382 if(*src == '\\'){
2383 src++;
2384 switch(*src){
2385 case 'n':
2386 *p++ = '\n';
2387 src++;
2388 break;
2390 case 'r':
2391 *p++ = '\r';
2392 src++;
2393 break;
2395 case 't':
2396 *p++ = '\t';
2397 src++;
2398 break;
2400 case 'v':
2401 *p++ = '\v';
2402 src++;
2403 break;
2405 case 'b':
2406 *p++ = '\b';
2407 src++;
2408 break;
2410 case 'f':
2411 *p++ = '\f';
2412 src++;
2413 break;
2415 case 'a':
2416 *p++ = '\007';
2417 src++;
2418 break;
2420 case '\\':
2421 *p++ = '\\';
2422 src++;
2423 break;
2425 case '?':
2426 *p++ = '?';
2427 src++;
2428 break;
2430 case '\'':
2431 *p++ = '\'';
2432 src++;
2433 break;
2435 case '\"':
2436 *p++ = '\"';
2437 src++;
2438 break;
2440 case 0: /* reached end of s too early */
2441 src++;
2442 break;
2444 /* hex number */
2445 case 'x':
2446 src++;
2447 if(isxpair(src)){
2448 c = X2C(src);
2449 src += 2;
2451 else if(isxdigit((unsigned char)*src)){
2452 c = XDIGIT2C(*src);
2453 src++;
2455 else
2456 c = 0;
2458 *p++ = c;
2460 break;
2462 /* octal number */
2463 default:
2464 c = read_octal(&src);
2465 *p++ = c;
2466 break;
2469 else
2470 *p++ = *src++;
2473 *p = '\0';
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.
2488 char *
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.
2509 char *
2510 remove_backslash_escapes(char *src)
2512 char *ans = NULL, *q, *p;
2513 int done = 0;
2515 if(src){
2516 p = q = (char *)fs_get(strlen(src) + 1);
2518 while(!done){
2519 switch(*src){
2520 case '\\':
2521 src++;
2522 if(*src){
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);
2528 src += 3;
2530 else
2531 *p++ = *src++;
2534 break;
2536 case '\0':
2537 case '/':
2538 done++;
2539 break;
2541 default:
2542 *p++ = *src++;
2543 break;
2547 *p = '\0';
2549 ans = cpystr(q);
2550 fs_give((void **)&q);
2553 return(ans);
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.
2567 char *
2568 add_viewerhdr_escapes(char *src)
2570 char *tmp, *ans = NULL;
2572 tmp = add_escapes(src, "/\\", '\\', ",", "");
2574 if(tmp){
2575 ans = dollar_escape_dollars(tmp);
2576 fs_give((void **) &tmp);
2579 return(ans);
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.
2593 char *
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.
2606 char *
2607 vcard_escape(char *src)
2609 char *p, *q;
2611 q = add_escapes(src, ";,\\", '\\', "", "");
2612 if(q){
2613 /* now do CRLF -> \n in place */
2614 for(p = q; *p != '\0'; p++)
2615 if(*p == '\r' && *(p+1) == '\n'){
2616 *p++ = '\\';
2617 *p = 'n';
2621 return(q);
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.
2634 char *
2635 vcard_unescape(char *src)
2637 char *ans = NULL, *p;
2638 int done = 0;
2640 if(src){
2641 p = ans = (char *)fs_get(strlen(src) + 1);
2643 while(!done){
2644 switch(*src){
2645 case '\\':
2646 src++;
2647 if(*src == 'n' || *src == 'N'){
2648 *p++ = '\n';
2649 src++;
2651 else if(*src)
2652 *p++ = *src++;
2654 break;
2656 case '\0':
2657 done++;
2658 break;
2660 default:
2661 *p++ = *src++;
2662 break;
2666 *p = '\0';
2669 return(ans);
2674 * Turn folded lines into long lines in place.
2676 * CRLF whitespace sequences are removed, the space is not preserved.
2678 void
2679 vcard_unfold(char *string)
2681 char *p = string;
2683 while(*string) /* while something to copy */
2684 if(*string == '\r' &&
2685 *(string+1) == '\n' &&
2686 (*(string+2) == SPACE || *(string+2) == TAB))
2687 string += 3;
2688 else
2689 *p++ = *string++;
2691 *p = '\0';
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.
2709 char *
2710 add_escapes(char *src, char *quote_these_chars, int quoting_char,
2711 char *hex_these_chars, char *hex_these_quoted_chars)
2713 char *ans = NULL;
2715 if(!quote_these_chars)
2716 alpine_panic("bad arg to add_escapes");
2718 if(src){
2719 char *q, *p, *qchar;
2721 p = q = (char *)fs_get(2*strlen(src) + 1);
2723 while(*src){
2724 if(*src == quoting_char)
2725 for(qchar = hex_these_quoted_chars; *qchar != '\0'; qchar++)
2726 if(*(src+1) == *qchar)
2727 break;
2729 if(*src == quoting_char && *qchar){
2730 src++; /* skip quoting_char */
2731 *p++ = '\\';
2732 *p++ = 'x';
2733 C2XPAIR(*src, p);
2734 src++; /* skip quoted char */
2736 else{
2737 for(qchar = quote_these_chars; *qchar != '\0'; qchar++)
2738 if(*src == *qchar)
2739 break;
2741 if(*qchar){ /* *src is a char to be quoted */
2742 *p++ = quoting_char;
2743 *p++ = *src++;
2745 else{
2746 for(qchar = hex_these_chars; *qchar != '\0'; qchar++)
2747 if(*src == *qchar)
2748 break;
2750 if(*qchar){ /* *src is a char to be escaped */
2751 *p++ = '\\';
2752 *p++ = 'x';
2753 C2XPAIR(*src, p);
2754 src++;
2756 else /* a regular char */
2757 *p++ = *src++;
2763 *p = '\0';
2765 ans = cpystr(q);
2766 fs_give((void **)&q);
2769 return(ans);
2774 * Copy a string enclosed in "" without fixing \" or \\. Skip past \"
2775 * but copy it as is, removing only the enclosing quotes.
2777 char *
2778 copy_quoted_string_asis(char *src)
2780 char *q = NULL, *p;
2781 int done = 0, quotes = 0;
2783 if(src){
2784 p = q = (char *)fs_get(strlen(src) + 1);
2786 while(!done){
2787 switch(*src){
2788 case QUOTE:
2789 if(++quotes == 2)
2790 done++;
2791 else
2792 src++;
2794 break;
2796 case BSLASH: /* don't count \" as a quote, just copy */
2797 if(*(src+1) == QUOTE){
2798 if(quotes == 1){
2799 *p++ = *src;
2800 *p++ = *(src+1);
2803 src += 2;
2805 else{
2806 if(quotes == 1)
2807 *p++ = *src;
2809 src++;
2812 break;
2814 case '\0':
2815 fs_give((void **)&q);
2816 return(NULL);
2818 default:
2819 if(quotes == 1)
2820 *p++ = *src;
2822 src++;
2824 break;
2828 *p = '\0';
2831 return(q);
2836 * isxpair -- return true if the first two chars in string are
2837 * hexadecimal characters
2840 isxpair(char *s)
2842 return(isxdigit((unsigned char) *s) && isxdigit((unsigned char) *(s+1)));
2850 * * * * * * * something to help managing lists of strings * * * * * * * *
2854 STRLIST_S *
2855 new_strlist(char *name)
2857 return new_strlist_auth(name, NULL, '\0');
2860 STRLIST_S *
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));
2868 if(name){
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;
2874 return(sp);
2878 STRLIST_S *
2879 copy_strlist(STRLIST_S *src)
2881 STRLIST_S *ret = NULL, *sl, *ss, *new_sl;
2883 if(src){
2884 ss = NULL;
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));
2888 if(sl->name)
2889 new_sl->name = cpystr(sl->name);
2891 if(ss){
2892 ss->next = new_sl;
2893 ss = ss->next;
2895 else{
2896 ret = new_sl;
2897 ss = ret;
2902 return(ret);
2907 * Add the second list to the end of the first.
2909 void
2910 combine_strlists(STRLIST_S **first, STRLIST_S *second)
2912 STRLIST_S *sl;
2914 if(!second)
2915 return;
2917 if(first){
2918 if(*first){
2919 for(sl = *first; sl->next; sl = sl->next)
2922 sl->next = second;
2924 else
2925 *first = second;
2930 void
2931 free_strlist(STRLIST_S **strp)
2933 if(strp && *strp){
2934 if((*strp)->next)
2935 free_strlist(&(*strp)->next);
2937 if((*strp)->name)
2938 fs_give((void **) &(*strp)->name);
2940 fs_give((void **) strp);
2944 void
2945 convert_decimal_to_roman (char *rn, size_t len, long n, char l)
2947 char symbols[8];
2948 int amo[8];
2949 int i, j, k;
2951 rn[0] = '\0';
2952 if(n >= 4000L || n <= 0L)
2953 return;
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';
2961 symbols[6] = l;
2962 symbols[7] = '\0';
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;
2970 amo[6] = n;
2971 amo[7] = 0; /* make valgrind happy */
2973 for(i = 0, j = 0; i < len && j < strlen(symbols); j++){
2974 if(amo[j] < 4){
2975 if(amo[j+1] != 4){
2976 for(k = 0; k < amo[j]; k++)
2977 rn[i++] = symbols[j];
2979 } else {
2980 if(amo[j-1] == 0){
2981 rn[i++] = symbols[j];
2982 rn[i++] = symbols[j-1];
2983 } else {
2984 rn[i++] = symbols[j];
2985 rn[i++] = symbols[j-2];
2989 if(i < len) rn[i] = '\0';
2990 rn[len-1] = '\0';
2993 void
2994 convert_decimal_to_alpha (char *rn, size_t len, long n, char l)
2996 int amo[16];
2997 int i;
2999 rn[0] = '\0';
3001 if(n < 0)
3002 return;
3004 for(i = 0; i < sizeof(amo) && n > 0; i++){
3005 amo[i] = n % 26;
3006 n = (n - amo[i])/26;
3008 amo[i] = -1;
3010 for(i = 0; i < len && amo[i] >= 0; i++)
3011 rn[i] = l + amo[i] - 1;
3012 if(i < len) rn[i] = '\0';
3013 rn[len-1] = '\0';
3016 void
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;
3026 if(endquote)
3027 eos = s;
3028 if(startquote == 0){
3029 if(*s == '"')
3030 startquote = 1;
3031 if(startquote)
3032 bos = s;
3034 if(startquote && endquote){
3035 if(bos == name && eos[1] == '\0'){
3036 for(s = name + 1; *s && s < eos; s++)
3037 *(s-1) = *s;
3038 *(s-1) = '\0';
3040 startquote = endquote = 0;