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