]>
git.saurik.com Git - apt.git/blob - methods/rred.cc
1 // Copyright (c) 2014 Anthony Towns
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.
10 #include <apt-pkg/fileutl.h>
11 #include <apt-pkg/mmap.h>
12 #include <apt-pkg/error.h>
13 #include <apt-pkg/acquire-method.h>
14 #include <apt-pkg/strutl.h>
15 #include <apt-pkg/hashes.h>
16 #include <apt-pkg/configuration.h>
32 #define BLOCK_SIZE (512*1024)
38 struct MemBlock
*next
;
40 MemBlock(size_t size
) : size(size
), next(NULL
)
42 free
= start
= new char[size
];
45 size_t avail(void) { return size
- (free
- start
); }
50 free
= start
= new char[BLOCK_SIZE
];
66 char *add_easy(char *src
, size_t len
, char *last
)
69 for (MemBlock
*k
= this; k
; k
= k
->next
) {
70 if (k
->free
== last
) {
71 if (len
<= k
->avail()) {
72 char *n
= k
->add(src
, len
);
80 } else if (last
>= start
&& last
< free
) {
88 char *add(char *src
, size_t len
) {
91 if (len
> BLOCK_SIZE
) {
92 next
= new MemBlock(len
);
97 return next
->add(src
, len
);
101 memcpy(dst
, src
, len
);
109 * 1. write out <offset> lines unchanged
110 * 2. skip <del_cnt> lines from source
111 * 3. write out <add_cnt> lines (<add>/<add_len>)
115 size_t add_cnt
; /* lines */
116 size_t add_len
; /* bytes */
122 del_cnt
= add_cnt
= add_len
= 0;
126 /* actually, don't write <lines> lines from <add> */
127 void skip_lines(size_t lines
)
130 char *s
= (char*) memchr(add
, '\n', add_len
);
133 add_len
-= (s
- add
);
138 assert(add_cnt
== 0);
149 std::list
<struct Change
> changes
;
150 std::list
<struct Change
>::iterator where
;
151 size_t pos
; // line number is as far left of iterator as possible
153 bool pos_is_okay(void)
157 std::list
<struct Change
>::iterator x
;
158 for (x
= changes
.begin(); x
!= where
; ++x
) {
159 assert(x
!= changes
.end());
160 cpos
+= x
->offset
+ x
->add_cnt
;
170 where
= changes
.end();
174 std::list
<struct Change
>::iterator
begin(void) { return changes
.begin(); }
175 std::list
<struct Change
>::iterator
end(void) { return changes
.end(); }
177 std::list
<struct Change
>::reverse_iterator
rbegin(void) { return changes
.rbegin(); }
178 std::list
<struct Change
>::reverse_iterator
rend(void) { return changes
.rend(); }
180 void add_change(Change c
) {
181 assert(pos_is_okay());
182 go_to_change_for(c
.offset
);
183 assert(pos
+ where
->offset
== c
.offset
);
185 delete_lines(c
.del_cnt
);
186 assert(pos
+ where
->offset
== c
.offset
);
188 assert(pos_is_okay());
189 if (where
->add_len
> 0)
191 assert(where
->add_len
== 0 && where
->add_cnt
== 0);
193 where
->add_len
= c
.add_len
;
194 where
->add_cnt
= c
.add_cnt
;
197 assert(pos_is_okay());
199 assert(pos_is_okay());
205 while (where
->offset
== 0 && where
!= changes
.begin()) {
208 std::list
<struct Change
>::iterator next
= where
;
211 while (next
!= changes
.end() && next
->offset
== 0) {
212 where
->del_cnt
+= next
->del_cnt
;
214 if (next
->add
== NULL
) {
215 next
= changes
.erase(next
);
216 } else if (where
->add
== NULL
) {
217 where
->add
= next
->add
;
218 where
->add_len
= next
->add_len
;
219 where
->add_cnt
= next
->add_cnt
;
220 next
= changes
.erase(next
);
227 void go_to_change_for(size_t line
)
229 while(where
!= changes
.end()) {
234 if (pos
+ where
->offset
+ where
->add_cnt
<= line
) {
238 // line is somewhere in this slot
239 if (line
< pos
+ where
->offset
) {
241 } else if (line
== pos
+ where
->offset
) {
249 /* it goes before this patch */
253 void new_change(void) { insert(where
->offset
); }
255 void insert(size_t offset
)
257 assert(pos_is_okay());
258 assert(where
== changes
.end() || offset
<= where
->offset
);
259 if (where
!= changes
.end())
260 where
->offset
-= offset
;
261 changes
.insert(where
, Change(offset
));
263 assert(pos_is_okay());
266 void split(size_t offset
)
268 assert(pos_is_okay());
270 assert(where
->offset
< offset
);
271 assert(offset
< where
->offset
+ where
->add_cnt
);
273 size_t keep_lines
= offset
- where
->offset
;
275 Change
before(*where
);
279 where
->skip_lines(keep_lines
);
281 before
.add_cnt
= keep_lines
;
282 before
.add_len
-= where
->add_len
;
284 changes
.insert(where
, before
);
286 assert(pos_is_okay());
289 void delete_lines(size_t cnt
)
291 std::list
<struct Change
>::iterator x
= where
;
292 assert(pos_is_okay());
303 if (x
== changes
.end()) {
311 where
->del_cnt
+= del
;
314 assert(pos_is_okay());
318 assert(pos_is_okay());
320 pos
-= where
->offset
+ where
->add_cnt
;
321 assert(pos_is_okay());
325 assert(pos_is_okay());
326 pos
+= where
->offset
+ where
->add_cnt
;
328 assert(pos_is_okay());
333 FileChanges filechanges
;
336 static bool retry_fwrite(char *b
, size_t l
, FILE *f
, Hashes
*hash
)
339 while (r
> 0 && l
> 0)
341 r
= fwrite(b
, 1, l
, f
);
343 hash
->Add((unsigned char*)b
, r
);
350 static void dump_rest(FILE *o
, FILE *i
, Hashes
*hash
)
352 char buffer
[BLOCK_SIZE
];
354 while (0 < (l
= fread(buffer
, 1, sizeof(buffer
), i
))) {
355 if (!retry_fwrite(buffer
, l
, o
, hash
))
360 static void dump_lines(FILE *o
, FILE *i
, size_t n
, Hashes
*hash
)
362 char buffer
[BLOCK_SIZE
];
364 if (fgets(buffer
, sizeof(buffer
), i
) == 0)
366 size_t const l
= strlen(buffer
);
367 if (l
== 0 || buffer
[l
-1] == '\n')
369 retry_fwrite(buffer
, l
, o
, hash
);
373 static void skip_lines(FILE *i
, int n
)
375 char buffer
[BLOCK_SIZE
];
377 if (fgets(buffer
, sizeof(buffer
), i
) == 0)
379 size_t const l
= strlen(buffer
);
380 if (l
== 0 || buffer
[l
-1] == '\n')
385 static void dump_mem(FILE *o
, char *p
, size_t s
, Hashes
*hash
) {
386 retry_fwrite(p
, s
, o
, hash
);
391 void read_diff(FileFd
&f
)
393 char buffer
[BLOCK_SIZE
];
394 bool cmdwanted
= true;
397 while(f
.ReadLine(buffer
, sizeof(buffer
)))
402 s
= strtol(buffer
, &m
, 10);
404 s
= e
= ch
.offset
+ ch
.add_cnt
;
406 } else if (*m
== ',') {
408 e
= strtol(m
, &c
, 10);
428 ch
.del_cnt
= e
- s
+ 1;
432 ch
.del_cnt
= e
- s
+ 1;
436 filechanges
.add_change(ch
);
439 } else { /* !cmdwanted */
440 if (buffer
[0] == '.' && buffer
[1] == '\n') {
442 filechanges
.add_change(ch
);
448 last
= ch
.add
+ ch
.add_len
;
450 add
= add_text
.add_easy(buffer
, l
, last
);
456 filechanges
.add_change(ch
);
459 ch
.offset
+= ch
.add_cnt
;
469 void write_diff(FILE *f
)
472 std::list
<struct Change
>::reverse_iterator ch
;
473 for (ch
= filechanges
.rbegin(); ch
!= filechanges
.rend(); ++ch
) {
474 line
+= ch
->offset
+ ch
->del_cnt
;
477 for (ch
= filechanges
.rbegin(); ch
!= filechanges
.rend(); ++ch
) {
478 std::list
<struct Change
>::reverse_iterator mg_i
, mg_e
= ch
;
479 while (ch
->del_cnt
== 0 && ch
->offset
== 0)
482 if (ch
->add_cnt
> 0) {
483 if (ch
->del_cnt
== 0) {
484 fprintf(f
, "%lua\n", line
);
485 } else if (ch
->del_cnt
== 1) {
486 fprintf(f
, "%luc\n", line
+1);
488 fprintf(f
, "%lu,%luc\n", line
+1, line
+ch
->del_cnt
);
493 dump_mem(f
, mg_i
->add
, mg_i
->add_len
, NULL
);
494 } while (mg_i
-- != mg_e
);
497 } else if (ch
->del_cnt
== 1) {
498 fprintf(f
, "%lud\n", line
+1);
499 } else if (ch
->del_cnt
> 1) {
500 fprintf(f
, "%lu,%lud\n", line
+1, line
+ch
->del_cnt
);
506 void apply_against_file(FILE *out
, FILE *in
, Hashes
*hash
= NULL
)
508 std::list
<struct Change
>::iterator ch
;
509 for (ch
= filechanges
.begin(); ch
!= filechanges
.end(); ++ch
) {
510 dump_lines(out
, in
, ch
->offset
, hash
);
511 skip_lines(in
, ch
->del_cnt
);
512 dump_mem(out
, ch
->add
, ch
->add_len
, hash
);
514 dump_rest(out
, in
, hash
);
518 class RredMethod
: public pkgAcqMethod
{
523 virtual bool Fetch(FetchItem
*Itm
) {
524 Debug
= _config
->FindB("Debug::pkgAcquire::RRed", false);
526 std::string Path
= Get
.Host
+ Get
.Path
; // rred:/path - no host
529 Res
.Filename
= Itm
->DestFile
;
530 if (Itm
->Uri
.empty())
532 Path
= Itm
->DestFile
;
533 Itm
->DestFile
.append(".result");
537 std::vector
<std::string
> patchpaths
;
540 if (FileExists(Path
+ ".ed") == true)
541 patchpaths
.push_back(Path
+ ".ed");
544 _error
->PushToStack();
545 std::vector
<std::string
> patches
= GetListOfFilesInDir(flNotFile(Path
), "gz", true, false);
546 _error
->RevertToStack();
548 std::string
const baseName
= Path
+ ".ed.";
549 for (std::vector
<std::string
>::const_iterator p
= patches
.begin();
550 p
!= patches
.end(); ++p
)
551 if (p
->compare(0, baseName
.length(), baseName
) == 0)
552 patchpaths
.push_back(*p
);
555 std::string patch_name
;
556 for (std::vector
<std::string
>::iterator I
= patchpaths
.begin();
557 I
!= patchpaths
.end();
562 std::clog
<< "Patching " << Path
<< " with " << patch_name
566 // all patches are compressed, even if the name doesn't reflect it
567 if (p
.Open(patch_name
, FileFd::ReadOnly
, FileFd::Gzip
) == false) {
568 std::cerr
<< "Could not open patch file " << patch_name
<< std::endl
;
569 _error
->DumpErrors(std::cerr
);
577 std::clog
<< "Applying patches against " << Path
578 << " and writing results to " << Itm
->DestFile
581 FILE *inp
= fopen(Path
.c_str(), "r");
582 FILE *out
= fopen(Itm
->DestFile
.c_str(), "w");
586 patch
.apply_against_file(out
, inp
, &hash
);
592 std::clog
<< "rred: finished file patching of " << Path
<< "." << std::endl
;
595 struct stat bufbase
, bufpatch
;
596 if (stat(Path
.c_str(), &bufbase
) != 0 ||
597 stat(patch_name
.c_str(), &bufpatch
) != 0)
598 return _error
->Errno("stat", _("Failed to stat"));
600 struct timespec times
[2];
601 times
[0].tv_sec
= bufbase
.st_atime
;
602 times
[1].tv_sec
= bufpatch
.st_mtime
;
603 times
[0].tv_nsec
= times
[1].tv_nsec
= 0;
604 if (utimensat(AT_FDCWD
, Itm
->DestFile
.c_str(), times
, 0) != 0)
605 return _error
->Errno("utimensat",_("Failed to set modification time"));
607 if (stat(Itm
->DestFile
.c_str(), &bufbase
) != 0)
608 return _error
->Errno("stat", _("Failed to stat"));
610 Res
.LastModified
= bufbase
.st_mtime
;
611 Res
.Size
= bufbase
.st_size
;
612 Res
.TakeHashes(hash
);
619 RredMethod() : pkgAcqMethod("2.0",SingleInstance
| SendConfig
), Debug(false) {}
622 int main(int argc
, char **argv
)
625 bool just_diff
= true;
633 if (argc
> 1 && strcmp(argv
[1], "-f") == 0) {
640 for (; i
< argc
; i
++) {
642 if (p
.Open(argv
[i
], FileFd::ReadOnly
) == false) {
643 _error
->DumpErrors(std::cerr
);
650 patch
.write_diff(stdout
);
656 patch
.apply_against_file(out
, inp
);