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