xref: /freebsd/usr.bin/mail/cmd1.c (revision f0adf7f5cdd241db2f2c817683191a6ef64a4e95)
1 /*-
2  * Copyright (c) 1980, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #ifndef lint
35 #if 0
36 static char sccsid[] = "@(#)cmd1.c	8.2 (Berkeley) 4/20/95";
37 #endif
38 #endif /* not lint */
39 #include <sys/cdefs.h>
40 __FBSDID("$FreeBSD$");
41 
42 #include "rcv.h"
43 #include "extern.h"
44 
45 /*
46  * Mail -- a mail program
47  *
48  * User commands.
49  */
50 
51 extern const struct cmd cmdtab[];
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(msgvec)
62 	int *msgvec;
63 {
64 	int n, mesg, flag, size;
65 	struct message *mp;
66 
67 	size = screensize();
68 	n = msgvec[0];
69 	if (n != 0)
70 		screen = (n-1)/size;
71 	if (screen < 0)
72 		screen = 0;
73 	mp = &message[screen * size];
74 	if (mp >= &message[msgCount])
75 		mp = &message[msgCount - size];
76 	if (mp < &message[0])
77 		mp = &message[0];
78 	flag = 0;
79 	mesg = mp - &message[0];
80 	if (dot != &message[n-1])
81 		dot = mp;
82 	for (; mp < &message[msgCount]; mp++) {
83 		mesg++;
84 		if (mp->m_flag & MDELETED)
85 			continue;
86 		if (flag++ >= size)
87 			break;
88 		printhead(mesg);
89 	}
90 	if (flag == 0) {
91 		printf("No more mail.\n");
92 		return (1);
93 	}
94 	return (0);
95 }
96 
97 /*
98  * Scroll to the next/previous screen
99  */
100 int
101 scroll(arg)
102 	char arg[];
103 {
104 	int s, size;
105 	int cur[1];
106 
107 	cur[0] = 0;
108 	size = screensize();
109 	s = screen;
110 	switch (*arg) {
111 	case 0:
112 	case '+':
113 		s++;
114 		if (s * size >= msgCount) {
115 			printf("On last screenful of messages\n");
116 			return (0);
117 		}
118 		screen = s;
119 		break;
120 
121 	case '-':
122 		if (--s < 0) {
123 			printf("On first screenful of messages\n");
124 			return (0);
125 		}
126 		screen = s;
127 		break;
128 
129 	default:
130 		printf("Unrecognized scrolling command \"%s\"\n", arg);
131 		return (1);
132 	}
133 	return (headers(cur));
134 }
135 
136 /*
137  * Compute screen size.
138  */
139 int
140 screensize()
141 {
142 	int s;
143 	char *cp;
144 
145 	if ((cp = value("screen")) != NULL && (s = atoi(cp)) > 0)
146 		return (s);
147 	return (screenheight - 4);
148 }
149 
150 /*
151  * Print out the headlines for each message
152  * in the passed message list.
153  */
154 int
155 from(msgvec)
156 	int *msgvec;
157 {
158 	int *ip;
159 
160 	for (ip = msgvec; *ip != 0; ip++)
161 		printhead(*ip);
162 	if (--ip >= msgvec)
163 		dot = &message[*ip - 1];
164 	return (0);
165 }
166 
167 /*
168  * Print out the header of a specific message.
169  * This is a slight improvement to the standard one.
170  */
171 void
172 printhead(mesg)
173 	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)readline(setinput(mp), headline, LINESIZE);
184 	if ((subjline = hfield("subject", mp)) == NULL)
185 		subjline = hfield("subj", mp);
186 	/*
187 	 * Bletch!
188 	 */
189 	curind = dot == mp ? '>' : ' ';
190 	dispc = ' ';
191 	if (mp->m_flag & MSAVED)
192 		dispc = '*';
193 	if (mp->m_flag & MPRESERVE)
194 		dispc = 'P';
195 	if ((mp->m_flag & (MREAD|MNEW)) == MNEW)
196 		dispc = 'N';
197 	if ((mp->m_flag & (MREAD|MNEW)) == 0)
198 		dispc = 'U';
199 	if (mp->m_flag & MBOX)
200 		dispc = 'M';
201 	parse(headline, &hl, pbuf);
202 	sprintf(wcount, "%3ld/%-5ld", mp->m_lines, mp->m_size);
203 	subjlen = screenwidth - 50 - strlen(wcount);
204 	name = value("show-rcpt") != NULL ?
205 		skin(hfield("to", mp)) : nameof(mp, 0);
206 	if (subjline == NULL || subjlen < 0)		/* pretty pathetic */
207 		printf("%c%c%3d %-20.20s  %16.16s %s\n",
208 			curind, dispc, mesg, name, hl.l_date, wcount);
209 	else
210 		printf("%c%c%3d %-20.20s  %16.16s %s \"%.*s\"\n",
211 			curind, dispc, mesg, name, hl.l_date, wcount,
212 			subjlen, subjline);
213 }
214 
215 /*
216  * Print out the value of dot.
217  */
218 int
219 pdot()
220 {
221 	printf("%d\n", dot - &message[0] + 1);
222 	return (0);
223 }
224 
225 /*
226  * Print out all the possible commands.
227  */
228 int
229 pcmdlist()
230 {
231 	const struct cmd *cp;
232 	int cc;
233 
234 	printf("Commands are:\n");
235 	for (cc = 0, cp = cmdtab; cp->c_name != NULL; cp++) {
236 		cc += strlen(cp->c_name) + 2;
237 		if (cc > 72) {
238 			printf("\n");
239 			cc = strlen(cp->c_name) + 2;
240 		}
241 		if ((cp+1)->c_name != NULL)
242 			printf("%s, ", cp->c_name);
243 		else
244 			printf("%s\n", cp->c_name);
245 	}
246 	return (0);
247 }
248 
249 /*
250  * Paginate messages, honor ignored fields.
251  */
252 int
253 more(msgvec)
254 	int *msgvec;
255 {
256 
257 	return (type1(msgvec, 1, 1));
258 }
259 
260 /*
261  * Paginate messages, even printing ignored fields.
262  */
263 int
264 More(msgvec)
265 	int *msgvec;
266 {
267 
268 	return (type1(msgvec, 0, 1));
269 }
270 
271 /*
272  * Type out messages, honor ignored fields.
273  */
274 int
275 type(msgvec)
276 	int *msgvec;
277 {
278 
279 	return (type1(msgvec, 1, 0));
280 }
281 
282 /*
283  * Type out messages, even printing ignored fields.
284  */
285 int
286 Type(msgvec)
287 	int *msgvec;
288 {
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(msgvec, doign, page)
299 	int *msgvec;
300 	int doign, page;
301 {
302 	int nlines, *ip;
303 	struct message *mp;
304 	char *cp;
305 	FILE *obuf;
306 
307 	obuf = stdout;
308 	if (setjmp(pipestop))
309 		goto close_pipe;
310 	if (value("interactive") != NULL &&
311 	    (page || (cp = value("crt")) != NULL)) {
312 		nlines = 0;
313 		if (!page) {
314 			for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++)
315 				nlines += message[*ip - 1].m_lines;
316 		}
317 		if (page || nlines > (*cp ? atoi(cp) : realscreenheight)) {
318 			cp = value("PAGER");
319 			if (cp == NULL || *cp == '\0')
320 				cp = _PATH_MORE;
321 			obuf = Popen(cp, "w");
322 			if (obuf == NULL) {
323 				warnx("%s", cp);
324 				obuf = stdout;
325 			} else
326 				(void)signal(SIGPIPE, brokpipe);
327 		}
328 	}
329 
330 	/*
331 	 * Send messages to the output.
332 	 *
333 	 */
334 	for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) {
335 		mp = &message[*ip - 1];
336 		touch(mp);
337 		dot = mp;
338 		if (value("quiet") == NULL)
339 			fprintf(obuf, "Message %d:\n", *ip);
340 		(void)sendmessage(mp, obuf, doign ? ignore : 0, NULL);
341 	}
342 
343 close_pipe:
344 	if (obuf != stdout) {
345 		/*
346 		 * Ignore SIGPIPE so it can't cause a duplicate close.
347 		 */
348 		(void)signal(SIGPIPE, SIG_IGN);
349 		(void)Pclose(obuf);
350 		(void)signal(SIGPIPE, SIG_DFL);
351 	}
352 	return (0);
353 }
354 
355 /*
356  * Respond to a broken pipe signal --
357  * probably caused by quitting more.
358  */
359 /*ARGSUSED*/
360 void
361 brokpipe(signo)
362 	int signo;
363 {
364 	longjmp(pipestop, 1);
365 }
366 
367 /*
368  * Print the top so many lines of each desired message.
369  * The number of lines is taken from the variable "toplines"
370  * and defaults to 5.
371  */
372 int
373 top(msgvec)
374 	int *msgvec;
375 {
376 	int *ip;
377 	struct message *mp;
378 	int c, topl, lines, lineb;
379 	char *valtop, linebuf[LINESIZE];
380 	FILE *ibuf;
381 
382 	topl = 5;
383 	valtop = value("toplines");
384 	if (valtop != NULL) {
385 		topl = atoi(valtop);
386 		if (topl < 0 || topl > 10000)
387 			topl = 5;
388 	}
389 	lineb = 1;
390 	for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
391 		mp = &message[*ip - 1];
392 		touch(mp);
393 		dot = mp;
394 		if (value("quiet") == NULL)
395 			printf("Message %d:\n", *ip);
396 		ibuf = setinput(mp);
397 		c = mp->m_lines;
398 		if (!lineb)
399 			printf("\n");
400 		for (lines = 0; lines < c && lines <= topl; lines++) {
401 			if (readline(ibuf, linebuf, sizeof(linebuf)) < 0)
402 				break;
403 			puts(linebuf);
404 			lineb = strspn(linebuf, " \t") == strlen(linebuf);
405 		}
406 	}
407 	return (0);
408 }
409 
410 /*
411  * Touch all the given messages so that they will
412  * get mboxed.
413  */
414 int
415 stouch(msgvec)
416 	int msgvec[];
417 {
418 	int *ip;
419 
420 	for (ip = msgvec; *ip != 0; ip++) {
421 		dot = &message[*ip-1];
422 		dot->m_flag |= MTOUCH;
423 		dot->m_flag &= ~MPRESERVE;
424 	}
425 	return (0);
426 }
427 
428 /*
429  * Make sure all passed messages get mboxed.
430  */
431 int
432 mboxit(msgvec)
433 	int msgvec[];
434 {
435 	int *ip;
436 
437 	for (ip = msgvec; *ip != 0; ip++) {
438 		dot = &message[*ip-1];
439 		dot->m_flag |= MTOUCH|MBOX;
440 		dot->m_flag &= ~MPRESERVE;
441 	}
442 	return (0);
443 }
444 
445 /*
446  * List the folders the user currently has.
447  */
448 int
449 folders()
450 {
451 	char dirname[PATHSIZE];
452 	char *cmd;
453 
454 	if (getfold(dirname, sizeof(dirname)) < 0) {
455 		printf("No value set for \"folder\"\n");
456 		return (1);
457 	}
458 	if ((cmd = value("LISTER")) == NULL)
459 		cmd = "ls";
460 	(void)run_command(cmd, 0, -1, -1, dirname, NULL, NULL);
461 	return (0);
462 }
463 
464 /*
465  * Update the mail file with any new messages that have
466  * come in since we started reading mail.
467  */
468 int
469 inc(v)
470 	void *v;
471 {
472 	int nmsg, mdot;
473 
474 	nmsg = incfile();
475 
476 	if (nmsg == 0)
477 		printf("No new mail.\n");
478 	else if (nmsg > 0) {
479 		mdot = newfileinfo(msgCount - nmsg);
480 		dot = &message[mdot - 1];
481 	} else
482 		printf("\"inc\" command failed...\n");
483 
484 	return (0);
485 }
486