X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/ae0b19f5f12d5a44189de636f79393680cf19167..0a64ecd792b4caca519d597a69b8baeda6a1bf77:/apt-pkg/contrib/strutl.cc?ds=inline diff --git a/apt-pkg/contrib/strutl.cc b/apt-pkg/contrib/strutl.cc index e49794f6a..8dd05b9c0 100644 --- a/apt-pkg/contrib/strutl.cc +++ b/apt-pkg/contrib/strutl.cc @@ -1,6 +1,6 @@ // -*- mode: cpp; mode: fold -*- // Description /*{{{*/ -// $Id: strutl.cc,v 1.43 2001/05/29 05:09:44 jgg Exp $ +// $Id: strutl.cc,v 1.48 2003/07/18 14:15:11 mdz Exp $ /* ###################################################################### String Util - Some useful string functions. @@ -15,10 +15,6 @@ ##################################################################### */ /*}}}*/ // Includes /*{{{*/ -#ifdef __GNUG__ -#pragma implementation "apt-pkg/strutl.h" -#endif - #include #include #include @@ -28,14 +24,88 @@ #include #include #include +#include #include #include #include #include +#include + +#include "config.h" using namespace std; /*}}}*/ +// UTF8ToCodeset - Convert some UTF-8 string for some codeset /*{{{*/ +// --------------------------------------------------------------------- +/* This is handy to use before display some information for enduser */ +bool UTF8ToCodeset(const char *codeset, const string &orig, string *dest) +{ + iconv_t cd; + const char *inbuf; + char *inptr, *outbuf; + size_t insize, bufsize; + dest->clear(); + + cd = iconv_open(codeset, "UTF-8"); + if (cd == (iconv_t)(-1)) { + // Something went wrong + if (errno == EINVAL) + _error->Error("conversion from 'UTF-8' to '%s' not available", + codeset); + else + perror("iconv_open"); + + return false; + } + + insize = bufsize = orig.size(); + inbuf = orig.data(); + inptr = (char *)inbuf; + outbuf = new char[bufsize]; + size_t lastError = -1; + + while (insize != 0) + { + char *outptr = outbuf; + size_t outsize = bufsize; + size_t const err = iconv(cd, &inptr, &insize, &outptr, &outsize); + dest->append(outbuf, outptr - outbuf); + if (err == (size_t)(-1)) + { + switch (errno) + { + case EILSEQ: + insize--; + inptr++; + // replace a series of unknown multibytes with a single "?" + if (lastError != insize) { + lastError = insize - 1; + dest->append("?"); + } + break; + case EINVAL: + insize = 0; + break; + case E2BIG: + if (outptr == outbuf) + { + bufsize *= 2; + delete[] outbuf; + outbuf = new char[bufsize]; + } + break; + } + } + } + + delete[] outbuf; + + iconv_close(cd); + + return true; +} + /*}}}*/ // strstrip - Remove white space from the front and back of a string /*{{{*/ // --------------------------------------------------------------------- /* This is handy to use when parsing a file. It also removes \n's left @@ -109,14 +179,14 @@ bool ParseQuoteWord(const char *&String,string &Res) { if (*C == '"') { - for (C++; *C != 0 && *C != '"'; C++); - if (*C == 0) + C = strchr(C + 1, '"'); + if (C == NULL) return false; } if (*C == '[') { - for (C++; *C != 0 && *C != ']'; C++); - if (*C == 0) + C = strchr(C + 1, ']'); + if (C == NULL) return false; } } @@ -128,7 +198,8 @@ bool ParseQuoteWord(const char *&String,string &Res) char *I; for (I = Buffer; I < Buffer + sizeof(Buffer) && Start != C; I++) { - if (*Start == '%' && Start + 2 < C) + if (*Start == '%' && Start + 2 < C && + isxdigit(Start[1]) && isxdigit(Start[2])) { Tmp[0] = Start[1]; Tmp[1] = Start[2]; @@ -197,13 +268,14 @@ bool ParseCWord(const char *&String,string &Res) // QuoteString - Convert a string into quoted from /*{{{*/ // --------------------------------------------------------------------- /* */ -string QuoteString(string Str,const char *Bad) +string QuoteString(const string &Str, const char *Bad) { string Res; - for (string::iterator I = Str.begin(); I != Str.end(); I++) + for (string::const_iterator I = Str.begin(); I != Str.end(); ++I) { if (strchr(Bad,*I) != 0 || isprint(*I) == 0 || - *I <= 0x20 || *I >= 0x7F) + *I == 0x25 || // percent '%' char + *I <= 0x20 || *I >= 0x7F) // control chars { char Buf[10]; sprintf(Buf,"%%%02x",(int)*I); @@ -218,12 +290,18 @@ string QuoteString(string Str,const char *Bad) // DeQuoteString - Convert a string from quoted from /*{{{*/ // --------------------------------------------------------------------- /* This undoes QuoteString */ -string DeQuoteString(string Str) +string DeQuoteString(const string &Str) +{ + return DeQuoteString(Str.begin(),Str.end()); +} +string DeQuoteString(string::const_iterator const &begin, + string::const_iterator const &end) { string Res; - for (string::const_iterator I = Str.begin(); I != Str.end(); I++) + for (string::const_iterator I = begin; I != end; ++I) { - if (*I == '%' && I + 2 < Str.end()) + if (*I == '%' && I + 2 < end && + isxdigit(I[1]) && isxdigit(I[2])) { char Tmp[3]; Tmp[0] = I[1]; @@ -262,13 +340,13 @@ string SizeToStr(double Size) { if (ASize < 100 && I != 0) { - sprintf(S,"%.1f%c",ASize,Ext[I]); + sprintf(S,"%'.1f %c",ASize,Ext[I]); break; } if (ASize < 10000) { - sprintf(S,"%.0f%c",ASize,Ext[I]); + sprintf(S,"%'.0f %c",ASize,Ext[I]); break; } ASize /= 1000.0; @@ -289,23 +367,27 @@ string TimeToStr(unsigned long Sec) { if (Sec > 60*60*24) { - sprintf(S,"%lid %lih%lim%lis",Sec/60/60/24,(Sec/60/60) % 24,(Sec/60) % 60,Sec % 60); + //d means days, h means hours, min means minutes, s means seconds + sprintf(S,_("%lid %lih %limin %lis"),Sec/60/60/24,(Sec/60/60) % 24,(Sec/60) % 60,Sec % 60); break; } if (Sec > 60*60) { - sprintf(S,"%lih%lim%lis",Sec/60/60,(Sec/60) % 60,Sec % 60); + //h means hours, min means minutes, s means seconds + sprintf(S,_("%lih %limin %lis"),Sec/60/60,(Sec/60) % 60,Sec % 60); break; } if (Sec > 60) { - sprintf(S,"%lim%lis",Sec/60,Sec % 60); + //min means minutes, s means seconds + sprintf(S,_("%limin %lis"),Sec/60,Sec % 60); break; } - - sprintf(S,"%lis",Sec); + + //s means seconds + sprintf(S,_("%lis"),Sec); break; } @@ -315,7 +397,7 @@ string TimeToStr(unsigned long Sec) // SubstVar - Substitute a string for another string /*{{{*/ // --------------------------------------------------------------------- /* This replaces all occurances of Subst with Contents in Str. */ -string SubstVar(string Str,string Subst,string Contents) +string SubstVar(const string &Str,const string &Subst,const string &Contents) { string::size_type Pos = 0; string::size_type OldPos = 0; @@ -341,26 +423,34 @@ string SubstVar(string Str,const struct SubstVar *Vars) return Str; } /*}}}*/ +// OutputInDepth - return a string with separator multiplied with depth /*{{{*/ +// --------------------------------------------------------------------- +/* Returns a string with the supplied separator depth + 1 times in it */ +std::string OutputInDepth(const unsigned long Depth, const char* Separator) +{ + std::string output = ""; + for(unsigned long d=Depth+1; d > 0; d--) + output.append(Separator); + return output; +} + /*}}}*/ // URItoFileName - Convert the uri into a unique file name /*{{{*/ // --------------------------------------------------------------------- /* This converts a URI into a safe filename. It quotes all unsafe characters and converts / to _ and removes the scheme identifier. The resulting file name should be unique and never occur again for a different file */ -string URItoFileName(string URI) +string URItoFileName(const string &URI) { // Nuke 'sensitive' items ::URI U(URI); - U.User = string(); - U.Password = string(); - U.Access = ""; + U.User.clear(); + U.Password.clear(); + U.Access.clear(); // "\x00-\x20{}|\\\\^\\[\\]<>\"\x7F-\xFF"; - URI = QuoteString(U,"\\|{}[]<>\"^~_=!@#$%^&*"); - string::iterator J = URI.begin(); - for (; J != URI.end(); J++) - if (*J == '/') - *J = '_'; - return URI; + string NewURI = QuoteString(U,"\\|{}[]<>\"^~_=!@#$%^&*"); + replace(NewURI.begin(),NewURI.end(),'/','_'); + return NewURI; } /*}}}*/ // Base64Encode - Base64 Encoding routine for short strings /*{{{*/ @@ -369,7 +459,7 @@ string URItoFileName(string URI) from wget and then patched and bug fixed. This spec can be found in rfc2045 */ -string Base64Encode(string S) +string Base64Encode(const string &S) { // Conversion table. static char tbl[64] = {'A','B','C','D','E','F','G','H', @@ -420,9 +510,9 @@ string Base64Encode(string S) return Final; } /*}}}*/ -// stringcmp - Arbitary string compare /*{{{*/ +// stringcmp - Arbitrary string compare /*{{{*/ // --------------------------------------------------------------------- -/* This safely compares two non-null terminated strings of arbitary +/* This safely compares two non-null terminated strings of arbitrary length */ int stringcmp(const char *A,const char *AEnd,const char *B,const char *BEnd) { @@ -478,13 +568,13 @@ int stringcmp(string::const_iterator A,string::const_iterator AEnd, } #endif /*}}}*/ -// stringcasecmp - Arbitary case insensitive string compare /*{{{*/ +// stringcasecmp - Arbitrary case insensitive string compare /*{{{*/ // --------------------------------------------------------------------- /* */ int stringcasecmp(const char *A,const char *AEnd,const char *B,const char *BEnd) { for (; A != AEnd && B != BEnd; A++, B++) - if (toupper(*A) != toupper(*B)) + if (tolower_ascii(*A) != tolower_ascii(*B)) break; if (A == AEnd && B == BEnd) @@ -493,7 +583,7 @@ int stringcasecmp(const char *A,const char *AEnd,const char *B,const char *BEnd) return 1; if (B == BEnd) return -1; - if (toupper(*A) < toupper(*B)) + if (tolower_ascii(*A) < tolower_ascii(*B)) return -1; return 1; } @@ -502,7 +592,7 @@ int stringcasecmp(string::const_iterator A,string::const_iterator AEnd, const char *B,const char *BEnd) { for (; A != AEnd && B != BEnd; A++, B++) - if (toupper(*A) != toupper(*B)) + if (tolower_ascii(*A) != tolower_ascii(*B)) break; if (A == AEnd && B == BEnd) @@ -511,7 +601,7 @@ int stringcasecmp(string::const_iterator A,string::const_iterator AEnd, return 1; if (B == BEnd) return -1; - if (toupper(*A) < toupper(*B)) + if (tolower_ascii(*A) < tolower_ascii(*B)) return -1; return 1; } @@ -519,7 +609,7 @@ int stringcasecmp(string::const_iterator A,string::const_iterator AEnd, string::const_iterator B,string::const_iterator BEnd) { for (; A != AEnd && B != BEnd; A++, B++) - if (toupper(*A) != toupper(*B)) + if (tolower_ascii(*A) != tolower_ascii(*B)) break; if (A == AEnd && B == BEnd) @@ -528,7 +618,7 @@ int stringcasecmp(string::const_iterator A,string::const_iterator AEnd, return 1; if (B == BEnd) return -1; - if (toupper(*A) < toupper(*B)) + if (tolower_ascii(*A) < tolower_ascii(*B)) return -1; return 1; } @@ -538,26 +628,26 @@ int stringcasecmp(string::const_iterator A,string::const_iterator AEnd, // --------------------------------------------------------------------- /* The format is like those used in package files and the method communication system */ -string LookupTag(string Message,const char *Tag,const char *Default) +string LookupTag(const string &Message,const char *Tag,const char *Default) { // Look for a matching tag. int Length = strlen(Tag); - for (string::iterator I = Message.begin(); I + Length < Message.end(); I++) + for (string::const_iterator I = Message.begin(); I + Length < Message.end(); ++I) { // Found the tag if (I[Length] == ':' && stringcasecmp(I,I+Length,Tag) == 0) { // Find the end of line and strip the leading/trailing spaces - string::iterator J; + string::const_iterator J; I += Length + 1; - for (; isspace(*I) != 0 && I < Message.end(); I++); - for (J = I; *J != '\n' && J < Message.end(); J++); - for (; J > I && isspace(J[-1]) != 0; J--); + for (; isspace(*I) != 0 && I < Message.end(); ++I); + for (J = I; *J != '\n' && J < Message.end(); ++J); + for (; J > I && isspace(J[-1]) != 0; --J); return string(I,J); } - for (; *I != '\n' && I < Message.end(); I++); + for (; *I != '\n' && I < Message.end(); ++I); } // Failed to find a match @@ -570,7 +660,7 @@ string LookupTag(string Message,const char *Tag,const char *Default) // --------------------------------------------------------------------- /* This inspects the string to see if it is true or if it is false and then returns the result. Several varients on true/false are checked. */ -int StringToBool(string Text,int Default) +int StringToBool(const string &Text,int Default) { char *End; int Res = strtol(Text.c_str(),&End,0); @@ -602,14 +692,16 @@ int StringToBool(string Text,int Default) year 2000 complient and timezone neutral */ string TimeRFC1123(time_t Date) { - struct tm Conv = *gmtime(&Date); - char Buf[300]; + struct tm Conv; + if (gmtime_r(&Date, &Conv) == NULL) + return ""; + char Buf[300]; const char *Day[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"}; const char *Month[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul", "Aug","Sep","Oct","Nov","Dec"}; - sprintf(Buf,"%s, %02i %s %i %02i:%02i:%02i GMT",Day[Conv.tm_wday], + snprintf(Buf, sizeof(Buf), "%s, %02i %s %i %02i:%02i:%02i GMT",Day[Conv.tm_wday], Conv.tm_mday,Month[Conv.tm_mon],Conv.tm_year+1900,Conv.tm_hour, Conv.tm_min,Conv.tm_sec); return Buf; @@ -619,11 +711,24 @@ string TimeRFC1123(time_t Date) // --------------------------------------------------------------------- /* This pulls full messages from the input FD into the message buffer. It assumes that messages will not pause during transit so no - fancy buffering is used. */ + fancy buffering is used. + + In particular: this reads blocks from the input until it believes + that it's run out of input text. Each block is terminated by a + double newline ('\n' followed by '\n'). As noted below, there is a + bug in this code: it assumes that all the blocks have been read if + it doesn't see additional text in the buffer after the last one is + parsed, which will cause it to lose blocks if the last block + coincides with the end of the buffer. + */ bool ReadMessages(int Fd, vector &List) { - char Buffer[4000]; + char Buffer[64000]; char *End = Buffer; + // Represents any left-over from the previous iteration of the + // parse loop. (i.e., if a message is split across the end + // of the buffer, it goes here) + string PartialMessage; while (1) { @@ -650,7 +755,8 @@ bool ReadMessages(int Fd, vector &List) continue; // Pull the message out - string Message(Buffer,0,I-Buffer); + string Message(Buffer,I-Buffer); + PartialMessage += Message; // Fix up the buffer for (; I < End && *I == '\n'; I++); @@ -658,10 +764,32 @@ bool ReadMessages(int Fd, vector &List) memmove(Buffer,I,End-Buffer); I = Buffer; - List.push_back(Message); + List.push_back(PartialMessage); + PartialMessage.clear(); } - if (End == Buffer) - return true; + if (End != Buffer) + { + // If there's text left in the buffer, store it + // in PartialMessage and throw the rest of the buffer + // away. This allows us to handle messages that + // are longer than the static buffer size. + PartialMessage += string(Buffer, End); + End = Buffer; + } + else + { + // BUG ALERT: if a message block happens to end at a + // multiple of 64000 characters, this will cause it to + // terminate early, leading to a badly formed block and + // probably crashing the method. However, this is the only + // way we have to find the end of the message block. I have + // an idea of how to fix this, but it will require changes + // to the protocol (essentially to mark the beginning and + // end of the block). + // + // -- dburrows 2008-04-02 + return true; + } if (WaitFd(Fd) == false) return false; @@ -671,28 +799,28 @@ bool ReadMessages(int Fd, vector &List) // MonthConv - Converts a month string into a number /*{{{*/ // --------------------------------------------------------------------- /* This was lifted from the boa webserver which lifted it from 'wn-v1.07' - Made it a bit more robust with a few touppers though. */ + Made it a bit more robust with a few tolower_ascii though. */ static int MonthConv(char *Month) { - switch (toupper(*Month)) + switch (tolower_ascii(*Month)) { - case 'A': - return toupper(Month[1]) == 'P'?3:7; - case 'D': + case 'a': + return tolower_ascii(Month[1]) == 'p'?3:7; + case 'd': return 11; - case 'F': + case 'f': return 1; - case 'J': - if (toupper(Month[1]) == 'A') + case 'j': + if (tolower_ascii(Month[1]) == 'a') return 0; - return toupper(Month[2]) == 'N'?5:6; - case 'M': - return toupper(Month[2]) == 'R'?2:4; - case 'N': + return tolower_ascii(Month[2]) == 'n'?5:6; + case 'm': + return tolower_ascii(Month[2]) == 'r'?2:4; + case 'n': return 10; - case 'O': + case 'o': return 9; - case 'S': + case 's': return 8; // Pretend it is January.. @@ -701,30 +829,70 @@ static int MonthConv(char *Month) } } /*}}}*/ -// timegm - Internal timegm function if gnu is not available /*{{{*/ +// timegm - Internal timegm if the gnu version is not available /*{{{*/ // --------------------------------------------------------------------- -/* Ripped this evil little function from wget - I prefer the use of - GNU timegm if possible as this technique will have interesting problems - with leap seconds, timezones and other. - - Converts struct tm to time_t, assuming the data in tm is UTC rather +/* Converts struct tm to time_t, assuming the data in tm is UTC rather than local timezone (mktime assumes the latter). - - Contributed by Roger Beeman , with the help of - Mark Baushke and the rest of the Gurus at CISCO. */ -#ifndef __USE_MISC // glib sets this + + This function is a nonstandard GNU extension that is also present on + the BSDs and maybe other systems. For others we follow the advice of + the manpage of timegm and use his portable replacement. */ +#ifndef HAVE_TIMEGM static time_t timegm(struct tm *t) { - time_t tl, tb; - - tl = mktime (t); - if (tl == -1) - return -1; - tb = mktime (gmtime (&tl)); - return (tl <= tb ? (tl + (tl - tb)) : (tl - (tb - tl))); + char *tz = getenv("TZ"); + setenv("TZ", "", 1); + tzset(); + time_t ret = mktime(t); + if (tz) + setenv("TZ", tz, 1); + else + unsetenv("TZ"); + tzset(); + return ret; } #endif /*}}}*/ +// FullDateToTime - Converts a HTTP1.1 full date strings into a time_t /*{{{*/ +// --------------------------------------------------------------------- +/* tries to parses a full date as specified in RFC2616 Section 3.3.1 + with one exception: All timezones (%Z) are accepted but the protocol + says that it MUST be GMT, but this one is equal to UTC which we will + encounter from time to time (e.g. in Release files) so we accept all + here and just assume it is GMT (or UTC) later on */ +bool RFC1123StrToTime(const char* const str,time_t &time) +{ + struct tm Tm; + setlocale (LC_ALL,"C"); + bool const invalid = + // Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 + (strptime(str, "%a, %d %b %Y %H:%M:%S %Z", &Tm) == NULL && + // Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036 + strptime(str, "%A, %d-%b-%y %H:%M:%S %Z", &Tm) == NULL && + // Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format + strptime(str, "%a %b %d %H:%M:%S %Y", &Tm) == NULL); + setlocale (LC_ALL,""); + if (invalid == true) + return false; + + time = timegm(&Tm); + return true; +} + /*}}}*/ +// FTPMDTMStrToTime - Converts a ftp modification date into a time_t /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool FTPMDTMStrToTime(const char* const str,time_t &time) +{ + struct tm Tm; + // MDTM includes no whitespaces but recommend and ignored by strptime + if (strptime(str, "%Y %m %d %H %M %S", &Tm) == NULL) + return false; + + time = timegm(&Tm); + return true; +} + /*}}}*/ // StrToTime - Converts a string into a time_t /*{{{*/ // --------------------------------------------------------------------- /* This handles all 3 populare time formats including RFC 1123, RFC 1036 @@ -732,15 +900,14 @@ static time_t timegm(struct tm *t) 'timegm' to convert a struct tm in UTC to a time_t. For some bizzar reason the C library does not provide any such function :< This also handles the weird, but unambiguous FTP time format*/ -bool StrToTime(string Val,time_t &Result) +bool StrToTime(const string &Val,time_t &Result) { struct tm Tm; char Month[10]; - const char *I = Val.c_str(); - + // Skip the day of the week - for (;*I != 0 && *I != ' '; I++); - + const char *I = strchr(Val.c_str(), ' '); + // Handle RFC 1123 time Month[0] = 0; if (sscanf(I," %d %3s %d %d:%d:%d GMT",&Tm.tm_mday,Month,&Tm.tm_year, @@ -802,6 +969,23 @@ bool StrToNum(const char *Str,unsigned long &Res,unsigned Len,unsigned Base) return true; } /*}}}*/ +// Base256ToNum - Convert a fixed length binary to a number /*{{{*/ +// --------------------------------------------------------------------- +/* This is used in decoding the 256bit encoded fixed length fields in + tar files */ +bool Base256ToNum(const char *Str,unsigned long &Res,unsigned int Len) +{ + if ((Str[0] & 0x80) == 0) + return false; + else + { + Res = Str[0] & 0x7F; + for(unsigned int i = 1; i < Len; ++i) + Res = (Res<<8) + Str[i]; + return true; + } +} + /*}}}*/ // HexDigit - Convert a hex character into an integer /*{{{*/ // --------------------------------------------------------------------- /* Helper for Hex2Num */ @@ -819,7 +1003,7 @@ static int HexDigit(int c) // Hex2Num - Convert a long hex number into a buffer /*{{{*/ // --------------------------------------------------------------------- /* The length of the buffer must be exactly 1/2 the length of the string. */ -bool Hex2Num(string Str,unsigned char *Num,unsigned int Length) +bool Hex2Num(const string &Str,unsigned char *Num,unsigned int Length) { if (Str.length() != Length*2) return false; @@ -878,6 +1062,24 @@ bool TokSplitString(char Tok,char *Input,char **List, return true; } /*}}}*/ +// VectorizeString - Split a string up into a vector of strings /*{{{*/ +// --------------------------------------------------------------------- +/* This can be used to split a given string up into a vector, so the + propose is the same as in the method above and this one is a bit slower + also, but the advantage is that we have an iteratable vector */ +vector VectorizeString(string const &haystack, char const &split) +{ + string::const_iterator start = haystack.begin(); + string::const_iterator end = start; + vector exploded; + do { + for (; end != haystack.end() && *end != split; ++end); + exploded.push_back(string(start, end)); + start = end + 1; + } while (end != haystack.end() && (++end) != haystack.end()); + return exploded; +} + /*}}}*/ // RegexChoice - Simple regex list/list matcher /*{{{*/ // --------------------------------------------------------------------- /* */ @@ -940,34 +1142,94 @@ unsigned long RegexChoice(RxChoiceList *Rxs,const char **ListBegin, /*}}}*/ // ioprintf - C format string outputter to C++ iostreams /*{{{*/ // --------------------------------------------------------------------- -/* This is used to make the internationalization strinc easier to translate - and to allow reordering of parameters */ +/* This is used to make the internationalization strings easier to translate + and to allow reordering of parameters */ void ioprintf(ostream &out,const char *format,...) { va_list args; va_start(args,format); // sprintf the description - char S[400]; + char S[4096]; vsnprintf(S,sizeof(S),format,args); out << S; } /*}}}*/ +// strprintf - C format string outputter to C++ strings /*{{{*/ +// --------------------------------------------------------------------- +/* This is used to make the internationalization strings easier to translate + and to allow reordering of parameters */ +void strprintf(string &out,const char *format,...) +{ + va_list args; + va_start(args,format); + + // sprintf the description + char S[4096]; + vsnprintf(S,sizeof(S),format,args); + out = string(S); +} + /*}}}*/ +// safe_snprintf - Safer snprintf /*{{{*/ +// --------------------------------------------------------------------- +/* This is a snprintf that will never (ever) go past 'End' and returns a + pointer to the end of the new string. The returned string is always null + terminated unless Buffer == end. This is a better alterantive to using + consecutive snprintfs. */ +char *safe_snprintf(char *Buffer,char *End,const char *Format,...) +{ + va_list args; + int Did; + + va_start(args,Format); + + if (End <= Buffer) + return End; + + Did = vsnprintf(Buffer,End - Buffer,Format,args); + if (Did < 0 || Buffer + Did > End) + return End; + return Buffer + Did; +} + /*}}}*/ +// StripEpoch - Remove the version "epoch" from a version string /*{{{*/ +// --------------------------------------------------------------------- +string StripEpoch(const string &VerStr) +{ + size_t i = VerStr.find(":"); + if (i == string::npos) + return VerStr; + return VerStr.substr(i+1); +} + +// tolower_ascii - tolower() function that ignores the locale /*{{{*/ +// --------------------------------------------------------------------- +/* This little function is the most called method we have and tries + therefore to do the absolut minimum - and is noteable faster than + standard tolower/toupper and as a bonus avoids problems with different + locales - we only operate on ascii chars anyway. */ +int tolower_ascii(int const c) +{ + if (c >= 'A' && c <= 'Z') + return c + 32; + return c; +} + /*}}}*/ // CheckDomainList - See if Host is in a , seperate list /*{{{*/ // --------------------------------------------------------------------- /* The domain list is a comma seperate list of domains that are suffix matched against the argument */ -bool CheckDomainList(string Host,string List) +bool CheckDomainList(const string &Host,const string &List) { string::const_iterator Start = List.begin(); - for (string::const_iterator Cur = List.begin(); Cur <= List.end(); Cur++) + for (string::const_iterator Cur = List.begin(); Cur <= List.end(); ++Cur) { if (Cur < List.end() && *Cur != ',') continue; // Match the end of the string.. - if ((Host.size() >= (unsigned)(Cur - List.begin())) && + if ((Host.size() >= (unsigned)(Cur - Start)) && Cur - Start != 0 && stringcasecmp(Host.end() - (Cur - Start),Host.end(),Start,Cur) == 0) return true; @@ -977,16 +1239,77 @@ bool CheckDomainList(string Host,string List) return false; } /*}}}*/ +// DeEscapeString - unescape (\0XX and \xXX) from a string /*{{{*/ +// --------------------------------------------------------------------- +/* */ +string DeEscapeString(const string &input) +{ + char tmp[3]; + string::const_iterator it, escape_start; + string output, octal, hex; + for (it = input.begin(); it != input.end(); ++it) + { + // just copy non-escape chars + if (*it != '\\') + { + output += *it; + continue; + } + + // deal with double escape + if (*it == '\\' && + (it + 1 < input.end()) && it[1] == '\\') + { + // copy + output += *it; + // advance iterator one step further + ++it; + continue; + } + + // ensure we have a char to read + if (it + 1 == input.end()) + continue; + // read it + ++it; + switch (*it) + { + case '0': + if (it + 2 <= input.end()) { + tmp[0] = it[1]; + tmp[1] = it[2]; + tmp[2] = 0; + output += (char)strtol(tmp, 0, 8); + it += 2; + } + break; + case 'x': + if (it + 2 <= input.end()) { + tmp[0] = it[1]; + tmp[1] = it[2]; + tmp[2] = 0; + output += (char)strtol(tmp, 0, 16); + it += 2; + } + break; + default: + // FIXME: raise exception here? + break; + } + } + return output; +} + /*}}}*/ // URI::CopyFrom - Copy from an object /*{{{*/ // --------------------------------------------------------------------- /* This parses the URI into all of its components */ -void URI::CopyFrom(string U) +void URI::CopyFrom(const string &U) { string::const_iterator I = U.begin(); // Locate the first colon, this separates the scheme - for (; I < U.end() && *I != ':' ; I++); + for (; I < U.end() && *I != ':' ; ++I); string::const_iterator FirstColon = I; /* Determine if this is a host type URI with a leading double // @@ -998,7 +1321,7 @@ void URI::CopyFrom(string U) /* Find the / indicating the end of the hostname, ignoring /'s in the square brackets */ bool InBracket = false; - for (; SingleSlash < U.end() && (*SingleSlash != '/' || InBracket == true); SingleSlash++) + for (; SingleSlash < U.end() && (*SingleSlash != '/' || InBracket == true); ++SingleSlash) { if (*SingleSlash == '[') InBracket = true; @@ -1010,14 +1333,14 @@ void URI::CopyFrom(string U) SingleSlash = U.end(); // We can now write the access and path specifiers - Access = string(U,0,FirstColon - U.begin()); + Access.assign(U.begin(),FirstColon); if (SingleSlash != U.end()) - Path = string(U,SingleSlash - U.begin()); + Path.assign(SingleSlash,U.end()); if (Path.empty() == true) Path = "/"; // Now we attempt to locate a user:pass@host fragment - if (FirstColon[1] == '/' && FirstColon[2] == '/') + if (FirstColon + 2 <= U.end() && FirstColon[1] == '/' && FirstColon[2] == '/') FirstColon += 3; else FirstColon += 1; @@ -1031,25 +1354,26 @@ void URI::CopyFrom(string U) I = FirstColon + 1; if (I > SingleSlash) I = SingleSlash; - for (; I < SingleSlash && *I != ':'; I++); + for (; I < SingleSlash && *I != ':'; ++I); string::const_iterator SecondColon = I; // Search for the @ after the colon - for (; I < SingleSlash && *I != '@'; I++); + for (; I < SingleSlash && *I != '@'; ++I); string::const_iterator At = I; // Now write the host and user/pass if (At == SingleSlash) { if (FirstColon < SingleSlash) - Host = string(U,FirstColon - U.begin(),SingleSlash - FirstColon); + Host.assign(FirstColon,SingleSlash); } else { - Host = string(U,At - U.begin() + 1,SingleSlash - At - 1); - User = string(U,FirstColon - U.begin(),SecondColon - FirstColon); + Host.assign(At+1,SingleSlash); + // username and password must be encoded (RFC 3986) + User.assign(DeQuoteString(FirstColon,SecondColon)); if (SecondColon < At) - Password = string(U,SecondColon - U.begin() + 1,At - SecondColon - 1); + Password.assign(DeQuoteString(SecondColon+1,At)); } // Now we parse the RFC 2732 [] hostnames. @@ -1077,7 +1401,7 @@ void URI::CopyFrom(string U) // Tsk, weird. if (InBracket == true) { - Host = string(); + Host.clear(); return; } @@ -1088,7 +1412,7 @@ void URI::CopyFrom(string U) return; Port = atoi(string(Host,Pos+1).c_str()); - Host = string(Host,0,Pos); + Host.assign(Host,0,Pos); } /*}}}*/ // URI::operator string - Convert the URI to a string /*{{{*/ @@ -1143,12 +1467,24 @@ URI::operator string() // URI::SiteOnly - Return the schema and site for the URI /*{{{*/ // --------------------------------------------------------------------- /* */ -string URI::SiteOnly(string URI) +string URI::SiteOnly(const string &URI) +{ + ::URI U(URI); + U.User.clear(); + U.Password.clear(); + U.Path.clear(); + U.Port = 0; + return U; +} + /*}}}*/ +// URI::NoUserPassword - Return the schema, site and path for the URI /*{{{*/ +// --------------------------------------------------------------------- +/* */ +string URI::NoUserPassword(const string &URI) { ::URI U(URI); - U.User = string(); - U.Password = string(); - U.Path = string(); + U.User.clear(); + U.Password.clear(); U.Port = 0; return U; }