cmd1.c revision 1.20 1 /* $NetBSD: cmd1.c,v 1.20 2003/03/29 21:41:04 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. 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.20 2003/03/29 21:41:04 christos 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);
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