]> git.saurik.com Git - apt.git/blobdiff - apt-pkg/contrib/strutl.cc
* some WS fixes
[apt.git] / apt-pkg / contrib / strutl.cc
index a6eaeaa79aa9f3980ccfb6db78e9d5f5b6dc1e05..a75fbdf9292e4720e4fab6878deaa699aa8a28c5 100644 (file)
@@ -1,12 +1,12 @@
 // -*- mode: cpp; mode: fold -*-
 // Description                                                         /*{{{*/
 // -*- mode: cpp; mode: fold -*-
 // Description                                                         /*{{{*/
-// $Id: strutl.cc,v 1.23 1999/03/16 07:27:17 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
    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/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 <ctype.h>
 #include <string.h>
 #include <stdio.h>
 #include <unistd.h>
+#include <regex.h>
 #include <errno.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   /*{{{*/
                                                                        /*}}}*/
 
 // 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
 // ---------------------------------------------------------------------
 /* 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
 bool ParseQuoteWord(const char *&String,string &Res)
 {
    // Skip leading whitespace
@@ -97,11 +107,17 @@ bool ParseQuoteWord(const char *&String,string &Res)
       return false;
    
    // Jump to the next word
       return false;
    
    // Jump to the next word
-   for (;*C != 0 && *C != ' '; C++)
+   for (;*C != 0 && isspace(*C) == 0; C++)
    {
       if (*C == '"')
       {
    {
       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;
       }
         if (*C == 0)
            return false;
       }
@@ -133,16 +149,16 @@ bool ParseQuoteWord(const char *&String,string &Res)
    Res = Buffer;
    
    // Skip ending white space
    Res = Buffer;
    
    // Skip ending white space
-   for (;*C != 0 && *C == ' '; C++);
+   for (;*C != 0 && isspace(*C) != 0; C++);
    String = C;
    return true;
 }
                                                                        /*}}}*/
 // ParseCWord - Parses a string like a C "" expression                 /*{{{*/
 // ---------------------------------------------------------------------
    String = C;
    return true;
 }
                                                                        /*}}}*/
 // 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. */
    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;
 {
    // 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++ = ' ';
       if (isspace(*C) == 0)
         return false;
       *Buf++ = ' ';
-   }   
+   }
    *Buf = 0;
    Res = Buffer;
    *Buf = 0;
    Res = Buffer;
+   String = C;
    return true;
 }
                                                                        /*}}}*/
    return true;
 }
                                                                        /*}}}*/
@@ -206,7 +223,7 @@ string QuoteString(string Str,const char *Bad)
 string DeQuoteString(string Str)
 {
    string Res;
 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())
       {
    {
       if (*I == '%' && I + 2 < Str.end())
       {
@@ -318,6 +335,13 @@ string SubstVar(string Str,string Subst,string Contents)
    
    return Temp + string(Str,OldPos);
 }
    
    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             /*{{{*/
 // ---------------------------------------------------------------------
                                                                        /*}}}*/
 // URItoFileName - Convert the uri into a unique file name             /*{{{*/
 // ---------------------------------------------------------------------
@@ -326,11 +350,14 @@ string SubstVar(string Str,string Subst,string Contents)
    file name should be unique and never occur again for a different file */
 string URItoFileName(string URI)
 {
    file name should be unique and never occur again for a different file */
 string URItoFileName(string URI)
 {
-   string::const_iterator I = URI.begin() + URI.find(':') + 1;
-   for (; I < URI.end() && *I == '/'; I++);
-
+   // Nuke 'sensitive' items
+   ::URI U(URI);
+   U.User = string();
+   U.Password = string();
+   U.Access = "";
+   
    // "\x00-\x20{}|\\\\^\\[\\]<>\"\x7F-\xFF";
    // "\x00-\x20{}|\\\\^\\[\\]<>\"\x7F-\xFF";
-   URI = QuoteString(string(I,URI.end() - I),"\\|{}[]<>\"^~_=!@#$%^&*");
+   URI = QuoteString(U,"\\|{}[]<>\"^~_=!@#$%^&*");
    string::iterator J = URI.begin();
    for (; J != URI.end(); J++)
       if (*J == '/') 
    string::iterator J = URI.begin();
    for (; J != URI.end(); J++)
       if (*J == '/') 
@@ -415,6 +442,43 @@ int stringcmp(const char *A,const char *AEnd,const char *B,const char *BEnd)
       return -1;
    return 1;
 }
       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            /*{{{*/
 // ---------------------------------------------------------------------
                                                                        /*}}}*/
 // stringcasecmp - Arbitary case insensitive string compare            /*{{{*/
 // ---------------------------------------------------------------------
@@ -435,6 +499,42 @@ int stringcasecmp(const char *A,const char *AEnd,const char *B,const char *BEnd)
       return -1;
    return 1;
 }
       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             /*{{{*/
 // ---------------------------------------------------------------------
                                                                        /*}}}*/
 // LookupTag - Lookup the value of a tag in a taged string             /*{{{*/
 // ---------------------------------------------------------------------
@@ -456,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--);
         
         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++);
       }
       
       for (; *I != '\n' && I < Message.end(); I++);
