File: | src/usr.bin/patch/inp.c |
Warning: | line 297, column 4 Null pointer passed as 1st argument to memory copy function |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: inp.c,v 1.49 2019/06/28 13:35:02 deraadt Exp $ */ | |||
2 | ||||
3 | /* | |||
4 | * patch - a program to apply diffs to original files | |||
5 | * | |||
6 | * Copyright 1986, Larry Wall | |||
7 | * | |||
8 | * Redistribution and use in source and binary forms, with or without | |||
9 | * modification, are permitted provided that the following condition is met: | |||
10 | * 1. Redistributions of source code must retain the above copyright notice, | |||
11 | * this condition and the following disclaimer. | |||
12 | * | |||
13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY | |||
14 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||
15 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
16 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR | |||
17 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |||
18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |||
19 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |||
20 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |||
21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |||
22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||
23 | * SUCH DAMAGE. | |||
24 | * | |||
25 | * -C option added in 1998, original code by Marc Espie, based on FreeBSD | |||
26 | * behaviour | |||
27 | */ | |||
28 | ||||
29 | #include <sys/stat.h> | |||
30 | #include <sys/mman.h> | |||
31 | ||||
32 | #include <ctype.h> | |||
33 | #include <fcntl.h> | |||
34 | #include <stddef.h> | |||
35 | #include <stdint.h> | |||
36 | #include <stdio.h> | |||
37 | #include <stdlib.h> | |||
38 | #include <string.h> | |||
39 | #include <unistd.h> | |||
40 | ||||
41 | #include "common.h" | |||
42 | #include "util.h" | |||
43 | #include "pch.h" | |||
44 | #include "inp.h" | |||
45 | ||||
46 | ||||
47 | /* Input-file-with-indexable-lines abstract type */ | |||
48 | ||||
49 | static off_t i_size; /* size of the input file */ | |||
50 | static char *i_womp; /* plan a buffer for entire file */ | |||
51 | static char **i_ptr; /* pointers to lines in i_womp */ | |||
52 | ||||
53 | static int tifd = -1; /* plan b virtual string array */ | |||
54 | static char *tibuf[2]; /* plan b buffers */ | |||
55 | static LINENUM tiline[2] = {-1, -1}; /* 1st line in each buffer */ | |||
56 | static size_t lines_per_buf; /* how many lines per buffer */ | |||
57 | static size_t tibuflen; /* plan b buffer length */ | |||
58 | static size_t tireclen; /* length of records in tmp file */ | |||
59 | ||||
60 | static bool_Bool rev_in_string(const char *); | |||
61 | static bool_Bool reallocate_lines(size_t *); | |||
62 | ||||
63 | /* returns false if insufficient memory */ | |||
64 | static bool_Bool plan_a(const char *); | |||
65 | ||||
66 | static void plan_b(const char *); | |||
67 | ||||
68 | /* New patch--prepare to edit another file. */ | |||
69 | ||||
70 | void | |||
71 | re_input(void) | |||
72 | { | |||
73 | if (using_plan_a) { | |||
74 | free(i_ptr); | |||
75 | i_ptr = NULL((void *)0); | |||
76 | if (i_womp != NULL((void *)0)) { | |||
77 | munmap(i_womp, i_size); | |||
78 | i_womp = NULL((void *)0); | |||
79 | } | |||
80 | i_size = 0; | |||
81 | } else { | |||
82 | using_plan_a = true1; /* maybe the next one is smaller */ | |||
83 | close(tifd); | |||
84 | tifd = -1; | |||
85 | free(tibuf[0]); | |||
86 | free(tibuf[1]); | |||
87 | tibuf[0] = tibuf[1] = NULL((void *)0); | |||
88 | tiline[0] = tiline[1] = -1; | |||
89 | tireclen = 0; | |||
90 | } | |||
91 | } | |||
92 | ||||
93 | /* Construct the line index, somehow or other. */ | |||
94 | ||||
95 | void | |||
96 | scan_input(const char *filename) | |||
97 | { | |||
98 | if (!plan_a(filename)) | |||
| ||||
99 | plan_b(filename); | |||
100 | if (verbose) { | |||
101 | say("Patching file %s using Plan %s...\n", filename, | |||
102 | (using_plan_a ? "A" : "B")); | |||
103 | } | |||
104 | } | |||
105 | ||||
106 | static bool_Bool | |||
107 | reallocate_lines(size_t *lines_allocatedp) | |||
108 | { | |||
109 | char **p; | |||
110 | size_t new_size; | |||
111 | ||||
112 | new_size = *lines_allocatedp * 3 / 2; | |||
113 | p = reallocarray(i_ptr, new_size + 2, sizeof(char *)); | |||
114 | if (p == NULL((void *)0)) { /* shucks, it was a near thing */ | |||
115 | munmap(i_womp, i_size); | |||
116 | i_womp = NULL((void *)0); | |||
117 | free(i_ptr); | |||
118 | i_ptr = NULL((void *)0); | |||
119 | *lines_allocatedp = 0; | |||
120 | return false0; | |||
121 | } | |||
122 | *lines_allocatedp = new_size; | |||
123 | i_ptr = p; | |||
124 | return true1; | |||
125 | } | |||
126 | ||||
127 | /* Try keeping everything in memory. */ | |||
128 | ||||
129 | static bool_Bool | |||
130 | plan_a(const char *filename) | |||
131 | { | |||
132 | int ifd, statfailed; | |||
133 | char *p, *s; | |||
134 | struct stat filestat; | |||
135 | off_t i; | |||
136 | ptrdiff_t sz; | |||
137 | size_t iline, lines_allocated; | |||
138 | ||||
139 | #ifdef DEBUGGING | |||
140 | if (debug & 8) | |||
141 | return false0; | |||
142 | #endif | |||
143 | ||||
144 | if (filename == NULL((void *)0) || *filename == '\0') | |||
145 | return false0; | |||
146 | ||||
147 | statfailed = stat(filename, &filestat); | |||
148 | if (statfailed && ok_to_create_file) { | |||
149 | int fd; | |||
150 | ||||
151 | if (verbose) | |||
152 | say("(Creating file %s...)\n", filename); | |||
153 | ||||
154 | /* | |||
155 | * in check_patch case, we still display `Creating file' even | |||
156 | * though we're not. The rule is that -C should be as similar | |||
157 | * to normal patch behavior as possible | |||
158 | */ | |||
159 | if (check_only) | |||
160 | return true1; | |||
161 | makedirs(filename, true1); | |||
162 | if ((fd = open(filename, O_CREAT0x0200 | O_TRUNC0x0400 | O_WRONLY0x0001, 0666)) != -1) | |||
163 | close(fd); | |||
164 | ||||
165 | statfailed = stat(filename, &filestat); | |||
166 | } | |||
167 | if (statfailed) | |||
168 | fatal("can't find %s\n", filename); | |||
169 | filemode = filestat.st_mode; | |||
170 | if (!S_ISREG(filemode)((filemode & 0170000) == 0100000)) | |||
171 | fatal("%s is not a normal file--can't patch\n", filename); | |||
172 | i_size = filestat.st_size; | |||
173 | if (out_of_mem) { | |||
174 | set_hunkmax(); /* make sure dynamic arrays are allocated */ | |||
175 | out_of_mem = false0; | |||
176 | return false0; /* force plan b because plan a bombed */ | |||
177 | } | |||
178 | if (i_size > SIZE_MAX0xffffffffffffffffUL) { | |||
179 | say("block too large to mmap\n"); | |||
180 | return false0; | |||
181 | } | |||
182 | if ((ifd = open(filename, O_RDONLY0x0000)) == -1) | |||
183 | pfatal("can't open file %s", filename); | |||
184 | ||||
185 | if (i_size) { | |||
186 | i_womp = mmap(NULL((void *)0), i_size, PROT_READ0x01, MAP_PRIVATE0x0002, ifd, 0); | |||
187 | if (i_womp == MAP_FAILED((void *)-1)) { | |||
188 | perror("mmap failed"); | |||
189 | i_womp = NULL((void *)0); | |||
190 | close(ifd); | |||
191 | return false0; | |||
192 | } | |||
193 | } else { | |||
194 | i_womp = NULL((void *)0); | |||
195 | } | |||
196 | ||||
197 | close(ifd); | |||
198 | if (i_size) | |||
199 | madvise(i_womp, i_size, MADV_SEQUENTIAL2); | |||
200 | ||||
201 | /* estimate the number of lines */ | |||
202 | lines_allocated = i_size / 25; | |||
203 | if (lines_allocated < 100) | |||
204 | lines_allocated = 100; | |||
205 | ||||
206 | if (!reallocate_lines(&lines_allocated)) | |||
207 | return false0; | |||
208 | ||||
209 | /* now scan the buffer and build pointer array */ | |||
210 | iline = 1; | |||
211 | i_ptr[iline] = i_womp; | |||
212 | /* test for NUL too, to maintain the behavior of the original code */ | |||
213 | for (s = i_womp, i = 0; i < i_size && *s != '\0'; s++, i++) { | |||
214 | if (*s == '\n') { | |||
215 | if (iline == lines_allocated) { | |||
216 | if (!reallocate_lines(&lines_allocated)) | |||
217 | return false0; | |||
218 | } | |||
219 | /* these are NOT NUL terminated */ | |||
220 | i_ptr[++iline] = s + 1; | |||
221 | } | |||
222 | } | |||
223 | /* if the last line contains no EOL, append one */ | |||
224 | if (i_size > 0 && i_womp[i_size - 1] != '\n') { | |||
225 | last_line_missing_eol = true1; | |||
226 | /* fix last line */ | |||
227 | sz = s - i_ptr[iline]; | |||
228 | p = malloc(sz + 1); | |||
229 | if (p == NULL((void *)0)) { | |||
230 | free(i_ptr); | |||
231 | i_ptr = NULL((void *)0); | |||
232 | munmap(i_womp, i_size); | |||
233 | i_womp = NULL((void *)0); | |||
234 | return false0; | |||
235 | } | |||
236 | ||||
237 | memcpy(p, i_ptr[iline], sz); | |||
238 | p[sz] = '\n'; | |||
239 | i_ptr[iline] = p; | |||
240 | /* count the extra line and make it point to some valid mem */ | |||
241 | i_ptr[++iline] = ""; | |||
242 | } else | |||
243 | last_line_missing_eol = false0; | |||
244 | ||||
245 | input_lines = iline - 1; | |||
246 | ||||
247 | /* now check for revision, if any */ | |||
248 | ||||
249 | if (revision != NULL((void *)0)) { | |||
250 | if (i_womp == NULL((void *)0) || !rev_in_string(i_womp)) { | |||
251 | if (force) { | |||
252 | if (verbose) | |||
253 | say("Warning: this file doesn't appear " | |||
254 | "to be the %s version--patching anyway.\n", | |||
255 | revision); | |||
256 | } else if (batch) { | |||
257 | fatal("this file doesn't appear to be the " | |||
258 | "%s version--aborting.\n", | |||
259 | revision); | |||
260 | } else { | |||
261 | ask("This file doesn't appear to be the " | |||
262 | "%s version--patch anyway? [n] ", | |||
263 | revision); | |||
264 | if (*buf != 'y') | |||
265 | fatal("aborted\n"); | |||
266 | } | |||
267 | } else if (verbose) | |||
268 | say("Good. This file appears to be the %s version.\n", | |||
269 | revision); | |||
270 | } | |||
271 | return true1; /* plan a will work */ | |||
272 | } | |||
273 | ||||
274 | /* Keep (virtually) nothing in memory. */ | |||
275 | ||||
276 | static void | |||
277 | plan_b(const char *filename) | |||
278 | { | |||
279 | FILE *ifp; | |||
280 | size_t i = 0, j, len, maxlen = 1; | |||
281 | char *lbuf = NULL((void *)0), *p; | |||
282 | bool_Bool found_revision = (revision == NULL((void *)0)); | |||
283 | ||||
284 | using_plan_a = false0; | |||
285 | if ((ifp = fopen(filename, "r")) == NULL((void *)0)) | |||
286 | pfatal("can't open file %s", filename); | |||
287 | (void) unlink(TMPINNAME); | |||
288 | if ((tifd = open(TMPINNAME, O_EXCL0x0800 | O_CREAT0x0200 | O_WRONLY0x0001, 0666)) == -1) | |||
289 | pfatal("can't open file %s", TMPINNAME); | |||
290 | while ((p = fgetln(ifp, &len)) != NULL((void *)0)) { | |||
291 | if (p[len - 1] == '\n') | |||
292 | p[len - 1] = '\0'; | |||
293 | else { | |||
294 | /* EOF without EOL, copy and add the NUL */ | |||
295 | if ((lbuf = malloc(len + 1)) == NULL((void *)0)) | |||
296 | fatal("out of memory\n"); | |||
297 | memcpy(lbuf, p, len); | |||
| ||||
298 | lbuf[len] = '\0'; | |||
299 | p = lbuf; | |||
300 | ||||
301 | last_line_missing_eol = true1; | |||
302 | len++; | |||
303 | } | |||
304 | if (revision != NULL((void *)0) && !found_revision && rev_in_string(p)) | |||
305 | found_revision = true1; | |||
306 | if (len > maxlen) | |||
307 | maxlen = len; /* find longest line */ | |||
308 | } | |||
309 | free(lbuf); | |||
310 | if (ferror(ifp)(!__isthreaded ? (((ifp)->_flags & 0x0040) != 0) : (ferror )(ifp))) | |||
311 | pfatal("can't read file %s", filename); | |||
312 | ||||
313 | if (revision != NULL((void *)0)) { | |||
314 | if (!found_revision) { | |||
315 | if (force) { | |||
316 | if (verbose) | |||
317 | say("Warning: this file doesn't appear " | |||
318 | "to be the %s version--patching anyway.\n", | |||
319 | revision); | |||
320 | } else if (batch) { | |||
321 | fatal("this file doesn't appear to be the " | |||
322 | "%s version--aborting.\n", | |||
323 | revision); | |||
324 | } else { | |||
325 | ask("This file doesn't appear to be the %s " | |||
326 | "version--patch anyway? [n] ", | |||
327 | revision); | |||
328 | if (*buf != 'y') | |||
329 | fatal("aborted\n"); | |||
330 | } | |||
331 | } else if (verbose) | |||
332 | say("Good. This file appears to be the %s version.\n", | |||
333 | revision); | |||
334 | } | |||
335 | fseek(ifp, 0L, SEEK_SET0); /* rewind file */ | |||
336 | tireclen = maxlen; | |||
337 | tibuflen = maxlen > BUFFERSIZE1024 ? maxlen : BUFFERSIZE1024; | |||
338 | lines_per_buf = tibuflen / maxlen; | |||
339 | tibuf[0] = malloc(tibuflen + 1); | |||
340 | if (tibuf[0] == NULL((void *)0)) | |||
341 | fatal("out of memory\n"); | |||
342 | tibuf[1] = malloc(tibuflen + 1); | |||
343 | if (tibuf[1] == NULL((void *)0)) | |||
344 | fatal("out of memory\n"); | |||
345 | for (i = 1;; i++) { | |||
346 | p = tibuf[0] + maxlen * (i % lines_per_buf); | |||
347 | if (i % lines_per_buf == 0) /* new block */ | |||
348 | if (write(tifd, tibuf[0], tibuflen) != | |||
349 | (ssize_t) tibuflen) | |||
350 | pfatal("can't write temp file"); | |||
351 | if (fgets(p, maxlen + 1, ifp) == NULL((void *)0)) { | |||
352 | input_lines = i - 1; | |||
353 | if (i % lines_per_buf != 0) | |||
354 | if (write(tifd, tibuf[0], tibuflen) != | |||
355 | (ssize_t) tibuflen) | |||
356 | pfatal("can't write temp file"); | |||
357 | break; | |||
358 | } | |||
359 | j = strlen(p); | |||
360 | /* These are '\n' terminated strings, so no need to add a NUL */ | |||
361 | if (j == 0 || p[j - 1] != '\n') | |||
362 | p[j] = '\n'; | |||
363 | } | |||
364 | fclose(ifp); | |||
365 | close(tifd); | |||
366 | if ((tifd = open(TMPINNAME, O_RDONLY0x0000)) == -1) | |||
367 | pfatal("can't reopen file %s", TMPINNAME); | |||
368 | } | |||
369 | ||||
370 | /* | |||
371 | * Fetch a line from the input file, \n terminated, not necessarily \0. | |||
372 | */ | |||
373 | char * | |||
374 | ifetch(LINENUM line, int whichbuf) | |||
375 | { | |||
376 | if (line < 1 || line > input_lines) { | |||
377 | if (warn_on_invalid_line) { | |||
378 | say("No such line %ld in input file, ignoring\n", line); | |||
379 | warn_on_invalid_line = false0; | |||
380 | } | |||
381 | return NULL((void *)0); | |||
382 | } | |||
383 | if (using_plan_a) | |||
384 | return i_ptr[line]; | |||
385 | else { | |||
386 | LINENUM offline = line % lines_per_buf; | |||
387 | LINENUM baseline = line - offline; | |||
388 | ||||
389 | if (tiline[0] == baseline) | |||
390 | whichbuf = 0; | |||
391 | else if (tiline[1] == baseline) | |||
392 | whichbuf = 1; | |||
393 | else { | |||
394 | tiline[whichbuf] = baseline; | |||
395 | ||||
396 | if (lseek(tifd, (off_t) (baseline / lines_per_buf * | |||
397 | tibuflen), SEEK_SET0) == -1) | |||
398 | pfatal("cannot seek in the temporary input file"); | |||
399 | ||||
400 | if (read(tifd, tibuf[whichbuf], tibuflen) | |||
401 | != (ssize_t) tibuflen) | |||
402 | pfatal("error reading tmp file %s", TMPINNAME); | |||
403 | } | |||
404 | return tibuf[whichbuf] + (tireclen * offline); | |||
405 | } | |||
406 | } | |||
407 | ||||
408 | /* | |||
409 | * True if the string argument contains the revision number we want. | |||
410 | */ | |||
411 | static bool_Bool | |||
412 | rev_in_string(const char *string) | |||
413 | { | |||
414 | const char *s; | |||
415 | size_t patlen; | |||
416 | ||||
417 | if (revision == NULL((void *)0)) | |||
418 | return true1; | |||
419 | patlen = strlen(revision); | |||
420 | if (strnEQ(string, revision, patlen)(!strncmp(string, revision, patlen)) && | |||
421 | isspace((unsigned char)string[patlen])) | |||
422 | return true1; | |||
423 | for (s = string; *s; s++) { | |||
424 | if (isspace((unsigned char)*s) && strnEQ(s + 1, revision, patlen)(!strncmp(s + 1, revision, patlen)) && | |||
425 | isspace((unsigned char)s[patlen + 1])) { | |||
426 | return true1; | |||
427 | } | |||
428 | } | |||
429 | return false0; | |||
430 | } |