NeoMutt  2025-12-11-980-ge38c27
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
functions.c
Go to the documentation of this file.
1
23
29
30#include "config.h"
31#include <limits.h>
32#include <stdbool.h>
33#include <stdio.h>
34#include <string.h>
35#include <sys/stat.h>
36#include "mutt/lib.h"
37#include "config/lib.h"
38#include "email/lib.h"
39#include "core/lib.h"
40#include "gui/lib.h"
41#include "functions.h"
42#include "lib.h"
43#include "attach/lib.h"
44#include "editor/lib.h"
45#include "history/lib.h"
46#include "imap/lib.h"
47#include "key/lib.h"
48#include "menu/lib.h"
49#include "nntp/lib.h"
50#include "pattern/lib.h"
51#include "question/lib.h"
52#include "send/lib.h"
53#include "globals.h"
54#include "module_data.h"
55#include "mutt_mailbox.h"
56#include "muttlib.h"
57#include "mx.h"
58#include "nntp/adata.h" // IWYU pragma: keep
59#include "nntp/mdata.h" // IWYU pragma: keep
60#include "private_data.h"
61#include "sort.h"
62
63static int op_subscribe_pattern(struct BrowserPrivateData *priv, const struct KeyEvent *event);
64
65// clang-format off
69static const struct MenuFuncOp OpBrowser[] = { /* map: browser */
70 { "catchup", OP_CATCHUP },
71 { "change-dir", OP_CHANGE_DIRECTORY },
72 { "check-new", OP_CHECK_NEW },
73 { "create-mailbox", OP_CREATE_MAILBOX },
74 { "delete-mailbox", OP_DELETE_MAILBOX },
75 { "descend-directory", OP_DESCEND_DIRECTORY },
76 { "display-filename", OP_BROWSER_TELL },
77 { "enter-mask", OP_ENTER_MASK },
78 { "goto-folder", OP_BROWSER_GOTO_FOLDER },
79 { "goto-parent", OP_GOTO_PARENT },
80 { "mailbox-list", OP_MAILBOX_LIST },
81 { "reload-active", OP_LOAD_ACTIVE },
82 { "rename-mailbox", OP_RENAME_MAILBOX },
83 { "select-new", OP_BROWSER_NEW_FILE },
84 { "sort", OP_SORT },
85 { "sort-reverse", OP_SORT_REVERSE },
86 { "subscribe", OP_BROWSER_SUBSCRIBE },
87 { "subscribe-pattern", OP_SUBSCRIBE_PATTERN },
88 { "toggle-mailboxes", OP_TOGGLE_MAILBOXES },
89 { "toggle-subscribed", OP_BROWSER_TOGGLE_LSUB },
90 { "uncatchup", OP_UNCATCHUP },
91 { "unsubscribe", OP_BROWSER_UNSUBSCRIBE },
92 { "unsubscribe-pattern", OP_UNSUBSCRIBE_PATTERN },
93 { "view-file", OP_BROWSER_VIEW_FILE },
94 // Deprecated
95 { "buffy-list", OP_MAILBOX_LIST, MFF_DEPRECATED },
96 { NULL, 0 },
97};
98
102static const struct MenuOpSeq BrowserDefaultBindings[] = { /* map: browser */
103 { OP_BROWSER_GOTO_FOLDER, "=" },
104 { OP_BROWSER_NEW_FILE, "N" },
105 { OP_BROWSER_SUBSCRIBE, "s" },
106 { OP_BROWSER_TELL, "@" },
107 { OP_BROWSER_TOGGLE_LSUB, "T" },
108 { OP_BROWSER_UNSUBSCRIBE, "u" },
109 { OP_BROWSER_VIEW_FILE, " " }, // <Space>
110 { OP_CHANGE_DIRECTORY, "c" },
111 { OP_CREATE_MAILBOX, "C" },
112 { OP_DELETE_MAILBOX, "d" },
113 { OP_ENTER_MASK, "m" },
114 { OP_GOTO_PARENT, "p" },
115 { OP_MAILBOX_LIST, "." },
116 { OP_RENAME_MAILBOX, "r" },
117 { OP_SORT, "o" },
118 { OP_SORT_REVERSE, "O" },
119 { OP_TOGGLE_MAILBOXES, "\t" }, // <Tab>
120 { 0, NULL },
121};
122// clang-format on
123
127void browser_init_keys(struct NeoMutt *n, struct SubMenu *sm_generic)
128{
130 ASSERT(mod_data);
131
132 struct MenuDefinition *md = NULL;
133 struct SubMenu *sm = NULL;
134
136 md = km_register_menu(MENU_BROWSER, "browser");
137 km_menu_add_submenu(md, sm);
138 km_menu_add_submenu(md, sm_generic);
140
141 mod_data->menu_browser = md;
142}
143
150void destroy_state(struct BrowserState *state)
151{
152 struct FolderFile *ff = NULL;
153 ARRAY_FOREACH(ff, &state->entry)
154 {
155 FREE(&ff->name);
156 FREE(&ff->desc);
157 }
158 ARRAY_FREE(&state->entry);
159 FREE(&state->folder);
160}
161
162// -----------------------------------------------------------------------------
163
179static int browser_add_selection(struct FolderFilePtrArray *ffpa, struct BrowserState *state,
180 struct Menu *menu, bool tagged, int count)
181{
182 if (!ffpa || !state || !menu)
183 return 0;
184
185 const int max = ARRAY_SIZE(&state->entry);
186
187 if (tagged)
188 {
189 struct FolderFile *ff = NULL;
190 ARRAY_FOREACH(ff, &state->entry)
191 {
192 if (ff->tagged)
193 ARRAY_ADD(ffpa, ff);
194 }
195 }
196 else
197 {
198 const int index = menu_get_index(menu);
199 if ((index < 0) || (index >= max))
200 return 0;
201
202 int n = (count > 1) ? count : 1;
203 if ((index + n) > max)
204 n = max - index;
205
206 for (int i = 0; i < n; i++)
207 {
208 struct FolderFile *ff = ARRAY_GET(&state->entry, index + i);
209 if (ff)
210 ARRAY_ADD(ffpa, ff);
211 }
212 }
213
214 return ARRAY_SIZE(ffpa);
215}
216
220static int op_browser_new_file(struct BrowserPrivateData *priv, const struct KeyEvent *event)
221{
223 struct Buffer *buf = buf_pool_get();
224 buf_printf(buf, "%s/", buf_string(&mod_data->last_dir));
225
226 struct FileCompletionData cdata = { false, priv->mailbox, NULL, NULL, NULL };
227 const int rc = mw_get_field(_("New file name: "), buf, MUTT_COMP_NONE,
228 HC_FILE, &CompleteMailboxOps, &cdata);
229 if (rc != 0)
230 {
231 buf_pool_release(&buf);
232 return FR_NO_ACTION;
233 }
234
235 buf_copy(priv->file, buf);
236 buf_pool_release(&buf);
237 priv->done = true;
238 return FR_DONE;
239}
240
247static void browser_apply_subscribe_nntp(struct FolderFilePtrArray *ffpa,
248 struct NntpAccountData *adata, bool subscribe)
249{
250 if (!ffpa || !adata)
251 return;
252
253 struct FolderFile **ffp = NULL;
254 ARRAY_FOREACH(ffp, ffpa)
255 {
256 struct FolderFile *ff = *ffp;
257 if (!ff || !ff->name)
258 continue;
259 if (subscribe)
260 mutt_newsgroup_subscribe(adata, ff->name);
261 else
263 }
264}
265
271static void browser_apply_subscribe_imap(struct FolderFilePtrArray *ffpa, bool subscribe)
272{
273 if (!ffpa)
274 return;
275
276 struct Buffer *buf = buf_pool_get();
277 struct FolderFile **ffp = NULL;
278 ARRAY_FOREACH(ffp, ffpa)
279 {
280 struct FolderFile *ff = *ffp;
281 if (!ff || !ff->name)
282 continue;
283 buf_strcpy(buf, ff->name);
284 expand_path(buf, false);
285 imap_subscribe(buf_string(buf), subscribe);
286 }
287 buf_pool_release(&buf);
288}
289
300static int op_browser_subscribe(struct BrowserPrivateData *priv, const struct KeyEvent *event)
301{
303 const bool subscribe = (event->op == OP_BROWSER_SUBSCRIBE);
304 const bool tagged = priv->menu->tag_prefix && (priv->menu->num_tagged > 0);
305
306 if (ARRAY_EMPTY(&priv->state.entry))
307 {
308 mutt_error(OptNews ? _("No newsgroups match the mask") : _("There are no mailboxes"));
309 return FR_ERROR;
310 }
311
312 struct FolderFilePtrArray ffpa = ARRAY_HEAD_INITIALIZER;
313 browser_add_selection(&ffpa, &priv->state, priv->menu, tagged, event->count);
314 if (ARRAY_EMPTY(&ffpa))
315 {
316 ARRAY_FREE(&ffpa);
317 return FR_NO_ACTION;
318 }
319 const int num = ARRAY_SIZE(&ffpa);
320
321 if (OptNews)
322 {
323 struct NntpAccountData *adata = mod_data->current_news_srv;
324 int rc = nntp_newsrc_parse(adata);
325 if (rc < 0)
326 {
327 ARRAY_FREE(&ffpa);
328 return FR_ERROR;
329 }
330
331 browser_apply_subscribe_nntp(&ffpa, adata, subscribe);
332
333 if (!tagged)
334 {
335 const int index = menu_get_index(priv->menu);
336 const int next = index + num;
337 if (next < ARRAY_SIZE(&priv->state.entry))
338 menu_set_index(priv->menu, next);
339 }
340
345 }
346 else
347 {
348 browser_apply_subscribe_imap(&ffpa, subscribe);
349 }
350
351 ARRAY_FREE(&ffpa);
352 return FR_SUCCESS;
353}
354
358static int op_browser_tell(struct BrowserPrivateData *priv, const struct KeyEvent *event)
359{
360 int index = menu_get_index(priv->menu);
361 if (ARRAY_EMPTY(&priv->state.entry))
362 return FR_ERROR;
363
364 mutt_message("%s", ARRAY_GET(&priv->state.entry, index)->name);
365 return FR_SUCCESS;
366}
367
371static int op_browser_toggle_lsub(struct BrowserPrivateData *priv, const struct KeyEvent *event)
372{
373 bool_str_toggle(NeoMutt->sub, "imap_list_subscribed", NULL);
374
375 const bool c_imap_list_subscribed = cs_subset_bool(NeoMutt->sub, "imap_list_subscribed");
376 mutt_message("set imap_list_subscribed = %s", c_imap_list_subscribed ? "yes" : "no");
377
378 mutt_unget_op(OP_CHECK_NEW);
379 return FR_SUCCESS;
380}
381
385static int op_browser_view_file(struct BrowserPrivateData *priv, const struct KeyEvent *event)
386{
388 if (ARRAY_EMPTY(&priv->state.entry))
389 {
390 mutt_error(_("No files match the file mask"));
391 return FR_ERROR;
392 }
393
394 int index = menu_get_index(priv->menu);
395 struct FolderFile *ff = ARRAY_GET(&priv->state.entry, index);
396 if (ff->selectable)
397 {
398 buf_strcpy(priv->file, ff->name);
399 priv->done = true;
400 return FR_DONE;
401 }
402 else if (S_ISDIR(ff->mode) ||
403 (S_ISLNK(ff->mode) && link_is_dir(buf_string(&mod_data->last_dir), ff->name)))
404 {
405 mutt_error(_("Can't view a directory"));
406 return FR_ERROR;
407 }
408 else
409 {
410 struct Buffer *path = buf_pool_get();
411 buf_concat_path(path, buf_string(&mod_data->last_dir), ff->name);
412 struct Body *b = mutt_make_file_attach(buf_string(path), NeoMutt->sub);
413 if (b)
414 {
415 mutt_view_attachment(NULL, b, MUTT_VA_REGULAR, NULL, NULL, priv->menu->win);
416 mutt_body_free(&b);
418 }
419 else
420 {
421 mutt_error(_("Error trying to view file"));
422 }
423 buf_pool_release(&path);
424 }
425 return FR_ERROR;
426}
427
436static struct NntpMboxData *browser_apply_catchup(struct FolderFilePtrArray *ffpa,
437 struct Mailbox *m,
438 struct NntpAccountData *adata, bool up)
439{
440 if (!ffpa || !adata)
441 return NULL;
442
443 struct NntpMboxData *mdata = NULL;
444 struct FolderFile **ffp = NULL;
445 ARRAY_FOREACH(ffp, ffpa)
446 {
447 struct FolderFile *ff = *ffp;
448 if (!ff || !ff->name)
449 continue;
450 if (up)
451 mdata = mutt_newsgroup_catchup(m, adata, ff->name);
452 else
453 mdata = mutt_newsgroup_uncatchup(m, adata, ff->name);
454 }
455 return mdata;
456}
457
468static int op_catchup(struct BrowserPrivateData *priv, const struct KeyEvent *event)
469{
471 if (!OptNews)
472 return FR_NOT_IMPL;
473
474 int rc = nntp_newsrc_parse(mod_data->current_news_srv);
475 if (rc < 0)
476 return FR_ERROR;
477
478 if (ARRAY_EMPTY(&priv->state.entry))
479 return FR_ERROR;
480
481 const bool tagged = priv->menu->tag_prefix && (priv->menu->num_tagged > 0);
482
483 struct FolderFilePtrArray ffpa = ARRAY_HEAD_INITIALIZER;
484 browser_add_selection(&ffpa, &priv->state, priv->menu, tagged, event->count);
485 if (ARRAY_EMPTY(&ffpa))
486 {
487 ARRAY_FREE(&ffpa);
488 return FR_NO_ACTION;
489 }
490 const int num = ARRAY_SIZE(&ffpa);
491
492 struct NntpMboxData *mdata = browser_apply_catchup(&ffpa, priv->mailbox,
493 mod_data->current_news_srv,
494 (event->op == OP_CATCHUP));
495 ARRAY_FREE(&ffpa);
496
497 if (mdata)
498 {
500 if (!tagged)
501 {
502 const int next = menu_get_index(priv->menu) + num;
503 if (next < priv->menu->max)
504 menu_set_index(priv->menu, next);
505 }
506 }
507
509
511 return FR_ERROR;
512}
513
521static int op_change_directory(struct BrowserPrivateData *priv, const struct KeyEvent *event)
522{
524 if (OptNews)
525 return FR_NOT_IMPL;
526
527 struct Buffer *buf = buf_pool_get();
528 buf_copy(buf, &mod_data->last_dir);
529 if (!priv->state.imap_browse)
530 {
531 /* add '/' at the end of the directory name if not already there */
532 size_t len = buf_len(buf);
533 if ((len > 0) && (buf_string(&mod_data->last_dir)[len - 1] != '/'))
534 buf_addch(buf, '/');
535 }
536
537 const int op = event->op;
538 if (op == OP_CHANGE_DIRECTORY)
539 {
540 struct FileCompletionData cdata = { false, priv->mailbox, NULL, NULL, NULL };
541 int rc = mw_get_field(_("Chdir to: "), buf, MUTT_COMP_NONE, HC_FILE,
542 &CompleteMailboxOps, &cdata);
543 if ((rc != 0) && buf_is_empty(buf))
544 {
545 buf_pool_release(&buf);
546 return FR_NO_ACTION;
547 }
548 }
549 else if (op == OP_GOTO_PARENT)
550 {
551 const int count = MAX(event->count, 1);
552 for (int i = 0; i < count; i++)
553 {
554 char prev[PATH_MAX] = { 0 };
555 mutt_str_copy(prev, buf_string(buf), sizeof(prev));
556 mutt_get_parent_path(buf_string(buf), buf->data, buf->dsize);
557 if (mutt_str_equal(buf_string(buf), prev))
558 break;
559 }
560 }
561
562 if (!buf_is_empty(buf))
563 {
564 priv->state.is_mailbox_list = false;
565 expand_path(buf, false);
566 if (imap_path_probe(buf_string(buf), NULL) == MUTT_IMAP)
567 {
568 buf_copy(&mod_data->last_dir, buf);
569 destroy_state(&priv->state);
570 init_state(&priv->state);
571 priv->state.imap_browse = true;
572 imap_browse(buf_string(&mod_data->last_dir), &priv->state);
573 browser_sort(&priv->state);
574 browser_highlight_default(&priv->state, priv->menu);
575 init_menu(&priv->state, priv->menu, priv->mailbox, priv->sbar);
576 }
577 else
578 {
579 if (buf_string(buf)[0] != '/')
580 {
581 /* in case dir is relative, make it relative to LastDir,
582 * not current working dir */
583 struct Buffer *tmp = buf_pool_get();
584 buf_concat_path(tmp, buf_string(&mod_data->last_dir), buf_string(buf));
585 buf_copy(buf, tmp);
586 buf_pool_release(&tmp);
587 }
588 /* Resolve path from <chdir>
589 * Avoids buildup such as /a/b/../../c
590 * Symlinks are always unraveled to keep code simple */
591 if (mutt_path_realpath(buf) == 0)
592 {
593 buf_pool_release(&buf);
594 return FR_ERROR;
595 }
596
597 struct stat st = { 0 };
598 if (stat(buf_string(buf), &st) == 0)
599 {
600 if (S_ISDIR(st.st_mode))
601 {
602 destroy_state(&priv->state);
603 if (examine_directory(priv->mailbox, priv->menu, &priv->state,
604 buf_string(buf), buf_string(priv->prefix)) == 0)
605 {
606 buf_copy(&mod_data->last_dir, buf);
607 }
608 else
609 {
610 mutt_error(_("Error scanning directory"));
611 if (examine_directory(priv->mailbox, priv->menu, &priv->state,
612 buf_string(&mod_data->last_dir),
613 buf_string(priv->prefix)) == -1)
614 {
615 priv->done = true;
616 return FR_ERROR;
617 }
618 }
619 browser_highlight_default(&priv->state, priv->menu);
620 init_menu(&priv->state, priv->menu, priv->mailbox, priv->sbar);
621 }
622 else
623 {
624 mutt_error(_("%s is not a directory"), buf_string(buf));
625 }
626 }
627 else
628 {
629 mutt_perror("%s", buf_string(buf));
630 }
631 }
632 }
633 buf_pool_release(&buf);
634 return FR_ERROR;
635}
636
640static int op_create_mailbox(struct BrowserPrivateData *priv, const struct KeyEvent *event)
641{
643 if (!priv->state.imap_browse)
644 {
645 mutt_error(_("Create is only supported for IMAP mailboxes"));
646 return FR_ERROR;
647 }
648
649 if (imap_mailbox_create(buf_string(&mod_data->last_dir)) != 0)
650 return FR_ERROR;
651
652 /* TODO: find a way to detect if the new folder would appear in
653 * this window, and insert it without starting over. */
654 destroy_state(&priv->state);
655 init_state(&priv->state);
656 priv->state.imap_browse = true;
657 imap_browse(buf_string(&mod_data->last_dir), &priv->state);
658 browser_sort(&priv->state);
659 browser_highlight_default(&priv->state, priv->menu);
660 init_menu(&priv->state, priv->menu, priv->mailbox, priv->sbar);
661
662 return FR_SUCCESS;
663}
664
676static int browser_apply_delete_mailbox(struct FolderFilePtrArray *ffpa,
677 struct BrowserState *state,
678 struct Mailbox *m, int *num_failed)
679{
680 int deleted = 0;
681 int failed = 0;
682
683 if (ffpa && state)
684 {
685 struct FolderFile **ffp = NULL;
686 ARRAY_FOREACH_REVERSE(ffp, ffpa)
687 {
688 struct FolderFile *ff = *ffp;
689 if (!ff || !ff->name)
690 continue;
691 if (imap_delete_mailbox(m, ff->name) != 0)
692 {
693 failed++;
694 continue;
695 }
696 FREE(&ff->name);
697 FREE(&ff->desc);
698 ARRAY_REMOVE(&state->entry, ff);
699 deleted++;
700 }
701 }
702
703 if (num_failed)
704 *num_failed = failed;
705 return deleted;
706}
707
714static int op_delete_mailbox(struct BrowserPrivateData *priv, const struct KeyEvent *event)
715{
716 if (ARRAY_EMPTY(&priv->state.entry))
717 return FR_ERROR;
718
719 const bool tagged = priv->menu->tag_prefix && (priv->menu->num_tagged > 0);
720
721 struct FolderFilePtrArray ffpa = ARRAY_HEAD_INITIALIZER;
722 browser_add_selection(&ffpa, &priv->state, priv->menu, tagged, event->count);
723 if (ARRAY_EMPTY(&ffpa))
724 {
725 ARRAY_FREE(&ffpa);
726 return FR_NO_ACTION;
727 }
728
729 // Pre-flight validation
730 struct FolderFile **ffp = NULL;
731 ARRAY_FOREACH(ffp, &ffpa)
732 {
733 struct FolderFile *ff = *ffp;
734 if (!ff->imap)
735 {
736 mutt_error(_("Delete is only supported for IMAP mailboxes"));
737 ARRAY_FREE(&ffpa);
738 return FR_ERROR;
739 }
740 // TODO(sileht): It could be better to select INBOX instead. But I
741 // don't want to manipulate Mailboxes/mailbox->account here for now.
742 // Let's just protect neomutt against crash for now. #1417
743 if (mutt_str_equal(mailbox_path(priv->mailbox), ff->name))
744 {
745 mutt_error(_("Can't delete currently selected mailbox"));
746 ARRAY_FREE(&ffpa);
747 return FR_ERROR;
748 }
749 }
750
751 const int num = ARRAY_SIZE(&ffpa);
752 char msg[128] = { 0 };
753 if (num > 1)
754 {
755 snprintf(msg, sizeof(msg), _("Really delete %d tagged mailboxes?"), num);
756 }
757 else
758 {
759 struct FolderFile *ff = *ARRAY_FIRST(&ffpa);
760 snprintf(msg, sizeof(msg), _("Really delete mailbox \"%s\"?"), ff->name);
761 }
762 if (query_yesorno(msg, MUTT_NO) != MUTT_YES)
763 {
764 mutt_message(_("Mailbox not deleted"));
765 ARRAY_FREE(&ffpa);
766 return FR_NO_ACTION;
767 }
768
769 int failed = 0;
770 const int deleted = browser_apply_delete_mailbox(&ffpa, &priv->state,
771 priv->mailbox, &failed);
772 ARRAY_FREE(&ffpa);
773
774 if (failed > 0)
775 mutt_error(_("Mailbox deletion failed"));
776 else if (num > 1)
777 mutt_message(_("%d mailboxes deleted"), deleted);
778 else if (deleted == 1)
779 mutt_message(_("Mailbox deleted"));
780
781 init_menu(&priv->state, priv->menu, priv->mailbox, priv->sbar);
782 return failed ? FR_ERROR : FR_SUCCESS;
783}
784
788static int op_enter_mask(struct BrowserPrivateData *priv, const struct KeyEvent *event)
789{
791 const struct Regex *c_mask = cs_subset_regex(NeoMutt->sub, "mask");
792 struct Buffer *buf = buf_pool_get();
793 buf_strcpy(buf, c_mask ? c_mask->pattern : NULL);
794 if (mw_get_field(_("File Mask: "), buf, MUTT_COMP_NONE, HC_OTHER, NULL, NULL) != 0)
795 {
796 buf_pool_release(&buf);
797 return FR_NO_ACTION;
798 }
799
800 buf_fix_dptr(buf);
801
802 priv->state.is_mailbox_list = false;
803 /* assume that the user wants to see everything */
804 if (buf_is_empty(buf))
805 buf_strcpy(buf, ".");
806
807 struct Buffer *errmsg = buf_pool_get();
808 int rc = cs_subset_str_string_set(NeoMutt->sub, "mask", buf_string(buf), errmsg);
809 buf_pool_release(&buf);
810 if (CSR_RESULT(rc) != CSR_SUCCESS)
811 {
812 if (!buf_is_empty(errmsg))
813 {
814 mutt_error("%s", buf_string(errmsg));
815 buf_pool_release(&errmsg);
816 }
817 return FR_ERROR;
818 }
819 buf_pool_release(&errmsg);
820
821 destroy_state(&priv->state);
822 if (priv->state.imap_browse)
823 {
824 init_state(&priv->state);
825 priv->state.imap_browse = true;
826 imap_browse(buf_string(&mod_data->last_dir), &priv->state);
827 browser_sort(&priv->state);
828 init_menu(&priv->state, priv->menu, priv->mailbox, priv->sbar);
829 }
830 else if (examine_directory(priv->mailbox, priv->menu, &priv->state,
831 buf_string(&mod_data->last_dir), NULL) == 0)
832 {
833 init_menu(&priv->state, priv->menu, priv->mailbox, priv->sbar);
834 }
835 else
836 {
837 mutt_error(_("Error scanning directory"));
838 priv->done = true;
839 return FR_ERROR;
840 }
841 priv->kill_prefix = false;
842 if (ARRAY_EMPTY(&priv->state.entry))
843 {
844 mutt_error(_("No files match the file mask"));
845 return FR_ERROR;
846 }
847 return FR_SUCCESS;
848}
849
853static int op_quit(struct BrowserPrivateData *priv, const struct KeyEvent *event)
854{
856 if (priv->multiple)
857 {
858 char **tfiles = NULL;
859
860 if (priv->menu->tag_prefix && (priv->menu->num_tagged != 0))
861 {
862 *priv->numfiles = priv->menu->num_tagged;
863 tfiles = MUTT_MEM_CALLOC(*priv->numfiles, char *);
864 size_t j = 0;
865 struct FolderFile *ff = NULL;
866 ARRAY_FOREACH(ff, &priv->state.entry)
867 {
868 if (ff->tagged)
869 {
870 struct Buffer *buf = buf_pool_get();
871 buf_concat_path(buf, buf_string(&mod_data->last_dir), ff->name);
872 expand_path(buf, false);
873 tfiles[j++] = buf_strdup(buf);
874 buf_pool_release(&buf);
875 }
876 }
877 *priv->files = tfiles;
878 }
879 else if (!buf_is_empty(priv->file)) /* no tagged entries. return selected entry */
880 {
881 *priv->numfiles = 1;
882 tfiles = MUTT_MEM_CALLOC(*priv->numfiles, char *);
883 expand_path(priv->file, false);
884 tfiles[0] = buf_strdup(priv->file);
885 *priv->files = tfiles;
886 }
887 }
888
889 priv->done = true;
890 return FR_DONE;
891}
892
896static int op_jump(struct BrowserPrivateData *priv, const struct KeyEvent *event)
897{
898 int num = event->count;
899
900 if (num == 0)
901 return FR_NO_ACTION;
902
903 struct Menu *menu = priv->menu;
904
905 if ((num < 1) || (num > menu->max))
906 {
907 mutt_warning(_("Invalid message number"));
908 return FR_ERROR;
909 }
910
911 menu_set_index(menu, num - 1); // Num is 1-based
912 return FR_SUCCESS;
913}
914
922static int op_generic_select_entry(struct BrowserPrivateData *priv, const struct KeyEvent *event)
923{
924 if (event->count > 0)
925 return op_jump(priv, event);
926
928 if (ARRAY_EMPTY(&priv->state.entry))
929 {
930 mutt_warning(_("No files match the file mask"));
931 return FR_ERROR;
932 }
933
934 const int op = event->op;
935 int index = menu_get_index(priv->menu);
936 struct FolderFile *ff = ARRAY_GET(&priv->state.entry, index);
937 if ((priv->menu->tag_prefix) && (op == OP_GENERIC_SELECT_ENTRY))
938 {
939 // Do nothing
940 }
941 else if (S_ISDIR(ff->mode) ||
942 (S_ISLNK(ff->mode) && link_is_dir(buf_string(&mod_data->last_dir), ff->name)) ||
943 ff->inferiors)
944 {
945 /* make sure this isn't a MH or maildir mailbox */
946 struct Buffer *buf = buf_pool_get();
947 if (priv->state.is_mailbox_list)
948 {
949 buf_strcpy(buf, ff->name);
950 expand_path(buf, false);
951 }
952 else if (priv->state.imap_browse)
953 {
954 buf_strcpy(buf, ff->name);
955 }
956 else
957 {
958 buf_concat_path(buf, buf_string(&mod_data->last_dir), ff->name);
959 }
960
961 enum MailboxType type = mx_path_probe(buf_string(buf));
962 buf_pool_release(&buf);
963
964 if ((op == OP_DESCEND_DIRECTORY) || (type == MUTT_MAILBOX_ERROR) ||
965 (type == MUTT_UNKNOWN) || ff->inferiors)
966 {
967 /* save the old directory */
968 buf_copy(priv->old_last_dir, &mod_data->last_dir);
969
970 if (mutt_str_equal(ff->name, ".."))
971 {
972 size_t lastdirlen = buf_len(&mod_data->last_dir);
973 if ((lastdirlen > 1) &&
974 mutt_str_equal("..", buf_string(&mod_data->last_dir) + lastdirlen - 2))
975 {
976 buf_addstr(&mod_data->last_dir, "/..");
977 }
978 else
979 {
980 char *p = NULL;
981 if (lastdirlen > 1)
982 p = strrchr(mod_data->last_dir.data + 1, '/');
983
984 if (p)
985 {
986 *p = '\0';
987 buf_fix_dptr(&mod_data->last_dir);
988 }
989 else
990 {
991 if (buf_string(&mod_data->last_dir)[0] == '/')
992 buf_strcpy(&mod_data->last_dir, "/");
993 else
994 buf_addstr(&mod_data->last_dir, "/..");
995 }
996 }
997 }
998 else if (priv->state.is_mailbox_list)
999 {
1000 buf_strcpy(&mod_data->last_dir, ff->name);
1001 expand_path(&mod_data->last_dir, false);
1002 }
1003 else if (priv->state.imap_browse)
1004 {
1005 buf_strcpy(&mod_data->last_dir, ff->name);
1006 /* tack on delimiter here */
1007
1008 /* special case "" needs no delimiter */
1009 struct Url *url = url_parse(ff->name);
1010 if (url && url->path && (ff->delim != '\0'))
1011 {
1012 buf_addch(&mod_data->last_dir, ff->delim);
1013 }
1014 url_free(&url);
1015 }
1016 else
1017 {
1018 struct Buffer *tmp = buf_pool_get();
1019 buf_concat_path(tmp, buf_string(&mod_data->last_dir), ff->name);
1020 buf_copy(&mod_data->last_dir, tmp);
1021 buf_pool_release(&tmp);
1022 }
1023
1024 destroy_state(&priv->state);
1025 if (priv->kill_prefix)
1026 {
1027 buf_reset(priv->prefix);
1028 priv->kill_prefix = false;
1029 }
1030 priv->state.is_mailbox_list = false;
1031 if (priv->state.imap_browse)
1032 {
1033 init_state(&priv->state);
1034 priv->state.imap_browse = true;
1035 imap_browse(buf_string(&mod_data->last_dir), &priv->state);
1036 browser_sort(&priv->state);
1037 }
1038 else
1039 {
1040 if (examine_directory(priv->mailbox, priv->menu, &priv->state,
1041 buf_string(&mod_data->last_dir),
1042 buf_string(priv->prefix)) == -1)
1043 {
1044 /* try to restore the old values */
1045 buf_copy(&mod_data->last_dir, priv->old_last_dir);
1046 if (examine_directory(priv->mailbox, priv->menu, &priv->state,
1047 buf_string(&mod_data->last_dir),
1048 buf_string(priv->prefix)) == -1)
1049 {
1050 buf_strcpy(&mod_data->last_dir, NeoMutt->home_dir);
1051 priv->done = true;
1052 return FR_DONE;
1053 }
1054 }
1055 /* resolve paths navigated from GUI */
1056 if (mutt_path_realpath(&mod_data->last_dir) == 0)
1057 return FR_ERROR;
1058 }
1059
1060 browser_highlight_default(&priv->state, priv->menu);
1061 init_menu(&priv->state, priv->menu, priv->mailbox, priv->sbar);
1062 priv->goto_swapper[0] = '\0';
1063 return FR_SUCCESS;
1064 }
1065 }
1066 else if (op == OP_DESCEND_DIRECTORY)
1067 {
1068 mutt_error(_("%s is not a directory"), ARRAY_GET(&priv->state.entry, index)->name);
1069 return FR_ERROR;
1070 }
1071
1072 if (priv->state.is_mailbox_list || OptNews)
1073 {
1074 buf_strcpy(priv->file, ff->name);
1075 expand_path(priv->file, false);
1076 }
1077 else if (priv->state.imap_browse)
1078 {
1079 buf_strcpy(priv->file, ff->name);
1080 }
1081 else
1082 {
1083 buf_concat_path(priv->file, buf_string(&mod_data->last_dir), ff->name);
1084 }
1085
1086 return op_quit(priv, event);
1087}
1088
1092static int op_load_active(struct BrowserPrivateData *priv, const struct KeyEvent *event)
1093{
1095 if (!OptNews)
1096 return FR_NOT_IMPL;
1097
1098 struct NntpAccountData *adata = mod_data->current_news_srv;
1099
1100 if (nntp_newsrc_parse(adata) < 0)
1101 return FR_ERROR;
1102
1103 for (size_t i = 0; i < adata->groups_num; i++)
1104 {
1105 struct NntpMboxData *mdata = adata->groups_list[i];
1106 if (mdata)
1107 mdata->deleted = true;
1108 }
1109 nntp_active_fetch(adata, true);
1112
1113 destroy_state(&priv->state);
1114 if (priv->state.is_mailbox_list)
1115 {
1116 examine_mailboxes(priv->mailbox, priv->menu, &priv->state);
1117 }
1118 else
1119 {
1120 if (examine_directory(priv->mailbox, priv->menu, &priv->state, NULL, NULL) == -1)
1121 return FR_ERROR;
1122 }
1123 init_menu(&priv->state, priv->menu, priv->mailbox, priv->sbar);
1124 return FR_SUCCESS;
1125}
1126
1130static int op_mailbox_list(struct BrowserPrivateData *priv, const struct KeyEvent *event)
1131{
1133 return FR_SUCCESS;
1134}
1135
1139static int op_rename_mailbox(struct BrowserPrivateData *priv, const struct KeyEvent *event)
1140{
1142 if (ARRAY_EMPTY(&priv->state.entry))
1143 return FR_ERROR;
1144
1145 int index = menu_get_index(priv->menu);
1146 struct FolderFile *ff = ARRAY_GET(&priv->state.entry, index);
1147 if (!ff->imap)
1148 {
1149 mutt_error(_("Rename is only supported for IMAP mailboxes"));
1150 return FR_ERROR;
1151 }
1152
1153 if (imap_mailbox_rename(ff->name) < 0)
1154 return FR_ERROR;
1155
1156 destroy_state(&priv->state);
1157 init_state(&priv->state);
1158 priv->state.imap_browse = true;
1159 imap_browse(buf_string(&mod_data->last_dir), &priv->state);
1160 browser_sort(&priv->state);
1161 browser_highlight_default(&priv->state, priv->menu);
1162 init_menu(&priv->state, priv->menu, priv->mailbox, priv->sbar);
1163
1164 return FR_SUCCESS;
1165}
1166
1174static int op_sort(struct BrowserPrivateData *priv, const struct KeyEvent *event)
1175{
1176 bool resort = true;
1177 int sort = -1;
1178 const int op = event->op;
1179 int reverse = (op == OP_SORT_REVERSE);
1180
1181 switch (mw_multi_choice((reverse) ?
1182 /* L10N: The highlighted letters must match the "Sort" options */
1183 _("Reverse sort by (d)ate, (a)lpha, si(z)e, d(e)scription, (c)ount, ne(w) count, or do(n)'t sort?") :
1184 /* L10N: The highlighted letters must match the "Reverse Sort" options */
1185 _("Sort by (d)ate, (a)lpha, si(z)e, d(e)scription, (c)ount, ne(w) count, or do(n)'t sort?"),
1186 /* L10N: These must match the highlighted letters from "Sort" and "Reverse Sort" */
1187 _("dazecwn")))
1188 {
1189 case -1: /* abort */
1190 resort = false;
1191 break;
1192
1193 case 1: /* (d)ate */
1194 sort = BROWSER_SORT_DATE;
1195 break;
1196
1197 case 2: /* (a)lpha */
1198 sort = BROWSER_SORT_ALPHA;
1199 break;
1200
1201 case 3: /* si(z)e */
1202 sort = BROWSER_SORT_SIZE;
1203 break;
1204
1205 case 4: /* d(e)scription */
1206 sort = BROWSER_SORT_DESC;
1207 break;
1208
1209 case 5: /* (c)ount */
1210 sort = BROWSER_SORT_COUNT;
1211 break;
1212
1213 case 6: /* ne(w) count */
1214 sort = BROWSER_SORT_NEW;
1215 break;
1216
1217 case 7: /* do(n)'t sort */
1218 sort = BROWSER_SORT_UNSORTED;
1219 break;
1220 }
1221
1222 if (!resort)
1223 return FR_NO_ACTION;
1224
1225 sort |= reverse ? SORT_REVERSE : 0;
1226 cs_subset_str_native_set(NeoMutt->sub, "browser_sort", sort, NULL);
1227 browser_sort(&priv->state);
1228 browser_highlight_default(&priv->state, priv->menu);
1230 return FR_SUCCESS;
1231}
1232
1240static int op_subscribe_pattern(struct BrowserPrivateData *priv, const struct KeyEvent *event)
1241{
1243 if (!OptNews)
1244 return FR_NOT_IMPL;
1245
1246 struct NntpAccountData *adata = mod_data->current_news_srv;
1247 regex_t rx = { 0 };
1248 int index = menu_get_index(priv->menu);
1249
1250 char tmp2[256] = { 0 };
1251
1252 const int op = event->op;
1253 struct Buffer *buf = buf_pool_get();
1254 if (op == OP_SUBSCRIBE_PATTERN)
1255 snprintf(tmp2, sizeof(tmp2), _("Subscribe pattern: "));
1256 else
1257 snprintf(tmp2, sizeof(tmp2), _("Unsubscribe pattern: "));
1258 /* buf comes from the buffer pool, so defaults to size 1024 */
1259 if ((mw_get_field(tmp2, buf, MUTT_COMP_NONE, HC_PATTERN, &CompletePatternOps, NULL) != 0) ||
1260 buf_is_empty(buf))
1261 {
1262 buf_pool_release(&buf);
1263 return FR_NO_ACTION;
1264 }
1265
1266 int err = REG_COMP(&rx, buf->data, REG_NOSUB);
1267 if (err != 0)
1268 {
1269 regerror(err, &rx, buf->data, buf->dsize);
1270 regfree(&rx);
1271 mutt_error("%s", buf_string(buf));
1272 buf_pool_release(&buf);
1273 return FR_ERROR;
1274 }
1276 index = 0;
1277 buf_pool_release(&buf);
1278
1279 int rc = nntp_newsrc_parse(adata);
1280 if (rc < 0)
1281 return FR_ERROR;
1282
1283 struct FolderFile *ff = NULL;
1284 ARRAY_FOREACH_FROM(ff, &priv->state.entry, index)
1285 {
1286 if (regexec(&rx, ff->name, 0, NULL, 0) == 0)
1287 {
1288 if (op == OP_SUBSCRIBE_PATTERN)
1289 mutt_newsgroup_subscribe(adata, ff->name);
1290 else
1291 mutt_newsgroup_unsubscribe(adata, ff->name);
1292 }
1293 }
1294
1295 if (op == OP_SUBSCRIBE_PATTERN)
1296 {
1297 for (size_t j = 0; j < adata->groups_num; j++)
1298 {
1299 struct NntpMboxData *mdata = adata->groups_list[j];
1300 if (mdata && mdata->group && !mdata->subscribed)
1301 {
1302 if (regexec(&rx, mdata->group, 0, NULL, 0) == 0)
1303 {
1305 browser_add_folder(priv->menu, &priv->state, mdata->group, NULL, NULL, NULL, mdata);
1306 }
1307 }
1308 }
1309 init_menu(&priv->state, priv->menu, priv->mailbox, priv->sbar);
1310 }
1311 if (rc > 0)
1316 regfree(&rx);
1317 return FR_SUCCESS;
1318}
1319
1328static int op_toggle_mailboxes(struct BrowserPrivateData *priv, const struct KeyEvent *event)
1329{
1331 if (priv->state.is_mailbox_list)
1332 {
1334 }
1335
1336 const int op = event->op;
1337 if (op == OP_TOGGLE_MAILBOXES)
1338 {
1340 }
1341
1342 if (op == OP_BROWSER_GOTO_FOLDER)
1343 {
1344 /* When in mailboxes mode, disables this feature */
1345 const char *const c_folder = cs_subset_string(NeoMutt->sub, "folder");
1346 if (c_folder)
1347 {
1348 mutt_debug(LL_DEBUG3, "= hit! Folder: %s, LastDir: %s\n", c_folder,
1349 buf_string(&mod_data->last_dir));
1350 if (priv->goto_swapper[0] == '\0')
1351 {
1352 if (!mutt_str_equal(buf_string(&mod_data->last_dir), c_folder))
1353 {
1354 /* Stores into goto_swapper LastDir, and swaps to `$folder` */
1355 mutt_str_copy(priv->goto_swapper, buf_string(&mod_data->last_dir),
1356 sizeof(priv->goto_swapper));
1357 buf_copy(&mod_data->last_dir_backup, &mod_data->last_dir);
1358 buf_strcpy(&mod_data->last_dir, c_folder);
1359 }
1360 }
1361 else
1362 {
1363 buf_copy(&mod_data->last_dir_backup, &mod_data->last_dir);
1364 buf_strcpy(&mod_data->last_dir, priv->goto_swapper);
1365 priv->goto_swapper[0] = '\0';
1366 }
1367 }
1368 }
1369 destroy_state(&priv->state);
1370 buf_reset(priv->prefix);
1371 priv->kill_prefix = false;
1372
1373 if (priv->state.is_mailbox_list)
1374 {
1375 examine_mailboxes(priv->mailbox, priv->menu, &priv->state);
1376 }
1377 else if (imap_path_probe(buf_string(&mod_data->last_dir), NULL) == MUTT_IMAP)
1378 {
1379 init_state(&priv->state);
1380 priv->state.imap_browse = true;
1381 imap_browse(buf_string(&mod_data->last_dir), &priv->state);
1382 browser_sort(&priv->state);
1383 }
1384 else if (examine_directory(priv->mailbox, priv->menu, &priv->state,
1385 buf_string(&mod_data->last_dir),
1386 buf_string(priv->prefix)) == -1)
1387 {
1388 priv->done = true;
1389 return FR_ERROR;
1390 }
1391 init_menu(&priv->state, priv->menu, priv->mailbox, priv->sbar);
1392 if (priv->state.is_mailbox_list)
1394 return FR_ERROR;
1395}
1396
1397// -----------------------------------------------------------------------------
1398
1402static const struct BrowserFunction BrowserFunctions[] = {
1403 // clang-format off
1404 { OP_BROWSER_GOTO_FOLDER, op_toggle_mailboxes },
1405 { OP_BROWSER_NEW_FILE, op_browser_new_file },
1406 { OP_BROWSER_SUBSCRIBE, op_browser_subscribe },
1407 { OP_BROWSER_TELL, op_browser_tell },
1408 { OP_BROWSER_TOGGLE_LSUB, op_browser_toggle_lsub },
1409 { OP_BROWSER_UNSUBSCRIBE, op_browser_subscribe },
1410 { OP_BROWSER_VIEW_FILE, op_browser_view_file },
1411 { OP_CATCHUP, op_catchup },
1412 { OP_CHANGE_DIRECTORY, op_change_directory },
1413 { OP_CHECK_NEW, op_toggle_mailboxes },
1414 { OP_CREATE_MAILBOX, op_create_mailbox },
1415 { OP_DELETE_MAILBOX, op_delete_mailbox },
1416 { OP_DESCEND_DIRECTORY, op_generic_select_entry },
1417 { OP_ENTER_MASK, op_enter_mask },
1418 { OP_EXIT, op_quit },
1419 { OP_GENERIC_SELECT_ENTRY, op_generic_select_entry },
1420 { OP_GOTO_PARENT, op_change_directory },
1421 { OP_LOAD_ACTIVE, op_load_active },
1422 { OP_MAILBOX_LIST, op_mailbox_list },
1423 { OP_QUIT, op_quit },
1424 { OP_RENAME_MAILBOX, op_rename_mailbox },
1425 { OP_SORT, op_sort },
1426 { OP_SORT_REVERSE, op_sort },
1427 { OP_SUBSCRIBE_PATTERN, op_subscribe_pattern },
1428 { OP_TOGGLE_MAILBOXES, op_toggle_mailboxes },
1429 { OP_UNCATCHUP, op_catchup },
1430 { OP_UNSUBSCRIBE_PATTERN, op_subscribe_pattern },
1431 { 0, NULL },
1432 // clang-format on
1433};
1434
1441int browser_function_dispatcher(struct MuttWindow *win, const struct KeyEvent *event)
1442{
1443 // The Dispatcher may be called on any Window in the Dialog
1444 struct MuttWindow *dlg = dialog_find(win);
1445 if (!dlg || !dlg->wdata)
1446 return FR_ERROR;
1447
1448 struct Menu *menu = dlg->wdata;
1449 struct BrowserPrivateData *priv = menu->mdata;
1450 if (!priv)
1451 return FR_ERROR;
1452
1453 int rc = FR_UNKNOWN;
1454 for (size_t i = 0; BrowserFunctions[i].op != OP_NULL; i++)
1455 {
1456 const struct BrowserFunction *fn = &BrowserFunctions[i];
1457 if (fn->op == event->op)
1458 {
1459 rc = fn->function(priv, event);
1460 break;
1461 }
1462 }
1463
1464 return rc;
1465}
#define ARRAY_FIRST(head)
Convenience method to get the first element.
Definition array.h:136
#define ARRAY_ADD(head, elem)
Add an element at the end of the array.
Definition array.h:157
#define ARRAY_FOREACH_REVERSE(elem, head)
Iterate backwards over all elements of the array.
Definition array.h:274
#define ARRAY_REMOVE(head, elem)
Remove an entry from the array, shifting down the subsequent entries.
Definition array.h:355
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition array.h:223
#define ARRAY_EMPTY(head)
Check if an array is empty.
Definition array.h:74
#define ARRAY_SIZE(head)
The number of elements stored.
Definition array.h:87
#define ARRAY_FREE(head)
Release all memory.
Definition array.h:209
#define ARRAY_FOREACH_FROM(elem, head, from)
Iterate from an index to the end.
Definition array.h:235
#define ARRAY_GET(head, idx)
Return the element at index.
Definition array.h:109
#define ARRAY_HEAD_INITIALIZER
Static initializer for arrays.
Definition array.h:58
GUI display the mailboxes in a side panel.
int bool_str_toggle(struct ConfigSubset *sub, const char *name, struct Buffer *err)
Toggle the value of a bool.
Definition bool.c:231
int imap_browse(const char *path, struct BrowserState *state)
IMAP hook into the folder browser.
Definition browse.c:196
int imap_mailbox_create(const char *path)
Create a new IMAP mailbox.
Definition browse.c:393
int imap_mailbox_rename(const char *path)
Rename a mailbox.
Definition browse.c:448
const struct CompleteOps CompleteMailboxOps
Auto-Completion of Files / Mailboxes.
Definition complete.c:159
static void browser_apply_subscribe_imap(struct FolderFilePtrArray *ffpa, bool subscribe)
Subscribe/unsubscribe to IMAP mailboxes.
Definition functions.c:271
static struct NntpMboxData * browser_apply_catchup(struct FolderFilePtrArray *ffpa, struct Mailbox *m, struct NntpAccountData *adata, bool up)
(Un)catchup a working set of newsgroups
Definition functions.c:436
static int browser_apply_delete_mailbox(struct FolderFilePtrArray *ffpa, struct BrowserState *state, struct Mailbox *m, int *num_failed)
Delete a working set of IMAP mailboxes.
Definition functions.c:676
static const struct MenuOpSeq BrowserDefaultBindings[]
Key bindings for the file Browser Menu.
Definition functions.c:102
static void browser_apply_subscribe_nntp(struct FolderFilePtrArray *ffpa, struct NntpAccountData *adata, bool subscribe)
Subscribe/unsubscribe to NNTP newsgroups.
Definition functions.c:247
static const struct MenuFuncOp OpBrowser[]
Functions for the file Browser Menu.
Definition functions.c:69
void browser_init_keys(struct NeoMutt *n, struct SubMenu *sm_generic)
Initialise the Browser Keybindings - Implements ::init_keys_api.
Definition functions.c:127
static int browser_add_selection(struct FolderFilePtrArray *ffpa, struct BrowserState *state, struct Menu *menu, bool tagged, int count)
Build a working set of FolderFile pointers for an action.
Definition functions.c:179
void destroy_state(struct BrowserState *state)
Free the BrowserState.
Definition functions.c:150
int browser_function_dispatcher(struct MuttWindow *win, const struct KeyEvent *event)
Perform a Browser function.
Definition functions.c:1441
static const struct BrowserFunction BrowserFunctions[]
All the NeoMutt functions that the Browser supports.
Definition functions.c:1402
Browser functions.
Select a Mailbox from a list.
void browser_sort(struct BrowserState *state)
Sort the entries in the browser.
Definition sort.c:186
Browser private Module data.
Private state data for the Browser.
Browser sorting functions.
@ BROWSER_SORT_ALPHA
Sort by name.
Definition sort.h:31
@ BROWSER_SORT_UNSORTED
Sort into the raw order.
Definition sort.h:37
@ BROWSER_SORT_COUNT
Sort by total message count.
Definition sort.h:32
@ BROWSER_SORT_DATE
Sort by date.
Definition sort.h:33
@ BROWSER_SORT_NEW
Sort by count of new messages.
Definition sort.h:35
@ BROWSER_SORT_SIZE
Sort by size.
Definition sort.h:36
@ BROWSER_SORT_DESC
Sort by description.
Definition sort.h:34
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition buffer.c:168
size_t buf_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition buffer.c:497
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition buffer.c:89
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition buffer.c:298
void buf_fix_dptr(struct Buffer *buf)
Move the dptr to end of the Buffer.
Definition buffer.c:189
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition buffer.c:248
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition buffer.c:233
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:401
size_t buf_copy(struct Buffer *dst, const struct Buffer *src)
Copy a Buffer's contents to another Buffer.
Definition buffer.c:607
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition buffer.c:577
size_t buf_concat_path(struct Buffer *buf, const char *dir, const char *fname)
Join a directory name and a filename.
Definition buffer.c:515
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
const struct Regex * cs_subset_regex(const struct ConfigSubset *sub, const char *name)
Get a regex config item by name.
Definition helpers.c:217
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition helpers.c:291
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition helpers.c:47
Convenience wrapper for the config headers.
#define CSR_RESULT(x)
Extract the result code from CSR_* flags.
Definition set.h:53
#define CSR_SUCCESS
Action completed successfully.
Definition set.h:33
#define SORT_REVERSE
Reverse the order of the sort.
Definition sort.h:40
Convenience wrapper for the core headers.
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition mailbox.h:216
MailboxType
Supported mailbox formats.
Definition mailbox.h:40
@ MUTT_MAILBOX_ERROR
Error occurred examining Mailbox.
Definition mailbox.h:42
@ MUTT_IMAP
'IMAP' Mailbox type
Definition mailbox.h:49
@ MUTT_UNKNOWN
Mailbox wasn't recognised.
Definition mailbox.h:43
struct MuttWindow * dialog_find(struct MuttWindow *win)
Find the parent Dialog of a Window.
Definition dialog.c:89
@ FR_SUCCESS
Valid function - successfully performed.
Definition dispatcher.h:40
@ FR_DONE
Exit the Dialog.
Definition dispatcher.h:36
@ FR_UNKNOWN
Unknown function.
Definition dispatcher.h:34
@ FR_ERROR
Valid function - error occurred.
Definition dispatcher.h:39
@ FR_NOT_IMPL
Invalid function - feature not enabled.
Definition dispatcher.h:37
@ FR_NO_ACTION
Valid function - no action performed.
Definition dispatcher.h:38
int examine_directory(struct Mailbox *m, struct Menu *menu, struct BrowserState *state, const char *dirname, const char *prefix)
Get list of all files/newsgroups with mask.
void init_menu(struct BrowserState *state, struct Menu *menu, struct Mailbox *m, struct MuttWindow *sbar)
Set up a new menu.
void init_state(struct BrowserState *state)
Initialise a browser state.
void browser_add_folder(const struct Menu *menu, struct BrowserState *state, const char *name, const char *desc, const struct stat *st, struct Mailbox *m, void *data)
Add a folder to the browser list.
void browser_highlight_default(struct BrowserState *state, struct Menu *menu)
Decide which browser item should be highlighted.
int examine_mailboxes(struct Mailbox *m, struct Menu *menu, struct BrowserState *state)
Get list of mailboxes/subscribed newsgroups.
bool link_is_dir(const char *folder, const char *path)
Does this symlink point to a directory?
Edit a string.
@ MUTT_COMP_NONE
No flags are set.
Definition wdata.h:46
void mutt_body_free(struct Body **ptr)
Free a Body.
Definition body.c:58
Structs that make up an email.
void mutt_unget_op(int op)
Return an operation to the input buffer.
Definition get.c:159
@ MFF_DEPRECATED
Function is deprecated.
Definition get.h:67
bool OptNews
(pseudo) used to change reader mode
Definition globals.c:53
Global variables.
static int op_quit(struct AliasFunctionData *fdata, const struct KeyEvent *event)
Save changes and exit this dialog - Implements alias_function_t -.
Definition functions.c:308
static int op_jump(struct AliasFunctionData *fdata, const struct KeyEvent *event)
Jump to an index number - Implements alias_function_t -.
Definition functions.c:316
static int op_generic_select_entry(struct AliasFunctionData *fdata, const struct KeyEvent *event)
select the current entry - Implements alias_function_t -
Definition functions.c:388
static int op_sort(struct AliasFunctionData *fdata, const struct KeyEvent *event)
sort aliases - Implements alias_function_t -
Definition functions.c:557
static int op_browser_new_file(struct BrowserPrivateData *priv, const struct KeyEvent *event)
Select a new file in this directory - Implements browser_function_t -.
Definition functions.c:220
static int op_rename_mailbox(struct BrowserPrivateData *priv, const struct KeyEvent *event)
Rename the current mailbox (IMAP only) - Implements browser_function_t -.
Definition functions.c:1139
static int op_create_mailbox(struct BrowserPrivateData *priv, const struct KeyEvent *event)
Create a new mailbox (IMAP only) - Implements browser_function_t -.
Definition functions.c:640
static int op_browser_view_file(struct BrowserPrivateData *priv, const struct KeyEvent *event)
View file - Implements browser_function_t -.
Definition functions.c:385
static int op_mailbox_list(struct BrowserPrivateData *priv, const struct KeyEvent *event)
List mailboxes with new mail - Implements browser_function_t -.
Definition functions.c:1130
static int op_catchup(struct BrowserPrivateData *priv, const struct KeyEvent *event)
Mark all articles in newsgroup as read - Implements browser_function_t -.
Definition functions.c:468
static int op_browser_subscribe(struct BrowserPrivateData *priv, const struct KeyEvent *event)
Subscribe to current mbox (IMAP/NNTP only) - Implements browser_function_t -.
Definition functions.c:300
static int op_delete_mailbox(struct BrowserPrivateData *priv, const struct KeyEvent *event)
Delete the current mailbox (IMAP only) - Implements browser_function_t -.
Definition functions.c:714
static int op_load_active(struct BrowserPrivateData *priv, const struct KeyEvent *event)
Load list of all newsgroups from NNTP server - Implements browser_function_t -.
Definition functions.c:1092
static int op_enter_mask(struct BrowserPrivateData *priv, const struct KeyEvent *event)
Enter a file mask - Implements browser_function_t -.
Definition functions.c:788
static int op_subscribe_pattern(struct BrowserPrivateData *priv, const struct KeyEvent *event)
Subscribe to newsgroups matching a pattern - Implements browser_function_t -.
Definition functions.c:1240
static int op_browser_tell(struct BrowserPrivateData *priv, const struct KeyEvent *event)
Display the currently selected file's name - Implements browser_function_t -.
Definition functions.c:358
static int op_browser_toggle_lsub(struct BrowserPrivateData *priv, const struct KeyEvent *event)
Toggle view all/subscribed mailboxes (IMAP only) - Implements browser_function_t -.
Definition functions.c:371
static int op_toggle_mailboxes(struct BrowserPrivateData *priv, const struct KeyEvent *event)
Toggle whether to browse mailboxes or all files - Implements browser_function_t -.
Definition functions.c:1328
static int op_change_directory(struct BrowserPrivateData *priv, const struct KeyEvent *event)
Change directories - Implements browser_function_t -.
Definition functions.c:521
int mw_get_field(const char *prompt, struct Buffer *buf, CompletionFlags complete, enum HistoryClass hclass, const struct CompleteOps *comp_api, void *cdata)
Ask the user for a string -.
Definition window.c:502
int mw_multi_choice(const char *prompt, const char *letters)
Offer the user a multiple choice question -.
Definition question.c:62
#define mutt_warning(...)
Definition logging2.h:92
#define mutt_error(...)
Definition logging2.h:94
#define mutt_message(...)
Definition logging2.h:93
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
#define mutt_perror(...)
Definition logging2.h:95
enum MailboxType imap_path_probe(const char *path, const struct stat *st)
Is this an IMAP Mailbox?
Definition imap.c:2683
Convenience wrapper for the gui headers.
Read/write command history from/to a file.
@ HC_FILE
Files.
Definition lib.h:59
@ HC_PATTERN
Patterns.
Definition lib.h:60
@ HC_OTHER
Miscellaneous strings.
Definition lib.h:61
IMAP network mailbox.
int imap_subscribe(const char *path, bool subscribe)
Subscribe to a mailbox.
Definition imap.c:1413
int imap_delete_mailbox(struct Mailbox *m, char *path)
Delete a mailbox.
Definition imap.c:611
void km_menu_add_submenu(struct MenuDefinition *md, struct SubMenu *sm)
Add a SubMenu to a Menu Definition.
Definition init.c:123
struct SubMenu * km_register_submenu(const struct MenuFuncOp functions[])
Register a submenu.
Definition init.c:88
struct MenuDefinition * km_register_menu(int menu, const char *name)
Register a menu.
Definition init.c:105
void km_menu_add_bindings(struct MenuDefinition *md, const struct MenuOpSeq bindings[])
Add Keybindings to a Menu.
Definition init.c:136
Manage keymappings.
@ LL_DEBUG3
Log at debug level 3.
Definition logging2.h:47
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
#define MUTT_MEM_CALLOC(n, type)
Definition memory.h:52
#define MAX(a, b)
Return the maximum of two values.
Definition memory.h:38
GUI present the user with a selectable list.
void menu_queue_redraw(struct Menu *menu, MenuRedrawFlags redraw)
Queue a request for a redraw.
Definition menu.c:177
int menu_get_index(struct Menu *menu)
Get the current selection in the Menu.
Definition menu.c:153
@ MENU_REDRAW_INDEX
Redraw the index.
Definition lib.h:61
@ MENU_REDRAW_FULL
Redraw everything.
Definition lib.h:64
MenuRedrawFlags menu_set_index(struct Menu *menu, int index)
Set the current selection in the Menu.
Definition menu.c:167
@ MODULE_ID_BROWSER
ModuleBrowser, Mailbox Browser
Definition module_api.h:52
@ MODULE_ID_NNTP
ModuleNntp, Nntp
Definition module_api.h:83
Convenience wrapper for the library headers.
#define _(a)
Definition message.h:28
size_t mutt_path_realpath(struct Buffer *path)
Resolve path, unraveling symlinks.
Definition path.c:377
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition string.c:666
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition string.c:587
#define PATH_MAX
Definition mutt.h:49
int mutt_view_attachment(FILE *fp, struct Body *b, enum ViewAttachMode mode, struct Email *e, struct AttachCtx *actx, struct MuttWindow *win)
View an attachment.
@ MUTT_VA_REGULAR
View using default method.
Definition mutt_attach.h:45
bool mutt_mailbox_list(void)
Show a message with the list of mailboxes with new mail.
Mailbox helper functions.
void mutt_get_parent_path(const char *path, char *buf, size_t buflen)
Find the parent of a path (or mailbox)
Definition muttlib.c:867
void expand_path(struct Buffer *buf, bool regex)
Create the canonical path.
Definition muttlib.c:122
Some miscellaneous functions.
enum MailboxType mx_path_probe(const char *path)
Find a mailbox that understands a path.
Definition mx.c:1326
API for mailboxes.
void * neomutt_get_module_data(struct NeoMutt *n, enum ModuleId id)
Get the private data for a Module.
Definition neomutt.c:666
Nntp-specific Account data.
Usenet network mailbox type; talk to an NNTP server.
void nntp_clear_cache(struct NntpAccountData *adata)
Clear the NNTP cache.
Definition newsrc.c:852
int nntp_active_fetch(struct NntpAccountData *adata, bool mark_new)
Fetch list of all newsgroups from server.
Definition nntp.c:2044
int nntp_newsrc_parse(struct NntpAccountData *adata)
Parse .newsrc file.
Definition newsrc.c:165
void nntp_newsrc_close(struct NntpAccountData *adata)
Unlock and close .newsrc file.
Definition newsrc.c:121
struct NntpMboxData * mutt_newsgroup_catchup(struct Mailbox *m, struct NntpAccountData *adata, char *group)
Catchup newsgroup.
Definition newsrc.c:1239
int nntp_newsrc_update(struct NntpAccountData *adata)
Update .newsrc file.
Definition newsrc.c:446
struct NntpMboxData * mutt_newsgroup_subscribe(struct NntpAccountData *adata, char *group)
Subscribe newsgroup.
Definition newsrc.c:1188
struct NntpMboxData * mutt_newsgroup_uncatchup(struct Mailbox *m, struct NntpAccountData *adata, char *group)
Uncatchup newsgroup.
Definition newsrc.c:1278
struct NntpMboxData * mutt_newsgroup_unsubscribe(struct NntpAccountData *adata, char *group)
Unsubscribe newsgroup.
Definition newsrc.c:1212
Nntp-specific Mailbox data.
const struct CompleteOps CompletePatternOps
Auto-Completion of Patterns.
Definition complete.c:98
Match patterns to emails.
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition pool.c:91
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:111
@ MUTT_NO
User answered 'No', or assume 'No'.
Definition quad.h:38
@ MUTT_YES
User answered 'Yes', or assume 'Yes'.
Definition quad.h:39
Ask the user a question.
enum QuadOption query_yesorno(const char *prompt, enum QuadOption def)
Ask the user a Yes/No question.
Definition question.c:329
#define REG_COMP(preg, regex, cflags)
Compile a regular expression.
Definition regex3.h:49
Convenience wrapper for the send headers.
struct Body * mutt_make_file_attach(const char *path, struct ConfigSubset *sub)
Create a file attachment.
Definition sendlib.c:617
#define ASSERT(COND)
Definition signal2.h:59
void * adata
Private data (for Mailbox backends)
Definition account.h:42
The body of an email.
Definition body.h:36
A NeoMutt function.
Definition functions.h:49
int op
Op code, e.g. OP_MAIN_LIMIT.
Definition functions.h:50
browser_function_t function
Function to call.
Definition functions.h:51
Browser private Module data.
Definition module_data.h:32
struct MenuDefinition * menu_browser
Browser menu definition.
Definition module_data.h:34
struct Buffer last_dir
Browser: previous selected directory.
Definition module_data.h:35
struct Buffer last_dir_backup
Browser: backup copy of the current directory.
Definition module_data.h:36
Private state data for the Browser.
char *** files
Array of selected files.
struct Menu * menu
Menu.
struct Buffer * prefix
Folder prefix string.
struct Buffer * old_last_dir
Previous to last dir.
bool kill_prefix
Prefix is in use.
bool done
Should we close the Dialog?
int last_selected_mailbox
Index of last selected Mailbox.
int * numfiles
Number of selected files.
struct Mailbox * mailbox
Mailbox.
struct BrowserState state
State containing list of files/dir/mailboxes.
struct Buffer * file
Buffer for the result.
bool multiple
Allow multiple selections.
struct MuttWindow * sbar
Status Bar.
char goto_swapper[PATH_MAX]
Saved path after <goto-folder>
State of the file/mailbox browser.
Definition lib.h:148
char * folder
Folder name.
Definition lib.h:151
bool is_mailbox_list
Viewing mailboxes.
Definition lib.h:152
struct BrowserEntryArray entry
Array of files / dirs / mailboxes.
Definition lib.h:149
bool imap_browse
IMAP folder.
Definition lib.h:150
String manipulation buffer.
Definition buffer.h:36
size_t dsize
Length of data.
Definition buffer.h:39
char * data
Pointer to data.
Definition buffer.h:37
Input for the file completion function.
Definition curs_lib.h:39
Browser entry representing a folder/dir.
Definition lib.h:82
bool selectable
Folder can be selected.
Definition lib.h:100
char delim
Path delimiter.
Definition lib.h:97
bool imap
This is an IMAP folder.
Definition lib.h:99
char * name
Name of file/dir/mailbox.
Definition lib.h:90
bool tagged
Folder is tagged.
Definition lib.h:106
char * desc
Description of mailbox.
Definition lib.h:91
mode_t mode
File permissions.
Definition lib.h:83
bool inferiors
Folder has children.
Definition lib.h:101
An event such as a keypress.
Definition get.h:75
int count
Optional count prefix, e.g. 3 for 3j
Definition get.h:78
int op
Function opcode, e.g. OP_HELP.
Definition get.h:77
A mailbox.
Definition mailbox.h:81
void * mdata
Driver specific data.
Definition mailbox.h:134
Functions for a Dialog or Window.
Definition menudef.h:44
Mapping between a function and an operation.
Definition menu.h:37
Mapping between an operation and a key sequence.
Definition menu.h:47
Definition lib.h:86
struct MuttWindow * win
Window holding the Menu.
Definition lib.h:94
int num_tagged
Number of tagged entries.
Definition lib.h:101
void * mdata
Private data.
Definition lib.h:155
bool tag_prefix
User has pressed <tag-prefix>
Definition lib.h:92
int max
Number of entries in the menu.
Definition lib.h:88
void * wdata
Private data.
Container for Accounts, Notifications.
Definition neomutt.h:41
char * home_dir
User's home directory.
Definition neomutt.h:55
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:49
NNTP-specific Account data -.
Definition adata.h:36
struct NntpMboxData ** groups_list
List of newsgroups.
Definition adata.h:60
unsigned int groups_num
Number of newsgroups.
Definition adata.h:58
NNTP-specific Mailbox data -.
Definition mdata.h:34
bool deleted
Newsgroup is deleted.
Definition mdata.h:45
struct NntpAccountData * adata
Account data.
Definition mdata.h:48
Nntp private Module data.
Definition module_data.h:30
struct NntpAccountData * current_news_srv
Current NNTP news server.
Definition module_data.h:32
Cached regular expression.
Definition regex3.h:85
char * pattern
printable version
Definition regex3.h:86
Collection of related functions.
Definition menudef.h:33
A parsed URL proto://user:password@host:port/path?a=1&b=2
Definition url.h:69
char * path
Path.
Definition url.h:75
int cs_subset_str_native_set(const struct ConfigSubset *sub, const char *name, intptr_t value, struct Buffer *err)
Natively set the value of a string config item.
Definition subset.c:303
int cs_subset_str_string_set(const struct ConfigSubset *sub, const char *name, const char *value, struct Buffer *err)
Set a config item by string.
Definition subset.c:392
@ MENU_BROWSER
General file/mailbox browser.
Definition type.h:39
struct Url * url_parse(const char *src)
Fill in Url.
Definition url.c:242
void url_free(struct Url **ptr)
Free the contents of a URL.
Definition url.c:124