]> git.saurik.com Git - apt.git/blame - apt-pkg/contrib/strutl.cc
add a simple container for HashStrings
[apt.git] / apt-pkg / contrib / strutl.cc
CommitLineData
6c139d6e
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
d48c6a7d 3// $Id: strutl.cc,v 1.48 2003/07/18 14:15:11 mdz Exp $
6c139d6e
AL
4/* ######################################################################
5
b2e465d6 6 String Util - Some useful string functions.
6c139d6e 7
b2e465d6
AL
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
24231681 10 especially in APT methods.
6c139d6e
AL
11
12 This source is placed in the Public Domain, do with it what you will
24231681 13 It was originally written by Jason Gunthorpe <jgg@gpu.srv.ualberta.ca>
6c139d6e
AL
14
15 ##################################################################### */
16 /*}}}*/
17// Includes /*{{{*/
ea542140
DK
18#include <config.h>
19
cdcc6d34 20#include <apt-pkg/strutl.h>
7049d16d 21#include <apt-pkg/fileutl.h>
b2e465d6 22#include <apt-pkg/error.h>
0a8a80e5 23
453b82a3
DK
24#include <stddef.h>
25#include <stdlib.h>
26#include <time.h>
27#include <string>
28#include <vector>
6c139d6e
AL
29#include <ctype.h>
30#include <string.h>
5076b3c2 31#include <sstream>
6c139d6e 32#include <stdio.h>
152ab79e 33#include <algorithm>
2b154e53 34#include <unistd.h>
b2e465d6 35#include <regex.h>
b0db36b1 36#include <errno.h>
b2e465d6 37#include <stdarg.h>
a52f938b 38#include <iconv.h>
0db4a45b 39
ea542140 40#include <apti18n.h>
6c139d6e 41 /*}}}*/
453b82a3
DK
42using namespace std;
43
65dbd5a1
MV
44// Strip - Remove white space from the front and back of a string /*{{{*/
45// ---------------------------------------------------------------------
46namespace APT {
47 namespace String {
b5787388 48std::string Strip(const std::string &str)
65dbd5a1 49{
b5787388
DK
50 // ensure we have at least one character
51 if (str.empty() == true)
52 return str;
53
54 char const * const s = str.c_str();
55 size_t start = 0;
56 for (; isspace(s[start]) != 0; ++start)
57 ; // find the first not-space
58
59 // string contains only whitespaces
60 if (s[start] == '\0')
65dbd5a1 61 return "";
b5787388
DK
62
63 size_t end = str.length() - 1;
64 for (; isspace(s[end]) != 0; --end)
65 ; // find the last not-space
66
67 return str.substr(start, end - start + 1);
65dbd5a1 68}
cf993341
MV
69
70bool Endswith(const std::string &s, const std::string &end)
71{
72 if (end.size() > s.size())
73 return false;
74 return (s.substr(s.size() - end.size(), s.size()) == end);
75}
76
65dbd5a1
MV
77}
78}
79 /*}}}*/
a52f938b
OS
80// UTF8ToCodeset - Convert some UTF-8 string for some codeset /*{{{*/
81// ---------------------------------------------------------------------
82/* This is handy to use before display some information for enduser */
83bool UTF8ToCodeset(const char *codeset, const string &orig, string *dest)
84{
85 iconv_t cd;
86 const char *inbuf;
b39c1859
MV
87 char *inptr, *outbuf;
88 size_t insize, bufsize;
89 dest->clear();
90
a52f938b
OS
91 cd = iconv_open(codeset, "UTF-8");
92 if (cd == (iconv_t)(-1)) {
93 // Something went wrong
94 if (errno == EINVAL)
95 _error->Error("conversion from 'UTF-8' to '%s' not available",
96 codeset);
97 else
98 perror("iconv_open");
99
a52f938b
OS
100 return false;
101 }
102
b39c1859 103 insize = bufsize = orig.size();
a52f938b
OS
104 inbuf = orig.data();
105 inptr = (char *)inbuf;
b39c1859
MV
106 outbuf = new char[bufsize];
107 size_t lastError = -1;
a52f938b 108
1f99b6d3
DK
109 while (insize != 0)
110 {
b39c1859
MV
111 char *outptr = outbuf;
112 size_t outsize = bufsize;
1f99b6d3 113 size_t const err = iconv(cd, &inptr, &insize, &outptr, &outsize);
b39c1859 114 dest->append(outbuf, outptr - outbuf);
1f99b6d3
DK
115 if (err == (size_t)(-1))
116 {
b39c1859
MV
117 switch (errno)
118 {
119 case EILSEQ:
120 insize--;
121 inptr++;
122 // replace a series of unknown multibytes with a single "?"
123 if (lastError != insize) {
124 lastError = insize - 1;
125 dest->append("?");
126 }
127 break;
128 case EINVAL:
129 insize = 0;
130 break;
131 case E2BIG:
132 if (outptr == outbuf)
133 {
134 bufsize *= 2;
135 delete[] outbuf;
136 outbuf = new char[bufsize];
137 }
138 break;
139 }
1f99b6d3
DK
140 }
141 }
a52f938b 142
a52f938b
OS
143 delete[] outbuf;
144
145 iconv_close(cd);
146
147 return true;
148}
149 /*}}}*/
6c139d6e
AL
150// strstrip - Remove white space from the front and back of a string /*{{{*/
151// ---------------------------------------------------------------------
152/* This is handy to use when parsing a file. It also removes \n's left
153 over from fgets and company */
154char *_strstrip(char *String)
155{
156 for (;*String != 0 && (*String == ' ' || *String == '\t'); String++);
157
158 if (*String == 0)
159 return String;
4fb400a6
MV
160 return _strrstrip(String);
161}
162 /*}}}*/
163// strrstrip - Remove white space from the back of a string /*{{{*/
164// ---------------------------------------------------------------------
165char *_strrstrip(char *String)
166{
6c139d6e
AL
167 char *End = String + strlen(String) - 1;
168 for (;End != String - 1 && (*End == ' ' || *End == '\t' || *End == '\n' ||
169 *End == '\r'); End--);
170 End++;
171 *End = 0;
172 return String;
d3e8fbb3 173}
6c139d6e
AL
174 /*}}}*/
175// strtabexpand - Converts tabs into 8 spaces /*{{{*/
176// ---------------------------------------------------------------------
177/* */
178char *_strtabexpand(char *String,size_t Len)
179{
180 for (char *I = String; I != I + Len && *I != 0; I++)
181 {
182 if (*I != '\t')
183 continue;
184 if (I + 8 > String + Len)
185 {
186 *I = 0;
187 return String;
188 }
189
190 /* Assume the start of the string is 0 and find the next 8 char
191 division */
192 int Len;
193 if (String == I)
194 Len = 1;
195 else
196 Len = 8 - ((String - I) % 8);
197 Len -= 2;
198 if (Len <= 0)
199 {
200 *I = ' ';
201 continue;
202 }
203
204 memmove(I + Len,I + 1,strlen(I) + 1);
205 for (char *J = I; J + Len != I; *I = ' ', I++);
206 }
207 return String;
208}
209 /*}}}*/
210// ParseQuoteWord - Parse a single word out of a string /*{{{*/
211// ---------------------------------------------------------------------
212/* This grabs a single word, converts any % escaped characters to their
213 proper values and advances the pointer. Double quotes are understood
7834cb57
AL
214 and striped out as well. This is for URI/URL parsing. It also can
215 understand [] brackets.*/
6c139d6e
AL
216bool ParseQuoteWord(const char *&String,string &Res)
217{
218 // Skip leading whitespace
219 const char *C = String;
220 for (;*C != 0 && *C == ' '; C++);
221 if (*C == 0)
222 return false;
223
224 // Jump to the next word
36f610f1 225 for (;*C != 0 && isspace(*C) == 0; C++)
6c139d6e
AL
226 {
227 if (*C == '"')
228 {
404528bd
DK
229 C = strchr(C + 1, '"');
230 if (C == NULL)
7834cb57
AL
231 return false;
232 }
233 if (*C == '[')
234 {
404528bd
DK
235 C = strchr(C + 1, ']');
236 if (C == NULL)
6c139d6e
AL
237 return false;
238 }
239 }
240
241 // Now de-quote characters
242 char Buffer[1024];
243 char Tmp[3];
244 const char *Start = String;
245 char *I;
246 for (I = Buffer; I < Buffer + sizeof(Buffer) && Start != C; I++)
247 {
436d7eab
DK
248 if (*Start == '%' && Start + 2 < C &&
249 isxdigit(Start[1]) && isxdigit(Start[2]))
6c139d6e
AL
250 {
251 Tmp[0] = Start[1];
252 Tmp[1] = Start[2];
1bc849af 253 Tmp[2] = 0;
6c139d6e
AL
254 *I = (char)strtol(Tmp,0,16);
255 Start += 3;
256 continue;
257 }
258 if (*Start != '"')
259 *I = *Start;
260 else
261 I--;
262 Start++;
263 }
264 *I = 0;
265 Res = Buffer;
266
267 // Skip ending white space
36f610f1 268 for (;*C != 0 && isspace(*C) != 0; C++);
6c139d6e
AL
269 String = C;
270 return true;
271}
272 /*}}}*/
08e8f724
AL
273// ParseCWord - Parses a string like a C "" expression /*{{{*/
274// ---------------------------------------------------------------------
b2e465d6 275/* This expects a series of space separated strings enclosed in ""'s.
08e8f724 276 It concatenates the ""'s into a single string. */
b2e465d6 277bool ParseCWord(const char *&String,string &Res)
08e8f724
AL
278{
279 // Skip leading whitespace
280 const char *C = String;
281 for (;*C != 0 && *C == ' '; C++);
282 if (*C == 0)
283 return false;
284
285 char Buffer[1024];
286 char *Buf = Buffer;
287 if (strlen(String) >= sizeof(Buffer))
288 return false;
289
290 for (; *C != 0; C++)
291 {
292 if (*C == '"')
293 {
294 for (C++; *C != 0 && *C != '"'; C++)
295 *Buf++ = *C;
296
297 if (*C == 0)
298 return false;
299
300 continue;
301 }
302
303 if (C != String && isspace(*C) != 0 && isspace(C[-1]) != 0)
304 continue;
305 if (isspace(*C) == 0)
306 return false;
307 *Buf++ = ' ';
b2e465d6 308 }
08e8f724
AL
309 *Buf = 0;
310 Res = Buffer;
b2e465d6 311 String = C;
08e8f724
AL
312 return true;
313}
314 /*}}}*/
6d5dd02a 315// QuoteString - Convert a string into quoted from /*{{{*/
1bc849af 316// ---------------------------------------------------------------------
6d5dd02a 317/* */
171c75f1 318string QuoteString(const string &Str, const char *Bad)
1bc849af
AL
319{
320 string Res;
f7f0d6c7 321 for (string::const_iterator I = Str.begin(); I != Str.end(); ++I)
1bc849af 322 {
6d5dd02a 323 if (strchr(Bad,*I) != 0 || isprint(*I) == 0 ||
436d7eab
DK
324 *I == 0x25 || // percent '%' char
325 *I <= 0x20 || *I >= 0x7F) // control chars
1bc849af 326 {
6d5dd02a
AL
327 char Buf[10];
328 sprintf(Buf,"%%%02x",(int)*I);
329 Res += Buf;
1bc849af
AL
330 }
331 else
332 Res += *I;
333 }
334 return Res;
335}
336 /*}}}*/
6d5dd02a 337// DeQuoteString - Convert a string from quoted from /*{{{*/
6c139d6e 338// ---------------------------------------------------------------------
6d5dd02a 339/* This undoes QuoteString */
171c75f1 340string DeQuoteString(const string &Str)
436d7eab
DK
341{
342 return DeQuoteString(Str.begin(),Str.end());
343}
344string DeQuoteString(string::const_iterator const &begin,
345 string::const_iterator const &end)
6c139d6e
AL
346{
347 string Res;
f7f0d6c7 348 for (string::const_iterator I = begin; I != end; ++I)
6c139d6e 349 {
436d7eab
DK
350 if (*I == '%' && I + 2 < end &&
351 isxdigit(I[1]) && isxdigit(I[2]))
6c139d6e 352 {
6d5dd02a
AL
353 char Tmp[3];
354 Tmp[0] = I[1];
355 Tmp[1] = I[2];
356 Tmp[2] = 0;
357 Res += (char)strtol(Tmp,0,16);
358 I += 2;
359 continue;
6c139d6e
AL
360 }
361 else
362 Res += *I;
363 }
6d5dd02a 364 return Res;
6c139d6e 365}
6d5dd02a
AL
366
367 /*}}}*/
6c139d6e
AL
368// SizeToStr - Convert a long into a human readable size /*{{{*/
369// ---------------------------------------------------------------------
24231681
AL
370/* A max of 4 digits are shown before conversion to the next highest unit.
371 The max length of the string will be 5 chars unless the size is > 10
6c139d6e
AL
372 YottaBytes (E24) */
373string SizeToStr(double Size)
374{
375 char S[300];
376 double ASize;
377 if (Size >= 0)
378 ASize = Size;
379 else
380 ASize = -1*Size;
381
382 /* bytes, KiloBytes, MegaBytes, GigaBytes, TeraBytes, PetaBytes,
383 ExaBytes, ZettaBytes, YottaBytes */
7f25bdff 384 char Ext[] = {'\0','k','M','G','T','P','E','Z','Y'};
6c139d6e
AL
385 int I = 0;
386 while (I <= 8)
387 {
388 if (ASize < 100 && I != 0)
389 {
4d8d8112 390 sprintf(S,"%'.1f %c",ASize,Ext[I]);
6c139d6e
AL
391 break;
392 }
393
394 if (ASize < 10000)
395 {
4d8d8112 396 sprintf(S,"%'.0f %c",ASize,Ext[I]);
6c139d6e
AL
397 break;
398 }
399 ASize /= 1000.0;
400 I++;
401 }
402
403 return S;
404}
405 /*}}}*/
406// TimeToStr - Convert the time into a string /*{{{*/
407// ---------------------------------------------------------------------
408/* Converts a number of seconds to a hms format */
409string TimeToStr(unsigned long Sec)
410{
411 char S[300];
412
413 while (1)
414 {
415 if (Sec > 60*60*24)
416 {
09fab244
MV
417 //d means days, h means hours, min means minutes, s means seconds
418 sprintf(S,_("%lid %lih %limin %lis"),Sec/60/60/24,(Sec/60/60) % 24,(Sec/60) % 60,Sec % 60);
6c139d6e
AL
419 break;
420 }
421
422 if (Sec > 60*60)
423 {
09fab244
MV
424 //h means hours, min means minutes, s means seconds
425 sprintf(S,_("%lih %limin %lis"),Sec/60/60,(Sec/60) % 60,Sec % 60);
6c139d6e
AL
426 break;
427 }
428
429 if (Sec > 60)
430 {
09fab244
MV
431 //min means minutes, s means seconds
432 sprintf(S,_("%limin %lis"),Sec/60,Sec % 60);
6c139d6e
AL
433 break;
434 }
09fab244
MV
435
436 //s means seconds
437 sprintf(S,_("%lis"),Sec);
6c139d6e
AL
438 break;
439 }
440
441 return S;
442}
443 /*}}}*/
444// SubstVar - Substitute a string for another string /*{{{*/
445// ---------------------------------------------------------------------
1e3f4083 446/* This replaces all occurrences of Subst with Contents in Str. */
171c75f1 447string SubstVar(const string &Str,const string &Subst,const string &Contents)
6c139d6e 448{
224dc038
DK
449 if (Subst.empty() == true)
450 return Str;
451
8efa2a3b 452 string::size_type Pos = 0;
6c139d6e
AL
453 string::size_type OldPos = 0;
454 string Temp;
224dc038
DK
455
456 while (OldPos < Str.length() &&
6c139d6e
AL
457 (Pos = Str.find(Subst,OldPos)) != string::npos)
458 {
224dc038
DK
459 if (OldPos != Pos)
460 Temp.append(Str, OldPos, Pos - OldPos);
461 if (Contents.empty() == false)
462 Temp.append(Contents);
463 OldPos = Pos + Subst.length();
6c139d6e 464 }
224dc038 465
6c139d6e
AL
466 if (OldPos == 0)
467 return Str;
224dc038
DK
468
469 if (OldPos >= Str.length())
470 return Temp;
6c139d6e
AL
471 return Temp + string(Str,OldPos);
472}
b2e465d6
AL
473string SubstVar(string Str,const struct SubstVar *Vars)
474{
475 for (; Vars->Subst != 0; Vars++)
476 Str = SubstVar(Str,Vars->Subst,*Vars->Contents);
477 return Str;
478}
6c139d6e 479 /*}}}*/
fa3b0945
MV
480// OutputInDepth - return a string with separator multiplied with depth /*{{{*/
481// ---------------------------------------------------------------------
482/* Returns a string with the supplied separator depth + 1 times in it */
483std::string OutputInDepth(const unsigned long Depth, const char* Separator)
484{
485 std::string output = "";
486 for(unsigned long d=Depth+1; d > 0; d--)
487 output.append(Separator);
488 return output;
489}
490 /*}}}*/
ad00ae81
AL
491// URItoFileName - Convert the uri into a unique file name /*{{{*/
492// ---------------------------------------------------------------------
493/* This converts a URI into a safe filename. It quotes all unsafe characters
494 and converts / to _ and removes the scheme identifier. The resulting
495 file name should be unique and never occur again for a different file */
171c75f1 496string URItoFileName(const string &URI)
ad00ae81 497{
54cf15cb
AL
498 // Nuke 'sensitive' items
499 ::URI U(URI);
171c75f1
MV
500 U.User.clear();
501 U.Password.clear();
502 U.Access.clear();
54cf15cb 503
ad00ae81 504 // "\x00-\x20{}|\\\\^\\[\\]<>\"\x7F-\xFF";
171c75f1
MV
505 string NewURI = QuoteString(U,"\\|{}[]<>\"^~_=!@#$%^&*");
506 replace(NewURI.begin(),NewURI.end(),'/','_');
507 return NewURI;
ad00ae81
AL
508}
509 /*}}}*/
6c139d6e
AL
510// Base64Encode - Base64 Encoding routine for short strings /*{{{*/
511// ---------------------------------------------------------------------
512/* This routine performs a base64 transformation on a string. It was ripped
513 from wget and then patched and bug fixed.
514
515 This spec can be found in rfc2045 */
171c75f1 516string Base64Encode(const string &S)
6c139d6e
AL
517{
518 // Conversion table.
519 static char tbl[64] = {'A','B','C','D','E','F','G','H',
520 'I','J','K','L','M','N','O','P',
521 'Q','R','S','T','U','V','W','X',
522 'Y','Z','a','b','c','d','e','f',
523 'g','h','i','j','k','l','m','n',
524 'o','p','q','r','s','t','u','v',
525 'w','x','y','z','0','1','2','3',
526 '4','5','6','7','8','9','+','/'};
527
528 // Pre-allocate some space
529 string Final;
530 Final.reserve((4*S.length() + 2)/3 + 2);
531
532 /* Transform the 3x8 bits to 4x6 bits, as required by
533 base64. */
5933aab2 534 for (string::const_iterator I = S.begin(); I < S.end(); I += 3)
6c139d6e
AL
535 {
536 char Bits[3] = {0,0,0};
537 Bits[0] = I[0];
5933aab2 538 if (I + 1 < S.end())
6c139d6e 539 Bits[1] = I[1];
5933aab2 540 if (I + 2 < S.end())
6c139d6e
AL
541 Bits[2] = I[2];
542
543 Final += tbl[Bits[0] >> 2];
544 Final += tbl[((Bits[0] & 3) << 4) + (Bits[1] >> 4)];
545
5933aab2 546 if (I + 1 >= S.end())
6c139d6e
AL
547 break;
548
549 Final += tbl[((Bits[1] & 0xf) << 2) + (Bits[2] >> 6)];
550
5933aab2 551 if (I + 2 >= S.end())
6c139d6e
AL
552 break;
553
554 Final += tbl[Bits[2] & 0x3f];
555 }
556
557 /* Apply the padding elements, this tells how many bytes the remote
558 end should discard */
559 if (S.length() % 3 == 2)
560 Final += '=';
561 if (S.length() % 3 == 1)
562 Final += "==";
563
564 return Final;
565}
566 /*}}}*/
0da8987a 567// stringcmp - Arbitrary string compare /*{{{*/
6c139d6e 568// ---------------------------------------------------------------------
7365ff46 569/* This safely compares two non-null terminated strings of arbitrary
6c139d6e
AL
570 length */
571int stringcmp(const char *A,const char *AEnd,const char *B,const char *BEnd)
47db8997
AL
572{
573 for (; A != AEnd && B != BEnd; A++, B++)
574 if (*A != *B)
575 break;
576
577 if (A == AEnd && B == BEnd)
578 return 0;
579 if (A == AEnd)
580 return 1;
581 if (B == BEnd)
582 return -1;
583 if (*A < *B)
584 return -1;
585 return 1;
586}
ae0b19f5
AL
587
588#if __GNUC__ >= 3
47db8997
AL
589int stringcmp(string::const_iterator A,string::const_iterator AEnd,
590 const char *B,const char *BEnd)
591{
592 for (; A != AEnd && B != BEnd; A++, B++)
593 if (*A != *B)
594 break;
595
596 if (A == AEnd && B == BEnd)
597 return 0;
598 if (A == AEnd)
599 return 1;
600 if (B == BEnd)
601 return -1;
602 if (*A < *B)
603 return -1;
604 return 1;
605}
606int stringcmp(string::const_iterator A,string::const_iterator AEnd,
607 string::const_iterator B,string::const_iterator BEnd)
6c139d6e
AL
608{
609 for (; A != AEnd && B != BEnd; A++, B++)
610 if (*A != *B)
611 break;
612
613 if (A == AEnd && B == BEnd)
614 return 0;
615 if (A == AEnd)
616 return 1;
617 if (B == BEnd)
618 return -1;
619 if (*A < *B)
620 return -1;
621 return 1;
622}
ae0b19f5 623#endif
6c139d6e 624 /*}}}*/
0da8987a 625// stringcasecmp - Arbitrary case insensitive string compare /*{{{*/
6c139d6e
AL
626// ---------------------------------------------------------------------
627/* */
628int stringcasecmp(const char *A,const char *AEnd,const char *B,const char *BEnd)
47db8997
AL
629{
630 for (; A != AEnd && B != BEnd; A++, B++)
6dc60370 631 if (tolower_ascii(*A) != tolower_ascii(*B))
47db8997
AL
632 break;
633
634 if (A == AEnd && B == BEnd)
635 return 0;
636 if (A == AEnd)
637 return 1;
638 if (B == BEnd)
639 return -1;
6dc60370 640 if (tolower_ascii(*A) < tolower_ascii(*B))
47db8997
AL
641 return -1;
642 return 1;
643}
ae0b19f5 644#if __GNUC__ >= 3
47db8997
AL
645int stringcasecmp(string::const_iterator A,string::const_iterator AEnd,
646 const char *B,const char *BEnd)
647{
648 for (; A != AEnd && B != BEnd; A++, B++)
6dc60370 649 if (tolower_ascii(*A) != tolower_ascii(*B))
47db8997
AL
650 break;
651
652 if (A == AEnd && B == BEnd)
653 return 0;
654 if (A == AEnd)
655 return 1;
656 if (B == BEnd)
657 return -1;
6dc60370 658 if (tolower_ascii(*A) < tolower_ascii(*B))
47db8997
AL
659 return -1;
660 return 1;
661}
662int stringcasecmp(string::const_iterator A,string::const_iterator AEnd,
663 string::const_iterator B,string::const_iterator BEnd)
6c139d6e
AL
664{
665 for (; A != AEnd && B != BEnd; A++, B++)
6dc60370 666 if (tolower_ascii(*A) != tolower_ascii(*B))
6c139d6e 667 break;
3b5421b4 668
6c139d6e
AL
669 if (A == AEnd && B == BEnd)
670 return 0;
671 if (A == AEnd)
672 return 1;
673 if (B == BEnd)
674 return -1;
6dc60370 675 if (tolower_ascii(*A) < tolower_ascii(*B))
6c139d6e
AL
676 return -1;
677 return 1;
678}
ae0b19f5 679#endif
6c139d6e 680 /*}}}*/
3b5421b4
AL
681// LookupTag - Lookup the value of a tag in a taged string /*{{{*/
682// ---------------------------------------------------------------------
683/* The format is like those used in package files and the method
684 communication system */
171c75f1 685string LookupTag(const string &Message,const char *Tag,const char *Default)
3b5421b4
AL
686{
687 // Look for a matching tag.
688 int Length = strlen(Tag);
f7f0d6c7 689 for (string::const_iterator I = Message.begin(); I + Length < Message.end(); ++I)
3b5421b4
AL
690 {
691 // Found the tag
692 if (I[Length] == ':' && stringcasecmp(I,I+Length,Tag) == 0)
693 {
694 // Find the end of line and strip the leading/trailing spaces
171c75f1 695 string::const_iterator J;
3b5421b4 696 I += Length + 1;
f7f0d6c7
DK
697 for (; isspace(*I) != 0 && I < Message.end(); ++I);
698 for (J = I; *J != '\n' && J < Message.end(); ++J);
699 for (; J > I && isspace(J[-1]) != 0; --J);
3b5421b4 700
0db4a45b 701 return string(I,J);
3b5421b4
AL
702 }
703
f7f0d6c7 704 for (; *I != '\n' && I < Message.end(); ++I);
3b5421b4
AL
705 }
706
707 // Failed to find a match
708 if (Default == 0)
709 return string();
710 return Default;
711}
712 /*}}}*/
713// StringToBool - Converts a string into a boolean /*{{{*/
714// ---------------------------------------------------------------------
715/* This inspects the string to see if it is true or if it is false and
716 then returns the result. Several varients on true/false are checked. */
171c75f1 717int StringToBool(const string &Text,int Default)
3b5421b4 718{
08be0ca3
MV
719 char *ParseEnd;
720 int Res = strtol(Text.c_str(),&ParseEnd,0);
721 // ensure that the entire string was converted by strtol to avoid
722 // failures on "apt-cache show -a 0ad" where the "0" is converted
723 const char *TextEnd = Text.c_str()+Text.size();
724 if (ParseEnd == TextEnd && Res >= 0 && Res <= 1)
3b5421b4
AL
725 return Res;
726
727 // Check for positives
728 if (strcasecmp(Text.c_str(),"no") == 0 ||
729 strcasecmp(Text.c_str(),"false") == 0 ||
730 strcasecmp(Text.c_str(),"without") == 0 ||
7f25bdff 731 strcasecmp(Text.c_str(),"off") == 0 ||
3b5421b4
AL
732 strcasecmp(Text.c_str(),"disable") == 0)
733 return 0;
734
735 // Check for negatives
736 if (strcasecmp(Text.c_str(),"yes") == 0 ||
737 strcasecmp(Text.c_str(),"true") == 0 ||
738 strcasecmp(Text.c_str(),"with") == 0 ||
7f25bdff 739 strcasecmp(Text.c_str(),"on") == 0 ||
3b5421b4
AL
740 strcasecmp(Text.c_str(),"enable") == 0)
741 return 1;
742
743 return Default;
744}
745 /*}}}*/
0a8a80e5
AL
746// TimeRFC1123 - Convert a time_t into RFC1123 format /*{{{*/
747// ---------------------------------------------------------------------
748/* This converts a time_t into a string time representation that is
749 year 2000 complient and timezone neutral */
750string TimeRFC1123(time_t Date)
751{
410327e1
DK
752 struct tm Conv;
753 if (gmtime_r(&Date, &Conv) == NULL)
754 return "";
0a8a80e5 755
410327e1 756 char Buf[300];
0a8a80e5
AL
757 const char *Day[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
758 const char *Month[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul",
759 "Aug","Sep","Oct","Nov","Dec"};
760
8ff84cf3 761 snprintf(Buf, sizeof(Buf), "%s, %02i %s %i %02i:%02i:%02i GMT",Day[Conv.tm_wday],
0a8a80e5
AL
762 Conv.tm_mday,Month[Conv.tm_mon],Conv.tm_year+1900,Conv.tm_hour,
763 Conv.tm_min,Conv.tm_sec);
764 return Buf;
765}
766 /*}}}*/
767// ReadMessages - Read messages from the FD /*{{{*/
768// ---------------------------------------------------------------------
769/* This pulls full messages from the input FD into the message buffer.
770 It assumes that messages will not pause during transit so no
ffc36991
DB
771 fancy buffering is used.
772
773 In particular: this reads blocks from the input until it believes
774 that it's run out of input text. Each block is terminated by a
775 double newline ('\n' followed by '\n'). As noted below, there is a
776 bug in this code: it assumes that all the blocks have been read if
777 it doesn't see additional text in the buffer after the last one is
778 parsed, which will cause it to lose blocks if the last block
779 coincides with the end of the buffer.
780 */
0a8a80e5
AL
781bool ReadMessages(int Fd, vector<string> &List)
782{
aee70518 783 char Buffer[64000];
0a8a80e5 784 char *End = Buffer;
ffc36991
DB
785 // Represents any left-over from the previous iteration of the
786 // parse loop. (i.e., if a message is split across the end
787 // of the buffer, it goes here)
788 string PartialMessage;
0a8a80e5
AL
789
790 while (1)
791 {
792 int Res = read(Fd,End,sizeof(Buffer) - (End-Buffer));
b0db36b1
AL
793 if (Res < 0 && errno == EINTR)
794 continue;
0a8a80e5
AL
795
796 // Process is dead, this is kind of bad..
797 if (Res == 0)
798 return false;
799
800 // No data
b2e465d6 801 if (Res < 0 && errno == EAGAIN)
0a8a80e5 802 return true;
b2e465d6
AL
803 if (Res < 0)
804 return false;
805
0a8a80e5
AL
806 End += Res;
807
808 // Look for the end of the message
c88edf1d 809 for (char *I = Buffer; I + 1 < End; I++)
0a8a80e5 810 {
70903865
DK
811 if (I[1] != '\n' ||
812 (I[0] != '\n' && strncmp(I, "\r\n\r\n", 4) != 0))
0a8a80e5
AL
813 continue;
814
815 // Pull the message out
d48c6a7d 816 string Message(Buffer,I-Buffer);
ffc36991 817 PartialMessage += Message;
0a8a80e5
AL
818
819 // Fix up the buffer
70903865 820 for (; I < End && (*I == '\n' || *I == '\r'); ++I);
0a8a80e5
AL
821 End -= I-Buffer;
822 memmove(Buffer,I,End-Buffer);
823 I = Buffer;
824
ffc36991
DB
825 List.push_back(PartialMessage);
826 PartialMessage.clear();
0a8a80e5 827 }
ffc36991
DB
828 if (End != Buffer)
829 {
830 // If there's text left in the buffer, store it
831 // in PartialMessage and throw the rest of the buffer
832 // away. This allows us to handle messages that
833 // are longer than the static buffer size.
834 PartialMessage += string(Buffer, End);
835 End = Buffer;
836 }
837 else
838 {
839 // BUG ALERT: if a message block happens to end at a
840 // multiple of 64000 characters, this will cause it to
841 // terminate early, leading to a badly formed block and
842 // probably crashing the method. However, this is the only
843 // way we have to find the end of the message block. I have
844 // an idea of how to fix this, but it will require changes
845 // to the protocol (essentially to mark the beginning and
846 // end of the block).
847 //
848 // -- dburrows 2008-04-02
849 return true;
850 }
0a8a80e5
AL
851
852 if (WaitFd(Fd) == false)
853 return false;
854 }
855}
856 /*}}}*/
24231681
AL
857// MonthConv - Converts a month string into a number /*{{{*/
858// ---------------------------------------------------------------------
859/* This was lifted from the boa webserver which lifted it from 'wn-v1.07'
6dc60370 860 Made it a bit more robust with a few tolower_ascii though. */
24231681
AL
861static int MonthConv(char *Month)
862{
6dc60370 863 switch (tolower_ascii(*Month))
24231681 864 {
6dc60370
DK
865 case 'a':
866 return tolower_ascii(Month[1]) == 'p'?3:7;
867 case 'd':
24231681 868 return 11;
6dc60370 869 case 'f':
24231681 870 return 1;
6dc60370
DK
871 case 'j':
872 if (tolower_ascii(Month[1]) == 'a')
24231681 873 return 0;
6dc60370
DK
874 return tolower_ascii(Month[2]) == 'n'?5:6;
875 case 'm':
876 return tolower_ascii(Month[2]) == 'r'?2:4;
877 case 'n':
24231681 878 return 10;
6dc60370 879 case 'o':
24231681 880 return 9;
6dc60370 881 case 's':
24231681
AL
882 return 8;
883
884 // Pretend it is January..
885 default:
886 return 0;
887 }
888}
889 /*}}}*/
55089145 890// timegm - Internal timegm if the gnu version is not available /*{{{*/
6d5dd02a 891// ---------------------------------------------------------------------
55089145 892/* Converts struct tm to time_t, assuming the data in tm is UTC rather
6d5dd02a 893 than local timezone (mktime assumes the latter).
41b6caf4 894
55089145
DK
895 This function is a nonstandard GNU extension that is also present on
896 the BSDs and maybe other systems. For others we follow the advice of
897 the manpage of timegm and use his portable replacement. */
898#ifndef HAVE_TIMEGM
6d5dd02a
AL
899static time_t timegm(struct tm *t)
900{
55089145
DK
901 char *tz = getenv("TZ");
902 setenv("TZ", "", 1);
903 tzset();
904 time_t ret = mktime(t);
905 if (tz)
906 setenv("TZ", tz, 1);
907 else
908 unsetenv("TZ");
909 tzset();
910 return ret;
6d5dd02a
AL
911}
912#endif
913 /*}}}*/
cd8cf88f
DK
914// FullDateToTime - Converts a HTTP1.1 full date strings into a time_t /*{{{*/
915// ---------------------------------------------------------------------
916/* tries to parses a full date as specified in RFC2616 Section 3.3.1
917 with one exception: All timezones (%Z) are accepted but the protocol
918 says that it MUST be GMT, but this one is equal to UTC which we will
919 encounter from time to time (e.g. in Release files) so we accept all
920 here and just assume it is GMT (or UTC) later on */
921bool RFC1123StrToTime(const char* const str,time_t &time)
922{
923 struct tm Tm;
24d7b626
DK
924 setlocale (LC_ALL,"C");
925 bool const invalid =
cd8cf88f 926 // Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
24d7b626 927 (strptime(str, "%a, %d %b %Y %H:%M:%S %Z", &Tm) == NULL &&
cd8cf88f
DK
928 // Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
929 strptime(str, "%A, %d-%b-%y %H:%M:%S %Z", &Tm) == NULL &&
930 // Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
24d7b626
DK
931 strptime(str, "%a %b %d %H:%M:%S %Y", &Tm) == NULL);
932 setlocale (LC_ALL,"");
933 if (invalid == true)
cd8cf88f
DK
934 return false;
935
936 time = timegm(&Tm);
937 return true;
938}
939 /*}}}*/
940// FTPMDTMStrToTime - Converts a ftp modification date into a time_t /*{{{*/
941// ---------------------------------------------------------------------
942/* */
943bool FTPMDTMStrToTime(const char* const str,time_t &time)
944{
945 struct tm Tm;
946 // MDTM includes no whitespaces but recommend and ignored by strptime
947 if (strptime(str, "%Y %m %d %H %M %S", &Tm) == NULL)
948 return false;
949
950 time = timegm(&Tm);
951 return true;
952}
953 /*}}}*/
24231681
AL
954// StrToTime - Converts a string into a time_t /*{{{*/
955// ---------------------------------------------------------------------
1e3f4083 956/* This handles all 3 popular time formats including RFC 1123, RFC 1036
24231681
AL
957 and the C library asctime format. It requires the GNU library function
958 'timegm' to convert a struct tm in UTC to a time_t. For some bizzar
f58a97d3
AL
959 reason the C library does not provide any such function :< This also
960 handles the weird, but unambiguous FTP time format*/
171c75f1 961bool StrToTime(const string &Val,time_t &Result)
24231681
AL
962{
963 struct tm Tm;
964 char Month[10];
404528bd 965
24231681 966 // Skip the day of the week
404528bd
DK
967 const char *I = strchr(Val.c_str(), ' ');
968
24231681 969 // Handle RFC 1123 time
f58a97d3 970 Month[0] = 0;
324cbd56 971 if (sscanf(I," %2d %3s %4d %2d:%2d:%2d GMT",&Tm.tm_mday,Month,&Tm.tm_year,
24231681
AL
972 &Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec) != 6)
973 {
974 // Handle RFC 1036 time
324cbd56 975 if (sscanf(I," %2d-%3s-%3d %2d:%2d:%2d GMT",&Tm.tm_mday,Month,
24231681
AL
976 &Tm.tm_year,&Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec) == 6)
977 Tm.tm_year += 1900;
978 else
979 {
980 // asctime format
324cbd56 981 if (sscanf(I," %3s %2d %2d:%2d:%2d %4d",Month,&Tm.tm_mday,
24231681 982 &Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec,&Tm.tm_year) != 6)
f58a97d3
AL
983 {
984 // 'ftp' time
7ef72446 985 if (sscanf(Val.c_str(),"%4d%2d%2d%2d%2d%2d",&Tm.tm_year,&Tm.tm_mon,
f58a97d3
AL
986 &Tm.tm_mday,&Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec) != 6)
987 return false;
988 Tm.tm_mon--;
989 }
24231681
AL
990 }
991 }
992
993 Tm.tm_isdst = 0;
f58a97d3
AL
994 if (Month[0] != 0)
995 Tm.tm_mon = MonthConv(Month);
70e0c168
MV
996 else
997 Tm.tm_mon = 0; // we don't have a month, so pick something
24231681
AL
998 Tm.tm_year -= 1900;
999
1000 // Convert to local time and then to GMT
1001 Result = timegm(&Tm);
1002 return true;
1003}
1004 /*}}}*/
ddc1d8d0
AL
1005// StrToNum - Convert a fixed length string to a number /*{{{*/
1006// ---------------------------------------------------------------------
1007/* This is used in decoding the crazy fixed length string headers in
1008 tar and ar files. */
1009bool StrToNum(const char *Str,unsigned long &Res,unsigned Len,unsigned Base)
1010{
1011 char S[30];
1012 if (Len >= sizeof(S))
1013 return false;
1014 memcpy(S,Str,Len);
1015 S[Len] = 0;
1016
1017 // All spaces is a zero
1018 Res = 0;
1019 unsigned I;
1020 for (I = 0; S[I] == ' '; I++);
1021 if (S[I] == 0)
1022 return true;
1023
1024 char *End;
1025 Res = strtoul(S,&End,Base);
1026 if (End == S)
1027 return false;
1028
1029 return true;
1030}
1031 /*}}}*/
650faab0
DK
1032// StrToNum - Convert a fixed length string to a number /*{{{*/
1033// ---------------------------------------------------------------------
1034/* This is used in decoding the crazy fixed length string headers in
1035 tar and ar files. */
1036bool StrToNum(const char *Str,unsigned long long &Res,unsigned Len,unsigned Base)
1037{
1038 char S[30];
1039 if (Len >= sizeof(S))
1040 return false;
1041 memcpy(S,Str,Len);
1042 S[Len] = 0;
1043
1044 // All spaces is a zero
1045 Res = 0;
1046 unsigned I;
1047 for (I = 0; S[I] == ' '; I++);
1048 if (S[I] == 0)
1049 return true;
1050
1051 char *End;
1052 Res = strtoull(S,&End,Base);
1053 if (End == S)
1054 return false;
1055
1056 return true;
1057}
1058 /*}}}*/
1059
54f2f0a3
NH
1060// Base256ToNum - Convert a fixed length binary to a number /*{{{*/
1061// ---------------------------------------------------------------------
1062/* This is used in decoding the 256bit encoded fixed length fields in
1063 tar files */
f688d1d3 1064bool Base256ToNum(const char *Str,unsigned long &Res,unsigned int Len)
54f2f0a3 1065{
54f2f0a3
NH
1066 if ((Str[0] & 0x80) == 0)
1067 return false;
1068 else
1069 {
1070 Res = Str[0] & 0x7F;
f688d1d3 1071 for(unsigned int i = 1; i < Len; ++i)
54f2f0a3
NH
1072 Res = (Res<<8) + Str[i];
1073 return true;
1074 }
1075}
1076 /*}}}*/
6e52073f
AL
1077// HexDigit - Convert a hex character into an integer /*{{{*/
1078// ---------------------------------------------------------------------
1079/* Helper for Hex2Num */
1080static int HexDigit(int c)
1081{
1082 if (c >= '0' && c <= '9')
1083 return c - '0';
1084 if (c >= 'a' && c <= 'f')
1085 return c - 'a' + 10;
1086 if (c >= 'A' && c <= 'F')
1087 return c - 'A' + 10;
1088 return 0;
1089}
1090 /*}}}*/
1091// Hex2Num - Convert a long hex number into a buffer /*{{{*/
1092// ---------------------------------------------------------------------
1093/* The length of the buffer must be exactly 1/2 the length of the string. */
171c75f1 1094bool Hex2Num(const string &Str,unsigned char *Num,unsigned int Length)
6e52073f 1095{
0db4a45b 1096 if (Str.length() != Length*2)
6e52073f
AL
1097 return false;
1098
1099 // Convert each digit. We store it in the same order as the string
1100 int J = 0;
0db4a45b 1101 for (string::const_iterator I = Str.begin(); I != Str.end();J++, I += 2)
6e52073f
AL
1102 {
1103 if (isxdigit(*I) == 0 || isxdigit(I[1]) == 0)
1104 return false;
1105
1106 Num[J] = HexDigit(I[0]) << 4;
1107 Num[J] += HexDigit(I[1]);
1108 }
1109
1110 return true;
1111}
1112 /*}}}*/
b2e465d6
AL
1113// TokSplitString - Split a string up by a given token /*{{{*/
1114// ---------------------------------------------------------------------
1115/* This is intended to be a faster splitter, it does not use dynamic
1116 memories. Input is changed to insert nulls at each token location. */
1117bool TokSplitString(char Tok,char *Input,char **List,
1118 unsigned long ListMax)
1119{
1120 // Strip any leading spaces
1121 char *Start = Input;
1122 char *Stop = Start + strlen(Start);
1123 for (; *Start != 0 && isspace(*Start) != 0; Start++);
1124
1125 unsigned long Count = 0;
1126 char *Pos = Start;
1127 while (Pos != Stop)
1128 {
1129 // Skip to the next Token
1130 for (; Pos != Stop && *Pos != Tok; Pos++);
1131
1132 // Back remove spaces
1133 char *End = Pos;
1134 for (; End > Start && (End[-1] == Tok || isspace(End[-1]) != 0); End--);
1135 *End = 0;
1136
1137 List[Count++] = Start;
1138 if (Count >= ListMax)
1139 {
1140 List[Count-1] = 0;
1141 return false;
1142 }
1143
1144 // Advance pos
1145 for (; Pos != Stop && (*Pos == Tok || isspace(*Pos) != 0 || *Pos == 0); Pos++);
1146 Start = Pos;
1147 }
1148
1149 List[Count] = 0;
1150 return true;
1151}
1152 /*}}}*/
3f42500d 1153// VectorizeString - Split a string up into a vector of strings /*{{{*/
d7cf5923
DK
1154// ---------------------------------------------------------------------
1155/* This can be used to split a given string up into a vector, so the
1156 propose is the same as in the method above and this one is a bit slower
3f42500d
DK
1157 also, but the advantage is that we have an iteratable vector */
1158vector<string> VectorizeString(string const &haystack, char const &split)
d7cf5923 1159{
a5414e56
DK
1160 vector<string> exploded;
1161 if (haystack.empty() == true)
1162 return exploded;
d7cf5923
DK
1163 string::const_iterator start = haystack.begin();
1164 string::const_iterator end = start;
d7cf5923
DK
1165 do {
1166 for (; end != haystack.end() && *end != split; ++end);
1167 exploded.push_back(string(start, end));
1168 start = end + 1;
1169 } while (end != haystack.end() && (++end) != haystack.end());
1170 return exploded;
1171}
1172 /*}}}*/
9572a54b 1173// StringSplit - split a string into a string vector by token /*{{{*/
00f4d9ff 1174// ---------------------------------------------------------------------
41053d72 1175/* See header for details.
00f4d9ff 1176 */
41053d72 1177vector<string> StringSplit(std::string const &s, std::string const &sep,
85bf0019 1178 unsigned int maxsplit)
00f4d9ff
MV
1179{
1180 vector<string> split;
1181 size_t start, pos;
85bf0019 1182
9572a54b 1183 // no seperator given, this is bogus
00f4d9ff
MV
1184 if(sep.size() == 0)
1185 return split;
85bf0019
MV
1186
1187 start = pos = 0;
9572a54b
MV
1188 while (pos != string::npos)
1189 {
00f4d9ff
MV
1190 pos = s.find(sep, start);
1191 split.push_back(s.substr(start, pos-start));
85bf0019 1192
9572a54b 1193 // if maxsplit is reached, the remaining string is the last item
2ddab3fb 1194 if(split.size() >= maxsplit)
85bf0019
MV
1195 {
1196 split[split.size()-1] = s.substr(start);
1197 break;
1198 }
1199 start = pos+sep.size();
9572a54b 1200 }
00f4d9ff
MV
1201 return split;
1202}
1203 /*}}}*/
b2e465d6
AL
1204// RegexChoice - Simple regex list/list matcher /*{{{*/
1205// ---------------------------------------------------------------------
1206/* */
1207unsigned long RegexChoice(RxChoiceList *Rxs,const char **ListBegin,
1208 const char **ListEnd)
1209{
1210 for (RxChoiceList *R = Rxs; R->Str != 0; R++)
1211 R->Hit = false;
1212
1213 unsigned long Hits = 0;
ef74268b 1214 for (; ListBegin < ListEnd; ++ListBegin)
b2e465d6
AL
1215 {
1216 // Check if the name is a regex
1217 const char *I;
1218 bool Regex = true;
1219 for (I = *ListBegin; *I != 0; I++)
1220 if (*I == '.' || *I == '?' || *I == '*' || *I == '|')
1221 break;
1222 if (*I == 0)
1223 Regex = false;
1224
1225 // Compile the regex pattern
1226 regex_t Pattern;
1227 if (Regex == true)
1228 if (regcomp(&Pattern,*ListBegin,REG_EXTENDED | REG_ICASE |
1229 REG_NOSUB) != 0)
1230 Regex = false;
1231
1232 // Search the list
1233 bool Done = false;
1234 for (RxChoiceList *R = Rxs; R->Str != 0; R++)
1235 {
1236 if (R->Str[0] == 0)
1237 continue;
1238
1239 if (strcasecmp(R->Str,*ListBegin) != 0)
1240 {
1241 if (Regex == false)
1242 continue;
1243 if (regexec(&Pattern,R->Str,0,0,0) != 0)
1244 continue;
1245 }
1246 Done = true;
1247
1248 if (R->Hit == false)
1249 Hits++;
1250
1251 R->Hit = true;
1252 }
1253
1254 if (Regex == true)
1255 regfree(&Pattern);
1256
1257 if (Done == false)
1258 _error->Warning(_("Selection %s not found"),*ListBegin);
1259 }
1260
1261 return Hits;
1262}
1263 /*}}}*/
5076b3c2 1264// {str,io}printf - C format string outputter to C++ strings/iostreams /*{{{*/
b2e465d6 1265// ---------------------------------------------------------------------
1168596f
AL
1266/* This is used to make the internationalization strings easier to translate
1267 and to allow reordering of parameters */
5076b3c2
DK
1268static bool iovprintf(ostream &out, const char *format,
1269 va_list &args, ssize_t &size) {
1270 char *S = (char*)malloc(size);
1271 ssize_t const n = vsnprintf(S, size, format, args);
1272 if (n > -1 && n < size) {
1273 out << S;
1274 free(S);
1275 return true;
1276 } else {
1277 if (n > -1)
1278 size = n + 1;
1279 else
1280 size *= 2;
1281 }
1282 free(S);
1283 return false;
1284}
1285void ioprintf(ostream &out,const char *format,...)
b2e465d6
AL
1286{
1287 va_list args;
5076b3c2
DK
1288 ssize_t size = 400;
1289 while (true) {
1290 va_start(args,format);
1291 if (iovprintf(out, format, args, size) == true)
1292 return;
1293 va_end(args);
1294 }
1168596f 1295}
5076b3c2 1296void strprintf(string &out,const char *format,...)
d4cd303e
MV
1297{
1298 va_list args;
5076b3c2
DK
1299 ssize_t size = 400;
1300 std::ostringstream outstr;
1301 while (true) {
1302 va_start(args,format);
1303 if (iovprintf(outstr, format, args, size) == true)
1304 break;
1305 va_end(args);
1306 }
1307 out = outstr.str();
d4cd303e
MV
1308}
1309 /*}}}*/
1168596f
AL
1310// safe_snprintf - Safer snprintf /*{{{*/
1311// ---------------------------------------------------------------------
1312/* This is a snprintf that will never (ever) go past 'End' and returns a
1313 pointer to the end of the new string. The returned string is always null
1314 terminated unless Buffer == end. This is a better alterantive to using
1315 consecutive snprintfs. */
1316char *safe_snprintf(char *Buffer,char *End,const char *Format,...)
1317{
1318 va_list args;
ea6db08d 1319 int Did;
1168596f 1320
1168596f
AL
1321 if (End <= Buffer)
1322 return End;
163dc55b 1323 va_start(args,Format);
1168596f 1324 Did = vsnprintf(Buffer,End - Buffer,Format,args);
163dc55b
MV
1325 va_end(args);
1326
1168596f
AL
1327 if (Did < 0 || Buffer + Did > End)
1328 return End;
1329 return Buffer + Did;
b2e465d6
AL
1330}
1331 /*}}}*/
cdb9307c
MV
1332// StripEpoch - Remove the version "epoch" from a version string /*{{{*/
1333// ---------------------------------------------------------------------
1334string StripEpoch(const string &VerStr)
1335{
1336 size_t i = VerStr.find(":");
1337 if (i == string::npos)
1338 return VerStr;
1339 return VerStr.substr(i+1);
1340}
69c2ecbd 1341 /*}}}*/
4e86942a
MV
1342// tolower_ascii - tolower() function that ignores the locale /*{{{*/
1343// ---------------------------------------------------------------------
6dc60370 1344/* This little function is the most called method we have and tries
1e3f4083 1345 therefore to do the absolut minimum - and is notable faster than
6dc60370
DK
1346 standard tolower/toupper and as a bonus avoids problems with different
1347 locales - we only operate on ascii chars anyway. */
1348int tolower_ascii(int const c)
4e86942a 1349{
6dc60370 1350 if (c >= 'A' && c <= 'Z')
4e86942a
MV
1351 return c + 32;
1352 return c;
1353}
1354 /*}}}*/
1355
1e3f4083 1356// CheckDomainList - See if Host is in a , separate list /*{{{*/
f8081133 1357// ---------------------------------------------------------------------
1e3f4083 1358/* The domain list is a comma separate list of domains that are suffix
f8081133 1359 matched against the argument */
171c75f1 1360bool CheckDomainList(const string &Host,const string &List)
f8081133 1361{
47db8997 1362 string::const_iterator Start = List.begin();
f7f0d6c7 1363 for (string::const_iterator Cur = List.begin(); Cur <= List.end(); ++Cur)
f8081133 1364 {
47db8997 1365 if (Cur < List.end() && *Cur != ',')
f8081133
AL
1366 continue;
1367
1368 // Match the end of the string..
e2c7e6b5 1369 if ((Host.size() >= (unsigned)(Cur - Start)) &&
f8081133 1370 Cur - Start != 0 &&
47db8997 1371 stringcasecmp(Host.end() - (Cur - Start),Host.end(),Start,Cur) == 0)
f8081133
AL
1372 return true;
1373
1374 Start = Cur + 1;
1375 }
1376 return false;
1377}
1378 /*}}}*/
b9179170
MV
1379// strv_length - Return the length of a NULL-terminated string array /*{{{*/
1380// ---------------------------------------------------------------------
1381/* */
1382size_t strv_length(const char **str_array)
1383{
1384 size_t i;
1385 for (i=0; str_array[i] != NULL; i++)
1386 /* nothing */
1387 ;
1388 return i;
1389}
1390
69c2ecbd 1391// DeEscapeString - unescape (\0XX and \xXX) from a string /*{{{*/
a513ace2 1392// ---------------------------------------------------------------------
cca2efe6
MV
1393/* */
1394string DeEscapeString(const string &input)
a513ace2 1395{
b9dc4706 1396 char tmp[3];
69c2ecbd
DK
1397 string::const_iterator it;
1398 string output;
f7f0d6c7 1399 for (it = input.begin(); it != input.end(); ++it)
a513ace2
MV
1400 {
1401 // just copy non-escape chars
1402 if (*it != '\\')
1403 {
1404 output += *it;
1405 continue;
1406 }
f8081133 1407
a513ace2
MV
1408 // deal with double escape
1409 if (*it == '\\' &&
1410 (it + 1 < input.end()) && it[1] == '\\')
1411 {
1412 // copy
1413 output += *it;
1414 // advance iterator one step further
f7f0d6c7 1415 ++it;
a513ace2
MV
1416 continue;
1417 }
1418
1419 // ensure we have a char to read
1420 if (it + 1 == input.end())
1421 continue;
f8081133 1422
a513ace2 1423 // read it
f7f0d6c7 1424 ++it;
a513ace2
MV
1425 switch (*it)
1426 {
1427 case '0':
b9dc4706 1428 if (it + 2 <= input.end()) {
a513ace2
MV
1429 tmp[0] = it[1];
1430 tmp[1] = it[2];
b9dc4706 1431 tmp[2] = 0;
a513ace2
MV
1432 output += (char)strtol(tmp, 0, 8);
1433 it += 2;
1434 }
1435 break;
1436 case 'x':
1437 if (it + 2 <= input.end()) {
1438 tmp[0] = it[1];
1439 tmp[1] = it[2];
1440 tmp[2] = 0;
1441 output += (char)strtol(tmp, 0, 16);
1442 it += 2;
1443 }
1444 break;
1445 default:
1446 // FIXME: raise exception here?
a513ace2
MV
1447 break;
1448 }
1449 }
1450 return output;
1451}
1452 /*}}}*/
be4401bf 1453// URI::CopyFrom - Copy from an object /*{{{*/
93bf083d
AL
1454// ---------------------------------------------------------------------
1455/* This parses the URI into all of its components */
171c75f1 1456void URI::CopyFrom(const string &U)
93bf083d 1457{
5933aab2 1458 string::const_iterator I = U.begin();
93bf083d 1459
b2e465d6 1460 // Locate the first colon, this separates the scheme
f7f0d6c7 1461 for (; I < U.end() && *I != ':' ; ++I);
5933aab2 1462 string::const_iterator FirstColon = I;
93bf083d 1463
bfd22fc0
AL
1464 /* Determine if this is a host type URI with a leading double //
1465 and then search for the first single / */
5933aab2
AL
1466 string::const_iterator SingleSlash = I;
1467 if (I + 3 < U.end() && I[1] == '/' && I[2] == '/')
bfd22fc0 1468 SingleSlash += 3;
67ff87bf
AL
1469
1470 /* Find the / indicating the end of the hostname, ignoring /'s in the
1471 square brackets */
1472 bool InBracket = false;
f7f0d6c7 1473 for (; SingleSlash < U.end() && (*SingleSlash != '/' || InBracket == true); ++SingleSlash)
67ff87bf
AL
1474 {
1475 if (*SingleSlash == '[')
1476 InBracket = true;
1477 if (InBracket == true && *SingleSlash == ']')
1478 InBracket = false;
1479 }
1480
5933aab2
AL
1481 if (SingleSlash > U.end())
1482 SingleSlash = U.end();
93bf083d
AL
1483
1484 // We can now write the access and path specifiers
171c75f1 1485 Access.assign(U.begin(),FirstColon);
5933aab2 1486 if (SingleSlash != U.end())
171c75f1 1487 Path.assign(SingleSlash,U.end());
92e889c8
AL
1488 if (Path.empty() == true)
1489 Path = "/";
1490
93bf083d 1491 // Now we attempt to locate a user:pass@host fragment
d48c6a7d 1492 if (FirstColon + 2 <= U.end() && FirstColon[1] == '/' && FirstColon[2] == '/')
f46e7681
AL
1493 FirstColon += 3;
1494 else
1495 FirstColon += 1;
5933aab2 1496 if (FirstColon >= U.end())
93bf083d
AL
1497 return;
1498
1499 if (FirstColon > SingleSlash)
1500 FirstColon = SingleSlash;
1501
3856756b
AL
1502 // Find the colon...
1503 I = FirstColon + 1;
1d38d0e9
AL
1504 if (I > SingleSlash)
1505 I = SingleSlash;
f7f0d6c7 1506 for (; I < SingleSlash && *I != ':'; ++I);
5933aab2 1507 string::const_iterator SecondColon = I;
3856756b
AL
1508
1509 // Search for the @ after the colon
f7f0d6c7 1510 for (; I < SingleSlash && *I != '@'; ++I);
5933aab2 1511 string::const_iterator At = I;
93bf083d 1512
93bf083d
AL
1513 // Now write the host and user/pass
1514 if (At == SingleSlash)
1515 {
1516 if (FirstColon < SingleSlash)
171c75f1 1517 Host.assign(FirstColon,SingleSlash);
93bf083d
AL
1518 }
1519 else
1520 {
171c75f1 1521 Host.assign(At+1,SingleSlash);
436d7eab
DK
1522 // username and password must be encoded (RFC 3986)
1523 User.assign(DeQuoteString(FirstColon,SecondColon));
93bf083d 1524 if (SecondColon < At)
436d7eab 1525 Password.assign(DeQuoteString(SecondColon+1,At));
93bf083d
AL
1526 }
1527
67ff87bf
AL
1528 // Now we parse the RFC 2732 [] hostnames.
1529 unsigned long PortEnd = 0;
1530 InBracket = false;
1531 for (unsigned I = 0; I != Host.length();)
1532 {
1533 if (Host[I] == '[')
1534 {
1535 InBracket = true;
1536 Host.erase(I,1);
1537 continue;
1538 }
1539
1540 if (InBracket == true && Host[I] == ']')
1541 {
1542 InBracket = false;
1543 Host.erase(I,1);
1544 PortEnd = I;
1545 continue;
1546 }
1547 I++;
1548 }
1549
1550 // Tsk, weird.
1551 if (InBracket == true)
1552 {
171c75f1 1553 Host.clear();
67ff87bf
AL
1554 return;
1555 }
1556
1d38d0e9 1557 // Now we parse off a port number from the hostname
93bf083d
AL
1558 Port = 0;
1559 string::size_type Pos = Host.rfind(':');
67ff87bf 1560 if (Pos == string::npos || Pos < PortEnd)
93bf083d
AL
1561 return;
1562
1563 Port = atoi(string(Host,Pos+1).c_str());
171c75f1 1564 Host.assign(Host,0,Pos);
93bf083d
AL
1565}
1566 /*}}}*/
1567// URI::operator string - Convert the URI to a string /*{{{*/
1568// ---------------------------------------------------------------------
1569/* */
1570URI::operator string()
1571{
54cf15cb
AL
1572 string Res;
1573
1574 if (Access.empty() == false)
1575 Res = Access + ':';
1576
93bf083d 1577 if (Host.empty() == false)
7834cb57 1578 {
54cf15cb
AL
1579 if (Access.empty() == false)
1580 Res += "//";
7834cb57 1581
93bf083d
AL
1582 if (User.empty() == false)
1583 {
5b63d2a9
MV
1584 // FIXME: Technically userinfo is permitted even less
1585 // characters than these, but this is not conveniently
1586 // expressed with a blacklist.
1587 Res += QuoteString(User, ":/?#[]@");
93bf083d 1588 if (Password.empty() == false)
5b63d2a9 1589 Res += ":" + QuoteString(Password, ":/?#[]@");
93bf083d
AL
1590 Res += "@";
1591 }
54cf15cb 1592
7834cb57
AL
1593 // Add RFC 2732 escaping characters
1594 if (Access.empty() == false &&
1595 (Host.find('/') != string::npos || Host.find(':') != string::npos))
1596 Res += '[' + Host + ']';
1597 else
1598 Res += Host;
1599
492f957a
AL
1600 if (Port != 0)
1601 {
1602 char S[30];
1603 sprintf(S,":%u",Port);
1604 Res += S;
1605 }
93bf083d
AL
1606 }
1607
1608 if (Path.empty() == false)
492f957a
AL
1609 {
1610 if (Path[0] != '/')
1611 Res += "/" + Path;
1612 else
1613 Res += Path;
1614 }
93bf083d
AL
1615
1616 return Res;
1617}
1618 /*}}}*/
b2e465d6
AL
1619// URI::SiteOnly - Return the schema and site for the URI /*{{{*/
1620// ---------------------------------------------------------------------
1621/* */
171c75f1 1622string URI::SiteOnly(const string &URI)
b2e465d6
AL
1623{
1624 ::URI U(URI);
171c75f1
MV
1625 U.User.clear();
1626 U.Password.clear();
1627 U.Path.clear();
b2e465d6
AL
1628 return U;
1629}
1630 /*}}}*/
5e02df82
MV
1631// URI::NoUserPassword - Return the schema, site and path for the URI /*{{{*/
1632// ---------------------------------------------------------------------
1633/* */
1634string URI::NoUserPassword(const string &URI)
1635{
1636 ::URI U(URI);
1637 U.User.clear();
1638 U.Password.clear();
5e02df82
MV
1639 return U;
1640}
1641 /*}}}*/