1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 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 #ifndef lint 33 #if 0 34 static char sccsid[] = "@(#)cmd1.c 8.2 (Berkeley) 4/20/95"; 35 #endif 36 #endif /* not lint */ 37 #include <sys/cdefs.h> 38 __FBSDID("$FreeBSD$"); 39 40 #include "rcv.h" 41 #include "extern.h" 42 43 /* 44 * Mail -- a mail program 45 * 46 * User commands. 47 */ 48 49 /* 50 * Print the current active headings. 51 * Don't change dot if invoker didn't give an argument. 52 */ 53 54 static int screen; 55 56 int 57 headers(void *v) 58 { 59 int *msgvec = v; 60 int n, mesg, flag, size; 61 struct message *mp; 62 63 size = screensize(); 64 n = msgvec[0]; 65 if (n != 0) 66 screen = (n-1)/size; 67 if (screen < 0) 68 screen = 0; 69 mp = &message[screen * size]; 70 if (mp >= &message[msgCount]) 71 mp = &message[msgCount - size]; 72 if (mp < &message[0]) 73 mp = &message[0]; 74 flag = 0; 75 mesg = mp - &message[0]; 76 if (dot != &message[n-1]) 77 dot = mp; 78 for (; mp < &message[msgCount]; mp++) { 79 mesg++; 80 if (mp->m_flag & MDELETED) 81 continue; 82 if (flag++ >= size) 83 break; 84 printhead(mesg); 85 } 86 if (flag == 0) { 87 printf("No more mail.\n"); 88 return (1); 89 } 90 return (0); 91 } 92 93 /* 94 * Scroll to the next/previous screen 95 */ 96 int 97 scroll(void *v) 98 { 99 char *arg = v; 100 int s, size; 101 int cur[1]; 102 103 cur[0] = 0; 104 size = screensize(); 105 s = screen; 106 switch (*arg) { 107 case 0: 108 case '+': 109 s++; 110 if (s * size >= msgCount) { 111 printf("On last screenful of messages\n"); 112 return (0); 113 } 114 screen = s; 115 break; 116 117 case '-': 118 if (--s < 0) { 119 printf("On first screenful of messages\n"); 120 return (0); 121 } 122 screen = s; 123 break; 124 125 default: 126 printf("Unrecognized scrolling command \"%s\"\n", arg); 127 return (1); 128 } 129 return (headers(cur)); 130 } 131 132 /* 133 * Compute screen size. 134 */ 135 int 136 screensize(void) 137 { 138 int s; 139 char *cp; 140 141 if ((cp = value("screen")) != NULL && (s = atoi(cp)) > 0) 142 return (s); 143 return (screenheight - 4); 144 } 145 146 /* 147 * Print out the headlines for each message 148 * in the passed message list. 149 */ 150 int 151 from(void *v) 152 { 153 int *msgvec = v; 154 int *ip; 155 156 for (ip = msgvec; *ip != 0; ip++) 157 printhead(*ip); 158 if (--ip >= msgvec) 159 dot = &message[*ip - 1]; 160 return (0); 161 } 162 163 /* 164 * Print out the header of a specific message. 165 * This is a slight improvement to the standard one. 166 */ 167 void 168 printhead(int mesg) 169 { 170 struct message *mp; 171 char headline[LINESIZE], wcount[LINESIZE], *subjline, dispc, curind; 172 char pbuf[BUFSIZ]; 173 struct headline hl; 174 int subjlen; 175 char *name; 176 177 mp = &message[mesg-1]; 178 (void)readline(setinput(mp), headline, LINESIZE); 179 if ((subjline = hfield("subject", mp)) == NULL) 180 subjline = hfield("subj", mp); 181 /* 182 * Bletch! 183 */ 184 curind = dot == mp ? '>' : ' '; 185 dispc = ' '; 186 if (mp->m_flag & MSAVED) 187 dispc = '*'; 188 if (mp->m_flag & MPRESERVE) 189 dispc = 'P'; 190 if ((mp->m_flag & (MREAD|MNEW)) == MNEW) 191 dispc = 'N'; 192 if ((mp->m_flag & (MREAD|MNEW)) == 0) 193 dispc = 'U'; 194 if (mp->m_flag & MBOX) 195 dispc = 'M'; 196 parse(headline, &hl, pbuf); 197 sprintf(wcount, "%3ld/%-5ld", mp->m_lines, mp->m_size); 198 subjlen = screenwidth - 50 - strlen(wcount); 199 name = value("show-rcpt") != NULL ? 200 skin(hfield("to", mp)) : nameof(mp, 0); 201 if (subjline == NULL || subjlen < 0) /* pretty pathetic */ 202 printf("%c%c%3d %-20.20s %16.16s %s\n", 203 curind, dispc, mesg, name, hl.l_date, wcount); 204 else 205 printf("%c%c%3d %-20.20s %16.16s %s \"%.*s\"\n", 206 curind, dispc, mesg, name, hl.l_date, wcount, 207 subjlen, subjline); 208 } 209 210 /* 211 * Print out the value of dot. 212 */ 213 int 214 pdot(void *arg __unused) 215 { 216 printf("%td\n", dot - &message[0] + 1); 217 return (0); 218 } 219 220 /* 221 * Print out all the possible commands. 222 */ 223 int 224 pcmdlist(void *arg __unused) 225 { 226 extern const struct cmd cmdtab[]; 227 const struct cmd *cp; 228 int cc; 229 230 printf("Commands are:\n"); 231 for (cc = 0, cp = cmdtab; cp->c_name != NULL; cp++) { 232 cc += strlen(cp->c_name) + 2; 233 if (cc > 72) { 234 printf("\n"); 235 cc = strlen(cp->c_name) + 2; 236 } 237 if ((cp+1)->c_name != NULL) 238 printf("%s, ", cp->c_name); 239 else 240 printf("%s\n", cp->c_name); 241 } 242 return (0); 243 } 244 245 /* 246 * Paginate messages, honor ignored fields. 247 */ 248 int 249 more(void *v) 250 { 251 int *msgvec = v; 252 253 return (type1(msgvec, 1, 1)); 254 } 255 256 /* 257 * Paginate messages, even printing ignored fields. 258 */ 259 int 260 More(void *v) 261 { 262 int *msgvec = v; 263 264 return (type1(msgvec, 0, 1)); 265 } 266 267 /* 268 * Type out messages, honor ignored fields. 269 */ 270 int 271 type(void *v) 272 { 273 int *msgvec = v; 274 275 return (type1(msgvec, 1, 0)); 276 } 277 278 /* 279 * Type out messages, even printing ignored fields. 280 */ 281 int 282 Type(void *v) 283 { 284 int *msgvec = v; 285 286 return (type1(msgvec, 0, 0)); 287 } 288 289 /* 290 * Type out the messages requested. 291 */ 292 static jmp_buf pipestop; 293 int 294 type1(int *msgvec, int doign, int page) 295 { 296 int nlines, *ip; 297 struct message *mp; 298 char *cp; 299 FILE *obuf; 300 301 obuf = stdout; 302 if (setjmp(pipestop)) 303 goto close_pipe; 304 if (value("interactive") != NULL && 305 (page || (cp = value("crt")) != NULL)) { 306 nlines = 0; 307 if (!page) { 308 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) 309 nlines += message[*ip - 1].m_lines; 310 } 311 if (page || nlines > (*cp ? atoi(cp) : realscreenheight)) { 312 cp = value("PAGER"); 313 if (cp == NULL || *cp == '\0') 314 cp = _PATH_LESS; 315 obuf = Popen(cp, "w"); 316 if (obuf == NULL) { 317 warnx("%s", cp); 318 obuf = stdout; 319 } else 320 (void)signal(SIGPIPE, brokpipe); 321 } 322 } 323 324 /* 325 * Send messages to the output. 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 fprintf(obuf, "Message %d:\n", *ip); 334 (void)sendmessage(mp, obuf, doign ? ignore : 0, NULL); 335 } 336 337 close_pipe: 338 if (obuf != stdout) { 339 /* 340 * Ignore SIGPIPE so it can't cause a duplicate close. 341 */ 342 (void)signal(SIGPIPE, SIG_IGN); 343 (void)Pclose(obuf); 344 (void)signal(SIGPIPE, SIG_DFL); 345 } 346 return (0); 347 } 348 349 /* 350 * Respond to a broken pipe signal -- 351 * probably caused by quitting more. 352 */ 353 /*ARGSUSED*/ 354 void 355 brokpipe(int signo __unused) 356 { 357 longjmp(pipestop, 1); 358 } 359 360 /* 361 * Print the top so many lines of each desired message. 362 * The number of lines is taken from the variable "toplines" 363 * and defaults to 5. 364 */ 365 int 366 top(void *v) 367 { 368 int *msgvec = v; 369 int *ip; 370 struct message *mp; 371 int c, topl, lines, lineb; 372 char *valtop, linebuf[LINESIZE]; 373 FILE *ibuf; 374 375 topl = 5; 376 valtop = value("toplines"); 377 if (valtop != NULL) { 378 topl = atoi(valtop); 379 if (topl < 0 || topl > 10000) 380 topl = 5; 381 } 382 lineb = 1; 383 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { 384 mp = &message[*ip - 1]; 385 touch(mp); 386 dot = mp; 387 if (value("quiet") == NULL) 388 printf("Message %d:\n", *ip); 389 ibuf = setinput(mp); 390 c = mp->m_lines; 391 if (!lineb) 392 printf("\n"); 393 for (lines = 0; lines < c && lines <= topl; lines++) { 394 if (readline(ibuf, linebuf, sizeof(linebuf)) < 0) 395 break; 396 puts(linebuf); 397 lineb = strspn(linebuf, " \t") == strlen(linebuf); 398 } 399 } 400 return (0); 401 } 402 403 /* 404 * Touch all the given messages so that they will 405 * get mboxed. 406 */ 407 int 408 stouch(void *v) 409 { 410 int *msgvec = v; 411 int *ip; 412 413 for (ip = msgvec; *ip != 0; ip++) { 414 dot = &message[*ip-1]; 415 dot->m_flag |= MTOUCH; 416 dot->m_flag &= ~MPRESERVE; 417 } 418 return (0); 419 } 420 421 /* 422 * Make sure all passed messages get mboxed. 423 */ 424 int 425 mboxit(void *v) 426 { 427 int *msgvec = v; 428 int *ip; 429 430 for (ip = msgvec; *ip != 0; ip++) { 431 dot = &message[*ip-1]; 432 dot->m_flag |= MTOUCH|MBOX; 433 dot->m_flag &= ~MPRESERVE; 434 } 435 return (0); 436 } 437 438 /* 439 * List the folders the user currently has. 440 */ 441 int 442 folders(void *arg __unused) 443 { 444 char dirname[PATHSIZE]; 445 char *cmd; 446 447 if (getfold(dirname, sizeof(dirname)) < 0) { 448 printf("No value set for \"folder\"\n"); 449 return (1); 450 } 451 if ((cmd = value("LISTER")) == NULL) 452 cmd = "ls"; 453 (void)run_command(cmd, 0, -1, -1, dirname, NULL); 454 return (0); 455 } 456 457 /* 458 * Update the mail file with any new messages that have 459 * come in since we started reading mail. 460 */ 461 int 462 inc(void *v __unused) 463 { 464 int nmsg, mdot; 465 466 nmsg = incfile(); 467 468 if (nmsg == 0) 469 printf("No new mail.\n"); 470 else if (nmsg > 0) { 471 mdot = newfileinfo(msgCount - nmsg); 472 dot = &message[mdot - 1]; 473 } else 474 printf("\"inc\" command failed...\n"); 475 476 return (0); 477 } 478