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