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