X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/59e4486eb632f4e56eb336fe5a7dc694059e741c..58d9e6e0f9bf93724637c8a5687fe22058fc75a1:/apt-pkg/contrib/strutl.cc

diff --git a/apt-pkg/contrib/strutl.cc b/apt-pkg/contrib/strutl.cc
index 37d263794..c1844de40 100644
--- a/apt-pkg/contrib/strutl.cc
+++ b/apt-pkg/contrib/strutl.cc
@@ -15,10 +15,6 @@
    ##################################################################### */
 									/*}}}*/
 // Includes								/*{{{*/
-#ifdef __GNUG__
-#pragma implementation "apt-pkg/strutl.h"
-#endif
-
 #include <apt-pkg/strutl.h>
 #include <apt-pkg/fileutl.h>
 #include <apt-pkg/error.h>
@@ -28,6 +24,7 @@
 #include <ctype.h>
 #include <string.h>
 #include <stdio.h>
+#include <algorithm>
 #include <unistd.h>
 #include <regex.h>
 #include <errno.h>
@@ -46,9 +43,10 @@ bool UTF8ToCodeset(const char *codeset, const string &orig, string *dest)
 {
   iconv_t cd;
   const char *inbuf;
-  char *inptr, *outbuf, *outptr;
-  size_t insize, outsize;
-  
+  char *inptr, *outbuf;
+  size_t insize, bufsize;
+  dest->clear();
+
   cd = iconv_open(codeset, "UTF-8");
   if (cd == (iconv_t)(-1)) {
      // Something went wrong
@@ -58,22 +56,49 @@ bool UTF8ToCodeset(const char *codeset, const string &orig, string *dest)
      else
 	perror("iconv_open");
      
-     // Clean the destination string
-     *dest = "";
-     
      return false;
   }
 
-  insize = outsize = orig.size();
+  insize = bufsize = orig.size();
   inbuf = orig.data();
   inptr = (char *)inbuf;
-  outbuf = new char[insize+1];
-  outptr = outbuf;
+  outbuf = new char[bufsize];
+  size_t lastError = -1;
 
-  iconv(cd, &inptr, &insize, &outptr, &outsize);
-  *outptr = '\0';
+  while (insize != 0)
+  {
+     char *outptr = outbuf;
+     size_t outsize = bufsize;
+     size_t const err = iconv(cd, &inptr, &insize, &outptr, &outsize);
+     dest->append(outbuf, outptr - outbuf);
+     if (err == (size_t)(-1))
+     {
+	switch (errno)
+	{
+	case EILSEQ:
+	   insize--;
+	   inptr++;
+	   // replace a series of unknown multibytes with a single "?"
+	   if (lastError != insize) {
+	      lastError = insize - 1;
+	      dest->append("?");
+	   }
+	   break;
+	case EINVAL:
+	   insize = 0;
+	   break;
+	case E2BIG:
+	   if (outptr == outbuf)
+	   {
+	      bufsize *= 2;
+	      delete[] outbuf;
+	      outbuf = new char[bufsize];
+	   }
+	   break;
+	}
+     }
+  }
 
-  *dest = outbuf;
   delete[] outbuf;
   
   iconv_close(cd);
@@ -173,7 +198,8 @@ bool ParseQuoteWord(const char *&String,string &Res)
    char *I;
    for (I = Buffer; I < Buffer + sizeof(Buffer) && Start != C; I++)
    {
-      if (*Start == '%' && Start + 2 < C)
+      if (*Start == '%' && Start + 2 < C &&
+	  isxdigit(Start[1]) && isxdigit(Start[2]))
       {
 	 Tmp[0] = Start[1];
 	 Tmp[1] = Start[2];
@@ -248,7 +274,8 @@ string QuoteString(const string &Str, const char *Bad)
    for (string::const_iterator I = Str.begin(); I != Str.end(); I++)
    {
       if (strchr(Bad,*I) != 0 || isprint(*I) == 0 || 
-	  *I <= 0x20 || *I >= 0x7F)
+	  *I == 0x25 || // percent '%' char
+	  *I <= 0x20 || *I >= 0x7F) // control chars
       {
 	 char Buf[10];
 	 sprintf(Buf,"%%%02x",(int)*I);
@@ -264,11 +291,17 @@ string QuoteString(const string &Str, const char *Bad)
 // ---------------------------------------------------------------------
 /* This undoes QuoteString */
 string DeQuoteString(const string &Str)
+{
+   return DeQuoteString(Str.begin(),Str.end());
+}
+string DeQuoteString(string::const_iterator const &begin,
+			string::const_iterator const &end)
 {
    string Res;
-   for (string::const_iterator I = Str.begin(); I != Str.end(); I++)
+   for (string::const_iterator I = begin; I != end; I++)
    {
-      if (*I == '%' && I + 2 < Str.end())
+      if (*I == '%' && I + 2 < end &&
+	  isxdigit(I[1]) && isxdigit(I[2]))
       {
 	 char Tmp[3];
 	 Tmp[0] = I[1];
@@ -307,13 +340,13 @@ string SizeToStr(double Size)
    {
       if (ASize < 100 && I != 0)
       {
-         sprintf(S,"%.1f%c",ASize,Ext[I]);
+         sprintf(S,"%'.1f%c",ASize,Ext[I]);
 	 break;
       }
       
       if (ASize < 10000)
       {
-         sprintf(S,"%.0f%c",ASize,Ext[I]);
+         sprintf(S,"%'.0f%c",ASize,Ext[I]);
 	 break;
       }
       ASize /= 1000.0;
@@ -334,23 +367,27 @@ string TimeToStr(unsigned long Sec)
    {
       if (Sec > 60*60*24)
       {
-	 sprintf(S,"%lid %lih%lim%lis",Sec/60/60/24,(Sec/60/60) % 24,(Sec/60) % 60,Sec % 60);
+	 //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)
       {
-	 sprintf(S,"%lih%lim%lis",Sec/60/60,(Sec/60) % 60,Sec % 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)
       {
-	 sprintf(S,"%lim%lis",Sec/60,Sec % 60);
+	 //min means minutes, s means seconds
+	 sprintf(S,_("%limin %lis"),Sec/60,Sec % 60);
 	 break;
       }
-      
-      sprintf(S,"%lis",Sec);
+
+      //s means seconds
+      sprintf(S,_("%lis"),Sec);
       break;
    }
    
@@ -386,6 +423,17 @@ string SubstVar(string Str,const struct SubstVar *Vars)
    return Str;
 }
 									/*}}}*/
+// OutputInDepth - return a string with separator multiplied with depth /*{{{*/
+// ---------------------------------------------------------------------
+/* Returns a string with the supplied separator depth + 1 times in it */
+std::string OutputInDepth(const unsigned long Depth, const char* Separator)
+{
+   std::string output = "";
+   for(unsigned long d=Depth+1; d > 0; d--)
+      output.append(Separator);
+   return output;
+}
+									/*}}}*/
 // URItoFileName - Convert the uri into a unique file name		/*{{{*/
 // ---------------------------------------------------------------------
 /* This converts a URI into a safe filename. It quotes all unsafe characters
@@ -462,9 +510,9 @@ string Base64Encode(const string &S)
    return Final;
 }
 									/*}}}*/
-// stringcmp - Arbitary string compare					/*{{{*/
+// stringcmp - Arbitrary string compare					/*{{{*/
 // ---------------------------------------------------------------------
-/* This safely compares two non-null terminated strings of arbitary 
+/* This safely compares two non-null terminated strings of arbitrary 
    length */
 int stringcmp(const char *A,const char *AEnd,const char *B,const char *BEnd)
 {
@@ -520,13 +568,13 @@ int stringcmp(string::const_iterator A,string::const_iterator AEnd,
 }
 #endif
 									/*}}}*/
-// stringcasecmp - Arbitary case insensitive string compare		/*{{{*/
+// stringcasecmp - Arbitrary case insensitive string compare		/*{{{*/
 // ---------------------------------------------------------------------
 /* */
 int stringcasecmp(const char *A,const char *AEnd,const char *B,const char *BEnd)
 {
    for (; A != AEnd && B != BEnd; A++, B++)
-      if (toupper(*A) != toupper(*B))
+      if (tolower_ascii(*A) != tolower_ascii(*B))
 	 break;
 
    if (A == AEnd && B == BEnd)
@@ -535,7 +583,7 @@ int stringcasecmp(const char *A,const char *AEnd,const char *B,const char *BEnd)
       return 1;
    if (B == BEnd)
       return -1;
-   if (toupper(*A) < toupper(*B))
+   if (tolower_ascii(*A) < tolower_ascii(*B))
       return -1;
    return 1;
 }
@@ -544,7 +592,7 @@ int stringcasecmp(string::const_iterator A,string::const_iterator AEnd,
 		  const char *B,const char *BEnd)
 {
    for (; A != AEnd && B != BEnd; A++, B++)
-      if (toupper(*A) != toupper(*B))
+      if (tolower_ascii(*A) != tolower_ascii(*B))
 	 break;
 
    if (A == AEnd && B == BEnd)
@@ -553,7 +601,7 @@ int stringcasecmp(string::const_iterator A,string::const_iterator AEnd,
       return 1;
    if (B == BEnd)
       return -1;
-   if (toupper(*A) < toupper(*B))
+   if (tolower_ascii(*A) < tolower_ascii(*B))
       return -1;
    return 1;
 }
@@ -561,7 +609,7 @@ int stringcasecmp(string::const_iterator A,string::const_iterator AEnd,
 		  string::const_iterator B,string::const_iterator BEnd)
 {
    for (; A != AEnd && B != BEnd; A++, B++)
-      if (toupper(*A) != toupper(*B))
+      if (tolower_ascii(*A) != tolower_ascii(*B))
 	 break;
 
    if (A == AEnd && B == BEnd)
@@ -570,7 +618,7 @@ int stringcasecmp(string::const_iterator A,string::const_iterator AEnd,
       return 1;
    if (B == BEnd)
       return -1;
-   if (toupper(*A) < toupper(*B))
+   if (tolower_ascii(*A) < tolower_ascii(*B))
       return -1;
    return 1;
 }
@@ -661,11 +709,24 @@ string TimeRFC1123(time_t Date)
 // ---------------------------------------------------------------------
 /* This pulls full messages from the input FD into the message buffer. 
    It assumes that messages will not pause during transit so no
-   fancy buffering is used. */
+   fancy buffering is used.
+
+   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.
+ */
 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)
    {
@@ -693,6 +754,7 @@ bool ReadMessages(int Fd, vector<string> &List)
 	 
 	 // Pull the message out
 	 string Message(Buffer,I-Buffer);
+	 PartialMessage += Message;
 
 	 // Fix up the buffer
 	 for (; I < End && *I == '\n'; I++);
@@ -700,10 +762,32 @@ bool ReadMessages(int Fd, vector<string> &List)
 	 memmove(Buffer,I,End-Buffer);
 	 I = Buffer;
 	 
-	 List.push_back(Message);
+	 List.push_back(PartialMessage);
+	 PartialMessage.clear();
       }
-      if (End == Buffer)
-	 return true;
+      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;
+	}
 
       if (WaitFd(Fd) == false)
 	 return false;
@@ -713,28 +797,28 @@ bool ReadMessages(int Fd, vector<string> &List)
 // 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 touppers though. */
+   Made it a bit more robust with a few tolower_ascii though. */
 static int MonthConv(char *Month)
 {
-   switch (toupper(*Month)) 
+   switch (tolower_ascii(*Month)) 
    {
-      case 'A':
-      return toupper(Month[1]) == 'P'?3:7;
-      case 'D':
+      case 'a':
+      return tolower_ascii(Month[1]) == 'p'?3:7;
+      case 'd':
       return 11;
-      case 'F':
+      case 'f':
       return 1;
-      case 'J':
-      if (toupper(Month[1]) == 'A')
+      case 'j':
+      if (tolower_ascii(Month[1]) == 'a')
 	 return 0;
-      return toupper(Month[2]) == 'N'?5:6;
-      case 'M':
-      return toupper(Month[2]) == 'R'?2:4;
-      case 'N':
+      return tolower_ascii(Month[2]) == 'n'?5:6;
+      case 'm':
+      return tolower_ascii(Month[2]) == 'r'?2:4;
+      case 'n':
       return 10;
-      case 'O':
+      case 'o':
       return 9;
-      case 'S':
+      case 's':
       return 8;
 
       // Pretend it is January..
@@ -743,34 +827,70 @@ static int MonthConv(char *Month)
    }   
 }
 									/*}}}*/
-// timegm - Internal timegm function if gnu is not available		/*{{{*/
+// timegm - Internal timegm if the gnu version is not available		/*{{{*/
 // ---------------------------------------------------------------------
-/* Ripped this evil little function from wget - I prefer the use of 
-   GNU timegm if possible as this technique will have interesting problems
-   with leap seconds, timezones and other.
-   
-   Converts struct tm to time_t, assuming the data in tm is UTC rather
+/* Converts struct tm to time_t, assuming the data in tm is UTC rather
    than local timezone (mktime assumes the latter).
-   
-   Contributed by Roger Beeman <beeman@cisco.com>, with the help of
-   Mark Baushke <mdb@cisco.com> and the rest of the Gurus at CISCO. */
-
-/* Turned it into an autoconf check, because GNU is not the only thing which
-   can provide timegm. -- 2002-09-22, Joel Baker */
 
-#ifndef HAVE_TIMEGM // Now with autoconf!
+   This function is a nonstandard GNU extension that is also present on
+   the BSDs and maybe other systems. For others we follow the advice of
+   the manpage of timegm and use his portable replacement. */
+#ifndef HAVE_TIMEGM
 static time_t timegm(struct tm *t)
 {
-   time_t tl, tb;
-   
-   tl = mktime (t);
-   if (tl == -1)
-      return -1;
-   tb = mktime (gmtime (&tl));
-   return (tl <= tb ? (tl + (tl - tb)) : (tl - (tb - tl)));
+   char *tz = getenv("TZ");
+   setenv("TZ", "", 1);
+   tzset();
+   time_t ret = mktime(t);
+   if (tz)
+      setenv("TZ", tz, 1);
+   else
+      unsetenv("TZ");
+   tzset();
+   return ret;
 }
 #endif
 									/*}}}*/
+// FullDateToTime - 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 */
+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)
+      return false;
+
+   time = timegm(&Tm);
+   return true;
+}
+									/*}}}*/
+// FTPMDTMStrToTime - Converts a ftp modification date into a time_t	/*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool FTPMDTMStrToTime(const char* const str,time_t &time)
+{
+   struct tm Tm;
+   // MDTM includes no whitespaces but recommend and ignored by strptime
+   if (strptime(str, "%Y %m %d %H %M %S", &Tm) == NULL)
+      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
@@ -924,6 +1044,24 @@ bool TokSplitString(char Tok,char *Input,char **List,
    return true;
 }
 									/*}}}*/
+// VectorizeString - Split a string up into a vector of strings		/*{{{*/
+// ---------------------------------------------------------------------
+/* This can be used to split a given string up into a vector, so the
+   propose is the same as in the method above and this one is a bit slower
+   also, but the advantage is that we have an iteratable vector */
+vector<string> VectorizeString(string const &haystack, char const &split)
+{
+   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));
+      start = end + 1;
+   } while (end != haystack.end() && (++end) != haystack.end());
+   return exploded;
+}
+									/*}}}*/
 // RegexChoice - Simple regex list/list matcher				/*{{{*/
 // ---------------------------------------------------------------------
 /* */
@@ -994,11 +1132,26 @@ void ioprintf(ostream &out,const char *format,...)
    va_start(args,format);
    
    // sprintf the description
-   char S[400];
+   char S[4096];
    vsnprintf(S,sizeof(S),format,args);
    out << S;
 }
 									/*}}}*/
+// 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,...) 
+{
+   va_list args;
+   va_start(args,format);
+   
+   // sprintf the description
+   char S[4096];
+   vsnprintf(S,sizeof(S),format,args);
+   out = string(S);
+}
+									/*}}}*/
 // safe_snprintf - Safer snprintf					/*{{{*/
 // ---------------------------------------------------------------------
 /* This is a snprintf that will never (ever) go past 'End' and returns a
@@ -1008,7 +1161,7 @@ void ioprintf(ostream &out,const char *format,...)
 char *safe_snprintf(char *Buffer,char *End,const char *Format,...)
 {
    va_list args;
-   unsigned long Did;
+   int Did;
 
    va_start(args,Format);
 
@@ -1022,6 +1175,20 @@ char *safe_snprintf(char *Buffer,char *End,const char *Format,...)
 }
 									/*}}}*/
 
+// 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
+   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)
+{
+   if (c >= 'A' && c <= 'Z')
+      return c + 32;
+   return c;
+}
+									/*}}}*/
+
 // CheckDomainList - See if Host is in a , seperate list		/*{{{*/
 // ---------------------------------------------------------------------
 /* The domain list is a comma seperate list of domains that are suffix
@@ -1115,9 +1282,10 @@ void URI::CopyFrom(const string &U)
    else
    {
       Host.assign(At+1,SingleSlash);
-      User.assign(FirstColon,SecondColon);
+      // username and password must be encoded (RFC 3986)
+      User.assign(DeQuoteString(FirstColon,SecondColon));
       if (SecondColon < At)
-	 Password.assign(SecondColon+1,At);
+	 Password.assign(DeQuoteString(SecondColon+1,At));
    }   
    
    // Now we parse the RFC 2732 [] hostnames.
@@ -1221,3 +1389,15 @@ string URI::SiteOnly(const string &URI)
    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;
+}
+									/*}}}*/