kernel: Fix buildkernel without INVARIANTS.
[dragonfly.git] / usr.bin / mail / cmd3.c
blobf32b29109b789becdb656db4e18ed312b7fbc274
1 /*
2 * Copyright (c) 1980, 1993
3 * The Regents of the University of California. All rights reserved.
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. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. 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.
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.
33 * @(#)cmd3.c 8.2 (Berkeley) 4/20/95
34 * $FreeBSD: src/usr.bin/mail/cmd3.c,v 1.4.6.4 2003/01/06 05:46:03 mikeh Exp $
37 #include "rcv.h"
38 #include "extern.h"
41 * Mail -- a mail program
43 * Still more user commands.
47 * Process a shell escape by saving signals, ignoring signals,
48 * and forking a sh -c
50 int
51 shell(char *str)
53 sig_t sigint = signal(SIGINT, SIG_IGN);
54 char *sh;
55 char cmd[BUFSIZ];
57 if (strlcpy(cmd, str, sizeof(cmd)) >= sizeof(cmd))
58 return (1);
59 if (bangexp(cmd, sizeof(cmd)) < 0)
60 return (1);
61 if ((sh = value("SHELL")) == NULL)
62 sh = _PATH_CSHELL;
63 run_command(sh, 0, -1, -1, "-c", cmd, NULL);
64 signal(SIGINT, sigint);
65 printf("!\n");
66 return (0);
70 * Fork an interactive shell.
72 /*ARGSUSED*/
73 int
74 dosh(char *str)
76 sig_t sigint = signal(SIGINT, SIG_IGN);
77 char *sh;
79 if ((sh = value("SHELL")) == NULL)
80 sh = _PATH_CSHELL;
81 run_command(sh, 0, -1, -1, NULL, NULL, NULL);
82 signal(SIGINT, sigint);
83 printf("\n");
84 return (0);
88 * Expand the shell escape by expanding unescaped !'s into the
89 * last issued command where possible.
91 int
92 bangexp(char *str, size_t strsize)
94 char bangbuf[BUFSIZ];
95 static char lastbang[BUFSIZ];
96 char *cp, *cp2;
97 int n, changed = 0;
99 cp = str;
100 cp2 = bangbuf;
101 n = sizeof(bangbuf);
102 while (*cp != '\0') {
103 if (*cp == '!') {
104 if (n < strlen(lastbang)) {
105 overf:
106 printf("Command buffer overflow\n");
107 return (-1);
109 changed++;
110 if (strlcpy(cp2, lastbang, sizeof(bangbuf) - (cp2 - bangbuf))
111 >= sizeof(bangbuf) - (cp2 - bangbuf))
112 goto overf;
113 cp2 += strlen(lastbang);
114 n -= strlen(lastbang);
115 cp++;
116 continue;
118 if (*cp == '\\' && cp[1] == '!') {
119 if (--n <= 1)
120 goto overf;
121 *cp2++ = '!';
122 cp += 2;
123 changed++;
125 if (--n <= 1)
126 goto overf;
127 *cp2++ = *cp++;
129 *cp2 = 0;
130 if (changed) {
131 printf("!%s\n", bangbuf);
132 fflush(stdout);
134 if (strlcpy(str, bangbuf, strsize) >= strsize)
135 goto overf;
136 if (strlcpy(lastbang, bangbuf, sizeof(lastbang)) >= sizeof(lastbang))
137 goto overf;
138 return (0);
142 * Print out a nice help message from some file or another.
146 help(void)
148 int c;
149 FILE *f;
151 if ((f = Fopen(_PATH_HELP, "r")) == NULL) {
152 warn("%s", _PATH_HELP);
153 return (1);
155 while ((c = getc(f)) != EOF)
156 putchar(c);
157 Fclose(f);
158 return (0);
162 * Change user's working directory.
165 schdir(char **arglist)
167 char *cp;
169 if (*arglist == NULL) {
170 if (homedir == NULL)
171 return (1);
172 cp = homedir;
173 } else
174 if ((cp = expand(*arglist)) == NULL)
175 return (1);
176 if (chdir(cp) < 0) {
177 warn("%s", cp);
178 return (1);
180 return (0);
184 respond(int *msgvec)
186 if (value("Replyall") == NULL && value("flipr") == NULL)
187 return (dorespond(msgvec));
188 else
189 return (doRespond(msgvec));
193 * Reply to a list of messages. Extract each name from the
194 * message header and send them off to mail1()
197 dorespond(int *msgvec)
199 struct message *mp;
200 char *cp, *rcv, *replyto;
201 char **ap;
202 struct name *np;
203 struct header head;
205 if (msgvec[1] != 0) {
206 printf("Sorry, can't reply to multiple messages at once\n");
207 return (1);
209 mp = &message[msgvec[0] - 1];
210 touch(mp);
211 dot = mp;
212 if ((rcv = skin(hfield("from", mp))) == NULL)
213 rcv = skin(nameof(mp, 1));
214 if ((replyto = skin(hfield("reply-to", mp))) != NULL)
215 np = extract(replyto, GTO);
216 else if ((cp = skin(hfield("to", mp))) != NULL)
217 np = extract(cp, GTO);
218 else
219 np = NULL;
220 np = elide(np);
222 * Delete my name from the reply list,
223 * and with it, all my alternate names.
225 np = delname(np, myname);
226 if (altnames)
227 for (ap = altnames; *ap != NULL; ap++)
228 np = delname(np, *ap);
229 if (np != NULL && replyto == NULL)
230 np = cat(np, extract(rcv, GTO));
231 else if (np == NULL) {
232 if (replyto != NULL)
233 printf("Empty reply-to field -- replying to author\n");
234 np = extract(rcv, GTO);
236 head.h_to = np;
237 if ((head.h_subject = hfield("subject", mp)) == NULL)
238 head.h_subject = hfield("subj", mp);
239 head.h_subject = reedit(head.h_subject);
240 if (replyto == NULL && (cp = skin(hfield("cc", mp))) != NULL) {
241 np = elide(extract(cp, GCC));
242 np = delname(np, myname);
243 if (altnames != 0)
244 for (ap = altnames; *ap != NULL; ap++)
245 np = delname(np, *ap);
246 head.h_cc = np;
247 } else
248 head.h_cc = NULL;
249 head.h_bcc = NULL;
250 head.h_smopts = NULL;
251 head.h_replyto = value("REPLYTO");
252 head.h_inreplyto = skin(hfield("message-id", mp));
253 mail1(&head, 1);
254 return (0);
258 * Modify the subject we are replying to to begin with Re: if
259 * it does not already.
261 char *
262 reedit(char *subj)
264 char *newsubj;
266 if (subj == NULL)
267 return (NULL);
268 if ((subj[0] == 'r' || subj[0] == 'R') &&
269 (subj[1] == 'e' || subj[1] == 'E') &&
270 subj[2] == ':')
271 return (subj);
272 newsubj = salloc(strlen(subj) + 5);
273 sprintf(newsubj, "Re: %s", subj);
274 return (newsubj);
278 * Preserve the named messages, so that they will be sent
279 * back to the system mailbox.
282 preserve(int *msgvec)
284 int *ip, mesg;
285 struct message *mp;
287 if (edit) {
288 printf("Cannot \"preserve\" in edit mode\n");
289 return (1);
291 for (ip = msgvec; *ip != 0; ip++) {
292 mesg = *ip;
293 mp = &message[mesg-1];
294 mp->m_flag |= MPRESERVE;
295 mp->m_flag &= ~MBOX;
296 dot = mp;
298 return (0);
302 * Mark all given messages as unread.
305 unread(int *msgvec)
307 int *ip;
309 for (ip = msgvec; *ip != 0; ip++) {
310 dot = &message[*ip-1];
311 dot->m_flag &= ~(MREAD|MTOUCH);
312 dot->m_flag |= MSTATUS;
314 return (0);
318 * Print the size of each message.
321 messize(int *msgvec)
323 struct message *mp;
324 int *ip, mesg;
326 for (ip = msgvec; *ip != 0; ip++) {
327 mesg = *ip;
328 mp = &message[mesg-1];
329 printf("%d: %ld/%ld\n", mesg, mp->m_lines, mp->m_size);
331 return (0);
335 * Quit quickly. If we are sourcing, just pop the input level
336 * by returning an error.
339 rexit(int e)
341 if (sourcing)
342 return (1);
343 exit(0);
344 /*NOTREACHED*/
348 * Set or display a variable value. Syntax is similar to that
349 * of csh.
352 set(char **arglist)
354 struct var *vp;
355 char *cp, *cp2;
356 char varbuf[BUFSIZ], **ap, **p;
357 int errs, h, s;
359 if (*arglist == NULL) {
360 for (h = 0, s = 1; h < HSHSIZE; h++)
361 for (vp = variables[h]; vp != NULL; vp = vp->v_link)
362 s++;
363 ap = (char **)salloc(s * sizeof(*ap));
364 for (h = 0, p = ap; h < HSHSIZE; h++)
365 for (vp = variables[h]; vp != NULL; vp = vp->v_link)
366 *p++ = vp->v_name;
367 *p = NULL;
368 sort(ap);
369 for (p = ap; *p != NULL; p++)
370 printf("%s\t%s\n", *p, value(*p));
371 return (0);
373 errs = 0;
374 for (ap = arglist; *ap != NULL; ap++) {
375 cp = *ap;
376 cp2 = varbuf;
377 while (cp2 < varbuf + sizeof(varbuf) - 1 && *cp != '=' && *cp != '\0')
378 *cp2++ = *cp++;
379 *cp2 = '\0';
380 if (*cp == '\0')
381 cp = "";
382 else
383 cp++;
384 if (equal(varbuf, "")) {
385 printf("Non-null variable name required\n");
386 errs++;
387 continue;
389 assign(varbuf, cp);
391 return (errs);
395 * Unset a bunch of variable values.
398 unset(char **arglist)
400 struct var *vp, *vp2;
401 int errs, h;
402 char **ap;
404 errs = 0;
405 for (ap = arglist; *ap != NULL; ap++) {
406 if ((vp2 = lookup(*ap)) == NULL) {
407 if (getenv(*ap))
408 unsetenv(*ap);
409 else if (!sourcing) {
410 printf("\"%s\": undefined variable\n", *ap);
411 errs++;
413 continue;
415 h = hash(*ap);
416 if (vp2 == variables[h]) {
417 variables[h] = variables[h]->v_link;
418 vfree(vp2->v_name);
419 vfree(vp2->v_value);
420 free(vp2);
421 continue;
423 for (vp = variables[h]; vp->v_link != vp2; vp = vp->v_link)
425 vp->v_link = vp2->v_link;
426 vfree(vp2->v_name);
427 vfree(vp2->v_value);
428 free(vp2);
430 return (errs);
434 * Put add users to a group.
437 group(char **argv)
439 struct grouphead *gh;
440 struct group *gp;
441 char **ap, *gname, **p;
442 int h, s;
444 if (*argv == NULL) {
445 for (h = 0, s = 1; h < HSHSIZE; h++)
446 for (gh = groups[h]; gh != NULL; gh = gh->g_link)
447 s++;
448 ap = (char **)salloc(s * sizeof(*ap));
449 for (h = 0, p = ap; h < HSHSIZE; h++)
450 for (gh = groups[h]; gh != NULL; gh = gh->g_link)
451 *p++ = gh->g_name;
452 *p = NULL;
453 sort(ap);
454 for (p = ap; *p != NULL; p++)
455 printgroup(*p);
456 return (0);
458 if (argv[1] == NULL) {
459 printgroup(*argv);
460 return (0);
462 gname = *argv;
463 h = hash(gname);
464 if ((gh = findgroup(gname)) == NULL) {
465 gh = calloc(sizeof(*gh), 1);
466 gh->g_name = vcopy(gname);
467 gh->g_list = NULL;
468 gh->g_link = groups[h];
469 groups[h] = gh;
473 * Insert names from the command list into the group.
474 * Who cares if there are duplicates? They get tossed
475 * later anyway.
478 for (ap = argv+1; *ap != NULL; ap++) {
479 gp = calloc(sizeof(*gp), 1);
480 gp->ge_name = vcopy(*ap);
481 gp->ge_link = gh->g_list;
482 gh->g_list = gp;
484 return (0);
488 * Sort the passed string vecotor into ascending dictionary
489 * order.
491 void
492 sort(char **list)
494 char **ap;
496 for (ap = list; *ap != NULL; ap++)
498 if (ap-list < 2)
499 return;
500 qsort(list, ap-list, sizeof(*list), diction);
504 * Do a dictionary order comparison of the arguments from
505 * qsort.
508 diction(const void *a, const void *b)
510 return (strcmp(*(const char **)a, *(const char **)b));
514 * The do nothing command for comments.
517 /*ARGSUSED*/
519 null(int e)
521 return (0);
525 * Change to another file. With no argument, print information about
526 * the current file.
529 file(char **argv)
531 if (argv[0] == NULL) {
532 newfileinfo(0);
533 return (0);
535 if (setfile(*argv) < 0)
536 return (1);
537 announce();
538 return (0);
542 * Expand file names like echo
545 echo(char **argv)
547 char **ap, *cp;
549 for (ap = argv; *ap != NULL; ap++) {
550 cp = *ap;
551 if ((cp = expand(cp)) != NULL) {
552 if (ap != argv)
553 printf(" ");
554 printf("%s", cp);
557 printf("\n");
558 return (0);
562 Respond(int *msgvec)
564 if (value("Replyall") == NULL && value("flipr") == NULL)
565 return (doRespond(msgvec));
566 else
567 return (dorespond(msgvec));
571 * Reply to a series of messages by simply mailing to the senders
572 * and not messing around with the To: and Cc: lists as in normal
573 * reply.
576 doRespond(int *msgvec)
578 struct header head;
579 struct message *mp;
580 int *ap;
581 char *cp, *mid;
583 head.h_to = NULL;
584 for (ap = msgvec; *ap != 0; ap++) {
585 mp = &message[*ap - 1];
586 touch(mp);
587 dot = mp;
588 if ((cp = skin(hfield("from", mp))) == NULL)
589 cp = skin(nameof(mp, 2));
590 head.h_to = cat(head.h_to, extract(cp, GTO));
591 mid = skin(hfield("message-id", mp));
593 if (head.h_to == NULL)
594 return (0);
595 mp = &message[msgvec[0] - 1];
596 if ((head.h_subject = hfield("subject", mp)) == NULL)
597 head.h_subject = hfield("subj", mp);
598 head.h_subject = reedit(head.h_subject);
599 head.h_cc = NULL;
600 head.h_bcc = NULL;
601 head.h_smopts = NULL;
602 head.h_replyto = value("REPLYTO");
603 head.h_inreplyto = mid;
604 mail1(&head, 1);
605 return (0);
609 * Conditional commands. These allow one to parameterize one's
610 * .mailrc and do some things if sending, others if receiving.
613 ifcmd(char **argv)
615 char *cp;
617 if (cond != CANY) {
618 printf("Illegal nested \"if\"\n");
619 return (1);
621 cond = CANY;
622 cp = argv[0];
623 switch (*cp) {
624 case 'r': case 'R':
625 cond = CRCV;
626 break;
628 case 's': case 'S':
629 cond = CSEND;
630 break;
632 default:
633 printf("Unrecognized if-keyword: \"%s\"\n", cp);
634 return (1);
636 return (0);
640 * Implement 'else'. This is pretty simple -- we just
641 * flip over the conditional flag.
644 elsecmd(void)
646 switch (cond) {
647 case CANY:
648 printf("\"Else\" without matching \"if\"\n");
649 return (1);
651 case CSEND:
652 cond = CRCV;
653 break;
655 case CRCV:
656 cond = CSEND;
657 break;
659 default:
660 printf("Mail's idea of conditions is screwed up\n");
661 cond = CANY;
662 break;
664 return (0);
668 * End of if statement. Just set cond back to anything.
671 endifcmd(void)
673 if (cond == CANY) {
674 printf("\"Endif\" without matching \"if\"\n");
675 return (1);
677 cond = CANY;
678 return (0);
682 * Set the list of alternate names.
685 alternates(char **namelist)
687 int c;
688 char **ap, **ap2, *cp;
690 c = argcount(namelist) + 1;
691 if (c == 1) {
692 if (altnames == 0)
693 return (0);
694 for (ap = altnames; *ap != NULL; ap++)
695 printf("%s ", *ap);
696 printf("\n");
697 return (0);
699 if (altnames != 0)
700 free(altnames);
701 altnames = calloc((unsigned)c, sizeof(char *));
702 for (ap = namelist, ap2 = altnames; *ap != NULL; ap++, ap2++) {
703 cp = calloc((unsigned)strlen(*ap) + 1, sizeof(char));
704 strcpy(cp, *ap);
705 *ap2 = cp;
707 *ap2 = NULL;
708 return (0);