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