xref: /openbsd/usr.bin/mail/cmd1.c (revision 78b63d65)
1 /*	$OpenBSD: cmd1.c,v 1.22 2001/11/21 20:41:55 millert Exp $	*/
2 /*	$NetBSD: cmd1.c,v 1.9 1997/07/09 05:29:48 mikel Exp $	*/
3 
4 /*-
5  * Copyright (c) 1980, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by the University of
19  *	California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36 
37 #ifndef lint
38 #if 0
39 static const char sccsid[] = "@(#)cmd1.c	8.2 (Berkeley) 4/20/95";
40 #else
41 static const char rcsid[] = "$OpenBSD: cmd1.c,v 1.22 2001/11/21 20:41:55 millert 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 
54 /*
55  * Print the current active headings.
56  * Don't change dot if invoker didn't give an argument.
57  */
58 
59 static int screen;
60 static volatile sig_atomic_t gothdrint;
61 
62 int
63 headers(void *v)
64 {
65 	int *msgvec = v;
66 	int n, mesg, flag, size;
67 	struct message *mp;
68 	struct sigaction act, oact;
69 	sigset_t oset;
70 
71 	size = screensize();
72 	n = msgvec[0];
73 	if (n != 0)
74 		screen = (n-1)/size;
75 	if (screen < 0)
76 		screen = 0;
77 	mp = &message[screen * size];
78 	if (mp >= &message[msgCount])
79 		mp = &message[msgCount - size];
80 	if (mp < &message[0])
81 		mp = &message[0];
82 	flag = 0;
83 	mesg = mp - &message[0];
84 	if (dot != &message[n-1])
85 		dot = mp;
86 	sigemptyset(&act.sa_mask);
87 	act.sa_flags = SA_RESTART;
88 	act.sa_handler = hdrint;
89 	if (sigaction(SIGINT, NULL, &oact) == 0 &&
90 	    oact.sa_handler != SIG_IGN) {
91 		(void)sigaction(SIGINT, &act, &oact);
92 		(void)sigprocmask(SIG_UNBLOCK, &intset, &oset);
93 	}
94 	for (gothdrint = 0; !gothdrint && mp < &message[msgCount]; mp++) {
95 		mesg++;
96 		if (mp->m_flag & MDELETED)
97 			continue;
98 		if (flag++ >= size)
99 			break;
100 		printhead(mesg);
101 	}
102 	if (gothdrint) {
103 		fflush(stdout);
104 		fputs("\nInterrupt\n", stderr);
105 	}
106 	if (oact.sa_handler != SIG_IGN) {
107 		(void)sigprocmask(SIG_SETMASK, &oset, NULL);
108 		(void)sigaction(SIGINT, &oact, NULL);
109 	}
110 	if (flag == 0) {
111 		puts("No more mail.");
112 		return(1);
113 	}
114 	return(0);
115 }
116 
117 /*
118  * Scroll to the next/previous screen
119  */
120 int
121 scroll(void *v)
122 {
123 	char *arg = v;
124 	int size, maxscreen;
125 	int cur[1];
126 
127 	cur[0] = 0;
128 	size = screensize();
129 	maxscreen = (msgCount - 1) / size;
130 	switch (*arg) {
131 	case 0:
132 	case '+':
133 		if (screen >= maxscreen) {
134 			puts("On last screenful of messages");
135 			return(0);
136 		}
137 		screen++;
138 		break;
139 
140 	case '-':
141 		if (screen <= 0) {
142 			puts("On first screenful of messages");
143 			return(0);
144 		}
145 		screen--;
146 		break;
147 
148 	default:
149 		printf("Unrecognized scrolling command \"%s\"\n", arg);
150 		return(1);
151 	}
152 	return(headers(cur));
153 }
154 
155 /*
156  * Compute screen size.
157  */
158 int
159 screensize(void)
160 {
161 	int s;
162 	char *cp;
163 
164 	if ((cp = value("screen")) != NULL && (s = atoi(cp)) > 0)
165 		return(s);
166 	return(screenheight - 4);
167 }
168 
169 /*
170  * Print out the headlines for each message
171  * in the passed message list.
172  */
173 int
174 from(void *v)
175 {
176 	int *msgvec = v;
177 	int *ip;
178 
179 	for (ip = msgvec; *ip != NULL; ip++)
180 		printhead(*ip);
181 	if (--ip >= msgvec)
182 		dot = &message[*ip - 1];
183 	return(0);
184 }
185 
186 /*
187  * Print out the header of a specific message.
188  * This is a slight improvement to the standard one.
189  */
190 void
191 printhead(int mesg)
192 {
193 	struct message *mp;
194 	char headline[LINESIZE], wcount[LINESIZE], *subjline, dispc, curind;
195 	char pbuf[BUFSIZ];
196 	struct headline hl;
197 	int subjlen;
198 	char *name;
199 	char *to, *from;
200 	struct name *np;
201 	char **ap;
202 
203 	mp = &message[mesg-1];
204 	(void)readline(setinput(mp), headline, LINESIZE, NULL);
205 	if ((subjline = hfield("subject", mp)) == NULL)
206 		subjline = hfield("subj", mp);
207 	/*
208 	 * Bletch!
209 	 */
210 	curind = dot == mp ? '>' : ' ';
211 	dispc = ' ';
212 	if (mp->m_flag & MSAVED)
213 		dispc = '*';
214 	if (mp->m_flag & MPRESERVE)
215 		dispc = 'P';
216 	if ((mp->m_flag & (MREAD|MNEW)) == MNEW)
217 		dispc = 'N';
218 	if ((mp->m_flag & (MREAD|MNEW)) == 0)
219 		dispc = 'U';
220 	if (mp->m_flag & MBOX)
221 		dispc = 'M';
222 	parse(headline, &hl, pbuf);
223 	(void)snprintf(wcount, sizeof(wcount), "%4d/%-5d", mp->m_lines,
224 	    mp->m_size);
225 	subjlen = screenwidth - 44 - strlen(wcount);
226 	from = nameof(mp, 0);
227 	to = skin(hfield("to", mp));
228 	np = extract(from, GTO);
229 	np = delname(np, myname);
230 	if (altnames)
231 		for (ap = altnames; *ap; ap++)
232 			np = delname(np, *ap);
233 	if (np)
234 		/* not from me */
235 		name = value("show-rcpt") != NULL && to ? to : from;
236 	else
237 		/* from me - show TO */
238 		name = value("showto") != NULL && to ? to : from;
239 	if (subjline == NULL || subjlen < 0) { /* pretty pathetic */
240 		subjline="";
241 		subjlen=0;
242 	}
243 	if (name == to)
244 		printf("%c%c%3d TO %-14.14s  %16.16s %s %.*s\n",
245 			curind, dispc, mesg, name, hl.l_date, wcount,
246 			subjlen, subjline);
247 	else
248 		printf("%c%c%3d %-17.17s  %16.16s %s %.*s\n",
249 			curind, dispc, mesg, name, hl.l_date, wcount,
250 			subjlen, subjline);
251 }
252 
253 /*
254  * Print out the value of dot.
255  */
256 int
257 pdot(void *v)
258 {
259 	printf("%d\n", (int)(dot - &message[0] + 1));
260 	return(0);
261 }
262 
263 /*
264  * Print out all the possible commands.
265  */
266 int
267 pcmdlist(void *v)
268 {
269 	extern const struct cmd cmdtab[];
270 	const struct cmd *cp;
271 	int cc;
272 
273 	puts("Commands are:");
274 	for (cc = 0, cp = cmdtab; cp->c_name != NULL; cp++) {
275 		cc += strlen(cp->c_name) + 2;
276 		if (cc > 72) {
277 			putchar('\n');
278 			cc = strlen(cp->c_name) + 2;
279 		}
280 		if ((cp+1)->c_name != NULL)
281 			printf("%s, ", cp->c_name);
282 		else
283 			puts(cp->c_name);
284 	}
285 	return(0);
286 }
287 
288 /*
289  * Pipe message to command
290  */
291 int
292 pipeit(void *ml, void *sl)
293 {
294 	int  *msgvec = ml;
295 	char *cmd    = sl;
296 
297 	return(type1(msgvec, cmd, 0, 0));
298 }
299 
300 /*
301  * Paginate messages, honor ignored fields.
302  */
303 int
304 more(void *v)
305 {
306 	int *msgvec = v;
307 	return(type1(msgvec, NULL, 1, 1));
308 }
309 
310 /*
311  * Paginate messages, even printing ignored fields.
312  */
313 int
314 More(void *v)
315 {
316 	int *msgvec = v;
317 
318 	return(type1(msgvec, NULL, 0, 1));
319 }
320 
321 /*
322  * Type out messages, honor ignored fields.
323  */
324 int
325 type(void *v)
326 {
327 	int *msgvec = v;
328 
329 	return(type1(msgvec, NULL, 1, 0));
330 }
331 
332 /*
333  * Type out messages, even printing ignored fields.
334  */
335 int
336 Type(void *v)
337 {
338 	int *msgvec = v;
339 
340 	return(type1(msgvec, NULL, 0, 0));
341 }
342 
343 /*
344  * Type out the messages requested.
345  */
346 int
347 type1(int *msgvec, char *cmd, int doign, int page)
348 {
349 	int nlines, *ip, restoreterm;
350 	struct message *mp;
351 	struct termios tbuf;
352 	char *cp;
353 	FILE *obuf;
354 
355 	obuf = stdout;
356 	restoreterm = 0;
357 
358 	/*
359 	 * start a pipe if needed.
360 	 */
361 	if (cmd) {
362 		restoreterm = (tcgetattr(fileno(stdin), &tbuf) == 0);
363 		obuf = Popen(cmd, "w");
364 		if (obuf == NULL) {
365 			warn("%s", cmd);
366 			obuf = stdout;
367 		}
368 	} else if (value("interactive") != NULL &&
369 	         (page || (cp = value("crt")) != NULL)) {
370 		nlines = 0;
371 		if (!page) {
372 			for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++)
373 				nlines += message[*ip - 1].m_lines;
374 		}
375 		if (page || nlines > (*cp ? atoi(cp) : realscreenheight)) {
376 			restoreterm = (tcgetattr(fileno(stdin), &tbuf) == 0);
377 			obuf = Popen(value("PAGER"), "w");
378 			if (obuf == NULL) {
379 				warn("%s", cp);
380 				obuf = stdout;
381 			}
382 		}
383 	}
384 
385 	/*
386 	 * Send messages to the output.
387 	 */
388 	for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) {
389 		mp = &message[*ip - 1];
390 		touch(mp);
391 		dot = mp;
392 		if (cmd == NULL && value("quiet") == NULL)
393 			fprintf(obuf, "Message %d:\n", *ip);
394 		if (sendmessage(mp, obuf, doign ? ignore : 0, NULL) == -1)
395 			break;
396 	}
397 
398 	if (obuf != stdout) {
399 		(void)Pclose(obuf);
400 		if (restoreterm)
401 			(void)tcsetattr(fileno(stdin), TCSADRAIN, &tbuf);
402 	}
403 	return(0);
404 }
405 
406 /*
407  * Print the top so many lines of each desired message.
408  * The number of lines is taken from the variable "toplines"
409  * and defaults to 5.
410  */
411 int
412 top(void * v)
413 {
414 	int *msgvec = v;
415 	int *ip;
416 	struct message *mp;
417 	int c, topl, lines, lineb;
418 	char *valtop, linebuf[LINESIZE];
419 	FILE *ibuf;
420 
421 	topl = 5;
422 	valtop = value("toplines");
423 	if (valtop != NULL) {
424 		topl = atoi(valtop);
425 		if (topl < 0 || topl > 10000)
426 			topl = 5;
427 	}
428 	lineb = 1;
429 	for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
430 		mp = &message[*ip - 1];
431 		touch(mp);
432 		dot = mp;
433 		if (value("quiet") == NULL)
434 			printf("Message %d:\n", *ip);
435 		ibuf = setinput(mp);
436 		c = mp->m_lines;
437 		if (!lineb)
438 			putchar('\n');
439 		for (lines = 0; lines < c && lines <= topl; lines++) {
440 			if (readline(ibuf, linebuf, sizeof(linebuf), NULL) < 0)
441 				break;
442 			puts(linebuf);
443 			lineb = blankline(linebuf);
444 		}
445 	}
446 	return(0);
447 }
448 
449 /*
450  * Touch all the given messages so that they will
451  * get mboxed.
452  */
453 int
454 stouch(void *v)
455 {
456 	int *msgvec = v;
457 	int *ip;
458 
459 	for (ip = msgvec; *ip != 0; ip++) {
460 		dot = &message[*ip-1];
461 		dot->m_flag |= MTOUCH;
462 		dot->m_flag &= ~MPRESERVE;
463 	}
464 	return(0);
465 }
466 
467 /*
468  * Make sure all passed messages get mboxed.
469  */
470 int
471 mboxit(void *v)
472 {
473 	int *msgvec = v;
474 	int *ip;
475 
476 	for (ip = msgvec; *ip != 0; ip++) {
477 		dot = &message[*ip-1];
478 		dot->m_flag |= MTOUCH|MBOX;
479 		dot->m_flag &= ~MPRESERVE;
480 	}
481 	return(0);
482 }
483 
484 /*
485  * List the folders the user currently has.
486  */
487 int
488 folders(void *v)
489 {
490 	char *files = (char *)v;
491 	char dirname[PATHSIZE];
492 	char cmd[BUFSIZ];
493 
494 	if (getfold(dirname, sizeof(dirname)) < 0)
495 		strlcpy(dirname, "$HOME", sizeof(dirname));
496 
497 	snprintf(cmd, sizeof(cmd), "cd %s; %s %s", dirname, value("LISTER"),
498 		files && *files ? files : "");
499 
500 	(void)run_command(value("SHELL"), 0, -1, -1, "-c", cmd, NULL);
501 	return(0);
502 }
503 
504 /*
505  * Update the mail file with any new messages that have
506  * come in since we started reading mail.
507  */
508 int
509 inc(void *v)
510 {
511 	int nmsg, mdot;
512 
513 	nmsg = incfile();
514 
515 	if (nmsg == 0) {
516 		puts("No new mail.");
517 	} else if (nmsg > 0) {
518 		mdot = newfileinfo(msgCount - nmsg);
519 		dot = &message[mdot - 1];
520 	} else {
521 		puts("\"inc\" command failed...");
522 	}
523 
524 	return(0);
525 }
526 
527 /*
528  * User hit ^C while printing the headers.
529  */
530 void
531 hdrint(int s)
532 {
533 
534 	gothdrint = 1;
535 }
536