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.
15 #include "mutt_curses.h"
16 #include "mutt_menu.h"
38 static struct mapping_t FolderHelp[] = {
39 {N_("Exit"), OP_EXIT},
40 {N_("Chdir"), OP_CHANGE_DIRECTORY},
41 {N_("Mask"), OP_ENTER_MASK},
42 {N_("Help"), OP_HELP},
47 static struct mapping_t FolderNewsHelp[] = {
48 {N_("Exit"), OP_EXIT},
49 {N_("List"), OP_TOGGLE_MAILBOXES},
50 {N_("Subscribe"), OP_BROWSER_SUBSCRIBE},
51 {N_("Unsubscribe"), OP_BROWSER_UNSUBSCRIBE},
52 {N_("Catchup"), OP_CATCHUP},
53 {N_("Mask"), OP_ENTER_MASK},
54 {N_("Help"), OP_HELP},
59 typedef struct folder_t {
60 struct folder_file *ff;
64 static char LastDir[_POSIX_PATH_MAX] = "";
65 static char LastDirBackup[_POSIX_PATH_MAX] = "";
67 /* Frees up the memory allocated for the local-global variables. */
68 static void destroy_state (struct browser_state *state)
72 for (c = 0; c < state->entrylen; c++) {
73 FREE (&((state->entry)[c].name));
74 FREE (&((state->entry)[c].desc));
75 FREE (&((state->entry)[c].st));
78 FREE (&state->folder);
83 static int browser_compare_subject (const void *a, const void *b)
85 struct folder_file *pa = (struct folder_file *) a;
86 struct folder_file *pb = (struct folder_file *) b;
88 int r = mutt_strcoll (pa->name, pb->name);
90 return ((BrowserSort & SORT_REVERSE) ? -r : r);
93 static int browser_compare_date (const void *a, const void *b)
95 struct folder_file *pa = (struct folder_file *) a;
96 struct folder_file *pb = (struct folder_file *) b;
98 int r = pa->mtime - pb->mtime;
100 return ((BrowserSort & SORT_REVERSE) ? -r : r);
103 static int browser_compare_size (const void *a, const void *b)
105 struct folder_file *pa = (struct folder_file *) a;
106 struct folder_file *pb = (struct folder_file *) b;
108 int r = pa->size - pb->size;
110 return ((BrowserSort & SORT_REVERSE) ? -r : r);
113 static void browser_sort (struct browser_state *state)
115 int (*f) (const void *, const void *);
117 switch (BrowserSort & SORT_MASK) {
122 if (option (OPTNEWS))
125 f = browser_compare_date;
129 if (option (OPTNEWS))
132 f = browser_compare_size;
136 f = browser_compare_subject;
139 qsort (state->entry, state->entrylen, sizeof (struct folder_file), f);
142 static int link_is_dir (const char *folder, const char *path)
145 char fullpath[_POSIX_PATH_MAX];
147 mutt_concat_path (fullpath, folder, path, sizeof (fullpath));
149 if (stat (fullpath, &st) == 0)
150 return (S_ISDIR (st.st_mode));
155 static const char *folder_format_str (char *dest, size_t destlen, char op,
156 const char *src, const char *fmt,
157 const char *ifstring,
158 const char *elsestring,
159 unsigned long data, format_flag flags)
161 char fn[SHORT_STRING], tmp[SHORT_STRING], permission[11];
162 char date[16], *t_fmt;
164 FOLDER *folder = (FOLDER *) data;
167 int optional = (flags & M_FORMAT_OPTIONAL);
171 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
172 snprintf (dest, destlen, tmp, folder->num + 1);
176 if (folder->ff->st != NULL) {
179 tnow - folder->ff->st->st_mtime <
180 31536000 ? "%b %d %H:%M" : "%b %d %Y";
181 strftime (date, sizeof (date), t_fmt,
182 localtime (&folder->ff->st->st_mtime));
183 mutt_format_s (dest, destlen, fmt, date);
186 mutt_format_s (dest, destlen, fmt, "");
194 if (folder->ff->imap)
195 s = NONULL (folder->ff->desc);
198 s = NONULL (folder->ff->name);
200 snprintf (fn, sizeof (fn), "%s%s", s,
201 folder->ff->st ? (S_ISLNK (folder->ff->st->st_mode) ? "@" :
202 (S_ISDIR (folder->ff->st->st_mode) ? "/" :
203 ((folder->ff->st->st_mode & S_IXUSR) !=
204 0 ? "*" : ""))) : "");
206 mutt_format_s (dest, destlen, fmt, fn);
210 if (folder->ff->st != NULL) {
211 snprintf (permission, sizeof (permission), "%c%c%c%c%c%c%c%c%c%c",
212 S_ISDIR (folder->ff->st->
213 st_mode) ? 'd' : (S_ISLNK (folder->ff->st->
214 st_mode) ? 'l' : '-'),
215 (folder->ff->st->st_mode & S_IRUSR) != 0 ? 'r' : '-',
216 (folder->ff->st->st_mode & S_IWUSR) != 0 ? 'w' : '-',
217 (folder->ff->st->st_mode & S_ISUID) !=
218 0 ? 's' : (folder->ff->st->st_mode & S_IXUSR) !=
220 (folder->ff->st->st_mode & S_IRGRP) != 0 ? 'r' : '-',
221 (folder->ff->st->st_mode & S_IWGRP) != 0 ? 'w' : '-',
222 (folder->ff->st->st_mode & S_ISGID) !=
223 0 ? 's' : (folder->ff->st->st_mode & S_IXGRP) !=
225 (folder->ff->st->st_mode & S_IROTH) != 0 ? 'r' : '-',
226 (folder->ff->st->st_mode & S_IWOTH) != 0 ? 'w' : '-',
227 (folder->ff->st->st_mode & S_ISVTX) !=
228 0 ? 't' : (folder->ff->st->st_mode & S_IXOTH) !=
230 mutt_format_s (dest, destlen, fmt, permission);
233 else if (folder->ff->imap) {
234 /* mark folders with subfolders AND mail */
235 snprintf (permission, sizeof (permission), "IMAP %c",
236 (folder->ff->inferiors
237 && folder->ff->selectable) ? '+' : ' ');
238 mutt_format_s (dest, destlen, fmt, permission);
242 mutt_format_s (dest, destlen, fmt, "");
246 if (folder->ff->st != NULL) {
247 if ((gr = getgrgid (folder->ff->st->st_gid)))
248 mutt_format_s (dest, destlen, fmt, gr->gr_name);
250 snprintf (tmp, sizeof (tmp), "%%%sld", fmt);
251 snprintf (dest, destlen, tmp, folder->ff->st->st_gid);
255 mutt_format_s (dest, destlen, fmt, "");
259 if (folder->ff->st != NULL) {
260 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
261 snprintf (dest, destlen, tmp, folder->ff->st->st_nlink);
264 mutt_format_s (dest, destlen, fmt, "");
269 if (mx_is_imap (folder->ff->desc)) {
271 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
272 snprintf (dest, destlen, tmp, folder->ff->new);
274 else if (!folder->ff->new)
279 snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
280 snprintf (dest, destlen, tmp, folder->ff->new ? 'N' : ' ');
284 if (folder->ff->st != NULL) {
285 snprintf (tmp, sizeof (tmp), "%%%sld", fmt);
286 snprintf (dest, destlen, tmp, (long) folder->ff->st->st_size);
289 mutt_format_s (dest, destlen, fmt, "");
293 snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
294 snprintf (dest, destlen, tmp, folder->ff->tagged ? '*' : ' ');
298 if (folder->ff->st != NULL) {
299 if ((pw = getpwuid (folder->ff->st->st_uid)))
300 mutt_format_s (dest, destlen, fmt, pw->pw_name);
302 snprintf (tmp, sizeof (tmp), "%%%sld", fmt);
303 snprintf (dest, destlen, tmp, folder->ff->st->st_uid);
307 mutt_format_s (dest, destlen, fmt, "");
311 snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
312 snprintf (dest, destlen, tmp, op);
317 mutt_FormatString (dest, destlen, ifstring, folder_format_str, data, 0);
318 else if (flags & M_FORMAT_OPTIONAL)
319 mutt_FormatString (dest, destlen, elsestring, folder_format_str, data, 0);
325 static const char *newsgroup_format_str (char *dest, size_t destlen, char op,
326 const char *src, const char *fmt,
327 const char *ifstring,
328 const char *elsestring,
332 char fn[SHORT_STRING], tmp[SHORT_STRING];
333 FOLDER *folder = (FOLDER *) data;
337 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
338 snprintf (dest, destlen, tmp, folder->num + 1);
342 strncpy (fn, folder->ff->name, sizeof (fn) - 1);
343 snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
344 snprintf (dest, destlen, tmp, fn);
348 snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
349 if (folder->ff->nd->subscribed)
350 snprintf (dest, destlen, tmp, ' ');
352 snprintf (dest, destlen, tmp, folder->ff->new ? 'N' : 'u');
356 snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
357 if (folder->ff->nd->deleted)
358 snprintf (dest, destlen, tmp, 'D');
360 snprintf (dest, destlen, tmp, folder->ff->nd->allowed ? ' ' : '-');
364 if (flags & M_FORMAT_OPTIONAL) {
365 if (folder->ff->nd->unread != 0)
366 mutt_FormatString (dest, destlen, ifstring, newsgroup_format_str,
369 mutt_FormatString (dest, destlen, elsestring, newsgroup_format_str,
372 else if (Context && Context->data == folder->ff->nd) {
373 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
374 snprintf (dest, destlen, tmp, Context->unread);
377 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
378 snprintf (dest, destlen, tmp, folder->ff->nd->unread);
383 if (Context && Context->data == folder->ff->nd) {
384 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
385 snprintf (dest, destlen, tmp, Context->new);
387 else if (option (OPTMARKOLD) &&
388 folder->ff->nd->lastCached >= folder->ff->nd->firstMessage &&
389 folder->ff->nd->lastCached <= folder->ff->nd->lastMessage) {
390 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
391 snprintf (dest, destlen, tmp,
392 folder->ff->nd->lastMessage - folder->ff->nd->lastCached);
395 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
396 snprintf (dest, destlen, tmp, folder->ff->nd->unread);
401 if (folder->ff->nd->desc != NULL) {
402 snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
403 snprintf (dest, destlen, tmp, folder->ff->nd->desc);
406 snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
407 snprintf (dest, destlen, tmp, "");
413 #endif /* USE_NNTP */
415 static void add_folder (MUTTMENU * m, struct browser_state *state,
416 const char *name, const struct stat *s,
419 if (state->entrylen == state->entrymax) {
420 /* need to allocate more space */
421 safe_realloc (&state->entry,
422 sizeof (struct folder_file) * (state->entrymax += 256));
423 memset (&state->entry[state->entrylen], 0,
424 sizeof (struct folder_file) * 256);
426 m->data = state->entry;
430 (state->entry)[state->entrylen].mode = s->st_mode;
431 (state->entry)[state->entrylen].mtime = s->st_mtime;
432 (state->entry)[state->entrylen].size = s->st_size;
434 (state->entry)[state->entrylen].st = safe_malloc (sizeof (struct stat));
435 memcpy ((state->entry)[state->entrylen].st, s, sizeof (struct stat));
438 (state->entry)[state->entrylen].new = new;
439 (state->entry)[state->entrylen].name = safe_strdup (name);
440 (state->entry)[state->entrylen].desc = safe_strdup (name);
442 (state->entry)[state->entrylen].imap = 0;
445 if (option (OPTNEWS))
446 (state->entry)[state->entrylen].nd = (NNTP_DATA *) data;
451 static void init_state (struct browser_state *state, MUTTMENU * menu)
454 state->entrymax = 256;
456 (struct folder_file *) safe_calloc (state->entrymax,
457 sizeof (struct folder_file));
459 state->imap_browse = 0;
462 menu->data = state->entry;
465 /* get list of all files/newsgroups with mask */
466 static int examine_directory (MUTTMENU * menu, struct browser_state *state,
467 char *d, const char *prefix)
470 if (option (OPTNEWS)) {
473 NNTP_SERVER *news = CurrentNewsSrv;
475 /* mutt_buffy_check (0); */
476 init_state (state, menu);
478 for (tmp = news->list; tmp; tmp = tmp->next) {
479 if (!(data = (NNTP_DATA *) tmp->data))
481 if (prefix && *prefix && strncmp (prefix, data->group,
482 strlen (prefix)) != 0)
484 if (!((regexec (Mask.rx, data->group, 0, NULL, 0) == 0) ^ Mask.not))
486 add_folder (menu, state, data->group, NULL, data, data->new);
490 #endif /* USE_NNTP */
495 char buffer[_POSIX_PATH_MAX + SHORT_STRING];
498 while (stat (d, &s) == -1) {
499 if (errno == ENOENT) {
500 /* The last used directory is deleted, try to use the parent dir. */
501 char *c = strrchr (d, '/');
512 if (!S_ISDIR (s.st_mode)) {
513 mutt_error (_("%s is not a directory."), d);
517 mutt_buffy_check (0);
519 if ((dp = opendir (d)) == NULL) {
524 init_state (state, menu);
526 while ((de = readdir (dp)) != NULL) {
527 if (mutt_strcmp (de->d_name, ".") == 0)
528 continue; /* we don't need . */
530 if (prefix && *prefix
531 && mutt_strncmp (prefix, de->d_name, mutt_strlen (prefix)) != 0)
533 if (!((regexec (Mask.rx, de->d_name, 0, NULL, 0) == 0) ^ Mask.not))
536 mutt_concat_path (buffer, d, de->d_name, sizeof (buffer));
537 if (lstat (buffer, &s) == -1)
540 if ((!S_ISREG (s.st_mode)) && (!S_ISDIR (s.st_mode)) &&
541 (!S_ISLNK (s.st_mode)))
545 while (tmp && mutt_strcmp (buffer, tmp->path))
547 add_folder (menu, state, de->d_name, &s, NULL, (tmp) ? tmp->new : 0);
551 draw_sidebar (CurrentMenu);
552 browser_sort (state);
556 /* get list of mailboxes/subscribed newsgroups */
557 static int examine_mailboxes (MUTTMENU * menu, struct browser_state *state)
560 char buffer[LONG_STRING];
563 if (option (OPTNEWS)) {
566 NNTP_SERVER *news = CurrentNewsSrv;
568 /* mutt_buffy_check (0); */
569 init_state (state, menu);
571 for (tmp = news->list; tmp; tmp = tmp->next) {
572 if ((data = (NNTP_DATA *) tmp->data) != NULL && (data->new ||
578 add_folder (menu, state, data->group, NULL, data, data->new);
584 BUFFY *tmp = Incoming;
588 mutt_buffy_check (0);
590 init_state (state, menu);
594 if (mx_is_imap (tmp->path)) {
595 add_folder (menu, state, tmp->path, NULL, NULL, tmp->new);
600 if (mx_is_pop (tmp->path)) {
601 add_folder (menu, state, tmp->path, NULL, NULL, tmp->new);
606 if (mx_is_nntp (tmp->path)) {
607 add_folder (menu, state, tmp->path, NULL, NULL, tmp->new);
611 if (lstat (tmp->path, &s) == -1)
614 if ((!S_ISREG (s.st_mode)) && (!S_ISDIR (s.st_mode)) &&
615 (!S_ISLNK (s.st_mode)))
618 strfcpy (buffer, NONULL (tmp->path), sizeof (buffer));
619 mutt_pretty_mailbox (buffer);
621 add_folder (menu, state, buffer, &s, NULL, tmp->new);
623 while ((tmp = tmp->next));
625 browser_sort (state);
629 static int select_file_search (MUTTMENU * menu, regex_t * re, int n)
632 if (option (OPTNEWS))
634 (re, ((struct folder_file *) menu->data)[n].desc, 0, NULL, 0));
637 (re, ((struct folder_file *) menu->data)[n].name, 0, NULL, 0));
640 static void folder_entry (char *s, size_t slen, MUTTMENU * menu, int num)
644 folder.ff = &((struct folder_file *) menu->data)[num];
648 if (option (OPTNEWS))
649 mutt_FormatString (s, slen, NONULL (GroupFormat), newsgroup_format_str,
650 (unsigned long) &folder, M_FORMAT_ARROWCURSOR);
653 mutt_FormatString (s, slen, NONULL (FolderFormat), folder_format_str,
654 (unsigned long) &folder, M_FORMAT_ARROWCURSOR);
657 static void init_menu (struct browser_state *state, MUTTMENU * menu,
658 char *title, size_t titlelen, int buffy)
660 char path[_POSIX_PATH_MAX];
662 menu->max = state->entrylen;
664 if (menu->current >= menu->max)
665 menu->current = menu->max - 1;
666 if (menu->current < 0)
668 if (menu->top > menu->current)
674 if (option (OPTNEWS)) {
676 snprintf (title, titlelen, "%s", _("Subscribed newsgroups"));
678 snprintf (title, titlelen, _("Newsgroups on server [%s]"),
679 CurrentNewsSrv->conn->account.host);
684 snprintf (title, titlelen, _("Mailboxes [%d]"), mutt_buffy_check (0));
686 strfcpy (path, LastDir, sizeof (path));
687 mutt_pretty_mailbox (path);
689 if (state->imap_browse && option (OPTIMAPLSUB))
690 snprintf (title, titlelen, _("Subscribed [%s], File mask: %s"),
691 path, NONULL (Mask.pattern));
694 snprintf (title, titlelen, _("Directory [%s], File mask: %s"),
695 path, NONULL (Mask.pattern));
697 menu->redraw = REDRAW_FULL;
700 static int file_tag (MUTTMENU * menu, int n, int m)
702 struct folder_file *ff = &(((struct folder_file *) menu->data)[n]);
705 if (S_ISDIR (ff->mode)
706 || (S_ISLNK (ff->mode) && link_is_dir (LastDir, ff->name))) {
707 mutt_error _("Can't attach a directory!");
713 ff->tagged = (m >= 0 ? m : !ff->tagged);
715 return ff->tagged - ot;
718 void _mutt_select_file (char *f, size_t flen, int flags, char ***files,
721 char buf[_POSIX_PATH_MAX];
722 char prefix[_POSIX_PATH_MAX] = "";
723 char helpstr[SHORT_STRING];
725 struct browser_state state;
728 int i, killPrefix = 0;
729 int multiple = (flags & M_SEL_MULTI) ? 1 : 0;
730 int folder = (flags & M_SEL_FOLDER) ? 1 : 0;
731 int buffy = (flags & M_SEL_BUFFY) ? 1 : 0;
733 buffy = buffy && folder;
735 memset (&state, 0, sizeof (struct browser_state));
738 strfcpy (LastDirBackup, LastDir, sizeof (LastDirBackup));
741 if (option (OPTNEWS)) {
743 strfcpy (prefix, f, sizeof (prefix));
747 /* default state for news reader mode is browse subscribed newsgroups */
749 for (list = CurrentNewsSrv->list; list; list = list->next) {
750 NNTP_DATA *data = (NNTP_DATA *) list->data;
752 if (data && data->subscribed) {
762 mutt_expand_path (f, flen);
764 if (mx_is_imap (f)) {
765 init_state (&state, NULL);
766 state.imap_browse = 1;
767 imap_browse (f, &state);
768 strfcpy (LastDir, state.folder, sizeof (LastDir));
772 for (i = mutt_strlen (f) - 1; i > 0 && f[i] != '/'; i--);
775 if (i > sizeof (LastDir) - 1)
776 i = sizeof (LastDir) - 1;
777 strncpy (LastDir, f, i);
781 getcwd (LastDir, sizeof (LastDir));
782 safe_strcat (LastDir, sizeof (LastDir), "/");
783 safe_strncat (LastDir, sizeof (LastDir), f, i);
788 strcpy (LastDir, "/"); /* __STRCPY_CHECKED__ */
790 getcwd (LastDir, sizeof (LastDir));
793 if (i <= 0 && f[0] != '/')
794 strfcpy (prefix, f, sizeof (prefix));
796 strfcpy (prefix, f + i + 1, sizeof (prefix));
804 getcwd (LastDir, sizeof (LastDir));
805 else if (!LastDir[0])
806 strfcpy (LastDir, NONULL (Maildir), sizeof (LastDir));
809 if (!buffy && mx_is_imap (LastDir)) {
810 init_state (&state, NULL);
811 state.imap_browse = 1;
812 imap_browse (LastDir, &state);
820 if (examine_mailboxes (NULL, &state) == -1)
825 if (!state.imap_browse)
827 if (examine_directory (NULL, &state, LastDir, prefix) == -1)
830 menu = mutt_new_menu ();
831 menu->menu = MENU_FOLDER;
832 menu->make_entry = folder_entry;
833 menu->search = select_file_search;
835 menu->data = state.entry;
837 menu->tag = file_tag;
839 menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_FOLDER,
841 (option (OPTNEWS)) ? FolderNewsHelp :
845 init_menu (&state, menu, title, sizeof (title), buffy);
848 switch (i = mutt_menuLoop (menu)) {
849 case OP_GENERIC_SELECT_ENTRY:
851 if (!state.entrylen) {
852 mutt_error _("No files match the file mask");
857 if (S_ISDIR (state.entry[menu->current].mode) ||
858 (S_ISLNK (state.entry[menu->current].mode) &&
859 link_is_dir (LastDir, state.entry[menu->current].name))
861 || state.entry[menu->current].inferiors
864 /* make sure this isn't a MH or maildir mailbox */
866 strfcpy (buf, state.entry[menu->current].name, sizeof (buf));
867 mutt_expand_path (buf, sizeof (buf));
870 else if (state.imap_browse) {
871 strfcpy (buf, state.entry[menu->current].name, sizeof (buf));
875 mutt_concat_path (buf, LastDir, state.entry[menu->current].name,
878 if ((mx_get_magic (buf) <= 0)
880 || state.entry[menu->current].inferiors
883 char OldLastDir[_POSIX_PATH_MAX];
885 /* save the old directory */
886 strfcpy (OldLastDir, LastDir, sizeof (OldLastDir));
888 if (mutt_strcmp (state.entry[menu->current].name, "..") == 0) {
889 if (mutt_strcmp ("..", LastDir + mutt_strlen (LastDir) - 2) == 0)
890 strcat (LastDir, "/.."); /* __STRCAT_CHECKED__ */
892 char *p = strrchr (LastDir + 1, '/');
897 if (LastDir[0] == '/')
900 strcat (LastDir, "/.."); /* __STRCAT_CHECKED__ */
905 strfcpy (LastDir, state.entry[menu->current].name,
907 mutt_expand_path (LastDir, sizeof (LastDir));
910 else if (state.imap_browse) {
914 strfcpy (LastDir, state.entry[menu->current].name,
916 /* tack on delimiter here */
917 n = strlen (LastDir) + 1;
919 /* special case "" needs no delimiter */
920 url_parse_ciss (&url, state.entry[menu->current].name);
922 (state.entry[menu->current].delim != '\0') &&
923 (n < sizeof (LastDir))) {
925 LastDir[n - 1] = state.entry[menu->current].delim;
930 char tmp[_POSIX_PATH_MAX];
932 mutt_concat_path (tmp, LastDir, state.entry[menu->current].name,
934 strfcpy (LastDir, tmp, sizeof (LastDir));
937 destroy_state (&state);
944 if (state.imap_browse) {
945 init_state (&state, NULL);
946 state.imap_browse = 1;
947 imap_browse (LastDir, &state);
948 menu->data = state.entry;
952 if (examine_directory (menu, &state, LastDir, prefix) == -1) {
953 /* try to restore the old values */
954 strfcpy (LastDir, OldLastDir, sizeof (LastDir));
955 if (examine_directory (menu, &state, LastDir, prefix) == -1) {
956 strfcpy (LastDir, NONULL (Homedir), sizeof (LastDir));
962 init_menu (&state, menu, title, sizeof (title), buffy);
968 if (buffy || option (OPTNEWS)) /* news have not path */
973 strfcpy (f, state.entry[menu->current].name, flen);
974 mutt_expand_path (f, flen);
977 else if (state.imap_browse)
978 strfcpy (f, state.entry[menu->current].name, flen);
981 mutt_concat_path (f, LastDir, state.entry[menu->current].name, flen);
983 /* Fall through to OP_EXIT */
992 *numfiles = menu->tagged;
993 tfiles = safe_calloc (*numfiles, sizeof (char *));
994 for (i = 0, j = 0; i < state.entrylen; i++) {
995 struct folder_file ff = state.entry[i];
996 char full[_POSIX_PATH_MAX];
999 mutt_concat_path (full, LastDir, ff.name, sizeof (full));
1000 mutt_expand_path (full, sizeof (full));
1001 tfiles[j++] = safe_strdup (full);
1006 else if (f[0]) { /* no tagged entries. return selected entry */
1008 tfiles = safe_calloc (*numfiles, sizeof (char *));
1009 mutt_expand_path (f, flen);
1010 tfiles[0] = safe_strdup (f);
1015 destroy_state (&state);
1016 mutt_menuDestroy (&menu);
1019 case OP_BROWSER_TELL:
1021 mutt_message ("%s", state.entry[menu->current].name);
1025 case OP_BROWSER_TOGGLE_LSUB:
1026 if (option (OPTIMAPLSUB)) {
1027 unset_option (OPTIMAPLSUB);
1030 set_option (OPTIMAPLSUB);
1032 mutt_ungetch (0, OP_CHECK_NEW);
1035 case OP_CREATE_MAILBOX:
1036 if (!state.imap_browse)
1037 mutt_error (_("Create is only supported for IMAP mailboxes"));
1039 imap_mailbox_create (LastDir);
1040 /* TODO: find a way to detect if the new folder would appear in
1041 * this window, and insert it without starting over. */
1042 destroy_state (&state);
1043 init_state (&state, NULL);
1044 state.imap_browse = 1;
1045 imap_browse (LastDir, &state);
1046 menu->data = state.entry;
1049 init_menu (&state, menu, title, sizeof (title), buffy);
1050 MAYBE_REDRAW (menu->redraw);
1054 case OP_RENAME_MAILBOX:
1055 if (!state.entry[menu->current].imap)
1056 mutt_error (_("Rename is only supported for IMAP mailboxes"));
1058 int nentry = menu->current;
1060 if (imap_mailbox_rename (state.entry[nentry].name) >= 0) {
1061 destroy_state (&state);
1062 init_state (&state, NULL);
1063 state.imap_browse = 1;
1064 imap_browse (LastDir, &state);
1065 menu->data = state.entry;
1068 init_menu (&state, menu, title, sizeof (title), buffy);
1069 MAYBE_REDRAW (menu->redraw);
1074 case OP_DELETE_MAILBOX:
1075 if (!state.entry[menu->current].imap)
1076 mutt_error (_("Delete is only supported for IMAP mailboxes"));
1078 char msg[SHORT_STRING];
1080 int nentry = menu->current;
1082 imap_parse_path (state.entry[nentry].name, &mx);
1083 snprintf (msg, sizeof (msg), _("Really delete mailbox \"%s\"?"),
1085 if (mutt_yesorno (msg, M_NO) == M_YES) {
1086 if (!imap_delete_mailbox (Context, mx)) {
1087 /* free the mailbox from the browser */
1088 FREE (&((state.entry)[nentry].name));
1089 FREE (&((state.entry)[nentry].desc));
1090 /* and move all other entries up */
1091 if (nentry + 1 < state.entrylen)
1092 memmove (state.entry + nentry, state.entry + nentry + 1,
1093 sizeof (struct folder_file) * (state.entrylen -
1096 mutt_message _("Mailbox deleted.");
1098 init_menu (&state, menu, title, sizeof (title), buffy);
1099 MAYBE_REDRAW (menu->redraw);
1103 mutt_message _("Mailbox not deleted.");
1109 case OP_CHANGE_DIRECTORY:
1112 if (option (OPTNEWS))
1116 strfcpy (buf, LastDir, sizeof (buf));
1118 if (!state.imap_browse)
1121 /* add '/' at the end of the directory name if not already there */
1122 int len = mutt_strlen (LastDir);
1124 if (len && LastDir[len - 1] != '/' && sizeof (buf) > len)
1128 if (mutt_get_field (_("Chdir to: "), buf, sizeof (buf), M_FILE) == 0 &&
1131 mutt_expand_path (buf, sizeof (buf));
1133 if (mx_is_imap (buf)) {
1134 strfcpy (LastDir, buf, sizeof (LastDir));
1135 destroy_state (&state);
1136 init_state (&state, NULL);
1137 state.imap_browse = 1;
1138 imap_browse (LastDir, &state);
1139 menu->data = state.entry;
1142 init_menu (&state, menu, title, sizeof (title), buffy);
1146 if (stat (buf, &st) == 0) {
1147 if (S_ISDIR (st.st_mode)) {
1148 destroy_state (&state);
1149 if (examine_directory (menu, &state, buf, prefix) == 0)
1150 strfcpy (LastDir, buf, sizeof (LastDir));
1152 mutt_error _("Error scanning directory.");
1154 if (examine_directory (menu, &state, LastDir, prefix) == -1) {
1155 mutt_menuDestroy (&menu);
1161 init_menu (&state, menu, title, sizeof (title), buffy);
1164 mutt_error (_("%s is not a directory."), buf);
1169 MAYBE_REDRAW (menu->redraw);
1174 strfcpy (buf, NONULL (Mask.pattern), sizeof (buf));
1175 if (mutt_get_field (_("File Mask: "), buf, sizeof (buf), 0) == 0) {
1176 regex_t *rx = (regex_t *) safe_malloc (sizeof (regex_t));
1181 /* assume that the user wants to see everything */
1183 strfcpy (buf, ".", sizeof (buf));
1191 if ((err = REGCOMP (rx, s, REG_NOSUB)) != 0) {
1192 regerror (err, rx, buf, sizeof (buf));
1195 mutt_error ("%s", buf);
1198 mutt_str_replace (&Mask.pattern, buf);
1204 destroy_state (&state);
1206 if (state.imap_browse) {
1207 init_state (&state, NULL);
1208 state.imap_browse = 1;
1209 imap_browse (LastDir, &state);
1210 menu->data = state.entry;
1211 init_menu (&state, menu, title, sizeof (title), buffy);
1215 if (examine_directory (menu, &state, LastDir, NULL) == 0)
1216 init_menu (&state, menu, title, sizeof (title), buffy);
1218 mutt_error _("Error scanning directory.");
1220 mutt_menuDestroy (&menu);
1224 if (!state.entrylen) {
1225 mutt_error _("No files match the file mask");
1231 MAYBE_REDRAW (menu->redraw);
1235 case OP_SORT_REVERSE:
1239 int reverse = (i == OP_SORT_REVERSE);
1241 switch (mutt_multi_choice ((reverse) ?
1243 ("Reverse sort by (d)ate, (a)lpha, si(z)e or do(n)'t sort? ")
1246 ("Sort by (d)ate, (a)lpha, si(z)e or do(n)'t sort? "),
1248 case -1: /* abort */
1252 case 1: /* (d)ate */
1253 BrowserSort = SORT_DATE;
1256 case 2: /* (a)lpha */
1257 BrowserSort = SORT_SUBJECT;
1260 case 3: /* si(z)e */
1261 BrowserSort = SORT_SIZE;
1264 case 4: /* do(n)'t sort */
1265 BrowserSort = SORT_ORDER;
1270 BrowserSort |= reverse ? SORT_REVERSE : 0;
1271 browser_sort (&state);
1272 menu->redraw = REDRAW_FULL;
1277 case OP_TOGGLE_MAILBOXES:
1281 destroy_state (&state);
1286 if (examine_mailboxes (menu, &state) == -1)
1290 else if (mx_is_imap (LastDir)) {
1291 init_state (&state, NULL);
1292 state.imap_browse = 1;
1293 imap_browse (LastDir, &state);
1294 menu->data = state.entry;
1297 else if (examine_directory (menu, &state, LastDir, prefix) == -1)
1299 init_menu (&state, menu, title, sizeof (title), buffy);
1306 case OP_BROWSER_NEW_FILE:
1308 snprintf (buf, sizeof (buf), "%s/", LastDir);
1309 if (mutt_get_field (_("New file name: "), buf, sizeof (buf), M_FILE) ==
1311 strfcpy (f, buf, flen);
1312 destroy_state (&state);
1313 mutt_menuDestroy (&menu);
1316 MAYBE_REDRAW (menu->redraw);
1319 case OP_BROWSER_VIEW_FILE:
1320 if (!state.entrylen) {
1321 mutt_error _("No files match the file mask");
1327 if (state.entry[menu->current].selectable) {
1328 strfcpy (f, state.entry[menu->current].name, flen);
1329 destroy_state (&state);
1330 mutt_menuDestroy (&menu);
1335 if (S_ISDIR (state.entry[menu->current].mode) ||
1336 (S_ISLNK (state.entry[menu->current].mode) &&
1337 link_is_dir (LastDir, state.entry[menu->current].name))) {
1338 mutt_error _("Can't view a directory");
1344 char buf[_POSIX_PATH_MAX];
1346 mutt_concat_path (buf, LastDir, state.entry[menu->current].name,
1348 b = mutt_make_file_attach (buf);
1350 mutt_view_attachment (NULL, b, M_REGULAR, NULL, NULL, 0);
1351 mutt_free_body (&b);
1352 menu->redraw = REDRAW_FULL;
1355 mutt_error _("Error trying to view file");
1362 if (option (OPTNEWS)) {
1363 struct folder_file *f = &state.entry[menu->current];
1366 if (i == OP_CATCHUP)
1367 nd = mutt_newsgroup_catchup (CurrentNewsSrv, f->name);
1369 nd = mutt_newsgroup_uncatchup (CurrentNewsSrv, f->name);
1373 struct folder_file ff;
1374 char buffer[_POSIX_PATH_MAX + SHORT_STRING];
1377 folder.ff->name = f->name;
1378 folder.ff->st = NULL;
1379 folder.ff->is_new = nd->new;
1382 mutt_FormatString (buffer, sizeof (buffer), NONULL(GroupFormat),
1383 newsgroup_format_str, (unsigned long) &folder,
1384 M_FORMAT_ARROWCURSOR);
1385 f->desc = safe_strdup (buffer); */
1386 if (menu->current + 1 < menu->max)
1388 menu->redraw = REDRAW_MOTION_RESYNCH;
1393 case OP_LOAD_ACTIVE:
1394 if (!option (OPTNEWS))
1401 for (tmp = CurrentNewsSrv->list; tmp; tmp = tmp->next) {
1402 if ((data = (NNTP_DATA *) tmp->data))
1406 nntp_get_active (CurrentNewsSrv);
1408 destroy_state (&state);
1410 examine_mailboxes (menu, &state);
1412 examine_directory (menu, &state, NULL, NULL);
1413 init_menu (&state, menu, title, sizeof (title), buffy);
1415 #endif /* USE_NNTP */
1417 #if defined USE_IMAP || defined USE_NNTP
1418 case OP_BROWSER_SUBSCRIBE:
1419 case OP_BROWSER_UNSUBSCRIBE:
1422 case OP_SUBSCRIBE_PATTERN:
1423 case OP_UNSUBSCRIBE_PATTERN:
1424 if (option (OPTNEWS)) {
1425 regex_t *rx = (regex_t *) safe_malloc (sizeof (regex_t));
1427 int j = menu->current;
1429 NNTP_SERVER *news = CurrentNewsSrv;
1431 if (i == OP_SUBSCRIBE_PATTERN || i == OP_UNSUBSCRIBE_PATTERN) {
1436 if (i == OP_SUBSCRIBE_PATTERN)
1437 snprintf (tmp, sizeof (tmp), _("Subscribe pattern: "));
1439 snprintf (tmp, sizeof (tmp), _("Unsubscribe pattern: "));
1440 if (mutt_get_field (tmp, buf, sizeof (buf), 0) != 0 || !buf[0]) {
1445 if ((err = REGCOMP (rx, s, REG_NOSUB)) != 0) {
1446 regerror (err, rx, buf, sizeof (buf));
1449 mutt_error ("%s", buf);
1452 menu->redraw = REDRAW_FULL;
1455 else if (!state.entrylen) {
1456 mutt_error _("No newsgroups match the mask");
1461 for (; j < state.entrylen; j++) {
1462 struct folder_file *f = &state.entry[j];
1464 if (i == OP_BROWSER_SUBSCRIBE || i == OP_BROWSER_UNSUBSCRIBE ||
1465 regexec (rx, f->name, 0, NULL, 0) == 0) {
1466 if (i == OP_BROWSER_SUBSCRIBE || i == OP_SUBSCRIBE_PATTERN)
1467 nd = mutt_newsgroup_subscribe (news, f->name);
1469 nd = mutt_newsgroup_unsubscribe (news, f->name);
1473 char buffer[_POSIX_PATH_MAX + SHORT_STRING];
1475 folder.name = f->name;
1477 folder.new = nd->new;
1480 mutt_FormatString (buffer, sizeof (buffer), NONULL(GroupFormat),
1481 newsgroup_format_str, (unsigned long) &folder,
1482 M_FORMAT_ARROWCURSOR);
1483 f->desc = safe_strdup (buffer);
1486 if (i == OP_BROWSER_SUBSCRIBE || i == OP_BROWSER_UNSUBSCRIBE) {
1487 if (menu->current + 1 < menu->max)
1489 menu->redraw = REDRAW_MOTION_RESYNCH;
1493 if (i == OP_SUBSCRIBE_PATTERN) {
1494 LIST *grouplist = NULL;
1497 grouplist = news->list;
1498 for (; grouplist; grouplist = grouplist->next) {
1499 nd = (NNTP_DATA *) grouplist->data;
1500 if (nd && nd->group && !nd->subscribed) {
1501 if (regexec (rx, nd->group, 0, NULL, 0) == 0) {
1502 mutt_newsgroup_subscribe (news, nd->group);
1503 add_folder (menu, &state, nd->group, NULL, nd, nd->new);
1507 init_menu (&state, menu, title, sizeof (title), buffy);
1509 mutt_newsrc_update (news);
1510 nntp_clear_cacheindex (news);
1511 if (i != OP_BROWSER_SUBSCRIBE && i != OP_BROWSER_UNSUBSCRIBE)
1517 #endif /* USE_IMAP && USE_NNTP */
1518 #endif /* USE_NNTP */
1521 if (i == OP_BROWSER_SUBSCRIBE)
1522 imap_subscribe (state.entry[menu->current].name, 1);
1524 imap_subscribe (state.entry[menu->current].name, 0);
1526 #endif /* USE_IMAP */
1533 strfcpy (LastDir, LastDirBackup, sizeof (LastDir));