- various "cosmetics" cleanups to shut up gcc complaints on higher warning
[apt.git] / apt-pkg / contrib / strutl.cc
1 // -*- mode: c++; mode: fold -*-
2 // Description                                                          /*{{{*/
3 // $Id: strutl.cc,v 1.48 2003/07/18 14:15:11 mdz Exp $
4 /* ######################################################################
5
6    String Util - Some useful string functions.
7
8    These have been collected from here and there to do all sorts of useful
9    things to strings. They are useful in file parsers, URI handlers and
10    especially in APT methods.   
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 #include <apt-pkg/error.h>
25
26 #include <apti18n.h>
27     
28 #include <ctype.h>
29 #include <string.h>
30 #include <stdio.h>
31 #include <unistd.h>
32 #include <regex.h>
33 #include <errno.h>
34 #include <stdarg.h>
35
36 #include "config.h"
37
38 using namespace std;
39                                                                         /*}}}*/
40
41 // strstrip - Remove white space from the front and back of a string    /*{{{*/
42 // ---------------------------------------------------------------------
43 /* This is handy to use when parsing a file. It also removes \n's left 
44    over from fgets and company */
45 char *_strstrip(char *String)
46 {
47    for (;*String != 0 && (*String == ' ' || *String == '\t'); String++);
48
49    if (*String == 0)
50       return String;
51
52    char *End = String + strlen(String) - 1;
53    for (;End != String - 1 && (*End == ' ' || *End == '\t' || *End == '\n' ||
54                                *End == '\r'); End--);
55    End++;
56    *End = 0;
57    return String;
58 }
59                                                                         /*}}}*/
60 // strtabexpand - Converts tabs into 8 spaces                           /*{{{*/
61 // ---------------------------------------------------------------------
62 /* */
63 char *_strtabexpand(char *String,size_t Len)
64 {
65    for (char *I = String; I != I + Len && *I != 0; I++)
66    {
67       if (*I != '\t')
68          continue;
69       if (I + 8 > String + Len)
70       {
71          *I = 0;
72          return String;
73       }
74
75       /* Assume the start of the string is 0 and find the next 8 char
76          division */
77       int Len;
78       if (String == I)
79          Len = 1;
80       else
81          Len = 8 - ((String - I) % 8);
82       Len -= 2;
83       if (Len <= 0)
84       {
85          *I = ' ';
86          continue;
87       }
88       
89       memmove(I + Len,I + 1,strlen(I) + 1);
90       for (char *J = I; J + Len != I; *I = ' ', I++);
91    }
92    return String;
93 }
94                                                                         /*}}}*/
95 // ParseQuoteWord - Parse a single word out of a string                 /*{{{*/
96 // ---------------------------------------------------------------------
97 /* This grabs a single word, converts any % escaped characters to their
98    proper values and advances the pointer. Double quotes are understood
99    and striped out as well. This is for URI/URL parsing. It also can 
100    understand [] brackets.*/
101 bool ParseQuoteWord(const char *&String,string &Res)
102 {
103    // Skip leading whitespace
104    const char *C = String;
105    for (;*C != 0 && *C == ' '; C++);
106    if (*C == 0)
107       return false;
108    
109    // Jump to the next word
110    for (;*C != 0 && isspace(*C) == 0; C++)
111    {
112       if (*C == '"')
113       {
114          for (C++; *C != 0 && *C != '"'; C++);
115          if (*C == 0)
116             return false;
117       }
118       if (*C == '[')
119       {
120          for (C++; *C != 0 && *C != ']'; C++);
121          if (*C == 0)
122             return false;
123       }
124    }
125
126    // Now de-quote characters
127    char Buffer[1024];
128    char Tmp[3];
129    const char *Start = String;
130    char *I;
131    for (I = Buffer; I < Buffer + sizeof(Buffer) && Start != C; I++)
132    {
133       if (*Start == '%' && Start + 2 < C)
134       {
135          Tmp[0] = Start[1];
136          Tmp[1] = Start[2];
137          Tmp[2] = 0;
138          *I = (char)strtol(Tmp,0,16);
139          Start += 3;
140          continue;
141       }
142       if (*Start != '"')
143          *I = *Start;
144       else
145          I--;
146       Start++;
147    }
148    *I = 0;
149    Res = Buffer;
150    
151    // Skip ending white space
152    for (;*C != 0 && isspace(*C) != 0; C++);
153    String = C;
154    return true;
155 }
156                                                                         /*}}}*/
157 // ParseCWord - Parses a string like a C "" expression                  /*{{{*/
158 // ---------------------------------------------------------------------
159 /* This expects a series of space separated strings enclosed in ""'s. 
160    It concatenates the ""'s into a single string. */
161 bool ParseCWord(const char *&String,string &Res)
162 {
163    // Skip leading whitespace
164    const char *C = String;
165    for (;*C != 0 && *C == ' '; C++);
166    if (*C == 0)
167       return false;
168    
169    char Buffer[1024];
170    char *Buf = Buffer;
171    if (strlen(String) >= sizeof(Buffer))
172        return false;
173        
174    for (; *C != 0; C++)
175    {
176       if (*C == '"')
177       {
178          for (C++; *C != 0 && *C != '"'; C++)
179             *Buf++ = *C;
180          
181          if (*C == 0)
182             return false;
183          
184          continue;
185       }      
186       
187       if (C != String && isspace(*C) != 0 && isspace(C[-1]) != 0)
188          continue;
189       if (isspace(*C) == 0)
190          return false;
191       *Buf++ = ' ';
192    }
193    *Buf = 0;
194    Res = Buffer;
195    String = C;
196    return true;
197 }
198                                                                         /*}}}*/
199 // QuoteString - Convert a string into quoted from                      /*{{{*/
200 // ---------------------------------------------------------------------
201 /* */
202 string QuoteString(string Str,const char *Bad)
203 {
204    string Res;
205    for (string::iterator I = Str.begin(); I != Str.end(); I++)
206    {
207       if (strchr(Bad,*I) != 0 || isprint(*I) == 0 || 
208           *I <= 0x20 || *I >= 0x7F)
209       {
210          char Buf[10];
211          sprintf(Buf,"%%%02x",(int)*I);
212          Res += Buf;
213       }
214       else
215          Res += *I;
216    }
217    return Res;
218 }
219                                                                         /*}}}*/
220 // DeQuoteString - Convert a string from quoted from                    /*{{{*/
221 // ---------------------------------------------------------------------
222 /* This undoes QuoteString */
223 string DeQuoteString(string Str)
224 {
225    string Res;
226    for (string::const_iterator I = Str.begin(); I != Str.end(); I++)
227    {
228       if (*I == '%' && I + 2 < Str.end())
229       {
230          char Tmp[3];
231          Tmp[0] = I[1];
232          Tmp[1] = I[2];
233          Tmp[2] = 0;
234          Res += (char)strtol(Tmp,0,16);
235          I += 2;
236          continue;
237       }
238       else
239          Res += *I;
240    }
241    return Res;   
242 }
243
244                                                                         /*}}}*/
245 // SizeToStr - Convert a long into a human readable size                /*{{{*/
246 // ---------------------------------------------------------------------
247 /* A max of 4 digits are shown before conversion to the next highest unit. 
248    The max length of the string will be 5 chars unless the size is > 10
249    YottaBytes (E24) */
250 string SizeToStr(double Size)
251 {
252    char S[300];
253    double ASize;
254    if (Size >= 0)
255       ASize = Size;
256    else
257       ASize = -1*Size;
258    
259    /* bytes, KiloBytes, MegaBytes, GigaBytes, TeraBytes, PetaBytes, 
260       ExaBytes, ZettaBytes, YottaBytes */
261    char Ext[] = {'\0','k','M','G','T','P','E','Z','Y'};
262    int I = 0;
263    while (I <= 8)
264    {
265       if (ASize < 100 && I != 0)
266       {
267          sprintf(S,"%.1f%c",ASize,Ext[I]);
268          break;
269       }
270       
271       if (ASize < 10000)
272       {
273          sprintf(S,"%.0f%c",ASize,Ext[I]);
274          break;
275       }
276       ASize /= 1000.0;
277       I++;
278    }
279    
280    return S;
281 }
282                                                                         /*}}}*/
283 // TimeToStr - Convert the time into a string                           /*{{{*/
284 // ---------------------------------------------------------------------
285 /* Converts a number of seconds to a hms format */
286 string TimeToStr(unsigned long Sec)
287 {
288    char S[300];
289    
290    while (1)
291    {
292       if (Sec > 60*60*24)
293       {
294          sprintf(S,"%lid %lih%lim%lis",Sec/60/60/24,(Sec/60/60) % 24,(Sec/60) % 60,Sec % 60);
295          break;
296       }
297       
298       if (Sec > 60*60)
299       {
300          sprintf(S,"%lih%lim%lis",Sec/60/60,(Sec/60) % 60,Sec % 60);
301          break;
302       }
303       
304       if (Sec > 60)
305       {
306          sprintf(S,"%lim%lis",Sec/60,Sec % 60);
307          break;
308       }
309       
310       sprintf(S,"%lis",Sec);
311       break;
312    }
313    
314    return S;
315 }
316                                                                         /*}}}*/
317 // SubstVar - Substitute a string for another string                    /*{{{*/
318 // ---------------------------------------------------------------------
319 /* This replaces all occurances of Subst with Contents in Str. */
320 string SubstVar(string Str,string Subst,string Contents)
321 {
322    string::size_type Pos = 0;
323    string::size_type OldPos = 0;
324    string Temp;
325    
326    while (OldPos < Str.length() && 
327           (Pos = Str.find(Subst,OldPos)) != string::npos)
328    {
329       Temp += string(Str,OldPos,Pos) + Contents;
330       OldPos = Pos + Subst.length();      
331    }
332    
333    if (OldPos == 0)
334       return Str;
335    
336    return Temp + string(Str,OldPos);
337 }
338
339 string SubstVar(string Str,const struct SubstVar *Vars)
340 {
341    for (; Vars->Subst != 0; Vars++)
342       Str = SubstVar(Str,Vars->Subst,*Vars->Contents);
343    return Str;
344 }
345                                                                         /*}}}*/
346 // URItoFileName - Convert the uri into a unique file name              /*{{{*/
347 // ---------------------------------------------------------------------
348 /* This converts a URI into a safe filename. It quotes all unsafe characters
349    and converts / to _ and removes the scheme identifier. The resulting
350    file name should be unique and never occur again for a different file */
351 string URItoFileName(string URI)
352 {
353    // Nuke 'sensitive' items
354    ::URI U(URI);
355    U.User = string();
356    U.Password = string();
357    U.Access = "";
358    
359    // "\x00-\x20{}|\\\\^\\[\\]<>\"\x7F-\xFF";
360    URI = QuoteString(U,"\\|{}[]<>\"^~_=!@#$%^&*");
361    string::iterator J = URI.begin();
362    for (; J != URI.end(); J++)
363       if (*J == '/') 
364          *J = '_';
365    return URI;
366 }
367                                                                         /*}}}*/
368 // Base64Encode - Base64 Encoding routine for short strings             /*{{{*/
369 // ---------------------------------------------------------------------
370 /* This routine performs a base64 transformation on a string. It was ripped
371    from wget and then patched and bug fixed.
372  
373    This spec can be found in rfc2045 */
374 string Base64Encode(string S)
375 {
376    // Conversion table.
377    static char tbl[64] = {'A','B','C','D','E','F','G','H',
378                           'I','J','K','L','M','N','O','P',
379                           'Q','R','S','T','U','V','W','X',
380                           'Y','Z','a','b','c','d','e','f',
381                           'g','h','i','j','k','l','m','n',
382                           'o','p','q','r','s','t','u','v',
383                           'w','x','y','z','0','1','2','3',
384                           '4','5','6','7','8','9','+','/'};
385    
386    // Pre-allocate some space
387    string Final;
388    Final.reserve((4*S.length() + 2)/3 + 2);
389
390    /* Transform the 3x8 bits to 4x6 bits, as required by
391       base64.  */
392    for (string::const_iterator I = S.begin(); I < S.end(); I += 3)
393    {
394       char Bits[3] = {0,0,0};
395       Bits[0] = I[0];
396       if (I + 1 < S.end())
397          Bits[1] = I[1];
398       if (I + 2 < S.end())
399          Bits[2] = I[2];
400
401       Final += tbl[Bits[0] >> 2];
402       Final += tbl[((Bits[0] & 3) << 4) + (Bits[1] >> 4)];
403       
404       if (I + 1 >= S.end())
405          break;
406       
407       Final += tbl[((Bits[1] & 0xf) << 2) + (Bits[2] >> 6)];
408       
409       if (I + 2 >= S.end())
410          break;
411       
412       Final += tbl[Bits[2] & 0x3f];
413    }
414
415    /* Apply the padding elements, this tells how many bytes the remote
416       end should discard */
417    if (S.length() % 3 == 2)
418       Final += '=';
419    if (S.length() % 3 == 1)
420       Final += "==";
421    
422    return Final;
423 }
424                                                                         /*}}}*/
425 // stringcmp - Arbitary string compare                                  /*{{{*/
426 // ---------------------------------------------------------------------
427 /* This safely compares two non-null terminated strings of arbitary 
428    length */
429 int stringcmp(const char *A,const char *AEnd,const char *B,const char *BEnd)
430 {
431    for (; A != AEnd && B != BEnd; A++, B++)
432       if (*A != *B)
433          break;
434    
435    if (A == AEnd && B == BEnd)
436       return 0;
437    if (A == AEnd)
438       return 1;
439    if (B == BEnd)
440       return -1;
441    if (*A < *B)
442       return -1;
443    return 1;
444 }
445
446 #if __GNUC__ >= 3
447 int stringcmp(string::const_iterator A,string::const_iterator AEnd,
448               const char *B,const char *BEnd)
449 {
450    for (; A != AEnd && B != BEnd; A++, B++)
451       if (*A != *B)
452          break;
453    
454    if (A == AEnd && B == BEnd)
455       return 0;
456    if (A == AEnd)
457       return 1;
458    if (B == BEnd)
459       return -1;
460    if (*A < *B)
461       return -1;
462    return 1;
463 }
464 int stringcmp(string::const_iterator A,string::const_iterator AEnd,
465               string::const_iterator B,string::const_iterator BEnd)
466 {
467    for (; A != AEnd && B != BEnd; A++, B++)
468       if (*A != *B)
469          break;
470    
471    if (A == AEnd && B == BEnd)
472       return 0;
473    if (A == AEnd)
474       return 1;
475    if (B == BEnd)
476       return -1;
477    if (*A < *B)
478       return -1;
479    return 1;
480 }
481 #endif
482                                                                         /*}}}*/
483 // stringcasecmp - Arbitary case insensitive string compare             /*{{{*/
484 // ---------------------------------------------------------------------
485 /* */
486 int stringcasecmp(const char *A,const char *AEnd,const char *B,const char *BEnd)
487 {
488    for (; A != AEnd && B != BEnd; A++, B++)
489       if (toupper(*A) != toupper(*B))
490          break;
491
492    if (A == AEnd && B == BEnd)
493       return 0;
494    if (A == AEnd)
495       return 1;
496    if (B == BEnd)
497       return -1;
498    if (toupper(*A) < toupper(*B))
499       return -1;
500    return 1;
501 }
502 #if __GNUC__ >= 3
503 int stringcasecmp(string::const_iterator A,string::const_iterator AEnd,
504                   const char *B,const char *BEnd)
505 {
506    for (; A != AEnd && B != BEnd; A++, B++)
507       if (toupper(*A) != toupper(*B))
508          break;
509
510    if (A == AEnd && B == BEnd)
511       return 0;
512    if (A == AEnd)
513       return 1;
514    if (B == BEnd)
515       return -1;
516    if (toupper(*A) < toupper(*B))
517       return -1;
518    return 1;
519 }
520 int stringcasecmp(string::const_iterator A,string::const_iterator AEnd,
521                   string::const_iterator B,string::const_iterator BEnd)
522 {
523    for (; A != AEnd && B != BEnd; A++, B++)
524       if (toupper(*A) != toupper(*B))
525          break;
526
527    if (A == AEnd && B == BEnd)
528       return 0;
529    if (A == AEnd)
530       return 1;
531    if (B == BEnd)
532       return -1;
533    if (toupper(*A) < toupper(*B))
534       return -1;
535    return 1;
536 }
537 #endif
538                                                                         /*}}}*/
539 // LookupTag - Lookup the value of a tag in a taged string              /*{{{*/
540 // ---------------------------------------------------------------------
541 /* The format is like those used in package files and the method 
542    communication system */
543 string LookupTag(string Message,const char *Tag,const char *Default)
544 {
545    // Look for a matching tag.
546    size_t Length = strlen(Tag);
547    for (string::iterator I = Message.begin(); I + Length < Message.end(); I++)
548    {
549       // Found the tag
550       if (I[Length] == ':' && stringcasecmp(I,I+Length,Tag) == 0)
551       {
552          // Find the end of line and strip the leading/trailing spaces
553          string::iterator J;
554          I += Length + 1;
555          for (; isspace(*I) != 0 && I < Message.end(); I++);
556          for (J = I; *J != '\n' && J < Message.end(); J++);
557          for (; J > I && isspace(J[-1]) != 0; J--);
558          
559          return string(I,J);
560       }
561       
562       for (; *I != '\n' && I < Message.end(); I++);
563    }   
564    
565    // Failed to find a match
566    if (Default == 0)
567       return string();
568    return Default;
569 }
570                                                                         /*}}}*/
571 // StringToBool - Converts a string into a boolean                      /*{{{*/
572 // ---------------------------------------------------------------------
573 /* This inspects the string to see if it is true or if it is false and
574    then returns the result. Several varients on true/false are checked. */
575 int StringToBool(string Text,int Default)
576 {
577    char *End;
578    int Res = strtol(Text.c_str(),&End,0);   
579    if (End != Text.c_str() && Res >= 0 && Res <= 1)
580       return Res;
581    
582    // Check for positives
583    if (strcasecmp(Text.c_str(),"no") == 0 ||
584        strcasecmp(Text.c_str(),"false") == 0 ||
585        strcasecmp(Text.c_str(),"without") == 0 ||
586        strcasecmp(Text.c_str(),"off") == 0 ||
587        strcasecmp(Text.c_str(),"disable") == 0)
588       return 0;
589    
590    // Check for negatives
591    if (strcasecmp(Text.c_str(),"yes") == 0 ||
592        strcasecmp(Text.c_str(),"true") == 0 ||
593        strcasecmp(Text.c_str(),"with") == 0 ||
594        strcasecmp(Text.c_str(),"on") == 0 ||
595        strcasecmp(Text.c_str(),"enable") == 0)
596       return 1;
597    
598    return Default;
599 }
600                                                                         /*}}}*/
601 // TimeRFC1123 - Convert a time_t into RFC1123 format                   /*{{{*/
602 // ---------------------------------------------------------------------
603 /* This converts a time_t into a string time representation that is
604    year 2000 complient and timezone neutral */
605 string TimeRFC1123(time_t Date)
606 {
607    struct tm Conv = *gmtime(&Date);
608    char Buf[300];
609
610    const char *Day[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
611    const char *Month[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul",
612                           "Aug","Sep","Oct","Nov","Dec"};
613
614    sprintf(Buf,"%s, %02i %s %i %02i:%02i:%02i GMT",Day[Conv.tm_wday],
615            Conv.tm_mday,Month[Conv.tm_mon],Conv.tm_year+1900,Conv.tm_hour,
616            Conv.tm_min,Conv.tm_sec);
617    return Buf;
618 }
619                                                                         /*}}}*/
620 // ReadMessages - Read messages from the FD                             /*{{{*/
621 // ---------------------------------------------------------------------
622 /* This pulls full messages from the input FD into the message buffer. 
623    It assumes that messages will not pause during transit so no
624    fancy buffering is used. */
625 bool ReadMessages(int Fd, vector<string> &List)
626 {
627    char Buffer[64000];
628    char *End = Buffer;
629    
630    while (1)
631    {
632       int Res = read(Fd,End,sizeof(Buffer) - (End-Buffer));
633       if (Res < 0 && errno == EINTR)
634          continue;
635       
636       // Process is dead, this is kind of bad..
637       if (Res == 0)
638          return false;
639       
640       // No data
641       if (Res < 0 && errno == EAGAIN)
642          return true;
643       if (Res < 0)
644          return false;
645                               
646       End += Res;
647       
648       // Look for the end of the message
649       for (char *I = Buffer; I + 1 < End; I++)
650       {
651          if (I[0] != '\n' || I[1] != '\n')
652             continue;
653          
654          // Pull the message out
655          string Message(Buffer,I-Buffer);
656
657          // Fix up the buffer
658          for (; I < End && *I == '\n'; I++);
659          End -= I-Buffer;        
660          memmove(Buffer,I,End-Buffer);
661          I = Buffer;
662          
663          List.push_back(Message);
664       }
665       if (End == Buffer)
666          return true;
667
668       if (WaitFd(Fd) == false)
669          return false;
670    }   
671 }
672                                                                         /*}}}*/
673 // MonthConv - Converts a month string into a number                    /*{{{*/
674 // ---------------------------------------------------------------------
675 /* This was lifted from the boa webserver which lifted it from 'wn-v1.07'
676    Made it a bit more robust with a few touppers though. */
677 static int MonthConv(char *Month)
678 {
679    switch (toupper(*Month)) 
680    {
681       case 'A':
682       return toupper(Month[1]) == 'P'?3:7;
683       case 'D':
684       return 11;
685       case 'F':
686       return 1;
687       case 'J':
688       if (toupper(Month[1]) == 'A')
689          return 0;
690       return toupper(Month[2]) == 'N'?5:6;
691       case 'M':
692       return toupper(Month[2]) == 'R'?2:4;
693       case 'N':
694       return 10;
695       case 'O':
696       return 9;
697       case 'S':
698       return 8;
699
700       // Pretend it is January..
701       default:
702       return 0;
703    }   
704 }
705                                                                         /*}}}*/
706 // timegm - Internal timegm function if gnu is not available            /*{{{*/
707 // ---------------------------------------------------------------------
708 /* Ripped this evil little function from wget - I prefer the use of 
709    GNU timegm if possible as this technique will have interesting problems
710    with leap seconds, timezones and other.
711    
712    Converts struct tm to time_t, assuming the data in tm is UTC rather
713    than local timezone (mktime assumes the latter).
714    
715    Contributed by Roger Beeman <beeman@cisco.com>, with the help of
716    Mark Baushke <mdb@cisco.com> and the rest of the Gurus at CISCO. */
717
718 /* Turned it into an autoconf check, because GNU is not the only thing which
719    can provide timegm. -- 2002-09-22, Joel Baker */
720
721 #ifndef HAVE_TIMEGM // Now with autoconf!
722 static time_t timegm(struct tm *t)
723 {
724    time_t tl, tb;
725    
726    tl = mktime (t);
727    if (tl == -1)
728       return -1;
729    tb = mktime (gmtime (&tl));
730    return (tl <= tb ? (tl + (tl - tb)) : (tl - (tb - tl)));
731 }
732 #endif
733                                                                         /*}}}*/
734 // StrToTime - Converts a string into a time_t                          /*{{{*/
735 // ---------------------------------------------------------------------
736 /* This handles all 3 populare time formats including RFC 1123, RFC 1036
737    and the C library asctime format. It requires the GNU library function
738    'timegm' to convert a struct tm in UTC to a time_t. For some bizzar
739    reason the C library does not provide any such function :< This also
740    handles the weird, but unambiguous FTP time format*/
741 bool StrToTime(string Val,time_t &Result)
742 {
743    struct tm Tm;
744    char Month[10];
745    const char *I = Val.c_str();
746    
747    // Skip the day of the week
748    for (;*I != 0  && *I != ' '; I++);
749    
750    // Handle RFC 1123 time
751    Month[0] = 0;
752    if (sscanf(I," %d %3s %d %d:%d:%d GMT",&Tm.tm_mday,Month,&Tm.tm_year,
753               &Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec) != 6)
754    {
755       // Handle RFC 1036 time
756       if (sscanf(I," %d-%3s-%d %d:%d:%d GMT",&Tm.tm_mday,Month,
757                  &Tm.tm_year,&Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec) == 6)
758          Tm.tm_year += 1900;
759       else
760       {
761          // asctime format
762          if (sscanf(I," %3s %d %d:%d:%d %d",Month,&Tm.tm_mday,
763                     &Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec,&Tm.tm_year) != 6)
764          {
765             // 'ftp' time
766             if (sscanf(Val.c_str(),"%4d%2d%2d%2d%2d%2d",&Tm.tm_year,&Tm.tm_mon,
767                        &Tm.tm_mday,&Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec) != 6)
768                return false;
769             Tm.tm_mon--;
770          }       
771       }
772    }
773    
774    Tm.tm_isdst = 0;
775    if (Month[0] != 0)
776       Tm.tm_mon = MonthConv(Month);
777    Tm.tm_year -= 1900;
778    
779    // Convert to local time and then to GMT
780    Result = timegm(&Tm);
781    return true;
782 }
783                                                                         /*}}}*/
784 // StrToNum - Convert a fixed length string to a number                 /*{{{*/
785 // ---------------------------------------------------------------------
786 /* This is used in decoding the crazy fixed length string headers in 
787    tar and ar files. */
788 bool StrToNum(const char *Str,unsigned long &Res,unsigned Len,unsigned Base)
789 {
790    char S[30];
791    if (Len >= sizeof(S))
792       return false;
793    memcpy(S,Str,Len);
794    S[Len] = 0;
795    
796    // All spaces is a zero
797    Res = 0;
798    unsigned I;
799    for (I = 0; S[I] == ' '; I++);
800    if (S[I] == 0)
801       return true;
802    
803    char *End;
804    Res = strtoul(S,&End,Base);
805    if (End == S)
806       return false;
807    
808    return true;
809 }
810                                                                         /*}}}*/
811 // HexDigit - Convert a hex character into an integer                   /*{{{*/
812 // ---------------------------------------------------------------------
813 /* Helper for Hex2Num */
814 static int HexDigit(int c)
815 {   
816    if (c >= '0' && c <= '9')
817       return c - '0';
818    if (c >= 'a' && c <= 'f')
819       return c - 'a' + 10;
820    if (c >= 'A' && c <= 'F')
821       return c - 'A' + 10;
822    return 0;
823 }
824                                                                         /*}}}*/
825 // Hex2Num - Convert a long hex number into a buffer                    /*{{{*/
826 // ---------------------------------------------------------------------
827 /* The length of the buffer must be exactly 1/2 the length of the string. */
828 bool Hex2Num(string Str,unsigned char *Num,unsigned int Length)
829 {
830    if (Str.length() != Length*2)
831       return false;
832    
833    // Convert each digit. We store it in the same order as the string
834    int J = 0;
835    for (string::const_iterator I = Str.begin(); I != Str.end();J++, I += 2)
836    {
837       if (isxdigit(*I) == 0 || isxdigit(I[1]) == 0)
838          return false;
839       
840       Num[J] = HexDigit(I[0]) << 4;
841       Num[J] += HexDigit(I[1]);
842    }
843    
844    return true;
845 }
846                                                                         /*}}}*/
847 // TokSplitString - Split a string up by a given token                  /*{{{*/
848 // ---------------------------------------------------------------------
849 /* This is intended to be a faster splitter, it does not use dynamic
850    memories. Input is changed to insert nulls at each token location. */
851 bool TokSplitString(char Tok,char *Input,char **List,
852                     unsigned long ListMax)
853 {
854    // Strip any leading spaces
855    char *Start = Input;
856    char *Stop = Start + strlen(Start);
857    for (; *Start != 0 && isspace(*Start) != 0; Start++);
858
859    unsigned long Count = 0;
860    char *Pos = Start;
861    while (Pos != Stop)
862    {
863       // Skip to the next Token
864       for (; Pos != Stop && *Pos != Tok; Pos++);
865       
866       // Back remove spaces
867       char *End = Pos;
868       for (; End > Start && (End[-1] == Tok || isspace(End[-1]) != 0); End--);
869       *End = 0;
870       
871       List[Count++] = Start;
872       if (Count >= ListMax)
873       {
874          List[Count-1] = 0;
875          return false;
876       }
877       
878       // Advance pos
879       for (; Pos != Stop && (*Pos == Tok || isspace(*Pos) != 0 || *Pos == 0); Pos++);
880       Start = Pos;
881    }
882    
883    List[Count] = 0;
884    return true;
885 }
886                                                                         /*}}}*/
887 // RegexChoice - Simple regex list/list matcher                         /*{{{*/
888 // ---------------------------------------------------------------------
889 /* */
890 unsigned long RegexChoice(RxChoiceList *Rxs,const char **ListBegin,
891                       const char **ListEnd)
892 {
893    for (RxChoiceList *R = Rxs; R->Str != 0; R++)
894       R->Hit = false;
895
896    unsigned long Hits = 0;
897    for (; ListBegin != ListEnd; ListBegin++)
898    {
899       // Check if the name is a regex
900       const char *I;
901       bool Regex = true;
902       for (I = *ListBegin; *I != 0; I++)
903          if (*I == '.' || *I == '?' || *I == '*' || *I == '|')
904             break;
905       if (*I == 0)
906          Regex = false;
907          
908       // Compile the regex pattern
909       regex_t Pattern;
910       if (Regex == true)
911          if (regcomp(&Pattern,*ListBegin,REG_EXTENDED | REG_ICASE |
912                      REG_NOSUB) != 0)
913             Regex = false;
914          
915       // Search the list
916       bool Done = false;
917       for (RxChoiceList *R = Rxs; R->Str != 0; R++)
918       {
919          if (R->Str[0] == 0)
920             continue;
921          
922          if (strcasecmp(R->Str,*ListBegin) != 0)
923          {
924             if (Regex == false)
925                continue;
926             if (regexec(&Pattern,R->Str,0,0,0) != 0)
927                continue;
928          }
929          Done = true;
930          
931          if (R->Hit == false)
932             Hits++;
933          
934          R->Hit = true;
935       }
936       
937       if (Regex == true)
938          regfree(&Pattern);
939       
940       if (Done == false)
941          _error->Warning(_("Selection %s not found"),*ListBegin);
942    }
943    
944    return Hits;
945 }
946                                                                         /*}}}*/
947 // ioprintf - C format string outputter to C++ iostreams                /*{{{*/
948 // ---------------------------------------------------------------------
949 /* This is used to make the internationalization strings easier to translate
950    and to allow reordering of parameters */
951 void ioprintf(ostream &out,const char *format,...) 
952 {
953    va_list args;
954    va_start(args,format);
955    
956    // sprintf the description
957    char S[400];
958    vsnprintf(S,sizeof(S),format,args);
959    out << S;
960 }
961                                                                         /*}}}*/
962 // safe_snprintf - Safer snprintf                                       /*{{{*/
963 // ---------------------------------------------------------------------
964 /* This is a snprintf that will never (ever) go past 'End' and returns a
965    pointer to the end of the new string. The returned string is always null
966    terminated unless Buffer == end. This is a better alterantive to using
967    consecutive snprintfs. */
968 char *safe_snprintf(char *Buffer,char *End,const char *Format,...)
969 {
970    va_list args;
971    unsigned long Did;
972
973    va_start(args,Format);
974
975    if (End <= Buffer)
976       return End;
977
978    Did = vsnprintf(Buffer,End - Buffer,Format,args);
979    if (Did < 0 || Buffer + Did > End)
980       return End;
981    return Buffer + Did;
982 }
983                                                                         /*}}}*/
984
985 // CheckDomainList - See if Host is in a , seperate list                /*{{{*/
986 // ---------------------------------------------------------------------
987 /* The domain list is a comma seperate list of domains that are suffix
988    matched against the argument */
989 bool CheckDomainList(string Host,string List)
990 {
991    string::const_iterator Start = List.begin();
992    for (string::const_iterator Cur = List.begin(); Cur <= List.end(); Cur++)
993    {
994       if (Cur < List.end() && *Cur != ',')
995          continue;
996       
997       // Match the end of the string..
998       if ((Host.size() >= (unsigned)(Cur - Start)) &&
999           Cur - Start != 0 &&
1000           stringcasecmp(Host.end() - (Cur - Start),Host.end(),Start,Cur) == 0)
1001          return true;
1002       
1003       Start = Cur + 1;
1004    }
1005    return false;
1006 }
1007                                                                         /*}}}*/
1008
1009 // URI::CopyFrom - Copy from an object                                  /*{{{*/
1010 // ---------------------------------------------------------------------
1011 /* This parses the URI into all of its components */
1012 void URI::CopyFrom(string U)
1013 {
1014    string::const_iterator I = U.begin();
1015
1016    // Locate the first colon, this separates the scheme
1017    for (; I < U.end() && *I != ':' ; I++);
1018    string::const_iterator FirstColon = I;
1019
1020    /* Determine if this is a host type URI with a leading double //
1021       and then search for the first single / */
1022    string::const_iterator SingleSlash = I;
1023    if (I + 3 < U.end() && I[1] == '/' && I[2] == '/')
1024       SingleSlash += 3;
1025    
1026    /* Find the / indicating the end of the hostname, ignoring /'s in the
1027       square brackets */
1028    bool InBracket = false;
1029    for (; SingleSlash < U.end() && (*SingleSlash != '/' || InBracket == true); SingleSlash++)
1030    {
1031       if (*SingleSlash == '[')
1032          InBracket = true;
1033       if (InBracket == true && *SingleSlash == ']')
1034          InBracket = false;
1035    }
1036    
1037    if (SingleSlash > U.end())
1038       SingleSlash = U.end();
1039
1040    // We can now write the access and path specifiers
1041    Access = string(U,0,FirstColon - U.begin());
1042    if (SingleSlash != U.end())
1043       Path = string(U,SingleSlash - U.begin());
1044    if (Path.empty() == true)
1045       Path = "/";
1046
1047    // Now we attempt to locate a user:pass@host fragment
1048    if (FirstColon + 2 <= U.end() && FirstColon[1] == '/' && FirstColon[2] == '/')
1049       FirstColon += 3;
1050    else
1051       FirstColon += 1;
1052    if (FirstColon >= U.end())
1053       return;
1054    
1055    if (FirstColon > SingleSlash)
1056       FirstColon = SingleSlash;
1057    
1058    // Find the colon...
1059    I = FirstColon + 1;
1060    if (I > SingleSlash)
1061       I = SingleSlash;
1062    for (; I < SingleSlash && *I != ':'; I++);
1063    string::const_iterator SecondColon = I;
1064    
1065    // Search for the @ after the colon
1066    for (; I < SingleSlash && *I != '@'; I++);
1067    string::const_iterator At = I;
1068    
1069    // Now write the host and user/pass
1070    if (At == SingleSlash)
1071    {
1072       if (FirstColon < SingleSlash)
1073          Host = string(U,FirstColon - U.begin(),SingleSlash - FirstColon);
1074    }
1075    else
1076    {
1077       Host = string(U,At - U.begin() + 1,SingleSlash - At - 1);
1078       User = string(U,FirstColon - U.begin(),SecondColon - FirstColon);
1079       if (SecondColon < At)
1080          Password = string(U,SecondColon - U.begin() + 1,At - SecondColon - 1);
1081    }   
1082    
1083    // Now we parse the RFC 2732 [] hostnames.
1084    string::size_type PortEnd = 0;
1085    InBracket = false;
1086    for (string::size_type  I = 0; I != Host.length();)
1087    {
1088       if (Host[I] == '[')
1089       {
1090          InBracket = true;
1091          Host.erase(I,1);
1092          continue;
1093       }
1094       
1095       if (InBracket == true && Host[I] == ']')
1096       {
1097          InBracket = false;
1098          Host.erase(I,1);
1099          PortEnd = I;
1100          continue;
1101       }
1102       I++;
1103    }
1104    
1105    // Tsk, weird.
1106    if (InBracket == true)
1107    {
1108       Host = string();
1109       return;
1110    }
1111    
1112    // Now we parse off a port number from the hostname
1113    Port = 0;
1114    string::size_type Pos = Host.rfind(':');
1115    if (Pos == string::npos || Pos < PortEnd)
1116       return;
1117    
1118    Port = atoi(string(Host,Pos+1).c_str());
1119    Host = string(Host,0,Pos);
1120 }
1121                                                                         /*}}}*/
1122 // URI::operator string - Convert the URI to a string                   /*{{{*/
1123 // ---------------------------------------------------------------------
1124 /* */
1125 URI::operator string()
1126 {
1127    string Res;
1128    
1129    if (Access.empty() == false)
1130       Res = Access + ':';
1131    
1132    if (Host.empty() == false)
1133    {     
1134       if (Access.empty() == false)
1135          Res += "//";
1136           
1137       if (User.empty() == false)
1138       {
1139          Res +=  User;
1140          if (Password.empty() == false)
1141             Res += ":" + Password;
1142          Res += "@";
1143       }
1144       
1145       // Add RFC 2732 escaping characters
1146       if (Access.empty() == false &&
1147           (Host.find('/') != string::npos || Host.find(':') != string::npos))
1148          Res += '[' + Host + ']';
1149       else
1150          Res += Host;
1151       
1152       if (Port != 0)
1153       {
1154          char S[30];
1155          sprintf(S,":%u",Port);
1156          Res += S;
1157       }  
1158    }
1159    
1160    if (Path.empty() == false)
1161    {
1162       if (Path[0] != '/')
1163          Res += "/" + Path;
1164       else
1165          Res += Path;
1166    }
1167    
1168    return Res;
1169 }
1170                                                                         /*}}}*/
1171 // URI::SiteOnly - Return the schema and site for the URI               /*{{{*/
1172 // ---------------------------------------------------------------------
1173 /* */
1174 string URI::SiteOnly(string URI)
1175 {
1176    ::URI U(URI);
1177    U.User = string();
1178    U.Password = string();
1179    U.Path = string();
1180    U.Port = 0;
1181    return U;
1182 }
1183                                                                         /*}}}*/