1 /* $NetBSD: cmd1.c,v 1.34 2013/10/18 20:17:59 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.34 2013/10/18 20:17:59 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 int * volatile mvec; 329 sig_t volatile oldsigpipe; /* avoid longjmp clobbering? */ 330 #ifdef MIME_SUPPORT 331 struct mime_info *volatile mip; /* avoid longjmp clobbering? */ 332 333 mip = NULL; 334 #endif 335 336 mvec = msgvec; 337 if ((obuf = last_registered_file(0)) == NULL) 338 obuf = stdout; 339 340 /* 341 * Even without MIME_SUPPORT, we need to handle SIGPIPE here 342 * or else the handler in execute() will grab things and our 343 * exit code will never be seen. 344 */ 345 sig_check(); 346 oldsigpipe = sig_signal(SIGPIPE, cmd1_brokpipe); 347 if (setjmp(pipestop)) 348 goto close_pipe; 349 350 msgCount = get_msgCount(); 351 352 recursive = do_recursion(); 353 for (ip = mvec; *ip && ip - mvec < msgCount; ip++) { 354 struct type1_core_args_s args; 355 struct message *mp; 356 357 mp = get_message(*ip); 358 dot = mp; 359 args.obuf = obuf; 360 args.parent = recursive ? mp : NULL; 361 args.igtab = doign ? ignore : 0; 362 #ifdef MIME_SUPPORT 363 args.mip = mime_decode ? __UNVOLATILE(&mip) : NULL; 364 #else 365 args.mip = NULL; 366 #endif 367 (void)thread_recursion(mp, type1_core, &args); 368 } 369 close_pipe: 370 #ifdef MIME_SUPPORT 371 if (mip != NULL) { 372 struct sigaction osa; 373 sigset_t oset; 374 375 /* 376 * Ignore SIGPIPE so it can't cause a duplicate close. 377 */ 378 (void)sig_ignore(SIGPIPE, &osa, &oset); 379 mime_decode_close(mip); 380 (void)sig_restore(SIGPIPE, &osa, &oset); 381 } 382 #endif 383 (void)sig_signal(SIGPIPE, oldsigpipe); 384 sig_check(); 385 return 0; 386 } 387 388 #ifdef MIME_SUPPORT 389 static int 390 de_mime(void) 391 { 392 393 return value(ENAME_MIME_DECODE_MSG) != NULL; 394 } 395 396 /* 397 * Identical to type(), but with opposite mime behavior. 398 */ 399 PUBLIC int 400 view(void *v) 401 { 402 int *msgvec; 403 404 msgvec = v; 405 return type1(msgvec, 1, !de_mime()); 406 } 407 408 /* 409 * Identical to Type(), but with opposite mime behavior. 410 */ 411 PUBLIC int 412 View(void *v) 413 { 414 int *msgvec; 415 416 msgvec = v; 417 return type1(msgvec, 0, !de_mime()); 418 } 419 #endif /* MIME_SUPPORT */ 420 421 /* 422 * Type out messages, honor ignored fields. 423 */ 424 PUBLIC int 425 type(void *v) 426 { 427 int *msgvec; 428 429 msgvec = v; 430 return type1(msgvec, 1, de_mime()); 431 } 432 433 /* 434 * Type out messages, even printing ignored fields. 435 */ 436 PUBLIC int 437 Type(void *v) 438 { 439 int *msgvec; 440 441 msgvec = v; 442 return type1(msgvec, 0, de_mime()); 443 } 444 445 /* 446 * Pipe the current message buffer to a command. 447 */ 448 PUBLIC int 449 pipecmd(void *v) 450 { 451 char *cmd; 452 FILE *volatile obuf; /* void longjmp clobbering */ 453 sig_t volatile oldsigpipe = sig_current(SIGPIPE); 454 455 cmd = v; 456 if (dot == NULL) { 457 warn("pipcmd: no current message"); 458 return 1; 459 } 460 461 obuf = stdout; 462 if (setjmp(pipestop)) 463 goto close_pipe; 464 465 sig_check(); 466 obuf = Popen(cmd, "we"); 467 if (obuf == NULL) { 468 warn("pipecmd: Popen failed: %s", cmd); 469 return 1; 470 } 471 472 oldsigpipe = sig_signal(SIGPIPE, cmd1_brokpipe); 473 474 (void)sendmessage(dot, obuf, ignoreall, NULL, NULL); 475 close_pipe: 476 sig_check(); 477 if (obuf != stdout) { 478 struct sigaction osa; 479 sigset_t oset; 480 /* 481 * Ignore SIGPIPE so it can't cause a duplicate close. 482 */ 483 (void)sig_ignore(SIGPIPE, &osa, &oset); 484 (void)Pclose(obuf); 485 (void)sig_restore(SIGPIPE, &osa, &oset); 486 } 487 (void)sig_signal(SIGPIPE, oldsigpipe); 488 sig_check(); 489 return 0; 490 } 491 492 struct top_core_args_s { 493 int lineb; 494 size_t topl; 495 struct message *parent; 496 }; 497 static int 498 top_core(struct message *mp, void *v) 499 { 500 char buffer[LINESIZE]; 501 struct top_core_args_s *args; 502 FILE *ibuf; 503 size_t lines; 504 size_t c; 505 506 args = v; 507 touch(mp); 508 if (!args->lineb) 509 (void)printf("\n"); 510 show_msgnum(stdout, mp, args->parent); 511 ibuf = setinput(mp); 512 c = mp->m_lines; 513 for (lines = 0; lines < c && lines <= args->topl; lines++) { 514 sig_check(); 515 if (readline(ibuf, buffer, (int)sizeof(buffer), 0) < 0) 516 break; 517 (void)puts(buffer); 518 args->lineb = blankline(buffer); 519 } 520 sig_check(); 521 return 0; 522 } 523 524 /* 525 * Print the top so many lines of each desired message. 526 * The number of lines is taken from the variable "toplines" 527 * and defaults to 5. 528 */ 529 PUBLIC int 530 top(void *v) 531 { 532 struct top_core_args_s args; 533 int recursive; 534 int msgCount; 535 int *msgvec; 536 int *ip; 537 int topl; 538 char *valtop; 539 540 msgvec = v; 541 topl = 5; 542 valtop = value(ENAME_TOPLINES); 543 if (valtop != NULL) { 544 topl = atoi(valtop); 545 if (topl < 0 || topl > 10000) 546 topl = 5; 547 } 548 args.topl = topl; 549 args.lineb = 1; 550 recursive = do_recursion(); 551 msgCount = get_msgCount(); 552 for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) { 553 struct message *mp; 554 555 mp = get_message(*ip); 556 dot = mp; 557 args.parent = recursive ? mp : NULL; 558 (void)thread_recursion(mp, top_core, &args); 559 } 560 return 0; 561 } 562 563 /* 564 * Touch all the given messages so that they will 565 * get mboxed. 566 */ 567 PUBLIC int 568 stouch(void *v) 569 { 570 int *msgvec; 571 int *ip; 572 573 msgvec = v; 574 for (ip = msgvec; *ip != 0; ip++) { 575 sig_check(); 576 dot = set_m_flag(*ip, ~(MPRESERVE | MTOUCH), MTOUCH); 577 } 578 return 0; 579 } 580 581 /* 582 * Make sure all passed messages get mboxed. 583 */ 584 PUBLIC int 585 mboxit(void *v) 586 { 587 int *msgvec; 588 int *ip; 589 590 msgvec = v; 591 for (ip = msgvec; *ip != 0; ip++) { 592 sig_check(); 593 dot = set_m_flag(*ip, 594 ~(MPRESERVE | MTOUCH | MBOX), MTOUCH | MBOX); 595 } 596 return 0; 597 } 598 599 /* 600 * List the folders the user currently has. 601 */ 602 /*ARGSUSED*/ 603 PUBLIC int 604 folders(void *v __unused) 605 { 606 char dirname[PATHSIZE]; 607 const char *cmd; 608 609 if (getfold(dirname, sizeof(dirname)) < 0) { 610 (void)printf("No value set for \"folder\"\n"); 611 return 1; 612 } 613 if ((cmd = value(ENAME_LISTER)) == NULL) 614 cmd = "ls"; 615 (void)run_command(cmd, NULL, -1, -1, dirname, NULL); 616 return 0; 617 } 618 619 /* 620 * Update the mail file with any new messages that have 621 * come in since we started reading mail. 622 */ 623 /*ARGSUSED*/ 624 PUBLIC int 625 inc(void *v __unused) 626 { 627 int nmsg; 628 int mdot; 629 630 nmsg = incfile(); 631 632 if (nmsg == 0) { 633 (void)printf("No new mail.\n"); 634 } else if (nmsg > 0) { 635 struct message *mp; 636 mdot = newfileinfo(get_abs_msgCount() - nmsg); 637 if ((mp = get_message(mdot)) != NULL) 638 dot = mp; 639 } else { 640 (void)printf("\"inc\" command failed...\n"); 641 } 642 return 0; 643 } 644