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