xref: /netbsd-src/usr.bin/sdiff/edit.c (revision b5677b36047b601b9addaaa494a58ceae82c2a6c)
1 /*	$NetBSD: edit.c,v 1.1 2007/02/18 22:13:42 rmind 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
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
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((int)(*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 ((nwritten = write(fd, text, len)) == -1 ||
136 		    nwritten != len) {
137 			warn("error writing to temp file");
138 			cleanup(filename);
139 		}
140 	}
141 	close(fd);
142 
143 	/* text is no longer used. */
144 	free(text);
145 
146 	/* Edit temp file. */
147 	edit(filename);
148 
149 	/* Open temporary file. */
150 	if (!(file = fopen(filename, "r"))) {
151 		warn("could not open edited file: %s", filename);
152 		cleanup(filename);
153 	}
154 
155 	/* Copy temporary file contents to output file. */
156 	for (nread = sizeof(buf); nread == sizeof(buf);) {
157 		nread = fread(buf, sizeof(*buf), sizeof(buf), file);
158 		/* Test for error or end of file. */
159 		if (nread != sizeof(buf) &&
160 		    (ferror(file) || !feof(file))) {
161 			warnx("error reading edited file: %s", filename);
162 			cleanup(filename);
163 		}
164 
165 		/*
166 		 * If we have nothing to read, break out of loop
167 		 * instead of writing nothing.
168 		 */
169 		if (!nread)
170 			break;
171 
172 		/* Write data we just read. */
173 		nwritten = fwrite(buf, sizeof(*buf), nread, outfile);
174 		if (nwritten != nread) {
175 			warnx("error writing to output file");
176 			cleanup(filename);
177 		}
178 	}
179 
180 	/* We've reached the end of the temporary file, so remove it. */
181 	if (unlink(filename))
182 		warn("could not delete: %s", filename);
183 	fclose(file);
184 
185 	free(filename);
186 
187 	return (0);
188 }
189