inp.c revision 1.13 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