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