util.c revision 1.29.2.1 1 1.24 joerg /*
2 1.24 joerg * $OpenBSD: util.c,v 1.32 2006/03/11 19:41:30 otto Exp $
3 1.24 joerg * $DragonFly: src/usr.bin/patch/util.c,v 1.9 2007/09/29 23:11:10 swildner Exp $
4 1.29.2.1 cjep * $NetBSD: util.c,v 1.29.2.1 2021/05/31 22:15:25 cjep Exp $
5 1.24 joerg */
6 1.16 itojun
7 1.16 itojun /*
8 1.24 joerg * patch - a program to apply diffs to original files
9 1.24 joerg *
10 1.24 joerg * Copyright 1986, Larry Wall
11 1.24 joerg *
12 1.16 itojun * Redistribution and use in source and binary forms, with or without
13 1.24 joerg * modification, are permitted provided that the following condition is met:
14 1.24 joerg * 1. Redistributions of source code must retain the above copyright notice,
15 1.24 joerg * this condition and the following disclaimer.
16 1.24 joerg *
17 1.24 joerg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
18 1.24 joerg * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 1.24 joerg * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 1.24 joerg * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
21 1.24 joerg * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 1.24 joerg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 1.24 joerg * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24 1.24 joerg * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 1.16 itojun * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 1.16 itojun * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 1.16 itojun * SUCH DAMAGE.
28 1.24 joerg *
29 1.24 joerg * -C option added in 1998, original code by Marc Espie, based on FreeBSD
30 1.24 joerg * behaviour
31 1.16 itojun */
32 1.16 itojun
33 1.5 christos #include <sys/cdefs.h>
34 1.29.2.1 cjep __RCSID("$NetBSD: util.c,v 1.29.2.1 2021/05/31 22:15:25 cjep Exp $");
35 1.2 mycroft
36 1.12 kristerw #include <sys/param.h>
37 1.24 joerg #include <sys/stat.h>
38 1.24 joerg
39 1.24 joerg #include <ctype.h>
40 1.24 joerg #include <errno.h>
41 1.24 joerg #include <fcntl.h>
42 1.24 joerg #include <libgen.h>
43 1.24 joerg #include <signal.h>
44 1.24 joerg #include <stdarg.h>
45 1.24 joerg #include <stdlib.h>
46 1.24 joerg #include <stdio.h>
47 1.24 joerg #include <string.h>
48 1.24 joerg #include <unistd.h>
49 1.12 kristerw
50 1.1 cgd #include "common.h"
51 1.1 cgd #include "util.h"
52 1.1 cgd #include "backupfile.h"
53 1.24 joerg #include "pathnames.h"
54 1.10 kristerw
55 1.24 joerg /* Rename a file, copying it if necessary. */
56 1.1 cgd
57 1.1 cgd int
58 1.24 joerg move_file(const char *from, const char *to)
59 1.1 cgd {
60 1.24 joerg int fromfd;
61 1.24 joerg ssize_t i;
62 1.21 skd
63 1.11 kristerw /* to stdout? */
64 1.1 cgd
65 1.11 kristerw if (strEQ(to, "-")) {
66 1.1 cgd #ifdef DEBUGGING
67 1.11 kristerw if (debug & 4)
68 1.11 kristerw say("Moving %s to stdout.\n", from);
69 1.1 cgd #endif
70 1.24 joerg fromfd = open(from, O_RDONLY);
71 1.11 kristerw if (fromfd < 0)
72 1.11 kristerw pfatal("internal error, can't reopen %s", from);
73 1.29.2.1 cjep while ((i = read(fromfd, buf, bufsz)) > 0)
74 1.24 joerg if (write(STDOUT_FILENO, buf, i) != i)
75 1.11 kristerw pfatal("write failed");
76 1.24 joerg close(fromfd);
77 1.11 kristerw return 0;
78 1.11 kristerw }
79 1.24 joerg if (backup_file(to) < 0) {
80 1.24 joerg say("Can't backup %s, output is in %s: %s\n", to, from,
81 1.24 joerg strerror(errno));
82 1.24 joerg return -1;
83 1.1 cgd }
84 1.1 cgd #ifdef DEBUGGING
85 1.1 cgd if (debug & 4)
86 1.11 kristerw say("Moving %s to %s.\n", from, to);
87 1.1 cgd #endif
88 1.24 joerg if (rename(from, to) < 0) {
89 1.24 joerg if (errno != EXDEV || copy_file(from, to) < 0) {
90 1.11 kristerw say("Can't create %s, output is in %s: %s\n",
91 1.11 kristerw to, from, strerror(errno));
92 1.11 kristerw return -1;
93 1.11 kristerw }
94 1.1 cgd }
95 1.24 joerg return 0;
96 1.24 joerg }
97 1.24 joerg
98 1.24 joerg /* Backup the original file. */
99 1.24 joerg
100 1.24 joerg int
101 1.24 joerg backup_file(const char *orig)
102 1.24 joerg {
103 1.24 joerg struct stat filestat;
104 1.24 joerg char bakname[MAXPATHLEN], *s, *simplename;
105 1.24 joerg dev_t orig_device;
106 1.24 joerg ino_t orig_inode;
107 1.24 joerg
108 1.24 joerg if (backup_type == none || stat(orig, &filestat) != 0)
109 1.24 joerg return 0; /* nothing to do */
110 1.24 joerg /*
111 1.24 joerg * If the user used zero prefixes or suffixes, then
112 1.24 joerg * he doesn't want backups. Yet we have to remove
113 1.24 joerg * orig to break possible hardlinks.
114 1.24 joerg */
115 1.24 joerg if ((origprae && *origprae == 0) || *simple_backup_suffix == 0) {
116 1.24 joerg unlink(orig);
117 1.24 joerg return 0;
118 1.24 joerg }
119 1.24 joerg orig_device = filestat.st_dev;
120 1.24 joerg orig_inode = filestat.st_ino;
121 1.24 joerg
122 1.24 joerg if (origprae) {
123 1.24 joerg if (strlcpy(bakname, origprae, sizeof(bakname)) >= sizeof(bakname) ||
124 1.24 joerg strlcat(bakname, orig, sizeof(bakname)) >= sizeof(bakname))
125 1.24 joerg fatal("filename %s too long for buffer\n", origprae);
126 1.24 joerg } else {
127 1.24 joerg if ((s = find_backup_file_name(orig)) == NULL)
128 1.24 joerg fatal("out of memory\n");
129 1.24 joerg if (strlcpy(bakname, s, sizeof(bakname)) >= sizeof(bakname))
130 1.24 joerg fatal("filename %s too long for buffer\n", s);
131 1.24 joerg free(s);
132 1.24 joerg }
133 1.24 joerg
134 1.24 joerg if ((simplename = strrchr(bakname, '/')) != NULL)
135 1.24 joerg simplename = simplename + 1;
136 1.24 joerg else
137 1.24 joerg simplename = bakname;
138 1.24 joerg
139 1.24 joerg /*
140 1.24 joerg * Find a backup name that is not the same file. Change the
141 1.24 joerg * first lowercase char into uppercase; if that isn't
142 1.24 joerg * sufficient, chop off the first char and try again.
143 1.24 joerg */
144 1.24 joerg while (stat(bakname, &filestat) == 0 &&
145 1.24 joerg orig_device == filestat.st_dev && orig_inode == filestat.st_ino) {
146 1.24 joerg /* Skip initial non-lowercase chars. */
147 1.24 joerg for (s = simplename; *s && !islower((unsigned char)*s); s++)
148 1.24 joerg ;
149 1.24 joerg if (*s)
150 1.24 joerg *s = toupper((unsigned char)*s);
151 1.24 joerg else
152 1.24 joerg memmove(simplename, simplename + 1,
153 1.24 joerg strlen(simplename + 1) + 1);
154 1.24 joerg }
155 1.24 joerg #ifdef DEBUGGING
156 1.24 joerg if (debug & 4)
157 1.24 joerg say("Moving %s to %s.\n", orig, bakname);
158 1.24 joerg #endif
159 1.24 joerg if (rename(orig, bakname) < 0) {
160 1.24 joerg if (errno != EXDEV || copy_file(orig, bakname) < 0)
161 1.24 joerg return -1;
162 1.24 joerg }
163 1.11 kristerw return 0;
164 1.11 kristerw }
165 1.11 kristerw
166 1.11 kristerw /*
167 1.11 kristerw * Copy a file.
168 1.11 kristerw */
169 1.24 joerg int
170 1.24 joerg copy_file(const char *from, const char *to)
171 1.11 kristerw {
172 1.24 joerg int tofd, fromfd;
173 1.24 joerg ssize_t i;
174 1.11 kristerw
175 1.24 joerg tofd = open(to, O_CREAT|O_TRUNC|O_WRONLY, 0666);
176 1.11 kristerw if (tofd < 0)
177 1.24 joerg return -1;
178 1.24 joerg fromfd = open(from, O_RDONLY, 0);
179 1.1 cgd if (fromfd < 0)
180 1.11 kristerw pfatal("internal error, can't reopen %s", from);
181 1.29.2.1 cjep while ((i = read(fromfd, buf, bufsz)) > 0)
182 1.11 kristerw if (write(tofd, buf, i) != i)
183 1.11 kristerw pfatal("write to %s failed", to);
184 1.24 joerg close(fromfd);
185 1.24 joerg close(tofd);
186 1.24 joerg return 0;
187 1.12 kristerw }
188 1.12 kristerw
189 1.12 kristerw /*
190 1.24 joerg * Allocate a unique area for a string.
191 1.11 kristerw */
192 1.1 cgd char *
193 1.24 joerg savestr(const char *s)
194 1.1 cgd {
195 1.24 joerg char *rv;
196 1.1 cgd
197 1.24 joerg if (!s)
198 1.24 joerg s = "Oops";
199 1.24 joerg rv = strdup(s);
200 1.24 joerg if (rv == NULL) {
201 1.24 joerg if (using_plan_a)
202 1.24 joerg out_of_mem = true;
203 1.24 joerg else
204 1.24 joerg fatal("out of memory\n");
205 1.24 joerg }
206 1.24 joerg return rv;
207 1.1 cgd }
208 1.1 cgd
209 1.11 kristerw /*
210 1.24 joerg * Vanilla terminal output (buffered).
211 1.11 kristerw */
212 1.1 cgd void
213 1.24 joerg say(const char *fmt, ...)
214 1.1 cgd {
215 1.24 joerg va_list ap;
216 1.24 joerg
217 1.24 joerg va_start(ap, fmt);
218 1.24 joerg vfprintf(stderr, fmt, ap);
219 1.11 kristerw va_end(ap);
220 1.24 joerg fflush(stderr);
221 1.1 cgd }
222 1.1 cgd
223 1.11 kristerw /*
224 1.11 kristerw * Terminal output, pun intended.
225 1.11 kristerw */
226 1.24 joerg void
227 1.24 joerg fatal(const char *fmt, ...)
228 1.1 cgd {
229 1.24 joerg va_list ap;
230 1.24 joerg
231 1.24 joerg va_start(ap, fmt);
232 1.11 kristerw fprintf(stderr, "patch: **** ");
233 1.24 joerg vfprintf(stderr, fmt, ap);
234 1.11 kristerw va_end(ap);
235 1.24 joerg my_exit(2);
236 1.1 cgd }
237 1.1 cgd
238 1.11 kristerw /*
239 1.24 joerg * Say something from patch, something from the system, then silence . . .
240 1.11 kristerw */
241 1.24 joerg void
242 1.24 joerg pfatal(const char *fmt, ...)
243 1.1 cgd {
244 1.24 joerg va_list ap;
245 1.24 joerg int errnum = errno;
246 1.24 joerg
247 1.11 kristerw fprintf(stderr, "patch: **** ");
248 1.24 joerg va_start(ap, fmt);
249 1.24 joerg vfprintf(stderr, fmt, ap);
250 1.24 joerg va_end(ap);
251 1.11 kristerw fprintf(stderr, ": %s\n", strerror(errnum));
252 1.24 joerg my_exit(2);
253 1.1 cgd }
254 1.1 cgd
255 1.11 kristerw /*
256 1.24 joerg * Get a response from the user via /dev/tty
257 1.11 kristerw */
258 1.1 cgd void
259 1.24 joerg ask(const char *fmt, ...)
260 1.1 cgd {
261 1.24 joerg va_list ap;
262 1.24 joerg ssize_t nr = 0;
263 1.24 joerg static int ttyfd = -1;
264 1.11 kristerw
265 1.24 joerg va_start(ap, fmt);
266 1.24 joerg vfprintf(stdout, fmt, ap);
267 1.11 kristerw va_end(ap);
268 1.24 joerg fflush(stdout);
269 1.24 joerg if (ttyfd < 0)
270 1.24 joerg ttyfd = open(_PATH_TTY, O_RDONLY);
271 1.24 joerg if (ttyfd >= 0) {
272 1.29.2.1 cjep if ((nr = read(ttyfd, buf, bufsz)) > 0 &&
273 1.24 joerg buf[nr - 1] == '\n')
274 1.24 joerg buf[nr - 1] = '\0';
275 1.24 joerg }
276 1.24 joerg if (ttyfd < 0 || nr <= 0) {
277 1.24 joerg /* no tty or error reading, pretend user entered 'return' */
278 1.24 joerg putchar('\n');
279 1.24 joerg buf[0] = '\0';
280 1.11 kristerw }
281 1.1 cgd }
282 1.1 cgd
283 1.11 kristerw /*
284 1.11 kristerw * How to handle certain events when not in a critical region.
285 1.11 kristerw */
286 1.1 cgd void
287 1.9 kristerw set_signals(int reset)
288 1.1 cgd {
289 1.24 joerg static sig_t hupval, intval;
290 1.1 cgd
291 1.11 kristerw if (!reset) {
292 1.11 kristerw hupval = signal(SIGHUP, SIG_IGN);
293 1.11 kristerw if (hupval != SIG_IGN)
294 1.24 joerg hupval = my_exit;
295 1.11 kristerw intval = signal(SIGINT, SIG_IGN);
296 1.11 kristerw if (intval != SIG_IGN)
297 1.24 joerg intval = my_exit;
298 1.11 kristerw }
299 1.24 joerg signal(SIGHUP, hupval);
300 1.24 joerg signal(SIGINT, intval);
301 1.1 cgd }
302 1.1 cgd
303 1.11 kristerw /*
304 1.11 kristerw * How to handle certain events when in a critical region.
305 1.11 kristerw */
306 1.1 cgd void
307 1.24 joerg ignore_signals(void)
308 1.1 cgd {
309 1.24 joerg signal(SIGHUP, SIG_IGN);
310 1.24 joerg signal(SIGINT, SIG_IGN);
311 1.1 cgd }
312 1.1 cgd
313 1.11 kristerw /*
314 1.24 joerg * Make sure we'll have the directories to create a file. If `striplast' is
315 1.24 joerg * true, ignore the last element of `filename'.
316 1.11 kristerw */
317 1.24 joerg
318 1.1 cgd void
319 1.24 joerg makedirs(const char *filename, bool striplast)
320 1.1 cgd {
321 1.24 joerg char *tmpbuf;
322 1.24 joerg
323 1.24 joerg if ((tmpbuf = strdup(filename)) == NULL)
324 1.24 joerg fatal("out of memory\n");
325 1.11 kristerw
326 1.24 joerg if (striplast) {
327 1.24 joerg char *s = strrchr(tmpbuf, '/');
328 1.25 joerg if (s == NULL) {
329 1.25 joerg free(tmpbuf);
330 1.24 joerg return; /* nothing to be done */
331 1.25 joerg }
332 1.24 joerg *s = '\0';
333 1.24 joerg }
334 1.24 joerg if (mkpath(tmpbuf) != 0)
335 1.24 joerg pfatal("creation of %s failed", tmpbuf);
336 1.24 joerg free(tmpbuf);
337 1.1 cgd }
338 1.1 cgd
339 1.11 kristerw /*
340 1.11 kristerw * Make filenames more reasonable.
341 1.11 kristerw */
342 1.1 cgd char *
343 1.24 joerg fetchname(const char *at, bool *exists, int strip_leading)
344 1.1 cgd {
345 1.24 joerg char *fullname, *name, *t;
346 1.24 joerg int sleading, tab;
347 1.24 joerg struct stat filestat;
348 1.11 kristerw
349 1.24 joerg if (at == NULL || *at == '\0')
350 1.11 kristerw return NULL;
351 1.11 kristerw while (isspace((unsigned char)*at))
352 1.11 kristerw at++;
353 1.1 cgd #ifdef DEBUGGING
354 1.11 kristerw if (debug & 128)
355 1.24 joerg say("fetchname %s %d\n", at, strip_leading);
356 1.1 cgd #endif
357 1.24 joerg /* So files can be created by diffing against /dev/null. */
358 1.24 joerg if (strnEQ(at, _PATH_DEVNULL, sizeof(_PATH_DEVNULL) - 1))
359 1.11 kristerw return NULL;
360 1.24 joerg name = fullname = t = savestr(at);
361 1.11 kristerw
362 1.24 joerg tab = strchr(t, '\t') != NULL;
363 1.24 joerg /* Strip off up to `strip_leading' path components and NUL terminate. */
364 1.24 joerg for (sleading = strip_leading; *t != '\0' && ((tab && *t != '\t') ||
365 1.24 joerg !isspace((unsigned char)*t)); t++) {
366 1.24 joerg if (t[0] == '/' && t[1] != '/' && t[1] != '\0')
367 1.11 kristerw if (--sleading >= 0)
368 1.11 kristerw name = t + 1;
369 1.24 joerg }
370 1.11 kristerw *t = '\0';
371 1.11 kristerw
372 1.11 kristerw /*
373 1.24 joerg * If no -p option was given (957 is the default value!), we were
374 1.24 joerg * given a relative pathname, and the leading directories that we
375 1.24 joerg * just stripped off all exist, put them back on.
376 1.11 kristerw */
377 1.11 kristerw if (strip_leading == 957 && name != fullname && *fullname != '/') {
378 1.11 kristerw name[-1] = '\0';
379 1.24 joerg if (stat(fullname, &filestat) == 0 && S_ISDIR(filestat.st_mode)) {
380 1.11 kristerw name[-1] = '/';
381 1.11 kristerw name = fullname;
382 1.11 kristerw }
383 1.11 kristerw }
384 1.24 joerg name = savestr(name);
385 1.24 joerg free(fullname);
386 1.24 joerg
387 1.24 joerg *exists = stat(name, &filestat) == 0;
388 1.24 joerg return name;
389 1.24 joerg }
390 1.24 joerg
391 1.24 joerg /*
392 1.24 joerg * Takes the name returned by fetchname and looks in RCS/SCCS directories
393 1.24 joerg * for a checked in version.
394 1.24 joerg */
395 1.24 joerg char *
396 1.24 joerg checked_in(char *file)
397 1.24 joerg {
398 1.24 joerg char *filebase, *filedir, tmpbuf[MAXPATHLEN];
399 1.24 joerg struct stat filestat;
400 1.11 kristerw
401 1.24 joerg filebase = basename(file);
402 1.24 joerg filedir = dirname(file);
403 1.24 joerg
404 1.24 joerg #define try(f, a1, a2, a3) \
405 1.24 joerg (snprintf(tmpbuf, sizeof tmpbuf, f, a1, a2, a3), stat(tmpbuf, &filestat) == 0)
406 1.24 joerg
407 1.24 joerg if (try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX) ||
408 1.24 joerg try("%s/RCS/%s%s", filedir, filebase, "") ||
409 1.24 joerg try("%s/%s%s", filedir, filebase, RCSSUFFIX) ||
410 1.24 joerg try("%s/SCCS/%s%s", filedir, SCCSPREFIX, filebase) ||
411 1.24 joerg try("%s/%s%s", filedir, SCCSPREFIX, filebase))
412 1.24 joerg return file;
413 1.24 joerg
414 1.24 joerg return NULL;
415 1.24 joerg }
416 1.11 kristerw
417 1.24 joerg void
418 1.24 joerg version(void)
419 1.24 joerg {
420 1.29 rhialto printf("Patch version 2.0-12u9-NetBSD\n");
421 1.24 joerg my_exit(EXIT_SUCCESS);
422 1.24 joerg }
423 1.1 cgd
424 1.24 joerg /*
425 1.24 joerg * Exit with cleanup.
426 1.24 joerg */
427 1.24 joerg void
428 1.24 joerg my_exit(int status)
429 1.24 joerg {
430 1.24 joerg unlink(TMPINNAME);
431 1.24 joerg if (!toutkeep)
432 1.24 joerg unlink(TMPOUTNAME);
433 1.24 joerg if (!trejkeep)
434 1.24 joerg unlink(TMPREJNAME);
435 1.24 joerg unlink(TMPPATNAME);
436 1.24 joerg exit(status);
437 1.1 cgd }
438 1.28 christos
439 1.28 christos void *
440 1.28 christos pch_realloc(void *ptr, size_t number, size_t size)
441 1.28 christos {
442 1.28 christos if (number > SIZE_MAX / size) {
443 1.28 christos errno = EOVERFLOW;
444 1.28 christos return NULL;
445 1.28 christos }
446 1.28 christos return realloc(ptr, number * size);
447 1.28 christos }
448