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
4 
5 /*
6  * ========================================================================
7  * Copyright 2013-2021 Eduardo Chappa
8  * Copyright 2006-2008 University of Washington
9  *
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
13  *
14  *     http://www.apache.org/licenses/LICENSE-2.0
15  *
16  * ========================================================================
17  */
18 
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 /.
55 
56  ====*/
57 
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"
64 
65 
66 void        char_to_octal_triple(int, char *);
67 char       *dollar_escape_dollars(char *);
68 void	    convert_string_to_utf8(char *, int);
69 
70 
71 /*----------------------------------------------------------------------
72        Replace dl characters in one string with another given string
73 
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
78 
79  Result: returns pointer in originl string to end of string just inserted
80   ---*/
81 char *
rplstr(char * os,size_t oslen,int dl,char * is)82 rplstr(char *os, size_t oslen, int dl, char *is)
83 {
84     char *x1, *x2, *x3;
85     int   diff;
86 
87     if(os == NULL)
88         return(NULL);
89 
90     x1 = os + strlen(os);
91 
92     /* can't delete more characters than there are */
93     if(dl > x1 - os)
94         dl = x1 - os;
95 
96     x2 = is;
97     if(is != NULL)
98       x2 = is + strlen(is);
99 
100     diff = (x2 - is) - dl;
101 
102     if(diff < 0){				/* String shrinks */
103         x3 = os;
104         if(is != NULL)
105           for(x2 = is; *x2; *x3++ = *x2++)	/* copy new string in */
106 	      ;
107 
108         for(x2 = x3 - diff; *x2; *x3++ = *x2++)	/* shift for delete */
109 	  ;
110 
111         *x3 = *x2;				/* the null */
112     }
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;
118 
119         for(; x3 >= os + (x2 - is); *x3-- = *x1--); /* shift*/
120 	  ;
121 
122 	if(is != NULL)
123           for(x1 = os, x2 = is; *x2 ; *x1++ = *x2++)
124 	    ;
125 
126         while(*x3) x3++;
127     }
128 
129     os[oslen-1] = '\0';
130 
131     return(x3);
132 }
133 
134 
135 
136 /*----------------------------------------------------------------------
137      Squeeze out blanks
138   ----------------------------------------------------------------------*/
139 void
sqzspaces(char * string)140 sqzspaces(char *string)
141 {
142     char *p = string;
143 
144     while((*string = *p++) != '\0')	   /* while something to copy       */
145       if(!isspace((unsigned char)*string)) /* only really copy if non-blank */
146 	string++;
147 }
148 
149 
150 
151 /*----------------------------------------------------------------------
152      Squeeze out CR's and LF's
153   ----------------------------------------------------------------------*/
154 void
sqznewlines(char * string)155 sqznewlines(char *string)
156 {
157     char *p = string;
158 
159     while((*string = *p++) != '\0')	      /* while something to copy  */
160       if(*string != '\r' && *string != '\n')  /* only copy if non-newline */
161 	string++;
162 }
163 
164 
165 
166 /*----------------------------------------------------------------------
167        Remove leading white space from a string in place
168 
169   Args: string -- string to remove space from
170   ----*/
171 void
removing_leading_white_space(char * string)172 removing_leading_white_space(char *string)
173 {
174     register char *p;
175 
176     if(!string || !*string || !isspace(*string))
177       return;
178 
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... */
182 	    ;
183 
184 	  return;
185       }
186 }
187 
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.
191  */
192 void
replace_tabs_by_space(char * orig)193 replace_tabs_by_space(char *orig)
194 {
195   char *s;
196 
197   for(s = orig; s != NULL && *s != '\0' ; s++)
198      if(*s == '\t') *s = ' ';
199 }
200 
201 
202 /*----------------------------------------------------------------------
203        Remove trailing white space from a string in place
204 
205   Args: string -- string to remove space from
206   ----*/
207 void
removing_trailing_white_space(char * string)208 removing_trailing_white_space(char *string)
209 {
210     char *p = NULL;
211 
212     if(!string || !*string)
213       return;
214 
215     for(; *string; string++)		/* remember start of whitespace */
216       p = (!isspace((unsigned char)*string)) ? NULL : (!p) ? string : p;
217 
218     if(p)				/* if whitespace, blast it */
219       *p = '\0';
220 }
221 
222 
223 void
removing_leading_and_trailing_white_space(char * string)224 removing_leading_and_trailing_white_space(char *string)
225 {
226     register char *p, *q = NULL;
227 
228     if(!string || !*string)
229       return;
230 
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;
236 	  }
237 	  else{
238 	      for(; (*string = *p++) != '\0'; string++)
239 		q = (!isspace((unsigned char)*string)) ? NULL : (!q) ? string : q;
240 	  }
241 
242 	  if(q)
243 	    *q = '\0';
244 
245 	  return;
246       }
247 
248     if(*string != '\0')
249       *string = '\0';
250 }
251 
252 
253 /*----------------------------------------------------------------------
254        Remove one set of double quotes surrounding string in place
255        Returns 1 if quotes were removed
256 
257   Args: string -- string to remove quotes from
258   ----*/
259 int
removing_double_quotes(char * string)260 removing_double_quotes(char *string)
261 {
262     register char *p;
263     int ret = 0;
264 
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);
272 	}
273     }
274 
275     return(ret);
276 }
277 
278 
279 
280 /*----------------------------------------------------------------------
281   return a pointer to first non-whitespace char in string
282 
283   Args: string -- string to scan
284   ----*/
285 char *
skip_white_space(char * string)286 skip_white_space(char *string)
287 {
288     while(*string && isspace((unsigned char) *string))
289       string++;
290 
291     return(string);
292 }
293 
294 
295 
296 /*----------------------------------------------------------------------
297   return a pointer to first whitespace char in string
298 
299   Args: string -- string to scan
300   ----*/
301 char *
skip_to_white_space(char * string)302 skip_to_white_space(char *string)
303 {
304     while(*string && !isspace((unsigned char) *string))
305       string++;
306 
307     return(string);
308 }
309 
310 
311 
312 /*----------------------------------------------------------------------
313        Remove quotes from a string in place
314 
315   Args: string -- string to remove quotes from
316   Rreturns: string passed us, but with quotes gone
317   ----*/
318 char *
removing_quotes(char * string)319 removing_quotes(char *string)
320 {
321     char *p, *q;
322 
323     if(*(p = q = string) == '\"'){
324 	q++;
325 	do
326 	  if(*q == '\"' || *q == '\\')
327 	    q++;
328 	while((*p++ = *q++) != '\0');
329     }
330 
331     return(string);
332 }
333 
334 
335 
336 /*---------------------------------------------------
337      Remove leading whitespace, trailing whitespace and convert
338      to lowercase
339 
340    Args: s, -- The string to clean
341 
342  Result: the cleaned string
343   ----*/
344 char *
strclean(char * string)345 strclean(char *string)
346 {
347     char *s = string, *sc = NULL, *p = NULL;
348 
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 */
354 	}
355 	else if(!p)				/* it's OK if sc == NULL */
356 	  p = sc;				/* start of blanks? */
357 
358 	if(sc)					/* if copying, copy */
359 	  *sc++ = isupper((unsigned char)(*s))
360 			  ? (unsigned char)tolower((unsigned char)(*s))
361 			  : (unsigned char)(*s);
362     }
363 
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 */
368 
369     return(string);
370 }
371 
372 
373 /*
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.
378  *
379  * Wid refers to UTF-8 screen width, not strlen width.
380  *
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.
386  *
387  *     FrontDots           ...stuvwxyz
388  *     EndDots             abcdefgh...
389  *     MidDots             abcd...wxyz
390  */
391 char *
short_str(char * src,char * buf,size_t buflen,int wid,WhereDots where)392 short_str(char *src, char *buf, size_t buflen, int wid, WhereDots where)
393 {
394     char *ans;
395     unsigned alen, first = 0, second = 0;
396 
397     if(wid <= 0){
398 	ans = buf;
399 	if(buflen > 0)
400 	  buf[0] = '\0';
401     }
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';
410 	    }
411 	}
412 	else{
413 	    char *q;
414 	    unsigned got_width;
415 
416 	    /*
417 	     * first == length of preellipsis text
418 	     * second == length of postellipsis text
419 	     */
420 	    if(where == FrontDots){
421 		first = 0;
422 		second = wid - 3;
423 	    }
424 	    else if(where == MidDots){
425 		first = (wid - 3)/2;
426 		second = wid - 3 - first;
427 	    }
428 	    else if(where == EndDots){
429 		first = wid - 3;
430 		second = 0;
431 	    }
432 
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++ = '.';
442 		}
443 	    }
444 
445 	    if(buflen - (q-buf) > 3){
446 		strncpy(q, "...", buflen - (q-buf));
447 		buf[buflen-1] = '\0';
448 		q += strlen(q);
449 	    }
450 
451 	    if(second > 0){
452 		char *p;
453 
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);
459 		}
460 	    }
461 
462 	    if(buflen - (q-buf) > 0)
463 	      *q = '\0';
464 
465 	    buf[buflen-1] = '\0';
466 	}
467     }
468 
469     return(ans);
470 }
471 
472 
473 
474 /*----------------------------------------------------------------------
475         Search one string for another
476 
477    Args:  haystack -- The string to search in, the larger string
478           needle   -- The string to search for, the smaller string
479 
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 *
srchstr(char * haystack,char * needle)489 srchstr(char *haystack, char *needle)
490 {
491     char *p, *q;
492 
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)))
499 
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;
509 	}
510 
511     return(NULL);
512 }
513 
514 
515 
516 /*----------------------------------------------------------------------
517         Search one string for another, from right
518 
519    Args:  is -- The string to search in, the larger string
520           ss -- The string to search for, the smaller string
521 
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 independent.
524   ----*/
525 
526 char *
srchrstr(register char * is,register char * ss)527 srchrstr(register char *is, register char *ss)
528 {
529     register char *sx, *sy;
530     char          *ss_store, *rv;
531     char          *begin_is;
532     char           temp[251];
533 
534     if(is == NULL || ss == NULL)
535       return(NULL);
536 
537     if(strlen(ss) > sizeof(temp) - 2)
538       ss_store = (char *)fs_get(strlen(ss) + 1);
539     else
540       ss_store = temp;
541 
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;
547 
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++)
558 	   ;
559 
560         if(!*sy){
561             rv = is;
562             break;
563         }
564 
565         is--;
566     }
567 
568     if(ss_store != temp)
569       fs_give((void **)&ss_store);
570 
571     return(rv);
572 }
573 
574 
575 
576 /*----------------------------------------------------------------------
577     A replacement for strchr or index ...
578 
579     Returns a pointer to the first occurrence of the character
580     'ch' in the specified string or NULL if it doesn't occur
581 
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 *
strindex(char * buffer,int ch)588 strindex(char *buffer, int ch)
589 {
590     do
591       if(*buffer == ch)
592 	return(buffer);
593     while (*buffer++ != '\0');
594 
595     return(NULL);
596 }
597 
598 
599 /* Returns a pointer to the last occurrence of the character
600  * 'ch' in the specified string or NULL if it doesn't occur
601  */
602 char *
strrindex(char * buffer,int ch)603 strrindex(char *buffer, int ch)
604 {
605     char *address = NULL;
606 
607     do
608       if(*buffer == ch)
609 	address = buffer;
610     while (*buffer++ != '\0');
611     return(address);
612 }
613 
614 
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 *
iutf8ncpy(char * d,char * s,int n)621 iutf8ncpy(char *d, char *s, int n)
622 {
623     register int i;
624 
625     if(!d || !s)
626       return(NULL);
627 
628     /*
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.
632      */
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 + '@';
638 	  }
639 	  else{
640 	      d[i] = '\0';
641 	      break;		/* don't fit */
642 	  }
643       }
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 */
651 		  }
652 	      }
653 	      else{
654 		  d[i] = '\0';
655 		  break;		/* too long */
656 	      }
657 	  }
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 */
664 		  }
665 	      }
666 	      else{
667 		  d[i] = '\0';
668 		  break;	/* won't fit */
669 	      }
670 	  }
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 */
678 		  }
679 	      }
680 	      else{
681 		  d[i] = '\0';
682 		  break;	/* won't fit */
683 	      }
684 	  }
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 */
693 		  }
694 	      }
695 	      else{
696 		  d[i] = '\0';
697 		  break;	/* won't fit */
698 	      }
699 	  }
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 */
709 		  }
710 	      }
711 	      else{
712 		  d[i] = '\0';
713 		  break;	/* won't fit */
714 	      }
715 	  }
716 	  else{
717 	      d[i] = '\0';
718 	      break;		/* don't fit */
719 	  }
720       }
721 
722     return(d);
723 }
724 
725 
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 *
istrncpy(char * d,char * s,int n)732 istrncpy(char *d, char *s, int n)
733 {
734     char *rv = d;
735     unsigned char c;
736 
737     if(!d || !s)
738       return(NULL);
739 
740     do
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 ? '~' : '^';
746 
747 	    if(n-- > 0){
748 		s++;
749 		*d = (c == 0x7f) ? '?' : (c & 0x1f) + '@';
750 	    }
751 	}
752       }
753       else{
754 	  if(n-- > 0)
755 	    *d = *s++;
756       }
757     while(n > 0 && *d++);
758 
759     return(rv);
760 }
761 
762 
763 void
convert_string_to_utf8(char * buf,int bufsize)764 convert_string_to_utf8(char *buf, int bufsize)
765 {
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);
772    }
773 }
774 
775 
776 
777 char *xdays[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL};
778 
779 char *
month_abbrev(int month_num)780 month_abbrev(int month_num)
781 {
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]);
787 }
788 
789 char *
month_abbrev_locale(int month_num)790 month_abbrev_locale(int month_num)
791 {
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;
799 
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));
805 
806 	    /*
807 	     * If it is all digits, then use the English
808 	     * words instead. Look for
809 	     *    "<digit>"
810 	     *    "<digit><digit>"    or
811 	     *    "<space><digit>"
812 	     */
813 	    if((buf[0] && !(buf[0] & 0x80)
814 	         && isdigit((unsigned char)buf[0]) && !buf[1])
815 	       ||
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));
821 
822 	    /*
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.
825 	     */
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;
829 
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;
834 
835 		for(; p > buf; p--)
836 		  *p = *(p-1);
837 
838 		buf[0] = ' ';
839 	    }
840 
841 	    return(buf);
842 	}
843     }
844     else
845       return(month_abbrev(month_num));
846 #else /* DISABLE_LOCALE_DATES */
847     return(month_abbrev(month_num));
848 #endif /* DISABLE_LOCALE_DATES */
849 }
850 
851 char *
month_name(int month_num)852 month_name(int month_num)
853 {
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]);
860 }
861 
862 char *
month_name_locale(int month_num)863 month_name_locale(int month_num)
864 {
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;
872 
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);
879 	}
880     }
881     else
882       return(month_name(month_num));
883 #else /* DISABLE_LOCALE_DATES */
884     return(month_name(month_num));
885 #endif /* DISABLE_LOCALE_DATES */
886 }
887 
888 
889 char *
day_abbrev(int day_of_week)890 day_abbrev(int day_of_week)
891 {
892     if(day_of_week < 0 || day_of_week > 6)
893       return("???");
894     return(xdays[day_of_week]);
895 }
896 
897 char *
day_abbrev_locale(int day_of_week)898 day_abbrev_locale(int day_of_week)
899 {
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;
907 
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);
913 	}
914     }
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 */
920 }
921 
922 char *
day_name(int day_of_week)923 day_name(int day_of_week)
924 {
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]);
930 }
931 
932 char *
day_name_locale(int day_of_week)933 day_name_locale(int day_of_week)
934 {
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;
942 
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);
948 	}
949     }
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 */
955 }
956 
957 
958 size_t
our_strftime(char * dst,size_t dst_size,char * format,struct tm * tm)959 our_strftime(char *dst, size_t dst_size, char *format, struct tm *tm)
960 {
961 #ifdef _WINDOWS
962     LPTSTR lptbuf, lptformat;
963     char *u;
964 
965     lptbuf = (LPTSTR) fs_get(dst_size * sizeof(TCHAR));
966     lptbuf[0] = '\0';
967     lptformat = utf8_to_lptstr((LPSTR) format);
968 
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);
975     }
976 
977     return(strlen(dst));
978 #else
979     return(strftime(dst, dst_size, format, tm));
980 #endif
981 }
982 
983 
984 /*----------------------------------------------------------------------
985       Return month number of month named in string
986 
987    Args: s -- string with 3 letter month abbreviation of form mmm-yyyy
988 
989  Result: Returns month number with January, year 1900, 2000... being 0;
990          -1 if no month/year is matched
991  ----*/
992 int
month_num(char * s)993 month_num(char *s)
994 {
995     int month = -1, year;
996     int i;
997 
998     if(F_ON(F_PRUNE_USES_ISO,ps_global)){
999 	char save, *p;
1000 	char digmon[3];
1001 
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);
1009 
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;
1017 	    }
1018 
1019 	    if(i == 12)
1020 	      return(-1);
1021 
1022 	    month = year * 12 + i;
1023 	}
1024     }
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;
1030 	    }
1031 
1032 	    if(i == 12)
1033 	      return(-1);
1034 
1035 	    year = atoi(s + 4);
1036 	    if(year == 0)
1037 	      return(-1);
1038 
1039 	    month = year * 12 + i;
1040 	}
1041     }
1042 
1043     return(month);
1044 }
1045 
1046 
1047 /*
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.
1051  */
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},
1077 };
1078 
1079 /*----------------------------------------------------------------------
1080   Parse date in or near RFC-822 format into the date structure
1081 
1082 Args: given_date -- The input string to parse
1083       d          -- Pointer to a struct date to place the result in
1084 
1085 Returns nothing
1086 
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
1094 
1095 All leading, intervening and trailing spaces tabs and commas are ignored.
1096 The preferred formats are the first or second ones.  If a field is unparsable
1097 it's value is left as -1.
1098 
1099   ----*/
1100 void
parse_date(char * given_date,struct date * d)1101 parse_date(char *given_date, struct date *d)
1102 {
1103     char *p, **i, *q;
1104     int   month, n;
1105 
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;
1115 
1116     if(given_date == NULL)
1117       return;
1118 
1119     p = given_date;
1120     while(*p && isspace((unsigned char)*p))
1121       p++;
1122 
1123     /* Start with weekday? */
1124     if((q=strchr(p, ',')) != NULL){
1125 
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;
1131 
1132 	    *q = ',';
1133 
1134 	    if(*i != NULL) {
1135 		/* Started with week day */
1136 		d->wkday = i - xdays;
1137 	    }
1138 	}
1139 
1140 	p = q+1;
1141 	while(*p && isspace((unsigned char)*p))
1142 	  p++;
1143     }
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;
1149 
1150 	*q = ' ';
1151 
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++;
1158 	}
1159     }
1160 
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++;
1167     }
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;
1173 
1174     }
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++;
1180 
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++;
1188     }
1189 
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++;
1206             }
1207         }
1208         while(*p && isspace((unsigned char)*p))
1209           p++;
1210     }
1211 
1212 
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 weird, 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++;
1231     }
1232 
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++;
1249             }
1250         }
1251     }
1252     while(*p && isspace((unsigned char)*p))
1253       p++;
1254 
1255 
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;
1283 	      }
1284         }
1285     }
1286 
1287     if(d->wkday == -1){
1288 	MESSAGECACHE elt;
1289 	struct tm   *tm;
1290 	time_t       t;
1291 
1292 	/*
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.
1295 	 */
1296 	if(mail_parse_date(&elt, (unsigned char *) given_date)){
1297 	    t = mail_longdate(&elt);
1298 	    tm = localtime(&t);
1299 
1300 	    if(tm)
1301 	      d->wkday = tm->tm_wday;
1302 	}
1303     }
1304 }
1305 
1306 
1307 char *
convert_date_to_local(char * date)1308 convert_date_to_local(char *date)
1309 {
1310     struct tm  *tm;
1311     time_t      ltime;
1312     static char datebuf[30];
1313 
1314     ltime = date_to_local_time_t(date);
1315     if(ltime == (time_t) -1)
1316       return(date);
1317 
1318     tm = localtime(&ltime);
1319 
1320     if(tm == NULL)
1321       return(date);
1322 
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);
1326 
1327     return(datebuf);
1328 }
1329 
1330 
1331 time_t
date_to_local_time_t(char * date)1332 date_to_local_time_t(char *date)
1333 {
1334     time_t      ourtime;
1335     struct tm   theirtime;
1336     struct date d;
1337     static int  zone = 1000000;		/* initialize timezone offset */
1338     static int  dst;
1339 
1340     if(zone == 1000000){
1341       int julian;
1342       struct tm *tm;
1343       time_t     now;
1344 
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;
1353 
1354 	  tm = localtime(&now);
1355 	  dst = tm->tm_isdst;		/* for converting back to our time */
1356 
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;
1360 
1361 	  zone *= 60;		/* change to seconds */
1362         }
1363       }
1364     }
1365 
1366     parse_date(date, &d);
1367 
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;
1376 
1377     theirtime.tm_isdst = dst;
1378 
1379     ourtime = mktime(&theirtime);	/* still theirtime, actually */
1380 
1381     /* convert to the time we want to show */
1382     if(ourtime != (time_t) -1)
1383       ourtime += zone;
1384 
1385     return(ourtime);
1386 }
1387 
1388 
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 *
repeat_char(int n,int c)1394 repeat_char(int n, int c)
1395 {
1396     static char bb[3][MAX_SCREEN_COLS+1];
1397     static int whichbb = 0;
1398     char *b;
1399 
1400     whichbb = (whichbb + 1) % 3;
1401     b = bb[whichbb];
1402 
1403     if(n > sizeof(bb[0]))
1404        n = sizeof(bb[0]) - 1;
1405 
1406     bb[whichbb][n--] = '\0';
1407     while(n >= 0)
1408       bb[whichbb][n--] = c;
1409 
1410     return(bb[whichbb]);
1411 }
1412 
1413 
1414 /*----------------------------------------------------------------------
1415    Format number as amount of bytes, appending Kb, Mb, Gb, bytes
1416 
1417   Args: bytes -- number of bytes to format
1418 
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 *
byte_string(long int bytes)1423 byte_string(long int bytes)
1424 {
1425     char       *a, aa[5];
1426     char       *abbrevs = "GMK";
1427     long        i, ones, tenths;
1428     static char string[50];
1429 
1430     ones   = 0L;
1431     tenths = 0L;
1432 
1433     if(bytes == 0L){
1434         strncpy(string, "0 bytes", sizeof(string));
1435 	string[sizeof(string)-1] = '\0';
1436     }
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;
1444             }
1445         }
1446 
1447         aa[0] = *a;  aa[1] = '\0';
1448 
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");
1453     }
1454 
1455     return(string);
1456 }
1457 
1458 
1459 
1460 /*----------------------------------------------------------------------
1461     Print a string corresponding to the number given:
1462       1st, 2nd, 3rd, 105th, 92342nd....
1463  ----*/
1464 
1465 char *
enth_string(int i)1466 enth_string(int i)
1467 {
1468     static char enth[10];
1469 
1470     enth[0] = '\0';
1471 
1472     switch (i % 10) {
1473 
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;
1480 
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;
1487 
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;
1494 
1495       default:
1496         snprintf(enth, sizeof(enth),"%dth",i);
1497         break;
1498     }
1499     return(enth);
1500 }
1501 
1502 
1503 /*
1504  * Inserts newlines for folding at whitespace.
1505  *
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.
1517  *
1518  * Returns   An allocated string which caller should free.
1519  */
1520 char *
fold(char * src,int width,int maxwidth,char * first_indent,char * indent,unsigned int flags)1521 fold(char *src, int width, int maxwidth, char *first_indent, char *indent, unsigned int flags)
1522 {
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;
1534 
1535     cr          = (flags & FLD_CRLF);
1536     preserve_ws = (flags & FLD_PWS);
1537 
1538     if(indent){
1539 	indent2 = (int) utf8_width(indent);
1540 	nbindent2 = strlen(indent);
1541     }
1542 
1543     if(first_indent){
1544 	indent1 = (int) utf8_width(first_indent);
1545 	nb = strlen(first_indent);
1546     }
1547 
1548     len = indent1;
1549     next_piece = src;
1550     eol = cr ? 2 : 1;
1551     if(!src || !*src)
1552       nb += eol;
1553 
1554     /*
1555      * We can't tell how much space is going to be needed without actually
1556      * passing through the data to see.
1557      */
1558     while(next_piece && *next_piece){
1559 	if(next_piece != src && indent2){
1560 	    len += indent2;
1561 	    nb += nbindent2;
1562 	}
1563 
1564 	this_width = (int) utf8_width(next_piece);
1565 	if(this_width + len <= width){
1566 	    nb += (strlen(next_piece) + eol);
1567 	    break;
1568 	}
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++){
1576 
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;
1581 		}
1582 
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;
1588 		}
1589 	    }
1590 
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;
1596 		}
1597 		if(winner == -1){
1598 		   winner = got_width < 512 ? got_width : 512;
1599 		   endptr = NULL;
1600 		}
1601 	    }
1602 
1603 	    if(winner == -1){ /* if no good folding spot, fold at width */
1604 		winner = starting_point;
1605 		endptr = NULL;
1606 	    }
1607 
1608 	    if(endptr == NULL){
1609 		endptr = utf8_count_forw_width(next_piece, (unsigned) winner, &got_width);
1610 		winner = (int) got_width;
1611 	    }
1612 
1613 	    nb += ((endptr - next_piece) + eol);
1614 	    next_piece = endptr;
1615 	    if(!preserve_ws && isspace((unsigned char) next_piece[0]))
1616 	      next_piece++;
1617 	}
1618 
1619 	len = 0;
1620     }
1621 
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;
1627 
1628     while(next_piece && *next_piece){
1629 	if(next_piece != src && indent2){
1630 	    sstrncpy(&p, indent, nb+1-(p-res));
1631 	    len += indent2;
1632 	}
1633 
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';
1639 
1640 	    if(p-res < nb+1)
1641 	      *p++ = '\n';
1642 
1643 	    break;
1644 	}
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++){
1652 
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;
1657 		}
1658 
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;
1664 		}
1665 	    }
1666 
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;
1672 		}
1673 		if(winner == -1){
1674 		   winner = got_width < 512 ? got_width : 512;
1675 		   endptr = NULL;
1676 		}
1677 	    }
1678 
1679 	    if(winner == -1){ /* if no good folding spot, fold at width */
1680 		winner = starting_point;
1681 		endptr = NULL;
1682 	    }
1683 
1684 	    if(endptr == NULL){
1685 		endptr = utf8_count_forw_width(next_piece, (unsigned) winner, &got_width);
1686 		winner = (int) got_width;
1687 	    }
1688 
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;
1695 	    }
1696 
1697 	    if(cr && p-res < nb+1)
1698 	      *p++ = '\r';
1699 
1700 	    if(p-res < nb+1)
1701 	      *p++ = '\n';
1702 
1703 	    if(!preserve_ws && isspace((unsigned char) next_piece[0]))
1704 	      next_piece++;
1705 	}
1706 
1707 	len = 0;
1708     }
1709 
1710     if(!src || !*src){
1711 	if(cr && p-res < nb+1)
1712 	  *p++ = '\r';
1713 
1714 	if(p-res < nb+1)
1715 	  *p++ = '\n';
1716     }
1717 
1718     if(p-res < nb+1)
1719       *p = '\0';
1720 
1721     res[nb] = '\0';
1722 
1723     return(res);
1724 }
1725 
1726 
1727 /*
1728  * strsquish - fancifies a string into the given buffer if it's too
1729  *	       long to fit in the given width
1730  */
1731 char *
strsquish(char * buf,size_t buflen,char * src,int width)1732 strsquish(char *buf, size_t buflen, char *src, int width)
1733 {
1734     /*
1735      * Replace strsquish() with calls to short_str().
1736      */
1737     if(width > 14)
1738       return(short_str(src, buf, buflen, width, MidDots));
1739     else
1740       return(short_str(src, buf, buflen, width, FrontDots));
1741 }
1742 
1743 
1744 char *
long2string(long int l)1745 long2string(long int l)
1746 {
1747     static char string[20];
1748 
1749     snprintf(string, sizeof(string), "%ld", l);
1750     return(string);
1751 }
1752 
1753 
1754 char *
ulong2string(unsigned long int l)1755 ulong2string(unsigned long int l)
1756 {
1757     static char string[20];
1758 
1759     snprintf(string, sizeof(string), "%lu", l);
1760     return(string);
1761 }
1762 
1763 
1764 char *
int2string(int i)1765 int2string(int i)
1766 {
1767     static char string[20];
1768 
1769     snprintf(string, sizeof(string), "%d", i);
1770     return(string);
1771 }
1772 
1773 
1774 /*
1775  * strtoval - convert the given string to a positive integer.
1776  */
1777 char *
strtoval(char * s,int * val,int minmum,int maxmum,int otherok,char * errbuf,size_t errbuflen,char * varname)1778 strtoval(char *s, int *val, int minmum, int maxmum, int otherok, char *errbuf,
1779 	 size_t errbuflen, char *varname)
1780 {
1781     int   i = 0, neg = 1;
1782     char *p = s, *errstr = NULL;
1783 
1784     removing_leading_and_trailing_white_space(p);
1785     for(; *p; p++)
1786       if(isdigit((unsigned char) *p)){
1787 	  i = (i * 10) + (*p - '0');
1788       }
1789       else if(*p == '-' && i == 0){
1790 	  neg = -1;
1791       }
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);
1797       }
1798 
1799     i *= neg;
1800 
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;
1813 
1814     return(errstr);
1815 }
1816 
1817 
1818 /*
1819  * strtolval - convert the given string to a positive _long_ integer.
1820  */
1821 char *
strtolval(char * s,long int * val,long int minmum,long int maxmum,long int otherok,char * errbuf,size_t errbuflen,char * varname)1822 strtolval(char *s, long int *val, long int minmum, long int maxmum, long int otherok,
1823 	  char *errbuf, size_t errbuflen, char *varname)
1824 {
1825     long  i = 0, neg = 1L;
1826     char *p = s, *errstr = NULL;
1827 
1828     removing_leading_and_trailing_white_space(p);
1829     for(; *p; p++)
1830       if(isdigit((unsigned char) *p)){
1831 	  i = (i * 10L) + (*p - '0');
1832       }
1833       else if(*p == '-' && i == 0L){
1834 	  neg = -1L;
1835       }
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);
1841       }
1842 
1843     i *= neg;
1844 
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;
1857 
1858     return(errstr);
1859 }
1860 
1861 
1862 /*
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.
1868  *
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_.
1879  */
1880 void
get_pair(char * string,char ** label,char ** value,int firstws,int strip_internal_label_quotes)1881 get_pair(char *string, char **label, char **value, int firstws, int strip_internal_label_quotes)
1882 {
1883     char *p, *q, *tmp, *token = NULL;
1884     int	  quoted = 0;
1885 
1886     *label = *value = NULL;
1887 
1888     /*
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.
1894      */
1895     for(p = string; p && *p;){
1896 	if(*p == '"')				/* quoted label? */
1897 	  quoted = (quoted) ? 0 : 1;
1898 
1899 	if(*p == '\\' && *(p+1) == '"')		/* escaped quote? */
1900 	  p++;					/* skip it... */
1901 
1902 	if(isspace((unsigned char)*p) && !quoted){	/* if space,  */
1903 	    while(*++p && isspace((unsigned char)*p))	/* move past it */
1904 	      ;
1905 
1906 	    if(!firstws || !token)
1907 	      token = p;			/* remember start of text */
1908 	}
1909 	else
1910 	  p++;
1911     }
1912 
1913     if(token){					/* copy label */
1914 	*label = p = (char *)fs_get(((token - string) + 1) * sizeof(char));
1915 
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';
1920 
1921 	removing_leading_and_trailing_white_space(tmp);
1922 	quoted = removing_double_quotes(tmp);
1923 
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;
1929 	}
1930 
1931 	*p = '\0';				/* tie off label */
1932 	fs_give((void **)&tmp);
1933 	if(*label == NULL)
1934 	  fs_give((void **)label);
1935     }
1936     else
1937       token = string;
1938 
1939     if(token){					/* copy value */
1940 	*value = p = (char *)fs_get((strlen(token) + 1) * sizeof(char));
1941 
1942 	tmp = cpystr(token);
1943 	removing_leading_and_trailing_white_space(tmp);
1944 	quoted = removing_double_quotes(tmp);
1945 
1946 	for(q = tmp; *q ; q++){
1947 	    if(quoted && *q == '\\' && (*(q+1) == '"' || *(q+1) == '\\'))
1948 	      *p++ = *++q;
1949 	    else
1950 	      *p++ = *q;
1951 	}
1952 
1953 	*p = '\0';				/* tie off value */
1954 	fs_give((void **)&tmp);
1955     }
1956 }
1957 
1958 
1959 /*
1960  *  This is sort of the inverse of get_pair.
1961  *
1962  * Args --  label -- the first half of the string
1963  *          value -- the last half of the string
1964  *
1965  * Returns -- an allocated string which is "label" SPACE "value"
1966  *
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.
1970  */
1971 char *
put_pair(char * label,char * value)1972 put_pair(char *label, char *value)
1973 {
1974     char *result, *lab = label, *val = value;
1975     size_t l;
1976 
1977     if(label && *label)
1978       lab = quote_if_needed(label);
1979 
1980     if(value && *value)
1981       val = quote_if_needed(value);
1982 
1983     l = strlen(lab) + strlen(val) +1;
1984     result = (char *) fs_get((l+1) * sizeof(char));
1985 
1986     snprintf(result, l+1, "%s%s%s",
1987 	    lab ? lab : "",
1988 	    (lab && lab[0] && val && val[0]) ? " " : "",
1989 	    val ? val : "");
1990 
1991     if(lab && lab != label)
1992       fs_give((void **)&lab);
1993     if(val && val != value)
1994       fs_give((void **)&val);
1995 
1996     return(result);
1997 }
1998 
1999 
2000 /*
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.
2004  */
2005 char *
quote_if_needed(char * src)2006 quote_if_needed(char *src)
2007 {
2008     char *result = src, *qsrc = NULL;
2009 
2010     if(src && *src){
2011 	/* need quoting? */
2012 	if(strpbrk(src, " \t") != NULL)
2013 	  qsrc = add_escapes(src, "\\\"", '\\', "", "");
2014 
2015 	if(qsrc && !*qsrc)
2016 	  fs_give((void **)&qsrc);
2017 
2018 	if(qsrc){
2019 	    size_t l;
2020 
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);
2025 	}
2026     }
2027 
2028     return(result);
2029 }
2030 
2031 
2032 /*
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.
2036  */
2037 int
read_octal(char ** s)2038 read_octal(char **s)
2039 {
2040     register int i, j;
2041 
2042     i = 0;
2043     for(j = 0; j < 3 && **s >= '0' && **s < '8' ; (*s)++, j++)
2044       i = (i * 8) + (int)(unsigned char)**s - '0';
2045 
2046     return(i);
2047 }
2048 
2049 
2050 /*
2051  * Convert two consecutive HEX digits to an integer.  First two
2052  * chars pointed to by "s" MUST already be tested for hexness.
2053  */
2054 int
read_hex(char * s)2055 read_hex(char *s)
2056 {
2057     return(X2C(s));
2058 }
2059 
2060 
2061 /*
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.
2064  */
2065 void
char_to_octal_triple(int c,char * octal)2066 char_to_octal_triple(int c, char *octal)
2067 {
2068     c &= 0xff;
2069 
2070     octal[2] = (c % 8) + '0';
2071     c /= 8;
2072     octal[1] = (c % 8) + '0';
2073     c /= 8;
2074     octal[0] = c + '0';
2075 }
2076 
2077 
2078 /*
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.
2083  *
2084  * Returns allocated C string version of s.
2085  */
2086 char *
string_to_cstring(char * s)2087 string_to_cstring(char *s)
2088 {
2089     char *b, *p;
2090     int   n, i, all_space_so_far = 1;
2091 
2092     if(!s)
2093       return(cpystr(""));
2094 
2095     n = 20;
2096     b = (char *)fs_get((n+1) * sizeof(char));
2097     p  = b;
2098     *p = '\0';
2099     i  = 0;
2100 
2101     while(*s){
2102 	if(*s != SPACE)
2103 	  all_space_so_far = 0;
2104 
2105 	if(i + 4 > n){
2106 	    /*
2107 	     * The output string may overflow the output buffer.
2108 	     * Make more room.
2109 	     */
2110 	    n += 20;
2111 	    fs_resize((void **)&b, (n+1) * sizeof(char));
2112 	    p = &b[i];
2113 	}
2114 	else{
2115 	    switch(*s){
2116 	      case '\n':
2117 		*p++ = '\\';
2118 		*p++ = 'n';
2119 		i += 2;
2120 		break;
2121 
2122 	      case '\r':
2123 		*p++ = '\\';
2124 		*p++ = 'r';
2125 		i += 2;
2126 		break;
2127 
2128 	      case '\t':
2129 		*p++ = '\\';
2130 		*p++ = 't';
2131 		i += 2;
2132 		break;
2133 
2134 	      case '\b':
2135 		*p++ = '\\';
2136 		*p++ = 'b';
2137 		i += 2;
2138 		break;
2139 
2140 	      case '\f':
2141 		*p++ = '\\';
2142 		*p++ = 'f';
2143 		i += 2;
2144 		break;
2145 
2146 	      case '\\':
2147 		*p++ = '\\';
2148 		*p++ = '\\';
2149 		i += 2;
2150 		break;
2151 
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;
2159 		}
2160 		else{
2161 		    /* fall through */
2162 		}
2163 
2164 
2165 	      default:
2166 		if(*s >= SPACE && *s < '~' && *s != '\"' && *s != '$'){
2167 		    *p++ = *s;
2168 		    i++;
2169 		}
2170 		else{  /* use octal output */
2171 		    *p++ = '\\';
2172 		    char_to_octal_triple(*s, p);
2173 		    p += 3;
2174 		    i += 4;
2175 		}
2176 
2177 		break;
2178 	    }
2179 
2180 	    s++;
2181 	}
2182     }
2183 
2184     *p = '\0';
2185     return(b);
2186 }
2187 
2188 
2189 /*
2190  * Convert C-style string, with backslash escapes, into a hex string, two
2191  * hex digits per character.
2192  *
2193  * Returns allocated hexstring version of s.
2194  */
2195 char *
cstring_to_hexstring(char * s)2196 cstring_to_hexstring(char *s)
2197 {
2198     char *b, *p;
2199     int   n, i, c;
2200 
2201     if(!s)
2202       return(cpystr(""));
2203 
2204     n = 20;
2205     b = (char *)fs_get((n+1) * sizeof(char));
2206     p  = b;
2207     *p = '\0';
2208     i  = 0;
2209 
2210     while(*s){
2211 	if(i + 2 > n){
2212 	    /*
2213 	     * The output string may overflow the output buffer.
2214 	     * Make more room.
2215 	     */
2216 	    n += 20;
2217 	    fs_resize((void **)&b, (n+1) * sizeof(char));
2218 	    p = &b[i];
2219 	}
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;
2230 
2231 		  case 'r':
2232 		    c = '\r';
2233 		    C2XPAIR(c, p);
2234 		    i += 2;
2235 		    s++;
2236 		    break;
2237 
2238 		  case 't':
2239 		    c = '\t';
2240 		    C2XPAIR(c, p);
2241 		    i += 2;
2242 		    s++;
2243 		    break;
2244 
2245 		  case 'v':
2246 		    c = '\v';
2247 		    C2XPAIR(c, p);
2248 		    i += 2;
2249 		    s++;
2250 		    break;
2251 
2252 		  case 'b':
2253 		    c = '\b';
2254 		    C2XPAIR(c, p);
2255 		    i += 2;
2256 		    s++;
2257 		    break;
2258 
2259 		  case 'f':
2260 		    c = '\f';
2261 		    C2XPAIR(c, p);
2262 		    i += 2;
2263 		    s++;
2264 		    break;
2265 
2266 		  case 'a':
2267 		    c = '\007';
2268 		    C2XPAIR(c, p);
2269 		    i += 2;
2270 		    s++;
2271 		    break;
2272 
2273 		  case '\\':
2274 		    c = '\\';
2275 		    C2XPAIR(c, p);
2276 		    i += 2;
2277 		    s++;
2278 		    break;
2279 
2280 		  case '?':
2281 		    c = '?';
2282 		    C2XPAIR(c, p);
2283 		    i += 2;
2284 		    s++;
2285 		    break;
2286 
2287 		  case '\'':
2288 		    c = '\'';
2289 		    C2XPAIR(c, p);
2290 		    i += 2;
2291 		    s++;
2292 		    break;
2293 
2294 		  case '\"':
2295 		    c = '\"';
2296 		    C2XPAIR(c, p);
2297 		    i += 2;
2298 		    s++;
2299 		    break;
2300 
2301 		  case 0: /* reached end of s too early */
2302 		    c = 0;
2303 		    C2XPAIR(c, p);
2304 		    i += 2;
2305 		    s++;
2306 		    break;
2307 
2308 		  /* hex number */
2309 		  case 'x':
2310 		    s++;
2311 		    if(isxpair(s)){
2312 			c = X2C(s);
2313 			s += 2;
2314 		    }
2315 		    else if(isxdigit((unsigned char)*s)){
2316 			c = XDIGIT2C(*s);
2317 			s++;
2318 		    }
2319 		    else
2320 		      c = 0;
2321 
2322 		    C2XPAIR(c, p);
2323 		    i += 2;
2324 
2325 		    break;
2326 
2327 		  /* octal number */
2328 		  default:
2329 		    c = read_octal(&s);
2330 		    C2XPAIR(c, p);
2331 		    i += 2;
2332 
2333 		    break;
2334 		}
2335 	    }
2336 	    else{
2337 		C2XPAIR(*s, p);
2338 		i += 2;
2339 		s++;
2340 	    }
2341 	}
2342     }
2343 
2344     *p = '\0';
2345     return(b);
2346 }
2347 
2348 
2349 /*
2350  * Convert C-style string, with backslash escapes, into a regular string.
2351  * Result goes in dst, which should be as big as src.
2352  *
2353  */
2354 void
cstring_to_string(char * src,char * dst)2355 cstring_to_string(char *src, char *dst)
2356 {
2357     char *p;
2358     int   c;
2359 
2360     dst[0] = '\0';
2361     if(!src)
2362       return;
2363 
2364     p  = dst;
2365 
2366     while(*src){
2367 	if(*src == '\\'){
2368 	    src++;
2369 	    switch(*src){
2370 	      case 'n':
2371 		*p++ = '\n';
2372 		src++;
2373 		break;
2374 
2375 	      case 'r':
2376 		*p++ = '\r';
2377 		src++;
2378 		break;
2379 
2380 	      case 't':
2381 		*p++ = '\t';
2382 		src++;
2383 		break;
2384 
2385 	      case 'v':
2386 		*p++ = '\v';
2387 		src++;
2388 		break;
2389 
2390 	      case 'b':
2391 		*p++ = '\b';
2392 		src++;
2393 		break;
2394 
2395 	      case 'f':
2396 		*p++ = '\f';
2397 		src++;
2398 		break;
2399 
2400 	      case 'a':
2401 		*p++ = '\007';
2402 		src++;
2403 		break;
2404 
2405 	      case '\\':
2406 		*p++ = '\\';
2407 		src++;
2408 		break;
2409 
2410 	      case '?':
2411 		*p++ = '?';
2412 		src++;
2413 		break;
2414 
2415 	      case '\'':
2416 		*p++ = '\'';
2417 		src++;
2418 		break;
2419 
2420 	      case '\"':
2421 		*p++ = '\"';
2422 		src++;
2423 		break;
2424 
2425 	      case 0: /* reached end of s too early */
2426 		src++;
2427 		break;
2428 
2429 	      /* hex number */
2430 	      case 'x':
2431 		src++;
2432 		if(isxpair(src)){
2433 		    c = X2C(src);
2434 		    src += 2;
2435 		}
2436 		else if(isxdigit((unsigned char)*src)){
2437 		    c = XDIGIT2C(*src);
2438 		    src++;
2439 		}
2440 		else
2441 		  c = 0;
2442 
2443 		*p++ = c;
2444 
2445 		break;
2446 
2447 	      /* octal number */
2448 	      default:
2449 		c = read_octal(&src);
2450 		*p++ = c;
2451 		break;
2452 	    }
2453 	}
2454 	else
2455 	  *p++ = *src++;
2456     }
2457 
2458     *p = '\0';
2459 }
2460 
2461 
2462 /*
2463  * Quotes /'s and \'s with \
2464  *
2465  * Args: src -- The source string.
2466  *
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 \".
2470  *
2471  *   The caller is responsible for freeing the memory allocated for the answer.
2472  */
2473 char *
add_backslash_escapes(char * src)2474 add_backslash_escapes(char *src)
2475 {
2476     return(add_escapes(src, "/\\\"", '\\', "", ""));
2477 }
2478 
2479 
2480 /*
2481  * Undoes backslash quoting of source string.
2482  *
2483  * Args: src -- The source string.
2484  *
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.
2491  *
2492  *   The caller is responsible for freeing the memory allocated for the answer.
2493  */
2494 char *
remove_backslash_escapes(char * src)2495 remove_backslash_escapes(char *src)
2496 {
2497     char *ans = NULL, *q, *p;
2498     int done = 0;
2499 
2500     if(src){
2501 	p = q = (char *)fs_get(strlen(src) + 1);
2502 
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;
2514 		    }
2515 		    else
2516 		      *p++ = *src++;
2517 		}
2518 
2519 		break;
2520 
2521 	      case '\0':
2522 	      case '/':
2523 		done++;
2524 		break;
2525 
2526 	      default:
2527 		*p++ = *src++;
2528 		break;
2529 	    }
2530 	}
2531 
2532 	*p = '\0';
2533 
2534 	ans = cpystr(q);
2535 	fs_give((void **)&q);
2536     }
2537 
2538     return(ans);
2539 }
2540 
2541 
2542 /*
2543  * Quote values for viewer-hdr-colors. We quote backslash, comma, and slash.
2544  *  Also replaces $ with $$.
2545  *
2546  * Args: src -- The source string.
2547  *
2548  * Returns: A string with backslash quoting added.
2549  *
2550  *   The caller is responsible for freeing the memory allocated for the answer.
2551  */
2552 char *
add_viewerhdr_escapes(char * src)2553 add_viewerhdr_escapes(char *src)
2554 {
2555     char *tmp, *ans = NULL;
2556 
2557     tmp = add_escapes(src, "/\\", '\\', ",", "");
2558 
2559     if(tmp){
2560 	ans = dollar_escape_dollars(tmp);
2561 	fs_give((void **) &tmp);
2562     }
2563 
2564     return(ans);
2565 }
2566 
2567 
2568 /*
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.
2571  *
2572  * Args: src -- The source string.
2573  *
2574  * Returns: A string with $$ quoting added.
2575  *
2576  *   The caller is responsible for freeing the memory allocated for the answer.
2577  */
2578 char *
dollar_escape_dollars(char * src)2579 dollar_escape_dollars(char *src)
2580 {
2581     return(add_escapes(src, "$", '$', "", ""));
2582 }
2583 
2584 
2585 /*
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.
2590  */
2591 char *
vcard_escape(char * src)2592 vcard_escape(char *src)
2593 {
2594     char *p, *q;
2595 
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';
2603 	  }
2604     }
2605 
2606     return(q);
2607 }
2608 
2609 
2610 /*
2611  * This undoes the vcard backslash quoting.
2612  *
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.
2616  *
2617  *   The caller is responsible for freeing the memory allocated for the answer.
2618  */
2619 char *
vcard_unescape(char * src)2620 vcard_unescape(char *src)
2621 {
2622     char *ans = NULL, *p;
2623     int done = 0;
2624 
2625     if(src){
2626 	p = ans = (char *)fs_get(strlen(src) + 1);
2627 
2628 	while(!done){
2629 	    switch(*src){
2630 	      case '\\':
2631 		src++;
2632 		if(*src == 'n' || *src == 'N'){
2633 		    *p++ = '\n';
2634 		    src++;
2635 		}
2636 		else if(*src)
2637 		  *p++ = *src++;
2638 
2639 		break;
2640 
2641 	      case '\0':
2642 		done++;
2643 		break;
2644 
2645 	      default:
2646 		*p++ = *src++;
2647 		break;
2648 	    }
2649 	}
2650 
2651 	*p = '\0';
2652     }
2653 
2654     return(ans);
2655 }
2656 
2657 
2658 /*
2659  * Turn folded lines into long lines in place.
2660  *
2661  * CRLF whitespace sequences are removed, the space is not preserved.
2662  */
2663 void
vcard_unfold(char * string)2664 vcard_unfold(char *string)
2665 {
2666     char *p = string;
2667 
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++;
2675 
2676     *p = '\0';
2677 }
2678 
2679 
2680 /*
2681  * Quote specified chars with escape char.
2682  *
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)
2690  *
2691  * Returns: An allocated copy of string with quoting added.
2692  *   The caller is responsible for freeing the memory allocated for the answer.
2693  */
2694 char *
add_escapes(char * src,char * quote_these_chars,int quoting_char,char * hex_these_chars,char * hex_these_quoted_chars)2695 add_escapes(char *src, char *quote_these_chars, int quoting_char,
2696 	    char *hex_these_chars, char *hex_these_quoted_chars)
2697 {
2698     char *ans = NULL;
2699 
2700     if(!quote_these_chars)
2701       alpine_panic("bad arg to add_escapes");
2702 
2703     if(src){
2704 	char *q, *p, *qchar;
2705 
2706 	p = q = (char *)fs_get(2*strlen(src) + 1);
2707 
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;
2713 
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 */
2720 	    }
2721 	    else{
2722 		for(qchar = quote_these_chars; *qchar != '\0'; qchar++)
2723 		  if(*src == *qchar)
2724 		    break;
2725 
2726 		if(*qchar){		/* *src is a char to be quoted */
2727 		    *p++ = quoting_char;
2728 		    *p++ = *src++;
2729 		}
2730 		else{
2731 		    for(qchar = hex_these_chars; *qchar != '\0'; qchar++)
2732 		      if(*src == *qchar)
2733 			break;
2734 
2735 		    if(*qchar){		/* *src is a char to be escaped */
2736 			*p++ = '\\';
2737 			*p++ = 'x';
2738 			C2XPAIR(*src, p);
2739 			src++;
2740 		    }
2741 		    else			/* a regular char */
2742 		      *p++ = *src++;
2743 		}
2744 	    }
2745 
2746 	}
2747 
2748 	*p = '\0';
2749 
2750 	ans = cpystr(q);
2751 	fs_give((void **)&q);
2752     }
2753 
2754     return(ans);
2755 }
2756 
2757 
2758 /*
2759  * Copy a string enclosed in "" without fixing \" or \\. Skip past \"
2760  * but copy it as is, removing only the enclosing quotes.
2761  */
2762 char *
copy_quoted_string_asis(char * src)2763 copy_quoted_string_asis(char *src)
2764 {
2765     char *q = NULL, *p;
2766     int   done = 0, quotes = 0;
2767 
2768     if(src){
2769 	p = q = (char *)fs_get(strlen(src) + 1);
2770 
2771 	while(!done){
2772 	    switch(*src){
2773 	      case QUOTE:
2774 		if(++quotes == 2)
2775 		  done++;
2776 		else
2777 		  src++;
2778 
2779 		break;
2780 
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);
2786 		    }
2787 
2788 		    src += 2;
2789 		}
2790 		else{
2791 		    if(quotes == 1)
2792 		      *p++ = *src;
2793 
2794 		    src++;
2795 		}
2796 
2797 		break;
2798 
2799 	      case '\0':
2800 		fs_give((void **)&q);
2801 		return(NULL);
2802 
2803 	      default:
2804 		if(quotes == 1)
2805 		  *p++ = *src;
2806 
2807 		src++;
2808 
2809 		break;
2810 	    }
2811 	}
2812 
2813 	*p = '\0';
2814     }
2815 
2816     return(q);
2817 }
2818 
2819 
2820 /*
2821  * isxpair -- return true if the first two chars in string are
2822  *	      hexadecimal characters
2823  */
2824 int
isxpair(char * s)2825 isxpair(char *s)
2826 {
2827     return(isxdigit((unsigned char) *s) && isxdigit((unsigned char) *(s+1)));
2828 }
2829 
2830 
2831 
2832 
2833 
2834 /*
2835  *  * * * * * *  something to help managing lists of strings   * * * * * * * *
2836  */
2837 
2838 
2839 STRLIST_S *
new_strlist(char * name)2840 new_strlist(char *name)
2841 {
2842   return new_strlist_auth(name, NULL, '\0');
2843 }
2844 
2845 STRLIST_S *
new_strlist_auth(char * name,char * authtype,char sep)2846 new_strlist_auth(char *name, char *authtype, char sep)
2847 {
2848     STRLIST_S *sp = (STRLIST_S *) fs_get(sizeof(STRLIST_S));
2849     int len = authtype ? strlen(authtype) : 0;
2850     int offset = authtype ? 1 : 0;
2851 
2852     memset(sp, 0, sizeof(STRLIST_S));
2853     if(name){
2854       sp->name = fs_get(strlen(name) + len + offset + 1);
2855       sprintf(sp->name, "%s%s%s", authtype ? authtype :  "",
2856 			authtype ? " " : "", name);
2857       if(authtype != NULL) sp->name[len] = sep;
2858     }
2859     return(sp);
2860 }
2861 
2862 
2863 STRLIST_S *
copy_strlist(STRLIST_S * src)2864 copy_strlist(STRLIST_S *src)
2865 {
2866     STRLIST_S *ret = NULL, *sl, *ss, *new_sl;
2867 
2868     if(src){
2869 	ss = NULL;
2870 	for(sl = src; sl; sl = sl->next){
2871 	    new_sl = (STRLIST_S *) fs_get(sizeof(*new_sl));
2872 	    memset((void *) new_sl, 0, sizeof(*new_sl));
2873 	    if(sl->name)
2874 	      new_sl->name = cpystr(sl->name);
2875 
2876 	    if(ss){
2877 		ss->next = new_sl;
2878 		ss = ss->next;
2879 	    }
2880 	    else{
2881 		ret = new_sl;
2882 		ss = ret;
2883 	    }
2884 	}
2885     }
2886 
2887     return(ret);
2888 }
2889 
2890 
2891 /*
2892  * Add the second list to the end of the first.
2893  */
2894 void
combine_strlists(STRLIST_S ** first,STRLIST_S * second)2895 combine_strlists(STRLIST_S **first, STRLIST_S *second)
2896 {
2897     STRLIST_S *sl;
2898 
2899     if(!second)
2900       return;
2901 
2902     if(first){
2903 	if(*first){
2904 	    for(sl = *first; sl->next; sl = sl->next)
2905 	      ;
2906 
2907 	    sl->next = second;
2908 	}
2909 	else
2910 	  *first = second;
2911     }
2912 }
2913 
2914 
2915 void
free_strlist(STRLIST_S ** strp)2916 free_strlist(STRLIST_S **strp)
2917 {
2918     if(strp && *strp){
2919 	if((*strp)->next)
2920 	  free_strlist(&(*strp)->next);
2921 
2922 	if((*strp)->name)
2923 	  fs_give((void **) &(*strp)->name);
2924 
2925 	fs_give((void **) strp);
2926     }
2927 }
2928 
2929 void
convert_decimal_to_roman(char * rn,size_t len,long n,char l)2930 convert_decimal_to_roman (char *rn, size_t len, long n, char l)
2931 {
2932   char symbols[8];
2933   int amo[8];
2934   int i, j, k;
2935 
2936   rn[0] = '\0';
2937   if(n >= 4000L || n <= 0L)
2938     return;
2939 
2940   symbols[0] = l + 'm' - 'i';
2941   symbols[1] = l + 'd' - 'i';
2942   symbols[2] = l + 'c' - 'i';
2943   symbols[3] = l + 'l' - 'i';
2944   symbols[4] = l + 'x' - 'i';
2945   symbols[5] = l + 'v' - 'i';
2946   symbols[6] = l;
2947   symbols[7] = '\0';
2948 
2949   amo[0] = n/1000; n -= amo[0]*1000;
2950   amo[1] = n/500;  n -= amo[1]*500;
2951   amo[2] = n/100;  n -= amo[2]*100;
2952   amo[3] = n/50;   n -= amo[3]*50;
2953   amo[4] = n/10;   n -= amo[4]*10;
2954   amo[5] = n/5;    n -= amo[5]*5;
2955   amo[6] = n;
2956   amo[7] = 0;		/* make valgrind happy */
2957 
2958   for(i = 0, j = 0; i < len && j < strlen(symbols); j++){
2959      if(amo[j] < 4){
2960 	if(amo[j+1] != 4){
2961 	  for(k = 0; k < amo[j]; k++)
2962 	    rn[i++] = symbols[j];
2963 	}
2964      } else {
2965 	if(amo[j-1] == 0){
2966 	  rn[i++] = symbols[j];
2967 	  rn[i++] = symbols[j-1];
2968 	} else {
2969 	  rn[i++] = symbols[j];
2970 	  rn[i++] = symbols[j-2];
2971 	}
2972      }
2973   }
2974   if(i < len) rn[i] = '\0';
2975   rn[len-1] = '\0';
2976 }
2977 
2978 void
convert_decimal_to_alpha(char * rn,size_t len,long n,char l)2979 convert_decimal_to_alpha (char *rn, size_t len, long n, char l)
2980 {
2981   int amo[16];
2982   int i;
2983 
2984   rn[0] = '\0';
2985 
2986   if(n < 0)
2987     return;
2988 
2989   for(i = 0; i < sizeof(amo) && n > 0; i++){
2990      amo[i] = n % 26;
2991           n = (n - amo[i])/26;
2992   }
2993   amo[i] = -1;
2994 
2995   for(i = 0; i < len && amo[i] >= 0; i++)
2996      rn[i] = l + amo[i] - 1;
2997   if(i < len) rn[i] = '\0';
2998   rn[len-1] = '\0';
2999 }
3000