xref: /plan9/sys/src/ape/cmd/patch/patch.c (revision 0b459c2cb92b7c9d88818e9a2f72e678e5bc4553)
1 /* patch - a program to apply diffs to original files */
2 
3 /* $Id: patch.c,v 1.23 1997/07/05 10:32:23 eggert Exp $ */
4 
5 /*
6 Copyright 1984, 1985, 1986, 1987, 1988 Larry Wall
7 Copyright 1989, 1990, 1991, 1992, 1993, 1997 Free Software Foundation, Inc.
8 
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2, or (at your option)
12 any later version.
13 
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with this program; see the file COPYING.
21 If not, write to the Free Software Foundation,
22 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 */
24 
25 #define XTERN
26 #include <common.h>
27 #undef XTERN
28 #define XTERN extern
29 #include <argmatch.h>
30 #include <backupfile.h>
31 #include <getopt.h>
32 #include <inp.h>
33 #include <pch.h>
34 #include <util.h>
35 #include <version.h>
36 
37 #if HAVE_UTIME_H
38 # include <utime.h>
39 #endif
40 /* Some nonstandard hosts don't declare this structure even in <utime.h>.  */
41 #if ! HAVE_STRUCT_UTIMBUF
42 struct utimbuf
43 {
44   time_t actime;
45   time_t modtime;
46 };
47 #endif
48 
49 /* Output stream state.  */
50 struct outstate
51 {
52   FILE *ofp;
53   int after_newline;
54   int zero_output;
55 };
56 
57 /* procedures */
58 
59 static FILE *create_output_file PARAMS ((char const *));
60 static LINENUM locate_hunk PARAMS ((LINENUM));
61 static bool apply_hunk PARAMS ((struct outstate *, LINENUM));
62 static bool copy_till PARAMS ((struct outstate *, LINENUM));
63 static bool patch_match PARAMS ((LINENUM, LINENUM, LINENUM, LINENUM));
64 static bool similar PARAMS ((char const *, size_t, char const *, size_t));
65 static bool spew_output PARAMS ((struct outstate *));
66 static char const *make_temp PARAMS ((int));
67 static int numeric_string PARAMS ((char const *, int, char const *));
68 static void abort_hunk PARAMS ((void));
69 static void cleanup PARAMS ((void));
70 static void get_some_switches PARAMS ((void));
71 static void init_output PARAMS ((char const *, struct outstate *));
72 static void init_reject PARAMS ((char const *));
73 static void reinitialize_almost_everything PARAMS ((void));
74 static void usage PARAMS ((FILE *, int)) __attribute__((noreturn));
75 
76 static int make_backups;
77 static int backup_if_mismatch;
78 static char const *version_control;
79 static int remove_empty_files;
80 
81 /* TRUE if -R was specified on command line.  */
82 static int reverse_flag_specified;
83 
84 /* how many input lines have been irretractably output */
85 static LINENUM last_frozen_line;
86 
87 static char const *do_defines; /* symbol to patch using ifdef, ifndef, etc. */
88 static char const if_defined[] = "\n#ifdef %s\n";
89 static char const not_defined[] = "#ifndef %s\n";
90 static char const else_defined[] = "\n#else\n";
91 static char const end_defined[] = "\n#endif /* %s */\n";
92 
93 static int Argc;
94 static char * const *Argv;
95 
96 static FILE *rejfp;  /* reject file pointer */
97 
98 static char const *patchname;
99 static char *rejname;
100 static char const * volatile TMPREJNAME;
101 
102 static LINENUM last_offset;
103 static LINENUM maxfuzz = 2;
104 
105 static char serrbuf[BUFSIZ];
106 
107 char const program_name[] = "patch";
108 
109 /* Apply a set of diffs as appropriate. */
110 
111 int main PARAMS ((int, char **));
112 
113 int
main(argc,argv)114 main(argc,argv)
115 int argc;
116 char **argv;
117 {
118     char const *val;
119     bool somefailed = FALSE;
120     struct outstate outstate;
121 
122     init_time ();
123 
124     setbuf(stderr, serrbuf);
125 
126     bufsize = 8 * 1024;
127     buf = xmalloc (bufsize);
128 
129     strippath = INT_MAX;
130 
131     posixly_correct = getenv ("POSIXLY_CORRECT") != 0;
132     backup_if_mismatch = ! posixly_correct;
133     patch_get = ((val = getenv ("PATCH_GET"))
134 		 ? numeric_string (val, 1, "PATCH_GET value")
135 		 : posixly_correct - 1);
136 
137     {
138       char const *v = getenv ("SIMPLE_BACKUP_SUFFIX");
139       if (v && *v)
140 	simple_backup_suffix = v;
141     }
142 
143     version_control = getenv ("PATCH_VERSION_CONTROL");
144     if (! version_control)
145       version_control = getenv ("VERSION_CONTROL");
146 
147     /* Cons up the names of the global temporary files.
148        Do this before `cleanup' can possibly be called (e.g. by `pfatal').  */
149     TMPOUTNAME = make_temp ('o');
150     TMPINNAME = make_temp ('i');
151     TMPREJNAME = make_temp ('r');
152     TMPPATNAME = make_temp ('p');
153 
154     /* parse switches */
155     Argc = argc;
156     Argv = argv;
157     get_some_switches();
158 
159     if (make_backups | backup_if_mismatch)
160       backup_type = get_version (version_control);
161 
162     init_output (outfile, &outstate);
163 
164     /* Make sure we clean up in case of disaster.  */
165     set_signals(0);
166 
167     for (
168 	open_patch_file (patchname);
169 	there_is_another_patch();
170 	reinitialize_almost_everything()
171     ) {					/* for each patch in patch file */
172       int hunk = 0;
173       int failed = 0;
174       int mismatch = 0;
175       char *outname = outfile ? outfile : inname;
176 
177       if (!skip_rest_of_patch)
178 	get_input_file (inname, outname);
179 
180       if (diff_type == ED_DIFF) {
181 	outstate.zero_output = 0;
182 	if (! dry_run)
183 	  {
184 	    do_ed_script (outstate.ofp);
185 	    if (! outfile)
186 	      {
187 		struct stat statbuf;
188 		if (stat (TMPOUTNAME, &statbuf) != 0)
189 		  pfatal ("%s", TMPOUTNAME);
190 		outstate.zero_output = statbuf.st_size == 0;
191 	      }
192 	  }
193       } else {
194 	int got_hunk;
195 	int apply_anyway = 0;
196 
197 	/* initialize the patched file */
198 	if (! skip_rest_of_patch && ! outfile)
199 	  init_output (TMPOUTNAME, &outstate);
200 
201 	/* initialize reject file */
202 	init_reject(TMPREJNAME);
203 
204 	/* find out where all the lines are */
205 	if (!skip_rest_of_patch)
206 	    scan_input (inname);
207 
208 	/* from here on, open no standard i/o files, because malloc */
209 	/* might misfire and we can't catch it easily */
210 
211 	/* apply each hunk of patch */
212 	while (0 < (got_hunk = another_hunk (diff_type, reverse))) {
213 	    LINENUM where = 0; /* Pacify `gcc -Wall'.  */
214 	    LINENUM newwhere;
215 	    LINENUM fuzz = 0;
216 	    LINENUM prefix_context = pch_prefix_context ();
217 	    LINENUM suffix_context = pch_suffix_context ();
218 	    LINENUM context = (prefix_context < suffix_context
219 			       ? suffix_context : prefix_context);
220 	    LINENUM mymaxfuzz = (maxfuzz < context ? maxfuzz : context);
221 	    hunk++;
222 	    if (!skip_rest_of_patch) {
223 		do {
224 		    where = locate_hunk(fuzz);
225 		    if (! where || fuzz || last_offset)
226 		      mismatch = 1;
227 		    if (hunk == 1 && ! where && ! (force | apply_anyway)
228 			&& reverse == reverse_flag_specified) {
229 						/* dwim for reversed patch? */
230 			if (!pch_swap()) {
231 			    say (
232 "Not enough memory to try swapped hunk!  Assuming unswapped.\n");
233 			    continue;
234 			}
235 			/* Try again.  */
236 			where = locate_hunk (fuzz);
237 			if (where
238 			    && (ok_to_reverse
239 				("%s patch detected!",
240 				 (reverse
241 				  ? "Unreversed"
242 				  : "Reversed (or previously applied)"))))
243 			  reverse ^= 1;
244 			else
245 			  {
246 			    /* Put it back to normal.  */
247 			    if (! pch_swap ())
248 			      fatal ("lost hunk on alloc error!");
249 			    if (where)
250 			      {
251 				apply_anyway = 1;
252 				fuzz--; /* Undo `++fuzz' below.  */
253 				where = 0;
254 			      }
255 			  }
256 		    }
257 		} while (!skip_rest_of_patch && !where
258 			 && ++fuzz <= mymaxfuzz);
259 
260 		if (skip_rest_of_patch) {		/* just got decided */
261 		  if (outstate.ofp && ! outfile)
262 		    {
263 		      fclose (outstate.ofp);
264 		      outstate.ofp = 0;
265 		    }
266 		}
267 	    }
268 
269 	    newwhere = pch_newfirst() + last_offset;
270 	    if (skip_rest_of_patch) {
271 		abort_hunk();
272 		failed++;
273 		if (verbosity == VERBOSE)
274 		    say ("Hunk #%d ignored at %ld.\n", hunk, newwhere);
275 	    }
276 	    else if (!where
277 		     || (where == 1 && pch_says_nonexistent (reverse)
278 			 && instat.st_size)) {
279 		if (where)
280 		  say ("Patch attempted to create file `%s', which already exists.\n", inname);
281 		abort_hunk();
282 		failed++;
283 		if (verbosity != SILENT)
284 		    say ("Hunk #%d FAILED at %ld.\n", hunk, newwhere);
285 	    }
286 	    else if (! apply_hunk (&outstate, where)) {
287 		abort_hunk ();
288 		failed++;
289 		if (verbosity != SILENT)
290 		    say ("Hunk #%d FAILED at %ld.\n", hunk, newwhere);
291 	    } else {
292 		if (verbosity == VERBOSE
293 		    || (verbosity != SILENT && (fuzz || last_offset))) {
294 		    say ("Hunk #%d succeeded at %ld", hunk, newwhere);
295 		    if (fuzz)
296 			say (" with fuzz %ld", fuzz);
297 		    if (last_offset)
298 			say (" (offset %ld line%s)",
299 			    last_offset, last_offset==1?"":"s");
300 		    say (".\n");
301 		}
302 	    }
303 	}
304 
305 	if (got_hunk < 0  &&  using_plan_a) {
306 	    if (outfile)
307 	      fatal ("out of memory using Plan A");
308 	    say ("\n\nRan out of memory using Plan A -- trying again...\n\n");
309 	    if (outstate.ofp)
310 	      {
311 		fclose (outstate.ofp);
312 		outstate.ofp = 0;
313 	      }
314 	    fclose (rejfp);
315 	    continue;
316 	}
317 
318 	/* finish spewing out the new file */
319 	if (!skip_rest_of_patch)
320 	  {
321 	    assert (hunk);
322 	    if (! spew_output (&outstate))
323 	      {
324 		say ("Skipping patch.\n");
325 		skip_rest_of_patch = TRUE;
326 	      }
327 	  }
328       }
329 
330       /* and put the output where desired */
331       ignore_signals ();
332       if (! skip_rest_of_patch && ! outfile) {
333 	  if (outstate.zero_output
334 	      && (remove_empty_files
335 		  || (pch_says_nonexistent (reverse ^ 1) == 2
336 		      && ! posixly_correct)))
337 	    {
338 	      if (verbosity == VERBOSE)
339 		say ("Removing file `%s'%s.\n", outname,
340 		     dry_run ? " and any empty ancestor directories" : "");
341 	      if (! dry_run)
342 		{
343 		  move_file ((char *) 0, outname, (mode_t) 0,
344 			     (make_backups
345 			      || (backup_if_mismatch && (mismatch | failed))));
346 		  removedirs (outname);
347 		}
348 	    }
349 	  else
350 	    {
351 	      if (! outstate.zero_output
352 		  && pch_says_nonexistent (reverse ^ 1))
353 		{
354 		  mismatch = 1;
355 		  if (verbosity != SILENT)
356 		    say ("File `%s' is not empty after patch, as expected.\n",
357 			 outname);
358 		}
359 
360 	      if (! dry_run)
361 		{
362 		  time_t t;
363 
364 		  move_file (TMPOUTNAME, outname, instat.st_mode,
365 			     (make_backups
366 			      || (backup_if_mismatch && (mismatch | failed))));
367 
368 		  if ((set_time | set_utc)
369 		      && (t = pch_timestamp (reverse ^ 1)) != (time_t) -1)
370 		    {
371 		      struct utimbuf utimbuf;
372 		      utimbuf.actime = utimbuf.modtime = t;
373 
374 		      if (! force && ! inerrno
375 			  && ! pch_says_nonexistent (reverse)
376 			  && (t = pch_timestamp (reverse)) != (time_t) -1
377 			  && t != instat.st_mtime)
378 			say ("not setting time of file `%s' (time mismatch)\n",
379 			     outname);
380 		      else if (! force && (mismatch | failed))
381 			say ("not setting time of file `%s' (contents mismatch)\n",
382 			     outname);
383 		      else if (utime (outname, &utimbuf) != 0)
384 			pfatal ("can't set timestamp on file `%s'", outname);
385 		    }
386 
387 		  if (! inerrno && chmod (outname, instat.st_mode) != 0)
388 		    pfatal ("can't set permissions on file `%s'", outname);
389 		}
390 	    }
391       }
392       if (diff_type != ED_DIFF) {
393 	if (fclose (rejfp) != 0)
394 	    write_fatal ();
395 	if (failed) {
396 	    somefailed = TRUE;
397 	    say ("%d out of %d hunk%s %s", failed, hunk, "s" + (hunk == 1),
398 		 skip_rest_of_patch ? "ignored" : "FAILED");
399 	    if (outname) {
400 		char *rej = rejname;
401 		if (!rejname) {
402 		    rej = xmalloc (strlen (outname) + 5);
403 		    strcpy (rej, outname);
404 		    addext (rej, ".rej", '#');
405 		}
406 		say (" -- saving rejects to %s", rej);
407 		if (! dry_run)
408 		  {
409 		    move_file (TMPREJNAME, rej, instat.st_mode, FALSE);
410 		    if (! inerrno
411 			&& (chmod (rej, (instat.st_mode
412 					 & ~(S_IXUSR|S_IXGRP|S_IXOTH)))
413 			    != 0))
414 		      pfatal ("can't set permissions on file `%s'", rej);
415 		  }
416 		if (!rejname)
417 		    free (rej);
418 	    }
419 	    say ("\n");
420 	}
421       }
422       set_signals (1);
423     }
424     if (outstate.ofp && (ferror (outstate.ofp) || fclose (outstate.ofp) != 0))
425       write_fatal ();
426     cleanup ();
427     if (somefailed)
428       exit (1);
429     return 0;
430 }
431 
432 /* Prepare to find the next patch to do in the patch file. */
433 
434 static void
reinitialize_almost_everything()435 reinitialize_almost_everything()
436 {
437     re_patch();
438     re_input();
439 
440     input_lines = 0;
441     last_frozen_line = 0;
442 
443     if (inname) {
444 	free (inname);
445 	inname = 0;
446     }
447 
448     last_offset = 0;
449 
450     diff_type = NO_DIFF;
451 
452     if (revision) {
453 	free(revision);
454 	revision = 0;
455     }
456 
457     reverse = reverse_flag_specified;
458     skip_rest_of_patch = FALSE;
459 }
460 
461 static char const shortopts[] = "bB:cd:D:eEfF:g:i:lnNo:p:r:RstTuvV:x:Y:z:Z";
462 static struct option const longopts[] =
463 {
464   {"backup", no_argument, NULL, 'b'},
465   {"prefix", required_argument, NULL, 'B'},
466   {"context", no_argument, NULL, 'c'},
467   {"directory", required_argument, NULL, 'd'},
468   {"ifdef", required_argument, NULL, 'D'},
469   {"ed", no_argument, NULL, 'e'},
470   {"remove-empty-files", no_argument, NULL, 'E'},
471   {"force", no_argument, NULL, 'f'},
472   {"fuzz", required_argument, NULL, 'F'},
473   {"get", no_argument, NULL, 'g'},
474   {"input", required_argument, NULL, 'i'},
475   {"ignore-whitespace", no_argument, NULL, 'l'},
476   {"normal", no_argument, NULL, 'n'},
477   {"forward", no_argument, NULL, 'N'},
478   {"output", required_argument, NULL, 'o'},
479   {"strip", required_argument, NULL, 'p'},
480   {"reject-file", required_argument, NULL, 'r'},
481   {"reverse", no_argument, NULL, 'R'},
482   {"quiet", no_argument, NULL, 's'},
483   {"silent", no_argument, NULL, 's'},
484   {"batch", no_argument, NULL, 't'},
485   {"set-time", no_argument, NULL, 'T'},
486   {"unified", no_argument, NULL, 'u'},
487   {"version", no_argument, NULL, 'v'},
488   {"version-control", required_argument, NULL, 'V'},
489   {"debug", required_argument, NULL, 'x'},
490   {"basename-prefix", required_argument, NULL, 'Y'},
491   {"suffix", required_argument, NULL, 'z'},
492   {"set-utc", no_argument, NULL, 'Z'},
493   {"dry-run", no_argument, NULL, 129},
494   {"verbose", no_argument, NULL, 130},
495   {"binary", no_argument, NULL, 131},
496   {"help", no_argument, NULL, 132},
497   {"backup-if-mismatch", no_argument, NULL, 133},
498   {"no-backup-if-mismatch", no_argument, NULL, 134},
499   {NULL, no_argument, NULL, 0}
500 };
501 
502 static char const *const option_help[] =
503 {
504 "Input options:",
505 "",
506 "  -p NUM  --strip=NUM  Strip NUM leading components from file names.",
507 "  -F LINES  --fuzz LINES  Set the fuzz factor to LINES for inexact matching.",
508 "  -l  --ignore-whitespace  Ignore white space changes between patch and input.",
509 "",
510 "  -c  --context  Interpret the patch as a context difference.",
511 "  -e  --ed  Interpret the patch as an ed script.",
512 "  -n  --normal  Interpret the patch as a normal difference.",
513 "  -u  --unified  Interpret the patch as a unified difference.",
514 "",
515 "  -N  --forward  Ignore patches that appear to be reversed or already applied.",
516 "  -R  --reverse  Assume patches were created with old and new files swapped.",
517 "",
518 "  -i PATCHFILE  --input=PATCHFILE  Read patch from PATCHFILE instead of stdin.",
519 "",
520 "Output options:",
521 "",
522 "  -o FILE  --output=FILE  Output patched files to FILE.",
523 "  -r FILE  --reject-file=FILE  Output rejects to FILE.",
524 "",
525 "  -D NAME  --ifdef=NAME  Make merged if-then-else output using NAME.",
526 "  -E  --remove-empty-files  Remove output files that are empty after patching.",
527 "",
528 "  -Z  --set-utc  Set times of patched files, assuming diff uses UTC (GMT).",
529 "  -T  --set-time  Likewise, assuming local time.",
530 "",
531 "Backup and version control options:",
532 "",
533 "  -b  --backup  Back up the original contents of each file.",
534 "  --backup-if-mismatch  Back up if the patch does not match exactly.",
535 "  --no-backup-if-mismatch  Back up mismatches only if otherwise requested.",
536 "",
537 "  -V STYLE  --version-control=STYLE  Use STYLE version control.",
538 "	STYLE is either 'simple', 'numbered', or 'existing'.",
539 "  -B PREFIX  --prefix=PREFIX  Prepend PREFIX to backup file names.",
540 "  -Y PREFIX  --basename-prefix=PREFIX  Prepend PREFIX to backup file basenames.",
541 "  -z SUFFIX  --suffix=SUFFIX  Append SUFFIX to backup file names.",
542 "",
543 "  -g NUM  --get=NUM  Get files from RCS or SCCS if positive; ask if negative.",
544 "",
545 "Miscellaneous options:",
546 "",
547 "  -t  --batch  Ask no questions; skip bad-Prereq patches; assume reversed.",
548 "  -f  --force  Like -t, but ignore bad-Prereq patches, and assume unreversed.",
549 "  -s  --quiet  --silent  Work silently unless an error occurs.",
550 "  --verbose  Output extra information about the work being done.",
551 "  --dry-run  Do not actually change any files; just print what would happen.",
552 "",
553 "  -d DIR  --directory=DIR  Change the working directory to DIR first.",
554 #if HAVE_SETMODE
555 "  --binary  Read and write data in binary mode.",
556 #else
557 "  --binary  Read and write data in binary mode (no effect on this platform).",
558 #endif
559 "",
560 "  -v  --version  Output version info.",
561 "  --help  Output this help.",
562 "",
563 "Report bugs to <bug-gnu-utils@prep.ai.mit.edu>.",
564 0
565 };
566 
567 static void
usage(stream,status)568 usage (stream, status)
569      FILE *stream;
570      int status;
571 {
572   char const * const *p;
573 
574   if (status != 0)
575     {
576       fprintf (stream, "%s: Try `%s --help' for more information.\n",
577 	       program_name, Argv[0]);
578     }
579   else
580     {
581       fprintf (stream, "Usage: %s [OPTION]... [ORIGFILE [PATCHFILE]]\n\n",
582 	       Argv[0]);
583       for (p = option_help;  *p;  p++)
584 	fprintf (stream, "%s\n", *p);
585     }
586 
587   exit (status);
588 }
589 
590 /* Process switches and filenames.  */
591 
592 static void
get_some_switches()593 get_some_switches()
594 {
595     register int optc;
596 
597     if (rejname)
598 	free (rejname);
599     rejname = 0;
600     if (optind == Argc)
601 	return;
602     while ((optc = getopt_long (Argc, Argv, shortopts, longopts, (int *) 0))
603 	   != -1) {
604 	switch (optc) {
605 	    case 'b':
606 		make_backups = 1;
607 		 /* Special hack for backward compatibility with CVS 1.9.
608 		    If the last 4 args are `-b SUFFIX ORIGFILE PATCHFILE',
609 		    treat `-b' as if it were `-b -z'.  */
610 		if (Argc - optind == 3
611 		    && strcmp (Argv[optind - 1], "-b") == 0
612 		    && ! (Argv[optind + 0][0] == '-' && Argv[optind + 0][1])
613 		    && ! (Argv[optind + 1][0] == '-' && Argv[optind + 1][1])
614 		    && ! (Argv[optind + 2][0] == '-' && Argv[optind + 2][1]))
615 		  {
616 		    optarg = Argv[optind++];
617 		    if (verbosity != SILENT)
618 		      say ("warning: the `-b %s' option is obsolete; use `-b -z %s' instead\n",
619 			   optarg, optarg);
620 		    goto case_z;
621 		  }
622 		break;
623 	    case 'B':
624 		if (!*optarg)
625 		  fatal ("backup prefix is empty");
626 		origprae = savestr (optarg);
627 		break;
628 	    case 'c':
629 		diff_type = CONTEXT_DIFF;
630 		break;
631 	    case 'd':
632 		if (chdir(optarg) < 0)
633 		  pfatal ("can't change directory to `%s'", optarg);
634 		break;
635 	    case 'D':
636 		do_defines = savestr (optarg);
637 		break;
638 	    case 'e':
639 		diff_type = ED_DIFF;
640 		break;
641 	    case 'E':
642 		remove_empty_files = TRUE;
643 		break;
644 	    case 'f':
645 		force = TRUE;
646 		break;
647 	    case 'F':
648 		maxfuzz = numeric_string (optarg, 0, "fuzz factor");
649 		break;
650 	    case 'g':
651 		patch_get = numeric_string (optarg, 1, "get option value");
652 		break;
653 	    case 'i':
654 		patchname = savestr (optarg);
655 		break;
656 	    case 'l':
657 		canonicalize = TRUE;
658 		break;
659 	    case 'n':
660 		diff_type = NORMAL_DIFF;
661 		break;
662 	    case 'N':
663 		noreverse = TRUE;
664 		break;
665 	    case 'o':
666 		if (strcmp (optarg, "-") == 0)
667 		  fatal ("can't output patches to standard output");
668 		outfile = savestr (optarg);
669 		break;
670 	    case 'p':
671 		strippath = numeric_string (optarg, 0, "strip count");
672 		break;
673 	    case 'r':
674 		rejname = savestr (optarg);
675 		break;
676 	    case 'R':
677 		reverse = 1;
678 		reverse_flag_specified = 1;
679 		break;
680 	    case 's':
681 		verbosity = SILENT;
682 		break;
683 	    case 't':
684 		batch = TRUE;
685 		break;
686 	    case 'T':
687 		set_time = 1;
688 		break;
689 	    case 'u':
690 		diff_type = UNI_DIFF;
691 		break;
692 	    case 'v':
693 		version();
694 		exit (0);
695 		break;
696 	    case 'V':
697 		version_control = optarg;
698 		break;
699 #if DEBUGGING
700 	    case 'x':
701 		debug = numeric_string (optarg, 1, "debugging option");
702 		break;
703 #endif
704 	    case 'Y':
705 		if (!*optarg)
706 		  fatal ("backup basename prefix is empty");
707 		origbase = savestr (optarg);
708 		break;
709 	    case 'z':
710 	    case_z:
711 		if (!*optarg)
712 		  fatal ("backup suffix is empty");
713 		simple_backup_suffix = savestr (optarg);
714 		break;
715 	    case 'Z':
716 		set_utc = 1;
717 		break;
718 	    case 129:
719 		dry_run = TRUE;
720 		break;
721 	    case 130:
722 		verbosity = VERBOSE;
723 		break;
724 	    case 131:
725 #if HAVE_SETMODE
726 		binary_transput = O_BINARY;
727 #endif
728 		break;
729 	    case 132:
730 		usage (stdout, 0);
731 	    case 133:
732 		backup_if_mismatch = 1;
733 		break;
734 	    case 134:
735 		backup_if_mismatch = 0;
736 		break;
737 	    default:
738 		usage (stderr, 2);
739 	}
740     }
741 
742     /* Process any filename args.  */
743     if (optind < Argc)
744       {
745 	inname = savestr (Argv[optind++]);
746 	invc = -1;
747 	if (optind < Argc)
748 	  {
749 	    patchname = savestr (Argv[optind++]);
750 	    if (optind < Argc)
751 	      {
752 		fprintf (stderr, "%s: extra operand `%s'\n",
753 			 program_name, Argv[optind]);
754 		usage (stderr, 2);
755 	      }
756 	  }
757       }
758 }
759 
760 /* Handle STRING (possibly negative if NEGATIVE_ALLOWED is nonzero)
761    of type ARGTYPE_MSGID by converting it to an integer,
762    returning the result.  */
763 static int
numeric_string(string,negative_allowed,argtype_msgid)764 numeric_string (string, negative_allowed, argtype_msgid)
765      char const *string;
766      int negative_allowed;
767      char const *argtype_msgid;
768 {
769   int value = 0;
770   char const *p = string;
771   int sign = *p == '-' ? -1 : 1;
772 
773   p += *p == '-' || *p == '+';
774 
775   do
776     {
777       int v10 = value * 10;
778       int digit = *p - '0';
779       int signed_digit = sign * digit;
780       int next_value = v10 + signed_digit;
781 
782       if (9 < (unsigned) digit)
783 	fatal ("%s `%s' is not a number", argtype_msgid, string);
784 
785       if (v10 / 10 != value || (next_value < v10) != (signed_digit < 0))
786 	fatal ("%s `%s' is too large", argtype_msgid, string);
787 
788       value = next_value;
789     }
790   while (*++p);
791 
792   if (value < 0 && ! negative_allowed)
793     fatal ("%s `%s' is negative", argtype_msgid, string);
794 
795   return value;
796 }
797 
798 /* Attempt to find the right place to apply this hunk of patch. */
799 
800 static LINENUM
locate_hunk(fuzz)801 locate_hunk(fuzz)
802 LINENUM fuzz;
803 {
804     register LINENUM first_guess = pch_first () + last_offset;
805     register LINENUM offset;
806     LINENUM pat_lines = pch_ptrn_lines();
807     LINENUM prefix_context = pch_prefix_context ();
808     LINENUM suffix_context = pch_suffix_context ();
809     LINENUM context = (prefix_context < suffix_context
810 		       ? suffix_context : prefix_context);
811     LINENUM prefix_fuzz = fuzz + prefix_context - context;
812     LINENUM suffix_fuzz = fuzz + suffix_context - context;
813     LINENUM max_where = input_lines - (pat_lines - suffix_fuzz) + 1;
814     LINENUM min_where = last_frozen_line + 1 - (prefix_context - prefix_fuzz);
815     LINENUM max_pos_offset = max_where - first_guess;
816     LINENUM max_neg_offset = first_guess - min_where;
817     LINENUM max_offset = (max_pos_offset < max_neg_offset
818 			  ? max_neg_offset : max_pos_offset);
819 
820     if (!pat_lines)			/* null range matches always */
821 	return first_guess;
822 
823     /* Do not try lines <= 0.  */
824     if (first_guess <= max_neg_offset)
825 	max_neg_offset = first_guess - 1;
826 
827     if (prefix_fuzz < 0)
828       {
829 	/* Can only match start of file.  */
830 
831 	if (suffix_fuzz < 0)
832 	  /* Can only match entire file.  */
833 	  if (pat_lines != input_lines || prefix_context < last_frozen_line)
834 	    return 0;
835 
836 	offset = 1 - first_guess;
837 	if (last_frozen_line <= prefix_context
838 	    && offset <= max_pos_offset
839 	    && patch_match (first_guess, offset, (LINENUM) 0, suffix_fuzz))
840 	  {
841 	    last_offset = offset;
842 	    return first_guess + offset;
843 	  }
844 	else
845 	  return 0;
846       }
847 
848     if (suffix_fuzz < 0)
849       {
850 	/* Can only match end of file.  */
851 	offset = first_guess - (input_lines - pat_lines + 1);
852 	if (offset <= max_neg_offset
853 	    && patch_match (first_guess, -offset, prefix_fuzz, (LINENUM) 0))
854 	  {
855 	    last_offset = - offset;
856 	    return first_guess - offset;
857 	  }
858 	else
859 	  return 0;
860       }
861 
862     for (offset = 0;  offset <= max_offset;  offset++) {
863 	if (offset <= max_pos_offset
864 	    && patch_match (first_guess, offset, prefix_fuzz, suffix_fuzz)) {
865 	    if (debug & 1)
866 		say ("Offset changing from %ld to %ld\n", last_offset, offset);
867 	    last_offset = offset;
868 	    return first_guess+offset;
869 	}
870 	if (0 < offset && offset <= max_neg_offset
871 	    && patch_match (first_guess, -offset, prefix_fuzz, suffix_fuzz)) {
872 	    if (debug & 1)
873 		say ("Offset changing from %ld to %ld\n", last_offset, -offset);
874 	    last_offset = -offset;
875 	    return first_guess-offset;
876 	}
877     }
878     return 0;
879 }
880 
881 /* We did not find the pattern, dump out the hunk so they can handle it. */
882 
883 static void
abort_hunk()884 abort_hunk()
885 {
886     register LINENUM i;
887     register LINENUM pat_end = pch_end ();
888     /* add in last_offset to guess the same as the previous successful hunk */
889     LINENUM oldfirst = pch_first() + last_offset;
890     LINENUM newfirst = pch_newfirst() + last_offset;
891     LINENUM oldlast = oldfirst + pch_ptrn_lines() - 1;
892     LINENUM newlast = newfirst + pch_repl_lines() - 1;
893     char const *stars =
894       (int) NEW_CONTEXT_DIFF <= (int) diff_type ? " ****" : "";
895     char const *minuses =
896       (int) NEW_CONTEXT_DIFF <= (int) diff_type ? " ----" : " -----";
897 
898     fprintf(rejfp, "***************\n");
899     for (i=0; i<=pat_end; i++) {
900 	switch (pch_char(i)) {
901 	case '*':
902 	    if (oldlast < oldfirst)
903 		fprintf(rejfp, "*** 0%s\n", stars);
904 	    else if (oldlast == oldfirst)
905 		fprintf(rejfp, "*** %ld%s\n", oldfirst, stars);
906 	    else
907 		fprintf(rejfp, "*** %ld,%ld%s\n", oldfirst, oldlast, stars);
908 	    break;
909 	case '=':
910 	    if (newlast < newfirst)
911 		fprintf(rejfp, "--- 0%s\n", minuses);
912 	    else if (newlast == newfirst)
913 		fprintf(rejfp, "--- %ld%s\n", newfirst, minuses);
914 	    else
915 		fprintf(rejfp, "--- %ld,%ld%s\n", newfirst, newlast, minuses);
916 	    break;
917 	case ' ': case '-': case '+': case '!':
918 	    fprintf (rejfp, "%c ", pch_char (i));
919 	    /* fall into */
920 	case '\n':
921 	    pch_write_line (i, rejfp);
922 	    break;
923 	default:
924 	    fatal ("fatal internal error in abort_hunk");
925 	}
926 	if (ferror (rejfp))
927 	  write_fatal ();
928     }
929 }
930 
931 /* We found where to apply it (we hope), so do it. */
932 
933 static bool
apply_hunk(outstate,where)934 apply_hunk (outstate, where)
935      struct outstate *outstate;
936      LINENUM where;
937 {
938     register LINENUM old = 1;
939     register LINENUM lastline = pch_ptrn_lines ();
940     register LINENUM new = lastline+1;
941     register enum {OUTSIDE, IN_IFNDEF, IN_IFDEF, IN_ELSE} def_state = OUTSIDE;
942     register char const *R_do_defines = do_defines;
943     register LINENUM pat_end = pch_end ();
944     register FILE *fp = outstate->ofp;
945 
946     where--;
947     while (pch_char(new) == '=' || pch_char(new) == '\n')
948 	new++;
949 
950     while (old <= lastline) {
951 	if (pch_char(old) == '-') {
952 	    assert (outstate->after_newline);
953 	    if (! copy_till (outstate, where + old - 1))
954 		return FALSE;
955 	    if (R_do_defines) {
956 		if (def_state == OUTSIDE) {
957 		    fprintf (fp, outstate->after_newline + if_defined,
958 			     R_do_defines);
959 		    def_state = IN_IFNDEF;
960 		}
961 		else if (def_state == IN_IFDEF) {
962 		    fprintf (fp, outstate->after_newline + else_defined);
963 		    def_state = IN_ELSE;
964 		}
965 		if (ferror (fp))
966 		  write_fatal ();
967 		outstate->after_newline = pch_write_line (old, fp);
968 		outstate->zero_output = 0;
969 	    }
970 	    last_frozen_line++;
971 	    old++;
972 	}
973 	else if (new > pat_end) {
974 	    break;
975 	}
976 	else if (pch_char(new) == '+') {
977 	    if (! copy_till (outstate, where + old - 1))
978 		return FALSE;
979 	    if (R_do_defines) {
980 		if (def_state == IN_IFNDEF) {
981 		    fprintf (fp, outstate->after_newline + else_defined);
982 		    def_state = IN_ELSE;
983 		}
984 		else if (def_state == OUTSIDE) {
985 		    fprintf (fp, outstate->after_newline + if_defined,
986 			     R_do_defines);
987 		    def_state = IN_IFDEF;
988 		}
989 		if (ferror (fp))
990 		  write_fatal ();
991 	    }
992 	    outstate->after_newline = pch_write_line (new, fp);
993 	    outstate->zero_output = 0;
994 	    new++;
995 	}
996 	else if (pch_char(new) != pch_char(old)) {
997 	    if (debug & 1)
998 	      say ("oldchar = '%c', newchar = '%c'\n",
999 		   pch_char (old), pch_char (new));
1000 	    fatal ("Out-of-sync patch, lines %ld,%ld -- mangled text or line numbers, maybe?",
1001 		pch_hunk_beg() + old,
1002 		pch_hunk_beg() + new);
1003 	}
1004 	else if (pch_char(new) == '!') {
1005 	    assert (outstate->after_newline);
1006 	    if (! copy_till (outstate, where + old - 1))
1007 		return FALSE;
1008 	    assert (outstate->after_newline);
1009 	    if (R_do_defines) {
1010 	       fprintf (fp, not_defined, R_do_defines);
1011 	       if (ferror (fp))
1012 		write_fatal ();
1013 	       def_state = IN_IFNDEF;
1014 	    }
1015 
1016 	    do
1017 	      {
1018 		if (R_do_defines) {
1019 		    outstate->after_newline = pch_write_line (old, fp);
1020 		}
1021 		last_frozen_line++;
1022 		old++;
1023 	      }
1024 	    while (pch_char (old) == '!');
1025 
1026 	    if (R_do_defines) {
1027 		fprintf (fp, outstate->after_newline + else_defined);
1028 		if (ferror (fp))
1029 		  write_fatal ();
1030 		def_state = IN_ELSE;
1031 	    }
1032 
1033 	    do
1034 	      {
1035 		outstate->after_newline = pch_write_line (new, fp);
1036 		new++;
1037 	      }
1038 	    while (pch_char (new) == '!');
1039 	    outstate->zero_output = 0;
1040 	}
1041 	else {
1042 	    assert(pch_char(new) == ' ');
1043 	    old++;
1044 	    new++;
1045 	    if (R_do_defines && def_state != OUTSIDE) {
1046 		fprintf (fp, outstate->after_newline + end_defined,
1047 			 R_do_defines);
1048 		if (ferror (fp))
1049 		  write_fatal ();
1050 		outstate->after_newline = 1;
1051 		def_state = OUTSIDE;
1052 	    }
1053 	}
1054     }
1055     if (new <= pat_end && pch_char(new) == '+') {
1056 	if (! copy_till (outstate, where + old - 1))
1057 	    return FALSE;
1058 	if (R_do_defines) {
1059 	    if (def_state == OUTSIDE) {
1060 		fprintf (fp, outstate->after_newline + if_defined,
1061 			 R_do_defines);
1062 		def_state = IN_IFDEF;
1063 	    }
1064 	    else if (def_state == IN_IFNDEF) {
1065 		fprintf (fp, outstate->after_newline + else_defined);
1066 		def_state = IN_ELSE;
1067 	    }
1068 	    if (ferror (fp))
1069 	      write_fatal ();
1070 	    outstate->zero_output = 0;
1071 	}
1072 
1073 	do
1074 	  {
1075 	    if (! outstate->after_newline  &&  putc ('\n', fp) == EOF)
1076 	      write_fatal ();
1077 	    outstate->after_newline = pch_write_line (new, fp);
1078 	    outstate->zero_output = 0;
1079 	    new++;
1080 	  }
1081 	while (new <= pat_end && pch_char (new) == '+');
1082     }
1083     if (R_do_defines && def_state != OUTSIDE) {
1084 	fprintf (fp, outstate->after_newline + end_defined, R_do_defines);
1085 	if (ferror (fp))
1086 	  write_fatal ();
1087 	outstate->after_newline = 1;
1088     }
1089     return TRUE;
1090 }
1091 
1092 /* Create an output file.  */
1093 
1094 static FILE *
create_output_file(name)1095 create_output_file (name)
1096      char const *name;
1097 {
1098   int fd = create_file (name, O_WRONLY | binary_transput, instat.st_mode);
1099   FILE *f = fdopen (fd, binary_transput ? "wb" : "w");
1100   if (! f)
1101     pfatal ("can't create `%s'", name);
1102   return f;
1103 }
1104 
1105 /* Open the new file. */
1106 
1107 static void
init_output(name,outstate)1108 init_output (name, outstate)
1109      char const *name;
1110      struct outstate *outstate;
1111 {
1112   outstate->ofp = name ? create_output_file (name) : (FILE *) 0;
1113   outstate->after_newline = 1;
1114   outstate->zero_output = 1;
1115 }
1116 
1117 /* Open a file to put hunks we can't locate. */
1118 
1119 static void
init_reject(name)1120 init_reject(name)
1121      char const *name;
1122 {
1123   rejfp = create_output_file (name);
1124 }
1125 
1126 /* Copy input file to output, up to wherever hunk is to be applied. */
1127 
1128 static bool
copy_till(outstate,lastline)1129 copy_till (outstate, lastline)
1130      register struct outstate *outstate;
1131      register LINENUM lastline;
1132 {
1133     register LINENUM R_last_frozen_line = last_frozen_line;
1134     register FILE *fp = outstate->ofp;
1135     register char const *s;
1136     size_t size;
1137 
1138     if (R_last_frozen_line > lastline)
1139       {
1140 	say ("misordered hunks! output would be garbled\n");
1141 	return FALSE;
1142       }
1143     while (R_last_frozen_line < lastline)
1144       {
1145 	s = ifetch (++R_last_frozen_line, 0, &size);
1146 	if (size)
1147 	  {
1148 	    if ((! outstate->after_newline  &&  putc ('\n', fp) == EOF)
1149 		|| ! fwrite (s, sizeof *s, size, fp))
1150 	      write_fatal ();
1151 	    outstate->after_newline = s[size - 1] == '\n';
1152 	    outstate->zero_output = 0;
1153 	  }
1154       }
1155     last_frozen_line = R_last_frozen_line;
1156     return TRUE;
1157 }
1158 
1159 /* Finish copying the input file to the output file. */
1160 
1161 static bool
spew_output(outstate)1162 spew_output (outstate)
1163      struct outstate *outstate;
1164 {
1165     if (debug & 256)
1166       say ("il=%ld lfl=%ld\n", input_lines, last_frozen_line);
1167 
1168     if (last_frozen_line < input_lines)
1169       if (! copy_till (outstate, input_lines))
1170 	return FALSE;
1171 
1172     if (outstate->ofp && ! outfile)
1173       {
1174 	if (fclose (outstate->ofp) != 0)
1175 	  write_fatal ();
1176 	outstate->ofp = 0;
1177       }
1178 
1179     return TRUE;
1180 }
1181 
1182 /* Does the patch pattern match at line base+offset? */
1183 
1184 static bool
patch_match(base,offset,prefix_fuzz,suffix_fuzz)1185 patch_match (base, offset, prefix_fuzz, suffix_fuzz)
1186 LINENUM base;
1187 LINENUM offset;
1188 LINENUM prefix_fuzz;
1189 LINENUM suffix_fuzz;
1190 {
1191     register LINENUM pline = 1 + prefix_fuzz;
1192     register LINENUM iline;
1193     register LINENUM pat_lines = pch_ptrn_lines () - suffix_fuzz;
1194     size_t size;
1195     register char const *p;
1196 
1197     for (iline=base+offset+prefix_fuzz; pline <= pat_lines; pline++,iline++) {
1198 	p = ifetch (iline, offset >= 0, &size);
1199 	if (canonicalize) {
1200 	    if (!similar(p, size,
1201 			 pfetch(pline),
1202 			 pch_line_len(pline) ))
1203 		return FALSE;
1204 	}
1205 	else if (size != pch_line_len (pline)
1206 		 || memcmp (p, pfetch (pline), size) != 0)
1207 	    return FALSE;
1208     }
1209     return TRUE;
1210 }
1211 
1212 /* Do two lines match with canonicalized white space? */
1213 
1214 static bool
similar(a,alen,b,blen)1215 similar (a, alen, b, blen)
1216      register char const *a;
1217      register size_t alen;
1218      register char const *b;
1219      register size_t blen;
1220 {
1221   /* Ignore presence or absence of trailing newlines.  */
1222   alen  -=  alen && a[alen - 1] == '\n';
1223   blen  -=  blen && b[blen - 1] == '\n';
1224 
1225   for (;;)
1226     {
1227       if (!blen || (*b == ' ' || *b == '\t'))
1228 	{
1229 	  while (blen && (*b == ' ' || *b == '\t'))
1230 	    b++, blen--;
1231 	  if (alen)
1232 	    {
1233 	      if (!(*a == ' ' || *a == '\t'))
1234 		return FALSE;
1235 	      do a++, alen--;
1236 	      while (alen && (*a == ' ' || *a == '\t'));
1237 	    }
1238 	  if (!alen || !blen)
1239 	    return alen == blen;
1240 	}
1241       else if (!alen || *a++ != *b++)
1242 	return FALSE;
1243       else
1244 	alen--, blen--;
1245     }
1246 }
1247 
1248 /* Make a temporary file.  */
1249 
1250 #if HAVE_MKTEMP
1251 char *mktemp PARAMS ((char *));
1252 #endif
1253 
1254 #ifndef TMPDIR
1255 #define TMPDIR "/tmp"
1256 #endif
1257 
1258 static char const *
make_temp(letter)1259 make_temp (letter)
1260      int letter;
1261 {
1262   char *r;
1263 #if HAVE_MKTEMP
1264   char const *tmpdir = getenv ("TMPDIR");	/* Unix tradition */
1265   if (!tmpdir) tmpdir = getenv ("TMP");		/* DOS tradition */
1266   if (!tmpdir) tmpdir = getenv ("TEMP");	/* another DOS tradition */
1267   if (!tmpdir) tmpdir = TMPDIR;
1268   r = xmalloc (strlen (tmpdir) + 10);
1269   sprintf (r, "%s/p%cXXXXXX", tmpdir, letter);
1270   mktemp (r);
1271   if (!*r)
1272     pfatal ("mktemp");
1273 #else
1274   r = xmalloc (L_tmpnam);
1275   if (! (tmpnam (r) == r && *r))
1276     pfatal ("tmpnam");
1277 #endif
1278   return r;
1279 }
1280 
1281 /* Fatal exit with cleanup. */
1282 
1283 void
fatal_exit(sig)1284 fatal_exit (sig)
1285      int sig;
1286 {
1287   cleanup ();
1288 
1289   if (sig)
1290     exit_with_signal (sig);
1291 
1292   exit (2);
1293 }
1294 
1295 static void
cleanup()1296 cleanup ()
1297 {
1298   unlink (TMPINNAME);
1299   unlink (TMPOUTNAME);
1300   unlink (TMPPATNAME);
1301   unlink (TMPREJNAME);
1302 }
1303