]> git.saurik.com Git - apt.git/blame - apt-pkg/contrib/strutl.cc
Newer sub/guess scripts
[apt.git] / apt-pkg / contrib / strutl.cc
CommitLineData
6c139d6e
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
66cadb07 3// $Id: strutl.cc,v 1.37 2001/02/23 06:08:57 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>
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
AL
197/* */
198string QuoteString(string Str,const char *Bad)
1bc849af
AL
199{
200 string Res;
201 for (string::iterator I = Str.begin(); I != Str.end(); I++)
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
AL
218/* This undoes QuoteString */
219string DeQuoteString(string Str)
6c139d6e
AL
220{
221 string Res;
222 for (string::iterator I = Str.begin(); I != Str.end(); I++)
223 {
6d5dd02a 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. */
316string SubstVar(string Str,string Subst,string Contents)
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 */
347string URItoFileName(string URI)
348{
54cf15cb
AL
349 // Nuke 'sensitive' items
350 ::URI U(URI);
351 U.User = string();
352 U.Password = string();
353 U.Access = "";
354
ad00ae81 355 // "\x00-\x20{}|\\\\^\\[\\]<>\"\x7F-\xFF";
54cf15cb 356 URI = QuoteString(U,"\\|{}[]<>\"^~_=!@#$%^&*");
ad00ae81
AL
357 string::iterator J = URI.begin();
358 for (; J != URI.end(); J++)
359 if (*J == '/')
360 *J = '_';
361 return URI;
362}
363 /*}}}*/
6c139d6e
AL
364// Base64Encode - Base64 Encoding routine for short strings /*{{{*/
365// ---------------------------------------------------------------------
366/* This routine performs a base64 transformation on a string. It was ripped
367 from wget and then patched and bug fixed.
368
369 This spec can be found in rfc2045 */
370string Base64Encode(string S)
371{
372 // Conversion table.
373 static char tbl[64] = {'A','B','C','D','E','F','G','H',
374 'I','J','K','L','M','N','O','P',
375 'Q','R','S','T','U','V','W','X',
376 'Y','Z','a','b','c','d','e','f',
377 'g','h','i','j','k','l','m','n',
378 'o','p','q','r','s','t','u','v',
379 'w','x','y','z','0','1','2','3',
380 '4','5','6','7','8','9','+','/'};
381
382 // Pre-allocate some space
383 string Final;
384 Final.reserve((4*S.length() + 2)/3 + 2);
385
386 /* Transform the 3x8 bits to 4x6 bits, as required by
387 base64. */
388 for (string::const_iterator I = S.begin(); I < S.end(); I += 3)
389 {
390 char Bits[3] = {0,0,0};
391 Bits[0] = I[0];
392 if (I + 1 < S.end())
393 Bits[1] = I[1];
394 if (I + 2 < S.end())
395 Bits[2] = I[2];
396
397 Final += tbl[Bits[0] >> 2];
398 Final += tbl[((Bits[0] & 3) << 4) + (Bits[1] >> 4)];
399
400 if (I + 1 >= S.end())
401 break;
402
403 Final += tbl[((Bits[1] & 0xf) << 2) + (Bits[2] >> 6)];
404
405 if (I + 2 >= S.end())
406 break;
407
408 Final += tbl[Bits[2] & 0x3f];
409 }
410
411 /* Apply the padding elements, this tells how many bytes the remote
412 end should discard */
413 if (S.length() % 3 == 2)
414 Final += '=';
415 if (S.length() % 3 == 1)
416 Final += "==";
417
418 return Final;
419}
420 /*}}}*/
421// stringcmp - Arbitary string compare /*{{{*/
422// ---------------------------------------------------------------------
423/* This safely compares two non-null terminated strings of arbitary
424 length */
425int stringcmp(const char *A,const char *AEnd,const char *B,const char *BEnd)
426{
427 for (; A != AEnd && B != BEnd; A++, B++)
428 if (*A != *B)
429 break;
430
431 if (A == AEnd && B == BEnd)
432 return 0;
433 if (A == AEnd)
434 return 1;
435 if (B == BEnd)
436 return -1;
437 if (*A < *B)
438 return -1;
439 return 1;
440}
441 /*}}}*/
442// stringcasecmp - Arbitary case insensitive string compare /*{{{*/
443// ---------------------------------------------------------------------
444/* */
445int stringcasecmp(const char *A,const char *AEnd,const char *B,const char *BEnd)
446{
447 for (; A != AEnd && B != BEnd; A++, B++)
448 if (toupper(*A) != toupper(*B))
449 break;
3b5421b4 450
6c139d6e
AL
451 if (A == AEnd && B == BEnd)
452 return 0;
453 if (A == AEnd)
454 return 1;
455 if (B == BEnd)
456 return -1;
457 if (toupper(*A) < toupper(*B))
458 return -1;
459 return 1;
460}
461 /*}}}*/
3b5421b4
AL
462// LookupTag - Lookup the value of a tag in a taged string /*{{{*/
463// ---------------------------------------------------------------------
464/* The format is like those used in package files and the method
465 communication system */
466string LookupTag(string Message,const char *Tag,const char *Default)
467{
468 // Look for a matching tag.
469 int Length = strlen(Tag);
470 for (string::iterator I = Message.begin(); I + Length < Message.end(); I++)
471 {
472 // Found the tag
473 if (I[Length] == ':' && stringcasecmp(I,I+Length,Tag) == 0)
474 {
475 // Find the end of line and strip the leading/trailing spaces
476 string::iterator J;
477 I += Length + 1;
478 for (; isspace(*I) != 0 && I < Message.end(); I++);
479 for (J = I; *J != '\n' && J < Message.end(); J++);
480 for (; J > I && isspace(J[-1]) != 0; J--);
481
482 return string(I,J-I);
483 }
484
485 for (; *I != '\n' && I < Message.end(); I++);
486 }
487
488 // Failed to find a match
489 if (Default == 0)
490 return string();
491 return Default;
492}
493 /*}}}*/
494// StringToBool - Converts a string into a boolean /*{{{*/
495// ---------------------------------------------------------------------
496/* This inspects the string to see if it is true or if it is false and
497 then returns the result. Several varients on true/false are checked. */
498int StringToBool(string Text,int Default = -1)
499{
500 char *End;
501 int Res = strtol(Text.c_str(),&End,0);
502 if (End != Text.c_str() && Res >= 0 && Res <= 1)
503 return Res;
504
505 // Check for positives
506 if (strcasecmp(Text.c_str(),"no") == 0 ||
507 strcasecmp(Text.c_str(),"false") == 0 ||
508 strcasecmp(Text.c_str(),"without") == 0 ||
7f25bdff 509 strcasecmp(Text.c_str(),"off") == 0 ||
3b5421b4
AL
510 strcasecmp(Text.c_str(),"disable") == 0)
511 return 0;
512
513 // Check for negatives
514 if (strcasecmp(Text.c_str(),"yes") == 0 ||
515 strcasecmp(Text.c_str(),"true") == 0 ||
516 strcasecmp(Text.c_str(),"with") == 0 ||
7f25bdff 517 strcasecmp(Text.c_str(),"on") == 0 ||
3b5421b4
AL
518 strcasecmp(Text.c_str(),"enable") == 0)
519 return 1;
520
521 return Default;
522}
523 /*}}}*/
0a8a80e5
AL
524// TimeRFC1123 - Convert a time_t into RFC1123 format /*{{{*/
525// ---------------------------------------------------------------------
526/* This converts a time_t into a string time representation that is
527 year 2000 complient and timezone neutral */
528string TimeRFC1123(time_t Date)
529{
530 struct tm Conv = *gmtime(&Date);
531 char Buf[300];
532
533 const char *Day[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
534 const char *Month[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul",
535 "Aug","Sep","Oct","Nov","Dec"};
536
537 sprintf(Buf,"%s, %02i %s %i %02i:%02i:%02i GMT",Day[Conv.tm_wday],
538 Conv.tm_mday,Month[Conv.tm_mon],Conv.tm_year+1900,Conv.tm_hour,
539 Conv.tm_min,Conv.tm_sec);
540 return Buf;
541}
542 /*}}}*/
543// ReadMessages - Read messages from the FD /*{{{*/
544// ---------------------------------------------------------------------
545/* This pulls full messages from the input FD into the message buffer.
546 It assumes that messages will not pause during transit so no
547 fancy buffering is used. */
548bool ReadMessages(int Fd, vector<string> &List)
549{
550 char Buffer[4000];
551 char *End = Buffer;
552
553 while (1)
554 {
555 int Res = read(Fd,End,sizeof(Buffer) - (End-Buffer));
b0db36b1
AL
556 if (Res < 0 && errno == EINTR)
557 continue;
0a8a80e5
AL
558
559 // Process is dead, this is kind of bad..
560 if (Res == 0)
561 return false;
562
563 // No data
b2e465d6 564 if (Res < 0 && errno == EAGAIN)
0a8a80e5 565 return true;
b2e465d6
AL
566 if (Res < 0)
567 return false;
568
0a8a80e5
AL
569 End += Res;
570
571 // Look for the end of the message
c88edf1d 572 for (char *I = Buffer; I + 1 < End; I++)
0a8a80e5
AL
573 {
574 if (I[0] != '\n' || I[1] != '\n')
575 continue;
576
577 // Pull the message out
578 string Message(Buffer,0,I-Buffer);
579
580 // Fix up the buffer
581 for (; I < End && *I == '\n'; I++);
582 End -= I-Buffer;
583 memmove(Buffer,I,End-Buffer);
584 I = Buffer;
585
586 List.push_back(Message);
587 }
588 if (End == Buffer)
589 return true;
590
591 if (WaitFd(Fd) == false)
592 return false;
593 }
594}
595 /*}}}*/
24231681
AL
596// MonthConv - Converts a month string into a number /*{{{*/
597// ---------------------------------------------------------------------
598/* This was lifted from the boa webserver which lifted it from 'wn-v1.07'
599 Made it a bit more robust with a few touppers though. */
600static int MonthConv(char *Month)
601{
602 switch (toupper(*Month))
603 {
604 case 'A':
605 return toupper(Month[1]) == 'P'?3:7;
606 case 'D':
607 return 11;
608 case 'F':
609 return 1;
610 case 'J':
611 if (toupper(Month[1]) == 'A')
612 return 0;
613 return toupper(Month[2]) == 'N'?5:6;
614 case 'M':
615 return toupper(Month[2]) == 'R'?2:4;
616 case 'N':
617 return 10;
618 case 'O':
619 return 9;
620 case 'S':
621 return 8;
622
623 // Pretend it is January..
624 default:
625 return 0;
626 }
627}
628 /*}}}*/
6d5dd02a
AL
629// timegm - Internal timegm function if gnu is not available /*{{{*/
630// ---------------------------------------------------------------------
631/* Ripped this evil little function from wget - I prefer the use of
632 GNU timegm if possible as this technique will have interesting problems
633 with leap seconds, timezones and other.
634
635 Converts struct tm to time_t, assuming the data in tm is UTC rather
636 than local timezone (mktime assumes the latter).
637
638 Contributed by Roger Beeman <beeman@cisco.com>, with the help of
639 Mark Baushke <mdb@cisco.com> and the rest of the Gurus at CISCO. */
640#ifndef __USE_MISC // glib sets this
641static time_t timegm(struct tm *t)
642{
643 time_t tl, tb;
644
645 tl = mktime (t);
646 if (tl == -1)
647 return -1;
648 tb = mktime (gmtime (&tl));
649 return (tl <= tb ? (tl + (tl - tb)) : (tl - (tb - tl)));
650}
651#endif
652 /*}}}*/
24231681
AL
653// StrToTime - Converts a string into a time_t /*{{{*/
654// ---------------------------------------------------------------------
655/* This handles all 3 populare time formats including RFC 1123, RFC 1036
656 and the C library asctime format. It requires the GNU library function
657 'timegm' to convert a struct tm in UTC to a time_t. For some bizzar
f58a97d3
AL
658 reason the C library does not provide any such function :< This also
659 handles the weird, but unambiguous FTP time format*/
24231681
AL
660bool StrToTime(string Val,time_t &Result)
661{
662 struct tm Tm;
663 char Month[10];
664 const char *I = Val.c_str();
665
666 // Skip the day of the week
667 for (;*I != 0 && *I != ' '; I++);
668
669 // Handle RFC 1123 time
f58a97d3 670 Month[0] = 0;
24231681
AL
671 if (sscanf(I," %d %3s %d %d:%d:%d GMT",&Tm.tm_mday,Month,&Tm.tm_year,
672 &Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec) != 6)
673 {
674 // Handle RFC 1036 time
675 if (sscanf(I," %d-%3s-%d %d:%d:%d GMT",&Tm.tm_mday,Month,
676 &Tm.tm_year,&Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec) == 6)
677 Tm.tm_year += 1900;
678 else
679 {
680 // asctime format
681 if (sscanf(I," %3s %d %d:%d:%d %d",Month,&Tm.tm_mday,
682 &Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec,&Tm.tm_year) != 6)
f58a97d3
AL
683 {
684 // 'ftp' time
7ef72446 685 if (sscanf(Val.c_str(),"%4d%2d%2d%2d%2d%2d",&Tm.tm_year,&Tm.tm_mon,
f58a97d3
AL
686 &Tm.tm_mday,&Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec) != 6)
687 return false;
688 Tm.tm_mon--;
689 }
24231681
AL
690 }
691 }
692
693 Tm.tm_isdst = 0;
f58a97d3
AL
694 if (Month[0] != 0)
695 Tm.tm_mon = MonthConv(Month);
24231681
AL
696 Tm.tm_year -= 1900;
697
698 // Convert to local time and then to GMT
699 Result = timegm(&Tm);
700 return true;
701}
702 /*}}}*/
ddc1d8d0
AL
703// StrToNum - Convert a fixed length string to a number /*{{{*/
704// ---------------------------------------------------------------------
705/* This is used in decoding the crazy fixed length string headers in
706 tar and ar files. */
707bool StrToNum(const char *Str,unsigned long &Res,unsigned Len,unsigned Base)
708{
709 char S[30];
710 if (Len >= sizeof(S))
711 return false;
712 memcpy(S,Str,Len);
713 S[Len] = 0;
714
715 // All spaces is a zero
716 Res = 0;
717 unsigned I;
718 for (I = 0; S[I] == ' '; I++);
719 if (S[I] == 0)
720 return true;
721
722 char *End;
723 Res = strtoul(S,&End,Base);
724 if (End == S)
725 return false;
726
727 return true;
728}
729 /*}}}*/
6e52073f
AL
730// HexDigit - Convert a hex character into an integer /*{{{*/
731// ---------------------------------------------------------------------
732/* Helper for Hex2Num */
733static int HexDigit(int c)
734{
735 if (c >= '0' && c <= '9')
736 return c - '0';
737 if (c >= 'a' && c <= 'f')
738 return c - 'a' + 10;
739 if (c >= 'A' && c <= 'F')
740 return c - 'A' + 10;
741 return 0;
742}
743 /*}}}*/
744// Hex2Num - Convert a long hex number into a buffer /*{{{*/
745// ---------------------------------------------------------------------
746/* The length of the buffer must be exactly 1/2 the length of the string. */
747bool Hex2Num(const char *Start,const char *End,unsigned char *Num,
748 unsigned int Length)
749{
bf3abeed 750 if (End - Start != (signed)(Length*2))
6e52073f
AL
751 return false;
752
753 // Convert each digit. We store it in the same order as the string
754 int J = 0;
755 for (const char *I = Start; I < End;J++, I += 2)
756 {
757 if (isxdigit(*I) == 0 || isxdigit(I[1]) == 0)
758 return false;
759
760 Num[J] = HexDigit(I[0]) << 4;
761 Num[J] += HexDigit(I[1]);
762 }
763
764 return true;
765}
766 /*}}}*/
b2e465d6
AL
767// TokSplitString - Split a string up by a given token /*{{{*/
768// ---------------------------------------------------------------------
769/* This is intended to be a faster splitter, it does not use dynamic
770 memories. Input is changed to insert nulls at each token location. */
771bool TokSplitString(char Tok,char *Input,char **List,
772 unsigned long ListMax)
773{
774 // Strip any leading spaces
775 char *Start = Input;
776 char *Stop = Start + strlen(Start);
777 for (; *Start != 0 && isspace(*Start) != 0; Start++);
778
779 unsigned long Count = 0;
780 char *Pos = Start;
781 while (Pos != Stop)
782 {
783 // Skip to the next Token
784 for (; Pos != Stop && *Pos != Tok; Pos++);
785
786 // Back remove spaces
787 char *End = Pos;
788 for (; End > Start && (End[-1] == Tok || isspace(End[-1]) != 0); End--);
789 *End = 0;
790
791 List[Count++] = Start;
792 if (Count >= ListMax)
793 {
794 List[Count-1] = 0;
795 return false;
796 }
797
798 // Advance pos
799 for (; Pos != Stop && (*Pos == Tok || isspace(*Pos) != 0 || *Pos == 0); Pos++);
800 Start = Pos;
801 }
802
803 List[Count] = 0;
804 return true;
805}
806 /*}}}*/
807// RegexChoice - Simple regex list/list matcher /*{{{*/
808// ---------------------------------------------------------------------
809/* */
810unsigned long RegexChoice(RxChoiceList *Rxs,const char **ListBegin,
811 const char **ListEnd)
812{
813 for (RxChoiceList *R = Rxs; R->Str != 0; R++)
814 R->Hit = false;
815
816 unsigned long Hits = 0;
817 for (; ListBegin != ListEnd; ListBegin++)
818 {
819 // Check if the name is a regex
820 const char *I;
821 bool Regex = true;
822 for (I = *ListBegin; *I != 0; I++)
823 if (*I == '.' || *I == '?' || *I == '*' || *I == '|')
824 break;
825 if (*I == 0)
826 Regex = false;
827
828 // Compile the regex pattern
829 regex_t Pattern;
830 if (Regex == true)
831 if (regcomp(&Pattern,*ListBegin,REG_EXTENDED | REG_ICASE |
832 REG_NOSUB) != 0)
833 Regex = false;
834
835 // Search the list
836 bool Done = false;
837 for (RxChoiceList *R = Rxs; R->Str != 0; R++)
838 {
839 if (R->Str[0] == 0)
840 continue;
841
842 if (strcasecmp(R->Str,*ListBegin) != 0)
843 {
844 if (Regex == false)
845 continue;
846 if (regexec(&Pattern,R->Str,0,0,0) != 0)
847 continue;
848 }
849 Done = true;
850
851 if (R->Hit == false)
852 Hits++;
853
854 R->Hit = true;
855 }
856
857 if (Regex == true)
858 regfree(&Pattern);
859
860 if (Done == false)
861 _error->Warning(_("Selection %s not found"),*ListBegin);
862 }
863
864 return Hits;
865}
866 /*}}}*/
867// ioprintf - C format string outputter to C++ iostreams /*{{{*/
868// ---------------------------------------------------------------------
869/* This is used to make the internationalization strinc easier to translate
870 and to allow reordering of parameters */
871void ioprintf(ostream &out,const char *format,...)
872{
873 va_list args;
874 va_start(args,format);
875
876 // sprintf the description
877 char S[400];
878 vsnprintf(S,sizeof(S),format,args);
879 out << S;
880}
881 /*}}}*/
93bf083d 882
f8081133
AL
883// CheckDomainList - See if Host is in a , seperate list /*{{{*/
884// ---------------------------------------------------------------------
885/* The domain list is a comma seperate list of domains that are suffix
886 matched against the argument */
887bool CheckDomainList(string Host,string List)
888{
889 const char *Start = List.begin();
890 for (const char *Cur = List.begin(); Cur <= List.end() ; Cur++)
891 {
892 if (Cur < List.end() && *Cur != ',')
893 continue;
894
895 // Match the end of the string..
66cadb07 896 if ((Host.size() >= (unsigned)(Cur - List.begin())) &&
f8081133
AL
897 Cur - Start != 0 &&
898 stringcasecmp(Host.end() - (Cur - Start),Host.end(),Start,Cur) == 0)
899 return true;
900
901 Start = Cur + 1;
902 }
903 return false;
904}
905 /*}}}*/
906
be4401bf 907// URI::CopyFrom - Copy from an object /*{{{*/
93bf083d
AL
908// ---------------------------------------------------------------------
909/* This parses the URI into all of its components */
be4401bf 910void URI::CopyFrom(string U)
93bf083d
AL
911{
912 string::const_iterator I = U.begin();
913
b2e465d6 914 // Locate the first colon, this separates the scheme
93bf083d
AL
915 for (; I < U.end() && *I != ':' ; I++);
916 string::const_iterator FirstColon = I;
917
bfd22fc0
AL
918 /* Determine if this is a host type URI with a leading double //
919 and then search for the first single / */
93bf083d
AL
920 string::const_iterator SingleSlash = I;
921 if (I + 3 < U.end() && I[1] == '/' && I[2] == '/')
bfd22fc0 922 SingleSlash += 3;
67ff87bf
AL
923
924 /* Find the / indicating the end of the hostname, ignoring /'s in the
925 square brackets */
926 bool InBracket = false;
927 for (; SingleSlash < U.end() && (*SingleSlash != '/' || InBracket == true); SingleSlash++)
928 {
929 if (*SingleSlash == '[')
930 InBracket = true;
931 if (InBracket == true && *SingleSlash == ']')
932 InBracket = false;
933 }
934
bfd22fc0 935 if (SingleSlash > U.end())
f46e7681 936 SingleSlash = U.end();
93bf083d
AL
937
938 // We can now write the access and path specifiers
939 Access = string(U,0,FirstColon - U.begin());
940 if (SingleSlash != U.end())
92e889c8
AL
941 Path = string(U,SingleSlash - U.begin());
942 if (Path.empty() == true)
943 Path = "/";
944
93bf083d 945 // Now we attempt to locate a user:pass@host fragment
542ec555 946 if (FirstColon[1] == '/' && FirstColon[2] == '/')
f46e7681
AL
947 FirstColon += 3;
948 else
949 FirstColon += 1;
93bf083d
AL
950 if (FirstColon >= U.end())
951 return;
952
953 if (FirstColon > SingleSlash)
954 FirstColon = SingleSlash;
955
3856756b
AL
956 // Find the colon...
957 I = FirstColon + 1;
1d38d0e9
AL
958 if (I > SingleSlash)
959 I = SingleSlash;
3856756b
AL
960 for (; I < SingleSlash && *I != ':'; I++);
961 string::const_iterator SecondColon = I;
962
963 // Search for the @ after the colon
93bf083d
AL
964 for (; I < SingleSlash && *I != '@'; I++);
965 string::const_iterator At = I;
966
93bf083d
AL
967 // Now write the host and user/pass
968 if (At == SingleSlash)
969 {
970 if (FirstColon < SingleSlash)
971 Host = string(U,FirstColon - U.begin(),SingleSlash - FirstColon);
972 }
973 else
974 {
975 Host = string(U,At - U.begin() + 1,SingleSlash - At - 1);
976 User = string(U,FirstColon - U.begin(),SecondColon - FirstColon);
977 if (SecondColon < At)
978 Password = string(U,SecondColon - U.begin() + 1,At - SecondColon - 1);
979 }
980
67ff87bf
AL
981 // Now we parse the RFC 2732 [] hostnames.
982 unsigned long PortEnd = 0;
983 InBracket = false;
984 for (unsigned I = 0; I != Host.length();)
985 {
986 if (Host[I] == '[')
987 {
988 InBracket = true;
989 Host.erase(I,1);
990 continue;
991 }
992
993 if (InBracket == true && Host[I] == ']')
994 {
995 InBracket = false;
996 Host.erase(I,1);
997 PortEnd = I;
998 continue;
999 }
1000 I++;
1001 }
1002
1003 // Tsk, weird.
1004 if (InBracket == true)
1005 {
1006 Host = string();
1007 return;
1008 }
1009
1d38d0e9 1010 // Now we parse off a port number from the hostname
93bf083d
AL
1011 Port = 0;
1012 string::size_type Pos = Host.rfind(':');
67ff87bf 1013 if (Pos == string::npos || Pos < PortEnd)
93bf083d
AL
1014 return;
1015
1016 Port = atoi(string(Host,Pos+1).c_str());
1017 Host = string(Host,0,Pos);
1018}
1019 /*}}}*/
1020// URI::operator string - Convert the URI to a string /*{{{*/
1021// ---------------------------------------------------------------------
1022/* */
1023URI::operator string()
1024{
54cf15cb
AL
1025 string Res;
1026
1027 if (Access.empty() == false)
1028 Res = Access + ':';
1029
93bf083d 1030 if (Host.empty() == false)
7834cb57 1031 {
54cf15cb
AL
1032 if (Access.empty() == false)
1033 Res += "//";
7834cb57 1034
93bf083d
AL
1035 if (User.empty() == false)
1036 {
54cf15cb 1037 Res += User;
93bf083d
AL
1038 if (Password.empty() == false)
1039 Res += ":" + Password;
1040 Res += "@";
1041 }
54cf15cb 1042
7834cb57
AL
1043 // Add RFC 2732 escaping characters
1044 if (Access.empty() == false &&
1045 (Host.find('/') != string::npos || Host.find(':') != string::npos))
1046 Res += '[' + Host + ']';
1047 else
1048 Res += Host;
1049
492f957a
AL
1050 if (Port != 0)
1051 {
1052 char S[30];
1053 sprintf(S,":%u",Port);
1054 Res += S;
1055 }
93bf083d
AL
1056 }
1057
1058 if (Path.empty() == false)
492f957a
AL
1059 {
1060 if (Path[0] != '/')
1061 Res += "/" + Path;
1062 else
1063 Res += Path;
1064 }
93bf083d
AL
1065
1066 return Res;
1067}
1068 /*}}}*/
b2e465d6
AL
1069// URI::SiteOnly - Return the schema and site for the URI /*{{{*/
1070// ---------------------------------------------------------------------
1071/* */
1072string URI::SiteOnly(string URI)
1073{
1074 ::URI U(URI);
1075 U.User = string();
1076 U.Password = string();
1077 U.Path = string();
1078 U.Port = 0;
1079 return U;
1080}
1081 /*}}}*/