return (s.substr(s.size() - end.size(), s.size()) == end);
}
+bool Startswith(const std::string &s, const std::string &start)
+{
+ if (start.size() > s.size())
+ return false;
+ return (s.substr(0, start.size()) == start);
+}
+
}
}
/*}}}*/
/* */
string QuoteString(const string &Str, const char *Bad)
{
- string Res;
+ 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,"%%%02x",(int)*I);
}
else
- Res += *I;
+ Res << *I;
}
- return Res;
+ return Res.str();
}
/*}}}*/
// DeQuoteString - Convert a string from quoted from /*{{{*/
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;
}
/*}}}*/
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 (Res < 0 && errno == EAGAIN)
+ if (Res < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
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[1] != '\n' ||
- (I[0] != '\n' && strncmp(I, "\r\n\r\n", 4) != 0))
- continue;
-
- // Pull the message out
- string Message(Buffer,I-Buffer);
- PartialMessage += Message;
-
- // Fix up the buffer
- for (; I < End && (*I == '\n' || *I == '\r'); ++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 is used in decoding the 256bit encoded fixed length fields in
tar files */
-bool Base256ToNum(const char *Str,unsigned long &Res,unsigned int Len)
+bool Base256ToNum(const char *Str,unsigned long long &Res,unsigned int Len)
{
if ((Str[0] & 0x80) == 0)
return false;
}
}
/*}}}*/
+// 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 */
va_list args;
ssize_t size = 400;
while (true) {
+ bool ret;
va_start(args,format);
- if (iovprintf(out, format, args, size) == true)
- return;
+ ret = iovprintf(out, format, args, size);
va_end(args);
+ if (ret == true)
+ return;
}
}
void strprintf(string &out,const char *format,...)
ssize_t size = 400;
std::ostringstream outstr;
while (true) {
+ bool ret;
va_start(args,format);
- if (iovprintf(outstr, format, args, size) == true)
- break;
+ ret = iovprintf(outstr, format, args, size);
va_end(args);
+ if (ret == true)
+ break;
}
out = outstr.str();
}
;
return i;
}
-
+ /*}}}*/
// DeEscapeString - unescape (\0XX and \xXX) from a string /*{{{*/
// ---------------------------------------------------------------------
/* */
/* */
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)
{
// FIXME: Technically userinfo is permitted even less
// characters than these, but this is not conveniently
// expressed with a blacklist.
- Res += QuoteString(User, ":/?#[]@");
+ Res << QuoteString(User, ":/?#[]@");
if (Password.empty() == false)
- Res += ":" + QuoteString(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 << ':' << 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);
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);