1/* $FreeBSD$ */
2
3#include <paths.h>
4
5#include "EXTERN.h"
6#include "common.h"
7#include "INTERN.h"
8#include "util.h"
9#include "backupfile.h"
10
11void	my_exit(int _status);		/* in patch.c */
12
13#ifndef HAVE_STRERROR
14static char *
15private_strerror (errnum)
16     int errnum;
17{
18  extern char *sys_errlist[];
19  extern int sys_nerr;
20
21  if (errnum > 0 && errnum <= sys_nerr)
22    return sys_errlist[errnum];
23  return "Unknown system error";
24}
25#define strerror private_strerror
26#endif /* !HAVE_STRERROR */
27
28/*
29 * Rename a file, copying it if necessary.
30 */
31int
32move_file(char *from, char *to)
33{
34	char bakname[512];
35	Reg1 char *s;
36	Reg2 int i;
37	Reg3 int fromfd;
38
39	/* to stdout? */
40
41	if (strEQ(to, "-")) {
42#ifdef DEBUGGING
43		if (debug & 4)
44			say2("Moving %s to stdout.\n", from);
45#endif
46		fromfd = open(from, 0);
47		if (fromfd < 0)
48			pfatal2("internal error, can't reopen %s", from);
49		while ((i = read(fromfd, buf, buf_size)) > 0)
50			if (write(1, buf, i) != 1)
51				pfatal1("write failed");
52		Close(fromfd);
53		return 0;
54	}
55
56	if (origprae) {
57		Strcpy(bakname, origprae);
58		Strcat(bakname, to);
59	} else {
60#ifndef NODIR
61		char *backupname = find_backup_file_name(to);
62		if (backupname == (char *) 0)
63		    fatal1("out of memory\n");
64		Strcpy(bakname, backupname);
65		free(backupname);
66#else /* NODIR */
67		Strcpy(bakname, to);
68		Strcat(bakname, simple_backup_suffix);
69#endif /* NODIR */
70	}
71
72	if (stat(to, &filestat) == 0) {	/* output file exists */
73		dev_t to_device = filestat.st_dev;
74		ino_t to_inode  = filestat.st_ino;
75		char *simplename = bakname;
76
77		for (s = bakname; *s; s++) {
78			if (*s == '/')
79				simplename = s + 1;
80		}
81		/*
82		 * Find a backup name that is not the same file.
83		 * Change the first lowercase char into uppercase;
84		 * if that isn't sufficient, chop off the first char
85		 * and try again.
86		 */
87		while (stat(bakname, &filestat) == 0 &&
88		    to_device == filestat.st_dev &&
89		    to_inode == filestat.st_ino) {
90			/* Skip initial non-lowercase chars.  */
91			for (s=simplename; *s && !islower((unsigned char)*s);
92			    s++)
93				;
94			if (*s)
95				*s = toupper((unsigned char)*s);
96			else
97				Strcpy(simplename, simplename + 1);
98		}
99		while (unlink(bakname) >= 0)
100			;	/* while() is for benefit of Eunice */
101#ifdef DEBUGGING
102		if (debug & 4)
103			say3("Moving %s to %s.\n", to, bakname);
104#endif
105		if (rename(to, bakname) < 0) {
106			say4("Can't backup %s, output is in %s: %s\n", to, from,
107			    strerror(errno));
108			return -1;
109		}
110		while (unlink(to) >= 0)
111			;
112	}
113#ifdef DEBUGGING
114	if (debug & 4)
115		say3("Moving %s to %s.\n", from, to);
116#endif
117	if (rename(from, to) < 0) {	/* different file system? */
118		Reg4 int tofd;
119
120		tofd = creat(to, 0666);
121		if (tofd < 0) {
122			say4("Can't create %s, output is in %s: %s\n",
123			    to, from, strerror(errno));
124			return -1;
125		}
126		fromfd = open(from, 0);
127		if (fromfd < 0)
128			pfatal2("internal error, can't reopen %s", from);
129		while ((i = read(fromfd, buf, buf_size)) > 0)
130			if (write(tofd, buf, i) != i)
131				pfatal1("write failed");
132		Close(fromfd);
133		Close(tofd);
134	}
135	Unlink(from);
136	return 0;
137}
138
139/*
140 * Copy a file.
141 */
142void
143copy_file(char *from, char *to)
144{
145	Reg3 int tofd;
146	Reg2 int fromfd;
147	Reg1 int i;
148
149	tofd = creat(to, 0666);
150	if (tofd < 0)
151		pfatal2("can't create %s", to);
152	fromfd = open(from, 0);
153	if (fromfd < 0)
154		pfatal2("internal error, can't reopen %s", from);
155	while ((i = read(fromfd, buf, buf_size)) > 0)
156		if (write(tofd, buf, i) != i)
157			pfatal2("write to %s failed", to);
158	Close(fromfd);
159	Close(tofd);
160}
161
162/*
163 * Allocate a unique area for a string.
164 */
165char *
166savestr(char *s)
167{
168	Reg3 char *rv;
169	Reg2 char *t;
170
171	if (!s)
172		s = "Oops";
173	t = s;
174	while (*t++)
175		;
176	rv = malloc((MEM) (t - s));
177	if (rv == Nullch) {
178		if (using_plan_a)
179			out_of_mem = TRUE;
180		else
181			fatal1("out of memory\n");
182	} else {
183		t = rv;
184		while ((*t++ = *s++));
185	}
186	return rv;
187}
188
189#if defined(lint) && defined(CANVARARG)
190
191/*VARARGS ARGSUSED*/
192say(pat) char *pat; { ; }
193/*VARARGS ARGSUSED*/
194fatal(pat) char *pat; { ; }
195/*VARARGS ARGSUSED*/
196pfatal(pat) char *pat; { ; }
197/*VARARGS ARGSUSED*/
198ask(pat) char *pat; { ; }
199
200#else
201
202/*
203 * Vanilla terminal output (buffered).
204 */
205void
206say(pat,arg1,arg2,arg3)
207char *pat;
208long arg1,arg2,arg3;
209{
210	fprintf(stderr, pat, arg1, arg2, arg3);
211	Fflush(stderr);
212}
213
214/*
215 * Terminal output, pun intended.
216 */
217void				/* very void */
218fatal(pat,arg1,arg2,arg3)
219char *pat;
220long arg1,arg2,arg3;
221{
222	fprintf(stderr, "patch: **** ");
223	fprintf(stderr, pat, arg1, arg2, arg3);
224	my_exit(1);
225}
226
227/*
228 * Say something from patch, something from the system, then silence...
229 */
230void				/* very void */
231pfatal(pat,arg1,arg2,arg3)
232char *pat;
233long arg1,arg2,arg3;
234{
235	int errnum = errno;
236
237	fprintf(stderr, "patch: **** ");
238	fprintf(stderr, pat, arg1, arg2, arg3);
239	fprintf(stderr, ": %s\n", strerror(errnum));
240	my_exit(1);
241}
242
243/*
244 * Get a response from the user, somehow or other.
245 */
246int
247ask(pat,arg1,arg2,arg3)
248char *pat;
249long arg1,arg2,arg3;
250{
251	int ttyfd;
252	int r;
253	bool tty2 = isatty(2);
254
255	Sprintf(buf, pat, arg1, arg2, arg3);
256	Fflush(stderr);
257	write(2, buf, strlen(buf));
258	if (tty2) {			/* might be redirected to a file */
259		r = read(2, buf, buf_size);
260	} else if (isatty(1)) {		/* this may be new file output */
261		Fflush(stdout);
262		write(1, buf, strlen(buf));
263		r = read(1, buf, buf_size);
264	} else if ((ttyfd = open(_PATH_TTY, 2)) >= 0 && isatty(ttyfd)) {
265					/* might be deleted or unwriteable */
266		write(ttyfd, buf, strlen(buf));
267		r = read(ttyfd, buf, buf_size);
268		Close(ttyfd);
269	} else if (isatty(0)) {		/* this is probably patch input */
270		Fflush(stdin);
271		write(0, buf, strlen(buf));
272		r = read(0, buf, buf_size);
273	} else {			/* no terminal at all--default it */
274		buf[0] = '\n';
275		buf[1] = 0;
276		say1(buf);
277		return 0;		/* signal possible error */
278	}
279	if (r <= 0)
280		buf[0] = 0;
281	else
282		buf[r] = '\0';
283	if (!tty2)
284		say1(buf);
285
286	if (r <= 0)
287		return 0;		/* if there was an error, return it */
288	else
289		return 1;
290}
291#endif /* lint */
292
293/*
294 * How to handle certain events when not in a critical region.
295 */
296void
297set_signals(int reset)
298{
299#ifndef lint
300	static RETSIGTYPE (*hupval)(),(*intval)();
301
302	if (!reset) {
303		hupval = signal(SIGHUP, SIG_IGN);
304		if (hupval != SIG_IGN)
305			hupval = (RETSIGTYPE(*)())my_exit;
306		intval = signal(SIGINT, SIG_IGN);
307		if (intval != SIG_IGN)
308			intval = (RETSIGTYPE(*)())my_exit;
309	}
310	Signal(SIGHUP, hupval);
311	Signal(SIGINT, intval);
312#endif
313}
314
315/*
316 * How to handle certain events when in a critical region.
317 */
318void
319ignore_signals(void)
320{
321#ifndef lint
322	Signal(SIGHUP, SIG_IGN);
323	Signal(SIGINT, SIG_IGN);
324#endif
325}
326
327/*
328 * Make sure we'll have the directories to create a file.
329 * If `striplast' is TRUE, ignore the last element of `filename'.
330 */
331void
332makedirs(filename,striplast)
333Reg1 char *filename;
334bool striplast;
335{
336	char tmpbuf[256];
337	Reg2 char *s = tmpbuf;
338	char *dirv[20];		/* Point to the NULs between elements.  */
339	Reg3 int i;
340	Reg4 int dirvp = 0;	/* Number of finished entries in dirv. */
341
342	/*
343	 * Copy `filename' into `tmpbuf' with a NUL instead of a slash
344	 * between the directories.
345	 */
346	while (*filename) {
347		if (*filename == '/') {
348			filename++;
349			dirv[dirvp++] = s;
350			*s++ = '\0';
351		} else {
352			*s++ = *filename++;
353		}
354	}
355	*s = '\0';
356	dirv[dirvp] = s;
357	if (striplast)
358		dirvp--;
359	if (dirvp < 0)
360		return;
361
362	strcpy(buf, "mkdir");
363	s = buf;
364	for (i = 0; i <= dirvp; i++) {
365		struct stat sbuf;
366
367		if (stat(tmpbuf, &sbuf) && errno == ENOENT) {
368			while (*s)
369				s++;
370			*s++ = ' ';
371			strcpy(s, tmpbuf);
372		}
373		*dirv[i] = '/';
374	}
375	if (s != buf)
376		system(buf);
377}
378
379/*
380 * Make filenames more reasonable.
381 */
382char *
383fetchname(char *at, int strip_leading, int assume_exists)
384{
385	char *fullname;
386	char *name;
387	Reg1 char *t;
388	char tmpbuf[200];
389	int sleading = strip_leading;
390
391	if (!at)
392		return Nullch;
393	while (isspace((unsigned char)*at))
394		at++;
395#ifdef DEBUGGING
396	if (debug & 128)
397		say4("fetchname %s %d %d\n",at,strip_leading,assume_exists);
398#endif
399	if (strnEQ(at, _PATH_DEVNULL, sizeof _PATH_DEVNULL - 1))
400		/* So files can be created by diffing against /dev/null. */
401		return Nullch;
402	name = fullname = t = savestr(at);
403
404	/* Strip off up to `sleading' leading slashes and null terminate. */
405	for (; *t && !isspace((unsigned char)*t); t++)
406		if (*t == '/')
407			if (--sleading >= 0)
408				name = t + 1;
409	*t = '\0';
410
411	/*
412	 * If no -p option was given (957 is the default value!),
413	 * we were given a relative pathname,
414	 * and the leading directories that we just stripped off all exist,
415	 * put them back on.
416	 */
417	if (strip_leading == 957 && name != fullname && *fullname != '/') {
418		name[-1] = '\0';
419		if (stat(fullname, &filestat) == 0 &&
420		    S_ISDIR(filestat.st_mode)) {
421			name[-1] = '/';
422			name = fullname;
423		}
424	}
425
426	name = savestr(name);
427	free(fullname);
428
429	if (stat(name, &filestat) && !assume_exists) {
430		char *filebase = basename(name);
431		int pathlen = filebase - name;
432
433		/* Put any leading path into `tmpbuf'. */
434		strncpy(tmpbuf, name, pathlen);
435
436#define try(f, a1, a2) \
437    (Sprintf(tmpbuf + pathlen, f, a1, a2), stat(tmpbuf, &filestat) == 0)
438		if (try("RCS/%s%s", filebase, RCSSUFFIX) ||
439		    try("RCS/%s%s", filebase,        "") ||
440		    try(    "%s%s", filebase, RCSSUFFIX) ||
441		    try("SCCS/%s%s", SCCSPREFIX, filebase) ||
442		    try(     "%s%s", SCCSPREFIX, filebase))
443			return name;
444		free(name);
445		name = Nullch;
446	}
447
448	return name;
449}
450
451char *
452xmalloc(unsigned int size)
453{
454	register char *p = (char *) malloc (size);
455	if (!p)
456		fatal("out of memory");
457	return p;
458}
459