Annotation of src/usr.bin/sdiff/edit.c, Revision 1.6
1.6 ! tedu 1: /* $OpenBSD: edit.c,v 1.5 2005/12/27 04:31:06 tedu Exp $ */
1.1 tedu 2:
3: /*
4: * Written by Raymond Lai <ray@cyth.net>.
5: * Public domain.
6: */
7:
8: #include <sys/types.h>
9: #include <sys/wait.h>
10:
11: #include <assert.h>
12: #include <ctype.h>
13: #include <err.h>
14: #include <stdio.h>
15: #include <stdlib.h>
16: #include <unistd.h>
17:
18: #include "extern.h"
19:
20: __dead static void cleanup(const char *);
21: static void edit(const char *);
22: static char *xmktemp(const char *);
23:
24: static void
25: cleanup(const char *filename)
26: {
27: if (unlink(filename))
28: err(2, "could not delete: %s", filename);
29: exit(2);
30: }
31:
32: /*
33: * Takes the name of a file and opens it with an editor.
34: */
35: static void
36: edit(const char *filename)
37: {
38: int status;
39: pid_t pid;
40: const char *editor;
41:
42: editor = getenv("VISUAL");
43: if (editor == NULL)
44: editor = getenv("EDITOR");
45: if (editor == NULL)
46: editor = "vi";
47:
48: /* Start editor on temporary file. */
49: switch (pid = fork()) {
50: case 0:
51: /* child */
52: execlp(editor, editor, filename, (void *)NULL);
53: warn("could not execute editor: %s", editor);
54: cleanup(filename);
55: /* NOTREACHED */
56: case -1:
57: warn("could not fork");
58: cleanup(filename);
59: /* NOTREACHED */
60: }
61:
62: /* parent */
63: /* Wait for editor to exit. */
64: if (waitpid(pid, &status, 0) == -1) {
65: warn("waitpid");
66: cleanup(filename);
67: /* NOTREACHED */
68: }
69:
70: /* Check that editor terminated normally. */
71: if (!WIFEXITED(status)) {
72: warn("%s terminated abnormally", editor);
73: cleanup(filename);
74: /* NOTREACHED */
75: }
76: }
77:
78: /*
79: * Creates and returns the name of a temporary file. Takes a string
80: * (or NULL) is written to the temporary file. The returned string
81: * needs to be freed.
82: */
83: static char *
84: xmktemp(const char *s)
85: {
86: FILE *file;
87: int fd;
88: const char *tmpdir;
89: char *filename;
90:
91: /* If TMPDIR is set, use it; otherwise use /tmp. */
92: if (!(tmpdir = getenv("TMPDIR")))
93: tmpdir = "/tmp";
94: if (asprintf(&filename, "%s/sdiff.XXXXXXXXXX", tmpdir) == -1)
1.6 ! tedu 95: err(2, "xmktemp");
1.1 tedu 96:
97: /* Create temp file. */
98: if ((fd = mkstemp(filename)) == -1)
99: err(2, "could not create temporary file");
100:
101: /* If we don't write anything to the file, just close. */
102: if (s == NULL) {
1.5 tedu 103: close(fd);
1.1 tedu 104:
105: return (filename);
106: }
107:
108: /* Open temp file for writing. */
109: if ((file = fdopen(fd, "w")) == NULL) {
110: warn("could not open %s", filename);
111: cleanup(filename);
112: /* NOTREACHED */
113: }
114:
115: /* Write to file. */
116: if (fputs(s, file)) {
117: warn("could not write to %s", filename);
118: cleanup(filename);
119: /* NOTREACHED */
120: }
121:
122: /* Close temp file. */
1.5 tedu 123: fclose(file);
1.1 tedu 124:
125: return (filename);
126: }
127:
128: /*
129: * Parse edit command. Returns 0 on success, -1 on error.
130: */
131: int
132: eparse(const char *cmd, const char *left, const char *right)
133: {
134: FILE *file;
135: size_t nread, nwritten;
136: const char *filename;
137: char buf[BUFSIZ], *text;
138:
139: assert(cmd);
140:
141: /* Skip whitespace. */
142: while (isspace(*cmd))
143: ++cmd;
144:
145: text = NULL;
146: switch (*cmd) {
147: case '\0':
148: /* Edit empty file. */
149: break;
150:
151: case 'b':
152: /* Both strings. */
153: if (left == NULL)
154: goto RIGHT;
155: if (right == NULL)
156: goto LEFT;
157:
158: /* Neither column is blank, so print both. */
159: if (asprintf(&text, "%s%s\n", left, right) == -1)
160: err(2, "could not allocate memory");
161: break;
162:
163: LEFT:
164: case 'l':
165: /* Skip if there is no left column. */
166: if (left == NULL)
167: break;
168:
169: if (asprintf(&text, "%s\n", left) == -1)
170: err(2, "could not allocate memory");
171:
172: break;
173:
174: RIGHT:
175: case 'r':
176: /* Skip if there is no right column. */
177: if (right == NULL)
178: break;
179:
180: if (asprintf(&text, "%s\n", right) == -1)
181: err(2, "could not allocate memory");
182:
183: break;
184:
185: default:
186: return (-1);
187: }
188:
189: /* Create temp file. */
190: filename = xmktemp(text);
191:
192: /* text is no longer used. */
193: free(text);
194:
195: /* Edit temp file. */
196: edit(filename);
197:
198: /* Open temporary file. */
199: if (!(file = fopen(filename, "r"))) {
200: warn("could not open edited file: %s", filename);
201: cleanup(filename);
202: /* NOTREACHED */
203: }
204:
205: /* Copy temporary file contents to output file. */
206: for (nread = sizeof(buf); nread == sizeof(buf);) {
207: nread = fread(buf, sizeof(*buf), sizeof(buf), file);
208: /* Test for error or end of file. */
209: if (nread != sizeof(buf) &&
210: (ferror(file) || !feof(file))) {
211: warnx("error reading edited file: %s", filename);
212: cleanup(filename);
213: /* NOTREACHED */
214: }
215:
216: /*
217: * If we have nothing to read, break out of loop
218: * instead of writing nothing.
219: */
220: if (!nread)
221: break;
222:
223: /* Write data we just read. */
224: nwritten = fwrite(buf, sizeof(*buf), nread, outfile);
225: if (nwritten != nread) {
226: warnx("error writing to output file");
227: cleanup(filename);
228: /* NOTREACHED */
229: }
230: }
231:
232: /* We've reached the end of the temporary file, so remove it. */
233: if (unlink(filename))
234: warn("could not delete: %s", filename);
1.5 tedu 235: fclose(file);
1.1 tedu 236:
237: /* filename was malloc()ed in xmktemp(). */
1.4 tedu 238: free((void *)filename);
1.1 tedu 239:
240: return (0);
241: }