/***********************************************************************1* *2* This software is part of the BSD package *3*Copyright (c) 1978-2010 The Regents of the University of California an*4* *5* Redistribution and use in source and binary forms, with or *6* without modification, are permitted provided that the following *7* conditions are met: *8* *9* 1. Redistributions of source code must retain the above *10* copyright notice, this list of conditions and the *11* following disclaimer. *12* *13* 2. Redistributions in binary form must reproduce the above *14* copyright notice, this list of conditions and the *15* following disclaimer in the documentation and/or other *16* materials provided with the distribution. *17* *18* 3. Neither the name of The Regents of the University of California*19* names of its contributors may be used to endorse or *20* promote products derived from this software without *21* specific prior written permission. *22* *23* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND *24* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, *25* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *26* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *27* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS *28* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *29* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED *30* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, *31* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON *32* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, *33* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY *34* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE *35* POSSIBILITY OF SUCH DAMAGE. *36* *37* Redistribution and use in source and binary forms, with or without *38* modification, are permitted provided that the following conditions *39* are met: *40* 1. Redistributions of source code must retain the above copyright *41* notice, this list of conditions and the following disclaimer. *42* 2. Redistributions in binary form must reproduce the above copyright *43* notice, this list of conditions and the following disclaimer in *44* the documentation and/or other materials provided with the *45* distribution. *46* 3. Neither the name of the University nor the names of its *47* contributors may be used to endorse or promote products derived *48* from this software without specific prior written permission. *49* *50* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" *51* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *52* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *53* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS *54* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, *55* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT *56* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF *57* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND *58* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, *59* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT *60* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF *61* SUCH DAMAGE. *62* *63* Kurt Shoens (UCB) *64* gsf *65* *66***********************************************************************/67#pragma prototyped68/*69* Mail -- a mail program70*71* Perform message editing functions.72*/7374#include "mailx.h"7576/*77* Edit a message list.78*/79static int80edit1(struct msg* msgvec, int type)81{82register struct msg* ip;83register struct msg* mp;84FILE* fp;85off_t size;86sig_t sigint;8788/*89* Deal with each message to be edited . . .90*/91for (ip = msgvec; ip->m_index; ip++) {92if (ip > msgvec) {93char buf[100];94char* p;9596note(PROMPT, "Edit message %d [ynq]? ", ip->m_index);97if (!fgets(buf, sizeof buf, stdin))98break;99for (p = buf; isspace(*p); p++);100if (*p == 'q' || *p == 'Q')101break;102if (*p == 'n' || *p == 'N')103continue;104}105state.msg.dot = mp = state.msg.list + ip->m_index - 1;106touchmsg(mp);107sigint = signal(SIGINT, SIG_IGN);108if (fp = run_editor(setinput(mp), mp->m_size, NiL, type, state.readonly)) {109if (!state.msg.op)110settmp(NiL, 0);111fseek(state.msg.op, (off_t)0, SEEK_END);112size = ftell(state.msg.op);113mp->m_block = blocknumber(size);114mp->m_offset = blockoffset(size);115msgflags(mp, MODIFY, 0);116rewind(fp);117filecopy(NiL, fp, state.tmp.dir, state.msg.op, NiL, (off_t)0, &mp->m_lines, &mp->m_size, 0);118fileclose(fp);119}120signal(SIGINT, sigint);121}122return 0;123}124125/*126* Edit a message list.127*/128int129editor(struct msg* msgvec)130{131return edit1(msgvec, 'e');132}133134/*135* Invoke the visual editor on a message list.136*/137int138visual(struct msg* msgvec)139{140return edit1(msgvec, 'v');141}142143/*144* Run an editor on the file at "fp" of "size" bytes,145* and return a new file pointer.146* Signals must be handled by the caller.147* "Type" is 'e' for state.var.editor, 'v' for state.var.visual.148*/149FILE*150run_editor(register FILE* fp, off_t size, struct header* hp, int type, int readonly)151{152FILE* ep;153time_t modtime;154int lc;155int err;156char* edit;157unsigned long editheaders = 0;158struct parse pp;159160/*161* Create and copy to the temporary file.162*/163if (!(ep = fileopen(state.tmp.edit, "EMw")))164goto ret1;165if (size) {166if (size < 0)167size = 0;168if (hp && state.var.editheaders) {169editheaders = GEDIT|GRULE;170headout(ep, hp, editheaders|GNL);171}172if (filecopy(NiL, fp, state.tmp.edit, ep, NiL, size, NiL, NiL, 0))173goto ret2;174}175modtime = state.openstat.st_mtime;176fileclose(ep);177ep = 0;178/*179* Edit the file.180*/181edit = type == 'e' ? state.var.editor : state.var.visual;182err = run_command(edit, 0, -1, -1, state.tmp.edit, NiL, NiL) < 0;183/*184* If in readonly mode or file unchanged, clean up and return.185*/186if (readonly)187goto ret2;188if (stat(state.tmp.edit, &state.openstat) < 0)189goto ret1;190if (modtime == state.openstat.st_mtime)191goto ret2;192if (err)193note(0, "%s did not exit normally but did do some changes -- you may want to re-edit", edit);194/*195* Now, switch to the temporary file.196*/197if (!(ep = fileopen(state.tmp.edit, "Ea+")))198goto ret2;199if (editheaders && headset(&pp, NiL, ep, hp, NiL, editheaders|GTO|GMETOO)) {200while (headget(&pp));201remove(state.tmp.edit);202if (!(ep = fileopen(state.tmp.edit, "EMa+")))203goto ret1;204filecopy(NiL, pp.fp, state.tmp.edit, ep, NiL, (off_t)0, NiL, NiL, 0);205fileclose(pp.fp);206}207/*208* Ensure that the tempEdit file ends with two newlines.209*210* XXX211* Probably ought to have a `From' line, as well.212*213* XXX214* If the file is only a single byte long, the seek is going to215* fail, but I'm not sure we care. In the case of any error, we216* pass back the file descriptor -- if we fail because the disk217* is too full, reads should continue to work and I see no reason218* to discard the user's work.219*/220if (fseek(ep, (off_t)-2, SEEK_END) < 0)221return ep;222lc = getc(ep) == '\n' ? 1 : 0;223if (getc(ep) == '\n')224++lc;225else226lc = 0;227228switch (lc) {229case 0:230if (putc('\n', ep) == EOF)231break;232/* FALLTHROUGH */233case 1:234putc('\n', ep);235/* FALLTHROUGH */236case 2:237break;238default:239abort();240break;241}242/*243* XXX: fflush() is necessary, so future stat(2) succeeds.244*/245fflush(ep);246remove(state.tmp.edit);247return ep;248249ret1: note(SYSTEM, "%s", state.tmp.edit);250ret2: remove(state.tmp.edit);251if (ep)252fileclose(ep);253return 0;254}255256257