* Add support for the TYPE and VALUE attributes of the html OL tag.
[alpine.git] / pith / string.c
blob0b65fe3fe874aad53c021681349c9f7a63fa4c72
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 2013-2016 Eduardo Chappa
8 * Copyright 2006-2008 University of Washington
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 && (flags & FLD_NEXTSPC)){
1592 for(i = starting_point; winner == -1 && i <= strlen(next_piece) != '\0' && i < 512; i++){
1593 endptr = utf8_count_forw_width(next_piece, i, &got_width);
1594 if(endptr && got_width == i && isspace((unsigned char) *endptr))
1595 winner = (int) i;
1597 if(winner == -1){
1598 winner = got_width < 512 ? got_width : 512;
1599 endptr = NULL;
1603 if(winner == -1){ /* if no good folding spot, fold at width */
1604 winner = starting_point;
1605 endptr = NULL;
1608 if(endptr == NULL){
1609 endptr = utf8_count_forw_width(next_piece, (unsigned) winner, &got_width);
1610 winner = (int) got_width;
1613 nb += ((endptr - next_piece) + eol);
1614 next_piece = endptr;
1615 if(!preserve_ws && isspace((unsigned char) next_piece[0]))
1616 next_piece++;
1619 len = 0;
1622 res = (char *) fs_get((nb+1) * sizeof(char));
1623 p = res;
1624 sstrncpy(&p, first_indent, nb+1-(p-res));
1625 len = indent1;
1626 next_piece = src;
1628 while(next_piece && *next_piece){
1629 if(next_piece != src && indent2){
1630 sstrncpy(&p, indent, nb+1-(p-res));
1631 len += indent2;
1634 this_width = (int) utf8_width(next_piece);
1635 if(this_width + len <= width){
1636 sstrncpy(&p, next_piece, nb+1-(p-res));
1637 if(cr && p-res < nb+1)
1638 *p++ = '\r';
1640 if(p-res < nb+1)
1641 *p++ = '\n';
1643 break;
1645 else{ /* fold it */
1646 starting_point = width - len; /* space left on this line */
1647 /* find a good folding spot */
1648 winner = -1;
1649 for(i = 0;
1650 winner == -1 && (starting_point - i > 5 || i < maxwidth - width);
1651 i++){
1653 if((shorter=starting_point-i) > 5){
1654 endptr = utf8_count_forw_width(next_piece, shorter, &got_width);
1655 if(endptr && got_width == shorter && isspace((unsigned char) *endptr))
1656 winner = (int) shorter;
1659 if(winner == -1
1660 && (longer=starting_point+i) && i < maxwidth - width){
1661 endptr = utf8_count_forw_width(next_piece, longer, &got_width);
1662 if(endptr && got_width == longer && isspace((unsigned char) *endptr))
1663 winner = (int) longer;
1667 if(winner == -1 && (flags & FLD_NEXTSPC)){
1668 for(i = starting_point; winner == -1 && i <= strlen(next_piece) && i < 512; i++){
1669 endptr = utf8_count_forw_width(next_piece, i, &got_width);
1670 if(endptr && got_width == i && isspace((unsigned char) *endptr))
1671 winner = (int) i;
1673 if(winner == -1){
1674 winner = got_width < 512 ? got_width : 512;
1675 endptr = NULL;
1679 if(winner == -1){ /* if no good folding spot, fold at width */
1680 winner = starting_point;
1681 endptr = NULL;
1684 if(endptr == NULL){
1685 endptr = utf8_count_forw_width(next_piece, (unsigned) winner, &got_width);
1686 winner = (int) got_width;
1689 if(endptr){
1690 save_char = *endptr;
1691 *endptr = '\0';
1692 sstrncpy(&p, next_piece, nb+1-(p-res));
1693 *endptr = save_char;
1694 next_piece = endptr;
1697 if(cr && p-res < nb+1)
1698 *p++ = '\r';
1700 if(p-res < nb+1)
1701 *p++ = '\n';
1703 if(!preserve_ws && isspace((unsigned char) next_piece[0]))
1704 next_piece++;
1707 len = 0;
1710 if(!src || !*src){
1711 if(cr && p-res < nb+1)
1712 *p++ = '\r';
1714 if(p-res < nb+1)
1715 *p++ = '\n';
1718 if(p-res < nb+1)
1719 *p = '\0';
1721 res[nb] = '\0';
1723 return(res);
1728 * strsquish - fancifies a string into the given buffer if it's too
1729 * long to fit in the given width
1731 char *
1732 strsquish(char *buf, size_t buflen, char *src, int width)
1735 * Replace strsquish() with calls to short_str().
1737 if(width > 14)
1738 return(short_str(src, buf, buflen, width, MidDots));
1739 else
1740 return(short_str(src, buf, buflen, width, FrontDots));
1744 char *
1745 long2string(long int l)
1747 static char string[20];
1749 snprintf(string, sizeof(string), "%ld", l);
1750 return(string);
1754 char *
1755 ulong2string(unsigned long int l)
1757 static char string[20];
1759 snprintf(string, sizeof(string), "%lu", l);
1760 return(string);
1764 char *
1765 int2string(int i)
1767 static char string[20];
1769 snprintf(string, sizeof(string), "%d", i);
1770 return(string);
1775 * strtoval - convert the given string to a positive integer.
1777 char *
1778 strtoval(char *s, int *val, int minmum, int maxmum, int otherok, char *errbuf,
1779 size_t errbuflen, char *varname)
1781 int i = 0, neg = 1;
1782 char *p = s, *errstr = NULL;
1784 removing_leading_and_trailing_white_space(p);
1785 for(; *p; p++)
1786 if(isdigit((unsigned char) *p)){
1787 i = (i * 10) + (*p - '0');
1789 else if(*p == '-' && i == 0){
1790 neg = -1;
1792 else{
1793 snprintf(errstr = errbuf, errbuflen,
1794 "Non-numeric value ('%c' in \"%.8s\") in %s. Using \"%d\"",
1795 *p, s, varname, *val);
1796 return(errbuf);
1799 i *= neg;
1801 /* range describes acceptable values */
1802 if(maxmum > minmum && (i < minmum || i > maxmum) && i != otherok)
1803 snprintf(errstr = errbuf, errbuflen,
1804 "%s of %d not supported (M%s %d). Using \"%d\"",
1805 varname, i, (i > maxmum) ? "ax" : "in",
1806 (i > maxmum) ? maxmum : minmum, *val);
1807 /* range describes unacceptable values */
1808 else if(minmum > maxmum && !(i < maxmum || i > minmum))
1809 snprintf(errstr = errbuf, errbuflen, "%s of %d not supported. Using \"%d\"",
1810 varname, i, *val);
1811 else
1812 *val = i;
1814 return(errstr);
1819 * strtolval - convert the given string to a positive _long_ integer.
1821 char *
1822 strtolval(char *s, long int *val, long int minmum, long int maxmum, long int otherok,
1823 char *errbuf, size_t errbuflen, char *varname)
1825 long i = 0, neg = 1L;
1826 char *p = s, *errstr = NULL;
1828 removing_leading_and_trailing_white_space(p);
1829 for(; *p; p++)
1830 if(isdigit((unsigned char) *p)){
1831 i = (i * 10L) + (*p - '0');
1833 else if(*p == '-' && i == 0L){
1834 neg = -1L;
1836 else{
1837 snprintf(errstr = errbuf, errbuflen,
1838 "Non-numeric value ('%c' in \"%.8s\") in %s. Using \"%ld\"",
1839 *p, s, varname, *val);
1840 return(errbuf);
1843 i *= neg;
1845 /* range describes acceptable values */
1846 if(maxmum > minmum && (i < minmum || i > maxmum) && i != otherok)
1847 snprintf(errstr = errbuf, errbuflen,
1848 "%s of %ld not supported (M%s %ld). Using \"%ld\"",
1849 varname, i, (i > maxmum) ? "ax" : "in",
1850 (i > maxmum) ? maxmum : minmum, *val);
1851 /* range describes unacceptable values */
1852 else if(minmum > maxmum && !(i < maxmum || i > minmum))
1853 snprintf(errstr = errbuf, errbuflen, "%s of %ld not supported. Using \"%ld\"",
1854 varname, i, *val);
1855 else
1856 *val = i;
1858 return(errstr);
1863 * Function to parse the given string into two space-delimited fields
1864 * Quotes may be used to surround labels or values with spaces in them.
1865 * Backslash negates the special meaning of a quote.
1866 * Unescaping of backslashes only happens if the pair member is quoted,
1867 * this provides for backwards compatibility.
1869 * Args -- string -- the source string
1870 * label -- the first half of the string, a return value
1871 * value -- the last half of the string, a return value
1872 * firstws -- if set, the halves are delimited by the first unquoted
1873 * whitespace, else by the last unquoted whitespace
1874 * strip_internal_label_quotes -- unescaped quotes in the middle of the label
1875 * are removed. This is useful for vars
1876 * like display-filters and url-viewers
1877 * which may require quoting of an arg
1878 * inside of a _TOKEN_.
1880 void
1881 get_pair(char *string, char **label, char **value, int firstws, int strip_internal_label_quotes)
1883 char *p, *q, *tmp, *token = NULL;
1884 int quoted = 0;
1886 *label = *value = NULL;
1889 * This for loop just finds the beginning of the value. If firstws
1890 * is set, then it begins after the first whitespace. Otherwise, it begins
1891 * after the last whitespace. Quoted whitespace doesn't count as
1892 * whitespace. If there is no unquoted whitespace, then there is no
1893 * label, there's just a value.
1895 for(p = string; p && *p;){
1896 if(*p == '"') /* quoted label? */
1897 quoted = (quoted) ? 0 : 1;
1899 if(*p == '\\' && *(p+1) == '"') /* escaped quote? */
1900 p++; /* skip it... */
1902 if(isspace((unsigned char)*p) && !quoted){ /* if space, */
1903 while(*++p && isspace((unsigned char)*p)) /* move past it */
1906 if(!firstws || !token)
1907 token = p; /* remember start of text */
1909 else
1910 p++;
1913 if(token){ /* copy label */
1914 *label = p = (char *)fs_get(((token - string) + 1) * sizeof(char));
1916 /* make a copy of the string */
1917 tmp = (char *)fs_get(((token - string) + 1) * sizeof(char));
1918 strncpy(tmp, string, token - string);
1919 tmp[token-string] = '\0';
1921 removing_leading_and_trailing_white_space(tmp);
1922 quoted = removing_double_quotes(tmp);
1924 for(q = tmp; *q; q++){
1925 if(quoted && *q == '\\' && (*(q+1) == '"' || *(q+1) == '\\'))
1926 *p++ = *++q;
1927 else if(!(strip_internal_label_quotes && *q == '"'))
1928 *p++ = *q;
1931 *p = '\0'; /* tie off label */
1932 fs_give((void **)&tmp);
1933 if(*label == '\0')
1934 fs_give((void **)label);
1936 else
1937 token = string;
1939 if(token){ /* copy value */
1940 *value = p = (char *)fs_get((strlen(token) + 1) * sizeof(char));
1942 tmp = cpystr(token);
1943 removing_leading_and_trailing_white_space(tmp);
1944 quoted = removing_double_quotes(tmp);
1946 for(q = tmp; *q ; q++){
1947 if(quoted && *q == '\\' && (*(q+1) == '"' || *(q+1) == '\\'))
1948 *p++ = *++q;
1949 else
1950 *p++ = *q;
1953 *p = '\0'; /* tie off value */
1954 fs_give((void **)&tmp);
1960 * This is sort of the inverse of get_pair.
1962 * Args -- label -- the first half of the string
1963 * value -- the last half of the string
1965 * Returns -- an allocated string which is "label" SPACE "value"
1967 * Label and value are quoted separately. If quoting is needed (they contain
1968 * whitespace) then backslash escaping is done inside the quotes for
1969 * " and for \. If quoting is not needed, no escaping is done.
1971 char *
1972 put_pair(char *label, char *value)
1974 char *result, *lab = label, *val = value;
1975 size_t l;
1977 if(label && *label)
1978 lab = quote_if_needed(label);
1980 if(value && *value)
1981 val = quote_if_needed(value);
1983 l = strlen(lab) + strlen(val) +1;
1984 result = (char *) fs_get((l+1) * sizeof(char));
1986 snprintf(result, l+1, "%s%s%s",
1987 lab ? lab : "",
1988 (lab && lab[0] && val && val[0]) ? " " : "",
1989 val ? val : "");
1991 if(lab && lab != label)
1992 fs_give((void **)&lab);
1993 if(val && val != value)
1994 fs_give((void **)&val);
1996 return(result);
2001 * This is for put_pair type uses. It returns either an allocated
2002 * string which is the quoted src string or it returns a pointer to
2003 * the src string if no quoting is needed.
2005 char *
2006 quote_if_needed(char *src)
2008 char *result = src, *qsrc = NULL;
2010 if(src && *src){
2011 /* need quoting? */
2012 if(strpbrk(src, " \t") != NULL)
2013 qsrc = add_escapes(src, "\\\"", '\\', "", "");
2015 if(qsrc && !*qsrc)
2016 fs_give((void **)&qsrc);
2018 if(qsrc){
2019 size_t l;
2021 l = strlen(qsrc)+2;
2022 result = (char *) fs_get((l+1) * sizeof(char));
2023 snprintf(result, l+1, "\"%s\"", qsrc);
2024 fs_give((void **)&qsrc);
2028 return(result);
2033 * Convert a 1, 2, or 3-digit octal string into an 8-bit character.
2034 * Only the first three characters of s will be used, and it is ok not
2035 * to null-terminate it.
2038 read_octal(char **s)
2040 register int i, j;
2042 i = 0;
2043 for(j = 0; j < 3 && **s >= '0' && **s < '8' ; (*s)++, j++)
2044 i = (i * 8) + (int)(unsigned char)**s - '0';
2046 return(i);
2051 * Convert two consecutive HEX digits to an integer. First two
2052 * chars pointed to by "s" MUST already be tested for hexness.
2055 read_hex(char *s)
2057 return(X2C(s));
2062 * Given a character c, put the 3-digit ascii octal value of that char
2063 * in the 2nd argument, which must be at least 3 in length.
2065 void
2066 char_to_octal_triple(int c, char *octal)
2068 c &= 0xff;
2070 octal[2] = (c % 8) + '0';
2071 c /= 8;
2072 octal[1] = (c % 8) + '0';
2073 c /= 8;
2074 octal[0] = c + '0';
2079 * Convert in memory string s to a C-style string, with backslash escapes
2080 * like they're used in C character constants.
2081 * Also convert leading spaces because read_pinerc deletes those
2082 * if not quoted.
2084 * Returns allocated C string version of s.
2086 char *
2087 string_to_cstring(char *s)
2089 char *b, *p;
2090 int n, i, all_space_so_far = 1;
2092 if(!s)
2093 return(cpystr(""));
2095 n = 20;
2096 b = (char *)fs_get((n+1) * sizeof(char));
2097 p = b;
2098 *p = '\0';
2099 i = 0;
2101 while(*s){
2102 if(*s != SPACE)
2103 all_space_so_far = 0;
2105 if(i + 4 > n){
2107 * The output string may overflow the output buffer.
2108 * Make more room.
2110 n += 20;
2111 fs_resize((void **)&b, (n+1) * sizeof(char));
2112 p = &b[i];
2114 else{
2115 switch(*s){
2116 case '\n':
2117 *p++ = '\\';
2118 *p++ = 'n';
2119 i += 2;
2120 break;
2122 case '\r':
2123 *p++ = '\\';
2124 *p++ = 'r';
2125 i += 2;
2126 break;
2128 case '\t':
2129 *p++ = '\\';
2130 *p++ = 't';
2131 i += 2;
2132 break;
2134 case '\b':
2135 *p++ = '\\';
2136 *p++ = 'b';
2137 i += 2;
2138 break;
2140 case '\f':
2141 *p++ = '\\';
2142 *p++ = 'f';
2143 i += 2;
2144 break;
2146 case '\\':
2147 *p++ = '\\';
2148 *p++ = '\\';
2149 i += 2;
2150 break;
2152 case SPACE:
2153 if(all_space_so_far){ /* use octal output */
2154 *p++ = '\\';
2155 char_to_octal_triple(*s, p);
2156 p += 3;
2157 i += 4;
2158 break;
2160 else{
2161 /* fall through */
2165 default:
2166 if(*s >= SPACE && *s < '~' && *s != '\"' && *s != '$'){
2167 *p++ = *s;
2168 i++;
2170 else{ /* use octal output */
2171 *p++ = '\\';
2172 char_to_octal_triple(*s, p);
2173 p += 3;
2174 i += 4;
2177 break;
2180 s++;
2184 *p = '\0';
2185 return(b);
2190 * Convert C-style string, with backslash escapes, into a hex string, two
2191 * hex digits per character.
2193 * Returns allocated hexstring version of s.
2195 char *
2196 cstring_to_hexstring(char *s)
2198 char *b, *p;
2199 int n, i, c;
2201 if(!s)
2202 return(cpystr(""));
2204 n = 20;
2205 b = (char *)fs_get((n+1) * sizeof(char));
2206 p = b;
2207 *p = '\0';
2208 i = 0;
2210 while(*s){
2211 if(i + 2 > n){
2213 * The output string may overflow the output buffer.
2214 * Make more room.
2216 n += 20;
2217 fs_resize((void **)&b, (n+1) * sizeof(char));
2218 p = &b[i];
2220 else{
2221 if(*s == '\\'){
2222 s++;
2223 switch(*s){
2224 case 'n':
2225 c = '\n';
2226 C2XPAIR(c, p);
2227 i += 2;
2228 s++;
2229 break;
2231 case 'r':
2232 c = '\r';
2233 C2XPAIR(c, p);
2234 i += 2;
2235 s++;
2236 break;
2238 case 't':
2239 c = '\t';
2240 C2XPAIR(c, p);
2241 i += 2;
2242 s++;
2243 break;
2245 case 'v':
2246 c = '\v';
2247 C2XPAIR(c, p);
2248 i += 2;
2249 s++;
2250 break;
2252 case 'b':
2253 c = '\b';
2254 C2XPAIR(c, p);
2255 i += 2;
2256 s++;
2257 break;
2259 case 'f':
2260 c = '\f';
2261 C2XPAIR(c, p);
2262 i += 2;
2263 s++;
2264 break;
2266 case 'a':
2267 c = '\007';
2268 C2XPAIR(c, p);
2269 i += 2;
2270 s++;
2271 break;
2273 case '\\':
2274 c = '\\';
2275 C2XPAIR(c, p);
2276 i += 2;
2277 s++;
2278 break;
2280 case '?':
2281 c = '?';
2282 C2XPAIR(c, p);
2283 i += 2;
2284 s++;
2285 break;
2287 case '\'':
2288 c = '\'';
2289 C2XPAIR(c, p);
2290 i += 2;
2291 s++;
2292 break;
2294 case '\"':
2295 c = '\"';
2296 C2XPAIR(c, p);
2297 i += 2;
2298 s++;
2299 break;
2301 case 0: /* reached end of s too early */
2302 c = 0;
2303 C2XPAIR(c, p);
2304 i += 2;
2305 s++;
2306 break;
2308 /* hex number */
2309 case 'x':
2310 s++;
2311 if(isxpair(s)){
2312 c = X2C(s);
2313 s += 2;
2315 else if(isxdigit((unsigned char)*s)){
2316 c = XDIGIT2C(*s);
2317 s++;
2319 else
2320 c = 0;
2322 C2XPAIR(c, p);
2323 i += 2;
2325 break;
2327 /* octal number */
2328 default:
2329 c = read_octal(&s);
2330 C2XPAIR(c, p);
2331 i += 2;
2333 break;
2336 else{
2337 C2XPAIR(*s, p);
2338 i += 2;
2339 s++;
2344 *p = '\0';
2345 return(b);
2350 * Convert C-style string, with backslash escapes, into a regular string.
2351 * Result goes in dst, which should be as big as src.
2354 void
2355 cstring_to_string(char *src, char *dst)
2357 char *p;
2358 int c;
2360 dst[0] = '\0';
2361 if(!src)
2362 return;
2364 p = dst;
2366 while(*src){
2367 if(*src == '\\'){
2368 src++;
2369 switch(*src){
2370 case 'n':
2371 *p++ = '\n';
2372 src++;
2373 break;
2375 case 'r':
2376 *p++ = '\r';
2377 src++;
2378 break;
2380 case 't':
2381 *p++ = '\t';
2382 src++;
2383 break;
2385 case 'v':
2386 *p++ = '\v';
2387 src++;
2388 break;
2390 case 'b':
2391 *p++ = '\b';
2392 src++;
2393 break;
2395 case 'f':
2396 *p++ = '\f';
2397 src++;
2398 break;
2400 case 'a':
2401 *p++ = '\007';
2402 src++;
2403 break;
2405 case '\\':
2406 *p++ = '\\';
2407 src++;
2408 break;
2410 case '?':
2411 *p++ = '?';
2412 src++;
2413 break;
2415 case '\'':
2416 *p++ = '\'';
2417 src++;
2418 break;
2420 case '\"':
2421 *p++ = '\"';
2422 src++;
2423 break;
2425 case 0: /* reached end of s too early */
2426 src++;
2427 break;
2429 /* hex number */
2430 case 'x':
2431 src++;
2432 if(isxpair(src)){
2433 c = X2C(src);
2434 src += 2;
2436 else if(isxdigit((unsigned char)*src)){
2437 c = XDIGIT2C(*src);
2438 src++;
2440 else
2441 c = 0;
2443 *p++ = c;
2445 break;
2447 /* octal number */
2448 default:
2449 c = read_octal(&src);
2450 *p++ = c;
2451 break;
2454 else
2455 *p++ = *src++;
2458 *p = '\0';
2463 * Quotes /'s and \'s with \
2465 * Args: src -- The source string.
2467 * Returns: A string with backslash quoting added. Any / in the string is
2468 * replaced with \/ and any \ is replaced with \\, and any
2469 * " is replaced with \".
2471 * The caller is responsible for freeing the memory allocated for the answer.
2473 char *
2474 add_backslash_escapes(char *src)
2476 return(add_escapes(src, "/\\\"", '\\', "", ""));
2481 * Undoes backslash quoting of source string.
2483 * Args: src -- The source string.
2485 * Returns: A string with backslash quoting removed or NULL. The string starts
2486 * at src and goes until the end of src or until a / is reached. The
2487 * / is not included in the string. /'s may be quoted by preceding
2488 * them with a backslash (\) and \'s may also be quoted by
2489 * preceding them with a \. In fact, \ quotes any character.
2490 * Not quite, \nnn is octal escape, \xXX is hex escape.
2492 * The caller is responsible for freeing the memory allocated for the answer.
2494 char *
2495 remove_backslash_escapes(char *src)
2497 char *ans = NULL, *q, *p;
2498 int done = 0;
2500 if(src){
2501 p = q = (char *)fs_get(strlen(src) + 1);
2503 while(!done){
2504 switch(*src){
2505 case '\\':
2506 src++;
2507 if(*src){
2508 if(isdigit((unsigned char)*src))
2509 *p++ = (char)read_octal(&src);
2510 else if((*src == 'x' || *src == 'X') &&
2511 *(src+1) && *(src+2) && isxpair(src+1)){
2512 *p++ = (char)read_hex(src+1);
2513 src += 3;
2515 else
2516 *p++ = *src++;
2519 break;
2521 case '\0':
2522 case '/':
2523 done++;
2524 break;
2526 default:
2527 *p++ = *src++;
2528 break;
2532 *p = '\0';
2534 ans = cpystr(q);
2535 fs_give((void **)&q);
2538 return(ans);
2543 * Quote values for viewer-hdr-colors. We quote backslash, comma, and slash.
2544 * Also replaces $ with $$.
2546 * Args: src -- The source string.
2548 * Returns: A string with backslash quoting added.
2550 * The caller is responsible for freeing the memory allocated for the answer.
2552 char *
2553 add_viewerhdr_escapes(char *src)
2555 char *tmp, *ans = NULL;
2557 tmp = add_escapes(src, "/\\", '\\', ",", "");
2559 if(tmp){
2560 ans = dollar_escape_dollars(tmp);
2561 fs_give((void **) &tmp);
2564 return(ans);
2569 * Quote dollar sign by preceding it with another dollar sign. We use $$
2570 * instead of \$ so that it will work for both PC-Pine and unix.
2572 * Args: src -- The source string.
2574 * Returns: A string with $$ quoting added.
2576 * The caller is responsible for freeing the memory allocated for the answer.
2578 char *
2579 dollar_escape_dollars(char *src)
2581 return(add_escapes(src, "$", '$', "", ""));
2586 * This adds the quoting for vcard backslash quoting.
2587 * That is, commas are backslashed, backslashes are backslashed,
2588 * semicolons are backslashed, and CRLFs are \n'd.
2589 * This is thought to be correct for draft-ietf-asid-mime-vcard-06.txt, Apr 98.
2591 char *
2592 vcard_escape(char *src)
2594 char *p, *q;
2596 q = add_escapes(src, ";,\\", '\\', "", "");
2597 if(q){
2598 /* now do CRLF -> \n in place */
2599 for(p = q; *p != '\0'; p++)
2600 if(*p == '\r' && *(p+1) == '\n'){
2601 *p++ = '\\';
2602 *p = 'n';
2606 return(q);
2611 * This undoes the vcard backslash quoting.
2613 * In particular, it turns \n into newline, \, into ',', \\ into \, \; -> ;.
2614 * In fact, \<anything_else> is also turned into <anything_else>. The ID
2615 * isn't clear on this.
2617 * The caller is responsible for freeing the memory allocated for the answer.
2619 char *
2620 vcard_unescape(char *src)
2622 char *ans = NULL, *p;
2623 int done = 0;
2625 if(src){
2626 p = ans = (char *)fs_get(strlen(src) + 1);
2628 while(!done){
2629 switch(*src){
2630 case '\\':
2631 src++;
2632 if(*src == 'n' || *src == 'N'){
2633 *p++ = '\n';
2634 src++;
2636 else if(*src)
2637 *p++ = *src++;
2639 break;
2641 case '\0':
2642 done++;
2643 break;
2645 default:
2646 *p++ = *src++;
2647 break;
2651 *p = '\0';
2654 return(ans);
2659 * Turn folded lines into long lines in place.
2661 * CRLF whitespace sequences are removed, the space is not preserved.
2663 void
2664 vcard_unfold(char *string)
2666 char *p = string;
2668 while(*string) /* while something to copy */
2669 if(*string == '\r' &&
2670 *(string+1) == '\n' &&
2671 (*(string+2) == SPACE || *(string+2) == TAB))
2672 string += 3;
2673 else
2674 *p++ = *string++;
2676 *p = '\0';
2681 * Quote specified chars with escape char.
2683 * Args: src -- The source string.
2684 * quote_these_chars -- Array of chars to quote
2685 * quoting_char -- The quoting char to be used (e.g., \)
2686 * hex_these_chars -- Array of chars to hex escape
2687 * hex_these_quoted_chars -- Array of chars to hex escape if they are
2688 * already quoted with quoting_char (that is,
2689 * turn \, into hex comma)
2691 * Returns: An allocated copy of string with quoting added.
2692 * The caller is responsible for freeing the memory allocated for the answer.
2694 char *
2695 add_escapes(char *src, char *quote_these_chars, int quoting_char,
2696 char *hex_these_chars, char *hex_these_quoted_chars)
2698 char *ans = NULL;
2700 if(!quote_these_chars)
2701 alpine_panic("bad arg to add_escapes");
2703 if(src){
2704 char *q, *p, *qchar;
2706 p = q = (char *)fs_get(2*strlen(src) + 1);
2708 while(*src){
2709 if(*src == quoting_char)
2710 for(qchar = hex_these_quoted_chars; *qchar != '\0'; qchar++)
2711 if(*(src+1) == *qchar)
2712 break;
2714 if(*src == quoting_char && *qchar){
2715 src++; /* skip quoting_char */
2716 *p++ = '\\';
2717 *p++ = 'x';
2718 C2XPAIR(*src, p);
2719 src++; /* skip quoted char */
2721 else{
2722 for(qchar = quote_these_chars; *qchar != '\0'; qchar++)
2723 if(*src == *qchar)
2724 break;
2726 if(*qchar){ /* *src is a char to be quoted */
2727 *p++ = quoting_char;
2728 *p++ = *src++;
2730 else{
2731 for(qchar = hex_these_chars; *qchar != '\0'; qchar++)
2732 if(*src == *qchar)
2733 break;
2735 if(*qchar){ /* *src is a char to be escaped */
2736 *p++ = '\\';
2737 *p++ = 'x';
2738 C2XPAIR(*src, p);
2739 src++;
2741 else /* a regular char */
2742 *p++ = *src++;
2748 *p = '\0';
2750 ans = cpystr(q);
2751 fs_give((void **)&q);
2754 return(ans);
2759 * Copy a string enclosed in "" without fixing \" or \\. Skip past \"
2760 * but copy it as is, removing only the enclosing quotes.
2762 char *
2763 copy_quoted_string_asis(char *src)
2765 char *q = NULL, *p;
2766 int done = 0, quotes = 0;
2768 if(src){
2769 p = q = (char *)fs_get(strlen(src) + 1);
2771 while(!done){
2772 switch(*src){
2773 case QUOTE:
2774 if(++quotes == 2)
2775 done++;
2776 else
2777 src++;
2779 break;
2781 case BSLASH: /* don't count \" as a quote, just copy */
2782 if(*(src+1) == QUOTE){
2783 if(quotes == 1){
2784 *p++ = *src;
2785 *p++ = *(src+1);
2788 src += 2;
2790 else{
2791 if(quotes == 1)
2792 *p++ = *src;
2794 src++;
2797 break;
2799 case '\0':
2800 fs_give((void **)&q);
2801 return(NULL);
2803 default:
2804 if(quotes == 1)
2805 *p++ = *src;
2807 src++;
2809 break;
2813 *p = '\0';
2816 return(q);
2821 * isxpair -- return true if the first two chars in string are
2822 * hexidecimal characters
2825 isxpair(char *s)
2827 return(isxdigit((unsigned char) *s) && isxdigit((unsigned char) *(s+1)));
2835 * * * * * * * something to help managing lists of strings * * * * * * * *
2839 STRLIST_S *
2840 new_strlist(char *name)
2842 STRLIST_S *sp = (STRLIST_S *) fs_get(sizeof(STRLIST_S));
2843 memset(sp, 0, sizeof(STRLIST_S));
2844 if(name)
2845 sp->name = cpystr(name);
2847 return(sp);
2851 STRLIST_S *
2852 copy_strlist(STRLIST_S *src)
2854 STRLIST_S *ret = NULL, *sl, *ss, *new_sl;
2856 if(src){
2857 ss = NULL;
2858 for(sl = src; sl; sl = sl->next){
2859 new_sl = (STRLIST_S *) fs_get(sizeof(*new_sl));
2860 memset((void *) new_sl, 0, sizeof(*new_sl));
2861 if(sl->name)
2862 new_sl->name = cpystr(sl->name);
2864 if(ss){
2865 ss->next = new_sl;
2866 ss = ss->next;
2868 else{
2869 ret = new_sl;
2870 ss = ret;
2875 return(ret);
2880 * Add the second list to the end of the first.
2882 void
2883 combine_strlists(STRLIST_S **first, STRLIST_S *second)
2885 STRLIST_S *sl;
2887 if(!second)
2888 return;
2890 if(first){
2891 if(*first){
2892 for(sl = *first; sl->next; sl = sl->next)
2895 sl->next = second;
2897 else
2898 *first = second;
2903 void
2904 free_strlist(STRLIST_S **strp)
2906 if(strp && *strp){
2907 if((*strp)->next)
2908 free_strlist(&(*strp)->next);
2910 if((*strp)->name)
2911 fs_give((void **) &(*strp)->name);
2913 fs_give((void **) strp);
2917 void
2918 convert_decimal_to_roman (char *rn, size_t len, long n, char l)
2920 char *symbols;
2921 int amo[6];
2922 int i, j, k;
2924 rn[0] = '\0';
2925 if(n >= 4000L || n <= 0L)
2926 return;
2928 if(l == 'i')
2929 symbols = "mdclxvi";
2930 else
2931 symbols = "MDCLXVI";
2933 amo[0] = n/1000; n -= amo[0]*1000;
2934 amo[1] = n/500; n -= amo[1]*500;
2935 amo[2] = n/100; n -= amo[2]*100;
2936 amo[3] = n/50; n -= amo[3]*50;
2937 amo[4] = n/10; n -= amo[4]*10;
2938 amo[5] = n/5; n -= amo[5]*5;
2939 amo[6] = n;
2941 for(i = 0, j = 0; j < strlen(symbols); j++){
2942 if(amo[j] < 4){
2943 if(amo[j+1] != 4){
2944 for(k = 0; k < amo[j]; k++)
2945 rn[i++] = symbols[j];
2947 } else {
2948 if(amo[j-1] == 0){
2949 rn[i++] = symbols[j];
2950 rn[i++] = symbols[j-1];
2951 } else {
2952 rn[i++] = symbols[j];
2953 rn[i++] = symbols[j-2];
2957 rn[i++] = '\0';
2958 rn[len] = '\0';
2961 void
2962 convert_decimal_to_alpha (char *rn, size_t len, long n, char l)
2964 char *symbols;
2965 int amo[16];
2966 int i, j, k;
2968 rn[0] = '\0';
2970 if(n < 0)
2971 return;
2973 for(i = 0; n > 0; i++){
2974 amo[i] = n % 26;
2975 n = (n - amo[i])/26;
2977 amo[i] = -1;
2979 for(i = 0; amo[i] >= 0; i++)
2980 rn[i] = l + amo[i] - 1;
2981 rn[i] = '\0';
2982 rn[len] = '\0';