1 // -*- mode: cpp; mode: fold -*- 
   3 // $Id: strutl.cc,v 1.48 2003/07/18 14:15:11 mdz Exp $ 
   4 /* ###################################################################### 
   6    String Util - Some useful string functions. 
   8    These have been collected from here and there to do all sorts of useful 
   9    things to strings. They are useful in file parsers, URI handlers and 
  10    especially in APT methods.    
  12    This source is placed in the Public Domain, do with it what you will 
  13    It was originally written by Jason Gunthorpe <jgg@gpu.srv.ualberta.ca> 
  15    ##################################################################### */ 
  18 #include <apt-pkg/strutl.h> 
  19 #include <apt-pkg/fileutl.h> 
  20 #include <apt-pkg/error.h> 
  39 // UTF8ToCodeset - Convert some UTF-8 string for some codeset           /*{{{*/ 
  40 // --------------------------------------------------------------------- 
  41 /* This is handy to use before display some information for enduser  */ 
  42 bool UTF8ToCodeset(const char *codeset
, const string 
&orig
, string 
*dest
) 
  47   size_t insize
, bufsize
; 
  50   cd 
= iconv_open(codeset
, "UTF-8"); 
  51   if (cd 
== (iconv_t
)(-1)) { 
  52      // Something went wrong 
  54         _error
->Error("conversion from 'UTF-8' to '%s' not available", 
  62   insize 
= bufsize 
= orig
.size(); 
  64   inptr 
= (char *)inbuf
; 
  65   outbuf 
= new char[bufsize
]; 
  66   size_t lastError 
= -1; 
  70      char *outptr 
= outbuf
; 
  71      size_t outsize 
= bufsize
; 
  72      size_t const err 
= iconv(cd
, &inptr
, &insize
, &outptr
, &outsize
); 
  73      dest
->append(outbuf
, outptr 
- outbuf
); 
  74      if (err 
== (size_t)(-1)) 
  81            // replace a series of unknown multibytes with a single "?" 
  82            if (lastError 
!= insize
) { 
  83               lastError 
= insize 
- 1; 
  95               outbuf 
= new char[bufsize
]; 
 109 // strstrip - Remove white space from the front and back of a string    /*{{{*/ 
 110 // --------------------------------------------------------------------- 
 111 /* This is handy to use when parsing a file. It also removes \n's left  
 112    over from fgets and company */ 
 113 char *_strstrip(char *String
) 
 115    for (;*String 
!= 0 && (*String 
== ' ' || *String 
== '\t'); String
++); 
 120    char *End 
= String 
+ strlen(String
) - 1; 
 121    for (;End 
!= String 
- 1 && (*End 
== ' ' || *End 
== '\t' || *End 
== '\n' || 
 122                                *End 
== '\r'); End
--); 
 128 // strtabexpand - Converts tabs into 8 spaces                           /*{{{*/ 
 129 // --------------------------------------------------------------------- 
 131 char *_strtabexpand(char *String
,size_t Len
) 
 133    for (char *I 
= String
; I 
!= I 
+ Len 
&& *I 
!= 0; I
++) 
 137       if (I 
+ 8 > String 
+ Len
) 
 143       /* Assume the start of the string is 0 and find the next 8 char 
 149          Len 
= 8 - ((String 
- I
) % 8); 
 157       memmove(I 
+ Len
,I 
+ 1,strlen(I
) + 1); 
 158       for (char *J 
= I
; J 
+ Len 
!= I
; *I 
= ' ', I
++); 
 163 // ParseQuoteWord - Parse a single word out of a string                 /*{{{*/ 
 164 // --------------------------------------------------------------------- 
 165 /* This grabs a single word, converts any % escaped characters to their 
 166    proper values and advances the pointer. Double quotes are understood 
 167    and striped out as well. This is for URI/URL parsing. It also can  
 168    understand [] brackets.*/ 
 169 bool ParseQuoteWord(const char *&String
,string 
&Res
) 
 171    // Skip leading whitespace 
 172    const char *C 
= String
; 
 173    for (;*C 
!= 0 && *C 
== ' '; C
++); 
 177    // Jump to the next word 
 178    for (;*C 
!= 0 && isspace(*C
) == 0; C
++) 
 182          for (C
++; *C 
!= 0 && *C 
!= '"'; C
++); 
 188          for (C
++; *C 
!= 0 && *C 
!= ']'; C
++); 
 194    // Now de-quote characters 
 197    const char *Start 
= String
; 
 199    for (I 
= Buffer
; I 
< Buffer 
+ sizeof(Buffer
) && Start 
!= C
; I
++) 
 201       if (*Start 
== '%' && Start 
+ 2 < C 
&& 
 202           isxdigit(Start
[1]) && isxdigit(Start
[2])) 
 207          *I 
= (char)strtol(Tmp
,0,16); 
 220    // Skip ending white space 
 221    for (;*C 
!= 0 && isspace(*C
) != 0; C
++); 
 226 // ParseCWord - Parses a string like a C "" expression                  /*{{{*/ 
 227 // --------------------------------------------------------------------- 
 228 /* This expects a series of space separated strings enclosed in ""'s.  
 229    It concatenates the ""'s into a single string. */ 
 230 bool ParseCWord(const char *&String
,string 
&Res
) 
 232    // Skip leading whitespace 
 233    const char *C 
= String
; 
 234    for (;*C 
!= 0 && *C 
== ' '; C
++); 
 240    if (strlen(String
) >= sizeof(Buffer
)) 
 247          for (C
++; *C 
!= 0 && *C 
!= '"'; C
++) 
 256       if (C 
!= String 
&& isspace(*C
) != 0 && isspace(C
[-1]) != 0) 
 258       if (isspace(*C
) == 0) 
 268 // QuoteString - Convert a string into quoted from                      /*{{{*/ 
 269 // --------------------------------------------------------------------- 
 271 string 
QuoteString(const string 
&Str
, const char *Bad
) 
 274    for (string::const_iterator I 
= Str
.begin(); I 
!= Str
.end(); I
++) 
 276       if (strchr(Bad
,*I
) != 0 || isprint(*I
) == 0 ||  
 277           *I 
== 0x25 || // percent '%' char 
 278           *I 
<= 0x20 || *I 
>= 0x7F) // control chars 
 281          sprintf(Buf
,"%%%02x",(int)*I
); 
 290 // DeQuoteString - Convert a string from quoted from                    /*{{{*/ 
 291 // --------------------------------------------------------------------- 
 292 /* This undoes QuoteString */ 
 293 string 
DeQuoteString(const string 
&Str
) 
 295    return DeQuoteString(Str
.begin(),Str
.end()); 
 297 string 
DeQuoteString(string::const_iterator 
const &begin
, 
 298                         string::const_iterator 
const &end
) 
 301    for (string::const_iterator I 
= begin
; I 
!= end
; I
++) 
 303       if (*I 
== '%' && I 
+ 2 < end 
&& 
 304           isxdigit(I
[1]) && isxdigit(I
[2])) 
 310          Res 
+= (char)strtol(Tmp
,0,16); 
 321 // SizeToStr - Convert a long into a human readable size                /*{{{*/ 
 322 // --------------------------------------------------------------------- 
 323 /* A max of 4 digits are shown before conversion to the next highest unit.  
 324    The max length of the string will be 5 chars unless the size is > 10 
 326 string 
SizeToStr(double Size
) 
 335    /* bytes, KiloBytes, MegaBytes, GigaBytes, TeraBytes, PetaBytes,  
 336       ExaBytes, ZettaBytes, YottaBytes */ 
 337    char Ext
[] = {'\0','k','M','G','T','P','E','Z','Y'}; 
 341       if (ASize 
< 100 && I 
!= 0) 
 343          sprintf(S
,"%'.1f %c",ASize
,Ext
[I
]); 
 349          sprintf(S
,"%'.0f %c",ASize
,Ext
[I
]); 
 359 // TimeToStr - Convert the time into a string                           /*{{{*/ 
 360 // --------------------------------------------------------------------- 
 361 /* Converts a number of seconds to a hms format */ 
 362 string 
TimeToStr(unsigned long Sec
) 
 370          //d means days, h means hours, min means minutes, s means seconds 
 371          sprintf(S
,_("%lid %lih %limin %lis"),Sec
/60/60/24,(Sec
/60/60) % 24,(Sec
/60) % 60,Sec 
% 60); 
 377          //h means hours, min means minutes, s means seconds 
 378          sprintf(S
,_("%lih %limin %lis"),Sec
/60/60,(Sec
/60) % 60,Sec 
% 60); 
 384          //min means minutes, s means seconds 
 385          sprintf(S
,_("%limin %lis"),Sec
/60,Sec 
% 60); 
 390       sprintf(S
,_("%lis"),Sec
); 
 397 // SubstVar - Substitute a string for another string                    /*{{{*/ 
 398 // --------------------------------------------------------------------- 
 399 /* This replaces all occurances of Subst with Contents in Str. */ 
 400 string 
SubstVar(const string 
&Str
,const string 
&Subst
,const string 
&Contents
) 
 402    string::size_type Pos 
= 0; 
 403    string::size_type OldPos 
= 0; 
 406    while (OldPos 
< Str
.length() &&  
 407           (Pos 
= Str
.find(Subst
,OldPos
)) != string::npos
) 
 409       Temp 
+= string(Str
,OldPos
,Pos
) + Contents
; 
 410       OldPos 
= Pos 
+ Subst
.length();       
 416    return Temp 
+ string(Str
,OldPos
); 
 419 string 
SubstVar(string Str
,const struct SubstVar 
*Vars
) 
 421    for (; Vars
->Subst 
!= 0; Vars
++) 
 422       Str 
= SubstVar(Str
,Vars
->Subst
,*Vars
->Contents
); 
 426 // OutputInDepth - return a string with separator multiplied with depth /*{{{*/ 
 427 // --------------------------------------------------------------------- 
 428 /* Returns a string with the supplied separator depth + 1 times in it */ 
 429 std::string 
OutputInDepth(const unsigned long Depth
, const char* Separator
) 
 431    std::string output 
= ""; 
 432    for(unsigned long d
=Depth
+1; d 
> 0; d
--) 
 433       output
.append(Separator
); 
 437 // URItoFileName - Convert the uri into a unique file name              /*{{{*/ 
 438 // --------------------------------------------------------------------- 
 439 /* This converts a URI into a safe filename. It quotes all unsafe characters 
 440    and converts / to _ and removes the scheme identifier. The resulting 
 441    file name should be unique and never occur again for a different file */ 
 442 string 
URItoFileName(const string 
&URI
) 
 444    // Nuke 'sensitive' items 
 450    // "\x00-\x20{}|\\\\^\\[\\]<>\"\x7F-\xFF"; 
 451    string NewURI 
= QuoteString(U
,"\\|{}[]<>\"^~_=!@#$%^&*"); 
 452    replace(NewURI
.begin(),NewURI
.end(),'/','_'); 
 456 // Base64Encode - Base64 Encoding routine for short strings             /*{{{*/ 
 457 // --------------------------------------------------------------------- 
 458 /* This routine performs a base64 transformation on a string. It was ripped 
 459    from wget and then patched and bug fixed. 
 461    This spec can be found in rfc2045 */ 
 462 string 
Base64Encode(const string 
&S
) 
 465    static char tbl
[64] = {'A','B','C','D','E','F','G','H', 
 466                           'I','J','K','L','M','N','O','P', 
 467                           'Q','R','S','T','U','V','W','X', 
 468                           'Y','Z','a','b','c','d','e','f', 
 469                           'g','h','i','j','k','l','m','n', 
 470                           'o','p','q','r','s','t','u','v', 
 471                           'w','x','y','z','0','1','2','3', 
 472                           '4','5','6','7','8','9','+','/'}; 
 474    // Pre-allocate some space 
 476    Final
.reserve((4*S
.length() + 2)/3 + 2); 
 478    /* Transform the 3x8 bits to 4x6 bits, as required by 
 480    for (string::const_iterator I 
= S
.begin(); I 
< S
.end(); I 
+= 3) 
 482       char Bits
[3] = {0,0,0}; 
 489       Final 
+= tbl
[Bits
[0] >> 2]; 
 490       Final 
+= tbl
[((Bits
[0] & 3) << 4) + (Bits
[1] >> 4)]; 
 492       if (I 
+ 1 >= S
.end()) 
 495       Final 
+= tbl
[((Bits
[1] & 0xf) << 2) + (Bits
[2] >> 6)]; 
 497       if (I 
+ 2 >= S
.end()) 
 500       Final 
+= tbl
[Bits
[2] & 0x3f]; 
 503    /* Apply the padding elements, this tells how many bytes the remote 
 504       end should discard */ 
 505    if (S
.length() % 3 == 2) 
 507    if (S
.length() % 3 == 1) 
 513 // stringcmp - Arbitrary string compare                                 /*{{{*/ 
 514 // --------------------------------------------------------------------- 
 515 /* This safely compares two non-null terminated strings of arbitrary  
 517 int stringcmp(const char *A
,const char *AEnd
,const char *B
,const char *BEnd
) 
 519    for (; A 
!= AEnd 
&& B 
!= BEnd
; A
++, B
++) 
 523    if (A 
== AEnd 
&& B 
== BEnd
) 
 535 int stringcmp(string::const_iterator A
,string::const_iterator AEnd
, 
 536               const char *B
,const char *BEnd
) 
 538    for (; A 
!= AEnd 
&& B 
!= BEnd
; A
++, B
++) 
 542    if (A 
== AEnd 
&& B 
== BEnd
) 
 552 int stringcmp(string::const_iterator A
,string::const_iterator AEnd
, 
 553               string::const_iterator B
,string::const_iterator BEnd
) 
 555    for (; A 
!= AEnd 
&& B 
!= BEnd
; A
++, B
++) 
 559    if (A 
== AEnd 
&& B 
== BEnd
) 
 571 // stringcasecmp - Arbitrary case insensitive string compare            /*{{{*/ 
 572 // --------------------------------------------------------------------- 
 574 int stringcasecmp(const char *A
,const char *AEnd
,const char *B
,const char *BEnd
) 
 576    for (; A 
!= AEnd 
&& B 
!= BEnd
; A
++, B
++) 
 577       if (tolower_ascii(*A
) != tolower_ascii(*B
)) 
 580    if (A 
== AEnd 
&& B 
== BEnd
) 
 586    if (tolower_ascii(*A
) < tolower_ascii(*B
)) 
 591 int stringcasecmp(string::const_iterator A
,string::const_iterator AEnd
, 
 592                   const char *B
,const char *BEnd
) 
 594    for (; A 
!= AEnd 
&& B 
!= BEnd
; A
++, B
++) 
 595       if (tolower_ascii(*A
) != tolower_ascii(*B
)) 
 598    if (A 
== AEnd 
&& B 
== BEnd
) 
 604    if (tolower_ascii(*A
) < tolower_ascii(*B
)) 
 608 int stringcasecmp(string::const_iterator A
,string::const_iterator AEnd
, 
 609                   string::const_iterator B
,string::const_iterator BEnd
) 
 611    for (; A 
!= AEnd 
&& B 
!= BEnd
; A
++, B
++) 
 612       if (tolower_ascii(*A
) != tolower_ascii(*B
)) 
 615    if (A 
== AEnd 
&& B 
== BEnd
) 
 621    if (tolower_ascii(*A
) < tolower_ascii(*B
)) 
 627 // LookupTag - Lookup the value of a tag in a taged string              /*{{{*/ 
 628 // --------------------------------------------------------------------- 
 629 /* The format is like those used in package files and the method  
 630    communication system */ 
 631 string 
LookupTag(const string 
&Message
,const char *Tag
,const char *Default
) 
 633    // Look for a matching tag. 
 634    int Length 
= strlen(Tag
); 
 635    for (string::const_iterator I 
= Message
.begin(); I 
+ Length 
< Message
.end(); I
++) 
 638       if (I
[Length
] == ':' && stringcasecmp(I
,I
+Length
,Tag
) == 0) 
 640          // Find the end of line and strip the leading/trailing spaces 
 641          string::const_iterator J
; 
 643          for (; isspace(*I
) != 0 && I 
< Message
.end(); I
++); 
 644          for (J 
= I
; *J 
!= '\n' && J 
< Message
.end(); J
++); 
 645          for (; J 
> I 
&& isspace(J
[-1]) != 0; J
--); 
 650       for (; *I 
!= '\n' && I 
< Message
.end(); I
++); 
 653    // Failed to find a match 
 659 // StringToBool - Converts a string into a boolean                      /*{{{*/ 
 660 // --------------------------------------------------------------------- 
 661 /* This inspects the string to see if it is true or if it is false and 
 662    then returns the result. Several varients on true/false are checked. */ 
 663 int StringToBool(const string 
&Text
,int Default
) 
 666    int Res 
= strtol(Text
.c_str(),&End
,0);    
 667    if (End 
!= Text
.c_str() && Res 
>= 0 && Res 
<= 1) 
 670    // Check for positives 
 671    if (strcasecmp(Text
.c_str(),"no") == 0 || 
 672        strcasecmp(Text
.c_str(),"false") == 0 || 
 673        strcasecmp(Text
.c_str(),"without") == 0 || 
 674        strcasecmp(Text
.c_str(),"off") == 0 || 
 675        strcasecmp(Text
.c_str(),"disable") == 0) 
 678    // Check for negatives 
 679    if (strcasecmp(Text
.c_str(),"yes") == 0 || 
 680        strcasecmp(Text
.c_str(),"true") == 0 || 
 681        strcasecmp(Text
.c_str(),"with") == 0 || 
 682        strcasecmp(Text
.c_str(),"on") == 0 || 
 683        strcasecmp(Text
.c_str(),"enable") == 0) 
 689 // TimeRFC1123 - Convert a time_t into RFC1123 format                   /*{{{*/ 
 690 // --------------------------------------------------------------------- 
 691 /* This converts a time_t into a string time representation that is 
 692    year 2000 complient and timezone neutral */ 
 693 string 
TimeRFC1123(time_t Date
) 
 696    if (gmtime_r(&Date
, &Conv
) == NULL
) 
 700    const char *Day
[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"}; 
 701    const char *Month
[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul", 
 702                           "Aug","Sep","Oct","Nov","Dec"}; 
 704    snprintf(Buf
, sizeof(Buf
), "%s, %02i %s %i %02i:%02i:%02i GMT",Day
[Conv
.tm_wday
], 
 705            Conv
.tm_mday
,Month
[Conv
.tm_mon
],Conv
.tm_year
+1900,Conv
.tm_hour
, 
 706            Conv
.tm_min
,Conv
.tm_sec
); 
 710 // ReadMessages - Read messages from the FD                             /*{{{*/ 
 711 // --------------------------------------------------------------------- 
 712 /* This pulls full messages from the input FD into the message buffer.  
 713    It assumes that messages will not pause during transit so no 
 714    fancy buffering is used. 
 716    In particular: this reads blocks from the input until it believes 
 717    that it's run out of input text.  Each block is terminated by a 
 718    double newline ('\n' followed by '\n').  As noted below, there is a 
 719    bug in this code: it assumes that all the blocks have been read if 
 720    it doesn't see additional text in the buffer after the last one is 
 721    parsed, which will cause it to lose blocks if the last block 
 722    coincides with the end of the buffer. 
 724 bool ReadMessages(int Fd
, vector
<string
> &List
) 
 728    // Represents any left-over from the previous iteration of the 
 729    // parse loop.  (i.e., if a message is split across the end 
 730    // of the buffer, it goes here) 
 731    string PartialMessage
; 
 735       int Res 
= read(Fd
,End
,sizeof(Buffer
) - (End
-Buffer
)); 
 736       if (Res 
< 0 && errno 
== EINTR
) 
 739       // Process is dead, this is kind of bad.. 
 744       if (Res 
< 0 && errno 
== EAGAIN
) 
 751       // Look for the end of the message 
 752       for (char *I 
= Buffer
; I 
+ 1 < End
; I
++) 
 754          if (I
[0] != '\n' || I
[1] != '\n') 
 757          // Pull the message out 
 758          string 
Message(Buffer
,I
-Buffer
); 
 759          PartialMessage 
+= Message
; 
 762          for (; I 
< End 
&& *I 
== '\n'; I
++); 
 764          memmove(Buffer
,I
,End
-Buffer
); 
 767          List
.push_back(PartialMessage
); 
 768          PartialMessage
.clear(); 
 772           // If there's text left in the buffer, store it 
 773           // in PartialMessage and throw the rest of the buffer 
 774           // away.  This allows us to handle messages that 
 775           // are longer than the static buffer size. 
 776           PartialMessage 
+= string(Buffer
, End
); 
 781           // BUG ALERT: if a message block happens to end at a 
 782           // multiple of 64000 characters, this will cause it to 
 783           // terminate early, leading to a badly formed block and 
 784           // probably crashing the method.  However, this is the only 
 785           // way we have to find the end of the message block.  I have 
 786           // an idea of how to fix this, but it will require changes 
 787           // to the protocol (essentially to mark the beginning and 
 788           // end of the block). 
 790           //  -- dburrows 2008-04-02 
 794       if (WaitFd(Fd
) == false) 
 799 // MonthConv - Converts a month string into a number                    /*{{{*/ 
 800 // --------------------------------------------------------------------- 
 801 /* This was lifted from the boa webserver which lifted it from 'wn-v1.07' 
 802    Made it a bit more robust with a few tolower_ascii though. */ 
 803 static int MonthConv(char *Month
) 
 805    switch (tolower_ascii(*Month
))  
 808       return tolower_ascii(Month
[1]) == 'p'?3:7; 
 814       if (tolower_ascii(Month
[1]) == 'a') 
 816       return tolower_ascii(Month
[2]) == 'n'?5:6; 
 818       return tolower_ascii(Month
[2]) == 'r'?2:4; 
 826       // Pretend it is January.. 
 832 // timegm - Internal timegm if the gnu version is not available         /*{{{*/ 
 833 // --------------------------------------------------------------------- 
 834 /* Converts struct tm to time_t, assuming the data in tm is UTC rather 
 835    than local timezone (mktime assumes the latter). 
 837    This function is a nonstandard GNU extension that is also present on 
 838    the BSDs and maybe other systems. For others we follow the advice of 
 839    the manpage of timegm and use his portable replacement. */ 
 841 static time_t timegm(struct tm 
*t
) 
 843    char *tz 
= getenv("TZ"); 
 846    time_t ret 
= mktime(t
); 
 856 // FullDateToTime - Converts a HTTP1.1 full date strings into a time_t  /*{{{*/ 
 857 // --------------------------------------------------------------------- 
 858 /* tries to parses a full date as specified in RFC2616 Section 3.3.1 
 859    with one exception: All timezones (%Z) are accepted but the protocol 
 860    says that it MUST be GMT, but this one is equal to UTC which we will 
 861    encounter from time to time (e.g. in Release files) so we accept all 
 862    here and just assume it is GMT (or UTC) later on */ 
 863 bool RFC1123StrToTime(const char* const str
,time_t &time
) 
 866    setlocale (LC_ALL
,"C"); 
 868    // Sun, 06 Nov 1994 08:49:37 GMT  ; RFC 822, updated by RFC 1123 
 869       (strptime(str
, "%a, %d %b %Y %H:%M:%S %Z", &Tm
) == NULL 
&& 
 870    // Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036 
 871        strptime(str
, "%A, %d-%b-%y %H:%M:%S %Z", &Tm
) == NULL 
&& 
 872    // Sun Nov  6 08:49:37 1994       ; ANSI C's asctime() format 
 873        strptime(str
, "%a %b %d %H:%M:%S %Y", &Tm
) == NULL
); 
 874    setlocale (LC_ALL
,""); 
 882 // FTPMDTMStrToTime - Converts a ftp modification date into a time_t    /*{{{*/ 
 883 // --------------------------------------------------------------------- 
 885 bool FTPMDTMStrToTime(const char* const str
,time_t &time
) 
 888    // MDTM includes no whitespaces but recommend and ignored by strptime 
 889    if (strptime(str
, "%Y %m %d %H %M %S", &Tm
) == NULL
) 
 896 // StrToTime - Converts a string into a time_t                          /*{{{*/ 
 897 // --------------------------------------------------------------------- 
 898 /* This handles all 3 populare time formats including RFC 1123, RFC 1036 
 899    and the C library asctime format. It requires the GNU library function 
 900    'timegm' to convert a struct tm in UTC to a time_t. For some bizzar 
 901    reason the C library does not provide any such function :< This also 
 902    handles the weird, but unambiguous FTP time format*/ 
 903 bool StrToTime(const string 
&Val
,time_t &Result
) 
 907    const char *I 
= Val
.c_str(); 
 909    // Skip the day of the week 
 910    for (;*I 
!= 0  && *I 
!= ' '; I
++); 
 912    // Handle RFC 1123 time 
 914    if (sscanf(I
," %d %3s %d %d:%d:%d GMT",&Tm
.tm_mday
,Month
,&Tm
.tm_year
, 
 915               &Tm
.tm_hour
,&Tm
.tm_min
,&Tm
.tm_sec
) != 6) 
 917       // Handle RFC 1036 time 
 918       if (sscanf(I
," %d-%3s-%d %d:%d:%d GMT",&Tm
.tm_mday
,Month
, 
 919                  &Tm
.tm_year
,&Tm
.tm_hour
,&Tm
.tm_min
,&Tm
.tm_sec
) == 6) 
 924          if (sscanf(I
," %3s %d %d:%d:%d %d",Month
,&Tm
.tm_mday
, 
 925                     &Tm
.tm_hour
,&Tm
.tm_min
,&Tm
.tm_sec
,&Tm
.tm_year
) != 6) 
 928             if (sscanf(Val
.c_str(),"%4d%2d%2d%2d%2d%2d",&Tm
.tm_year
,&Tm
.tm_mon
, 
 929                        &Tm
.tm_mday
,&Tm
.tm_hour
,&Tm
.tm_min
,&Tm
.tm_sec
) != 6) 
 938       Tm
.tm_mon 
= MonthConv(Month
); 
 941    // Convert to local time and then to GMT 
 942    Result 
= timegm(&Tm
); 
 946 // StrToNum - Convert a fixed length string to a number                 /*{{{*/ 
 947 // --------------------------------------------------------------------- 
 948 /* This is used in decoding the crazy fixed length string headers in  
 950 bool StrToNum(const char *Str
,unsigned long &Res
,unsigned Len
,unsigned Base
) 
 953    if (Len 
>= sizeof(S
)) 
 958    // All spaces is a zero 
 961    for (I 
= 0; S
[I
] == ' '; I
++); 
 966    Res 
= strtoul(S
,&End
,Base
); 
 973 // Base256ToNum - Convert a fixed length binary to a number             /*{{{*/ 
 974 // --------------------------------------------------------------------- 
 975 /* This is used in decoding the 256bit encoded fixed length fields in 
 977 bool Base256ToNum(const char *Str
,unsigned long &Res
,unsigned int Len
) 
 979    if ((Str
[0] & 0x80) == 0) 
 984       for(unsigned int i 
= 1; i 
< Len
; ++i
) 
 985          Res 
= (Res
<<8) + Str
[i
]; 
 990 // HexDigit - Convert a hex character into an integer                   /*{{{*/ 
 991 // --------------------------------------------------------------------- 
 992 /* Helper for Hex2Num */ 
 993 static int HexDigit(int c
) 
 995    if (c 
>= '0' && c 
<= '9') 
 997    if (c 
>= 'a' && c 
<= 'f') 
 999    if (c 
>= 'A' && c 
<= 'F') 
1000       return c 
- 'A' + 10; 
1004 // Hex2Num - Convert a long hex number into a buffer                    /*{{{*/ 
1005 // --------------------------------------------------------------------- 
1006 /* The length of the buffer must be exactly 1/2 the length of the string. */ 
1007 bool Hex2Num(const string 
&Str
,unsigned char *Num
,unsigned int Length
) 
1009    if (Str
.length() != Length
*2) 
1012    // Convert each digit. We store it in the same order as the string 
1014    for (string::const_iterator I 
= Str
.begin(); I 
!= Str
.end();J
++, I 
+= 2) 
1016       if (isxdigit(*I
) == 0 || isxdigit(I
[1]) == 0) 
1019       Num
[J
] = HexDigit(I
[0]) << 4; 
1020       Num
[J
] += HexDigit(I
[1]); 
1026 // TokSplitString - Split a string up by a given token                  /*{{{*/ 
1027 // --------------------------------------------------------------------- 
1028 /* This is intended to be a faster splitter, it does not use dynamic 
1029    memories. Input is changed to insert nulls at each token location. */ 
1030 bool TokSplitString(char Tok
,char *Input
,char **List
, 
1031                     unsigned long ListMax
) 
1033    // Strip any leading spaces 
1034    char *Start 
= Input
; 
1035    char *Stop 
= Start 
+ strlen(Start
); 
1036    for (; *Start 
!= 0 && isspace(*Start
) != 0; Start
++); 
1038    unsigned long Count 
= 0; 
1042       // Skip to the next Token 
1043       for (; Pos 
!= Stop 
&& *Pos 
!= Tok
; Pos
++); 
1045       // Back remove spaces 
1047       for (; End 
> Start 
&& (End
[-1] == Tok 
|| isspace(End
[-1]) != 0); End
--); 
1050       List
[Count
++] = Start
; 
1051       if (Count 
>= ListMax
) 
1058       for (; Pos 
!= Stop 
&& (*Pos 
== Tok 
|| isspace(*Pos
) != 0 || *Pos 
== 0); Pos
++); 
1066 // VectorizeString - Split a string up into a vector of strings         /*{{{*/ 
1067 // --------------------------------------------------------------------- 
1068 /* This can be used to split a given string up into a vector, so the 
1069    propose is the same as in the method above and this one is a bit slower 
1070    also, but the advantage is that we have an iteratable vector */ 
1071 vector
<string
> VectorizeString(string 
const &haystack
, char const &split
) 
1073    string::const_iterator start 
= haystack
.begin(); 
1074    string::const_iterator end 
= start
; 
1075    vector
<string
> exploded
; 
1077       for (; end 
!= haystack
.end() && *end 
!= split
; ++end
); 
1078       exploded
.push_back(string(start
, end
)); 
1080    } while (end 
!= haystack
.end() && (++end
) != haystack
.end()); 
1084 // RegexChoice - Simple regex list/list matcher                         /*{{{*/ 
1085 // --------------------------------------------------------------------- 
1087 unsigned long RegexChoice(RxChoiceList 
*Rxs
,const char **ListBegin
, 
1088                       const char **ListEnd
) 
1090    for (RxChoiceList 
*R 
= Rxs
; R
->Str 
!= 0; R
++) 
1093    unsigned long Hits 
= 0; 
1094    for (; ListBegin 
!= ListEnd
; ListBegin
++) 
1096       // Check if the name is a regex 
1099       for (I 
= *ListBegin
; *I 
!= 0; I
++) 
1100          if (*I 
== '.' || *I 
== '?' || *I 
== '*' || *I 
== '|') 
1105       // Compile the regex pattern 
1108          if (regcomp(&Pattern
,*ListBegin
,REG_EXTENDED 
| REG_ICASE 
| 
1114       for (RxChoiceList 
*R 
= Rxs
; R
->Str 
!= 0; R
++) 
1119          if (strcasecmp(R
->Str
,*ListBegin
) != 0) 
1123             if (regexec(&Pattern
,R
->Str
,0,0,0) != 0) 
1128          if (R
->Hit 
== false) 
1138          _error
->Warning(_("Selection %s not found"),*ListBegin
); 
1144 // ioprintf - C format string outputter to C++ iostreams                /*{{{*/ 
1145 // --------------------------------------------------------------------- 
1146 /* This is used to make the internationalization strings easier to translate 
1147    and to allow reordering of parameters */ 
1148 void ioprintf(ostream 
&out
,const char *format
,...)  
1151    va_start(args
,format
); 
1153    // sprintf the description 
1155    vsnprintf(S
,sizeof(S
),format
,args
); 
1159 // strprintf - C format string outputter to C++ strings                 /*{{{*/ 
1160 // --------------------------------------------------------------------- 
1161 /* This is used to make the internationalization strings easier to translate 
1162    and to allow reordering of parameters */ 
1163 void strprintf(string 
&out
,const char *format
,...)  
1166    va_start(args
,format
); 
1168    // sprintf the description 
1170    vsnprintf(S
,sizeof(S
),format
,args
); 
1174 // safe_snprintf - Safer snprintf                                       /*{{{*/ 
1175 // --------------------------------------------------------------------- 
1176 /* This is a snprintf that will never (ever) go past 'End' and returns a 
1177    pointer to the end of the new string. The returned string is always null 
1178    terminated unless Buffer == end. This is a better alterantive to using 
1179    consecutive snprintfs. */ 
1180 char *safe_snprintf(char *Buffer
,char *End
,const char *Format
,...) 
1185    va_start(args
,Format
); 
1190    Did 
= vsnprintf(Buffer
,End 
- Buffer
,Format
,args
); 
1191    if (Did 
< 0 || Buffer 
+ Did 
> End
) 
1193    return Buffer 
+ Did
; 
1196 // StripEpoch - Remove the version "epoch" from a version string        /*{{{*/ 
1197 // --------------------------------------------------------------------- 
1198 string 
StripEpoch(const string 
&VerStr
) 
1200    size_t i 
= VerStr
.find(":"); 
1201    if (i 
== string::npos
) 
1203    return VerStr
.substr(i
+1); 
1206 // tolower_ascii - tolower() function that ignores the locale           /*{{{*/ 
1207 // --------------------------------------------------------------------- 
1208 /* This little function is the most called method we have and tries 
1209    therefore to do the absolut minimum - and is noteable faster than 
1210    standard tolower/toupper and as a bonus avoids problems with different 
1211    locales - we only operate on ascii chars anyway. */ 
1212 int tolower_ascii(int const c
) 
1214    if (c 
>= 'A' && c 
<= 'Z') 
1220 // CheckDomainList - See if Host is in a , seperate list                /*{{{*/ 
1221 // --------------------------------------------------------------------- 
1222 /* The domain list is a comma seperate list of domains that are suffix 
1223    matched against the argument */ 
1224 bool CheckDomainList(const string 
&Host
,const string 
&List
) 
1226    string::const_iterator Start 
= List
.begin(); 
1227    for (string::const_iterator Cur 
= List
.begin(); Cur 
<= List
.end(); Cur
++) 
1229       if (Cur 
< List
.end() && *Cur 
!= ',') 
1232       // Match the end of the string.. 
1233       if ((Host
.size() >= (unsigned)(Cur 
- Start
)) && 
1235           stringcasecmp(Host
.end() - (Cur 
- Start
),Host
.end(),Start
,Cur
) == 0) 
1244 // URI::CopyFrom - Copy from an object                                  /*{{{*/ 
1245 // --------------------------------------------------------------------- 
1246 /* This parses the URI into all of its components */ 
1247 void URI::CopyFrom(const string 
&U
) 
1249    string::const_iterator I 
= U
.begin(); 
1251    // Locate the first colon, this separates the scheme 
1252    for (; I 
< U
.end() && *I 
!= ':' ; I
++); 
1253    string::const_iterator FirstColon 
= I
; 
1255    /* Determine if this is a host type URI with a leading double // 
1256       and then search for the first single / */ 
1257    string::const_iterator SingleSlash 
= I
; 
1258    if (I 
+ 3 < U
.end() && I
[1] == '/' && I
[2] == '/') 
1261    /* Find the / indicating the end of the hostname, ignoring /'s in the 
1263    bool InBracket 
= false; 
1264    for (; SingleSlash 
< U
.end() && (*SingleSlash 
!= '/' || InBracket 
== true); SingleSlash
++) 
1266       if (*SingleSlash 
== '[') 
1268       if (InBracket 
== true && *SingleSlash 
== ']') 
1272    if (SingleSlash 
> U
.end()) 
1273       SingleSlash 
= U
.end(); 
1275    // We can now write the access and path specifiers 
1276    Access
.assign(U
.begin(),FirstColon
); 
1277    if (SingleSlash 
!= U
.end()) 
1278       Path
.assign(SingleSlash
,U
.end()); 
1279    if (Path
.empty() == true) 
1282    // Now we attempt to locate a user:pass@host fragment 
1283    if (FirstColon 
+ 2 <= U
.end() && FirstColon
[1] == '/' && FirstColon
[2] == '/') 
1287    if (FirstColon 
>= U
.end()) 
1290    if (FirstColon 
> SingleSlash
) 
1291       FirstColon 
= SingleSlash
; 
1293    // Find the colon... 
1295    if (I 
> SingleSlash
) 
1297    for (; I 
< SingleSlash 
&& *I 
!= ':'; I
++); 
1298    string::const_iterator SecondColon 
= I
; 
1300    // Search for the @ after the colon 
1301    for (; I 
< SingleSlash 
&& *I 
!= '@'; I
++); 
1302    string::const_iterator At 
= I
; 
1304    // Now write the host and user/pass 
1305    if (At 
== SingleSlash
) 
1307       if (FirstColon 
< SingleSlash
) 
1308          Host
.assign(FirstColon
,SingleSlash
); 
1312       Host
.assign(At
+1,SingleSlash
); 
1313       // username and password must be encoded (RFC 3986) 
1314       User
.assign(DeQuoteString(FirstColon
,SecondColon
)); 
1315       if (SecondColon 
< At
) 
1316          Password
.assign(DeQuoteString(SecondColon
+1,At
)); 
1319    // Now we parse the RFC 2732 [] hostnames. 
1320    unsigned long PortEnd 
= 0; 
1322    for (unsigned I 
= 0; I 
!= Host
.length();) 
1331       if (InBracket 
== true && Host
[I
] == ']') 
1342    if (InBracket 
== true) 
1348    // Now we parse off a port number from the hostname 
1350    string::size_type Pos 
= Host
.rfind(':'); 
1351    if (Pos 
== string::npos 
|| Pos 
< PortEnd
) 
1354    Port 
= atoi(string(Host
,Pos
+1).c_str()); 
1355    Host
.assign(Host
,0,Pos
); 
1358 // URI::operator string - Convert the URI to a string                   /*{{{*/ 
1359 // --------------------------------------------------------------------- 
1361 URI::operator string() 
1365    if (Access
.empty() == false) 
1368    if (Host
.empty() == false) 
1370       if (Access
.empty() == false) 
1373       if (User
.empty() == false) 
1376          if (Password
.empty() == false) 
1377             Res 
+= ":" + Password
; 
1381       // Add RFC 2732 escaping characters 
1382       if (Access
.empty() == false && 
1383           (Host
.find('/') != string::npos 
|| Host
.find(':') != string::npos
)) 
1384          Res 
+= '[' + Host 
+ ']'; 
1391          sprintf(S
,":%u",Port
); 
1396    if (Path
.empty() == false) 
1407 // URI::SiteOnly - Return the schema and site for the URI               /*{{{*/ 
1408 // --------------------------------------------------------------------- 
1410 string 
URI::SiteOnly(const string 
&URI
) 
1420 // URI::NoUserPassword - Return the schema, site and path for the URI   /*{{{*/ 
1421 // --------------------------------------------------------------------- 
1423 string 
URI::NoUserPassword(const string 
&URI
)