Home | History | Annotate | Line # | Download | only in mail
cmd1.c revision 1.20
      1 /*	$NetBSD: cmd1.c,v 1.20 2003/03/29 21:41:04 christos Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 1980, 1993
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. All advertising materials mentioning features or use of this software
     16  *    must display the following acknowledgement:
     17  *	This product includes software developed by the University of
     18  *	California, Berkeley and its contributors.
     19  * 4. Neither the name of the University nor the names of its contributors
     20  *    may be used to endorse or promote products derived from this software
     21  *    without specific prior written permission.
     22  *
     23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     33  * SUCH DAMAGE.
     34  */
     35 
     36 #include <sys/cdefs.h>
     37 #ifndef lint
     38 #if 0
     39 static char sccsid[] = "@(#)cmd1.c	8.2 (Berkeley) 4/20/95";
     40 #else
     41 __RCSID("$NetBSD: cmd1.c,v 1.20 2003/03/29 21:41:04 christos 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 extern const struct cmd cmdtab[];
     54 
     55 /*
     56  * Print the current active headings.
     57  * Don't change dot if invoker didn't give an argument.
     58  */
     59 
     60 static int screen;
     61 
     62 int
     63 headers(void *v)
     64 {
     65 	int *msgvec = v;
     66 	int n, mesg, flag;
     67 	struct message *mp;
     68 	int size;
     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  */
    103 int
    104 scroll(void *v)
    105 {
    106 	char *arg = v;
    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  */
    142 int
    143 screensize(void)
    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  */
    157 int
    158 from(void *v)
    159 {
    160 	int *msgvec = v;
    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  */
    174 void
    175 printhead(int mesg)
    176 {
    177 	struct message *mp;
    178 	char headline[LINESIZE], wcount[LINESIZE], *subjline, dispc, curind;
    179 	char pbuf[BUFSIZ];
    180 	struct headline hl;
    181 	int subjlen;
    182 	char *name;
    183 
    184 	mp = &message[mesg-1];
    185 	(void)readline(setinput(mp), headline, LINESIZE);
    186 	if ((subjline = hfield("subject", mp)) == NULL)
    187 		subjline = hfield("subj", mp);
    188 	/*
    189 	 * Bletch!
    190 	 */
    191 	curind = dot == mp ? '>' : ' ';
    192 	dispc = ' ';
    193 	if (mp->m_flag & MSAVED)
    194 		dispc = '*';
    195 	if (mp->m_flag & MPRESERVE)
    196 		dispc = 'P';
    197 	if ((mp->m_flag & (MREAD|MNEW)) == MNEW)
    198 		dispc = 'N';
    199 	if ((mp->m_flag & (MREAD|MNEW)) == 0)
    200 		dispc = 'U';
    201 	if (mp->m_flag & MBOX)
    202 		dispc = 'M';
    203 	parse(headline, &hl, pbuf);
    204 	snprintf(wcount, LINESIZE, "%3ld/%-5ld", mp->m_blines, mp->m_size);
    205 	subjlen = screenwidth - 50 - strlen(wcount);
    206 	name = value("show-rcpt") != NULL ?
    207 		skin(hfield("to", mp)) : nameof(mp, 0);
    208 	if (subjline == NULL || subjlen < 0)		/* pretty pathetic */
    209 		printf("%c%c%3d %-20.20s  %16.16s %s\n",
    210 			curind, dispc, mesg, name, hl.l_date, wcount);
    211 	else
    212 		printf("%c%c%3d %-20.20s  %16.16s %s \"%.*s\"\n",
    213 			curind, dispc, mesg, name, hl.l_date, wcount,
    214 			subjlen, subjline);
    215 }
    216 
    217 /*
    218  * Print out the value of dot.
    219  */
    220 int
    221 pdot(void *v)
    222 {
    223 	printf("%d\n", (int)(dot - &message[0] + 1));
    224 	return(0);
    225 }
    226 
    227 /*
    228  * Print out all the possible commands.
    229  */
    230 int
    231 pcmdlist(void *v)
    232 {
    233 	const struct cmd *cp;
    234 	int cc;
    235 
    236 	printf("Commands are:\n");
    237 	for (cc = 0, cp = cmdtab; cp->c_name != NULL; cp++) {
    238 		cc += strlen(cp->c_name) + 2;
    239 		if (cc > 72) {
    240 			printf("\n");
    241 			cc = strlen(cp->c_name) + 2;
    242 		}
    243 		if ((cp+1)->c_name != NULL)
    244 			printf("%s, ", cp->c_name);
    245 		else
    246 			printf("%s\n", cp->c_name);
    247 	}
    248 	return(0);
    249 }
    250 
    251 /*
    252  * Paginate messages, honor ignored fields.
    253  */
    254 int
    255 more(void *v)
    256 {
    257 	int *msgvec = v;
    258 	return (type1(msgvec, 1, 1));
    259 }
    260 
    261 /*
    262  * Paginate messages, even printing ignored fields.
    263  */
    264 int
    265 More(void *v)
    266 {
    267 	int *msgvec = v;
    268 
    269 	return (type1(msgvec, 0, 1));
    270 }
    271 
    272 /*
    273  * Type out messages, honor ignored fields.
    274  */
    275 int
    276 type(void *v)
    277 {
    278 	int *msgvec = v;
    279 
    280 	return(type1(msgvec, 1, 0));
    281 }
    282 
    283 /*
    284  * Type out messages, even printing ignored fields.
    285  */
    286 int
    287 Type(void *v)
    288 {
    289 	int *msgvec = v;
    290 
    291 	return(type1(msgvec, 0, 0));
    292 }
    293 
    294 /*
    295  * Type out the messages requested.
    296  */
    297 jmp_buf	pipestop;
    298 int
    299 type1(int *msgvec, int doign, int page)
    300 {
    301 	int *ip;
    302 	struct message *mp;
    303 	char *cp;
    304 	int nlines;
    305 	FILE *obuf;
    306 #if __GNUC__
    307 	/* Avoid longjmp clobbering */
    308 	(void)&cp;
    309 	(void)&obuf;
    310 #endif
    311 
    312 	obuf = stdout;
    313 	if (setjmp(pipestop))
    314 		goto close_pipe;
    315 	if (value("interactive") != NULL &&
    316 	    (page || (cp = value("crt")) != NULL)) {
    317 		nlines = 0;
    318 		if (!page) {
    319 			for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++)
    320 				nlines += message[*ip - 1].m_blines;
    321 		}
    322 		if (page || nlines > (*cp ? atoi(cp) : realscreenheight)) {
    323 			cp = value("PAGER");
    324 			if (cp == NULL || *cp == '\0')
    325 				cp = _PATH_MORE;
    326 			obuf = Popen(cp, "w");
    327 			if (obuf == NULL) {
    328 				warn("%s", cp);
    329 				obuf = stdout;
    330 			} else
    331 				signal(SIGPIPE, brokpipe);
    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 close_pipe:
    343 	if (obuf != stdout) {
    344 		/*
    345 		 * Ignore SIGPIPE so it can't cause a duplicate close.
    346 		 */
    347 		signal(SIGPIPE, SIG_IGN);
    348 		Pclose(obuf);
    349 		signal(SIGPIPE, SIG_DFL);
    350 	}
    351 	return(0);
    352 }
    353 
    354 /*
    355  * Respond to a broken pipe signal --
    356  * probably caused by quitting more.
    357  */
    358 void
    359 brokpipe(int signo)
    360 {
    361 	longjmp(pipestop, 1);
    362 }
    363 
    364 /*
    365  * Print the top so many lines of each desired message.
    366  * The number of lines is taken from the variable "toplines"
    367  * and defaults to 5.
    368  */
    369 int
    370 top(void *v)
    371 {
    372 	int *msgvec = v;
    373 	int *ip;
    374 	struct message *mp;
    375 	int c, topl, lines, lineb;
    376 	char *valtop, linebuf[LINESIZE];
    377 	FILE *ibuf;
    378 
    379 	topl = 5;
    380 	valtop = value("toplines");
    381 	if (valtop != NULL) {
    382 		topl = atoi(valtop);
    383 		if (topl < 0 || topl > 10000)
    384 			topl = 5;
    385 	}
    386 	lineb = 1;
    387 	for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
    388 		mp = &message[*ip - 1];
    389 		touch(mp);
    390 		dot = mp;
    391 		if (value("quiet") == NULL)
    392 			printf("Message %d:\n", *ip);
    393 		ibuf = setinput(mp);
    394 		c = mp->m_lines;
    395 		if (!lineb)
    396 			printf("\n");
    397 		for (lines = 0; lines < c && lines <= topl; lines++) {
    398 			if (readline(ibuf, linebuf, LINESIZE) < 0)
    399 				break;
    400 			puts(linebuf);
    401 			lineb = blankline(linebuf);
    402 		}
    403 	}
    404 	return(0);
    405 }
    406 
    407 /*
    408  * Touch all the given messages so that they will
    409  * get mboxed.
    410  */
    411 int
    412 stouch(void *v)
    413 {
    414 	int *msgvec = v;
    415 	int *ip;
    416 
    417 	for (ip = msgvec; *ip != 0; ip++) {
    418 		dot = &message[*ip-1];
    419 		dot->m_flag |= MTOUCH;
    420 		dot->m_flag &= ~MPRESERVE;
    421 	}
    422 	return(0);
    423 }
    424 
    425 /*
    426  * Make sure all passed messages get mboxed.
    427  */
    428 int
    429 mboxit(void *v)
    430 {
    431 	int *msgvec = v;
    432 	int *ip;
    433 
    434 	for (ip = msgvec; *ip != 0; ip++) {
    435 		dot = &message[*ip-1];
    436 		dot->m_flag |= MTOUCH|MBOX;
    437 		dot->m_flag &= ~MPRESERVE;
    438 	}
    439 	return(0);
    440 }
    441 
    442 /*
    443  * List the folders the user currently has.
    444  */
    445 int
    446 folders(void *v)
    447 {
    448 	char dirname[PATHSIZE];
    449 	char *cmd;
    450 
    451 	if (getfold(dirname) < 0) {
    452 		printf("No value set for \"folder\"\n");
    453 		return 1;
    454 	}
    455 	if ((cmd = value("LISTER")) == NULL)
    456 		cmd = "ls";
    457 	(void)run_command(cmd, 0, -1, -1, dirname, NULL);
    458 	return 0;
    459 }
    460 
    461 /*
    462  * Update the mail file with any new messages that have
    463  * come in since we started reading mail.
    464  */
    465 int
    466 inc(void *v)
    467 {
    468 	int nmsg, mdot;
    469 
    470 	nmsg = incfile();
    471 
    472 	if (nmsg == 0) {
    473 	printf("No new mail.\n");
    474 	} else if (nmsg > 0) {
    475 		mdot = newfileinfo(msgCount - nmsg);
    476 		dot = &message[mdot - 1];
    477 	} else {
    478 	printf("\"inc\" command failed...\n");
    479 	}
    480 
    481 	return 0;
    482 }
    483