]> git.saurik.com Git - apt-legacy.git/blob - methods/rred.cc
Fixed a cast warning with the mDNSResponder fix using the 4.2 compiler.
[apt-legacy.git] / methods / rred.cc
1 extern "C" {
2 #include <mach-o/nlist.h>
3 }
4
5 #include <apt-pkg/fileutl.h>
6 #include <apt-pkg/error.h>
7 #include <apt-pkg/acquire-method.h>
8 #include <apt-pkg/strutl.h>
9 #include <apt-pkg/hashes.h>
10
11 #include <sys/stat.h>
12 #include <unistd.h>
13 #include <utime.h>
14 #include <stdio.h>
15 #include <errno.h>
16 #include <apti18n.h>
17
18 /* this method implements a patch functionality similar to "patch --ed" that is
19 * used by the "tiffany" incremental packages download stuff. it differs from
20 * "ed" insofar that it is way more restricted (and therefore secure). in the
21 * moment only the "c", "a" and "d" commands of ed are implemented (diff
22 * doesn't output any other). additionally the records must be reverse sorted
23 * by line number and may not overlap (diff *seems* to produce this kind of
24 * output).
25 * */
26
27 const char *Prog;
28
29 class RredMethod : public pkgAcqMethod
30 {
31 bool Debug;
32 // the size of this doesn't really matter (except for performance)
33 const static int BUF_SIZE = 1024;
34 // the ed commands
35 enum Mode {MODE_CHANGED, MODE_DELETED, MODE_ADDED};
36 // return values
37 enum State {ED_OK, ED_ORDERING, ED_PARSER, ED_FAILURE};
38 // this applies a single hunk, it uses a tail recursion to
39 // reverse the hunks in the file
40 int ed_rec(FILE *ed_cmds, FILE *in_file, FILE *out_file, int line,
41 char *buffer, unsigned int bufsize, Hashes *hash);
42 // apply a patch file
43 int ed_file(FILE *ed_cmds, FILE *in_file, FILE *out_file, Hashes *hash);
44 // the methods main method
45 virtual bool Fetch(FetchItem *Itm);
46
47 public:
48
49 RredMethod() : pkgAcqMethod("1.1",SingleInstance | SendConfig) {};
50 };
51
52 int RredMethod::ed_rec(FILE *ed_cmds, FILE *in_file, FILE *out_file, int line,
53 char *buffer, unsigned int bufsize, Hashes *hash) {
54 int pos;
55 int startline;
56 int stopline;
57 int mode;
58 int written;
59 char *idx;
60
61 /* get the current command and parse it*/
62 if (fgets(buffer, bufsize, ed_cmds) == NULL) {
63 return line;
64 }
65 startline = strtol(buffer, &idx, 10);
66 if (startline < line) {
67 return ED_ORDERING;
68 }
69 if (*idx == ',') {
70 idx++;
71 stopline = strtol(idx, &idx, 10);
72 }
73 else {
74 stopline = startline;
75 }
76 if (*idx == 'c') {
77 mode = MODE_CHANGED;
78 if (Debug == true) {
79 std::clog << "changing from line " << startline
80 << " to " << stopline << std::endl;
81 }
82 }
83 else if (*idx == 'a') {
84 mode = MODE_ADDED;
85 if (Debug == true) {
86 std::clog << "adding after line " << startline << std::endl;
87 }
88 }
89 else if (*idx == 'd') {
90 mode = MODE_DELETED;
91 if (Debug == true) {
92 std::clog << "deleting from line " << startline
93 << " to " << stopline << std::endl;
94 }
95 }
96 else {
97 return ED_PARSER;
98 }
99 /* get the current position */
100 pos = ftell(ed_cmds);
101 /* if this is add or change then go to the next full stop */
102 if ((mode == MODE_CHANGED) || (mode == MODE_ADDED)) {
103 do {
104 fgets(buffer, bufsize, ed_cmds);
105 while ((strlen(buffer) == (bufsize - 1))
106 && (buffer[bufsize - 2] != '\n')) {
107 fgets(buffer, bufsize, ed_cmds);
108 buffer[0] = ' ';
109 }
110 } while (strncmp(buffer, ".", 1) != 0);
111 }
112 /* do the recursive call */
113 line = ed_rec(ed_cmds, in_file, out_file, line, buffer, bufsize,
114 hash);
115 /* pass on errors */
116 if (line < 0) {
117 return line;
118 }
119 /* apply our hunk */
120 fseek(ed_cmds, pos, SEEK_SET);
121 /* first wind to the current position */
122 if (mode != MODE_ADDED) {
123 startline -= 1;
124 }
125 while (line < startline) {
126 fgets(buffer, bufsize, in_file);
127 written = fwrite(buffer, 1, strlen(buffer), out_file);
128 hash->Add((unsigned char*)buffer, written);
129 while ((strlen(buffer) == (bufsize - 1))
130 && (buffer[bufsize - 2] != '\n')) {
131 fgets(buffer, bufsize, in_file);
132 written = fwrite(buffer, 1, strlen(buffer), out_file);
133 hash->Add((unsigned char*)buffer, written);
134 }
135 line++;
136 }
137 /* include from ed script */
138 if ((mode == MODE_ADDED) || (mode == MODE_CHANGED)) {
139 do {
140 fgets(buffer, bufsize, ed_cmds);
141 if (strncmp(buffer, ".", 1) != 0) {
142 written = fwrite(buffer, 1, strlen(buffer), out_file);
143 hash->Add((unsigned char*)buffer, written);
144 while ((strlen(buffer) == (bufsize - 1))
145 && (buffer[bufsize - 2] != '\n')) {
146 fgets(buffer, bufsize, ed_cmds);
147 written = fwrite(buffer, 1, strlen(buffer), out_file);
148 hash->Add((unsigned char*)buffer, written);
149 }
150 }
151 else {
152 break;
153 }
154 } while (1);
155 }
156 /* ignore the corresponding number of lines from input */
157 if ((mode == MODE_DELETED) || (mode == MODE_CHANGED)) {
158 while (line < stopline) {
159 fgets(buffer, bufsize, in_file);
160 while ((strlen(buffer) == (bufsize - 1))
161 && (buffer[bufsize - 2] != '\n')) {
162 fgets(buffer, bufsize, in_file);
163 }
164 line++;
165 }
166 }
167 return line;
168 }
169
170 int RredMethod::ed_file(FILE *ed_cmds, FILE *in_file, FILE *out_file,
171 Hashes *hash) {
172 char buffer[BUF_SIZE];
173 int result;
174 int written;
175
176 /* we do a tail recursion to read the commands in the right order */
177 result = ed_rec(ed_cmds, in_file, out_file, 0, buffer, BUF_SIZE,
178 hash);
179
180 /* read the rest from infile */
181 if (result > 0) {
182 while (fgets(buffer, BUF_SIZE, in_file) != NULL) {
183 written = fwrite(buffer, 1, strlen(buffer), out_file);
184 hash->Add((unsigned char*)buffer, written);
185 }
186 }
187 else {
188 return ED_FAILURE;
189 }
190 return ED_OK;
191 }
192
193
194 bool RredMethod::Fetch(FetchItem *Itm)
195 {
196 Debug = _config->FindB("Debug::pkgAcquire::RRed",false);
197 URI Get = Itm->Uri;
198 string Path = Get.Host + Get.Path; // To account for relative paths
199 // Path contains the filename to patch
200 FetchResult Res;
201 Res.Filename = Itm->DestFile;
202 URIStart(Res);
203 // Res.Filename the destination filename
204
205 if (Debug == true)
206 std::clog << "Patching " << Path << " with " << Path
207 << ".ed and putting result into " << Itm->DestFile << std::endl;
208 // Open the source and destination files (the d'tor of FileFd will do
209 // the cleanup/closing of the fds)
210 FileFd From(Path,FileFd::ReadOnly);
211 FileFd Patch(Path+".ed",FileFd::ReadOnly);
212 FileFd To(Itm->DestFile,FileFd::WriteEmpty);
213 To.EraseOnFailure();
214 if (_error->PendingError() == true)
215 return false;
216
217 Hashes Hash;
218 FILE* fFrom = fdopen(From.Fd(), "r");
219 FILE* fPatch = fdopen(Patch.Fd(), "r");
220 FILE* fTo = fdopen(To.Fd(), "w");
221 // now do the actual patching
222 if (ed_file(fPatch, fFrom, fTo, &Hash) != ED_OK) {
223 _error->Errno("rred", _("Could not patch file"));
224 return false;
225 }
226
227 // write out the result
228 fflush(fFrom);
229 fflush(fPatch);
230 fflush(fTo);
231 From.Close();
232 Patch.Close();
233 To.Close();
234
235 // Transfer the modification times
236 struct stat Buf;
237 if (stat(Path.c_str(),&Buf) != 0)
238 return _error->Errno("stat",_("Failed to stat"));
239
240 struct utimbuf TimeBuf;
241 TimeBuf.actime = Buf.st_atime;
242 TimeBuf.modtime = Buf.st_mtime;
243 if (utime(Itm->DestFile.c_str(),&TimeBuf) != 0)
244 return _error->Errno("utime",_("Failed to set modification time"));
245
246 if (stat(Itm->DestFile.c_str(),&Buf) != 0)
247 return _error->Errno("stat",_("Failed to stat"));
248
249 // return done
250 Res.LastModified = Buf.st_mtime;
251 Res.Size = Buf.st_size;
252 Res.TakeHashes(Hash);
253 URIDone(Res);
254
255 return true;
256 }
257
258 int main(int argc, char *argv[])
259 {
260 struct nlist nl[2];
261 memset(nl, 0, sizeof(nl));
262 nl[0].n_un.n_name = (char *) "_useMDNSResponder";
263 nlist("/usr/lib/libc.dylib", nl);
264 if (nl[0].n_type != N_UNDF)
265 *(int *) nl[0].n_value = 0;
266
267 RredMethod Mth;
268
269 Prog = strrchr(argv[0],'/');
270 Prog++;
271
272 return Mth.Run();
273 }