1 /* $NetBSD: cmd3.c,v 1.44 2017/11/09 20:27:50 christos Exp $ */
2
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 #include <sys/cdefs.h>
33 #ifndef lint
34 #if 0
35 static char sccsid[] = "@(#)cmd3.c 8.2 (Berkeley) 4/20/95";
36 #else
37 __RCSID("$NetBSD: cmd3.c,v 1.44 2017/11/09 20:27:50 christos Exp $");
38 #endif
39 #endif /* not lint */
40
41 #include "rcv.h"
42 #include <assert.h>
43 #include <util.h>
44 #include "extern.h"
45 #include "mime.h"
46 #include "sig.h"
47 #include "thread.h"
48
49 /*
50 * Mail -- a mail program
51 *
52 * Still more user commands.
53 */
54
55
56 /*
57 * Do a dictionary order comparison of the arguments from
58 * qsort.
59 */
60 static int
diction(const void * a,const void * b)61 diction(const void *a, const void *b)
62 {
63
64 return strcmp(*(const char *const *)a, *(const char *const *)b);
65 }
66
67 /*
68 * Sort the passed string vector into ascending dictionary
69 * order.
70 */
71 PUBLIC void
sort(const char ** list)72 sort(const char **list)
73 {
74 const char **ap;
75
76 for (ap = list; *ap != NULL; ap++)
77 continue;
78 if (ap - list < 2)
79 return;
80 qsort(list, (size_t)(ap - list), sizeof(*list), diction);
81 }
82
83 /*
84 * Expand the shell escape by expanding unescaped !'s into the
85 * last issued command where possible.
86 */
87 static int
bangexp(char * str)88 bangexp(char *str)
89 {
90 static char lastbang[128];
91 char bangbuf[LINESIZE];
92 char *cp, *cp2;
93 ssize_t n;
94 int changed;
95
96 changed = 0;
97 cp = str;
98 cp2 = bangbuf;
99 n = sizeof(bangbuf); /* bytes left in bangbuf */
100 while (*cp) {
101 if (*cp == '!') {
102 if (n < (int)strlen(lastbang)) {
103 overf:
104 (void)printf("Command buffer overflow\n");
105 return -1;
106 }
107 changed++;
108 (void)strcpy(cp2, lastbang);
109 cp2 += strlen(lastbang);
110 n -= strlen(lastbang);
111 cp++;
112 continue;
113 }
114 if (*cp == '\\' && cp[1] == '!') {
115 if (--n <= 1)
116 goto overf;
117 *cp2++ = '!';
118 cp += 2;
119 changed++;
120 }
121 if (--n <= 1)
122 goto overf;
123 *cp2++ = *cp++;
124 }
125 *cp2 = 0;
126 if (changed) {
127 (void)printf("!%s\n", bangbuf);
128 (void)fflush(stdout);
129 }
130 (void)strcpy(str, bangbuf);
131 (void)strlcpy(lastbang, bangbuf, sizeof(lastbang));
132 return 0;
133 }
134
135 /*
136 * Process a shell escape by saving signals, ignoring signals,
137 * and forking a sh -c
138 */
139 PUBLIC int
shell(void * v)140 shell(void *v)
141 {
142 struct sigaction osa;
143 sigset_t oset;
144 char *str;
145 const char *shellcmd;
146 char cmd[LINESIZE];
147
148 str = v;
149 sig_check();
150 (void)sig_ignore(SIGINT, &osa, &oset);
151 (void)strcpy(cmd, str);
152 if (bangexp(cmd) < 0)
153 return 1;
154 if ((shellcmd = value(ENAME_SHELL)) == NULL)
155 shellcmd = _PATH_CSHELL;
156 (void)run_command(shellcmd, NULL, 0, 1, "-c", cmd, NULL);
157 (void)sig_restore(SIGINT, &osa, &oset);
158 (void)printf("!\n");
159 sig_check();
160 return 0;
161 }
162
163 /*
164 * Fork an interactive shell.
165 */
166 /*ARGSUSED*/
167 PUBLIC int
dosh(void * v __unused)168 dosh(void *v __unused)
169 {
170 struct sigaction osa;
171 sigset_t oset;
172 const char *shellcmd;
173
174 sig_check();
175 (void)sig_ignore(SIGINT, &osa, &oset);
176 if ((shellcmd = value(ENAME_SHELL)) == NULL)
177 shellcmd = _PATH_CSHELL;
178 (void)run_command(shellcmd, NULL, 0, 1, NULL);
179 (void)sig_restore(SIGINT, &osa, &oset);
180 (void)putchar('\n');
181 sig_check();
182 return 0;
183 }
184
185 /*
186 * Print out a nice help message from some file or another.
187 */
188
189 /*ARGSUSED*/
190 PUBLIC int
help(void * v __unused)191 help(void *v __unused)
192 {
193
194 cathelp(_PATH_HELP);
195 return 0;
196 }
197
198 /*
199 * Change user's working directory.
200 */
201 PUBLIC int
schdir(void * v)202 schdir(void *v)
203 {
204 char **arglist;
205 const char *cp;
206
207 arglist = v;
208 if (*arglist == NULL)
209 cp = homedir;
210 else
211 if ((cp = expand(*arglist)) == NULL)
212 return 1;
213 if (chdir(cp) < 0) {
214 warn("%s", cp);
215 return 1;
216 }
217 return 0;
218 }
219
220 /*
221 * Return the smopts field if "ReplyAsRecipient" is defined.
222 */
223 static struct name *
set_smopts(struct message * mp)224 set_smopts(struct message *mp)
225 {
226 char *cp;
227 struct name *np;
228 char *reply_as_recipient;
229
230 np = NULL;
231 reply_as_recipient = value(ENAME_REPLYASRECIPIENT);
232 if (reply_as_recipient &&
233 (cp = skin(hfield("to", mp))) != NULL &&
234 extract(cp, GTO)->n_flink == NULL) { /* check for one recipient */
235 char *p, *q;
236 size_t len = strlen(cp);
237 /*
238 * XXX - perhaps we always want to ignore
239 * "undisclosed-recipients:;" ?
240 */
241 for (p = q = reply_as_recipient; *p; p = q) {
242 while (*q != '\0' && *q != ',' && !is_WSP(*q))
243 q++;
244 if (p + len == q && strncasecmp(cp, p, len) == 0)
245 return np;
246 while (*q == ',' || is_WSP(*q))
247 q++;
248 }
249 np = extract(__UNCONST("-f"), GSMOPTS);
250 np = cat(np, extract(cp, GSMOPTS));
251 }
252
253 return np;
254 }
255
256 /*
257 * Modify the subject we are replying to to begin with Re: if
258 * it does not already.
259 */
260 static char *
reedit(char * subj,const char * pref)261 reedit(char *subj, const char *pref)
262 {
263 char *newsubj;
264 size_t preflen;
265
266 assert(pref != NULL);
267 if (subj == NULL)
268 return __UNCONST(pref);
269 preflen = strlen(pref);
270 if (strncasecmp(subj, pref, preflen) == 0)
271 return subj;
272 newsubj = salloc(strlen(subj) + preflen + 1 + 1);
273 (void)sprintf(newsubj, "%s %s", pref, subj);
274 return newsubj;
275 }
276
277 /*
278 * Set the "In-Reply-To" and "References" header fields appropriately.
279 * Used in replies.
280 */
281 static void
set_ident_fields(struct header * hp,struct message * mp)282 set_ident_fields(struct header *hp, struct message *mp)
283 {
284 char *in_reply_to;
285 char *references;
286
287 in_reply_to = hfield("message-id", mp);
288 hp->h_in_reply_to = in_reply_to;
289
290 references = hfield("references", mp);
291 hp->h_references = extract(references, GMISC);
292 hp->h_references = cat(hp->h_references, extract(in_reply_to, GMISC));
293 }
294
295 /*
296 * Reply to a list of messages. Extract each name from the
297 * message header and send them off to mail1()
298 */
299 static int
respond_core(int * msgvec)300 respond_core(int *msgvec)
301 {
302 struct message *mp;
303 char *cp, *rcv, *replyto;
304 char **ap;
305 struct name *np;
306 struct header head;
307
308 /* ensure that all header fields are initially NULL */
309 (void)memset(&head, 0, sizeof(head));
310
311 if (msgvec[1] != 0) {
312 (void)printf("Sorry, can't reply to multiple messages at once\n");
313 return 1;
314 }
315 mp = get_message(msgvec[0]);
316 touch(mp);
317 dot = mp;
318 if ((rcv = skin(hfield("from", mp))) == NULL)
319 rcv = skin(nameof(mp, 1));
320 if ((replyto = skin(hfield("reply-to", mp))) != NULL)
321 np = extract(replyto, GTO);
322 else if ((cp = skin(hfield("to", mp))) != NULL)
323 np = extract(cp, GTO);
324 else
325 np = NULL;
326 np = elide(np);
327 /*
328 * Delete my name from the reply list,
329 * and with it, all my alternate names.
330 */
331 np = delname(np, myname);
332 if (altnames)
333 for (ap = altnames; *ap; ap++)
334 np = delname(np, *ap);
335 if (np != NULL && replyto == NULL)
336 np = cat(np, extract(rcv, GTO));
337 else if (np == NULL) {
338 if (replyto != NULL)
339 (void)printf("Empty reply-to field -- replying to author\n");
340 np = extract(rcv, GTO);
341 }
342 head.h_to = np;
343 if ((head.h_subject = hfield("subject", mp)) == NULL)
344 head.h_subject = hfield("subj", mp);
345 head.h_subject = reedit(head.h_subject, "Re:");
346 if (replyto == NULL && (cp = skin(hfield("cc", mp))) != NULL) {
347 np = elide(extract(cp, GCC));
348 np = delname(np, myname);
349 if (altnames != 0)
350 for (ap = altnames; *ap; ap++)
351 np = delname(np, *ap);
352 head.h_cc = np;
353 } else
354 head.h_cc = NULL;
355 head.h_bcc = NULL;
356 head.h_smopts = set_smopts(mp);
357 #ifdef MIME_SUPPORT
358 head.h_attach = NULL;
359 #endif
360 set_ident_fields(&head, mp);
361 mail1(&head, 1);
362 return 0;
363 }
364
365 /*
366 * Reply to a series of messages by simply mailing to the senders
367 * and not messing around with the To: and Cc: lists as in normal
368 * reply.
369 */
370 static int
Respond_core(int msgvec[])371 Respond_core(int msgvec[])
372 {
373 struct header head;
374 struct message *mp;
375 int *ap;
376 char *cp;
377
378 /* ensure that all header fields are initially NULL */
379 (void)memset(&head, 0, sizeof(head));
380
381 head.h_to = NULL;
382 for (ap = msgvec; *ap != 0; ap++) {
383 mp = get_message(*ap);
384 touch(mp);
385 dot = mp;
386 if ((cp = skin(hfield("from", mp))) == NULL)
387 cp = skin(nameof(mp, 2));
388 head.h_to = cat(head.h_to, extract(cp, GTO));
389 }
390 if (head.h_to == NULL)
391 return 0;
392 mp = get_message(msgvec[0]);
393 if ((head.h_subject = hfield("subject", mp)) == NULL)
394 head.h_subject = hfield("subj", mp);
395 head.h_subject = reedit(head.h_subject, "Re:");
396 head.h_cc = NULL;
397 head.h_bcc = NULL;
398 head.h_smopts = set_smopts(mp);
399 #ifdef MIME_SUPPORT
400 head.h_attach = NULL;
401 #endif
402 set_ident_fields(&head, mp);
403 mail1(&head, 1);
404 return 0;
405 }
406
407 PUBLIC int
respond(void * v)408 respond(void *v)
409 {
410 int *msgvec = v;
411 if (value(ENAME_REPLYALL) == NULL)
412 return respond_core(msgvec);
413 else
414 return Respond_core(msgvec);
415 }
416
417 PUBLIC int
Respond(void * v)418 Respond(void *v)
419 {
420 int *msgvec = v;
421 if (value(ENAME_REPLYALL) == NULL)
422 return Respond_core(msgvec);
423 else
424 return respond_core(msgvec);
425 }
426
427 #ifdef MIME_SUPPORT
428 static int
forward_one(int msgno,struct name * h_to)429 forward_one(int msgno, struct name *h_to)
430 {
431 struct attachment attach;
432 struct message *mp;
433 struct header hdr;
434
435 mp = get_message(msgno);
436 if (mp == NULL) {
437 (void)printf("no such message %d\n", msgno);
438 return 1;
439 }
440 (void)printf("message %d\n", msgno);
441
442 (void)memset(&attach, 0, sizeof(attach));
443 attach.a_type = ATTACH_MSG;
444 attach.a_msg = mp;
445 attach.a_Content = get_mime_content(&attach, 0);
446
447 (void)memset(&hdr, 0, sizeof(hdr));
448 hdr.h_to = h_to;
449 if ((hdr.h_subject = hfield("subject", mp)) == NULL)
450 hdr.h_subject = hfield("subj", mp);
451 hdr.h_subject = reedit(hdr.h_subject, "Fwd:");
452 hdr.h_attach = &attach;
453 hdr.h_smopts = set_smopts(mp);
454
455 set_ident_fields(&hdr, mp);
456 mail1(&hdr, 1);
457 return 0;
458 }
459
460 PUBLIC int
forward(void * v)461 forward(void *v)
462 {
463 int *msgvec = v;
464 int *ip;
465 struct header hdr;
466 int rval;
467
468 if (forwardtab[0].i_count == 0) {
469 /* setup the forward tab */
470 add_ignore("Status", forwardtab);
471 }
472 (void)memset(&hdr, 0, sizeof(hdr));
473 if ((rval = grabh(&hdr, GTO)) != 0)
474 return rval;
475
476 if (hdr.h_to == NULL) {
477 (void)printf("address missing!\n");
478 return 1;
479 }
480 for (ip = msgvec; *ip; ip++) {
481 int e;
482 if ((e = forward_one(*ip, hdr.h_to)) != 0)
483 return e;
484 }
485 return 0;
486 }
487 #endif /* MIME_SUPPORT */
488
489 static int
bounce_one(int msgno,const char ** smargs,struct name * h_to)490 bounce_one(int msgno, const char **smargs, struct name *h_to)
491 {
492 char mailtempname[PATHSIZE];
493 struct message *mp;
494 int fd;
495 FILE *obuf;
496 int rval;
497
498 rval = 0;
499
500 obuf = NULL;
501 (void)snprintf(mailtempname, sizeof(mailtempname),
502 "%s/mail.RsXXXXXXXXXX", tmpdir);
503 if ((fd = mkstemp(mailtempname)) == -1 ||
504 (obuf = Fdopen(fd, "wef+")) == NULL) {
505 if (fd != -1)
506 (void)close(fd);
507 warn("%s", mailtempname);
508 rval = 1;
509 goto done;
510 }
511 (void)rm(mailtempname);
512
513 mp = get_message(msgno);
514
515 if (mp == NULL) {
516 (void)printf("no such message %d\n", msgno);
517 rval = 1;
518 goto done;
519 }
520 else {
521 char *cp;
522 char **ap;
523 struct name *np;
524 struct header hdr;
525
526 /*
527 * Construct and output a new "To:" field:
528 * Remove our address from anything in the old "To:" field
529 * and append that list to the bounce address(es).
530 */
531 np = NULL;
532 if ((cp = skin(hfield("to", mp))) != NULL)
533 np = extract(cp, GTO);
534 np = delname(np, myname);
535 if (altnames)
536 for (ap = altnames; *ap; ap++)
537 np = delname(np, *ap);
538 np = cat(h_to, np);
539 (void)memset(&hdr, 0, sizeof(hdr));
540 hdr.h_to = elide(np);
541 (void)puthead(&hdr, obuf, GTO | GCOMMA);
542 }
543 if (sendmessage(mp, obuf, bouncetab, NULL, NULL)) {
544 (void)printf("bounce failed for message %d\n", msgno);
545 rval = 1;
546 goto done;
547 }
548 rewind(obuf); /* XXX - here or inside mail2() */
549 mail2(obuf, smargs);
550 done:
551 if (obuf)
552 (void)Fclose(obuf);
553 return rval;
554 }
555
556 PUBLIC int
bounce(void * v)557 bounce(void *v)
558 {
559 int *msgvec;
560 int *ip;
561 const char **smargs;
562 struct header hdr;
563 int rval;
564
565 msgvec = v;
566 if (bouncetab[0].i_count == 0) {
567 /* setup the bounce tab */
568 add_ignore("Status", bouncetab);
569 add_ignore("Delivered-To", bouncetab);
570 add_ignore("To", bouncetab);
571 add_ignore("X-Original-To", bouncetab);
572 }
573 (void)memset(&hdr, 0, sizeof(hdr));
574 if ((rval = grabh(&hdr, GTO)) != 0)
575 return rval;
576
577 if (hdr.h_to == NULL)
578 return 1;
579
580 smargs = unpack(NULL, hdr.h_to);
581 for (ip = msgvec; *ip; ip++) {
582 int e;
583 if ((e = bounce_one(*ip, smargs, hdr.h_to)) != 0)
584 return e;
585 }
586 return 0;
587 }
588
589 /*
590 * Preserve the named messages, so that they will be sent
591 * back to the system mailbox.
592 */
593 PUBLIC int
preserve(void * v)594 preserve(void *v)
595 {
596 int *msgvec;
597 int *ip;
598
599 msgvec = v;
600 if (edit) {
601 (void)printf("Cannot \"preserve\" in edit mode\n");
602 return 1;
603 }
604 for (ip = msgvec; *ip != 0; ip++)
605 dot = set_m_flag(*ip, ~(MBOX | MPRESERVE), MPRESERVE);
606
607 return 0;
608 }
609
610 /*
611 * Mark all given messages as unread, preserving the new status.
612 */
613 PUBLIC int
unread(void * v)614 unread(void *v)
615 {
616 int *msgvec;
617 int *ip;
618
619 msgvec = v;
620 for (ip = msgvec; *ip != 0; ip++)
621 dot = set_m_flag(*ip, ~(MREAD | MTOUCH | MSTATUS), MSTATUS);
622
623 return 0;
624 }
625
626 /*
627 * Mark all given messages as read.
628 */
629 PUBLIC int
markread(void * v)630 markread(void *v)
631 {
632 int *msgvec;
633 int *ip;
634
635 msgvec = v;
636 for (ip = msgvec; *ip != 0; ip++)
637 dot = set_m_flag(*ip,
638 ~(MNEW | MTOUCH | MREAD | MSTATUS), MREAD | MSTATUS);
639
640 return 0;
641 }
642
643 /*
644 * Print the size of each message.
645 */
646 PUBLIC int
messize(void * v)647 messize(void *v)
648 {
649 int *msgvec;
650 struct message *mp;
651 int *ip, mesg;
652
653 msgvec = v;
654 for (ip = msgvec; *ip != 0; ip++) {
655 mesg = *ip;
656 mp = get_message(mesg);
657 (void)printf("%d: %ld/%llu\n", mesg, mp->m_blines,
658 (unsigned long long)mp->m_size);
659 }
660 return 0;
661 }
662
663 /*
664 * Quit quickly. If we are sourcing, just pop the input level
665 * by returning an error.
666 */
667 /*ARGSUSED*/
668 PUBLIC int
rexit(void * v __unused)669 rexit(void *v __unused)
670 {
671 if (sourcing)
672 return 1;
673 exit(0);
674 /*NOTREACHED*/
675 }
676
677 /*
678 * Set or display a variable value. Syntax is similar to that
679 * of csh.
680 */
681 PUBLIC int
set(void * v)682 set(void *v)
683 {
684 const char **arglist = v;
685 struct var *vp;
686 const char *cp;
687 char varbuf[LINESIZE];
688 const char **ap, **p;
689 int errs, h, s;
690 size_t l;
691
692 if (*arglist == NULL) {
693 for (h = 0, s = 1; h < HSHSIZE; h++)
694 for (vp = variables[h]; vp != NULL; vp = vp->v_link)
695 s++;
696 ap = salloc(s * sizeof(*ap));
697 for (h = 0, p = ap; h < HSHSIZE; h++)
698 for (vp = variables[h]; vp != NULL; vp = vp->v_link)
699 *p++ = vp->v_name;
700 *p = NULL;
701 sort(ap);
702 for (p = ap; *p != NULL; p++)
703 (void)printf("%s\t%s\n", *p, value(*p));
704 return 0;
705 }
706 errs = 0;
707 for (ap = arglist; *ap != NULL; ap++) {
708 cp = *ap;
709 while (*cp != '=' && *cp != '\0')
710 ++cp;
711 l = cp - *ap;
712 if (l >= sizeof(varbuf))
713 l = sizeof(varbuf) - 1;
714 (void)strncpy(varbuf, *ap, l);
715 varbuf[l] = '\0';
716 if (*cp == '\0')
717 cp = "";
718 else
719 cp++;
720 if (equal(varbuf, "")) {
721 (void)printf("Non-null variable name required\n");
722 errs++;
723 continue;
724 }
725 assign(varbuf, cp);
726 }
727 return errs;
728 }
729
730 /*
731 * Unset a bunch of variable values.
732 */
733 PUBLIC int
unset(void * v)734 unset(void *v)
735 {
736 char **arglist = v;
737 struct var *vp, *vp2;
738 int errs, h;
739 char **ap;
740
741 errs = 0;
742 for (ap = arglist; *ap != NULL; ap++) {
743 if ((vp2 = lookup(*ap)) == NULL) {
744 if (getenv(*ap)) {
745 (void)unsetenv(*ap);
746 } else if (!sourcing) {
747 (void)printf("\"%s\": undefined variable\n", *ap);
748 errs++;
749 }
750 continue;
751 }
752 h = hash(*ap);
753 if (vp2 == variables[h]) {
754 variables[h] = variables[h]->v_link;
755 v_free(vp2->v_name);
756 v_free(vp2->v_value);
757 free(vp2);
758 continue;
759 }
760 for (vp = variables[h]; vp->v_link != vp2; vp = vp->v_link)
761 continue;
762 vp->v_link = vp2->v_link;
763 v_free(vp2->v_name);
764 v_free(vp2->v_value);
765 free(vp2);
766 }
767 return errs;
768 }
769
770 /*
771 * Show a variable value.
772 */
773 PUBLIC int
show(void * v)774 show(void *v)
775 {
776 const char **arglist = v;
777 struct var *vp;
778 const char **ap, **p;
779 int h, s;
780
781 if (*arglist == NULL) {
782 for (h = 0, s = 1; h < HSHSIZE; h++)
783 for (vp = variables[h]; vp != NULL; vp = vp->v_link)
784 s++;
785 ap = salloc(s * sizeof(*ap));
786 for (h = 0, p = ap; h < HSHSIZE; h++)
787 for (vp = variables[h]; vp != NULL; vp = vp->v_link)
788 *p++ = vp->v_name;
789 *p = NULL;
790 sort(ap);
791 for (p = ap; *p != NULL; p++)
792 (void)printf("%s=%s\n", *p, value(*p));
793 return 0;
794 }
795
796 for (ap = arglist; *ap != NULL; ap++) {
797 char *val = value(*ap);
798 (void)printf("%s=%s\n", *ap, val ? val : "<null>");
799 }
800 return 0;
801 }
802
803
804 /*
805 * Put add users to a group.
806 */
807 PUBLIC int
group(void * v)808 group(void *v)
809 {
810 const char **argv = v;
811 struct grouphead *gh;
812 struct group *gp;
813 int h;
814 int s;
815 const char *gname;
816 const char **ap, **p;
817
818 if (*argv == NULL) {
819 for (h = 0, s = 1; h < HSHSIZE; h++)
820 for (gh = groups[h]; gh != NULL; gh = gh->g_link)
821 s++;
822 ap = salloc(s * sizeof(*ap));
823 for (h = 0, p = ap; h < HSHSIZE; h++)
824 for (gh = groups[h]; gh != NULL; gh = gh->g_link)
825 *p++ = gh->g_name;
826 *p = NULL;
827 sort(ap);
828 for (p = ap; *p != NULL; p++)
829 printgroup(*p);
830 return 0;
831 }
832 if (argv[1] == NULL) {
833 printgroup(*argv);
834 return 0;
835 }
836 gname = *argv;
837 h = hash(gname);
838 if ((gh = findgroup(gname)) == NULL) {
839 gh = ecalloc(1, sizeof(*gh));
840 gh->g_name = vcopy(gname);
841 gh->g_list = NULL;
842 gh->g_link = groups[h];
843 groups[h] = gh;
844 }
845
846 /*
847 * Insert names from the command list into the group.
848 * Who cares if there are duplicates? They get tossed
849 * later anyway.
850 */
851
852 for (ap = argv + 1; *ap != NULL; ap++) {
853 gp = ecalloc(1, sizeof(*gp));
854 gp->ge_name = vcopy(*ap);
855 gp->ge_link = gh->g_list;
856 gh->g_list = gp;
857 }
858 return 0;
859 }
860
861 /*
862 * Delete the named group alias. Return zero if the group was
863 * successfully deleted, or -1 if there was no such group.
864 */
865 static int
delgroup(const char * name)866 delgroup(const char *name)
867 {
868 struct grouphead *gh, *p;
869 struct group *g;
870 int h;
871
872 h = hash(name);
873 for (gh = groups[h], p = NULL; gh != NULL; p = gh, gh = gh->g_link)
874 if (strcmp(gh->g_name, name) == 0) {
875 if (p == NULL)
876 groups[h] = gh->g_link;
877 else
878 p->g_link = gh->g_link;
879 while (gh->g_list != NULL) {
880 g = gh->g_list;
881 gh->g_list = g->ge_link;
882 free(g->ge_name);
883 free(g);
884 }
885 free(gh->g_name);
886 free(gh);
887 return 0;
888 }
889 return -1;
890 }
891
892 /*
893 * The unalias command takes a list of alises
894 * and discards the remembered groups of users.
895 */
896 PUBLIC int
unalias(void * v)897 unalias(void *v)
898 {
899 char **ap;
900
901 for (ap = v; *ap != NULL; ap++)
902 (void)delgroup(*ap);
903 return 0;
904 }
905
906 /*
907 * The do nothing command for comments.
908 */
909 /*ARGSUSED*/
910 PUBLIC int
null(void * v __unused)911 null(void *v __unused)
912 {
913 return 0;
914 }
915
916 /*
917 * Change to another file. With no argument, print information about
918 * the current file.
919 */
920 PUBLIC int
file(void * v)921 file(void *v)
922 {
923 char **argv = v;
924
925 if (argv[0] == NULL) {
926 (void)newfileinfo(0);
927 return 0;
928 }
929 if (setfile(*argv) < 0)
930 return 1;
931 announce();
932
933 return 0;
934 }
935
936 /*
937 * Expand file names like echo
938 */
939 PUBLIC int
echo(void * v)940 echo(void *v)
941 {
942 char **argv = v;
943 char **ap;
944 const char *cp;
945
946 for (ap = argv; *ap != NULL; ap++) {
947 cp = *ap;
948 if ((cp = expand(cp)) != NULL) {
949 if (ap != argv)
950 (void)putchar(' ');
951 (void)printf("%s", cp);
952 }
953 }
954 (void)putchar('\n');
955 return 0;
956 }
957
958 /*
959 * Routines to push and pop the condition code to support nested
960 * if/else/endif statements.
961 */
962 static void
push_cond(int c_cond)963 push_cond(int c_cond)
964 {
965 struct cond_stack_s *csp;
966 csp = emalloc(sizeof(*csp));
967 csp->c_cond = c_cond;
968 csp->c_next = cond_stack;
969 cond_stack = csp;
970 }
971
972 static int
pop_cond(void)973 pop_cond(void)
974 {
975 int c_cond;
976 struct cond_stack_s *csp;
977
978 if ((csp = cond_stack) == NULL)
979 return -1;
980
981 c_cond = csp->c_cond;
982 cond_stack = csp->c_next;
983 free(csp);
984 return c_cond;
985 }
986
987 /*
988 * Conditional commands. These allow one to parameterize one's
989 * .mailrc and do some things if sending, others if receiving.
990 */
991 static int
if_push(void)992 if_push(void)
993 {
994 push_cond(cond);
995 cond &= ~CELSE;
996 if ((cond & (CIF | CSKIP)) == (CIF | CSKIP)) {
997 cond |= CIGN;
998 return 1;
999 }
1000 return 0;
1001 }
1002
1003 PUBLIC int
ifcmd(void * v)1004 ifcmd(void *v)
1005 {
1006 char **argv = v;
1007 char *keyword = argv[0];
1008 static const struct modetbl_s {
1009 const char *m_name;
1010 enum mailmode_e m_mode;
1011 } modetbl[] = {
1012 { "receiving", mm_receiving },
1013 { "sending", mm_sending },
1014 { "headersonly", mm_hdrsonly },
1015 { NULL, 0 },
1016 };
1017 const struct modetbl_s *mtp;
1018
1019 if (if_push())
1020 return 0;
1021
1022 cond = CIF;
1023 for (mtp = modetbl; mtp->m_name; mtp++)
1024 if (strcasecmp(keyword, mtp->m_name) == 0)
1025 break;
1026
1027 if (mtp->m_name == NULL) {
1028 cond = CNONE;
1029 (void)printf("Unrecognized if-keyword: \"%s\"\n", keyword);
1030 return 1;
1031 }
1032 if (mtp->m_mode != mailmode)
1033 cond |= CSKIP;
1034
1035 return 0;
1036 }
1037
1038 PUBLIC int
ifdefcmd(void * v)1039 ifdefcmd(void *v)
1040 {
1041 char **argv = v;
1042
1043 if (if_push())
1044 return 0;
1045
1046 cond = CIF;
1047 if (value(argv[0]) == NULL)
1048 cond |= CSKIP;
1049
1050 return 0;
1051 }
1052
1053 PUBLIC int
ifndefcmd(void * v)1054 ifndefcmd(void *v)
1055 {
1056 int rval;
1057 rval = ifdefcmd(v);
1058 cond ^= CSKIP;
1059 return rval;
1060 }
1061
1062 /*
1063 * Implement 'else'. This is pretty simple -- we just
1064 * flip over the conditional flag.
1065 */
1066 /*ARGSUSED*/
1067 PUBLIC int
elsecmd(void * v __unused)1068 elsecmd(void *v __unused)
1069 {
1070 if (cond_stack == NULL || (cond & (CIF | CELSE)) != CIF) {
1071 (void)printf("\"else\" without matching \"if\"\n");
1072 cond = CNONE;
1073 return 1;
1074 }
1075 if ((cond & CIGN) == 0) {
1076 cond ^= CSKIP;
1077 cond |= CELSE;
1078 }
1079 return 0;
1080 }
1081
1082 /*
1083 * End of if statement. Just set cond back to anything.
1084 */
1085 /*ARGSUSED*/
1086 PUBLIC int
endifcmd(void * v __unused)1087 endifcmd(void *v __unused)
1088 {
1089 if (cond_stack == NULL || (cond & CIF) != CIF) {
1090 (void)printf("\"endif\" without matching \"if\"\n");
1091 cond = CNONE;
1092 return 1;
1093 }
1094 cond = pop_cond();
1095 return 0;
1096 }
1097
1098 /*
1099 * Set the list of alternate names.
1100 */
1101 PUBLIC int
alternates(void * v)1102 alternates(void *v)
1103 {
1104 char **namelist = v;
1105 size_t c;
1106 char **ap, **ap2, *cp;
1107
1108 c = argcount(namelist) + 1;
1109 if (c == 1) {
1110 if (altnames == 0)
1111 return 0;
1112 for (ap = altnames; *ap; ap++)
1113 (void)printf("%s ", *ap);
1114 (void)printf("\n");
1115 return 0;
1116 }
1117 if (altnames != 0)
1118 free(altnames);
1119 altnames = ecalloc(c, sizeof(char *));
1120 for (ap = namelist, ap2 = altnames; *ap; ap++, ap2++) {
1121 cp = ecalloc(strlen(*ap) + 1, sizeof(char));
1122 (void)strcpy(cp, *ap);
1123 *ap2 = cp;
1124 }
1125 *ap2 = 0;
1126 return 0;
1127 }
1128