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