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