xref: /netbsd-src/usr.bin/mail/cmd1.c (revision ce2c90c7c172d95d2402a5b3d96d8f8e6d138a21)
1 /*	$NetBSD: cmd1.c,v 1.25 2006/09/29 14:59:31 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.25 2006/09/29 14:59:31 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 	volatile FILE *obuf;	/* avoid longjmp clobbering */
305 
306 	obuf = stdout;
307 	if (setjmp(pipestop))
308 		goto close_pipe;
309 	if (value("interactive") != NULL &&
310 	    (page || (cp = value("crt")) != NULL)) {
311 		nlines = 0;
312 		if (!page) {
313 			for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++)
314 				nlines += message[*ip - 1].m_blines;
315 		}
316 		if (page || nlines > (*cp ? atoi(cp) : realscreenheight)) {
317 			cp = value("PAGER");
318 			if (cp == NULL || *cp == '\0')
319 				cp = _PATH_MORE;
320 			obuf = Popen(cp, "w");
321 			if (obuf == NULL) {
322 				warn("%s", cp);
323 				obuf = stdout;
324 			} else
325 				(void)signal(SIGPIPE, brokpipe);
326 		}
327 	}
328 	for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) {
329 		mp = &message[*ip - 1];
330 		touch(mp);
331 		dot = mp;
332 		if (value("quiet") == NULL)
333 			(void)fprintf(__UNVOLATILE(obuf), "Message %d:\n", *ip);
334 		(void)sendmessage(mp, __UNVOLATILE(obuf), doign ? ignore : 0, NULL);
335 	}
336 close_pipe:
337 	if (obuf != stdout) {
338 		/*
339 		 * Ignore SIGPIPE so it can't cause a duplicate close.
340 		 */
341 		(void)signal(SIGPIPE, SIG_IGN);
342 		(void)Pclose(__UNVOLATILE(obuf));
343 		(void)signal(SIGPIPE, SIG_DFL);
344 	}
345 	return(0);
346 }
347 
348 /*
349  * Respond to a broken pipe signal --
350  * probably caused by quitting more.
351  */
352 void
353 /*ARGSUSED*/
354 brokpipe(int signo)
355 {
356 	longjmp(pipestop, 1);
357 }
358 
359 /*
360  * Pipe the current message buffer to a command.
361  */
362 int
363 pipecmd(void *v)
364 {
365 	char *cmd = v;
366 	volatile FILE *obuf;	/* avoid longjmp clobbering */
367 
368 	if (dot == NULL) {
369 		warn("pipcmd: no current message");
370 		return 1;
371 	}
372 
373 	obuf = stdout;
374 	if (setjmp(pipestop))
375 		goto close_pipe;
376 
377 	obuf = Popen(cmd, "w");
378 	if (obuf == NULL) {
379 		warn("pipecmd: Popen failed: %s", cmd);
380 		return 1;
381 	} else
382 		(void)signal(SIGPIPE, brokpipe);
383 
384 	(void)sendmessage(dot, __UNVOLATILE(obuf), ignoreall, NULL);
385 
386  close_pipe:
387 	if (obuf != stdout) {
388 		/*
389 		 * Ignore SIGPIPE so it can't cause a duplicate close.
390 		 */
391 		(void)signal(SIGPIPE, SIG_IGN);
392 		(void)Pclose(__UNVOLATILE(obuf));
393 		(void)signal(SIGPIPE, SIG_DFL);
394 	}
395 	return 0;
396 }
397 
398 /*
399  * Print the top so many lines of each desired message.
400  * The number of lines is taken from the variable "toplines"
401  * and defaults to 5.
402  */
403 int
404 top(void *v)
405 {
406 	int *msgvec = v;
407 	int *ip;
408 	struct message *mp;
409 	int c, topl, lines, lineb;
410 	char *valtop, linebuf[LINESIZE];
411 	FILE *ibuf;
412 
413 	topl = 5;
414 	valtop = value("toplines");
415 	if (valtop != NULL) {
416 		topl = atoi(valtop);
417 		if (topl < 0 || topl > 10000)
418 			topl = 5;
419 	}
420 	lineb = 1;
421 	for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
422 		mp = &message[*ip - 1];
423 		touch(mp);
424 		dot = mp;
425 		if (value("quiet") == NULL)
426 			(void)printf("Message %d:\n", *ip);
427 		ibuf = setinput(mp);
428 		c = mp->m_lines;
429 		if (!lineb)
430 			(void)printf("\n");
431 		for (lines = 0; lines < c && lines <= topl; lines++) {
432 			if (readline(ibuf, linebuf, LINESIZE) < 0)
433 				break;
434 			(void)puts(linebuf);
435 			lineb = blankline(linebuf);
436 		}
437 	}
438 	return(0);
439 }
440 
441 /*
442  * Touch all the given messages so that they will
443  * get mboxed.
444  */
445 int
446 stouch(void *v)
447 {
448 	int *msgvec = v;
449 	int *ip;
450 
451 	for (ip = msgvec; *ip != 0; ip++) {
452 		dot = &message[*ip-1];
453 		dot->m_flag |= MTOUCH;
454 		dot->m_flag &= ~MPRESERVE;
455 	}
456 	return(0);
457 }
458 
459 /*
460  * Make sure all passed messages get mboxed.
461  */
462 int
463 mboxit(void *v)
464 {
465 	int *msgvec = v;
466 	int *ip;
467 
468 	for (ip = msgvec; *ip != 0; ip++) {
469 		dot = &message[*ip-1];
470 		dot->m_flag |= MTOUCH|MBOX;
471 		dot->m_flag &= ~MPRESERVE;
472 	}
473 	return(0);
474 }
475 
476 /*
477  * List the folders the user currently has.
478  */
479 int
480 /*ARGSUSED*/
481 folders(void *v)
482 {
483 	char dirname[PATHSIZE];
484 	const char *cmd;
485 
486 	if (getfold(dirname) < 0) {
487 		(void)printf("No value set for \"folder\"\n");
488 		return 1;
489 	}
490 	if ((cmd = value("LISTER")) == NULL)
491 		cmd = "ls";
492 	(void)run_command(cmd, 0, -1, -1, dirname, NULL);
493 	return 0;
494 }
495 
496 /*
497  * Update the mail file with any new messages that have
498  * come in since we started reading mail.
499  */
500 int
501 /*ARGSUSED*/
502 inc(void *v)
503 {
504 	int nmsg, mdot;
505 
506 	nmsg = incfile();
507 
508 	if (nmsg == 0) {
509 	(void)printf("No new mail.\n");
510 	} else if (nmsg > 0) {
511 		mdot = newfileinfo(msgCount - nmsg);
512 		dot = &message[mdot - 1];
513 	} else {
514 	(void)printf("\"inc\" command failed...\n");
515 	}
516 
517 	return 0;
518 }
519