1 // -*- mode: cpp; mode: fold -*- 
   3 // $Id: tagfile.cc,v 1.37.2.2 2003/12/31 16:02:30 mdz Exp $ 
   4 /* ###################################################################### 
   6    Fast scanner for RFC-822 type header information 
   8    This uses a rotating buffer to load the package information into. 
   9    The scanner runs over it and isolates and indexes a single section. 
  11    ##################################################################### */ 
  13 // Include Files                                                        /*{{{*/ 
  16 #include <apt-pkg/tagfile.h> 
  17 #include <apt-pkg/error.h> 
  18 #include <apt-pkg/strutl.h> 
  19 #include <apt-pkg/fileutl.h> 
  34 class APT_HIDDEN pkgTagFilePrivate                                      
/*{{{*/ 
  37    void Reset(FileFd 
* const pFd
, unsigned long long const pSize
, pkgTagFile::Flags 
const pFlags
) 
  49       isCommentedLine 
= false; 
  53    pkgTagFilePrivate(FileFd 
* const pFd
, unsigned long long const Size
, pkgTagFile::Flags 
const pFlags
) : Buffer(NULL
) 
  55       Reset(pFd
, Size
, pFlags
); 
  58    pkgTagFile::Flags Flags
; 
  63    unsigned long long iOffset
; 
  64    unsigned long long Size
; 
  70       FileChunk(bool const pgood
, size_t const plength
) : good(pgood
), length(plength
) {} 
  72    std::list
<FileChunk
> chunks
; 
  81 class APT_HIDDEN pkgTagSectionPrivate                                   
/*{{{*/ 
  84    pkgTagSectionPrivate() 
  88       unsigned int StartTag
; 
  90       unsigned int StartValue
; 
  91       unsigned int NextInBucket
; 
  93       explicit TagData(unsigned int const StartTag
) : StartTag(StartTag
), EndTag(0), StartValue(0), NextInBucket(0) {} 
  95    std::vector
<TagData
> Tags
; 
  99 static unsigned long AlphaHash(const char *Text
, size_t Length
)         /*{{{*/ 
 101    /* This very simple hash function for the last 8 letters gives 
 102       very good performance on the debian package files */ 
 105     Text 
+= (Length 
- 8); 
 108    unsigned long Res 
= 0; 
 109    for (size_t i 
= 0; i 
< Length
; ++i
) 
 110       Res 
= ((unsigned long)(Text
[i
]) & 0xDF) ^ (Res 
<< 1); 
 115 // TagFile::pkgTagFile - Constructor                                    /*{{{*/ 
 116 pkgTagFile::pkgTagFile(FileFd 
* const pFd
,pkgTagFile::Flags 
const pFlags
, unsigned long long const Size
) 
 117    : d(new pkgTagFilePrivate(pFd
, Size 
+ 4, pFlags
)) 
 119    Init(pFd
, pFlags
, Size
); 
 121 pkgTagFile::pkgTagFile(FileFd 
* const pFd
,unsigned long long const Size
) 
 122    : pkgTagFile(pFd
, pkgTagFile::STRICT
, Size
) 
 125 void pkgTagFile::Init(FileFd 
* const pFd
, pkgTagFile::Flags 
const pFlags
, unsigned long long Size
) 
 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 */ 
 132    d
->Reset(pFd
, Size
, pFlags
); 
 134    if (d
->Fd
->IsOpen() == false) 
 135       d
->Start 
= d
->End 
= d
->Buffer 
= 0; 
 137       d
->Buffer 
= (char*)malloc(sizeof(char) * Size
); 
 139    if (d
->Buffer 
== NULL
) 
 144    d
