Add count_nonlocal()
[s-mailx.git] / quit.c
blob98afd556677fd7c18c9b007010c22d8cc1ad56ca
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Termination processing.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2014 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
6 */
7 /*
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
40 #ifndef HAVE_AMALGAMATION
41 # include "nail.h"
42 #endif
44 #include <fcntl.h>
45 #include <utime.h>
47 enum quitflags {
48 QUITFLAG_HOLD = 001,
49 QUITFLAG_KEEPSAVE = 002,
50 QUITFLAG_APPEND = 004,
51 QUITFLAG_EMPTYBOX = 010
54 struct quitnames {
55 enum quitflags flag;
56 enum okeys okey;
59 static struct quitnames const _quitnames[] = {
60 {QUITFLAG_HOLD, ok_b_hold},
61 {QUITFLAG_KEEPSAVE, ok_b_keepsave},
62 {QUITFLAG_APPEND, ok_b_append},
63 {QUITFLAG_EMPTYBOX, ok_b_emptybox}
66 static char _mboxname[PATH_MAX]; /* Name of mbox */
68 /* Touch the indicated file */
69 static void _alter(char const *name);
71 /* Preserve all the appropriate messages back in the system mailbox, and print
72 * a nice message indicated how many were saved. On any error, just return -1.
73 * Else return 0. Incorporate the any new mail that we found */
74 static int writeback(FILE *res, FILE *obuf);
76 /* Terminate an editing session by attempting to write out the user's file from
77 * the temporary. Save any new stuff appended to the file */
78 static void edstop(void);
80 static void
81 _alter(char const *name)
83 struct stat sb;
84 struct utimbuf utb;
85 NYD_ENTER;
87 if (!stat(name, &sb)) {
88 utb.actime = time(NULL) + 1;
89 utb.modtime = sb.st_mtime;
90 utime(name, &utb);
92 NYD_LEAVE;
95 static int
96 writeback(FILE *res, FILE *obuf)
98 struct message *mp;
99 int rv = -1, p, c;
100 NYD_ENTER;
102 if (fseek(obuf, 0L, SEEK_SET) == -1)
103 goto jleave;
105 #ifndef APPEND
106 if (res != NULL)
107 while ((c = getc(res)) != EOF)
108 putc(c, obuf);
109 #endif
110 srelax_hold();
111 for (p = 0, mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
112 if ((mp->m_flag & MPRESERVE) || !(mp->m_flag & MTOUCH)) {
113 ++p;
114 if (sendmp(mp, obuf, NULL, NULL, SEND_MBOX, NULL) < 0) {
115 srelax_rele();
116 goto jerror;
118 srelax();
120 srelax_rele();
121 #ifdef APPEND
122 if (res != NULL)
123 while ((c = getc(res)) != EOF)
124 putc(c, obuf);
125 #endif
126 ftrunc(obuf);
128 if (ferror(obuf)) {
129 jerror:
130 perror(mailname);
131 fseek(obuf, 0L, SEEK_SET);
132 goto jleave;
134 if (fseek(obuf, 0L, SEEK_SET) == -1)
135 goto jleave;
137 _alter(mailname);
138 if (p == 1)
139 printf(tr(155, "Held 1 message in %s\n"), displayname);
140 else
141 printf(tr(156, "Held %d messages in %s\n"), p, displayname);
142 rv = 0;
143 jleave:
144 if (res != NULL)
145 Fclose(res);
146 NYD_LEAVE;
147 return rv;
150 static void
151 edstop(void) /* TODO oh my god - and REMOVE that CRAPPY reset(0) jump!! */
153 int gotcha, c;
154 struct message *mp;
155 FILE *obuf = NULL, *ibuf = NULL;
156 struct stat statb;
157 bool_t doreset;
158 NYD_ENTER;
160 hold_sigs();
161 doreset = FAL0;
163 if (mb.mb_perm == 0)
164 goto jleave;
166 for (mp = message, gotcha = 0; PTRCMP(mp, <, message + msgCount); ++mp) {
167 if (mp->m_flag & MNEW) {
168 mp->m_flag &= ~MNEW;
169 mp->m_flag |= MSTATUS;
171 if (mp->m_flag & (MODIFY | MDELETED | MSTATUS | MFLAG | MUNFLAG |
172 MANSWER | MUNANSWER | MDRAFT | MUNDRAFT))
173 ++gotcha;
175 if (!gotcha)
176 goto jleave;
178 doreset = TRU1;
180 if (!stat(mailname, &statb) && statb.st_size > mailsize) {
181 if ((obuf = Ftmp(NULL, "edstop", OF_RDWR | OF_UNLINK | OF_REGISTER,
182 0600)) == NULL) {
183 perror(tr(167, "tmpfile"));
184 goto jleave;
186 if ((ibuf = Zopen(mailname, "r", &mb.mb_compressed)) == NULL) {
187 perror(mailname);
188 Fclose(obuf);
189 goto jleave;
191 fseek(ibuf, (long)mailsize, SEEK_SET);
192 while ((c = getc(ibuf)) != EOF)
193 putc(c, obuf);
194 Fclose(ibuf);
195 ibuf = obuf;
196 fflush_rewind(obuf);
199 printf(tr(168, "\"%s\" "), displayname);
200 fflush(stdout);
201 if ((obuf = Zopen(mailname, "r+", &mb.mb_compressed)) == NULL) {
202 perror(mailname);
203 goto jleave;
205 ftrunc(obuf);
207 srelax_hold();
208 c = 0;
209 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp) {
210 if (mp->m_flag & MDELETED)
211 continue;
212 ++c;
213 if (sendmp(mp, obuf, NULL, NULL, SEND_MBOX, NULL) < 0) {
214 perror(mailname);
215 srelax_rele();
216 goto jleave;
218 srelax();
220 srelax_rele();
222 gotcha = (c == 0 && ibuf == NULL);
223 if (ibuf != NULL) {
224 while ((c = getc(ibuf)) != EOF)
225 putc(c, obuf);
227 fflush(obuf);
228 if (ferror(obuf)) {
229 perror(mailname);
230 goto jleave;
232 Fclose(obuf);
234 doreset = FAL0;
236 if (gotcha && !ok_blook(emptybox)) {
237 rm(mailname);
238 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
239 ? tr(169, "removed\n") : tr(211, "removed.\n"));
240 } else
241 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
242 ? tr(170, "complete\n") : tr(212, "updated.\n"));
243 fflush(stdout);
244 jleave:
245 if (ibuf != NULL)
246 Fclose(ibuf);
247 rele_sigs();
248 NYD_LEAVE;
249 if (doreset)
250 reset(0);
253 FL int
254 c_quit(void *v)
256 int rv;
257 NYD_ENTER;
258 UNUSED(v);
260 /* If we are sourcing, then return 1 so evaluate() can handle it.
261 * Otherwise return -1 to abort command loop */
262 rv = sourcing ? 1 : -1;
263 NYD_LEAVE;
264 return rv;
267 FL void
268 quit(void)
270 int p, modify, anystat, c;
271 FILE *fbuf = NULL, *rbuf, *abuf;
272 struct message *mp;
273 char *tempResid;
274 struct stat minfo;
275 NYD_ENTER;
277 /* If we are read only, we can't do anything, so just return quickly. IMAP
278 * can set some flags (e.g. "\\Seen") so imap_quit must be called anyway */
279 if (mb.mb_perm == 0 && mb.mb_type != MB_IMAP)
280 goto jleave;
282 /* TODO lex.c:setfile() has just called hold_sigs(); before it called
283 * TODO us, but this causes uninterruptible hangs due to blocked sigs
284 * TODO anywhere except for MB_FILE (all others install their own
285 * TODO handlers, as it seems, properly); marked YYY */
286 switch (mb.mb_type) {
287 case MB_FILE:
288 break;
289 case MB_MAILDIR:
290 rele_sigs(); /* YYY */
291 maildir_quit();
292 hold_sigs(); /* YYY */
293 goto jleave;
294 #ifdef HAVE_POP3
295 case MB_POP3:
296 rele_sigs(); /* YYY */
297 pop3_quit();
298 hold_sigs(); /* YYY */
299 goto jleave;
300 #endif
301 #ifdef HAVE_IMAP
302 case MB_IMAP:
303 case MB_CACHE:
304 rele_sigs(); /* YYY */
305 imap_quit();
306 hold_sigs(); /* YYY */
307 goto jleave;
308 #endif
309 case MB_VOID:
310 default:
311 goto jleave;
314 /* If editing (not reading system mail box), then do the work in edstop() */
315 if (edit) {
316 edstop();
317 goto jleave;
320 /* See if there any messages to save in mbox. If no, we
321 * can save copying mbox to /tmp and back.
323 * Check also to see if any files need to be preserved.
324 * Delete all untouched messages to keep them out of mbox.
325 * If all the messages are to be preserved, just exit with
326 * a message */
327 fbuf = Zopen(mailname, "r+", &mb.mb_compressed);
328 if (fbuf == NULL) {
329 if (errno == ENOENT)
330 goto jleave;
331 jnewmail:
332 printf(tr(166, "Thou hast new mail.\n"));
333 goto jleave;
336 if (fcntl_lock(fileno(fbuf), FLOCK_WRITE) == -1 ||
337 dot_lock(mailname, fileno(fbuf), 1, stdout, ".") == -1) {
338 perror(tr(157, "Unable to lock mailbox"));
339 Fclose(fbuf);
340 fbuf = NULL;
341 goto jleave;
344 rbuf = NULL;
345 if (!fstat(fileno(fbuf), &minfo) && minfo.st_size > mailsize) {
346 printf(tr(158, "New mail has arrived.\n"));
347 rbuf = Ftmp(&tempResid, "quit", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600);
348 if (rbuf == NULL || fbuf == NULL)
349 goto jnewmail;
350 #ifdef APPEND
351 fseek(fbuf, (long)mailsize, SEEK_SET);
352 while ((c = getc(fbuf)) != EOF)
353 putc(c, rbuf);
354 #else
355 p = minfo.st_size - mailsize;
356 while (p-- > 0) {
357 c = getc(fbuf);
358 if (c == EOF)
359 goto jnewmail;
360 putc(c, rbuf);
362 #endif
363 fflush_rewind(rbuf);
366 anystat = holdbits();
367 modify = 0;
368 for (c = 0, p = 0, mp = message; PTRCMP(mp, <, message + msgCount); ++mp) {
369 if (mp->m_flag & MBOX)
370 c++;
371 if (mp->m_flag & MPRESERVE)
372 p++;
373 if (mp->m_flag & MODIFY)
374 modify++;
376 if (p == msgCount && !modify && !anystat) {
377 if (p == 1)
378 printf(tr(155, "Held 1 message in %s\n"), displayname);
379 else if (p > 1)
380 printf(tr(156, "Held %d messages in %s\n"), p, displayname);
381 goto jleave;
383 if (c == 0) {
384 if (p != 0) {
385 writeback(rbuf, fbuf);
386 goto jleave;
388 goto jcream;
391 if (makembox() == STOP)
392 goto jleave;
394 /* Now we are ready to copy back preserved files to the system mailbox, if
395 * any were requested */
396 if (p != 0) {
397 writeback(rbuf, fbuf);
398 goto jleave;
401 /* Finally, remove his file. If new mail has arrived, copy it back */
402 jcream:
403 if (rbuf != NULL) {
404 abuf = fbuf;
405 fseek(abuf, 0L, SEEK_SET);
406 while ((c = getc(rbuf)) != EOF)
407 putc(c, abuf);
408 Fclose(rbuf);
409 ftrunc(abuf);
410 _alter(mailname);
411 goto jleave;
413 demail();
414 jleave:
415 if (fbuf != NULL) {
416 Fclose(fbuf);
417 dot_unlock(mailname);
419 NYD_LEAVE;
422 FL int
423 holdbits(void)
425 struct message *mp;
426 int anystat, autohold, holdbit, nohold;
427 NYD_ENTER;
429 anystat = 0;
430 autohold = ok_blook(hold);
431 holdbit = autohold ? MPRESERVE : MBOX;
432 nohold = MBOX | MSAVED | MDELETED | MPRESERVE;
433 if (ok_blook(keepsave))
434 nohold &= ~MSAVED;
435 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp) {
436 if (mp->m_flag & MNEW) {
437 mp->m_flag &= ~MNEW;
438 mp->m_flag |= MSTATUS;
440 if (mp->m_flag & (MSTATUS | MFLAG | MUNFLAG | MANSWER | MUNANSWER |
441 MDRAFT | MUNDRAFT))
442 ++anystat;
443 if (!(mp->m_flag & MTOUCH))
444 mp->m_flag |= MPRESERVE;
445 if (!(mp->m_flag & nohold))
446 mp->m_flag |= holdbit;
448 NYD_LEAVE;
449 return anystat;
452 FL enum okay
453 makembox(void) /* TODO oh my god */
455 struct message *mp;
456 char *mbox, *tempQuit;
457 int mcount, c;
458 FILE *ibuf = NULL, *obuf, *abuf;
459 enum protocol prot;
460 enum okay rv = STOP;
461 NYD_ENTER;
463 mbox = _mboxname;
464 mcount = 0;
465 if (!ok_blook(append)) {
466 if ((obuf = Ftmp(&tempQuit, "makembox",
467 OF_WRONLY | OF_HOLDSIGS | OF_REGISTER, 0600)) == NULL) {
468 perror(tr(163, "temporary mail quit file"));
469 goto jleave;
471 if ((ibuf = Fopen(tempQuit, "r")) == NULL)
472 perror(tempQuit);
473 Ftmp_release(&tempQuit);
474 if (ibuf == NULL) {
475 Fclose(obuf);
476 goto jleave;
479 if ((abuf = Zopen(mbox, "r", NULL)) != NULL) {
480 while ((c = getc(abuf)) != EOF)
481 putc(c, obuf);
482 Fclose(abuf);
484 if (ferror(obuf)) {
485 perror(tr(163, "temporary mail quit file"));
486 Fclose(ibuf);
487 Fclose(obuf);
488 goto jleave;
490 Fclose(obuf);
492 if ((c = open(mbox, O_CREAT | O_TRUNC | O_WRONLY, 0600)) != -1)
493 close(c);
494 if ((obuf = Zopen(mbox, "r+", NULL)) == NULL) {
495 perror(mbox);
496 Fclose(ibuf);
497 goto jleave;
499 } else {
500 if ((obuf = Zopen(mbox, "a", NULL)) == NULL) {
501 perror(mbox);
502 goto jleave;
504 fchmod(fileno(obuf), 0600);
507 srelax_hold();
508 prot = which_protocol(mbox);
509 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp) {
510 if (mp->m_flag & MBOX) {
511 ++mcount;
512 if (prot == PROTO_IMAP &&
513 saveignore[0].i_count == 0 && saveignore[1].i_count == 0
514 #ifdef HAVE_IMAP /* TODO revisit */
515 && imap_thisaccount(mbox)
516 #endif
518 #ifdef HAVE_IMAP
519 if (imap_copy(mp, PTR2SIZE(mp - message + 1), mbox) == STOP)
520 #endif
521 goto jerr;
522 } else if (sendmp(mp, obuf, saveignore, NULL, SEND_MBOX, NULL) < 0) {
523 perror(mbox);
524 jerr:
525 if (ibuf != NULL)
526 Fclose(ibuf);
527 Fclose(obuf);
528 srelax_rele();
529 goto jleave;
531 mp->m_flag |= MBOXED;
532 srelax();
535 srelax_rele();
537 /* Copy the user's old mbox contents back to the end of the stuff we just
538 * saved. If we are appending, this is unnecessary */
539 if (!ok_blook(append)) {
540 rewind(ibuf);
541 c = getc(ibuf);
542 while (c != EOF) {
543 putc(c, obuf);
544 if (ferror(obuf))
545 break;
546 c = getc(ibuf);
548 Fclose(ibuf);
549 fflush(obuf);
551 ftrunc(obuf);
552 if (ferror(obuf)) {
553 perror(mbox);
554 Fclose(obuf);
555 goto jleave;
557 if (Fclose(obuf) != 0) {
558 if (prot != PROTO_IMAP)
559 perror(mbox);
560 goto jleave;
562 if (mcount == 1)
563 printf(tr(164, "Saved 1 message in mbox\n"));
564 else
565 printf(tr(165, "Saved %d messages in mbox\n"), mcount);
566 rv = OKAY;
567 jleave:
568 NYD_LEAVE;
569 return rv;
572 FL void
573 save_mbox_for_possible_quitstuff(void) /* TODO try to get rid of that */
575 char const *cp;
576 NYD_ENTER;
578 if ((cp = expand("&")) == NULL)
579 cp = "";
580 n_strlcpy(_mboxname, cp, sizeof _mboxname);
581 NYD_LEAVE;
584 FL int
585 savequitflags(void)
587 enum quitflags qf = 0;
588 size_t i;
589 NYD_ENTER;
591 for (i = 0; i < NELEM(_quitnames); ++i)
592 if (_var_oklook(_quitnames[i].okey) != NULL)
593 qf |= _quitnames[i].flag;
594 NYD_LEAVE;
595 return qf;
598 FL void
599 restorequitflags(int qf)
601 size_t i;
602 NYD_ENTER;
604 for (i = 0; i < NELEM(_quitnames); ++i) {
605 char *x = _var_oklook(_quitnames[i].okey);
606 if (qf & _quitnames[i].flag) {
607 if (x == NULL)
608 _var_okset(_quitnames[i].okey, TRU1);
609 } else if (x != NULL)
610 _var_okclear(_quitnames[i].okey);
612 NYD_LEAVE;
615 /* vim:set fenc=utf-8:s-it-mode */