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