pc64: An mfence is supposed to suffice for TSC_DEADLINE vs. xAPIC ordering.
[dragonfly.git] / usr.bin / mail / cmd3.c
blobaab6be11af7147328205416e12accf8c02ab909f
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. 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.
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.
29 * @(#)cmd3.c 8.2 (Berkeley) 4/20/95
30 * $FreeBSD: src/usr.bin/mail/cmd3.c,v 1.4.6.4 2003/01/06 05:46:03 mikeh Exp $
33 #include "rcv.h"
34 #include "extern.h"
37 * Mail -- a mail program
39 * Still more user commands.
43 * Process a shell escape by saving signals, ignoring signals,
44 * and forking a sh -c
46 int
47 shell(char *str)
49 sig_t sigint = signal(SIGINT, SIG_IGN);
50 char *sh;
51 char cmd[BUFSIZ];
53 if (strlcpy(cmd, str, sizeof(cmd)) >= sizeof(cmd))
54 return (1);
55 if (bangexp(cmd, sizeof(cmd)) < 0)
56 return (1);
57 if ((sh = value("SHELL")) == NULL)
58 sh = _PATH_CSHELL;
59 run_command(sh, 0, -1, -1, "-c", cmd, NULL);
60 signal(SIGINT, sigint);
61 printf("!\n");
62 return (0);
66 * Fork an interactive shell.
68 /*ARGSUSED*/
69 int
70 dosh(char *str)
72 sig_t sigint = signal(SIGINT, SIG_IGN);
73 char *sh;
75 if ((sh = value("SHELL")) == NULL)
76 sh = _PATH_CSHELL;
77 run_command(sh, 0, -1, -1, NULL, NULL, NULL);
78 signal(SIGINT, sigint);
79 printf("\n");
80 return (0);
84 * Expand the shell escape by expanding unescaped !'s into the
85 * last issued command where possible.
87 int
88 bangexp(char *str, size_t strsize)
90 char bangbuf[BUFSIZ];
91 static char lastbang[BUFSIZ];
92 char *cp, *cp2;
93 int n, changed = 0;
95 cp = str;
96 cp2 = bangbuf;
97 n = sizeof(bangbuf);
98 while (*cp != '\0') {
99 if (*cp == '!') {
100 if (n < strlen(lastbang)) {
101 overf:
102 printf("Command buffer overflow\n");
103 return (-1);
105 changed++;
106 if (strlcpy(cp2, lastbang, sizeof(bangbuf) - (cp2 - bangbuf))
107 >= sizeof(bangbuf) - (cp2 - bangbuf))
108 goto overf;
109 cp2 += strlen(lastbang);
110 n -= strlen(lastbang);
111 cp++;
112 continue;
114 if (*cp == '\\' && cp[1] == '!') {
115 if (--n <= 1)
116 goto overf;
117 *cp2++ = '!';
118 cp += 2;
119 changed++;
121 if (--n <= 1)
122 goto overf;
123 *cp2++ = *cp++;
125 *cp2 = 0;
126 if (changed) {
127 printf("!%s\n", bangbuf);
128 fflush(stdout);
130 if (strlcpy(str, bangbuf, strsize) >= strsize)
131 goto overf;
132 if (strlcpy(lastbang, bangbuf, sizeof(lastbang)) >= sizeof(lastbang))
133 goto overf;
134 return (0);
138 * Print out a nice help message from some file or another.
142 help(void)
144 int c;
145 FILE *f;
147 if ((f = Fopen(_PATH_HELP, "r")) == NULL) {
148 warn("%s", _PATH_HELP);
149 return (1);
151 while ((c = getc(f)) != EOF)
152 putchar(c);
153 Fclose(f);
154 return (0);
158 * Change user's working directory.
161 schdir(char **arglist)
163 char *cp;
165 if (*arglist == NULL) {
166 if (homedir == NULL)
167 return (1);
168 cp = homedir;
169 } else
170 if ((cp = expand(*arglist)) == NULL)
171 return (1);
172 if (chdir(cp) < 0) {
173 warn("%s", cp);
174 return (1);
176 return (0);
180 respond(int *msgvec)
182 if (value("Replyall") == NULL && value("flipr") == NULL)
183 return (dorespond(msgvec));
184 else
185 return (doRespond(msgvec));
189 * Reply to a list of messages. Extract each name from the
190 * message header and send them off to mail1()
193 dorespond(int *msgvec)
195 struct message *mp;
196 char *cp, *rcv, *replyto;
197 char **ap;
198 struct name *np;
199 struct header head;
201 if (msgvec[1] != 0) {
202 printf("Sorry, can't reply to multiple messages at once\n");
203 return (1);
205 mp = &message[msgvec[0] - 1];
206 touch(mp);
207 dot = mp;
208 if ((rcv = skin(hfield("from", mp))) == NULL)
209 rcv = skin(nameof(mp, 1));
210 if ((replyto = skin(hfield("reply-to", mp))) != NULL)
211 np = extract(replyto, GTO);
212 else if ((cp = skin(hfield("to", mp))) != NULL)
213 np = extract(cp, GTO);
214 else
215 np = NULL;
216 np = elide(np);
218 * Delete my name from the reply list,
219 * and with it, all my alternate names.
221 np = delname(np, myname);
222 if (altnames)
223 for (ap = altnames; *ap != NULL; ap++)
224 np = delname(np, *ap);
225 if (np != NULL && replyto == NULL)
226 np = cat(np, extract(rcv, GTO));
227 else if (np == NULL) {
228 if (replyto != NULL)
229 printf("Empty reply-to field -- replying to author\n");
230 np = extract(rcv, GTO);
232 head.h_to = np;
233 if ((head.h_subject = hfield("subject", mp)) == NULL)
234 head.h_subject = hfield("subj", mp);
235 head.h_subject = reedit(head.h_subject);
236 if (replyto == NULL && (cp = skin(hfield("cc", mp))) != NULL) {
237 np = elide(extract(cp, GCC));
238 np = delname(np, myname);
239 if (altnames != 0)
240 for (ap = altnames; *ap != NULL; ap++)
241 np = delname(np, *ap);
242 head.h_cc = np;
243 } else
244 head.h_cc = NULL;
245 head.h_bcc = NULL;
246 head.h_smopts = NULL;
247 head.h_replyto = value("REPLYTO");
248 head.h_inreplyto = skin(hfield("message-id", mp));
249 mail1(&head, 1);
250 return (0);
254 * Modify the subject we are replying to to begin with Re: if
255 * it does not already.
257 char *
258 reedit(char *subj)
260 char *newsubj;
262 if (subj == NULL)
263 return (NULL);
264 if ((subj[0] == 'r' || subj[0] == 'R') &&
265 (subj[1] == 'e' || subj[1] == 'E') &&
266 subj[2] == ':')
267 return (subj);
268 newsubj = salloc(strlen(subj) + 5);
269 sprintf(newsubj, "Re: %s", subj);
270 return (newsubj);
274 * Preserve the named messages, so that they will be sent
275 * back to the system mailbox.
278 preserve(int *msgvec)
280 int *ip, mesg;
281 struct message *mp;
283 if (edit) {
284 printf("Cannot \"preserve\" in edit mode\n");
285 return (1);
287 for (ip = msgvec; *ip != 0; ip++) {
288 mesg = *ip;
289 mp = &message[mesg-1];
290 mp->m_flag |= MPRESERVE;
291 mp->m_flag &= ~MBOX;
292 dot = mp;
294 return (0);
298 * Mark all given messages as unread.
301 unread(int *msgvec)
303 int *ip;
305 for (ip = msgvec; *ip != 0; ip++) {
306 dot = &message[*ip-1];
307 dot->m_flag &= ~(MREAD|MTOUCH);
308 dot->m_flag |= MSTATUS;
310 return (0);
314 * Print the size of each message.
317 messize(int *msgvec)
319 struct message *mp;
320 int *ip, mesg;
322 for (ip = msgvec; *ip != 0; ip++) {
323 mesg = *ip;
324 mp = &message[mesg-1];
325 printf("%d: %ld/%ld\n", mesg, mp->m_lines, mp->m_size);
327 return (0);
331 * Quit quickly. If we are sourcing, just pop the input level
332 * by returning an error.
335 rexit(int e)
337 if (sourcing)
338 return (1);
339 exit(0);
340 /*NOTREACHED*/
344 * Set or display a variable value. Syntax is similar to that
345 * of csh.
348 set(char **arglist)
350 struct var *vp;
351 char *cp, *cp2;
352 char varbuf[BUFSIZ], **ap, **p;
353 int errs, h, s;
355 if (*arglist == NULL) {
356 for (h = 0, s = 1; h < HSHSIZE; h++)
357 for (vp = variables[h]; vp != NULL; vp = vp->v_link)
358 s++;
359 ap = (char **)salloc(s * sizeof(*ap));
360 for (h = 0, p = ap; h < HSHSIZE; h++)
361 for (vp = variables[h]; vp != NULL; vp = vp->v_link)
362 *p++ = vp->v_name;
363 *p = NULL;
364 sort(ap);
365 for (p = ap; *p != NULL; p++)
366 printf("%s\t%s\n", *p, value(*p));
367 return (0);
369 errs = 0;
370 for (ap = arglist; *ap != NULL; ap++) {
371 cp = *ap;
372 cp2 = varbuf;
373 while (cp2 < varbuf + sizeof(varbuf) - 1 && *cp != '=' && *cp != '\0')
374 *cp2++ = *cp++;
375 *cp2 = '\0';
376 if (*cp == '\0')
377 cp = "";
378 else
379 cp++;
380 if (equal(varbuf, "")) {
381 printf("Non-null variable name required\n");
382 errs++;
383 continue;
385 assign(varbuf, cp);
387 return (errs);
391 * Unset a bunch of variable values.
394 unset(char **arglist)
396 struct var *vp, *vp2;
397 int errs, h;
398 char **ap;
400 errs = 0;
401 for (ap = arglist; *ap != NULL; ap++) {
402 if ((vp2 = lookup(*ap)) == NULL) {
403 if (getenv(*ap))
404 unsetenv(*ap);
405 else if (!sourcing) {
406 printf("\"%s\": undefined variable\n", *ap);
407 errs++;
409 continue;
411 h = hash(*ap);
412 if (vp2 == variables[h]) {
413 variables[h] = variables[h]->v_link;
414 vfree(vp2->v_name);
415 vfree(vp2->v_value);
416 free(vp2);
417 continue;
419 for (vp = variables[h]; vp->v_link != vp2; vp = vp->v_link)
421 vp->v_link = vp2->v_link;
422 vfree(vp2->v_name);
423 vfree(vp2->v_value);
424 free(vp2);
426 return (errs);
430 * Put add users to a group.
433 group(char **argv)
435 struct grouphead *gh;
436 struct group *gp;
437 char **ap, *gname, **p;
438 int h, s;
440 if (*argv == NULL) {
441 for (h = 0, s = 1; h < HSHSIZE; h++)
442 for (gh = groups[h]; gh != NULL; gh = gh->g_link)
443 s++;
444 ap = (char **)salloc(s * sizeof(*ap));
445 for (h = 0, p = ap; h < HSHSIZE; h++)
446 for (gh = groups[h]; gh != NULL; gh = gh->g_link)
447 *p++ = gh->g_name;
448 *p = NULL;
449 sort(ap);
450 for (p = ap; *p != NULL; p++)
451 printgroup(*p);
452 return (0);
454 if (argv[1] == NULL) {
455 printgroup(*argv);
456 return (0);
458 gname = *argv;
459 h = hash(gname);
460 if ((gh = findgroup(gname)) == NULL) {
461 gh = calloc(sizeof(*gh), 1);
462 gh->g_name = vcopy(gname);
463 gh->g_list = NULL;
464 gh->g_link = groups[h];
465 groups[h] = gh;
469 * Insert names from the command list into the group.
470 * Who cares if there are duplicates? They get tossed
471 * later anyway.
474 for (ap = argv+1; *ap != NULL; ap++) {
475 gp = calloc(sizeof(*gp), 1);
476 gp->ge_name = vcopy(*ap);
477 gp->ge_link = gh->g_list;
478 gh->g_list = gp;
480 return (0);
484 * Sort the passed string vecotor into ascending dictionary
485 * order.
487 void
488 sort(char **list)
490 char **ap;
492 for (ap = list; *ap != NULL; ap++)
494 if (ap-list < 2)
495 return;
496 qsort(list, ap-list, sizeof(*list), diction);
500 * Do a dictionary order comparison of the arguments from
501 * qsort.
504 diction(const void *a, const void *b)
506 return (strcmp(*(const char **)a, *(const char **)b));
510 * The do nothing command for comments.
513 /*ARGSUSED*/
515 null(int e)
517 return (0);
521 * Change to another file. With no argument, print information about
522 * the current file.
525 file(char **argv)
527 if (argv[0] == NULL) {
528 newfileinfo(0);
529 return (0);
531 if (setfile(*argv) < 0)
532 return (1);
533 announce();
534 return (0);
538 * Expand file names like echo
541 echo(char **argv)
543 char **ap, *cp;
545 for (ap = argv; *ap != NULL; ap++) {
546 cp = *ap;
547 if ((cp = expand(cp)) != NULL) {
548 if (ap != argv)
549 printf(" ");
550 printf("%s", cp);
553 printf("\n");
554 return (0);
558 Respond(int *msgvec)
560 if (value("Replyall") == NULL && value("flipr") == NULL)
561 return (doRespond(msgvec));
562 else
563 return (dorespond(msgvec));
567 * Reply to a series of messages by simply mailing to the senders
568 * and not messing around with the To: and Cc: lists as in normal
569 * reply.
572 doRespond(int *msgvec)
574 struct header head;
575 struct message *mp;
576 int *ap;
577 char *cp, *mid;
579 head.h_to = NULL;
580 for (ap = msgvec; *ap != 0; ap++) {
581 mp = &message[*ap - 1];
582 touch(mp);
583 dot = mp;
584 if ((cp = skin(hfield("from", mp))) == NULL)
585 cp = skin(nameof(mp, 2));
586 head.h_to = cat(head.h_to, extract(cp, GTO));
587 mid = skin(hfield("message-id", mp));
589 if (head.h_to == NULL)
590 return (0);
591 mp = &message[msgvec[0] - 1];
592 if ((head.h_subject = hfield("subject", mp)) == NULL)
593 head.h_subject = hfield("subj", mp);
594 head.h_subject = reedit(head.h_subject);
595 head.h_cc = NULL;
596 head.h_bcc = NULL;
597 head.h_smopts = NULL;
598 head.h_replyto = value("REPLYTO");
599 head.h_inreplyto = mid;
600 mail1(&head, 1);
601 return (0);
605 * Conditional commands. These allow one to parameterize one's
606 * .mailrc and do some things if sending, others if receiving.
609 ifcmd(char **argv)
611 char *cp;
613 if (cond != CANY) {
614 printf("Illegal nested \"if\"\n");
615 return (1);
617 cond = CANY;
618 cp = argv[0];
619 switch (*cp) {
620 case 'r': case 'R':
621 cond = CRCV;
622 break;
624 case 's': case 'S':
625 cond = CSEND;
626 break;
628 default:
629 printf("Unrecognized if-keyword: \"%s\"\n", cp);
630 return (1);
632 return (0);
636 * Implement 'else'. This is pretty simple -- we just
637 * flip over the conditional flag.
640 elsecmd(void)
642 switch (cond) {
643 case CANY:
644 printf("\"Else\" without matching \"if\"\n");
645 return (1);
647 case CSEND:
648 cond = CRCV;
649 break;
651 case CRCV:
652 cond = CSEND;
653 break;
655 default:
656 printf("Mail's idea of conditions is screwed up\n");
657 cond = CANY;
658 break;
660 return (0);
664 * End of if statement. Just set cond back to anything.
667 endifcmd(void)
669 if (cond == CANY) {
670 printf("\"Endif\" without matching \"if\"\n");
671 return (1);
673 cond = CANY;
674 return (0);
678 * Set the list of alternate names.
681 alternates(char **namelist)
683 int c;
684 char **ap, **ap2, *cp;
686 c = argcount(namelist) + 1;
687 if (c == 1) {
688 if (altnames == 0)
689 return (0);
690 for (ap = altnames; *ap != NULL; ap++)
691 printf("%s ", *ap);
692 printf("\n");
693 return (0);
695 if (altnames != 0)
696 free(altnames);
697 altnames = calloc((unsigned)c, sizeof(char *));
698 for (ap = namelist, ap2 = altnames; *ap != NULL; ap++, ap2++) {
699 cp = calloc((unsigned)strlen(*ap) + 1, sizeof(char));
700 strcpy(cp, *ap);
701 *ap2 = cp;
703 *ap2 = NULL;
704 return (0);