1/* $OpenBSD: edit.c,v 1.21 2019/06/28 13:35:01 deraadt Exp $ */ 2/* $NetBSD: edit.c,v 1.5 1996/06/08 19:48:20 christos Exp $ */ 3 4/* 5 * Copyright (c) 1980, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33#include <sys/types.h> 34#include <sys/wait.h> 35 36#include "rcv.h" 37#include <errno.h> 38#include <fcntl.h> 39#include "extern.h" 40 41int editit(const char *, const char *); 42 43/* 44 * Mail -- a mail program 45 * 46 * Perform message editing functions. 47 */ 48 49/* 50 * Edit a message list. 51 */ 52int 53editor(void *v) 54{ 55 int *msgvec = v; 56 57 return(edit1(msgvec, 'e')); 58} 59 60/* 61 * Invoke the visual editor on a message list. 62 */ 63int 64visual(void *v) 65{ 66 int *msgvec = v; 67 68 return(edit1(msgvec, 'v')); 69} 70 71/* 72 * Edit a message by writing the message into a funnily-named file 73 * (which should not exist) and forking an editor on it. 74 * We get the editor from the stuff above. 75 */ 76int 77edit1(int *msgvec, int type) 78{ 79 int nl = 0, c, i; 80 FILE *fp; 81 struct sigaction oact; 82 sigset_t oset; 83 struct message *mp; 84 off_t size; 85 86 /* 87 * Deal with each message to be edited . . . 88 */ 89 for (i = 0; msgvec[i] && i < msgCount; i++) { 90 if (i > 0) { 91 char buf[100]; 92 char *p; 93 94 printf("Edit message %d [ynq]? ", msgvec[i]); 95 if (fgets(buf, sizeof(buf), stdin) == NULL) 96 break; 97 for (p = buf; *p == ' ' || *p == '\t'; p++) 98 ; 99 if (*p == 'q') 100 break; 101 if (*p == 'n') 102 continue; 103 } 104 dot = mp = &message[msgvec[i] - 1]; 105 touch(mp); 106 (void)ignoresig(SIGINT, &oact, &oset); 107 fp = run_editor(setinput(mp), (off_t)mp->m_size, type, readonly); 108 if (fp != NULL) { 109 (void)fseek(otf, 0L, SEEK_END); 110 size = ftell(otf); 111 mp->m_block = blockof(size); 112 mp->m_offset = offsetof(size); 113 mp->m_size = fsize(fp); 114 mp->m_lines = 0; 115 mp->m_flag |= MODIFY; 116 rewind(fp); 117 while ((c = getc(fp)) != EOF) { 118 if (c == '\n') { 119 mp->m_lines++; 120 nl++; 121 } else 122 nl = 0; 123 if (putc(c, otf) == EOF) 124 break; 125 } 126 for (; nl < 2; nl++) { 127 mp->m_lines++; 128 mp->m_size++; 129 putc('\n', otf); 130 } 131 if (ferror(otf)) 132 warn("%s", tmpdir); 133 (void)Fclose(fp); 134 } 135 (void)sigprocmask(SIG_SETMASK, &oset, NULL); 136 (void)sigaction(SIGINT, &oact, NULL); 137 } 138 return(0); 139} 140 141/* 142 * Run an editor on the file at "fpp" of "size" bytes, 143 * and return a new file pointer. 144 * Signals must be handled by the caller. 145 * "Type" is 'e' for _PATH_EX, 'v' for _PATH_VI. 146 */ 147FILE * 148run_editor(FILE *fp, off_t size, int type, int readonly) 149{ 150 FILE *nf = NULL; 151 int t; 152 time_t modtime; 153 char *edit, tempname[PATHSIZE]; 154 struct stat statb; 155 156 (void)snprintf(tempname, sizeof(tempname), 157 "%s/mail.ReXXXXXXXXXX", tmpdir); 158 if ((t = mkstemp(tempname)) == -1 || 159 (nf = Fdopen(t, "w")) == NULL) { 160 warn("%s", tempname); 161 goto out; 162 } 163 if (readonly && fchmod(t, 0400) == -1) { 164 warn("%s", tempname); 165 (void)rm(tempname); 166 goto out; 167 } 168 if (size >= 0) 169 while (--size >= 0 && (t = getc(fp)) != EOF) 170 (void)putc(t, nf); 171 else 172 while ((t = getc(fp)) != EOF) 173 (void)putc(t, nf); 174 (void)fflush(nf); 175 if (fstat(fileno(nf), &statb) == -1) 176 modtime = 0; 177 else 178 modtime = statb.st_mtime; 179 if (ferror(nf)) { 180 (void)Fclose(nf); 181 warn("%s", tempname); 182 (void)rm(tempname); 183 nf = NULL; 184 goto out; 185 } 186 if (Fclose(nf) < 0) { 187 warn("%s", tempname); 188 (void)rm(tempname); 189 nf = NULL; 190 goto out; 191 } 192 nf = NULL; 193 if (type == 'e') { 194 edit = value("EDITOR"); 195 if (edit == NULL || edit[0] == '\0') 196 edit = _PATH_EX; 197 } else { 198 edit = value("VISUAL"); 199 if (edit == NULL || edit[0] == '\0') 200 edit = _PATH_VI; 201 } 202 if (editit(edit, tempname) == -1) { 203 (void)rm(tempname); 204 goto out; 205 } 206 /* 207 * If in read only mode or file unchanged, just remove the editor 208 * temporary and return. 209 */ 210 if (readonly) { 211 (void)rm(tempname); 212 goto out; 213 } 214 if (stat(tempname, &statb) == -1) { 215 warn("%s", tempname); 216 goto out; 217 } 218 if (modtime == statb.st_mtime) { 219 (void)rm(tempname); 220 goto out; 221 } 222 /* 223 * Now switch to new file. 224 */ 225 if ((nf = Fopen(tempname, "a+")) == NULL) { 226 warn("%s", tempname); 227 (void)rm(tempname); 228 goto out; 229 } 230 (void)rm(tempname); 231out: 232 return(nf); 233} 234 235/* 236 * Execute an editor on the specified pathname, which is interpreted 237 * from the shell. This means flags may be included. 238 * 239 * Returns -1 on error, or the exit value on success. 240 */ 241int 242editit(const char *ed, const char *pathname) 243{ 244 char *argp[] = {"sh", "-c", NULL, NULL}, *p; 245 sig_t sighup, sigint, sigquit, sigchld; 246 pid_t pid; 247 int saved_errno, st, ret = -1; 248 249 if (ed == NULL) 250 ed = getenv("VISUAL"); 251 if (ed == NULL || ed[0] == '\0') 252 ed = getenv("EDITOR"); 253 if (ed == NULL || ed[0] == '\0') 254 ed = _PATH_VI; 255 if (asprintf(&p, "%s %s", ed, pathname) == -1) 256 return (-1); 257 argp[2] = p; 258 259 sighup = signal(SIGHUP, SIG_IGN); 260 sigint = signal(SIGINT, SIG_IGN); 261 sigquit = signal(SIGQUIT, SIG_IGN); 262 sigchld = signal(SIGCHLD, SIG_DFL); 263 if ((pid = fork()) == -1) 264 goto fail; 265 if (pid == 0) { 266 execv(_PATH_BSHELL, argp); 267 _exit(127); 268 } 269 while (waitpid(pid, &st, 0) == -1) 270 if (errno != EINTR) 271 goto fail; 272 if (!WIFEXITED(st)) 273 errno = EINTR; 274 else 275 ret = WEXITSTATUS(st); 276 277 fail: 278 saved_errno = errno; 279 (void)signal(SIGHUP, sighup); 280 (void)signal(SIGINT, sigint); 281 (void)signal(SIGQUIT, sigquit); 282 (void)signal(SIGCHLD, sigchld); 283 free(p); 284 errno = saved_errno; 285 return (ret); 286} 287