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