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
shell(str)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
dosh(str)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
bangexp(str)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
help()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
schdir(arglist)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
respond(msgvec)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
_respond(msgvec)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 *
reedit(subj)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
preserve(msgvec)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
unread(msgvec)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
messize(msgvec)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
rexit(e)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
set(arglist)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
unset(arglist)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
group(argv)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
sort(list)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
diction(a,b)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
null(e)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
file(argv)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
echo(argv)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
Respond(msgvec)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
_Respond(msgvec)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
ifcmd(argv)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
elsecmd()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
endifcmd()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
alternates(namelist)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