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