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