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 ##################################################################### */
19 #pragma implementation "apt-pkg/strutl.h"
22 #include <apt-pkg/strutl.h>
23 #include <apt-pkg/fileutl.h>
24 #include <apt-pkg/error.h>
41 // strstrip - Remove white space from the front and back of a string /*{{{*/
42 // ---------------------------------------------------------------------
43 /* This is handy to use when parsing a file. It also removes \n's left
44 over from fgets and company */
45 char *_strstrip(char *String
)
47 for (;*String
!= 0 && (*String
== ' ' || *String
== '\t'); String
++);
52 char *End
= String
+ strlen(String
) - 1;
53 for (;End
!= String
- 1 && (*End
== ' ' || *End
== '\t' || *End
== '\n' ||
54 *End
== '\r'); End
--);
60 // strtabexpand - Converts tabs into 8 spaces /*{{{*/
61 // ---------------------------------------------------------------------
63 char *_strtabexpand(char *String
,size_t Len
)
65 for (char *I
= String
; I
!= I
+ Len
&& *I
!= 0; I
++)
69 if (I
+ 8 > String
+ Len
)
75 /* Assume the start of the string is 0 and find the next 8 char
81 Len
= 8 - ((String
- I
) % 8);
89 memmove(I
+ Len
,I
+ 1,strlen(I
) + 1);
90 for (char *J
= I
; J
+ Len
!= I
; *I
= ' ', I
++);
95 // ParseQuoteWord - Parse a single word out of a string /*{{{*/
96 // ---------------------------------------------------------------------
97 /* This grabs a single word, converts any % escaped characters to their
98 proper values and advances the pointer. Double quotes are understood
99 and striped out as well. This is for URI/URL parsing. It also can
100 understand [] brackets.*/
101 bool ParseQuoteWord(const char *&String
,string
&Res
)
103 // Skip leading whitespace
104 const char *C
= String
;
105 for (;*C
!= 0 && *C
== ' '; C
++);
109 // Jump to the next word
110 for (;*C
!= 0 && isspace(*C
) == 0; C
++)
114 for (C
++; *C
!= 0 && *C
!= '"'; C
++);
120 for (C
++; *C
!= 0 && *C
!= ']'; C
++);
126 // Now de-quote characters
129 const char *Start
= String
;
131 for (I
= Buffer
; I
< Buffer
+ sizeof(Buffer
) && Start
!= C
; I
++)
133 if (*Start
== '%' && Start
+ 2 < C
)
138 *I
= (char)strtol(Tmp
,0,16);
151 // Skip ending white space
152 for (;*C
!= 0 && isspace(*C
) != 0; C
++);
157 // ParseCWord - Parses a string like a C "" expression /*{{{*/
158 // ---------------------------------------------------------------------
159 /* This expects a series of space separated strings enclosed in ""'s.
160 It concatenates the ""'s into a single string. */
161 bool ParseCWord(const char *&String
,string
&Res
)
163 // Skip leading whitespace
164 const char *C
= String
;
165 for (;*C
!= 0 && *C
== ' '; C
++);
171 if (strlen(String
) >= sizeof(Buffer
))
178 for (C
++; *C
!= 0 && *C
!= '"'; C
++)
187 if (C
!= String
&& isspace(*C
) != 0 && isspace(C
[-1]) != 0)
189 if (isspace(*C
) == 0)
199 // QuoteString - Convert a string into quoted from /*{{{*/
200 // ---------------------------------------------------------------------
202 string
QuoteString(string Str
,const char *Bad
)
205 for (string::iterator I
= Str
.begin(); I
!= Str
.end(); I
++)
207 if (strchr(Bad
,*I
) != 0 || isprint(*I
) == 0 ||
208 *I
<= 0x20 || *I
>= 0x7F)
211 sprintf(Buf
,"%%%02x",(int)*I
);
220 // DeQuoteString - Convert a string from quoted from /*{{{*/
221 // ---------------------------------------------------------------------
222 /* This undoes QuoteString */
223 string
DeQuoteString(string Str
)
226 for (string::const_iterator I
= Str
.begin(); I
!= Str
.end(); I
++)
228 if (*I
== '%' && I
+ 2 < Str
.end())
234 Res
+= (char)strtol(Tmp
,0,16);
245 // SizeToStr - Convert a long into a human readable size /*{{{*/
246 // ---------------------------------------------------------------------
247 /* A max of 4 digits are shown before conversion to the next highest unit.
248 The max length of the string will be 5 chars unless the size is > 10
250 string
SizeToStr(double Size
)
259 /* bytes, KiloBytes, MegaBytes, GigaBytes, TeraBytes, PetaBytes,
260 ExaBytes, ZettaBytes, YottaBytes */
261 char Ext
[] = {'\0','k','M','G','T','P','E','Z','Y'};
265 if (ASize
< 100 && I
!= 0)
267 sprintf(S
,"%.1f%c",ASize
,Ext
[I
]);
273 sprintf(S
,"%.0f%c",ASize
,Ext
[I
]);
283 // TimeToStr - Convert the time into a string /*{{{*/
284 // ---------------------------------------------------------------------
285 /* Converts a number of seconds to a hms format */
286 string
TimeToStr(unsigned long Sec
)
294 sprintf(S
,"%lid %lih%lim%lis",Sec
/60/60/24,(Sec
/60/60) % 24,(Sec
/60) % 60,Sec
% 60);
300 sprintf(S
,"%lih%lim%lis",Sec
/60/60,(Sec
/60) % 60,Sec
% 60);
306 sprintf(S
,"%lim%lis",Sec
/60,Sec
% 60);
310 sprintf(S
,"%lis",Sec
);
317 // SubstVar - Substitute a string for another string /*{{{*/
318 // ---------------------------------------------------------------------
319 /* This replaces all occurances of Subst with Contents in Str. */
320 string
SubstVar(string Str
,string Subst
,string Contents
)
322 string::size_type Pos
= 0;
323 string::size_type OldPos
= 0;
326 while (OldPos
< Str
.length() &&
327 (Pos
= Str
.find(Subst
,OldPos
)) != string::npos
)
329 Temp
+= string(Str
,OldPos
,Pos
) + Contents
;
330 OldPos
= Pos
+ Subst
.length();
336 return Temp
+ string(Str
,OldPos
);
339 string
SubstVar(string Str
,const struct SubstVar
*Vars
)
341 for (; Vars
->Subst
!= 0; Vars
++)
342 Str
= SubstVar(Str
,Vars
->Subst
,*Vars
->Contents
);
346 // URItoFileName - Convert the uri into a unique file name /*{{{*/
347 // ---------------------------------------------------------------------
348 /* This converts a URI into a safe filename. It quotes all unsafe characters
349 and converts / to _ and removes the scheme identifier. The resulting
350 file name should be unique and never occur again for a different file */
351 string
URItoFileName(string URI
)
353 // Nuke 'sensitive' items
356 U
.Password
= string();
359 // "\x00-\x20{}|\\\\^\\[\\]<>\"\x7F-\xFF";
360 URI
= QuoteString(U
,"\\|{}[]<>\"^~_=!@#$%^&*");
361 string::iterator J
= URI
.begin();
362 for (; J
!= URI
.end(); J
++)
368 // Base64Encode - Base64 Encoding routine for short strings /*{{{*/
369 // ---------------------------------------------------------------------
370 /* This routine performs a base64 transformation on a string. It was ripped
371 from wget and then patched and bug fixed.
373 This spec can be found in rfc2045 */
374 string
Base64Encode(string S
)
377 static char tbl
[64] = {'A','B','C','D','E','F','G','H',
378 'I','J','K','L','M','N','O','P',
379 'Q','R','S','T','U','V','W','X',
380 'Y','Z','a','b','c','d','e','f',
381 'g','h','i','j','k','l','m','n',
382 'o','p','q','r','s','t','u','v',
383 'w','x','y','z','0','1','2','3',
384 '4','5','6','7','8','9','+','/'};
386 // Pre-allocate some space
388 Final
.reserve((4*S
.length() + 2)/3 + 2);
390 /* Transform the 3x8 bits to 4x6 bits, as required by
392 for (string::const_iterator I
= S
.begin(); I
< S
.end(); I
+= 3)
394 char Bits
[3] = {0,0,0};
401 Final
+= tbl
[Bits
[0] >> 2];
402 Final
+= tbl
[((Bits
[0] & 3) << 4) + (Bits
[1] >> 4)];
404 if (I
+ 1 >= S
.end())
407 Final
+= tbl
[((Bits
[1] & 0xf) << 2) + (Bits
[2] >> 6)];
409 if (I
+ 2 >= S
.end())
412 Final
+= tbl
[Bits
[2] & 0x3f];
415 /* Apply the padding elements, this tells how many bytes the remote
416 end should discard */
417 if (S
.length() % 3 == 2)
419 if (S
.length() % 3 == 1)
425 // stringcmp - Arbitary string compare /*{{{*/
426 // ---------------------------------------------------------------------
427 /* This safely compares two non-null terminated strings of arbitary
429 int stringcmp(const char *A
,const char *AEnd
,const char *B
,const char *BEnd
)
431 for (; A
!= AEnd
&& B
!= BEnd
; A
++, B
++)
435 if (A
== AEnd
&& B
== BEnd
)
447 int stringcmp(string::const_iterator A
,string::const_iterator AEnd
,
448 const char *B
,const char *BEnd
)
450 for (; A
!= AEnd
&& B
!= BEnd
; A
++, B
++)
454 if (A
== AEnd
&& B
== BEnd
)
464 int stringcmp(string::const_iterator A
,string::const_iterator AEnd
,
465 string::const_iterator B
,string::const_iterator BEnd
)
467 for (; A
!= AEnd
&& B
!= BEnd
; A
++, B
++)
471 if (A
== AEnd
&& B
== BEnd
)
483 // stringcasecmp - Arbitary case insensitive string compare /*{{{*/
484 // ---------------------------------------------------------------------
486 int stringcasecmp(const char *A
,const char *AEnd
,const char *B
,const char *BEnd
)
488 for (; A
!= AEnd
&& B
!= BEnd
; A
++, B
++)
489 if (toupper(*A
) != toupper(*B
))
492 if (A
== AEnd
&& B
== BEnd
)
498 if (toupper(*A
) < toupper(*B
))
503 int stringcasecmp(string::const_iterator A
,string::const_iterator AEnd
,
504 const char *B
,const char *BEnd
)
506 for (; A
!= AEnd
&& B
!= BEnd
; A
++, B
++)
507 if (toupper(*A
) != toupper(*B
))
510 if (A
== AEnd
&& B
== BEnd
)
516 if (toupper(*A
) < toupper(*B
))
520 int stringcasecmp(string::const_iterator A
,string::const_iterator AEnd
,
521 string::const_iterator B
,string::const_iterator BEnd
)
523 for (; A
!= AEnd
&& B
!= BEnd
; A
++, B
++)
524 if (toupper(*A
) != toupper(*B
))
527 if (A
== AEnd
&& B
== BEnd
)
533 if (toupper(*A
) < toupper(*B
))
539 // LookupTag - Lookup the value of a tag in a taged string /*{{{*/
540 // ---------------------------------------------------------------------
541 /* The format is like those used in package files and the method
542 communication system */
543 string
LookupTag(string Message
,const char *Tag
,const char *Default
)
545 // Look for a matching tag.
546 int Length
= strlen(Tag
);
547 for (string::iterator I
= Message
.begin(); I
+ Length
< Message
.end(); I
++)
550 if (I
[Length
] == ':' && stringcasecmp(I
,I
+Length
,Tag
) == 0)
552 // Find the end of line and strip the leading/trailing spaces
555 for (; isspace(*I
) != 0 && I
< Message
.end(); I
++);
556 for (J
= I
; *J
!= '\n' && J
< Message
.end(); J
++);
557 for (; J
> I
&& isspace(J
[-1]) != 0; J
--);
562 for (; *I
!= '\n' && I
< Message
.end(); I
++);
565 // Failed to find a match
571 // StringToBool - Converts a string into a boolean /*{{{*/
572 // ---------------------------------------------------------------------
573 /* This inspects the string to see if it is true or if it is false and
574 then returns the result. Several varients on true/false are checked. */
575 int StringToBool(string Text
,int Default
)
578 int Res
= strtol(Text
.c_str(),&End
,0);
579 if (End
!= Text
.c_str() && Res
>= 0 && Res
<= 1)
582 // Check for positives
583 if (strcasecmp(Text
.c_str(),"no") == 0 ||
584 strcasecmp(Text
.c_str(),"false") == 0 ||
585 strcasecmp(Text
.c_str(),"without") == 0 ||
586 strcasecmp(Text
.c_str(),"off") == 0 ||
587 strcasecmp(Text
.c_str(),"disable") == 0)
590 // Check for negatives
591 if (strcasecmp(Text
.c_str(),"yes") == 0 ||
592 strcasecmp(Text
.c_str(),"true") == 0 ||
593 strcasecmp(Text
.c_str(),"with") == 0 ||
594 strcasecmp(Text
.c_str(),"on") == 0 ||
595 strcasecmp(Text
.c_str(),"enable") == 0)
601 // TimeRFC1123 - Convert a time_t into RFC1123 format /*{{{*/
602 // ---------------------------------------------------------------------
603 /* This converts a time_t into a string time representation that is
604 year 2000 complient and timezone neutral */
605 string
TimeRFC1123(time_t Date
)
607 struct tm Conv
= *gmtime(&Date
);
610 const char *Day
[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
611 const char *Month
[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul",
612 "Aug","Sep","Oct","Nov","Dec"};
614 sprintf(Buf
,"%s, %02i %s %i %02i:%02i:%02i GMT",Day
[Conv
.tm_wday
],
615 Conv
.tm_mday
,Month
[Conv
.tm_mon
],Conv
.tm_year
+1900,Conv
.tm_hour
,
616 Conv
.tm_min
,Conv
.tm_sec
);
620 // ReadMessages - Read messages from the FD /*{{{*/
621 // ---------------------------------------------------------------------
622 /* This pulls full messages from the input FD into the message buffer.
623 It assumes that messages will not pause during transit so no
624 fancy buffering is used. */
625 bool ReadMessages(int Fd
, vector
<string
> &List
)
632 int Res
= read(Fd
,End
,sizeof(Buffer
) - (End
-Buffer
));
633 if (Res
< 0 && errno
== EINTR
)
636 // Process is dead, this is kind of bad..
641 if (Res
< 0 && errno
== EAGAIN
)
648 // Look for the end of the message
649 for (char *I
= Buffer
; I
+ 1 < End
; I
++)
651 if (I
[0] != '\n' || I
[1] != '\n')
654 // Pull the message out
655 string
Message(Buffer
,I
-Buffer
);
658 for (; I
< End
&& *I
== '\n'; I
++);
660 memmove(Buffer
,I
,End
-Buffer
);
663 List
.push_back(Message
);
668 if (WaitFd(Fd
) == false)
673 // MonthConv - Converts a month string into a number /*{{{*/
674 // ---------------------------------------------------------------------
675 /* This was lifted from the boa webserver which lifted it from 'wn-v1.07'
676 Made it a bit more robust with a few touppers though. */
677 static int MonthConv(char *Month
)
679 switch (toupper(*Month
))
682 return toupper(Month
[1]) == 'P'?3:7;
688 if (toupper(Month
[1]) == 'A')
690 return toupper(Month
[2]) == 'N'?5:6;
692 return toupper(Month
[2]) == 'R'?2:4;
700 // Pretend it is January..
706 // timegm - Internal timegm function if gnu is not available /*{{{*/
707 // ---------------------------------------------------------------------
708 /* Ripped this evil little function from wget - I prefer the use of
709 GNU timegm if possible as this technique will have interesting problems
710 with leap seconds, timezones and other.
712 Converts struct tm to time_t, assuming the data in tm is UTC rather
713 than local timezone (mktime assumes the latter).
715 Contributed by Roger Beeman <beeman@cisco.com>, with the help of
716 Mark Baushke <mdb@cisco.com> and the rest of the Gurus at CISCO. */
718 /* Turned it into an autoconf check, because GNU is not the only thing which
719 can provide timegm. -- 2002-09-22, Joel Baker */
721 #ifndef HAVE_TIMEGM // Now with autoconf!
722 static time_t timegm(struct tm
*t
)
729 tb
= mktime (gmtime (&tl
));
730 return (tl
<= tb
? (tl
+ (tl
- tb
)) : (tl
- (tb
- tl
)));
734 // StrToTime - Converts a string into a time_t /*{{{*/
735 // ---------------------------------------------------------------------
736 /* This handles all 3 populare time formats including RFC 1123, RFC 1036
737 and the C library asctime format. It requires the GNU library function
738 'timegm' to convert a struct tm in UTC to a time_t. For some bizzar
739 reason the C library does not provide any such function :< This also
740 handles the weird, but unambiguous FTP time format*/
741 bool StrToTime(string Val
,time_t &Result
)
745 const char *I
= Val
.c_str();
747 // Skip the day of the week
748 for (;*I
!= 0 && *I
!= ' '; I
++);
750 // Handle RFC 1123 time
752 if (sscanf(I
," %d %3s %d %d:%d:%d GMT",&Tm
.tm_mday
,Month
,&Tm
.tm_year
,
753 &Tm
.tm_hour
,&Tm
.tm_min
,&Tm
.tm_sec
) != 6)
755 // Handle RFC 1036 time
756 if (sscanf(I
," %d-%3s-%d %d:%d:%d GMT",&Tm
.tm_mday
,Month
,
757 &Tm
.tm_year
,&Tm
.tm_hour
,&Tm
.tm_min
,&Tm
.tm_sec
) == 6)
762 if (sscanf(I
," %3s %d %d:%d:%d %d",Month
,&Tm
.tm_mday
,
763 &Tm
.tm_hour
,&Tm
.tm_min
,&Tm
.tm_sec
,&Tm
.tm_year
) != 6)
766 if (sscanf(Val
.c_str(),"%4d%2d%2d%2d%2d%2d",&Tm
.tm_year
,&Tm
.tm_mon
,
767 &Tm
.tm_mday
,&Tm
.tm_hour
,&Tm
.tm_min
,&Tm
.tm_sec
) != 6)
776 Tm
.tm_mon
= MonthConv(Month
);
779 // Convert to local time and then to GMT
780 Result
= timegm(&Tm
);
784 // StrToNum - Convert a fixed length string to a number /*{{{*/
785 // ---------------------------------------------------------------------
786 /* This is used in decoding the crazy fixed length string headers in
788 bool StrToNum(const char *Str
,unsigned long &Res
,unsigned Len
,unsigned Base
)
791 if (Len
>= sizeof(S
))
796 // All spaces is a zero
799 for (I
= 0; S
[I
] == ' '; I
++);
804 Res
= strtoul(S
,&End
,Base
);
811 // HexDigit - Convert a hex character into an integer /*{{{*/
812 // ---------------------------------------------------------------------
813 /* Helper for Hex2Num */
814 static int HexDigit(int c
)
816 if (c
>= '0' && c
<= '9')
818 if (c
>= 'a' && c
<= 'f')
820 if (c
>= 'A' && c
<= 'F')
825 // Hex2Num - Convert a long hex number into a buffer /*{{{*/
826 // ---------------------------------------------------------------------
827 /* The length of the buffer must be exactly 1/2 the length of the string. */
828 bool Hex2Num(string Str
,unsigned char *Num
,unsigned int Length
)
830 if (Str
.length() != Length
*2)
833 // Convert each digit. We store it in the same order as the string
835 for (string::const_iterator I
= Str
.begin(); I
!= Str
.end();J
++, I
+= 2)
837 if (isxdigit(*I
) == 0 || isxdigit(I
[1]) == 0)
840 Num
[J
] = HexDigit(I
[0]) << 4;
841 Num
[J
] += HexDigit(I
[1]);
847 // TokSplitString - Split a string up by a given token /*{{{*/
848 // ---------------------------------------------------------------------
849 /* This is intended to be a faster splitter, it does not use dynamic
850 memories. Input is changed to insert nulls at each token location. */
851 bool TokSplitString(char Tok
,char *Input
,char **List
,
852 unsigned long ListMax
)
854 // Strip any leading spaces
856 char *Stop
= Start
+ strlen(Start
);
857 for (; *Start
!= 0 && isspace(*Start
) != 0; Start
++);
859 unsigned long Count
= 0;
863 // Skip to the next Token
864 for (; Pos
!= Stop
&& *Pos
!= Tok
; Pos
++);
866 // Back remove spaces
868 for (; End
> Start
&& (End
[-1] == Tok
|| isspace(End
[-1]) != 0); End
--);
871 List
[Count
++] = Start
;
872 if (Count
>= ListMax
)
879 for (; Pos
!= Stop
&& (*Pos
== Tok
|| isspace(*Pos
) != 0 || *Pos
== 0); Pos
++);
887 // RegexChoice - Simple regex list/list matcher /*{{{*/
888 // ---------------------------------------------------------------------
890 unsigned long RegexChoice(RxChoiceList
*Rxs
,const char **ListBegin
,
891 const char **ListEnd
)
893 for (RxChoiceList
*R
= Rxs
; R
->Str
!= 0; R
++)
896 unsigned long Hits
= 0;
897 for (; ListBegin
!= ListEnd
; ListBegin
++)
899 // Check if the name is a regex
902 for (I
= *ListBegin
; *I
!= 0; I
++)
903 if (*I
== '.' || *I
== '?' || *I
== '*' || *I
== '|')
908 // Compile the regex pattern
911 if (regcomp(&Pattern
,*ListBegin
,REG_EXTENDED
| REG_ICASE
|
917 for (RxChoiceList
*R
= Rxs
; R
->Str
!= 0; R
++)
922 if (strcasecmp(R
->Str
,*ListBegin
) != 0)
926 if (regexec(&Pattern
,R
->Str
,0,0,0) != 0)
941 _error
->Warning(_("Selection %s not found"),*ListBegin
);
947 // ioprintf - C format string outputter to C++ iostreams /*{{{*/
948 // ---------------------------------------------------------------------
949 /* This is used to make the internationalization strings easier to translate
950 and to allow reordering of parameters */
951 void ioprintf(ostream
&out
,const char *format
,...)
954 va_start(args
,format
);
956 // sprintf the description
958 vsnprintf(S
,sizeof(S
),format
,args
);
962 // safe_snprintf - Safer snprintf /*{{{*/
963 // ---------------------------------------------------------------------
964 /* This is a snprintf that will never (ever) go past 'End' and returns a
965 pointer to the end of the new string. The returned string is always null
966 terminated unless Buffer == end. This is a better alterantive to using
967 consecutive snprintfs. */
968 char *safe_snprintf(char *Buffer
,char *End
,const char *Format
,...)
973 va_start(args
,Format
);
978 Did
= vsnprintf(Buffer
,End
- Buffer
,Format
,args
);
979 if (Did
< 0 || Buffer
+ Did
> End
)
985 // CheckDomainList - See if Host is in a , seperate list /*{{{*/
986 // ---------------------------------------------------------------------
987 /* The domain list is a comma seperate list of domains that are suffix
988 matched against the argument */
989 bool CheckDomainList(string Host
,string List
)
991 string::const_iterator Start
= List
.begin();
992 for (string::const_iterator Cur
= List
.begin(); Cur
<= List
.end(); Cur
++)
994 if (Cur
< List
.end() && *Cur
!= ',')
997 // Match the end of the string..
998 if ((Host
.size() >= (unsigned)(Cur
- Start
)) &&
1000 stringcasecmp(Host
.end() - (Cur
- Start
),Host
.end(),Start
,Cur
) == 0)
1009 // URI::CopyFrom - Copy from an object /*{{{*/
1010 // ---------------------------------------------------------------------
1011 /* This parses the URI into all of its components */
1012 void URI::CopyFrom(string U
)
1014 string::const_iterator I
= U
.begin();
1016 // Locate the first colon, this separates the scheme
1017 for (; I
< U
.end() && *I
!= ':' ; I
++);
1018 string::const_iterator FirstColon
= I
;
1020 /* Determine if this is a host type URI with a leading double //
1021 and then search for the first single / */
1022 string::const_iterator SingleSlash
= I
;
1023 if (I
+ 3 < U
.end() && I
[1] == '/' && I
[2] == '/')
1026 /* Find the / indicating the end of the hostname, ignoring /'s in the
1028 bool InBracket
= false;
1029 for (; SingleSlash
< U
.end() && (*SingleSlash
!= '/' || InBracket
== true); SingleSlash
++)
1031 if (*SingleSlash
== '[')
1033 if (InBracket
== true && *SingleSlash
== ']')
1037 if (SingleSlash
> U
.end())
1038 SingleSlash
= U
.end();
1040 // We can now write the access and path specifiers
1041 Access
= string(U
,0,FirstColon
- U
.begin());
1042 if (SingleSlash
!= U
.end())
1043 Path
= string(U
,SingleSlash
- U
.begin());
1044 if (Path
.empty() == true)
1047 // Now we attempt to locate a user:pass@host fragment
1048 if (FirstColon
+ 2 <= U
.end() && FirstColon
[1] == '/' && FirstColon
[2] == '/')
1052 if (FirstColon
>= U
.end())
1055 if (FirstColon
> SingleSlash
)
1056 FirstColon
= SingleSlash
;
1058 // Find the colon...
1060 if (I
> SingleSlash
)
1062 for (; I
< SingleSlash
&& *I
!= ':'; I
++);
1063 string::const_iterator SecondColon
= I
;
1065 // Search for the @ after the colon
1066 for (; I
< SingleSlash
&& *I
!= '@'; I
++);
1067 string::const_iterator At
= I
;
1069 // Now write the host and user/pass
1070 if (At
== SingleSlash
)
1072 if (FirstColon
< SingleSlash
)
1073 Host
= string(U
,FirstColon
- U
.begin(),SingleSlash
- FirstColon
);
1077 Host
= string(U
,At
- U
.begin() + 1,SingleSlash
- At
- 1);
1078 User
= string(U
,FirstColon
- U
.begin(),SecondColon
- FirstColon
);
1079 if (SecondColon
< At
)
1080 Password
= string(U
,SecondColon
- U
.begin() + 1,At
- SecondColon
- 1);
1083 // Now we parse the RFC 2732 [] hostnames.
1084 unsigned long PortEnd
= 0;
1086 for (unsigned I
= 0; I
!= Host
.length();)
1095 if (InBracket
== true && Host
[I
] == ']')
1106 if (InBracket
== true)
1112 // Now we parse off a port number from the hostname
1114 string::size_type Pos
= Host
.rfind(':');
1115 if (Pos
== string::npos
|| Pos
< PortEnd
)
1118 Port
= atoi(string(Host
,Pos
+1).c_str());
1119 Host
= string(Host
,0,Pos
);
1122 // URI::operator string - Convert the URI to a string /*{{{*/
1123 // ---------------------------------------------------------------------
1125 URI::operator string()
1129 if (Access
.empty() == false)
1132 if (Host
.empty() == false)
1134 if (Access
.empty() == false)
1137 if (User
.empty() == false)
1140 if (Password
.empty() == false)
1141 Res
+= ":" + Password
;
1145 // Add RFC 2732 escaping characters
1146 if (Access
.empty() == false &&
1147 (Host
.find('/') != string::npos
|| Host
.find(':') != string::npos
))
1148 Res
+= '[' + Host
+ ']';
1155 sprintf(S
,":%u",Port
);
1160 if (Path
.empty() == false)
1171 // URI::SiteOnly - Return the schema and site for the URI /*{{{*/
1172 // ---------------------------------------------------------------------
1174 string
URI::SiteOnly(string URI
)
1178 U
.Password
= string();