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