Annotation of src/usr.bin/patch/util.c, Revision 1.24
1.24 ! otto 1: /* $OpenBSD: util.c,v 1.23 2003/07/30 15:47:54 millert Exp $ */
1.2 niklas 2:
1.1 deraadt 3: #ifndef lint
1.24 ! otto 4: static const char rcsid[] = "$OpenBSD: util.c,v 1.23 2003/07/30 15:47:54 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: /*
1.23 millert 219: * Get a response from the user via /dev/tty
1.13 deraadt 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.23 millert 225: ssize_t nr;
226: static int ttyfd = -1;
1.13 deraadt 227:
228: va_start(ap, fmt);
1.23 millert 229: vfprintf(stdout, fmt, ap);
1.13 deraadt 230: va_end(ap);
1.23 millert 231: fflush(stdout);
232: if (ttyfd < 0)
233: ttyfd = open(_PATH_TTY, O_RDONLY);
234: if (ttyfd >= 0) {
235: if ((nr = read(ttyfd, buf, sizeof(buf))) > 0 &&
236: buf[nr - 1] == '\n')
237: buf[nr - 1] = '\0';
238: }
239: if (ttyfd < 0 || nr <= 0) {
240: /* no tty or error reading, pretend user entered 'return' */
241: putchar('\n');
242: buf[0] = '\0';
1.13 deraadt 243: }
1.1 deraadt 244: }
245:
1.13 deraadt 246: /*
247: * How to handle certain events when not in a critical region.
248: */
1.1 deraadt 249: void
1.13 deraadt 250: set_signals(int reset)
1.1 deraadt 251: {
1.13 deraadt 252: static sig_t hupval, intval;
1.1 deraadt 253:
1.13 deraadt 254: if (!reset) {
255: hupval = signal(SIGHUP, SIG_IGN);
256: if (hupval != SIG_IGN)
257: hupval = (sig_t) my_exit;
258: intval = signal(SIGINT, SIG_IGN);
259: if (intval != SIG_IGN)
260: intval = (sig_t) my_exit;
261: }
262: signal(SIGHUP, hupval);
263: signal(SIGINT, intval);
1.1 deraadt 264: }
265:
1.13 deraadt 266: /*
267: * How to handle certain events when in a critical region.
268: */
1.1 deraadt 269: void
1.13 deraadt 270: ignore_signals(void)
1.1 deraadt 271: {
1.13 deraadt 272: signal(SIGHUP, SIG_IGN);
273: signal(SIGINT, SIG_IGN);
1.1 deraadt 274: }
275:
1.13 deraadt 276: /*
277: * Make sure we'll have the directories to create a file. If `striplast' is
278: * TRUE, ignore the last element of `filename'.
279: */
1.1 deraadt 280:
281: void
1.20 otto 282: makedirs(const char *filename, bool striplast)
1.1 deraadt 283: {
1.13 deraadt 284: char *tmpbuf;
1.9 provos 285:
1.13 deraadt 286: if ((tmpbuf = strdup(filename)) == NULL)
287: fatal("out of memory\n");
1.9 provos 288:
1.13 deraadt 289: if (striplast) {
290: char *s = strrchr(tmpbuf, '/');
291: if (s == NULL)
292: return; /* nothing to be done */
293: *s = '\0';
294: }
1.22 millert 295: if (snprintf(buf, sizeof(buf), "%s -p %s", _PATH_MKDIR, tmpbuf)
296: >= sizeof(buf))
1.13 deraadt 297: fatal("buffer too small to hold %.20s...\n", tmpbuf);
1.9 provos 298:
1.13 deraadt 299: if (system(buf))
300: pfatal("%.40s failed", buf);
1.1 deraadt 301: }
302:
1.13 deraadt 303: /*
304: * Make filenames more reasonable.
305: */
306: char *
1.20 otto 307: fetchname(const char *at, int strip_leading, int assume_exists)
1.13 deraadt 308: {
1.20 otto 309: char *fullname, *name, *t, tmpbuf[200];
1.24 ! otto 310: int sleading;
1.20 otto 311: struct stat filestat;
1.1 deraadt 312:
1.20 otto 313: if (at == NULL || *at == '\0')
1.16 otto 314: return NULL;
1.13 deraadt 315: while (isspace(*at))
316: at++;
1.1 deraadt 317: #ifdef DEBUGGING
1.13 deraadt 318: if (debug & 128)
319: say("fetchname %s %d %d\n", at, strip_leading, assume_exists);
1.1 deraadt 320: #endif
1.22 millert 321: /* So files can be created by diffing against /dev/null. */
322: if (strnEQ(at, _PATH_DEVNULL, sizeof(_PATH_DEVNULL) - 1))
323: return NULL;
1.13 deraadt 324: name = fullname = t = savestr(at);
325:
1.21 millert 326: /* Strip off up to `strip_leading' path components and NUL terminate. */
1.24 ! otto 327: for (sleading = strip_leading; *t != '\0' && !isspace(*t); t++) {
1.21 millert 328: if (t[0] == '/' && t[1] != '/' && t[1] != '\0')
1.24 ! otto 329: if (--sleading >= 0)
1.13 deraadt 330: name = t + 1;
1.21 millert 331: }
1.13 deraadt 332: *t = '\0';
333:
334: /*
335: * If no -p option was given (957 is the default value!), we were
336: * given a relative pathname, and the leading directories that we
337: * just stripped off all exist, put them back on.
338: */
339: if (strip_leading == 957 && name != fullname && *fullname != '/') {
340: name[-1] = '\0';
341: if (stat(fullname, &filestat) == 0 && S_ISDIR(filestat.st_mode)) {
342: name[-1] = '/';
343: name = fullname;
344: }
1.1 deraadt 345: }
1.13 deraadt 346: name = savestr(name);
347: free(fullname);
1.1 deraadt 348:
1.13 deraadt 349: if (stat(name, &filestat) && !assume_exists) {
350: char *filebase = basename(name);
351: char *filedir = dirname(name);
352:
353: #define try(f, a1, a2, a3) \
354: (snprintf(tmpbuf, sizeof tmpbuf, f, a1, a2, a3), stat(tmpbuf, &filestat) == 0)
355:
356: if (try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX) ||
357: try("%s/RCS/%s%s", filedir, filebase, "") ||
358: try("%s/%s%s", filedir, filebase, RCSSUFFIX) ||
359: try("%s/SCCS/%s%s", filedir, SCCSPREFIX, filebase) ||
360: try("%s/%s%s", filedir, SCCSPREFIX, filebase))
361: return name;
362: free(name);
1.16 otto 363: name = NULL;
1.13 deraadt 364: }
365: return name;
1.12 deraadt 366: }
367:
368: void
1.13 deraadt 369: version(void)
1.12 deraadt 370: {
1.13 deraadt 371: fprintf(stderr, "Patch version 2.0-12u8-OpenBSD\n");
1.19 millert 372: my_exit(EXIT_SUCCESS);
1.16 otto 373: }
374:
375: /*
376: * Exit with cleanup.
377: */
378: void
379: my_exit(int status)
380: {
381: unlink(TMPINNAME);
382: if (!toutkeep)
383: unlink(TMPOUTNAME);
384: if (!trejkeep)
385: unlink(TMPREJNAME);
386: unlink(TMPPATNAME);
387: exit(status);
1.1 deraadt 388: }