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