12dd076b8SGabor Kovesdan /*- 22dd076b8SGabor Kovesdan * Copyright 1986, Larry Wall 32dd076b8SGabor Kovesdan * 42dd076b8SGabor Kovesdan * Redistribution and use in source and binary forms, with or without 52dd076b8SGabor Kovesdan * modification, are permitted provided that the following condition is met: 62dd076b8SGabor Kovesdan * 1. Redistributions of source code must retain the above copyright notice, 72dd076b8SGabor Kovesdan * this condition and the following disclaimer. 82dd076b8SGabor Kovesdan * 92dd076b8SGabor Kovesdan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY 102dd076b8SGabor Kovesdan * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 112dd076b8SGabor Kovesdan * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 122dd076b8SGabor Kovesdan * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 132dd076b8SGabor Kovesdan * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 142dd076b8SGabor Kovesdan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 152dd076b8SGabor Kovesdan * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 162dd076b8SGabor Kovesdan * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 172dd076b8SGabor Kovesdan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 182dd076b8SGabor Kovesdan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 192dd076b8SGabor Kovesdan * SUCH DAMAGE. 202dd076b8SGabor Kovesdan * 212dd076b8SGabor Kovesdan * patch - a program to apply diffs to original files 222dd076b8SGabor Kovesdan * 232dd076b8SGabor Kovesdan * -C option added in 1998, original code by Marc Espie, based on FreeBSD 242dd076b8SGabor Kovesdan * behaviour 252dd076b8SGabor Kovesdan * 26e56ef7d3SXin LI * $OpenBSD: inp.c,v 1.36 2012/04/10 14:46:34 ajacoutot Exp $ 27e56ef7d3SXin LI * $FreeBSD$ 282dd076b8SGabor Kovesdan */ 292dd076b8SGabor Kovesdan 302dd076b8SGabor Kovesdan #include <sys/types.h> 312dd076b8SGabor Kovesdan #include <sys/file.h> 322dd076b8SGabor Kovesdan #include <sys/stat.h> 332dd076b8SGabor Kovesdan #include <sys/mman.h> 34*1e3e5815SXin LI #include <sys/wait.h> 352dd076b8SGabor Kovesdan 362dd076b8SGabor Kovesdan #include <ctype.h> 37*1e3e5815SXin LI #include <errno.h> 382dd076b8SGabor Kovesdan #include <libgen.h> 392dd076b8SGabor Kovesdan #include <stddef.h> 40df6e4074SPedro F. Giffuni #include <stdint.h> 412dd076b8SGabor Kovesdan #include <stdio.h> 422dd076b8SGabor Kovesdan #include <stdlib.h> 432dd076b8SGabor Kovesdan #include <string.h> 442dd076b8SGabor Kovesdan #include <unistd.h> 452dd076b8SGabor Kovesdan 462dd076b8SGabor Kovesdan #include "common.h" 472dd076b8SGabor Kovesdan #include "util.h" 482dd076b8SGabor Kovesdan #include "pch.h" 492dd076b8SGabor Kovesdan #include "inp.h" 502dd076b8SGabor Kovesdan 512dd076b8SGabor Kovesdan 522dd076b8SGabor Kovesdan /* Input-file-with-indexable-lines abstract type */ 532dd076b8SGabor Kovesdan 542dd076b8SGabor Kovesdan static size_t i_size; /* size of the input file */ 552dd076b8SGabor Kovesdan static char *i_womp; /* plan a buffer for entire file */ 562dd076b8SGabor Kovesdan static char **i_ptr; /* pointers to lines in i_womp */ 572dd076b8SGabor Kovesdan static char empty_line[] = { '\0' }; 582dd076b8SGabor Kovesdan 592dd076b8SGabor Kovesdan static int tifd = -1; /* plan b virtual string array */ 602dd076b8SGabor Kovesdan static char *tibuf[2]; /* plan b buffers */ 612dd076b8SGabor Kovesdan static LINENUM tiline[2] = {-1, -1}; /* 1st line in each buffer */ 62a45060f0SPedro F. Giffuni static size_t lines_per_buf; /* how many lines per buffer */ 63a45060f0SPedro F. Giffuni static size_t tibuflen; /* plan b buffer length */ 64a45060f0SPedro F. Giffuni static size_t tireclen; /* length of records in tmp file */ 652dd076b8SGabor Kovesdan 662dd076b8SGabor Kovesdan static bool rev_in_string(const char *); 672dd076b8SGabor Kovesdan static bool reallocate_lines(size_t *); 682dd076b8SGabor Kovesdan 692dd076b8SGabor Kovesdan /* returns false if insufficient memory */ 702dd076b8SGabor Kovesdan static bool plan_a(const char *); 712dd076b8SGabor Kovesdan 722dd076b8SGabor Kovesdan static void plan_b(const char *); 732dd076b8SGabor Kovesdan 742dd076b8SGabor Kovesdan /* New patch--prepare to edit another file. */ 752dd076b8SGabor Kovesdan 762dd076b8SGabor Kovesdan void 772dd076b8SGabor Kovesdan re_input(void) 782dd076b8SGabor Kovesdan { 792dd076b8SGabor Kovesdan if (using_plan_a) { 802dd076b8SGabor Kovesdan free(i_ptr); 812dd076b8SGabor Kovesdan i_ptr = NULL; 822dd076b8SGabor Kovesdan if (i_womp != NULL) { 832dd076b8SGabor Kovesdan munmap(i_womp, i_size); 842dd076b8SGabor Kovesdan i_womp = NULL; 852dd076b8SGabor Kovesdan } 862dd076b8SGabor Kovesdan i_size = 0; 872dd076b8SGabor Kovesdan } else { 882dd076b8SGabor Kovesdan using_plan_a = true; /* maybe the next one is smaller */ 892dd076b8SGabor Kovesdan close(tifd); 902dd076b8SGabor Kovesdan tifd = -1; 912dd076b8SGabor Kovesdan free(tibuf[0]); 922dd076b8SGabor Kovesdan free(tibuf[1]); 932dd076b8SGabor Kovesdan tibuf[0] = tibuf[1] = NULL; 942dd076b8SGabor Kovesdan tiline[0] = tiline[1] = -1; 952dd076b8SGabor Kovesdan tireclen = 0; 962dd076b8SGabor Kovesdan } 972dd076b8SGabor Kovesdan } 982dd076b8SGabor Kovesdan 992dd076b8SGabor Kovesdan /* Construct the line index, somehow or other. */ 1002dd076b8SGabor Kovesdan 1012dd076b8SGabor Kovesdan void 1022dd076b8SGabor Kovesdan scan_input(const char *filename) 1032dd076b8SGabor Kovesdan { 1042dd076b8SGabor Kovesdan if (!plan_a(filename)) 1052dd076b8SGabor Kovesdan plan_b(filename); 1062dd076b8SGabor Kovesdan if (verbose) { 1072dd076b8SGabor Kovesdan say("Patching file %s using Plan %s...\n", filename, 1082dd076b8SGabor Kovesdan (using_plan_a ? "A" : "B")); 1092dd076b8SGabor Kovesdan } 1102dd076b8SGabor Kovesdan } 1112dd076b8SGabor Kovesdan 1122dd076b8SGabor Kovesdan static bool 1132dd076b8SGabor Kovesdan reallocate_lines(size_t *lines_allocated) 1142dd076b8SGabor Kovesdan { 1152dd076b8SGabor Kovesdan char **p; 1162dd076b8SGabor Kovesdan size_t new_size; 1172dd076b8SGabor Kovesdan 1182dd076b8SGabor Kovesdan new_size = *lines_allocated * 3 / 2; 1192dd076b8SGabor Kovesdan p = realloc(i_ptr, (new_size + 2) * sizeof(char *)); 1202dd076b8SGabor Kovesdan if (p == NULL) { /* shucks, it was a near thing */ 1212dd076b8SGabor Kovesdan munmap(i_womp, i_size); 1222dd076b8SGabor Kovesdan i_womp = NULL; 1232dd076b8SGabor Kovesdan free(i_ptr); 1242dd076b8SGabor Kovesdan i_ptr = NULL; 1252dd076b8SGabor Kovesdan *lines_allocated = 0; 1262dd076b8SGabor Kovesdan return false; 1272dd076b8SGabor Kovesdan } 1282dd076b8SGabor Kovesdan *lines_allocated = new_size; 1292dd076b8SGabor Kovesdan i_ptr = p; 1302dd076b8SGabor Kovesdan return true; 1312dd076b8SGabor Kovesdan } 1322dd076b8SGabor Kovesdan 1332dd076b8SGabor Kovesdan /* Try keeping everything in memory. */ 1342dd076b8SGabor Kovesdan 1352dd076b8SGabor Kovesdan static bool 1362dd076b8SGabor Kovesdan plan_a(const char *filename) 1372dd076b8SGabor Kovesdan { 138*1e3e5815SXin LI int ifd, statfailed, devnull, pstat; 1392dd076b8SGabor Kovesdan char *p, *s, lbuf[INITLINELEN]; 1402dd076b8SGabor Kovesdan struct stat filestat; 1412dd076b8SGabor Kovesdan ptrdiff_t sz; 1422dd076b8SGabor Kovesdan size_t i; 1432dd076b8SGabor Kovesdan size_t iline, lines_allocated; 144*1e3e5815SXin LI pid_t pid; 145*1e3e5815SXin LI char *argp[4] = {NULL}; 1462dd076b8SGabor Kovesdan 1472dd076b8SGabor Kovesdan #ifdef DEBUGGING 1482dd076b8SGabor Kovesdan if (debug & 8) 1492dd076b8SGabor Kovesdan return false; 1502dd076b8SGabor Kovesdan #endif 1512dd076b8SGabor Kovesdan 1522dd076b8SGabor Kovesdan if (filename == NULL || *filename == '\0') 1532dd076b8SGabor Kovesdan return false; 1542dd076b8SGabor Kovesdan 1552dd076b8SGabor Kovesdan statfailed = stat(filename, &filestat); 1562dd076b8SGabor Kovesdan if (statfailed && ok_to_create_file) { 1572dd076b8SGabor Kovesdan if (verbose) 1582dd076b8SGabor Kovesdan say("(Creating file %s...)\n", filename); 1592dd076b8SGabor Kovesdan 1602dd076b8SGabor Kovesdan /* 1612dd076b8SGabor Kovesdan * in check_patch case, we still display `Creating file' even 1622dd076b8SGabor Kovesdan * though we're not. The rule is that -C should be as similar 1632dd076b8SGabor Kovesdan * to normal patch behavior as possible 1642dd076b8SGabor Kovesdan */ 1652dd076b8SGabor Kovesdan if (check_only) 1662dd076b8SGabor Kovesdan return true; 1672dd076b8SGabor Kovesdan makedirs(filename, true); 1682dd076b8SGabor Kovesdan close(creat(filename, 0666)); 1692dd076b8SGabor Kovesdan statfailed = stat(filename, &filestat); 1702dd076b8SGabor Kovesdan } 1712dd076b8SGabor Kovesdan if (statfailed && check_only) 1722dd076b8SGabor Kovesdan fatal("%s not found, -C mode, can't probe further\n", filename); 173*1e3e5815SXin LI /* For nonexistent or read-only files, look for RCS versions. */ 174*1e3e5815SXin LI 1752dd076b8SGabor Kovesdan if (statfailed || 1762dd076b8SGabor Kovesdan /* No one can write to it. */ 1772dd076b8SGabor Kovesdan (filestat.st_mode & 0222) == 0 || 1782dd076b8SGabor Kovesdan /* I can't write to it. */ 1792dd076b8SGabor Kovesdan ((filestat.st_mode & 0022) == 0 && filestat.st_uid != getuid())) { 180*1e3e5815SXin LI char *filebase, *filedir; 1812dd076b8SGabor Kovesdan struct stat cstat; 1822dd076b8SGabor Kovesdan char *tmp_filename1, *tmp_filename2; 1832dd076b8SGabor Kovesdan 1842dd076b8SGabor Kovesdan tmp_filename1 = strdup(filename); 1852dd076b8SGabor Kovesdan tmp_filename2 = strdup(filename); 1862dd076b8SGabor Kovesdan if (tmp_filename1 == NULL || tmp_filename2 == NULL) 1872dd076b8SGabor Kovesdan fatal("strdupping filename"); 188*1e3e5815SXin LI 1892dd076b8SGabor Kovesdan filebase = basename(tmp_filename1); 1902dd076b8SGabor Kovesdan filedir = dirname(tmp_filename2); 1912dd076b8SGabor Kovesdan 1922dd076b8SGabor Kovesdan #define try(f, a1, a2, a3) \ 193*1e3e5815SXin LI (snprintf(lbuf, sizeof(lbuf), f, a1, a2, a3), stat(lbuf, &cstat) == 0) 1942dd076b8SGabor Kovesdan 1952dd076b8SGabor Kovesdan /* 1962dd076b8SGabor Kovesdan * else we can't write to it but it's not under a version 1972dd076b8SGabor Kovesdan * control system, so just proceed. 1982dd076b8SGabor Kovesdan */ 199*1e3e5815SXin LI if (try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX) || 200*1e3e5815SXin LI try("%s/RCS/%s%s", filedir, filebase, "") || 201*1e3e5815SXin LI try("%s/%s%s", filedir, filebase, RCSSUFFIX)) { 2022dd076b8SGabor Kovesdan if (!statfailed) { 2032dd076b8SGabor Kovesdan if ((filestat.st_mode & 0222) != 0) 2042dd076b8SGabor Kovesdan /* The owner can write to it. */ 2052dd076b8SGabor Kovesdan fatal("file %s seems to be locked " 206*1e3e5815SXin LI "by somebody else under RCS\n", 207*1e3e5815SXin LI filename); 2082dd076b8SGabor Kovesdan /* 2092dd076b8SGabor Kovesdan * It might be checked out unlocked. See if 2102dd076b8SGabor Kovesdan * it's safe to check out the default version 2112dd076b8SGabor Kovesdan * locked. 2122dd076b8SGabor Kovesdan */ 2132dd076b8SGabor Kovesdan if (verbose) 2142dd076b8SGabor Kovesdan say("Comparing file %s to default " 215*1e3e5815SXin LI "RCS version...\n", filename); 216*1e3e5815SXin LI 217*1e3e5815SXin LI switch (pid = fork()) { 218*1e3e5815SXin LI case -1: 219*1e3e5815SXin LI fatal("can't fork: %s\n", 220*1e3e5815SXin LI strerror(errno)); 221*1e3e5815SXin LI case 0: 222*1e3e5815SXin LI devnull = open("/dev/null", O_RDONLY); 223*1e3e5815SXin LI if (devnull == -1) { 224*1e3e5815SXin LI fatal("can't open /dev/null: %s", 225*1e3e5815SXin LI strerror(errno)); 226*1e3e5815SXin LI } 227*1e3e5815SXin LI (void)dup2(devnull, STDOUT_FILENO); 228*1e3e5815SXin LI argp[0] = strdup(RCSDIFF); 229*1e3e5815SXin LI argp[1] = strdup(filename); 230*1e3e5815SXin LI execv(RCSDIFF, argp); 231*1e3e5815SXin LI exit(127); 232*1e3e5815SXin LI } 233*1e3e5815SXin LI pid = waitpid(pid, &pstat, 0); 234*1e3e5815SXin LI if (pid == -1 || WEXITSTATUS(pstat) != 0) { 2352dd076b8SGabor Kovesdan fatal("can't check out file %s: " 236*1e3e5815SXin LI "differs from default RCS version\n", 237*1e3e5815SXin LI filename); 2382dd076b8SGabor Kovesdan } 239*1e3e5815SXin LI } 240*1e3e5815SXin LI 2412dd076b8SGabor Kovesdan if (verbose) 242*1e3e5815SXin LI say("Checking out file %s from RCS...\n", 243*1e3e5815SXin LI filename); 244*1e3e5815SXin LI 245*1e3e5815SXin LI switch (pid = fork()) { 246*1e3e5815SXin LI case -1: 247*1e3e5815SXin LI fatal("can't fork: %s\n", strerror(errno)); 248*1e3e5815SXin LI case 0: 249*1e3e5815SXin LI argp[0] = strdup(CHECKOUT); 250*1e3e5815SXin LI argp[1] = strdup("-l"); 251*1e3e5815SXin LI argp[2] = strdup(filename); 252*1e3e5815SXin LI execv(CHECKOUT, argp); 253*1e3e5815SXin LI exit(127); 2542dd076b8SGabor Kovesdan } 255*1e3e5815SXin LI pid = waitpid(pid, &pstat, 0); 256*1e3e5815SXin LI if (pid == -1 || WEXITSTATUS(pstat) != 0 || 257*1e3e5815SXin LI stat(filename, &filestat)) { 258*1e3e5815SXin LI fatal("can't check out file %s from RCS\n", 259*1e3e5815SXin LI filename); 2602dd076b8SGabor Kovesdan } 261*1e3e5815SXin LI } else if (statfailed) { 262*1e3e5815SXin LI fatal("can't find %s\n", filename); 263*1e3e5815SXin LI } 264*1e3e5815SXin LI free(tmp_filename1); 265*1e3e5815SXin LI free(tmp_filename2); 266*1e3e5815SXin LI } 267*1e3e5815SXin LI 2682dd076b8SGabor Kovesdan filemode = filestat.st_mode; 2692dd076b8SGabor Kovesdan if (!S_ISREG(filemode)) 2702dd076b8SGabor Kovesdan fatal("%s is not a normal file--can't patch\n", filename); 2712dd076b8SGabor Kovesdan if ((uint64_t)filestat.st_size > SIZE_MAX) { 2722dd076b8SGabor Kovesdan say("block too large to mmap\n"); 2732dd076b8SGabor Kovesdan return false; 2742dd076b8SGabor Kovesdan } 2752dd076b8SGabor Kovesdan i_size = (size_t)filestat.st_size; 2762dd076b8SGabor Kovesdan if (out_of_mem) { 2772dd076b8SGabor Kovesdan set_hunkmax(); /* make sure dynamic arrays are allocated */ 2782dd076b8SGabor Kovesdan out_of_mem = false; 2792dd076b8SGabor Kovesdan return false; /* force plan b because plan a bombed */ 2802dd076b8SGabor Kovesdan } 2812dd076b8SGabor Kovesdan if ((ifd = open(filename, O_RDONLY)) < 0) 2822dd076b8SGabor Kovesdan pfatal("can't open file %s", filename); 2832dd076b8SGabor Kovesdan 2842dd076b8SGabor Kovesdan if (i_size) { 2852dd076b8SGabor Kovesdan i_womp = mmap(NULL, i_size, PROT_READ, MAP_PRIVATE, ifd, 0); 2862dd076b8SGabor Kovesdan if (i_womp == MAP_FAILED) { 2872dd076b8SGabor Kovesdan perror("mmap failed"); 2882dd076b8SGabor Kovesdan i_womp = NULL; 2892dd076b8SGabor Kovesdan close(ifd); 2902dd076b8SGabor Kovesdan return false; 2912dd076b8SGabor Kovesdan } 2922dd076b8SGabor Kovesdan } else { 2932dd076b8SGabor Kovesdan i_womp = NULL; 2942dd076b8SGabor Kovesdan } 2952dd076b8SGabor Kovesdan 2962dd076b8SGabor Kovesdan close(ifd); 2972dd076b8SGabor Kovesdan if (i_size) 2982dd076b8SGabor Kovesdan madvise(i_womp, i_size, MADV_SEQUENTIAL); 2992dd076b8SGabor Kovesdan 3002dd076b8SGabor Kovesdan /* estimate the number of lines */ 3012dd076b8SGabor Kovesdan lines_allocated = i_size / 25; 3022dd076b8SGabor Kovesdan if (lines_allocated < 100) 3032dd076b8SGabor Kovesdan lines_allocated = 100; 3042dd076b8SGabor Kovesdan 3052dd076b8SGabor Kovesdan if (!reallocate_lines(&lines_allocated)) 3062dd076b8SGabor Kovesdan return false; 3072dd076b8SGabor Kovesdan 3082dd076b8SGabor Kovesdan /* now scan the buffer and build pointer array */ 3092dd076b8SGabor Kovesdan iline = 1; 3102dd076b8SGabor Kovesdan i_ptr[iline] = i_womp; 3112dd076b8SGabor Kovesdan /* test for NUL too, to maintain the behavior of the original code */ 3122dd076b8SGabor Kovesdan for (s = i_womp, i = 0; i < i_size && *s != '\0'; s++, i++) { 3132dd076b8SGabor Kovesdan if (*s == '\n') { 3142dd076b8SGabor Kovesdan if (iline == lines_allocated) { 3152dd076b8SGabor Kovesdan if (!reallocate_lines(&lines_allocated)) 3162dd076b8SGabor Kovesdan return false; 3172dd076b8SGabor Kovesdan } 3182dd076b8SGabor Kovesdan /* these are NOT NUL terminated */ 3192dd076b8SGabor Kovesdan i_ptr[++iline] = s + 1; 3202dd076b8SGabor Kovesdan } 3212dd076b8SGabor Kovesdan } 3222dd076b8SGabor Kovesdan /* if the last line contains no EOL, append one */ 3232dd076b8SGabor Kovesdan if (i_size > 0 && i_womp[i_size - 1] != '\n') { 3242dd076b8SGabor Kovesdan last_line_missing_eol = true; 3252dd076b8SGabor Kovesdan /* fix last line */ 3262dd076b8SGabor Kovesdan sz = s - i_ptr[iline]; 3272dd076b8SGabor Kovesdan p = malloc(sz + 1); 3282dd076b8SGabor Kovesdan if (p == NULL) { 3292dd076b8SGabor Kovesdan free(i_ptr); 3302dd076b8SGabor Kovesdan i_ptr = NULL; 3312dd076b8SGabor Kovesdan munmap(i_womp, i_size); 3322dd076b8SGabor Kovesdan i_womp = NULL; 3332dd076b8SGabor Kovesdan return false; 3342dd076b8SGabor Kovesdan } 3352dd076b8SGabor Kovesdan 3362dd076b8SGabor Kovesdan memcpy(p, i_ptr[iline], sz); 3372dd076b8SGabor Kovesdan p[sz] = '\n'; 3382dd076b8SGabor Kovesdan i_ptr[iline] = p; 3392dd076b8SGabor Kovesdan /* count the extra line and make it point to some valid mem */ 3402dd076b8SGabor Kovesdan i_ptr[++iline] = empty_line; 3412dd076b8SGabor Kovesdan } else 3422dd076b8SGabor Kovesdan last_line_missing_eol = false; 3432dd076b8SGabor Kovesdan 3442dd076b8SGabor Kovesdan input_lines = iline - 1; 3452dd076b8SGabor Kovesdan 3462dd076b8SGabor Kovesdan /* now check for revision, if any */ 3472dd076b8SGabor Kovesdan 3482dd076b8SGabor Kovesdan if (revision != NULL) { 349a45060f0SPedro F. Giffuni if (i_womp == NULL || !rev_in_string(i_womp)) { 3502dd076b8SGabor Kovesdan if (force) { 3512dd076b8SGabor Kovesdan if (verbose) 3522dd076b8SGabor Kovesdan say("Warning: this file doesn't appear " 3532dd076b8SGabor Kovesdan "to be the %s version--patching anyway.\n", 3542dd076b8SGabor Kovesdan revision); 3552dd076b8SGabor Kovesdan } else if (batch) { 3562dd076b8SGabor Kovesdan fatal("this file doesn't appear to be the " 3572dd076b8SGabor Kovesdan "%s version--aborting.\n", 3582dd076b8SGabor Kovesdan revision); 3592dd076b8SGabor Kovesdan } else { 3602dd076b8SGabor Kovesdan ask("This file doesn't appear to be the " 3612dd076b8SGabor Kovesdan "%s version--patch anyway? [n] ", 3622dd076b8SGabor Kovesdan revision); 3632dd076b8SGabor Kovesdan if (*buf != 'y') 3642dd076b8SGabor Kovesdan fatal("aborted\n"); 3652dd076b8SGabor Kovesdan } 3662dd076b8SGabor Kovesdan } else if (verbose) 3672dd076b8SGabor Kovesdan say("Good. This file appears to be the %s version.\n", 3682dd076b8SGabor Kovesdan revision); 3692dd076b8SGabor Kovesdan } 3702dd076b8SGabor Kovesdan return true; /* plan a will work */ 3712dd076b8SGabor Kovesdan } 3722dd076b8SGabor Kovesdan 3732dd076b8SGabor Kovesdan /* Keep (virtually) nothing in memory. */ 3742dd076b8SGabor Kovesdan 3752dd076b8SGabor Kovesdan static void 3762dd076b8SGabor Kovesdan plan_b(const char *filename) 3772dd076b8SGabor Kovesdan { 3782dd076b8SGabor Kovesdan FILE *ifp; 379a45060f0SPedro F. Giffuni size_t i = 0, j, len, maxlen = 1; 380a45060f0SPedro F. Giffuni char *lbuf = NULL, *p; 3812dd076b8SGabor Kovesdan bool found_revision = (revision == NULL); 3822dd076b8SGabor Kovesdan 3832dd076b8SGabor Kovesdan using_plan_a = false; 3842dd076b8SGabor Kovesdan if ((ifp = fopen(filename, "r")) == NULL) 3852dd076b8SGabor Kovesdan pfatal("can't open file %s", filename); 3862dd076b8SGabor Kovesdan unlink(TMPINNAME); 3872dd076b8SGabor Kovesdan if ((tifd = open(TMPINNAME, O_EXCL | O_CREAT | O_WRONLY, 0666)) < 0) 3882dd076b8SGabor Kovesdan pfatal("can't open file %s", TMPINNAME); 389a45060f0SPedro F. Giffuni while ((p = fgetln(ifp, &len)) != NULL) { 390a45060f0SPedro F. Giffuni if (p[len - 1] == '\n') 391a45060f0SPedro F. Giffuni p[len - 1] = '\0'; 392a45060f0SPedro F. Giffuni else { 393a45060f0SPedro F. Giffuni /* EOF without EOL, copy and add the NUL */ 394a45060f0SPedro F. Giffuni if ((lbuf = malloc(len + 1)) == NULL) 395a45060f0SPedro F. Giffuni fatal("out of memory\n"); 396a45060f0SPedro F. Giffuni memcpy(lbuf, p, len); 397a45060f0SPedro F. Giffuni lbuf[len] = '\0'; 398a45060f0SPedro F. Giffuni p = lbuf; 399a45060f0SPedro F. Giffuni 400a45060f0SPedro F. Giffuni last_line_missing_eol = true; 401a45060f0SPedro F. Giffuni len++; 4022dd076b8SGabor Kovesdan } 403a45060f0SPedro F. Giffuni if (revision != NULL && !found_revision && rev_in_string(p)) 404a45060f0SPedro F. Giffuni found_revision = true; 405a45060f0SPedro F. Giffuni if (len > maxlen) 406a45060f0SPedro F. Giffuni maxlen = len; /* find longest line */ 407a45060f0SPedro F. Giffuni } 408a45060f0SPedro F. Giffuni free(lbuf); 409a45060f0SPedro F. Giffuni if (ferror(ifp)) 410a45060f0SPedro F. Giffuni pfatal("can't read file %s", filename); 4112dd076b8SGabor Kovesdan 4122dd076b8SGabor Kovesdan if (revision != NULL) { 4132dd076b8SGabor Kovesdan if (!found_revision) { 4142dd076b8SGabor Kovesdan if (force) { 4152dd076b8SGabor Kovesdan if (verbose) 4162dd076b8SGabor Kovesdan say("Warning: this file doesn't appear " 4172dd076b8SGabor Kovesdan "to be the %s version--patching anyway.\n", 4182dd076b8SGabor Kovesdan revision); 4192dd076b8SGabor Kovesdan } else if (batch) { 4202dd076b8SGabor Kovesdan fatal("this file doesn't appear to be the " 4212dd076b8SGabor Kovesdan "%s version--aborting.\n", 4222dd076b8SGabor Kovesdan revision); 4232dd076b8SGabor Kovesdan } else { 4242dd076b8SGabor Kovesdan ask("This file doesn't appear to be the %s " 4252dd076b8SGabor Kovesdan "version--patch anyway? [n] ", 4262dd076b8SGabor Kovesdan revision); 4272dd076b8SGabor Kovesdan if (*buf != 'y') 4282dd076b8SGabor Kovesdan fatal("aborted\n"); 4292dd076b8SGabor Kovesdan } 4302dd076b8SGabor Kovesdan } else if (verbose) 4312dd076b8SGabor Kovesdan say("Good. This file appears to be the %s version.\n", 4322dd076b8SGabor Kovesdan revision); 4332dd076b8SGabor Kovesdan } 4342dd076b8SGabor Kovesdan fseek(ifp, 0L, SEEK_SET); /* rewind file */ 4352dd076b8SGabor Kovesdan tireclen = maxlen; 436a45060f0SPedro F. Giffuni tibuflen = maxlen > BUFFERSIZE ? maxlen : BUFFERSIZE; 437a45060f0SPedro F. Giffuni lines_per_buf = tibuflen / maxlen; 438a45060f0SPedro F. Giffuni tibuf[0] = malloc(tibuflen + 1); 4392dd076b8SGabor Kovesdan if (tibuf[0] == NULL) 4402dd076b8SGabor Kovesdan fatal("out of memory\n"); 441a45060f0SPedro F. Giffuni tibuf[1] = malloc(tibuflen + 1); 4422dd076b8SGabor Kovesdan if (tibuf[1] == NULL) 4432dd076b8SGabor Kovesdan fatal("out of memory\n"); 4442dd076b8SGabor Kovesdan for (i = 1;; i++) { 4452dd076b8SGabor Kovesdan p = tibuf[0] + maxlen * (i % lines_per_buf); 4462dd076b8SGabor Kovesdan if (i % lines_per_buf == 0) /* new block */ 447a45060f0SPedro F. Giffuni if (write(tifd, tibuf[0], tibuflen) != 448a45060f0SPedro F. Giffuni (ssize_t) tibuflen) 4492dd076b8SGabor Kovesdan pfatal("can't write temp file"); 4502dd076b8SGabor Kovesdan if (fgets(p, maxlen + 1, ifp) == NULL) { 4512dd076b8SGabor Kovesdan input_lines = i - 1; 4522dd076b8SGabor Kovesdan if (i % lines_per_buf != 0) 453a45060f0SPedro F. Giffuni if (write(tifd, tibuf[0], tibuflen) != 454a45060f0SPedro F. Giffuni (ssize_t) tibuflen) 4552dd076b8SGabor Kovesdan pfatal("can't write temp file"); 4562dd076b8SGabor Kovesdan break; 4572dd076b8SGabor Kovesdan } 4582dd076b8SGabor Kovesdan j = strlen(p); 4592dd076b8SGabor Kovesdan /* These are '\n' terminated strings, so no need to add a NUL */ 4602dd076b8SGabor Kovesdan if (j == 0 || p[j - 1] != '\n') 4612dd076b8SGabor Kovesdan p[j] = '\n'; 4622dd076b8SGabor Kovesdan } 4632dd076b8SGabor Kovesdan fclose(ifp); 4642dd076b8SGabor Kovesdan close(tifd); 4652dd076b8SGabor Kovesdan if ((tifd = open(TMPINNAME, O_RDONLY)) < 0) 4662dd076b8SGabor Kovesdan pfatal("can't reopen file %s", TMPINNAME); 4672dd076b8SGabor Kovesdan } 4682dd076b8SGabor Kovesdan 4692dd076b8SGabor Kovesdan /* 4702dd076b8SGabor Kovesdan * Fetch a line from the input file, \n terminated, not necessarily \0. 4712dd076b8SGabor Kovesdan */ 4722dd076b8SGabor Kovesdan char * 4732dd076b8SGabor Kovesdan ifetch(LINENUM line, int whichbuf) 4742dd076b8SGabor Kovesdan { 4752dd076b8SGabor Kovesdan if (line < 1 || line > input_lines) { 4762dd076b8SGabor Kovesdan if (warn_on_invalid_line) { 4772dd076b8SGabor Kovesdan say("No such line %ld in input file, ignoring\n", line); 4782dd076b8SGabor Kovesdan warn_on_invalid_line = false; 4792dd076b8SGabor Kovesdan } 4802dd076b8SGabor Kovesdan return NULL; 4812dd076b8SGabor Kovesdan } 4822dd076b8SGabor Kovesdan if (using_plan_a) 4832dd076b8SGabor Kovesdan return i_ptr[line]; 4842dd076b8SGabor Kovesdan else { 4852dd076b8SGabor Kovesdan LINENUM offline = line % lines_per_buf; 4862dd076b8SGabor Kovesdan LINENUM baseline = line - offline; 4872dd076b8SGabor Kovesdan 4882dd076b8SGabor Kovesdan if (tiline[0] == baseline) 4892dd076b8SGabor Kovesdan whichbuf = 0; 4902dd076b8SGabor Kovesdan else if (tiline[1] == baseline) 4912dd076b8SGabor Kovesdan whichbuf = 1; 4922dd076b8SGabor Kovesdan else { 4932dd076b8SGabor Kovesdan tiline[whichbuf] = baseline; 4942dd076b8SGabor Kovesdan 4952dd076b8SGabor Kovesdan if (lseek(tifd, (off_t) (baseline / lines_per_buf * 496a45060f0SPedro F. Giffuni tibuflen), SEEK_SET) < 0) 4972dd076b8SGabor Kovesdan pfatal("cannot seek in the temporary input file"); 4982dd076b8SGabor Kovesdan 499a45060f0SPedro F. Giffuni if (read(tifd, tibuf[whichbuf], tibuflen) != 500a45060f0SPedro F. Giffuni (ssize_t) tibuflen) 5012dd076b8SGabor Kovesdan pfatal("error reading tmp file %s", TMPINNAME); 5022dd076b8SGabor Kovesdan } 5032dd076b8SGabor Kovesdan return tibuf[whichbuf] + (tireclen * offline); 5042dd076b8SGabor Kovesdan } 5052dd076b8SGabor Kovesdan } 5062dd076b8SGabor Kovesdan 5072dd076b8SGabor Kovesdan /* 5082dd076b8SGabor Kovesdan * True if the string argument contains the revision number we want. 5092dd076b8SGabor Kovesdan */ 5102dd076b8SGabor Kovesdan static bool 5112dd076b8SGabor Kovesdan rev_in_string(const char *string) 5122dd076b8SGabor Kovesdan { 5132dd076b8SGabor Kovesdan const char *s; 5142dd076b8SGabor Kovesdan size_t patlen; 5152dd076b8SGabor Kovesdan 5162dd076b8SGabor Kovesdan if (revision == NULL) 5172dd076b8SGabor Kovesdan return true; 5182dd076b8SGabor Kovesdan patlen = strlen(revision); 5192dd076b8SGabor Kovesdan if (strnEQ(string, revision, patlen) && isspace((unsigned char)string[patlen])) 5202dd076b8SGabor Kovesdan return true; 5212dd076b8SGabor Kovesdan for (s = string; *s; s++) { 5222dd076b8SGabor Kovesdan if (isspace((unsigned char)*s) && strnEQ(s + 1, revision, patlen) && 5232dd076b8SGabor Kovesdan isspace((unsigned char)s[patlen + 1])) { 5242dd076b8SGabor Kovesdan return true; 5252dd076b8SGabor Kovesdan } 5262dd076b8SGabor Kovesdan } 5272dd076b8SGabor Kovesdan return false; 5282dd076b8SGabor Kovesdan } 529