1 /* $OpenBSD: edit.c,v 1.18 2008/07/16 14:49:09 martynas 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 #ifndef lint 34 #if 0 35 static const char sccsid[] = "@(#)edit.c 8.1 (Berkeley) 6/6/93"; 36 #else 37 static const char rcsid[] = "$OpenBSD: edit.c,v 1.18 2008/07/16 14:49:09 martynas Exp $"; 38 #endif 39 #endif /* not lint */ 40 41 #include <sys/types.h> 42 #include <sys/wait.h> 43 44 #include "rcv.h" 45 #include <errno.h> 46 #include <fcntl.h> 47 #include "extern.h" 48 49 int editit(const char *, const char *); 50 51 /* 52 * Mail -- a mail program 53 * 54 * Perform message editing functions. 55 */ 56 57 /* 58 * Edit a message list. 59 */ 60 int 61 editor(void *v) 62 { 63 int *msgvec = v; 64 65 return(edit1(msgvec, 'e')); 66 } 67 68 /* 69 * Invoke the visual editor on a message list. 70 */ 71 int 72 visual(void *v) 73 { 74 int *msgvec = v; 75 76 return(edit1(msgvec, 'v')); 77 } 78 79 /* 80 * Edit a message by writing the message into a funnily-named file 81 * (which should not exist) and forking an editor on it. 82 * We get the editor from the stuff above. 83 */ 84 int 85 edit1(int *msgvec, int type) 86 { 87 int c, i; 88 FILE *fp; 89 struct sigaction oact; 90 sigset_t oset; 91 struct message *mp; 92 off_t size; 93 94 /* 95 * Deal with each message to be edited . . . 96 */ 97 for (i = 0; msgvec[i] && i < msgCount; i++) { 98 if (i > 0) { 99 char buf[100]; 100 char *p; 101 102 printf("Edit message %d [ynq]? ", msgvec[i]); 103 if (fgets(buf, sizeof(buf), stdin) == NULL) 104 break; 105 for (p = buf; *p == ' ' || *p == '\t'; p++) 106 ; 107 if (*p == 'q') 108 break; 109 if (*p == 'n') 110 continue; 111 } 112 dot = mp = &message[msgvec[i] - 1]; 113 touch(mp); 114 (void)ignoresig(SIGINT, &oact, &oset); 115 fp = run_editor(setinput(mp), (off_t)mp->m_size, type, readonly); 116 if (fp != NULL) { 117 (void)fseek(otf, 0L, SEEK_END); 118 size = ftell(otf); 119 mp->m_block = blockof(size); 120 mp->m_offset = offsetof(size); 121 mp->m_size = fsize(fp); 122 mp->m_lines = 0; 123 mp->m_flag |= MODIFY; 124 rewind(fp); 125 while ((c = getc(fp)) != EOF) { 126 if (c == '\n') 127 mp->m_lines++; 128 if (putc(c, otf) == EOF) 129 break; 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 */ 147 FILE * 148 run_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) < 0) 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) < 0) { 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); 231 out: 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 */ 241 int 242 editit(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