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 char *argp[] = {"sh", "-c", NULL, NULL}, *ed, *p; 39 sig_t sighup, sigint, sigquit, sigchld; 40 pid_t pid; 41 int saved_errno, st, ret = -1; 42 43 ed = getenv("VISUAL"); 44 if (ed == NULL || ed[0] == '\0') 45 ed = getenv("EDITOR"); 46 if (ed == NULL || ed[0] == '\0') 47 ed = _PATH_VI; 48 if (asprintf(&p, "%s %s", ed, pathname) == -1) 49 return (-1); 50 argp[2] = p; 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 execv(_PATH_BSHELL, argp); 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 free(p); 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 } 210