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