]>
git.saurik.com Git - apt.git/blob - methods/rred.cc
f4d77c64c16e032972ded8a8b714a7c35196511e
1 #include <apt-pkg/fileutl.h>
2 #include <apt-pkg/error.h>
3 #include <apt-pkg/acquire-method.h>
4 #include <apt-pkg/strutl.h>
5 #include <apt-pkg/hashes.h>
14 /* this method implements a patch functionality similar to "patch --ed" that is
15 * used by the "tiffany" incremental packages download stuff. it differs from
16 * "ed" insofar that it is way more restricted (and therefore secure). in the
17 * moment only the "c", "a" and "d" commands of ed are implemented (diff
18 * doesn't output any other). additionally the records must be reverse sorted
19 * by line number and may not overlap (diff *seems* to produce this kind of
25 class RredMethod
: public pkgAcqMethod
27 // the size of this doesn't really matter (except for performance)
28 const static int BUF_SIZE
= 1024;
30 enum Mode
{MODE_CHANGED
, MODE_DELETED
, MODE_ADDED
};
32 enum State
{ED_OK
, ED_ORDERING
, ED_PARSER
, ED_FAILURE
};
33 // this applies a single hunk, it uses a tail recursion to
34 // reverse the hunks in the file
35 int ed_rec(FILE *ed_cmds
, FILE *in_file
, FILE *out_file
, int line
,
36 char *buffer
, unsigned int bufsize
, Hashes
*hash
);
38 int ed_file(FILE *ed_cmds
, FILE *in_file
, FILE *out_file
, Hashes
*hash
);
39 // the methods main method
40 virtual bool Fetch(FetchItem
*Itm
);
44 RredMethod() : pkgAcqMethod("1.1",SingleInstance
| SendConfig
) {};
47 int RredMethod::ed_rec(FILE *ed_cmds
, FILE *in_file
, FILE *out_file
, int line
,
48 char *buffer
, unsigned int bufsize
, Hashes
*hash
) {
56 /* get the current command and parse it*/
57 if (fgets(buffer
, bufsize
, ed_cmds
) == NULL
) {
60 startline
= strtol(buffer
, &idx
, 10);
61 if (startline
< line
) {
66 stopline
= strtol(idx
, &idx
, 10);
74 else if (*idx
== 'a') {
77 else if (*idx
== 'd') {
83 /* get the current position */
85 /* if this is add or change then go to the next full stop */
86 if ((mode
== MODE_CHANGED
) || (mode
== MODE_ADDED
)) {
88 fgets(buffer
, bufsize
, ed_cmds
);
89 while ((strlen(buffer
) == (bufsize
- 1))
90 && (buffer
[bufsize
- 2] != '\n')) {
91 fgets(buffer
, bufsize
, ed_cmds
);
94 } while (strncmp(buffer
, ".", 1) != 0);
96 /* do the recursive call */
97 line
= ed_rec(ed_cmds
, in_file
, out_file
, line
, buffer
, bufsize
,
104 fseek(ed_cmds
, pos
, SEEK_SET
);
105 /* first wind to the current position */
106 if (mode
!= MODE_ADDED
) {
109 while (line
< startline
) {
110 fgets(buffer
, bufsize
, in_file
);
111 written
= fwrite(buffer
, 1, strlen(buffer
), out_file
);
112 hash
->Add((unsigned char*)buffer
, written
);
113 while ((strlen(buffer
) == (bufsize
- 1))
114 && (buffer
[bufsize
- 2] != '\n')) {
115 fgets(buffer
, bufsize
, in_file
);
116 written
= fwrite(buffer
, 1, strlen(buffer
), out_file
);
117 hash
->Add((unsigned char*)buffer
, written
);
121 /* include from ed script */
122 if ((mode
== MODE_ADDED
) || (mode
== MODE_CHANGED
)) {
124 fgets(buffer
, bufsize
, ed_cmds
);
125 if (strncmp(buffer
, ".", 1) != 0) {
126 written
= fwrite(buffer
, 1, strlen(buffer
), out_file
);
127 hash
->Add((unsigned char*)buffer
, written
);
128 while ((strlen(buffer
) == (bufsize
- 1))
129 && (buffer
[bufsize
- 2] != '\n')) {
130 fgets(buffer
, bufsize
, ed_cmds
);
131 written
= fwrite(buffer
, 1, strlen(buffer
), out_file
);
132 hash
->Add((unsigned char*)buffer
, written
);
140 /* ignore the corresponding number of lines from input */
141 if ((mode
== MODE_DELETED
) || (mode
== MODE_CHANGED
)) {
142 while (line
< stopline
) {
143 fgets(buffer
, bufsize
, in_file
);
144 while ((strlen(buffer
) == (bufsize
- 1))
145 && (buffer
[bufsize
- 2] != '\n')) {
146 fgets(buffer
, bufsize
, in_file
);
154 int RredMethod::ed_file(FILE *ed_cmds
, FILE *in_file
, FILE *out_file
,
156 char buffer
[BUF_SIZE
];
160 /* we do a tail recursion to read the commands in the right order */
161 result
= ed_rec(ed_cmds
, in_file
, out_file
, 0, buffer
, BUF_SIZE
,
164 /* read the rest from infile */
166 while (fgets(buffer
, BUF_SIZE
, in_file
) != NULL
) {
167 written
= fwrite(buffer
, 1, strlen(buffer
), out_file
);
168 hash
->Add((unsigned char*)buffer
, written
);
178 bool RredMethod::Fetch(FetchItem
*Itm
)
181 string Path
= Get
.Host
+ Get
.Path
; // To account for relative paths
182 // Path contains the filename to patch
184 Res
.Filename
= Itm
->DestFile
;
186 // Res.Filename the destination filename
188 // Open the source and destination files (the d'tor of FileFd will do
189 // the cleanup/closing of the fds)
190 FileFd
From(Path
,FileFd::ReadOnly
);
191 FileFd
Patch(Path
+".ed",FileFd::ReadOnly
);
192 FileFd
To(Itm
->DestFile
,FileFd::WriteEmpty
);
194 if (_error
->PendingError() == true)
198 FILE* fFrom
= fdopen(From
.Fd(), "r");
199 FILE* fPatch
= fdopen(Patch
.Fd(), "r");
200 FILE* fTo
= fdopen(To
.Fd(), "w");
201 // now do the actual patching
202 if (ed_file(fPatch
, fFrom
, fTo
, &Hash
) != ED_OK
) {
203 _error
->Errno("rred", _("Could not patch file"));
207 // write out the result
215 // Transfer the modification times
217 if (stat(Path
.c_str(),&Buf
) != 0)
218 return _error
->Errno("stat",_("Failed to stat"));
220 struct utimbuf TimeBuf
;
221 TimeBuf
.actime
= Buf
.st_atime
;
222 TimeBuf
.modtime
= Buf
.st_mtime
;
223 if (utime(Itm
->DestFile
.c_str(),&TimeBuf
) != 0)
224 return _error
->Errno("utime",_("Failed to set modification time"));
226 if (stat(Itm
->DestFile
.c_str(),&Buf
) != 0)
227 return _error
->Errno("stat",_("Failed to stat"));
230 Res
.LastModified
= Buf
.st_mtime
;
231 Res
.Size
= Buf
.st_size
;
232 Res
.TakeHashes(Hash
);
238 int main(int argc
, char *argv
[])
242 Prog
= strrchr(argv
[0],'/');