]>
Commit | Line | Data |
---|---|---|
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 | ||
14 | const char *Prog; | |
15 | ||
16 | class 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 |
39 | int 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 | ||
146 | int 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? | |
172 | bool 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 | ||
232 | int main(int argc, char *argv[]) | |
233 | { | |
234 | RredMethod Mth; | |
235 | ||
236 | Prog = strrchr(argv[0],'/'); | |
237 | Prog++; | |
238 | ||
239 | return Mth.Run(); | |
240 | } |