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