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