##################################################################### */
/*}}}*/
// Includes /*{{{*/
+#include <config.h>
+
#include <apt-pkg/strutl.h>
#include <apt-pkg/fileutl.h>
#include <apt-pkg/error.h>
-#include <apti18n.h>
-
+#include <stddef.h>
+#include <stdlib.h>
+#include <time.h>
+#include <string>
+#include <vector>
#include <ctype.h>
#include <string.h>
+#include <sstream>
#include <stdio.h>
#include <algorithm>
#include <unistd.h>
#include <stdarg.h>
#include <iconv.h>
-#include "config.h"
-
-using namespace std;
+#include <apti18n.h>
/*}}}*/
+using namespace std;
+// Strip - Remove white space from the front and back of a string /*{{{*/
+// ---------------------------------------------------------------------
+namespace APT {
+ namespace String {
+std::string Strip(const std::string &s)
+{
+ size_t start = s.find_first_not_of(" \t\n");
+ // only whitespace
+ if (start == string::npos)
+ return "";
+ size_t end = s.find_last_not_of(" \t\n");
+ return s.substr(start, end-start+1);
+}
+
+bool Endswith(const std::string &s, const std::string &end)
+{
+ if (end.size() > s.size())
+ return false;
+ return (s.substr(s.size() - end.size(), s.size()) == end);
+}
+
+}
+}
+ /*}}}*/
// UTF8ToCodeset - Convert some UTF-8 string for some codeset /*{{{*/
// ---------------------------------------------------------------------
/* This is handy to use before display some information for enduser */
if (*String == 0)
return String;
-
+ return _strrstrip(String);
+}
+ /*}}}*/
+// strrstrip - Remove white space from the back of a string /*{{{*/
+// ---------------------------------------------------------------------
+char *_strrstrip(char *String)
+{
char *End = String + strlen(String) - 1;
for (;End != String - 1 && (*End == ' ' || *End == '\t' || *End == '\n' ||
*End == '\r'); End--);
End++;
*End = 0;
return String;
-};
+}
/*}}}*/
// strtabexpand - Converts tabs into 8 spaces /*{{{*/
// ---------------------------------------------------------------------
{
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;
}
}
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 == 0x25 || // percent '%' char
string::const_iterator const &end)
{
string Res;
- for (string::const_iterator I = begin; I != end; I++)
+ for (string::const_iterator I = begin; I != end; ++I)
{
if (*I == '%' && I + 2 < end &&
isxdigit(I[1]) && isxdigit(I[2]))
/*}}}*/
// SubstVar - Substitute a string for another string /*{{{*/
// ---------------------------------------------------------------------
-/* This replaces all occurances of Subst with Contents in Str. */
+/* This replaces all occurrences of Subst with Contents in Str. */
string SubstVar(const string &Str,const string &Subst,const string &Contents)
{
string::size_type Pos = 0;
{
// 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)
// 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
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;
// Look for the end of the message
for (char *I = Buffer; I + 1 < End; I++)
{
- if (I[0] != '\n' || I[1] != '\n')
+ if (I[1] != '\n' ||
+ (I[0] != '\n' && strncmp(I, "\r\n\r\n", 4) != 0))
continue;
// Pull the message out
PartialMessage += Message;
// Fix up the buffer
- for (; I < End && *I == '\n'; I++);
+ for (; I < End && (*I == '\n' || *I == '\r'); ++I);
End -= I-Buffer;
memmove(Buffer,I,End-Buffer);
I = Buffer;
/*}}}*/
// StrToTime - Converts a string into a time_t /*{{{*/
// ---------------------------------------------------------------------
-/* This handles all 3 populare time formats including RFC 1123, RFC 1036
+/* This handles all 3 popular time formats including RFC 1123, RFC 1036
and the C library asctime format. It requires the GNU library function
'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
{
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
Tm.tm_isdst = 0;
if (Month[0] != 0)
Tm.tm_mon = MonthConv(Month);
+ else
+ Tm.tm_mon = 0; // we don't have a month, so pick something
Tm.tm_year -= 1900;
// Convert to local time and then to GMT
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
also, but the advantage is that we have an iteratable vector */
vector<string> VectorizeString(string const &haystack, char const &split)
{
+ vector<string> exploded;
+ if (haystack.empty() == true)
+ return exploded;
string::const_iterator start = haystack.begin();
string::const_iterator end = start;
- vector<string> exploded;
do {
for (; end != haystack.end() && *end != split; ++end);
exploded.push_back(string(start, end));
return exploded;
}
/*}}}*/
+// StringSplit - split a string into a string vector by token /*{{{*/
+// ---------------------------------------------------------------------
+/* See header for details.
+ */
+vector<string> StringSplit(std::string const &s, std::string const &sep,
+ unsigned int maxsplit)
+{
+ vector<string> split;
+ size_t start, pos;
+
+ // no seperator given, this is bogus
+ if(sep.size() == 0)
+ return split;
+
+ start = pos = 0;
+ while (pos != string::npos)
+ {
+ pos = s.find(sep, start);
+ split.push_back(s.substr(start, pos-start));
+
+ // if maxsplit is reached, the remaining string is the last item
+ if(split.size() >= maxsplit)
+ {
+ split[split.size()-1] = s.substr(start);
+ break;
+ }
+ start = pos+sep.size();
+ }
+ return split;
+}
+ /*}}}*/
// RegexChoice - Simple regex list/list matcher /*{{{*/
// ---------------------------------------------------------------------
/* */
R->Hit = false;
unsigned long Hits = 0;
- for (; ListBegin != ListEnd; ListBegin++)
+ for (; ListBegin < ListEnd; ++ListBegin)
{
// Check if the name is a regex
const char *I;
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 /*{{{*/
va_list args;
int Did;
- va_start(args,Format);
-
if (End <= Buffer)
return End;
-
+ va_start(args,Format);
Did = vsnprintf(Buffer,End - Buffer,Format,args);
+ va_end(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
+ therefore to do the absolut minimum - and is notable 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)
}
/*}}}*/
-// CheckDomainList - See if Host is in a , seperate list /*{{{*/
+// CheckDomainList - See if Host is in a , separate list /*{{{*/
// ---------------------------------------------------------------------
-/* The domain list is a comma seperate list of domains that are suffix
+/* The domain list is a comma separate list of domains that are suffix
matched against the argument */
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;
return false;
}
/*}}}*/
+// strv_length - Return the length of a NULL-terminated string array /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+size_t strv_length(const char **str_array)
+{
+ size_t i;
+ for (i=0; str_array[i] != NULL; i++)
+ /* nothing */
+ ;
+ return i;
+}
+// DeEscapeString - unescape (\0XX and \xXX) from a string /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+string DeEscapeString(const string &input)
+{
+ char tmp[3];
+ string::const_iterator it;
+ string output;
+ 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 */
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 //
/* 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;
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 (User.empty() == false)
{
- Res += User;
+ // FIXME: Technically userinfo is permitted even less
+ // characters than these, but this is not conveniently
+ // expressed with a blacklist.
+ Res += QuoteString(User, ":/?#[]@");
if (Password.empty() == false)
- Res += ":" + Password;
+ Res += ":" + QuoteString(Password, ":/?#[]@");
Res += "@";
}
U.User.clear();
U.Password.clear();
U.Path.clear();
- U.Port = 0;
return U;
}
/*}}}*/
::URI U(URI);
U.User.clear();
U.Password.clear();
- U.Port = 0;
return U;
}
/*}}}*/