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