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