Start development on 386BSD 0.0
[unix-history] / .ref-BSD-4_3_Net_2 / usr / src / usr.bin / mail / cmd3.c
CommitLineData
d0aeaf5a
DF
1/*
2 * Copyright (c) 1980 Regents of the University of California.
0c5f72fb
KB
3 * All rights reserved.
4 *
af359dea
C
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.
d0aeaf5a
DF
32 */
33
acfc7e9b 34#ifndef lint
1c15e888 35static char sccsid[] = "@(#)cmd3.c 5.24 (Berkeley) 6/25/90";
acfc7e9b 36#endif /* not lint */
ca6bb034
KS
37
38#include "rcv.h"
ca6bb034
KS
39
40/*
41 * Mail -- a mail program
42 *
43 * Still more user commands.
44 */
45
ca6bb034
KS
46/*
47 * Process a shell escape by saving signals, ignoring signals,
48 * and forking a sh -c
49 */
ca6bb034
KS
50shell(str)
51 char *str;
52{
e62a1467 53 sig_t sigint = signal(SIGINT, SIG_IGN);
d33aa50d 54 char *shell;
e8adee6e 55 char cmd[BUFSIZ];
ca6bb034 56
d33aa50d 57 (void) strcpy(cmd, str);
e8adee6e 58 if (bangexp(cmd) < 0)
2ee3bce2 59 return 1;
d33aa50d 60 if ((shell = value("SHELL")) == NOSTR)
435e8dff 61 shell = _PATH_CSHELL;
d33aa50d
EW
62 (void) run_command(shell, 0, -1, -1, "-c", cmd, NOSTR);
63 (void) signal(SIGINT, sigint);
ca6bb034 64 printf("!\n");
d33aa50d 65 return 0;
ca6bb034
KS
66}
67
68/*
69 * Fork an interactive shell.
70 */
828615a1 71/*ARGSUSED*/
ca6bb034
KS
72dosh(str)
73 char *str;
74{
e62a1467 75 sig_t sigint = signal(SIGINT, SIG_IGN);
d33aa50d 76 char *shell;
828615a1 77
d33aa50d 78 if ((shell = value("SHELL")) == NOSTR)
435e8dff 79 shell = _PATH_CSHELL;
d33aa50d
EW
80 (void) run_command(shell, 0, -1, -1, NOSTR);
81 (void) signal(SIGINT, sigint);
ca6bb034 82 putchar('\n');
d33aa50d 83 return 0;
ca6bb034
KS
84}
85
e8adee6e
KS
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
ca6bb034
KS
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
07b0d286 149 if ((f = Fopen(_PATH_HELP, "r")) == NULL) {
435e8dff 150 perror(_PATH_HELP);
ca6bb034
KS
151 return(1);
152 }
153 while ((c = getc(f)) != EOF)
154 putchar(c);
07b0d286 155 Fclose(f);
ca6bb034
KS
156 return(0);
157}
158
159/*
160 * Change user's working directory.
161 */
23464f86
EW
162schdir(arglist)
163 char **arglist;
ca6bb034 164{
23464f86 165 char *cp;
ca6bb034 166
23464f86 167 if (*arglist == NOSTR)
ca6bb034
KS
168 cp = homedir;
169 else
23464f86 170 if ((cp = expand(*arglist)) == NOSTR)
ca6bb034
KS
171 return(1);
172 if (chdir(cp) < 0) {
173 perror(cp);
174 return(1);
175 }
23464f86 176 return 0;
ca6bb034
KS
177}
178
397029be
S
179respond(msgvec)
180 int *msgvec;
181{
182 if (value("Replyall") == NOSTR)
183 return (_respond(msgvec));
184 else
185 return (_Respond(msgvec));
186}
187
ca6bb034
KS
188/*
189 * Reply to a list of messages. Extract each name from the
190 * message header and send them off to mail1()
191 */
397029be 192_respond(msgvec)
ca6bb034
KS
193 int *msgvec;
194{
195 struct message *mp;
828615a1 196 char *cp, *rcv, *replyto;
3d6f01e5 197 char **ap;
ca6bb034
KS
198 struct name *np;
199 struct header head;
ca6bb034
KS
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];
8225e7c3 206 touch(mp);
ca6bb034 207 dot = mp;
3d6f01e5
EW
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);
1c04ff22
KS
217 /*
218 * Delete my name from the reply list,
219 * and with it, all my alternate names.
220 */
470c33f3 221 np = delname(np, myname);
7949d400 222 if (altnames)
1c04ff22 223 for (ap = altnames; *ap; ap++)
470c33f3 224 np = delname(np, *ap);
3d6f01e5
EW
225 if (np != NIL && replyto == NOSTR)
226 np = cat(np, extract(rcv, GTO));
227 else if (np == NIL) {
228 if (replyto != NOSTR)
52984be9 229 printf("Empty reply-to field -- replying to author\n");
3d6f01e5 230 np = extract(rcv, GTO);
52984be9 231 }
3d6f01e5
EW
232 head.h_to = np;
233 if ((head.h_subject = hfield("subject", mp)) == NOSTR)
ca6bb034 234 head.h_subject = hfield("subj", mp);
fc8872bb 235 head.h_subject = reedit(head.h_subject);
3d6f01e5
EW
236 if (replyto == NOSTR && (cp = skin(hfield("cc", mp))) != NOSTR) {
237 np = elide(extract(cp, GCC));
470c33f3 238 np = delname(np, myname);
3d6f01e5
EW
239 if (altnames != 0)
240 for (ap = altnames; *ap; ap++)
470c33f3 241 np = delname(np, *ap);
3d6f01e5
EW
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);
ca6bb034
KS
248 return(0);
249}
250
fc8872bb
KS
251/*
252 * Modify the subject we are replying to to begin with Re: if
253 * it does not already.
254 */
fc8872bb
KS
255char *
256reedit(subj)
3d6f01e5 257 register char *subj;
fc8872bb 258{
3d6f01e5 259 char *newsubj;
fc8872bb
KS
260
261 if (subj == NOSTR)
3d6f01e5
EW
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: ");
c6bb00f3 269 strcpy(newsubj + 4, subj);
3d6f01e5 270 return newsubj;
fc8872bb
KS
271}
272
ca6bb034
KS
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;
8fe58892 292 mp->m_flag &= ~MBOX;
ca6bb034
KS
293 dot = mp;
294 }
295 return(0);
296}
297
397029be
S
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
ca6bb034
KS
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];
397029be 327 printf("%d: %d/%ld\n", mesg, mp->m_lines, mp->m_size);
ca6bb034
KS
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);
828615a1 342 /*NOTREACHED*/
ca6bb034
KS
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
470c33f3 358 if (*arglist == NOSTR) {
ca6bb034
KS
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;
ca6bb034
KS
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);
828615a1 418 cfree((char *)vp2);
ca6bb034
KS
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);
828615a1 426 cfree((char *) vp2);
ca6bb034
KS
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
470c33f3 444 if (*argv == NOSTR) {
ca6bb034
KS
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 }
470c33f3 458 if (argv[1] == NOSTR) {
ca6bb034
KS
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;
828615a1 502 qsort((char *)list, ap-list, sizeof *list, diction);
ca6bb034
KS
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
828615a1 520/*ARGSUSED*/
ca6bb034
KS
521null(e)
522{
828615a1 523 return 0;
ca6bb034
KS
524}
525
526/*
2ee3bce2
EW
527 * Change to another file. With no argument, print information about
528 * the current file.
ca6bb034 529 */
1031a04a 530file(argv)
2a0f6531 531 register char **argv;
ca6bb034 532{
ca6bb034 533
1031a04a 534 if (argv[0] == NOSTR) {
890194fb 535 newfileinfo();
2a0f6531 536 return 0;
1031a04a 537 }
8421b6a6 538 if (setfile(*argv) < 0)
2ee3bce2 539 return 1;
4d20fb09 540 announce();
828615a1 541 return 0;
1031a04a
KS
542}
543
ca6bb034
KS
544/*
545 * Expand file names like echo
546 */
ca6bb034
KS
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;
790344bc
EW
555 if ((cp = expand(cp)) != NOSTR) {
556 if (ap != argv)
557 putchar(' ');
558 printf("%s", cp);
559 }
ca6bb034 560 }
790344bc 561 putchar('\n');
3d6f01e5 562 return 0;
ca6bb034
KS
563}
564
397029be
S
565Respond(msgvec)
566 int *msgvec;
567{
568 if (value("Replyall") == NOSTR)
569 return (_Respond(msgvec));
570 else
571 return (_respond(msgvec));
572}
573
ca6bb034
KS
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 */
397029be 579_Respond(msgvec)
ca6bb034
KS
580 int msgvec[];
581{
582 struct header head;
583 struct message *mp;
3d6f01e5
EW
584 register int *ap;
585 register char *cp;
ca6bb034 586
3d6f01e5 587 head.h_to = NIL;
ca6bb034
KS
588 for (ap = msgvec; *ap != 0; ap++) {
589 mp = &message[*ap - 1];
8225e7c3 590 touch(mp);
3d6f01e5
EW
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));
ca6bb034 595 }
3d6f01e5
EW
596 if (head.h_to == NIL)
597 return 0;
ca6bb034 598 mp = &message[msgvec[0] - 1];
3d6f01e5
EW
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;
ca6bb034 607}
d344b00c
KS
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}
1c04ff22
KS
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);
828615a1 705 altnames = (char **) calloc((unsigned) c, sizeof (char *));
1c04ff22 706 for (ap = namelist, ap2 = altnames; *ap; ap++, ap2++) {
828615a1 707 cp = (char *) calloc((unsigned) strlen(*ap) + 1, sizeof (char));
1c04ff22
KS
708 strcpy(cp, *ap);
709 *ap2 = cp;
710 }
711 *ap2 = 0;
712 return(0);
713}