X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/52d9d000a74ea71edff93f00ae38aceb9ada53d4..d52f514fd380cdeef7ff68a54f81b82932d113cf:/apt-pkg/contrib/strutl.cc diff --git a/apt-pkg/contrib/strutl.cc b/apt-pkg/contrib/strutl.cc index 1b9922a31..ca096d736 100644 --- a/apt-pkg/contrib/strutl.cc +++ b/apt-pkg/contrib/strutl.cc @@ -15,14 +15,15 @@ ##################################################################### */ /*}}}*/ // Includes /*{{{*/ +#include + #include #include #include -#include - #include #include +#include #include #include #include @@ -31,7 +32,7 @@ #include #include -#include "config.h" +#include using namespace std; /*}}}*/ @@ -179,14 +180,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; } } @@ -198,7 +199,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]; @@ -270,10 +272,11 @@ bool ParseCWord(const char *&String,string &Res) string QuoteString(const string &Str, const char *Bad) { string Res; - for (string::const_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); @@ -289,11 +292,17 @@ string QuoteString(const string &Str, const char *Bad) // --------------------------------------------------------------------- /* This undoes QuoteString */ 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]; @@ -332,13 +341,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; @@ -566,7 +575,7 @@ int stringcmp(string::const_iterator A,string::const_iterator AEnd, 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) @@ -575,7 +584,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; } @@ -584,7 +593,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) @@ -593,7 +602,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; } @@ -601,7 +610,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) @@ -610,7 +619,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; } @@ -624,7 +633,7 @@ string LookupTag(const string &Message,const char *Tag,const char *Default) { // Look for a matching tag. int Length = strlen(Tag); - for (string::const_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) @@ -632,14 +641,14 @@ string LookupTag(const string &Message,const char *Tag,const char *Default) // Find the end of line and strip the leading/trailing spaces 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 @@ -684,14 +693,16 @@ int StringToBool(const 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; @@ -789,28 +800,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.. @@ -819,34 +830,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. */ - -/* Turned it into an autoconf check, because GNU is not the only thing which - can provide timegm. -- 2002-09-22, Joel Baker */ -#ifndef HAVE_TIMEGM // Now with autoconf! + 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 @@ -858,24 +905,23 @@ 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, + if (sscanf(I," %2d %3s %4d %2d:%2d:%2d GMT",&Tm.tm_mday,Month,&Tm.tm_year, &Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec) != 6) { // Handle RFC 1036 time - if (sscanf(I," %d-%3s-%d %d:%d:%d GMT",&Tm.tm_mday,Month, + if (sscanf(I," %2d-%3s-%3d %2d:%2d:%2d GMT",&Tm.tm_mday,Month, &Tm.tm_year,&Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec) == 6) Tm.tm_year += 1900; else { // asctime format - if (sscanf(I," %3s %d %d:%d:%d %d",Month,&Tm.tm_mday, + if (sscanf(I," %3s %2d %2d:%2d:%2d %4d",Month,&Tm.tm_mday, &Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec,&Tm.tm_year) != 6) { // 'ftp' time @@ -924,6 +970,51 @@ bool StrToNum(const char *Str,unsigned long &Res,unsigned Len,unsigned Base) return true; } /*}}}*/ +// StrToNum - Convert a fixed length string to a number /*{{{*/ +// --------------------------------------------------------------------- +/* This is used in decoding the crazy fixed length string headers in + tar and ar files. */ +bool StrToNum(const char *Str,unsigned long long &Res,unsigned Len,unsigned Base) +{ + char S[30]; + if (Len >= sizeof(S)) + return false; + memcpy(S,Str,Len); + S[Len] = 0; + + // All spaces is a zero + Res = 0; + unsigned I; + for (I = 0; S[I] == ' '; I++); + if (S[I] == 0) + return true; + + char *End; + Res = strtoull(S,&End,Base); + if (End == S) + return false; + + 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 */ @@ -1000,12 +1091,12 @@ bool TokSplitString(char Tok,char *Input,char **List, return true; } /*}}}*/ -// ExplodeString - Split a string up into a vector /*{{{*/ +// 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 an iteratable vector */ -vector ExplodeString(string const &haystack, char const &split) + 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; @@ -1078,34 +1169,50 @@ unsigned long RegexChoice(RxChoiceList *Rxs,const char **ListBegin, return Hits; } /*}}}*/ -// ioprintf - C format string outputter to C++ iostreams /*{{{*/ +// {str,io}printf - C format string outputter to C++ strings/iostreams /*{{{*/ // --------------------------------------------------------------------- /* This is used to make the internationalization strings easier to translate and to allow reordering of parameters */ -void ioprintf(ostream &out,const char *format,...) +static bool iovprintf(ostream &out, const char *format, + va_list &args, ssize_t &size) { + char *S = (char*)malloc(size); + ssize_t const n = vsnprintf(S, size, format, args); + if (n > -1 && n < size) { + out << S; + free(S); + return true; + } else { + if (n > -1) + size = n + 1; + else + size *= 2; + } + free(S); + return false; +} +void ioprintf(ostream &out,const char *format,...) { va_list args; - va_start(args,format); - - // sprintf the description - char S[4096]; - vsnprintf(S,sizeof(S),format,args); - out << S; + ssize_t size = 400; + while (true) { + va_start(args,format); + if (iovprintf(out, format, args, size) == true) + return; + va_end(args); + } } - /*}}}*/ -// 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,...) +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); + ssize_t size = 400; + std::ostringstream outstr; + while (true) { + va_start(args,format); + if (iovprintf(outstr, format, args, size) == true) + break; + va_end(args); + } + out = outstr.str(); } /*}}}*/ // safe_snprintf - Safer snprintf /*{{{*/ @@ -1117,7 +1224,7 @@ void strprintf(string &out,const char *format,...) char *safe_snprintf(char *Buffer,char *End,const char *Format,...) { va_list args; - unsigned long Did; + int Did; va_start(args,Format); @@ -1130,13 +1237,25 @@ char *safe_snprintf(char *Buffer,char *End,const char *Format,...) 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 /*{{{*/ // --------------------------------------------------------------------- -/* */ -int tolower_ascii(int c) +/* 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' and c <= 'Z') + if (c >= 'A' && c <= 'Z') return c + 32; return c; } @@ -1149,7 +1268,7 @@ int tolower_ascii(int c) 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; @@ -1165,7 +1284,68 @@ bool CheckDomainList(const string &Host,const 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 */ @@ -1174,7 +1354,7 @@ 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 // @@ -1186,7 +1366,7 @@ void URI::CopyFrom(const 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; @@ -1219,11 +1399,11 @@ void URI::CopyFrom(const 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 @@ -1235,9 +1415,10 @@ void URI::CopyFrom(const string &U) else { Host.assign(At+1,SingleSlash); - User.assign(FirstColon,SecondColon); + // username and password must be encoded (RFC 3986) + User.assign(DeQuoteString(FirstColon,SecondColon)); if (SecondColon < At) - Password.assign(SecondColon+1,At); + Password.assign(DeQuoteString(SecondColon+1,At)); } // Now we parse the RFC 2732 [] hostnames.