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