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