-S: do not (re)set variable after resources etc. if c_set() fails!
[s-mailx.git] / cmd2.c
blob215368d1dfcfaf7fc1183b1ff05c480c142e6e52
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ More user commands.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2015 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. 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 cmd2
38 #ifndef HAVE_AMALGAMATION
39 # include "nail.h"
40 #endif
42 /* Save/copy the indicated messages at the end of the passed file name.
43 * If mark is true, mark the message "saved" */
44 static int save1(char *str, int domark, char const *cmd,
45 struct ignoretab *ignoret, int convert, int sender_record,
46 int domove);
48 /* Snarf the file from the end of the command line and return a pointer to it.
49 * If there is no file attached, return the mbox file. Put a null in front of
50 * the file name so that the message list processing won't see it, unless the
51 * file name is the only thing on the line, in which case, return 0 in the
52 * reference flag variable */
53 static char * snarf(char *linebuf, bool_t *flag, bool_t usembox);
55 /* Delete the indicated messages. Set dot to some nice place afterwards */
56 static int delm(int *msgvec);
58 static int
59 save1(char *str, int domark, char const *cmd, struct ignoretab *ignoret,
60 int convert, int sender_record, int domove)
62 ui64_t mstats[1], tstats[2];
63 struct stat st;
64 int last = 0, *msgvec, *ip;
65 struct message *mp;
66 char *file = NULL, *cp, *cq;
67 char const *disp = "", *shell = NULL;
68 FILE *obuf;
69 bool_t success = FAL0, isflag;
70 NYD_ENTER;
72 msgvec = salloc((msgCount + 2) * sizeof *msgvec);
73 if (sender_record) {
74 for (cp = str; *cp != '\0' && spacechar(*cp); ++cp)
76 isflag = (*cp != '\0');
77 } else {
78 if ((file = snarf(str, &isflag, convert != SEND_TOFILE)) == NULL)
79 goto jleave;
80 while(spacechar(*file))
81 ++file;
82 if (*file == '|') {
83 ++file;
84 shell = ok_vlook(SHELL);
88 if (!isflag) {
89 *msgvec = first(0, MMNORM);
90 msgvec[1] = 0;
91 } else if (getmsglist(str, msgvec, 0) < 0)
92 goto jleave;
93 if (*msgvec == 0) {
94 if (pstate & (PS_HOOK_MASK | PS_ROBOT)) {
95 success = TRU1;
96 goto jleave;
98 printf(_("No messages to %s.\n"), cmd);
99 goto jleave;
102 if (sender_record) {
103 if ((cp = nameof(message + *msgvec - 1, 0)) == NULL) {
104 printf(_("Cannot determine message sender to %s.\n"), cmd);
105 goto jleave;
108 for (cq = cp; *cq != '\0' && *cq != '@'; cq++)
110 *cq = '\0';
111 if (ok_blook(outfolder)) {
112 size_t sz = strlen(cp) +1;
113 file = salloc(sz + 1);
114 file[0] = '+';
115 memcpy(file + 1, cp, sz);
116 } else
117 file = cp;
120 /* Pipe target is special TODO hacked in later, normalize flow! */
121 if (shell != NULL) {
122 if ((obuf = Popen(file, "w", shell, NULL, 1)) == NULL) {
123 int esave = errno;
125 n_perr(file, esave);
126 errno = esave;
127 goto jleave;
129 isflag = FAL0;
130 disp = _("[Piped]");
131 goto jsend;
134 if ((file = expand(file)) == NULL)
135 goto jleave;
137 obuf = ((convert == SEND_TOFILE) ? Fopen(file, "a+") : Zopen(file, "a+"));
138 if (obuf == NULL) {
139 obuf = ((convert == SEND_TOFILE) ? Fopen(file, "wx") : Zopen(file, "wx"));
140 if (obuf == NULL) {
141 n_perr(file, 0);
142 goto jleave;
144 isflag = TRU1;
145 disp = _("[New file]");
146 } else {
147 isflag = FAL0;
148 disp = _("[Appended]");
151 /* TODO RETURN check, but be aware of protocols: v15: Mailbox->lock()! */
152 n_file_lock(fileno(obuf), FLT_WRITE, 0,0, UIZ_MAX);
154 if (!isflag && !fstat(fileno(obuf), &st) && S_ISREG(st.st_mode) &&
155 fseek(obuf, -2L, SEEK_END) == 0) {
156 char buf[2];
157 int prependnl = 0;
159 switch (fread(buf, sizeof *buf, 2, obuf)) {
160 case 2:
161 if (buf[1] != '\n') {
162 prependnl = 1;
163 break;
165 /* FALLTHRU */
166 case 1:
167 if (buf[0] != '\n')
168 prependnl = 1;
169 break;
170 default:
171 if (ferror(obuf)) {
172 n_perr(file, 0);
173 goto jleave;
175 prependnl = 0;
178 fflush(obuf);
179 if (prependnl) {
180 putc('\n', obuf);
181 fflush(obuf);
185 jsend:
186 success = TRU1;
187 tstats[0] = tstats[1] = 0;
189 srelax_hold();
190 for (ip = msgvec; *ip != 0 && UICMP(z, PTR2SIZE(ip - msgvec), <, msgCount);
191 ++ip) {
192 mp = message + *ip - 1;
193 if (sendmp(mp, obuf, ignoret, NULL, convert, mstats) < 0) {
194 success = FAL0;
195 goto jferr;
197 srelax();
199 touch(mp);
200 if (domark)
201 mp->m_flag |= MSAVED;
202 if (domove) {
203 mp->m_flag |= MDELETED | MSAVED;
204 last = *ip;
207 tstats[0] += mstats[0];
208 tstats[1] += mp->m_lines;/* TODO won't work, need target! v15!! */
210 srelax_rele();
212 fflush(obuf);
213 if (ferror(obuf)) {
214 jferr:
215 n_perr(file, 0);
216 if (!success)
217 srelax_rele();
218 success = FAL0;
220 if (shell != NULL) {
221 if (!Pclose(obuf, TRU1))
222 success = FAL0;
223 } else if (Fclose(obuf) != 0)
224 success = FAL0;
226 if (success) {
227 printf("\"%s\" %s %" /*PRIu64 "/%"*/ PRIu64 " bytes\n",
228 file, disp, /*tstats[1], TODO v15: lines written */ tstats[0]);
229 } else if (domark) {
230 for (ip = msgvec; *ip != 0 &&
231 UICMP(z, PTR2SIZE(ip - msgvec), <, msgCount); ++ip) {
232 mp = message + *ip - 1;
233 mp->m_flag &= ~MSAVED;
235 } else if (domove) {
236 for (ip = msgvec; *ip != 0 &&
237 UICMP(z, PTR2SIZE(ip - msgvec), <, msgCount); ++ip) {
238 mp = message + *ip - 1;
239 mp->m_flag &= ~(MSAVED | MDELETED);
243 if (domove && last && success) {
244 setdot(message + last - 1);
245 last = first(0, MDELETED);
246 setdot(message + (last != 0 ? last - 1 : 0));
248 jleave:
249 NYD_LEAVE;
250 return (success == FAL0);
253 static char *
254 snarf(char *linebuf, bool_t *flag, bool_t usembox)
256 char *cp;
257 NYD_ENTER;
259 if ((cp = laststring(linebuf, flag, TRU1)) == NULL) {
260 if (usembox) {
261 *flag = FAL0;
262 cp = expand("&");
263 } else
264 n_err(_("No file specified\n"));
266 NYD_LEAVE;
267 return cp;
270 static int
271 delm(int *msgvec)
273 struct message *mp;
274 int rv = -1, *ip, last;
275 NYD_ENTER;
277 last = 0;
278 for (ip = msgvec; *ip != 0; ++ip) {
279 mp = message + *ip - 1;
280 touch(mp);
281 mp->m_flag |= MDELETED | MTOUCH;
282 mp->m_flag &= ~(MPRESERVE | MSAVED | MBOX);
283 last = *ip;
285 if (last != 0) {
286 setdot(message + last - 1);
287 last = first(0, MDELETED);
288 if (last != 0) {
289 setdot(message + last - 1);
290 rv = 0;
291 } else {
292 setdot(message);
295 NYD_LEAVE;
296 return rv;
299 FL int
300 c_next(void *v)
302 int list[2], *ip, *ip2, mdot, *msgvec = v, rv = 1;
303 struct message *mp;
304 NYD_ENTER;
306 if (*msgvec != 0) {
307 /* If some messages were supplied, find the first applicable one
308 * following dot using wrap around */
309 mdot = (int)PTR2SIZE(dot - message + 1);
311 /* Find first message in supplied message list which follows dot */
312 for (ip = msgvec; *ip != 0; ++ip) {
313 if ((mb.mb_threaded ? message[*ip - 1].m_threadpos > dot->m_threadpos
314 : *ip > mdot))
315 break;
317 if (*ip == 0)
318 ip = msgvec;
319 ip2 = ip;
320 do {
321 mp = message + *ip2 - 1;
322 if (!(mp->m_flag & MMNDEL)) {
323 setdot(mp);
324 goto jhitit;
326 if (*ip2 != 0)
327 ++ip2;
328 if (*ip2 == 0)
329 ip2 = msgvec;
330 } while (ip2 != ip);
331 printf(_("No messages applicable\n"));
332 goto jleave;
335 /* If this is the first command, select message 1. Note that this must
336 * exist for us to get here at all */
337 if (!(pstate & PS_SAW_COMMAND)) {
338 if (msgCount == 0)
339 goto jateof;
340 goto jhitit;
343 /* Just find the next good message after dot, no wraparound */
344 if (mb.mb_threaded == 0) {
345 for (mp = dot + !!(pstate & PS_DID_PRINT_DOT);
346 PTRCMP(mp, <, message + msgCount); ++mp)
347 if (!(mp->m_flag & MMNORM))
348 break;
349 } else {
350 /* TODO The threading code had some bugs that caused crashes.
351 * TODO The last thing (before the deep look) happens here,
352 * TODO so let's not trust PS_DID_PRINT_DOT but check & hope it fixes */
353 if ((mp = dot) != NULL && (pstate & PS_DID_PRINT_DOT))
354 mp = next_in_thread(mp);
355 while (mp != NULL && (mp->m_flag & MMNORM))
356 mp = next_in_thread(mp);
358 if (mp == NULL || PTRCMP(mp, >=, message + msgCount)) {
359 jateof:
360 printf(_("At EOF\n"));
361 rv = 0;
362 goto jleave;
364 setdot(mp);
366 /* Print dot */
367 jhitit:
368 list[0] = (int)PTR2SIZE(dot - message + 1);
369 list[1] = 0;
370 rv = c_type(list);
371 jleave:
372 NYD_LEAVE;
373 return rv;
376 FL int
377 c_dotmove(void *v)
379 char const *args;
380 int msgvec[2], rv;
381 NYD_ENTER;
383 if (*(args = v) == '\0' || args[1] != '\0') {
384 jerr:
385 n_err(_("Synopsis: dotmove: up <-> or down <+> by one message\n"));
386 rv = 1;
387 } else switch (args[0]) {
388 case '-':
389 case '+':
390 if (msgCount == 0) {
391 printf(_("At EOF\n"));
392 rv = 0;
393 } else if (getmsglist(UNCONST(/*TODO*/ args), msgvec, 0) > 0) {
394 setdot(message + msgvec[0] - 1);
395 msgvec[1] = 0;
396 rv = c_headers(msgvec);
397 } else
398 rv = 1;
399 break;
400 default:
401 goto jerr;
403 NYD_LEAVE;
404 return rv;
407 FL int
408 c_save(void *v)
410 char *str = v;
411 int rv;
412 NYD_ENTER;
414 rv = save1(str, 1, "save", saveignore, SEND_MBOX, 0, 0);
415 NYD_LEAVE;
416 return rv;
419 FL int
420 c_Save(void *v)
422 char *str = v;
423 int rv;
424 NYD_ENTER;
426 rv = save1(str, 1, "save", saveignore, SEND_MBOX, 1, 0);
427 NYD_LEAVE;
428 return rv;
431 FL int
432 c_copy(void *v)
434 char *str = v;
435 int rv;
436 NYD_ENTER;
438 rv = save1(str, 0, "copy", saveignore, SEND_MBOX, 0, 0);
439 NYD_LEAVE;
440 return rv;
443 FL int
444 c_Copy(void *v)
446 char *str = v;
447 int rv;
448 NYD_ENTER;
450 rv = save1(str, 0, "copy", saveignore, SEND_MBOX, 1, 0);
451 NYD_LEAVE;
452 return rv;
455 FL int
456 c_move(void *v)
458 char *str = v;
459 int rv;
460 NYD_ENTER;
462 rv = save1(str, 0, "move", saveignore, SEND_MBOX, 0, 1);
463 NYD_LEAVE;
464 return rv;
467 FL int
468 c_Move(void *v)
470 char *str = v;
471 int rv;
472 NYD_ENTER;
474 rv = save1(str, 0, "move", saveignore, SEND_MBOX, 1, 1);
475 NYD_LEAVE;
476 return rv;
479 FL int
480 c_decrypt(void *v)
482 char *str = v;
483 int rv;
484 NYD_ENTER;
486 rv = save1(str, 0, "decrypt", saveignore, SEND_DECRYPT, 0, 0);
487 NYD_LEAVE;
488 return rv;
491 FL int
492 c_Decrypt(void *v)
494 char *str = v;
495 int rv;
496 NYD_ENTER;
498 rv = save1(str, 0, "decrypt", saveignore, SEND_DECRYPT, 1, 0);
499 NYD_LEAVE;
500 return rv;
503 FL int
504 c_write(void *v)
506 char *str = v;
507 int rv;
508 NYD_ENTER;
510 if (str == NULL || *str == '\0')
511 str = savestr("/dev/null");
512 rv = save1(str, 0, "write", allignore, SEND_TOFILE, 0, 0);
513 NYD_LEAVE;
514 return rv;
517 FL int
518 c_delete(void *v)
520 int *msgvec = v;
521 NYD_ENTER;
523 delm(msgvec);
524 NYD_LEAVE;
525 return 0;
528 FL int
529 c_deltype(void *v)
531 int list[2], rv = 0, *msgvec = v, lastdot;
532 NYD_ENTER;
534 lastdot = (int)PTR2SIZE(dot - message + 1);
535 if (delm(msgvec) >= 0) {
536 list[0] = (int)PTR2SIZE(dot - message + 1);
537 if (list[0] > lastdot) {
538 touch(dot);
539 list[1] = 0;
540 rv = c_type(list);
541 goto jleave;
543 printf(_("At EOF\n"));
544 } else
545 printf(_("No more messages\n"));
546 jleave:
547 NYD_LEAVE;
548 return rv;
551 FL int
552 c_undelete(void *v)
554 int *msgvec = v, *ip;
555 struct message *mp;
556 NYD_ENTER;
558 for (ip = msgvec; *ip != 0 && UICMP(z, PTR2SIZE(ip - msgvec), <, msgCount);
559 ++ip) {
560 mp = message + *ip - 1;
561 touch(mp);
562 setdot(mp);
563 if (mp->m_flag & (MDELETED | MSAVED))
564 mp->m_flag &= ~(MDELETED | MSAVED);
565 else
566 mp->m_flag &= ~MDELETED;
568 NYD_LEAVE;
569 return 0;
572 FL int
573 c_stouch(void *v)
575 int *msgvec = v, *ip;
576 NYD_ENTER;
578 for (ip = msgvec; *ip != 0; ++ip) {
579 setdot(message + *ip - 1);
580 dot->m_flag |= MTOUCH;
581 dot->m_flag &= ~MPRESERVE;
582 pstate |= PS_DID_PRINT_DOT;
584 NYD_LEAVE;
585 return 0;
588 FL int
589 c_mboxit(void *v)
591 int *msgvec = v, *ip;
592 NYD_ENTER;
594 if (pstate & PS_EDIT) {
595 n_err(_("`mbox' can only be used in a system mailbox\n")); /* TODO */
596 goto jleave;
599 for (ip = msgvec; *ip != 0; ++ip) {
600 setdot(message + *ip - 1);
601 dot->m_flag |= MTOUCH | MBOX;
602 dot->m_flag &= ~MPRESERVE;
603 pstate |= PS_DID_PRINT_DOT;
605 jleave:
606 NYD_LEAVE;
607 return 0;
610 FL int
611 c_preserve(void *v)
613 int *msgvec = v, *ip, mesg, rv = 1;
614 struct message *mp;
615 NYD_ENTER;
617 if (pstate & PS_EDIT) {
618 printf(_("Cannot \"preserve\" in a system mailbox\n"));
619 goto jleave;
622 for (ip = msgvec; *ip != 0; ++ip) {
623 mesg = *ip;
624 mp = message + mesg - 1;
625 mp->m_flag |= MPRESERVE;
626 mp->m_flag &= ~MBOX;
627 setdot(mp);
628 pstate |= PS_DID_PRINT_DOT;
630 rv = 0;
631 jleave:
632 NYD_LEAVE;
633 return rv;
636 FL int
637 c_unread(void *v)
639 int *msgvec = v, *ip;
640 NYD_ENTER;
642 for (ip = msgvec; *ip != 0; ++ip) {
643 setdot(message + *ip - 1);
644 dot->m_flag &= ~(MREAD | MTOUCH);
645 dot->m_flag |= MSTATUS;
646 pstate |= PS_DID_PRINT_DOT;
648 NYD_LEAVE;
649 return 0;
652 FL int
653 c_seen(void *v)
655 int *msgvec = v, *ip;
656 NYD_ENTER;
658 for (ip = msgvec; *ip != 0; ++ip) {
659 struct message *mp = message + *ip - 1;
660 setdot(mp);
661 touch(mp);
663 NYD_LEAVE;
664 return 0;
667 FL int
668 c_flag(void *v)
670 struct message *m;
671 int *msgvec = v, *ip;
672 NYD_ENTER;
674 for (ip = msgvec; *ip != 0; ++ip) {
675 m = message + *ip - 1;
676 setdot(m);
677 if (!(m->m_flag & (MFLAG | MFLAGGED)))
678 m->m_flag |= MFLAG | MFLAGGED;
680 NYD_LEAVE;
681 return 0;
684 FL int
685 c_unflag(void *v)
687 struct message *m;
688 int *msgvec = v, *ip;
689 NYD_ENTER;
691 for (ip = msgvec; *ip != 0; ++ip) {
692 m = message + *ip - 1;
693 setdot(m);
694 if (m->m_flag & (MFLAG | MFLAGGED)) {
695 m->m_flag &= ~(MFLAG | MFLAGGED);
696 m->m_flag |= MUNFLAG;
699 NYD_LEAVE;
700 return 0;
703 FL int
704 c_answered(void *v)
706 struct message *m;
707 int *msgvec = v, *ip;
708 NYD_ENTER;
710 for (ip = msgvec; *ip != 0; ++ip) {
711 m = message + *ip - 1;
712 setdot(m);
713 if (!(m->m_flag & (MANSWER | MANSWERED)))
714 m->m_flag |= MANSWER | MANSWERED;
716 NYD_LEAVE;
717 return 0;
720 FL int
721 c_unanswered(void *v)
723 struct message *m;
724 int *msgvec = v, *ip;
725 NYD_ENTER;
727 for (ip = msgvec; *ip != 0; ++ip) {
728 m = message + *ip - 1;
729 setdot(m);
730 if (m->m_flag & (MANSWER | MANSWERED)) {
731 m->m_flag &= ~(MANSWER | MANSWERED);
732 m->m_flag |= MUNANSWER;
735 NYD_LEAVE;
736 return 0;
739 FL int
740 c_draft(void *v)
742 struct message *m;
743 int *msgvec = v, *ip;
744 NYD_ENTER;
746 for (ip = msgvec; *ip != 0; ++ip) {
747 m = message + *ip - 1;
748 setdot(m);
749 if (!(m->m_flag & (MDRAFT | MDRAFTED)))
750 m->m_flag |= MDRAFT | MDRAFTED;
752 NYD_LEAVE;
753 return 0;
756 FL int
757 c_undraft(void *v)
759 struct message *m;
760 int *msgvec = v, *ip;
761 NYD_ENTER;
763 for (ip = msgvec; *ip != 0; ++ip) {
764 m = message + *ip - 1;
765 setdot(m);
766 if (m->m_flag & (MDRAFT | MDRAFTED)) {
767 m->m_flag &= ~(MDRAFT | MDRAFTED);
768 m->m_flag |= MUNDRAFT;
771 NYD_LEAVE;
772 return 0;
775 /* s-it-mode */