]> git.saurik.com Git - apt.git/blobdiff - apt-pkg/contrib/strutl.cc
apt-key: change to / before find to satisfy its CWD needs
[apt.git] / apt-pkg / contrib / strutl.cc
index 05624f7fba8a6f4fea0fd2d19ee92f871d97756a..fba1e3306c5753f128b1096ee79c2777933daf83 100644 (file)
 #include <apt-pkg/fileutl.h>
 #include <apt-pkg/error.h>
 
+#include <algorithm>
+#include <iomanip>
+#include <locale>
+#include <sstream>
+#include <string>
+#include <vector>
+
 #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 <regex.h>
 #include <errno.h>
@@ -71,14 +74,14 @@ 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);
+   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.substr(0, start.size()) == start);
+   return (s.compare(0, start.size(), start) == 0);
 }
 
 }
@@ -331,7 +334,7 @@ string QuoteString(const string &Str, const char *Bad)
          *I == 0x25 || // percent '%' char
          *I <= 0x20 || *I >= 0x7F) // control chars
       {
-        ioprintf(Res,"%%%02x",(int)*I);
+        ioprintf(Res, "%%%02hhx", *I);
       }
       else
         Res << *I;
@@ -464,7 +467,9 @@ string SubstVar(const string &Str,const string &Subst,const string &Contents)
 
    if (OldPos >= Str.length())
       return Temp;
-   return Temp + string(Str,OldPos);
+
+   Temp.append(Str, OldPos, string::npos);
+   return Temp;
 }
 string SubstVar(string Str,const struct SubstVar *Vars)
 {
@@ -690,9 +695,9 @@ 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 (; isspace_ascii(*I) != 0 && I < Message.end(); ++I);
         for (J = I; *J != '\n' && J < Message.end(); ++J);
-        for (; J > I && isspace(J[-1]) != 0; --J);
+        for (; J > I && isspace_ascii(J[-1]) != 0; --J);
         
         return string(I,J);
       }
@@ -749,15 +754,11 @@ string TimeRFC1123(time_t Date)
    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"};
-
-   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;
+   auto const posix = std::locale("C.UTF-8");
+   std::ostringstream datestr;
+   datestr.imbue(posix);
+   datestr << std::put_time(&Conv, "%a, %d %b %Y %H:%M:%S GMT");
+   return datestr.str();
 }
                                                                        /*}}}*/
 // ReadMessages - Read messages from the FD                            /*{{{*/
@@ -788,7 +789,11 @@ bool ReadMessages(int Fd, vector<string> &List)
         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;
@@ -915,29 +920,56 @@ static time_t timegm(struct tm *t)
 }
 #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)
+   struct tm t;
+   auto const &posix = std::locale("C.UTF-8");
+   auto const parse_time = [&](char const * const s, bool const has_timezone) {
+      std::istringstream ss(str);
+      ss.imbue(posix);
+      ss >> std::get_time(&t, s);
+      if (has_timezone && ss.fail() == false)
+      {
+        std::string timezone;
+        ss >> timezone;
+        if (timezone.empty())
+           return false;
+        if (timezone != "GMT" && timezone != "UTC" && timezone != "Z") // RFC 822
+        {
+           // numeric timezones as a should of RFC 1123 and generally preferred
+           try {
+              size_t pos;
+              auto const zone = std::stoi(timezone, &pos);
+              if (zone != 0 || pos != timezone.length())
+                 return false;
+           } catch (...) {
+              return false;
+           }
+        }
+      }
+      t.tm_isdst = 0;
+      return ss.fail() == false;
+   };
+
+   bool const good =
+      // Sun, 06 Nov 1994 08:49:37 GMT  ; RFC 822, updated by RFC 1123
+      parse_time("%a, %d %b %Y %H:%M:%S", true) ||
+      // Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
+      parse_time("%A, %d-%b-%y %H:%M:%S", true) ||
+      // Sun Nov  6 08:49:37 1994       ; ANSI C's asctime() format
+      parse_time("%c", false); // "%a %b %d %H:%M:%S %Y"
+   if (good == false)
       return false;
 
-   time = timegm(&Tm);
+   time = timegm(&t);
    return true;
 }
                                                                        /*}}}*/
@@ -1106,26 +1138,36 @@ static int HexDigit(int c)
       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;
@@ -1201,7 +1243,7 @@ vector<string> StringSplit(std::string const &s, std::string const &sep,
    vector<string> split;
    size_t start, pos;
 
-   // no seperator given, this is bogus
+   // no separator given, this is bogus
    if(sep.size() == 0)
       return split;
 
@@ -1364,17 +1406,32 @@ string StripEpoch(const string &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 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);
 }
                                                                        /*}}}*/
 
@@ -1622,7 +1679,7 @@ URI::operator string()
         Res << Host;
 
       if (Port != 0)
-        Res << ':' << Port;
+        Res << ':' << std::to_string(Port);
    }
 
    if (Path.empty() == false)