1 /* inputting files to be patched */ 2 3 /* $Id: inp.c,v 1.18 1997/07/21 17:59:46 eggert Exp $ */ 4 5 /* 6 Copyright 1986, 1988 Larry Wall 7 Copyright 1991, 1992, 1993, 1997 Free Software Foundation, Inc. 8 9 This program is free software; you can redistribute it and/or modify 10 it under the terms of the GNU General Public License as published by 11 the Free Software Foundation; either version 2, or (at your option) 12 any later version. 13 14 This program is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with this program; see the file COPYING. 21 If not, write to the Free Software Foundation, 22 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 23 */ 24 25 #define XTERN extern 26 #include <common.h> 27 #include <backupfile.h> 28 #include <pch.h> 29 #include <util.h> 30 #undef XTERN 31 #define XTERN 32 #include <inp.h> 33 34 /* Input-file-with-indexable-lines abstract type */ 35 36 static char *i_buffer; /* plan A buffer */ 37 static char const **i_ptr; /* pointers to lines in plan A buffer */ 38 39 static size_t tibufsize; /* size of plan b buffers */ 40 #ifndef TIBUFSIZE_MINIMUM 41 #define TIBUFSIZE_MINIMUM (8 * 1024) /* minimum value for tibufsize */ 42 #endif 43 static int tifd = -1; /* plan b virtual string array */ 44 static char *tibuf[2]; /* plan b buffers */ 45 static LINENUM tiline[2] = {-1, -1}; /* 1st line in each buffer */ 46 static LINENUM lines_per_buf; /* how many lines per buffer */ 47 static size_t tireclen; /* length of records in tmp file */ 48 static size_t last_line_size; /* size of last input line */ 49 50 static bool plan_a PARAMS ((char const *));/* yield FALSE if memory runs out */ 51 static void plan_b PARAMS ((char const *)); 52 static void report_revision PARAMS ((int)); 53 static void too_many_lines PARAMS ((char const *)) __attribute__((noreturn)); 54 55 /* New patch--prepare to edit another file. */ 56 57 void 58 re_input() 59 { 60 if (using_plan_a) { 61 free (i_buffer); 62 free (i_ptr); 63 } 64 else { 65 close (tifd); 66 tifd = -1; 67 free(tibuf[0]); 68 tibuf[0] = 0; 69 tiline[0] = tiline[1] = -1; 70 tireclen = 0; 71 } 72 } 73 74 /* Construct the line index, somehow or other. */ 75 76 void 77 scan_input(filename) 78 char *filename; 79 { 80 using_plan_a = ! (debug & 16) && plan_a (filename); 81 if (!using_plan_a) 82 plan_b(filename); 83 switch (verbosity) 84 { 85 case SILENT: 86 break; 87 88 case VERBOSE: 89 say ("Patching file `%s' using Plan %s...\n", 90 filename, using_plan_a ? "A" : "B"); 91 break; 92 93 case DEFAULT_VERBOSITY: 94 say ("patching file `%s'\n", filename); 95 break; 96 } 97 } 98 99 /* Report whether a desired revision was found. */ 100 101 static void 102 report_revision (found_revision) 103 int found_revision; 104 { 105 if (found_revision) 106 { 107 if (verbosity == VERBOSE) 108 say ("Good. This file appears to be the %s version.\n", revision); 109 } 110 else if (force) 111 { 112 if (verbosity != SILENT) 113 say ("Warning: this file doesn't appear to be the %s version -- patching anyway.\n", 114 revision); 115 } 116 else if (batch) 117 { 118 fatal ("This file doesn't appear to be the %s version -- aborting.", 119 revision); 120 } 121 else 122 { 123 ask ("This file doesn't appear to be the %s version -- patch anyway? [n] ", 124 revision); 125 if (*buf != 'y') 126 fatal ("aborted"); 127 } 128 } 129 130 131 static void 132 too_many_lines (filename) 133 char const *filename; 134 { 135 fatal ("File `%s' has too many lines.", filename); 136 } 137 138 139 void 140 get_input_file (filename, outname) 141 char const *filename; 142 char const *outname; 143 { 144 int elsewhere = strcmp (filename, outname); 145 char const *cs; 146 char *diffbuf; 147 char *getbuf; 148 149 if (inerrno == -1) 150 inerrno = stat (inname, &instat) == 0 ? 0 : errno; 151 152 /* Perhaps look for RCS or SCCS versions. */ 153 if (patch_get 154 && invc != 0 155 && (inerrno 156 || (! elsewhere 157 && (/* No one can write to it. */ 158 (instat.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)) == 0 159 /* Only the owner (who's not me) can write to it. */ 160 || ((instat.st_mode & (S_IWGRP|S_IWOTH)) == 0 161 && instat.st_uid != geteuid ())))) 162 && (invc = !! (cs = (version_controller 163 (filename, elsewhere, 164 inerrno ? (struct stat *) 0 : &instat, 165 &getbuf, &diffbuf))))) { 166 167 if (!inerrno) { 168 if (!elsewhere 169 && (instat.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)) != 0) 170 /* Somebody can write to it. */ 171 fatal ("file `%s' seems to be locked by somebody else under %s", 172 filename, cs); 173 /* It might be checked out unlocked. See if it's safe to 174 check out the default version locked. */ 175 if (verbosity == VERBOSE) 176 say ("Comparing file `%s' to default %s version...\n", 177 filename, cs); 178 if (systemic (diffbuf) != 0) 179 { 180 say ("warning: patching file `%s', which does not match default %s version\n", 181 filename, cs); 182 cs = 0; 183 } 184 } 185 186 if (cs && version_get (filename, cs, ! inerrno, elsewhere, getbuf, 187 &instat)) 188 inerrno = 0; 189 190 free (getbuf); 191 free (diffbuf); 192 193 } else if (inerrno && !pch_says_nonexistent (reverse)) 194 { 195 errno = inerrno; 196 pfatal ("can't find file `%s'", filename); 197 } 198 199 if (inerrno) 200 { 201 instat.st_mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH; 202 instat.st_size = 0; 203 } 204 else if (! S_ISREG (instat.st_mode)) 205 fatal ("`%s' is not a regular file -- can't patch", filename); 206 } 207 208 209 /* Try keeping everything in memory. */ 210 211 static bool 212 plan_a(filename) 213 char const *filename; 214 { 215 register char const *s; 216 register char const *lim; 217 register char const **ptr; 218 register char *buffer; 219 register LINENUM iline; 220 size_t size = instat.st_size; 221 222 /* Fail if the file size doesn't fit in a size_t, 223 or if storage isn't available. */ 224 if (! (size == instat.st_size 225 && (buffer = malloc (size ? size : (size_t) 1)))) 226 return FALSE; 227 228 /* Read the input file, but don't bother reading it if it's empty. 229 When creating files, the files do not actually exist. */ 230 if (size) 231 { 232 int ifd = open (filename, O_RDONLY|binary_transput); 233 size_t buffered = 0, n; 234 if (ifd < 0) 235 pfatal ("can't open file `%s'", filename); 236 237 while (size - buffered != 0) 238 { 239 n = read (ifd, buffer + buffered, size - buffered); 240 if (n == 0) 241 { 242 /* Some non-POSIX hosts exaggerate st_size in text mode; 243 or the file may have shrunk! */ 244 size = buffered; 245 break; 246 } 247 if (n == (size_t) -1) 248 { 249 /* Perhaps size is too large for this host. */ 250 close (ifd); 251 free (buffer); 252 return FALSE; 253 } 254 buffered += n; 255 } 256 257 if (close (ifd) != 0) 258 read_fatal (); 259 } 260 261 /* Scan the buffer and build array of pointers to lines. */ 262 lim = buffer + size; 263 iline = 3; /* 1 unused, 1 for SOF, 1 for EOF if last line is incomplete */ 264 for (s = buffer; (s = (char *) memchr (s, '\n', lim - s)); s++) 265 if (++iline < 0) 266 too_many_lines (filename); 267 if (! (iline == (size_t) iline 268 && (size_t) iline * sizeof *ptr / sizeof *ptr == (size_t) iline 269 && (ptr = (char const **) malloc ((size_t) iline * sizeof *ptr)))) 270 { 271 free (buffer); 272 return FALSE; 273 } 274 iline = 0; 275 for (s = buffer; ; s++) 276 { 277 ptr[++iline] = s; 278 if (! (s = (char *) memchr (s, '\n', lim - s))) 279 break; 280 } 281 if (size && lim[-1] != '\n') 282 ptr[++iline] = lim; 283 input_lines = iline - 1; 284 285 if (revision) 286 { 287 char const *rev = revision; 288 int rev0 = rev[0]; 289 int found_revision = 0; 290 size_t revlen = strlen (rev); 291 292 if (revlen <= size) 293 { 294 char const *limrev = lim - revlen; 295 296 for (s = buffer; (s = (char *) memchr (s, rev0, limrev - s)); s++) 297 if (memcmp (s, rev, revlen) == 0 298 && (s == buffer || ISSPACE ((unsigned char) s[-1])) 299 && (s + 1 == limrev || ISSPACE ((unsigned char) s[revlen]))) 300 { 301 found_revision = 1; 302 break; 303 } 304 } 305 306 report_revision (found_revision); 307 } 308 309 /* Plan A will work. */ 310 i_buffer = buffer; 311 i_ptr = ptr; 312 return TRUE; 313 } 314 315 /* Keep (virtually) nothing in memory. */ 316 317 static void 318 plan_b(filename) 319 char const *filename; 320 { 321 register FILE *ifp; 322 register int c; 323 register size_t len; 324 register size_t maxlen; 325 register int found_revision; 326 register size_t i; 327 register char const *rev; 328 register size_t revlen; 329 register LINENUM line = 1; 330 331 if (instat.st_size == 0) 332 filename = NULL_DEVICE; 333 if (! (ifp = fopen (filename, binary_transput ? "rb" : "r"))) 334 pfatal ("can't open file `%s'", filename); 335 tifd = create_file (TMPINNAME, O_RDWR | O_BINARY, (mode_t) 0); 336 i = 0; 337 len = 0; 338 maxlen = 1; 339 rev = revision; 340 found_revision = !rev; 341 revlen = rev ? strlen (rev) : 0; 342 343 while ((c = getc (ifp)) != EOF) 344 { 345 len++; 346 347 if (c == '\n') 348 { 349 if (++line < 0) 350 too_many_lines (filename); 351 if (maxlen < len) 352 maxlen = len; 353 len = 0; 354 } 355 356 if (!found_revision) 357 { 358 if (i == revlen) 359 { 360 found_revision = ISSPACE ((unsigned char) c); 361 i = (size_t) -1; 362 } 363 else if (i != (size_t) -1) 364 i = rev[i]==c ? i + 1 : (size_t) -1; 365 366 if (i == (size_t) -1 && ISSPACE ((unsigned char) c)) 367 i = 0; 368 } 369 } 370 371 if (revision) 372 report_revision (found_revision); 373 Fseek (ifp, (off_t) 0, SEEK_SET); /* rewind file */ 374 for (tibufsize = TIBUFSIZE_MINIMUM; tibufsize < maxlen; tibufsize <<= 1) 375 continue; 376 lines_per_buf = tibufsize / maxlen; 377 tireclen = maxlen; 378 tibuf[0] = xmalloc (2 * tibufsize); 379 tibuf[1] = tibuf[0] + tibufsize; 380 381 for (line = 1; ; line++) 382 { 383 char *p = tibuf[0] + maxlen * (line % lines_per_buf); 384 char const *p0 = p; 385 if (! (line % lines_per_buf)) /* new block */ 386 if (write (tifd, tibuf[0], tibufsize) != tibufsize) 387 write_fatal (); 388 if ((c = getc (ifp)) == EOF) 389 break; 390 391 for (;;) 392 { 393 *p++ = c; 394 if (c == '\n') 395 { 396 last_line_size = p - p0; 397 break; 398 } 399 400 if ((c = getc (ifp)) == EOF) 401 { 402 last_line_size = p - p0; 403 line++; 404 goto EOF_reached; 405 } 406 } 407 } 408 EOF_reached: 409 if (ferror (ifp) || fclose (ifp) != 0) 410 read_fatal (); 411 412 if (line % lines_per_buf != 0) 413 if (write (tifd, tibuf[0], tibufsize) != tibufsize) 414 write_fatal (); 415 input_lines = line - 1; 416 } 417 418 /* Fetch a line from the input file. */ 419 420 char const * 421 ifetch (line, whichbuf, psize) 422 register LINENUM line; 423 int whichbuf; /* ignored when file in memory */ 424 size_t *psize; 425 { 426 register char const *q; 427 register char const *p; 428 429 if (line < 1 || line > input_lines) { 430 *psize = 0; 431 return ""; 432 } 433 if (using_plan_a) { 434 p = i_ptr[line]; 435 *psize = i_ptr[line + 1] - p; 436 return p; 437 } else { 438 LINENUM offline = line % lines_per_buf; 439 LINENUM baseline = line - offline; 440 441 if (tiline[0] == baseline) 442 whichbuf = 0; 443 else if (tiline[1] == baseline) 444 whichbuf = 1; 445 else { 446 tiline[whichbuf] = baseline; 447 if (lseek (tifd, (off_t) (baseline/lines_per_buf * tibufsize), 448 SEEK_SET) == -1 449 || read (tifd, tibuf[whichbuf], tibufsize) < 0) 450 read_fatal (); 451 } 452 p = tibuf[whichbuf] + (tireclen*offline); 453 if (line == input_lines) 454 *psize = last_line_size; 455 else { 456 for (q = p; *q++ != '\n'; ) 457 continue; 458 *psize = q - p; 459 } 460 return p; 461 } 462 } 463