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