cmd1.c revision 1.26
1/* $OpenBSD: cmd1.c,v 1.26 2007/04/03 18:01:49 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 35static const char sccsid[] = "@(#)cmd1.c 8.2 (Berkeley) 4/20/95"; 36#else 37static const char rcsid[] = "$OpenBSD: cmd1.c,v 1.26 2007/04/03 18:01:49 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 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 && 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 */ 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 = 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 */ 156int 157screensize(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 */ 171int 172from(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 */ 188void 189printhead(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 */ 252int 253pdot(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 */ 262int 263pcmdlist(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 */ 287int 288pipeit(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 */ 299int 300more(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 */ 309int 310More(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 */ 320int 321type(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 */ 331int 332Type(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 */ 342int 343type1(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 obuf = Popen(value("PAGER"), "w"); 374 if (obuf == NULL) { 375 warn("%s", cp); 376 obuf = stdout; 377 } 378 } 379 } 380 381 /* 382 * Send messages to the output. 383 */ 384 for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) { 385 mp = &message[*ip - 1]; 386 touch(mp); 387 dot = mp; 388 if (cmd == NULL && value("quiet") == NULL) 389 fprintf(obuf, "Message %d:\n", *ip); 390 if (sendmessage(mp, obuf, doign ? ignore : 0, NULL) == -1) 391 break; 392 } 393 394 if (obuf != stdout) { 395 (void)Pclose(obuf); 396 if (restoreterm) 397 (void)tcsetattr(fileno(stdin), TCSADRAIN, &tbuf); 398 } 399 return(0); 400} 401 402/* 403 * Print the top so many lines of each desired message. 404 * The number of lines is taken from the variable "toplines" 405 * and defaults to 5. 406 */ 407int 408top(void * v) 409{ 410 int *msgvec = v; 411 int *ip; 412 struct message *mp; 413 int c, topl, lines, lineb; 414 char *valtop, linebuf[LINESIZE]; 415 FILE *ibuf; 416 417 topl = 5; 418 valtop = value("toplines"); 419 if (valtop != NULL) { 420 topl = atoi(valtop); 421 if (topl < 0 || topl > 10000) 422 topl = 5; 423 } 424 lineb = 1; 425 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { 426 mp = &message[*ip - 1]; 427 touch(mp); 428 dot = mp; 429 if (value("quiet") == NULL) 430 printf("Message %d:\n", *ip); 431 ibuf = setinput(mp); 432 c = mp->m_lines; 433 if (!lineb) 434 putchar('\n'); 435 for (lines = 0; lines < c && lines <= topl; lines++) { 436 if (readline(ibuf, linebuf, sizeof(linebuf), NULL) < 0) 437 break; 438 puts(linebuf); 439 lineb = blankline(linebuf); 440 } 441 } 442 return(0); 443} 444 445/* 446 * Touch all the given messages so that they will 447 * get mboxed. 448 */ 449int 450stouch(void *v) 451{ 452 int *msgvec = v; 453 int *ip; 454 455 for (ip = msgvec; *ip != 0; ip++) { 456 dot = &message[*ip-1]; 457 dot->m_flag |= MTOUCH; 458 dot->m_flag &= ~MPRESERVE; 459 } 460 return(0); 461} 462 463/* 464 * Make sure all passed messages get mboxed. 465 */ 466int 467mboxit(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|MBOX; 475 dot->m_flag &= ~MPRESERVE; 476 } 477 return(0); 478} 479 480/* 481 * List the folders the user currently has. 482 */ 483int 484folders(void *v) 485{ 486 char *files = (char *)v; 487 char dirname[PATHSIZE]; 488 char cmd[BUFSIZ]; 489 490 if (getfold(dirname, sizeof(dirname)) < 0) 491 strlcpy(dirname, "$HOME", sizeof(dirname)); 492 493 snprintf(cmd, sizeof(cmd), "cd %s; %s %s", dirname, value("LISTER"), 494 files && *files ? files : ""); 495 496 (void)run_command(value("SHELL"), 0, -1, -1, "-c", cmd, NULL); 497 return(0); 498} 499 500/* 501 * Update the mail file with any new messages that have 502 * come in since we started reading mail. 503 */ 504int 505inc(void *v) 506{ 507 int nmsg, mdot; 508 509 nmsg = incfile(); 510 511 if (nmsg == 0) { 512 puts("No new mail."); 513 } else if (nmsg > 0) { 514 mdot = newfileinfo(msgCount - nmsg); 515 dot = &message[mdot - 1]; 516 } else { 517 puts("\"inc\" command failed..."); 518 } 519 520 return(0); 521} 522 523/* 524 * User hit ^C while printing the headers. 525 */ 526void 527hdrint(int s) 528{ 529 530 gothdrint = 1; 531} 532