xref: /netbsd-src/usr.bin/sdiff/edit.c (revision 3b6b08b599974eb758be548fcb1beb970687fbfc)
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