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