Annotation of src/usr.bin/mail/edit.c, Revision 1.21
1.21 ! deraadt 1: /* $OpenBSD: edit.c,v 1.20 2017/03/28 09:14:43 natano Exp $ */
1.2 deraadt 2: /* $NetBSD: edit.c,v 1.5 1996/06/08 19:48:20 christos Exp $ */
3:
1.1 deraadt 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.
1.12 millert 16: * 3. Neither the name of the University nor the names of its contributors
1.1 deraadt 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:
1.15 ray 33: #include <sys/types.h>
34: #include <sys/wait.h>
35:
1.1 deraadt 36: #include "rcv.h"
1.15 ray 37: #include <errno.h>
1.1 deraadt 38: #include <fcntl.h>
39: #include "extern.h"
40:
1.15 ray 41: int editit(const char *, const char *);
42:
1.1 deraadt 43: /*
44: * Mail -- a mail program
45: *
46: * Perform message editing functions.
47: */
48:
49: /*
50: * Edit a message list.
51: */
52: int
1.11 millert 53: editor(void *v)
1.1 deraadt 54: {
1.2 deraadt 55: int *msgvec = v;
1.1 deraadt 56:
1.3 millert 57: return(edit1(msgvec, 'e'));
1.1 deraadt 58: }
59:
60: /*
61: * Invoke the visual editor on a message list.
62: */
63: int
1.11 millert 64: visual(void *v)
1.1 deraadt 65: {
1.2 deraadt 66: int *msgvec = v;
1.1 deraadt 67:
1.3 millert 68: return(edit1(msgvec, 'v'));
1.1 deraadt 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
1.11 millert 77: edit1(int *msgvec, int type)
1.1 deraadt 78: {
1.20 natano 79: int nl = 0, c, i;
1.1 deraadt 80: FILE *fp;
1.10 millert 81: struct sigaction oact;
82: sigset_t oset;
1.7 millert 83: struct message *mp;
1.1 deraadt 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]);
1.14 cloder 95: if (fgets(buf, sizeof(buf), stdin) == NULL)
1.1 deraadt 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);
1.10 millert 106: (void)ignoresig(SIGINT, &oact, &oset);
1.13 deraadt 107: fp = run_editor(setinput(mp), (off_t)mp->m_size, type, readonly);
1.1 deraadt 108: if (fp != NULL) {
1.16 tobias 109: (void)fseek(otf, 0L, SEEK_END);
1.1 deraadt 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) {
1.20 natano 118: if (c == '\n') {
1.1 deraadt 119: mp->m_lines++;
1.20 natano 120: nl++;
121: } else
122: nl = 0;
1.1 deraadt 123: if (putc(c, otf) == EOF)
124: break;
1.20 natano 125: }
126: for (; nl < 2; nl++) {
127: mp->m_lines++;
128: mp->m_size++;
129: putc('\n', otf);
1.1 deraadt 130: }
131: if (ferror(otf))
1.18 martynas 132: warn("%s", tmpdir);
1.3 millert 133: (void)Fclose(fp);
1.1 deraadt 134: }
1.10 millert 135: (void)sigprocmask(SIG_SETMASK, &oset, NULL);
136: (void)sigaction(SIGINT, &oact, NULL);
1.1 deraadt 137: }
1.3 millert 138: return(0);
1.1 deraadt 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 *
1.11 millert 148: run_editor(FILE *fp, off_t size, int type, int readonly)
1.1 deraadt 149: {
1.7 millert 150: FILE *nf = NULL;
151: int t;
1.1 deraadt 152: time_t modtime;
1.6 millert 153: char *edit, tempname[PATHSIZE];
1.1 deraadt 154: struct stat statb;
155:
1.6 millert 156: (void)snprintf(tempname, sizeof(tempname),
157: "%s/mail.ReXXXXXXXXXX", tmpdir);
158: if ((t = mkstemp(tempname)) == -1 ||
159: (nf = Fdopen(t, "w")) == NULL) {
1.8 millert 160: warn("%s", tempname);
1.1 deraadt 161: goto out;
162: }
1.6 millert 163: if (readonly && fchmod(t, 0400) == -1) {
1.8 millert 164: warn("%s", tempname);
1.6 millert 165: (void)rm(tempname);
1.1 deraadt 166: goto out;
167: }
168: if (size >= 0)
169: while (--size >= 0 && (t = getc(fp)) != EOF)
1.4 millert 170: (void)putc(t, nf);
1.1 deraadt 171: else
172: while ((t = getc(fp)) != EOF)
1.4 millert 173: (void)putc(t, nf);
174: (void)fflush(nf);
1.21 ! deraadt 175: if (fstat(fileno(nf), &statb) == -1)
1.1 deraadt 176: modtime = 0;
177: else
178: modtime = statb.st_mtime;
179: if (ferror(nf)) {
1.4 millert 180: (void)Fclose(nf);
1.8 millert 181: warn("%s", tempname);
1.6 millert 182: (void)rm(tempname);
1.1 deraadt 183: nf = NULL;
184: goto out;
185: }
186: if (Fclose(nf) < 0) {
1.8 millert 187: warn("%s", tempname);
1.6 millert 188: (void)rm(tempname);
1.1 deraadt 189: nf = NULL;
190: goto out;
191: }
192: nf = NULL;
1.15 ray 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) {
1.6 millert 203: (void)rm(tempname);
1.1 deraadt 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) {
1.6 millert 211: (void)rm(tempname);
1.1 deraadt 212: goto out;
213: }
1.21 ! deraadt 214: if (stat(tempname, &statb) == -1) {
1.8 millert 215: warn("%s", tempname);
1.1 deraadt 216: goto out;
217: }
218: if (modtime == statb.st_mtime) {
1.6 millert 219: (void)rm(tempname);
1.1 deraadt 220: goto out;
221: }
222: /*
223: * Now switch to new file.
224: */
1.6 millert 225: if ((nf = Fopen(tempname, "a+")) == NULL) {
1.8 millert 226: warn("%s", tempname);
1.6 millert 227: (void)rm(tempname);
1.1 deraadt 228: goto out;
229: }
1.6 millert 230: (void)rm(tempname);
1.1 deraadt 231: out:
1.3 millert 232: return(nf);
1.15 ray 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;
1.17 deraadt 245: sig_t sighup, sigint, sigquit, sigchld;
1.15 ray 246: pid_t pid;
1.17 deraadt 247: int saved_errno, st, ret = -1;
1.15 ray 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);
1.17 deraadt 262: sigchld = signal(SIGCHLD, SIG_DFL);
1.15 ray 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;
1.17 deraadt 272: if (!WIFEXITED(st))
1.15 ray 273: errno = EINTR;
1.17 deraadt 274: else
275: ret = WEXITSTATUS(st);
1.15 ray 276:
277: fail:
278: saved_errno = errno;
279: (void)signal(SIGHUP, sighup);
280: (void)signal(SIGINT, sigint);
281: (void)signal(SIGQUIT, sigquit);
1.17 deraadt 282: (void)signal(SIGCHLD, sigchld);
1.15 ray 283: free(p);
284: errno = saved_errno;
1.17 deraadt 285: return (ret);
1.1 deraadt 286: }