]> git.saurik.com Git - apt.git/blobdiff - apt-pkg/contrib/strutl.cc
Merge branch 'debian/sid' into debian/experimental
[apt.git] / apt-pkg / contrib / strutl.cc
index ce69c7a027ea6306c5a1d421e8c756fcd9885a4a..ebf9c9ea6e4187eba9cd2ee1608e930768e97fca 100644 (file)
@@ -45,14 +45,26 @@ using namespace std;
 // ---------------------------------------------------------------------
 namespace APT {
    namespace String {
-std::string Strip(const std::string &s)
+std::string Strip(const std::string &str)
 {
-   size_t start = s.find_first_not_of(" \t\n");
-   // only whitespace
-   if (start == string::npos)
+   // 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 = s.find_last_not_of(" \t\n");
-   return s.substr(start, end-start+1);
+
+   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)
@@ -62,6 +74,13 @@ bool Endswith(const std::string &s, const std::string &end)
    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);
+}
+
 }
 }
                                                                        /*}}}*/
@@ -704,9 +723,12 @@ string LookupTag(const string &Message,const char *Tag,const char *Default)
    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
@@ -757,86 +779,94 @@ string TimeRFC1123(time_t Date)
 
    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                   /*{{{*/
@@ -1046,7 +1076,7 @@ bool StrToNum(const char *Str,unsigned long long &Res,unsigned Len,unsigned Base
 // ---------------------------------------------------------------------
 /* 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;
@@ -1059,6 +1089,23 @@ bool Base256ToNum(const char *Str,unsigned long &Res,unsigned int Len)
    }
 }
                                                                        /*}}}*/
+// 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 */