]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/sdiff/edit.c
zfs: merge openzfs/zfs@a03ebd9be
[FreeBSD/FreeBSD.git] / usr.bin / sdiff / edit.c
1 /*      $OpenBSD: edit.c,v 1.19 2009/06/07 13:29:50 ray Exp $ */
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 <errno.h>
14 #include <paths.h>
15 #include <signal.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <unistd.h>
20
21 #include "extern.h"
22
23 static void
24 cleanup(const char *filename)
25 {
26
27         if (unlink(filename))
28                 err(2, "could not delete: %s", filename);
29         exit(2);
30 }
31
32 /*
33  * Execute an editor on the specified pathname, which is interpreted
34  * from the shell.  This means flags may be included.
35  *
36  * Returns -1 on error, or the exit value on success.
37  */
38 static int
39 editit(const char *pathname)
40 {
41         sig_t sighup, sigint, sigquit, sigchld;
42         pid_t pid;
43         int saved_errno, st, ret = -1;
44         const char *ed;
45
46         ed = getenv("VISUAL");
47         if (ed == NULL)
48                 ed = getenv("EDITOR");
49         if (ed == NULL)
50                 ed = _PATH_VI;
51
52         sighup = signal(SIGHUP, SIG_IGN);
53         sigint = signal(SIGINT, SIG_IGN);
54         sigquit = signal(SIGQUIT, SIG_IGN);
55         sigchld = signal(SIGCHLD, SIG_DFL);
56         if ((pid = fork()) == -1)
57                 goto fail;
58         if (pid == 0) {
59                 execlp(ed, ed, pathname, (char *)NULL);
60                 _exit(127);
61         }
62         while (waitpid(pid, &st, 0) == -1)
63                 if (errno != EINTR)
64                         goto fail;
65         if (!WIFEXITED(st))
66                 errno = EINTR;
67         else
68                 ret = WEXITSTATUS(st);
69
70  fail:
71         saved_errno = errno;
72         (void)signal(SIGHUP, sighup);
73         (void)signal(SIGINT, sigint);
74         (void)signal(SIGQUIT, sigquit);
75         (void)signal(SIGCHLD, sigchld);
76         errno = saved_errno;
77         return (ret);
78 }
79
80 /*
81  * Parse edit command.  Returns 0 on success, -1 on error.
82  */
83 int
84 eparse(const char *cmd, const char *left, const char *right)
85 {
86         FILE *file;
87         size_t nread;
88         int fd;
89         char *filename;
90         char buf[BUFSIZ], *text;
91
92         /* Skip whitespace. */
93         while (isspace(*cmd))
94                 ++cmd;
95
96         text = NULL;
97         switch (*cmd) {
98         case '\0':
99                 /* Edit empty file. */
100                 break;
101
102         case 'b':
103                 /* Both strings. */
104                 if (left == NULL)
105                         goto RIGHT;
106                 if (right == NULL)
107                         goto LEFT;
108
109                 /* Neither column is blank, so print both. */
110                 if (asprintf(&text, "%s\n%s\n", left, right) == -1)
111                         err(2, "could not allocate memory");
112                 break;
113
114         case 'l':
115 LEFT:
116                 /* Skip if there is no left column. */
117                 if (left == NULL)
118                         break;
119
120                 if (asprintf(&text, "%s\n", left) == -1)
121                         err(2, "could not allocate memory");
122
123                 break;
124
125         case 'r':
126 RIGHT:
127                 /* Skip if there is no right column. */
128                 if (right == NULL)
129                         break;
130
131                 if (asprintf(&text, "%s\n", right) == -1)
132                         err(2, "could not allocate memory");
133
134                 break;
135
136         default:
137                 return (-1);
138         }
139
140         /* Create temp file. */
141         if (asprintf(&filename, "%s/sdiff.XXXXXXXXXX", tmpdir) == -1)
142                 err(2, "asprintf");
143         if ((fd = mkstemp(filename)) == -1)
144                 err(2, "mkstemp");
145         if (text != NULL) {
146                 size_t len;
147                 ssize_t nwritten;
148
149                 len = strlen(text);
150                 if ((nwritten = write(fd, text, len)) == -1 ||
151                     (size_t)nwritten != len) {
152                         warn("error writing to temp file");
153                         cleanup(filename);
154                 }
155         }
156         close(fd);
157
158         /* text is no longer used. */
159         free(text);
160
161         /* Edit temp file. */
162         if (editit(filename) == -1) {
163                 warn("error editing %s", filename);
164                 cleanup(filename);
165         }
166
167         /* Open temporary file. */
168         if (!(file = fopen(filename, "r"))) {
169                 warn("could not open edited file: %s", filename);
170                 cleanup(filename);
171         }
172
173         /* Copy temporary file contents to output file. */
174         for (nread = sizeof(buf); nread == sizeof(buf);) {
175                 size_t nwritten;
176
177                 nread = fread(buf, sizeof(*buf), sizeof(buf), file);
178                 /* Test for error or end of file. */
179                 if (nread != sizeof(buf) &&
180                     (ferror(file) || !feof(file))) {
181                         warnx("error reading edited file: %s", filename);
182                         cleanup(filename);
183                 }
184
185                 /*
186                  * If we have nothing to read, break out of loop
187                  * instead of writing nothing.
188                  */
189                 if (!nread)
190                         break;
191
192                 /* Write data we just read. */
193                 nwritten = fwrite(buf, sizeof(*buf), nread, outfp);
194                 if (nwritten != nread) {
195                         warnx("error writing to output file");
196                         cleanup(filename);
197                 }
198         }
199
200         /* We've reached the end of the temporary file, so remove it. */
201         if (unlink(filename))
202                 warn("could not delete: %s", filename);
203         fclose(file);
204
205         free(filename);
206
207         return (0);
208 }