xref: /openbsd-src/usr.bin/sdiff/edit.c (revision 28ce9ad7ae13161808b516d4f547e97c643d614f)
1*28ce9ad7Sderaadt /*	$OpenBSD: edit.c,v 1.20 2013/11/26 21:08:12 deraadt Exp $ */
2228e906dStedu 
3228e906dStedu /*
4228e906dStedu  * Written by Raymond Lai <ray@cyth.net>.
5228e906dStedu  * Public domain.
6228e906dStedu  */
7228e906dStedu 
8228e906dStedu #include <sys/types.h>
9228e906dStedu #include <sys/wait.h>
10228e906dStedu 
11228e906dStedu #include <ctype.h>
12228e906dStedu #include <err.h>
13624f0200Sray #include <errno.h>
14624f0200Sray #include <paths.h>
15624f0200Sray #include <signal.h>
16228e906dStedu #include <stdio.h>
17228e906dStedu #include <stdlib.h>
1831342e88Sray #include <string.h>
19228e906dStedu #include <unistd.h>
20228e906dStedu 
211d3b72fdSotto #include "common.h"
22228e906dStedu #include "extern.h"
23228e906dStedu 
24624f0200Sray int editit(const char *);
25228e906dStedu 
26228e906dStedu /*
2792b1eb5aSray  * Execute an editor on the specified pathname, which is interpreted
2892b1eb5aSray  * from the shell.  This means flags may be included.
2992b1eb5aSray  *
3092b1eb5aSray  * Returns -1 on error, or the exit value on success.
31228e906dStedu  */
32624f0200Sray int
editit(const char * pathname)33624f0200Sray editit(const char *pathname)
34228e906dStedu {
35624f0200Sray 	char *argp[] = {"sh", "-c", NULL, NULL}, *ed, *p;
3643316b18Sderaadt 	sig_t sighup, sigint, sigquit, sigchld;
37228e906dStedu 	pid_t pid;
3843316b18Sderaadt 	int saved_errno, st, ret = -1;
39228e906dStedu 
40624f0200Sray 	ed = getenv("VISUAL");
41624f0200Sray 	if (ed == NULL || ed[0] == '\0')
42624f0200Sray 		ed = getenv("EDITOR");
43624f0200Sray 	if (ed == NULL || ed[0] == '\0')
44624f0200Sray 		ed = _PATH_VI;
45624f0200Sray 	if (asprintf(&p, "%s %s", ed, pathname) == -1)
46624f0200Sray 		return (-1);
47624f0200Sray 	argp[2] = p;
48228e906dStedu 
49624f0200Sray 	sighup = signal(SIGHUP, SIG_IGN);
50624f0200Sray 	sigint = signal(SIGINT, SIG_IGN);
51624f0200Sray 	sigquit = signal(SIGQUIT, SIG_IGN);
5243316b18Sderaadt 	sigchld = signal(SIGCHLD, SIG_DFL);
5392b1eb5aSray 	if ((pid = fork()) == -1)
5492b1eb5aSray 		goto fail;
55624f0200Sray 	if (pid == 0) {
56624f0200Sray 		execv(_PATH_BSHELL, argp);
57624f0200Sray 		_exit(127);
58228e906dStedu 	}
5992b1eb5aSray 	while (waitpid(pid, &st, 0) == -1)
60624f0200Sray 		if (errno != EINTR)
6192b1eb5aSray 			goto fail;
6243316b18Sderaadt 	if (!WIFEXITED(st))
6392b1eb5aSray 		errno = EINTR;
6443316b18Sderaadt 	else
6543316b18Sderaadt 		ret = WEXITSTATUS(st);
6692b1eb5aSray 
6792b1eb5aSray  fail:
6892b1eb5aSray 	saved_errno = errno;
6992b1eb5aSray 	(void)signal(SIGHUP, sighup);
7092b1eb5aSray 	(void)signal(SIGINT, sigint);
7192b1eb5aSray 	(void)signal(SIGQUIT, sigquit);
7243316b18Sderaadt 	(void)signal(SIGCHLD, sigchld);
7392b1eb5aSray 	free(p);
7492b1eb5aSray 	errno = saved_errno;
7543316b18Sderaadt 	return (ret);
76228e906dStedu }
77228e906dStedu 
78228e906dStedu /*
79228e906dStedu  * Parse edit command.  Returns 0 on success, -1 on error.
80228e906dStedu  */
81228e906dStedu int
eparse(const char * cmd,const char * left,const char * right)82228e906dStedu eparse(const char *cmd, const char *left, const char *right)
83228e906dStedu {
84228e906dStedu 	FILE *file;
858f6d6b01Ssteven 	size_t nread;
8631342e88Sray 	int fd;
87951d632eSotto 	char *filename;
88228e906dStedu 	char buf[BUFSIZ], *text;
89228e906dStedu 
90228e906dStedu 	/* Skip whitespace. */
91*28ce9ad7Sderaadt 	while (isspace((unsigned char)*cmd))
92228e906dStedu 		++cmd;
93228e906dStedu 
94228e906dStedu 	text = NULL;
95228e906dStedu 	switch (*cmd) {
96228e906dStedu 	case '\0':
97228e906dStedu 		/* Edit empty file. */
98228e906dStedu 		break;
99228e906dStedu 
100228e906dStedu 	case 'b':
101228e906dStedu 		/* Both strings. */
102228e906dStedu 		if (left == NULL)
103228e906dStedu 			goto RIGHT;
104228e906dStedu 		if (right == NULL)
105228e906dStedu 			goto LEFT;
106228e906dStedu 
107228e906dStedu 		/* Neither column is blank, so print both. */
108488667c1Sclaudio 		if (asprintf(&text, "%s\n%s\n", left, right) == -1)
109228e906dStedu 			err(2, "could not allocate memory");
110228e906dStedu 		break;
111228e906dStedu 
112228e906dStedu 	case 'l':
1130fed3240Sderaadt LEFT:
114228e906dStedu 		/* Skip if there is no left column. */
115228e906dStedu 		if (left == NULL)
116228e906dStedu 			break;
117228e906dStedu 
118228e906dStedu 		if (asprintf(&text, "%s\n", left) == -1)
119228e906dStedu 			err(2, "could not allocate memory");
120228e906dStedu 
121228e906dStedu 		break;
122228e906dStedu 
123228e906dStedu 	case 'r':
1240fed3240Sderaadt RIGHT:
125228e906dStedu 		/* Skip if there is no right column. */
126228e906dStedu 		if (right == NULL)
127228e906dStedu 			break;
128228e906dStedu 
129228e906dStedu 		if (asprintf(&text, "%s\n", right) == -1)
130228e906dStedu 			err(2, "could not allocate memory");
131228e906dStedu 
132228e906dStedu 		break;
133228e906dStedu 
134228e906dStedu 	default:
135228e906dStedu 		return (-1);
136228e906dStedu 	}
137228e906dStedu 
138228e906dStedu 	/* Create temp file. */
13931342e88Sray 	if (asprintf(&filename, "%s/sdiff.XXXXXXXXXX", tmpdir) == -1)
14031342e88Sray 		err(2, "asprintf");
14131342e88Sray 	if ((fd = mkstemp(filename)) == -1)
14231342e88Sray 		err(2, "mkstemp");
14331342e88Sray 	if (text != NULL) {
14431342e88Sray 		size_t len;
1458f6d6b01Ssteven 		ssize_t nwritten;
14631342e88Sray 
14731342e88Sray 		len = strlen(text);
14831342e88Sray 		if ((nwritten = write(fd, text, len)) == -1 ||
14931342e88Sray 		    nwritten != len) {
15031342e88Sray 			warn("error writing to temp file");
15131342e88Sray 			cleanup(filename);
15231342e88Sray 		}
15331342e88Sray 	}
15431342e88Sray 	close(fd);
155228e906dStedu 
156228e906dStedu 	/* text is no longer used. */
157228e906dStedu 	free(text);
158228e906dStedu 
159228e906dStedu 	/* Edit temp file. */
160624f0200Sray 	if (editit(filename) == -1) {
161624f0200Sray 		warn("error editing %s", filename);
162624f0200Sray 		cleanup(filename);
163624f0200Sray 	}
164228e906dStedu 
165228e906dStedu 	/* Open temporary file. */
166228e906dStedu 	if (!(file = fopen(filename, "r"))) {
167228e906dStedu 		warn("could not open edited file: %s", filename);
168228e906dStedu 		cleanup(filename);
169228e906dStedu 	}
170228e906dStedu 
171228e906dStedu 	/* Copy temporary file contents to output file. */
172228e906dStedu 	for (nread = sizeof(buf); nread == sizeof(buf);) {
1738f6d6b01Ssteven 		size_t nwritten;
1748f6d6b01Ssteven 
175228e906dStedu 		nread = fread(buf, sizeof(*buf), sizeof(buf), file);
176228e906dStedu 		/* Test for error or end of file. */
177228e906dStedu 		if (nread != sizeof(buf) &&
178228e906dStedu 		    (ferror(file) || !feof(file))) {
179228e906dStedu 			warnx("error reading edited file: %s", filename);
180228e906dStedu 			cleanup(filename);
181228e906dStedu 		}
182228e906dStedu 
183228e906dStedu 		/*
184228e906dStedu 		 * If we have nothing to read, break out of loop
185228e906dStedu 		 * instead of writing nothing.
186228e906dStedu 		 */
187228e906dStedu 		if (!nread)
188228e906dStedu 			break;
189228e906dStedu 
190228e906dStedu 		/* Write data we just read. */
191d4ba4722Sray 		nwritten = fwrite(buf, sizeof(*buf), nread, outfp);
192228e906dStedu 		if (nwritten != nread) {
193228e906dStedu 			warnx("error writing to output file");
194228e906dStedu 			cleanup(filename);
195228e906dStedu 		}
196228e906dStedu 	}
197228e906dStedu 
198228e906dStedu 	/* We've reached the end of the temporary file, so remove it. */
199228e906dStedu 	if (unlink(filename))
200228e906dStedu 		warn("could not delete: %s", filename);
201df0f73baStedu 	fclose(file);
202228e906dStedu 
203951d632eSotto 	free(filename);
204228e906dStedu 
205228e906dStedu 	return (0);
206228e906dStedu }
207