xref: /dflybsd-src/usr.bin/sdiff/edit.c (revision c9733229451fac5faa53b1a016b01866eae75a1c)
1*c9733229SMatthew Dillon /*	$OpenBSD: edit.c,v 1.19 2009/06/07 13:29:50 ray Exp $ */
2*c9733229SMatthew Dillon 
3*c9733229SMatthew Dillon /*
4*c9733229SMatthew Dillon  * Written by Raymond Lai <ray@cyth.net>.
5*c9733229SMatthew Dillon  * Public domain.
6*c9733229SMatthew Dillon  */
7*c9733229SMatthew Dillon 
8*c9733229SMatthew Dillon #include <sys/types.h>
9*c9733229SMatthew Dillon #include <sys/wait.h>
10*c9733229SMatthew Dillon 
11*c9733229SMatthew Dillon #include <ctype.h>
12*c9733229SMatthew Dillon #include <err.h>
13*c9733229SMatthew Dillon #include <errno.h>
14*c9733229SMatthew Dillon #include <paths.h>
15*c9733229SMatthew Dillon #include <signal.h>
16*c9733229SMatthew Dillon #include <stdio.h>
17*c9733229SMatthew Dillon #include <stdlib.h>
18*c9733229SMatthew Dillon #include <string.h>
19*c9733229SMatthew Dillon #include <unistd.h>
20*c9733229SMatthew Dillon 
21*c9733229SMatthew Dillon #include "extern.h"
22*c9733229SMatthew Dillon 
23*c9733229SMatthew Dillon static void
cleanup(const char * filename)24*c9733229SMatthew Dillon cleanup(const char *filename)
25*c9733229SMatthew Dillon {
26*c9733229SMatthew Dillon 
27*c9733229SMatthew Dillon 	if (unlink(filename))
28*c9733229SMatthew Dillon 		err(2, "could not delete: %s", filename);
29*c9733229SMatthew Dillon 	exit(2);
30*c9733229SMatthew Dillon }
31*c9733229SMatthew Dillon 
32*c9733229SMatthew Dillon /*
33*c9733229SMatthew Dillon  * Execute an editor on the specified pathname, which is interpreted
34*c9733229SMatthew Dillon  * from the shell.  This means flags may be included.
35*c9733229SMatthew Dillon  *
36*c9733229SMatthew Dillon  * Returns -1 on error, or the exit value on success.
37*c9733229SMatthew Dillon  */
38*c9733229SMatthew Dillon static int
editit(const char * pathname)39*c9733229SMatthew Dillon editit(const char *pathname)
40*c9733229SMatthew Dillon {
41*c9733229SMatthew Dillon 	sig_t sighup, sigint, sigquit, sigchld;
42*c9733229SMatthew Dillon 	pid_t pid;
43*c9733229SMatthew Dillon 	int saved_errno, st, ret = -1;
44*c9733229SMatthew Dillon 	const char *ed;
45*c9733229SMatthew Dillon 
46*c9733229SMatthew Dillon 	ed = getenv("VISUAL");
47*c9733229SMatthew Dillon 	if (ed == NULL)
48*c9733229SMatthew Dillon 		ed = getenv("EDITOR");
49*c9733229SMatthew Dillon 	if (ed == NULL)
50*c9733229SMatthew Dillon 		ed = _PATH_VI;
51*c9733229SMatthew Dillon 
52*c9733229SMatthew Dillon 	sighup = signal(SIGHUP, SIG_IGN);
53*c9733229SMatthew Dillon 	sigint = signal(SIGINT, SIG_IGN);
54*c9733229SMatthew Dillon 	sigquit = signal(SIGQUIT, SIG_IGN);
55*c9733229SMatthew Dillon 	sigchld = signal(SIGCHLD, SIG_DFL);
56*c9733229SMatthew Dillon 	if ((pid = fork()) == -1)
57*c9733229SMatthew Dillon 		goto fail;
58*c9733229SMatthew Dillon 	if (pid == 0) {
59*c9733229SMatthew Dillon 		execlp(ed, ed, pathname, (char *)NULL);
60*c9733229SMatthew Dillon 		_exit(127);
61*c9733229SMatthew Dillon 	}
62*c9733229SMatthew Dillon 	while (waitpid(pid, &st, 0) == -1)
63*c9733229SMatthew Dillon 		if (errno != EINTR)
64*c9733229SMatthew Dillon 			goto fail;
65*c9733229SMatthew Dillon 	if (!WIFEXITED(st))
66*c9733229SMatthew Dillon 		errno = EINTR;
67*c9733229SMatthew Dillon 	else
68*c9733229SMatthew Dillon 		ret = WEXITSTATUS(st);
69*c9733229SMatthew Dillon 
70*c9733229SMatthew Dillon  fail:
71*c9733229SMatthew Dillon 	saved_errno = errno;
72*c9733229SMatthew Dillon 	(void)signal(SIGHUP, sighup);
73*c9733229SMatthew Dillon 	(void)signal(SIGINT, sigint);
74*c9733229SMatthew Dillon 	(void)signal(SIGQUIT, sigquit);
75*c9733229SMatthew Dillon 	(void)signal(SIGCHLD, sigchld);
76*c9733229SMatthew Dillon 	errno = saved_errno;
77*c9733229SMatthew Dillon 	return (ret);
78*c9733229SMatthew Dillon }
79*c9733229SMatthew Dillon 
80*c9733229SMatthew Dillon /*
81*c9733229SMatthew Dillon  * Parse edit command.  Returns 0 on success, -1 on error.
82*c9733229SMatthew Dillon  */
83*c9733229SMatthew Dillon int
eparse(const char * cmd,const char * left,const char * right)84*c9733229SMatthew Dillon eparse(const char *cmd, const char *left, const char *right)
85*c9733229SMatthew Dillon {
86*c9733229SMatthew Dillon 	FILE *file;
87*c9733229SMatthew Dillon 	size_t nread;
88*c9733229SMatthew Dillon 	int fd;
89*c9733229SMatthew Dillon 	char *filename;
90*c9733229SMatthew Dillon 	char buf[BUFSIZ], *text;
91*c9733229SMatthew Dillon 
92*c9733229SMatthew Dillon 	/* Skip whitespace. */
93*c9733229SMatthew Dillon 	while (isspace(*cmd))
94*c9733229SMatthew Dillon 		++cmd;
95*c9733229SMatthew Dillon 
96*c9733229SMatthew Dillon 	text = NULL;
97*c9733229SMatthew Dillon 	switch (*cmd) {
98*c9733229SMatthew Dillon 	case '\0':
99*c9733229SMatthew Dillon 		/* Edit empty file. */
100*c9733229SMatthew Dillon 		break;
101*c9733229SMatthew Dillon 
102*c9733229SMatthew Dillon 	case 'b':
103*c9733229SMatthew Dillon 		/* Both strings. */
104*c9733229SMatthew Dillon 		if (left == NULL)
105*c9733229SMatthew Dillon 			goto RIGHT;
106*c9733229SMatthew Dillon 		if (right == NULL)
107*c9733229SMatthew Dillon 			goto LEFT;
108*c9733229SMatthew Dillon 
109*c9733229SMatthew Dillon 		/* Neither column is blank, so print both. */
110*c9733229SMatthew Dillon 		if (asprintf(&text, "%s\n%s\n", left, right) == -1)
111*c9733229SMatthew Dillon 			err(2, "could not allocate memory");
112*c9733229SMatthew Dillon 		break;
113*c9733229SMatthew Dillon 
114*c9733229SMatthew Dillon 	case 'l':
115*c9733229SMatthew Dillon LEFT:
116*c9733229SMatthew Dillon 		/* Skip if there is no left column. */
117*c9733229SMatthew Dillon 		if (left == NULL)
118*c9733229SMatthew Dillon 			break;
119*c9733229SMatthew Dillon 
120*c9733229SMatthew Dillon 		if (asprintf(&text, "%s\n", left) == -1)
121*c9733229SMatthew Dillon 			err(2, "could not allocate memory");
122*c9733229SMatthew Dillon 
123*c9733229SMatthew Dillon 		break;
124*c9733229SMatthew Dillon 
125*c9733229SMatthew Dillon 	case 'r':
126*c9733229SMatthew Dillon RIGHT:
127*c9733229SMatthew Dillon 		/* Skip if there is no right column. */
128*c9733229SMatthew Dillon 		if (right == NULL)
129*c9733229SMatthew Dillon 			break;
130*c9733229SMatthew Dillon 
131*c9733229SMatthew Dillon 		if (asprintf(&text, "%s\n", right) == -1)
132*c9733229SMatthew Dillon 			err(2, "could not allocate memory");
133*c9733229SMatthew Dillon 
134*c9733229SMatthew Dillon 		break;
135*c9733229SMatthew Dillon 
136*c9733229SMatthew Dillon 	default:
137*c9733229SMatthew Dillon 		return (-1);
138*c9733229SMatthew Dillon 	}
139*c9733229SMatthew Dillon 
140*c9733229SMatthew Dillon 	/* Create temp file. */
141*c9733229SMatthew Dillon 	if (asprintf(&filename, "%s/sdiff.XXXXXXXXXX", tmpdir) == -1)
142*c9733229SMatthew Dillon 		err(2, "asprintf");
143*c9733229SMatthew Dillon 	if ((fd = mkstemp(filename)) == -1)
144*c9733229SMatthew Dillon 		err(2, "mkstemp");
145*c9733229SMatthew Dillon 	if (text != NULL) {
146*c9733229SMatthew Dillon 		size_t len;
147*c9733229SMatthew Dillon 		ssize_t nwritten;
148*c9733229SMatthew Dillon 
149*c9733229SMatthew Dillon 		len = strlen(text);
150*c9733229SMatthew Dillon 		if ((nwritten = write(fd, text, len)) == -1 ||
151*c9733229SMatthew Dillon 		    (size_t)nwritten != len) {
152*c9733229SMatthew Dillon 			warn("error writing to temp file");
153*c9733229SMatthew Dillon 			cleanup(filename);
154*c9733229SMatthew Dillon 		}
155*c9733229SMatthew Dillon 	}
156*c9733229SMatthew Dillon 	close(fd);
157*c9733229SMatthew Dillon 
158*c9733229SMatthew Dillon 	/* text is no longer used. */
159*c9733229SMatthew Dillon 	free(text);
160*c9733229SMatthew Dillon 
161*c9733229SMatthew Dillon 	/* Edit temp file. */
162*c9733229SMatthew Dillon 	if (editit(filename) == -1) {
163*c9733229SMatthew Dillon 		warn("error editing %s", filename);
164*c9733229SMatthew Dillon 		cleanup(filename);
165*c9733229SMatthew Dillon 	}
166*c9733229SMatthew Dillon 
167*c9733229SMatthew Dillon 	/* Open temporary file. */
168*c9733229SMatthew Dillon 	if (!(file = fopen(filename, "r"))) {
169*c9733229SMatthew Dillon 		warn("could not open edited file: %s", filename);
170*c9733229SMatthew Dillon 		cleanup(filename);
171*c9733229SMatthew Dillon 	}
172*c9733229SMatthew Dillon 
173*c9733229SMatthew Dillon 	/* Copy temporary file contents to output file. */
174*c9733229SMatthew Dillon 	for (nread = sizeof(buf); nread == sizeof(buf);) {
175*c9733229SMatthew Dillon 		size_t nwritten;
176*c9733229SMatthew Dillon 
177*c9733229SMatthew Dillon 		nread = fread(buf, sizeof(*buf), sizeof(buf), file);
178*c9733229SMatthew Dillon 		/* Test for error or end of file. */
179*c9733229SMatthew Dillon 		if (nread != sizeof(buf) &&
180*c9733229SMatthew Dillon 		    (ferror(file) || !feof(file))) {
181*c9733229SMatthew Dillon 			warnx("error reading edited file: %s", filename);
182*c9733229SMatthew Dillon 			cleanup(filename);
183*c9733229SMatthew Dillon 		}
184*c9733229SMatthew Dillon 
185*c9733229SMatthew Dillon 		/*
186*c9733229SMatthew Dillon 		 * If we have nothing to read, break out of loop
187*c9733229SMatthew Dillon 		 * instead of writing nothing.
188*c9733229SMatthew Dillon 		 */
189*c9733229SMatthew Dillon 		if (!nread)
190*c9733229SMatthew Dillon 			break;
191*c9733229SMatthew Dillon 
192*c9733229SMatthew Dillon 		/* Write data we just read. */
193*c9733229SMatthew Dillon 		nwritten = fwrite(buf, sizeof(*buf), nread, outfp);
194*c9733229SMatthew Dillon 		if (nwritten != nread) {
195*c9733229SMatthew Dillon 			warnx("error writing to output file");
196*c9733229SMatthew Dillon 			cleanup(filename);
197*c9733229SMatthew Dillon 		}
198*c9733229SMatthew Dillon 	}
199*c9733229SMatthew Dillon 
200*c9733229SMatthew Dillon 	/* We've reached the end of the temporary file, so remove it. */
201*c9733229SMatthew Dillon 	if (unlink(filename))
202*c9733229SMatthew Dillon 		warn("could not delete: %s", filename);
203*c9733229SMatthew Dillon 	fclose(file);
204*c9733229SMatthew Dillon 
205*c9733229SMatthew Dillon 	free(filename);
206*c9733229SMatthew Dillon 
207*c9733229SMatthew Dillon 	return (0);
208*c9733229SMatthew Dillon }
209