@@ -472,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. */
 // ---------------------------------------------------------------------
 /* 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);   
 {
    char *End;
    int Res = strtol(Text.c_str(),&End,0);   
@@ -524,7 +624,7 @@ string TimeRFC1123(time_t Date)
    fancy buffering is used. */
 bool ReadMessages(int Fd, vector<string> &List)
 {
    fancy buffering is used. */
 bool ReadMessages(int Fd, vector<string> &List)
 {
-   char Buffer[4000];
+   char Buffer[64000];
    char *End = Buffer;
    
    while (1)
    char *End = Buffer;
    
    while (1)
@@ -538,9 +638,11 @@ bool ReadMessages(int Fd, vector<string> &List)
         return false;
       
       // No data
         return false;
       
       // No data
-      if (Res <= 0)
+      if (Res < 0 && errno == EAGAIN)
         return true;
         return true;
-
+      if (Res < 0)
+        return false;
+                             
       End += Res;
       
       // Look for the end of the message
       End += Res;
       
       // Look for the end of the message
@@ -550,7 +652,7 @@ bool ReadMessages(int Fd, vector<string> &List)
            continue;
         
         // Pull the message out
            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++);
 
         // Fix up the buffer
         for (; I < End && *I == '\n'; I++);
@@ -612,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. */
    
    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;
 static time_t timegm(struct tm *t)
 {
    time_t tl, tb;
@@ -630,7 +736,8 @@ static time_t timegm(struct tm *t)
 /* This handles all 3 populare time formats including RFC 1123, RFC 1036
    and the C library asctime format. It requires the GNU library function
    'timegm' to convert a struct tm in UTC to a time_t. For some bizzar
 /* This handles all 3 populare time formats including RFC 1123, RFC 1036
    and the C library asctime format. It requires the GNU library function
    '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 :<*/
+   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)
 {
    struct tm Tm;
 bool StrToTime(string Val,time_t &Result)
 {
    struct tm Tm;
@@ -641,6 +748,7 @@ bool StrToTime(string Val,time_t &Result)
    for (;*I != 0  && *I != ' '; I++);
    
    // Handle RFC 1123 time
    for (;*I != 0  && *I != ' '; I++);
    
    // Handle RFC 1123 time
+   Month[0] = 0;
    if (sscanf(I," %d %3s %d %d:%d:%d GMT",&Tm.tm_mday,Month,&Tm.tm_year,
              &Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec) != 6)
    {
    if (sscanf(I," %d %3s %d %d:%d:%d GMT",&Tm.tm_mday,Month,&Tm.tm_year,
              &Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec) != 6)
    {
@@ -653,12 +761,19 @@ bool StrToTime(string Val,time_t &Result)
         // asctime format
         if (sscanf(I," %3s %d %d:%d:%d %d",Month,&Tm.tm_mday,
                    &Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec,&Tm.tm_year) != 6)
         // asctime format
         if (sscanf(I," %3s %d %d:%d:%d %d",Month,&Tm.tm_mday,
                    &Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec,&Tm.tm_year) != 6)
-           return false;
+        {
+           // 'ftp' time
+           if (sscanf(Val.c_str(),"%4d%2d%2d%2d%2d%2d",&Tm.tm_year,&Tm.tm_mon,
+                      &Tm.tm_mday,&Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec) != 6)
+              return false;
+           Tm.tm_mon--;
+        }       
       }
    }
    
    Tm.tm_isdst = 0;
       }
    }
    
    Tm.tm_isdst = 0;
-   Tm.tm_mon = MonthConv(Month);
+   if (Month[0] != 0)
+      Tm.tm_mon = MonthConv(Month);
    Tm.tm_year -= 1900;
    
    // Convert to local time and then to GMT
    Tm.tm_year -= 1900;
    
    // Convert to local time and then to GMT
@@ -666,6 +781,230 @@ bool StrToTime(string Val,time_t &Result)
    return true;
 }
                                                                        /*}}}*/
    return true;
 }
                                                                        /*}}}*/
