Sweep-fix comparing pointers with 0 (and assigning 0 to pointers).
[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  */
36
37 #include "rcv.h"
38 #include <sys/wait.h>
39 #include "extern.h"
40
41 /*
42  * Mail -- a mail program
43  *
44  * More user commands.
45  */
46
47 extern int wait_status;
48
49 /*
50  * If any arguments were given, go to the next applicable argument
51  * following dot, otherwise, go to the next applicable message.
52  * If given as first command with no arguments, print first message.
53  */
54 int
55 next(int *msgvec)
56 {
57         struct message *mp;
58         int *ip, *ip2, list[2], mdot;
59
60         if (*msgvec != 0) {
61
62                 /*
63                  * If some messages were supplied, find the
64                  * first applicable one following dot using
65                  * wrap around.
66                  */
67
68                 mdot = dot - &message[0] + 1;
69
70                 /*
71                  * Find the first message in the supplied
72                  * message list which follows dot.
73                  */
74
75                 for (ip = msgvec; *ip != 0; ip++)
76                         if (*ip > mdot)
77                                 break;
78                 if (*ip == 0)
79                         ip = msgvec;
80                 ip2 = ip;
81                 do {
82                         mp = &message[*ip2 - 1];
83                         if ((mp->m_flag & MDELETED) == 0) {
84                                 dot = mp;
85                                 goto hitit;
86                         }
87                         if (*ip2 != 0)
88                                 ip2++;
89                         if (*ip2 == 0)
90                                 ip2 = msgvec;
91                 } while (ip2 != ip);
92                 printf("No messages applicable\n");
93                 return (1);
94         }
95
96         /*
97          * If this is the first command, select message 1.
98          * Note that this must exist for us to get here at all.
99          */
100
101         if (!sawcom)
102                 goto hitit;
103
104         /*
105          * Just find the next good message after dot, no
106          * wraparound.
107          */
108
109         for (mp = dot+1; mp < &message[msgCount]; mp++)
110                 if ((mp->m_flag & (MDELETED|MSAVED)) == 0)
111                         break;
112         if (mp >= &message[msgCount]) {
113                 printf("At EOF\n");
114                 return (0);
115         }
116         dot = mp;
117 hitit:
118         /*
119          * Print dot.
120          */
121
122         list[0] = dot - &message[0] + 1;
123         list[1] = 0;
124         return (type(list));
125 }
126
127 /*
128  * Save a message in a file.  Mark the message as saved
129  * so we can discard when the user quits.
130  */
131 int
132 save(char *str)
133 {
134         return (save1(str, 1, "save", saveignore));
135 }
136
137 /*
138  * Copy a message to a file without affected its saved-ness
139  */
140 int
141 copycmd(char *str)
142 {
143         return (save1(str, 0, "copy", saveignore));
144 }
145
146 /*
147  * Save/copy the indicated messages at the end of the passed file name.
148  * If mark is true, mark the message "saved."
149  */
150 int
151 save1(char *str, int mark, const char *cmd, struct ignoretab *ignore)
152 {
153         struct message *mp;
154         char *file;
155         const char *disp;
156         int f, *msgvec, *ip;
157         FILE *obuf;
158
159         msgvec = (int *)salloc((msgCount + 2) * sizeof(*msgvec));
160         if ((file = snarf(str, &f)) == NULL)
161                 return (1);
162         if (!f) {
163                 *msgvec = first(0, MMNORM);
164                 if (*msgvec == 0) {
165                         printf("No messages to %s.\n", cmd);
166                         return (1);
167                 }
168                 msgvec[1] = 0;
169         }
170         if (f && getmsglist(str, msgvec, 0) < 0)
171                 return (1);
172         if ((file = expand(file)) == NULL)
173                 return (1);
174         printf("\"%s\" ", file);
175         fflush(stdout);
176         if (access(file, 0) >= 0)
177                 disp = "[Appended]";
178         else
179                 disp = "[New file]";
180         if ((obuf = Fopen(file, "a")) == NULL) {
181                 warn(NULL);
182                 return (1);
183         }
184         for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
185                 mp = &message[*ip - 1];
186                 touch(mp);
187                 if (sendmessage(mp, obuf, ignore, NULL) < 0) {
188                         warnx("%s", file);
189                         Fclose(obuf);
190                         return (1);
191                 }
192                 if (mark)
193                         mp->m_flag |= MSAVED;
194         }
195         fflush(obuf);
196         if (ferror(obuf))
197                 warn("%s", file);
198         Fclose(obuf);
199         printf("%s\n", disp);
200         return (0);
201 }
202
203 /*
204  * Write the indicated messages at the end of the passed
205  * file name, minus header and trailing blank line.
206  */
207 int
208 swrite(char *str)
209 {
210         return (save1(str, 1, "write", ignoreall));
211 }
212
213 /*
214  * Snarf the file from the end of the command line and
215  * return a pointer to it.  If there is no file attached,
216  * just return NULL.  Put a null in front of the file
217  * name so that the message list processing won't see it,
218  * unless the file name is the only thing on the line, in
219  * which case, return 0 in the reference flag variable.
220  */
221
222 char *
223 snarf(char *linebuf, int *flag)
224 {
225         char *cp;
226
227         *flag = 1;
228         cp = strlen(linebuf) + linebuf - 1;
229
230         /*
231          * Strip away trailing blanks.
232          */
233
234         while (cp > linebuf && isspace((unsigned char)*cp))
235                 cp--;
236         *++cp = '\0';
237
238         /*
239          * Now search for the beginning of the file name.
240          */
241
242         while (cp > linebuf && !isspace((unsigned char)*cp))
243                 cp--;
244         if (*cp == '\0') {
245                 printf("No file specified.\n");
246                 return (NULL);
247         }
248         if (isspace((unsigned char)*cp))
249                 *cp++ = '\0';
250         else
251                 *flag = 0;
252         return (cp);
253 }
254
255 /*
256  * Delete messages.
257  */
258 int
259 delete(int *msgvec)
260 {
261         delm(msgvec);
262         return (0);
263 }
264
265 /*
266  * Delete messages, then type the new dot.
267  */
268 int
269 deltype(int *msgvec)
270 {
271         int list[2];
272         int lastdot;
273
274         lastdot = dot - &message[0] + 1;
275         if (delm(msgvec) >= 0) {
276                 list[0] = dot - &message[0] + 1;
277                 if (list[0] > lastdot) {
278                         touch(dot);
279                         list[1] = 0;
280                         return (type(list));
281                 }
282                 printf("At EOF\n");
283         } else
284                 printf("No more messages\n");
285         return (0);
286 }
287
288 /*
289  * Delete the indicated messages.
290  * Set dot to some nice place afterwards.
291  * Internal interface.
292  */
293 int
294 delm(int *msgvec)
295 {
296         struct message *mp;
297         int *ip, last;
298
299         last = 0;
300         for (ip = msgvec; *ip != 0; ip++) {
301                 mp = &message[*ip - 1];
302                 touch(mp);
303                 mp->m_flag |= MDELETED|MTOUCH;
304                 mp->m_flag &= ~(MPRESERVE|MSAVED|MBOX);
305                 last = *ip;
306         }
307         if (last != 0) {
308                 dot = &message[last-1];
309                 last = first(0, MDELETED);
310                 if (last != 0) {
311                         dot = &message[last-1];
312                         return (0);
313                 }
314                 else {
315                         dot = &message[0];
316                         return (-1);
317                 }
318         }
319
320         /*
321          * Following can't happen -- it keeps lint happy
322          */
323
324         return (-1);
325 }
326
327 /*
328  * Undelete the indicated messages.
329  */
330 int
331 undelete_messages(int *msgvec)
332 {
333         struct message *mp;
334         int *ip;
335
336         for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
337                 mp = &message[*ip - 1];
338                 touch(mp);
339                 dot = mp;
340                 mp->m_flag &= ~MDELETED;
341         }
342         return (0);
343 }
344
345 /*
346  * Interactively dump core on "core"
347  */
348 int
349 core(void)
350 {
351         int pid;
352
353         switch (pid = fork()) {
354         case -1:
355                 warn("fork");
356                 return (1);
357         case 0:
358                 abort();
359                 _exit(1);
360         }
361         printf("Okie dokie");
362         fflush(stdout);
363         wait_child(pid);
364         if (WIFSIGNALED(wait_status) && WCOREDUMP(wait_status))
365                 printf(" -- Core dumped.\n");
366         else
367                 printf(" -- Can't dump core.\n");
368         return (0);
369 }
370
371 /*
372  * Clobber as many bytes of stack as the user requests.
373  */
374 int
375 clobber(char **argv)
376 {
377         int times;
378
379         if (argv[0] == NULL)
380                 times = 1;
381         else
382                 times = (atoi(argv[0]) + 511) / 512;
383         clob1(times);
384         return (0);
385 }
386
387 /*
388  * Clobber the stack.
389  */
390 void
391 clob1(int n)
392 {
393         char buf[512];
394         char *cp;
395
396         if (n <= 0)
397                 return;
398         for (cp = buf; cp < &buf[512]; *cp++ = 0xFF)
399                 ;
400         clob1(n - 1);
401 }
402
403 /*
404  * Add the given header fields to the retained list.
405  * If no arguments, print the current list of retained fields.
406  */
407 int
408 retfield(char **list)
409 {
410         return (ignore1(list, ignore + 1, "retained"));
411 }
412
413 /*
414  * Add the given header fields to the ignored list.
415  * If no arguments, print the current list of ignored fields.
416  */
417 int
418 igfield(char **list)
419 {
420         return (ignore1(list, ignore, "ignored"));
421 }
422
423 int
424 saveretfield(char **list)
425 {
426         return (ignore1(list, saveignore + 1, "retained"));
427 }
428
429 int
430 saveigfield(char **list)
431 {
432         return (ignore1(list, saveignore, "ignored"));
433 }
434
435 int
436 ignore1(char **list, struct ignoretab *tab, const char *which)
437 {
438         char field[LINESIZE];
439         int h;
440         struct ignore *igp;
441         char **ap;
442
443         if (*list == NULL)
444                 return (igshow(tab, which));
445         for (ap = list; *ap != NULL; ap++) {
446                 istrncpy(field, *ap, sizeof(field));
447                 if (member(field, tab))
448                         continue;
449                 h = hash(field);
450                 igp = calloc(1, sizeof(struct ignore));
451                 igp->i_field = calloc((unsigned)strlen(field) + 1,
452                     sizeof(char));
453                 strcpy(igp->i_field, field);
454                 igp->i_link = tab->i_head[h];
455                 tab->i_head[h] = igp;
456                 tab->i_count++;
457         }
458         return (0);
459 }
460
461 /*
462  * Print out all currently retained fields.
463  */
464 int
465 igshow(struct ignoretab *tab, const char *which)
466 {
467         int h;
468         struct ignore *igp;
469         char **ap, **ring;
470
471         if (tab->i_count == 0) {
472                 printf("No fields currently being %s.\n", which);
473                 return (0);
474         }
475         ring = (char **)salloc((tab->i_count + 1) * sizeof(char *));
476         ap = ring;
477         for (h = 0; h < HSHSIZE; h++)
478                 for (igp = tab->i_head[h]; igp != NULL; igp = igp->i_link)
479                         *ap++ = igp->i_field;
480         *ap = NULL;
481         qsort(ring, tab->i_count, sizeof(char *), igcomp);
482         for (ap = ring; *ap != NULL; ap++)
483                 printf("%s\n", *ap);
484         return (0);
485 }
486
487 /*
488  * Compare two names for sorting ignored field list.
489  */
490 int
491 igcomp(const void *l, const void *r)
492 {
493         return (strcmp(*(const char **)l, *(const char **)r));
494 }