1 /*
2  * This file contains static string helper functions.
3  *
4  * climm Copyright (C) © 2001-2007 Rüdiger Kuhlmann
5  *
6  * climm is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; version 2 dated June, 1991.
9  *
10  * climm is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
13  * License for more details.
14  *
15  * In addition, as a special exception permission is granted to link the
16  * code of this release of climm with the OpenSSL project's "OpenSSL"
17  * library, and distribute the linked executables.  You must obey the GNU
18  * General Public License in all respects for all of the code used other
19  * than "OpenSSL".  If you modify this file, you may extend this exception
20  * to your version of the file, but you are not obligated to do so.  If you
21  * do not wish to do so, delete this exception statement from your version
22  * of this file.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this package; if not, write to the Free Software
26  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
27  * 02111-1307, USA.
28  *
29  * $Id: util_str.c 2829 2009-08-16 19:24:29Z kuhlmann $
30  */
31 
32 #include "climm.h"
33 #include <stdarg.h>
34 #if HAVE_NETDB_H
35 #include <netdb.h>
36 #endif
37 #if HAVE_NETINET_IN_H
38 #include <netinet/in.h>
39 #endif
40 #if HAVE_ARPA_INET_H
41 #include <arpa/inet.h>
42 #endif
43 #if HAVE_WINSOCK2_H
44 #include <winsock2.h>
45 #endif
46 #if HAVE_WCTYPE_H
47 #include <wctype.h>
48 #endif
49 #include <ctype.h>
50 #include <assert.h>
51 #include "util_str.h"
52 #include "conv.h"
53 #include "contact.h"
54 #include "preferences.h"
55 
56 /*
57  * Initialize a dynamically allocated string.
58  *
59  * Returns NULL if not enough memory.
60  */
s_init(str_t str,const char * init,size_t add)61 str_t s_init (str_t str, const char *init, size_t add)
62 {
63     UDWORD initlen, size;
64 
65     initlen = strlen (init);
66     size = ((initlen + add) | 0x7f) + 1;
67 
68     if (!str->txt || size > str->max)
69     {
70         if (str->txt && str->max)
71             free (str->txt);
72         str->txt = malloc (size);
73         if (!str->txt)
74         {
75             str->max = str->len = 0;
76             str->txt = "\0";
77             return NULL;
78         }
79         str->max = size;
80     }
81     str->len = initlen;
82     memcpy (str->txt, init, initlen + 1);
83     return str;
84 }
85 
86 /*
87  * Increase storage size of dynamically allocated string.
88  *
89  * Returns NULL if not enough memory.
90  */
s_blow(str_t str,size_t len)91 str_t s_blow (str_t str, size_t len)
92 {
93     int newlen;
94     char *tmp;
95 
96     newlen = 1 + ((str->max + len - 1) | 0x7f);
97 
98     if (!str->txt || !str->max)
99         tmp = malloc (newlen);
100     else
101         tmp = realloc (str->txt, newlen);
102 
103     if (!tmp)
104     {
105         if (!str->txt || !str->max)
106         {
107             str->max = str->len = 0;
108             str->txt = "\0";
109         }
110         return NULL;
111     }
112     str->txt = tmp;
113     str->max = newlen;
114     return str;
115 }
116 
117 
118 /*
119  * Appends a byte to a dynamically allocated string.
120  */
s_catc(str_t str,char add)121 str_t s_catc (str_t str, char add)
122 {
123     if (str->len + 2 > str->max)
124         if (!s_blow (str, str->len + 2 - str->max))
125             return NULL;
126 
127     str->txt[str->len++] = add;
128     str->txt[str->len] = '\0';
129     return str;
130 }
131 
132 /*
133  * Appends bytes to a dynamically allocated string.
134  */
s_catn(str_t str,const char * add,size_t len)135 str_t s_catn (str_t str, const char *add, size_t len)
136 {
137     if (str->len + len + 2 > str->max)
138         if (!s_blow (str, str->len + len + 2 - str->max))
139             return NULL;
140 
141     memcpy (str->txt + str->len, add, len);
142     str->len += len;
143     str->txt[str->len] = '\0';
144     return str;
145 }
146 
147 /*
148  * Appends a string to a dynamically allocated string.
149  */
s_cat(str_t str,const char * add)150 str_t s_cat (str_t str, const char *add)
151 {
152     int addlen = strlen (add);
153 
154     if (str->len + addlen + 2 > str->max)
155         if (!s_blow (str, str->len + addlen + 2 - str->max))
156             return NULL;
157 
158     memcpy (str->txt + str->len, add, addlen + 1);
159     str->len += addlen;
160     return str;
161 }
162 
163 
164 /*
165  * Appends a formatted string to a dynamically allocated string.
166  */
s_catf(str_t str,const char * fmt,...)167 str_t s_catf (str_t str, const char *fmt, ...)
168 {
169     va_list args;
170     size_t add = 128;
171     int rc;
172 
173     if (str->max < add || str->len + 2 >= str->max)
174         s_blow (str, add);
175 
176     while (1)
177     {
178         if (str->len + 2 >= str->max)
179             return NULL;
180         str->txt[str->max - 2] = '\0';
181         va_start (args, fmt);
182         rc = vsnprintf (str->txt + str->len, str->max - str->len - 1, fmt, args);
183         va_end (args);
184         if (rc >= 0 && rc < str->max - str->len - 2 && !str->txt[str->max - 2])
185         {
186             str->len += strlen (str->txt + str->len);
187             return str;
188         }
189         add = (rc > 0 ? rc - str->max + str->len + 5 : str->max);
190         if (!s_blow (str, add))
191         {
192             str->txt[str->max - 2] = '\0';
193             str->len = str->max - 2;
194             return str;
195         }
196     }
197     return str;
198 }
199 
200 /*
201  * Insert a string into a dynamically allocated string.
202  */
s_insn(str_t str,size_t pos,const char * ins,size_t len)203 str_t s_insn (str_t str, size_t pos, const char *ins, size_t len)
204 {
205     if (pos > str->len || pos < 0)
206         return NULL;
207     if (str->len + len + 2 > str->max)
208         if (!s_blow (str, str->len + len + 2 - str->max))
209             return NULL;
210 
211     memmove (str->txt + pos + len, str->txt + pos, str->len - pos + 1);
212     memcpy (str->txt + pos, ins, len);
213     str->len += len;
214     return str;
215 }
216 
217 /*
218  * Insert a byte into a dynamically allocated string.
219  */
s_insc(str_t str,size_t pos,char ins)220 str_t s_insc (str_t str, size_t pos, char ins)
221 {
222     if (pos > str->len || pos < 0)
223         return NULL;
224     if (str->len + 3 > str->max)
225         if (!s_blow (str, str->len + 3 - str->max))
226             return NULL;
227 
228     memmove (str->txt + pos + 1, str->txt + pos, str->len - pos + 1);
229     str->txt[pos] = ins;
230     str->len++;
231     return str;
232 }
233 
234 /*
235  * Delete a byte from a dynamically allocated string.
236  */
s_delc(str_t str,size_t pos)237 str_t s_delc (str_t str, size_t pos)
238 {
239     if (pos > str->len || pos < 0)
240         return NULL;
241     memmove (str->txt + pos, str->txt + pos + 1, str->len - pos);
242     str->len--;
243     return str;
244 }
245 
246 /*
247  * Delete bytes from a dynamically allocated string.
248  */
s_deln(str_t str,size_t pos,size_t len)249 str_t s_deln (str_t str, size_t pos, size_t len)
250 {
251     if (pos + len > str->len)
252         return NULL;
253     memmove (str->txt + pos, str->txt + pos + len, str->len - pos);
254     str->len -= len;
255     return str;
256 }
257 
258 
259 /*
260  * Frees a dynamically allocated string.
261  */
s_done(str_t str)262 void s_done (str_t str)
263 {
264     if (str->txt && str->max)
265         free (str->txt);
266     str->txt = "\0";
267     str->len = str->max = 0;
268 }
269 
270 /*
271  * Return a static formatted string.
272  */
s_sprintf(const char * fmt,...)273 const char *s_sprintf (const char *fmt, ...)
274 {
275     static char *buf = NULL;
276     static int size = 0;
277     va_list args;
278     char *nbuf;
279     int rc, nsize;
280 
281     if (!buf)
282         buf = calloc (1, size = 1024);
283 
284     while (1)
285     {
286         buf[size - 2] = '\0';
287         va_start (args, fmt);
288         rc = vsnprintf (buf, size, fmt, args);
289         va_end (args);
290 
291         if (rc >= 0 && rc < size && !buf[size - 2])
292             break;
293 
294         nsize = (rc > 0 ? rc + 5 : size * 2);
295         nbuf = malloc (nsize);
296         if (!nbuf)
297             break;
298         free (buf);
299         buf = nbuf;
300         size = nsize;
301     }
302     return buf;
303 }
304 
305 /*
306  * Return a static string consisting of the given IP.
307  */
s_ip(UDWORD ip)308 const char *s_ip (UDWORD ip)
309 {
310     struct sockaddr_in sin;
311     sin.sin_addr.s_addr = htonl (ip);
312     return s_sprintf ("%s", inet_ntoa (sin.sin_addr));
313 }
314 
315 /*
316  * Return a static string describing the status.
317  */
s_status(status_t status,UDWORD nativestatus)318 const char *s_status (status_t status, UDWORD nativestatus)
319 {
320     static char buf[200];
321 
322     if (status == ims_offline)
323         return i18n (1969, "offline");
324 
325     if (ContactIsInv (status))
326         snprintf (buf, sizeof (buf), "%s-", i18n (1975, "invisible"));
327     else
328         buf[0] = '\0';
329 
330     switch (ContactClearInv (status))
331     {
332         case imr_dnd:     snprintf (buf + strlen (buf), sizeof(buf) - strlen (buf), "%s", i18n (1971, "do not disturb")); break;
333         case imr_occ:     snprintf (buf + strlen (buf), sizeof(buf) - strlen (buf), "%s", i18n (1973, "occupied"));       break;
334         case imr_na:      snprintf (buf + strlen (buf), sizeof(buf) - strlen (buf), "%s", i18n (1974, "not available"));  break;
335         case imr_away:    snprintf (buf + strlen (buf), sizeof(buf) - strlen (buf), "%s", i18n (1972, "away"));           break;
336         case imr_ffc:     snprintf (buf + strlen (buf), sizeof(buf) - strlen (buf), "%s", i18n (1976, "free for chat"));  break;
337         case imr_offline: assert (0);
338         case imr_online:
339             if (buf[0])
340                 buf[strlen (buf) - 1] = '\0';
341             else
342                 snprintf (buf + strlen (buf), sizeof(buf) - strlen (buf), "%s", i18n (1970, "online"));
343     }
344 
345     if (prG->verbose)
346         snprintf (buf + strlen (buf), sizeof(buf) - strlen (buf), " %08lx", nativestatus);
347 
348     return buf;
349 }
350 
351 /*
352  * Return a static string short describing the status.
353  */
s_status_short(status_t status)354 const char *s_status_short (status_t status)
355 {
356     static char buf[20];
357 
358     if (status == ims_offline)
359         return i18n (2621, "off");
360     if (status == ims_inv)
361         return i18n (2622, "inv");
362 
363     if (ContactIsInv (status))
364         snprintf (buf, sizeof (buf), "%s-", i18n (2622, "inv"));
365     else
366         buf[0] = '\0';
367 
368     switch (ContactClearInv (status))
369     {
370         case imr_dnd:     snprintf (buf + strlen (buf), sizeof (buf) - strlen (buf), "%s", i18n (2623, "dnd"));  break;
371         case imr_occ:     snprintf (buf + strlen (buf), sizeof (buf) - strlen (buf), "%s", i18n (2624, "occ"));  break;
372         case imr_na:      snprintf (buf + strlen (buf), sizeof (buf) - strlen (buf), "%s", i18n (2625, "na"));   break;
373         case imr_away:    snprintf (buf + strlen (buf), sizeof (buf) - strlen (buf), "%s", i18n (2626, "away")); break;
374         case imr_ffc:     snprintf (buf + strlen (buf), sizeof (buf) - strlen (buf), "%s", i18n (2627, "ffc"));  break;
375         case imr_offline: assert (0);
376         case imr_online:  snprintf (buf + strlen (buf), sizeof (buf) - strlen (buf), "%s", i18n (2628, "online"));
377     }
378     return buf;
379 }
380 
381 /*
382  * Returns static string describing the given time.
383  */
s_time(time_t * stamp)384 const char *s_time (time_t *stamp)
385 {
386     static str_s str = { NULL, 0, 0 };
387     struct timeval p = {0L, 0L};
388     struct tm now;
389     struct tm *thetime;
390     time_t nowsec;
391     size_t rc = 0;
392 
393     if (stamp && *stamp != NOW)
394     {
395         nowsec = time (NULL);
396         now = *localtime (&nowsec);
397         thetime = localtime (stamp);
398     }
399     else
400     {
401 #ifdef HAVE_GETTIMEOFDAY
402         if (gettimeofday (&p, NULL) == -1)
403 #endif
404         {
405             p.tv_usec = 0L;
406             nowsec = time (NULL);
407         }
408 #ifdef HAVE_GETTIMEOFDAY
409         else
410             nowsec = p.tv_sec;
411 #endif
412         thetime = &now;
413         stamp = NULL;
414         now = *localtime (&nowsec);
415     }
416 
417     s_init (&str, "", 32);
418     while (!rc)
419     {
420         rc = strftime (str.txt, str.max,
421                     thetime->tm_year == now.tm_year
422                  && thetime->tm_mon  == now.tm_mon
423                  && thetime->tm_mday == now.tm_mday
424                  ? "%H:%M:%S" : "%a %b %d %H:%M:%S %Y", thetime); /*"*/
425         if (rc <= 0 || rc >= str.max)
426         {
427             rc = 0;
428             s_blow (&str, 32);
429         }
430         else
431         {
432             str.txt[str.max - 1] = 0;
433             str.len = strlen (str.txt);
434         }
435     }
436     if (prG->verbose > 7)
437         s_catf (&str, ".%.06ld", p.tv_usec);
438     else if (prG->verbose > 1)
439         s_catf (&str, ".%.03ld", p.tv_usec / 1000);
440 
441     return ConvFrom (&str, prG->enc_loc)->txt;
442 }
443 
444 /*
445  * Returns static string describing the given time in strftime format.
446  */
s_strftime(time_t * stamp,const char * fmt,char as_gmt)447 const char *s_strftime (time_t *stamp, const char *fmt, char as_gmt)
448 {
449     static str_s str = { NULL, 0, 0 };
450     struct tm *thetime;
451     size_t rc = 0;
452     char *dotfmt;
453 
454     /* strftime()'s error reporting is incomplete, so make sure a correct result is never empty */
455     dotfmt = malloc (strlen (fmt) + 2);
456     strcpy (dotfmt, fmt);
457     strcat (dotfmt, ".");
458 
459     if (stamp && *stamp != NOW)
460         thetime = as_gmt ? gmtime (stamp) : localtime (stamp);
461     else
462     {
463         time_t nowsec = time (NULL);
464         thetime = as_gmt ? gmtime (&nowsec) : localtime (&nowsec);
465     }
466 
467     s_init (&str, "", 32);
468 
469     while (!rc)
470     {
471         rc = strftime (str.txt, str.max, dotfmt, thetime);
472         if (rc <= 0 || rc >= str.max)
473         {
474             rc = 0;
475             s_blow (&str, 32);
476         }
477         else
478         {
479             str.txt[str.max - 1] = 0;
480             str.len = strlen (str.txt);
481             if (str.len && str.txt [str.len - 1] == '.')
482             {
483                 str.txt [str.len - 1] = 0;
484                 str.len--;
485             }
486         }
487     }
488     s_free (dotfmt);
489 
490     return ConvFrom (&str, prG->enc_loc)->txt;
491 }
492 
493 /*
494  * strtok()/strsep() replacement, use to separate message fields
495  */
s_msgtok(char * txt)496 const char *s_msgtok (char *txt)
497 {
498     static char *str = NULL;
499     char *p, *t;
500 
501     if (txt)
502         str = txt;
503     if (!str)
504         return NULL;
505     if (txt && !*txt)
506     {
507         str = NULL;
508         return NULL;
509     }
510     p = strchr (t = str, Conv0xFE);
511     if (p)
512     {
513         *p = '\0';
514         str = p + 1;
515     }
516     else
517     {
518         str = NULL;
519     }
520     return t;
521 }
522 
523 /*
524  * Indent a string two characters.
525  */
s_ind(const char * str)526 const char *s_ind (const char *str)
527 {
528     static str_s t;
529     UDWORD cnt = 5;
530     const char *p;
531     char *q;
532 
533     if (!str || !*str)
534         return "";
535 
536     for (p = str; *p; p++, cnt++)
537         if (*p == '\n')
538             cnt += 2;
539 
540     s_init (&t, "", cnt);
541     if (t.max < cnt)
542         return "<out of memory>";
543 
544     q = t.txt;
545     *q++ = ' ';
546     *q++ = ' ';
547     for (p = str; *p; )
548         if ((*q++ = *p++) == '\n')
549             if (*p)
550             {
551                 *q++ = ' ';
552                 *q++ = ' ';
553             }
554     *q = '\0';
555     return t.txt;
556 }
557 
558 /*
559  * Count the string length in unicode characters.
560  */
s_strlen(const char * str)561 UDWORD s_strlen (const char *str)
562 {
563     UDWORD c;
564 
565     for (c = 0; *str; str++)
566         if (!(*str & 0x80) || (*str & 0x40))
567             c++;
568     return c;
569 }
570 
571 /*
572  * Count the string length in unicode characters.
573  */
s_strnlen(const char * str,UDWORD len)574 UDWORD s_strnlen (const char *str, UDWORD len)
575 {
576     UDWORD c;
577 
578     for (c = 0; *str && len; str++, len--)
579         if (!(*str & 0x80) || (*str & 0x40))
580             c++;
581     return c;
582 }
583 
584 /*
585  * Gives the byte offset of a character offset in UTF-8.
586  */
s_offset(const char * str,UDWORD offset)587 UDWORD s_offset  (const char *str, UDWORD offset)
588 {
589     int off;
590     for (off = 0; offset && *str; offset--, off++)
591     {
592         if (*str & 0x80)
593         {
594             str++;
595             while (*str && ((*str & 0xc0) == 0x80))
596                 str++, off++;
597         }
598         else
599             str++;
600     }
601     return off;
602 }
603 
604 /*
605  * Split off a certain amount of recoded bytes
606  */
s_split(const char ** input,UBYTE enc,int len)607 strc_t s_split (const char **input, UBYTE enc, int len)
608 {
609     static str_s str = { NULL, 0, 0 };
610     str_s in = { NULL, 0, 0 };
611     strc_t tr;
612     int off, offt, offold, offnl, offin, offmax;
613     UDWORD ucs;
614 
615     in.txt = (char *) *input;
616     in.len = strlen (*input);
617     off = offt = offnl = offin = offmax = 0;
618 
619     while (in.txt[off])
620     {
621         offold = off;
622         ucs = ConvGetUTF8 (&in, &off);
623         tr = ConvToLen (in.txt + offold, enc, off - offold);
624         if (ucs == '\r')
625             offnl = offold;
626         if (!(ucs & ~0xff) && isspace (ucs & 0xff))
627             offin = offold;
628         if (tr->len > len)
629         {
630             off = offold;
631             break;
632         }
633         if (ucs == '\n')
634             offnl = off;
635         if (!iswalnum (ucs))
636             offin = off;
637         len -= tr->len;
638         if (!in.txt[off])
639             offmax = off;
640     }
641     if (offmax)
642         off = offmax;
643     else if (offnl)
644         off = offnl;
645     else if (offin)
646         off = offin;
647     s_init (&str, "", off + 2);
648     memcpy (str.txt, in.txt, off);
649     str.len = off;
650     str.txt[off] = 0;
651     *input += off;
652     return &str;
653 }
654 
655 /*
656  * Replace all occurrences of a substring by another
657  */
s_strrepl(str_t str,const char * old,const char * news)658 void s_strrepl (str_t str, const char *old, const char *news)
659 {
660     char *e;
661     size_t d = 0, olen = strlen (old), nlen = strlen (news);
662 
663     if (str->len >= str->max)
664         s_blow (str, 1);
665     str->txt[str->len] = 0;
666     while ((e = strstr (str->txt + d, old)))
667     {
668         if (str->len + nlen - olen + 1 >= str->max)
669             s_blow (str, nlen - olen);
670         d = e - str->txt;
671         memmove (e + nlen, e + olen, str->len - d - olen + 1);
672         memmove (e, news, nlen);
673         str->len += nlen - olen;
674         if (nlen >= olen)
675             d++; /* prevent endless loops */
676     }
677 }
678 
679 
680 #define noctl(x) ((((x & 0x60) && x != 0x7f)) ? ConvUTF8 (x) : ".")
681 
682 /*
683  * Hex dump to a string with ASCII.
684  */
s_dump(const UBYTE * data,UWORD len)685 const char *s_dump (const UBYTE *data, UWORD len)
686 {
687     static str_s t;
688     UDWORD i, off;
689     const unsigned char *d = (const unsigned char *)data;
690 
691     s_init (&t, "", 100);
692 
693     while (len >= 16)
694     {
695         s_catf (&t, "%02x %02x %02x %02x %02x %02x %02x %02x  "
696                               "%02x %02x %02x %02x %02x %02x %02x %02x  ",
697                     d[0], d[1],  d[2],  d[3],  d[4],  d[5],  d[6],  d[7],
698                     d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]);
699         s_catc (&t, '\"');
700         s_cat (&t, noctl  (d[0]));  s_cat (&t, noctl  (d[1]));
701         s_cat (&t, noctl  (d[2]));  s_cat (&t, noctl  (d[3]));
702         s_cat (&t, noctl  (d[4]));  s_cat (&t, noctl  (d[5]));
703         s_cat (&t, noctl  (d[6]));  s_cat (&t, noctl  (d[7]));
704         s_cat (&t, " ");
705         s_cat (&t, noctl  (d[8]));  s_cat (&t, noctl  (d[9]));
706         s_cat (&t, noctl (d[10]));  s_cat (&t, noctl (d[11]));
707         s_cat (&t, noctl (d[12]));  s_cat (&t, noctl (d[13]));
708         s_cat (&t, noctl (d[14]));  s_cat (&t, noctl (d[15]));
709         s_catc (&t, '"');
710         s_catc (&t, '\n');
711         len -= 16;
712         d += 16;
713     }
714     if (!len)
715         return (t.txt);
716     for (off = i = 0; i <= 16; i++)
717     {
718         if (len)
719         {
720             s_catf (&t, "%02x ", *d++);
721             len--;
722             off++;
723         }
724         else if (i != 16)
725             s_catf (&t, "   ");
726         if (i == 7)
727             s_catc (&t, ' ');
728 
729     }
730     s_catc (&t, ' ');
731     s_catc (&t, '\'');
732     while (off)
733     {
734         s_cat (&t, noctl (*(d - off)));
735         off--;
736     }
737     s_catf (&t, "'\n");
738     return t.txt;
739 }
740 
741 /*
742  * Hex dump to a string without ASCII.
743  */
s_dumpnd(const UBYTE * data,UWORD len)744 const char *s_dumpnd (const UBYTE *data, UWORD len)
745 {
746     static str_s t;
747     UDWORD i;
748     const unsigned char *d = (const unsigned char *)data;
749 
750     s_init (&t, "", 100);
751 
752     while (len > 16)
753     {
754         s_catf (&t, "%02x %02x %02x %02x %02x %02x %02x %02x  "
755                     "%02x %02x %02x %02x %02x %02x %02x %02x\\\n",
756                     d[0], d[1],  d[2],  d[3],  d[4],  d[5],  d[6],  d[7],
757                     d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]);
758         len -= 16;
759         d += 16;
760     }
761     if (len > 10)
762     {
763         for (i = 0; i < len && i < 8; i++)
764         {
765             s_catf (&t, "%02x ", *d++);
766         }
767         s_catc (&t, '\n');
768         len -= i;
769     }
770     for (i = 0; i < 10; i++)
771     {
772         if (len)
773         {
774             s_catf (&t, "%02x ", *d++);
775             len--;
776         }
777         else
778             s_cat (&t, "   ");
779         if (i == 7)
780             s_catc (&t, ' ');
781     }
782     return t.txt;
783 }
784 
785 #define SUPERSAFE "%*+-_.:=@/0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
786 
787 /*
788  * Quote a string for use in a config file
789  */
s_quote(const char * input)790 const char *s_quote (const char *input)
791 {
792     static str_s t;
793     const char *tmp;
794 
795     if (!input || !*input)
796         return "\"\"";
797     for (tmp = input; *tmp; tmp++)
798         if (!strchr (SUPERSAFE, *tmp))
799             break;
800     if (!*tmp)
801         return input;
802 
803     s_init (&t, "", 100);
804 
805     s_catc (&t, '\"');
806     for (tmp = input; *tmp; tmp++)
807     {
808         if (strchr ("\\\"", *tmp))
809         {
810             s_catc (&t, '\\');
811             s_catc (&t, *tmp);
812         }
813         else if (*tmp & 0xe0)
814             s_catc (&t, *tmp);
815         else
816         {
817             s_catc (&t, '\\');
818             s_catc (&t, 'x');
819             s_catc (&t, (*tmp / 16) <= 9 ? ((UBYTE)*tmp / 16) + '0'
820                                      : ((UBYTE)*tmp / 16) - 10 + 'a');
821             s_catc (&t, (*tmp & 15) <= 9 ? (*tmp & 15) + '0'
822                                      : (*tmp & 15) - 10 + 'a');
823         }
824     }
825     s_catc (&t, '\"');
826     return t.txt;
827 }
828 
829 /*
830  * Quote a string for display. May use quotes to make sure text boundary is clear.
831  */
s_cquote(const char * input,const char * color)832 const char *s_cquote (const char *input, const char *color)
833 {
834     static str_s t;
835     const char *tmp;
836 
837     if (!input || !*input)
838         return "\"\"";
839     s_init (&t, "", 100);
840 
841     for (tmp = input; *tmp; tmp++)
842         if (!strchr (SUPERSAFE, *tmp))
843             break;
844     if (!*tmp)
845     {
846         s_cat (&t, color);
847         s_cat (&t, input);
848         s_cat (&t, COLNONE);
849         return t.txt;
850     }
851 
852     s_catc (&t, '\"');
853     s_cat (&t, color);
854     for (tmp = input; *tmp; tmp++)
855     {
856         if (strchr ("\\\"", *tmp))
857         {
858             s_catc (&t, '\\');
859             s_catc (&t, *tmp);
860         }
861         else if (*tmp & 0xe0)
862             s_catc (&t, *tmp);
863         else
864         {
865             s_catc (&t, '\\');
866             s_catc (&t, 'x');
867             s_catc (&t, (*tmp / 16) <= 9 ? ((UBYTE)*tmp / 16) + '0'
868                                      : ((UBYTE)*tmp / 16) - 10 + 'a');
869             s_catc (&t, (*tmp & 15) <= 9 ? (*tmp & 15) + '0'
870                                      : (*tmp & 15) - 10 + 'a');
871         }
872     }
873     s_cat (&t, COLNONE);
874     s_catc (&t, '\"');
875     return t.txt;
876 }
877 
878 /*
879  * Quote a string for message display. Uses quotes only for the empty string.
880  */
s_mquote(const char * input,const char * color,BOOL allownl)881 const char *s_mquote (const char *input, const char *color, BOOL allownl)
882 {
883     static str_s t;
884     const char *tmp;
885 
886     if (!input || !*input)
887         return "\"\"";
888     s_init (&t, "", 100);
889 
890     for (tmp = input; *tmp; tmp++)
891         if (!strchr (SUPERSAFE, *tmp))
892             break;
893     if (!*tmp)
894     {
895         s_cat (&t, color);
896         s_cat (&t, input);
897         s_cat (&t, COLNONE);
898         return t.txt;
899     }
900 
901     s_cat (&t, color);
902     for (tmp = input; *tmp; tmp++)
903     {
904         if (*tmp == '\n' && !tmp[1])
905             ;
906         else if (*tmp & 0xe0 || (*tmp == '\n' && allownl))
907             s_catc (&t, *tmp);
908         else if (*tmp != '\r' || !allownl || tmp[1] != '\n')
909         {
910             s_cat  (&t, COLINVCHAR);
911             s_catc (&t, *tmp - 1 + 'A');
912             s_cat  (&t, color);
913         }
914     }
915     s_cat (&t, COLNONE);
916     return t.txt;
917 }
918 
919