WvStreams
strutils.cc
1/*
2 * Worldvisions Weaver Software:
3 * Copyright (C) 1997-2002 Net Integration Technologies, Inc.
4 *
5 * Various useful string-based utilities.
6 *
7 */
8#include "strutils.h"
9#include "wvbuf.h"
10#include <ctype.h>
11#include <stdio.h>
12#include <string.h>
13#include <time.h>
14#include <errno.h>
15
16#ifndef _WIN32
17//#include <uuid.h>
18#include <errno.h>
19#include <netdb.h>
20#include <unistd.h>
21#else
22#undef errno
23#define errno GetLastError()
24#define strcasecmp _stricmp
25#include <winsock2.h>
26#include <direct.h>
27#ifndef EACCES
28#define EACCES 0xfff
29#endif
30#endif
31
32char *terminate_string(char *string, char c)
33/**********************************************/
34// Add character c to the end of a string after removing crlf's.
35// NOTE: You need a buffer that's at least one character bigger than the
36// current length of the string, including the terminating NULL.
37{
38 char *p;
39
40 if (string == NULL)
41 return NULL;
42
43 p = string + strlen(string) - 1;
44 while (p >= string)
45 {
46 if (*p == '\r' || *p == '\n')
47 --p;
48 else
49 break;
50 }
51
52 *(++p) = c;
53 *(++p) = 0;
54
55 return string;
56}
57
58
59char *trim_string(char *string)
60/*********************************/
61// Trims spaces off the front and end of strings. Modifies the string.
62// Specifically DOES allow string==NULL; returns NULL in that case.
63{
64 char *p;
65 char *q;
66
67 if (string == NULL)
68 return NULL;
69
70 p = string;
71 q = string + strlen(string) - 1;
72
73 while (q >= p && isspace(*q))
74 *(q--) = 0;
75 while (isspace(*p))
76 p++;
77
78 return p;
79}
80
81
82char *trim_string(char *string, char c)
83// Searches the string for c and removes it plus everything afterwards.
84// Modifies the string and returns NULL if string == NULL.
85{
86 char *p;
87
88 if (string == NULL)
89 return NULL;
90
91 p = string;
92
93 while (*p != 0 && *p != c)
94 p++;
95
96 while (*p)
97 *(p++) = 0;
98
99 return string;
100}
101
102
103// return the string formed by concatenating string 'a' and string 'b' with
104// the 'sep' character between them. For example,
105// spacecat("xx", "yy", ";")
106// returns "xx;yy", and
107// spacecat("xx;;", "yy", ";")
108// returns "xx;;;yy", and
109// spacecat("xx;;", "yy", ";", true)
110// returns "xx;yy".
111//
112// This function is much faster than the more obvious WvString("%s;%s", a, b),
113// so it's useful when you're producing a *lot* of string data.
114WvString spacecat(WvStringParm a, WvStringParm b, char sep, bool onesep)
115{
116 size_t alen = strlen(a);
117 size_t blen = strlen(b);
118
119 // If we only want one separator, eat away at the back of string a
120 if (onesep && alen)
121 {
122 while (a[alen-1] == sep)
123 --alen;
124 }
125
126 // Create the destination string, and give it an appropriate size.
127 // Then, fill it with string a.
128 WvString s;
129 s.setsize(alen + blen + 2);
130 char *cptr = s.edit();
131
132 memcpy(cptr, a, alen);
133
134 // Write the separator in the appropriate spot.
135 cptr[alen] = sep;
136
137 // If we only want one separator, eat away at the from of string b.
138 size_t boffset = 0;
139 if (onesep)
140 {
141 while (b[boffset] == sep)
142 ++boffset;
143 }
144
145 // Now copy the second half of the string in and terminate with a NUL.
146 memcpy(cptr+alen+1, b.cstr()+boffset, blen-boffset);
147 cptr[alen+1+blen-boffset] = 0;
148
149 return s;
150}
151
152
153// Replaces whitespace characters with nonbreaking spaces.
154char *non_breaking(const char * string)
155{
156 if (string == NULL)
157 return (NULL);
158
159 WvDynBuf buf;
160
161 while (*string)
162 {
163 if (isspace(*string))
164 buf.putstr("&nbsp;");
165 else
166 buf.putch(*string);
167 string++;
168 }
169
170 WvString s(buf.getstr());
171 char *nbstr = new char[s.len() + 1];
172 return strcpy(nbstr, s.edit());
173}
174
175
176// Searches _string (up to length bytes), replacing any occurrences of c1
177// with c2.
178void replace_char(void *_string, char c1, char c2, int length)
179{
180 char *string = (char *)_string;
181 for (int i=0; i < length; i++)
182 if (*(string+i) == c1)
183 *(string+i) = c2;
184}
185
186// Snip off the first part of 'haystack' if it consists of 'needle'.
187char *snip_string(char *haystack, char *needle)
188{
189 if(!haystack)
190 return NULL;
191 if(!needle)
192 return haystack;
193 char *p = strstr(haystack, needle);
194 if(!p || p != haystack)
195 return haystack;
196 else
197 return haystack + strlen(needle);
198}
199
200
201char *strlwr(char *string)
202{
203 char *p = string;
204 while (p && *p)
205 {
206 *p = tolower(*p);
207 p++;
208 }
209
210 return string;
211}
212
213
214char *strupr(char *string)
215{
216 char *p = string;
217 while (p && *p)
218 {
219 *p = toupper(*p);
220 p++;
221 }
222
223 return string;
224}
225
226
227// true if all the characters in "string" are isalnum().
228bool is_word(const char *p)
229{
230 assert(p);
231
232 while (*p)
233 {
234 if(!isalnum(*p++))
235 return false;
236 }
237
238 return true;
239}
240
241
242// produce a hexadecimal dump of the data buffer in 'buf' of length 'len'.
243// it is formatted with 16 bytes per line; each line has an address offset,
244// hex representation, and printable representation.
245WvString hexdump_buffer(const void *_buf, size_t len, bool charRep)
246{
247 const unsigned char *buf = (const unsigned char *)_buf;
248 size_t count, count2, top;
249 WvString out;
250
251 out.setsize(len / 16 * 80 + 80);
252 char *cptr = out.edit();
253
254 for (count = 0; count < len; count+=16)
255 {
256 top = len-count < 16 ? len-count : 16;
257 cptr += sprintf(cptr, "[%03X] ", (unsigned int)count);
258
259 // dump hex values
260 for (count2 = 0; count2 < top; count2++)
261 {
262 if (count2 && !(count2 % 4))
263 *cptr++ = ' ';
264 cptr += sprintf(cptr, "%02X", buf[count+count2]);
265 }
266
267 // print horizontal separation
268 for (count2 = top; count2 < 16; count2++)
269 {
270 if (count2 && !(count2 % 4))
271 {
272 strcat(cptr, " ");
273 cptr += 3;
274 }
275 else
276 {
277 strcat(cptr, " ");
278 cptr += 2;
279 }
280 }
281
282 *cptr++ = ' ';
283
284 // dump character representation
285 if (charRep)
286 {
287 for (count2 = 0; count2 < top; count2++)
288 {
289 if (!(count2 % 4))
290 *cptr++ = ' ';
291 *cptr++ = (isprint(buf[count+count2])
292 ? buf[count+count2] : '.');
293 }
294 }
295
296 *cptr++ = '\n';
297 }
298 *cptr = 0;
299 return out;
300}
301
302
303// return true if the character is a newline.
304bool isnewline(char c)
305{
306 return c=='\n' || c=='\r';
307}
308
309
310// ex: WvString foo = url_decode("I+am+text.%0D%0A");
312{
313 if (!str)
314 return str;
315
316 const char *iptr;
317 char *optr;
318 const char *idx1, *idx2;
319 static const char hex[] = "0123456789ABCDEF";
320 WvString in, intmp(str), out;
321
322 in = trim_string(intmp.edit());
323 out.setsize(strlen(in) + 1);
324
325 optr = out.edit();
326 for (iptr = in, optr = out.edit(); *iptr; iptr++)
327 {
328 if (*iptr == '+' && !no_space)
329 *optr++ = ' ';
330 else if (*iptr == '%' && iptr[1] && iptr[2])
331 {
332 idx1 = strchr(hex, toupper((unsigned char) iptr[1]));
333 idx2 = strchr(hex, toupper((unsigned char) iptr[2]));
334
335 if (idx1 && idx2)
336 *optr++ = ((idx1 - hex) << 4) | (idx2 - hex);
337
338 iptr += 2;
339 }
340 else
341 *optr++ = *iptr;
342 }
343
344 *optr = 0;
345
346 return out;
347}
348
349
350// And its magic companion: url_encode
352{
353 unsigned int i;
354 WvDynBuf retval;
355
356 for (i=0; i < str.len(); i++)
357 {
358 if (((!!unsafe && !strchr(unsafe, str[i])) ||
359 (!unsafe && (isalnum(str[i]) || strchr("_.!~*'()-", str[i])))) &&
360 str[i] != '%')
361 {
362 retval.put(&str[i], 1);
363 }
364 else
365 {
366 char buf[4];
367 sprintf(buf, "%%%02X", str[i] & 0xff);
368 retval.put(&buf, 3);
369 }
370 }
371
372 return retval.getstr();
373}
374
375
376WvString diff_dates(time_t t1, time_t t2)
377{
378 char out[25]; //Should be more then enough
379 double diff = difftime(t1, t2);
380 if(diff < 0)
381 diff = -diff;
382 if(diff > (60 * 60 * 24))
383 //give a touch more granularity then the rest
384 sprintf(out, "%.1f day(s)", diff / (60 * 60 * 24));
385 else if(diff > (60 * 60))
386 sprintf(out, "%.0f hour(s)", diff / (60 * 60));
387 else if(diff > 60)
388 sprintf(out, "%.0f minute(s)", diff / 60);
389 else
390 sprintf(out, "%.0f second(s)", diff);
391 return out;
392}
393
394
396{
397 WvString out;
398 out.setsize(80);
399
400 if (when < 0)
401 when = time(NULL);
402
403 struct tm *tmwhen = localtime(&when);
404 strftime(out.edit(), 80, "%a, %d %b %Y %H:%M:%S %z", tmwhen);
405
406 return out;
407}
408
409
411{
412 // stick a backslash in front of every !isalnum() character in s1
413 if (!s1)
414 return "";
415
416 WvString s2;
417 s2.setsize(s1.len() * 2 + 1);
418
419 const char *p1 = s1;
420 char *p2 = s2.edit();
421 while (*p1)
422 {
423 if (!isalnum(*p1))
424 *p2++ = '\\';
425 *p2++ = *p1++;
426 }
427 *p2 = 0;
428
429 return s2;
430}
431
432
433int strcount(WvStringParm s, const char c)
434{
435 int n=0;
436 const char *p = s;
437 while ((p=strchr(p, c)) != NULL && p++)
438 n++;
439
440 return n;
441}
442
443
445{
446 WvString dn("");
447
448 WvStringList fqdnlist;
449 WvStringList::Iter i(fqdnlist);
450
451 fqdnlist.split(hostname, ".");
452 for (i.rewind(); i.next(); )
453 dn.append("dc=%s,", *i);
454 dn.append("cn=%s", hostname);
455
456 return dn;
457}
458
459
461{
462 WvString nice;
463 char *optr, *optr_start;
464 const char *iptr;
465 bool last_was_dash;
466
467 nice.setsize(name.len() + 2);
468
469 iptr = name;
470 optr = optr_start = nice.edit();
471 if (!isascii(*iptr) || !isalnum(*(const unsigned char *)iptr))
472 *optr++ = 'x'; // DNS names must start with a letter!
473
474 last_was_dash = false;
475 for (; *iptr; iptr++)
476 {
477 if (!isascii(*iptr))
478 continue; // skip it entirely
479
480 if (*iptr == '-' || *iptr == '_')
481 {
482 if (last_was_dash)
483 continue;
484 last_was_dash = true;
485 *optr++ = '-';
486 }
487 else if (isalnum(*(const unsigned char *)iptr) || *iptr == '.')
488 {
489 *optr++ = *iptr;
490 last_was_dash = false;
491 }
492 }
493
494 if (optr > optr_start && !isalnum(*(const unsigned char *)(optr-1)))
495 *optr++ = 'x'; // must _end_ in a letter/number too!
496
497 *optr++ = 0;
498
499 if (!nice.len())
500 return "UNKNOWN";
501
502 return nice;
503}
504
505
507{
508 WvString tmp(fullname);
509 char *cptr = strrchr(tmp.edit(), '/');
510
511 if (!cptr) // no slash at all
512 return fullname;
513 else if (!cptr[1]) // terminating slash
514 {
515 *cptr = 0;
516 return getfilename(tmp);
517 }
518 else // no terminating slash
519 return cptr+1;
520}
521
522
523WvString getdirname(WvStringParm fullname)
524{
525 WvString tmp(fullname);
526 char *cptr = strrchr(tmp.edit(), '/');
527
528 if (!cptr) // no slash at all
529 return ".";
530 else if (!cptr[1]) // terminating slash
531 {
532 *cptr = 0;
533 return getdirname(tmp);
534 }
535 else // no terminating slash
536 {
537 *cptr = 0;
538 return !tmp ? WvString("/") : tmp;
539 }
540}
541
542// Programmatically determine the units. In order, these are:
543// bytes, kilobytes, megabytes, gigabytes, terabytes, petabytes,
544// exabytes, zettabytes, yottabytes. Note that these are SI
545// prefixes, not binary ones.
546
547// This structure allows us to choose between SI-prefixes which are
548// powers of 10, and IEC-prefixes which are powers of 2.
550{
551 const char *name;
552 unsigned long long base;
553};
554
555// SI-prefixes:
556// kilo, mega, giga, tera, peta, and exa.
557static const prefix_t si[] =
558{
559 { "k", 1000ull },
560 { "M", 1000ull * 1000ull },
561 { "G", 1000ull * 1000ull * 1000ull },
562 { "T", 1000ull * 1000ull * 1000ull * 1000ull },
563 { "P", 1000ull * 1000ull * 1000ull * 1000ull * 1000ull},
564 { "E", 1000ull * 1000ull * 1000ull * 1000ull * 1000ull * 1000ull},
565 { "Z", 0 },
566 { "Y", 0 },
567 { NULL, 0 }
568};
569
570// IEC-prefixes:
571// kibi, mebi, gibi, tebi, pebi, and exbi.
572static const prefix_t iec[] =
573{
574 { "Ki", 1024ull },
575 { "Mi", 1024ull * 1024ull},
576 { "Gi", 1024ull * 1024ull * 1024ull },
577 { "Ti", 1024ull * 1024ull * 1024ull * 1024ull },
578 { "Pi", 1024ull * 1024ull * 1024ull * 1024ull * 1024ull},
579 { "Ei", 1024ull * 1024ull * 1024ull * 1024ull * 1024ull * 1024ull},
580 { "Zi", 0 },
581 { "Yi", 0 },
582 { NULL, 0 }
583};
584
585
586// This function expects size to be ten-times the actual number.
587static inline unsigned long long _sizetoa_rounder(RoundingMethod method,
588 unsigned long long size,
589 unsigned long long remainder,
590 unsigned long long base)
591{
592 unsigned long long half = base / 2;
593 unsigned long long significant_digits = size / base;
594 switch (method)
595 {
596 case ROUND_DOWN:
597 break;
598
599 case ROUND_UP:
600 if (remainder || (size % base))
601 ++significant_digits;
602 break;
603
604 case ROUND_UP_AT_POINT_FIVE:
605 if ((size % base) >= half)
606 ++significant_digits;
607 break;
608
609 case ROUND_DOWN_AT_POINT_FIVE:
610 unsigned long long r = size % base;
611 if ((r > half) || (remainder && (r == half)))
612 ++significant_digits;
613 break;
614 }
615 return significant_digits;
616}
617
618
619// This function helps sizetoa() and sizektoa() below. It takes a
620// bunch of digits, and the default unit (indexed by size); and turns
621// them into a WvString that's formatted to human-readable rounded
622// sizes, with one decimal place.
623//
624// You must be very careful here never to add anything to size.
625// Otherwise, you might cause an overflow to occur. Similarly, you
626// must be careful when you subtract or you might cause an underflow.
627static WvString _sizetoa(unsigned long long size, unsigned long blocksize,
628 RoundingMethod rounding_method,
629 const prefix_t *prefixes, WvStringParm unit)
630{
631 assert(blocksize);
632
633 // To understand rounding, consider the display of the value 999949.
634 // For each rounding method the string displayed should be:
635 // ROUND_DOWN: 999.9 kB
636 // ROUND_UP_AT_POINT_FIVE: 999.9 kB
637 // ROUND_UP: 1.0 MB
638 // On the other hand, for the value 999950, the strings should be:
639 // ROUND_DOWN: 999.9 kB
640 // ROUND_DOWN_AT_POINT_FIVE: 999.9 kB
641 // ROUND_UP_AT_POINT_FIVE: 1.0 MB
642 // ROUND_UP: 1.0 MB
643
644 // Deal with blocksizes without overflowing.
645 const unsigned long long group_base = prefixes[0].base;
646 int shift = 0;
647 unsigned long prev_blocksize = 0;
648 while (blocksize >= group_base)
649 {
650 prev_blocksize = blocksize;
651 blocksize /= group_base;
652 ++shift;
653 }
654
655 // If we have a very large blocksize, make sure to keep enough of
656 // it to make rounding possible.
657 if (prev_blocksize && prev_blocksize != group_base)
658 {
659 blocksize = prev_blocksize;
660 --shift;
661 }
662
663 int p = -1;
664 unsigned long long significant_digits = size * 10;
665 unsigned int remainder = 0;
666 if (significant_digits < size)
667 {
668 // A really big size. We'll divide by a grouping before going up one.
669 remainder = size % group_base;
670 size /= group_base;
671 ++shift;
672 }
673 while (size >= group_base)
674 {
675 ++p;
676 significant_digits = _sizetoa_rounder(rounding_method,
677 size * 10,
678 remainder,
679 prefixes[p].base);
680 if (significant_digits < (group_base * 10)
681 || !prefixes[p + shift + 1].name)
682 break;
683 }
684
685 // Correct for blocksizes that aren't powers of group_base.
686 if (blocksize > 1)
687 {
688 significant_digits *= blocksize;
689 while (significant_digits >= (group_base * 10)
690 && prefixes[p + shift + 1].name)
691 {
692 significant_digits = _sizetoa_rounder(rounding_method,
693 significant_digits,
694 0,
695 group_base);
696 ++p;
697 }
698 }
699
700 // Now we can return our result.
701 return WvString("%s.%s %s%s",
702 significant_digits / 10,
703 significant_digits % 10,
704 prefixes[p + shift].name,
705 unit);
706}
707
708WvString sizetoa(unsigned long long blocks, unsigned long blocksize,
709 RoundingMethod rounding_method)
710{
711 unsigned long long bytes = blocks * blocksize;
712
713 // Test if we are dealing in just bytes.
714 if (bytes < 1000 && bytes >= blocks)
715 return WvString("%s bytes", bytes);
716
717 return _sizetoa(blocks, blocksize, rounding_method, si, "B");
718}
719
720
721WvString sizektoa(unsigned long long kbytes, RoundingMethod rounding_method)
722{
723 if (kbytes < 1000)
724 return WvString("%s kB", kbytes);
725
726 return sizetoa(kbytes, 1000, rounding_method);
727}
728
729WvString sizeitoa(unsigned long long blocks, unsigned long blocksize,
730 RoundingMethod rounding_method)
731{
732 unsigned long long bytes = blocks * blocksize;
733
734 // Test if we are dealing in just bytes.
735 if (bytes < 1024 && bytes >= blocks)
736 return WvString("%s bytes", bytes);
737
738 return _sizetoa(blocks, blocksize, rounding_method, iec, "B");
739}
740
741
742WvString sizekitoa(unsigned long long kbytes, RoundingMethod rounding_method)
743{
744 if (kbytes < 1024)
745 return WvString("%s KiB", kbytes);
746
747 return sizeitoa(kbytes, 1024, rounding_method);
748}
749
750WvString secondstoa(unsigned int total_seconds)
751{
752 WvString result("");
753
754 unsigned int days = total_seconds / (3600*24);
755 total_seconds %= (3600*24);
756 unsigned int hours = total_seconds / 3600;
757 total_seconds %= 3600;
758 unsigned int mins = total_seconds / 60;
759 unsigned int secs = total_seconds % 60;
760
761 int num_elements = (days > 0) + (hours > 0) + (mins > 0);
762
763 if (days > 0)
764 {
765 result.append(days);
766 result.append(days > 1 ? " days" : " day");
767 num_elements--;
768 if (num_elements > 1)
769 result.append(", ");
770 else if (num_elements == 1)
771 result.append(" and ");
772 }
773 if (hours > 0)
774 {
775 result.append(hours);
776 result.append(hours > 1 ? " hours" : " hour");
777 num_elements--;
778 if (num_elements > 1)
779 result.append(", ");
780 else if (num_elements == 1)
781 result.append(" and ");
782 }
783 if (mins > 0)
784 {
785 result.append(mins);
786 result.append(mins > 1 ? " minutes" : " minute");
787 }
788 if (days == 0 && hours == 0 && mins == 0)
789 {
790 result.append(secs);
791 result.append(secs != 1 ? " seconds" : " second");
792 }
793
794 return result;
795}
796
798{
799 WvDynBuf buf;
800 const char *sptr = s, *eptr;
801
802 while ((eptr = strstr(sptr, a)) != NULL)
803 {
804 buf.put(sptr, eptr-sptr);
805 buf.putstr(b);
806 sptr = eptr + strlen(a);
807 }
808
809 buf.put(sptr, strlen(sptr));
810
811 return buf.getstr();
812}
813
815{
816 WvDynBuf out;
817
818 bool last = false;
819
820 for (int i = 0; s[i] != '\0'; i++)
821 {
822 if (s[i] != c)
823 {
824 out.putch(s[i]);
825 last = false;
826 }
827 else if (!last)
828 {
829 out.putch(c);
830 last = true;
831 }
832 }
833
834 return out.getstr();
835}
836
837
839{
840 struct tm *tm = gmtime(&t);
841 WvString s;
842
843 s.setsize(128);
844 strftime(s.edit(), 128, "%a, %d %b %Y %H:%M:%S GMT", tm);
845
846 return s;
847}
848
849
850int lookup(const char *str, const char * const *table, bool case_sensitive)
851{
852 for (int i = 0; table[i]; ++i)
853 {
854 if (case_sensitive)
855 {
856 if (strcmp(str, table[i]) != 0)
857 continue;
858 }
859 else
860 {
861 if (strcasecmp(str, table[i]) != 0)
862 continue;
863 }
864 return i;
865 }
866 return -1;
867}
868
869
871{
872 int maxlen = 0;
873 for (;;)
874 {
875 maxlen += 80;
876 char *name = new char[maxlen];
877 int result = gethostname(name, maxlen);
878 if (result == 0)
879 {
880 WvString hostname(name);
881 deletev name;
882 return hostname;
883 }
884#ifdef _WIN32
885 assert(errno == WSAEFAULT);
886#else
887 assert(errno == EINVAL);
888#endif
889 }
890}
891
892
894{
895 struct hostent *myhost;
896
897 myhost = gethostbyname(hostname());
898 if (myhost)
899 return myhost->h_name;
900 else
901 return WvString::null;
902}
903
904
906{
907 int maxlen = 0;
908 for (;;)
909 {
910 maxlen += 80;
911 char *name = new char[maxlen];
912 char *res = getcwd(name, maxlen);
913 if (res)
914 {
915 WvString s(name);
916 deletev name;
917 return s;
918 }
919 if (errno == EACCES || errno == ENOENT)
920 return "."; // can't deal with those errors
921 assert(errno == ERANGE); // buffer too small
922 }
923}
924
925
926WvString metriculate(const off_t i)
927{
928 WvString res;
929 int digits=0;
930 int digit=0;
931 long long int j=i;
932 char *p;
933
934 while (j)
935 {
936 digits++;
937 j/=10;
938 }
939
940 j=i;
941 // setsize says it takes care of the terminating NULL char
942 res.setsize(digits + ((digits - 1) / 3) + ((j < 0) ? 1 : 0));
943 p = res.edit();
944 if (j < 0)
945 {
946 *p++ = '-';
947 j = -j;
948 }
949
950 p += digits + ((digits - 1) / 3);
951 *p-- = '\0';
952
953 for (digit=0; digit<digits; digit++)
954 {
955 *p-- = '0' + ( j%10 );
956 if (((digit+1) % 3) == 0 && digit < digits - 1)
957 *p-- = ' ';
958 j /= 10;
959 }
960
961 return res;
962}
963
964
966{
967 if (!line || !a)
968 return WvString::null;
969
970 const char *loc = strstr(line, a);
971 if (loc == 0)
972 return "";
973
974 loc += a.len();
975 WvString ret = loc;
976 ret.unique();
977 return ret;
978}
979
980
982{
983 if (!line || !a)
984 return WvString::null;
985
986 WvString ret = line;
987 ret.unique();
988 char *loc = strstr(ret.edit(), a);
989
990 if (loc == 0)
991 return line;
992
993 loc[0] = '\0';
994 return ret;
995}
996
997
998WvString substr(WvString line, unsigned int pos, unsigned int len)
999{
1000 const char *tmp = line.cstr();
1001 if (pos > line.len()-1)
1002 return "";
1003 tmp += pos;
1004
1005 WvString ret = tmp;
1006 char *tmp2 = ret.edit();
1007 if (pos + len < line.len())
1008 tmp2[len] = '\0';
1009
1010 return ret;
1011}
1012
1013const CStrExtraEscape CSTR_TCLSTR_ESCAPES[3] =
1014{
1015 { '{', "\\<" },
1016 { '}', "\\>" },
1017 { 0, NULL }
1018};
1019
1020static inline const char *cstr_escape_char(char ch)
1021{
1022 static const char *xlat[256] =
1023 {
1024 "\\0", "\\x01", "\\x02", "\\x03", "\\x04", "\\x05", "\\x06", "\\a",
1025 "\\b", "\\t", "\\n", "\\v", "\\x0C", "\\r", "\\x0E", "\\x0F",
1026 "\\x10", "\\x11", "\\x12", "\\x13", "\\x14", "\\x15", "\\x16", "\\x17",
1027 "\\x18", "\\x19", "\\x1A", "\\x1B", "\\x1C", "\\x1D", "\\x1E", "\\x1F",
1028 " ", "!", "\\\"", "#", "$", "%", "&", "'",
1029 "(", ")", "*", "+", ",", "-", ".", "/",
1030 "0", "1", "2", "3", "4", "5", "6", "7",
1031 "8", "9", ":", ";", "<", "=", ">", "?",
1032 "@", "A", "B", "C", "D", "E", "F", "G",
1033 "H", "I", "J", "K", "L", "M", "N", "O",
1034 "P", "Q", "R", "S", "T", "U", "V", "W",
1035 "X", "Y", "Z", "[", "\\\\", "]", "^", "_",
1036 "`", "a", "b", "c", "d", "e", "f", "g",
1037 "h", "i", "j", "k", "l", "m", "n", "o",
1038 "p", "q", "r", "s", "t", "u", "v", "w",
1039 "x", "y", "z", "{", "|", "}", "~", "\\x7F",
1040 "\\x80", "\\x81", "\\x82", "\\x83", "\\x84", "\\x85", "\\x86", "\\x87",
1041 "\\x88", "\\x89", "\\x8A", "\\x8B", "\\x8C", "\\x8D", "\\x8E", "\\x8F",
1042 "\\x90", "\\x91", "\\x92", "\\x93", "\\x94", "\\x95", "\\x96", "\\x97",
1043 "\\x98", "\\x99", "\\x9A", "\\x9B", "\\x9C", "\\x9D", "\\x9E", "\\x9F",
1044 "\\xA0", "\\xA1", "\\xA2", "\\xA3", "\\xA4", "\\xA5", "\\xA6", "\\xA7",
1045 "\\xA8", "\\xA9", "\\xAA", "\\xAB", "\\xAC", "\\xAD", "\\xAE", "\\xAF",
1046 "\\xB0", "\\xB1", "\\xB2", "\\xB3", "\\xB4", "\\xB5", "\\xB6", "\\xB7",
1047 "\\xB8", "\\xB9", "\\xBA", "\\xBB", "\\xBC", "\\xBD", "\\xBE", "\\xBF",
1048 "\\xC0", "\\xC1", "\\xC2", "\\xC3", "\\xC4", "\\xC5", "\\xC6", "\\xC7",
1049 "\\xC8", "\\xC9", "\\xCA", "\\xCB", "\\xCC", "\\xCD", "\\xCE", "\\xCF",
1050 "\\xD0", "\\xD1", "\\xD2", "\\xD3", "\\xD4", "\\xD5", "\\xD6", "\\xD7",
1051 "\\xD8", "\\xD9", "\\xDA", "\\xDB", "\\xDC", "\\xDD", "\\xDE", "\\xDF",
1052 "\\xE0", "\\xE1", "\\xE2", "\\xE3", "\\xE4", "\\xE5", "\\xE6", "\\xE7",
1053 "\\xE8", "\\xE9", "\\xEA", "\\xEB", "\\xEC", "\\xED", "\\xEE", "\\xEF",
1054 "\\xF0", "\\xF1", "\\xF2", "\\xF3", "\\xF4", "\\xF5", "\\xF6", "\\xF7",
1055 "\\xF8", "\\xF9", "\\xFA", "\\xFB", "\\xFC", "\\xFD", "\\xFE", "\\xFF"
1056 };
1057 return xlat[(unsigned char)ch];
1058}
1059
1060static inline int hex_digit_val(char ch)
1061{
1062 static int val[256] =
1063 {
1064 -1, -1, -1, -1, -1, -1, -1, -1,
1065 -1, -1, -1, -1, -1, -1, -1, -1,
1066 -1, -1, -1, -1, -1, -1, -1, -1,
1067 -1, -1, -1, -1, -1, -1, -1, -1,
1068 -1, -1, -1, -1, -1, -1, -1, -1,
1069 -1, -1, -1, -1, -1, -1, -1, -1,
1070 0, 1, 2, 3, 4, 5, 6, 7,
1071 8, 9, -1, -1, -1, -1, -1, -1,
1072 -1, 10, 11, 12, 13, 14, 15, -1,
1073 -1, -1, -1, -1, -1, -1, -1, -1,
1074 -1, -1, -1, -1, -1, -1, -1, -1,
1075 -1, -1, -1, -1, -1, -1, -1, -1,
1076 -1, 10, 11, 12, 13, 14, 15, -1,
1077 -1, -1, -1, -1, -1, -1, -1, -1,
1078 -1, -1, -1, -1, -1, -1, -1, -1,
1079 -1, -1, -1, -1, -1, -1, -1, -1,
1080 -1, -1, -1, -1, -1, -1, -1, -1,
1081 -1, -1, -1, -1, -1, -1, -1, -1,
1082 -1, -1, -1, -1, -1, -1, -1, -1,
1083 -1, -1, -1, -1, -1, -1, -1, -1,
1084 -1, -1, -1, -1, -1, -1, -1, -1,
1085 -1, -1, -1, -1, -1, -1, -1, -1,
1086 -1, -1, -1, -1, -1, -1, -1, -1,
1087 -1, -1, -1, -1, -1, -1, -1, -1,
1088 -1, -1, -1, -1, -1, -1, -1, -1,
1089 -1, -1, -1, -1, -1, -1, -1, -1,
1090 -1, -1, -1, -1, -1, -1, -1, -1,
1091 -1, -1, -1, -1, -1, -1, -1, -1,
1092 -1, -1, -1, -1, -1, -1, -1, -1,
1093 -1, -1, -1, -1, -1, -1, -1, -1,
1094 -1, -1, -1, -1, -1, -1, -1, -1,
1095 -1, -1, -1, -1, -1, -1, -1, -1
1096 };
1097 return val[(unsigned char)ch];
1098}
1099
1100static inline bool cstr_unescape_char(const char *&cstr, char &ch)
1101{
1102 if (*cstr == '\\')
1103 {
1104 ++cstr;
1105
1106 switch (*cstr)
1107 {
1108 case '"': ch = '"'; break;
1109 case 't': ch = '\t'; break;
1110 case 'n': ch = '\n'; break;
1111 case '\\': ch = '\\'; break;
1112 case 'r': ch = '\r'; break;
1113 case 'a': ch = '\a'; break;
1114 case 'v': ch = '\v'; break;
1115 case 'b': ch = '\b'; break;
1116 case '0': ch = '\0'; break;
1117 case 'x':
1118 {
1119 int vals[2];
1120 int i;
1121 for (i=0; i<2; ++i)
1122 {
1123 if ((vals[i] = hex_digit_val(*++cstr)) == -1)
1124 return false;
1125 }
1126 ch = (vals[0] << 4) | vals[1];
1127 }
1128 break;
1129 default: return false;
1130 }
1131
1132 ++cstr;
1133
1134 return true;
1135 }
1136 else
1137 {
1138 ch = *cstr++;
1139 return true;
1140 }
1141}
1142
1143WvString cstr_escape(const void *data, size_t size,
1144 const CStrExtraEscape extra_escapes[])
1145{
1146 if (!data) return WvString::null;
1147
1148 const char *cdata = (const char *)data;
1149
1150 WvString result;
1151 result.setsize(4*size + 3); // We could do better but it would slow us down
1152 char *cstr = result.edit();
1153
1154 *cstr++ = '\"';
1155 while (size-- > 0)
1156 {
1157 const char *esc = NULL;
1158 if (extra_escapes)
1159 {
1160 const CStrExtraEscape *extra = &extra_escapes[0];
1161 while (extra->ch && extra->esc)
1162 {
1163 if (*cdata == extra->ch)
1164 {
1165 esc = extra->esc;
1166 break;
1167 }
1168
1169 ++extra;
1170 }
1171 }
1172 if (!esc) esc = cstr_escape_char(*cdata);
1173 ++cdata;
1174 while (*esc) *cstr++ = *esc++;
1175 }
1176 *cstr++ = '\"';
1177 *cstr = '\0';
1178
1179 return result;
1180}
1181
1182bool cstr_unescape(WvStringParm cstr, void *data, size_t max_size, size_t &size,
1183 const CStrExtraEscape extra_escapes[])
1184{
1185 const char *q = cstr;
1186 char *cdata = (char *)data;
1187
1188 if (!q) goto misformatted;
1189 size = 0;
1190
1191 for (;;)
1192 {
1193 while (isspace(*q)) q++;
1194 if (*q == '\0') break;
1195
1196 if (*q++ != '\"') goto misformatted;
1197 while (*q && *q != '\"')
1198 {
1199 bool found = false;
1200 char unesc;
1201 if (extra_escapes)
1202 {
1203 const CStrExtraEscape *extra = &extra_escapes[0];
1204 while (extra->ch && extra->esc)
1205 {
1206 size_t len = strlen(extra->esc);
1207 if (strncmp(extra->esc, q, len) == 0)
1208 {
1209 unesc = extra->ch;
1210 q += len;
1211 found = true;
1212 break;
1213 }
1214
1215 ++extra;
1216 }
1217 }
1218 if (!found && !cstr_unescape_char(q, unesc)) goto misformatted;
1219 if (size++ < max_size && cdata) *cdata++ = unesc;
1220 }
1221 if (*q++ != '\"') goto misformatted;
1222 }
1223
1224 return size <= max_size;
1225
1226misformatted:
1227
1228 size = 0;
1229 return false;
1230}
1231
1233{
1234 WvString out;
1235 out.setsize(80);
1236
1237 if (when < 0)
1238 when = time(NULL);
1239
1240 struct tm *tmwhen = localtime(&when);
1241 strftime(out.edit(), 80, "%b %d %I:%M:%S %p", tmwhen);
1242
1243 return out;
1244}
1245
1247{
1248 WvString out;
1249 out.setsize(12);
1250
1251 if (when < 0)
1252 when = time(NULL);
1253
1254 struct tm *tmwhen = localtime(&when);
1255 strftime(out.edit(), 12, "%H:%M:%S", tmwhen);
1256
1257 return out;
1258}
1259
1261{
1262 WvString out;
1263 out.setsize(16);
1264
1265 if (when < 0)
1266 when = time(NULL);
1267
1268 struct tm *tmwhen = localtime(&when);
1269 strftime(out.edit(), 16, "%Y-%m-%d", tmwhen);
1270
1271 return out;
1272}
1273
1275{
1276 WvString out;
1277 out.setsize(24);
1278
1279 if (when < 0)
1280 when = time(NULL);
1281
1282 struct tm *tmwhen = localtime(&when);
1283 strftime(out.edit(), 24, "%Y-%m-%d %H:%M:%S", tmwhen);
1284
1285 return out;
1286}
1287
1288
1294time_t intl_gmtoff(time_t t)
1295{
1296 struct tm *l = localtime(&t);
1297 l->tm_isdst = 0;
1298 time_t local = mktime(l);
1299 time_t gmt = mktime(gmtime(&t));
1300
1301 return local-gmt;
1302}
1303
1304
1305// Removes any trailing punctuation ('.', '?', or '!') from the line
1307{
1308 WvString ret = line;
1309 char * edit = ret.edit();
1310 int last = ret.len() - 1;
1311 if (edit[last] == '.' || edit[last] == '?' || edit[last] == '!')
1312 edit[last] = '\0';
1313
1314 return ret;
1315}
1316
1317
1319{
1320 char buf[(sizeof(ptr) * 2) + 3];
1321 int rv;
1322
1323 rv = snprintf(buf, sizeof(buf), "%p", ptr);
1324
1325 assert(rv != -1);
1326
1327 return buf;
1328}
void put(const T *data, size_t count)
Writes the specified number of elements from the specified storage location into the buffer at its ta...
Definition wvbufbase.h:483
A WvFastString acts exactly like a WvString, but can take (const char *) strings without needing to a...
Definition wvstring.h:94
const char * cstr() const
return a (const char *) for this string.
Definition wvstring.h:267
This is a WvList of WvStrings, and is a really handy way to parse strings.
void split(WvStringParm s, const char *splitchars=" \t\r\n", int limit=0)
split s and form a list ignoring splitchars (except at beginning and end) ie.
WvString is an implementation of a simple and efficient printable-string class.
Definition wvstring.h:330
WvString & unique()
make the buf and str pointers owned only by this WvString.
Definition wvstring.cc:306
char * edit()
make the string editable, and return a non-const (char*)
Definition wvstring.h:397
bool cstr_unescape(WvStringParm cstr, void *data, size_t max_size, size_t &size, const CStrExtraEscape extra_escapes[]=NULL)
Converts a C-style string constant into data.
Definition strutils.cc:1182
bool isnewline(char c)
Returns true if 'c' is a newline or carriage return character.
Definition strutils.cc:304
WvString beforestr(WvStringParm line, WvStringParm a)
Returns everything in line (exclusively) before 'a'.
Definition strutils.cc:981
char * terminate_string(char *string, char c)
Add character c to the end of a string after removing terminating carriage returns/linefeeds if any.
Definition strutils.cc:32
WvString fqdomainname()
Get the fqdn of the local host, using gethostbyname() and gethostname()
Definition strutils.cc:893
WvString encode_hostname_as_DN(WvStringParm hostname)
Example: encode_hostname_as_DN("www.fizzle.com") will result in dc=www,dc=fizzle,dc=com,...
Definition strutils.cc:444
WvString strreplace(WvStringParm s, WvStringParm a, WvStringParm b)
Replace any instances of "a" with "b" in "s".
Definition strutils.cc:797
WvString backslash_escape(WvStringParm s1)
Returns a string with a backslash in front of every non alphanumeric character in s1.
Definition strutils.cc:410
WvString url_encode(WvStringParm str, WvStringParm unsafe="")
Converts all those pesky spaces, colons, and other nasties into nice unreadable Quasi-Unicode codes.
Definition strutils.cc:351
WvString getfilename(WvStringParm fullname)
Take a full path/file name and splits it up into respective pathname and filename.
Definition strutils.cc:506
WvString rfc822_date(time_t _when=-1)
Returns an RFC822-compatible date made out of _when, or, if _when < 0, out of the current time.
Definition strutils.cc:395
WvString hexdump_buffer(const void *buf, size_t len, bool charRep=true)
Produce a hexadecimal dump of the data buffer in 'buf' of length 'len'.
Definition strutils.cc:245
WvString ptr2str(void *ptr)
Converts a pointer into a string, like glibc's p formatter would do.
Definition strutils.cc:1318
WvString sizetoa(unsigned long long blocks, unsigned long blocksize=1, RoundingMethod rounding_method=ROUND_UP_AT_POINT_FIVE)
Given a number of blocks and a blocksize (default==1 byte), return a WvString containing a human-read...
Definition strutils.cc:708
int lookup(const char *str, const char *const *table, bool case_sensitive=false)
Finds a string in an array and returns its index.
Definition strutils.cc:850
WvString local_date(time_t _when=-1)
Return the local date (TZ applied) out of _when.
Definition strutils.cc:1232
WvString nice_hostname(WvStringParm name)
Given a hostname, turn it into a "nice" one.
Definition strutils.cc:460
bool is_word(const char *string)
Returns true if all characters in 'string' are isalnum() (alphanumeric).
Definition strutils.cc:228
int strcount(WvStringParm s, const char c)
How many times does 'c' occur in "s"?
Definition strutils.cc:433
WvString sizeitoa(unsigned long long blocks, unsigned long blocksize=1, RoundingMethod rounding_method=ROUND_UP_AT_POINT_FIVE)
Given a number of blocks and a blocksize (default==1 byte), return a WvString containing a human-read...
Definition strutils.cc:729
WvString intl_date(time_t _when=-1)
Return the local date (in format of ISO 8601) out of _when.
Definition strutils.cc:1260
WvString rfc1123_date(time_t _when)
Returns an RFC1123-compatible date made out of _when.
Definition strutils.cc:838
WvString sizektoa(unsigned long long kbytes, RoundingMethod rounding_method=ROUND_UP_AT_POINT_FIVE)
Given a size in kilobyes, return a human readable size.
Definition strutils.cc:721
WvString diff_dates(time_t t1, time_t t2)
Returns the difference between to dates in a human readable format.
Definition strutils.cc:376
WvString hostname()
Do gethostname() without a fixed-length buffer.
Definition strutils.cc:870
WvString metriculate(const off_t i)
Inserts SI-style spacing into a number (eg passing 9876543210 returns "9 876 543 210")
Definition strutils.cc:926
WvString secondstoa(unsigned int total_seconds)
Given a number of seconds, returns a formatted human-readable string saying how long the period is.
Definition strutils.cc:750
char * non_breaking(const char *string)
Replaces all whitespace characters in the string with non-breaking spaces (&#160;) for use with web stuff...
Definition strutils.cc:154
WvString sizekitoa(unsigned long long kbytes, RoundingMethod rounding_method=ROUND_UP_AT_POINT_FIVE)
Given a size in kilobytes, return a human readable size.
Definition strutils.cc:742
char * trim_string(char *string)
Trims whitespace from the beginning and end of the character string, including carriage return / line...
Definition strutils.cc:59
time_t intl_gmtoff(time_t t)
Return the number of seconds by which localtime (at the given timestamp) is offset from GMT.
Definition strutils.cc:1294
WvString substr(WvString line, unsigned int pos, unsigned int len)
Returns the string of length len starting at pos in line.
Definition strutils.cc:998
WvString url_decode(WvStringParm str, bool no_space=false)
Converts escaped characters (things like %20 etc.) from web URLS into their normal ASCII representati...
Definition strutils.cc:311
WvString afterstr(WvStringParm line, WvStringParm a)
Returns everything in line (exclusively) after a.
Definition strutils.cc:965
void replace_char(void *string, char c1, char c2, int length)
Replace all instances of c1 with c2 for the first 'length' characters in 'string'.
Definition strutils.cc:178
WvString intl_datetime(time_t _when=-1)
Return the local date and time (in format of ISO 8601) out of _when.
Definition strutils.cc:1274
WvString cstr_escape(const void *data, size_t size, const CStrExtraEscape extra_escapes[]=NULL)
Converts data into a C-style string constant.
Definition strutils.cc:1143
char * strlwr(char *string)
In-place modify a character string so that all contained letters are in lower case.
Definition strutils.cc:201
char * snip_string(char *haystack, char *needle)
Snip off the first part of 'haystack' if it consists of 'needle'.
Definition strutils.cc:187
WvString depunctuate(WvStringParm line)
Removes any trailing punctuation ('.
Definition strutils.cc:1306
char * strupr(char *string)
In-place modify a character string so that all contained letters are in upper case.
Definition strutils.cc:214
WvString spacecat(WvStringParm a, WvStringParm b, char sep=' ', bool onesep=false)
return the string formed by concatenating string 'a' and string 'b' with the 'sep' character between ...
Definition strutils.cc:114
WvString undupe(WvStringParm s, char c)
Replace any consecutive instances of character c with a single one.
Definition strutils.cc:814
WvString wvgetcwd()
Get the current working directory without a fixed-length buffer.
Definition strutils.cc:905
WvString intl_time(time_t _when=-1)
Return the local time (in format of ISO 8601) out of _when.
Definition strutils.cc:1246