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