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