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