]> git.saurik.com Git - apt.git/blob - apt-pkg/contrib/strutl.cc
G++3 fixes from Randolph
[apt.git] / apt-pkg / contrib / strutl.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: strutl.cc,v 1.42 2001/05/29 04:40:34 jgg 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
36 using namespace std;
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 */
43 char *_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 /* */
61 char *_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
97 and striped out as well. This is for URI/URL parsing. It also can
98 understand [] brackets.*/
99 bool 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
108 for (;*C != 0 && isspace(*C) == 0; C++)
109 {
110 if (*C == '"')
111 {
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++);
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];
135 Tmp[2] = 0;
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
150 for (;*C != 0 && isspace(*C) != 0; C++);
151 String = C;
152 return true;
153 }
154 /*}}}*/
155 // ParseCWord - Parses a string like a C "" expression /*{{{*/
156 // ---------------------------------------------------------------------
157 /* This expects a series of space separated strings enclosed in ""'s.
158 It concatenates the ""'s into a single string. */
159 bool ParseCWord(const char *&String,string &Res)
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++ = ' ';
190 }
191 *Buf = 0;
192 Res = Buffer;
193 String = C;
194 return true;
195 }
196 /*}}}*/
197 // QuoteString - Convert a string into quoted from /*{{{*/
198 // ---------------------------------------------------------------------
199 /* */
200 string QuoteString(string Str,const char *Bad)
201 {
202 string Res;
203 for (string::iterator I = Str.begin(); I != Str.end(); I++)
204 {
205 if (strchr(Bad,*I) != 0 || isprint(*I) == 0 ||
206 *I <= 0x20 || *I >= 0x7F)
207 {
208 char Buf[10];
209 sprintf(Buf,"%%%02x",(int)*I);
210 Res += Buf;
211 }
212 else
213 Res += *I;
214 }
215 return Res;
216 }
217 /*}}}*/
218 // DeQuoteString - Convert a string from quoted from /*{{{*/
219 // ---------------------------------------------------------------------
220 /* This undoes QuoteString */
221 string DeQuoteString(string Str)
222 {
223 string Res;
224 for (string::const_iterator I = Str.begin(); I != Str.end(); I++)
225 {
226 if (*I == '%' && I + 2 < Str.end())
227 {
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;
235 }
236 else
237 Res += *I;
238 }
239 return Res;
240 }
241
242 /*}}}*/
243 // SizeToStr - Convert a long into a human readable size /*{{{*/
244 // ---------------------------------------------------------------------
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
247 YottaBytes (E24) */
248 string 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 */
259 char Ext[] = {'\0','k','M','G','T','P','E','Z','Y'};
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 */
284 string 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. */
318 string SubstVar(string Str,string Subst,string Contents)
319 {
320 string::size_type Pos = 0;
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 }
336
337 string 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 }
343 /*}}}*/
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 */
349 string URItoFileName(string URI)
350 {
351 // Nuke 'sensitive' items
352 ::URI U(URI);
353 U.User = string();
354 U.Password = string();
355 U.Access = "";
356
357 // "\x00-\x20{}|\\\\^\\[\\]<>\"\x7F-\xFF";
358 URI = QuoteString(U,"\\|{}[]<>\"^~_=!@#$%^&*");
359 string::iterator J = URI.begin();
360 for (; J != URI.end(); J++)
361 if (*J == '/')
362 *J = '_';
363 return URI;
364 }
365 /*}}}*/
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 */
372 string 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. */
390 for (string::const_iterator I = S.begin(); I < S.end(); I += 3)
391 {
392 char Bits[3] = {0,0,0};
393 Bits[0] = I[0];
394 if (I + 1 < S.end())
395 Bits[1] = I[1];
396 if (I + 2 < S.end())
397 Bits[2] = I[2];
398
399 Final += tbl[Bits[0] >> 2];
400 Final += tbl[((Bits[0] & 3) << 4) + (Bits[1] >> 4)];
401
402 if (I + 1 >= S.end())
403 break;
404
405 Final += tbl[((Bits[1] & 0xf) << 2) + (Bits[2] >> 6)];
406
407 if (I + 2 >= S.end())
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 */
427 int 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 int stringcmp(string::const_iterator A,string::const_iterator AEnd,
444 const char *B,const char *BEnd)
445 {
446 for (; A != AEnd && B != BEnd; A++, B++)
447 if (*A != *B)
448 break;
449
450 if (A == AEnd && B == BEnd)
451 return 0;
452 if (A == AEnd)
453 return 1;
454 if (B == BEnd)
455 return -1;
456 if (*A < *B)
457 return -1;
458 return 1;
459 }
460 int stringcmp(string::const_iterator A,string::const_iterator AEnd,
461 string::const_iterator B,string::const_iterator BEnd)
462 {
463 for (; A != AEnd && B != BEnd; A++, B++)
464 if (*A != *B)
465 break;
466
467 if (A == AEnd && B == BEnd)
468 return 0;
469 if (A == AEnd)
470 return 1;
471 if (B == BEnd)
472 return -1;
473 if (*A < *B)
474 return -1;
475 return 1;
476 }
477 /*}}}*/
478 // stringcasecmp - Arbitary case insensitive string compare /*{{{*/
479 // ---------------------------------------------------------------------
480 /* */
481 int stringcasecmp(const char *A,const char *AEnd,const char *B,const char *BEnd)
482 {
483 for (; A != AEnd && B != BEnd; A++, B++)
484 if (toupper(*A) != toupper(*B))
485 break;
486
487 if (A == AEnd && B == BEnd)
488 return 0;
489 if (A == AEnd)
490 return 1;
491 if (B == BEnd)
492 return -1;
493 if (toupper(*A) < toupper(*B))
494 return -1;
495 return 1;
496 }
497 int stringcasecmp(string::const_iterator A,string::const_iterator AEnd,
498 const char *B,const char *BEnd)
499 {
500 for (; A != AEnd && B != BEnd; A++, B++)
501 if (toupper(*A) != toupper(*B))
502 break;
503
504 if (A == AEnd && B == BEnd)
505 return 0;
506 if (A == AEnd)
507 return 1;
508 if (B == BEnd)
509 return -1;
510 if (toupper(*A) < toupper(*B))
511 return -1;
512 return 1;
513 }
514 int stringcasecmp(string::const_iterator A,string::const_iterator AEnd,
515 string::const_iterator B,string::const_iterator BEnd)
516 {
517 for (; A != AEnd && B != BEnd; A++, B++)
518 if (toupper(*A) != toupper(*B))
519 break;
520
521 if (A == AEnd && B == BEnd)
522 return 0;
523 if (A == AEnd)
524 return 1;
525 if (B == BEnd)
526 return -1;
527 if (toupper(*A) < toupper(*B))
528 return -1;
529 return 1;
530 }
531 /*}}}*/
532 // LookupTag - Lookup the value of a tag in a taged string /*{{{*/
533 // ---------------------------------------------------------------------
534 /* The format is like those used in package files and the method
535 communication system */
536 string LookupTag(string Message,const char *Tag,const char *Default)
537 {
538 // Look for a matching tag.
539 int Length = strlen(Tag);
540 for (string::iterator I = Message.begin(); I + Length < Message.end(); I++)
541 {
542 // Found the tag
543 if (I[Length] == ':' && stringcasecmp(I,I+Length,Tag) == 0)
544 {
545 // Find the end of line and strip the leading/trailing spaces
546 string::iterator J;
547 I += Length + 1;
548 for (; isspace(*I) != 0 && I < Message.end(); I++);
549 for (J = I; *J != '\n' && J < Message.end(); J++);
550 for (; J > I && isspace(J[-1]) != 0; J--);
551
552 return string(I,J);
553 }
554
555 for (; *I != '\n' && I < Message.end(); I++);
556 }
557
558 // Failed to find a match
559 if (Default == 0)
560 return string();
561 return Default;
562 }
563 /*}}}*/
564 // StringToBool - Converts a string into a boolean /*{{{*/
565 // ---------------------------------------------------------------------
566 /* This inspects the string to see if it is true or if it is false and
567 then returns the result. Several varients on true/false are checked. */
568 int StringToBool(string Text,int Default)
569 {
570 char *End;
571 int Res = strtol(Text.c_str(),&End,0);
572 if (End != Text.c_str() && Res >= 0 && Res <= 1)
573 return Res;
574
575 // Check for positives
576 if (strcasecmp(Text.c_str(),"no") == 0 ||
577 strcasecmp(Text.c_str(),"false") == 0 ||
578 strcasecmp(Text.c_str(),"without") == 0 ||
579 strcasecmp(Text.c_str(),"off") == 0 ||
580 strcasecmp(Text.c_str(),"disable") == 0)
581 return 0;
582
583 // Check for negatives
584 if (strcasecmp(Text.c_str(),"yes") == 0 ||
585 strcasecmp(Text.c_str(),"true") == 0 ||
586 strcasecmp(Text.c_str(),"with") == 0 ||
587 strcasecmp(Text.c_str(),"on") == 0 ||
588 strcasecmp(Text.c_str(),"enable") == 0)
589 return 1;
590
591 return Default;
592 }
593 /*}}}*/
594 // TimeRFC1123 - Convert a time_t into RFC1123 format /*{{{*/
595 // ---------------------------------------------------------------------
596 /* This converts a time_t into a string time representation that is
597 year 2000 complient and timezone neutral */
598 string TimeRFC1123(time_t Date)
599 {
600 struct tm Conv = *gmtime(&Date);
601 char Buf[300];
602
603 const char *Day[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
604 const char *Month[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul",
605 "Aug","Sep","Oct","Nov","Dec"};
606
607 sprintf(Buf,"%s, %02i %s %i %02i:%02i:%02i GMT",Day[Conv.tm_wday],
608 Conv.tm_mday,Month[Conv.tm_mon],Conv.tm_year+1900,Conv.tm_hour,
609 Conv.tm_min,Conv.tm_sec);
610 return Buf;
611 }
612 /*}}}*/
613 // ReadMessages - Read messages from the FD /*{{{*/
614 // ---------------------------------------------------------------------
615 /* This pulls full messages from the input FD into the message buffer.
616 It assumes that messages will not pause during transit so no
617 fancy buffering is used. */
618 bool ReadMessages(int Fd, vector<string> &List)
619 {
620 char Buffer[4000];
621 char *End = Buffer;
622
623 while (1)
624 {
625 int Res = read(Fd,End,sizeof(Buffer) - (End-Buffer));
626 if (Res < 0 && errno == EINTR)
627 continue;
628
629 // Process is dead, this is kind of bad..
630 if (Res == 0)
631 return false;
632
633 // No data
634 if (Res < 0 && errno == EAGAIN)
635 return true;
636 if (Res < 0)
637 return false;
638
639 End += Res;
640
641 // Look for the end of the message
642 for (char *I = Buffer; I + 1 < End; I++)
643 {
644 if (I[0] != '\n' || I[1] != '\n')
645 continue;
646
647 // Pull the message out
648 string Message(Buffer,0,I-Buffer);
649
650 // Fix up the buffer
651 for (; I < End && *I == '\n'; I++);
652 End -= I-Buffer;
653 memmove(Buffer,I,End-Buffer);
654 I = Buffer;
655
656 List.push_back(Message);
657 }
658 if (End == Buffer)
659 return true;
660
661 if (WaitFd(Fd) == false)
662 return false;
663 }
664 }
665 /*}}}*/
666 // MonthConv - Converts a month string into a number /*{{{*/
667 // ---------------------------------------------------------------------
668 /* This was lifted from the boa webserver which lifted it from 'wn-v1.07'
669 Made it a bit more robust with a few touppers though. */
670 static int MonthConv(char *Month)
671 {
672 switch (toupper(*Month))
673 {
674 case 'A':
675 return toupper(Month[1]) == 'P'?3:7;
676 case 'D':
677 return 11;
678 case 'F':
679 return 1;
680 case 'J':
681 if (toupper(Month[1]) == 'A')
682 return 0;
683 return toupper(Month[2]) == 'N'?5:6;
684 case 'M':
685 return toupper(Month[2]) == 'R'?2:4;
686 case 'N':
687 return 10;
688 case 'O':
689 return 9;
690 case 'S':
691 return 8;
692
693 // Pretend it is January..
694 default:
695 return 0;
696 }
697 }
698 /*}}}*/
699 // timegm - Internal timegm function if gnu is not available /*{{{*/
700 // ---------------------------------------------------------------------
701 /* Ripped this evil little function from wget - I prefer the use of
702 GNU timegm if possible as this technique will have interesting problems
703 with leap seconds, timezones and other.
704
705 Converts struct tm to time_t, assuming the data in tm is UTC rather
706 than local timezone (mktime assumes the latter).
707
708 Contributed by Roger Beeman <beeman@cisco.com>, with the help of
709 Mark Baushke <mdb@cisco.com> and the rest of the Gurus at CISCO. */
710 #ifndef __USE_MISC // glib sets this
711 static time_t timegm(struct tm *t)
712 {
713 time_t tl, tb;
714
715 tl = mktime (t);
716 if (tl == -1)
717 return -1;
718 tb = mktime (gmtime (&tl));
719 return (tl <= tb ? (tl + (tl - tb)) : (tl - (tb - tl)));
720 }
721 #endif
722 /*}}}*/
723 // StrToTime - Converts a string into a time_t /*{{{*/
724 // ---------------------------------------------------------------------
725 /* This handles all 3 populare time formats including RFC 1123, RFC 1036
726 and the C library asctime format. It requires the GNU library function
727 'timegm' to convert a struct tm in UTC to a time_t. For some bizzar
728 reason the C library does not provide any such function :< This also
729 handles the weird, but unambiguous FTP time format*/
730 bool StrToTime(string Val,time_t &Result)
731 {
732 struct tm Tm;
733 char Month[10];
734 const char *I = Val.c_str();
735
736 // Skip the day of the week
737 for (;*I != 0 && *I != ' '; I++);
738
739 // Handle RFC 1123 time
740 Month[0] = 0;
741 if (sscanf(I," %d %3s %d %d:%d:%d GMT",&Tm.tm_mday,Month,&Tm.tm_year,
742 &Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec) != 6)
743 {
744 // Handle RFC 1036 time
745 if (sscanf(I," %d-%3s-%d %d:%d:%d GMT",&Tm.tm_mday,Month,
746 &Tm.tm_year,&Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec) == 6)
747 Tm.tm_year += 1900;
748 else
749 {
750 // asctime format
751 if (sscanf(I," %3s %d %d:%d:%d %d",Month,&Tm.tm_mday,
752 &Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec,&Tm.tm_year) != 6)
753 {
754 // 'ftp' time
755 if (sscanf(Val.c_str(),"%4d%2d%2d%2d%2d%2d",&Tm.tm_year,&Tm.tm_mon,
756 &Tm.tm_mday,&Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec) != 6)
757 return false;
758 Tm.tm_mon--;
759 }
760 }
761 }
762
763 Tm.tm_isdst = 0;
764 if (Month[0] != 0)
765 Tm.tm_mon = MonthConv(Month);
766 Tm.tm_year -= 1900;
767
768 // Convert to local time and then to GMT
769 Result = timegm(&Tm);
770 return true;
771 }
772 /*}}}*/
773 // StrToNum - Convert a fixed length string to a number /*{{{*/
774 // ---------------------------------------------------------------------
775 /* This is used in decoding the crazy fixed length string headers in
776 tar and ar files. */
777 bool StrToNum(const char *Str,unsigned long &Res,unsigned Len,unsigned Base)
778 {
779 char S[30];
780 if (Len >= sizeof(S))
781 return false;
782 memcpy(S,Str,Len);
783 S[Len] = 0;
784
785 // All spaces is a zero
786 Res = 0;
787 unsigned I;
788 for (I = 0; S[I] == ' '; I++);
789 if (S[I] == 0)
790 return true;
791
792 char *End;
793 Res = strtoul(S,&End,Base);
794 if (End == S)
795 return false;
796
797 return true;
798 }
799 /*}}}*/
800 // HexDigit - Convert a hex character into an integer /*{{{*/
801 // ---------------------------------------------------------------------
802 /* Helper for Hex2Num */
803 static int HexDigit(int c)
804 {
805 if (c >= '0' && c <= '9')
806 return c - '0';
807 if (c >= 'a' && c <= 'f')
808 return c - 'a' + 10;
809 if (c >= 'A' && c <= 'F')
810 return c - 'A' + 10;
811 return 0;
812 }
813 /*}}}*/
814 // Hex2Num - Convert a long hex number into a buffer /*{{{*/
815 // ---------------------------------------------------------------------
816 /* The length of the buffer must be exactly 1/2 the length of the string. */
817 bool Hex2Num(string Str,unsigned char *Num,unsigned int Length)
818 {
819 if (Str.length() != Length*2)
820 return false;
821
822 // Convert each digit. We store it in the same order as the string
823 int J = 0;
824 for (string::const_iterator I = Str.begin(); I != Str.end();J++, I += 2)
825 {
826 if (isxdigit(*I) == 0 || isxdigit(I[1]) == 0)
827 return false;
828
829 Num[J] = HexDigit(I[0]) << 4;
830 Num[J] += HexDigit(I[1]);
831 }
832
833 return true;
834 }
835 /*}}}*/
836 // TokSplitString - Split a string up by a given token /*{{{*/
837 // ---------------------------------------------------------------------
838 /* This is intended to be a faster splitter, it does not use dynamic
839 memories. Input is changed to insert nulls at each token location. */
840 bool TokSplitString(char Tok,char *Input,char **List,
841 unsigned long ListMax)
842 {
843 // Strip any leading spaces
844 char *Start = Input;
845 char *Stop = Start + strlen(Start);
846 for (; *Start != 0 && isspace(*Start) != 0; Start++);
847
848 unsigned long Count = 0;
849 char *Pos = Start;
850 while (Pos != Stop)
851 {
852 // Skip to the next Token
853 for (; Pos != Stop && *Pos != Tok; Pos++);
854
855 // Back remove spaces
856 char *End = Pos;
857 for (; End > Start && (End[-1] == Tok || isspace(End[-1]) != 0); End--);
858 *End = 0;
859
860 List[Count++] = Start;
861 if (Count >= ListMax)
862 {
863 List[Count-1] = 0;
864 return false;
865 }
866
867 // Advance pos
868 for (; Pos != Stop && (*Pos == Tok || isspace(*Pos) != 0 || *Pos == 0); Pos++);
869 Start = Pos;
870 }
871
872 List[Count] = 0;
873 return true;
874 }
875 /*}}}*/
876 // RegexChoice - Simple regex list/list matcher /*{{{*/
877 // ---------------------------------------------------------------------
878 /* */
879 unsigned long RegexChoice(RxChoiceList *Rxs,const char **ListBegin,
880 const char **ListEnd)
881 {
882 for (RxChoiceList *R = Rxs; R->Str != 0; R++)
883 R->Hit = false;
884
885 unsigned long Hits = 0;
886 for (; ListBegin != ListEnd; ListBegin++)
887 {
888 // Check if the name is a regex
889 const char *I;
890 bool Regex = true;
891 for (I = *ListBegin; *I != 0; I++)
892 if (*I == '.' || *I == '?' || *I == '*' || *I == '|')
893 break;
894 if (*I == 0)
895 Regex = false;
896
897 // Compile the regex pattern
898 regex_t Pattern;
899 if (Regex == true)
900 if (regcomp(&Pattern,*ListBegin,REG_EXTENDED | REG_ICASE |
901 REG_NOSUB) != 0)
902 Regex = false;
903
904 // Search the list
905 bool Done = false;
906 for (RxChoiceList *R = Rxs; R->Str != 0; R++)
907 {
908 if (R->Str[0] == 0)
909 continue;
910
911 if (strcasecmp(R->Str,*ListBegin) != 0)
912 {
913 if (Regex == false)
914 continue;
915 if (regexec(&Pattern,R->Str,0,0,0) != 0)
916 continue;
917 }
918 Done = true;
919
920 if (R->Hit == false)
921 Hits++;
922
923 R->Hit = true;
924 }
925
926 if (Regex == true)
927 regfree(&Pattern);
928
929 if (Done == false)
930 _error->Warning(_("Selection %s not found"),*ListBegin);
931 }
932
933 return Hits;
934 }
935 /*}}}*/
936 // ioprintf - C format string outputter to C++ iostreams /*{{{*/
937 // ---------------------------------------------------------------------
938 /* This is used to make the internationalization strinc easier to translate
939 and to allow reordering of parameters */
940 void ioprintf(ostream &out,const char *format,...)
941 {
942 va_list args;
943 va_start(args,format);
944
945 // sprintf the description
946 char S[400];
947 vsnprintf(S,sizeof(S),format,args);
948 out << S;
949 }
950 /*}}}*/
951
952 // CheckDomainList - See if Host is in a , seperate list /*{{{*/
953 // ---------------------------------------------------------------------
954 /* The domain list is a comma seperate list of domains that are suffix
955 matched against the argument */
956 bool CheckDomainList(string Host,string List)
957 {
958 string::const_iterator Start = List.begin();
959 for (string::const_iterator Cur = List.begin(); Cur <= List.end(); Cur++)
960 {
961 if (Cur < List.end() && *Cur != ',')
962 continue;
963
964 // Match the end of the string..
965 if ((Host.size() >= (unsigned)(Cur - List.begin())) &&
966 Cur - Start != 0 &&
967 stringcasecmp(Host.end() - (Cur - Start),Host.end(),Start,Cur) == 0)
968 return true;
969
970 Start = Cur + 1;
971 }
972 return false;
973 }
974 /*}}}*/
975
976 // URI::CopyFrom - Copy from an object /*{{{*/
977 // ---------------------------------------------------------------------
978 /* This parses the URI into all of its components */
979 void URI::CopyFrom(string U)
980 {
981 string::const_iterator I = U.begin();
982
983 // Locate the first colon, this separates the scheme
984 for (; I < U.end() && *I != ':' ; I++);
985 string::const_iterator FirstColon = I;
986
987 /* Determine if this is a host type URI with a leading double //
988 and then search for the first single / */
989 string::const_iterator SingleSlash = I;
990 if (I + 3 < U.end() && I[1] == '/' && I[2] == '/')
991 SingleSlash += 3;
992
993 /* Find the / indicating the end of the hostname, ignoring /'s in the
994 square brackets */
995 bool InBracket = false;
996 for (; SingleSlash < U.end() && (*SingleSlash != '/' || InBracket == true); SingleSlash++)
997 {
998 if (*SingleSlash == '[')
999 InBracket = true;
1000 if (InBracket == true && *SingleSlash == ']')
1001 InBracket = false;
1002 }
1003
1004 if (SingleSlash > U.end())
1005 SingleSlash = U.end();
1006
1007 // We can now write the access and path specifiers
1008 Access = string(U,0,FirstColon - U.begin());
1009 if (SingleSlash != U.end())
1010 Path = string(U,SingleSlash - U.begin());
1011 if (Path.empty() == true)
1012 Path = "/";
1013
1014 // Now we attempt to locate a user:pass@host fragment
1015 if (FirstColon[1] == '/' && FirstColon[2] == '/')
1016 FirstColon += 3;
1017 else
1018 FirstColon += 1;
1019 if (FirstColon >= U.end())
1020 return;
1021
1022 if (FirstColon > SingleSlash)
1023 FirstColon = SingleSlash;
1024
1025 // Find the colon...
1026 I = FirstColon + 1;
1027 if (I > SingleSlash)
1028 I = SingleSlash;
1029 for (; I < SingleSlash && *I != ':'; I++);
1030 string::const_iterator SecondColon = I;
1031
1032 // Search for the @ after the colon
1033 for (; I < SingleSlash && *I != '@'; I++);
1034 string::const_iterator At = I;
1035
1036 // Now write the host and user/pass
1037 if (At == SingleSlash)
1038 {
1039 if (FirstColon < SingleSlash)
1040 Host = string(U,FirstColon - U.begin(),SingleSlash - FirstColon);
1041 }
1042 else
1043 {
1044 Host = string(U,At - U.begin() + 1,SingleSlash - At - 1);
1045 User = string(U,FirstColon - U.begin(),SecondColon - FirstColon);
1046 if (SecondColon < At)
1047 Password = string(U,SecondColon - U.begin() + 1,At - SecondColon - 1);
1048 }
1049
1050 // Now we parse the RFC 2732 [] hostnames.
1051 unsigned long PortEnd = 0;
1052 InBracket = false;
1053 for (unsigned I = 0; I != Host.length();)
1054 {
1055 if (Host[I] == '[')
1056 {
1057 InBracket = true;
1058 Host.erase(I,1);
1059 continue;
1060 }
1061
1062 if (InBracket == true && Host[I] == ']')
1063 {
1064 InBracket = false;
1065 Host.erase(I,1);
1066 PortEnd = I;
1067 continue;
1068 }
1069 I++;
1070 }
1071
1072 // Tsk, weird.
1073 if (InBracket == true)
1074 {
1075 Host = string();
1076 return;
1077 }
1078
1079 // Now we parse off a port number from the hostname
1080 Port = 0;
1081 string::size_type Pos = Host.rfind(':');
1082 if (Pos == string::npos || Pos < PortEnd)
1083 return;
1084
1085 Port = atoi(string(Host,Pos+1).c_str());
1086 Host = string(Host,0,Pos);
1087 }
1088 /*}}}*/
1089 // URI::operator string - Convert the URI to a string /*{{{*/
1090 // ---------------------------------------------------------------------
1091 /* */
1092 URI::operator string()
1093 {
1094 string Res;
1095
1096 if (Access.empty() == false)
1097 Res = Access + ':';
1098
1099 if (Host.empty() == false)
1100 {
1101 if (Access.empty() == false)
1102 Res += "//";
1103
1104 if (User.empty() == false)
1105 {
1106 Res += User;
1107 if (Password.empty() == false)
1108 Res += ":" + Password;
1109 Res += "@";
1110 }
1111
1112 // Add RFC 2732 escaping characters
1113 if (Access.empty() == false &&
1114 (Host.find('/') != string::npos || Host.find(':') != string::npos))
1115 Res += '[' + Host + ']';
1116 else
1117 Res += Host;
1118
1119 if (Port != 0)
1120 {
1121 char S[30];
1122 sprintf(S,":%u",Port);
1123 Res += S;
1124 }
1125 }
1126
1127 if (Path.empty() == false)
1128 {
1129 if (Path[0] != '/')
1130 Res += "/" + Path;
1131 else
1132 Res += Path;
1133 }
1134
1135 return Res;
1136 }
1137 /*}}}*/
1138 // URI::SiteOnly - Return the schema and site for the URI /*{{{*/
1139 // ---------------------------------------------------------------------
1140 /* */
1141 string URI::SiteOnly(string URI)
1142 {
1143 ::URI U(URI);
1144 U.User = string();
1145 U.Password = string();
1146 U.Path = string();
1147 U.Port = 0;
1148 return U;
1149 }
1150 /*}}}*/