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