1 // -*- mode: cpp; mode: fold -*-
3 // $Id: strutl.cc,v 1.42 2001/05/29 04:40:34 jgg 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>
39 // strstrip - Remove white space from the front and back of a string /*{{{*/
40 // ---------------------------------------------------------------------
41 /* This is handy to use when parsing a file. It also removes \n's left
42 over from fgets and company */
43 char *_strstrip(char *String
)
45 for (;*String
!= 0 && (*String
== ' ' || *String
== '\t'); String
++);
50 char *End
= String
+ strlen(String
) - 1;
51 for (;End
!= String
- 1 && (*End
== ' ' || *End
== '\t' || *End
== '\n' ||
52 *End
== '\r'); End
--);
58 // strtabexpand - Converts tabs into 8 spaces /*{{{*/
59 // ---------------------------------------------------------------------
61 char *_strtabexpand(char *String
,size_t Len
)
63 for (char *I
= String
; I
!= I
+ Len
&& *I
!= 0; I
++)
67 if (I
+ 8 > String
+ Len
)
73 /* Assume the start of the string is 0 and find the next 8 char
79 Len
= 8 - ((String
- I
) % 8);
87 memmove(I
+ Len
,I
+ 1,strlen(I
) + 1);
88 for (char *J
= I
; J
+ Len
!= I
; *I
= ' ', I
++);
93 // ParseQuoteWord - Parse a single word out of a string /*{{{*/
94 // ---------------------------------------------------------------------
95 /* This grabs a single word, converts any % escaped characters to their
96 proper values and advances the pointer. Double quotes are understood
97 and striped out as well. This is for URI/URL parsing. It also can
98 understand [] brackets.*/
99 bool ParseQuoteWord(const char *&String
,string
&Res
)
101 // Skip leading whitespace
102 const char *C
= String
;
103 for (;*C
!= 0 && *C
== ' '; C
++);
107 // Jump to the next word
108 for (;*C
!= 0 && isspace(*C
) == 0; C
++)
112 for (C
++; *C
!= 0 && *C
!= '"'; C
++);
118 for (C
++; *C
!= 0 && *C
!= ']'; C
++);
124 // Now de-quote characters
127 const char *Start
= String
;
129 for (I
= Buffer
; I
< Buffer
+ sizeof(Buffer
) && Start
!= C
; I
++)
131 if (*Start
== '%' && Start
+ 2 < C
)
136 *I
= (char)strtol(Tmp
,0,16);
149 // Skip ending white space
150 for (;*C
!= 0 && isspace(*C
) != 0; C
++);
155 // ParseCWord - Parses a string like a C "" expression /*{{{*/
156 // ---------------------------------------------------------------------
157 /* This expects a series of space separated strings enclosed in ""'s.
158 It concatenates the ""'s into a single string. */
159 bool ParseCWord(const char *&String
,string
&Res
)
161 // Skip leading whitespace
162 const char *C
= String
;
163 for (;*C
!= 0 && *C
== ' '; C
++);
169 if (strlen(String
) >= sizeof(Buffer
))
176 for (C
++; *C
!= 0 && *C
!= '"'; C
++)
185 if (C
!= String
&& isspace(*C
) != 0 && isspace(C
[-1]) != 0)
187 if (isspace(*C
) == 0)
197 // QuoteString - Convert a string into quoted from /*{{{*/
198 // ---------------------------------------------------------------------
200 string
QuoteString(string Str
,const char *Bad
)
203 for (string::iterator I
= Str
.begin(); I
!= Str
.end(); I
++)
205 if (strchr(Bad
,*I
) != 0 || isprint(*I
) == 0 ||
206 *I
<= 0x20 || *I
>= 0x7F)
209 sprintf(Buf
,"%%%02x",(int)*I
);
218 // DeQuoteString - Convert a string from quoted from /*{{{*/
219 // ---------------------------------------------------------------------
220 /* This undoes QuoteString */
221 string
DeQuoteString(string Str
)
224 for (string::const_iterator I
= Str
.begin(); I
!= Str
.end(); I
++)
226 if (*I
== '%' && I
+ 2 < Str
.end())
232 Res
+= (char)strtol(Tmp
,0,16);
243 // SizeToStr - Convert a long into a human readable size /*{{{*/
244 // ---------------------------------------------------------------------
245 /* A max of 4 digits are shown before conversion to the next highest unit.
246 The max length of the string will be 5 chars unless the size is > 10
248 string
SizeToStr(double Size
)
257 /* bytes, KiloBytes, MegaBytes, GigaBytes, TeraBytes, PetaBytes,
258 ExaBytes, ZettaBytes, YottaBytes */
259 char Ext
[] = {'\0','k','M','G','T','P','E','Z','Y'};
263 if (ASize
< 100 && I
!= 0)
265 sprintf(S
,"%.1f%c",ASize
,Ext
[I
]);
271 sprintf(S
,"%.0f%c",ASize
,Ext
[I
]);
281 // TimeToStr - Convert the time into a string /*{{{*/
282 // ---------------------------------------------------------------------
283 /* Converts a number of seconds to a hms format */
284 string
TimeToStr(unsigned long Sec
)
292 sprintf(S
,"%lid %lih%lim%lis",Sec
/60/60/24,(Sec
/60/60) % 24,(Sec
/60) % 60,Sec
% 60);
298 sprintf(S
,"%lih%lim%lis",Sec
/60/60,(Sec
/60) % 60,Sec
% 60);
304 sprintf(S
,"%lim%lis",Sec
/60,Sec
% 60);
308 sprintf(S
,"%lis",Sec
);
315 // SubstVar - Substitute a string for another string /*{{{*/
316 // ---------------------------------------------------------------------
317 /* This replaces all occurances of Subst with Contents in Str. */
318 string
SubstVar(string Str
,string Subst
,string Contents
)
320 string::size_type Pos
= 0;
321 string::size_type OldPos
= 0;
324 while (OldPos
< Str
.length() &&
325 (Pos
= Str
.find(Subst
,OldPos
)) != string::npos
)
327 Temp
+= string(Str
,OldPos
,Pos
) + Contents
;
328 OldPos
= Pos
+ Subst
.length();
334 return Temp
+ string(Str
,OldPos
);
337 string
SubstVar(string Str
,const struct SubstVar
*Vars
)
339 for (; Vars
->Subst
!= 0; Vars
++)
340 Str
= SubstVar(Str
,Vars
->Subst
,*Vars
->Contents
);
344 // URItoFileName - Convert the uri into a unique file name /*{{{*/
345 // ---------------------------------------------------------------------
346 /* This converts a URI into a safe filename. It quotes all unsafe characters
347 and converts / to _ and removes the scheme identifier. The resulting
348 file name should be unique and never occur again for a different file */
349 string
URItoFileName(string URI
)
351 // Nuke 'sensitive' items
354 U
.Password
= string();
357 // "\x00-\x20{}|\\\\^\\[\\]<>\"\x7F-\xFF";
358 URI
= QuoteString(U
,"\\|{}[]<>\"^~_=!@#$%^&*");
359 string::iterator J
= URI
.begin();
360 for (; J
!= URI
.end(); J
++)
366 // Base64Encode - Base64 Encoding routine for short strings /*{{{*/
367 // ---------------------------------------------------------------------
368 /* This routine performs a base64 transformation on a string. It was ripped
369 from wget and then patched and bug fixed.
371 This spec can be found in rfc2045 */
372 string
Base64Encode(string S
)
375 static char tbl
[64] = {'A','B','C','D','E','F','G','H',
376 'I','J','K','L','M','N','O','P',
377 'Q','R','S','T','U','V','W','X',
378 'Y','Z','a','b','c','d','e','f',
379 'g','h','i','j','k','l','m','n',
380 'o','p','q','r','s','t','u','v',
381 'w','x','y','z','0','1','2','3',
382 '4','5','6','7','8','9','+','/'};
384 // Pre-allocate some space
386 Final
.reserve((4*S
.length() + 2)/3 + 2);
388 /* Transform the 3x8 bits to 4x6 bits, as required by
390 for (string::const_iterator I
= S
.begin(); I
< S
.end(); I
+= 3)
392 char Bits
[3] = {0,0,0};
399 Final
+= tbl
[Bits
[0] >> 2];
400 Final
+= tbl
[((Bits
[0] & 3) << 4) + (Bits
[1] >> 4)];
402 if (I
+ 1 >= S
.end())
405 Final
+= tbl
[((Bits
[1] & 0xf) << 2) + (Bits
[2] >> 6)];
407 if (I
+ 2 >= S
.end())
410 Final
+= tbl
[Bits
[2] & 0x3f];
413 /* Apply the padding elements, this tells how many bytes the remote
414 end should discard */
415 if (S
.length() % 3 == 2)
417 if (S
.length() % 3 == 1)
423 // stringcmp - Arbitary string compare /*{{{*/
424 // ---------------------------------------------------------------------
425 /* This safely compares two non-null terminated strings of arbitary
427 int stringcmp(const char *A
,const char *AEnd
,const char *B
,const char *BEnd
)
429 for (; A
!= AEnd
&& B
!= BEnd
; A
++, B
++)
433 if (A
== AEnd
&& B
== BEnd
)
443 int stringcmp(string::const_iterator A
,string::const_iterator AEnd
,
444 const char *B
,const char *BEnd
)
446 for (; A
!= AEnd
&& B
!= BEnd
; A
++, B
++)
450 if (A
== AEnd
&& B
== BEnd
)
460 int stringcmp(string::const_iterator A
,string::const_iterator AEnd
,
461 string::const_iterator B
,string::const_iterator BEnd
)
463 for (; A
!= AEnd
&& B
!= BEnd
; A
++, B
++)
467 if (A
== AEnd
&& B
== BEnd
)
478 // stringcasecmp - Arbitary case insensitive string compare /*{{{*/
479 // ---------------------------------------------------------------------
481 int stringcasecmp(const char *A
,const char *AEnd
,const char *B
,const char *BEnd
)
483 for (; A
!= AEnd
&& B
!= BEnd
; A
++, B
++)
484 if (toupper(*A
) != toupper(*B
))
487 if (A
== AEnd
&& B
== BEnd
)
493 if (toupper(*A
) < toupper(*B
))
497 int stringcasecmp(string::const_iterator A
,string::const_iterator AEnd
,
498 const char *B
,const char *BEnd
)
500 for (; A
!= AEnd
&& B
!= BEnd
; A
++, B
++)
501 if (toupper(*A
) != toupper(*B
))
504 if (A
== AEnd
&& B
== BEnd
)
510 if (toupper(*A
) < toupper(*B
))
514 int stringcasecmp(string::const_iterator A
,string::const_iterator AEnd
,
515 string::const_iterator B
,string::const_iterator BEnd
)
517 for (; A
!= AEnd
&& B
!= BEnd
; A
++, B
++)
518 if (toupper(*A
) != toupper(*B
))
521 if (A
== AEnd
&& B
== BEnd
)
527 if (toupper(*A
) < toupper(*B
))
532 // LookupTag - Lookup the value of a tag in a taged string /*{{{*/
533 // ---------------------------------------------------------------------
534 /* The format is like those used in package files and the method
535 communication system */
536 string
LookupTag(string Message
,const char *Tag
,const char *Default
)
538 // Look for a matching tag.
539 int Length
= strlen(Tag
);
540 for (string::iterator I
= Message
.begin(); I
+ Length
< Message
.end(); I
++)
543 if (I
[Length
] == ':' && stringcasecmp(I
,I
+Length
,Tag
) == 0)
545 // Find the end of line and strip the leading/trailing spaces
548 for (; isspace(*I
) != 0 && I
< Message
.end(); I
++);
549 for (J
= I
; *J
!= '\n' && J
< Message
.end(); J
++);
550 for (; J
> I
&& isspace(J
[-1]) != 0; J
--);
555 for (; *I
!= '\n' && I
< Message
.end(); I
++);
558 // Failed to find a match
564 // StringToBool - Converts a string into a boolean /*{{{*/
565 // ---------------------------------------------------------------------
566 /* This inspects the string to see if it is true or if it is false and
567 then returns the result. Several varients on true/false are checked. */
568 int StringToBool(string Text
,int Default
)
571 int Res
= strtol(Text
.c_str(),&End
,0);
572 if (End
!= Text
.c_str() && Res
>= 0 && Res
<= 1)
575 // Check for positives
576 if (strcasecmp(Text
.c_str(),"no") == 0 ||
577 strcasecmp(Text
.c_str(),"false") == 0 ||
578 strcasecmp(Text
.c_str(),"without") == 0 ||
579 strcasecmp(Text
.c_str(),"off") == 0 ||
580 strcasecmp(Text
.c_str(),"disable") == 0)
583 // Check for negatives
584 if (strcasecmp(Text
.c_str(),"yes") == 0 ||
585 strcasecmp(Text
.c_str(),"true") == 0 ||
586 strcasecmp(Text
.c_str(),"with") == 0 ||
587 strcasecmp(Text
.c_str(),"on") == 0 ||
588 strcasecmp(Text
.c_str(),"enable") == 0)
594 // TimeRFC1123 - Convert a time_t into RFC1123 format /*{{{*/
595 // ---------------------------------------------------------------------
596 /* This converts a time_t into a string time representation that is
597 year 2000 complient and timezone neutral */
598 string
TimeRFC1123(time_t Date
)
600 struct tm Conv
= *gmtime(&Date
);
603 const char *Day
[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
604 const char *Month
[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul",
605 "Aug","Sep","Oct","Nov","Dec"};
607 sprintf(Buf
,"%s, %02i %s %i %02i:%02i:%02i GMT",Day
[Conv
.tm_wday
],
608 Conv
.tm_mday
,Month
[Conv
.tm_mon
],Conv
.tm_year
+1900,Conv
.tm_hour
,
609 Conv
.tm_min
,Conv
.tm_sec
);
613 // ReadMessages - Read messages from the FD /*{{{*/
614 // ---------------------------------------------------------------------
615 /* This pulls full messages from the input FD into the message buffer.
616 It assumes that messages will not pause during transit so no
617 fancy buffering is used. */
618 bool ReadMessages(int Fd
, vector
<string
> &List
)
625 int Res
= read(Fd
,End
,sizeof(Buffer
) - (End
-Buffer
));
626 if (Res
< 0 && errno
== EINTR
)
629 // Process is dead, this is kind of bad..
634 if (Res
< 0 && errno
== EAGAIN
)
641 // Look for the end of the message
642 for (char *I
= Buffer
; I
+ 1 < End
; I
++)
644 if (I
[0] != '\n' || I
[1] != '\n')
647 // Pull the message out
648 string
Message(Buffer
,0,I
-Buffer
);
651 for (; I
< End
&& *I
== '\n'; I
++);
653 memmove(Buffer
,I
,End
-Buffer
);
656 List
.push_back(Message
);
661 if (WaitFd(Fd
) == false)
666 // MonthConv - Converts a month string into a number /*{{{*/
667 // ---------------------------------------------------------------------
668 /* This was lifted from the boa webserver which lifted it from 'wn-v1.07'
669 Made it a bit more robust with a few touppers though. */
670 static int MonthConv(char *Month
)
672 switch (toupper(*Month
))
675 return toupper(Month
[1]) == 'P'?3:7;
681 if (toupper(Month
[1]) == 'A')
683 return toupper(Month
[2]) == 'N'?5:6;
685 return toupper(Month
[2]) == 'R'?2:4;
693 // Pretend it is January..
699 // timegm - Internal timegm function if gnu is not available /*{{{*/
700 // ---------------------------------------------------------------------
701 /* Ripped this evil little function from wget - I prefer the use of
702 GNU timegm if possible as this technique will have interesting problems
703 with leap seconds, timezones and other.
705 Converts struct tm to time_t, assuming the data in tm is UTC rather
706 than local timezone (mktime assumes the latter).
708 Contributed by Roger Beeman <beeman@cisco.com>, with the help of
709 Mark Baushke <mdb@cisco.com> and the rest of the Gurus at CISCO. */
710 #ifndef __USE_MISC // glib sets this
711 static time_t timegm(struct tm
*t
)
718 tb
= mktime (gmtime (&tl
));
719 return (tl
<= tb
? (tl
+ (tl
- tb
)) : (tl
- (tb
- tl
)));
723 // StrToTime - Converts a string into a time_t /*{{{*/
724 // ---------------------------------------------------------------------
725 /* This handles all 3 populare time formats including RFC 1123, RFC 1036
726 and the C library asctime format. It requires the GNU library function
727 'timegm' to convert a struct tm in UTC to a time_t. For some bizzar
728 reason the C library does not provide any such function :< This also
729 handles the weird, but unambiguous FTP time format*/
730 bool StrToTime(string Val
,time_t &Result
)
734 const char *I
= Val
.c_str();
736 // Skip the day of the week
737 for (;*I
!= 0 && *I
!= ' '; I
++);
739 // Handle RFC 1123 time
741 if (sscanf(I
," %d %3s %d %d:%d:%d GMT",&Tm
.tm_mday
,Month
,&Tm
.tm_year
,
742 &Tm
.tm_hour
,&Tm
.tm_min
,&Tm
.tm_sec
) != 6)
744 // Handle RFC 1036 time
745 if (sscanf(I
," %d-%3s-%d %d:%d:%d GMT",&Tm
.tm_mday
,Month
,
746 &Tm
.tm_year
,&Tm
.tm_hour
,&Tm
.tm_min
,&Tm
.tm_sec
) == 6)
751 if (sscanf(I
," %3s %d %d:%d:%d %d",Month
,&Tm
.tm_mday
,
752 &Tm
.tm_hour
,&Tm
.tm_min
,&Tm
.tm_sec
,&Tm
.tm_year
) != 6)
755 if (sscanf(Val
.c_str(),"%4d%2d%2d%2d%2d%2d",&Tm
.tm_year
,&Tm
.tm_mon
,
756 &Tm
.tm_mday
,&Tm
.tm_hour
,&Tm
.tm_min
,&Tm
.tm_sec
) != 6)
765 Tm
.tm_mon
= MonthConv(Month
);
768 // Convert to local time and then to GMT
769 Result
= timegm(&Tm
);
773 // StrToNum - Convert a fixed length string to a number /*{{{*/
774 // ---------------------------------------------------------------------
775 /* This is used in decoding the crazy fixed length string headers in
777 bool StrToNum(const char *Str
,unsigned long &Res
,unsigned Len
,unsigned Base
)
780 if (Len
>= sizeof(S
))
785 // All spaces is a zero
788 for (I
= 0; S
[I
] == ' '; I
++);
793 Res
= strtoul(S
,&End
,Base
);
800 // HexDigit - Convert a hex character into an integer /*{{{*/
801 // ---------------------------------------------------------------------
802 /* Helper for Hex2Num */
803 static int HexDigit(int c
)
805 if (c
>= '0' && c
<= '9')
807 if (c
>= 'a' && c
<= 'f')
809 if (c
>= 'A' && c
<= 'F')
814 // Hex2Num - Convert a long hex number into a buffer /*{{{*/
815 // ---------------------------------------------------------------------
816 /* The length of the buffer must be exactly 1/2 the length of the string. */
817 bool Hex2Num(string Str
,unsigned char *Num
,unsigned int Length
)
819 if (Str
.length() != Length
*2)
822 // Convert each digit. We store it in the same order as the string
824 for (string::const_iterator I
= Str
.begin(); I
!= Str
.end();J
++, I
+= 2)
826 if (isxdigit(*I
) == 0 || isxdigit(I
[1]) == 0)
829 Num
[J
] = HexDigit(I
[0]) << 4;
830 Num
[J
] += HexDigit(I
[1]);
836 // TokSplitString - Split a string up by a given token /*{{{*/
837 // ---------------------------------------------------------------------
838 /* This is intended to be a faster splitter, it does not use dynamic
839 memories. Input is changed to insert nulls at each token location. */
840 bool TokSplitString(char Tok
,char *Input
,char **List
,
841 unsigned long ListMax
)
843 // Strip any leading spaces
845 char *Stop
= Start
+ strlen(Start
);
846 for (; *Start
!= 0 && isspace(*Start
) != 0; Start
++);
848 unsigned long Count
= 0;
852 // Skip to the next Token
853 for (; Pos
!= Stop
&& *Pos
!= Tok
; Pos
++);
855 // Back remove spaces
857 for (; End
> Start
&& (End
[-1] == Tok
|| isspace(End
[-1]) != 0); End
--);
860 List
[Count
++] = Start
;
861 if (Count
>= ListMax
)
868 for (; Pos
!= Stop
&& (*Pos
== Tok
|| isspace(*Pos
) != 0 || *Pos
== 0); Pos
++);
876 // RegexChoice - Simple regex list/list matcher /*{{{*/
877 // ---------------------------------------------------------------------
879 unsigned long RegexChoice(RxChoiceList
*Rxs
,const char **ListBegin
,
880 const char **ListEnd
)
882 for (RxChoiceList
*R
= Rxs
; R
->Str
!= 0; R
++)
885 unsigned long Hits
= 0;
886 for (; ListBegin
!= ListEnd
; ListBegin
++)
888 // Check if the name is a regex
891 for (I
= *ListBegin
; *I
!= 0; I
++)
892 if (*I
== '.' || *I
== '?' || *I
== '*' || *I
== '|')
897 // Compile the regex pattern
900 if (regcomp(&Pattern
,*ListBegin
,REG_EXTENDED
| REG_ICASE
|
906 for (RxChoiceList
*R
= Rxs
; R
->Str
!= 0; R
++)
911 if (strcasecmp(R
->Str
,*ListBegin
) != 0)
915 if (regexec(&Pattern
,R
->Str
,0,0,0) != 0)
930 _error
->Warning(_("Selection %s not found"),*ListBegin
);
936 // ioprintf - C format string outputter to C++ iostreams /*{{{*/
937 // ---------------------------------------------------------------------
938 /* This is used to make the internationalization strinc easier to translate
939 and to allow reordering of parameters */
940 void ioprintf(ostream
&out
,const char *format
,...)
943 va_start(args
,format
);
945 // sprintf the description
947 vsnprintf(S
,sizeof(S
),format
,args
);
952 // CheckDomainList - See if Host is in a , seperate list /*{{{*/
953 // ---------------------------------------------------------------------
954 /* The domain list is a comma seperate list of domains that are suffix
955 matched against the argument */
956 bool CheckDomainList(string Host
,string List
)
958 string::const_iterator Start
= List
.begin();
959 for (string::const_iterator Cur
= List
.begin(); Cur
<= List
.end(); Cur
++)
961 if (Cur
< List
.end() && *Cur
!= ',')
964 // Match the end of the string..
965 if ((Host
.size() >= (unsigned)(Cur
- List
.begin())) &&
967 stringcasecmp(Host
.end() - (Cur
- Start
),Host
.end(),Start
,Cur
) == 0)
976 // URI::CopyFrom - Copy from an object /*{{{*/
977 // ---------------------------------------------------------------------
978 /* This parses the URI into all of its components */
979 void URI::CopyFrom(string U
)
981 string::const_iterator I
= U
.begin();
983 // Locate the first colon, this separates the scheme
984 for (; I
< U
.end() && *I
!= ':' ; I
++);
985 string::const_iterator FirstColon
= I
;
987 /* Determine if this is a host type URI with a leading double //
988 and then search for the first single / */
989 string::const_iterator SingleSlash
= I
;
990 if (I
+ 3 < U
.end() && I
[1] == '/' && I
[2] == '/')
993 /* Find the / indicating the end of the hostname, ignoring /'s in the
995 bool InBracket
= false;
996 for (; SingleSlash
< U
.end() && (*SingleSlash
!= '/' || InBracket
== true); SingleSlash
++)
998 if (*SingleSlash
== '[')
1000 if (InBracket
== true && *SingleSlash
== ']')
1004 if (SingleSlash
> U
.end())
1005 SingleSlash
= U
.end();
1007 // We can now write the access and path specifiers
1008 Access
= string(U
,0,FirstColon
- U
.begin());
1009 if (SingleSlash
!= U
.end())
1010 Path
= string(U
,SingleSlash
- U
.begin());
1011 if (Path
.empty() == true)
1014 // Now we attempt to locate a user:pass@host fragment
1015 if (FirstColon
[1] == '/' && FirstColon
[2] == '/')
1019 if (FirstColon
>= U
.end())
1022 if (FirstColon
> SingleSlash
)
1023 FirstColon
= SingleSlash
;
1025 // Find the colon...
1027 if (I
> SingleSlash
)
1029 for (; I
< SingleSlash
&& *I
!= ':'; I
++);
1030 string::const_iterator SecondColon
= I
;
1032 // Search for the @ after the colon
1033 for (; I
< SingleSlash
&& *I
!= '@'; I
++);
1034 string::const_iterator At
= I
;
1036 // Now write the host and user/pass
1037 if (At
== SingleSlash
)
1039 if (FirstColon
< SingleSlash
)
1040 Host
= string(U
,FirstColon
- U
.begin(),SingleSlash
- FirstColon
);
1044 Host
= string(U
,At
- U
.begin() + 1,SingleSlash
- At
- 1);
1045 User
= string(U
,FirstColon
- U
.begin(),SecondColon
- FirstColon
);
1046 if (SecondColon
< At
)
1047 Password
= string(U
,SecondColon
- U
.begin() + 1,At
- SecondColon
- 1);
1050 // Now we parse the RFC 2732 [] hostnames.
1051 unsigned long PortEnd
= 0;
1053 for (unsigned I
= 0; I
!= Host
.length();)
1062 if (InBracket
== true && Host
[I
] == ']')
1073 if (InBracket
== true)
1079 // Now we parse off a port number from the hostname
1081 string::size_type Pos
= Host
.rfind(':');
1082 if (Pos
== string::npos
|| Pos
< PortEnd
)
1085 Port
= atoi(string(Host
,Pos
+1).c_str());
1086 Host
= string(Host
,0,Pos
);
1089 // URI::operator string - Convert the URI to a string /*{{{*/
1090 // ---------------------------------------------------------------------
1092 URI::operator string()
1096 if (Access
.empty() == false)
1099 if (Host
.empty() == false)
1101 if (Access
.empty() == false)
1104 if (User
.empty() == false)
1107 if (Password
.empty() == false)
1108 Res
+= ":" + Password
;
1112 // Add RFC 2732 escaping characters
1113 if (Access
.empty() == false &&
1114 (Host
.find('/') != string::npos
|| Host
.find(':') != string::npos
))
1115 Res
+= '[' + Host
+ ']';
1122 sprintf(S
,":%u",Port
);
1127 if (Path
.empty() == false)
1138 // URI::SiteOnly - Return the schema and site for the URI /*{{{*/
1139 // ---------------------------------------------------------------------
1141 string
URI::SiteOnly(string URI
)
1145 U
.Password
= string();