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