added depend label
[unix-history] / usr / src / usr.bin / patch / patch.c
CommitLineData
9c13eddc 1#ifndef lint
be052237 2static char sccsid[] = "@(#)patch.c 5.8 (Berkeley) %G%";
9c13eddc
KM
3#endif not lint
4
5/* patch - a program to apply diffs to original files
6 *
7 * $Header: patch.c,v 1.3 85/03/26 15:07:43 lwall Exp $
8 *
9 * Copyright 1984, Larry Wall
10 *
11 * This program may be copied as long as you don't try to make any
12 * money off of it, or pretend that you wrote it.
13 *
14 * $Log: patch.c,v $
56b0c556
KM
15 * 85/08/15 van%ucbmonet@berkeley
16 * Changes for 4.3bsd diff -c.
17 *
9c13eddc
KM
18 * Revision 1.3 85/03/26 15:07:43 lwall
19 * Frozen.
20 *
21 * Revision 1.2.1.9 85/03/12 17:03:35 lwall
22 * Changed pfp->_file to fileno(pfp).
23 *
24 * Revision 1.2.1.8 85/03/12 16:30:43 lwall
25 * Check i_ptr and i_womp to make sure they aren't null before freeing.
26 * Also allow ed output to be suppressed.
27 *
28 * Revision 1.2.1.7 85/03/12 15:56:13 lwall
29 * Added -p option from jromine@uci-750a.
30 *
31 * Revision 1.2.1.6 85/03/12 12:12:51 lwall
32 * Now checks for normalness of file to patch.
33 *
34 * Revision 1.2.1.5 85/03/12 11:52:12 lwall
35 * Added -D (#ifdef) option from joe@fluke.
36 *
37 * Revision 1.2.1.4 84/12/06 11:14:15 lwall
38 * Made smarter about SCCS subdirectories.
39 *
40 * Revision 1.2.1.3 84/12/05 11:18:43 lwall
41 * Added -l switch to do loose string comparison.
42 *
43 * Revision 1.2.1.2 84/12/04 09:47:13 lwall
44 * Failed hunk count not reset on multiple patch file.
45 *
46 * Revision 1.2.1.1 84/12/04 09:42:37 lwall
47 * Branch for sdcrdcf changes.
48 *
49 * Revision 1.2 84/11/29 13:29:51 lwall
50 * Linted. Identifiers uniqified. Fixed i_ptr malloc() bug. Fixed
51 * multiple calls to mktemp(). Will now work on machines that can only
52 * read 32767 chars. Added -R option for diffs with new and old swapped.
53 * Various cosmetic changes.
54 *
55 * Revision 1.1 84/11/09 17:03:58 lwall
56 * Initial revision
57 *
58 */
59
60#define DEBUGGING
61
62/* shut lint up about the following when return value ignored */
63
64#define Signal (void)signal
65#define Unlink (void)unlink
66#define Lseek (void)lseek
67#define Fseek (void)fseek
68#define Fstat (void)fstat
69#define Pclose (void)pclose
70#define Close (void)close
71#define Fclose (void)fclose
72#define Fflush (void)fflush
73#define Sprintf (void)sprintf
74#define Mktemp (void)mktemp
75#define Strcpy (void)strcpy
76#define Strcat (void)strcat
77
78#include <stdio.h>
79#include <assert.h>
80#include <sys/types.h>
81#include <sys/stat.h>
82#include <ctype.h>
83#include <signal.h>
84
85/* constants */
86
87#define TRUE (1)
88#define FALSE (0)
89
756fee39 90#define MAXHUNKSIZE 2000
9c13eddc
KM
91#define MAXLINELEN 1024
92#define BUFFERSIZE 1024
93#define ORIGEXT ".orig"
94#define SCCSPREFIX "s."
95#define GET "get -e %s"
96#define RCSSUFFIX ",v"
97#define CHECKOUT "co -l %s"
98
99/* handy definitions */
100
101#define Null(t) ((t)0)
102#define Nullch Null(char *)
103#define Nullfp Null(FILE *)
104
105#define Ctl(ch) (ch & 037)
106
107#define strNE(s1,s2) (strcmp(s1,s2))
108#define strEQ(s1,s2) (!strcmp(s1,s2))
109#define strnNE(s1,s2,l) (strncmp(s1,s2,l))
110#define strnEQ(s1,s2,l) (!strncmp(s1,s2,l))
111
112/* typedefs */
113
114typedef char bool;
115typedef long LINENUM; /* must be signed */
116typedef unsigned MEM; /* what to feed malloc */
117
118/* globals */
119
120int Argc; /* guess */
121char **Argv;
122
123struct stat filestat; /* file statistics area */
124
125char serrbuf[BUFSIZ]; /* buffer for stderr */
126char buf[MAXLINELEN]; /* general purpose buffer */
127FILE *pfp = Nullfp; /* patch file pointer */
128FILE *ofp = Nullfp; /* output file pointer */
129FILE *rejfp = Nullfp; /* reject file pointer */
130
131LINENUM input_lines = 0; /* how long is input file in lines */
132LINENUM last_frozen_line = 0; /* how many input lines have been */
133 /* irretractibly output */
134
135#define MAXFILEC 2
136int filec = 0; /* how many file arguments? */
137char *filearg[MAXFILEC];
138
139char *outname = Nullch;
140char rejname[128];
141
142char *origext = Nullch;
143
144char TMPOUTNAME[] = "/tmp/patchoXXXXXX";
145char TMPINNAME[] = "/tmp/patchiXXXXXX"; /* you might want /usr/tmp here */
146char TMPREJNAME[] = "/tmp/patchrXXXXXX";
147char TMPPATNAME[] = "/tmp/patchpXXXXXX";
148
149LINENUM last_offset = 0;
150#ifdef DEBUGGING
151int debug = 0;
152#endif
153bool verbose = TRUE;
154bool reverse = FALSE;
adcf44a2
JB
155bool noreverse = FALSE;
156bool skip_this_patch = FALSE;
9c13eddc
KM
157bool usepath = FALSE;
158bool canonicalize = FALSE;
159
160#define CONTEXT_DIFF 1
161#define NORMAL_DIFF 2
162#define ED_DIFF 3
56b0c556 163#define NEW_CONTEXT_DIFF 4
9c13eddc
KM
164int diff_type = 0;
165
166int do_defines = 0; /* patch using ifdef, ifndef, etc. */
167char if_defined[128]; /* #ifdef xyzzy */
168char not_defined[128]; /* #ifndef xyzzy */
169char else_defined[] = "#else\n"; /* #else */
170char end_defined[128]; /* #endif xyzzy */
171
172char *revision = Nullch; /* prerequisite revision, if any */
173
174/* procedures */
175
176LINENUM locate_hunk();
177bool patch_match();
178bool similar();
179char *malloc();
180char *savestr();
181char *strcpy();
182char *strcat();
183char *sprintf(); /* usually */
184int my_exit();
185bool rev_in_string();
186char *fetchname();
187long atol();
188long lseek();
189char *mktemp();
190
191/* patch type */
192
193bool there_is_another_patch();
194bool another_hunk();
195char *pfetch();
196int pch_line_len();
197LINENUM pch_first();
198LINENUM pch_ptrn_lines();
199LINENUM pch_newfirst();
200LINENUM pch_repl_lines();
201LINENUM pch_end();
202LINENUM pch_context();
203LINENUM pch_hunk_beg();
204char pch_char();
205char *pfetch();
206char *pgets();
207
208/* input file type */
209
210char *ifetch();
211
212/* apply a context patch to a named file */
213
214main(argc,argv)
215int argc;
216char **argv;
217{
218 LINENUM where;
219 int hunk = 0;
220 int failed = 0;
221 int i;
222
223 setbuf(stderr,serrbuf);
224 for (i = 0; i<MAXFILEC; i++)
225 filearg[i] = Nullch;
226 Mktemp(TMPOUTNAME);
227 Mktemp(TMPINNAME);
228 Mktemp(TMPREJNAME);
229 Mktemp(TMPPATNAME);
230
231 /* parse switches */
232 Argc = argc;
233 Argv = argv;
234 get_some_switches();
235
236 /* make sure we clean up /tmp in case of disaster */
237 set_signals();
238
239 for (
240 open_patch_file(filearg[1]);
241 there_is_another_patch();
242 reinitialize_almost_everything()
243 ) { /* for each patch in patch file */
244
245 if (outname == Nullch)
246 outname = savestr(filearg[0]);
247
248 /* initialize the patched file */
249 init_output(TMPOUTNAME);
250
251 /* for ed script just up and do it and exit */
252 if (diff_type == ED_DIFF) {
253 do_ed_script();
254 continue;
255 }
256
257 /* initialize reject file */
258 init_reject(TMPREJNAME);
259
260 /* find out where all the lines are */
261 scan_input(filearg[0]);
262
263 /* from here on, open no standard i/o files, because malloc */
264 /* might misfire */
265
266 /* apply each hunk of patch */
267 hunk = 0;
268 failed = 0;
269 while (another_hunk()) {
270 hunk++;
271 where = locate_hunk();
272 if (hunk == 1 && where == Null(LINENUM)) {
273 /* dwim for reversed patch? */
274 pch_swap();
275 reverse = !reverse;
276 where = locate_hunk(); /* try again */
277 if (where == Null(LINENUM)) {
278 pch_swap(); /* no, put it back to normal */
279 reverse = !reverse;
adcf44a2
JB
280 } else if (noreverse) {
281 pch_swap(); /* put it back to normal */
282 reverse = !reverse;
283 say("Ignoring previously applied (or reversed) patch.\n");
284 skip_this_patch = TRUE;
9c13eddc
KM
285 }
286 else {
287 say("%seversed (or previously applied) patch detected! %s -R.\n",
288 reverse ? "R" : "Unr",
289 reverse ? "Assuming" : "Ignoring");
290 }
291 }
adcf44a2 292 if (where == Null(LINENUM) || skip_this_patch) {
9c13eddc
KM
293 abort_hunk();
294 failed++;
295 if (verbose)
296 say("Hunk #%d failed.\n",hunk);
297 }
298 else {
299 apply_hunk(where);
300 if (verbose)
301 if (last_offset)
302 say("Hunk #%d succeeded (offset %d line%s).\n",
303 hunk,last_offset,last_offset==1?"":"s");
304 else
305 say("Hunk #%d succeeded.\n", hunk);
306 }
307 }
308
309 assert(hunk);
310
311 /* finish spewing out the new file */
312 spew_output();
313
314 /* and put the output where desired */
315 ignore_signals();
316 move_file(TMPOUTNAME,outname);
317 Fclose(rejfp);
318 rejfp = Nullfp;
319 if (failed) {
320 if (!*rejname) {
321 Strcpy(rejname, outname);
322 Strcat(rejname, ".rej");
323 }
324 say("%d out of %d hunks failed--saving rejects to %s\n",
325 failed, hunk, rejname);
326 move_file(TMPREJNAME,rejname);
327 }
328 set_signals();
329 }
330 my_exit(0);
331}
332
333reinitialize_almost_everything()
334{
335 re_patch();
336 re_input();
337
338 input_lines = 0;
339 last_frozen_line = 0;
340
341 filec = 0;
342 if (filearg[0] != Nullch) {
343 free(filearg[0]);
344 filearg[0] = Nullch;
345 }
346
347 if (outname != Nullch) {
348 free(outname);
349 outname = Nullch;
350 }
351
352 last_offset = 0;
353
354 diff_type = 0;
355
356 if (revision != Nullch) {
357 free(revision);
358 revision = Nullch;
359 }
360
361 reverse = FALSE;
adcf44a2 362 skip_this_patch = FALSE;
9c13eddc
KM
363
364 get_some_switches();
365
366 if (filec >= 2)
367 fatal("You may not change to a different patch file.\n");
368}
369
370get_some_switches()
371{
372 register char *s;
373
374 rejname[0] = '\0';
375 if (!Argc)
376 return;
377 for (Argc--,Argv++; Argc; Argc--,Argv++) {
378 s = Argv[0];
379 if (strEQ(s,"+")) {
380 return; /* + will be skipped by for loop */
381 }
382 if (*s != '-' || !s[1]) {
383 if (filec == MAXFILEC)
384 fatal("Too many file arguments.\n");
385 filearg[filec++] = savestr(s);
386 }
387 else {
388 switch (*++s) {
389 case 'b':
390 origext = savestr(Argv[1]);
391 Argc--,Argv++;
392 break;
393 case 'c':
394 diff_type = CONTEXT_DIFF;
395 break;
396 case 'd':
397 if (chdir(Argv[1]) < 0)
398 fatal("Can't cd to %s.\n",Argv[1]);
399 Argc--,Argv++;
400 break;
401 case 'D':
402 do_defines++;
403 Sprintf(if_defined, "#ifdef %s\n", Argv[1]);
404 Sprintf(not_defined, "#ifndef %s\n", Argv[1]);
405 Sprintf(end_defined, "#endif %s\n", Argv[1]);
406 Argc--,Argv++;
407 break;
408 case 'e':
409 diff_type = ED_DIFF;
410 break;
411 case 'l':
412 canonicalize = TRUE;
413 break;
414 case 'n':
415 diff_type = NORMAL_DIFF;
416 break;
417 case 'o':
418 outname = savestr(Argv[1]);
419 Argc--,Argv++;
420 break;
421 case 'p':
422 usepath = TRUE; /* do not strip path names */
423 break;
424 case 'r':
425 Strcpy(rejname,Argv[1]);
426 Argc--,Argv++;
427 break;
428 case 'R':
429 reverse = TRUE;
430 break;
adcf44a2
JB
431 case 'N':
432 noreverse = TRUE;
433 break;
9c13eddc
KM
434 case 's':
435 verbose = FALSE;
436 break;
437#ifdef DEBUGGING
438 case 'x':
439 debug = atoi(s+1);
440 break;
441#endif
442 default:
443 fatal("Unrecognized switch: %s\n",Argv[0]);
444 }
445 }
446 }
447}
448
449LINENUM
450locate_hunk()
451{
452 register LINENUM first_guess = pch_first() + last_offset;
453 register LINENUM offset;
454 LINENUM pat_lines = pch_ptrn_lines();
455 register LINENUM max_pos_offset = input_lines - first_guess
456 - pat_lines + 1;
457 register LINENUM max_neg_offset = first_guess - last_frozen_line - 1
458 - pch_context();
459
460 if (!pat_lines) /* null range matches always */
461 return first_guess;
462 if (max_neg_offset >= first_guess) /* do not try lines < 0 */
463 max_neg_offset = first_guess - 1;
464 if (first_guess <= input_lines && patch_match(first_guess,(LINENUM)0))
465 return first_guess;
466 for (offset = 1; ; offset++) {
467 bool check_after = (offset <= max_pos_offset);
468 bool check_before = (offset <= max_pos_offset);
469
470 if (check_after && patch_match(first_guess,offset)) {
471#ifdef DEBUGGING
472 if (debug & 1)
473 printf("Offset changing from %d to %d\n",last_offset,offset);
474#endif
475 last_offset = offset;
476 return first_guess+offset;
477 }
478 else if (check_before && patch_match(first_guess,-offset)) {
479#ifdef DEBUGGING
480 if (debug & 1)
481 printf("Offset changing from %d to %d\n",last_offset,-offset);
482#endif
483 last_offset = -offset;
484 return first_guess-offset;
485 }
486 else if (!check_before && !check_after)
487 return Null(LINENUM);
488 }
489}
490
491/* we did not find the pattern, dump out the hunk so they can handle it */
492
493abort_hunk()
494{
495 register LINENUM i;
496 register LINENUM pat_end = pch_end();
497 /* add in last_offset to guess the same as the previous successful hunk */
498 int oldfirst = pch_first() + last_offset;
499 int newfirst = pch_newfirst() + last_offset;
500 int oldlast = oldfirst + pch_ptrn_lines() - 1;
501 int newlast = newfirst + pch_repl_lines() - 1;
502
503 fprintf(rejfp,"***************\n");
504 for (i=0; i<=pat_end; i++) {
505 switch (pch_char(i)) {
506 case '*':
756fee39
VJ
507 if (diff_type == NEW_CONTEXT_DIFF)
508 fprintf(rejfp,"*** %d,%d ****\n", oldfirst, oldlast);
509 else
510 fprintf(rejfp,"*** %d,%d\n", oldfirst, oldlast);
9c13eddc
KM
511 break;
512 case '=':
513 fprintf(rejfp,"--- %d,%d -----\n", newfirst, newlast);
514 break;
515 case '\n':
516 fprintf(rejfp,"%s", pfetch(i));
517 break;
518 case ' ': case '-': case '+': case '!':
519 fprintf(rejfp,"%c %s", pch_char(i), pfetch(i));
520 break;
521 default:
522 say("Fatal internal error in abort_hunk().\n");
523 abort();
524 }
525 }
526}
527
528/* we found where to apply it (we hope), so do it */
529
530apply_hunk(where)
531LINENUM where;
532{
533 register LINENUM old = 1;
534 register LINENUM lastline = pch_ptrn_lines();
535 register LINENUM new = lastline+1;
536 register int def_state = 0; /* -1 = ifndef, 1 = ifdef */
537
538 where--;
539 while (pch_char(new) == '=' || pch_char(new) == '\n')
540 new++;
541
542 while (old <= lastline) {
543 if (pch_char(old) == '-') {
544 copy_till(where + old - 1);
545 if (do_defines) {
546 if (def_state == 0) {
547 fputs(not_defined, ofp);
548 def_state = -1;
549 } else
550 if (def_state == 1) {
551 fputs(else_defined, ofp);
552 def_state = 2;
553 }
554 fputs(pfetch(old), ofp);
555 }
556 last_frozen_line++;
557 old++;
558 }
559 else if (pch_char(new) == '+') {
560 copy_till(where + old - 1);
561 if (do_defines) {
562 if (def_state == -1) {
563 fputs(else_defined, ofp);
564 def_state = 2;
565 } else
566 if (def_state == 0) {
567 fputs(if_defined, ofp);
568 def_state = 1;
569 }
570 }
571 fputs(pfetch(new),ofp);
572 new++;
573 }
574 else {
575 if (pch_char(new) != pch_char(old)) {
576 say("Out-of-sync patch, lines %d,%d\n",
577 pch_hunk_beg() + old - 1,
578 pch_hunk_beg() + new - 1);
579#ifdef DEBUGGING
580 printf("oldchar = '%c', newchar = '%c'\n",
581 pch_char(old), pch_char(new));
582#endif
583 my_exit(1);
584 }
585 if (pch_char(new) == '!') {
586 copy_till(where + old - 1);
587 if (do_defines) {
588 fputs(not_defined,ofp);
589 def_state = -1;
590 }
591 while (pch_char(old) == '!') {
592 if (do_defines) {
593 fputs(pfetch(old),ofp);
594 }
595 last_frozen_line++;
596 old++;
597 }
598 if (do_defines) {
599 fputs(else_defined, ofp);
600 def_state = 2;
601 }
602 while (pch_char(new) == '!') {
603 fputs(pfetch(new),ofp);
604 new++;
605 }
606 if (do_defines) {
607 fputs(end_defined, ofp);
608 def_state = 0;
609 }
610 }
611 else {
612 assert(pch_char(new) == ' ');
613 old++;
614 new++;
615 }
616 }
617 }
618 if (new <= pch_end() && pch_char(new) == '+') {
619 copy_till(where + old - 1);
620 if (do_defines) {
621 if (def_state == 0) {
622 fputs(if_defined, ofp);
623 def_state = 1;
624 } else
625 if (def_state == -1) {
626 fputs(else_defined, ofp);
627 def_state = 2;
628 }
629 }
630 while (new <= pch_end() && pch_char(new) == '+') {
631 fputs(pfetch(new),ofp);
632 new++;
633 }
634 }
635 if (do_defines && def_state) {
636 fputs(end_defined, ofp);
637 }
638}
639
640do_ed_script()
641{
642 FILE *pipefp, *popen();
643 bool this_line_is_command = FALSE;
644 register char *t;
645 long beginning_of_this_line;
646
647 Unlink(TMPOUTNAME);
648 copy_file(filearg[0],TMPOUTNAME);
649 if (verbose)
650 Sprintf(buf,"/bin/ed %s",TMPOUTNAME);
651 else
652 Sprintf(buf,"/bin/ed - %s",TMPOUTNAME);
653 pipefp = popen(buf,"w");
654 for (;;) {
655 beginning_of_this_line = ftell(pfp);
656 if (pgets(buf,sizeof buf,pfp) == Nullch) {
657 next_intuit_at(beginning_of_this_line);
658 break;
659 }
660 for (t=buf; isdigit(*t) || *t == ','; t++) ;
661 this_line_is_command = (isdigit(*buf) &&
662 (*t == 'd' || *t == 'c' || *t == 'a') );
663 if (this_line_is_command) {
664 fputs(buf,pipefp);
665 if (*t != 'd') {
666 while (pgets(buf,sizeof buf,pfp) != Nullch) {
667 fputs(buf,pipefp);
668 if (strEQ(buf,".\n"))
669 break;
670 }
671 }
672 }
673 else {
674 next_intuit_at(beginning_of_this_line);
675 break;
676 }
677 }
678 fprintf(pipefp,"w\n");
679 fprintf(pipefp,"q\n");
680 Fflush(pipefp);
681 Pclose(pipefp);
682 ignore_signals();
683 move_file(TMPOUTNAME,outname);
684 set_signals();
685}
686
687init_output(name)
688char *name;
689{
690 ofp = fopen(name,"w");
691 if (ofp == Nullfp)
692 fatal("patch: can't create %s.\n",name);
693}
694
695init_reject(name)
696char *name;
697{
698 rejfp = fopen(name,"w");
699 if (rejfp == Nullfp)
700 fatal("patch: can't create %s.\n",name);
701}
702
703move_file(from,to)
704char *from, *to;
705{
706 char bakname[512];
707 register char *s;
708 int fromfd;
709 register int i;
710
711 /* to stdout? */
712
713 if (strEQ(to,"-")) {
714#ifdef DEBUGGING
715 if (debug & 4)
716 say("Moving %s to stdout.\n",from);
717#endif
718 fromfd = open(from,0);
719 if (fromfd < 0)
720 fatal("patch: internal error, can't reopen %s\n",from);
721 while ((i=read(fromfd,buf,sizeof buf)) > 0)
722 if (write(1,buf,i) != 1)
723 fatal("patch: write failed\n");
724 Close(fromfd);
725 return;
726 }
727
728 Strcpy(bakname,to);
729 Strcat(bakname,origext?origext:ORIGEXT);
730 if (stat(to,&filestat) >= 0) { /* output file exists */
731 dev_t to_device = filestat.st_dev;
732 ino_t to_inode = filestat.st_ino;
733 char *simplename = bakname;
734
735 for (s=bakname; *s; s++) {
736 if (*s == '/')
737 simplename = s+1;
738 }
739 /* find a backup name that is not the same file */
740 while (stat(bakname,&filestat) >= 0 &&
741 to_device == filestat.st_dev && to_inode == filestat.st_ino) {
742 for (s=simplename; *s && !islower(*s); s++) ;
743 if (*s)
744 *s = toupper(*s);
745 else
746 Strcpy(simplename, simplename+1);
747 }
748 while (unlink(bakname) >= 0) ; /* while() is for benefit of Eunice */
749#ifdef DEBUGGING
750 if (debug & 4)
751 say("Moving %s to %s.\n",to,bakname);
752#endif
753 if (link(to,bakname) < 0) {
754 say("patch: can't backup %s, output is in %s\n",
755 to,from);
756 return;
757 }
758 while (unlink(to) >= 0) ;
759 }
760#ifdef DEBUGGING
761 if (debug & 4)
762 say("Moving %s to %s.\n",from,to);
763#endif
764 if (link(from,to) < 0) { /* different file system? */
765 int tofd;
766
767 tofd = creat(to,0666);
768 if (tofd < 0) {
769 say("patch: can't create %s, output is in %s.\n",
770 to, from);
771 return;
772 }
773 fromfd = open(from,0);
774 if (fromfd < 0)
775 fatal("patch: internal error, can't reopen %s\n",from);
776 while ((i=read(fromfd,buf,sizeof buf)) > 0)
777 if (write(tofd,buf,i) != i)
778 fatal("patch: write failed\n");
779 Close(fromfd);
780 Close(tofd);
781 }
782 Unlink(from);
783}
784
785copy_file(from,to)
786char *from, *to;
787{
788 int tofd;
789 int fromfd;
790 register int i;
791
792 tofd = creat(to,0666);
793 if (tofd < 0)
794 fatal("patch: can't create %s.\n", to);
795 fromfd = open(from,0);
796 if (fromfd < 0)
797 fatal("patch: internal error, can't reopen %s\n",from);
798 while ((i=read(fromfd,buf,sizeof buf)) > 0)
799 if (write(tofd,buf,i) != i)
800 fatal("patch: write (%s) failed\n", to);
801 Close(fromfd);
802 Close(tofd);
803}
804
805copy_till(lastline)
806register LINENUM lastline;
807{
808 if (last_frozen_line > lastline)
809 say("patch: misordered hunks! output will be garbled.\n");
810 while (last_frozen_line < lastline) {
811 dump_line(++last_frozen_line);
812 }
813}
814
815spew_output()
816{
817 copy_till(input_lines); /* dump remainder of file */
818 Fclose(ofp);
819 ofp = Nullfp;
820}
821
822dump_line(line)
823LINENUM line;
824{
825 register char *s;
826
827 for (s=ifetch(line,0); putc(*s,ofp) != '\n'; s++) ;
828}
829
830/* does the patch pattern match at line base+offset? */
831
832bool
833patch_match(base,offset)
834LINENUM base;
835LINENUM offset;
836{
837 register LINENUM pline;
838 register LINENUM iline;
839 register LINENUM pat_lines = pch_ptrn_lines();
840
841 for (pline = 1, iline=base+offset; pline <= pat_lines; pline++,iline++) {
842 if (canonicalize) {
843 if (!similar(ifetch(iline,(offset >= 0)),
844 pfetch(pline),
845 pch_line_len(pline) ))
846 return FALSE;
847 }
848 else if (strnNE(ifetch(iline,(offset >= 0)),
849 pfetch(pline),
850 pch_line_len(pline) ))
851 return FALSE;
852 }
853 return TRUE;
854}
855
856/* match two lines with canonicalized white space */
857
858bool
859similar(a,b,len)
860register char *a, *b;
861register int len;
862{
863 while (len) {
864 if (isspace(*b)) { /* whitespace (or \n) to match? */
865 if (!isspace(*a)) /* no corresponding whitespace? */
866 return FALSE;
867 while (len && isspace(*b) && *b != '\n')
868 b++,len--; /* skip pattern whitespace */
869 while (isspace(*a) && *a != '\n')
870 a++; /* skip target whitespace */
871 if (*a == '\n' || *b == '\n')
872 return (*a == *b); /* should end in sync */
873 }
874 else if (*a++ != *b++) /* match non-whitespace chars */
875 return FALSE;
876 else
877 len--; /* probably not necessary */
878 }
879 return TRUE; /* actually, this is not reached */
880 /* since there is always a \n */
881}
882
883/* input file with indexable lines abstract type */
884
885bool using_plan_a = TRUE;
886static long i_size; /* size of the input file */
887static char *i_womp; /* plan a buffer for entire file */
888static char **i_ptr; /* pointers to lines in i_womp */
889
890static int tifd = -1; /* plan b virtual string array */
891static char *tibuf[2]; /* plan b buffers */
892static LINENUM tiline[2] = {-1,-1}; /* 1st line in each buffer */
893static LINENUM lines_per_buf; /* how many lines per buffer */
894static int tireclen; /* length of records in tmp file */
895
896re_input()
897{
898 if (using_plan_a) {
899 i_size = 0;
900 /*NOSTRICT*/
901 if (i_ptr != Null(char**))
902 free((char *)i_ptr);
903 if (i_womp != Nullch)
904 free(i_womp);
905 i_womp = Nullch;
906 i_ptr = Null(char **);
907 }
908 else {
909 using_plan_a = TRUE; /* maybe the next one is smaller */
910 Close(tifd);
911 tifd = -1;
912 free(tibuf[0]);
913 free(tibuf[1]);
914 tibuf[0] = tibuf[1] = Nullch;
915 tiline[0] = tiline[1] = -1;
916 tireclen = 0;
917 }
918}
919
920scan_input(filename)
921char *filename;
922{
923 bool plan_a();
924
925 if (!plan_a(filename))
926 plan_b(filename);
927}
928
929/* try keeping everything in memory */
930
931bool
932plan_a(filename)
933char *filename;
934{
935 int ifd;
936 register char *s;
937 register LINENUM iline;
938
939 if (stat(filename,&filestat) < 0) {
940 Sprintf(buf,"RCS/%s%s",filename,RCSSUFFIX);
941 if (stat(buf,&filestat) >= 0 || stat(buf+4,&filestat) >= 0) {
942 Sprintf(buf,CHECKOUT,filename);
943 if (verbose)
944 say("Can't find %s--attempting to check it out from RCS.\n",
945 filename);
946 if (system(buf) || stat(filename,&filestat))
947 fatal("Can't check out %s.\n",filename);
948 }
949 else {
950 Sprintf(buf,"SCCS/%s%s",SCCSPREFIX,filename);
951 if (stat(buf,&filestat) >= 0 || stat(buf+5,&filestat) >= 0) {
952 Sprintf(buf,GET,filename);
953 if (verbose)
954 say("Can't find %s--attempting to get it from SCCS.\n",
955 filename);
956 if (system(buf) || stat(filename,&filestat))
957 fatal("Can't get %s.\n",filename);
958 }
959 else
960 fatal("Can't find %s.\n",filename);
961 }
962 }
963 if ((filestat.st_mode & S_IFMT) & ~S_IFREG)
964 fatal("%s is not a normal file--can't patch.\n",filename);
965 i_size = filestat.st_size;
966 /*NOSTRICT*/
967 i_womp = malloc((MEM)(i_size+2));
968 if (i_womp == Nullch)
969 return FALSE;
970 if ((ifd = open(filename,0)) < 0)
971 fatal("Can't open file %s\n",filename);
972 /*NOSTRICT*/
973 if (read(ifd,i_womp,(int)i_size) != i_size) {
974 Close(ifd);
975 free(i_womp);
976 return FALSE;
977 }
978 Close(ifd);
979 if (i_womp[i_size-1] != '\n')
980 i_womp[i_size++] = '\n';
981 i_womp[i_size] = '\0';
982
983 /* count the lines in the buffer so we know how many pointers we need */
984
985 iline = 0;
986 for (s=i_womp; *s; s++) {
987 if (*s == '\n')
988 iline++;
989 }
990 /*NOSTRICT*/
991 i_ptr = (char **)malloc((MEM)((iline + 2) * sizeof(char *)));
992 if (i_ptr == Null(char **)) { /* shucks, it was a near thing */
993 free((char *)i_womp);
994 return FALSE;
995 }
996
997 /* now scan the buffer and build pointer array */
998
999 iline = 1;
1000 i_ptr[iline] = i_womp;
1001 for (s=i_womp; *s; s++) {
1002 if (*s == '\n')
1003 i_ptr[++iline] = s+1; /* these are NOT null terminated */
1004 }
1005 input_lines = iline - 1;
1006
1007 /* now check for revision, if any */
1008
1009 if (revision != Nullch) {
1010 if (!rev_in_string(i_womp)) {
1011 ask("This file doesn't appear to be the %s version--patch anyway? [n] ",
1012 revision);
1013 if (*buf != 'y')
1014 fatal("Aborted.\n");
1015 }
1016 else if (verbose)
1017 say("Good. This file appears to be the %s version.\n",
1018 revision);
1019 }
1020 return TRUE; /* plan a will work */
1021}
1022
1023/* keep (virtually) nothing in memory */
1024
1025plan_b(filename)
1026char *filename;
1027{
1028 FILE *ifp;
1029 register int i = 0;
1030 register int maxlen = 1;
1031 bool found_revision = (revision == Nullch);
1032
1033 using_plan_a = FALSE;
1034 if ((ifp = fopen(filename,"r")) == Nullfp)
1035 fatal("Can't open file %s\n",filename);
1036 if ((tifd = creat(TMPINNAME,0666)) < 0)
1037 fatal("Can't open file %s\n",TMPINNAME);
1038 while (fgets(buf,sizeof buf, ifp) != Nullch) {
1039 if (revision != Nullch && !found_revision && rev_in_string(buf))
1040 found_revision = TRUE;
1041 if ((i = strlen(buf)) > maxlen)
1042 maxlen = i; /* find longest line */
1043 }
1044 if (revision != Nullch) {
1045 if (!found_revision) {
1046 ask("This file doesn't appear to be the %s version--patch anyway? [n] ",
1047 revision);
1048 if (*buf != 'y')
1049 fatal("Aborted.\n");
1050 }
1051 else if (verbose)
1052 say("Good. This file appears to be the %s version.\n",
1053 revision);
1054 }
1055 Fseek(ifp,0L,0); /* rewind file */
1056 lines_per_buf = BUFFERSIZE / maxlen;
1057 tireclen = maxlen;
1058 tibuf[0] = malloc((MEM)(BUFFERSIZE + 1));
1059 tibuf[1] = malloc((MEM)(BUFFERSIZE + 1));
1060 if (tibuf[1] == Nullch)
1061 fatal("Can't seem to get enough memory.\n");
1062 for (i=1; ; i++) {
1063 if (! (i % lines_per_buf)) /* new block */
1064 if (write(tifd,tibuf[0],BUFFERSIZE) < BUFFERSIZE)
1065 fatal("patch: can't write temp file.\n");
1066 if (fgets(tibuf[0] + maxlen * (i%lines_per_buf), maxlen + 1, ifp)
1067 == Nullch) {
1068 input_lines = i - 1;
1069 if (i % lines_per_buf)
1070 if (write(tifd,tibuf[0],BUFFERSIZE) < BUFFERSIZE)
1071 fatal("patch: can't write temp file.\n");
1072 break;
1073 }
1074 }
1075 Fclose(ifp);
1076 Close(tifd);
1077 if ((tifd = open(TMPINNAME,0)) < 0) {
1078 fatal("Can't reopen file %s\n",TMPINNAME);
1079 }
1080}
1081
1082/* fetch a line from the input file, \n terminated, not necessarily \0 */
1083char *
1084ifetch(line,whichbuf)
1085register LINENUM line;
1086int whichbuf; /* ignored when file in memory */
1087{
1088 if (line < 1 || line > input_lines)
1089 return "";
1090 if (using_plan_a)
1091 return i_ptr[line];
1092 else {
1093 LINENUM offline = line % lines_per_buf;
1094 LINENUM baseline = line - offline;
1095
1096 if (tiline[0] == baseline)
1097 whichbuf = 0;
1098 else if (tiline[1] == baseline)
1099 whichbuf = 1;
1100 else {
1101 tiline[whichbuf] = baseline;
1102 Lseek(tifd,(long)baseline / lines_per_buf * BUFFERSIZE,0);
1103 if (read(tifd,tibuf[whichbuf],BUFFERSIZE) < 0)
1104 fatal("Error reading tmp file %s.\n",TMPINNAME);
1105 }
1106 return tibuf[whichbuf] + (tireclen*offline);
1107 }
1108}
1109
1110/* patch abstract type */
1111
1112static long p_filesize; /* size of the patch file */
1113static LINENUM p_first; /* 1st line number */
1114static LINENUM p_newfirst; /* 1st line number of replacement */
1115static LINENUM p_ptrn_lines; /* # lines in pattern */
1116static LINENUM p_repl_lines; /* # lines in replacement text */
1117static LINENUM p_end = -1; /* last line in hunk */
1118static LINENUM p_max; /* max allowed value of p_end */
1119static LINENUM p_context = 3; /* # of context lines */
1120static LINENUM p_input_line = 0; /* current line # from patch file */
1121static char *p_line[MAXHUNKSIZE]; /* the text of the hunk */
1122static char p_char[MAXHUNKSIZE]; /* +, -, and ! */
1123static int p_len[MAXHUNKSIZE]; /* length of each line */
1124static int p_indent; /* indent to patch */
1125static long p_base; /* where to intuit this time */
1126static long p_start; /* where intuit found a patch */
1127
1128re_patch()
1129{
1130 p_first = (LINENUM)0;
1131 p_newfirst = (LINENUM)0;
1132 p_ptrn_lines = (LINENUM)0;
1133 p_repl_lines = (LINENUM)0;
1134 p_end = (LINENUM)-1;
1135 p_max = (LINENUM)0;
1136 p_indent = 0;
1137}
1138
1139open_patch_file(filename)
1140char *filename;
1141{
1142 if (filename == Nullch || !*filename || strEQ(filename,"-")) {
1143 pfp = fopen(TMPPATNAME,"w");
1144 if (pfp == Nullfp)
1145 fatal("patch: can't create %s.\n",TMPPATNAME);
1146 while (fgets(buf,sizeof buf,stdin) != NULL)
1147 fputs(buf,pfp);
1148 Fclose(pfp);
1149 filename = TMPPATNAME;
1150 }
1151 pfp = fopen(filename,"r");
1152 if (pfp == Nullfp)
1153 fatal("patch file %s not found\n",filename);
1154 Fstat(fileno(pfp), &filestat);
1155 p_filesize = filestat.st_size;
1156 next_intuit_at(0L); /* start at the beginning */
1157}
1158
1159bool
1160there_is_another_patch()
1161{
1162 bool no_input_file = (filearg[0] == Nullch);
1163
1164 if (p_base != 0L && p_base >= p_filesize) {
1165 if (verbose)
1166 say("done\n");
1167 return FALSE;
1168 }
1169 if (verbose)
1170 say("Hmm...");
1171 diff_type = intuit_diff_type();
1172 if (!diff_type) {
1173 if (p_base != 0L) {
1174 if (verbose)
1175 say(" Ignoring the trailing garbage.\ndone\n");
1176 }
1177 else
1178 say(" I can't seem to find a patch in there anywhere.\n");
1179 return FALSE;
1180 }
1181 if (verbose)
1182 say(" %sooks like %s to me...\n",
1183 (p_base == 0L ? "L" : "The next patch l"),
1184 diff_type == CONTEXT_DIFF ? "a context diff" :
56b0c556 1185 diff_type == NEW_CONTEXT_DIFF ? "a new-style context diff" :
9c13eddc
KM
1186 diff_type == NORMAL_DIFF ? "a normal diff" :
1187 "an ed script" );
1188 if (p_indent && verbose)
1189 say("(Patch is indented %d space%s.)\n",p_indent,p_indent==1?"":"s");
1190 skip_to(p_start);
1191 if (no_input_file) {
adcf44a2 1192 while (filearg[0] == Nullch) {
9c13eddc
KM
1193 ask("File to patch: ");
1194 filearg[0] = fetchname(buf);
1195 }
adcf44a2 1196 if (verbose) {
9c13eddc
KM
1197 say("Patching file %s...\n",filearg[0]);
1198 }
1199 }
1200 return TRUE;
1201}
1202
1203intuit_diff_type()
1204{
1205 long this_line = 0;
1206 long previous_line;
1207 long first_command_line = -1;
1208 bool last_line_was_command = FALSE;
1209 bool this_line_is_command = FALSE;
56b0c556
KM
1210 bool last_line_was_stars = FALSE;
1211 bool this_line_is_stars = FALSE;
9c13eddc
KM
1212 register int indent;
1213 register char *s, *t;
1214 char *oldname = Nullch;
1215 char *newname = Nullch;
1216 bool no_filearg = (filearg[0] == Nullch);
1217
1218 Fseek(pfp,p_base,0);
1219 for (;;) {
1220 previous_line = this_line;
1221 last_line_was_command = this_line_is_command;
56b0c556 1222 last_line_was_stars = this_line_is_stars;
9c13eddc
KM
1223 this_line = ftell(pfp);
1224 indent = 0;
1225 if (fgets(buf,sizeof buf,pfp) == Nullch) {
1226 if (first_command_line >= 0L) {
1227 /* nothing but deletes!? */
1228 p_start = first_command_line;
1229 return ED_DIFF;
1230 }
1231 else {
1232 p_start = this_line;
1233 return 0;
1234 }
1235 }
1236 for (s = buf; *s == ' ' || *s == '\t'; s++) {
1237 if (*s == '\t')
1238 indent += 8 - (indent % 8);
1239 else
1240 indent++;
1241 }
1242 for (t=s; isdigit(*t) || *t == ','; t++) ;
1243 this_line_is_command = (isdigit(*s) &&
1244 (*t == 'd' || *t == 'c' || *t == 'a') );
1245 if (first_command_line < 0L && this_line_is_command) {
1246 first_command_line = this_line;
1247 p_indent = indent; /* assume this for now */
1248 }
1249 if (strnEQ(s,"*** ",4))
1250 oldname = fetchname(s+4);
1251 else if (strnEQ(s,"--- ",4)) {
1252 newname = fetchname(s+4);
1253 if (no_filearg) {
1254 if (oldname && newname) {
1255 if (strlen(oldname) < strlen(newname))
1256 filearg[0] = oldname;
1257 else
1258 filearg[0] = newname;
1259 }
1260 else if (oldname)
1261 filearg[0] = oldname;
1262 else if (newname)
1263 filearg[0] = newname;
1264 }
1265 }
1266 else if (strnEQ(s,"Index:",6)) {
1267 if (no_filearg)
1268 filearg[0] = fetchname(s+6);
1269 /* this filearg might get limboed */
1270 }
1271 else if (strnEQ(s,"Prereq:",7)) {
1272 for (t=s+7; isspace(*t); t++) ;
1273 revision = savestr(t);
1274 for (t=revision; *t && !isspace(*t); t++) ;
1275 *t = '\0';
1276 if (!*revision) {
1277 free(revision);
1278 revision = Nullch;
1279 }
1280 }
1281 if ((!diff_type || diff_type == ED_DIFF) &&
1282 first_command_line >= 0L &&
1283 strEQ(s,".\n") ) {
1284 p_indent = indent;
1285 p_start = first_command_line;
1286 return ED_DIFF;
1287 }
56b0c556
KM
1288 this_line_is_stars = strnEQ(s,"********",8);
1289 if ((!diff_type || diff_type == CONTEXT_DIFF) && last_line_was_stars &&
1290 strnEQ(s,"*** ",4)) {
1291 /* if this is a new context diff the character just before */
1292 /* the newline is a '*'. */
1293 while (*s != '\n')
1294 s++;
9c13eddc 1295 p_indent = indent;
56b0c556
KM
1296 p_start = previous_line;
1297 return (*(s-1) == '*' ? NEW_CONTEXT_DIFF : CONTEXT_DIFF);
9c13eddc
KM
1298 }
1299 if ((!diff_type || diff_type == NORMAL_DIFF) &&
1300 last_line_was_command &&
1301 (strnEQ(s,"< ",2) || strnEQ(s,"> ",2)) ) {
1302 p_start = previous_line;
1303 p_indent = indent;
1304 return NORMAL_DIFF;
1305 }
1306 }
1307}
1308
1309char *
1310fetchname(at)
1311char *at;
1312{
1313 char *s = savestr(at);
1314 char *name;
1315 register char *t;
1316 char tmpbuf[200];
1317
1318 for (t=s; isspace(*t); t++) ;
1319 name = t;
1320 for (; *t && !isspace(*t); t++)
1321 if (!usepath)
1322 if (*t == '/')
1323 name = t+1;
1324 *t = '\0';
1325 name = savestr(name);
1326 Sprintf(tmpbuf,"RCS/%s",name);
1327 free(s);
1328 if (stat(name,&filestat) < 0) {
1329 Strcat(tmpbuf,RCSSUFFIX);
1330 if (stat(tmpbuf,&filestat) < 0 && stat(tmpbuf+4,&filestat) < 0) {
1331 Sprintf(tmpbuf,"SCCS/%s%s",SCCSPREFIX,name);
1332 if (stat(tmpbuf,&filestat) < 0 && stat(tmpbuf+5,&filestat) < 0) {
1333 free(name);
1334 name = Nullch;
1335 }
1336 }
1337 }
1338 return name;
1339}
1340
1341next_intuit_at(file_pos)
1342long file_pos;
1343{
1344 p_base = file_pos;
1345}
1346
1347skip_to(file_pos)
1348long file_pos;
1349{
1350 char *ret;
1351
1352 assert(p_base <= file_pos);
1353 if (verbose && p_base < file_pos) {
1354 Fseek(pfp,p_base,0);
1355 say("The text leading up to this was:\n--------------------------\n");
1356 while (ftell(pfp) < file_pos) {
1357 ret = fgets(buf,sizeof buf,pfp);
1358 assert(ret != Nullch);
1359 say("|%s",buf);
1360 }
1361 say("--------------------------\n");
1362 }
1363 else
1364 Fseek(pfp,file_pos,0);
1365}
1366
1367bool
1368another_hunk()
1369{
1370 register char *s;
1371 char *ret;
56b0c556 1372 register int context = 0;
9c13eddc
KM
1373
1374 while (p_end >= 0) {
1375 free(p_line[p_end--]);
1376 }
1377 assert(p_end == -1);
1378
1379 p_max = MAXHUNKSIZE; /* gets reduced when --- found */
1380 if (diff_type == CONTEXT_DIFF) {
1381 long line_beginning = ftell(pfp);
1382 LINENUM repl_beginning = 0;
1383
1384 ret = pgets(buf,sizeof buf, pfp);
1385 if (ret == Nullch || strnNE(buf,"********",8)) {
1386 next_intuit_at(line_beginning);
1387 return FALSE;
1388 }
1389 p_context = 100;
1390 while (p_end < p_max) {
1391 ret = pgets(buf,sizeof buf, pfp);
1392 if (ret == Nullch) {
1393 if (p_max - p_end < 4)
1394 Strcpy(buf," \n"); /* assume blank lines got chopped */
1395 else
1396 fatal("Unexpected end of file in patch.\n");
1397 }
1398 p_input_line++;
1399 if (strnEQ(buf,"********",8))
1400 fatal("Unexpected end of hunk at line %d.\n",
1401 p_input_line);
1402 p_char[++p_end] = *buf;
1403 switch (*buf) {
1404 case '*':
1405 if (p_end != 0)
1406 fatal("Unexpected *** at line %d: %s", p_input_line, buf);
1407 context = 0;
1408 p_line[p_end] = savestr(buf);
1409 for (s=buf; *s && !isdigit(*s); s++) ;
756fee39
VJ
1410 if (!isdigit(*s))
1411 fatal("Malformed patch at line %d: %s", p_input_line, buf);
9c13eddc
KM
1412 p_first = (LINENUM) atol(s);
1413 while (isdigit(*s)) s++;
1414 for (; *s && !isdigit(*s); s++) ;
756fee39
VJ
1415 if (!isdigit(*s))
1416 p_ptrn_lines = 1;
1417 else
1418 p_ptrn_lines = ((LINENUM)atol(s)) - p_first + 1;
9c13eddc
KM
1419 break;
1420 case '-':
1421 if (buf[1] == '-') {
1422 if (p_end != p_ptrn_lines + 1 &&
1423 p_end != p_ptrn_lines + 2)
1424 fatal("Unexpected --- at line %d: %s",
1425 p_input_line,buf);
1426 repl_beginning = p_end;
1427 context = 0;
1428 p_line[p_end] = savestr(buf);
1429 p_char[p_end] = '=';
1430 for (s=buf; *s && !isdigit(*s); s++) ;
756fee39
VJ
1431 if (!isdigit(*s))
1432 fatal("Malformed patch at line %d: %s",
1433 p_input_line, buf);
9c13eddc
KM
1434 p_newfirst = (LINENUM) atol(s);
1435 while (isdigit(*s)) s++;
1436 for (; *s && !isdigit(*s); s++) ;
756fee39
VJ
1437 if (!isdigit(*s))
1438 p_max = p_newfirst;
1439 else
1440 p_max = ((LINENUM)atol(s));
1441 p_max += 1 + p_end - p_newfirst;
1442 if (p_max >= MAXHUNKSIZE)
1443 fatal("Hunk too large (%d lines) at line %d: %s",
1444 p_max - p_end, p_input_line, buf);
9c13eddc
KM
1445 break;
1446 }
1447 /* FALL THROUGH */
1448 case '+': case '!':
1449 if (context > 0) {
1450 if (context < p_context)
1451 p_context = context;
1452 context = -100;
1453 }
1454 p_line[p_end] = savestr(buf+2);
1455 break;
1456 case '\t': case '\n': /* assume the 2 spaces got eaten */
1457 p_line[p_end] = savestr(buf);
1458 if (p_end != p_ptrn_lines + 1) {
1459 context++;
1460 p_char[p_end] = ' ';
1461 }
1462 break;
1463 case ' ':
1464 context++;
1465 p_line[p_end] = savestr(buf+2);
1466 break;
1467 default:
1468 fatal("Malformed patch at line %d: %s",p_input_line,buf);
1469 }
756fee39
VJ
1470 /* set up p_len for strncmp() so we don't have to */
1471 /* assume null termination */
1472 if (p_line[p_end])
6174924f 1473 p_len[p_end] = strlen(p_line[p_end]);
756fee39
VJ
1474 else
1475 p_len[p_end] = 0;
9c13eddc
KM
1476 }
1477 if (p_end >=0 && !p_ptrn_lines)
1478 fatal("No --- found in patch at line %d\n", pch_hunk_beg());
1479 p_repl_lines = p_end - repl_beginning;
be052237 1480 p_char[p_end+1] = '^'; /* add a stopper for apply_hunk */
9c13eddc 1481 }
56b0c556
KM
1482 else if (diff_type == NEW_CONTEXT_DIFF) {
1483 long line_beginning = ftell(pfp);
1484 LINENUM repl_beginning = 0;
1485 LINENUM fillcnt = 0;
1486 LINENUM fillsrc;
1487 LINENUM filldst;
1488
1489 ret = pgets(buf,sizeof buf, pfp);
1490 if (ret == Nullch || strnNE(buf,"********",8)) {
1491 next_intuit_at(line_beginning);
1492 return FALSE;
1493 }
1494 p_context = 0;
1495 while (p_end < p_max) {
1496 ret = pgets(buf,sizeof buf, pfp);
1497 if (ret == Nullch) {
1498 if (p_max - p_end < 4)
1499 Strcpy(buf," \n"); /* assume blank lines got chopped */
756fee39 1500 else
56b0c556
KM
1501 fatal("Unexpected end of file in patch.\n");
1502 }
1503 p_input_line++;
1504 p_char[++p_end] = *buf;
1505 switch (*buf) {
adcf44a2 1506 case '*': /* another hunk */
756fee39
VJ
1507 if (strnEQ(buf,"********",8))
1508 fatal("Unexpected end of hunk at line %d.\n",
1509 p_input_line);
1510
56b0c556
KM
1511 if (p_end != 0)
1512 fatal("Unexpected *** at line %d: %s", p_input_line, buf);
1513 context = 0;
1514 p_line[p_end] = savestr(buf);
1515 for (s=buf; *s && !isdigit(*s); s++) ;
756fee39
VJ
1516 if (!isdigit(*s))
1517 fatal("Malformed patch at line %d: %s", p_input_line, buf);
56b0c556
KM
1518 p_first = (LINENUM) atol(s);
1519 while (isdigit(*s)) s++;
1520 for (; *s && !isdigit(*s); s++) ;
756fee39
VJ
1521 if (!isdigit(*s))
1522 p_ptrn_lines = 1;
1523 else
1524 p_ptrn_lines = ((LINENUM)atol(s)) - p_first + 1;
56b0c556
KM
1525 break;
1526 case '-':
1527 if (buf[1] == '-') {
1528 if (p_end != p_ptrn_lines + 1) {
1529 if (p_end == 1) {
1530 /* `old' lines were omitted - set up to fill them */
1531 /* in from 'new' context lines. */
1532 p_end = p_ptrn_lines + 1;
1533 fillsrc = p_end + 1;
1534 filldst = 1;
1535 fillcnt = p_ptrn_lines;
1536 } else
1537 fatal("Unexpected --- at line %d: %s",
1538 p_input_line,buf);
1539 }
1540 repl_beginning = p_end;
1541 p_line[p_end] = savestr(buf);
1542 p_char[p_end] = '=';
1543 for (s=buf; *s && !isdigit(*s); s++) ;
756fee39
VJ
1544 if (!isdigit(*s))
1545 fatal("Malformed patch at line %d: %s",
1546 p_input_line, buf);
56b0c556
KM
1547 p_newfirst = (LINENUM) atol(s);
1548 while (isdigit(*s)) s++;
1549 for (; *s && !isdigit(*s); s++) ;
756fee39
VJ
1550 if (!isdigit(*s))
1551 p_max = p_newfirst;
1552 else
1553 p_max = ((LINENUM)atol(s));
1554 p_max += 1 + p_end - p_newfirst;
1555 if (p_max >= MAXHUNKSIZE)
1556 fatal("Hunk too large (%d lines) at line %d: %s",
1557 p_max - p_end, p_input_line, buf);
1558 if (p_max - p_end == context) {
1559 /* redundant 'new' context lines were omitted */
1560 /* set up to fill them in from the the old file's */
1561 /* context */
1562 fillsrc = 1;
1563 filldst = p_end + 1;
1564 fillcnt = p_max - repl_beginning;
1565 p_end = p_max;
1566 }
56b0c556
KM
1567 break;
1568 }
1569 /* FALL THROUGH */
1570 case '+': case '!':
1571 if (context > 0 && p_context == 0) {
1572 p_context = context;
1573 }
1574 p_line[p_end] = savestr(buf+2);
1575 break;
1576 case '\t': case '\n': /* assume the 2 spaces got eaten */
1577 p_line[p_end] = savestr(buf);
be052237
VJ
1578 context++;
1579 p_char[p_end] = ' ';
56b0c556
KM
1580 break;
1581 case ' ':
1582 context++;
1583 p_line[p_end] = savestr(buf+2);
1584 break;
1585 default:
1586 fatal("Malformed patch at line %d: %s",p_input_line,buf);
1587 }
756fee39
VJ
1588 /* set up p_len for strncmp() so we don't have to */
1589 /* assume null termination */
1590 if (p_line[p_end])
6174924f 1591 p_len[p_end] = strlen(p_line[p_end]);
756fee39
VJ
1592 else
1593 p_len[p_end] = 0;
56b0c556
KM
1594 }
1595 if (p_end >=0 && !p_ptrn_lines)
1596 fatal("No --- found in patch at line %d\n", pch_hunk_beg());
1597
1598 /* if there were omitted context lines, fill them in */
1599 if (fillcnt) {
1600 while (fillcnt-- > 0) {
1601 while (p_char[fillsrc] != ' ')
1602 fillsrc++;
756fee39
VJ
1603 if (p_line[fillsrc])
1604 p_line[filldst] = savestr (p_line[fillsrc]);
1605 else
1606 p_line[filldst] = p_line[fillsrc];
56b0c556
KM
1607 p_char[filldst] = p_char[fillsrc];
1608 p_len[filldst] = p_len[fillsrc];
1609 fillsrc++; filldst++;
1610 }
756fee39 1611 assert(filldst==p_end+1 || filldst==repl_beginning);
56b0c556
KM
1612 }
1613 p_repl_lines = p_end - repl_beginning;
be052237 1614 p_char[p_end+1] = '^'; /* add a stopper for apply_hunk */
56b0c556 1615 }
9c13eddc
KM
1616 else { /* normal diff--fake it up */
1617 char hunk_type;
1618 register int i;
1619 LINENUM min, max;
1620 long line_beginning = ftell(pfp);
1621
1622 p_context = 0;
1623 ret = pgets(buf,sizeof buf, pfp);
1624 p_input_line++;
1625 if (ret == Nullch || !isdigit(*buf)) {
1626 next_intuit_at(line_beginning);
1627 return FALSE;
1628 }
1629 p_first = (LINENUM)atol(buf);
1630 for (s=buf; isdigit(*s); s++) ;
1631 if (*s == ',') {
1632 p_ptrn_lines = (LINENUM)atol(++s) - p_first + 1;
1633 while (isdigit(*s)) s++;
1634 }
1635 else
1636 p_ptrn_lines = (*s != 'a');
1637 hunk_type = *s;
1638 if (hunk_type == 'a')
1639 p_first++; /* do append rather than insert */
1640 min = (LINENUM)atol(++s);
1641 for (; isdigit(*s); s++) ;
1642 if (*s == ',')
1643 max = (LINENUM)atol(++s);
1644 else
1645 max = min;
1646 if (hunk_type == 'd')
1647 min++;
1648 p_end = p_ptrn_lines + 1 + max - min + 1;
1649 p_newfirst = min;
1650 p_repl_lines = max - min + 1;
1651 Sprintf(buf,"*** %d,%d\n", p_first, p_first + p_ptrn_lines - 1);
1652 p_line[0] = savestr(buf);
1653 p_char[0] = '*';
1654 for (i=1; i<=p_ptrn_lines; i++) {
1655 ret = pgets(buf,sizeof buf, pfp);
1656 p_input_line++;
1657 if (ret == Nullch)
1658 fatal("Unexpected end of file in patch at line %d.\n",
1659 p_input_line);
1660 if (*buf != '<')
1661 fatal("< expected at line %d of patch.\n", p_input_line);
1662 p_line[i] = savestr(buf+2);
756fee39 1663 if (p_line[i])
6174924f 1664 p_len[i] = strlen(p_line[i]);
756fee39
VJ
1665 else
1666 p_len[i] = 0;
9c13eddc
KM
1667 p_char[i] = '-';
1668 }
1669 if (hunk_type == 'c') {
1670 ret = pgets(buf,sizeof buf, pfp);
1671 p_input_line++;
1672 if (ret == Nullch)
1673 fatal("Unexpected end of file in patch at line %d.\n",
1674 p_input_line);
1675 if (*buf != '-')
1676 fatal("--- expected at line %d of patch.\n", p_input_line);
1677 }
1678 Sprintf(buf,"--- %d,%d\n",min,max);
1679 p_line[i] = savestr(buf);
1680 p_char[i] = '=';
1681 for (i++; i<=p_end; i++) {
1682 ret = pgets(buf,sizeof buf, pfp);
1683 p_input_line++;
1684 if (ret == Nullch)
1685 fatal("Unexpected end of file in patch at line %d.\n",
1686 p_input_line);
1687 if (*buf != '>')
1688 fatal("> expected at line %d of patch.\n", p_input_line);
1689 p_line[i] = savestr(buf+2);
756fee39
VJ
1690 /* set up p_len for strncmp() so we don't have to */
1691 /* assume null termination */
1692 if (p_line[i])
6174924f 1693 p_len[i] = strlen(p_line[i]);
756fee39
VJ
1694 else
1695 p_len[i] = 0;
9c13eddc
KM
1696 p_char[i] = '+';
1697 }
1698 }
1699 if (reverse) /* backwards patch? */
1700 pch_swap();
1701#ifdef DEBUGGING
1702 if (debug & 2) {
1703 int i;
1704 char special;
1705
1706 for (i=0; i <= p_end; i++) {
1707 if (i == p_ptrn_lines)
1708 special = '^';
1709 else
1710 special = ' ';
1711 printf("%3d %c %c %s",i,p_char[i],special,p_line[i]);
1712 }
1713 }
1714#endif
1715 return TRUE;
1716}
1717
1718char *
1719pgets(bf,sz,fp)
1720char *bf;
1721int sz;
1722FILE *fp;
1723{
1724 char *ret = fgets(bf,sz,fp);
1725 register char *s;
1726 register int indent = 0;
1727
1728 if (p_indent && ret != Nullch) {
1729 for (s=buf; indent < p_indent && (*s == ' ' || *s == '\t'); s++) {
1730 if (*s == '\t')
1731 indent += 8 - (indent % 7);
1732 else
1733 indent++;
1734 }
1735 if (buf != s)
1736 Strcpy(buf,s);
1737 }
1738 return ret;
1739}
1740
1741pch_swap()
1742{
1743 char *tp_line[MAXHUNKSIZE]; /* the text of the hunk */
1744 char tp_char[MAXHUNKSIZE]; /* +, -, and ! */
1745 int tp_len[MAXHUNKSIZE]; /* length of each line */
1746 register LINENUM i, n;
1747 bool blankline = FALSE;
1748 register char *s;
1749
1750 i = p_first;
1751 p_first = p_newfirst;
1752 p_newfirst = i;
1753
1754 /* make a scratch copy */
1755
1756 for (i=0; i<=p_end; i++) {
1757 tp_line[i] = p_line[i];
1758 tp_char[i] = p_char[i];
1759 tp_len[i] = p_len[i];
1760 }
1761
1762 /* now turn the new into the old */
1763
1764 i = p_ptrn_lines + 1;
1765 if (tp_char[i] == '\n') { /* account for possible blank line */
1766 blankline = TRUE;
1767 i++;
1768 }
1769 for (n=0; i <= p_end; i++,n++) {
1770 p_line[n] = tp_line[i];
1771 p_char[n] = tp_char[i];
1772 if (p_char[n] == '+')
1773 p_char[n] = '-';
1774 p_len[n] = tp_len[i];
1775 }
1776 if (blankline) {
1777 i = p_ptrn_lines + 1;
1778 p_line[n] = tp_line[i];
1779 p_char[n] = tp_char[i];
1780 p_len[n] = tp_len[i];
1781 n++;
1782 }
1783 assert(p_char[0] == '=');
1784 p_char[0] = '*';
1785 for (s=p_line[0]; *s; s++)
1786 if (*s == '-')
1787 *s = '*';
1788
1789 /* now turn the old into the new */
1790
1791 assert(tp_char[0] == '*');
1792 tp_char[0] = '=';
1793 for (s=tp_line[0]; *s; s++)
1794 if (*s == '*')
1795 *s = '-';
1796 for (i=0; n <= p_end; i++,n++) {
1797 p_line[n] = tp_line[i];
1798 p_char[n] = tp_char[i];
1799 if (p_char[n] == '-')
1800 p_char[n] = '+';
1801 p_len[n] = tp_len[i];
1802 }
1803 assert(i == p_ptrn_lines + 1);
1804 i = p_ptrn_lines;
1805 p_ptrn_lines = p_repl_lines;
1806 p_repl_lines = i;
1807}
1808
1809LINENUM
1810pch_first()
1811{
1812 return p_first;
1813}
1814
1815LINENUM
1816pch_ptrn_lines()
1817{
1818 return p_ptrn_lines;
1819}
1820
1821LINENUM
1822pch_newfirst()
1823{
1824 return p_newfirst;
1825}
1826
1827LINENUM
1828pch_repl_lines()
1829{
1830 return p_repl_lines;
1831}
1832
1833LINENUM
1834pch_end()
1835{
1836 return p_end;
1837}
1838
1839LINENUM
1840pch_context()
1841{
1842 return p_context;
1843}
1844
1845pch_line_len(line)
1846LINENUM line;
1847{
1848 return p_len[line];
1849}
1850
1851char
1852pch_char(line)
1853LINENUM line;
1854{
1855 return p_char[line];
1856}
1857
1858char *
1859pfetch(line)
1860LINENUM line;
1861{
1862 return p_line[line];
1863}
1864
1865LINENUM
1866pch_hunk_beg()
1867{
1868 return p_input_line - p_end - 1;
1869}
1870
1871char *
1872savestr(s)
1873register char *s;
1874{
1875 register char *rv,
1876 *t;
1877
1878 t = s;
1879 while (*t++);
1880 rv = malloc((MEM) (t - s));
1881 if (rv == NULL)
1882 fatal ("patch: out of memory (savestr)\n");
1883 t = rv;
1884 while (*t++ = *s++);
1885 return rv;
1886}
1887
1888my_exit(status)
1889int status;
1890{
1891 Unlink(TMPINNAME);
1892 Unlink(TMPOUTNAME);
1893 Unlink(TMPREJNAME);
1894 Unlink(TMPPATNAME);
1895 exit(status);
1896}
1897
1898#ifdef lint
1899
1900/*VARARGS ARGSUSED*/
1901say(pat) char *pat; { ; }
1902/*VARARGS ARGSUSED*/
1903fatal(pat) char *pat; { ; }
1904/*VARARGS ARGSUSED*/
1905ask(pat) char *pat; { ; }
1906
1907#else lint
1908
1909say(pat,arg1,arg2,arg3)
1910char *pat;
1911int arg1,arg2,arg3;
1912{
1913 fprintf(stderr,pat,arg1,arg2,arg3);
1914 Fflush(stderr);
1915}
1916
1917fatal(pat,arg1,arg2,arg3)
1918char *pat;
1919int arg1,arg2,arg3;
1920{
1921 say(pat,arg1,arg2,arg3);
1922 my_exit(1);
1923}
1924
1925ask(pat,arg1,arg2,arg3)
1926char *pat;
1927int arg1,arg2,arg3;
1928{
1929 int ttyfd = open("/dev/tty",2);
1930 int r;
1931
1932 say(pat,arg1,arg2,arg3);
1933 if (ttyfd >= 0) {
1934 r = read(ttyfd, buf, sizeof buf);
1935 Close(ttyfd);
1936 }
1937 else
1938 r = read(2, buf, sizeof buf);
1939 if (r <= 0)
1940 buf[0] = 0;
1941}
1942#endif lint
1943
1944bool
1945rev_in_string(string)
1946char *string;
1947{
1948 register char *s;
1949 register int patlen;
1950
1951 if (revision == Nullch)
1952 return TRUE;
1953 patlen = strlen(revision);
1954 for (s = string; *s; s++) {
1955 if (isspace(*s) && strnEQ(s+1,revision,patlen) &&
1956 isspace(s[patlen+1] )) {
1957 return TRUE;
1958 }
1959 }
1960 return FALSE;
1961}
1962
1963set_signals()
1964{
1965 /*NOSTRICT*/
1966 if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
1967 Signal(SIGHUP, my_exit);
1968 /*NOSTRICT*/
1969 if (signal(SIGINT, SIG_IGN) != SIG_IGN)
1970 Signal(SIGINT, my_exit);
1971}
1972
1973ignore_signals()
1974{
1975 /*NOSTRICT*/
1976 Signal(SIGHUP, SIG_IGN);
1977 /*NOSTRICT*/
1978 Signal(SIGINT, SIG_IGN);
1979}