2 * Copyright (C) 1996-2000,2007,2010,2013 Michael R. Elkins <me@mutt.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24 #include "mutt_curses.h"
25 #include "mutt_menu.h"
45 static const struct mapping_t FolderHelp[] = {
46 { N_("Exit"), OP_EXIT },
47 { N_("Chdir"), OP_CHANGE_DIRECTORY },
48 { N_("Mask"), OP_ENTER_MASK },
49 { N_("Help"), OP_HELP },
53 typedef struct folder_t
55 struct folder_file *ff;
59 static BUFFER *LastDir = NULL;
60 static BUFFER *LastDirBackup = NULL;
62 void mutt_browser_cleanup (void)
64 mutt_buffer_free (&LastDir);
65 mutt_buffer_free (&LastDirBackup);
68 /* Frees up the memory allocated for the local-global variables. */
69 static void destroy_state (struct browser_state *state)
73 for (c = 0; c < state->entrylen; c++)
75 FREE (&((state->entry)[c].display_name));
76 FREE (&((state->entry)[c].full_path));
79 FREE (&state->folder);
84 static int browser_compare_subject (const void *a, const void *b)
86 struct folder_file *pa = (struct folder_file *) a;
87 struct folder_file *pb = (struct folder_file *) b;
89 int r = mutt_strcoll (pa->display_name, pb->display_name);
91 return ((BrowserSort & SORT_REVERSE) ? -r : r);
94 static int browser_compare_date (const void *a, const void *b)
96 struct folder_file *pa = (struct folder_file *) a;
97 struct folder_file *pb = (struct folder_file *) b;
99 int r = pa->mtime - pb->mtime;
101 return ((BrowserSort & SORT_REVERSE) ? -r : r);
104 static int browser_compare_size (const void *a, const void *b)
106 struct folder_file *pa = (struct folder_file *) a;
107 struct folder_file *pb = (struct folder_file *) b;
109 int r = pa->size - pb->size;
111 return ((BrowserSort & SORT_REVERSE) ? -r : r);
114 static int browser_compare_count (const void *a, const void *b)
116 struct folder_file *pa = (struct folder_file *) a;
117 struct folder_file *pb = (struct folder_file *) b;
119 int r = pa->msg_count - pb->msg_count;
121 return ((BrowserSort & SORT_REVERSE) ? -r : r);
124 static int browser_compare_unread (const void *a, const void *b)
126 struct folder_file *pa = (struct folder_file *) a;
127 struct folder_file *pb = (struct folder_file *) b;
129 int r = pa->msg_unread - pb->msg_unread;
131 return ((BrowserSort & SORT_REVERSE) ? -r : r);
134 static void browser_sort (struct browser_state *state)
136 int (*f) (const void *, const void *);
138 switch (BrowserSort & SORT_MASK)
143 f = browser_compare_date;
146 f = browser_compare_size;
149 f = browser_compare_count;
152 f = browser_compare_unread;
156 f = browser_compare_subject;
159 qsort (state->entry, state->entrylen, sizeof (struct folder_file), f);
162 static int link_is_dir (const char *full_path)
167 if (stat (full_path, &st) == 0)
168 retval = S_ISDIR (st.st_mode);
174 folder_format_str (char *dest, size_t destlen, size_t col, int cols, char op, const char *src,
175 const char *fmt, const char *ifstring, const char *elsestring,
176 unsigned long data, format_flag flags)
178 char fn[SHORT_STRING], tmp[SHORT_STRING], permission[11];
179 char date[SHORT_STRING], *t_fmt;
181 FOLDER *folder = (FOLDER *) data;
184 int optional = (flags & MUTT_FORMAT_OPTIONAL);
189 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
190 snprintf (dest, destlen, tmp, folder->num + 1);
195 if (folder->ff->local)
197 int do_locales = TRUE;
201 t_fmt = NONULL(DateFmt);
211 t_fmt = tnow - folder->ff->mtime < 31536000 ? "%b %d %H:%M" : "%b %d %Y";
215 setlocale (LC_TIME, "C");
216 strftime (date, sizeof (date), t_fmt, localtime (&folder->ff->mtime));
218 setlocale (LC_TIME, "");
220 mutt_format_s (dest, destlen, fmt, date);
223 mutt_format_s (dest, destlen, fmt, "");
228 char *s = NONULL (folder->ff->display_name);
230 snprintf (fn, sizeof (fn), "%s%s", s,
232 (S_ISLNK (folder->ff->mode) ?
234 (S_ISDIR (folder->ff->mode) ?
236 ((folder->ff->mode & S_IXUSR) != 0 ?
241 mutt_format_s (dest, destlen, fmt, fn);
245 if (folder->ff->local)
247 snprintf (permission, sizeof (permission), "%c%c%c%c%c%c%c%c%c%c",
248 S_ISDIR(folder->ff->mode) ? 'd' : (S_ISLNK(folder->ff->mode) ? 'l' : '-'),
249 (folder->ff->mode & S_IRUSR) != 0 ? 'r': '-',
250 (folder->ff->mode & S_IWUSR) != 0 ? 'w' : '-',
251 (folder->ff->mode & S_ISUID) != 0 ? 's' : (folder->ff->mode & S_IXUSR) != 0 ? 'x': '-',
252 (folder->ff->mode & S_IRGRP) != 0 ? 'r' : '-',
253 (folder->ff->mode & S_IWGRP) != 0 ? 'w' : '-',
254 (folder->ff->mode & S_ISGID) != 0 ? 's' : (folder->ff->mode & S_IXGRP) != 0 ? 'x': '-',
255 (folder->ff->mode & S_IROTH) != 0 ? 'r' : '-',
256 (folder->ff->mode & S_IWOTH) != 0 ? 'w' : '-',
257 (folder->ff->mode & S_ISVTX) != 0 ? 't' : (folder->ff->mode & S_IXOTH) != 0 ? 'x': '-');
258 mutt_format_s (dest, destlen, fmt, permission);
261 else if (folder->ff->imap)
263 /* mark folders with subfolders AND mail */
264 snprintf (permission, sizeof (permission), "IMAP %c",
265 (folder->ff->inferiors && folder->ff->selectable) ? '+' : ' ');
266 mutt_format_s (dest, destlen, fmt, permission);
270 mutt_format_s (dest, destlen, fmt, "");
274 if (folder->ff->local)
276 if ((gr = getgrgid (folder->ff->gid)))
277 mutt_format_s (dest, destlen, fmt, gr->gr_name);
280 snprintf (tmp, sizeof (tmp), "%%%sld", fmt);
281 snprintf (dest, destlen, tmp, folder->ff->gid);
285 mutt_format_s (dest, destlen, fmt, "");
289 if (folder->ff->local)
291 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
292 snprintf (dest, destlen, tmp, folder->ff->nlink);
295 mutt_format_s (dest, destlen, fmt, "");
301 if (folder->ff->has_buffy)
303 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
304 snprintf (dest, destlen, tmp, folder->ff->msg_count);
307 mutt_format_s (dest, destlen, fmt, "");
309 else if (!folder->ff->msg_count)
314 snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
315 snprintf (dest, destlen, tmp, folder->ff->new ? 'N' : ' ');
321 if (folder->ff->has_buffy)
323 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
324 snprintf (dest, destlen, tmp, folder->ff->msg_unread);
327 mutt_format_s (dest, destlen, fmt, "");
329 else if (!folder->ff->msg_unread)
334 if (folder->ff->local)
336 mutt_pretty_size(fn, sizeof(fn), folder->ff->size);
337 snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
338 snprintf (dest, destlen, tmp, fn);
341 mutt_format_s (dest, destlen, fmt, "");
345 snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
346 snprintf (dest, destlen, tmp, folder->ff->tagged ? '*' : ' ');
350 if (folder->ff->local)
352 if ((pw = getpwuid (folder->ff->uid)))
353 mutt_format_s (dest, destlen, fmt, pw->pw_name);
356 snprintf (tmp, sizeof (tmp), "%%%sld", fmt);
357 snprintf (dest, destlen, tmp, folder->ff->uid);
361 mutt_format_s (dest, destlen, fmt, "");
365 snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
366 snprintf (dest, destlen, tmp, op);
371 mutt_FormatString (dest, destlen, col, cols, ifstring, folder_format_str, data, 0);
372 else if (flags & MUTT_FORMAT_OPTIONAL)
373 mutt_FormatString (dest, destlen, col, cols, elsestring, folder_format_str, data, 0);
378 static void add_folder (MUTTMENU *m, struct browser_state *state,
379 const char *display_name, const char *full_path,
380 const struct stat *s, BUFFY *b)
382 if (state->entrylen == state->entrymax)
384 /* need to allocate more space */
385 safe_realloc (&state->entry,
386 sizeof (struct folder_file) * (state->entrymax += 256));
387 memset (&state->entry[state->entrylen], 0,
388 sizeof (struct folder_file) * 256);
390 m->data = state->entry;
395 (state->entry)[state->entrylen].mode = s->st_mode;
396 (state->entry)[state->entrylen].mtime = s->st_mtime;
397 (state->entry)[state->entrylen].size = s->st_size;
398 (state->entry)[state->entrylen].gid = s->st_gid;
399 (state->entry)[state->entrylen].uid = s->st_uid;
400 (state->entry)[state->entrylen].nlink = s->st_nlink;
402 (state->entry)[state->entrylen].local = 1;
407 (state->entry)[state->entrylen].has_buffy = 1;
408 (state->entry)[state->entrylen].new = b->new;
409 (state->entry)[state->entrylen].msg_count = b->msg_count;
410 (state->entry)[state->entrylen].msg_unread = b->msg_unread;
413 (state->entry)[state->entrylen].display_name = safe_strdup (display_name);
414 (state->entry)[state->entrylen].full_path = safe_strdup (full_path);
416 (state->entry)[state->entrylen].imap = 0;
421 static void init_state (struct browser_state *state, MUTTMENU *menu)
424 state->entrymax = 256;
425 state->entry = (struct folder_file *) safe_calloc (state->entrymax, sizeof (struct folder_file));
427 state->imap_browse = 0;
430 menu->data = state->entry;
433 static int examine_directory (MUTTMENU *menu, struct browser_state *state,
434 const char *d, const char *prefix)
439 BUFFER *full_path = NULL;
442 while (stat (d, &s) == -1)
446 /* The last used directory is deleted, try to use the parent dir. */
447 char *c = strrchr (d, '/');
459 if (!S_ISDIR (s.st_mode))
461 mutt_error (_("%s is not a directory."), d);
465 mutt_buffy_check (0);
467 if ((dp = opendir (d)) == NULL)
473 full_path = mutt_buffer_pool_get ();
474 init_state (state, menu);
476 while ((de = readdir (dp)) != NULL)
478 if (mutt_strcmp (de->d_name, ".") == 0)
479 continue; /* we don't need . */
481 if (prefix && *prefix && mutt_strncmp (prefix, de->d_name, mutt_strlen (prefix)) != 0)
483 if (!((regexec (Mask.rx, de->d_name, 0, NULL, 0) == 0) ^ Mask.not))
486 mutt_buffer_concat_path (full_path, d, de->d_name);
487 if (lstat (mutt_b2s (full_path), &s) == -1)
490 /* No size for directories or symlinks */
491 if (S_ISDIR (s.st_mode) || S_ISLNK (s.st_mode))
493 else if (! S_ISREG (s.st_mode))
497 while (tmp && mutt_strcmp (mutt_b2s (full_path), mutt_b2s (tmp->pathbuf)))
499 if (tmp && Context &&
500 !mutt_strcmp (tmp->realpath, Context->realpath))
502 tmp->msg_count = Context->msgcount;
503 tmp->msg_unread = Context->unread;
505 add_folder (menu, state, de->d_name, mutt_b2s (full_path), &s, tmp);
508 browser_sort (state);
510 mutt_buffer_pool_release (&full_path);
514 static int examine_mailboxes (MUTTMENU *menu, struct browser_state *state)
517 BUFFY *tmp = Incoming;
518 BUFFER *mailbox = NULL;
523 mutt_buffy_check (0);
525 mailbox = mutt_buffer_pool_get ();
526 md = mutt_buffer_pool_get ();
527 init_state (state, menu);
532 !mutt_strcmp (tmp->realpath, Context->realpath))
534 tmp->msg_count = Context->msgcount;
535 tmp->msg_unread = Context->unread;
538 mutt_buffer_strcpy (mailbox, mutt_b2s (tmp->pathbuf));
539 if (option (OPTBROWSERABBRMAILBOXES))
540 mutt_buffer_pretty_mailbox (mailbox);
543 if (mx_is_imap (mutt_b2s (tmp->pathbuf)))
545 add_folder (menu, state, mutt_b2s (mailbox), mutt_b2s (tmp->pathbuf), NULL, tmp);
550 if (mx_is_pop (mutt_b2s (tmp->pathbuf)))
552 add_folder (menu, state, mutt_b2s (mailbox), mutt_b2s (tmp->pathbuf), NULL, tmp);
556 if (lstat (mutt_b2s (tmp->pathbuf), &s) == -1)
559 if ((! S_ISREG (s.st_mode)) && (! S_ISDIR (s.st_mode)) &&
560 (! S_ISLNK (s.st_mode)))
563 if (mx_is_maildir (mutt_b2s (tmp->pathbuf)))
567 mutt_buffer_printf (md, "%s/new", mutt_b2s (tmp->pathbuf));
568 if (stat (mutt_b2s (md), &s) < 0)
570 mutt_buffer_printf (md, "%s/cur", mutt_b2s (tmp->pathbuf));
571 if (stat (mutt_b2s (md), &st2) < 0)
573 if (st2.st_mtime > s.st_mtime)
574 s.st_mtime = st2.st_mtime;
577 add_folder (menu, state, mutt_b2s (mailbox), mutt_b2s (tmp->pathbuf), &s, tmp);
579 while ((tmp = tmp->next));
580 browser_sort (state);
582 mutt_buffer_pool_release (&mailbox);
583 mutt_buffer_pool_release (&md);
587 static int select_file_search (MUTTMENU *menu, regex_t *re, int n)
589 return (regexec (re, ((struct folder_file *) menu->data)[n].display_name, 0, NULL, 0));
592 static void folder_entry (char *s, size_t slen, MUTTMENU *menu, int num)
596 folder.ff = &((struct folder_file *) menu->data)[num];
599 mutt_FormatString (s, slen, 0, MuttIndexWindow->cols, NONULL(FolderFormat), folder_format_str,
600 (unsigned long) &folder, MUTT_FORMAT_ARROWCURSOR);
603 static void set_sticky_cursor (struct browser_state *state, MUTTMENU *menu, const char *defaultsel)
607 if (option (OPTBROWSERSTICKYCURSOR) && defaultsel && *defaultsel)
609 for (i = 0; i < menu->max; i++)
611 if (!mutt_strcmp (defaultsel, state->entry[i].full_path))
620 static void init_menu (struct browser_state *state, MUTTMENU *menu, char *title,
621 size_t titlelen, int buffy, const char *defaultsel)
625 path = mutt_buffer_pool_get ();
627 menu->max = state->entrylen;
629 if (menu->current >= menu->max)
630 menu->current = menu->max - 1;
631 if (menu->current < 0)
633 if (menu->top > menu->current)
639 snprintf (title, titlelen, _("Mailboxes [%d]"), mutt_buffy_check (0));
642 mutt_buffer_strcpy (path, mutt_b2s (LastDir));
643 mutt_buffer_pretty_mailbox (path);
645 if (state->imap_browse && option (OPTIMAPLSUB))
646 snprintf (title, titlelen, _("Subscribed [%s], File mask: %s"),
647 mutt_b2s (path), NONULL (Mask.pattern));
650 snprintf (title, titlelen, _("Directory [%s], File mask: %s"),
651 mutt_b2s (path), NONULL(Mask.pattern));
653 menu->redraw = REDRAW_FULL;
655 set_sticky_cursor (state, menu, defaultsel);
657 mutt_buffer_pool_release (&path);
660 static int file_tag (MUTTMENU *menu, int n, int m)
662 struct folder_file *ff = &(((struct folder_file *)menu->data)[n]);
664 if (S_ISDIR (ff->mode) ||
665 (S_ISLNK (ff->mode) && link_is_dir (ff->full_path)))
667 mutt_error _("Can't attach a directory!");
672 ff->tagged = (m >= 0 ? m : !ff->tagged);
674 return ff->tagged - ot;
677 void _mutt_select_file (char *f, size_t flen, int flags, char ***files, int *numfiles)
679 BUFFER *f_buf = NULL;
681 f_buf = mutt_buffer_pool_get ();
683 mutt_buffer_strcpy (f_buf, NONULL (f));
684 _mutt_buffer_select_file (f_buf, flags, files, numfiles);
685 strfcpy (f, mutt_b2s (f_buf), flen);
687 mutt_buffer_pool_release (&f_buf);
690 void _mutt_buffer_select_file (BUFFER *f, int flags, char ***files, int *numfiles)
693 BUFFER *prefix = NULL;
695 BUFFER *OldLastDir = NULL;
696 BUFFER *defaultsel = NULL;
697 char helpstr[LONG_STRING];
699 struct browser_state state;
700 MUTTMENU *menu = NULL;
702 int op, killPrefix = 0;
704 int multiple = (flags & MUTT_SEL_MULTI) ? 1 : 0;
705 int folder = (flags & MUTT_SEL_FOLDER) ? 1 : 0;
706 int buffy = (flags & MUTT_SEL_BUFFY) ? 1 : 0;
708 buffy = buffy && folder;
710 buf = mutt_buffer_pool_get ();
711 prefix = mutt_buffer_pool_get ();
712 tmp = mutt_buffer_pool_get ();
713 OldLastDir = mutt_buffer_pool_get ();
714 defaultsel = mutt_buffer_pool_get ();
716 memset (&state, 0, sizeof (struct browser_state));
720 LastDir = mutt_buffer_new ();
721 mutt_buffer_increase_size (LastDir, _POSIX_PATH_MAX);
722 LastDirBackup = mutt_buffer_new ();
723 mutt_buffer_increase_size (LastDirBackup, _POSIX_PATH_MAX);
727 mutt_buffer_strcpy (LastDirBackup, mutt_b2s (LastDir));
731 mutt_buffer_expand_path (f);
733 if (mx_is_imap (mutt_b2s (f)))
735 init_state (&state, NULL);
736 state.imap_browse = 1;
737 if (!imap_browse (mutt_b2s (f), &state))
738 mutt_buffer_strcpy (LastDir, state.folder);
743 for (i = mutt_buffer_len (f) - 1;
744 i > 0 && (mutt_b2s (f))[i] != '/' ;
748 if ((mutt_b2s (f))[0] == '/')
749 mutt_buffer_strcpy_n (LastDir, mutt_b2s (f), i);
752 mutt_getcwd (LastDir);
753 mutt_buffer_addch (LastDir, '/');
754 mutt_buffer_addstr_n (LastDir, mutt_b2s (f), i);
759 if ((mutt_b2s (f))[0] == '/')
760 mutt_buffer_strcpy (LastDir, "/");
762 mutt_getcwd (LastDir);
765 if (i <= 0 && (mutt_b2s (f))[0] != '/')
766 mutt_buffer_strcpy (prefix, mutt_b2s (f));
768 mutt_buffer_strcpy (prefix, mutt_b2s (f) + i + 1);
777 mutt_getcwd (LastDir);
778 else if (!*(mutt_b2s (LastDir)))
779 mutt_buffer_strcpy (LastDir, NONULL(Maildir));
782 mutt_buffer_strcpy (defaultsel, NONULL (Context->path));
785 if (!buffy && mx_is_imap (mutt_b2s (LastDir)))
787 init_state (&state, NULL);
788 state.imap_browse = 1;
789 imap_browse (mutt_b2s (LastDir), &state);
790 browser_sort (&state);
795 i = mutt_buffer_len (LastDir);
796 while (i && mutt_b2s (LastDir)[--i] == '/')
797 LastDir->data[i] = '\0';
798 mutt_buffer_fix_dptr (LastDir);
799 if (!*(mutt_b2s (LastDir)))
800 mutt_getcwd (LastDir);
804 mutt_buffer_clear (f);
808 if (examine_mailboxes (NULL, &state) == -1)
813 if (!state.imap_browse)
815 if (examine_directory (NULL, &state, mutt_b2s (LastDir), mutt_b2s (prefix)) == -1)
818 menu = mutt_new_menu (MENU_FOLDER);
819 menu->make_entry = folder_entry;
820 menu->search = select_file_search;
822 menu->data = state.entry;
824 menu->tag = file_tag;
826 menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_FOLDER,
828 mutt_push_current_menu (menu);
830 init_menu (&state, menu, title, sizeof (title), buffy, mutt_b2s (defaultsel));
834 op = mutt_menuLoop (menu);
837 mutt_buffer_strcpy (defaultsel, state.entry[menu->current].full_path);
841 case OP_DESCEND_DIRECTORY:
842 case OP_GENERIC_SELECT_ENTRY:
846 mutt_error _("No files match the file mask");
850 if (S_ISDIR (state.entry[menu->current].mode) ||
851 (S_ISLNK (state.entry[menu->current].mode) &&
852 link_is_dir (state.entry[menu->current].full_path))
854 || state.entry[menu->current].inferiors
858 if (op == OP_DESCEND_DIRECTORY
859 || (mx_get_magic (state.entry[menu->current].full_path) <= 0)
861 || state.entry[menu->current].inferiors
865 /* save the old directory */
866 mutt_buffer_strcpy (OldLastDir, mutt_b2s (LastDir));
868 mutt_buffer_strcpy (defaultsel, mutt_b2s (OldLastDir));
869 if (mutt_buffer_len (defaultsel) && (*(defaultsel->dptr - 1) == '/'))
872 *(defaultsel->dptr) = '\0';
875 if (mutt_strcmp (state.entry[menu->current].display_name, "..") == 0)
877 size_t lastdirlen = mutt_buffer_len (LastDir);
879 if ((lastdirlen > 1) &&
880 mutt_strcmp ("..", mutt_b2s (LastDir) + lastdirlen - 2) == 0)
882 mutt_buffer_addstr (LastDir, "/..");
888 p = strrchr (mutt_b2s (LastDir) + 1, '/');
893 mutt_buffer_fix_dptr (LastDir);
897 if (mutt_b2s (LastDir)[0] == '/')
898 mutt_buffer_strcpy (LastDir, "/");
900 mutt_buffer_addstr (LastDir, "/..");
906 mutt_buffer_strcpy (LastDir, state.entry[menu->current].full_path);
909 else if (state.imap_browse)
913 mutt_buffer_strcpy (LastDir, state.entry[menu->current].full_path);
914 /* tack on delimiter here */
916 /* special case "" needs no delimiter */
917 url_parse_ciss (&url, state.entry[menu->current].full_path);
919 (state.entry[menu->current].delim != '\0'))
921 mutt_buffer_addch (LastDir, state.entry[menu->current].delim);
927 mutt_buffer_strcpy (LastDir, state.entry[menu->current].full_path);
930 destroy_state (&state);
933 mutt_buffer_clear (prefix);
938 if (state.imap_browse)
940 init_state (&state, NULL);
941 state.imap_browse = 1;
942 imap_browse (mutt_b2s (LastDir), &state);
943 browser_sort (&state);
944 menu->data = state.entry;
948 if (examine_directory (menu, &state, mutt_b2s (LastDir), mutt_b2s (prefix)) == -1)
950 /* try to restore the old values */
951 mutt_buffer_strcpy (LastDir, mutt_b2s (OldLastDir));
952 if (examine_directory (menu, &state, mutt_b2s (LastDir), mutt_b2s (prefix)) == -1)
954 mutt_buffer_strcpy (LastDir, NONULL(Homedir));
960 init_menu (&state, menu, title, sizeof (title), buffy, mutt_b2s (defaultsel));
964 else if (op == OP_DESCEND_DIRECTORY)
966 mutt_error (_("%s is not a directory."), state.entry[menu->current].display_name);
970 mutt_buffer_strcpy (f, state.entry[menu->current].full_path);
972 /* Fall through to OP_EXIT */
982 *numfiles = menu->tagged;
983 tfiles = safe_calloc (*numfiles, sizeof (char *));
984 for (i = 0, j = 0; i < state.entrylen; i++)
985 if (state.entry[i].tagged)
986 tfiles[j++] = safe_strdup (state.entry[i].full_path);
989 else if ((mutt_b2s (f))[0]) /* no tagged entries. return selected entry */
992 tfiles = safe_calloc (*numfiles, sizeof (char *));
993 tfiles[0] = safe_strdup (mutt_b2s (f));
998 destroy_state (&state);
1001 case OP_BROWSER_TELL:
1003 mutt_message("%s", state.entry[menu->current].full_path);
1007 case OP_BROWSER_SUBSCRIBE:
1008 imap_subscribe (state.entry[menu->current].full_path, 1);
1011 case OP_BROWSER_UNSUBSCRIBE:
1012 imap_subscribe (state.entry[menu->current].full_path, 0);
1015 case OP_BROWSER_TOGGLE_LSUB:
1016 if (option (OPTIMAPLSUB))
1017 unset_option (OPTIMAPLSUB);
1019 set_option (OPTIMAPLSUB);
1021 mutt_unget_event (0, OP_CHECK_NEW);
1024 case OP_CREATE_MAILBOX:
1025 if (!state.imap_browse)
1027 mutt_error (_("Create is only supported for IMAP mailboxes"));
1031 if (!imap_mailbox_create (mutt_b2s (LastDir), defaultsel))
1033 /* TODO: find a way to detect if the new folder would appear in
1034 * this window, and insert it without starting over. */
1035 destroy_state (&state);
1036 init_state (&state, NULL);
1037 state.imap_browse = 1;
1038 imap_browse (mutt_b2s (LastDir), &state);
1039 browser_sort (&state);
1040 menu->data = state.entry;
1043 init_menu (&state, menu, title, sizeof (title), buffy, mutt_b2s (defaultsel));
1045 /* else leave error on screen */
1048 case OP_RENAME_MAILBOX:
1049 if (!state.entry[menu->current].imap)
1050 mutt_error (_("Rename is only supported for IMAP mailboxes"));
1053 int nentry = menu->current;
1055 if (imap_mailbox_rename (state.entry[nentry].full_path, defaultsel) >= 0)
1057 destroy_state (&state);
1058 init_state (&state, NULL);
1059 state.imap_browse = 1;
1060 imap_browse (mutt_b2s (LastDir), &state);
1061 browser_sort (&state);
1062 menu->data = state.entry;
1065 init_menu (&state, menu, title, sizeof (title), buffy, mutt_b2s (defaultsel));
1070 case OP_DELETE_MAILBOX:
1071 if (!state.entry[menu->current].imap)
1072 mutt_error (_("Delete is only supported for IMAP mailboxes"));
1075 char msg[SHORT_STRING];
1077 int nentry = menu->current;
1079 imap_parse_path (state.entry[nentry].full_path, &mx);
1082 mutt_error _("Cannot delete root folder");
1085 snprintf (msg, sizeof (msg), _("Really delete mailbox \"%s\"?"),
1087 if (mutt_yesorno (msg, MUTT_NO) == MUTT_YES)
1089 if (!imap_delete_mailbox (Context, mx))
1091 /* free the mailbox from the browser */
1092 FREE (&((state.entry)[nentry].display_name));
1093 FREE (&((state.entry)[nentry].full_path));
1094 /* and move all other entries up */
1095 if (nentry+1 < state.entrylen)
1096 memmove (state.entry + nentry, state.entry + nentry + 1,
1097 sizeof (struct folder_file) * (state.entrylen - (nentry+1)));
1098 memset (&state.entry[state.entrylen - 1], 0,
1099 sizeof (struct folder_file));
1101 mutt_message _("Mailbox deleted.");
1102 mutt_buffer_clear (defaultsel);
1103 init_menu (&state, menu, title, sizeof (title), buffy, mutt_b2s (defaultsel));
1106 mutt_error _("Mailbox deletion failed.");
1109 mutt_message _("Mailbox not deleted.");
1115 case OP_CHANGE_DIRECTORY:
1117 mutt_buffer_strcpy (buf, mutt_b2s (LastDir));
1118 mutt_buffer_clear (defaultsel);
1120 if (!state.imap_browse)
1123 /* add '/' at the end of the directory name if not already there */
1124 size_t len = mutt_buffer_len (LastDir);
1125 if (len && (mutt_b2s (LastDir)[len-1] != '/'))
1126 mutt_buffer_addch (buf, '/');
1129 /* buf comes from the buffer pool, so defaults to size LONG_STRING */
1130 if ((mutt_buffer_get_field (_("Chdir to: "), buf, MUTT_FILE) == 0) &&
1131 mutt_buffer_len (buf))
1134 mutt_buffer_expand_path (buf);
1136 if (mx_is_imap (mutt_b2s (buf)))
1138 mutt_buffer_strcpy (LastDir, mutt_b2s (buf));
1139 destroy_state (&state);
1140 init_state (&state, NULL);
1141 state.imap_browse = 1;
1142 imap_browse (mutt_b2s (LastDir), &state);
1143 browser_sort (&state);
1144 menu->data = state.entry;
1147 init_menu (&state, menu, title, sizeof (title), buffy, mutt_b2s (defaultsel));
1152 if (*(mutt_b2s (buf)) != '/')
1154 /* in case dir is relative, make it relative to LastDir,
1155 * not current working dir */
1156 mutt_buffer_concat_path (tmp, mutt_b2s (LastDir), mutt_b2s (buf));
1157 mutt_buffer_strcpy (buf, mutt_b2s (tmp));
1159 if (stat (mutt_b2s (buf), &st) == 0)
1161 if (S_ISDIR (st.st_mode))
1163 destroy_state (&state);
1164 if (examine_directory (menu, &state, mutt_b2s (buf), mutt_b2s (prefix)) == 0)
1165 mutt_buffer_strcpy (LastDir, mutt_b2s (buf));
1168 mutt_error _("Error scanning directory.");
1169 if (examine_directory (menu, &state, mutt_b2s (LastDir), mutt_b2s (prefix)) == -1)
1176 init_menu (&state, menu, title, sizeof (title), buffy, mutt_b2s (defaultsel));
1179 mutt_error (_("%s is not a directory."), mutt_b2s (buf));
1182 mutt_perror (mutt_b2s (buf));
1189 mutt_buffer_strcpy (buf, NONULL(Mask.pattern));
1190 /* buf comes from the buffer pool, so defaults to size LONG_STRING */
1191 if (mutt_buffer_get_field (_("File Mask: "), buf, 0) == 0)
1193 regex_t *rx = (regex_t *) safe_malloc (sizeof (regex_t));
1194 const char *s = mutt_b2s (buf);
1198 /* assume that the user wants to see everything */
1199 if (!(mutt_buffer_len (buf)))
1200 mutt_buffer_strcpy (buf, ".");
1209 if ((err = REGCOMP (rx, s, REG_NOSUB)) != 0)
1211 regerror (err, rx, buf->data, buf->dsize);
1212 mutt_buffer_fix_dptr (buf);
1214 mutt_error ("%s", mutt_b2s (buf));
1218 mutt_str_replace (&Mask.pattern, mutt_b2s (buf));
1224 destroy_state (&state);
1226 if (state.imap_browse)
1228 init_state (&state, NULL);
1229 state.imap_browse = 1;
1230 imap_browse (mutt_b2s (LastDir), &state);
1231 browser_sort (&state);
1232 menu->data = state.entry;
1233 init_menu (&state, menu, title, sizeof (title), buffy, mutt_b2s (defaultsel));
1237 if (examine_directory (menu, &state, mutt_b2s (LastDir), NULL) == 0)
1238 init_menu (&state, menu, title, sizeof (title), buffy, mutt_b2s (defaultsel));
1241 mutt_error _("Error scanning directory.");
1245 if (!state.entrylen)
1247 mutt_error _("No files match the file mask");
1255 case OP_SORT_REVERSE:
1259 int reverse = (op == OP_SORT_REVERSE);
1261 switch (mutt_multi_choice ((reverse) ?
1262 _("Reverse sort by (d)ate, (a)lpha, si(z)e, (c)ount, (u)nread, or do(n)'t sort? ") :
1263 _("Sort by (d)ate, (a)lpha, si(z)e, (c)ount, (u)nread, or do(n)'t sort? "),
1266 case -1: /* abort */
1270 case 1: /* (d)ate */
1271 BrowserSort = SORT_DATE;
1274 case 2: /* (a)lpha */
1275 BrowserSort = SORT_SUBJECT;
1278 case 3: /* si(z)e */
1279 BrowserSort = SORT_SIZE;
1282 case 4: /* (c)ount */
1283 BrowserSort = SORT_COUNT;
1286 case 5: /* (u)nread */
1287 BrowserSort = SORT_UNREAD;
1290 case 6: /* do(n)'t sort */
1291 BrowserSort = SORT_ORDER;
1297 BrowserSort |= reverse ? SORT_REVERSE : 0;
1298 browser_sort (&state);
1299 set_sticky_cursor (&state, menu, mutt_b2s (defaultsel));
1300 menu->redraw = REDRAW_FULL;
1305 case OP_TOGGLE_MAILBOXES:
1311 destroy_state (&state);
1312 mutt_buffer_clear (prefix);
1317 if (examine_mailboxes (menu, &state) == -1)
1321 else if (mx_is_imap (mutt_b2s (LastDir)))
1323 init_state (&state, NULL);
1324 state.imap_browse = 1;
1325 imap_browse (mutt_b2s (LastDir), &state);
1326 browser_sort (&state);
1327 menu->data = state.entry;
1330 else if (examine_directory (menu, &state, mutt_b2s (LastDir), mutt_b2s (prefix)) == -1)
1332 init_menu (&state, menu, title, sizeof (title), buffy, mutt_b2s (defaultsel));
1339 case OP_BROWSER_NEW_FILE:
1341 mutt_buffer_printf (buf, "%s/", mutt_b2s (LastDir));
1342 /* buf comes from the buffer pool, so defaults to size LONG_STRING */
1343 if (mutt_buffer_get_field (_("New file name: "), buf, MUTT_FILE) == 0)
1345 mutt_buffer_strcpy (f, mutt_b2s (buf));
1346 destroy_state (&state);
1351 case OP_BROWSER_VIEW_FILE:
1352 if (!state.entrylen)
1354 mutt_error _("No files match the file mask");
1359 if (state.entry[menu->current].selectable)
1361 mutt_buffer_strcpy (f, state.entry[menu->current].full_path);
1362 destroy_state (&state);
1367 if (S_ISDIR (state.entry[menu->current].mode) ||
1368 (S_ISLNK (state.entry[menu->current].mode) &&
1369 link_is_dir (state.entry[menu->current].full_path)))
1371 mutt_error _("Can't view a directory");
1378 b = mutt_make_file_attach (state.entry[menu->current].full_path);
1381 mutt_view_attachment (NULL, b, MUTT_REGULAR, NULL, NULL);
1382 mutt_free_body (&b);
1383 menu->redraw = REDRAW_FULL;
1386 mutt_error _("Error trying to view file");
1392 mutt_buffer_pool_release (&buf);
1393 mutt_buffer_pool_release (&prefix);
1394 mutt_buffer_pool_release (&tmp);
1395 mutt_buffer_pool_release (&OldLastDir);
1396 mutt_buffer_pool_release (&defaultsel);
1400 mutt_pop_current_menu (menu);
1401 mutt_menuDestroy (&menu);
1405 mutt_buffer_strcpy (LastDir, mutt_b2s (LastDirBackup));