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