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