]> git.saurik.com Git - apt.git/blame - apt-pkg/tagfile.cc
prepare-release: travis: Ignore build profiles in build-depends
[apt.git] / apt-pkg / tagfile.cc
CommitLineData
578bfd0a
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
b3d44315 3// $Id: tagfile.cc,v 1.37.2.2 2003/12/31 16:02:30 mdz Exp $
578bfd0a
AL
4/* ######################################################################
5
6 Fast scanner for RFC-822 type header information
7
ad00ae81 8 This uses a rotating buffer to load the package information into.
578bfd0a
AL
9 The scanner runs over it and isolates and indexes a single section.
10
11 ##################################################################### */
12 /*}}}*/
13// Include Files /*{{{*/
ea542140
DK
14#include<config.h>
15
094a497d
AL
16#include <apt-pkg/tagfile.h>
17#include <apt-pkg/error.h>
cdcc6d34 18#include <apt-pkg/strutl.h>
472ff00e 19#include <apt-pkg/fileutl.h>
578bfd0a 20
55153bf9
DK
21#include <list>
22
578bfd0a
AL
23#include <string>
24#include <stdio.h>
851a45a8 25#include <ctype.h>
453b82a3
DK
26#include <stdlib.h>
27#include <string.h>
ea542140
DK
28
29#include <apti18n.h>
578bfd0a
AL
30 /*}}}*/
31
851a45a8
AL
32using std::string;
33
55153bf9 34class APT_HIDDEN pkgTagFilePrivate /*{{{*/
1abbce9e
MV
35{
36public:
55153bf9 37 void Reset(FileFd * const pFd, unsigned long long const pSize, pkgTagFile::Flags const pFlags)
1abbce9e 38 {
3d8232bf
DK
39 if (Buffer != NULL)
40 free(Buffer);
6c55f07a 41 Buffer = NULL;
3d8232bf 42 Fd = pFd;
55153bf9 43 Flags = pFlags;
6c55f07a
DK
44 Start = NULL;
45 End = NULL;
46 Done = false;
47 iOffset = 0;
48 Size = pSize;
55153bf9
DK
49 isCommentedLine = false;
50 chunks.clear();
1abbce9e 51 }
6c55f07a 52
55153bf9 53 pkgTagFilePrivate(FileFd * const pFd, unsigned long long const Size, pkgTagFile::Flags const pFlags) : Buffer(NULL)
6c55f07a 54 {
55153bf9 55 Reset(pFd, Size, pFlags);
6c55f07a
DK
56 }
57 FileFd * Fd;
55153bf9 58 pkgTagFile::Flags Flags;
1abbce9e
MV
59 char *Buffer;
60 char *Start;
61 char *End;
62 bool Done;
650faab0
DK
63 unsigned long long iOffset;
64 unsigned long long Size;
55153bf9
DK
65 bool isCommentedLine;
66 struct FileChunk
67 {
68 bool const good;
69 size_t length;
70 FileChunk(bool const pgood, size_t const plength) : good(pgood), length(plength) {}
71 };
72 std::list<FileChunk> chunks;
3d8232bf
DK
73
74 ~pkgTagFilePrivate()
75 {
76 if (Buffer != NULL)
77 free(Buffer);
78 }
1abbce9e 79};
55153bf9
DK
80 /*}}}*/
81class APT_HIDDEN pkgTagSectionPrivate /*{{{*/
bc4ccfeb
DK
82{
83public:
84 pkgTagSectionPrivate()
85 {
86 }
87 struct TagData {
88 unsigned int StartTag;
89 unsigned int EndTag;
90 unsigned int StartValue;
91 unsigned int NextInBucket;
92
e8afd168 93 explicit TagData(unsigned int const StartTag) : StartTag(StartTag), EndTag(0), StartValue(0), NextInBucket(0) {}
bc4ccfeb
DK
94 };
95 std::vector<TagData> Tags;
96};
55153bf9 97 /*}}}*/
bc4ccfeb 98
8710a36a
DK
99static unsigned long AlphaHash(const char *Text, size_t Length) /*{{{*/
100{
101 /* This very simple hash function for the last 8 letters gives
102 very good performance on the debian package files */
103 if (Length > 8)
104 {
105 Text += (Length - 8);
106 Length = 8;
107 }
108 unsigned long Res = 0;
109 for (size_t i = 0; i < Length; ++i)
110 Res = ((unsigned long)(Text[i]) & 0xDF) ^ (Res << 1);
111 return Res & 0xFF;
112}
113 /*}}}*/
114
578bfd0a 115// TagFile::pkgTagFile - Constructor /*{{{*/
55153bf9
DK
116pkgTagFile::pkgTagFile(FileFd * const pFd,pkgTagFile::Flags const pFlags, unsigned long long const Size)
117 : d(new pkgTagFilePrivate(pFd, Size + 4, pFlags))
118{
119 Init(pFd, pFlags, Size);
120}
6c55f07a 121pkgTagFile::pkgTagFile(FileFd * const pFd,unsigned long long const Size)
55153bf9 122 : pkgTagFile(pFd, pkgTagFile::STRICT, Size)
feab34c5 123{
feab34c5 124}
55153bf9 125void pkgTagFile::Init(FileFd * const pFd, pkgTagFile::Flags const pFlags, unsigned long long Size)
578bfd0a 126{
0aae6d14
DK
127 /* The size is increased by 4 because if we start with the Size of the
128 filename we need to try to read 1 char more to see an EOF faster, 1
129 char the end-pointer can be on and maybe 2 newlines need to be added
130 to the end of the file -> 4 extra chars */
131 Size += 4;
55153bf9 132 d->Reset(pFd, Size, pFlags);
1abbce9e 133
6c55f07a 134 if (d->Fd->IsOpen() == false)
1abbce9e 135 d->Start = d->End = d->Buffer = 0;
4b2803b8
DK
136 else
137 d->Buffer = (char*)malloc(sizeof(char) * Size);
138
139 if (d->Buffer == NULL)
1abbce9e 140 d->Done = true;
4b2803b8
DK
141 else
142 d->Done = false;
143
1abbce9e 144 d->Start = d->End = d->Buffer;
1abbce9e 145 d->iOffset = 0;
4b2803b8
DK
146 if (d->Done == false)
147 Fill();
55153bf9
DK
148}
149void pkgTagFile::Init(FileFd * const pFd,unsigned long long Size)
150{
151 Init(pFd, pkgTagFile::STRICT, Size);
578bfd0a
AL
152}
153 /*}}}*/
b2e465d6 154// TagFile::~pkgTagFile - Destructor /*{{{*/
29f7b36c
AL
155pkgTagFile::~pkgTagFile()
156{
1abbce9e
MV
157 delete d;
158}
159 /*}}}*/
4b2803b8 160// TagFile::Offset - Return the current offset in the buffer /*{{{*/
a02db58f 161APT_PURE unsigned long pkgTagFile::Offset()
1abbce9e
MV
162{
163 return d->iOffset;
29f7b36c
AL
164}
165 /*}}}*/
75c541fd 166// TagFile::Resize - Resize the internal buffer /*{{{*/
578bfd0a 167// ---------------------------------------------------------------------
75c541fd
MV
168/* Resize the internal buffer (double it in size). Fail if a maximum size
169 * size is reached.
170 */
171bool pkgTagFile::Resize()
578bfd0a 172{
75c541fd 173 // fail is the buffer grows too big
1abbce9e 174 if(d->Size > 1024*1024+1)
432b168c
MV
175 return false;
176
0aae6d14
DK
177 return Resize(d->Size * 2);
178}
179bool pkgTagFile::Resize(unsigned long long const newSize)
180{
181 unsigned long long const EndSize = d->End - d->Start;
0aae6d14 182
75c541fd 183 // get new buffer and use it
55153bf9 184 char* const newBuffer = static_cast<char*>(realloc(d->Buffer, sizeof(char) * newSize));
4b2803b8
DK
185 if (newBuffer == NULL)
186 return false;
187 d->Buffer = newBuffer;
0aae6d14 188 d->Size = newSize;
99c2e5ac 189
75c541fd 190 // update the start/end pointers to the new buffer
1abbce9e
MV
191 d->Start = d->Buffer;
192 d->End = d->Start + EndSize;
75c541fd
MV
193 return true;
194}
81e9789b 195 /*}}}*/
578bfd0a
AL
196// TagFile::Step - Advance to the next section /*{{{*/
197// ---------------------------------------------------------------------
75c541fd
MV
198/* If the Section Scanner fails we refill the buffer and try again.
199 * If that fails too, double the buffer size and try again until a
200 * maximum buffer is reached.
201 */
578bfd0a
AL
202bool pkgTagFile::Step(pkgTagSection &Tag)
203{
8710a36a 204 if(Tag.Scan(d->Start,d->End - d->Start) == false)
0852eaef 205 {
8710a36a
DK
206 do
207 {
208 if (Fill() == false)
209 return false;
210
211 if(Tag.Scan(d->Start,d->End - d->Start, false))
212 break;
75c541fd 213
8710a36a 214 if (Resize() == false)
2f6a2fbb 215 return _error->Error(_("Unable to parse package file %s (%d)"),
6c55f07a 216 d->Fd->Name().c_str(), 1);
8710a36a
DK
217
218 } while (Tag.Scan(d->Start,d->End - d->Start, false) == false);
613f9499 219 }
8710a36a 220
55153bf9
DK
221 size_t tagSize = Tag.size();
222 d->Start += tagSize;
223
224 if ((d->Flags & pkgTagFile::SUPPORT_COMMENTS) == 0)
225 d->iOffset += tagSize;
226 else
227 {
228 auto first = d->chunks.begin();
229 for (; first != d->chunks.end(); ++first)
230 {
231 if (first->good == false)
232 d->iOffset += first->length;
233 else
234 {
235 if (tagSize < first->length)
236 {
237 first->length -= tagSize;
238 d->iOffset += tagSize;
239 break;
240 }
241 else
242 {
243 tagSize -= first->length;
244 d->iOffset += first->length;
245 }
246 }
247 }
248 d->chunks.erase(d->chunks.begin(), first);
249 }
b2e465d6
AL
250
251 Tag.Trim();
99c2e5ac
MV
252 return true;
253}
254 /*}}}*/
255// TagFile::Fill - Top up the buffer /*{{{*/
256// ---------------------------------------------------------------------
257/* This takes the bit at the end of the buffer and puts it at the start
258 then fills the rest from the file */
55153bf9
DK
259static bool FillBuffer(pkgTagFilePrivate * const d)
260{
261 unsigned long long Actual = 0;
262 // See if only a bit of the file is left
263 unsigned long long const dataSize = d->Size - ((d->End - d->Buffer) + 1);
264 if (d->Fd->Read(d->End, dataSize, &Actual) == false)
265 return false;
266 if (Actual != dataSize)
267 d->Done = true;
268 d->End += Actual;
269 return true;
270}
271static void RemoveCommentsFromBuffer(pkgTagFilePrivate * const d)
272{
273 // look for valid comments in the buffer
274 char * good_start = nullptr, * bad_start = nullptr;
275 char * current = d->Start;
276 if (d->isCommentedLine == false)
277 {
278 if (d->Start == d->Buffer)
279 {
280 // the start of the buffer is a newline as a record can't start
281 // in the middle of a line by definition.
282 if (*d->Start == '#')
283 {
284 d->isCommentedLine = true;
285 ++current;
286 if (current > d->End)
287 d->chunks.emplace_back(false, 1);
288 }
289 }
290 if (d->isCommentedLine == false)
291 good_start = d->Start;
292 else
293 bad_start = d->Start;
294 }
295 else
296 bad_start = d->Start;
297
298 std::vector<std::pair<char*, size_t>> good_parts;
299 while (current <= d->End)
300 {
301 size_t const restLength = (d->End - current) + 1;
302 if (d->isCommentedLine == false)
303 {
304 current = static_cast<char*>(memchr(current, '#', restLength));
305 if (current == nullptr)
306 {
307 size_t const goodLength = d->End - good_start;
308 d->chunks.emplace_back(true, goodLength);
309 if (good_start != d->Start)
310 good_parts.push_back(std::make_pair(good_start, goodLength));
311 break;
312 }
313 bad_start = current;
314 --current;
315 // ensure that this is really a comment and not a '#' in the middle of a line
316 if (*current == '\n')
317 {
318 size_t const goodLength = (current - good_start) + 1;
319 d->chunks.emplace_back(true, goodLength);
320 good_parts.push_back(std::make_pair(good_start, goodLength));
321 good_start = nullptr;
322 d->isCommentedLine = true;
323 }
324 current += 2;
325 }
326 else // the current line is a comment
327 {
328 current = static_cast<char*>(memchr(current, '\n', restLength));
329 if (current == nullptr)
330 {
331 d->chunks.emplace_back(false, (d->End - bad_start));
332 break;
333 }
334 ++current;
335 // is the next line a comment, too?
336 if (current > d->End || *current != '#')
337 {
338 d->chunks.emplace_back(false, (current - bad_start));
339 good_start = current;
340 bad_start = nullptr;
341 d->isCommentedLine = false;
342 }
343 ++current;
344 }
345 }
346
347 if (good_parts.empty() == false)
348 {
349 // we found comments, so move later parts over them
350 current = d->Start;
351 for (auto const &good: good_parts)
352 {
353 memmove(current, good.first, good.second);
354 current += good.second;
355 }
356 d->End = current;
357 }
358
359 if (d->isCommentedLine == true)
360 {
361 // deal with a buffer containing only comments
362 // or an (unfinished) comment at the end
363 if (good_parts.empty() == true)
364 d->End = d->Start;
365 else
366 d->Start = d->End;
367 }
368 else
369 {
370 // the buffer was all comment, but ended with the buffer
371 if (good_parts.empty() == true && good_start >= d->End)
372 d->End = d->Start;
373 else
374 d->Start = d->End;
375 }
376}
99c2e5ac
MV
377bool pkgTagFile::Fill()
378{
55153bf9
DK
379 unsigned long long const EndSize = d->End - d->Start;
380 if (EndSize != 0)
381 {
382 memmove(d->Buffer,d->Start,EndSize);
383 d->Start = d->End = d->Buffer + EndSize;
384 }
385 else
386 d->Start = d->End = d->Buffer;
387
650faab0 388 unsigned long long Actual = 0;
55153bf9 389 while (d->Done == false && d->Size > (Actual + 1))
99c2e5ac 390 {
55153bf9 391 if (FillBuffer(d) == false)
99c2e5ac 392 return false;
55153bf9
DK
393 if ((d->Flags & pkgTagFile::SUPPORT_COMMENTS) != 0)
394 RemoveCommentsFromBuffer(d);
395 Actual = d->End - d->Buffer;
99c2e5ac 396 }
55153bf9
DK
397 d->Start = d->Buffer;
398
1abbce9e 399 if (d->Done == true)
99c2e5ac
MV
400 {
401 if (EndSize <= 3 && Actual == 0)
402 return false;
1abbce9e 403 if (d->Size - (d->End - d->Buffer) < 4)
99c2e5ac 404 return true;
55153bf9 405
99c2e5ac
MV
406 // Append a double new line if one does not exist
407 unsigned int LineCount = 0;
1abbce9e 408 for (const char *E = d->End - 1; E - d->End < 6 && (*E == '\n' || *E == '\r'); E--)
99c2e5ac 409 if (*E == '\n')
55153bf9 410 ++LineCount;
0aae6d14
DK
411 if (LineCount < 2)
412 {
55153bf9 413 if (static_cast<unsigned long long>(d->End - d->Buffer) >= d->Size)
0aae6d14 414 Resize(d->Size + 3);
55153bf9 415 for (; LineCount < 2; ++LineCount)
0aae6d14
DK
416 *d->End++ = '\n';
417 }
99c2e5ac 418 }
578bfd0a
AL
419 return true;
420}
421 /*}}}*/
ad00ae81
AL
422// TagFile::Jump - Jump to a pre-recorded location in the file /*{{{*/
423// ---------------------------------------------------------------------
03e39e59
AL
424/* This jumps to a pre-recorded file location and reads the record
425 that is there */
650faab0 426bool pkgTagFile::Jump(pkgTagSection &Tag,unsigned long long Offset)
ad00ae81 427{
55153bf9 428 if ((d->Flags & pkgTagFile::SUPPORT_COMMENTS) == 0 &&
b2e465d6 429 // We are within a buffer space of the next hit..
55153bf9 430 Offset >= d->iOffset && d->iOffset + (d->End - d->Start) > Offset)
b2e465d6 431 {
650faab0 432 unsigned long long Dist = Offset - d->iOffset;
1abbce9e
MV
433 d->Start += Dist;
434 d->iOffset += Dist;
e62aa1dd
DK
435 // if we have seen the end, don't ask for more
436 if (d->Done == true)
437 return Tag.Scan(d->Start, d->End - d->Start);
438 else
439 return Step(Tag);
b2e465d6
AL
440 }
441
2ca99a0d 442 // Reposition and reload..
1abbce9e
MV
443 d->iOffset = Offset;
444 d->Done = false;
6c55f07a 445 if (d->Fd->Seek(Offset) == false)
2ca99a0d 446 return false;
1abbce9e 447 d->End = d->Start = d->Buffer;
55153bf9
DK
448 d->isCommentedLine = false;
449 d->chunks.clear();
450
2ca99a0d
MV
451 if (Fill() == false)
452 return false;
99c2e5ac 453
1abbce9e 454 if (Tag.Scan(d->Start, d->End - d->Start) == true)
2ca99a0d
MV
455 return true;
456
457 // This appends a double new line (for the real eof handling)
458 if (Fill() == false)
459 return false;
0852eaef 460
8710a36a 461 if (Tag.Scan(d->Start, d->End - d->Start, false) == false)
6c55f07a 462 return _error->Error(_("Unable to parse package file %s (%d)"),d->Fd->Name().c_str(), 2);
06bba740 463
ad00ae81
AL
464 return true;
465}
466 /*}}}*/
b40394c0
MV
467// pkgTagSection::pkgTagSection - Constructor /*{{{*/
468// ---------------------------------------------------------------------
469/* */
bc4ccfeb 470APT_IGNORE_DEPRECATED_PUSH
b40394c0 471pkgTagSection::pkgTagSection()
6c55f07a 472 : Section(0), d(new pkgTagSectionPrivate()), Stop(0)
b40394c0 473{
bc4ccfeb 474 memset(&AlphaIndexes, 0, sizeof(AlphaIndexes));
b40394c0 475}
bc4ccfeb 476APT_IGNORE_DEPRECATED_POP
b40394c0 477 /*}}}*/
578bfd0a 478// TagSection::Scan - Scan for the end of the header information /*{{{*/
8710a36a 479bool pkgTagSection::Scan(const char *Start,unsigned long MaxLength, bool const Restart)
578bfd0a 480{
8710a36a 481 Section = Start;
578bfd0a 482 const char *End = Start + MaxLength;
8710a36a 483
bc4ccfeb 484 if (Restart == false && d->Tags.empty() == false)
8710a36a 485 {
bc4ccfeb 486 Stop = Section + d->Tags.back().StartTag;
8710a36a
DK
487 if (End <= Stop)
488 return false;
489 Stop = (const char *)memchr(Stop,'\n',End - Stop);
490 if (Stop == NULL)
491 return false;
492 ++Stop;
493 }
494 else
495 {
496 Stop = Section;
bc4ccfeb 497 if (d->Tags.empty() == false)
8710a36a 498 {
bc4ccfeb
DK
499 memset(&AlphaIndexes, 0, sizeof(AlphaIndexes));
500 d->Tags.clear();
8710a36a 501 }
bc4ccfeb 502 d->Tags.reserve(0x100);
8710a36a 503 }
bc4ccfeb 504 unsigned int TagCount = d->Tags.size();
c7b5ce1c 505
2ca99a0d 506 if (Stop == 0)
0852eaef 507 return false;
81e9789b 508
bc4ccfeb 509 pkgTagSectionPrivate::TagData lastTagData(0);
8710a36a
DK
510 lastTagData.EndTag = 0;
511 unsigned long lastTagHash = 0;
512 while (Stop < End)
578bfd0a 513 {
75ab11ae
MV
514 TrimRecord(true,End);
515
516 // this can happen when TrimRecord trims away the entire Record
517 // (e.g. because it just contains comments)
518 if(Stop == End)
519 return true;
81e9789b 520
90d64280 521 // Start a new index and add it to the hash
74dedb4a 522 if (isspace_ascii(Stop[0]) == 0)
c1a22377 523 {
8710a36a
DK
524 // store the last found tag
525 if (lastTagData.EndTag != 0)
526 {
bc4ccfeb
DK
527 if (AlphaIndexes[lastTagHash] != 0)
528 lastTagData.NextInBucket = AlphaIndexes[lastTagHash];
529 APT_IGNORE_DEPRECATED_PUSH
530 AlphaIndexes[lastTagHash] = TagCount;
bc4ccfeb
DK
531 APT_IGNORE_DEPRECATED_POP
532 d->Tags.push_back(lastTagData);
8710a36a
DK
533 }
534
bc4ccfeb
DK
535 APT_IGNORE_DEPRECATED(++TagCount;)
536 lastTagData = pkgTagSectionPrivate::TagData(Stop - Section);
8710a36a
DK
537 // find the colon separating tag and value
538 char const * Colon = (char const *) memchr(Stop, ':', End - Stop);
539 if (Colon == NULL)
540 return false;
541 // find the end of the tag (which might or might not be the colon)
542 char const * EndTag = Colon;
543 --EndTag;
74dedb4a 544 for (; EndTag > Stop && isspace_ascii(*EndTag) != 0; --EndTag)
8710a36a
DK
545 ;
546 ++EndTag;
547 lastTagData.EndTag = EndTag - Section;
548 lastTagHash = AlphaHash(Stop, EndTag - Stop);
549 // find the beginning of the value
550 Stop = Colon + 1;
67c90775 551 for (; Stop < End && isspace_ascii(*Stop) != 0; ++Stop)
c72f5c4f
DK
552 if (*Stop == '\n' && Stop[1] != ' ')
553 break;
8710a36a
DK
554 if (Stop >= End)
555 return false;
556 lastTagData.StartValue = Stop - Section;
c1a22377 557 }
0a8e3465 558
c1a22377 559 Stop = (const char *)memchr(Stop,'\n',End - Stop);
8710a36a 560
c1a22377 561 if (Stop == 0)
0852eaef 562 return false;
81e9789b 563
75ab11ae
MV
564 for (; Stop+1 < End && Stop[1] == '\r'; Stop++)
565 /* nothing */
566 ;
c1a22377 567
f3bcc383
AL
568 // Double newline marks the end of the record
569 if (Stop+1 < End && Stop[1] == '\n')
578bfd0a 570 {
8710a36a
DK
571 if (lastTagData.EndTag != 0)
572 {
bc4ccfeb
DK
573 if (AlphaIndexes[lastTagHash] != 0)
574 lastTagData.NextInBucket = AlphaIndexes[lastTagHash];
575 APT_IGNORE_DEPRECATED(AlphaIndexes[lastTagHash] = TagCount;)
bc4ccfeb 576 d->Tags.push_back(lastTagData);
8710a36a
DK
577 }
578
bc4ccfeb 579 pkgTagSectionPrivate::TagData const td(Stop - Section);
bc4ccfeb 580 d->Tags.push_back(td);
81e9789b 581 TrimRecord(false,End);
0852eaef 582 return true;
578bfd0a
AL
583 }
584
c1a22377
AL
585 Stop++;
586 }
138d4b3d 587
0852eaef 588 return false;
578bfd0a
AL
589}
590 /*}}}*/
81e9789b
MV
591// TagSection::TrimRecord - Trim off any garbage before/after a record /*{{{*/
592// ---------------------------------------------------------------------
593/* There should be exactly 2 newline at the end of the record, no more. */
594void pkgTagSection::TrimRecord(bool BeforeRecord, const char*& End)
595{
596 if (BeforeRecord == true)
597 return;
598 for (; Stop < End && (Stop[0] == '\n' || Stop[0] == '\r'); Stop++);
599}
600 /*}}}*/
b2e465d6
AL
601// TagSection::Trim - Trim off any trailing garbage /*{{{*/
602// ---------------------------------------------------------------------
603/* There should be exactly 1 newline at the end of the buffer, no more. */
604void pkgTagSection::Trim()
605{
606 for (; Stop > Section + 2 && (Stop[-2] == '\n' || Stop[-2] == '\r'); Stop--);
607}
608 /*}}}*/
8710a36a
DK
609// TagSection::Exists - return True if a tag exists /*{{{*/
610bool pkgTagSection::Exists(const char* const Tag) const
c8b860fb
MV
611{
612 unsigned int tmp;
613 return Find(Tag, tmp);
614}
615 /*}}}*/
578bfd0a
AL
616// TagSection::Find - Locate a tag /*{{{*/
617// ---------------------------------------------------------------------
618/* This searches the section for a tag that matches the given string. */
c8b860fb 619bool pkgTagSection::Find(const char *Tag,unsigned int &Pos) const
578bfd0a 620{
8710a36a 621 size_t const Length = strlen(Tag);
bc4ccfeb 622 unsigned int Bucket = AlphaIndexes[AlphaHash(Tag, Length)];
8710a36a 623 if (Bucket == 0)
c1a22377 624 return false;
8710a36a 625
bc4ccfeb 626 for (; Bucket != 0; Bucket = d->Tags[Bucket - 1].NextInBucket)
578bfd0a 627 {
bc4ccfeb 628 if ((d->Tags[Bucket - 1].EndTag - d->Tags[Bucket - 1].StartTag) != Length)
578bfd0a
AL
629 continue;
630
bc4ccfeb 631 char const * const St = Section + d->Tags[Bucket - 1].StartTag;
8710a36a 632 if (strncasecmp(Tag,St,Length) != 0)
b2e465d6 633 continue;
8710a36a
DK
634
635 Pos = Bucket - 1;
b2e465d6
AL
636 return true;
637 }
638
639 Pos = 0;
640 return false;
641}
b2e465d6
AL
642bool pkgTagSection::Find(const char *Tag,const char *&Start,
643 const char *&End) const
644{
8710a36a
DK
645 unsigned int Pos;
646 if (Find(Tag, Pos) == false)
b2e465d6 647 return false;
578bfd0a 648
bc4ccfeb 649 Start = Section + d->Tags[Pos].StartValue;
8710a36a 650 // Strip off the gunk from the end
bc4ccfeb 651 End = Section + d->Tags[Pos + 1].StartTag;
8710a36a
DK
652 if (unlikely(Start > End))
653 return _error->Error("Internal parsing error");
654
74dedb4a 655 for (; isspace_ascii(End[-1]) != 0 && End > Start; --End);
8710a36a
DK
656
657 return true;
578bfd0a
AL
658}
659 /*}}}*/
0e66b144 660// TagSection::FindS - Find a string /*{{{*/
b2e465d6 661string pkgTagSection::FindS(const char *Tag) const
a05599f1
AL
662{
663 const char *Start;
664 const char *End;
665 if (Find(Tag,Start,End) == false)
666 return string();
667 return string(Start,End);
668}
669 /*}}}*/
8d058ea5
DK
670// TagSection::FindRawS - Find a string /*{{{*/
671string pkgTagSection::FindRawS(const char *Tag) const
672{
673 unsigned int Pos;
674 if (Find(Tag, Pos) == false)
675 return "";
676
677 char const *Start = (char const *) memchr(Section + d->Tags[Pos].EndTag, ':', d->Tags[Pos].StartValue - d->Tags[Pos].EndTag);
678 ++Start;
679 char const *End = Section + d->Tags[Pos + 1].StartTag;
680 if (unlikely(Start > End))
681 return "";
682
74dedb4a 683 for (; isspace_ascii(End[-1]) != 0 && End > Start; --End);
8d058ea5
DK
684
685 return std::string(Start, End - Start);
686}
687 /*}}}*/
a05599f1
AL
688// TagSection::FindI - Find an integer /*{{{*/
689// ---------------------------------------------------------------------
690/* */
b2e465d6 691signed int pkgTagSection::FindI(const char *Tag,signed long Default) const
a05599f1
AL
692{
693 const char *Start;
b0b4efb9
AL
694 const char *Stop;
695 if (Find(Tag,Start,Stop) == false)
696 return Default;
697
698 // Copy it into a temp buffer so we can use strtol
699 char S[300];
700 if ((unsigned)(Stop - Start) >= sizeof(S))
701 return Default;
702 strncpy(S,Start,Stop-Start);
703 S[Stop - Start] = 0;
809aa216
JAK
704
705 errno = 0;
b0b4efb9
AL
706 char *End;
707 signed long Result = strtol(S,&End,10);
137e8ad4
JAK
708 if (errno == ERANGE ||
709 Result < std::numeric_limits<int>::min() || Result > std::numeric_limits<int>::max()) {
809aa216 710 errno = ERANGE;
137e8ad4 711 _error->Error(_("Cannot convert %s to integer: out of range"), S);
809aa216 712 }
b0b4efb9
AL
713 if (S == End)
714 return Default;
715 return Result;
716}
717 /*}}}*/
e2c66de5
DK
718// TagSection::FindULL - Find an unsigned long long integer /*{{{*/
719// ---------------------------------------------------------------------
720/* */
721unsigned long long pkgTagSection::FindULL(const char *Tag, unsigned long long const &Default) const
722{
723 const char *Start;
724 const char *Stop;
725 if (Find(Tag,Start,Stop) == false)
726 return Default;
727
728 // Copy it into a temp buffer so we can use strtoull
729 char S[100];
730 if ((unsigned)(Stop - Start) >= sizeof(S))
731 return Default;
732 strncpy(S,Start,Stop-Start);
733 S[Stop - Start] = 0;
734
735 char *End;
736 unsigned long long Result = strtoull(S,&End,10);
737 if (S == End)
738 return Default;
739 return Result;
740}
741 /*}}}*/
a2fdb57f
MV
742// TagSection::FindB - Find boolean value /*{{{*/
743// ---------------------------------------------------------------------
744/* */
745bool pkgTagSection::FindB(const char *Tag, bool const &Default) const
746{
747 const char *Start, *Stop;
748 if (Find(Tag, Start, Stop) == false)
749 return Default;
750 return StringToBool(string(Start, Stop));
751}
752 /*}}}*/
b0b4efb9
AL
753// TagSection::FindFlag - Locate a yes/no type flag /*{{{*/
754// ---------------------------------------------------------------------
755/* The bits marked in Flag are masked on/off in Flags */
dfe66c72
DK
756bool pkgTagSection::FindFlag(const char * const Tag, uint8_t &Flags,
757 uint8_t const Flag) const
758{
759 const char *Start;
760 const char *Stop;
761 if (Find(Tag,Start,Stop) == false)
762 return true;
763 return FindFlag(Flags, Flag, Start, Stop);
764}
765bool pkgTagSection::FindFlag(uint8_t &Flags, uint8_t const Flag,
766 char const* const Start, char const* const Stop)
767{
768 switch (StringToBool(string(Start, Stop)))
769 {
770 case 0:
771 Flags &= ~Flag;
772 return true;
773
774 case 1:
775 Flags |= Flag;
776 return true;
777
778 default:
779 _error->Warning("Unknown flag value: %s",string(Start,Stop).c_str());
780 return true;
781 }
782 return true;
783}
b0b4efb9 784bool pkgTagSection::FindFlag(const char *Tag,unsigned long &Flags,
b2e465d6 785 unsigned long Flag) const
b0b4efb9
AL
786{
787 const char *Start;
788 const char *Stop;
789 if (Find(Tag,Start,Stop) == false)
790 return true;
fe0f7911
DK
791 return FindFlag(Flags, Flag, Start, Stop);
792}
d64e130a 793bool pkgTagSection::FindFlag(unsigned long &Flags, unsigned long Flag,
fe0f7911
DK
794 char const* Start, char const* Stop)
795{
796 switch (StringToBool(string(Start, Stop)))
797 {
b0b4efb9
AL
798 case 0:
799 Flags &= ~Flag;
800 return true;
801
802 case 1:
803 Flags |= Flag;
804 return true;
805
806 default:
b2e465d6 807 _error->Warning("Unknown flag value: %s",string(Start,Stop).c_str());
b0b4efb9
AL
808 return true;
809 }
810 return true;
a05599f1
AL
811}
812 /*}}}*/
55153bf9 813void pkgTagSection::Get(const char *&Start,const char *&Stop,unsigned int I) const/*{{{*/
bc4ccfeb
DK
814{
815 Start = Section + d->Tags[I].StartTag;
816 Stop = Section + d->Tags[I+1].StartTag;
817}
55153bf9 818 /*}}}*/
8710a36a 819APT_PURE unsigned int pkgTagSection::Count() const { /*{{{*/
bc4ccfeb 820 if (d->Tags.empty() == true)
8710a36a
DK
821 return 0;
822 // the last element is just marking the end and isn't a real one
bc4ccfeb 823 return d->Tags.size() - 1;
8710a36a
DK
824}
825 /*}}}*/
8d058ea5
DK
826// TagSection::Write - Ordered (re)writing of fields /*{{{*/
827pkgTagSection::Tag pkgTagSection::Tag::Remove(std::string const &Name)
828{
829 return Tag(REMOVE, Name, "");
830}
831pkgTagSection::Tag pkgTagSection::Tag::Rename(std::string const &OldName, std::string const &NewName)
832{
833 return Tag(RENAME, OldName, NewName);
834}
835pkgTagSection::Tag pkgTagSection::Tag::Rewrite(std::string const &Name, std::string const &Data)
836{
837 if (Data.empty() == true)
838 return Tag(REMOVE, Name, "");
839 else
840 return Tag(REWRITE, Name, Data);
841}
842static bool WriteTag(FileFd &File, std::string Tag, std::string const &Value)
843{
74dedb4a 844 if (Value.empty() || isspace_ascii(Value[0]) != 0)
8d058ea5
DK
845 Tag.append(":");
846 else
847 Tag.append(": ");
848 Tag.append(Value);
849 Tag.append("\n");
850 return File.Write(Tag.c_str(), Tag.length());
851}
852static bool RewriteTags(FileFd &File, pkgTagSection const * const This, char const * const Tag,
853 std::vector<pkgTagSection::Tag>::const_iterator &R,
854 std::vector<pkgTagSection::Tag>::const_iterator const &REnd)
855{
856 size_t const TagLen = strlen(Tag);
857 for (; R != REnd; ++R)
858 {
859 std::string data;
860 if (R->Name.length() == TagLen && strncasecmp(R->Name.c_str(), Tag, R->Name.length()) == 0)
861 {
862 if (R->Action != pkgTagSection::Tag::REWRITE)
863 break;
864 data = R->Data;
865 }
866 else if(R->Action == pkgTagSection::Tag::RENAME && R->Data.length() == TagLen &&
867 strncasecmp(R->Data.c_str(), Tag, R->Data.length()) == 0)
868 data = This->FindRawS(R->Name.c_str());
869 else
870 continue;
871
872 return WriteTag(File, Tag, data);
873 }
874 return true;
875}
876bool pkgTagSection::Write(FileFd &File, char const * const * const Order, std::vector<Tag> const &Rewrite) const
877{
878 // first pass: Write everything we have an order for
879 if (Order != NULL)
880 {
881 for (unsigned int I = 0; Order[I] != 0; ++I)
882 {
883 std::vector<Tag>::const_iterator R = Rewrite.begin();
884 if (RewriteTags(File, this, Order[I], R, Rewrite.end()) == false)
885 return false;
886 if (R != Rewrite.end())
887 continue;
888
889 if (Exists(Order[I]) == false)
890 continue;
891
892 if (WriteTag(File, Order[I], FindRawS(Order[I])) == false)
893 return false;
894 }
895 }
896 // second pass: See if we have tags which aren't ordered
897 if (d->Tags.empty() == false)
898 {
899 for (std::vector<pkgTagSectionPrivate::TagData>::const_iterator T = d->Tags.begin(); T != d->Tags.end() - 1; ++T)
900 {
901 char const * const fieldname = Section + T->StartTag;
902 size_t fieldnamelen = T->EndTag - T->StartTag;
903 if (Order != NULL)
904 {
905 unsigned int I = 0;
906 for (; Order[I] != 0; ++I)
907 {
908 if (fieldnamelen == strlen(Order[I]) && strncasecmp(fieldname, Order[I], fieldnamelen) == 0)
909 break;
910 }
911 if (Order[I] != 0)
912 continue;
913 }
914
915 std::string const name(fieldname, fieldnamelen);
916 std::vector<Tag>::const_iterator R = Rewrite.begin();
917 if (RewriteTags(File, this, name.c_str(), R, Rewrite.end()) == false)
918 return false;
919 if (R != Rewrite.end())
920 continue;
921
922 if (WriteTag(File, name, FindRawS(name.c_str())) == false)
923 return false;
924 }
925 }
926 // last pass: see if there are any rewrites remaining we haven't done yet
927 for (std::vector<Tag>::const_iterator R = Rewrite.begin(); R != Rewrite.end(); ++R)
928 {
929 if (R->Action == Tag::REMOVE)
930 continue;
931 std::string const name = ((R->Action == Tag::RENAME) ? R->Data : R->Name);
932 if (Exists(name.c_str()))
933 continue;
934 if (Order != NULL)
935 {
936 unsigned int I = 0;
937 for (; Order[I] != 0; ++I)
938 {
939 if (strncasecmp(name.c_str(), Order[I], name.length()) == 0 && name.length() == strlen(Order[I]))
940 break;
941 }
942 if (Order[I] != 0)
943 continue;
944 }
945
946 if (WriteTag(File, name, ((R->Action == Tag::RENAME) ? FindRawS(R->Name.c_str()) : R->Data)) == false)
947 return false;
948 }
949 return true;
950}
951 /*}}}*/
e8fb1cdf 952
81460e32
DK
953void pkgUserTagSection::TrimRecord(bool /*BeforeRecord*/, const char* &End)/*{{{*/
954{
955 for (; Stop < End && (Stop[0] == '\n' || Stop[0] == '\r' || Stop[0] == '#'); Stop++)
956 if (Stop[0] == '#')
957 Stop = (const char*) memchr(Stop,'\n',End-Stop);
958}
959 /*}}}*/
960
e8fb1cdf
DK
961#include "tagfile-order.c"
962
b2e465d6
AL
963// TFRewrite - Rewrite a control record /*{{{*/
964// ---------------------------------------------------------------------
965/* This writes the control record to stdout rewriting it as necessary. The
966 override map item specificies the rewriting rules to follow. This also
967 takes the time to sort the feild list. */
8d058ea5 968APT_IGNORE_DEPRECATED_PUSH
b2e465d6
AL
969bool TFRewrite(FILE *Output,pkgTagSection const &Tags,const char *Order[],
970 TFRewriteData *Rewrite)
971{
972 unsigned char Visited[256]; // Bit 1 is Order, Bit 2 is Rewrite
973 for (unsigned I = 0; I != 256; I++)
974 Visited[I] = 0;
975
976 // Set new tag up as necessary.
977 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
978 {
979 if (Rewrite[J].NewTag == 0)
980 Rewrite[J].NewTag = Rewrite[J].Tag;
981 }
982
983 // Write all all of the tags, in order.
9e51c0b6 984 if (Order != NULL)
b2e465d6 985 {
9e51c0b6 986 for (unsigned int I = 0; Order[I] != 0; I++)
b2e465d6 987 {
9e51c0b6
MV
988 bool Rewritten = false;
989
990 // See if this is a field that needs to be rewritten
991 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
992 {
993 if (strcasecmp(Rewrite[J].Tag,Order[I]) == 0)
994 {
995 Visited[J] |= 2;
996 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
997 {
74dedb4a 998 if (isspace_ascii(Rewrite[J].Rewrite[0]))
9e51c0b6
MV
999 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
1000 else
1001 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
1002 }
1003 Rewritten = true;
1004 break;
1005 }
1006 }
b2e465d6 1007
9e51c0b6
MV
1008 // See if it is in the fragment
1009 unsigned Pos;
1010 if (Tags.Find(Order[I],Pos) == false)
1011 continue;
1012 Visited[Pos] |= 1;
1013
1014 if (Rewritten == true)
1015 continue;
b2e465d6 1016
9e51c0b6
MV
1017 /* Write out this element, taking a moment to rewrite the tag
1018 in case of changes of case. */
1019 const char *Start;
1020 const char *Stop;
1021 Tags.Get(Start,Stop,Pos);
b2e465d6 1022
9e51c0b6
MV
1023 if (fputs(Order[I],Output) < 0)
1024 return _error->Errno("fputs","IO Error to output");
1025 Start += strlen(Order[I]);
1026 if (fwrite(Start,Stop - Start,1,Output) != 1)
1027 return _error->Errno("fwrite","IO Error to output");
1028 if (Stop[-1] != '\n')
1029 fprintf(Output,"\n");
1030 }
1031 }
b2e465d6
AL
1032
1033 // Now write all the old tags that were missed.
1034 for (unsigned int I = 0; I != Tags.Count(); I++)
1035 {
1036 if ((Visited[I] & 1) == 1)
1037 continue;
1038
1039 const char *Start;
1040 const char *Stop;
1041 Tags.Get(Start,Stop,I);
1042 const char *End = Start;
1043 for (; End < Stop && *End != ':'; End++);
1044
1045 // See if this is a field that needs to be rewritten
1046 bool Rewritten = false;
1047 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
1048 {
1049 if (stringcasecmp(Start,End,Rewrite[J].Tag) == 0)
1050 {
1051 Visited[J] |= 2;
1052 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
1053 {
74dedb4a 1054 if (isspace_ascii(Rewrite[J].Rewrite[0]))
b2e465d6
AL
1055 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
1056 else
1057 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
1058 }
1059
1060 Rewritten = true;
1061 break;
1062 }
1063 }
1064
1065 if (Rewritten == true)
1066 continue;
1067
1068 // Write out this element
1069 if (fwrite(Start,Stop - Start,1,Output) != 1)
1070 return _error->Errno("fwrite","IO Error to output");
1071 if (Stop[-1] != '\n')
1072 fprintf(Output,"\n");
1073 }
1074
1075 // Now write all the rewrites that were missed
1076 for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
1077 {
1078 if ((Visited[J] & 2) == 2)
1079 continue;
1080
1081 if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
1082 {
74dedb4a 1083 if (isspace_ascii(Rewrite[J].Rewrite[0]))
b2e465d6
AL
1084 fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
1085 else
1086 fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
1087 }
1088 }
1089
1090 return true;
1091}
8d058ea5 1092APT_IGNORE_DEPRECATED_POP
b2e465d6 1093 /*}}}*/
862bafea 1094
bc4ccfeb 1095pkgTagSection::~pkgTagSection() { delete d; }