You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

678 lines
15 KiB

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