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