Annotation of src/usr.bin/patch/util.c, Revision 1.21
1.21 ! skd 1: /* $NetBSD: util.c,v 1.20 2004/10/30 21:52:09 dsl Exp $ */
1.16 itojun 2:
3: /*
4: * Copyright (c) 1988, Larry Wall
5: *
6: * Redistribution and use in source and binary forms, with or without
7: * modification, are permitted provided that the following condition
8: * is met:
9: * 1. Redistributions of source code must retain the above copyright
10: * notice, this condition and the following disclaimer.
11: *
12: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
13: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
14: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
15: * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
16: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
18: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
19: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
20: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
21: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
22: * SUCH DAMAGE.
23: */
24:
1.5 christos 25: #include <sys/cdefs.h>
1.2 mycroft 26: #ifndef lint
1.21 ! skd 27: __RCSID("$NetBSD: util.c,v 1.20 2004/10/30 21:52:09 dsl Exp $");
1.2 mycroft 28: #endif /* not lint */
29:
1.12 kristerw 30: #include <sys/param.h>
31:
1.1 cgd 32: #include "EXTERN.h"
33: #include "common.h"
34: #include "INTERN.h"
35: #include "util.h"
36: #include "backupfile.h"
1.10 kristerw 37:
1.5 christos 38: #include <stdarg.h>
39: #include <stdlib.h>
40: #include <unistd.h>
41: #include <fcntl.h>
1.1 cgd 42:
1.11 kristerw 43: /*
44: * Rename a file, copying it if necessary.
45: */
1.1 cgd 46: int
1.9 kristerw 47: move_file(char *from, char *to)
1.1 cgd 48: {
1.12 kristerw 49: char bakname[MAXPATHLEN];
1.11 kristerw 50: char *s;
1.14 kristerw 51: size_t i;
1.11 kristerw 52: int fromfd;
1.1 cgd 53:
1.21 ! skd 54: if ( check_only == TRUE )
! 55: return(0);
! 56:
1.11 kristerw 57: /* to stdout? */
1.1 cgd 58:
1.11 kristerw 59: if (strEQ(to, "-")) {
1.1 cgd 60: #ifdef DEBUGGING
1.11 kristerw 61: if (debug & 4)
62: say("Moving %s to stdout.\n", from);
1.1 cgd 63: #endif
1.11 kristerw 64: fromfd = open(from, 0);
65: if (fromfd < 0)
66: pfatal("internal error, can't reopen %s", from);
67: while ((i = read(fromfd, buf, sizeof buf)) > 0)
68: if (write(1, buf, i) != 1)
69: pfatal("write failed");
70: Close(fromfd);
71: return 0;
72: }
1.1 cgd 73:
1.11 kristerw 74: if (origprae) {
1.18 itojun 75: strlcpy(bakname, origprae, sizeof(bakname));
76: strlcat(bakname, to, sizeof(bakname));
1.11 kristerw 77: } else {
1.1 cgd 78: #ifndef NODIR
1.11 kristerw 79: char *backupname = find_backup_file_name(to);
1.18 itojun 80: strlcpy(bakname, backupname, sizeof(bakname));
1.11 kristerw 81: free(backupname);
1.1 cgd 82: #else /* NODIR */
1.18 itojun 83: strlcpy(bakname, to, sizeof(bakname));
84: strlcat(bakname, simple_backup_suffix, sizeof(bakname));
1.1 cgd 85: #endif /* NODIR */
1.11 kristerw 86: }
1.1 cgd 87:
1.11 kristerw 88: if (stat(to, &filestat) == 0) { /* output file exists */
89: dev_t to_device = filestat.st_dev;
90: ino_t to_inode = filestat.st_ino;
91: char *simplename = bakname;
1.1 cgd 92:
1.11 kristerw 93: for (s = bakname; *s; s++) {
94: if (*s == '/')
95: simplename = s + 1;
96: }
97: /*
98: * Find a backup name that is not the same file.
99: * Change the first lowercase char into uppercase;
100: * if that isn't sufficient, chop off the first char
101: * and try again.
102: */
103: while (stat(bakname, &filestat) == 0 &&
104: to_device == filestat.st_dev &&
105: to_inode == filestat.st_ino) {
106: /* Skip initial non-lowercase chars. */
107: for (s = simplename;
1.20 dsl 108: *s && *s == toupper((unsigned char)*s);
1.11 kristerw 109: s++)
110: ;
111: if (*s)
1.20 dsl 112: *s = toupper((unsigned char)*s);
1.11 kristerw 113: else
1.19 itojun 114: strcpy(simplename, simplename + 1);
1.11 kristerw 115: }
116: while (unlink(bakname) >= 0)
117: ; /* while() is for benefit of Eunice */
118: #ifdef DEBUGGING
119: if (debug & 4)
120: say("Moving %s to %s.\n", to, bakname);
121: #endif
122: if (link(to, bakname) < 0) {
123: /*
124: * Maybe `to' is a symlink into a different file
125: * system. Copying replaces the symlink with a file;
126: * using rename would be better.
127: */
128: int tofd;
129: int bakfd;
130:
131: bakfd = creat(bakname, 0666);
132: if (bakfd < 0) {
133: say("Can't backup %s, output is in %s: %s\n",
134: to, from, strerror(errno));
135: return -1;
136: }
137: tofd = open(to, 0);
138: if (tofd < 0)
139: pfatal("internal error, can't open %s", to);
140: while ((i = read(tofd, buf, sizeof buf)) > 0)
141: if (write(bakfd, buf, i) != i)
142: pfatal("write failed");
143: Close(tofd);
144: Close(bakfd);
145: }
146: while (unlink(to) >= 0) ;
1.1 cgd 147: }
148: #ifdef DEBUGGING
149: if (debug & 4)
1.11 kristerw 150: say("Moving %s to %s.\n", from, to);
1.1 cgd 151: #endif
1.11 kristerw 152: if (link(from, to) < 0) { /* different file system? */
153: int tofd;
154:
155: tofd = creat(to, 0666);
156: if (tofd < 0) {
157: say("Can't create %s, output is in %s: %s\n",
158: to, from, strerror(errno));
159: return -1;
160: }
161: fromfd = open(from, 0);
162: if (fromfd < 0)
163: pfatal("internal error, can't reopen %s", from);
164: while ((i = read(fromfd, buf, sizeof buf)) > 0)
165: if (write(tofd, buf, i) != i)
166: pfatal("write failed");
167: Close(fromfd);
168: Close(tofd);
1.1 cgd 169: }
1.11 kristerw 170: Unlink(from);
171: return 0;
172: }
173:
174: /*
175: * Copy a file.
176: */
177: void
178: copy_file(char *from, char *to)
179: {
1.9 kristerw 180: int tofd;
1.11 kristerw 181: int fromfd;
1.14 kristerw 182: size_t i;
1.11 kristerw 183:
1.1 cgd 184: tofd = creat(to, 0666);
1.11 kristerw 185: if (tofd < 0)
186: pfatal("can't create %s", to);
1.1 cgd 187: fromfd = open(from, 0);
188: if (fromfd < 0)
1.11 kristerw 189: pfatal("internal error, can't reopen %s", from);
190: while ((i = read(fromfd, buf, sizeof buf)) > 0)
191: if (write(tofd, buf, i) != i)
192: pfatal("write to %s failed", to);
1.1 cgd 193: Close(fromfd);
194: Close(tofd);
195: }
196:
1.11 kristerw 197: /*
1.12 kristerw 198: * malloc with result test.
199: */
200: void *
201: xmalloc(size_t size)
202: {
203: void *p;
204:
205: if ((p = malloc(size)) == NULL)
206: fatal("out of memory\n");
207: return p;
208: }
209:
210: /*
1.15 kristerw 211: * realloc with result test.
1.12 kristerw 212: */
1.15 kristerw 213: void *
214: xrealloc(void *ptr, size_t size)
1.12 kristerw 215: {
1.15 kristerw 216: void *p;
1.12 kristerw 217:
1.15 kristerw 218: if ((p = realloc(ptr, size)) == NULL)
1.12 kristerw 219: fatal("out of memory\n");
220: return p;
221: }
222:
223: /*
1.15 kristerw 224: * strdup with result test.
1.11 kristerw 225: */
1.1 cgd 226: char *
1.15 kristerw 227: xstrdup(const char *s)
1.1 cgd 228: {
1.15 kristerw 229: char *p;
1.1 cgd 230:
1.15 kristerw 231: if ((p = strdup(s)) == NULL)
232: fatal("out of memory\n");
233: return p;
1.1 cgd 234: }
235:
1.11 kristerw 236: /*
1.12 kristerw 237: * Vanilla terminal output.
1.11 kristerw 238: */
1.1 cgd 239: void
1.5 christos 240: say(const char *pat, ...)
1.1 cgd 241: {
1.11 kristerw 242: va_list ap;
243: va_start(ap, pat);
1.5 christos 244:
1.11 kristerw 245: vfprintf(stderr, pat, ap);
246: va_end(ap);
247: Fflush(stderr);
1.1 cgd 248: }
249:
1.11 kristerw 250: /*
251: * Terminal output, pun intended.
252: */
1.1 cgd 253: void /* very void */
1.5 christos 254: fatal(const char *pat, ...)
1.1 cgd 255: {
1.11 kristerw 256: va_list ap;
257: va_start(ap, pat);
1.5 christos 258:
1.11 kristerw 259: fprintf(stderr, "patch: **** ");
260: vfprintf(stderr, pat, ap);
261: va_end(ap);
262: my_exit(1);
1.1 cgd 263: }
264:
1.11 kristerw 265: /*
266: * Say something from patch, something from the system, then silence...
267: */
1.1 cgd 268: void /* very void */
1.5 christos 269: pfatal(const char *pat, ...)
1.1 cgd 270: {
1.11 kristerw 271: va_list ap;
272: int errnum = errno;
273: va_start(ap, pat);
1.5 christos 274:
1.11 kristerw 275: fprintf(stderr, "patch: **** ");
276: vfprintf(stderr, pat, ap);
277: fprintf(stderr, ": %s\n", strerror(errnum));
278: va_end(ap);
279: my_exit(1);
1.1 cgd 280: }
281:
1.11 kristerw 282: /*
283: * Get a response from the user, somehow or other.
284: */
1.1 cgd 285: void
1.5 christos 286: ask(const char *pat, ...)
1.1 cgd 287: {
1.11 kristerw 288: int ttyfd;
289: int r;
290: bool tty2 = isatty(2);
291: va_list ap;
292: va_start(ap, pat);
293:
294: (void)vsprintf(buf, pat, ap);
295: va_end(ap);
296: Fflush(stderr);
297: write(2, buf, strlen(buf));
298: if (tty2) { /* might be redirected to a file */
299: r = read(2, buf, sizeof buf);
300: } else if (isatty(1)) { /* this may be new file output */
301: Fflush(stdout);
302: write(1, buf, strlen(buf));
303: r = read(1, buf, sizeof buf);
304: } else if ((ttyfd = open("/dev/tty", 2)) >= 0 && isatty(ttyfd)) {
1.13 wiz 305: /* might be deleted or unwritable */
1.11 kristerw 306: write(ttyfd, buf, strlen(buf));
307: r = read(ttyfd, buf, sizeof buf);
308: Close(ttyfd);
309: } else if (isatty(0)) { /* this is probably patch input */
310: Fflush(stdin);
311: write(0, buf, strlen(buf));
312: r = read(0, buf, sizeof buf);
313: } else { /* no terminal at all--default it */
314: buf[0] = '\n';
315: r = 1;
316: }
317: if (r <= 0)
318: buf[0] = 0;
319: else
320: buf[r] = '\0';
321: if (!tty2)
322: say("%s", buf);
1.1 cgd 323: }
324:
1.11 kristerw 325: /*
326: * How to handle certain events when not in a critical region.
327: */
1.1 cgd 328: void
1.9 kristerw 329: set_signals(int reset)
1.1 cgd 330: {
1.11 kristerw 331: static void (*hupval)(int);
332: static void (*intval)(int);
1.1 cgd 333:
1.11 kristerw 334: if (!reset) {
335: hupval = signal(SIGHUP, SIG_IGN);
336: if (hupval != SIG_IGN)
337: hupval = my_exit;
338: intval = signal(SIGINT, SIG_IGN);
339: if (intval != SIG_IGN)
340: intval = my_exit;
341: }
342: Signal(SIGHUP, hupval);
343: Signal(SIGINT, intval);
1.1 cgd 344: }
345:
1.11 kristerw 346: /*
347: * How to handle certain events when in a critical region.
348: */
1.1 cgd 349: void
350: ignore_signals()
351: {
1.11 kristerw 352: Signal(SIGHUP, SIG_IGN);
353: Signal(SIGINT, SIG_IGN);
1.1 cgd 354: }
355:
1.11 kristerw 356: /*
357: * Make sure we'll have the directories to create a file.
358: * If `striplast' is TRUE, ignore the last element of `filename'.
359: */
1.1 cgd 360: void
1.9 kristerw 361: makedirs(char *filename, bool striplast)
1.1 cgd 362: {
1.12 kristerw 363: char tmpbuf[MAXPATHLEN];
1.11 kristerw 364: char *s = tmpbuf;
1.12 kristerw 365: char *dirv[MAXPATHLEN]; /* Point to the NULs between elements. */
1.11 kristerw 366: int i;
367: int dirvp = 0; /* Number of finished entries in dirv. */
368:
369: /*
370: * Copy `filename' into `tmpbuf' with a NUL instead of a slash
371: * between the directories.
372: */
373: while (*filename) {
374: if (*filename == '/') {
375: filename++;
376: dirv[dirvp++] = s;
377: *s++ = '\0';
378: } else {
379: *s++ = *filename++;
380: }
381: }
382: *s = '\0';
383: dirv[dirvp] = s;
384: if (striplast)
385: dirvp--;
386: if (dirvp < 0)
387: return;
388:
1.18 itojun 389: strlcpy(buf, "mkdir", sizeof(buf));
1.11 kristerw 390: s = buf;
391: for (i = 0; i <= dirvp; i++) {
392: struct stat sbuf;
393:
394: if (stat(tmpbuf, &sbuf) && errno == ENOENT) {
395: while (*s)
396: s++;
397: *s++ = ' ';
1.18 itojun 398: strlcpy(s, tmpbuf, sizeof(buf) - (s - buf));
1.11 kristerw 399: }
400: *dirv[i] = '/';
401: }
402: if (s != buf)
403: system(buf);
1.1 cgd 404: }
405:
1.11 kristerw 406: /*
407: * Make filenames more reasonable.
408: */
1.1 cgd 409: char *
1.9 kristerw 410: fetchname(char *at, int strip_leading, int assume_exists)
1.1 cgd 411: {
1.11 kristerw 412: char *fullname;
413: char *name;
414: char *t;
1.12 kristerw 415: char tmpbuf[MAXPATHLEN];
1.11 kristerw 416: int sleading = strip_leading;
417:
418: if (!at)
419: return NULL;
420: while (isspace((unsigned char)*at))
421: at++;
1.1 cgd 422: #ifdef DEBUGGING
1.11 kristerw 423: if (debug & 128)
424: say("fetchname %s %d %d\n", at, strip_leading, assume_exists);
1.1 cgd 425: #endif
1.11 kristerw 426: filename_is_dev_null = FALSE;
427: if (strnEQ(at, "/dev/null", 9)) {
428: /* So files can be created by diffing against /dev/null. */
429: filename_is_dev_null = TRUE;
430: return NULL;
431: }
1.12 kristerw 432: name = fullname = t = xstrdup(at);
1.11 kristerw 433:
434: /* Strip off up to `sleading' leading slashes and null terminate. */
435: for (; *t && !isspace((unsigned char)*t); t++)
436: if (*t == '/')
437: if (--sleading >= 0)
438: name = t + 1;
439: *t = '\0';
440:
441: /*
442: * If no -p option was given (957 is the default value!),
443: * we were given a relative pathname,
444: * and the leading directories that we just stripped off all exist,
445: * put them back on.
446: */
447: if (strip_leading == 957 && name != fullname && *fullname != '/') {
448: name[-1] = '\0';
449: if (stat(fullname, &filestat) == 0 &&
450: S_ISDIR(filestat.st_mode)) {
451: name[-1] = '/';
452: name = fullname;
453: }
454: }
455:
1.12 kristerw 456: name = xstrdup(name);
1.11 kristerw 457: free(fullname);
458:
459: if (stat(name, &filestat) && !assume_exists) {
460: char *filebase = basename(name);
1.14 kristerw 461: size_t pathlen = filebase - name;
1.11 kristerw 462:
463: /* Put any leading path into `tmpbuf'. */
1.17 itojun 464: if (pathlen >= sizeof(tmpbuf))
465: return NULL;
1.11 kristerw 466: strncpy(tmpbuf, name, pathlen);
1.17 itojun 467: tmpbuf[pathlen] = '\0';
1.11 kristerw 468:
469: #define try(f, a1, a2) \
1.18 itojun 470: (snprintf(tmpbuf + pathlen, sizeof(tmpbuf) - pathlen, f, a1, a2), \
471: stat(tmpbuf, &filestat) == 0)
1.11 kristerw 472: #define try1(f, a1) \
1.18 itojun 473: (snprintf(tmpbuf + pathlen, sizeof(tmpbuf) - pathlen, f, a1), \
474: stat(tmpbuf, &filestat) == 0)
1.11 kristerw 475: if (try("RCS/%s%s", filebase, RCSSUFFIX) ||
476: try1("RCS/%s" , filebase) ||
477: try( "%s%s", filebase, RCSSUFFIX) ||
478: try("SCCS/%s%s", SCCSPREFIX, filebase) ||
479: try( "%s%s", SCCSPREFIX, filebase))
480: return name;
481: free(name);
482: name = NULL;
483: }
1.1 cgd 484:
1.11 kristerw 485: return name;
1.1 cgd 486: }
CVSweb <webmaster@jp.NetBSD.org>