->Start 
= d
->End 
= d
->Buffer
; 
 146    if (d
->Done 
== false) 
 149 void pkgTagFile::Init(FileFd 
* const pFd
,unsigned long long Size
) 
 151    Init(pFd
, pkgTagFile::STRICT
, Size
); 
 154 // TagFile::~pkgTagFile - Destructor                                    /*{{{*/ 
 155 pkgTagFile::~pkgTagFile() 
 160 // TagFile::Offset - Return the current offset in the buffer            /*{{{*/ 
 161 APT_PURE 
unsigned long pkgTagFile::Offset() 
 166 // TagFile::Resize - Resize the internal buffer                         /*{{{*/ 
 167 // --------------------------------------------------------------------- 
 168 /* Resize the internal buffer (double it in size). Fail if a maximum size 
 171 bool pkgTagFile::Resize() 
 173    // fail is the buffer grows too big 
 174    if(d
->Size 
> 1024*1024+1) 
 177    return Resize(d
->Size 
* 2); 
 179 bool pkgTagFile::Resize(unsigned long long const newSize
) 
 181    unsigned long long const EndSize 
= d
->End 
- d
->Start
; 
 183    // get new buffer and use it 
 184    char* const newBuffer 
= static_cast<char*>(realloc(d
->Buffer
, sizeof(char) * newSize
)); 
 185    if (newBuffer 
== NULL
) 
 187    d
->Buffer 
= newBuffer
; 
 190    // update the start/end pointers to the new buffer 
 191    d
->Start 
= d
->Buffer
; 
 192    d
->End 
= d
->Start 
+ EndSize
; 
 196 // TagFile::Step - Advance to the next section                          /*{{{*/ 
 197 // --------------------------------------------------------------------- 
 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. 
 202 bool pkgTagFile::Step(pkgTagSection 
&Tag
) 
 204    if(Tag
.Scan(d
->Start
,d
->End 
- d
->Start
) == false) 
 211          if(Tag
.Scan(d
->Start
,d
->End 
- d
->Start
, false)) 
 214          if (Resize() == false) 
 215             return _error
->Error(_("Unable to parse package file %s (%d)"), 
 216                   d
->Fd
->Name().c_str(), 1); 
 218       } while (Tag
.Scan(d
->Start
,d
->End 
- d
->Start
, false) == false); 
 221    size_t tagSize 
= Tag
.size(); 
 224    if ((d
->Flags 
& pkgTagFile::SUPPORT_COMMENTS
) == 0) 
 225       d
->iOffset 
+= tagSize
; 
 228       auto first 
= d
->chunks
.begin(); 
 229       for (; first 
!= d
->chunks
.end(); ++first
) 
 231          if (first
->good 
== false) 
 232             d
->iOffset 
+= first
->length
; 
 235             if (tagSize 
< first
->length
) 
 237                first
->length 
-= tagSize
; 
 238                d
->iOffset 
+= tagSize
; 
 243                tagSize 
-= first
->length
; 
 244                d
->iOffset 
+= first
->length
; 
 248       d
->chunks
.erase(d
->chunks
.begin(), first
); 
 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 */ 
 259 static bool FillBuffer(pkgTagFilePrivate 
* const d
) 
 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) 
 266    if (Actual 
!= dataSize
) 
 271 static void RemoveCommentsFromBuffer(pkgTagFilePrivate 
* const d
) 
 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) 
 278       if (d
->Start 
== d
->Buffer
) 
 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 
== '#') 
 284             d
->isCommentedLine 
= true; 
 286             if (current 
> d
->End
) 
 287                d
->chunks
.emplace_back(false, 1); 
 290       if (d
->isCommentedLine 
== false) 
 291          good_start 
= d
->Start
; 
 293          bad_start 
= d
->Start
; 
 296       bad_start 
= d
->Start
; 
 298    std::vector
<std::pair
<char*, size_t>> good_parts
; 
 299    while (current 
<= d
->End
) 
 301       size_t const restLength 
= (d
->End 
- current
) + 1; 
 302       if (d
->isCommentedLine 
== false) 
 304          current 
