Add the DragonFly cvs id and perform general cleanups on cvs/rcs/sccs ids. Most
[dragonfly.git] / usr.bin / mail / cmd2.c
1 /*
2  * Copyright (c) 1980, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * @(#)cmd2.c   8.1 (Berkeley) 6/6/93
34  * $FreeBSD: src/usr.bin/mail/cmd2.c,v 1.5.6.3 2003/01/06 05:46:03 mikeh Exp $
35  * $DragonFly: src/usr.bin/mail/cmd2.c,v 1.2 2003/06/17 04:29:28 dillon Exp $
36  */
37
38 #include "rcv.h"
39 #include <sys/wait.h>
40 #include "extern.h"
41
42 /*
43  * Mail -- a mail program
44  *
45  * More user commands.
46  */
47
48 extern int wait_status;
49
50 /*
51  * If any arguments were given, go to the next applicable argument
52  * following dot, otherwise, go to the next applicable message.
53  * If given as first command with no arguments, print first message.
54  */
55 int
56 next(msgvec)
57         int *msgvec;
58 {
59         struct message *mp;
60         int *ip, *ip2, list[2], mdot;
61
62         if (*msgvec != 0) {
63
64                 /*
65                  * If some messages were supplied, find the
66                  * first applicable one following dot using
67                  * wrap around.
68                  */
69
70                 mdot = dot - &message[0] + 1;
71
72                 /*
73                  * Find the first message in the supplied
74                  * message list which follows dot.
75                  */
76
77                 for (ip = msgvec; *ip != 0; ip++)
78                         if (*ip > mdot)
79                                 break;
80                 if (*ip == 0)
81                         ip = msgvec;
82                 ip2 = ip;
83                 do {
84                         mp = &message[*ip2 - 1];
85                         if ((mp->m_flag & MDELETED) == 0) {
86                                 dot = mp;
87                                 goto hitit;
88                         }
89                         if (*ip2 != 0)
90                                 ip2++;
91                         if (*ip2 == 0)
92                                 ip2 = msgvec;
93                 } while (ip2 != ip);
94                 printf("No messages applicable\n");
95                 return (1);
96         }
97
98         /*
99          * If this is the first command, select message 1.
100          * Note that this must exist for us to get here at all.
101          */
102
103         if (!sawcom)
104                 goto hitit;
105
106         /*
107          * Just find the next good message after dot, no
108          * wraparound.
109          */
110
111         for (mp = dot+1; mp < &message[msgCount]; mp++)
112                 if ((mp->m_flag & (MDELETED|MSAVED)) == 0)
113                         break;
114         if (mp >= &message[msgCount]) {
115                 printf("At EOF\n");
116                 return (0);
117         }
118         dot = mp;
119 hitit:
120         /*
121          * Print dot.
122          */
123
124         list[0] = dot - &message[0] + 1;
125         list[1] = 0;
126         return (type(list));
127 }
128
129 /*
130  * Save a message in a file.  Mark the message as saved
131  * so we can discard when the user quits.
132  */
133 int
134 save(str)
135         char str[];
136 {
137
138         return (save1(str, 1, "save", saveignore));
139 }
140
141 /*
142  * Copy a message to a file without affected its saved-ness
143  */
144 int
145 copycmd(str)
146         char str[];
147 {
148
149         return (save1(str, 0, "copy", saveignore));
150 }
151
152 /*
153  * Save/copy the indicated messages at the end of the passed file name.
154  * If mark is true, mark the message "saved."
155  */
156 int
157 save1(str, mark, cmd, ignore)
158         char str[];
159         int mark;
160         const char *cmd;
161         struct ignoretab *ignore;
162 {
163         struct message *mp;
164         char *file;
165         const char *disp;
166         int f, *msgvec, *ip;
167         FILE *obuf;
168
169         msgvec = (int *)salloc((msgCount + 2) * sizeof(*msgvec));
170         if ((file = snarf(str, &f)) == NULL)
171                 return (1);
172         if (!f) {
173                 *msgvec = first(0, MMNORM);
174                 if (*msgvec == 0) {
175                         printf("No messages to %s.\n", cmd);
176                         return (1);
177                 }
178                 msgvec[1] = 0;
179         }
180         if (f && getmsglist(str, msgvec, 0) < 0)
181                 return (1);
182         if ((file = expand(file)) == NULL)
183                 return (1);
184         printf("\"%s\" ", file);
185         (void)fflush(stdout);
186         if (access(file, 0) >= 0)
187                 disp = "[Appended]";
188         else
189                 disp = "[New file]";
190         if ((obuf = Fopen(file, "a")) == NULL) {
191                 warn((char *)NULL);
192                 return (1);
193         }
194         for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
195                 mp = &message[*ip - 1];
196                 touch(mp);
197                 if (sendmessage(mp, obuf, ignore, NULL) < 0) {
198                         warnx("%s", file);
199                         (void)Fclose(obuf);
200                         return (1);
201                 }
202                 if (mark)
203                         mp->m_flag |= MSAVED;
204         }
205         (void)fflush(obuf);
206         if (ferror(obuf))
207                 warn("%s", file);
208         (void)Fclose(obuf);
209         printf("%s\n", disp);
210         return (0);
211 }
212
213 /*
214  * Write the indicated messages at the end of the passed
215  * file name, minus header and trailing blank line.
216  */
217 int
218 swrite(str)
219         char str[];
220 {
221
222         return (save1(str, 1, "write", ignoreall));
223 }
224
225 /*
226  * Snarf the file from the end of the command line and
227  * return a pointer to it.  If there is no file attached,
228  * just return NULL.  Put a null in front of the file
229  * name so that the message list processing won't see it,
230  * unless the file name is the only thing on the line, in
231  * which case, return 0 in the reference flag variable.
232  */
233
234 char *
235 snarf(linebuf, flag)
236         char linebuf[];
237         int *flag;
238 {
239         char *cp;
240
241         *flag = 1;
242         cp = strlen(linebuf) + linebuf - 1;
243
244         /*
245          * Strip away trailing blanks.
246          */
247
248         while (cp > linebuf && isspace((unsigned char)*cp))
249                 cp--;
250         *++cp = '\0';
251
252         /*
253          * Now search for the beginning of the file name.
254          */
255
256         while (cp > linebuf && !isspace((unsigned char)*cp))
257                 cp--;
258         if (*cp == '\0') {
259                 printf("No file specified.\n");
260                 return (NULL);
261         }
262         if (isspace((unsigned char)*cp))
263                 *cp++ = '\0';
264         else
265                 *flag = 0;
266         return (cp);
267 }
268
269 /*
270  * Delete messages.
271  */
272 int
273 delete(msgvec)
274         int msgvec[];
275 {
276
277         delm(msgvec);
278         return (0);
279 }
280
281 /*
282  * Delete messages, then type the new dot.
283  */
284 int
285 deltype(msgvec)
286         int msgvec[];
287 {
288         int list[2];
289         int lastdot;
290
291         lastdot = dot - &message[0] + 1;
292         if (delm(msgvec) >= 0) {
293                 list[0] = dot - &message[0] + 1;
294                 if (list[0] > lastdot) {
295                         touch(dot);
296                         list[1] = 0;
297                         return (type(list));
298                 }
299                 printf("At EOF\n");
300         } else
301                 printf("No more messages\n");
302         return (0);
303 }
304
305 /*
306  * Delete the indicated messages.
307  * Set dot to some nice place afterwards.
308  * Internal interface.
309  */
310 int
311 delm(msgvec)
312         int *msgvec;
313 {
314         struct message *mp;
315         int *ip, last;
316
317         last = 0;
318         for (ip = msgvec; *ip != 0; ip++) {
319                 mp = &message[*ip - 1];
320                 touch(mp);
321                 mp->m_flag |= MDELETED|MTOUCH;
322                 mp->m_flag &= ~(MPRESERVE|MSAVED|MBOX);
323                 last = *ip;
324         }
325         if (last != 0) {
326                 dot = &message[last-1];
327                 last = first(0, MDELETED);
328                 if (last != 0) {
329                         dot = &message[last-1];
330                         return (0);
331                 }
332                 else {
333                         dot = &message[0];
334                         return (-1);
335                 }
336         }
337
338         /*
339          * Following can't happen -- it keeps lint happy
340          */
341
342         return (-1);
343 }
344
345 /*
346  * Undelete the indicated messages.
347  */
348 int
349 undelete_messages(msgvec)
350         int *msgvec;
351 {
352         struct message *mp;
353         int *ip;
354
355         for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
356                 mp = &message[*ip - 1];
357                 touch(mp);
358                 dot = mp;
359                 mp->m_flag &= ~MDELETED;
360         }
361         return (0);
362 }
363
364 /*
365  * Interactively dump core on "core"
366  */
367 int
368 core()
369 {
370         int pid;
371
372         switch (pid = fork()) {
373         case -1:
374                 warn("fork");
375                 return (1);
376         case 0:
377                 abort();
378                 _exit(1);
379         }
380         printf("Okie dokie");
381         (void)fflush(stdout);
382         wait_child(pid);
383         if (WIFSIGNALED(wait_status) && WCOREDUMP(wait_status))
384                 printf(" -- Core dumped.\n");
385         else
386                 printf(" -- Can't dump core.\n");
387         return (0);
388 }
389
390 /*
391  * Clobber as many bytes of stack as the user requests.
392  */
393 int
394 clobber(argv)
395         char **argv;
396 {
397         int times;
398
399         if (argv[0] == 0)
400                 times = 1;
401         else
402                 times = (atoi(argv[0]) + 511) / 512;
403         clob1(times);
404         return (0);
405 }
406
407 /*
408  * Clobber the stack.
409  */
410 void
411 clob1(n)
412         int n;
413 {
414         char buf[512];
415         char *cp;
416
417         if (n <= 0)
418                 return;
419         for (cp = buf; cp < &buf[512]; *cp++ = 0xFF)
420                 ;
421         clob1(n - 1);
422 }
423
424 /*
425  * Add the given header fields to the retained list.
426  * If no arguments, print the current list of retained fields.
427  */
428 int
429 retfield(list)
430         char *list[];
431 {
432
433         return (ignore1(list, ignore + 1, "retained"));
434 }
435
436 /*
437  * Add the given header fields to the ignored list.
438  * If no arguments, print the current list of ignored fields.
439  */
440 int
441 igfield(list)
442         char *list[];
443 {
444
445         return (ignore1(list, ignore, "ignored"));
446 }
447
448 int
449 saveretfield(list)
450         char *list[];
451 {
452
453         return (ignore1(list, saveignore + 1, "retained"));
454 }
455
456 int
457 saveigfield(list)
458         char *list[];
459 {
460
461         return (ignore1(list, saveignore, "ignored"));
462 }
463
464 int
465 ignore1(list, tab, which)
466         char *list[];
467         struct ignoretab *tab;
468         const char *which;
469 {
470         char field[LINESIZE];
471         int h;
472         struct ignore *igp;
473         char **ap;
474
475         if (*list == NULL)
476                 return (igshow(tab, which));
477         for (ap = list; *ap != 0; ap++) {
478                 istrncpy(field, *ap, sizeof(field));
479                 if (member(field, tab))
480                         continue;
481                 h = hash(field);
482                 igp = calloc(1, sizeof(struct ignore));
483                 igp->i_field = calloc((unsigned)strlen(field) + 1,
484                     sizeof(char));
485                 strcpy(igp->i_field, field);
486                 igp->i_link = tab->i_head[h];
487                 tab->i_head[h] = igp;
488                 tab->i_count++;
489         }
490         return (0);
491 }
492
493 /*
494  * Print out all currently retained fields.
495  */
496 int
497 igshow(tab, which)
498         struct ignoretab *tab;
499         const char *which;
500 {
501         int h;
502         struct ignore *igp;
503         char **ap, **ring;
504
505         if (tab->i_count == 0) {
506                 printf("No fields currently being %s.\n", which);
507                 return (0);
508         }
509         ring = (char **)salloc((tab->i_count + 1) * sizeof(char *));
510         ap = ring;
511         for (h = 0; h < HSHSIZE; h++)
512                 for (igp = tab->i_head[h]; igp != NULL; igp = igp->i_link)
513                         *ap++ = igp->i_field;
514         *ap = 0;
515         qsort(ring, tab->i_count, sizeof(char *), igcomp);
516         for (ap = ring; *ap != 0; ap++)
517                 printf("%s\n", *ap);
518         return (0);
519 }
520
521 /*
522  * Compare two names for sorting ignored field list.
523  */
524 int
525 igcomp(l, r)
526         const void *l, *r;
527 {
528
529         return (strcmp(*(const char **)l, *(const char **)r));
530 }