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