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