WvStreams
|
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(" "); 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 }