rred.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660
  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/fileutl.h>
  9. #include <apt-pkg/error.h>
  10. #include <apt-pkg/acquire-method.h>
  11. #include <apt-pkg/strutl.h>
  12. #include <apt-pkg/hashes.h>
  13. #include <apt-pkg/configuration.h>
  14. #include <stddef.h>
  15. #include <iostream>
  16. #include <string>
  17. #include <list>
  18. #include <vector>
  19. #include <assert.h>
  20. #include <stdio.h>
  21. #include <stdlib.h>
  22. #include <string.h>
  23. #include <sys/stat.h>
  24. #include <sys/time.h>
  25. #include <apti18n.h>
  26. #define BLOCK_SIZE (512*1024)
  27. class MemBlock {
  28. char *start;
  29. size_t size;
  30. char *free;
  31. struct MemBlock *next;
  32. MemBlock(size_t size) : size(size), next(NULL)
  33. {
  34. free = start = new char[size];
  35. }
  36. size_t avail(void) { return size - (free - start); }
  37. public:
  38. MemBlock(void) {
  39. free = start = new char[BLOCK_SIZE];
  40. size = BLOCK_SIZE;
  41. next = NULL;
  42. }
  43. ~MemBlock() {
  44. delete [] start;
  45. delete next;
  46. }
  47. void clear(void) {
  48. free = start;
  49. if (next)
  50. next->clear();
  51. }
  52. char *add_easy(char *src, size_t len, char *last)
  53. {
  54. if (last) {
  55. for (MemBlock *k = this; k; k = k->next) {
  56. if (k->free == last) {
  57. if (len <= k->avail()) {
  58. char *n = k->add(src, len);
  59. assert(last == n);
  60. if (last == n)
  61. return NULL;
  62. return n;
  63. } else {
  64. break;
  65. }
  66. } else if (last >= start && last < free) {
  67. break;
  68. }
  69. }
  70. }
  71. return add(src, len);
  72. }
  73. char *add(char *src, size_t len) {
  74. if (len > avail()) {
  75. if (!next) {
  76. if (len > BLOCK_SIZE) {
  77. next = new MemBlock(len);
  78. } else {
  79. next = new MemBlock;
  80. }
  81. }
  82. return next->add(src, len);
  83. }
  84. char *dst = free;
  85. free += len;
  86. memcpy(dst, src, len);
  87. return dst;
  88. }
  89. };
  90. struct Change {
  91. /* Ordering:
  92. *
  93. * 1. write out <offset> lines unchanged
  94. * 2. skip <del_cnt> lines from source
  95. * 3. write out <add_cnt> lines (<add>/<add_len>)
  96. */
  97. size_t offset;
  98. size_t del_cnt;
  99. size_t add_cnt; /* lines */
  100. size_t add_len; /* bytes */
  101. char *add;
  102. Change(int off)
  103. {
  104. offset = off;
  105. del_cnt = add_cnt = add_len = 0;
  106. add = NULL;
  107. }
  108. /* actually, don't write <lines> lines from <add> */
  109. void skip_lines(size_t lines)
  110. {
  111. while (lines > 0) {
  112. char *s = (char*) memchr(add, '\n', add_len);
  113. assert(s != NULL);
  114. s++;
  115. add_len -= (s - add);
  116. add_cnt--;
  117. lines--;
  118. if (add_len == 0) {
  119. add = NULL;
  120. assert(add_cnt == 0);
  121. assert(lines == 0);
  122. } else {
  123. add = s;
  124. assert(add_cnt > 0);
  125. }
  126. }
  127. }
  128. };
  129. class FileChanges {
  130. std::list<struct Change> changes;
  131. std::list<struct Change>::iterator where;
  132. size_t pos; // line number is as far left of iterator as possible
  133. bool pos_is_okay(void) const
  134. {
  135. #ifdef POSDEBUG
  136. size_t cpos = 0;
  137. std::list<struct Change>::const_iterator x;
  138. for (x = changes.begin(); x != where; ++x) {
  139. assert(x != changes.end());
  140. cpos += x->offset + x->add_cnt;
  141. }
  142. return cpos == pos;
  143. #else
  144. return true;
  145. #endif
  146. }
  147. public:
  148. FileChanges() {
  149. where = changes.end();
  150. pos = 0;
  151. }
  152. std::list<struct Change>::iterator begin(void) { return changes.begin(); }
  153. std::list<struct Change>::iterator end(void) { return changes.end(); }
  154. std::list<struct Change>::reverse_iterator rbegin(void) { return changes.rbegin(); }
  155. std::list<struct Change>::reverse_iterator rend(void) { return changes.rend(); }
  156. void add_change(Change c) {
  157. assert(pos_is_okay());
  158. go_to_change_for(c.offset);
  159. assert(pos + where->offset == c.offset);
  160. if (c.del_cnt > 0)
  161. delete_lines(c.del_cnt);
  162. assert(pos + where->offset == c.offset);
  163. if (c.add_len > 0) {
  164. assert(pos_is_okay());
  165. if (where->add_len > 0)
  166. new_change();
  167. assert(where->add_len == 0 && where->add_cnt == 0);
  168. where->add_len = c.add_len;
  169. where->add_cnt = c.add_cnt;
  170. where->add = c.add;
  171. }
  172. assert(pos_is_okay());
  173. merge();
  174. assert(pos_is_okay());
  175. }
  176. private:
  177. void merge(void)
  178. {
  179. while (where->offset == 0 && where != changes.begin()) {
  180. left();
  181. }
  182. std::list<struct Change>::iterator next = where;
  183. ++next;
  184. while (next != changes.end() && next->offset == 0) {
  185. where->del_cnt += next->del_cnt;
  186. next->del_cnt = 0;
  187. if (next->add == NULL) {
  188. next = changes.erase(next);
  189. } else if (where->add == NULL) {
  190. where->add = next->add;
  191. where->add_len = next->add_len;
  192. where->add_cnt = next->add_cnt;
  193. next = changes.erase(next);
  194. } else {
  195. ++next;
  196. }
  197. }
  198. }
  199. void go_to_change_for(size_t line)
  200. {
  201. while(where != changes.end()) {
  202. if (line < pos) {
  203. left();
  204. continue;
  205. }
  206. if (pos + where->offset + where->add_cnt <= line) {
  207. right();
  208. continue;
  209. }
  210. // line is somewhere in this slot
  211. if (line < pos + where->offset) {
  212. break;
  213. } else if (line == pos + where->offset) {
  214. return;
  215. } else {
  216. split(line - pos);
  217. right();
  218. return;
  219. }
  220. }
  221. /* it goes before this patch */
  222. insert(line-pos);
  223. }
  224. void new_change(void) { insert(where->offset); }
  225. void insert(size_t offset)
  226. {
  227. assert(pos_is_okay());
  228. assert(where == changes.end() || offset <= where->offset);
  229. if (where != changes.end())
  230. where->offset -= offset;
  231. changes.insert(where, Change(offset));
  232. --where;
  233. assert(pos_is_okay());
  234. }
  235. void split(size_t offset)
  236. {
  237. assert(pos_is_okay());
  238. assert(where->offset < offset);
  239. assert(offset < where->offset + where->add_cnt);
  240. size_t keep_lines = offset - where->offset;
  241. Change before(*where);
  242. where->del_cnt = 0;
  243. where->offset = 0;
  244. where->skip_lines(keep_lines);
  245. before.add_cnt = keep_lines;
  246. before.add_len -= where->add_len;
  247. changes.insert(where, before);
  248. --where;
  249. assert(pos_is_okay());
  250. }
  251. void delete_lines(size_t cnt)
  252. {
  253. std::list<struct Change>::iterator x = where;
  254. assert(pos_is_okay());
  255. while (cnt > 0)
  256. {
  257. size_t del;
  258. del = x->add_cnt;
  259. if (del > cnt)
  260. del = cnt;
  261. x->skip_lines(del);
  262. cnt -= del;
  263. ++x;
  264. if (x == changes.end()) {
  265. del = cnt;
  266. } else {
  267. del = x->offset;
  268. if (del > cnt)
  269. del = cnt;
  270. x->offset -= del;
  271. }
  272. where->del_cnt += del;
  273. cnt -= del;
  274. }
  275. assert(pos_is_okay());
  276. }
  277. void left(void) {
  278. assert(pos_is_okay());
  279. --where;
  280. pos -= where->offset + where->add_cnt;
  281. assert(pos_is_okay());
  282. }
  283. void right(void) {
  284. assert(pos_is_okay());
  285. pos += where->offset + where->add_cnt;
  286. ++where;
  287. assert(pos_is_okay());
  288. }
  289. };
  290. class Patch {
  291. FileChanges filechanges;
  292. MemBlock add_text;
  293. static bool retry_fwrite(char *b, size_t l, FILE *f, Hashes *hash)
  294. {
  295. size_t r = 1;
  296. while (r > 0 && l > 0)
  297. {
  298. r = fwrite(b, 1, l, f);
  299. if (hash)
  300. hash->Add((unsigned char*)b, r);
  301. l -= r;
  302. b += r;
  303. }
  304. return l == 0;
  305. }
  306. static void dump_rest(FILE *o, FILE *i, Hashes *hash)
  307. {
  308. char buffer[BLOCK_SIZE];
  309. size_t l;
  310. while (0 < (l = fread(buffer, 1, sizeof(buffer), i))) {
  311. if (!retry_fwrite(buffer, l, o, hash))
  312. break;
  313. }
  314. }
  315. static void dump_lines(FILE *o, FILE *i, size_t n, Hashes *hash)
  316. {
  317. char buffer[BLOCK_SIZE];
  318. while (n > 0) {
  319. if (fgets(buffer, sizeof(buffer), i) == 0)
  320. buffer[0] = '\0';
  321. size_t const l = strlen(buffer);
  322. if (l == 0 || buffer[l-1] == '\n')
  323. n--;
  324. retry_fwrite(buffer, l, o, hash);
  325. }
  326. }
  327. static void skip_lines(FILE *i, int n)
  328. {
  329. char buffer[BLOCK_SIZE];
  330. while (n > 0) {
  331. if (fgets(buffer, sizeof(buffer), i) == 0)
  332. buffer[0] = '\0';
  333. size_t const l = strlen(buffer);
  334. if (l == 0 || buffer[l-1] == '\n')
  335. n--;
  336. }
  337. }
  338. static void dump_mem(FILE *o, char *p, size_t s, Hashes *hash) {
  339. retry_fwrite(p, s, o, hash);
  340. }
  341. public:
  342. void read_diff(FileFd &f)
  343. {
  344. char buffer[BLOCK_SIZE];
  345. bool cmdwanted = true;
  346. Change ch(0);
  347. while(f.ReadLine(buffer, sizeof(buffer)))
  348. {
  349. if (cmdwanted) {
  350. char *m, *c;
  351. size_t s, e;
  352. s = strtol(buffer, &m, 10);
  353. if (m == buffer) {
  354. s = e = ch.offset + ch.add_cnt;
  355. c = buffer;
  356. } else if (*m == ',') {
  357. m++;
  358. e = strtol(m, &c, 10);
  359. } else {
  360. e = s;
  361. c = m;
  362. }
  363. switch(*c) {
  364. case 'a':
  365. cmdwanted = false;
  366. ch.add = NULL;
  367. ch.add_cnt = 0;
  368. ch.add_len = 0;
  369. ch.offset = s;
  370. ch.del_cnt = 0;
  371. break;
  372. case 'c':
  373. cmdwanted = false;
  374. ch.add = NULL;
  375. ch.add_cnt = 0;
  376. ch.add_len = 0;
  377. ch.offset = s - 1;
  378. ch.del_cnt = e - s + 1;
  379. break;
  380. case 'd':
  381. ch.offset = s - 1;
  382. ch.del_cnt = e - s + 1;
  383. ch.add = NULL;
  384. ch.add_cnt = 0;
  385. ch.add_len = 0;
  386. filechanges.add_change(ch);
  387. break;
  388. }
  389. } else { /* !cmdwanted */
  390. if (buffer[0] == '.' && buffer[1] == '\n') {
  391. cmdwanted = true;
  392. filechanges.add_change(ch);
  393. } else {
  394. char *last = NULL;
  395. char *add;
  396. size_t l;
  397. if (ch.add)
  398. last = ch.add + ch.add_len;
  399. l = strlen(buffer);
  400. add = add_text.add_easy(buffer, l, last);
  401. if (!add) {
  402. ch.add_len += l;
  403. ch.add_cnt++;
  404. } else {
  405. if (ch.add) {
  406. filechanges.add_change(ch);
  407. ch.del_cnt = 0;
  408. }
  409. ch.offset += ch.add_cnt;
  410. ch.add = add;
  411. ch.add_len = l;
  412. ch.add_cnt = 1;
  413. }
  414. }
  415. }
  416. }
  417. }
  418. void write_diff(FILE *f)
  419. {
  420. unsigned long long line = 0;
  421. std::list<struct Change>::reverse_iterator ch;
  422. for (ch = filechanges.rbegin(); ch != filechanges.rend(); ++ch) {
  423. line += ch->offset + ch->del_cnt;
  424. }
  425. for (ch = filechanges.rbegin(); ch != filechanges.rend(); ++ch) {
  426. std::list<struct Change>::reverse_iterator mg_i, mg_e = ch;
  427. while (ch->del_cnt == 0 && ch->offset == 0)
  428. ++ch;
  429. line -= ch->del_cnt;
  430. if (ch->add_cnt > 0) {
  431. if (ch->del_cnt == 0) {
  432. fprintf(f, "%llua\n", line);
  433. } else if (ch->del_cnt == 1) {
  434. fprintf(f, "%lluc\n", line+1);
  435. } else {
  436. fprintf(f, "%llu,%lluc\n", line+1, line+ch->del_cnt);
  437. }
  438. mg_i = ch;
  439. do {
  440. dump_mem(f, mg_i->add, mg_i->add_len, NULL);
  441. } while (mg_i-- != mg_e);
  442. fprintf(f, ".\n");
  443. } else if (ch->del_cnt == 1) {
  444. fprintf(f, "%llud\n", line+1);
  445. } else if (ch->del_cnt > 1) {
  446. fprintf(f, "%llu,%llud\n", line+1, line+ch->del_cnt);
  447. }
  448. line -= ch->offset;
  449. }
  450. }
  451. void apply_against_file(FILE *out, FILE *in, Hashes *hash = NULL)
  452. {
  453. std::list<struct Change>::iterator ch;
  454. for (ch = filechanges.begin(); ch != filechanges.end(); ++ch) {
  455. dump_lines(out, in, ch->offset, hash);
  456. skip_lines(in, ch->del_cnt);
  457. dump_mem(out, ch->add, ch->add_len, hash);
  458. }
  459. dump_rest(out, in, hash);
  460. }
  461. };
  462. class RredMethod : public pkgAcqMethod {
  463. private:
  464. bool Debug;
  465. protected:
  466. virtual bool Fetch(FetchItem *Itm) {
  467. Debug = _config->FindB("Debug::pkgAcquire::RRed", false);
  468. URI Get = Itm->Uri;
  469. std::string Path = Get.Host + Get.Path; // rred:/path - no host
  470. FetchResult Res;
  471. Res.Filename = Itm->DestFile;
  472. if (Itm->Uri.empty())
  473. {
  474. Path = Itm->DestFile;
  475. Itm->DestFile.append(".result");
  476. } else
  477. URIStart(Res);
  478. std::vector<std::string> patchpaths;
  479. Patch patch;
  480. if (FileExists(Path + ".ed") == true)
  481. patchpaths.push_back(Path + ".ed");
  482. else
  483. {
  484. _error->PushToStack();
  485. std::vector<std::string> patches = GetListOfFilesInDir(flNotFile(Path), "gz", true, false);
  486. _error->RevertToStack();
  487. std::string const baseName = Path + ".ed.";
  488. for (std::vector<std::string>::const_iterator p = patches.begin();
  489. p != patches.end(); ++p)
  490. if (p->compare(0, baseName.length(), baseName) == 0)
  491. patchpaths.push_back(*p);
  492. }
  493. std::string patch_name;
  494. for (std::vector<std::string>::iterator I = patchpaths.begin();
  495. I != patchpaths.end();
  496. ++I)
  497. {
  498. patch_name = *I;
  499. if (Debug == true)
  500. std::clog << "Patching " << Path << " with " << patch_name
  501. << std::endl;
  502. FileFd p;
  503. // all patches are compressed, even if the name doesn't reflect it
  504. if (p.Open(patch_name, FileFd::ReadOnly, FileFd::Gzip) == false) {
  505. std::cerr << "Could not open patch file " << patch_name << std::endl;
  506. _error->DumpErrors(std::cerr);
  507. abort();
  508. }
  509. patch.read_diff(p);
  510. p.Close();
  511. }
  512. if (Debug == true)
  513. std::clog << "Applying patches against " << Path
  514. << " and writing results to " << Itm->DestFile
  515. << std::endl;
  516. FILE *inp = fopen(Path.c_str(), "r");
  517. FILE *out = fopen(Itm->DestFile.c_str(), "w");
  518. Hashes hash;
  519. patch.apply_against_file(out, inp, &hash);
  520. fclose(out);
  521. fclose(inp);
  522. if (Debug == true) {
  523. std::clog << "rred: finished file patching of " << Path << "." << std::endl;
  524. }
  525. struct stat bufbase, bufpatch;
  526. if (stat(Path.c_str(), &bufbase) != 0 ||
  527. stat(patch_name.c_str(), &bufpatch) != 0)
  528. return _error->Errno("stat", _("Failed to stat"));
  529. struct timeval times[2];
  530. times[0].tv_sec = bufbase.st_atime;
  531. times[1].tv_sec = bufpatch.st_mtime;
  532. times[0].tv_usec = times[1].tv_usec = 0;
  533. if (utimes(Itm->DestFile.c_str(), times) != 0)
  534. return _error->Errno("utimes",_("Failed to set modification time"));
  535. if (stat(Itm->DestFile.c_str(), &bufbase) != 0)
  536. return _error->Errno("stat", _("Failed to stat"));
  537. Res.LastModified = bufbase.st_mtime;
  538. Res.Size = bufbase.st_size;
  539. Res.TakeHashes(hash);
  540. URIDone(Res);
  541. return true;
  542. }
  543. public:
  544. RredMethod() : pkgAcqMethod("2.0",SingleInstance | SendConfig), Debug(false) {}
  545. };
  546. int main(int argc, char **argv)
  547. {
  548. int i;
  549. bool just_diff = true;
  550. Patch patch;
  551. if (argc <= 1) {
  552. RredMethod Mth;
  553. return Mth.Run();
  554. }
  555. if (argc > 1 && strcmp(argv[1], "-f") == 0) {
  556. just_diff = false;
  557. i = 2;
  558. } else {
  559. i = 1;
  560. }
  561. for (; i < argc; i++) {
  562. FileFd p;
  563. if (p.Open(argv[i], FileFd::ReadOnly) == false) {
  564. _error->DumpErrors(std::cerr);
  565. exit(1);
  566. }
  567. patch.read_diff(p);
  568. }
  569. if (just_diff) {
  570. patch.write_diff(stdout);
  571. } else {
  572. FILE *out, *inp;
  573. out = stdout;
  574. inp = stdin;
  575. patch.apply_against_file(out, inp);
  576. }
  577. return 0;
  578. }