Wed Oct 21 17:17:04 2009 UTC ()
nbpatch-20091021:
Do not try to mmap a zero length file. This can fail e.g. on Solaris.


(joerg)
diff -r1.3 -r1.4 pkgsrc/devel/nbpatch/Makefile
diff -r1.5 -r1.6 pkgsrc/devel/nbpatch/files/inp.c

cvs diff -r1.3 -r1.4 pkgsrc/devel/nbpatch/Makefile (switch to unified diff)

--- pkgsrc/devel/nbpatch/Makefile 2008/09/10 19:23:31 1.3
+++ pkgsrc/devel/nbpatch/Makefile 2009/10/21 17:17:04 1.4
@@ -1,30 +1,30 @@ @@ -1,30 +1,30 @@
1# $NetBSD: Makefile,v 1.3 2008/09/10 19:23:31 joerg Exp $ 1# $NetBSD: Makefile,v 1.4 2009/10/21 17:17:04 joerg Exp $
2# 2#
3 3
4DISTNAME= nbpatch-20080910 4DISTNAME= nbpatch-20091021
5CATEGORIES= devel 5CATEGORIES= devel
6MASTER_SITES= # empty 6MASTER_SITES= # empty
7DISTFILES= # empty 7DISTFILES= # empty
8 8
9MAINTAINER= joerg@NetBSD.org 9MAINTAINER= joerg@NetBSD.org
10HOMEPAGE= http://www.pkgsrc.org/ 10HOMEPAGE= http://www.pkgsrc.org/
11COMMENT= Patch files using diff output 11COMMENT= Patch files using diff output
12 12
13PKG_DESTDIR_SUPPORT= user-destdir 13PKG_DESTDIR_SUPPORT= user-destdir
14 14
15# Allow checkperms and othher core dependencies to use patches 15# Allow checkperms and othher core dependencies to use patches
16CHECK_PERMS= no 16CHECK_PERMS= no
17 17
18USE_BSD_MAKEFILE= yes 18USE_BSD_MAKEFILE= yes
19USE_FEATURES= nbcompat 19USE_FEATURES= nbcompat
20 20
21NBCOMPAT_CONFIGURE_ARGS+= --enable-bsd-getopt 21NBCOMPAT_CONFIGURE_ARGS+= --enable-bsd-getopt
22 22
23ALL_ENV+= LDADD=${LIBS:Q} 23ALL_ENV+= LDADD=${LIBS:Q}
24 24
25INSTALLATION_DIRS= bin ${PKGMANDIR}/cat1 ${PKGMANDIR}/man1 25INSTALLATION_DIRS= bin ${PKGMANDIR}/cat1 ${PKGMANDIR}/man1
26 26
27do-extract: 27do-extract:
28 ${CP} -r ${FILESDIR} ${WRKSRC} 28 ${CP} -r ${FILESDIR} ${WRKSRC}
29 29
30.include "../../mk/bsd.pkg.mk" 30.include "../../mk/bsd.pkg.mk"

cvs diff -r1.5 -r1.6 pkgsrc/devel/nbpatch/files/inp.c (switch to unified diff)

