pch.c revision 252636
1246091Sdelphij 2246074Sgabor/*- 3246074Sgabor * Copyright 1986, Larry Wall 4246074Sgabor * 5246074Sgabor * Redistribution and use in source and binary forms, with or without 6246074Sgabor * modification, are permitted provided that the following condition is met: 7246074Sgabor * 1. Redistributions of source code must retain the above copyright notice, 8246074Sgabor * this condition and the following disclaimer. 9246074Sgabor * 10246074Sgabor * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY 11246074Sgabor * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 12246074Sgabor * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 13246074Sgabor * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 14246074Sgabor * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 15246074Sgabor * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 16246074Sgabor * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 17246074Sgabor * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 18246074Sgabor * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 19246074Sgabor * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 20246074Sgabor * SUCH DAMAGE. 21246074Sgabor * 22246074Sgabor * patch - a program to apply diffs to original files 23246074Sgabor * 24246074Sgabor * -C option added in 1998, original code by Marc Espie, based on FreeBSD 25246074Sgabor * behaviour 26246074Sgabor * 27246091Sdelphij * $OpenBSD: pch.c,v 1.39 2012/04/11 08:07:13 ajacoutot Exp $ 28246091Sdelphij * $FreeBSD: head/usr.bin/patch/pch.c 252636 2013-07-03 22:44:26Z obrien $ 29246074Sgabor */ 30246074Sgabor 31246074Sgabor#include <sys/types.h> 32246074Sgabor#include <sys/stat.h> 33246074Sgabor 34246074Sgabor#include <ctype.h> 35246074Sgabor#include <libgen.h> 36246074Sgabor#include <limits.h> 37246074Sgabor#include <stdio.h> 38246074Sgabor#include <stdlib.h> 39246074Sgabor#include <string.h> 40246074Sgabor#include <unistd.h> 41246074Sgabor 42246074Sgabor#include "common.h" 43246074Sgabor#include "util.h" 44246074Sgabor#include "pch.h" 45246074Sgabor#include "pathnames.h" 46246074Sgabor 47246074Sgabor/* Patch (diff listing) abstract type. */ 48246074Sgabor 49246074Sgaborstatic long p_filesize; /* size of the patch file */ 50246074Sgaborstatic LINENUM p_first; /* 1st line number */ 51246074Sgaborstatic LINENUM p_newfirst; /* 1st line number of replacement */ 52246074Sgaborstatic LINENUM p_ptrn_lines; /* # lines in pattern */ 53246074Sgaborstatic LINENUM p_repl_lines; /* # lines in replacement text */ 54246074Sgaborstatic LINENUM p_end = -1; /* last line in hunk */ 55246074Sgaborstatic LINENUM p_max; /* max allowed value of p_end */ 56246074Sgaborstatic LINENUM p_context = 3; /* # of context lines */ 57246074Sgaborstatic LINENUM p_input_line = 0; /* current line # from patch file */ 58246074Sgaborstatic char **p_line = NULL;/* the text of the hunk */ 59246074Sgaborstatic short *p_len = NULL; /* length of each line */ 60246074Sgaborstatic char *p_char = NULL; /* +, -, and ! */ 61246074Sgaborstatic int hunkmax = INITHUNKMAX; /* size of above arrays to begin with */ 62246074Sgaborstatic int p_indent; /* indent to patch */ 63246074Sgaborstatic LINENUM p_base; /* where to intuit this time */ 64246074Sgaborstatic LINENUM p_bline; /* line # of p_base */ 65246074Sgaborstatic LINENUM p_start; /* where intuit found a patch */ 66246074Sgaborstatic LINENUM p_sline; /* and the line number for it */ 67246074Sgaborstatic LINENUM p_hunk_beg; /* line number of current hunk */ 68246074Sgaborstatic LINENUM p_efake = -1; /* end of faked up lines--don't free */ 69246074Sgaborstatic LINENUM p_bfake = -1; /* beg of faked up lines */ 70246074Sgaborstatic FILE *pfp = NULL; /* patch file pointer */ 71246074Sgaborstatic char *bestguess = NULL; /* guess at correct filename */ 72246074Sgabor 73246074Sgaborstatic void grow_hunkmax(void); 74246074Sgaborstatic int intuit_diff_type(void); 75246074Sgaborstatic void next_intuit_at(LINENUM, LINENUM); 76246074Sgaborstatic void skip_to(LINENUM, LINENUM); 77246074Sgaborstatic size_t pgets(bool _do_indent); 78246074Sgaborstatic char *best_name(const struct file_name *, bool); 79246074Sgaborstatic char *posix_name(const struct file_name *, bool); 80246074Sgaborstatic size_t num_components(const char *); 81246074Sgabor 82246074Sgabor/* 83246074Sgabor * Prepare to look for the next patch in the patch file. 84246074Sgabor */ 85246074Sgaborvoid 86246074Sgaborre_patch(void) 87246074Sgabor{ 88246074Sgabor p_first = 0; 89246074Sgabor p_newfirst = 0; 90246074Sgabor p_ptrn_lines = 0; 91246074Sgabor p_repl_lines = 0; 92246074Sgabor p_end = (LINENUM) - 1; 93246074Sgabor p_max = 0; 94246074Sgabor p_indent = 0; 95246074Sgabor} 96246074Sgabor 97246074Sgabor/* 98246074Sgabor * Open the patch file at the beginning of time. 99246074Sgabor */ 100246074Sgaborvoid 101246074Sgaboropen_patch_file(const char *filename) 102246074Sgabor{ 103246074Sgabor struct stat filestat; 104252636Sobrien int nr, nw; 105246074Sgabor 106246074Sgabor if (filename == NULL || *filename == '\0' || strEQ(filename, "-")) { 107246074Sgabor pfp = fopen(TMPPATNAME, "w"); 108246074Sgabor if (pfp == NULL) 109246074Sgabor pfatal("can't create %s", TMPPATNAME); 110252636Sobrien while ((nr = fread(buf, 1, buf_size, stdin)) > 0) { 111252636Sobrien nw = fwrite(buf, 1, nr, pfp); 112252636Sobrien if (nr != nw) 113252636Sobrien pfatal("write error to %s", TMPPATNAME); 114252636Sobrien } 115246074Sgabor if (ferror(pfp) || fclose(pfp)) 116246074Sgabor pfatal("can't write %s", TMPPATNAME); 117246074Sgabor filename = TMPPATNAME; 118246074Sgabor } 119246074Sgabor pfp = fopen(filename, "r"); 120246074Sgabor if (pfp == NULL) 121246074Sgabor pfatal("patch file %s not found", filename); 122246074Sgabor fstat(fileno(pfp), &filestat); 123246074Sgabor p_filesize = filestat.st_size; 124246074Sgabor next_intuit_at(0L, 1L); /* start at the beginning */ 125246074Sgabor set_hunkmax(); 126246074Sgabor} 127246074Sgabor 128246074Sgabor/* 129246074Sgabor * Make sure our dynamically realloced tables are malloced to begin with. 130246074Sgabor */ 131246074Sgaborvoid 132246074Sgaborset_hunkmax(void) 133246074Sgabor{ 134246074Sgabor if (p_line == NULL) 135246074Sgabor p_line = calloc((size_t) hunkmax, sizeof(char *)); 136246074Sgabor if (p_len == NULL) 137246074Sgabor p_len = calloc((size_t) hunkmax, sizeof(short)); 138246074Sgabor if (p_char == NULL) 139246074Sgabor p_char = calloc((size_t) hunkmax, sizeof(char)); 140246074Sgabor} 141246074Sgabor 142246074Sgabor/* 143246074Sgabor * Enlarge the arrays containing the current hunk of patch. 144246074Sgabor */ 145246074Sgaborstatic void 146246074Sgaborgrow_hunkmax(void) 147246074Sgabor{ 148246074Sgabor int new_hunkmax; 149246074Sgabor char **new_p_line; 150246074Sgabor short *new_p_len; 151246074Sgabor char *new_p_char; 152246074Sgabor 153246074Sgabor new_hunkmax = hunkmax * 2; 154246074Sgabor 155246074Sgabor if (p_line == NULL || p_len == NULL || p_char == NULL) 156246074Sgabor fatal("Internal memory allocation error\n"); 157246074Sgabor 158246074Sgabor new_p_line = realloc(p_line, new_hunkmax * sizeof(char *)); 159246074Sgabor if (new_p_line == NULL) 160246074Sgabor free(p_line); 161246074Sgabor 162246074Sgabor new_p_len = realloc(p_len, new_hunkmax * sizeof(short)); 163246074Sgabor if (new_p_len == NULL) 164246074Sgabor free(p_len); 165246074Sgabor 166246074Sgabor new_p_char = realloc(p_char, new_hunkmax * sizeof(char)); 167246074Sgabor if (new_p_char == NULL) 168246074Sgabor free(p_char); 169246074Sgabor 170246074Sgabor p_char = new_p_char; 171246074Sgabor p_len = new_p_len; 172246074Sgabor p_line = new_p_line; 173246074Sgabor 174246074Sgabor if (p_line != NULL && p_len != NULL && p_char != NULL) { 175246074Sgabor hunkmax = new_hunkmax; 176246074Sgabor return; 177246074Sgabor } 178246074Sgabor 179246074Sgabor if (!using_plan_a) 180246074Sgabor fatal("out of memory\n"); 181246074Sgabor out_of_mem = true; /* whatever is null will be allocated again */ 182246074Sgabor /* from within plan_a(), of all places */ 183246074Sgabor} 184246074Sgabor 185246074Sgabor/* True if the remainder of the patch file contains a diff of some sort. */ 186246074Sgabor 187246074Sgaborbool 188246074Sgaborthere_is_another_patch(void) 189246074Sgabor{ 190246074Sgabor bool exists = false; 191246074Sgabor 192246074Sgabor if (p_base != 0L && p_base >= p_filesize) { 193246074Sgabor if (verbose) 194246074Sgabor say("done\n"); 195246074Sgabor return false; 196246074Sgabor } 197246074Sgabor if (verbose) 198246074Sgabor say("Hmm..."); 199246074Sgabor diff_type = intuit_diff_type(); 200246074Sgabor if (!diff_type) { 201246074Sgabor if (p_base != 0L) { 202246074Sgabor if (verbose) 203246074Sgabor say(" Ignoring the trailing garbage.\ndone\n"); 204246074Sgabor } else 205246074Sgabor say(" I can't seem to find a patch in there anywhere.\n"); 206246074Sgabor return false; 207246074Sgabor } 208246074Sgabor if (verbose) 209246074Sgabor say(" %sooks like %s to me...\n", 210246074Sgabor (p_base == 0L ? "L" : "The next patch l"), 211246074Sgabor diff_type == UNI_DIFF ? "a unified diff" : 212246074Sgabor diff_type == CONTEXT_DIFF ? "a context diff" : 213246074Sgabor diff_type == NEW_CONTEXT_DIFF ? "a new-style context diff" : 214246074Sgabor diff_type == NORMAL_DIFF ? "a normal diff" : 215246074Sgabor "an ed script"); 216246074Sgabor if (p_indent && verbose) 217246074Sgabor say("(Patch is indented %d space%s.)\n", p_indent, 218246074Sgabor p_indent == 1 ? "" : "s"); 219246074Sgabor skip_to(p_start, p_sline); 220246074Sgabor while (filearg[0] == NULL) { 221246074Sgabor if (force || batch) { 222246074Sgabor say("No file to patch. Skipping...\n"); 223246074Sgabor filearg[0] = savestr(bestguess); 224246074Sgabor skip_rest_of_patch = true; 225246074Sgabor return true; 226246074Sgabor } 227246074Sgabor ask("File to patch: "); 228246074Sgabor if (*buf != '\n') { 229246074Sgabor free(bestguess); 230246074Sgabor bestguess = savestr(buf); 231246074Sgabor filearg[0] = fetchname(buf, &exists, 0); 232246074Sgabor } 233246074Sgabor if (!exists) { 234246074Sgabor ask("No file found--skip this patch? [n] "); 235246074Sgabor if (*buf != 'y') 236246074Sgabor continue; 237246074Sgabor if (verbose) 238246074Sgabor say("Skipping patch...\n"); 239246074Sgabor free(filearg[0]); 240246074Sgabor filearg[0] = fetchname(bestguess, &exists, 0); 241246074Sgabor skip_rest_of_patch = true; 242246074Sgabor return true; 243246074Sgabor } 244246074Sgabor } 245246074Sgabor return true; 246246074Sgabor} 247246074Sgabor 248246074Sgaborstatic void 249246074Sgaborp4_fetchname(struct file_name *name, char *str) 250246074Sgabor{ 251246074Sgabor char *t, *h; 252246074Sgabor 253246074Sgabor /* Skip leading whitespace. */ 254246074Sgabor while (isspace((unsigned char)*str)) 255246074Sgabor str++; 256246074Sgabor 257246074Sgabor /* Remove the file revision number. */ 258246074Sgabor for (t = str, h = NULL; *t != '\0' && !isspace((unsigned char)*t); t++) 259246074Sgabor if (*t == '#') 260246074Sgabor h = t; 261246074Sgabor if (h != NULL) 262246074Sgabor *h = '\0'; 263246074Sgabor 264246074Sgabor name->path = fetchname(str, &name->exists, strippath); 265246074Sgabor} 266246074Sgabor 267246074Sgabor/* Determine what kind of diff is in the remaining part of the patch file. */ 268246074Sgabor 269246074Sgaborstatic int 270246074Sgaborintuit_diff_type(void) 271246074Sgabor{ 272246074Sgabor long this_line = 0, previous_line; 273246074Sgabor long first_command_line = -1; 274246074Sgabor LINENUM fcl_line = -1; 275246074Sgabor bool last_line_was_command = false, this_is_a_command = false; 276246074Sgabor bool stars_last_line = false, stars_this_line = false; 277246074Sgabor char *s, *t; 278246074Sgabor int indent, retval; 279246074Sgabor struct file_name names[MAX_FILE]; 280246074Sgabor 281246074Sgabor memset(names, 0, sizeof(names)); 282246074Sgabor ok_to_create_file = false; 283246074Sgabor fseek(pfp, p_base, SEEK_SET); 284246074Sgabor p_input_line = p_bline - 1; 285246074Sgabor for (;;) { 286246074Sgabor previous_line = this_line; 287246074Sgabor last_line_was_command = this_is_a_command; 288246074Sgabor stars_last_line = stars_this_line; 289246074Sgabor this_line = ftell(pfp); 290246074Sgabor indent = 0; 291246074Sgabor p_input_line++; 292246074Sgabor if (pgets(false) == 0) { 293246074Sgabor if (first_command_line >= 0L) { 294246074Sgabor /* nothing but deletes!? */ 295246074Sgabor p_start = first_command_line; 296246074Sgabor p_sline = fcl_line; 297246074Sgabor retval = ED_DIFF; 298246074Sgabor goto scan_exit; 299246074Sgabor } else { 300246074Sgabor p_start = this_line; 301246074Sgabor p_sline = p_input_line; 302246074Sgabor retval = 0; 303246074Sgabor goto scan_exit; 304246074Sgabor } 305246074Sgabor } 306246074Sgabor for (s = buf; *s == ' ' || *s == '\t' || *s == 'X'; s++) { 307246074Sgabor if (*s == '\t') 308246074Sgabor indent += 8 - (indent % 8); 309246074Sgabor else 310246074Sgabor indent++; 311246074Sgabor } 312246074Sgabor for (t = s; isdigit((unsigned char)*t) || *t == ','; t++) 313246074Sgabor ; 314246074Sgabor this_is_a_command = (isdigit((unsigned char)*s) && 315246074Sgabor (*t == 'd' || *t == 'c' || *t == 'a')); 316246074Sgabor if (first_command_line < 0L && this_is_a_command) { 317246074Sgabor first_command_line = this_line; 318246074Sgabor fcl_line = p_input_line; 319246074Sgabor p_indent = indent; /* assume this for now */ 320246074Sgabor } 321246074Sgabor if (!stars_last_line && strnEQ(s, "*** ", 4)) 322246074Sgabor names[OLD_FILE].path = fetchname(s + 4, 323246074Sgabor &names[OLD_FILE].exists, strippath); 324246074Sgabor else if (strnEQ(s, "--- ", 4)) 325246074Sgabor names[NEW_FILE].path = fetchname(s + 4, 326246074Sgabor &names[NEW_FILE].exists, strippath); 327246074Sgabor else if (strnEQ(s, "+++ ", 4)) 328246074Sgabor /* pretend it is the old name */ 329246074Sgabor names[OLD_FILE].path = fetchname(s + 4, 330246074Sgabor &names[OLD_FILE].exists, strippath); 331246074Sgabor else if (strnEQ(s, "Index:", 6)) 332246074Sgabor names[INDEX_FILE].path = fetchname(s + 6, 333246074Sgabor &names[INDEX_FILE].exists, strippath); 334246074Sgabor else if (strnEQ(s, "Prereq:", 7)) { 335246074Sgabor for (t = s + 7; isspace((unsigned char)*t); t++) 336246074Sgabor ; 337246074Sgabor revision = savestr(t); 338246074Sgabor for (t = revision; *t && !isspace((unsigned char)*t); t++) 339246074Sgabor ; 340246074Sgabor *t = '\0'; 341246074Sgabor if (*revision == '\0') { 342246074Sgabor free(revision); 343246074Sgabor revision = NULL; 344246074Sgabor } 345246074Sgabor } else if (strnEQ(s, "==== ", 5)) { 346246074Sgabor /* Perforce-style diffs. */ 347246074Sgabor if ((t = strstr(s + 5, " - ")) != NULL) 348246074Sgabor p4_fetchname(&names[NEW_FILE], t + 3); 349246074Sgabor p4_fetchname(&names[OLD_FILE], s + 5); 350246074Sgabor } 351246074Sgabor if ((!diff_type || diff_type == ED_DIFF) && 352246074Sgabor first_command_line >= 0L && 353246074Sgabor strEQ(s, ".\n")) { 354246074Sgabor p_indent = indent; 355246074Sgabor p_start = first_command_line; 356246074Sgabor p_sline = fcl_line; 357246074Sgabor retval = ED_DIFF; 358246074Sgabor goto scan_exit; 359246074Sgabor } 360246074Sgabor if ((!diff_type || diff_type == UNI_DIFF) && strnEQ(s, "@@ -", 4)) { 361246074Sgabor if (strnEQ(s + 4, "0,0", 3)) 362246074Sgabor ok_to_create_file = true; 363246074Sgabor p_indent = indent; 364246074Sgabor p_start = this_line; 365246074Sgabor p_sline = p_input_line; 366246074Sgabor retval = UNI_DIFF; 367246074Sgabor goto scan_exit; 368246074Sgabor } 369246074Sgabor stars_this_line = strnEQ(s, "********", 8); 370246074Sgabor if ((!diff_type || diff_type == CONTEXT_DIFF) && stars_last_line && 371246074Sgabor strnEQ(s, "*** ", 4)) { 372246074Sgabor if (atol(s + 4) == 0) 373246074Sgabor ok_to_create_file = true; 374246074Sgabor /* 375246074Sgabor * If this is a new context diff the character just 376246074Sgabor * before the newline is a '*'. 377246074Sgabor */ 378246074Sgabor while (*s != '\n') 379246074Sgabor s++; 380246074Sgabor p_indent = indent; 381246074Sgabor p_start = previous_line; 382246074Sgabor p_sline = p_input_line - 1; 383246074Sgabor retval = (*(s - 1) == '*' ? NEW_CONTEXT_DIFF : CONTEXT_DIFF); 384246074Sgabor goto scan_exit; 385246074Sgabor } 386246074Sgabor if ((!diff_type || diff_type == NORMAL_DIFF) && 387246074Sgabor last_line_was_command && 388246074Sgabor (strnEQ(s, "< ", 2) || strnEQ(s, "> ", 2))) { 389246074Sgabor p_start = previous_line; 390246074Sgabor p_sline = p_input_line - 1; 391246074Sgabor p_indent = indent; 392246074Sgabor retval = NORMAL_DIFF; 393246074Sgabor goto scan_exit; 394246074Sgabor } 395246074Sgabor } 396246074Sgaborscan_exit: 397246074Sgabor if (retval == UNI_DIFF) { 398246074Sgabor /* unswap old and new */ 399246074Sgabor struct file_name tmp = names[OLD_FILE]; 400246074Sgabor names[OLD_FILE] = names[NEW_FILE]; 401246074Sgabor names[NEW_FILE] = tmp; 402246074Sgabor } 403246074Sgabor if (filearg[0] == NULL) { 404246074Sgabor if (posix) 405246074Sgabor filearg[0] = posix_name(names, ok_to_create_file); 406246074Sgabor else { 407246074Sgabor /* Ignore the Index: name for context diffs, like GNU */ 408246074Sgabor if (names[OLD_FILE].path != NULL || 409246074Sgabor names[NEW_FILE].path != NULL) { 410246074Sgabor free(names[INDEX_FILE].path); 411246074Sgabor names[INDEX_FILE].path = NULL; 412246074Sgabor } 413246074Sgabor filearg[0] = best_name(names, ok_to_create_file); 414246074Sgabor } 415246074Sgabor } 416246074Sgabor 417246074Sgabor free(bestguess); 418246074Sgabor bestguess = NULL; 419246074Sgabor if (filearg[0] != NULL) 420246074Sgabor bestguess = savestr(filearg[0]); 421246074Sgabor else if (!ok_to_create_file) { 422246074Sgabor /* 423246074Sgabor * We don't want to create a new file but we need a 424246074Sgabor * filename to set bestguess. Avoid setting filearg[0] 425246074Sgabor * so the file is not created automatically. 426246074Sgabor */ 427246074Sgabor if (posix) 428246074Sgabor bestguess = posix_name(names, true); 429246074Sgabor else 430246074Sgabor bestguess = best_name(names, true); 431246074Sgabor } 432246074Sgabor free(names[OLD_FILE].path); 433246074Sgabor free(names[NEW_FILE].path); 434246074Sgabor free(names[INDEX_FILE].path); 435246074Sgabor return retval; 436246074Sgabor} 437246074Sgabor 438246074Sgabor/* 439246074Sgabor * Remember where this patch ends so we know where to start up again. 440246074Sgabor */ 441246074Sgaborstatic void 442246074Sgabornext_intuit_at(LINENUM file_pos, LINENUM file_line) 443246074Sgabor{ 444246074Sgabor p_base = file_pos; 445246074Sgabor p_bline = file_line; 446246074Sgabor} 447246074Sgabor 448246074Sgabor/* 449246074Sgabor * Basically a verbose fseek() to the actual diff listing. 450246074Sgabor */ 451246074Sgaborstatic void 452246074Sgaborskip_to(LINENUM file_pos, LINENUM file_line) 453246074Sgabor{ 454246074Sgabor size_t len; 455246074Sgabor 456246074Sgabor if (p_base > file_pos) 457246074Sgabor fatal("Internal error: seek %ld>%ld\n", p_base, file_pos); 458246074Sgabor if (verbose && p_base < file_pos) { 459246074Sgabor fseek(pfp, p_base, SEEK_SET); 460246074Sgabor say("The text leading up to this was:\n--------------------------\n"); 461246074Sgabor while (ftell(pfp) < file_pos) { 462246074Sgabor len = pgets(false); 463246074Sgabor if (len == 0) 464246074Sgabor fatal("Unexpected end of file\n"); 465246074Sgabor say("|%s", buf); 466246074Sgabor } 467246074Sgabor say("--------------------------\n"); 468246074Sgabor } else 469246074Sgabor fseek(pfp, file_pos, SEEK_SET); 470246074Sgabor p_input_line = file_line - 1; 471246074Sgabor} 472246074Sgabor 473246074Sgabor/* Make this a function for better debugging. */ 474246074Sgaborstatic void 475246074Sgabormalformed(void) 476246074Sgabor{ 477246074Sgabor fatal("malformed patch at line %ld: %s", p_input_line, buf); 478246074Sgabor /* about as informative as "Syntax error" in C */ 479246074Sgabor} 480246074Sgabor 481246074Sgabor/* 482246074Sgabor * True if the line has been discarded (i.e. it is a line saying 483246074Sgabor * "\ No newline at end of file".) 484246074Sgabor */ 485246074Sgaborstatic bool 486246074Sgaborremove_special_line(void) 487246074Sgabor{ 488246074Sgabor int c; 489246074Sgabor 490246074Sgabor c = fgetc(pfp); 491246074Sgabor if (c == '\\') { 492246074Sgabor do { 493246074Sgabor c = fgetc(pfp); 494246074Sgabor } while (c != EOF && c != '\n'); 495246074Sgabor 496246074Sgabor return true; 497246074Sgabor } 498246074Sgabor if (c != EOF) 499246074Sgabor fseek(pfp, -1L, SEEK_CUR); 500246074Sgabor 501246074Sgabor return false; 502246074Sgabor} 503246074Sgabor 504246074Sgabor/* 505246074Sgabor * True if there is more of the current diff listing to process. 506246074Sgabor */ 507246074Sgaborbool 508246074Sgaboranother_hunk(void) 509246074Sgabor{ 510246074Sgabor long line_beginning; /* file pos of the current line */ 511246074Sgabor LINENUM repl_beginning; /* index of --- line */ 512246074Sgabor LINENUM fillcnt; /* #lines of missing ptrn or repl */ 513246074Sgabor LINENUM fillsrc; /* index of first line to copy */ 514246074Sgabor LINENUM filldst; /* index of first missing line */ 515246074Sgabor bool ptrn_spaces_eaten; /* ptrn was slightly misformed */ 516246074Sgabor bool repl_could_be_missing; /* no + or ! lines in this hunk */ 517246074Sgabor bool repl_missing; /* we are now backtracking */ 518246074Sgabor long repl_backtrack_position; /* file pos of first repl line */ 519246074Sgabor LINENUM repl_patch_line; /* input line number for same */ 520246074Sgabor LINENUM ptrn_copiable; /* # of copiable lines in ptrn */ 521246074Sgabor char *s; 522246074Sgabor size_t len; 523246074Sgabor int context = 0; 524246074Sgabor 525246074Sgabor while (p_end >= 0) { 526246074Sgabor if (p_end == p_efake) 527246074Sgabor p_end = p_bfake; /* don't free twice */ 528246074Sgabor else 529246074Sgabor free(p_line[p_end]); 530246074Sgabor p_end--; 531246074Sgabor } 532246074Sgabor p_efake = -1; 533246074Sgabor 534246074Sgabor p_max = hunkmax; /* gets reduced when --- found */ 535246074Sgabor if (diff_type == CONTEXT_DIFF || diff_type == NEW_CONTEXT_DIFF) { 536246074Sgabor line_beginning = ftell(pfp); 537246074Sgabor repl_beginning = 0; 538246074Sgabor fillcnt = 0; 539246074Sgabor fillsrc = 0; 540246074Sgabor filldst = 0; 541246074Sgabor ptrn_spaces_eaten = false; 542246074Sgabor repl_could_be_missing = true; 543246074Sgabor repl_missing = false; 544246074Sgabor repl_backtrack_position = 0; 545246074Sgabor repl_patch_line = 0; 546246074Sgabor ptrn_copiable = 0; 547246074Sgabor 548246074Sgabor len = pgets(true); 549246074Sgabor p_input_line++; 550246074Sgabor if (len == 0 || strnNE(buf, "********", 8)) { 551246074Sgabor next_intuit_at(line_beginning, p_input_line); 552246074Sgabor return false; 553246074Sgabor } 554246074Sgabor p_context = 100; 555246074Sgabor p_hunk_beg = p_input_line + 1; 556246074Sgabor while (p_end < p_max) { 557246074Sgabor line_beginning = ftell(pfp); 558246074Sgabor len = pgets(true); 559246074Sgabor p_input_line++; 560246074Sgabor if (len == 0) { 561246074Sgabor if (p_max - p_end < 4) { 562246074Sgabor /* assume blank lines got chopped */ 563246074Sgabor strlcpy(buf, " \n", buf_size); 564246074Sgabor } else { 565246074Sgabor if (repl_beginning && repl_could_be_missing) { 566246074Sgabor repl_missing = true; 567246074Sgabor goto hunk_done; 568246074Sgabor } 569246074Sgabor fatal("unexpected end of file in patch\n"); 570246074Sgabor } 571246074Sgabor } 572246074Sgabor p_end++; 573246074Sgabor if (p_end >= hunkmax) 574246074Sgabor fatal("Internal error: hunk larger than hunk " 575246074Sgabor "buffer size"); 576246074Sgabor p_char[p_end] = *buf; 577246074Sgabor p_line[p_end] = NULL; 578246074Sgabor switch (*buf) { 579246074Sgabor case '*': 580246074Sgabor if (strnEQ(buf, "********", 8)) { 581246074Sgabor if (repl_beginning && repl_could_be_missing) { 582246074Sgabor repl_missing = true; 583246074Sgabor goto hunk_done; 584246074Sgabor } else 585246074Sgabor fatal("unexpected end of hunk " 586246074Sgabor "at line %ld\n", 587246074Sgabor p_input_line); 588246074Sgabor } 589246074Sgabor if (p_end != 0) { 590246074Sgabor if (repl_beginning && repl_could_be_missing) { 591246074Sgabor repl_missing = true; 592246074Sgabor goto hunk_done; 593246074Sgabor } 594246074Sgabor fatal("unexpected *** at line %ld: %s", 595246074Sgabor p_input_line, buf); 596246074Sgabor } 597246074Sgabor context = 0; 598246074Sgabor p_line[p_end] = savestr(buf); 599246074Sgabor if (out_of_mem) { 600246074Sgabor p_end--; 601246074Sgabor return false; 602246074Sgabor } 603246074Sgabor for (s = buf; *s && !isdigit((unsigned char)*s); s++) 604246074Sgabor ; 605246074Sgabor if (!*s) 606246074Sgabor malformed(); 607246074Sgabor if (strnEQ(s, "0,0", 3)) 608246074Sgabor memmove(s, s + 2, strlen(s + 2) + 1); 609246074Sgabor p_first = (LINENUM) atol(s); 610246074Sgabor while (isdigit((unsigned char)*s)) 611246074Sgabor s++; 612246074Sgabor if (*s == ',') { 613246074Sgabor for (; *s && !isdigit((unsigned char)*s); s++) 614246074Sgabor ; 615246074Sgabor if (!*s) 616246074Sgabor malformed(); 617246074Sgabor p_ptrn_lines = ((LINENUM) atol(s)) - p_first + 1; 618246074Sgabor } else if (p_first) 619246074Sgabor p_ptrn_lines = 1; 620246074Sgabor else { 621246074Sgabor p_ptrn_lines = 0; 622246074Sgabor p_first = 1; 623246074Sgabor } 624246074Sgabor 625246074Sgabor /* we need this much at least */ 626246074Sgabor p_max = p_ptrn_lines + 6; 627246074Sgabor while (p_max >= hunkmax) 628246074Sgabor grow_hunkmax(); 629246074Sgabor p_max = hunkmax; 630246074Sgabor break; 631246074Sgabor case '-': 632246074Sgabor if (buf[1] == '-') { 633246074Sgabor if (repl_beginning || 634246074Sgabor (p_end != p_ptrn_lines + 1 + 635246074Sgabor (p_char[p_end - 1] == '\n'))) { 636246074Sgabor if (p_end == 1) { 637246074Sgabor /* 638246074Sgabor * `old' lines were omitted; 639246074Sgabor * set up to fill them in 640246074Sgabor * from 'new' context lines. 641246074Sgabor */ 642246074Sgabor p_end = p_ptrn_lines + 1; 643246074Sgabor fillsrc = p_end + 1; 644246074Sgabor filldst = 1; 645246074Sgabor fillcnt = p_ptrn_lines; 646246074Sgabor } else { 647246074Sgabor if (repl_beginning) { 648246074Sgabor if (repl_could_be_missing) { 649246074Sgabor repl_missing = true; 650246074Sgabor goto hunk_done; 651246074Sgabor } 652246074Sgabor fatal("duplicate \"---\" at line %ld--check line numbers at line %ld\n", 653246074Sgabor p_input_line, p_hunk_beg + repl_beginning); 654246074Sgabor } else { 655246074Sgabor fatal("%s \"---\" at line %ld--check line numbers at line %ld\n", 656246074Sgabor (p_end <= p_ptrn_lines 657246074Sgabor ? "Premature" 658246074Sgabor : "Overdue"), 659246074Sgabor p_input_line, p_hunk_beg); 660246074Sgabor } 661246074Sgabor } 662246074Sgabor } 663246074Sgabor repl_beginning = p_end; 664246074Sgabor repl_backtrack_position = ftell(pfp); 665246074Sgabor repl_patch_line = p_input_line; 666246074Sgabor p_line[p_end] = savestr(buf); 667246074Sgabor if (out_of_mem) { 668246074Sgabor p_end--; 669246074Sgabor return false; 670246074Sgabor } 671246074Sgabor p_char[p_end] = '='; 672246074Sgabor for (s = buf; *s && !isdigit((unsigned char)*s); s++) 673246074Sgabor ; 674246074Sgabor if (!*s) 675246074Sgabor malformed(); 676246074Sgabor p_newfirst = (LINENUM) atol(s); 677246074Sgabor while (isdigit((unsigned char)*s)) 678246074Sgabor s++; 679246074Sgabor if (*s == ',') { 680246074Sgabor for (; *s && !isdigit((unsigned char)*s); s++) 681246074Sgabor ; 682246074Sgabor if (!*s) 683246074Sgabor malformed(); 684246074Sgabor p_repl_lines = ((LINENUM) atol(s)) - 685246074Sgabor p_newfirst + 1; 686246074Sgabor } else if (p_newfirst) 687246074Sgabor p_repl_lines = 1; 688246074Sgabor else { 689246074Sgabor p_repl_lines = 0; 690246074Sgabor p_newfirst = 1; 691246074Sgabor } 692246074Sgabor p_max = p_repl_lines + p_end; 693246074Sgabor if (p_max > MAXHUNKSIZE) 694246074Sgabor fatal("hunk too large (%ld lines) at line %ld: %s", 695246074Sgabor p_max, p_input_line, buf); 696246074Sgabor while (p_max >= hunkmax) 697246074Sgabor grow_hunkmax(); 698246074Sgabor if (p_repl_lines != ptrn_copiable && 699246074Sgabor (p_context != 0 || p_repl_lines != 1)) 700246074Sgabor repl_could_be_missing = false; 701246074Sgabor break; 702246074Sgabor } 703246074Sgabor goto change_line; 704246074Sgabor case '+': 705246074Sgabor case '!': 706246074Sgabor repl_could_be_missing = false; 707246074Sgabor change_line: 708246074Sgabor if (buf[1] == '\n' && canonicalize) 709246074Sgabor strlcpy(buf + 1, " \n", buf_size - 1); 710246074Sgabor if (!isspace((unsigned char)buf[1]) && buf[1] != '>' && 711246074Sgabor buf[1] != '<' && 712246074Sgabor repl_beginning && repl_could_be_missing) { 713246074Sgabor repl_missing = true; 714246074Sgabor goto hunk_done; 715246074Sgabor } 716246074Sgabor if (context >= 0) { 717246074Sgabor if (context < p_context) 718246074Sgabor p_context = context; 719246074Sgabor context = -1000; 720246074Sgabor } 721246074Sgabor p_line[p_end] = savestr(buf + 2); 722246074Sgabor if (out_of_mem) { 723246074Sgabor p_end--; 724246074Sgabor return false; 725246074Sgabor } 726246074Sgabor if (p_end == p_ptrn_lines) { 727246074Sgabor if (remove_special_line()) { 728246074Sgabor int l; 729246074Sgabor 730246074Sgabor l = strlen(p_line[p_end]) - 1; 731246074Sgabor (p_line[p_end])[l] = 0; 732246074Sgabor } 733246074Sgabor } 734246074Sgabor break; 735246074Sgabor case '\t': 736246074Sgabor case '\n': /* assume the 2 spaces got eaten */ 737246074Sgabor if (repl_beginning && repl_could_be_missing && 738246074Sgabor (!ptrn_spaces_eaten || 739246074Sgabor diff_type == NEW_CONTEXT_DIFF)) { 740246074Sgabor repl_missing = true; 741246074Sgabor goto hunk_done; 742246074Sgabor } 743246074Sgabor p_line[p_end] = savestr(buf); 744246074Sgabor if (out_of_mem) { 745246074Sgabor p_end--; 746246074Sgabor return false; 747246074Sgabor } 748246074Sgabor if (p_end != p_ptrn_lines + 1) { 749246074Sgabor ptrn_spaces_eaten |= (repl_beginning != 0); 750246074Sgabor context++; 751246074Sgabor if (!repl_beginning) 752246074Sgabor ptrn_copiable++; 753246074Sgabor p_char[p_end] = ' '; 754246074Sgabor } 755246074Sgabor break; 756246074Sgabor case ' ': 757246074Sgabor if (!isspace((unsigned char)buf[1]) && 758246074Sgabor repl_beginning && repl_could_be_missing) { 759246074Sgabor repl_missing = true; 760246074Sgabor goto hunk_done; 761246074Sgabor } 762246074Sgabor context++; 763246074Sgabor if (!repl_beginning) 764246074Sgabor ptrn_copiable++; 765246074Sgabor p_line[p_end] = savestr(buf + 2); 766246074Sgabor if (out_of_mem) { 767246074Sgabor p_end--; 768246074Sgabor return false; 769246074Sgabor } 770246074Sgabor break; 771246074Sgabor default: 772246074Sgabor if (repl_beginning && repl_could_be_missing) { 773246074Sgabor repl_missing = true; 774246074Sgabor goto hunk_done; 775246074Sgabor } 776246074Sgabor malformed(); 777246074Sgabor } 778246074Sgabor /* set up p_len for strncmp() so we don't have to */ 779246074Sgabor /* assume null termination */ 780246074Sgabor if (p_line[p_end]) 781246074Sgabor p_len[p_end] = strlen(p_line[p_end]); 782246074Sgabor else 783246074Sgabor p_len[p_end] = 0; 784246074Sgabor } 785246074Sgabor 786246074Sgaborhunk_done: 787246074Sgabor if (p_end >= 0 && !repl_beginning) 788246074Sgabor fatal("no --- found in patch at line %ld\n", pch_hunk_beg()); 789246074Sgabor 790246074Sgabor if (repl_missing) { 791246074Sgabor 792246074Sgabor /* reset state back to just after --- */ 793246074Sgabor p_input_line = repl_patch_line; 794246074Sgabor for (p_end--; p_end > repl_beginning; p_end--) 795246074Sgabor free(p_line[p_end]); 796246074Sgabor fseek(pfp, repl_backtrack_position, SEEK_SET); 797246074Sgabor 798246074Sgabor /* redundant 'new' context lines were omitted - set */ 799246074Sgabor /* up to fill them in from the old file context */ 800246074Sgabor if (!p_context && p_repl_lines == 1) { 801246074Sgabor p_repl_lines = 0; 802246074Sgabor p_max--; 803246074Sgabor } 804246074Sgabor fillsrc = 1; 805246074Sgabor filldst = repl_beginning + 1; 806246074Sgabor fillcnt = p_repl_lines; 807246074Sgabor p_end = p_max; 808246074Sgabor } else if (!p_context && fillcnt == 1) { 809246074Sgabor /* the first hunk was a null hunk with no context */ 810246074Sgabor /* and we were expecting one line -- fix it up. */ 811246074Sgabor while (filldst < p_end) { 812246074Sgabor p_line[filldst] = p_line[filldst + 1]; 813246074Sgabor p_char[filldst] = p_char[filldst + 1]; 814246074Sgabor p_len[filldst] = p_len[filldst + 1]; 815246074Sgabor filldst++; 816246074Sgabor } 817246074Sgabor#if 0 818246074Sgabor repl_beginning--; /* this doesn't need to be fixed */ 819246074Sgabor#endif 820246074Sgabor p_end--; 821246074Sgabor p_first++; /* do append rather than insert */ 822246074Sgabor fillcnt = 0; 823246074Sgabor p_ptrn_lines = 0; 824246074Sgabor } 825246074Sgabor if (diff_type == CONTEXT_DIFF && 826246074Sgabor (fillcnt || (p_first > 1 && ptrn_copiable > 2 * p_context))) { 827246074Sgabor if (verbose) 828246074Sgabor say("%s\n%s\n%s\n", 829246074Sgabor "(Fascinating--this is really a new-style context diff but without", 830246074Sgabor "the telltale extra asterisks on the *** line that usually indicate", 831246074Sgabor "the new style...)"); 832246074Sgabor diff_type = NEW_CONTEXT_DIFF; 833246074Sgabor } 834246074Sgabor /* if there were omitted context lines, fill them in now */ 835246074Sgabor if (fillcnt) { 836246074Sgabor p_bfake = filldst; /* remember where not to free() */ 837246074Sgabor p_efake = filldst + fillcnt - 1; 838246074Sgabor while (fillcnt-- > 0) { 839246074Sgabor while (fillsrc <= p_end && p_char[fillsrc] != ' ') 840246074Sgabor fillsrc++; 841246074Sgabor if (fillsrc > p_end) 842246074Sgabor fatal("replacement text or line numbers mangled in hunk at line %ld\n", 843246074Sgabor p_hunk_beg); 844246074Sgabor p_line[filldst] = p_line[fillsrc]; 845246074Sgabor p_char[filldst] = p_char[fillsrc]; 846246074Sgabor p_len[filldst] = p_len[fillsrc]; 847246074Sgabor fillsrc++; 848246074Sgabor filldst++; 849246074Sgabor } 850246074Sgabor while (fillsrc <= p_end && fillsrc != repl_beginning && 851246074Sgabor p_char[fillsrc] != ' ') 852246074Sgabor fillsrc++; 853246074Sgabor#ifdef DEBUGGING 854246074Sgabor if (debug & 64) 855246074Sgabor printf("fillsrc %ld, filldst %ld, rb %ld, e+1 %ld\n", 856246074Sgabor fillsrc, filldst, repl_beginning, p_end + 1); 857246074Sgabor#endif 858246074Sgabor if (fillsrc != p_end + 1 && fillsrc != repl_beginning) 859246074Sgabor malformed(); 860246074Sgabor if (filldst != p_end + 1 && filldst != repl_beginning) 861246074Sgabor malformed(); 862246074Sgabor } 863246074Sgabor if (p_line[p_end] != NULL) { 864246074Sgabor if (remove_special_line()) { 865246074Sgabor p_len[p_end] -= 1; 866246074Sgabor (p_line[p_end])[p_len[p_end]] = 0; 867246074Sgabor } 868246074Sgabor } 869246074Sgabor } else if (diff_type == UNI_DIFF) { 870246074Sgabor LINENUM fillold; /* index of old lines */ 871246074Sgabor LINENUM fillnew; /* index of new lines */ 872246074Sgabor char ch; 873246074Sgabor 874246074Sgabor line_beginning = ftell(pfp); /* file pos of the current line */ 875246074Sgabor len = pgets(true); 876246074Sgabor p_input_line++; 877246074Sgabor if (len == 0 || strnNE(buf, "@@ -", 4)) { 878246074Sgabor next_intuit_at(line_beginning, p_input_line); 879246074Sgabor return false; 880246074Sgabor } 881246074Sgabor s = buf + 4; 882246074Sgabor if (!*s) 883246074Sgabor malformed(); 884246074Sgabor p_first = (LINENUM) atol(s); 885246074Sgabor while (isdigit((unsigned char)*s)) 886246074Sgabor s++; 887246074Sgabor if (*s == ',') { 888246074Sgabor p_ptrn_lines = (LINENUM) atol(++s); 889246074Sgabor while (isdigit((unsigned char)*s)) 890246074Sgabor s++; 891246074Sgabor } else 892246074Sgabor p_ptrn_lines = 1; 893246074Sgabor if (*s == ' ') 894246074Sgabor s++; 895246074Sgabor if (*s != '+' || !*++s) 896246074Sgabor malformed(); 897246074Sgabor p_newfirst = (LINENUM) atol(s); 898246074Sgabor while (isdigit((unsigned char)*s)) 899246074Sgabor s++; 900246074Sgabor if (*s == ',') { 901246074Sgabor p_repl_lines = (LINENUM) atol(++s); 902246074Sgabor while (isdigit((unsigned char)*s)) 903246074Sgabor s++; 904246074Sgabor } else 905246074Sgabor p_repl_lines = 1; 906246074Sgabor if (*s == ' ') 907246074Sgabor s++; 908246074Sgabor if (*s != '@') 909246074Sgabor malformed(); 910246074Sgabor if (!p_ptrn_lines) 911246074Sgabor p_first++; /* do append rather than insert */ 912246074Sgabor p_max = p_ptrn_lines + p_repl_lines + 1; 913246074Sgabor while (p_max >= hunkmax) 914246074Sgabor grow_hunkmax(); 915246074Sgabor fillold = 1; 916246074Sgabor fillnew = fillold + p_ptrn_lines; 917246074Sgabor p_end = fillnew + p_repl_lines; 918246074Sgabor snprintf(buf, buf_size, "*** %ld,%ld ****\n", p_first, 919246074Sgabor p_first + p_ptrn_lines - 1); 920246074Sgabor p_line[0] = savestr(buf); 921246074Sgabor if (out_of_mem) { 922246074Sgabor p_end = -1; 923246074Sgabor return false; 924246074Sgabor } 925246074Sgabor p_char[0] = '*'; 926246074Sgabor snprintf(buf, buf_size, "--- %ld,%ld ----\n", p_newfirst, 927246074Sgabor p_newfirst + p_repl_lines - 1); 928246074Sgabor p_line[fillnew] = savestr(buf); 929246074Sgabor if (out_of_mem) { 930246074Sgabor p_end = 0; 931246074Sgabor return false; 932246074Sgabor } 933246074Sgabor p_char[fillnew++] = '='; 934246074Sgabor p_context = 100; 935246074Sgabor context = 0; 936246074Sgabor p_hunk_beg = p_input_line + 1; 937246074Sgabor while (fillold <= p_ptrn_lines || fillnew <= p_end) { 938246074Sgabor line_beginning = ftell(pfp); 939246074Sgabor len = pgets(true); 940246074Sgabor p_input_line++; 941246074Sgabor if (len == 0) { 942246074Sgabor if (p_max - fillnew < 3) { 943246074Sgabor /* assume blank lines got chopped */ 944246074Sgabor strlcpy(buf, " \n", buf_size); 945246074Sgabor } else { 946246074Sgabor fatal("unexpected end of file in patch\n"); 947246074Sgabor } 948246074Sgabor } 949246074Sgabor if (*buf == '\t' || *buf == '\n') { 950246074Sgabor ch = ' '; /* assume the space got eaten */ 951246074Sgabor s = savestr(buf); 952246074Sgabor } else { 953246074Sgabor ch = *buf; 954246074Sgabor s = savestr(buf + 1); 955246074Sgabor } 956246074Sgabor if (out_of_mem) { 957246074Sgabor while (--fillnew > p_ptrn_lines) 958246074Sgabor free(p_line[fillnew]); 959246074Sgabor p_end = fillold - 1; 960246074Sgabor return false; 961246074Sgabor } 962246074Sgabor switch (ch) { 963246074Sgabor case '-': 964246074Sgabor if (fillold > p_ptrn_lines) { 965246074Sgabor free(s); 966246074Sgabor p_end = fillnew - 1; 967246074Sgabor malformed(); 968246074Sgabor } 969246074Sgabor p_char[fillold] = ch; 970246074Sgabor p_line[fillold] = s; 971246074Sgabor p_len[fillold++] = strlen(s); 972246074Sgabor if (fillold > p_ptrn_lines) { 973246074Sgabor if (remove_special_line()) { 974246074Sgabor p_len[fillold - 1] -= 1; 975246074Sgabor s[p_len[fillold - 1]] = 0; 976246074Sgabor } 977246074Sgabor } 978246074Sgabor break; 979246074Sgabor case '=': 980246074Sgabor ch = ' '; 981246074Sgabor /* FALL THROUGH */ 982246074Sgabor case ' ': 983246074Sgabor if (fillold > p_ptrn_lines) { 984246074Sgabor free(s); 985246074Sgabor while (--fillnew > p_ptrn_lines) 986246074Sgabor free(p_line[fillnew]); 987246074Sgabor p_end = fillold - 1; 988246074Sgabor malformed(); 989246074Sgabor } 990246074Sgabor context++; 991246074Sgabor p_char[fillold] = ch; 992246074Sgabor p_line[fillold] = s; 993246074Sgabor p_len[fillold++] = strlen(s); 994246074Sgabor s = savestr(s); 995246074Sgabor if (out_of_mem) { 996246074Sgabor while (--fillnew > p_ptrn_lines) 997246074Sgabor free(p_line[fillnew]); 998246074Sgabor p_end = fillold - 1; 999246074Sgabor return false; 1000246074Sgabor } 1001246074Sgabor if (fillold > p_ptrn_lines) { 1002246074Sgabor if (remove_special_line()) { 1003246074Sgabor p_len[fillold - 1] -= 1; 1004246074Sgabor s[p_len[fillold - 1]] = 0; 1005246074Sgabor } 1006246074Sgabor } 1007246074Sgabor /* FALL THROUGH */ 1008246074Sgabor case '+': 1009246074Sgabor if (fillnew > p_end) { 1010246074Sgabor free(s); 1011246074Sgabor while (--fillnew > p_ptrn_lines) 1012246074Sgabor free(p_line[fillnew]); 1013246074Sgabor p_end = fillold - 1; 1014246074Sgabor malformed(); 1015246074Sgabor } 1016246074Sgabor p_char[fillnew] = ch; 1017246074Sgabor p_line[fillnew] = s; 1018246074Sgabor p_len[fillnew++] = strlen(s); 1019246074Sgabor if (fillold > p_ptrn_lines) { 1020246074Sgabor if (remove_special_line()) { 1021246074Sgabor p_len[fillnew - 1] -= 1; 1022246074Sgabor s[p_len[fillnew - 1]] = 0; 1023246074Sgabor } 1024246074Sgabor } 1025246074Sgabor break; 1026246074Sgabor default: 1027246074Sgabor p_end = fillnew; 1028246074Sgabor malformed(); 1029246074Sgabor } 1030246074Sgabor if (ch != ' ' && context > 0) { 1031246074Sgabor if (context < p_context) 1032246074Sgabor p_context = context; 1033246074Sgabor context = -1000; 1034246074Sgabor } 1035246074Sgabor } /* while */ 1036246074Sgabor } else { /* normal diff--fake it up */ 1037246074Sgabor char hunk_type; 1038246074Sgabor int i; 1039246074Sgabor LINENUM min, max; 1040246074Sgabor 1041246074Sgabor line_beginning = ftell(pfp); 1042246074Sgabor p_context = 0; 1043246074Sgabor len = pgets(true); 1044246074Sgabor p_input_line++; 1045246074Sgabor if (len == 0 || !isdigit((unsigned char)*buf)) { 1046246074Sgabor next_intuit_at(line_beginning, p_input_line); 1047246074Sgabor return false; 1048246074Sgabor } 1049246074Sgabor p_first = (LINENUM) atol(buf); 1050246074Sgabor for (s = buf; isdigit((unsigned char)*s); s++) 1051246074Sgabor ; 1052246074Sgabor if (*s == ',') { 1053246074Sgabor p_ptrn_lines = (LINENUM) atol(++s) - p_first + 1; 1054246074Sgabor while (isdigit((unsigned char)*s)) 1055246074Sgabor s++; 1056246074Sgabor } else 1057246074Sgabor p_ptrn_lines = (*s != 'a'); 1058246074Sgabor hunk_type = *s; 1059246074Sgabor if (hunk_type == 'a') 1060246074Sgabor p_first++; /* do append rather than insert */ 1061246074Sgabor min = (LINENUM) atol(++s); 1062246074Sgabor for (; isdigit((unsigned char)*s); s++) 1063246074Sgabor ; 1064246074Sgabor if (*s == ',') 1065246074Sgabor max = (LINENUM) atol(++s); 1066246074Sgabor else 1067246074Sgabor max = min; 1068246074Sgabor if (hunk_type == 'd') 1069246074Sgabor min++; 1070246074Sgabor p_end = p_ptrn_lines + 1 + max - min + 1; 1071246074Sgabor if (p_end > MAXHUNKSIZE) 1072246074Sgabor fatal("hunk too large (%ld lines) at line %ld: %s", 1073246074Sgabor p_end, p_input_line, buf); 1074246074Sgabor while (p_end >= hunkmax) 1075246074Sgabor grow_hunkmax(); 1076246074Sgabor p_newfirst = min; 1077246074Sgabor p_repl_lines = max - min + 1; 1078246074Sgabor snprintf(buf, buf_size, "*** %ld,%ld\n", p_first, 1079246074Sgabor p_first + p_ptrn_lines - 1); 1080246074Sgabor p_line[0] = savestr(buf); 1081246074Sgabor if (out_of_mem) { 1082246074Sgabor p_end = -1; 1083246074Sgabor return false; 1084246074Sgabor } 1085246074Sgabor p_char[0] = '*'; 1086246074Sgabor for (i = 1; i <= p_ptrn_lines; i++) { 1087246074Sgabor len = pgets(true); 1088246074Sgabor p_input_line++; 1089246074Sgabor if (len == 0) 1090246074Sgabor fatal("unexpected end of file in patch at line %ld\n", 1091246074Sgabor p_input_line); 1092246074Sgabor if (*buf != '<') 1093246074Sgabor fatal("< expected at line %ld of patch\n", 1094246074Sgabor p_input_line); 1095246074Sgabor p_line[i] = savestr(buf + 2); 1096246074Sgabor if (out_of_mem) { 1097246074Sgabor p_end = i - 1; 1098246074Sgabor return false; 1099246074Sgabor } 1100246074Sgabor p_len[i] = strlen(p_line[i]); 1101246074Sgabor p_char[i] = '-'; 1102246074Sgabor } 1103246074Sgabor 1104246074Sgabor if (remove_special_line()) { 1105246074Sgabor p_len[i - 1] -= 1; 1106246074Sgabor (p_line[i - 1])[p_len[i - 1]] = 0; 1107246074Sgabor } 1108246074Sgabor if (hunk_type == 'c') { 1109246074Sgabor len = pgets(true); 1110246074Sgabor p_input_line++; 1111246074Sgabor if (len == 0) 1112246074Sgabor fatal("unexpected end of file in patch at line %ld\n", 1113246074Sgabor p_input_line); 1114246074Sgabor if (*buf != '-') 1115246074Sgabor fatal("--- expected at line %ld of patch\n", 1116246074Sgabor p_input_line); 1117246074Sgabor } 1118246074Sgabor snprintf(buf, buf_size, "--- %ld,%ld\n", min, max); 1119246074Sgabor p_line[i] = savestr(buf); 1120246074Sgabor if (out_of_mem) { 1121246074Sgabor p_end = i - 1; 1122246074Sgabor return false; 1123246074Sgabor } 1124246074Sgabor p_char[i] = '='; 1125246074Sgabor for (i++; i <= p_end; i++) { 1126246074Sgabor len = pgets(true); 1127246074Sgabor p_input_line++; 1128246074Sgabor if (len == 0) 1129246074Sgabor fatal("unexpected end of file in patch at line %ld\n", 1130246074Sgabor p_input_line); 1131246074Sgabor if (*buf != '>') 1132246074Sgabor fatal("> expected at line %ld of patch\n", 1133246074Sgabor p_input_line); 1134246074Sgabor p_line[i] = savestr(buf + 2); 1135246074Sgabor if (out_of_mem) { 1136246074Sgabor p_end = i - 1; 1137246074Sgabor return false; 1138246074Sgabor } 1139246074Sgabor p_len[i] = strlen(p_line[i]); 1140246074Sgabor p_char[i] = '+'; 1141246074Sgabor } 1142246074Sgabor 1143246074Sgabor if (remove_special_line()) { 1144246074Sgabor p_len[i - 1] -= 1; 1145246074Sgabor (p_line[i - 1])[p_len[i - 1]] = 0; 1146246074Sgabor } 1147246074Sgabor } 1148246074Sgabor if (reverse) /* backwards patch? */ 1149246074Sgabor if (!pch_swap()) 1150246074Sgabor say("Not enough memory to swap next hunk!\n"); 1151246074Sgabor#ifdef DEBUGGING 1152246074Sgabor if (debug & 2) { 1153246074Sgabor int i; 1154246074Sgabor char special; 1155246074Sgabor 1156246074Sgabor for (i = 0; i <= p_end; i++) { 1157246074Sgabor if (i == p_ptrn_lines) 1158246074Sgabor special = '^'; 1159246074Sgabor else 1160246074Sgabor special = ' '; 1161246074Sgabor fprintf(stderr, "%3d %c %c %s", i, p_char[i], 1162246074Sgabor special, p_line[i]); 1163246074Sgabor fflush(stderr); 1164246074Sgabor } 1165246074Sgabor } 1166246074Sgabor#endif 1167246074Sgabor if (p_end + 1 < hunkmax)/* paranoia reigns supreme... */ 1168246074Sgabor p_char[p_end + 1] = '^'; /* add a stopper for apply_hunk */ 1169246074Sgabor return true; 1170246074Sgabor} 1171246074Sgabor 1172246074Sgabor/* 1173246074Sgabor * Input a line from the patch file. 1174246074Sgabor * Worry about indentation if do_indent is true. 1175246074Sgabor * The line is read directly into the buf global variable which 1176246074Sgabor * is resized if necessary in order to hold the complete line. 1177246074Sgabor * Returns the number of characters read including the terminating 1178246074Sgabor * '\n', if any. 1179246074Sgabor */ 1180246074Sgaborsize_t 1181246074Sgaborpgets(bool do_indent) 1182246074Sgabor{ 1183246074Sgabor char *line; 1184246074Sgabor size_t len; 1185246074Sgabor int indent = 0, skipped = 0; 1186246074Sgabor 1187246074Sgabor line = fgetln(pfp, &len); 1188246074Sgabor if (line != NULL) { 1189246074Sgabor if (len + 1 > buf_size) { 1190246074Sgabor while (len + 1 > buf_size) 1191246074Sgabor buf_size *= 2; 1192246074Sgabor free(buf); 1193246074Sgabor buf = malloc(buf_size); 1194246074Sgabor if (buf == NULL) 1195246074Sgabor fatal("out of memory\n"); 1196246074Sgabor } 1197246074Sgabor if (do_indent == 1 && p_indent) { 1198246074Sgabor for (; 1199246074Sgabor indent < p_indent && (*line == ' ' || *line == '\t' || *line == 'X'); 1200246074Sgabor line++, skipped++) { 1201246074Sgabor if (*line == '\t') 1202246074Sgabor indent += 8 - (indent %7); 1203246074Sgabor else 1204246074Sgabor indent++; 1205246074Sgabor } 1206246074Sgabor } 1207246074Sgabor strncpy(buf, line, len - skipped); 1208246074Sgabor buf[len - skipped] = '\0'; 1209246074Sgabor } 1210246074Sgabor return len; 1211246074Sgabor} 1212246074Sgabor 1213246074Sgabor 1214246074Sgabor/* 1215246074Sgabor * Reverse the old and new portions of the current hunk. 1216246074Sgabor */ 1217246074Sgaborbool 1218246074Sgaborpch_swap(void) 1219246074Sgabor{ 1220246074Sgabor char **tp_line; /* the text of the hunk */ 1221246074Sgabor short *tp_len; /* length of each line */ 1222246074Sgabor char *tp_char; /* +, -, and ! */ 1223246074Sgabor LINENUM i; 1224246074Sgabor LINENUM n; 1225246074Sgabor bool blankline = false; 1226246074Sgabor char *s; 1227246074Sgabor 1228246074Sgabor i = p_first; 1229246074Sgabor p_first = p_newfirst; 1230246074Sgabor p_newfirst = i; 1231246074Sgabor 1232246074Sgabor /* make a scratch copy */ 1233246074Sgabor 1234246074Sgabor tp_line = p_line; 1235246074Sgabor tp_len = p_len; 1236246074Sgabor tp_char = p_char; 1237246074Sgabor p_line = NULL; /* force set_hunkmax to allocate again */ 1238246074Sgabor p_len = NULL; 1239246074Sgabor p_char = NULL; 1240246074Sgabor set_hunkmax(); 1241246074Sgabor if (p_line == NULL || p_len == NULL || p_char == NULL) { 1242246074Sgabor 1243246074Sgabor free(p_line); 1244246074Sgabor p_line = tp_line; 1245246074Sgabor free(p_len); 1246246074Sgabor p_len = tp_len; 1247246074Sgabor free(p_char); 1248246074Sgabor p_char = tp_char; 1249246074Sgabor return false; /* not enough memory to swap hunk! */ 1250246074Sgabor } 1251246074Sgabor /* now turn the new into the old */ 1252246074Sgabor 1253246074Sgabor i = p_ptrn_lines + 1; 1254246074Sgabor if (tp_char[i] == '\n') { /* account for possible blank line */ 1255246074Sgabor blankline = true; 1256246074Sgabor i++; 1257246074Sgabor } 1258246074Sgabor if (p_efake >= 0) { /* fix non-freeable ptr range */ 1259246074Sgabor if (p_efake <= i) 1260246074Sgabor n = p_end - i + 1; 1261246074Sgabor else 1262246074Sgabor n = -i; 1263246074Sgabor p_efake += n; 1264246074Sgabor p_bfake += n; 1265246074Sgabor } 1266246074Sgabor for (n = 0; i <= p_end; i++, n++) { 1267246074Sgabor p_line[n] = tp_line[i]; 1268246074Sgabor p_char[n] = tp_char[i]; 1269246074Sgabor if (p_char[n] == '+') 1270246074Sgabor p_char[n] = '-'; 1271246074Sgabor p_len[n] = tp_len[i]; 1272246074Sgabor } 1273246074Sgabor if (blankline) { 1274246074Sgabor i = p_ptrn_lines + 1; 1275246074Sgabor p_line[n] = tp_line[i]; 1276246074Sgabor p_char[n] = tp_char[i]; 1277246074Sgabor p_len[n] = tp_len[i]; 1278246074Sgabor n++; 1279246074Sgabor } 1280246074Sgabor if (p_char[0] != '=') 1281246074Sgabor fatal("Malformed patch at line %ld: expected '=' found '%c'\n", 1282246074Sgabor p_input_line, p_char[0]); 1283246074Sgabor p_char[0] = '*'; 1284246074Sgabor for (s = p_line[0]; *s; s++) 1285246074Sgabor if (*s == '-') 1286246074Sgabor *s = '*'; 1287246074Sgabor 1288246074Sgabor /* now turn the old into the new */ 1289246074Sgabor 1290246074Sgabor if (p_char[0] != '*') 1291246074Sgabor fatal("Malformed patch at line %ld: expected '*' found '%c'\n", 1292246074Sgabor p_input_line, p_char[0]); 1293246074Sgabor tp_char[0] = '='; 1294246074Sgabor for (s = tp_line[0]; *s; s++) 1295246074Sgabor if (*s == '*') 1296246074Sgabor *s = '-'; 1297246074Sgabor for (i = 0; n <= p_end; i++, n++) { 1298246074Sgabor p_line[n] = tp_line[i]; 1299246074Sgabor p_char[n] = tp_char[i]; 1300246074Sgabor if (p_char[n] == '-') 1301246074Sgabor p_char[n] = '+'; 1302246074Sgabor p_len[n] = tp_len[i]; 1303246074Sgabor } 1304246074Sgabor 1305246074Sgabor if (i != p_ptrn_lines + 1) 1306246074Sgabor fatal("Malformed patch at line %ld: expected %ld lines, " 1307246074Sgabor "got %ld\n", 1308246074Sgabor p_input_line, p_ptrn_lines + 1, i); 1309246074Sgabor 1310246074Sgabor i = p_ptrn_lines; 1311246074Sgabor p_ptrn_lines = p_repl_lines; 1312246074Sgabor p_repl_lines = i; 1313246074Sgabor 1314246074Sgabor free(tp_line); 1315246074Sgabor free(tp_len); 1316246074Sgabor free(tp_char); 1317246074Sgabor 1318246074Sgabor return true; 1319246074Sgabor} 1320246074Sgabor 1321246074Sgabor/* 1322246074Sgabor * Return the specified line position in the old file of the old context. 1323246074Sgabor */ 1324246074SgaborLINENUM 1325246074Sgaborpch_first(void) 1326246074Sgabor{ 1327246074Sgabor return p_first; 1328246074Sgabor} 1329246074Sgabor 1330246074Sgabor/* 1331246074Sgabor * Return the number of lines of old context. 1332246074Sgabor */ 1333246074SgaborLINENUM 1334246074Sgaborpch_ptrn_lines(void) 1335246074Sgabor{ 1336246074Sgabor return p_ptrn_lines; 1337246074Sgabor} 1338246074Sgabor 1339246074Sgabor/* 1340246074Sgabor * Return the probable line position in the new file of the first line. 1341246074Sgabor */ 1342246074SgaborLINENUM 1343246074Sgaborpch_newfirst(void) 1344246074Sgabor{ 1345246074Sgabor return p_newfirst; 1346246074Sgabor} 1347246074Sgabor 1348246074Sgabor/* 1349246074Sgabor * Return the number of lines in the replacement text including context. 1350246074Sgabor */ 1351246074SgaborLINENUM 1352246074Sgaborpch_repl_lines(void) 1353246074Sgabor{ 1354246074Sgabor return p_repl_lines; 1355246074Sgabor} 1356246074Sgabor 1357246074Sgabor/* 1358246074Sgabor * Return the number of lines in the whole hunk. 1359246074Sgabor */ 1360246074SgaborLINENUM 1361246074Sgaborpch_end(void) 1362246074Sgabor{ 1363246074Sgabor return p_end; 1364246074Sgabor} 1365246074Sgabor 1366246074Sgabor/* 1367246074Sgabor * Return the number of context lines before the first changed line. 1368246074Sgabor */ 1369246074SgaborLINENUM 1370246074Sgaborpch_context(void) 1371246074Sgabor{ 1372246074Sgabor return p_context; 1373246074Sgabor} 1374246074Sgabor 1375246074Sgabor/* 1376246074Sgabor * Return the length of a particular patch line. 1377246074Sgabor */ 1378246074Sgaborshort 1379246074Sgaborpch_line_len(LINENUM line) 1380246074Sgabor{ 1381246074Sgabor return p_len[line]; 1382246074Sgabor} 1383246074Sgabor 1384246074Sgabor/* 1385246074Sgabor * Return the control character (+, -, *, !, etc) for a patch line. 1386246074Sgabor */ 1387246074Sgaborchar 1388246074Sgaborpch_char(LINENUM line) 1389246074Sgabor{ 1390246074Sgabor return p_char[line]; 1391246074Sgabor} 1392246074Sgabor 1393246074Sgabor/* 1394246074Sgabor * Return a pointer to a particular patch line. 1395246074Sgabor */ 1396246074Sgaborchar * 1397246074Sgaborpfetch(LINENUM line) 1398246074Sgabor{ 1399246074Sgabor return p_line[line]; 1400246074Sgabor} 1401246074Sgabor 1402246074Sgabor/* 1403246074Sgabor * Return where in the patch file this hunk began, for error messages. 1404246074Sgabor */ 1405246074SgaborLINENUM 1406246074Sgaborpch_hunk_beg(void) 1407246074Sgabor{ 1408246074Sgabor return p_hunk_beg; 1409246074Sgabor} 1410246074Sgabor 1411246074Sgabor/* 1412246074Sgabor * Apply an ed script by feeding ed itself. 1413246074Sgabor */ 1414246074Sgaborvoid 1415246074Sgabordo_ed_script(void) 1416246074Sgabor{ 1417246074Sgabor char *t; 1418246074Sgabor long beginning_of_this_line; 1419246074Sgabor FILE *pipefp = NULL; 1420246074Sgabor 1421246074Sgabor if (!skip_rest_of_patch) { 1422246074Sgabor if (copy_file(filearg[0], TMPOUTNAME) < 0) { 1423246074Sgabor unlink(TMPOUTNAME); 1424246074Sgabor fatal("can't create temp file %s", TMPOUTNAME); 1425246074Sgabor } 1426246074Sgabor snprintf(buf, buf_size, "%s%s%s", _PATH_ED, 1427246074Sgabor verbose ? " " : " -s ", TMPOUTNAME); 1428246074Sgabor pipefp = popen(buf, "w"); 1429246074Sgabor } 1430246074Sgabor for (;;) { 1431246074Sgabor beginning_of_this_line = ftell(pfp); 1432246074Sgabor if (pgets(true) == 0) { 1433246074Sgabor next_intuit_at(beginning_of_this_line, p_input_line); 1434246074Sgabor break; 1435246074Sgabor } 1436246074Sgabor p_input_line++; 1437246074Sgabor for (t = buf; isdigit((unsigned char)*t) || *t == ','; t++) 1438246074Sgabor ; 1439246074Sgabor /* POSIX defines allowed commands as {a,c,d,i,s} */ 1440246074Sgabor if (isdigit((unsigned char)*buf) && (*t == 'a' || *t == 'c' || 1441246074Sgabor *t == 'd' || *t == 'i' || *t == 's')) { 1442246074Sgabor if (pipefp != NULL) 1443246074Sgabor fputs(buf, pipefp); 1444246074Sgabor if (*t != 'd') { 1445246074Sgabor while (pgets(true)) { 1446246074Sgabor p_input_line++; 1447246074Sgabor if (pipefp != NULL) 1448246074Sgabor fputs(buf, pipefp); 1449246074Sgabor if (strEQ(buf, ".\n")) 1450246074Sgabor break; 1451246074Sgabor } 1452246074Sgabor } 1453246074Sgabor } else { 1454246074Sgabor next_intuit_at(beginning_of_this_line, p_input_line); 1455246074Sgabor break; 1456246074Sgabor } 1457246074Sgabor } 1458246074Sgabor if (pipefp == NULL) 1459246074Sgabor return; 1460246074Sgabor fprintf(pipefp, "w\n"); 1461246074Sgabor fprintf(pipefp, "q\n"); 1462246074Sgabor fflush(pipefp); 1463246074Sgabor pclose(pipefp); 1464246074Sgabor ignore_signals(); 1465246074Sgabor if (!check_only) { 1466246074Sgabor if (move_file(TMPOUTNAME, outname) < 0) { 1467246074Sgabor toutkeep = true; 1468246074Sgabor chmod(TMPOUTNAME, filemode); 1469246074Sgabor } else 1470246074Sgabor chmod(outname, filemode); 1471246074Sgabor } 1472246074Sgabor set_signals(1); 1473246074Sgabor} 1474246074Sgabor 1475246074Sgabor/* 1476246074Sgabor * Choose the name of the file to be patched based on POSIX rules. 1477246074Sgabor * NOTE: the POSIX rules are amazingly stupid and we only follow them 1478246074Sgabor * if the user specified --posix or set POSIXLY_CORRECT. 1479246074Sgabor */ 1480246074Sgaborstatic char * 1481246074Sgaborposix_name(const struct file_name *names, bool assume_exists) 1482246074Sgabor{ 1483246074Sgabor char *path = NULL; 1484246074Sgabor int i; 1485246074Sgabor 1486246074Sgabor /* 1487246074Sgabor * POSIX states that the filename will be chosen from one 1488246074Sgabor * of the old, new and index names (in that order) if 1489246074Sgabor * the file exists relative to CWD after -p stripping. 1490246074Sgabor */ 1491246074Sgabor for (i = 0; i < MAX_FILE; i++) { 1492246074Sgabor if (names[i].path != NULL && names[i].exists) { 1493246074Sgabor path = names[i].path; 1494246074Sgabor break; 1495246074Sgabor } 1496246074Sgabor } 1497246074Sgabor if (path == NULL && !assume_exists) { 1498246074Sgabor /* 1499246074Sgabor * No files found, look for something we can checkout from 1500246074Sgabor * RCS/SCCS dirs. Same order as above. 1501246074Sgabor */ 1502246074Sgabor for (i = 0; i < MAX_FILE; i++) { 1503246074Sgabor if (names[i].path != NULL && 1504246074Sgabor (path = checked_in(names[i].path)) != NULL) 1505246074Sgabor break; 1506246074Sgabor } 1507246074Sgabor /* 1508246074Sgabor * Still no match? Check to see if the diff could be creating 1509246074Sgabor * a new file. 1510246074Sgabor */ 1511246074Sgabor if (path == NULL && ok_to_create_file && 1512246074Sgabor names[NEW_FILE].path != NULL) 1513246074Sgabor path = names[NEW_FILE].path; 1514246074Sgabor } 1515246074Sgabor 1516246074Sgabor return path ? savestr(path) : NULL; 1517246074Sgabor} 1518246074Sgabor 1519246074Sgabor/* 1520246074Sgabor * Choose the name of the file to be patched based the "best" one 1521246074Sgabor * available. 1522246074Sgabor */ 1523246074Sgaborstatic char * 1524246074Sgaborbest_name(const struct file_name *names, bool assume_exists) 1525246074Sgabor{ 1526246074Sgabor size_t min_components, min_baselen, min_len, tmp; 1527246074Sgabor char *best = NULL; 1528246074Sgabor int i; 1529246074Sgabor 1530246074Sgabor /* 1531246074Sgabor * The "best" name is the one with the fewest number of path 1532246074Sgabor * components, the shortest basename length, and the shortest 1533246074Sgabor * overall length (in that order). We only use the Index: file 1534246074Sgabor * if neither of the old or new files could be intuited from 1535246074Sgabor * the diff header. 1536246074Sgabor */ 1537246074Sgabor min_components = min_baselen = min_len = SIZE_MAX; 1538246074Sgabor for (i = INDEX_FILE; i >= OLD_FILE; i--) { 1539246074Sgabor if (names[i].path == NULL || 1540246074Sgabor (!names[i].exists && !assume_exists)) 1541246074Sgabor continue; 1542246074Sgabor if ((tmp = num_components(names[i].path)) > min_components) 1543246074Sgabor continue; 1544250943Sse if (tmp < min_components) { 1545250943Sse min_components = tmp; 1546250943Sse best = names[i].path; 1547250943Sse } 1548246074Sgabor if ((tmp = strlen(basename(names[i].path))) > min_baselen) 1549246074Sgabor continue; 1550250943Sse if (tmp < min_baselen) { 1551250943Sse min_baselen = tmp; 1552250943Sse best = names[i].path; 1553250943Sse } 1554246074Sgabor if ((tmp = strlen(names[i].path)) > min_len) 1555246074Sgabor continue; 1556246074Sgabor min_len = tmp; 1557246074Sgabor best = names[i].path; 1558246074Sgabor } 1559246074Sgabor if (best == NULL) { 1560246074Sgabor /* 1561246074Sgabor * No files found, look for something we can checkout from 1562246074Sgabor * RCS/SCCS dirs. Logic is identical to that above... 1563246074Sgabor */ 1564246074Sgabor min_components = min_baselen = min_len = SIZE_MAX; 1565246074Sgabor for (i = INDEX_FILE; i >= OLD_FILE; i--) { 1566246074Sgabor if (names[i].path == NULL || 1567246074Sgabor checked_in(names[i].path) == NULL) 1568246074Sgabor continue; 1569246074Sgabor if ((tmp = num_components(names[i].path)) > min_components) 1570246074Sgabor continue; 1571246074Sgabor min_components = tmp; 1572246074Sgabor if ((tmp = strlen(basename(names[i].path))) > min_baselen) 1573246074Sgabor continue; 1574246074Sgabor min_baselen = tmp; 1575246074Sgabor if ((tmp = strlen(names[i].path)) > min_len) 1576246074Sgabor continue; 1577246074Sgabor min_len = tmp; 1578246074Sgabor best = names[i].path; 1579246074Sgabor } 1580246074Sgabor /* 1581246074Sgabor * Still no match? Check to see if the diff could be creating 1582246074Sgabor * a new file. 1583246074Sgabor */ 1584246074Sgabor if (best == NULL && ok_to_create_file && 1585246074Sgabor names[NEW_FILE].path != NULL) 1586246074Sgabor best = names[NEW_FILE].path; 1587246074Sgabor } 1588246074Sgabor 1589246074Sgabor return best ? savestr(best) : NULL; 1590246074Sgabor} 1591246074Sgabor 1592246074Sgaborstatic size_t 1593246074Sgabornum_components(const char *path) 1594246074Sgabor{ 1595246074Sgabor size_t n; 1596246074Sgabor const char *cp; 1597246074Sgabor 1598246074Sgabor for (n = 0, cp = path; (cp = strchr(cp, '/')) != NULL; n++, cp++) { 1599246074Sgabor while (*cp == '/') 1600246074Sgabor cp++; /* skip consecutive slashes */ 1601246074Sgabor } 1602246074Sgabor return n; 1603246074Sgabor} 1604