= static_cast<char*>(memchr(current
, '#', restLength
)); 
 305          if (current 
== nullptr) 
 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
)); 
 315          // ensure that this is really a comment and not a '#' in the middle of a line 
 316          if (*current 
== '\n') 
 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; 
 326       else // the current line is a comment 
 328          current 
= static_cast<char*>(memchr(current
, '\n', restLength
)); 
 329          if (current 
== nullptr) 
 331             d
->chunks
.emplace_back(false, (d
->End 
- bad_start
)); 
 335          // is the next line a comment, too? 
 336          if (current 
> d
->End 
|| *current 
!= '#') 
 338             d
->chunks
.emplace_back(false, (current 
- bad_start
)); 
 339             good_start 
= current
; 
 341             d
->isCommentedLine 
= false; 
 347    if (good_parts
.empty() == false) 
 349       // we found comments, so move later parts over them 
 351       for (auto const &good
: good_parts
) 
 353          memmove(current
, good
.first
, good
.second
); 
 354          current 
+= good
.second
; 
 359    if (d
->isCommentedLine 
== true) 
 361       // deal with a buffer containing only comments 
 362       // or an (unfinished) comment at the end 
 363       if (good_parts
.empty() == true) 
 370       // the buffer was all comment, but ended with the buffer 
 371       if (good_parts
.empty() == true && good_start 
>= d
->End
) 
 377 bool pkgTagFile::Fill() 
 379    unsigned long long const EndSize 
= d
->End 
- d
->Start
; 
 382       memmove(d
->Buffer
,d
->Start
,EndSize
); 
 383       d
->Start 
= d
->End 
= d
->Buffer 
+ EndSize
; 
 386       d
->Start 
= d
->End 
= d
->Buffer
; 
 388    unsigned long long Actual 
= 0; 
 389    while (d
->Done 
== false && d
->Size 
> (Actual 
+ 1)) 
 391       if (FillBuffer(d
) == false) 
 393       if ((d
->Flags 
& pkgTagFile::SUPPORT_COMMENTS
) != 0) 
 394          RemoveCommentsFromBuffer(d
); 
 395       Actual 
= d
->End 
- d
->Buffer
; 
 397    d
->Start 
= d
->Buffer
; 
 401       if (EndSize 
<= 3 && Actual 
== 0) 
 403       if (d
->Size 
- (d
->End 
- d
->Buffer
) < 4) 
 406       // Append a double new line if one does not exist 
 407       unsigned int LineCount 
= 0; 
 408       for (const char *E 
= d
->End 
- 1; E 
- d
->End 
< 6 && (*E 
== '\n' || *E 
== '\r'); E
--) 
 413          if (static_cast<unsigned long long>(d
->End 
- d
->Buffer
) >= d
->Size
) 
 415          for (; LineCount 
< 2; ++LineCount
) 
 422 // TagFile::Jump - Jump to a pre-recorded location in the file          /*{{{*/ 
 423 // --------------------------------------------------------------------- 
 424 /* This jumps to a pre-recorded file location and reads the record 
 426 bool pkgTagFile::Jump(pkgTagSection 
&Tag
,unsigned long long Offset
) 
 428    if ((d
->Flags 
& pkgTagFile::SUPPORT_COMMENTS
) == 0 && 
 429    // We are within a buffer space of the next hit.. 
 430          Offset 
>= d
->iOffset 
&& d
->iOffset 
+ (d
->End 
- d
->Start
) > Offset
) 
 432       unsigned long long Dist 
= Offset 
- d
->iOffset
; 
 435       // if we have seen the end, don't ask for more 
 437          return Tag
.Scan(d
->Start
, d
->End 
- d
->Start
); 
 442    // Reposition and reload.. 
 445    if (d
->Fd
->Seek(Offset
) == false) 
 447    d
->End 
= d
->Start 
= d
->Buffer
; 
 448    d
->isCommentedLine 
= false; 
 454    if (Tag
.Scan(d
->Start
, d
->End 
- d
->Start
) == true) 
 457    // This appends a double new line (for the real eof handling) 
 461    if (Tag
.Scan(d
->Start
, d
->End 
- d
->Start
, false) == false) 
 462       return _error
->Error(_("Unable to parse package file %s (%d)"),d
->Fd
->Name().c_str(), 2); 
 467 // pkgTagSection::pkgTagSection - Constructor                           /*{{{*/ 
 468 // --------------------------------------------------------------------- 
 470 APT_IGNORE_DEPRECATED_PUSH
 
 471 pkgTagSection::pkgTagSection() 
 472    : Section(0), d(new pkgTagSectionPrivate()), Stop(0) 
 474    memset(&AlphaIndexes
, 0, sizeof(AlphaIndexes
)); 
 476 APT_IGNORE_DEPRECATED_POP
 
 478 // TagSection::Scan - Scan for the end of the header information        /*{{{*/ 
 479 bool pkgTagSection::Scan(const char *Start
,unsigned long MaxLength
, bool const Restart
) 
 482    const char *End 
