pch.c revision 276807
1139749Simp 2113584Ssimokawa/*- 3103285Sikob * Copyright 1986, Larry Wall 4103285Sikob * 5103285Sikob * Redistribution and use in source and binary forms, with or without 6103285Sikob * modification, are permitted provided that the following condition is met: 7103285Sikob * 1. Redistributions of source code must retain the above copyright notice, 8103285Sikob * this condition and the following disclaimer. 9103285Sikob * 10103285Sikob * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY 11103285Sikob * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 12103285Sikob * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 13103285Sikob * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 14103285Sikob * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 15103285Sikob * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 16103285Sikob * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 17106802Ssimokawa * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 18103285Sikob * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 19103285Sikob * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 20103285Sikob * SUCH DAMAGE. 21103285Sikob * 22103285Sikob * patch - a program to apply diffs to original files 23103285Sikob * 24103285Sikob * -C option added in 1998, original code by Marc Espie, based on FreeBSD 25103285Sikob * behaviour 26103285Sikob * 27103285Sikob * $OpenBSD: pch.c,v 1.43 2014/11/18 17:03:35 tobias Exp $ 28103285Sikob * $FreeBSD: stable/10/usr.bin/patch/pch.c 276807 2015-01-08 03:44:54Z pfg $ 29103285Sikob */ 30103285Sikob 31103285Sikob#include <sys/types.h> 32103285Sikob#include <sys/stat.h> 33103285Sikob 34103285Sikob#include <ctype.h> 35103285Sikob#include <libgen.h> 36103285Sikob#include <limits.h> 37106802Ssimokawa#include <stdio.h> 38103285Sikob#include <stdlib.h> 39103285Sikob#include <string.h> 40103285Sikob#include <unistd.h> 41103285Sikob 42103285Sikob#include "common.h" 43103285Sikob#include "util.h" 44103285Sikob#include "pch.h" 45103285Sikob#include "pathnames.h" 46103285Sikob 47103285Sikob/* Patch (diff listing) abstract type. */ 48103285Sikob 49103285Sikobstatic off_t p_filesize; /* size of the patch file */ 50169123Ssimokawastatic LINENUM p_first; /* 1st line number */ 51103285Sikobstatic LINENUM p_newfirst; /* 1st line number of replacement */ 52103285Sikobstatic LINENUM p_ptrn_lines; /* # lines in pattern */ 53103285Sikobstatic LINENUM p_repl_lines; /* # lines in replacement text */ 54113584Ssimokawastatic LINENUM p_end = -1; /* last line in hunk */ 55170374Ssimokawastatic LINENUM p_max; /* max allowed value of p_end */ 56103285Sikobstatic LINENUM p_context = 3; /* # of context lines */ 57103285Sikobstatic LINENUM p_input_line = 0; /* current line # from patch file */ 58103285Sikobstatic char **p_line = NULL;/* the text of the hunk */ 59127468Ssimokawastatic unsigned short *p_len = NULL; /* length of each line */ 60117067Ssimokawastatic char *p_char = NULL; /* +, -, and ! */ 61117067Ssimokawastatic int hunkmax = INITHUNKMAX; /* size of above arrays to begin with */ 62117067Ssimokawastatic int p_indent; /* indent to patch */ 63127468Ssimokawastatic off_t p_base; /* where to intuit this time */ 64127468Ssimokawastatic LINENUM p_bline; /* line # of p_base */ 65127468Ssimokawastatic off_t p_start; /* where intuit found a patch */ 66127468Ssimokawastatic LINENUM p_sline; /* and the line number for it */ 67127468Ssimokawastatic LINENUM p_hunk_beg; /* line number of current hunk */ 68127468Ssimokawastatic LINENUM p_efake = -1; /* end of faked up lines--don't free */ 69127468Ssimokawastatic LINENUM p_bfake = -1; /* beg of faked up lines */ 70127468Ssimokawastatic FILE *pfp = NULL; /* patch file pointer */ 71103285Sikobstatic char *bestguess = NULL; /* guess at correct filename */ 72103285Sikob 73113584Ssimokawastatic void grow_hunkmax(void); 74103285Sikobstatic int intuit_diff_type(void); 75103285Sikobstatic void next_intuit_at(off_t, LINENUM); 76103285Sikobstatic void skip_to(off_t, LINENUM); 77127468Ssimokawastatic size_t pgets(bool _do_indent); 78103285Sikobstatic char *best_name(const struct file_name *, bool); 79103285Sikobstatic char *posix_name(const struct file_name *, bool); 80106802Ssimokawastatic size_t num_components(const char *); 81169123Ssimokawastatic LINENUM strtolinenum(char *, char **); 82170400Ssimokawa 83169123Ssimokawa/* 84169123Ssimokawa * Prepare to look for the next patch in the patch file. 85169123Ssimokawa */ 86170400Ssimokawavoid 87170400Ssimokaware_patch(void) 88170400Ssimokawa{ 89169123Ssimokawa p_first = 0; 90103285Sikob p_newfirst = 0; 91103285Sikob p_ptrn_lines = 0; 92113584Ssimokawa p_repl_lines = 0; 93103285Sikob p_end = (LINENUM) - 1; 94103285Sikob p_max = 0; 95113584Ssimokawa p_indent = 0; 96103285Sikob} 97103285Sikob 98170374Ssimokawa/* 99103285Sikob * Open the patch file at the beginning of time. 100103285Sikob */ 101103285Sikobvoid 102103285Sikobopen_patch_file(const char *filename) 103103285Sikob{ 104103285Sikob struct stat filestat; 105113584Ssimokawa int nr, nw; 106116376Ssimokawa 107124378Ssimokawa if (filename == NULL || *filename == '\0' || strEQ(filename, "-")) { 108129585Sdfr pfp = fopen(TMPPATNAME, "w"); 109103285Sikob if (pfp == NULL) 110103285Sikob pfatal("can't create %s", TMPPATNAME); 111170374Ssimokawa while ((nr = fread(buf, 1, buf_size, stdin)) > 0) { 112170374Ssimokawa nw = fwrite(buf, 1, nr, pfp); 113170374Ssimokawa if (nr != nw) 114170374Ssimokawa pfatal("write error to %s", TMPPATNAME); 115170374Ssimokawa } 116170374Ssimokawa if (ferror(pfp) || fclose(pfp)) 117170374Ssimokawa pfatal("can't write %s", TMPPATNAME); 118170374Ssimokawa filename = TMPPATNAME; 119170374Ssimokawa } 120170374Ssimokawa pfp = fopen(filename, "r"); 121170374Ssimokawa if (pfp == NULL) 122170374Ssimokawa pfatal("patch file %s not found", filename); 123170374Ssimokawa if (fstat(fileno(pfp), &filestat)) 124170374Ssimokawa pfatal("can't stat %s", filename); 125170374Ssimokawa p_filesize = filestat.st_size; 126170374Ssimokawa next_intuit_at(0, 1L); /* start at the beginning */ 127170374Ssimokawa set_hunkmax(); 128103285Sikob} 129103285Sikob 130103285Sikob/* 131103285Sikob * Make sure our dynamically realloced tables are malloced to begin with. 132103285Sikob */ 133103285Sikobvoid 134103285Sikobset_hunkmax(void) 135103285Sikob{ 136124169Ssimokawa if (p_line == NULL) 137124169Ssimokawa p_line = malloc(hunkmax * sizeof(char *)); 138124169Ssimokawa if (p_len == NULL) 139124169Ssimokawa p_len = malloc(hunkmax * sizeof(unsigned short)); 140124169Ssimokawa if (p_char == NULL) 141124169Ssimokawa p_char = malloc(hunkmax * sizeof(char)); 142124169Ssimokawa} 143124169Ssimokawa 144129585Sdfr/* 145129585Sdfr * Enlarge the arrays containing the current hunk of patch. 146124169Ssimokawa */ 147124169Ssimokawastatic void 148124169Ssimokawagrow_hunkmax(void) 149124169Ssimokawa{ 150113584Ssimokawa int new_hunkmax = hunkmax * 2; 151129585Sdfr 152113584Ssimokawa if (p_line == NULL || p_len == NULL || p_char == NULL) 153124169Ssimokawa fatal("Internal memory allocation error\n"); 154124169Ssimokawa 155124169Ssimokawa p_line = reallocf(p_line, new_hunkmax * sizeof(char *)); 156124169Ssimokawa p_len = reallocf(p_len, new_hunkmax * sizeof(unsigned short)); 157113584Ssimokawa p_char = reallocf(p_char, new_hunkmax * sizeof(char)); 158124169Ssimokawa 159124169Ssimokawa if (p_line != NULL && p_len != NULL && p_char != NULL) { 160129585Sdfr hunkmax = new_hunkmax; 161129585Sdfr return; 162129585Sdfr } 163129585Sdfr 164124169Ssimokawa if (!using_plan_a) 165124169Ssimokawa fatal("out of memory\n"); 166124169Ssimokawa out_of_mem = true; /* whatever is null will be allocated again */ 167170374Ssimokawa /* from within plan_a(), of all places */ 168170374Ssimokawa} 169170374Ssimokawa 170103285Sikob/* True if the remainder of the patch file contains a diff of some sort. */ 171103285Sikob 172103285Sikobbool 173103285Sikobthere_is_another_patch(void) 174103285Sikob{ 175103285Sikob bool exists = false; 176103285Sikob 177103285Sikob if (p_base != 0 && p_base >= p_filesize) { 178103285Sikob if (verbose) 179112523Ssimokawa say("done\n"); 180103285Sikob return false; 181103285Sikob } 182103285Sikob if (verbose) 183103285Sikob say("Hmm..."); 184103285Sikob diff_type = intuit_diff_type(); 185103285Sikob if (!diff_type) { 186103285Sikob if (p_base != 0) { 187103285Sikob if (verbose) 188103285Sikob say(" Ignoring the trailing garbage.\ndone\n"); 189103285Sikob } else 190103285Sikob say(" I can't seem to find a patch in there anywhere.\n"); 191103285Sikob return false; 192103285Sikob } 193103285Sikob if (verbose) 194103285Sikob say(" %sooks like %s to me...\n", 195103285Sikob (p_base == 0 ? "L" : "The next patch l"), 196103285Sikob diff_type == UNI_DIFF ? "a unified diff" : 197103285Sikob diff_type == CONTEXT_DIFF ? "a context diff" : 198103285Sikob diff_type == NEW_CONTEXT_DIFF ? "a new-style context diff" : 199103285Sikob diff_type == NORMAL_DIFF ? "a normal diff" : 200103285Sikob "an ed script"); 201103285Sikob if (p_indent && verbose) 202103285Sikob say("(Patch is indented %d space%s.)\n", p_indent, 203103285Sikob p_indent == 1 ? "" : "s"); 204103285Sikob skip_to(p_start, p_sline); 205103285Sikob while (filearg[0] == NULL) { 206103285Sikob if (force || batch) { 207103285Sikob say("No file to patch. Skipping...\n"); 208113584Ssimokawa filearg[0] = xstrdup(bestguess); 209103285Sikob skip_rest_of_patch = true; 210103285Sikob return true; 211103285Sikob } 212103285Sikob ask("File to patch: "); 213103285Sikob if (*buf != '\n') { 214103285Sikob free(bestguess); 215103285Sikob bestguess = xstrdup(buf); 216103285Sikob filearg[0] = fetchname(buf, &exists, 0); 217103285Sikob } 218103285Sikob if (!exists) { 219103285Sikob ask("No file found--skip this patch? [n] "); 220103285Sikob if (*buf != 'y') 221103285Sikob continue; 222103285Sikob if (verbose) 223103285Sikob say("Skipping patch...\n"); 224103285Sikob free(filearg[0]); 225103285Sikob filearg[0] = fetchname(bestguess, &exists, 0); 226103285Sikob skip_rest_of_patch = true; 227103285Sikob return true; 228103285Sikob } 229103285Sikob } 230103285Sikob return true; 231103285Sikob} 232103285Sikob 233103285Sikobstatic void 234103285Sikobp4_fetchname(struct file_name *name, char *str) 235103285Sikob{ 236103285Sikob char *t, *h; 237103285Sikob 238103285Sikob /* Skip leading whitespace. */ 239103285Sikob while (isspace((unsigned char)*str)) 240103285Sikob str++; 241103285Sikob 242103285Sikob /* Remove the file revision number. */ 243103285Sikob for (t = str, h = NULL; *t != '\0' && !isspace((unsigned char)*t); t++) 244103285Sikob if (*t == '#') 245103285Sikob h = t; 246103285Sikob if (h != NULL) 247103285Sikob *h = '\0'; 248103285Sikob 249103285Sikob name->path = fetchname(str, &name->exists, strippath); 250103285Sikob} 251103285Sikob 252103285Sikob/* Determine what kind of diff is in the remaining part of the patch file. */ 253103285Sikob 254103285Sikobstatic int 255103285Sikobintuit_diff_type(void) 256103285Sikob{ 257103285Sikob off_t this_line = 0, previous_line; 258103285Sikob off_t first_command_line = -1; 259103285Sikob LINENUM fcl_line = -1; 260103285Sikob bool last_line_was_command = false, this_is_a_command = false; 261103285Sikob bool stars_last_line = false, stars_this_line = false; 262103285Sikob char *s, *t; 263103285Sikob int indent, retval; 264103285Sikob struct file_name names[MAX_FILE]; 265103285Sikob 266103285Sikob memset(names, 0, sizeof(names)); 267103285Sikob ok_to_create_file = false; 268103285Sikob fseeko(pfp, p_base, SEEK_SET); 269103285Sikob p_input_line = p_bline - 1; 270103285Sikob for (;;) { 271103285Sikob previous_line = this_line; 272170374Ssimokawa last_line_was_command = this_is_a_command; 273129585Sdfr stars_last_line = stars_this_line; 274129585Sdfr this_line = ftello(pfp); 275103285Sikob indent = 0; 276129585Sdfr p_input_line++; 277103285Sikob if (pgets(false) == 0) { 278103285Sikob if (first_command_line >= 0) { 279103285Sikob /* nothing but deletes!? */ 280103285Sikob p_start = first_command_line; 281103285Sikob p_sline = fcl_line; 282103285Sikob retval = ED_DIFF; 283103285Sikob goto scan_exit; 284103285Sikob } else { 285103285Sikob p_start = this_line; 286103285Sikob p_sline = p_input_line; 287103285Sikob retval = 0; 288129585Sdfr goto scan_exit; 289103285Sikob } 290103285Sikob } 291103285Sikob for (s = buf; *s == ' ' || *s == '\t' || *s == 'X'; s++) { 292103285Sikob if (*s == '\t') 293129585Sdfr indent += 8 - (indent % 8); 294103285Sikob else 295103285Sikob indent++; 296103285Sikob } 297103285Sikob for (t = s; isdigit((unsigned char)*t) || *t == ','; t++) 298103285Sikob ; 299103285Sikob this_is_a_command = (isdigit((unsigned char)*s) && 300103285Sikob (*t == 'd' || *t == 'c' || *t == 'a')); 301103285Sikob if (first_command_line < 0 && this_is_a_command) { 302103285Sikob first_command_line = this_line; 303103285Sikob fcl_line = p_input_line; 304109280Ssimokawa p_indent = indent; /* assume this for now */ 305103285Sikob } 306107653Ssimokawa if (!stars_last_line && strnEQ(s, "*** ", 4)) 307103285Sikob names[OLD_FILE].path = fetchname(s + 4, 308132432Ssimokawa &names[OLD_FILE].exists, strippath); 309188509Ssbruno else if (strnEQ(s, "--- ", 4)) 310188509Ssbruno names[NEW_FILE].path = fetchname(s + 4, 311103285Sikob &names[NEW_FILE].exists, strippath); 312103285Sikob else if (strnEQ(s, "+++ ", 4)) 313103285Sikob /* pretend it is the old name */ 314103285Sikob names[OLD_FILE].path = fetchname(s + 4, 315129585Sdfr &names[OLD_FILE].exists, strippath); 316106790Ssimokawa else if (strnEQ(s, "Index:", 6)) 317103285Sikob names[INDEX_FILE].path = fetchname(s + 6, 318129585Sdfr &names[INDEX_FILE].exists, strippath); 319108500Ssimokawa else if (strnEQ(s, "Prereq:", 7)) { 320103285Sikob for (t = s + 7; isspace((unsigned char)*t); t++) 321103285Sikob ; 322108500Ssimokawa revision = xstrdup(t); 323108500Ssimokawa for (t = revision; 324108500Ssimokawa *t && !isspace((unsigned char)*t); t++) 325103285Sikob ; 326103285Sikob *t = '\0'; 327108500Ssimokawa if (*revision == '\0') { 328103285Sikob free(revision); 329103285Sikob revision = NULL; 330103285Sikob } 331109280Ssimokawa } else if (strnEQ(s, "==== ", 5)) { 332103285Sikob /* Perforce-style diffs. */ 333108500Ssimokawa if ((t = strstr(s + 5, " - ")) != NULL) 334132432Ssimokawa p4_fetchname(&names[NEW_FILE], t + 3); 335188509Ssbruno p4_fetchname(&names[OLD_FILE], s + 5); 336108527Ssimokawa } 337109280Ssimokawa if ((!diff_type || diff_type == ED_DIFF) && 338108527Ssimokawa first_command_line >= 0 && 339108527Ssimokawa strEQ(s, ".\n")) { 340108500Ssimokawa p_indent = indent; 341108500Ssimokawa p_start = first_command_line; 342108500Ssimokawa p_sline = fcl_line; 343108500Ssimokawa retval = ED_DIFF; 344108500Ssimokawa goto scan_exit; 345132432Ssimokawa } 346188509Ssbruno if ((!diff_type || diff_type == UNI_DIFF) && strnEQ(s, "@@ -", 4)) { 347108500Ssimokawa if (strnEQ(s + 4, "0,0", 3)) 348109280Ssimokawa ok_to_create_file = true; 349108500Ssimokawa p_indent = indent; 350108500Ssimokawa p_start = this_line; 351108500Ssimokawa p_sline = p_input_line; 352188509Ssbruno retval = UNI_DIFF; 353108500Ssimokawa goto scan_exit; 354188509Ssbruno } 355188509Ssbruno stars_this_line = strnEQ(s, "********", 8); 356108500Ssimokawa if ((!diff_type || diff_type == CONTEXT_DIFF) && stars_last_line && 357103285Sikob strnEQ(s, "*** ", 4)) { 358103285Sikob if (strtolinenum(s + 4, &s) == 0) 359103285Sikob ok_to_create_file = true; 360103285Sikob /* 361130585Sphk * If this is a new context diff the character just 362103285Sikob * at the end of the line is a '*'. 363103285Sikob */ 364103285Sikob while (*s && *s != '\n') 365103285Sikob s++; 366103285Sikob p_indent = indent; 367103285Sikob p_start = previous_line; 368129585Sdfr p_sline = p_input_line - 1; 369103285Sikob retval = (*(s - 1) == '*' ? NEW_CONTEXT_DIFF : CONTEXT_DIFF); 370103285Sikob goto scan_exit; 371103285Sikob } 372103285Sikob if ((!diff_type || diff_type == NORMAL_DIFF) && 373103285Sikob last_line_was_command && 374103285Sikob (strnEQ(s, "< ", 2) || strnEQ(s, "> ", 2))) { 375103285Sikob p_start = previous_line; 376103285Sikob p_sline = p_input_line - 1; 377103285Sikob p_indent = indent; 378103285Sikob retval = NORMAL_DIFF; 379103285Sikob goto scan_exit; 380103285Sikob } 381103285Sikob } 382103285Sikobscan_exit: 383103285Sikob if (retval == UNI_DIFF) { 384103285Sikob /* unswap old and new */ 385103285Sikob struct file_name tmp = names[OLD_FILE]; 386103285Sikob names[OLD_FILE] = names[NEW_FILE]; 387103285Sikob names[NEW_FILE] = tmp; 388103285Sikob } 389103285Sikob if (filearg[0] == NULL) { 390103285Sikob if (posix) 391103285Sikob filearg[0] = posix_name(names, ok_to_create_file); 392103285Sikob else { 393103285Sikob /* Ignore the Index: name for context diffs, like GNU */ 394103285Sikob if (names[OLD_FILE].path != NULL || 395103285Sikob names[NEW_FILE].path != NULL) { 396103285Sikob free(names[INDEX_FILE].path); 397103285Sikob names[INDEX_FILE].path = NULL; 398103285Sikob } 399103285Sikob filearg[0] = best_name(names, ok_to_create_file); 400103285Sikob } 401103285Sikob } 402103285Sikob 403103285Sikob free(bestguess); 404103285Sikob bestguess = NULL; 405119118Ssimokawa if (filearg[0] != NULL) 406119118Ssimokawa bestguess = xstrdup(filearg[0]); 407119118Ssimokawa else if (!ok_to_create_file) { 408119118Ssimokawa /* 409119118Ssimokawa * We don't want to create a new file but we need a 410119118Ssimokawa * filename to set bestguess. Avoid setting filearg[0] 411119118Ssimokawa * so the file is not created automatically. 412119118Ssimokawa */ 413119118Ssimokawa if (posix) 414119118Ssimokawa bestguess = posix_name(names, true); 415119118Ssimokawa else 416119118Ssimokawa bestguess = best_name(names, true); 417119118Ssimokawa } 418119118Ssimokawa free(names[OLD_FILE].path); 419103285Sikob free(names[NEW_FILE].path); 420119118Ssimokawa free(names[INDEX_FILE].path); 421103285Sikob return retval; 422103285Sikob} 423103285Sikob 424103285Sikob/* 425106790Ssimokawa * Remember where this patch ends so we know where to start up again. 426108530Ssimokawa */ 427108530Ssimokawastatic void 428103285Sikobnext_intuit_at(off_t file_pos, LINENUM file_line) 429129585Sdfr{ 430108530Ssimokawa p_base = file_pos; 431108530Ssimokawa p_bline = file_line; 432108530Ssimokawa} 433108530Ssimokawa 434108530Ssimokawa/* 435108530Ssimokawa * Basically a verbose fseeko() to the actual diff listing. 436108530Ssimokawa */ 437108530Ssimokawastatic void 438108530Ssimokawaskip_to(off_t file_pos, LINENUM file_line) 439167685Ssimokawa{ 440167685Ssimokawa size_t len; 441108530Ssimokawa 442108530Ssimokawa if (p_base > file_pos) 443108530Ssimokawa fatal("Internal error: seek %lld>%lld\n", 444108530Ssimokawa (long long)p_base, (long long)file_pos); 445108530Ssimokawa if (verbose && p_base < file_pos) { 446108530Ssimokawa fseeko(pfp, p_base, SEEK_SET); 447108530Ssimokawa say("The text leading up to this was:\n--------------------------\n"); 448108530Ssimokawa while (ftello(pfp) < file_pos) { 449108530Ssimokawa len = pgets(false); 450108530Ssimokawa if (len == 0) 451108530Ssimokawa fatal("Unexpected end of file\n"); 452108530Ssimokawa say("|%s", buf); 453108701Ssimokawa } 454108701Ssimokawa say("--------------------------\n"); 455108530Ssimokawa } else 456108530Ssimokawa fseeko(pfp, file_pos, SEEK_SET); 457108530Ssimokawa p_input_line = file_line - 1; 458108530Ssimokawa} 459108530Ssimokawa 460108530Ssimokawa/* Make this a function for better debugging. */ 461108530Ssimokawastatic void 462108530Ssimokawamalformed(void) 463108530Ssimokawa{ 464108530Ssimokawa fatal("malformed patch at line %ld: %s", p_input_line, buf); 465108530Ssimokawa /* about as informative as "Syntax error" in C */ 466108701Ssimokawa} 467108701Ssimokawa 468108530Ssimokawa/* 469108530Ssimokawa * True if the line has been discarded (i.e. it is a line saying 470108530Ssimokawa * "\ No newline at end of file".) 471108530Ssimokawa */ 472108530Ssimokawastatic bool 473108530Ssimokawaremove_special_line(void) 474108530Ssimokawa{ 475108530Ssimokawa int c; 476132432Ssimokawa 477108530Ssimokawa c = fgetc(pfp); 478108530Ssimokawa if (c == '\\') { 479108530Ssimokawa do { 480108530Ssimokawa c = fgetc(pfp); 481108530Ssimokawa } while (c != EOF && c != '\n'); 482108530Ssimokawa 483108530Ssimokawa return true; 484108530Ssimokawa } 485108530Ssimokawa if (c != EOF) 486108530Ssimokawa fseeko(pfp, -1, SEEK_CUR); 487108530Ssimokawa 488108530Ssimokawa return false; 489108530Ssimokawa} 490108530Ssimokawa 491108530Ssimokawa/* 492108530Ssimokawa * True if there is more of the current diff listing to process. 493108530Ssimokawa */ 494108530Ssimokawabool 495108530Ssimokawaanother_hunk(void) 496108530Ssimokawa{ 497108530Ssimokawa off_t line_beginning; /* file pos of the current line */ 498108530Ssimokawa LINENUM repl_beginning; /* index of --- line */ 499108530Ssimokawa LINENUM fillcnt; /* #lines of missing ptrn or repl */ 500108530Ssimokawa LINENUM fillsrc; /* index of first line to copy */ 501108530Ssimokawa LINENUM filldst; /* index of first missing line */ 502108530Ssimokawa bool ptrn_spaces_eaten; /* ptrn was slightly misformed */ 503108530Ssimokawa bool repl_could_be_missing; /* no + or ! lines in this hunk */ 504108530Ssimokawa bool repl_missing; /* we are now backtracking */ 505108701Ssimokawa off_t repl_backtrack_position; /* file pos of first repl line */ 506129585Sdfr LINENUM repl_patch_line; /* input line number for same */ 507103285Sikob LINENUM ptrn_copiable; /* # of copiable lines in ptrn */ 508103285Sikob char *s; 509129541Sdfr size_t len; 510108530Ssimokawa int context = 0; 511108530Ssimokawa 512129541Sdfr while (p_end >= 0) { 513108530Ssimokawa if (p_end == p_efake) 514108530Ssimokawa p_end = p_bfake; /* don't free twice */ 515108530Ssimokawa else 516108530Ssimokawa free(p_line[p_end]); 517108530Ssimokawa p_end--; 518108530Ssimokawa } 519108530Ssimokawa p_efake = -1; 520108530Ssimokawa 521108530Ssimokawa p_max = hunkmax; /* gets reduced when --- found */ 522108530Ssimokawa if (diff_type == CONTEXT_DIFF || diff_type == NEW_CONTEXT_DIFF) { 523108530Ssimokawa line_beginning = ftello(pfp); 524108701Ssimokawa repl_beginning = 0; 525108530Ssimokawa fillcnt = 0; 526132432Ssimokawa fillsrc = 0; 527108530Ssimokawa filldst = 0; 528108530Ssimokawa ptrn_spaces_eaten = false; 529108530Ssimokawa repl_could_be_missing = true; 530108530Ssimokawa repl_missing = false; 531108530Ssimokawa repl_backtrack_position = 0; 532108530Ssimokawa repl_patch_line = 0; 533132432Ssimokawa ptrn_copiable = 0; 534108530Ssimokawa 535108530Ssimokawa len = pgets(true); 536108701Ssimokawa p_input_line++; 537108701Ssimokawa if (len == 0 || strnNE(buf, "********", 8)) { 538108701Ssimokawa next_intuit_at(line_beginning, p_input_line); 539108701Ssimokawa return false; 540108530Ssimokawa } 541108530Ssimokawa p_context = 100; 542108701Ssimokawa p_hunk_beg = p_input_line + 1; 543108701Ssimokawa while (p_end < p_max) { 544108701Ssimokawa line_beginning = ftello(pfp); 545108701Ssimokawa len = pgets(true); 546108701Ssimokawa p_input_line++; 547108701Ssimokawa if (len == 0) { 548108701Ssimokawa if (p_max - p_end < 4) { 549108701Ssimokawa /* assume blank lines got chopped */ 550108701Ssimokawa strlcpy(buf, " \n", buf_size); 551108701Ssimokawa } else { 552108701Ssimokawa if (repl_beginning && repl_could_be_missing) { 553132432Ssimokawa repl_missing = true; 554108530Ssimokawa goto hunk_done; 555108530Ssimokawa } 556108530Ssimokawa fatal("unexpected end of file in patch\n"); 557108701Ssimokawa } 558108530Ssimokawa } 559113584Ssimokawa p_end++; 560108530Ssimokawa if (p_end >= hunkmax) 561108530Ssimokawa fatal("Internal error: hunk larger than hunk " 562113584Ssimokawa "buffer size"); 563108530Ssimokawa p_char[p_end] = *buf; 564108530Ssimokawa p_line[p_end] = NULL; 565108701Ssimokawa switch (*buf) { 566108530Ssimokawa case '*': 567108642Ssimokawa if (strnEQ(buf, "********", 8)) { 568108701Ssimokawa if (repl_beginning && repl_could_be_missing) { 569108642Ssimokawa repl_missing = true; 570108642Ssimokawa goto hunk_done; 571108530Ssimokawa } else 572108530Ssimokawa fatal("unexpected end of hunk " 573108530Ssimokawa "at line %ld\n", 574108701Ssimokawa p_input_line); 575108701Ssimokawa } 576108701Ssimokawa if (p_end != 0) { 577116978Ssimokawa if (repl_beginning && repl_could_be_missing) { 578108701Ssimokawa repl_missing = true; 579108701Ssimokawa goto hunk_done; 580108701Ssimokawa } 581108701Ssimokawa fatal("unexpected *** at line %ld: %s", 582116978Ssimokawa p_input_line, buf); 583116978Ssimokawa } 584116978Ssimokawa context = 0; 585116978Ssimokawa p_line[p_end] = savestr(buf); 586116978Ssimokawa if (out_of_mem) { 587116978Ssimokawa p_end--; 588108530Ssimokawa return false; 589108530Ssimokawa } 590108530Ssimokawa for (s = buf; 591108530Ssimokawa *s && !isdigit((unsigned char)*s); s++) 592108530Ssimokawa ; 593108530Ssimokawa if (!*s) 594108530Ssimokawa malformed(); 595108530Ssimokawa if (strnEQ(s, "0,0", 3)) 596108530Ssimokawa memmove(s, s + 2, strlen(s + 2) + 1); 597108701Ssimokawa p_first = strtolinenum(s, &s); 598129541Sdfr if (*s == ',') { 599170374Ssimokawa for (; 600108530Ssimokawa *s && !isdigit((unsigned char)*s); s++) 601108530Ssimokawa ; 602108530Ssimokawa if (!*s) 603170374Ssimokawa malformed(); 604170374Ssimokawa p_ptrn_lines = strtolinenum(s, &s) - p_first + 1; 605170374Ssimokawa if (p_ptrn_lines < 0) 606108530Ssimokawa malformed(); 607108530Ssimokawa } else if (p_first) 608108530Ssimokawa p_ptrn_lines = 1; 609108530Ssimokawa else { 610108530Ssimokawa p_ptrn_lines = 0; 611108530Ssimokawa p_first = 1; 612108530Ssimokawa } 613121781Ssimokawa if (p_first >= LINENUM_MAX - p_ptrn_lines || 614129585Sdfr p_ptrn_lines >= LINENUM_MAX - 6) 615129585Sdfr malformed(); 616108530Ssimokawa 617121781Ssimokawa /* we need this much at least */ 618103285Sikob p_max = p_ptrn_lines + 6; 619121781Ssimokawa while (p_max >= hunkmax) 620103285Sikob grow_hunkmax(); 621121781Ssimokawa p_max = hunkmax; 622121781Ssimokawa break; 623118416Ssimokawa case '-': 624118416Ssimokawa if (buf[1] == '-') { 625118416Ssimokawa if (repl_beginning || 626118416Ssimokawa (p_end != p_ptrn_lines + 1 + 627129541Sdfr (p_char[p_end - 1] == '\n'))) { 628110045Ssimokawa if (p_end == 1) { 629110045Ssimokawa /* 630110045Ssimokawa * `old' lines were omitted; 631110045Ssimokawa * set up to fill them in 632110045Ssimokawa * from 'new' context lines. 633110045Ssimokawa */ 634110045Ssimokawa p_end = p_ptrn_lines + 1; 635110045Ssimokawa fillsrc = p_end + 1; 636103285Sikob filldst = 1; 637129541Sdfr fillcnt = p_ptrn_lines; 638118820Ssimokawa } else { 639118820Ssimokawa if (repl_beginning) { 640103285Sikob if (repl_could_be_missing) { 641103285Sikob repl_missing = true; 642103285Sikob goto hunk_done; 643103285Sikob } 644103285Sikob fatal("duplicate \"---\" at line %ld--check line numbers at line %ld\n", 645103285Sikob p_input_line, p_hunk_beg + repl_beginning); 646113584Ssimokawa } else { 647113584Ssimokawa fatal("%s \"---\" at line %ld--check line numbers at line %ld\n", 648113584Ssimokawa (p_end <= p_ptrn_lines 649113584Ssimokawa ? "Premature" 650113584Ssimokawa : "Overdue"), 651103285Sikob p_input_line, p_hunk_beg); 652103285Sikob } 653103285Sikob } 654103285Sikob } 655103285Sikob repl_beginning = p_end; 656113584Ssimokawa repl_backtrack_position = ftello(pfp); 657113584Ssimokawa repl_patch_line = p_input_line; 658113584Ssimokawa p_line[p_end] = savestr(buf); 659113584Ssimokawa if (out_of_mem) { 660103285Sikob p_end--; 661118293Ssimokawa return false; 662118293Ssimokawa } 663118293Ssimokawa p_char[p_end] = '='; 664118293Ssimokawa for (s = buf; *s && !isdigit((unsigned char)*s); s++) 665118293Ssimokawa ; 666103285Sikob if (!*s) 667103285Sikob malformed(); 668110593Ssimokawa p_newfirst = strtolinenum(s, &s); 669110593Ssimokawa if (*s == ',') { 670103285Sikob for (; *s && !isdigit((unsigned char)*s); s++) 671103285Sikob ; 672103285Sikob if (!*s) 673103285Sikob malformed(); 674103285Sikob p_repl_lines = strtolinenum(s, &s) - 675103285Sikob p_newfirst + 1; 676103285Sikob if (p_repl_lines < 0) 677103285Sikob malformed(); 678103285Sikob } else if (p_newfirst) 679118293Ssimokawa p_repl_lines = 1; 680118293Ssimokawa else { 681103285Sikob p_repl_lines = 0; 682103285Sikob p_newfirst = 1; 683103285Sikob } 684103285Sikob if (p_newfirst >= LINENUM_MAX - p_repl_lines || 685103285Sikob p_repl_lines >= LINENUM_MAX - p_end) 686113584Ssimokawa malformed(); 687103285Sikob p_max = p_repl_lines + p_end; 688113584Ssimokawa if (p_max > MAXHUNKSIZE) 689113584Ssimokawa fatal("hunk too large (%ld lines) at line %ld: %s", 690113584Ssimokawa p_max, p_input_line, buf); 691113584Ssimokawa while (p_max >= hunkmax) 692103285Sikob grow_hunkmax(); 693103285Sikob if (p_repl_lines != ptrn_copiable && 694103285Sikob (p_context != 0 || p_repl_lines != 1)) 695116376Ssimokawa repl_could_be_missing = false; 696116376Ssimokawa break; 697103285Sikob } 698103285Sikob goto change_line; 699103285Sikob case '+': 700103285Sikob case '!': 701103285Sikob repl_could_be_missing = false; 702103285Sikob change_line: 703103285Sikob if (buf[1] == '\n' && canonicalize) 704103285Sikob strlcpy(buf + 1, " \n", buf_size - 1); 705113584Ssimokawa if (!isspace((unsigned char)buf[1]) && 706103285Sikob buf[1] != '>' && buf[1] != '<' && 707103285Sikob repl_beginning && repl_could_be_missing) { 708129541Sdfr repl_missing = true; 709103285Sikob goto hunk_done; 710113584Ssimokawa } 711113584Ssimokawa if (context >= 0) { 712113584Ssimokawa if (context < p_context) 713113584Ssimokawa p_context = context; 714108527Ssimokawa context = -1000; 715108527Ssimokawa } 716113584Ssimokawa p_line[p_end] = savestr(buf + 2); 717129585Sdfr if (out_of_mem) { 718113584Ssimokawa p_end--; 719113584Ssimokawa return false; 720113584Ssimokawa } 721113584Ssimokawa if (p_end == p_ptrn_lines) { 722109736Ssimokawa if (remove_special_line()) { 723109736Ssimokawa int l; 724113584Ssimokawa 725113584Ssimokawa l = strlen(p_line[p_end]) - 1; 726108527Ssimokawa (p_line[p_end])[l] = 0; 727108527Ssimokawa } 728108527Ssimokawa } 729113584Ssimokawa break; 730108527Ssimokawa case '\t': 731108527Ssimokawa case '\n': /* assume the 2 spaces got eaten */ 732103285Sikob if (repl_beginning && repl_could_be_missing && 733113584Ssimokawa (!ptrn_spaces_eaten || 734108527Ssimokawa diff_type == NEW_CONTEXT_DIFF)) { 735108527Ssimokawa repl_missing = true; 736108527Ssimokawa goto hunk_done; 737113584Ssimokawa } 738108527Ssimokawa p_line[p_end] = savestr(buf); 739108527Ssimokawa if (out_of_mem) { 740103285Sikob p_end--; 741109814Ssimokawa return false; 742109814Ssimokawa } 743109814Ssimokawa if (p_end != p_ptrn_lines + 1) { 744109814Ssimokawa ptrn_spaces_eaten |= (repl_beginning != 0); 745103285Sikob context++; 746109814Ssimokawa if (!repl_beginning) 747109814Ssimokawa ptrn_copiable++; 748103285Sikob p_char[p_end] = ' '; 749103285Sikob } 750103285Sikob break; 751103285Sikob case ' ': 752103285Sikob if (!isspace((unsigned char)buf[1]) && 753103285Sikob repl_beginning && repl_could_be_missing) { 754103285Sikob repl_missing = true; 755103285Sikob goto hunk_done; 756103285Sikob } 757113584Ssimokawa context++; 758103285Sikob if (!repl_beginning) 759113584Ssimokawa ptrn_copiable++; 760113584Ssimokawa p_line[p_end] = savestr(buf + 2); 761113584Ssimokawa if (out_of_mem) { 762103285Sikob p_end--; 763103285Sikob return false; 764103285Sikob } 765103285Sikob break; 766106790Ssimokawa default: 767113584Ssimokawa if (repl_beginning && repl_could_be_missing) { 768113584Ssimokawa repl_missing = true; 769170374Ssimokawa goto hunk_done; 770170374Ssimokawa } 771170374Ssimokawa malformed(); 772170374Ssimokawa } 773170374Ssimokawa /* set up p_len for strncmp() so we don't have to */ 774170374Ssimokawa /* assume null termination */ 775170374Ssimokawa if (p_line[p_end]) 776170374Ssimokawa p_len[p_end] = strlen(p_line[p_end]); 777170374Ssimokawa else 778108530Ssimokawa p_len[p_end] = 0; 779108530Ssimokawa } 780103285Sikob 781108530Ssimokawahunk_done: 782103285Sikob if (p_end >= 0 && !repl_beginning) 783106790Ssimokawa fatal("no --- found in patch at line %ld\n", pch_hunk_beg()); 784106790Ssimokawa 785106790Ssimokawa if (repl_missing) { 786103285Sikob 787103285Sikob /* reset state back to just after --- */ 788103285Sikob p_input_line = repl_patch_line; 789103285Sikob for (p_end--; p_end > repl_beginning; p_end--) 790103285Sikob free(p_line[p_end]); 791106790Ssimokawa fseeko(pfp, repl_backtrack_position, SEEK_SET); 792129585Sdfr 793106790Ssimokawa /* redundant 'new' context lines were omitted - set */ 794103285Sikob /* up to fill them in from the old file context */ 795103285Sikob if (!p_context && p_repl_lines == 1) { 796103285Sikob p_repl_lines = 0; 797103285Sikob p_max--; 798103285Sikob } 799108527Ssimokawa fillsrc = 1; 800108527Ssimokawa filldst = repl_beginning + 1; 801108527Ssimokawa fillcnt = p_repl_lines; 802108527Ssimokawa p_end = p_max; 803108527Ssimokawa } else if (!p_context && fillcnt == 1) { 804113584Ssimokawa /* the first hunk was a null hunk with no context */ 805113584Ssimokawa /* and we were expecting one line -- fix it up. */ 806113584Ssimokawa while (filldst < p_end) { 807113584Ssimokawa p_line[filldst] = p_line[filldst + 1]; 808108527Ssimokawa p_char[filldst] = p_char[filldst + 1]; 809108527Ssimokawa p_len[filldst] = p_len[filldst + 1]; 810108527Ssimokawa filldst++; 811108527Ssimokawa } 812108527Ssimokawa#if 0 813108527Ssimokawa repl_beginning--; /* this doesn't need to be fixed */ 814108527Ssimokawa#endif 815108527Ssimokawa p_end--; 816108527Ssimokawa p_first++; /* do append rather than insert */ 817108527Ssimokawa fillcnt = 0; 818108527Ssimokawa p_ptrn_lines = 0; 819170374Ssimokawa } 820170374Ssimokawa if (diff_type == CONTEXT_DIFF && 821170374Ssimokawa (fillcnt || (p_first > 1 && ptrn_copiable > 2 * p_context))) { 822170374Ssimokawa if (verbose) 823170374Ssimokawa say("%s\n%s\n%s\n", 824170374Ssimokawa "(Fascinating--this is really a new-style context diff but without", 825170374Ssimokawa "the telltale extra asterisks on the *** line that usually indicate", 826170374Ssimokawa "the new style...)"); 827108527Ssimokawa diff_type = NEW_CONTEXT_DIFF; 828108527Ssimokawa } 829108527Ssimokawa /* if there were omitted context lines, fill them in now */ 830108527Ssimokawa if (fillcnt) { 831108655Ssimokawa p_bfake = filldst; /* remember where not to free() */ 832108655Ssimokawa p_efake = filldst + fillcnt - 1; 833108655Ssimokawa while (fillcnt-- > 0) { 834108655Ssimokawa while (fillsrc <= p_end && p_char[fillsrc] != ' ') 835108655Ssimokawa fillsrc++; 836108655Ssimokawa if (fillsrc > p_end) 837106790Ssimokawa fatal("replacement text or line numbers mangled in hunk at line %ld\n", 838113584Ssimokawa p_hunk_beg); 839113584Ssimokawa p_line[filldst] = p_line[fillsrc]; 840113584Ssimokawa p_char[filldst] = p_char[fillsrc]; 841120660Ssimokawa p_len[filldst] = p_len[fillsrc]; 842113584Ssimokawa fillsrc++; 843113584Ssimokawa filldst++; 844113584Ssimokawa } 845113584Ssimokawa while (fillsrc <= p_end && fillsrc != repl_beginning && 846113584Ssimokawa p_char[fillsrc] != ' ') 847113584Ssimokawa fillsrc++; 848113584Ssimokawa#ifdef DEBUGGING 849113584Ssimokawa if (debug & 64) 850113584Ssimokawa printf("fillsrc %ld, filldst %ld, rb %ld, e+1 %ld\n", 851113584Ssimokawa fillsrc, filldst, repl_beginning, p_end + 1); 852113584Ssimokawa#endif 853113584Ssimokawa if (fillsrc != p_end + 1 && fillsrc != repl_beginning) 854113584Ssimokawa malformed(); 855113584Ssimokawa if (filldst != p_end + 1 && filldst != repl_beginning) 856113584Ssimokawa malformed(); 857113584Ssimokawa } 858113584Ssimokawa if (p_line[p_end] != NULL) { 859113584Ssimokawa if (remove_special_line()) { 860113584Ssimokawa p_len[p_end] -= 1; 861113584Ssimokawa (p_line[p_end])[p_len[p_end]] = 0; 862113584Ssimokawa } 863113584Ssimokawa } 864113584Ssimokawa } else if (diff_type == UNI_DIFF) { 865113584Ssimokawa LINENUM fillold; /* index of old lines */ 866113584Ssimokawa LINENUM fillnew; /* index of new lines */ 867113584Ssimokawa char ch; 868113584Ssimokawa 869113584Ssimokawa line_beginning = ftello(pfp); /* file pos of the current line */ 870106790Ssimokawa len = pgets(true); 871103285Sikob p_input_line++; 872103285Sikob if (len == 0 || strnNE(buf, "@@ -", 4)) { 873120660Ssimokawa next_intuit_at(line_beginning, p_input_line); 874103285Sikob return false; 875129585Sdfr } 876103285Sikob s = buf + 4; 877103285Sikob if (!*s) 878120660Ssimokawa malformed(); 879103285Sikob p_first = strtolinenum(s, &s); 880120660Ssimokawa if (*s == ',') { 881129585Sdfr p_ptrn_lines = strtolinenum(s + 1, &s); 882103285Sikob } else 883108655Ssimokawa p_ptrn_lines = 1; 884103285Sikob if (*s == ' ') 885170374Ssimokawa s++; 886170374Ssimokawa if (*s != '+' || !*++s) 887103285Sikob malformed(); 888103285Sikob p_newfirst = strtolinenum(s, &s); 889103285Sikob if (*s == ',') { 890103285Sikob p_repl_lines = strtolinenum(s + 1, &s); 891103285Sikob } else 892103285Sikob p_repl_lines = 1; 893103285Sikob if (*s == ' ') 894103285Sikob s++; 895103285Sikob if (*s != '@') 896103285Sikob malformed(); 897103285Sikob if (p_first >= LINENUM_MAX - p_ptrn_lines || 898103285Sikob p_newfirst > LINENUM_MAX - p_repl_lines || 899103285Sikob p_ptrn_lines >= LINENUM_MAX - p_repl_lines - 1) 900103285Sikob malformed(); 901103285Sikob if (!p_ptrn_lines) 902103285Sikob p_first++; /* do append rather than insert */ 903103285Sikob p_max = p_ptrn_lines + p_repl_lines + 1; 904103285Sikob while (p_max >= hunkmax) 905170374Ssimokawa grow_hunkmax(); 906103285Sikob fillold = 1; 907103285Sikob fillnew = fillold + p_ptrn_lines; 908103285Sikob p_end = fillnew + p_repl_lines; 909170374Ssimokawa snprintf(buf, buf_size, "*** %ld,%ld ****\n", p_first, 910103285Sikob p_first + p_ptrn_lines - 1); 911103285Sikob p_line[0] = savestr(buf); 912170374Ssimokawa if (out_of_mem) { 913103285Sikob p_end = -1; 914120660Ssimokawa return false; 915103285Sikob } 916103285Sikob p_char[0] = '*'; 917120660Ssimokawa snprintf(buf, buf_size, "--- %ld,%ld ----\n", p_newfirst, 918103285Sikob p_newfirst + p_repl_lines - 1); 919113584Ssimokawa p_line[fillnew] = savestr(buf); 920119155Ssimokawa if (out_of_mem) { 921119155Ssimokawa p_end = 0; 922119155Ssimokawa return false; 923119155Ssimokawa } 924119155Ssimokawa p_char[fillnew++] = '='; 925119155Ssimokawa p_context = 100; 926120660Ssimokawa context = 0; 927103285Sikob p_hunk_beg = p_input_line + 1; 928103285Sikob while (fillold <= p_ptrn_lines || fillnew <= p_end) { 929113584Ssimokawa line_beginning = ftello(pfp); 930103285Sikob len = pgets(true); 931103285Sikob p_input_line++; 932119155Ssimokawa if (len == 0) { 933119155Ssimokawa if (p_max - fillnew < 3) { 934103285Sikob /* assume blank lines got chopped */ 935103285Sikob strlcpy(buf, " \n", buf_size); 936103285Sikob } else { 937113584Ssimokawa fatal("unexpected end of file in patch\n"); 938103285Sikob } 939103285Sikob } 940103285Sikob if (*buf == '\t' || *buf == '\n') { 941103285Sikob ch = ' '; /* assume the space got eaten */ 942113584Ssimokawa s = savestr(buf); 943113584Ssimokawa } else { 944119155Ssimokawa ch = *buf; 945113584Ssimokawa s = savestr(buf + 1); 946103285Sikob } 947103285Sikob if (out_of_mem) { 948113584Ssimokawa while (--fillnew > p_ptrn_lines) 949113584Ssimokawa free(p_line[fillnew]); 950103285Sikob p_end = fillold - 1; 951113584Ssimokawa return false; 952113584Ssimokawa } 953113584Ssimokawa switch (ch) { 954113584Ssimokawa case '-': 955119155Ssimokawa if (fillold > p_ptrn_lines) { 956113584Ssimokawa free(s); 957103285Sikob p_end = fillnew - 1; 958111942Ssimokawa malformed(); 959103285Sikob } 960103285Sikob p_char[fillold] = ch; 961120660Ssimokawa p_line[fillold] = s; 962113584Ssimokawa p_len[fillold++] = strlen(s); 963113584Ssimokawa if (fillold > p_ptrn_lines) { 964103285Sikob if (remove_special_line()) { 965113584Ssimokawa p_len[fillold - 1] -= 1; 966120660Ssimokawa s[p_len[fillold - 1]] = 0; 967113584Ssimokawa } 968113584Ssimokawa } 969103285Sikob break; 970111942Ssimokawa case '=': 971113584Ssimokawa ch = ' '; 972113584Ssimokawa /* FALL THROUGH */ 973113584Ssimokawa case ' ': 974113584Ssimokawa if (fillold > p_ptrn_lines) { 975113584Ssimokawa free(s); 976113584Ssimokawa while (--fillnew > p_ptrn_lines) 977113584Ssimokawa free(p_line[fillnew]); 978113584Ssimokawa p_end = fillold - 1; 979113584Ssimokawa malformed(); 980113584Ssimokawa } 981113584Ssimokawa context++; 982111942Ssimokawa p_char[fillold] = ch; 983111942Ssimokawa p_line[fillold] = s; 984113584Ssimokawa p_len[fillold++] = strlen(s); 985113584Ssimokawa s = savestr(s); 986111942Ssimokawa if (out_of_mem) { 987111942Ssimokawa while (--fillnew > p_ptrn_lines) 988113584Ssimokawa free(p_line[fillnew]); 989111942Ssimokawa p_end = fillold - 1; 990111942Ssimokawa return false; 991111942Ssimokawa } 992111942Ssimokawa if (fillold > p_ptrn_lines) { 993103285Sikob if (remove_special_line()) { 994113584Ssimokawa p_len[fillold - 1] -= 1; 995113584Ssimokawa s[p_len[fillold - 1]] = 0; 996113584Ssimokawa } 997113584Ssimokawa } 998113584Ssimokawa /* FALL THROUGH */ 999113584Ssimokawa case '+': 1000113584Ssimokawa if (fillnew > p_end) { 1001113584Ssimokawa free(s); 1002113584Ssimokawa while (--fillnew > p_ptrn_lines) 1003103285Sikob free(p_line[fillnew]); 1004108655Ssimokawa p_end = fillold - 1; 1005108655Ssimokawa malformed(); 1006132432Ssimokawa } 1007187993Ssbruno p_char[fillnew] = ch; 1008108655Ssimokawa p_line[fillnew] = s; 1009103285Sikob p_len[fillnew++] = strlen(s); 1010103285Sikob if (fillold > p_ptrn_lines) { 1011113584Ssimokawa if (remove_special_line()) { 1012113584Ssimokawa p_len[fillnew - 1] -= 1; 1013113584Ssimokawa s[p_len[fillnew - 1]] = 0; 1014113584Ssimokawa } 1015103285Sikob } 1016103285Sikob break; 1017103285Sikob default: 1018103285Sikob p_end = fillnew; 1019103285Sikob malformed(); 1020113584Ssimokawa } 1021103285Sikob if (ch != ' ' && context > 0) { 1022170374Ssimokawa if (context < p_context) 1023103285Sikob p_context = context; 1024103285Sikob context = -1000; 1025103285Sikob } 1026103285Sikob } /* while */ 1027103285Sikob } else { /* normal diff--fake it up */ 1028107653Ssimokawa char hunk_type; 1029103285Sikob int i; 1030103285Sikob LINENUM min, max; 1031103285Sikob 1032103285Sikob line_beginning = ftello(pfp); 1033113584Ssimokawa p_context = 0; 1034113584Ssimokawa len = pgets(true); 1035103285Sikob p_input_line++; 1036103285Sikob if (len == 0 || !isdigit((unsigned char)*buf)) { 1037103285Sikob next_intuit_at(line_beginning, p_input_line); 1038103285Sikob return false; 1039132432Ssimokawa } 1040107653Ssimokawa p_first = strtolinenum(buf, &s); 1041103285Sikob if (*s == ',') { 1042113584Ssimokawa p_ptrn_lines = strtolinenum(s + 1, &s) - p_first + 1; 1043103285Sikob if (p_ptrn_lines < 0) 1044103285Sikob malformed(); 1045103285Sikob } else 1046106790Ssimokawa p_ptrn_lines = (*s != 'a'); 1047103285Sikob hunk_type = *s; 1048103285Sikob if (hunk_type == 'a') 1049103285Sikob p_first++; /* do append rather than insert */ 1050103285Sikob min = strtolinenum(s + 1, &s); 1051106790Ssimokawa if (*s == ',') 1052106790Ssimokawa max = strtolinenum(s + 1, &s); 1053106790Ssimokawa else 1054103285Sikob max = min; 1055103285Sikob if (min < 0 || min > max || max - min == LINENUM_MAX) 1056170374Ssimokawa malformed(); 1057103285Sikob if (hunk_type == 'd') 1058170374Ssimokawa min++; 1059103285Sikob p_newfirst = min; 1060103285Sikob p_repl_lines = max - min + 1; 1061106790Ssimokawa if (p_newfirst > LINENUM_MAX - p_repl_lines || 1062106790Ssimokawa p_ptrn_lines >= LINENUM_MAX - p_repl_lines - 1) 1063106790Ssimokawa malformed(); 1064103285Sikob p_end = p_ptrn_lines + p_repl_lines + 1; 1065103285Sikob if (p_end > MAXHUNKSIZE) 1066170374Ssimokawa fatal("hunk too large (%ld lines) at line %ld: %s", 1067103285Sikob p_end, p_input_line, buf); 1068170374Ssimokawa while (p_end >= hunkmax) 1069103285Sikob grow_hunkmax(); 1070103285Sikob snprintf(buf, buf_size, "*** %ld,%ld\n", p_first, 1071106790Ssimokawa p_first + p_ptrn_lines - 1); 1072106790Ssimokawa p_line[0] = savestr(buf); 1073106790Ssimokawa if (out_of_mem) { 1074103285Sikob p_end = -1; 1075113584Ssimokawa return false; 1076103285Sikob } 1077120660Ssimokawa p_char[0] = '*'; 1078103285Sikob for (i = 1; i <= p_ptrn_lines; i++) { 1079129585Sdfr len = pgets(true); 1080113584Ssimokawa p_input_line++; 1081103285Sikob if (len == 0) 1082103285Sikob fatal("unexpected end of file in patch at line %ld\n", 1083113584Ssimokawa p_input_line); 1084103285Sikob if (*buf != '<') 1085103285Sikob fatal("< expected at line %ld of patch\n", 1086113584Ssimokawa p_input_line); 1087103285Sikob p_line[i] = savestr(buf + 2); 1088103285Sikob if (out_of_mem) { 1089113584Ssimokawa p_end = i - 1; 1090103285Sikob return false; 1091103285Sikob } 1092103285Sikob p_len[i] = strlen(p_line[i]); 1093103285Sikob p_char[i] = '-'; 1094103285Sikob } 1095103285Sikob 1096113584Ssimokawa if (remove_special_line()) { 1097113584Ssimokawa p_len[i - 1] -= 1; 1098103285Sikob (p_line[i - 1])[p_len[i - 1]] = 0; 1099103285Sikob } 1100113584Ssimokawa if (hunk_type == 'c') { 1101113584Ssimokawa len = pgets(true); 1102170425Ssimokawa p_input_line++; 1103103285Sikob if (len == 0) 1104103285Sikob fatal("unexpected end of file in patch at line %ld\n", 1105103285Sikob p_input_line); 1106113584Ssimokawa if (*buf != '-') 1107113584Ssimokawa fatal("--- expected at line %ld of patch\n", 1108113584Ssimokawa p_input_line); 1109119155Ssimokawa } 1110167629Ssimokawa snprintf(buf, buf_size, "--- %ld,%ld\n", min, max); 1111119155Ssimokawa p_line[i] = savestr(buf); 1112103285Sikob if (out_of_mem) { 1113113584Ssimokawa p_end = i - 1; 1114113584Ssimokawa return false; 1115103285Sikob } 1116103285Sikob p_char[i] = '='; 1117103285Sikob for (i++; i <= p_end; i++) { 1118103285Sikob len = pgets(true); 1119103285Sikob p_input_line++; 1120103285Sikob if (len == 0) 1121113584Ssimokawa fatal("unexpected end of file in patch at line %ld\n", 1122103285Sikob p_input_line); 1123110577Ssimokawa if (*buf != '>') 1124103285Sikob fatal("> expected at line %ld of patch\n", 1125103285Sikob p_input_line); 1126103285Sikob p_line[i] = savestr(buf + 2); 1127103285Sikob if (out_of_mem) { 1128103285Sikob p_end = i - 1; 1129110577Ssimokawa return false; 1130103285Sikob } 1131103285Sikob p_len[i] = strlen(p_line[i]); 1132103285Sikob p_char[i] = '+'; 1133103285Sikob } 1134103285Sikob 1135103285Sikob if (remove_special_line()) { 1136103285Sikob p_len[i - 1] -= 1; 1137103285Sikob (p_line[i - 1])[p_len[i - 1]] = 0; 1138103285Sikob } 1139103285Sikob } 1140103285Sikob if (reverse) /* backwards patch? */ 1141103285Sikob if (!pch_swap()) 1142103285Sikob say("Not enough memory to swap next hunk!\n"); 1143103285Sikob#ifdef DEBUGGING 1144103285Sikob if (debug & 2) { 1145103285Sikob int i; 1146103285Sikob char special; 1147103285Sikob 1148103285Sikob for (i = 0; i <= p_end; i++) { 1149103285Sikob if (i == p_ptrn_lines) 1150103285Sikob special = '^'; 1151103285Sikob else 1152103285Sikob special = ' '; 1153103285Sikob fprintf(stderr, "%3d %c %c %s", i, p_char[i], 1154110577Ssimokawa special, p_line[i]); 1155103285Sikob fflush(stderr); 1156170374Ssimokawa } 1157119289Ssimokawa } 1158113584Ssimokawa#endif 1159113584Ssimokawa if (p_end + 1 < hunkmax)/* paranoia reigns supreme... */ 1160119289Ssimokawa p_char[p_end + 1] = '^'; /* add a stopper for apply_hunk */ 1161113584Ssimokawa return true; 1162113584Ssimokawa} 1163170427Ssimokawa 1164170374Ssimokawa/* 1165170425Ssimokawa * Input a line from the patch file. 1166170374Ssimokawa * Worry about indentation if do_indent is true. 1167114218Ssimokawa * The line is read directly into the buf global variable which 1168167630Ssimokawa * is resized if necessary in order to hold the complete line. 1169167630Ssimokawa * Returns the number of characters read including the terminating 1170114218Ssimokawa * '\n', if any. 1171114218Ssimokawa */ 1172170374Ssimokawasize_t 1173114218Ssimokawapgets(bool do_indent) 1174120660Ssimokawa{ 1175113584Ssimokawa char *line; 1176114218Ssimokawa size_t len; 1177103285Sikob int indent = 0, skipped = 0; 1178110577Ssimokawa 1179110577Ssimokawa line = fgetln(pfp, &len); 1180110577Ssimokawa if (line != NULL) { 1181110577Ssimokawa if (len + 1 > buf_size) { 1182113584Ssimokawa while (len + 1 > buf_size) 1183113584Ssimokawa buf_size *= 2; 1184103285Sikob free(buf); 1185170374Ssimokawa buf = malloc(buf_size); 1186110269Ssimokawa if (buf == NULL) 1187170374Ssimokawa fatal("out of memory\n"); 1188103285Sikob } 1189103285Sikob if (do_indent == 1 && p_indent) { 1190103285Sikob for (; 1191103285Sikob indent < p_indent && (*line == ' ' || *line == '\t' || *line == 'X'); 1192103285Sikob line++, skipped++) { 1193111956Ssimokawa if (*line == '\t') 1194111956Ssimokawa indent += 8 - (indent %7); 1195111956Ssimokawa else 1196111956Ssimokawa indent++; 1197111956Ssimokawa } 1198111956Ssimokawa } 1199103285Sikob memcpy(buf, line, len - skipped); 1200103285Sikob buf[len - skipped] = '\0'; 1201103285Sikob } 1202103285Sikob return len; 1203103285Sikob} 1204170374Ssimokawa 1205103285Sikob 1206170374Ssimokawa/* 1207103285Sikob * Reverse the old and new portions of the current hunk. 1208103285Sikob */ 1209103285Sikobbool 1210106790Ssimokawapch_swap(void) 1211106790Ssimokawa{ 1212106790Ssimokawa char **tp_line; /* the text of the hunk */ 1213103285Sikob unsigned short *tp_len;/* length of each line */ 1214103285Sikob char *tp_char; /* +, -, and ! */ 1215113584Ssimokawa LINENUM i; 1216103285Sikob LINENUM n; 1217108527Ssimokawa bool blankline = false; 1218108527Ssimokawa char *s; 1219108527Ssimokawa 1220113584Ssimokawa i = p_first; 1221103285Sikob p_first = p_newfirst; 1222113584Ssimokawa p_newfirst = i; 1223113584Ssimokawa 1224113584Ssimokawa /* make a scratch copy */ 1225113584Ssimokawa 1226113584Ssimokawa tp_line = p_line; 1227113584Ssimokawa tp_len = p_len; 1228113584Ssimokawa tp_char = p_char; 1229103285Sikob p_line = NULL; /* force set_hunkmax to allocate again */ 1230103285Sikob p_len = NULL; 1231103285Sikob p_char = NULL; 1232113584Ssimokawa set_hunkmax(); 1233110195Ssimokawa if (p_line == NULL || p_len == NULL || p_char == NULL) { 1234103285Sikob 1235108527Ssimokawa free(p_line); 1236103285Sikob p_line = tp_line; 1237106790Ssimokawa free(p_len); 1238106790Ssimokawa p_len = tp_len; 1239113584Ssimokawa free(p_char); 1240103285Sikob p_char = tp_char; 1241103285Sikob return false; /* not enough memory to swap hunk! */ 1242103285Sikob } 1243108642Ssimokawa /* now turn the new into the old */ 1244108642Ssimokawa 1245108642Ssimokawa i = p_ptrn_lines + 1; 1246108642Ssimokawa if (tp_char[i] == '\n') { /* account for possible blank line */ 1247113584Ssimokawa blankline = true; 1248113584Ssimokawa i++; 1249113584Ssimokawa } 1250113584Ssimokawa if (p_efake >= 0) { /* fix non-freeable ptr range */ 1251113584Ssimokawa if (p_efake <= i) 1252113584Ssimokawa n = p_end - i + 1; 1253113584Ssimokawa else 1254113584Ssimokawa n = -i; 1255113584Ssimokawa p_efake += n; 1256113584Ssimokawa p_bfake += n; 1257117126Sscottl } 1258127468Ssimokawa for (n = 0; i <= p_end; i++, n++) { 1259117126Sscottl p_line[n] = tp_line[i]; 1260170374Ssimokawa p_char[n] = tp_char[i]; 1261117228Ssimokawa if (p_char[n] == '+') 1262117228Ssimokawa p_char[n] = '-'; 1263113584Ssimokawa p_len[n] = tp_len[i]; 1264113584Ssimokawa } 1265103285Sikob if (blankline) { 1266103285Sikob i = p_ptrn_lines + 1; 1267103285Sikob p_line[n] = tp_line[i]; 1268103285Sikob p_char[n] = tp_char[i]; 1269103285Sikob p_len[n] = tp_len[i]; 1270113584Ssimokawa n++; 1271103285Sikob } 1272109379Ssimokawa if (p_char[0] != '=') 1273103285Sikob fatal("Malformed patch at line %ld: expected '=' found '%c'\n", 1274103285Sikob p_input_line, p_char[0]); 1275109379Ssimokawa p_char[0] = '*'; 1276113584Ssimokawa for (s = p_line[0]; *s; s++) 1277113584Ssimokawa if (*s == '-') 1278113584Ssimokawa *s = '*'; 1279113584Ssimokawa 1280113584Ssimokawa /* now turn the old into the new */ 1281124836Ssimokawa 1282103285Sikob if (p_char[0] != '*') 1283103285Sikob fatal("Malformed patch at line %ld: expected '*' found '%c'\n", 1284103285Sikob p_input_line, p_char[0]); 1285103285Sikob tp_char[0] = '='; 1286103285Sikob for (s = tp_line[0]; *s; s++) 1287113584Ssimokawa if (*s == '*') 1288113584Ssimokawa *s = '-'; 1289113584Ssimokawa for (i = 0; n <= p_end; i++, n++) { 1290113584Ssimokawa p_line[n] = tp_line[i]; 1291113584Ssimokawa p_char[n] = tp_char[i]; 1292113584Ssimokawa if (p_char[n] == '-') 1293113584Ssimokawa p_char[n] = '+'; 1294113584Ssimokawa p_len[n] = tp_len[i]; 1295113584Ssimokawa } 1296113584Ssimokawa 1297113584Ssimokawa if (i != p_ptrn_lines + 1) 1298103285Sikob fatal("Malformed patch at line %ld: expected %ld lines, " 1299113584Ssimokawa "got %ld\n", 1300108530Ssimokawa p_input_line, p_ptrn_lines + 1, i); 1301108530Ssimokawa 1302108530Ssimokawa i = p_ptrn_lines; 1303108530Ssimokawa p_ptrn_lines = p_repl_lines; 1304108530Ssimokawa p_repl_lines = i; 1305108530Ssimokawa 1306103285Sikob free(tp_line); 1307103285Sikob free(tp_len); 1308103285Sikob free(tp_char); 1309103285Sikob 1310103285Sikob return true; 1311108642Ssimokawa} 1312108642Ssimokawa 1313108642Ssimokawa/* 1314103285Sikob * Return the specified line position in the old file of the old context. 1315103285Sikob */ 1316108527SsimokawaLINENUM 1317103285Sikobpch_first(void) 1318106790Ssimokawa{ 1319106790Ssimokawa return p_first; 1320106790Ssimokawa} 1321103285Sikob 1322103285Sikob/* 1323109890Ssimokawa * Return the number of lines of old context. 1324113584Ssimokawa */ 1325113584SsimokawaLINENUM 1326103285Sikobpch_ptrn_lines(void) 1327103285Sikob{ 1328109890Ssimokawa return p_ptrn_lines; 1329167086Sjhb} 1330103285Sikob 1331103285Sikob/* 1332103285Sikob * Return the probable line position in the new file of the first line. 1333103285Sikob */ 1334106790SsimokawaLINENUM 1335106790Ssimokawapch_newfirst(void) 1336106790Ssimokawa{ 1337103285Sikob return p_newfirst; 1338103285Sikob} 1339103285Sikob 1340103285Sikob/* 1341103285Sikob * Return the number of lines in the replacement text including context. 1342103285Sikob */ 1343109890SsimokawaLINENUM 1344167086Sjhbpch_repl_lines(void) 1345103285Sikob{ 1346103285Sikob return p_repl_lines; 1347103285Sikob} 1348103285Sikob 1349106790Ssimokawa/* 1350113584Ssimokawa * Return the number of lines in the whole hunk. 1351106790Ssimokawa */ 1352129585SdfrLINENUM 1353103285Sikobpch_end(void) 1354113584Ssimokawa{ 1355103285Sikob return p_end; 1356103285Sikob} 1357103285Sikob 1358103285Sikob/* 1359106790Ssimokawa * Return the number of context lines before the first changed line. 1360106790Ssimokawa */ 1361103285SikobLINENUM 1362103285Sikobpch_context(void) 1363113584Ssimokawa{ 1364129585Sdfr return p_context; 1365103285Sikob} 1366120660Ssimokawa 1367103285Sikob/* 1368103285Sikob * Return the length of a particular patch line. 1369103285Sikob */ 1370103285Sikobunsigned short 1371103285Sikobpch_line_len(LINENUM line) 1372103285Sikob{ 1373103285Sikob return p_len[line]; 1374103285Sikob} 1375103285Sikob 1376103285Sikob/* 1377103285Sikob * Return the control character (+, -, *, !, etc) for a patch line. 1378103285Sikob */ 1379123740Speterchar 1380103285Sikobpch_char(LINENUM line) 1381103285Sikob{ 1382103285Sikob return p_char[line]; 1383103285Sikob} 1384103285Sikob 1385103285Sikob/* 1386103285Sikob * Return a pointer to a particular patch line. 1387103285Sikob */ 1388103285Sikobchar * 1389103285Sikobpfetch(LINENUM line) 1390113584Ssimokawa{ 1391113584Ssimokawa return p_line[line]; 1392103285Sikob} 1393103285Sikob 1394103285Sikob/* 1395109892Ssimokawa * Return where in the patch file this hunk began, for error messages. 1396113584Ssimokawa */ 1397113584SsimokawaLINENUM 1398113584Ssimokawapch_hunk_beg(void) 1399113584Ssimokawa{ 1400103285Sikob return p_hunk_beg; 1401103285Sikob} 1402113584Ssimokawa 1403113584Ssimokawa/* 1404113584Ssimokawa * Apply an ed script by feeding ed itself. 1405109280Ssimokawa */ 1406113584Ssimokawavoid 1407113584Ssimokawado_ed_script(void) 1408113584Ssimokawa{ 1409103285Sikob char *t; 1410103285Sikob off_t beginning_of_this_line; 1411103285Sikob FILE *pipefp = NULL; 1412103285Sikob 1413113584Ssimokawa if (!skip_rest_of_patch) { 1414113584Ssimokawa if (copy_file(filearg[0], TMPOUTNAME) < 0) { 1415103285Sikob unlink(TMPOUTNAME); 1416103285Sikob fatal("can't create temp file %s", TMPOUTNAME); 1417106790Ssimokawa } 1418106790Ssimokawa snprintf(buf, buf_size, "%s%s%s", _PATH_ED, 1419106790Ssimokawa verbose ? " " : " -s ", TMPOUTNAME); 1420103285Sikob pipefp = popen(buf, "w"); 1421103285Sikob } 1422109892Ssimokawa for (;;) { 1423129585Sdfr beginning_of_this_line = ftello(pfp); 1424103285Sikob if (pgets(true) == 0) { 1425120660Ssimokawa next_intuit_at(beginning_of_this_line, p_input_line); 1426103285Sikob break; 1427103285Sikob } 1428103285Sikob p_input_line++; 1429103285Sikob for (t = buf; isdigit((unsigned char)*t) || *t == ','; t++) 1430103285Sikob ; 1431103285Sikob /* POSIX defines allowed commands as {a,c,d,i,s} */ 1432103285Sikob if (isdigit((unsigned char)*buf) && 1433103285Sikob (*t == 'a' || *t == 'c' || *t == 'd' || *t == 'i' || *t == 's')) { 1434103285Sikob if (pipefp != NULL) 1435103285Sikob fputs(buf, pipefp); 1436103285Sikob if (*t != 'd') { 1437103285Sikob while (pgets(true)) { 1438103285Sikob p_input_line++; 1439103285Sikob if (pipefp != NULL) 1440123740Speter fputs(buf, pipefp); 1441103285Sikob if (strEQ(buf, ".\n")) 1442103285Sikob break; 1443103285Sikob } 1444103285Sikob } 1445103285Sikob } else { 1446103285Sikob next_intuit_at(beginning_of_this_line, p_input_line); 1447103285Sikob break; 1448103285Sikob } 1449103285Sikob } 1450103285Sikob if (pipefp == NULL) 1451103285Sikob return; 1452103285Sikob fprintf(pipefp, "w\n"); 1453103285Sikob fprintf(pipefp, "q\n"); 1454108642Ssimokawa fflush(pipefp); 1455103285Sikob pclose(pipefp); 1456103285Sikob ignore_signals(); 1457103285Sikob if (!check_only) { 1458103285Sikob if (move_file(TMPOUTNAME, outname) < 0) { 1459113584Ssimokawa toutkeep = true; 1460113584Ssimokawa chmod(TMPOUTNAME, filemode); 1461113584Ssimokawa } else 1462103285Sikob chmod(outname, filemode); 1463109892Ssimokawa } 1464109892Ssimokawa set_signals(1); 1465113584Ssimokawa} 1466113584Ssimokawa 1467103285Sikob/* 1468103285Sikob * Choose the name of the file to be patched based on POSIX rules. 1469113584Ssimokawa * NOTE: the POSIX rules are amazingly stupid and we only follow them 1470113584Ssimokawa * if the user specified --posix or set POSIXLY_CORRECT. 1471113584Ssimokawa */ 1472113584Ssimokawastatic char * 1473113584Ssimokawaposix_name(const struct file_name *names, bool assume_exists) 1474113584Ssimokawa{ 1475103285Sikob char *path = NULL; 1476103285Sikob int i; 1477103285Sikob 1478103285Sikob /* 1479113584Ssimokawa * POSIX states that the filename will be chosen from one 1480113584Ssimokawa * of the old, new and index names (in that order) if 1481103285Sikob * the file exists relative to CWD after -p stripping. 1482113584Ssimokawa */ 1483113584Ssimokawa for (i = 0; i < MAX_FILE; i++) { 1484103285Sikob if (names[i].path != NULL && names[i].exists) { 1485103285Sikob path = names[i].path; 1486103285Sikob break; 1487113584Ssimokawa } 1488103285Sikob } 1489103285Sikob if (path == NULL && !assume_exists) { 1490103285Sikob /* 1491103285Sikob * No files found, look for something we can checkout from 1492106790Ssimokawa * RCS/SCCS dirs. Same order as above. 1493106790Ssimokawa */ 1494113584Ssimokawa for (i = 0; i < MAX_FILE; i++) { 1495109890Ssimokawa if (names[i].path != NULL && 1496109890Ssimokawa (path = checked_in(names[i].path)) != NULL) 1497109890Ssimokawa break; 1498109890Ssimokawa } 1499109890Ssimokawa /* 1500109890Ssimokawa * Still no match? Check to see if the diff could be creating 1501113584Ssimokawa * a new file. 1502109890Ssimokawa */ 1503113584Ssimokawa if (path == NULL && ok_to_create_file && 1504113584Ssimokawa names[NEW_FILE].path != NULL) 1505113584Ssimokawa path = names[NEW_FILE].path; 1506109890Ssimokawa } 1507109890Ssimokawa 1508109890Ssimokawa return path ? xstrdup(path) : NULL; 1509109890Ssimokawa} 1510109890Ssimokawa 1511113584Ssimokawastatic char * 1512109890Ssimokawacompare_names(const struct file_name *names, bool assume_exists, int phase) 1513109890Ssimokawa{ 1514109890Ssimokawa size_t min_components, min_baselen, min_len, tmp; 1515109890Ssimokawa char *best = NULL; 1516109890Ssimokawa char *path; 1517109890Ssimokawa int i; 1518109890Ssimokawa 1519109890Ssimokawa /* 1520109890Ssimokawa * The "best" name is the one with the fewest number of path 1521109890Ssimokawa * components, the shortest basename length, and the shortest 1522109890Ssimokawa * overall length (in that order). We only use the Index: file 1523109890Ssimokawa * if neither of the old or new files could be intuited from 1524109890Ssimokawa * the diff header. 1525106790Ssimokawa */ 1526103285Sikob min_components = min_baselen = min_len = SIZE_MAX; 1527103285Sikob for (i = INDEX_FILE; i >= OLD_FILE; i--) { 1528103285Sikob path = names[i].path; 1529103285Sikob if (path == NULL || 1530103285Sikob (phase == 1 && !names[i].exists && !assume_exists) || 1531109890Ssimokawa (phase == 2 && checked_in(path) == NULL)) 1532129585Sdfr continue; 1533109890Ssimokawa if ((tmp = num_components(path)) > min_components) 1534109890Ssimokawa continue; 1535103285Sikob if (tmp < min_components) { 1536103285Sikob min_components = tmp; 1537109890Ssimokawa best = path; 1538109890Ssimokawa } 1539109890Ssimokawa if ((tmp = strlen(basename(path))) > min_baselen) 1540109890Ssimokawa continue; 1541109179Ssimokawa if (tmp < min_baselen) { 1542109890Ssimokawa min_baselen = tmp; 1543103285Sikob best = path; 1544113584Ssimokawa } 1545109179Ssimokawa if ((tmp = strlen(path)) > min_len) 1546109179Ssimokawa continue; 1547170374Ssimokawa min_len = tmp; 1548103285Sikob best = path; 1549103285Sikob } 1550103285Sikob return best; 1551103285Sikob} 1552109890Ssimokawa 1553109892Ssimokawa/* 1554109890Ssimokawa * Choose the name of the file to be patched based the "best" one 1555170374Ssimokawa * available. 1556109890Ssimokawa */ 1557109890Ssimokawastatic char * 1558120660Ssimokawabest_name(const struct file_name *names, bool assume_exists) 1559109890Ssimokawa{ 1560113584Ssimokawa char *best; 1561113584Ssimokawa 1562109890Ssimokawa best = compare_names(names, assume_exists, 1); 1563109890Ssimokawa if (best == NULL) { 1564109890Ssimokawa best = compare_names(names, assume_exists, 2); 1565113584Ssimokawa /* 1566113584Ssimokawa * Still no match? Check to see if the diff could be creating 1567113584Ssimokawa * a new file. 1568113584Ssimokawa */ 1569109892Ssimokawa if (best == NULL && ok_to_create_file && 1570109890Ssimokawa names[NEW_FILE].path != NULL) 1571113584Ssimokawa best = names[NEW_FILE].path; 1572113584Ssimokawa } 1573109892Ssimokawa 1574113584Ssimokawa return best ? xstrdup(best) : NULL; 1575113584Ssimokawa} 1576109892Ssimokawa 1577103285Sikobstatic size_t 1578109890Ssimokawanum_components(const char *path) 1579109890Ssimokawa{ 1580109890Ssimokawa size_t n; 1581109403Ssimokawa const char *cp; 1582170374Ssimokawa 1583113584Ssimokawa for (n = 0, cp = path; (cp = strchr(cp, '/')) != NULL; n++, cp++) { 1584113584Ssimokawa while (*cp == '/') 1585109890Ssimokawa cp++; /* skip consecutive slashes */ 1586109890Ssimokawa } 1587113584Ssimokawa return n; 1588113584Ssimokawa} 1589113584Ssimokawa 1590109890Ssimokawa/* 1591109890Ssimokawa * Convert number at NPTR into LINENUM and save address of first 1592109890Ssimokawa * character that is not a digit in ENDPTR. If conversion is not 1593113584Ssimokawa * possible, call fatal. 1594109890Ssimokawa */ 1595113584Ssimokawastatic LINENUM 1596109403Ssimokawastrtolinenum(char *nptr, char **endptr) 1597109403Ssimokawa{ 1598109403Ssimokawa LINENUM rv; 1599113584Ssimokawa char c; 1600109890Ssimokawa char *p; 1601109890Ssimokawa const char *errstr; 1602113584Ssimokawa 1603113584Ssimokawa for (p = nptr; isdigit((unsigned char)*p); p++) 1604167629Ssimokawa ; 1605109890Ssimokawa 1606113584Ssimokawa if (p == nptr) 1607113584Ssimokawa malformed(); 1608113584Ssimokawa 1609113584Ssimokawa c = *p; 1610109403Ssimokawa *p = '\0'; 1611109890Ssimokawa 1612109890Ssimokawa rv = strtonum(nptr, 0, LINENUM_MAX, &errstr); 1613109890Ssimokawa if (errstr != NULL) 1614109890Ssimokawa fatal("invalid line number at line %ld: `%s' is %s\n", 1615109890Ssimokawa p_input_line, nptr, errstr); 1616113584Ssimokawa 1617109890Ssimokawa *p = c; 1618109890Ssimokawa *endptr = p; 1619109890Ssimokawa 1620109356Ssimokawa return rv; 1621109356Ssimokawa} 1622113584Ssimokawa