Sync
[ntk/apt.git] / apt-pkg / contrib / strutl.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: strutl.cc,v 1.6 1998/10/22 04:56:48 jgg Exp $
4 /* ######################################################################
5
6 String Util - Some usefull string functions.
7
8 strstrip - Remove whitespace from the front and end of a line.
9
10 This source is placed in the Public Domain, do with it what you will
11 It was originally written by Jason Gunthorpe <jgg@gpu.srv.ualberta.ca>
12
13 ##################################################################### */
14 /*}}}*/
15 // Includes /*{{{*/
16 #include <strutl.h>
17 #include <apt-pkg/fileutl.h>
18
19 #include <ctype.h>
20 #include <string.h>
21 #include <stdio.h>
22 #include <time.h>
23 /*}}}*/
24
25 // strstrip - Remove white space from the front and back of a string /*{{{*/
26 // ---------------------------------------------------------------------
27 /* This is handy to use when parsing a file. It also removes \n's left
28 over from fgets and company */
29 char *_strstrip(char *String)
30 {
31 for (;*String != 0 && (*String == ' ' || *String == '\t'); String++);
32
33 if (*String == 0)
34 return String;
35
36 char *End = String + strlen(String) - 1;
37 for (;End != String - 1 && (*End == ' ' || *End == '\t' || *End == '\n' ||
38 *End == '\r'); End--);
39 End++;
40 *End = 0;
41 return String;
42 };
43 /*}}}*/
44 // strtabexpand - Converts tabs into 8 spaces /*{{{*/
45 // ---------------------------------------------------------------------
46 /* */
47 char *_strtabexpand(char *String,size_t Len)
48 {
49 for (char *I = String; I != I + Len && *I != 0; I++)
50 {
51 if (*I != '\t')
52 continue;
53 if (I + 8 > String + Len)
54 {
55 *I = 0;
56 return String;
57 }
58
59 /* Assume the start of the string is 0 and find the next 8 char
60 division */
61 int Len;
62 if (String == I)
63 Len = 1;
64 else
65 Len = 8 - ((String - I) % 8);
66 Len -= 2;
67 if (Len <= 0)
68 {
69 *I = ' ';
70 continue;
71 }
72
73 memmove(I + Len,I + 1,strlen(I) + 1);
74 for (char *J = I; J + Len != I; *I = ' ', I++);
75 }
76 return String;
77 }
78 /*}}}*/
79 // ParseQuoteWord - Parse a single word out of a string /*{{{*/
80 // ---------------------------------------------------------------------
81 /* This grabs a single word, converts any % escaped characters to their
82 proper values and advances the pointer. Double quotes are understood
83 and striped out as well. This is for URI/URL parsing. */
84 bool ParseQuoteWord(const char *&String,string &Res)
85 {
86 // Skip leading whitespace
87 const char *C = String;
88 for (;*C != 0 && *C == ' '; C++);
89 if (*C == 0)
90 return false;
91
92 // Jump to the next word
93 for (;*C != 0 && *C != ' '; C++)
94 {
95 if (*C == '"')
96 {
97 for (C++;*C != 0 && *C != '"'; C++);
98 if (*C == 0)
99 return false;
100 }
101 }
102
103 // Now de-quote characters
104 char Buffer[1024];
105 char Tmp[3];
106 const char *Start = String;
107 char *I;
108 for (I = Buffer; I < Buffer + sizeof(Buffer) && Start != C; I++)
109 {
110 if (*Start == '%' && Start + 2 < C)
111 {
112 Tmp[0] = Start[1];
113 Tmp[1] = Start[2];
114 Tmp[3] = 0;
115 *I = (char)strtol(Tmp,0,16);
116 Start += 3;
117 continue;
118 }
119 if (*Start != '"')
120 *I = *Start;
121 else
122 I--;
123 Start++;
124 }
125 *I = 0;
126 Res = Buffer;
127
128 // Skip ending white space
129 for (;*C != 0 && *C == ' '; C++);
130 String = C;
131 return true;
132 }
133 /*}}}*/
134 // ParseCWord - Parses a string like a C "" expression /*{{{*/
135 // ---------------------------------------------------------------------
136 /* This expects a series of space seperated strings enclosed in ""'s.
137 It concatenates the ""'s into a single string. */
138 bool ParseCWord(const char *String,string &Res)
139 {
140 // Skip leading whitespace
141 const char *C = String;
142 for (;*C != 0 && *C == ' '; C++);
143 if (*C == 0)
144 return false;
145
146 char Buffer[1024];
147 char *Buf = Buffer;
148 if (strlen(String) >= sizeof(Buffer))
149 return false;
150
151 for (; *C != 0; C++)
152 {
153 if (*C == '"')
154 {
155 for (C++; *C != 0 && *C != '"'; C++)
156 *Buf++ = *C;
157
158 if (*C == 0)
159 return false;
160
161 continue;
162 }
163
164 if (C != String && isspace(*C) != 0 && isspace(C[-1]) != 0)
165 continue;
166 if (isspace(*C) == 0)
167 return false;
168 *Buf++ = ' ';
169 }
170 *Buf = 0;
171 Res = Buffer;
172 return true;
173 }
174 /*}}}*/
175 // QuoteString - Convert a string into quoted from /*{{{*/
176 // ---------------------------------------------------------------------
177 /* */
178 string QuoteString(string Str,const char *Bad)
179 {
180 string Res;
181 for (string::iterator I = Str.begin(); I != Str.end(); I++)
182 {
183 if (strchr(Bad,*I) != 0 || isprint(*I) == 0 ||
184 *I <= 0x20 || *I >= 0x7F)
185 {
186 char Buf[10];
187 sprintf(Buf,"%%%02x",(int)*I);
188 Res += Buf;
189 }
190 else
191 Res += *I;
192 }
193 return Res;
194 }
195 /*}}}*/
196 // SizeToStr - Convert a long into a human readable size /*{{{*/
197 // ---------------------------------------------------------------------
198 /* A max of 4 digits are shown before conversion to the next highest unit. The
199 max length of the string will be 5 chars unless the size is > 10
200 YottaBytes (E24) */
201 string SizeToStr(double Size)
202 {
203 char S[300];
204 double ASize;
205 if (Size >= 0)
206 ASize = Size;
207 else
208 ASize = -1*Size;
209
210 /* bytes, KiloBytes, MegaBytes, GigaBytes, TeraBytes, PetaBytes,
211 ExaBytes, ZettaBytes, YottaBytes */
212 char Ext[] = {'b','k','M','G','T','P','E','Z','Y'};
213 int I = 0;
214 while (I <= 8)
215 {
216 if (ASize < 100 && I != 0)
217 {
218 sprintf(S,"%.1f%c",ASize,Ext[I]);
219 break;
220 }
221
222 if (ASize < 10000)
223 {
224 sprintf(S,"%.0f%c",ASize,Ext[I]);
225 break;
226 }
227 ASize /= 1000.0;
228 I++;
229 }
230
231 return S;
232 }
233 /*}}}*/
234 // TimeToStr - Convert the time into a string /*{{{*/
235 // ---------------------------------------------------------------------
236 /* Converts a number of seconds to a hms format */
237 string TimeToStr(unsigned long Sec)
238 {
239 char S[300];
240
241 while (1)
242 {
243 if (Sec > 60*60*24)
244 {
245 sprintf(S,"%lid %lih%lim%lis",Sec/60/60/24,(Sec/60/60) % 24,(Sec/60) % 60,Sec % 60);
246 break;
247 }
248
249 if (Sec > 60*60)
250 {
251 sprintf(S,"%lih%lim%lis",Sec/60/60,(Sec/60) % 60,Sec % 60);
252 break;
253 }
254
255 if (Sec > 60)
256 {
257 sprintf(S,"%lim%lis",Sec/60,Sec % 60);
258 break;
259 }
260
261 sprintf(S,"%lis",Sec);
262 break;
263 }
264
265 return S;
266 }
267 /*}}}*/
268 // SubstVar - Substitute a string for another string /*{{{*/
269 // ---------------------------------------------------------------------
270 /* This replaces all occurances of Subst with Contents in Str. */
271 string SubstVar(string Str,string Subst,string Contents)
272 {
273 string::size_type Pos = 0;
274 string::size_type OldPos = 0;
275 string Temp;
276
277 while (OldPos < Str.length() &&
278 (Pos = Str.find(Subst,OldPos)) != string::npos)
279 {
280 Temp += string(Str,OldPos,Pos) + Contents;
281 OldPos = Pos + Subst.length();
282 }
283
284 if (OldPos == 0)
285 return Str;
286
287 return Temp + string(Str,OldPos);
288 }
289 /*}}}*/
290 // URItoFileName - Convert the uri into a unique file name /*{{{*/
291 // ---------------------------------------------------------------------
292 /* This converts a URI into a safe filename. It quotes all unsafe characters
293 and converts / to _ and removes the scheme identifier. The resulting
294 file name should be unique and never occur again for a different file */
295 string URItoFileName(string URI)
296 {
297 string::const_iterator I = URI.begin() + URI.find(':') + 1;
298 for (; I < URI.end() && *I == '/'; I++);
299
300 // "\x00-\x20{}|\\\\^\\[\\]<>\"\x7F-\xFF";
301 URI = QuoteString(string(I,URI.end() - I),"\\|{}[]<>\"^~_=!@#$%^&*");
302 string::iterator J = URI.begin();
303 for (; J != URI.end(); J++)
304 if (*J == '/')
305 *J = '_';
306 return URI;
307 }
308 /*}}}*/
309 // URIAccess - Return the access method for the URI /*{{{*/
310 // ---------------------------------------------------------------------
311 /* */
312 string URIAccess(string URI)
313 {
314 string::size_type Pos = URI.find(':');
315 if (Pos == string::npos)
316 return URI;
317 return string(URI,0,Pos);
318 }
319 /*}}}*/
320 // Base64Encode - Base64 Encoding routine for short strings /*{{{*/
321 // ---------------------------------------------------------------------
322 /* This routine performs a base64 transformation on a string. It was ripped
323 from wget and then patched and bug fixed.
324
325 This spec can be found in rfc2045 */
326 string Base64Encode(string S)
327 {
328 // Conversion table.
329 static char tbl[64] = {'A','B','C','D','E','F','G','H',
330 'I','J','K','L','M','N','O','P',
331 'Q','R','S','T','U','V','W','X',
332 'Y','Z','a','b','c','d','e','f',
333 'g','h','i','j','k','l','m','n',
334 'o','p','q','r','s','t','u','v',
335 'w','x','y','z','0','1','2','3',
336 '4','5','6','7','8','9','+','/'};
337
338 // Pre-allocate some space
339 string Final;
340 Final.reserve((4*S.length() + 2)/3 + 2);
341
342 /* Transform the 3x8 bits to 4x6 bits, as required by
343 base64. */
344 for (string::const_iterator I = S.begin(); I < S.end(); I += 3)
345 {
346 char Bits[3] = {0,0,0};
347 Bits[0] = I[0];
348 if (I + 1 < S.end())
349 Bits[1] = I[1];
350 if (I + 2 < S.end())
351 Bits[2] = I[2];
352
353 Final += tbl[Bits[0] >> 2];
354 Final += tbl[((Bits[0] & 3) << 4) + (Bits[1] >> 4)];
355
356 if (I + 1 >= S.end())
357 break;
358
359 Final += tbl[((Bits[1] & 0xf) << 2) + (Bits[2] >> 6)];
360
361 if (I + 2 >= S.end())
362 break;
363
364 Final += tbl[Bits[2] & 0x3f];
365 }
366
367 /* Apply the padding elements, this tells how many bytes the remote
368 end should discard */
369 if (S.length() % 3 == 2)
370 Final += '=';
371 if (S.length() % 3 == 1)
372 Final += "==";
373
374 return Final;
375 }
376 /*}}}*/
377 // stringcmp - Arbitary string compare /*{{{*/
378 // ---------------------------------------------------------------------
379 /* This safely compares two non-null terminated strings of arbitary
380 length */
381 int stringcmp(const char *A,const char *AEnd,const char *B,const char *BEnd)
382 {
383 for (; A != AEnd && B != BEnd; A++, B++)
384 if (*A != *B)
385 break;
386
387 if (A == AEnd && B == BEnd)
388 return 0;
389 if (A == AEnd)
390 return 1;
391 if (B == BEnd)
392 return -1;
393 if (*A < *B)
394 return -1;
395 return 1;
396 }
397 /*}}}*/
398 // stringcasecmp - Arbitary case insensitive string compare /*{{{*/
399 // ---------------------------------------------------------------------
400 /* */
401 int stringcasecmp(const char *A,const char *AEnd,const char *B,const char *BEnd)
402 {
403 for (; A != AEnd && B != BEnd; A++, B++)
404 if (toupper(*A) != toupper(*B))
405 break;
406
407 if (A == AEnd && B == BEnd)
408 return 0;
409 if (A == AEnd)
410 return 1;
411 if (B == BEnd)
412 return -1;
413 if (toupper(*A) < toupper(*B))
414 return -1;
415 return 1;
416 }
417 /*}}}*/
418 // LookupTag - Lookup the value of a tag in a taged string /*{{{*/
419 // ---------------------------------------------------------------------
420 /* The format is like those used in package files and the method
421 communication system */
422 string LookupTag(string Message,const char *Tag,const char *Default)
423 {
424 // Look for a matching tag.
425 int Length = strlen(Tag);
426 for (string::iterator I = Message.begin(); I + Length < Message.end(); I++)
427 {
428 // Found the tag
429 if (I[Length] == ':' && stringcasecmp(I,I+Length,Tag) == 0)
430 {
431 // Find the end of line and strip the leading/trailing spaces
432 string::iterator J;
433 I += Length + 1;
434 for (; isspace(*I) != 0 && I < Message.end(); I++);
435 for (J = I; *J != '\n' && J < Message.end(); J++);
436 for (; J > I && isspace(J[-1]) != 0; J--);
437
438 return string(I,J-I);
439 }
440
441 for (; *I != '\n' && I < Message.end(); I++);
442 }
443
444 // Failed to find a match
445 if (Default == 0)
446 return string();
447 return Default;
448 }
449 /*}}}*/
450 // StringToBool - Converts a string into a boolean /*{{{*/
451 // ---------------------------------------------------------------------
452 /* This inspects the string to see if it is true or if it is false and
453 then returns the result. Several varients on true/false are checked. */
454 int StringToBool(string Text,int Default = -1)
455 {
456 char *End;
457 int Res = strtol(Text.c_str(),&End,0);
458 if (End != Text.c_str() && Res >= 0 && Res <= 1)
459 return Res;
460
461 // Check for positives
462 if (strcasecmp(Text.c_str(),"no") == 0 ||
463 strcasecmp(Text.c_str(),"false") == 0 ||
464 strcasecmp(Text.c_str(),"without") == 0 ||
465 strcasecmp(Text.c_str(),"disable") == 0)
466 return 0;
467
468 // Check for negatives
469 if (strcasecmp(Text.c_str(),"yes") == 0 ||
470 strcasecmp(Text.c_str(),"true") == 0 ||
471 strcasecmp(Text.c_str(),"with") == 0 ||
472 strcasecmp(Text.c_str(),"enable") == 0)
473 return 1;
474
475 return Default;
476 }
477 /*}}}*/
478 // TimeRFC1123 - Convert a time_t into RFC1123 format /*{{{*/
479 // ---------------------------------------------------------------------
480 /* This converts a time_t into a string time representation that is
481 year 2000 complient and timezone neutral */
482 string TimeRFC1123(time_t Date)
483 {
484 struct tm Conv = *gmtime(&Date);
485 char Buf[300];
486
487 const char *Day[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
488 const char *Month[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul",
489 "Aug","Sep","Oct","Nov","Dec"};
490
491 sprintf(Buf,"%s, %02i %s %i %02i:%02i:%02i GMT",Day[Conv.tm_wday],
492 Conv.tm_mday,Month[Conv.tm_mon],Conv.tm_year+1900,Conv.tm_hour,
493 Conv.tm_min,Conv.tm_sec);
494 return Buf;
495 }
496 /*}}}*/
497 // ReadMessages - Read messages from the FD /*{{{*/
498 // ---------------------------------------------------------------------
499 /* This pulls full messages from the input FD into the message buffer.
500 It assumes that messages will not pause during transit so no
501 fancy buffering is used. */
502 bool ReadMessages(int Fd, vector<string> &List)
503 {
504 char Buffer[4000];
505 char *End = Buffer;
506
507 while (1)
508 {
509 int Res = read(Fd,End,sizeof(Buffer) - (End-Buffer));
510
511 // Process is dead, this is kind of bad..
512 if (Res == 0)
513 return false;
514
515 // No data
516 if (Res <= 0)
517 return true;
518
519 End += Res;
520
521 // Look for the end of the message
522 for (char *I = Buffer; I < End; I++)
523 {
524 if (I[0] != '\n' || I[1] != '\n')
525 continue;
526
527 // Pull the message out
528 string Message(Buffer,0,I-Buffer);
529
530 // Fix up the buffer
531 for (; I < End && *I == '\n'; I++);
532 End -= I-Buffer;
533 memmove(Buffer,I,End-Buffer);
534 I = Buffer;
535
536 List.push_back(Message);
537 }
538 if (End == Buffer)
539 return true;
540
541 if (WaitFd(Fd) == false)
542 return false;
543 }
544 }
545 /*}}}*/