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