1 // -*- mode: cpp; mode: fold -*-
3 // $Id: strutl.cc,v 1.48 2003/07/18 14:15:11 mdz Exp $
4 /* ######################################################################
6 String Util - Some useful string functions.
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.
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>
15 ##################################################################### */
20 #include <apt-pkg/strutl.h>
21 #include <apt-pkg/fileutl.h>
22 #include <apt-pkg/error.h>
44 // Strip - Remove white space from the front and back of a string /*{{{*/
45 // ---------------------------------------------------------------------
48 std::string
Strip(const std::string
&s
)
50 size_t start
= s
.find_first_not_of(" \t\n");
52 if (start
== string::npos
)
54 size_t end
= s
.find_last_not_of(" \t\n");
55 return s
.substr(start
, end
-start
+1);
58 bool Endswith(const std::string
&s
, const std::string
&end
)
60 if (end
.size() > s
.size())
62 return (s
.substr(s
.size() - end
.size(), s
.size()) == end
);
65 bool Startswith(const std::string
&s
, const std::string
&start
)
67 if (start
.size() > s
.size())
69 return (s
.substr(0, start
.size()) == start
);
75 // UTF8ToCodeset - Convert some UTF-8 string for some codeset /*{{{*/
76 // ---------------------------------------------------------------------
77 /* This is handy to use before display some information for enduser */
78 bool UTF8ToCodeset(const char *codeset
, const string
&orig
, string
*dest
)
83 size_t insize
, bufsize
;
86 cd
= iconv_open(codeset
, "UTF-8");
87 if (cd
== (iconv_t
)(-1)) {
88 // Something went wrong
90 _error
->Error("conversion from 'UTF-8' to '%s' not available",
98 insize
= bufsize
= orig
.size();
100 inptr
= (char *)inbuf
;
101 outbuf
= new char[bufsize
];
102 size_t lastError
= -1;
106 char *outptr
= outbuf
;
107 size_t outsize
= bufsize
;
108 size_t const err
= iconv(cd
, &inptr
, &insize
, &outptr
, &outsize
);
109 dest
->append(outbuf
, outptr
- outbuf
);
110 if (err
== (size_t)(-1))
117 // replace a series of unknown multibytes with a single "?"
118 if (lastError
!= insize
) {
119 lastError
= insize
- 1;
127 if (outptr
== outbuf
)
131 outbuf
= new char[bufsize
];
145 // strstrip - Remove white space from the front and back of a string /*{{{*/
146 // ---------------------------------------------------------------------
147 /* This is handy to use when parsing a file. It also removes \n's left
148 over from fgets and company */
149 char *_strstrip(char *String
)
151 for (;*String
!= 0 && (*String
== ' ' || *String
== '\t'); String
++);
155 return _strrstrip(String
);
158 // strrstrip - Remove white space from the back of a string /*{{{*/
159 // ---------------------------------------------------------------------
160 char *_strrstrip(char *String
)
162 char *End
= String
+ strlen(String
) - 1;
163 for (;End
!= String
- 1 && (*End
== ' ' || *End
== '\t' || *End
== '\n' ||
164 *End
== '\r'); End
--);
170 // strtabexpand - Converts tabs into 8 spaces /*{{{*/
171 // ---------------------------------------------------------------------
173 char *_strtabexpand(char *String
,size_t Len
)
175 for (char *I
= String
; I
!= I
+ Len
&& *I
!= 0; I
++)
179 if (I
+ 8 > String
+ Len
)
185 /* Assume the start of the string is 0 and find the next 8 char
191 Len
= 8 - ((String
- I
) % 8);
199 memmove(I
+ Len
,I
+ 1,strlen(I
) + 1);
200 for (char *J
= I
; J
+ Len
!= I
; *I
= ' ', I
++);
205 // ParseQuoteWord - Parse a single word out of a string /*{{{*/
206 // ---------------------------------------------------------------------
207 /* This grabs a single word, converts any % escaped characters to their
208 proper values and advances the pointer. Double quotes are understood
209 and striped out as well. This is for URI/URL parsing. It also can
210 understand [] brackets.*/
211 bool ParseQuoteWord(const char *&String
,string
&Res
)
213 // Skip leading whitespace
214 const char *C
= String
;
215 for (;*C
!= 0 && *C
== ' '; C
++);
219 // Jump to the next word
220 for (;*C
!= 0 && isspace(*C
) == 0; C
++)
224 C
= strchr(C
+ 1, '"');
230 C
= strchr(C
+ 1, ']');
236 // Now de-quote characters
239 const char *Start
= String
;
241 for (I
= Buffer
; I
< Buffer
+ sizeof(Buffer
) && Start
!= C
; I
++)
243 if (*Start
== '%' && Start
+ 2 < C
&&
244 isxdigit(Start
[1]) && isxdigit(Start
[2]))
249 *I
= (char)strtol(Tmp
,0,16);
262 // Skip ending white space
263 for (;*C
!= 0 && isspace(*C
) != 0; C
++);
268 // ParseCWord - Parses a string like a C "" expression /*{{{*/
269 // ---------------------------------------------------------------------
270 /* This expects a series of space separated strings enclosed in ""'s.
271 It concatenates the ""'s into a single string. */
272 bool ParseCWord(const char *&String
,string
&Res
)
274 // Skip leading whitespace
275 const char *C
= String
;
276 for (;*C
!= 0 && *C
== ' '; C
++);
282 if (strlen(String
) >= sizeof(Buffer
))
289 for (C
++; *C
!= 0 && *C
!= '"'; C
++)
298 if (C
!= String
&& isspace(*C
) != 0 && isspace(C
[-1]) != 0)
300 if (isspace(*C
) == 0)
310 // QuoteString - Convert a string into quoted from /*{{{*/
311 // ---------------------------------------------------------------------
313 string
QuoteString(const string
&Str
, const char *Bad
)
316 for (string::const_iterator I
= Str
.begin(); I
!= Str
.end(); ++I
)
318 if (strchr(Bad
,*I
) != 0 || isprint(*I
) == 0 ||
319 *I
== 0x25 || // percent '%' char
320 *I
<= 0x20 || *I
>= 0x7F) // control chars
323 sprintf(Buf
,"%%%02x",(int)*I
);
332 // DeQuoteString - Convert a string from quoted from /*{{{*/
333 // ---------------------------------------------------------------------
334 /* This undoes QuoteString */
335 string
DeQuoteString(const string
&Str
)
337 return DeQuoteString(Str
.begin(),Str
.end());
339 string
DeQuoteString(string::const_iterator
const &begin
,
340 string::const_iterator
const &end
)
343 for (string::const_iterator I
= begin
; I
!= end
; ++I
)
345 if (*I
== '%' && I
+ 2 < end
&&
346 isxdigit(I
[1]) && isxdigit(I
[2]))
352 Res
+= (char)strtol(Tmp
,0,16);
363 // SizeToStr - Convert a long into a human readable size /*{{{*/
364 // ---------------------------------------------------------------------
365 /* A max of 4 digits are shown before conversion to the next highest unit.
366 The max length of the string will be 5 chars unless the size is > 10
368 string
SizeToStr(double Size
)
377 /* bytes, KiloBytes, MegaBytes, GigaBytes, TeraBytes, PetaBytes,
378 ExaBytes, ZettaBytes, YottaBytes */
379 char Ext
[] = {'\0','k','M','G','T','P','E','Z','Y'};
383 if (ASize
< 100 && I
!= 0)
385 sprintf(S
,"%'.1f %c",ASize
,Ext
[I
]);
391 sprintf(S
,"%'.0f %c",ASize
,Ext
[I
]);
401 // TimeToStr - Convert the time into a string /*{{{*/
402 // ---------------------------------------------------------------------
403 /* Converts a number of seconds to a hms format */
404 string
TimeToStr(unsigned long Sec
)
412 //d means days, h means hours, min means minutes, s means seconds
413 sprintf(S
,_("%lid %lih %limin %lis"),Sec
/60/60/24,(Sec
/60/60) % 24,(Sec
/60) % 60,Sec
% 60);
419 //h means hours, min means minutes, s means seconds
420 sprintf(S
,_("%lih %limin %lis"),Sec
/60/60,(Sec
/60) % 60,Sec
% 60);
426 //min means minutes, s means seconds
427 sprintf(S
,_("%limin %lis"),Sec
/60,Sec
% 60);
432 sprintf(S
,_("%lis"),Sec
);
439 // SubstVar - Substitute a string for another string /*{{{*/
440 // ---------------------------------------------------------------------
441 /* This replaces all occurrences of Subst with Contents in Str. */
442 string
SubstVar(const string
&Str
,const string
&Subst
,const string
&Contents
)
444 if (Subst
.empty() == true)
447 string::size_type Pos
= 0;
448 string::size_type OldPos
= 0;
451 while (OldPos
< Str
.length() &&
452 (Pos
= Str
.find(Subst
,OldPos
)) != string::npos
)
455 Temp
.append(Str
, OldPos
, Pos
- OldPos
);
456 if (Contents
.empty() == false)
457 Temp
.append(Contents
);
458 OldPos
= Pos
+ Subst
.length();
464 if (OldPos
>= Str
.length())
466 return Temp
+ string(Str
,OldPos
);
468 string
SubstVar(string Str
,const struct SubstVar
*Vars
)
470 for (; Vars
->Subst
!= 0; Vars
++)
471 Str
= SubstVar(Str
,Vars
->Subst
,*Vars
->Contents
);
475 // OutputInDepth - return a string with separator multiplied with depth /*{{{*/
476 // ---------------------------------------------------------------------
477 /* Returns a string with the supplied separator depth + 1 times in it */
478 std::string
OutputInDepth(const unsigned long Depth
, const char* Separator
)
480 std::string output
= "";
481 for(unsigned long d
=Depth
+1; d
> 0; d
--)
482 output
.append(Separator
);
486 // URItoFileName - Convert the uri into a unique file name /*{{{*/
487 // ---------------------------------------------------------------------
488 /* This converts a URI into a safe filename. It quotes all unsafe characters
489 and converts / to _ and removes the scheme identifier. The resulting
490 file name should be unique and never occur again for a different file */
491 string
URItoFileName(const string
&URI
)
493 // Nuke 'sensitive' items
499 // "\x00-\x20{}|\\\\^\\[\\]<>\"\x7F-\xFF";
500 string NewURI
= QuoteString(U
,"\\|{}[]<>\"^~_=!@#$%^&*");
501 replace(NewURI
.begin(),NewURI
.end(),'/','_');
505 // Base64Encode - Base64 Encoding routine for short strings /*{{{*/
506 // ---------------------------------------------------------------------
507 /* This routine performs a base64 transformation on a string. It was ripped
508 from wget and then patched and bug fixed.
510 This spec can be found in rfc2045 */
511 string
Base64Encode(const string
&S
)
514 static char tbl
[64] = {'A','B','C','D','E','F','G','H',
515 'I','J','K','L','M','N','O','P',
516 'Q','R','S','T','U','V','W','X',
517 'Y','Z','a','b','c','d','e','f',
518 'g','h','i','j','k','l','m','n',
519 'o','p','q','r','s','t','u','v',
520 'w','x','y','z','0','1','2','3',
521 '4','5','6','7','8','9','+','/'};
523 // Pre-allocate some space
525 Final
.reserve((4*S
.length() + 2)/3 + 2);
527 /* Transform the 3x8 bits to 4x6 bits, as required by
529 for (string::const_iterator I
= S
.begin(); I
< S
.end(); I
+= 3)
531 char Bits
[3] = {0,0,0};
538 Final
+= tbl
[Bits
[0] >> 2];
539 Final
+= tbl
[((Bits
[0] & 3) << 4) + (Bits
[1] >> 4)];
541 if (I
+ 1 >= S
.end())
544 Final
+= tbl
[((Bits
[1] & 0xf) << 2) + (Bits
[2] >> 6)];
546 if (I
+ 2 >= S
.end())
549 Final
+= tbl
[Bits
[2] & 0x3f];
552 /* Apply the padding elements, this tells how many bytes the remote
553 end should discard */
554 if (S
.length() % 3 == 2)
556 if (S
.length() % 3 == 1)
562 // stringcmp - Arbitrary string compare /*{{{*/
563 // ---------------------------------------------------------------------
564 /* This safely compares two non-null terminated strings of arbitrary
566 int stringcmp(const char *A
,const char *AEnd
,const char *B
,const char *BEnd
)
568 for (; A
!= AEnd
&& B
!= BEnd
; A
++, B
++)
572 if (A
== AEnd
&& B
== BEnd
)
584 int stringcmp(string::const_iterator A
,string::const_iterator AEnd
,
585 const char *B
,const char *BEnd
)
587 for (; A
!= AEnd
&& B
!= BEnd
; A
++, B
++)
591 if (A
== AEnd
&& B
== BEnd
)
601 int stringcmp(string::const_iterator A
,string::const_iterator AEnd
,
602 string::const_iterator B
,string::const_iterator BEnd
)
604 for (; A
!= AEnd
&& B
!= BEnd
; A
++, B
++)
608 if (A
== AEnd
&& B
== BEnd
)
620 // stringcasecmp - Arbitrary case insensitive string compare /*{{{*/
621 // ---------------------------------------------------------------------
623 int stringcasecmp(const char *A
,const char *AEnd
,const char *B
,const char *BEnd
)
625 for (; A
!= AEnd
&& B
!= BEnd
; A
++, B
++)
626 if (tolower_ascii(*A
) != tolower_ascii(*B
))
629 if (A
== AEnd
&& B
== BEnd
)
635 if (tolower_ascii(*A
) < tolower_ascii(*B
))
640 int stringcasecmp(string::const_iterator A
,string::const_iterator AEnd
,
641 const char *B
,const char *BEnd
)
643 for (; A
!= AEnd
&& B
!= BEnd
; A
++, B
++)
644 if (tolower_ascii(*A
) != tolower_ascii(*B
))
647 if (A
== AEnd
&& B
== BEnd
)
653 if (tolower_ascii(*A
) < tolower_ascii(*B
))
657 int stringcasecmp(string::const_iterator A
,string::const_iterator AEnd
,
658 string::const_iterator B
,string::const_iterator BEnd
)
660 for (; A
!= AEnd
&& B
!= BEnd
; A
++, B
++)
661 if (tolower_ascii(*A
) != tolower_ascii(*B
))
664 if (A
== AEnd
&& B
== BEnd
)
670 if (tolower_ascii(*A
) < tolower_ascii(*B
))
676 // LookupTag - Lookup the value of a tag in a taged string /*{{{*/
677 // ---------------------------------------------------------------------
678 /* The format is like those used in package files and the method
679 communication system */
680 string
LookupTag(const string
&Message
,const char *Tag
,const char *Default
)
682 // Look for a matching tag.
683 int Length
= strlen(Tag
);
684 for (string::const_iterator I
= Message
.begin(); I
+ Length
< Message
.end(); ++I
)
687 if (I
[Length
] == ':' && stringcasecmp(I
,I
+Length
,Tag
) == 0)
689 // Find the end of line and strip the leading/trailing spaces
690 string::const_iterator J
;
692 for (; isspace(*I
) != 0 && I
< Message
.end(); ++I
);
693 for (J
= I
; *J
!= '\n' && J
< Message
.end(); ++J
);
694 for (; J
> I
&& isspace(J
[-1]) != 0; --J
);
699 for (; *I
!= '\n' && I
< Message
.end(); ++I
);
702 // Failed to find a match
708 // StringToBool - Converts a string into a boolean /*{{{*/
709 // ---------------------------------------------------------------------
710 /* This inspects the string to see if it is true or if it is false and
711 then returns the result. Several varients on true/false are checked. */
712 int StringToBool(const string
&Text
,int Default
)
715 int Res
= strtol(Text
.c_str(),&ParseEnd
,0);
716 // ensure that the entire string was converted by strtol to avoid
717 // failures on "apt-cache show -a 0ad" where the "0" is converted
718 const char *TextEnd
= Text
.c_str()+Text
.size();
719 if (ParseEnd
== TextEnd
&& Res
>= 0 && Res
<= 1)
722 // Check for positives
723 if (strcasecmp(Text
.c_str(),"no") == 0 ||
724 strcasecmp(Text
.c_str(),"false") == 0 ||
725 strcasecmp(Text
.c_str(),"without") == 0 ||
726 strcasecmp(Text
.c_str(),"off") == 0 ||
727 strcasecmp(Text
.c_str(),"disable") == 0)
730 // Check for negatives
731 if (strcasecmp(Text
.c_str(),"yes") == 0 ||
732 strcasecmp(Text
.c_str(),"true") == 0 ||
733 strcasecmp(Text
.c_str(),"with") == 0 ||
734 strcasecmp(Text
.c_str(),"on") == 0 ||
735 strcasecmp(Text
.c_str(),"enable") == 0)
741 // TimeRFC1123 - Convert a time_t into RFC1123 format /*{{{*/
742 // ---------------------------------------------------------------------
743 /* This converts a time_t into a string time representation that is
744 year 2000 complient and timezone neutral */
745 string
TimeRFC1123(time_t Date
)
748 if (gmtime_r(&Date
, &Conv
) == NULL
)
752 const char *Day
[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
753 const char *Month
[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul",
754 "Aug","Sep","Oct","Nov","Dec"};
756 snprintf(Buf
, sizeof(Buf
), "%s, %02i %s %i %02i:%02i:%02i GMT",Day
[Conv
.tm_wday
],
757 Conv
.tm_mday
,Month
[Conv
.tm_mon
],Conv
.tm_year
+1900,Conv
.tm_hour
,
758 Conv
.tm_min
,Conv
.tm_sec
);
762 // ReadMessages - Read messages from the FD /*{{{*/
763 // ---------------------------------------------------------------------
764 /* This pulls full messages from the input FD into the message buffer.
765 It assumes that messages will not pause during transit so no
766 fancy buffering is used.
768 In particular: this reads blocks from the input until it believes
769 that it's run out of input text. Each block is terminated by a
770 double newline ('\n' followed by '\n'). As noted below, there is a
771 bug in this code: it assumes that all the blocks have been read if
772 it doesn't see additional text in the buffer after the last one is
773 parsed, which will cause it to lose blocks if the last block
774 coincides with the end of the buffer.
776 bool ReadMessages(int Fd
, vector
<string
> &List
)
780 // Represents any left-over from the previous iteration of the
781 // parse loop. (i.e., if a message is split across the end
782 // of the buffer, it goes here)
783 string PartialMessage
;
787 int Res
= read(Fd
,End
,sizeof(Buffer
) - (End
-Buffer
));
788 if (Res
< 0 && errno
== EINTR
)
791 // Process is dead, this is kind of bad..
796 if (Res
< 0 && errno
== EAGAIN
)
803 // Look for the end of the message
804 for (char *I
= Buffer
; I
+ 1 < End
; I
++)
807 (I
[0] != '\n' && strncmp(I
, "\r\n\r\n", 4) != 0))
810 // Pull the message out
811 string
Message(Buffer
,I
-Buffer
);
812 PartialMessage
+= Message
;
815 for (; I
< End
&& (*I
== '\n' || *I
== '\r'); ++I
);
817 memmove(Buffer
,I
,End
-Buffer
);
820 List
.push_back(PartialMessage
);
821 PartialMessage
.clear();
825 // If there's text left in the buffer, store it
826 // in PartialMessage and throw the rest of the buffer
827 // away. This allows us to handle messages that
828 // are longer than the static buffer size.
829 PartialMessage
+= string(Buffer
, End
);
834 // BUG ALERT: if a message block happens to end at a
835 // multiple of 64000 characters, this will cause it to
836 // terminate early, leading to a badly formed block and
837 // probably crashing the method. However, this is the only
838 // way we have to find the end of the message block. I have
839 // an idea of how to fix this, but it will require changes
840 // to the protocol (essentially to mark the beginning and
841 // end of the block).
843 // -- dburrows 2008-04-02
847 if (WaitFd(Fd
) == false)
852 // MonthConv - Converts a month string into a number /*{{{*/
853 // ---------------------------------------------------------------------
854 /* This was lifted from the boa webserver which lifted it from 'wn-v1.07'
855 Made it a bit more robust with a few tolower_ascii though. */
856 static int MonthConv(char *Month
)
858 switch (tolower_ascii(*Month
))
861 return tolower_ascii(Month
[1]) == 'p'?3:7;
867 if (tolower_ascii(Month
[1]) == 'a')
869 return tolower_ascii(Month
[2]) == 'n'?5:6;
871 return tolower_ascii(Month
[2]) == 'r'?2:4;
879 // Pretend it is January..
885 // timegm - Internal timegm if the gnu version is not available /*{{{*/
886 // ---------------------------------------------------------------------
887 /* Converts struct tm to time_t, assuming the data in tm is UTC rather
888 than local timezone (mktime assumes the latter).
890 This function is a nonstandard GNU extension that is also present on
891 the BSDs and maybe other systems. For others we follow the advice of
892 the manpage of timegm and use his portable replacement. */
894 static time_t timegm(struct tm
*t
)
896 char *tz
= getenv("TZ");
899 time_t ret
= mktime(t
);
909 // FullDateToTime - Converts a HTTP1.1 full date strings into a time_t /*{{{*/
910 // ---------------------------------------------------------------------
911 /* tries to parses a full date as specified in RFC2616 Section 3.3.1
912 with one exception: All timezones (%Z) are accepted but the protocol
913 says that it MUST be GMT, but this one is equal to UTC which we will
914 encounter from time to time (e.g. in Release files) so we accept all
915 here and just assume it is GMT (or UTC) later on */
916 bool RFC1123StrToTime(const char* const str
,time_t &time
)
919 setlocale (LC_ALL
,"C");
921 // Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
922 (strptime(str
, "%a, %d %b %Y %H:%M:%S %Z", &Tm
) == NULL
&&
923 // Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
924 strptime(str
, "%A, %d-%b-%y %H:%M:%S %Z", &Tm
) == NULL
&&
925 // Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
926 strptime(str
, "%a %b %d %H:%M:%S %Y", &Tm
) == NULL
);
927 setlocale (LC_ALL
,"");
935 // FTPMDTMStrToTime - Converts a ftp modification date into a time_t /*{{{*/
936 // ---------------------------------------------------------------------
938 bool FTPMDTMStrToTime(const char* const str
,time_t &time
)
941 // MDTM includes no whitespaces but recommend and ignored by strptime
942 if (strptime(str
, "%Y %m %d %H %M %S", &Tm
) == NULL
)
949 // StrToTime - Converts a string into a time_t /*{{{*/
950 // ---------------------------------------------------------------------
951 /* This handles all 3 popular time formats including RFC 1123, RFC 1036
952 and the C library asctime format. It requires the GNU library function
953 'timegm' to convert a struct tm in UTC to a time_t. For some bizzar
954 reason the C library does not provide any such function :< This also
955 handles the weird, but unambiguous FTP time format*/
956 bool StrToTime(const string
&Val
,time_t &Result
)
961 // Skip the day of the week
962 const char *I
= strchr(Val
.c_str(), ' ');
964 // Handle RFC 1123 time
966 if (sscanf(I
," %2d %3s %4d %2d:%2d:%2d GMT",&Tm
.tm_mday
,Month
,&Tm
.tm_year
,
967 &Tm
.tm_hour
,&Tm
.tm_min
,&Tm
.tm_sec
) != 6)
969 // Handle RFC 1036 time
970 if (sscanf(I
," %2d-%3s-%3d %2d:%2d:%2d GMT",&Tm
.tm_mday
,Month
,
971 &Tm
.tm_year
,&Tm
.tm_hour
,&Tm
.tm_min
,&Tm
.tm_sec
) == 6)
976 if (sscanf(I
," %3s %2d %2d:%2d:%2d %4d",Month
,&Tm
.tm_mday
,
977 &Tm
.tm_hour
,&Tm
.tm_min
,&Tm
.tm_sec
,&Tm
.tm_year
) != 6)
980 if (sscanf(Val
.c_str(),"%4d%2d%2d%2d%2d%2d",&Tm
.tm_year
,&Tm
.tm_mon
,
981 &Tm
.tm_mday
,&Tm
.tm_hour
,&Tm
.tm_min
,&Tm
.tm_sec
) != 6)
990 Tm
.tm_mon
= MonthConv(Month
);
992 Tm
.tm_mon
= 0; // we don't have a month, so pick something
995 // Convert to local time and then to GMT
996 Result
= timegm(&Tm
);
1000 // StrToNum - Convert a fixed length string to a number /*{{{*/
1001 // ---------------------------------------------------------------------
1002 /* This is used in decoding the crazy fixed length string headers in
1003 tar and ar files. */
1004 bool StrToNum(const char *Str
,unsigned long &Res
,unsigned Len
,unsigned Base
)
1007 if (Len
>= sizeof(S
))
1012 // All spaces is a zero
1015 for (I
= 0; S
[I
] == ' '; I
++);
1020 Res
= strtoul(S
,&End
,Base
);
1027 // StrToNum - Convert a fixed length string to a number /*{{{*/
1028 // ---------------------------------------------------------------------
1029 /* This is used in decoding the crazy fixed length string headers in
1030 tar and ar files. */
1031 bool StrToNum(const char *Str
,unsigned long long &Res
,unsigned Len
,unsigned Base
)
1034 if (Len
>= sizeof(S
))
1039 // All spaces is a zero
1042 for (I
= 0; S
[I
] == ' '; I
++);
1047 Res
= strtoull(S
,&End
,Base
);
1055 // Base256ToNum - Convert a fixed length binary to a number /*{{{*/
1056 // ---------------------------------------------------------------------
1057 /* This is used in decoding the 256bit encoded fixed length fields in
1059 bool Base256ToNum(const char *Str
,unsigned long long &Res
,unsigned int Len
)
1061 if ((Str
[0] & 0x80) == 0)
1065 Res
= Str
[0] & 0x7F;
1066 for(unsigned int i
= 1; i
< Len
; ++i
)
1067 Res
= (Res
<<8) + Str
[i
];
1072 // Base256ToNum - Convert a fixed length binary to a number /*{{{*/
1073 // ---------------------------------------------------------------------
1074 /* This is used in decoding the 256bit encoded fixed length fields in
1076 bool Base256ToNum(const char *Str
,unsigned long &Res
,unsigned int Len
)
1078 unsigned long long Num
;
1081 rc
= Base256ToNum(Str
, Num
, Len
);
1089 // HexDigit - Convert a hex character into an integer /*{{{*/
1090 // ---------------------------------------------------------------------
1091 /* Helper for Hex2Num */
1092 static int HexDigit(int c
)
1094 if (c
>= '0' && c
<= '9')
1096 if (c
>= 'a' && c
<= 'f')
1097 return c
- 'a' + 10;
1098 if (c
>= 'A' && c
<= 'F')
1099 return c
- 'A' + 10;
1103 // Hex2Num - Convert a long hex number into a buffer /*{{{*/
1104 // ---------------------------------------------------------------------
1105 /* The length of the buffer must be exactly 1/2 the length of the string. */
1106 bool Hex2Num(const string
&Str
,unsigned char *Num
,unsigned int Length
)
1108 if (Str
.length() != Length
*2)
1111 // Convert each digit. We store it in the same order as the string
1113 for (string::const_iterator I
= Str
.begin(); I
!= Str
.end();J
++, I
+= 2)
1115 if (isxdigit(*I
) == 0 || isxdigit(I
[1]) == 0)
1118 Num
[J
] = HexDigit(I
[0]) << 4;
1119 Num
[J
] += HexDigit(I
[1]);
1125 // TokSplitString - Split a string up by a given token /*{{{*/
1126 // ---------------------------------------------------------------------
1127 /* This is intended to be a faster splitter, it does not use dynamic
1128 memories. Input is changed to insert nulls at each token location. */
1129 bool TokSplitString(char Tok
,char *Input
,char **List
,
1130 unsigned long ListMax
)
1132 // Strip any leading spaces
1133 char *Start
= Input
;
1134 char *Stop
= Start
+ strlen(Start
);
1135 for (; *Start
!= 0 && isspace(*Start
) != 0; Start
++);
1137 unsigned long Count
= 0;
1141 // Skip to the next Token
1142 for (; Pos
!= Stop
&& *Pos
!= Tok
; Pos
++);
1144 // Back remove spaces
1146 for (; End
> Start
&& (End
[-1] == Tok
|| isspace(End
[-1]) != 0); End
--);
1149 List
[Count
++] = Start
;
1150 if (Count
>= ListMax
)
1157 for (; Pos
!= Stop
&& (*Pos
== Tok
|| isspace(*Pos
) != 0 || *Pos
== 0); Pos
++);
1165 // VectorizeString - Split a string up into a vector of strings /*{{{*/
1166 // ---------------------------------------------------------------------
1167 /* This can be used to split a given string up into a vector, so the
1168 propose is the same as in the method above and this one is a bit slower
1169 also, but the advantage is that we have an iteratable vector */
1170 vector
<string
> VectorizeString(string
const &haystack
, char const &split
)
1172 vector
<string
> exploded
;
1173 if (haystack
.empty() == true)
1175 string::const_iterator start
= haystack
.begin();
1176 string::const_iterator end
= start
;
1178 for (; end
!= haystack
.end() && *end
!= split
; ++end
);
1179 exploded
.push_back(string(start
, end
));
1181 } while (end
!= haystack
.end() && (++end
) != haystack
.end());
1185 // StringSplit - split a string into a string vector by token /*{{{*/
1186 // ---------------------------------------------------------------------
1187 /* See header for details.
1189 vector
<string
> StringSplit(std::string
const &s
, std::string
const &sep
,
1190 unsigned int maxsplit
)
1192 vector
<string
> split
;
1195 // no seperator given, this is bogus
1200 while (pos
!= string::npos
)
1202 pos
= s
.find(sep
, start
);
1203 split
.push_back(s
.substr(start
, pos
-start
));
1205 // if maxsplit is reached, the remaining string is the last item
1206 if(split
.size() >= maxsplit
)
1208 split
[split
.size()-1] = s
.substr(start
);
1211 start
= pos
+sep
.size();
1216 // RegexChoice - Simple regex list/list matcher /*{{{*/
1217 // ---------------------------------------------------------------------
1219 unsigned long RegexChoice(RxChoiceList
*Rxs
,const char **ListBegin
,
1220 const char **ListEnd
)
1222 for (RxChoiceList
*R
= Rxs
; R
->Str
!= 0; R
++)
1225 unsigned long Hits
= 0;
1226 for (; ListBegin
< ListEnd
; ++ListBegin
)
1228 // Check if the name is a regex
1231 for (I
= *ListBegin
; *I
!= 0; I
++)
1232 if (*I
== '.' || *I
== '?' || *I
== '*' || *I
== '|')
1237 // Compile the regex pattern
1240 if (regcomp(&Pattern
,*ListBegin
,REG_EXTENDED
| REG_ICASE
|
1246 for (RxChoiceList
*R
= Rxs
; R
->Str
!= 0; R
++)
1251 if (strcasecmp(R
->Str
,*ListBegin
) != 0)
1255 if (regexec(&Pattern
,R
->Str
,0,0,0) != 0)
1260 if (R
->Hit
== false)
1270 _error
->Warning(_("Selection %s not found"),*ListBegin
);
1276 // {str,io}printf - C format string outputter to C++ strings/iostreams /*{{{*/
1277 // ---------------------------------------------------------------------
1278 /* This is used to make the internationalization strings easier to translate
1279 and to allow reordering of parameters */
1280 static bool iovprintf(ostream
&out
, const char *format
,
1281 va_list &args
, ssize_t
&size
) {
1282 char *S
= (char*)malloc(size
);
1283 ssize_t
const n
= vsnprintf(S
, size
, format
, args
);
1284 if (n
> -1 && n
< size
) {
1297 void ioprintf(ostream
&out
,const char *format
,...)
1302 va_start(args
,format
);
1303 if (iovprintf(out
, format
, args
, size
) == true)
1308 void strprintf(string
&out
,const char *format
,...)
1312 std::ostringstream outstr
;
1314 va_start(args
,format
);
1315 if (iovprintf(outstr
, format
, args
, size
) == true)
1322 // safe_snprintf - Safer snprintf /*{{{*/
1323 // ---------------------------------------------------------------------
1324 /* This is a snprintf that will never (ever) go past 'End' and returns a
1325 pointer to the end of the new string. The returned string is always null
1326 terminated unless Buffer == end. This is a better alterantive to using
1327 consecutive snprintfs. */
1328 char *safe_snprintf(char *Buffer
,char *End
,const char *Format
,...)
1335 va_start(args
,Format
);
1336 Did
= vsnprintf(Buffer
,End
- Buffer
,Format
,args
);
1339 if (Did
< 0 || Buffer
+ Did
> End
)
1341 return Buffer
+ Did
;
1344 // StripEpoch - Remove the version "epoch" from a version string /*{{{*/
1345 // ---------------------------------------------------------------------
1346 string
StripEpoch(const string
&VerStr
)
1348 size_t i
= VerStr
.find(":");
1349 if (i
== string::npos
)
1351 return VerStr
.substr(i
+1);
1354 // tolower_ascii - tolower() function that ignores the locale /*{{{*/
1355 // ---------------------------------------------------------------------
1356 /* This little function is the most called method we have and tries
1357 therefore to do the absolut minimum - and is notable faster than
1358 standard tolower/toupper and as a bonus avoids problems with different
1359 locales - we only operate on ascii chars anyway. */
1360 int tolower_ascii(int const c
)
1362 if (c
>= 'A' && c
<= 'Z')
1368 // CheckDomainList - See if Host is in a , separate list /*{{{*/
1369 // ---------------------------------------------------------------------
1370 /* The domain list is a comma separate list of domains that are suffix
1371 matched against the argument */
1372 bool CheckDomainList(const string
&Host
,const string
&List
)
1374 string::const_iterator Start
= List
.begin();
1375 for (string::const_iterator Cur
= List
.begin(); Cur
<= List
.end(); ++Cur
)
1377 if (Cur
< List
.end() && *Cur
!= ',')
1380 // Match the end of the string..
1381 if ((Host
.size() >= (unsigned)(Cur
- Start
)) &&
1383 stringcasecmp(Host
.end() - (Cur
- Start
),Host
.end(),Start
,Cur
) == 0)
1391 // strv_length - Return the length of a NULL-terminated string array /*{{{*/
1392 // ---------------------------------------------------------------------
1394 size_t strv_length(const char **str_array
)
1397 for (i
=0; str_array
[i
] != NULL
; i
++)
1403 // DeEscapeString - unescape (\0XX and \xXX) from a string /*{{{*/
1404 // ---------------------------------------------------------------------
1406 string
DeEscapeString(const string
&input
)
1409 string::const_iterator it
;
1411 for (it
= input
.begin(); it
!= input
.end(); ++it
)
1413 // just copy non-escape chars
1420 // deal with double escape
1422 (it
+ 1 < input
.end()) && it
[1] == '\\')
1426 // advance iterator one step further
1431 // ensure we have a char to read
1432 if (it
+ 1 == input
.end())
1440 if (it
+ 2 <= input
.end()) {
1444 output
+= (char)strtol(tmp
, 0, 8);
1449 if (it
+ 2 <= input
.end()) {
1453 output
+= (char)strtol(tmp
, 0, 16);
1458 // FIXME: raise exception here?
1465 // URI::CopyFrom - Copy from an object /*{{{*/
1466 // ---------------------------------------------------------------------
1467 /* This parses the URI into all of its components */
1468 void URI::CopyFrom(const string
&U
)
1470 string::const_iterator I
= U
.begin();
1472 // Locate the first colon, this separates the scheme
1473 for (; I
< U
.end() && *I
!= ':' ; ++I
);
1474 string::const_iterator FirstColon
= I
;
1476 /* Determine if this is a host type URI with a leading double //
1477 and then search for the first single / */
1478 string::const_iterator SingleSlash
= I
;
1479 if (I
+ 3 < U
.end() && I
[1] == '/' && I
[2] == '/')
1482 /* Find the / indicating the end of the hostname, ignoring /'s in the
1484 bool InBracket
= false;
1485 for (; SingleSlash
< U
.end() && (*SingleSlash
!= '/' || InBracket
== true); ++SingleSlash
)
1487 if (*SingleSlash
== '[')
1489 if (InBracket
== true && *SingleSlash
== ']')
1493 if (SingleSlash
> U
.end())
1494 SingleSlash
= U
.end();
1496 // We can now write the access and path specifiers
1497 Access
.assign(U
.begin(),FirstColon
);
1498 if (SingleSlash
!= U
.end())
1499 Path
.assign(SingleSlash
,U
.end());
1500 if (Path
.empty() == true)
1503 // Now we attempt to locate a user:pass@host fragment
1504 if (FirstColon
+ 2 <= U
.end() && FirstColon
[1] == '/' && FirstColon
[2] == '/')
1508 if (FirstColon
>= U
.end())
1511 if (FirstColon
> SingleSlash
)
1512 FirstColon
= SingleSlash
;
1514 // Find the colon...
1516 if (I
> SingleSlash
)
1518 for (; I
< SingleSlash
&& *I
!= ':'; ++I
);
1519 string::const_iterator SecondColon
= I
;
1521 // Search for the @ after the colon
1522 for (; I
< SingleSlash
&& *I
!= '@'; ++I
);
1523 string::const_iterator At
= I
;
1525 // Now write the host and user/pass
1526 if (At
== SingleSlash
)
1528 if (FirstColon
< SingleSlash
)
1529 Host
.assign(FirstColon
,SingleSlash
);
1533 Host
.assign(At
+1,SingleSlash
);
1534 // username and password must be encoded (RFC 3986)
1535 User
.assign(DeQuoteString(FirstColon
,SecondColon
));
1536 if (SecondColon
< At
)
1537 Password
.assign(DeQuoteString(SecondColon
+1,At
));
1540 // Now we parse the RFC 2732 [] hostnames.
1541 unsigned long PortEnd
= 0;
1543 for (unsigned I
= 0; I
!= Host
.length();)
1552 if (InBracket
== true && Host
[I
] == ']')
1563 if (InBracket
== true)
1569 // Now we parse off a port number from the hostname
1571 string::size_type Pos
= Host
.rfind(':');
1572 if (Pos
== string::npos
|| Pos
< PortEnd
)
1575 Port
= atoi(string(Host
,Pos
+1).c_str());
1576 Host
.assign(Host
,0,Pos
);
1579 // URI::operator string - Convert the URI to a string /*{{{*/
1580 // ---------------------------------------------------------------------
1582 URI::operator string()
1586 if (Access
.empty() == false)
1589 if (Host
.empty() == false)
1591 if (Access
.empty() == false)
1594 if (User
.empty() == false)
1596 // FIXME: Technically userinfo is permitted even less
1597 // characters than these, but this is not conveniently
1598 // expressed with a blacklist.
1599 Res
+= QuoteString(User
, ":/?#[]@");
1600 if (Password
.empty() == false)
1601 Res
+= ":" + QuoteString(Password
, ":/?#[]@");
1605 // Add RFC 2732 escaping characters
1606 if (Access
.empty() == false &&
1607 (Host
.find('/') != string::npos
|| Host
.find(':') != string::npos
))
1608 Res
+= '[' + Host
+ ']';
1615 sprintf(S
,":%u",Port
);
1620 if (Path
.empty() == false)
1631 // URI::SiteOnly - Return the schema and site for the URI /*{{{*/
1632 // ---------------------------------------------------------------------
1634 string
URI::SiteOnly(const string
&URI
)
1643 // URI::NoUserPassword - Return the schema, site and path for the URI /*{{{*/
1644 // ---------------------------------------------------------------------
1646 string
URI::NoUserPassword(const string
&URI
)