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