tagfile.cc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580
  1. // -*- mode: cpp; mode: fold -*-
  2. // Description /*{{{*/
  3. // $Id: tagfile.cc,v 1.37.2.2 2003/12/31 16:02:30 mdz Exp $
  4. /* ######################################################################
  5. Fast scanner for RFC-822 type header information
  6. This uses a rotating buffer to load the package information into.
  7. The scanner runs over it and isolates and indexes a single section.
  8. ##################################################################### */
  9. /*}}}*/
  10. // Include Files /*{{{*/
  11. #ifdef __GNUG__
  12. #pragma implementation "apt-pkg/tagfile.h"
  13. #endif
  14. #include <apt-pkg/tagfile.h>
  15. #include <apt-pkg/error.h>
  16. #include <apt-pkg/strutl.h>
  17. #include <apti18n.h>
  18. #include <string>
  19. #include <stdio.h>
  20. #include <ctype.h>
  21. /*}}}*/
  22. using std::string;
  23. // TagFile::pkgTagFile - Constructor /*{{{*/
  24. // ---------------------------------------------------------------------
  25. /* */
  26. pkgTagFile::pkgTagFile(FileFd *pFd,unsigned long Size) :
  27. Fd(*pFd),
  28. Size(Size),
  29. Map(NULL),
  30. Buffer(0)
  31. {
  32. if (Fd.IsOpen() == false)
  33. {
  34. Start = End = Buffer = 0;
  35. Done = true;
  36. iOffset = 0;
  37. Map = NULL;
  38. return;
  39. }
  40. // check if we can MMap it
  41. if(Fd.Size() == 0)
  42. {
  43. Buffer = new char[Size];
  44. Start = End = Buffer;
  45. Done = false;
  46. Fill();
  47. } else {
  48. Map = new MMap (Fd, MMap::Public | MMap::ReadOnly);
  49. Buffer = (char *) Map->Data ();
  50. Start = Buffer;
  51. End = Buffer + Map->Size ();
  52. }
  53. iOffset = 0;
  54. }
  55. /*}}}*/
  56. // TagFile::~pkgTagFile - Destructor /*{{{*/
  57. // ---------------------------------------------------------------------
  58. /* */
  59. pkgTagFile::~pkgTagFile()
  60. {
  61. if(!Map) delete [] Buffer;
  62. delete Map;
  63. }
  64. /*}}}*/
  65. // TagFile::Step - Advance to the next section /*{{{*/
  66. // ---------------------------------------------------------------------
  67. /* If the Section Scanner fails we refill the buffer and try again. */
  68. bool pkgTagFile::Step(pkgTagSection &Tag)
  69. {
  70. if ((Map != NULL) && (Start == End))
  71. return false;
  72. if (Tag.Scan(Start,End - Start) == false)
  73. {
  74. if (Map != NULL)
  75. return _error->Error(_("Unable to parse package file %s (1)"),
  76. Fd.Name().c_str());
  77. if (Fill() == false)
  78. return false;
  79. if (Tag.Scan(Start,End - Start) == false)
  80. return _error->Error(_("Unable to parse package file %s (1)"),
  81. Fd.Name().c_str());
  82. }
  83. Start += Tag.size();
  84. iOffset += Tag.size();
  85. Tag.Trim();
  86. return true;
  87. }
  88. /*}}}*/
  89. // TagFile::Fill - Top up the buffer /*{{{*/
  90. // ---------------------------------------------------------------------
  91. /* This takes the bit at the end of the buffer and puts it at the start
  92. then fills the rest from the file */
  93. bool pkgTagFile::Fill()
  94. {
  95. unsigned long EndSize = End - Start;
  96. unsigned long Actual = 0;
  97. memmove(Buffer,Start,EndSize);
  98. Start = Buffer;
  99. End = Buffer + EndSize;
  100. if (Done == false)
  101. {
  102. // See if only a bit of the file is left
  103. if (Fd.Read(End,Size - (End - Buffer),&Actual) == false)
  104. return false;
  105. if (Actual != Size - (End - Buffer))
  106. Done = true;
  107. End += Actual;
  108. }
  109. if (Done == true)
  110. {
  111. if (EndSize <= 3 && Actual == 0)
  112. return false;
  113. if (Size - (End - Buffer) < 4)
  114. return true;
  115. // Append a double new line if one does not exist
  116. unsigned int LineCount = 0;
  117. for (const char *E = End - 1; E - End < 6 && (*E == '\n' || *E == '\r'); E--)
  118. if (*E == '\n')
  119. LineCount++;
  120. for (; LineCount < 2; LineCount++)
  121. *End++ = '\n';
  122. return true;
  123. }
  124. return true;
  125. }
  126. /*}}}*/
  127. // TagFile::Jump - Jump to a pre-recorded location in the file /*{{{*/
  128. // ---------------------------------------------------------------------
  129. /* This jumps to a pre-recorded file location and reads the record
  130. that is there */
  131. bool pkgTagFile::Jump(pkgTagSection &Tag,unsigned long Offset)
  132. {
  133. // We are within a buffer space of the next hit..
  134. if (Offset >= iOffset && iOffset + (End - Start) > Offset)
  135. {
  136. unsigned long Dist = Offset - iOffset;
  137. Start += Dist;
  138. iOffset += Dist;
  139. return Step(Tag);
  140. }
  141. iOffset = Offset;
  142. if (Map != NULL)
  143. {
  144. Start = Buffer + iOffset;
  145. }
  146. else
  147. {
  148. // Reposition and reload..
  149. Done = false;
  150. if (Fd.Seek(Offset) == false)
  151. return false;
  152. End = Start = Buffer;
  153. if (Fill() == false)
  154. return false;
  155. if (Tag.Scan(Start,End - Start) == true)
  156. return true;
  157. // This appends a double new line (for the real eof handling)
  158. if (Fill() == false)
  159. return false;
  160. }
  161. if (Tag.Scan(Start,End - Start) == false)
  162. return _error->Error(_("Unable to parse package file %s (2)"),Fd.Name().c_str());
  163. return true;
  164. }
  165. /*}}}*/
  166. // TagSection::Scan - Scan for the end of the header information /*{{{*/
  167. // ---------------------------------------------------------------------
  168. /* This looks for the first double new line in the data stream. It also
  169. indexes the tags in the section. This very simple hash function for the
  170. first 3 letters gives very good performance on the debian package files */
  171. inline static unsigned long AlphaHash(const char *Text, const char *End = 0)
  172. {
  173. unsigned long Res = 0;
  174. for (; Text != End && *Text != ':' && *Text != 0; Text++)
  175. Res = (unsigned long)(*Text) ^ (Res << 2);
  176. return Res & 0xFF;
  177. }
  178. bool pkgTagSection::Scan(const char *Start,unsigned long MaxLength)
  179. {
  180. const char *End = Start + MaxLength;
  181. Stop = Section = Start;
  182. memset(AlphaIndexes,0,sizeof(AlphaIndexes));
  183. if (Stop == 0 || MaxLength == 0)
  184. return false;
  185. TagCount = 0;
  186. while (TagCount+1 < sizeof(Indexes)/sizeof(Indexes[0]) && Stop < End)
  187. {
  188. // Start a new index and add it to the hash
  189. if (isspace(Stop[0]) == 0)
  190. {
  191. Indexes[TagCount++] = Stop - Section;
  192. AlphaIndexes[AlphaHash(Stop,End)] = TagCount;
  193. }
  194. Stop = (const char *)memchr(Stop,'\n',End - Stop);
  195. if (Stop == 0)
  196. return false;
  197. for (; Stop+1 < End && Stop[1] == '\r'; Stop++);
  198. // Double newline marks the end of the record
  199. if (Stop+1 < End && Stop[1] == '\n')
  200. {
  201. Indexes[TagCount] = Stop - Section;
  202. for (; Stop < End && (Stop[0] == '\n' || Stop[0] == '\r'); Stop++);
  203. return true;
  204. }
  205. Stop++;
  206. }
  207. if ((Stop+1 >= End) && (End[-1] == '\n' || End[-1] == '\r'))
  208. {
  209. Indexes[TagCount] = (End - 1) - Section;
  210. return true;
  211. }
  212. return false;
  213. }
  214. /*}}}*/
  215. // TagSection::Trim - Trim off any trailing garbage /*{{{*/
  216. // ---------------------------------------------------------------------
  217. /* There should be exactly 1 newline at the end of the buffer, no more. */
  218. void pkgTagSection::Trim()
  219. {
  220. for (; Stop > Section + 2 && (Stop[-2] == '\n' || Stop[-2] == '\r'); Stop--);
  221. }
  222. /*}}}*/
  223. // TagSection::Find - Locate a tag /*{{{*/
  224. // ---------------------------------------------------------------------
  225. /* This searches the section for a tag that matches the given string. */
  226. bool pkgTagSection::Find(const char *Tag,unsigned &Pos) const
  227. {
  228. unsigned int Length = strlen(Tag);
  229. unsigned int I = AlphaIndexes[AlphaHash(Tag)];
  230. if (I == 0)
  231. return false;
  232. I--;
  233. for (unsigned int Counter = 0; Counter != TagCount; Counter++,
  234. I = (I+1)%TagCount)
  235. {
  236. const char *St;
  237. St = Section + Indexes[I];
  238. if (strncasecmp(Tag,St,Length) != 0)
  239. continue;
  240. // Make sure the colon is in the right place
  241. const char *C = St + Length;
  242. for (; isspace(*C) != 0; C++);
  243. if (*C != ':')
  244. continue;
  245. Pos = I;
  246. return true;
  247. }
  248. Pos = 0;
  249. return false;
  250. }
  251. /*}}}*/
  252. // TagSection::Find - Locate a tag /*{{{*/
  253. // ---------------------------------------------------------------------
  254. /* This searches the section for a tag that matches the given string. */
  255. bool pkgTagSection::Find(const char *Tag,const char *&Start,
  256. const char *&End) const
  257. {
  258. unsigned int Length = strlen(Tag);
  259. unsigned int I = AlphaIndexes[AlphaHash(Tag)];
  260. if (I == 0)
  261. return false;
  262. I--;
  263. for (unsigned int Counter = 0; Counter != TagCount; Counter++,
  264. I = (I+1)%TagCount)
  265. {
  266. const char *St;
  267. St = Section + Indexes[I];
  268. if (strncasecmp(Tag,St,Length) != 0)
  269. continue;
  270. // Make sure the colon is in the right place
  271. const char *C = St + Length;
  272. for (; isspace(*C) != 0; C++);
  273. if (*C != ':')
  274. continue;
  275. // Strip off the gunk from the start end
  276. Start = C;
  277. End = Section + Indexes[I+1];
  278. if (Start >= End)
  279. return _error->Error("Internal parsing error");
  280. for (; (isspace(*Start) != 0 || *Start == ':') && Start < End; Start++);
  281. for (; isspace(End[-1]) != 0 && End > Start; End--);
  282. return true;
  283. }
  284. Start = End = 0;
  285. return false;
  286. }
  287. /*}}}*/
  288. // TagSection::FindS - Find a string /*{{{*/
  289. // ---------------------------------------------------------------------
  290. /* */
  291. string pkgTagSection::FindS(const char *Tag) const
  292. {
  293. const char *Start;
  294. const char *End;
  295. if (Find(Tag,Start,End) == false)
  296. return string();
  297. return string(Start,End);
  298. }
  299. /*}}}*/
  300. // TagSection::FindI - Find an integer /*{{{*/
  301. // ---------------------------------------------------------------------
  302. /* */
  303. signed int pkgTagSection::FindI(const char *Tag,signed long Default) const
  304. {
  305. const char *Start;
  306. const char *Stop;
  307. if (Find(Tag,Start,Stop) == false)
  308. return Default;
  309. // Copy it into a temp buffer so we can use strtol
  310. char S[300];
  311. if ((unsigned)(Stop - Start) >= sizeof(S))
  312. return Default;
  313. strncpy(S,Start,Stop-Start);
  314. S[Stop - Start] = 0;
  315. char *End;
  316. signed long Result = strtol(S,&End,10);
  317. if (S == End)
  318. return Default;
  319. return Result;
  320. }
  321. /*}}}*/
  322. // TagSection::FindFlag - Locate a yes/no type flag /*{{{*/
  323. // ---------------------------------------------------------------------
  324. /* The bits marked in Flag are masked on/off in Flags */
  325. bool pkgTagSection::FindFlag(const char *Tag,unsigned long &Flags,
  326. unsigned long Flag) const
  327. {
  328. const char *Start;
  329. const char *Stop;
  330. if (Find(Tag,Start,Stop) == false)
  331. return true;
  332. switch (StringToBool(string(Start,Stop)))
  333. {
  334. case 0:
  335. Flags &= ~Flag;
  336. return true;
  337. case 1:
  338. Flags |= Flag;
  339. return true;
  340. default:
  341. _error->Warning("Unknown flag value: %s",string(Start,Stop).c_str());
  342. return true;
  343. }
  344. return true;
  345. }
  346. /*}}}*/
  347. // TFRewrite - Rewrite a control record /*{{{*/
  348. // ---------------------------------------------------------------------
  349. /* This writes the control record to stdout rewriting it as necessary. The
  350. override map item specificies the rewriting rules to follow. This also
  351. takes the time to sort the feild list. */
  352. /* The order of this list is taken from dpkg source lib/parse.c the fieldinfos
  353. array. */
  354. static const char *iTFRewritePackageOrder[] = {
  355. "Package",
  356. "Essential",
  357. "Status",
  358. "Priority",
  359. "Section",
  360. "Installed-Size",
  361. "Maintainer",
  362. "Architecture",
  363. "Source",
  364. "Version",
  365. "Revision", // Obsolete
  366. "Config-Version", // Obsolete
  367. "Replaces",
  368. "Provides",
  369. "Depends",
  370. "Pre-Depends",
  371. "Recommends",
  372. "Suggests",
  373. "Conflicts",
  374. "Conffiles",
  375. "Filename",
  376. "Size",
  377. "MD5Sum",
  378. "SHA1",
  379. "SHA256",
  380. "MSDOS-Filename", // Obsolete
  381. "Description",
  382. 0};
  383. static const char *iTFRewriteSourceOrder[] = {"Package",
  384. "Source",
  385. "Binary",
  386. "Version",
  387. "Priority",
  388. "Section",
  389. "Maintainer",
  390. "Build-Depends",
  391. "Build-Depends-Indep",
  392. "Build-Conflicts",
  393. "Build-Conflicts-Indep",
  394. "Architecture",
  395. "Standards-Version",
  396. "Format",
  397. "Directory",
  398. "Files",
  399. 0};
  400. /* Two levels of initialization are used because gcc will set the symbol
  401. size of an array to the length of the array, causing dynamic relinking
  402. errors. Doing this makes the symbol size constant */
  403. const char **TFRewritePackageOrder = iTFRewritePackageOrder;
  404. const char **TFRewriteSourceOrder = iTFRewriteSourceOrder;
  405. bool TFRewrite(FILE *Output,pkgTagSection const &Tags,const char *Order[],
  406. TFRewriteData *Rewrite)
  407. {
  408. unsigned char Visited[256]; // Bit 1 is Order, Bit 2 is Rewrite
  409. for (unsigned I = 0; I != 256; I++)
  410. Visited[I] = 0;
  411. // Set new tag up as necessary.
  412. for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
  413. {
  414. if (Rewrite[J].NewTag == 0)
  415. Rewrite[J].NewTag = Rewrite[J].Tag;
  416. }
  417. // Write all all of the tags, in order.
  418. for (unsigned int I = 0; Order[I] != 0; I++)
  419. {
  420. bool Rewritten = false;
  421. // See if this is a field that needs to be rewritten
  422. for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
  423. {
  424. if (strcasecmp(Rewrite[J].Tag,Order[I]) == 0)
  425. {
  426. Visited[J] |= 2;
  427. if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
  428. {
  429. if (isspace(Rewrite[J].Rewrite[0]))
  430. fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
  431. else
  432. fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
  433. }
  434. Rewritten = true;
  435. break;
  436. }
  437. }
  438. // See if it is in the fragment
  439. unsigned Pos;
  440. if (Tags.Find(Order[I],Pos) == false)
  441. continue;
  442. Visited[Pos] |= 1;
  443. if (Rewritten == true)
  444. continue;
  445. /* Write out this element, taking a moment to rewrite the tag
  446. in case of changes of case. */
  447. const char *Start;
  448. const char *Stop;
  449. Tags.Get(Start,Stop,Pos);
  450. if (fputs(Order[I],Output) < 0)
  451. return _error->Errno("fputs","IO Error to output");
  452. Start += strlen(Order[I]);
  453. if (fwrite(Start,Stop - Start,1,Output) != 1)
  454. return _error->Errno("fwrite","IO Error to output");
  455. if (Stop[-1] != '\n')
  456. fprintf(Output,"\n");
  457. }
  458. // Now write all the old tags that were missed.
  459. for (unsigned int I = 0; I != Tags.Count(); I++)
  460. {
  461. if ((Visited[I] & 1) == 1)
  462. continue;
  463. const char *Start;
  464. const char *Stop;
  465. Tags.Get(Start,Stop,I);
  466. const char *End = Start;
  467. for (; End < Stop && *End != ':'; End++);
  468. // See if this is a field that needs to be rewritten
  469. bool Rewritten = false;
  470. for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
  471. {
  472. if (stringcasecmp(Start,End,Rewrite[J].Tag) == 0)
  473. {
  474. Visited[J] |= 2;
  475. if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
  476. {
  477. if (isspace(Rewrite[J].Rewrite[0]))
  478. fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
  479. else
  480. fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
  481. }
  482. Rewritten = true;
  483. break;
  484. }
  485. }
  486. if (Rewritten == true)
  487. continue;
  488. // Write out this element
  489. if (fwrite(Start,Stop - Start,1,Output) != 1)
  490. return _error->Errno("fwrite","IO Error to output");
  491. if (Stop[-1] != '\n')
  492. fprintf(Output,"\n");
  493. }
  494. // Now write all the rewrites that were missed
  495. for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
  496. {
  497. if ((Visited[J] & 2) == 2)
  498. continue;
  499. if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
  500. {
  501. if (isspace(Rewrite[J].Rewrite[0]))
  502. fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
  503. else
  504. fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
  505. }
  506. }
  507. return true;
  508. }
  509. /*}}}*/