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 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 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 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 193 from(int *msgvec) 194 { 195 register int *ip; 196 197 for (ip = msgvec; *ip != NULL; 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 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 * 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 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 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 398 more(int *msgvec) 399 { 400 return (type1(msgvec, 1, 1)); 401 } 402 403 /* 404 * Paginate messages, even printing ignored fields. 405 */ 406 int 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 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 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 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 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 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 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 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 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 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 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