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