]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - usr.bin/mail/cmd3.c
MFC r368207,368607:
[FreeBSD/stable/10.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  * 4. 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, NULL, 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 < 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(char **arglist)
167 {
168         char *cp;
169
170         if (*arglist == NULL) {
171                 if (homedir == NULL)
172                         return (1);
173                 cp = homedir;
174         } else
175                 if ((cp = expand(*arglist)) == NULL)
176                         return (1);
177         if (chdir(cp) < 0) {
178                 warn("%s", cp);
179                 return (1);
180         }
181         return (0);
182 }
183
184 int
185 respond(int *msgvec)
186 {
187         if (value("Replyall") == NULL && value("flipr") == NULL)
188                 return (dorespond(msgvec));
189         else
190                 return (doRespond(msgvec));
191 }
192
193 /*
194  * Reply to a list of messages.  Extract each name from the
195  * message header and send them off to mail1()
196  */
197 int
198 dorespond(int *msgvec)
199 {
200         struct message *mp;
201         char *cp, *rcv, *replyto;
202         char **ap;
203         struct name *np;
204         struct header head;
205
206         if (msgvec[1] != 0) {
207                 printf("Sorry, can't reply to multiple messages at once\n");
208                 return (1);
209         }
210         mp = &message[msgvec[0] - 1];
211         touch(mp);
212         dot = mp;
213         if ((rcv = skin(hfield("from", mp))) == NULL)
214                 rcv = skin(nameof(mp, 1));
215         if ((replyto = skin(hfield("reply-to", mp))) != NULL)
216                 np = extract(replyto, GTO);
217         else if ((cp = skin(hfield("to", mp))) != NULL)
218                 np = extract(cp, GTO);
219         else
220                 np = NULL;
221         np = elide(np);
222         /*
223          * Delete my name from the reply list,
224          * and with it, all my alternate names.
225          */
226         np = delname(np, myname);
227         if (altnames)
228                 for (ap = altnames; *ap != NULL; ap++)
229                         np = delname(np, *ap);
230         if (np != NULL && replyto == NULL)
231                 np = cat(np, extract(rcv, GTO));
232         else if (np == NULL) {
233                 if (replyto != NULL)
234                         printf("Empty reply-to field -- replying to author\n");
235                 np = extract(rcv, GTO);
236         }
237         head.h_to = np;
238         if ((head.h_subject = hfield("subject", mp)) == NULL)
239                 head.h_subject = hfield("subj", mp);
240         head.h_subject = reedit(head.h_subject);
241         if (replyto == NULL && (cp = skin(hfield("cc", mp))) != NULL) {
242                 np = elide(extract(cp, GCC));
243                 np = delname(np, myname);
244                 if (altnames != 0)
245                         for (ap = altnames; *ap != NULL; ap++)
246                                 np = delname(np, *ap);
247                 head.h_cc = np;
248         } else
249                 head.h_cc = NULL;
250         head.h_bcc = NULL;
251         head.h_smopts = NULL;
252         head.h_replyto = value("REPLYTO");
253         head.h_inreplyto = skin(hfield("message-id", mp));
254         mail1(&head, 1);
255         return (0);
256 }
257
258 /*
259  * Modify the subject we are replying to to begin with Re: if
260  * it does not already.
261  */
262 char *
263 reedit(char *subj)
264 {
265         char *newsubj;
266
267         if (subj == NULL)
268                 return (NULL);
269         if ((subj[0] == 'r' || subj[0] == 'R') &&
270             (subj[1] == 'e' || subj[1] == 'E') &&
271             subj[2] == ':')
272                 return (subj);
273         newsubj = salloc(strlen(subj) + 5);
274         sprintf(newsubj, "Re: %s", subj);
275         return (newsubj);
276 }
277
278 /*
279  * Preserve the named messages, so that they will be sent
280  * back to the system mailbox.
281  */
282 int
283 preserve(int *msgvec)
284 {
285         int *ip, mesg;
286         struct message *mp;
287
288         if (edit) {
289                 printf("Cannot \"preserve\" in edit mode\n");
290                 return (1);
291         }
292         for (ip = msgvec; *ip != 0; ip++) {
293                 mesg = *ip;
294                 mp = &message[mesg-1];
295                 mp->m_flag |= MPRESERVE;
296                 mp->m_flag &= ~MBOX;
297                 dot = mp;
298         }
299         return (0);
300 }
301
302 /*
303  * Mark all given messages as unread.
304  */
305 int
306 unread(int msgvec[])
307 {
308         int *ip;
309
310         for (ip = msgvec; *ip != 0; ip++) {
311                 dot = &message[*ip-1];
312                 dot->m_flag &= ~(MREAD|MTOUCH);
313                 dot->m_flag |= MSTATUS;
314         }
315         return (0);
316 }
317
318 /*
319  * Print the size of each message.
320  */
321 int
322 messize(int *msgvec)
323 {
324         struct message *mp;
325         int *ip, mesg;
326
327         for (ip = msgvec; *ip != 0; ip++) {
328                 mesg = *ip;
329                 mp = &message[mesg-1];
330                 printf("%d: %ld/%ld\n", mesg, mp->m_lines, mp->m_size);
331         }
332         return (0);
333 }
334
335 /*
336  * Quit quickly.  If we are sourcing, just pop the input level
337  * by returning an error.
338  */
339 int
340 rexit(int e __unused)
341 {
342         if (sourcing)
343                 return (1);
344         exit(0);
345         /*NOTREACHED*/
346 }
347
348 /*
349  * Set or display a variable value.  Syntax is similar to that
350  * of csh.
351  */
352 int
353 set(char **arglist)
354 {
355         struct var *vp;
356         char *cp, *cp2;
357         char varbuf[BUFSIZ], **ap, **p;
358         int errs, h, s;
359
360         if (*arglist == NULL) {
361                 for (h = 0, s = 1; h < HSHSIZE; h++)
362                         for (vp = variables[h]; vp != NULL; vp = vp->v_link)
363                                 s++;
364                 ap = (char **)salloc(s * sizeof(*ap));
365                 for (h = 0, p = ap; h < HSHSIZE; h++)
366                         for (vp = variables[h]; vp != NULL; vp = vp->v_link)
367                                 *p++ = vp->v_name;
368                 *p = NULL;
369                 sort(ap);
370                 for (p = ap; *p != NULL; p++)
371                         printf("%s\t%s\n", *p, value(*p));
372                 return (0);
373         }
374         errs = 0;
375         for (ap = arglist; *ap != NULL; ap++) {
376                 cp = *ap;
377                 cp2 = varbuf;
378                 while (cp2 < varbuf + sizeof(varbuf) - 1 && *cp != '=' && *cp != '\0')
379                         *cp2++ = *cp++;
380                 *cp2 = '\0';
381                 if (*cp == '\0')
382                         cp = "";
383                 else
384                         cp++;
385                 if (equal(varbuf, "")) {
386                         printf("Non-null variable name required\n");
387                         errs++;
388                         continue;
389                 }
390                 assign(varbuf, cp);
391         }
392         return (errs);
393 }
394
395 /*
396  * Unset a bunch of variable values.
397  */
398 int
399 unset(char **arglist)
400 {
401         struct var *vp, *vp2;
402         int errs, h;
403         char **ap;
404
405         errs = 0;
406         for (ap = arglist; *ap != NULL; ap++) {
407                 if ((vp2 = lookup(*ap)) == NULL) {
408                         if (getenv(*ap)) 
409                                 unsetenv(*ap);
410                         else if (!sourcing) {
411                                 printf("\"%s\": undefined variable\n", *ap);
412                                 errs++;
413                         }
414                         continue;
415                 }
416                 h = hash(*ap);
417                 if (vp2 == variables[h]) {
418                         variables[h] = variables[h]->v_link;
419                         vfree(vp2->v_name);
420                         vfree(vp2->v_value);
421                         (void)free(vp2);
422                         continue;
423                 }
424                 for (vp = variables[h]; vp->v_link != vp2; vp = vp->v_link)
425                         ;
426                 vp->v_link = vp2->v_link;
427                 vfree(vp2->v_name);
428                 vfree(vp2->v_value);
429                 (void)free(vp2);
430         }
431         return (errs);
432 }
433
434 /*
435  * Put add users to a group.
436  */
437 int
438 group(char **argv)
439 {
440         struct grouphead *gh;
441         struct group *gp;
442         char **ap, *gname, **p;
443         int h, s;
444
445         if (*argv == NULL) {
446                 for (h = 0, s = 1; h < HSHSIZE; h++)
447                         for (gh = groups[h]; gh != NULL; gh = gh->g_link)
448                                 s++;
449                 ap = (char **)salloc(s * sizeof(*ap));
450                 for (h = 0, p = ap; h < HSHSIZE; h++)
451                         for (gh = groups[h]; gh != NULL; gh = gh->g_link)
452                                 *p++ = gh->g_name;
453                 *p = NULL;
454                 sort(ap);
455                 for (p = ap; *p != NULL; p++)
456                         printgroup(*p);
457                 return (0);
458         }
459         if (argv[1] == NULL) {
460                 printgroup(*argv);
461                 return (0);
462         }
463         gname = *argv;
464         h = hash(gname);
465         if ((gh = findgroup(gname)) == NULL) {
466                 if ((gh = calloc(1, sizeof(*gh))) == NULL)
467                         err(1, "Out of memory");
468                 gh->g_name = vcopy(gname);
469                 gh->g_list = NULL;
470                 gh->g_link = groups[h];
471                 groups[h] = gh;
472         }
473
474         /*
475          * Insert names from the command list into the group.
476          * Who cares if there are duplicates?  They get tossed
477          * later anyway.
478          */
479
480         for (ap = argv+1; *ap != NULL; ap++) {
481                 if ((gp = calloc(1, sizeof(*gp))) == NULL)
482                         err(1, "Out of memory");
483                 gp->ge_name = vcopy(*ap);
484                 gp->ge_link = gh->g_list;
485                 gh->g_list = gp;
486         }
487         return (0);
488 }
489
490 /*
491  * Sort the passed string vecotor into ascending dictionary
492  * order.
493  */
494 void
495 sort(char **list)
496 {
497         char **ap;
498
499         for (ap = list; *ap != NULL; ap++)
500                 ;
501         if (ap-list < 2)
502                 return;
503         qsort(list, ap-list, sizeof(*list), diction);
504 }
505
506 /*
507  * Do a dictionary order comparison of the arguments from
508  * qsort.
509  */
510 int
511 diction(const void *a, const void *b)
512 {
513         return (strcmp(*(const char **)a, *(const char **)b));
514 }
515
516 /*
517  * The do nothing command for comments.
518  */
519
520 /*ARGSUSED*/
521 int
522 null(int e __unused)
523 {
524         return (0);
525 }
526
527 /*
528  * Change to another file.  With no argument, print information about
529  * the current file.
530  */
531 int
532 file(char **argv)
533 {
534
535         if (argv[0] == NULL) {
536                 newfileinfo(0);
537                 return (0);
538         }
539         if (setfile(*argv) < 0)
540                 return (1);
541         announce();
542         return (0);
543 }
544
545 /*
546  * Expand file names like echo
547  */
548 int
549 echo(char **argv)
550 {
551         char **ap, *cp;
552
553         for (ap = argv; *ap != NULL; ap++) {
554                 cp = *ap;
555                 if ((cp = expand(cp)) != NULL) {
556                         if (ap != argv)
557                                 printf(" ");
558                         printf("%s", cp);
559                 }
560         }
561         printf("\n");
562         return (0);
563 }
564
565 int
566 Respond(int *msgvec)
567 {
568         if (value("Replyall") == NULL && value("flipr") == NULL)
569                 return (doRespond(msgvec));
570         else
571                 return (dorespond(msgvec));
572 }
573
574 /*
575  * Reply to a series of messages by simply mailing to the senders
576  * and not messing around with the To: and Cc: lists as in normal
577  * reply.
578  */
579 int
580 doRespond(int msgvec[])
581 {
582         struct header head;
583         struct message *mp;
584         int *ap;
585         char *cp, *mid;
586
587         head.h_to = NULL;
588         for (ap = msgvec; *ap != 0; ap++) {
589                 mp = &message[*ap - 1];
590                 touch(mp);
591                 dot = mp;
592                 if ((cp = skin(hfield("from", mp))) == NULL)
593                         cp = skin(nameof(mp, 2));
594                 head.h_to = cat(head.h_to, extract(cp, GTO));
595                 mid = skin(hfield("message-id", mp));
596         }
597         if (head.h_to == NULL)
598                 return (0);
599         mp = &message[msgvec[0] - 1];
600         if ((head.h_subject = hfield("subject", mp)) == NULL)
601                 head.h_subject = hfield("subj", mp);
602         head.h_subject = reedit(head.h_subject);
603         head.h_cc = NULL;
604         head.h_bcc = NULL;
605         head.h_smopts = NULL;
606         head.h_replyto = value("REPLYTO");
607         head.h_inreplyto = mid;
608         mail1(&head, 1);
609         return (0);
610 }
611
612 /*
613  * Conditional commands.  These allow one to parameterize one's
614  * .mailrc and do some things if sending, others if receiving.
615  */
616 int
617 ifcmd(char **argv)
618 {
619         char *cp;
620
621         if (cond != CANY) {
622                 printf("Illegal nested \"if\"\n");
623                 return (1);
624         }
625         cond = CANY;
626         cp = argv[0];
627         switch (*cp) {
628         case 'r': case 'R':
629                 cond = CRCV;
630                 break;
631
632         case 's': case 'S':
633                 cond = CSEND;
634                 break;
635
636         default:
637                 printf("Unrecognized if-keyword: \"%s\"\n", cp);
638                 return (1);
639         }
640         return (0);
641 }
642
643 /*
644  * Implement 'else'.  This is pretty simple -- we just
645  * flip over the conditional flag.
646  */
647 int
648 elsecmd(void)
649 {
650
651         switch (cond) {
652         case CANY:
653                 printf("\"Else\" without matching \"if\"\n");
654                 return (1);
655
656         case CSEND:
657                 cond = CRCV;
658                 break;
659
660         case CRCV:
661                 cond = CSEND;
662                 break;
663
664         default:
665                 printf("Mail's idea of conditions is screwed up\n");
666                 cond = CANY;
667                 break;
668         }
669         return (0);
670 }
671
672 /*
673  * End of if statement.  Just set cond back to anything.
674  */
675 int
676 endifcmd(void)
677 {
678
679         if (cond == CANY) {
680                 printf("\"Endif\" without matching \"if\"\n");
681                 return (1);
682         }
683         cond = CANY;
684         return (0);
685 }
686
687 /*
688  * Set the list of alternate names.
689  */
690 int
691 alternates(char **namelist)
692 {
693         int c;
694         char **ap, **ap2, *cp;
695
696         c = argcount(namelist) + 1;
697         if (c == 1) {
698                 if (altnames == 0)
699                         return (0);
700                 for (ap = altnames; *ap != NULL; ap++)
701                         printf("%s ", *ap);
702                 printf("\n");
703                 return (0);
704         }
705         if (altnames != 0)
706                 (void)free(altnames);
707         if ((altnames = calloc((unsigned)c, sizeof(char *))) == NULL)
708                 err(1, "Out of memory");
709         for (ap = namelist, ap2 = altnames; *ap != NULL; ap++, ap2++) {
710                 cp = calloc((unsigned)strlen(*ap) + 1, sizeof(char));
711                 strcpy(cp, *ap);
712                 *ap2 = cp;
713         }
714         *ap2 = 0;
715         return (0);
716 }