Drop strcomma() legacy (it is n_strsep())
[s-mailx.git] / cmd3.c
blobc4e505442bf5aa0857bfc1fcbe05c856d5b94442
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 static char * _bang_buf;
45 static size_t _bang_size;
47 /* Modify subject we reply to to begin with Re: if it does not already */
48 static char * _reedit(char *subj);
50 /* Expand the shell escape by expanding unescaped !'s into the last issued
51 * command where possible */
52 static void _bangexp(char **str, size_t *size);
54 static void make_ref_and_cs(struct message *mp, struct header *head);
56 /* Get PTF to implementation of command `c' (i.e., take care for *flipr*) */
57 static int (* respond_or_Respond(int c))(int *, int);
59 /* Reply to a single message. Extract each name from the message header and
60 * send them off to mail1() */
61 static int respond_internal(int *msgvec, int recipient_record);
63 /* Reply to a series of messages by simply mailing to the senders and not
64 * messing around with the To: and Cc: lists as in normal reply */
65 static int Respond_internal(int *msgvec, int recipient_record);
67 /* Forward a message to a new recipient, in the sense of RFC 2822 */
68 static int forward1(char *str, int recipient_record);
70 /* Modify the subject we are replying to to begin with Fwd: */
71 static char * fwdedit(char *subj);
73 /* Sort the passed string vecotor into ascending dictionary order */
74 static void asort(char **list);
76 /* Do a dictionary order comparison of the arguments from qsort */
77 static int diction(const void *a, const void *b);
79 /* Do the real work of resending */
80 static int resend1(void *v, int add_resent);
82 /* ..to stdout */
83 static void list_shortcuts(void);
85 /* */
86 static enum okay delete_shortcut(char const *str);
88 static char *
89 _reedit(char *subj)
91 struct str in, out;
92 char *newsubj = NULL;
93 NYD_ENTER;
95 if (subj == NULL || *subj == '\0')
96 goto j_leave;
98 in.s = subj;
99 in.l = strlen(subj);
100 mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV);
102 /* XXX _reedit: we should take into account Aw: etc. (see mime.c!) */
103 if ((out.s[0] == 'r' || out.s[0] == 'R') &&
104 (out.s[1] == 'e' || out.s[1] == 'E') && out.s[2] == ':') {
105 newsubj = savestr(out.s);
106 goto jleave;
108 newsubj = salloc(out.l + 5);
109 sstpcpy(sstpcpy(newsubj, "Re: "), out.s);
110 jleave:
111 free(out.s);
112 j_leave:
113 NYD_LEAVE;
114 return newsubj;
117 static void
118 _bangexp(char **str, size_t *size)
120 char *bangbuf;
121 int changed = 0;
122 bool_t dobang;
123 size_t sz, i, j, bangbufsize;
124 NYD_ENTER;
126 dobang = ok_blook(bang);
128 bangbuf = smalloc(bangbufsize = *size);
129 i = j = 0;
130 while ((*str)[i]) {
131 if (dobang) {
132 if ((*str)[i] == '!') {
133 sz = strlen(_bang_buf);
134 bangbuf = srealloc(bangbuf, bangbufsize += sz);
135 ++changed;
136 memcpy(bangbuf + j, _bang_buf, sz + 1);
137 j += sz;
138 i++;
139 continue;
142 if ((*str)[i] == '\\' && (*str)[i + 1] == '!') {
143 bangbuf[j++] = '!';
144 i += 2;
145 ++changed;
147 bangbuf[j++] = (*str)[i++];
149 bangbuf[j] = '\0';
150 if (changed) {
151 printf("!%s\n", bangbuf);
152 fflush(stdout);
154 sz = j + 1;
155 if (sz > *size)
156 *str = srealloc(*str, *size = sz);
157 memcpy(*str, bangbuf, sz);
158 if (sz > _bang_size)
159 _bang_buf = srealloc(_bang_buf, _bang_size = sz);
160 memcpy(_bang_buf, bangbuf, sz);
161 free(bangbuf);
162 NYD_LEAVE;
165 static void
166 make_ref_and_cs(struct message *mp, struct header *head)
168 char *oldref, *oldmsgid, *newref, *cp;
169 size_t oldreflen = 0, oldmsgidlen = 0, reflen;
170 unsigned i;
171 struct name *n;
172 NYD_ENTER;
174 oldref = hfield1("references", mp);
175 oldmsgid = hfield1("message-id", mp);
176 if (oldmsgid == NULL || *oldmsgid == '\0') {
177 head->h_ref = NULL;
178 goto jleave;
180 reflen = 1;
181 if (oldref) {
182 oldreflen = strlen(oldref);
183 reflen += oldreflen + 2;
185 if (oldmsgid) {
186 oldmsgidlen = strlen(oldmsgid);
187 reflen += oldmsgidlen;
190 newref = ac_alloc(reflen);
191 if (oldref != NULL) {
192 memcpy(newref, oldref, oldreflen + 1);
193 if (oldmsgid != NULL) {
194 newref[oldreflen++] = ',';
195 newref[oldreflen++] = ' ';
196 memcpy(newref + oldreflen, oldmsgid, oldmsgidlen + 1);
198 } else if (oldmsgid)
199 memcpy(newref, oldmsgid, oldmsgidlen + 1);
200 n = extract(newref, GREF);
201 ac_free(newref);
203 /* Limit the references to 21 entries */
204 while (n->n_flink != NULL)
205 n = n->n_flink;
206 for (i = 1; i < 21; ++i) {
207 if (n->n_blink != NULL)
208 n = n->n_blink;
209 else
210 break;
212 n->n_blink = NULL;
213 head->h_ref = n;
214 if (ok_blook(reply_in_same_charset) &&
215 (cp = hfield1("content-type", mp)) != NULL)
216 head->h_charset = mime_getparam("charset", cp);
217 jleave:
218 NYD_LEAVE;
221 static int
222 (*respond_or_Respond(int c))(int *, int)
224 int opt = 0;
225 int (*rv)(int*, int);
226 NYD_ENTER;
228 opt += ok_blook(Replyall);
229 opt += ok_blook(flipr);
230 rv = ((opt == 1) ^ (c == 'R')) ? &Respond_internal : &respond_internal;
231 NYD_LEAVE;
232 return rv;
235 static int
236 respond_internal(int *msgvec, int recipient_record)
238 struct header head;
239 struct message *mp;
240 char *cp, *rcv;
241 struct name *np = NULL;
242 enum gfield gf;
243 int rv = 1;
244 NYD_ENTER;
246 gf = ok_blook(fullnames) ? GFULL : GSKIN;
248 if (msgvec[1] != 0) {
249 fprintf(stderr, tr(37,
250 "Sorry, can't reply to multiple messages at once\n"));
251 goto jleave;
254 mp = &message[msgvec[0] - 1];
255 touch(mp);
256 setdot(mp);
258 if ((rcv = hfield1("reply-to", mp)) == NULL)
259 if ((rcv = hfield1("from", mp)) == NULL)
260 rcv = nameof(mp, 1);
261 if (rcv != NULL)
262 np = lextract(rcv, GTO | gf);
263 if (!ok_blook(recipients_in_cc) && (cp = hfield1("to", mp)) != NULL)
264 np = cat(np, lextract(cp, GTO | gf));
265 /* Delete my name from reply list, and with it, all my alternate names */
266 np = elide(delete_alternates(np));
267 if (np == NULL)
268 np = lextract(rcv, GTO | gf);
270 memset(&head, 0, sizeof head);
271 head.h_to = np;
272 head.h_subject = hfield1("subject", mp);
273 head.h_subject = _reedit(head.h_subject);
275 /* Cc: */
276 np = NULL;
277 if (ok_blook(recipients_in_cc) && (cp = hfield1("to", mp)) != NULL)
278 np = lextract(cp, GCC | gf);
279 if ((cp = hfield1("cc", mp)) != NULL)
280 np = cat(np, lextract(cp, GCC | gf));
281 if (np != NULL)
282 head.h_cc = elide(delete_alternates(np));
283 make_ref_and_cs(mp, &head);
285 if (ok_blook(quote_as_attachment)) {
286 head.h_attach = csalloc(1, sizeof *head.h_attach);
287 head.h_attach->a_msgno = *msgvec;
288 head.h_attach->a_content_description = tr(512,
289 "Original message content");
292 if (mail1(&head, 1, mp, NULL, recipient_record, 0) == OKAY &&
293 ok_blook(markanswered) && (mp->m_flag & MANSWERED) == 0)
294 mp->m_flag |= MANSWER | MANSWERED;
295 rv = 0;
296 jleave:
297 NYD_LEAVE;
298 return rv;
301 static int
302 Respond_internal(int *msgvec, int recipient_record)
304 struct header head;
305 struct message *mp;
306 int *ap;
307 char *cp;
308 enum gfield gf;
309 NYD_ENTER;
311 memset(&head, 0, sizeof head);
312 gf = ok_blook(fullnames) ? GFULL : GSKIN;
314 for (ap = msgvec; *ap != 0; ++ap) {
315 mp = &message[*ap - 1];
316 touch(mp);
317 setdot(mp);
318 if ((cp = hfield1("reply-to", mp)) == NULL)
319 if ((cp = hfield1("from", mp)) == NULL)
320 cp = nameof(mp, 2);
321 head.h_to = cat(head.h_to, lextract(cp, GTO | gf));
323 if (head.h_to == NULL)
324 goto jleave;
326 mp = &message[msgvec[0] - 1];
327 head.h_subject = hfield1("subject", mp);
328 head.h_subject = _reedit(head.h_subject);
329 make_ref_and_cs(mp, &head);
331 if (ok_blook(quote_as_attachment)) {
332 head.h_attach = csalloc(1, sizeof *head.h_attach);
333 head.h_attach->a_msgno = *msgvec;
334 head.h_attach->a_content_description = tr(512,
335 "Original message content");
338 if (mail1(&head, 1, mp, NULL, recipient_record, 0) == OKAY &&
339 ok_blook(markanswered) && (mp->m_flag & MANSWERED) == 0)
340 mp->m_flag |= MANSWER | MANSWERED;
341 jleave:
342 NYD_LEAVE;
343 return 0;
346 static int
347 forward1(char *str, int recipient_record)
349 int *msgvec, rv = 1;
350 char *recipient;
351 struct message *mp;
352 struct header head;
353 bool_t f, forward_as_attachment;
354 NYD_ENTER;
356 if ((recipient = laststring(str, &f, 0)) == NULL) {
357 puts(tr(47, "No recipient specified."));
358 goto jleave;
361 forward_as_attachment = ok_blook(forward_as_attachment);
362 msgvec = salloc((msgCount + 2) * sizeof *msgvec);
364 if (!f) {
365 *msgvec = first(0, MMNORM);
366 if (*msgvec == 0) {
367 if (inhook) {
368 rv = 0;
369 goto jleave;
371 printf("No messages to forward.\n");
372 goto jleave;
374 msgvec[1] = 0;
375 } else if (getmsglist(str, msgvec, 0) < 0)
376 goto jleave;
378 if (*msgvec == 0) {
379 if (inhook) {
380 rv = 0;
381 goto jleave;
383 printf("No applicable messages.\n");
384 goto jleave;
386 if (msgvec[1] != 0) {
387 printf("Cannot forward multiple messages at once\n");
388 goto jleave;
391 memset(&head, 0, sizeof head);
392 if ((head.h_to = lextract(recipient, GTO |
393 (ok_blook(fullnames) ? GFULL : GSKIN))) == NULL)
394 goto jleave;
396 mp = &message[*msgvec - 1];
398 if (forward_as_attachment) {
399 head.h_attach = csalloc(1, sizeof *head.h_attach);
400 head.h_attach->a_msgno = *msgvec;
401 head.h_attach->a_content_description = "Forwarded message";
402 } else {
403 touch(mp);
404 setdot(mp);
406 head.h_subject = hfield1("subject", mp);
407 head.h_subject = fwdedit(head.h_subject);
408 mail1(&head, 1, (forward_as_attachment ? NULL : mp), NULL, recipient_record,
410 rv = 0;
411 jleave:
412 NYD_LEAVE;
413 return rv;
416 static char *
417 fwdedit(char *subj)
419 struct str in, out;
420 char *newsubj = NULL;
421 NYD_ENTER;
423 if (subj == NULL || *subj == '\0')
424 goto jleave;
426 in.s = subj;
427 in.l = strlen(subj);
428 mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV);
430 newsubj = salloc(out.l + 6);
431 memcpy(newsubj, "Fwd: ", 5);
432 memcpy(newsubj + 5, out.s, out.l + 1);
433 free(out.s);
434 jleave:
435 NYD_LEAVE;
436 return newsubj;
439 static void
440 asort(char **list)
442 char **ap;
443 NYD_ENTER;
445 for (ap = list; *ap != NULL; ++ap)
447 if (PTR2SIZE(ap - list) >= 2)
448 qsort(list, PTR2SIZE(ap - list), sizeof *list, diction);
449 NYD_LEAVE;
452 static int
453 diction(const void *a, const void *b)
455 int rv;
456 NYD_ENTER;
458 rv = strcmp(*(char**)UNCONST(a), *(char**)UNCONST(b));
459 NYD_LEAVE;
460 return rv;
463 static int
464 resend1(void *v, int add_resent)
466 char *name, *str;
467 struct name *to, *sn;
468 int *ip, *msgvec;
469 bool_t f = TRU1;
470 NYD_ENTER;
472 str = v;
473 msgvec = salloc((msgCount + 2) * sizeof *msgvec);
474 name = laststring(str, &f, 1);
475 if (name == NULL) {
476 puts(tr(47, "No recipient specified."));
477 goto jleave;
480 if (!f) {
481 *msgvec = first(0, MMNORM);
482 if (*msgvec == 0) {
483 if (inhook) {
484 f = FAL0;
485 goto jleave;
487 puts(tr(48, "No applicable messages."));
488 goto jleave;
490 msgvec[1] = 0;
491 } else if (getmsglist(str, msgvec, 0) < 0)
492 goto jleave;
494 if (*msgvec == 0) {
495 if (inhook) {
496 f = FAL0;
497 goto jleave;
499 printf("No applicable messages.\n");
500 goto jleave;
503 sn = nalloc(name, GTO);
504 to = usermap(sn, FAL0);
505 for (ip = msgvec; *ip != 0 && UICMP(z, PTR2SIZE(ip - msgvec), <, msgCount);
506 ++ip)
507 if (resend_msg(&message[*ip - 1], to, add_resent) != OKAY)
508 goto jleave;
509 f = FAL0;
510 jleave:
511 NYD_LEAVE;
512 return (f != FAL0);
515 static void
516 list_shortcuts(void)
518 struct shortcut *s;
519 NYD_ENTER;
521 for (s = shortcuts; s; s = s->sh_next)
522 printf("%s=%s\n", s->sh_short, s->sh_long);
523 NYD_LEAVE;
526 static enum okay
527 delete_shortcut(char const *str)
529 struct shortcut *sp, *sq;
530 enum okay rv = STOP;
531 NYD_ENTER;
533 for (sp = shortcuts, sq = NULL; sp; sq = sp, sp = sp->sh_next) {
534 if (strcmp(sp->sh_short, str) == 0) {
535 free(sp->sh_short);
536 free(sp->sh_long);
537 if (sq)
538 sq->sh_next = sp->sh_next;
539 if (sp == shortcuts)
540 shortcuts = sp->sh_next;
541 free(sp);
542 rv = OKAY;
543 break;
546 NYD_LEAVE;
547 return rv;
550 FL int
551 shell(void *v)
553 char const *sh = NULL;
554 char *str = v, *cmd;
555 size_t cmdsize;
556 sighandler_type sigint;
557 NYD_ENTER;
559 cmd = smalloc(cmdsize = strlen(str) + 1);
560 memcpy(cmd, str, cmdsize);
561 _bangexp(&cmd, &cmdsize);
562 if ((sh = ok_vlook(SHELL)) == NULL)
563 sh = XSHELL;
565 sigint = safe_signal(SIGINT, SIG_IGN);
566 run_command(sh, 0, -1, -1, "-c", cmd, NULL);
567 safe_signal(SIGINT, sigint);
568 printf("!\n");
570 free(cmd);
571 NYD_LEAVE;
572 return 0;
575 FL int
576 dosh(void *v)
578 sighandler_type sigint;
579 char const *sh;
580 NYD_ENTER;
581 UNUSED(v);
583 if ((sh = ok_vlook(SHELL)) == NULL)
584 sh = XSHELL;
586 sigint = safe_signal(SIGINT, SIG_IGN);
587 run_command(sh, 0, -1, -1, NULL, NULL, NULL);
588 safe_signal(SIGINT, sigint);
589 putchar('\n');
590 NYD_LEAVE;
591 return 0;
594 FL int
595 help(void *v)
597 int ret = 0;
598 char *arg;
599 NYD_ENTER;
601 arg = *(char**)v;
603 if (arg != NULL) {
604 #ifdef HAVE_DOCSTRINGS
605 ret = !print_comm_docstr(arg);
606 if (ret)
607 fprintf(stderr, tr(91, "Unknown command: `%s'\n"), arg);
608 #else
609 ret = ccmdnotsupp(NULL);
610 #endif
611 goto jleave;
614 /* Very ugly, but take care for compiler supported string lengths :( */
615 printf(tr(295, "%s commands:\n"), progname);
616 puts(tr(296,
617 "type <message list> type messages\n"
618 "next goto and type next message\n"
619 "from <message list> give head lines of messages\n"
620 "headers print out active message headers\n"
621 "delete <message list> delete messages\n"
622 "undelete <message list> undelete messages\n"));
623 puts(tr(297,
624 "save <message list> folder append messages to folder and mark as saved\n"
625 "copy <message list> folder append messages to folder without marking them\n"
626 "write <message list> file append message texts to file, save attachments\n"
627 "preserve <message list> keep incoming messages in mailbox even if saved\n"
628 "Reply <message list> reply to message senders\n"
629 "reply <message list> reply to message senders and all recipients\n"));
630 puts(tr(298,
631 "mail addresses mail to specific recipients\n"
632 "file folder change to another folder\n"
633 "quit quit and apply changes to folder\n"
634 "xit quit and discard changes made to folder\n"
635 "! shell escape\n"
636 "cd <directory> chdir to directory or home if none given\n"
637 "list list names of all available commands\n"));
638 printf(tr(299,
639 "\nA <message list> consists of integers, ranges of same, or other criteria\n"
640 "separated by spaces. If omitted, %s uses the last message typed.\n"),
641 progname);
643 jleave:
644 NYD_LEAVE;
645 return ret;
648 FL int
649 c_cwd(void *v)
651 char buf[PATH_MAX]; /* TODO getcwd(3) may return a larger value */
652 NYD_ENTER;
654 if (getcwd(buf, sizeof buf) != NULL) {
655 puts(buf);
656 v = (void*)0x1;
657 } else {
658 perror("getcwd");
659 v = NULL;
661 NYD_LEAVE;
662 return (v == NULL);
665 FL int
666 c_chdir(void *v)
668 char **arglist = v;
669 char const *cp;
670 NYD_ENTER;
672 if (*arglist == NULL)
673 cp = homedir;
674 else if ((cp = file_expand(*arglist)) == NULL)
675 goto jleave;
676 if (chdir(cp) < 0) {
677 perror(cp);
678 cp = NULL;
680 jleave:
681 NYD_LEAVE;
682 return (cp == NULL);
685 FL int
686 respond(void *v)
688 int rv;
689 NYD_ENTER;
691 rv = (*respond_or_Respond('r'))(v, 0);
692 NYD_LEAVE;
693 return rv;
696 FL int
697 respondall(void *v)
699 int rv;
700 NYD_ENTER;
702 rv = respond_internal(v, 0);
703 NYD_LEAVE;
704 return rv;
707 FL int
708 respondsender(void *v)
710 int rv;
711 NYD_ENTER;
713 rv = Respond_internal(v, 0);
714 NYD_LEAVE;
715 return rv;
718 FL int
719 Respond(void *v)
721 int rv;
722 NYD_ENTER;
724 rv = (*respond_or_Respond('R'))(v, 0);
725 NYD_LEAVE;
726 return rv;
729 FL int
730 followup(void *v)
732 int rv;
733 NYD_ENTER;
735 rv = (*respond_or_Respond('r'))(v, 1);
736 NYD_LEAVE;
737 return rv;
740 FL int
741 followupall(void *v)
743 int rv;
744 NYD_ENTER;
746 rv = respond_internal(v, 1);
747 NYD_LEAVE;
748 return rv;
751 FL int
752 followupsender(void *v)
754 int rv;
755 NYD_ENTER;
757 rv = Respond_internal(v, 1);
758 NYD_LEAVE;
759 return rv;
762 FL int
763 Followup(void *v)
765 int rv;
766 NYD_ENTER;
768 rv = (*respond_or_Respond('R'))(v, 1);
769 NYD_LEAVE;
770 return rv;
773 FL int
774 forwardcmd(void *v)
776 int rv;
777 NYD_ENTER;
779 rv = forward1(v, 0);
780 NYD_LEAVE;
781 return rv;
784 FL int
785 Forwardcmd(void *v)
787 int rv;
788 NYD_ENTER;
790 rv = forward1(v, 1);
791 NYD_LEAVE;
792 return rv;
795 FL int
796 resendcmd(void *v)
798 int rv;
799 NYD_ENTER;
801 rv = resend1(v, 1);
802 NYD_LEAVE;
803 return rv;
806 FL int
807 Resendcmd(void *v)
809 int rv;
810 NYD_ENTER;
812 rv = resend1(v, 0);
813 NYD_LEAVE;
814 return rv;
817 FL int
818 preserve(void *v)
820 int *msgvec = v, *ip, mesg, rv = 1;
821 struct message *mp;
822 NYD_ENTER;
824 if (edit) {
825 printf(tr(39, "Cannot \"preserve\" in edit mode\n"));
826 goto jleave;
829 for (ip = msgvec; *ip != 0; ++ip) {
830 mesg = *ip;
831 mp = &message[mesg - 1];
832 mp->m_flag |= MPRESERVE;
833 mp->m_flag &= ~MBOX;
834 setdot(mp);
835 did_print_dot = TRU1;
837 rv = 0;
838 jleave:
839 NYD_LEAVE;
840 return rv;
843 FL int
844 unread(void *v)
846 int *msgvec = v, *ip;
847 NYD_ENTER;
849 for (ip = msgvec; *ip != 0; ++ip) {
850 setdot(&message[*ip - 1]);
851 dot->m_flag &= ~(MREAD | MTOUCH);
852 dot->m_flag |= MSTATUS;
853 #ifdef HAVE_IMAP
854 if (mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE)
855 imap_unread(&message[*ip - 1], *ip); /* TODO return? */
856 #endif
857 did_print_dot = TRU1;
859 NYD_LEAVE;
860 return 0;
863 FL int
864 seen(void *v)
866 int *msgvec = v, *ip;
867 NYD_ENTER;
869 for (ip = msgvec; *ip != 0; ++ip) {
870 setdot(&message[*ip - 1]);
871 touch(&message[*ip - 1]);
873 NYD_LEAVE;
874 return 0;
877 FL int
878 messize(void *v)
880 int *msgvec = v, *ip, mesg;
881 struct message *mp;
882 NYD_ENTER;
884 for (ip = msgvec; *ip != 0; ++ip) {
885 mesg = *ip;
886 mp = &message[mesg - 1];
887 printf("%d: ", mesg);
888 if (mp->m_xlines > 0)
889 printf("%ld", mp->m_xlines);
890 else
891 putchar(' ');
892 printf("/%lu\n", (ul_it)mp->m_xsize);
894 NYD_LEAVE;
895 return 0;
898 FL int
899 rexit(void *v)
901 UNUSED(v);
902 NYD_ENTER;
904 if (sourcing) {
905 NYD_LEAVE;
906 return 1;
908 exit(0);
911 FL int
912 set(void *v)
914 char **ap = v, *cp, *cp2, *varbuf, c;
915 int errs = 0;
916 NYD_ENTER;
918 if (*ap == NULL) {
919 var_list_all();
920 goto jleave;
923 for (; *ap != NULL; ++ap) {
924 cp = *ap;
925 cp2 = varbuf = ac_alloc(strlen(cp) + 1);
926 for (; (c = *cp) != '=' && c != '\0'; ++cp)
927 *cp2++ = c;
928 *cp2 = '\0';
929 if (c == '\0')
930 cp = UNCONST("");
931 else
932 ++cp;
933 if (varbuf == cp2) {
934 fprintf(stderr, tr(41, "Non-null variable name required\n"));
935 ++errs;
936 goto jnext;
938 if (varbuf[0] == 'n' && varbuf[1] == 'o')
939 errs += _var_vokclear(&varbuf[2]);
940 else
941 errs += _var_vokset(varbuf, (uintptr_t)cp);
942 jnext:
943 ac_free(varbuf);
945 jleave:
946 NYD_LEAVE;
947 return errs;
950 FL int
951 unset(void *v)
953 int errs;
954 char **ap;
955 NYD_ENTER;
957 errs = 0;
958 for (ap = v; *ap != NULL; ++ap)
959 errs += _var_vokclear(*ap);
960 NYD_LEAVE;
961 return errs;
964 FL int
965 group(void *v)
967 char **argv = v, **ap, *gname, **p;
968 struct grouphead *gh;
969 struct group *gp;
970 int h, s;
971 NYD_ENTER;
973 if (*argv == NULL) {
974 for (h = 0, s = 1; h < HSHSIZE; ++h)
975 for (gh = groups[h]; gh != NULL; gh = gh->g_link)
976 s++;
977 ap = salloc(s * sizeof *ap);
978 for (h = 0, p = ap; h < HSHSIZE; ++h)
979 for (gh = groups[h]; gh != NULL; gh = gh->g_link)
980 *p++ = gh->g_name;
981 *p = NULL;
982 asort(ap);
983 for (p = ap; *p != NULL; ++p)
984 printgroup(*p);
985 goto jleave;
988 if (argv[1] == NULL) {
989 printgroup(*argv);
990 goto jleave;
993 gname = *argv;
994 h = hash(gname);
995 if ((gh = findgroup(gname)) == NULL) {
996 gh = scalloc(1, sizeof *gh);
997 gh->g_name = sstrdup(gname);
998 gh->g_list = NULL;
999 gh->g_link = groups[h];
1000 groups[h] = gh;
1003 /* Insert names from the command list into the group. Who cares if there
1004 * are duplicates? They get tossed later anyway */
1005 for (ap = argv + 1; *ap != NULL; ++ap) {
1006 gp = scalloc(1, sizeof *gp);
1007 gp->ge_name = sstrdup(*ap);
1008 gp->ge_link = gh->g_list;
1009 gh->g_list = gp;
1011 jleave:
1012 NYD_LEAVE;
1013 return 0;
1016 FL int
1017 ungroup(void *v)
1019 char **argv = v;
1020 int rv = 1;
1021 NYD_ENTER;
1023 if (*argv == NULL) {
1024 fprintf(stderr, tr(209, "Must specify alias to remove\n"));
1025 goto jleave;
1029 remove_group(*argv);
1030 while (*++argv != NULL);
1031 rv = 0;
1032 jleave:
1033 NYD_LEAVE;
1034 return rv;
1037 FL int
1038 cfile(void *v)
1040 char **argv = v;
1041 int i;
1042 NYD_ENTER;
1044 if (*argv == NULL) {
1045 newfileinfo();
1046 i = 0;
1047 goto jleave;
1050 if (inhook) {
1051 fprintf(stderr, tr(516, "Cannot change folder from within a hook.\n"));
1052 i = 1;
1053 goto jleave;
1056 save_mbox_for_possible_quitstuff();
1058 i = setfile(*argv, 0);
1059 if (i < 0) {
1060 i = 1;
1061 goto jleave;
1063 callhook(mailname, 0);
1064 if (i > 0 && !ok_blook(emptystart)) {
1065 i = 1;
1066 goto jleave;
1068 announce(ok_blook(bsdcompat) || ok_blook(bsdannounce));
1069 i = 0;
1070 jleave:
1071 NYD_LEAVE;
1072 return i;
1075 FL int
1076 echo(void *v)
1078 char const **argv = v, **ap, *cp;
1079 int c;
1080 NYD_ENTER;
1082 for (ap = argv; *ap != NULL; ++ap) {
1083 cp = *ap;
1084 if ((cp = fexpand(cp, FEXP_NSHORTCUT)) != NULL) {
1085 if (ap != argv)
1086 putchar(' ');
1087 c = 0;
1088 while (*cp != '\0' && (c = expand_shell_escape(&cp, FAL0)) > 0)
1089 putchar(c);
1090 /* \c ends overall processing */
1091 if (c < 0)
1092 goto jleave;
1095 putchar('\n');
1096 jleave:
1097 NYD_LEAVE;
1098 return 0;
1101 FL int
1102 c_if(void *v)
1104 int rv = 1;
1105 char **argv = v, *cp, *op;
1106 NYD_ENTER;
1108 if (cond_state != COND_ANY) {
1109 fprintf(stderr, tr(42, "Illegal nested \"if\"\n"));
1110 goto jleave;
1113 cp = argv[0];
1114 if (*cp != '$' && argv[1] != NULL) {
1115 jesyn:
1116 fprintf(stderr, tr(528, "Invalid conditional expression \"%s %s %s\"\n"),
1117 argv[0], (argv[1] != NULL ? argv[1] : ""),
1118 (argv[2] != NULL ? argv[2] : ""));
1119 cond_state = COND_ANY;
1120 goto jleave;
1123 switch (*cp) {
1124 case '0':
1125 cond_state = COND_NOEXEC;
1126 break;
1127 case '1':
1128 cond_state = COND_EXEC;
1129 break;
1130 case 'R': case 'r':
1131 cond_state = COND_RCV;
1132 break;
1133 case 'S': case 's':
1134 cond_state = COND_SEND;
1135 break;
1136 case 'T': case 't':
1137 cond_state = COND_TERM;
1138 break;
1139 case '$':
1140 /* Look up the value in question, we need it anyway */
1141 v = vok_vlook(++cp);
1143 /* Single argument, "implicit boolean" form? */
1144 if ((op = argv[1]) == NULL) {
1145 cond_state = (v == NULL) ? COND_NOEXEC : COND_EXEC;
1146 break;
1149 /* Three argument comparison form? */
1150 if (argv[2] == NULL || op[0] == '\0' || op[1] != '=' || op[2] != '\0')
1151 goto jesyn;
1152 /* A null value is treated as the empty string */
1153 if (v == NULL)
1154 v = UNCONST("");
1155 if (strcmp(v, argv[2]))
1156 v = NULL;
1157 switch (op[0]) {
1158 case '!':
1159 case '=':
1160 cond_state = (((op[0] == '!') ^ (v == NULL))
1161 ? COND_NOEXEC : COND_EXEC);
1162 break;
1163 default:
1164 goto jesyn;
1166 break;
1167 default:
1168 fprintf(stderr, tr(43, "Unrecognized if-keyword: \"%s\"\n"), cp);
1169 cond_state = COND_ANY;
1170 goto jleave;
1172 rv = 0;
1173 jleave:
1174 NYD_LEAVE;
1175 return rv;
1178 FL int
1179 c_else(void *v)
1181 int rv = 1;
1182 NYD_ENTER;
1183 UNUSED(v);
1185 switch (cond_state) {
1186 case COND_ANY:
1187 fprintf(stderr, tr(44, "\"Else\" without matching \"if\"\n"));
1188 goto jleave;
1189 case COND_SEND:
1190 cond_state = COND_RCV;
1191 break;
1192 case COND_RCV:
1193 cond_state = COND_SEND;
1194 break;
1195 case COND_TERM:
1196 cond_state = COND_NOTERM;
1197 break;
1198 case COND_EXEC:
1199 cond_state = COND_NOEXEC;
1200 break;
1201 case COND_NOEXEC:
1202 cond_state = COND_EXEC;
1203 break;
1204 default:
1205 fprintf(stderr, tr(45, "Mail's idea of conditions is screwed up\n"));
1206 cond_state = COND_ANY;
1207 goto jleave;
1209 rv = 0;
1210 jleave:
1211 NYD_LEAVE;
1212 return rv;
1215 FL int
1216 c_endif(void *v)
1218 int rv;
1219 NYD_ENTER;
1220 UNUSED(v);
1222 if (cond_state == COND_ANY) {
1223 fprintf(stderr, tr(46, "\"Endif\" without matching \"if\"\n"));
1224 rv = 1;
1225 } else {
1226 cond_state = COND_ANY;
1227 rv = 0;
1229 NYD_LEAVE;
1230 return rv;
1233 FL int
1234 alternates(void *v)
1236 size_t l;
1237 char **namelist = v, **ap, **ap2, *cp;
1238 NYD_ENTER;
1240 l = argcount(namelist) + 1;
1241 if (l == 1) {
1242 if (altnames == NULL)
1243 goto jleave;
1244 for (ap = altnames; *ap != NULL; ++ap)
1245 printf("%s ", *ap);
1246 printf("\n");
1247 goto jleave;
1250 if (altnames != NULL) {
1251 for (ap = altnames; *ap != NULL; ++ap)
1252 free(*ap);
1253 free(altnames);
1255 altnames = smalloc(l * sizeof(char*));
1256 for (ap = namelist, ap2 = altnames; *ap != NULL; ++ap, ++ap2) {
1257 l = strlen(*ap) + 1;
1258 cp = smalloc(l);
1259 memcpy(cp, *ap, l);
1260 *ap2 = cp;
1262 *ap2 = NULL;
1263 jleave:
1264 NYD_LEAVE;
1265 return 0;
1268 FL int
1269 newmail(void *v)
1271 int val = 1, mdot;
1272 NYD_ENTER;
1273 UNUSED(v);
1275 if (
1276 #ifdef HAVE_IMAP
1277 (mb.mb_type != MB_IMAP || imap_newmail(1)) &&
1278 #endif
1279 (val = setfile(mailname, 1)) == 0) {
1280 mdot = getmdot(1);
1281 setdot(&message[mdot - 1]);
1283 NYD_LEAVE;
1284 return val;
1287 FL int
1288 shortcut(void *v)
1290 char **args = v;
1291 struct shortcut *s;
1292 int rv;
1293 NYD_ENTER;
1295 if (args[0] == NULL) {
1296 list_shortcuts();
1297 rv = 0;
1298 goto jleave;
1301 rv = 1;
1302 if (args[1] == NULL) {
1303 fprintf(stderr, tr(220, "expansion name for shortcut missing\n"));
1304 goto jleave;
1306 if (args[2] != NULL) {
1307 fprintf(stderr, tr(221, "too many arguments\n"));
1308 goto jleave;
1311 if ((s = get_shortcut(args[0])) != NULL) {
1312 free(s->sh_long);
1313 s->sh_long = sstrdup(args[1]);
1314 } else {
1315 s = scalloc(1, sizeof *s);
1316 s->sh_short = sstrdup(args[0]);
1317 s->sh_long = sstrdup(args[1]);
1318 s->sh_next = shortcuts;
1319 shortcuts = s;
1321 rv = 0;
1322 jleave:
1323 NYD_LEAVE;
1324 return rv;
1327 FL struct shortcut *
1328 get_shortcut(char const *str)
1330 struct shortcut *s;
1331 NYD_ENTER;
1333 for (s = shortcuts; s != NULL; s = s->sh_next)
1334 if (!strcmp(str, s->sh_short))
1335 break;
1336 NYD_LEAVE;
1337 return s;
1340 FL int
1341 unshortcut(void *v)
1343 char **args = v;
1344 bool_t errs = FAL0;
1345 NYD_ENTER;
1347 if (args[0] == NULL) {
1348 fprintf(stderr, tr(222, "need shortcut names to remove\n"));
1349 errs = TRU1;
1350 goto jleave;
1353 while (*args != NULL) {
1354 if (delete_shortcut(*args) != OKAY) {
1355 errs = TRU1;
1356 fprintf(stderr, tr(223, "%s: no such shortcut\n"), *args);
1358 ++args;
1360 jleave:
1361 NYD_LEAVE;
1362 return errs;
1365 FL int
1366 cflag(void *v)
1368 struct message *m;
1369 int *msgvec = v, *ip;
1370 NYD_ENTER;
1372 for (ip = msgvec; *ip != 0; ++ip) {
1373 m = &message[*ip - 1];
1374 setdot(m);
1375 if ((m->m_flag & (MFLAG | MFLAGGED)) == 0)
1376 m->m_flag |= MFLAG | MFLAGGED;
1378 NYD_LEAVE;
1379 return 0;
1382 FL int
1383 cunflag(void *v)
1385 struct message *m;
1386 int *msgvec = v, *ip;
1387 NYD_ENTER;
1389 for (ip = msgvec; *ip != 0; ++ip) {
1390 m = &message[*ip - 1];
1391 setdot(m);
1392 if (m->m_flag & (MFLAG | MFLAGGED)) {
1393 m->m_flag &= ~(MFLAG | MFLAGGED);
1394 m->m_flag |= MUNFLAG;
1397 NYD_LEAVE;
1398 return 0;
1401 FL int
1402 canswered(void *v)
1404 struct message *m;
1405 int *msgvec = v, *ip;
1406 NYD_ENTER;
1408 for (ip = msgvec; *ip != 0; ++ip) {
1409 m = &message[*ip - 1];
1410 setdot(m);
1411 if ((m->m_flag & (MANSWER | MANSWERED)) == 0)
1412 m->m_flag |= MANSWER | MANSWERED;
1414 NYD_LEAVE;
1415 return 0;
1418 FL int
1419 cunanswered(void *v)
1421 struct message *m;
1422 int *msgvec = v, *ip;
1423 NYD_ENTER;
1425 for (ip = msgvec; *ip != 0; ++ip) {
1426 m = &message[*ip - 1];
1427 setdot(m);
1428 if (m->m_flag & (MANSWER | MANSWERED)) {
1429 m->m_flag &= ~(MANSWER | MANSWERED);
1430 m->m_flag |= MUNANSWER;
1433 NYD_LEAVE;
1434 return 0;
1437 FL int
1438 cdraft(void *v)
1440 struct message *m;
1441 int *msgvec = v, *ip;
1442 NYD_ENTER;
1444 for (ip = msgvec; *ip != 0; ++ip) {
1445 m = &message[*ip - 1];
1446 setdot(m);
1447 if ((m->m_flag & (MDRAFT | MDRAFTED)) == 0)
1448 m->m_flag |= MDRAFT | MDRAFTED;
1450 NYD_LEAVE;
1451 return 0;
1454 FL int
1455 cundraft(void *v)
1457 struct message *m;
1458 int *msgvec = v, *ip;
1459 NYD_ENTER;
1461 for (ip = msgvec; *ip != 0; ++ip) {
1462 m = &message[*ip - 1];
1463 setdot(m);
1464 if (m->m_flag & (MDRAFT | MDRAFTED)) {
1465 m->m_flag &= ~(MDRAFT | MDRAFTED);
1466 m->m_flag |= MUNDRAFT;
1469 NYD_LEAVE;
1470 return 0;
1473 FL int
1474 cnoop(void *v)
1476 int rv = 0;
1477 NYD_ENTER;
1478 UNUSED(v);
1480 switch (mb.mb_type) {
1481 case MB_IMAP:
1482 #ifdef HAVE_IMAP
1483 imap_noop();
1484 #else
1485 rv = ccmdnotsupp(NULL);
1486 #endif
1487 break;
1488 case MB_POP3:
1489 #ifdef HAVE_POP3
1490 pop3_noop();
1491 #else
1492 rv = ccmdnotsupp(NULL);
1493 #endif
1494 break;
1495 default:
1496 break;
1498 NYD_LEAVE;
1499 return rv;
1502 FL int
1503 cremove(void *v)
1505 char const *fmt;
1506 size_t fmt_len;
1507 char **args = v, *name;
1508 int ec = 0;
1509 NYD_ENTER;
1511 if (*args == NULL) {
1512 fprintf(stderr, tr(290, "Syntax is: remove mailbox ...\n"));
1513 ec = 1;
1514 goto jleave;
1517 fmt = tr(287, "Remove \"%s\" (y/n) ? ");
1518 fmt_len = strlen(fmt);
1519 do {
1520 if ((name = expand(*args)) == NULL)
1521 continue;
1523 if (!strcmp(name, mailname)) {
1524 fprintf(stderr, tr(286, "Cannot remove current mailbox \"%s\".\n"),
1525 name);
1526 ec |= 1;
1527 continue;
1530 size_t vl = strlen(name) + fmt_len +1;
1531 char *vb = ac_alloc(vl);
1532 bool_t asw;
1533 snprintf(vb, vl, fmt, name);
1534 asw = getapproval(vb, TRU1);
1535 ac_free(vb);
1536 if (!asw)
1537 continue;
1540 switch (which_protocol(name)) {
1541 case PROTO_FILE:
1542 if (unlink(name) < 0) { /* TODO do not handle .gz .bz2 */
1543 perror(name);
1544 ec |= 1;
1546 break;
1547 case PROTO_POP3:
1548 fprintf(stderr, tr(288, "Cannot remove POP3 mailbox \"%s\".\n"),name);
1549 ec |= 1;
1550 break;
1551 case PROTO_IMAP:
1552 #ifdef HAVE_IMAP
1553 if (imap_remove(name) != OKAY)
1554 #endif
1555 ec |= 1;
1556 break;
1557 case PROTO_MAILDIR:
1558 if (maildir_remove(name) != OKAY)
1559 ec |= 1;
1560 break;
1561 case PROTO_UNKNOWN:
1562 fprintf(stderr, tr(289, "Unknown protocol in \"%s\". Not removed.\n"),
1563 name);
1564 ec |= 1;
1565 break;
1567 } while (*++args != NULL);
1568 jleave:
1569 NYD_LEAVE;
1570 return ec;
1573 FL int
1574 crename(void *v)
1576 char **args = v, *old, *new;
1577 enum protocol oldp, newp;
1578 int ec;
1579 NYD_ENTER;
1581 ec = 1;
1583 if (args[0] == NULL || args[1] == NULL || args[2] != NULL) {
1584 fprintf(stderr, "Syntax: rename old new\n");
1585 goto jleave;
1588 if ((old = expand(args[0])) == NULL)
1589 goto jleave;
1590 oldp = which_protocol(old);
1591 if ((new = expand(args[1])) == NULL)
1592 goto jleave;
1593 newp = which_protocol(new);
1595 if (!strcmp(old, mailname) || !strcmp(new, mailname)) {
1596 fprintf(stderr, tr(291, "Cannot rename current mailbox \"%s\".\n"), old);
1597 goto jleave;
1599 if ((oldp == PROTO_IMAP || newp == PROTO_IMAP) && oldp != newp) {
1600 fprintf(stderr, tr(292, "Can only rename folders of same type.\n"));
1601 goto jleave;
1604 ec = 0;
1606 if (newp == PROTO_POP3)
1607 goto jnopop3;
1608 switch (oldp) {
1609 case PROTO_FILE:
1610 if (link(old, new) == -1) {
1611 switch (errno) {
1612 case EACCES:
1613 case EEXIST:
1614 case ENAMETOOLONG:
1615 case ENOENT:
1616 case ENOSPC:
1617 case EXDEV:
1618 perror(new);
1619 break;
1620 default:
1621 perror(old);
1623 ec |= 1;
1624 } else if (unlink(old) == -1) {
1625 perror(old);
1626 ec |= 1;
1628 break;
1629 case PROTO_MAILDIR:
1630 if (rename(old, new) == -1) {
1631 perror(old);
1632 ec |= 1;
1634 break;
1635 case PROTO_POP3:
1636 jnopop3:
1637 fprintf(stderr, tr(293, "Cannot rename POP3 mailboxes.\n"));
1638 ec |= 1;
1639 break;
1640 #ifdef HAVE_IMAP
1641 case PROTO_IMAP:
1642 if (imap_rename(old, new) != OKAY)
1643 ec |= 1;
1644 break;
1645 #endif
1646 case PROTO_UNKNOWN:
1647 default:
1648 fprintf(stderr, tr(294,
1649 "Unknown protocol in \"%s\" and \"%s\". Not renamed.\n"), old, new);
1650 ec |= 1;
1651 break;
1653 jleave:
1654 NYD_LEAVE;
1655 return ec;
1658 /* vim:set fenc=utf-8:s-it-mode */