= Start 
+ MaxLength
; 
 484    if (Restart 
== false && d
->Tags
.empty() == false) 
 486       Stop 
= Section 
+ d
->Tags
.back().StartTag
; 
 489       Stop 
= (const char *)memchr(Stop
,'\n',End 
- Stop
); 
 497       if (d
->Tags
.empty() == false) 
 499          memset(&AlphaIndexes
, 0, sizeof(AlphaIndexes
)); 
 502       d
->Tags
.reserve(0x100); 
 504    unsigned int TagCount 
= d
->Tags
.size(); 
 509    pkgTagSectionPrivate::TagData 
lastTagData(0); 
 510    lastTagData
.EndTag 
= 0; 
 511    unsigned long lastTagHash 
= 0; 
 514       TrimRecord(true,End
); 
 516       // this can happen when TrimRecord trims away the entire Record 
 517       // (e.g. because it just contains comments) 
 521       // Start a new index and add it to the hash 
 522       if (isspace_ascii(Stop
[0]) == 0) 
 524          // store the last found tag 
 525          if (lastTagData
.EndTag 
!= 0) 
 527             if (AlphaIndexes
[lastTagHash
] != 0) 
 528                lastTagData
.NextInBucket 
= AlphaIndexes
[lastTagHash
]; 
 529             APT_IGNORE_DEPRECATED_PUSH
 
 530             AlphaIndexes
[lastTagHash
] = TagCount
; 
 531             APT_IGNORE_DEPRECATED_POP
 
 532             d
->Tags
.push_back(lastTagData
); 
 535          APT_IGNORE_DEPRECATED(++TagCount
;) 
 536          lastTagData 
= pkgTagSectionPrivate::TagData(Stop 
- Section
); 
 537          // find the colon separating tag and value 
 538          char const * Colon 
= (char const *) memchr(Stop
, ':', End 
- Stop
); 
 541          // find the end of the tag (which might or might not be the colon) 
 542          char const * EndTag 
= Colon
; 
 544          for (; EndTag 
> Stop 
&& isspace_ascii(*EndTag
) != 0; --EndTag
) 
 547          lastTagData
.EndTag 
= EndTag 
- Section
; 
 548          lastTagHash 
= AlphaHash(Stop
, EndTag 
- Stop
); 
 549          // find the beginning of the value 
 551          for (; Stop 
< End 
&& isspace_ascii(*Stop
) != 0; ++Stop
) 
 552             if (*Stop 
== '\n' && Stop
[1] != ' ') 
 556          lastTagData
.StartValue 
= Stop 
- Section
; 
 559       Stop 
