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