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