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