1 /* $OpenBSD: cmd1.c,v 1.29 2011/04/06 11:36:26 miod Exp $ */
2 /* $NetBSD: cmd1.c,v 1.9 1997/07/09 05:29:48 mikel Exp $ */
3
4 /*-
5 * Copyright (c) 1980, 1993
6 * The Regents of the University of California. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33 #include "rcv.h"
34 #include "extern.h"
35
36 /*
37 * Mail -- a mail program
38 *
39 * User commands.
40 */
41
42 /*
43 * Print the current active headings.
44 * Don't change dot if invoker didn't give an argument.
45 */
46
47 static int screen;
48 static volatile sig_atomic_t gothdrint;
49
50 int
headers(void * v)51 headers(void *v)
52 {
53 int *msgvec = v;
54 int n, mesg, flag, size;
55 struct message *mp;
56 struct sigaction act, oact;
57 sigset_t oset;
58
59 size = screensize();
60 n = msgvec[0];
61 if (n != 0 && size > 0)
62 screen = (n-1)/size;
63 if (screen < 0)
64 screen = 0;
65 mp = &message[screen * size];
66 if (mp >= &message[msgCount])
67 mp = &message[msgCount - size];
68 if (mp < &message[0])
69 mp = &message[0];
70 flag = 0;
71 mesg = mp - &message[0];
72 if (dot != &message[n-1])
73 dot = mp;
74 sigemptyset(&act.sa_mask);
75 act.sa_flags = SA_RESTART;
76 act.sa_handler = hdrint;
77 if (sigaction(SIGINT, NULL, &oact) == 0 &&
78 oact.sa_handler != SIG_IGN) {
79 (void)sigaction(SIGINT, &act, &oact);
80 (void)sigprocmask(SIG_UNBLOCK, &intset, &oset);
81 }
82 for (gothdrint = 0; !gothdrint && mp < &message[msgCount]; mp++) {
83 mesg++;
84 if (mp->m_flag & MDELETED)
85 continue;
86 if (flag++ >= size)
87 break;
88 printhead(mesg);
89 }
90 if (gothdrint) {
91 fflush(stdout);
92 fputs("\nInterrupt\n", stderr);
93 }
94 if (oact.sa_handler != SIG_IGN) {
95 (void)sigprocmask(SIG_SETMASK, &oset, NULL);
96 (void)sigaction(SIGINT, &oact, NULL);
97 }
98 if (flag == 0) {
99 puts("No more mail.");
100 return(1);
101 }
102 return(0);
103 }
104
105 /*
106 * Scroll to the next/previous screen
107 */
108 int
scroll(void * v)109 scroll(void *v)
110 {
111 char *arg = v;
112 int size, maxscreen;
113 int cur[1];
114
115 cur[0] = 0;
116 size = screensize();
117 maxscreen = 0;
118 if (size > 0)
119 maxscreen = (msgCount - 1) / size;
120 switch (*arg) {
121 case 0:
122 case '+':
123 if (screen >= maxscreen) {
124 puts("On last screenful of messages");
125 return(0);
126 }
127 screen++;
128 break;
129
130 case '-':
131 if (screen <= 0) {
132 puts("On first screenful of messages");
133 return(0);
134 }
135 screen--;
136 break;
137
138 default:
139 printf("Unrecognized scrolling command \"%s\"\n", arg);
140 return(1);
141 }
142 return(headers(cur));
143 }
144
145 /*
146 * Compute screen size.
147 */
148 int
screensize(void)149 screensize(void)
150 {
151 int s;
152 char *cp;
153
154 if ((cp = value("screen")) != NULL && (s = atoi(cp)) > 0)
155 return(s);
156 return(screenheight - 4);
157 }
158
159 /*
160 * Print out the headlines for each message
161 * in the passed message list.
162 */
163 int
from(void * v)164 from(void *v)
165 {
166 int *msgvec = v;
167 int *ip;
168
169 for (ip = msgvec; *ip != 0; ip++)
170 printhead(*ip);
171 if (--ip >= msgvec)
172 dot = &message[*ip - 1];
173 return(0);
174 }
175
176 /*
177 * Print out the header of a specific message.
178 * This is a slight improvement to the standard one.
179 */
180 void
printhead(int mesg)181 printhead(int mesg)
182 {
183 struct message *mp;
184 char headline[LINESIZE], *subjline, dispc, curind;
185 char visname[LINESIZE], vissub[LINESIZE];
186 char pbuf[LINESIZE];
187 char fmtline[LINESIZE];
188 const char *fmt;
189 struct headline hl;
190 char *name;
191 char *to, *from;
192 struct name *np;
193 char **ap;
194
195 mp = &message[mesg-1];
196 (void)readline(setinput(mp), headline, LINESIZE, NULL);
197 if ((subjline = hfield("subject", mp)) == NULL &&
198 (subjline = hfield("subj", mp)) == NULL)
199 subjline = "";
200 /*
201 * Bletch!
202 */
203 curind = dot == mp ? '>' : ' ';
204 dispc = ' ';
205 if (mp->m_flag & MSAVED)
206 dispc = '*';
207 if (mp->m_flag & MPRESERVE)
208 dispc = 'P';
209 if ((mp->m_flag & (MREAD|MNEW)) == MNEW)
210 dispc = 'N';
211 if ((mp->m_flag & (MREAD|MNEW)) == 0)
212 dispc = 'U';
213 if (mp->m_flag & MBOX)
214 dispc = 'M';
215 parse(headline, &hl, pbuf);
216 from = nameof(mp, 0);
217 to = skin(hfield("to", mp));
218 np = extract(from, GTO);
219 np = delname(np, myname);
220 if (altnames)
221 for (ap = altnames; *ap; ap++)
222 np = delname(np, *ap);
223 if (np)
224 /* not from me */
225 name = value("show-rcpt") != NULL && to ? to : from;
226 else
227 /* from me - show TO */
228 name = value("showto") != NULL && to ? to : from;
229 strnvis(visname, name, sizeof(visname), VIS_SAFE|VIS_NOSLASH);
230 if (name == to)
231 fmt = "%c%c%3d TO %-14.14s %16.16s %4d/%-5d %s";
232 else
233 fmt = "%c%c%3d %-17.17s %16.16s %4d/%-5d %s";
234 strnvis(vissub, subjline, sizeof(vissub), VIS_SAFE|VIS_NOSLASH);
235 /* hl.l_date was sanity-checked when read in. */
236 snprintf(fmtline, sizeof(fmtline), fmt, curind, dispc, mesg, visname,
237 hl.l_date, mp->m_lines, mp->m_size, vissub);
238 printf("%.*s\n", screenwidth, fmtline);
239 }
240
241 /*
242 * Print out the value of dot.
243 */
244 int
pdot(void * v)245 pdot(void *v)
246 {
247 printf("%d\n", (int)(dot - &message[0] + 1));
248 return(0);
249 }
250
251 /*
252 * Print out all the possible commands.
253 */
254 int
pcmdlist(void * v)255 pcmdlist(void *v)
256 {
257 extern const struct cmd cmdtab[];
258 const struct cmd *cp;
259 int cc;
260
261 puts("Commands are:");
262 for (cc = 0, cp = cmdtab; cp->c_name != NULL; cp++) {
263 cc += strlen(cp->c_name) + 2;
264 if (cc > 72) {
265 putchar('\n');
266 cc = strlen(cp->c_name) + 2;
267 }
268 if ((cp+1)->c_name != NULL)
269 printf("%s, ", cp->c_name);
270 else
271 puts(cp->c_name);
272 }
273 return(0);
274 }
275
276 /*
277 * Pipe message to command
278 */
279 int
pipeit(void * ml,void * sl)280 pipeit(void *ml, void *sl)
281 {
282 int *msgvec = ml;
283 char *cmd = sl;
284
285 return(type1(msgvec, cmd, 0, 0));
286 }
287
288 /*
289 * Paginate messages, honor ignored fields.
290 */
291 int
more(void * v)292 more(void *v)
293 {
294 int *msgvec = v;
295 return(type1(msgvec, NULL, 1, 1));
296 }
297
298 /*
299 * Paginate messages, even printing ignored fields.
300 */
301 int
More(void * v)302 More(void *v)
303 {
304 int *msgvec = v;
305
306 return(type1(msgvec, NULL, 0, 1));
307 }
308
309 /*
310 * Type out messages, honor ignored fields.
311 */
312 int
type(void * v)313 type(void *v)
314 {
315 int *msgvec = v;
316
317 return(type1(msgvec, NULL, 1, 0));
318 }
319
320 /*
321 * Type out messages, even printing ignored fields.
322 */
323 int
Type(void * v)324 Type(void *v)
325 {
326 int *msgvec = v;
327
328 return(type1(msgvec, NULL, 0, 0));
329 }
330
331 /*
332 * Type out the messages requested.
333 */
334 int
type1(int * msgvec,char * cmd,int doign,int page)335 type1(int *msgvec, char *cmd, int doign, int page)
336 {
337 int nlines, *ip, restoreterm;
338 struct message *mp;
339 struct termios tbuf;
340 char *cp;
341 FILE *obuf;
342
343 obuf = stdout;
344 restoreterm = 0;
345
346 /*
347 * start a pipe if needed.
348 */
349 if (cmd) {
350 restoreterm = (tcgetattr(fileno(stdin), &tbuf) == 0);
351 obuf = Popen(cmd, "w");
352 if (obuf == NULL) {
353 warn("%s", cmd);
354 obuf = stdout;
355 }
356 } else if (value("interactive") != NULL &&
357 (page || (cp = value("crt")) != NULL)) {
358 nlines = 0;
359 if (!page) {
360 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++)
361 nlines += message[*ip - 1].m_lines;
362 }
363 if (page || nlines > (*cp ? atoi(cp) : realscreenheight)) {
364 restoreterm = (tcgetattr(fileno(stdin), &tbuf) == 0);
365 cp = value("PAGER");
366 obuf = Popen(cp, "w");
367 if (obuf == NULL) {
368 warn("%s", cp);
369 obuf = stdout;
370 }
371 }
372 }
373
374 /*
375 * Send messages to the output.
376 */
377 for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) {
378 mp = &message[*ip - 1];
379 touch(mp);
380 dot = mp;
381 if (cmd == NULL && value("quiet") == NULL)
382 fprintf(obuf, "Message %d:\n", *ip);
383 if (sendmessage(mp, obuf, doign ? ignore : 0, NULL) == -1)
384 break;
385 }
386
387 if (obuf != stdout) {
388 (void)Pclose(obuf);
389 if (restoreterm)
390 (void)tcsetattr(fileno(stdin), TCSADRAIN, &tbuf);
391 }
392 return(0);
393 }
394
395 /*
396 * Print the top so many lines of each desired message.
397 * The number of lines is taken from the variable "toplines"
398 * and defaults to 5.
399 */
400 int
top(void * v)401 top(void * v)
402 {
403 int *msgvec = v;
404 int *ip;
405 struct message *mp;
406 int c, topl, lines, lineb;
407 char *valtop, linebuf[LINESIZE];
408 FILE *ibuf;
409
410 topl = 5;
411 valtop = value("toplines");
412 if (valtop != NULL) {
413 topl = atoi(valtop);
414 if (topl < 0 || topl > 10000)
415 topl = 5;
416 }
417 lineb = 1;
418 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
419 mp = &message[*ip - 1];
420 touch(mp);
421 dot = mp;
422 if (value("quiet") == NULL)
423 printf("Message %d:\n", *ip);
424 ibuf = setinput(mp);
425 c = mp->m_lines;
426 if (!lineb)
427 putchar('\n');
428 for (lines = 0; lines < c && lines <= topl; lines++) {
429 if (readline(ibuf, linebuf, sizeof(linebuf), NULL) < 0)
430 break;
431 puts(linebuf);
432 lineb = blankline(linebuf);
433 }
434 }
435 return(0);
436 }
437
438 /*
439 * Touch all the given messages so that they will
440 * get mboxed.
441 */
442 int
stouch(void * v)443 stouch(void *v)
444 {
445 int *msgvec = v;
446 int *ip;
447
448 for (ip = msgvec; *ip != 0; ip++) {
449 dot = &message[*ip-1];
450 dot->m_flag |= MTOUCH;
451 dot->m_flag &= ~MPRESERVE;
452 }
453 return(0);
454 }
455
456 /*
457 * Make sure all passed messages get mboxed.
458 */
459 int
mboxit(void * v)460 mboxit(void *v)
461 {
462 int *msgvec = v;
463 int *ip;
464
465 for (ip = msgvec; *ip != 0; ip++) {
466 dot = &message[*ip-1];
467 dot->m_flag |= MTOUCH|MBOX;
468 dot->m_flag &= ~MPRESERVE;
469 }
470 return(0);
471 }
472
473 /*
474 * List the folders the user currently has.
475 */
476 int
folders(void * v)477 folders(void *v)
478 {
479 char *files = (char *)v;
480 char dirname[PATHSIZE];
481 char cmd[BUFSIZ];
482
483 if (getfold(dirname, sizeof(dirname)) < 0)
484 strlcpy(dirname, "$HOME", sizeof(dirname));
485
486 snprintf(cmd, sizeof(cmd), "cd %s; %s %s", dirname, value("LISTER"),
487 files && *files ? files : "");
488
489 (void)run_command(value("SHELL"), 0, -1, -1, "-c", cmd, NULL);
490 return(0);
491 }
492
493 /*
494 * Update the mail file with any new messages that have
495 * come in since we started reading mail.
496 */
497 int
inc(void * v)498 inc(void *v)
499 {
500 int nmsg, mdot;
501
502 nmsg = incfile();
503
504 if (nmsg == 0) {
505 puts("No new mail.");
506 } else if (nmsg > 0) {
507 mdot = newfileinfo(msgCount - nmsg);
508 dot = &message[mdot - 1];
509 } else {
510 puts("\"inc\" command failed...");
511 }
512
513 return(0);
514 }
515
516 /*
517 * User hit ^C while printing the headers.
518 */
519 void
hdrint(int s)520 hdrint(int s)
521 {
522
523 gothdrint = 1;
524 }
525