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