1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22
23 /*
24 * Copyright 2014 Joyent, Inc.
25 */
26
27 /*
28 * Copyright 2001 Sun Microsystems, Inc. All rights reserved.
29 * Use is subject to license terms.
30 */
31
32 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
33 /* All Rights Reserved */
34
35 /*
36 * University Copyright- Copyright (c) 1982, 1986, 1988
37 * The Regents of the University of California
38 * All Rights Reserved
39 *
40 * University Acknowledgment- Portions of this document are derived from
41 * software developed by the University of California, Berkeley, and its
42 * contributors.
43 */
44
45 #include <err.h>
46 #include "rcv.h"
47 #include <locale.h>
48
49 /*
50 * mailx -- a modified version of a University of California at Berkeley
51 * mail program
52 *
53 * User commands.
54 */
55
56 static char *dispname(char *hdr);
57 static void print(register struct message *mp, FILE *obuf, int doign);
58 static int type1(int *msgvec, int doign, int page);
59 static int topputs(const char *line, FILE *obuf);
60
61 void brokpipe(int sig);
62 jmp_buf pipestop;
63
64 /*
65 * Print the current active headings.
66 * Don't change dot if invoker didn't give an argument.
67 */
68
69 static int curscreen = 0, oldscreensize = 0;
70
71 int
headers(int * msgvec)72 headers(int *msgvec)
73 {
74 register int n, mesg, flag;
75 register struct message *mp;
76 int size;
77
78 size = screensize();
79 n = msgvec[0];
80 if (n != 0)
81 curscreen = (n-1)/size;
82 if (curscreen < 0)
83 curscreen = 0;
84 mp = &message[curscreen * size];
85 if (mp >= &message[msgCount])
86 mp = &message[msgCount - size];
87 if (mp < &message[0])
88 mp = &message[0];
89 flag = 0;
90 mesg = mp - &message[0];
91 if (dot != &message[n-1])
92 dot = mp;
93 if (Hflag)
94 mp = message;
95 for (; mp < &message[msgCount]; mp++) {
96 mesg++;
97 if (mp->m_flag & MDELETED)
98 continue;
99 if (flag++ >= size && !Hflag)
100 break;
101 printhead(mesg);
102 sreset();
103 }
104 if (flag == 0) {
105 printf(gettext("No more mail.\n"));
106 return (1);
107 }
108 return (0);
109 }
110
111 /*
112 * Scroll to the next/previous screen
113 */
114
115 int
scroll(char arg[])116 scroll(char arg[])
117 {
118 register int s, size;
119 int cur[1];
120
121 cur[0] = 0;
122 size = screensize();
123 s = curscreen;
124 switch (*arg) {
125 case 0:
126 case '+':
127 s++;
128 if (s * size > msgCount) {
129 printf(gettext("On last screenful of messages\n"));
130 return (0);
131 }
132 curscreen = s;
133 break;
134
135 case '-':
136 if (--s < 0) {
137 printf(gettext("On first screenful of messages\n"));
138 return (0);
139 }
140 curscreen = s;
141 break;
142
143 default:
144 printf(gettext("Unrecognized scrolling command \"%s\"\n"), arg);
145 return (1);
146 }
147 return (headers(cur));
148 }
149
150 /*
151 * Compute what the screen size should be.
152 * We use the following algorithm:
153 * If user specifies with screen option, use that.
154 * If baud rate < 1200, use 5
155 * If baud rate = 1200, use 10
156 * If baud rate > 1200, use 20
157 */
158 int
screensize(void)159 screensize(void)
160 {
161 register char *cp;
162 register int newscreensize, tmp;
163 #ifdef TIOCGWINSZ
164 struct winsize ws;
165 #endif
166
167 if ((cp = value("screen")) != NOSTR && (tmp = atoi(cp)) > 0)
168 newscreensize = tmp;
169 else if (baud < B1200)
170 newscreensize = 5;
171 else if (baud == B1200)
172 newscreensize = 10;
173 #ifdef TIOCGWINSZ
174 else if (ioctl(fileno(stdout), TIOCGWINSZ, &ws) == 0 && ws.ws_row > 4)
175 newscreensize = ws.ws_row - 4;
176 #endif
177 else
178 newscreensize = 20;
179 /* renormalize the value of curscreen */
180 if (newscreensize != oldscreensize) {
181 curscreen = curscreen * oldscreensize / newscreensize;
182 oldscreensize = newscreensize;
183 }
184 return (newscreensize);
185 }
186
187 /*
188 * Print out the headlines for each message
189 * in the passed message list.
190 */
191
192 int
from(int * msgvec)193 from(int *msgvec)
194 {
195 int *ip;
196
197 for (ip = msgvec; *ip != 0; ip++) {
198 printhead(*ip);
199 sreset();
200 }
201 if (--ip >= msgvec)
202 dot = &message[*ip - 1];
203 return (0);
204 }
205
206 /*
207 * Print out the header of a specific message.
208 * This is a slight improvement to the standard one.
209 */
210
211 void
printhead(int mesg)212 printhead(int mesg)
213 {
214 struct message *mp;
215 FILE *ibuf;
216 char headline[LINESIZE], *subjline, dispc, curind;
217 char *fromline;
218 char pbuf[LINESIZE];
219 char name[LINESIZE];
220 headline_t *hl;
221 register char *cp;
222 int showto;
223
224 if (headline_alloc(&hl) != 0) {
225 err(1, "could not allocate memory");
226 }
227
228 mp = &message[mesg-1];
229 ibuf = setinput(mp);
230 readline(ibuf, headline);
231 if ((subjline = hfield("subject", mp, addone)) == NOSTR &&
232 (subjline = hfield("subj", mp, addone)) == NOSTR &&
233 (subjline = hfield("message-status", mp, addone)) == NOSTR)
234 subjline = "";
235
236 curind = (!Hflag && dot == mp) ? '>' : ' ';
237 dispc = ' ';
238 showto = 0;
239 if ((mp->m_flag & (MREAD|MNEW)) == (MREAD|MNEW))
240 dispc = 'R';
241 if (!(int)value("bsdcompat") && (mp->m_flag & (MREAD|MNEW)) == MREAD)
242 dispc = 'O';
243 if ((mp->m_flag & (MREAD|MNEW)) == MNEW)
244 dispc = 'N';
245 if ((mp->m_flag & (MREAD|MNEW)) == 0)
246 dispc = 'U';
247 if (mp->m_flag & MSAVED)
248 if ((int)value("bsdcompat"))
249 dispc = '*';
250 else
251 dispc = 'S';
252 if (mp->m_flag & MPRESERVE)
253 if ((int)value("bsdcompat"))
254 dispc = 'P';
255 else
256 dispc = 'H';
257 if (mp->m_flag & MBOX)
258 dispc = 'M';
259 if (parse_headline(headline, hl) == -1) {
260 headline_reset(hl);
261 }
262 if (custr_len(hl->hl_date) == 0) {
263 if (custr_append(hl->hl_date, "<Unknown date>") != 0) {
264 err(1, "could not print header");
265 }
266 }
267
268 /*
269 * Netnews interface?
270 */
271
272 if (newsflg) {
273 if ((fromline = hfield("newsgroups", mp, addone)) == NOSTR &&
274 (fromline = hfield("article-id", mp, addone)) == NOSTR)
275 fromline = "<>";
276 else
277 for (cp = fromline; *cp; cp++) { /* limit length */
278 if (any(*cp, " ,\n")) {
279 *cp = '\0';
280 break;
281 }
282 }
283 /*
284 * else regular.
285 */
286
287 } else {
288 fromline = nameof(mp);
289 if (value("showto") &&
290 samebody(myname, skin(fromline), FALSE) &&
291 (cp = hfield("to", mp, addto))) {
292 showto = 1;
293 yankword(cp, fromline = name, sizeof (name),
294 docomma(cp));
295 }
296 if (value("showname"))
297 fromline = dispname(fromline);
298 else
299 fromline = skin(fromline);
300 }
301 printf("%c%c%3d ", curind, dispc, mesg);
302 if ((int)value("showfull")) {
303 if (showto)
304 printf("To %-15s ", fromline);
305 else
306 printf("%-18s ", fromline);
307 } else {
308 if (showto)
309 printf("To %-15.15s ", fromline);
310 else
311 printf("%-18.18s ", fromline);
312 }
313 if (mp->m_text) {
314 printf("%16.16s %4ld/%-5ld %-.25s\n",
315 custr_cstr(hl->hl_date), mp->m_lines, mp->m_size,
316 subjline);
317 } else {
318 printf("%16.16s binary/%-5ld %-.25s\n", custr_cstr(hl->hl_date),
319 mp->m_size, subjline);
320 }
321
322 headline_free(hl);
323 }
324
325 /*
326 * Return the full name from an RFC-822 header line
327 * or the last two (or one) component of the address.
328 */
329
330 static char *
dispname(char * hdr)331 dispname(char *hdr)
332 {
333 char *cp, *cp2;
334
335 if (hdr == 0)
336 return (0);
337 if (((cp = strchr(hdr, '<')) != 0) && (cp > hdr)) {
338 *cp = 0;
339 if ((*hdr == '"') && ((cp = strrchr(++hdr, '"')) != 0))
340 *cp = 0;
341 return (hdr);
342 } else if ((cp = strchr(hdr, '(')) != 0) {
343 hdr = ++cp;
344 if ((cp = strchr(hdr, '+')) != 0)
345 *cp = 0;
346 if ((cp = strrchr(hdr, ')')) != 0)
347 *cp = 0;
348 return (hdr);
349 }
350 cp = skin(hdr);
351 if ((cp2 = strrchr(cp, '!')) != 0) {
352 while (cp2 >= cp && *--cp2 != '!');
353 cp = ++cp2;
354 }
355 return (cp);
356 }
357
358 /*
359 * Print out the value of dot.
360 */
361
362 int
pdot(void)363 pdot(void)
364 {
365 printf("%d\n", dot - &message[0] + 1);
366 return (0);
367 }
368
369 /*
370 * Print out all the possible commands.
371 */
372
373 int
pcmdlist(void)374 pcmdlist(void)
375 {
376 register const struct cmd *cp;
377 register int cc;
378
379 printf("Commands are:\n");
380 for (cc = 0, cp = cmdtab; cp->c_name != NULL; cp++) {
381 cc += strlen(cp->c_name) + 2;
382 if (cc > 72) {
383 printf("\n");
384 cc = strlen(cp->c_name) + 2;
385 }
386 if ((cp+1)->c_name != NOSTR)
387 printf("%s, ", cp->c_name);
388 else
389 printf("%s\n", cp->c_name);
390 }
391 return (0);
392 }
393
394 /*
395 * Paginate messages, honor ignored fields.
396 */
397 int
more(int * msgvec)398 more(int *msgvec)
399 {
400 return (type1(msgvec, 1, 1));
401 }
402
403 /*
404 * Paginate messages, even printing ignored fields.
405 */
406 int
More(int * msgvec)407 More(int *msgvec)
408 {
409
410 return (type1(msgvec, 0, 1));
411 }
412
413 /*
414 * Type out messages, honor ignored fields.
415 */
416 int
type(int * msgvec)417 type(int *msgvec)
418 {
419
420 return (type1(msgvec, 1, 0));
421 }
422
423 /*
424 * Type out messages, even printing ignored fields.
425 */
426 int
Type(int * msgvec)427 Type(int *msgvec)
428 {
429
430 return (type1(msgvec, 0, 0));
431 }
432
433 /*
434 * Type out the messages requested.
435 */
436 static int
type1(int * msgvec,int doign,int page)437 type1(int *msgvec, int doign, int page)
438 {
439 int *ip;
440 register struct message *mp;
441 register int mesg;
442 register char *cp;
443 long nlines;
444 FILE *obuf;
445 void (*sigint)(int), (*sigpipe)(int);
446 int setsigs = 0;
447
448 obuf = stdout;
449 if (setjmp(pipestop)) {
450 if (obuf != stdout) {
451 pipef = NULL;
452 npclose(obuf);
453 }
454 goto ret0;
455 }
456 if (intty && outtty && (page || (cp = value("crt")) != NOSTR)) {
457 if (!page) {
458 nlines = 0;
459 for (ip = msgvec, nlines = 0;
460 *ip && ip-msgvec < msgCount; ip++)
461 nlines += message[*ip - 1].m_lines;
462 }
463 if (page ||
464 nlines > (*cp == '\0' ? screensize() - 2 : atoi(cp))) {
465 obuf = npopen(MORE, "w");
466 if (obuf == NULL) {
467 perror(MORE);
468 obuf = stdout;
469 } else {
470 pipef = obuf;
471 sigint = sigset(SIGINT, SIG_IGN);
472 sigpipe = sigset(SIGPIPE, brokpipe);
473 setsigs++;
474 }
475 }
476 }
477 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
478 mesg = *ip;
479 touch(mesg);
480 mp = &message[mesg-1];
481 dot = mp;
482 print(mp, obuf, doign);
483 }
484 if (obuf != stdout) {
485 pipef = NULL;
486 npclose(obuf);
487 }
488 ret0:
489 if (setsigs) {
490 sigset(SIGPIPE, sigpipe);
491 sigset(SIGINT, sigint);
492 }
493 return (0);
494 }
495
496 /*
497 * Respond to a broken pipe signal --
498 * probably caused by user quitting more.
499 */
500 void
501 #ifdef __cplusplus
brokpipe(int)502 brokpipe(int)
503 #else
504 /* ARGSUSED */
505 brokpipe(int s)
506 #endif
507 {
508 #ifdef OLD_BSD_SIGS
509 sigrelse(SIGPIPE);
510 #endif
511 longjmp(pipestop, 1);
512 }
513
514 /*
515 * Print the indicated message on standard output.
516 */
517
518 static void
print(register struct message * mp,FILE * obuf,int doign)519 print(register struct message *mp, FILE *obuf, int doign)
520 {
521
522 if (value("quiet") == NOSTR && (!doign || !isign("message", 0)))
523 fprintf(obuf, "Message %2d:\n", mp - &message[0] + 1);
524 touch(mp - &message[0] + 1);
525 if (mp->m_text) {
526 (void) msend(mp, obuf, doign ? M_IGNORE : 0, fputs);
527 } else {
528 fprintf(obuf, "\n%s\n", gettext(binmsg));
529 }
530 }
531
532 /*
533 * Print the top so many lines of each desired message.
534 * The number of lines is taken from the variable "toplines"
535 * and defaults to 5.
536 */
537
538 static long top_linecount, top_maxlines, top_lineb;
539 static jmp_buf top_buf;
540
541 int
top(int * msgvec)542 top(int *msgvec)
543 {
544 register int *ip;
545 register struct message *mp;
546 register int mesg;
547 char *valtop;
548
549 top_maxlines = 5;
550 valtop = value("toplines");
551 if (valtop != NOSTR) {
552 top_maxlines = atoi(valtop);
553 if (top_maxlines < 0 || top_maxlines > 10000)
554 top_maxlines = 5;
555 }
556 top_lineb = 1;
557 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
558 mesg = *ip;
559 touch(mesg);
560 mp = &message[mesg-1];
561 dot = mp;
562 if (value("quiet") == NOSTR)
563 printf("Message %2d:\n", mesg);
564 if (!top_lineb)
565 printf("\n");
566 top_linecount = 0;
567 if (setjmp(top_buf) == 0) {
568 if (mp->m_text) {
569 (void) msend(mp, stdout, M_IGNORE, topputs);
570 } else {
571 printf("\n%s\n", gettext(binmsg));
572 }
573 }
574 }
575 return (0);
576 }
577
578 int
topputs(const char * line,FILE * obuf)579 topputs(const char *line, FILE *obuf)
580 {
581 if (top_linecount++ >= top_maxlines)
582 longjmp(top_buf, 1);
583 top_lineb = blankline(line);
584 return (fputs(line, obuf));
585 }
586
587 /*
588 * Touch all the given messages so that they will
589 * get mboxed.
590 */
591
592 int
stouch(int msgvec[])593 stouch(int msgvec[])
594 {
595 register int *ip;
596
597 for (ip = msgvec; *ip != 0; ip++) {
598 dot = &message[*ip-1];
599 dot->m_flag |= MTOUCH;
600 dot->m_flag &= ~MPRESERVE;
601 }
602 return (0);
603 }
604
605 /*
606 * Make sure all passed messages get mboxed.
607 */
608
609 int
mboxit(int msgvec[])610 mboxit(int msgvec[])
611 {
612 register int *ip;
613
614 for (ip = msgvec; *ip != 0; ip++) {
615 dot = &message[*ip-1];
616 dot->m_flag |= MTOUCH|MBOX;
617 dot->m_flag &= ~MPRESERVE;
618 }
619 return (0);
620 }
621
622 /*
623 * List the folders the user currently has.
624 */
625 int
folders(char ** arglist)626 folders(char **arglist)
627 {
628 char dirname[BUFSIZ], cmd[BUFSIZ];
629
630 if (getfold(dirname) < 0) {
631 printf(gettext("No value set for \"folder\"\n"));
632 return (-1);
633 }
634 if (*arglist) {
635 nstrcat(dirname, sizeof (dirname), "/");
636 nstrcat(dirname, sizeof (dirname), *arglist);
637 }
638 snprintf(cmd, sizeof (cmd), "%s %s", LS, dirname);
639 return (system(cmd));
640 }
641