]> git.saurik.com Git - apt.git/blobdiff - apt-pkg/contrib/strutl.cc
* Install-Reason -> Auto-Installed in pkgstates
[apt.git] / apt-pkg / contrib / strutl.cc
index 6b22cfe032635e41f5f48e8ea9eab84220dbdbf4..a75fbdf9292e4720e4fab6878deaa699aa8a28c5 100644 (file)
@@ -1,12 +1,12 @@
 // -*- mode: cpp; mode: fold -*-
 // Description                                                         /*{{{*/
-// $Id: strutl.cc,v 1.32 2000/01/10 03:44:54 jgg Exp $
+// $Id: strutl.cc,v 1.48 2003/07/18 14:15:11 mdz Exp $
 /* ######################################################################
 
-   String Util - Some usefull string functions.
+   String Util - Some useful string functions.
 
-   These have been collected from here and there to do all sorts of usefull
-   things to strings. They are usefull in file parsers, URI handlers and
+   These have been collected from here and there to do all sorts of useful
+   things to strings. They are useful in file parsers, URI handlers and
    especially in APT methods.   
    
    This source is placed in the Public Domain, do with it what you will
 
 #include <apt-pkg/strutl.h>
 #include <apt-pkg/fileutl.h>
+#include <apt-pkg/error.h>
 
+#include <apti18n.h>
+    
 #include <ctype.h>
 #include <string.h>
 #include <stdio.h>
 #include <unistd.h>
+#include <regex.h>
 #include <errno.h>
+#include <stdarg.h>
+
+#include "config.h"
+
+using namespace std;
                                                                        /*}}}*/
 
 // strstrip - Remove white space from the front and back of a string   /*{{{*/
@@ -87,7 +96,8 @@ char *_strtabexpand(char *String,size_t Len)
 // ---------------------------------------------------------------------
 /* This grabs a single word, converts any % escaped characters to their
    proper values and advances the pointer. Double quotes are understood
-   and striped out as well. This is for URI/URL parsing. */
+   and striped out as well. This is for URI/URL parsing. It also can 
+   understand [] brackets.*/
 bool ParseQuoteWord(const char *&String,string &Res)
 {
    // Skip leading whitespace
@@ -101,7 +111,13 @@ bool ParseQuoteWord(const char *&String,string &Res)
    {
       if (*C == '"')
       {
-        for (C++;*C != 0 && *C != '"'; C++);
+        for (C++; *C != 0 && *C != '"'; C++);
+        if (*C == 0)
+           return false;
+      }
+      if (*C == '[')
+      {
+        for (C++; *C != 0 && *C != ']'; C++);
         if (*C == 0)
            return false;
       }
@@ -140,9 +156,9 @@ bool ParseQuoteWord(const char *&String,string &Res)
                                                                        /*}}}*/
 // ParseCWord - Parses a string like a C "" expression                 /*{{{*/
 // ---------------------------------------------------------------------
-/* This expects a series of space seperated strings enclosed in ""'s. 
+/* This expects a series of space separated strings enclosed in ""'s. 
    It concatenates the ""'s into a single string. */
-bool ParseCWord(const char *String,string &Res)
+bool ParseCWord(const char *&String,string &Res)
 {
    // Skip leading whitespace
    const char *C = String;
@@ -173,9 +189,10 @@ bool ParseCWord(const char *String,string &Res)
       if (isspace(*C) == 0)
         return false;
       *Buf++ = ' ';
-   }   
+   }
    *Buf = 0;
    Res = Buffer;
+   String = C;
    return true;
 }
                                                                        /*}}}*/
