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