]> git.saurik.com Git - apt.git/blame - apt-pkg/contrib/strutl.cc
Join with aliencode
[apt.git] / apt-pkg / contrib / strutl.cc
CommitLineData
6c139d6e
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
b2e465d6 3// $Id: strutl.cc,v 1.35 2001/02/20 07:03:17 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
be4401bf 883// URI::CopyFrom - Copy from an object /*{{{*/
93bf083d
AL
884// ---------------------------------------------------------------------
885/* This parses the URI into all of its components */
be4401bf 886void URI::CopyFrom(string U)
93bf083d
AL
887{
888 string::const_iterator I = U.begin();
889
b2e465d6 890 // Locate the first colon, this separates the scheme
93bf083d
AL
891 for (; I < U.end() && *I != ':' ; I++);
892 string::const_iterator FirstColon = I;
893
bfd22fc0
AL
894 /* Determine if this is a host type URI with a leading double //
895 and then search for the first single / */
93bf083d
AL
896 string::const_iterator SingleSlash = I;
897 if (I + 3 < U.end() && I[1] == '/' && I[2] == '/')
bfd22fc0 898 SingleSlash += 3;
67ff87bf
AL
899
900 /* Find the / indicating the end of the hostname, ignoring /'s in the
901 square brackets */
902 bool InBracket = false;
903 for (; SingleSlash < U.end() && (*SingleSlash != '/' || InBracket == true); SingleSlash++)
904 {
905 if (*SingleSlash == '[')
906 InBracket = true;
907 if (InBracket == true && *SingleSlash == ']')
908 InBracket = false;
909 }
910
bfd22fc0 911 if (SingleSlash > U.end())
f46e7681 912 SingleSlash = U.end();
93bf083d
AL
913
914 // We can now write the access and path specifiers
915 Access = string(U,0,FirstColon - U.begin());
916 if (SingleSlash != U.end())
92e889c8
AL
917 Path = string(U,SingleSlash - U.begin());
918 if (Path.empty() == true)
919 Path = "/";
920
93bf083d 921 // Now we attempt to locate a user:pass@host fragment
542ec555 922 if (FirstColon[1] == '/' && FirstColon[2] == '/')
f46e7681
AL
923 FirstColon += 3;
924 else
925 FirstColon += 1;
93bf083d
AL
926 if (FirstColon >= U.end())
927 return;
928
929 if (FirstColon > SingleSlash)
930 FirstColon = SingleSlash;
931
3856756b
AL
932 // Find the colon...
933 I = FirstColon + 1;
1d38d0e9
AL
934 if (I > SingleSlash)
935 I = SingleSlash;
3856756b
AL
936 for (; I < SingleSlash && *I != ':'; I++);
937 string::const_iterator SecondColon = I;
938
939 // Search for the @ after the colon
93bf083d
AL
940 for (; I < SingleSlash && *I != '@'; I++);
941 string::const_iterator At = I;
942
93bf083d
AL
943 // Now write the host and user/pass
944 if (At == SingleSlash)
945 {
946 if (FirstColon < SingleSlash)
947 Host = string(U,FirstColon - U.begin(),SingleSlash - FirstColon);
948 }
949 else
950 {
951 Host = string(U,At - U.begin() + 1,SingleSlash - At - 1);
952 User = string(U,FirstColon - U.begin(),SecondColon - FirstColon);
953 if (SecondColon < At)
954 Password = string(U,SecondColon - U.begin() + 1,At - SecondColon - 1);
955 }
956
67ff87bf
AL
957 // Now we parse the RFC 2732 [] hostnames.
958 unsigned long PortEnd = 0;
959 InBracket = false;
960 for (unsigned I = 0; I != Host.length();)
961 {
962 if (Host[I] == '[')
963 {
964 InBracket = true;
965 Host.erase(I,1);
966 continue;
967 }
968
969 if (InBracket == true && Host[I] == ']')
970 {
971 InBracket = false;
972 Host.erase(I,1);
973 PortEnd = I;
974 continue;
975 }
976 I++;
977 }
978
979 // Tsk, weird.
980 if (InBracket == true)
981 {
982 Host = string();
983 return;
984 }
985
1d38d0e9 986 // Now we parse off a port number from the hostname
93bf083d
AL
987 Port = 0;
988 string::size_type Pos = Host.rfind(':');
67ff87bf 989 if (Pos == string::npos || Pos < PortEnd)
93bf083d
AL
990 return;
991
992 Port = atoi(string(Host,Pos+1).c_str());
993 Host = string(Host,0,Pos);
994}
995 /*}}}*/
996// URI::operator string - Convert the URI to a string /*{{{*/
997// ---------------------------------------------------------------------
998/* */
999URI::operator string()
1000{
54cf15cb
AL
1001 string Res;
1002
1003 if (Access.empty() == false)
1004 Res = Access + ':';
1005
93bf083d 1006 if (Host.empty() == false)
7834cb57 1007 {
54cf15cb
AL
1008 if (Access.empty() == false)
1009 Res += "//";
7834cb57 1010
93bf083d
AL
1011 if (User.empty() == false)
1012 {
54cf15cb 1013 Res += User;
93bf083d
AL
1014 if (Password.empty() == false)
1015 Res += ":" + Password;
1016 Res += "@";
1017 }
54cf15cb 1018
7834cb57
AL
1019 // Add RFC 2732 escaping characters
1020 if (Access.empty() == false &&
1021 (Host.find('/') != string::npos || Host.find(':') != string::npos))
1022 Res += '[' + Host + ']';
1023 else
1024 Res += Host;
1025
492f957a
AL
1026 if (Port != 0)
1027 {
1028 char S[30];
1029 sprintf(S,":%u",Port);
1030 Res += S;
1031 }
93bf083d
AL
1032 }
1033
1034 if (Path.empty() == false)
492f957a
AL
1035 {
1036 if (Path[0] != '/')
1037 Res += "/" + Path;
1038 else
1039 Res += Path;
1040 }
93bf083d
AL
1041
1042 return Res;
1043}
1044 /*}}}*/
b2e465d6
AL
1045// URI::SiteOnly - Return the schema and site for the URI /*{{{*/
1046// ---------------------------------------------------------------------
1047/* */
1048string URI::SiteOnly(string URI)
1049{
1050 ::URI U(URI);
1051 U.User = string();
1052 U.Password = string();
1053 U.Path = string();
1054 U.Port = 0;
1055 return U;
1056}
1057 /*}}}*/