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