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
) 
 695    struct tm Conv 
= *gmtime(&Date
); 
 698    const char *Day
[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"}; 
 699    const char *Month
[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul", 
 700                           "Aug","Sep","Oct","Nov","Dec"}; 
 702    sprintf(Buf
,"%s, %02i %s %i %02i:%02i:%02i GMT",Day
[Conv
.tm_wday
], 
 703            Conv
.tm_mday
,Month
[Conv
.tm_mon
],Conv
.tm_year
+1900,Conv
.tm_hour
, 
 704            Conv
.tm_min
,Conv
.tm_sec
); 
 708 // ReadMessages - Read messages from the FD                             /*{{{*/ 
 709 // --------------------------------------------------------------------- 
 710 /* This pulls full messages from the input FD into the message buffer.  
 711    It assumes that messages will not pause during transit so no 
 712    fancy buffering is used. 
 714    In particular: this reads blocks from the input until it believes 
 715    that it's run out of input text.  Each block is terminated by a 
 716    double newline ('\n' followed by '\n').  As noted below, there is a 
 717    bug in this code: it assumes that all the blocks have been read if 
 718    it doesn't see additional text in the buffer after the last one is 
 719    parsed, which will cause it to lose blocks if the last block 
 720    coincides with the end of the buffer. 
 722 bool ReadMessages(int Fd
, vector
<string
> &List
) 
 726    // Represents any left-over from the previous iteration of the 
 727    // parse loop.  (i.e., if a message is split across the end 
 728    // of the buffer, it goes here) 
 729    string PartialMessage
; 
 733       int Res 
= read(Fd
,End
,sizeof(Buffer
) - (End
-Buffer
)); 
 734       if (Res 
< 0 && errno 
== EINTR
) 
 737       // Process is dead, this is kind of bad.. 
 742       if (Res 
< 0 && errno 
== EAGAIN
) 
 749       // Look for the end of the message 
 750       for (char *I 
= Buffer
; I 
+ 1 < End
; I
++) 
 752          if (I
[0] != '\n' || I
[1] != '\n') 
 755          // Pull the message out 
 756          string 
Message(Buffer
,I
-Buffer
); 
 757          PartialMessage 
+= Message
; 
 760          for (; I 
< End 
&& *I 
== '\n'; I
++); 
 762          memmove(Buffer
,I
,End
-Buffer
); 
 765          List
.push_back(PartialMessage
); 
 766          PartialMessage
.clear(); 
 770           // If there's text left in the buffer, store it 
 771           // in PartialMessage and throw the rest of the buffer 
 772           // away.  This allows us to handle messages that 
 773           // are longer than the static buffer size. 
 774           PartialMessage 
+= string(Buffer
, End
); 
 779           // BUG ALERT: if a message block happens to end at a 
 780           // multiple of 64000 characters, this will cause it to 
 781           // terminate early, leading to a badly formed block and 
 782           // probably crashing the method.  However, this is the only 
 783           // way we have to find the end of the message block.  I have 
 784           // an idea of how to fix this, but it will require changes 
 785           // to the protocol (essentially to mark the beginning and 
 786           // end of the block). 
 788           //  -- dburrows 2008-04-02 
 792       if (WaitFd(Fd
) == false) 
 797 // MonthConv - Converts a month string into a number                    /*{{{*/ 
 798 // --------------------------------------------------------------------- 
 799 /* This was lifted from the boa webserver which lifted it from 'wn-v1.07' 
 800    Made it a bit more robust with a few tolower_ascii though. */ 
 801 static int MonthConv(char *Month
) 
 803    switch (tolower_ascii(*Month
))  
 806       return tolower_ascii(Month
[1]) == 'p'?3:7; 
 812       if (tolower_ascii(Month
[1]) == 'a') 
 814       return tolower_ascii(Month
[2]) == 'n'?5:6; 
 816       return tolower_ascii(Month
[2]) == 'r'?2:4; 
 824       // Pretend it is January.. 
 830 // timegm - Internal timegm if the gnu version is not available         /*{{{*/ 
 831 // --------------------------------------------------------------------- 
 832 /* Converts struct tm to time_t, assuming the data in tm is UTC rather 
 833    than local timezone (mktime assumes the latter). 
 835    This function is a nonstandard GNU extension that is also present on 
 836    the BSDs and maybe other systems. For others we follow the advice of 
 837    the manpage of timegm and use his portable replacement. */ 
 839 static time_t timegm(struct tm 
*t
) 
 841    char *tz 
= getenv("TZ"); 
 844    time_t ret 
= mktime(t
); 
 854 // FullDateToTime - Converts a HTTP1.1 full date strings into a time_t  /*{{{*/ 
 855 // --------------------------------------------------------------------- 
 856 /* tries to parses a full date as specified in RFC2616 Section 3.3.1 
 857    with one exception: All timezones (%Z) are accepted but the protocol 
 858    says that it MUST be GMT, but this one is equal to UTC which we will 
 859    encounter from time to time (e.g. in Release files) so we accept all 
 860    here and just assume it is GMT (or UTC) later on */ 
 861 bool RFC1123StrToTime(const char* const str
,time_t &time
) 
 864    setlocale (LC_ALL
,"C"); 
 866    // Sun, 06 Nov 1994 08:49:37 GMT  ; RFC 822, updated by RFC 1123 
 867       (strptime(str
, "%a, %d %b %Y %H:%M:%S %Z", &Tm
) == NULL 
&& 
 868    // Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036 
 869        strptime(str
, "%A, %d-%b-%y %H:%M:%S %Z", &Tm
) == NULL 
&& 
 870    // Sun Nov  6 08:49:37 1994       ; ANSI C's asctime() format 
 871        strptime(str
, "%a %b %d %H:%M:%S %Y", &Tm
) == NULL
); 
 872    setlocale (LC_ALL
,""); 
 880 // FTPMDTMStrToTime - Converts a ftp modification date into a time_t    /*{{{*/ 
 881 // --------------------------------------------------------------------- 
 883 bool FTPMDTMStrToTime(const char* const str
,time_t &time
) 
 886    // MDTM includes no whitespaces but recommend and ignored by strptime 
 887    if (strptime(str
, "%Y %m %d %H %M %S", &Tm
) == NULL
) 
 894 // StrToTime - Converts a string into a time_t                          /*{{{*/ 
 895 // --------------------------------------------------------------------- 
 896 /* This handles all 3 populare time formats including RFC 1123, RFC 1036 
 897    and the C library asctime format. It requires the GNU library function 
 898    'timegm' to convert a struct tm in UTC to a time_t. For some bizzar 
 899    reason the C library does not provide any such function :< This also 
 900    handles the weird, but unambiguous FTP time format*/ 
 901 bool StrToTime(const string 
&Val
,time_t &Result
) 
 905    const char *I 
= Val
.c_str(); 
 907    // Skip the day of the week 
 908    for (;*I 
!= 0  && *I 
!= ' '; I
++); 
 910    // Handle RFC 1123 time 
 912    if (sscanf(I
," %d %3s %d %d:%d:%d GMT",&Tm
.tm_mday
,Month
,&Tm
.tm_year
, 
 913               &Tm
.tm_hour
,&Tm
.tm_min
,&Tm
.tm_sec
) != 6) 
 915       // Handle RFC 1036 time 
 916       if (sscanf(I
," %d-%3s-%d %d:%d:%d GMT",&Tm
.tm_mday
,Month
, 
 917                  &Tm
.tm_year
,&Tm
.tm_hour
,&Tm
.tm_min
,&Tm
.tm_sec
) == 6) 
 922          if (sscanf(I
," %3s %d %d:%d:%d %d",Month
,&Tm
.tm_mday
, 
 923                     &Tm
.tm_hour
,&Tm
.tm_min
,&Tm
.tm_sec
,&Tm
.tm_year
) != 6) 
 926             if (sscanf(Val
.c_str(),"%4d%2d%2d%2d%2d%2d",&Tm
.tm_year
,&Tm
.tm_mon
, 
 927                        &Tm
.tm_mday
,&Tm
.tm_hour
,&Tm
.tm_min
,&Tm
.tm_sec
) != 6) 
 936       Tm
.tm_mon 
= MonthConv(Month
); 
 939    // Convert to local time and then to GMT 
 940    Result 
= timegm(&Tm
); 
 944 // StrToNum - Convert a fixed length string to a number                 /*{{{*/ 
 945 // --------------------------------------------------------------------- 
 946 /* This is used in decoding the crazy fixed length string headers in  
 948 bool StrToNum(const char *Str
,unsigned long &Res
,unsigned Len
,unsigned Base
) 
 951    if (Len 
>= sizeof(S
)) 
 956    // All spaces is a zero 
 959    for (I 
= 0; S
[I
] == ' '; I
++); 
 964    Res 
= strtoul(S
,&End
,Base
); 
 971 // HexDigit - Convert a hex character into an integer                   /*{{{*/ 
 972 // --------------------------------------------------------------------- 
 973 /* Helper for Hex2Num */ 
 974 static int HexDigit(int c
) 
 976    if (c 
>= '0' && c 
<= '9') 
 978    if (c 
>= 'a' && c 
<= 'f') 
 980    if (c 
>= 'A' && c 
<= 'F') 
 985 // Hex2Num - Convert a long hex number into a buffer                    /*{{{*/ 
 986 // --------------------------------------------------------------------- 
 987 /* The length of the buffer must be exactly 1/2 the length of the string. */ 
 988 bool Hex2Num(const string 
&Str
,unsigned char *Num
,unsigned int Length
) 
 990    if (Str
.length() != Length
*2) 
 993    // Convert each digit. We store it in the same order as the string 
 995    for (string::const_iterator I 
= Str
.begin(); I 
!= Str
.end();J
++, I 
+= 2) 
 997       if (isxdigit(*I
) == 0 || isxdigit(I
[1]) == 0) 
1000       Num
[J
] = HexDigit(I
[0]) << 4; 
1001       Num
[J
] += HexDigit(I
[1]); 
1007 // TokSplitString - Split a string up by a given token                  /*{{{*/ 
1008 // --------------------------------------------------------------------- 
1009 /* This is intended to be a faster splitter, it does not use dynamic 
1010    memories. Input is changed to insert nulls at each token location. */ 
1011 bool TokSplitString(char Tok
,char *Input
,char **List
, 
1012                     unsigned long ListMax
) 
1014    // Strip any leading spaces 
1015    char *Start 
= Input
; 
1016    char *Stop 
= Start 
+ strlen(Start
); 
1017    for (; *Start 
!= 0 && isspace(*Start
) != 0; Start
++); 
1019    unsigned long Count 
= 0; 
1023       // Skip to the next Token 
1024       for (; Pos 
!= Stop 
&& *Pos 
!= Tok
; Pos
++); 
1026       // Back remove spaces 
1028       for (; End 
> Start 
&& (End
[-1] == Tok 
|| isspace(End
[-1]) != 0); End
--); 
1031       List
[Count
++] = Start
; 
1032       if (Count 
>= ListMax
) 
1039       for (; Pos 
!= Stop 
&& (*Pos 
== Tok 
|| isspace(*Pos
) != 0 || *Pos 
== 0); Pos
++); 
1047 // VectorizeString - Split a string up into a vector of strings         /*{{{*/ 
1048 // --------------------------------------------------------------------- 
1049 /* This can be used to split a given string up into a vector, so the 
1050    propose is the same as in the method above and this one is a bit slower 
1051    also, but the advantage is that we have an iteratable vector */ 
1052 vector
<string
> VectorizeString(string 
const &haystack
, char const &split
) 
1054    string::const_iterator start 
= haystack
.begin(); 
1055    string::const_iterator end 
= start
; 
1056    vector
<string
> exploded
; 
1058       for (; end 
!= haystack
.end() && *end 
!= split
; ++end
); 
1059       exploded
.push_back(string(start
, end
)); 
1061    } while (end 
!= haystack
.end() && (++end
) != haystack
.end()); 
1065 // RegexChoice - Simple regex list/list matcher                         /*{{{*/ 
1066 // --------------------------------------------------------------------- 
1068 unsigned long RegexChoice(RxChoiceList 
*Rxs
,const char **ListBegin
, 
1069                       const char **ListEnd
) 
1071    for (RxChoiceList 
*R 
= Rxs
; R
->Str 
!= 0; R
++) 
1074    unsigned long Hits 
= 0; 
1075    for (; ListBegin 
!= ListEnd
; ListBegin
++) 
1077       // Check if the name is a regex 
1080       for (I 
= *ListBegin
; *I 
!= 0; I
++) 
1081          if (*I 
== '.' || *I 
== '?' || *I 
== '*' || *I 
== '|') 
1086       // Compile the regex pattern 
1089          if (regcomp(&Pattern
,*ListBegin
,REG_EXTENDED 
| REG_ICASE 
| 
1095       for (RxChoiceList 
*R 
= Rxs
; R
->Str 
!= 0; R
++) 
1100          if (strcasecmp(R
->Str
,*ListBegin
) != 0) 
1104             if (regexec(&Pattern
,R
->Str
,0,0,0) != 0) 
1109          if (R
->Hit 
== false) 
1119          _error
->Warning(_("Selection %s not found"),*ListBegin
); 
1125 // ioprintf - C format string outputter to C++ iostreams                /*{{{*/ 
1126 // --------------------------------------------------------------------- 
1127 /* This is used to make the internationalization strings easier to translate 
1128    and to allow reordering of parameters */ 
1129 void ioprintf(ostream 
&out
,const char *format
,...)  
1132    va_start(args
,format
); 
1134    // sprintf the description 
1136    vsnprintf(S
,sizeof(S
),format
,args
); 
1140 // strprintf - C format string outputter to C++ strings                 /*{{{*/ 
1141 // --------------------------------------------------------------------- 
1142 /* This is used to make the internationalization strings easier to translate 
1143    and to allow reordering of parameters */ 
1144 void strprintf(string 
&out
,const char *format
,...)  
1147    va_start(args
,format
); 
1149    // sprintf the description 
1151    vsnprintf(S
,sizeof(S
),format
,args
); 
1155 // safe_snprintf - Safer snprintf                                       /*{{{*/ 
1156 // --------------------------------------------------------------------- 
1157 /* This is a snprintf that will never (ever) go past 'End' and returns a 
1158    pointer to the end of the new string. The returned string is always null 
1159    terminated unless Buffer == end. This is a better alterantive to using 
1160    consecutive snprintfs. */ 
1161 char *safe_snprintf(char *Buffer
,char *End
,const char *Format
,...) 
1166    va_start(args
,Format
); 
1171    Did 
= vsnprintf(Buffer
,End 
- Buffer
,Format
,args
); 
1172    if (Did 
< 0 || Buffer 
+ Did 
> End
) 
1174    return Buffer 
+ Did
; 
1178 // tolower_ascii - tolower() function that ignores the locale           /*{{{*/ 
1179 // --------------------------------------------------------------------- 
1180 /* This little function is the most called method we have and tries 
1181    therefore to do the absolut minimum - and is noteable faster than 
1182    standard tolower/toupper and as a bonus avoids problems with different 
1183    locales - we only operate on ascii chars anyway. */ 
1184 int tolower_ascii(int const c
) 
1186    if (c 
>= 'A' && c 
<= 'Z') 
1192 // CheckDomainList - See if Host is in a , seperate list                /*{{{*/ 
1193 // --------------------------------------------------------------------- 
1194 /* The domain list is a comma seperate list of domains that are suffix 
1195    matched against the argument */ 
1196 bool CheckDomainList(const string 
&Host
,const string 
&List
) 
1198    string::const_iterator Start 
= List
.begin(); 
1199    for (string::const_iterator Cur 
= List
.begin(); Cur 
<= List
.end(); Cur
++) 
1201       if (Cur 
< List
.end() && *Cur 
!= ',') 
1204       // Match the end of the string.. 
1205       if ((Host
.size() >= (unsigned)(Cur 
- Start
)) && 
1207           stringcasecmp(Host
.end() - (Cur 
- Start
),Host
.end(),Start
,Cur
) == 0) 
1216 // URI::CopyFrom - Copy from an object                                  /*{{{*/ 
1217 // --------------------------------------------------------------------- 
1218 /* This parses the URI into all of its components */ 
1219 void URI::CopyFrom(const string 
&U
) 
1221    string::const_iterator I 
= U
.begin(); 
1223    // Locate the first colon, this separates the scheme 
1224    for (; I 
< U
.end() && *I 
!= ':' ; I
++); 
1225    string::const_iterator FirstColon 
= I
; 
1227    /* Determine if this is a host type URI with a leading double // 
1228       and then search for the first single / */ 
1229    string::const_iterator SingleSlash 
= I
; 
1230    if (I 
+ 3 < U
.end() && I
[1] == '/' && I
[2] == '/') 
1233    /* Find the / indicating the end of the hostname, ignoring /'s in the 
1235    bool InBracket 
= false; 
1236    for (; SingleSlash 
< U
.end() && (*SingleSlash 
!= '/' || InBracket 
== true); SingleSlash
++) 
1238       if (*SingleSlash 
== '[') 
1240       if (InBracket 
== true && *SingleSlash 
== ']') 
1244    if (SingleSlash 
> U
.end()) 
1245       SingleSlash 
= U
.end(); 
1247    // We can now write the access and path specifiers 
1248    Access
.assign(U
.begin(),FirstColon
); 
1249    if (SingleSlash 
!= U
.end()) 
1250       Path
.assign(SingleSlash
,U
.end()); 
1251    if (Path
.empty() == true) 
1254    // Now we attempt to locate a user:pass@host fragment 
1255    if (FirstColon 
+ 2 <= U
.end() && FirstColon
[1] == '/' && FirstColon
[2] == '/') 
1259    if (FirstColon 
>= U
.end()) 
1262    if (FirstColon 
> SingleSlash
) 
1263       FirstColon 
= SingleSlash
; 
1265    // Find the colon... 
1267    if (I 
> SingleSlash
) 
1269    for (; I 
< SingleSlash 
&& *I 
!= ':'; I
++); 
1270    string::const_iterator SecondColon 
= I
; 
1272    // Search for the @ after the colon 
1273    for (; I 
< SingleSlash 
&& *I 
!= '@'; I
++); 
1274    string::const_iterator At 
= I
; 
1276    // Now write the host and user/pass 
1277    if (At 
== SingleSlash
) 
1279       if (FirstColon 
< SingleSlash
) 
1280          Host
.assign(FirstColon
,SingleSlash
); 
1284       Host
.assign(At
+1,SingleSlash
); 
1285       // username and password must be encoded (RFC 3986) 
1286       User
.assign(DeQuoteString(FirstColon
,SecondColon
)); 
1287       if (SecondColon 
< At
) 
1288          Password
.assign(DeQuoteString(SecondColon
+1,At
)); 
1291    // Now we parse the RFC 2732 [] hostnames. 
1292    unsigned long PortEnd 
= 0; 
1294    for (unsigned I 
= 0; I 
!= Host
.length();) 
1303       if (InBracket 
== true && Host
[I
] == ']') 
1314    if (InBracket 
== true) 
1320    // Now we parse off a port number from the hostname 
1322    string::size_type Pos 
= Host
.rfind(':'); 
1323    if (Pos 
== string::npos 
|| Pos 
< PortEnd
) 
1326    Port 
= atoi(string(Host
,Pos
+1).c_str()); 
1327    Host
.assign(Host
,0,Pos
); 
1330 // URI::operator string - Convert the URI to a string                   /*{{{*/ 
1331 // --------------------------------------------------------------------- 
1333 URI::operator string() 
1337    if (Access
.empty() == false) 
1340    if (Host
.empty() == false) 
1342       if (Access
.empty() == false) 
1345       if (User
.empty() == false) 
1348          if (Password
.empty() == false) 
1349             Res 
+= ":" + Password
; 
1353       // Add RFC 2732 escaping characters 
1354       if (Access
.empty() == false && 
1355           (Host
.find('/') != string::npos 
|| Host
.find(':') != string::npos
)) 
1356          Res 
+= '[' + Host 
+ ']'; 
1363          sprintf(S
,":%u",Port
); 
1368    if (Path
.empty() == false) 
1379 // URI::SiteOnly - Return the schema and site for the URI               /*{{{*/ 
1380 // --------------------------------------------------------------------- 
1382 string 
URI::SiteOnly(const string 
&URI
) 
1392 // URI::NoUserPassword - Return the schema, site and path for the URI   /*{{{*/ 
1393 // --------------------------------------------------------------------- 
1395 string 
URI::NoUserPassword(const string 
&URI
)