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