1/* $FreeBSD$ 2 * 3 * $Log: inp.c,v $ 4 * Revision 2.0.1.1 88/06/03 15:06:13 lwall 5 * patch10: made a little smarter about sccs files 6 * 7 * Revision 2.0 86/09/17 15:37:02 lwall 8 * Baseline for netwide release. 9 * 10 */ 11 12#include "EXTERN.h" 13#include "common.h" 14#include "util.h" 15#include "pch.h" 16#include "INTERN.h" 17#include "inp.h" 18 19/* Input-file-with-indexable-lines abstract type. */ 20 21static long i_size; /* Size of the input file */ 22static char *i_womp; /* Plan a buffer for entire file */ 23static char **i_ptr; /* Pointers to lines in i_womp */ 24 25static int tifd = -1; /* Plan b virtual string array */ 26static char *tibuf[2]; /* Plan b buffers */ 27static LINENUM tiline[2] = {-1, -1}; /* 1st line in each buffer */ 28static LINENUM lines_per_buf; /* How many lines per buffer */ 29static int tireclen; /* Length of records in tmp file */ 30 31/* 32 * New patch--prepare to edit another file. 33 */ 34void 35re_input(void) 36{ 37 if (using_plan_a) { 38 i_size = 0; 39#ifndef lint 40 if (i_ptr != Null(char**)) 41 free((char *)i_ptr); 42#endif 43 if (i_womp != Nullch) 44 free(i_womp); 45 i_womp = Nullch; 46 i_ptr = Null(char **); 47 } else { 48 using_plan_a = TRUE; /* maybe the next one is smaller */ 49 Close(tifd); 50 tifd = -1; 51 free(tibuf[0]); 52 free(tibuf[1]); 53 tibuf[0] = tibuf[1] = Nullch; 54 tiline[0] = tiline[1] = -1; 55 tireclen = 0; 56 } 57} 58 59/* 60 * Constuct the line index, somehow or other. 61 */ 62void 63scan_input(char *filename) 64{ 65 if (!plan_a(filename)) 66 plan_b(filename); 67 if (verbose) { 68 say3("Patching file %s using Plan %s...\n", filename, 69 (using_plan_a ? "A" : "B") ); 70 } 71} 72 73/* 74 * Try keeping everything in memory. 75 */ 76bool 77plan_a(char *filename) 78{ 79 int ifd, statfailed; 80 Reg1 char *s; 81 Reg2 LINENUM iline; 82 char lbuf[INITLINELEN]; 83 int output_elsewhere = strcmp(filename, outname); 84 extern int check_patch; 85 86 statfailed = stat(filename, &filestat); 87 if (statfailed && ok_to_create_file) { 88 if (verbose) 89 say2("(Creating file %s...)\n",filename); 90 if (check_patch) 91 return TRUE; 92 makedirs(filename, TRUE); 93 close(creat(filename, 0666)); 94 statfailed = stat(filename, &filestat); 95 } 96 if (statfailed && check_patch) { 97 fatal2("%s not found and in check_patch mode.", filename); 98 } 99 /* 100 * For nonexistent or read-only files, look for RCS or SCCS 101 * versions. 102 */ 103 if (statfailed || 104 (! output_elsewhere && 105 (/* No one can write to it. */ 106 (filestat.st_mode & 0222) == 0 || 107 /* I can't write to it. */ 108 ((filestat.st_mode & 0022) == 0 && filestat.st_uid != myuid)))) { 109 struct stat cstat; 110 char *cs = Nullch; 111 char *filebase; 112 int pathlen; 113 114 filebase = basename(filename); 115 pathlen = filebase - filename; 116 117 /* 118 * Put any leading path into `s'. 119 * Leave room in lbuf for the diff command. 120 */ 121 s = lbuf + 20; 122 strncpy(s, filename, pathlen); 123 124#define try(f,a1,a2) (Sprintf(s + pathlen, f, a1, a2), stat(s, &cstat) == 0) 125 if ((try("RCS/%s%s", filebase, RCSSUFFIX) || 126 try("RCS/%s%s", filebase, "") || 127 try("%s%s", filebase, RCSSUFFIX)) && 128 /* 129 * Check that RCS file is not working file. 130 * Some hosts don't report file name length errors. 131 */ 132 (statfailed || 133 ((filestat.st_dev ^ cstat.st_dev) | 134 (filestat.st_ino ^ cstat.st_ino)))) { 135 Sprintf(buf, output_elsewhere?CHECKOUT:CHECKOUT_LOCKED, filename); 136 Sprintf(lbuf, RCSDIFF, filename); 137 cs = "RCS"; 138 } else if (try("SCCS/%s%s", SCCSPREFIX, filebase) || 139 try("%s%s", SCCSPREFIX, filebase)) { 140 Sprintf(buf, output_elsewhere?GET:GET_LOCKED, s); 141 Sprintf(lbuf, SCCSDIFF, s, filename); 142 cs = "SCCS"; 143 } else if (statfailed) 144 fatal2("can't find %s\n", filename); 145 /* 146 * else we can't write to it but it's not under a version 147 * control system, so just proceed. 148 */ 149 if (cs) { 150 if (!statfailed) { 151 if ((filestat.st_mode & 0222) != 0) 152 /* The owner can write to it. */ 153 fatal3( 154"file %s seems to be locked by somebody else under %s\n", 155 filename, cs); 156 /* 157 * It might be checked out unlocked. See if 158 * it's safe to check out the default version 159 * locked. 160 */ 161 if (verbose) 162 say3( 163"Comparing file %s to default %s version...\n", 164 filename, cs); 165 if (system(lbuf)) 166 fatal3( 167"can't check out file %s: differs from default %s version\n", 168 filename, cs); 169 } 170 if (verbose) 171 say3("Checking out file %s from %s...\n", filename, cs); 172 if (system(buf) || stat(filename, &filestat)) 173 fatal3("can't check out file %s from %s\n", 174 filename, cs); 175 } 176 } 177 filemode = filestat.st_mode; 178 if (!S_ISREG(filemode)) 179 fatal2("%s is not a normal file--can't patch\n", filename); 180 i_size = filestat.st_size; 181 if (out_of_mem) { 182 set_hunkmax(); /* make sure dynamic arrays are allocated */ 183 out_of_mem = FALSE; 184 return FALSE; /* force plan b because plan a bombed */ 185 } 186#ifdef lint 187 i_womp = Nullch; 188#else 189 /* 190 * Lint says this may alloc less than i_size, 191 * but that's okay, I think. 192 */ 193 i_womp = malloc((MEM)(i_size + 2)); 194#endif 195 if (i_womp == Nullch) 196 return FALSE; 197 if ((ifd = open(filename, 0)) < 0) 198 pfatal2("can't open file %s", filename); 199#ifndef lint 200 if (read(ifd, i_womp, (int)i_size) != i_size) { 201 /* 202 * This probably means i_size > 15 or 16 bits worth. At 203 * this point it doesn't matter if i_womp was undersized. 204 */ 205 Close(ifd); 206 free(i_womp); 207 return FALSE; 208 } 209#endif 210 Close(ifd); 211 if (i_size && i_womp[i_size - 1] != '\n') 212 i_womp[i_size++] = '\n'; 213 i_womp[i_size] = '\0'; 214 215 /* 216 * Count the lines in the buffer so we know how many pointers we 217 * need. 218 */ 219 iline = 0; 220 for (s = i_womp; *s; s++) { 221 if (*s == '\n') 222 iline++; 223 } 224#ifdef lint 225 i_ptr = Null(char**); 226#else 227 i_ptr = (char **)malloc((MEM)((iline + 2) * sizeof(char *))); 228#endif 229 if (i_ptr == Null(char **)) { /* shucks, it was a near thing */ 230 free((char *)i_womp); 231 return FALSE; 232 } 233 234 /* Now scan the buffer and build pointer array. */ 235 iline = 1; 236 i_ptr[iline] = i_womp; 237 for (s = i_womp; *s; s++) { 238 if (*s == '\n') { 239 /* These are NOT null terminated. */ 240 i_ptr[++iline] = s + 1; 241 } 242 } 243 input_lines = iline - 1; 244 245 /* Now check for revision, if any. */ 246 if (revision != Nullch) { 247 if (!rev_in_string(i_womp)) { 248 if (force) { 249 if (verbose) 250 say2( 251"Warning: this file doesn't appear to be the %s version--patching anyway.\n", 252 revision); 253 } else if (batch) { 254 fatal2( 255"this file doesn't appear to be the %s version--aborting.\n", revision); 256 } else { 257 (void) ask2( 258"This file doesn't appear to be the %s version--patch anyway? [n] ", 259 revision); 260 if (*buf != 'y') 261 fatal1("aborted\n"); 262 } 263 } else if (verbose) 264 say2("Good. This file appears to be the %s version.\n", 265 revision); 266 } 267 268 return TRUE; /* Plan a will work. */ 269} 270 271/* 272 * Keep (virtually) nothing in memory. 273 */ 274void 275plan_b(char *filename) 276{ 277 Reg3 FILE *ifp; 278 Reg1 int i = 0; 279 Reg2 int maxlen = 1; 280 Reg4 bool found_revision = (revision == Nullch); 281 282 using_plan_a = FALSE; 283 if ((ifp = fopen(filename, "r")) == Nullfp) 284 pfatal2("can't open file %s", filename); 285 if ((tifd = creat(TMPINNAME, 0666)) < 0) 286 pfatal2("can't open file %s", TMPINNAME); 287 while (fgets(buf, buf_size, ifp) != Nullch) { 288 if (revision != Nullch && !found_revision && rev_in_string(buf)) 289 found_revision = TRUE; 290 if ((i = strlen(buf)) > maxlen) 291 maxlen = i; /* Find longest line. */ 292 } 293 if (revision != Nullch) { 294 if (!found_revision) { 295 if (force) { 296 if (verbose) 297 say2( 298"Warning: this file doesn't appear to be the %s version--patching anyway.\n", 299 revision); 300 } else if (batch) { 301 fatal2( 302"this file doesn't appear to be the %s version--aborting.\n", revision); 303 } else { 304 (void) ask2( 305"This file doesn't appear to be the %s version--patch anyway? [n] ", 306 revision); 307 if (*buf != 'y') 308 fatal1("aborted\n"); 309 } 310 } else if (verbose) 311 say2("Good. This file appears to be the %s version.\n", 312 revision); 313 } 314 Fseek(ifp, 0L, 0); /* Rewind file. */ 315 lines_per_buf = BUFFERSIZE / maxlen; 316 tireclen = maxlen; 317 tibuf[0] = malloc((MEM)(BUFFERSIZE + 1)); 318 tibuf[1] = malloc((MEM)(BUFFERSIZE + 1)); 319 if (tibuf[1] == Nullch) 320 fatal1("out of memory\n"); 321 for (i = 1; ; i++) { 322 if (! (i % lines_per_buf)) /* New block. */ 323 if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE) 324 pfatal1("can't write temp file"); 325 if (fgets(tibuf[0] + maxlen * (i%lines_per_buf), 326 maxlen + 1, ifp) == Nullch) { 327 input_lines = i - 1; 328 if (i % lines_per_buf) 329 if (write(tifd, tibuf[0], BUFFERSIZE) < 330 BUFFERSIZE) 331 pfatal1("can't write temp file"); 332 break; 333 } 334 } 335 Fclose(ifp); 336 Close(tifd); 337 if ((tifd = open(TMPINNAME, 0)) < 0) { 338 pfatal2("can't reopen file %s", TMPINNAME); 339 } 340} 341 342/* 343 * Fetch a line from the input file, \n terminated, not necessarily \0. 344 */ 345char * 346ifetch(line,whichbuf) 347Reg1 LINENUM line; 348int whichbuf; /* ignored when file in memory */ 349{ 350 if (line < 1 || line > input_lines) 351 return ""; 352 if (using_plan_a) 353 return i_ptr[line]; 354 else { 355 LINENUM offline = line % lines_per_buf; 356 LINENUM baseline = line - offline; 357 358 if (tiline[0] == baseline) 359 whichbuf = 0; 360 else if (tiline[1] == baseline) 361 whichbuf = 1; 362 else { 363 tiline[whichbuf] = baseline; 364#ifndef lint /* complains of long accuracy */ 365 Lseek(tifd, (long)baseline / lines_per_buf * BUFFERSIZE, 0); 366#endif 367 if (read(tifd, tibuf[whichbuf], BUFFERSIZE) < 0) 368 pfatal2("error reading tmp file %s", TMPINNAME); 369 } 370 return tibuf[whichbuf] + (tireclen * offline); 371 } 372} 373 374/* 375 * True if the string argument contains the revision number we want. 376 */ 377bool 378rev_in_string(char *string) 379{ 380 Reg1 char *s; 381 Reg2 int patlen; 382 383 if (revision == Nullch) 384 return TRUE; 385 patlen = strlen(revision); 386 if (strnEQ(string,revision,patlen) && 387 isspace((unsigned char)string[patlen])) 388 return TRUE; 389 for (s = string; *s; s++) { 390 if (isspace((unsigned char)*s) && 391 strnEQ(s + 1, revision, patlen) && 392 isspace((unsigned char)s[patlen + 1] )) { 393 return TRUE; 394 } 395 } 396 return FALSE; 397} 398