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