1 /* $NetBSD: inp.c,v 1.13 2003/05/30 22:33:58 kristerw Exp $ */ 2 #include <sys/cdefs.h> 3 #ifndef lint 4 __RCSID("$NetBSD: inp.c,v 1.13 2003/05/30 22:33:58 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 void plan_a(char *); 20 static bool rev_in_string(char *); 21 22 /* Input-file-with-indexable-lines abstract type. */ 23 24 static size_t 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 /* 29 * New patch -- prepare to edit another file. 30 */ 31 void 32 re_input(void) 33 { 34 i_size = 0; 35 36 if (i_ptr != NULL) 37 free(i_ptr); 38 if (i_womp != NULL) 39 free(i_womp); 40 i_womp = NULL; 41 i_ptr = NULL; 42 } 43 44 /* 45 * Construct the line index, somehow or other. 46 */ 47 void 48 scan_input(char *filename) 49 { 50 plan_a(filename); 51 if (verbose) 52 say("Patching file %s using Plan A...\n", filename); 53 } 54 55 /* 56 * Try keeping everything in memory. 57 */ 58 static void 59 plan_a(char *filename) 60 { 61 int ifd, statfailed; 62 char *s; 63 LINENUM iline; 64 char lbuf[MAXLINELEN]; 65 66 statfailed = stat(filename, &filestat); 67 if (statfailed && ok_to_create_file) { 68 if (verbose) 69 say("(Creating file %s...)\n",filename); 70 makedirs(filename, TRUE); 71 close(creat(filename, 0666)); 72 statfailed = stat(filename, &filestat); 73 } 74 /* 75 * For nonexistent or read-only files, look for RCS or SCCS 76 * versions. 77 */ 78 if (statfailed || 79 /* No one can write to it. */ 80 (filestat.st_mode & 0222) == 0 || 81 /* I can't write to it. */ 82 ((filestat.st_mode & 0022) == 0 && filestat.st_uid != myuid)) { 83 struct stat cstat; 84 char *cs = NULL; 85 char *filebase; 86 size_t pathlen; 87 88 filebase = basename(filename); 89 pathlen = filebase - filename; 90 91 /* 92 * Put any leading path into `s'. 93 * Leave room in lbuf for the diff command. 94 */ 95 s = lbuf + 20; 96 strncpy(s, filename, pathlen); 97 98 #define try(f, a1, a2) (Sprintf(s + pathlen, f, a1, a2), stat(s, &cstat) == 0) 99 #define try1(f, a1) (Sprintf(s + pathlen, f, a1), stat(s, &cstat) == 0) 100 if (try("RCS/%s%s", filebase, RCSSUFFIX) || 101 try1("RCS/%s" , filebase) || 102 try("%s%s", filebase, RCSSUFFIX)) { 103 Sprintf(buf, CHECKOUT, filename); 104 Sprintf(lbuf, RCSDIFF, filename); 105 cs = "RCS"; 106 } else if (try("SCCS/%s%s", SCCSPREFIX, filebase) || 107 try("%s%s", SCCSPREFIX, filebase)) { 108 Sprintf(buf, GET, s); 109 Sprintf(lbuf, SCCSDIFF, s, filename); 110 cs = "SCCS"; 111 } else if (statfailed) 112 fatal("can't find %s\n", filename); 113 /* 114 * else we can't write to it but it's not under a version 115 * control system, so just proceed. 116 */ 117 if (cs) { 118 if (!statfailed) { 119 if ((filestat.st_mode & 0222) != 0) 120 /* The owner can write to it. */ 121 fatal( 122 "file %s seems to be locked by somebody else under %s\n", 123 filename, cs); 124 /* 125 * It might be checked out unlocked. See if 126 * it's safe to check out the default version 127 * locked. 128 */ 129 if (verbose) 130 say( 131 "Comparing file %s to default %s version...\n", 132 filename, cs); 133 if (system(lbuf)) 134 fatal( 135 "can't check out file %s: differs from default %s version\n", 136 filename, cs); 137 } 138 if (verbose) 139 say("Checking out file %s from %s...\n", 140 filename, cs); 141 if (system(buf) || stat(filename, &filestat)) 142 fatal("can't check out file %s from %s\n", 143 filename, cs); 144 } 145 } 146 if (old_file_is_dev_null && 147 ok_to_create_file && 148 (filestat.st_size != 0)) { 149 fatal( 150 "patch creates new file but existing file %s not empty\n", 151 filename); 152 } 153 154 filemode = filestat.st_mode; 155 if (!S_ISREG(filemode)) 156 fatal("%s is not a normal file--can't patch\n", filename); 157 i_size = filestat.st_size; 158 159 i_womp = xmalloc(i_size + 2); 160 if ((ifd = open(filename, 0)) < 0) 161 pfatal("can't open file %s", filename); 162 if (read(ifd, i_womp, i_size) != i_size) 163 pfatal("read error"); 164 Close(ifd); 165 if (i_size && i_womp[i_size - 1] != '\n') 166 i_womp[i_size++] = '\n'; 167 i_womp[i_size] = '\0'; 168 169 /* 170 * Count the lines in the buffer so we know how many pointers we 171 * need. 172 */ 173 iline = 0; 174 for (s = i_womp; *s; s++) { 175 if (*s == '\n') 176 iline++; 177 } 178 i_ptr = xmalloc((iline + 2) * sizeof(char *)); 179 180 /* Now scan the buffer and build pointer array. */ 181 iline = 1; 182 i_ptr[iline] = i_womp; 183 for (s = i_womp; *s; s++) { 184 if (*s == '\n') { 185 /* These are NOT null terminated. */ 186 i_ptr[++iline] = s + 1; 187 } 188 } 189 input_lines = iline - 1; 190 191 /* Now check for revision, if any. */ 192 if (revision != NULL) { 193 if (!rev_in_string(i_womp)) { 194 if (force) { 195 if (verbose) 196 say( 197 "Warning: this file doesn't appear to be the %s version--patching anyway.\n", 198 revision); 199 } else if (batch) { 200 fatal( 201 "this file doesn't appear to be the %s version--aborting.\n", revision); 202 } else { 203 ask( 204 "This file doesn't appear to be the %s version--patch anyway? [n] ", 205 revision); 206 if (*buf != 'y') 207 fatal("aborted\n"); 208 } 209 } else if (verbose) 210 say("Good. This file appears to be the %s version.\n", 211 revision); 212 } 213 } 214 215 /* 216 * Fetch a line from the input file, \n terminated, not necessarily \0. 217 */ 218 char * 219 ifetch(LINENUM line) 220 { 221 if (line < 1 || line > input_lines) 222 return ""; 223 224 return i_ptr[line]; 225 } 226 227 /* 228 * True if the string argument contains the revision number we want. 229 */ 230 static bool 231 rev_in_string(char *string) 232 { 233 char *s; 234 size_t patlen; 235 236 if (revision == NULL) 237 return TRUE; 238 patlen = strlen(revision); 239 if (strnEQ(string,revision,patlen) && 240 isspace((unsigned char)string[patlen])) 241 return TRUE; 242 for (s = string; *s; s++) { 243 if (isspace((unsigned char)*s) && 244 strnEQ(s + 1, revision, patlen) && 245 isspace((unsigned char)s[patlen + 1] )) { 246 return TRUE; 247 } 248 } 249 return FALSE; 250 } 251