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