1 /* $OpenBSD: cmd1.c,v 1.27 2008/07/15 19:13:25 martynas 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.27 2008/07/15 19:13:25 martynas 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 && size > 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 = 0; 126 if (size > 0) 127 maxscreen = (msgCount - 1) / size; 128 switch (*arg) { 129 case 0: 130 case '+': 131 if (screen >= maxscreen) { 132 puts("On last screenful of messages"); 133 return(0); 134 } 135 screen++; 136 break; 137 138 case '-': 139 if (screen <= 0) { 140 puts("On first screenful of messages"); 141 return(0); 142 } 143 screen--; 144 break; 145 146 default: 147 printf("Unrecognized scrolling command \"%s\"\n", arg); 148 return(1); 149 } 150 return(headers(cur)); 151 } 152 153 /* 154 * Compute screen size. 155 */ 156 int 157 screensize(void) 158 { 159 int s; 160 char *cp; 161 162 if ((cp = value("screen")) != NULL && (s = atoi(cp)) > 0) 163 return(s); 164 return(screenheight - 4); 165 } 166 167 /* 168 * Print out the headlines for each message 169 * in the passed message list. 170 */ 171 int 172 from(void *v) 173 { 174 int *msgvec = v; 175 int *ip; 176 177 for (ip = msgvec; *ip != NULL; ip++) 178 printhead(*ip); 179 if (--ip >= msgvec) 180 dot = &message[*ip - 1]; 181 return(0); 182 } 183 184 /* 185 * Print out the header of a specific message. 186 * This is a slight improvement to the standard one. 187 */ 188 void 189 printhead(int mesg) 190 { 191 struct message *mp; 192 char headline[LINESIZE], *subjline, dispc, curind; 193 char visname[LINESIZE], vissub[LINESIZE]; 194 char pbuf[LINESIZE]; 195 char fmtline[LINESIZE]; 196 const char *fmt; 197 struct headline hl; 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)) == NULL) 207 subjline = ""; 208 /* 209 * Bletch! 210 */ 211 curind = dot == mp ? '>' : ' '; 212 dispc = ' '; 213 if (mp->m_flag & MSAVED) 214 dispc = '*'; 215 if (mp->m_flag & MPRESERVE) 216 dispc = 'P'; 217 if ((mp->m_flag & (MREAD|MNEW)) == MNEW) 218 dispc = 'N'; 219 if ((mp->m_flag & (MREAD|MNEW)) == 0) 220 dispc = 'U'; 221 if (mp->m_flag & MBOX) 222 dispc = 'M'; 223 parse(headline, &hl, pbuf); 224 from = nameof(mp, 0); 225 to = skin(hfield("to", mp)); 226 np = extract(from, GTO); 227 np = delname(np, myname); 228 if (altnames) 229 for (ap = altnames; *ap; ap++) 230 np = delname(np, *ap); 231 if (np) 232 /* not from me */ 233 name = value("show-rcpt") != NULL && to ? to : from; 234 else 235 /* from me - show TO */ 236 name = value("showto") != NULL && to ? to : from; 237 strnvis(visname, name, sizeof(visname), VIS_SAFE|VIS_NOSLASH); 238 if (name == to) 239 fmt = "%c%c%3d TO %-14.14s %16.16s %4d/%-5d %s"; 240 else 241 fmt = "%c%c%3d %-17.17s %16.16s %4d/%-5d %s"; 242 strnvis(vissub, subjline, sizeof(vissub), VIS_SAFE|VIS_NOSLASH); 243 /* hl.l_date was sanity-checked when read in. */ 244 snprintf(fmtline, sizeof(fmtline), fmt, curind, dispc, mesg, visname, 245 hl.l_date, mp->m_lines, mp->m_size, vissub); 246 printf("%.*s\n", screenwidth, fmtline); 247 } 248 249 /* 250 * Print out the value of dot. 251 */ 252 int 253 pdot(void *v) 254 { 255 printf("%d\n", (int)(dot - &message[0] + 1)); 256 return(0); 257 } 258 259 /* 260 * Print out all the possible commands. 261 */ 262 int 263 pcmdlist(void *v) 264 { 265 extern const struct cmd cmdtab[]; 266 const struct cmd *cp; 267 int cc; 268 269 puts("Commands are:"); 270 for (cc = 0, cp = cmdtab; cp->c_name != NULL; cp++) { 271 cc += strlen(cp->c_name) + 2; 272 if (cc > 72) { 273 putchar('\n'); 274 cc = strlen(cp->c_name) + 2; 275 } 276 if ((cp+1)->c_name != NULL) 277 printf("%s, ", cp->c_name); 278 else 279 puts(cp->c_name); 280 } 281 return(0); 282 } 283 284 /* 285 * Pipe message to command 286 */ 287 int 288 pipeit(void *ml, void *sl) 289 { 290 int *msgvec = ml; 291 char *cmd = sl; 292 293 return(type1(msgvec, cmd, 0, 0)); 294 } 295 296 /* 297 * Paginate messages, honor ignored fields. 298 */ 299 int 300 more(void *v) 301 { 302 int *msgvec = v; 303 return(type1(msgvec, NULL, 1, 1)); 304 } 305 306 /* 307 * Paginate messages, even printing ignored fields. 308 */ 309 int 310 More(void *v) 311 { 312 int *msgvec = v; 313 314 return(type1(msgvec, NULL, 0, 1)); 315 } 316 317 /* 318 * Type out messages, honor ignored fields. 319 */ 320 int 321 type(void *v) 322 { 323 int *msgvec = v; 324 325 return(type1(msgvec, NULL, 1, 0)); 326 } 327 328 /* 329 * Type out messages, even printing ignored fields. 330 */ 331 int 332 Type(void *v) 333 { 334 int *msgvec = v; 335 336 return(type1(msgvec, NULL, 0, 0)); 337 } 338 339 /* 340 * Type out the messages requested. 341 */ 342 int 343 type1(int *msgvec, char *cmd, int doign, int page) 344 { 345 int nlines, *ip, restoreterm; 346 struct message *mp; 347 struct termios tbuf; 348 char *cp; 349 FILE *obuf; 350 351 obuf = stdout; 352 restoreterm = 0; 353 354 /* 355 * start a pipe if needed. 356 */ 357 if (cmd) { 358 restoreterm = (tcgetattr(fileno(stdin), &tbuf) == 0); 359 obuf = Popen(cmd, "w"); 360 if (obuf == NULL) { 361 warn("%s", cmd); 362 obuf = stdout; 363 } 364 } else if (value("interactive") != NULL && 365 (page || (cp = value("crt")) != NULL)) { 366 nlines = 0; 367 if (!page) { 368 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) 369 nlines += message[*ip - 1].m_lines; 370 } 371 if (page || nlines > (*cp ? atoi(cp) : realscreenheight)) { 372 restoreterm = (tcgetattr(fileno(stdin), &tbuf) == 0); 373 cp = value("PAGER"); 374 obuf = Popen(cp, "w"); 375 if (obuf == NULL) { 376 warn("%s", cp); 377 obuf = stdout; 378 } 379 } 380 } 381 382 /* 383 * Send messages to the output. 384 */ 385 for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) { 386 mp = &message[*ip - 1]; 387 touch(mp); 388 dot = mp; 389 if (cmd == NULL && value("quiet") == NULL) 390 fprintf(obuf, "Message %d:\n", *ip); 391 if (sendmessage(mp, obuf, doign ? ignore : 0, NULL) == -1) 392 break; 393 } 394 395 if (obuf != stdout) { 396 (void)Pclose(obuf); 397 if (restoreterm) 398 (void)tcsetattr(fileno(stdin), TCSADRAIN, &tbuf); 399 } 400 return(0); 401 } 402 403 /* 404 * Print the top so many lines of each desired message. 405 * The number of lines is taken from the variable "toplines" 406 * and defaults to 5. 407 */ 408 int 409 top(void * v) 410 { 411 int *msgvec = v; 412 int *ip; 413 struct message *mp; 414 int c, topl, lines, lineb; 415 char *valtop, linebuf[LINESIZE]; 416 FILE *ibuf; 417 418 topl = 5; 419 valtop = value("toplines"); 420 if (valtop != NULL) { 421 topl = atoi(valtop); 422 if (topl < 0 || topl > 10000) 423 topl = 5; 424 } 425 lineb = 1; 426 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { 427 mp = &message[*ip - 1]; 428 touch(mp); 429 dot = mp; 430 if (value("quiet") == NULL) 431 printf("Message %d:\n", *ip); 432 ibuf = setinput(mp); 433 c = mp->m_lines; 434 if (!lineb) 435 putchar('\n'); 436 for (lines = 0; lines < c && lines <= topl; lines++) { 437 if (readline(ibuf, linebuf, sizeof(linebuf), NULL) < 0) 438 break; 439 puts(linebuf); 440 lineb = blankline(linebuf); 441 } 442 } 443 return(0); 444 } 445 446 /* 447 * Touch all the given messages so that they will 448 * get mboxed. 449 */ 450 int 451 stouch(void *v) 452 { 453 int *msgvec = v; 454 int *ip; 455 456 for (ip = msgvec; *ip != 0; ip++) { 457 dot = &message[*ip-1]; 458 dot->m_flag |= MTOUCH; 459 dot->m_flag &= ~MPRESERVE; 460 } 461 return(0); 462 } 463 464 /* 465 * Make sure all passed messages get mboxed. 466 */ 467 int 468 mboxit(void *v) 469 { 470 int *msgvec = v; 471 int *ip; 472 473 for (ip = msgvec; *ip != 0; ip++) { 474 dot = &message[*ip-1]; 475 dot->m_flag |= MTOUCH|MBOX; 476 dot->m_flag &= ~MPRESERVE; 477 } 478 return(0); 479 } 480 481 /* 482 * List the folders the user currently has. 483 */ 484 int 485 folders(void *v) 486 { 487 char *files = (char *)v; 488 char dirname[PATHSIZE]; 489 char cmd[BUFSIZ]; 490 491 if (getfold(dirname, sizeof(dirname)) < 0) 492 strlcpy(dirname, "$HOME", sizeof(dirname)); 493 494 snprintf(cmd, sizeof(cmd), "cd %s; %s %s", dirname, value("LISTER"), 495 files && *files ? files : ""); 496 497 (void)run_command(value("SHELL"), 0, -1, -1, "-c", cmd, NULL); 498 return(0); 499 } 500 501 /* 502 * Update the mail file with any new messages that have 503 * come in since we started reading mail. 504 */ 505 int 506 inc(void *v) 507 { 508 int nmsg, mdot; 509 510 nmsg = incfile(); 511 512 if (nmsg == 0) { 513 puts("No new mail."); 514 } else if (nmsg > 0) { 515 mdot = newfileinfo(msgCount - nmsg); 516 dot = &message[mdot - 1]; 517 } else { 518 puts("\"inc\" command failed..."); 519 } 520 521 return(0); 522 } 523 524 /* 525 * User hit ^C while printing the headers. 526 */ 527 void 528 hdrint(int s) 529 { 530 531 gothdrint = 1; 532 } 533