removed some unnecessary global vars, dynamically allocate some others,
[unix-history] / usr / src / usr.bin / mail / cmd2.c
CommitLineData
d0aeaf5a
DF
1/*
2 * Copyright (c) 1980 Regents of the University of California.
0c5f72fb
KB
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms are permitted
acfc7e9b
KB
6 * provided that the above copyright notice and this paragraph are
7 * duplicated in all such forms and that any documentation,
8 * advertising materials, and other materials related to such
9 * distribution and use acknowledge that the software was developed
10 * by the University of California, Berkeley. The name of the
11 * University may not be used to endorse or promote products derived
12 * from this software without specific prior written permission.
13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
d0aeaf5a
DF
16 */
17
acfc7e9b
KB
18#ifndef lint
19static char sccsid[] = "@(#)cmd2.c 5.7 (Berkeley) %G%";
20#endif /* not lint */
a13d1f28
KS
21
22#include "rcv.h"
23#include <sys/stat.h>
828615a1 24#include <sys/wait.h>
a13d1f28
KS
25
26/*
27 * Mail -- a mail program
28 *
29 * More user commands.
30 */
31
a13d1f28
KS
32/*
33 * If any arguments were given, go to the next applicable argument
34 * following dot, otherwise, go to the next applicable message.
35 * If given as first command with no arguments, print first message.
36 */
37
38next(msgvec)
39 int *msgvec;
40{
41 register struct message *mp;
42 register int *ip, *ip2;
43 int list[2], mdot;
44
45 if (*msgvec != NULL) {
46
47 /*
48 * If some messages were supplied, find the
49 * first applicable one following dot using
50 * wrap around.
51 */
52
53 mdot = dot - &message[0] + 1;
30629819
KS
54
55 /*
56 * Find the first message in the supplied
57 * message list which follows dot.
58 */
59
a13d1f28
KS
60 for (ip = msgvec; *ip != NULL; ip++)
61 if (*ip > mdot)
62 break;
63 if (*ip == NULL)
64 ip = msgvec;
65 ip2 = ip;
66 do {
a13d1f28
KS
67 mp = &message[*ip2 - 1];
68 if ((mp->m_flag & MDELETED) == 0) {
69 dot = mp;
70 goto hitit;
71 }
30629819
KS
72 if (*ip2 != NULL)
73 ip2++;
74 if (*ip2 == NULL)
75 ip2 = msgvec;
a13d1f28
KS
76 } while (ip2 != ip);
77 printf("No messages applicable\n");
78 return(1);
79 }
80
81 /*
82 * If this is the first command, select message 1.
83 * Note that this must exist for us to get here at all.
84 */
85
799afc8f 86 if (!sawcom)
a13d1f28 87 goto hitit;
a13d1f28
KS
88
89 /*
90 * Just find the next good message after dot, no
91 * wraparound.
92 */
93
94 for (mp = dot+1; mp < &message[msgCount]; mp++)
95 if ((mp->m_flag & (MDELETED|MSAVED)) == 0)
96 break;
97 if (mp >= &message[msgCount]) {
98 printf("At EOF\n");
99 return(0);
100 }
101 dot = mp;
102hitit:
103 /*
104 * Print dot.
105 */
106
107 list[0] = dot - &message[0] + 1;
108 list[1] = NULL;
109 return(type(list));
110}
111
112/*
c7db23a4
KS
113 * Save a message in a file. Mark the message as saved
114 * so we can discard when the user quits.
a13d1f28 115 */
a13d1f28
KS
116save(str)
117 char str[];
118{
c7db23a4
KS
119
120 return(save1(str, 1));
121}
122
123/*
124 * Copy a message to a file without affected its saved-ness
125 */
126copycmd(str)
127 char str[];
128{
129
130 return(save1(str, 0));
131}
132
133/*
134 * Save/copy the indicated messages at the end of the passed file name.
135 * If mark is true, mark the message "saved."
136 */
137save1(str, mark)
138 char str[];
139{
a13d1f28
KS
140 register int *ip, mesg;
141 register struct message *mp;
c7db23a4 142 char *file, *disp, *cmd;
7667ac30
CS
143 int f, *msgvec, lc, t;
144 long cc;
a13d1f28
KS
145 FILE *obuf;
146 struct stat statb;
147
c7db23a4 148 cmd = mark ? "save" : "copy";
a13d1f28
KS
149 msgvec = (int *) salloc((msgCount + 2) * sizeof *msgvec);
150 if ((file = snarf(str, &f)) == NOSTR)
151 return(1);
152 if (!f) {
153 *msgvec = first(0, MMNORM);
154 if (*msgvec == NULL) {
c7db23a4 155 printf("No messages to %s.\n", cmd);
a13d1f28
KS
156 return(1);
157 }
158 msgvec[1] = NULL;
159 }
160 if (f && getmsglist(str, msgvec, 0) < 0)
161 return(1);
162 if ((file = expand(file)) == NOSTR)
163 return(1);
164 printf("\"%s\" ", file);
80187484 165 fflush(stdout);
a13d1f28
KS
166 if (stat(file, &statb) >= 0)
167 disp = "[Appended]";
168 else
169 disp = "[New file]";
170 if ((obuf = fopen(file, "a")) == NULL) {
171 perror(NOSTR);
172 return(1);
173 }
7667ac30
CS
174 cc = 0L;
175 lc = 0;
a13d1f28
KS
176 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
177 mesg = *ip;
178 touch(mesg);
179 mp = &message[mesg-1];
887efe38 180 if ((t = send(mp, obuf, saveignore)) < 0) {
a13d1f28
KS
181 perror(file);
182 fclose(obuf);
183 return(1);
184 }
185 lc += t;
7667ac30 186 cc += mp->m_size;
c7db23a4
KS
187 if (mark)
188 mp->m_flag |= MSAVED;
a13d1f28
KS
189 }
190 fflush(obuf);
191 if (ferror(obuf))
192 perror(file);
193 fclose(obuf);
7667ac30 194 printf("%s %d/%ld\n", disp, lc, cc);
a13d1f28
KS
195 return(0);
196}
197
198/*
199 * Write the indicated messages at the end of the passed
200 * file name, minus header and trailing blank line.
201 */
202
203swrite(str)
204 char str[];
205{
206 register int *ip, mesg;
207 register struct message *mp;
208 register char *file, *disp;
209 char linebuf[BUFSIZ];
210 int f, *msgvec, lc, cc, t;
211 FILE *obuf, *mesf;
212 struct stat statb;
213
214 msgvec = (int *) salloc((msgCount + 2) * sizeof *msgvec);
215 if ((file = snarf(str, &f)) == NOSTR)
216 return(1);
217 if ((file = expand(file)) == NOSTR)
218 return(1);
219 if (!f) {
220 *msgvec = first(0, MMNORM);
221 if (*msgvec == NULL) {
222 printf("No messages to write.\n");
223 return(1);
224 }
225 msgvec[1] = NULL;
226 }
227 if (f && getmsglist(str, msgvec, 0) < 0)
228 return(1);
229 printf("\"%s\" ", file);
80187484 230 fflush(stdout);
a13d1f28
KS
231 if (stat(file, &statb) >= 0)
232 disp = "[Appended]";
233 else
234 disp = "[New file]";
235 if ((obuf = fopen(file, "a")) == NULL) {
236 perror(NOSTR);
237 return(1);
238 }
239 cc = lc = 0;
240 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
241 mesg = *ip;
242 touch(mesg);
243 mp = &message[mesg-1];
244 mesf = setinput(mp);
9dc54648
S
245 t = mp->m_lines - 1;
246 while (t-- > 0) {
247 readline(mesf, linebuf);
248 if (blankline(linebuf))
249 break;
250 }
a13d1f28
KS
251 while (t-- > 0) {
252 fgets(linebuf, BUFSIZ, mesf);
253 fputs(linebuf, obuf);
254 cc += strlen(linebuf);
255 }
256 lc += mp->m_lines - 2;
257 mp->m_flag |= MSAVED;
258 }
259 fflush(obuf);
260 if (ferror(obuf))
261 perror(file);
262 fclose(obuf);
263 printf("%s %d/%d\n", disp, lc, cc);
264 return(0);
265}
266
267/*
268 * Snarf the file from the end of the command line and
269 * return a pointer to it. If there is no file attached,
270 * just return NOSTR. Put a null in front of the file
271 * name so that the message list processing won't see it,
272 * unless the file name is the only thing on the line, in
273 * which case, return 0 in the reference flag variable.
274 */
275
276char *
277snarf(linebuf, flag)
278 char linebuf[];
279 int *flag;
280{
281 register char *cp;
282
283 *flag = 1;
284 cp = strlen(linebuf) + linebuf - 1;
285
286 /*
287 * Strip away trailing blanks.
288 */
289
828615a1 290 while (cp > linebuf && isspace(*cp))
a13d1f28
KS
291 cp--;
292 *++cp = 0;
293
294 /*
295 * Now search for the beginning of the file name.
296 */
297
828615a1 298 while (cp > linebuf && !isspace(*cp))
a13d1f28
KS
299 cp--;
300 if (*cp == '\0') {
301 printf("No file specified.\n");
302 return(NOSTR);
303 }
828615a1 304 if (isspace(*cp))
a13d1f28
KS
305 *cp++ = 0;
306 else
307 *flag = 0;
308 return(cp);
309}
310
311/*
312 * Delete messages.
313 */
314
315delete(msgvec)
316 int msgvec[];
317{
318 return(delm(msgvec));
319}
320
321/*
322 * Delete messages, then type the new dot.
323 */
324
325deltype(msgvec)
326 int msgvec[];
327{
328 int list[2];
12c002da 329 int lastdot;
a13d1f28 330
12c002da 331 lastdot = dot - &message[0] + 1;
a13d1f28
KS
332 if (delm(msgvec) >= 0) {
333 list[0] = dot - &message[0];
334 list[0]++;
12c002da
KS
335 if (list[0] > lastdot) {
336 touch(list[0]);
337 list[1] = NULL;
338 return(type(list));
339 }
340 printf("At EOF\n");
341 return(0);
a13d1f28
KS
342 }
343 else {
344 printf("No more messages\n");
345 return(0);
346 }
347}
348
349/*
350 * Delete the indicated messages.
351 * Set dot to some nice place afterwards.
352 * Internal interface.
353 */
354
355delm(msgvec)
356 int *msgvec;
357{
358 register struct message *mp;
359 register *ip, mesg;
360 int last;
361
362 last = NULL;
363 for (ip = msgvec; *ip != NULL; ip++) {
364 mesg = *ip;
365 touch(mesg);
366 mp = &message[mesg-1];
3c139aaf
KS
367 mp->m_flag |= MDELETED|MTOUCH;
368 mp->m_flag &= ~(MPRESERVE|MSAVED|MBOX);
a13d1f28
KS
369 last = mesg;
370 }
371 if (last != NULL) {
372 dot = &message[last-1];
373 last = first(0, MDELETED);
374 if (last != NULL) {
375 dot = &message[last-1];
376 return(0);
377 }
378 else {
379 dot = &message[0];
380 return(-1);
381 }
382 }
383
384 /*
385 * Following can't happen -- it keeps lint happy
386 */
387
388 return(-1);
389}
390
391/*
392 * Undelete the indicated messages.
393 */
394
395undelete(msgvec)
396 int *msgvec;
397{
398 register struct message *mp;
399 register *ip, mesg;
400
401 for (ip = msgvec; ip-msgvec < msgCount; ip++) {
402 mesg = *ip;
403 if (mesg == 0)
404 return;
405 touch(mesg);
406 mp = &message[mesg-1];
407 dot = mp;
408 mp->m_flag &= ~MDELETED;
409 }
410}
411
412/*
413 * Interactively dump core on "core"
414 */
415
416core()
417{
418 register int pid;
828615a1 419 union wait status;
a13d1f28
KS
420
421 if ((pid = vfork()) == -1) {
422 perror("fork");
423 return(1);
424 }
425 if (pid == 0) {
426 abort();
427 _exit(1);
428 }
429 printf("Okie dokie");
430 fflush(stdout);
431 while (wait(&status) != pid)
432 ;
828615a1 433 if (status.w_coredump)
a13d1f28
KS
434 printf(" -- Core dumped\n");
435 else
436 printf("\n");
828615a1 437 return 0;
a13d1f28 438}
47e37b8b
KS
439
440/*
441 * Clobber as many bytes of stack as the user requests.
442 */
443clobber(argv)
444 char **argv;
445{
446 register int times;
447
448 if (argv[0] == 0)
449 times = 1;
450 else
451 times = (atoi(argv[0]) + 511) / 512;
ab61fde0 452 clob1(times);
47e37b8b
KS
453}
454
455/*
456 * Clobber the stack.
457 */
ab61fde0 458clob1(n)
47e37b8b
KS
459{
460 char buf[512];
461 register char *cp;
462
463 if (n <= 0)
464 return;
465 for (cp = buf; cp < &buf[512]; *cp++ = 0xFF)
466 ;
ab61fde0 467 clob1(n - 1);
47e37b8b 468}
eed74ec1 469
46053c99
S
470/*
471 * Add the given header fields to the retained list.
472 * If no arguments, print the current list of retained fields.
473 */
474retfield(list)
475 char *list[];
476{
46053c99 477
887efe38 478 return ignore1(list, ignore + 1, "retained");
46053c99
S
479}
480
481/*
887efe38
EW
482 * Add the given header fields to the ignored list.
483 * If no arguments, print the current list of ignored fields.
46053c99 484 */
887efe38
EW
485igfield(list)
486 char *list[];
46053c99 487{
46053c99 488
887efe38 489 return ignore1(list, ignore, "ignored");
46053c99
S
490}
491
887efe38
EW
492saveretfield(list)
493 char *list[];
494{
495
496 return ignore1(list, saveignore + 1, "retained");
497}
498
499saveigfield(list)
eed74ec1
KS
500 char *list[];
501{
887efe38
EW
502
503 return ignore1(list, saveignore, "ignored");
504}
505
506ignore1(list, tab, which)
507 char *list[];
508 struct ignoretab *tab;
509 char *which;
510{
eed74ec1
KS
511 char field[BUFSIZ];
512 register int h;
513 register struct ignore *igp;
514 char **ap;
515
516 if (argcount(list) == 0)
887efe38 517 return igshow(tab, which);
eed74ec1
KS
518 for (ap = list; *ap != 0; ap++) {
519 istrcpy(field, *ap);
887efe38
EW
520 if (member(field, tab))
521 continue;
eed74ec1
KS
522 h = hash(field);
523 igp = (struct ignore *) calloc(1, sizeof (struct ignore));
828615a1
EW
524 igp->i_field = calloc((unsigned) strlen(field) + 1,
525 sizeof (char));
eed74ec1 526 strcpy(igp->i_field, field);
887efe38
EW
527 igp->i_link = tab->i_head[h];
528 tab->i_head[h] = igp;
529 tab->i_count++;
eed74ec1 530 }
887efe38 531 return 0;
eed74ec1
KS
532}
533
534/*
887efe38 535 * Print out all currently retained fields.
eed74ec1 536 */
887efe38
EW
537igshow(tab, which)
538 struct ignoretab *tab;
539 char *which;
eed74ec1 540{
887efe38 541 register int h;
eed74ec1 542 struct ignore *igp;
93be40ac
KS
543 char **ap, **ring;
544 int igcomp();
eed74ec1 545
887efe38
EW
546 if (tab->i_count == 0) {
547 printf("No fields currently being %s.\n", which);
548 return 0;
93be40ac 549 }
887efe38 550 ring = (char **) salloc((tab->i_count + 1) * sizeof (char *));
93be40ac
KS
551 ap = ring;
552 for (h = 0; h < HSHSIZE; h++)
887efe38 553 for (igp = tab->i_head[h]; igp != 0; igp = igp->i_link)
93be40ac
KS
554 *ap++ = igp->i_field;
555 *ap = 0;
887efe38 556 qsort((char *) ring, tab->i_count, sizeof (char *), igcomp);
93be40ac
KS
557 for (ap = ring; *ap != 0; ap++)
558 printf("%s\n", *ap);
887efe38 559 return 0;
eed74ec1 560}
93be40ac
KS
561
562/*
563 * Compare two names for sorting ignored field list.
564 */
565igcomp(l, r)
566 char **l, **r;
567{
568
887efe38 569 return strcmp(*l, *r);
93be40ac 570}