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