Home | History | Annotate | Line # | Download | only in mail
cmd1.c revision 1.26
      1 /*	$NetBSD: cmd1.c,v 1.26 2006/10/21 21:37:20 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. Neither the name of the University nor the names of its contributors
     16  *    may be used to endorse or promote products derived from this software
     17  *    without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     29  * SUCH DAMAGE.
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 #ifndef lint
     34 #if 0
     35 static char sccsid[] = "@(#)cmd1.c	8.2 (Berkeley) 4/20/95";
     36 #else
     37 __RCSID("$NetBSD: cmd1.c,v 1.26 2006/10/21 21:37:20 christos Exp $");
     38 #endif
     39 #endif /* not lint */
     40 
     41 #include "rcv.h"
     42 #include "extern.h"
     43 #ifdef MIME_SUPPORT
     44 #include "mime.h"
     45 #endif
     46 
     47 /*
     48  * Mail -- a mail program
     49  *
     50  * User commands.
     51  */
     52 
     53 /*
     54  * Print the current active headings.
     55  * Don't change dot if invoker didn't give an argument.
     56  */
     57 
     58 static int screen;
     59 
     60 int
     61 headers(void *v)
     62 {
     63 	int *msgvec = v;
     64 	int n, mesg, flag;
     65 	struct message *mp;
     66 	int size;
     67 
     68 	size = screensize();
     69 	n = msgvec[0];
     70 	if (n != 0)
     71 		screen = (n-1)/size;
     72 	if (screen < 0)
     73 		screen = 0;
     74 	mp = &message[screen * size];
     75 	if (mp >= &message[msgCount])
     76 		mp = &message[msgCount - size];
     77 	if (mp < &message[0])
     78 		mp = &message[0];
     79 	flag = 0;
     80 	mesg = mp - &message[0];
     81 	if (dot != &message[n - 1])
     82 		dot = mp;
     83 	for (; mp < &message[msgCount]; mp++) {
     84 		mesg++;
     85 		if (mp->m_flag & MDELETED)
     86 			continue;
     87 		if (flag++ >= size)
     88 			break;
     89 		printhead(mesg);
     90 	}
     91 	if (flag == 0) {
     92 		(void)printf("No more mail.\n");
     93 		return(1);
     94 	}
     95 	return(0);
     96 }
     97 
     98 /*
     99  * Scroll to the next/previous screen
    100  */
    101 int
    102 scroll(void *v)
    103 {
    104 	char *arg = v;
    105 	int s, size;
    106 	int cur[1];
    107 
    108 	cur[0] = 0;
    109 	size = screensize();
    110 	s = screen;
    111 	switch (*arg) {
    112 	case 0:
    113 	case '+':
    114 		s++;
    115 		if (s * size >= msgCount) {
    116 			(void)printf("On last screenful of messages\n");
    117 			return(0);
    118 		}
    119 		screen = s;
    120 		break;
    121 
    122 	case '-':
    123 		if (--s < 0) {
    124 			(void)printf("On first screenful of messages\n");
    125 			return(0);
    126 		}
    127 		screen = s;
    128 		break;
    129 
    130 	default:
    131 		(void)printf("Unrecognized scrolling command \"%s\"\n", arg);
    132 		return(1);
    133 	}
    134 	return(headers(cur));
    135 }
    136 
    137 /*
    138  * Compute screen size.
    139  */
    140 int
    141 screensize(void)
    142 {
    143 	int s;
    144 	char *cp;
    145 
    146 	if ((cp = value("screen")) != NULL && (s = atoi(cp)) > 0)
    147 		return s;
    148 	return screenheight - 4;
    149 }
    150 
    151 /*
    152  * Print out the headlines for each message
    153  * in the passed message list.
    154  */
    155 int
    156 from(void *v)
    157 {
    158 	int *msgvec = v;
    159 	int *ip;
    160 
    161 	for (ip = msgvec; *ip != 0; ip++)
    162 		printhead(*ip);
    163 	if (--ip >= msgvec)
    164 		dot = &message[*ip - 1];
    165 	return(0);
    166 }
    167 
    168 /*
    169  * Print out the header of a specific message.
    170  * This is a slight improvement to the standard one.
    171  */
    172 void
    173 printhead(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)mail_readline(setinput(mp), headline, LINESIZE);
    184 	if ((subjline = hfield("subject", mp)) == NULL)
    185 		subjline = hfield("subj", mp);
    186 
    187 	/*
    188 	 * Bletch!
    189 	 */
    190 	curind = dot == mp ? '>' : ' ';
    191 	dispc = ' ';
    192 	if (mp->m_flag & MSAVED)
    193 		dispc = '*';
    194 	if (mp->m_flag & MPRESERVE)
    195 		dispc = 'P';
    196 	if ((mp->m_flag & (MREAD|MNEW)) == MNEW)
    197 		dispc = 'N';
    198 	if ((mp->m_flag & (MREAD|MNEW)) == 0)
    199 		dispc = 'U';
    200 	if (mp->m_flag & MBOX)
    201 		dispc = 'M';
    202 	parse(headline, &hl, pbuf);
    203 	(void)snprintf(wcount, LINESIZE, "%3ld/%-5llu", mp->m_blines,
    204 	    (unsigned long long)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 		(void)printf("%c%c%3d %-20.20s  %16.16s %s\n",
    210 			curind, dispc, mesg, name, hl.l_date, wcount);
    211 	else
    212 		(void)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 /*ARGSUSED*/
    222 pdot(void *v __unused)
    223 {
    224 	(void)printf("%d\n", (int)(dot - &message[0] + 1));
    225 	return(0);
    226 }
    227 
    228 /*
    229  * Print out all the possible commands.
    230  */
    231 int
    232 /*ARGSUSED*/
    233 pcmdlist(void *v __unused)
    234 {
    235 	const struct cmd *cp;
    236 	int cc;
    237 
    238 	(void)printf("Commands are:\n");
    239 	for (cc = 0, cp = cmdtab; cp->c_name != NULL; cp++) {
    240 		cc += strlen(cp->c_name) + 2;
    241 		if (cc > 72) {
    242 			(void)printf("\n");
    243 			cc = strlen(cp->c_name) + 2;
    244 		}
    245 		if ((cp + 1)->c_name != NULL)
    246 			(void)printf("%s, ", cp->c_name);
    247 		else
    248 			(void)printf("%s\n", cp->c_name);
    249 	}
    250 	return(0);
    251 }
    252 
    253 #ifdef MIME_SUPPORT
    254 static int
    255 de_mime(const char *name)
    256 {
    257 	const char *p;
    258 	const char *list;
    259 
    260 #define DELIM	" \t,"	/* list of string delimiters */
    261 
    262 	list = value(ENAME_MIME_DECODE_MSG);
    263 	if (list == NULL)
    264 		return 0;
    265 
    266 	if (list[0] == '\0')
    267 		return 1;
    268 
    269 	p = strcasestr(list, name);
    270 	if (p == NULL)
    271 		return 0;
    272 
    273 	return strchr(DELIM, p[strlen(name)]) && (
    274 		p == list || strchr(DELIM, p[-1]));
    275 
    276 #undef DELIM
    277 }
    278 #endif /* MIME_SUPPORT */
    279 
    280 /*
    281  * Paginate messages, honor ignored fields.
    282  */
    283 int
    284 more(void *v)
    285 {
    286 	int *msgvec = v;
    287 #ifdef MIME_SUPPORT
    288 	return type1(msgvec, 1, 1, de_mime("more"));
    289 #else
    290 	return(type1(msgvec, 1, 1));
    291 #endif
    292 
    293 }
    294 
    295 /*
    296  * Paginate messages, even printing ignored fields.
    297  */
    298 int
    299 More(void *v)
    300 {
    301 	int *msgvec = v;
    302 
    303 #ifdef MIME_SUPPORT
    304 	return type1(msgvec, 0, 1, de_mime("more"));
    305 #else
    306 	return(type1(msgvec, 0, 1));
    307 #endif
    308 }
    309 
    310 /*
    311  * Type out messages, honor ignored fields.
    312  */
    313 int
    314 type(void *v)
    315 {
    316 	int *msgvec = v;
    317 
    318 #ifdef MIME_SUPPORT
    319 	return type1(msgvec, 1, 0, de_mime("type"));
    320 #else
    321 	return(type1(msgvec, 1, 0));
    322 #endif
    323 }
    324 
    325 /*
    326  * Type out messages, even printing ignored fields.
    327  */
    328 int
    329 Type(void *v)
    330 {
    331 	int *msgvec = v;
    332 
    333 #ifdef MIME_SUPPORT
    334 	return type1(msgvec, 0, 0, de_mime("type"));
    335 #else
    336 	return(type1(msgvec, 0, 0));
    337 #endif
    338 }
    339 
    340 
    341 #ifdef MIME_SUPPORT
    342 /*
    343  * Paginate messages, honor ignored fields.
    344  */
    345 int
    346 page(void *v)
    347 {
    348 	int *msgvec = v;
    349 #ifdef MIME_SUPPORT
    350 	return type1(msgvec, 1, 1, de_mime("page"));
    351 #else
    352 	return(type1(msgvec, 1, 1));
    353 #endif
    354 
    355 }
    356 
    357 /*
    358  * Paginate messages, even printing ignored fields.
    359  */
    360 int
    361 Page(void *v)
    362 {
    363 	int *msgvec = v;
    364 
    365 #ifdef MIME_SUPPORT
    366 	return type1(msgvec, 0, 1, de_mime("page"));
    367 #else
    368 	return(type1(msgvec, 0, 1));
    369 #endif
    370 }
    371 
    372 /*
    373  * Type out messages, honor ignored fields.
    374  */
    375 int
    376 print(void *v)
    377 {
    378 	int *msgvec = v;
    379 
    380 #ifdef MIME_SUPPORT
    381 	return type1(msgvec, 1, 0, de_mime("print"));
    382 #else
    383 	return(type1(msgvec, 1, 0));
    384 #endif
    385 }
    386 
    387 /*
    388  * Type out messages, even printing ignored fields.
    389  */
    390 int
    391 Print(void *v)
    392 {
    393 	int *msgvec = v;
    394 
    395 #ifdef MIME_SUPPORT
    396 	return type1(msgvec, 0, 0, de_mime("print"));
    397 #else
    398 	return(type1(msgvec, 0, 0));
    399 #endif
    400 }
    401 
    402 /*
    403  * Identical to type(), but with opposite mime behavior.
    404  */
    405 int
    406 view(void *v)
    407 {
    408 	int *msgvec = v;
    409 	return type1(msgvec, 1, 0, !de_mime("print"));
    410 }
    411 
    412 /*
    413  * Identical to Type(), but with opposite mime behavior.
    414  */
    415 int
    416 View(void *v)
    417 {
    418 	int *msgvec = v;
    419 
    420 	return type1(msgvec, 0, 0, !de_mime("print"));
    421 }
    422 #endif /* MIME_SUPPORT */
    423 
    424 /*
    425  * Type out the messages requested.
    426  */
    427 jmp_buf	pipestop;
    428 int
    429 #ifdef MIME_SUPPORT
    430 type1(int *msgvec, int doign, int dopage, int mime_decode)
    431 #else
    432 type1(int *msgvec, int doign, int dopage)
    433 #endif
    434 {
    435 	int *ip;
    436 	struct message *mp;
    437 	const char *cp;
    438 	int nlines;
    439 
    440 	/* Some volatile variables so longjmp will get the current not
    441 	 * starting values.  Note it is the variable that is volatile,
    442 	 * not what it is pointing at! */
    443 #ifdef MIME_SUPPORT
    444 	struct mime_info *volatile mip; /* avoid longjmp clobbering */
    445 #endif
    446 	FILE *volatile obuf;		/* avoid longjmp clobbering */
    447 
    448 #ifdef MIME_SUPPORT
    449 	mip = NULL;
    450 #endif
    451 	obuf = stdout;
    452 	if (setjmp(pipestop))
    453 		goto close_pipe;
    454 	if (value("interactive") != NULL &&
    455 	    (dopage || (cp = value("crt")) != NULL)) {
    456 		nlines = 0;
    457 		if (!dopage) {
    458 			for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++)
    459 				nlines += message[*ip - 1].m_blines;
    460 		}
    461 		if (dopage || nlines > (*cp ? atoi(cp) : realscreenheight)) {
    462 			cp = value("PAGER");
    463 			if (cp == NULL || *cp == '\0')
    464 				cp = _PATH_MORE;
    465 			obuf = Popen(cp, "w");
    466 			if (obuf == NULL) {
    467 				warn("%s", cp);
    468 				obuf = stdout;
    469 			} else
    470 				(void)signal(SIGPIPE, brokpipe);
    471 		}
    472 	}
    473 	for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) {
    474 		mp = &message[*ip - 1];
    475 		touch(mp);
    476 		dot = mp;
    477 		if (value("quiet") == NULL)
    478 			(void)fprintf(obuf, "Message %d:\n", *ip);
    479 #ifdef MIME_SUPPORT
    480 		if (mime_decode)
    481 			mip = mime_decode_open(mp);
    482 		(void)mime_sendmessage(mp, obuf, doign ? ignore : 0, NULL, mip);
    483 		mime_decode_close(mip);
    484 #else
    485 		(void)sendmessage(mp, obuf, doign ? ignore : 0, NULL);
    486 #endif
    487 	}
    488 close_pipe:
    489 #ifdef MIME_SUPPORT
    490 	if (mip != NULL || obuf != stdout) {
    491 		/*
    492 		 * Ignore SIGPIPE so it can't cause a duplicate close.
    493 		 */
    494 		(void)signal(SIGPIPE, SIG_IGN);
    495 		mime_decode_close(mip);
    496 		if (obuf != stdout)
    497 			(void)Pclose(obuf);
    498 		(void)signal(SIGPIPE, SIG_DFL);
    499 
    500 	}
    501 #else
    502 	if (obuf != stdout) {
    503 		/*
    504 		 * Ignore SIGPIPE so it can't cause a duplicate close.
    505 		 */
    506 		(void)signal(SIGPIPE, SIG_IGN);
    507 		(void)Pclose(obuf);
    508 		(void)signal(SIGPIPE, SIG_DFL);
    509 	}
    510 #endif
    511 	return(0);
    512 }
    513 
    514 /*
    515  * Respond to a broken pipe signal --
    516  * probably caused by quitting more.
    517  */
    518 void
    519 /*ARGSUSED*/
    520 brokpipe(int signo __unused)
    521 {
    522 	longjmp(pipestop, 1);
    523 }
    524 
    525 /*
    526  * Pipe the current message buffer to a command.
    527  */
    528 int
    529 pipecmd(void *v)
    530 {
    531 	char *cmd = v;
    532 	FILE *volatile obuf;	/* void longjmp clobbering - we want
    533 				   the current value not start value */
    534 	if (dot == NULL) {
    535 		warn("pipcmd: no current message");
    536 		return 1;
    537 	}
    538 
    539 	obuf = stdout;
    540 	if (setjmp(pipestop))
    541 		goto close_pipe;
    542 
    543 	obuf = Popen(cmd, "w");
    544 	if (obuf == NULL) {
    545 		warn("pipecmd: Popen failed: %s", cmd);
    546 		return 1;
    547 	} else
    548 		(void)signal(SIGPIPE, brokpipe);
    549 
    550 #ifdef MIME_SUPPORT
    551 	(void)sendmessage(dot, obuf, ignoreall, NULL, NULL);
    552 #else
    553 	(void)sendmessage(dot, obuf, ignoreall, NULL);
    554 #endif
    555 
    556  close_pipe:
    557 	if (obuf != stdout) {
    558 		/*
    559 		 * Ignore SIGPIPE so it can't cause a duplicate close.
    560 		 */
    561 		(void)signal(SIGPIPE, SIG_IGN);
    562 		(void)Pclose(obuf);
    563 		(void)signal(SIGPIPE, SIG_DFL);
    564 	}
    565 	return 0;
    566 }
    567 
    568 /*
    569  * Print the top so many lines of each desired message.
    570  * The number of lines is taken from the variable "toplines"
    571  * and defaults to 5.
    572  */
    573 int
    574 top(void *v)
    575 {
    576 	int *msgvec = v;
    577 	int *ip;
    578 	struct message *mp;
    579 	int c, topl, lines, lineb;
    580 	char *valtop, linebuf[LINESIZE];
    581 	FILE *ibuf;
    582 
    583 	topl = 5;
    584 	valtop = value("toplines");
    585 	if (valtop != NULL) {
    586 		topl = atoi(valtop);
    587 		if (topl < 0 || topl > 10000)
    588 			topl = 5;
    589 	}
    590 	lineb = 1;
    591 	for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
    592 		mp = &message[*ip - 1];
    593 		touch(mp);
    594 		dot = mp;
    595 		if (value("quiet") == NULL)
    596 			(void)printf("Message %d:\n", *ip);
    597 		ibuf = setinput(mp);
    598 		c = mp->m_lines;
    599 		if (!lineb)
    600 			(void)printf("\n");
    601 		for (lines = 0; lines < c && lines <= topl; lines++) {
    602 			if (mail_readline(ibuf, linebuf, LINESIZE) < 0)
    603 				break;
    604 			(void)puts(linebuf);
    605 			lineb = blankline(linebuf);
    606 		}
    607 	}
    608 	return(0);
    609 }
    610 
    611 /*
    612  * Touch all the given messages so that they will
    613  * get mboxed.
    614  */
    615 int
    616 stouch(void *v)
    617 {
    618 	int *msgvec = v;
    619 	int *ip;
    620 
    621 	for (ip = msgvec; *ip != 0; ip++) {
    622 		dot = &message[*ip - 1];
    623 		dot->m_flag |= MTOUCH;
    624 		dot->m_flag &= ~MPRESERVE;
    625 	}
    626 	return(0);
    627 }
    628 
    629 /*
    630  * Make sure all passed messages get mboxed.
    631  */
    632 int
    633 mboxit(void *v)
    634 {
    635 	int *msgvec = v;
    636 	int *ip;
    637 
    638 	for (ip = msgvec; *ip != 0; ip++) {
    639 		dot = &message[*ip - 1];
    640 		dot->m_flag |= MTOUCH|MBOX;
    641 		dot->m_flag &= ~MPRESERVE;
    642 	}
    643 	return(0);
    644 }
    645 
    646 /*
    647  * List the folders the user currently has.
    648  */
    649 int
    650 /*ARGSUSED*/
    651 folders(void *v __unused)
    652 {
    653 	char dirname[PATHSIZE];
    654 	const char *cmd;
    655 
    656 	if (getfold(dirname) < 0) {
    657 		(void)printf("No value set for \"folder\"\n");
    658 		return 1;
    659 	}
    660 	if ((cmd = value("LISTER")) == NULL)
    661 		cmd = "ls";
    662 	(void)run_command(cmd, 0, -1, -1, dirname, NULL);
    663 	return 0;
    664 }
    665 
    666 /*
    667  * Update the mail file with any new messages that have
    668  * come in since we started reading mail.
    669  */
    670 int
    671 /*ARGSUSED*/
    672 inc(void *v __unused)
    673 {
    674 	int nmsg, mdot;
    675 
    676 	nmsg = incfile();
    677 
    678 	if (nmsg == 0) {
    679 	(void)printf("No new mail.\n");
    680 	} else if (nmsg > 0) {
    681 		mdot = newfileinfo(msgCount - nmsg);
    682 		dot = &message[mdot - 1];
    683 	} else {
    684 	(void)printf("\"inc\" command failed...\n");
    685 	}
    686 
    687 	return 0;
    688 }
    689