Merge lp:~jamesodhunt/ubuntu/trusty/patch/bug-1306412 into lp:ubuntu/trusty/patch
- Trusty (14.04)
- bug-1306412
- Merge into trusty
Proposed by
James Hunt
Status: | Rejected | ||||
---|---|---|---|---|---|
Rejected by: | Brian Murray | ||||
Proposed branch: | lp:~jamesodhunt/ubuntu/trusty/patch/bug-1306412 | ||||
Merge into: | lp:ubuntu/trusty/patch | ||||
Diff against target: |
2055 lines (+2009/-1) 6 files modified
.pc/applied-patches (+1/-0) .pc/fix-non-numeric-arg-crash.diff/src/patch.c (+1979/-0) debian/changelog (+7/-0) debian/patches/fix-non-numeric-arg-crash.diff (+19/-0) debian/patches/series (+1/-0) src/patch.c (+2/-1) |
||||
To merge this branch: | bzr merge lp:~jamesodhunt/ubuntu/trusty/patch/bug-1306412 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu branches | Pending | ||
Review via email: mp+217937@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
James Hunt (jamesodhunt) wrote : | # |
Revision history for this message
Brian Murray (brian-murray) wrote : | # |
I've already uploaded a fix for this to Trusty.
Unmerged revisions
- 14. By James Hunt
-
debian/
patches/ fix-non- numeric- arg-crash. diff: Fix crash when option
expecting a numeric given non-numeric (LP: #1306412).
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file '.pc/applied-patches' |
2 | --- .pc/applied-patches 2013-11-04 12:36:11 +0000 |
3 | +++ .pc/applied-patches 2014-05-01 16:07:46 +0000 |
4 | @@ -2,3 +2,4 @@ |
5 | 558485-backupmode |
6 | m-merge |
7 | add_manpage_time.patch |
8 | +fix-non-numeric-arg-crash.diff |
9 | |
10 | === added directory '.pc/fix-non-numeric-arg-crash.diff' |
11 | === added file '.pc/fix-non-numeric-arg-crash.diff/.timestamp' |
12 | === added directory '.pc/fix-non-numeric-arg-crash.diff/src' |
13 | === added file '.pc/fix-non-numeric-arg-crash.diff/src/patch.c' |
14 | --- .pc/fix-non-numeric-arg-crash.diff/src/patch.c 1970-01-01 00:00:00 +0000 |
15 | +++ .pc/fix-non-numeric-arg-crash.diff/src/patch.c 2014-05-01 16:07:46 +0000 |
16 | @@ -0,0 +1,1979 @@ |
17 | +/* patch - a program to apply diffs to original files */ |
18 | + |
19 | +/* Copyright (C) 1984, 1985, 1986, 1987, 1988 Larry Wall |
20 | + |
21 | + Copyright (C) 1989-1993, 1997-1999, 2002-2003, 2006, 2009-2012 Free Software |
22 | + Foundation, Inc. |
23 | + |
24 | + This program is free software: you can redistribute it and/or modify |
25 | + it under the terms of the GNU General Public License as published by |
26 | + the Free Software Foundation, either version 3 of the License, or |
27 | + (at your option) any later version. |
28 | + |
29 | + This program is distributed in the hope that it will be useful, |
30 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
31 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
32 | + GNU General Public License for more details. |
33 | + |
34 | + You should have received a copy of the GNU General Public License |
35 | + along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
36 | + |
37 | +#define XTERN |
38 | +#include <common.h> |
39 | +#undef XTERN |
40 | +#define XTERN extern |
41 | +#include <argmatch.h> |
42 | +#include <exitfail.h> |
43 | +#include <getopt.h> |
44 | +#include <inp.h> |
45 | +#include <pch.h> |
46 | +#include <quotearg.h> |
47 | +#include <util.h> |
48 | +#include <version.h> |
49 | +#include <xalloc.h> |
50 | +#include <gl_linked_list.h> |
51 | +#include <gl_xlist.h> |
52 | + |
53 | +/* procedures */ |
54 | + |
55 | +static FILE *create_output_file (char const *, int); |
56 | +static lin locate_hunk (lin); |
57 | +static bool check_line_endings (lin); |
58 | +static bool apply_hunk (struct outstate *, lin); |
59 | +static bool patch_match (lin, lin, lin, lin); |
60 | +static bool spew_output (struct outstate *, struct stat *); |
61 | +static int numeric_string (char const *, bool, char const *); |
62 | +static void cleanup (void); |
63 | +static void get_some_switches (void); |
64 | +static void init_output (struct outstate *); |
65 | +static FILE *open_outfile (char const *); |
66 | +static void init_reject (char const *); |
67 | +static void reinitialize_almost_everything (void); |
68 | +static void remove_if_needed (char const *, bool *); |
69 | +static void usage (FILE *, int) __attribute__((noreturn)); |
70 | + |
71 | +static void abort_hunk (char const *, bool, bool); |
72 | +static void abort_hunk_context (bool, bool); |
73 | +static void abort_hunk_unified (bool, bool); |
74 | + |
75 | +static void output_file (char const *, bool *, const struct stat *, char const *, |
76 | + const struct stat *, mode_t, bool); |
77 | + |
78 | +static void init_files_to_delete (void); |
79 | +static void init_files_to_output (void); |
80 | +static void delete_files (void); |
81 | +static void output_files (struct stat const *); |
82 | + |
83 | +#ifdef ENABLE_MERGE |
84 | +static bool merge; |
85 | +#else |
86 | +# define merge false |
87 | +#endif |
88 | + |
89 | +static enum diff reject_format = NO_DIFF; /* automatic */ |
90 | +static bool make_backups; |
91 | +static bool backup_if_mismatch; |
92 | +static char const *version_control; |
93 | +static char const *version_control_context; |
94 | +static bool remove_empty_files; |
95 | +static bool explicit_inname; |
96 | +static enum { RO_IGNORE, RO_WARN, RO_FAIL } read_only_behavior = RO_WARN; |
97 | + |
98 | +/* true if -R was specified on command line. */ |
99 | +static bool reverse_flag_specified; |
100 | + |
101 | +static char const *do_defines; /* symbol to patch using ifdef, ifndef, etc. */ |
102 | +static char const if_defined[] = "\n#ifdef %s\n"; |
103 | +static char const not_defined[] = "\n#ifndef %s\n"; |
104 | +static char const else_defined[] = "\n#else\n"; |
105 | +static char const end_defined[] = "\n#endif\n"; |
106 | + |
107 | +static int Argc; |
108 | +static char **Argv; |
109 | + |
110 | +static FILE *rejfp; /* reject file pointer */ |
111 | + |
112 | +static char const *patchname; |
113 | +static char *rejname; |
114 | +static char const * TMPREJNAME; |
115 | +static bool TMPREJNAME_needs_removal; |
116 | + |
117 | +static lin maxfuzz = 2; |
118 | + |
119 | +static char serrbuf[BUFSIZ]; |
120 | + |
121 | +/* Apply a set of diffs as appropriate. */ |
122 | + |
123 | +int |
124 | +main (int argc, char **argv) |
125 | +{ |
126 | + char const *val; |
127 | + bool somefailed = false; |
128 | + struct outstate outstate; |
129 | + struct stat tmpoutst; |
130 | + char numbuf[LINENUM_LENGTH_BOUND + 1]; |
131 | + bool written_to_rejname = false; |
132 | + bool apply_empty_patch = false; |
133 | + mode_t file_type; |
134 | + int outfd = -1; |
135 | + bool have_git_diff = false; |
136 | + |
137 | + exit_failure = 2; |
138 | + set_program_name (argv[0]); |
139 | + init_time (); |
140 | + |
141 | + setbuf(stderr, serrbuf); |
142 | + |
143 | + bufsize = 8 * 1024; |
144 | + buf = xmalloc (bufsize); |
145 | + |
146 | + strippath = -1; |
147 | + |
148 | + val = getenv ("QUOTING_STYLE"); |
149 | + { |
150 | + int i = val ? argmatch (val, quoting_style_args, 0, 0) : -1; |
151 | + set_quoting_style ((struct quoting_options *) 0, |
152 | + i < 0 ? shell_quoting_style : (enum quoting_style) i); |
153 | + } |
154 | + |
155 | + posixly_correct = getenv ("POSIXLY_CORRECT") != 0; |
156 | + backup_if_mismatch = ! posixly_correct; |
157 | + patch_get = ((val = getenv ("PATCH_GET")) |
158 | + ? numeric_string (val, true, "PATCH_GET value") |
159 | + : 0); |
160 | + |
161 | + val = getenv ("SIMPLE_BACKUP_SUFFIX"); |
162 | + simple_backup_suffix = val && *val ? val : ".orig"; |
163 | + |
164 | + if ((version_control = getenv ("PATCH_VERSION_CONTROL"))) |
165 | + version_control_context = "$PATCH_VERSION_CONTROL"; |
166 | + else if ((version_control = getenv ("VERSION_CONTROL"))) |
167 | + version_control_context = "$VERSION_CONTROL"; |
168 | + |
169 | + /* parse switches */ |
170 | + Argc = argc; |
171 | + Argv = argv; |
172 | + get_some_switches(); |
173 | + |
174 | + /* Make get_date() assume that context diff headers use UTC. */ |
175 | + if (set_utc) |
176 | + setenv ("TZ", "UTC", 1); |
177 | + |
178 | + if (make_backups | backup_if_mismatch) |
179 | + backup_type = get_version (version_control_context, version_control); |
180 | + |
181 | + init_backup_hash_table (); |
182 | + init_files_to_delete (); |
183 | + init_files_to_output (); |
184 | + |
185 | + init_output (&outstate); |
186 | + if (outfile) |
187 | + outstate.ofp = open_outfile (outfile); |
188 | + |
189 | + /* Make sure we clean up in case of disaster. */ |
190 | + set_signals (false); |
191 | + |
192 | + if (inname && outfile) |
193 | + { |
194 | + /* When an input and an output filename is given and the patch is |
195 | + empty, copy the input file to the output file. In this case, the |
196 | + input file must be a regular file (i.e., symlinks cannot be copied |
197 | + this way). */ |
198 | + apply_empty_patch = true; |
199 | + file_type = S_IFREG; |
200 | + inerrno = -1; |
201 | + } |
202 | + for ( |
203 | + open_patch_file (patchname); |
204 | + there_is_another_patch (! (inname || posixly_correct), &file_type) |
205 | + || apply_empty_patch; |
206 | + reinitialize_almost_everything(), |
207 | + apply_empty_patch = false |
208 | + ) { /* for each patch in patch file */ |
209 | + int hunk = 0; |
210 | + int failed = 0; |
211 | + bool mismatch = false; |
212 | + char const *outname = NULL; |
213 | + |
214 | + if (have_git_diff != pch_git_diff ()) |
215 | + { |
216 | + if (have_git_diff) |
217 | + { |
218 | + output_files (NULL); |
219 | + inerrno = -1; |
220 | + } |
221 | + have_git_diff = ! have_git_diff; |
222 | + } |
223 | + |
224 | + if (TMPREJNAME_needs_removal) |
225 | + { |
226 | + if (rejfp) |
227 | + { |
228 | + fclose (rejfp); |
229 | + rejfp = NULL; |
230 | + } |
231 | + remove_if_needed (TMPREJNAME, &TMPREJNAME_needs_removal); |
232 | + } |
233 | + if (TMPOUTNAME_needs_removal) |
234 | + { |
235 | + if (outfd != -1) |
236 | + { |
237 | + close (outfd); |
238 | + outfd = -1; |
239 | + } |
240 | + remove_if_needed (TMPOUTNAME, &TMPOUTNAME_needs_removal); |
241 | + } |
242 | + |
243 | + if (! skip_rest_of_patch && ! file_type) |
244 | + { |
245 | + say ("File %s: can't change file type from 0%o to 0%o.\n", |
246 | + quotearg (inname), |
247 | + pch_mode (reverse) & S_IFMT, |
248 | + pch_mode (! reverse) & S_IFMT); |
249 | + skip_rest_of_patch = true; |
250 | + somefailed = true; |
251 | + } |
252 | + |
253 | + if (! skip_rest_of_patch) |
254 | + { |
255 | + if (outfile) |
256 | + outname = outfile; |
257 | + else if (pch_copy () || pch_rename ()) |
258 | + outname = pch_name (! strcmp (inname, pch_name (OLD))); |
259 | + else |
260 | + outname = inname; |
261 | + } |
262 | + |
263 | + if (pch_git_diff () && ! skip_rest_of_patch) |
264 | + { |
265 | + struct stat outstat; |
266 | + int outerrno = 0; |
267 | + |
268 | + /* Try to recognize concatenated git diffs based on the SHA1 hashes |
269 | + in the headers. Will not always succeed for patches that rename |
270 | + or copy files. */ |
271 | + |
272 | + if (! strcmp (inname, outname)) |
273 | + { |
274 | + if (inerrno == -1) |
275 | + inerrno = stat_file (inname, &instat); |
276 | + outstat = instat; |
277 | + outerrno = inerrno; |
278 | + } |
279 | + else |
280 | + outerrno = stat_file (outname, &outstat); |
281 | + |
282 | + if (! outerrno) |
283 | + { |
284 | + if (has_queued_output (&outstat)) |
285 | + { |
286 | + output_files (&outstat); |
287 | + outerrno = stat_file (outname, &outstat); |
288 | + inerrno = -1; |
289 | + } |
290 | + if (! outerrno) |
291 | + set_queued_output (&outstat, true); |
292 | + } |
293 | + } |
294 | + |
295 | + if (! skip_rest_of_patch) |
296 | + { |
297 | + if (! get_input_file (inname, outname, file_type)) |
298 | + { |
299 | + skip_rest_of_patch = true; |
300 | + somefailed = true; |
301 | + } |
302 | + } |
303 | + |
304 | + if (read_only_behavior != RO_IGNORE |
305 | + && ! inerrno && ! S_ISLNK (instat.st_mode) |
306 | + && access (inname, W_OK) != 0) |
307 | + { |
308 | + say ("File %s is read-only; ", quotearg (inname)); |
309 | + if (read_only_behavior == RO_WARN) |
310 | + say ("trying to patch anyway\n"); |
311 | + else |
312 | + { |
313 | + say ("refusing to patch\n"); |
314 | + skip_rest_of_patch = true; |
315 | + somefailed = true; |
316 | + } |
317 | + } |
318 | + |
319 | + tmpoutst.st_size = -1; |
320 | + outfd = make_tempfile (&TMPOUTNAME, 'o', outname, |
321 | + O_WRONLY | binary_transput, |
322 | + instat.st_mode & S_IRWXUGO); |
323 | + TMPOUTNAME_needs_removal = true; |
324 | + if (diff_type == ED_DIFF) { |
325 | + outstate.zero_output = false; |
326 | + somefailed |= skip_rest_of_patch; |
327 | + do_ed_script (inname, TMPOUTNAME, &TMPOUTNAME_needs_removal, |
328 | + outstate.ofp); |
329 | + if (! dry_run && ! outfile && ! skip_rest_of_patch) |
330 | + { |
331 | + if (fstat (outfd, &tmpoutst) != 0) |
332 | + pfatal ("%s", TMPOUTNAME); |
333 | + outstate.zero_output = tmpoutst.st_size == 0; |
334 | + } |
335 | + close (outfd); |
336 | + outfd = -1; |
337 | + } else { |
338 | + int got_hunk; |
339 | + bool apply_anyway = merge; /* don't try to reverse when merging */ |
340 | + |
341 | + if (! skip_rest_of_patch && diff_type == GIT_BINARY_DIFF) { |
342 | + say ("File %s: git binary diffs are not supported.\n", |
343 | + quotearg (outname)); |
344 | + skip_rest_of_patch = true; |
345 | + somefailed = true; |
346 | + } |
347 | + /* initialize the patched file */ |
348 | + if (! skip_rest_of_patch && ! outfile) |
349 | + { |
350 | + init_output (&outstate); |
351 | + outstate.ofp = fdopen(outfd, binary_transput ? "wb" : "w"); |
352 | + if (! outstate.ofp) |
353 | + pfatal ("%s", TMPOUTNAME); |
354 | + } |
355 | + |
356 | + /* find out where all the lines are */ |
357 | + if (!skip_rest_of_patch) { |
358 | + scan_input (inname, file_type); |
359 | + |
360 | + if (verbosity != SILENT) |
361 | + { |
362 | + bool renamed = strcmp (inname, outname); |
363 | + |
364 | + say ("%s %s %s%c", |
365 | + dry_run ? "checking" : "patching", |
366 | + S_ISLNK (file_type) ? "symbolic link" : "file", |
367 | + quotearg (outname), renamed ? ' ' : '\n'); |
368 | + if (renamed) |
369 | + say ("(%s from %s)\n", |
370 | + pch_copy () ? "copied" : |
371 | + (pch_rename () ? "renamed" : "read"), |
372 | + inname); |
373 | + if (verbosity == VERBOSE) |
374 | + say ("Using Plan %s...\n", using_plan_a ? "A" : "B"); |
375 | + } |
376 | + } |
377 | + |
378 | + /* from here on, open no standard i/o files, because malloc */ |
379 | + /* might misfire and we can't catch it easily */ |
380 | + |
381 | + /* apply each hunk of patch */ |
382 | + while (0 < (got_hunk = another_hunk (diff_type, reverse))) |
383 | + { |
384 | + lin where = 0; /* Pacify 'gcc -Wall'. */ |
385 | + lin newwhere; |
386 | + lin fuzz = 0; |
387 | + lin mymaxfuzz; |
388 | + |
389 | + if (merge) |
390 | + { |
391 | + /* When in merge mode, don't apply with fuzz. */ |
392 | + mymaxfuzz = 0; |
393 | + } |
394 | + else |
395 | + { |
396 | + lin prefix_context = pch_prefix_context (); |
397 | + lin suffix_context = pch_suffix_context (); |
398 | + lin context = (prefix_context < suffix_context |
399 | + ? suffix_context : prefix_context); |
400 | + mymaxfuzz = (maxfuzz < context ? maxfuzz : context); |
401 | + } |
402 | + |
403 | + hunk++; |
404 | + if (!skip_rest_of_patch) { |
405 | + do { |
406 | + where = locate_hunk(fuzz); |
407 | + if (! where || fuzz || in_offset) |
408 | + mismatch = true; |
409 | + if (hunk == 1 && ! where && ! (force | apply_anyway) |
410 | + && reverse == reverse_flag_specified) { |
411 | + /* dwim for reversed patch? */ |
412 | + if (!pch_swap()) { |
413 | + say ( |
414 | +"Not enough memory to try swapped hunk! Assuming unswapped.\n"); |
415 | + continue; |
416 | + } |
417 | + /* Try again. */ |
418 | + where = locate_hunk (fuzz); |
419 | + if (where |
420 | + && (ok_to_reverse |
421 | + ("%s patch detected!", |
422 | + (reverse |
423 | + ? "Unreversed" |
424 | + : "Reversed (or previously applied)")))) |
425 | + reverse = ! reverse; |
426 | + else |
427 | + { |
428 | + /* Put it back to normal. */ |
429 | + if (! pch_swap ()) |
430 | + fatal ("lost hunk on alloc error!"); |
431 | + if (where) |
432 | + { |
433 | + apply_anyway = true; |
434 | + fuzz--; /* Undo '++fuzz' below. */ |
435 | + where = 0; |
436 | + } |
437 | + } |
438 | + } |
439 | + } while (!skip_rest_of_patch && !where |
440 | + && ++fuzz <= mymaxfuzz); |
441 | + |
442 | + if (skip_rest_of_patch) { /* just got decided */ |
443 | + if (outstate.ofp && ! outfile) |
444 | + { |
445 | + fclose (outstate.ofp); |
446 | + outstate.ofp = 0; |
447 | + outfd = -1; |
448 | + } |
449 | + } |
450 | + } |
451 | + |
452 | + newwhere = (where ? where : pch_first()) + out_offset; |
453 | + if (skip_rest_of_patch |
454 | + || (merge && ! merge_hunk (hunk, &outstate, where, |
455 | + &somefailed)) |
456 | + || (! merge |
457 | + && ((where == 1 && pch_says_nonexistent (reverse) == 2 |
458 | + && instat.st_size) |
459 | + || ! where |
460 | + || ! apply_hunk (&outstate, where)))) |
461 | + { |
462 | + abort_hunk (outname, ! failed, reverse); |
463 | + failed++; |
464 | + if (verbosity == VERBOSE || |
465 | + (! skip_rest_of_patch && verbosity != SILENT)) |
466 | + say ("Hunk #%d %s at %s%s.\n", hunk, |
467 | + skip_rest_of_patch ? "ignored" : "FAILED", |
468 | + format_linenum (numbuf, newwhere), |
469 | + ! skip_rest_of_patch && check_line_endings (newwhere) |
470 | + ? " (different line endings)" : ""); |
471 | + } |
472 | + else if (! merge && |
473 | + (verbosity == VERBOSE |
474 | + || (verbosity != SILENT && (fuzz || in_offset)))) |
475 | + { |
476 | + say ("Hunk #%d succeeded at %s", hunk, |
477 | + format_linenum (numbuf, newwhere)); |
478 | + if (fuzz) |
479 | + say (" with fuzz %s", format_linenum (numbuf, fuzz)); |
480 | + if (in_offset) |
481 | + say (" (offset %s line%s)", |
482 | + format_linenum (numbuf, in_offset), |
483 | + "s" + (in_offset == 1)); |
484 | + say (".\n"); |
485 | + } |
486 | + } |
487 | + |
488 | + if (!skip_rest_of_patch) |
489 | + { |
490 | + if (got_hunk < 0 && using_plan_a) |
491 | + { |
492 | + if (outfile) |
493 | + fatal ("out of memory using Plan A"); |
494 | + say ("\n\nRan out of memory using Plan A -- trying again...\n\n"); |
495 | + if (outstate.ofp) |
496 | + { |
497 | + fclose (outstate.ofp); |
498 | + outstate.ofp = 0; |
499 | + } |
500 | + continue; |
501 | + } |
502 | + |
503 | + /* Finish spewing out the new file. */ |
504 | + if (! spew_output (&outstate, &tmpoutst)) |
505 | + { |
506 | + say ("Skipping patch.\n"); |
507 | + skip_rest_of_patch = true; |
508 | + } |
509 | + } |
510 | + } |
511 | + |
512 | + /* and put the output where desired */ |
513 | + ignore_signals (); |
514 | + if (! skip_rest_of_patch && ! outfile) { |
515 | + bool backup = make_backups |
516 | + || (backup_if_mismatch && (mismatch | failed)); |
517 | + if (outstate.zero_output |
518 | + && (remove_empty_files |
519 | + || (pch_says_nonexistent (! reverse) == 2 |
520 | + && ! posixly_correct) |
521 | + || S_ISLNK (file_type))) |
522 | + { |
523 | + if (! dry_run) |
524 | + output_file (NULL, NULL, NULL, outname, |
525 | + (inname == outname) ? &instat : NULL, |
526 | + file_type | 0, backup); |
527 | + } |
528 | + else |
529 | + { |
530 | + if (! outstate.zero_output |
531 | + && pch_says_nonexistent (! reverse) == 2 |
532 | + && (remove_empty_files || ! posixly_correct) |
533 | + && ! (merge && somefailed)) |
534 | + { |
535 | + mismatch = true; |
536 | + somefailed = true; |
537 | + if (verbosity != SILENT) |
538 | + say ("File %s is not empty after patch; not deleting\n", |
539 | + quotearg (outname)); |
540 | + } |
541 | + |
542 | + if (! dry_run) |
543 | + { |
544 | + mode_t old_mode = pch_mode (reverse); |
545 | + mode_t new_mode = pch_mode (! reverse); |
546 | + bool set_mode = new_mode && old_mode != new_mode; |
547 | + |
548 | + /* Avoid replacing files when nothing has changed. */ |
549 | + if (failed < hunk || diff_type == ED_DIFF || set_mode |
550 | + || pch_copy () || pch_rename ()) |
551 | + { |
552 | + enum file_attributes attr = 0; |
553 | + struct timespec new_time = pch_timestamp (! reverse); |
554 | + mode_t mode = file_type | |
555 | + ((new_mode ? new_mode : instat.st_mode) & S_IRWXUGO); |
556 | + |
557 | + if ((set_time | set_utc) && new_time.tv_sec != -1) |
558 | + { |
559 | + struct timespec old_time = pch_timestamp (reverse); |
560 | + |
561 | + if (! force && ! inerrno |
562 | + && pch_says_nonexistent (reverse) != 2 |
563 | + && old_time.tv_sec != -1 |
564 | + && timespec_cmp (old_time, |
565 | + get_stat_mtime (&instat))) |
566 | + say ("Not setting time of file %s " |
567 | + "(time mismatch)\n", |
568 | + quotearg (outname)); |
569 | + else if (! force && (mismatch | failed)) |
570 | + say ("Not setting time of file %s " |
571 | + "(contents mismatch)\n", |
572 | + quotearg (outname)); |
573 | + else |
574 | + attr |= FA_TIMES; |
575 | + } |
576 | + |
577 | + if (inerrno) |
578 | + set_file_attributes (TMPOUTNAME, attr, NULL, NULL, |
579 | + mode, &new_time); |
580 | + else |
581 | + { |
582 | + attr |= FA_IDS | FA_MODE | FA_XATTRS; |
583 | + set_file_attributes (TMPOUTNAME, attr, inname, &instat, |
584 | + mode, &new_time); |
585 | + } |
586 | + |
587 | + output_file (TMPOUTNAME, &TMPOUTNAME_needs_removal, |
588 | + &tmpoutst, outname, NULL, mode, backup); |
589 | + |
590 | + if (pch_rename ()) |
591 | + output_file (NULL, NULL, NULL, inname, &instat, |
592 | + mode, backup); |
593 | + } |
594 | + else |
595 | + output_file (outname, NULL, &tmpoutst, NULL, NULL, |
596 | + file_type | 0, backup); |
597 | + } |
598 | + } |
599 | + } |
600 | + if (diff_type != ED_DIFF) { |
601 | + struct stat rejst; |
602 | + |
603 | + if (failed) { |
604 | + if (fstat (fileno (rejfp), &rejst) != 0 || fclose (rejfp) != 0) |
605 | + write_fatal (); |
606 | + rejfp = NULL; |
607 | + somefailed = true; |
608 | + say ("%d out of %d hunk%s %s", failed, hunk, "s" + (hunk == 1), |
609 | + skip_rest_of_patch ? "ignored" : "FAILED"); |
610 | + if (outname && (! rejname || strcmp (rejname, "-") != 0)) { |
611 | + char *rej = rejname; |
612 | + if (!rejname) { |
613 | + /* FIXME: This should really be done differently! */ |
614 | + const char *s = simple_backup_suffix; |
615 | + size_t len; |
616 | + simple_backup_suffix = ".rej"; |
617 | + rej = find_backup_file_name (outname, simple_backups); |
618 | + len = strlen (rej); |
619 | + if (rej[len - 1] == '~') |
620 | + rej[len - 1] = '#'; |
621 | + simple_backup_suffix = s; |
622 | + } |
623 | + if (! dry_run) |
624 | + { |
625 | + say (" -- saving rejects to file %s\n", quotearg (rej)); |
626 | + if (rejname) |
627 | + { |
628 | + if (! written_to_rejname) |
629 | + { |
630 | + copy_file (TMPREJNAME, rejname, 0, 0, |
631 | + S_IFREG | 0666, true); |
632 | + written_to_rejname = true; |
633 | + } |
634 | + else |
635 | + append_to_file (TMPREJNAME, rejname); |
636 | + } |
637 | + else |
638 | + { |
639 | + struct stat oldst; |
640 | + int olderrno; |
641 | + |
642 | + olderrno = stat_file (rej, &oldst); |
643 | + if (olderrno && olderrno != ENOENT) |
644 | + write_fatal (); |
645 | + if (! olderrno && lookup_file_id (&oldst) == CREATED) |
646 | + append_to_file (TMPREJNAME, rej); |
647 | + else |
648 | + move_file (TMPREJNAME, &TMPREJNAME_needs_removal, |
649 | + &rejst, rej, S_IFREG | 0666, false); |
650 | + } |
651 | + } |
652 | + else |
653 | + say ("\n"); |
654 | + if (!rejname) |
655 | + free (rej); |
656 | + } else |
657 | + say ("\n"); |
658 | + } |
659 | + } |
660 | + set_signals (true); |
661 | + } |
662 | + if (outstate.ofp && (ferror (outstate.ofp) || fclose (outstate.ofp) != 0)) |
663 | + write_fatal (); |
664 | + output_files (NULL); |
665 | + delete_files (); |
666 | + cleanup (); |
667 | + if (somefailed) |
668 | + exit (1); |
669 | + return 0; |
670 | +} |
671 | + |
672 | +/* Prepare to find the next patch to do in the patch file. */ |
673 | + |
674 | +static void |
675 | +reinitialize_almost_everything (void) |
676 | +{ |
677 | + re_patch(); |
678 | + re_input(); |
679 | + |
680 | + input_lines = 0; |
681 | + last_frozen_line = 0; |
682 | + |
683 | + if (inname && ! explicit_inname) { |
684 | + free (inname); |
685 | + inname = 0; |
686 | + } |
687 | + |
688 | + in_offset = 0; |
689 | + out_offset = 0; |
690 | + |
691 | + diff_type = NO_DIFF; |
692 | + |
693 | + if (revision) { |
694 | + free(revision); |
695 | + revision = 0; |
696 | + } |
697 | + |
698 | + reverse = reverse_flag_specified; |
699 | + skip_rest_of_patch = false; |
700 | +} |
701 | + |
702 | +static char const shortopts[] = "bB:cd:D:eEfF:g:i:l" |
703 | +#if defined ENABLE_MERGE |
704 | + "m" |
705 | +#endif |
706 | + "nNo:p:r:RstTuvV:x:Y:z:Z"; |
707 | + |
708 | +static struct option const longopts[] = |
709 | +{ |
710 | + {"backup", no_argument, NULL, 'b'}, |
711 | + {"prefix", required_argument, NULL, 'B'}, |
712 | + {"context", no_argument, NULL, 'c'}, |
713 | + {"directory", required_argument, NULL, 'd'}, |
714 | + {"ifdef", required_argument, NULL, 'D'}, |
715 | + {"ed", no_argument, NULL, 'e'}, |
716 | + {"remove-empty-files", no_argument, NULL, 'E'}, |
717 | + {"force", no_argument, NULL, 'f'}, |
718 | + {"fuzz", required_argument, NULL, 'F'}, |
719 | + {"get", required_argument, NULL, 'g'}, |
720 | + {"input", required_argument, NULL, 'i'}, |
721 | + {"ignore-whitespace", no_argument, NULL, 'l'}, |
722 | +#ifdef ENABLE_MERGE |
723 | + {"merge", optional_argument, NULL, 'm'}, |
724 | +#endif |
725 | + {"normal", no_argument, NULL, 'n'}, |
726 | + {"forward", no_argument, NULL, 'N'}, |
727 | + {"output", required_argument, NULL, 'o'}, |
728 | + {"strip", required_argument, NULL, 'p'}, |
729 | + {"reject-file", required_argument, NULL, 'r'}, |
730 | + {"reverse", no_argument, NULL, 'R'}, |
731 | + {"quiet", no_argument, NULL, 's'}, |
732 | + {"silent", no_argument, NULL, 's'}, |
733 | + {"batch", no_argument, NULL, 't'}, |
734 | + {"set-time", no_argument, NULL, 'T'}, |
735 | + {"unified", no_argument, NULL, 'u'}, |
736 | + {"version", no_argument, NULL, 'v'}, |
737 | + {"version-control", required_argument, NULL, 'V'}, |
738 | + {"debug", required_argument, NULL, 'x'}, |
739 | + {"basename-prefix", required_argument, NULL, 'Y'}, |
740 | + {"suffix", required_argument, NULL, 'z'}, |
741 | + {"set-utc", no_argument, NULL, 'Z'}, |
742 | + {"dry-run", no_argument, NULL, CHAR_MAX + 1}, |
743 | + {"verbose", no_argument, NULL, CHAR_MAX + 2}, |
744 | + {"binary", no_argument, NULL, CHAR_MAX + 3}, |
745 | + {"help", no_argument, NULL, CHAR_MAX + 4}, |
746 | + {"backup-if-mismatch", no_argument, NULL, CHAR_MAX + 5}, |
747 | + {"no-backup-if-mismatch", no_argument, NULL, CHAR_MAX + 6}, |
748 | + {"posix", no_argument, NULL, CHAR_MAX + 7}, |
749 | + {"quoting-style", required_argument, NULL, CHAR_MAX + 8}, |
750 | + {"reject-format", required_argument, NULL, CHAR_MAX + 9}, |
751 | + {"read-only", required_argument, NULL, CHAR_MAX + 10}, |
752 | + {"follow-symlinks", no_argument, NULL, CHAR_MAX + 11}, |
753 | + {NULL, no_argument, NULL, 0} |
754 | +}; |
755 | + |
756 | +static char const *const option_help[] = |
757 | +{ |
758 | +"Input options:", |
759 | +"", |
760 | +" -p NUM --strip=NUM Strip NUM leading components from file names.", |
761 | +" -F LINES --fuzz LINES Set the fuzz factor to LINES for inexact matching.", |
762 | +" -l --ignore-whitespace Ignore white space changes between patch and input.", |
763 | +"", |
764 | +" -c --context Interpret the patch as a context difference.", |
765 | +" -e --ed Interpret the patch as an ed script.", |
766 | +" -n --normal Interpret the patch as a normal difference.", |
767 | +" -u --unified Interpret the patch as a unified difference.", |
768 | +"", |
769 | +" -N --forward Ignore patches that appear to be reversed or already applied.", |
770 | +" -R --reverse Assume patches were created with old and new files swapped.", |
771 | +"", |
772 | +" -i PATCHFILE --input=PATCHFILE Read patch from PATCHFILE instead of stdin.", |
773 | +"", |
774 | +"Output options:", |
775 | +"", |
776 | +" -o FILE --output=FILE Output patched files to FILE.", |
777 | +" -r FILE --reject-file=FILE Output rejects to FILE.", |
778 | +"", |
779 | +" -D NAME --ifdef=NAME Make merged if-then-else output using NAME.", |
780 | +#ifdef ENABLE_MERGE |
781 | +" -m --merge Merge using conflict markers instead of creating reject files.", |
782 | +#endif |
783 | +" -E --remove-empty-files Remove output files that are empty after patching.", |
784 | +"", |
785 | +" -Z --set-utc Set times of patched files, assuming diff uses UTC (GMT).", |
786 | +" -T --set-time Likewise, assuming local time.", |
787 | +"", |
788 | +" --quoting-style=WORD output file names using quoting style WORD.", |
789 | +" Valid WORDs are: literal, shell, shell-always, c, escape.", |
790 | +" Default is taken from QUOTING_STYLE env variable, or 'shell' if unset.", |
791 | +"", |
792 | +"Backup and version control options:", |
793 | +"", |
794 | +" -b --backup Back up the original contents of each file.", |
795 | +" --backup-if-mismatch Back up if the patch does not match exactly.", |
796 | +" --no-backup-if-mismatch Back up mismatches only if otherwise requested.", |
797 | +"", |
798 | +" -V STYLE --version-control=STYLE Use STYLE version control.", |
799 | +" STYLE is either 'simple', 'numbered', or 'existing'.", |
800 | +" -B PREFIX --prefix=PREFIX Prepend PREFIX to backup file names.", |
801 | +" -Y PREFIX --basename-prefix=PREFIX Prepend PREFIX to backup file basenames.", |
802 | +" -z SUFFIX --suffix=SUFFIX Append SUFFIX to backup file names.", |
803 | +"", |
804 | +" -g NUM --get=NUM Get files from RCS etc. if positive; ask if negative.", |
805 | +"", |
806 | +"Miscellaneous options:", |
807 | +"", |
808 | +" -t --batch Ask no questions; skip bad-Prereq patches; assume reversed.", |
809 | +" -f --force Like -t, but ignore bad-Prereq patches, and assume unreversed.", |
810 | +" -s --quiet --silent Work silently unless an error occurs.", |
811 | +" --verbose Output extra information about the work being done.", |
812 | +" --dry-run Do not actually change any files; just print what would happen.", |
813 | +" --posix Conform to the POSIX standard.", |
814 | +"", |
815 | +" -d DIR --directory=DIR Change the working directory to DIR first.", |
816 | +" --reject-format=FORMAT Create 'context' or 'unified' rejects.", |
817 | +" --binary Read and write data in binary mode.", |
818 | +" --read-only=BEHAVIOR How to handle read-only input files: 'ignore' that they", |
819 | +" are read-only, 'warn' (default), or 'fail'.", |
820 | +"", |
821 | +" -v --version Output version info.", |
822 | +" --help Output this help.", |
823 | +"", |
824 | +"Report bugs to <" PACKAGE_BUGREPORT ">.", |
825 | +0 |
826 | +}; |
827 | + |
828 | +static void |
829 | +usage (FILE *stream, int status) |
830 | +{ |
831 | + char const * const *p; |
832 | + |
833 | + if (status != 0) |
834 | + { |
835 | + fprintf (stream, "%s: Try '%s --help' for more information.\n", |
836 | + program_name, Argv[0]); |
837 | + } |
838 | + else |
839 | + { |
840 | + fprintf (stream, "Usage: %s [OPTION]... [ORIGFILE [PATCHFILE]]\n\n", |
841 | + Argv[0]); |
842 | + for (p = option_help; *p; p++) |
843 | + fprintf (stream, "%s\n", *p); |
844 | + } |
845 | + |
846 | + exit (status); |
847 | +} |
848 | + |
849 | +/* Process switches and filenames. */ |
850 | + |
851 | +static void |
852 | +get_some_switches (void) |
853 | +{ |
854 | + int optc; |
855 | + |
856 | + free (rejname); |
857 | + rejname = 0; |
858 | + if (optind == Argc) |
859 | + return; |
860 | + while ((optc = getopt_long (Argc, Argv, shortopts, longopts, (int *) 0)) |
861 | + != -1) { |
862 | + switch (optc) { |
863 | + case 'b': |
864 | + make_backups = true; |
865 | + /* Special hack for backward compatibility with CVS 1.9. |
866 | + If the last 4 args are '-b SUFFIX ORIGFILE PATCHFILE', |
867 | + treat '-b' as if it were '-b -z'. */ |
868 | + if (Argc - optind == 3 |
869 | + && strcmp (Argv[optind - 1], "-b") == 0 |
870 | + && ! (Argv[optind + 0][0] == '-' && Argv[optind + 0][1]) |
871 | + && ! (Argv[optind + 1][0] == '-' && Argv[optind + 1][1]) |
872 | + && ! (Argv[optind + 2][0] == '-' && Argv[optind + 2][1])) |
873 | + { |
874 | + optarg = Argv[optind++]; |
875 | + if (verbosity != SILENT) |
876 | + say ("warning: the '-b %s' option is obsolete; use '-b -z %s' instead\n", |
877 | + optarg, optarg); |
878 | + goto case_z; |
879 | + } |
880 | + break; |
881 | + case 'B': |
882 | + if (!*optarg) |
883 | + fatal ("backup prefix is empty"); |
884 | + origprae = savestr (optarg); |
885 | + break; |
886 | + case 'c': |
887 | + diff_type = CONTEXT_DIFF; |
888 | + break; |
889 | + case 'd': |
890 | + if (chdir(optarg) < 0) |
891 | + pfatal ("Can't change to directory %s", quotearg (optarg)); |
892 | + break; |
893 | + case 'D': |
894 | + do_defines = savestr (optarg); |
895 | + break; |
896 | + case 'e': |
897 | + diff_type = ED_DIFF; |
898 | + break; |
899 | + case 'E': |
900 | + remove_empty_files = true; |
901 | + break; |
902 | + case 'f': |
903 | + force = true; |
904 | + break; |
905 | + case 'F': |
906 | + maxfuzz = numeric_string (optarg, false, "fuzz factor"); |
907 | + break; |
908 | + case 'g': |
909 | + patch_get = numeric_string (optarg, true, "get option value"); |
910 | + break; |
911 | + case 'i': |
912 | + patchname = savestr (optarg); |
913 | + break; |
914 | + case 'l': |
915 | + canonicalize = true; |
916 | + break; |
917 | +#ifdef ENABLE_MERGE |
918 | + case 'm': |
919 | + merge = true; |
920 | + if (optarg) |
921 | + { |
922 | + if (! strcmp (optarg, "merge")) |
923 | + conflict_style = MERGE_MERGE; |
924 | + else if (! strcmp (optarg, "diff3")) |
925 | + conflict_style = MERGE_DIFF3; |
926 | + else |
927 | + usage (stderr, 2); |
928 | + } |
929 | + else |
930 | + conflict_style = MERGE_MERGE; |
931 | + break; |
932 | +#endif |
933 | + case 'n': |
934 | + diff_type = NORMAL_DIFF; |
935 | + break; |
936 | + case 'N': |
937 | + noreverse = true; |
938 | + break; |
939 | + case 'o': |
940 | + outfile = savestr (optarg); |
941 | + break; |
942 | + case 'p': |
943 | + strippath = numeric_string (optarg, false, "strip count"); |
944 | + break; |
945 | + case 'r': |
946 | + rejname = savestr (optarg); |
947 | + break; |
948 | + case 'R': |
949 | + reverse = true; |
950 | + reverse_flag_specified = true; |
951 | + break; |
952 | + case 's': |
953 | + verbosity = SILENT; |
954 | + break; |
955 | + case 't': |
956 | + batch = true; |
957 | + break; |
958 | + case 'T': |
959 | + set_time = true; |
960 | + break; |
961 | + case 'u': |
962 | + diff_type = UNI_DIFF; |
963 | + break; |
964 | + case 'v': |
965 | + version(); |
966 | + exit (0); |
967 | + break; |
968 | + case 'V': |
969 | + version_control = optarg; |
970 | + version_control_context = "--version-control or -V option"; |
971 | + break; |
972 | +#if DEBUGGING |
973 | + case 'x': |
974 | + debug = numeric_string (optarg, true, "debugging option"); |
975 | + break; |
976 | +#endif |
977 | + case 'Y': |
978 | + if (!*optarg) |
979 | + fatal ("backup basename prefix is empty"); |
980 | + origbase = savestr (optarg); |
981 | + break; |
982 | + case 'z': |
983 | + case_z: |
984 | + if (!*optarg) |
985 | + fatal ("backup suffix is empty"); |
986 | + origsuff = savestr (optarg); |
987 | + break; |
988 | + case 'Z': |
989 | + set_utc = true; |
990 | + break; |
991 | + case CHAR_MAX + 1: |
992 | + dry_run = true; |
993 | + break; |
994 | + case CHAR_MAX + 2: |
995 | + verbosity = VERBOSE; |
996 | + break; |
997 | + case CHAR_MAX + 3: |
998 | + no_strip_trailing_cr = true; |
999 | +#if HAVE_SETMODE_DOS |
1000 | + binary_transput = O_BINARY; |
1001 | +#endif |
1002 | + break; |
1003 | + case CHAR_MAX + 4: |
1004 | + usage (stdout, 0); |
1005 | + case CHAR_MAX + 5: |
1006 | + backup_if_mismatch = true; |
1007 | + break; |
1008 | + case CHAR_MAX + 6: |
1009 | + backup_if_mismatch = false; |
1010 | + break; |
1011 | + case CHAR_MAX + 7: |
1012 | + posixly_correct = true; |
1013 | + break; |
1014 | + case CHAR_MAX + 8: |
1015 | + { |
1016 | + int i = argmatch (optarg, quoting_style_args, 0, 0); |
1017 | + if (i < 0) |
1018 | + { |
1019 | + invalid_arg ("quoting style", optarg, i); |
1020 | + usage (stderr, 2); |
1021 | + } |
1022 | + set_quoting_style ((struct quoting_options *) 0, |
1023 | + (enum quoting_style) i); |
1024 | + } |
1025 | + break; |
1026 | + case CHAR_MAX + 9: |
1027 | + if (strcmp (optarg, "context") == 0) |
1028 | + reject_format = NEW_CONTEXT_DIFF; |
1029 | + else if (strcmp (optarg, "unified") == 0) |
1030 | + reject_format = UNI_DIFF; |
1031 | + else |
1032 | + usage (stderr, 2); |
1033 | + break; |
1034 | + case CHAR_MAX + 10: |
1035 | + if (strcmp (optarg, "ignore") == 0) |
1036 | + read_only_behavior = RO_IGNORE; |
1037 | + else if (strcmp (optarg, "warn") == 0) |
1038 | + read_only_behavior = RO_WARN; |
1039 | + else if (strcmp (optarg, "fail") == 0) |
1040 | + read_only_behavior = RO_FAIL; |
1041 | + else |
1042 | + usage (stderr, 2); |
1043 | + break; |
1044 | + case CHAR_MAX + 11: |
1045 | + follow_symlinks = true; |
1046 | + break; |
1047 | + default: |
1048 | + usage (stderr, 2); |
1049 | + } |
1050 | + } |
1051 | + |
1052 | + /* Process any filename args. */ |
1053 | + if (optind < Argc) |
1054 | + { |
1055 | + inname = savestr (Argv[optind++]); |
1056 | + explicit_inname = true; |
1057 | + invc = -1; |
1058 | + if (optind < Argc) |
1059 | + { |
1060 | + patchname = savestr (Argv[optind++]); |
1061 | + if (optind < Argc) |
1062 | + { |
1063 | + fprintf (stderr, "%s: %s: extra operand\n", |
1064 | + program_name, quotearg (Argv[optind])); |
1065 | + usage (stderr, 2); |
1066 | + } |
1067 | + } |
1068 | + } |
1069 | +} |
1070 | + |
1071 | +/* Handle STRING (possibly negative if NEGATIVE_ALLOWED is nonzero) |
1072 | + of type ARGTYPE_MSGID by converting it to an integer, |
1073 | + returning the result. */ |
1074 | +static int |
1075 | +numeric_string (char const *string, |
1076 | + bool negative_allowed, |
1077 | + char const *argtype_msgid) |
1078 | +{ |
1079 | + int value = 0; |
1080 | + char const *p = string; |
1081 | + int sign = *p == '-' ? -1 : 1; |
1082 | + |
1083 | + p += *p == '-' || *p == '+'; |
1084 | + |
1085 | + do |
1086 | + { |
1087 | + int v10 = value * 10; |
1088 | + int digit = *p - '0'; |
1089 | + int signed_digit = sign * digit; |
1090 | + int next_value = v10 + signed_digit; |
1091 | + |
1092 | + if (9 < (unsigned) digit) |
1093 | + fatal ("%s %s is not a number", argtype_msgid, quotearg (string)); |
1094 | + |
1095 | + if (v10 / 10 != value || (next_value < v10) != (signed_digit < 0)) |
1096 | + fatal ("%s %s is too large", argtype_msgid, quotearg (string)); |
1097 | + |
1098 | + value = next_value; |
1099 | + } |
1100 | + while (*++p); |
1101 | + |
1102 | + if (value < 0 && ! negative_allowed) |
1103 | + fatal ("%s %s is negative", argtype_msgid, quotearg (string)); |
1104 | + |
1105 | + return value; |
1106 | +} |
1107 | + |
1108 | +/* Attempt to find the right place to apply this hunk of patch. */ |
1109 | + |
1110 | +static lin |
1111 | +locate_hunk (lin fuzz) |
1112 | +{ |
1113 | + lin first_guess = pch_first () + in_offset; |
1114 | + lin offset; |
1115 | + lin pat_lines = pch_ptrn_lines(); |
1116 | + lin prefix_context = pch_prefix_context (); |
1117 | + lin suffix_context = pch_suffix_context (); |
1118 | + lin context = (prefix_context < suffix_context |
1119 | + ? suffix_context : prefix_context); |
1120 | + lin prefix_fuzz = fuzz + prefix_context - context; |
1121 | + lin suffix_fuzz = fuzz + suffix_context - context; |
1122 | + lin max_where = input_lines - (pat_lines - suffix_fuzz) + 1; |
1123 | + lin min_where = last_frozen_line + 1 - (prefix_context - prefix_fuzz); |
1124 | + lin max_pos_offset = max_where - first_guess; |
1125 | + lin max_neg_offset = first_guess - min_where; |
1126 | + lin max_offset = (max_pos_offset < max_neg_offset |
1127 | + ? max_neg_offset : max_pos_offset); |
1128 | + |
1129 | + if (!pat_lines) /* null range matches always */ |
1130 | + return first_guess; |
1131 | + |
1132 | + /* Do not try lines <= 0. */ |
1133 | + if (first_guess <= max_neg_offset) |
1134 | + max_neg_offset = first_guess - 1; |
1135 | + |
1136 | + if (prefix_fuzz < 0 && pch_first () <= 1) |
1137 | + { |
1138 | + /* Can only match start of file. */ |
1139 | + |
1140 | + if (suffix_fuzz < 0) |
1141 | + /* Can only match entire file. */ |
1142 | + if (pat_lines != input_lines || prefix_context < last_frozen_line) |
1143 | + return 0; |
1144 | + |
1145 | + offset = 1 - first_guess; |
1146 | + if (last_frozen_line <= prefix_context |
1147 | + && offset <= max_pos_offset |
1148 | + && patch_match (first_guess, offset, 0, suffix_fuzz)) |
1149 | + { |
1150 | + in_offset += offset; |
1151 | + return first_guess + offset; |
1152 | + } |
1153 | + else |
1154 | + return 0; |
1155 | + } |
1156 | + else if (prefix_fuzz < 0) |
1157 | + prefix_fuzz = 0; |
1158 | + |
1159 | + if (suffix_fuzz < 0) |
1160 | + { |
1161 | + /* Can only match end of file. */ |
1162 | + offset = first_guess - (input_lines - pat_lines + 1); |
1163 | + if (offset <= max_neg_offset |
1164 | + && patch_match (first_guess, -offset, prefix_fuzz, 0)) |
1165 | + { |
1166 | + in_offset -= offset; |
1167 | + return first_guess - offset; |
1168 | + } |
1169 | + else |
1170 | + return 0; |
1171 | + } |
1172 | + |
1173 | + for (offset = 0; offset <= max_offset; offset++) { |
1174 | + char numbuf0[LINENUM_LENGTH_BOUND + 1]; |
1175 | + char numbuf1[LINENUM_LENGTH_BOUND + 1]; |
1176 | + if (offset <= max_pos_offset |
1177 | + && patch_match (first_guess, offset, prefix_fuzz, suffix_fuzz)) { |
1178 | + if (debug & 1) |
1179 | + say ("Offset changing from %s to %s\n", |
1180 | + format_linenum (numbuf0, in_offset), |
1181 | + format_linenum (numbuf1, in_offset + offset)); |
1182 | + in_offset += offset; |
1183 | + return first_guess+offset; |
1184 | + } |
1185 | + if (0 < offset && offset <= max_neg_offset |
1186 | + && patch_match (first_guess, -offset, prefix_fuzz, suffix_fuzz)) { |
1187 | + if (debug & 1) |
1188 | + say ("Offset changing from %s to %s\n", |
1189 | + format_linenum (numbuf0, in_offset), |
1190 | + format_linenum (numbuf1, in_offset - offset)); |
1191 | + in_offset -= offset; |
1192 | + return first_guess-offset; |
1193 | + } |
1194 | + } |
1195 | + return 0; |
1196 | +} |
1197 | + |
1198 | +static void __attribute__ ((noreturn)) |
1199 | +mangled_patch (lin old, lin new) |
1200 | +{ |
1201 | + char numbuf0[LINENUM_LENGTH_BOUND + 1]; |
1202 | + char numbuf1[LINENUM_LENGTH_BOUND + 1]; |
1203 | + if (debug & 1) |
1204 | + say ("oldchar = '%c', newchar = '%c'\n", |
1205 | + pch_char (old), pch_char (new)); |
1206 | + fatal ("Out-of-sync patch, lines %s,%s -- mangled text or line numbers, " |
1207 | + "maybe?", |
1208 | + format_linenum (numbuf0, pch_hunk_beg () + old), |
1209 | + format_linenum (numbuf1, pch_hunk_beg () + new)); |
1210 | +} |
1211 | + |
1212 | +/* Output a line number range in unified format. */ |
1213 | + |
1214 | +static void |
1215 | +print_unidiff_range (FILE *fp, lin start, lin count) |
1216 | +{ |
1217 | + char numbuf0[LINENUM_LENGTH_BOUND + 1]; |
1218 | + char numbuf1[LINENUM_LENGTH_BOUND + 1]; |
1219 | + |
1220 | + switch (count) |
1221 | + { |
1222 | + case 0: |
1223 | + fprintf (fp, "%s,0", format_linenum (numbuf0, start - 1)); |
1224 | + break; |
1225 | + |
1226 | + case 1: |
1227 | + fprintf (fp, "%s", format_linenum (numbuf0, start)); |
1228 | + break; |
1229 | + |
1230 | + default: |
1231 | + fprintf (fp, "%s,%s", |
1232 | + format_linenum (numbuf0, start), |
1233 | + format_linenum (numbuf1, count)); |
1234 | + break; |
1235 | + } |
1236 | +} |
1237 | + |
1238 | +static void |
1239 | +print_header_line (FILE *fp, const char *tag, bool reverse) |
1240 | +{ |
1241 | + const char *name = pch_name (reverse); |
1242 | + const char *timestr = pch_timestr (reverse); |
1243 | + |
1244 | + fprintf (fp, "%s %s%s\n", tag, name ? name : "/dev/null", |
1245 | + timestr ? timestr : ""); |
1246 | +} |
1247 | + |
1248 | +/* Produce unified reject files */ |
1249 | + |
1250 | +static void |
1251 | +abort_hunk_unified (bool header, bool reverse) |
1252 | +{ |
1253 | + lin old = 1; |
1254 | + lin lastline = pch_ptrn_lines (); |
1255 | + lin new = lastline + 1; |
1256 | + |
1257 | + if (header) |
1258 | + { |
1259 | + if (pch_name (INDEX)) |
1260 | + fprintf(rejfp, "Index: %s\n", pch_name (INDEX)); |
1261 | + print_header_line (rejfp, "---", reverse); |
1262 | + print_header_line (rejfp, "+++", ! reverse); |
1263 | + } |
1264 | + |
1265 | + /* Add out_offset to guess the same as the previous successful hunk. */ |
1266 | + fprintf (rejfp, "@@ -"); |
1267 | + print_unidiff_range (rejfp, pch_first () + out_offset, lastline); |
1268 | + fprintf (rejfp, " +"); |
1269 | + print_unidiff_range (rejfp, pch_newfirst () + out_offset, pch_repl_lines ()); |
1270 | + fprintf (rejfp, " @@\n"); |
1271 | + |
1272 | + while (pch_char (new) == '=' || pch_char (new) == '\n') |
1273 | + new++; |
1274 | + |
1275 | + if (diff_type != UNI_DIFF) |
1276 | + pch_normalize (UNI_DIFF); |
1277 | + |
1278 | + for (; ; old++, new++) |
1279 | + { |
1280 | + for (; pch_char (old) == '-'; old++) |
1281 | + { |
1282 | + fputc ('-', rejfp); |
1283 | + pch_write_line (old, rejfp); |
1284 | + } |
1285 | + for (; pch_char (new) == '+'; new++) |
1286 | + { |
1287 | + fputc ('+', rejfp); |
1288 | + pch_write_line (new, rejfp); |
1289 | + } |
1290 | + |
1291 | + if (old > lastline) |
1292 | + break; |
1293 | + |
1294 | + if (pch_char (new) != pch_char (old)) |
1295 | + mangled_patch (old, new); |
1296 | + |
1297 | + fputc (' ', rejfp); |
1298 | + pch_write_line (old, rejfp); |
1299 | + } |
1300 | + if (pch_char (new) != '^') |
1301 | + mangled_patch (old, new); |
1302 | +} |
1303 | + |
1304 | +/* Output the rejected patch in context format. */ |
1305 | + |
1306 | +static void |
1307 | +abort_hunk_context (bool header, bool reverse) |
1308 | +{ |
1309 | + lin i; |
1310 | + lin pat_end = pch_end (); |
1311 | + /* add in out_offset to guess the same as the previous successful hunk */ |
1312 | + lin oldfirst = pch_first() + out_offset; |
1313 | + lin newfirst = pch_newfirst() + out_offset; |
1314 | + lin oldlast = oldfirst + pch_ptrn_lines() - 1; |
1315 | + lin newlast = newfirst + pch_repl_lines() - 1; |
1316 | + char const *stars = |
1317 | + (int) NEW_CONTEXT_DIFF <= (int) diff_type ? " ****" : ""; |
1318 | + char const *minuses = |
1319 | + (int) NEW_CONTEXT_DIFF <= (int) diff_type ? " ----" : " -----"; |
1320 | + char const *c_function = pch_c_function(); |
1321 | + |
1322 | + if (diff_type == UNI_DIFF) |
1323 | + pch_normalize (NEW_CONTEXT_DIFF); |
1324 | + |
1325 | + if (header) |
1326 | + { |
1327 | + if (pch_name (INDEX)) |
1328 | + fprintf(rejfp, "Index: %s\n", pch_name (INDEX)); |
1329 | + print_header_line (rejfp, "***", reverse); |
1330 | + print_header_line (rejfp, "---", ! reverse); |
1331 | + } |
1332 | + fprintf(rejfp, "***************%s\n", c_function ? c_function : ""); |
1333 | + for (i=0; i<=pat_end; i++) { |
1334 | + char numbuf0[LINENUM_LENGTH_BOUND + 1]; |
1335 | + char numbuf1[LINENUM_LENGTH_BOUND + 1]; |
1336 | + switch (pch_char(i)) { |
1337 | + case '*': |
1338 | + if (oldlast < oldfirst) |
1339 | + fprintf(rejfp, "*** 0%s\n", stars); |
1340 | + else if (oldlast == oldfirst) |
1341 | + fprintf (rejfp, "*** %s%s\n", |
1342 | + format_linenum (numbuf0, oldfirst), stars); |
1343 | + else |
1344 | + fprintf (rejfp, "*** %s,%s%s\n", |
1345 | + format_linenum (numbuf0, oldfirst), |
1346 | + format_linenum (numbuf1, oldlast), stars); |
1347 | + break; |
1348 | + case '=': |
1349 | + if (newlast < newfirst) |
1350 | + fprintf(rejfp, "--- 0%s\n", minuses); |
1351 | + else if (newlast == newfirst) |
1352 | + fprintf (rejfp, "--- %s%s\n", |
1353 | + format_linenum (numbuf0, newfirst), minuses); |
1354 | + else |
1355 | + fprintf (rejfp, "--- %s,%s%s\n", |
1356 | + format_linenum (numbuf0, newfirst), |
1357 | + format_linenum (numbuf1, newlast), minuses); |
1358 | + break; |
1359 | + case ' ': case '-': case '+': case '!': |
1360 | + fprintf (rejfp, "%c ", pch_char (i)); |
1361 | + /* fall into */ |
1362 | + case '\n': |
1363 | + pch_write_line (i, rejfp); |
1364 | + break; |
1365 | + default: |
1366 | + fatal ("fatal internal error in abort_hunk_context"); |
1367 | + } |
1368 | + if (ferror (rejfp)) |
1369 | + write_fatal (); |
1370 | + } |
1371 | +} |
1372 | + |
1373 | +/* Output the rejected hunk. */ |
1374 | + |
1375 | +static void |
1376 | +abort_hunk (char const *outname, bool header, bool reverse) |
1377 | +{ |
1378 | + if (! TMPREJNAME_needs_removal) |
1379 | + init_reject (outname); |
1380 | + if (reject_format == UNI_DIFF |
1381 | + || (reject_format == NO_DIFF && diff_type == UNI_DIFF)) |
1382 | + abort_hunk_unified (header, reverse); |
1383 | + else |
1384 | + abort_hunk_context (header, reverse); |
1385 | +} |
1386 | + |
1387 | +/* We found where to apply it (we hope), so do it. */ |
1388 | + |
1389 | +static bool |
1390 | +apply_hunk (struct outstate *outstate, lin where) |
1391 | +{ |
1392 | + lin old = 1; |
1393 | + lin lastline = pch_ptrn_lines (); |
1394 | + lin new = lastline+1; |
1395 | + enum {OUTSIDE, IN_IFNDEF, IN_IFDEF, IN_ELSE} def_state = OUTSIDE; |
1396 | + char const *R_do_defines = do_defines; |
1397 | + lin pat_end = pch_end (); |
1398 | + FILE *fp = outstate->ofp; |
1399 | + |
1400 | + where--; |
1401 | + while (pch_char(new) == '=' || pch_char(new) == '\n') |
1402 | + new++; |
1403 | + |
1404 | + while (old <= lastline) { |
1405 | + if (pch_char(old) == '-') { |
1406 | + assert (outstate->after_newline); |
1407 | + if (! copy_till (outstate, where + old - 1)) |
1408 | + return false; |
1409 | + if (R_do_defines) { |
1410 | + if (def_state == OUTSIDE) { |
1411 | + fprintf (fp, outstate->after_newline + not_defined, |
1412 | + R_do_defines); |
1413 | + def_state = IN_IFNDEF; |
1414 | + } |
1415 | + else if (def_state == IN_IFDEF) { |
1416 | + fputs (outstate->after_newline + else_defined, fp); |
1417 | + def_state = IN_ELSE; |
1418 | + } |
1419 | + if (ferror (fp)) |
1420 | + write_fatal (); |
1421 | + outstate->after_newline = pch_write_line (old, fp); |
1422 | + outstate->zero_output = false; |
1423 | + } |
1424 | + last_frozen_line++; |
1425 | + old++; |
1426 | + } |
1427 | + else if (new > pat_end) { |
1428 | + break; |
1429 | + } |
1430 | + else if (pch_char(new) == '+') { |
1431 | + if (! copy_till (outstate, where + old - 1)) |
1432 | + return false; |
1433 | + if (R_do_defines) { |
1434 | + if (def_state == IN_IFNDEF) { |
1435 | + fputs (outstate->after_newline + else_defined, fp); |
1436 | + def_state = IN_ELSE; |
1437 | + } |
1438 | + else if (def_state == OUTSIDE) { |
1439 | + fprintf (fp, outstate->after_newline + if_defined, |
1440 | + R_do_defines); |
1441 | + def_state = IN_IFDEF; |
1442 | + } |
1443 | + if (ferror (fp)) |
1444 | + write_fatal (); |
1445 | + } |
1446 | + outstate->after_newline = pch_write_line (new, fp); |
1447 | + outstate->zero_output = false; |
1448 | + new++; |
1449 | + } |
1450 | + else if (pch_char(new) != pch_char(old)) |
1451 | + mangled_patch (old, new); |
1452 | + else if (pch_char(new) == '!') { |
1453 | + assert (outstate->after_newline); |
1454 | + if (! copy_till (outstate, where + old - 1)) |
1455 | + return false; |
1456 | + assert (outstate->after_newline); |
1457 | + if (R_do_defines) { |
1458 | + fprintf (fp, 1 + not_defined, R_do_defines); |
1459 | + if (ferror (fp)) |
1460 | + write_fatal (); |
1461 | + def_state = IN_IFNDEF; |
1462 | + } |
1463 | + |
1464 | + do |
1465 | + { |
1466 | + if (R_do_defines) { |
1467 | + outstate->after_newline = pch_write_line (old, fp); |
1468 | + } |
1469 | + last_frozen_line++; |
1470 | + old++; |
1471 | + } |
1472 | + while (pch_char (old) == '!'); |
1473 | + |
1474 | + if (R_do_defines) { |
1475 | + fputs (outstate->after_newline + else_defined, fp); |
1476 | + if (ferror (fp)) |
1477 | + write_fatal (); |
1478 | + def_state = IN_ELSE; |
1479 | + } |
1480 | + |
1481 | + do |
1482 | + { |
1483 | + outstate->after_newline = pch_write_line (new, fp); |
1484 | + new++; |
1485 | + } |
1486 | + while (pch_char (new) == '!'); |
1487 | + outstate->zero_output = false; |
1488 | + } |
1489 | + else { |
1490 | + assert(pch_char(new) == ' '); |
1491 | + old++; |
1492 | + new++; |
1493 | + if (R_do_defines && def_state != OUTSIDE) { |
1494 | + fputs (outstate->after_newline + end_defined, fp); |
1495 | + if (ferror (fp)) |
1496 | + write_fatal (); |
1497 | + outstate->after_newline = true; |
1498 | + def_state = OUTSIDE; |
1499 | + } |
1500 | + } |
1501 | + } |
1502 | + if (new <= pat_end && pch_char(new) == '+') { |
1503 | + if (! copy_till (outstate, where + old - 1)) |
1504 | + return false; |
1505 | + if (R_do_defines) { |
1506 | + if (def_state == OUTSIDE) { |
1507 | + fprintf (fp, outstate->after_newline + if_defined, |
1508 | + R_do_defines); |
1509 | + def_state = IN_IFDEF; |
1510 | + } |
1511 | + else if (def_state == IN_IFNDEF) { |
1512 | + fputs (outstate->after_newline + else_defined, fp); |
1513 | + def_state = IN_ELSE; |
1514 | + } |
1515 | + if (ferror (fp)) |
1516 | + write_fatal (); |
1517 | + outstate->zero_output = false; |
1518 | + } |
1519 | + |
1520 | + do |
1521 | + { |
1522 | + if (! outstate->after_newline && putc ('\n', fp) == EOF) |
1523 | + write_fatal (); |
1524 | + outstate->after_newline = pch_write_line (new, fp); |
1525 | + outstate->zero_output = false; |
1526 | + new++; |
1527 | + } |
1528 | + while (new <= pat_end && pch_char (new) == '+'); |
1529 | + } |
1530 | + if (R_do_defines && def_state != OUTSIDE) { |
1531 | + fputs (outstate->after_newline + end_defined, fp); |
1532 | + if (ferror (fp)) |
1533 | + write_fatal (); |
1534 | + outstate->after_newline = true; |
1535 | + } |
1536 | + out_offset += pch_repl_lines() - pch_ptrn_lines (); |
1537 | + return true; |
1538 | +} |
1539 | + |
1540 | +/* Create an output file. */ |
1541 | + |
1542 | +static FILE * |
1543 | +create_output_file (char const *name, int open_flags) |
1544 | +{ |
1545 | + int fd = create_file (name, O_WRONLY | binary_transput | open_flags, |
1546 | + instat.st_mode, true); |
1547 | + FILE *f = fdopen (fd, binary_transput ? "wb" : "w"); |
1548 | + if (! f) |
1549 | + pfatal ("Can't create file %s", quotearg (name)); |
1550 | + return f; |
1551 | +} |
1552 | + |
1553 | +/* Open the new file. */ |
1554 | + |
1555 | +static void |
1556 | +init_output (struct outstate *outstate) |
1557 | +{ |
1558 | + outstate->ofp = NULL; |
1559 | + outstate->after_newline = true; |
1560 | + outstate->zero_output = true; |
1561 | +} |
1562 | + |
1563 | +static FILE * |
1564 | +open_outfile (char const *name) |
1565 | +{ |
1566 | + if (strcmp (name, "-") != 0) |
1567 | + return create_output_file (name, 0); |
1568 | + else |
1569 | + { |
1570 | + FILE *ofp; |
1571 | + int stdout_dup = dup (fileno (stdout)); |
1572 | + if (stdout_dup == -1) |
1573 | + pfatal ("Failed to duplicate standard output"); |
1574 | + ofp = fdopen (stdout_dup, "a"); |
1575 | + if (! ofp) |
1576 | + pfatal ("Failed to duplicate standard output"); |
1577 | + if (dup2 (fileno (stderr), fileno (stdout)) == -1) |
1578 | + pfatal ("Failed to redirect messages to standard error"); |
1579 | + /* FIXME: Do we need to switch stdout_dup into O_BINARY mode here? */ |
1580 | + return ofp; |
1581 | + } |
1582 | +} |
1583 | + |
1584 | +/* Open a file to put hunks we can't locate. */ |
1585 | + |
1586 | +static void |
1587 | +init_reject (char const *outname) |
1588 | +{ |
1589 | + int fd; |
1590 | + fd = make_tempfile (&TMPREJNAME, 'r', outname, O_WRONLY | binary_transput, |
1591 | + 0666); |
1592 | + TMPREJNAME_needs_removal = true; |
1593 | + rejfp = fdopen (fd, binary_transput ? "wb" : "w"); |
1594 | + if (! rejfp) |
1595 | + pfatal ("Can't open stream for file %s", quotearg (TMPREJNAME)); |
1596 | +} |
1597 | + |
1598 | +/* Copy input file to output, up to wherever hunk is to be applied. */ |
1599 | + |
1600 | +bool |
1601 | +copy_till (struct outstate *outstate, lin lastline) |
1602 | +{ |
1603 | + lin R_last_frozen_line = last_frozen_line; |
1604 | + FILE *fp = outstate->ofp; |
1605 | + char const *s; |
1606 | + size_t size; |
1607 | + |
1608 | + if (R_last_frozen_line > lastline) |
1609 | + { |
1610 | + say ("misordered hunks! output would be garbled\n"); |
1611 | + return false; |
1612 | + } |
1613 | + while (R_last_frozen_line < lastline) |
1614 | + { |
1615 | + s = ifetch (++R_last_frozen_line, false, &size); |
1616 | + if (size) |
1617 | + { |
1618 | + if ((! outstate->after_newline && putc ('\n', fp) == EOF) |
1619 | + || ! fwrite (s, sizeof *s, size, fp)) |
1620 | + write_fatal (); |
1621 | + outstate->after_newline = s[size - 1] == '\n'; |
1622 | + outstate->zero_output = false; |
1623 | + } |
1624 | + } |
1625 | + last_frozen_line = R_last_frozen_line; |
1626 | + return true; |
1627 | +} |
1628 | + |
1629 | +/* Finish copying the input file to the output file. */ |
1630 | + |
1631 | +static bool |
1632 | +spew_output (struct outstate *outstate, struct stat *st) |
1633 | +{ |
1634 | + if (debug & 256) |
1635 | + { |
1636 | + char numbuf0[LINENUM_LENGTH_BOUND + 1]; |
1637 | + char numbuf1[LINENUM_LENGTH_BOUND + 1]; |
1638 | + say ("il=%s lfl=%s\n", |
1639 | + format_linenum (numbuf0, input_lines), |
1640 | + format_linenum (numbuf1, last_frozen_line)); |
1641 | + } |
1642 | + |
1643 | + if (last_frozen_line < input_lines) |
1644 | + if (! copy_till (outstate, input_lines)) |
1645 | + return false; |
1646 | + |
1647 | + if (outstate->ofp && ! outfile) |
1648 | + { |
1649 | + if (fflush (outstate->ofp) != 0 |
1650 | + || fstat (fileno (outstate->ofp), st) != 0 |
1651 | + || fclose (outstate->ofp) != 0) |
1652 | + write_fatal (); |
1653 | + outstate->ofp = 0; |
1654 | + } |
1655 | + |
1656 | + return true; |
1657 | +} |
1658 | + |
1659 | +/* Does the patch pattern match at line base+offset? */ |
1660 | + |
1661 | +static bool |
1662 | +patch_match (lin base, lin offset, lin prefix_fuzz, lin suffix_fuzz) |
1663 | +{ |
1664 | + lin pline = 1 + prefix_fuzz; |
1665 | + lin iline; |
1666 | + lin pat_lines = pch_ptrn_lines () - suffix_fuzz; |
1667 | + size_t size; |
1668 | + char const *p; |
1669 | + |
1670 | + for (iline=base+offset+prefix_fuzz; pline <= pat_lines; pline++,iline++) { |
1671 | + p = ifetch (iline, offset >= 0, &size); |
1672 | + if (canonicalize) { |
1673 | + if (!similar(p, size, |
1674 | + pfetch(pline), |
1675 | + pch_line_len(pline) )) |
1676 | + return false; |
1677 | + } |
1678 | + else if (size != pch_line_len (pline) |
1679 | + || memcmp (p, pfetch (pline), size) != 0) |
1680 | + return false; |
1681 | + } |
1682 | + return true; |
1683 | +} |
1684 | + |
1685 | +/* Check if the line endings in the input file and in the patch differ. */ |
1686 | + |
1687 | +static bool |
1688 | +check_line_endings (lin where) |
1689 | +{ |
1690 | + char const *p; |
1691 | + size_t size; |
1692 | + bool input_crlf, patch_crlf; |
1693 | + |
1694 | + p = pfetch (1); |
1695 | + size = pch_line_len (1); |
1696 | + if (! size) |
1697 | + return false; |
1698 | + patch_crlf = size >= 2 && p[size - 2] == '\r' && p[size - 1] == '\n'; |
1699 | + |
1700 | + if (! input_lines) |
1701 | + return false; |
1702 | + if (where > input_lines) |
1703 | + where = input_lines; |
1704 | + p = ifetch (where, false, &size); |
1705 | + if (! size) |
1706 | + return false; |
1707 | + input_crlf = size >= 2 && p[size - 2] == '\r' && p[size - 1] == '\n'; |
1708 | + |
1709 | + return patch_crlf != input_crlf; |
1710 | +} |
1711 | + |
1712 | +/* Do two lines match with canonicalized white space? */ |
1713 | + |
1714 | +bool |
1715 | +similar (char const *a, size_t alen, char const *b, size_t blen) |
1716 | +{ |
1717 | + /* Ignore presence or absence of trailing newlines. */ |
1718 | + alen -= alen && a[alen - 1] == '\n'; |
1719 | + blen -= blen && b[blen - 1] == '\n'; |
1720 | + |
1721 | + for (;;) |
1722 | + { |
1723 | + if (!blen || (*b == ' ' || *b == '\t')) |
1724 | + { |
1725 | + while (blen && (*b == ' ' || *b == '\t')) |
1726 | + b++, blen--; |
1727 | + if (alen) |
1728 | + { |
1729 | + if (!(*a == ' ' || *a == '\t')) |
1730 | + return false; |
1731 | + do a++, alen--; |
1732 | + while (alen && (*a == ' ' || *a == '\t')); |
1733 | + } |
1734 | + if (!alen || !blen) |
1735 | + return alen == blen; |
1736 | + } |
1737 | + else if (!alen || *a++ != *b++) |
1738 | + return false; |
1739 | + else |
1740 | + alen--, blen--; |
1741 | + } |
1742 | +} |
1743 | + |
1744 | +/* Deferred deletion of files. */ |
1745 | + |
1746 | +struct file_to_delete { |
1747 | + char *name; |
1748 | + struct stat st; |
1749 | + bool backup; |
1750 | +}; |
1751 | + |
1752 | +static gl_list_t files_to_delete; |
1753 | + |
1754 | +static void |
1755 | +init_files_to_delete (void) |
1756 | +{ |
1757 | + files_to_delete = gl_list_create_empty (GL_LINKED_LIST, NULL, NULL, NULL, true); |
1758 | +} |
1759 | + |
1760 | +static void |
1761 | +delete_file_later (const char *name, const struct stat *st, bool backup) |
1762 | +{ |
1763 | + struct file_to_delete *file_to_delete; |
1764 | + struct stat st_tmp; |
1765 | + |
1766 | + if (! st) |
1767 | + { |
1768 | + if (stat_file (name, &st_tmp) != 0) |
1769 | + pfatal ("Can't get file attributes of %s %s", "file", name); |
1770 | + st = &st_tmp; |
1771 | + } |
1772 | + file_to_delete = xmalloc (sizeof *file_to_delete); |
1773 | + file_to_delete->name = xstrdup (name); |
1774 | + file_to_delete->st = *st; |
1775 | + file_to_delete->backup = backup; |
1776 | + gl_list_add_last (files_to_delete, file_to_delete); |
1777 | + insert_file_id (st, DELETE_LATER); |
1778 | +} |
1779 | + |
1780 | +static void |
1781 | +delete_files (void) |
1782 | +{ |
1783 | + gl_list_iterator_t iter; |
1784 | + const void *elt; |
1785 | + |
1786 | + iter = gl_list_iterator (files_to_delete); |
1787 | + while (gl_list_iterator_next (&iter, &elt, NULL)) |
1788 | + { |
1789 | + const struct file_to_delete *file_to_delete = elt; |
1790 | + |
1791 | + if (lookup_file_id (&file_to_delete->st) == DELETE_LATER) |
1792 | + { |
1793 | + mode_t mode = file_to_delete->st.st_mode; |
1794 | + |
1795 | + if (verbosity == VERBOSE) |
1796 | + say ("Removing %s %s\n", |
1797 | + S_ISLNK (mode) ? "symbolic link" : "file", |
1798 | + quotearg (file_to_delete->name)); |
1799 | + move_file (0, 0, 0, file_to_delete->name, mode, |
1800 | + file_to_delete->backup); |
1801 | + removedirs (file_to_delete->name); |
1802 | + } |
1803 | + } |
1804 | + gl_list_iterator_free (&iter); |
1805 | +} |
1806 | + |
1807 | +/* Putting output files into place and removing them. */ |
1808 | + |
1809 | +struct file_to_output { |
1810 | + char *from; |
1811 | + struct stat from_st; |
1812 | + char *to; |
1813 | + mode_t mode; |
1814 | + bool backup; |
1815 | +}; |
1816 | + |
1817 | +static gl_list_t files_to_output; |
1818 | + |
1819 | +static void |
1820 | +output_file_later (char const *from, bool *from_needs_removal, const struct stat *from_st, |
1821 | + char const *to, mode_t mode, bool backup) |
1822 | +{ |
1823 | + struct file_to_output *file_to_output; |
1824 | + |
1825 | + file_to_output = xmalloc (sizeof *file_to_output); |
1826 | + file_to_output->from = xstrdup (from); |
1827 | + file_to_output->from_st = *from_st; |
1828 | + file_to_output->to = to ? xstrdup (to) : NULL; |
1829 | + file_to_output->mode = mode; |
1830 | + file_to_output->backup = backup; |
1831 | + gl_list_add_last (files_to_output, file_to_output); |
1832 | + if (from_needs_removal) |
1833 | + *from_needs_removal = false; |
1834 | +} |
1835 | + |
1836 | +static void |
1837 | +output_file_now (char const *from, bool *from_needs_removal, |
1838 | + const struct stat *from_st, char const *to, |
1839 | + mode_t mode, bool backup) |
1840 | +{ |
1841 | + if (to == NULL) |
1842 | + { |
1843 | + if (backup) |
1844 | + create_backup (from, from_st, true); |
1845 | + } |
1846 | + else |
1847 | + { |
1848 | + assert (from_st->st_size != -1); |
1849 | + move_file (from, from_needs_removal, from_st, to, mode, backup); |
1850 | + } |
1851 | +} |
1852 | + |
1853 | +static void |
1854 | +output_file (char const *from, bool *from_needs_removal, |
1855 | + const struct stat *from_st, char const *to, |
1856 | + const struct stat *to_st, mode_t mode, bool backup) |
1857 | +{ |
1858 | + if (from == NULL) |
1859 | + { |
1860 | + /* Remember which files should be deleted and only delete them when the |
1861 | + entire input to patch has been processed. This allows to correctly |
1862 | + determine for which files backup files have already been created. */ |
1863 | + |
1864 | + delete_file_later (to, to_st, backup); |
1865 | + } |
1866 | + else if (pch_git_diff () && pch_says_nonexistent (reverse) != 2) |
1867 | + { |
1868 | + /* In git-style diffs, the "before" state of each patch refers to the initial |
1869 | + state before modifying any files, input files can be referenced more than |
1870 | + once (when creating copies), and output files are modified at most once. |
1871 | + However, the input to GNU patch may consist of multiple concatenated |
1872 | + git-style diffs, which must be processed separately. (The same output |
1873 | + file may go through multiple revisions.) |
1874 | + |
1875 | + To implement this, we remember which files to /modify/ instead of |
1876 | + modifying the files immediately, but we create /new/ output files |
1877 | + immediately. The new output files serve as markers to detect when a |
1878 | + file is modified more than once; this allows to recognize most |
1879 | + concatenated git-style diffs. |
1880 | + */ |
1881 | + |
1882 | + output_file_later (from, from_needs_removal, from_st, to, mode, backup); |
1883 | + } |
1884 | + else |
1885 | + output_file_now (from, from_needs_removal, from_st, to, mode, backup); |
1886 | +} |
1887 | + |
1888 | +static void |
1889 | +dispose_file_to_output (const void *elt) |
1890 | +{ |
1891 | + const struct file_to_output *file_to_output = elt; |
1892 | + |
1893 | + free (file_to_output->from); |
1894 | + free (file_to_output->to); |
1895 | +} |
1896 | + |
1897 | +static void |
1898 | +init_files_to_output (void) |
1899 | +{ |
1900 | + files_to_output = gl_list_create_empty (GL_LINKED_LIST, NULL, NULL, |
1901 | + dispose_file_to_output, true); |
1902 | +} |
1903 | + |
1904 | +static void |
1905 | +gl_list_clear (gl_list_t list) |
1906 | +{ |
1907 | + while (gl_list_size (list) > 0) |
1908 | + gl_list_remove_at (list, 0); |
1909 | +} |
1910 | + |
1911 | +static void |
1912 | +output_files (struct stat const *st) |
1913 | +{ |
1914 | + gl_list_iterator_t iter; |
1915 | + const void *elt; |
1916 | + |
1917 | + iter = gl_list_iterator (files_to_output); |
1918 | + while (gl_list_iterator_next (&iter, &elt, NULL)) |
1919 | + { |
1920 | + const struct file_to_output *file_to_output = elt; |
1921 | + bool from_needs_removal = true; |
1922 | + struct stat const *from_st = &file_to_output->from_st; |
1923 | + |
1924 | + output_file_now (file_to_output->from, &from_needs_removal, |
1925 | + from_st, file_to_output->to, |
1926 | + file_to_output->mode, file_to_output->backup); |
1927 | + if (file_to_output->to && from_needs_removal) |
1928 | + unlink (file_to_output->from); |
1929 | + |
1930 | + if (st && st->st_dev == from_st->st_dev && st->st_ino == from_st->st_ino) |
1931 | + { |
1932 | + /* Free the list up to here. */ |
1933 | + for (;;) |
1934 | + { |
1935 | + const void *elt2 = gl_list_get_at (files_to_output, 0); |
1936 | + gl_list_remove_at (files_to_output, 0); |
1937 | + if (elt == elt2) |
1938 | + break; |
1939 | + } |
1940 | + gl_list_iterator_free (&iter); |
1941 | + return; |
1942 | + } |
1943 | + } |
1944 | + gl_list_iterator_free (&iter); |
1945 | + gl_list_clear (files_to_output); |
1946 | +} |
1947 | + |
1948 | +static void |
1949 | +forget_output_files (void) |
1950 | +{ |
1951 | + gl_list_iterator_t iter = gl_list_iterator (files_to_output); |
1952 | + const void *elt; |
1953 | + |
1954 | + while (gl_list_iterator_next (&iter, &elt, NULL)) |
1955 | + { |
1956 | + const struct file_to_output *file_to_output = elt; |
1957 | + |
1958 | + unlink (file_to_output->from); |
1959 | + } |
1960 | + gl_list_iterator_free (&iter); |
1961 | + gl_list_clear (files_to_output); |
1962 | +} |
1963 | + |
1964 | +/* Fatal exit with cleanup. */ |
1965 | + |
1966 | +void |
1967 | +fatal_exit (int sig) |
1968 | +{ |
1969 | + cleanup (); |
1970 | + |
1971 | + if (sig) |
1972 | + exit_with_signal (sig); |
1973 | + |
1974 | + exit (2); |
1975 | +} |
1976 | + |
1977 | +static void |
1978 | +remove_if_needed (char const *name, bool *needs_removal) |
1979 | +{ |
1980 | + if (*needs_removal) |
1981 | + { |
1982 | + unlink (name); |
1983 | + *needs_removal = false; |
1984 | + } |
1985 | +} |
1986 | + |
1987 | +static void |
1988 | +cleanup (void) |
1989 | +{ |
1990 | + remove_if_needed (TMPINNAME, &TMPINNAME_needs_removal); |
1991 | + remove_if_needed (TMPOUTNAME, &TMPOUTNAME_needs_removal); |
1992 | + remove_if_needed (TMPPATNAME, &TMPPATNAME_needs_removal); |
1993 | + remove_if_needed (TMPREJNAME, &TMPREJNAME_needs_removal); |
1994 | + forget_output_files (); |
1995 | +} |
1996 | |
1997 | === modified file 'debian/changelog' |
1998 | --- debian/changelog 2013-11-04 12:36:11 +0000 |
1999 | +++ debian/changelog 2014-05-01 16:07:46 +0000 |
2000 | @@ -1,3 +1,10 @@ |
2001 | +patch (2.7.1-4ubuntu1) UNRELEASED; urgency=medium |
2002 | + |
2003 | + * debian/patches/fix-non-numeric-arg-crash.diff: Fix crash when option |
2004 | + expecting a numeric given non-numeric (LP: #1306412). |
2005 | + |
2006 | + -- James Hunt <james.hunt@ubuntu.com> Thu, 01 May 2014 17:00:17 +0100 |
2007 | + |
2008 | patch (2.7.1-4) unstable; urgency=low |
2009 | |
2010 | * New maintainer (closes: #728664). |
2011 | |
2012 | === added file 'debian/patches/fix-non-numeric-arg-crash.diff' |
2013 | --- debian/patches/fix-non-numeric-arg-crash.diff 1970-01-01 00:00:00 +0000 |
2014 | +++ debian/patches/fix-non-numeric-arg-crash.diff 2014-05-01 16:07:46 +0000 |
2015 | @@ -0,0 +1,19 @@ |
2016 | +Description: Fix crash when an argument expecting a numeric is given |
2017 | + a non-numeric. |
2018 | +Author: James Hunt <james.hunt@ubuntu.com> |
2019 | +Bug-Ubuntu: https://bugs.launchpad.net/bugs/1306412 |
2020 | +Forwarded: yes |
2021 | +Last-Update: 2014-05-01 |
2022 | + |
2023 | +--- |
2024 | + |
2025 | +--- a/src/patch.c |
2026 | ++++ b/src/patch.c |
2027 | +@@ -1975,5 +1975,6 @@ |
2028 | + remove_if_needed (TMPOUTNAME, &TMPOUTNAME_needs_removal); |
2029 | + remove_if_needed (TMPPATNAME, &TMPPATNAME_needs_removal); |
2030 | + remove_if_needed (TMPREJNAME, &TMPREJNAME_needs_removal); |
2031 | +- forget_output_files (); |
2032 | ++ if (files_to_output) |
2033 | ++ forget_output_files (); |
2034 | + } |
2035 | |
2036 | === modified file 'debian/patches/series' |
2037 | --- debian/patches/series 2013-11-04 12:36:11 +0000 |
2038 | +++ debian/patches/series 2014-05-01 16:07:46 +0000 |
2039 | @@ -3,3 +3,4 @@ |
2040 | m-merge |
2041 | #disable-update-version |
2042 | add_manpage_time.patch |
2043 | +fix-non-numeric-arg-crash.diff |
2044 | |
2045 | === modified file 'src/patch.c' |
2046 | --- src/patch.c 2013-06-30 16:14:19 +0000 |
2047 | +++ src/patch.c 2014-05-01 16:07:46 +0000 |
2048 | @@ -1975,5 +1975,6 @@ |
2049 | remove_if_needed (TMPOUTNAME, &TMPOUTNAME_needs_removal); |
2050 | remove_if_needed (TMPPATNAME, &TMPPATNAME_needs_removal); |
2051 | remove_if_needed (TMPREJNAME, &TMPREJNAME_needs_removal); |
2052 | - forget_output_files (); |
2053 | + if (files_to_output) |
2054 | + forget_output_files (); |
2055 | } |
Ah - I see debian has now applied the original patch so maybe easier to sync from there?