xref: /netbsd-src/usr.bin/mail/cmd3.c (revision ce63d6c20fc4ec8ddc95c84bb229e3c4ecf82b69)
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, 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 
34 #ifndef lint
35 static char sccsid[] = "@(#)cmd3.c	5.24 (Berkeley) 6/25/90";
36 #endif /* not lint */
37 
38 #include "rcv.h"
39 
40 /*
41  * Mail -- a mail program
42  *
43  * Still more user commands.
44  */
45 
46 /*
47  * Process a shell escape by saving signals, ignoring signals,
48  * and forking a sh -c
49  */
50 shell(str)
51 	char *str;
52 {
53 	sig_t sigint = signal(SIGINT, SIG_IGN);
54 	char *shell;
55 	char cmd[BUFSIZ];
56 
57 	(void) strcpy(cmd, str);
58 	if (bangexp(cmd) < 0)
59 		return 1;
60 	if ((shell = value("SHELL")) == NOSTR)
61 		shell = _PATH_CSHELL;
62 	(void) run_command(shell, 0, -1, -1, "-c", cmd, NOSTR);
63 	(void) signal(SIGINT, sigint);
64 	printf("!\n");
65 	return 0;
66 }
67 
68 /*
69  * Fork an interactive shell.
70  */
71 /*ARGSUSED*/
72 dosh(str)
73 	char *str;
74 {
75 	sig_t sigint = signal(SIGINT, SIG_IGN);
76 	char *shell;
77 
78 	if ((shell = value("SHELL")) == NOSTR)
79 		shell = _PATH_CSHELL;
80 	(void) run_command(shell, 0, -1, -1, NOSTR);
81 	(void) signal(SIGINT, sigint);
82 	putchar('\n');
83 	return 0;
84 }
85 
86 /*
87  * Expand the shell escape by expanding unescaped !'s into the
88  * last issued command where possible.
89  */
90 
91 char	lastbang[128];
92 
93 bangexp(str)
94 	char *str;
95 {
96 	char bangbuf[BUFSIZ];
97 	register char *cp, *cp2;
98 	register int n;
99 	int changed = 0;
100 
101 	cp = str;
102 	cp2 = bangbuf;
103 	n = BUFSIZ;
104 	while (*cp) {
105 		if (*cp == '!') {
106 			if (n < strlen(lastbang)) {
107 overf:
108 				printf("Command buffer overflow\n");
109 				return(-1);
110 			}
111 			changed++;
112 			strcpy(cp2, lastbang);
113 			cp2 += strlen(lastbang);
114 			n -= strlen(lastbang);
115 			cp++;
116 			continue;
117 		}
118 		if (*cp == '\\' && cp[1] == '!') {
119 			if (--n <= 1)
120 				goto overf;
121 			*cp2++ = '!';
122 			cp += 2;
123 			changed++;
124 		}
125 		if (--n <= 1)
126 			goto overf;
127 		*cp2++ = *cp++;
128 	}
129 	*cp2 = 0;
130 	if (changed) {
131 		printf("!%s\n", bangbuf);
132 		fflush(stdout);
133 	}
134 	strcpy(str, bangbuf);
135 	strncpy(lastbang, bangbuf, 128);
136 	lastbang[127] = 0;
137 	return(0);
138 }
139 
140 /*
141  * Print out a nice help message from some file or another.
142  */
143 
144 help()
145 {
146 	register c;
147 	register FILE *f;
148 
149 	if ((f = Fopen(_PATH_HELP, "r")) == NULL) {
150 		perror(_PATH_HELP);
151 		return(1);
152 	}
153 	while ((c = getc(f)) != EOF)
154 		putchar(c);
155 	Fclose(f);
156 	return(0);
157 }
158 
159 /*
160  * Change user's working directory.
161  */
162 schdir(arglist)
163 	char **arglist;
164 {
165 	char *cp;
166 
167 	if (*arglist == NOSTR)
168 		cp = homedir;
169 	else
170 		if ((cp = expand(*arglist)) == NOSTR)
171 			return(1);
172 	if (chdir(cp) < 0) {
173 		perror(cp);
174 		return(1);
175 	}
176 	return 0;
177 }
178 
179 respond(msgvec)
180 	int *msgvec;
181 {
182 	if (value("Replyall") == NOSTR)
183 		return (_respond(msgvec));
184 	else
185 		return (_Respond(msgvec));
186 }
187 
188 /*
189  * Reply to a list of messages.  Extract each name from the
190  * message header and send them off to mail1()
191  */
192 _respond(msgvec)
193 	int *msgvec;
194 {
195 	struct message *mp;
196 	char *cp, *rcv, *replyto;
197 	char **ap;
198 	struct name *np;
199 	struct header head;
200 
201 	if (msgvec[1] != 0) {
202 		printf("Sorry, can't reply to multiple messages at once\n");
203 		return(1);
204 	}
205 	mp = &message[msgvec[0] - 1];
206 	touch(mp);
207 	dot = mp;
208 	if ((rcv = skin(hfield("from", mp))) == NOSTR)
209 		rcv = skin(nameof(mp, 1));
210 	if ((replyto = skin(hfield("reply-to", mp))) != NOSTR)
211 		np = extract(replyto, GTO);
212 	else if ((cp = skin(hfield("to", mp))) != NOSTR)
213 		np = extract(cp, GTO);
214 	else
215 		np = NIL;
216 	np = elide(np);
217 	/*
218 	 * Delete my name from the reply list,
219 	 * and with it, all my alternate names.
220 	 */
221 	np = delname(np, myname);
222 	if (altnames)
223 		for (ap = altnames; *ap; ap++)
224 			np = delname(np, *ap);
225 	if (np != NIL && replyto == NOSTR)
226 		np = cat(np, extract(rcv, GTO));
227 	else if (np == NIL) {
228 		if (replyto != NOSTR)
229 			printf("Empty reply-to field -- replying to author\n");
230 		np = extract(rcv, GTO);
231 	}
232 	head.h_to = np;
233 	if ((head.h_subject = hfield("subject", mp)) == NOSTR)
234 		head.h_subject = hfield("subj", mp);
235 	head.h_subject = reedit(head.h_subject);
236 	if (replyto == NOSTR && (cp = skin(hfield("cc", mp))) != NOSTR) {
237 		np = elide(extract(cp, GCC));
238 		np = delname(np, myname);
239 		if (altnames != 0)
240 			for (ap = altnames; *ap; ap++)
241 				np = delname(np, *ap);
242 		head.h_cc = np;
243 	} else
244 		head.h_cc = NIL;
245 	head.h_bcc = NIL;
246 	head.h_smopts = NIL;
247 	mail1(&head, 1);
248 	return(0);
249 }
250 
251 /*
252  * Modify the subject we are replying to to begin with Re: if
253  * it does not already.
254  */
255 char *
256 reedit(subj)
257 	register char *subj;
258 {
259 	char *newsubj;
260 
261 	if (subj == NOSTR)
262 		return NOSTR;
263 	if ((subj[0] == 'r' || subj[0] == 'R') &&
264 	    (subj[1] == 'e' || subj[1] == 'E') &&
265 	    subj[2] == ':')
266 		return subj;
267 	newsubj = salloc(strlen(subj) + 5);
268 	strcpy(newsubj, "Re: ");
269 	strcpy(newsubj + 4, subj);
270 	return newsubj;
271 }
272 
273 /*
274  * Preserve the named messages, so that they will be sent
275  * back to the system mailbox.
276  */
277 
278 preserve(msgvec)
279 	int *msgvec;
280 {
281 	register struct message *mp;
282 	register int *ip, mesg;
283 
284 	if (edit) {
285 		printf("Cannot \"preserve\" in edit mode\n");
286 		return(1);
287 	}
288 	for (ip = msgvec; *ip != NULL; ip++) {
289 		mesg = *ip;
290 		mp = &message[mesg-1];
291 		mp->m_flag |= MPRESERVE;
292 		mp->m_flag &= ~MBOX;
293 		dot = mp;
294 	}
295 	return(0);
296 }
297 
298 /*
299  * Mark all given messages as unread.
300  */
301 unread(msgvec)
302 	int	msgvec[];
303 {
304 	register int *ip;
305 
306 	for (ip = msgvec; *ip != NULL; ip++) {
307 		dot = &message[*ip-1];
308 		dot->m_flag &= ~(MREAD|MTOUCH);
309 		dot->m_flag |= MSTATUS;
310 	}
311 	return(0);
312 }
313 
314 /*
315  * Print the size of each message.
316  */
317 
318 messize(msgvec)
319 	int *msgvec;
320 {
321 	register struct message *mp;
322 	register int *ip, mesg;
323 
324 	for (ip = msgvec; *ip != NULL; ip++) {
325 		mesg = *ip;
326 		mp = &message[mesg-1];
327 		printf("%d: %d/%ld\n", mesg, mp->m_lines, mp->m_size);
328 	}
329 	return(0);
330 }
331 
332 /*
333  * Quit quickly.  If we are sourcing, just pop the input level
334  * by returning an error.
335  */
336 
337 rexit(e)
338 {
339 	if (sourcing)
340 		return(1);
341 	exit(e);
342 	/*NOTREACHED*/
343 }
344 
345 /*
346  * Set or display a variable value.  Syntax is similar to that
347  * of csh.
348  */
349 
350 set(arglist)
351 	char **arglist;
352 {
353 	register struct var *vp;
354 	register char *cp, *cp2;
355 	char varbuf[BUFSIZ], **ap, **p;
356 	int errs, h, s;
357 
358 	if (*arglist == NOSTR) {
359 		for (h = 0, s = 1; h < HSHSIZE; h++)
360 			for (vp = variables[h]; vp != NOVAR; vp = vp->v_link)
361 				s++;
362 		ap = (char **) salloc(s * sizeof *ap);
363 		for (h = 0, p = ap; h < HSHSIZE; h++)
364 			for (vp = variables[h]; vp != NOVAR; vp = vp->v_link)
365 				*p++ = vp->v_name;
366 		*p = NOSTR;
367 		sort(ap);
368 		for (p = ap; *p != NOSTR; p++)
369 			printf("%s\t%s\n", *p, value(*p));
370 		return(0);
371 	}
372 	errs = 0;
373 	for (ap = arglist; *ap != NOSTR; ap++) {
374 		cp = *ap;
375 		cp2 = varbuf;
376 		while (*cp != '=' && *cp != '\0')
377 			*cp2++ = *cp++;
378 		*cp2 = '\0';
379 		if (*cp == '\0')
380 			cp = "";
381 		else
382 			cp++;
383 		if (equal(varbuf, "")) {
384 			printf("Non-null variable name required\n");
385 			errs++;
386 			continue;
387 		}
388 		assign(varbuf, cp);
389 	}
390 	return(errs);
391 }
392 
393 /*
394  * Unset a bunch of variable values.
395  */
396 
397 unset(arglist)
398 	char **arglist;
399 {
400 	register struct var *vp, *vp2;
401 	int errs, h;
402 	char **ap;
403 
404 	errs = 0;
405 	for (ap = arglist; *ap != NOSTR; ap++) {
406 		if ((vp2 = lookup(*ap)) == NOVAR) {
407 			if (!sourcing) {
408 				printf("\"%s\": undefined variable\n", *ap);
409 				errs++;
410 			}
411 			continue;
412 		}
413 		h = hash(*ap);
414 		if (vp2 == variables[h]) {
415 			variables[h] = variables[h]->v_link;
416 			vfree(vp2->v_name);
417 			vfree(vp2->v_value);
418 			cfree((char *)vp2);
419 			continue;
420 		}
421 		for (vp = variables[h]; vp->v_link != vp2; vp = vp->v_link)
422 			;
423 		vp->v_link = vp2->v_link;
424 		vfree(vp2->v_name);
425 		vfree(vp2->v_value);
426 		cfree((char *) vp2);
427 	}
428 	return(errs);
429 }
430 
431 /*
432  * Put add users to a group.
433  */
434 
435 group(argv)
436 	char **argv;
437 {
438 	register struct grouphead *gh;
439 	register struct group *gp;
440 	register int h;
441 	int s;
442 	char **ap, *gname, **p;
443 
444 	if (*argv == NOSTR) {
445 		for (h = 0, s = 1; h < HSHSIZE; h++)
446 			for (gh = groups[h]; gh != NOGRP; gh = gh->g_link)
447 				s++;
448 		ap = (char **) salloc(s * sizeof *ap);
449 		for (h = 0, p = ap; h < HSHSIZE; h++)
450 			for (gh = groups[h]; gh != NOGRP; gh = gh->g_link)
451 				*p++ = gh->g_name;
452 		*p = NOSTR;
453 		sort(ap);
454 		for (p = ap; *p != NOSTR; p++)
455 			printgroup(*p);
456 		return(0);
457 	}
458 	if (argv[1] == NOSTR) {
459 		printgroup(*argv);
460 		return(0);
461 	}
462 	gname = *argv;
463 	h = hash(gname);
464 	if ((gh = findgroup(gname)) == NOGRP) {
465 		gh = (struct grouphead *) calloc(sizeof *gh, 1);
466 		gh->g_name = vcopy(gname);
467 		gh->g_list = NOGE;
468 		gh->g_link = groups[h];
469 		groups[h] = gh;
470 	}
471 
472 	/*
473 	 * Insert names from the command list into the group.
474 	 * Who cares if there are duplicates?  They get tossed
475 	 * later anyway.
476 	 */
477 
478 	for (ap = argv+1; *ap != NOSTR; ap++) {
479 		gp = (struct group *) calloc(sizeof *gp, 1);
480 		gp->ge_name = vcopy(*ap);
481 		gp->ge_link = gh->g_list;
482 		gh->g_list = gp;
483 	}
484 	return(0);
485 }
486 
487 /*
488  * Sort the passed string vecotor into ascending dictionary
489  * order.
490  */
491 
492 sort(list)
493 	char **list;
494 {
495 	register char **ap;
496 	int diction();
497 
498 	for (ap = list; *ap != NOSTR; ap++)
499 		;
500 	if (ap-list < 2)
501 		return;
502 	qsort((char *)list, ap-list, sizeof *list, diction);
503 }
504 
505 /*
506  * Do a dictionary order comparison of the arguments from
507  * qsort.
508  */
509 
510 diction(a, b)
511 	register char **a, **b;
512 {
513 	return(strcmp(*a, *b));
514 }
515 
516 /*
517  * The do nothing command for comments.
518  */
519 
520 /*ARGSUSED*/
521 null(e)
522 {
523 	return 0;
524 }
525 
526 /*
527  * Change to another file.  With no argument, print information about
528  * the current file.
529  */
530 file(argv)
531 	register char **argv;
532 {
533 
534 	if (argv[0] == NOSTR) {
535 		newfileinfo();
536 		return 0;
537 	}
538 	if (setfile(*argv) < 0)
539 		return 1;
540 	announce();
541 	return 0;
542 }
543 
544 /*
545  * Expand file names like echo
546  */
547 echo(argv)
548 	char **argv;
549 {
550 	register char **ap;
551 	register char *cp;
552 
553 	for (ap = argv; *ap != NOSTR; ap++) {
554 		cp = *ap;
555 		if ((cp = expand(cp)) != NOSTR) {
556 			if (ap != argv)
557 				putchar(' ');
558 			printf("%s", cp);
559 		}
560 	}
561 	putchar('\n');
562 	return 0;
563 }
564 
565 Respond(msgvec)
566 	int *msgvec;
567 {
568 	if (value("Replyall") == NOSTR)
569 		return (_Respond(msgvec));
570 	else
571 		return (_respond(msgvec));
572 }
573 
574 /*
575  * Reply to a series of messages by simply mailing to the senders
576  * and not messing around with the To: and Cc: lists as in normal
577  * reply.
578  */
579 _Respond(msgvec)
580 	int msgvec[];
581 {
582 	struct header head;
583 	struct message *mp;
584 	register int *ap;
585 	register char *cp;
586 
587 	head.h_to = NIL;
588 	for (ap = msgvec; *ap != 0; ap++) {
589 		mp = &message[*ap - 1];
590 		touch(mp);
591 		dot = mp;
592 		if ((cp = skin(hfield("from", mp))) == NOSTR)
593 			cp = skin(nameof(mp, 2));
594 		head.h_to = cat(head.h_to, extract(cp, GTO));
595 	}
596 	if (head.h_to == NIL)
597 		return 0;
598 	mp = &message[msgvec[0] - 1];
599 	if ((head.h_subject = hfield("subject", mp)) == NOSTR)
600 		head.h_subject = hfield("subj", mp);
601 	head.h_subject = reedit(head.h_subject);
602 	head.h_cc = NIL;
603 	head.h_bcc = NIL;
604 	head.h_smopts = NIL;
605 	mail1(&head, 1);
606 	return 0;
607 }
608 
609 /*
610  * Conditional commands.  These allow one to parameterize one's
611  * .mailrc and do some things if sending, others if receiving.
612  */
613 
614 ifcmd(argv)
615 	char **argv;
616 {
617 	register char *cp;
618 
619 	if (cond != CANY) {
620 		printf("Illegal nested \"if\"\n");
621 		return(1);
622 	}
623 	cond = CANY;
624 	cp = argv[0];
625 	switch (*cp) {
626 	case 'r': case 'R':
627 		cond = CRCV;
628 		break;
629 
630 	case 's': case 'S':
631 		cond = CSEND;
632 		break;
633 
634 	default:
635 		printf("Unrecognized if-keyword: \"%s\"\n", cp);
636 		return(1);
637 	}
638 	return(0);
639 }
640 
641 /*
642  * Implement 'else'.  This is pretty simple -- we just
643  * flip over the conditional flag.
644  */
645 
646 elsecmd()
647 {
648 
649 	switch (cond) {
650 	case CANY:
651 		printf("\"Else\" without matching \"if\"\n");
652 		return(1);
653 
654 	case CSEND:
655 		cond = CRCV;
656 		break;
657 
658 	case CRCV:
659 		cond = CSEND;
660 		break;
661 
662 	default:
663 		printf("Mail's idea of conditions is screwed up\n");
664 		cond = CANY;
665 		break;
666 	}
667 	return(0);
668 }
669 
670 /*
671  * End of if statement.  Just set cond back to anything.
672  */
673 
674 endifcmd()
675 {
676 
677 	if (cond == CANY) {
678 		printf("\"Endif\" without matching \"if\"\n");
679 		return(1);
680 	}
681 	cond = CANY;
682 	return(0);
683 }
684 
685 /*
686  * Set the list of alternate names.
687  */
688 alternates(namelist)
689 	char **namelist;
690 {
691 	register int c;
692 	register char **ap, **ap2, *cp;
693 
694 	c = argcount(namelist) + 1;
695 	if (c == 1) {
696 		if (altnames == 0)
697 			return(0);
698 		for (ap = altnames; *ap; ap++)
699 			printf("%s ", *ap);
700 		printf("\n");
701 		return(0);
702 	}
703 	if (altnames != 0)
704 		cfree((char *) altnames);
705 	altnames = (char **) calloc((unsigned) c, sizeof (char *));
706 	for (ap = namelist, ap2 = altnames; *ap; ap++, ap2++) {
707 		cp = (char *) calloc((unsigned) strlen(*ap) + 1, sizeof (char));
708 		strcpy(cp, *ap);
709 		*ap2 = cp;
710 	}
711 	*ap2 = 0;
712 	return(0);
713 }
714