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