xref: /dragonfly/usr.bin/mail/cmd1.c (revision c37c9ab3)
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. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * @(#)cmd1.c	8.2 (Berkeley) 4/20/95
30  * $FreeBSD: src/usr.bin/mail/cmd1.c,v 1.3.6.3 2003/01/06 05:46:03 mikeh Exp $
31  */
32 
33 #include "rcv.h"
34 #include "extern.h"
35 
36 /*
37  * Mail -- a mail program
38  *
39  * User commands.
40  */
41 
42 extern const struct cmd cmdtab[];
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(int *msgvec)
53 {
54 	int n, mesg, flag, size;
55 	struct message *mp;
56 
57 	size = screensize();
58 	n = msgvec[0];
59 	if (n != 0)
60 		screen = (n-1)/size;
61 	if (screen < 0)
62 		screen = 0;
63 	mp = &message[screen * size];
64 	if (mp >= &message[msgCount])
65 		mp = &message[msgCount - size];
66 	if (mp < &message[0])
67 		mp = &message[0];
68 	flag = 0;
69 	mesg = mp - &message[0];
70 	if (dot != &message[n-1])
71 		dot = mp;
72 	for (; mp < &message[msgCount]; mp++) {
73 		mesg++;
74 		if (mp->m_flag & MDELETED)
75 			continue;
76 		if (flag++ >= size)
77 			break;
78 		printhead(mesg);
79 	}
80 	if (flag == 0) {
81 		printf("No more mail.\n");
82 		return (1);
83 	}
84 	return (0);
85 }
86 
87 /*
88  * Scroll to the next/previous screen
89  */
90 int
91 scroll(char *arg)
92 {
93 	int s, size;
94 	int cur[1];
95 
96 	cur[0] = 0;
97 	size = screensize();
98 	s = screen;
99 	switch (*arg) {
100 	case 0:
101 	case '+':
102 		s++;
103 		if (s * size >= msgCount) {
104 			printf("On last screenful of messages\n");
105 			return (0);
106 		}
107 		screen = s;
108 		break;
109 
110 	case '-':
111 		if (--s < 0) {
112 			printf("On first screenful of messages\n");
113 			return (0);
114 		}
115 		screen = s;
116 		break;
117 
118 	default:
119 		printf("Unrecognized scrolling command \"%s\"\n", arg);
120 		return (1);
121 	}
122 	return (headers(cur));
123 }
124 
125 /*
126  * Compute screen size.
127  */
128 int
129 screensize(void)
130 {
131 	int s;
132 	char *cp;
133 
134 	if ((cp = value("screen")) != NULL && (s = atoi(cp)) > 0)
135 		return (s);
136 	return (screenheight - 4);
137 }
138 
139 /*
140  * Print out the headlines for each message
141  * in the passed message list.
142  */
143 int
144 from(int *msgvec)
145 {
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 	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)
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)
217 {
218 	const struct cmd *cp;
219 	int cc;
220 
221 	printf("Commands are:\n");
222 	for (cc = 0, cp = cmdtab; cp->c_name != NULL; cp++) {
223 		cc += strlen(cp->c_name) + 2;
224 		if (cc > 72) {
225 			printf("\n");
226 			cc = strlen(cp->c_name) + 2;
227 		}
228 		if ((cp+1)->c_name != NULL)
229 			printf("%s, ", cp->c_name);
230 		else
231 			printf("%s\n", cp->c_name);
232 	}
233 	return (0);
234 }
235 
236 /*
237  * Paginate messages, honor ignored fields.
238  */
239 int
240 more(int *msgvec)
241 {
242 	return (type1(msgvec, 1, 1));
243 }
244 
245 /*
246  * Paginate messages, even printing ignored fields.
247  */
248 int
249 More(int *msgvec)
250 {
251 	return (type1(msgvec, 0, 1));
252 }
253 
254 /*
255  * Type out messages, honor ignored fields.
256  */
257 int
258 type(int *msgvec)
259 {
260 	return (type1(msgvec, 1, 0));
261 }
262 
263 /*
264  * Type out messages, even printing ignored fields.
265  */
266 int
267 Type(int *msgvec)
268 {
269 	return (type1(msgvec, 0, 0));
270 }
271 
272 /*
273  * Type out the messages requested.
274  */
275 jmp_buf	pipestop;
276 int
277 type1(int *msgvec, int doign, int page)
278 {
279 	int nlines, *ip;
280 	struct message *mp;
281 	char *cp;
282 	FILE *obuf;
283 
284 	obuf = stdout;
285 	if (setjmp(pipestop))
286 		goto close_pipe;
287 	if (value("interactive") != NULL &&
288 	    (page || (cp = value("crt")) != NULL)) {
289 		nlines = 0;
290 		if (!page) {
291 			for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++)
292 				nlines += message[*ip - 1].m_lines;
293 		}
294 		if (page || nlines > (*cp ? atoi(cp) : realscreenheight)) {
295 			cp = value("PAGER");
296 			if (cp == NULL || *cp == '\0')
297 				cp = _PATH_MORE;
298 			obuf = Popen(cp, "w");
299 			if (obuf == NULL) {
300 				warnx("%s", cp);
301 				obuf = stdout;
302 			} else
303 				signal(SIGPIPE, brokpipe);
304 		}
305 	}
306 
307 	/*
308 	 * Send messages to the output.
309 	 *
310 	 */
311 	for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) {
312 		mp = &message[*ip - 1];
313 		touch(mp);
314 		dot = mp;
315 		if (value("quiet") == NULL)
316 			fprintf(obuf, "Message %d:\n", *ip);
317 		sendmessage(mp, obuf, doign ? ignore : 0, NULL);
318 	}
319 
320 close_pipe:
321 	if (obuf != stdout) {
322 		/*
323 		 * Ignore SIGPIPE so it can't cause a duplicate close.
324 		 */
325 		signal(SIGPIPE, SIG_IGN);
326 		Pclose(obuf);
327 		signal(SIGPIPE, SIG_DFL);
328 	}
329 	return (0);
330 }
331 
332 /*
333  * Respond to a broken pipe signal --
334  * probably caused by quitting more.
335  */
336 /*ARGSUSED*/
337 void
338 brokpipe(int signo)
339 {
340 	longjmp(pipestop, 1);
341 }
342 
343 /*
344  * Print the top so many lines of each desired message.
345  * The number of lines is taken from the variable "toplines"
346  * and defaults to 5.
347  */
348 int
349 top(int *msgvec)
350 {
351 	int *ip;
352 	struct message *mp;
353 	int c, topl, lines, lineb;
354 	char *valtop, linebuf[LINESIZE];
355 	FILE *ibuf;
356 
357 	topl = 5;
358 	valtop = value("toplines");
359 	if (valtop != NULL) {
360 		topl = atoi(valtop);
361 		if (topl < 0 || topl > 10000)
362 			topl = 5;
363 	}
364 	lineb = 1;
365 	for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
366 		mp = &message[*ip - 1];
367 		touch(mp);
368 		dot = mp;
369 		if (value("quiet") == NULL)
370 			printf("Message %d:\n", *ip);
371 		ibuf = setinput(mp);
372 		c = mp->m_lines;
373 		if (!lineb)
374 			printf("\n");
375 		for (lines = 0; lines < c && lines <= topl; lines++) {
376 			if (readline(ibuf, linebuf, sizeof(linebuf)) < 0)
377 				break;
378 			puts(linebuf);
379 			lineb = strspn(linebuf, " \t") == strlen(linebuf);
380 		}
381 	}
382 	return (0);
383 }
384 
385 /*
386  * Touch all the given messages so that they will
387  * get mboxed.
388  */
389 int
390 stouch(int *msgvec)
391 {
392 	int *ip;
393 
394 	for (ip = msgvec; *ip != 0; ip++) {
395 		dot = &message[*ip-1];
396 		dot->m_flag |= MTOUCH;
397 		dot->m_flag &= ~MPRESERVE;
398 	}
399 	return (0);
400 }
401 
402 /*
403  * Make sure all passed messages get mboxed.
404  */
405 int
406 mboxit(int *msgvec)
407 {
408 	int *ip;
409 
410 	for (ip = msgvec; *ip != 0; ip++) {
411 		dot = &message[*ip-1];
412 		dot->m_flag |= MTOUCH|MBOX;
413 		dot->m_flag &= ~MPRESERVE;
414 	}
415 	return (0);
416 }
417 
418 /*
419  * List the folders the user currently has.
420  */
421 int
422 folders(void)
423 {
424 	char dirname[PATHSIZE];
425 	char *cmd;
426 
427 	if (getfold(dirname, sizeof(dirname)) < 0) {
428 		printf("No value set for \"folder\"\n");
429 		return (1);
430 	}
431 	if ((cmd = value("LISTER")) == NULL)
432 		cmd = "ls";
433 	run_command(cmd, 0, -1, -1, dirname, NULL, NULL);
434 	return (0);
435 }
436 
437 /*
438  * Update the mail file with any new messages that have
439  * come in since we started reading mail.
440  */
441 int
442 inc(void *v)
443 {
444 	int nmsg, mdot;
445 
446 	nmsg = incfile();
447 
448 	if (nmsg == 0)
449 		printf("No new mail.\n");
450 	else if (nmsg > 0) {
451 		mdot = newfileinfo(msgCount - nmsg);
452 		dot = &message[mdot - 1];
453 	} else
454 		printf("\"inc\" command failed...\n");
455 
456 	return (0);
457 }
458