1255736Sdavidch/*	$OpenBSD: edit.c,v 1.19 2009/06/07 13:29:50 ray Exp $ */
2265411Sdavidcs
3255736Sdavidch/*
4255736Sdavidch * Written by Raymond Lai <ray@cyth.net>.
5255736Sdavidch * Public domain.
6255736Sdavidch */
7255736Sdavidch
8255736Sdavidch#include <sys/cdefs.h>
9255736Sdavidch__FBSDID("$FreeBSD: stable/11/usr.bin/sdiff/edit.c 307772 2016-10-22 13:15:19Z bapt $");
10255736Sdavidch
11255736Sdavidch#include <sys/types.h>
12255736Sdavidch#include <sys/wait.h>
13255736Sdavidch
14255736Sdavidch#include <ctype.h>
15255736Sdavidch#include <err.h>
16255736Sdavidch#include <errno.h>
17255736Sdavidch#include <paths.h>
18255736Sdavidch#include <signal.h>
19255736Sdavidch#include <stdio.h>
20255736Sdavidch#include <stdlib.h>
21255736Sdavidch#include <string.h>
22255736Sdavidch#include <unistd.h>
23255736Sdavidch
24255736Sdavidch#include "extern.h"
25255736Sdavidch
26255736Sdavidchstatic void
27255736Sdavidchcleanup(const char *filename)
28255736Sdavidch{
29255736Sdavidch
30255736Sdavidch	if (unlink(filename))
31255736Sdavidch		err(2, "could not delete: %s", filename);
32255736Sdavidch	exit(2);
33255736Sdavidch}
34255736Sdavidch
35255736Sdavidch/*
36255736Sdavidch * Execute an editor on the specified pathname, which is interpreted
37255736Sdavidch * from the shell.  This means flags may be included.
38255736Sdavidch *
39255736Sdavidch * Returns -1 on error, or the exit value on success.
40255736Sdavidch */
41255736Sdavidchstatic int
42255736Sdavidcheditit(const char *pathname)
43255736Sdavidch{
44255736Sdavidch	sig_t sighup, sigint, sigquit, sigchld;
45255736Sdavidch	pid_t pid;
46255736Sdavidch	int saved_errno, st, ret = -1;
47255736Sdavidch	const char *ed;
48255736Sdavidch
49255736Sdavidch	ed = getenv("VISUAL");
50255736Sdavidch	if (ed == NULL)
51255736Sdavidch		ed = getenv("EDITOR");
52255736Sdavidch	if (ed == NULL)
53255736Sdavidch		ed = _PATH_VI;
54281855Srodrigc
55255736Sdavidch	sighup = signal(SIGHUP, SIG_IGN);
56255736Sdavidch	sigint = signal(SIGINT, SIG_IGN);
57255736Sdavidch	sigquit = signal(SIGQUIT, SIG_IGN);
58255736Sdavidch	sigchld = signal(SIGCHLD, SIG_DFL);
59255736Sdavidch	if ((pid = fork()) == -1)
60255736Sdavidch		goto fail;
61266979Smarcel	if (pid == 0) {
62255736Sdavidch		execlp(ed, ed, pathname, (char *)NULL);
63255736Sdavidch		_exit(127);
64255736Sdavidch	}
65255736Sdavidch	while (waitpid(pid, &st, 0) == -1)
66255736Sdavidch		if (errno != EINTR)
67255736Sdavidch			goto fail;
68255736Sdavidch	if (!WIFEXITED(st))
69255736Sdavidch		errno = EINTR;
70255736Sdavidch	else
71255736Sdavidch		ret = WEXITSTATUS(st);
72255736Sdavidch
73255736Sdavidch fail:
74255736Sdavidch	saved_errno = errno;
75255736Sdavidch	(void)signal(SIGHUP, sighup);
76255736Sdavidch	(void)signal(SIGINT, sigint);
77255736Sdavidch	(void)signal(SIGQUIT, sigquit);
78255736Sdavidch	(void)signal(SIGCHLD, sigchld);
79255736Sdavidch	errno = saved_errno;
80255736Sdavidch	return (ret);
81255736Sdavidch}
82255736Sdavidch
83255736Sdavidch/*
84255736Sdavidch * Parse edit command.  Returns 0 on success, -1 on error.
85255736Sdavidch */
86255736Sdavidchint
87255736Sdavidcheparse(const char *cmd, const char *left, const char *right)
88255736Sdavidch{
89255736Sdavidch	FILE *file;
90255736Sdavidch	size_t nread;
91255736Sdavidch	int fd;
92255736Sdavidch	char *filename;
93255736Sdavidch	char buf[BUFSIZ], *text;
94255736Sdavidch
95255736Sdavidch	/* Skip whitespace. */
96255736Sdavidch	while (isspace(*cmd))
97255736Sdavidch		++cmd;
98255736Sdavidch
99255736Sdavidch	text = NULL;
100255736Sdavidch	switch (*cmd) {
101255736Sdavidch	case '\0':
102255736Sdavidch		/* Edit empty file. */
103255736Sdavidch		break;
104255736Sdavidch
105255736Sdavidch	case 'b':
106255736Sdavidch		/* Both strings. */
107255736Sdavidch		if (left == NULL)
108255736Sdavidch			goto RIGHT;
109255736Sdavidch		if (right == NULL)
110255736Sdavidch			goto LEFT;
111255736Sdavidch
112255736Sdavidch		/* Neither column is blank, so print both. */
113255736Sdavidch		if (asprintf(&text, "%s\n%s\n", left, right) == -1)
114296071Sdavidcs			err(2, "could not allocate memory");
115296071Sdavidcs		break;
116296071Sdavidcs
117255736Sdavidch	case 'l':
118255736SdavidchLEFT:
119255736Sdavidch		/* Skip if there is no left column. */
120255736Sdavidch		if (left == NULL)
121255736Sdavidch			break;
122255736Sdavidch
123255736Sdavidch		if (asprintf(&text, "%s\n", left) == -1)
124255736Sdavidch			err(2, "could not allocate memory");
125255736Sdavidch
126255736Sdavidch		break;
127255736Sdavidch
128255736Sdavidch	case 'r':
129255736SdavidchRIGHT:
130255736Sdavidch		/* Skip if there is no right column. */
131255736Sdavidch		if (right == NULL)
132255736Sdavidch			break;
133255736Sdavidch
134255736Sdavidch		if (asprintf(&text, "%s\n", right) == -1)
135255736Sdavidch			err(2, "could not allocate memory");
136255736Sdavidch
137255736Sdavidch		break;
138255736Sdavidch
139255736Sdavidch	default:
140255736Sdavidch		return (-1);
141255736Sdavidch	}
142255736Sdavidch
143255736Sdavidch	/* Create temp file. */
144255736Sdavidch	if (asprintf(&filename, "%s/sdiff.XXXXXXXXXX", tmpdir) == -1)
145255736Sdavidch		err(2, "asprintf");
146255736Sdavidch	if ((fd = mkstemp(filename)) == -1)
147255736Sdavidch		err(2, "mkstemp");
148255736Sdavidch	if (text != NULL) {
149255736Sdavidch		size_t len;
150255736Sdavidch		ssize_t nwritten;
151255736Sdavidch
152255736Sdavidch		len = strlen(text);
153255736Sdavidch		if ((nwritten = write(fd, text, len)) == -1 ||
154255736Sdavidch		    (size_t)nwritten != len) {
155255736Sdavidch			warn("error writing to temp file");
156255736Sdavidch			cleanup(filename);
157255736Sdavidch		}
158255736Sdavidch	}
159255736Sdavidch	close(fd);
160255736Sdavidch
161255736Sdavidch	/* text is no longer used. */
162255736Sdavidch	free(text);
163255736Sdavidch
164255736Sdavidch	/* Edit temp file. */
165255736Sdavidch	if (editit(filename) == -1) {
166255736Sdavidch		warn("error editing %s", filename);
167255736Sdavidch		cleanup(filename);
168255736Sdavidch	}
169255736Sdavidch
170255736Sdavidch	/* Open temporary file. */
171321517Sae	if (!(file = fopen(filename, "r"))) {
172255736Sdavidch		warn("could not open edited file: %s", filename);
173255736Sdavidch		cleanup(filename);
174255736Sdavidch	}
175255736Sdavidch
176255736Sdavidch	/* Copy temporary file contents to output file. */
177255736Sdavidch	for (nread = sizeof(buf); nread == sizeof(buf);) {
178255736Sdavidch		size_t nwritten;
179255736Sdavidch
180255736Sdavidch		nread = fread(buf, sizeof(*buf), sizeof(buf), file);
181255736Sdavidch		/* Test for error or end of file. */
182255736Sdavidch		if (nread != sizeof(buf) &&
183255736Sdavidch		    (ferror(file) || !feof(file))) {
184255736Sdavidch			warnx("error reading edited file: %s", filename);
185255736Sdavidch			cleanup(filename);
186255736Sdavidch		}
187255736Sdavidch
188255736Sdavidch		/*
189255736Sdavidch		 * If we have nothing to read, break out of loop
190255736Sdavidch		 * instead of writing nothing.
191255736Sdavidch		 */
192255736Sdavidch		if (!nread)
193255736Sdavidch			break;
194255736Sdavidch
195255736Sdavidch		/* Write data we just read. */
196255736Sdavidch		nwritten = fwrite(buf, sizeof(*buf), nread, outfp);
197255736Sdavidch		if (nwritten != nread) {
198255736Sdavidch			warnx("error writing to output file");
199255736Sdavidch			cleanup(filename);
200255736Sdavidch		}
201255736Sdavidch	}
202255736Sdavidch
203255736Sdavidch	/* We've reached the end of the temporary file, so remove it. */
204255736Sdavidch	if (unlink(filename))
205255736Sdavidch		warn("could not delete: %s", filename);
206255736Sdavidch	fclose(file);
207255736Sdavidch
208255736Sdavidch	free(filename);
209255736Sdavidch
210255736Sdavidch	return (0);
211255736Sdavidch}
212255736Sdavidch