sopen(): add missing space to error message
[s-mailx.git] / cmd3.c
blob39dcd362072fc6489a1221a31db0b8c008673b4a
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 - 2016 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
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. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
35 #undef n_FILE
36 #define n_FILE cmd3
38 #ifndef HAVE_AMALGAMATION
39 # include "nail.h"
40 #endif
42 static char *_bang_buf;
43 static size_t _bang_size;
45 /* Modify subject we reply to to begin with Re: if it does not already */
46 static char * _reedit(char *subj);
48 /* Expand the shell escape by expanding unescaped !'s into the last issued
49 * command where possible */
50 static void _bangexp(char **str, size_t *size);
52 static void make_ref_and_cs(struct message *mp, struct header *head);
54 /* `reply' and `Lreply' workhorse */
55 static int _list_reply(int *msgvec, enum header_flags hf);
57 /* Get PTF to implementation of command c (i.e., take care for *flipr*) */
58 static int (* _reply_or_Reply(char c))(int *, bool_t);
60 /* Reply to a single message. Extract each name from the message header and
61 * send them off to mail1() */
62 static int _reply(int *msgvec, bool_t recipient_record);
64 /* Reply to a series of messages by simply mailing to the senders and not
65 * messing around with the To: and Cc: lists as in normal reply */
66 static int _Reply(int *msgvec, bool_t recipient_record);
68 /* Forward a message to a new recipient, in the sense of RFC 2822 */
69 static int _fwd(char *str, int recipient_record);
71 /* Modify the subject we are replying to to begin with Fwd: */
72 static char * __fwdedit(char *subj);
74 /* Do the real work of resending */
75 static int _resend1(void *v, bool_t add_resent);
77 /* c_file, c_File */
78 static int _c_file(void *v, enum fedit_mode fm);
80 static char *
81 _reedit(char *subj)
83 struct str in, out;
84 char *newsubj = NULL;
85 NYD_ENTER;
87 if (subj == NULL || *subj == '\0')
88 goto jleave;
90 in.s = subj;
91 in.l = strlen(subj);
92 mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV);
94 if ((newsubj = subject_re_trim(out.s)) != out.s)
95 newsubj = savestr(out.s);
96 else {
97 /* RFC mandates english "Re: " */
98 newsubj = salloc(out.l + 4 +1);
99 sstpcpy(sstpcpy(newsubj, "Re: "), out.s);
102 free(out.s);
103 jleave:
104 NYD_LEAVE;
105 return newsubj;
108 static void
109 _bangexp(char **str, size_t *size)
111 char *bangbuf;
112 int changed = 0;
113 bool_t dobang;
114 size_t sz, i, j, bangbufsize;
115 NYD_ENTER;
117 dobang = ok_blook(bang);
119 bangbuf = smalloc(bangbufsize = *size);
120 i = j = 0;
121 while ((*str)[i]) {
122 if (dobang) {
123 if ((*str)[i] == '!') {
124 sz = strlen(_bang_buf);
125 bangbuf = srealloc(bangbuf, bangbufsize += sz);
126 ++changed;
127 memcpy(bangbuf + j, _bang_buf, sz + 1);
128 j += sz;
129 i++;
130 continue;
133 if ((*str)[i] == '\\' && (*str)[i + 1] == '!') {
134 bangbuf[j++] = '!';
135 i += 2;
136 ++changed;
138 bangbuf[j++] = (*str)[i++];
140 bangbuf[j] = '\0';
141 if (changed) {
142 printf("!%s\n", bangbuf);
143 fflush(stdout);
145 sz = j + 1;
146 if (sz > *size)
147 *str = srealloc(*str, *size = sz);
148 memcpy(*str, bangbuf, sz);
149 if (sz > _bang_size)
150 _bang_buf = srealloc(_bang_buf, _bang_size = sz);
151 memcpy(_bang_buf, bangbuf, sz);
152 free(bangbuf);
153 NYD_LEAVE;
156 static void
157 make_ref_and_cs(struct message *mp, struct header *head) /* TODO rewrite FAST */
159 char *oldref, *oldmsgid, *newref, *cp;
160 size_t oldreflen = 0, oldmsgidlen = 0, reflen;
161 unsigned i;
162 struct name *n;
163 NYD_ENTER;
165 oldref = hfield1("references", mp);
166 oldmsgid = hfield1("message-id", mp);
167 if (oldmsgid == NULL || *oldmsgid == '\0') {
168 head->h_ref = NULL;
169 goto jleave;
172 reflen = 1;
173 if (oldref) {
174 oldreflen = strlen(oldref);
175 reflen += oldreflen + 2;
177 if (oldmsgid) {
178 oldmsgidlen = strlen(oldmsgid);
179 reflen += oldmsgidlen;
182 newref = smalloc(reflen);
183 if (oldref != NULL) {
184 memcpy(newref, oldref, oldreflen +1);
185 if (oldmsgid != NULL) {
186 newref[oldreflen++] = ',';
187 newref[oldreflen++] = ' ';
188 memcpy(newref + oldreflen, oldmsgid, oldmsgidlen +1);
190 } else if (oldmsgid)
191 memcpy(newref, oldmsgid, oldmsgidlen +1);
192 n = extract(newref, GREF);
193 free(newref);
195 /* Limit number of references TODO better on parser side */
196 while (n->n_flink != NULL)
197 n = n->n_flink;
198 for (i = 1; i <= REFERENCES_MAX; ++i) {
199 if (n->n_blink != NULL)
200 n = n->n_blink;
201 else
202 break;
204 n->n_blink = NULL;
205 head->h_ref = n;
206 if (ok_blook(reply_in_same_charset) &&
207 (cp = hfield1("content-type", mp)) != NULL)
208 head->h_charset = mime_param_get("charset", cp);
209 jleave:
210 NYD_LEAVE;
213 static int
214 _list_reply(int *msgvec, enum header_flags hf)
216 struct header head;
217 struct message *mp;
218 char const *reply_to, *rcv, *cp;
219 enum gfield gf;
220 struct name *rt, *mft, *np;
221 int *save_msgvec;
222 NYD_ENTER;
224 /* TODO Since we may recur and do stuff with message lists we need to save
225 * TODO away the argument vector as long as that isn't done by machinery */
227 size_t i;
228 for (i = 0; msgvec[i] != 0; ++i)
230 ++i;
231 save_msgvec = ac_alloc(sizeof(*save_msgvec) * i);
232 while (i-- > 0)
233 save_msgvec[i] = msgvec[i];
234 msgvec = save_msgvec;
237 jnext_msg:
238 mp = message + *msgvec - 1;
239 touch(mp);
240 setdot(mp);
242 memset(&head, 0, sizeof head);
243 head.h_flags = hf;
245 head.h_subject = _reedit(hfield1("subject", mp));
246 gf = ok_blook(fullnames) ? GFULL : GSKIN;
247 rt = mft = NULL;
249 rcv = NULL;
250 if ((reply_to = hfield1("reply-to", mp)) != NULL &&
251 (cp = ok_vlook(reply_to_honour)) != NULL &&
252 (rt = checkaddrs(lextract(reply_to, GTO | gf), EACM_STRICT, NULL)
253 ) != NULL) {
254 char const *tr = _("Reply-To %s%s");
255 size_t l = strlen(tr) + strlen(rt->n_name) + 3 +1;
256 char *sp = salloc(l);
258 snprintf(sp, l, tr, rt->n_name, (rt->n_flink != NULL ? "..." : n_empty));
259 if (quadify(cp, UIZ_MAX, sp, TRU1) > FAL0)
260 rcv = reply_to;
263 if (rcv == NULL && (rcv = hfield1("from", mp)) == NULL)
264 rcv = nameof(mp, 1);
266 /* Cc: */
267 np = NULL;
268 if (ok_blook(recipients_in_cc) && (cp = hfield1("to", mp)) != NULL)
269 np = lextract(cp, GCC | gf);
270 if ((cp = hfield1("cc", mp)) != NULL)
271 np = cat(np, lextract(cp, GCC | gf));
272 if (np != NULL)
273 head.h_cc = delete_alternates(np);
275 /* To: */
276 np = NULL;
277 if (rcv != NULL)
278 np = (rcv == reply_to) ? namelist_dup(rt, GTO | gf)
279 : lextract(rcv, GTO | gf);
280 if (!ok_blook(recipients_in_cc) && (cp = hfield1("to", mp)) != NULL)
281 np = cat(np, lextract(cp, GTO | gf));
282 /* Delete my name from reply list, and with it, all my alternate names */
283 np = delete_alternates(np);
284 if (count(np) == 0)
285 np = lextract(rcv, GTO | gf);
286 head.h_to = np;
288 /* The user may have send this to himself, don't ignore that */
289 namelist_vaporise_head(&head, EACM_NORMAL, FAL0, NULL);
290 if (head.h_to == NULL)
291 head.h_to = np;
293 /* Mail-Followup-To: */
294 mft = NULL;
295 if (ok_vlook(followup_to_honour) != NULL &&
296 (cp = hfield1("mail-followup-to", mp)) != NULL &&
297 (mft = np = checkaddrs(lextract(cp, GTO | gf), EACM_STRICT, NULL)
298 ) != NULL) {
299 char const *tr = _("Followup-To %s%s");
300 size_t l = strlen(tr) + strlen(np->n_name) + 3 +1;
301 char *sp = salloc(l);
303 snprintf(sp, l, tr, np->n_name, (np->n_flink != NULL ? "..." : n_empty));
304 if (quadify(ok_vlook(followup_to_honour), UIZ_MAX, sp, TRU1) > FAL0) {
305 head.h_cc = NULL;
306 head.h_to = np;
307 head.h_mft =
308 mft = namelist_vaporise_head(&head, EACM_STRICT, FAL0, NULL);
309 } else
310 mft = NULL;
313 /* Special massage for list (follow-up) messages */
314 if (mft != NULL || (hf & HF_LIST_REPLY) || ok_blook(followup_to)) {
315 /* Learn about a possibly sending mailing list; use do for break; */
316 if ((cp = hfield1("list-post", mp)) != NULL) do {
317 struct name *x;
319 if ((x = lextract(cp, GEXTRA | GSKIN)) == NULL || x->n_flink != NULL ||
320 (cp = url_mailto_to_address(x->n_name)) == NULL ||
321 /* XXX terribly wasteful to create a new name, and can't we find
322 * XXX a way to mitigate that?? */
323 is_addr_invalid(x = nalloc(cp, GEXTRA | GSKIN), EACM_STRICT)) {
324 if (options & OPT_D_V)
325 n_err(_("Message contains invalid List-Post: header\n"));
326 cp = NULL;
327 break;
329 cp = x->n_name;
331 /* A special case has been seen on e.g. ietf-announce@ietf.org:
332 * these usually post to multiple groups, with ietf-announce@
333 * in List-Post:, but with Reply-To: set to ietf@ietf.org (since
334 * -announce@ is only used for announcements, say).
335 * So our desire is to honour this request and actively overwrite
336 * List-Post: for our purpose; but only if its a single address.
337 * However, to avoid ambiguities with users that place themselve in
338 * Reply-To: and mailing lists which don't overwrite this (or only
339 * extend this, shall such exist), only do so if reply_to exists of
340 * a single address which points to the same domain as List-Post: */
341 if (reply_to != NULL && rt->n_flink == NULL &&
342 name_is_same_domain(x, rt))
343 cp = rt->n_name; /* rt is EACM_STRICT tested */
345 /* "Automatically `mlist'" the List-Post: address temporarily */
346 if (is_mlist(cp, FAL0) == MLIST_OTHER)
347 head.h_list_post = cp;
348 else
349 cp = NULL;
350 } while (0);
352 /* In case of list replies we actively sort out any non-list recipient,
353 * but _only_ if we did not honour a MFT:, assuming that members of MFT
354 * were there for a reason; cp is still List-Post:/eqivalent */
355 if ((hf & HF_LIST_REPLY) && mft == NULL) {
356 struct name *nhp = head.h_to;
357 head.h_to = NULL;
358 j_lt_redo:
359 while (nhp != NULL) {
360 np = nhp;
361 nhp = nhp->n_flink;
363 if ((cp != NULL && !asccasecmp(cp, np->n_name)) ||
364 is_mlist(np->n_name, FAL0) != MLIST_OTHER) {
365 np->n_type = (np->n_type & ~GMASK) | GTO;
366 np->n_flink = head.h_to;
367 head.h_to = np;
370 if ((nhp = head.h_cc) != NULL) {
371 head.h_cc = NULL;
372 goto j_lt_redo;
377 make_ref_and_cs(mp, &head);
379 if (ok_blook(quote_as_attachment)) {
380 head.h_attach = csalloc(1, sizeof *head.h_attach);
381 head.h_attach->a_msgno = *msgvec;
382 head.h_attach->a_content_description = _("Original message content");
385 if (mail1(&head, 1, mp, NULL, !!(hf & HF_RECIPIENT_RECORD), 0) == OKAY &&
386 ok_blook(markanswered) && !(mp->m_flag & MANSWERED))
387 mp->m_flag |= MANSWER | MANSWERED;
389 if (*++msgvec != 0) {
390 /* TODO message (error) ring.., less sleep */
391 printf(_("Waiting a second before proceeding to the next message..\n"));
392 fflush(stdout);
393 n_msleep(1000, FAL0);
394 goto jnext_msg;
397 ac_free(save_msgvec);
398 NYD_LEAVE;
399 return 0;
402 static int
403 (*_reply_or_Reply(char c))(int *, bool_t)
405 int (*rv)(int*, bool_t);
406 NYD_ENTER;
408 rv = (ok_blook(flipr) ^ (c == 'R')) ? &_Reply : &_reply;
409 NYD_LEAVE;
410 return rv;
413 static int
414 _reply(int *msgvec, bool_t recipient_record)
416 int rv;
417 NYD_ENTER;
419 rv = _list_reply(msgvec, recipient_record ? HF_RECIPIENT_RECORD : HF_NONE);
420 NYD_LEAVE;
421 return rv;
424 static int
425 _Reply(int *msgvec, bool_t recipient_record)
427 struct header head;
428 struct message *mp;
429 int *ap;
430 char *cp;
431 enum gfield gf;
432 NYD_ENTER;
434 memset(&head, 0, sizeof head);
435 gf = ok_blook(fullnames) ? GFULL : GSKIN;
437 for (ap = msgvec; *ap != 0; ++ap) {
438 char const *rp;
439 struct name *rt;
441 mp = message + *ap - 1;
442 touch(mp);
443 setdot(mp);
445 if ((rp = hfield1("reply-to", mp)) != NULL &&
446 (cp = ok_vlook(reply_to_honour)) != NULL &&
447 (rt = checkaddrs(lextract(rp, GTO | gf), EACM_STRICT, NULL)
448 ) != NULL) {
449 char const *tr = _("Reply-To %s%s");
450 size_t l = strlen(tr) + strlen(rt->n_name) + 3 +1;
451 char *sp = salloc(l);
453 snprintf(sp, l, tr, rt->n_name, (rt->n_flink != NULL ? "..."
454 : n_empty));
455 if (quadify(cp, UIZ_MAX, sp, TRU1) > FAL0) {
456 head.h_to = cat(head.h_to, rt);
457 continue;
461 if ((cp = hfield1("from", mp)) == NULL)
462 cp = nameof(mp, 2);
463 head.h_to = cat(head.h_to, lextract(cp, GTO | gf));
465 if (head.h_to == NULL)
466 goto jleave;
468 mp = message + msgvec[0] - 1;
469 head.h_subject = hfield1("subject", mp);
470 head.h_subject = _reedit(head.h_subject);
471 make_ref_and_cs(mp, &head);
473 if (ok_blook(quote_as_attachment)) {
474 head.h_attach = csalloc(1, sizeof *head.h_attach);
475 head.h_attach->a_msgno = *msgvec;
476 head.h_attach->a_content_description = _("Original message content");
479 if (mail1(&head, 1, mp, NULL, recipient_record, 0) == OKAY &&
480 ok_blook(markanswered) && !(mp->m_flag & MANSWERED))
481 mp->m_flag |= MANSWER | MANSWERED;
482 jleave:
483 NYD_LEAVE;
484 return 0;
487 static int
488 _fwd(char *str, int recipient_record)
490 struct header head;
491 int *msgvec, rv = 1;
492 char *recipient;
493 struct message *mp;
494 bool_t f, forward_as_attachment;
495 NYD_ENTER;
497 if ((recipient = laststring(str, &f, TRU1)) == NULL) {
498 puts(_("No recipient specified."));
499 goto jleave;
502 forward_as_attachment = ok_blook(forward_as_attachment);
503 msgvec = salloc((msgCount + 2) * sizeof *msgvec);
505 if (!f) {
506 *msgvec = first(0, MMNORM);
507 if (*msgvec == 0) {
508 if (pstate & (PS_HOOK_MASK | PS_ROBOT)) {
509 rv = 0;
510 goto jleave;
512 printf(_("No messages to forward.\n"));
513 goto jleave;
515 msgvec[1] = 0;
516 } else if (getmsglist(str, msgvec, 0) < 0)
517 goto jleave;
519 if (*msgvec == 0) {
520 if (pstate & (PS_HOOK_MASK | PS_ROBOT)) {
521 rv = 0;
522 goto jleave;
524 printf(_("No applicable messages.\n"));
525 goto jleave;
527 if (msgvec[1] != 0) {
528 printf(_("Cannot forward multiple messages at once\n"));
529 goto jleave;
532 memset(&head, 0, sizeof head);
533 if ((head.h_to = lextract(recipient,
534 (GTO | (ok_blook(fullnames) ? GFULL : GSKIN)))) == NULL)
535 goto jleave;
537 mp = message + *msgvec - 1;
539 if (forward_as_attachment) {
540 head.h_attach = csalloc(1, sizeof *head.h_attach);
541 head.h_attach->a_msgno = *msgvec;
542 head.h_attach->a_content_description = _("Forwarded message");
543 } else {
544 touch(mp);
545 setdot(mp);
547 head.h_subject = hfield1("subject", mp);
548 head.h_subject = __fwdedit(head.h_subject);
549 mail1(&head, 1, (forward_as_attachment ? NULL : mp), NULL, recipient_record,
551 rv = 0;
552 jleave:
553 NYD_LEAVE;
554 return rv;
557 static char *
558 __fwdedit(char *subj)
560 struct str in, out;
561 char *newsubj = NULL;
562 NYD_ENTER;
564 if (subj == NULL || *subj == '\0')
565 goto jleave;
567 in.s = subj;
568 in.l = strlen(subj);
569 mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV);
571 newsubj = salloc(out.l + 6);
572 memcpy(newsubj, "Fwd: ", 5); /* XXX localizable */
573 memcpy(newsubj + 5, out.s, out.l +1);
574 free(out.s);
575 jleave:
576 NYD_LEAVE;
577 return newsubj;
580 static int
581 _resend1(void *v, bool_t add_resent)
583 char *name, *str;
584 struct name *to, *sn;
585 int *ip, *msgvec;
586 bool_t f = TRU1;
587 NYD_ENTER;
589 str = v;
590 msgvec = salloc((msgCount + 2) * sizeof *msgvec);
591 name = laststring(str, &f, TRU1);
592 if (name == NULL) {
593 puts(_("No recipient specified."));
594 goto jleave;
597 if (!f) {
598 *msgvec = first(0, MMNORM);
599 if (*msgvec == 0) {
600 if (pstate & (PS_HOOK_MASK | PS_ROBOT)) {
601 f = FAL0;
602 goto jleave;
604 puts(_("No applicable messages."));
605 goto jleave;
607 msgvec[1] = 0;
608 } else if (getmsglist(str, msgvec, 0) < 0)
609 goto jleave;
611 if (*msgvec == 0) {
612 if (pstate & (PS_HOOK_MASK | PS_ROBOT)) {
613 f = FAL0;
614 goto jleave;
616 printf("No applicable messages.\n");
617 goto jleave;
620 sn = nalloc(name, GTO | GSKIN);
621 to = usermap(sn, FAL0);
622 for (ip = msgvec; *ip != 0 && UICMP(z, PTR2SIZE(ip - msgvec), <, msgCount);
623 ++ip)
624 if (resend_msg(message + *ip - 1, to, add_resent) != OKAY)
625 goto jleave;
626 f = FAL0;
627 jleave:
628 NYD_LEAVE;
629 return (f != FAL0);
632 static int
633 _c_file(void *v, enum fedit_mode fm)
635 char **argv = v;
636 int i;
637 NYD2_ENTER;
639 if (*argv == NULL) {
640 newfileinfo();
641 i = 0;
642 goto jleave;
645 if (pstate & PS_HOOK_MASK) {
646 n_err(_("Cannot change folder from within a hook\n"));
647 i = 1;
648 goto jleave;
651 save_mbox_for_possible_quitstuff();
653 i = setfile(*argv, fm);
654 if (i < 0) {
655 i = 1;
656 goto jleave;
658 assert(!(fm & FEDIT_NEWMAIL)); /* (Prevent implementation error) */
659 if (pstate & PS_SETFILE_OPENED)
660 check_folder_hook(FAL0);
662 if (i > 0) {
663 /* TODO Don't report "no messages" == 1 == error when we're in, e.g.,
664 * TODO a macro: because that recursed commando loop will terminate the
665 * TODO entire macro due to that! So either the user needs to be able
666 * TODO to react&ignore this "error" (as in "if DOSTUFF" or "DOSTUFF;
667 * TODO if $?", then "overriding an "error"), or we need a different
668 * TODO return that differentiates */
669 i = (pstate & PS_ROBOT) ? 0 : 1;
670 goto jleave;
672 if (pstate & PS_SETFILE_OPENED)
673 announce(ok_blook(bsdcompat) || ok_blook(bsdannounce));
674 i = 0;
675 jleave:
676 NYD2_LEAVE;
677 return i;
680 FL int
681 c_shell(void *v)
683 char *str = v, *cmd;
684 size_t cmdsize;
685 sigset_t mask;
686 NYD_ENTER;
688 cmd = smalloc(cmdsize = strlen(str) +1);
689 memcpy(cmd, str, cmdsize);
690 _bangexp(&cmd, &cmdsize);
692 sigemptyset(&mask);
693 run_command(ok_vlook(SHELL), &mask, COMMAND_FD_PASS, COMMAND_FD_PASS, "-c",
694 cmd, NULL, NULL);
695 printf("!\n");
697 free(cmd);
698 NYD_LEAVE;
699 return 0;
702 FL int
703 c_dosh(void *v)
705 NYD_ENTER;
706 n_UNUSED(v);
708 run_command(ok_vlook(SHELL), 0, COMMAND_FD_PASS, COMMAND_FD_PASS, NULL,
709 NULL, NULL, NULL);
710 putchar('\n');
711 NYD_LEAVE;
712 return 0;
715 FL int
716 c_cwd(void *v)
718 char buf[PATH_MAX]; /* TODO getcwd(3) may return a larger value */
719 NYD_ENTER;
721 if (getcwd(buf, sizeof buf) != NULL) {
722 puts(buf);
723 v = (void*)0x1;
724 } else {
725 n_perr(_("getcwd"), 0);
726 v = NULL;
728 NYD_LEAVE;
729 return (v == NULL);
732 FL int
733 c_chdir(void *v)
735 char **arglist = v;
736 char const *cp;
737 NYD_ENTER;
739 if (*arglist == NULL)
740 cp = ok_vlook(HOME);
741 else if ((cp = fexpand(*arglist, FEXP_LOCAL | FEXP_NOPROTO)) == NULL)
742 goto jleave;
743 if (chdir(cp) == -1) {
744 n_perr(cp, 0);
745 cp = NULL;
747 jleave:
748 NYD_LEAVE;
749 return (cp == NULL);
752 FL int
753 c_reply(void *v)
755 int rv;
756 NYD_ENTER;
758 rv = (*_reply_or_Reply('r'))(v, FAL0);
759 NYD_LEAVE;
760 return rv;
763 FL int
764 c_replyall(void *v)
766 int rv;
767 NYD_ENTER;
769 rv = _reply(v, FAL0);
770 NYD_LEAVE;
771 return rv;
774 FL int
775 c_replysender(void *v)
777 int rv;
778 NYD_ENTER;
780 rv = _Reply(v, FAL0);
781 NYD_LEAVE;
782 return rv;
785 FL int
786 c_Reply(void *v)
788 int rv;
789 NYD_ENTER;
791 rv = (*_reply_or_Reply('R'))(v, FAL0);
792 NYD_LEAVE;
793 return rv;
796 FL int
797 c_Lreply(void *v)
799 int rv;
800 NYD_ENTER;
802 rv = _list_reply(v, HF_LIST_REPLY);
803 NYD_LEAVE;
804 return rv;
807 FL int
808 c_followup(void *v)
810 int rv;
811 NYD_ENTER;
813 rv = (*_reply_or_Reply('r'))(v, TRU1);
814 NYD_LEAVE;
815 return rv;
818 FL int
819 c_followupall(void *v)
821 int rv;
822 NYD_ENTER;
824 rv = _reply(v, TRU1);
825 NYD_LEAVE;
826 return rv;
829 FL int
830 c_followupsender(void *v)
832 int rv;
833 NYD_ENTER;
835 rv = _Reply(v, TRU1);
836 NYD_LEAVE;
837 return rv;
840 FL int
841 c_Followup(void *v)
843 int rv;
844 NYD_ENTER;
846 rv = (*_reply_or_Reply('R'))(v, TRU1);
847 NYD_LEAVE;
848 return rv;
851 FL int
852 c_forward(void *v)
854 int rv;
855 NYD_ENTER;
857 rv = _fwd(v, 0);
858 NYD_LEAVE;
859 return rv;
862 FL int
863 c_Forward(void *v)
865 int rv;
866 NYD_ENTER;
868 rv = _fwd(v, 1);
869 NYD_LEAVE;
870 return rv;
873 FL int
874 c_resend(void *v)
876 int rv;
877 NYD_ENTER;
879 rv = _resend1(v, TRU1);
880 NYD_LEAVE;
881 return rv;
884 FL int
885 c_Resend(void *v)
887 int rv;
888 NYD_ENTER;
890 rv = _resend1(v, FAL0);
891 NYD_LEAVE;
892 return rv;
895 FL int
896 c_messize(void *v)
898 int *msgvec = v, *ip, mesg;
899 struct message *mp;
900 NYD_ENTER;
902 for (ip = msgvec; *ip != 0; ++ip) {
903 mesg = *ip;
904 mp = message + mesg - 1;
905 printf("%d: ", mesg);
906 if (mp->m_xlines > 0)
907 printf("%ld", mp->m_xlines);
908 else
909 putchar(' ');
910 printf("/%lu\n", (ul_i)mp->m_xsize);
912 NYD_LEAVE;
913 return 0;
916 FL int
917 c_file(void *v)
919 int rv;
920 NYD_ENTER;
922 rv = _c_file(v, FEDIT_NONE);
923 NYD_LEAVE;
924 return rv;
927 FL int
928 c_File(void *v)
930 int rv;
931 NYD_ENTER;
933 rv = _c_file(v, FEDIT_RDONLY);
934 NYD_LEAVE;
935 return rv;
938 FL int
939 c_echo(void *v){
940 char const **argv, **ap, *cp;
941 NYD_ENTER;
943 for(ap = argv = v; *ap != NULL; ++ap){
944 if(ap != argv)
945 putchar(' ');
946 if((cp = fexpand(*ap, FEXP_NSHORTCUT | FEXP_NVAR)) == NULL)
947 cp = *ap;
948 fputs(cp, stdout);
950 putchar('\n');
951 NYD_LEAVE;
952 return 0;
955 FL int
956 c_newmail(void *v)
958 int val = 1, mdot;
959 NYD_ENTER;
960 n_UNUSED(v);
962 if ((val = setfile(mailname,
963 FEDIT_NEWMAIL | ((mb.mb_perm & MB_DELE) ? 0 : FEDIT_RDONLY))
964 ) == 0) {
965 mdot = getmdot(1);
966 setdot(message + mdot - 1);
968 NYD_LEAVE;
969 return val;
972 FL int
973 c_noop(void *v)
975 int rv = 0;
976 NYD_ENTER;
977 n_UNUSED(v);
979 switch (mb.mb_type) {
980 case MB_POP3:
981 #ifdef HAVE_POP3
982 pop3_noop();
983 #else
984 rv = c_cmdnotsupp(NULL);
985 #endif
986 break;
987 default:
988 break;
990 NYD_LEAVE;
991 return rv;
994 FL int
995 c_remove(void *v)
997 char const *fmt;
998 size_t fmt_len;
999 char **args, *name, *ename;
1000 int ec;
1001 NYD_ENTER;
1003 if (*(args = v) == NULL) {
1004 n_err(_("Synopsis: remove: <mailbox>...\n"));
1005 ec = 1;
1006 goto jleave;
1009 ec = 0;
1011 fmt = _("Remove %s");
1012 fmt_len = strlen(fmt);
1013 do {
1014 if ((name = expand(*args)) == NULL)
1015 continue;
1016 ename = n_shexp_quote_cp(name, FAL0);
1018 if (!strcmp(name, mailname)) {
1019 n_err(_("Cannot remove current mailbox %s\n"), ename);
1020 ec |= 1;
1021 continue;
1024 size_t vl = strlen(ename) + fmt_len +1;
1025 char *vb = salloc(vl);
1026 bool_t asw;
1027 snprintf(vb, vl, fmt, ename);
1028 asw = getapproval(vb, TRU1);
1029 if (!asw)
1030 continue;
1033 switch (which_protocol(name)) {
1034 case PROTO_FILE:
1035 if (unlink(name) == -1) { /* TODO do not handle .gz .bz2 .xz.. */
1036 int se = errno;
1038 if (se == EISDIR) {
1039 struct stat sb;
1041 if (!stat(name, &sb) && S_ISDIR(sb.st_mode)) {
1042 if (!rmdir(name))
1043 break;
1044 se = errno;
1047 n_perr(name, se);
1048 ec |= 1;
1050 break;
1051 case PROTO_POP3:
1052 n_err(_("Cannot remove POP3 mailbox %s\n"), ename);
1053 ec |= 1;
1054 break;
1055 case PROTO_MAILDIR:
1056 if (maildir_remove(name) != OKAY)
1057 ec |= 1;
1058 break;
1059 case PROTO_UNKNOWN:
1060 n_err(_("Not removed: unknown protocol: %s\n"), ename);
1061 ec |= 1;
1062 break;
1064 } while (*++args != NULL);
1065 jleave:
1066 NYD_LEAVE;
1067 return ec;
1070 FL int
1071 c_rename(void *v)
1073 char **args = v, *old, *new;
1074 enum protocol oldp, newp;
1075 int ec;
1076 NYD_ENTER;
1078 ec = 1;
1080 if (args[0] == NULL || args[1] == NULL || args[2] != NULL) {
1081 n_err(_("Synopsis: rename: <old> <new>\n"));
1082 goto jleave;
1085 if ((old = expand(args[0])) == NULL)
1086 goto jleave;
1087 oldp = which_protocol(old);
1088 if ((new = expand(args[1])) == NULL)
1089 goto jleave;
1090 newp = which_protocol(new);
1092 if (!strcmp(old, mailname) || !strcmp(new, mailname)) {
1093 n_err(_("Cannot rename current mailbox %s\n"),
1094 n_shexp_quote_cp(old, FAL0));
1095 goto jleave;
1098 ec = 0;
1100 if (newp == PROTO_POP3)
1101 goto jnopop3;
1102 switch (oldp) {
1103 case PROTO_FILE:
1104 if (link(old, new) == -1) {
1105 switch (errno) {
1106 case EACCES:
1107 case EEXIST:
1108 case ENAMETOOLONG:
1109 case ENOENT:
1110 case ENOSPC:
1111 case EXDEV:
1112 n_perr(new, 0);
1113 break;
1114 default:
1115 n_perr(old, 0);
1116 break;
1118 ec |= 1;
1119 } else if (unlink(old) == -1) {
1120 n_perr(old, 0);
1121 ec |= 1;
1123 break;
1124 case PROTO_MAILDIR:
1125 if (rename(old, new) == -1) {
1126 n_perr(old, 0);
1127 ec |= 1;
1129 break;
1130 case PROTO_POP3:
1131 jnopop3:
1132 n_err(_("Cannot rename POP3 mailboxes\n"));
1133 ec |= 1;
1134 break;
1135 case PROTO_UNKNOWN:
1136 default:
1137 n_err(_("Unknown protocol in %s and %s; not renamed\n"),
1138 n_shexp_quote_cp(old, FAL0), n_shexp_quote_cp(new, FAL0));
1139 ec |= 1;
1140 break;
1142 jleave:
1143 NYD_LEAVE;
1144 return ec;
1147 FL int
1148 c_urlencode(void *v) /* XXX IDNA?? */
1150 char **ap;
1151 NYD_ENTER;
1153 OBSOLETE("`urlencode': please use `urlcodec enc[ode]' instead");
1155 for (ap = v; *ap != NULL; ++ap) {
1156 char *in = *ap, *out = urlxenc(in, FAL0);
1158 if(out == NULL)
1159 out = n_UNCONST(V_(n_error));
1160 printf(" in: <%s> (%" PRIuZ " bytes)\nout: <%s> (%" PRIuZ " bytes)\n",
1161 in, strlen(in), out, strlen(out));
1163 NYD_LEAVE;
1164 return 0;
1167 FL int
1168 c_urldecode(void *v) /* XXX IDNA?? */
1170 char **ap;
1171 NYD_ENTER;
1173 OBSOLETE("`urldecode': please use `urlcodec dec[ode]' instead");
1175 for (ap = v; *ap != NULL; ++ap) {
1176 char *in = *ap, *out = urlxdec(in);
1178 if(out == NULL)
1179 out = n_UNCONST(V_(n_error));
1180 printf(" in: <%s> (%" PRIuZ " bytes)\nout: <%s> (%" PRIuZ " bytes)\n",
1181 in, strlen(in), out, strlen(out));
1183 NYD_LEAVE;
1184 return 0;
1187 /* s-it-mode */