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