1*ce982eb7SThomas Cort /* $NetBSD: edit.c,v 1.4 2011/09/01 07:18:51 plunky Exp $ */
2*ce982eb7SThomas Cort /* $OpenBSD: edit.c,v 1.14 2006/05/25 03:20:32 ray Exp $ */
3*ce982eb7SThomas Cort
4*ce982eb7SThomas Cort /*
5*ce982eb7SThomas Cort * Written by Raymond Lai <ray@cyth.net>.
6*ce982eb7SThomas Cort * Public domain.
7*ce982eb7SThomas Cort */
8*ce982eb7SThomas Cort
9*ce982eb7SThomas Cort #include <sys/types.h>
10*ce982eb7SThomas Cort #include <sys/wait.h>
11*ce982eb7SThomas Cort
12*ce982eb7SThomas Cort #include <ctype.h>
13*ce982eb7SThomas Cort #include <err.h>
14*ce982eb7SThomas Cort #include <stdio.h>
15*ce982eb7SThomas Cort #include <stdlib.h>
16*ce982eb7SThomas Cort #include <string.h>
17*ce982eb7SThomas Cort #include <unistd.h>
18*ce982eb7SThomas Cort
19*ce982eb7SThomas Cort #include "common.h"
20*ce982eb7SThomas Cort #include "extern.h"
21*ce982eb7SThomas Cort
22*ce982eb7SThomas Cort static void edit(const char *);
23*ce982eb7SThomas Cort
24*ce982eb7SThomas Cort /*
25*ce982eb7SThomas Cort * Takes the name of a file and opens it with an editor.
26*ce982eb7SThomas Cort */
27*ce982eb7SThomas Cort static void
edit(const char * filename)28*ce982eb7SThomas Cort edit(const char *filename)
29*ce982eb7SThomas Cort {
30*ce982eb7SThomas Cort int status;
31*ce982eb7SThomas Cort pid_t pid;
32*ce982eb7SThomas Cort const char *editor;
33*ce982eb7SThomas Cort
34*ce982eb7SThomas Cort editor = getenv("VISUAL");
35*ce982eb7SThomas Cort if (editor == NULL)
36*ce982eb7SThomas Cort editor = getenv("EDITOR");
37*ce982eb7SThomas Cort if (editor == NULL)
38*ce982eb7SThomas Cort editor = "vi";
39*ce982eb7SThomas Cort
40*ce982eb7SThomas Cort /* Start editor on temporary file. */
41*ce982eb7SThomas Cort switch (pid = fork()) {
42*ce982eb7SThomas Cort case 0:
43*ce982eb7SThomas Cort /* child */
44*ce982eb7SThomas Cort execlp(editor, editor, filename, (void *)NULL);
45*ce982eb7SThomas Cort warn("could not execute editor: %s", editor);
46*ce982eb7SThomas Cort cleanup(filename);
47*ce982eb7SThomas Cort case -1:
48*ce982eb7SThomas Cort warn("could not fork");
49*ce982eb7SThomas Cort cleanup(filename);
50*ce982eb7SThomas Cort }
51*ce982eb7SThomas Cort
52*ce982eb7SThomas Cort /* parent */
53*ce982eb7SThomas Cort /* Wait for editor to exit. */
54*ce982eb7SThomas Cort if (waitpid(pid, &status, 0) == -1) {
55*ce982eb7SThomas Cort warn("waitpid");
56*ce982eb7SThomas Cort cleanup(filename);
57*ce982eb7SThomas Cort }
58*ce982eb7SThomas Cort
59*ce982eb7SThomas Cort /* Check that editor terminated normally. */
60*ce982eb7SThomas Cort if (!WIFEXITED(status)) {
61*ce982eb7SThomas Cort warn("%s terminated abnormally", editor);
62*ce982eb7SThomas Cort cleanup(filename);
63*ce982eb7SThomas Cort }
64*ce982eb7SThomas Cort }
65*ce982eb7SThomas Cort
66*ce982eb7SThomas Cort /*
67*ce982eb7SThomas Cort * Parse edit command. Returns 0 on success, -1 on error.
68*ce982eb7SThomas Cort */
69*ce982eb7SThomas Cort int
eparse(const char * cmd,const char * left,const char * right)70*ce982eb7SThomas Cort eparse(const char *cmd, const char *left, const char *right)
71*ce982eb7SThomas Cort {
72*ce982eb7SThomas Cort FILE *file;
73*ce982eb7SThomas Cort size_t nread, nwritten;
74*ce982eb7SThomas Cort int fd;
75*ce982eb7SThomas Cort char *filename;
76*ce982eb7SThomas Cort char buf[BUFSIZ], *text;
77*ce982eb7SThomas Cort
78*ce982eb7SThomas Cort /* Skip whitespace. */
79*ce982eb7SThomas Cort while (isspace((int)(*cmd)))
80*ce982eb7SThomas Cort ++cmd;
81*ce982eb7SThomas Cort
82*ce982eb7SThomas Cort text = NULL;
83*ce982eb7SThomas Cort switch (*cmd) {
84*ce982eb7SThomas Cort case '\0':
85*ce982eb7SThomas Cort /* Edit empty file. */
86*ce982eb7SThomas Cort break;
87*ce982eb7SThomas Cort
88*ce982eb7SThomas Cort case 'b':
89*ce982eb7SThomas Cort /* Both strings. */
90*ce982eb7SThomas Cort if (left == NULL)
91*ce982eb7SThomas Cort goto RIGHT;
92*ce982eb7SThomas Cort if (right == NULL)
93*ce982eb7SThomas Cort goto LEFT;
94*ce982eb7SThomas Cort
95*ce982eb7SThomas Cort /* Neither column is blank, so print both. */
96*ce982eb7SThomas Cort if (asprintf(&text, "%s\n%s\n", left, right) == -1)
97*ce982eb7SThomas Cort err(2, "could not allocate memory");
98*ce982eb7SThomas Cort break;
99*ce982eb7SThomas Cort
100*ce982eb7SThomas Cort case 'l':
101*ce982eb7SThomas Cort LEFT:
102*ce982eb7SThomas Cort /* Skip if there is no left column. */
103*ce982eb7SThomas Cort if (left == NULL)
104*ce982eb7SThomas Cort break;
105*ce982eb7SThomas Cort
106*ce982eb7SThomas Cort if (asprintf(&text, "%s\n", left) == -1)
107*ce982eb7SThomas Cort err(2, "could not allocate memory");
108*ce982eb7SThomas Cort
109*ce982eb7SThomas Cort break;
110*ce982eb7SThomas Cort
111*ce982eb7SThomas Cort case 'r':
112*ce982eb7SThomas Cort RIGHT:
113*ce982eb7SThomas Cort /* Skip if there is no right column. */
114*ce982eb7SThomas Cort if (right == NULL)
115*ce982eb7SThomas Cort break;
116*ce982eb7SThomas Cort
117*ce982eb7SThomas Cort if (asprintf(&text, "%s\n", right) == -1)
118*ce982eb7SThomas Cort err(2, "could not allocate memory");
119*ce982eb7SThomas Cort
120*ce982eb7SThomas Cort break;
121*ce982eb7SThomas Cort
122*ce982eb7SThomas Cort default:
123*ce982eb7SThomas Cort return (-1);
124*ce982eb7SThomas Cort }
125*ce982eb7SThomas Cort
126*ce982eb7SThomas Cort /* Create temp file. */
127*ce982eb7SThomas Cort if (asprintf(&filename, "%s/sdiff.XXXXXXXXXX", tmpdir) == -1)
128*ce982eb7SThomas Cort err(2, "asprintf");
129*ce982eb7SThomas Cort if ((fd = mkstemp(filename)) == -1)
130*ce982eb7SThomas Cort err(2, "mkstemp");
131*ce982eb7SThomas Cort if (text != NULL) {
132*ce982eb7SThomas Cort size_t len;
133*ce982eb7SThomas Cort
134*ce982eb7SThomas Cort len = strlen(text);
135*ce982eb7SThomas Cort if ((size_t)write(fd, text, len) != len) {
136*ce982eb7SThomas Cort warn("error writing to temp file");
137*ce982eb7SThomas Cort cleanup(filename);
138*ce982eb7SThomas Cort }
139*ce982eb7SThomas Cort }
140*ce982eb7SThomas Cort close(fd);
141*ce982eb7SThomas Cort
142*ce982eb7SThomas Cort /* text is no longer used. */
143*ce982eb7SThomas Cort free(text);
144*ce982eb7SThomas Cort
145*ce982eb7SThomas Cort /* Edit temp file. */
146*ce982eb7SThomas Cort edit(filename);
147*ce982eb7SThomas Cort
148*ce982eb7SThomas Cort /* Open temporary file. */
149*ce982eb7SThomas Cort if (!(file = fopen(filename, "r"))) {
150*ce982eb7SThomas Cort warn("could not open edited file: %s", filename);
151*ce982eb7SThomas Cort cleanup(filename);
152*ce982eb7SThomas Cort }
153*ce982eb7SThomas Cort
154*ce982eb7SThomas Cort /* Copy temporary file contents to output file. */
155*ce982eb7SThomas Cort for (nread = sizeof(buf); nread == sizeof(buf);) {
156*ce982eb7SThomas Cort nread = fread(buf, sizeof(*buf), sizeof(buf), file);
157*ce982eb7SThomas Cort /* Test for error or end of file. */
158*ce982eb7SThomas Cort if (nread != sizeof(buf) &&
159*ce982eb7SThomas Cort (ferror(file) || !feof(file))) {
160*ce982eb7SThomas Cort warnx("error reading edited file: %s", filename);
161*ce982eb7SThomas Cort cleanup(filename);
162*ce982eb7SThomas Cort }
163*ce982eb7SThomas Cort
164*ce982eb7SThomas Cort /*
165*ce982eb7SThomas Cort * If we have nothing to read, break out of loop
166*ce982eb7SThomas Cort * instead of writing nothing.
167*ce982eb7SThomas Cort */
168*ce982eb7SThomas Cort if (!nread)
169*ce982eb7SThomas Cort break;
170*ce982eb7SThomas Cort
171*ce982eb7SThomas Cort /* Write data we just read. */
172*ce982eb7SThomas Cort nwritten = fwrite(buf, sizeof(*buf), nread, outfile);
173*ce982eb7SThomas Cort if (nwritten != nread) {
174*ce982eb7SThomas Cort warnx("error writing to output file");
175*ce982eb7SThomas Cort cleanup(filename);
176*ce982eb7SThomas Cort }
177*ce982eb7SThomas Cort }
178*ce982eb7SThomas Cort
179*ce982eb7SThomas Cort /* We've reached the end of the temporary file, so remove it. */
180*ce982eb7SThomas Cort if (unlink(filename))
181*ce982eb7SThomas Cort warn("could not delete: %s", filename);
182*ce982eb7SThomas Cort fclose(file);
183*ce982eb7SThomas Cort
184*ce982eb7SThomas Cort free(filename);
185*ce982eb7SThomas Cort
186*ce982eb7SThomas Cort return (0);
187*ce982eb7SThomas Cort }
188