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