xref: /freebsd/usr.bin/sdiff/edit.c (revision 430f7286a566b1407c7b32ce13585caf5aa59b92)
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 	char *argp[] = {"sh", "-c", NULL, NULL}, *ed, *p;
39 	sig_t sighup, sigint, sigquit, sigchld;
40 	pid_t pid;
41 	int saved_errno, st, ret = -1;
42 
43 	ed = getenv("VISUAL");
44 	if (ed == NULL || ed[0] == '\0')
45 		ed = getenv("EDITOR");
46 	if (ed == NULL || ed[0] == '\0')
47 		ed = _PATH_VI;
48 	if (asprintf(&p, "%s %s", ed, pathname) == -1)
49 		return (-1);
50 	argp[2] = p;
51 
52 	sighup = signal(SIGHUP, SIG_IGN);
53 	sigint = signal(SIGINT, SIG_IGN);
54 	sigquit = signal(SIGQUIT, SIG_IGN);
55 	sigchld = signal(SIGCHLD, SIG_DFL);
56 	if ((pid = fork()) == -1)
57 		goto fail;
58 	if (pid == 0) {
59 		execv(_PATH_BSHELL, argp);
60 		_exit(127);
61 	}
62 	while (waitpid(pid, &st, 0) == -1)
63 		if (errno != EINTR)
64 			goto fail;
65 	if (!WIFEXITED(st))
66 		errno = EINTR;
67 	else
68 		ret = WEXITSTATUS(st);
69 
70  fail:
71 	saved_errno = errno;
72 	(void)signal(SIGHUP, sighup);
73 	(void)signal(SIGINT, sigint);
74 	(void)signal(SIGQUIT, sigquit);
75 	(void)signal(SIGCHLD, sigchld);
76 	free(p);
77 	errno = saved_errno;
78 	return (ret);
79 }
80 
81 /*
82  * Parse edit command.  Returns 0 on success, -1 on error.
83  */
84 int
85 eparse(const char *cmd, const char *left, const char *right)
86 {
87 	FILE *file;
88 	size_t nread;
89 	int fd;
90 	char *filename;
91 	char buf[BUFSIZ], *text;
92 
93 	/* Skip whitespace. */
94 	while (isspace(*cmd))
95 		++cmd;
96 
97 	text = NULL;
98 	switch (*cmd) {
99 	case '\0':
100 		/* Edit empty file. */
101 		break;
102 
103 	case 'b':
104 		/* Both strings. */
105 		if (left == NULL)
106 			goto RIGHT;
107 		if (right == NULL)
108 			goto LEFT;
109 
110 		/* Neither column is blank, so print both. */
111 		if (asprintf(&text, "%s\n%s\n", left, right) == -1)
112 			err(2, "could not allocate memory");
113 		break;
114 
115 	case 'l':
116 LEFT:
117 		/* Skip if there is no left column. */
118 		if (left == NULL)
119 			break;
120 
121 		if (asprintf(&text, "%s\n", left) == -1)
122 			err(2, "could not allocate memory");
123 
124 		break;
125 
126 	case 'r':
127 RIGHT:
128 		/* Skip if there is no right column. */
129 		if (right == NULL)
130 			break;
131 
132 		if (asprintf(&text, "%s\n", right) == -1)
133 			err(2, "could not allocate memory");
134 
135 		break;
136 
137 	default:
138 		return (-1);
139 	}
140 
141 	/* Create temp file. */
142 	if (asprintf(&filename, "%s/sdiff.XXXXXXXXXX", tmpdir) == -1)
143 		err(2, "asprintf");
144 	if ((fd = mkstemp(filename)) == -1)
145 		err(2, "mkstemp");
146 	if (text != NULL) {
147 		size_t len;
148 		ssize_t nwritten;
149 
150 		len = strlen(text);
151 		if ((nwritten = write(fd, text, len)) == -1 ||
152 		    (size_t)nwritten != len) {
153 			warn("error writing to temp file");
154 			cleanup(filename);
155 		}
156 	}
157 	close(fd);
158 
159 	/* text is no longer used. */
160 	free(text);
161 
162 	/* Edit temp file. */
163 	if (editit(filename) == -1) {
164 		warn("error editing %s", filename);
165 		cleanup(filename);
166 	}
167 
168 	/* Open temporary file. */
169 	if (!(file = fopen(filename, "r"))) {
170 		warn("could not open edited file: %s", filename);
171 		cleanup(filename);
172 	}
173 
174 	/* Copy temporary file contents to output file. */
175 	for (nread = sizeof(buf); nread == sizeof(buf);) {
176 		size_t nwritten;
177 
178 		nread = fread(buf, sizeof(*buf), sizeof(buf), file);
179 		/* Test for error or end of file. */
180 		if (nread != sizeof(buf) &&
181 		    (ferror(file) || !feof(file))) {
182 			warnx("error reading edited file: %s", filename);
183 			cleanup(filename);
184 		}
185 
186 		/*
187 		 * If we have nothing to read, break out of loop
188 		 * instead of writing nothing.
189 		 */
190 		if (!nread)
191 			break;
192 
193 		/* Write data we just read. */
194 		nwritten = fwrite(buf, sizeof(*buf), nread, outfp);
195 		if (nwritten != nread) {
196 			warnx("error writing to output file");
197 			cleanup(filename);
198 		}
199 	}
200 
201 	/* We've reached the end of the temporary file, so remove it. */
202 	if (unlink(filename))
203 		warn("could not delete: %s", filename);
204 	fclose(file);
205 
206 	free(filename);
207 
208 	return (0);
209 }
210