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