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.
14 #include <lib-lib/mem.h>
15 #include <lib-lib/str.h>
16 #include <lib-lib/macros.h>
17 #include <lib-lib/file.h>
18 #include <lib-lib/mapping.h>
20 #include <lib-ui/curses.h>
21 #include <lib-ui/enter.h>
22 #include <lib-ui/menu.h>
31 #include <imap/imap.h>
32 #include <imap/mx_imap.h>
48 static struct mapping_t FolderHelp[] = {
49 {N_("Exit"), OP_EXIT},
50 {N_("Chdir"), OP_CHANGE_DIRECTORY},
51 {N_("Mask"), OP_ENTER_MASK},
52 {N_("Help"), OP_HELP},
57 static struct mapping_t FolderNewsHelp[] = {
58 {N_("Exit"), OP_EXIT},
59 {N_("List"), OP_TOGGLE_MAILBOXES},
60 {N_("Subscribe"), OP_BROWSER_SUBSCRIBE},
61 {N_("Unsubscribe"), OP_BROWSER_UNSUBSCRIBE},
62 {N_("Catchup"), OP_CATCHUP},
63 {N_("Mask"), OP_ENTER_MASK},
64 {N_("Help"), OP_HELP},
69 typedef struct folder_t {
70 struct folder_file *ff;
74 static char LastDir[_POSIX_PATH_MAX] = "";
75 static char LastDirBackup[_POSIX_PATH_MAX] = "";
77 /* Frees up the memory allocated for the local-global variables. */
78 static void destroy_state (struct browser_state *state)
82 for (c = 0; c < state->entrylen; c++) {
83 p_delete(&((state->entry)[c].name));
84 p_delete(&((state->entry)[c].desc));
85 p_delete(&((state->entry)[c].st));
87 p_delete(&state->folder);
88 p_delete(&state->entry);
91 static int browser_compare_subject (const void *a, const void *b)
93 struct folder_file *pa = (struct folder_file *) a;
94 struct folder_file *pb = (struct folder_file *) b;
96 int r = strcoll(NONULL(pa->name), NONULL(pb->name));
98 return ((BrowserSort & SORT_REVERSE) ? -r : r);
101 static int browser_compare_date (const void *a, const void *b)
103 struct folder_file *pa = (struct folder_file *) a;
104 struct folder_file *pb = (struct folder_file *) b;
106 int r = pa->mtime - pb->mtime;
108 return ((BrowserSort & SORT_REVERSE) ? -r : r);
111 static int browser_compare_size (const void *a, const void *b)
113 struct folder_file *pa = (struct folder_file *) a;
114 struct folder_file *pb = (struct folder_file *) b;
116 int r = pa->size - pb->size;
118 return ((BrowserSort & SORT_REVERSE) ? -r : r);
121 static void browser_sort (struct browser_state *state)
123 int (*f) (const void *, const void *);
125 switch (BrowserSort & SORT_MASK) {
130 if (option (OPTNEWS))
133 f = browser_compare_date;
137 if (option (OPTNEWS))
140 f = browser_compare_size;
144 f = browser_compare_subject;
147 qsort (state->entry, state->entrylen, sizeof (struct folder_file), f);
150 static int link_is_dir (const char *folder, const char *path)
153 char fullpath[_POSIX_PATH_MAX];
155 mutt_concat_path(fullpath, sizeof(fullpath), folder, path);
157 if (stat (fullpath, &st) == 0)
158 return (S_ISDIR (st.st_mode));
163 static const char *folder_format_str (char *dest, size_t destlen, char op,
164 const char *src, const char *fmt,
165 const char *ifstring,
166 const char *elsestring,
167 unsigned long data, format_flag flags)
169 char fn[SHORT_STRING], tmp[SHORT_STRING], permission[11], date[16];
172 FOLDER *folder = (FOLDER *) data;
175 int optional = (flags & M_FORMAT_OPTIONAL);
179 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
180 snprintf (dest, destlen, tmp, folder->num + 1);
184 if (folder->ff->st != NULL) {
187 tnow - folder->ff->st->st_mtime <
188 31536000 ? "%b %d %H:%M" : "%b %d %Y";
189 strftime (date, sizeof (date), t_fmt,
190 localtime (&folder->ff->st->st_mtime));
191 mutt_format_s (dest, destlen, fmt, date);
194 mutt_format_s (dest, destlen, fmt, "");
201 if (folder->ff->imap)
202 s = NONULL (folder->ff->desc);
204 s = NONULL (folder->ff->name);
206 snprintf (fn, sizeof (fn), "%s%s", s,
207 folder->ff->st ? (S_ISLNK (folder->ff->st->st_mode) ? "@" :
208 (S_ISDIR (folder->ff->st->st_mode) ? "/" :
209 ((folder->ff->st->st_mode & S_IXUSR) !=
210 0 ? "*" : ""))) : "");
212 mutt_format_s (dest, destlen, fmt, fn);
216 if (folder->ff->st != NULL) {
217 snprintf (permission, sizeof (permission), "%c%c%c%c%c%c%c%c%c%c",
218 S_ISDIR (folder->ff->st->
219 st_mode) ? 'd' : (S_ISLNK (folder->ff->st->
220 st_mode) ? 'l' : '-'),
221 (folder->ff->st->st_mode & S_IRUSR) != 0 ? 'r' : '-',
222 (folder->ff->st->st_mode & S_IWUSR) != 0 ? 'w' : '-',
223 (folder->ff->st->st_mode & S_ISUID) !=
224 0 ? 's' : (folder->ff->st->st_mode & S_IXUSR) !=
226 (folder->ff->st->st_mode & S_IRGRP) != 0 ? 'r' : '-',
227 (folder->ff->st->st_mode & S_IWGRP) != 0 ? 'w' : '-',
228 (folder->ff->st->st_mode & S_ISGID) !=
229 0 ? 's' : (folder->ff->st->st_mode & S_IXGRP) !=
231 (folder->ff->st->st_mode & S_IROTH) != 0 ? 'r' : '-',
232 (folder->ff->st->st_mode & S_IWOTH) != 0 ? 'w' : '-',
233 (folder->ff->st->st_mode & S_ISVTX) !=
234 0 ? 't' : (folder->ff->st->st_mode & S_IXOTH) !=
236 mutt_format_s (dest, destlen, fmt, permission);
238 else if (folder->ff->imap) {
239 /* mark folders with subfolders AND mail */
240 snprintf (permission, sizeof (permission), "IMAP %c",
241 (folder->ff->inferiors
242 && folder->ff->selectable) ? '+' : ' ');
243 mutt_format_s (dest, destlen, fmt, permission);
246 mutt_format_s (dest, destlen, fmt, "");
250 if (folder->ff->st != NULL) {
251 if ((gr = getgrgid (folder->ff->st->st_gid)))
252 mutt_format_s (dest, destlen, fmt, gr->gr_name);
254 snprintf (tmp, sizeof (tmp), "%%%sld", fmt);
255 snprintf (dest, destlen, tmp, folder->ff->st->st_gid);
259 mutt_format_s (dest, destlen, fmt, "");
263 if (folder->ff->st != NULL) {
264 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
265 snprintf (dest, destlen, tmp, folder->ff->st->st_nlink);
268 mutt_format_s (dest, destlen, fmt, "");
272 if (imap_is_magic (folder->ff->desc, NULL) == M_IMAP) {
274 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
275 snprintf (dest, destlen, tmp, folder->ff->new);
277 else if (!folder->ff->new)
281 snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
282 snprintf (dest, destlen, tmp, folder->ff->new ? 'N' : ' ');
286 if (folder->ff->st != NULL) {
287 snprintf (tmp, sizeof (tmp), "%%%sld", fmt);
288 snprintf (dest, destlen, tmp, (long) folder->ff->st->st_size);
291 mutt_format_s (dest, destlen, fmt, "");
295 snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
296 snprintf (dest, destlen, tmp, folder->ff->tagged ? '*' : ' ');
300 if (folder->ff->st != NULL) {
301 if ((pw = getpwuid (folder->ff->st->st_uid)))
302 mutt_format_s (dest, destlen, fmt, pw->pw_name);
304 snprintf (tmp, sizeof (tmp), "%%%sld", fmt);
305 snprintf (dest, destlen, tmp, folder->ff->st->st_uid);
309 mutt_format_s (dest, destlen, fmt, "");
313 snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
314 snprintf (dest, destlen, tmp, op);
319 mutt_FormatString (dest, destlen, ifstring, folder_format_str, data, 0);
320 else if (flags & M_FORMAT_OPTIONAL)
321 mutt_FormatString (dest, destlen, elsestring, folder_format_str, data, 0);
327 static const char *newsgroup_format_str (char *dest, size_t destlen, char op,
328 const char *src, const char *fmt,
329 const char *ifstring,
330 const char *elsestring,
334 char fn[SHORT_STRING], tmp[SHORT_STRING];
335 FOLDER *folder = (FOLDER *) data;
339 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
340 snprintf (dest, destlen, tmp, folder->num + 1);
344 m_strcpy(fn, sizeof(fn), folder->ff->name);
345 snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
346 snprintf (dest, destlen, tmp, fn);
350 snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
351 if (folder->ff->nd->subscribed)
352 snprintf (dest, destlen, tmp, ' ');
354 snprintf (dest, destlen, tmp, folder->ff->new ? 'N' : 'u');
358 snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
359 if (folder->ff->nd->deleted)
360 snprintf (dest, destlen, tmp, 'D');
362 snprintf (dest, destlen, tmp, folder->ff->nd->allowed ? ' ' : '-');
366 if (flags & M_FORMAT_OPTIONAL) {
367 if (folder->ff->nd->unread != 0)
368 mutt_FormatString (dest, destlen, ifstring, newsgroup_format_str,
371 mutt_FormatString (dest, destlen, elsestring, newsgroup_format_str,
374 else if (Context && Context->data == folder->ff->nd) {
375 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
376 snprintf (dest, destlen, tmp, Context->unread);
379 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
380 snprintf (dest, destlen, tmp, folder->ff->nd->unread);
385 if (Context && Context->data == folder->ff->nd) {
386 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
387 snprintf (dest, destlen, tmp, Context->new);
389 else if (option (OPTMARKOLD) &&
390 folder->ff->nd->lastCached >= folder->ff->nd->firstMessage &&
391 folder->ff->nd->lastCached <= folder->ff->nd->lastMessage) {
392 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
393 snprintf (dest, destlen, tmp,
394 folder->ff->nd->lastMessage - folder->ff->nd->lastCached);
397 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
398 snprintf (dest, destlen, tmp, folder->ff->nd->unread);
403 if (folder->ff->nd->desc != NULL) {
404 snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
405 snprintf (dest, destlen, tmp, folder->ff->nd->desc);
408 snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
409 snprintf (dest, destlen, tmp, "");
415 #endif /* USE_NNTP */
418 static void add_folder (MUTTMENU * m, struct browser_state *state,
419 const char *name, const struct stat *s,
422 static void add_folder (MUTTMENU * m, struct browser_state *state,
423 const char *name, const struct stat *s,
427 if (state->entrylen == state->entrymax) {
428 /* need to allocate more space */
429 p_realloc(&state->entry, state->entrymax += 256);
430 p_clear(&state->entry[state->entrylen], 256);
432 m->data = state->entry;
436 (state->entry)[state->entrylen].mode = s->st_mode;
437 (state->entry)[state->entrylen].mtime = s->st_mtime;
438 (state->entry)[state->entrylen].size = s->st_size;
439 (state->entry)[state->entrylen].st = p_dup(s, 1);
442 (state->entry)[state->entrylen].new = new;
443 (state->entry)[state->entrylen].name = m_strdup(name);
444 (state->entry)[state->entrylen].desc = m_strdup(name);
445 (state->entry)[state->entrylen].imap = 0;
447 if (option (OPTNEWS))
448 (state->entry)[state->entrylen].nd = (NNTP_DATA *) data;
453 static void init_state (struct browser_state *state, MUTTMENU * menu)
456 state->entrymax = 256;
457 state->entry = p_new(struct folder_file, state->entrymax);
458 state->imap_browse = 0;
460 menu->data = state->entry;
463 /* get list of all files/newsgroups with mask */
464 static int examine_directory (MUTTMENU * menu, struct browser_state *state,
465 char *d, const char *prefix)
468 if (option (OPTNEWS)) {
471 NNTP_SERVER *news = CurrentNewsSrv;
473 /* buffy_check (0); */
474 init_state (state, menu);
476 for (tmp = news->list; tmp; tmp = tmp->next) {
477 if (!(data = (NNTP_DATA *) tmp->data))
479 nntp_sync_sidebar (data);
480 if (prefix && *prefix && strncmp (prefix, data->group,
481 m_strlen(prefix)) != 0)
483 if (!((regexec (Mask.rx, data->group, 0, NULL, 0) == 0) ^ Mask.not))
486 add_folder (menu, state, data->group, NULL, data, data->new);
488 add_folder (menu, state, data->group, NULL, data->new);
491 sidebar_draw (CurrentMenu);
494 #endif /* USE_NNTP */
499 char buffer[_POSIX_PATH_MAX + SHORT_STRING];
502 while (stat (d, &s) == -1) {
503 if (errno == ENOENT) {
504 /* The last used directory is deleted, try to use the parent dir. */
505 char *c = strrchr (d, '/');
516 if (!S_ISDIR (s.st_mode)) {
517 mutt_error (_("%s is not a directory."), d);
523 if ((dp = opendir (d)) == NULL) {
528 init_state (state, menu);
530 while ((de = readdir (dp)) != NULL) {
531 if (m_strcmp(de->d_name, ".") == 0)
532 continue; /* we don't need . */
534 if (prefix && *prefix
535 && m_strncmp(prefix, de->d_name, m_strlen(prefix)) != 0)
537 if (!((regexec (Mask.rx, de->d_name, 0, NULL, 0) == 0) ^ Mask.not))
540 mutt_concat_path(buffer, sizeof(buffer), d, de->d_name);
541 if (lstat (buffer, &s) == -1)
544 if ((!S_ISREG (s.st_mode)) && (!S_ISDIR (s.st_mode)) &&
545 (!S_ISLNK (s.st_mode)))
548 i = buffy_lookup (buffer);
550 add_folder (menu, state, de->d_name, &s, NULL, i >= 0 ? ((BUFFY*) Incoming->data[i])->new : 0);
552 add_folder (menu, state, de->d_name, &s, i >= 0 ? ((BUFFY*) Incoming->data[i])->new : 0);
557 sidebar_draw (CurrentMenu);
558 browser_sort (state);
562 /* get list of mailboxes/subscribed newsgroups */
563 static int examine_mailboxes (MUTTMENU * menu, struct browser_state *state)
566 char buffer[LONG_STRING];
569 if (option (OPTNEWS)) {
572 NNTP_SERVER *news = CurrentNewsSrv;
574 /* buffy_check (0); */
575 init_state (state, menu);
577 for (tmp = news->list; tmp; tmp = tmp->next) {
578 if ((data = (NNTP_DATA*) tmp->data) == NULL)
580 nntp_sync_sidebar (data);
581 if ((data->new || (data->subscribed &&
582 (!option (OPTSHOWONLYUNREAD)|| data->unread))))
583 add_folder (menu, state, data->group, NULL, data, data->new);
585 sidebar_draw (CurrentMenu);
597 init_state (state, menu);
599 for (i = 0; i < Incoming->length; i++) {
600 tmp = (BUFFY*) Incoming->data[i];
601 tmp->magic = mx_get_magic (tmp->path);
602 if (tmp->magic == M_IMAP) {
604 add_folder (menu, state, tmp->path, NULL, NULL, tmp->new);
606 add_folder (menu, state, tmp->path, NULL, tmp->new);
610 if (tmp->magic == M_POP) {
612 add_folder (menu, state, tmp->path, NULL, NULL, tmp->new);
614 add_folder (menu, state, tmp->path, NULL, tmp->new);
619 if (tmp->magic == M_NNTP) {
620 add_folder (menu, state, tmp->path, NULL, NULL, tmp->new);
624 if (lstat (tmp->path, &s) == -1)
627 if ((!S_ISREG (s.st_mode)) && (!S_ISDIR (s.st_mode)) &&
628 (!S_ISLNK (s.st_mode)))
631 m_strcpy(buffer, sizeof(buffer), NONULL(tmp->path));
632 mutt_pretty_mailbox (buffer);
635 add_folder (menu, state, buffer, &s, NULL, tmp->new);
637 add_folder (menu, state, buffer, &s, tmp->new);
641 browser_sort (state);
645 static int select_file_search (MUTTMENU * menu, regex_t * re, int n)
648 if (option (OPTNEWS))
650 (re, ((struct folder_file *) menu->data)[n].desc, 0, NULL, 0));
653 (re, ((struct folder_file *) menu->data)[n].name, 0, NULL, 0));
656 static void folder_entry (char *s, size_t slen, MUTTMENU * menu, int num)
660 folder.ff = &((struct folder_file *) menu->data)[num];
664 if (option (OPTNEWS))
665 mutt_FormatString (s, slen, NONULL (GroupFormat), newsgroup_format_str,
666 (unsigned long) &folder, M_FORMAT_ARROWCURSOR);
669 mutt_FormatString (s, slen, NONULL (FolderFormat), folder_format_str,
670 (unsigned long) &folder, M_FORMAT_ARROWCURSOR);
673 static void init_menu (struct browser_state *state, MUTTMENU * menu,
674 char *title, size_t titlelen, int buffy)
676 char path[_POSIX_PATH_MAX];
678 menu->max = state->entrylen;
680 if (menu->current >= menu->max)
681 menu->current = menu->max - 1;
682 if (menu->current < 0)
684 if (menu->top > menu->current)
690 if (option (OPTNEWS)) {
692 snprintf (title, titlelen, "%s", _("Subscribed newsgroups"));
694 snprintf (title, titlelen, _("Newsgroups on server [%s]"),
695 CurrentNewsSrv->conn->account.host);
700 snprintf(title, titlelen, _("Mailboxes [%d]"), buffy_check(0));
702 m_strcpy(path, sizeof(path), LastDir);
703 mutt_pretty_mailbox (path);
704 if (state->imap_browse && option (OPTIMAPLSUB))
705 snprintf (title, titlelen, _("Subscribed [%s], File mask: %s"),
706 path, NONULL (Mask.pattern));
708 snprintf (title, titlelen, _("Directory [%s], File mask: %s"),
709 path, NONULL (Mask.pattern));
711 menu->redraw = REDRAW_FULL;
714 static int file_tag (MUTTMENU * menu, int n, int m)
716 struct folder_file *ff = &(((struct folder_file *) menu->data)[n]);
719 if (S_ISDIR (ff->mode)
720 || (S_ISLNK (ff->mode) && link_is_dir (LastDir, ff->name))) {
721 mutt_error _("Can't attach a directory!");
727 ff->tagged = (m >= 0 ? m : !ff->tagged);
729 return ff->tagged - ot;
732 void _mutt_select_file (char *f, size_t flen, int flags, char ***files,
735 char buf[_POSIX_PATH_MAX];
736 char prefix[_POSIX_PATH_MAX] = "";
737 char helpstr[SHORT_STRING];
739 struct browser_state state;
742 int i, killPrefix = 0;
743 int multiple = (flags & M_SEL_MULTI) ? 1 : 0;
744 int folder = (flags & M_SEL_FOLDER) ? 1 : 0;
745 int buffy = (flags & M_SEL_BUFFY) ? 1 : 0;
747 buffy = buffy && folder;
752 m_strcpy(LastDirBackup, sizeof(LastDirBackup), LastDir);
755 if (option (OPTNEWS)) {
757 m_strcpy(prefix, sizeof(prefix), f);
761 /* default state for news reader mode is browse subscribed newsgroups */
763 for (list = CurrentNewsSrv->list; list; list = list->next) {
764 NNTP_DATA *data = (NNTP_DATA *) list->data;
766 if (data && data->subscribed) {
776 mutt_expand_path (f, flen);
777 if (imap_is_magic (f, NULL) == M_IMAP) {
778 init_state (&state, NULL);
779 state.imap_browse = 1;
780 if (!imap_browse (f, &state))
781 m_strcpy(LastDir, sizeof(LastDir), state.folder);
784 for (i = m_strlen(f) - 1; i > 0 && f[i] != '/'; i--);
787 i = MIN(ssizeof(LastDir) - 1, i);
788 m_strcpy(LastDir, sizeof(LastDir), f);
791 getcwd(LastDir, sizeof(LastDir));
792 m_strcat(LastDir, sizeof(LastDir), "/");
793 m_strncat(LastDir, sizeof(LastDir), f, i);
798 strcpy (LastDir, "/"); /* __STRCPY_CHECKED__ */
800 getcwd (LastDir, sizeof (LastDir));
803 if (i <= 0 && f[0] != '/')
804 m_strcpy(prefix, sizeof(prefix), f);
806 m_strcpy(prefix, sizeof(prefix), f + i + 1);
812 getcwd (LastDir, sizeof (LastDir));
813 else if (!LastDir[0])
814 m_strcpy(LastDir, sizeof(LastDir), NONULL(Maildir));
816 if (!buffy && imap_is_magic (LastDir, NULL) == M_IMAP) {
817 init_state (&state, NULL);
818 state.imap_browse = 1;
819 imap_browse (LastDir, &state);
820 browser_sort (&state);
827 if (examine_mailboxes (NULL, &state) == -1)
831 if (!state.imap_browse)
832 if (examine_directory (NULL, &state, LastDir, prefix) == -1)
835 menu = mutt_new_menu ();
836 menu->menu = MENU_FOLDER;
837 menu->make_entry = folder_entry;
838 menu->search = select_file_search;
840 menu->data = state.entry;
842 menu->tag = file_tag;
844 menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_FOLDER,
846 (option (OPTNEWS)) ? FolderNewsHelp :
850 init_menu (&state, menu, title, sizeof (title), buffy);
853 switch (i = mutt_menuLoop (menu)) {
854 case OP_GENERIC_SELECT_ENTRY:
856 if (!state.entrylen) {
857 mutt_error _("No files match the file mask");
862 if (S_ISDIR (state.entry[menu->current].mode) ||
863 (S_ISLNK (state.entry[menu->current].mode) &&
864 link_is_dir (LastDir, state.entry[menu->current].name))
865 || state.entry[menu->current].inferiors
867 /* make sure this isn't a MH or maildir mailbox */
869 m_strcpy(buf, sizeof(buf), state.entry[menu->current].name);
870 mutt_expand_path (buf, sizeof (buf));
872 else if (state.imap_browse) {
873 m_strcpy(buf, sizeof(buf), state.entry[menu->current].name);
876 mutt_concat_path(buf, sizeof(buf), LastDir,
877 state.entry[menu->current].name);
879 if ((mx_get_magic (buf) <= 0)
880 || state.entry[menu->current].inferiors)
882 char OldLastDir[_POSIX_PATH_MAX];
884 /* save the old directory */
885 m_strcpy(OldLastDir, sizeof(OldLastDir), LastDir);
887 if (m_strcmp(state.entry[menu->current].name, "..") == 0) {
888 if (m_strcmp("..", LastDir + m_strlen(LastDir) - 2) == 0)
889 strcat (LastDir, "/.."); /* __STRCAT_CHECKED__ */
891 char *p = strrchr (LastDir + 1, '/');
896 if (LastDir[0] == '/')
899 strcat (LastDir, "/.."); /* __STRCAT_CHECKED__ */
904 m_strcpy(LastDir, sizeof(LastDir),
905 state.entry[menu->current].name);
906 mutt_expand_path (LastDir, sizeof (LastDir));
908 else if (state.imap_browse) {
912 m_strcpy(LastDir, sizeof(LastDir),
913 state.entry[menu->current].name);
914 /* tack on delimiter here */
915 n = m_strlen(LastDir) + 1;
917 /* special case "" needs no delimiter */
918 url_parse_ciss (&url, state.entry[menu->current].name);
920 (state.entry[menu->current].delim != '\0') &&
921 (n < sizeof (LastDir))) {
923 LastDir[n - 1] = state.entry[menu->current].delim;
927 char tmp[_POSIX_PATH_MAX];
929 mutt_concat_path(tmp, sizeof(tmp), LastDir,
930 state.entry[menu->current].name);
931 m_strcpy(LastDir, sizeof(LastDir), tmp);
934 destroy_state (&state);
940 if (state.imap_browse) {
941 init_state (&state, NULL);
942 state.imap_browse = 1;
943 imap_browse (LastDir, &state);
944 browser_sort (&state);
945 menu->data = state.entry;
948 if (examine_directory (menu, &state, LastDir, prefix) == -1) {
949 /* try to restore the old values */
950 m_strcpy(LastDir, sizeof(LastDir), OldLastDir);
951 if (examine_directory (menu, &state, LastDir, prefix) == -1) {
952 m_strcpy(LastDir, sizeof(LastDir), NONULL(Homedir));
958 init_menu (&state, menu, title, sizeof (title), buffy);
964 if (buffy || option (OPTNEWS)) /* news have not path */
969 m_strcpy(f, flen, state.entry[menu->current].name);
970 mutt_expand_path (f, flen);
972 else if (state.imap_browse)
973 m_strcpy(f, flen, state.entry[menu->current].name);
975 mutt_concat_path(f, flen, LastDir, state.entry[menu->current].name);
977 /* Fall through to OP_EXIT */
987 *numfiles = menu->tagged;
988 tfiles = p_new(char *, *numfiles);
989 for (h = 0, j = 0; h < state.entrylen; i++) {
990 struct folder_file ff = state.entry[i];
991 char full[_POSIX_PATH_MAX];
994 mutt_concat_path(full, sizeof(full), LastDir, ff.name);
995 mutt_expand_path (full, sizeof (full));
996 tfiles[j++] = m_strdup(full);
1001 else if (f[0]) { /* no tagged entries. return selected entry */
1003 tfiles = p_new(char *, *numfiles);
1004 mutt_expand_path (f, flen);
1005 tfiles[0] = m_strdup(f);
1010 destroy_state (&state);
1011 mutt_menuDestroy (&menu);
1014 case OP_BROWSER_TELL:
1016 mutt_message ("%s", state.entry[menu->current].name);
1019 case OP_BROWSER_TOGGLE_LSUB:
1020 if (option (OPTIMAPLSUB)) {
1021 unset_option (OPTIMAPLSUB);
1024 set_option (OPTIMAPLSUB);
1026 mutt_ungetch (0, OP_CHECK_NEW);
1029 case OP_CREATE_MAILBOX:
1030 if (!state.imap_browse)
1031 mutt_error (_("Create is only supported for IMAP mailboxes"));
1033 imap_mailbox_create (LastDir);
1034 /* TODO: find a way to detect if the new folder would appear in
1035 * this window, and insert it without starting over. */
1036 destroy_state (&state);
1037 init_state (&state, NULL);
1038 state.imap_browse = 1;
1039 imap_browse (LastDir, &state);
1040 browser_sort (&state);
1041 menu->data = state.entry;
1044 init_menu (&state, menu, title, sizeof (title), buffy);
1045 MAYBE_REDRAW (menu->redraw);
1049 case OP_RENAME_MAILBOX:
1050 if (!state.entry[menu->current].imap)
1051 mutt_error (_("Rename is only supported for IMAP mailboxes"));
1053 int nentry = menu->current;
1055 if (imap_mailbox_rename (state.entry[nentry].name) >= 0) {
1056 destroy_state (&state);
1057 init_state (&state, NULL);
1058 state.imap_browse = 1;
1059 imap_browse (LastDir, &state);
1060 browser_sort (&state);
1061 menu->data = state.entry;
1064 init_menu (&state, menu, title, sizeof (title), buffy);
1065 MAYBE_REDRAW (menu->redraw);
1070 case OP_DELETE_MAILBOX:
1071 if (!state.entry[menu->current].imap)
1072 mutt_error (_("Delete is only supported for IMAP mailboxes"));
1074 char msg[SHORT_STRING];
1076 int nentry = menu->current;
1078 imap_parse_path (state.entry[nentry].name, &mx);
1079 snprintf (msg, sizeof (msg), _("Really delete mailbox \"%s\"?"),
1081 if (mutt_yesorno (msg, M_NO) == M_YES) {
1082 if (!imap_delete_mailbox (Context, mx)) {
1083 /* free the mailbox from the browser */
1084 p_delete(&((state.entry)[nentry].name));
1085 p_delete(&((state.entry)[nentry].desc));
1086 /* and move all other entries up */
1087 if (nentry + 1 < state.entrylen)
1088 memmove (state.entry + nentry, state.entry + nentry + 1,
1089 sizeof (struct folder_file) * (state.entrylen -
1092 mutt_message _("Mailbox deleted.");
1094 init_menu (&state, menu, title, sizeof (title), buffy);
1095 MAYBE_REDRAW (menu->redraw);
1099 mutt_message _("Mailbox not deleted.");
1104 case OP_CHANGE_DIRECTORY:
1107 if (option (OPTNEWS))
1111 m_strcpy(buf, sizeof(buf), LastDir);
1112 if (!state.imap_browse)
1114 /* add '/' at the end of the directory name if not already there */
1115 size_t len = m_strlen(LastDir);
1117 if (len && LastDir[len - 1] != '/' && sizeof (buf) > len)
1121 if (mutt_get_field (_("Chdir to: "), buf, sizeof (buf), M_FILE) == 0 &&
1124 mutt_expand_path (buf, sizeof (buf));
1125 if (imap_is_magic (buf, NULL) == M_IMAP) {
1126 m_strcpy(LastDir, sizeof(LastDir), buf);
1127 destroy_state (&state);
1128 init_state (&state, NULL);
1129 state.imap_browse = 1;
1130 imap_browse (LastDir, &state);
1131 browser_sort (&state);
1132 menu->data = state.entry;
1135 init_menu (&state, menu, title, sizeof (title), buffy);
1138 if (stat (buf, &st) == 0) {
1139 if (S_ISDIR (st.st_mode)) {
1140 destroy_state (&state);
1141 if (examine_directory (menu, &state, buf, prefix) == 0)
1142 m_strcpy(LastDir, sizeof(LastDir), buf);
1144 mutt_error _("Error scanning directory.");
1146 if (examine_directory (menu, &state, LastDir, prefix) == -1) {
1147 mutt_menuDestroy (&menu);
1153 init_menu (&state, menu, title, sizeof (title), buffy);
1156 mutt_error (_("%s is not a directory."), buf);
1161 MAYBE_REDRAW (menu->redraw);
1166 m_strcpy(buf, sizeof(buf), NONULL(Mask.pattern));
1167 if (mutt_get_field (_("File Mask: "), buf, sizeof (buf), 0) == 0) {
1168 regex_t *rx = p_new(regex_t, 1);
1173 /* assume that the user wants to see everything */
1175 m_strcpy(buf, sizeof(buf), ".");
1178 s = vskipspaces(s + 1);
1182 if ((err = REGCOMP (rx, s, REG_NOSUB)) != 0) {
1183 regerror (err, rx, buf, sizeof (buf));
1186 mutt_error ("%s", buf);
1189 m_strreplace(&Mask.pattern, buf);
1195 destroy_state (&state);
1196 if (state.imap_browse) {
1197 init_state (&state, NULL);
1198 state.imap_browse = 1;
1199 imap_browse (LastDir, &state);
1200 browser_sort (&state);
1201 menu->data = state.entry;
1202 init_menu (&state, menu, title, sizeof (title), buffy);
1205 if (examine_directory (menu, &state, LastDir, NULL) == 0)
1206 init_menu (&state, menu, title, sizeof (title), buffy);
1208 mutt_error _("Error scanning directory.");
1210 mutt_menuDestroy (&menu);
1214 if (!state.entrylen) {
1215 mutt_error _("No files match the file mask");
1221 MAYBE_REDRAW (menu->redraw);
1225 case OP_SORT_REVERSE:
1229 int reverse = (i == OP_SORT_REVERSE);
1231 switch (mutt_multi_choice ((reverse) ?
1233 ("Reverse sort by (d)ate, (a)lpha, si(z)e or do(n)'t sort? ")
1236 ("Sort by (d)ate, (a)lpha, si(z)e or do(n)'t sort? "),
1238 case -1: /* abort */
1242 case 1: /* (d)ate */
1243 BrowserSort = SORT_DATE;
1246 case 2: /* (a)lpha */
1247 BrowserSort = SORT_SUBJECT;
1250 case 3: /* si(z)e */
1251 BrowserSort = SORT_SIZE;
1254 case 4: /* do(n)'t sort */
1255 BrowserSort = SORT_ORDER;
1260 BrowserSort |= reverse ? SORT_REVERSE : 0;
1261 browser_sort (&state);
1262 menu->redraw = REDRAW_FULL;
1267 case OP_TOGGLE_MAILBOXES:
1271 destroy_state (&state);
1276 if (examine_mailboxes (menu, &state) == -1)
1279 else if (imap_is_magic (LastDir, NULL) == M_IMAP) {
1280 init_state (&state, NULL);
1281 state.imap_browse = 1;
1282 imap_browse (LastDir, &state);
1283 browser_sort (&state);
1284 menu->data = state.entry;
1286 else if (examine_directory (menu, &state, LastDir, prefix) == -1)
1288 init_menu (&state, menu, title, sizeof (title), buffy);
1292 if (option (OPTFORCEBUFFYCHECK))
1297 case OP_BROWSER_NEW_FILE:
1299 snprintf (buf, sizeof (buf), "%s/", LastDir);
1300 if (mutt_get_field (_("New file name: "), buf, sizeof (buf), M_FILE) ==
1302 m_strcpy(f, flen, buf);
1303 destroy_state (&state);
1304 mutt_menuDestroy (&menu);
1307 MAYBE_REDRAW (menu->redraw);
1310 case OP_BROWSER_VIEW_FILE:
1311 if (!state.entrylen) {
1312 mutt_error _("No files match the file mask");
1317 if (state.entry[menu->current].selectable) {
1318 m_strcpy(f, flen, state.entry[menu->current].name);
1319 destroy_state (&state);
1320 mutt_menuDestroy (&menu);
1324 if (S_ISDIR (state.entry[menu->current].mode) ||
1325 (S_ISLNK (state.entry[menu->current].mode) &&
1326 link_is_dir (LastDir, state.entry[menu->current].name))) {
1327 mutt_error _("Can't view a directory");
1333 char nbuf[_POSIX_PATH_MAX];
1335 mutt_concat_path(nbuf, sizeof(nbuf), LastDir,
1336 state.entry[menu->current].name);
1337 b = mutt_make_file_attach (nbuf);
1339 mutt_view_attachment (NULL, b, M_REGULAR, NULL, NULL, 0);
1340 mutt_free_body (&b);
1341 menu->redraw = REDRAW_FULL;
1344 mutt_error _("Error trying to view file");
1351 if (option (OPTNEWS)) {
1352 struct folder_file *f = &state.entry[menu->current];
1355 if (i == OP_CATCHUP)
1356 nd = mutt_newsgroup_catchup (CurrentNewsSrv, f->name);
1358 nd = mutt_newsgroup_uncatchup (CurrentNewsSrv, f->name);
1362 struct folder_file ff;
1363 char buffer[_POSIX_PATH_MAX + SHORT_STRING];
1366 folder.ff->name = f->name;
1367 folder.ff->st = NULL;
1368 folder.ff->is_new = nd->new;
1371 mutt_FormatString (buffer, sizeof (buffer), NONULL(GroupFormat),
1372 newsgroup_format_str, (unsigned long) &folder,
1373 M_FORMAT_ARROWCURSOR);
1374 f->desc = m_strdup(buffer); */
1375 if (menu->current + 1 < menu->max)
1377 menu->redraw = REDRAW_MOTION_RESYNCH;
1382 case OP_LOAD_ACTIVE:
1383 if (!option (OPTNEWS))
1390 for (tmp = CurrentNewsSrv->list; tmp; tmp = tmp->next) {
1391 if ((data = (NNTP_DATA *) tmp->data))
1395 nntp_get_active (CurrentNewsSrv);
1397 destroy_state (&state);
1399 examine_mailboxes (menu, &state);
1401 examine_directory (menu, &state, NULL, NULL);
1402 init_menu (&state, menu, title, sizeof (title), buffy);
1404 #endif /* USE_NNTP */
1406 case OP_BROWSER_SUBSCRIBE:
1407 case OP_BROWSER_UNSUBSCRIBE:
1409 case OP_SUBSCRIBE_PATTERN:
1410 case OP_UNSUBSCRIBE_PATTERN:
1411 if (option (OPTNEWS)) {
1412 regex_t *rx = p_new(regex_t, 1);
1414 int j = menu->current;
1416 NNTP_SERVER *news = CurrentNewsSrv;
1418 if (i == OP_SUBSCRIBE_PATTERN || i == OP_UNSUBSCRIBE_PATTERN) {
1423 if (i == OP_SUBSCRIBE_PATTERN)
1424 snprintf (tmp, sizeof (tmp), _("Subscribe pattern: "));
1426 snprintf (tmp, sizeof (tmp), _("Unsubscribe pattern: "));
1427 if (mutt_get_field (tmp, buf, sizeof (buf), 0) != 0 || !buf[0]) {
1432 if ((err = REGCOMP (rx, s, REG_NOSUB)) != 0) {
1433 regerror (err, rx, buf, sizeof (buf));
1436 mutt_error ("%s", buf);
1439 menu->redraw = REDRAW_FULL;
1442 else if (!state.entrylen) {
1443 mutt_error _("No newsgroups match the mask");
1448 for (; j < state.entrylen; j++) {
1449 struct folder_file *f = &state.entry[j];
1451 if (i == OP_BROWSER_SUBSCRIBE || i == OP_BROWSER_UNSUBSCRIBE ||
1452 regexec (rx, f->name, 0, NULL, 0) == 0) {
1453 if (i == OP_BROWSER_SUBSCRIBE || i == OP_SUBSCRIBE_PATTERN)
1454 nd = mutt_newsgroup_subscribe (news, f->name);
1456 nd = mutt_newsgroup_unsubscribe (news, f->name);
1460 char buffer[_POSIX_PATH_MAX + SHORT_STRING];
1462 folder.name = f->name;
1464 folder.new = nd->new;
1467 mutt_FormatString (buffer, sizeof (buffer), NONULL(GroupFormat),
1468 newsgroup_format_str, (unsigned long) &folder,
1469 M_FORMAT_ARROWCURSOR);
1470 f->desc = m_strdup(buffer);
1473 if (i == OP_BROWSER_SUBSCRIBE || i == OP_BROWSER_UNSUBSCRIBE) {
1474 if (menu->current + 1 < menu->max)
1476 menu->redraw = REDRAW_MOTION_RESYNCH;
1480 if (i == OP_SUBSCRIBE_PATTERN) {
1481 string_list_t *grouplist = NULL;
1484 grouplist = news->list;
1485 for (; grouplist; grouplist = grouplist->next) {
1486 nd = (NNTP_DATA *) grouplist->data;
1487 if (nd && nd->group && !nd->subscribed) {
1488 if (regexec (rx, nd->group, 0, NULL, 0) == 0) {
1489 mutt_newsgroup_subscribe (news, nd->group);
1490 add_folder (menu, &state, nd->group, NULL, nd, nd->new);
1494 init_menu (&state, menu, title, sizeof (title), buffy);
1496 mutt_newsrc_update (news);
1497 nntp_clear_cacheindex (news);
1498 if (i != OP_BROWSER_SUBSCRIBE && i != OP_BROWSER_UNSUBSCRIBE)
1503 #endif /* USE_NNTP */
1505 if (i == OP_BROWSER_SUBSCRIBE)
1506 imap_subscribe (state.entry[menu->current].name, 1);
1508 imap_subscribe (state.entry[menu->current].name, 0);
1516 m_strcpy(LastDir, sizeof(LastDir), LastDirBackup);