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 } 209