]> git.saurik.com Git - apt.git/blob - methods/rred.cc
f4d77c64c16e032972ded8a8b714a7c35196511e
[apt.git] / methods / rred.cc
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>
6
7 #include <sys/stat.h>
8 #include <unistd.h>
9 #include <utime.h>
10 #include <stdio.h>
11 #include <errno.h>
12 #include <apti18n.h>
13
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
20 * output).
21 * */
22
23 const char *Prog;
24
25 class RredMethod : public pkgAcqMethod
26 {
27 // the size of this doesn't really matter (except for performance)
28 const static int BUF_SIZE = 1024;
29 // the ed commands
30 enum Mode {MODE_CHANGED, MODE_DELETED, MODE_ADDED};
31 // return values
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);
37 // apply a patch file
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);
41
42 public:
43
44 RredMethod() : pkgAcqMethod("1.1",SingleInstance | SendConfig) {};
45 };
46
47 int RredMethod::ed_rec(FILE *ed_cmds, FILE *in_file, FILE *out_file, int line,
48 char *buffer, unsigned int bufsize, Hashes *hash) {
49 int pos;
50 int startline;
51 int stopline;
52 int mode;
53 int written;
54 char *idx;
55
56 /* get the current command and parse it*/
57 if (fgets(buffer, bufsize, ed_cmds) == NULL) {
58 return line;
59 }
60 startline = strtol(buffer, &idx, 10);
61 if (startline < line) {
62 return ED_ORDERING;
63 }
64 if (*idx == ',') {
65 idx++;
66 stopline = strtol(idx, &idx, 10);
67 }
68 else {
69 stopline = startline;
70 }
71 if (*idx == 'c') {
72 mode = MODE_CHANGED;
73 }
74 else if (*idx == 'a') {
75 mode = MODE_ADDED;
76 }
77 else if (*idx == 'd') {
78 mode = MODE_DELETED;
79 }
80 else {
81 return ED_PARSER;
82 }
83 /* get the current position */
84 pos = ftell(ed_cmds);
85 /* if this is add or change then go to the next full stop */
86 if ((mode == MODE_CHANGED) || (mode == MODE_ADDED)) {
87 do {
88 fgets(buffer, bufsize, ed_cmds);
89 while ((strlen(buffer) == (bufsize - 1))
90 && (buffer[bufsize - 2] != '\n')) {
91 fgets(buffer, bufsize, ed_cmds);
92 buffer[0] = ' ';
93 }
94 } while (strncmp(buffer, ".", 1) != 0);
95 }
96 /* do the recursive call */
97 line = ed_rec(ed_cmds, in_file, out_file, line, buffer, bufsize,
98 hash);
99 /* pass on errors */
100 if (line < 0) {
101 return line;
102 }
103 /* apply our hunk */
104 fseek(ed_cmds, pos, SEEK_SET);
105 /* first wind to the current position */
106 if (mode != MODE_ADDED) {
107 startline -= 1;
108 }
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);
118 }
119 line++;
120 }
121 /* include from ed script */
122 if ((mode == MODE_ADDED) || (mode == MODE_CHANGED)) {
123 do {
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);
133 }
134 }
135 else {
136 break;
137 }
138 } while (1);
139 }
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);
147 }
148 line++;
149 }
150 }
151 return line;
152 }
153
154 int RredMethod::ed_file(FILE *ed_cmds, FILE *in_file, FILE *out_file,
155 Hashes *hash) {
156 char buffer[BUF_SIZE];
157 int result;
158 int written;
159
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,
162 hash);
163
164 /* read the rest from infile */
165 if (result > 0) {
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);
169 }
170 }
171 else {
172 return ED_FAILURE;
173 }
174 return ED_OK;
175 }
176
177
178 bool RredMethod::Fetch(FetchItem *Itm)
179 {
180 URI Get = Itm->Uri;
181 string Path = Get.Host + Get.Path; // To account for relative paths
182 // Path contains the filename to patch
183 FetchResult Res;
184 Res.Filename = Itm->DestFile;
185 URIStart(Res);
186 // Res.Filename the destination filename
187
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);
193 To.EraseOnFailure();
194 if (_error->PendingError() == true)
195 return false;
196
197 Hashes Hash;
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"));
204 return false;
205 }
206
207 // write out the result
208 fflush(fFrom);
209 fflush(fPatch);
210 fflush(fTo);
211 From.Close();
212 Patch.Close();
213 To.Close();
214
215 // Transfer the modification times
216 struct stat Buf;
217 if (stat(Path.c_str(),&Buf) != 0)
218 return _error->Errno("stat",_("Failed to stat"));
219
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"));
225
226 if (stat(Itm->DestFile.c_str(),&Buf) != 0)
227 return _error->Errno("stat",_("Failed to stat"));
228
229 // return done
230 Res.LastModified = Buf.st_mtime;
231 Res.Size = Buf.st_size;
232 Res.TakeHashes(Hash);
233 URIDone(Res);
234
235 return true;
236 }
237
238 int main(int argc, char *argv[])
239 {
240 RredMethod Mth;
241
242 Prog = strrchr(argv[0],'/');
243 Prog++;
244
245 return Mth.Run();
246 }