Home | History | Annotate | Line # | Download | only in mail
cmd1.c revision 1.23
      1 /*	$NetBSD: cmd1.c,v 1.23 2005/07/19 23:07:10 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.23 2005/07/19 23:07:10 christos Exp $");
     38 #endif
     39 #endif /* not lint */
     40 
     41 #include "rcv.h"
     42 #include "extern.h"
     43 
     44 /*
     45  * Mail -- a mail program
     46  *
     47  * User commands.
     48  */
     49 
     50 /*
     51  * Print the current active headings.
     52  * Don't change dot if invoker didn't give an argument.
     53  */
     54 
     55 static int screen;
     56 
     57 int
     58 headers(void *v)
     59 {
     60 	int *msgvec = v;
     61 	int n, mesg, flag;
     62 	struct message *mp;
     63 	int size;
     64 
     65 	size = screensize();
     66 	n = msgvec[0];
     67 	if (n != 0)
     68 		screen = (n-1)/size;
     69 	if (screen < 0)
     70 		screen = 0;
     71 	mp = &message[screen * size];
     72 	if (mp >= &message[msgCount])
     73 		mp = &message[msgCount - size];
     74 	if (mp < &message[0])
     75 		mp = &message[0];
     76 	flag = 0;
     77 	mesg = mp - &message[0];
     78 	if (dot != &message[n-1])
     79 		dot = mp;
     80 	for (; mp < &message[msgCount]; mp++) {
     81 		mesg++;
     82 		if (mp->m_flag & MDELETED)
     83 			continue;
     84 		if (flag++ >= size)
     85 			break;
     86 		printhead(mesg);
     87 	}
     88 	if (flag == 0) {
     89 		(void)printf("No more mail.\n");
     90 		return(1);
     91 	}
     92 	return(0);
     93 }
     94 
     95 /*
     96  * Scroll to the next/previous screen
     97  */
     98 int
     99 scroll(void *v)
    100 {
    101 	char *arg = v;
    102 	int s, size;
    103 	int cur[1];
    104 
    105 	cur[0] = 0;
    106 	size = screensize();
    107 	s = screen;
    108 	switch (*arg) {
    109 	case 0:
    110 	case '+':
    111 		s++;
    112 		if (s * size >= msgCount) {
    113 			(void)printf("On last screenful of messages\n");
    114 			return(0);
    115 		}
    116 		screen = s;
    117 		break;
    118 
    119 	case '-':
    120 		if (--s < 0) {
    121 			(void)printf("On first screenful of messages\n");
    122 			return(0);
    123 		}
    124 		screen = s;
    125 		break;
    126 
    127 	default:
    128 		(void)printf("Unrecognized scrolling command \"%s\"\n", arg);
    129 		return(1);
    130 	}
    131 	return(headers(cur));
    132 }
    133 
    134 /*
    135  * Compute screen size.
    136  */
    137 int
    138 screensize(void)
    139 {
    140 	int s;
    141 	char *cp;
    142 
    143 	if ((cp = value("screen")) != NULL && (s = atoi(cp)) > 0)
    144 		return s;
    145 	return screenheight - 4;
    146 }
    147 
    148 /*
    149  * Print out the headlines for each message
    150  * in the passed message list.
    151  */
    152 int
    153 from(void *v)
    154 {
    155 	int *msgvec = v;
    156 	int *ip;
    157 
    158 	for (ip = msgvec; *ip != 0; ip++)
    159 		printhead(*ip);
    160 	if (--ip >= msgvec)
    161 		dot = &message[*ip - 1];
    162 	return(0);
    163 }
    164 
    165 /*
    166  * Print out the header of a specific message.
    167  * This is a slight improvement to the standard one.
    168  */
    169 void
    170 printhead(int mesg)
    171 {
    172 	struct message *mp;
    173 	char headline[LINESIZE], wcount[LINESIZE], *subjline, dispc, curind;
    174 	char pbuf[BUFSIZ];
    175 	struct headline hl;
    176 	int subjlen;
    177 	char *name;
    178 
    179 	mp = &message[mesg-1];
    180 	(void)readline(setinput(mp), headline, LINESIZE);
    181 	if ((subjline = hfield("subject", mp)) == NULL)
    182 		subjline = hfield("subj", mp);
    183 	/*
    184 	 * Bletch!
    185 	 */
    186 	curind = dot == mp ? '>' : ' ';
    187 	dispc = ' ';
    188 	if (mp->m_flag & MSAVED)
    189 		dispc = '*';
    190 	if (mp->m_flag & MPRESERVE)
    191 		dispc = 'P';
    192 	if ((mp->m_flag & (MREAD|MNEW)) == MNEW)
    193 		dispc = 'N';
    194 	if ((mp->m_flag & (MREAD|MNEW)) == 0)
    195 		dispc = 'U';
    196 	if (mp->m_flag & MBOX)
    197 		dispc = 'M';
    198 	parse(headline, &hl, pbuf);
    199 	(void)snprintf(wcount, LINESIZE, "%3ld/%-5llu", mp->m_blines,
    200 	    /*LINTED*/
    201 	    (unsigned long long)mp->m_size);
    202 	subjlen = screenwidth - 50 - strlen(wcount);
    203 	name = value("show-rcpt") != NULL ?
    204 		skin(hfield("to", mp)) : nameof(mp, 0);
    205 	if (subjline == NULL || subjlen < 0)		/* pretty pathetic */
    206 		(void)printf("%c%c%3d %-20.20s  %16.16s %s\n",
    207 			curind, dispc, mesg, name, hl.l_date, wcount);
    208 	else
    209 		(void)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  */
    217 int
    218 /*ARGSUSED*/
    219 pdot(void *v)
    220 {
    221 	(void)printf("%d\n", (int)(dot - &message[0] + 1));
    222 	return(0);
    223 }
    224 
    225 /*
    226  * Print out all the possible commands.
    227  */
    228 int
    229 /*ARGSUSED*/
    230 pcmdlist(void *v)
    231 {
    232 	const struct cmd *cp;
    233 	int cc;
    234 
    235 	(void)printf("Commands are:\n");
    236 	for (cc = 0, cp = cmdtab; cp->c_name != NULL; cp++) {
    237 		cc += strlen(cp->c_name) + 2;
    238 		if (cc > 72) {
    239 			(void)printf("\n");
    240 			cc = strlen(cp->c_name) + 2;
    241 		}
    242 		if ((cp+1)->c_name != NULL)
    243 			(void)printf("%s, ", cp->c_name);
    244 		else
    245 			(void)printf("%s\n", cp->c_name);
    246 	}
    247 	return(0);
    248 }
    249 
    250 /*
    251  * Paginate messages, honor ignored fields.
    252  */
    253 int
    254 more(void *v)
    255 {
    256 	int *msgvec = v;
    257 	return (type1(msgvec, 1, 1));
    258 }
    259 
    260 /*
    261  * Paginate messages, even printing ignored fields.
    262  */
    263 int
    264 More(void *v)
    265 {
    266 	int *msgvec = v;
    267 
    268 	return (type1(msgvec, 0, 1));
    269 }
    270 
    271 /*
    272  * Type out messages, honor ignored fields.
    273  */
    274 int
    275 type(void *v)
    276 {
    277 	int *msgvec = v;
    278 
    279 	return(type1(msgvec, 1, 0));
    280 }
    281 
    282 /*
    283  * Type out messages, even printing ignored fields.
    284  */
    285 int
    286 Type(void *v)
    287 {
    288 	int *msgvec = v;
    289 
    290 	return(type1(msgvec, 0, 0));
    291 }
    292 
    293 /*
    294  * Type out the messages requested.
    295  */
    296 jmp_buf	pipestop;
    297 int
    298 type1(int *msgvec, int doign, int page)
    299 {
    300 	int *ip;
    301 	struct message *mp;
    302 	const char *cp;
    303 	int nlines;
    304 	FILE *obuf;
    305 #if __GNUC__
    306 	/* Avoid longjmp clobbering */
    307 	(void)&cp;
    308 	(void)&obuf;
    309 #endif
    310 
    311 	obuf = stdout;
    312 	if (setjmp(pipestop))
    313 		goto close_pipe;
    314 	if (value("interactive") != NULL &&
    315 	    (page || (cp = value("crt")) != NULL)) {
    316 		nlines = 0;
    317 		if (!page) {
    318 			for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++)
    319 				nlines += message[*ip - 1].m_blines;
    320 		}
    321 		if (page || nlines > (*cp ? atoi(cp) : realscreenheight)) {
    322 			cp = value("PAGER");
    323 			if (cp == NULL || *cp == '\0')
    324 				cp = _PATH_MORE;
    325 			obuf = Popen(cp, "w");
    326 			if (obuf == NULL) {
    327 				warn("%s", cp);
    328 				obuf = stdout;
    329 			} else
    330 				(void)signal(SIGPIPE, brokpipe);
    331 		}
    332 	}
    333 	for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) {
    334 		mp = &message[*ip - 1];
    335 		touch(mp);
    336 		dot = mp;
    337 		if (value("quiet") == NULL)
    338 			(void)fprintf(obuf, "Message %d:\n", *ip);
    339 		(void)sendmessage(mp, obuf, doign ? ignore : 0, NULL);
    340 	}
    341 close_pipe:
    342 	if (obuf != stdout) {
    343 		/*
    344 		 * Ignore SIGPIPE so it can't cause a duplicate close.
    345 		 */
    346 		(void)signal(SIGPIPE, SIG_IGN);
    347 		(void)Pclose(obuf);
    348 		(void)signal(SIGPIPE, SIG_DFL);
    349 	}
    350 	return(0);
    351 }
    352 
    353 /*
    354  * Respond to a broken pipe signal --
    355  * probably caused by quitting more.
    356  */
    357 void
    358 /*ARGSUSED*/
    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 			(void)printf("Message %d:\n", *ip);
    393 		ibuf = setinput(mp);
    394 		c = mp->m_lines;
    395 		if (!lineb)
    396 			(void)printf("\n");
    397 		for (lines = 0; lines < c && lines <= topl; lines++) {
    398 			if (readline(ibuf, linebuf, LINESIZE) < 0)
    399 				break;
    400 			(void)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 /*ARGSUSED*/
    447 folders(void *v)
    448 {
    449 	char dirname[PATHSIZE];
    450 	const char *cmd;
    451 
    452 	if (getfold(dirname) < 0) {
    453 		(void)printf("No value set for \"folder\"\n");
    454 		return 1;
    455 	}
    456 	if ((cmd = value("LISTER")) == NULL)
    457 		cmd = "ls";
    458 	(void)run_command(cmd, 0, -1, -1, dirname, NULL);
    459 	return 0;
    460 }
    461 
    462 /*
    463  * Update the mail file with any new messages that have
    464  * come in since we started reading mail.
    465  */
    466 int
    467 /*ARGSUSED*/
    468 inc(void *v)
    469 {
    470 	int nmsg, mdot;
    471 
    472 	nmsg = incfile();
    473 
    474 	if (nmsg == 0) {
    475 	(void)printf("No new mail.\n");
    476 	} else if (nmsg > 0) {
    477 		mdot = newfileinfo(msgCount - nmsg);
    478 		dot = &message[mdot - 1];
    479 	} else {
    480 	(void)printf("\"inc\" command failed...\n");
    481 	}
    482 
    483 	return 0;
    484 }
    485