cmd1.c revision 1.27 1 /* $NetBSD: cmd1.c,v 1.27 2006/10/31 20:07:32 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.27 2006/10/31 20:07:32 christos Exp $");
38 #endif
39 #endif /* not lint */
40
41 #include "rcv.h"
42 #include "extern.h"
43 #include "format.h"
44 #ifdef MIME_SUPPORT
45 #include "mime.h"
46 #endif
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(void *v)
63 {
64 int *msgvec = v;
65 int n, mesg, flag;
66 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 (void)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(void *v)
104 {
105 char *arg = v;
106 int s, size;
107 int cur[1];
108
109 cur[0] = 0;
110 size = screensize();
111 s = screen;
112 switch (*arg) {
113 case 0:
114 case '+':
115 s++;
116 if (s * size >= msgCount) {
117 (void)printf("On last screenful of messages\n");
118 return(0);
119 }
120 screen = s;
121 break;
122
123 case '-':
124 if (--s < 0) {
125 (void)printf("On first screenful of messages\n");
126 return(0);
127 }
128 screen = s;
129 break;
130
131 default:
132 (void)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(void)
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(void *v)
158 {
159 int *msgvec = v;
160 int *ip;
161
162 for (ip = msgvec; *ip != 0; ip++)
163 printhead(*ip);
164 if (--ip >= msgvec)
165 dot = &message[*ip - 1];
166 return(0);
167 }
168
169 /*
170 * Print out the header of a specific message.
171 * This is a slight improvement to the standard one.
172 */
173 void
174 printhead(int mesg)
175 {
176 #if 1
177 const char *fmtstr;
178 char *msgline;
179
180 fmtstr = value(ENAME_HEADER_FORMAT);
181 /*
182 * XXX - should we use the old code here if
183 * value(ENAME_HEADER_FORMAT) is null?
184 */
185 if (fmtstr == NULL)
186 fmtstr = DEFAULT_HEADER_FORMAT;
187 msgline = smsgprintf(fmtstr, &message[mesg - 1]);
188 if (screenwidth > 0)
189 msgline[screenwidth] = '\0';
190 (void)printf("%s\n", msgline);
191 #else
192 struct message *mp;
193 char headline[LINESIZE], wcount[LINESIZE], *subjline, dispc, curind;
194 char pbuf[BUFSIZ];
195 struct headline hl;
196 int subjlen;
197 char *name;
198
199 mp = &message[mesg - 1];
200 (void)mail_readline(setinput(mp), headline, LINESIZE);
201 if ((subjline = hfield("subject", mp)) == NULL)
202 subjline = hfield("subj", mp);
203
204 /*
205 * Bletch!
206 */
207 curind = dot == mp ? '>' : ' ';
208 dispc = ' ';
209 if (mp->m_flag & MSAVED)
210 dispc = '*';
211 if (mp->m_flag & MPRESERVE)
212 dispc = 'P';
213 if ((mp->m_flag & (MREAD|MNEW)) == MNEW)
214 dispc = 'N';
215 if ((mp->m_flag & (MREAD|MNEW)) == 0)
216 dispc = 'U';
217 if (mp->m_flag & MBOX)
218 dispc = 'M';
219 parse(headline, &hl, pbuf);
220 (void)snprintf(wcount, LINESIZE, "%3ld/%-5llu", mp->m_blines,
221 (unsigned long long)mp->m_size);
222 subjlen = screenwidth - 50 - strlen(wcount);
223 name = value("show-rcpt") != NULL ?
224 skin(hfield("to", mp)) : nameof(mp, 0);
225 if (subjline == NULL || subjlen < 0) /* pretty pathetic */
226 (void)printf("%c%c%3d %-20.20s %16.16s %s\n",
227 curind, dispc, mesg, name, hl.l_date, wcount);
228 else
229 (void)printf("%c%c%3d %-20.20s %16.16s %s \"%.*s\"\n",
230 curind, dispc, mesg, name, hl.l_date, wcount,
231 subjlen, subjline);
232 #endif
233 }
234
235 /*
236 * Print out the value of dot.
237 */
238 int
239 /*ARGSUSED*/
240 pdot(void *v __unused)
241 {
242 (void)printf("%d\n", (int)(dot - &message[0] + 1));
243 return(0);
244 }
245
246 /*
247 * Print out all the possible commands.
248 */
249 int
250 /*ARGSUSED*/
251 pcmdlist(void *v __unused)
252 {
253 const struct cmd *cp;
254 int cc;
255
256 (void)printf("Commands are:\n");
257 for (cc = 0, cp = cmdtab; cp->c_name != NULL; cp++) {
258 cc += strlen(cp->c_name) + 2;
259 if (cc > 72) {
260 (void)printf("\n");
261 cc = strlen(cp->c_name) + 2;
262 }
263 if ((cp + 1)->c_name != NULL)
264 (void)printf("%s, ", cp->c_name);
265 else
266 (void)printf("%s\n", cp->c_name);
267 }
268 return(0);
269 }
270
271 #ifdef MIME_SUPPORT
272 static int
273 de_mime(const char *name)
274 {
275 const char *p;
276 const char *list;
277
278 #define DELIM " \t," /* list of string delimiters */
279
280 list = value(ENAME_MIME_DECODE_MSG);
281 if (list == NULL)
282 return 0;
283
284 if (list[0] == '\0')
285 return 1;
286
287 p = strcasestr(list, name);
288 if (p == NULL)
289 return 0;
290
291 return strchr(DELIM, p[strlen(name)]) && (
292 p == list || strchr(DELIM, p[-1]));
293
294 #undef DELIM
295 }
296 #endif /* MIME_SUPPORT */
297
298 /*
299 * Paginate messages, honor ignored fields.
300 */
301 int
302 more(void *v)
303 {
304 int *msgvec = v;
305 #ifdef MIME_SUPPORT
306 return type1(msgvec, 1, 1, de_mime("more"));
307 #else
308 return(type1(msgvec, 1, 1));
309 #endif
310
311 }
312
313 /*
314 * Paginate messages, even printing ignored fields.
315 */
316 int
317 More(void *v)
318 {
319 int *msgvec = v;
320
321 #ifdef MIME_SUPPORT
322 return type1(msgvec, 0, 1, de_mime("more"));
323 #else
324 return(type1(msgvec, 0, 1));
325 #endif
326 }
327
328 /*
329 * Type out messages, honor ignored fields.
330 */
331 int
332 type(void *v)
333 {
334 int *msgvec = v;
335
336 #ifdef MIME_SUPPORT
337 return type1(msgvec, 1, 0, de_mime("type"));
338 #else
339 return(type1(msgvec, 1, 0));
340 #endif
341 }
342
343 /*
344 * Type out messages, even printing ignored fields.
345 */
346 int
347 Type(void *v)
348 {
349 int *msgvec = v;
350
351 #ifdef MIME_SUPPORT
352 return type1(msgvec, 0, 0, de_mime("type"));
353 #else
354 return(type1(msgvec, 0, 0));
355 #endif
356 }
357
358
359 #ifdef MIME_SUPPORT
360 /*
361 * Paginate messages, honor ignored fields.
362 */
363 int
364 page(void *v)
365 {
366 int *msgvec = v;
367 #ifdef MIME_SUPPORT
368 return type1(msgvec, 1, 1, de_mime("page"));
369 #else
370 return(type1(msgvec, 1, 1));
371 #endif
372
373 }
374
375 /*
376 * Paginate messages, even printing ignored fields.
377 */
378 int
379 Page(void *v)
380 {
381 int *msgvec = v;
382
383 #ifdef MIME_SUPPORT
384 return type1(msgvec, 0, 1, de_mime("page"));
385 #else
386 return(type1(msgvec, 0, 1));
387 #endif
388 }
389
390 /*
391 * Type out messages, honor ignored fields.
392 */
393 int
394 print(void *v)
395 {
396 int *msgvec = v;
397
398 #ifdef MIME_SUPPORT
399 return type1(msgvec, 1, 0, de_mime("print"));
400 #else
401 return(type1(msgvec, 1, 0));
402 #endif
403 }
404
405 /*
406 * Type out messages, even printing ignored fields.
407 */
408 int
409 Print(void *v)
410 {
411 int *msgvec = v;
412
413 #ifdef MIME_SUPPORT
414 return type1(msgvec, 0, 0, de_mime("print"));
415 #else
416 return(type1(msgvec, 0, 0));
417 #endif
418 }
419
420 /*
421 * Identical to type(), but with opposite mime behavior.
422 */
423 int
424 view(void *v)
425 {
426 int *msgvec = v;
427 return type1(msgvec, 1, 0, !de_mime("print"));
428 }
429
430 /*
431 * Identical to Type(), but with opposite mime behavior.
432 */
433 int
434 View(void *v)
435 {
436 int *msgvec = v;
437
438 return type1(msgvec, 0, 0, !de_mime("print"));
439 }
440 #endif /* MIME_SUPPORT */
441
442 /*
443 * Type out the messages requested.
444 */
445 jmp_buf pipestop;
446 int
447 #ifdef MIME_SUPPORT
448 type1(int *msgvec, int doign, int dopage, int mime_decode)
449 #else
450 type1(int *msgvec, int doign, int dopage)
451 #endif
452 {
453 int *ip;
454 struct message *mp;
455 const char *cp;
456 int nlines;
457
458 /* Some volatile variables so longjmp will get the current not
459 * starting values. Note it is the variable that is volatile,
460 * not what it is pointing at! */
461 #ifdef MIME_SUPPORT
462 struct mime_info *volatile mip; /* avoid longjmp clobbering */
463 #endif
464 FILE *volatile obuf; /* avoid longjmp clobbering */
465
466 #ifdef MIME_SUPPORT
467 mip = NULL;
468 #endif
469 obuf = stdout;
470 if (setjmp(pipestop))
471 goto close_pipe;
472 if (value("interactive") != NULL &&
473 (dopage || (cp = value("crt")) != NULL)) {
474 nlines = 0;
475 if (!dopage) {
476 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++)
477 nlines += message[*ip - 1].m_blines;
478 }
479 if (dopage || nlines > (*cp ? atoi(cp) : realscreenheight)) {
480 cp = value("PAGER");
481 if (cp == NULL || *cp == '\0')
482 cp = _PATH_MORE;
483 obuf = Popen(cp, "w");
484 if (obuf == NULL) {
485 warn("%s", cp);
486 obuf = stdout;
487 } else
488 (void)signal(SIGPIPE, brokpipe);
489 }
490 }
491 for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) {
492 mp = &message[*ip - 1];
493 touch(mp);
494 dot = mp;
495 if (value("quiet") == NULL)
496 (void)fprintf(obuf, "Message %d:\n", *ip);
497 #ifdef MIME_SUPPORT
498 if (mime_decode)
499 mip = mime_decode_open(mp);
500 (void)mime_sendmessage(mp, obuf, doign ? ignore : 0, NULL, mip);
501 mime_decode_close(mip);
502 #else
503 (void)sendmessage(mp, obuf, doign ? ignore : 0, NULL);
504 #endif
505 }
506 close_pipe:
507 #ifdef MIME_SUPPORT
508 if (mip != NULL || obuf != stdout) {
509 /*
510 * Ignore SIGPIPE so it can't cause a duplicate close.
511 */
512 (void)signal(SIGPIPE, SIG_IGN);
513 mime_decode_close(mip);
514 if (obuf != stdout)
515 (void)Pclose(obuf);
516 (void)signal(SIGPIPE, SIG_DFL);
517
518 }
519 #else
520 if (obuf != stdout) {
521 /*
522 * Ignore SIGPIPE so it can't cause a duplicate close.
523 */
524 (void)signal(SIGPIPE, SIG_IGN);
525 (void)Pclose(obuf);
526 (void)signal(SIGPIPE, SIG_DFL);
527 }
528 #endif
529 return(0);
530 }
531
532 /*
533 * Respond to a broken pipe signal --
534 * probably caused by quitting more.
535 */
536 void
537 /*ARGSUSED*/
538 brokpipe(int signo __unused)
539 {
540 longjmp(pipestop, 1);
541 }
542
543 /*
544 * Pipe the current message buffer to a command.
545 */
546 int
547 pipecmd(void *v)
548 {
549 char *cmd = v;
550 FILE *volatile obuf; /* void longjmp clobbering - we want
551 the current value not start value */
552 if (dot == NULL) {
553 warn("pipcmd: no current message");
554 return 1;
555 }
556
557 obuf = stdout;
558 if (setjmp(pipestop))
559 goto close_pipe;
560
561 obuf = Popen(cmd, "w");
562 if (obuf == NULL) {
563 warn("pipecmd: Popen failed: %s", cmd);
564 return 1;
565 } else
566 (void)signal(SIGPIPE, brokpipe);
567
568 #ifdef MIME_SUPPORT
569 (void)sendmessage(dot, obuf, ignoreall, NULL, NULL);
570 #else
571 (void)sendmessage(dot, obuf, ignoreall, NULL);
572 #endif
573
574 close_pipe:
575 if (obuf != stdout) {
576 /*
577 * Ignore SIGPIPE so it can't cause a duplicate close.
578 */
579 (void)signal(SIGPIPE, SIG_IGN);
580 (void)Pclose(obuf);
581 (void)signal(SIGPIPE, SIG_DFL);
582 }
583 return 0;
584 }
585
586 /*
587 * Print the top so many lines of each desired message.
588 * The number of lines is taken from the variable "toplines"
589 * and defaults to 5.
590 */
591 int
592 top(void *v)
593 {
594 int *msgvec = v;
595 int *ip;
596 struct message *mp;
597 int c, topl, lines, lineb;
598 char *valtop, linebuf[LINESIZE];
599 FILE *ibuf;
600
601 topl = 5;
602 valtop = value("toplines");
603 if (valtop != NULL) {
604 topl = atoi(valtop);
605 if (topl < 0 || topl > 10000)
606 topl = 5;
607 }
608 lineb = 1;
609 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
610 mp = &message[*ip - 1];
611 touch(mp);
612 dot = mp;
613 if (value("quiet") == NULL)
614 (void)printf("Message %d:\n", *ip);
615 ibuf = setinput(mp);
616 c = mp->m_lines;
617 if (!lineb)
618 (void)printf("\n");
619 for (lines = 0; lines < c && lines <= topl; lines++) {
620 if (mail_readline(ibuf, linebuf, LINESIZE) < 0)
621 break;
622 (void)puts(linebuf);
623 lineb = blankline(linebuf);
624 }
625 }
626 return(0);
627 }
628
629 /*
630 * Touch all the given messages so that they will
631 * get mboxed.
632 */
633 int
634 stouch(void *v)
635 {
636 int *msgvec = v;
637 int *ip;
638
639 for (ip = msgvec; *ip != 0; ip++) {
640 dot = &message[*ip - 1];
641 dot->m_flag |= MTOUCH;
642 dot->m_flag &= ~MPRESERVE;
643 }
644 return(0);
645 }
646
647 /*
648 * Make sure all passed messages get mboxed.
649 */
650 int
651 mboxit(void *v)
652 {
653 int *msgvec = v;
654 int *ip;
655
656 for (ip = msgvec; *ip != 0; ip++) {
657 dot = &message[*ip - 1];
658 dot->m_flag |= MTOUCH|MBOX;
659 dot->m_flag &= ~MPRESERVE;
660 }
661 return(0);
662 }
663
664 /*
665 * List the folders the user currently has.
666 */
667 int
668 /*ARGSUSED*/
669 folders(void *v __unused)
670 {
671 char dirname[PATHSIZE];
672 const char *cmd;
673
674 if (getfold(dirname) < 0) {
675 (void)printf("No value set for \"folder\"\n");
676 return 1;
677 }
678 if ((cmd = value("LISTER")) == NULL)
679 cmd = "ls";
680 (void)run_command(cmd, 0, -1, -1, dirname, NULL);
681 return 0;
682 }
683
684 /*
685 * Update the mail file with any new messages that have
686 * come in since we started reading mail.
687 */
688 int
689 /*ARGSUSED*/
690 inc(void *v __unused)
691 {
692 int nmsg, mdot;
693
694 nmsg = incfile();
695
696 if (nmsg == 0) {
697 (void)printf("No new mail.\n");
698 } else if (nmsg > 0) {
699 mdot = newfileinfo(msgCount - nmsg);
700 dot = &message[mdot - 1];
701 } else {
702 (void)printf("\"inc\" command failed...\n");
703 }
704
705 return 0;
706 }
707