1 /* $NetBSD: cmd1.c,v 1.33 2012/06/12 19:03:26 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.33 2012/06/12 19:03:26 christos Exp $"); 38 #endif 39 #endif /* not lint */ 40 41 #include <assert.h> 42 43 #include "rcv.h" 44 #include "extern.h" 45 #include "format.h" 46 #ifdef MIME_SUPPORT 47 #include "mime.h" 48 #endif 49 #include "sig.h" 50 #include "thread.h" 51 52 53 /* 54 * Mail -- a mail program 55 * 56 * User commands. 57 */ 58 59 static int screen; 60 61 /* 62 * Compute screen size. 63 */ 64 static int 65 screensize(void) 66 { 67 int s; 68 char *cp; 69 70 if ((cp = value(ENAME_SCREEN)) != NULL && (s = atoi(cp)) > 0) 71 return s; 72 return screenheight - 4; 73 } 74 75 /* 76 * Print out the header of a specific message. 77 * This is a slight improvement to the standard one. 78 */ 79 PUBLIC void 80 printhead(int mesg) 81 { 82 const char *fmtstr; 83 char *msgline; 84 85 fmtstr = value(ENAME_HEADER_FORMAT); 86 if (fmtstr == NULL) 87 fmtstr = DEFAULT_HEADER_FORMAT; 88 msgline = smsgprintf(fmtstr, get_message(mesg)); 89 if (screenwidth > 0) 90 msgline[screenwidth] = '\0'; 91 (void)printf("%s\n", msgline); 92 sig_check(); 93 } 94 95 /* 96 * Print the current active headings. 97 * Don't change dot if invoker didn't give an argument. 98 */ 99 PUBLIC int 100 headers(void *v) 101 { 102 int *msgvec; 103 int n; 104 int flag; 105 struct message *mp; 106 int size; 107 108 msgvec = v; 109 size = screensize(); 110 n = msgvec[0]; 111 if (n != 0) 112 screen = (n - 1)/size; 113 if (screen < 0) 114 screen = 0; 115 116 if ((mp = get_message(screen * size + 1)) == NULL) { 117 int msgCount; 118 msgCount = get_msgCount(); 119 if (screen * size + 1 > msgCount) 120 mp = get_message(msgCount - size + 1); 121 if (mp == NULL) 122 mp = get_message(1); 123 } 124 flag = 0; 125 if (dot != get_message(n)) 126 dot = mp; 127 for (/*EMPTY*/; mp; mp = next_message(mp)) { 128 if (mp->m_flag & MDELETED) 129 continue; 130 if (flag++ >= size) 131 break; 132 printhead(get_msgnum(mp)); 133 } 134 if (flag == 0) { 135 (void)printf("No more mail.\n"); 136 return 1; 137 } 138 return 0; 139 } 140 141 /* 142 * Scroll to the next/previous screen 143 */ 144 PUBLIC int 145 scroll(void *v) 146 { 147 char *arg; 148 int s; 149 int size; 150 int cur[1]; 151 152 arg = v; 153 cur[0] = 0; 154 size = screensize(); 155 s = screen; 156 switch (*arg) { 157 case 0: 158 case '+': 159 s++; 160 if (s * size >= get_msgCount()) { 161 (void)printf("On last screenful of messages\n"); 162 return 0; 163 } 164 screen = s; 165 break; 166 167 case '-': 168 if (--s < 0) { 169 (void)printf("On first screenful of messages\n"); 170 return 0; 171 } 172 screen = s; 173 break; 174 175 default: 176 (void)printf("Unrecognized scrolling command \"%s\"\n", arg); 177 return 1; 178 } 179 return headers(cur); 180 } 181 182 /* 183 * Print out the headlines for each message 184 * in the passed message list. 185 */ 186 PUBLIC int 187 from(void *v) 188 { 189 int *msgvec; 190 int *ip; 191 192 msgvec = v; 193 for (ip = msgvec; *ip != 0; ip++) 194 printhead(*ip); 195 if (--ip >= msgvec) 196 dot = get_message(*ip); 197 return 0; 198 } 199 200 /* 201 * Print out the value of dot. 202 */ 203 /*ARGSUSED*/ 204 PUBLIC int 205 pdot(void *v) 206 { 207 int *msgvec; 208 209 msgvec = v; 210 dot = get_message(msgvec[0]); 211 212 (void)printf("%d\n", get_msgnum(dot)); 213 return 0; 214 } 215 216 /* 217 * Print out all the possible commands. 218 */ 219 /*ARGSUSED*/ 220 PUBLIC int 221 pcmdlist(void *v __unused) 222 { 223 const struct cmd *cp; 224 size_t cc; 225 226 (void)printf("Commands are:\n"); 227 cc = 0; 228 for (cp = cmdtab; cp->c_name != NULL; cp++) { 229 cc += strlen(cp->c_name) + 2; 230 if (cc > 72) { 231 (void)printf("\n"); 232 cc = strlen(cp->c_name) + 2; 233 } 234 if ((cp + 1)->c_name != NULL) 235 (void)printf("%s, ", cp->c_name); 236 else 237 (void)printf("%s\n", cp->c_name); 238 sig_check(); 239 } 240 return 0; 241 } 242 243 244 PUBLIC char * 245 sget_msgnum(struct message *mp, struct message *parent) 246 { 247 char *p; 248 249 if (parent == NULL || parent == mp) { 250 (void)sasprintf(&p, "%d", mp->m_index); 251 return p; 252 } 253 p = sget_msgnum(mp->m_plink, parent); 254 255 (void)sasprintf(&p, "%s.%d", p, mp->m_index); 256 return p; 257 } 258 259 PUBLIC void 260 show_msgnum(FILE *obuf, struct message *mp, struct message *parent) 261 { 262 263 if (value(ENAME_QUIET) == NULL) 264 (void)fprintf(obuf, "Message %s:\n", sget_msgnum(mp, parent)); 265 } 266 267 struct type1_core_args_s { 268 FILE *obuf; 269 struct message *parent; 270 struct ignoretab *igtab; 271 struct mime_info **mip; 272 }; 273 static int 274 type1_core(struct message *mp, void *v) 275 { 276 struct type1_core_args_s *args; 277 278 args = v; 279 touch(mp); 280 show_msgnum(args->obuf, mp, args->parent); 281 #ifdef MIME_SUPPORT 282 if (args->mip == NULL) 283 (void)mime_sendmessage(mp, args->obuf, args->igtab, NULL, NULL); 284 else { 285 *args->mip = mime_decode_open(mp); 286 (void)mime_sendmessage(mp, args->obuf, args->igtab, NULL, *args->mip); 287 mime_decode_close(*args->mip); 288 } 289 #else 290 (void)sendmessage(mp, args->obuf, args->igtab, NULL, NULL); 291 #endif 292 sig_check(); 293 return 0; 294 } 295 296 /* 297 * Respond to a broken pipe signal -- 298 * probably caused by quitting more. 299 */ 300 static jmp_buf pipestop; 301 302 /*ARGSUSED*/ 303 __dead static void 304 cmd1_brokpipe(int signo __unused) 305 { 306 307 longjmp(pipestop, 1); 308 } 309 310 /* 311 * Type out the messages requested. 312 */ 313 #ifndef MIME_SUPPORT 314 # define type1(a,b,c) legacy_type1(a,b) 315 #endif 316 static int 317 type1(int *msgvec, int doign, int mime_decode) 318 { 319 int recursive; 320 int *ip; 321 int msgCount; 322 /* 323 * Some volatile variables so longjmp will get the current not 324 * starting values. Note it is the variable that is volatile, 325 * not what it is pointing at! 326 */ 327 FILE *volatile obuf; /* avoid longjmp clobbering */ 328 sig_t volatile oldsigpipe; /* avoid longjmp clobbering? */ 329 #ifdef MIME_SUPPORT 330 struct mime_info *volatile mip; /* avoid longjmp clobbering? */ 331 332 mip = NULL; 333 #endif 334 335 if ((obuf = last_registered_file(0)) == NULL) 336 obuf = stdout; 337 338 /* 339 * Even without MIME_SUPPORT, we need to handle SIGPIPE here 340 * or else the handler in execute() will grab things and our 341 * exit code will never be seen. 342 */ 343 sig_check(); 344 oldsigpipe = sig_signal(SIGPIPE, cmd1_brokpipe); 345 if (setjmp(pipestop)) 346 goto close_pipe; 347 348 msgCount = get_msgCount(); 349 350 recursive = do_recursion(); 351 for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) { 352 struct type1_core_args_s args; 353 struct message *mp; 354 355 mp = get_message(*ip); 356 dot = mp; 357 args.obuf = obuf; 358 args.parent = recursive ? mp : NULL; 359 args.igtab = doign ? ignore : 0; 360 #ifdef MIME_SUPPORT 361 args.mip = mime_decode ? __UNVOLATILE(&mip) : NULL; 362 #else 363 args.mip = NULL; 364 #endif 365 (void)thread_recursion(mp, type1_core, &args); 366 } 367 close_pipe: 368 #ifdef MIME_SUPPORT 369 if (mip != NULL) { 370 struct sigaction osa; 371 sigset_t oset; 372 373 /* 374 * Ignore SIGPIPE so it can't cause a duplicate close. 375 */ 376 (void)sig_ignore(SIGPIPE, &osa, &oset); 377 mime_decode_close(mip); 378 (void)sig_restore(SIGPIPE, &osa, &oset); 379 } 380 #endif 381 (void)sig_signal(SIGPIPE, oldsigpipe); 382 sig_check(); 383 return 0; 384 } 385 386 #ifdef MIME_SUPPORT 387 static int 388 de_mime(void) 389 { 390 391 return value(ENAME_MIME_DECODE_MSG) != NULL; 392 } 393 394 /* 395 * Identical to type(), but with opposite mime behavior. 396 */ 397 PUBLIC int 398 view(void *v) 399 { 400 int *msgvec; 401 402 msgvec = v; 403 return type1(msgvec, 1, !de_mime()); 404 } 405 406 /* 407 * Identical to Type(), but with opposite mime behavior. 408 */ 409 PUBLIC int 410 View(void *v) 411 { 412 int *msgvec; 413 414 msgvec = v; 415 return type1(msgvec, 0, !de_mime()); 416 } 417 #endif /* MIME_SUPPORT */ 418 419 /* 420 * Type out messages, honor ignored fields. 421 */ 422 PUBLIC int 423 type(void *v) 424 { 425 int *msgvec; 426 427 msgvec = v; 428 return type1(msgvec, 1, de_mime()); 429 } 430 431 /* 432 * Type out messages, even printing ignored fields. 433 */ 434 PUBLIC int 435 Type(void *v) 436 { 437 int *msgvec; 438 439 msgvec = v; 440 return type1(msgvec, 0, de_mime()); 441 } 442 443 /* 444 * Pipe the current message buffer to a command. 445 */ 446 PUBLIC int 447 pipecmd(void *v) 448 { 449 char *cmd; 450 FILE *volatile obuf; /* void longjmp clobbering */ 451 sig_t volatile oldsigpipe = sig_current(SIGPIPE); 452 453 cmd = v; 454 if (dot == NULL) { 455 warn("pipcmd: no current message"); 456 return 1; 457 } 458 459 obuf = stdout; 460 if (setjmp(pipestop)) 461 goto close_pipe; 462 463 sig_check(); 464 obuf = Popen(cmd, "we"); 465 if (obuf == NULL) { 466 warn("pipecmd: Popen failed: %s", cmd); 467 return 1; 468 } 469 470 oldsigpipe = sig_signal(SIGPIPE, cmd1_brokpipe); 471 472 (void)sendmessage(dot, obuf, ignoreall, NULL, NULL); 473 close_pipe: 474 sig_check(); 475 if (obuf != stdout) { 476 struct sigaction osa; 477 sigset_t oset; 478 /* 479 * Ignore SIGPIPE so it can't cause a duplicate close. 480 */ 481 (void)sig_ignore(SIGPIPE, &osa, &oset); 482 (void)Pclose(obuf); 483 (void)sig_restore(SIGPIPE, &osa, &oset); 484 } 485 (void)sig_signal(SIGPIPE, oldsigpipe); 486 sig_check(); 487 return 0; 488 } 489 490 struct top_core_args_s { 491 int lineb; 492 size_t topl; 493 struct message *parent; 494 }; 495 static int 496 top_core(struct message *mp, void *v) 497 { 498 char buffer[LINESIZE]; 499 struct top_core_args_s *args; 500 FILE *ibuf; 501 size_t lines; 502 size_t c; 503 504 args = v; 505 touch(mp); 506 if (!args->lineb) 507 (void)printf("\n"); 508 show_msgnum(stdout, mp, args->parent); 509 ibuf = setinput(mp); 510 c = mp->m_lines; 511 for (lines = 0; lines < c && lines <= args->topl; lines++) { 512 sig_check(); 513 if (readline(ibuf, buffer, (int)sizeof(buffer), 0) < 0) 514 break; 515 (void)puts(buffer); 516 args->lineb = blankline(buffer); 517 } 518 sig_check(); 519 return 0; 520 } 521 522 /* 523 * Print the top so many lines of each desired message. 524 * The number of lines is taken from the variable "toplines" 525 * and defaults to 5. 526 */ 527 PUBLIC int 528 top(void *v) 529 { 530 struct top_core_args_s args; 531 int recursive; 532 int msgCount; 533 int *msgvec; 534 int *ip; 535 int topl; 536 char *valtop; 537 538 msgvec = v; 539 topl = 5; 540 valtop = value(ENAME_TOPLINES); 541 if (valtop != NULL) { 542 topl = atoi(valtop); 543 if (topl < 0 || topl > 10000) 544 topl = 5; 545 } 546 args.topl = topl; 547 args.lineb = 1; 548 recursive = do_recursion(); 549 msgCount = get_msgCount(); 550 for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) { 551 struct message *mp; 552 553 mp = get_message(*ip); 554 dot = mp; 555 args.parent = recursive ? mp : NULL; 556 (void)thread_recursion(mp, top_core, &args); 557 } 558 return 0; 559 } 560 561 /* 562 * Touch all the given messages so that they will 563 * get mboxed. 564 */ 565 PUBLIC int 566 stouch(void *v) 567 { 568 int *msgvec; 569 int *ip; 570 571 msgvec = v; 572 for (ip = msgvec; *ip != 0; ip++) { 573 sig_check(); 574 dot = set_m_flag(*ip, ~(MPRESERVE | MTOUCH), MTOUCH); 575 } 576 return 0; 577 } 578 579 /* 580 * Make sure all passed messages get mboxed. 581 */ 582 PUBLIC int 583 mboxit(void *v) 584 { 585 int *msgvec; 586 int *ip; 587 588 msgvec = v; 589 for (ip = msgvec; *ip != 0; ip++) { 590 sig_check(); 591 dot = set_m_flag(*ip, 592 ~(MPRESERVE | MTOUCH | MBOX), MTOUCH | MBOX); 593 } 594 return 0; 595 } 596 597 /* 598 * List the folders the user currently has. 599 */ 600 /*ARGSUSED*/ 601 PUBLIC int 602 folders(void *v __unused) 603 { 604 char dirname[PATHSIZE]; 605 const char *cmd; 606 607 if (getfold(dirname, sizeof(dirname)) < 0) { 608 (void)printf("No value set for \"folder\"\n"); 609 return 1; 610 } 611 if ((cmd = value(ENAME_LISTER)) == NULL) 612 cmd = "ls"; 613 (void)run_command(cmd, NULL, -1, -1, dirname, NULL); 614 return 0; 615 } 616 617 /* 618 * Update the mail file with any new messages that have 619 * come in since we started reading mail. 620 */ 621 /*ARGSUSED*/ 622 PUBLIC int 623 inc(void *v __unused) 624 { 625 int nmsg; 626 int mdot; 627 628 nmsg = incfile(); 629 630 if (nmsg == 0) { 631 (void)printf("No new mail.\n"); 632 } else if (nmsg > 0) { 633 struct message *mp; 634 mdot = newfileinfo(get_abs_msgCount() - nmsg); 635 if ((mp = get_message(mdot)) != NULL) 636 dot = mp; 637 } else { 638 (void)printf("\"inc\" command failed...\n"); 639 } 640 return 0; 641 } 642