Annotation of src/usr.bin/patch/util.c, Revision 1.27
1.24 joerg 1: /*
2: * $OpenBSD: util.c,v 1.32 2006/03/11 19:41:30 otto Exp $
3: * $DragonFly: src/usr.bin/patch/util.c,v 1.9 2007/09/29 23:11:10 swildner Exp $
1.27 ! joerg 4: * $NetBSD: util.c,v 1.26 2010/10/02 19:31:14 wiz Exp $
1.24 joerg 5: */
1.16 itojun 6:
7: /*
1.24 joerg 8: * patch - a program to apply diffs to original files
9: *
10: * Copyright 1986, Larry Wall
11: *
1.16 itojun 12: * Redistribution and use in source and binary forms, with or without
1.24 joerg 13: * modification, are permitted provided that the following condition is met:
14: * 1. Redistributions of source code must retain the above copyright notice,
15: * this condition and the following disclaimer.
16: *
17: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
18: * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20: * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
21: * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23: * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24: * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
1.16 itojun 25: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27: * SUCH DAMAGE.
1.24 joerg 28: *
29: * -C option added in 1998, original code by Marc Espie, based on FreeBSD
30: * behaviour
1.16 itojun 31: */
32:
1.5 christos 33: #include <sys/cdefs.h>
1.27 ! joerg 34: __RCSID("$NetBSD: util.c,v 1.26 2010/10/02 19:31:14 wiz Exp $");
1.2 mycroft 35:
1.12 kristerw 36: #include <sys/param.h>
1.24 joerg 37: #include <sys/stat.h>
38:
39: #include <ctype.h>
40: #include <errno.h>
41: #include <fcntl.h>
42: #include <libgen.h>
43: #include <signal.h>
44: #include <stdarg.h>
45: #include <stdlib.h>
46: #include <stdio.h>
47: #include <string.h>
48: #include <unistd.h>
1.12 kristerw 49:
1.1 cgd 50: #include "common.h"
51: #include "util.h"
52: #include "backupfile.h"
1.24 joerg 53: #include "pathnames.h"
1.10 kristerw 54:
1.24 joerg 55: /* Rename a file, copying it if necessary. */
1.1 cgd 56:
57: int
1.24 joerg 58: move_file(const char *from, const char *to)
1.1 cgd 59: {
1.24 joerg 60: int fromfd;
61: ssize_t i;
1.21 skd 62:
1.11 kristerw 63: /* to stdout? */
1.1 cgd 64:
1.11 kristerw 65: if (strEQ(to, "-")) {
1.1 cgd 66: #ifdef DEBUGGING
1.11 kristerw 67: if (debug & 4)
68: say("Moving %s to stdout.\n", from);
1.1 cgd 69: #endif
1.24 joerg 70: fromfd = open(from, O_RDONLY);
1.11 kristerw 71: if (fromfd < 0)
72: pfatal("internal error, can't reopen %s", from);
1.24 joerg 73: while ((i = read(fromfd, buf, buf_len)) > 0)
74: if (write(STDOUT_FILENO, buf, i) != i)
1.11 kristerw 75: pfatal("write failed");
1.24 joerg 76: close(fromfd);
1.11 kristerw 77: return 0;
78: }
1.24 joerg 79: if (backup_file(to) < 0) {
80: say("Can't backup %s, output is in %s: %s\n", to, from,
81: strerror(errno));
82: return -1;
1.1 cgd 83: }
84: #ifdef DEBUGGING
85: if (debug & 4)
1.11 kristerw 86: say("Moving %s to %s.\n", from, to);
1.1 cgd 87: #endif
1.24 joerg 88: if (rename(from, to) < 0) {
89: if (errno != EXDEV || copy_file(from, to) < 0) {
1.11 kristerw 90: say("Can't create %s, output is in %s: %s\n",
91: to, from, strerror(errno));
92: return -1;
93: }
1.1 cgd 94: }
1.24 joerg 95: return 0;
96: }
97:
98: /* Backup the original file. */
99:
100: int
101: backup_file(const char *orig)
102: {
103: struct stat filestat;
104: char bakname[MAXPATHLEN], *s, *simplename;
105: dev_t orig_device;
106: ino_t orig_inode;
107:
108: if (backup_type == none || stat(orig, &filestat) != 0)
109: return 0; /* nothing to do */
110: /*
111: * If the user used zero prefixes or suffixes, then
112: * he doesn't want backups. Yet we have to remove
113: * orig to break possible hardlinks.
114: */
115: if ((origprae && *origprae == 0) || *simple_backup_suffix == 0) {
116: unlink(orig);
117: return 0;
118: }
119: orig_device = filestat.st_dev;
120: orig_inode = filestat.st_ino;
121:
122: if (origprae) {
123: if (strlcpy(bakname, origprae, sizeof(bakname)) >= sizeof(bakname) ||
124: strlcat(bakname, orig, sizeof(bakname)) >= sizeof(bakname))
125: fatal("filename %s too long for buffer\n", origprae);
126: } else {
127: if ((s = find_backup_file_name(orig)) == NULL)
128: fatal("out of memory\n");
129: if (strlcpy(bakname, s, sizeof(bakname)) >= sizeof(bakname))
130: fatal("filename %s too long for buffer\n", s);
131: free(s);
132: }
133:
134: if ((simplename = strrchr(bakname, '/')) != NULL)
135: simplename = simplename + 1;
136: else
137: simplename = bakname;
138:
139: /*
140: * Find a backup name that is not the same file. Change the
141: * first lowercase char into uppercase; if that isn't
142: * sufficient, chop off the first char and try again.
143: */
144: while (stat(bakname, &filestat) == 0 &&
145: orig_device == filestat.st_dev && orig_inode == filestat.st_ino) {
146: /* Skip initial non-lowercase chars. */
147: for (s = simplename; *s && !islower((unsigned char)*s); s++)
148: ;
149: if (*s)
150: *s = toupper((unsigned char)*s);
151: else
152: memmove(simplename, simplename + 1,
153: strlen(simplename + 1) + 1);
154: }
155: #ifdef DEBUGGING
156: if (debug & 4)
157: say("Moving %s to %s.\n", orig, bakname);
158: #endif
159: if (rename(orig, bakname) < 0) {
160: if (errno != EXDEV || copy_file(orig, bakname) < 0)
161: return -1;
162: }
1.11 kristerw 163: return 0;
164: }
165:
166: /*
167: * Copy a file.
168: */
1.24 joerg 169: int
170: copy_file(const char *from, const char *to)
1.11 kristerw 171: {
1.24 joerg 172: int tofd, fromfd;
173: ssize_t i;
1.11 kristerw 174:
1.24 joerg 175: tofd = open(to, O_CREAT|O_TRUNC|O_WRONLY, 0666);
1.11 kristerw 176: if (tofd < 0)
1.24 joerg 177: return -1;
178: fromfd = open(from, O_RDONLY, 0);
1.1 cgd 179: if (fromfd < 0)
1.11 kristerw 180: pfatal("internal error, can't reopen %s", from);
1.24 joerg 181: while ((i = read(fromfd, buf, buf_len)) > 0)
1.11 kristerw 182: if (write(tofd, buf, i) != i)
183: pfatal("write to %s failed", to);
1.24 joerg 184: close(fromfd);
185: close(tofd);
186: return 0;
1.12 kristerw 187: }
188:
189: /*
1.24 joerg 190: * Allocate a unique area for a string.
1.11 kristerw 191: */
1.1 cgd 192: char *
1.24 joerg 193: savestr(const char *s)
1.1 cgd 194: {
1.24 joerg 195: char *rv;
1.1 cgd 196:
1.24 joerg 197: if (!s)
198: s = "Oops";
199: rv = strdup(s);
200: if (rv == NULL) {
201: if (using_plan_a)
202: out_of_mem = true;
203: else
204: fatal("out of memory\n");
205: }
206: return rv;
1.1 cgd 207: }
208:
1.11 kristerw 209: /*
1.24 joerg 210: * Vanilla terminal output (buffered).
1.11 kristerw 211: */
1.1 cgd 212: void
1.24 joerg 213: say(const char *fmt, ...)
1.1 cgd 214: {
1.24 joerg 215: va_list ap;
216:
217: va_start(ap, fmt);
218: vfprintf(stderr, fmt, ap);
1.11 kristerw 219: va_end(ap);
1.24 joerg 220: fflush(stderr);
1.1 cgd 221: }
222:
1.11 kristerw 223: /*
224: * Terminal output, pun intended.
225: */
1.24 joerg 226: void
227: fatal(const char *fmt, ...)
1.1 cgd 228: {
1.24 joerg 229: va_list ap;
230:
231: va_start(ap, fmt);
1.11 kristerw 232: fprintf(stderr, "patch: **** ");
1.24 joerg 233: vfprintf(stderr, fmt, ap);
1.11 kristerw 234: va_end(ap);
1.24 joerg 235: my_exit(2);
1.1 cgd 236: }
237:
1.11 kristerw 238: /*
1.24 joerg 239: * Say something from patch, something from the system, then silence . . .
1.11 kristerw 240: */
1.24 joerg 241: void
242: pfatal(const char *fmt, ...)
1.1 cgd 243: {
1.24 joerg 244: va_list ap;
245: int errnum = errno;
246:
1.11 kristerw 247: fprintf(stderr, "patch: **** ");
1.24 joerg 248: va_start(ap, fmt);
249: vfprintf(stderr, fmt, ap);
250: va_end(ap);
1.11 kristerw 251: fprintf(stderr, ": %s\n", strerror(errnum));
1.24 joerg 252: my_exit(2);
1.1 cgd 253: }
254:
1.11 kristerw 255: /*
1.24 joerg 256: * Get a response from the user via /dev/tty
1.11 kristerw 257: */
1.1 cgd 258: void
1.24 joerg 259: ask(const char *fmt, ...)
1.1 cgd 260: {
1.24 joerg 261: va_list ap;
262: ssize_t nr = 0;
263: static int ttyfd = -1;
1.11 kristerw 264:
1.24 joerg 265: va_start(ap, fmt);
266: vfprintf(stdout, fmt, ap);
1.11 kristerw 267: va_end(ap);
1.24 joerg 268: fflush(stdout);
269: if (ttyfd < 0)
270: ttyfd = open(_PATH_TTY, O_RDONLY);
271: if (ttyfd >= 0) {
272: if ((nr = read(ttyfd, buf, buf_len)) > 0 &&
273: buf[nr - 1] == '\n')
274: buf[nr - 1] = '\0';
275: }
276: if (ttyfd < 0 || nr <= 0) {
277: /* no tty or error reading, pretend user entered 'return' */
278: putchar('\n');
279: buf[0] = '\0';
1.11 kristerw 280: }
1.1 cgd 281: }
282:
1.11 kristerw 283: /*
284: * How to handle certain events when not in a critical region.
285: */
1.1 cgd 286: void
1.9 kristerw 287: set_signals(int reset)
1.1 cgd 288: {
1.24 joerg 289: static sig_t hupval, intval;
1.1 cgd 290:
1.11 kristerw 291: if (!reset) {
292: hupval = signal(SIGHUP, SIG_IGN);
293: if (hupval != SIG_IGN)
1.24 joerg 294: hupval = my_exit;
1.11 kristerw 295: intval = signal(SIGINT, SIG_IGN);
296: if (intval != SIG_IGN)
1.24 joerg 297: intval = my_exit;
1.11 kristerw 298: }
1.24 joerg 299: signal(SIGHUP, hupval);
300: signal(SIGINT, intval);
1.1 cgd 301: }
302:
1.11 kristerw 303: /*
304: * How to handle certain events when in a critical region.
305: */
1.1 cgd 306: void
1.24 joerg 307: ignore_signals(void)
1.1 cgd 308: {
1.24 joerg 309: signal(SIGHUP, SIG_IGN);
310: signal(SIGINT, SIG_IGN);
1.1 cgd 311: }
312:
1.11 kristerw 313: /*
1.24 joerg 314: * Make sure we'll have the directories to create a file. If `striplast' is
315: * true, ignore the last element of `filename'.
1.11 kristerw 316: */
1.24 joerg 317:
1.1 cgd 318: void
1.24 joerg 319: makedirs(const char *filename, bool striplast)
1.1 cgd 320: {
1.24 joerg 321: char *tmpbuf;
322:
323: if ((tmpbuf = strdup(filename)) == NULL)
324: fatal("out of memory\n");
1.11 kristerw 325:
1.24 joerg 326: if (striplast) {
327: char *s = strrchr(tmpbuf, '/');
1.25 joerg 328: if (s == NULL) {
329: free(tmpbuf);
1.24 joerg 330: return; /* nothing to be done */
1.25 joerg 331: }
1.24 joerg 332: *s = '\0';
333: }
334: if (mkpath(tmpbuf) != 0)
335: pfatal("creation of %s failed", tmpbuf);
336: free(tmpbuf);
1.1 cgd 337: }
338:
1.11 kristerw 339: /*
340: * Make filenames more reasonable.
341: */
1.1 cgd 342: char *
1.24 joerg 343: fetchname(const char *at, bool *exists, int strip_leading)
1.1 cgd 344: {
1.24 joerg 345: char *fullname, *name, *t;
346: int sleading, tab;
347: struct stat filestat;
1.11 kristerw 348:
1.24 joerg 349: if (at == NULL || *at == '\0')
1.11 kristerw 350: return NULL;
351: while (isspace((unsigned char)*at))
352: at++;
1.1 cgd 353: #ifdef DEBUGGING
1.11 kristerw 354: if (debug & 128)
1.24 joerg 355: say("fetchname %s %d\n", at, strip_leading);
1.1 cgd 356: #endif
1.24 joerg 357: /* So files can be created by diffing against /dev/null. */
358: if (strnEQ(at, _PATH_DEVNULL, sizeof(_PATH_DEVNULL) - 1))
1.11 kristerw 359: return NULL;
1.24 joerg 360: name = fullname = t = savestr(at);
1.11 kristerw 361:
1.24 joerg 362: tab = strchr(t, '\t') != NULL;
363: /* Strip off up to `strip_leading' path components and NUL terminate. */
364: for (sleading = strip_leading; *t != '\0' && ((tab && *t != '\t') ||
365: !isspace((unsigned char)*t)); t++) {
366: if (t[0] == '/' && t[1] != '/' && t[1] != '\0')
1.11 kristerw 367: if (--sleading >= 0)
368: name = t + 1;
1.24 joerg 369: }
1.11 kristerw 370: *t = '\0';
371:
372: /*
1.24 joerg 373: * If no -p option was given (957 is the default value!), we were
374: * given a relative pathname, and the leading directories that we
375: * just stripped off all exist, put them back on.
1.11 kristerw 376: */
377: if (strip_leading == 957 && name != fullname && *fullname != '/') {
378: name[-1] = '\0';
1.24 joerg 379: if (stat(fullname, &filestat) == 0 && S_ISDIR(filestat.st_mode)) {
1.11 kristerw 380: name[-1] = '/';
381: name = fullname;
382: }
383: }
1.24 joerg 384: name = savestr(name);
385: free(fullname);
386:
387: *exists = stat(name, &filestat) == 0;
388: return name;
389: }
390:
391: /*
392: * Takes the name returned by fetchname and looks in RCS/SCCS directories
393: * for a checked in version.
394: */
395: char *
396: checked_in(char *file)
397: {
398: char *filebase, *filedir, tmpbuf[MAXPATHLEN];
399: struct stat filestat;
1.11 kristerw 400:
1.24 joerg 401: filebase = basename(file);
402: filedir = dirname(file);
403:
404: #define try(f, a1, a2, a3) \
405: (snprintf(tmpbuf, sizeof tmpbuf, f, a1, a2, a3), stat(tmpbuf, &filestat) == 0)
406:
407: if (try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX) ||
408: try("%s/RCS/%s%s", filedir, filebase, "") ||
409: try("%s/%s%s", filedir, filebase, RCSSUFFIX) ||
410: try("%s/SCCS/%s%s", filedir, SCCSPREFIX, filebase) ||
411: try("%s/%s%s", filedir, SCCSPREFIX, filebase))
412: return file;
413:
414: return NULL;
415: }
1.11 kristerw 416:
1.24 joerg 417: void
418: version(void)
419: {
1.26 wiz 420: printf("Patch version 2.0-12u8-NetBSD\n");
1.24 joerg 421: my_exit(EXIT_SUCCESS);
422: }
1.1 cgd 423:
1.24 joerg 424: /*
425: * Exit with cleanup.
426: */
427: void
428: my_exit(int status)
429: {
430: unlink(TMPINNAME);
431: if (!toutkeep)
432: unlink(TMPOUTNAME);
433: if (!trejkeep)
434: unlink(TMPREJNAME);
435: unlink(TMPPATNAME);
436: exit(status);
1.1 cgd 437: }
CVSweb <webmaster@jp.NetBSD.org>