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