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 
32 char *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 
59 char *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 
82 char *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.
114 WvString 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.
154 char *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.
178 void 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'.
187 char *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 
201 char *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 
214 char *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().
228 bool 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.
245 WvString 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.
304 bool isnewline(char c)
305 {
306  return c=='\n' || c=='\r';
307 }
308 
309 
310 // ex: WvString foo = url_decode("I+am+text.%0D%0A");
311 WvString url_decode(WvStringParm str, bool no_space)
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
351 WvString url_encode(WvStringParm str, WvStringParm unsafe)
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 
376 WvString 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 
395 WvString rfc822_date(time_t when)
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 
410 WvString backslash_escape(WvStringParm s1)
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 
433 int 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 
460 WvString nice_hostname(WvStringParm name)
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 
506 WvString getfilename(WvStringParm fullname)
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 
523 WvString 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.
549 struct prefix_t
550 {
551  const char *name;
552  unsigned long long base;
553 };
554 
555 // SI-prefixes:
556 // kilo, mega, giga, tera, peta, and exa.
557 static 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.
572 static 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.
587 static 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.
627 static 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 
708 WvString 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 
721 WvString 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 
729 WvString 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 
742 WvString 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 
750 WvString 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 
797 WvString strreplace(WvStringParm s, WvStringParm a, WvStringParm b)
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 
814 WvString undupe(WvStringParm s, char c)
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 
850 int 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 
926 WvString 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 
965 WvString afterstr(WvStringParm line, WvStringParm a)
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 
981 WvString beforestr(WvStringParm line, WvStringParm a)
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 
998 WvString 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 
1013 const CStrExtraEscape CSTR_TCLSTR_ESCAPES[3] =
1014 {
1015  { '{', "\\<" },
1016  { '}', "\\>" },
1017  { 0, NULL }
1018 };
1019 
1020 static 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 
1060 static 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 
1100 static 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 
1143 WvString 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 
1182 bool 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 
1226 misformatted:
1227 
1228  size = 0;
1229  return false;
1230 }
1231 
1232 WvString local_date(time_t when)
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 
1246 WvString intl_time(time_t when)
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 
1260 WvString intl_date(time_t when)
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 
1294 time_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
1306 WvString depunctuate(WvStringParm 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 
1318 WvString ptr2str(void* ptr)
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 }
WvString::edit
char * edit()
make the string editable, and return a non-const (char*)
Definition: wvstring.h:397
intl_datetime
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
intl_gmtoff
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
intl_time
WvString intl_time(time_t _when=-1)
Return the local time (in format of ISO 8601) out of _when.
Definition: strutils.cc:1246
lookup
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
fqdomainname
WvString fqdomainname()
Get the fqdn of the local host, using gethostbyname() and gethostname()
Definition: strutils.cc:893
getfilename
WvString getfilename(WvStringParm fullname)
Take a full path/file name and splits it up into respective pathname and filename.
Definition: strutils.cc:506
url_decode
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
sizetoa
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
isnewline
bool isnewline(char c)
Returns true if 'c' is a newline or carriage return character.
Definition: strutils.cc:304
CStrExtraEscape
Definition: wvstrutils.h:537
rfc1123_date
WvString rfc1123_date(time_t _when)
Returns an RFC1123-compatible date made out of _when.
Definition: strutils.cc:838
WvBufBase< unsigned char >::getstr
WvString getstr()
Returns the entire buffer as a null-terminated WvString.
Definition: wvbuffer.cc:17
WvBufBase< unsigned char >::putstr
void putstr(WvStringParm str)
Copies a WvString into the buffer, excluding the null-terminator.
Definition: wvbuffer.cc:11
undupe
WvString undupe(WvStringParm s, char c)
Replace any consecutive instances of character c with a single one.
Definition: strutils.cc:814
WvBufBase< unsigned char >::putch
void putch(int ch)
Puts a single character into the buffer as an int.
Definition: wvbuf.h:76
diff_dates
WvString diff_dates(time_t t1, time_t t2)
Returns the difference between to dates in a human readable format.
Definition: strutils.cc:376
trim_string
char * trim_string(char *string)
Trims whitespace from the beginning and end of the character string, including carriage return / line...
Definition: strutils.cc:59
spacecat
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
local_date
WvString local_date(time_t _when=-1)
Return the local date (TZ applied) out of _when.
Definition: strutils.cc:1232
encode_hostname_as_DN
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
replace_char
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
WvString is an implementation of a simple and efficient printable-string class.
Definition: wvstring.h:329
afterstr
WvString afterstr(WvStringParm line, WvStringParm a)
Returns everything in line (exclusively) after a.
Definition: strutils.cc:965
url_encode
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
depunctuate
WvString depunctuate(WvStringParm line)
Removes any trailing punctuation ('.
Definition: strutils.cc:1306
sizekitoa
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
is_word
bool is_word(const char *string)
Returns true if all characters in 'string' are isalnum() (alphanumeric).
Definition: strutils.cc:228
non_breaking
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
nice_hostname
WvString nice_hostname(WvStringParm name)
Given a hostname, turn it into a "nice" one.
Definition: strutils.cc:460
WvFastString::cstr
const char * cstr() const
return a (const char *) for this string.
Definition: wvstring.h:267
substr
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
strcount
int strcount(WvStringParm s, const char c)
How many times does 'c' occur in "s"?
Definition: strutils.cc:433
beforestr
WvString beforestr(WvStringParm line, WvStringParm a)
Returns everything in line (exclusively) before 'a'.
Definition: strutils.cc:981
snip_string
char * snip_string(char *haystack, char *needle)
Snip off the first part of 'haystack' if it consists of 'needle'.
Definition: strutils.cc:187
rfc822_date
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
deletev
#define deletev
Remplacement for delete[].
Definition: delete.h:129
sizektoa
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
intl_date
WvString intl_date(time_t _when=-1)
Return the local date (in format of ISO 8601) out of _when.
Definition: strutils.cc:1260
strlwr
char * strlwr(char *string)
In-place modify a character string so that all contained letters are in lower case.
Definition: strutils.cc:201
prefix_t
Definition: strutils.cc:549
WvString::unique
WvString & unique()
make the buf and str pointers owned only by this WvString.
Definition: wvstring.cc:306
metriculate
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
strupr
char * strupr(char *string)
In-place modify a character string so that all contained letters are in upper case.
Definition: strutils.cc:214
hexdump_buffer
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
WvDynBufBase< unsigned char >
sizeitoa
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
secondstoa
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
cstr_escape
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
wvgetcwd
WvString wvgetcwd()
Get the current working directory without a fixed-length buffer.
Definition: strutils.cc:905
hostname
WvString hostname()
Do gethostname() without a fixed-length buffer.
Definition: strutils.cc:870
terminate_string
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
WvStringList
This is a WvList of WvStrings, and is a really handy way to parse strings.
Definition: wvstringlist.h:27
WvStringList::split
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.
Definition: wvstringlist.cc:19
backslash_escape
WvString backslash_escape(WvStringParm s1)
Returns a string with a backslash in front of every non alphanumeric character in s1.
Definition: strutils.cc:410
cstr_unescape
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
strreplace
WvString strreplace(WvStringParm s, WvStringParm a, WvStringParm b)
Replace any instances of "a" with "b" in "s".
Definition: strutils.cc:797
ptr2str
WvString ptr2str(void *ptr)
Converts a pointer into a string, like glibc's p formatter would do.
Definition: strutils.cc:1318