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