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