xref: /freebsd/usr.bin/sdiff/edit.c (revision 1ddda798a64a992da5e443482766406aecca4273)
113b5b548SBaptiste Daroussin /*	$OpenBSD: edit.c,v 1.19 2009/06/07 13:29:50 ray Exp $ */
213b5b548SBaptiste Daroussin 
313b5b548SBaptiste Daroussin /*
413b5b548SBaptiste Daroussin  * Written by Raymond Lai <ray@cyth.net>.
513b5b548SBaptiste Daroussin  * Public domain.
613b5b548SBaptiste Daroussin  */
713b5b548SBaptiste Daroussin 
813b5b548SBaptiste Daroussin #include <sys/cdefs.h>
913b5b548SBaptiste Daroussin __FBSDID("$FreeBSD$");
1013b5b548SBaptiste Daroussin 
1113b5b548SBaptiste Daroussin #include <sys/types.h>
1213b5b548SBaptiste Daroussin #include <sys/wait.h>
1313b5b548SBaptiste Daroussin 
1413b5b548SBaptiste Daroussin #include <ctype.h>
1513b5b548SBaptiste Daroussin #include <err.h>
1613b5b548SBaptiste Daroussin #include <errno.h>
1713b5b548SBaptiste Daroussin #include <paths.h>
1813b5b548SBaptiste Daroussin #include <signal.h>
1913b5b548SBaptiste Daroussin #include <stdio.h>
2013b5b548SBaptiste Daroussin #include <stdlib.h>
2113b5b548SBaptiste Daroussin #include <string.h>
2213b5b548SBaptiste Daroussin #include <unistd.h>
2313b5b548SBaptiste Daroussin 
2413b5b548SBaptiste Daroussin #include "common.h"
2513b5b548SBaptiste Daroussin #include "extern.h"
2613b5b548SBaptiste Daroussin 
2713b5b548SBaptiste Daroussin int editit(const char *);
2813b5b548SBaptiste Daroussin 
2913b5b548SBaptiste Daroussin /*
3013b5b548SBaptiste Daroussin  * Execute an editor on the specified pathname, which is interpreted
3113b5b548SBaptiste Daroussin  * from the shell.  This means flags may be included.
3213b5b548SBaptiste Daroussin  *
3313b5b548SBaptiste Daroussin  * Returns -1 on error, or the exit value on success.
3413b5b548SBaptiste Daroussin  */
3513b5b548SBaptiste Daroussin int
3613b5b548SBaptiste Daroussin editit(const char *pathname)
3713b5b548SBaptiste Daroussin {
3813b5b548SBaptiste Daroussin 	sig_t sighup, sigint, sigquit, sigchld;
3913b5b548SBaptiste Daroussin 	pid_t pid;
4013b5b548SBaptiste Daroussin 	int saved_errno, st, ret = -1;
4115eaa1aeSBaptiste Daroussin 	const char *ed;
4213b5b548SBaptiste Daroussin 
43*1ddda798SBaptiste Daroussin 	ed = getenv("VISUAL");
44*1ddda798SBaptiste Daroussin 	if (ed == NULL)
45*1ddda798SBaptiste Daroussin 		ed = getenv("EDITOR");
46*1ddda798SBaptiste Daroussin 	if (ed == NULL)
4713b5b548SBaptiste Daroussin 		ed = _PATH_VI;
4813b5b548SBaptiste Daroussin 
4913b5b548SBaptiste Daroussin 	sighup = signal(SIGHUP, SIG_IGN);
5013b5b548SBaptiste Daroussin 	sigint = signal(SIGINT, SIG_IGN);
5113b5b548SBaptiste Daroussin 	sigquit = signal(SIGQUIT, SIG_IGN);
5213b5b548SBaptiste Daroussin 	sigchld = signal(SIGCHLD, SIG_DFL);
5313b5b548SBaptiste Daroussin 	if ((pid = fork()) == -1)
5413b5b548SBaptiste Daroussin 		goto fail;
5513b5b548SBaptiste Daroussin 	if (pid == 0) {
5615eaa1aeSBaptiste Daroussin 		execlp(ed, ed, pathname, (char *)NULL);
5713b5b548SBaptiste Daroussin 		_exit(127);
5813b5b548SBaptiste Daroussin 	}
5913b5b548SBaptiste Daroussin 	while (waitpid(pid, &st, 0) == -1)
6013b5b548SBaptiste Daroussin 		if (errno != EINTR)
6113b5b548SBaptiste Daroussin 			goto fail;
6213b5b548SBaptiste Daroussin 	if (!WIFEXITED(st))
6313b5b548SBaptiste Daroussin 		errno = EINTR;
6413b5b548SBaptiste Daroussin 	else
6513b5b548SBaptiste Daroussin 		ret = WEXITSTATUS(st);
6613b5b548SBaptiste Daroussin 
6713b5b548SBaptiste Daroussin  fail:
6813b5b548SBaptiste Daroussin 	saved_errno = errno;
6913b5b548SBaptiste Daroussin 	(void)signal(SIGHUP, sighup);
7013b5b548SBaptiste Daroussin 	(void)signal(SIGINT, sigint);
7113b5b548SBaptiste Daroussin 	(void)signal(SIGQUIT, sigquit);
7213b5b548SBaptiste Daroussin 	(void)signal(SIGCHLD, sigchld);
7313b5b548SBaptiste Daroussin 	errno = saved_errno;
7413b5b548SBaptiste Daroussin 	return (ret);
7513b5b548SBaptiste Daroussin }
7613b5b548SBaptiste Daroussin 
7713b5b548SBaptiste Daroussin /*
7813b5b548SBaptiste Daroussin  * Parse edit command.  Returns 0 on success, -1 on error.
7913b5b548SBaptiste Daroussin  */
8013b5b548SBaptiste Daroussin int
8113b5b548SBaptiste Daroussin eparse(const char *cmd, const char *left, const char *right)
8213b5b548SBaptiste Daroussin {
8313b5b548SBaptiste Daroussin 	FILE *file;
8413b5b548SBaptiste Daroussin 	size_t nread;
8513b5b548SBaptiste Daroussin 	int fd;
8613b5b548SBaptiste Daroussin 	char *filename;
8713b5b548SBaptiste Daroussin 	char buf[BUFSIZ], *text;
8813b5b548SBaptiste Daroussin 
8913b5b548SBaptiste Daroussin 	/* Skip whitespace. */
9013b5b548SBaptiste Daroussin 	while (isspace(*cmd))
9113b5b548SBaptiste Daroussin 		++cmd;
9213b5b548SBaptiste Daroussin 
9313b5b548SBaptiste Daroussin 	text = NULL;
9413b5b548SBaptiste Daroussin 	switch (*cmd) {
9513b5b548SBaptiste Daroussin 	case '\0':
9613b5b548SBaptiste Daroussin 		/* Edit empty file. */
9713b5b548SBaptiste Daroussin 		break;
9813b5b548SBaptiste Daroussin 
9913b5b548SBaptiste Daroussin 	case 'b':
10013b5b548SBaptiste Daroussin 		/* Both strings. */
10113b5b548SBaptiste Daroussin 		if (left == NULL)
10213b5b548SBaptiste Daroussin 			goto RIGHT;
10313b5b548SBaptiste Daroussin 		if (right == NULL)
10413b5b548SBaptiste Daroussin 			goto LEFT;
10513b5b548SBaptiste Daroussin 
10613b5b548SBaptiste Daroussin 		/* Neither column is blank, so print both. */
10713b5b548SBaptiste Daroussin 		if (asprintf(&text, "%s\n%s\n", left, right) == -1)
10813b5b548SBaptiste Daroussin 			err(2, "could not allocate memory");
10913b5b548SBaptiste Daroussin 		break;
11013b5b548SBaptiste Daroussin 
11113b5b548SBaptiste Daroussin 	case 'l':
11213b5b548SBaptiste Daroussin LEFT:
11313b5b548SBaptiste Daroussin 		/* Skip if there is no left column. */
11413b5b548SBaptiste Daroussin 		if (left == NULL)
11513b5b548SBaptiste Daroussin 			break;
11613b5b548SBaptiste Daroussin 
11713b5b548SBaptiste Daroussin 		if (asprintf(&text, "%s\n", left) == -1)
11813b5b548SBaptiste Daroussin 			err(2, "could not allocate memory");
11913b5b548SBaptiste Daroussin 
12013b5b548SBaptiste Daroussin 		break;
12113b5b548SBaptiste Daroussin 
12213b5b548SBaptiste Daroussin 	case 'r':
12313b5b548SBaptiste Daroussin RIGHT:
12413b5b548SBaptiste Daroussin 		/* Skip if there is no right column. */
12513b5b548SBaptiste Daroussin 		if (right == NULL)
12613b5b548SBaptiste Daroussin 			break;
12713b5b548SBaptiste Daroussin 
12813b5b548SBaptiste Daroussin 		if (asprintf(&text, "%s\n", right) == -1)
12913b5b548SBaptiste Daroussin 			err(2, "could not allocate memory");
13013b5b548SBaptiste Daroussin 
13113b5b548SBaptiste Daroussin 		break;
13213b5b548SBaptiste Daroussin 
13313b5b548SBaptiste Daroussin 	default:
13413b5b548SBaptiste Daroussin 		return (-1);
13513b5b548SBaptiste Daroussin 	}
13613b5b548SBaptiste Daroussin 
13713b5b548SBaptiste Daroussin 	/* Create temp file. */
13813b5b548SBaptiste Daroussin 	if (asprintf(&filename, "%s/sdiff.XXXXXXXXXX", tmpdir) == -1)
13913b5b548SBaptiste Daroussin 		err(2, "asprintf");
14013b5b548SBaptiste Daroussin 	if ((fd = mkstemp(filename)) == -1)
14113b5b548SBaptiste Daroussin 		err(2, "mkstemp");
14213b5b548SBaptiste Daroussin 	if (text != NULL) {
14313b5b548SBaptiste Daroussin 		size_t len;
14413b5b548SBaptiste Daroussin 		ssize_t nwritten;
14513b5b548SBaptiste Daroussin 
14613b5b548SBaptiste Daroussin 		len = strlen(text);
14713b5b548SBaptiste Daroussin 		if ((nwritten = write(fd, text, len)) == -1 ||
14813b5b548SBaptiste Daroussin 		    (size_t)nwritten != len) {
14913b5b548SBaptiste Daroussin 			warn("error writing to temp file");
15013b5b548SBaptiste Daroussin 			cleanup(filename);
15113b5b548SBaptiste Daroussin 		}
15213b5b548SBaptiste Daroussin 	}
15313b5b548SBaptiste Daroussin 	close(fd);
15413b5b548SBaptiste Daroussin 
15513b5b548SBaptiste Daroussin 	/* text is no longer used. */
15613b5b548SBaptiste Daroussin 	free(text);
15713b5b548SBaptiste Daroussin 
15813b5b548SBaptiste Daroussin 	/* Edit temp file. */
15913b5b548SBaptiste Daroussin 	if (editit(filename) == -1) {
16013b5b548SBaptiste Daroussin 		warn("error editing %s", filename);
16113b5b548SBaptiste Daroussin 		cleanup(filename);
16213b5b548SBaptiste Daroussin 	}
16313b5b548SBaptiste Daroussin 
16413b5b548SBaptiste Daroussin 	/* Open temporary file. */
16513b5b548SBaptiste Daroussin 	if (!(file = fopen(filename, "r"))) {
16613b5b548SBaptiste Daroussin 		warn("could not open edited file: %s", filename);
16713b5b548SBaptiste Daroussin 		cleanup(filename);
16813b5b548SBaptiste Daroussin 	}
16913b5b548SBaptiste Daroussin 
17013b5b548SBaptiste Daroussin 	/* Copy temporary file contents to output file. */
17113b5b548SBaptiste Daroussin 	for (nread = sizeof(buf); nread == sizeof(buf);) {
17213b5b548SBaptiste Daroussin 		size_t nwritten;
17313b5b548SBaptiste Daroussin 
17413b5b548SBaptiste Daroussin 		nread = fread(buf, sizeof(*buf), sizeof(buf), file);
17513b5b548SBaptiste Daroussin 		/* Test for error or end of file. */
17613b5b548SBaptiste Daroussin 		if (nread != sizeof(buf) &&
17713b5b548SBaptiste Daroussin 		    (ferror(file) || !feof(file))) {
17813b5b548SBaptiste Daroussin 			warnx("error reading edited file: %s", filename);
17913b5b548SBaptiste Daroussin 			cleanup(filename);
18013b5b548SBaptiste Daroussin 		}
18113b5b548SBaptiste Daroussin 
18213b5b548SBaptiste Daroussin 		/*
18313b5b548SBaptiste Daroussin 		 * If we have nothing to read, break out of loop
18413b5b548SBaptiste Daroussin 		 * instead of writing nothing.
18513b5b548SBaptiste Daroussin 		 */
18613b5b548SBaptiste Daroussin 		if (!nread)
18713b5b548SBaptiste Daroussin 			break;
18813b5b548SBaptiste Daroussin 
18913b5b548SBaptiste Daroussin 		/* Write data we just read. */
19013b5b548SBaptiste Daroussin 		nwritten = fwrite(buf, sizeof(*buf), nread, outfp);
19113b5b548SBaptiste Daroussin 		if (nwritten != nread) {
19213b5b548SBaptiste Daroussin 			warnx("error writing to output file");
19313b5b548SBaptiste Daroussin 			cleanup(filename);
19413b5b548SBaptiste Daroussin 		}
19513b5b548SBaptiste Daroussin 	}
19613b5b548SBaptiste Daroussin 
19713b5b548SBaptiste Daroussin 	/* We've reached the end of the temporary file, so remove it. */
19813b5b548SBaptiste Daroussin 	if (unlink(filename))
19913b5b548SBaptiste Daroussin 		warn("could not delete: %s", filename);
20013b5b548SBaptiste Daroussin 	fclose(file);
20113b5b548SBaptiste Daroussin 
20213b5b548SBaptiste Daroussin 	free(filename);
20313b5b548SBaptiste Daroussin 
20413b5b548SBaptiste Daroussin 	return (0);
20513b5b548SBaptiste Daroussin }
206