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