@@ -206,7 +223,7 @@ string QuoteString(string Str,const char *Bad)
 string DeQuoteString(string Str)
 {
    string Res;
-   for (string::iterator I = Str.begin(); I != Str.end(); I++)
+   for (string::const_iterator I = Str.begin(); I != Str.end(); I++)
    {
       if (*I == '%' && I + 2 < Str.end())
       {
@@ -318,6 +335,13 @@ string SubstVar(string Str,string Subst,string Contents)
    
    return Temp + string(Str,OldPos);
 }
+
+string SubstVar(string Str,const struct SubstVar *Vars)
+{
+   for (; Vars->Subst != 0; Vars++)
+      Str = SubstVar(Str,Vars->Subst,*Vars->Contents);
+   return Str;
+}
                                                                        /*}}}*/
 // URItoFileName - Convert the uri into a unique file name             /*{{{*/
 // ---------------------------------------------------------------------
@@ -418,6 +442,43 @@ int stringcmp(const char *A,const char *AEnd,const char *B,const char *BEnd)
       return -1;
    return 1;
 }
+
+#if __GNUC__ >= 3
+int stringcmp(string::const_iterator A,string::const_iterator AEnd,
+             const char *B,const char *BEnd)
+{
+   for (; A != AEnd && B != BEnd; A++, B++)
+      if (*A != *B)
+        break;
+   
+   if (A == AEnd && B == BEnd)
+      return 0;
+   if (A == AEnd)
+      return 1;
+   if (B == BEnd)
+      return -1;
+   if (*A < *B)
+      return -1;
+   return 1;
+}
+int stringcmp(string::const_iterator A,string::const_iterator AEnd,
+             string::const_iterator B,string::const_iterator BEnd)
+{
+   for (; A != AEnd && B != BEnd; A++, B++)
+      if (*A != *B)
+        break;
+   
+   if (A == AEnd && B == BEnd)
+      return 0;
+   if (A == AEnd)
+      return 1;
+   if (B == BEnd)
+      return -1;
+   if (*A < *B)
+      return -1;
+   return 1;
+}
+#endif
                                                                        /*}}}*/
 // stringcasecmp - Arbitary case insensitive string compare            /*{{{*/
 // ---------------------------------------------------------------------
@@ -438,6 +499,42 @@ int stringcasecmp(const char *A,const char *AEnd,const char *B,const char *BEnd)
       return -1;
    return 1;
 }
+#if __GNUC__ >= 3
+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))
+        break;
+
+   if (A == AEnd && B == BEnd)
+      return 0;
+   if (A == AEnd)
+      return 1;
+   if (B == BEnd)
+      return -1;
+   if (toupper(*A) < toupper(*B))
+      return -1;
+   return 1;
+}
+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))
+        break;
+
+   if (A == AEnd && B == BEnd)
+      return 0;
+   if (A == AEnd)
+      return 1;
+   if (B == BEnd)
+      return -1;
+   if (toupper(*A) < toupper(*B))
+      return -1;
+   return 1;
+}
+#endif
                                                                        /*}}}*/
 // LookupTag - Lookup the value of a tag in a taged string             /*{{{*/
 // ---------------------------------------------------------------------
@@ -459,7 +556,7 @@ string LookupTag(string Message,const char *Tag,const char *Default)
         for (J = I; *J != '\n' && J < Message.end(); J++);
         for (; J > I && isspace(J[-1]) != 0; J--);
         
-        return string(I,J-I);
+        return string(I,J);
       }
       
       for (; *I != '\n' && I < Message.end(); I++);
