xref: /netbsd-src/usr.bin/mail/cmd1.c (revision 1394f01b4a9e99092957ca5d824d67219565d9b5)
1 /*	$NetBSD: cmd1.c,v 1.9 1997/07/09 05:29:48 mikel 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 #ifndef lint
37 #if 0
38 static char sccsid[] = "@(#)cmd1.c	8.2 (Berkeley) 4/20/95";
39 #else
40 static char rcsid[] = "$NetBSD: cmd1.c,v 1.9 1997/07/09 05:29:48 mikel Exp $";
41 #endif
42 #endif /* not lint */
43 
44 #include "rcv.h"
45 #include "extern.h"
46 
47 /*
48  * Mail -- a mail program
49  *
50  * User commands.
51  */
52 
53 /*
54  * Print the current active headings.
55  * Don't change dot if invoker didn't give an argument.
56  */
57 
58 static int screen;
59 
60 int
61 headers(v)
62 	void *v;
63 {
64 	int *msgvec = v;
65 	register int n, mesg, flag;
66 	register struct message *mp;
67 	int size;
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 		printf("No more mail.\n");
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 	register 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()
144 {
145 	int s;
146 	char *cp;
147 
148 	if ((cp = value("screen")) != NOSTR && (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(v)
159 	void *v;
160 {
161 	int *msgvec = v;
162 	register int *ip;
163 
164 	for (ip = msgvec; *ip != 0; ip++)
165 		printhead(*ip);
166 	if (--ip >= msgvec)
167 		dot = &message[*ip - 1];
168 	return(0);
169 }
170 
171 /*
172  * Print out the header of a specific message.
173  * This is a slight improvement to the standard one.
174  */
175 void
176 printhead(mesg)
177 	int mesg;
178 {
179 	struct message *mp;
180 	char headline[LINESIZE], wcount[LINESIZE], *subjline, dispc, curind;
181 	char pbuf[BUFSIZ];
182 	struct headline hl;
183 	int subjlen;
184 	char *name;
185 
186 	mp = &message[mesg-1];
187 	(void) readline(setinput(mp), headline, LINESIZE);
188 	if ((subjline = hfield("subject", mp)) == NOSTR)
189 		subjline = hfield("subj", mp);
190 	/*
191 	 * Bletch!
192 	 */
193 	curind = dot == mp ? '>' : ' ';
194 	dispc = ' ';
195 	if (mp->m_flag & MSAVED)
196 		dispc = '*';
197 	if (mp->m_flag & MPRESERVE)
198 		dispc = 'P';
199 	if ((mp->m_flag & (MREAD|MNEW)) == MNEW)
200 		dispc = 'N';
201 	if ((mp->m_flag & (MREAD|MNEW)) == 0)
202 		dispc = 'U';
203 	if (mp->m_flag & MBOX)
204 		dispc = 'M';
205 	parse(headline, &hl, pbuf);
206 	snprintf(wcount, LINESIZE, "%3ld/%-5ld", mp->m_lines, mp->m_size);
207 	subjlen = screenwidth - 50 - strlen(wcount);
208 	name = value("show-rcpt") != NOSTR ?
209 		skin(hfield("to", mp)) : nameof(mp, 0);
210 	if (subjline == NOSTR || 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", 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 	register const struct cmd *cp;
239 	register int cc;
240 
241 	printf("Commands are:\n");
242 	for (cc = 0, cp = cmdtab; cp->c_name != NULL; cp++) {
243 		cc += strlen(cp->c_name) + 2;
244 		if (cc > 72) {
245 			printf("\n");
246 			cc = strlen(cp->c_name) + 2;
247 		}
248 		if ((cp+1)->c_name != NOSTR)
249 			printf("%s, ", cp->c_name);
250 		else
251 			printf("%s\n", 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 jmp_buf	pipestop;
307 int
308 type1(msgvec, doign, page)
309 	int *msgvec;
310 	int doign, page;
311 {
312 	register *ip;
313 	struct message *mp;
314 	char *cp;
315 	int nlines;
316 	FILE *obuf;
317 #if __GNUC__
318 	/* Avoid longjmp clobbering */
319 	(void) &cp;
320 	(void) &obuf;
321 #endif
322 
323 	obuf = stdout;
324 	if (setjmp(pipestop))
325 		goto close_pipe;
326 	if (value("interactive") != NOSTR &&
327 	    (page || (cp = value("crt")) != NOSTR)) {
328 		nlines = 0;
329 		if (!page) {
330 			for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++)
331 				nlines += message[*ip - 1].m_lines;
332 		}
333 		if (page || nlines > (*cp ? atoi(cp) : realscreenheight)) {
334 			cp = value("PAGER");
335 			if (cp == NULL || *cp == '\0')
336 				cp = _PATH_MORE;
337 			obuf = Popen(cp, "w");
338 			if (obuf == NULL) {
339 				perror(cp);
340 				obuf = stdout;
341 			} else
342 				signal(SIGPIPE, brokpipe);
343 		}
344 	}
345 	for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) {
346 		mp = &message[*ip - 1];
347 		touch(mp);
348 		dot = mp;
349 		if (value("quiet") == NOSTR)
350 			fprintf(obuf, "Message %d:\n", *ip);
351 		(void) send(mp, obuf, doign ? ignore : 0, NOSTR);
352 	}
353 close_pipe:
354 	if (obuf != stdout) {
355 		/*
356 		 * Ignore SIGPIPE so it can't cause a duplicate close.
357 		 */
358 		signal(SIGPIPE, SIG_IGN);
359 		Pclose(obuf);
360 		signal(SIGPIPE, SIG_DFL);
361 	}
362 	return(0);
363 }
364 
365 /*
366  * Respond to a broken pipe signal --
367  * probably caused by quitting more.
368  */
369 void
370 brokpipe(signo)
371 	int signo;
372 {
373 	longjmp(pipestop, 1);
374 }
375 
376 /*
377  * Print the top so many lines of each desired message.
378  * The number of lines is taken from the variable "toplines"
379  * and defaults to 5.
380  */
381 int
382 top(v)
383 	void *v;
384 {
385 	int *msgvec = v;
386 	register int *ip;
387 	register struct message *mp;
388 	int c, topl, lines, lineb;
389 	char *valtop, linebuf[LINESIZE];
390 	FILE *ibuf;
391 
392 	topl = 5;
393 	valtop = value("toplines");
394 	if (valtop != NOSTR) {
395 		topl = atoi(valtop);
396 		if (topl < 0 || topl > 10000)
397 			topl = 5;
398 	}
399 	lineb = 1;
400 	for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
401 		mp = &message[*ip - 1];
402 		touch(mp);
403 		dot = mp;
404 		if (value("quiet") == NOSTR)
405 			printf("Message %d:\n", *ip);
406 		ibuf = setinput(mp);
407 		c = mp->m_lines;
408 		if (!lineb)
409 			printf("\n");
410 		for (lines = 0; lines < c && lines <= topl; lines++) {
411 			if (readline(ibuf, linebuf, LINESIZE) < 0)
412 				break;
413 			puts(linebuf);
414 			lineb = blankline(linebuf);
415 		}
416 	}
417 	return(0);
418 }
419 
420 /*
421  * Touch all the given messages so that they will
422  * get mboxed.
423  */
424 int
425 stouch(v)
426 	void *v;
427 {
428 	int *msgvec = v;
429 	register int *ip;
430 
431 	for (ip = msgvec; *ip != 0; ip++) {
432 		dot = &message[*ip-1];
433 		dot->m_flag |= MTOUCH;
434 		dot->m_flag &= ~MPRESERVE;
435 	}
436 	return(0);
437 }
438 
439 /*
440  * Make sure all passed messages get mboxed.
441  */
442 int
443 mboxit(v)
444 	void *v;
445 {
446 	int *msgvec = v;
447 	register int *ip;
448 
449 	for (ip = msgvec; *ip != 0; ip++) {
450 		dot = &message[*ip-1];
451 		dot->m_flag |= MTOUCH|MBOX;
452 		dot->m_flag &= ~MPRESERVE;
453 	}
454 	return(0);
455 }
456 
457 /*
458  * List the folders the user currently has.
459  */
460 int
461 folders(v)
462 	void *v;
463 {
464 	char dirname[PATHSIZE];
465 	char *cmd;
466 
467 	if (getfold(dirname) < 0) {
468 		printf("No value set for \"folder\"\n");
469 		return 1;
470 	}
471 	if ((cmd = value("LISTER")) == NOSTR)
472 		cmd = "ls";
473 	(void) run_command(cmd, 0, -1, -1, dirname, NOSTR, NOSTR);
474 	return 0;
475 }
476 
477 /*
478  * Update the mail file with any new messages that have
479  * come in since we started reading mail.
480  */
481 int
482 inc(v)
483 	void *v;
484 {
485 	int nmsg, mdot;
486 
487 	nmsg = incfile();
488 
489 	if (nmsg == 0) {
490 	printf("No new mail.\n");
491 	} else if (nmsg > 0) {
492 		mdot = newfileinfo(msgCount - nmsg);
493 		dot = &message[mdot - 1];
494 	} else {
495 	printf("\"inc\" command failed...\n");
496 	}
497 
498 	return 0;
499 }
500