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