= (const char *)memchr(Stop
,'\n',End 
- Stop
); 
 564       for (; Stop
+1 < End 
&& Stop
[1] == '\r'; Stop
++) 
 568       // Double newline marks the end of the record 
 569       if (Stop
+1 < End 
&& Stop
[1] == '\n') 
 571          if (lastTagData
.EndTag 
!= 0) 
 573             if (AlphaIndexes
[lastTagHash
] != 0) 
 574                lastTagData
.NextInBucket 
= AlphaIndexes
[lastTagHash
]; 
 575             APT_IGNORE_DEPRECATED(AlphaIndexes
[lastTagHash
] = TagCount
;) 
 576             d
->Tags
.push_back(lastTagData
); 
 579          pkgTagSectionPrivate::TagData 
const td(Stop 
- Section
); 
 580          d
->Tags
.push_back(td
); 
 581          TrimRecord(false,End
); 
 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. */ 
 594 void pkgTagSection::TrimRecord(bool BeforeRecord
, const char*& End
) 
 596    if (BeforeRecord 
== true) 
 598    for (; Stop 
< End 
&& (Stop
[0] == '\n' || Stop
[0] == '\r'); Stop
++); 
 601 // TagSection::Trim - Trim off any trailing garbage                     /*{{{*/ 
 602 // --------------------------------------------------------------------- 
 603 /* There should be exactly 1 newline at the end of the buffer, no more. */ 
 604 void pkgTagSection::Trim() 
 606    for (; Stop 
> Section 
+ 2 && (Stop
[-2] == '\n' || Stop
[-2] == '\r'); Stop
--); 
 609 // TagSection::Exists - return True if a tag exists                     /*{{{*/ 
 610 bool pkgTagSection::Exists(const char* const Tag
) const 
 613    return Find(Tag
, tmp
); 
 616 // TagSection::Find - Locate a tag                                      /*{{{*/ 
 617 // --------------------------------------------------------------------- 
 618 /* This searches the section for a tag that matches the given string. */ 
 619 bool pkgTagSection::Find(const char *Tag
,unsigned int &Pos
) const 
 621    size_t const Length 
= strlen(Tag
); 
 622    unsigned int Bucket 
= AlphaIndexes
[AlphaHash(Tag
, Length
)]; 
 626    for (; Bucket 
!= 0; Bucket 
= d
->Tags
[Bucket 
- 1].NextInBucket
) 
 628       if ((d
->Tags
[Bucket 
- 1].EndTag 
- d
->Tags
[Bucket 
- 1].StartTag
) != Length
) 
 631       char const * const St 
= Section 
+ d
->Tags
[Bucket 
- 1].StartTag
; 
 632       if (strncasecmp(Tag
,St
,Length
) != 0) 
 642 bool pkgTagSection::Find(const char *Tag
,const char *&Start
, 
 643                          const char *&End
) const 
 646    if (Find(Tag
, Pos
) == false) 
 649    Start 
= Section 
+ d
->Tags
[Pos
].StartValue
; 
 650    // Strip off the gunk from the end 
 651    End 
= Section 
+ d
->Tags
[Pos 
+ 1].StartTag
; 
 652    if (unlikely(Start 
> End
)) 
 653       return _error
->Error("Internal parsing error"); 
 655    for (; isspace_ascii(End
[-1]) != 0 && End 
> Start
; --End
); 
 660 // TagSection::FindS - Find a string                                    /*{{{*/ 
 661 string 
pkgTagSection::FindS(const char *Tag
) const 
 665    if (Find(Tag
,Start
,End
) == false) 
 667    return string(Start
,End
);       
 670 // TagSection::FindRawS - Find a string                                 /*{{{*/ 
 671 string 
pkgTagSection::FindRawS(const char *Tag
) const 
 674    if (Find(Tag
, Pos
) == false) 
 677    char const *Start 
= (char const *) memchr(Section 
+ d
->Tags
[Pos
].EndTag
, ':', d
->Tags
[Pos
].StartValue 
- d
->Tags
[Pos
].EndTag
); 
 679    char const *End 
