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