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