2 * Copyright notice from original mutt:
3 * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
5 * This file is part of mutt-ng, see http://www.muttng.org/.
6 * It's licensed under the GNU General Public License,
7 * please see the file GPL in the top level source directory.
10 #include <lib-lib/lib-lib.h>
16 #include <lib-ui/curses.h>
17 #include <lib-ui/enter.h>
18 #include <lib-ui/menu.h>
19 #include <lib-ui/sidebar.h>
20 #include <lib-mx/mx.h>
28 #include <imap/imap.h>
30 #include <nntp/nntp.h>
33 static struct mapping_t FolderHelp[] = {
34 {N_("Exit"), OP_EXIT},
35 {N_("Chdir"), OP_CHANGE_DIRECTORY},
36 {N_("Mask"), OP_ENTER_MASK},
37 {N_("Help"), OP_HELP},
42 static struct mapping_t FolderNewsHelp[] = {
43 {N_("Exit"), OP_EXIT},
44 {N_("List"), OP_TOGGLE_MAILBOXES},
45 {N_("Subscribe"), OP_BROWSER_SUBSCRIBE},
46 {N_("Unsubscribe"), OP_BROWSER_UNSUBSCRIBE},
47 {N_("Catchup"), OP_CATCHUP},
48 {N_("Mask"), OP_ENTER_MASK},
49 {N_("Help"), OP_HELP},
54 typedef struct folder_t {
55 struct folder_file *ff;
59 static char LastDir[_POSIX_PATH_MAX] = "";
60 static char LastDirBackup[_POSIX_PATH_MAX] = "";
62 /* Frees up the memory allocated for the local-global variables. */
63 static void destroy_state (struct browser_state *state)
67 for (c = 0; c < state->entrylen; c++) {
68 p_delete(&((state->entry)[c].name));
69 p_delete(&((state->entry)[c].desc));
70 p_delete(&((state->entry)[c].st));
72 p_delete(&state->folder);
73 p_delete(&state->entry);
76 static int browser_compare_subject (const void *a, const void *b)
78 struct folder_file *pa = (struct folder_file *) a;
79 struct folder_file *pb = (struct folder_file *) b;
81 int r = strcoll(NONULL(pa->name), NONULL(pb->name));
83 return ((BrowserSort & SORT_REVERSE) ? -r : r);
86 static int browser_compare_date (const void *a, const void *b)
88 struct folder_file *pa = (struct folder_file *) a;
89 struct folder_file *pb = (struct folder_file *) b;
91 int r = pa->mtime - pb->mtime;
93 return ((BrowserSort & SORT_REVERSE) ? -r : r);
96 static int browser_compare_size (const void *a, const void *b)
98 struct folder_file *pa = (struct folder_file *) a;
99 struct folder_file *pb = (struct folder_file *) b;
101 int r = pa->size - pb->size;
103 return ((BrowserSort & SORT_REVERSE) ? -r : r);
106 static void browser_sort (struct browser_state *state)
108 int (*f) (const void *, const void *);
110 switch (BrowserSort & SORT_MASK) {
115 if (option (OPTNEWS))
118 f = browser_compare_date;
122 if (option (OPTNEWS))
125 f = browser_compare_size;
129 f = browser_compare_subject;
132 qsort (state->entry, state->entrylen, sizeof (struct folder_file), f);
135 static int link_is_dir (const char *folder, const char *path)
138 char fullpath[_POSIX_PATH_MAX];
140 mutt_concat_path(fullpath, sizeof(fullpath), folder, path);
142 if (stat (fullpath, &st) == 0)
143 return (S_ISDIR (st.st_mode));
148 static const char *folder_format_str (char *dest, ssize_t destlen, char op,
149 const char *src, const char *fmt,
150 const char *ifstring,
151 const char *elsestring,
152 unsigned long data, format_flag flags)
154 char fn[STRING], tmp[STRING], permission[11], date[16];
157 FOLDER *folder = (FOLDER *) data;
160 int optional = (flags & M_FORMAT_OPTIONAL);
164 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
165 snprintf (dest, destlen, tmp, folder->num + 1);
169 if (folder->ff->st != NULL) {
172 tnow - folder->ff->st->st_mtime <
173 31536000 ? "%b %d %H:%M" : "%b %d %Y";
174 strftime (date, sizeof (date), t_fmt,
175 localtime (&folder->ff->st->st_mtime));
176 mutt_format_s (dest, destlen, fmt, date);
179 mutt_format_s (dest, destlen, fmt, "");
186 if (folder->ff->imap)
187 s = NONULL (folder->ff->desc);
189 s = NONULL (folder->ff->name);
191 snprintf (fn, sizeof (fn), "%s%s", s,
192 folder->ff->st ? (S_ISLNK (folder->ff->st->st_mode) ? "@" :
193 (S_ISDIR (folder->ff->st->st_mode) ? "/" :
194 ((folder->ff->st->st_mode & S_IXUSR) !=
195 0 ? "*" : ""))) : "");
197 mutt_format_s (dest, destlen, fmt, fn);
201 if (folder->ff->st != NULL) {
202 snprintf (permission, sizeof (permission), "%c%c%c%c%c%c%c%c%c%c",
203 S_ISDIR (folder->ff->st->
204 st_mode) ? 'd' : (S_ISLNK (folder->ff->st->
205 st_mode) ? 'l' : '-'),
206 (folder->ff->st->st_mode & S_IRUSR) != 0 ? 'r' : '-',
207 (folder->ff->st->st_mode & S_IWUSR) != 0 ? 'w' : '-',
208 (folder->ff->st->st_mode & S_ISUID) !=
209 0 ? 's' : (folder->ff->st->st_mode & S_IXUSR) !=
211 (folder->ff->st->st_mode & S_IRGRP) != 0 ? 'r' : '-',
212 (folder->ff->st->st_mode & S_IWGRP) != 0 ? 'w' : '-',
213 (folder->ff->st->st_mode & S_ISGID) !=
214 0 ? 's' : (folder->ff->st->st_mode & S_IXGRP) !=
216 (folder->ff->st->st_mode & S_IROTH) != 0 ? 'r' : '-',
217 (folder->ff->st->st_mode & S_IWOTH) != 0 ? 'w' : '-',
218 (folder->ff->st->st_mode & S_ISVTX) !=
219 0 ? 't' : (folder->ff->st->st_mode & S_IXOTH) !=
221 mutt_format_s (dest, destlen, fmt, permission);
223 else if (folder->ff->imap) {
224 /* mark folders with subfolders AND mail */
225 snprintf (permission, sizeof (permission), "IMAP %c",
226 (folder->ff->inferiors
227 && folder->ff->selectable) ? '+' : ' ');
228 mutt_format_s (dest, destlen, fmt, permission);
231 mutt_format_s (dest, destlen, fmt, "");
235 if (folder->ff->st != NULL) {
236 if ((gr = getgrgid (folder->ff->st->st_gid)))
237 mutt_format_s (dest, destlen, fmt, gr->gr_name);
239 snprintf (tmp, sizeof (tmp), "%%%sld", fmt);
240 snprintf (dest, destlen, tmp, folder->ff->st->st_gid);
244 mutt_format_s (dest, destlen, fmt, "");
248 if (folder->ff->st != NULL) {
249 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
250 snprintf (dest, destlen, tmp, folder->ff->st->st_nlink);
253 mutt_format_s (dest, destlen, fmt, "");
257 if (imap_is_magic (folder->ff->desc, NULL) == M_IMAP) {
259 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
260 snprintf (dest, destlen, tmp, folder->ff->new);
262 else if (!folder->ff->new)
266 snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
267 snprintf (dest, destlen, tmp, folder->ff->new ? 'N' : ' ');
271 if (folder->ff->st != NULL) {
272 snprintf (tmp, sizeof (tmp), "%%%sld", fmt);
273 snprintf (dest, destlen, tmp, (long) folder->ff->st->st_size);
276 mutt_format_s (dest, destlen, fmt, "");
280 snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
281 snprintf (dest, destlen, tmp, folder->ff->tagged ? '*' : ' ');
285 if (folder->ff->st != NULL) {
286 if ((pw = getpwuid (folder->ff->st->st_uid)))
287 mutt_format_s (dest, destlen, fmt, pw->pw_name);
289 snprintf (tmp, sizeof (tmp), "%%%sld", fmt);
290 snprintf (dest, destlen, tmp, folder->ff->st->st_uid);
294 mutt_format_s (dest, destlen, fmt, "");
298 snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
299 snprintf (dest, destlen, tmp, op);
304 mutt_FormatString (dest, destlen, ifstring, folder_format_str, data, 0);
305 else if (flags & M_FORMAT_OPTIONAL)
306 mutt_FormatString (dest, destlen, elsestring, folder_format_str, data, 0);
312 static const char *newsgroup_format_str (char *dest, ssize_t destlen, char op,
313 const char *src, const char *fmt,
314 const char *ifstring,
315 const char *elsestring,
319 char fn[STRING], tmp[STRING];
320 FOLDER *folder = (FOLDER *) data;
324 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
325 snprintf (dest, destlen, tmp, folder->num + 1);
329 m_strcpy(fn, sizeof(fn), folder->ff->name);
330 snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
331 snprintf (dest, destlen, tmp, fn);
335 snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
336 if (folder->ff->nd->subscribed)
337 snprintf (dest, destlen, tmp, ' ');
339 snprintf (dest, destlen, tmp, folder->ff->new ? 'N' : 'u');
343 snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
344 if (folder->ff->nd->deleted)
345 snprintf (dest, destlen, tmp, 'D');
347 snprintf (dest, destlen, tmp, folder->ff->nd->allowed ? ' ' : '-');
351 if (flags & M_FORMAT_OPTIONAL) {
352 if (folder->ff->nd->unread != 0)
353 mutt_FormatString (dest, destlen, ifstring, newsgroup_format_str,
356 mutt_FormatString (dest, destlen, elsestring, newsgroup_format_str,
359 else if (Context && Context->data == folder->ff->nd) {
360 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
361 snprintf (dest, destlen, tmp, Context->unread);
364 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
365 snprintf (dest, destlen, tmp, folder->ff->nd->unread);
370 if (Context && Context->data == folder->ff->nd) {
371 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
372 snprintf (dest, destlen, tmp, Context->new);
374 else if (option (OPTMARKOLD) &&
375 folder->ff->nd->lastCached >= folder->ff->nd->firstMessage &&
376 folder->ff->nd->lastCached <= folder->ff->nd->lastMessage) {
377 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
378 snprintf (dest, destlen, tmp,
379 folder->ff->nd->lastMessage - folder->ff->nd->lastCached);
382 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
383 snprintf (dest, destlen, tmp, folder->ff->nd->unread);
388 if (folder->ff->nd->desc != NULL) {
389 snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
390 snprintf (dest, destlen, tmp, folder->ff->nd->desc);
393 snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
394 snprintf (dest, destlen, tmp, "");
400 #endif /* USE_NNTP */
403 static void add_folder (MUTTMENU * m, struct browser_state *state,
404 const char *name, const struct stat *s,
407 static void add_folder (MUTTMENU * m, struct browser_state *state,
408 const char *name, const struct stat *s,
412 if (state->entrylen == state->entrymax) {
413 /* need to allocate more space */
414 p_realloc(&state->entry, state->entrymax += 256);
415 p_clear(&state->entry[state->entrylen], 256);
417 m->data = state->entry;
421 (state->entry)[state->entrylen].mode = s->st_mode;
422 (state->entry)[state->entrylen].mtime = s->st_mtime;
423 (state->entry)[state->entrylen].size = s->st_size;
424 (state->entry)[state->entrylen].st = p_dup(s, 1);
427 (state->entry)[state->entrylen].new = new;
428 (state->entry)[state->entrylen].name = m_strdup(name);
429 (state->entry)[state->entrylen].desc = m_strdup(name);
430 (state->entry)[state->entrylen].imap = 0;
432 if (option (OPTNEWS))
433 (state->entry)[state->entrylen].nd = (NNTP_DATA *) data;
438 static void init_state (struct browser_state *state, MUTTMENU * menu)
441 state->entrymax = 256;
442 state->entry = p_new(struct folder_file, state->entrymax);
443 state->imap_browse = 0;
445 menu->data = state->entry;
448 /* get list of all files/newsgroups with mask */
449 static int examine_directory (MUTTMENU * menu, struct browser_state *state,
450 char *d, const char *prefix)
453 if (option (OPTNEWS)) {
456 NNTP_SERVER *news = CurrentNewsSrv;
458 /* buffy_check (0); */
459 init_state (state, menu);
461 for (tmp = news->list; tmp; tmp = tmp->next) {
462 if (!(data = (NNTP_DATA *) tmp->data))
464 nntp_sync_sidebar (data);
465 if (m_strncmp (prefix, data->group, m_strlen(prefix)) != 0)
467 if (!((regexec (Mask.rx, data->group, 0, NULL, 0) == 0) ^ Mask.not))
470 add_folder (menu, state, data->group, NULL, data, data->new);
472 add_folder (menu, state, data->group, NULL, data->new);
478 #endif /* USE_NNTP */
483 char buffer[_POSIX_PATH_MAX + STRING];
486 while (stat (d, &s) == -1) {
487 if (errno == ENOENT) {
488 /* The last used directory is deleted, try to use the parent dir. */
489 char *c = strrchr (d, '/');
500 if (!S_ISDIR (s.st_mode)) {
501 mutt_error (_("%s is not a directory."), d);
507 if ((dp = opendir (d)) == NULL) {
512 init_state (state, menu);
514 while ((de = readdir (dp)) != NULL) {
515 if (m_strcmp(de->d_name, ".") == 0)
516 continue; /* we don't need . */
518 if (m_strncmp(prefix, de->d_name, m_strlen(prefix)) != 0)
520 if (!((regexec (Mask.rx, de->d_name, 0, NULL, 0) == 0) ^ Mask.not))
523 mutt_concat_path(buffer, sizeof(buffer), d, de->d_name);
524 if (lstat (buffer, &s) == -1)
527 if ((!S_ISREG (s.st_mode)) && (!S_ISDIR (s.st_mode)) &&
528 (!S_ISLNK (s.st_mode)))
531 i = buffy_lookup (buffer);
533 add_folder (menu, state, de->d_name, &s, NULL, i >= 0 ? Incoming.arr[i]->new : 0);
535 add_folder (menu, state, de->d_name, &s, i >= 0 ? Incoming.arr[i]->new : 0);
541 browser_sort (state);
545 /* get list of mailboxes/subscribed newsgroups */
546 static int examine_mailboxes (MUTTMENU * menu, struct browser_state *state)
549 char buffer[LONG_STRING];
552 if (option (OPTNEWS)) {
555 NNTP_SERVER *news = CurrentNewsSrv;
557 /* buffy_check (0); */
558 init_state (state, menu);
560 for (tmp = news->list; tmp; tmp = tmp->next) {
561 if ((data = (NNTP_DATA*) tmp->data) == NULL)
563 nntp_sync_sidebar (data);
564 if ((data->new || (data->subscribed &&
565 (!option (OPTSHOWONLYUNREAD)|| data->unread))))
566 add_folder (menu, state, data->group, NULL, data, data->new);
580 init_state (state, menu);
582 for (i = 0; i < Incoming.len; i++) {
583 tmp = Incoming.arr[i];
584 tmp->magic = mx_get_magic (tmp->path);
585 if (tmp->magic == M_IMAP) {
587 add_folder (menu, state, tmp->path, NULL, NULL, tmp->new);
589 add_folder (menu, state, tmp->path, NULL, tmp->new);
593 if (tmp->magic == M_POP) {
595 add_folder (menu, state, tmp->path, NULL, NULL, tmp->new);
597 add_folder (menu, state, tmp->path, NULL, tmp->new);
602 if (tmp->magic == M_NNTP) {
603 add_folder (menu, state, tmp->path, NULL, NULL, tmp->new);
607 if (lstat (tmp->path, &s) == -1)
610 if ((!S_ISREG (s.st_mode)) && (!S_ISDIR (s.st_mode)) &&
611 (!S_ISLNK (s.st_mode)))
614 m_strcpy(buffer, sizeof(buffer), NONULL(tmp->path));
615 mutt_pretty_mailbox (buffer);
618 add_folder (menu, state, buffer, &s, NULL, tmp->new);
620 add_folder (menu, state, buffer, &s, tmp->new);
624 browser_sort (state);
628 static int select_file_search (MUTTMENU * menu, regex_t * re, int n)
631 if (option (OPTNEWS))
633 (re, ((struct folder_file *) menu->data)[n].desc, 0, NULL, 0));
636 (re, ((struct folder_file *) menu->data)[n].name, 0, NULL, 0));
639 static void folder_entry (char *s, ssize_t slen, MUTTMENU * menu, int num)
643 folder.ff = &((struct folder_file *) menu->data)[num];
647 if (option (OPTNEWS))
648 mutt_FormatString (s, slen, NONULL (GroupFormat), newsgroup_format_str,
649 (unsigned long) &folder, M_FORMAT_ARROWCURSOR);
652 mutt_FormatString (s, slen, NONULL (FolderFormat), folder_format_str,
653 (unsigned long) &folder, M_FORMAT_ARROWCURSOR);
656 static void init_menu (struct browser_state *state, MUTTMENU * menu,
657 char *title, ssize_t titlelen, int buffy)
659 char path[_POSIX_PATH_MAX];
661 menu->max = state->entrylen;
663 if (menu->current >= menu->max)
664 menu->current = menu->max - 1;
665 if (menu->current < 0)
667 if (menu->top > menu->current)
673 if (option (OPTNEWS)) {
675 snprintf (title, titlelen, "%s", _("Subscribed newsgroups"));
677 snprintf (title, titlelen, _("Newsgroups on server [%s]"),
678 CurrentNewsSrv->conn->account.host);
683 snprintf(title, titlelen, _("Mailboxes [%d]"), buffy_check(0));
685 m_strcpy(path, sizeof(path), LastDir);
686 mutt_pretty_mailbox (path);
687 if (state->imap_browse && option (OPTIMAPLSUB))
688 snprintf (title, titlelen, _("Subscribed [%s], File mask: %s"),
689 path, NONULL (Mask.pattern));
691 snprintf (title, titlelen, _("Directory [%s], File mask: %s"),
692 path, NONULL (Mask.pattern));
694 menu->redraw = REDRAW_FULL;
697 static int file_tag (MUTTMENU * menu, int n, int m)
699 struct folder_file *ff = &(((struct folder_file *) menu->data)[n]);
702 if (S_ISDIR (ff->mode)
703 || (S_ISLNK (ff->mode) && link_is_dir (LastDir, ff->name))) {
704 mutt_error _("Can't attach a directory!");
710 ff->tagged = (m >= 0 ? m : !ff->tagged);
712 return ff->tagged - ot;
715 void _mutt_select_file (char *f, ssize_t flen, int flags, char ***files,
718 char buf[_POSIX_PATH_MAX];
719 char prefix[_POSIX_PATH_MAX] = "";
720 char helpstr[STRING];
722 struct browser_state state;
725 int i, killPrefix = 0;
726 int multiple = (flags & M_SEL_MULTI) ? 1 : 0;
727 int folder = (flags & M_SEL_FOLDER) ? 1 : 0;
728 int buffy = (flags & M_SEL_BUFFY) ? 1 : 0;
730 buffy = buffy && folder;
735 m_strcpy(LastDirBackup, sizeof(LastDirBackup), LastDir);
738 if (option (OPTNEWS)) {
740 m_strcpy(prefix, sizeof(prefix), f);
744 /* default state for news reader mode is browse subscribed newsgroups */
746 for (list = CurrentNewsSrv->list; list; list = list->next) {
747 NNTP_DATA *data = (NNTP_DATA *) list->data;
749 if (data && data->subscribed) {
759 mutt_expand_path (f, flen);
760 if (imap_is_magic (f, NULL) == M_IMAP) {
761 init_state (&state, NULL);
762 state.imap_browse = 1;
763 if (!imap_browse (f, &state))
764 m_strcpy(LastDir, sizeof(LastDir), state.folder);
767 for (i = m_strlen(f) - 1; i > 0 && f[i] != '/'; i--);
770 i = MIN(ssizeof(LastDir) - 1, i);
771 m_strcpy(LastDir, sizeof(LastDir), f);
774 getcwd(LastDir, sizeof(LastDir));
775 m_strcat(LastDir, sizeof(LastDir), "/");
776 m_strncat(LastDir, sizeof(LastDir), f, i);
781 m_strcpy(LastDir, sizeof(LastDir), "/");
783 getcwd (LastDir, sizeof (LastDir));
786 if (i <= 0 && f[0] != '/')
787 m_strcpy(prefix, sizeof(prefix), f);
789 m_strcpy(prefix, sizeof(prefix), f + i + 1);
795 getcwd (LastDir, sizeof (LastDir));
796 else if (!LastDir[0])
797 m_strcpy(LastDir, sizeof(LastDir), NONULL(Maildir));
799 if (!buffy && imap_is_magic (LastDir, NULL) == M_IMAP) {
800 init_state (&state, NULL);
801 state.imap_browse = 1;
802 imap_browse (LastDir, &state);
803 browser_sort (&state);
810 if (examine_mailboxes (NULL, &state) == -1)
814 if (!state.imap_browse)
815 if (examine_directory (NULL, &state, LastDir, prefix) == -1)
818 menu = mutt_new_menu ();
819 menu->menu = MENU_FOLDER;
820 menu->make_entry = folder_entry;
821 menu->search = select_file_search;
823 menu->data = state.entry;
825 menu->tag = file_tag;
827 menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_FOLDER,
829 (option (OPTNEWS)) ? FolderNewsHelp :
833 init_menu (&state, menu, title, sizeof (title), buffy);
836 switch (i = mutt_menuLoop (menu)) {
837 case OP_GENERIC_SELECT_ENTRY:
839 if (!state.entrylen) {
840 mutt_error _("No files match the file mask");
845 if (S_ISDIR (state.entry[menu->current].mode) ||
846 (S_ISLNK (state.entry[menu->current].mode) &&
847 link_is_dir (LastDir, state.entry[menu->current].name))
848 || state.entry[menu->current].inferiors
850 /* make sure this isn't a MH or maildir mailbox */
852 m_strcpy(buf, sizeof(buf), state.entry[menu->current].name);
853 mutt_expand_path (buf, sizeof (buf));
855 else if (state.imap_browse) {
856 m_strcpy(buf, sizeof(buf), state.entry[menu->current].name);
859 mutt_concat_path(buf, sizeof(buf), LastDir,
860 state.entry[menu->current].name);
862 if ((mx_get_magic (buf) <= 0)
863 || state.entry[menu->current].inferiors)
865 char OldLastDir[_POSIX_PATH_MAX];
867 /* save the old directory */
868 m_strcpy(OldLastDir, sizeof(OldLastDir), LastDir);
870 if (m_strcmp(state.entry[menu->current].name, "..") == 0) {
871 if (m_strcmp("..", LastDir + m_strlen(LastDir) - 2) == 0)
872 m_strcat(LastDir, sizeof(LastDir), "/..");
874 char *p = strrchr (LastDir + 1, '/');
879 if (LastDir[0] == '/')
882 m_strcat(LastDir, sizeof(LastDir), "/..");
887 m_strcpy(LastDir, sizeof(LastDir),
888 state.entry[menu->current].name);
889 mutt_expand_path (LastDir, sizeof (LastDir));
891 else if (state.imap_browse) {
895 m_strcpy(LastDir, sizeof(LastDir),
896 state.entry[menu->current].name);
897 /* tack on delimiter here */
898 n = m_strlen(LastDir) + 1;
900 /* special case "" needs no delimiter */
901 url_parse_ciss (&url, state.entry[menu->current].name);
903 (state.entry[menu->current].delim != '\0') &&
904 (n < ssizeof (LastDir))) {
906 LastDir[n - 1] = state.entry[menu->current].delim;
910 char tmp[_POSIX_PATH_MAX];
912 mutt_concat_path(tmp, sizeof(tmp), LastDir,
913 state.entry[menu->current].name);
914 m_strcpy(LastDir, sizeof(LastDir), tmp);
917 destroy_state (&state);
923 if (state.imap_browse) {
924 init_state (&state, NULL);
925 state.imap_browse = 1;
926 imap_browse (LastDir, &state);
927 browser_sort (&state);
928 menu->data = state.entry;
931 if (examine_directory (menu, &state, LastDir, prefix) == -1) {
932 /* try to restore the old values */
933 m_strcpy(LastDir, sizeof(LastDir), OldLastDir);
934 if (examine_directory (menu, &state, LastDir, prefix) == -1) {
935 m_strcpy(LastDir, sizeof(LastDir), NONULL(Homedir));
941 init_menu (&state, menu, title, sizeof (title), buffy);
947 if (buffy || option (OPTNEWS)) /* news have not path */
952 m_strcpy(f, flen, state.entry[menu->current].name);
953 mutt_expand_path (f, flen);
955 else if (state.imap_browse)
956 m_strcpy(f, flen, state.entry[menu->current].name);
958 mutt_concat_path(f, flen, LastDir, state.entry[menu->current].name);
960 /* Fall through to OP_EXIT */
970 *numfiles = menu->tagged;
971 tfiles = p_new(char *, *numfiles);
972 for (h = 0, j = 0; h < state.entrylen; i++) {
973 struct folder_file ff = state.entry[i];
974 char full[_POSIX_PATH_MAX];
977 mutt_concat_path(full, sizeof(full), LastDir, ff.name);
978 mutt_expand_path (full, sizeof (full));
979 tfiles[j++] = m_strdup(full);
984 else if (f[0]) { /* no tagged entries. return selected entry */
986 tfiles = p_new(char *, *numfiles);
987 mutt_expand_path (f, flen);
988 tfiles[0] = m_strdup(f);
993 destroy_state (&state);
994 mutt_menuDestroy (&menu);
997 case OP_BROWSER_TELL:
999 mutt_message ("%s", state.entry[menu->current].name);
1002 case OP_BROWSER_TOGGLE_LSUB:
1003 if (option (OPTIMAPLSUB)) {
1004 unset_option (OPTIMAPLSUB);
1007 set_option (OPTIMAPLSUB);
1009 mutt_ungetch (0, OP_CHECK_NEW);
1012 case OP_CREATE_MAILBOX:
1013 if (!state.imap_browse)
1014 mutt_error (_("Create is only supported for IMAP mailboxes"));
1016 imap_mailbox_create (LastDir);
1017 /* TODO: find a way to detect if the new folder would appear in
1018 * this window, and insert it without starting over. */
1019 destroy_state (&state);
1020 init_state (&state, NULL);
1021 state.imap_browse = 1;
1022 imap_browse (LastDir, &state);
1023 browser_sort (&state);
1024 menu->data = state.entry;
1027 init_menu (&state, menu, title, sizeof (title), buffy);
1028 MAYBE_REDRAW (menu->redraw);
1032 case OP_RENAME_MAILBOX:
1033 if (!state.entry[menu->current].imap)
1034 mutt_error (_("Rename is only supported for IMAP mailboxes"));
1036 int nentry = menu->current;
1038 if (imap_mailbox_rename (state.entry[nentry].name) >= 0) {
1039 destroy_state (&state);
1040 init_state (&state, NULL);
1041 state.imap_browse = 1;
1042 imap_browse (LastDir, &state);
1043 browser_sort (&state);
1044 menu->data = state.entry;
1047 init_menu (&state, menu, title, sizeof (title), buffy);
1048 MAYBE_REDRAW (menu->redraw);
1053 case OP_DELETE_MAILBOX:
1054 if (!state.entry[menu->current].imap)
1055 mutt_error (_("Delete is only supported for IMAP mailboxes"));
1059 int nentry = menu->current;
1061 imap_parse_path (state.entry[nentry].name, &mx);
1062 snprintf (msg, sizeof (msg), _("Really delete mailbox \"%s\"?"),
1064 if (mutt_yesorno (msg, M_NO) == M_YES) {
1065 if (!imap_delete_mailbox (Context, mx)) {
1066 /* free the mailbox from the browser */
1067 p_delete(&((state.entry)[nentry].name));
1068 p_delete(&((state.entry)[nentry].desc));
1069 /* and move all other entries up */
1070 if (nentry + 1 < state.entrylen)
1071 memmove (state.entry + nentry, state.entry + nentry + 1,
1072 sizeof (struct folder_file) * (state.entrylen -
1075 mutt_message _("Mailbox deleted.");
1077 init_menu (&state, menu, title, sizeof (title), buffy);
1078 MAYBE_REDRAW (menu->redraw);
1082 mutt_message _("Mailbox not deleted.");
1087 case OP_CHANGE_DIRECTORY:
1090 if (option (OPTNEWS))
1094 m_strcpy(buf, sizeof(buf), LastDir);
1095 if (!state.imap_browse)
1097 /* add '/' at the end of the directory name if not already there */
1098 ssize_t len = m_strlen(LastDir);
1100 if (len && LastDir[len - 1] != '/' && ssizeof(buf) > len)
1104 if (mutt_get_field (_("Chdir to: "), buf, sizeof (buf), M_FILE) == 0 &&
1107 mutt_expand_path (buf, sizeof (buf));
1108 if (imap_is_magic (buf, NULL) == M_IMAP) {
1109 m_strcpy(LastDir, sizeof(LastDir), buf);
1110 destroy_state (&state);
1111 init_state (&state, NULL);
1112 state.imap_browse = 1;
1113 imap_browse (LastDir, &state);
1114 browser_sort (&state);
1115 menu->data = state.entry;
1118 init_menu (&state, menu, title, sizeof (title), buffy);
1121 if (stat (buf, &st) == 0) {
1122 if (S_ISDIR (st.st_mode)) {
1123 destroy_state (&state);
1124 if (examine_directory (menu, &state, buf, prefix) == 0)
1125 m_strcpy(LastDir, sizeof(LastDir), buf);
1127 mutt_error _("Error scanning directory.");
1129 if (examine_directory (menu, &state, LastDir, prefix) == -1) {
1130 mutt_menuDestroy (&menu);
1136 init_menu (&state, menu, title, sizeof (title), buffy);
1139 mutt_error (_("%s is not a directory."), buf);
1144 MAYBE_REDRAW (menu->redraw);
1149 m_strcpy(buf, sizeof(buf), NONULL(Mask.pattern));
1150 if (mutt_get_field (_("File Mask: "), buf, sizeof (buf), 0) == 0) {
1151 regex_t *rx = p_new(regex_t, 1);
1156 /* assume that the user wants to see everything */
1158 m_strcpy(buf, sizeof(buf), ".");
1161 s = vskipspaces(s + 1);
1165 if ((err = REGCOMP (rx, s, REG_NOSUB)) != 0) {
1166 regerror (err, rx, buf, sizeof (buf));
1169 mutt_error ("%s", buf);
1172 m_strreplace(&Mask.pattern, buf);
1178 destroy_state (&state);
1179 if (state.imap_browse) {
1180 init_state (&state, NULL);
1181 state.imap_browse = 1;
1182 imap_browse (LastDir, &state);
1183 browser_sort (&state);
1184 menu->data = state.entry;
1185 init_menu (&state, menu, title, sizeof (title), buffy);
1188 if (examine_directory (menu, &state, LastDir, NULL) == 0)
1189 init_menu (&state, menu, title, sizeof (title), buffy);
1191 mutt_error _("Error scanning directory.");
1193 mutt_menuDestroy (&menu);
1197 if (!state.entrylen) {
1198 mutt_error _("No files match the file mask");
1204 MAYBE_REDRAW (menu->redraw);
1208 case OP_SORT_REVERSE:
1212 int reverse = (i == OP_SORT_REVERSE);
1214 switch (mutt_multi_choice ((reverse) ?
1216 ("Reverse sort by (d)ate, (a)lpha, si(z)e or do(n)'t sort? ")
1219 ("Sort by (d)ate, (a)lpha, si(z)e or do(n)'t sort? "),
1221 case -1: /* abort */
1225 case 1: /* (d)ate */
1226 BrowserSort = SORT_DATE;
1229 case 2: /* (a)lpha */
1230 BrowserSort = SORT_SUBJECT;
1233 case 3: /* si(z)e */
1234 BrowserSort = SORT_SIZE;
1237 case 4: /* do(n)'t sort */
1238 BrowserSort = SORT_ORDER;
1243 BrowserSort |= reverse ? SORT_REVERSE : 0;
1244 browser_sort (&state);
1245 menu->redraw = REDRAW_FULL;
1250 case OP_TOGGLE_MAILBOXES:
1254 destroy_state (&state);
1259 if (examine_mailboxes (menu, &state) == -1)
1262 else if (imap_is_magic (LastDir, NULL) == M_IMAP) {
1263 init_state (&state, NULL);
1264 state.imap_browse = 1;
1265 imap_browse (LastDir, &state);
1266 browser_sort (&state);
1267 menu->data = state.entry;
1269 else if (examine_directory (menu, &state, LastDir, prefix) == -1)
1271 init_menu (&state, menu, title, sizeof (title), buffy);
1275 if (option (OPTFORCEBUFFYCHECK))
1280 case OP_BROWSER_NEW_FILE:
1282 snprintf (buf, sizeof (buf), "%s/", LastDir);
1283 if (mutt_get_field (_("New file name: "), buf, sizeof (buf), M_FILE) ==
1285 m_strcpy(f, flen, buf);
1286 destroy_state (&state);
1287 mutt_menuDestroy (&menu);
1290 MAYBE_REDRAW (menu->redraw);
1293 case OP_BROWSER_VIEW_FILE:
1294 if (!state.entrylen) {
1295 mutt_error _("No files match the file mask");
1300 if (state.entry[menu->current].selectable) {
1301 m_strcpy(f, flen, state.entry[menu->current].name);
1302 destroy_state (&state);
1303 mutt_menuDestroy (&menu);
1307 if (S_ISDIR (state.entry[menu->current].mode) ||
1308 (S_ISLNK (state.entry[menu->current].mode) &&
1309 link_is_dir (LastDir, state.entry[menu->current].name))) {
1310 mutt_error _("Can't view a directory");
1316 char nbuf[_POSIX_PATH_MAX];
1318 mutt_concat_path(nbuf, sizeof(nbuf), LastDir,
1319 state.entry[menu->current].name);
1320 b = mutt_make_file_attach (nbuf);
1322 mutt_view_attachment (NULL, b, M_REGULAR, NULL, NULL, 0);
1324 menu->redraw = REDRAW_FULL;
1327 mutt_error _("Error trying to view file");
1334 if (option (OPTNEWS)) {
1335 struct folder_file *folder_f = &state.entry[menu->current];
1338 if (i == OP_CATCHUP)
1339 nd = mutt_newsgroup_catchup (CurrentNewsSrv, folder_f->name);
1341 nd = mutt_newsgroup_uncatchup (CurrentNewsSrv, folder_f->name);
1344 if (menu->current + 1 < menu->max)
1346 menu->redraw = REDRAW_MOTION_RESYNCH;
1351 case OP_LOAD_ACTIVE:
1352 if (!option (OPTNEWS))
1359 for (tmp = CurrentNewsSrv->list; tmp; tmp = tmp->next) {
1360 if ((data = (NNTP_DATA *) tmp->data))
1364 nntp_get_active (CurrentNewsSrv);
1366 destroy_state (&state);
1368 examine_mailboxes (menu, &state);
1370 examine_directory (menu, &state, NULL, NULL);
1371 init_menu (&state, menu, title, sizeof (title), buffy);
1373 #endif /* USE_NNTP */
1375 case OP_BROWSER_SUBSCRIBE:
1376 case OP_BROWSER_UNSUBSCRIBE:
1378 case OP_SUBSCRIBE_PATTERN:
1379 case OP_UNSUBSCRIBE_PATTERN:
1380 if (option (OPTNEWS)) {
1381 regex_t *rx = p_new(regex_t, 1);
1383 int j = menu->current;
1385 NNTP_SERVER *news = CurrentNewsSrv;
1387 if (i == OP_SUBSCRIBE_PATTERN || i == OP_UNSUBSCRIBE_PATTERN) {
1392 if (i == OP_SUBSCRIBE_PATTERN)
1393 snprintf (tmp, sizeof (tmp), _("Subscribe pattern: "));
1395 snprintf (tmp, sizeof (tmp), _("Unsubscribe pattern: "));
1396 if (mutt_get_field (tmp, buf, sizeof (buf), 0) != 0 || !buf[0]) {
1401 if ((err = REGCOMP (rx, s, REG_NOSUB)) != 0) {
1402 regerror (err, rx, buf, sizeof (buf));
1405 mutt_error ("%s", buf);
1408 menu->redraw = REDRAW_FULL;
1411 else if (!state.entrylen) {
1412 mutt_error _("No newsgroups match the mask");
1417 for (; j < state.entrylen; j++) {
1418 struct folder_file *folderf = &state.entry[j];
1420 if (i == OP_BROWSER_SUBSCRIBE || i == OP_BROWSER_UNSUBSCRIBE ||
1421 regexec (rx, folderf->name, 0, NULL, 0) == 0) {
1422 if (i == OP_BROWSER_SUBSCRIBE || i == OP_SUBSCRIBE_PATTERN)
1423 nd = mutt_newsgroup_subscribe (news, folderf->name);
1425 nd = mutt_newsgroup_unsubscribe (news, folderf->name);
1427 if (i == OP_BROWSER_SUBSCRIBE || i == OP_BROWSER_UNSUBSCRIBE) {
1428 if (menu->current + 1 < menu->max)
1430 menu->redraw = REDRAW_MOTION_RESYNCH;
1434 if (i == OP_SUBSCRIBE_PATTERN) {
1435 string_list_t *grouplist = NULL;
1438 grouplist = news->list;
1439 for (; grouplist; grouplist = grouplist->next) {
1440 nd = (NNTP_DATA *) grouplist->data;
1441 if (nd && nd->group && !nd->subscribed) {
1442 if (regexec (rx, nd->group, 0, NULL, 0) == 0) {
1443 mutt_newsgroup_subscribe (news, nd->group);
1444 add_folder (menu, &state, nd->group, NULL, nd, nd->new);
1448 init_menu (&state, menu, title, sizeof (title), buffy);
1450 mutt_newsrc_update (news);
1451 nntp_clear_cacheindex (news);
1452 if (i != OP_BROWSER_SUBSCRIBE && i != OP_BROWSER_UNSUBSCRIBE)
1457 #endif /* USE_NNTP */
1459 if (i == OP_BROWSER_SUBSCRIBE)
1460 imap_subscribe (state.entry[menu->current].name, 1);
1462 imap_subscribe (state.entry[menu->current].name, 0);
1470 m_strcpy(LastDir, sizeof(LastDir), LastDirBackup);