]> git.saurik.com Git - apt.git/blob - apt-pkg/contrib/strutl.cc
a2464c9a36bb4b59413625be996632b405fa6bf9
[apt.git] / apt-pkg / contrib / strutl.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: strutl.cc,v 1.21 1999/03/15 08:10:39 jgg Exp $
4 /* ######################################################################
5
6 String Util - Some usefull string functions.
7
8 These have been collected from here and there to do all sorts of usefull
9 things to strings. They are usefull in file parsers, URI handlers and
10 especially in APT methods.
11
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>
14
15 ##################################################################### */
16 /*}}}*/
17 // Includes /*{{{*/
18 #ifdef __GNUG__
19 #pragma implementation "apt-pkg/strutl.h"
20 #endif
21
22 #include <apt-pkg/strutl.h>
23 #include <apt-pkg//fileutl.h>
24
25 #include <ctype.h>
26 #include <string.h>
27 #include <stdio.h>
28 #include <unistd.h>
29 /*}}}*/
30
31 // strstrip - Remove white space from the front and back of a string /*{{{*/
32 // ---------------------------------------------------------------------
33 /* This is handy to use when parsing a file. It also removes \n's left
34 over from fgets and company */
35 char *_strstrip(char *String)
36 {
37 for (;*String != 0 && (*String == ' ' || *String == '\t'); String++);
38
39 if (*String == 0)
40 return String;
41
42 char *End = String + strlen(String) - 1;
43 for (;End != String - 1 && (*End == ' ' || *End == '\t' || *End == '\n' ||
44 *End == '\r'); End--);
45 End++;
46 *End = 0;
47 return String;
48 };
49 /*}}}*/
50 // strtabexpand - Converts tabs into 8 spaces /*{{{*/
51 // ---------------------------------------------------------------------
52 /* */
53 char *_strtabexpand(char *String,size_t Len)
54 {
55 for (char *I = String; I != I + Len && *I != 0; I++)
56 {
57 if (*I != '\t')
58 continue;
59 if (I + 8 > String + Len)
60 {
61 *I = 0;
62 return String;
63 }
64
65 /* Assume the start of the string is 0 and find the next 8 char
66 division */
67 int Len;
68 if (String == I)
69 Len = 1;
70 else
71 Len = 8 - ((String - I) % 8);
72 Len -= 2;
73 if (Len <= 0)
74 {
75 *I = ' ';
76 continue;
77 }
78
79 memmove(I + Len,I + 1,strlen(I) + 1);
80 for (char *J = I; J + Len != I; *I = ' ', I++);
81 }
82 return String;
83 }
84 /*}}}*/
85 // ParseQuoteWord - Parse a single word out of a string /*{{{*/
86 // ---------------------------------------------------------------------
87 /* This grabs a single word, converts any % escaped characters to their
88 proper values and advances the pointer. Double quotes are understood
89 and striped out as well. This is for URI/URL parsing. */
90 bool ParseQuoteWord(const char *&String,string &Res)
91 {
92 // Skip leading whitespace
93 const char *C = String;
94 for (;*C != 0 && *C == ' '; C++);
95 if (*C == 0)
96 return false;
97
98 // Jump to the next word
99 for (;*C != 0 && *C != ' '; C++)
100 {
101 if (*C == '"')
102 {
103 for (C++;*C != 0 && *C != '"'; C++);
104 if (*C == 0)
105 return false;
106 }
107 }
108
109 // Now de-quote characters
110 char Buffer[1024];
111 char Tmp[3];
112 const char *Start = String;
113 char *I;
114 for (I = Buffer; I < Buffer + sizeof(Buffer) && Start != C; I++)
115 {
116 if (*Start == '%' && Start + 2 < C)
117 {
118 Tmp[0] = Start[1];
119 Tmp[1] = Start[2];
120 Tmp[2] = 0;
121 *I = (char)strtol(Tmp,0,16);
122 Start += 3;
123 continue;
124 }
125 if (*Start != '"')
126 *I = *Start;
127 else
128 I--;
129 Start++;
130 }
131 *I = 0;
132 Res = Buffer;
133
134 // Skip ending white space
135 for (;*C != 0 && *C == ' '; C++);
136 String = C;
137 return true;
138 }
139 /*}}}*/
140 // ParseCWord - Parses a string like a C "" expression /*{{{*/
141 // ---------------------------------------------------------------------
142 /* This expects a series of space seperated strings enclosed in ""'s.
143 It concatenates the ""'s into a single string. */
144 bool ParseCWord(const char *String,string &Res)
145 {
146 // Skip leading whitespace
147 const char *C = String;
148 for (;*C != 0 && *C == ' '; C++);
149 if (*C == 0)
150 return false;
151
152 char Buffer[1024];
153 char *Buf = Buffer;
154 if (strlen(String) >= sizeof(Buffer))
155 return false;
156
157 for (; *C != 0; C++)
158 {
159 if (*C == '"')
160 {
161 for (C++; *C != 0 && *C != '"'; C++)
162 *Buf++ = *C;
163
164 if (*C == 0)
165 return false;
166
167 continue;
168 }
169
170 if (C != String && isspace(*C) != 0 && isspace(C[-1]) != 0)
171 continue;
172 if (isspace(*C) == 0)
173 return false;
174 *Buf++ = ' ';
175 }
176 *Buf = 0;
177 Res = Buffer;
178 return true;
179 }
180 /*}}}*/
181 // QuoteString - Convert a string into quoted from /*{{{*/
182 // ---------------------------------------------------------------------
183 /* */
184 string QuoteString(string Str,const char *Bad)
185 {
186 string Res;
187 for (string::iterator I = Str.begin(); I != Str.end(); I++)
188 {
189 if (strchr(Bad,*I) != 0 || isprint(*I) == 0 ||
190 *I <= 0x20 || *I >= 0x7F)
191 {
192 char Buf[10];
193 sprintf(Buf,"%%%02x",(int)*I);
194 Res += Buf;
195 }
196 else
197 Res += *I;
198 }
199 return Res;
200 }
201 /*}}}*/
202 // DeQuoteString - Convert a string from quoted from /*{{{*/
203 // ---------------------------------------------------------------------
204 /* This undoes QuoteString */
205 string DeQuoteString(string Str)
206 {
207 string Res;
208 for (string::iterator I = Str.begin(); I != Str.end(); I++)
209 {
210 if (*I == '%' && I + 2 < Str.end())
211 {
212 char Tmp[3];
213 Tmp[0] = I[1];
214 Tmp[1] = I[2];
215 Tmp[2] = 0;
216 Res += (char)strtol(Tmp,0,16);
217 I += 2;
218 continue;
219 }
220 else
221 Res += *I;
222 }
223 return Res;
224 }
225
226 /*}}}*/
227 // SizeToStr - Convert a long into a human readable size /*{{{*/
228 // ---------------------------------------------------------------------
229 /* A max of 4 digits are shown before conversion to the next highest unit.
230 The max length of the string will be 5 chars unless the size is > 10
231 YottaBytes (E24) */
232 string SizeToStr(double Size)
233 {
234 char S[300];
235 double ASize;
236 if (Size >= 0)
237 ASize = Size;
238 else
239 ASize = -1*Size;
240
241 /* bytes, KiloBytes, MegaBytes, GigaBytes, TeraBytes, PetaBytes,
242 ExaBytes, ZettaBytes, YottaBytes */
243 char Ext[] = {'\0','k','M','G','T','P','E','Z','Y'};
244 int I = 0;
245 while (I <= 8)
246 {
247 if (ASize < 100 && I != 0)
248 {
249 sprintf(S,"%.1f%c",ASize,Ext[I]);
250 break;
251 }
252
253 if (ASize < 10000)
254 {
255 sprintf(S,"%.0f%c",ASize,Ext[I]);
256 break;
257 }
258 ASize /= 1000.0;
259 I++;
260 }
261
262 return S;
263 }
264 /*}}}*/
265 // TimeToStr - Convert the time into a string /*{{{*/
266 // ---------------------------------------------------------------------
267 /* Converts a number of seconds to a hms format */
268 string TimeToStr(unsigned long Sec)
269 {
270 char S[300];
271
272 while (1)
273 {
274 if (Sec > 60*60*24)
275 {
276 sprintf(S,"%lid %lih%lim%lis",Sec/60/60/24,(Sec/60/60) % 24,(Sec/60) % 60,Sec % 60);
277 break;
278 }
279
280 if (Sec > 60*60)
281 {
282 sprintf(S,"%lih%lim%lis",Sec/60/60,(Sec/60) % 60,Sec % 60);
283 break;
284 }
285
286 if (Sec > 60)
287 {
288 sprintf(S,"%lim%lis",Sec/60,Sec % 60);
289 break;
290 }
291
292 sprintf(S,"%lis",Sec);
293 break;
294 }
295
296 return S;
297 }
298 /*}}}*/
299 // SubstVar - Substitute a string for another string /*{{{*/
300 // ---------------------------------------------------------------------
301 /* This replaces all occurances of Subst with Contents in Str. */
302 string SubstVar(string Str,string Subst,string Contents)
303 {
304 string::size_type Pos = 0;
305 string::size_type OldPos = 0;
306 string Temp;
307
308 while (OldPos < Str.length() &&
309 (Pos = Str.find(Subst,OldPos)) != string::npos)
310 {
311 Temp += string(Str,OldPos,Pos) + Contents;
312 OldPos = Pos + Subst.length();
313 }
314
315 if (OldPos == 0)
316 return Str;
317
318 return Temp + string(Str,OldPos);
319 }
320 /*}}}*/
321 // URItoFileName - Convert the uri into a unique file name /*{{{*/
322 // ---------------------------------------------------------------------
323 /* This converts a URI into a safe filename. It quotes all unsafe characters
324 and converts / to _ and removes the scheme identifier. The resulting
325 file name should be unique and never occur again for a different file */
326 string URItoFileName(string URI)
327 {
328 string::const_iterator I = URI.begin() + URI.find(':') + 1;
329 for (; I < URI.end() && *I == '/'; I++);
330
331 // "\x00-\x20{}|\\\\^\\[\\]<>\"\x7F-\xFF";
332 URI = QuoteString(string(I,URI.end() - I),"\\|{}[]<>\"^~_=!@#$%^&*");
333 string::iterator J = URI.begin();
334 for (; J != URI.end(); J++)
335 if (*J == '/')
336 *J = '_';
337 return URI;
338 }
339 /*}}}*/
340 // Base64Encode - Base64 Encoding routine for short strings /*{{{*/
341 // ---------------------------------------------------------------------
342 /* This routine performs a base64 transformation on a string. It was ripped
343 from wget and then patched and bug fixed.
344
345 This spec can be found in rfc2045 */
346 string Base64Encode(string S)
347 {
348 // Conversion table.
349 static char tbl[64] = {'A','B','C','D','E','F','G','H',
350 'I','J','K','L','M','N','O','P',
351 'Q','R','S','T','U','V','W','X',
352 'Y','Z','a','b','c','d','e','f',
353 'g','h','i','j','k','l','m','n',
354 'o','p','q','r','s','t','u','v',
355 'w','x','y','z','0','1','2','3',
356 '4','5','6','7','8','9','+','/'};
357
358 // Pre-allocate some space
359 string Final;
360 Final.reserve((4*S.length() + 2)/3 + 2);
361
362 /* Transform the 3x8 bits to 4x6 bits, as required by
363 base64. */
364 for (string::const_iterator I = S.begin(); I < S.end(); I += 3)
365 {
366 char Bits[3] = {0,0,0};
367 Bits[0] = I[0];
368 if (I + 1 < S.end())
369 Bits[1] = I[1];
370 if (I + 2 < S.end())
371 Bits[2] = I[2];
372
373 Final += tbl[Bits[0] >> 2];
374 Final += tbl[((Bits[0] & 3) << 4) + (Bits[1] >> 4)];
375
376 if (I + 1 >= S.end())
377 break;
378
379 Final += tbl[((Bits[1] & 0xf) << 2) + (Bits[2] >> 6)];
380
381 if (I + 2 >= S.end())
382 break;
383
384 Final += tbl[Bits[2] & 0x3f];
385 }
386
387 /* Apply the padding elements, this tells how many bytes the remote
388 end should discard */
389 if (S.length() % 3 == 2)
390 Final += '=';
391 if (S.length() % 3 == 1)
392 Final += "==";
393
394 return Final;
395 }
396 /*}}}*/
397 // stringcmp - Arbitary string compare /*{{{*/
398 // ---------------------------------------------------------------------
399 /* This safely compares two non-null terminated strings of arbitary
400 length */
401 int stringcmp(const char *A,const char *AEnd,const char *B,const char *BEnd)
402 {
403 for (; A != AEnd && B != BEnd; A++, B++)
404 if (*A != *B)
405 break;
406
407 if (A == AEnd && B == BEnd)
408 return 0;
409 if (A == AEnd)
410 return 1;
411 if (B == BEnd)
412 return -1;
413 if (*A < *B)
414 return -1;
415 return 1;
416 }
417 /*}}}*/
418 // stringcasecmp - Arbitary case insensitive string compare /*{{{*/
419 // ---------------------------------------------------------------------
420 /* */
421 int stringcasecmp(const char *A,const char *AEnd,const char *B,const char *BEnd)
422 {
423 for (; A != AEnd && B != BEnd; A++, B++)
424 if (toupper(*A) != toupper(*B))
425 break;
426
427 if (A == AEnd && B == BEnd)
428 return 0;
429 if (A == AEnd)
430 return 1;
431 if (B == BEnd)
432 return -1;
433 if (toupper(*A) < toupper(*B))
434 return -1;
435 return 1;
436 }
437 /*}}}*/
438 // LookupTag - Lookup the value of a tag in a taged string /*{{{*/
439 // ---------------------------------------------------------------------
440 /* The format is like those used in package files and the method
441 communication system */
442 string LookupTag(string Message,const char *Tag,const char *Default)
443 {
444 // Look for a matching tag.
445 int Length = strlen(Tag);
446 for (string::iterator I = Message.begin(); I + Length < Message.end(); I++)
447 {
448 // Found the tag
449 if (I[Length] == ':' && stringcasecmp(I,I+Length,Tag) == 0)
450 {
451 // Find the end of line and strip the leading/trailing spaces
452 string::iterator J;
453 I += Length + 1;
454 for (; isspace(*I) != 0 && I < Message.end(); I++);
455 for (J = I; *J != '\n' && J < Message.end(); J++);
456 for (; J > I && isspace(J[-1]) != 0; J--);
457
458 return string(I,J-I);
459 }
460
461 for (; *I != '\n' && I < Message.end(); I++);
462 }
463
464 // Failed to find a match
465 if (Default == 0)
466 return string();
467 return Default;
468 }
469 /*}}}*/
470 // StringToBool - Converts a string into a boolean /*{{{*/
471 // ---------------------------------------------------------------------
472 /* This inspects the string to see if it is true or if it is false and
473 then returns the result. Several varients on true/false are checked. */
474 int StringToBool(string Text,int Default = -1)
475 {
476 char *End;
477 int Res = strtol(Text.c_str(),&End,0);
478 if (End != Text.c_str() && Res >= 0 && Res <= 1)
479 return Res;
480
481 // Check for positives
482 if (strcasecmp(Text.c_str(),"no") == 0 ||
483 strcasecmp(Text.c_str(),"false") == 0 ||
484 strcasecmp(Text.c_str(),"without") == 0 ||
485 strcasecmp(Text.c_str(),"off") == 0 ||
486 strcasecmp(Text.c_str(),"disable") == 0)
487 return 0;
488
489 // Check for negatives
490 if (strcasecmp(Text.c_str(),"yes") == 0 ||
491 strcasecmp(Text.c_str(),"true") == 0 ||
492 strcasecmp(Text.c_str(),"with") == 0 ||
493 strcasecmp(Text.c_str(),"on") == 0 ||
494 strcasecmp(Text.c_str(),"enable") == 0)
495 return 1;
496
497 return Default;
498 }
499 /*}}}*/
500 // TimeRFC1123 - Convert a time_t into RFC1123 format /*{{{*/
501 // ---------------------------------------------------------------------
502 /* This converts a time_t into a string time representation that is
503 year 2000 complient and timezone neutral */
504 string TimeRFC1123(time_t Date)
505 {
506 struct tm Conv = *gmtime(&Date);
507 char Buf[300];
508
509 const char *Day[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
510 const char *Month[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul",
511 "Aug","Sep","Oct","Nov","Dec"};
512
513 sprintf(Buf,"%s, %02i %s %i %02i:%02i:%02i GMT",Day[Conv.tm_wday],
514 Conv.tm_mday,Month[Conv.tm_mon],Conv.tm_year+1900,Conv.tm_hour,
515 Conv.tm_min,Conv.tm_sec);
516 return Buf;
517 }
518 /*}}}*/
519 // ReadMessages - Read messages from the FD /*{{{*/
520 // ---------------------------------------------------------------------
521 /* This pulls full messages from the input FD into the message buffer.
522 It assumes that messages will not pause during transit so no
523 fancy buffering is used. */
524 bool ReadMessages(int Fd, vector<string> &List)
525 {
526 char Buffer[4000];
527 char *End = Buffer;
528
529 while (1)
530 {
531 int Res = read(Fd,End,sizeof(Buffer) - (End-Buffer));
532
533 // Process is dead, this is kind of bad..
534 if (Res == 0)
535 return false;
536
537 // No data
538 if (Res <= 0)
539 return true;
540
541 End += Res;
542
543 // Look for the end of the message
544 for (char *I = Buffer; I + 1 < End; I++)
545 {
546 if (I[0] != '\n' || I[1] != '\n')
547 continue;
548
549 // Pull the message out
550 string Message(Buffer,0,I-Buffer);
551
552 // Fix up the buffer
553 for (; I < End && *I == '\n'; I++);
554 End -= I-Buffer;
555 memmove(Buffer,I,End-Buffer);
556 I = Buffer;
557
558 List.push_back(Message);
559 }
560 if (End == Buffer)
561 return true;
562
563 if (WaitFd(Fd) == false)
564 return false;
565 }
566 }
567 /*}}}*/
568 // MonthConv - Converts a month string into a number /*{{{*/
569 // ---------------------------------------------------------------------
570 /* This was lifted from the boa webserver which lifted it from 'wn-v1.07'
571 Made it a bit more robust with a few touppers though. */
572 static int MonthConv(char *Month)
573 {
574 switch (toupper(*Month))
575 {
576 case 'A':
577 return toupper(Month[1]) == 'P'?3:7;
578 case 'D':
579 return 11;
580 case 'F':
581 return 1;
582 case 'J':
583 if (toupper(Month[1]) == 'A')
584 return 0;
585 return toupper(Month[2]) == 'N'?5:6;
586 case 'M':
587 return toupper(Month[2]) == 'R'?2:4;
588 case 'N':
589 return 10;
590 case 'O':
591 return 9;
592 case 'S':
593 return 8;
594
595 // Pretend it is January..
596 default:
597 return 0;
598 }
599 }
600 /*}}}*/
601 // timegm - Internal timegm function if gnu is not available /*{{{*/
602 // ---------------------------------------------------------------------
603 /* Ripped this evil little function from wget - I prefer the use of
604 GNU timegm if possible as this technique will have interesting problems
605 with leap seconds, timezones and other.
606
607 Converts struct tm to time_t, assuming the data in tm is UTC rather
608 than local timezone (mktime assumes the latter).
609
610 Contributed by Roger Beeman <beeman@cisco.com>, with the help of
611 Mark Baushke <mdb@cisco.com> and the rest of the Gurus at CISCO. */
612 #ifndef __USE_MISC // glib sets this
613 static time_t timegm(struct tm *t)
614 {
615 time_t tl, tb;
616
617 tl = mktime (t);
618 if (tl == -1)
619 return -1;
620 tb = mktime (gmtime (&tl));
621 return (tl <= tb ? (tl + (tl - tb)) : (tl - (tb - tl)));
622 }
623 #endif
624 /*}}}*/
625 // StrToTime - Converts a string into a time_t /*{{{*/
626 // ---------------------------------------------------------------------
627 /* This handles all 3 populare time formats including RFC 1123, RFC 1036
628 and the C library asctime format. It requires the GNU library function
629 'timegm' to convert a struct tm in UTC to a time_t. For some bizzar
630 reason the C library does not provide any such function :<*/
631 bool StrToTime(string Val,time_t &Result)
632 {
633 struct tm Tm;
634 char Month[10];
635 const char *I = Val.c_str();
636
637 // Skip the day of the week
638 for (;*I != 0 && *I != ' '; I++);
639
640 // Handle RFC 1123 time
641 if (sscanf(I," %d %3s %d %d:%d:%d GMT",&Tm.tm_mday,Month,&Tm.tm_year,
642 &Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec) != 6)
643 {
644 // Handle RFC 1036 time
645 if (sscanf(I," %d-%3s-%d %d:%d:%d GMT",&Tm.tm_mday,Month,
646 &Tm.tm_year,&Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec) == 6)
647 Tm.tm_year += 1900;
648 else
649 {
650 // asctime format
651 if (sscanf(I," %3s %d %d:%d:%d %d",Month,&Tm.tm_mday,
652 &Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec,&Tm.tm_year) != 6)
653 return false;
654 }
655 }
656
657 Tm.tm_isdst = 0;
658 Tm.tm_mon = MonthConv(Month);
659 Tm.tm_year -= 1900;
660
661 // Convert to local time and then to GMT
662 Result = timegm(&Tm);
663 return true;
664 }
665 /*}}}*/
666
667 // URI::CopyFrom - Copy from an object /*{{{*/
668 // ---------------------------------------------------------------------
669 /* This parses the URI into all of its components */
670 void URI::CopyFrom(string U)
671 {
672 string::const_iterator I = U.begin();
673
674 // Locate the first colon, this seperates the scheme
675 for (; I < U.end() && *I != ':' ; I++);
676 string::const_iterator FirstColon = I;
677
678 /* Determine if this is a host type URI with a leading double //
679 and then search for the first single / */
680 string::const_iterator SingleSlash = I;
681 if (I + 3 < U.end() && I[1] == '/' && I[2] == '/')
682 SingleSlash += 3;
683 for (; SingleSlash < U.end() && *SingleSlash != '/'; SingleSlash++);
684 if (SingleSlash > U.end())
685 SingleSlash = U.end();
686
687 // We can now write the access and path specifiers
688 Access = string(U,0,FirstColon - U.begin());
689 if (SingleSlash != U.end())
690 Path = string(U,SingleSlash - U.begin());
691 if (Path.empty() == true)
692 Path = "/";
693
694 // Now we attempt to locate a user:pass@host fragment
695 if (FirstColon[1] == '/' && FirstColon[2] == '/')
696 FirstColon += 3;
697 else
698 FirstColon += 1;
699 if (FirstColon >= U.end())
700 return;
701
702 if (FirstColon > SingleSlash)
703 FirstColon = SingleSlash;
704
705 // Search for the @
706 I = FirstColon;
707 for (; I < SingleSlash && *I != '@'; I++);
708 string::const_iterator At = I;
709
710 // Colon in the @ section
711 I = FirstColon + 1;
712 for (; I < At && *I != ':'; I++);
713 string::const_iterator SecondColon = I;
714
715 // Now write the host and user/pass
716 if (At == SingleSlash)
717 {
718 if (FirstColon < SingleSlash)
719 Host = string(U,FirstColon - U.begin(),SingleSlash - FirstColon);
720 }
721 else
722 {
723 Host = string(U,At - U.begin() + 1,SingleSlash - At - 1);
724 User = string(U,FirstColon - U.begin(),SecondColon - FirstColon);
725 if (SecondColon < At)
726 Password = string(U,SecondColon - U.begin() + 1,At - SecondColon - 1);
727 }
728
729 // Now we parse off a pot number from the hostname
730 Port = 0;
731 string::size_type Pos = Host.rfind(':');
732 if (Pos == string::npos)
733 return;
734
735 Port = atoi(string(Host,Pos+1).c_str());
736 Host = string(Host,0,Pos);
737 }
738 /*}}}*/
739 // URI::operator string - Convert the URI to a string /*{{{*/
740 // ---------------------------------------------------------------------
741 /* */
742 URI::operator string()
743 {
744 string Res = Access + ':';
745 if (Host.empty() == false)
746 {
747 Res += "//";
748 if (User.empty() == false)
749 {
750 Res += "//" + User;
751 if (Password.empty() == false)
752 Res += ":" + Password;
753 Res += "@";
754 }
755 Res += Host;
756 if (Port != 0)
757 {
758 char S[30];
759 sprintf(S,":%u",Port);
760 Res += S;
761 }
762 }
763
764 if (Path.empty() == false)
765 {
766 if (Path[0] != '/')
767 Res += "/" + Path;
768 else
769 Res += Path;
770 }
771
772 return Res;
773 }
774 /*}}}*/