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