]> git.saurik.com Git - apt.git/blame - apt-pkg/contrib/strutl.cc
let DPKG_COLORS default to our APT::Color setting
[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;
732510fe
AW
467
468 Temp.append(Str, OldPos, string::npos);
469 return Temp;
6c139d6e 470}
b2e465d6
AL
471string SubstVar(string Str,const struct SubstVar *Vars)
472{
473 for (; Vars->Subst != 0; Vars++)
474 Str = SubstVar(Str,Vars->Subst,*Vars->Contents);
475 return Str;
476}
6c139d6e 477 /*}}}*/
fa3b0945
MV
478// OutputInDepth - return a string with separator multiplied with depth /*{{{*/
479// ---------------------------------------------------------------------
480/* Returns a string with the supplied separator depth + 1 times in it */
481std::string OutputInDepth(const unsigned long Depth, const char* Separator)
482{
483 std::string output = "";
484 for(unsigned long d=Depth+1; d > 0; d--)
485 output.append(Separator);
486 return output;
487}
488 /*}}}*/
ad00ae81
AL
489// URItoFileName - Convert the uri into a unique file name /*{{{*/
490// ---------------------------------------------------------------------
491/* This converts a URI into a safe filename. It quotes all unsafe characters
492 and converts / to _ and removes the scheme identifier. The resulting
493 file name should be unique and never occur again for a different file */
171c75f1 494string URItoFileName(const string &URI)
ad00ae81 495{
54cf15cb
AL
496 // Nuke 'sensitive' items
497 ::URI U(URI);
171c75f1
MV
498 U.User.clear();
499 U.Password.clear();
500 U.Access.clear();
54cf15cb 501
ad00ae81 502 // "\x00-\x20{}|\\\\^\\[\\]<>\"\x7F-\xFF";
171c75f1
MV
503 string NewURI = QuoteString(U,"\\|{}[]<>\"^~_=!@#$%^&*");
504 replace(NewURI.begin(),NewURI.end(),'/','_');
505 return NewURI;
ad00ae81
AL
506}
507 /*}}}*/
6c139d6e
AL
508// Base64Encode - Base64 Encoding routine for short strings /*{{{*/
509// ---------------------------------------------------------------------
510/* This routine performs a base64 transformation on a string. It was ripped
511 from wget and then patched and bug fixed.
512
513 This spec can be found in rfc2045 */
171c75f1 514string Base64Encode(const string &S)
6c139d6e
AL
515{
516 // Conversion table.
517 static char tbl[64] = {'A','B','C','D','E','F','G','H',
518 'I','J','K','L','M','N','O','P',
519 'Q','R','S','T','U','V','W','X',
520 'Y','Z','a','b','c','d','e','f',
521 'g','h','i','j','k','l','m','n',
522 'o','p','q','r','s','t','u','v',
523 'w','x','y','z','0','1','2','3',
524 '4','5','6','7','8','9','+','/'};
525
526 // Pre-allocate some space
527 string Final;
528 Final.reserve((4*S.length() + 2)/3 + 2);
529
530 /* Transform the 3x8 bits to 4x6 bits, as required by
531 base64. */
5933aab2 532 for (string::const_iterator I = S.begin(); I < S.end(); I += 3)
6c139d6e
AL
533 {
534 char Bits[3] = {0,0,0};
535 Bits[0] = I[0];
5933aab2 536 if (I + 1 < S.end())
6c139d6e 537 Bits[1] = I[1];
5933aab2 538 if (I + 2 < S.end())
6c139d6e
AL
539 Bits[2] = I[2];
540
541 Final += tbl[Bits[0] >> 2];
542 Final += tbl[((Bits[0] & 3) << 4) + (Bits[1] >> 4)];
543
5933aab2 544 if (I + 1 >= S.end())
6c139d6e
AL
545 break;
546
547 Final += tbl[((Bits[1] & 0xf) << 2) + (Bits[2] >> 6)];
548
5933aab2 549 if (I + 2 >= S.end())
6c139d6e
AL
550 break;
551
552 Final += tbl[Bits[2] & 0x3f];
553 }
554
555 /* Apply the padding elements, this tells how many bytes the remote
556 end should discard */
557 if (S.length() % 3 == 2)
558 Final += '=';
559 if (S.length() % 3 == 1)
560 Final += "==";
561
562 return Final;
563}
564 /*}}}*/
0da8987a 565// stringcmp - Arbitrary string compare /*{{{*/
6c139d6e 566// ---------------------------------------------------------------------
7365ff46 567/* This safely compares two non-null terminated strings of arbitrary
6c139d6e
AL
568 length */
569int stringcmp(const char *A,const char *AEnd,const char *B,const char *BEnd)
47db8997
AL
570{
571 for (; A != AEnd && B != BEnd; A++, B++)
572 if (*A != *B)
573 break;
574
575 if (A == AEnd && B == BEnd)
576 return 0;
577 if (A == AEnd)
578 return 1;
579 if (B == BEnd)
580 return -1;
581 if (*A < *B)
582 return -1;
583 return 1;
584}
ae0b19f5
AL
585
586#if __GNUC__ >= 3
47db8997
AL
587int stringcmp(string::const_iterator A,string::const_iterator AEnd,
588 const char *B,const char *BEnd)
589{
590 for (; A != AEnd && B != BEnd; A++, B++)
591 if (*A != *B)
592 break;
593
594 if (A == AEnd && B == BEnd)
595 return 0;
596 if (A == AEnd)
597 return 1;
598 if (B == BEnd)
599 return -1;
600 if (*A < *B)
601 return -1;
602 return 1;
603}
604int stringcmp(string::const_iterator A,string::const_iterator AEnd,
605 string::const_iterator B,string::const_iterator BEnd)
6c139d6e
AL
606{
607 for (; A != AEnd && B != BEnd; A++, B++)
608 if (*A != *B)
609 break;
610
611 if (A == AEnd && B == BEnd)
612 return 0;
613 if (A == AEnd)
614 return 1;
615 if (B == BEnd)
616 return -1;
617 if (*A < *B)
618 return -1;
619 return 1;
620}
ae0b19f5 621#endif
6c139d6e 622 /*}}}*/
0da8987a 623// stringcasecmp - Arbitrary case insensitive string compare /*{{{*/
6c139d6e
AL
624// ---------------------------------------------------------------------
625/* */
626int stringcasecmp(const char *A,const char *AEnd,const char *B,const char *BEnd)
47db8997
AL
627{
628 for (; A != AEnd && B != BEnd; A++, B++)
6dc60370 629 if (tolower_ascii(*A) != tolower_ascii(*B))
47db8997
AL
630 break;
631
632 if (A == AEnd && B == BEnd)
633 return 0;
634 if (A == AEnd)
635 return 1;
636 if (B == BEnd)
637 return -1;
6dc60370 638 if (tolower_ascii(*A) < tolower_ascii(*B))
47db8997
AL
639 return -1;
640 return 1;
641}
ae0b19f5 642#if __GNUC__ >= 3
47db8997
AL
643int stringcasecmp(string::const_iterator A,string::const_iterator AEnd,
644 const char *B,const char *BEnd)
645{
646 for (; A != AEnd && B != BEnd; A++, B++)
6dc60370 647 if (tolower_ascii(*A) != tolower_ascii(*B))
47db8997
AL
648 break;
649
650 if (A == AEnd && B == BEnd)
651 return 0;
652 if (A == AEnd)
653 return 1;
654 if (B == BEnd)
655 return -1;
6dc60370 656 if (tolower_ascii(*A) < tolower_ascii(*B))
47db8997
AL
657 return -1;
658 return 1;
659}
660int stringcasecmp(string::const_iterator A,string::const_iterator AEnd,
661 string::const_iterator B,string::const_iterator BEnd)
6c139d6e
AL
662{
663 for (; A != AEnd && B != BEnd; A++, B++)
6dc60370 664 if (tolower_ascii(*A) != tolower_ascii(*B))
6c139d6e 665 break;
3b5421b4 666
6c139d6e
AL
667 if (A == AEnd && B == BEnd)
668 return 0;
669 if (A == AEnd)
670 return 1;
671 if (B == BEnd)
672 return -1;
6dc60370 673 if (tolower_ascii(*A) < tolower_ascii(*B))
6c139d6e
AL
674 return -1;
675 return 1;
676}
ae0b19f5 677#endif
6c139d6e 678 /*}}}*/
3b5421b4
AL
679// LookupTag - Lookup the value of a tag in a taged string /*{{{*/
680// ---------------------------------------------------------------------
681/* The format is like those used in package files and the method
682 communication system */
171c75f1 683string LookupTag(const string &Message,const char *Tag,const char *Default)
3b5421b4
AL
684{
685 // Look for a matching tag.
686 int Length = strlen(Tag);
f7f0d6c7 687 for (string::const_iterator I = Message.begin(); I + Length < Message.end(); ++I)
3b5421b4
AL
688 {
689 // Found the tag
690 if (I[Length] == ':' && stringcasecmp(I,I+Length,Tag) == 0)
691 {
692 // Find the end of line and strip the leading/trailing spaces
171c75f1 693 string::const_iterator J;
3b5421b4 694 I += Length + 1;
74dedb4a 695 for (; isspace_ascii(*I) != 0 && I < Message.end(); ++I);
f7f0d6c7 696 for (J = I; *J != '\n' && J < Message.end(); ++J);
74dedb4a 697 for (; J > I && isspace_ascii(J[-1]) != 0; --J);
3b5421b4 698
0db4a45b 699 return string(I,J);
3b5421b4
AL
700 }
701
f7f0d6c7 702 for (; *I != '\n' && I < Message.end(); ++I);
3b5421b4
AL
703 }
704
705 // Failed to find a match
706 if (Default == 0)
707 return string();
708 return Default;
709}
710 /*}}}*/
711// StringToBool - Converts a string into a boolean /*{{{*/
712// ---------------------------------------------------------------------
713/* This inspects the string to see if it is true or if it is false and
714 then returns the result. Several varients on true/false are checked. */
171c75f1 715int StringToBool(const string &Text,int Default)
3b5421b4 716{
08be0ca3
MV
717 char *ParseEnd;
718 int Res = strtol(Text.c_str(),&ParseEnd,0);
719 // ensure that the entire string was converted by strtol to avoid
720 // failures on "apt-cache show -a 0ad" where the "0" is converted
721 const char *TextEnd = Text.c_str()+Text.size();
722 if (ParseEnd == TextEnd && Res >= 0 && Res <= 1)
3b5421b4
AL
723 return Res;
724
725 // Check for positives
726 if (strcasecmp(Text.c_str(),"no") == 0 ||
727 strcasecmp(Text.c_str(),"false") == 0 ||
728 strcasecmp(Text.c_str(),"without") == 0 ||
7f25bdff 729 strcasecmp(Text.c_str(),"off") == 0 ||
3b5421b4
AL
730 strcasecmp(Text.c_str(),"disable") == 0)
731 return 0;
732
733 // Check for negatives
734 if (strcasecmp(Text.c_str(),"yes") == 0 ||
735 strcasecmp(Text.c_str(),"true") == 0 ||
736 strcasecmp(Text.c_str(),"with") == 0 ||
7f25bdff 737 strcasecmp(Text.c_str(),"on") == 0 ||
3b5421b4
AL
738 strcasecmp(Text.c_str(),"enable") == 0)
739 return 1;
740
741 return Default;
742}
743 /*}}}*/
0a8a80e5
AL
744// TimeRFC1123 - Convert a time_t into RFC1123 format /*{{{*/
745// ---------------------------------------------------------------------
746/* This converts a time_t into a string time representation that is
747 year 2000 complient and timezone neutral */
748string TimeRFC1123(time_t Date)
749{
410327e1
DK
750 struct tm Conv;
751 if (gmtime_r(&Date, &Conv) == NULL)
752 return "";
0a8a80e5 753
410327e1 754 char Buf[300];
0a8a80e5
AL
755 const char *Day[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
756 const char *Month[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul",
757 "Aug","Sep","Oct","Nov","Dec"};
758
8ff84cf3 759 snprintf(Buf, sizeof(Buf), "%s, %02i %s %i %02i:%02i:%02i GMT",Day[Conv.tm_wday],
0a8a80e5
AL
760 Conv.tm_mday,Month[Conv.tm_mon],Conv.tm_year+1900,Conv.tm_hour,
761 Conv.tm_min,Conv.tm_sec);
762 return Buf;
763}
764 /*}}}*/
765// ReadMessages - Read messages from the FD /*{{{*/
766// ---------------------------------------------------------------------
767/* This pulls full messages from the input FD into the message buffer.
768 It assumes that messages will not pause during transit so no
ffc36991
DB
769 fancy buffering is used.
770
771 In particular: this reads blocks from the input until it believes
772 that it's run out of input text. Each block is terminated by a
d8c71b3b 773 double newline ('\n' followed by '\n').
ffc36991 774 */
0a8a80e5
AL
775bool ReadMessages(int Fd, vector<string> &List)
776{
aee70518 777 char Buffer[64000];
ffc36991
DB
778 // Represents any left-over from the previous iteration of the
779 // parse loop. (i.e., if a message is split across the end
780 // of the buffer, it goes here)
781 string PartialMessage;
d8c71b3b
DK
782
783 do {
784 int const Res = read(Fd, Buffer, sizeof(Buffer));
b0db36b1
AL
785 if (Res < 0 && errno == EINTR)
786 continue;
d8c71b3b
DK
787
788 // process we read from has died
0a8a80e5
AL
789 if (Res == 0)
790 return false;
d8c71b3b 791
0a8a80e5 792 // No data
3b0e76ec 793#if EAGAIN != EWOULDBLOCK
d8c71b3b 794 if (Res < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
3b0e76ec
DK
795#else
796 if (Res < 0 && errno == EAGAIN)
797#endif
0a8a80e5 798 return true;
b2e465d6
AL
799 if (Res < 0)
800 return false;
d8c71b3b
DK
801
802 // extract the message(s) from the buffer
803 char const *Start = Buffer;
804 char const * const End = Buffer + Res;
805
806 char const * NL = (char const *) memchr(Start, '\n', End - Start);
807 if (NL == NULL)
0a8a80e5 808 {
d8c71b3b
DK
809 // end of buffer: store what we have so far and read new data in
810 PartialMessage.append(Start, End - Start);
811 Start = End;
0a8a80e5 812 }
ffc36991 813 else
d8c71b3b
DK
814 ++NL;
815
816 if (PartialMessage.empty() == false && Start < End)
817 {
818 // if we start with a new line, see if the partial message we have ended with one
819 // so that we properly detect records ending between two read() runs
820 // cases are: \n|\n , \r\n|\r\n and \r\n\r|\n
821 // the case \r|\n\r\n is handled by the usual double-newline handling
822 if ((NL - Start) == 1 || ((NL - Start) == 2 && *Start == '\r'))
823 {
824 if (APT::String::Endswith(PartialMessage, "\n") || APT::String::Endswith(PartialMessage, "\r\n\r"))
825 {
826 PartialMessage.erase(PartialMessage.find_last_not_of("\r\n") + 1);
827 List.push_back(PartialMessage);
828 PartialMessage.clear();
829 while (NL < End && (*NL == '\n' || *NL == '\r')) ++NL;
830 Start = NL;
831 }
832 }
833 }
834
835 while (Start < End) {
836 char const * NL2 = (char const *) memchr(NL, '\n', End - NL);
837 if (NL2 == NULL)
838 {
839 // end of buffer: store what we have so far and read new data in
840 PartialMessage.append(Start, End - Start);
841 break;
842 }
843 ++NL2;
844
845 // did we find a double newline?
846 if ((NL2 - NL) == 1 || ((NL2 - NL) == 2 && *NL == '\r'))
847 {
848 PartialMessage.append(Start, NL2 - Start);
849 PartialMessage.erase(PartialMessage.find_last_not_of("\r\n") + 1);
850 List.push_back(PartialMessage);
851 PartialMessage.clear();
852 while (NL2 < End && (*NL2 == '\n' || *NL2 == '\r')) ++NL2;
853 Start = NL2;
854 }
855 NL = NL2;
856 }
857
858 // we have read at least one complete message and nothing left
859 if (PartialMessage.empty() == true)
860 return true;
0a8a80e5
AL
861
862 if (WaitFd(Fd) == false)
863 return false;
d8c71b3b 864 } while (true);
0a8a80e5
AL
865}
866 /*}}}*/
24231681
AL
867// MonthConv - Converts a month string into a number /*{{{*/
868// ---------------------------------------------------------------------
869/* This was lifted from the boa webserver which lifted it from 'wn-v1.07'
6dc60370 870 Made it a bit more robust with a few tolower_ascii though. */
24231681
AL
871static int MonthConv(char *Month)
872{
6dc60370 873 switch (tolower_ascii(*Month))
24231681 874 {
6dc60370
DK
875 case 'a':
876 return tolower_ascii(Month[1]) == 'p'?3:7;
877 case 'd':
24231681 878 return 11;
6dc60370 879 case 'f':
24231681 880 return 1;
6dc60370
DK
881 case 'j':
882 if (tolower_ascii(Month[1]) == 'a')
24231681 883 return 0;
6dc60370
DK
884 return tolower_ascii(Month[2]) == 'n'?5:6;
885 case 'm':
886 return tolower_ascii(Month[2]) == 'r'?2:4;
887 case 'n':
24231681 888 return 10;
6dc60370 889 case 'o':
24231681 890 return 9;
6dc60370 891 case 's':
24231681
AL
892 return 8;
893
894 // Pretend it is January..
895 default:
896 return 0;
897 }
898}
899 /*}}}*/
55089145 900// timegm - Internal timegm if the gnu version is not available /*{{{*/
6d5dd02a 901// ---------------------------------------------------------------------
55089145 902/* Converts struct tm to time_t, assuming the data in tm is UTC rather
6d5dd02a 903 than local timezone (mktime assumes the latter).
41b6caf4 904
55089145
DK
905 This function is a nonstandard GNU extension that is also present on
906 the BSDs and maybe other systems. For others we follow the advice of
907 the manpage of timegm and use his portable replacement. */
908#ifndef HAVE_TIMEGM
6d5dd02a
AL
909static time_t timegm(struct tm *t)
910{
55089145
DK
911 char *tz = getenv("TZ");
912 setenv("TZ", "", 1);
913 tzset();
914 time_t ret = mktime(t);
915 if (tz)
916 setenv("TZ", tz, 1);
917 else
918 unsetenv("TZ");
919 tzset();
920 return ret;
6d5dd02a
AL
921}
922#endif
923 /*}}}*/
cd8cf88f
DK
924// FullDateToTime - Converts a HTTP1.1 full date strings into a time_t /*{{{*/
925// ---------------------------------------------------------------------
926/* tries to parses a full date as specified in RFC2616 Section 3.3.1
927 with one exception: All timezones (%Z) are accepted but the protocol
928 says that it MUST be GMT, but this one is equal to UTC which we will
929 encounter from time to time (e.g. in Release files) so we accept all
930 here and just assume it is GMT (or UTC) later on */
931bool RFC1123StrToTime(const char* const str,time_t &time)
932{
933 struct tm Tm;
24d7b626
DK
934 setlocale (LC_ALL,"C");
935 bool const invalid =
cd8cf88f 936 // Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
24d7b626 937 (strptime(str, "%a, %d %b %Y %H:%M:%S %Z", &Tm) == NULL &&
cd8cf88f
DK
938 // Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
939 strptime(str, "%A, %d-%b-%y %H:%M:%S %Z", &Tm) == NULL &&
940 // Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
24d7b626
DK
941 strptime(str, "%a %b %d %H:%M:%S %Y", &Tm) == NULL);
942 setlocale (LC_ALL,"");
943 if (invalid == true)
cd8cf88f
DK
944 return false;
945
946 time = timegm(&Tm);
947 return true;
948}
949 /*}}}*/
950// FTPMDTMStrToTime - Converts a ftp modification date into a time_t /*{{{*/
951// ---------------------------------------------------------------------
952/* */
953bool FTPMDTMStrToTime(const char* const str,time_t &time)
954{
955 struct tm Tm;
956 // MDTM includes no whitespaces but recommend and ignored by strptime
957 if (strptime(str, "%Y %m %d %H %M %S", &Tm) == NULL)
958 return false;
959
960 time = timegm(&Tm);
961 return true;
962}
963 /*}}}*/
24231681
AL
964// StrToTime - Converts a string into a time_t /*{{{*/
965// ---------------------------------------------------------------------
1e3f4083 966/* This handles all 3 popular time formats including RFC 1123, RFC 1036
24231681
AL
967 and the C library asctime format. It requires the GNU library function
968 'timegm' to convert a struct tm in UTC to a time_t. For some bizzar
f58a97d3
AL
969 reason the C library does not provide any such function :< This also
970 handles the weird, but unambiguous FTP time format*/
171c75f1 971bool StrToTime(const string &Val,time_t &Result)
24231681
AL
972{
973 struct tm Tm;
974 char Month[10];
404528bd 975
24231681 976 // Skip the day of the week
404528bd
DK
977 const char *I = strchr(Val.c_str(), ' ');
978
24231681 979 // Handle RFC 1123 time
f58a97d3 980 Month[0] = 0;
324cbd56 981 if (sscanf(I," %2d %3s %4d %2d:%2d:%2d GMT",&Tm.tm_mday,Month,&Tm.tm_year,
24231681
AL
982 &Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec) != 6)
983 {
984 // Handle RFC 1036 time
324cbd56 985 if (sscanf(I," %2d-%3s-%3d %2d:%2d:%2d GMT",&Tm.tm_mday,Month,
24231681
AL
986 &Tm.tm_year,&Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec) == 6)
987 Tm.tm_year += 1900;
988 else
989 {
990 // asctime format
324cbd56 991 if (sscanf(I," %3s %2d %2d:%2d:%2d %4d",Month,&Tm.tm_mday,
24231681 992 &Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec,&Tm.tm_year) != 6)
f58a97d3
AL
993 {
994 // 'ftp' time
7ef72446 995 if (sscanf(Val.c_str(),"%4d%2d%2d%2d%2d%2d",&Tm.tm_year,&Tm.tm_mon,
f58a97d3
AL
996 &Tm.tm_mday,&Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec) != 6)
997 return false;
998 Tm.tm_mon--;
999 }
24231681
AL
1000 }
1001 }
1002
1003 Tm.tm_isdst = 0;
f58a97d3
AL
1004 if (Month[0] != 0)
1005 Tm.tm_mon = MonthConv(Month);
70e0c168
MV
1006 else
1007 Tm.tm_mon = 0; // we don't have a month, so pick something
24231681
AL
1008 Tm.tm_year -= 1900;
1009
1010 // Convert to local time and then to GMT
1011 Result = timegm(&Tm);
1012 return true;
1013}
1014 /*}}}*/
ddc1d8d0
AL
1015// StrToNum - Convert a fixed length string to a number /*{{{*/
1016// ---------------------------------------------------------------------
1017/* This is used in decoding the crazy fixed length string headers in
1018 tar and ar files. */
1019bool StrToNum(const char *Str,unsigned long &Res,unsigned Len,unsigned Base)
1020{
1021 char S[30];
1022 if (Len >= sizeof(S))
1023 return false;
1024 memcpy(S,Str,Len);
1025 S[Len] = 0;
1026
1027 // All spaces is a zero
1028 Res = 0;
1029 unsigned I;
1030 for (I = 0; S[I] == ' '; I++);
1031 if (S[I] == 0)
1032 return true;
1033
1034 char *End;
1035 Res = strtoul(S,&End,Base);
1036 if (End == S)
1037 return false;
1038
1039 return true;
1040}
1041 /*}}}*/
650faab0
DK
1042// StrToNum - Convert a fixed length string to a number /*{{{*/
1043// ---------------------------------------------------------------------
1044/* This is used in decoding the crazy fixed length string headers in
1045 tar and ar files. */
1046bool StrToNum(const char *Str,unsigned long long &Res,unsigned Len,unsigned Base)
1047{
1048 char S[30];
1049 if (Len >= sizeof(S))
1050 return false;
1051 memcpy(S,Str,Len);
1052 S[Len] = 0;
1053
1054 // All spaces is a zero
1055 Res = 0;
1056 unsigned I;
1057 for (I = 0; S[I] == ' '; I++);
1058 if (S[I] == 0)
1059 return true;
1060
1061 char *End;
1062 Res = strtoull(S,&End,Base);
1063 if (End == S)
1064 return false;
1065
1066 return true;
1067}
1068 /*}}}*/
1069
54f2f0a3
NH
1070// Base256ToNum - Convert a fixed length binary to a number /*{{{*/
1071// ---------------------------------------------------------------------
1072/* This is used in decoding the 256bit encoded fixed length fields in
1073 tar files */
3c09d634 1074bool Base256ToNum(const char *Str,unsigned long long &Res,unsigned int Len)
54f2f0a3 1075{
54f2f0a3
NH
1076 if ((Str[0] & 0x80) == 0)
1077 return false;
1078 else
1079 {
1080 Res = Str[0] & 0x7F;
f688d1d3 1081 for(unsigned int i = 1; i < Len; ++i)
54f2f0a3
NH
1082 Res = (Res<<8) + Str[i];
1083 return true;
1084 }
1085}
1086 /*}}}*/
3c09d634
GJ
1087// Base256ToNum - Convert a fixed length binary to a number /*{{{*/
1088// ---------------------------------------------------------------------
1089/* This is used in decoding the 256bit encoded fixed length fields in
1090 tar files */
1091bool Base256ToNum(const char *Str,unsigned long &Res,unsigned int Len)
1092{
1093 unsigned long long Num;
1094 bool rc;
1095
1096 rc = Base256ToNum(Str, Num, Len);
1097 Res = Num;
1098 if (Res != Num)
1099 return false;
1100
1101 return rc;
1102}
1103 /*}}}*/
6e52073f
AL
1104// HexDigit - Convert a hex character into an integer /*{{{*/
1105// ---------------------------------------------------------------------
1106/* Helper for Hex2Num */
1107static int HexDigit(int c)
1108{
1109 if (c >= '0' && c <= '9')
1110 return c - '0';
1111 if (c >= 'a' && c <= 'f')
1112 return c - 'a' + 10;
1113 if (c >= 'A' && c <= 'F')
1114 return c - 'A' + 10;
fc8f1c22 1115 return -1;
6e52073f
AL
1116}
1117 /*}}}*/
1118// Hex2Num - Convert a long hex number into a buffer /*{{{*/
1119// ---------------------------------------------------------------------
1120/* The length of the buffer must be exactly 1/2 the length of the string. */
171c75f1 1121bool Hex2Num(const string &Str,unsigned char *Num,unsigned int Length)
eff0c22e
JAK
1122{
1123 return Hex2Num(APT::StringView(Str), Num, Length);
1124}
1125
1126bool Hex2Num(const APT::StringView Str,unsigned char *Num,unsigned int Length)
6e52073f 1127{
0db4a45b 1128 if (Str.length() != Length*2)
6e52073f
AL
1129 return false;
1130
1131 // Convert each digit. We store it in the same order as the string
1132 int J = 0;
eff0c22e 1133 for (auto I = Str.begin(); I != Str.end();J++, I += 2)
6e52073f 1134 {
fc8f1c22
NT
1135 int first_half = HexDigit(I[0]);
1136 int second_half;
1137 if (first_half < 0)
6e52073f
AL
1138 return false;
1139
fc8f1c22
NT
1140 second_half = HexDigit(I[1]);
1141 if (second_half < 0)
1142 return false;
1143 Num[J] = first_half << 4;
1144 Num[J] += second_half;
6e52073f
AL
1145 }
1146
1147 return true;
1148}
1149 /*}}}*/
b2e465d6
AL
1150// TokSplitString - Split a string up by a given token /*{{{*/
1151// ---------------------------------------------------------------------
1152/* This is intended to be a faster splitter, it does not use dynamic
1153 memories. Input is changed to insert nulls at each token location. */
1154bool TokSplitString(char Tok,char *Input,char **List,
1155 unsigned long ListMax)
1156{
1157 // Strip any leading spaces
1158 char *Start = Input;
1159 char *Stop = Start + strlen(Start);
1160 for (; *Start != 0 && isspace(*Start) != 0; Start++);
1161
1162 unsigned long Count = 0;
1163 char *Pos = Start;
1164 while (Pos != Stop)
1165 {
1166 // Skip to the next Token
1167 for (; Pos != Stop && *Pos != Tok; Pos++);
1168
1169 // Back remove spaces
1170 char *End = Pos;
1171 for (; End > Start && (End[-1] == Tok || isspace(End[-1]) != 0); End--);
1172 *End = 0;
1173
1174 List[Count++] = Start;
1175 if (Count >= ListMax)
1176 {
1177 List[Count-1] = 0;
1178 return false;
1179 }
1180
1181 // Advance pos
1182 for (; Pos != Stop && (*Pos == Tok || isspace(*Pos) != 0 || *Pos == 0); Pos++);
1183 Start = Pos;
1184 }
1185
1186 List[Count] = 0;
1187 return true;
1188}
1189 /*}}}*/
3f42500d 1190// VectorizeString - Split a string up into a vector of strings /*{{{*/
d7cf5923
DK
1191// ---------------------------------------------------------------------
1192/* This can be used to split a given string up into a vector, so the
1193 propose is the same as in the method above and this one is a bit slower
3f42500d
DK
1194 also, but the advantage is that we have an iteratable vector */
1195vector<string> VectorizeString(string const &haystack, char const &split)
d7cf5923 1196{
a5414e56
DK
1197 vector<string> exploded;
1198 if (haystack.empty() == true)
1199 return exploded;
d7cf5923
DK
1200 string::const_iterator start = haystack.begin();
1201 string::const_iterator end = start;
d7cf5923
DK
1202 do {
1203 for (; end != haystack.end() && *end != split; ++end);
1204 exploded.push_back(string(start, end));
1205 start = end + 1;
1206 } while (end != haystack.end() && (++end) != haystack.end());
1207 return exploded;
1208}
1209 /*}}}*/
9572a54b 1210// StringSplit - split a string into a string vector by token /*{{{*/
00f4d9ff 1211// ---------------------------------------------------------------------
41053d72 1212/* See header for details.
00f4d9ff 1213 */
41053d72 1214vector<string> StringSplit(std::string const &s, std::string const &sep,
85bf0019 1215 unsigned int maxsplit)
00f4d9ff
MV
1216{
1217 vector<string> split;
1218 size_t start, pos;
85bf0019 1219
8d89cda7 1220 // no separator given, this is bogus
00f4d9ff
MV
1221 if(sep.size() == 0)
1222 return split;
85bf0019
MV
1223
1224 start = pos = 0;
9572a54b
MV
1225 while (pos != string::npos)
1226 {
00f4d9ff
MV
1227 pos = s.find(sep, start);
1228 split.push_back(s.substr(start, pos-start));
85bf0019 1229
9572a54b 1230 // if maxsplit is reached, the remaining string is the last item
2ddab3fb 1231 if(split.size() >= maxsplit)
85bf0019
MV
1232 {
1233 split[split.size()-1] = s.substr(start);
1234 break;
1235 }
1236 start = pos+sep.size();
9572a54b 1237 }
00f4d9ff
MV
1238 return split;
1239}
1240 /*}}}*/
b2e465d6
AL
1241// RegexChoice - Simple regex list/list matcher /*{{{*/
1242// ---------------------------------------------------------------------
1243/* */
1244unsigned long RegexChoice(RxChoiceList *Rxs,const char **ListBegin,
1245 const char **ListEnd)
1246{
1247 for (RxChoiceList *R = Rxs; R->Str != 0; R++)
1248 R->Hit = false;
1249
1250 unsigned long Hits = 0;
ef74268b 1251 for (; ListBegin < ListEnd; ++ListBegin)
b2e465d6
AL
1252 {
1253 // Check if the name is a regex
1254 const char *I;
1255 bool Regex = true;
1256 for (I = *ListBegin; *I != 0; I++)
1257 if (*I == '.' || *I == '?' || *I == '*' || *I == '|')
1258 break;
1259 if (*I == 0)
1260 Regex = false;
1261
1262 // Compile the regex pattern
1263 regex_t Pattern;
1264 if (Regex == true)
1265 if (regcomp(&Pattern,*ListBegin,REG_EXTENDED | REG_ICASE |
1266 REG_NOSUB) != 0)
1267 Regex = false;
1268
1269 // Search the list
1270 bool Done = false;
1271 for (RxChoiceList *R = Rxs; R->Str != 0; R++)
1272 {
1273 if (R->Str[0] == 0)
1274 continue;
1275
1276 if (strcasecmp(R->Str,*ListBegin) != 0)
1277 {
1278 if (Regex == false)
1279 continue;
1280 if (regexec(&Pattern,R->Str,0,0,0) != 0)
1281 continue;
1282 }
1283 Done = true;
1284
1285 if (R->Hit == false)
1286 Hits++;
1287
1288 R->Hit = true;
1289 }
1290
1291 if (Regex == true)
1292 regfree(&Pattern);
1293
1294 if (Done == false)
1295 _error->Warning(_("Selection %s not found"),*ListBegin);
1296 }
1297
1298 return Hits;
1299}
1300 /*}}}*/
5076b3c2 1301// {str,io}printf - C format string outputter to C++ strings/iostreams /*{{{*/
b2e465d6 1302// ---------------------------------------------------------------------
1168596f
AL
1303/* This is used to make the internationalization strings easier to translate
1304 and to allow reordering of parameters */
5076b3c2
DK
1305static bool iovprintf(ostream &out, const char *format,
1306 va_list &args, ssize_t &size) {
1307 char *S = (char*)malloc(size);
1308 ssize_t const n = vsnprintf(S, size, format, args);
1309 if (n > -1 && n < size) {
1310 out << S;
1311 free(S);
1312 return true;
1313 } else {
1314 if (n > -1)
1315 size = n + 1;
1316 else
1317 size *= 2;
1318 }
1319 free(S);
1320 return false;
1321}
1322void ioprintf(ostream &out,const char *format,...)
b2e465d6
AL
1323{
1324 va_list args;
5076b3c2
DK
1325 ssize_t size = 400;
1326 while (true) {
e8afd168 1327 bool ret;
5076b3c2 1328 va_start(args,format);
ce105e87 1329 ret = iovprintf(out, format, args, size);
5076b3c2 1330 va_end(args);
ce105e87
DK
1331 if (ret == true)
1332 return;
5076b3c2 1333 }
1168596f 1334}
5076b3c2 1335void strprintf(string &out,const char *format,...)
d4cd303e
MV
1336{
1337 va_list args;
5076b3c2
DK
1338 ssize_t size = 400;
1339 std::ostringstream outstr;
1340 while (true) {
e8afd168 1341 bool ret;
5076b3c2 1342 va_start(args,format);
ce105e87 1343 ret = iovprintf(outstr, format, args, size);
5076b3c2 1344 va_end(args);
ce105e87
DK
1345 if (ret == true)
1346 break;
5076b3c2
DK
1347 }
1348 out = outstr.str();
d4cd303e
MV
1349}
1350 /*}}}*/
1168596f
AL
1351// safe_snprintf - Safer snprintf /*{{{*/
1352// ---------------------------------------------------------------------
1353/* This is a snprintf that will never (ever) go past 'End' and returns a
1354 pointer to the end of the new string. The returned string is always null
1355 terminated unless Buffer == end. This is a better alterantive to using
1356 consecutive snprintfs. */
1357char *safe_snprintf(char *Buffer,char *End,const char *Format,...)
1358{
1359 va_list args;
ea6db08d 1360 int Did;
1168596f 1361
1168596f
AL
1362 if (End <= Buffer)
1363 return End;
163dc55b 1364 va_start(args,Format);
1168596f 1365 Did = vsnprintf(Buffer,End - Buffer,Format,args);
163dc55b
MV
1366 va_end(args);
1367
1168596f
AL
1368 if (Did < 0 || Buffer + Did > End)
1369 return End;
1370 return Buffer + Did;
b2e465d6
AL
1371}
1372 /*}}}*/
cdb9307c
MV
1373// StripEpoch - Remove the version "epoch" from a version string /*{{{*/
1374// ---------------------------------------------------------------------
1375string StripEpoch(const string &VerStr)
1376{
1377 size_t i = VerStr.find(":");
1378 if (i == string::npos)
1379 return VerStr;
1380 return VerStr.substr(i+1);
1381}
69c2ecbd 1382 /*}}}*/
98eb4e9e 1383
4e86942a
MV
1384// tolower_ascii - tolower() function that ignores the locale /*{{{*/
1385// ---------------------------------------------------------------------
6dc60370 1386/* This little function is the most called method we have and tries
1e3f4083 1387 therefore to do the absolut minimum - and is notable faster than
6dc60370
DK
1388 standard tolower/toupper and as a bonus avoids problems with different
1389 locales - we only operate on ascii chars anyway. */
98eb4e9e 1390#undef tolower_ascii
390344f9 1391int tolower_ascii(int const c) APT_CONST APT_COLD;
6dc60370 1392int tolower_ascii(int const c)
4e86942a 1393{
98eb4e9e 1394 return tolower_ascii_inline(c);
4e86942a
MV
1395}
1396 /*}}}*/
1397
98b06343
JAK
1398// isspace_ascii - isspace() function that ignores the locale /*{{{*/
1399// ---------------------------------------------------------------------
1400/* This little function is one of the most called methods we have and tries
1401 therefore to do the absolut minimum - and is notable faster than
1402 standard isspace() and as a bonus avoids problems with different
1403 locales - we only operate on ascii chars anyway. */
98eb4e9e 1404#undef isspace_ascii
390344f9 1405int isspace_ascii(int const c) APT_CONST APT_COLD;
98b06343
JAK
1406int isspace_ascii(int const c)
1407{
98eb4e9e 1408 return isspace_ascii_inline(c);
98b06343
JAK
1409}
1410 /*}}}*/
1411
1e3f4083 1412// CheckDomainList - See if Host is in a , separate list /*{{{*/
f8081133 1413// ---------------------------------------------------------------------
1e3f4083 1414/* The domain list is a comma separate list of domains that are suffix
f8081133 1415 matched against the argument */
171c75f1 1416bool CheckDomainList(const string &Host,const string &List)
f8081133 1417{
47db8997 1418 string::const_iterator Start = List.begin();
f7f0d6c7 1419 for (string::const_iterator Cur = List.begin(); Cur <= List.end(); ++Cur)
f8081133 1420 {
47db8997 1421 if (Cur < List.end() && *Cur != ',')
f8081133
AL
1422 continue;
1423
1424 // Match the end of the string..
e2c7e6b5 1425 if ((Host.size() >= (unsigned)(Cur - Start)) &&
f8081133 1426 Cur - Start != 0 &&
47db8997 1427 stringcasecmp(Host.end() - (Cur - Start),Host.end(),Start,Cur) == 0)
f8081133
AL
1428 return true;
1429
1430 Start = Cur + 1;
1431 }
1432 return false;
1433}
1434 /*}}}*/
b9179170
MV
1435// strv_length - Return the length of a NULL-terminated string array /*{{{*/
1436// ---------------------------------------------------------------------
1437/* */
1438size_t strv_length(const char **str_array)
1439{
1440 size_t i;
1441 for (i=0; str_array[i] != NULL; i++)
1442 /* nothing */
1443 ;
1444 return i;
1445}
b8eba208 1446 /*}}}*/
69c2ecbd 1447// DeEscapeString - unescape (\0XX and \xXX) from a string /*{{{*/
a513ace2 1448// ---------------------------------------------------------------------
cca2efe6
MV
1449/* */
1450string DeEscapeString(const string &input)
a513ace2 1451{
b9dc4706 1452 char tmp[3];
69c2ecbd
DK
1453 string::const_iterator it;
1454 string output;
f7f0d6c7 1455 for (it = input.begin(); it != input.end(); ++it)
a513ace2
MV
1456 {
1457 // just copy non-escape chars
1458 if (*it != '\\')
1459 {
1460 output += *it;
1461 continue;
1462 }
f8081133 1463
a513ace2
MV
1464 // deal with double escape
1465 if (*it == '\\' &&
1466 (it + 1 < input.end()) && it[1] == '\\')
1467 {
1468 // copy
1469 output += *it;
1470 // advance iterator one step further
f7f0d6c7 1471 ++it;
a513ace2
MV
1472 continue;
1473 }
1474
1475 // ensure we have a char to read
1476 if (it + 1 == input.end())
1477 continue;
f8081133 1478
a513ace2 1479 // read it
f7f0d6c7 1480 ++it;
a513ace2
MV
1481 switch (*it)
1482 {
1483 case '0':
b9dc4706 1484 if (it + 2 <= input.end()) {
a513ace2
MV
1485 tmp[0] = it[1];
1486 tmp[1] = it[2];
b9dc4706 1487 tmp[2] = 0;
a513ace2
MV
1488 output += (char)strtol(tmp, 0, 8);
1489 it += 2;
1490 }
1491 break;
1492 case 'x':
1493 if (it + 2 <= input.end()) {
1494 tmp[0] = it[1];
1495 tmp[1] = it[2];
1496 tmp[2] = 0;
1497 output += (char)strtol(tmp, 0, 16);
1498 it += 2;
1499 }
1500 break;
1501 default:
1502 // FIXME: raise exception here?
a513ace2
MV
1503 break;
1504 }
1505 }
1506 return output;
1507}
1508 /*}}}*/
be4401bf 1509// URI::CopyFrom - Copy from an object /*{{{*/
93bf083d
AL
1510// ---------------------------------------------------------------------
1511/* This parses the URI into all of its components */
171c75f1 1512void URI::CopyFrom(const string &U)
93bf083d 1513{
5933aab2 1514 string::const_iterator I = U.begin();
93bf083d 1515
b2e465d6 1516 // Locate the first colon, this separates the scheme
f7f0d6c7 1517 for (; I < U.end() && *I != ':' ; ++I);
5933aab2 1518 string::const_iterator FirstColon = I;
93bf083d 1519
bfd22fc0
AL
1520 /* Determine if this is a host type URI with a leading double //
1521 and then search for the first single / */
5933aab2
AL
1522 string::const_iterator SingleSlash = I;
1523 if (I + 3 < U.end() && I[1] == '/' && I[2] == '/')
bfd22fc0 1524 SingleSlash += 3;
67ff87bf
AL
1525
1526 /* Find the / indicating the end of the hostname, ignoring /'s in the
1527 square brackets */
1528 bool InBracket = false;
f7f0d6c7 1529 for (; SingleSlash < U.end() && (*SingleSlash != '/' || InBracket == true); ++SingleSlash)
67ff87bf
AL
1530 {
1531 if (*SingleSlash == '[')
1532 InBracket = true;
1533 if (InBracket == true && *SingleSlash == ']')
1534 InBracket = false;
1535 }
1536
5933aab2
AL
1537 if (SingleSlash > U.end())
1538 SingleSlash = U.end();
93bf083d
AL
1539
1540 // We can now write the access and path specifiers
171c75f1 1541 Access.assign(U.begin(),FirstColon);
5933aab2 1542 if (SingleSlash != U.end())
171c75f1 1543 Path.assign(SingleSlash,U.end());
92e889c8
AL
1544 if (Path.empty() == true)
1545 Path = "/";
1546
93bf083d 1547 // Now we attempt to locate a user:pass@host fragment
d48c6a7d 1548 if (FirstColon + 2 <= U.end() && FirstColon[1] == '/' && FirstColon[2] == '/')
f46e7681
AL
1549 FirstColon += 3;
1550 else
1551 FirstColon += 1;
5933aab2 1552 if (FirstColon >= U.end())
93bf083d
AL
1553 return;
1554
1555 if (FirstColon > SingleSlash)
1556 FirstColon = SingleSlash;
1557
3856756b
AL
1558 // Find the colon...
1559 I = FirstColon + 1;
1d38d0e9
AL
1560 if (I > SingleSlash)
1561 I = SingleSlash;
f7f0d6c7 1562 for (; I < SingleSlash && *I != ':'; ++I);
5933aab2 1563 string::const_iterator SecondColon = I;
3856756b
AL
1564
1565 // Search for the @ after the colon
f7f0d6c7 1566 for (; I < SingleSlash && *I != '@'; ++I);
5933aab2 1567 string::const_iterator At = I;
93bf083d 1568
93bf083d
AL
1569 // Now write the host and user/pass
1570 if (At == SingleSlash)
1571 {
1572 if (FirstColon < SingleSlash)
171c75f1 1573 Host.assign(FirstColon,SingleSlash);
93bf083d
AL
1574 }
1575 else
1576 {
171c75f1 1577 Host.assign(At+1,SingleSlash);
436d7eab
DK
1578 // username and password must be encoded (RFC 3986)
1579 User.assign(DeQuoteString(FirstColon,SecondColon));
93bf083d 1580 if (SecondColon < At)
436d7eab 1581 Password.assign(DeQuoteString(SecondColon+1,At));
93bf083d
AL
1582 }
1583
67ff87bf
AL
1584 // Now we parse the RFC 2732 [] hostnames.
1585 unsigned long PortEnd = 0;
1586 InBracket = false;
1587 for (unsigned I = 0; I != Host.length();)
1588 {
1589 if (Host[I] == '[')
1590 {
1591 InBracket = true;
1592 Host.erase(I,1);
1593 continue;
1594 }
1595
1596 if (InBracket == true && Host[I] == ']')
1597 {
1598 InBracket = false;
1599 Host.erase(I,1);
1600 PortEnd = I;
1601 continue;
1602 }
1603 I++;
1604 }
1605
1606 // Tsk, weird.
1607 if (InBracket == true)
1608 {
171c75f1 1609 Host.clear();
67ff87bf
AL
1610 return;
1611 }
1612
1d38d0e9 1613 // Now we parse off a port number from the hostname
93bf083d
AL
1614 Port = 0;
1615 string::size_type Pos = Host.rfind(':');
67ff87bf 1616 if (Pos == string::npos || Pos < PortEnd)
93bf083d
AL
1617 return;
1618
1619 Port = atoi(string(Host,Pos+1).c_str());
171c75f1 1620 Host.assign(Host,0,Pos);
93bf083d
AL
1621}
1622 /*}}}*/
1623// URI::operator string - Convert the URI to a string /*{{{*/
1624// ---------------------------------------------------------------------
1625/* */
1626URI::operator string()
1627{
b8eba208
DK
1628 std::stringstream Res;
1629
54cf15cb 1630 if (Access.empty() == false)
b8eba208
DK
1631 Res << Access << ':';
1632
93bf083d 1633 if (Host.empty() == false)
b8eba208 1634 {
54cf15cb 1635 if (Access.empty() == false)
b8eba208
DK
1636 Res << "//";
1637
93bf083d
AL
1638 if (User.empty() == false)
1639 {
5b63d2a9
MV
1640 // FIXME: Technically userinfo is permitted even less
1641 // characters than these, but this is not conveniently
1642 // expressed with a blacklist.
b8eba208 1643 Res << QuoteString(User, ":/?#[]@");
93bf083d 1644 if (Password.empty() == false)
b8eba208
DK
1645 Res << ":" << QuoteString(Password, ":/?#[]@");
1646 Res << "@";
93bf083d 1647 }
b8eba208 1648
7834cb57 1649 // Add RFC 2732 escaping characters
b8eba208
DK
1650 if (Access.empty() == false && Host.find_first_of("/:") != string::npos)
1651 Res << '[' << Host << ']';
7834cb57 1652 else
b8eba208
DK
1653 Res << Host;
1654
492f957a 1655 if (Port != 0)
b8eba208 1656 Res << ':' << Port;
93bf083d 1657 }
b8eba208 1658
93bf083d 1659 if (Path.empty() == false)
492f957a
AL
1660 {
1661 if (Path[0] != '/')
b8eba208 1662 Res << "/" << Path;
492f957a 1663 else
b8eba208 1664 Res << Path;
492f957a 1665 }
b8eba208
DK
1666
1667 return Res.str();
93bf083d
AL
1668}
1669 /*}}}*/
b2e465d6 1670// URI::SiteOnly - Return the schema and site for the URI /*{{{*/
171c75f1 1671string URI::SiteOnly(const string &URI)
b2e465d6
AL
1672{
1673 ::URI U(URI);
171c75f1
MV
1674 U.User.clear();
1675 U.Password.clear();
1676 U.Path.clear();
b2e465d6
AL
1677 return U;
1678}
1679 /*}}}*/
1da3b7b8
DK
1680// URI::ArchiveOnly - Return the schema, site and cleaned path for the URI /*{{{*/
1681string URI::ArchiveOnly(const string &URI)
1682{
1683 ::URI U(URI);
1684 U.User.clear();
1685 U.Password.clear();
1686 if (U.Path.empty() == false && U.Path[U.Path.length() - 1] == '/')
1687 U.Path.erase(U.Path.length() - 1);
1688 return U;
1689}
1690 /*}}}*/
5e02df82 1691// URI::NoUserPassword - Return the schema, site and path for the URI /*{{{*/
5e02df82
MV
1692string URI::NoUserPassword(const string &URI)
1693{
1694 ::URI U(URI);
1695 U.User.clear();
1696 U.Password.clear();
5e02df82
MV
1697 return U;
1698}
1699 /*}}}*/