rred.cc 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759
  1. // Copyright (c) 2014 Anthony Towns
  2. //
  3. // This program is free software; you can redistribute it and/or modify
  4. // it under the terms of the GNU General Public License as published by
  5. // the Free Software Foundation; either version 2 of the License, or
  6. // (at your option) any later version.
  7. #include <config.h>
  8. #include <apt-pkg/init.h>
  9. #include <apt-pkg/fileutl.h>
  10. #include <apt-pkg/error.h>
  11. #include <apt-pkg/acquire-method.h>
  12. #include <apt-pkg/strutl.h>
  13. #include <apt-pkg/hashes.h>
  14. #include <apt-pkg/configuration.h>
  15. #include "aptmethod.h"
  16. #include <stddef.h>
  17. #include <iostream>
  18. #include <string>
  19. #include <list>
  20. #include <vector>
  21. #include <assert.h>
  22. #include <errno.h>
  23. #include <stdio.h>
  24. #include <stdlib.h>
  25. #include <string.h>
  26. #include <sys/stat.h>
  27. #include <sys/time.h>
  28. #include <apti18n.h>
  29. #define BLOCK_SIZE (512*1024)
  30. class MemBlock {
  31. char *start;
  32. size_t size;
  33. char *free;
  34. MemBlock *next;
  35. explicit MemBlock(size_t size) : size(size), next(NULL)
  36. {
  37. free = start = new char[size];
  38. }
  39. size_t avail(void) { return size - (free - start); }
  40. public:
  41. MemBlock(void) {
  42. free = start = new char[BLOCK_SIZE];
  43. size = BLOCK_SIZE;
  44. next = NULL;
  45. }
  46. ~MemBlock() {
  47. delete [] start;
  48. delete next;
  49. }
  50. void clear(void) {
  51. free = start;
  52. if (next)
  53. next->clear();
  54. }
  55. char *add_easy(char *src, size_t len, char *last)
  56. {
  57. if (last) {
  58. for (MemBlock *k = this; k; k = k->next) {
  59. if (k->free == last) {
  60. if (len <= k->avail()) {
  61. char *n = k->add(src, len);
  62. assert(last == n);
  63. if (last == n)
  64. return NULL;
  65. return n;
  66. } else {
  67. break;
  68. }
  69. } else if (last >= start && last < free) {
  70. break;
  71. }
  72. }
  73. }
  74. return add(src, len);
  75. }
  76. char *add(char *src, size_t len) {
  77. if (len > avail()) {
  78. if (!next) {
  79. if (len > BLOCK_SIZE) {
  80. next = new MemBlock(len);
  81. } else {
  82. next = new MemBlock;
  83. }
  84. }
  85. return next->add(src, len);
  86. }
  87. char *dst = free;
  88. free += len;
  89. memcpy(dst, src, len);
  90. return dst;
  91. }
  92. };
  93. struct Change {
  94. /* Ordering:
  95. *
  96. * 1. write out <offset> lines unchanged
  97. * 2. skip <del_cnt> lines from source
  98. * 3. write out <add_cnt> lines (<add>/<add_len>)
  99. */
  100. size_t offset;
  101. size_t del_cnt;
  102. size_t add_cnt; /* lines */
  103. size_t add_len; /* bytes */
  104. char *add;
  105. explicit Change(size_t off)
  106. {
  107. offset = off;
  108. del_cnt = add_cnt = add_len = 0;
  109. add = NULL;
  110. }
  111. /* actually, don't write <lines> lines from <add> */
  112. void skip_lines(size_t lines)
  113. {
  114. while (lines > 0) {
  115. char *s = (char*) memchr(add, '\n', add_len);
  116. assert(s != NULL);
  117. s++;
  118. add_len -= (s - add);
  119. add_cnt--;
  120. lines--;
  121. if (add_len == 0) {
  122. add = NULL;
  123. assert(add_cnt == 0);
  124. assert(lines == 0);
  125. } else {
  126. add = s;
  127. assert(add_cnt > 0);
  128. }
  129. }
  130. }
  131. };
  132. class FileChanges {
  133. std::list<struct Change> changes;
  134. std::list<struct Change>::iterator where;
  135. size_t pos; // line number is as far left of iterator as possible
  136. bool pos_is_okay(void) const
  137. {
  138. #ifdef POSDEBUG
  139. size_t cpos = 0;
  140. std::list<struct Change>::const_iterator x;
  141. for (x = changes.begin(); x != where; ++x) {
  142. assert(x != changes.end());
  143. cpos += x->offset + x->add_cnt;
  144. }
  145. return cpos == pos;
  146. #else
  147. return true;
  148. #endif
  149. }
  150. public:
  151. FileChanges() {
  152. where = changes.end();
  153. pos = 0;
  154. }
  155. std::list<struct Change>::iterator begin(void) { return changes.begin(); }
  156. std::list<struct Change>::iterator end(void) { return changes.end(); }
  157. std::list<struct Change>::reverse_iterator rbegin(void) { return changes.rbegin(); }
  158. std::list<struct Change>::reverse_iterator rend(void) { return changes.rend(); }
  159. void add_change(Change c) {
  160. assert(pos_is_okay());
  161. go_to_change_for(c.offset);
  162. assert(pos + where->offset == c.offset);
  163. if (c.del_cnt > 0)
  164. delete_lines(c.del_cnt);
  165. assert(pos + where->offset == c.offset);
  166. if (c.add_len > 0) {
  167. assert(pos_is_okay());
  168. if (where->add_len > 0)
  169. new_change();
  170. assert(where->add_len == 0 && where->add_cnt == 0);
  171. where->add_len = c.add_len;
  172. where->add_cnt = c.add_cnt;
  173. where->add = c.add;
  174. }
  175. assert(pos_is_okay());
  176. merge();
  177. assert(pos_is_okay());
  178. }
  179. private:
  180. void merge(void)
  181. {
  182. while (where->offset == 0 && where != changes.begin()) {
  183. left();
  184. }
  185. std::list<struct Change>::iterator next = where;
  186. ++next;
  187. while (next != changes.end() && next->offset == 0) {
  188. where->del_cnt += next->del_cnt;
  189. next->del_cnt = 0;
  190. if (next->add == NULL) {
  191. next = changes.erase(next);
  192. } else if (where->add == NULL) {
  193. where->add = next->add;
  194. where->add_len = next->add_len;
  195. where->add_cnt = next->add_cnt;
  196. next = changes.erase(next);
  197. } else {
  198. ++next;
  199. }
  200. }
  201. }
  202. void go_to_change_for(size_t line)
  203. {
  204. while(where != changes.end()) {
  205. if (line < pos) {
  206. left();
  207. continue;
  208. }
  209. if (pos + where->offset + where->add_cnt <= line) {
  210. right();
  211. continue;
  212. }
  213. // line is somewhere in this slot
  214. if (line < pos + where->offset) {
  215. break;
  216. } else if (line == pos + where->offset) {
  217. return;
  218. } else {
  219. split(line - pos);
  220. right();
  221. return;
  222. }
  223. }
  224. /* it goes before this patch */
  225. insert(line-pos);
  226. }
  227. void new_change(void) { insert(where->offset); }
  228. void insert(size_t offset)
  229. {
  230. assert(pos_is_okay());
  231. assert(where == changes.end() || offset <= where->offset);
  232. if (where != changes.end())
  233. where->offset -= offset;
  234. changes.insert(where, Change(offset));
  235. --where;
  236. assert(pos_is_okay());
  237. }
  238. void split(size_t offset)
  239. {
  240. assert(pos_is_okay());
  241. assert(where->offset < offset);
  242. assert(offset < where->offset + where->add_cnt);
  243. size_t keep_lines = offset - where->offset;
  244. Change before(*where);
  245. where->del_cnt = 0;
  246. where->offset = 0;
  247. where->skip_lines(keep_lines);
  248. before.add_cnt = keep_lines;
  249. before.add_len -= where->add_len;
  250. changes.insert(where, before);
  251. --where;
  252. assert(pos_is_okay());
  253. }
  254. void delete_lines(size_t cnt)
  255. {
  256. std::list<struct Change>::iterator x = where;
  257. assert(pos_is_okay());
  258. while (cnt > 0)
  259. {
  260. size_t del;
  261. del = x->add_cnt;
  262. if (del > cnt)
  263. del = cnt;
  264. x->skip_lines(del);
  265. cnt -= del;
  266. ++x;
  267. if (x == changes.end()) {
  268. del = cnt;
  269. } else {
  270. del = x->offset;
  271. if (del > cnt)
  272. del = cnt;
  273. x->offset -= del;
  274. }
  275. where->del_cnt += del;
  276. cnt -= del;
  277. }
  278. assert(pos_is_okay());
  279. }
  280. void left(void) {
  281. assert(pos_is_okay());
  282. --where;
  283. pos -= where->offset + where->add_cnt;
  284. assert(pos_is_okay());
  285. }
  286. void right(void) {
  287. assert(pos_is_okay());
  288. pos += where->offset + where->add_cnt;
  289. ++where;
  290. assert(pos_is_okay());
  291. }
  292. };
  293. class Patch {
  294. FileChanges filechanges;
  295. MemBlock add_text;
  296. static bool retry_fwrite(char *b, size_t l, FileFd &f, Hashes *hash)
  297. {
  298. if (f.Write(b, l) == false)
  299. return false;
  300. if (hash)
  301. hash->Add((unsigned char*)b, l);
  302. return true;
  303. }
  304. static void dump_rest(FileFd &o, FileFd &i, Hashes *hash)
  305. {
  306. char buffer[BLOCK_SIZE];
  307. unsigned long long l = 0;
  308. while (i.Read(buffer, sizeof(buffer), &l)) {
  309. if (l ==0 || !retry_fwrite(buffer, l, o, hash))
  310. break;
  311. }
  312. }
  313. static void dump_lines(FileFd &o, FileFd &i, size_t n, Hashes *hash)
  314. {
  315. char buffer[BLOCK_SIZE];
  316. while (n > 0) {
  317. if (i.ReadLine(buffer, sizeof(buffer)) == NULL)
  318. buffer[0] = '\0';
  319. size_t const l = strlen(buffer);
  320. if (l == 0 || buffer[l-1] == '\n')
  321. n--;
  322. retry_fwrite(buffer, l, o, hash);
  323. }
  324. }
  325. static void skip_lines(FileFd &i, int n)
  326. {
  327. char buffer[BLOCK_SIZE];
  328. while (n > 0) {
  329. if (i.ReadLine(buffer, sizeof(buffer)) == NULL)
  330. buffer[0] = '\0';
  331. size_t const l = strlen(buffer);
  332. if (l == 0 || buffer[l-1] == '\n')
  333. n--;
  334. }
  335. }
  336. static void dump_mem(FileFd &o, char *p, size_t s, Hashes *hash) {
  337. retry_fwrite(p, s, o, hash);
  338. }
  339. public:
  340. bool read_diff(FileFd &f, Hashes * const h)
  341. {
  342. char buffer[BLOCK_SIZE];
  343. bool cmdwanted = true;
  344. Change ch(std::numeric_limits<size_t>::max());
  345. if (f.ReadLine(buffer, sizeof(buffer)) == NULL)
  346. return _error->Error("Reading first line of patchfile %s failed", f.Name().c_str());
  347. do {
  348. if (h != NULL)
  349. h->Add(buffer);
  350. if (cmdwanted) {
  351. char *m, *c;
  352. size_t s, e;
  353. errno = 0;
  354. s = strtoul(buffer, &m, 10);
  355. if (unlikely(m == buffer || s == std::numeric_limits<unsigned long>::max() || errno != 0))
  356. return _error->Error("Parsing patchfile %s failed: Expected an effected line start", f.Name().c_str());
  357. else if (*m == ',') {
  358. ++m;
  359. e = strtol(m, &c, 10);
  360. if (unlikely(m == c || e == std::numeric_limits<unsigned long>::max() || errno != 0))
  361. return _error->Error("Parsing patchfile %s failed: Expected an effected line end", f.Name().c_str());
  362. if (unlikely(e < s))
  363. return _error->Error("Parsing patchfile %s failed: Effected lines end %lu is before start %lu", f.Name().c_str(), e, s);
  364. } else {
  365. e = s;
  366. c = m;
  367. }
  368. if (s > ch.offset)
  369. return _error->Error("Parsing patchfile %s failed: Effected line is after previous effected line", f.Name().c_str());
  370. switch(*c) {
  371. case 'a':
  372. cmdwanted = false;
  373. ch.add = NULL;
  374. ch.add_cnt = 0;
  375. ch.add_len = 0;
  376. ch.offset = s;
  377. ch.del_cnt = 0;
  378. break;
  379. case 'c':
  380. if (unlikely(s == 0))
  381. return _error->Error("Parsing patchfile %s failed: Change command can't effect line zero", f.Name().c_str());
  382. cmdwanted = false;
  383. ch.add = NULL;
  384. ch.add_cnt = 0;
  385. ch.add_len = 0;
  386. ch.offset = s - 1;
  387. ch.del_cnt = e - s + 1;
  388. break;
  389. case 'd':
  390. if (unlikely(s == 0))
  391. return _error->Error("Parsing patchfile %s failed: Delete command can't effect line zero", f.Name().c_str());
  392. ch.offset = s - 1;
  393. ch.del_cnt = e - s + 1;
  394. ch.add = NULL;
  395. ch.add_cnt = 0;
  396. ch.add_len = 0;
  397. filechanges.add_change(ch);
  398. break;
  399. default:
  400. return _error->Error("Parsing patchfile %s failed: Unknown command", f.Name().c_str());
  401. }
  402. } else { /* !cmdwanted */
  403. if (strcmp(buffer, ".\n") == 0) {
  404. cmdwanted = true;
  405. filechanges.add_change(ch);
  406. } else {
  407. char *last = NULL;
  408. char *add;
  409. size_t l;
  410. if (ch.add)
  411. last = ch.add + ch.add_len;
  412. l = strlen(buffer);
  413. add = add_text.add_easy(buffer, l, last);
  414. if (!add) {
  415. ch.add_len += l;
  416. ch.add_cnt++;
  417. } else {
  418. if (ch.add) {
  419. filechanges.add_change(ch);
  420. ch.del_cnt = 0;
  421. }
  422. ch.offset += ch.add_cnt;
  423. ch.add = add;
  424. ch.add_len = l;
  425. ch.add_cnt = 1;
  426. }
  427. }
  428. }
  429. } while(f.ReadLine(buffer, sizeof(buffer)));
  430. return true;
  431. }
  432. void write_diff(FileFd &f)
  433. {
  434. unsigned long long line = 0;
  435. std::list<struct Change>::reverse_iterator ch;
  436. for (ch = filechanges.rbegin(); ch != filechanges.rend(); ++ch) {
  437. line += ch->offset + ch->del_cnt;
  438. }
  439. for (ch = filechanges.rbegin(); ch != filechanges.rend(); ++ch) {
  440. std::list<struct Change>::reverse_iterator mg_i, mg_e = ch;
  441. while (ch->del_cnt == 0 && ch->offset == 0)
  442. {
  443. ++ch;
  444. if (unlikely(ch == filechanges.rend()))
  445. return;
  446. }
  447. line -= ch->del_cnt;
  448. std::string buf;
  449. if (ch->add_cnt > 0) {
  450. if (ch->del_cnt == 0) {
  451. strprintf(buf, "%llua\n", line);
  452. } else if (ch->del_cnt == 1) {
  453. strprintf(buf, "%lluc\n", line+1);
  454. } else {
  455. strprintf(buf, "%llu,%lluc\n", line+1, line+ch->del_cnt);
  456. }
  457. f.Write(buf.c_str(), buf.length());
  458. mg_i = ch;
  459. do {
  460. dump_mem(f, mg_i->add, mg_i->add_len, NULL);
  461. } while (mg_i-- != mg_e);
  462. buf = ".\n";
  463. f.Write(buf.c_str(), buf.length());
  464. } else if (ch->del_cnt == 1) {
  465. strprintf(buf, "%llud\n", line+1);
  466. f.Write(buf.c_str(), buf.length());
  467. } else if (ch->del_cnt > 1) {
  468. strprintf(buf, "%llu,%llud\n", line+1, line+ch->del_cnt);
  469. f.Write(buf.c_str(), buf.length());
  470. }
  471. line -= ch->offset;
  472. }
  473. }
  474. void apply_against_file(FileFd &out, FileFd &in, Hashes *hash = NULL)
  475. {
  476. std::list<struct Change>::iterator ch;
  477. for (ch = filechanges.begin(); ch != filechanges.end(); ++ch) {
  478. dump_lines(out, in, ch->offset, hash);
  479. skip_lines(in, ch->del_cnt);
  480. dump_mem(out, ch->add, ch->add_len, hash);
  481. }
  482. dump_rest(out, in, hash);
  483. out.Flush();
  484. }
  485. };
  486. class RredMethod : public aptMethod {
  487. private:
  488. bool Debug;
  489. struct PDiffFile {
  490. std::string FileName;
  491. HashStringList ExpectedHashes;
  492. PDiffFile(std::string const &FileName, HashStringList const &ExpectedHashes) :
  493. FileName(FileName), ExpectedHashes(ExpectedHashes) {}
  494. };
  495. HashStringList ReadExpectedHashesForPatch(unsigned int const patch, std::string const &Message)
  496. {
  497. HashStringList ExpectedHashes;
  498. for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
  499. {
  500. std::string tagname;
  501. strprintf(tagname, "Patch-%d-%s-Hash", patch, *type);
  502. std::string const hashsum = LookupTag(Message, tagname.c_str());
  503. if (hashsum.empty() == false)
  504. ExpectedHashes.push_back(HashString(*type, hashsum));
  505. }
  506. return ExpectedHashes;
  507. }
  508. protected:
  509. virtual bool URIAcquire(std::string const &Message, FetchItem *Itm) APT_OVERRIDE {
  510. Debug = _config->FindB("Debug::pkgAcquire::RRed", false);
  511. URI Get = Itm->Uri;
  512. std::string Path = Get.Host + Get.Path; // rred:/path - no host
  513. FetchResult Res;
  514. Res.Filename = Itm->DestFile;
  515. if (Itm->Uri.empty())
  516. {
  517. Path = Itm->DestFile;
  518. Itm->DestFile.append(".result");
  519. } else
  520. URIStart(Res);
  521. std::vector<PDiffFile> patchfiles;
  522. Patch patch;
  523. if (FileExists(Path + ".ed") == true)
  524. {
  525. HashStringList const ExpectedHashes = ReadExpectedHashesForPatch(0, Message);
  526. std::string const FileName = Path + ".ed";
  527. if (ExpectedHashes.usable() == false)
  528. return _error->Error("No hashes found for uncompressed patch: %s", FileName.c_str());
  529. patchfiles.push_back(PDiffFile(FileName, ExpectedHashes));
  530. }
  531. else
  532. {
  533. _error->PushToStack();
  534. std::vector<std::string> patches = GetListOfFilesInDir(flNotFile(Path), "gz", true, false);
  535. _error->RevertToStack();
  536. std::string const baseName = Path + ".ed.";
  537. unsigned int seen_patches = 0;
  538. for (std::vector<std::string>::const_iterator p = patches.begin();
  539. p != patches.end(); ++p)
  540. {
  541. if (p->compare(0, baseName.length(), baseName) == 0)
  542. {
  543. HashStringList const ExpectedHashes = ReadExpectedHashesForPatch(seen_patches, Message);
  544. if (ExpectedHashes.usable() == false)
  545. return _error->Error("No hashes found for uncompressed patch %d: %s", seen_patches, p->c_str());
  546. patchfiles.push_back(PDiffFile(*p, ExpectedHashes));
  547. ++seen_patches;
  548. }
  549. }
  550. }
  551. std::string patch_name;
  552. for (std::vector<PDiffFile>::iterator I = patchfiles.begin();
  553. I != patchfiles.end();
  554. ++I)
  555. {
  556. patch_name = I->FileName;
  557. if (Debug == true)
  558. std::clog << "Patching " << Path << " with " << patch_name
  559. << std::endl;
  560. FileFd p;
  561. Hashes patch_hash(I->ExpectedHashes);
  562. // all patches are compressed, even if the name doesn't reflect it
  563. if (p.Open(patch_name, FileFd::ReadOnly, FileFd::Gzip) == false ||
  564. patch.read_diff(p, &patch_hash) == false)
  565. {
  566. _error->DumpErrors(std::cerr, GlobalError::DEBUG, false);
  567. return false;
  568. }
  569. p.Close();
  570. HashStringList const hsl = patch_hash.GetHashStringList();
  571. if (hsl != I->ExpectedHashes)
  572. return _error->Error("Hash Sum mismatch for uncompressed patch %s", patch_name.c_str());
  573. }
  574. if (Debug == true)
  575. std::clog << "Applying patches against " << Path
  576. << " and writing results to " << Itm->DestFile
  577. << std::endl;
  578. FileFd inp, out;
  579. if (inp.Open(Path, FileFd::ReadOnly, FileFd::Extension) == false)
  580. {
  581. std::cerr << "FAILED to open inp " << Path << std::endl;
  582. return _error->Error("Failed to open inp %s", Path.c_str());
  583. }
  584. if (out.Open(Itm->DestFile, FileFd::WriteOnly | FileFd::Create | FileFd::BufferedWrite, FileFd::Extension) == false)
  585. {
  586. std::cerr << "FAILED to open out " << Itm->DestFile << std::endl;
  587. return _error->Error("Failed to open out %s", Itm->DestFile.c_str());
  588. }
  589. Hashes hash(Itm->ExpectedHashes);
  590. patch.apply_against_file(out, inp, &hash);
  591. out.Close();
  592. inp.Close();
  593. if (_error->PendingError() == true) {
  594. std::cerr << "FAILED to read or write files" << std::endl;
  595. return false;
  596. }
  597. if (Debug == true) {
  598. std::clog << "rred: finished file patching of " << Path << "." << std::endl;
  599. }
  600. struct stat bufbase, bufpatch;
  601. if (stat(Path.c_str(), &bufbase) != 0 ||
  602. stat(patch_name.c_str(), &bufpatch) != 0)
  603. return _error->Errno("stat", _("Failed to stat %s"), Path.c_str());
  604. struct timeval times[2];
  605. times[0].tv_sec = bufbase.st_atime;
  606. times[1].tv_sec = bufpatch.st_mtime;
  607. times[0].tv_usec = times[1].tv_usec = 0;
  608. if (utimes(Itm->DestFile.c_str(), times) != 0)
  609. return _error->Errno("utimes",_("Failed to set modification time"));
  610. if (stat(Itm->DestFile.c_str(), &bufbase) != 0)
  611. return _error->Errno("stat", _("Failed to stat %s"), Itm->DestFile.c_str());
  612. Res.LastModified = bufbase.st_mtime;
  613. Res.Size = bufbase.st_size;
  614. Res.TakeHashes(hash);
  615. URIDone(Res);
  616. return true;
  617. }
  618. public:
  619. RredMethod() : aptMethod("rred", "2.0", SendConfig), Debug(false) {}
  620. };
  621. int main(int argc, char **argv)
  622. {
  623. int i;
  624. bool just_diff = true;
  625. bool test = false;
  626. Patch patch;
  627. if (argc <= 1) {
  628. RredMethod Mth;
  629. return Mth.Run();
  630. }
  631. // Usage: rred -t input output diff ...
  632. if (argc > 1 && strcmp(argv[1], "-t") == 0) {
  633. // Read config files so we see compressors.
  634. pkgInitConfig(*_config);
  635. just_diff = false;
  636. test = true;
  637. i = 4;
  638. } else if (argc > 1 && strcmp(argv[1], "-f") == 0) {
  639. just_diff = false;
  640. i = 2;
  641. } else {
  642. i = 1;
  643. }
  644. for (; i < argc; i++) {
  645. FileFd p;
  646. if (p.Open(argv[i], FileFd::ReadOnly) == false) {
  647. _error->DumpErrors(std::cerr);
  648. exit(1);
  649. }
  650. if (patch.read_diff(p, NULL) == false)
  651. {
  652. _error->DumpErrors(std::cerr);
  653. exit(2);
  654. }
  655. }
  656. if (test) {
  657. FileFd out, inp;
  658. std::cerr << "Patching " << argv[2] << " into " << argv[3] << "\n";
  659. inp.Open(argv[2], FileFd::ReadOnly,FileFd::Extension);
  660. out.Open(argv[3], FileFd::WriteOnly | FileFd::Create | FileFd::BufferedWrite, FileFd::Extension);
  661. patch.apply_against_file(out, inp);
  662. out.Close();
  663. } else if (just_diff) {
  664. FileFd out;
  665. out.OpenDescriptor(STDOUT_FILENO, FileFd::WriteOnly | FileFd::Create);
  666. patch.write_diff(out);
  667. out.Close();
  668. } else {
  669. FileFd out, inp;
  670. out.OpenDescriptor(STDOUT_FILENO, FileFd::WriteOnly | FileFd::Create | FileFd::BufferedWrite);
  671. inp.OpenDescriptor(STDIN_FILENO, FileFd::ReadOnly);
  672. patch.apply_against_file(out, inp);
  673. out.Close();
  674. }
  675. return 0;
  676. }