hammer2 - Refactor reserved block selection in freemap code (2)
[dragonfly.git] / usr.bin / mail / quit.c
1 /*
2  * Copyright (c) 1980, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * @(#)quit.c   8.2 (Berkeley) 4/28/95
30  * $FreeBSD: src/usr.bin/mail/quit.c,v 1.2.6.3 2003/01/06 05:46:03 mikeh Exp $
31  * $DragonFly: src/usr.bin/mail/quit.c,v 1.5 2004/09/08 03:01:11 joerg Exp $
32  */
33
34 #include "rcv.h"
35 #include <fcntl.h>
36 #include "extern.h"
37
38 static void     edstop(void);
39 static int      writeback(FILE *res);
40
41 /*
42  * Rcv -- receive mail rationally.
43  *
44  * Termination processing.
45  */
46
47 /*
48  * The "quit" command.
49  */
50 int
51 quitcmd(void)
52 {
53         /*
54          * If we are sourcing, then return 1 so execute() can handle it.
55          * Otherwise, return -1 to abort command loop.
56          */
57         if (sourcing)
58                 return (1);
59         return (-1);
60 }
61
62 /*
63  * Save all of the undetermined messages at the top of "mbox"
64  * Save all untouched messages back in the system mailbox.
65  * Remove the system mailbox, if none saved there.
66  */
67 void
68 quit(void)
69 {
70         int mcount, p, modify, autohold, anystat, holdbit, nohold;
71         FILE *ibuf, *obuf, *fbuf, *rbuf, *readstat, *abuf;
72         struct message *mp;
73         int c, fd;
74         struct stat minfo;
75         char *mbox, tempname[PATHSIZE];
76
77         /*
78          * If we are read only, we can't do anything,
79          * so just return quickly.
80          */
81         if (readonly)
82                 return;
83         /*
84          * If editing (not reading system mail box), then do the work
85          * in edstop()
86          */
87         if (edit) {
88                 edstop();
89                 return;
90         }
91
92         /*
93          * See if there any messages to save in mbox.  If no, we
94          * can save copying mbox to /tmp and back.
95          *
96          * Check also to see if any files need to be preserved.
97          * Delete all untouched messages to keep them out of mbox.
98          * If all the messages are to be preserved, just exit with
99          * a message.
100          */
101
102         fbuf = Fopen(mailname, "r");
103         if (fbuf == NULL)
104                 goto newmail;
105         flock(fileno(fbuf), LOCK_EX);
106         rbuf = NULL;
107         if (fstat(fileno(fbuf), &minfo) >= 0 && minfo.st_size > mailsize) {
108                 printf("New mail has arrived.\n");
109                 snprintf(tempname, sizeof(tempname), "%s/mail.RqXXXXXXXXXX",
110                          tmpdir);
111                 if ((fd = mkstemp(tempname)) == -1 ||
112                     (rbuf = Fdopen(fd, "w")) == NULL)
113                         goto newmail;
114 #ifdef APPEND
115                 fseeko(fbuf, mailsize, SEEK_SET);
116                 while ((c = getc(fbuf)) != EOF)
117                         putc(c, rbuf);
118 #else
119                 p = minfo.st_size - mailsize;
120                 while (p-- > 0) {
121                         c = getc(fbuf);
122                         if (c == EOF)
123                                 goto newmail;
124                         putc(c, rbuf);
125                 }
126 #endif
127                 Fclose(rbuf);
128                 if ((rbuf = Fopen(tempname, "r")) == NULL)
129                         goto newmail;
130                 rm(tempname);
131         }
132
133         /*
134          * Adjust the message flags in each message.
135          */
136
137         anystat = 0;
138         autohold = value("hold") != NULL;
139         holdbit = autohold ? MPRESERVE : MBOX;
140         nohold = MBOX|MSAVED|MDELETED|MPRESERVE;
141         if (value("keepsave") != NULL)
142                 nohold &= ~MSAVED;
143         for (mp = &message[0]; mp < &message[msgCount]; mp++) {
144                 if (mp->m_flag & MNEW) {
145                         mp->m_flag &= ~MNEW;
146                         mp->m_flag |= MSTATUS;
147                 }
148                 if (mp->m_flag & MSTATUS)
149                         anystat++;
150                 if ((mp->m_flag & MTOUCH) == 0)
151                         mp->m_flag |= MPRESERVE;
152                 if ((mp->m_flag & nohold) == 0)
153                         mp->m_flag |= holdbit;
154         }
155         modify = 0;
156         if (Tflag != NULL) {
157                 if ((readstat = Fopen(Tflag, "w")) == NULL)
158                         Tflag = NULL;
159         }
160         for (c = 0, p = 0, mp = &message[0]; mp < &message[msgCount]; mp++) {
161                 if (mp->m_flag & MBOX)
162                         c++;
163                 if (mp->m_flag & MPRESERVE)
164                         p++;
165                 if (mp->m_flag & MODIFY)
166                         modify++;
167                 if (Tflag != NULL && (mp->m_flag & (MREAD|MDELETED)) != 0) {
168                         char *id;
169
170                         if ((id = hfield("article-id", mp)) != NULL)
171                                 fprintf(readstat, "%s\n", id);
172                 }
173         }
174         if (Tflag != NULL)
175                 Fclose(readstat);
176         if (p == msgCount && !modify && !anystat) {
177                 printf("Held %d message%s in %s\n",
178                         p, p == 1 ? "" : "s", mailname);
179                 Fclose(fbuf);
180                 return;
181         }
182         if (c == 0) {
183                 if (p != 0) {
184                         writeback(rbuf);
185                         Fclose(fbuf);
186                         return;
187                 }
188                 goto cream;
189         }
190
191         /*
192          * Create another temporary file and copy user's mbox file
193          * darin.  If there is no mbox, copy nothing.
194          * If he has specified "append" don't copy his mailbox,
195          * just copy saveable entries at the end.
196          */
197
198         mbox = expand("&");
199         mcount = c;
200         if (value("append") == NULL) {
201                 snprintf(tempname, sizeof(tempname), "%s/mail.RmXXXXXXXXXX",
202                          tmpdir);
203                 if ((fd = mkstemp(tempname)) == -1 ||
204                     (obuf = Fdopen(fd, "w")) == NULL) {
205                         warn("%s", tempname);
206                         Fclose(fbuf);
207                         return;
208                 }
209                 if ((ibuf = Fopen(tempname, "r")) == NULL) {
210                         warn("%s", tempname);
211                         rm(tempname);
212                         Fclose(obuf);
213                         Fclose(fbuf);
214                         return;
215                 }
216                 rm(tempname);
217                 if ((abuf = Fopen(mbox, "r")) != NULL) {
218                         while ((c = getc(abuf)) != EOF)
219                                 putc(c, obuf);
220                         Fclose(abuf);
221                 }
222                 if (ferror(obuf)) {
223                         warnx("%s", tempname);
224                         Fclose(ibuf);
225                         Fclose(obuf);
226                         Fclose(fbuf);
227                         return;
228                 }
229                 Fclose(obuf);
230                 close(open(mbox, O_CREAT | O_TRUNC | O_WRONLY, 0600));
231                 if ((obuf = Fopen(mbox, "r+")) == NULL) {
232                         warn("%s", mbox);
233                         Fclose(ibuf);
234                         Fclose(fbuf);
235                         return;
236                 }
237         }
238         if (value("append") != NULL) {
239                 if ((obuf = Fopen(mbox, "a")) == NULL) {
240                         warn("%s", mbox);
241                         Fclose(fbuf);
242                         return;
243                 }
244                 fchmod(fileno(obuf), 0600);
245         }
246         for (mp = &message[0]; mp < &message[msgCount]; mp++)
247                 if (mp->m_flag & MBOX)
248                         if (sendmessage(mp, obuf, saveignore, NULL) < 0) {
249                                 warnx("%s", mbox);
250                                 Fclose(ibuf);
251                                 Fclose(obuf);
252                                 Fclose(fbuf);
253                                 return;
254                         }
255
256         /*
257          * Copy the user's old mbox contents back
258          * to the end of the stuff we just saved.
259          * If we are appending, this is unnecessary.
260          */
261
262         if (value("append") == NULL) {
263                 rewind(ibuf);
264                 c = getc(ibuf);
265                 while (c != EOF) {
266                         putc(c, obuf);
267                         if (ferror(obuf))
268                                 break;
269                         c = getc(ibuf);
270                 }
271                 Fclose(ibuf);
272         }
273         fflush(obuf);
274         trunc(obuf);
275         if (ferror(obuf)) {
276                 warn("%s", mbox);
277                 Fclose(obuf);
278                 Fclose(fbuf);
279                 return;
280         }
281         Fclose(obuf);
282         if (mcount == 1)
283                 printf("Saved 1 message in mbox\n");
284         else
285                 printf("Saved %d messages in mbox\n", mcount);
286
287         /*
288          * Now we are ready to copy back preserved files to
289          * the system mailbox, if any were requested.
290          */
291
292         if (p != 0) {
293                 writeback(rbuf);
294                 Fclose(fbuf);
295                 return;
296         }
297
298         /*
299          * Finally, remove his /var/mail file.
300          * If new mail has arrived, copy it back.
301          */
302
303 cream:
304         if (rbuf != NULL) {
305                 abuf = Fopen(mailname, "r+");
306                 if (abuf == NULL)
307                         goto newmail;
308                 while ((c = getc(rbuf)) != EOF)
309                         putc(c, abuf);
310                 Fclose(rbuf);
311                 trunc(abuf);
312                 Fclose(abuf);
313                 alter(mailname);
314                 Fclose(fbuf);
315                 return;
316         }
317         demail();
318         Fclose(fbuf);
319         return;
320
321 newmail:
322         printf("Thou hast new mail.\n");
323         if (fbuf != NULL)
324                 Fclose(fbuf);
325 }
326
327 /*
328  * Preserve all the appropriate messages back in the system
329  * mailbox, and print a nice message indicated how many were
330  * saved.  On any error, just return -1.  Else return 0.
331  * Incorporate the any new mail that we found.
332  */
333 static int
334 writeback(FILE *res)
335 {
336         struct message *mp;
337         int p, c;
338         FILE *obuf;
339
340         p = 0;
341         if ((obuf = Fopen(mailname, "r+")) == NULL) {
342                 warn("%s", mailname);
343                 return (-1);
344         }
345 #ifndef APPEND
346         if (res != NULL)
347                 while ((c = getc(res)) != EOF)
348                         putc(c, obuf);
349 #endif
350         for (mp = &message[0]; mp < &message[msgCount]; mp++)
351                 if ((mp->m_flag&MPRESERVE)||(mp->m_flag&MTOUCH)==0) {
352                         p++;
353                         if (sendmessage(mp, obuf, NULL, NULL) < 0) {
354                                 warnx("%s", mailname);
355                                 Fclose(obuf);
356                                 return (-1);
357                         }
358                 }
359 #ifdef APPEND
360         if (res != NULL)
361                 while ((c = getc(res)) != EOF)
362                         putc(c, obuf);
363 #endif
364         fflush(obuf);
365         trunc(obuf);
366         if (ferror(obuf)) {
367                 warn("%s", mailname);
368                 Fclose(obuf);
369                 return (-1);
370         }
371         if (res != NULL)
372                 Fclose(res);
373         Fclose(obuf);
374         alter(mailname);
375         if (p == 1)
376                 printf("Held 1 message in %s\n", mailname);
377         else
378                 printf("Held %d messages in %s\n", p, mailname);
379         return (0);
380 }
381
382 /*
383  * Terminate an editing session by attempting to write out the user's
384  * file from the temporary.  Save any new stuff appended to the file.
385  */
386 static void
387 edstop(void)
388 {
389         int gotcha, c;
390         struct message *mp;
391         FILE *obuf, *ibuf, *readstat;
392         struct stat statb;
393         char tempname[PATHSIZE];
394
395         if (readonly)
396                 return;
397         holdsigs();
398         if (Tflag != NULL) {
399                 if ((readstat = Fopen(Tflag, "w")) == NULL)
400                         Tflag = NULL;
401         }
402         for (mp = &message[0], gotcha = 0; mp < &message[msgCount]; mp++) {
403                 if (mp->m_flag & MNEW) {
404                         mp->m_flag &= ~MNEW;
405                         mp->m_flag |= MSTATUS;
406                 }
407                 if (mp->m_flag & (MODIFY|MDELETED|MSTATUS))
408                         gotcha++;
409                 if (Tflag != NULL && (mp->m_flag & (MREAD|MDELETED)) != 0) {
410                         char *id;
411
412                         if ((id = hfield("article-id", mp)) != NULL)
413                                 fprintf(readstat, "%s\n", id);
414                 }
415         }
416         if (Tflag != NULL)
417                 Fclose(readstat);
418         if (!gotcha || Tflag != NULL)
419                 goto done;
420         ibuf = NULL;
421         if (stat(mailname, &statb) >= 0 && statb.st_size > mailsize) {
422                 int fd;
423
424                 snprintf(tempname, sizeof(tempname), "%s/mbox.XXXXXXXXXX",
425                          tmpdir);
426                 if ((fd = mkstemp(tempname)) == -1 ||
427                     (obuf = Fdopen(fd, "w")) == NULL) {
428                         warn("%s", tempname);
429                         relsesigs();
430                         reset(0);
431                 }
432                 if ((ibuf = Fopen(mailname, "r")) == NULL) {
433                         warn("%s", mailname);
434                         Fclose(obuf);
435                         rm(tempname);
436                         relsesigs();
437                         reset(0);
438                 }
439                 fseeko(ibuf, mailsize, SEEK_SET);
440                 while ((c = getc(ibuf)) != EOF)
441                         putc(c, obuf);
442                 Fclose(ibuf);
443                 Fclose(obuf);
444                 if ((ibuf = Fopen(tempname, "r")) == NULL) {
445                         warn("%s", tempname);
446                         rm(tempname);
447                         relsesigs();
448                         reset(0);
449                 }
450                 rm(tempname);
451         }
452         printf("\"%s\" ", mailname);
453         fflush(stdout);
454         if ((obuf = Fopen(mailname, "r+")) == NULL) {
455                 warn("%s", mailname);
456                 relsesigs();
457                 reset(0);
458         }
459         trunc(obuf);
460         c = 0;
461         for (mp = &message[0]; mp < &message[msgCount]; mp++) {
462                 if ((mp->m_flag & MDELETED) != 0)
463                         continue;
464                 c++;
465                 if (sendmessage(mp, obuf, NULL, NULL) < 0) {
466                         warnx("%s", mailname);
467                         relsesigs();
468                         reset(0);
469                 }
470         }
471         gotcha = (c == 0 && ibuf == NULL);
472         if (ibuf != NULL) {
473                 while ((c = getc(ibuf)) != EOF)
474                         putc(c, obuf);
475                 Fclose(ibuf);
476         }
477         fflush(obuf);
478         if (ferror(obuf)) {
479                 warn("%s", mailname);
480                 relsesigs();
481                 reset(0);
482         }
483         Fclose(obuf);
484         if (gotcha) {
485                 rm(mailname);
486                 printf("removed\n");
487         } else
488                 printf("complete\n");
489         fflush(stdout);
490
491 done:
492         relsesigs();
493 }