xref: /netbsd-src/usr.bin/patch/util.c (revision aaf4ece63a859a04e37cf3a7229b5fab0157cc06)
1 /*	$NetBSD: util.c,v 1.21 2005/03/25 23:00:55 skd Exp $	*/
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 
25 #include <sys/cdefs.h>
26 #ifndef lint
27 __RCSID("$NetBSD: util.c,v 1.21 2005/03/25 23:00:55 skd Exp $");
28 #endif /* not lint */
29 
30 #include <sys/param.h>
31 
32 #include "EXTERN.h"
33 #include "common.h"
34 #include "INTERN.h"
35 #include "util.h"
36 #include "backupfile.h"
37 
38 #include <stdarg.h>
39 #include <stdlib.h>
40 #include <unistd.h>
41 #include <fcntl.h>
42 
43 /*
44  * Rename a file, copying it if necessary.
45  */
46 int
47 move_file(char *from, char *to)
48 {
49 	char bakname[MAXPATHLEN];
50 	char *s;
51 	size_t i;
52 	int fromfd;
53 
54 	if ( check_only == TRUE )
55 		return(0);
56 
57 	/* to stdout? */
58 
59 	if (strEQ(to, "-")) {
60 #ifdef DEBUGGING
61 		if (debug & 4)
62 			say("Moving %s to stdout.\n", from);
63 #endif
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 	}
73 
74 	if (origprae) {
75 		strlcpy(bakname, origprae, sizeof(bakname));
76 		strlcat(bakname, to, sizeof(bakname));
77 	} else {
78 #ifndef NODIR
79 		char *backupname = find_backup_file_name(to);
80 		strlcpy(bakname, backupname, sizeof(bakname));
81 		free(backupname);
82 #else /* NODIR */
83 		strlcpy(bakname, to, sizeof(bakname));
84     		strlcat(bakname, simple_backup_suffix, sizeof(bakname));
85 #endif /* NODIR */
86 	}
87 
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;
92 
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;
108 			     *s && *s == toupper((unsigned char)*s);
109 			     s++)
110 				;
111 			if (*s)
112 				*s = toupper((unsigned char)*s);
113 			else
114 				strcpy(simplename, simplename + 1);
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) ;
147 	}
148 #ifdef DEBUGGING
149 	if (debug & 4)
150 		say("Moving %s to %s.\n", from, to);
151 #endif
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);
169 	}
170 	Unlink(from);
171 	return 0;
172 }
173 
174 /*
175  * Copy a file.
176  */
177 void
178 copy_file(char *from, char *to)
179 {
180 	int tofd;
181 	int fromfd;
182 	size_t i;
183 
184 	tofd = creat(to, 0666);
185 	if (tofd < 0)
186 		pfatal("can't create %s", to);
187 	fromfd = open(from, 0);
188 	if (fromfd < 0)
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);
193 	Close(fromfd);
194 	Close(tofd);
195 }
196 
197 /*
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 /*
211  * realloc with result test.
212  */
213 void *
214 xrealloc(void *ptr, size_t size)
215 {
216 	void *p;
217 
218 	if ((p = realloc(ptr, size)) == NULL)
219 		fatal("out of memory\n");
220 	return p;
221 }
222 
223 /*
224  * strdup with result test.
225  */
226 char *
227 xstrdup(const char *s)
228 {
229 	char *p;
230 
231 	if ((p = strdup(s)) == NULL)
232 		fatal("out of memory\n");
233 	return p;
234 }
235 
236 /*
237  * Vanilla terminal output.
238  */
239 void
240 say(const char *pat, ...)
241 {
242 	va_list ap;
243 	va_start(ap, pat);
244 
245 	vfprintf(stderr, pat, ap);
246 	va_end(ap);
247 	Fflush(stderr);
248 }
249 
250 /*
251  * Terminal output, pun intended.
252  */
253 void				/* very void */
254 fatal(const char *pat, ...)
255 {
256 	va_list ap;
257 	va_start(ap, pat);
258 
259 	fprintf(stderr, "patch: **** ");
260 	vfprintf(stderr, pat, ap);
261 	va_end(ap);
262 	my_exit(1);
263 }
264 
265 /*
266  * Say something from patch, something from the system, then silence...
267  */
268 void				/* very void */
269 pfatal(const char *pat, ...)
270 {
271 	va_list ap;
272 	int errnum = errno;
273 	va_start(ap, pat);
274 
275 	fprintf(stderr, "patch: **** ");
276 	vfprintf(stderr, pat, ap);
277 	fprintf(stderr, ": %s\n", strerror(errnum));
278 	va_end(ap);
279 	my_exit(1);
280 }
281 
282 /*
283  * Get a response from the user, somehow or other.
284  */
285 void
286 ask(const char *pat, ...)
287 {
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)) {
305 					/* might be deleted or unwritable */
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);
323 }
324 
325 /*
326  * How to handle certain events when not in a critical region.
327  */
328 void
329 set_signals(int reset)
330 {
331 	static void (*hupval)(int);
332 	static void (*intval)(int);
333 
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);
344 }
345 
346 /*
347  * How to handle certain events when in a critical region.
348  */
349 void
350 ignore_signals()
351 {
352 	Signal(SIGHUP, SIG_IGN);
353 	Signal(SIGINT, SIG_IGN);
354 }
355 
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  */
360 void
361 makedirs(char *filename, bool striplast)
362 {
363 	char tmpbuf[MAXPATHLEN];
364 	char *s = tmpbuf;
365 	char *dirv[MAXPATHLEN];	/* Point to the NULs between elements.  */
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 
389 	strlcpy(buf, "mkdir", sizeof(buf));
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++ = ' ';
398 			strlcpy(s, tmpbuf, sizeof(buf) - (s - buf));
399 		}
400 		*dirv[i] = '/';
401 	}
402 	if (s != buf)
403 		system(buf);
404 }
405 
406 /*
407  * Make filenames more reasonable.
408  */
409 char *
410 fetchname(char *at, int strip_leading, int assume_exists)
411 {
412 	char *fullname;
413 	char *name;
414 	char *t;
415 	char tmpbuf[MAXPATHLEN];
416 	int sleading = strip_leading;
417 
418 	if (!at)
419 		return NULL;
420 	while (isspace((unsigned char)*at))
421 		at++;
422 #ifdef DEBUGGING
423 	if (debug & 128)
424 		say("fetchname %s %d %d\n", at, strip_leading, assume_exists);
425 #endif
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 	}
432 	name = fullname = t = xstrdup(at);
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 
456 	name = xstrdup(name);
457 	free(fullname);
458 
459 	if (stat(name, &filestat) && !assume_exists) {
460 		char *filebase = basename(name);
461 		size_t pathlen = filebase - name;
462 
463 		/* Put any leading path into `tmpbuf'. */
464 		if (pathlen >= sizeof(tmpbuf))
465 			return NULL;
466 		strncpy(tmpbuf, name, pathlen);
467 		tmpbuf[pathlen] = '\0';
468 
469 #define try(f, a1, a2) \
470     (snprintf(tmpbuf + pathlen, sizeof(tmpbuf) - pathlen, f, a1, a2), \
471      stat(tmpbuf, &filestat) == 0)
472 #define try1(f, a1) \
473     (snprintf(tmpbuf + pathlen, sizeof(tmpbuf) - pathlen, f, a1), \
474      stat(tmpbuf, &filestat) == 0)
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 	}
484 
485 	return name;
486 }
487