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