= Section 
+ d
->Tags
[Pos 
+ 1].StartTag
; 
 680    if (unlikely(Start 
> End
)) 
 683    for (; isspace_ascii(End
[-1]) != 0 && End 
> Start
; --End
); 
 685    return std::string(Start
, End 
- Start
); 
 688 // TagSection::FindI - Find an integer                                  /*{{{*/ 
 689 // --------------------------------------------------------------------- 
 691 signed int pkgTagSection::FindI(const char *Tag
,signed long Default
) const 
 695    if (Find(Tag
,Start
,Stop
) == false) 
 698    // Copy it into a temp buffer so we can use strtol 
 700    if ((unsigned)(Stop 
- Start
) >= sizeof(S
)) 
 702    strncpy(S
,Start
,Stop
-Start
); 
 707    signed long Result 
= strtol(S
,&End
,10); 
 708    if (errno 
== ERANGE 
|| 
 709        Result 
< std::numeric_limits
<int>::min() || Result 
> std::numeric_limits
<int>::max()) { 
 711       _error
->Error(_("Cannot convert %s to integer: out of range"), S
); 
 718 // TagSection::FindULL - Find an unsigned long long integer             /*{{{*/ 
 719 // --------------------------------------------------------------------- 
 721 unsigned long long pkgTagSection::FindULL(const char *Tag
, unsigned long long const &Default
) const 
 725    if (Find(Tag
,Start
,Stop
) == false) 
 728    // Copy it into a temp buffer so we can use strtoull 
 730    if ((unsigned)(Stop 
- Start
) >= sizeof(S
)) 
 732    strncpy(S
,Start
,Stop
-Start
); 
 736    unsigned long long Result 
= strtoull(S
,&End
,10); 
 742 // TagSection::FindB - Find boolean value                               /*{{{*/ 
 743 // --------------------------------------------------------------------- 
 745 bool pkgTagSection::FindB(const char *Tag
, bool const &Default
) const 
 747    const char *Start
, *Stop
; 
 748    if (Find(Tag
, Start
, Stop
) == false) 
 750    return StringToBool(string(Start
, Stop
)); 
 753 // TagSection::FindFlag - Locate a yes/no type flag                     /*{{{*/ 
 754 // --------------------------------------------------------------------- 
 755 /* The bits marked in Flag are masked on/off in Flags */ 
 756 bool pkgTagSection::FindFlag(const char * const Tag
, uint8_t &Flags
, 
 757                              uint8_t const Flag
) const 
 761    if (Find(Tag
,Start
,Stop
) == false) 
 763    return FindFlag(Flags
, Flag
, Start
, Stop
); 
 765 bool pkgTagSection::FindFlag(uint8_t &Flags
, uint8_t const Flag
, 
 766                                         char const* const Start
, char const* const Stop
) 
 768    switch (StringToBool(string(Start
, Stop
))) 
 779       _error
->Warning("Unknown flag value: %s",string(Start
,Stop
).c_str()); 
 784 bool pkgTagSection::FindFlag(const char *Tag
,unsigned long &Flags
, 
 785                              unsigned long Flag
) const 
 789    if (Find(Tag
,Start
,Stop
) == false) 
 791    return FindFlag(Flags
, Flag
, Start
, Stop
); 
 793 bool pkgTagSection::FindFlag(unsigned long &Flags
, unsigned long Flag
, 
 794                                         char const* Start
, char const* Stop
) 
 796    switch (StringToBool(string(Start
, Stop
))) 
 807       _error
->Warning("Unknown flag value: %s",string(Start
,Stop
).c_str()); 
 813 void pkgTagSection::Get(const char *&Start
,const char *&Stop
,unsigned int I
) const/*{{{*/ 
 815    Start 
= Section 
+ d
->Tags
[I
].StartTag
; 
 816    Stop 
= Section 
+ d
->Tags
[I
+1].StartTag
; 
 819 APT_PURE 
unsigned int pkgTagSection::Count() const {                    /*{{{*/ 
 820    if (d
->Tags
.empty() == true) 
 822    // the last element is just marking the end and isn't a real one 
 823    return d
->Tags
.size() - 1; 
 826 // TagSection::Write - Ordered (re)writing of fields                    /*{{{*/ 
 827 pkgTagSection::Tag 
pkgTagSection::Tag::Remove(std::string 
const &Name
) 
 829    return Tag(REMOVE
, Name
, ""); 
 831 pkgTagSection::Tag 
pkgTagSection::Tag::Rename(std::string 
const &OldName
, std::string 
const &NewName
) 
 833    return Tag(RENAME
, OldName
, NewName
); 
 835 pkgTagSection::Tag 
pkgTagSection::Tag::Rewrite(std::string 
const &Name
, std::string 
const &Data
) 
 837    if (Data
.empty() == true) 
 838       return Tag(REMOVE
, Name
, ""); 
 840       return Tag(REWRITE
, Name
, Data
); 
 842 static bool WriteTag(FileFd 
&File
, std::string Tag
, std::string 
const &Value
) 
 844    if (Value
.empty() || isspace_ascii(Value
[0]) != 0) 
 850    return File
.Write(Tag
.c_str(), Tag
.length()); 
 852 static 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
) 
 856    size_t const TagLen 
= strlen(Tag
); 
 857    for (; R 
!= REnd
; ++R
) 
 860       if (R
->Name
.length() == TagLen 
&& strncasecmp(R
->Name
.c_str(), Tag
, R
->Name
.length()) == 0) 
 862          if (R
->Action 
!= pkgTagSection::Tag::REWRITE
) 
 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()); 
 872       return WriteTag(File
, Tag
, data
); 
 876 bool pkgTagSection::Write(FileFd 
&File
, char const * const * const Order
, std::vector
<Tag
> const &Rewrite
) const 
 878    // first pass: Write everything we have an order for 
 881       for (unsigned int I 
= 0; Order
[I
] != 0; ++I
) 
 883          std::vector
