xref: /netbsd-src/usr.bin/mail/cmd1.c (revision d48f14661dda8638fee055ba15d35bdfb29b9fa8)
1 /*	$NetBSD: cmd1.c,v 1.23 2005/07/19 23:07:10 christos 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. 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 <sys/cdefs.h>
33 #ifndef lint
34 #if 0
35 static char sccsid[] = "@(#)cmd1.c	8.2 (Berkeley) 4/20/95";
36 #else
37 __RCSID("$NetBSD: cmd1.c,v 1.23 2005/07/19 23:07:10 christos Exp $");
38 #endif
39 #endif /* not lint */
40 
41 #include "rcv.h"
42 #include "extern.h"
43 
44 /*
45  * Mail -- a mail program
46  *
47  * User commands.
48  */
49 
50 /*
51  * Print the current active headings.
52  * Don't change dot if invoker didn't give an argument.
53  */
54 
55 static int screen;
56 
57 int
58 headers(void *v)
59 {
60 	int *msgvec = v;
61 	int n, mesg, flag;
62 	struct message *mp;
63 	int size;
64 
65 	size = screensize();
66 	n = msgvec[0];
67 	if (n != 0)
68 		screen = (n-1)/size;
69 	if (screen < 0)
70 		screen = 0;
71 	mp = &message[screen * size];
72 	if (mp >= &message[msgCount])
73 		mp = &message[msgCount - size];
74 	if (mp < &message[0])
75 		mp = &message[0];
76 	flag = 0;
77 	mesg = mp - &message[0];
78 	if (dot != &message[n-1])
79 		dot = mp;
80 	for (; mp < &message[msgCount]; mp++) {
81 		mesg++;
82 		if (mp->m_flag & MDELETED)
83 			continue;
84 		if (flag++ >= size)
85 			break;
86 		printhead(mesg);
87 	}
88 	if (flag == 0) {
89 		(void)printf("No more mail.\n");
90 		return(1);
91 	}
92 	return(0);
93 }
94 
95 /*
96  * Scroll to the next/previous screen
97  */
98 int
99 scroll(void *v)
100 {
101 	char *arg = v;
102 	int s, size;
103 	int cur[1];
104 
105 	cur[0] = 0;
106 	size = screensize();
107 	s = screen;
108 	switch (*arg) {
109 	case 0:
110 	case '+':
111 		s++;
112 		if (s * size >= msgCount) {
113 			(void)printf("On last screenful of messages\n");
114 			return(0);
115 		}
116 		screen = s;
117 		break;
118 
119 	case '-':
120 		if (--s < 0) {
121 			(void)printf("On first screenful of messages\n");
122 			return(0);
123 		}
124 		screen = s;
125 		break;
126 
127 	default:
128 		(void)printf("Unrecognized scrolling command \"%s\"\n", arg);
129 		return(1);
130 	}
131 	return(headers(cur));
132 }
133 
134 /*
135  * Compute screen size.
136  */
137 int
138 screensize(void)
139 {
140 	int s;
141 	char *cp;
142 
143 	if ((cp = value("screen")) != NULL && (s = atoi(cp)) > 0)
144 		return s;
145 	return screenheight - 4;
146 }
147 
148 /*
149  * Print out the headlines for each message
150  * in the passed message list.
151  */
152 int
153 from(void *v)
154 {
155 	int *msgvec = v;
156 	int *ip;
157 
158 	for (ip = msgvec; *ip != 0; ip++)
159 		printhead(*ip);
160 	if (--ip >= msgvec)
161 		dot = &message[*ip - 1];
162 	return(0);
163 }
164 
165 /*
166  * Print out the header of a specific message.
167  * This is a slight improvement to the standard one.
168  */
169 void
170 printhead(int mesg)
171 {
172 	struct message *mp;
173 	char headline[LINESIZE], wcount[LINESIZE], *subjline, dispc, curind;
174 	char pbuf[BUFSIZ];
175 	struct headline hl;
176 	int subjlen;
177 	char *name;
178 
179 	mp = &message[mesg-1];
180 	(void)readline(setinput(mp), headline, LINESIZE);
181 	if ((subjline = hfield("subject", mp)) == NULL)
182 		subjline = hfield("subj", mp);
183 	/*
184 	 * Bletch!
185 	 */
186 	curind = dot == mp ? '>' : ' ';
187 	dispc = ' ';
188 	if (mp->m_flag & MSAVED)
189 		dispc = '*';
190 	if (mp->m_flag & MPRESERVE)
191 		dispc = 'P';
192 	if ((mp->m_flag & (MREAD|MNEW)) == MNEW)
193 		dispc = 'N';
194 	if ((mp->m_flag & (MREAD|MNEW)) == 0)
195 		dispc = 'U';
196 	if (mp->m_flag & MBOX)
197 		dispc = 'M';
198 	parse(headline, &hl, pbuf);
199 	(void)snprintf(wcount, LINESIZE, "%3ld/%-5llu", mp->m_blines,
200 	    /*LINTED*/
201 	    (unsigned long long)mp->m_size);
202 	subjlen = screenwidth - 50 - strlen(wcount);
203 	name = value("show-rcpt") != NULL ?
204 		skin(hfield("to", mp)) : nameof(mp, 0);
205 	if (subjline == NULL || subjlen < 0)		/* pretty pathetic */
206 		(void)printf("%c%c%3d %-20.20s  %16.16s %s\n",
207 			curind, dispc, mesg, name, hl.l_date, wcount);
208 	else
209 		(void)printf("%c%c%3d %-20.20s  %16.16s %s \"%.*s\"\n",
210 			curind, dispc, mesg, name, hl.l_date, wcount,
211 			subjlen, subjline);
212 }
213 
214 /*
215  * Print out the value of dot.
216  */
217 int
218 /*ARGSUSED*/
219 pdot(void *v)
220 {
221 	(void)printf("%d\n", (int)(dot - &message[0] + 1));
222 	return(0);
223 }
224 
225 /*
226  * Print out all the possible commands.
227  */
228 int
229 /*ARGSUSED*/
230 pcmdlist(void *v)
231 {
232 	const struct cmd *cp;
233 	int cc;
234 
235 	(void)printf("Commands are:\n");
236 	for (cc = 0, cp = cmdtab; cp->c_name != NULL; cp++) {
237 		cc += strlen(cp->c_name) + 2;
238 		if (cc > 72) {
239 			(void)printf("\n");
240 			cc = strlen(cp->c_name) + 2;
241 		}
242 		if ((cp+1)->c_name != NULL)
243 			(void)printf("%s, ", cp->c_name);
244 		else
245 			(void)printf("%s\n", cp->c_name);
246 	}
247 	return(0);
248 }
249 
250 /*
251  * Paginate messages, honor ignored fields.
252  */
253 int
254 more(void *v)
255 {
256 	int *msgvec = v;
257 	return (type1(msgvec, 1, 1));
258 }
259 
260 /*
261  * Paginate messages, even printing ignored fields.
262  */
263 int
264 More(void *v)
265 {
266 	int *msgvec = v;
267 
268 	return (type1(msgvec, 0, 1));
269 }
270 
271 /*
272  * Type out messages, honor ignored fields.
273  */
274 int
275 type(void *v)
276 {
277 	int *msgvec = v;
278 
279 	return(type1(msgvec, 1, 0));
280 }
281 
282 /*
283  * Type out messages, even printing ignored fields.
284  */
285 int
286 Type(void *v)
287 {
288 	int *msgvec = v;
289 
290 	return(type1(msgvec, 0, 0));
291 }
292 
293 /*
294  * Type out the messages requested.
295  */
296 jmp_buf	pipestop;
297 int
298 type1(int *msgvec, int doign, int page)
299 {
300 	int *ip;
301 	struct message *mp;
302 	const char *cp;
303 	int nlines;
304 	FILE *obuf;
305 #if __GNUC__
306 	/* Avoid longjmp clobbering */
307 	(void)&cp;
308 	(void)&obuf;
309 #endif
310 
311 	obuf = stdout;
312 	if (setjmp(pipestop))
313 		goto close_pipe;
314 	if (value("interactive") != NULL &&
315 	    (page || (cp = value("crt")) != NULL)) {
316 		nlines = 0;
317 		if (!page) {
318 			for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++)
319 				nlines += message[*ip - 1].m_blines;
320 		}
321 		if (page || nlines > (*cp ? atoi(cp) : realscreenheight)) {
322 			cp = value("PAGER");
323 			if (cp == NULL || *cp == '\0')
324 				cp = _PATH_MORE;
325 			obuf = Popen(cp, "w");
326 			if (obuf == NULL) {
327 				warn("%s", cp);
328 				obuf = stdout;
329 			} else
330 				(void)signal(SIGPIPE, brokpipe);
331 		}
332 	}
333 	for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) {
334 		mp = &message[*ip - 1];
335 		touch(mp);
336 		dot = mp;
337 		if (value("quiet") == NULL)
338 			(void)fprintf(obuf, "Message %d:\n", *ip);
339 		(void)sendmessage(mp, obuf, doign ? ignore : 0, NULL);
340 	}
341 close_pipe:
342 	if (obuf != stdout) {
343 		/*
344 		 * Ignore SIGPIPE so it can't cause a duplicate close.
345 		 */
346 		(void)signal(SIGPIPE, SIG_IGN);
347 		(void)Pclose(obuf);
348 		(void)signal(SIGPIPE, SIG_DFL);
349 	}
350 	return(0);
351 }
352 
353 /*
354  * Respond to a broken pipe signal --
355  * probably caused by quitting more.
356  */
357 void
358 /*ARGSUSED*/
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 			(void)printf("Message %d:\n", *ip);
393 		ibuf = setinput(mp);
394 		c = mp->m_lines;
395 		if (!lineb)
396 			(void)printf("\n");
397 		for (lines = 0; lines < c && lines <= topl; lines++) {
398 			if (readline(ibuf, linebuf, LINESIZE) < 0)
399 				break;
400 			(void)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 /*ARGSUSED*/
447 folders(void *v)
448 {
449 	char dirname[PATHSIZE];
450 	const char *cmd;
451 
452 	if (getfold(dirname) < 0) {
453 		(void)printf("No value set for \"folder\"\n");
454 		return 1;
455 	}
456 	if ((cmd = value("LISTER")) == NULL)
457 		cmd = "ls";
458 	(void)run_command(cmd, 0, -1, -1, dirname, NULL);
459 	return 0;
460 }
461 
462 /*
463  * Update the mail file with any new messages that have
464  * come in since we started reading mail.
465  */
466 int
467 /*ARGSUSED*/
468 inc(void *v)
469 {
470 	int nmsg, mdot;
471 
472 	nmsg = incfile();
473 
474 	if (nmsg == 0) {
475 	(void)printf("No new mail.\n");
476 	} else if (nmsg > 0) {
477 		mdot = newfileinfo(msgCount - nmsg);
478 		dot = &message[mdot - 1];
479 	} else {
480 	(void)printf("\"inc\" command failed...\n");
481 	}
482 
483 	return 0;
484 }
485