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