cmd1.c revision 88150
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
38static const char rcsid[] =
39  "$FreeBSD: head/usr.bin/mail/cmd1.c 88150 2001-12-18 20:52:09Z mikeh $";
40#endif /* not lint */
41
42#include "rcv.h"
43#include "extern.h"
44
45/*
46 * Mail -- a mail program
47 *
48 * User commands.
49 */
50
51extern 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
58static int screen;
59
60int
61headers(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 */
100int
101scroll(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 */
139int
140screensize()
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 */
154int
155from(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 */
171void
172printhead(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 */
218int
219pdot()
220{
221	printf("%d\n", dot - &message[0] + 1);
222	return (0);
223}
224
225/*
226 * Print out all the possible commands.
227 */
228int
229pcmdlist()
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 */
252int
253more(msgvec)
254	int *msgvec;
255{
256
257	return (type1(msgvec, 1, 1));
258}
259
260/*
261 * Paginate messages, even printing ignored fields.
262 */
263int
264More(msgvec)
265	int *msgvec;
266{
267
268	return (type1(msgvec, 0, 1));
269}
270
271/*
272 * Type out messages, honor ignored fields.
273 */
274int
275type(msgvec)
276	int *msgvec;
277{
278
279	return (type1(msgvec, 1, 0));
280}
281
282/*
283 * Type out messages, even printing ignored fields.
284 */
285int
286Type(msgvec)
287	int *msgvec;
288{
289
290	return (type1(msgvec, 0, 0));
291}
292
293/*
294 * Type out the messages requested.
295 */
296jmp_buf	pipestop;
297int
298type1(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
343close_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*/
360void
361brokpipe(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 */
372int
373top(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 */
414int
415stouch(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 */
431int
432mboxit(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 */
448int
449folders()
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 */
468int
469inc(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}
486