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