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