]> git.saurik.com Git - apt.git/blob - apt-pkg/contrib/strutl.cc
fixed typo in fileutl.cc (damn you, studly caps...)
[apt.git] / apt-pkg / contrib / strutl.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: strutl.cc,v 1.37 2001/02/23 06:08:57 jgg Exp $
4 /* ######################################################################
5
6 String Util - Some useful string functions.
7
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
10 especially in APT methods.
11
12 This source is placed in the Public Domain, do with it what you will
13 It was originally written by Jason Gunthorpe <jgg@gpu.srv.ualberta.ca>
14
15 ##################################################################### */
16 /*}}}*/
17 // Includes /*{{{*/
18 #ifdef __GNUG__
19 #pragma implementation "apt-pkg/strutl.h"
20 #endif
21
22 #include <apt-pkg/strutl.h>
23 #include <apt-pkg/fileutl.h>
24 #include <apt-pkg/error.h>
25
26 #include <apti18n.h>
27
28 #include <ctype.h>
29 #include <string.h>
30 #include <stdio.h>
31 #include <unistd.h>
32 #include <regex.h>
33 #include <errno.h>
34 #include <stdarg.h>
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 */
41 char *_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 /* */
59 char *_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
95 and striped out as well. This is for URI/URL parsing. It also can
96 understand [] brackets.*/
97 bool 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
106 for (;*C != 0 && isspace(*C) == 0; C++)
107 {
108 if (*C == '"')
109 {
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++);
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];
133 Tmp[2] = 0;
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
148 for (;*C != 0 && isspace(*C) != 0; C++);
149 String = C;
150 return true;
151 }
152 /*}}}*/
153 // ParseCWord - Parses a string like a C "" expression /*{{{*/
154 // ---------------------------------------------------------------------
155 /* This expects a series of space separated strings enclosed in ""'s.
156 It concatenates the ""'s into a single string. */
157 bool ParseCWord(const char *&String,string &Res)
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++ = ' ';
188 }
189 *Buf = 0;
190 Res = Buffer;
191 String = C;
192 return true;
193 }
194 /*}}}*/
195 // QuoteString - Convert a string into quoted from /*{{{*/
196 // ---------------------------------------------------------------------
197 /* */
198 string QuoteString(string Str,const char *Bad)
199 {
200 string Res;
201 for (string::iterator I = Str.begin(); I != Str.end(); I++)
202 {
203 if (strchr(Bad,*I) != 0 || isprint(*I) == 0 ||
204 *I <= 0x20 || *I >= 0x7F)
205 {
206 char Buf[10];
207 sprintf(Buf,"%%%02x",(int)*I);
208 Res += Buf;
209 }
210 else
211 Res += *I;
212 }
213 return Res;
214 }
215 /*}}}*/
216 // DeQuoteString - Convert a string from quoted from /*{{{*/
217 // ---------------------------------------------------------------------
218 /* This undoes QuoteString */
219 string DeQuoteString(string Str)
220 {
221 string Res;
222 for (string::iterator I = Str.begin(); I != Str.end(); I++)
223 {
224 if (*I == '%' && I + 2 < Str.end())
225 {
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;
233 }
234 else
235 Res += *I;
236 }
237 return Res;
238 }
239
240 /*}}}*/
241 // SizeToStr - Convert a long into a human readable size /*{{{*/
242 // ---------------------------------------------------------------------
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
245 YottaBytes (E24) */
246 string 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 */
257 char Ext[] = {'\0','k','M','G','T','P','E','Z','Y'};
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 */
282 string 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. */
316 string SubstVar(string Str,string Subst,string Contents)
317 {
318 string::size_type Pos = 0;
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 }
334
335 string 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 }
341 /*}}}*/
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 */
347 string URItoFileName(string URI)
348 {
349 // Nuke 'sensitive' items
350 ::URI U(URI);
351 U.User = string();
352 U.Password = string();
353 U.Access = "";
354
355 // "\x00-\x20{}|\\\\^\\[\\]<>\"\x7F-\xFF";
356 URI = QuoteString(U,"\\|{}[]<>\"^~_=!@#$%^&*");
357 string::iterator J = URI.begin();
358 for (; J != URI.end(); J++)
359 if (*J == '/')
360 *J = '_';
361 return URI;
362 }
363 /*}}}*/
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 */
370 string 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 */
425 int 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 /* */
445 int 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;
450
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 /*}}}*/
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 */
466 string 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. */
498 int 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 ||
509 strcasecmp(Text.c_str(),"off") == 0 ||
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 ||
517 strcasecmp(Text.c_str(),"on") == 0 ||
518 strcasecmp(Text.c_str(),"enable") == 0)
519 return 1;
520
521 return Default;
522 }
523 /*}}}*/
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 */
528 string 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. */
548 bool 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));
556 if (Res < 0 && errno == EINTR)
557 continue;
558
559 // Process is dead, this is kind of bad..
560 if (Res == 0)
561 return false;
562
563 // No data
564 if (Res < 0 && errno == EAGAIN)
565 return true;
566 if (Res < 0)
567 return false;
568
569 End += Res;
570
571 // Look for the end of the message
572 for (char *I = Buffer; I + 1 < End; I++)
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 /*}}}*/
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. */
600 static 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 /*}}}*/
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
641 static 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 /*}}}*/
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
658 reason the C library does not provide any such function :< This also
659 handles the weird, but unambiguous FTP time format*/
660 bool 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
670 Month[0] = 0;
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)
683 {
684 // 'ftp' time
685 if (sscanf(Val.c_str(),"%4d%2d%2d%2d%2d%2d",&Tm.tm_year,&Tm.tm_mon,
686 &Tm.tm_mday,&Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec) != 6)
687 return false;
688 Tm.tm_mon--;
689 }
690 }
691 }
692
693 Tm.tm_isdst = 0;
694 if (Month[0] != 0)
695 Tm.tm_mon = MonthConv(Month);
696 Tm.tm_year -= 1900;
697
698 // Convert to local time and then to GMT
699 Result = timegm(&Tm);
700 return true;
701 }
702 /*}}}*/
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. */
707 bool 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 /*}}}*/
730 // HexDigit - Convert a hex character into an integer /*{{{*/
731 // ---------------------------------------------------------------------
732 /* Helper for Hex2Num */
733 static 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. */
747 bool Hex2Num(const char *Start,const char *End,unsigned char *Num,
748 unsigned int Length)
749 {
750 if (End - Start != (signed)(Length*2))
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 /*}}}*/
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. */
771 bool 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 /* */
810 unsigned 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 */
871 void 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 /*}}}*/
882
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 */
887 bool 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..
896 if ((Host.size() >= (unsigned)(Cur - List.begin())) &&
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
907 // URI::CopyFrom - Copy from an object /*{{{*/
908 // ---------------------------------------------------------------------
909 /* This parses the URI into all of its components */
910 void URI::CopyFrom(string U)
911 {
912 string::const_iterator I = U.begin();
913
914 // Locate the first colon, this separates the scheme
915 for (; I < U.end() && *I != ':' ; I++);
916 string::const_iterator FirstColon = I;
917
918 /* Determine if this is a host type URI with a leading double //
919 and then search for the first single / */
920 string::const_iterator SingleSlash = I;
921 if (I + 3 < U.end() && I[1] == '/' && I[2] == '/')
922 SingleSlash += 3;
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
935 if (SingleSlash > U.end())
936 SingleSlash = U.end();
937
938 // We can now write the access and path specifiers
939 Access = string(U,0,FirstColon - U.begin());
940 if (SingleSlash != U.end())
941 Path = string(U,SingleSlash - U.begin());
942 if (Path.empty() == true)
943 Path = "/";
944
945 // Now we attempt to locate a user:pass@host fragment
946 if (FirstColon[1] == '/' && FirstColon[2] == '/')
947 FirstColon += 3;
948 else
949 FirstColon += 1;
950 if (FirstColon >= U.end())
951 return;
952
953 if (FirstColon > SingleSlash)
954 FirstColon = SingleSlash;
955
956 // Find the colon...
957 I = FirstColon + 1;
958 if (I > SingleSlash)
959 I = SingleSlash;
960 for (; I < SingleSlash && *I != ':'; I++);
961 string::const_iterator SecondColon = I;
962
963 // Search for the @ after the colon
964 for (; I < SingleSlash && *I != '@'; I++);
965 string::const_iterator At = I;
966
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
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
1010 // Now we parse off a port number from the hostname
1011 Port = 0;
1012 string::size_type Pos = Host.rfind(':');
1013 if (Pos == string::npos || Pos < PortEnd)
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 /* */
1023 URI::operator string()
1024 {
1025 string Res;
1026
1027 if (Access.empty() == false)
1028 Res = Access + ':';
1029
1030 if (Host.empty() == false)
1031 {
1032 if (Access.empty() == false)
1033 Res += "//";
1034
1035 if (User.empty() == false)
1036 {
1037 Res += User;
1038 if (Password.empty() == false)
1039 Res += ":" + Password;
1040 Res += "@";
1041 }
1042
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
1050 if (Port != 0)
1051 {
1052 char S[30];
1053 sprintf(S,":%u",Port);
1054 Res += S;
1055 }
1056 }
1057
1058 if (Path.empty() == false)
1059 {
1060 if (Path[0] != '/')
1061 Res += "/" + Path;
1062 else
1063 Res += Path;
1064 }
1065
1066 return Res;
1067 }
1068 /*}}}*/
1069 // URI::SiteOnly - Return the schema and site for the URI /*{{{*/
1070 // ---------------------------------------------------------------------
1071 /* */
1072 string 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 /*}}}*/