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