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