--- pkgsrc/devel/nbpatch/files/inp.c 2009/06/05 20:00:26 1.5
+++ pkgsrc/devel/nbpatch/files/inp.c 2009/10/21 17:17:04 1.6
@@ -1,500 +1,504 @@ @@ -1,500 +1,504 @@
1/* 1/*
2 * $OpenBSD: inp.c,v 1.34 2006/03/11 19:41:30 otto Exp $ 2 * $OpenBSD: inp.c,v 1.34 2006/03/11 19:41:30 otto Exp $
3 * $DragonFly: src/usr.bin/patch/inp.c,v 1.6 2007/09/29 23:11:10 swildner Exp $ 3 * $DragonFly: src/usr.bin/patch/inp.c,v 1.6 2007/09/29 23:11:10 swildner Exp $
4 * $NetBSD: inp.c,v 1.5 2009/06/05 20:00:26 joerg Exp $ 4 * $NetBSD: inp.c,v 1.6 2009/10/21 17:17:04 joerg Exp $
5 */ 5 */
6 6
7/* 7/*
8 * patch - a program to apply diffs to original files 8 * patch - a program to apply diffs to original files
9 *  9 *
10 * Copyright 1986, Larry Wall 10 * Copyright 1986, Larry Wall
11 *  11 *
12 * Redistribution and use in source and binary forms, with or without 12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following condition is met: 13 * modification, are permitted provided that the following condition is met:
14 * 1. Redistributions of source code must retain the above copyright notice, 14 * 1. Redistributions of source code must retain the above copyright notice,
15 * this condition and the following disclaimer. 15 * this condition and the following disclaimer.
16 *  16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 20 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
21 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE. 27 * SUCH DAMAGE.
28 *  28 *
29 * -C option added in 1998, original code by Marc Espie, based on FreeBSD 29 * -C option added in 1998, original code by Marc Espie, based on FreeBSD
30 * behaviour 30 * behaviour
31 */ 31 */
32 32
33#if HAVE_CONFIG_H 33#if HAVE_CONFIG_H
34#include "config.h" 34#include "config.h"
35#endif 35#endif
36#include <nbcompat.h> 36#include <nbcompat.h>
37 37
38#include <sys/types.h> 38#include <sys/types.h>
39#include <sys/file.h> 39#include <sys/file.h>
40#include <sys/stat.h> 40#include <sys/stat.h>
41#include <sys/mman.h> 41#include <sys/mman.h>
42 42
43#include <ctype.h> 43#include <ctype.h>
44#include <fcntl.h> 44#include <fcntl.h>
45#include <libgen.h> 45#include <libgen.h>
46#include <limits.h> 46#include <limits.h>
47#if HAVE_STDDEF_H 47#if HAVE_STDDEF_H
48#include <stddef.h> 48#include <stddef.h>
49#endif 49#endif
50#include <stdio.h> 50#include <stdio.h>
51#include <stdlib.h> 51#include <stdlib.h>
52#include <string.h> 52#include <string.h>
53#include <unistd.h> 53#include <unistd.h>
54 54
55#include "common.h" 55#include "common.h"
56#include "util.h" 56#include "util.h"
57#include "pch.h" 57#include "pch.h"
58#include "inp.h" 58#include "inp.h"
59 59
60#ifndef MAP_FAILED 60#ifndef MAP_FAILED
61#define MAP_FAILED (void *)(-1) 61#define MAP_FAILED (void *)(-1)
62#endif 62#endif
63 63
64 64
65/* Input-file-with-indexable-lines abstract type */ 65/* Input-file-with-indexable-lines abstract type */
66 66
67static off_t i_size; /* size of the input file */ 67static off_t i_size; /* size of the input file */
68static char *i_womp; /* plan a buffer for entire file */ 68static char *i_womp; /* plan a buffer for entire file */
69static char **i_ptr; /* pointers to lines in i_womp */ 69static char **i_ptr; /* pointers to lines in i_womp */
70static char empty_line[] = { '\0' }; 70static char empty_line[] = { '\0' };
71 71
72static int tifd = -1; /* plan b virtual string array */ 72static int tifd = -1; /* plan b virtual string array */
73static char *tibuf[2]; /* plan b buffers */ 73static char *tibuf[2]; /* plan b buffers */
74static LINENUM tiline[2] = {-1, -1}; /* 1st line in each buffer */ 74static LINENUM tiline[2] = {-1, -1}; /* 1st line in each buffer */
75static LINENUM lines_per_buf; /* how many lines per buffer */ 75static LINENUM lines_per_buf; /* how many lines per buffer */
76static int tireclen; /* length of records in tmp file */ 76static int tireclen; /* length of records in tmp file */
77 77
78static bool rev_in_string(const char *); 78static bool rev_in_string(const char *);
79static bool reallocate_lines(size_t *); 79static bool reallocate_lines(size_t *);
80 80
81/* returns false if insufficient memory */ 81/* returns false if insufficient memory */
82static bool plan_a(const char *); 82static bool plan_a(const char *);
83 83
84static void plan_b(const char *); 84static void plan_b(const char *);
85 85
86/* New patch--prepare to edit another file. */ 86/* New patch--prepare to edit another file. */
87 87
88void 88void
89re_input(void) 89re_input(void)
90{ 90{
91 if (using_plan_a) { 91 if (using_plan_a) {
92 i_size = 0; 92 i_size = 0;
93 free(i_ptr); 93 free(i_ptr);
94 i_ptr = NULL; 94 i_ptr = NULL;
95 if (i_womp != NULL) { 95 if (i_womp != NULL) {
96 munmap(i_womp, i_size); 96 munmap(i_womp, i_size);
97 i_womp = NULL; 97 i_womp = NULL;
98 } 98 }
99 } else { 99 } else {
100 using_plan_a = true; /* maybe the next one is smaller */ 100 using_plan_a = true; /* maybe the next one is smaller */
101 close(tifd); 101 close(tifd);
102 tifd = -1; 102 tifd = -1;
103 free(tibuf[0]); 103 free(tibuf[0]);
104 free(tibuf[1]); 104 free(tibuf[1]);
105 tibuf[0] = tibuf[1] = NULL; 105 tibuf[0] = tibuf[1] = NULL;
106 tiline[0] = tiline[1] = -1; 106 tiline[0] = tiline[1] = -1;
107 tireclen = 0; 107 tireclen = 0;
108 } 108 }
109} 109}
110 110
111/* Construct the line index, somehow or other. */ 111/* Construct the line index, somehow or other. */
112 112
113void 113void
114scan_input(const char *filename) 114scan_input(const char *filename)
115{ 115{
116 if (!plan_a(filename)) 116 if (!plan_a(filename))
117 plan_b(filename); 117 plan_b(filename);
118 if (verbose) { 118 if (verbose) {
119 say("Patching file %s using Plan %s...\n", filename, 119 say("Patching file %s using Plan %s...\n", filename,
120 (using_plan_a ? "A" : "B")); 120 (using_plan_a ? "A" : "B"));
121 } 121 }
122} 122}
123 123
124static bool 124static bool
125reallocate_lines(size_t *lines_allocated) 125reallocate_lines(size_t *lines_allocated)
126{ 126{
127 char **p; 127 char **p;
128 size_t new_size; 128 size_t new_size;
129 129
130 new_size = *lines_allocated * 3 / 2; 130 new_size = *lines_allocated * 3 / 2;
131 p = realloc(i_ptr, (new_size + 2) * sizeof(char *)); 131 p = realloc(i_ptr, (new_size + 2) * sizeof(char *));
132 if (p == NULL) { /* shucks, it was a near thing */ 132 if (p == NULL) { /* shucks, it was a near thing */
133 munmap(i_womp, i_size); 133 munmap(i_womp, i_size);
134 i_womp = NULL; 134 i_womp = NULL;
135 free(i_ptr); 135 free(i_ptr);
136 i_ptr = NULL; 136 i_ptr = NULL;
137 *lines_allocated = 0; 137 *lines_allocated = 0;
138 return false; 138 return false;
139 } 139 }
140 *lines_allocated = new_size; 140 *lines_allocated = new_size;
141 i_ptr = p; 141 i_ptr = p;
142 return true; 142 return true;
143} 143}
144 144
145/* Try keeping everything in memory. */ 145/* Try keeping everything in memory. */
146 146
147static bool 147static bool
148plan_a(const char *filename) 148plan_a(const char *filename)
149{ 149{
150 int ifd, statfailed; 150 int ifd, statfailed;
151 char *p, *s, lbuf[MAXLINELEN]; 151 char *p, *s, lbuf[MAXLINELEN];
152 struct stat filestat; 152 struct stat filestat;
153 off_t i; 153 off_t i;
154 ptrdiff_t sz; 154 ptrdiff_t sz;
155 size_t iline, lines_allocated; 155 size_t iline, lines_allocated;
156 156
157#ifdef DEBUGGING 157#ifdef DEBUGGING
158 if (debug & 8) 158 if (debug & 8)
159 return false; 159 return false;
160#endif 160#endif
161 161
162 if (filename == NULL || *filename == '\0') 162 if (filename == NULL || *filename == '\0')
163 return false; 163 return false;
164 164
165 statfailed = stat(filename, &filestat); 165 statfailed = stat(filename, &filestat);
166 if (statfailed && ok_to_create_file) { 166 if (statfailed && ok_to_create_file) {
167 if (verbose) 167 if (verbose)
168 say("(Creating file %s...)\n", filename); 168 say("(Creating file %s...)\n", filename);
169 169
170 /* 170 /*
171 * in check_patch case, we still display `Creating file' even 171 * in check_patch case, we still display `Creating file' even
172 * though we're not. The rule is that -C should be as similar 172 * though we're not. The rule is that -C should be as similar
173 * to normal patch behavior as possible 173 * to normal patch behavior as possible
174 */ 174 */
175 if (check_only) 175 if (check_only)
176 return true; 176 return true;
177 makedirs(filename, true); 177 makedirs(filename, true);
178 close(open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0666)); 178 close(open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0666));
179 statfailed = stat(filename, &filestat); 179 statfailed = stat(filename, &filestat);
180 } 180 }
181 if (statfailed && check_only) 181 if (statfailed && check_only)
182 fatal("%s not found, -C mode, can't probe further\n", filename); 182 fatal("%s not found, -C mode, can't probe further\n", filename);
183 /* For nonexistent or read-only files, look for RCS or SCCS versions. */ 183 /* For nonexistent or read-only files, look for RCS or SCCS versions. */
184 if (statfailed || 184 if (statfailed ||
185 /* No one can write to it. */ 185 /* No one can write to it. */
186 (filestat.st_mode & 0222) == 0 || 186 (filestat.st_mode & 0222) == 0 ||
187 /* I can't write to it. */ 187 /* I can't write to it. */
188 ((filestat.st_mode & 0022) == 0 && filestat.st_uid != getuid())) { 188 ((filestat.st_mode & 0022) == 0 && filestat.st_uid != getuid())) {
189 const char *cs = NULL, *filebase, *filedir; 189 const char *cs = NULL, *filebase, *filedir;
190 struct stat cstat; 190 struct stat cstat;
191 char *tmp_filename1, *tmp_filename2; 191 char *tmp_filename1, *tmp_filename2;
192 192
193 tmp_filename1 = strdup(filename); 193 tmp_filename1 = strdup(filename);
194 tmp_filename2 = strdup(filename); 194 tmp_filename2 = strdup(filename);
195 if (tmp_filename1 == NULL || tmp_filename2 == NULL) 195 if (tmp_filename1 == NULL || tmp_filename2 == NULL)
196 fatal("strdupping filename"); 196 fatal("strdupping filename");
197 filebase = basename(tmp_filename1); 197 filebase = basename(tmp_filename1);
198 filedir = dirname(tmp_filename2); 198 filedir = dirname(tmp_filename2);
199 199
200 /* Leave room in lbuf for the diff command. */ 200 /* Leave room in lbuf for the diff command. */
201 s = lbuf + 20; 201 s = lbuf + 20;
202 202
203#define try(f, a1, a2, a3) \ 203#define try(f, a1, a2, a3) \
204 (snprintf(s, sizeof lbuf - 20, f, a1, a2, a3), stat(s, &cstat) == 0) 204 (snprintf(s, sizeof lbuf - 20, f, a1, a2, a3), stat(s, &cstat) == 0)
205 205
206 if (try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX) || 206 if (try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX) ||
207 try("%s/RCS/%s%s", filedir, filebase, "") || 207 try("%s/RCS/%s%s", filedir, filebase, "") ||
208 try("%s/%s%s", filedir, filebase, RCSSUFFIX)) { 208 try("%s/%s%s", filedir, filebase, RCSSUFFIX)) {
209 snprintf(buf, buf_len, CHECKOUT, filename); 209 snprintf(buf, buf_len, CHECKOUT, filename);
210 snprintf(lbuf, sizeof lbuf, RCSDIFF, filename); 210 snprintf(lbuf, sizeof lbuf, RCSDIFF, filename);
211 cs = "RCS"; 211 cs = "RCS";
212 } else if (try("%s/SCCS/%s%s", filedir, SCCSPREFIX, filebase) || 212 } else if (try("%s/SCCS/%s%s", filedir, SCCSPREFIX, filebase) ||
213 try("%s/%s%s", filedir, SCCSPREFIX, filebase)) { 213 try("%s/%s%s", filedir, SCCSPREFIX, filebase)) {
214 snprintf(buf, buf_len, GET, s); 214 snprintf(buf, buf_len, GET, s);
215 snprintf(lbuf, sizeof lbuf, SCCSDIFF, s, filename); 215 snprintf(lbuf, sizeof lbuf, SCCSDIFF, s, filename);
216 cs = "SCCS"; 216 cs = "SCCS";
217 } else if (statfailed) 217 } else if (statfailed)
218 fatal("can't find %s\n", filename); 218 fatal("can't find %s\n", filename);
219 219
220 free(tmp_filename1); 220 free(tmp_filename1);
221 free(tmp_filename2); 221 free(tmp_filename2);
222 222
223 /* 223 /*
224 * else we can't write to it but it's not under a version 224 * else we can't write to it but it's not under a version
225 * control system, so just proceed. 225 * control system, so just proceed.
226 */ 226 */
227 if (cs) { 227 if (cs) {
228 if (!statfailed) { 228 if (!statfailed) {
229 if ((filestat.st_mode & 0222) != 0) 229 if ((filestat.st_mode & 0222) != 0)
230 /* The owner can write to it. */ 230 /* The owner can write to it. */
231 fatal("file %s seems to be locked " 231 fatal("file %s seems to be locked "
232 "by somebody else under %s\n", 232 "by somebody else under %s\n",
233 filename, cs); 233 filename, cs);
234 /* 234 /*
235 * It might be checked out unlocked. See if 235 * It might be checked out unlocked. See if
236 * it's safe to check out the default version 236 * it's safe to check out the default version
237 * locked. 237 * locked.
238 */ 238 */
239 if (verbose) 239 if (verbose)
240 say("Comparing file %s to default " 240 say("Comparing file %s to default "
241 "%s version...\n", 241 "%s version...\n",
242 filename, cs); 242 filename, cs);
243 if (system(lbuf)) 243 if (system(lbuf))
244 fatal("can't check out file %s: " 244 fatal("can't check out file %s: "
245 "differs from default %s version\n", 245 "differs from default %s version\n",
246 filename, cs); 246 filename, cs);
247 } 247 }
248 if (verbose) 248 if (verbose)
249 say("Checking out file %s from %s...\n", 249 say("Checking out file %s from %s...\n",
250 filename, cs); 250 filename, cs);
251 if (system(buf) || stat(filename, &filestat)) 251 if (system(buf) || stat(filename, &filestat))
252 fatal("can't check out file %s from %s\n", 252 fatal("can't check out file %s from %s\n",
253 filename, cs); 253 filename, cs);
254 } 254 }
255 } 255 }
256 filemode = filestat.st_mode; 256 filemode = filestat.st_mode;
257 if (!S_ISREG(filemode)) 257 if (!S_ISREG(filemode))
258 fatal("%s is not a normal file--can't patch\n", filename); 258 fatal("%s is not a normal file--can't patch\n", filename);
259 i_size = filestat.st_size; 259 i_size = filestat.st_size;
260 if (out_of_mem) { 260 if (out_of_mem) {
261 set_hunkmax(); /* make sure dynamic arrays are allocated */ 261 set_hunkmax(); /* make sure dynamic arrays are allocated */
262 out_of_mem = false; 262 out_of_mem = false;
263 return false; /* force plan b because plan a bombed */ 263 return false; /* force plan b because plan a bombed */
264 } 264 }
265 if (i_size > SIZE_MAX) { 265 if (i_size > SIZE_MAX) {
266 say("block too large to mmap\n"); 266 say("block too large to mmap\n");
267 return false; 267 return false;
268 } 268 }
269 if ((ifd = open(filename, O_RDONLY)) < 0) 269 if ((ifd = open(filename, O_RDONLY)) < 0)
270 pfatal("can't open file %s", filename); 270 pfatal("can't open file %s", filename);
271 271
272 i_womp = mmap(NULL, i_size, PROT_READ, MAP_PRIVATE, ifd, 0); 272 if (i_size) {
273 if (i_womp == MAP_FAILED) { 273 i_womp = mmap(NULL, i_size, PROT_READ, MAP_PRIVATE, ifd, 0);
274 perror("mmap failed"); 274 if (i_womp == MAP_FAILED) {
 275 perror("mmap failed");
 276 i_womp = NULL;
 277 close(ifd);
 278 return false;
 279 }
 280 } else {
275 i_womp = NULL; 281 i_womp = NULL;
276 close(ifd); 
277 return false; 
278 } 282 }
279 283
280 close(ifd); 284 close(ifd);
281#ifdef MADV_SEQUENTIAL 285#ifdef MADV_SEQUENTIAL
282 if (i_size) 286 if (i_size)
283 madvise(i_womp, i_size, MADV_SEQUENTIAL); 287 madvise(i_womp, i_size, MADV_SEQUENTIAL);
284#elif defined(POSIX_MADV_SEQUENTIAL) 288#elif defined(POSIX_MADV_SEQUENTIAL)
285 if (i_size) 289 if (i_size)
286 posix_madvise(i_womp, i_size, POSIX_MADV_SEQUENTIAL); 290 posix_madvise(i_womp, i_size, POSIX_MADV_SEQUENTIAL);
287#endif 291#endif
288 292
289 /* estimate the number of lines */ 293 /* estimate the number of lines */
290 lines_allocated = i_size / 25; 294 lines_allocated = i_size / 25;
291 if (lines_allocated < 100) 295 if (lines_allocated < 100)
292 lines_allocated = 100; 296 lines_allocated = 100;
293 297
294 if (!reallocate_lines(&lines_allocated)) 298 if (!reallocate_lines(&lines_allocated))
295 return false; 299 return false;
296 300
297 /* now scan the buffer and build pointer array */ 301 /* now scan the buffer and build pointer array */
298 iline = 1; 302 iline = 1;
299 i_ptr[iline] = i_womp; 303 i_ptr[iline] = i_womp;
300 /* test for NUL too, to maintain the behavior of the original code */ 304 /* test for NUL too, to maintain the behavior of the original code */
301 for (s = i_womp, i = 0; i < i_size && *s != '\0'; s++, i++) { 305 for (s = i_womp, i = 0; i < i_size && *s != '\0'; s++, i++) {
302 if (*s == '\n') { 306 if (*s == '\n') {
303 if (iline == lines_allocated) { 307 if (iline == lines_allocated) {
304 if (!reallocate_lines(&lines_allocated)) 308 if (!reallocate_lines(&lines_allocated))
305 return false; 309 return false;
306 } 310 }
307 /* these are NOT NUL terminated */ 311 /* these are NOT NUL terminated */
308 i_ptr[++iline] = s + 1; 312 i_ptr[++iline] = s + 1;
309 } 313 }
310 } 314 }
311 /* if the last line contains no EOL, append one */ 315 /* if the last line contains no EOL, append one */
312 if (i_size > 0 && i_womp[i_size - 1] != '\n') { 316 if (i_size > 0 && i_womp[i_size - 1] != '\n') {
313 last_line_missing_eol = true; 317 last_line_missing_eol = true;
314 /* fix last line */ 318 /* fix last line */
315 sz = s - i_ptr[iline]; 319 sz = s - i_ptr[iline];
316 p = malloc(sz + 1); 320 p = malloc(sz + 1);
317 if (p == NULL) { 321 if (p == NULL) {
318 free(i_ptr); 322 free(i_ptr);
319 i_ptr = NULL; 323 i_ptr = NULL;
320 munmap(i_womp, i_size); 324 munmap(i_womp, i_size);
321 i_womp = NULL; 325 i_womp = NULL;
322 return false; 326 return false;
323 } 327 }
324 328
325 memcpy(p, i_ptr[iline], sz); 329 memcpy(p, i_ptr[iline], sz);
326 p[sz] = '\n'; 330 p[sz] = '\n';
327 i_ptr[iline] = p; 331 i_ptr[iline] = p;
328 /* count the extra line and make it point to some valid mem */ 332 /* count the extra line and make it point to some valid mem */
329 i_ptr[++iline] = empty_line; 333 i_ptr[++iline] = empty_line;
330 } else 334 } else
331 last_line_missing_eol = false; 335 last_line_missing_eol = false;
332 336
333 input_lines = iline - 1; 337 input_lines = iline - 1;
334 338
335 /* now check for revision, if any */ 339 /* now check for revision, if any */
336 340
337 if (revision != NULL) { 341 if (revision != NULL) {
338 if (!rev_in_string(i_womp)) { 342 if (!rev_in_string(i_womp)) {
339 if (force) { 343 if (force) {
340 if (verbose) 344 if (verbose)
341 say("Warning: this file doesn't appear " 345 say("Warning: this file doesn't appear "
342 "to be the %s version--patching anyway.\n", 346 "to be the %s version--patching anyway.\n",
343 revision); 347 revision);
344 } else if (batch) { 348 } else if (batch) {
345 fatal("this file doesn't appear to be the " 349 fatal("this file doesn't appear to be the "
346 "%s version--aborting.\n", 350 "%s version--aborting.\n",
347 revision); 351 revision);
348 } else { 352 } else {
349 ask("This file doesn't appear to be the " 353 ask("This file doesn't appear to be the "
350 "%s version--patch anyway? [n] ", 354 "%s version--patch anyway? [n] ",
351 revision); 355 revision);
352 if (*buf != 'y') 356 if (*buf != 'y')
353 fatal("aborted\n"); 357 fatal("aborted\n");
354 } 358 }
355 } else if (verbose) 359 } else if (verbose)
356 say("Good. This file appears to be the %s version.\n", 360 say("Good. This file appears to be the %s version.\n",
357 revision); 361 revision);
358 } 362 }
359 return true; /* plan a will work */ 363 return true; /* plan a will work */
360} 364}
361 365
362/* Keep (virtually) nothing in memory. */ 366/* Keep (virtually) nothing in memory. */
363 367
364static void 368static void
365plan_b(const char *filename) 369plan_b(const char *filename)
366{ 370{
367 FILE *ifp; 371 FILE *ifp;
368 size_t i = 0, j, maxlen = 1; 372 size_t i = 0, j, maxlen = 1;
369 char *p; 373 char *p;
370 bool found_revision = (revision == NULL); 374 bool found_revision = (revision == NULL);
371 375
372 using_plan_a = false; 376 using_plan_a = false;
373 if ((ifp = fopen(filename, "r")) == NULL) 377 if ((ifp = fopen(filename, "r")) == NULL)
374 pfatal("can't open file %s", filename); 378 pfatal("can't open file %s", filename);
375 unlink(TMPINNAME); 379 unlink(TMPINNAME);
376 if ((tifd = open(TMPINNAME, O_EXCL | O_CREAT | O_WRONLY, 0666)) < 0) 380 if ((tifd = open(TMPINNAME, O_EXCL | O_CREAT | O_WRONLY, 0666)) < 0)
377 pfatal("can't open file %s", TMPINNAME); 381 pfatal("can't open file %s", TMPINNAME);
378 while (fgets(buf, buf_len, ifp) != NULL) { 382 while (fgets(buf, buf_len, ifp) != NULL) {
379 if (revision != NULL && !found_revision && rev_in_string(buf)) 383 if (revision != NULL && !found_revision && rev_in_string(buf))
380 found_revision = true; 384 found_revision = true;
381 if ((i = strlen(buf)) > maxlen) 385 if ((i = strlen(buf)) > maxlen)
382 maxlen = i; /* find longest line */ 386 maxlen = i; /* find longest line */
383 } 387 }
384 last_line_missing_eol = i > 0 && buf[i - 1] != '\n'; 388 last_line_missing_eol = i > 0 && buf[i - 1] != '\n';
385 if (last_line_missing_eol && maxlen == i) 389 if (last_line_missing_eol && maxlen == i)
386 maxlen++; 390 maxlen++;
387 391
388 if (revision != NULL) { 392 if (revision != NULL) {
389 if (!found_revision) { 393 if (!found_revision) {
390 if (force) { 394 if (force) {
391 if (verbose) 395 if (verbose)
392 say("Warning: this file doesn't appear " 396 say("Warning: this file doesn't appear "
393 "to be the %s version--patching anyway.\n", 397 "to be the %s version--patching anyway.\n",
394 revision); 398 revision);
395 } else if (batch) { 399 } else if (batch) {
396 fatal("this file doesn't appear to be the " 400 fatal("this file doesn't appear to be the "
397 "%s version--aborting.\n", 401 "%s version--aborting.\n",
398 revision); 402 revision);
399 } else { 403 } else {
400 ask("This file doesn't appear to be the %s " 404 ask("This file doesn't appear to be the %s "
401 "version--patch anyway? [n] ", 405 "version--patch anyway? [n] ",
402 revision); 406 revision);
403 if (*buf != 'y') 407 if (*buf != 'y')
404 fatal("aborted\n"); 408 fatal("aborted\n");
405 } 409 }
406 } else if (verbose) 410 } else if (verbose)
407 say("Good. This file appears to be the %s version.\n", 411 say("Good. This file appears to be the %s version.\n",
408 revision); 412 revision);
409 } 413 }
410 fseek(ifp, 0L, SEEK_SET); /* rewind file */ 414 fseek(ifp, 0L, SEEK_SET); /* rewind file */
411 lines_per_buf = BUFFERSIZE / maxlen; 415 lines_per_buf = BUFFERSIZE / maxlen;
412 tireclen = maxlen; 416 tireclen = maxlen;
413 tibuf[0] = malloc(BUFFERSIZE + 1); 417 tibuf[0] = malloc(BUFFERSIZE + 1);
414 if (tibuf[0] == NULL) 418 if (tibuf[0] == NULL)
415 fatal("out of memory\n"); 419 fatal("out of memory\n");
416 tibuf[1] = malloc(BUFFERSIZE + 1); 420 tibuf[1] = malloc(BUFFERSIZE + 1);
417 if (tibuf[1] == NULL) 421 if (tibuf[1] == NULL)
418 fatal("out of memory\n"); 422 fatal("out of memory\n");
419 for (i = 1;; i++) { 423 for (i = 1;; i++) {
420 p = tibuf[0] + maxlen * (i % lines_per_buf); 424 p = tibuf[0] + maxlen * (i % lines_per_buf);
421 if (i % lines_per_buf == 0) /* new block */ 425 if (i % lines_per_buf == 0) /* new block */
422 if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE) 426 if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE)
423 pfatal("can't write temp file"); 427 pfatal("can't write temp file");
424 if (fgets(p, maxlen + 1, ifp) == NULL) { 428 if (fgets(p, maxlen + 1, ifp) == NULL) {
425 input_lines = i - 1; 429 input_lines = i - 1;
426 if (i % lines_per_buf != 0) 430 if (i % lines_per_buf != 0)
427 if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE) 431 if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE)
428 pfatal("can't write temp file"); 432 pfatal("can't write temp file");
429 break; 433 break;
430 } 434 }
431 j = strlen(p); 435 j = strlen(p);
432 /* These are '\n' terminated strings, so no need to add a NUL */ 436 /* These are '\n' terminated strings, so no need to add a NUL */
433 if (j == 0 || p[j - 1] != '\n') 437 if (j == 0 || p[j - 1] != '\n')
434 p[j] = '\n'; 438 p[j] = '\n';
435 } 439 }
436 fclose(ifp); 440 fclose(ifp);
437 close(tifd); 441 close(tifd);
438 if ((tifd = open(TMPINNAME, O_RDONLY)) < 0) 442 if ((tifd = open(TMPINNAME, O_RDONLY)) < 0)
439 pfatal("can't reopen file %s", TMPINNAME); 443 pfatal("can't reopen file %s", TMPINNAME);
440} 444}
441 445
442/* 446/*
443 * Fetch a line from the input file, \n terminated, not necessarily \0. 447 * Fetch a line from the input file, \n terminated, not necessarily \0.
444 */ 448 */
445char * 449char *
446ifetch(LINENUM line, int whichbuf) 450ifetch(LINENUM line, int whichbuf)
447{ 451{
448 if (line < 1 || line > input_lines) { 452 if (line < 1 || line > input_lines) {
449 if (warn_on_invalid_line) { 453 if (warn_on_invalid_line) {
450 say("No such line %ld in input file, ignoring\n", line); 454 say("No such line %ld in input file, ignoring\n", line);
451 warn_on_invalid_line = false; 455 warn_on_invalid_line = false;
452 } 456 }
453 return NULL; 457 return NULL;
454 } 458 }
455 if (using_plan_a) 459 if (using_plan_a)
456 return i_ptr[line]; 460 return i_ptr[line];
457 else { 461 else {
458 LINENUM offline = line % lines_per_buf; 462 LINENUM offline = line % lines_per_buf;
459 LINENUM baseline = line - offline; 463 LINENUM baseline = line - offline;
460 464
461 if (tiline[0] == baseline) 465 if (tiline[0] == baseline)
462 whichbuf = 0; 466 whichbuf = 0;
463 else if (tiline[1] == baseline) 467 else if (tiline[1] == baseline)
464 whichbuf = 1; 468 whichbuf = 1;
465 else { 469 else {
466 tiline[whichbuf] = baseline; 470 tiline[whichbuf] = baseline;
467 471
468 if (lseek(tifd, (off_t) (baseline / lines_per_buf * 472 if (lseek(tifd, (off_t) (baseline / lines_per_buf *
469 BUFFERSIZE), SEEK_SET) < 0) 473 BUFFERSIZE), SEEK_SET) < 0)
470 pfatal("cannot seek in the temporary input file"); 474 pfatal("cannot seek in the temporary input file");
471 475
472 if (read(tifd, tibuf[whichbuf], BUFFERSIZE) < 0) 476 if (read(tifd, tibuf[whichbuf], BUFFERSIZE) < 0)
473 pfatal("error reading tmp file %s", TMPINNAME); 477 pfatal("error reading tmp file %s", TMPINNAME);
474 } 478 }
475 return tibuf[whichbuf] + (tireclen * offline); 479 return tibuf[whichbuf] + (tireclen * offline);
476 } 480 }
477} 481}
478 482
479/* 483/*
480 * True if the string argument contains the revision number we want. 484 * True if the string argument contains the revision number we want.
481 */ 485 */
482static bool 486static bool
483rev_in_string(const char *string) 487rev_in_string(const char *string)
484{ 488{
485 const char *s; 489 const char *s;
486 size_t patlen; 490 size_t patlen;
487 491
488 if (revision == NULL) 492 if (revision == NULL)
489 return true; 493 return true;
490 patlen = strlen(revision); 494 patlen = strlen(revision);
491 if (strnEQ(string, revision, patlen) && isspace((unsigned char)string[patlen])) 495 if (strnEQ(string, revision, patlen) && isspace((unsigned char)string[patlen]))
492 return true; 496 return true;
493 for (s = string; *s; s++) { 497 for (s = string; *s; s++) {
494 if (isspace((unsigned char)*s) && strnEQ(s + 1, revision, patlen) && 498 if (isspace((unsigned char)*s) && strnEQ(s + 1, revision, patlen) &&
495 isspace((unsigned char)s[patlen + 1])) { 499 isspace((unsigned char)s[patlen + 1])) {
496 return true; 500 return true;
497 } 501 }
498 } 502 }
499 return false; 503 return false;
500} 504}