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