]> git.saurik.com Git - apt.git/blobdiff - apt-pkg/contrib/strutl.cc
Add support for the LANGUAGE environment variable
[apt.git] / apt-pkg / contrib / strutl.cc
index 303cb27dbce986be7f972f8ada6c992db0ffc090..d3d6e27398f07f3d0ebdd36bfcff62919d084c9e 100644 (file)
    ##################################################################### */
                                                                        /*}}}*/
 // 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);
@@ -242,10 +267,10 @@ bool ParseCWord(const char *&String,string &Res)
 // QuoteString - Convert a string into quoted from                     /*{{{*/
 // ---------------------------------------------------------------------
 /* */
-string QuoteString(string Str,const char *Bad)
+string QuoteString(const string &Str, const char *Bad)
 {
    string Res;
-   for (string::iterator I = Str.begin(); I != Str.end(); I++)
+   for (string::const_iterator I = Str.begin(); I != Str.end(); I++)
    {
       if (strchr(Bad,*I) != 0 || isprint(*I) == 0 || 
          *I <= 0x20 || *I >= 0x7F)
@@ -263,7 +288,7 @@ string QuoteString(string Str,const char *Bad)
 // DeQuoteString - Convert a string from quoted from                    /*{{{*/
 // ---------------------------------------------------------------------
 /* This undoes QuoteString */
-string DeQuoteString(string Str)
+string DeQuoteString(const string &Str)
 {
    string Res;
    for (string::const_iterator I = Str.begin(); I != Str.end(); I++)
@@ -307,13 +332,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 +359,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;
    }
    
@@ -360,7 +389,7 @@ string TimeToStr(unsigned long Sec)
 // SubstVar - Substitute a string for another string                   /*{{{*/
 // ---------------------------------------------------------------------
 /* This replaces all occurances of Subst with Contents in Str. */
-string SubstVar(string Str,string Subst,string Contents)
+string SubstVar(const string &Str,const string &Subst,const string &Contents)
 {
    string::size_type Pos = 0;
    string::size_type OldPos = 0;
@@ -386,26 +415,34 @@ 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
    and converts / to _ and removes the scheme identifier. The resulting
    file name should be unique and never occur again for a different file */
-string URItoFileName(string URI)
+string URItoFileName(const string &URI)
 {
    // Nuke 'sensitive' items
    ::URI U(URI);
-   U.User = string();
-   U.Password = string();
-   U.Access = "";
+   U.User.clear();
+   U.Password.clear();
+   U.Access.clear();
    
    // "\x00-\x20{}|\\\\^\\[\\]<>\"\x7F-\xFF";
-   URI = QuoteString(U,"\\|{}[]<>\"^~=!@#$%^&*");
-   string::iterator J = URI.begin();
-   for (; J != URI.end(); J++)
-      if (*J == '/') 
-        *J = '_';
-   return URI;
+   string NewURI = QuoteString(U,"\\|{}[]<>\"^~_=!@#$%^&*");
+   replace(NewURI.begin(),NewURI.end(),'/','_');
+   return NewURI;
 }
                                                                        /*}}}*/
 // Base64Encode - Base64 Encoding routine for short strings            /*{{{*/
@@ -414,7 +451,7 @@ string URItoFileName(string URI)
    from wget and then patched and bug fixed.
  
    This spec can be found in rfc2045 */
-string Base64Encode(string S)
+string Base64Encode(const string &S)
 {
    // Conversion table.
    static char tbl[64] = {'A','B','C','D','E','F','G','H',
@@ -465,9 +502,9 @@ string Base64Encode(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)
 {
@@ -523,7 +560,7 @@ 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)
@@ -583,17 +620,17 @@ int stringcasecmp(string::const_iterator A,string::const_iterator AEnd,
 // ---------------------------------------------------------------------
 /* The format is like those used in package files and the method 
    communication system */
-string LookupTag(string Message,const char *Tag,const char *Default)
+string LookupTag(const string &Message,const char *Tag,const char *Default)
 {
    // Look for a matching tag.
    int Length = strlen(Tag);
-   for (string::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::iterator J;
+        string::const_iterator J;
         I += Length + 1;
         for (; isspace(*I) != 0 && I < Message.end(); I++);
         for (J = I; *J != '\n' && J < Message.end(); J++);
@@ -615,7 +652,7 @@ string LookupTag(string Message,const char *Tag,const char *Default)
 // ---------------------------------------------------------------------
 /* This inspects the string to see if it is true or if it is false and
    then returns the result. Several varients on true/false are checked. */
-int StringToBool(string Text,int Default)
+int StringToBool(const string &Text,int Default)
 {
    char *End;
    int Res = strtol(Text.c_str(),&End,0);   
@@ -664,11 +701,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)
    {
@@ -696,6 +746,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++);
@@ -703,10 +754,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;
@@ -781,7 +854,7 @@ static time_t timegm(struct tm *t)
    '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
    handles the weird, but unambiguous FTP time format*/
-bool StrToTime(string Val,time_t &Result)
+bool StrToTime(const string &Val,time_t &Result)
 {
    struct tm Tm;
    char Month[10];
@@ -868,7 +941,7 @@ static int HexDigit(int c)
 // 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(string Str,unsigned char *Num,unsigned int Length)
+bool Hex2Num(const string &Str,unsigned char *Num,unsigned int Length)
 {
    if (Str.length() != Length*2)
       return false;
@@ -927,6 +1000,23 @@ bool TokSplitString(char Tok,char *Input,char **List,
    return true;
 }
                                                                        /*}}}*/
+// ExplodeString - Split a string up into a vector                     /*{{{*/
+// ---------------------------------------------------------------------
+/* 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 an iteratable vector */
+vector<string> ExplodeString(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                                /*{{{*/
 // ---------------------------------------------------------------------
 /* */
@@ -997,11 +1087,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
@@ -1025,11 +1130,22 @@ char *safe_snprintf(char *Buffer,char *End,const char *Format,...)
 }
                                                                        /*}}}*/
 
+// tolower_ascii - tolower() function that ignores the locale          /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+int tolower_ascii(int c)
+{
+   if (c >= 'A' and 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
    matched against the argument */
-bool CheckDomainList(string Host,string List)
+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++)
@@ -1052,7 +1168,7 @@ bool CheckDomainList(string Host,string List)
 // URI::CopyFrom - Copy from an object                                 /*{{{*/
 // ---------------------------------------------------------------------
 /* This parses the URI into all of its components */
-void URI::CopyFrom(string U)
+void URI::CopyFrom(const string &U)
 {
    string::const_iterator I = U.begin();
 
@@ -1081,9 +1197,9 @@ void URI::CopyFrom(string U)
       SingleSlash = U.end();
 
    // We can now write the access and path specifiers
-   Access = string(U,0,FirstColon - U.begin());
+   Access.assign(U.begin(),FirstColon);
    if (SingleSlash != U.end())
-      Path = string(U,SingleSlash - U.begin());
+      Path.assign(SingleSlash,U.end());
    if (Path.empty() == true)
       Path = "/";
 
@@ -1113,14 +1229,14 @@ void URI::CopyFrom(string U)
    if (At == SingleSlash)
    {
       if (FirstColon < SingleSlash)
-        Host = string(U,FirstColon - U.begin(),SingleSlash - FirstColon);
+        Host.assign(FirstColon,SingleSlash);
    }
    else
    {
-      Host = string(U,At - U.begin() + 1,SingleSlash - At - 1);
-      User = string(U,FirstColon - U.begin(),SecondColon - FirstColon);
+      Host.assign(At+1,SingleSlash);
+      User.assign(FirstColon,SecondColon);
       if (SecondColon < At)
-        Password = string(U,SecondColon - U.begin() + 1,At - SecondColon - 1);
+        Password.assign(SecondColon+1,At);
    }   
    
    // Now we parse the RFC 2732 [] hostnames.
@@ -1148,7 +1264,7 @@ void URI::CopyFrom(string U)
    // Tsk, weird.
    if (InBracket == true)
    {
-      Host = string();
+      Host.clear();
       return;
    }
    
@@ -1159,7 +1275,7 @@ void URI::CopyFrom(string U)
       return;
    
    Port = atoi(string(Host,Pos+1).c_str());
-   Host = string(Host,0,Pos);
+   Host.assign(Host,0,Pos);
 }
                                                                        /*}}}*/
 // URI::operator string - Convert the URI to a string                  /*{{{*/
@@ -1214,12 +1330,12 @@ URI::operator string()
 // URI::SiteOnly - Return the schema and site for the URI              /*{{{*/
 // ---------------------------------------------------------------------
 /* */
-string URI::SiteOnly(string URI)
+string URI::SiteOnly(const string &URI)
 {
    ::URI U(URI);
-   U.User = string();
-   U.Password = string();
-   U.Path = string();
+   U.User.clear();
+   U.Password.clear();
+   U.Path.clear();
    U.Port = 0;
    return U;
 }