1 /* $NetBSD: inp.c,v 1.6 1999/02/09 05:15:45 sommerfe Exp $ */ 2 #include <sys/cdefs.h> 3 #ifndef lint 4 __RCSID("$NetBSD: inp.c,v 1.6 1999/02/09 05:15:45 sommerfe 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 #include <stdlib.h> 15 #include <unistd.h> 16 #include <fcntl.h> 17 18 /* Input-file-with-indexable-lines abstract type */ 19 20 static long i_size; /* size of the input file */ 21 static char *i_womp; /* plan a buffer for entire file */ 22 static char **i_ptr; /* pointers to lines in i_womp */ 23 24 static int tifd = -1; /* plan b virtual string array */ 25 static char *tibuf[2]; /* plan b buffers */ 26 static LINENUM tiline[2] = {-1, -1}; /* 1st line in each buffer */ 27 static LINENUM lines_per_buf; /* how many lines per buffer */ 28 static int tireclen; /* length of records in tmp file */ 29 30 /* New patch--prepare to edit another file. */ 31 32 void 33 re_input() 34 { 35 if (using_plan_a) { 36 i_size = 0; 37 #ifndef lint 38 if (i_ptr != Null(char**)) 39 free((char *)i_ptr); 40 #endif 41 if (i_womp != Nullch) 42 free(i_womp); 43 i_womp = Nullch; 44 i_ptr = Null(char **); 45 } 46 else { 47 using_plan_a = TRUE; /* maybe the next one is smaller */ 48 Close(tifd); 49 tifd = -1; 50 free(tibuf[0]); 51 free(tibuf[1]); 52 tibuf[0] = tibuf[1] = Nullch; 53 tiline[0] = tiline[1] = -1; 54 tireclen = 0; 55 } 56 } 57 58 /* Constuct the line index, somehow or other. */ 59 60 void 61 scan_input(filename) 62 char *filename; 63 { 64 if (!plan_a(filename)) 65 plan_b(filename); 66 if (verbose) { 67 say3("Patching file %s using Plan %s...\n", filename, 68 (using_plan_a ? "A" : "B") ); 69 } 70 } 71 72 /* Try keeping everything in memory. */ 73 74 bool 75 plan_a(filename) 76 char *filename; 77 { 78 int ifd, statfailed; 79 Reg1 char *s; 80 Reg2 LINENUM iline; 81 char lbuf[MAXLINELEN]; 82 83 statfailed = stat(filename, &filestat); 84 if (statfailed && ok_to_create_file) { 85 if (verbose) 86 say2("(Creating file %s...)\n",filename); 87 makedirs(filename, TRUE); 88 close(creat(filename, 0666)); 89 statfailed = stat(filename, &filestat); 90 } 91 /* For nonexistent or read-only files, look for RCS or SCCS versions. */ 92 if (statfailed 93 /* No one can write to it. */ 94 || (filestat.st_mode & 0222) == 0 95 /* I can't write to it. */ 96 || ((filestat.st_mode & 0022) == 0 && filestat.st_uid != myuid)) { 97 struct stat cstat; 98 char *cs = Nullch; 99 char *filebase; 100 int pathlen; 101 102 filebase = basename(filename); 103 pathlen = filebase - filename; 104 105 /* Put any leading path into `s'. 106 Leave room in lbuf for the diff command. */ 107 s = lbuf + 20; 108 strncpy(s, filename, pathlen); 109 110 #define try(f, a1, a2) (Sprintf(s + pathlen, f, a1, a2), stat(s, &cstat) == 0) 111 #define try1(f, a1) (Sprintf(s + pathlen, f, a1), stat(s, &cstat) == 0) 112 if ( try("RCS/%s%s", filebase, RCSSUFFIX) 113 || try1("RCS/%s" , filebase) 114 || try( "%s%s", filebase, RCSSUFFIX)) { 115 Sprintf(buf, CHECKOUT, filename); 116 Sprintf(lbuf, RCSDIFF, filename); 117 cs = "RCS"; 118 } else if ( try("SCCS/%s%s", SCCSPREFIX, filebase) 119 || try( "%s%s", SCCSPREFIX, filebase)) { 120 Sprintf(buf, GET, s); 121 Sprintf(lbuf, SCCSDIFF, s, filename); 122 cs = "SCCS"; 123 } else if (statfailed) 124 fatal2("can't find %s\n", filename); 125 /* else we can't write to it but it's not under a version 126 control system, so just proceed. */ 127 if (cs) { 128 if (!statfailed) { 129 if ((filestat.st_mode & 0222) != 0) 130 /* The owner can write to it. */ 131 fatal3("file %s seems to be locked by somebody else under %s\n", 132 filename, cs); 133 /* It might be checked out unlocked. See if it's safe to 134 check out the default version locked. */ 135 if (verbose) 136 say3("Comparing file %s to default %s version...\n", 137 filename, cs); 138 if (system(lbuf)) 139 fatal3("can't check out file %s: differs from default %s version\n", 140 filename, cs); 141 } 142 if (verbose) 143 say3("Checking out file %s from %s...\n", filename, cs); 144 if (system(buf) || stat(filename, &filestat)) 145 fatal3("can't check out file %s from %s\n", filename, cs); 146 } 147 } 148 if (old_file_is_dev_null && ok_to_create_file && (filestat.st_size != 0)) { 149 fatal2("patch creates new file but existing file %s not empty\n", 150 filename); 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, 0)) < 0) 171 pfatal2("can't open file %s", filename); 172 #ifndef lint 173 if (read(ifd, i_womp, (int)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 if ((tifd = creat(TMPINNAME, 0666)) < 0) 255 pfatal2("can't open file %s", TMPINNAME); 256 while (fgets(buf, sizeof buf, ifp) != Nullch) { 257 if (revision != Nullch && !found_revision && rev_in_string(buf)) 258 found_revision = TRUE; 259 if ((i = strlen(buf)) > maxlen) 260 maxlen = i; /* find longest line */ 261 } 262 if (revision != Nullch) { 263 if (!found_revision) { 264 if (force) { 265 if (verbose) 266 say2( 267 "Warning: this file doesn't appear to be the %s version--patching anyway.\n", 268 revision); 269 } 270 else if (batch) { 271 fatal2( 272 "this file doesn't appear to be the %s version--aborting.\n", revision); 273 } 274 else { 275 ask2( 276 "This file doesn't appear to be the %s version--patch anyway? [n] ", 277 revision); 278 if (*buf != 'y') 279 fatal1("aborted\n"); 280 } 281 } 282 else if (verbose) 283 say2("Good. This file appears to be the %s version.\n", 284 revision); 285 } 286 Fseek(ifp, 0L, 0); /* rewind file */ 287 lines_per_buf = BUFFERSIZE / maxlen; 288 tireclen = maxlen; 289 tibuf[0] = malloc((MEM)(BUFFERSIZE + 1)); 290 tibuf[1] = malloc((MEM)(BUFFERSIZE + 1)); 291 if (tibuf[1] == Nullch) 292 fatal1("out of memory\n"); 293 for (i=1; ; i++) { 294 if (! (i % lines_per_buf)) /* new block */ 295 if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE) 296 pfatal1("can't write temp file"); 297 if (fgets(tibuf[0] + maxlen * (i%lines_per_buf), maxlen + 1, ifp) 298 == Nullch) { 299 input_lines = i - 1; 300 if (i % lines_per_buf) 301 if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE) 302 pfatal1("can't write temp file"); 303 break; 304 } 305 } 306 Fclose(ifp); 307 Close(tifd); 308 if ((tifd = open(TMPINNAME, 0)) < 0) { 309 pfatal2("can't reopen file %s", TMPINNAME); 310 } 311 } 312 313 /* Fetch a line from the input file, \n terminated, not necessarily \0. */ 314 315 char * 316 ifetch(line,whichbuf) 317 Reg1 LINENUM line; 318 int whichbuf; /* ignored when file in memory */ 319 { 320 if (line < 1 || line > input_lines) 321 return ""; 322 if (using_plan_a) 323 return i_ptr[line]; 324 else { 325 LINENUM offline = line % lines_per_buf; 326 LINENUM baseline = line - offline; 327 328 if (tiline[0] == baseline) 329 whichbuf = 0; 330 else if (tiline[1] == baseline) 331 whichbuf = 1; 332 else { 333 tiline[whichbuf] = baseline; 334 #ifndef lint /* complains of long accuracy */ 335 Lseek(tifd, (long)baseline / lines_per_buf * BUFFERSIZE, 0); 336 #endif 337 if (read(tifd, tibuf[whichbuf], BUFFERSIZE) < 0) 338 pfatal2("error reading tmp file %s", TMPINNAME); 339 } 340 return tibuf[whichbuf] + (tireclen*offline); 341 } 342 } 343 344 /* True if the string argument contains the revision number we want. */ 345 346 bool 347 rev_in_string(string) 348 char *string; 349 { 350 Reg1 char *s; 351 Reg2 int patlen; 352 353 if (revision == Nullch) 354 return TRUE; 355 patlen = strlen(revision); 356 if (strnEQ(string,revision,patlen) && isspace((unsigned char)string[patlen])) 357 return TRUE; 358 for (s = string; *s; s++) { 359 if (isspace((unsigned char)*s) && strnEQ(s+1, revision, patlen) && 360 isspace((unsigned char)s[patlen+1] )) { 361 return TRUE; 362 } 363 } 364 return FALSE; 365 } 366