+// StrToNum - Convert a fixed length string to a number                        /*{{{*/
+// ---------------------------------------------------------------------
+/* This is used in decoding the crazy fixed length string headers in 
+   tar and ar files. */
+bool StrToNum(const char *Str,unsigned long &Res,unsigned Len,unsigned Base)
+{
+   char S[30];
+   if (Len >= sizeof(S))
+      return false;
+   memcpy(S,Str,Len);
+   S[Len] = 0;
+   
+   // All spaces is a zero
+   Res = 0;
+   unsigned I;
+   for (I = 0; S[I] == ' '; I++);
+   if (S[I] == 0)
+      return true;
+   
+   char *End;
+   Res = strtoul(S,&End,Base);
+   if (End == S)
+      return false;
+   
+   return true;
+}
+                                                                       /*}}}*/
+// HexDigit - Convert a hex character into an integer                  /*{{{*/
+// ---------------------------------------------------------------------
+/* Helper for Hex2Num */
+static int HexDigit(int c)
+{   
+   if (c >= '0' && c <= '9')
+      return c - '0';
+   if (c >= 'a' && c <= 'f')
+      return c - 'a' + 10;
+   if (c >= 'A' && c <= 'F')
+      return c - 'A' + 10;
+   return 0;
+}
+                                                                       /*}}}*/
+// 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)
+{
+   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)
+   {
+      if (isxdigit(*I) == 0 || isxdigit(I[1]) == 0)
+        return false;
+      
+      Num[J] = HexDigit(I[0]) << 4;
+      Num[J] += HexDigit(I[1]);
+   }
+   
+   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                                 /*{{{*/
 // ---------------------------------------------------------------------
 
 // URI::CopyFrom - Copy from an object                                 /*{{{*/
 // ---------------------------------------------------------------------
@@ -674,7 +1013,7 @@ void URI::CopyFrom(string U)
 {
    string::const_iterator I = U.begin();
 
 {
    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;
 
    for (; I < U.end() && *I != ':' ; I++);
    string::const_iterator FirstColon = I;
 
@@ -683,7 +1022,18 @@ void URI::CopyFrom(string U)
    string::const_iterator SingleSlash = I;
    if (I + 3 < U.end() && I[1] == '/' && I[2] == '/')
       SingleSlash += 3;
    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();
 
    if (SingleSlash > U.end())
       SingleSlash = U.end();
 
@@ -695,7 +1045,7 @@ void URI::CopyFrom(string U)
       Path = "/";
 
    // Now we attempt to locate a user:pass@host fragment
       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;
       FirstColon += 3;
    else
       FirstColon += 1;
@@ -705,16 +1055,17 @@ void URI::CopyFrom(string U)
    if (FirstColon > SingleSlash)
       FirstColon = SingleSlash;
    
    if (FirstColon > SingleSlash)
       FirstColon = SingleSlash;
    
-   // Search for the @
-   I = FirstColon;
+   // Find the colon...
+   I = FirstColon + 1;
+   if (I > SingleSlash)
+      I = SingleSlash;
+   for (; I < SingleSlash && *I != ':'; I++);
+   string::const_iterator SecondColon = I;
+   
+   // Search for the @ after the colon
    for (; I < SingleSlash && *I != '@'; I++);
    string::const_iterator At = I;
    
    for (; I < SingleSlash && *I != '@'; I++);
    string::const_iterator At = I;
    
-   // Colon in the @ section
-   I = FirstColon + 1;
-   for (; I < At && *I != ':'; I++);
-   string::const_iterator SecondColon = I;
-      
    // Now write the host and user/pass
    if (At == SingleSlash)
    {
    // Now write the host and user/pass
    if (At == SingleSlash)
    {
@@ -729,10 +1080,39 @@ void URI::CopyFrom(string U)
         Password = string(U,SecondColon - U.begin() + 1,At - SecondColon - 1);
    }   
    
         Password = string(U,SecondColon - U.begin() + 1,At - SecondColon - 1);
    }   
    
-   // Now we parse off a pot number from the hostname
+   // 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(':');
    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());
       return;
    
    Port = atoi(string(Host,Pos+1).c_str());
@@ -744,18 +1124,31 @@ void URI::CopyFrom(string U)
 /* */
 URI::operator string()
 {
 /* */
 URI::operator string()
 {
-   string Res = Access + ':';
+   string Res;
+   
+   if (Access.empty() == false)
+      Res = Access + ':';
+   
    if (Host.empty() == false)
    if (Host.empty() == false)
-   {
-      Res += "//";
+   {    
+      if (Access.empty() == false)
+        Res += "//";
+          
       if (User.empty() == false)
       {
       if (User.empty() == false)
       {
-        Res += "//" + User;
+        Res +=  User;
         if (Password.empty() == false)
            Res += ":" + Password;
         Res += "@";
       }
         if (Password.empty() == false)
            Res += ":" + Password;
         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];
       if (Port != 0)
       {
         char S[30];
@@ -775,3 +1168,16 @@ URI::operator string()
    return Res;
 }
                                                                        /*}}}*/
    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;
+}
+                                                                       /*}}}*/