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