Annotation of src/usr.bin/sdiff/edit.c, Revision 1.3
1.3 ! tedu 1: /* $OpenBSD: edit.c,v 1.2 2005/12/27 04:06:16 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)
95: err(2, "could not allocate memory");
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) {
103: if (close(fd)) {
104: warn("could not close %s", filename);
105: cleanup(filename);
106: /* NOTREACHED */
107: }
108:
109: return (filename);
110: }
111:
112: /* Open temp file for writing. */
113: if ((file = fdopen(fd, "w")) == NULL) {
114: warn("could not open %s", filename);
115: cleanup(filename);
116: /* NOTREACHED */
117: }
118:
119: /* Write to file. */
120: if (fputs(s, file)) {
121: warn("could not write to %s", filename);
122: cleanup(filename);
123: /* NOTREACHED */
124: }
125:
126: /* Close temp file. */
127: if (fclose(file)) {
128: warn("could not close %s", filename);
129: cleanup(filename);
130: /* NOTREACHED */
131: }
132:
133: return (filename);
134: }
135:
136: /*
137: * Parse edit command. Returns 0 on success, -1 on error.
138: */
139: int
140: eparse(const char *cmd, const char *left, const char *right)
141: {
142: FILE *file;
143: size_t nread, nwritten;
144: const char *filename;
145: char buf[BUFSIZ], *text;
146:
147: assert(cmd);
148:
149: /* Skip whitespace. */
150: while (isspace(*cmd))
151: ++cmd;
152:
153: text = NULL;
154: switch (*cmd) {
155: case '\0':
156: /* Edit empty file. */
157: break;
158:
159: case 'b':
160: /* Both strings. */
161: if (left == NULL)
162: goto RIGHT;
163: if (right == NULL)
164: goto LEFT;
165:
166: /* Neither column is blank, so print both. */
167: if (asprintf(&text, "%s%s\n", left, right) == -1)
168: err(2, "could not allocate memory");
169: break;
170:
171: LEFT:
172: case 'l':
173: /* Skip if there is no left column. */
174: if (left == NULL)
175: break;
176:
177: if (asprintf(&text, "%s\n", left) == -1)
178: err(2, "could not allocate memory");
179:
180: break;
181:
182: RIGHT:
183: case 'r':
184: /* Skip if there is no right column. */
185: if (right == NULL)
186: break;
187:
188: if (asprintf(&text, "%s\n", right) == -1)
189: err(2, "could not allocate memory");
190:
191: break;
192:
193: default:
194: return (-1);
195: }
196:
197: /* Create temp file. */
198: filename = xmktemp(text);
199:
200: /* text is no longer used. */
201: free(text);
202:
203: /* Edit temp file. */
204: edit(filename);
205:
206: /* Open temporary file. */
207: if (!(file = fopen(filename, "r"))) {
208: warn("could not open edited file: %s", filename);
209: cleanup(filename);
210: /* NOTREACHED */
211: }
212:
213: /* Copy temporary file contents to output file. */
214: for (nread = sizeof(buf); nread == sizeof(buf);) {
215: nread = fread(buf, sizeof(*buf), sizeof(buf), file);
216: /* Test for error or end of file. */
217: if (nread != sizeof(buf) &&
218: (ferror(file) || !feof(file))) {
219: warnx("error reading edited file: %s", filename);
220: cleanup(filename);
221: /* NOTREACHED */
222: }
223:
224: /*
225: * If we have nothing to read, break out of loop
226: * instead of writing nothing.
227: */
228: if (!nread)
229: break;
230:
231: /* Write data we just read. */
232: nwritten = fwrite(buf, sizeof(*buf), nread, outfile);
233: if (nwritten != nread) {
234: warnx("error writing to output file");
235: cleanup(filename);
236: /* NOTREACHED */
237: }
238: }
239:
240: /* We've reached the end of the temporary file, so remove it. */
241: if (unlink(filename))
242: warn("could not delete: %s", filename);
243: if (fclose(file))
244: warn("could not close: %s", filename);
245:
246: /* filename was malloc()ed in xmktemp(). */
1.3 ! tedu 247: free(filename);
1.1 tedu 248:
249: return (0);
250: }