@@ -475,7 +572,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 = -1)
+int StringToBool(string Text,int Default)
 {
    char *End;
    int Res = strtol(Text.c_str(),&End,0);   
@@ -527,7 +624,7 @@ string TimeRFC1123(time_t Date)
    fancy buffering is used. */
 bool ReadMessages(int Fd, vector<string> &List)
 {
-   char Buffer[4000];
+   char Buffer[64000];
    char *End = Buffer;
    
    while (1)
@@ -541,9 +638,11 @@ bool ReadMessages(int Fd, vector<string> &List)
         return false;
       
       // No data
-      if (Res <= 0)
+      if (Res < 0 && errno == EAGAIN)
         return true;
-
+      if (Res < 0)
+        return false;
+                             
       End += Res;
       
       // Look for the end of the message
@@ -553,7 +652,7 @@ bool ReadMessages(int Fd, vector<string> &List)
            continue;
         
         // Pull the message out
-        string Message(Buffer,0,I-Buffer);
+        string Message(Buffer,I-Buffer);
 
         // Fix up the buffer
         for (; I < End && *I == '\n'; I++);
@@ -615,7 +714,11 @@ static int MonthConv(char *Month)
    
    Contributed by Roger Beeman <beeman@cisco.com>, with the help of
    Mark Baushke <mdb@cisco.com> and the rest of the Gurus at CISCO. */
-#ifndef __USE_MISC        // glib sets this
+
+/* 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!
 static time_t timegm(struct tm *t)
 {
    time_t tl, tb;
@@ -722,15 +825,14 @@ 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(const char *Start,const char *End,unsigned char *Num,
-            unsigned int Length)
+bool Hex2Num(string Str,unsigned char *Num,unsigned int Length)
 {
-   if (End - Start != (signed)(Length*2))
+   if (Str.length() != Length*2)
       return false;
    
    // Convert each digit. We store it in the same order as the string
    int J = 0;
-   for (const char *I = Start; I < End;J++, I += 2)
+   for (string::const_iterator I = Str.begin(); I != Str.end();J++, I += 2)
    {
       if (isxdigit(*I) == 0 || isxdigit(I[1]) == 0)
         return false;
@@ -742,6 +844,167 @@ bool Hex2Num(const char *Start,const char *End,unsigned char *Num,
    return true;
 }
                                                                        /*}}}*/
+// TokSplitString - Split a string up by a given token                 /*{{{*/
+// ---------------------------------------------------------------------
+/* This is intended to be a faster splitter, it does not use dynamic
+   memories. Input is changed to insert nulls at each token location. */
+bool TokSplitString(char Tok,char *Input,char **List,
+                   unsigned long ListMax)
+{
+   // Strip any leading spaces
+   char *Start = Input;
+   char *Stop = Start + strlen(Start);
+   for (; *Start != 0 && isspace(*Start) != 0; Start++);
+
+   unsigned long Count = 0;
+   char *Pos = Start;
+   while (Pos != Stop)
+   {
+      // Skip to the next Token
+      for (; Pos != Stop && *Pos != Tok; Pos++);
+      
+      // Back remove spaces
+      char *End = Pos;
+      for (; End > Start && (End[-1] == Tok || isspace(End[-1]) != 0); End--);
+      *End = 0;
+      
+      List[Count++] = Start;
+      if (Count >= ListMax)
+      {
+        List[Count-1] = 0;
+        return false;
+      }
+      
+      // Advance pos
+      for (; Pos != Stop && (*Pos == Tok || isspace(*Pos) != 0 || *Pos == 0); Pos++);
+      Start = Pos;
+   }
+   
+   List[Count] = 0;
+   return true;
+}
+                                                                       /*}}}*/
+// RegexChoice - Simple regex list/list matcher                                /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+unsigned long RegexChoice(RxChoiceList *Rxs,const char **ListBegin,
+                     const char **ListEnd)
+{
+   for (RxChoiceList *R = Rxs; R->Str != 0; R++)
+      R->Hit = false;
+
+   unsigned long Hits = 0;
+   for (; ListBegin != ListEnd; ListBegin++)
+   {
+      // Check if the name is a regex
+      const char *I;
+      bool Regex = true;
+      for (I = *ListBegin; *I != 0; I++)
+        if (*I == '.' || *I == '?' || *I == '*' || *I == '|')
+           break;
+      if (*I == 0)
+        Regex = false;
+        
+      // Compile the regex pattern
+      regex_t Pattern;
+      if (Regex == true)
+        if (regcomp(&Pattern,*ListBegin,REG_EXTENDED | REG_ICASE |
+                    REG_NOSUB) != 0)
+           Regex = false;
+        
+      // Search the list
+      bool Done = false;
+      for (RxChoiceList *R = Rxs; R->Str != 0; R++)
+      {
+        if (R->Str[0] == 0)
+           continue;
+        
+        if (strcasecmp(R->Str,*ListBegin) != 0)
+        {
+           if (Regex == false)
+              continue;
+           if (regexec(&Pattern,R->Str,0,0,0) != 0)
+              continue;
+        }
+        Done = true;
+        
+        if (R->Hit == false)
+           Hits++;
+        
+        R->Hit = true;
+      }
+      
+      if (Regex == true)
+        regfree(&Pattern);
+      
+      if (Done == false)
+        _error->Warning(_("Selection %s not found"),*ListBegin);
+   }
+   
+   return Hits;
+}
+                                                                       /*}}}*/
+// ioprintf - C format string outputter to C++ iostreams               /*{{{*/
+// ---------------------------------------------------------------------
+/* This is used to make the internationalization strings easier to translate
+   and to allow reordering of parameters */
+void ioprintf(ostream &out,const char *format,...) 
+{
+   va_list args;
+   va_start(args,format);
+   
+   // sprintf the description
+   char S[400];
+   vsnprintf(S,sizeof(S),format,args);
+   out << S;
+}
+                                                                       /*}}}*/
+// safe_snprintf - Safer snprintf                                      /*{{{*/
+// ---------------------------------------------------------------------
+/* This is a snprintf that will never (ever) go past 'End' and returns a
+   pointer to the end of the new string. The returned string is always null
+   terminated unless Buffer == end. This is a better alterantive to using
+   consecutive snprintfs. */
+char *safe_snprintf(char *Buffer,char *End,const char *Format,...)
+{
+   va_list args;
+   unsigned long Did;
+
+   va_start(args,Format);
+
+   if (End <= Buffer)
+      return End;
+
+   Did = vsnprintf(Buffer,End - Buffer,Format,args);
+   if (Did < 0 || Buffer + Did > End)
+      return End;
+   return Buffer + Did;
+}
+                                                                       /*}}}*/
+
+// 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)
+{
+   string::const_iterator Start = List.begin();
+   for (string::const_iterator Cur = List.begin(); Cur <= List.end(); Cur++)
+   {
+      if (Cur < List.end() && *Cur != ',')
+        continue;
+      
+      // Match the end of the string..
+      if ((Host.size() >= (unsigned)(Cur - Start)) &&
+         Cur - Start != 0 &&
+         stringcasecmp(Host.end() - (Cur - Start),Host.end(),Start,Cur) == 0)
+        return true;
+      
+      Start = Cur + 1;
+   }
+   return false;
+}
+                                                                       /*}}}*/
 
 // URI::CopyFrom - Copy from an object                                 /*{{{*/
 // ---------------------------------------------------------------------
@@ -750,7 +1013,7 @@ void URI::CopyFrom(string U)
 {
    string::const_iterator I = U.begin();
 
-   // Locate the first colon, this seperates the scheme
+   // Locate the first colon, this separates the scheme
    for (; I < U.end() && *I != ':' ; I++);
    string::const_iterator FirstColon = I;
 
@@ -759,7 +1022,18 @@ void URI::CopyFrom(string U)
    string::const_iterator SingleSlash = I;
    if (I + 3 < U.end() && I[1] == '/' && I[2] == '/')
       SingleSlash += 3;
-   for (; SingleSlash < U.end() && *SingleSlash != '/'; SingleSlash++);
+   
+   /* Find the / indicating the end of the hostname, ignoring /'s in the
+      square brackets */
+   bool InBracket = false;
+   for (; SingleSlash < U.end() && (*SingleSlash != '/' || InBracket == true); SingleSlash++)
+   {
+      if (*SingleSlash == '[')
+        InBracket = true;
+      if (InBracket == true && *SingleSlash == ']')
+        InBracket = false;
+   }
+   
    if (SingleSlash > U.end())
       SingleSlash = U.end();
 
@@ -771,7 +1045,7 @@ void URI::CopyFrom(string U)
       Path = "/";
 
    // Now we attempt to locate a user:pass@host fragment
-   if (FirstColon[1] == '/' && FirstColon[2] == '/')
+   if (FirstColon + 2 <= U.end() && FirstColon[1] == '/' && FirstColon[2] == '/')
       FirstColon += 3;
    else
       FirstColon += 1;
@@ -806,10 +1080,39 @@ void URI::CopyFrom(string U)
         Password = string(U,SecondColon - U.begin() + 1,At - SecondColon - 1);
    }   
    
+   // Now we parse the RFC 2732 [] hostnames.
+   unsigned long PortEnd = 0;
+   InBracket = false;
+   for (unsigned I = 0; I != Host.length();)
+   {
+      if (Host[I] == '[')
+      {
+        InBracket = true;
+        Host.erase(I,1);
+        continue;
+      }
+      
+      if (InBracket == true && Host[I] == ']')
+      {
+        InBracket = false;
+        Host.erase(I,1);
+        PortEnd = I;
+        continue;
+      }
+      I++;
+   }
+   
+   // Tsk, weird.
+   if (InBracket == true)
+   {
+      Host = string();
+      return;
+   }
+   
    // Now we parse off a port number from the hostname
    Port = 0;
    string::size_type Pos = Host.rfind(':');
-   if (Pos == string::npos)
+   if (Pos == string::npos || Pos < PortEnd)
       return;
    
    Port = atoi(string(Host,Pos+1).c_str());
@@ -827,10 +1130,10 @@ URI::operator string()
       Res = Access + ':';
    
    if (Host.empty() == false)
-   {
+   {    
       if (Access.empty() == false)
         Res += "//";
-      
+          
       if (User.empty() == false)
       {
         Res +=  User;
@@ -839,7 +1142,13 @@ URI::operator string()
         Res += "@";
       }
       
-      Res += Host;
+      // Add RFC 2732 escaping characters
+      if (Access.empty() == false &&
+         (Host.find('/') != string::npos || Host.find(':') != string::npos))
+        Res += '[' + Host + ']';
+      else
+        Res += Host;
+      
       if (Port != 0)
       {
         char S[30];
@@ -859,3 +1168,16 @@ URI::operator string()
    return Res;
 }
                                                                        /*}}}*/
+// URI::SiteOnly - Return the schema and site for the URI              /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+string URI::SiteOnly(string URI)
+{
+   ::URI U(URI);
+   U.User = string();
+   U.Password = string();
+   U.Path = string();
+   U.Port = 0;
+   return U;
+}
+                                                                       /*}}}*/