INSTALL: update for v14.6
[s-mailx.git] / cmd3.c
bloba29f5383bf345fc3fb1f630271a85e5a996beb0f
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Still more user commands.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2014 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
6 */
7 /*
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
40 #ifndef HAVE_AMALGAMATION
41 # include "nail.h"
42 #endif
44 struct cond_stack {
45 struct cond_stack *c_outer;
46 bool_t c_noop; /* Outer stack !c_go, entirely no-op */
47 bool_t c_go; /* Green light */
48 bool_t c_else; /* In `else' clause */
49 ui8_t __dummy[5];
52 static struct cond_stack *_cond_stack;
53 static char * _bang_buf;
54 static size_t _bang_size;
56 /* Modify subject we reply to to begin with Re: if it does not already */
57 static char * _reedit(char *subj);
59 /* Expand the shell escape by expanding unescaped !'s into the last issued
60 * command where possible */
61 static void _bangexp(char **str, size_t *size);
63 static void make_ref_and_cs(struct message *mp, struct header *head);
65 /* Get PTF to implementation of command `c' (i.e., take care for *flipr*) */
66 static int (* respond_or_Respond(int c))(int *, int);
68 /* Reply to a single message. Extract each name from the message header and
69 * send them off to mail1() */
70 static int respond_internal(int *msgvec, int recipient_record);
72 /* Reply to a series of messages by simply mailing to the senders and not
73 * messing around with the To: and Cc: lists as in normal reply */
74 static int Respond_internal(int *msgvec, int recipient_record);
76 /* Forward a message to a new recipient, in the sense of RFC 2822 */
77 static int forward1(char *str, int recipient_record);
79 /* Modify the subject we are replying to to begin with Fwd: */
80 static char * fwdedit(char *subj);
82 /* Sort the passed string vecotor into ascending dictionary order */
83 static void asort(char **list);
85 /* Do a dictionary order comparison of the arguments from qsort */
86 static int diction(const void *a, const void *b);
88 /* Do the real work of resending */
89 static int resend1(void *v, int add_resent);
91 /* ..to stdout */
92 static void list_shortcuts(void);
94 /* */
95 static enum okay delete_shortcut(char const *str);
97 static char *
98 _reedit(char *subj)
100 struct str in, out;
101 char *newsubj = NULL;
102 NYD_ENTER;
104 if (subj == NULL || *subj == '\0')
105 goto j_leave;
107 in.s = subj;
108 in.l = strlen(subj);
109 mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV);
111 /* XXX _reedit: we should take into account Aw: etc. (see mime.c!) */
112 if ((out.s[0] == 'r' || out.s[0] == 'R') &&
113 (out.s[1] == 'e' || out.s[1] == 'E') && out.s[2] == ':') {
114 newsubj = savestr(out.s);
115 goto jleave;
117 newsubj = salloc(out.l + 5);
118 sstpcpy(sstpcpy(newsubj, "Re: "), out.s);
119 jleave:
120 free(out.s);
121 j_leave:
122 NYD_LEAVE;
123 return newsubj;
126 static void
127 _bangexp(char **str, size_t *size)
129 char *bangbuf;
130 int changed = 0;
131 bool_t dobang;
132 size_t sz, i, j, bangbufsize;
133 NYD_ENTER;
135 dobang = ok_blook(bang);
137 bangbuf = smalloc(bangbufsize = *size);
138 i = j = 0;
139 while ((*str)[i]) {
140 if (dobang) {
141 if ((*str)[i] == '!') {
142 sz = strlen(_bang_buf);
143 bangbuf = srealloc(bangbuf, bangbufsize += sz);
144 ++changed;
145 memcpy(bangbuf + j, _bang_buf, sz + 1);
146 j += sz;
147 i++;
148 continue;
151 if ((*str)[i] == '\\' && (*str)[i + 1] == '!') {
152 bangbuf[j++] = '!';
153 i += 2;
154 ++changed;
156 bangbuf[j++] = (*str)[i++];
158 bangbuf[j] = '\0';
159 if (changed) {
160 printf("!%s\n", bangbuf);
161 fflush(stdout);
163 sz = j + 1;
164 if (sz > *size)
165 *str = srealloc(*str, *size = sz);
166 memcpy(*str, bangbuf, sz);
167 if (sz > _bang_size)
168 _bang_buf = srealloc(_bang_buf, _bang_size = sz);
169 memcpy(_bang_buf, bangbuf, sz);
170 free(bangbuf);
171 NYD_LEAVE;
174 static void
175 make_ref_and_cs(struct message *mp, struct header *head)
177 char *oldref, *oldmsgid, *newref, *cp;
178 size_t oldreflen = 0, oldmsgidlen = 0, reflen;
179 unsigned i;
180 struct name *n;
181 NYD_ENTER;
183 oldref = hfield1("references", mp);
184 oldmsgid = hfield1("message-id", mp);
185 if (oldmsgid == NULL || *oldmsgid == '\0') {
186 head->h_ref = NULL;
187 goto jleave;
189 reflen = 1;
190 if (oldref) {
191 oldreflen = strlen(oldref);
192 reflen += oldreflen + 2;
194 if (oldmsgid) {
195 oldmsgidlen = strlen(oldmsgid);
196 reflen += oldmsgidlen;
199 newref = ac_alloc(reflen);
200 if (oldref != NULL) {
201 memcpy(newref, oldref, oldreflen + 1);
202 if (oldmsgid != NULL) {
203 newref[oldreflen++] = ',';
204 newref[oldreflen++] = ' ';
205 memcpy(newref + oldreflen, oldmsgid, oldmsgidlen + 1);
207 } else if (oldmsgid)
208 memcpy(newref, oldmsgid, oldmsgidlen + 1);
209 n = extract(newref, GREF);
210 ac_free(newref);
212 /* Limit the references to 21 entries */
213 while (n->n_flink != NULL)
214 n = n->n_flink;
215 for (i = 1; i < 21; ++i) {
216 if (n->n_blink != NULL)
217 n = n->n_blink;
218 else
219 break;
221 n->n_blink = NULL;
222 head->h_ref = n;
223 if (ok_blook(reply_in_same_charset) &&
224 (cp = hfield1("content-type", mp)) != NULL)
225 head->h_charset = mime_getparam("charset", cp);
226 jleave:
227 NYD_LEAVE;
230 static int
231 (*respond_or_Respond(int c))(int *, int)
233 int opt = 0;
234 int (*rv)(int*, int);
235 NYD_ENTER;
237 opt += ok_blook(Replyall);
238 opt += ok_blook(flipr);
239 rv = ((opt == 1) ^ (c == 'R')) ? &Respond_internal : &respond_internal;
240 NYD_LEAVE;
241 return rv;
244 static int
245 respond_internal(int *msgvec, int recipient_record)
247 struct header head;
248 struct message *mp;
249 char *cp, *rcv;
250 struct name *np = NULL;
251 enum gfield gf;
252 int rv = 1;
253 NYD_ENTER;
255 gf = ok_blook(fullnames) ? GFULL : GSKIN;
257 if (msgvec[1] != 0) {
258 fprintf(stderr, tr(37,
259 "Sorry, can't reply to multiple messages at once\n"));
260 goto jleave;
263 mp = &message[msgvec[0] - 1];
264 touch(mp);
265 setdot(mp);
267 if ((rcv = hfield1("reply-to", mp)) == NULL)
268 if ((rcv = hfield1("from", mp)) == NULL)
269 rcv = nameof(mp, 1);
270 if (rcv != NULL)
271 np = lextract(rcv, GTO | gf);
272 if (!ok_blook(recipients_in_cc) && (cp = hfield1("to", mp)) != NULL)
273 np = cat(np, lextract(cp, GTO | gf));
274 /* Delete my name from reply list, and with it, all my alternate names */
275 np = elide(delete_alternates(np));
276 if (np == NULL)
277 np = lextract(rcv, GTO | gf);
279 memset(&head, 0, sizeof head);
280 head.h_to = np;
281 head.h_subject = hfield1("subject", mp);
282 head.h_subject = _reedit(head.h_subject);
284 /* Cc: */
285 np = NULL;
286 if (ok_blook(recipients_in_cc) && (cp = hfield1("to", mp)) != NULL)
287 np = lextract(cp, GCC | gf);
288 if ((cp = hfield1("cc", mp)) != NULL)
289 np = cat(np, lextract(cp, GCC | gf));
290 if (np != NULL)
291 head.h_cc = elide(delete_alternates(np));
292 make_ref_and_cs(mp, &head);
294 if (ok_blook(quote_as_attachment)) {
295 head.h_attach = csalloc(1, sizeof *head.h_attach);
296 head.h_attach->a_msgno = *msgvec;
297 head.h_attach->a_content_description = tr(512,
298 "Original message content");
301 if (mail1(&head, 1, mp, NULL, recipient_record, 0) == OKAY &&
302 ok_blook(markanswered) && (mp->m_flag & MANSWERED) == 0)
303 mp->m_flag |= MANSWER | MANSWERED;
304 rv = 0;
305 jleave:
306 NYD_LEAVE;
307 return rv;
310 static int
311 Respond_internal(int *msgvec, int recipient_record)
313 struct header head;
314 struct message *mp;
315 int *ap;
316 char *cp;
317 enum gfield gf;
318 NYD_ENTER;
320 memset(&head, 0, sizeof head);
321 gf = ok_blook(fullnames) ? GFULL : GSKIN;
323 for (ap = msgvec; *ap != 0; ++ap) {
324 mp = &message[*ap - 1];
325 touch(mp);
326 setdot(mp);
327 if ((cp = hfield1("reply-to", mp)) == NULL)
328 if ((cp = hfield1("from", mp)) == NULL)
329 cp = nameof(mp, 2);
330 head.h_to = cat(head.h_to, lextract(cp, GTO | gf));
332 if (head.h_to == NULL)
333 goto jleave;
335 mp = &message[msgvec[0] - 1];
336 head.h_subject = hfield1("subject", mp);
337 head.h_subject = _reedit(head.h_subject);
338 make_ref_and_cs(mp, &head);
340 if (ok_blook(quote_as_attachment)) {
341 head.h_attach = csalloc(1, sizeof *head.h_attach);
342 head.h_attach->a_msgno = *msgvec;
343 head.h_attach->a_content_description = tr(512,
344 "Original message content");
347 if (mail1(&head, 1, mp, NULL, recipient_record, 0) == OKAY &&
348 ok_blook(markanswered) && (mp->m_flag & MANSWERED) == 0)
349 mp->m_flag |= MANSWER | MANSWERED;
350 jleave:
351 NYD_LEAVE;
352 return 0;
355 static int
356 forward1(char *str, int recipient_record)
358 int *msgvec, rv = 1;
359 char *recipient;
360 struct message *mp;
361 struct header head;
362 bool_t f, forward_as_attachment;
363 NYD_ENTER;
365 if ((recipient = laststring(str, &f, 0)) == NULL) {
366 puts(tr(47, "No recipient specified."));
367 goto jleave;
370 forward_as_attachment = ok_blook(forward_as_attachment);
371 msgvec = salloc((msgCount + 2) * sizeof *msgvec);
373 if (!f) {
374 *msgvec = first(0, MMNORM);
375 if (*msgvec == 0) {
376 if (inhook) {
377 rv = 0;
378 goto jleave;
380 printf("No messages to forward.\n");
381 goto jleave;
383 msgvec[1] = 0;
384 } else if (getmsglist(str, msgvec, 0) < 0)
385 goto jleave;
387 if (*msgvec == 0) {
388 if (inhook) {
389 rv = 0;
390 goto jleave;
392 printf("No applicable messages.\n");
393 goto jleave;
395 if (msgvec[1] != 0) {
396 printf("Cannot forward multiple messages at once\n");
397 goto jleave;
400 memset(&head, 0, sizeof head);
401 if ((head.h_to = lextract(recipient, GTO |
402 (ok_blook(fullnames) ? GFULL : GSKIN))) == NULL)
403 goto jleave;
405 mp = &message[*msgvec - 1];
407 if (forward_as_attachment) {
408 head.h_attach = csalloc(1, sizeof *head.h_attach);
409 head.h_attach->a_msgno = *msgvec;
410 head.h_attach->a_content_description = "Forwarded message";
411 } else {
412 touch(mp);
413 setdot(mp);
415 head.h_subject = hfield1("subject", mp);
416 head.h_subject = fwdedit(head.h_subject);
417 mail1(&head, 1, (forward_as_attachment ? NULL : mp), NULL, recipient_record,
419 rv = 0;
420 jleave:
421 NYD_LEAVE;
422 return rv;
425 static char *
426 fwdedit(char *subj)
428 struct str in, out;
429 char *newsubj = NULL;
430 NYD_ENTER;
432 if (subj == NULL || *subj == '\0')
433 goto jleave;
435 in.s = subj;
436 in.l = strlen(subj);
437 mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV);
439 newsubj = salloc(out.l + 6);
440 memcpy(newsubj, "Fwd: ", 5);
441 memcpy(newsubj + 5, out.s, out.l + 1);
442 free(out.s);
443 jleave:
444 NYD_LEAVE;
445 return newsubj;
448 static void
449 asort(char **list)
451 char **ap;
452 NYD_ENTER;
454 for (ap = list; *ap != NULL; ++ap)
456 if (PTR2SIZE(ap - list) >= 2)
457 qsort(list, PTR2SIZE(ap - list), sizeof *list, diction);
458 NYD_LEAVE;
461 static int
462 diction(const void *a, const void *b)
464 int rv;
465 NYD_ENTER;
467 rv = strcmp(*(char**)UNCONST(a), *(char**)UNCONST(b));
468 NYD_LEAVE;
469 return rv;
472 static int
473 resend1(void *v, int add_resent)
475 char *name, *str;
476 struct name *to, *sn;
477 int *ip, *msgvec;
478 bool_t f = TRU1;
479 NYD_ENTER;
481 str = v;
482 msgvec = salloc((msgCount + 2) * sizeof *msgvec);
483 name = laststring(str, &f, 1);
484 if (name == NULL) {
485 puts(tr(47, "No recipient specified."));
486 goto jleave;
489 if (!f) {
490 *msgvec = first(0, MMNORM);
491 if (*msgvec == 0) {
492 if (inhook) {
493 f = FAL0;
494 goto jleave;
496 puts(tr(48, "No applicable messages."));
497 goto jleave;
499 msgvec[1] = 0;
500 } else if (getmsglist(str, msgvec, 0) < 0)
501 goto jleave;
503 if (*msgvec == 0) {
504 if (inhook) {
505 f = FAL0;
506 goto jleave;
508 printf("No applicable messages.\n");
509 goto jleave;
512 sn = nalloc(name, GTO);
513 to = usermap(sn, FAL0);
514 for (ip = msgvec; *ip != 0 && UICMP(z, PTR2SIZE(ip - msgvec), <, msgCount);
515 ++ip)
516 if (resend_msg(&message[*ip - 1], to, add_resent) != OKAY)
517 goto jleave;
518 f = FAL0;
519 jleave:
520 NYD_LEAVE;
521 return (f != FAL0);
524 static void
525 list_shortcuts(void)
527 struct shortcut *s;
528 NYD_ENTER;
530 for (s = shortcuts; s; s = s->sh_next)
531 printf("%s=%s\n", s->sh_short, s->sh_long);
532 NYD_LEAVE;
535 static enum okay
536 delete_shortcut(char const *str)
538 struct shortcut *sp, *sq;
539 enum okay rv = STOP;
540 NYD_ENTER;
542 for (sp = shortcuts, sq = NULL; sp; sq = sp, sp = sp->sh_next) {
543 if (!strcmp(sp->sh_short, str)) {
544 free(sp->sh_short);
545 free(sp->sh_long);
546 if (sq)
547 sq->sh_next = sp->sh_next;
548 if (sp == shortcuts)
549 shortcuts = sp->sh_next;
550 free(sp);
551 rv = OKAY;
552 break;
555 NYD_LEAVE;
556 return rv;
559 FL int
560 c_shell(void *v)
562 char const *sh = NULL;
563 char *str = v, *cmd;
564 size_t cmdsize;
565 sighandler_type sigint;
566 NYD_ENTER;
568 cmd = smalloc(cmdsize = strlen(str) + 1);
569 memcpy(cmd, str, cmdsize);
570 _bangexp(&cmd, &cmdsize);
571 if ((sh = ok_vlook(SHELL)) == NULL)
572 sh = XSHELL;
574 sigint = safe_signal(SIGINT, SIG_IGN);
575 run_command(sh, 0, -1, -1, "-c", cmd, NULL);
576 safe_signal(SIGINT, sigint);
577 printf("!\n");
579 free(cmd);
580 NYD_LEAVE;
581 return 0;
584 FL int
585 c_dosh(void *v)
587 sighandler_type sigint;
588 char const *sh;
589 NYD_ENTER;
590 UNUSED(v);
592 if ((sh = ok_vlook(SHELL)) == NULL)
593 sh = XSHELL;
595 sigint = safe_signal(SIGINT, SIG_IGN);
596 run_command(sh, 0, -1, -1, NULL, NULL, NULL);
597 safe_signal(SIGINT, sigint);
598 putchar('\n');
599 NYD_LEAVE;
600 return 0;
603 FL int
604 c_help(void *v)
606 int ret = 0;
607 char *arg;
608 NYD_ENTER;
610 arg = *(char**)v;
612 if (arg != NULL) {
613 #ifdef HAVE_DOCSTRINGS
614 ret = !print_comm_docstr(arg);
615 if (ret)
616 fprintf(stderr, tr(91, "Unknown command: `%s'\n"), arg);
617 #else
618 ret = c_cmdnotsupp(NULL);
619 #endif
620 goto jleave;
623 /* Very ugly, but take care for compiler supported string lengths :( */
624 printf(tr(295, "%s commands:\n"), progname);
625 puts(tr(296,
626 "type <message list> type messages\n"
627 "next goto and type next message\n"
628 "from <message list> give head lines of messages\n"
629 "headers print out active message headers\n"
630 "delete <message list> delete messages\n"
631 "undelete <message list> undelete messages\n"));
632 puts(tr(297,
633 "save <message list> folder append messages to folder and mark as saved\n"
634 "copy <message list> folder append messages to folder without marking them\n"
635 "write <message list> file append message texts to file, save attachments\n"
636 "preserve <message list> keep incoming messages in mailbox even if saved\n"
637 "Reply <message list> reply to message senders\n"
638 "reply <message list> reply to message senders and all recipients\n"));
639 puts(tr(298,
640 "mail addresses mail to specific recipients\n"
641 "file folder change to another folder\n"
642 "quit quit and apply changes to folder\n"
643 "xit quit and discard changes made to folder\n"
644 "! shell escape\n"
645 "cd <directory> chdir to directory or home if none given\n"
646 "list list names of all available commands\n"));
647 printf(tr(299,
648 "\nA <message list> consists of integers, ranges of same, or other criteria\n"
649 "separated by spaces. If omitted, %s uses the last message typed.\n"),
650 progname);
652 jleave:
653 NYD_LEAVE;
654 return ret;
657 FL int
658 c_cwd(void *v)
660 char buf[PATH_MAX]; /* TODO getcwd(3) may return a larger value */
661 NYD_ENTER;
663 if (getcwd(buf, sizeof buf) != NULL) {
664 puts(buf);
665 v = (void*)0x1;
666 } else {
667 perror("getcwd");
668 v = NULL;
670 NYD_LEAVE;
671 return (v == NULL);
674 FL int
675 c_chdir(void *v)
677 char **arglist = v;
678 char const *cp;
679 NYD_ENTER;
681 if (*arglist == NULL)
682 cp = homedir;
683 else if ((cp = file_expand(*arglist)) == NULL)
684 goto jleave;
685 if (chdir(cp) < 0) {
686 perror(cp);
687 cp = NULL;
689 jleave:
690 NYD_LEAVE;
691 return (cp == NULL);
694 FL int
695 c_respond(void *v)
697 int rv;
698 NYD_ENTER;
700 rv = (*respond_or_Respond('r'))(v, 0);
701 NYD_LEAVE;
702 return rv;
705 FL int
706 c_respondall(void *v)
708 int rv;
709 NYD_ENTER;
711 rv = respond_internal(v, 0);
712 NYD_LEAVE;
713 return rv;
716 FL int
717 c_respondsender(void *v)
719 int rv;
720 NYD_ENTER;
722 rv = Respond_internal(v, 0);
723 NYD_LEAVE;
724 return rv;
727 FL int
728 c_Respond(void *v)
730 int rv;
731 NYD_ENTER;
733 rv = (*respond_or_Respond('R'))(v, 0);
734 NYD_LEAVE;
735 return rv;
738 FL int
739 c_followup(void *v)
741 int rv;
742 NYD_ENTER;
744 rv = (*respond_or_Respond('r'))(v, 1);
745 NYD_LEAVE;
746 return rv;
749 FL int
750 c_followupall(void *v)
752 int rv;
753 NYD_ENTER;
755 rv = respond_internal(v, 1);
756 NYD_LEAVE;
757 return rv;
760 FL int
761 c_followupsender(void *v)
763 int rv;
764 NYD_ENTER;
766 rv = Respond_internal(v, 1);
767 NYD_LEAVE;
768 return rv;
771 FL int
772 c_Followup(void *v)
774 int rv;
775 NYD_ENTER;
777 rv = (*respond_or_Respond('R'))(v, 1);
778 NYD_LEAVE;
779 return rv;
782 FL int
783 c_forward(void *v)
785 int rv;
786 NYD_ENTER;
788 rv = forward1(v, 0);
789 NYD_LEAVE;
790 return rv;
793 FL int
794 c_Forward(void *v)
796 int rv;
797 NYD_ENTER;
799 rv = forward1(v, 1);
800 NYD_LEAVE;
801 return rv;
804 FL int
805 c_resend(void *v)
807 int rv;
808 NYD_ENTER;
810 rv = resend1(v, 1);
811 NYD_LEAVE;
812 return rv;
815 FL int
816 c_Resend(void *v)
818 int rv;
819 NYD_ENTER;
821 rv = resend1(v, 0);
822 NYD_LEAVE;
823 return rv;
826 FL int
827 c_preserve(void *v)
829 int *msgvec = v, *ip, mesg, rv = 1;
830 struct message *mp;
831 NYD_ENTER;
833 if (edit) {
834 printf(tr(39, "Cannot \"preserve\" in edit mode\n"));
835 goto jleave;
838 for (ip = msgvec; *ip != 0; ++ip) {
839 mesg = *ip;
840 mp = &message[mesg - 1];
841 mp->m_flag |= MPRESERVE;
842 mp->m_flag &= ~MBOX;
843 setdot(mp);
844 did_print_dot = TRU1;
846 rv = 0;
847 jleave:
848 NYD_LEAVE;
849 return rv;
852 FL int
853 c_unread(void *v)
855 int *msgvec = v, *ip;
856 NYD_ENTER;
858 for (ip = msgvec; *ip != 0; ++ip) {
859 setdot(&message[*ip - 1]);
860 dot->m_flag &= ~(MREAD | MTOUCH);
861 dot->m_flag |= MSTATUS;
862 #ifdef HAVE_IMAP
863 if (mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE)
864 imap_unread(&message[*ip - 1], *ip); /* TODO return? */
865 #endif
866 did_print_dot = TRU1;
868 NYD_LEAVE;
869 return 0;
872 FL int
873 c_seen(void *v)
875 int *msgvec = v, *ip;
876 NYD_ENTER;
878 for (ip = msgvec; *ip != 0; ++ip) {
879 setdot(&message[*ip - 1]);
880 touch(&message[*ip - 1]);
882 NYD_LEAVE;
883 return 0;
886 FL int
887 c_messize(void *v)
889 int *msgvec = v, *ip, mesg;
890 struct message *mp;
891 NYD_ENTER;
893 for (ip = msgvec; *ip != 0; ++ip) {
894 mesg = *ip;
895 mp = &message[mesg - 1];
896 printf("%d: ", mesg);
897 if (mp->m_xlines > 0)
898 printf("%ld", mp->m_xlines);
899 else
900 putchar(' ');
901 printf("/%lu\n", (ul_it)mp->m_xsize);
903 NYD_LEAVE;
904 return 0;
907 FL int
908 c_rexit(void *v)
910 UNUSED(v);
911 NYD_ENTER;
913 if (sourcing) {
914 NYD_LEAVE;
915 return 1;
917 exit(0);
920 FL int
921 c_set(void *v)
923 char **ap = v, *cp, *cp2, *varbuf, c;
924 int errs = 0;
925 NYD_ENTER;
927 if (*ap == NULL) {
928 var_list_all();
929 goto jleave;
932 for (; *ap != NULL; ++ap) {
933 cp = *ap;
934 cp2 = varbuf = ac_alloc(strlen(cp) + 1);
935 for (; (c = *cp) != '=' && c != '\0'; ++cp)
936 *cp2++ = c;
937 *cp2 = '\0';
938 if (c == '\0')
939 cp = UNCONST("");
940 else
941 ++cp;
942 if (varbuf == cp2) {
943 fprintf(stderr, tr(41, "Non-null variable name required\n"));
944 ++errs;
945 goto jnext;
947 if (varbuf[0] == 'n' && varbuf[1] == 'o')
948 errs += _var_vokclear(&varbuf[2]);
949 else
950 errs += _var_vokset(varbuf, (uintptr_t)cp);
951 jnext:
952 ac_free(varbuf);
954 jleave:
955 NYD_LEAVE;
956 return errs;
959 FL int
960 c_unset(void *v)
962 int errs;
963 char **ap;
964 NYD_ENTER;
966 errs = 0;
967 for (ap = v; *ap != NULL; ++ap)
968 errs += _var_vokclear(*ap);
969 NYD_LEAVE;
970 return errs;
973 FL int
974 c_group(void *v)
976 char **argv = v, **ap, *gname, **p;
977 struct grouphead *gh;
978 struct group *gp;
979 int h, s;
980 NYD_ENTER;
982 if (*argv == NULL) {
983 for (h = 0, s = 1; h < HSHSIZE; ++h)
984 for (gh = groups[h]; gh != NULL; gh = gh->g_link)
985 s++;
986 ap = salloc(s * sizeof *ap);
987 for (h = 0, p = ap; h < HSHSIZE; ++h)
988 for (gh = groups[h]; gh != NULL; gh = gh->g_link)
989 *p++ = gh->g_name;
990 *p = NULL;
991 asort(ap);
992 for (p = ap; *p != NULL; ++p)
993 printgroup(*p);
994 goto jleave;
997 if (argv[1] == NULL) {
998 printgroup(*argv);
999 goto jleave;
1002 gname = *argv;
1003 h = hash(gname);
1004 if ((gh = findgroup(gname)) == NULL) {
1005 gh = scalloc(1, sizeof *gh);
1006 gh->g_name = sstrdup(gname);
1007 gh->g_list = NULL;
1008 gh->g_link = groups[h];
1009 groups[h] = gh;
1012 /* Insert names from the command list into the group. Who cares if there
1013 * are duplicates? They get tossed later anyway */
1014 for (ap = argv + 1; *ap != NULL; ++ap) {
1015 gp = scalloc(1, sizeof *gp);
1016 gp->ge_name = sstrdup(*ap);
1017 gp->ge_link = gh->g_list;
1018 gh->g_list = gp;
1020 jleave:
1021 NYD_LEAVE;
1022 return 0;
1025 FL int
1026 c_ungroup(void *v)
1028 char **argv = v;
1029 int rv = 1;
1030 NYD_ENTER;
1032 if (*argv == NULL) {
1033 fprintf(stderr, tr(209, "Must specify alias to remove\n"));
1034 goto jleave;
1038 remove_group(*argv);
1039 while (*++argv != NULL);
1040 rv = 0;
1041 jleave:
1042 NYD_LEAVE;
1043 return rv;
1046 FL int
1047 c_file(void *v)
1049 char **argv = v;
1050 int i;
1051 NYD_ENTER;
1053 if (*argv == NULL) {
1054 newfileinfo();
1055 i = 0;
1056 goto jleave;
1059 if (inhook) {
1060 fprintf(stderr, tr(516, "Cannot change folder from within a hook.\n"));
1061 i = 1;
1062 goto jleave;
1065 save_mbox_for_possible_quitstuff();
1067 i = setfile(*argv, 0);
1068 if (i < 0) {
1069 i = 1;
1070 goto jleave;
1072 callhook(mailname, 0);
1073 if (i > 0 && !ok_blook(emptystart)) {
1074 i = 1;
1075 goto jleave;
1077 announce(ok_blook(bsdcompat) || ok_blook(bsdannounce));
1078 i = 0;
1079 jleave:
1080 NYD_LEAVE;
1081 return i;
1084 FL int
1085 c_echo(void *v)
1087 char const **argv = v, **ap, *cp;
1088 int c;
1089 NYD_ENTER;
1091 for (ap = argv; *ap != NULL; ++ap) {
1092 cp = *ap;
1093 if ((cp = fexpand(cp, FEXP_NSHORTCUT)) != NULL) {
1094 if (ap != argv)
1095 putchar(' ');
1096 c = 0;
1097 while (*cp != '\0' && (c = expand_shell_escape(&cp, FAL0)) > 0)
1098 putchar(c);
1099 /* \c ends overall processing */
1100 if (c < 0)
1101 goto jleave;
1104 putchar('\n');
1105 jleave:
1106 NYD_LEAVE;
1107 return 0;
1110 FL int
1111 c_if(void *v)
1113 struct cond_stack *csp;
1114 int rv = 1;
1115 char **argv = v, *cp, *op;
1116 NYD_ENTER;
1118 csp = smalloc(sizeof *csp);
1119 csp->c_outer = _cond_stack;
1120 csp->c_noop = condstack_isskip();
1121 csp->c_go = TRU1;
1122 csp->c_else = FAL0;
1123 _cond_stack = csp;
1125 cp = argv[0];
1126 if (*cp != '$' && argv[1] != NULL) {
1127 jesyn:
1128 fprintf(stderr, tr(528, "Invalid conditional expression \"%s %s %s\"\n"),
1129 argv[0], (argv[1] != NULL ? argv[1] : ""),
1130 (argv[2] != NULL ? argv[2] : ""));
1131 goto jleave;
1134 switch (*cp) {
1135 case '0':
1136 csp->c_go = FAL0;
1137 break;
1138 case 'R': case 'r':
1139 csp->c_go = !(options & OPT_SENDMODE);
1140 break;
1141 case 'S': case 's':
1142 csp->c_go = ((options & OPT_SENDMODE) != 0);
1143 break;
1144 case 'T': case 't':
1145 csp->c_go = ((options & OPT_TTYIN) != 0);
1146 break;
1147 case '$':
1148 /* Look up the value in question, we need it anyway */
1149 v = vok_vlook(++cp);
1151 /* Single argument, "implicit boolean" form? */
1152 if ((op = argv[1]) == NULL) {
1153 csp->c_go = (v != NULL);
1154 break;
1157 /* Three argument comparison form? */
1158 if (argv[2] == NULL || op[0] == '\0' || op[1] != '=' || op[2] != '\0')
1159 goto jesyn;
1160 /* A null value is treated as the empty string */
1161 if (v == NULL)
1162 v = UNCONST("");
1163 if (strcmp(v, argv[2]))
1164 v = NULL;
1165 switch (op[0]) {
1166 case '!':
1167 case '=':
1168 csp->c_go = ((op[0] == '=') ^ (v == NULL));
1169 break;
1170 default:
1171 goto jesyn;
1173 break;
1174 default:
1175 fprintf(stderr, tr(43, "Unrecognized if-keyword: \"%s\"\n"), cp);
1176 case '1':
1177 csp->c_go = TRU1;
1178 goto jleave;
1180 rv = 0;
1181 jleave:
1182 NYD_LEAVE;
1183 return rv;
1186 FL int
1187 c_else(void *v)
1189 int rv;
1190 NYD_ENTER;
1191 UNUSED(v);
1193 if (_cond_stack == NULL || _cond_stack->c_else) {
1194 fprintf(stderr, tr(44, "\"else\" without matching \"if\"\n"));
1195 rv = 1;
1196 } else {
1197 _cond_stack->c_go = !_cond_stack->c_go;
1198 _cond_stack->c_else = TRU1;
1199 rv = 0;
1201 NYD_LEAVE;
1202 return rv;
1205 FL int
1206 c_endif(void *v)
1208 struct cond_stack *csp;
1209 int rv;
1210 NYD_ENTER;
1211 UNUSED(v);
1213 if ((csp = _cond_stack) == NULL) {
1214 fprintf(stderr, tr(46, "\"endif\" without matching \"if\"\n"));
1215 rv = 1;
1216 } else {
1217 _cond_stack = csp->c_outer;
1218 free(csp);
1219 rv = 0;
1221 NYD_LEAVE;
1222 return rv;
1225 FL bool_t
1226 condstack_isskip(void)
1228 bool_t rv;
1229 NYD_ENTER;
1231 rv = (_cond_stack != NULL && (_cond_stack->c_noop || !_cond_stack->c_go));
1232 NYD_LEAVE;
1233 return rv;
1236 FL void *
1237 condstack_release(void)
1239 void *rv;
1240 NYD_ENTER;
1242 rv = _cond_stack;
1243 _cond_stack = NULL;
1244 NYD_LEAVE;
1245 return rv;
1248 FL bool_t
1249 condstack_take(void *self)
1251 struct cond_stack *csp;
1252 bool_t rv;
1253 NYD_ENTER;
1255 if (!(rv = ((csp = _cond_stack) == NULL)))
1256 do {
1257 _cond_stack = csp->c_outer;
1258 free(csp);
1259 } while ((csp = _cond_stack) != NULL);
1261 _cond_stack = self;
1262 NYD_LEAVE;
1263 return rv;
1266 FL int
1267 c_alternates(void *v)
1269 size_t l;
1270 char **namelist = v, **ap, **ap2, *cp;
1271 NYD_ENTER;
1273 l = argcount(namelist) + 1;
1274 if (l == 1) {
1275 if (altnames == NULL)
1276 goto jleave;
1277 for (ap = altnames; *ap != NULL; ++ap)
1278 printf("%s ", *ap);
1279 printf("\n");
1280 goto jleave;
1283 if (altnames != NULL) {
1284 for (ap = altnames; *ap != NULL; ++ap)
1285 free(*ap);
1286 free(altnames);
1288 altnames = smalloc(l * sizeof(char*));
1289 for (ap = namelist, ap2 = altnames; *ap != NULL; ++ap, ++ap2) {
1290 l = strlen(*ap) + 1;
1291 cp = smalloc(l);
1292 memcpy(cp, *ap, l);
1293 *ap2 = cp;
1295 *ap2 = NULL;
1296 jleave:
1297 NYD_LEAVE;
1298 return 0;
1301 FL int
1302 c_newmail(void *v)
1304 int val = 1, mdot;
1305 NYD_ENTER;
1306 UNUSED(v);
1308 if (
1309 #ifdef HAVE_IMAP
1310 (mb.mb_type != MB_IMAP || imap_newmail(1)) &&
1311 #endif
1312 (val = setfile(mailname, 1)) == 0) {
1313 mdot = getmdot(1);
1314 setdot(&message[mdot - 1]);
1316 NYD_LEAVE;
1317 return val;
1320 FL int
1321 c_shortcut(void *v)
1323 char **args = v;
1324 struct shortcut *s;
1325 int rv;
1326 NYD_ENTER;
1328 if (args[0] == NULL) {
1329 list_shortcuts();
1330 rv = 0;
1331 goto jleave;
1334 rv = 1;
1335 if (args[1] == NULL) {
1336 fprintf(stderr, tr(220, "expansion name for shortcut missing\n"));
1337 goto jleave;
1339 if (args[2] != NULL) {
1340 fprintf(stderr, tr(221, "too many arguments\n"));
1341 goto jleave;
1344 if ((s = get_shortcut(args[0])) != NULL) {
1345 free(s->sh_long);
1346 s->sh_long = sstrdup(args[1]);
1347 } else {
1348 s = scalloc(1, sizeof *s);
1349 s->sh_short = sstrdup(args[0]);
1350 s->sh_long = sstrdup(args[1]);
1351 s->sh_next = shortcuts;
1352 shortcuts = s;
1354 rv = 0;
1355 jleave:
1356 NYD_LEAVE;
1357 return rv;
1360 FL struct shortcut *
1361 get_shortcut(char const *str)
1363 struct shortcut *s;
1364 NYD_ENTER;
1366 for (s = shortcuts; s != NULL; s = s->sh_next)
1367 if (!strcmp(str, s->sh_short))
1368 break;
1369 NYD_LEAVE;
1370 return s;
1373 FL int
1374 c_unshortcut(void *v)
1376 char **args = v;
1377 bool_t errs = FAL0;
1378 NYD_ENTER;
1380 if (args[0] == NULL) {
1381 fprintf(stderr, tr(222, "need shortcut names to remove\n"));
1382 errs = TRU1;
1383 goto jleave;
1386 while (*args != NULL) {
1387 if (delete_shortcut(*args) != OKAY) {
1388 errs = TRU1;
1389 fprintf(stderr, tr(223, "%s: no such shortcut\n"), *args);
1391 ++args;
1393 jleave:
1394 NYD_LEAVE;
1395 return errs;
1398 FL int
1399 c_flag(void *v)
1401 struct message *m;
1402 int *msgvec = v, *ip;
1403 NYD_ENTER;
1405 for (ip = msgvec; *ip != 0; ++ip) {
1406 m = &message[*ip - 1];
1407 setdot(m);
1408 if ((m->m_flag & (MFLAG | MFLAGGED)) == 0)
1409 m->m_flag |= MFLAG | MFLAGGED;
1411 NYD_LEAVE;
1412 return 0;
1415 FL int
1416 c_unflag(void *v)
1418 struct message *m;
1419 int *msgvec = v, *ip;
1420 NYD_ENTER;
1422 for (ip = msgvec; *ip != 0; ++ip) {
1423 m = &message[*ip - 1];
1424 setdot(m);
1425 if (m->m_flag & (MFLAG | MFLAGGED)) {
1426 m->m_flag &= ~(MFLAG | MFLAGGED);
1427 m->m_flag |= MUNFLAG;
1430 NYD_LEAVE;
1431 return 0;
1434 FL int
1435 c_answered(void *v)
1437 struct message *m;
1438 int *msgvec = v, *ip;
1439 NYD_ENTER;
1441 for (ip = msgvec; *ip != 0; ++ip) {
1442 m = &message[*ip - 1];
1443 setdot(m);
1444 if ((m->m_flag & (MANSWER | MANSWERED)) == 0)
1445 m->m_flag |= MANSWER | MANSWERED;
1447 NYD_LEAVE;
1448 return 0;
1451 FL int
1452 c_unanswered(void *v)
1454 struct message *m;
1455 int *msgvec = v, *ip;
1456 NYD_ENTER;
1458 for (ip = msgvec; *ip != 0; ++ip) {
1459 m = &message[*ip - 1];
1460 setdot(m);
1461 if (m->m_flag & (MANSWER | MANSWERED)) {
1462 m->m_flag &= ~(MANSWER | MANSWERED);
1463 m->m_flag |= MUNANSWER;
1466 NYD_LEAVE;
1467 return 0;
1470 FL int
1471 c_draft(void *v)
1473 struct message *m;
1474 int *msgvec = v, *ip;
1475 NYD_ENTER;
1477 for (ip = msgvec; *ip != 0; ++ip) {
1478 m = &message[*ip - 1];
1479 setdot(m);
1480 if ((m->m_flag & (MDRAFT | MDRAFTED)) == 0)
1481 m->m_flag |= MDRAFT | MDRAFTED;
1483 NYD_LEAVE;
1484 return 0;
1487 FL int
1488 c_undraft(void *v)
1490 struct message *m;
1491 int *msgvec = v, *ip;
1492 NYD_ENTER;
1494 for (ip = msgvec; *ip != 0; ++ip) {
1495 m = &message[*ip - 1];
1496 setdot(m);
1497 if (m->m_flag & (MDRAFT | MDRAFTED)) {
1498 m->m_flag &= ~(MDRAFT | MDRAFTED);
1499 m->m_flag |= MUNDRAFT;
1502 NYD_LEAVE;
1503 return 0;
1506 FL int
1507 c_noop(void *v)
1509 int rv = 0;
1510 NYD_ENTER;
1511 UNUSED(v);
1513 switch (mb.mb_type) {
1514 case MB_IMAP:
1515 #ifdef HAVE_IMAP
1516 imap_noop();
1517 #else
1518 rv = c_cmdnotsupp(NULL);
1519 #endif
1520 break;
1521 case MB_POP3:
1522 #ifdef HAVE_POP3
1523 pop3_noop();
1524 #else
1525 rv = c_cmdnotsupp(NULL);
1526 #endif
1527 break;
1528 default:
1529 break;
1531 NYD_LEAVE;
1532 return rv;
1535 FL int
1536 c_remove(void *v)
1538 char const *fmt;
1539 size_t fmt_len;
1540 char **args = v, *name;
1541 int ec = 0;
1542 NYD_ENTER;
1544 if (*args == NULL) {
1545 fprintf(stderr, tr(290, "Syntax is: remove mailbox ...\n"));
1546 ec = 1;
1547 goto jleave;
1550 fmt = tr(287, "Remove \"%s\" (y/n) ? ");
1551 fmt_len = strlen(fmt);
1552 do {
1553 if ((name = expand(*args)) == NULL)
1554 continue;
1556 if (!strcmp(name, mailname)) {
1557 fprintf(stderr, tr(286, "Cannot remove current mailbox \"%s\".\n"),
1558 name);
1559 ec |= 1;
1560 continue;
1563 size_t vl = strlen(name) + fmt_len +1;
1564 char *vb = ac_alloc(vl);
1565 bool_t asw;
1566 snprintf(vb, vl, fmt, name);
1567 asw = getapproval(vb, TRU1);
1568 ac_free(vb);
1569 if (!asw)
1570 continue;
1573 switch (which_protocol(name)) {
1574 case PROTO_FILE:
1575 if (unlink(name) < 0) { /* TODO do not handle .gz .bz2 */
1576 perror(name);
1577 ec |= 1;
1579 break;
1580 case PROTO_POP3:
1581 fprintf(stderr, tr(288, "Cannot remove POP3 mailbox \"%s\".\n"),name);
1582 ec |= 1;
1583 break;
1584 case PROTO_IMAP:
1585 #ifdef HAVE_IMAP
1586 if (imap_remove(name) != OKAY)
1587 #endif
1588 ec |= 1;
1589 break;
1590 case PROTO_MAILDIR:
1591 if (maildir_remove(name) != OKAY)
1592 ec |= 1;
1593 break;
1594 case PROTO_UNKNOWN:
1595 fprintf(stderr, tr(289, "Unknown protocol in \"%s\". Not removed.\n"),
1596 name);
1597 ec |= 1;
1598 break;
1600 } while (*++args != NULL);
1601 jleave:
1602 NYD_LEAVE;
1603 return ec;
1606 FL int
1607 c_rename(void *v)
1609 char **args = v, *old, *new;
1610 enum protocol oldp, newp;
1611 int ec;
1612 NYD_ENTER;
1614 ec = 1;
1616 if (args[0] == NULL || args[1] == NULL || args[2] != NULL) {
1617 fprintf(stderr, "Syntax: rename old new\n");
1618 goto jleave;
1621 if ((old = expand(args[0])) == NULL)
1622 goto jleave;
1623 oldp = which_protocol(old);
1624 if ((new = expand(args[1])) == NULL)
1625 goto jleave;
1626 newp = which_protocol(new);
1628 if (!strcmp(old, mailname) || !strcmp(new, mailname)) {
1629 fprintf(stderr, tr(291, "Cannot rename current mailbox \"%s\".\n"), old);
1630 goto jleave;
1632 if ((oldp == PROTO_IMAP || newp == PROTO_IMAP) && oldp != newp) {
1633 fprintf(stderr, tr(292, "Can only rename folders of same type.\n"));
1634 goto jleave;
1637 ec = 0;
1639 if (newp == PROTO_POP3)
1640 goto jnopop3;
1641 switch (oldp) {
1642 case PROTO_FILE:
1643 if (link(old, new) == -1) {
1644 switch (errno) {
1645 case EACCES:
1646 case EEXIST:
1647 case ENAMETOOLONG:
1648 case ENOENT:
1649 case ENOSPC:
1650 case EXDEV:
1651 perror(new);
1652 break;
1653 default:
1654 perror(old);
1656 ec |= 1;
1657 } else if (unlink(old) == -1) {
1658 perror(old);
1659 ec |= 1;
1661 break;
1662 case PROTO_MAILDIR:
1663 if (rename(old, new) == -1) {
1664 perror(old);
1665 ec |= 1;
1667 break;
1668 case PROTO_POP3:
1669 jnopop3:
1670 fprintf(stderr, tr(293, "Cannot rename POP3 mailboxes.\n"));
1671 ec |= 1;
1672 break;
1673 #ifdef HAVE_IMAP
1674 case PROTO_IMAP:
1675 if (imap_rename(old, new) != OKAY)
1676 ec |= 1;
1677 break;
1678 #endif
1679 case PROTO_UNKNOWN:
1680 default:
1681 fprintf(stderr, tr(294,
1682 "Unknown protocol in \"%s\" and \"%s\". Not renamed.\n"), old, new);
1683 ec |= 1;
1684 break;
1686 jleave:
1687 NYD_LEAVE;
1688 return ec;
1691 /* vim:set fenc=utf-8:s-it-mode */