]> git.saurik.com Git - apt.git/blame - apt-pkg/contrib/strutl.cc
* huge merge to bring the apt debian-sid and ubuntu branches as close
[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 /*{{{*/
492f957a 18#ifdef __GNUG__
cdcc6d34 19#pragma implementation "apt-pkg/strutl.h"
492f957a
AL
20#endif
21
cdcc6d34 22#include <apt-pkg/strutl.h>
7049d16d 23#include <apt-pkg/fileutl.h>
b2e465d6 24#include <apt-pkg/error.h>
0a8a80e5 25
b2e465d6
AL
26#include <apti18n.h>
27
6c139d6e
AL
28#include <ctype.h>
29#include <string.h>
30#include <stdio.h>
152ab79e 31#include <algorithm>
2b154e53 32#include <unistd.h>
b2e465d6 33#include <regex.h>
b0db36b1 34#include <errno.h>
b2e465d6 35#include <stdarg.h>
a52f938b 36#include <iconv.h>
0db4a45b 37
41b6caf4
AL
38#include "config.h"
39
0db4a45b 40using namespace std;
6c139d6e
AL
41 /*}}}*/
42
a52f938b
OS
43// UTF8ToCodeset - Convert some UTF-8 string for some codeset /*{{{*/
44// ---------------------------------------------------------------------
45/* This is handy to use before display some information for enduser */
46bool UTF8ToCodeset(const char *codeset, const string &orig, string *dest)
47{
48 iconv_t cd;
49 const char *inbuf;
50 char *inptr, *outbuf, *outptr;
d9f5f288 51 size_t insize, outsize;
a52f938b
OS
52
53 cd = iconv_open(codeset, "UTF-8");
54 if (cd == (iconv_t)(-1)) {
55 // Something went wrong
56 if (errno == EINVAL)
57 _error->Error("conversion from 'UTF-8' to '%s' not available",
58 codeset);
59 else
60 perror("iconv_open");
61
62 // Clean the destination string
63 *dest = "";
64
65 return false;
66 }
67
68 insize = outsize = orig.size();
69 inbuf = orig.data();
70 inptr = (char *)inbuf;
71 outbuf = new char[insize+1];
72 outptr = outbuf;
73
74 iconv(cd, &inptr, &insize, &outptr, &outsize);
75 *outptr = '\0';
76
77 *dest = outbuf;
78 delete[] outbuf;
79
80 iconv_close(cd);
81
82 return true;
83}
84 /*}}}*/
6c139d6e
AL
85// strstrip - Remove white space from the front and back of a string /*{{{*/
86// ---------------------------------------------------------------------
87/* This is handy to use when parsing a file. It also removes \n's left
88 over from fgets and company */
89char *_strstrip(char *String)
90{
91 for (;*String != 0 && (*String == ' ' || *String == '\t'); String++);
92
93 if (*String == 0)
94 return String;
95
96 char *End = String + strlen(String) - 1;
97 for (;End != String - 1 && (*End == ' ' || *End == '\t' || *End == '\n' ||
98 *End == '\r'); End--);
99 End++;
100 *End = 0;
101 return String;
102};
103 /*}}}*/
104// strtabexpand - Converts tabs into 8 spaces /*{{{*/
105// ---------------------------------------------------------------------
106/* */
107char *_strtabexpand(char *String,size_t Len)
108{
109 for (char *I = String; I != I + Len && *I != 0; I++)
110 {
111 if (*I != '\t')
112 continue;
113 if (I + 8 > String + Len)
114 {
115 *I = 0;
116 return String;
117 }
118
119 /* Assume the start of the string is 0 and find the next 8 char
120 division */
121 int Len;
122 if (String == I)
123 Len = 1;
124 else
125 Len = 8 - ((String - I) % 8);
126 Len -= 2;
127 if (Len <= 0)
128 {
129 *I = ' ';
130 continue;
131 }
132
133 memmove(I + Len,I + 1,strlen(I) + 1);
134 for (char *J = I; J + Len != I; *I = ' ', I++);
135 }
136 return String;
137}
138 /*}}}*/
139// ParseQuoteWord - Parse a single word out of a string /*{{{*/
140// ---------------------------------------------------------------------
141/* This grabs a single word, converts any % escaped characters to their
142 proper values and advances the pointer. Double quotes are understood
7834cb57
AL
143 and striped out as well. This is for URI/URL parsing. It also can
144 understand [] brackets.*/
6c139d6e
AL
145bool ParseQuoteWord(const char *&String,string &Res)
146{
147 // Skip leading whitespace
148 const char *C = String;
149 for (;*C != 0 && *C == ' '; C++);
150 if (*C == 0)
151 return false;
152
153 // Jump to the next word
36f610f1 154 for (;*C != 0 && isspace(*C) == 0; C++)
6c139d6e
AL
155 {
156 if (*C == '"')
157 {
7834cb57
AL
158 for (C++; *C != 0 && *C != '"'; C++);
159 if (*C == 0)
160 return false;
161 }
162 if (*C == '[')
163 {
164 for (C++; *C != 0 && *C != ']'; C++);
6c139d6e
AL
165 if (*C == 0)
166 return false;
167 }
168 }
169
170 // Now de-quote characters
171 char Buffer[1024];
172 char Tmp[3];
173 const char *Start = String;
174 char *I;
175 for (I = Buffer; I < Buffer + sizeof(Buffer) && Start != C; I++)
176 {
177 if (*Start == '%' && Start + 2 < C)
178 {
179 Tmp[0] = Start[1];
180 Tmp[1] = Start[2];
1bc849af 181 Tmp[2] = 0;
6c139d6e
AL
182 *I = (char)strtol(Tmp,0,16);
183 Start += 3;
184 continue;
185 }
186 if (*Start != '"')
187 *I = *Start;
188 else
189 I--;
190 Start++;
191 }
192 *I = 0;
193 Res = Buffer;
194
195 // Skip ending white space
36f610f1 196 for (;*C != 0 && isspace(*C) != 0; C++);
6c139d6e
AL
197 String = C;
198 return true;
199}
200 /*}}}*/
08e8f724
AL
201// ParseCWord - Parses a string like a C "" expression /*{{{*/
202// ---------------------------------------------------------------------
b2e465d6 203/* This expects a series of space separated strings enclosed in ""'s.
08e8f724 204 It concatenates the ""'s into a single string. */
b2e465d6 205bool ParseCWord(const char *&String,string &Res)
08e8f724
AL
206{
207 // Skip leading whitespace
208 const char *C = String;
209 for (;*C != 0 && *C == ' '; C++);
210 if (*C == 0)
211 return false;
212
213 char Buffer[1024];
214 char *Buf = Buffer;
215 if (strlen(String) >= sizeof(Buffer))
216 return false;
217
218 for (; *C != 0; C++)
219 {
220 if (*C == '"')
221 {
222 for (C++; *C != 0 && *C != '"'; C++)
223 *Buf++ = *C;
224
225 if (*C == 0)
226 return false;
227
228 continue;
229 }
230
231 if (C != String && isspace(*C) != 0 && isspace(C[-1]) != 0)
232 continue;
233 if (isspace(*C) == 0)
234 return false;
235 *Buf++ = ' ';
b2e465d6 236 }
08e8f724
AL
237 *Buf = 0;
238 Res = Buffer;
b2e465d6 239 String = C;
08e8f724
AL
240 return true;
241}
242 /*}}}*/
6d5dd02a 243// QuoteString - Convert a string into quoted from /*{{{*/
1bc849af 244// ---------------------------------------------------------------------
6d5dd02a 245/* */
171c75f1 246string QuoteString(const string &Str, const char *Bad)
1bc849af
AL
247{
248 string Res;
171c75f1 249 for (string::const_iterator I = Str.begin(); I != Str.end(); I++)
1bc849af 250 {
6d5dd02a
AL
251 if (strchr(Bad,*I) != 0 || isprint(*I) == 0 ||
252 *I <= 0x20 || *I >= 0x7F)
1bc849af 253 {
6d5dd02a
AL
254 char Buf[10];
255 sprintf(Buf,"%%%02x",(int)*I);
256 Res += Buf;
1bc849af
AL
257 }
258 else
259 Res += *I;
260 }
261 return Res;
262}
263 /*}}}*/
6d5dd02a 264// DeQuoteString - Convert a string from quoted from /*{{{*/
6c139d6e 265// ---------------------------------------------------------------------
6d5dd02a 266/* This undoes QuoteString */
171c75f1 267string DeQuoteString(const string &Str)
6c139d6e
AL
268{
269 string Res;
5933aab2 270 for (string::const_iterator I = Str.begin(); I != Str.end(); I++)
6c139d6e 271 {
5933aab2 272 if (*I == '%' && I + 2 < Str.end())
6c139d6e 273 {
6d5dd02a
AL
274 char Tmp[3];
275 Tmp[0] = I[1];
276 Tmp[1] = I[2];
277 Tmp[2] = 0;
278 Res += (char)strtol(Tmp,0,16);
279 I += 2;
280 continue;
6c139d6e
AL
281 }
282 else
283 Res += *I;
284 }
6d5dd02a 285 return Res;
6c139d6e 286}
6d5dd02a
AL
287
288 /*}}}*/
6c139d6e
AL
289// SizeToStr - Convert a long into a human readable size /*{{{*/
290// ---------------------------------------------------------------------
24231681
AL
291/* A max of 4 digits are shown before conversion to the next highest unit.
292 The max length of the string will be 5 chars unless the size is > 10
6c139d6e
AL
293 YottaBytes (E24) */
294string SizeToStr(double Size)
295{
296 char S[300];
297 double ASize;
298 if (Size >= 0)
299 ASize = Size;
300 else
301 ASize = -1*Size;
302
303 /* bytes, KiloBytes, MegaBytes, GigaBytes, TeraBytes, PetaBytes,
304 ExaBytes, ZettaBytes, YottaBytes */
7f25bdff 305 char Ext[] = {'\0','k','M','G','T','P','E','Z','Y'};
6c139d6e
AL
306 int I = 0;
307 while (I <= 8)
308 {
309 if (ASize < 100 && I != 0)
310 {
311 sprintf(S,"%.1f%c",ASize,Ext[I]);
312 break;
313 }
314
315 if (ASize < 10000)
316 {
317 sprintf(S,"%.0f%c",ASize,Ext[I]);
318 break;
319 }
320 ASize /= 1000.0;
321 I++;
322 }
323
324 return S;
325}
326 /*}}}*/
327// TimeToStr - Convert the time into a string /*{{{*/
328// ---------------------------------------------------------------------
329/* Converts a number of seconds to a hms format */
330string TimeToStr(unsigned long Sec)
331{
332 char S[300];
333
334 while (1)
335 {
336 if (Sec > 60*60*24)
337 {
338 sprintf(S,"%lid %lih%lim%lis",Sec/60/60/24,(Sec/60/60) % 24,(Sec/60) % 60,Sec % 60);
339 break;
340 }
341
342 if (Sec > 60*60)
343 {
344 sprintf(S,"%lih%lim%lis",Sec/60/60,(Sec/60) % 60,Sec % 60);
345 break;
346 }
347
348 if (Sec > 60)
349 {
350 sprintf(S,"%lim%lis",Sec/60,Sec % 60);
351 break;
352 }
353
354 sprintf(S,"%lis",Sec);
355 break;
356 }
357
358 return S;
359}
360 /*}}}*/
361// SubstVar - Substitute a string for another string /*{{{*/
362// ---------------------------------------------------------------------
363/* This replaces all occurances of Subst with Contents in Str. */
171c75f1 364string SubstVar(const string &Str,const string &Subst,const string &Contents)
6c139d6e 365{
8efa2a3b 366 string::size_type Pos = 0;
6c139d6e
AL
367 string::size_type OldPos = 0;
368 string Temp;
369
370 while (OldPos < Str.length() &&
371 (Pos = Str.find(Subst,OldPos)) != string::npos)
372 {
373 Temp += string(Str,OldPos,Pos) + Contents;
374 OldPos = Pos + Subst.length();
375 }
376
377 if (OldPos == 0)
378 return Str;
379
380 return Temp + string(Str,OldPos);
381}
b2e465d6
AL
382
383string SubstVar(string Str,const struct SubstVar *Vars)
384{
385 for (; Vars->Subst != 0; Vars++)
386 Str = SubstVar(Str,Vars->Subst,*Vars->Contents);
387 return Str;
388}
6c139d6e 389 /*}}}*/
ad00ae81
AL
390// URItoFileName - Convert the uri into a unique file name /*{{{*/
391// ---------------------------------------------------------------------
392/* This converts a URI into a safe filename. It quotes all unsafe characters
393 and converts / to _ and removes the scheme identifier. The resulting
394 file name should be unique and never occur again for a different file */
171c75f1 395string URItoFileName(const string &URI)
ad00ae81 396{
54cf15cb
AL
397 // Nuke 'sensitive' items
398 ::URI U(URI);
171c75f1
MV
399 U.User.clear();
400 U.Password.clear();
401 U.Access.clear();
54cf15cb 402
ad00ae81 403 // "\x00-\x20{}|\\\\^\\[\\]<>\"\x7F-\xFF";
171c75f1
MV
404 string NewURI = QuoteString(U,"\\|{}[]<>\"^~_=!@#$%^&*");
405 replace(NewURI.begin(),NewURI.end(),'/','_');
406 return NewURI;
ad00ae81
AL
407}
408 /*}}}*/
6c139d6e
AL
409// Base64Encode - Base64 Encoding routine for short strings /*{{{*/
410// ---------------------------------------------------------------------
411/* This routine performs a base64 transformation on a string. It was ripped
412 from wget and then patched and bug fixed.
413
414 This spec can be found in rfc2045 */
171c75f1 415string Base64Encode(const string &S)
6c139d6e
AL
416{
417 // Conversion table.
418 static char tbl[64] = {'A','B','C','D','E','F','G','H',
419 'I','J','K','L','M','N','O','P',
420 'Q','R','S','T','U','V','W','X',
421 'Y','Z','a','b','c','d','e','f',
422 'g','h','i','j','k','l','m','n',
423 'o','p','q','r','s','t','u','v',
424 'w','x','y','z','0','1','2','3',
425 '4','5','6','7','8','9','+','/'};
426
427 // Pre-allocate some space
428 string Final;
429 Final.reserve((4*S.length() + 2)/3 + 2);
430
431 /* Transform the 3x8 bits to 4x6 bits, as required by
432 base64. */
5933aab2 433 for (string::const_iterator I = S.begin(); I < S.end(); I += 3)
6c139d6e
AL
434 {
435 char Bits[3] = {0,0,0};
436 Bits[0] = I[0];
5933aab2 437 if (I + 1 < S.end())
6c139d6e 438 Bits[1] = I[1];
5933aab2 439 if (I + 2 < S.end())
6c139d6e
AL
440 Bits[2] = I[2];
441
442 Final += tbl[Bits[0] >> 2];
443 Final += tbl[((Bits[0] & 3) << 4) + (Bits[1] >> 4)];
444
5933aab2 445 if (I + 1 >= S.end())
6c139d6e
AL
446 break;
447
448 Final += tbl[((Bits[1] & 0xf) << 2) + (Bits[2] >> 6)];
449
5933aab2 450 if (I + 2 >= S.end())
6c139d6e
AL
451 break;
452
453 Final += tbl[Bits[2] & 0x3f];
454 }
455
456 /* Apply the padding elements, this tells how many bytes the remote
457 end should discard */
458 if (S.length() % 3 == 2)
459 Final += '=';
460 if (S.length() % 3 == 1)
461 Final += "==";
462
463 return Final;
464}
465 /*}}}*/
466// stringcmp - Arbitary string compare /*{{{*/
467// ---------------------------------------------------------------------
468/* This safely compares two non-null terminated strings of arbitary
469 length */
470int stringcmp(const char *A,const char *AEnd,const char *B,const char *BEnd)
47db8997
AL
471{
472 for (; A != AEnd && B != BEnd; A++, B++)
473 if (*A != *B)
474 break;
475
476 if (A == AEnd && B == BEnd)
477 return 0;
478 if (A == AEnd)
479 return 1;
480 if (B == BEnd)
481 return -1;
482 if (*A < *B)
483 return -1;
484 return 1;
485}
ae0b19f5
AL
486
487#if __GNUC__ >= 3
47db8997
AL
488int stringcmp(string::const_iterator A,string::const_iterator AEnd,
489 const char *B,const char *BEnd)
490{
491 for (; A != AEnd && B != BEnd; A++, B++)
492 if (*A != *B)
493 break;
494
495 if (A == AEnd && B == BEnd)
496 return 0;
497 if (A == AEnd)
498 return 1;
499 if (B == BEnd)
500 return -1;
501 if (*A < *B)
502 return -1;
503 return 1;
504}
505int stringcmp(string::const_iterator A,string::const_iterator AEnd,
506 string::const_iterator B,string::const_iterator BEnd)
6c139d6e
AL
507{
508 for (; A != AEnd && B != BEnd; A++, B++)
509 if (*A != *B)
510 break;
511
512 if (A == AEnd && B == BEnd)
513 return 0;
514 if (A == AEnd)
515 return 1;
516 if (B == BEnd)
517 return -1;
518 if (*A < *B)
519 return -1;
520 return 1;
521}
ae0b19f5 522#endif
6c139d6e
AL
523 /*}}}*/
524// stringcasecmp - Arbitary case insensitive string compare /*{{{*/
525// ---------------------------------------------------------------------
526/* */
527int stringcasecmp(const char *A,const char *AEnd,const char *B,const char *BEnd)
47db8997
AL
528{
529 for (; A != AEnd && B != BEnd; A++, B++)
530 if (toupper(*A) != toupper(*B))
531 break;
532
533 if (A == AEnd && B == BEnd)
534 return 0;
535 if (A == AEnd)
536 return 1;
537 if (B == BEnd)
538 return -1;
539 if (toupper(*A) < toupper(*B))
540 return -1;
541 return 1;
542}
ae0b19f5 543#if __GNUC__ >= 3
47db8997
AL
544int stringcasecmp(string::const_iterator A,string::const_iterator AEnd,
545 const char *B,const char *BEnd)
546{
547 for (; A != AEnd && B != BEnd; A++, B++)
548 if (toupper(*A) != toupper(*B))
549 break;
550
551 if (A == AEnd && B == BEnd)
552 return 0;
553 if (A == AEnd)
554 return 1;
555 if (B == BEnd)
556 return -1;
557 if (toupper(*A) < toupper(*B))
558 return -1;
559 return 1;
560}
561int stringcasecmp(string::const_iterator A,string::const_iterator AEnd,
562 string::const_iterator B,string::const_iterator BEnd)
6c139d6e
AL
563{
564 for (; A != AEnd && B != BEnd; A++, B++)
565 if (toupper(*A) != toupper(*B))
566 break;
3b5421b4 567
6c139d6e
AL
568 if (A == AEnd && B == BEnd)
569 return 0;
570 if (A == AEnd)
571 return 1;
572 if (B == BEnd)
573 return -1;
574 if (toupper(*A) < toupper(*B))
575 return -1;
576 return 1;
577}
ae0b19f5 578#endif
6c139d6e 579 /*}}}*/
3b5421b4
AL
580// LookupTag - Lookup the value of a tag in a taged string /*{{{*/
581// ---------------------------------------------------------------------
582/* The format is like those used in package files and the method
583 communication system */
171c75f1 584string LookupTag(const string &Message,const char *Tag,const char *Default)
3b5421b4
AL
585{
586 // Look for a matching tag.
587 int Length = strlen(Tag);
171c75f1 588 for (string::const_iterator I = Message.begin(); I + Length < Message.end(); I++)
3b5421b4
AL
589 {
590 // Found the tag
591 if (I[Length] == ':' && stringcasecmp(I,I+Length,Tag) == 0)
592 {
593 // Find the end of line and strip the leading/trailing spaces
171c75f1 594 string::const_iterator J;
3b5421b4 595 I += Length + 1;
47db8997
AL
596 for (; isspace(*I) != 0 && I < Message.end(); I++);
597 for (J = I; *J != '\n' && J < Message.end(); J++);
3b5421b4
AL
598 for (; J > I && isspace(J[-1]) != 0; J--);
599
0db4a45b 600 return string(I,J);
3b5421b4
AL
601 }
602
47db8997 603 for (; *I != '\n' && I < Message.end(); I++);
3b5421b4
AL
604 }
605
606 // Failed to find a match
607 if (Default == 0)
608 return string();
609 return Default;
610}
611 /*}}}*/
612// StringToBool - Converts a string into a boolean /*{{{*/
613// ---------------------------------------------------------------------
614/* This inspects the string to see if it is true or if it is false and
615 then returns the result. Several varients on true/false are checked. */
171c75f1 616int StringToBool(const string &Text,int Default)
3b5421b4
AL
617{
618 char *End;
619 int Res = strtol(Text.c_str(),&End,0);
620 if (End != Text.c_str() && Res >= 0 && Res <= 1)
621 return Res;
622
623 // Check for positives
624 if (strcasecmp(Text.c_str(),"no") == 0 ||
625 strcasecmp(Text.c_str(),"false") == 0 ||
626 strcasecmp(Text.c_str(),"without") == 0 ||
7f25bdff 627 strcasecmp(Text.c_str(),"off") == 0 ||
3b5421b4
AL
628 strcasecmp(Text.c_str(),"disable") == 0)
629 return 0;
630
631 // Check for negatives
632 if (strcasecmp(Text.c_str(),"yes") == 0 ||
633 strcasecmp(Text.c_str(),"true") == 0 ||
634 strcasecmp(Text.c_str(),"with") == 0 ||
7f25bdff 635 strcasecmp(Text.c_str(),"on") == 0 ||
3b5421b4
AL
636 strcasecmp(Text.c_str(),"enable") == 0)
637 return 1;
638
639 return Default;
640}
641 /*}}}*/
0a8a80e5
AL
642// TimeRFC1123 - Convert a time_t into RFC1123 format /*{{{*/
643// ---------------------------------------------------------------------
644/* This converts a time_t into a string time representation that is
645 year 2000 complient and timezone neutral */
646string TimeRFC1123(time_t Date)
647{
648 struct tm Conv = *gmtime(&Date);
649 char Buf[300];
650
651 const char *Day[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
652 const char *Month[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul",
653 "Aug","Sep","Oct","Nov","Dec"};
654
655 sprintf(Buf,"%s, %02i %s %i %02i:%02i:%02i GMT",Day[Conv.tm_wday],
656 Conv.tm_mday,Month[Conv.tm_mon],Conv.tm_year+1900,Conv.tm_hour,
657 Conv.tm_min,Conv.tm_sec);
658 return Buf;
659}
660 /*}}}*/
661// ReadMessages - Read messages from the FD /*{{{*/
662// ---------------------------------------------------------------------
663/* This pulls full messages from the input FD into the message buffer.
664 It assumes that messages will not pause during transit so no
665 fancy buffering is used. */
666bool ReadMessages(int Fd, vector<string> &List)
667{
aee70518 668 char Buffer[64000];
0a8a80e5
AL
669 char *End = Buffer;
670
671 while (1)
672 {
673 int Res = read(Fd,End,sizeof(Buffer) - (End-Buffer));
b0db36b1
AL
674 if (Res < 0 && errno == EINTR)
675 continue;
0a8a80e5
AL
676
677 // Process is dead, this is kind of bad..
678 if (Res == 0)
679 return false;
680
681 // No data
b2e465d6 682 if (Res < 0 && errno == EAGAIN)
0a8a80e5 683 return true;
b2e465d6
AL
684 if (Res < 0)
685 return false;
686
0a8a80e5
AL
687 End += Res;
688
689 // Look for the end of the message
c88edf1d 690 for (char *I = Buffer; I + 1 < End; I++)
0a8a80e5
AL
691 {
692 if (I[0] != '\n' || I[1] != '\n')
693 continue;
694
695 // Pull the message out
d48c6a7d 696 string Message(Buffer,I-Buffer);
0a8a80e5
AL
697
698 // Fix up the buffer
699 for (; I < End && *I == '\n'; I++);
700 End -= I-Buffer;
701 memmove(Buffer,I,End-Buffer);
702 I = Buffer;
703
704 List.push_back(Message);
705 }
706 if (End == Buffer)
707 return true;
708
709 if (WaitFd(Fd) == false)
710 return false;
711 }
712}
713 /*}}}*/
24231681
AL
714// MonthConv - Converts a month string into a number /*{{{*/
715// ---------------------------------------------------------------------
716/* This was lifted from the boa webserver which lifted it from 'wn-v1.07'
717 Made it a bit more robust with a few touppers though. */
718static int MonthConv(char *Month)
719{
720 switch (toupper(*Month))
721 {
722 case 'A':
723 return toupper(Month[1]) == 'P'?3:7;
724 case 'D':
725 return 11;
726 case 'F':
727 return 1;
728 case 'J':
729 if (toupper(Month[1]) == 'A')
730 return 0;
731 return toupper(Month[2]) == 'N'?5:6;
732 case 'M':
733 return toupper(Month[2]) == 'R'?2:4;
734 case 'N':
735 return 10;
736 case 'O':
737 return 9;
738 case 'S':
739 return 8;
740
741 // Pretend it is January..
742 default:
743 return 0;
744 }
745}
746 /*}}}*/
6d5dd02a
AL
747// timegm - Internal timegm function if gnu is not available /*{{{*/
748// ---------------------------------------------------------------------
749/* Ripped this evil little function from wget - I prefer the use of
750 GNU timegm if possible as this technique will have interesting problems
751 with leap seconds, timezones and other.
752
753 Converts struct tm to time_t, assuming the data in tm is UTC rather
754 than local timezone (mktime assumes the latter).
755
756 Contributed by Roger Beeman <beeman@cisco.com>, with the help of
757 Mark Baushke <mdb@cisco.com> and the rest of the Gurus at CISCO. */
41b6caf4
AL
758
759/* Turned it into an autoconf check, because GNU is not the only thing which
760 can provide timegm. -- 2002-09-22, Joel Baker */
761
762#ifndef HAVE_TIMEGM // Now with autoconf!
6d5dd02a
AL
763static time_t timegm(struct tm *t)
764{
765 time_t tl, tb;
766
767 tl = mktime (t);
768 if (tl == -1)
769 return -1;
770 tb = mktime (gmtime (&tl));
771 return (tl <= tb ? (tl + (tl - tb)) : (tl - (tb - tl)));
772}
773#endif
774 /*}}}*/
24231681
AL
775// StrToTime - Converts a string into a time_t /*{{{*/
776// ---------------------------------------------------------------------
777/* This handles all 3 populare time formats including RFC 1123, RFC 1036
778 and the C library asctime format. It requires the GNU library function
779 'timegm' to convert a struct tm in UTC to a time_t. For some bizzar
f58a97d3
AL
780 reason the C library does not provide any such function :< This also
781 handles the weird, but unambiguous FTP time format*/
171c75f1 782bool StrToTime(const string &Val,time_t &Result)
24231681
AL
783{
784 struct tm Tm;
785 char Month[10];
786 const char *I = Val.c_str();
787
788 // Skip the day of the week
789 for (;*I != 0 && *I != ' '; I++);
790
791 // Handle RFC 1123 time
f58a97d3 792 Month[0] = 0;
24231681
AL
793 if (sscanf(I," %d %3s %d %d:%d:%d GMT",&Tm.tm_mday,Month,&Tm.tm_year,
794 &Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec) != 6)
795 {
796 // Handle RFC 1036 time
797 if (sscanf(I," %d-%3s-%d %d:%d:%d GMT",&Tm.tm_mday,Month,
798 &Tm.tm_year,&Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec) == 6)
799 Tm.tm_year += 1900;
800 else
801 {
802 // asctime format
803 if (sscanf(I," %3s %d %d:%d:%d %d",Month,&Tm.tm_mday,
804 &Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec,&Tm.tm_year) != 6)
f58a97d3
AL
805 {
806 // 'ftp' time
7ef72446 807 if (sscanf(Val.c_str(),"%4d%2d%2d%2d%2d%2d",&Tm.tm_year,&Tm.tm_mon,
f58a97d3
AL
808 &Tm.tm_mday,&Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec) != 6)
809 return false;
810 Tm.tm_mon--;
811 }
24231681
AL
812 }
813 }
814
815 Tm.tm_isdst = 0;
f58a97d3
AL
816 if (Month[0] != 0)
817 Tm.tm_mon = MonthConv(Month);
24231681
AL
818 Tm.tm_year -= 1900;
819
820 // Convert to local time and then to GMT
821 Result = timegm(&Tm);
822 return true;
823}
824 /*}}}*/
ddc1d8d0
AL
825// StrToNum - Convert a fixed length string to a number /*{{{*/
826// ---------------------------------------------------------------------
827/* This is used in decoding the crazy fixed length string headers in
828 tar and ar files. */
829bool StrToNum(const char *Str,unsigned long &Res,unsigned Len,unsigned Base)
830{
831 char S[30];
832 if (Len >= sizeof(S))
833 return false;
834 memcpy(S,Str,Len);
835 S[Len] = 0;
836
837 // All spaces is a zero
838 Res = 0;
839 unsigned I;
840 for (I = 0; S[I] == ' '; I++);
841 if (S[I] == 0)
842 return true;
843
844 char *End;
845 Res = strtoul(S,&End,Base);
846 if (End == S)
847 return false;
848
849 return true;
850}
851 /*}}}*/
6e52073f
AL
852// HexDigit - Convert a hex character into an integer /*{{{*/
853// ---------------------------------------------------------------------
854/* Helper for Hex2Num */
855static int HexDigit(int c)
856{
857 if (c >= '0' && c <= '9')
858 return c - '0';
859 if (c >= 'a' && c <= 'f')
860 return c - 'a' + 10;
861 if (c >= 'A' && c <= 'F')
862 return c - 'A' + 10;
863 return 0;
864}
865 /*}}}*/
866// Hex2Num - Convert a long hex number into a buffer /*{{{*/
867// ---------------------------------------------------------------------
868/* The length of the buffer must be exactly 1/2 the length of the string. */
171c75f1 869bool Hex2Num(const string &Str,unsigned char *Num,unsigned int Length)
6e52073f 870{
0db4a45b 871 if (Str.length() != Length*2)
6e52073f
AL
872 return false;
873
874 // Convert each digit. We store it in the same order as the string
875 int J = 0;
0db4a45b 876 for (string::const_iterator I = Str.begin(); I != Str.end();J++, I += 2)
6e52073f
AL
877 {
878 if (isxdigit(*I) == 0 || isxdigit(I[1]) == 0)
879 return false;
880
881 Num[J] = HexDigit(I[0]) << 4;
882 Num[J] += HexDigit(I[1]);
883 }
884
885 return true;
886}
887 /*}}}*/
b2e465d6
AL
888// TokSplitString - Split a string up by a given token /*{{{*/
889// ---------------------------------------------------------------------
890/* This is intended to be a faster splitter, it does not use dynamic
891 memories. Input is changed to insert nulls at each token location. */
892bool TokSplitString(char Tok,char *Input,char **List,
893 unsigned long ListMax)
894{
895 // Strip any leading spaces
896 char *Start = Input;
897 char *Stop = Start + strlen(Start);
898 for (; *Start != 0 && isspace(*Start) != 0; Start++);
899
900 unsigned long Count = 0;
901 char *Pos = Start;
902 while (Pos != Stop)
903 {
904 // Skip to the next Token
905 for (; Pos != Stop && *Pos != Tok; Pos++);
906
907 // Back remove spaces
908 char *End = Pos;
909 for (; End > Start && (End[-1] == Tok || isspace(End[-1]) != 0); End--);
910 *End = 0;
911
912 List[Count++] = Start;
913 if (Count >= ListMax)
914 {
915 List[Count-1] = 0;
916 return false;
917 }
918
919 // Advance pos
920 for (; Pos != Stop && (*Pos == Tok || isspace(*Pos) != 0 || *Pos == 0); Pos++);
921 Start = Pos;
922 }
923
924 List[Count] = 0;
925 return true;
926}
927 /*}}}*/
928// RegexChoice - Simple regex list/list matcher /*{{{*/
929// ---------------------------------------------------------------------
930/* */
931unsigned long RegexChoice(RxChoiceList *Rxs,const char **ListBegin,
932 const char **ListEnd)
933{
934 for (RxChoiceList *R = Rxs; R->Str != 0; R++)
935 R->Hit = false;
936
937 unsigned long Hits = 0;
938 for (; ListBegin != ListEnd; ListBegin++)
939 {
940 // Check if the name is a regex
941 const char *I;
942 bool Regex = true;
943 for (I = *ListBegin; *I != 0; I++)
944 if (*I == '.' || *I == '?' || *I == '*' || *I == '|')
945 break;
946 if (*I == 0)
947 Regex = false;
948
949 // Compile the regex pattern
950 regex_t Pattern;
951 if (Regex == true)
952 if (regcomp(&Pattern,*ListBegin,REG_EXTENDED | REG_ICASE |
953 REG_NOSUB) != 0)
954 Regex = false;
955
956 // Search the list
957 bool Done = false;
958 for (RxChoiceList *R = Rxs; R->Str != 0; R++)
959 {
960 if (R->Str[0] == 0)
961 continue;
962
963 if (strcasecmp(R->Str,*ListBegin) != 0)
964 {
965 if (Regex == false)
966 continue;
967 if (regexec(&Pattern,R->Str,0,0,0) != 0)
968 continue;
969 }
970 Done = true;
971
972 if (R->Hit == false)
973 Hits++;
974
975 R->Hit = true;
976 }
977
978 if (Regex == true)
979 regfree(&Pattern);
980
981 if (Done == false)
982 _error->Warning(_("Selection %s not found"),*ListBegin);
983 }
984
985 return Hits;
986}
987 /*}}}*/
988// ioprintf - C format string outputter to C++ iostreams /*{{{*/
989// ---------------------------------------------------------------------
1168596f
AL
990/* This is used to make the internationalization strings easier to translate
991 and to allow reordering of parameters */
b2e465d6
AL
992void ioprintf(ostream &out,const char *format,...)
993{
994 va_list args;
995 va_start(args,format);
996
997 // sprintf the description
998 char S[400];
999 vsnprintf(S,sizeof(S),format,args);
e7b470ee 1000 out << S;
1168596f
AL
1001}
1002 /*}}}*/
1003// safe_snprintf - Safer snprintf /*{{{*/
1004// ---------------------------------------------------------------------
1005/* This is a snprintf that will never (ever) go past 'End' and returns a
1006 pointer to the end of the new string. The returned string is always null
1007 terminated unless Buffer == end. This is a better alterantive to using
1008 consecutive snprintfs. */
1009char *safe_snprintf(char *Buffer,char *End,const char *Format,...)
1010{
1011 va_list args;
1012 unsigned long Did;
1013
1014 va_start(args,Format);
1015
1016 if (End <= Buffer)
1017 return End;
1018
1019 Did = vsnprintf(Buffer,End - Buffer,Format,args);
1020 if (Did < 0 || Buffer + Did > End)
1021 return End;
1022 return Buffer + Did;
b2e465d6
AL
1023}
1024 /*}}}*/
93bf083d 1025
f8081133
AL
1026// CheckDomainList - See if Host is in a , seperate list /*{{{*/
1027// ---------------------------------------------------------------------
1028/* The domain list is a comma seperate list of domains that are suffix
1029 matched against the argument */
171c75f1 1030bool CheckDomainList(const string &Host,const string &List)
f8081133 1031{
47db8997
AL
1032 string::const_iterator Start = List.begin();
1033 for (string::const_iterator Cur = List.begin(); Cur <= List.end(); Cur++)
f8081133 1034 {
47db8997 1035 if (Cur < List.end() && *Cur != ',')
f8081133
AL
1036 continue;
1037
1038 // Match the end of the string..
e2c7e6b5 1039 if ((Host.size() >= (unsigned)(Cur - Start)) &&
f8081133 1040 Cur - Start != 0 &&
47db8997 1041 stringcasecmp(Host.end() - (Cur - Start),Host.end(),Start,Cur) == 0)
f8081133
AL
1042 return true;
1043
1044 Start = Cur + 1;
1045 }
1046 return false;
1047}
1048 /*}}}*/
1049
be4401bf 1050// URI::CopyFrom - Copy from an object /*{{{*/
93bf083d
AL
1051// ---------------------------------------------------------------------
1052/* This parses the URI into all of its components */
171c75f1 1053void URI::CopyFrom(const string &U)
93bf083d 1054{
5933aab2 1055 string::const_iterator I = U.begin();
93bf083d 1056
b2e465d6 1057 // Locate the first colon, this separates the scheme
5933aab2
AL
1058 for (; I < U.end() && *I != ':' ; I++);
1059 string::const_iterator FirstColon = I;
93bf083d 1060
bfd22fc0
AL
1061 /* Determine if this is a host type URI with a leading double //
1062 and then search for the first single / */
5933aab2
AL
1063 string::const_iterator SingleSlash = I;
1064 if (I + 3 < U.end() && I[1] == '/' && I[2] == '/')
bfd22fc0 1065 SingleSlash += 3;
67ff87bf
AL
1066
1067 /* Find the / indicating the end of the hostname, ignoring /'s in the
1068 square brackets */
1069 bool InBracket = false;
5933aab2 1070 for (; SingleSlash < U.end() && (*SingleSlash != '/' || InBracket == true); SingleSlash++)
67ff87bf
AL
1071 {
1072 if (*SingleSlash == '[')
1073 InBracket = true;
1074 if (InBracket == true && *SingleSlash == ']')
1075 InBracket = false;
1076 }
1077
5933aab2
AL
1078 if (SingleSlash > U.end())
1079 SingleSlash = U.end();
93bf083d
AL
1080
1081 // We can now write the access and path specifiers
171c75f1 1082 Access.assign(U.begin(),FirstColon);
5933aab2 1083 if (SingleSlash != U.end())
171c75f1 1084 Path.assign(SingleSlash,U.end());
92e889c8
AL
1085 if (Path.empty() == true)
1086 Path = "/";
1087
93bf083d 1088 // Now we attempt to locate a user:pass@host fragment
d48c6a7d 1089 if (FirstColon + 2 <= U.end() && FirstColon[1] == '/' && FirstColon[2] == '/')
f46e7681
AL
1090 FirstColon += 3;
1091 else
1092 FirstColon += 1;
5933aab2 1093 if (FirstColon >= U.end())
93bf083d
AL
1094 return;
1095
1096 if (FirstColon > SingleSlash)
1097 FirstColon = SingleSlash;
1098
3856756b
AL
1099 // Find the colon...
1100 I = FirstColon + 1;
1d38d0e9
AL
1101 if (I > SingleSlash)
1102 I = SingleSlash;
3856756b 1103 for (; I < SingleSlash && *I != ':'; I++);
5933aab2 1104 string::const_iterator SecondColon = I;
3856756b
AL
1105
1106 // Search for the @ after the colon
93bf083d 1107 for (; I < SingleSlash && *I != '@'; I++);
5933aab2 1108 string::const_iterator At = I;
93bf083d 1109
93bf083d
AL
1110 // Now write the host and user/pass
1111 if (At == SingleSlash)
1112 {
1113 if (FirstColon < SingleSlash)
171c75f1 1114 Host.assign(FirstColon,SingleSlash);
93bf083d
AL
1115 }
1116 else
1117 {
171c75f1
MV
1118 Host.assign(At+1,SingleSlash);
1119 User.assign(FirstColon,SecondColon);
93bf083d 1120 if (SecondColon < At)
171c75f1 1121 Password.assign(SecondColon+1,At);
93bf083d
AL
1122 }
1123
67ff87bf
AL
1124 // Now we parse the RFC 2732 [] hostnames.
1125 unsigned long PortEnd = 0;
1126 InBracket = false;
1127 for (unsigned I = 0; I != Host.length();)
1128 {
1129 if (Host[I] == '[')
1130 {
1131 InBracket = true;
1132 Host.erase(I,1);
1133 continue;
1134 }
1135
1136 if (InBracket == true && Host[I] == ']')
1137 {
1138 InBracket = false;
1139 Host.erase(I,1);
1140 PortEnd = I;
1141 continue;
1142 }
1143 I++;
1144 }
1145
1146 // Tsk, weird.
1147 if (InBracket == true)
1148 {
171c75f1 1149 Host.clear();
67ff87bf
AL
1150 return;
1151 }
1152
1d38d0e9 1153 // Now we parse off a port number from the hostname
93bf083d
AL
1154 Port = 0;
1155 string::size_type Pos = Host.rfind(':');
67ff87bf 1156 if (Pos == string::npos || Pos < PortEnd)
93bf083d
AL
1157 return;
1158
1159 Port = atoi(string(Host,Pos+1).c_str());
171c75f1 1160 Host.assign(Host,0,Pos);
93bf083d
AL
1161}
1162 /*}}}*/
1163// URI::operator string - Convert the URI to a string /*{{{*/
1164// ---------------------------------------------------------------------
1165/* */
1166URI::operator string()
1167{
54cf15cb
AL
1168 string Res;
1169
1170 if (Access.empty() == false)
1171 Res = Access + ':';
1172
93bf083d 1173 if (Host.empty() == false)
7834cb57 1174 {
54cf15cb
AL
1175 if (Access.empty() == false)
1176 Res += "//";
7834cb57 1177
93bf083d
AL
1178 if (User.empty() == false)
1179 {
54cf15cb 1180 Res += User;
93bf083d
AL
1181 if (Password.empty() == false)
1182 Res += ":" + Password;
1183 Res += "@";
1184 }
54cf15cb 1185
7834cb57
AL
1186 // Add RFC 2732 escaping characters
1187 if (Access.empty() == false &&
1188 (Host.find('/') != string::npos || Host.find(':') != string::npos))
1189 Res += '[' + Host + ']';
1190 else
1191 Res += Host;
1192
492f957a
AL
1193 if (Port != 0)
1194 {
1195 char S[30];
1196 sprintf(S,":%u",Port);
1197 Res += S;
1198 }
93bf083d
AL
1199 }
1200
1201 if (Path.empty() == false)
492f957a
AL
1202 {
1203 if (Path[0] != '/')
1204 Res += "/" + Path;
1205 else
1206 Res += Path;
1207 }
93bf083d
AL
1208
1209 return Res;
1210}
1211 /*}}}*/
b2e465d6
AL
1212// URI::SiteOnly - Return the schema and site for the URI /*{{{*/
1213// ---------------------------------------------------------------------
1214/* */
171c75f1 1215string URI::SiteOnly(const string &URI)
b2e465d6
AL
1216{
1217 ::URI U(URI);
171c75f1
MV
1218 U.User.clear();
1219 U.Password.clear();
1220 U.Path.clear();
b2e465d6
AL
1221 U.Port = 0;
1222 return U;
1223}
1224 /*}}}*/