]> git.saurik.com Git - apt.git/blame - methods/rred.cc
* bugfix/updates in the rred.cc code (big thanks for helping Robert!)
[apt.git] / methods / rred.cc
CommitLineData
2e178d1c
MV
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
14const char *Prog;
15
16class RredMethod : public pkgAcqMethod
17{
18 virtual bool Fetch(FetchItem *Itm);
19
20 public:
21
22 RredMethod() : pkgAcqMethod("1.1",SingleInstance | SendConfig) {};
23
24};
25
26#define BUF_SIZE (1024)
27
3de9ff77 28// XXX use enums
2e178d1c
MV
29#define MODE_CHANGED 0
30#define MODE_DELETED 1
31#define MODE_ADDED 2
32
3de9ff77 33#define ED_OK 0
2e178d1c
MV
34#define ED_ORDERING 1
35#define ED_PARSER 2
36#define ED_FAILURE 3
37
3de9ff77 38// XXX someone better go out and understand the error reporting/handling here...
2e178d1c
MV
39int ed_rec(FILE *ed_cmds, FILE *in_file, FILE *out_file, int line,
40 char *buffer, unsigned int bufsize, Hashes *hash) {
41 int pos;
42 int startline;
43 int stopline;
44 int mode;
45 int written;
46 char *idx;
47
48 /* get the current command and parse it*/
49 if (fgets(buffer, bufsize, ed_cmds) == NULL) {
50 return line;
51 }
52 startline = strtol(buffer, &idx, 10);
53 if (startline < line) {
54 return ED_ORDERING;
55 }
56 if (*idx == ',') {
57 idx++;
58 stopline = strtol(idx, &idx, 10);
59 }
60 else {
61 stopline = startline;
62 }
63 if (*idx == 'c') {
64 mode = MODE_CHANGED;
65 }
66 else if (*idx == 'a') {
67 mode = MODE_ADDED;
68 }
69 else if (*idx == 'd') {
70 mode = MODE_DELETED;
71 }
72 else {
73 return ED_PARSER;
74 }
75 /* get the current position */
76 pos = ftell(ed_cmds);
77 /* if this is add or change then go to the next full stop */
78 if ((mode == MODE_CHANGED) || (mode == MODE_ADDED)) {
79 do {
80 fgets(buffer, bufsize, ed_cmds);
81 while ((strlen(buffer) == (bufsize - 1))
82 && (buffer[bufsize - 2] != '\n')) {
83 fgets(buffer, bufsize, ed_cmds);
84 buffer[0] = ' ';
85 }
86 } while (strncmp(buffer, ".", 1) != 0);
87 }
88 /* do the recursive call */
89 line = ed_rec(ed_cmds, in_file, out_file, line, buffer, bufsize,
90 hash);
91 /* pass on errors */
92 if (line < 0) {
93 return line;
94 }
95 /* apply our hunk */
96 fseek(ed_cmds, pos, SEEK_SET);
97 /* first wind to the current position */
98 if (mode != MODE_ADDED) {
99 startline -= 1;
100 }
101 while (line < startline) {
102 fgets(buffer, bufsize, in_file);
103 written = fwrite(buffer, 1, strlen(buffer), out_file);
104 hash->Add((unsigned char*)buffer, written);
105 while ((strlen(buffer) == (bufsize - 1))
106 && (buffer[bufsize - 2] != '\n')) {
107 fgets(buffer, bufsize, in_file);
108 written = fwrite(buffer, 1, strlen(buffer), out_file);
109 hash->Add((unsigned char*)buffer, written);
110 }
111 line++;
112 }
113 /* include from ed script */
114 if ((mode == MODE_ADDED) || (mode == MODE_CHANGED)) {
115 do {
116 fgets(buffer, bufsize, ed_cmds);
117 if (strncmp(buffer, ".", 1) != 0) {
118 written = fwrite(buffer, 1, strlen(buffer), out_file);
119 hash->Add((unsigned char*)buffer, written);
120 while ((strlen(buffer) == (bufsize - 1))
121 && (buffer[bufsize - 2] != '\n')) {
122 fgets(buffer, bufsize, ed_cmds);
123 written = fwrite(buffer, 1, strlen(buffer), out_file);
124 hash->Add((unsigned char*)buffer, written);
125 }
126 }
127 else {
128 break;
129 }
130 } while (1);
131 }
132 /* ignore the corresponding number of lines from input */
133 if ((mode == MODE_DELETED) || (mode == MODE_CHANGED)) {
134 while (line < stopline) {
135 fgets(buffer, bufsize, in_file);
136 while ((strlen(buffer) == (bufsize - 1))
137 && (buffer[bufsize - 2] != '\n')) {
138 fgets(buffer, bufsize, in_file);
139 }
140 line++;
141 }
142 }
143 return line;
144}
145
146int ed_file(FILE *ed_cmds, FILE *in_file, FILE *out_file, Hashes *hash) {
147 char buffer[BUF_SIZE];
148 int result;
149 int written;
150
151 /* we do a tail recursion to read the commands in the right order */
152 result = ed_rec(ed_cmds, in_file, out_file, 0, buffer, BUF_SIZE,
153 hash);
154
155 /* read the rest from infile */
156 if (result > 0) {
157 while (fgets(buffer, BUF_SIZE, in_file) != NULL) {
158 written = fwrite(buffer, 1, strlen(buffer), out_file);
3de9ff77 159 hash->Add((unsigned char*)buffer, written);
2e178d1c
MV
160 }
161 }
162 else {
163 // XXX better error handling
164 fprintf(stderr, "Error: %i\n", result);
165 return ED_FAILURE;
166 }
3de9ff77 167 return ED_OK;
2e178d1c
MV
168}
169
170
171// XXX do we need modification times as well?
172bool RredMethod::Fetch(FetchItem *Itm)
173{
174 URI Get = Itm->Uri;
175 string Path = Get.Host + Get.Path; // To account for relative paths
176 // Path contains the filename to patch
177 FetchResult Res;
178 Res.Filename = Itm->DestFile;
179 URIStart(Res);
180 // Res.Filename the destination filename
4a0a786f 181
59a704f0
MV
182 // Open the source and destination files (the d'tor of FileFd will do
183 // the cleanup/closing of the fds)
2e178d1c
MV
184 FileFd From(Path,FileFd::ReadOnly);
185 FileFd Patch(Path+".ed",FileFd::ReadOnly);
186 FileFd To(Itm->DestFile,FileFd::WriteEmpty);
187 To.EraseOnFailure();
188 if (_error->PendingError() == true)
189 return false;
190
191 Hashes Hash;
192 FILE* fFrom = fdopen(From.Fd(), "r");
193 FILE* fPatch = fdopen(Patch.Fd(), "r");
194 FILE* fTo = fdopen(To.Fd(), "w");
195 // now do the actual patching
3de9ff77
MV
196 if (ed_file(fPatch, fFrom, fTo, &Hash) != ED_OK) {
197 return false;
198 }
199
200 // write out the result
201 fflush(fFrom);
202 fflush(fPatch);
203 fflush(fTo);
204 From.Close();
205 Patch.Close();
206 To.Close();
207
208 // Transfer the modification times
209 struct stat Buf;
210 if (stat(Path.c_str(),&Buf) != 0)
211 return _error->Errno("stat",_("Failed to stat"));
212
213 struct utimbuf TimeBuf;
214 TimeBuf.actime = Buf.st_atime;
215 TimeBuf.modtime = Buf.st_mtime;
216 if (utime(Itm->DestFile.c_str(),&TimeBuf) != 0)
217 return _error->Errno("utime",_("Failed to set modification time"));
218
219 if (stat(Itm->DestFile.c_str(),&Buf) != 0)
220 return _error->Errno("stat",_("Failed to stat"));
221
222 // return done
223 Res.LastModified = Buf.st_mtime;
224 Res.Size = Buf.st_size;
2e178d1c
MV
225 Res.TakeHashes(Hash);
226 URIDone(Res);
3de9ff77 227
2e178d1c
MV
228 return true;
229}
230 /*}}}*/
231
232int main(int argc, char *argv[])
233{
234 RredMethod Mth;
235
236 Prog = strrchr(argv[0],'/');
237 Prog++;
238
239 return Mth.Run();
240}