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