apt  2.2.4
About: Apt (Advanced Package Tool) is a management system for software packages (Debian/Ubuntu). Release series 2.2.
  Fossies Dox: apt-2.2.4.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

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