<Tag
>::const_iterator R 
= Rewrite
.begin(); 
 884          if (RewriteTags(File
, this, Order
[I
], R
, Rewrite
.end()) == false) 
 886          if (R 
!= Rewrite
.end()) 
 889          if (Exists(Order
[I
]) == false) 
 892          if (WriteTag(File
, Order
[I
], FindRawS(Order
[I
])) == false) 
 896    // second pass: See if we have tags which aren't ordered 
 897    if (d
->Tags
.empty() == false) 
 899       for (std::vector
<pkgTagSectionPrivate::TagData
>::const_iterator T 
= d
->Tags
.begin(); T 
!= d
->Tags
.end() - 1; ++T
) 
 901          char const * const fieldname 
= Section 
+ T
->StartTag
; 
 902          size_t fieldnamelen 
= T
->EndTag 
- T
->StartTag
; 
 906             for (; Order
[I
] != 0; ++I
) 
 908                if (fieldnamelen 
== strlen(Order
[I
]) && strncasecmp(fieldname
, Order
[I
], fieldnamelen
) == 0) 
 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) 
 919          if (R 
!= Rewrite
.end()) 
 922          if (WriteTag(File
, name
, FindRawS(name
.c_str())) == false) 
 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
) 
 929       if (R
->Action 
== Tag::REMOVE
) 
 931       std::string 
