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