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));
149 folder_format_str(char *dest, ssize_t destlen, char op,
150 const char *src, const char *fmt,
151 const char *ifstr, const char *elstr,
152 anytype data, format_flag flags)
154 char fn[STRING], tmp[STRING], permission[11], date[16];
157 FOLDER *folder = data.ptr;
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 if (flags & M_FORMAT_OPTIONAL)
305 m_strformat(dest, destlen, 0, optional ? ifstr : elstr,
306 folder_format_str, data, 0);
313 newsgroup_format_str(char *dest, ssize_t destlen, char op,
314 const char *src, const char *fmt,
315 const char *ifstr, const char *elstr,
316 anytype data, format_flag flags)
318 char fn[STRING], tmp[STRING];
319 FOLDER *folder = data.ptr;
323 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
324 snprintf (dest, destlen, tmp, folder->num + 1);
328 m_strcpy(fn, sizeof(fn), folder->ff->name);
329 snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
330 snprintf (dest, destlen, tmp, fn);
334 snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
335 if (folder->ff->nd->subscribed)
336 snprintf (dest, destlen, tmp, ' ');
338 snprintf (dest, destlen, tmp, folder->ff->new ? 'N' : 'u');
342 snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
343 if (folder->ff->nd->deleted)
344 snprintf (dest, destlen, tmp, 'D');
346 snprintf (dest, destlen, tmp, folder->ff->nd->allowed ? ' ' : '-');
350 if (flags & M_FORMAT_OPTIONAL) {
351 if (folder->ff->nd->unread != 0)
352 m_strformat(dest, destlen, 0, ifstr, newsgroup_format_str,
355 m_strformat (dest, destlen, 0, elstr, newsgroup_format_str,
358 else if (Context && Context->data == folder->ff->nd) {
359 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
360 snprintf (dest, destlen, tmp, Context->unread);
363 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
364 snprintf (dest, destlen, tmp, folder->ff->nd->unread);
369 if (Context && Context->data == folder->ff->nd) {
370 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
371 snprintf (dest, destlen, tmp, Context->new);
373 else if (option (OPTMARKOLD) &&
374 folder->ff->nd->lastCached >= folder->ff->nd->firstMessage &&
375 folder->ff->nd->lastCached <= folder->ff->nd->lastMessage) {
376 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
377 snprintf (dest, destlen, tmp,
378 folder->ff->nd->lastMessage - folder->ff->nd->lastCached);
381 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
382 snprintf (dest, destlen, tmp, folder->ff->nd->unread);
387 if (folder->ff->nd->desc != NULL) {
388 snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
389 snprintf (dest, destlen, tmp, folder->ff->nd->desc);
392 snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
393 snprintf (dest, destlen, tmp, "");
399 #endif /* USE_NNTP */
402 static void add_folder (MUTTMENU * m, struct browser_state *state,
403 const char *name, const struct stat *s,
406 static void add_folder (MUTTMENU * m, struct browser_state *state,
407 const char *name, const struct stat *s,
411 if (state->entrylen == state->entrymax) {
412 /* need to allocate more space */
413 p_realloc(&state->entry, state->entrymax += 256);
414 p_clear(&state->entry[state->entrylen], 256);
416 m->data = state->entry;
420 (state->entry)[state->entrylen].mode = s->st_mode;
421 (state->entry)[state->entrylen].mtime = s->st_mtime;
422 (state->entry)[state->entrylen].size = s->st_size;
423 (state->entry)[state->entrylen].st = p_dup(s, 1);
426 (state->entry)[state->entrylen].new = new;
427 (state->entry)[state->entrylen].name = m_strdup(name);
428 (state->entry)[state->entrylen].desc = m_strdup(name);
429 (state->entry)[state->entrylen].imap = 0;
431 if (option (OPTNEWS))
432 (state->entry)[state->entrylen].nd = (NNTP_DATA *) data;
437 static void init_state (struct browser_state *state, MUTTMENU * menu)
440 state->entrymax = 256;
441 state->entry = p_new(struct folder_file, state->entrymax);
442 state->imap_browse = 0;
444 menu->data = state->entry;
447 /* get list of all files/newsgroups with mask */
448 static int examine_directory (MUTTMENU * menu, struct browser_state *state,
449 char *d, const char *prefix)
452 if (option (OPTNEWS)) {
455 NNTP_SERVER *news = CurrentNewsSrv;
457 /* buffy_check (0); */
458 init_state (state, menu);
460 for (tmp = news->list; tmp; tmp = tmp->next) {
461 if (!(data = (NNTP_DATA *) tmp->data))
463 nntp_sync_sidebar (data);
464 if (m_strncmp (prefix, data->group, m_strlen(prefix)) != 0)
466 if (!((regexec (Mask.rx, data->group, 0, NULL, 0) == 0) ^ Mask.not))
469 add_folder (menu, state, data->group, NULL, data, data->new);
471 add_folder (menu, state, data->group, NULL, data->new);
477 #endif /* USE_NNTP */
482 char buffer[_POSIX_PATH_MAX + STRING];
485 while (stat (d, &s) == -1) {
486 if (errno == ENOENT) {
487 /* The last used directory is deleted, try to use the parent dir. */
488 char *c = strrchr (d, '/');
499 if (!S_ISDIR (s.st_mode)) {
500 mutt_error (_("%s is not a directory."), d);
506 if ((dp = opendir (d)) == NULL) {
511 init_state (state, menu);
513 while ((de = readdir (dp)) != NULL) {
514 if (m_strcmp(de->d_name, ".") == 0)
515 continue; /* we don't need . */
517 if (m_strncmp(prefix, de->d_name, m_strlen(prefix)) != 0)
519 if (!((regexec (Mask.rx, de->d_name, 0, NULL, 0) == 0) ^ Mask.not))
522 mutt_concat_path(buffer, sizeof(buffer), d, de->d_name);
523 if (lstat (buffer, &s) == -1)
526 if ((!S_ISREG (s.st_mode)) && (!S_ISDIR (s.st_mode)) &&
527 (!S_ISLNK (s.st_mode)))
530 i = buffy_lookup (buffer);
532 add_folder (menu, state, de->d_name, &s, NULL, i >= 0 ? Incoming.arr[i]->new : 0);
534 add_folder (menu, state, de->d_name, &s, i >= 0 ? Incoming.arr[i]->new : 0);
540 browser_sort (state);
544 /* get list of mailboxes/subscribed newsgroups */
545 static int examine_mailboxes (MUTTMENU * menu, struct browser_state *state)
548 char buffer[LONG_STRING];
551 if (option (OPTNEWS)) {
554 NNTP_SERVER *news = CurrentNewsSrv;
556 /* buffy_check (0); */
557 init_state (state, menu);
559 for (tmp = news->list; tmp; tmp = tmp->next) {
560 if ((data = (NNTP_DATA*) tmp->data) == NULL)
562 nntp_sync_sidebar (data);
563 if ((data->new || (data->subscribed &&
564 (!option (OPTSHOWONLYUNREAD)|| data->unread))))
565 add_folder (menu, state, data->group, NULL, data, data->new);
579 init_state (state, menu);
581 for (i = 0; i < Incoming.len; i++) {
582 tmp = Incoming.arr[i];
583 tmp->magic = mx_get_magic (tmp->path);
584 if (tmp->magic == M_IMAP) {
586 add_folder (menu, state, tmp->path, NULL, NULL, tmp->new);
588 add_folder (menu, state, tmp->path, NULL, tmp->new);
592 if (tmp->magic == M_POP) {
594 add_folder (menu, state, tmp->path, NULL, NULL, tmp->new);
596 add_folder (menu, state, tmp->path, NULL, tmp->new);
601 if (tmp->magic == M_NNTP) {
602 add_folder (menu, state, tmp->path, NULL, NULL, tmp->new);
606 if (lstat (tmp->path, &s) == -1)
609 if ((!S_ISREG (s.st_mode)) && (!S_ISDIR (s.st_mode)) &&
610 (!S_ISLNK (s.st_mode)))
613 m_strcpy(buffer, sizeof(buffer), NONULL(tmp->path));
614 mutt_pretty_mailbox (buffer);
617 add_folder (menu, state, buffer, &s, NULL, tmp->new);
619 add_folder (menu, state, buffer, &s, tmp->new);
623 browser_sort (state);
627 static int select_file_search (MUTTMENU * menu, regex_t * re, int n)
630 if (option (OPTNEWS))
632 (re, ((struct folder_file *) menu->data)[n].desc, 0, NULL, 0));
635 (re, ((struct folder_file *) menu->data)[n].name, 0, NULL, 0));
638 static void folder_entry (char *s, ssize_t slen, MUTTMENU * menu, int num)
642 folder.ff = &((struct folder_file *) menu->data)[num];
646 if (option (OPTNEWS))
647 m_strformat(s, slen, COLS - SW, GroupFormat, newsgroup_format_str, &folder,
648 option(OPTARROWCURSOR) ? M_FORMAT_ARROWCURSOR : 0);
651 m_strformat(s, slen, COLS - SW, FolderFormat, folder_format_str, &folder,
652 option(OPTARROWCURSOR) ? M_FORMAT_ARROWCURSOR : 0);
655 static void init_menu (struct browser_state *state, MUTTMENU * menu,
656 char *title, ssize_t titlelen, int buffy)
658 char path[_POSIX_PATH_MAX];
660 menu->max = state->entrylen;
662 if (menu->current >= menu->max)
663 menu->current = menu->max - 1;
664 if (menu->current < 0)
666 if (menu->top > menu->current)
672 if (option (OPTNEWS)) {
674 snprintf (title, titlelen, "%s", _("Subscribed newsgroups"));
676 snprintf (title, titlelen, _("Newsgroups on server [%s]"),
677 CurrentNewsSrv->conn->account.host);
682 snprintf(title, titlelen, _("Mailboxes [%d]"), buffy_check(0));
684 m_strcpy(path, sizeof(path), LastDir);
685 mutt_pretty_mailbox (path);
686 if (state->imap_browse && option (OPTIMAPLSUB))
687 snprintf (title, titlelen, _("Subscribed [%s], File mask: %s"),
688 path, NONULL (Mask.pattern));
690 snprintf (title, titlelen, _("Directory [%s], File mask: %s"),
691 path, NONULL (Mask.pattern));
693 menu->redraw = REDRAW_FULL;
696 static int file_tag (MUTTMENU * menu, int n, int m)
698 struct folder_file *ff = &(((struct folder_file *) menu->data)[n]);
701 if (S_ISDIR (ff->mode)
702 || (S_ISLNK (ff->mode) && link_is_dir (LastDir, ff->name))) {
703 mutt_error _("Can't attach a directory!");
709 ff->tagged = (m >= 0 ? m : !ff->tagged);
711 return ff->tagged - ot;
714 void _mutt_select_file (char *f, ssize_t flen, int flags, char ***files,
717 char buf[_POSIX_PATH_MAX];
718 char prefix[_POSIX_PATH_MAX] = "";
719 char helpstr[STRING];
721 struct browser_state state;
724 int i, killPrefix = 0;
725 int multiple = (flags & M_SEL_MULTI) ? 1 : 0;
726 int folder = (flags & M_SEL_FOLDER) ? 1 : 0;
727 int buffy = (flags & M_SEL_BUFFY) ? 1 : 0;
729 buffy = buffy && folder;
734 m_strcpy(LastDirBackup, sizeof(LastDirBackup), LastDir);
737 if (option (OPTNEWS)) {
739 m_strcpy(prefix, sizeof(prefix), f);
743 /* default state for news reader mode is browse subscribed newsgroups */
745 for (list = CurrentNewsSrv->list; list; list = list->next) {
746 NNTP_DATA *data = (NNTP_DATA *) list->data;
748 if (data && data->subscribed) {
758 mutt_expand_path (f, flen);
759 if (imap_is_magic (f, NULL) == M_IMAP) {
760 init_state (&state, NULL);
761 state.imap_browse = 1;
762 if (!imap_browse (f, &state))
763 m_strcpy(LastDir, sizeof(LastDir), state.folder);
766 for (i = m_strlen(f) - 1; i > 0 && f[i] != '/'; i--);
769 i = MIN(ssizeof(LastDir) - 1, i);
770 m_strcpy(LastDir, sizeof(LastDir), f);
773 getcwd(LastDir, sizeof(LastDir));
774 m_strcat(LastDir, sizeof(LastDir), "/");
775 m_strncat(LastDir, sizeof(LastDir), f, i);
780 m_strcpy(LastDir, sizeof(LastDir), "/");
782 getcwd (LastDir, sizeof (LastDir));
785 if (i <= 0 && f[0] != '/')
786 m_strcpy(prefix, sizeof(prefix), f);
788 m_strcpy(prefix, sizeof(prefix), f + i + 1);
794 getcwd (LastDir, sizeof (LastDir));
795 else if (!LastDir[0])
796 m_strcpy(LastDir, sizeof(LastDir), NONULL(Maildir));
798 if (!buffy && imap_is_magic (LastDir, NULL) == M_IMAP) {
799 init_state (&state, NULL);
800 state.imap_browse = 1;
801 imap_browse (LastDir, &state);
802 browser_sort (&state);
809 if (examine_mailboxes (NULL, &state) == -1)
813 if (!state.imap_browse)
814 if (examine_directory (NULL, &state, LastDir, prefix) == -1)
817 menu = mutt_new_menu ();
818 menu->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 (option (OPTNEWS)) ? FolderNewsHelp :
832 init_menu (&state, menu, title, sizeof (title), buffy);
835 switch (i = mutt_menuLoop (menu)) {
836 case OP_GENERIC_SELECT_ENTRY:
838 if (!state.entrylen) {
839 mutt_error _("No files match the file mask");
844 if (S_ISDIR (state.entry[menu->current].mode) ||
845 (S_ISLNK (state.entry[menu->current].mode) &&
846 link_is_dir (LastDir, state.entry[menu->current].name))
847 || state.entry[menu->current].inferiors
849 /* make sure this isn't a MH or maildir mailbox */
851 m_strcpy(buf, sizeof(buf), state.entry[menu->current].name);
852 mutt_expand_path (buf, sizeof (buf));
854 else if (state.imap_browse) {
855 m_strcpy(buf, sizeof(buf), state.entry[menu->current].name);
858 mutt_concat_path(buf, sizeof(buf), LastDir,
859 state.entry[menu->current].name);
861 if ((mx_get_magic (buf) <= 0)
862 || state.entry[menu->current].inferiors)
864 char OldLastDir[_POSIX_PATH_MAX];
866 /* save the old directory */
867 m_strcpy(OldLastDir, sizeof(OldLastDir), LastDir);
869 if (m_strcmp(state.entry[menu->current].name, "..") == 0) {
870 if (m_strcmp("..", LastDir + m_strlen(LastDir) - 2) == 0)
871 m_strcat(LastDir, sizeof(LastDir), "/..");
873 char *p = strrchr (LastDir + 1, '/');
878 if (LastDir[0] == '/')
881 m_strcat(LastDir, sizeof(LastDir), "/..");
886 m_strcpy(LastDir, sizeof(LastDir),
887 state.entry[menu->current].name);
888 mutt_expand_path (LastDir, sizeof (LastDir));
890 else if (state.imap_browse) {
894 m_strcpy(LastDir, sizeof(LastDir),
895 state.entry[menu->current].name);
896 /* tack on delimiter here */
897 n = m_strlen(LastDir) + 1;
899 /* special case "" needs no delimiter */
900 url_parse_ciss (&url, state.entry[menu->current].name);
902 (state.entry[menu->current].delim != '\0') &&
903 (n < ssizeof (LastDir))) {
905 LastDir[n - 1] = state.entry[menu->current].delim;
909 char tmp[_POSIX_PATH_MAX];
911 mutt_concat_path(tmp, sizeof(tmp), LastDir,
912 state.entry[menu->current].name);
913 m_strcpy(LastDir, sizeof(LastDir), tmp);
916 destroy_state (&state);
922 if (state.imap_browse) {
923 init_state (&state, NULL);
924 state.imap_browse = 1;
925 imap_browse (LastDir, &state);
926 browser_sort (&state);
927 menu->data = state.entry;
930 if (examine_directory (menu, &state, LastDir, prefix) == -1) {
931 /* try to restore the old values */
932 m_strcpy(LastDir, sizeof(LastDir), OldLastDir);
933 if (examine_directory (menu, &state, LastDir, prefix) == -1) {
934 m_strcpy(LastDir, sizeof(LastDir), NONULL(Homedir));
940 init_menu (&state, menu, title, sizeof (title), buffy);
946 if (buffy || option (OPTNEWS)) /* news have not path */
951 m_strcpy(f, flen, state.entry[menu->current].name);
952 mutt_expand_path (f, flen);
954 else if (state.imap_browse)
955 m_strcpy(f, flen, state.entry[menu->current].name);
957 mutt_concat_path(f, flen, LastDir, state.entry[menu->current].name);
959 /* Fall through to OP_EXIT */
969 *numfiles = menu->tagged;
970 tfiles = p_new(char *, *numfiles);
971 for (h = 0, j = 0; h < state.entrylen; i++) {
972 struct folder_file ff = state.entry[i];
973 char full[_POSIX_PATH_MAX];
976 mutt_concat_path(full, sizeof(full), LastDir, ff.name);
977 mutt_expand_path (full, sizeof (full));
978 tfiles[j++] = m_strdup(full);
983 else if (f[0]) { /* no tagged entries. return selected entry */
985 tfiles = p_new(char *, *numfiles);
986 mutt_expand_path (f, flen);
987 tfiles[0] = m_strdup(f);
992 destroy_state (&state);
993 mutt_menuDestroy (&menu);
996 case OP_BROWSER_TELL:
998 mutt_message ("%s", state.entry[menu->current].name);
1001 case OP_BROWSER_TOGGLE_LSUB:
1002 if (option (OPTIMAPLSUB)) {
1003 unset_option (OPTIMAPLSUB);
1006 set_option (OPTIMAPLSUB);
1008 mutt_ungetch (0, OP_CHECK_NEW);
1011 case OP_CREATE_MAILBOX:
1012 if (!state.imap_browse)
1013 mutt_error (_("Create is only supported for IMAP mailboxes"));
1015 imap_mailbox_create (LastDir);
1016 /* TODO: find a way to detect if the new folder would appear in
1017 * this window, and insert it without starting over. */
1018 destroy_state (&state);
1019 init_state (&state, NULL);
1020 state.imap_browse = 1;
1021 imap_browse (LastDir, &state);
1022 browser_sort (&state);
1023 menu->data = state.entry;
1026 init_menu (&state, menu, title, sizeof (title), buffy);
1027 MAYBE_REDRAW (menu->redraw);
1031 case OP_RENAME_MAILBOX:
1032 if (!state.entry[menu->current].imap)
1033 mutt_error (_("Rename is only supported for IMAP mailboxes"));
1035 int nentry = menu->current;
1037 if (imap_mailbox_rename (state.entry[nentry].name) >= 0) {
1038 destroy_state (&state);
1039 init_state (&state, NULL);
1040 state.imap_browse = 1;
1041 imap_browse (LastDir, &state);
1042 browser_sort (&state);
1043 menu->data = state.entry;
1046 init_menu (&state, menu, title, sizeof (title), buffy);
1047 MAYBE_REDRAW (menu->redraw);
1052 case OP_DELETE_MAILBOX:
1053 if (!state.entry[menu->current].imap)
1054 mutt_error (_("Delete is only supported for IMAP mailboxes"));
1058 int nentry = menu->current;
1060 imap_parse_path (state.entry[nentry].name, &mx);
1061 snprintf (msg, sizeof (msg), _("Really delete mailbox \"%s\"?"),
1063 if (mutt_yesorno (msg, M_NO) == M_YES) {
1064 if (!imap_delete_mailbox (Context, mx)) {
1065 /* free the mailbox from the browser */
1066 p_delete(&((state.entry)[nentry].name));
1067 p_delete(&((state.entry)[nentry].desc));
1068 /* and move all other entries up */
1069 if (nentry + 1 < state.entrylen)
1070 memmove (state.entry + nentry, state.entry + nentry + 1,
1071 sizeof (struct folder_file) * (state.entrylen -
1074 mutt_message _("Mailbox deleted.");
1076 init_menu (&state, menu, title, sizeof (title), buffy);
1077 MAYBE_REDRAW (menu->redraw);
1081 mutt_message _("Mailbox not deleted.");
1086 case OP_CHANGE_DIRECTORY:
1089 if (option (OPTNEWS))
1093 m_strcpy(buf, sizeof(buf), LastDir);
1094 if (!state.imap_browse)
1096 /* add '/' at the end of the directory name if not already there */
1097 ssize_t len = m_strlen(LastDir);
1099 if (len && LastDir[len - 1] != '/' && ssizeof(buf) > len)
1103 if (mutt_get_field (_("Chdir to: "), buf, sizeof (buf), M_FILE) == 0 &&
1106 mutt_expand_path (buf, sizeof (buf));
1107 if (imap_is_magic (buf, NULL) == M_IMAP) {
1108 m_strcpy(LastDir, sizeof(LastDir), buf);
1109 destroy_state (&state);
1110 init_state (&state, NULL);
1111 state.imap_browse = 1;
1112 imap_browse (LastDir, &state);
1113 browser_sort (&state);
1114 menu->data = state.entry;
1117 init_menu (&state, menu, title, sizeof (title), buffy);
1120 if (stat (buf, &st) == 0) {
1121 if (S_ISDIR (st.st_mode)) {
1122 destroy_state (&state);
1123 if (examine_directory (menu, &state, buf, prefix) == 0)
1124 m_strcpy(LastDir, sizeof(LastDir), buf);
1126 mutt_error _("Error scanning directory.");
1128 if (examine_directory (menu, &state, LastDir, prefix) == -1) {
1129 mutt_menuDestroy (&menu);
1135 init_menu (&state, menu, title, sizeof (title), buffy);
1138 mutt_error (_("%s is not a directory."), buf);
1143 MAYBE_REDRAW (menu->redraw);
1148 m_strcpy(buf, sizeof(buf), NONULL(Mask.pattern));
1149 if (mutt_get_field (_("File Mask: "), buf, sizeof (buf), 0) == 0) {
1150 regex_t *rx = p_new(regex_t, 1);
1155 /* assume that the user wants to see everything */
1157 m_strcpy(buf, sizeof(buf), ".");
1160 s = vskipspaces(s + 1);
1164 if ((err = REGCOMP (rx, s, REG_NOSUB)) != 0) {
1165 regerror (err, rx, buf, sizeof (buf));
1168 mutt_error ("%s", buf);
1171 m_strreplace(&Mask.pattern, buf);
1177 destroy_state (&state);
1178 if (state.imap_browse) {
1179 init_state (&state, NULL);
1180 state.imap_browse = 1;
1181 imap_browse (LastDir, &state);
1182 browser_sort (&state);
1183 menu->data = state.entry;
1184 init_menu (&state, menu, title, sizeof (title), buffy);
1187 if (examine_directory (menu, &state, LastDir, NULL) == 0)
1188 init_menu (&state, menu, title, sizeof (title), buffy);
1190 mutt_error _("Error scanning directory.");
1192 mutt_menuDestroy (&menu);
1196 if (!state.entrylen) {
1197 mutt_error _("No files match the file mask");
1203 MAYBE_REDRAW (menu->redraw);
1207 case OP_SORT_REVERSE:
1211 int reverse = (i == OP_SORT_REVERSE);
1213 switch (mutt_multi_choice ((reverse) ?
1215 ("Reverse sort by (d)ate, (a)lpha, si(z)e or do(n)'t sort? ")
1218 ("Sort by (d)ate, (a)lpha, si(z)e or do(n)'t sort? "),
1220 case -1: /* abort */
1224 case 1: /* (d)ate */
1225 BrowserSort = SORT_DATE;
1228 case 2: /* (a)lpha */
1229 BrowserSort = SORT_SUBJECT;
1232 case 3: /* si(z)e */
1233 BrowserSort = SORT_SIZE;
1236 case 4: /* do(n)'t sort */
1237 BrowserSort = SORT_ORDER;
1242 BrowserSort |= reverse ? SORT_REVERSE : 0;
1243 browser_sort (&state);
1244 menu->redraw = REDRAW_FULL;
1249 case OP_TOGGLE_MAILBOXES:
1253 destroy_state (&state);
1258 if (examine_mailboxes (menu, &state) == -1)
1261 else if (imap_is_magic (LastDir, NULL) == M_IMAP) {
1262 init_state (&state, NULL);
1263 state.imap_browse = 1;
1264 imap_browse (LastDir, &state);
1265 browser_sort (&state);
1266 menu->data = state.entry;
1268 else if (examine_directory (menu, &state, LastDir, prefix) == -1)
1270 init_menu (&state, menu, title, sizeof (title), buffy);
1274 if (option (OPTFORCEBUFFYCHECK))
1279 case OP_BROWSER_NEW_FILE:
1281 snprintf (buf, sizeof (buf), "%s/", LastDir);
1282 if (mutt_get_field (_("New file name: "), buf, sizeof (buf), M_FILE) ==
1284 m_strcpy(f, flen, buf);
1285 destroy_state (&state);
1286 mutt_menuDestroy (&menu);
1289 MAYBE_REDRAW (menu->redraw);
1292 case OP_BROWSER_VIEW_FILE:
1293 if (!state.entrylen) {
1294 mutt_error _("No files match the file mask");
1299 if (state.entry[menu->current].selectable) {
1300 m_strcpy(f, flen, state.entry[menu->current].name);
1301 destroy_state (&state);
1302 mutt_menuDestroy (&menu);
1306 if (S_ISDIR (state.entry[menu->current].mode) ||
1307 (S_ISLNK (state.entry[menu->current].mode) &&
1308 link_is_dir (LastDir, state.entry[menu->current].name))) {
1309 mutt_error _("Can't view a directory");
1315 char nbuf[_POSIX_PATH_MAX];
1317 mutt_concat_path(nbuf, sizeof(nbuf), LastDir,
1318 state.entry[menu->current].name);
1319 b = mutt_make_file_attach (nbuf);
1321 mutt_view_attachment (NULL, b, M_REGULAR, NULL, NULL, 0);
1323 menu->redraw = REDRAW_FULL;
1326 mutt_error _("Error trying to view file");
1333 if (option (OPTNEWS)) {
1334 struct folder_file *folder_f = &state.entry[menu->current];
1337 if (i == OP_CATCHUP)
1338 nd = mutt_newsgroup_catchup (CurrentNewsSrv, folder_f->name);
1340 nd = mutt_newsgroup_uncatchup (CurrentNewsSrv, folder_f->name);
1343 if (menu->current + 1 < menu->max)
1345 menu->redraw = REDRAW_MOTION_RESYNCH;
1350 case OP_LOAD_ACTIVE:
1351 if (!option (OPTNEWS))
1358 for (tmp = CurrentNewsSrv->list; tmp; tmp = tmp->next) {
1359 if ((data = (NNTP_DATA *) tmp->data))
1363 nntp_get_active (CurrentNewsSrv);
1365 destroy_state (&state);
1367 examine_mailboxes (menu, &state);
1369 examine_directory (menu, &state, NULL, NULL);
1370 init_menu (&state, menu, title, sizeof (title), buffy);
1372 #endif /* USE_NNTP */
1374 case OP_BROWSER_SUBSCRIBE:
1375 case OP_BROWSER_UNSUBSCRIBE:
1377 case OP_SUBSCRIBE_PATTERN:
1378 case OP_UNSUBSCRIBE_PATTERN:
1379 if (option (OPTNEWS)) {
1380 regex_t *rx = p_new(regex_t, 1);
1382 int j = menu->current;
1384 NNTP_SERVER *news = CurrentNewsSrv;
1386 if (i == OP_SUBSCRIBE_PATTERN || i == OP_UNSUBSCRIBE_PATTERN) {
1391 if (i == OP_SUBSCRIBE_PATTERN)
1392 snprintf (tmp, sizeof (tmp), _("Subscribe pattern: "));
1394 snprintf (tmp, sizeof (tmp), _("Unsubscribe pattern: "));
1395 if (mutt_get_field (tmp, buf, sizeof (buf), 0) != 0 || !buf[0]) {
1400 if ((err = REGCOMP (rx, s, REG_NOSUB)) != 0) {
1401 regerror (err, rx, buf, sizeof (buf));
1404 mutt_error ("%s", buf);
1407 menu->redraw = REDRAW_FULL;
1410 else if (!state.entrylen) {
1411 mutt_error _("No newsgroups match the mask");
1416 for (; j < state.entrylen; j++) {
1417 struct folder_file *folderf = &state.entry[j];
1419 if (i == OP_BROWSER_SUBSCRIBE || i == OP_BROWSER_UNSUBSCRIBE ||
1420 regexec (rx, folderf->name, 0, NULL, 0) == 0) {
1421 if (i == OP_BROWSER_SUBSCRIBE || i == OP_SUBSCRIBE_PATTERN)
1422 nd = mutt_newsgroup_subscribe (news, folderf->name);
1424 nd = mutt_newsgroup_unsubscribe (news, folderf->name);
1426 if (i == OP_BROWSER_SUBSCRIBE || i == OP_BROWSER_UNSUBSCRIBE) {
1427 if (menu->current + 1 < menu->max)
1429 menu->redraw = REDRAW_MOTION_RESYNCH;
1433 if (i == OP_SUBSCRIBE_PATTERN) {
1434 string_list_t *grouplist = NULL;
1437 grouplist = news->list;
1438 for (; grouplist; grouplist = grouplist->next) {
1439 nd = (NNTP_DATA *) grouplist->data;
1440 if (nd && nd->group && !nd->subscribed) {
1441 if (regexec (rx, nd->group, 0, NULL, 0) == 0) {
1442 mutt_newsgroup_subscribe (news, nd->group);
1443 add_folder (menu, &state, nd->group, NULL, nd, nd->new);
1447 init_menu (&state, menu, title, sizeof (title), buffy);
1449 mutt_newsrc_update (news);
1450 nntp_clear_cacheindex (news);
1451 if (i != OP_BROWSER_SUBSCRIBE && i != OP_BROWSER_UNSUBSCRIBE)
1456 #endif /* USE_NNTP */
1458 if (i == OP_BROWSER_SUBSCRIBE)
1459 imap_subscribe (state.entry[menu->current].name, 1);
1461 imap_subscribe (state.entry[menu->current].name, 0);
1469 m_strcpy(LastDir, sizeof(LastDir), LastDirBackup);