WvStreams
strutils.cc
00001 /*
00002  * Worldvisions Weaver Software:
00003  *   Copyright (C) 1997-2002 Net Integration Technologies, Inc.
00004  * 
00005  * Various useful string-based utilities.
00006  *
00007  */
00008 #include "strutils.h"
00009 #include "wvbuf.h"
00010 #include <ctype.h>
00011 #include <stdio.h>
00012 #include <string.h>
00013 #include <time.h>
00014 #include <errno.h>
00015 
00016 #ifndef _WIN32
00017 //#include <uuid.h>
00018 #include <errno.h>
00019 #include <netdb.h>
00020 #include <unistd.h>
00021 #else
00022 #undef errno
00023 #define errno GetLastError()
00024 #define strcasecmp _stricmp
00025 #include <winsock2.h>
00026 #include <direct.h>
00027 #ifndef EACCES
00028 #define EACCES 0xfff
00029 #endif
00030 #endif
00031 
00032 char *terminate_string(char *string, char c)
00033 /**********************************************/
00034 // Add character c to the end of a string after removing crlf's.
00035 // NOTE: You need a buffer that's at least one character bigger than the
00036 // current length of the string, including the terminating NULL.
00037 {
00038     char *p;
00039 
00040     if (string == NULL)
00041         return NULL;
00042 
00043     p = string + strlen(string) - 1;
00044     while (p >= string)
00045     {
00046         if (*p == '\r' || *p == '\n')
00047             --p;
00048         else
00049             break;
00050     }
00051 
00052     *(++p) = c;
00053     *(++p) = 0;
00054 
00055     return string;
00056 }
00057 
00058 
00059 char *trim_string(char *string)
00060 /*********************************/
00061 // Trims spaces off the front and end of strings.  Modifies the string.
00062 // Specifically DOES allow string==NULL; returns NULL in that case.
00063 {
00064     char *p;
00065     char *q;
00066 
00067     if (string == NULL)
00068         return NULL;
00069 
00070     p = string;
00071     q = string + strlen(string) - 1;
00072 
00073     while (q >= p && isspace(*q))
00074         *(q--) = 0;
00075     while (isspace(*p))
00076         p++;
00077 
00078     return p;
00079 }
00080 
00081 
00082 char *trim_string(char *string, char c)
00083 // Searches the string for c and removes it plus everything afterwards.
00084 // Modifies the string and returns NULL if string == NULL.
00085 {
00086     char *p;
00087 
00088     if (string == NULL)
00089         return NULL;
00090 
00091     p = string;
00092 
00093     while (*p != 0 && *p != c)
00094         p++;
00095 
00096     while (*p)
00097         *(p++) = 0;
00098 
00099     return string;
00100 }
00101 
00102 
00103 // return the string formed by concatenating string 'a' and string 'b' with
00104 // the 'sep' character between them.  For example,
00105 //    spacecat("xx", "yy", ";")
00106 // returns "xx;yy", and
00107 //    spacecat("xx;;", "yy", ";")
00108 // returns "xx;;;yy", and
00109 //    spacecat("xx;;", "yy", ";", true)
00110 // returns "xx;yy".
00111 //
00112 // This function is much faster than the more obvious WvString("%s;%s", a, b),
00113 // so it's useful when you're producing a *lot* of string data.
00114 WvString spacecat(WvStringParm a, WvStringParm b, char sep, bool onesep)
00115 {
00116     size_t alen = strlen(a);
00117     size_t blen = strlen(b);
00118 
00119     // If we only want one separator, eat away at the back of string a
00120     if (onesep && alen)
00121     {
00122         while (a[alen-1] == sep)
00123             --alen;
00124     }
00125 
00126     // Create the destination string, and give it an appropriate size.
00127     // Then, fill it with string a.
00128     WvString s;
00129     s.setsize(alen + blen + 2);
00130     char *cptr = s.edit();
00131 
00132     memcpy(cptr, a, alen);
00133 
00134     // Write the separator in the appropriate spot.
00135     cptr[alen] = sep;
00136 
00137     // If we only want one separator, eat away at the from of string b.
00138     size_t boffset = 0;
00139     if (onesep)
00140     {
00141         while (b[boffset] == sep)
00142             ++boffset;
00143     }
00144 
00145     // Now copy the second half of the string in and terminate with a NUL.
00146     memcpy(cptr+alen+1, b.cstr()+boffset, blen-boffset);
00147     cptr[alen+1+blen-boffset] = 0;
00148 
00149     return s;
00150 }
00151 
00152 
00153 // Replaces whitespace characters with nonbreaking spaces.
00154 char *non_breaking(const char * string)
00155 {
00156     if (string == NULL)
00157         return (NULL);
00158 
00159     WvDynBuf buf;
00160 
00161     while (*string)
00162     {
00163         if (isspace(*string))
00164             buf.putstr("&nbsp;");
00165         else 
00166             buf.putch(*string);
00167         string++;
00168     }
00169 
00170     WvString s(buf.getstr());
00171     char *nbstr = new char[s.len() + 1];
00172     return strcpy(nbstr, s.edit());
00173 }
00174 
00175 
00176 // Searches _string (up to length bytes), replacing any occurrences of c1
00177 // with c2.
00178 void replace_char(void *_string, char c1, char c2, int length)
00179 {
00180     char *string = (char *)_string;
00181     for (int i=0; i < length; i++)
00182         if (*(string+i) == c1)
00183             *(string+i) = c2;
00184 }
00185 
00186 // Snip off the first part of 'haystack' if it consists of 'needle'.
00187 char *snip_string(char *haystack, char *needle)
00188 {
00189     if(!haystack)
00190         return NULL;
00191     if(!needle)
00192         return haystack;
00193     char *p = strstr(haystack, needle);
00194     if(!p || p != haystack)
00195         return haystack;
00196     else
00197         return haystack + strlen(needle);
00198 }
00199 
00200 
00201 char *strlwr(char *string)
00202 {
00203     char *p = string;
00204     while (p && *p)
00205     {
00206         *p = tolower(*p);
00207         p++;
00208     }
00209 
00210     return string;
00211 }
00212 
00213 
00214 char *strupr(char *string)
00215 {
00216     char *p = string;
00217     while (p && *p)
00218     {
00219         *p = toupper(*p);
00220         p++;
00221     }
00222 
00223     return string;
00224 }
00225 
00226 
00227 // true if all the characters in "string" are isalnum().
00228 bool is_word(const char *p)
00229 {
00230     assert(p);
00231 
00232     while (*p)
00233     {
00234         if(!isalnum(*p++))
00235             return false;
00236     }
00237     
00238     return true;
00239 }
00240 
00241 
00242 // produce a hexadecimal dump of the data buffer in 'buf' of length 'len'.
00243 // it is formatted with 16 bytes per line; each line has an address offset,
00244 // hex representation, and printable representation.
00245 WvString hexdump_buffer(const void *_buf, size_t len, bool charRep)
00246 {
00247     const unsigned char *buf = (const unsigned char *)_buf;
00248     size_t count, count2, top;
00249     WvString out;
00250 
00251     out.setsize(len / 16 * 80 + 80);
00252     char *cptr = out.edit();
00253     
00254     for (count = 0; count < len; count+=16)
00255     {
00256         top = len-count < 16 ? len-count : 16;
00257         cptr += sprintf(cptr, "[%03X] ", (unsigned int)count);
00258         
00259         // dump hex values
00260         for (count2 = 0; count2 < top; count2++)
00261         {
00262             if (count2 && !(count2 % 4))
00263                 *cptr++ = ' ';
00264             cptr += sprintf(cptr, "%02X", buf[count+count2]);
00265         }
00266         
00267         // print horizontal separation
00268         for (count2 = top; count2 < 16; count2++)
00269         {
00270             if (count2 && !(count2 % 4))
00271             {
00272                 strcat(cptr, "   ");
00273                 cptr += 3;
00274             }
00275             else
00276             {
00277                 strcat(cptr, "  ");
00278                 cptr += 2;
00279             }
00280         }
00281         
00282         *cptr++ = ' ';
00283         
00284         // dump character representation
00285         if (charRep)
00286         {
00287             for (count2 = 0; count2 < top; count2++)
00288             {
00289                 if (!(count2 % 4))
00290                     *cptr++ = ' ';
00291                 *cptr++ = (isprint(buf[count+count2])
00292                            ? buf[count+count2] : '.');
00293             }
00294         }
00295 
00296         *cptr++ = '\n';
00297     }
00298     *cptr = 0;
00299     return out;
00300 }
00301 
00302 
00303 // return true if the character is a newline.
00304 bool isnewline(char c)
00305 {
00306     return c=='\n' || c=='\r';
00307 }
00308 
00309 
00310 // ex: WvString foo = url_decode("I+am+text.%0D%0A");
00311 WvString url_decode(WvStringParm str, bool no_space)
00312 {
00313     if (!str)
00314         return str;
00315  
00316     const char *iptr;
00317     char *optr;
00318     char *idx1, *idx2;
00319     static const char hex[] = "0123456789ABCDEF";
00320     WvString in, intmp(str), out;
00321 
00322     in = trim_string(intmp.edit());
00323     out.setsize(strlen(in) + 1);
00324 
00325     optr = out.edit();
00326     for (iptr = in, optr = out.edit(); *iptr; iptr++)
00327     {
00328         if (*iptr == '+' && !no_space)
00329             *optr++ = ' ';
00330         else if (*iptr == '%' && iptr[1] && iptr[2])
00331         {
00332             idx1 = strchr((char*)hex, toupper((unsigned char) iptr[1]));
00333             idx2 = strchr((char*)hex, toupper((unsigned char) iptr[2]));
00334 
00335             if (idx1 && idx2)
00336                 *optr++ = ((idx1 - hex) << 4) | (idx2 - hex);
00337 
00338             iptr += 2;
00339         }
00340         else
00341             *optr++ = *iptr;
00342     }
00343 
00344     *optr = 0;
00345 
00346     return out;
00347 }
00348 
00349 
00350 // And its magic companion: url_encode
00351 WvString url_encode(WvStringParm str, WvStringParm unsafe)
00352 {
00353     unsigned int i;
00354     WvDynBuf retval;
00355 
00356     for (i=0; i < str.len(); i++)
00357     {
00358         if (((!!unsafe && !strchr(unsafe, str[i])) ||
00359              (!unsafe && (isalnum(str[i]) || strchr("_.!~*'()-", str[i])))) &&
00360             str[i] != '%')
00361         {
00362             retval.put(&str[i], 1);
00363         }
00364         else
00365         {               
00366             char buf[4];
00367             sprintf(buf, "%%%02X", str[i] & 0xff);
00368             retval.put(&buf, 3);
00369         }
00370     }
00371 
00372     return retval.getstr();
00373 }
00374 
00375 
00376 WvString diff_dates(time_t t1, time_t t2)
00377 {
00378     char out[25]; //Should be more then enough
00379     double diff = difftime(t1, t2);
00380     if(diff < 0)
00381         diff = -diff;
00382     if(diff > (60 * 60 * 24))
00383         //give a touch more granularity then the rest
00384         sprintf(out, "%.1f day(s)", diff / (60 * 60 * 24));
00385     else if(diff > (60 * 60)) 
00386         sprintf(out, "%.0f hour(s)", diff / (60 * 60));
00387     else if(diff > 60)
00388         sprintf(out, "%.0f minute(s)", diff / 60);
00389     else
00390         sprintf(out, "%.0f second(s)", diff);
00391     return out;
00392 }
00393 
00394 
00395 WvString rfc822_date(time_t when)
00396 {
00397     WvString out;
00398     out.setsize(80);
00399 
00400     if (when < 0)
00401         when = time(NULL);
00402 
00403     struct tm *tmwhen = localtime(&when);
00404     strftime(out.edit(), 80, "%a, %d %b %Y %H:%M:%S %z", tmwhen);
00405 
00406     return out;
00407 }
00408 
00409 
00410 WvString backslash_escape(WvStringParm s1)
00411 {
00412     // stick a backslash in front of every !isalnum() character in s1
00413     if (!s1)
00414         return "";
00415 
00416     WvString s2;
00417     s2.setsize(s1.len() * 2 + 1);
00418 
00419     const char *p1 = s1;
00420     char *p2 = s2.edit();
00421     while (*p1)
00422     {
00423         if (!isalnum(*p1))
00424             *p2++ = '\\';
00425         *p2++ = *p1++;
00426     }
00427     *p2 = 0;
00428 
00429     return s2;
00430 }
00431 
00432 
00433 int strcount(WvStringParm s, const char c)
00434 {
00435     int n=0;
00436     const char *p = s;
00437     while ((p=strchr(p, c)) != NULL && p++)
00438         n++;
00439 
00440     return n;
00441 }
00442 
00443 
00444 WvString encode_hostname_as_DN(WvStringParm hostname)
00445 {
00446     WvString dn("");
00447     
00448     WvStringList fqdnlist;
00449     WvStringList::Iter i(fqdnlist);
00450     
00451     fqdnlist.split(hostname, ".");
00452     for (i.rewind(); i.next(); )
00453         dn.append("dc=%s,", *i);
00454     dn.append("cn=%s", hostname);
00455     
00456     return dn;
00457 }
00458 
00459 
00460 WvString nice_hostname(WvStringParm name)
00461 {
00462     WvString nice;
00463     char *optr, *optr_start;
00464     const char *iptr;
00465     bool last_was_dash;
00466     
00467     nice.setsize(name.len() + 2);
00468 
00469     iptr = name;
00470     optr = optr_start = nice.edit();
00471     if (!isascii(*iptr) || !isalnum(*(const unsigned char *)iptr))
00472         *optr++ = 'x'; // DNS names must start with a letter!
00473     
00474     last_was_dash = false;
00475     for (; *iptr; iptr++)
00476     {
00477         if (!isascii(*iptr))
00478             continue; // skip it entirely
00479         
00480         if (*iptr == '-' || *iptr == '_')
00481         {
00482             if (last_was_dash)
00483                 continue;
00484             last_was_dash = true;
00485             *optr++ = '-';
00486         }
00487         else if (isalnum(*(const unsigned char *)iptr) || *iptr == '.')
00488         {
00489             *optr++ = *iptr;
00490             last_was_dash = false;
00491         }
00492     }
00493     
00494     if (optr > optr_start && !isalnum(*(const unsigned char *)(optr-1)))
00495         *optr++ = 'x'; // must _end_ in a letter/number too!
00496     
00497     *optr++ = 0;
00498     
00499     if (!nice.len())
00500         return "UNKNOWN";
00501     
00502     return nice;
00503 }
00504 
00505 
00506 WvString getfilename(WvStringParm fullname)
00507 {
00508     WvString tmp(fullname);
00509     char *cptr = strrchr(tmp.edit(), '/');
00510     
00511     if (!cptr) // no slash at all
00512         return fullname;
00513     else if (!cptr[1]) // terminating slash
00514     {
00515         *cptr = 0;
00516         return getfilename(tmp);
00517     }
00518     else // no terminating slash
00519         return cptr+1;
00520 }
00521 
00522 
00523 WvString getdirname(WvStringParm fullname)
00524 {
00525     WvString tmp(fullname);
00526     char *cptr = strrchr(tmp.edit(), '/');
00527     
00528     if (!cptr) // no slash at all
00529         return ".";
00530     else if (!cptr[1]) // terminating slash
00531     {
00532         *cptr = 0;
00533         return getdirname(tmp);
00534     }
00535     else // no terminating slash
00536     {
00537         *cptr = 0;
00538         return !tmp ? WvString("/") : tmp;
00539     }
00540 }
00541 
00542 // Programmatically determine the units.  In order, these are:
00543 // bytes, kilobytes, megabytes, gigabytes, terabytes, petabytes,
00544 // exabytes, zettabytes, yottabytes.  Note that these are SI
00545 // prefixes, not binary ones.
00546 
00547 // This structure allows us to choose between SI-prefixes which are
00548 // powers of 10, and IEC-prefixes which are powers of 2.
00549 struct prefix_t
00550 {
00551     const char *name;
00552     unsigned long long base;
00553 };
00554 
00555 // SI-prefixes:
00556 // kilo, mega, giga, tera, peta, and exa.
00557 static const prefix_t si[] =
00558 {
00559     { "k", 1000ull },
00560     { "M", 1000ull * 1000ull },
00561     { "G", 1000ull * 1000ull * 1000ull },
00562     { "T", 1000ull * 1000ull * 1000ull * 1000ull },
00563     { "P", 1000ull * 1000ull * 1000ull * 1000ull * 1000ull},
00564     { "E", 1000ull * 1000ull * 1000ull * 1000ull * 1000ull * 1000ull},
00565     { "Z", 0 },
00566     { "Y", 0 },
00567     { NULL, 0 }
00568 };
00569 
00570 // IEC-prefixes:
00571 // kibi, mebi, gibi, tebi, pebi, and exbi.
00572 static const prefix_t iec[] =
00573 {
00574     { "Ki", 1024ull },
00575     { "Mi", 1024ull * 1024ull},
00576     { "Gi", 1024ull * 1024ull * 1024ull },
00577     { "Ti", 1024ull * 1024ull * 1024ull * 1024ull },
00578     { "Pi", 1024ull * 1024ull * 1024ull * 1024ull * 1024ull},
00579     { "Ei", 1024ull * 1024ull * 1024ull * 1024ull * 1024ull * 1024ull},
00580     { "Zi", 0 },
00581     { "Yi", 0 },
00582     { NULL, 0 }
00583 };
00584 
00585 
00586 // This function expects size to be ten-times the actual number.
00587 static inline unsigned long long _sizetoa_rounder(RoundingMethod method,
00588                                                   unsigned long long size,
00589                                                   unsigned long long remainder,
00590                                                   unsigned long long base)
00591 {
00592     unsigned long long half = base / 2;
00593     unsigned long long significant_digits = size / base;
00594     switch (method)
00595     {
00596     case ROUND_DOWN:
00597         break;
00598 
00599     case ROUND_UP:
00600         if (remainder || (size % base))
00601             ++significant_digits;
00602         break;
00603 
00604     case ROUND_UP_AT_POINT_FIVE:
00605         if ((size % base) >= half)
00606             ++significant_digits;
00607         break;
00608 
00609     case ROUND_DOWN_AT_POINT_FIVE:
00610         unsigned long long r = size % base;
00611         if ((r > half) || (remainder && (r == half)))
00612             ++significant_digits;
00613         break;
00614     }
00615     return significant_digits;
00616 }
00617 
00618 
00619 // This function helps sizetoa() and sizektoa() below.  It takes a
00620 // bunch of digits, and the default unit (indexed by size); and turns
00621 // them into a WvString that's formatted to human-readable rounded
00622 // sizes, with one decimal place.
00623 //
00624 // You must be very careful here never to add anything to size.
00625 // Otherwise, you might cause an overflow to occur.  Similarly, you
00626 // must be careful when you subtract or you might cause an underflow.
00627 static WvString _sizetoa(unsigned long long size, unsigned long blocksize,
00628                          RoundingMethod rounding_method,
00629                          const prefix_t *prefixes, WvStringParm unit)
00630 {
00631     assert(blocksize);
00632 
00633     // To understand rounding, consider the display of the value 999949.
00634     // For each rounding method the string displayed should be:
00635     // ROUND_DOWN: 999.9 kB
00636     // ROUND_UP_AT_POINT_FIVE: 999.9 kB
00637     // ROUND_UP: 1.0 MB
00638     // On the other hand, for the value 999950, the strings should be:
00639     // ROUND_DOWN: 999.9 kB
00640     // ROUND_DOWN_AT_POINT_FIVE: 999.9 kB
00641     // ROUND_UP_AT_POINT_FIVE: 1.0 MB
00642     // ROUND_UP: 1.0 MB
00643 
00644     // Deal with blocksizes without overflowing.
00645     const unsigned long long group_base = prefixes[0].base;
00646     int shift = 0;
00647     unsigned long prev_blocksize = 0;
00648     while (blocksize >= group_base)
00649     {
00650         prev_blocksize = blocksize;
00651         blocksize /= group_base;
00652         ++shift;
00653     }
00654 
00655     // If we have a very large blocksize, make sure to keep enough of
00656     // it to make rounding possible.
00657     if (prev_blocksize && prev_blocksize != group_base)
00658     {
00659         blocksize = prev_blocksize;
00660         --shift;
00661     }
00662 
00663     int p = -1;
00664     unsigned long long significant_digits = size * 10;
00665     unsigned int remainder = 0;
00666     if (significant_digits < size)
00667     {
00668         // A really big size.  We'll divide by a grouping before going up one.
00669         remainder = size % group_base;
00670         size /= group_base;
00671         ++shift;
00672     }
00673     while (size >= group_base)
00674     {
00675         ++p;
00676         significant_digits = _sizetoa_rounder(rounding_method,
00677                                               size * 10,
00678                                               remainder,
00679                                               prefixes[p].base);
00680         if (significant_digits < (group_base * 10)
00681             || !prefixes[p + shift + 1].name)
00682             break;
00683     }
00684 
00685     // Correct for blocksizes that aren't powers of group_base.
00686     if (blocksize > 1)
00687     {
00688         significant_digits *= blocksize;
00689         while (significant_digits >= (group_base * 10)
00690                && prefixes[p + shift + 1].name)
00691         {
00692             significant_digits = _sizetoa_rounder(rounding_method,
00693                                                   significant_digits,
00694                                                   0,
00695                                                   group_base);
00696             ++p;
00697         }
00698     }
00699 
00700     // Now we can return our result.
00701     return WvString("%s.%s %s%s",
00702                     significant_digits / 10,
00703                     significant_digits % 10,
00704                     prefixes[p + shift].name,
00705                     unit);
00706 }
00707 
00708 WvString sizetoa(unsigned long long blocks, unsigned long blocksize,
00709                  RoundingMethod rounding_method)
00710 {
00711     unsigned long long bytes = blocks * blocksize;
00712 
00713     // Test if we are dealing in just bytes.
00714     if (bytes < 1000 && bytes >= blocks)
00715         return WvString("%s bytes", bytes);
00716 
00717     return _sizetoa(blocks, blocksize, rounding_method, si, "B");
00718 }
00719 
00720 
00721 WvString sizektoa(unsigned long long kbytes, RoundingMethod rounding_method)
00722 {
00723     if (kbytes < 1000)
00724         return WvString("%s kB", kbytes);
00725 
00726     return sizetoa(kbytes, 1000, rounding_method);
00727 }
00728 
00729 WvString sizeitoa(unsigned long long blocks, unsigned long blocksize,
00730                   RoundingMethod rounding_method)
00731 {
00732     unsigned long long bytes = blocks * blocksize;
00733 
00734     // Test if we are dealing in just bytes.
00735     if (bytes < 1024 && bytes >= blocks)
00736         return WvString("%s bytes", bytes);
00737 
00738     return _sizetoa(blocks, blocksize, rounding_method, iec, "B");
00739 }
00740 
00741 
00742 WvString sizekitoa(unsigned long long kbytes, RoundingMethod rounding_method)
00743 {
00744     if (kbytes < 1024)
00745         return WvString("%s KiB", kbytes);
00746 
00747     return sizeitoa(kbytes, 1024, rounding_method);
00748 }
00749 
00750 WvString secondstoa(unsigned int total_seconds)
00751 {
00752     WvString result("");
00753 
00754     unsigned int days = total_seconds / (3600*24);
00755     total_seconds %= (3600*24);
00756     unsigned int hours = total_seconds / 3600;
00757     total_seconds %= 3600;
00758     unsigned int mins = total_seconds / 60;
00759     unsigned int secs = total_seconds % 60; 
00760 
00761     int num_elements = (days > 0) + (hours > 0) + (mins > 0);
00762 
00763     if (days > 0)
00764     {
00765         result.append(days);
00766         result.append(days > 1 ? " days" : " day");
00767         num_elements--;
00768         if (num_elements > 1)
00769             result.append(", ");
00770         else if (num_elements == 1)
00771             result.append(" and ");
00772     }
00773     if (hours > 0)
00774     {
00775         result.append(hours);
00776         result.append(hours > 1 ? " hours" : " hour");
00777         num_elements--;
00778         if (num_elements > 1)
00779             result.append(", ");
00780         else if (num_elements == 1)
00781             result.append(" and ");
00782     }
00783     if (mins > 0)
00784     {
00785         result.append(mins);
00786         result.append(mins > 1 ? " minutes" : " minute");
00787     }
00788     if (days == 0 && hours == 0 && mins == 0)
00789     {
00790         result.append(secs);
00791         result.append(secs != 1 ? " seconds" : " second");
00792     }
00793 
00794     return result;
00795 }
00796 
00797 WvString strreplace(WvStringParm s, WvStringParm a, WvStringParm b)
00798 {
00799     WvDynBuf buf;
00800     const char *sptr = s, *eptr;
00801     
00802     while ((eptr = strstr(sptr, a)) != NULL)
00803     {
00804         buf.put(sptr, eptr-sptr);
00805         buf.putstr(b);
00806         sptr = eptr + strlen(a);
00807     }
00808     
00809     buf.put(sptr, strlen(sptr));
00810     
00811     return buf.getstr();
00812 }
00813 
00814 WvString undupe(WvStringParm s, char c)
00815 {
00816     WvDynBuf out;
00817 
00818     bool last = false;
00819 
00820     for (int i = 0; s[i] != '\0'; i++)
00821     {
00822         if (s[i] != c)
00823         {
00824             out.putch(s[i]);
00825             last = false;
00826         }
00827         else if (!last)
00828         {
00829             out.putch(c);
00830             last = true;
00831         }
00832     }
00833     
00834     return out.getstr();
00835 }
00836 
00837 
00838 WvString rfc1123_date(time_t t)
00839 {
00840     struct tm *tm = gmtime(&t);
00841     WvString s;
00842 
00843     s.setsize(128);
00844     strftime(s.edit(), 128, "%a, %d %b %Y %H:%M:%S GMT", tm);
00845 
00846     return s;
00847 }
00848 
00849 
00850 int lookup(const char *str, const char * const *table, bool case_sensitive)
00851 {
00852     for (int i = 0; table[i]; ++i)
00853     {
00854         if (case_sensitive)
00855         {
00856             if (strcmp(str, table[i]) != 0)
00857                 continue;
00858         }
00859         else
00860         {
00861             if (strcasecmp(str, table[i]) != 0)
00862                 continue;
00863         }
00864         return i;
00865     }
00866     return -1;
00867 }
00868 
00869 
00870 WvString hostname()
00871 {
00872     int maxlen = 0;
00873     for (;;)
00874     {
00875         maxlen += 80;
00876         char *name = new char[maxlen];
00877         int result = gethostname(name, maxlen);
00878         if (result == 0)
00879         {
00880             WvString hostname(name);
00881             deletev name;
00882             return hostname;
00883         }
00884 #ifdef _WIN32
00885         assert(errno == WSAEFAULT);
00886 #else
00887         assert(errno == EINVAL);
00888 #endif
00889     }
00890 }
00891 
00892 
00893 WvString fqdomainname() 
00894 {
00895     struct hostent *myhost;
00896 
00897     myhost = gethostbyname(hostname());
00898     if (myhost)
00899         return myhost->h_name;
00900     else
00901         return WvString::null;
00902 }
00903 
00904 
00905 WvString wvgetcwd()
00906 {
00907     int maxlen = 0;
00908     for (;;)
00909     {
00910         maxlen += 80;
00911         char *name = new char[maxlen];
00912         char *res = getcwd(name, maxlen);
00913         if (res)
00914         {
00915             WvString s(name);
00916             deletev name;
00917             return s;
00918         }
00919         if (errno == EACCES || errno == ENOENT)
00920             return "."; // can't deal with those errors
00921         assert(errno == ERANGE); // buffer too small
00922     }
00923 }
00924 
00925 
00926 WvString metriculate(const off_t i)
00927 {
00928     WvString res;
00929     int digits=0;
00930     int digit=0;
00931     long long int j=i;
00932     char *p;
00933 
00934     while (j)
00935     {
00936         digits++;
00937         j/=10;
00938     }
00939 
00940     j=i;
00941     // setsize says it takes care of the terminating NULL char
00942     res.setsize(digits + ((digits - 1) / 3) + ((j < 0) ? 1 : 0));
00943     p = res.edit();
00944     if (j < 0)
00945     {
00946         *p++ = '-';
00947         j = -j;
00948     }
00949 
00950     p += digits + ((digits - 1) / 3);
00951     *p-- = '\0';
00952 
00953     for (digit=0; digit<digits; digit++)
00954     {
00955         *p-- = '0' + ( j%10 );
00956         if (((digit+1) % 3) == 0 && digit < digits - 1)
00957             *p-- = ' ';
00958         j /= 10;
00959     }
00960 
00961     return res;
00962 }
00963 
00964 
00965 WvString afterstr(WvStringParm line, WvStringParm a)
00966 {
00967     if (!line || !a)
00968         return WvString::null;
00969 
00970     char *loc = (char*)strstr(line, a);
00971     if (loc == 0)
00972         return "";
00973 
00974     loc += a.len();
00975     WvString ret = loc;
00976     ret.unique();
00977     return ret;
00978 }
00979 
00980 
00981 WvString beforestr(WvStringParm line, WvStringParm a)
00982 {
00983     if (!line || !a)
00984         return WvString::null;
00985 
00986     WvString ret = line;
00987     ret.unique();     
00988     char *loc = (char*)strstr(ret, a);
00989 
00990     if (loc == 0)
00991         return line;
00992 
00993     loc[0] = '\0';
00994     return ret;
00995 }
00996 
00997 
00998 WvString substr(WvString line, unsigned int pos, unsigned int len)
00999 {
01000     const char *tmp = line.cstr();
01001     if (pos > line.len()-1)
01002         return "";
01003     tmp += pos;
01004 
01005     WvString ret = tmp;
01006     char *tmp2 = ret.edit();
01007     if (pos + len < line.len())
01008         tmp2[len] = '\0';
01009 
01010     return ret;
01011 }
01012 
01013 const CStrExtraEscape CSTR_TCLSTR_ESCAPES[3] =
01014 {
01015     { '{', "\\<" },
01016     { '}', "\\>" },
01017     { 0, NULL }
01018 };
01019 
01020 static inline const char *cstr_escape_char(char ch)
01021 {
01022     static const char *xlat[256] =
01023     {
01024         "\\0", "\\x01", "\\x02", "\\x03", "\\x04", "\\x05", "\\x06", "\\a", 
01025         "\\b", "\\t", "\\n", "\\v", "\\x0C", "\\r", "\\x0E", "\\x0F", 
01026         "\\x10", "\\x11", "\\x12", "\\x13", "\\x14", "\\x15", "\\x16", "\\x17", 
01027         "\\x18", "\\x19", "\\x1A", "\\x1B", "\\x1C", "\\x1D", "\\x1E", "\\x1F", 
01028         " ", "!", "\\\"", "#", "$", "%", "&", "'", 
01029         "(", ")", "*", "+", ",", "-", ".", "/", 
01030         "0", "1", "2", "3", "4", "5", "6", "7", 
01031         "8", "9", ":", ";", "<", "=", ">", "?", 
01032         "@", "A", "B", "C", "D", "E", "F", "G", 
01033         "H", "I", "J", "K", "L", "M", "N", "O", 
01034         "P", "Q", "R", "S", "T", "U", "V", "W", 
01035         "X", "Y", "Z", "[", "\\\\", "]", "^", "_", 
01036         "`", "a", "b", "c", "d", "e", "f", "g", 
01037         "h", "i", "j", "k", "l", "m", "n", "o", 
01038         "p", "q", "r", "s", "t", "u", "v", "w", 
01039         "x", "y", "z", "{", "|", "}", "~", "\\x7F", 
01040         "\\x80", "\\x81", "\\x82", "\\x83", "\\x84", "\\x85", "\\x86", "\\x87", 
01041         "\\x88", "\\x89", "\\x8A", "\\x8B", "\\x8C", "\\x8D", "\\x8E", "\\x8F", 
01042         "\\x90", "\\x91", "\\x92", "\\x93", "\\x94", "\\x95", "\\x96", "\\x97", 
01043         "\\x98", "\\x99", "\\x9A", "\\x9B", "\\x9C", "\\x9D", "\\x9E", "\\x9F", 
01044         "\\xA0", "\\xA1", "\\xA2", "\\xA3", "\\xA4", "\\xA5", "\\xA6", "\\xA7", 
01045         "\\xA8", "\\xA9", "\\xAA", "\\xAB", "\\xAC", "\\xAD", "\\xAE", "\\xAF", 
01046         "\\xB0", "\\xB1", "\\xB2", "\\xB3", "\\xB4", "\\xB5", "\\xB6", "\\xB7", 
01047         "\\xB8", "\\xB9", "\\xBA", "\\xBB", "\\xBC", "\\xBD", "\\xBE", "\\xBF", 
01048         "\\xC0", "\\xC1", "\\xC2", "\\xC3", "\\xC4", "\\xC5", "\\xC6", "\\xC7", 
01049         "\\xC8", "\\xC9", "\\xCA", "\\xCB", "\\xCC", "\\xCD", "\\xCE", "\\xCF", 
01050         "\\xD0", "\\xD1", "\\xD2", "\\xD3", "\\xD4", "\\xD5", "\\xD6", "\\xD7", 
01051         "\\xD8", "\\xD9", "\\xDA", "\\xDB", "\\xDC", "\\xDD", "\\xDE", "\\xDF", 
01052         "\\xE0", "\\xE1", "\\xE2", "\\xE3", "\\xE4", "\\xE5", "\\xE6", "\\xE7", 
01053         "\\xE8", "\\xE9", "\\xEA", "\\xEB", "\\xEC", "\\xED", "\\xEE", "\\xEF", 
01054         "\\xF0", "\\xF1", "\\xF2", "\\xF3", "\\xF4", "\\xF5", "\\xF6", "\\xF7", 
01055         "\\xF8", "\\xF9", "\\xFA", "\\xFB", "\\xFC", "\\xFD", "\\xFE", "\\xFF"
01056     };
01057     return xlat[(unsigned char)ch];
01058 }
01059 
01060 static inline int hex_digit_val(char ch)
01061 {
01062     static int val[256] =
01063     {
01064         -1, -1, -1, -1, -1, -1, -1, -1,
01065         -1, -1, -1, -1, -1, -1, -1, -1,
01066         -1, -1, -1, -1, -1, -1, -1, -1,
01067         -1, -1, -1, -1, -1, -1, -1, -1,
01068         -1, -1, -1, -1, -1, -1, -1, -1,
01069         -1, -1, -1, -1, -1, -1, -1, -1,
01070         0, 1, 2, 3, 4, 5, 6, 7, 
01071         8, 9, -1, -1, -1, -1, -1, -1,
01072         -1, 10, 11, 12, 13, 14, 15, -1,
01073         -1, -1, -1, -1, -1, -1, -1, -1,
01074         -1, -1, -1, -1, -1, -1, -1, -1,
01075         -1, -1, -1, -1, -1, -1, -1, -1,
01076         -1, 10, 11, 12, 13, 14, 15, -1,
01077         -1, -1, -1, -1, -1, -1, -1, -1,
01078         -1, -1, -1, -1, -1, -1, -1, -1,
01079         -1, -1, -1, -1, -1, -1, -1, -1,
01080         -1, -1, -1, -1, -1, -1, -1, -1,
01081         -1, -1, -1, -1, -1, -1, -1, -1,
01082         -1, -1, -1, -1, -1, -1, -1, -1,
01083         -1, -1, -1, -1, -1, -1, -1, -1,
01084         -1, -1, -1, -1, -1, -1, -1, -1,
01085         -1, -1, -1, -1, -1, -1, -1, -1,
01086         -1, -1, -1, -1, -1, -1, -1, -1,
01087         -1, -1, -1, -1, -1, -1, -1, -1,
01088         -1, -1, -1, -1, -1, -1, -1, -1,
01089         -1, -1, -1, -1, -1, -1, -1, -1,
01090         -1, -1, -1, -1, -1, -1, -1, -1,
01091         -1, -1, -1, -1, -1, -1, -1, -1,
01092         -1, -1, -1, -1, -1, -1, -1, -1,
01093         -1, -1, -1, -1, -1, -1, -1, -1,
01094         -1, -1, -1, -1, -1, -1, -1, -1,
01095         -1, -1, -1, -1, -1, -1, -1, -1
01096     };
01097     return val[(unsigned char)ch];
01098 }
01099 
01100 static inline bool cstr_unescape_char(const char *&cstr, char &ch)
01101 {
01102     if (*cstr == '\\')
01103     {
01104         ++cstr;
01105     
01106         switch (*cstr)
01107         {
01108             case '"': ch = '"'; break;
01109             case 't': ch = '\t'; break;
01110             case 'n': ch = '\n'; break;
01111             case '\\': ch = '\\'; break;
01112             case 'r': ch = '\r'; break;
01113             case 'a': ch = '\a'; break;
01114             case 'v': ch = '\v'; break;
01115             case 'b': ch = '\b'; break;
01116             case '0': ch = '\0'; break;
01117             case 'x':
01118             {
01119                 int vals[2];
01120                 int i;
01121                 for (i=0; i<2; ++i)
01122                 {
01123                     if ((vals[i] = hex_digit_val(*++cstr)) == -1)
01124                         return false;
01125                 }
01126                 ch = (vals[0] << 4) | vals[1];
01127             }
01128             break;
01129             default: return false;
01130         }
01131         
01132         ++cstr;
01133         
01134         return true;
01135     }
01136     else
01137     {
01138         ch = *cstr++;
01139         return true;
01140     }
01141 }
01142 
01143 WvString cstr_escape(const void *data, size_t size,
01144         const CStrExtraEscape extra_escapes[])
01145 {
01146     if (!data) return WvString::null;
01147 
01148     const char *cdata = (const char *)data;
01149 
01150     WvString result;
01151     result.setsize(4*size + 3); // We could do better but it would slow us down
01152     char *cstr = result.edit();
01153     
01154     *cstr++ = '\"';
01155     while (size-- > 0)
01156     {
01157         const char *esc = NULL;
01158         if (extra_escapes)
01159         {
01160             const CStrExtraEscape *extra = &extra_escapes[0];
01161             while (extra->ch && extra->esc)
01162             {
01163                 if (*cdata == extra->ch)
01164                 {
01165                     esc = extra->esc;
01166                     break;
01167                 }
01168                 
01169                 ++extra;
01170             }
01171         }
01172         if (!esc) esc = cstr_escape_char(*cdata);
01173         ++cdata;
01174         while (*esc) *cstr++ = *esc++;
01175     }
01176     *cstr++ = '\"';
01177     *cstr = '\0';
01178     
01179     return result;
01180 }
01181 
01182 bool cstr_unescape(WvStringParm cstr, void *data, size_t max_size, size_t &size,
01183         const CStrExtraEscape extra_escapes[])
01184 {
01185     const char *q = cstr;
01186     char *cdata = (char *)data;
01187     
01188     if (!q) goto misformatted;
01189     size = 0;
01190     
01191     for (;;)
01192     {
01193         while (isspace(*q)) q++;
01194         if (*q == '\0') break;
01195 
01196         if (*q++ != '\"') goto misformatted;
01197         while (*q && *q != '\"')
01198         {
01199             bool found = false;
01200             char unesc;
01201             if (extra_escapes)
01202             {
01203                 const CStrExtraEscape *extra = &extra_escapes[0];
01204                 while (extra->ch && extra->esc)
01205                 {
01206                     size_t len = strlen(extra->esc);
01207                     if (strncmp(extra->esc, q, len) == 0)
01208                     {
01209                         unesc = extra->ch;
01210                         q += len;
01211                         found = true;
01212                         break;
01213                     }
01214                     
01215                     ++extra;
01216                 }
01217             }
01218             if (!found && !cstr_unescape_char(q, unesc)) goto misformatted;
01219             if (size++ < max_size && cdata) *cdata++ = unesc;
01220         }
01221         if (*q++ != '\"') goto misformatted;
01222     }
01223     
01224     return size <= max_size;
01225 
01226 misformatted:
01227 
01228     size = 0;
01229     return false;
01230 }
01231 
01232 WvString local_date(time_t when)
01233 {
01234     WvString out;
01235     out.setsize(80);
01236 
01237     if (when < 0)
01238         when = time(NULL);
01239 
01240     struct tm *tmwhen = localtime(&when);
01241     strftime(out.edit(), 80, "%b %d %I:%M:%S %p", tmwhen);
01242 
01243     return out;
01244 }
01245 
01246 WvString intl_time(time_t when)
01247 {
01248     WvString out;
01249     out.setsize(12);
01250 
01251     if (when < 0)
01252         when = time(NULL);
01253 
01254     struct tm *tmwhen = localtime(&when); 
01255     strftime(out.edit(), 12, "%H:%M:%S", tmwhen);
01256 
01257     return out;
01258 }
01259 
01260 WvString intl_date(time_t when)
01261 {
01262     WvString out;
01263     out.setsize(16);
01264 
01265     if (when < 0)
01266         when = time(NULL);
01267 
01268     struct tm *tmwhen = localtime(&when); 
01269     strftime(out.edit(), 16, "%Y-%m-%d", tmwhen);
01270 
01271     return out;
01272 }
01273 
01274 WvString intl_datetime(time_t when)
01275 {
01276     WvString out;
01277     out.setsize(24);
01278 
01279     if (when < 0)
01280         when = time(NULL);
01281 
01282     struct tm *tmwhen = localtime(&when); 
01283     strftime(out.edit(), 24, "%Y-%m-%d %H:%M:%S", tmwhen);
01284 
01285     return out;
01286 }
01287 
01288 
01294 time_t intl_gmtoff(time_t t)
01295 {
01296     struct tm *l = localtime(&t);
01297     l->tm_isdst = 0;
01298     time_t local = mktime(l);
01299     time_t gmt   = mktime(gmtime(&t));
01300     
01301     return local-gmt;
01302 }
01303 
01304 
01305 // Removes any trailing punctuation ('.', '?', or '!') from the line
01306 WvString depunctuate(WvStringParm line)
01307 {
01308     WvString ret = line;
01309     char * edit = ret.edit();
01310     int last = ret.len() - 1;
01311     if (edit[last] == '.' || edit[last] == '?' || edit[last] == '!')
01312         edit[last] = '\0';
01313 
01314     return ret;
01315 }
01316 
01317 
01318 WvString ptr2str(void* ptr)
01319 {
01320     char buf[(sizeof(ptr) * 2) + 3];
01321     int rv;
01322 
01323     rv = snprintf(buf, sizeof(buf), "%p", ptr);
01324 
01325     assert(rv != -1);
01326 
01327     return buf;
01328 }