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