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