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