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