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