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 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * @(#)cmd1.c 8.2 (Berkeley) 4/20/95 34 * $FreeBSD: src/usr.bin/mail/cmd1.c,v 1.3.6.3 2003/01/06 05:46:03 mikeh Exp $ 35 * $DragonFly: src/usr.bin/mail/cmd1.c,v 1.4 2004/09/08 03:01:11 joerg Exp $ 36 */ 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 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 return (type1(msgvec, 1, 1)); 248 } 249 250 /* 251 * Paginate messages, even printing ignored fields. 252 */ 253 int 254 More(int *msgvec) 255 { 256 return (type1(msgvec, 0, 1)); 257 } 258 259 /* 260 * Type out messages, honor ignored fields. 261 */ 262 int 263 type(int *msgvec) 264 { 265 return (type1(msgvec, 1, 0)); 266 } 267 268 /* 269 * Type out messages, even printing ignored fields. 270 */ 271 int 272 Type(int *msgvec) 273 { 274 return (type1(msgvec, 0, 0)); 275 } 276 277 /* 278 * Type out the messages requested. 279 */ 280 jmp_buf pipestop; 281 int 282 type1(int *msgvec, int doign, int page) 283 { 284 int nlines, *ip; 285 struct message *mp; 286 char *cp; 287 FILE *obuf; 288 289 obuf = stdout; 290 if (setjmp(pipestop)) 291 goto close_pipe; 292 if (value("interactive") != NULL && 293 (page || (cp = value("crt")) != NULL)) { 294 nlines = 0; 295 if (!page) { 296 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) 297 nlines += message[*ip - 1].m_lines; 298 } 299 if (page || nlines > (*cp ? atoi(cp) : realscreenheight)) { 300 cp = value("PAGER"); 301 if (cp == NULL || *cp == '\0') 302 cp = _PATH_MORE; 303 obuf = Popen(cp, "w"); 304 if (obuf == NULL) { 305 warnx("%s", cp); 306 obuf = stdout; 307 } else 308 signal(SIGPIPE, brokpipe); 309 } 310 } 311 312 /* 313 * Send messages to the output. 314 * 315 */ 316 for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) { 317 mp = &message[*ip - 1]; 318 touch(mp); 319 dot = mp; 320 if (value("quiet") == NULL) 321 fprintf(obuf, "Message %d:\n", *ip); 322 sendmessage(mp, obuf, doign ? ignore : 0, NULL); 323 } 324 325 close_pipe: 326 if (obuf != stdout) { 327 /* 328 * Ignore SIGPIPE so it can't cause a duplicate close. 329 */ 330 signal(SIGPIPE, SIG_IGN); 331 Pclose(obuf); 332 signal(SIGPIPE, SIG_DFL); 333 } 334 return (0); 335 } 336 337 /* 338 * Respond to a broken pipe signal -- 339 * probably caused by quitting more. 340 */ 341 /*ARGSUSED*/ 342 void 343 brokpipe(int signo) 344 { 345 longjmp(pipestop, 1); 346 } 347 348 /* 349 * Print the top so many lines of each desired message. 350 * The number of lines is taken from the variable "toplines" 351 * and defaults to 5. 352 */ 353 int 354 top(int *msgvec) 355 { 356 int *ip; 357 struct message *mp; 358 int c, topl, lines, lineb; 359 char *valtop, linebuf[LINESIZE]; 360 FILE *ibuf; 361 362 topl = 5; 363 valtop = value("toplines"); 364 if (valtop != NULL) { 365 topl = atoi(valtop); 366 if (topl < 0 || topl > 10000) 367 topl = 5; 368 } 369 lineb = 1; 370 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { 371 mp = &message[*ip - 1]; 372 touch(mp); 373 dot = mp; 374 if (value("quiet") == NULL) 375 printf("Message %d:\n", *ip); 376 ibuf = setinput(mp); 377 c = mp->m_lines; 378 if (!lineb) 379 printf("\n"); 380 for (lines = 0; lines < c && lines <= topl; lines++) { 381 if (readline(ibuf, linebuf, sizeof(linebuf)) < 0) 382 break; 383 puts(linebuf); 384 lineb = strspn(linebuf, " \t") == strlen(linebuf); 385 } 386 } 387 return (0); 388 } 389 390 /* 391 * Touch all the given messages so that they will 392 * get mboxed. 393 */ 394 int 395 stouch(int *msgvec) 396 { 397 int *ip; 398 399 for (ip = msgvec; *ip != 0; ip++) { 400 dot = &message[*ip-1]; 401 dot->m_flag |= MTOUCH; 402 dot->m_flag &= ~MPRESERVE; 403 } 404 return (0); 405 } 406 407 /* 408 * Make sure all passed messages get mboxed. 409 */ 410 int 411 mboxit(int *msgvec) 412 { 413 int *ip; 414 415 for (ip = msgvec; *ip != 0; ip++) { 416 dot = &message[*ip-1]; 417 dot->m_flag |= MTOUCH|MBOX; 418 dot->m_flag &= ~MPRESERVE; 419 } 420 return (0); 421 } 422 423 /* 424 * List the folders the user currently has. 425 */ 426 int 427 folders(void) 428 { 429 char dirname[PATHSIZE]; 430 char *cmd; 431 432 if (getfold(dirname, sizeof(dirname)) < 0) { 433 printf("No value set for \"folder\"\n"); 434 return (1); 435 } 436 if ((cmd = value("LISTER")) == NULL) 437 cmd = "ls"; 438 run_command(cmd, 0, -1, -1, dirname, NULL, NULL); 439 return (0); 440 } 441 442 /* 443 * Update the mail file with any new messages that have 444 * come in since we started reading mail. 445 */ 446 int 447 inc(void *v) 448 { 449 int nmsg, mdot; 450 451 nmsg = incfile(); 452 453 if (nmsg == 0) 454 printf("No new mail.\n"); 455 else if (nmsg > 0) { 456 mdot = newfileinfo(msgCount - nmsg); 457 dot = &message[mdot - 1]; 458 } else 459 printf("\"inc\" command failed...\n"); 460 461 return (0); 462 } 463