const name 
= ((R
->Action 
== Tag::RENAME
) ? R
->Data 
: R
->Name
); 
 932       if (Exists(name
.c_str())) 
 937          for (; Order
[I
] != 0; ++I
) 
 939             if (strncasecmp(name
.c_str(), Order
[I
], name
.length()) == 0 && name
.length() == strlen(Order
[I
])) 
 946       if (WriteTag(File
, name
, ((R
->Action 
== Tag::RENAME
) ? FindRawS(R
->Name
.c_str()) : R
->Data
)) == false) 
 953 void pkgUserTagSection::TrimRecord(bool /*BeforeRecord*/, const char* &End
)/*{{{*/ 
 955    for (; Stop 
< End 
&& (Stop
[0] == '\n' || Stop
[0] == '\r' || Stop
[0] == '#'); Stop
++) 
 957          Stop 
= (const char*) memchr(Stop
,'\n',End
-Stop
); 
 961 #include "tagfile-order.c" 
 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. */ 
 968 APT_IGNORE_DEPRECATED_PUSH
 
 969 bool TFRewrite(FILE *Output
,pkgTagSection 
const &Tags
,const char *Order
[], 
 970                TFRewriteData 
*Rewrite
) 
 972    unsigned char Visited
[256];   // Bit 1 is Order, Bit 2 is Rewrite 
 973    for (unsigned I 
= 0; I 
!= 256; I
++) 
 976    // Set new tag up as necessary. 
 977    for (unsigned int J 
= 0; Rewrite 
!= 0 && Rewrite
[J
].Tag 
!= 0; J
++) 
 979       if (Rewrite
[J
].NewTag 
== 0) 
 980          Rewrite
[J
].NewTag 
= Rewrite
[J
].Tag
; 
 983    // Write all all of the tags, in order. 
 986       for (unsigned int I 
= 0; Order
[I
] != 0; I
++) 
 988          bool Rewritten 
= false; 
 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
++) 
 993             if (strcasecmp(Rewrite
[J
].Tag
,Order
[I
]) == 0) 
 996                if (Rewrite
[J
].Rewrite 
!= 0 && Rewrite
[J
].Rewrite
[0] != 0) 
 998                   if (isspace_ascii(Rewrite
[J
].Rewrite
[0])) 
 999                      fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
); 
1001                      fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
); 
1008          // See if it is in the fragment 
1010          if (Tags
.Find(Order
[I
],Pos
) == false) 
1014          if (Rewritten 
== true) 
1017          /* Write out this element, taking a moment to rewrite the tag 
1018             in case of changes of case. */ 
1021          Tags
.Get(Start
,Stop
,Pos
); 
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"); 
1033    // Now write all the old tags that were missed. 
1034    for (unsigned int I 
= 0; I 
!= Tags
.Count(); I
++) 
1036       if ((Visited
[I
] & 1) == 1) 
1041       Tags
.Get(Start
,Stop
,I
); 
1042       const char *End 
= Start
; 
1043       for (; End 
< Stop 
&& *End 
!= ':'; End
++); 
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
++) 
1049          if (stringcasecmp(Start
,End
,Rewrite
[J
].Tag
) == 0) 
1052             if (Rewrite
[J
].Rewrite 
!= 0 && Rewrite
[J
].Rewrite
[0] != 0) 
1054                if (isspace_ascii(Rewrite
[J
].Rewrite
[0])) 
1055                   fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
); 
1057                   fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
); 
1065       if (Rewritten 
== true) 
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"); 
1075    // Now write all the rewrites that were missed 
1076    for (unsigned int J 
= 0; Rewrite 
!= 0 && Rewrite
[J
].Tag 
!= 0; J
++) 
1078       if ((Visited
[J
] & 2) == 2) 
1081       if (Rewrite
[J
].Rewrite 
!= 0 && Rewrite
[J
].Rewrite
[0] != 0) 
1083          if (isspace_ascii(Rewrite
[J
].Rewrite
[0])) 
1084             fprintf(Output
,"%s:%s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
); 
1086             fprintf(Output
,"%s: %s\n",Rewrite
[J
].NewTag
,Rewrite
[J
].Rewrite
); 
1092 APT_IGNORE_DEPRECATED_POP
 
1095 pkgTagSection::~pkgTagSection() { delete d
; }