Annotation of src/usr.bin/patch/util.c, Revision 1.38
1.38 ! tobias 1: /* $OpenBSD: util.c,v 1.37 2014/11/22 15:49:28 tobias Exp $ */
1.26 otto 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: */
1.1 deraadt 28:
1.16 otto 29: #include <sys/param.h>
30: #include <sys/stat.h>
31:
32: #include <ctype.h>
33: #include <errno.h>
34: #include <fcntl.h>
35: #include <libgen.h>
36: #include <paths.h>
1.32 otto 37: #include <signal.h>
1.16 otto 38: #include <stdarg.h>
39: #include <stdlib.h>
1.20 otto 40: #include <stdio.h>
1.16 otto 41: #include <string.h>
42: #include <unistd.h>
43:
1.1 deraadt 44: #include "common.h"
45: #include "util.h"
46: #include "backupfile.h"
1.22 millert 47: #include "pathnames.h"
1.1 deraadt 48:
49: /* Rename a file, copying it if necessary. */
50:
51: int
1.20 otto 52: move_file(const char *from, const char *to)
1.1 deraadt 53: {
1.20 otto 54: int fromfd;
55: ssize_t i;
1.1 deraadt 56:
1.13 deraadt 57: /* to stdout? */
1.1 deraadt 58:
1.13 deraadt 59: if (strEQ(to, "-")) {
1.1 deraadt 60: #ifdef DEBUGGING
1.13 deraadt 61: if (debug & 4)
62: say("Moving %s to stdout.\n", from);
1.1 deraadt 63: #endif
1.13 deraadt 64: fromfd = open(from, O_RDONLY);
65: if (fromfd < 0)
66: pfatal("internal error, can't reopen %s", from);
67: while ((i = read(fromfd, buf, sizeof buf)) > 0)
1.16 otto 68: if (write(STDOUT_FILENO, buf, i) != i)
1.13 deraadt 69: pfatal("write failed");
70: close(fromfd);
71: return 0;
1.1 deraadt 72: }
1.18 millert 73: if (backup_file(to) < 0) {
74: say("Can't backup %s, output is in %s: %s\n", to, from,
75: strerror(errno));
76: return -1;
77: }
78: #ifdef DEBUGGING
79: if (debug & 4)
80: say("Moving %s to %s.\n", from, to);
81: #endif
82: if (rename(from, to) < 0) {
83: if (errno != EXDEV || copy_file(from, to) < 0) {
84: say("Can't create %s, output is in %s: %s\n",
85: to, from, strerror(errno));
86: return -1;
87: }
88: }
89: return 0;
90: }
91:
92: /* Backup the original file. */
93:
94: int
1.20 otto 95: backup_file(const char *orig)
1.18 millert 96: {
1.20 otto 97: struct stat filestat;
98: char bakname[MAXPATHLEN], *s, *simplename;
99: dev_t orig_device;
100: ino_t orig_inode;
1.18 millert 101:
102: if (backup_type == none || stat(orig, &filestat) != 0)
103: return 0; /* nothing to do */
104: orig_device = filestat.st_dev;
105: orig_inode = filestat.st_ino;
106:
1.13 deraadt 107: if (origprae) {
108: if (strlcpy(bakname, origprae, sizeof(bakname)) >= sizeof(bakname) ||
1.18 millert 109: strlcat(bakname, orig, sizeof(bakname)) >= sizeof(bakname))
1.13 deraadt 110: fatal("filename %s too long for buffer\n", origprae);
111: } else {
1.18 millert 112: if ((s = find_backup_file_name(orig)) == NULL)
1.13 deraadt 113: fatal("out of memory\n");
1.18 millert 114: if (strlcpy(bakname, s, sizeof(bakname)) >= sizeof(bakname))
115: fatal("filename %s too long for buffer\n", s);
116: free(s);
1.1 deraadt 117: }
1.13 deraadt 118:
1.18 millert 119: if ((simplename = strrchr(bakname, '/')) != NULL)
120: simplename = simplename + 1;
121: else
122: simplename = bakname;
123:
124: /*
125: * Find a backup name that is not the same file. Change the
126: * first lowercase char into uppercase; if that isn't
127: * sufficient, chop off the first char and try again.
128: */
129: while (stat(bakname, &filestat) == 0 &&
130: orig_device == filestat.st_dev && orig_inode == filestat.st_ino) {
131: /* Skip initial non-lowercase chars. */
1.36 deraadt 132: for (s = simplename; *s && !islower((unsigned char)*s); s++)
1.18 millert 133: ;
134: if (*s)
1.36 deraadt 135: *s = toupper((unsigned char)*s);
1.18 millert 136: else
137: memmove(simplename, simplename + 1,
138: strlen(simplename + 1) + 1);
1.1 deraadt 139: }
140: #ifdef DEBUGGING
1.13 deraadt 141: if (debug & 4)
1.18 millert 142: say("Moving %s to %s.\n", orig, bakname);
1.1 deraadt 143: #endif
1.18 millert 144: if (rename(orig, bakname) < 0) {
145: if (errno != EXDEV || copy_file(orig, bakname) < 0)
1.13 deraadt 146: return -1;
147: }
148: return 0;
149: }
150:
151: /*
152: * Copy a file.
153: */
1.18 millert 154: int
1.20 otto 155: copy_file(const char *from, const char *to)
1.13 deraadt 156: {
1.20 otto 157: int tofd, fromfd;
158: ssize_t i;
1.12 deraadt 159:
1.18 millert 160: tofd = open(to, O_CREAT|O_TRUNC|O_WRONLY, 0666);
1.13 deraadt 161: if (tofd < 0)
1.18 millert 162: return -1;
163: fromfd = open(from, O_RDONLY, 0);
1.1 deraadt 164: if (fromfd < 0)
1.13 deraadt 165: pfatal("internal error, can't reopen %s", from);
166: while ((i = read(fromfd, buf, sizeof buf)) > 0)
167: if (write(tofd, buf, i) != i)
168: pfatal("write to %s failed", to);
1.12 deraadt 169: close(fromfd);
170: close(tofd);
1.18 millert 171: return 0;
1.1 deraadt 172: }
173:
1.13 deraadt 174: /*
175: * Allocate a unique area for a string.
176: */
1.1 deraadt 177: char *
1.20 otto 178: savestr(const char *s)
1.1 deraadt 179: {
1.20 otto 180: char *rv;
1.1 deraadt 181:
1.13 deraadt 182: if (!s)
183: s = "Oops";
1.20 otto 184: rv = strdup(s);
1.16 otto 185: if (rv == NULL) {
1.13 deraadt 186: if (using_plan_a)
1.25 otto 187: out_of_mem = true;
1.13 deraadt 188: else
189: fatal("out of memory\n");
190: }
1.38 ! tobias 191: return rv;
! 192: }
! 193:
! 194: /*
! 195: * Allocate a unique area for a string. Call fatal if out of memory.
! 196: */
! 197: char *
! 198: xstrdup(const char *s)
! 199: {
! 200: char *rv;
! 201:
! 202: if (!s)
! 203: s = "Oops";
! 204: rv = strdup(s);
! 205: if (rv == NULL)
! 206: fatal("out of memory\n");
1.13 deraadt 207: return rv;
1.1 deraadt 208: }
209:
1.13 deraadt 210: /*
211: * Vanilla terminal output (buffered).
212: */
1.1 deraadt 213: void
1.20 otto 214: say(const char *fmt, ...)
1.1 deraadt 215: {
1.13 deraadt 216: va_list ap;
1.12 deraadt 217:
1.13 deraadt 218: va_start(ap, fmt);
1.35 ray 219: vfprintf(stdout, fmt, ap);
1.13 deraadt 220: va_end(ap);
1.35 ray 221: fflush(stdout);
1.1 deraadt 222: }
223:
1.13 deraadt 224: /*
225: * Terminal output, pun intended.
226: */
1.12 deraadt 227: void
1.20 otto 228: fatal(const char *fmt, ...)
1.1 deraadt 229: {
1.13 deraadt 230: va_list ap;
1.12 deraadt 231:
1.13 deraadt 232: va_start(ap, fmt);
233: fprintf(stderr, "patch: **** ");
234: vfprintf(stderr, fmt, ap);
235: va_end(ap);
1.19 millert 236: my_exit(2);
1.1 deraadt 237: }
238:
1.13 deraadt 239: /*
240: * Say something from patch, something from the system, then silence . . .
241: */
1.12 deraadt 242: void
1.20 otto 243: pfatal(const char *fmt, ...)
1.1 deraadt 244: {
1.13 deraadt 245: va_list ap;
246: int errnum = errno;
1.1 deraadt 247:
1.13 deraadt 248: fprintf(stderr, "patch: **** ");
249: va_start(ap, fmt);
250: vfprintf(stderr, fmt, ap);
251: va_end(ap);
252: fprintf(stderr, ": %s\n", strerror(errnum));
1.19 millert 253: my_exit(2);
1.1 deraadt 254: }
255:
1.13 deraadt 256: /*
1.23 millert 257: * Get a response from the user via /dev/tty
1.13 deraadt 258: */
1.1 deraadt 259: void
1.20 otto 260: ask(const char *fmt, ...)
1.1 deraadt 261: {
1.13 deraadt 262: va_list ap;
1.23 millert 263: ssize_t nr;
264: static int ttyfd = -1;
1.13 deraadt 265:
266: va_start(ap, fmt);
1.23 millert 267: vfprintf(stdout, fmt, ap);
1.13 deraadt 268: va_end(ap);
1.23 millert 269: fflush(stdout);
270: if (ttyfd < 0)
271: ttyfd = open(_PATH_TTY, O_RDONLY);
272: if (ttyfd >= 0) {
273: if ((nr = read(ttyfd, buf, sizeof(buf))) > 0 &&
274: buf[nr - 1] == '\n')
275: buf[nr - 1] = '\0';
276: }
277: if (ttyfd < 0 || nr <= 0) {
278: /* no tty or error reading, pretend user entered 'return' */
279: putchar('\n');
280: buf[0] = '\0';
1.13 deraadt 281: }
1.1 deraadt 282: }
283:
1.13 deraadt 284: /*
285: * How to handle certain events when not in a critical region.
286: */
1.1 deraadt 287: void
1.13 deraadt 288: set_signals(int reset)
1.1 deraadt 289: {
1.13 deraadt 290: static sig_t hupval, intval;
1.1 deraadt 291:
1.13 deraadt 292: if (!reset) {
293: hupval = signal(SIGHUP, SIG_IGN);
294: if (hupval != SIG_IGN)
295: hupval = (sig_t) my_exit;
296: intval = signal(SIGINT, SIG_IGN);
297: if (intval != SIG_IGN)
298: intval = (sig_t) my_exit;
299: }
300: signal(SIGHUP, hupval);
301: signal(SIGINT, intval);
1.1 deraadt 302: }
303:
1.13 deraadt 304: /*
305: * How to handle certain events when in a critical region.
306: */
1.1 deraadt 307: void
1.13 deraadt 308: ignore_signals(void)
1.1 deraadt 309: {
1.13 deraadt 310: signal(SIGHUP, SIG_IGN);
311: signal(SIGINT, SIG_IGN);
1.1 deraadt 312: }
313:
1.13 deraadt 314: /*
315: * Make sure we'll have the directories to create a file. If `striplast' is
1.25 otto 316: * true, ignore the last element of `filename'.
1.13 deraadt 317: */
1.1 deraadt 318:
319: void
1.20 otto 320: makedirs(const char *filename, bool striplast)
1.1 deraadt 321: {
1.13 deraadt 322: char *tmpbuf;
1.9 provos 323:
1.13 deraadt 324: if ((tmpbuf = strdup(filename)) == NULL)
325: fatal("out of memory\n");
1.9 provos 326:
1.13 deraadt 327: if (striplast) {
328: char *s = strrchr(tmpbuf, '/');
1.34 oga 329: if (s == NULL) {
330: free(tmpbuf);
1.13 deraadt 331: return; /* nothing to be done */
1.34 oga 332: }
1.13 deraadt 333: *s = '\0';
334: }
1.31 otto 335: if (mkpath(tmpbuf) != 0)
1.30 espie 336: pfatal("creation of %s failed", tmpbuf);
337: free(tmpbuf);
1.1 deraadt 338: }
339:
1.13 deraadt 340: /*
341: * Make filenames more reasonable.
342: */
343: char *
1.27 millert 344: fetchname(const char *at, bool *exists, int strip_leading)
1.13 deraadt 345: {
1.27 millert 346: char *fullname, *name, *t;
1.29 otto 347: int sleading, tab;
1.20 otto 348: struct stat filestat;
1.1 deraadt 349:
1.20 otto 350: if (at == NULL || *at == '\0')
1.16 otto 351: return NULL;
1.36 deraadt 352: while (isspace((unsigned char)*at))
1.13 deraadt 353: at++;
1.1 deraadt 354: #ifdef DEBUGGING
1.13 deraadt 355: if (debug & 128)
1.27 millert 356: say("fetchname %s %d\n", at, strip_leading);
1.1 deraadt 357: #endif
1.22 millert 358: /* So files can be created by diffing against /dev/null. */
359: if (strnEQ(at, _PATH_DEVNULL, sizeof(_PATH_DEVNULL) - 1))
360: return NULL;
1.13 deraadt 361: name = fullname = t = savestr(at);
362:
1.29 otto 363: tab = strchr(t, '\t') != NULL;
1.21 millert 364: /* Strip off up to `strip_leading' path components and NUL terminate. */
1.29 otto 365: for (sleading = strip_leading; *t != '\0' && ((tab && *t != '\t') ||
1.36 deraadt 366: !isspace((unsigned char)*t)); t++) {
1.21 millert 367: if (t[0] == '/' && t[1] != '/' && t[1] != '\0')
1.24 otto 368: if (--sleading >= 0)
1.13 deraadt 369: name = t + 1;
1.21 millert 370: }
1.13 deraadt 371: *t = '\0';
372:
373: /*
374: * If no -p option was given (957 is the default value!), we were
375: * given a relative pathname, and the leading directories that we
376: * just stripped off all exist, put them back on.
377: */
378: if (strip_leading == 957 && name != fullname && *fullname != '/') {
379: name[-1] = '\0';
380: if (stat(fullname, &filestat) == 0 && S_ISDIR(filestat.st_mode)) {
381: name[-1] = '/';
382: name = fullname;
383: }
1.1 deraadt 384: }
1.13 deraadt 385: name = savestr(name);
386: free(fullname);
1.1 deraadt 387:
1.27 millert 388: *exists = stat(name, &filestat) == 0;
389: return name;
390: }
391:
392: /*
1.37 tobias 393: * Takes the name returned by fetchname and looks in RCS directory
1.27 millert 394: * for a checked in version.
395: */
396: char *
397: checked_in(char *file)
398: {
399: char *filebase, *filedir, tmpbuf[MAXPATHLEN];
400: struct stat filestat;
401:
402: filebase = basename(file);
403: filedir = dirname(file);
1.13 deraadt 404:
405: #define try(f, a1, a2, a3) \
1.27 millert 406: (snprintf(tmpbuf, sizeof tmpbuf, f, a1, a2, a3), stat(tmpbuf, &filestat) == 0)
1.13 deraadt 407:
1.27 millert 408: if (try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX) ||
409: try("%s/RCS/%s%s", filedir, filebase, "") ||
1.37 tobias 410: try("%s/%s%s", filedir, filebase, RCSSUFFIX))
1.27 millert 411: return file;
412:
413: return NULL;
1.12 deraadt 414: }
415:
416: void
1.13 deraadt 417: version(void)
1.12 deraadt 418: {
1.13 deraadt 419: fprintf(stderr, "Patch version 2.0-12u8-OpenBSD\n");
1.19 millert 420: my_exit(EXIT_SUCCESS);
1.16 otto 421: }
422:
423: /*
424: * Exit with cleanup.
425: */
426: void
427: my_exit(int status)
428: {
429: unlink(TMPINNAME);
430: if (!toutkeep)
431: unlink(TMPOUTNAME);
432: if (!trejkeep)
433: unlink(TMPREJNAME);
434: unlink(TMPPATNAME);
435: exit(status);
1.1 deraadt 436: }