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