1/* See LICENSE file for copyright and license details. */
2#include "paths.h"
3#include "util.h"
4#include "wexec.h"
5
6#include <sys/file.h>
7#include <sys/mman.h>
8#include <sys/param.h>
9#include <sys/stat.h>
10#include <sys/wait.h>
11
12#include <ctype.h>
13#include <dirent.h>
14#include <errno.h>
15#include <fcntl.h>
16#include <getopt.h>
17#include <libgen.h>
18#include <limits.h>
19#include <stdarg.h>
20#include <stddef.h>
21#include <stdint.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <unistd.h>
26
27#define DEBUGGING
28
29#define MAXHUNKSIZE 100000
30#define INITHUNKMAX 125
31#define INITLINELEN 8192
32#define BUFFERSIZE 1024
33#define LINENUM_MAX LONG_MAX
34
35#define SCCSPREFIX "s."
36#define GET "get -e %s"
37#define SCCSDIFF "get -p %s | diff - %s >/dev/null"
38
39#define RCSSUFFIX ",v"
40#define CHECKOUT "co"
41#define RCSDIFF "rcsdiff"
42
43#define ORIGEXT ".orig"
44#define REJEXT ".rej"
45
46#define strNE(s1, s2) (strcmp(s1, s2))
47#define strEQ(s1, s2) (!strcmp(s1, s2))
48#define strnNE(s1, s2, l) (strncmp(s1, s2, l))
49#define strnEQ(s1, s2, l) (!strncmp(s1, s2, l))
50
51#define OLD_FILE 0
52#define NEW_FILE 1
53#define INDEX_FILE 2
54#define MAX_FILE 3
55
56#define CONTEXT_DIFF 1
57#define NORMAL_DIFF 2
58#define ED_DIFF 3
59#define NEW_CONTEXT_DIFF 4
60#define UNI_DIFF 5
61
62#define _PATH_ED "ed"
63
64#define ISDIGIT(c) (isascii((unsigned char)c) && isdigit((unsigned char)c))
65
66#ifndef __UNCONST
67#define __UNCONST(a) ((void *)(unsigned long)(const void *)(a))
68#endif
69
70#ifndef __dead
71#define __dead __attribute__((__noreturn__))
72#endif
73
74typedef long LINENUM;
75
76enum BackupType {
77 BACKUP_UNDEFINED,
78 BACKUP_NONE,
79 BACKUP_SIMPLE,
80 BACKUP_NUMBERED_EXISTING,
81 BACKUP_NUMBERED
82};
83
84struct FileName {
85 char *path;
86 int exists;
87};
88
89mode_t filemode = 0644;
90char *buf;
91size_t bufsz;
92int using_plan_a = 1;
93int out_of_mem = 0;
94
95#define MAXFILEC 2
96char *filearg[MAXFILEC];
97int ok_to_create_file = 0;
98char *outname = NULL;
99char *origprae = NULL;
100char *TMPOUTNAME;
101char *TMPINNAME;
102char *TMPREJNAME;
103char *TMPPATNAME;
104int toutkeep = 0;
105int trejkeep = 0;
106int warn_on_invalid_line;
107int last_line_missing_eol;
108
109#ifdef DEBUGGING
110int debug = 0;
111#endif
112
113int force = 0;
114int batch = 0;
115int verbose = 1;
116int reverse = 0;
117int noreverse = 0;
118int skip_rest_of_patch = 0;
119int strippath = 957;
120int canonicalize = 0;
121int check_only = 0;
122int diff_type = 0;
123char *revision = NULL;
124LINENUM input_lines = 0;
125int posix = 0;
126int backup_if_mismatch = -1;
127
128enum BackupType backup_type = BACKUP_UNDEFINED;
129const char *simple_backup_suffix = "~";
130
131static int remove_empty_files = 0;
132static int reverse_flag_specified = 0;
133static char rejname[PATH_MAX];
134static char serrbuf[BUFSIZ];
135static LINENUM last_frozen_line = 0;
136static int Argc;
137static char **Argv;
138static int Argc_last;
139static char **Argv_last;
140static FILE *ofp = NULL;
141static FILE *rejfp = NULL;
142static int filec = 0;
143static LINENUM last_offset = 0;
144static LINENUM maxfuzz = 2;
145
146static int do_defines = 0;
147static char if_defined[128];
148static char not_defined[128];
149static const char else_defined[] = "#else\n";
150static char end_defined[128];
151
152/* function declarations */
153static void reinitialize_almost_everything(void);
154static void get_some_switches(void);
155static void reset_getopt_state(void);
156static LINENUM locate_hunk(LINENUM);
157static void rej_line(int, LINENUM);
158static void abort_hunk(void);
159static void apply_hunk(LINENUM);
160static void init_output(const char *);
161static void init_reject(const char *);
162static void copy_till(LINENUM, int);
163static int spew_output(void);
164static void dump_input_line(LINENUM, int);
165static int patch_match(LINENUM, LINENUM, LINENUM);
166static int similar(const char *, const char *, ssize_t);
167static void usage(void);
168
169void re_patch(void);
170void open_patch_file(const char *);
171void set_hunkmax(void);
172int there_is_another_patch(void);
173int another_hunk(void);
174int pch_swap(void);
175char *pfetch(LINENUM);
176ssize_t pch_line_len(LINENUM);
177LINENUM pch_first(void);
178LINENUM pch_ptrn_lines(void);
179LINENUM pch_newfirst(void);
180LINENUM pch_repl_lines(void);
181LINENUM pch_end(void);
182LINENUM pch_context(void);
183LINENUM pch_hunk_beg(void);
184char pch_char(LINENUM);
185void do_ed_script(void);
186
187void re_input(void);
188void scan_input(const char *);
189char *ifetch(LINENUM, int);
190
191char *fetchname(const char *, int *, int);
192char *checked_in(char *);
193LINENUM strtolinenum(char *, char **);
194int backup_file(const char *);
195int move_file(const char *, const char *);
196int copy_file(const char *, const char *);
197void say(const char *, ...);
198__dead void fatal(const char *, ...);
199__dead void pfatal(const char *, ...);
200void ask(const char *, ...);
201char *savestr(const char *);
202void set_signals(int);
203void ignore_signals(void);
204void makedirs(const char *, int);
205__dead void version(void);
206__dead void my_exit(int);
207static void *pch_realloc(void *, size_t, size_t);
208int mkpath(char *);
209char *find_backup_file_name(const char *);
210enum BackupType get_version(const char *);
211
212static off_t i_size;
213static char *i_womp;
214static char **i_ptr;
215static char empty_line[] = {'\0'};
216
217static int tifd = -1;
218static char *tibuf[2];
219static LINENUM tiline[2] = {-1, -1};
220static LINENUM lines_per_buf;
221static int tireclen;
222
223static int rev_in_string(const char *);
224static int reallocate_lines(size_t *);
225static int plan_a(const char *);
226static void plan_b(const char *);
227
228static long p_filesize;
229static LINENUM p_first;
230static LINENUM p_newfirst;
231static LINENUM p_ptrn_lines;
232static LINENUM p_repl_lines;
233static LINENUM p_end = -1;
234static LINENUM p_max;
235static LINENUM p_context = 3;
236static LINENUM p_input_line = 0;
237static char **p_line = NULL;
238static ssize_t *p_len = NULL;
239static char *p_char = NULL;
240static int hunkmax = INITHUNKMAX;
241static int p_indent;
242static LINENUM p_base;
243static LINENUM p_bline;
244static LINENUM p_start;
245static LINENUM p_sline;
246static LINENUM p_hunk_beg;
247static LINENUM p_efake = -1;
248static LINENUM p_bfake = -1;
249static FILE *pfp = NULL;
250static char *bestguess = NULL;
251
252static void grow_hunkmax(void);
253static int intuit_diff_type(void);
254static void next_intuit_at(LINENUM, LINENUM);
255static void skip_to(LINENUM, LINENUM);
256static int pgetline(char **, size_t *, FILE *);
257static char *best_name(const struct FileName *, int);
258static char *posix_name(const struct FileName *, int);
259static size_t num_components(const char *);
260
261static char *patch_concat(const char *, const char *);
262static char *make_version_name(const char *, int);
263static int max_backup_version(const char *, const char *);
264static int version_number(const char *, const char *, size_t);
265static int argmatch(const char *, const char **);
266static void invalid_arg(const char *, const char *, int);
267
268static const char *backup_args[] = {
269 "none", "never", "simple", "nil", "existing", "t", "numbered", 0
270};
271static enum BackupType backup_types[] = {
272 BACKUP_NONE,
273 BACKUP_SIMPLE,
274 BACKUP_SIMPLE,
275 BACKUP_NUMBERED_EXISTING,
276 BACKUP_NUMBERED_EXISTING,
277 BACKUP_NUMBERED,
278 BACKUP_NUMBERED
279};
280
281/* implementation */
282
283// ?man patch: apply a diff file to an original
284int
285main(int argc, char *argv[])
286{
287 int error, hunk, failed, i, fd;
288 LINENUM where, newwhere, fuzz, mymaxfuzz;
289 const char *tmpdir;
290 char *v;
291
292 error = 0;
293 where = 0;
294 bufsz = INITLINELEN;
295 if ((buf = malloc(bufsz)) == NULL)
296 pfatal("allocating input buffer");
297 buf[0] = '\0';
298
299 setbuf(stderr, serrbuf);
300 for (i = 0; i < MAXFILEC; i++)
301 filearg[i] = NULL;
302
303 if ((tmpdir = getenv("TMPDIR")) == NULL || *tmpdir == '\0')
304 tmpdir = ARUU_PATH_TMP;
305 for (i = strlen(tmpdir) - 1; i > 0 && tmpdir[i] == '/'; i--)
306 ;
307 i++;
308 if (asprintf(&TMPOUTNAME, "%.*s/patchoXXXXXXXXXX", i, tmpdir) == -1)
309 fatal("cannot allocate memory");
310 if ((fd = mkstemp(TMPOUTNAME)) < 0)
311 pfatal("cant create %s", TMPOUTNAME);
312 close(fd);
313
314 if (asprintf(&TMPINNAME, "%.*s/patchiXXXXXXXXXX", i, tmpdir) == -1)
315 fatal("cannot allocate memory");
316 if ((fd = mkstemp(TMPINNAME)) < 0)
317 pfatal("cant create %s", TMPINNAME);
318 close(fd);
319
320 if (asprintf(&TMPREJNAME, "%.*s/patchrXXXXXXXXXX", i, tmpdir) == -1)
321 fatal("cannot allocate memory");
322 if ((fd = mkstemp(TMPREJNAME)) < 0)
323 pfatal("cant create %s", TMPREJNAME);
324 close(fd);
325
326 if (asprintf(&TMPPATNAME, "%.*s/patchpXXXXXXXXXX", i, tmpdir) == -1)
327 fatal("cannot allocate memory");
328 if ((fd = mkstemp(TMPPATNAME)) < 0)
329 pfatal("cant create %s", TMPPATNAME);
330 close(fd);
331
332 v = getenv("SIMPLE_BACKUP_SUFFIX");
333 if (v)
334 simple_backup_suffix = v;
335 else
336 simple_backup_suffix = ORIGEXT;
337
338 if ((v = getenv("PATCH_VERSION_CONTROL")) == NULL)
339 v = getenv("VERSION_CONTROL");
340 if (v != NULL)
341 backup_type = get_version(v);
342
343 Argc = argc;
344 Argv = argv;
345 get_some_switches();
346
347 if (backup_type == BACKUP_UNDEFINED)
348 backup_type = posix ? BACKUP_NONE : BACKUP_NUMBERED_EXISTING;
349
350 set_signals(0);
351
352 for (open_patch_file(filearg[1]); there_is_another_patch(); reinitialize_almost_everything()) {
353 warn_on_invalid_line = 1;
354 if (outname == NULL)
355 outname = savestr(filearg[0]);
356
357 if (diff_type == ED_DIFF) {
358 do_ed_script();
359 continue;
360 }
361 if (!skip_rest_of_patch)
362 init_output(TMPOUTNAME);
363
364 init_reject(TMPREJNAME);
365
366 if (!skip_rest_of_patch)
367 scan_input(filearg[0]);
368
369 hunk = 0;
370 failed = 0;
371 out_of_mem = 0;
372 while (another_hunk()) {
373 hunk++;
374 fuzz = 0;
375 mymaxfuzz = pch_context();
376 if (maxfuzz < mymaxfuzz)
377 mymaxfuzz = maxfuzz;
378 if (!skip_rest_of_patch) {
379 do {
380 where = locate_hunk(fuzz);
381 if (hunk == 1 && where == 0 && !force) {
382 if (!pch_swap()) {
383 if (fuzz == 0)
384 say("Not "
385 "enough "
386 "memory to "
387 "try "
388 "swapped "
389 "hunk! "
390 "Assuming "
391 "unswapped."
392 "\n");
393 continue;
394 }
395 reverse = !reverse;
396 where = locate_hunk(fuzz);
397 if (where == 0) {
398 if (!pch_swap())
399 fatal(
400 "lost "
401 "hunk on "
402 "alloc "
403 "error!"
404 "\n"
405 );
406 reverse = !reverse;
407 } else if (noreverse) {
408 if (!pch_swap())
409 fatal(
410 "lost "
411 "hunk on "
412 "alloc "
413 "error!"
414 "\n"
415 );
416 reverse = !reverse;
417 say("Ignoring "
418 "previously "
419 "applied (or "
420 "reversed) "
421 "patch.\n");
422 skip_rest_of_patch = 1;
423 } else if (batch) {
424 if (verbose)
425 say("%seversed "
426 "(or "
427 "%sprevious"
428 "ly "
429 "applied) "
430 "patch "
431 "detected! "
432 "%s -R.",
433 reverse ? "R" : "Unr",
434 reverse ? "" : "not ",
435 reverse ? "Assuming" : "Ignoring");
436 } else {
437 ask("%seversed (or "
438 "%spreviously "
439 "applied) patch "
440 "detected! %s -R? "
441 "[y] ",
442 reverse ? "R" : "Unr",
443 reverse ? "" : "not ",
444 reverse ? "Assume" : "Ignore");
445 if (*buf == 'n') {
446 ask("Apply "
447 "anyway? "
448 "[n] ");
449 if (*buf != 'y')
450 skip_rest_of_patch = 1;
451 where = 0;
452 reverse = !reverse;
453 if (!pch_swap())
454 fatal("lost hunk on alloc error!\n");
455 }
456 }
457 }
458 } while (!skip_rest_of_patch && where == 0 && ++fuzz <= mymaxfuzz);
459
460 if (skip_rest_of_patch) {
461 if (ferror(ofp) || fclose(ofp)) {
462 say("Error writing %s\n", TMPOUTNAME);
463 error = 1;
464 }
465 ofp = NULL;
466 }
467 }
468 newwhere = pch_newfirst() + last_offset;
469 if (skip_rest_of_patch) {
470 abort_hunk();
471 failed++;
472 if (verbose)
473 say("Hunk #%d ignored at %ld.\n", hunk, newwhere);
474 } else if (where == 0) {
475 abort_hunk();
476 failed++;
477 if (verbose)
478 say("Hunk #%d failed at %ld.\n", hunk, newwhere);
479 } else {
480 apply_hunk(where);
481 if (verbose) {
482 say("Hunk #%d succeeded at %ld", hunk, newwhere);
483 if (fuzz != 0)
484 say(" with fuzz %ld", fuzz);
485 if (last_offset)
486 say(" (offset %ld line%s)", last_offset, last_offset == 1L ? "" : "s");
487 say(".\n");
488 }
489 }
490 }
491
492 if (out_of_mem && using_plan_a) {
493 Argc = Argc_last;
494 Argv = Argv_last;
495 say("\n\nRan out of memory using Plan A--trying "
496 "again...\n\n");
497 if (ofp)
498 fclose(ofp);
499 ofp = NULL;
500 if (rejfp)
501 fclose(rejfp);
502 rejfp = NULL;
503 continue;
504 }
505 if (hunk == 0)
506 fatal("internal error: hunk should not be 0\n");
507
508 if (!skip_rest_of_patch && !spew_output()) {
509 say("cant write %s\n", TMPOUTNAME);
510 error = 1;
511 }
512
513 ignore_signals();
514 if (!skip_rest_of_patch) {
515 struct stat statbuf;
516 char *realout = outname;
517
518 if (!check_only) {
519 enum BackupType saved = backup_type;
520 if (failed > 0 && backup_if_mismatch > 0 && backup_type == BACKUP_NONE)
521 backup_type = BACKUP_SIMPLE;
522 if (move_file(TMPOUTNAME, outname) < 0) {
523 toutkeep = 1;
524 realout = TMPOUTNAME;
525 chmod(TMPOUTNAME, filemode);
526 } else
527 chmod(outname, filemode);
528 backup_type = saved;
529
530 if (remove_empty_files && stat(realout, &statbuf) == 0 && statbuf.st_size == 0) {
531 if (verbose)
532 say("Removing %s (empty after "
533 "patching).\n",
534 realout);
535 unlink(realout);
536 }
537 }
538 }
539 if (ferror(rejfp) || fclose(rejfp)) {
540 say("Error writing %s\n", rejname);
541 error = 1;
542 }
543 rejfp = NULL;
544 if (failed) {
545 error = 1;
546 if (*rejname == '\0') {
547 if (strlcpy(rejname, outname, sizeof(rejname)) >= sizeof(rejname))
548 fatal("filename %s is too long\n", outname);
549 if (strlcat(rejname, REJEXT, sizeof(rejname)) >= sizeof(rejname))
550 fatal("filename %s is too long\n", outname);
551 }
552 if (skip_rest_of_patch) {
553 say("%d out of %d hunks ignored--saving "
554 "rejects to %s\n",
555 failed,
556 hunk,
557 rejname);
558 } else {
559 say("%d out of %d hunks FAILED -- saving "
560 "rejects to %s\n",
561 failed,
562 hunk,
563 rejname);
564 }
565 if (!check_only && move_file(TMPREJNAME, rejname) < 0)
566 trejkeep = 1;
567 }
568 set_signals(1);
569 }
570 my_exit(error);
571}
572
573static void
574reset_getopt_state(void)
575{
576#if defined(__GLIBC__) || defined(__linux__)
577 optind = 0;
578#else
579 optreset = 1;
580 optind = 1;
581#endif
582}
583
584static void
585reinitialize_almost_everything(void)
586{
587 re_patch();
588 re_input();
589
590 input_lines = 0;
591 last_frozen_line = 0;
592 filec = 0;
593 if (!out_of_mem) {
594 free(filearg[0]);
595 filearg[0] = NULL;
596 }
597 free(outname);
598 outname = NULL;
599 last_offset = 0;
600 diff_type = 0;
601 free(revision);
602 revision = NULL;
603 reverse = reverse_flag_specified;
604 skip_rest_of_patch = 0;
605 get_some_switches();
606}
607
608static void
609get_some_switches(void)
610{
611 const char *options = "b::B:cCd:D:eEfF:i:lnNo:p:r:RstuvV:x:z:";
612 static struct option longopts[] = {
613 {"backup", no_argument, 0, 'b'},
614 {"backup-if-mismatch", no_argument, &backup_if_mismatch, 1},
615 {"batch", no_argument, 0, 't'},
616 {"check", no_argument, 0, 'C'},
617 {"context", no_argument, 0, 'c'},
618 {"debug", required_argument, 0, 'x'},
619 {"directory", required_argument, 0, 'd'},
620 {"ed", no_argument, 0, 'e'},
621 {"force", no_argument, 0, 'f'},
622 {"forward", no_argument, 0, 'N'},
623 {"fuzz", required_argument, 0, 'F'},
624 {"ifdef", required_argument, 0, 'D'},
625 {"input", required_argument, 0, 'i'},
626 {"ignore-whitespace", no_argument, 0, 'l'},
627 {"no-backup-if-mismatch", no_argument, &backup_if_mismatch, 0},
628 {"normal", no_argument, 0, 'n'},
629 {"output", required_argument, 0, 'o'},
630 {"prefix", required_argument, 0, 'B'},
631 {"quiet", no_argument, 0, 's'},
632 {"reject-file", required_argument, 0, 'r'},
633 {"remove-empty-files", no_argument, 0, 'E'},
634 {"reverse", no_argument, 0, 'R'},
635 {"silent", no_argument, 0, 's'},
636 {"strip", required_argument, 0, 'p'},
637 {"suffix", required_argument, 0, 'z'},
638 {"unified", no_argument, 0, 'u'},
639 {"version", no_argument, 0, 'v'},
640 {"version-control", required_argument, 0, 'V'},
641 {"posix", no_argument, &posix, 1},
642 {NULL, 0, 0, 0}
643 };
644 int ch;
645
646 rejname[0] = '\0';
647 Argc_last = Argc;
648 Argv_last = Argv;
649 if (!Argc)
650 return;
651 reset_getopt_state();
652 while ((ch = getopt_long(Argc, Argv, options, longopts, NULL)) != -1) {
653 switch (ch) {
654 case 'b':
655 if (backup_type == BACKUP_UNDEFINED)
656 backup_type = BACKUP_NUMBERED_EXISTING;
657 if (optarg == NULL)
658 break;
659 if (verbose)
660 say("Warning, the -b suffix option has been "
661 "obsoleted by the -z option\n");
662 /* FALLTHROUGH */
663 case 'z':
664 simple_backup_suffix = savestr(optarg);
665 break;
666 case 'B':
667 origprae = savestr(optarg);
668 break;
669 case 'c':
670 diff_type = CONTEXT_DIFF;
671 break;
672 case 'C':
673 check_only = 1;
674 break;
675 case 'd':
676 if (chdir(optarg) < 0)
677 pfatal("cant cd to %s", optarg);
678 break;
679 case 'D':
680 do_defines = 1;
681 if (!isalpha((unsigned char)*optarg) && *optarg != '_')
682 fatal("argument to -D is not an identifier\n");
683 snprintf(if_defined, sizeof if_defined, "#ifdef %s\n", optarg);
684 snprintf(not_defined, sizeof not_defined, "#ifndef %s\n", optarg);
685 snprintf(end_defined, sizeof end_defined, "#endif /* %s */\n", optarg);
686 break;
687 case 'e':
688 diff_type = ED_DIFF;
689 break;
690 case 'E':
691 remove_empty_files = 1;
692 break;
693 case 'f':
694 force = 1;
695 break;
696 case 'F':
697 maxfuzz = strtolinenum(optarg, NULL);
698 break;
699 case 'i':
700 free(filearg[1]);
701 filearg[1] = fetchname(optarg, &warn_on_invalid_line, 0);
702 break;
703 case 'l':
704 canonicalize = 1;
705 break;
706 case 'n':
707 diff_type = NORMAL_DIFF;
708 break;
709 case 'N':
710 noreverse = 1;
711 break;
712 case 'o':
713 outname = savestr(optarg);
714 break;
715 case 'p':
716 strippath = (int)strtolinenum(optarg, NULL);
717 break;
718 case 'r':
719 if (strlcpy(rejname, optarg, sizeof(rejname)) >= sizeof(rejname))
720 fatal("reject filename %s too long\n", optarg);
721 break;
722 case 'R':
723 reverse = 1;
724 reverse_flag_specified = 1;
725 break;
726 case 's':
727 verbose = 0;
728 break;
729 case 't':
730 batch = 1;
731 break;
732 case 'u':
733 diff_type = UNI_DIFF;
734 break;
735 case 'v':
736 version();
737 break;
738 case 'V':
739 backup_type = get_version(optarg);
740 break;
741 case 'x':
742#ifdef DEBUGGING
743 debug = (int)strtolinenum(optarg, NULL);
744#endif
745 break;
746 default:
747 if (ch == 0)
748 break;
749 usage();
750 }
751 }
752 Argc -= optind;
753 Argv += optind;
754
755 for (ch = 0; ch < Argc; ch++) {
756 if (filec >= MAXFILEC)
757 usage();
758 filearg[filec++] = savestr(Argv[ch]);
759 }
760}
761
762static void
763usage(void)
764{
765 fprintf(
766 stderr,
767 "usage: patch [-bCcEeflNnRstuv] [-B backup-prefix] [-D symbol] "
768 "[-d directory] [-F max-fuzz] [-i patchfile] [-o out-file] "
769 "[-p strip-count] [-r rej-name] [-V method] [-x number] "
770 "[-z backup-ext] [origfile [patchfile]]\n"
771 );
772 my_exit(2);
773}
774
775static LINENUM
776locate_hunk(LINENUM fuzz)
777{
778 LINENUM first_hunk_line = pch_first();
779 LINENUM count = pch_ptrn_lines();
780 LINENUM max_line = input_lines - count + 1;
781 LINENUM line;
782 LINENUM offset;
783
784 if (!count)
785 return first_hunk_line + last_offset;
786
787 if (first_hunk_line + last_offset <= 0)
788 offset = 1 - first_hunk_line;
789 else if (first_hunk_line + last_offset > max_line)
790 offset = max_line - first_hunk_line;
791 else
792 offset = last_offset;
793
794 for (line = first_hunk_line + offset; line <= max_line; line++) {
795 if (patch_match(line, count, fuzz)) {
796 last_offset = line - first_hunk_line;
797 return line;
798 }
799 }
800 for (line = first_hunk_line + offset - 1; line >= 1; line--) {
801 if (patch_match(line, count, fuzz)) {
802 last_offset = line - first_hunk_line;
803 return line;
804 }
805 }
806 return 0;
807}
808
809static int
810patch_match(LINENUM line, LINENUM count, LINENUM fuzz)
811{
812 LINENUM i;
813 LINENUM pat_line;
814 char *str;
815 char *pat;
816 ssize_t len;
817
818 for (i = 1; i <= count; i++) {
819 pat_line = i;
820 if (i <= fuzz)
821 continue;
822 if (i > count - fuzz)
823 continue;
824 str = ifetch(line + i - 1 - fuzz, 0);
825 if (str == NULL)
826 return 0;
827 pat = pfetch(pat_line);
828 len = pch_line_len(pat_line);
829 if (canonicalize) {
830 if (!similar(str, pat, len))
831 return 0;
832 } else if (strncmp(str, pat, len) != 0)
833 return 0;
834 }
835 return 1;
836}
837
838static int
839similar(const char *a, const char *b, ssize_t len)
840{
841 while (len > 0) {
842 if (isspace((unsigned char)*b)) {
843 if (!isspace((unsigned char)*a))
844 return 0;
845 while (len > 0 && isspace((unsigned char)*b)) {
846 b++;
847 len--;
848 }
849 while (isspace((unsigned char)*a) && *a != '\n')
850 a++;
851 } else {
852 if (*a != *b)
853 return 0;
854 a++;
855 b++;
856 len--;
857 }
858 }
859 if (*a == '\n' || *a == '\r') {
860 while (*a == '\n' || *a == '\r')
861 a++;
862 return *a == '\0';
863 }
864 return *a == '\0' || isspace((unsigned char)*a);
865}
866
867static void
868abort_hunk(void)
869{
870 LINENUM i;
871
872 for (i = 1; i <= pch_end(); i++)
873 rej_line(i, i);
874}
875
876static void
877rej_line(int pat_line, LINENUM i)
878{
879 char *str = pfetch(pat_line);
880 char ch = pch_char(pat_line);
881 (void)i;
882 if (rejfp != NULL) {
883 if (ch == ' ')
884 fprintf(rejfp, " %s", str);
885 else
886 fprintf(rejfp, "%c%s", ch, str);
887 }
888}
889
890static void
891apply_hunk(LINENUM where)
892{
893 LINENUM old = 1;
894 const LINENUM lastline = pch_ptrn_lines();
895 LINENUM new = lastline + 1;
896#define OUTSIDE 0
897#define IN_IFNDEF 1
898#define IN_IFDEF 2
899#define IN_ELSE 3
900 int def_state = OUTSIDE;
901 const LINENUM pat_end = pch_end();
902
903 where--;
904 while (pch_char(new) == '=' || pch_char(new) == '\n')
905 new ++;
906
907 while (old <= lastline) {
908 if (pch_char(old) == '-') {
909 copy_till(where + old - 1, 0);
910 if (do_defines) {
911 if (def_state == OUTSIDE) {
912 fputs(not_defined, ofp);
913 def_state = IN_IFNDEF;
914 } else if (def_state == IN_IFDEF) {
915 fputs(else_defined, ofp);
916 def_state = IN_ELSE;
917 }
918 fputs(pfetch(old), ofp);
919 }
920 last_frozen_line++;
921 old++;
922 } else if (new > pat_end) {
923 break;
924 } else if (pch_char(new) == '+') {
925 copy_till(where + old - 1, 0);
926 if (do_defines) {
927 if (def_state == IN_IFNDEF) {
928 fputs(else_defined, ofp);
929 def_state = IN_ELSE;
930 } else if (def_state == OUTSIDE) {
931 fputs(if_defined, ofp);
932 def_state = IN_IFDEF;
933 }
934 }
935 fputs(pfetch(new), ofp);
936 new ++;
937 } else if (pch_char(new) != pch_char(old)) {
938 say("Out-of-sync patch, lines %ld,%ld--mangled text or "
939 "line numbers, maybe?\n",
940 pch_hunk_beg() + old,
941 pch_hunk_beg() + new);
942 my_exit(2);
943 } else if (pch_char(new) == '!') {
944 copy_till(where + old - 1, 0);
945 if (do_defines) {
946 fputs(not_defined, ofp);
947 def_state = IN_IFNDEF;
948 }
949 while (pch_char(old) == '!') {
950 if (do_defines) {
951 fputs(pfetch(old), ofp);
952 }
953 last_frozen_line++;
954 old++;
955 }
956 if (do_defines) {
957 fputs(else_defined, ofp);
958 def_state = IN_ELSE;
959 }
960 while (pch_char(new) == '!') {
961 fputs(pfetch(new), ofp);
962 new ++;
963 }
964 } else {
965 if (pch_char(new) != ' ')
966 fatal("Internal error: expected ' '\n");
967 old++;
968 new ++;
969 if (do_defines && def_state != OUTSIDE) {
970 fputs(end_defined, ofp);
971 def_state = OUTSIDE;
972 }
973 }
974 }
975 if (new <= pat_end &&pch_char(new) == '+') {
976 copy_till(where + old - 1, 0);
977 if (do_defines) {
978 if (def_state == OUTSIDE) {
979 fputs(if_defined, ofp);
980 def_state = IN_IFDEF;
981 } else if (def_state == IN_IFNDEF) {
982 fputs(else_defined, ofp);
983 def_state = IN_ELSE;
984 }
985 }
986 while (new <= pat_end &&pch_char(new) == '+') {
987 fputs(pfetch(new), ofp);
988 new ++;
989 }
990 }
991 if (do_defines && def_state != OUTSIDE) {
992 fputs(end_defined, ofp);
993 }
994#undef OUTSIDE
995#undef IN_IFNDEF
996#undef IN_IFDEF
997#undef IN_ELSE
998}
999
1000static void
1001dump_input_line(LINENUM line, int write_newline)
1002{
1003 char *s = ifetch(line, 0);
1004 if (s == NULL)
1005 return;
1006 for (; *s != '\n' && *s != '\0'; s++)
1007 putc(*s, ofp);
1008 if (write_newline)
1009 putc('\n', ofp);
1010}
1011
1012static void
1013copy_till(LINENUM line, int end)
1014{
1015 if (last_frozen_line > line)
1016 fatal("misordered hunks! output would be garbled\n");
1017 while (last_frozen_line < line) {
1018 if (++last_frozen_line == line && end)
1019 dump_input_line(last_frozen_line, !last_line_missing_eol);
1020 else
1021 dump_input_line(last_frozen_line, 1);
1022 }
1023}
1024
1025static int
1026spew_output(void)
1027{
1028 if (input_lines)
1029 copy_till(input_lines, 1);
1030 if (ferror(ofp) || fclose(ofp))
1031 return 0;
1032 ofp = NULL;
1033 return 1;
1034}
1035
1036static void
1037init_output(const char *name)
1038{
1039 ofp = fopen(name, "w");
1040 if (ofp == NULL)
1041 pfatal("cant create %s", name);
1042}
1043
1044static void
1045init_reject(const char *name)
1046{
1047 rejfp = fopen(name, "w");
1048 if (rejfp == NULL)
1049 pfatal("cant create %s", name);
1050}
1051
1052void
1053re_input(void)
1054{
1055 if (using_plan_a) {
1056 i_size = 0;
1057 free(i_ptr);
1058 i_ptr = NULL;
1059 if (i_womp != NULL) {
1060 munmap(i_womp, i_size);
1061 i_womp = NULL;
1062 }
1063 } else {
1064 using_plan_a = 1;
1065 close(tifd);
1066 tifd = -1;
1067 free(tibuf[0]);
1068 free(tibuf[1]);
1069 tibuf[0] = tibuf[1] = NULL;
1070 tiline[0] = tiline[1] = -1;
1071 tireclen = 0;
1072 }
1073}
1074
1075void
1076scan_input(const char *filename)
1077{
1078 if (!plan_a(filename))
1079 plan_b(filename);
1080 if (verbose) {
1081 say("Patching file %s using Plan %s...\n", filename, (using_plan_a ? "A" : "B"));
1082 }
1083}
1084
1085static int
1086reallocate_lines(size_t *lines_allocated)
1087{
1088 char **p;
1089 size_t new_size;
1090
1091 new_size = *lines_allocated * 3 / 2;
1092 p = pch_realloc(i_ptr, new_size + 2, sizeof(char *));
1093 if (p == NULL) {
1094 munmap(i_womp, i_size);
1095 i_womp = NULL;
1096 free(i_ptr);
1097 i_ptr = NULL;
1098 *lines_allocated = 0;
1099 return 0;
1100 }
1101 *lines_allocated = new_size;
1102 i_ptr = p;
1103 return 1;
1104}
1105
1106static int
1107plan_a(const char *filename)
1108{
1109 int ifd, statfailed;
1110 char *p, *s, *lbuf;
1111 struct stat filestat;
1112 off_t i;
1113 ptrdiff_t sz;
1114 size_t iline, lines_allocated, lbufsz;
1115
1116#ifdef DEBUGGING
1117 if (debug & 8)
1118 return 0;
1119#endif
1120
1121 if (filename == NULL || *filename == '\0')
1122 return 0;
1123
1124 statfailed = stat(filename, &filestat);
1125 if (statfailed && ok_to_create_file) {
1126 if (verbose)
1127 say("(Creating file %s...)\n", filename);
1128 if (check_only)
1129 return 1;
1130 makedirs(filename, 1);
1131 close(creat(filename, 0666));
1132 statfailed = stat(filename, &filestat);
1133 }
1134 if (statfailed && check_only)
1135 fatal("%s not found, -C mode, cant probe further\n", filename);
1136
1137 if (statfailed || (filestat.st_mode & 0222) == 0
1138 || ((filestat.st_mode & 0022) == 0 && filestat.st_uid != getuid())) {
1139 char *filebase, *filedir;
1140 struct stat cstat;
1141 char *tmp_filename1, *tmp_filename2;
1142
1143 tmp_filename1 = strdup(filename);
1144 tmp_filename2 = strdup(filename);
1145 if (tmp_filename1 == NULL || tmp_filename2 == NULL)
1146 fatal("strdupping filename");
1147 filebase = basename(tmp_filename1);
1148 filedir = dirname(tmp_filename2);
1149
1150 lbufsz = INITLINELEN;
1151 if ((lbuf = malloc(bufsz)) == NULL)
1152 pfatal("allocating line buffer");
1153 lbuf[0] = '\0';
1154
1155#define try(f, a1, a2, a3) (snprintf(lbuf, lbufsz, f, a1, a2, a3), stat(lbuf, &cstat) == 0)
1156
1157 if (try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX)
1158 || try("%s/RCS/%s%s", filedir, filebase, "")
1159 || try("%s/%s%s", filedir, filebase, RCSSUFFIX)) {
1160 if (!statfailed) {
1161 if ((filestat.st_mode & 0222) != 0)
1162 fatal(
1163 "file %s seems to be locked by "
1164 "somebody else under RCS\n",
1165 filename
1166 );
1167 if (verbose)
1168 say("Comparing file %s to default RCS "
1169 "version...\n",
1170 filename);
1171 {
1172 char *rcsdiff_argv[3];
1173 rcsdiff_argv[0] = __UNCONST(RCSDIFF);
1174 rcsdiff_argv[1] = __UNCONST(filename);
1175 rcsdiff_argv[2] = NULL;
1176 if (wexecvp(RCSDIFF, rcsdiff_argv) != 0)
1177 fatal(
1178 "cant check out file %s: "
1179 "differs from default "
1180 "RCS version\n",
1181 filename
1182 );
1183 }
1184 }
1185 if (verbose)
1186 say("Checking out file %s from RCS...\n", filename);
1187 {
1188 char *co_argv[4];
1189 co_argv[0] = __UNCONST(CHECKOUT);
1190 co_argv[1] = __UNCONST("-l");
1191 co_argv[2] = __UNCONST(filename);
1192 co_argv[3] = NULL;
1193 if (wexecvp(CHECKOUT, co_argv) != 0 || stat(filename, &filestat))
1194 fatal(
1195 "cant check out file %s from "
1196 "RCS\n",
1197 filename
1198 );
1199 }
1200 } else if (statfailed) {
1201 fatal("cant find %s\n", filename);
1202 }
1203#undef try
1204 free(lbuf);
1205 free(tmp_filename1);
1206 free(tmp_filename2);
1207 }
1208
1209 filemode = filestat.st_mode;
1210 if (!S_ISREG(filemode))
1211 fatal("%s is not a normal file--cant patch\n", filename);
1212 i_size = filestat.st_size;
1213 if (out_of_mem) {
1214 set_hunkmax();
1215 out_of_mem = 0;
1216 return 0;
1217 }
1218 if ((uintmax_t)i_size > (uintmax_t)SIZE_MAX) {
1219 say("block too large to mmap\n");
1220 return 0;
1221 }
1222 if ((ifd = open(filename, O_RDONLY)) < 0)
1223 pfatal("cant open file %s", filename);
1224
1225 if (i_size) {
1226 i_womp = mmap(NULL, i_size, PROT_READ, MAP_PRIVATE, ifd, 0);
1227 if (i_womp == MAP_FAILED) {
1228 perror("mmap failed");
1229 i_womp = NULL;
1230 close(ifd);
1231 return 0;
1232 }
1233 } else
1234 i_womp = NULL;
1235
1236 close(ifd);
1237 if (i_size)
1238 madvise(i_womp, i_size, MADV_SEQUENTIAL);
1239
1240 lines_allocated = i_size / 25;
1241 if (lines_allocated < 100)
1242 lines_allocated = 100;
1243
1244 if (!reallocate_lines(&lines_allocated))
1245 return 0;
1246
1247 iline = 1;
1248 i_ptr[iline] = i_womp;
1249 for (s = i_womp, i = 0; i < i_size && *s != '\0'; s++, i++) {
1250 if (*s == '\n') {
1251 if (iline == lines_allocated) {
1252 if (!reallocate_lines(&lines_allocated))
1253 return 0;
1254 }
1255 i_ptr[++iline] = s + 1;
1256 }
1257 }
1258 if (i_size > 0 && i_womp[i_size - 1] != '\n') {
1259 last_line_missing_eol = 1;
1260 sz = s - i_ptr[iline];
1261 p = malloc(sz + 1);
1262 if (p == NULL) {
1263 free(i_ptr);
1264 i_ptr = NULL;
1265 munmap(i_womp, i_size);
1266 i_womp = NULL;
1267 return 0;
1268 }
1269 memcpy(p, i_ptr[iline], sz);
1270 p[sz] = '\n';
1271 i_ptr[iline] = p;
1272 i_ptr[++iline] = empty_line;
1273 } else
1274 last_line_missing_eol = 0;
1275
1276 input_lines = iline - 1;
1277
1278 if (revision != NULL) {
1279 if (!rev_in_string(i_womp)) {
1280 if (force) {
1281 if (verbose)
1282 say("Warning: this file doesnt appear "
1283 "to be the %s version--patching "
1284 "anyway\n",
1285 revision);
1286 } else if (batch) {
1287 fatal(
1288 "this file doesnt appear to be the %s "
1289 "version--aborting\n",
1290 revision
1291 );
1292 } else {
1293 ask("This file doesnt appear to be the %s "
1294 "version--patch anyway? [n] ",
1295 revision);
1296 if (*buf != 'y')
1297 fatal("aborted\n");
1298 }
1299 } else if (verbose)
1300 say("Good. This file appears to be the %s version.\n", revision);
1301 }
1302 return 1;
1303}
1304
1305static void
1306plan_b(const char *filename)
1307{
1308 FILE *ifp;
1309 size_t i = 0, j, maxlen = 1;
1310 char *p;
1311 int found_revision = (revision == NULL);
1312
1313 using_plan_a = 0;
1314 if ((ifp = fopen(filename, "r")) == NULL)
1315 pfatal("cant open file %s", filename);
1316 unlink(TMPINNAME);
1317 if ((tifd = open(TMPINNAME, O_EXCL | O_CREAT | O_WRONLY, 0666)) < 0)
1318 pfatal("cant open file %s", TMPINNAME);
1319 while (getline(&buf, &bufsz, ifp) != -1) {
1320 if (revision != NULL && !found_revision && rev_in_string(buf))
1321 found_revision = 1;
1322 if ((i = strlen(buf)) > maxlen)
1323 maxlen = i;
1324 }
1325 last_line_missing_eol = i > 0 && buf[i - 1] != '\n';
1326 if (last_line_missing_eol && maxlen == i)
1327 maxlen++;
1328
1329 if (revision != NULL) {
1330 if (!found_revision) {
1331 if (force) {
1332 if (verbose)
1333 say("Warning: this file doesnt appear "
1334 "to be the %s version--patching "
1335 "anyway\n",
1336 revision);
1337 } else if (batch) {
1338 fatal(
1339 "this file doesnt appear to be the %s "
1340 "version--aborting\n",
1341 revision
1342 );
1343 } else {
1344 ask("This file doesnt appear to be the %s "
1345 "version--patch anyway? [n] ",
1346 revision);
1347 if (*buf != 'y')
1348 fatal("aborted\n");
1349 }
1350 } else if (verbose)
1351 say("Good. This file appears to be the %s version.\n", revision);
1352 }
1353 fseek(ifp, 0L, SEEK_SET);
1354 lines_per_buf = BUFFERSIZE / maxlen;
1355 tireclen = maxlen;
1356 tibuf[0] = malloc(BUFFERSIZE + 1);
1357 if (tibuf[0] == NULL)
1358 fatal("out of memory\n");
1359 tibuf[1] = malloc(BUFFERSIZE + 1);
1360 if (tibuf[1] == NULL)
1361 fatal("out of memory\n");
1362 for (i = 1;; i++) {
1363 p = tibuf[0] + maxlen * (i % lines_per_buf);
1364 if (i % lines_per_buf == 0) {
1365 if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE)
1366 pfatal("cant write temp file");
1367 }
1368 if (fgets(p, maxlen + 1, ifp) == NULL) {
1369 input_lines = i - 1;
1370 if (i % lines_per_buf != 0) {
1371 if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE)
1372 pfatal("cant write temp file");
1373 }
1374 break;
1375 }
1376 j = strlen(p);
1377 if (j == 0 || p[j - 1] != '\n')
1378 p[j] = '\n';
1379 }
1380 fclose(ifp);
1381 close(tifd);
1382 if ((tifd = open(TMPINNAME, O_RDONLY)) < 0)
1383 pfatal("cant reopen file %s", TMPINNAME);
1384}
1385
1386char *
1387ifetch(LINENUM line, int whichbuf)
1388{
1389 if (line < 1 || line > input_lines) {
1390 if (warn_on_invalid_line) {
1391 say("No such line %ld in input file, ignoring\n", line);
1392 warn_on_invalid_line = 0;
1393 }
1394 return NULL;
1395 }
1396 if (using_plan_a)
1397 return i_ptr[line];
1398 else {
1399 LINENUM offline = line % lines_per_buf;
1400 LINENUM baseline = line - offline;
1401
1402 if (tiline[0] == baseline)
1403 whichbuf = 0;
1404 else if (tiline[1] == baseline)
1405 whichbuf = 1;
1406 else {
1407 tiline[whichbuf] = baseline;
1408 if (lseek(tifd, (off_t)(baseline / lines_per_buf * BUFFERSIZE), SEEK_SET) < 0)
1409 pfatal(
1410 "cannot seek in the temporary input "
1411 "file"
1412 );
1413 if (read(tifd, tibuf[whichbuf], BUFFERSIZE) < 0)
1414 pfatal("error reading tmp file %s", TMPINNAME);
1415 }
1416 return tibuf[whichbuf] + (tireclen * offline);
1417 }
1418}
1419
1420static int
1421rev_in_string(const char *string)
1422{
1423 const char *s;
1424 size_t patlen;
1425
1426 if (revision == NULL)
1427 return 1;
1428 patlen = strlen(revision);
1429 if (strnEQ(string, revision, patlen) && isspace((unsigned char)string[patlen]))
1430 return 1;
1431 for (s = string; *s; s++) {
1432 if (isspace((unsigned char)*s) && strnEQ(s + 1, revision, patlen)
1433 && isspace((unsigned char)s[patlen + 1]))
1434 return 1;
1435 }
1436 return 0;
1437}
1438
1439void
1440re_patch(void)
1441{
1442 p_first = 0;
1443 p_newfirst = 0;
1444 p_ptrn_lines = 0;
1445 p_repl_lines = 0;
1446 p_end = (LINENUM)-1;
1447 p_max = 0;
1448 p_indent = 0;
1449}
1450
1451void
1452open_patch_file(const char *filename)
1453{
1454 struct stat filestat;
1455
1456 if (filename == NULL || *filename == '\0' || strEQ(filename, "-")) {
1457 pfp = fopen(TMPPATNAME, "w");
1458 if (pfp == NULL)
1459 pfatal("cant create %s", TMPPATNAME);
1460 while (getline(&buf, &bufsz, stdin) != -1)
1461 fprintf(pfp, "%s", buf);
1462 if (ferror(pfp) || fclose(pfp))
1463 pfatal("cant write %s", TMPPATNAME);
1464 filename = TMPPATNAME;
1465 }
1466 pfp = fopen(filename, "r");
1467 if (pfp == NULL)
1468 pfatal("patch file %s not found", filename);
1469 fstat(fileno(pfp), &filestat);
1470 p_filesize = filestat.st_size;
1471 next_intuit_at(0L, 1L);
1472 set_hunkmax();
1473}
1474
1475void
1476set_hunkmax(void)
1477{
1478 if (p_line == NULL)
1479 p_line = calloc((size_t)hunkmax, sizeof(char *));
1480 if (p_len == NULL)
1481 p_len = calloc((size_t)hunkmax, sizeof(ssize_t));
1482 if (p_char == NULL)
1483 p_char = calloc((size_t)hunkmax, sizeof(char));
1484}
1485
1486static void
1487grow_hunkmax(void)
1488{
1489 int new_hunkmax;
1490 char **new_p_line;
1491 ssize_t *new_p_len;
1492 char *new_p_char;
1493
1494 new_hunkmax = hunkmax * 2;
1495 if (p_line == NULL || p_len == NULL || p_char == NULL)
1496 fatal("Internal memory allocation error\n");
1497
1498 new_p_line = pch_realloc(p_line, new_hunkmax, sizeof(char *));
1499 if (new_p_line == NULL)
1500 free(p_line);
1501 new_p_len = pch_realloc(p_len, new_hunkmax, sizeof(ssize_t));
1502 if (new_p_len == NULL)
1503 free(p_len);
1504 new_p_char = pch_realloc(p_char, new_hunkmax, sizeof(char));
1505 if (new_p_char == NULL)
1506 free(p_char);
1507
1508 p_char = new_p_char;
1509 p_len = new_p_len;
1510 p_line = new_p_line;
1511
1512 if (p_line != NULL && p_len != NULL && p_char != NULL) {
1513 hunkmax = new_hunkmax;
1514 return;
1515 }
1516
1517 if (!using_plan_a)
1518 fatal("out of memory\n");
1519 out_of_mem = 1;
1520}
1521
1522int
1523there_is_another_patch(void)
1524{
1525 int exists = 0;
1526
1527 if (p_base != 0L && p_base >= p_filesize) {
1528 if (verbose)
1529 say("done\n");
1530 return 0;
1531 }
1532 if (verbose)
1533 say("Hmm...");
1534 diff_type = intuit_diff_type();
1535 if (!diff_type) {
1536 if (p_base != 0L) {
1537 if (verbose)
1538 say(" Ignoring the trailing garbage.\ndone\n");
1539 } else
1540 say(" I cant seem to find a patch in there anywhere\n");
1541 return 0;
1542 }
1543 if (verbose) {
1544 say(" %sooks like %s to me...\n",
1545 (p_base == 0L ? "L" : "The next patch l"),
1546 diff_type == UNI_DIFF ? "a unified diff"
1547 : diff_type == CONTEXT_DIFF ? "a context diff"
1548 : diff_type == NEW_CONTEXT_DIFF ? "a new-style context diff"
1549 : diff_type == NORMAL_DIFF ? "a normal diff"
1550 : "an ed script");
1551 }
1552 if (p_indent && verbose)
1553 say("(Patch is indented %d space%s.)\n", p_indent, p_indent == 1 ? "" : "s");
1554 skip_to(p_start, p_sline);
1555 while (filearg[0] == NULL) {
1556 if (force || batch) {
1557 say("No file to patch. Skipping...\n");
1558 filearg[0] = savestr(bestguess);
1559 skip_rest_of_patch = 1;
1560 return 1;
1561 }
1562 ask("File to patch: ");
1563 if (*buf != '\n') {
1564 free(bestguess);
1565 bestguess = savestr(buf);
1566 filearg[0] = fetchname(buf, &exists, 0);
1567 }
1568 if (!exists) {
1569 ask("No file found--skip this patch? [n] ");
1570 if (*buf != 'y')
1571 continue;
1572 if (verbose)
1573 say("Skipping patch...\n");
1574 free(filearg[0]);
1575 filearg[0] = fetchname(bestguess, &exists, 0);
1576 skip_rest_of_patch = 1;
1577 return 1;
1578 }
1579 }
1580 return 1;
1581}
1582
1583static int
1584intuit_diff_type(void)
1585{
1586 long this_line = 0, previous_line;
1587 long first_command_line = -1;
1588 LINENUM fcl_line = -1;
1589 int last_line_was_command = 0, this_is_a_command = 0;
1590 int stars_last_line = 0, stars_this_line = 0;
1591 char *s, *t;
1592 int indent, retval;
1593 struct FileName names[MAX_FILE];
1594
1595 memset(names, 0, sizeof(names));
1596 ok_to_create_file = 0;
1597 fseek(pfp, p_base, SEEK_SET);
1598 p_input_line = p_bline - 1;
1599 for (;;) {
1600 previous_line = this_line;
1601 last_line_was_command = this_is_a_command;
1602 stars_last_line = stars_this_line;
1603 this_line = ftell(pfp);
1604 indent = 0;
1605 p_input_line++;
1606 if (getline(&buf, &bufsz, pfp) == -1) {
1607 if (first_command_line >= 0L) {
1608 p_start = first_command_line;
1609 p_sline = fcl_line;
1610 retval = ED_DIFF;
1611 goto scan_exit;
1612 } else {
1613 p_start = this_line;
1614 p_sline = p_input_line;
1615 retval = 0;
1616 goto scan_exit;
1617 }
1618 }
1619 for (s = buf; *s == ' ' || *s == '\t' || *s == 'X'; s++) {
1620 if (*s == '\t')
1621 indent += 8 - (indent % 8);
1622 else
1623 indent++;
1624 }
1625 for (t = s; isdigit((unsigned char)*t) || *t == ','; t++)
1626 ;
1627 this_is_a_command = (isdigit((unsigned char)*s) && (*t == 'd' || *t == 'c' || *t == 'a'));
1628 if (first_command_line < 0L && this_is_a_command) {
1629 first_command_line = this_line;
1630 fcl_line = p_input_line;
1631 p_indent = indent;
1632 }
1633 if (!stars_last_line && strnEQ(s, "*** ", 4)) {
1634 names[OLD_FILE].path = fetchname(s + 4, &names[OLD_FILE].exists, strippath);
1635 } else if (strnEQ(s, "--- ", 4)) {
1636 names[NEW_FILE].path = fetchname(s + 4, &names[NEW_FILE].exists, strippath);
1637 } else if (strnEQ(s, "+++ ", 4)) {
1638 names[OLD_FILE].path = fetchname(s + 4, &names[OLD_FILE].exists, strippath);
1639 } else if (strnEQ(s, "Index:", 6)) {
1640 names[INDEX_FILE].path = fetchname(s + 6, &names[INDEX_FILE].exists, strippath);
1641 } else if (strnEQ(s, "Prereq:", 7)) {
1642 for (t = s + 7; isspace((unsigned char)*t); t++)
1643 ;
1644 revision = savestr(t);
1645 for (t = revision; *t && !isspace((unsigned char)*t); t++)
1646 ;
1647 *t = '\0';
1648 if (*revision == '\0') {
1649 free(revision);
1650 revision = NULL;
1651 }
1652 }
1653 if ((!diff_type || diff_type == ED_DIFF) && first_command_line >= 0L && strEQ(s, ".\n")) {
1654 p_indent = indent;
1655 p_start = first_command_line;
1656 p_sline = fcl_line;
1657 retval = ED_DIFF;
1658 goto scan_exit;
1659 }
1660 if ((!diff_type || diff_type == UNI_DIFF) && strnEQ(s, "@@ -", 4)) {
1661 if (strnEQ(s + 4, "0,0", 3))
1662 ok_to_create_file = 1;
1663 p_indent = indent;
1664 p_start = this_line;
1665 p_sline = p_input_line;
1666 retval = UNI_DIFF;
1667 goto scan_exit;
1668 }
1669 stars_this_line = strnEQ(s, "********", 8);
1670 if ((!diff_type || diff_type == CONTEXT_DIFF) && stars_last_line && strnEQ(s, "*** ", 4)) {
1671 if (atol(s + 4) == 0)
1672 ok_to_create_file = 1;
1673 while (*s != '\n')
1674 s++;
1675 p_indent = indent;
1676 p_start = previous_line;
1677 p_sline = p_input_line - 1;
1678 retval = (*(s - 1) == '*' ? NEW_CONTEXT_DIFF : CONTEXT_DIFF);
1679 goto scan_exit;
1680 }
1681 if ((!diff_type || diff_type == NORMAL_DIFF) && last_line_was_command
1682 && (strnEQ(s, "< ", 2) || strnEQ(s, "> ", 2))) {
1683 p_start = previous_line;
1684 p_sline = p_input_line - 1;
1685 p_indent = indent;
1686 retval = NORMAL_DIFF;
1687 goto scan_exit;
1688 }
1689 }
1690scan_exit:
1691 if (retval == UNI_DIFF) {
1692 struct FileName tmp = names[OLD_FILE];
1693 names[OLD_FILE] = names[NEW_FILE];
1694 names[NEW_FILE] = tmp;
1695 }
1696 if (filearg[0] == NULL) {
1697 if (posix)
1698 filearg[0] = posix_name(names, ok_to_create_file);
1699 else {
1700 if (names[OLD_FILE].path != NULL || names[NEW_FILE].path != NULL) {
1701 free(names[INDEX_FILE].path);
1702 names[INDEX_FILE].path = NULL;
1703 }
1704 filearg[0] = best_name(names, ok_to_create_file);
1705 }
1706 }
1707 free(bestguess);
1708 bestguess = NULL;
1709 if (filearg[0] != NULL)
1710 bestguess = savestr(filearg[0]);
1711 else if (!ok_to_create_file) {
1712 if (posix)
1713 bestguess = posix_name(names, 1);
1714 else
1715 bestguess = best_name(names, 1);
1716 }
1717 free(names[OLD_FILE].path);
1718 free(names[NEW_FILE].path);
1719 free(names[INDEX_FILE].path);
1720 return retval;
1721}
1722
1723static void
1724next_intuit_at(LINENUM file_pos, LINENUM file_line)
1725{
1726 p_base = file_pos;
1727 p_bline = file_line;
1728}
1729
1730static void
1731skip_to(LINENUM file_pos, LINENUM file_line)
1732{
1733 int ret;
1734
1735 if (p_base > file_pos)
1736 fatal("Internal error: seek %ld>%ld\n", p_base, file_pos);
1737 if (verbose && p_base < file_pos) {
1738 fseek(pfp, p_base, SEEK_SET);
1739 say("The text leading up to this "
1740 "was:\n--------------------------\n");
1741 while (ftell(pfp) < file_pos) {
1742 ret = getline(&buf, &bufsz, pfp);
1743 if (ret == -1)
1744 fatal("Unexpected end of file\n");
1745 say("|%s", buf);
1746 }
1747 say("--------------------------\n");
1748 } else
1749 fseek(pfp, file_pos, SEEK_SET);
1750 p_input_line = file_line - 1;
1751}
1752
1753__dead static void
1754malformed(void)
1755{
1756 fatal("malformed patch at line %ld: %s", p_input_line, buf);
1757}
1758
1759static LINENUM
1760getlinenum(const char *s)
1761{
1762 LINENUM l = (LINENUM)atol(s);
1763 if (l < 0) {
1764 l = 0;
1765 malformed();
1766 }
1767 return l;
1768}
1769
1770static LINENUM
1771getskiplinenum(char **p)
1772{
1773 char *s = *p;
1774 LINENUM l = getlinenum(s);
1775 while (isdigit((unsigned char)*s))
1776 s++;
1777 *p = s;
1778 return l;
1779}
1780
1781static int
1782remove_special_line(void)
1783{
1784 int c;
1785
1786 c = fgetc(pfp);
1787 if (c == '\\') {
1788 do {
1789 c = fgetc(pfp);
1790 } while (c != EOF && c != '\n');
1791 return 1;
1792 }
1793 if (c != EOF)
1794 fseek(pfp, -1L, SEEK_CUR);
1795 return 0;
1796}
1797
1798int
1799another_hunk(void)
1800{
1801 long line_beginning;
1802 LINENUM repl_beginning;
1803 LINENUM fillcnt;
1804 LINENUM fillsrc;
1805 LINENUM filldst;
1806 int ptrn_spaces_eaten;
1807 int repl_could_be_missing;
1808 int repl_missing;
1809 long repl_backtrack_position;
1810 LINENUM repl_patch_line;
1811 LINENUM ptrn_copiable;
1812 char *s;
1813 int context = 0;
1814 int ret;
1815
1816 while (p_end >= 0) {
1817 if (p_end == p_efake)
1818 p_end = p_bfake;
1819 else
1820 free(p_line[p_end]);
1821 p_end--;
1822 }
1823 p_efake = -1;
1824 p_max = hunkmax;
1825
1826 if (diff_type == CONTEXT_DIFF || diff_type == NEW_CONTEXT_DIFF) {
1827 line_beginning = ftell(pfp);
1828 repl_beginning = 0;
1829 fillcnt = 0;
1830 fillsrc = 0;
1831 filldst = 0;
1832 ptrn_spaces_eaten = 0;
1833 repl_could_be_missing = 1;
1834 repl_missing = 0;
1835 repl_backtrack_position = 0;
1836 repl_patch_line = 0;
1837 ptrn_copiable = 0;
1838
1839 ret = pgetline(&buf, &bufsz, pfp);
1840 p_input_line++;
1841 if (ret == -1 || strnNE(buf, "********", 8)) {
1842 next_intuit_at(line_beginning, p_input_line);
1843 return 0;
1844 }
1845 p_context = 100;
1846 p_hunk_beg = p_input_line + 1;
1847 while (p_end < p_max) {
1848 ret = pgetline(&buf, &bufsz, pfp);
1849 p_input_line++;
1850 if (ret == -1) {
1851 if (repl_beginning && repl_could_be_missing) {
1852 repl_missing = 1;
1853 goto hunk_done;
1854 }
1855 fatal("unexpected end of file in patch\n");
1856 }
1857 p_end++;
1858 if (p_end >= hunkmax)
1859 fatal(
1860 "Internal error: hunk larger than hunk "
1861 "buffer size"
1862 );
1863 p_char[p_end] = *buf;
1864 p_line[p_end] = NULL;
1865 switch (*buf) {
1866 case '*':
1867 if (strnEQ(buf, "********", 8)) {
1868 if (repl_beginning && repl_could_be_missing) {
1869 repl_missing = 1;
1870 goto hunk_done;
1871 } else
1872 fatal(
1873 "unexpected end of hunk "
1874 "at line %ld\n",
1875 p_input_line
1876 );
1877 }
1878 if (p_end != 0) {
1879 if (repl_beginning && repl_could_be_missing) {
1880 repl_missing = 1;
1881 goto hunk_done;
1882 }
1883 fatal("unexpected *** at line %ld: %s", p_input_line, buf);
1884 }
1885 context = 0;
1886 p_line[p_end] = savestr(buf);
1887 if (out_of_mem) {
1888 p_end--;
1889 return 0;
1890 }
1891 for (s = buf; *s && !isdigit((unsigned char)*s); s++)
1892 ;
1893 if (!*s)
1894 malformed();
1895 if (strnEQ(s, "0,0", 3))
1896 memmove(s, s + 2, strlen(s + 2) + 1);
1897 p_first = getskiplinenum(&s);
1898 if (*s == ',') {
1899 for (; *s && !isdigit((unsigned char)*s); s++)
1900 ;
1901 if (!*s)
1902 malformed();
1903 p_ptrn_lines = (getlinenum(s)) - p_first + 1;
1904 if (p_ptrn_lines < 0)
1905 malformed();
1906 } else if (p_first)
1907 p_ptrn_lines = 1;
1908 else {
1909 p_ptrn_lines = 0;
1910 p_first = 1;
1911 }
1912 if (p_first >= LINENUM_MAX - p_ptrn_lines || p_ptrn_lines >= LINENUM_MAX - 6)
1913 malformed();
1914 p_max = p_ptrn_lines + 6;
1915 while (p_max >= hunkmax)
1916 grow_hunkmax();
1917 p_max = hunkmax;
1918 break;
1919 case '-':
1920 if (buf[1] == '-') {
1921 if (repl_beginning || (p_end != p_ptrn_lines + 1 + (p_char[p_end - 1] == '\n'))) {
1922 if (p_end == 1) {
1923 p_end = p_ptrn_lines + 1;
1924 fillsrc = p_end + 1;
1925 filldst = 1;
1926 fillcnt = p_ptrn_lines;
1927 } else {
1928 if (repl_beginning) {
1929 if (repl_could_be_missing) {
1930 repl_missing = 1;
1931 goto hunk_done;
1932 }
1933 fatal(
1934 "duplicat"
1935 "e "
1936 "\"---\" "
1937 "at line "
1938 "%ld\n",
1939 p_input_line
1940 );
1941 } else {
1942 fatal(
1943 "prematur"
1944 "e/"
1945 "overdue "
1946 "\"---\" "
1947 "at line "
1948 "%ld\n",
1949 p_input_line
1950 );
1951 }
1952 }
1953 }
1954 repl_beginning = p_end;
1955 repl_backtrack_position = ftell(pfp);
1956 repl_patch_line = p_input_line;
1957 p_line[p_end] = savestr(buf);
1958 if (out_of_mem) {
1959 p_end--;
1960 return 0;
1961 }
1962 p_char[p_end] = '=';
1963 for (s = buf; *s && !isdigit((unsigned char)*s); s++)
1964 ;
1965 if (!*s)
1966 malformed();
1967 p_newfirst = getskiplinenum(&s);
1968 if (*s == ',') {
1969 for (; *s && !isdigit((unsigned char)*s); s++)
1970 ;
1971 if (!*s)
1972 malformed();
1973 p_repl_lines = (getlinenum(s)) - p_newfirst + 1;
1974 if (p_repl_lines < 0)
1975 malformed();
1976 } else if (p_newfirst)
1977 p_repl_lines = 1;
1978 else {
1979 p_repl_lines = 0;
1980 p_newfirst = 1;
1981 }
1982 if (p_newfirst >= LINENUM_MAX - p_repl_lines || p_repl_lines >= LINENUM_MAX - p_end)
1983 malformed();
1984 p_max = p_repl_lines + p_end;
1985 if (p_max > MAXHUNKSIZE)
1986 fatal(
1987 "hunk too large at line "
1988 "%ld\n",
1989 p_input_line
1990 );
1991 while (p_max >= hunkmax)
1992 grow_hunkmax();
1993 if (p_repl_lines != ptrn_copiable && (p_context != 0 || p_repl_lines != 1))
1994 repl_could_be_missing = 0;
1995 break;
1996 }
1997 goto change_line;
1998 case '+':
1999 case '!':
2000 repl_could_be_missing = 0;
2001 change_line:
2002 if (buf[1] == '\n' && canonicalize)
2003 strlcpy(buf + 1, " \n", bufsz - 1);
2004 if (!isspace((unsigned char)buf[1]) && buf[1] != '>' && buf[1] != '<' && repl_beginning
2005 && repl_could_be_missing) {
2006 repl_missing = 1;
2007 goto hunk_done;
2008 }
2009 if (context >= 0) {
2010 if (context < p_context)
2011 p_context = context;
2012 context = -1000;
2013 }
2014 p_line[p_end] = savestr(buf + 2);
2015 if (out_of_mem) {
2016 p_end--;
2017 return 0;
2018 }
2019 if (p_end == p_ptrn_lines) {
2020 if (remove_special_line()) {
2021 int length = strlen(p_line[p_end]) - 1;
2022 (p_line[p_end])[length] = 0;
2023 }
2024 }
2025 break;
2026 case '\t':
2027 case '\n':
2028 if (repl_beginning && repl_could_be_missing
2029 && (!ptrn_spaces_eaten || diff_type == NEW_CONTEXT_DIFF)) {
2030 repl_missing = 1;
2031 goto hunk_done;
2032 }
2033 p_line[p_end] = savestr(buf);
2034 if (out_of_mem) {
2035 p_end--;
2036 return 0;
2037 }
2038 if (p_end != p_ptrn_lines + 1) {
2039 ptrn_spaces_eaten |= (repl_beginning != 0);
2040 context++;
2041 if (!repl_beginning)
2042 ptrn_copiable++;
2043 p_char[p_end] = ' ';
2044 }
2045 break;
2046 case ' ':
2047 if (!isspace((unsigned char)buf[1]) && repl_beginning && repl_could_be_missing) {
2048 repl_missing = 1;
2049 goto hunk_done;
2050 }
2051 context++;
2052 if (!repl_beginning)
2053 ptrn_copiable++;
2054 p_line[p_end] = savestr(buf + 2);
2055 if (out_of_mem) {
2056 p_end--;
2057 return 0;
2058 }
2059 break;
2060 default:
2061 if (repl_beginning && repl_could_be_missing) {
2062 repl_missing = 1;
2063 goto hunk_done;
2064 }
2065 malformed();
2066 }
2067 if (p_line[p_end])
2068 p_len[p_end] = strlen(p_line[p_end]);
2069 else
2070 p_len[p_end] = 0;
2071 }
2072
2073 hunk_done:
2074 if (p_end >= 0 && !repl_beginning)
2075 fatal("no --- found in patch at line %ld\n", pch_hunk_beg());
2076
2077 if (repl_missing) {
2078 p_input_line = repl_patch_line;
2079 for (p_end--; p_end > repl_beginning; p_end--)
2080 free(p_line[p_end]);
2081 fseek(pfp, repl_backtrack_position, SEEK_SET);
2082
2083 if (!p_context && p_repl_lines == 1) {
2084 p_repl_lines = 0;
2085 p_max--;
2086 }
2087 fillsrc = 1;
2088 filldst = repl_beginning + 1;
2089 fillcnt = p_repl_lines;
2090 p_end = p_max;
2091 } else if (!p_context && fillcnt == 1) {
2092 while (filldst < p_end) {
2093 p_line[filldst] = p_line[filldst + 1];
2094 p_char[filldst] = p_char[filldst + 1];
2095 p_len[filldst] = p_len[filldst + 1];
2096 filldst++;
2097 }
2098 p_end--;
2099 p_first++;
2100 fillcnt = 0;
2101 p_ptrn_lines = 0;
2102 }
2103 if (diff_type == CONTEXT_DIFF && (fillcnt || (p_first > 1 && ptrn_copiable > 2 * p_context))) {
2104 diff_type = NEW_CONTEXT_DIFF;
2105 }
2106 if (fillcnt) {
2107 p_bfake = filldst;
2108 p_efake = filldst + fillcnt - 1;
2109 while (fillcnt-- > 0) {
2110 while (fillsrc <= p_end && p_char[fillsrc] != ' ')
2111 fillsrc++;
2112 if (fillsrc > p_end)
2113 fatal(
2114 "replacement text mangled in "
2115 "hunk at line %ld\n",
2116 p_hunk_beg
2117 );
2118 p_line[filldst] = p_line[fillsrc];
2119 p_char[filldst] = p_char[fillsrc];
2120 p_len[filldst] = p_len[fillsrc];
2121 fillsrc++;
2122 filldst++;
2123 }
2124 while (fillsrc <= p_end && fillsrc != repl_beginning && p_char[fillsrc] != ' ')
2125 fillsrc++;
2126 if (fillsrc != p_end + 1 && fillsrc != repl_beginning)
2127 malformed();
2128 if (filldst != p_end + 1 && filldst != repl_beginning)
2129 malformed();
2130 }
2131 if (p_line[p_end] != NULL) {
2132 if (remove_special_line()) {
2133 p_len[p_end] -= 1;
2134 (p_line[p_end])[p_len[p_end]] = 0;
2135 }
2136 }
2137 } else if (diff_type == UNI_DIFF) {
2138 LINENUM fillold;
2139 LINENUM fillnew;
2140 char ch;
2141
2142 line_beginning = ftell(pfp);
2143 ret = pgetline(&buf, &bufsz, pfp);
2144 p_input_line++;
2145 if (ret == -1 || strnNE(buf, "@@ -", 4)) {
2146 next_intuit_at(line_beginning, p_input_line);
2147 return 0;
2148 }
2149 s = buf + 4;
2150 if (!*s)
2151 malformed();
2152 p_first = getskiplinenum(&s);
2153 if (*s == ',') {
2154 s++;
2155 p_ptrn_lines = getskiplinenum(&s);
2156 } else
2157 p_ptrn_lines = 1;
2158 if (p_first >= LINENUM_MAX - p_ptrn_lines)
2159 malformed();
2160 if (*s == ' ')
2161 s++;
2162 if (*s != '+' || !*++s)
2163 malformed();
2164 p_newfirst = getskiplinenum(&s);
2165 if (*s == ',') {
2166 s++;
2167 p_repl_lines = getskiplinenum(&s);
2168 } else
2169 p_repl_lines = 1;
2170 if (*s == ' ')
2171 s++;
2172 if (*s != '@')
2173 malformed();
2174 if (p_first >= LINENUM_MAX - p_ptrn_lines || p_newfirst > LINENUM_MAX - p_repl_lines
2175 || p_ptrn_lines >= LINENUM_MAX - p_repl_lines - 1)
2176 malformed();
2177 if (!p_ptrn_lines)
2178 p_first++;
2179 p_max = p_ptrn_lines + p_repl_lines + 1;
2180 while (p_max >= hunkmax)
2181 grow_hunkmax();
2182 fillold = 1;
2183 fillnew = fillold + p_ptrn_lines;
2184 p_end = fillnew + p_repl_lines;
2185 snprintf(buf, bufsz, "*** %ld,%ld ****\n", p_first, p_first + p_ptrn_lines - 1);
2186 p_line[0] = savestr(buf);
2187 if (out_of_mem) {
2188 p_end = -1;
2189 return 0;
2190 }
2191 p_char[0] = '*';
2192 snprintf(buf, bufsz, "--- %ld,%ld ----\n", p_newfirst, p_newfirst + p_repl_lines - 1);
2193 p_line[fillnew] = savestr(buf);
2194 if (out_of_mem) {
2195 p_end = 0;
2196 return 0;
2197 }
2198 p_char[fillnew++] = '=';
2199 p_context = 100;
2200 context = 0;
2201 p_hunk_beg = p_input_line + 1;
2202 while (fillold <= p_ptrn_lines || fillnew <= p_end) {
2203 ret = pgetline(&buf, &bufsz, pfp);
2204 p_input_line++;
2205 if (ret == -1) {
2206 if (p_max - fillnew < 3)
2207 strlcpy(buf, " \n", bufsz);
2208 else
2209 fatal(
2210 "unexpected end of file in "
2211 "patch\n"
2212 );
2213 }
2214 if (*buf == '\t' || *buf == '\n') {
2215 ch = ' ';
2216 s = savestr(buf);
2217 } else {
2218 ch = *buf;
2219 s = savestr(buf + 1);
2220 }
2221 if (out_of_mem) {
2222 while (--fillnew > p_ptrn_lines)
2223 free(p_line[fillnew]);
2224 p_end = fillold - 1;
2225 return 0;
2226 }
2227 switch (ch) {
2228 case '-':
2229 if (fillold > p_ptrn_lines) {
2230 free(s);
2231 p_end = fillnew - 1;
2232 malformed();
2233 }
2234 p_char[fillold] = ch;
2235 p_line[fillold] = s;
2236 p_len[fillold++] = strlen(s);
2237 if (fillold > p_ptrn_lines) {
2238 if (remove_special_line()) {
2239 p_len[fillold - 1] -= 1;
2240 s[p_len[fillold - 1]] = 0;
2241 }
2242 }
2243 break;
2244 case '=':
2245 ch = ' ';
2246 /* FALL THROUGH */
2247 case ' ':
2248 if (fillold > p_ptrn_lines) {
2249 free(s);
2250 while (--fillnew > p_ptrn_lines)
2251 free(p_line[fillnew]);
2252 p_end = fillold - 1;
2253 malformed();
2254 }
2255 context++;
2256 p_char[fillold] = ch;
2257 p_line[fillold] = s;
2258 p_len[fillold++] = strlen(s);
2259 s = savestr(s);
2260 if (out_of_mem) {
2261 while (--fillnew > p_ptrn_lines)
2262 free(p_line[fillnew]);
2263 p_end = fillold - 1;
2264 return 0;
2265 }
2266 if (fillold > p_ptrn_lines) {
2267 if (remove_special_line()) {
2268 p_len[fillold - 1] -= 1;
2269 s[p_len[fillold - 1]] = 0;
2270 }
2271 }
2272 /* FALL THROUGH */
2273 case '+':
2274 if (fillnew > p_end) {
2275 free(s);
2276 while (--fillnew > p_ptrn_lines)
2277 free(p_line[fillnew]);
2278 p_end = fillold - 1;
2279 malformed();
2280 }
2281 p_char[fillnew] = ch;
2282 p_line[fillnew] = s;
2283 p_len[fillnew++] = strlen(s);
2284 if (fillold > p_ptrn_lines) {
2285 if (remove_special_line()) {
2286 p_len[fillnew - 1] -= 1;
2287 s[p_len[fillnew - 1]] = 0;
2288 }
2289 }
2290 break;
2291 default:
2292 p_end = fillnew;
2293 malformed();
2294 }
2295 if (ch != ' ' && context > 0) {
2296 if (context < p_context)
2297 p_context = context;
2298 context = -1000;
2299 }
2300 }
2301 } else {
2302 char hunk_type;
2303 int i;
2304 LINENUM min, max;
2305
2306 line_beginning = ftell(pfp);
2307 p_context = 0;
2308 ret = pgetline(&buf, &bufsz, pfp);
2309 p_input_line++;
2310 if (ret == -1 || !isdigit((unsigned char)*buf)) {
2311 next_intuit_at(line_beginning, p_input_line);
2312 return 0;
2313 }
2314 s = buf;
2315 p_first = getskiplinenum(&s);
2316 if (*s == ',') {
2317 s++;
2318 p_ptrn_lines = getskiplinenum(&s) - p_first + 1;
2319 } else
2320 p_ptrn_lines = (*s != 'a');
2321 if (p_first >= LINENUM_MAX - p_ptrn_lines)
2322 malformed();
2323 hunk_type = *s++;
2324 if (hunk_type == 'a')
2325 p_first++;
2326 min = getskiplinenum(&s);
2327 if (*s == ',')
2328 max = getlinenum(++s);
2329 else
2330 max = min;
2331 if (min < 0 || min > max || max - min == LINENUM_MAX)
2332 malformed();
2333 if (hunk_type == 'd')
2334 min++;
2335 p_end = p_ptrn_lines + p_repl_lines + 1;
2336 p_newfirst = min;
2337 p_repl_lines = max - min + 1;
2338 if (p_newfirst > LINENUM_MAX - p_repl_lines || p_ptrn_lines >= LINENUM_MAX - p_repl_lines - 1)
2339 malformed();
2340 p_end = p_ptrn_lines + p_repl_lines + 1;
2341 if (p_end > MAXHUNKSIZE)
2342 fatal("hunk too large at line %ld\n", p_input_line);
2343 while (p_end >= hunkmax)
2344 grow_hunkmax();
2345 snprintf(buf, bufsz, "*** %ld,%ld\n", p_first, p_first + p_ptrn_lines - 1);
2346 p_line[0] = savestr(buf);
2347 if (out_of_mem) {
2348 p_end = -1;
2349 return 0;
2350 }
2351 p_char[0] = '*';
2352 for (i = 1; i <= p_ptrn_lines; i++) {
2353 ret = pgetline(&buf, &bufsz, pfp);
2354 p_input_line++;
2355 if (ret == -1)
2356 fatal(
2357 "unexpected end of file in patch at line "
2358 "%ld\n",
2359 p_input_line
2360 );
2361 if (*buf != '<')
2362 fatal("< expected at line %ld of patch\n", p_input_line);
2363 p_line[i] = savestr(buf + 2);
2364 if (out_of_mem) {
2365 p_end = i - 1;
2366 return 0;
2367 }
2368 p_len[i] = strlen(p_line[i]);
2369 p_char[i] = '-';
2370 }
2371 if (remove_special_line()) {
2372 p_len[i - 1] -= 1;
2373 (p_line[i - 1])[p_len[i - 1]] = 0;
2374 }
2375 if (hunk_type == 'c') {
2376 ret = pgetline(&buf, &bufsz, pfp);
2377 p_input_line++;
2378 if (ret == -1)
2379 fatal(
2380 "unexpected end of file in patch at line "
2381 "%ld\n",
2382 p_input_line
2383 );
2384 if (*buf != '-')
2385 fatal("--- expected at line %ld of patch\n", p_input_line);
2386 }
2387 snprintf(buf, bufsz, "--- %ld,%ld\n", min, max);
2388 p_line[i] = savestr(buf);
2389 if (out_of_mem) {
2390 p_end = i - 1;
2391 return 0;
2392 }
2393 p_char[i] = '=';
2394 for (i++; i <= p_end; i++) {
2395 ret = pgetline(&buf, &bufsz, pfp);
2396 p_input_line++;
2397 if (ret == -1)
2398 fatal(
2399 "unexpected end of file in patch at line "
2400 "%ld\n",
2401 p_input_line
2402 );
2403 if (*buf != '>')
2404 fatal("> expected at line %ld of patch\n", p_input_line);
2405 p_line[i] = savestr(buf + 2);
2406 if (out_of_mem) {
2407 p_end = i - 1;
2408 return 0;
2409 }
2410 p_len[i] = strlen(p_line[i]);
2411 p_char[i] = '+';
2412 }
2413 if (remove_special_line()) {
2414 p_len[i - 1] -= 1;
2415 (p_line[i - 1])[p_len[i - 1]] = 0;
2416 }
2417 }
2418 if (reverse) {
2419 if (!pch_swap())
2420 say("Not enough memory to swap next hunk!\n");
2421 }
2422 if (p_end + 1 < hunkmax)
2423 p_char[p_end + 1] = '^';
2424 return 1;
2425}
2426
2427int
2428pgetline(char **bf, size_t *sz, FILE *fp)
2429{
2430 char *s;
2431 int indent = 0;
2432 int ret;
2433
2434 ret = getline(bf, sz, fp);
2435 if (p_indent && ret != -1) {
2436 for (s = buf; indent < p_indent && (*s == ' ' || *s == '\t' || *s == 'X'); s++) {
2437 if (*s == '\t')
2438 indent += 8 - (indent % 7);
2439 else
2440 indent++;
2441 }
2442 if (buf != s && strlcpy(buf, s, bufsz) >= bufsz)
2443 fatal("buffer too small in pgetline()\n");
2444 }
2445 return ret;
2446}
2447
2448int
2449pch_swap(void)
2450{
2451 char **tp_line;
2452 ssize_t *tp_len;
2453 char *tp_char;
2454 LINENUM i;
2455 LINENUM n;
2456 int blankline = 0;
2457 char *s;
2458
2459 i = p_first;
2460 p_first = p_newfirst;
2461 p_newfirst = i;
2462
2463 tp_line = p_line;
2464 tp_len = p_len;
2465 tp_char = p_char;
2466 p_line = NULL;
2467 p_len = NULL;
2468 p_char = NULL;
2469 set_hunkmax();
2470 if (p_line == NULL || p_len == NULL || p_char == NULL) {
2471 free(p_line);
2472 p_line = tp_line;
2473 free(p_len);
2474 p_len = tp_len;
2475 free(p_char);
2476 p_char = tp_char;
2477 return 0;
2478 }
2479
2480 i = p_ptrn_lines + 1;
2481 if (tp_char[i] == '\n') {
2482 blankline = 1;
2483 i++;
2484 }
2485 if (p_efake >= 0) {
2486 if (p_efake <= i)
2487 n = p_end - i + 1;
2488 else
2489 n = -i;
2490 p_efake += n;
2491 p_bfake += n;
2492 }
2493 for (n = 0; i <= p_end; i++, n++) {
2494 p_line[n] = tp_line[i];
2495 p_char[n] = tp_char[i];
2496 if (p_char[n] == '+')
2497 p_char[n] = '-';
2498 p_len[n] = tp_len[i];
2499 }
2500 if (blankline) {
2501 i = p_ptrn_lines + 1;
2502 p_line[n] = tp_line[i];
2503 p_char[n] = tp_char[i];
2504 p_len[n] = tp_len[i];
2505 n++;
2506 }
2507 if (p_char[0] != '=')
2508 fatal("expected '=' found '%c'\n", p_char[0]);
2509 p_char[0] = '*';
2510 for (s = p_line[0]; *s; s++)
2511 if (*s == '-')
2512 *s = '*';
2513
2514 if (p_char[0] != '*')
2515 fatal("expected '*' found '%c'\n", p_char[0]);
2516 tp_char[0] = '=';
2517 for (s = tp_line[0]; *s; s++)
2518 if (*s == '*')
2519 *s = '-';
2520 for (i = 0; n <= p_end; i++, n++) {
2521 p_line[n] = tp_line[i];
2522 p_char[n] = tp_char[i];
2523 if (p_char[n] == '-')
2524 p_char[n] = '+';
2525 p_len[n] = tp_len[i];
2526 }
2527 if (i != p_ptrn_lines + 1)
2528 fatal("expected %ld lines, got %ld\n", p_ptrn_lines + 1, i);
2529
2530 i = p_ptrn_lines;
2531 p_ptrn_lines = p_repl_lines;
2532 p_repl_lines = i;
2533
2534 free(tp_line);
2535 free(tp_len);
2536 free(tp_char);
2537 return 1;
2538}
2539
2540LINENUM
2541pch_first(void)
2542{
2543 return p_first;
2544}
2545
2546LINENUM
2547pch_ptrn_lines(void)
2548{
2549 return p_ptrn_lines;
2550}
2551
2552LINENUM
2553pch_newfirst(void)
2554{
2555 return p_newfirst;
2556}
2557
2558LINENUM
2559pch_repl_lines(void)
2560{
2561 return p_repl_lines;
2562}
2563
2564LINENUM
2565pch_end(void)
2566{
2567 return p_end;
2568}
2569
2570LINENUM
2571pch_context(void)
2572{
2573 return p_context;
2574}
2575
2576ssize_t
2577pch_line_len(LINENUM line)
2578{
2579 return p_len[line];
2580}
2581
2582char
2583pch_char(LINENUM line)
2584{
2585 return p_char[line];
2586}
2587
2588char *
2589pfetch(LINENUM line)
2590{
2591 return p_line[line];
2592}
2593
2594LINENUM
2595pch_hunk_beg(void)
2596{
2597 return p_hunk_beg;
2598}
2599
2600void
2601do_ed_script(void)
2602{
2603 char *t;
2604 long beginning_of_this_line;
2605 FILE *pipefp = NULL;
2606 int continuation;
2607
2608 if (!skip_rest_of_patch) {
2609 if (copy_file(filearg[0], TMPOUTNAME) < 0) {
2610 unlink(TMPOUTNAME);
2611 fatal("cant create temp file %s", TMPOUTNAME);
2612 }
2613 snprintf(buf, bufsz, "%s -S%s %s", _PATH_ED, verbose ? "" : "s", TMPOUTNAME);
2614 pipefp = wpopen(buf, NULL, "w");
2615 }
2616 for (;;) {
2617 beginning_of_this_line = ftell(pfp);
2618 if (pgetline(&buf, &bufsz, pfp) == -1) {
2619 next_intuit_at(beginning_of_this_line, p_input_line);
2620 break;
2621 }
2622 p_input_line++;
2623 for (t = buf; isdigit((unsigned char)*t) || *t == ','; t++)
2624 ;
2625 if (isdigit((unsigned char)*buf)
2626 && (*t == 'a' || *t == 'c' || *t == 'd' || *t == 'i' || *t == 's')) {
2627 if (pipefp != NULL)
2628 fprintf(pipefp, "%s", buf);
2629 if (*t == 's') {
2630 for (;;) {
2631 continuation = 0;
2632 t = strchr(buf, '\0') - 1;
2633 while (--t >= buf && *t == '\\')
2634 continuation = !continuation;
2635 if (!continuation || pgetline(&buf, &bufsz, pfp) == -1)
2636 break;
2637 if (pipefp != NULL)
2638 fprintf(pipefp, "%s", buf);
2639 }
2640 } else if (*t != 'd') {
2641 while (pgetline(&buf, &bufsz, pfp) != -1) {
2642 p_input_line++;
2643 if (pipefp != NULL)
2644 fprintf(pipefp, "%s", buf);
2645 if (strEQ(buf, ".\n"))
2646 break;
2647 }
2648 }
2649 } else {
2650 next_intuit_at(beginning_of_this_line, p_input_line);
2651 break;
2652 }
2653 }
2654 if (pipefp == NULL)
2655 return;
2656 fprintf(pipefp, "w\n");
2657 fprintf(pipefp, "q\n");
2658 fflush(pipefp);
2659 wpclose(pipefp);
2660 ignore_signals();
2661 if (!check_only) {
2662 if (move_file(TMPOUTNAME, outname) < 0) {
2663 toutkeep = 1;
2664 chmod(TMPOUTNAME, filemode);
2665 } else
2666 chmod(outname, filemode);
2667 }
2668 set_signals(1);
2669}
2670
2671static char *
2672posix_name(const struct FileName *names, int assume_exists)
2673{
2674 char *path = NULL;
2675 int i;
2676
2677 for (i = 0; i < MAX_FILE; i++) {
2678 if (names[i].path != NULL && names[i].exists) {
2679 path = names[i].path;
2680 break;
2681 }
2682 }
2683 if (path == NULL && !assume_exists) {
2684 for (i = 0; i < MAX_FILE; i++) {
2685 if (names[i].path != NULL && (path = checked_in(names[i].path)) != NULL)
2686 break;
2687 }
2688 if (path == NULL && ok_to_create_file && names[NEW_FILE].path != NULL)
2689 path = names[NEW_FILE].path;
2690 }
2691 return path ? savestr(path) : NULL;
2692}
2693
2694static char *
2695best_name(const struct FileName *names, int assume_exists)
2696{
2697 size_t min_components, min_baselen, min_len, tmp;
2698 char *best = NULL;
2699 int i;
2700
2701 min_components = min_baselen = min_len = SIZE_MAX;
2702 for (i = INDEX_FILE; i >= OLD_FILE; i--) {
2703 if (names[i].path == NULL || (!names[i].exists && !assume_exists))
2704 continue;
2705 if ((tmp = num_components(names[i].path)) > min_components)
2706 continue;
2707 min_components = tmp;
2708 if ((tmp = strlen(basename(names[i].path))) > min_baselen)
2709 continue;
2710 min_baselen = tmp;
2711 if ((tmp = strlen(names[i].path)) > min_len)
2712 continue;
2713 min_len = tmp;
2714 best = names[i].path;
2715 }
2716 if (best == NULL) {
2717 min_components = min_baselen = min_len = SIZE_MAX;
2718 for (i = INDEX_FILE; i >= OLD_FILE; i--) {
2719 if (names[i].path == NULL || checked_in(names[i].path) == NULL)
2720 continue;
2721 if ((tmp = num_components(names[i].path)) > min_components)
2722 continue;
2723 min_components = tmp;
2724 if ((tmp = strlen(basename(names[i].path))) > min_baselen)
2725 continue;
2726 min_baselen = tmp;
2727 if ((tmp = strlen(names[i].path)) > min_len)
2728 continue;
2729 min_len = tmp;
2730 best = names[i].path;
2731 }
2732 if (best == NULL && ok_to_create_file && names[NEW_FILE].path != NULL)
2733 best = names[NEW_FILE].path;
2734 }
2735 return best ? savestr(best) : NULL;
2736}
2737
2738static size_t
2739num_components(const char *path)
2740{
2741 size_t n;
2742 const char *cp;
2743
2744 for (n = 0, cp = path; (cp = strchr(cp, '/')) != NULL; n++, cp++) {
2745 while (*cp == '/')
2746 cp++;
2747 }
2748 return n;
2749}
2750
2751LINENUM
2752strtolinenum(char *nptr, char **endptr)
2753{
2754 LINENUM rv;
2755 char c;
2756 char *p;
2757 const char *errstr;
2758
2759 for (p = nptr; isdigit((unsigned char)*p); p++)
2760 ;
2761
2762 if (p == nptr)
2763 malformed();
2764
2765 c = *p;
2766 *p = '\0';
2767
2768 rv = strtonum(nptr, 0, LINENUM_MAX, &errstr);
2769 if (errstr != NULL)
2770 fatal("invalid line number at line %ld: `%s' is %s\n", p_input_line, nptr, errstr);
2771
2772 *p = c;
2773 *endptr = p;
2774
2775 return rv;
2776}
2777
2778int
2779move_file(const char *from, const char *to)
2780{
2781 int fromfd;
2782 ssize_t i;
2783
2784 if (strEQ(to, "-")) {
2785 fromfd = open(from, O_RDONLY);
2786 if (fromfd < 0)
2787 pfatal("internal error, cant reopen %s", from);
2788 while ((i = read(fromfd, buf, bufsz)) > 0) {
2789 if (write(STDOUT_FILENO, buf, i) != i)
2790 pfatal("write failed");
2791 }
2792 close(fromfd);
2793 return 0;
2794 }
2795 if (backup_file(to) < 0) {
2796 say("Cant backup %s, output is in %s: %s\n", to, from, strerror(errno));
2797 return -1;
2798 }
2799 if (rename(from, to) < 0) {
2800 if (errno != EXDEV || copy_file(from, to) < 0) {
2801 say("Cant create %s, output is in %s: %s\n", to, from, strerror(errno));
2802 return -1;
2803 }
2804 }
2805 return 0;
2806}
2807
2808int
2809backup_file(const char *orig)
2810{
2811 struct stat filestat;
2812 char bakname[PATH_MAX], *s, *simplename;
2813 dev_t orig_device;
2814 ino_t orig_inode;
2815
2816 if (backup_type == BACKUP_NONE || stat(orig, &filestat) != 0)
2817 return 0;
2818 if ((origprae && *origprae == 0) || *simple_backup_suffix == 0) {
2819 unlink(orig);
2820 return 0;
2821 }
2822 orig_device = filestat.st_dev;
2823 orig_inode = filestat.st_ino;
2824
2825 if (origprae) {
2826 if (strlcpy(bakname, origprae, sizeof(bakname)) >= sizeof(bakname)
2827 || strlcat(bakname, orig, sizeof(bakname)) >= sizeof(bakname))
2828 fatal("filename too long\n");
2829 } else {
2830 if ((s = find_backup_file_name(orig)) == NULL)
2831 fatal("out of memory\n");
2832 if (strlcpy(bakname, s, sizeof(bakname)) >= sizeof(bakname))
2833 fatal("filename too long\n");
2834 free(s);
2835 }
2836
2837 if ((simplename = strrchr(bakname, '/')) != NULL)
2838 simplename = simplename + 1;
2839 else
2840 simplename = bakname;
2841
2842 while (stat(bakname, &filestat) == 0 && orig_device == filestat.st_dev
2843 && orig_inode == filestat.st_ino) {
2844 for (s = simplename; *s && !islower((unsigned char)*s); s++)
2845 ;
2846 if (*s)
2847 *s = toupper((unsigned char)*s);
2848 else
2849 memmove(simplename, simplename + 1, strlen(simplename + 1) + 1);
2850 }
2851 if (rename(orig, bakname) < 0) {
2852 if (errno != EXDEV || copy_file(orig, bakname) < 0)
2853 return -1;
2854 }
2855 return 0;
2856}
2857
2858int
2859copy_file(const char *from, const char *to)
2860{
2861 int tofd, fromfd;
2862 ssize_t i;
2863
2864 tofd = open(to, O_CREAT | O_TRUNC | O_WRONLY, 0666);
2865 if (tofd < 0)
2866 return -1;
2867 fromfd = open(from, O_RDONLY, 0);
2868 if (fromfd < 0)
2869 pfatal("internal error, cant reopen %s", from);
2870 while ((i = read(fromfd, buf, bufsz)) > 0) {
2871 if (write(tofd, buf, i) != i)
2872 pfatal("write to %s failed", to);
2873 }
2874 close(fromfd);
2875 close(tofd);
2876 return 0;
2877}
2878
2879char *
2880savestr(const char *s)
2881{
2882 char *rv;
2883
2884 if (!s)
2885 s = "Oops";
2886 rv = strdup(s);
2887 if (rv == NULL) {
2888 if (using_plan_a)
2889 out_of_mem = 1;
2890 else
2891 fatal("out of memory\n");
2892 }
2893 return rv;
2894}
2895
2896void
2897say(const char *fmt, ...)
2898{
2899 va_list ap;
2900
2901 va_start(ap, fmt);
2902 vfprintf(stderr, fmt, ap);
2903 va_end(ap);
2904 fflush(stderr);
2905}
2906
2907__dead void
2908fatal(const char *fmt, ...)
2909{
2910 va_list ap;
2911
2912 va_start(ap, fmt);
2913 fprintf(stderr, "patch: **** ");
2914 vfprintf(stderr, fmt, ap);
2915 va_end(ap);
2916 my_exit(2);
2917}
2918
2919__dead void
2920pfatal(const char *fmt, ...)
2921{
2922 va_list ap;
2923 int errnum = errno;
2924
2925 fprintf(stderr, "patch: **** ");
2926 va_start(ap, fmt);
2927 vfprintf(stderr, fmt, ap);
2928 va_end(ap);
2929 fprintf(stderr, ": %s\n", strerror(errnum));
2930 my_exit(2);
2931}
2932
2933void
2934ask(const char *fmt, ...)
2935{
2936 va_list ap;
2937 ssize_t nr = 0;
2938 static int ttyfd = -1;
2939
2940 va_start(ap, fmt);
2941 vfprintf(stdout, fmt, ap);
2942 va_end(ap);
2943 fflush(stdout);
2944 if (ttyfd < 0)
2945 ttyfd = open(ARUU_PATH_DEVTTY, O_RDONLY);
2946 if (ttyfd >= 0) {
2947 if ((nr = read(ttyfd, buf, bufsz)) > 0 && buf[nr - 1] == '\n')
2948 buf[nr - 1] = '\0';
2949 }
2950 if (ttyfd < 0 || nr <= 0) {
2951 putchar('\n');
2952 buf[0] = '\0';
2953 }
2954}
2955
2956void
2957set_signals(int reset)
2958{
2959 static sig_t hupval, intval;
2960
2961 if (!reset) {
2962 hupval = signal(SIGHUP, SIG_IGN);
2963 if (hupval != SIG_IGN)
2964 hupval = (sig_t)my_exit;
2965 intval = signal(SIGINT, SIG_IGN);
2966 if (intval != SIG_IGN)
2967 intval = (sig_t)my_exit;
2968 }
2969 signal(SIGHUP, hupval);
2970 signal(SIGINT, intval);
2971}
2972
2973void
2974ignore_signals(void)
2975{
2976 signal(SIGHUP, SIG_IGN);
2977 signal(SIGINT, SIG_IGN);
2978}
2979
2980void
2981makedirs(const char *filename, int striplast)
2982{
2983 char *tmpbuf;
2984
2985 if ((tmpbuf = strdup(filename)) == NULL)
2986 fatal("out of memory\n");
2987
2988 if (striplast) {
2989 char *s = strrchr(tmpbuf, '/');
2990 if (s == NULL) {
2991 free(tmpbuf);
2992 return;
2993 }
2994 *s = '\0';
2995 }
2996 if (mkpath(tmpbuf) != 0)
2997 pfatal("creation of %s failed", tmpbuf);
2998 free(tmpbuf);
2999}
3000
3001char *
3002fetchname(const char *at, int *exists, int strip_leading)
3003{
3004 char *fullname, *name, *t;
3005 int sleading, tab;
3006 struct stat filestat;
3007
3008 if (at == NULL || *at == '\0')
3009 return NULL;
3010 while (isspace((unsigned char)*at))
3011 at++;
3012 if (strnEQ(at, ARUU_PATH_DEVNULL, sizeof(ARUU_PATH_DEVNULL) - 1))
3013 return NULL;
3014 name = fullname = t = savestr(at);
3015 tab = strchr(t, '\t') != NULL;
3016 for (sleading = strip_leading; *t != '\0' && ((tab && *t != '\t') || !isspace((unsigned char)*t));
3017 t++) {
3018 if (t[0] == '/' && t[1] != '/' && t[1] != '\0') {
3019 if (--sleading >= 0)
3020 name = t + 1;
3021 }
3022 }
3023 *t = '\0';
3024
3025 if (strip_leading == 957 && name != fullname && *fullname != '/') {
3026 name[-1] = '\0';
3027 if (stat(fullname, &filestat) == 0 && S_ISDIR(filestat.st_mode)) {
3028 name[-1] = '/';
3029 name = fullname;
3030 }
3031 }
3032 name = savestr(name);
3033 free(fullname);
3034
3035 *exists = stat(name, &filestat) == 0;
3036 return name;
3037}
3038
3039char *
3040checked_in(char *file)
3041{
3042 char *filebase, *filedir, tmpbuf[PATH_MAX];
3043 struct stat filestat;
3044
3045 filebase = basename(file);
3046 filedir = dirname(file);
3047
3048#define try(f, a1, a2, a3) \
3049 (snprintf(tmpbuf, sizeof tmpbuf, f, a1, a2, a3), stat(tmpbuf, &filestat) == 0)
3050
3051 if (try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX) || try("%s/RCS/%s%s", filedir, filebase, "")
3052 || try("%s/%s%s", filedir, filebase, RCSSUFFIX)
3053 || try("%s/SCCS/%s%s", filedir, SCCSPREFIX, filebase)
3054 || try("%s/%s%s", filedir, SCCSPREFIX, filebase))
3055 return file;
3056
3057 return NULL;
3058}
3059
3060__dead void
3061version(void)
3062{
3063 printf("Patch version 2.0-12u9-NetBSD\n");
3064 my_exit(EXIT_SUCCESS);
3065}
3066
3067__dead void
3068my_exit(int status)
3069{
3070 unlink(TMPINNAME);
3071 if (!toutkeep)
3072 unlink(TMPOUTNAME);
3073 if (!trejkeep)
3074 unlink(TMPREJNAME);
3075 unlink(TMPPATNAME);
3076 exit(status);
3077}
3078
3079static void *
3080pch_realloc(void *ptr, size_t number, size_t size)
3081{
3082 if (number > SIZE_MAX / size) {
3083 errno = EOVERFLOW;
3084 return NULL;
3085 }
3086 return realloc(ptr, number * size);
3087}
3088
3089int
3090mkpath(char *path)
3091{
3092 struct stat sb;
3093 char *slash;
3094 int done = 0;
3095
3096 slash = path;
3097 while (!done) {
3098 slash += strspn(slash, "/");
3099 slash += strcspn(slash, "/");
3100 done = (*slash == '\0');
3101 *slash = '\0';
3102 if (stat(path, &sb)) {
3103 if (errno != ENOENT || (mkdir(path, 0777) && errno != EEXIST)) {
3104 weprintf("%s", path);
3105 return -1;
3106 }
3107 } else if (!S_ISDIR(sb.st_mode)) {
3108 weprintf("%s: %s", path, strerror(ENOTDIR));
3109 return -1;
3110 }
3111 *slash = '/';
3112 }
3113 return 0;
3114}
3115
3116char *
3117find_backup_file_name(const char *file)
3118{
3119 char *dir, *base_versions, *tmp_file;
3120 int highest_backup;
3121
3122 if (backup_type == BACKUP_SIMPLE)
3123 return patch_concat(file, simple_backup_suffix);
3124 tmp_file = strdup(file);
3125 if (tmp_file == NULL)
3126 return NULL;
3127 base_versions = patch_concat(basename(tmp_file), ".~");
3128 free(tmp_file);
3129 if (base_versions == NULL)
3130 return NULL;
3131 tmp_file = strdup(file);
3132 if (tmp_file == NULL) {
3133 free(base_versions);
3134 return NULL;
3135 }
3136 dir = dirname(tmp_file);
3137 if (dir == NULL) {
3138 free(base_versions);
3139 free(tmp_file);
3140 return NULL;
3141 }
3142 highest_backup = max_backup_version(base_versions, dir);
3143 free(base_versions);
3144 free(tmp_file);
3145 if (backup_type == BACKUP_NUMBERED_EXISTING && highest_backup == 0)
3146 return patch_concat(file, simple_backup_suffix);
3147 return make_version_name(file, highest_backup + 1);
3148}
3149
3150static int
3151max_backup_version(const char *file, const char *dir)
3152{
3153 DIR *dirp;
3154 struct dirent *dp;
3155 int highest_version, this_version;
3156 size_t file_name_length;
3157
3158 dirp = opendir(dir);
3159 if (dirp == NULL)
3160 return 0;
3161
3162 highest_version = 0;
3163 file_name_length = strlen(file);
3164 while ((dp = readdir(dirp)) != NULL) {
3165 if (strlen(dp->d_name) <= file_name_length)
3166 continue;
3167 this_version = version_number(file, dp->d_name, file_name_length);
3168 if (this_version > highest_version)
3169 highest_version = this_version;
3170 }
3171 closedir(dirp);
3172 return highest_version;
3173}
3174
3175static char *
3176make_version_name(const char *file, int version_num)
3177{
3178 char *backup_name;
3179
3180 if (asprintf(&backup_name, "%s.~%d~", file, version_num) == -1)
3181 return NULL;
3182 return backup_name;
3183}
3184
3185static int
3186version_number(const char *base, const char *backup, size_t base_length)
3187{
3188 int version_num;
3189 const char *p;
3190
3191 version_num = 0;
3192 if (!strncmp(base, backup, base_length) && ISDIGIT(backup[base_length])) {
3193 for (p = &backup[base_length]; ISDIGIT(*p); ++p)
3194 version_num = version_num * 10 + *p - '0';
3195 if (p[0] != '~' || p[1])
3196 version_num = 0;
3197 }
3198 return version_num;
3199}
3200
3201static char *
3202patch_concat(const char *str1, const char *str2)
3203{
3204 char *newstr;
3205
3206 if (asprintf(&newstr, "%s%s", str1, str2) == -1)
3207 return NULL;
3208 return newstr;
3209}
3210
3211static int
3212argmatch(const char *arg, const char **optlist)
3213{
3214 int i;
3215 size_t arglen;
3216 int matchind = -1;
3217 int ambiguous = 0;
3218
3219 arglen = strlen(arg);
3220 for (i = 0; optlist[i]; i++) {
3221 if (!strncmp(optlist[i], arg, arglen)) {
3222 if (strlen(optlist[i]) == arglen)
3223 return i;
3224 else if (matchind == -1)
3225 matchind = i;
3226 else
3227 ambiguous = 1;
3228 }
3229 }
3230 if (ambiguous)
3231 return -2;
3232 else
3233 return matchind;
3234}
3235
3236static void
3237invalid_arg(const char *kind, const char *value, int problem)
3238{
3239 fprintf(stderr, "patch: ");
3240 if (problem == -1)
3241 fprintf(stderr, "invalid");
3242 else
3243 fprintf(stderr, "ambiguous");
3244 fprintf(stderr, " %s `%s'\n", kind, value);
3245}
3246
3247enum BackupType
3248get_version(const char *version_str)
3249{
3250 int i;
3251
3252 if (version_str == NULL || *version_str == '\0')
3253 return BACKUP_NUMBERED_EXISTING;
3254 i = argmatch(version_str, backup_args);
3255 if (i >= 0)
3256 return backup_types[i];
3257 invalid_arg("version control type", version_str, i);
3258 exit(2);
3259}