1 /* $OpenBSD: cmd1.c,v 1.24 2003/06/03 02:56:11 millert Exp $ */ 2 /* $NetBSD: cmd1.c,v 1.9 1997/07/09 05:29:48 mikel Exp $ */ 3 4 /*- 5 * Copyright (c) 1980, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #ifndef lint 34 #if 0 35 static const char sccsid[] = "@(#)cmd1.c 8.2 (Berkeley) 4/20/95"; 36 #else 37 static const char rcsid[] = "$OpenBSD: cmd1.c,v 1.24 2003/06/03 02:56:11 millert Exp $"; 38 #endif 39 #endif /* not lint */ 40 41 #include "rcv.h" 42 #include "extern.h" 43 44 /* 45 * Mail -- a mail program 46 * 47 * User commands. 48 */ 49 50 /* 51 * Print the current active headings. 52 * Don't change dot if invoker didn't give an argument. 53 */ 54 55 static int screen; 56 static volatile sig_atomic_t gothdrint; 57 58 int 59 headers(void *v) 60 { 61 int *msgvec = v; 62 int n, mesg, flag, size; 63 struct message *mp; 64 struct sigaction act, oact; 65 sigset_t oset; 66 67 size = screensize(); 68 n = msgvec[0]; 69 if (n != 0) 70 screen = (n-1)/size; 71 if (screen < 0) 72 screen = 0; 73 mp = &message[screen * size]; 74 if (mp >= &message[msgCount]) 75 mp = &message[msgCount - size]; 76 if (mp < &message[0]) 77 mp = &message[0]; 78 flag = 0; 79 mesg = mp - &message[0]; 80 if (dot != &message[n-1]) 81 dot = mp; 82 sigemptyset(&act.sa_mask); 83 act.sa_flags = SA_RESTART; 84 act.sa_handler = hdrint; 85 if (sigaction(SIGINT, NULL, &oact) == 0 && 86 oact.sa_handler != SIG_IGN) { 87 (void)sigaction(SIGINT, &act, &oact); 88 (void)sigprocmask(SIG_UNBLOCK, &intset, &oset); 89 } 90 for (gothdrint = 0; !gothdrint && mp < &message[msgCount]; mp++) { 91 mesg++; 92 if (mp->m_flag & MDELETED) 93 continue; 94 if (flag++ >= size) 95 break; 96 printhead(mesg); 97 } 98 if (gothdrint) { 99 fflush(stdout); 100 fputs("\nInterrupt\n", stderr); 101 } 102 if (oact.sa_handler != SIG_IGN) { 103 (void)sigprocmask(SIG_SETMASK, &oset, NULL); 104 (void)sigaction(SIGINT, &oact, NULL); 105 } 106 if (flag == 0) { 107 puts("No more mail."); 108 return(1); 109 } 110 return(0); 111 } 112 113 /* 114 * Scroll to the next/previous screen 115 */ 116 int 117 scroll(void *v) 118 { 119 char *arg = v; 120 int size, maxscreen; 121 int cur[1]; 122 123 cur[0] = 0; 124 size = screensize(); 125 maxscreen = (msgCount - 1) / size; 126 switch (*arg) { 127 case 0: 128 case '+': 129 if (screen >= maxscreen) { 130 puts("On last screenful of messages"); 131 return(0); 132 } 133 screen++; 134 break; 135 136 case '-': 137 if (screen <= 0) { 138 puts("On first screenful of messages"); 139 return(0); 140 } 141 screen--; 142 break; 143 144 default: 145 printf("Unrecognized scrolling command \"%s\"\n", arg); 146 return(1); 147 } 148 return(headers(cur)); 149 } 150 151 /* 152 * Compute screen size. 153 */ 154 int 155 screensize(void) 156 { 157 int s; 158 char *cp; 159 160 if ((cp = value("screen")) != NULL && (s = atoi(cp)) > 0) 161 return(s); 162 return(screenheight - 4); 163 } 164 165 /* 166 * Print out the headlines for each message 167 * in the passed message list. 168 */ 169 int 170 from(void *v) 171 { 172 int *msgvec = v; 173 int *ip; 174 175 for (ip = msgvec; *ip != NULL; ip++) 176 printhead(*ip); 177 if (--ip >= msgvec) 178 dot = &message[*ip - 1]; 179 return(0); 180 } 181 182 /* 183 * Print out the header of a specific message. 184 * This is a slight improvement to the standard one. 185 */ 186 void 187 printhead(int mesg) 188 { 189 struct message *mp; 190 char headline[LINESIZE], wcount[LINESIZE], *subjline, dispc, curind; 191 char visline[LINESIZE]; 192 char pbuf[BUFSIZ]; 193 struct headline hl; 194 int subjlen; 195 char *name; 196 char *to, *from; 197 struct name *np; 198 char **ap; 199 200 mp = &message[mesg-1]; 201 (void)readline(setinput(mp), headline, LINESIZE, NULL); 202 if ((subjline = hfield("subject", mp)) == NULL) 203 subjline = hfield("subj", mp); 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, sizeof(wcount), "%4d/%-5d", mp->m_lines, 221 mp->m_size); 222 subjlen = screenwidth - 44 - strlen(wcount); 223 from = nameof(mp, 0); 224 to = skin(hfield("to", mp)); 225 np = extract(from, GTO); 226 np = delname(np, myname); 227 if (altnames) 228 for (ap = altnames; *ap; ap++) 229 np = delname(np, *ap); 230 if (np) 231 /* not from me */ 232 name = value("show-rcpt") != NULL && to ? to : from; 233 else 234 /* from me - show TO */ 235 name = value("showto") != NULL && to ? to : from; 236 if (subjline == NULL || subjlen < 0) { /* pretty pathetic */ 237 subjline=""; 238 subjlen=0; 239 } 240 printf("%c%c%3d ", curind, dispc, mesg); 241 strnvis(visline, name, sizeof(visline), VIS_SAFE|VIS_NOSLASH); 242 if (name == to) 243 printf("TO %-14.14s", visline); 244 else 245 printf("%-17.17s", visline); 246 /* hl.l_date was sanity-checked when read in. wcount we just made. */ 247 strnvis(visline, subjline, sizeof(visline), VIS_SAFE|VIS_NOSLASH); 248 printf(" %16.16s %s %.*s\n", hl.l_date, wcount, subjlen, visline); 249 } 250 251 /* 252 * Print out the value of dot. 253 */ 254 int 255 pdot(void *v) 256 { 257 printf("%d\n", (int)(dot - &message[0] + 1)); 258 return(0); 259 } 260 261 /* 262 * Print out all the possible commands. 263 */ 264 int 265 pcmdlist(void *v) 266 { 267 extern const struct cmd cmdtab[]; 268 const struct cmd *cp; 269 int cc; 270 271 puts("Commands are:"); 272 for (cc = 0, cp = cmdtab; cp->c_name != NULL; cp++) { 273 cc += strlen(cp->c_name) + 2; 274 if (cc > 72) { 275 putchar('\n'); 276 cc = strlen(cp->c_name) + 2; 277 } 278 if ((cp+1)->c_name != NULL) 279 printf("%s, ", cp->c_name); 280 else 281 puts(cp->c_name); 282 } 283 return(0); 284 } 285 286 /* 287 * Pipe message to command 288 */ 289 int 290 pipeit(void *ml, void *sl) 291 { 292 int *msgvec = ml; 293 char *cmd = sl; 294 295 return(type1(msgvec, cmd, 0, 0)); 296 } 297 298 /* 299 * Paginate messages, honor ignored fields. 300 */ 301 int 302 more(void *v) 303 { 304 int *msgvec = v; 305 return(type1(msgvec, NULL, 1, 1)); 306 } 307 308 /* 309 * Paginate messages, even printing ignored fields. 310 */ 311 int 312 More(void *v) 313 { 314 int *msgvec = v; 315 316 return(type1(msgvec, NULL, 0, 1)); 317 } 318 319 /* 320 * Type out messages, honor ignored fields. 321 */ 322 int 323 type(void *v) 324 { 325 int *msgvec = v; 326 327 return(type1(msgvec, NULL, 1, 0)); 328 } 329 330 /* 331 * Type out messages, even printing ignored fields. 332 */ 333 int 334 Type(void *v) 335 { 336 int *msgvec = v; 337 338 return(type1(msgvec, NULL, 0, 0)); 339 } 340 341 /* 342 * Type out the messages requested. 343 */ 344 int 345 type1(int *msgvec, char *cmd, int doign, int page) 346 { 347 int nlines, *ip, restoreterm; 348 struct message *mp; 349 struct termios tbuf; 350 char *cp; 351 FILE *obuf; 352 353 obuf = stdout; 354 restoreterm = 0; 355 356 /* 357 * start a pipe if needed. 358 */ 359 if (cmd) { 360 restoreterm = (tcgetattr(fileno(stdin), &tbuf) == 0); 361 obuf = Popen(cmd, "w"); 362 if (obuf == NULL) { 363 warn("%s", cmd); 364 obuf = stdout; 365 } 366 } else if (value("interactive") != NULL && 367 (page || (cp = value("crt")) != NULL)) { 368 nlines = 0; 369 if (!page) { 370 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) 371 nlines += message[*ip - 1].m_lines; 372 } 373 if (page || nlines > (*cp ? atoi(cp) : realscreenheight)) { 374 restoreterm = (tcgetattr(fileno(stdin), &tbuf) == 0); 375 obuf = Popen(value("PAGER"), "w"); 376 if (obuf == NULL) { 377 warn("%s", cp); 378 obuf = stdout; 379 } 380 } 381 } 382 383 /* 384 * Send messages to the output. 385 */ 386 for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) { 387 mp = &message[*ip - 1]; 388 touch(mp); 389 dot = mp; 390 if (cmd == NULL && value("quiet") == NULL) 391 fprintf(obuf, "Message %d:\n", *ip); 392 if (sendmessage(mp, obuf, doign ? ignore : 0, NULL) == -1) 393 break; 394 } 395 396 if (obuf != stdout) { 397 (void)Pclose(obuf); 398 if (restoreterm) 399 (void)tcsetattr(fileno(stdin), TCSADRAIN, &tbuf); 400 } 401 return(0); 402 } 403 404 /* 405 * Print the top so many lines of each desired message. 406 * The number of lines is taken from the variable "toplines" 407 * and defaults to 5. 408 */ 409 int 410 top(void * v) 411 { 412 int *msgvec = v; 413 int *ip; 414 struct message *mp; 415 int c, topl, lines, lineb; 416 char *valtop, linebuf[LINESIZE]; 417 FILE *ibuf; 418 419 topl = 5; 420 valtop = value("toplines"); 421 if (valtop != NULL) { 422 topl = atoi(valtop); 423 if (topl < 0 || topl > 10000) 424 topl = 5; 425 } 426 lineb = 1; 427 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { 428 mp = &message[*ip - 1]; 429 touch(mp); 430 dot = mp; 431 if (value("quiet") == NULL) 432 printf("Message %d:\n", *ip); 433 ibuf = setinput(mp); 434 c = mp->m_lines; 435 if (!lineb) 436 putchar('\n'); 437 for (lines = 0; lines < c && lines <= topl; lines++) { 438 if (readline(ibuf, linebuf, sizeof(linebuf), NULL) < 0) 439 break; 440 puts(linebuf); 441 lineb = blankline(linebuf); 442 } 443 } 444 return(0); 445 } 446 447 /* 448 * Touch all the given messages so that they will 449 * get mboxed. 450 */ 451 int 452 stouch(void *v) 453 { 454 int *msgvec = v; 455 int *ip; 456 457 for (ip = msgvec; *ip != 0; ip++) { 458 dot = &message[*ip-1]; 459 dot->m_flag |= MTOUCH; 460 dot->m_flag &= ~MPRESERVE; 461 } 462 return(0); 463 } 464 465 /* 466 * Make sure all passed messages get mboxed. 467 */ 468 int 469 mboxit(void *v) 470 { 471 int *msgvec = v; 472 int *ip; 473 474 for (ip = msgvec; *ip != 0; ip++) { 475 dot = &message[*ip-1]; 476 dot->m_flag |= MTOUCH|MBOX; 477 dot->m_flag &= ~MPRESERVE; 478 } 479 return(0); 480 } 481 482 /* 483 * List the folders the user currently has. 484 */ 485 int 486 folders(void *v) 487 { 488 char *files = (char *)v; 489 char dirname[PATHSIZE]; 490 char cmd[BUFSIZ]; 491 492 if (getfold(dirname, sizeof(dirname)) < 0) 493 strlcpy(dirname, "$HOME", sizeof(dirname)); 494 495 snprintf(cmd, sizeof(cmd), "cd %s; %s %s", dirname, value("LISTER"), 496 files && *files ? files : ""); 497 498 (void)run_command(value("SHELL"), 0, -1, -1, "-c", cmd, NULL); 499 return(0); 500 } 501 502 /* 503 * Update the mail file with any new messages that have 504 * come in since we started reading mail. 505 */ 506 int 507 inc(void *v) 508 { 509 int nmsg, mdot; 510 511 nmsg = incfile(); 512 513 if (nmsg == 0) { 514 puts("No new mail."); 515 } else if (nmsg > 0) { 516 mdot = newfileinfo(msgCount - nmsg); 517 dot = &message[mdot - 1]; 518 } else { 519 puts("\"inc\" command failed..."); 520 } 521 522 return(0); 523 } 524 525 /* 526 * User hit ^C while printing the headers. 527 */ 528 void 529 hdrint(int s) 530 { 531 532 gothdrint = 1; 533 } 534