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