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