1/* $NetBSD: cmd1.c,v 1.30 2009/04/10 13:08:24 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 35static char sccsid[] = "@(#)cmd1.c 8.2 (Berkeley) 4/20/95"; 36#else 37__RCSID("$NetBSD: cmd1.c,v 1.30 2009/04/10 13:08:24 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 59static int screen; 60 61/* 62 * Compute screen size. 63 */ 64static int 65screensize(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 */ 79PUBLIC void 80printhead(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 */ 99PUBLIC int 100headers(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 */ 144PUBLIC int 145scroll(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 */ 186PUBLIC int 187from(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*/ 204PUBLIC int 205pdot(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*/ 220PUBLIC int 221pcmdlist(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 244PUBLIC char * 245sget_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 259PUBLIC void 260show_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 267struct type1_core_args_s { 268 FILE *obuf; 269 struct message *parent; 270 struct ignoretab *igtab; 271 struct mime_info **mip; 272}; 273static int 274type1_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 */ 300static jmp_buf pipestop; 301 302/*ARGSUSED*/ 303__dead static void 304cmd1_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 316static int 317type1(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 } 367close_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 387static int 388de_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 */ 397PUBLIC int 398view(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 */ 409PUBLIC int 410View(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 */ 422PUBLIC int 423type(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 */ 434PUBLIC int 435Type(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 */ 446PUBLIC int 447pipecmd(void *v) 448{ 449 char *cmd; 450 FILE *volatile obuf; /* void longjmp clobbering */ 451 sig_t volatile oldsigpipe; /* XXX - is volatile needed? */ 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, "w"); 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 490struct top_core_args_s { 491 int lineb; 492 size_t topl; 493 struct message *parent; 494}; 495static int 496top_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 */ 527PUBLIC int 528top(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 */ 565PUBLIC int 566stouch(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 */ 582PUBLIC int 583mboxit(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*/ 601PUBLIC int 602folders(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*/ 622PUBLIC int 623inc(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