* Create help for explaining how encrypted password file support
[alpine.git] / pith / string.c
blob6ad1045af71e714ecf57ac29b8b36807639d7573
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: string.c 910 2008-01-14 22:28:38Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2006-2008 University of Washington
8 * Copyright 2013-2014 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 /*======================================================================
20 string.c
21 Misc extra and useful string functions
22 - rplstr replace a substring with another string
23 - sqzspaces Squeeze out the extra blanks in a string
24 - sqznewlines Squeeze out \n and \r.
25 - removing_trailing_white_space
26 - short_str Replace part of string with ... for display
27 - removing_leading_white_space
28 Remove leading or trailing white space
29 - removing_double_quotes
30 Remove surrounding double quotes
31 - strclean
32 both of above plus convert to lower case
33 - skip_white_space
34 return pointer to first non-white-space char
35 - skip_to_white_space
36 return pointer to first white-space char
37 - srchstr Search a string for first occurrence of a sub string
38 - srchrstr Search a string for last occurrence of a sub string
39 - strindex Replacement for strchr/index
40 - strrindex Replacement for strrchr/rindex
41 - sstrncpy Copy one string onto another, advancing dest'n pointer
42 - istrncpy Copy n chars between bufs, making ctrl chars harmless
43 - month_abbrev Return three letter abbreviations for months
44 - month_num Calculate month number from month/year string
45 - cannon_date Formalize format of a some what formatted date
46 - repeat_char Returns a string n chars long
47 - fold Inserts newlines for folding at whitespace.
48 - byte_string Format number of bytes with Kb, Mb, Gb or bytes
49 - enth-string Format number i.e. 1: 1st, 983: 983rd....
50 - string_to_cstring Convert string to C-style constant string with \'s
51 - cstring_to_hexstring Convert cstring to hex string
52 - cstring_to_string Convert C-style string to string
53 - add_backslash_escapes Escape / and \ with \
54 - remove_backslash_escapes Undo the \ escaping, and stop string at /.
56 ====*/
58 #include "../pith/headers.h"
59 #include "../pith/string.h"
60 #include "../pith/state.h"
61 #include "../pith/conf.h"
62 #include "../pith/escapes.h"
63 #include "../pith/util.h"
66 void char_to_octal_triple(int, char *);
67 char *dollar_escape_dollars(char *);
68 void convert_string_to_utf8(char *, int);
71 /*----------------------------------------------------------------------
72 Replace dl characters in one string with another given string
74 args: os -- pointer into output string
75 oslen -- size of output string starting at os
76 dl -- the number of character to delete starting at os
77 is -- The string to insert
79 Result: returns pointer in originl string to end of string just inserted
80 ---*/
81 char *
82 rplstr(char *os, size_t oslen, int dl, char *is)
84 char *x1, *x2, *x3;
85 int diff;
87 if(os == NULL)
88 return(NULL);
90 x1 = os + strlen(os);
92 /* can't delete more characters than there are */
93 if(dl > x1 - os)
94 dl = x1 - os;
96 x2 = is;
97 if(is != NULL)
98 x2 = is + strlen(is);
100 diff = (x2 - is) - dl;
102 if(diff < 0){ /* String shrinks */
103 x3 = os;
104 if(is != NULL)
105 for(x2 = is; *x2; *x3++ = *x2++) /* copy new string in */
108 for(x2 = x3 - diff; *x2; *x3++ = *x2++) /* shift for delete */
111 *x3 = *x2; /* the null */
113 else{ /* String grows */
114 /* make room for insert */
115 x3 = x1 + diff;
116 if(x3 >= os + oslen) /* just protecting ourselves */
117 x3 = os + oslen - 1;
119 for(; x3 >= os + (x2 - is); *x3-- = *x1--); /* shift*/
122 if(is != NULL)
123 for(x1 = os, x2 = is; *x2 ; *x1++ = *x2++)
126 while(*x3) x3++;
129 os[oslen-1] = '\0';
131 return(x3);
136 /*----------------------------------------------------------------------
137 Squeeze out blanks
138 ----------------------------------------------------------------------*/
139 void
140 sqzspaces(char *string)
142 char *p = string;
144 while((*string = *p++) != '\0') /* while something to copy */
145 if(!isspace((unsigned char)*string)) /* only really copy if non-blank */
146 string++;
151 /*----------------------------------------------------------------------
152 Squeeze out CR's and LF's
153 ----------------------------------------------------------------------*/
154 void
155 sqznewlines(char *string)
157 char *p = string;
159 while((*string = *p++) != '\0') /* while something to copy */
160 if(*string != '\r' && *string != '\n') /* only copy if non-newline */
161 string++;
166 /*----------------------------------------------------------------------
167 Remove leading white space from a string in place
169 Args: string -- string to remove space from
170 ----*/
171 void
172 removing_leading_white_space(char *string)
174 register char *p;
176 if(!string || !*string || !isspace(*string))
177 return;
179 for(p = string; *p; p++) /* find the first non-blank */
180 if(!isspace((unsigned char) *p)){
181 while((*string++ = *p++) != '\0') /* copy back from there... */
184 return;
188 /* replace_embedded_tabs_by_space
189 replace any tab by only one space, when we do not want to see them
190 in the from or subject field.
192 void
193 replace_tabs_by_space(char *orig)
195 char *s;
197 for(s = orig; s != NULL && *s != '\0' ; s++)
198 if(*s == '\t') *s = ' ';
202 /*----------------------------------------------------------------------
203 Remove trailing white space from a string in place
205 Args: string -- string to remove space from
206 ----*/
207 void
208 removing_trailing_white_space(char *string)
210 char *p = NULL;
212 if(!string || !*string)
213 return;
215 for(; *string; string++) /* remember start of whitespace */
216 p = (!isspace((unsigned char)*string)) ? NULL : (!p) ? string : p;
218 if(p) /* if whitespace, blast it */
219 *p = '\0';
223 void
224 removing_leading_and_trailing_white_space(char *string)
226 register char *p, *q = NULL;
228 if(!string || !*string)
229 return;
231 for(p = string; *p; p++) /* find the first non-blank */
232 if(!isspace((unsigned char)*p)){
233 if(p == string){ /* don't have to copy in this case */
234 for(; *string; string++)
235 q = (!isspace((unsigned char)*string)) ? NULL : (!q) ? string : q;
237 else{
238 for(; (*string = *p++) != '\0'; string++)
239 q = (!isspace((unsigned char)*string)) ? NULL : (!q) ? string : q;
242 if(q)
243 *q = '\0';
245 return;
248 if(*string != '\0')
249 *string = '\0';
253 /*----------------------------------------------------------------------
254 Remove one set of double quotes surrounding string in place
255 Returns 1 if quotes were removed
257 Args: string -- string to remove quotes from
258 ----*/
260 removing_double_quotes(char *string)
262 register char *p;
263 int ret = 0;
265 if(string && string[0] == '"' && string[1] != '\0'){
266 p = string + strlen(string) - 1;
267 if(*p == '"'){
268 ret++;
269 *p = '\0';
270 for(p = string; *p; p++)
271 *p = *(p+1);
275 return(ret);
280 /*----------------------------------------------------------------------
281 return a pointer to first non-whitespace char in string
283 Args: string -- string to scan
284 ----*/
285 char *
286 skip_white_space(char *string)
288 while(*string && isspace((unsigned char) *string))
289 string++;
291 return(string);
296 /*----------------------------------------------------------------------
297 return a pointer to first whitespace char in string
299 Args: string -- string to scan
300 ----*/
301 char *
302 skip_to_white_space(char *string)
304 while(*string && !isspace((unsigned char) *string))
305 string++;
307 return(string);
312 /*----------------------------------------------------------------------
313 Remove quotes from a string in place
315 Args: string -- string to remove quotes from
316 Rreturns: string passed us, but with quotes gone
317 ----*/
318 char *
319 removing_quotes(char *string)
321 char *p, *q;
323 if(*(p = q = string) == '\"'){
324 q++;
326 if(*q == '\"' || *q == '\\')
327 q++;
328 while((*p++ = *q++) != '\0');
331 return(string);
336 /*---------------------------------------------------
337 Remove leading whitespace, trailing whitespace and convert
338 to lowercase
340 Args: s, -- The string to clean
342 Result: the cleaned string
343 ----*/
344 char *
345 strclean(char *string)
347 char *s = string, *sc = NULL, *p = NULL;
349 for(; *s; s++){ /* single pass */
350 if(!isspace((unsigned char)*s)){
351 p = NULL; /* not start of blanks */
352 if(!sc) /* first non-blank? */
353 sc = string; /* start copying */
355 else if(!p) /* it's OK if sc == NULL */
356 p = sc; /* start of blanks? */
358 if(sc) /* if copying, copy */
359 *sc++ = isupper((unsigned char)(*s))
360 ? (unsigned char)tolower((unsigned char)(*s))
361 : (unsigned char)(*s);
364 if(p) /* if ending blanks */
365 *p = '\0'; /* tie off beginning */
366 else if(!sc) /* never saw a non-blank */
367 *string = '\0'; /* so tie whole thing off */
369 return(string);
374 * Returns a pointer to a short version of the string.
375 * If src is not longer than wid, pointer points to src.
376 * If longer than wid, a version which is wid long is made in
377 * buf and the pointer points there.
379 * Wid refers to UTF-8 screen width, not strlen width.
381 * Args src -- The string to be shortened
382 * buf -- A place to put the short version
383 * wid -- Desired width of shortened string
384 * where -- Where should the dots be in the shortened string. Can be
385 * FrontDots, MidDots, EndDots.
387 * FrontDots ...stuvwxyz
388 * EndDots abcdefgh...
389 * MidDots abcd...wxyz
391 char *
392 short_str(char *src, char *buf, size_t buflen, int wid, WhereDots where)
394 char *ans;
395 unsigned alen, first, second;
397 if(wid <= 0){
398 ans = buf;
399 if(buflen > 0)
400 buf[0] = '\0';
402 else if((alen = utf8_width(src)) <= wid)
403 ans = src;
404 else{
405 ans = buf;
406 if(wid < 5){
407 if(buflen > wid){
408 strncpy(buf, "....", buflen);
409 buf[wid] = '\0';
412 else{
413 char *q;
414 unsigned got_width;
417 * first == length of preellipsis text
418 * second == length of postellipsis text
420 if(where == FrontDots){
421 first = 0;
422 second = wid - 3;
424 else if(where == MidDots){
425 first = (wid - 3)/2;
426 second = wid - 3 - first;
428 else if(where == EndDots){
429 first = wid - 3;
430 second = 0;
433 q = buf;
434 if(first > 0){
435 q += utf8_to_width(q, src, buflen, first, &got_width);
436 if(got_width != first){
437 if(second)
438 second++;
439 else
440 while(got_width < first && buflen-(q-buf) > 0)
441 *q++ = '.';
445 if(buflen - (q-buf) > 3){
446 strncpy(q, "...", buflen - (q-buf));
447 buf[buflen-1] = '\0';
448 q += strlen(q);
451 if(second > 0){
452 char *p;
454 p = utf8_count_back_width(src, src+strlen(src), second, &got_width);
455 if(buflen - (q-buf) > strlen(p)){
456 strncpy(q, p, buflen - (q-buf));
457 buf[buflen-1] = '\0';
458 q += strlen(q);
462 if(buflen - (q-buf) > 0)
463 *q = '\0';
465 buf[buflen-1] = '\0';
469 return(ans);
474 /*----------------------------------------------------------------------
475 Search one string for another
477 Args: haystack -- The string to search in, the larger string
478 needle -- The string to search for, the smaller string
480 Search for first occurrence of needle in the haystack, and return a pointer
481 into the string haystack when it is found. The text we are dealing with is
482 UTF-8. We'd like the search to be case-independent but we're not sure what
483 that means for UTF-8. We're not even sure what matching means. We're not going
484 to worry about composed characters and canonical forms and anything like that
485 for now. Instead, we'll do the case-independent thing for ascii but exact
486 equality for the rest of the character space.
487 ----*/
488 char *
489 srchstr(char *haystack, char *needle)
491 char *p, *q;
493 #define CMPNOCASE(x, y) (((isascii((unsigned char) (x)) && isupper((unsigned char) (x))) \
494 ? tolower((unsigned char) (x)) \
495 : (unsigned char) (x)) \
496 == ((isascii((unsigned char) (y)) && isupper((unsigned char) (y))) \
497 ? tolower((unsigned char) (y)) \
498 : (unsigned char) (y)))
500 if(needle && haystack)
501 for(; *haystack; haystack++)
502 for(p = needle, q = haystack; ; p++, q++){
503 if(!*p)
504 return(haystack); /* winner! */
505 else if(!*q)
506 return(NULL); /* len(needle) > len(haystack)! */
507 else if(*p != *q && !CMPNOCASE(*p, *q))
508 break;
511 return(NULL);
516 /*----------------------------------------------------------------------
517 Search one string for another, from right
519 Args: is -- The string to search in, the larger string
520 ss -- The string to search for, the smaller string
522 Search for last occurrence of ss in the is, and return a pointer
523 into the string is when it is found. The search is case indepedent.
524 ----*/
526 char *
527 srchrstr(register char *is, register char *ss)
529 register char *sx, *sy;
530 char *ss_store, *rv;
531 char *begin_is;
532 char temp[251];
534 if(is == NULL || ss == NULL)
535 return(NULL);
537 if(strlen(ss) > sizeof(temp) - 2)
538 ss_store = (char *)fs_get(strlen(ss) + 1);
539 else
540 ss_store = temp;
542 for(sx = ss, sy = ss_store; *sx != '\0' ; sx++, sy++)
543 *sy = isupper((unsigned char)(*sx))
544 ? (unsigned char)tolower((unsigned char)(*sx))
545 : (unsigned char)(*sx);
546 *sy = *sx;
548 begin_is = is;
549 is = is + strlen(is) - strlen(ss_store);
550 rv = NULL;
551 while(is >= begin_is){
552 for(sx = is, sy = ss_store;
553 ((*sx == *sy)
554 || ((isupper((unsigned char)(*sx))
555 ? (unsigned char)tolower((unsigned char)(*sx))
556 : (unsigned char)(*sx)) == (unsigned char)(*sy))) && *sy;
557 sx++, sy++)
560 if(!*sy){
561 rv = is;
562 break;
565 is--;
568 if(ss_store != temp)
569 fs_give((void **)&ss_store);
571 return(rv);
576 /*----------------------------------------------------------------------
577 A replacement for strchr or index ...
579 Returns a pointer to the first occurrence of the character
580 'ch' in the specified string or NULL if it doesn't occur
582 ....so we don't have to worry if it's there or not. We bring our own.
583 If we really care about efficiency and think the local one is more
584 efficient the local one can be used, but most of the things that take
585 a long time are in the c-client and not in pine.
586 ----*/
587 char *
588 strindex(char *buffer, int ch)
591 if(*buffer == ch)
592 return(buffer);
593 while (*buffer++ != '\0');
595 return(NULL);
599 /* Returns a pointer to the last occurrence of the character
600 * 'ch' in the specified string or NULL if it doesn't occur
602 char *
603 strrindex(char *buffer, int ch)
605 char *address = NULL;
608 if(*buffer == ch)
609 address = buffer;
610 while (*buffer++ != '\0');
611 return(address);
615 /*----------------------------------------------------------------------
616 copy at most n chars of the UTF-8 source string onto the destination string
617 returning pointer to start of destination and converting any undisplayable
618 characters to harmless character equivalents.
619 ----*/
620 char *
621 iutf8ncpy(char *d, char *s, int n)
623 register int i;
625 if(!d || !s)
626 return(NULL);
629 * BUG: this needs to get improved to actually count the
630 * character "cell" positions. For now, at least don't break
631 * a multi-byte character.
633 for(i = 0; i < n && (d[i] = *s) != '\0'; s++, i++)
634 if((unsigned char)(*s) < 0x80 && FILTER_THIS(*s)){
635 if(i+1 < n){
636 d[i] = '^';
637 d[++i] = (*s == 0x7f) ? '?' : *s + '@';
639 else{
640 d[i] = '\0';
641 break; /* don't fit */
644 else if(*s & 0x80){
645 /* multi-byte character */
646 if((*s & 0xE0) == 0xC0){
647 if(i+1 < n){
648 if(((d[++i] = *++s) & 0xC0) != 0x80){
649 d[i] = '\0';
650 break; /* bogus utf-8 */
653 else{
654 d[i] = '\0';
655 break; /* too long */
658 else if((*s & 0xF0) == 0xE0){
659 if(i+2 < n){
660 if(!(((d[++i] = *++s) & 0xC0) == 0x80
661 && ((d[++i] = *++s) & 0xC0) == 0x80)){
662 d[i] = '\0';
663 break; /* bogus utf-8 */
666 else{
667 d[i] = '\0';
668 break; /* won't fit */
671 else if((*s & 0xF8) == 0xF0){
672 if(i+3 < n){
673 if(!(((d[++i] = *++s) & 0xC0) == 0x80
674 && ((d[++i] = *++s) & 0xC0) == 0x80
675 && ((d[++i] = *++s) & 0xC0) == 0x80)){
676 d[i] = '\0';
677 break; /* bogus utf-8 */
680 else{
681 d[i] = '\0';
682 break; /* won't fit */
685 else if((*s & 0xFC) == 0xF8){
686 if(i+4 < n){
687 if(!(((d[++i] = *++s) & 0xC0) == 0x80
688 && ((d[++i] = *++s) & 0xC0) == 0x80
689 && ((d[++i] = *++s) & 0xC0) == 0x80
690 && ((d[++i] = *++s) & 0xC0) == 0x80)){
691 d[i] = '\0';
692 break; /* bogus utf-8 */
695 else{
696 d[i] = '\0';
697 break; /* won't fit */
700 else if((*s & 0xFE) == 0xFC){
701 if(i+5 < n){
702 if(!(((d[++i] = *++s) & 0xC0) == 0x80
703 && ((d[++i] = *++s) & 0xC0) == 0x80
704 && ((d[++i] = *++s) & 0xC0) == 0x80
705 && ((d[++i] = *++s) & 0xC0) == 0x80
706 && ((d[++i] = *++s) & 0xC0) == 0x80)){
707 d[i] = '\0';
708 break; /* bogus utf-8 */
711 else{
712 d[i] = '\0';
713 break; /* won't fit */
716 else{
717 d[i] = '\0';
718 break; /* don't fit */
722 return(d);
726 /*----------------------------------------------------------------------
727 copy at most n chars of the source string onto the destination string
728 returning pointer to start of destination and converting any undisplayable
729 characters to harmless character equivalents.
730 ----*/
731 char *
732 istrncpy(char *d, char *s, int n)
734 char *rv = d;
735 unsigned char c;
737 if(!d || !s)
738 return(NULL);
741 if(*s && (unsigned char)(*s) < 0x80 && FILTER_THIS(*s)
742 && !(*(s+1) && *s == ESCAPE && match_escapes(s+1))){
743 if(n-- > 0){
744 c = (unsigned char) *s;
745 *d++ = c >= 0x80 ? '~' : '^';
747 if(n-- > 0){
748 s++;
749 *d = (c == 0x7f) ? '?' : (c & 0x1f) + '@';
753 else{
754 if(n-- > 0)
755 *d = *s++;
757 while(n > 0 && *d++);
759 return(rv);
763 void
764 convert_string_to_utf8(char *buf, int bufsize)
766 char *s;
767 if(strucmp("UTF-8", ps_global->display_charmap) &&
768 (s = convert_to_utf8(buf, ps_global->display_charmap, 0)) != NULL){
769 strncpy(buf, s, bufsize);
770 buf[bufsize-1] = '\0';
771 fs_give((void **)&s);
777 char *xdays[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL};
779 char *
780 month_abbrev(int month_num)
782 static char *xmonths[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
783 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL};
784 if(month_num < 1 || month_num > 12)
785 return("xxx");
786 return(xmonths[month_num - 1]);
789 char *
790 month_abbrev_locale(int month_num)
792 #ifndef DISABLE_LOCALE_DATES
793 if(F_OFF(F_DISABLE_INDEX_LOCALE_DATES, ps_global)){
794 if(month_num < 1 || month_num > 12)
795 return("xxx");
796 else{
797 static char buf[120];
798 struct tm tm;
800 memset(&tm, 0, sizeof(tm));
801 tm.tm_year = 107;
802 tm.tm_mon = month_num-1;
803 our_strftime(buf, sizeof(buf), "%b", &tm);
804 convert_string_to_utf8(buf, sizeof(buf));
807 * If it is all digits, then use the English
808 * words instead. Look for
809 * "<digit>"
810 * "<digit><digit>" or
811 * "<space><digit>"
813 if((buf[0] && !(buf[0] & 0x80)
814 && isdigit((unsigned char)buf[0]) && !buf[1])
816 (buf[0] && !(buf[0] & 0x80)
817 && (isdigit((unsigned char)buf[0]) || buf[0] == ' ')
818 && buf[1] && !(buf[1] & 0x80)
819 && isdigit((unsigned char)buf[1]) && !buf[2]))
820 return(month_abbrev(month_num));
823 * If buf[0] is a digit then assume that there should be a leading
824 * space if it leads off with a single digit.
826 if(buf[0] && !(buf[0] & 0x80) && isdigit((unsigned char) buf[0])
827 && !(buf[1] && !(buf[1] & 0x80) && isdigit((unsigned char) buf[1]))){
828 char *p;
830 /* insert space at start of buf */
831 p = buf+strlen(buf) + 1;
832 if(p > buf + sizeof(buf) - 1)
833 p = buf + sizeof(buf) - 1;
835 for(; p > buf; p--)
836 *p = *(p-1);
838 buf[0] = ' ';
841 return(buf);
844 else
845 return(month_abbrev(month_num));
846 #else /* DISABLE_LOCALE_DATES */
847 return(month_abbrev(month_num));
848 #endif /* DISABLE_LOCALE_DATES */
851 char *
852 month_name(int month_num)
854 static char *months[] = {"January", "February", "March", "April",
855 "May", "June", "July", "August", "September", "October",
856 "November", "December", NULL};
857 if(month_num < 1 || month_num > 12)
858 return("");
859 return(months[month_num - 1]);
862 char *
863 month_name_locale(int month_num)
865 #ifndef DISABLE_LOCALE_DATES
866 if(F_OFF(F_DISABLE_INDEX_LOCALE_DATES, ps_global)){
867 if(month_num < 1 || month_num > 12)
868 return("");
869 else{
870 static char buf[120];
871 struct tm tm;
873 memset(&tm, 0, sizeof(tm));
874 tm.tm_year = 107;
875 tm.tm_mon = month_num-1;
876 our_strftime(buf, sizeof(buf), "%B", &tm);
877 convert_string_to_utf8(buf, sizeof(buf));
878 return(buf);
881 else
882 return(month_name(month_num));
883 #else /* DISABLE_LOCALE_DATES */
884 return(month_name(month_num));
885 #endif /* DISABLE_LOCALE_DATES */
889 char *
890 day_abbrev(int day_of_week)
892 if(day_of_week < 0 || day_of_week > 6)
893 return("???");
894 return(xdays[day_of_week]);
897 char *
898 day_abbrev_locale(int day_of_week)
900 #ifndef DISABLE_LOCALE_DATES
901 if(F_OFF(F_DISABLE_INDEX_LOCALE_DATES, ps_global)){
902 if(day_of_week < 0 || day_of_week > 6)
903 return("???");
904 else{
905 static char buf[120];
906 struct tm tm;
908 memset(&tm, 0, sizeof(tm));
909 tm.tm_wday = day_of_week;
910 our_strftime(buf, sizeof(buf), "%a", &tm);
911 convert_string_to_utf8(buf, sizeof(buf));
912 return(buf);
915 else
916 return(day_abbrev(day_of_week));
917 #else /* DISABLE_LOCALE_DATES */
918 return(day_abbrev(day_of_week));
919 #endif /* DISABLE_LOCALE_DATES */
922 char *
923 day_name(int day_of_week)
925 static char *days[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
926 "Thursday", "Friday", "Saturday", NULL};
927 if(day_of_week < 0 || day_of_week > 6)
928 return("");
929 return(days[day_of_week]);
932 char *
933 day_name_locale(int day_of_week)
935 #ifndef DISABLE_LOCALE_DATES
936 if(F_OFF(F_DISABLE_INDEX_LOCALE_DATES, ps_global)){
937 if(day_of_week < 0 || day_of_week > 6)
938 return("");
939 else{
940 static char buf[120];
941 struct tm tm;
943 memset(&tm, 0, sizeof(tm));
944 tm.tm_wday = day_of_week;
945 our_strftime(buf, sizeof(buf), "%A", &tm);
946 convert_string_to_utf8(buf, sizeof(buf));
947 return(buf);
950 else
951 return(day_name(day_of_week));
952 #else /* DISABLE_LOCALE_DATES */
953 return(day_name(day_of_week));
954 #endif /* DISABLE_LOCALE_DATES */
958 size_t
959 our_strftime(char *dst, size_t dst_size, char *format, struct tm *tm)
961 #ifdef _WINDOWS
962 LPTSTR lptbuf, lptformat;
963 char *u;
965 lptbuf = (LPTSTR) fs_get(dst_size * sizeof(TCHAR));
966 lptbuf[0] = '\0';
967 lptformat = utf8_to_lptstr((LPSTR) format);
969 _tcsftime(lptbuf, dst_size, lptformat, tm);
970 u = lptstr_to_utf8(lptbuf);
971 if(u){
972 strncpy(dst, u, dst_size);
973 dst[dst_size-1] = '\0';
974 fs_give((void **) &u);
977 return(strlen(dst));
978 #else
979 return(strftime(dst, dst_size, format, tm));
980 #endif
984 /*----------------------------------------------------------------------
985 Return month number of month named in string
987 Args: s -- string with 3 letter month abbreviation of form mmm-yyyy
989 Result: Returns month number with January, year 1900, 2000... being 0;
990 -1 if no month/year is matched
991 ----*/
993 month_num(char *s)
995 int month = -1, year;
996 int i;
998 if(F_ON(F_PRUNE_USES_ISO,ps_global)){
999 char save, *p;
1000 char digmon[3];
1002 if(s && strlen(s) > 4 && s[4] == '-'){
1003 save = s[4];
1004 s[4] = '\0';
1005 year = atoi(s);
1006 s[4] = save;
1007 if(year == 0)
1008 return(-1);
1010 p = s + 5;
1011 for(i = 0; i < 12; i++){
1012 digmon[0] = ((i+1) < 10) ? '0' : '1';
1013 digmon[1] = '0' + (i+1) % 10;
1014 digmon[2] = '\0';
1015 if(strcmp(digmon, p) == 0)
1016 break;
1019 if(i == 12)
1020 return(-1);
1022 month = year * 12 + i;
1025 else{
1026 if(s && strlen(s) > 3 && s[3] == '-'){
1027 for(i = 0; i < 12; i++){
1028 if(struncmp(month_abbrev(i+1), s, 3) == 0)
1029 break;
1032 if(i == 12)
1033 return(-1);
1035 year = atoi(s + 4);
1036 if(year == 0)
1037 return(-1);
1039 month = year * 12 + i;
1043 return(month);
1048 * Structure containing all knowledge of symbolic time zones.
1049 * To add support for a given time zone, add it here, but make sure
1050 * the zone name is in upper case.
1052 static struct {
1053 char *zone;
1054 short len,
1055 hour_offset,
1056 min_offset;
1057 } known_zones[] = {
1058 {"PST", 3, -8, 0}, /* Pacific Standard */
1059 {"PDT", 3, -7, 0}, /* Pacific Daylight */
1060 {"MST", 3, -7, 0}, /* Mountain Standard */
1061 {"MDT", 3, -6, 0}, /* Mountain Daylight */
1062 {"CST", 3, -6, 0}, /* Central Standard */
1063 {"CDT", 3, -5, 0}, /* Central Daylight */
1064 {"EST", 3, -5, 0}, /* Eastern Standard */
1065 {"EDT", 3, -4, 0}, /* Eastern Daylight */
1066 {"JST", 3, 9, 0}, /* Japan Standard */
1067 {"GMT", 3, 0, 0}, /* Universal Time */
1068 {"UT", 2, 0, 0}, /* Universal Time */
1069 #ifdef IST_MEANS_ISREAL
1070 {"IST", 3, 2, 0}, /* Israel Standard */
1071 #else
1072 #ifdef IST_MEANS_INDIA
1073 {"IST", 3, 5, 30}, /* India Standard */
1074 #endif
1075 #endif
1076 {NULL, 0, 0},
1079 /*----------------------------------------------------------------------
1080 Parse date in or near RFC-822 format into the date structure
1082 Args: given_date -- The input string to parse
1083 d -- Pointer to a struct date to place the result in
1085 Returns nothing
1087 The following date formats are accepted:
1088 WKDAY DD MM YY HH:MM:SS ZZ
1089 DD MM YY HH:MM:SS ZZ
1090 WKDAY DD MM HH:MM:SS YY ZZ
1091 DD MM HH:MM:SS YY ZZ
1092 DD MM WKDAY HH:MM:SS YY ZZ
1093 DD MM WKDAY YY MM HH:MM:SS ZZ
1095 All leading, intervening and trailing spaces tabs and commas are ignored.
1096 The prefered formats are the first or second ones. If a field is unparsable
1097 it's value is left as -1.
1099 ----*/
1100 void
1101 parse_date(char *given_date, struct date *d)
1103 char *p, **i, *q;
1104 int month, n;
1106 d->sec = -1;
1107 d->minute= -1;
1108 d->hour = -1;
1109 d->day = -1;
1110 d->month = -1;
1111 d->year = -1;
1112 d->wkday = -1;
1113 d->hours_off_gmt = -1;
1114 d->min_off_gmt = -1;
1116 if(given_date == NULL)
1117 return;
1119 p = given_date;
1120 while(*p && isspace((unsigned char)*p))
1121 p++;
1123 /* Start with weekday? */
1124 if((q=strchr(p, ',')) != NULL){
1126 if(q - p == 3){
1127 *q = '\0';
1128 for(i = xdays; *i != NULL; i++)
1129 if(strucmp(p, *i) == 0) /* Match first 3 letters */
1130 break;
1132 *q = ',';
1134 if(*i != NULL) {
1135 /* Started with week day */
1136 d->wkday = i - xdays;
1140 p = q+1;
1141 while(*p && isspace((unsigned char)*p))
1142 p++;
1144 else if((q=strchr(p, ' ')) != NULL && q - p == 3){
1145 *q = '\0';
1146 for(i = xdays; *i != NULL; i++)
1147 if(strucmp(p, *i) == 0) /* Match first 3 letters */
1148 break;
1150 *q = ' ';
1152 if(*i != NULL) {
1153 /* Started with week day */
1154 d->wkday = i - xdays;
1155 p = q+1;
1156 while(*p && isspace((unsigned char)*p))
1157 p++;
1161 if(isdigit((unsigned char)*p)) {
1162 d->day = atoi(p);
1163 while(*p && isdigit((unsigned char)*p))
1164 p++;
1165 while(*p && (*p == '-' || *p == ',' || isspace((unsigned char)*p)))
1166 p++;
1168 for(month = 1; month <= 12; month++)
1169 if(struncmp(p, month_abbrev(month), 3) == 0)
1170 break;
1171 if(month < 13) {
1172 d->month = month;
1175 /* Move over month, (or whatever is there) */
1176 while(*p && !isspace((unsigned char)*p) && *p != ',' && *p != '-')
1177 p++;
1178 while(*p && (isspace((unsigned char)*p) || *p == ',' || *p == '-'))
1179 p++;
1181 /* Check again for day */
1182 if(isdigit((unsigned char)*p) && d->day == -1) {
1183 d->day = atoi(p);
1184 while(*p && isdigit((unsigned char)*p))
1185 p++;
1186 while(*p && (*p == '-' || *p == ',' || isspace((unsigned char)*p)))
1187 p++;
1190 /*-- Check for time --*/
1191 for(q = p; *q && isdigit((unsigned char)*q); q++);
1192 if(*q == ':') {
1193 /* It's the time (out of place) */
1194 d->hour = atoi(p);
1195 while(*p && *p != ':' && !isspace((unsigned char)*p))
1196 p++;
1197 if(*p == ':') {
1198 p++;
1199 d->minute = atoi(p);
1200 while(*p && *p != ':' && !isspace((unsigned char)*p))
1201 p++;
1202 if(*p == ':') {
1203 d->sec = atoi(p);
1204 while(*p && !isspace((unsigned char)*p))
1205 p++;
1208 while(*p && isspace((unsigned char)*p))
1209 p++;
1213 /* Get the year 0-49 is 2000-2049; 50-100 is 1950-1999 and
1214 101-9999 is 101-9999 */
1215 if(isdigit((unsigned char)*p)) {
1216 d->year = atoi(p);
1217 if(d->year < 50)
1218 d->year += 2000;
1219 else if(d->year < 100)
1220 d->year += 1900;
1221 while(*p && isdigit((unsigned char)*p))
1222 p++;
1223 while(*p && (*p == '-' || *p == ',' || isspace((unsigned char)*p)))
1224 p++;
1225 } else {
1226 /* Something wierd, skip it and try to resynch */
1227 while(*p && !isspace((unsigned char)*p) && *p != ',' && *p != '-')
1228 p++;
1229 while(*p && (isspace((unsigned char)*p) || *p == ',' || *p == '-'))
1230 p++;
1233 /*-- Now get hours minutes, seconds and ignore tenths --*/
1234 for(q = p; *q && isdigit((unsigned char)*q); q++);
1235 if(*q == ':' && d->hour == -1) {
1236 d->hour = atoi(p);
1237 while(*p && *p != ':' && !isspace((unsigned char)*p))
1238 p++;
1239 if(*p == ':') {
1240 p++;
1241 d->minute = atoi(p);
1242 while(*p && *p != ':' && !isspace((unsigned char)*p))
1243 p++;
1244 if(*p == ':') {
1245 p++;
1246 d->sec = atoi(p);
1247 while(*p && !isspace((unsigned char)*p))
1248 p++;
1252 while(*p && isspace((unsigned char)*p))
1253 p++;
1256 /*-- The time zone --*/
1257 d->hours_off_gmt = 0;
1258 d->min_off_gmt = 0;
1259 if(*p) {
1260 if((*p == '+' || *p == '-')
1261 && isdigit((unsigned char)p[1])
1262 && isdigit((unsigned char)p[2])
1263 && isdigit((unsigned char)p[3])
1264 && isdigit((unsigned char)p[4])
1265 && !isdigit((unsigned char)p[5])) {
1266 char tmp[3];
1267 d->min_off_gmt = d->hours_off_gmt = (*p == '+' ? 1 : -1);
1268 p++;
1269 tmp[0] = *p++;
1270 tmp[1] = *p++;
1271 tmp[2] = '\0';
1272 d->hours_off_gmt *= atoi(tmp);
1273 tmp[0] = *p++;
1274 tmp[1] = *p++;
1275 tmp[2] = '\0';
1276 d->min_off_gmt *= atoi(tmp);
1277 } else {
1278 for(n = 0; known_zones[n].zone; n++)
1279 if(struncmp(p, known_zones[n].zone, known_zones[n].len) == 0){
1280 d->hours_off_gmt = (int) known_zones[n].hour_offset;
1281 d->min_off_gmt = (int) known_zones[n].min_offset;
1282 break;
1287 if(d->wkday == -1){
1288 MESSAGECACHE elt;
1289 struct tm *tm;
1290 time_t t;
1293 * Not sure why we aren't just using this from the gitgo, but
1294 * since not sure will just use it to repair wkday.
1296 if(mail_parse_date(&elt, (unsigned char *) given_date)){
1297 t = mail_longdate(&elt);
1298 tm = localtime(&t);
1300 if(tm)
1301 d->wkday = tm->tm_wday;
1307 char *
1308 convert_date_to_local(char *date)
1310 struct tm *tm;
1311 time_t ltime;
1312 static char datebuf[26];
1314 ltime = date_to_local_time_t(date);
1315 if(ltime == (time_t) -1)
1316 return(date);
1318 tm = localtime(&ltime);
1320 if(tm == NULL)
1321 return(date);
1323 snprintf(datebuf, sizeof(datebuf), "%.3s, %d %.3s %d %02d:%02d:%02d",
1324 day_abbrev(tm->tm_wday), tm->tm_mday, month_abbrev(tm->tm_mon+1),
1325 tm->tm_year+1900, tm->tm_hour, tm->tm_min, tm->tm_sec);
1327 return(datebuf);
1331 time_t
1332 date_to_local_time_t(char *date)
1334 time_t ourtime;
1335 struct tm theirtime;
1336 struct date d;
1337 static int zone = 1000000; /* initialize timezone offset */
1338 static int dst;
1340 if(zone == 1000000){
1341 int julian;
1342 struct tm *tm;
1343 time_t now;
1345 zone = 0;
1346 /* find difference between gmtime and localtime, from c-client do_date */
1347 now = time((time_t *) 0);
1348 if(now != (time_t) -1){
1349 tm = gmtime(&now);
1350 if(tm != NULL){
1351 zone = tm->tm_hour * 60 + tm->tm_min; /* minutes */
1352 julian = tm->tm_yday;
1354 tm = localtime(&now);
1355 dst = tm->tm_isdst; /* for converting back to our time */
1357 zone = tm->tm_hour * 60 + tm->tm_min - zone;
1358 if((julian = tm->tm_yday - julian) != 0)
1359 zone += ((julian < 0) == (abs(julian) == 1)) ? -24*60 : 24*60;
1361 zone *= 60; /* change to seconds */
1366 parse_date(date, &d);
1368 /* put d into struct tm so we can use mktime */
1369 memset(&theirtime, 0, sizeof(theirtime));
1370 theirtime.tm_year = d.year - 1900;
1371 theirtime.tm_mon = d.month - 1;
1372 theirtime.tm_mday = d.day;
1373 theirtime.tm_hour = d.hour - d.hours_off_gmt;
1374 theirtime.tm_min = d.minute - d.min_off_gmt;
1375 theirtime.tm_sec = d.sec;
1377 theirtime.tm_isdst = dst;
1379 ourtime = mktime(&theirtime); /* still theirtime, actually */
1381 /* convert to the time we want to show */
1382 if(ourtime != (time_t) -1)
1383 ourtime += zone;
1385 return(ourtime);
1389 /*----------------------------------------------------------------------
1390 Create a little string of blanks of the specified length.
1391 Max n is MAX_SCREEN_COLS. Can use up to e repeat_char results at once.
1392 ----*/
1393 char *
1394 repeat_char(int n, int c)
1396 static char bb[3][MAX_SCREEN_COLS+1];
1397 static int whichbb = 0;
1398 char *b;
1400 whichbb = (whichbb + 1) % 3;
1401 b = bb[whichbb];
1403 if(n > sizeof(bb[0]))
1404 n = sizeof(bb[0]) - 1;
1406 bb[whichbb][n--] = '\0';
1407 while(n >= 0)
1408 bb[whichbb][n--] = c;
1410 return(bb[whichbb]);
1414 /*----------------------------------------------------------------------
1415 Format number as amount of bytes, appending Kb, Mb, Gb, bytes
1417 Args: bytes -- number of bytes to format
1419 Returns pointer to static string. The numbers are divided to produce a
1420 nice string with precision of about 2-4 digits
1421 ----*/
1422 char *
1423 byte_string(long int bytes)
1425 char *a, aa[5];
1426 char *abbrevs = "GMK";
1427 long i, ones, tenths;
1428 static char string[10];
1430 ones = 0L;
1431 tenths = 0L;
1433 if(bytes == 0L){
1434 strncpy(string, "0 bytes", sizeof(string));
1435 string[sizeof(string)-1] = '\0';
1437 else {
1438 for(a = abbrevs, i = 1000000000; i >= 1; i /= 1000, a++) {
1439 if(bytes > i) {
1440 ones = bytes/i;
1441 if(ones < 10L && i > 10L)
1442 tenths = (bytes - (ones * i)) / (i / 10L);
1443 break;
1447 aa[0] = *a; aa[1] = '\0';
1449 if(tenths == 0)
1450 snprintf(string, sizeof(string), "%ld%s%s", ones, aa, *a ? "B" : "bytes");
1451 else
1452 snprintf(string, sizeof(string), "%ld.%ld%s%s", ones, tenths, aa, *a ? "B" : "bytes");
1455 return(string);
1460 /*----------------------------------------------------------------------
1461 Print a string corresponding to the number given:
1462 1st, 2nd, 3rd, 105th, 92342nd....
1463 ----*/
1465 char *
1466 enth_string(int i)
1468 static char enth[10];
1470 enth[0] = '\0';
1472 switch (i % 10) {
1474 case 1:
1475 if( (i % 100 ) == 11)
1476 snprintf(enth, sizeof(enth),"%dth", i);
1477 else
1478 snprintf(enth, sizeof(enth),"%dst", i);
1479 break;
1481 case 2:
1482 if ((i % 100) == 12)
1483 snprintf(enth, sizeof(enth), "%dth",i);
1484 else
1485 snprintf(enth, sizeof(enth), "%dnd",i);
1486 break;
1488 case 3:
1489 if(( i % 100) == 13)
1490 snprintf(enth, sizeof(enth), "%dth",i);
1491 else
1492 snprintf(enth, sizeof(enth), "%drd",i);
1493 break;
1495 default:
1496 snprintf(enth, sizeof(enth),"%dth",i);
1497 break;
1499 return(enth);
1504 * Inserts newlines for folding at whitespace.
1506 * Args src -- The source text.
1507 * width -- Approximately where the fold should happen.
1508 * maxwidth -- Maximum width we want to fold at.
1509 * first_indent -- String to use as indent on first line.
1510 * indent -- String to use as indent for subsequent folded lines.
1511 * flags -- FLD_CRLF End of line is \r\n instead of \n.
1512 * FLD_PWS PreserveWhiteSpace when folding. This is
1513 * for vcard folding where CRLF SPACE is
1514 * removed when unfolding, so we need to
1515 * leave the space in. With rfc2822 unfolding
1516 * only the CRLF is removed when unfolding.
1518 * Returns An allocated string which caller should free.
1520 char *
1521 fold(char *src, int width, int maxwidth, char *first_indent, char *indent, unsigned int flags)
1523 char *next_piece, *res, *p;
1524 int i, len = 0, starting_point, winner, eol, this_width;
1525 int indent1 = 0, /* width of first_indent */
1526 indent2 = 0, /* width of indent */
1527 nbindent2 = 0, /* number of bytes in indent */
1528 nb = 0; /* number of bytes needed */
1529 int cr, preserve_ws;
1530 char save_char;
1531 char *endptr = NULL;
1532 unsigned shorter, longer;
1533 unsigned got_width;
1535 cr = (flags & FLD_CRLF);
1536 preserve_ws = (flags & FLD_PWS);
1538 if(indent){
1539 indent2 = (int) utf8_width(indent);
1540 nbindent2 = strlen(indent);
1543 if(first_indent){
1544 indent1 = (int) utf8_width(first_indent);
1545 nb = strlen(first_indent);
1548 len = indent1;
1549 next_piece = src;
1550 eol = cr ? 2 : 1;
1551 if(!src || !*src)
1552 nb += eol;
1555 * We can't tell how much space is going to be needed without actually
1556 * passing through the data to see.
1558 while(next_piece && *next_piece){
1559 if(next_piece != src && indent2){
1560 len += indent2;
1561 nb += nbindent2;
1564 this_width = (int) utf8_width(next_piece);
1565 if(this_width + len <= width){
1566 nb += (strlen(next_piece) + eol);
1567 break;
1569 else{ /* fold it */
1570 starting_point = width - len; /* space left on this line */
1571 /* find a good folding spot */
1572 winner = -1;
1573 for(i = 0;
1574 winner == -1 && (starting_point - i > 5 || i < maxwidth - width);
1575 i++){
1577 if((shorter=starting_point-i) > 5){
1578 endptr = utf8_count_forw_width(next_piece, shorter, &got_width);
1579 if(endptr && got_width == shorter && isspace((unsigned char) *endptr))
1580 winner = (int) shorter;
1583 if(winner == -1
1584 && (longer=starting_point+i) && i < maxwidth - width){
1585 endptr = utf8_count_forw_width(next_piece, longer, &got_width);
1586 if(endptr && got_width == longer && isspace((unsigned char) *endptr))
1587 winner = (int) longer;
1591 if(winner == -1){ /* if no good folding spot, fold at width */
1592 winner = starting_point;
1593 endptr = NULL;
1596 if(endptr == NULL){
1597 endptr = utf8_count_forw_width(next_piece, (unsigned) winner, &got_width);
1598 winner = (int) got_width;
1601 nb += ((endptr - next_piece) + eol);
1602 next_piece = endptr;
1603 if(!preserve_ws && isspace((unsigned char) next_piece[0]))
1604 next_piece++;
1607 len = 0;
1610 res = (char *) fs_get((nb+1) * sizeof(char));
1611 p = res;
1612 sstrncpy(&p, first_indent, nb+1-(p-res));
1613 len = indent1;
1614 next_piece = src;
1616 while(next_piece && *next_piece){
1617 if(next_piece != src && indent2){
1618 sstrncpy(&p, indent, nb+1-(p-res));
1619 len += indent2;
1622 this_width = (int) utf8_width(next_piece);
1623 if(this_width + len <= width){
1624 sstrncpy(&p, next_piece, nb+1-(p-res));
1625 if(cr && p-res < nb+1)
1626 *p++ = '\r';
1628 if(p-res < nb+1)
1629 *p++ = '\n';
1631 break;
1633 else{ /* fold it */
1634 starting_point = width - len; /* space left on this line */
1635 /* find a good folding spot */
1636 winner = -1;
1637 for(i = 0;
1638 winner == -1 && (starting_point - i > 5 || i < maxwidth - width);
1639 i++){
1641 if((shorter=starting_point-i) > 5){
1642 endptr = utf8_count_forw_width(next_piece, shorter, &got_width);
1643 if(endptr && got_width == shorter && isspace((unsigned char) *endptr))
1644 winner = (int) shorter;
1647 if(winner == -1
1648 && (longer=starting_point+i) && i < maxwidth - width){
1649 endptr = utf8_count_forw_width(next_piece, longer, &got_width);
1650 if(endptr && got_width == longer && isspace((unsigned char) *endptr))
1651 winner = (int) longer;
1655 if(winner == -1){ /* if no good folding spot, fold at width */
1656 winner = starting_point;
1657 endptr = NULL;
1660 if(endptr == NULL){
1661 endptr = utf8_count_forw_width(next_piece, (unsigned) winner, &got_width);
1662 winner = (int) got_width;
1665 if(endptr){
1666 save_char = *endptr;
1667 *endptr = '\0';
1668 sstrncpy(&p, next_piece, nb+1-(p-res));
1669 *endptr = save_char;
1670 next_piece = endptr;
1673 if(cr && p-res < nb+1)
1674 *p++ = '\r';
1676 if(p-res < nb+1)
1677 *p++ = '\n';
1679 if(!preserve_ws && isspace((unsigned char) next_piece[0]))
1680 next_piece++;
1683 len = 0;
1686 if(!src || !*src){
1687 if(cr && p-res < nb+1)
1688 *p++ = '\r';
1690 if(p-res < nb+1)
1691 *p++ = '\n';
1694 if(p-res < nb+1)
1695 *p = '\0';
1697 res[nb] = '\0';
1699 return(res);
1704 * strsquish - fancifies a string into the given buffer if it's too
1705 * long to fit in the given width
1707 char *
1708 strsquish(char *buf, size_t buflen, char *src, int width)
1711 * Replace strsquish() with calls to short_str().
1713 if(width > 14)
1714 return(short_str(src, buf, buflen, width, MidDots));
1715 else
1716 return(short_str(src, buf, buflen, width, FrontDots));
1720 char *
1721 long2string(long int l)
1723 static char string[20];
1725 snprintf(string, sizeof(string), "%ld", l);
1726 return(string);
1730 char *
1731 ulong2string(unsigned long int l)
1733 static char string[20];
1735 snprintf(string, sizeof(string), "%lu", l);
1736 return(string);
1740 char *
1741 int2string(int i)
1743 static char string[20];
1745 snprintf(string, sizeof(string), "%d", i);
1746 return(string);
1751 * strtoval - convert the given string to a positive integer.
1753 char *
1754 strtoval(char *s, int *val, int minmum, int maxmum, int otherok, char *errbuf,
1755 size_t errbuflen, char *varname)
1757 int i = 0, neg = 1;
1758 char *p = s, *errstr = NULL;
1760 removing_leading_and_trailing_white_space(p);
1761 for(; *p; p++)
1762 if(isdigit((unsigned char) *p)){
1763 i = (i * 10) + (*p - '0');
1765 else if(*p == '-' && i == 0){
1766 neg = -1;
1768 else{
1769 snprintf(errstr = errbuf, errbuflen,
1770 "Non-numeric value ('%c' in \"%.8s\") in %s. Using \"%d\"",
1771 *p, s, varname, *val);
1772 return(errbuf);
1775 i *= neg;
1777 /* range describes acceptable values */
1778 if(maxmum > minmum && (i < minmum || i > maxmum) && i != otherok)
1779 snprintf(errstr = errbuf, errbuflen,
1780 "%s of %d not supported (M%s %d). Using \"%d\"",
1781 varname, i, (i > maxmum) ? "ax" : "in",
1782 (i > maxmum) ? maxmum : minmum, *val);
1783 /* range describes unacceptable values */
1784 else if(minmum > maxmum && !(i < maxmum || i > minmum))
1785 snprintf(errstr = errbuf, errbuflen, "%s of %d not supported. Using \"%d\"",
1786 varname, i, *val);
1787 else
1788 *val = i;
1790 return(errstr);
1795 * strtolval - convert the given string to a positive _long_ integer.
1797 char *
1798 strtolval(char *s, long int *val, long int minmum, long int maxmum, long int otherok,
1799 char *errbuf, size_t errbuflen, char *varname)
1801 long i = 0, neg = 1L;
1802 char *p = s, *errstr = NULL;
1804 removing_leading_and_trailing_white_space(p);
1805 for(; *p; p++)
1806 if(isdigit((unsigned char) *p)){
1807 i = (i * 10L) + (*p - '0');
1809 else if(*p == '-' && i == 0L){
1810 neg = -1L;
1812 else{
1813 snprintf(errstr = errbuf, errbuflen,
1814 "Non-numeric value ('%c' in \"%.8s\") in %s. Using \"%ld\"",
1815 *p, s, varname, *val);
1816 return(errbuf);
1819 i *= neg;
1821 /* range describes acceptable values */
1822 if(maxmum > minmum && (i < minmum || i > maxmum) && i != otherok)
1823 snprintf(errstr = errbuf, errbuflen,
1824 "%s of %ld not supported (M%s %ld). Using \"%ld\"",
1825 varname, i, (i > maxmum) ? "ax" : "in",
1826 (i > maxmum) ? maxmum : minmum, *val);
1827 /* range describes unacceptable values */
1828 else if(minmum > maxmum && !(i < maxmum || i > minmum))
1829 snprintf(errstr = errbuf, errbuflen, "%s of %ld not supported. Using \"%ld\"",
1830 varname, i, *val);
1831 else
1832 *val = i;
1834 return(errstr);
1839 * Function to parse the given string into two space-delimited fields
1840 * Quotes may be used to surround labels or values with spaces in them.
1841 * Backslash negates the special meaning of a quote.
1842 * Unescaping of backslashes only happens if the pair member is quoted,
1843 * this provides for backwards compatibility.
1845 * Args -- string -- the source string
1846 * label -- the first half of the string, a return value
1847 * value -- the last half of the string, a return value
1848 * firstws -- if set, the halves are delimited by the first unquoted
1849 * whitespace, else by the last unquoted whitespace
1850 * strip_internal_label_quotes -- unescaped quotes in the middle of the label
1851 * are removed. This is useful for vars
1852 * like display-filters and url-viewers
1853 * which may require quoting of an arg
1854 * inside of a _TOKEN_.
1856 void
1857 get_pair(char *string, char **label, char **value, int firstws, int strip_internal_label_quotes)
1859 char *p, *q, *tmp, *token = NULL;
1860 int quoted = 0;
1862 *label = *value = NULL;
1865 * This for loop just finds the beginning of the value. If firstws
1866 * is set, then it begins after the first whitespace. Otherwise, it begins
1867 * after the last whitespace. Quoted whitespace doesn't count as
1868 * whitespace. If there is no unquoted whitespace, then there is no
1869 * label, there's just a value.
1871 for(p = string; p && *p;){
1872 if(*p == '"') /* quoted label? */
1873 quoted = (quoted) ? 0 : 1;
1875 if(*p == '\\' && *(p+1) == '"') /* escaped quote? */
1876 p++; /* skip it... */
1878 if(isspace((unsigned char)*p) && !quoted){ /* if space, */
1879 while(*++p && isspace((unsigned char)*p)) /* move past it */
1882 if(!firstws || !token)
1883 token = p; /* remember start of text */
1885 else
1886 p++;
1889 if(token){ /* copy label */
1890 *label = p = (char *)fs_get(((token - string) + 1) * sizeof(char));
1892 /* make a copy of the string */
1893 tmp = (char *)fs_get(((token - string) + 1) * sizeof(char));
1894 strncpy(tmp, string, token - string);
1895 tmp[token-string] = '\0';
1897 removing_leading_and_trailing_white_space(tmp);
1898 quoted = removing_double_quotes(tmp);
1900 for(q = tmp; *q; q++){
1901 if(quoted && *q == '\\' && (*(q+1) == '"' || *(q+1) == '\\'))
1902 *p++ = *++q;
1903 else if(!(strip_internal_label_quotes && *q == '"'))
1904 *p++ = *q;
1907 *p = '\0'; /* tie off label */
1908 fs_give((void **)&tmp);
1909 if(*label == '\0')
1910 fs_give((void **)label);
1912 else
1913 token = string;
1915 if(token){ /* copy value */
1916 *value = p = (char *)fs_get((strlen(token) + 1) * sizeof(char));
1918 tmp = cpystr(token);
1919 removing_leading_and_trailing_white_space(tmp);
1920 quoted = removing_double_quotes(tmp);
1922 for(q = tmp; *q ; q++){
1923 if(quoted && *q == '\\' && (*(q+1) == '"' || *(q+1) == '\\'))
1924 *p++ = *++q;
1925 else
1926 *p++ = *q;
1929 *p = '\0'; /* tie off value */
1930 fs_give((void **)&tmp);
1936 * This is sort of the inverse of get_pair.
1938 * Args -- label -- the first half of the string
1939 * value -- the last half of the string
1941 * Returns -- an allocated string which is "label" SPACE "value"
1943 * Label and value are quoted separately. If quoting is needed (they contain
1944 * whitespace) then backslash escaping is done inside the quotes for
1945 * " and for \. If quoting is not needed, no escaping is done.
1947 char *
1948 put_pair(char *label, char *value)
1950 char *result, *lab = label, *val = value;
1951 size_t l;
1953 if(label && *label)
1954 lab = quote_if_needed(label);
1956 if(value && *value)
1957 val = quote_if_needed(value);
1959 l = strlen(lab) + strlen(val) +1;
1960 result = (char *) fs_get((l+1) * sizeof(char));
1962 snprintf(result, l+1, "%s%s%s",
1963 lab ? lab : "",
1964 (lab && lab[0] && val && val[0]) ? " " : "",
1965 val ? val : "");
1967 if(lab && lab != label)
1968 fs_give((void **)&lab);
1969 if(val && val != value)
1970 fs_give((void **)&val);
1972 return(result);
1977 * This is for put_pair type uses. It returns either an allocated
1978 * string which is the quoted src string or it returns a pointer to
1979 * the src string if no quoting is needed.
1981 char *
1982 quote_if_needed(char *src)
1984 char *result = src, *qsrc = NULL;
1986 if(src && *src){
1987 /* need quoting? */
1988 if(strpbrk(src, " \t") != NULL)
1989 qsrc = add_escapes(src, "\\\"", '\\', "", "");
1991 if(qsrc && !*qsrc)
1992 fs_give((void **)&qsrc);
1994 if(qsrc){
1995 size_t l;
1997 l = strlen(qsrc)+2;
1998 result = (char *) fs_get((l+1) * sizeof(char));
1999 snprintf(result, l+1, "\"%s\"", qsrc);
2000 fs_give((void **)&qsrc);
2004 return(result);
2009 * Convert a 1, 2, or 3-digit octal string into an 8-bit character.
2010 * Only the first three characters of s will be used, and it is ok not
2011 * to null-terminate it.
2014 read_octal(char **s)
2016 register int i, j;
2018 i = 0;
2019 for(j = 0; j < 3 && **s >= '0' && **s < '8' ; (*s)++, j++)
2020 i = (i * 8) + (int)(unsigned char)**s - '0';
2022 return(i);
2027 * Convert two consecutive HEX digits to an integer. First two
2028 * chars pointed to by "s" MUST already be tested for hexness.
2031 read_hex(char *s)
2033 return(X2C(s));
2038 * Given a character c, put the 3-digit ascii octal value of that char
2039 * in the 2nd argument, which must be at least 3 in length.
2041 void
2042 char_to_octal_triple(int c, char *octal)
2044 c &= 0xff;
2046 octal[2] = (c % 8) + '0';
2047 c /= 8;
2048 octal[1] = (c % 8) + '0';
2049 c /= 8;
2050 octal[0] = c + '0';
2055 * Convert in memory string s to a C-style string, with backslash escapes
2056 * like they're used in C character constants.
2057 * Also convert leading spaces because read_pinerc deletes those
2058 * if not quoted.
2060 * Returns allocated C string version of s.
2062 char *
2063 string_to_cstring(char *s)
2065 char *b, *p;
2066 int n, i, all_space_so_far = 1;
2068 if(!s)
2069 return(cpystr(""));
2071 n = 20;
2072 b = (char *)fs_get((n+1) * sizeof(char));
2073 p = b;
2074 *p = '\0';
2075 i = 0;
2077 while(*s){
2078 if(*s != SPACE)
2079 all_space_so_far = 0;
2081 if(i + 4 > n){
2083 * The output string may overflow the output buffer.
2084 * Make more room.
2086 n += 20;
2087 fs_resize((void **)&b, (n+1) * sizeof(char));
2088 p = &b[i];
2090 else{
2091 switch(*s){
2092 case '\n':
2093 *p++ = '\\';
2094 *p++ = 'n';
2095 i += 2;
2096 break;
2098 case '\r':
2099 *p++ = '\\';
2100 *p++ = 'r';
2101 i += 2;
2102 break;
2104 case '\t':
2105 *p++ = '\\';
2106 *p++ = 't';
2107 i += 2;
2108 break;
2110 case '\b':
2111 *p++ = '\\';
2112 *p++ = 'b';
2113 i += 2;
2114 break;
2116 case '\f':
2117 *p++ = '\\';
2118 *p++ = 'f';
2119 i += 2;
2120 break;
2122 case '\\':
2123 *p++ = '\\';
2124 *p++ = '\\';
2125 i += 2;
2126 break;
2128 case SPACE:
2129 if(all_space_so_far){ /* use octal output */
2130 *p++ = '\\';
2131 char_to_octal_triple(*s, p);
2132 p += 3;
2133 i += 4;
2134 break;
2136 else{
2137 /* fall through */
2141 default:
2142 if(*s >= SPACE && *s < '~' && *s != '\"' && *s != '$'){
2143 *p++ = *s;
2144 i++;
2146 else{ /* use octal output */
2147 *p++ = '\\';
2148 char_to_octal_triple(*s, p);
2149 p += 3;
2150 i += 4;
2153 break;
2156 s++;
2160 *p = '\0';
2161 return(b);
2166 * Convert C-style string, with backslash escapes, into a hex string, two
2167 * hex digits per character.
2169 * Returns allocated hexstring version of s.
2171 char *
2172 cstring_to_hexstring(char *s)
2174 char *b, *p;
2175 int n, i, c;
2177 if(!s)
2178 return(cpystr(""));
2180 n = 20;
2181 b = (char *)fs_get((n+1) * sizeof(char));
2182 p = b;
2183 *p = '\0';
2184 i = 0;
2186 while(*s){
2187 if(i + 2 > n){
2189 * The output string may overflow the output buffer.
2190 * Make more room.
2192 n += 20;
2193 fs_resize((void **)&b, (n+1) * sizeof(char));
2194 p = &b[i];
2196 else{
2197 if(*s == '\\'){
2198 s++;
2199 switch(*s){
2200 case 'n':
2201 c = '\n';
2202 C2XPAIR(c, p);
2203 i += 2;
2204 s++;
2205 break;
2207 case 'r':
2208 c = '\r';
2209 C2XPAIR(c, p);
2210 i += 2;
2211 s++;
2212 break;
2214 case 't':
2215 c = '\t';
2216 C2XPAIR(c, p);
2217 i += 2;
2218 s++;
2219 break;
2221 case 'v':
2222 c = '\v';
2223 C2XPAIR(c, p);
2224 i += 2;
2225 s++;
2226 break;
2228 case 'b':
2229 c = '\b';
2230 C2XPAIR(c, p);
2231 i += 2;
2232 s++;
2233 break;
2235 case 'f':
2236 c = '\f';
2237 C2XPAIR(c, p);
2238 i += 2;
2239 s++;
2240 break;
2242 case 'a':
2243 c = '\007';
2244 C2XPAIR(c, p);
2245 i += 2;
2246 s++;
2247 break;
2249 case '\\':
2250 c = '\\';
2251 C2XPAIR(c, p);
2252 i += 2;
2253 s++;
2254 break;
2256 case '?':
2257 c = '?';
2258 C2XPAIR(c, p);
2259 i += 2;
2260 s++;
2261 break;
2263 case '\'':
2264 c = '\'';
2265 C2XPAIR(c, p);
2266 i += 2;
2267 s++;
2268 break;
2270 case '\"':
2271 c = '\"';
2272 C2XPAIR(c, p);
2273 i += 2;
2274 s++;
2275 break;
2277 case 0: /* reached end of s too early */
2278 c = 0;
2279 C2XPAIR(c, p);
2280 i += 2;
2281 s++;
2282 break;
2284 /* hex number */
2285 case 'x':
2286 s++;
2287 if(isxpair(s)){
2288 c = X2C(s);
2289 s += 2;
2291 else if(isxdigit((unsigned char)*s)){
2292 c = XDIGIT2C(*s);
2293 s++;
2295 else
2296 c = 0;
2298 C2XPAIR(c, p);
2299 i += 2;
2301 break;
2303 /* octal number */
2304 default:
2305 c = read_octal(&s);
2306 C2XPAIR(c, p);
2307 i += 2;
2309 break;
2312 else{
2313 C2XPAIR(*s, p);
2314 i += 2;
2315 s++;
2320 *p = '\0';
2321 return(b);
2326 * Convert C-style string, with backslash escapes, into a regular string.
2327 * Result goes in dst, which should be as big as src.
2330 void
2331 cstring_to_string(char *src, char *dst)
2333 char *p;
2334 int c;
2336 dst[0] = '\0';
2337 if(!src)
2338 return;
2340 p = dst;
2342 while(*src){
2343 if(*src == '\\'){
2344 src++;
2345 switch(*src){
2346 case 'n':
2347 *p++ = '\n';
2348 src++;
2349 break;
2351 case 'r':
2352 *p++ = '\r';
2353 src++;
2354 break;
2356 case 't':
2357 *p++ = '\t';
2358 src++;
2359 break;
2361 case 'v':
2362 *p++ = '\v';
2363 src++;
2364 break;
2366 case 'b':
2367 *p++ = '\b';
2368 src++;
2369 break;
2371 case 'f':
2372 *p++ = '\f';
2373 src++;
2374 break;
2376 case 'a':
2377 *p++ = '\007';
2378 src++;
2379 break;
2381 case '\\':
2382 *p++ = '\\';
2383 src++;
2384 break;
2386 case '?':
2387 *p++ = '?';
2388 src++;
2389 break;
2391 case '\'':
2392 *p++ = '\'';
2393 src++;
2394 break;
2396 case '\"':
2397 *p++ = '\"';
2398 src++;
2399 break;
2401 case 0: /* reached end of s too early */
2402 src++;
2403 break;
2405 /* hex number */
2406 case 'x':
2407 src++;
2408 if(isxpair(src)){
2409 c = X2C(src);
2410 src += 2;
2412 else if(isxdigit((unsigned char)*src)){
2413 c = XDIGIT2C(*src);
2414 src++;
2416 else
2417 c = 0;
2419 *p++ = c;
2421 break;
2423 /* octal number */
2424 default:
2425 c = read_octal(&src);
2426 *p++ = c;
2427 break;
2430 else
2431 *p++ = *src++;
2434 *p = '\0';
2439 * Quotes /'s and \'s with \
2441 * Args: src -- The source string.
2443 * Returns: A string with backslash quoting added. Any / in the string is
2444 * replaced with \/ and any \ is replaced with \\, and any
2445 * " is replaced with \".
2447 * The caller is responsible for freeing the memory allocated for the answer.
2449 char *
2450 add_backslash_escapes(char *src)
2452 return(add_escapes(src, "/\\\"", '\\', "", ""));
2457 * Undoes backslash quoting of source string.
2459 * Args: src -- The source string.
2461 * Returns: A string with backslash quoting removed or NULL. The string starts
2462 * at src and goes until the end of src or until a / is reached. The
2463 * / is not included in the string. /'s may be quoted by preceding
2464 * them with a backslash (\) and \'s may also be quoted by
2465 * preceding them with a \. In fact, \ quotes any character.
2466 * Not quite, \nnn is octal escape, \xXX is hex escape.
2468 * The caller is responsible for freeing the memory allocated for the answer.
2470 char *
2471 remove_backslash_escapes(char *src)
2473 char *ans = NULL, *q, *p;
2474 int done = 0;
2476 if(src){
2477 p = q = (char *)fs_get(strlen(src) + 1);
2479 while(!done){
2480 switch(*src){
2481 case '\\':
2482 src++;
2483 if(*src){
2484 if(isdigit((unsigned char)*src))
2485 *p++ = (char)read_octal(&src);
2486 else if((*src == 'x' || *src == 'X') &&
2487 *(src+1) && *(src+2) && isxpair(src+1)){
2488 *p++ = (char)read_hex(src+1);
2489 src += 3;
2491 else
2492 *p++ = *src++;
2495 break;
2497 case '\0':
2498 case '/':
2499 done++;
2500 break;
2502 default:
2503 *p++ = *src++;
2504 break;
2508 *p = '\0';
2510 ans = cpystr(q);
2511 fs_give((void **)&q);
2514 return(ans);
2519 * Quote values for viewer-hdr-colors. We quote backslash, comma, and slash.
2520 * Also replaces $ with $$.
2522 * Args: src -- The source string.
2524 * Returns: A string with backslash quoting added.
2526 * The caller is responsible for freeing the memory allocated for the answer.
2528 char *
2529 add_viewerhdr_escapes(char *src)
2531 char *tmp, *ans = NULL;
2533 tmp = add_escapes(src, "/\\", '\\', ",", "");
2535 if(tmp){
2536 ans = dollar_escape_dollars(tmp);
2537 fs_give((void **) &tmp);
2540 return(ans);
2545 * Quote dollar sign by preceding it with another dollar sign. We use $$
2546 * instead of \$ so that it will work for both PC-Pine and unix.
2548 * Args: src -- The source string.
2550 * Returns: A string with $$ quoting added.
2552 * The caller is responsible for freeing the memory allocated for the answer.
2554 char *
2555 dollar_escape_dollars(char *src)
2557 return(add_escapes(src, "$", '$', "", ""));
2562 * This adds the quoting for vcard backslash quoting.
2563 * That is, commas are backslashed, backslashes are backslashed,
2564 * semicolons are backslashed, and CRLFs are \n'd.
2565 * This is thought to be correct for draft-ietf-asid-mime-vcard-06.txt, Apr 98.
2567 char *
2568 vcard_escape(char *src)
2570 char *p, *q;
2572 q = add_escapes(src, ";,\\", '\\', "", "");
2573 if(q){
2574 /* now do CRLF -> \n in place */
2575 for(p = q; *p != '\0'; p++)
2576 if(*p == '\r' && *(p+1) == '\n'){
2577 *p++ = '\\';
2578 *p = 'n';
2582 return(q);
2587 * This undoes the vcard backslash quoting.
2589 * In particular, it turns \n into newline, \, into ',', \\ into \, \; -> ;.
2590 * In fact, \<anything_else> is also turned into <anything_else>. The ID
2591 * isn't clear on this.
2593 * The caller is responsible for freeing the memory allocated for the answer.
2595 char *
2596 vcard_unescape(char *src)
2598 char *ans = NULL, *p;
2599 int done = 0;
2601 if(src){
2602 p = ans = (char *)fs_get(strlen(src) + 1);
2604 while(!done){
2605 switch(*src){
2606 case '\\':
2607 src++;
2608 if(*src == 'n' || *src == 'N'){
2609 *p++ = '\n';
2610 src++;
2612 else if(*src)
2613 *p++ = *src++;
2615 break;
2617 case '\0':
2618 done++;
2619 break;
2621 default:
2622 *p++ = *src++;
2623 break;
2627 *p = '\0';
2630 return(ans);
2635 * Turn folded lines into long lines in place.
2637 * CRLF whitespace sequences are removed, the space is not preserved.
2639 void
2640 vcard_unfold(char *string)
2642 char *p = string;
2644 while(*string) /* while something to copy */
2645 if(*string == '\r' &&
2646 *(string+1) == '\n' &&
2647 (*(string+2) == SPACE || *(string+2) == TAB))
2648 string += 3;
2649 else
2650 *p++ = *string++;
2652 *p = '\0';
2657 * Quote specified chars with escape char.
2659 * Args: src -- The source string.
2660 * quote_these_chars -- Array of chars to quote
2661 * quoting_char -- The quoting char to be used (e.g., \)
2662 * hex_these_chars -- Array of chars to hex escape
2663 * hex_these_quoted_chars -- Array of chars to hex escape if they are
2664 * already quoted with quoting_char (that is,
2665 * turn \, into hex comma)
2667 * Returns: An allocated copy of string with quoting added.
2668 * The caller is responsible for freeing the memory allocated for the answer.
2670 char *
2671 add_escapes(char *src, char *quote_these_chars, int quoting_char,
2672 char *hex_these_chars, char *hex_these_quoted_chars)
2674 char *ans = NULL;
2676 if(!quote_these_chars)
2677 panic("bad arg to add_escapes");
2679 if(src){
2680 char *q, *p, *qchar;
2682 p = q = (char *)fs_get(2*strlen(src) + 1);
2684 while(*src){
2685 if(*src == quoting_char)
2686 for(qchar = hex_these_quoted_chars; *qchar != '\0'; qchar++)
2687 if(*(src+1) == *qchar)
2688 break;
2690 if(*src == quoting_char && *qchar){
2691 src++; /* skip quoting_char */
2692 *p++ = '\\';
2693 *p++ = 'x';
2694 C2XPAIR(*src, p);
2695 src++; /* skip quoted char */
2697 else{
2698 for(qchar = quote_these_chars; *qchar != '\0'; qchar++)
2699 if(*src == *qchar)
2700 break;
2702 if(*qchar){ /* *src is a char to be quoted */
2703 *p++ = quoting_char;
2704 *p++ = *src++;
2706 else{
2707 for(qchar = hex_these_chars; *qchar != '\0'; qchar++)
2708 if(*src == *qchar)
2709 break;
2711 if(*qchar){ /* *src is a char to be escaped */
2712 *p++ = '\\';
2713 *p++ = 'x';
2714 C2XPAIR(*src, p);
2715 src++;
2717 else /* a regular char */
2718 *p++ = *src++;
2724 *p = '\0';
2726 ans = cpystr(q);
2727 fs_give((void **)&q);
2730 return(ans);
2735 * Copy a string enclosed in "" without fixing \" or \\. Skip past \"
2736 * but copy it as is, removing only the enclosing quotes.
2738 char *
2739 copy_quoted_string_asis(char *src)
2741 char *q = NULL, *p;
2742 int done = 0, quotes = 0;
2744 if(src){
2745 p = q = (char *)fs_get(strlen(src) + 1);
2747 while(!done){
2748 switch(*src){
2749 case QUOTE:
2750 if(++quotes == 2)
2751 done++;
2752 else
2753 src++;
2755 break;
2757 case BSLASH: /* don't count \" as a quote, just copy */
2758 if(*(src+1) == QUOTE){
2759 if(quotes == 1){
2760 *p++ = *src;
2761 *p++ = *(src+1);
2764 src += 2;
2766 else{
2767 if(quotes == 1)
2768 *p++ = *src;
2770 src++;
2773 break;
2775 case '\0':
2776 fs_give((void **)&q);
2777 return(NULL);
2779 default:
2780 if(quotes == 1)
2781 *p++ = *src;
2783 src++;
2785 break;
2789 *p = '\0';
2792 return(q);
2797 * isxpair -- return true if the first two chars in string are
2798 * hexidecimal characters
2801 isxpair(char *s)
2803 return(isxdigit((unsigned char) *s) && isxdigit((unsigned char) *(s+1)));
2811 * * * * * * * something to help managing lists of strings * * * * * * * *
2815 STRLIST_S *
2816 new_strlist(char *name)
2818 STRLIST_S *sp = (STRLIST_S *) fs_get(sizeof(STRLIST_S));
2819 memset(sp, 0, sizeof(STRLIST_S));
2820 if(name)
2821 sp->name = cpystr(name);
2823 return(sp);
2827 STRLIST_S *
2828 copy_strlist(STRLIST_S *src)
2830 STRLIST_S *ret = NULL, *sl, *ss, *new_sl;
2832 if(src){
2833 ss = NULL;
2834 for(sl = src; sl; sl = sl->next){
2835 new_sl = (STRLIST_S *) fs_get(sizeof(*new_sl));
2836 memset((void *) new_sl, 0, sizeof(*new_sl));
2837 if(sl->name)
2838 new_sl->name = cpystr(sl->name);
2840 if(ss){
2841 ss->next = new_sl;
2842 ss = ss->next;
2844 else{
2845 ret = new_sl;
2846 ss = ret;
2851 return(ret);
2856 * Add the second list to the end of the first.
2858 void
2859 combine_strlists(STRLIST_S **first, STRLIST_S *second)
2861 STRLIST_S *sl;
2863 if(!second)
2864 return;
2866 if(first){
2867 if(*first){
2868 for(sl = *first; sl->next; sl = sl->next)
2871 sl->next = second;
2873 else
2874 *first = second;
2879 void
2880 free_strlist(STRLIST_S **strp)
2882 if(strp && *strp){
2883 if((*strp)->next)
2884 free_strlist(&(*strp)->next);
2886 if((*strp)->name)
2887 fs_give((void **) &(*strp)->name);
2889 fs_give((void **) strp);