##################################################################### */
/*}}}*/
// Includes /*{{{*/
+#include <config.h>
+
#include <apt-pkg/strutl.h>
#include <apt-pkg/fileutl.h>
#include <apt-pkg/error.h>
-#include <apti18n.h>
-
+#include <algorithm>
+#include <iomanip>
+#include <locale>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <time.h>
#include <ctype.h>
#include <string.h>
#include <stdio.h>
-#include <algorithm>
#include <unistd.h>
#include <regex.h>
#include <errno.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 &str)
+{
+ // ensure we have at least one character
+ if (str.empty() == true)
+ return str;
+
+ char const * const s = str.c_str();
+ size_t start = 0;
+ for (; isspace(s[start]) != 0; ++start)
+ ; // find the first not-space
+
+ // string contains only whitespaces
+ if (s[start] == '\0')
+ return "";
+
+ size_t end = str.length() - 1;
+ for (; isspace(s[end]) != 0; --end)
+ ; // find the last not-space
+
+ return str.substr(start, end - start + 1);
+}
+
+bool Endswith(const std::string &s, const std::string &end)
+{
+ if (end.size() > s.size())
+ return false;
+ return (s.compare(s.size() - end.size(), end.size(), end) == 0);
+}
+bool Startswith(const std::string &s, const std::string &start)
+{
+ if (start.size() > s.size())
+ return false;
+ return (s.compare(0, start.size(), start) == 0);
+}
+
+}
+}
+ /*}}}*/
// 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++)
+ std::stringstream Res;
+ for (string::const_iterator I = Str.begin(); I != Str.end(); ++I)
{
- if (strchr(Bad,*I) != 0 || isprint(*I) == 0 ||
+ if (strchr(Bad,*I) != 0 || isprint(*I) == 0 ||
*I == 0x25 || // percent '%' char
*I <= 0x20 || *I >= 0x7F) // control chars
{
- char Buf[10];
- sprintf(Buf,"%%%02x",(int)*I);
- Res += Buf;
+ ioprintf(Res, "%%%02hhx", *I);
}
else
- Res += *I;
+ Res << *I;
}
- return Res;
+ return Res.str();
}
/*}}}*/
// DeQuoteString - Convert a string from quoted from /*{{{*/
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]))
YottaBytes (E24) */
string SizeToStr(double Size)
{
- char S[300];
double ASize;
if (Size >= 0)
ASize = Size;
else
ASize = -1*Size;
-
+
/* bytes, KiloBytes, MegaBytes, GigaBytes, TeraBytes, PetaBytes,
ExaBytes, ZettaBytes, YottaBytes */
char Ext[] = {'\0','k','M','G','T','P','E','Z','Y'};
{
if (ASize < 100 && I != 0)
{
- sprintf(S,"%'.1f %c",ASize,Ext[I]);
- break;
+ std::string S;
+ strprintf(S, "%'.1f %c", ASize, Ext[I]);
+ return S;
}
-
+
if (ASize < 10000)
{
- sprintf(S,"%'.0f %c",ASize,Ext[I]);
- break;
+ std::string S;
+ strprintf(S, "%'.0f %c", ASize, Ext[I]);
+ return S;
}
ASize /= 1000.0;
I++;
}
-
- return S;
+ return "";
}
/*}}}*/
// TimeToStr - Convert the time into a string /*{{{*/
/* Converts a number of seconds to a hms format */
string TimeToStr(unsigned long Sec)
{
- char S[300];
-
- while (1)
+ std::string S;
+ if (Sec > 60*60*24)
{
- if (Sec > 60*60*24)
- {
- //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)
- {
- //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)
- {
- //min means minutes, s means seconds
- sprintf(S,_("%limin %lis"),Sec/60,Sec % 60);
- break;
- }
-
- //s means seconds
- sprintf(S,_("%lis"),Sec);
- break;
+ //TRANSLATOR: d means days, h means hours, min means minutes, s means seconds
+ strprintf(S,_("%lid %lih %limin %lis"),Sec/60/60/24,(Sec/60/60) % 24,(Sec/60) % 60,Sec % 60);
+ }
+ else if (Sec > 60*60)
+ {
+ //TRANSLATOR: h means hours, min means minutes, s means seconds
+ strprintf(S,_("%lih %limin %lis"),Sec/60/60,(Sec/60) % 60,Sec % 60);
+ }
+ else if (Sec > 60)
+ {
+ //TRANSLATOR: min means minutes, s means seconds
+ strprintf(S,_("%limin %lis"),Sec/60,Sec % 60);
+ }
+ else
+ {
+ //TRANSLATOR: s means seconds
+ strprintf(S,_("%lis"),Sec);
}
-
return S;
}
/*}}}*/
// 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)
{
+ if (Subst.empty() == true)
+ return Str;
+
string::size_type Pos = 0;
string::size_type OldPos = 0;
string Temp;
-
- while (OldPos < Str.length() &&
+
+ while (OldPos < Str.length() &&
(Pos = Str.find(Subst,OldPos)) != string::npos)
{
- Temp += string(Str,OldPos,Pos) + Contents;
- OldPos = Pos + Subst.length();
+ if (OldPos != Pos)
+ Temp.append(Str, OldPos, Pos - OldPos);
+ if (Contents.empty() == false)
+ Temp.append(Contents);
+ OldPos = Pos + Subst.length();
}
-
+
if (OldPos == 0)
return Str;
-
- return Temp + string(Str,OldPos);
-}
+ if (OldPos >= Str.length())
+ return Temp;
+
+ Temp.append(Str, OldPos, string::npos);
+ return Temp;
+}
string SubstVar(string Str,const struct SubstVar *Vars)
{
for (; Vars->Subst != 0; Vars++)
{
// 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_ascii(*I) != 0 && I < Message.end(); ++I);
+ for (J = I; *J != '\n' && J < Message.end(); ++J);
+ for (; J > I && isspace_ascii(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
then returns the result. Several varients on true/false are checked. */
int StringToBool(const string &Text,int Default)
{
- char *End;
- int Res = strtol(Text.c_str(),&End,0);
- if (End != Text.c_str() && Res >= 0 && Res <= 1)
+ char *ParseEnd;
+ int Res = strtol(Text.c_str(),&ParseEnd,0);
+ // ensure that the entire string was converted by strtol to avoid
+ // failures on "apt-cache show -a 0ad" where the "0" is converted
+ const char *TextEnd = Text.c_str()+Text.size();
+ if (ParseEnd == TextEnd && Res >= 0 && Res <= 1)
return Res;
// Check for positives
year 2000 complient and timezone neutral */
string TimeRFC1123(time_t Date)
{
- struct tm Conv = *gmtime(&Date);
- 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"};
+ return TimeRFC1123(Date, false);
+}
+string TimeRFC1123(time_t Date, bool const NumericTimezone)
+{
+ struct tm Conv;
+ if (gmtime_r(&Date, &Conv) == NULL)
+ return "";
- sprintf(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;
+ auto const posix = std::locale("C.UTF-8");
+ std::ostringstream datestr;
+ datestr.imbue(posix);
+ APT::StringView const fmt("%a, %d %b %Y %H:%M:%S");
+ std::use_facet<std::time_put<char>>(posix).put(
+ std::ostreambuf_iterator<char>(datestr),
+ datestr, ' ', &Conv, fmt.data(), fmt.data() + fmt.size());
+ if (NumericTimezone)
+ datestr << " +0000";
+ else
+ datestr << " GMT";
+ return datestr.str();
}
/*}}}*/
// ReadMessages - Read messages from the FD /*{{{*/
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.
+ double newline ('\n' followed by '\n').
*/
bool ReadMessages(int Fd, vector<string> &List)
{
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)
- {
- int Res = read(Fd,End,sizeof(Buffer) - (End-Buffer));
+
+ do {
+ int const Res = read(Fd, Buffer, sizeof(Buffer));
if (Res < 0 && errno == EINTR)
continue;
-
- // Process is dead, this is kind of bad..
+
+ // process we read from has died
if (Res == 0)
return false;
-
+
// No data
+#if EAGAIN != EWOULDBLOCK
+ if (Res < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
+#else
if (Res < 0 && errno == EAGAIN)
+#endif
return true;
if (Res < 0)
return false;
-
- End += Res;
-
- // Look for the end of the message
- for (char *I = Buffer; I + 1 < End; I++)
+
+ // extract the message(s) from the buffer
+ char const *Start = Buffer;
+ char const * const End = Buffer + Res;
+
+ char const * NL = (char const *) memchr(Start, '\n', End - Start);
+ if (NL == NULL)
{
- if (I[0] != '\n' || I[1] != '\n')
- continue;
-
- // Pull the message out
- string Message(Buffer,I-Buffer);
- PartialMessage += Message;
-
- // Fix up the buffer
- for (; I < End && *I == '\n'; I++);
- End -= I-Buffer;
- memmove(Buffer,I,End-Buffer);
- I = Buffer;
-
- List.push_back(PartialMessage);
- PartialMessage.clear();
+ // end of buffer: store what we have so far and read new data in
+ PartialMessage.append(Start, End - Start);
+ Start = End;
}
- 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;
- }
+ ++NL;
+
+ if (PartialMessage.empty() == false && Start < End)
+ {
+ // if we start with a new line, see if the partial message we have ended with one
+ // so that we properly detect records ending between two read() runs
+ // cases are: \n|\n , \r\n|\r\n and \r\n\r|\n
+ // the case \r|\n\r\n is handled by the usual double-newline handling
+ if ((NL - Start) == 1 || ((NL - Start) == 2 && *Start == '\r'))
+ {
+ if (APT::String::Endswith(PartialMessage, "\n") || APT::String::Endswith(PartialMessage, "\r\n\r"))
+ {
+ PartialMessage.erase(PartialMessage.find_last_not_of("\r\n") + 1);
+ List.push_back(PartialMessage);
+ PartialMessage.clear();
+ while (NL < End && (*NL == '\n' || *NL == '\r')) ++NL;
+ Start = NL;
+ }
+ }
+ }
+
+ while (Start < End) {
+ char const * NL2 = (char const *) memchr(NL, '\n', End - NL);
+ if (NL2 == NULL)
+ {
+ // end of buffer: store what we have so far and read new data in
+ PartialMessage.append(Start, End - Start);
+ break;
+ }
+ ++NL2;
+
+ // did we find a double newline?
+ if ((NL2 - NL) == 1 || ((NL2 - NL) == 2 && *NL == '\r'))
+ {
+ PartialMessage.append(Start, NL2 - Start);
+ PartialMessage.erase(PartialMessage.find_last_not_of("\r\n") + 1);
+ List.push_back(PartialMessage);
+ PartialMessage.clear();
+ while (NL2 < End && (*NL2 == '\n' || *NL2 == '\r')) ++NL2;
+ Start = NL2;
+ }
+ NL = NL2;
+ }
+
+ // we have read at least one complete message and nothing left
+ if (PartialMessage.empty() == true)
+ return true;
if (WaitFd(Fd) == false)
return false;
- }
+ } while (true);
}
/*}}}*/
// 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 tolower_ascii though. */
-static int MonthConv(char *Month)
+static int MonthConv(char const * const Month)
{
switch (tolower_ascii(*Month))
{
}
#endif
/*}}}*/
-// FullDateToTime - Converts a HTTP1.1 full date strings into a time_t /*{{{*/
+// RFC1123StrToTime - 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 */
+/* tries to parses a full date as specified in RFC7231 §7.1.1.1
+ with one exception: HTTP/1.1 valid dates need to have GMT as timezone.
+ As we encounter dates from UTC or with a numeric timezone in other places,
+ we allow them here to to be able to reuse the method. Either way, a date
+ must be in UTC or parsing will fail. Previous implementations of this
+ method used to ignore the timezone and assume always UTC. */
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)
+ unsigned short day = 0;
+ signed int year = 0; // yes, Y23K problem – we gonna worry then…
+ std::string weekday, month, datespec, timespec, zone;
+ std::istringstream ss(str);
+ auto const &posix = std::locale("C.UTF-8");
+ ss.imbue(posix);
+ ss >> weekday;
+ // we only superficially check weekday, mostly to avoid accepting localized
+ // weekdays here and take only its length to decide which datetime format we
+ // encounter here. The date isn't stored.
+ std::transform(weekday.begin(), weekday.end(), weekday.begin(), ::tolower);
+ std::array<char const * const, 7> c_weekdays = {{ "sun", "mon", "tue", "wed", "thu", "fri", "sat" }};
+ if (std::find(c_weekdays.begin(), c_weekdays.end(), weekday.substr(0,3)) == c_weekdays.end())
+ return false;
+
+ switch (weekday.length())
+ {
+ case 4:
+ // Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
+ if (weekday[3] != ',')
+ return false;
+ ss >> day >> month >> year >> timespec >> zone;
+ break;
+ case 3:
+ // Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
+ ss >> month >> day >> timespec >> year;
+ zone = "UTC";
+ break;
+ case 0:
+ case 1:
+ case 2:
+ return false;
+ default:
+ // Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
+ if (weekday[weekday.length() - 1] != ',')
+ return false;
+ ss >> datespec >> timespec >> zone;
+ auto const expldate = VectorizeString(datespec, '-');
+ if (expldate.size() != 3)
+ return false;
+ try {
+ size_t pos;
+ day = std::stoi(expldate[0], &pos);
+ if (pos != expldate[0].length())
+ return false;
+ year = 1900 + std::stoi(expldate[2], &pos);
+ if (pos != expldate[2].length())
+ return false;
+ strprintf(datespec, "%.4d-%.2d-%.2d", year, MonthConv(expldate[1].c_str()) + 1, day);
+ } catch (...) {
+ return false;
+ }
+ break;
+ }
+
+ if (ss.fail() || ss.bad() || !ss.eof())
return false;
+ if (zone != "GMT" && zone != "UTC" && zone != "Z") // RFC 822
+ {
+ // numeric timezones as a should of RFC 1123 and generally preferred
+ try {
+ size_t pos;
+ auto const z = std::stoi(zone, &pos);
+ if (z != 0 || pos != zone.length())
+ return false;
+ } catch (...) {
+ return false;
+ }
+ }
+
+ if (datespec.empty())
+ {
+ if (month.empty())
+ return false;
+ strprintf(datespec, "%.4d-%.2d-%.2d", year, MonthConv(month.c_str()) + 1, day);
+ }
+
+ std::string const datetime = datespec + ' ' + timespec;
+ struct tm Tm;
+ if (strptime(datetime.c_str(), "%Y-%m-%d %H:%M:%S", &Tm) == nullptr)
+ 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
+/* 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
+ tar files */
+bool Base256ToNum(const char *Str,unsigned long 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;
+ }
+}
+ /*}}}*/
+// 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)
+{
+ unsigned long long Num;
+ bool rc;
+
+ rc = Base256ToNum(Str, Num, Len);
+ Res = Num;
+ if (Res != Num)
+ return false;
+
+ return rc;
+}
+ /*}}}*/
// HexDigit - Convert a hex character into an integer /*{{{*/
// ---------------------------------------------------------------------
/* Helper for Hex2Num */
return c - 'a' + 10;
if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
- return 0;
+ return -1;
}
/*}}}*/
// 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(const string &Str,unsigned char *Num,unsigned int Length)
+{
+ return Hex2Num(APT::StringView(Str), Num, Length);
+}
+
+bool Hex2Num(const APT::StringView Str,unsigned char *Num,unsigned int Length)
{
if (Str.length() != Length*2)
return false;
// Convert each digit. We store it in the same order as the string
int J = 0;
- for (string::const_iterator I = Str.begin(); I != Str.end();J++, I += 2)
+ for (auto I = Str.begin(); I != Str.end();J++, I += 2)
{
- if (isxdigit(*I) == 0 || isxdigit(I[1]) == 0)
+ int first_half = HexDigit(I[0]);
+ int second_half;
+ if (first_half < 0)
return false;
- Num[J] = HexDigit(I[0]) << 4;
- Num[J] += HexDigit(I[1]);
+ second_half = HexDigit(I[1]);
+ if (second_half < 0)
+ return false;
+ Num[J] = first_half << 4;
+ Num[J] += second_half;
}
return true;
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 separator 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) {
+ bool ret;
+ va_start(args,format);
+ ret = iovprintf(out, format, args, size);
+ va_end(args);
+ if (ret == true)
+ return;
+ }
}
- /*}}}*/
-// 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) {
+ bool ret;
+ va_start(args,format);
+ ret = iovprintf(outstr, format, args, size);
+ va_end(args);
+ if (ret == true)
+ break;
+ }
+ 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;
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. */
+#undef tolower_ascii
+int tolower_ascii(int const c) APT_CONST APT_COLD;
int tolower_ascii(int const c)
{
- if (c >= 'A' && c <= 'Z')
- return c + 32;
- return c;
+ return tolower_ascii_inline(c);
+}
+ /*}}}*/
+
+// isspace_ascii - isspace() function that ignores the locale /*{{{*/
+// ---------------------------------------------------------------------
+/* This little function is one of the most called methods we have and tries
+ therefore to do the absolut minimum - and is notable faster than
+ standard isspace() and as a bonus avoids problems with different
+ locales - we only operate on ascii chars anyway. */
+#undef isspace_ascii
+int isspace_ascii(int const c) APT_CONST APT_COLD;
+int isspace_ascii(int const c)
+{
+ return isspace_ascii_inline(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++);
- string::const_iterator SecondColon = I;
-
- // Search for the @ after the colon
- for (; I < SingleSlash && *I != '@'; I++);
- string::const_iterator At = I;
-
+
+ // Search for the @ separating user:pass from host
+ auto const RevAt = std::find(
+ std::string::const_reverse_iterator(SingleSlash),
+ std::string::const_reverse_iterator(I), '@');
+ string::const_iterator const At = RevAt.base() == I ? SingleSlash : std::prev(RevAt.base());
+ // and then look for the colon between user and pass
+ string::const_iterator const SecondColon = std::find(I, At, ':');
+
// Now write the host and user/pass
if (At == SingleSlash)
{
/* */
URI::operator string()
{
- string Res;
-
+ std::stringstream Res;
+
if (Access.empty() == false)
- Res = Access + ':';
-
+ Res << Access << ':';
+
if (Host.empty() == false)
- {
+ {
if (Access.empty() == false)
- Res += "//";
-
+ Res << "//";
+
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 += "@";
+ Res << ":" << QuoteString(Password, ":/?#[]@");
+ Res << "@";
}
-
+
// Add RFC 2732 escaping characters
- if (Access.empty() == false &&
- (Host.find('/') != string::npos || Host.find(':') != string::npos))
- Res += '[' + Host + ']';
+ if (Access.empty() == false && Host.find_first_of("/:") != string::npos)
+ Res << '[' << Host << ']';
else
- Res += Host;
-
+ Res << Host;
+
if (Port != 0)
- {
- char S[30];
- sprintf(S,":%u",Port);
- Res += S;
- }
+ Res << ':' << std::to_string(Port);
}
-
+
if (Path.empty() == false)
{
if (Path[0] != '/')
- Res += "/" + Path;
+ Res << "/" << Path;
else
- Res += Path;
+ Res << Path;
}
-
- return Res;
+
+ return Res.str();
}
/*}}}*/
// URI::SiteOnly - Return the schema and site for the 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::ArchiveOnly - Return the schema, site and cleaned path for the URI /*{{{*/
+string URI::ArchiveOnly(const string &URI)
+{
+ ::URI U(URI);
+ U.User.clear();
+ U.Password.clear();
+ if (U.Path.empty() == false && U.Path[U.Path.length() - 1] == '/')
+ U.Path.erase(U.Path.length() - 1);
return U;
}
/*}}}*/
// URI::NoUserPassword - Return the schema, site and path for the URI /*{{{*/
-// ---------------------------------------------------------------------
-/* */
string URI::NoUserPassword(const string &URI)
{
::URI U(URI);
U.User.clear();
U.Password.clear();
- U.Port = 0;
return U;
}
/*}}}*/