1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45#define FOR_patch
46#include "toys.h"
47
48GLOBALS(
49 char *i, *d;
50 long p, g, F;
51
52 void *current_hunk;
53 long oldline, oldlen, newline, newlen, linenum, outnum;
54 int context, state, filein, fileout, filepatch, hunknum;
55 char *tempname;
56)
57
58
59
60
61
62
63
64
65static void do_line(void *data)
66{
67 struct double_list *dlist = data;
68
69 TT.outnum++;
70 if (TT.state>1)
71 if (0>dprintf(TT.state==2 ? 2 : TT.fileout,"%s\n",dlist->data+(TT.state>3)))
72 perror_exit("write");
73
74 if (FLAG(x))
75 fprintf(stderr, "DO %d %ld: %s\n", TT.state, TT.outnum, dlist->data);
76
77 llist_free_double(data);
78}
79
80static void finish_oldfile(void)
81{
82 if (TT.tempname) replace_tempfile(TT.filein, TT.fileout, &TT.tempname);
83 TT.fileout = TT.filein = -1;
84}
85
86static void fail_hunk(void)
87{
88 if (!TT.current_hunk) return;
89
90 fprintf(stderr, "Hunk %d FAILED %ld/%ld.\n",
91 TT.hunknum, TT.oldline, TT.newline);
92 toys.exitval = 1;
93
94
95
96
97 TT.state = 2;
98 llist_traverse(TT.current_hunk, do_line);
99 TT.current_hunk = NULL;
100 if (!FLAG(dry_run)) delete_tempfile(TT.filein, TT.fileout, &TT.tempname);
101 TT.state = 0;
102}
103
104
105static int loosecmp(char *aa, char *bb)
106{
107 int a = 0, b = 0;
108
109 for (;;) {
110 while (isspace(aa[a])) a++;
111 while (isspace(bb[b])) b++;
112 if (aa[a] != bb[b]) return 1;
113 if (!aa[a]) return 0;
114 a++, b++;
115 }
116}
117
118
119
120
121
122
123
124
125static int apply_one_hunk(void)
126{
127 struct double_list *plist, *buf = 0, *check;
128 int matcheof, trail = 0, reverse = FLAG(R), backwarn = 0, allfuzz, fuzz, i;
129 int (*lcmp)(char *aa, char *bb) = FLAG(l) ? (void *)loosecmp : (void *)strcmp;
130
131
132 dlist_terminate(TT.current_hunk);
133 for (fuzz = 0, plist = TT.current_hunk; plist; plist = plist->next) {
134 char c = *plist->data, *s;
135
136 if (c==' ') trail++;
137 else trail = 0;
138
139
140
141
142 if (c==' ' || c=="-+"[reverse]) {
143 s = plist->data+1;
144 while (isspace(*s)) s++;
145 if (*s && s[1] && !isspace(s[1])) fuzz++;
146 }
147
148 if (FLAG(x)) fprintf(stderr, "HUNK:%s\n", plist->data);
149 }
150 matcheof = !trail || trail < TT.context;
151 if (fuzz<2) allfuzz = 0;
152 else allfuzz = FLAG(F) ? TT.F : (TT.context ? TT.context-1 : 0);
153
154 if (FLAG(x)) fprintf(stderr,"MATCHEOF=%c\n", matcheof ? 'Y' : 'N');
155
156
157
158 plist = TT.current_hunk;
159 fuzz = 0;
160 for (;;) {
161 char *data = get_line(TT.filein);
162
163
164
165 while (plist && *plist->data == "+-"[reverse]) {
166 if (data && !lcmp(data, plist->data+1))
167 if (!backwarn) backwarn = TT.linenum;
168 plist = plist->next;
169 }
170
171
172 if (!data) {
173 if (FLAG(x)) fprintf(stderr, "INEOF\n");
174
175
176 if (!plist && matcheof) break;
177
178 if (backwarn && !FLAG(s))
179 fprintf(stderr, "Possibly reversed hunk %d at %ld\n",
180 TT.hunknum, TT.linenum);
181
182
183 fail_hunk();
184 goto done;
185 } else {
186 TT.linenum++;
187 if (FLAG(x)) fprintf(stderr, "IN: %s\n", data);
188 }
189 check = dlist_add(&buf, data);
190
191
192
193
194 for (i = 0;; i++) {
195 if (!plist || lcmp(check->data, plist->data+1)) {
196
197
198 if (plist && *plist->data == ' ' && fuzz<allfuzz) {
199 if (FLAG(x))
200 fprintf(stderr, "FUZZED: %ld %s\n", TT.linenum, plist->data);
201 fuzz++;
202
203 goto fuzzed;
204 }
205
206 if (FLAG(x)) {
207 int bug = 0;
208
209 if (!plist) fprintf(stderr, "NULL plist\n");
210 else {
211 while (plist->data[bug] == check->data[bug]) bug++;
212 fprintf(stderr, "NOT(%d:%d!=%d): %s\n", bug, plist->data[bug],
213 check->data[bug], plist->data);
214 }
215 }
216
217
218 if (!TT.context || trail>TT.context) {
219 fail_hunk();
220 goto done;
221 }
222
223
224 TT.state = 3;
225 do_line(check = dlist_pop(&buf));
226 plist = TT.current_hunk;
227 fuzz = 0;
228
229
230 if (!buf) break;
231 check = buf;
232 } else {
233 if (FLAG(x)) fprintf(stderr, "MAYBE: %s\n", plist->data);
234fuzzed:
235
236 plist = plist->next;
237 if (!plist && !matcheof) goto out;
238 check = check->next;
239 if (check == buf) break;
240 }
241 }
242 }
243out:
244
245 TT.state = "-+"[reverse];
246 while ((plist = dlist_pop(&TT.current_hunk))) {
247 if (TT.state == *plist->data || *plist->data == ' ') {
248 if (*plist->data == ' ') dprintf(TT.fileout, "%s\n", buf->data);
249 llist_free_double(dlist_pop(&buf));
250 } else dprintf(TT.fileout, "%s\n", plist->data+1);
251 llist_free_double(plist);
252 }
253 TT.current_hunk = 0;
254 TT.state = 1;
255done:
256 llist_traverse(buf, do_line);
257
258 return TT.state;
259}
260
261
262char *unquote_file(char *filename)
263{
264 char *s = filename, *t;
265
266
267 if (*s++ != '"' || !*s) return xstrdup(filename);
268
269
270 for (t = filename = xmalloc(strlen(s) + 1); *s != '"'; s++) {
271 if (!s[1]) error_exit("bad %s", filename);
272
273
274 if (*s != '\\') *t++ = *s;
275 else if (*++s >= '0' && *s < '8') {
276 *t++ = strtoul(s, &s, 8);
277 s--;
278 } else {
279 if (!(*t = unescape(*s))) *t = *s;;
280 t++;
281 }
282 }
283 *t = 0;
284
285 return filename;
286}
287
288
289
290
291
292
293
294
295
296void patch_main(void)
297{
298 int reverse = FLAG(R), state = 0, patchlinenum = 0, strip = 0;
299 char *oldname = NULL, *newname = NULL;
300
301 if (toys.optc == 2) TT.i = toys.optargs[1];
302 if (TT.i) TT.filepatch = xopenro(TT.i);
303 TT.filein = TT.fileout = -1;
304
305 if (TT.d) xchdir(TT.d);
306
307
308 for (;;) {
309 char *patchline;
310
311 patchline = get_line(TT.filepatch);
312 if (!patchline) break;
313
314
315 if (strip || !patchlinenum++) {
316 int len = strlen(patchline);
317 if (len && patchline[len-1] == '\r') {
318 if (!strip && !FLAG(s)) fprintf(stderr, "Removing DOS newlines\n");
319 strip = 1;
320 patchline[len-1]=0;
321 }
322 }
323 if (!*patchline) {
324 free(patchline);
325 patchline = xstrdup(" ");
326 }
327
328
329 if (state >= 2) {
330 if (*patchline==' ' || *patchline=='+' || *patchline=='-') {
331 dlist_add((void *)&TT.current_hunk, patchline);
332
333 if (*patchline != '+') TT.oldlen--;
334 if (*patchline != '-') TT.newlen--;
335
336
337 if (*patchline==' ' && state==2) TT.context++;
338 else state=3;
339
340
341 if (!TT.oldlen && !TT.newlen) state = apply_one_hunk();
342 continue;
343 }
344 dlist_terminate(TT.current_hunk);
345 fail_hunk();
346 state = 0;
347 continue;
348 }
349
350
351 if (!strncmp("--- ", patchline, 4) || !strncmp("+++ ", patchline, 4)) {
352 char *s, **name = &oldname;
353 int i;
354
355 if (*patchline == '+') {
356 name = &newname;
357 state = 1;
358 }
359
360 free(*name);
361 finish_oldfile();
362
363
364 for (s = patchline+4; *s && *s!='\t'; s++);
365 i = atoi(s);
366 if (i>1900 && i<=1970) *name = xstrdup("/dev/null");
367 else {
368 *s = 0;
369 *name = unquote_file(patchline+4);
370 }
371
372
373
374
375
376
377
378
379 } else if (state == 1 && !strncmp("@@ -", patchline, 4)) {
380 int i;
381 char *s = patchline+4;
382
383
384
385 TT.oldlen = TT.newlen = 1;
386 TT.oldline = strtol(s, &s, 10);
387 if (*s == ',') TT.oldlen=strtol(s+1, &s, 10);
388 TT.newline = strtol(s+2, &s, 10);
389 if (*s == ',') TT.newlen = strtol(s+1, &s, 10);
390
391 TT.context = 0;
392 state = 2;
393
394
395 if (TT.filein == -1) {
396 int oldsum, newsum, del = 0;
397 char *name;
398
399 oldsum = TT.oldline + TT.oldlen;
400 newsum = TT.newline + TT.newlen;
401
402
403
404 if (toys.optc) {
405 char **which = reverse ? &oldname : &newname;
406
407 free(*which);
408 *which = strdup(toys.optargs[0]);
409
410 toys.optflags |= FLAG_p;
411 TT.p = 0;
412 }
413
414 name = reverse ? oldname : newname;
415
416
417
418 if (!strcmp(name, "/dev/null") || !(reverse ? oldsum : newsum)) {
419 name = reverse ? newname : oldname;
420 del++;
421 }
422
423
424 for (i = 0, s = name; *s;) {
425 if (FLAG(p) && TT.p == i) break;
426 if (*s++ != '/') continue;
427 while (*s == '/') s++;
428 name = s;
429 i++;
430 }
431
432 if (del) {
433 if (!FLAG(s)) printf("removing %s\n", name);
434 xunlink(name);
435 state = 0;
436
437 } else if (!FLAG(p) || i <= TT.p) {
438
439 if ((!strcmp(oldname, "/dev/null") || !oldsum) && access(name, F_OK))
440 {
441 if (!FLAG(s)) printf("creating %s\n", name);
442 if (mkpath(name)) perror_exit("mkpath %s", name);
443 TT.filein = xcreate(name, O_CREAT|O_EXCL|O_RDWR, 0666);
444 } else {
445 if (!FLAG(s)) printf("patching %s\n", name);
446 TT.filein = xopenro(name);
447 }
448 if (FLAG(dry_run)) TT.fileout = xopen("/dev/null", O_RDWR);
449 else TT.fileout = copy_tempfile(TT.filein, name, &TT.tempname);
450 TT.linenum = TT.outnum = TT.hunknum = 0;
451 }
452 }
453
454 TT.hunknum++;
455
456 continue;
457 }
458
459
460 free(patchline);
461 }
462
463 finish_oldfile();
464
465 if (CFG_TOYBOX_FREE) {
466 close(TT.filepatch);
467 free(oldname);
468 free(newname);
469 }
470}
471