Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
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
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
 
565
};
566
 
567
static void
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
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
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
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
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
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 *
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
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
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
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
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
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
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 *
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
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
1296
cleanup ()
1297
{
1298
  unlink (TMPINNAME);
1299
  unlink (TMPOUTNAME);
1300
  unlink (TMPPATNAME);
1301
  unlink (TMPREJNAME);
1302
}