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