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