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