Initial import from FreeBSD RELENG_4:
[games.git] / usr.bin / mail / cmd1.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. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #ifndef lint
35 #if 0
36 static char sccsid[] = "@(#)cmd1.c      8.2 (Berkeley) 4/20/95";
37 #endif
38 #endif /* not lint */
39 #include <sys/cdefs.h>
40 __FBSDID("$FreeBSD: src/usr.bin/mail/cmd1.c,v 1.3.6.3 2003/01/06 05:46:03 mikeh Exp $");
41
42 #include "rcv.h"
43 #include "extern.h"
44
45 /*
46  * Mail -- a mail program
47  *
48  * User commands.
49  */
50
51 extern const struct cmd cmdtab[];
52
53 /*
54  * Print the current active headings.
55  * Don't change dot if invoker didn't give an argument.
56  */
57
58 static int screen;
59
60 int
61 headers(msgvec)
62         int *msgvec;
63 {
64         int n, mesg, flag, size;
65         struct message *mp;
66
67         size = screensize();
68         n = msgvec[0];
69         if (n != 0)
70                 screen = (n-1)/size;
71         if (screen < 0)
72                 screen = 0;
73         mp = &message[screen * size];
74         if (mp >= &message[msgCount])
75                 mp = &message[msgCount - size];
76         if (mp < &message[0])
77                 mp = &message[0];
78         flag = 0;
79         mesg = mp - &message[0];
80         if (dot != &message[n-1])
81                 dot = mp;
82         for (; mp < &message[msgCount]; mp++) {
83                 mesg++;
84                 if (mp->m_flag & MDELETED)
85                         continue;
86                 if (flag++ >= size)
87                         break;
88                 printhead(mesg);
89         }
90         if (flag == 0) {
91                 printf("No more mail.\n");
92                 return (1);
93         }
94         return (0);
95 }
96
97 /*
98  * Scroll to the next/previous screen
99  */
100 int
101 scroll(arg)
102         char arg[];
103 {
104         int s, size;
105         int cur[1];
106
107         cur[0] = 0;
108         size = screensize();
109         s = screen;
110         switch (*arg) {
111         case 0:
112         case '+':
113                 s++;
114                 if (s * size >= msgCount) {
115                         printf("On last screenful of messages\n");
116                         return (0);
117                 }
118                 screen = s;
119                 break;
120
121         case '-':
122                 if (--s < 0) {
123                         printf("On first screenful of messages\n");
124                         return (0);
125                 }
126                 screen = s;
127                 break;
128
129         default:
130                 printf("Unrecognized scrolling command \"%s\"\n", arg);
131                 return (1);
132         }
133         return (headers(cur));
134 }
135
136 /*
137  * Compute screen size.
138  */
139 int
140 screensize()
141 {
142         int s;
143         char *cp;
144
145         if ((cp = value("screen")) != NULL && (s = atoi(cp)) > 0)
146                 return (s);
147         return (screenheight - 4);
148 }
149
150 /*
151  * Print out the headlines for each message
152  * in the passed message list.
153  */
154 int
155 from(msgvec)
156         int *msgvec;
157 {
158         int *ip;
159
160         for (ip = msgvec; *ip != 0; ip++)
161                 printhead(*ip);
162         if (--ip >= msgvec)
163                 dot = &message[*ip - 1];
164         return (0);
165 }
166
167 /*
168  * Print out the header of a specific message.
169  * This is a slight improvement to the standard one.
170  */
171 void
172 printhead(mesg)
173         int mesg;
174 {
175         struct message *mp;
176         char headline[LINESIZE], wcount[LINESIZE], *subjline, dispc, curind;
177         char pbuf[BUFSIZ];
178         struct headline hl;
179         int subjlen;
180         char *name;
181
182         mp = &message[mesg-1];
183         (void)readline(setinput(mp), headline, LINESIZE);
184         if ((subjline = hfield("subject", mp)) == NULL)
185                 subjline = hfield("subj", mp);
186         /*
187          * Bletch!
188          */
189         curind = dot == mp ? '>' : ' ';
190         dispc = ' ';
191         if (mp->m_flag & MSAVED)
192                 dispc = '*';
193         if (mp->m_flag & MPRESERVE)
194                 dispc = 'P';
195         if ((mp->m_flag & (MREAD|MNEW)) == MNEW)
196                 dispc = 'N';
197         if ((mp->m_flag & (MREAD|MNEW)) == 0)
198                 dispc = 'U';
199         if (mp->m_flag & MBOX)
200                 dispc = 'M';
201         parse(headline, &hl, pbuf);
202         sprintf(wcount, "%3ld/%-5ld", mp->m_lines, mp->m_size);
203         subjlen = screenwidth - 50 - strlen(wcount);
204         name = value("show-rcpt") != NULL ?
205                 skin(hfield("to", mp)) : nameof(mp, 0);
206         if (subjline == NULL || subjlen < 0)            /* pretty pathetic */
207                 printf("%c%c%3d %-20.20s  %16.16s %s\n",
208                         curind, dispc, mesg, name, hl.l_date, wcount);
209         else
210                 printf("%c%c%3d %-20.20s  %16.16s %s \"%.*s\"\n",
211                         curind, dispc, mesg, name, hl.l_date, wcount,
212                         subjlen, subjline);
213 }
214
215 /*
216  * Print out the value of dot.
217  */
218 int
219 pdot()
220 {
221         printf("%d\n", dot - &message[0] + 1);
222         return (0);
223 }
224
225 /*
226  * Print out all the possible commands.
227  */
228 int
229 pcmdlist()
230 {
231         const struct cmd *cp;
232         int cc;
233
234         printf("Commands are:\n");
235         for (cc = 0, cp = cmdtab; cp->c_name != NULL; cp++) {
236                 cc += strlen(cp->c_name) + 2;
237                 if (cc > 72) {
238                         printf("\n");
239                         cc = strlen(cp->c_name) + 2;
240                 }
241                 if ((cp+1)->c_name != NULL)
242                         printf("%s, ", cp->c_name);
243                 else
244                         printf("%s\n", cp->c_name);
245         }
246         return (0);
247 }
248
249 /*
250  * Paginate messages, honor ignored fields.
251  */
252 int
253 more(msgvec)
254         int *msgvec;
255 {
256
257         return (type1(msgvec, 1, 1));
258 }
259
260 /*
261  * Paginate messages, even printing ignored fields.
262  */
263 int
264 More(msgvec)
265         int *msgvec;
266 {
267
268         return (type1(msgvec, 0, 1));
269 }
270
271 /*
272  * Type out messages, honor ignored fields.
273  */
274 int
275 type(msgvec)
276         int *msgvec;
277 {
278
279         return (type1(msgvec, 1, 0));
280 }
281
282 /*
283  * Type out messages, even printing ignored fields.
284  */
285 int
286 Type(msgvec)
287         int *msgvec;
288 {
289
290         return (type1(msgvec, 0, 0));
291 }
292
293 /*
294  * Type out the messages requested.
295  */
296 jmp_buf pipestop;
297 int
298 type1(msgvec, doign, page)
299         int *msgvec;
300         int doign, page;
301 {
302         int nlines, *ip;
303         struct message *mp;
304         char *cp;
305         FILE *obuf;
306
307         obuf = stdout;
308         if (setjmp(pipestop))
309                 goto close_pipe;
310         if (value("interactive") != NULL &&
311             (page || (cp = value("crt")) != NULL)) {
312                 nlines = 0;
313                 if (!page) {
314                         for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++)
315                                 nlines += message[*ip - 1].m_lines;
316                 }
317                 if (page || nlines > (*cp ? atoi(cp) : realscreenheight)) {
318                         cp = value("PAGER");
319                         if (cp == NULL || *cp == '\0')
320                                 cp = _PATH_MORE;
321                         obuf = Popen(cp, "w");
322                         if (obuf == NULL) {
323                                 warnx("%s", cp);
324                                 obuf = stdout;
325                         } else
326                                 (void)signal(SIGPIPE, brokpipe);
327                 }
328         }
329
330         /*
331          * Send messages to the output.
332          *
333          */
334         for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) {
335                 mp = &message[*ip - 1];
336                 touch(mp);
337                 dot = mp;
338                 if (value("quiet") == NULL)
339                         fprintf(obuf, "Message %d:\n", *ip);
340                 (void)sendmessage(mp, obuf, doign ? ignore : 0, NULL);
341         }
342
343 close_pipe:
344         if (obuf != stdout) {
345                 /*
346                  * Ignore SIGPIPE so it can't cause a duplicate close.
347                  */
348                 (void)signal(SIGPIPE, SIG_IGN);
349                 (void)Pclose(obuf);
350                 (void)signal(SIGPIPE, SIG_DFL);
351         }
352         return (0);
353 }
354
355 /*
356  * Respond to a broken pipe signal --
357  * probably caused by quitting more.
358  */
359 /*ARGSUSED*/
360 void
361 brokpipe(signo)
362         int signo;
363 {
364         longjmp(pipestop, 1);
365 }
366
367 /*
368  * Print the top so many lines of each desired message.
369  * The number of lines is taken from the variable "toplines"
370  * and defaults to 5.
371  */
372 int
373 top(msgvec)
374         int *msgvec;
375 {
376         int *ip;
377         struct message *mp;
378         int c, topl, lines, lineb;
379         char *valtop, linebuf[LINESIZE];
380         FILE *ibuf;
381
382         topl = 5;
383         valtop = value("toplines");
384         if (valtop != NULL) {
385                 topl = atoi(valtop);
386                 if (topl < 0 || topl > 10000)
387                         topl = 5;
388         }
389         lineb = 1;
390         for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
391                 mp = &message[*ip - 1];
392                 touch(mp);
393                 dot = mp;
394                 if (value("quiet") == NULL)
395                         printf("Message %d:\n", *ip);
396                 ibuf = setinput(mp);
397                 c = mp->m_lines;
398                 if (!lineb)
399                         printf("\n");
400                 for (lines = 0; lines < c && lines <= topl; lines++) {
401                         if (readline(ibuf, linebuf, sizeof(linebuf)) < 0)
402                                 break;
403                         puts(linebuf);
404                         lineb = strspn(linebuf, " \t") == strlen(linebuf);
405                 }
406         }
407         return (0);
408 }
409
410 /*
411  * Touch all the given messages so that they will
412  * get mboxed.
413  */
414 int
415 stouch(msgvec)
416         int msgvec[];
417 {
418         int *ip;
419
420         for (ip = msgvec; *ip != 0; ip++) {
421                 dot = &message[*ip-1];
422                 dot->m_flag |= MTOUCH;
423                 dot->m_flag &= ~MPRESERVE;
424         }
425         return (0);
426 }
427
428 /*
429  * Make sure all passed messages get mboxed.
430  */
431 int
432 mboxit(msgvec)
433         int msgvec[];
434 {
435         int *ip;
436
437         for (ip = msgvec; *ip != 0; ip++) {
438                 dot = &message[*ip-1];
439                 dot->m_flag |= MTOUCH|MBOX;
440                 dot->m_flag &= ~MPRESERVE;
441         }
442         return (0);
443 }
444
445 /*
446  * List the folders the user currently has.
447  */
448 int
449 folders()
450 {
451         char dirname[PATHSIZE];
452         char *cmd;
453
454         if (getfold(dirname, sizeof(dirname)) < 0) {
455                 printf("No value set for \"folder\"\n");
456                 return (1);
457         }
458         if ((cmd = value("LISTER")) == NULL)
459                 cmd = "ls";
460         (void)run_command(cmd, 0, -1, -1, dirname, NULL, NULL);
461         return (0);
462 }
463
464 /*
465  * Update the mail file with any new messages that have
466  * come in since we started reading mail.
467  */
468 int
469 inc(v)
470         void *v;
471 {
472         int nmsg, mdot;
473
474         nmsg = incfile();
475
476         if (nmsg == 0)
477                 printf("No new mail.\n");
478         else if (nmsg > 0) {
479                 mdot = newfileinfo(msgCount - nmsg);
480                 dot = &message[mdot - 1];
481         } else
482                 printf("\"inc\" command failed...\n");
483
484         return (0);
485 }