]> git.decadent.org.uk Git - dak.git/blob - tools/dsync-0.0/libdsync/contrib/strutl.cc
Merge mainline
[dak.git] / tools / dsync-0.0 / libdsync / contrib / strutl.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description                                                          /*{{{*/
3 // $Id: strutl.cc,v 1.4 1999/10/24 06:53:12 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 "dsync/strutl.h"
20 #endif
21
22 #include <dsync/strutl.h>
23 #include <dsync/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       const char *i = Message.c_str() + (I - Message.begin());
454       if (I[Length] == ':' && stringcasecmp(i,i+Length,Tag) == 0)
455       {
456          // Find the end of line and strip the leading/trailing spaces
457          string::iterator J;
458          I += Length + 1;
459          for (; isspace(*I) != 0 && I < Message.end(); I++);
460          for (J = I; *J != '\n' && J < Message.end(); J++);
461          for (; J > I && isspace(J[-1]) != 0; J--);
462          
463          return string(i,J-I);
464       }
465       
466       for (; *I != '\n' && I < Message.end(); I++);
467    }   
468    
469    // Failed to find a match
470    if (Default == 0)
471       return string();
472    return Default;
473 }
474                                                                         /*}}}*/
475 // StringToBool - Converts a string into a boolean                      /*{{{*/
476 // ---------------------------------------------------------------------
477 /* This inspects the string to see if it is true or if it is false and
478    then returns the result. Several varients on true/false are checked. */
479 int StringToBool(string Text,int Default)
480 {
481    char *End;
482    int Res = strtol(Text.c_str(),&End,0);   
483    if (End != Text.c_str() && Res >= 0 && Res <= 1)
484       return Res;
485    
486    // Check for positives
487    if (strcasecmp(Text.c_str(),"no") == 0 ||
488        strcasecmp(Text.c_str(),"false") == 0 ||
489        strcasecmp(Text.c_str(),"without") == 0 ||
490        strcasecmp(Text.c_str(),"off") == 0 ||
491        strcasecmp(Text.c_str(),"disable") == 0)
492       return 0;
493    
494    // Check for negatives
495    if (strcasecmp(Text.c_str(),"yes") == 0 ||
496        strcasecmp(Text.c_str(),"true") == 0 ||
497        strcasecmp(Text.c_str(),"with") == 0 ||
498        strcasecmp(Text.c_str(),"on") == 0 ||
499        strcasecmp(Text.c_str(),"enable") == 0)
500       return 1;
501    
502    return Default;
503 }
504                                                                         /*}}}*/
505 // TimeRFC1123 - Convert a time_t into RFC1123 format                   /*{{{*/
506 // ---------------------------------------------------------------------
507 /* This converts a time_t into a string time representation that is
508    year 2000 complient and timezone neutral */
509 string TimeRFC1123(time_t Date)
510 {
511    struct tm Conv = *gmtime(&Date);
512    char Buf[300];
513
514    const char *Day[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
515    const char *Month[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul",
516                           "Aug","Sep","Oct","Nov","Dec"};
517
518    sprintf(Buf,"%s, %02i %s %i %02i:%02i:%02i GMT",Day[Conv.tm_wday],
519            Conv.tm_mday,Month[Conv.tm_mon],Conv.tm_year+1900,Conv.tm_hour,
520            Conv.tm_min,Conv.tm_sec);
521    return Buf;
522 }
523                                                                         /*}}}*/
524 // ReadMessages - Read messages from the FD                             /*{{{*/
525 // ---------------------------------------------------------------------
526 /* This pulls full messages from the input FD into the message buffer. 
527    It assumes that messages will not pause during transit so no
528    fancy buffering is used. */
529 bool ReadMessages(int Fd, vector<string> &List)
530 {
531    char Buffer[4000];
532    char *End = Buffer;
533    
534    while (1)
535    {
536       int Res = read(Fd,End,sizeof(Buffer) - (End-Buffer));
537       if (Res < 0 && errno == EINTR)
538          continue;
539       
540       // Process is dead, this is kind of bad..
541       if (Res == 0)
542          return false;
543       
544       // No data
545       if (Res <= 0)
546          return true;
547
548       End += Res;
549       
550       // Look for the end of the message
551       for (char *I = Buffer; I + 1 < End; I++)
552       {
553          if (I[0] != '\n' || I[1] != '\n')
554             continue;
555          
556          // Pull the message out
557          string Message(Buffer,0,I-Buffer);
558
559          // Fix up the buffer
560          for (; I < End && *I == '\n'; I++);
561          End -= I-Buffer;        
562          memmove(Buffer,I,End-Buffer);
563          I = Buffer;
564          
565          List.push_back(Message);
566       }
567       if (End == Buffer)
568          return true;
569
570       if (WaitFd(Fd) == false)
571          return false;
572    }   
573 }
574                                                                         /*}}}*/
575 // MonthConv - Converts a month string into a number                    /*{{{*/
576 // ---------------------------------------------------------------------
577 /* This was lifted from the boa webserver which lifted it from 'wn-v1.07'
578    Made it a bit more robust with a few touppers though. */
579 static int MonthConv(char *Month)
580 {
581    switch (toupper(*Month)) 
582    {
583       case 'A':
584       return toupper(Month[1]) == 'P'?3:7;
585       case 'D':
586       return 11;
587       case 'F':
588       return 1;
589       case 'J':
590       if (toupper(Month[1]) == 'A')
591          return 0;
592       return toupper(Month[2]) == 'N'?5:6;
593       case 'M':
594       return toupper(Month[2]) == 'R'?2:4;
595       case 'N':
596       return 10;
597       case 'O':
598       return 9;
599       case 'S':
600       return 8;
601
602       // Pretend it is January..
603       default:
604       return 0;
605    }   
606 }
607                                                                         /*}}}*/
608 // timegm - Internal timegm function if gnu is not available            /*{{{*/
609 // ---------------------------------------------------------------------
610 /* Ripped this evil little function from wget - I prefer the use of 
611    GNU timegm if possible as this technique will have interesting problems
612    with leap seconds, timezones and other.
613    
614    Converts struct tm to time_t, assuming the data in tm is UTC rather
615    than local timezone (mktime assumes the latter).
616    
617    Contributed by Roger Beeman <beeman@cisco.com>, with the help of
618    Mark Baushke <mdb@cisco.com> and the rest of the Gurus at CISCO. */
619 #ifndef __USE_MISC        // glib sets this
620 static time_t timegm(struct tm *t)
621 {
622    time_t tl, tb;
623    
624    tl = mktime (t);
625    if (tl == -1)
626       return -1;
627    tb = mktime (gmtime (&tl));
628    return (tl <= tb ? (tl + (tl - tb)) : (tl - (tb - tl)));
629 }
630 #endif
631                                                                         /*}}}*/
632 // StrToTime - Converts a string into a time_t                          /*{{{*/
633 // ---------------------------------------------------------------------
634 /* This handles all 3 populare time formats including RFC 1123, RFC 1036
635    and the C library asctime format. It requires the GNU library function
636    'timegm' to convert a struct tm in UTC to a time_t. For some bizzar
637    reason the C library does not provide any such function :<*/
638 bool StrToTime(string Val,time_t &Result)
639 {
640    struct tm Tm;
641    char Month[10];
642    const char *I = Val.c_str();
643    
644    // Skip the day of the week
645    for (;*I != 0  && *I != ' '; I++);
646    
647    // Handle RFC 1123 time
648    if (sscanf(I," %d %3s %d %d:%d:%d GMT",&Tm.tm_mday,Month,&Tm.tm_year,
649               &Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec) != 6)
650    {
651       // Handle RFC 1036 time
652       if (sscanf(I," %d-%3s-%d %d:%d:%d GMT",&Tm.tm_mday,Month,
653                  &Tm.tm_year,&Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec) == 6)
654          Tm.tm_year += 1900;
655       else
656       {
657          // asctime format
658          if (sscanf(I," %3s %d %d:%d:%d %d",Month,&Tm.tm_mday,
659                     &Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec,&Tm.tm_year) != 6)
660             return false;
661       }
662    }
663    
664    Tm.tm_isdst = 0;
665    Tm.tm_mon = MonthConv(Month);
666    Tm.tm_year -= 1900;
667    
668    // Convert to local time and then to GMT
669    Result = timegm(&Tm);
670    return true;
671 }
672                                                                         /*}}}*/
673 // StrToNum - Convert a fixed length string to a number                 /*{{{*/
674 // ---------------------------------------------------------------------
675 /* This is used in decoding the crazy fixed length string headers in 
676    tar and ar files. */
677 bool StrToNum(const char *Str,unsigned long &Res,unsigned Len,unsigned Base)
678 {
679    char S[30];
680    if (Len >= sizeof(S))
681       return false;
682    memcpy(S,Str,Len);
683    S[Len] = 0;
684    
685    // All spaces is a zero
686    Res = 0;
687    unsigned I;
688    for (I = 0; S[I] == ' '; I++);
689    if (S[I] == 0)
690       return true;
691    
692    char *End;
693    Res = strtoul(S,&End,Base);
694    if (End == S)
695       return false;
696    
697    return true;
698 }
699                                                                         /*}}}*/
700 // HexDigit - Convert a hex character into an integer                   /*{{{*/
701 // ---------------------------------------------------------------------
702 /* Helper for Hex2Num */
703 static int HexDigit(int c)
704 {   
705    if (c >= '0' && c <= '9')
706       return c - '0';
707    if (c >= 'a' && c <= 'f')
708       return c - 'a' + 10;
709    if (c >= 'A' && c <= 'F')
710       return c - 'A' + 10;
711    return 0;
712 }
713                                                                         /*}}}*/
714 // Hex2Num - Convert a long hex number into a buffer                    /*{{{*/
715 // ---------------------------------------------------------------------
716 /* The length of the buffer must be exactly 1/2 the length of the string. */
717 bool Hex2Num(const char *Start,const char *End,unsigned char *Num,
718              unsigned int Length)
719 {
720    if (End - Start != (signed)(Length*2))
721       return false;
722    
723    // Convert each digit. We store it in the same order as the string
724    int J = 0;
725    for (const char *I = Start; I < End;J++, I += 2)
726    {
727       if (isxdigit(*I) == 0 || isxdigit(I[1]) == 0)
728          return false;
729       
730       Num[J] = HexDigit(I[0]) << 4;
731       Num[J] += HexDigit(I[1]);
732    }
733    
734    return true;
735 }
736                                                                         /*}}}*/
737
738 // URI::CopyFrom - Copy from an object                                  /*{{{*/
739 // ---------------------------------------------------------------------
740 /* This parses the URI into all of its components */
741 void URI::CopyFrom(string U)
742 {
743    string::const_iterator I = U.begin();
744
745    // Locate the first colon, this seperates the scheme
746    for (; I < U.end() && *I != ':' ; I++);
747    string::const_iterator FirstColon = I;
748
749    /* Determine if this is a host type URI with a leading double //
750       and then search for the first single / */
751    string::const_iterator SingleSlash = I;
752    if (I + 3 < U.end() && I[1] == '/' && I[2] == '/')
753       SingleSlash += 3;
754    for (; SingleSlash < U.end() && *SingleSlash != '/'; SingleSlash++);
755    if (SingleSlash > U.end())
756       SingleSlash = U.end();
757
758    // We can now write the access and path specifiers
759    Access = string(U,0,FirstColon - U.begin());
760    if (SingleSlash != U.end())
761       Path = string(U,SingleSlash - U.begin());
762    if (Path.empty() == true)
763       Path = "/";
764
765    // Now we attempt to locate a user:pass@host fragment
766    if (FirstColon[1] == '/' && FirstColon[2] == '/')
767       FirstColon += 3;
768    else
769       FirstColon += 1;
770    if (FirstColon >= U.end())
771       return;
772    
773    if (FirstColon > SingleSlash)
774       FirstColon = SingleSlash;
775    
776    // Find the colon...
777    I = FirstColon + 1;
778    if (I > SingleSlash)
779       I = SingleSlash;
780    for (; I < SingleSlash && *I != ':'; I++);
781    string::const_iterator SecondColon = I;
782    
783    // Search for the @ after the colon
784    for (; I < SingleSlash && *I != '@'; I++);
785    string::const_iterator At = I;
786    
787    // Now write the host and user/pass
788    if (At == SingleSlash)
789    {
790       if (FirstColon < SingleSlash)
791          Host = string(U,FirstColon - U.begin(),SingleSlash - FirstColon);
792    }
793    else
794    {
795       Host = string(U,At - U.begin() + 1,SingleSlash - At - 1);
796       User = string(U,FirstColon - U.begin(),SecondColon - FirstColon);
797       if (SecondColon < At)
798          Password = string(U,SecondColon - U.begin() + 1,At - SecondColon - 1);
799    }   
800    
801    // Now we parse off a port number from the hostname
802    Port = 0;
803    string::size_type Pos = Host.rfind(':');
804    if (Pos == string::npos)
805       return;
806    
807    Port = atoi(string(Host,Pos+1).c_str());
808    Host = string(Host,0,Pos);
809 }
810                                                                         /*}}}*/
811 // URI::operator string - Convert the URI to a string                   /*{{{*/
812 // ---------------------------------------------------------------------
813 /* */
814 URI::operator string()
815 {
816    string Res;
817    
818    if (Access.empty() == false)
819       Res = Access + ':';
820    
821    if (Host.empty() == false)
822    {
823       if (Access.empty() == false)
824          Res += "//";
825       
826       if (User.empty() == false)
827       {
828          Res +=  User;
829          if (Password.empty() == false)
830             Res += ":" + Password;
831          Res += "@";
832       }
833       
834       Res += Host;
835       if (Port != 0)
836       {
837          char S[30];
838          sprintf(S,":%u",Port);
839          Res += S;
840       }  
841    }
842    
843    if (Path.empty() == false)
844    {
845       if (Path[0] != '/')
846          Res += "/" + Path;
847       else
848          Res += Path;
849    }
850    
851    return Res;
852 }
853                                                                         /*}}}*/