cmd1.c revision 1.24 1 /* $NetBSD: cmd1.c,v 1.24 2006/09/18 19:46:21 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.24 2006/09/18 19:46:21 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 * Pipe the current message buffer to a command.
366 */
367 int
368 pipecmd(void *v)
369 {
370 char *cmd = v;
371 FILE *obuf;
372 #if __GNUC__
373 /* Avoid longjmp clobbering */
374 (void)&obuf;
375 #endif
376 if (dot == NULL) {
377 warn("pipcmd: no current message");
378 return 1;
379 }
380
381 obuf = stdout;
382 if (setjmp(pipestop))
383 goto close_pipe;
384
385 obuf = Popen(cmd, "w");
386 if (obuf == NULL) {
387 warn("pipecmd: Popen failed: %s", cmd);
388 return 1;
389 } else
390 (void)signal(SIGPIPE, brokpipe);
391
392 (void)sendmessage(dot, obuf, ignoreall, NULL);
393
394 close_pipe:
395 if (obuf != stdout) {
396 /*
397 * Ignore SIGPIPE so it can't cause a duplicate close.
398 */
399 (void)signal(SIGPIPE, SIG_IGN);
400 (void)Pclose(obuf);
401 (void)signal(SIGPIPE, SIG_DFL);
402 }
403 return 0;
404 }
405
406 /*
407 * Print the top so many lines of each desired message.
408 * The number of lines is taken from the variable "toplines"
409 * and defaults to 5.
410 */
411 int
412 top(void *v)
413 {
414 int *msgvec = v;
415 int *ip;
416 struct message *mp;
417 int c, topl, lines, lineb;
418 char *valtop, linebuf[LINESIZE];
419 FILE *ibuf;
420
421 topl = 5;
422 valtop = value("toplines");
423 if (valtop != NULL) {
424 topl = atoi(valtop);
425 if (topl < 0 || topl > 10000)
426 topl = 5;
427 }
428 lineb = 1;
429 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
430 mp = &message[*ip - 1];
431 touch(mp);
432 dot = mp;
433 if (value("quiet") == NULL)
434 (void)printf("Message %d:\n", *ip);
435 ibuf = setinput(mp);
436 c = mp->m_lines;
437 if (!lineb)
438 (void)printf("\n");
439 for (lines = 0; lines < c && lines <= topl; lines++) {
440 if (readline(ibuf, linebuf, LINESIZE) < 0)
441 break;
442 (void)puts(linebuf);
443 lineb = blankline(linebuf);
444 }
445 }
446 return(0);
447 }
448
449 /*
450 * Touch all the given messages so that they will
451 * get mboxed.
452 */
453 int
454 stouch(void *v)
455 {
456 int *msgvec = v;
457 int *ip;
458
459 for (ip = msgvec; *ip != 0; ip++) {
460 dot = &message[*ip-1];
461 dot->m_flag |= MTOUCH;
462 dot->m_flag &= ~MPRESERVE;
463 }
464 return(0);
465 }
466
467 /*
468 * Make sure all passed messages get mboxed.
469 */
470 int
471 mboxit(void *v)
472 {
473 int *msgvec = v;
474 int *ip;
475
476 for (ip = msgvec; *ip != 0; ip++) {
477 dot = &message[*ip-1];
478 dot->m_flag |= MTOUCH|MBOX;
479 dot->m_flag &= ~MPRESERVE;
480 }
481 return(0);
482 }
483
484 /*
485 * List the folders the user currently has.
486 */
487 int
488 /*ARGSUSED*/
489 folders(void *v)
490 {
491 char dirname[PATHSIZE];
492 const char *cmd;
493
494 if (getfold(dirname) < 0) {
495 (void)printf("No value set for \"folder\"\n");
496 return 1;
497 }
498 if ((cmd = value("LISTER")) == NULL)
499 cmd = "ls";
500 (void)run_command(cmd, 0, -1, -1, dirname, NULL);
501 return 0;
502 }
503
504 /*
505 * Update the mail file with any new messages that have
506 * come in since we started reading mail.
507 */
508 int
509 /*ARGSUSED*/
510 inc(void *v)
511 {
512 int nmsg, mdot;
513
514 nmsg = incfile();
515
516 if (nmsg == 0) {
517 (void)printf("No new mail.\n");
518 } else if (nmsg > 0) {
519 mdot = newfileinfo(msgCount - nmsg);
520 dot = &message[mdot - 1];
521 } else {
522 (void)printf("\"inc\" command failed...\n");
523 }
524
525 return 0;
526 }
527