1 /* $OpenBSD: edit.c,v 1.19 2009/10/27 23:59:40 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 41 int 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 */ 52 int 53 editor(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 */ 63 int 64 visual(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 */ 76 int 77 edit1(int *msgvec, int type) 78 { 79 int 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 if (putc(c, otf) == EOF) 121 break; 122 } 123 if (ferror(otf)) 124 warn("%s", tmpdir); 125 (void)Fclose(fp); 126 } 127 (void)sigprocmask(SIG_SETMASK, &oset, NULL); 128 (void)sigaction(SIGINT, &oact, NULL); 129 } 130 return(0); 131 } 132 133 /* 134 * Run an editor on the file at "fpp" of "size" bytes, 135 * and return a new file pointer. 136 * Signals must be handled by the caller. 137 * "Type" is 'e' for _PATH_EX, 'v' for _PATH_VI. 138 */ 139 FILE * 140 run_editor(FILE *fp, off_t size, int type, int readonly) 141 { 142 FILE *nf = NULL; 143 int t; 144 time_t modtime; 145 char *edit, tempname[PATHSIZE]; 146 struct stat statb; 147 148 (void)snprintf(tempname, sizeof(tempname), 149 "%s/mail.ReXXXXXXXXXX", tmpdir); 150 if ((t = mkstemp(tempname)) == -1 || 151 (nf = Fdopen(t, "w")) == NULL) { 152 warn("%s", tempname); 153 goto out; 154 } 155 if (readonly && fchmod(t, 0400) == -1) { 156 warn("%s", tempname); 157 (void)rm(tempname); 158 goto out; 159 } 160 if (size >= 0) 161 while (--size >= 0 && (t = getc(fp)) != EOF) 162 (void)putc(t, nf); 163 else 164 while ((t = getc(fp)) != EOF) 165 (void)putc(t, nf); 166 (void)fflush(nf); 167 if (fstat(fileno(nf), &statb) < 0) 168 modtime = 0; 169 else 170 modtime = statb.st_mtime; 171 if (ferror(nf)) { 172 (void)Fclose(nf); 173 warn("%s", tempname); 174 (void)rm(tempname); 175 nf = NULL; 176 goto out; 177 } 178 if (Fclose(nf) < 0) { 179 warn("%s", tempname); 180 (void)rm(tempname); 181 nf = NULL; 182 goto out; 183 } 184 nf = NULL; 185 if (type == 'e') { 186 edit = value("EDITOR"); 187 if (edit == NULL || edit[0] == '\0') 188 edit = _PATH_EX; 189 } else { 190 edit = value("VISUAL"); 191 if (edit == NULL || edit[0] == '\0') 192 edit = _PATH_VI; 193 } 194 if (editit(edit, tempname) == -1) { 195 (void)rm(tempname); 196 goto out; 197 } 198 /* 199 * If in read only mode or file unchanged, just remove the editor 200 * temporary and return. 201 */ 202 if (readonly) { 203 (void)rm(tempname); 204 goto out; 205 } 206 if (stat(tempname, &statb) < 0) { 207 warn("%s", tempname); 208 goto out; 209 } 210 if (modtime == statb.st_mtime) { 211 (void)rm(tempname); 212 goto out; 213 } 214 /* 215 * Now switch to new file. 216 */ 217 if ((nf = Fopen(tempname, "a+")) == NULL) { 218 warn("%s", tempname); 219 (void)rm(tempname); 220 goto out; 221 } 222 (void)rm(tempname); 223 out: 224 return(nf); 225 } 226 227 /* 228 * Execute an editor on the specified pathname, which is interpreted 229 * from the shell. This means flags may be included. 230 * 231 * Returns -1 on error, or the exit value on success. 232 */ 233 int 234 editit(const char *ed, const char *pathname) 235 { 236 char *argp[] = {"sh", "-c", NULL, NULL}, *p; 237 sig_t sighup, sigint, sigquit, sigchld; 238 pid_t pid; 239 int saved_errno, st, ret = -1; 240 241 if (ed == NULL) 242 ed = getenv("VISUAL"); 243 if (ed == NULL || ed[0] == '\0') 244 ed = getenv("EDITOR"); 245 if (ed == NULL || ed[0] == '\0') 246 ed = _PATH_VI; 247 if (asprintf(&p, "%s %s", ed, pathname) == -1) 248 return (-1); 249 argp[2] = p; 250 251 sighup = signal(SIGHUP, SIG_IGN); 252 sigint = signal(SIGINT, SIG_IGN); 253 sigquit = signal(SIGQUIT, SIG_IGN); 254 sigchld = signal(SIGCHLD, SIG_DFL); 255 if ((pid = fork()) == -1) 256 goto fail; 257 if (pid == 0) { 258 execv(_PATH_BSHELL, argp); 259 _exit(127); 260 } 261 while (waitpid(pid, &st, 0) == -1) 262 if (errno != EINTR) 263 goto fail; 264 if (!WIFEXITED(st)) 265 errno = EINTR; 266 else 267 ret = WEXITSTATUS(st); 268 269 fail: 270 saved_errno = errno; 271 (void)signal(SIGHUP, sighup); 272 (void)signal(SIGINT, sigint); 273 (void)signal(SIGQUIT, sigquit); 274 (void)signal(SIGCHLD, sigchld); 275 free(p); 276 errno = saved_errno; 277 return (ret); 278 } 279