Merge lp:~jamesodhunt/ubuntu/trusty/patch/bug-1306412 into lp:ubuntu/trusty/patch

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
Reviewer Review Type Date Requested Status
Ubuntu branches Pending
Review via email: mp+217937@code.launchpad.net
To post a comment you must log in.
Revision history for this message
James Hunt (jamesodhunt) wrote :

Ah - I see debian has now applied the original patch so maybe easier to sync from there?

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

Subscribers

People subscribed via source and target branches

to all changes: