]>
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
;
42 free
= start
= new char[size
];
47 size_t avail(void) { return size
- (free
- start
); }
52 free
= start
= new char[BLOCK_SIZE
];
68 char *add_easy(char *src
, size_t len
, char *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
);
82 } else if (last
>= start
&& last
< free
) {
90 char *add(char *src
, size_t len
) {
93 if (len
> BLOCK_SIZE
) {
94 next
= new MemBlock(len
);
99 return next
->add(src
, len
);
103 memcpy(dst
, src
, len
);
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>)
117 size_t add_cnt
; /* lines */
118 size_t add_len
; /* bytes */
124 del_cnt
= add_cnt
= add_len
= 0;
128 /* actually, don't write <lines> lines from <add> */
129 void skip_lines(size_t lines
)
132 char *s
= (char*) memchr(add
, '\n', add_len
);
135 add_len
-= (s
- add
);
140 assert(add_cnt
== 0);
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
155 bool pos_is_okay(void)
159 std::list
<struct Change
>::iterator x
;
160 for (x
= changes
.begin(); x
!= where
; x
++) {
161 assert(x
!= changes
.end());
162 cpos
+= x
->offset
+ x
->add_cnt
;
172 where
= changes
.end();
176 std::list
<struct Change
>::iterator
begin(void) { return changes
.begin(); }
177 std::list
<struct Change
>::iterator
end(void) { return changes
.end(); }
179 std::list
<struct Change
>::reverse_iterator
rbegin(void) { return changes
.rbegin(); }
180 std::list
<struct Change
>::reverse_iterator
rend(void) { return changes
.rend(); }
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
);
187 delete_lines(c
.del_cnt
);
188 assert(pos
+ where
->offset
== c
.offset
);
190 assert(pos_is_okay());
191 if (where
->add_len
> 0)
193 assert(where
->add_len
== 0 && where
->add_cnt
== 0);
195 where
->add_len
= c
.add_len
;
196 where
->add_cnt
= c
.add_cnt
;
199 assert(pos_is_okay());
201 assert(pos_is_okay());
207 while (where
->offset
== 0 && where
!= changes
.begin()) {
210 std::list
<struct Change
>::iterator next
= where
;
213 while (next
!= changes
.end() && next
->offset
== 0) {
214 where
->del_cnt
+= next
->del_cnt
;
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
);
229 void go_to_change_for(size_t line
)
231 while(where
!= changes
.end()) {
236 if (pos
+ where
->offset
+ where
->add_cnt
<= line
) {
240 // line is somewhere in this slot
241 if (line
< pos
+ where
->offset
) {
243 } else if (line
== pos
+ where
->offset
) {
251 /* it goes before this patch */
255 void new_change(void) { insert(where
->offset
); }
257 void insert(size_t offset
)
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
));
265 assert(pos_is_okay());
268 void split(size_t offset
)
270 assert(pos_is_okay());
272 assert(where
->offset
< offset
);
273 assert(offset
< where
->offset
+ where
->add_cnt
);
275 size_t keep_lines
= offset
- where
->offset
;
277 Change
before(*where
);
281 where
->skip_lines(keep_lines
);
283 before
.add_cnt
= keep_lines
;
284 before
.add_len
-= where
->add_len
;
286 changes
.insert(where
, before
);
288 assert(pos_is_okay());
291 size_t check_next_offset(size_t max
)
293 assert(pos_is_okay());
297 if (where
!= changes
.end()) {
298 if (where
->offset
< max
)
302 assert(pos_is_okay());
307 void delete_lines(size_t cnt
)
309 std::list
<struct Change
>::iterator x
= where
;
310 assert(pos_is_okay());
321 if (x
== changes
.end()) {
329 where
->del_cnt
+= del
;
332 assert(pos_is_okay());
336 assert(pos_is_okay());
338 pos
-= where
->offset
+ where
->add_cnt
;
339 assert(pos_is_okay());
343 assert(pos_is_okay());
344 pos
+= where
->offset
+ where
->add_cnt
;
346 assert(pos_is_okay());
351 FileChanges filechanges
;
354 static void dump_rest(FILE *o
, FILE *i
, Hashes
*hash
)
356 char buffer
[BLOCK_SIZE
];
358 while (0 < (l
= fread(buffer
, 1, sizeof(buffer
), i
))) {
359 fwrite(buffer
, 1, l
, o
);
361 hash
->Add((unsigned char*)buffer
, l
);
365 static void dump_lines(FILE *o
, FILE *i
, size_t n
, Hashes
*hash
)
367 char buffer
[BLOCK_SIZE
];
370 if (fgets(buffer
, sizeof(buffer
), i
) == 0)
373 if (l
== 0 || buffer
[l
-1] == '\n')
375 fwrite(buffer
, 1, l
, o
);
378 hash
->Add((unsigned char*)buffer
, l
);
382 static void skip_lines(FILE *i
, int n
)
384 char buffer
[BLOCK_SIZE
];
387 if (fgets(buffer
, sizeof(buffer
), i
) == 0)
390 if (l
== 0 || buffer
[l
-1] == '\n')
395 static bool dump_mem(FILE *o
, char *p
, size_t s
, Hashes
*hash
) {
398 r
= fwrite(p
, 1, s
, o
);
400 hash
->Add((unsigned char*)p
, s
);
403 if (r
== 0) return false;
410 void read_diff(FILE *f
)
412 char buffer
[BLOCK_SIZE
];
413 bool cmdwanted
= true;
416 while(fgets(buffer
, sizeof(buffer
), f
))
421 s
= strtol(buffer
, &m
, 10);
423 s
= e
= ch
.offset
+ ch
.add_cnt
;
425 } else if (*m
== ',') {
427 e
= strtol(m
, &c
, 10);
447 ch
.del_cnt
= e
- s
+ 1;
451 ch
.del_cnt
= e
- s
+ 1;
455 filechanges
.add_change(ch
);
458 } else { /* !cmdwaanted */
459 if (buffer
[0] == '.' && buffer
[1] == '\n') {
461 filechanges
.add_change(ch
);
467 last
= ch
.add
+ ch
.add_len
;
469 add
= add_text
.add_easy(buffer
, l
, last
);
475 filechanges
.add_change(ch
);
478 ch
.offset
+= ch
.add_cnt
;
488 void write_diff(FILE *f
)
491 std::list
<struct Change
>::reverse_iterator ch
;
492 for (ch
= filechanges
.rbegin(); ch
!= filechanges
.rend(); ch
++) {
493 line
+= ch
->offset
+ ch
->del_cnt
;
496 for (ch
= filechanges
.rbegin(); ch
!= filechanges
.rend(); ch
++) {
497 std::list
<struct Change
>::reverse_iterator mg_i
, mg_e
= ch
;
498 while (ch
->del_cnt
== 0 && ch
->offset
== 0)
501 if (ch
->add_cnt
> 0) {
502 if (ch
->del_cnt
== 0) {
503 fprintf(f
, "%lua\n", line
);
504 } else if (ch
->del_cnt
== 1) {
505 fprintf(f
, "%luc\n", line
+1);
507 fprintf(f
, "%lu,%luc\n", line
+1, line
+ch
->del_cnt
);
512 dump_mem(f
, mg_i
->add
, mg_i
->add_len
, NULL
);
513 } while (mg_i
-- != mg_e
);
516 } else if (ch
->del_cnt
== 1) {
517 fprintf(f
, "%lud\n", line
+1);
518 } else if (ch
->del_cnt
> 1) {
519 fprintf(f
, "%lu,%lud\n", line
+1, line
+ch
->del_cnt
);
525 void apply_against_file(FILE *out
, FILE *in
, Hashes
*hash
= NULL
)
527 std::list
<struct Change
>::iterator ch
;
528 for (ch
= filechanges
.begin(); ch
!= filechanges
.end(); ch
++) {
529 dump_lines(out
, in
, ch
->offset
, hash
);
530 skip_lines(in
, ch
->del_cnt
);
531 dump_mem(out
, ch
->add
, ch
->add_len
, hash
);
533 dump_rest(out
, in
, hash
);
537 bool LookupPatches(const std::string
&Message
, std::vector
<std::string
> &lines
)
539 const char *Tag
= "Patches";
540 const size_t Length
= strlen(Tag
);
542 std::string::const_iterator I
, J
;
544 std::clog
<< "Looking for \"Patches:\" section in message:\n\n" << Message
<< "\n\n";
547 for (I
= Message
.begin(); I
+ Length
< Message
.end(); ++I
)
549 if (I
[Length
] == ':' && stringcasecmp(I
, I
+Length
, Tag
) == 0)
551 // found the tag, now read the patches
553 for (; I
< Message
.end() && *I
!= '\n'; ++I
);
554 if (I
< Message
.end()) I
++;
555 if (I
== Message
.end() || *I
!= ' ')
557 while (I
< Message
.end() && isspace(*I
)) I
++;
558 for (J
= I
; J
< Message
.end() && *J
!= '\n'; ++J
)
562 while (I
< J
&& isspace(*J
));
564 lines
.push_back(std::string(I
,++J
));
569 std::clog
<< "Found " << lines
.size() << " patches!\n";
574 std::clog
<< "Found no patches! :(\n";
580 class RredMethod
: public pkgAcqMethod
{
583 std::vector
<std::string
> patchpaths
;
586 virtual bool HandleMessage(int Number
, std::string Message
) {
590 LookupPatches(Message
, patchpaths
);
591 std::clog
<< "Ended up with " << patchpaths
.size() << " patches!\n";
594 return pkgAcqMethod::HandleMessage(Number
, Message
);
597 virtual bool Fetch(FetchItem
*Itm
) {
598 Debug
= _config
->FindB("Debug::pkgAcquire::RRed", false);
600 std::string Path
= Get
.Host
+ Get
.Path
; // rred:/path - no host
603 Res
.Filename
= Itm
->DestFile
;
604 if (Itm
->Uri
.empty()) {
605 Path
= Itm
->DestFile
;
606 Itm
->DestFile
.append(".result");
612 if (patchpaths
.empty())
614 patchpaths
.push_back(Path
+ ".ed");
617 std::string patch_name
;
618 for (std::vector
<std::string
>::iterator I
= patchpaths
.begin();
619 I
!= patchpaths
.end();
624 std::clog
<< "Patching " << Path
<< " with " << patch_name
627 FILE *p
= fopen(patch_name
.c_str(), "r");
629 std::clog
<< "Could not open patch file " << patch_name
<< std::endl
;
637 std::clog
<< "Applying patches against " << Path
638 << " and writing results to " << Itm
->DestFile
641 FILE *inp
= fopen(Path
.c_str(), "r");
642 FILE *out
= fopen(Itm
->DestFile
.c_str(), "w");
646 patch
.apply_against_file(out
, inp
, &hash
);
652 std::clog
<< "rred: finished file patching of " << Path
<< "." << std::endl
;
655 struct stat bufbase
, bufpatch
;
656 if (stat(Path
.c_str(), &bufbase
) != 0 ||
657 stat(patch_name
.c_str(), &bufpatch
) != 0)
658 return _error
->Errno("stat", _("Failed to stat"));
660 struct utimbuf timebuf
;
661 timebuf
.actime
= bufbase
.st_atime
;
662 timebuf
.modtime
= bufpatch
.st_mtime
;
663 if (utime(Itm
->DestFile
.c_str(), &timebuf
) != 0)
664 return _error
->Errno("utime", _("Failed to set modification time"));
666 if (stat(Itm
->DestFile
.c_str(), &bufbase
) != 0)
667 return _error
->Errno("stat", _("Failed to stat"));
669 Res
.LastModified
= bufbase
.st_mtime
;
670 Res
.Size
= bufbase
.st_size
;
671 Res
.TakeHashes(hash
);
678 RredMethod() : pkgAcqMethod("2.0",SingleInstance
| SendConfig
) {}
681 int main(int argc
, char **argv
)
684 bool just_diff
= true;
692 if (argc
> 1 && strcmp(argv
[1], "-f") == 0) {
699 for (; i
< argc
; i
++) {
701 p
= fopen(argv
[i
], "r");
710 patch
.write_diff(stdout
);
716 patch
.apply_against_file(out
, inp
);