]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - usr.bin/mail/cmd3.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.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                 gh = calloc(sizeof(*gh), 1);
467                 gh->g_name = vcopy(gname);
468                 gh->g_list = NULL;
469                 gh->g_link = groups[h];
470                 groups[h] = gh;
471         }
472
473         /*
474          * Insert names from the command list into the group.
475          * Who cares if there are duplicates?  They get tossed
476          * later anyway.
477          */
478
479         for (ap = argv+1; *ap != NULL; ap++) {
480                 gp = calloc(sizeof(*gp), 1);
481                 gp->ge_name = vcopy(*ap);
482                 gp->ge_link = gh->g_list;
483                 gh->g_list = gp;
484         }
485         return (0);
486 }
487
488 /*
489  * Sort the passed string vecotor into ascending dictionary
490  * order.
491  */
492 void
493 sort(char **list)
494 {
495         char **ap;
496
497         for (ap = list; *ap != NULL; ap++)
498                 ;
499         if (ap-list < 2)
500                 return;
501         qsort(list, ap-list, sizeof(*list), diction);
502 }
503
504 /*
505  * Do a dictionary order comparison of the arguments from
506  * qsort.
507  */
508 int
509 diction(const void *a, const void *b)
510 {
511         return (strcmp(*(const char **)a, *(const char **)b));
512 }
513
514 /*
515  * The do nothing command for comments.
516  */
517
518 /*ARGSUSED*/
519 int
520 null(int e __unused)
521 {
522         return (0);
523 }
524
525 /*
526  * Change to another file.  With no argument, print information about
527  * the current file.
528  */
529 int
530 file(char **argv)
531 {
532
533         if (argv[0] == NULL) {
534                 newfileinfo(0);
535                 return (0);
536         }
537         if (setfile(*argv) < 0)
538                 return (1);
539         announce();
540         return (0);
541 }
542
543 /*
544  * Expand file names like echo
545  */
546 int
547 echo(char **argv)
548 {
549         char **ap, *cp;
550
551         for (ap = argv; *ap != NULL; ap++) {
552                 cp = *ap;
553                 if ((cp = expand(cp)) != NULL) {
554                         if (ap != argv)
555                                 printf(" ");
556                         printf("%s", cp);
557                 }
558         }
559         printf("\n");
560         return (0);
561 }
562
563 int
564 Respond(int *msgvec)
565 {
566         if (value("Replyall") == NULL && value("flipr") == NULL)
567                 return (doRespond(msgvec));
568         else
569                 return (dorespond(msgvec));
570 }
571
572 /*
573  * Reply to a series of messages by simply mailing to the senders
574  * and not messing around with the To: and Cc: lists as in normal
575  * reply.
576  */
577 int
578 doRespond(int msgvec[])
579 {
580         struct header head;
581         struct message *mp;
582         int *ap;
583         char *cp, *mid;
584
585         head.h_to = NULL;
586         for (ap = msgvec; *ap != 0; ap++) {
587                 mp = &message[*ap - 1];
588                 touch(mp);
589                 dot = mp;
590                 if ((cp = skin(hfield("from", mp))) == NULL)
591                         cp = skin(nameof(mp, 2));
592                 head.h_to = cat(head.h_to, extract(cp, GTO));
593                 mid = skin(hfield("message-id", mp));
594         }
595         if (head.h_to == NULL)
596                 return (0);
597         mp = &message[msgvec[0] - 1];
598         if ((head.h_subject = hfield("subject", mp)) == NULL)
599                 head.h_subject = hfield("subj", mp);
600         head.h_subject = reedit(head.h_subject);
601         head.h_cc = NULL;
602         head.h_bcc = NULL;
603         head.h_smopts = NULL;
604         head.h_replyto = value("REPLYTO");
605         head.h_inreplyto = mid;
606         mail1(&head, 1);
607         return (0);
608 }
609
610 /*
611  * Conditional commands.  These allow one to parameterize one's
612  * .mailrc and do some things if sending, others if receiving.
613  */
614 int
615 ifcmd(char **argv)
616 {
617         char *cp;
618
619         if (cond != CANY) {
620                 printf("Illegal nested \"if\"\n");
621                 return (1);
622         }
623         cond = CANY;
624         cp = argv[0];
625         switch (*cp) {
626         case 'r': case 'R':
627                 cond = CRCV;
628                 break;
629
630         case 's': case 'S':
631                 cond = CSEND;
632                 break;
633
634         default:
635                 printf("Unrecognized if-keyword: \"%s\"\n", cp);
636                 return (1);
637         }
638         return (0);
639 }
640
641 /*
642  * Implement 'else'.  This is pretty simple -- we just
643  * flip over the conditional flag.
644  */
645 int
646 elsecmd(void)
647 {
648
649         switch (cond) {
650         case CANY:
651                 printf("\"Else\" without matching \"if\"\n");
652                 return (1);
653
654         case CSEND:
655                 cond = CRCV;
656                 break;
657
658         case CRCV:
659                 cond = CSEND;
660                 break;
661
662         default:
663                 printf("Mail's idea of conditions is screwed up\n");
664                 cond = CANY;
665                 break;
666         }
667         return (0);
668 }
669
670 /*
671  * End of if statement.  Just set cond back to anything.
672  */
673 int
674 endifcmd(void)
675 {
676
677         if (cond == CANY) {
678                 printf("\"Endif\" without matching \"if\"\n");
679                 return (1);
680         }
681         cond = CANY;
682         return (0);
683 }
684
685 /*
686  * Set the list of alternate names.
687  */
688 int
689 alternates(char **namelist)
690 {
691         int c;
692         char **ap, **ap2, *cp;
693
694         c = argcount(namelist) + 1;
695         if (c == 1) {
696                 if (altnames == 0)
697                         return (0);
698                 for (ap = altnames; *ap != NULL; ap++)
699                         printf("%s ", *ap);
700                 printf("\n");
701                 return (0);
702         }
703         if (altnames != 0)
704                 (void)free(altnames);
705         altnames = calloc((unsigned)c, sizeof(char *));
706         for (ap = namelist, ap2 = altnames; *ap != NULL; ap++, ap2++) {
707                 cp = calloc((unsigned)strlen(*ap) + 1, sizeof(char));
708                 strcpy(cp, *ap);
709                 *ap2 = cp;
710         }
711         *ap2 = 0;
712         return (0);
713 }