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>
33 typedef struct folder_t {
34 struct folder_file *ff;
38 static char LastDir[_POSIX_PATH_MAX] = "";
39 static char LastDirBackup[_POSIX_PATH_MAX] = "";
41 /* Frees up the memory allocated for the local-global variables. */
42 static void destroy_state (struct browser_state *state)
46 for (c = 0; c < state->entrylen; c++) {
47 p_delete(&state->entry[c].name);
48 p_delete(&state->entry[c].desc);
49 p_delete(&state->entry[c].st);
51 p_delete(&state->folder);
52 p_delete(&state->entry);
55 static int browser_compare_subject (const void *a, const void *b)
57 struct folder_file *pa = (struct folder_file *) a;
58 struct folder_file *pb = (struct folder_file *) b;
60 int r = strcoll(NONULL(pa->name), NONULL(pb->name));
62 return ((BrowserSort & SORT_REVERSE) ? -r : r);
65 static int browser_compare_date (const void *a, const void *b)
67 struct folder_file *pa = (struct folder_file *) a;
68 struct folder_file *pb = (struct folder_file *) b;
70 int r = pa->mtime - pb->mtime;
72 return ((BrowserSort & SORT_REVERSE) ? -r : r);
75 static int browser_compare_size (const void *a, const void *b)
77 struct folder_file *pa = (struct folder_file *) a;
78 struct folder_file *pb = (struct folder_file *) b;
80 int r = pa->size - pb->size;
82 return ((BrowserSort & SORT_REVERSE) ? -r : r);
85 static void browser_sort (struct browser_state *state)
87 int (*f) (const void *, const void *);
89 switch (BrowserSort & SORT_MASK) {
97 f = browser_compare_date;
101 if (option (OPTNEWS))
104 f = browser_compare_size;
108 f = browser_compare_subject;
111 qsort (state->entry, state->entrylen, sizeof (struct folder_file), f);
114 static int link_is_dir (const char *folder, const char *path)
117 char fullpath[_POSIX_PATH_MAX];
119 mutt_concat_path(fullpath, sizeof(fullpath), folder, path);
121 if (stat (fullpath, &st) == 0)
122 return (S_ISDIR (st.st_mode));
128 folder_format_str(char *dest, ssize_t destlen, char op,
129 const char *src, const char *fmt,
130 const char *ifstr, const char *elstr,
131 anytype data, format_flag flags)
133 char fn[STRING], tmp[STRING], permission[11], date[16];
136 FOLDER *folder = data.ptr;
139 int optional = (flags & M_FORMAT_OPTIONAL);
143 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
144 snprintf (dest, destlen, tmp, folder->num + 1);
148 if (folder->ff->st != NULL) {
151 tnow - folder->ff->st->st_mtime <
152 31536000 ? "%b %d %H:%M" : "%b %d %Y";
153 strftime (date, sizeof (date), t_fmt,
154 localtime (&folder->ff->st->st_mtime));
155 mutt_format_s (dest, destlen, fmt, date);
158 mutt_format_s (dest, destlen, fmt, "");
165 if (folder->ff->imap)
166 s = NONULL(folder->ff->desc);
168 s = NONULL(folder->ff->name);
170 snprintf (fn, sizeof (fn), "%s%s", s,
171 folder->ff->st ? (S_ISLNK (folder->ff->st->st_mode) ? "@" :
172 (S_ISDIR (folder->ff->st->st_mode) ? "/" :
173 ((folder->ff->st->st_mode & S_IXUSR) !=
174 0 ? "*" : ""))) : "");
176 mutt_format_s (dest, destlen, fmt, fn);
180 if (folder->ff->st != NULL) {
181 snprintf (permission, sizeof (permission), "%c%c%c%c%c%c%c%c%c%c",
182 S_ISDIR(folder->ff->st-> st_mode)
183 ? 'd' : (S_ISLNK(folder->ff->st-> st_mode) ? 'l' : '-'),
184 (folder->ff->st->st_mode & S_IRUSR) != 0 ? 'r' : '-',
185 (folder->ff->st->st_mode & S_IWUSR) != 0 ? 'w' : '-',
186 (folder->ff->st->st_mode & S_ISUID) !=
187 0 ? 's' : (folder->ff->st->st_mode & S_IXUSR) !=
189 (folder->ff->st->st_mode & S_IRGRP) != 0 ? 'r' : '-',
190 (folder->ff->st->st_mode & S_IWGRP) != 0 ? 'w' : '-',
191 (folder->ff->st->st_mode & S_ISGID) !=
192 0 ? 's' : (folder->ff->st->st_mode & S_IXGRP) !=
194 (folder->ff->st->st_mode & S_IROTH) != 0 ? 'r' : '-',
195 (folder->ff->st->st_mode & S_IWOTH) != 0 ? 'w' : '-',
196 (folder->ff->st->st_mode & S_ISVTX) !=
197 0 ? 't' : (folder->ff->st->st_mode & S_IXOTH) !=
199 mutt_format_s (dest, destlen, fmt, permission);
201 else if (folder->ff->imap) {
202 /* mark folders with subfolders AND mail */
203 snprintf (permission, sizeof (permission), "IMAP %c",
204 (folder->ff->inferiors
205 && folder->ff->selectable) ? '+' : ' ');
206 mutt_format_s (dest, destlen, fmt, permission);
209 mutt_format_s (dest, destlen, fmt, "");
213 if (folder->ff->st != NULL) {
214 if ((gr = getgrgid (folder->ff->st->st_gid)))
215 mutt_format_s (dest, destlen, fmt, gr->gr_name);
217 snprintf (tmp, sizeof (tmp), "%%%sld", fmt);
218 snprintf (dest, destlen, tmp, folder->ff->st->st_gid);
222 mutt_format_s (dest, destlen, fmt, "");
226 if (folder->ff->st != NULL) {
227 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
228 snprintf (dest, destlen, tmp, folder->ff->st->st_nlink);
231 mutt_format_s (dest, destlen, fmt, "");
235 if (imap_is_magic (folder->ff->desc, NULL) == M_IMAP) {
237 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
238 snprintf (dest, destlen, tmp, folder->ff->new);
240 else if (!folder->ff->new)
244 snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
245 snprintf (dest, destlen, tmp, folder->ff->new ? 'N' : ' ');
249 if (folder->ff->st != NULL) {
250 snprintf (tmp, sizeof (tmp), "%%%sld", fmt);
251 snprintf (dest, destlen, tmp, (long) folder->ff->st->st_size);
254 mutt_format_s (dest, destlen, fmt, "");
258 snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
259 snprintf (dest, destlen, tmp, folder->ff->tagged ? '*' : ' ');
263 if (folder->ff->st != NULL) {
264 if ((pw = getpwuid (folder->ff->st->st_uid)))
265 mutt_format_s (dest, destlen, fmt, pw->pw_name);
267 snprintf (tmp, sizeof (tmp), "%%%sld", fmt);
268 snprintf (dest, destlen, tmp, folder->ff->st->st_uid);
272 mutt_format_s (dest, destlen, fmt, "");
276 snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
277 snprintf (dest, destlen, tmp, op);
282 if (flags & M_FORMAT_OPTIONAL)
283 m_strformat(dest, destlen, 0, optional ? ifstr : elstr,
284 folder_format_str, data, 0);
291 newsgroup_format_str(char *dest, ssize_t destlen, char op,
292 const char *src, const char *fmt,
293 const char *ifstr, const char *elstr,
294 anytype data, format_flag flags)
296 char fn[STRING], tmp[STRING];
297 FOLDER *folder = data.ptr;
301 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
302 snprintf (dest, destlen, tmp, folder->num + 1);
306 m_strcpy(fn, sizeof(fn), folder->ff->name);
307 snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
308 snprintf (dest, destlen, tmp, fn);
312 snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
313 if (folder->ff->nd->subscribed)
314 snprintf (dest, destlen, tmp, ' ');
316 snprintf (dest, destlen, tmp, folder->ff->new ? 'N' : 'u');
320 snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
321 if (folder->ff->nd->deleted)
322 snprintf (dest, destlen, tmp, 'D');
324 snprintf (dest, destlen, tmp, folder->ff->nd->allowed ? ' ' : '-');
328 if (flags & M_FORMAT_OPTIONAL) {
329 if (folder->ff->nd->unread != 0)
330 m_strformat(dest, destlen, 0, ifstr, newsgroup_format_str,
333 m_strformat (dest, destlen, 0, elstr, newsgroup_format_str,
336 else if (Context && Context->data == folder->ff->nd) {
337 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
338 snprintf (dest, destlen, tmp, Context->unread);
341 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
342 snprintf (dest, destlen, tmp, folder->ff->nd->unread);
347 if (Context && Context->data == folder->ff->nd) {
348 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
349 snprintf (dest, destlen, tmp, Context->new);
351 else if (option (OPTMARKOLD) &&
352 folder->ff->nd->lastCached >= folder->ff->nd->first &&
353 folder->ff->nd->lastCached <= folder->ff->nd->last) {
354 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
355 snprintf (dest, destlen, tmp,
356 folder->ff->nd->last - folder->ff->nd->lastCached);
359 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
360 snprintf (dest, destlen, tmp, folder->ff->nd->unread);
365 if (folder->ff->nd->desc != NULL) {
366 snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
367 snprintf (dest, destlen, tmp, folder->ff->nd->desc);
370 snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
371 snprintf (dest, destlen, tmp, "");
377 #endif /* USE_NNTP */
380 static void add_folder (MUTTMENU * m, struct browser_state *state,
381 const char *name, const struct stat *s,
384 static void add_folder (MUTTMENU * m, struct browser_state *state,
385 const char *name, const struct stat *s,
389 if (state->entrylen == state->entrymax) {
390 /* need to allocate more space */
391 p_realloc(&state->entry, state->entrymax += 256);
392 p_clear(&state->entry[state->entrylen], 256);
394 m->data = state->entry;
398 (state->entry)[state->entrylen].mode = s->st_mode;
399 (state->entry)[state->entrylen].mtime = s->st_mtime;
400 (state->entry)[state->entrylen].size = s->st_size;
401 (state->entry)[state->entrylen].st = p_dup(s, 1);
404 (state->entry)[state->entrylen].new = new;
405 (state->entry)[state->entrylen].name = m_strdup(name);
406 (state->entry)[state->entrylen].desc = m_strdup(name);
407 (state->entry)[state->entrylen].imap = 0;
409 if (option (OPTNEWS))
410 (state->entry)[state->entrylen].nd = data;
415 static void init_state (struct browser_state *state, MUTTMENU * menu)
418 state->entrymax = 256;
419 state->entry = p_new(struct folder_file, state->entrymax);
420 state->imap_browse = 0;
422 menu->data = state->entry;
425 /* get list of all files/newsgroups with mask */
426 static int examine_directory (MUTTMENU * menu, struct browser_state *state,
427 char *d, const char *prefix)
430 if (option (OPTNEWS)) {
432 nntp_server_t *news = CurrentNewsSrv;
434 /* buffy_check (0); */
435 init_state (state, menu);
437 for (data = news->list; data; data = data->next) {
438 nntp_sync_sidebar(data);
439 if (m_strncmp (prefix, data->group, m_strlen(prefix)) != 0)
441 if (!((regexec (Mask.rx, data->group, 0, NULL, 0) == 0) ^ Mask.neg))
443 add_folder (menu, state, data->group, NULL, data, data->new);
448 #endif /* USE_NNTP */
453 char buffer[_POSIX_PATH_MAX + STRING];
456 while (stat (d, &s) == -1) {
457 if (errno == ENOENT) {
458 /* The last used directory is deleted, try to use the parent dir. */
459 char *c = strrchr (d, '/');
470 if (!S_ISDIR (s.st_mode)) {
471 mutt_error (_("%s is not a directory."), d);
477 if ((dp = opendir (d)) == NULL) {
482 init_state (state, menu);
484 while ((de = readdir (dp)) != NULL) {
485 if (m_strcmp(de->d_name, ".") == 0)
486 continue; /* we don't need . */
488 if (m_strncmp(prefix, de->d_name, m_strlen(prefix)) != 0)
490 if (!((regexec (Mask.rx, de->d_name, 0, NULL, 0) == 0) ^ Mask.neg))
493 mutt_concat_path(buffer, sizeof(buffer), d, de->d_name);
494 if (lstat (buffer, &s) == -1)
497 if ((!S_ISREG (s.st_mode)) && (!S_ISDIR (s.st_mode)) &&
498 (!S_ISLNK (s.st_mode)))
501 i = buffy_lookup (buffer);
503 add_folder (menu, state, de->d_name, &s, NULL, i >= 0 ? Incoming.arr[i]->new : 0);
505 add_folder (menu, state, de->d_name, &s, i >= 0 ? Incoming.arr[i]->new : 0);
511 browser_sort (state);
515 /* get list of mailboxes/subscribed newsgroups */
516 static int examine_mailboxes (MUTTMENU * menu, struct browser_state *state)
519 char buffer[LONG_STRING];
522 if (option (OPTNEWS)) {
524 nntp_server_t *news = CurrentNewsSrv;
526 /* buffy_check (0); */
527 init_state (state, menu);
529 for (data = news->list; data; data = data->next) {
530 nntp_sync_sidebar (data);
531 if ((data->new || (data->subscribed &&
532 (!option (OPTSHOWONLYUNREAD)|| data->unread))))
533 add_folder (menu, state, data->group, NULL, data, data->new);
547 init_state (state, menu);
549 for (i = 0; i < Incoming.len; i++) {
550 tmp = Incoming.arr[i];
551 tmp->magic = mx_get_magic (tmp->path);
552 if (tmp->magic == M_IMAP) {
554 add_folder (menu, state, tmp->path, NULL, NULL, tmp->new);
556 add_folder (menu, state, tmp->path, NULL, tmp->new);
560 if (tmp->magic == M_POP) {
562 add_folder (menu, state, tmp->path, NULL, NULL, tmp->new);
564 add_folder (menu, state, tmp->path, NULL, tmp->new);
569 if (tmp->magic == M_NNTP) {
570 add_folder (menu, state, tmp->path, NULL, NULL, tmp->new);
574 if (lstat (tmp->path, &s) == -1)
577 if ((!S_ISREG (s.st_mode)) && (!S_ISDIR (s.st_mode)) &&
578 (!S_ISLNK (s.st_mode)))
581 m_strcpy(buffer, sizeof(buffer), NONULL(tmp->path));
582 mutt_pretty_mailbox (buffer);
585 add_folder (menu, state, buffer, &s, NULL, tmp->new);
587 add_folder (menu, state, buffer, &s, tmp->new);
591 browser_sort (state);
595 static int select_file_search (MUTTMENU * menu, regex_t * re, int n)
598 if (option (OPTNEWS))
600 (re, ((struct folder_file *) menu->data)[n].desc, 0, NULL, 0));
603 (re, ((struct folder_file *) menu->data)[n].name, 0, NULL, 0));
606 static void folder_entry (char *s, ssize_t slen, MUTTMENU * menu, int num)
610 folder.ff = &((struct folder_file *) menu->data)[num];
614 if (option (OPTNEWS))
615 m_strformat(s, slen, COLS - SW, GroupFormat, newsgroup_format_str,
619 m_strformat(s, slen, COLS - SW, FolderFormat, folder_format_str,
623 static void init_menu (struct browser_state *state, MUTTMENU * menu,
624 char *title, ssize_t titlelen, int buffy)
626 char path[_POSIX_PATH_MAX];
628 menu->max = state->entrylen;
630 if (menu->current >= menu->max)
631 menu->current = menu->max - 1;
632 if (menu->current < 0)
634 if (menu->top > menu->current)
640 if (option (OPTNEWS)) {
642 snprintf (title, titlelen, "%s", _("Subscribed newsgroups"));
644 snprintf (title, titlelen, _("Newsgroups on server [%s]"),
645 CurrentNewsSrv->conn->account.host);
650 snprintf(title, titlelen, _("Mailboxes [%d]"), buffy_check(0));
652 m_strcpy(path, sizeof(path), LastDir);
653 mutt_pretty_mailbox (path);
654 if (state->imap_browse && option (OPTIMAPLSUB))
655 snprintf (title, titlelen, _("Subscribed [%s], File mask: %s"),
656 path, NONULL (Mask.pattern));
658 snprintf (title, titlelen, _("Directory [%s], File mask: %s"),
659 path, NONULL (Mask.pattern));
661 menu->redraw = REDRAW_FULL;
664 static int file_tag (MUTTMENU * menu, int n, int m)
666 struct folder_file *ff = &(((struct folder_file *) menu->data)[n]);
669 if (S_ISDIR (ff->mode)
670 || (S_ISLNK (ff->mode) && link_is_dir (LastDir, ff->name))) {
671 mutt_error _("Can't attach a directory!");
677 ff->tagged = (m >= 0 ? m : !ff->tagged);
679 return ff->tagged - ot;
682 void mutt_select_file (char *f, ssize_t flen, int flags, char ***files,
685 char buf[_POSIX_PATH_MAX];
686 char prefix[_POSIX_PATH_MAX] = "";
688 struct browser_state state;
691 int i, killPrefix = 0;
692 int multiple = (flags & M_SEL_MULTI) ? 1 : 0;
693 int folder = (flags & M_SEL_FOLDER) ? 1 : 0;
694 int buffy = (flags & M_SEL_BUFFY) ? 1 : 0;
696 buffy = buffy && folder;
701 m_strcpy(LastDirBackup, sizeof(LastDirBackup), LastDir);
704 if (option (OPTNEWS)) {
706 m_strcpy(prefix, sizeof(prefix), f);
710 /* default state for news reader mode is browse subscribed newsgroups */
712 for (list = CurrentNewsSrv->list; list; list = list->next) {
713 if (list->subscribed) {
723 mutt_expand_path (f, flen);
724 if (imap_is_magic (f, NULL) == M_IMAP) {
725 init_state (&state, NULL);
726 state.imap_browse = 1;
727 if (!imap_browse (f, &state))
728 m_strcpy(LastDir, sizeof(LastDir), state.folder);
731 for (i = m_strlen(f) - 1; i > 0 && f[i] != '/'; i--);
734 i = MIN(ssizeof(LastDir) - 1, i);
735 m_strcpy(LastDir, sizeof(LastDir), f);
738 getcwd(LastDir, sizeof(LastDir));
739 m_strcat(LastDir, sizeof(LastDir), "/");
740 m_strncat(LastDir, sizeof(LastDir), f, i);
745 m_strcpy(LastDir, sizeof(LastDir), "/");
747 getcwd (LastDir, sizeof (LastDir));
750 if (i <= 0 && f[0] != '/')
751 m_strcpy(prefix, sizeof(prefix), f);
753 m_strcpy(prefix, sizeof(prefix), f + i + 1);
759 getcwd (LastDir, sizeof (LastDir));
760 else if (!LastDir[0])
761 m_strcpy(LastDir, sizeof(LastDir), NONULL(Maildir));
763 if (!buffy && imap_is_magic (LastDir, NULL) == M_IMAP) {
764 init_state (&state, NULL);
765 state.imap_browse = 1;
766 imap_browse (LastDir, &state);
767 browser_sort (&state);
774 if (examine_mailboxes (NULL, &state) == -1)
778 if (!state.imap_browse)
779 if (examine_directory (NULL, &state, LastDir, prefix) == -1)
782 menu = mutt_new_menu ();
783 menu->menu = MENU_FOLDER;
784 menu->make_entry = folder_entry;
785 menu->search = select_file_search;
787 menu->data = state.entry;
789 menu->tag = file_tag;
790 init_menu (&state, menu, title, sizeof (title), buffy);
793 switch (i = mutt_menuLoop (menu)) {
794 case OP_GENERIC_SELECT_ENTRY:
796 if (!state.entrylen) {
797 mutt_error _("No files match the file mask");
802 if (S_ISDIR (state.entry[menu->current].mode) ||
803 (S_ISLNK (state.entry[menu->current].mode) &&
804 link_is_dir (LastDir, state.entry[menu->current].name))
805 || state.entry[menu->current].inferiors
807 /* make sure this isn't a MH or maildir mailbox */
809 m_strcpy(buf, sizeof(buf), state.entry[menu->current].name);
810 mutt_expand_path (buf, sizeof (buf));
812 else if (state.imap_browse) {
813 m_strcpy(buf, sizeof(buf), state.entry[menu->current].name);
816 mutt_concat_path(buf, sizeof(buf), LastDir,
817 state.entry[menu->current].name);
819 if ((mx_get_magic (buf) <= 0)
820 || state.entry[menu->current].inferiors)
822 char OldLastDir[_POSIX_PATH_MAX];
824 /* save the old directory */
825 m_strcpy(OldLastDir, sizeof(OldLastDir), LastDir);
827 if (m_strcmp(state.entry[menu->current].name, "..") == 0) {
828 if (m_strcmp("..", LastDir + m_strlen(LastDir) - 2) == 0)
829 m_strcat(LastDir, sizeof(LastDir), "/..");
831 char *p = strrchr (LastDir + 1, '/');
836 if (LastDir[0] == '/')
839 m_strcat(LastDir, sizeof(LastDir), "/..");
844 m_strcpy(LastDir, sizeof(LastDir),
845 state.entry[menu->current].name);
846 mutt_expand_path (LastDir, sizeof (LastDir));
848 else if (state.imap_browse) {
852 m_strcpy(LastDir, sizeof(LastDir),
853 state.entry[menu->current].name);
854 /* tack on delimiter here */
855 n = m_strlen(LastDir) + 1;
857 /* special case "" needs no delimiter */
858 url_parse_ciss (&url, state.entry[menu->current].name);
860 (state.entry[menu->current].delim != '\0') &&
861 (n < ssizeof (LastDir))) {
863 LastDir[n - 1] = state.entry[menu->current].delim;
867 char tmp[_POSIX_PATH_MAX];
869 mutt_concat_path(tmp, sizeof(tmp), LastDir,
870 state.entry[menu->current].name);
871 m_strcpy(LastDir, sizeof(LastDir), tmp);
874 destroy_state (&state);
880 if (state.imap_browse) {
881 init_state (&state, NULL);
882 state.imap_browse = 1;
883 imap_browse (LastDir, &state);
884 browser_sort (&state);
885 menu->data = state.entry;
888 if (examine_directory (menu, &state, LastDir, prefix) == -1) {
889 /* try to restore the old values */
890 m_strcpy(LastDir, sizeof(LastDir), OldLastDir);
891 if (examine_directory (menu, &state, LastDir, prefix) == -1) {
892 m_strcpy(LastDir, sizeof(LastDir), NONULL(mod_core.homedir));
898 init_menu (&state, menu, title, sizeof (title), buffy);
904 if (buffy || option (OPTNEWS)) /* news have not path */
909 m_strcpy(f, flen, state.entry[menu->current].name);
910 mutt_expand_path (f, flen);
912 else if (state.imap_browse)
913 m_strcpy(f, flen, state.entry[menu->current].name);
915 mutt_concat_path(f, flen, LastDir, state.entry[menu->current].name);
917 /* Fall through to OP_EXIT */
927 *numfiles = menu->tagged;
928 tfiles = p_new(char *, *numfiles);
929 for (h = 0, j = 0; h < state.entrylen; i++) {
930 struct folder_file ff = state.entry[i];
931 char full[_POSIX_PATH_MAX];
934 mutt_concat_path(full, sizeof(full), LastDir, ff.name);
935 mutt_expand_path (full, sizeof (full));
936 tfiles[j++] = m_strdup(full);
941 else if (f[0]) { /* no tagged entries. return selected entry */
943 tfiles = p_new(char *, *numfiles);
944 mutt_expand_path (f, flen);
945 tfiles[0] = m_strdup(f);
950 destroy_state (&state);
951 mutt_menuDestroy (&menu);
954 case OP_BROWSER_TELL:
956 mutt_message ("%s", state.entry[menu->current].name);
959 case OP_BROWSER_TOGGLE_LSUB:
960 if (option (OPTIMAPLSUB)) {
961 unset_option (OPTIMAPLSUB);
964 set_option (OPTIMAPLSUB);
966 mutt_ungetch (0, OP_CHECK_NEW);
969 case OP_CREATE_MAILBOX:
970 if (!state.imap_browse)
971 mutt_error (_("Create is only supported for IMAP mailboxes"));
973 imap_mailbox_create (LastDir);
974 /* TODO: find a way to detect if the new folder would appear in
975 * this window, and insert it without starting over. */
976 destroy_state (&state);
977 init_state (&state, NULL);
978 state.imap_browse = 1;
979 imap_browse (LastDir, &state);
980 browser_sort (&state);
981 menu->data = state.entry;
984 init_menu (&state, menu, title, sizeof (title), buffy);
985 MAYBE_REDRAW (menu->redraw);
989 case OP_RENAME_MAILBOX:
990 if (!state.entry[menu->current].imap)
991 mutt_error (_("Rename is only supported for IMAP mailboxes"));
993 int nentry = menu->current;
995 if (imap_mailbox_rename (state.entry[nentry].name) >= 0) {
996 destroy_state (&state);
997 init_state (&state, NULL);
998 state.imap_browse = 1;
999 imap_browse (LastDir, &state);
1000 browser_sort (&state);
1001 menu->data = state.entry;
1004 init_menu (&state, menu, title, sizeof (title), buffy);
1005 MAYBE_REDRAW (menu->redraw);
1010 case OP_DELETE_MAILBOX:
1011 if (!state.entry[menu->current].imap)
1012 mutt_error (_("Delete is only supported for IMAP mailboxes"));
1016 int nentry = menu->current;
1018 imap_parse_path (state.entry[nentry].name, &mx);
1019 snprintf (msg, sizeof (msg), _("Really delete mailbox \"%s\"?"),
1021 if (mutt_yesorno (msg, M_NO) == M_YES) {
1022 if (!imap_delete_mailbox (Context, mx)) {
1023 /* free the mailbox from the browser */
1024 p_delete(&((state.entry)[nentry].name));
1025 p_delete(&((state.entry)[nentry].desc));
1026 /* and move all other entries up */
1027 if (nentry + 1 < state.entrylen)
1028 memmove (state.entry + nentry, state.entry + nentry + 1,
1029 sizeof (struct folder_file) * (state.entrylen -
1032 mutt_message _("Mailbox deleted.");
1034 init_menu (&state, menu, title, sizeof (title), buffy);
1035 MAYBE_REDRAW (menu->redraw);
1039 mutt_message _("Mailbox not deleted.");
1044 case OP_CHANGE_DIRECTORY:
1047 if (option (OPTNEWS))
1051 m_strcpy(buf, sizeof(buf), LastDir);
1052 if (!state.imap_browse)
1054 /* add '/' at the end of the directory name if not already there */
1055 ssize_t len = m_strlen(LastDir);
1057 if (len && LastDir[len - 1] != '/' && ssizeof(buf) > len)
1061 if (mutt_get_field (_("Chdir to: "), buf, sizeof (buf), M_FILE) == 0 &&
1064 mutt_expand_path (buf, sizeof (buf));
1065 if (imap_is_magic (buf, NULL) == M_IMAP) {
1066 m_strcpy(LastDir, sizeof(LastDir), buf);
1067 destroy_state (&state);
1068 init_state (&state, NULL);
1069 state.imap_browse = 1;
1070 imap_browse (LastDir, &state);
1071 browser_sort (&state);
1072 menu->data = state.entry;
1075 init_menu (&state, menu, title, sizeof (title), buffy);
1078 if (stat (buf, &st) == 0) {
1079 if (S_ISDIR (st.st_mode)) {
1080 destroy_state (&state);
1081 if (examine_directory (menu, &state, buf, prefix) == 0)
1082 m_strcpy(LastDir, sizeof(LastDir), buf);
1084 mutt_error _("Error scanning directory.");
1086 if (examine_directory (menu, &state, LastDir, prefix) == -1) {
1087 mutt_menuDestroy (&menu);
1093 init_menu (&state, menu, title, sizeof (title), buffy);
1096 mutt_error (_("%s is not a directory."), buf);
1101 MAYBE_REDRAW (menu->redraw);
1106 m_strcpy(buf, sizeof(buf), NONULL(Mask.pattern));
1107 if (mutt_get_field (_("File Mask: "), buf, sizeof (buf), 0) == 0) {
1108 regex_t *rx = p_new(regex_t, 1);
1113 /* assume that the user wants to see everything */
1115 m_strcpy(buf, sizeof(buf), ".");
1118 s = vskipspaces(s + 1);
1122 if ((err = REGCOMP (rx, s, REG_NOSUB)) != 0) {
1123 regerror (err, rx, buf, sizeof (buf));
1126 mutt_error ("%s", buf);
1129 m_strreplace(&Mask.pattern, buf);
1135 destroy_state (&state);
1136 if (state.imap_browse) {
1137 init_state (&state, NULL);
1138 state.imap_browse = 1;
1139 imap_browse (LastDir, &state);
1140 browser_sort (&state);
1141 menu->data = state.entry;
1142 init_menu (&state, menu, title, sizeof (title), buffy);
1145 if (examine_directory (menu, &state, LastDir, NULL) == 0)
1146 init_menu (&state, menu, title, sizeof (title), buffy);
1148 mutt_error _("Error scanning directory.");
1150 mutt_menuDestroy (&menu);
1154 if (!state.entrylen) {
1155 mutt_error _("No files match the file mask");
1161 MAYBE_REDRAW (menu->redraw);
1165 case OP_SORT_REVERSE:
1169 int reverse = (i == OP_SORT_REVERSE);
1171 switch (mutt_multi_choice ((reverse) ?
1173 ("Reverse sort by (d)ate, (a)lpha, si(z)e or do(n)'t sort? ")
1176 ("Sort by (d)ate, (a)lpha, si(z)e or do(n)'t sort? "),
1178 case -1: /* abort */
1182 case 1: /* (d)ate */
1183 BrowserSort = SORT_DATE;
1186 case 2: /* (a)lpha */
1187 BrowserSort = SORT_SUBJECT;
1190 case 3: /* si(z)e */
1191 BrowserSort = SORT_SIZE;
1194 case 4: /* do(n)'t sort */
1195 BrowserSort = SORT_ORDER;
1200 BrowserSort |= reverse ? SORT_REVERSE : 0;
1201 browser_sort (&state);
1202 menu->redraw = REDRAW_FULL;
1207 case OP_TOGGLE_MAILBOXES:
1211 destroy_state (&state);
1216 if (examine_mailboxes (menu, &state) == -1)
1219 else if (imap_is_magic (LastDir, NULL) == M_IMAP) {
1220 init_state (&state, NULL);
1221 state.imap_browse = 1;
1222 imap_browse (LastDir, &state);
1223 browser_sort (&state);
1224 menu->data = state.entry;
1226 else if (examine_directory (menu, &state, LastDir, prefix) == -1)
1228 init_menu (&state, menu, title, sizeof (title), buffy);
1232 if (option (OPTFORCEBUFFYCHECK))
1237 case OP_BROWSER_NEW_FILE:
1239 snprintf (buf, sizeof (buf), "%s/", LastDir);
1240 if (mutt_get_field (_("New file name: "), buf, sizeof (buf), M_FILE) ==
1242 m_strcpy(f, flen, buf);
1243 destroy_state (&state);
1244 mutt_menuDestroy (&menu);
1247 MAYBE_REDRAW (menu->redraw);
1250 case OP_BROWSER_VIEW_FILE:
1251 if (!state.entrylen) {
1252 mutt_error _("No files match the file mask");
1257 if (state.entry[menu->current].selectable) {
1258 m_strcpy(f, flen, state.entry[menu->current].name);
1259 destroy_state (&state);
1260 mutt_menuDestroy (&menu);
1264 if (S_ISDIR (state.entry[menu->current].mode) ||
1265 (S_ISLNK (state.entry[menu->current].mode) &&
1266 link_is_dir (LastDir, state.entry[menu->current].name))) {
1267 mutt_error _("Can't view a directory");
1273 char nbuf[_POSIX_PATH_MAX];
1275 mutt_concat_path(nbuf, sizeof(nbuf), LastDir,
1276 state.entry[menu->current].name);
1277 b = mutt_make_file_attach (nbuf);
1279 mutt_view_attachment (NULL, b, M_REGULAR, NULL, NULL, 0);
1281 menu->redraw = REDRAW_FULL;
1284 mutt_error _("Error trying to view file");
1291 if (option (OPTNEWS)) {
1292 struct folder_file *folder_f = &state.entry[menu->current];
1295 if (i == OP_CATCHUP)
1296 nd = mutt_newsgroup_catchup (CurrentNewsSrv, folder_f->name);
1298 nd = mutt_newsgroup_uncatchup (CurrentNewsSrv, folder_f->name);
1301 if (menu->current + 1 < menu->max)
1303 menu->redraw = REDRAW_MOTION_RESYNCH;
1308 case OP_LOAD_ACTIVE:
1309 if (!option (OPTNEWS))
1315 for (data = CurrentNewsSrv->list; data; data = data->next) {
1319 nntp_get_active (CurrentNewsSrv);
1321 destroy_state (&state);
1323 examine_mailboxes (menu, &state);
1325 examine_directory (menu, &state, NULL, NULL);
1326 init_menu (&state, menu, title, sizeof (title), buffy);
1328 #endif /* USE_NNTP */
1330 case OP_BROWSER_SUBSCRIBE:
1331 case OP_BROWSER_UNSUBSCRIBE:
1333 case OP_SUBSCRIBE_PATTERN:
1334 case OP_UNSUBSCRIBE_PATTERN:
1335 if (option (OPTNEWS)) {
1336 regex_t *rx = p_new(regex_t, 1);
1338 int j = menu->current;
1340 nntp_server_t *news = CurrentNewsSrv;
1342 if (i == OP_SUBSCRIBE_PATTERN || i == OP_UNSUBSCRIBE_PATTERN) {
1347 if (i == OP_SUBSCRIBE_PATTERN)
1348 snprintf (tmp, sizeof (tmp), _("Subscribe pattern: "));
1350 snprintf (tmp, sizeof (tmp), _("Unsubscribe pattern: "));
1351 if (mutt_get_field (tmp, buf, sizeof (buf), 0) != 0 || !buf[0]) {
1356 if ((err = REGCOMP (rx, s, REG_NOSUB)) != 0) {
1357 regerror (err, rx, buf, sizeof (buf));
1360 mutt_error ("%s", buf);
1363 menu->redraw = REDRAW_FULL;
1366 else if (!state.entrylen) {
1367 mutt_error _("No newsgroups match the mask");
1372 for (; j < state.entrylen; j++) {
1373 struct folder_file *folderf = &state.entry[j];
1375 if (i == OP_BROWSER_SUBSCRIBE || i == OP_BROWSER_UNSUBSCRIBE ||
1376 regexec (rx, folderf->name, 0, NULL, 0) == 0) {
1377 if (i == OP_BROWSER_SUBSCRIBE || i == OP_SUBSCRIBE_PATTERN)
1378 nd = mutt_newsgroup_subscribe (news, folderf->name);
1380 nd = mutt_newsgroup_unsubscribe (news, folderf->name);
1382 if (i == OP_BROWSER_SUBSCRIBE || i == OP_BROWSER_UNSUBSCRIBE) {
1383 if (menu->current + 1 < menu->max)
1385 menu->redraw = REDRAW_MOTION_RESYNCH;
1389 if (i == OP_SUBSCRIBE_PATTERN) {
1390 nd = news ? news->list : NULL;
1391 for (; nd; nd = nd->next) {
1392 if (nd->group && !nd->subscribed) {
1393 if (regexec (rx, nd->group, 0, NULL, 0) == 0) {
1394 mutt_newsgroup_subscribe (news, nd->group);
1395 add_folder (menu, &state, nd->group, NULL, nd, nd->new);
1399 init_menu (&state, menu, title, sizeof (title), buffy);
1401 mutt_newsrc_update (news);
1402 if (i != OP_BROWSER_SUBSCRIBE && i != OP_BROWSER_UNSUBSCRIBE)
1407 #endif /* USE_NNTP */
1409 if (i == OP_BROWSER_SUBSCRIBE)
1410 imap_subscribe (state.entry[menu->current].name, 1);
1412 imap_subscribe (state.entry[menu->current].name, 0);
1420 m_strcpy(LastDir, sizeof(LastDir), LastDirBackup);