1*cd9ca5b7Smmcc /* $OpenBSD: cmd2.c,v 1.22 2015/10/16 17:56:07 mmcc Exp $ */
2db59c1a6Smillert /* $NetBSD: cmd2.c,v 1.7 1997/05/17 19:55:10 pk Exp $ */
37eb34045Sderaadt
4df930be7Sderaadt /*
5df930be7Sderaadt * Copyright (c) 1980, 1993
6df930be7Sderaadt * The Regents of the University of California. All rights reserved.
7df930be7Sderaadt *
8df930be7Sderaadt * Redistribution and use in source and binary forms, with or without
9df930be7Sderaadt * modification, are permitted provided that the following conditions
10df930be7Sderaadt * are met:
11df930be7Sderaadt * 1. Redistributions of source code must retain the above copyright
12df930be7Sderaadt * notice, this list of conditions and the following disclaimer.
13df930be7Sderaadt * 2. Redistributions in binary form must reproduce the above copyright
14df930be7Sderaadt * notice, this list of conditions and the following disclaimer in the
15df930be7Sderaadt * documentation and/or other materials provided with the distribution.
16f75387cbSmillert * 3. Neither the name of the University nor the names of its contributors
17df930be7Sderaadt * may be used to endorse or promote products derived from this software
18df930be7Sderaadt * without specific prior written permission.
19df930be7Sderaadt *
20df930be7Sderaadt * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21df930be7Sderaadt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22df930be7Sderaadt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23df930be7Sderaadt * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24df930be7Sderaadt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25df930be7Sderaadt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26df930be7Sderaadt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27df930be7Sderaadt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28df930be7Sderaadt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29df930be7Sderaadt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30df930be7Sderaadt * SUCH DAMAGE.
31df930be7Sderaadt */
32df930be7Sderaadt
33df930be7Sderaadt #include "rcv.h"
34df930be7Sderaadt #include <sys/wait.h>
35df930be7Sderaadt #include "extern.h"
36df930be7Sderaadt
37df930be7Sderaadt /*
38df930be7Sderaadt * Mail -- a mail program
39df930be7Sderaadt *
40df930be7Sderaadt * More user commands.
41df930be7Sderaadt */
424a9caef2Smillert static int igcomp(const void *, const void *);
43df930be7Sderaadt
44df930be7Sderaadt /*
45df930be7Sderaadt * If any arguments were given, go to the next applicable argument
46df930be7Sderaadt * following dot, otherwise, go to the next applicable message.
47df930be7Sderaadt * If given as first command with no arguments, print first message.
48df930be7Sderaadt */
49df930be7Sderaadt int
next(void * v)504a9caef2Smillert next(void *v)
51df930be7Sderaadt {
5236999bedSmillert struct message *mp;
537eb34045Sderaadt int *msgvec = v;
5436999bedSmillert int *ip, *ip2, list[2], mdot;
55df930be7Sderaadt
56691235adSmiod if (*msgvec != 0) {
57df930be7Sderaadt /*
58df930be7Sderaadt * If some messages were supplied, find the
59df930be7Sderaadt * first applicable one following dot using
60df930be7Sderaadt * wrap around.
61df930be7Sderaadt */
62df930be7Sderaadt mdot = dot - &message[0] + 1;
63df930be7Sderaadt
64df930be7Sderaadt /*
65df930be7Sderaadt * Find the first message in the supplied
66df930be7Sderaadt * message list which follows dot.
67df930be7Sderaadt */
68691235adSmiod for (ip = msgvec; *ip != 0; ip++)
69df930be7Sderaadt if (*ip > mdot)
70df930be7Sderaadt break;
71ca9f5f35Savsm if (*ip == 0)
72df930be7Sderaadt ip = msgvec;
73df930be7Sderaadt ip2 = ip;
74df930be7Sderaadt do {
75df930be7Sderaadt mp = &message[*ip2 - 1];
76df930be7Sderaadt if ((mp->m_flag & MDELETED) == 0) {
77df930be7Sderaadt dot = mp;
78df930be7Sderaadt goto hitit;
79df930be7Sderaadt }
80691235adSmiod if (*ip2 != 0)
81df930be7Sderaadt ip2++;
82ca9f5f35Savsm if (*ip2 == 0)
83df930be7Sderaadt ip2 = msgvec;
84df930be7Sderaadt } while (ip2 != ip);
85db59c1a6Smillert puts("No messages applicable");
86df930be7Sderaadt return(1);
87df930be7Sderaadt }
88df930be7Sderaadt
89df930be7Sderaadt /*
90df930be7Sderaadt * If this is the first command, select message 1.
91df930be7Sderaadt * Note that this must exist for us to get here at all.
92df930be7Sderaadt */
93df930be7Sderaadt if (!sawcom)
94df930be7Sderaadt goto hitit;
95df930be7Sderaadt
96df930be7Sderaadt /*
97df930be7Sderaadt * Just find the next good message after dot, no
98df930be7Sderaadt * wraparound.
99df930be7Sderaadt */
100df930be7Sderaadt for (mp = dot+1; mp < &message[msgCount]; mp++)
101df930be7Sderaadt if ((mp->m_flag & (MDELETED|MSAVED)) == 0)
102df930be7Sderaadt break;
103df930be7Sderaadt if (mp >= &message[msgCount]) {
104db59c1a6Smillert puts("At EOF");
105df930be7Sderaadt return(0);
106df930be7Sderaadt }
107df930be7Sderaadt dot = mp;
108df930be7Sderaadt hitit:
109df930be7Sderaadt /*
110df930be7Sderaadt * Print dot.
111df930be7Sderaadt */
112df930be7Sderaadt list[0] = dot - &message[0] + 1;
113691235adSmiod list[1] = 0;
114df930be7Sderaadt return(type(list));
115df930be7Sderaadt }
116df930be7Sderaadt
117df930be7Sderaadt /*
118df930be7Sderaadt * Save a message in a file. Mark the message as saved
119df930be7Sderaadt * so we can discard when the user quits.
120df930be7Sderaadt */
121df930be7Sderaadt int
save(void * v)1224a9caef2Smillert save(void *v)
123df930be7Sderaadt {
1247eb34045Sderaadt char *str = v;
125df930be7Sderaadt
126db59c1a6Smillert return(save1(str, 1, "save", saveignore));
127df930be7Sderaadt }
128df930be7Sderaadt
129df930be7Sderaadt /*
130df930be7Sderaadt * Copy a message to a file without affected its saved-ness
131df930be7Sderaadt */
132df930be7Sderaadt int
copycmd(void * v)1334a9caef2Smillert copycmd(void *v)
134df930be7Sderaadt {
1357eb34045Sderaadt char *str = v;
136df930be7Sderaadt
137db59c1a6Smillert return(save1(str, 0, "copy", saveignore));
138df930be7Sderaadt }
139df930be7Sderaadt
140df930be7Sderaadt /*
141df930be7Sderaadt * Save/copy the indicated messages at the end of the passed file name.
142df930be7Sderaadt * If mark is true, mark the message "saved."
143df930be7Sderaadt */
144df930be7Sderaadt int
save1(char * str,int mark,char * cmd,struct ignoretab * ignore)1454a9caef2Smillert save1(char *str, int mark, char *cmd, struct ignoretab *ignore)
146df930be7Sderaadt {
14736999bedSmillert struct message *mp;
148df930be7Sderaadt char *file, *disp;
14936999bedSmillert int f, *msgvec, *ip;
150df930be7Sderaadt FILE *obuf;
151df930be7Sderaadt
152db59c1a6Smillert msgvec = (int *)salloc((msgCount + 2) * sizeof(*msgvec));
153c318c72bSmillert if ((file = snarf(str, &f)) == NULL)
154df930be7Sderaadt return(1);
155df930be7Sderaadt if (!f) {
156df930be7Sderaadt *msgvec = first(0, MMNORM);
157ca9f5f35Savsm if (*msgvec == 0) {
158df930be7Sderaadt printf("No messages to %s.\n", cmd);
159df930be7Sderaadt return(1);
160df930be7Sderaadt }
161691235adSmiod msgvec[1] = 0;
162df930be7Sderaadt }
163df930be7Sderaadt if (f && getmsglist(str, msgvec, 0) < 0)
164df930be7Sderaadt return(1);
165c318c72bSmillert if ((file = expand(file)) == NULL)
166df930be7Sderaadt return(1);
167df930be7Sderaadt printf("\"%s\" ", file);
168df930be7Sderaadt fflush(stdout);
16995719f9aSgsoares if (access(file, F_OK) >= 0)
170df930be7Sderaadt disp = "[Appended]";
171df930be7Sderaadt else
172df930be7Sderaadt disp = "[New file]";
173df930be7Sderaadt if ((obuf = Fopen(file, "a")) == NULL) {
174c318c72bSmillert warn(NULL);
175df930be7Sderaadt return(1);
176df930be7Sderaadt }
177df930be7Sderaadt for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
178df930be7Sderaadt mp = &message[*ip - 1];
179df930be7Sderaadt touch(mp);
18080d25fdaSmillert if (sendmessage(mp, obuf, ignore, NULL) < 0) {
18136999bedSmillert warn("%s", file);
182db59c1a6Smillert (void)Fclose(obuf);
183df930be7Sderaadt return(1);
184df930be7Sderaadt }
185df930be7Sderaadt if (mark)
186df930be7Sderaadt mp->m_flag |= MSAVED;
187df930be7Sderaadt }
188df930be7Sderaadt fflush(obuf);
189df930be7Sderaadt if (ferror(obuf))
19036999bedSmillert warn("%s", file);
191db59c1a6Smillert (void)Fclose(obuf);
192df930be7Sderaadt printf("%s\n", disp);
193df930be7Sderaadt return(0);
194df930be7Sderaadt }
195df930be7Sderaadt
196df930be7Sderaadt /*
197df930be7Sderaadt * Write the indicated messages at the end of the passed
198df930be7Sderaadt * file name, minus header and trailing blank line.
199df930be7Sderaadt */
200df930be7Sderaadt int
swrite(void * v)2014a9caef2Smillert swrite(void *v)
202df930be7Sderaadt {
2037eb34045Sderaadt char *str = v;
204df930be7Sderaadt
205db59c1a6Smillert return(save1(str, 1, "write", ignoreall));
206df930be7Sderaadt }
207df930be7Sderaadt
208df930be7Sderaadt /*
209df930be7Sderaadt * Snarf the file from the end of the command line and
210df930be7Sderaadt * return a pointer to it. If there is no file attached,
211c318c72bSmillert * just return NULL. Put a null in front of the file
212df930be7Sderaadt * name so that the message list processing won't see it,
213df930be7Sderaadt * unless the file name is the only thing on the line, in
214df930be7Sderaadt * which case, return 0 in the reference flag variable.
215df930be7Sderaadt */
216df930be7Sderaadt char *
snarf(char * linebuf,int * flag)2174a9caef2Smillert snarf(char *linebuf, int *flag)
218df930be7Sderaadt {
21936999bedSmillert char *cp;
220df930be7Sderaadt
221df930be7Sderaadt *flag = 1;
222df930be7Sderaadt cp = strlen(linebuf) + linebuf - 1;
223df930be7Sderaadt
224df930be7Sderaadt /*
225df930be7Sderaadt * Strip away trailing blanks.
226df930be7Sderaadt */
227085f113bSokan while (cp > linebuf && isspace((unsigned char)*cp))
228df930be7Sderaadt cp--;
229df930be7Sderaadt *++cp = 0;
230df930be7Sderaadt
231df930be7Sderaadt /*
232df930be7Sderaadt * Now search for the beginning of the file name.
233df930be7Sderaadt */
234085f113bSokan while (cp > linebuf && !isspace((unsigned char)*cp))
235df930be7Sderaadt cp--;
236df930be7Sderaadt if (*cp == '\0') {
237db59c1a6Smillert puts("No file specified.");
238c318c72bSmillert return(NULL);
239df930be7Sderaadt }
240085f113bSokan if (isspace((unsigned char)*cp))
241df930be7Sderaadt *cp++ = 0;
242df930be7Sderaadt else
243df930be7Sderaadt *flag = 0;
244df930be7Sderaadt return(cp);
245df930be7Sderaadt }
246df930be7Sderaadt
247df930be7Sderaadt /*
248df930be7Sderaadt * Delete messages.
249df930be7Sderaadt */
250df930be7Sderaadt int
deletecmd(void * v)251ecebcaffSderaadt deletecmd(void *v)
252df930be7Sderaadt {
2537eb34045Sderaadt int *msgvec = v;
2544a9caef2Smillert
255df930be7Sderaadt delm(msgvec);
256db59c1a6Smillert return(0);
257df930be7Sderaadt }
258df930be7Sderaadt
259df930be7Sderaadt /*
260df930be7Sderaadt * Delete messages, then type the new dot.
261df930be7Sderaadt */
262df930be7Sderaadt int
deltype(void * v)2634a9caef2Smillert deltype(void *v)
264df930be7Sderaadt {
2657eb34045Sderaadt int *msgvec = v;
266df930be7Sderaadt int list[2];
267df930be7Sderaadt int lastdot;
268df930be7Sderaadt
269df930be7Sderaadt lastdot = dot - &message[0] + 1;
270df930be7Sderaadt if (delm(msgvec) >= 0) {
271df930be7Sderaadt list[0] = dot - &message[0] + 1;
272df930be7Sderaadt if (list[0] > lastdot) {
273df930be7Sderaadt touch(dot);
274691235adSmiod list[1] = 0;
275df930be7Sderaadt return(type(list));
276df930be7Sderaadt }
277db59c1a6Smillert puts("At EOF");
278df930be7Sderaadt } else
279db59c1a6Smillert puts("No more messages");
280df930be7Sderaadt return(0);
281df930be7Sderaadt }
282df930be7Sderaadt
283df930be7Sderaadt /*
284df930be7Sderaadt * Delete the indicated messages.
285df930be7Sderaadt * Set dot to some nice place afterwards.
286df930be7Sderaadt * Internal interface.
287df930be7Sderaadt */
288df930be7Sderaadt int
delm(int * msgvec)2894a9caef2Smillert delm(int *msgvec)
290df930be7Sderaadt {
29136999bedSmillert struct message *mp;
29236999bedSmillert int *ip, last;
293df930be7Sderaadt
294691235adSmiod last = 0;
295691235adSmiod for (ip = msgvec; *ip != 0; ip++) {
296df930be7Sderaadt mp = &message[*ip - 1];
297df930be7Sderaadt touch(mp);
298df930be7Sderaadt mp->m_flag |= MDELETED|MTOUCH;
299df930be7Sderaadt mp->m_flag &= ~(MPRESERVE|MSAVED|MBOX);
300df930be7Sderaadt last = *ip;
301df930be7Sderaadt }
302691235adSmiod if (last != 0) {
303df930be7Sderaadt dot = &message[last-1];
304df930be7Sderaadt last = first(0, MDELETED);
305691235adSmiod if (last != 0) {
306df930be7Sderaadt dot = &message[last-1];
307df930be7Sderaadt return(0);
308df930be7Sderaadt }
309df930be7Sderaadt else {
310df930be7Sderaadt dot = &message[0];
311df930be7Sderaadt return(-1);
312df930be7Sderaadt }
313df930be7Sderaadt }
314df930be7Sderaadt
315df930be7Sderaadt /*
316acf82b0aSguenther * Following can't happen
317df930be7Sderaadt */
318df930be7Sderaadt return(-1);
319df930be7Sderaadt }
320df930be7Sderaadt
321df930be7Sderaadt /*
322df930be7Sderaadt * Undelete the indicated messages.
323df930be7Sderaadt */
324df930be7Sderaadt int
undeletecmd(void * v)3254a9caef2Smillert undeletecmd(void *v)
326df930be7Sderaadt {
3277eb34045Sderaadt int *msgvec = v;
32836999bedSmillert int *ip;
32936999bedSmillert struct message *mp;
330df930be7Sderaadt
331df930be7Sderaadt for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
332df930be7Sderaadt mp = &message[*ip - 1];
333df930be7Sderaadt touch(mp);
334df930be7Sderaadt dot = mp;
335df930be7Sderaadt mp->m_flag &= ~MDELETED;
336df930be7Sderaadt }
337db59c1a6Smillert return(0);
338df930be7Sderaadt }
339df930be7Sderaadt
340df930be7Sderaadt /*
341df930be7Sderaadt * Add the given header fields to the retained list.
342df930be7Sderaadt * If no arguments, print the current list of retained fields.
343df930be7Sderaadt */
344df930be7Sderaadt int
retfield(void * v)3454a9caef2Smillert retfield(void *v)
346df930be7Sderaadt {
3477eb34045Sderaadt char **list = v;
348df930be7Sderaadt
349db59c1a6Smillert return(ignore1(list, ignore + 1, "retained"));
350df930be7Sderaadt }
351df930be7Sderaadt
352df930be7Sderaadt /*
353df930be7Sderaadt * Add the given header fields to the ignored list.
354df930be7Sderaadt * If no arguments, print the current list of ignored fields.
355df930be7Sderaadt */
356df930be7Sderaadt int
igfield(void * v)3574a9caef2Smillert igfield(void *v)
358df930be7Sderaadt {
3597eb34045Sderaadt char **list = v;
360df930be7Sderaadt
361db59c1a6Smillert return(ignore1(list, ignore, "ignored"));
362df930be7Sderaadt }
363df930be7Sderaadt
364df930be7Sderaadt int
saveretfield(void * v)3654a9caef2Smillert saveretfield(void *v)
366df930be7Sderaadt {
3677eb34045Sderaadt char **list = v;
368df930be7Sderaadt
369db59c1a6Smillert return(ignore1(list, saveignore + 1, "retained"));
370df930be7Sderaadt }
371df930be7Sderaadt
372df930be7Sderaadt int
saveigfield(void * v)3734a9caef2Smillert saveigfield(void *v)
374df930be7Sderaadt {
3757eb34045Sderaadt char **list = v;
376df930be7Sderaadt
377db59c1a6Smillert return(ignore1(list, saveignore, "ignored"));
378df930be7Sderaadt }
379df930be7Sderaadt
380df930be7Sderaadt int
ignore1(char ** list,struct ignoretab * tab,char * which)3814a9caef2Smillert ignore1(char **list, struct ignoretab *tab, char *which)
382df930be7Sderaadt {
383db59c1a6Smillert char field[LINESIZE];
384df930be7Sderaadt char **ap;
38536999bedSmillert struct ignore *igp;
38636999bedSmillert int h;
387df930be7Sderaadt
388c318c72bSmillert if (*list == NULL)
389db59c1a6Smillert return(igshow(tab, which));
390df930be7Sderaadt for (ap = list; *ap != 0; ap++) {
3914a9caef2Smillert istrlcpy(field, *ap, sizeof(field));
392df930be7Sderaadt if (member(field, tab))
393df930be7Sderaadt continue;
394df930be7Sderaadt h = hash(field);
395*cd9ca5b7Smmcc igp = calloc(1, sizeof(struct ignore));
396d984dacaSmillert if (igp == NULL)
397*cd9ca5b7Smmcc err(1, "calloc");
398d984dacaSmillert igp->i_field = strdup(field);
399d984dacaSmillert if (igp->i_field == NULL)
400*cd9ca5b7Smmcc err(1, "strdup");
401df930be7Sderaadt igp->i_link = tab->i_head[h];
402df930be7Sderaadt tab->i_head[h] = igp;
403df930be7Sderaadt tab->i_count++;
404df930be7Sderaadt }
405db59c1a6Smillert return(0);
406df930be7Sderaadt }
407df930be7Sderaadt
408df930be7Sderaadt /*
409df930be7Sderaadt * Print out all currently retained fields.
410df930be7Sderaadt */
411df930be7Sderaadt int
igshow(struct ignoretab * tab,char * which)4124a9caef2Smillert igshow(struct ignoretab *tab, char *which)
413df930be7Sderaadt {
41436999bedSmillert int h;
415df930be7Sderaadt struct ignore *igp;
416df930be7Sderaadt char **ap, **ring;
417df930be7Sderaadt
418df930be7Sderaadt if (tab->i_count == 0) {
419df930be7Sderaadt printf("No fields currently being %s.\n", which);
420db59c1a6Smillert return(0);
421df930be7Sderaadt }
422df930be7Sderaadt ring = (char **)salloc((tab->i_count + 1) * sizeof(char *));
423df930be7Sderaadt ap = ring;
424df930be7Sderaadt for (h = 0; h < HSHSIZE; h++)
425df930be7Sderaadt for (igp = tab->i_head[h]; igp != 0; igp = igp->i_link)
426df930be7Sderaadt *ap++ = igp->i_field;
427df930be7Sderaadt *ap = 0;
428df930be7Sderaadt qsort(ring, tab->i_count, sizeof(char *), igcomp);
429df930be7Sderaadt for (ap = ring; *ap != 0; ap++)
430db59c1a6Smillert puts(*ap);
431db59c1a6Smillert return(0);
432df930be7Sderaadt }
433df930be7Sderaadt
434df930be7Sderaadt /*
435df930be7Sderaadt * Compare two names for sorting ignored field list.
436df930be7Sderaadt */
4377eb34045Sderaadt static int
igcomp(const void * l,const void * r)4384a9caef2Smillert igcomp(const void *l, const void *r)
439df930be7Sderaadt {
4404a9caef2Smillert
441df930be7Sderaadt return(strcmp(*(char **)l, *(char **)r));
442df930be7Sderaadt }
443