cmd1.c revision 74769
11590Srgrimes/*-
21590Srgrimes * Copyright (c) 1980, 1993
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
41590Srgrimes *
51590Srgrimes * Redistribution and use in source and binary forms, with or without
61590Srgrimes * modification, are permitted provided that the following conditions
71590Srgrimes * are met:
81590Srgrimes * 1. Redistributions of source code must retain the above copyright
91590Srgrimes *    notice, this list of conditions and the following disclaimer.
101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111590Srgrimes *    notice, this list of conditions and the following disclaimer in the
121590Srgrimes *    documentation and/or other materials provided with the distribution.
131590Srgrimes * 3. All advertising materials mentioning features or use of this software
141590Srgrimes *    must display the following acknowledgement:
151590Srgrimes *	This product includes software developed by the University of
161590Srgrimes *	California, Berkeley and its contributors.
171590Srgrimes * 4. Neither the name of the University nor the names of its contributors
181590Srgrimes *    may be used to endorse or promote products derived from this software
191590Srgrimes *    without specific prior written permission.
201590Srgrimes *
211590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311590Srgrimes * SUCH DAMAGE.
321590Srgrimes */
331590Srgrimes
341590Srgrimes#ifndef lint
3574769Smikeh#if 0
361590Srgrimesstatic char sccsid[] = "@(#)cmd1.c	8.1 (Berkeley) 6/6/93";
3774769Smikeh#endif
3874769Smikehstatic const char rcsid[] =
3974769Smikeh  "$FreeBSD: head/usr.bin/mail/cmd1.c 74769 2001-03-25 04:57:05Z mikeh $";
401590Srgrimes#endif /* not lint */
411590Srgrimes
421590Srgrimes#include "rcv.h"
431590Srgrimes#include "extern.h"
441590Srgrimes
451590Srgrimes/*
461590Srgrimes * Mail -- a mail program
471590Srgrimes *
481590Srgrimes * User commands.
491590Srgrimes */
501590Srgrimes
511590Srgrimes/*
521590Srgrimes * Print the current active headings.
531590Srgrimes * Don't change dot if invoker didn't give an argument.
541590Srgrimes */
551590Srgrimes
561590Srgrimesstatic int screen;
571590Srgrimes
581590Srgrimesint
591590Srgrimesheaders(msgvec)
601590Srgrimes	int *msgvec;
611590Srgrimes{
621590Srgrimes	register int n, mesg, flag;
631590Srgrimes	register struct message *mp;
641590Srgrimes	int size;
651590Srgrimes
661590Srgrimes	size = screensize();
671590Srgrimes	n = msgvec[0];
681590Srgrimes	if (n != 0)
691590Srgrimes		screen = (n-1)/size;
701590Srgrimes	if (screen < 0)
711590Srgrimes		screen = 0;
721590Srgrimes	mp = &message[screen * size];
731590Srgrimes	if (mp >= &message[msgCount])
741590Srgrimes		mp = &message[msgCount - size];
751590Srgrimes	if (mp < &message[0])
761590Srgrimes		mp = &message[0];
771590Srgrimes	flag = 0;
781590Srgrimes	mesg = mp - &message[0];
791590Srgrimes	if (dot != &message[n-1])
801590Srgrimes		dot = mp;
811590Srgrimes	for (; mp < &message[msgCount]; mp++) {
821590Srgrimes		mesg++;
831590Srgrimes		if (mp->m_flag & MDELETED)
841590Srgrimes			continue;
851590Srgrimes		if (flag++ >= size)
861590Srgrimes			break;
871590Srgrimes		printhead(mesg);
881590Srgrimes	}
891590Srgrimes	if (flag == 0) {
901590Srgrimes		printf("No more mail.\n");
911590Srgrimes		return(1);
921590Srgrimes	}
931590Srgrimes	return(0);
941590Srgrimes}
951590Srgrimes
961590Srgrimes/*
971590Srgrimes * Scroll to the next/previous screen
981590Srgrimes */
991590Srgrimesint
1001590Srgrimesscroll(arg)
1011590Srgrimes	char arg[];
1021590Srgrimes{
1031590Srgrimes	register int s, size;
1041590Srgrimes	int cur[1];
1051590Srgrimes
1061590Srgrimes	cur[0] = 0;
1071590Srgrimes	size = screensize();
1081590Srgrimes	s = screen;
1091590Srgrimes	switch (*arg) {
1101590Srgrimes	case 0:
1111590Srgrimes	case '+':
1121590Srgrimes		s++;
1131590Srgrimes		if (s * size > msgCount) {
1141590Srgrimes			printf("On last screenful of messages\n");
1151590Srgrimes			return(0);
1161590Srgrimes		}
1171590Srgrimes		screen = s;
1181590Srgrimes		break;
1191590Srgrimes
1201590Srgrimes	case '-':
1211590Srgrimes		if (--s < 0) {
1221590Srgrimes			printf("On first screenful of messages\n");
1231590Srgrimes			return(0);
1241590Srgrimes		}
1251590Srgrimes		screen = s;
1261590Srgrimes		break;
1271590Srgrimes
1281590Srgrimes	default:
1291590Srgrimes		printf("Unrecognized scrolling command \"%s\"\n", arg);
1301590Srgrimes		return(1);
1311590Srgrimes	}
1321590Srgrimes	return(headers(cur));
1331590Srgrimes}
1341590Srgrimes
1351590Srgrimes/*
1361590Srgrimes * Compute screen size.
1371590Srgrimes */
1381590Srgrimesint
1391590Srgrimesscreensize()
1401590Srgrimes{
1411590Srgrimes	int s;
1421590Srgrimes	char *cp;
1431590Srgrimes
1441590Srgrimes	if ((cp = value("screen")) != NOSTR && (s = atoi(cp)) > 0)
1451590Srgrimes		return s;
1461590Srgrimes	return screenheight - 4;
1471590Srgrimes}
1481590Srgrimes
1491590Srgrimes/*
1501590Srgrimes * Print out the headlines for each message
1511590Srgrimes * in the passed message list.
1521590Srgrimes */
1531590Srgrimesint
1541590Srgrimesfrom(msgvec)
1551590Srgrimes	int *msgvec;
1561590Srgrimes{
1571590Srgrimes	register int *ip;
1581590Srgrimes
15929574Sphk	for (ip = msgvec; *ip != 0; ip++)
1601590Srgrimes		printhead(*ip);
1611590Srgrimes	if (--ip >= msgvec)
1621590Srgrimes		dot = &message[*ip - 1];
1631590Srgrimes	return(0);
1641590Srgrimes}
1651590Srgrimes
1661590Srgrimes/*
1671590Srgrimes * Print out the header of a specific message.
1681590Srgrimes * This is a slight improvement to the standard one.
1691590Srgrimes */
1701590Srgrimesvoid
1711590Srgrimesprinthead(mesg)
1721590Srgrimes	int mesg;
1731590Srgrimes{
1741590Srgrimes	struct message *mp;
1751590Srgrimes	char headline[LINESIZE], wcount[LINESIZE], *subjline, dispc, curind;
1761590Srgrimes	char pbuf[BUFSIZ];
1771590Srgrimes	struct headline hl;
1781590Srgrimes	int subjlen;
1791590Srgrimes	char *name;
1801590Srgrimes
1811590Srgrimes	mp = &message[mesg-1];
1821590Srgrimes	(void) readline(setinput(mp), headline, LINESIZE);
1831590Srgrimes	if ((subjline = hfield("subject", mp)) == NOSTR)
1841590Srgrimes		subjline = hfield("subj", mp);
1851590Srgrimes	/*
1861590Srgrimes	 * Bletch!
1871590Srgrimes	 */
1881590Srgrimes	curind = dot == mp ? '>' : ' ';
1891590Srgrimes	dispc = ' ';
1901590Srgrimes	if (mp->m_flag & MSAVED)
1911590Srgrimes		dispc = '*';
1921590Srgrimes	if (mp->m_flag & MPRESERVE)
1931590Srgrimes		dispc = 'P';
1941590Srgrimes	if ((mp->m_flag & (MREAD|MNEW)) == MNEW)
1951590Srgrimes		dispc = 'N';
1961590Srgrimes	if ((mp->m_flag & (MREAD|MNEW)) == 0)
1971590Srgrimes		dispc = 'U';
1981590Srgrimes	if (mp->m_flag & MBOX)
1991590Srgrimes		dispc = 'M';
2001590Srgrimes	parse(headline, &hl, pbuf);
20137453Sbde	sprintf(wcount, "%3ld/%-5ld", mp->m_lines, mp->m_size);
2021590Srgrimes	subjlen = screenwidth - 50 - strlen(wcount);
2031590Srgrimes	name = value("show-rcpt") != NOSTR ?
2041590Srgrimes		skin(hfield("to", mp)) : nameof(mp, 0);
2051590Srgrimes	if (subjline == NOSTR || subjlen < 0)		/* pretty pathetic */
2061590Srgrimes		printf("%c%c%3d %-20.20s  %16.16s %s\n",
2071590Srgrimes			curind, dispc, mesg, name, hl.l_date, wcount);
2081590Srgrimes	else
2091590Srgrimes		printf("%c%c%3d %-20.20s  %16.16s %s \"%.*s\"\n",
2101590Srgrimes			curind, dispc, mesg, name, hl.l_date, wcount,
2111590Srgrimes			subjlen, subjline);
2121590Srgrimes}
2131590Srgrimes
2141590Srgrimes/*
2151590Srgrimes * Print out the value of dot.
2161590Srgrimes */
2171590Srgrimesint
2181590Srgrimespdot()
2191590Srgrimes{
2201590Srgrimes	printf("%d\n", dot - &message[0] + 1);
2211590Srgrimes	return(0);
2221590Srgrimes}
2231590Srgrimes
2241590Srgrimes/*
2251590Srgrimes * Print out all the possible commands.
2261590Srgrimes */
2271590Srgrimesint
2281590Srgrimespcmdlist()
2291590Srgrimes{
2301590Srgrimes	register struct cmd *cp;
2311590Srgrimes	register int cc;
2321590Srgrimes	extern struct cmd cmdtab[];
2331590Srgrimes
2341590Srgrimes	printf("Commands are:\n");
2351590Srgrimes	for (cc = 0, cp = cmdtab; cp->c_name != NULL; cp++) {
2361590Srgrimes		cc += strlen(cp->c_name) + 2;
2371590Srgrimes		if (cc > 72) {
2381590Srgrimes			printf("\n");
2391590Srgrimes			cc = strlen(cp->c_name) + 2;
2401590Srgrimes		}
2411590Srgrimes		if ((cp+1)->c_name != NOSTR)
2421590Srgrimes			printf("%s, ", cp->c_name);
2431590Srgrimes		else
2441590Srgrimes			printf("%s\n", cp->c_name);
2451590Srgrimes	}
2461590Srgrimes	return(0);
2471590Srgrimes}
2481590Srgrimes
2491590Srgrimes/*
2501590Srgrimes * Paginate messages, honor ignored fields.
2511590Srgrimes */
2521590Srgrimesint
2531590Srgrimesmore(msgvec)
2541590Srgrimes	int *msgvec;
2551590Srgrimes{
2561590Srgrimes	return (type1(msgvec, 1, 1));
2571590Srgrimes}
2581590Srgrimes
2591590Srgrimes/*
2601590Srgrimes * Paginate messages, even printing ignored fields.
2611590Srgrimes */
2621590Srgrimesint
2631590SrgrimesMore(msgvec)
2641590Srgrimes	int *msgvec;
2651590Srgrimes{
2661590Srgrimes
2671590Srgrimes	return (type1(msgvec, 0, 1));
2681590Srgrimes}
2691590Srgrimes
2701590Srgrimes/*
2711590Srgrimes * Type out messages, honor ignored fields.
2721590Srgrimes */
2731590Srgrimesint
2741590Srgrimestype(msgvec)
2751590Srgrimes	int *msgvec;
2761590Srgrimes{
2771590Srgrimes
2781590Srgrimes	return(type1(msgvec, 1, 0));
2791590Srgrimes}
2801590Srgrimes
2811590Srgrimes/*
2821590Srgrimes * Type out messages, even printing ignored fields.
2831590Srgrimes */
2841590Srgrimesint
2851590SrgrimesType(msgvec)
2861590Srgrimes	int *msgvec;
2871590Srgrimes{
2881590Srgrimes
2891590Srgrimes	return(type1(msgvec, 0, 0));
2901590Srgrimes}
2911590Srgrimes
2921590Srgrimes/*
2931590Srgrimes * Type out the messages requested.
2941590Srgrimes */
2951590Srgrimesjmp_buf	pipestop;
2961590Srgrimesint
2971590Srgrimestype1(msgvec, doign, page)
2981590Srgrimes	int *msgvec;
2991590Srgrimes	int doign, page;
3001590Srgrimes{
3011590Srgrimes	register *ip;
3021590Srgrimes	register struct message *mp;
3031590Srgrimes	register char *cp;
3041590Srgrimes	int nlines;
3051590Srgrimes	FILE *obuf;
3061590Srgrimes
3071590Srgrimes	obuf = stdout;
3081590Srgrimes	if (setjmp(pipestop))
3091590Srgrimes		goto close_pipe;
3101590Srgrimes	if (value("interactive") != NOSTR &&
3111590Srgrimes	    (page || (cp = value("crt")) != NOSTR)) {
3121590Srgrimes		nlines = 0;
3131590Srgrimes		if (!page) {
3141590Srgrimes			for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++)
3151590Srgrimes				nlines += message[*ip - 1].m_lines;
3161590Srgrimes		}
3171590Srgrimes		if (page || nlines > (*cp ? atoi(cp) : realscreenheight)) {
3181590Srgrimes			cp = value("PAGER");
3191590Srgrimes			if (cp == NULL || *cp == '\0')
3201590Srgrimes				cp = _PATH_MORE;
3211590Srgrimes			obuf = Popen(cp, "w");
3221590Srgrimes			if (obuf == NULL) {
32374769Smikeh				warnx("%s", cp);
3241590Srgrimes				obuf = stdout;
3251590Srgrimes			} else
3261590Srgrimes				signal(SIGPIPE, brokpipe);
3271590Srgrimes		}
3281590Srgrimes	}
3291590Srgrimes	for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) {
3301590Srgrimes		mp = &message[*ip - 1];
3311590Srgrimes		touch(mp);
3321590Srgrimes		dot = mp;
3331590Srgrimes		if (value("quiet") == NOSTR)
3341590Srgrimes			fprintf(obuf, "Message %d:\n", *ip);
33574769Smikeh		(void) sendmessage(mp, obuf, doign ? ignore : 0, NOSTR);
3361590Srgrimes	}
3371590Srgrimesclose_pipe:
3381590Srgrimes	if (obuf != stdout) {
3391590Srgrimes		/*
3401590Srgrimes		 * Ignore SIGPIPE so it can't cause a duplicate close.
3411590Srgrimes		 */
3421590Srgrimes		signal(SIGPIPE, SIG_IGN);
3431590Srgrimes		Pclose(obuf);
3441590Srgrimes		signal(SIGPIPE, SIG_DFL);
3451590Srgrimes	}
3461590Srgrimes	return(0);
3471590Srgrimes}
3481590Srgrimes
3491590Srgrimes/*
3501590Srgrimes * Respond to a broken pipe signal --
3511590Srgrimes * probably caused by quitting more.
3521590Srgrimes */
3531590Srgrimesvoid
3541590Srgrimesbrokpipe(signo)
3551590Srgrimes	int signo;
3561590Srgrimes{
3571590Srgrimes	longjmp(pipestop, 1);
3581590Srgrimes}
3591590Srgrimes
3601590Srgrimes/*
3611590Srgrimes * Print the top so many lines of each desired message.
3621590Srgrimes * The number of lines is taken from the variable "toplines"
3631590Srgrimes * and defaults to 5.
3641590Srgrimes */
3651590Srgrimesint
3661590Srgrimestop(msgvec)
3671590Srgrimes	int *msgvec;
3681590Srgrimes{
3691590Srgrimes	register int *ip;
3701590Srgrimes	register struct message *mp;
3711590Srgrimes	int c, topl, lines, lineb;
3721590Srgrimes	char *valtop, linebuf[LINESIZE];
3731590Srgrimes	FILE *ibuf;
3741590Srgrimes
3751590Srgrimes	topl = 5;
3761590Srgrimes	valtop = value("toplines");
3771590Srgrimes	if (valtop != NOSTR) {
3781590Srgrimes		topl = atoi(valtop);
3791590Srgrimes		if (topl < 0 || topl > 10000)
3801590Srgrimes			topl = 5;
3811590Srgrimes	}
3821590Srgrimes	lineb = 1;
3831590Srgrimes	for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
3841590Srgrimes		mp = &message[*ip - 1];
3851590Srgrimes		touch(mp);
3861590Srgrimes		dot = mp;
3871590Srgrimes		if (value("quiet") == NOSTR)
3881590Srgrimes			printf("Message %d:\n", *ip);
3891590Srgrimes		ibuf = setinput(mp);
3901590Srgrimes		c = mp->m_lines;
3911590Srgrimes		if (!lineb)
3921590Srgrimes			printf("\n");
3931590Srgrimes		for (lines = 0; lines < c && lines <= topl; lines++) {
39474769Smikeh			if (readline(ibuf, linebuf, sizeof(linebuf)) < 0)
3951590Srgrimes				break;
3961590Srgrimes			puts(linebuf);
39774769Smikeh			lineb = strspn(linebuf, " \t") == strlen(linebuf);
3981590Srgrimes		}
3991590Srgrimes	}
4001590Srgrimes	return(0);
4011590Srgrimes}
4021590Srgrimes
4031590Srgrimes/*
4041590Srgrimes * Touch all the given messages so that they will
4051590Srgrimes * get mboxed.
4061590Srgrimes */
4071590Srgrimesint
4081590Srgrimesstouch(msgvec)
4091590Srgrimes	int msgvec[];
4101590Srgrimes{
4111590Srgrimes	register int *ip;
4121590Srgrimes
4131590Srgrimes	for (ip = msgvec; *ip != 0; ip++) {
4141590Srgrimes		dot = &message[*ip-1];
4151590Srgrimes		dot->m_flag |= MTOUCH;
4161590Srgrimes		dot->m_flag &= ~MPRESERVE;
4171590Srgrimes	}
4181590Srgrimes	return(0);
4191590Srgrimes}
4201590Srgrimes
4211590Srgrimes/*
4221590Srgrimes * Make sure all passed messages get mboxed.
4231590Srgrimes */
4241590Srgrimesint
4251590Srgrimesmboxit(msgvec)
4261590Srgrimes	int msgvec[];
4271590Srgrimes{
4281590Srgrimes	register int *ip;
4291590Srgrimes
4301590Srgrimes	for (ip = msgvec; *ip != 0; ip++) {
4311590Srgrimes		dot = &message[*ip-1];
4321590Srgrimes		dot->m_flag |= MTOUCH|MBOX;
4331590Srgrimes		dot->m_flag &= ~MPRESERVE;
4341590Srgrimes	}
4351590Srgrimes	return(0);
4361590Srgrimes}
4371590Srgrimes
4381590Srgrimes/*
4391590Srgrimes * List the folders the user currently has.
4401590Srgrimes */
4411590Srgrimesint
4421590Srgrimesfolders()
4431590Srgrimes{
44474769Smikeh	char dirname[PATHSIZE];
4451590Srgrimes	char *cmd;
4461590Srgrimes
44774769Smikeh	if (getfold(dirname, sizeof(dirname)) < 0) {
4481590Srgrimes		printf("No value set for \"folder\"\n");
4491590Srgrimes		return 1;
4501590Srgrimes	}
4511590Srgrimes	if ((cmd = value("LISTER")) == NOSTR)
4521590Srgrimes		cmd = "ls";
4531590Srgrimes	(void) run_command(cmd, 0, -1, -1, dirname, NOSTR, NOSTR);
4541590Srgrimes	return 0;
4551590Srgrimes}
456