NeoMutt  2019-12-07-168-gc45f47
Teaching an old dog new tricks
DOXYGEN
browser.c
Go to the documentation of this file.
1 
29 #include "config.h"
30 #include <dirent.h>
31 #include <errno.h>
32 #include <grp.h>
33 #include <limits.h>
34 #include <locale.h>
35 #include <pwd.h>
36 #include <regex.h>
37 #include <stdbool.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <sys/stat.h>
42 #include "mutt/lib.h"
43 #include "config/lib.h"
44 #include "email/lib.h"
45 #include "core/lib.h"
46 #include "conn/lib.h"
47 #include "gui/lib.h"
48 #include "mutt.h"
49 #include "browser.h"
50 #include "context.h"
51 #include "format_flags.h"
52 #include "globals.h"
53 #include "keymap.h"
54 #include "mutt_attach.h"
55 #include "mutt_mailbox.h"
56 #include "mutt_menu.h"
57 #include "muttlib.h"
58 #include "mx.h"
59 #include "opcodes.h"
60 #include "options.h"
61 #include "sendlib.h"
62 #ifdef USE_IMAP
63 #include "imap/lib.h"
64 #endif
65 #ifdef USE_NNTP
66 #include "nntp/lib.h"
67 #endif
68 
69 /* These Config Variables are only used in browser.c */
77 
78 static const struct Mapping FolderHelp[] = {
79  { N_("Exit"), OP_EXIT },
80  { N_("Chdir"), OP_CHANGE_DIRECTORY },
81  { N_("Goto"), OP_BROWSER_GOTO_FOLDER },
82  { N_("Mask"), OP_ENTER_MASK },
83  { N_("Help"), OP_HELP },
84  { NULL, 0 },
85 };
86 
87 #ifdef USE_NNTP
88 static struct Mapping FolderNewsHelp[] = {
89  { N_("Exit"), OP_EXIT },
90  { N_("List"), OP_TOGGLE_MAILBOXES },
91  { N_("Subscribe"), OP_BROWSER_SUBSCRIBE },
92  { N_("Unsubscribe"), OP_BROWSER_UNSUBSCRIBE },
93  { N_("Catchup"), OP_CATCHUP },
94  { N_("Mask"), OP_ENTER_MASK },
95  { N_("Help"), OP_HELP },
96  { NULL, 0 },
97 };
98 #endif
99 
100 static struct Buffer LastDir = { 0 };
101 static struct Buffer LastDirBackup = { 0 };
102 
108 static void init_lastdir(void)
109 {
110  static bool done = false;
111  if (!done)
112  {
113  mutt_buffer_alloc(&LastDir, PATH_MAX);
114  mutt_buffer_alloc(&LastDirBackup, PATH_MAX);
115  done = true;
116  }
117 }
118 
123 {
124  mutt_buffer_dealloc(&LastDir);
125  mutt_buffer_dealloc(&LastDirBackup);
126 }
127 
134 static void destroy_state(struct BrowserState *state)
135 {
136  for (size_t c = 0; c < state->entrylen; c++)
137  {
138  FREE(&((state->entry)[c].name));
139  FREE(&((state->entry)[c].desc));
140  }
141 #ifdef USE_IMAP
142  FREE(&state->folder);
143 #endif
144  FREE(&state->entry);
145 }
146 
155 static int browser_compare_subject(const void *a, const void *b)
156 {
157  const struct FolderFile *pa = (const struct FolderFile *) a;
158  const struct FolderFile *pb = (const struct FolderFile *) b;
159 
160  /* inbox should be sorted ahead of its siblings */
161  int r = mutt_inbox_cmp(pa->name, pb->name);
162  if (r == 0)
163  r = mutt_str_strcoll(pa->name, pb->name);
164  return (C_SortBrowser & SORT_REVERSE) ? -r : r;
165 }
166 
175 static int browser_compare_desc(const void *a, const void *b)
176 {
177  const struct FolderFile *pa = (const struct FolderFile *) a;
178  const struct FolderFile *pb = (const struct FolderFile *) b;
179 
180  int r = mutt_str_strcoll(pa->desc, pb->desc);
181 
182  return (C_SortBrowser & SORT_REVERSE) ? -r : r;
183 }
184 
193 static int browser_compare_date(const void *a, const void *b)
194 {
195  const struct FolderFile *pa = (const struct FolderFile *) a;
196  const struct FolderFile *pb = (const struct FolderFile *) b;
197 
198  int r = pa->mtime - pb->mtime;
199 
200  return (C_SortBrowser & SORT_REVERSE) ? -r : r;
201 }
202 
211 static int browser_compare_size(const void *a, const void *b)
212 {
213  const struct FolderFile *pa = (const struct FolderFile *) a;
214  const struct FolderFile *pb = (const struct FolderFile *) b;
215 
216  int r = pa->size - pb->size;
217 
218  return (C_SortBrowser & SORT_REVERSE) ? -r : r;
219 }
220 
229 static int browser_compare_count(const void *a, const void *b)
230 {
231  const struct FolderFile *pa = (const struct FolderFile *) a;
232  const struct FolderFile *pb = (const struct FolderFile *) b;
233 
234  int r = 0;
235  if (pa->has_mailbox && pb->has_mailbox)
236  r = pa->msg_count - pb->msg_count;
237  else if (pa->has_mailbox)
238  r = -1;
239  else
240  r = 1;
241 
242  return (C_SortBrowser & SORT_REVERSE) ? -r : r;
243 }
244 
253 static int browser_compare_count_new(const void *a, const void *b)
254 {
255  const struct FolderFile *pa = (const struct FolderFile *) a;
256  const struct FolderFile *pb = (const struct FolderFile *) b;
257 
258  int r = 0;
259  if (pa->has_mailbox && pb->has_mailbox)
260  r = pa->msg_unread - pb->msg_unread;
261  else if (pa->has_mailbox)
262  r = -1;
263  else
264  r = 1;
265 
266  return (C_SortBrowser & SORT_REVERSE) ? -r : r;
267 }
268 
281 static int browser_compare(const void *a, const void *b)
282 {
283  const struct FolderFile *pa = (const struct FolderFile *) a;
284  const struct FolderFile *pb = (const struct FolderFile *) b;
285 
286  if ((mutt_str_strcoll(pa->desc, "../") == 0) || (mutt_str_strcoll(pa->desc, "..") == 0))
287  return -1;
288  if ((mutt_str_strcoll(pb->desc, "../") == 0) || (mutt_str_strcoll(pb->desc, "..") == 0))
289  return 1;
290 
291  switch (C_SortBrowser & SORT_MASK)
292  {
293  case SORT_COUNT:
294  return browser_compare_count(a, b);
295  case SORT_DATE:
296  return browser_compare_date(a, b);
297  case SORT_DESC:
298  return browser_compare_desc(a, b);
299  case SORT_SIZE:
300  return browser_compare_size(a, b);
301  case SORT_UNREAD:
302  return browser_compare_count_new(a, b);
303  case SORT_SUBJECT:
304  default:
305  return browser_compare_subject(a, b);
306  }
307 }
308 
316 static void browser_sort(struct BrowserState *state)
317 {
318  switch (C_SortBrowser & SORT_MASK)
319  {
320  /* Also called "I don't care"-sort-method. */
321  case SORT_ORDER:
322  return;
323 #ifdef USE_NNTP
324  case SORT_SIZE:
325  case SORT_DATE:
326  if (OptNews)
327  return;
328 #endif
329  default:
330  break;
331  }
332 
333  qsort(state->entry, state->entrylen, sizeof(struct FolderFile), browser_compare);
334 }
335 
343 static bool link_is_dir(const char *folder, const char *path)
344 {
345  struct stat st;
346  bool retval = false;
347 
348  struct Buffer *fullpath = mutt_buffer_pool_get();
349  mutt_buffer_concat_path(fullpath, folder, path);
350 
351  if (stat(mutt_b2s(fullpath), &st) == 0)
352  retval = S_ISDIR(st.st_mode);
353 
354  mutt_buffer_pool_release(&fullpath);
355 
356  return retval;
357 }
358 
379 static const char *folder_format_str(char *buf, size_t buflen, size_t col, int cols,
380  char op, const char *src, const char *prec,
381  const char *if_str, const char *else_str,
382  unsigned long data, MuttFormatFlags flags)
383 {
384  char fn[128], fmt[128];
385  struct Folder *folder = (struct Folder *) data;
386  bool optional = (flags & MUTT_FORMAT_OPTIONAL);
387 
388  switch (op)
389  {
390  case 'C':
391  snprintf(fmt, sizeof(fmt), "%%%sd", prec);
392  snprintf(buf, buflen, fmt, folder->num + 1);
393  break;
394 
395  case 'd':
396  case 'D':
397  if (folder->ff->local)
398  {
399  bool do_locales = true;
400 
401  char *t_fmt = NULL;
402  if (op == 'D')
403  {
404  t_fmt = NONULL(C_DateFormat);
405  if (*t_fmt == '!')
406  {
407  t_fmt++;
408  do_locales = false;
409  }
410  }
411  else
412  {
413  static const time_t one_year = 31536000;
414  t_fmt = ((mutt_date_epoch() - folder->ff->mtime) < one_year) ?
415  "%b %d %H:%M" :
416  "%b %d %Y";
417  }
418 
419  if (!do_locales)
420  setlocale(LC_TIME, "C");
421  char date[128];
422  mutt_date_localtime_format(date, sizeof(date), t_fmt, folder->ff->mtime);
423  if (!do_locales)
424  setlocale(LC_TIME, "");
425 
426  mutt_format_s(buf, buflen, prec, date);
427  }
428  else
429  mutt_format_s(buf, buflen, prec, "");
430  break;
431 
432  case 'f':
433  {
434  char *s = NULL;
435 
436  s = NONULL(folder->ff->name);
437 
438  snprintf(fn, sizeof(fn), "%s%s", s,
439  folder->ff->local ?
440  (S_ISLNK(folder->ff->mode) ?
441  "@" :
442  (S_ISDIR(folder->ff->mode) ?
443  "/" :
444  (((folder->ff->mode & S_IXUSR) != 0) ? "*" : ""))) :
445  "");
446 
447  mutt_format_s(buf, buflen, prec, fn);
448  break;
449  }
450  case 'F':
451  {
452  if (folder->ff->local)
453  {
454  char permission[11];
455  snprintf(permission, sizeof(permission), "%c%c%c%c%c%c%c%c%c%c",
456  S_ISDIR(folder->ff->mode) ? 'd' : (S_ISLNK(folder->ff->mode) ? 'l' : '-'),
457  ((folder->ff->mode & S_IRUSR) != 0) ? 'r' : '-',
458  ((folder->ff->mode & S_IWUSR) != 0) ? 'w' : '-',
459  ((folder->ff->mode & S_ISUID) != 0) ?
460  's' :
461  ((folder->ff->mode & S_IXUSR) != 0) ? 'x' : '-',
462  ((folder->ff->mode & S_IRGRP) != 0) ? 'r' : '-',
463  ((folder->ff->mode & S_IWGRP) != 0) ? 'w' : '-',
464  ((folder->ff->mode & S_ISGID) != 0) ?
465  's' :
466  ((folder->ff->mode & S_IXGRP) != 0) ? 'x' : '-',
467  ((folder->ff->mode & S_IROTH) != 0) ? 'r' : '-',
468  ((folder->ff->mode & S_IWOTH) != 0) ? 'w' : '-',
469  ((folder->ff->mode & S_ISVTX) != 0) ?
470  't' :
471  ((folder->ff->mode & S_IXOTH) != 0) ? 'x' : '-');
472  mutt_format_s(buf, buflen, prec, permission);
473  }
474 #ifdef USE_IMAP
475  else if (folder->ff->imap)
476  {
477  char permission[11];
478  /* mark folders with subfolders AND mail */
479  snprintf(permission, sizeof(permission), "IMAP %c",
480  (folder->ff->inferiors && folder->ff->selectable) ? '+' : ' ');
481  mutt_format_s(buf, buflen, prec, permission);
482  }
483 #endif
484  else
485  mutt_format_s(buf, buflen, prec, "");
486  break;
487  }
488 
489  case 'g':
490  if (folder->ff->local)
491  {
492  struct group *gr = getgrgid(folder->ff->gid);
493  if (gr)
494  mutt_format_s(buf, buflen, prec, gr->gr_name);
495  else
496  {
497  snprintf(fmt, sizeof(fmt), "%%%sld", prec);
498  snprintf(buf, buflen, fmt, folder->ff->gid);
499  }
500  }
501  else
502  mutt_format_s(buf, buflen, prec, "");
503  break;
504 
505  case 'i':
506  {
507  char *s = NULL;
508  if (folder->ff->desc)
509  s = folder->ff->desc;
510  else
511  s = folder->ff->name;
512 
513  snprintf(fn, sizeof(fn), "%s%s", s,
514  folder->ff->local ?
515  (S_ISLNK(folder->ff->mode) ?
516  "@" :
517  (S_ISDIR(folder->ff->mode) ?
518  "/" :
519  (((folder->ff->mode & S_IXUSR) != 0) ? "*" : ""))) :
520  "");
521 
522  mutt_format_s(buf, buflen, prec, fn);
523  break;
524  }
525 
526  case 'l':
527  if (folder->ff->local)
528  {
529  snprintf(fmt, sizeof(fmt), "%%%sd", prec);
530  snprintf(buf, buflen, fmt, folder->ff->nlink);
531  }
532  else
533  mutt_format_s(buf, buflen, prec, "");
534  break;
535 
536  case 'm':
537  if (!optional)
538  {
539  if (folder->ff->has_mailbox)
540  {
541  snprintf(fmt, sizeof(fmt), "%%%sd", prec);
542  snprintf(buf, buflen, fmt, folder->ff->msg_count);
543  }
544  else
545  mutt_format_s(buf, buflen, prec, "");
546  }
547  else if (folder->ff->msg_count == 0)
548  optional = false;
549  break;
550 
551  case 'N':
552  snprintf(fmt, sizeof(fmt), "%%%sc", prec);
553  snprintf(buf, buflen, fmt, folder->ff->has_new_mail ? 'N' : ' ');
554  break;
555 
556  case 'n':
557  if (!optional)
558  {
559  if (folder->ff->has_mailbox)
560  {
561  snprintf(fmt, sizeof(fmt), "%%%sd", prec);
562  snprintf(buf, buflen, fmt, folder->ff->msg_unread);
563  }
564  else
565  mutt_format_s(buf, buflen, prec, "");
566  }
567  else if (folder->ff->msg_unread == 0)
568  optional = false;
569  break;
570 
571  case 's':
572  if (folder->ff->local)
573  {
574  mutt_str_pretty_size(fn, sizeof(fn), folder->ff->size);
575  snprintf(fmt, sizeof(fmt), "%%%ss", prec);
576  snprintf(buf, buflen, fmt, fn);
577  }
578  else
579  mutt_format_s(buf, buflen, prec, "");
580  break;
581 
582  case 't':
583  snprintf(fmt, sizeof(fmt), "%%%sc", prec);
584  snprintf(buf, buflen, fmt, folder->ff->tagged ? '*' : ' ');
585  break;
586 
587  case 'u':
588  if (folder->ff->local)
589  {
590  struct passwd *pw = getpwuid(folder->ff->uid);
591  if (pw)
592  mutt_format_s(buf, buflen, prec, pw->pw_name);
593  else
594  {
595  snprintf(fmt, sizeof(fmt), "%%%sld", prec);
596  snprintf(buf, buflen, fmt, folder->ff->uid);
597  }
598  }
599  else
600  mutt_format_s(buf, buflen, prec, "");
601  break;
602 
603  default:
604  snprintf(fmt, sizeof(fmt), "%%%sc", prec);
605  snprintf(buf, buflen, fmt, op);
606  break;
607  }
608 
609  if (optional)
610  {
611  mutt_expando_format(buf, buflen, col, cols, if_str, folder_format_str, data,
613  }
614  else if (flags & MUTT_FORMAT_OPTIONAL)
615  {
616  mutt_expando_format(buf, buflen, col, cols, else_str, folder_format_str,
617  data, MUTT_FORMAT_NO_FLAGS);
618  }
619 
620  return src;
621 }
622 
633 static void add_folder(struct Menu *menu, struct BrowserState *state,
634  const char *name, const char *desc, const struct stat *s,
635  struct Mailbox *m, void *data)
636 {
637  if (m && (m->flags & MB_HIDDEN))
638  {
639  return;
640  }
641 
642  if (state->entrylen == state->entrymax)
643  {
644  /* need to allocate more space */
645  mutt_mem_realloc(&state->entry, sizeof(struct FolderFile) * (state->entrymax += 256));
646  memset(&state->entry[state->entrylen], 0, sizeof(struct FolderFile) * 256);
647  if (menu)
648  menu->data = state->entry;
649  }
650 
651  if (s)
652  {
653  (state->entry)[state->entrylen].mode = s->st_mode;
654  (state->entry)[state->entrylen].mtime = s->st_mtime;
655  (state->entry)[state->entrylen].size = s->st_size;
656  (state->entry)[state->entrylen].gid = s->st_gid;
657  (state->entry)[state->entrylen].uid = s->st_uid;
658  (state->entry)[state->entrylen].nlink = s->st_nlink;
659 
660  (state->entry)[state->entrylen].local = true;
661  }
662  else
663  (state->entry)[state->entrylen].local = false;
664 
665  if (m)
666  {
667  (state->entry)[state->entrylen].has_mailbox = true;
668  (state->entry)[state->entrylen].has_new_mail = m->has_new;
669  (state->entry)[state->entrylen].msg_count = m->msg_count;
670  (state->entry)[state->entrylen].msg_unread = m->msg_unread;
671  }
672 
673  (state->entry)[state->entrylen].name = mutt_str_strdup(name);
674  (state->entry)[state->entrylen].desc = mutt_str_strdup(desc ? desc : name);
675 #ifdef USE_IMAP
676  (state->entry)[state->entrylen].imap = false;
677 #endif
678 #ifdef USE_NNTP
679  if (OptNews)
680  (state->entry)[state->entrylen].nd = data;
681 #endif
682  (state->entrylen)++;
683 }
684 
690 static void init_state(struct BrowserState *state, struct Menu *menu)
691 {
692  state->entrylen = 0;
693  state->entrymax = 256;
694  state->entry = mutt_mem_calloc(state->entrymax, sizeof(struct FolderFile));
695 #ifdef USE_IMAP
696  state->imap_browse = false;
697 #endif
698  if (menu)
699  menu->data = state->entry;
700 }
701 
711 static int examine_directory(struct Menu *menu, struct BrowserState *state,
712  const char *d, const char *prefix)
713 {
714  int rc = -1;
715  struct Buffer *buf = mutt_buffer_pool_get();
716 #ifdef USE_NNTP
717  if (OptNews)
718  {
720 
721  init_state(state, menu);
722 
723  for (unsigned int i = 0; i < adata->groups_num; i++)
724  {
725  struct NntpMboxData *mdata = adata->groups_list[i];
726  if (!mdata)
727  continue;
728  if (prefix && *prefix && !mutt_str_startswith(mdata->group, prefix, CASE_MATCH))
729  continue;
730  if (!mutt_regex_match(C_Mask, mdata->group))
731  {
732  continue;
733  }
734  add_folder(menu, state, mdata->group, NULL, NULL, NULL, mdata);
735  }
736  }
737  else
738 #endif /* USE_NNTP */
739  {
740  struct stat s;
741  DIR *dp = NULL;
742  struct dirent *de = NULL;
743 
744  while (stat(d, &s) == -1)
745  {
746  if (errno == ENOENT)
747  {
748  /* The last used directory is deleted, try to use the parent dir. */
749  char *c = strrchr(d, '/');
750 
751  if (c && (c > d))
752  {
753  *c = '\0';
754  continue;
755  }
756  }
757  mutt_perror(d);
758  goto ed_out;
759  }
760 
761  if (!S_ISDIR(s.st_mode))
762  {
763  mutt_error(_("%s is not a directory"), d);
764  goto ed_out;
765  }
766 
767  if (Context && Context->mailbox)
769 
770  dp = opendir(d);
771  if (!dp)
772  {
773  mutt_perror(d);
774  goto ed_out;
775  }
776 
777  init_state(state, menu);
778 
779  struct MailboxList ml = neomutt_mailboxlist_get_all(NeoMutt, MUTT_MAILBOX_ANY);
780  while ((de = readdir(dp)))
781  {
782  if (mutt_str_strcmp(de->d_name, ".") == 0)
783  continue; /* we don't need . */
784 
785  if (prefix && *prefix && !mutt_str_startswith(de->d_name, prefix, CASE_MATCH))
786  {
787  continue;
788  }
789  if (!mutt_regex_match(C_Mask, de->d_name))
790  {
791  continue;
792  }
793 
794  mutt_buffer_concat_path(buf, d, de->d_name);
795  if (lstat(mutt_b2s(buf), &s) == -1)
796  continue;
797 
798  /* No size for directories or symlinks */
799  if (S_ISDIR(s.st_mode) || S_ISLNK(s.st_mode))
800  s.st_size = 0;
801  else if (!S_ISREG(s.st_mode))
802  continue;
803 
804  struct MailboxNode *np = NULL;
805  STAILQ_FOREACH(np, &ml, entries)
806  {
807  if (mutt_str_strcmp(mutt_b2s(buf), mailbox_path(np->mailbox)) == 0)
808  break;
809  }
810 
811  if (np && Context && Context->mailbox &&
813  {
816  }
817  add_folder(menu, state, de->d_name, NULL, &s, np ? np->mailbox : NULL, NULL);
818  }
820  closedir(dp);
821  }
822  browser_sort(state);
823  rc = 0;
824 ed_out:
826  return rc;
827 }
828 
836 static int examine_mailboxes(struct Menu *menu, struct BrowserState *state)
837 {
838  struct stat s;
839  struct Buffer *md = NULL;
840  struct Buffer *mailbox = NULL;
841 
842 #ifdef USE_NNTP
843  if (OptNews)
844  {
846 
847  init_state(state, menu);
848 
849  for (unsigned int i = 0; i < adata->groups_num; i++)
850  {
851  struct NntpMboxData *mdata = adata->groups_list[i];
852  if (mdata && (mdata->has_new_mail ||
853  (mdata->subscribed && (mdata->unread || !C_ShowOnlyUnread))))
854  {
855  add_folder(menu, state, mdata->group, NULL, NULL, NULL, mdata);
856  }
857  }
858  }
859  else
860 #endif
861  {
862  init_state(state, menu);
863 
865  return -1;
866  mailbox = mutt_buffer_pool_get();
867  md = mutt_buffer_pool_get();
869 
870  struct MailboxList ml = neomutt_mailboxlist_get_all(NeoMutt, MUTT_MAILBOX_ANY);
871  struct MailboxNode *np = NULL;
872  STAILQ_FOREACH(np, &ml, entries)
873  {
874  if (!np->mailbox)
875  continue;
876 
877  if (Context && Context->mailbox &&
879  {
882  }
883 
884  mutt_buffer_strcpy(mailbox, mailbox_path(np->mailbox));
887 
888  switch (np->mailbox->magic)
889  {
890  case MUTT_IMAP:
891  case MUTT_POP:
892  add_folder(menu, state, mutt_b2s(mailbox), np->mailbox->name, NULL,
893  np->mailbox, NULL);
894  continue;
895  case MUTT_NOTMUCH:
896  case MUTT_NNTP:
897  add_folder(menu, state, mailbox_path(np->mailbox), np->mailbox->name,
898  NULL, np->mailbox, NULL);
899  continue;
900  default: /* Continue */
901  break;
902  }
903 
904  if (lstat(mailbox_path(np->mailbox), &s) == -1)
905  continue;
906 
907  if ((!S_ISREG(s.st_mode)) && (!S_ISDIR(s.st_mode)) && (!S_ISLNK(s.st_mode)))
908  continue;
909 
910  if (np->mailbox->magic == MUTT_MAILDIR)
911  {
912  struct stat st2;
913 
914  mutt_buffer_printf(md, "%s/new", mailbox_path(np->mailbox));
915  if (stat(mutt_b2s(md), &s) < 0)
916  s.st_mtime = 0;
917  mutt_buffer_printf(md, "%s/cur", mailbox_path(np->mailbox));
918  if (stat(mutt_b2s(md), &st2) < 0)
919  st2.st_mtime = 0;
920  if (st2.st_mtime > s.st_mtime)
921  s.st_mtime = st2.st_mtime;
922  }
923 
924  add_folder(menu, state, mutt_b2s(mailbox), np->mailbox->name, &s, np->mailbox, NULL);
925  }
927  }
928  browser_sort(state);
929 
930  mutt_buffer_pool_release(&mailbox);
932  return 0;
933 }
934 
938 static int select_file_search(struct Menu *menu, regex_t *rx, int line)
939 {
940 #ifdef USE_NNTP
941  if (OptNews)
942  return regexec(rx, ((struct FolderFile *) menu->data)[line].desc, 0, NULL, 0);
943 #endif
944  struct FolderFile current_ff = ((struct FolderFile *) menu->data)[line];
945  char *search_on = current_ff.desc ? current_ff.desc : current_ff.name;
946 
947  return regexec(rx, search_on, 0, NULL, 0);
948 }
949 
953 static void folder_make_entry(char *buf, size_t buflen, struct Menu *menu, int line)
954 {
955  struct Folder folder;
956 
957  folder.ff = &((struct FolderFile *) menu->data)[line];
958  folder.num = line;
959 
960 #ifdef USE_NNTP
961  if (OptNews)
962  {
963  mutt_expando_format(buf, buflen, 0, menu->win_index->state.cols,
965  (unsigned long) &folder, MUTT_FORMAT_ARROWCURSOR);
966  }
967  else
968 #endif
969  {
970  mutt_expando_format(buf, buflen, 0, menu->win_index->state.cols,
972  (unsigned long) &folder, MUTT_FORMAT_ARROWCURSOR);
973  }
974 }
975 
984 static void browser_highlight_default(struct BrowserState *state, struct Menu *menu)
985 {
986  menu->top = 0;
987  /* Reset menu position to 1.
988  * We do not risk overflow as the init_menu function changes
989  * current if it is bigger than state->entrylen. */
990  if ((mutt_str_strcmp(state->entry[0].desc, "..") == 0) ||
991  (mutt_str_strcmp(state->entry[0].desc, "../") == 0))
992  {
993  /* Skip the first entry, unless there's only one entry. */
994  menu->current = (menu->max > 1);
995  }
996  else
997  {
998  menu->current = 0;
999  }
1000 }
1001 
1010 static void init_menu(struct BrowserState *state, struct Menu *menu,
1011  char *title, size_t titlelen, bool mailbox)
1012 {
1013  menu->max = state->entrylen;
1014 
1015  if (menu->current >= menu->max)
1016  menu->current = menu->max - 1;
1017  if (menu->current < 0)
1018  menu->current = 0;
1019  if (menu->top > menu->current)
1020  menu->top = 0;
1021 
1022  menu->tagged = 0;
1023 
1024 #ifdef USE_NNTP
1025  if (OptNews)
1026  {
1027  if (mailbox)
1028  snprintf(title, titlelen, _("Subscribed newsgroups"));
1029  else
1030  {
1031  snprintf(title, titlelen, _("Newsgroups on server [%s]"),
1033  }
1034  }
1035  else
1036 #endif
1037  {
1038  if (mailbox)
1039  {
1040  menu->is_mailbox_list = true;
1041  snprintf(title, titlelen, _("Mailboxes [%d]"),
1042  mutt_mailbox_check(Context ? Context->mailbox : NULL, 0));
1043  }
1044  else
1045  {
1046  struct Buffer *path = mutt_buffer_pool_get();
1047  menu->is_mailbox_list = false;
1048  mutt_buffer_copy(path, &LastDir);
1050 #ifdef USE_IMAP
1051  if (state->imap_browse && C_ImapListSubscribed)
1052  {
1053  snprintf(title, titlelen, _("Subscribed [%s], File mask: %s"),
1054  mutt_b2s(path), NONULL(C_Mask ? C_Mask->pattern : NULL));
1055  }
1056  else
1057 #endif
1058  {
1059  snprintf(title, titlelen, _("Directory [%s], File mask: %s"),
1060  mutt_b2s(path), NONULL(C_Mask ? C_Mask->pattern : NULL));
1061  }
1062  mutt_buffer_pool_release(&path);
1063  }
1064  }
1065 
1066  /* Browser tracking feature.
1067  * The goal is to highlight the good directory if LastDir is the parent dir
1068  * of LastDirBackup (this occurs mostly when one hit "../"). It should also work
1069  * properly when the user is in examine_mailboxes-mode. */
1070  if (mutt_str_startswith(mutt_b2s(&LastDirBackup), mutt_b2s(&LastDir), CASE_MATCH))
1071  {
1072  char target_dir[PATH_MAX] = { 0 };
1073 
1074 #ifdef USE_IMAP
1075  /* Check what kind of dir LastDirBackup is. */
1076  if (imap_path_probe(mutt_b2s(&LastDirBackup), NULL) == MUTT_IMAP)
1077  {
1078  mutt_str_strfcpy(target_dir, mutt_b2s(&LastDirBackup), sizeof(target_dir));
1079  imap_clean_path(target_dir, sizeof(target_dir));
1080  }
1081  else
1082 #endif
1083  mutt_str_strfcpy(target_dir, strrchr(mutt_b2s(&LastDirBackup), '/') + 1,
1084  sizeof(target_dir));
1085 
1086  /* If we get here, it means that LastDir is the parent directory of
1087  * LastDirBackup. I.e., we're returning from a subdirectory, and we want
1088  * to position the cursor on the directory we're returning from. */
1089  bool matched = false;
1090  for (unsigned int i = 0; i < state->entrylen; i++)
1091  {
1092  if (mutt_str_strcmp(state->entry[i].name, target_dir) == 0)
1093  {
1094  menu->current = i;
1095  matched = true;
1096  break;
1097  }
1098  }
1099  if (!matched)
1100  browser_highlight_default(state, menu);
1101  }
1102  else
1103  browser_highlight_default(state, menu);
1104 
1105  menu->redraw = REDRAW_FULL;
1106 }
1107 
1111 static int file_tag(struct Menu *menu, int sel, int act)
1112 {
1113  struct FolderFile *ff = &(((struct FolderFile *) menu->data)[sel]);
1114  if (S_ISDIR(ff->mode) || (S_ISLNK(ff->mode) && link_is_dir(mutt_b2s(&LastDir), ff->name)))
1115  {
1116  mutt_error(_("Can't attach a directory"));
1117  return 0;
1118  }
1119 
1120  bool ot = ff->tagged;
1121  ff->tagged = ((act >= 0) ? act : !ff->tagged);
1122 
1123  return ff->tagged - ot;
1124 }
1125 
1136 void mutt_browser_select_dir(const char *f)
1137 {
1138  init_lastdir();
1139 
1140  mutt_buffer_strcpy(&LastDirBackup, f);
1141 
1142  /* Method that will fetch the parent path depending on the type of the path. */
1143  char buf[PATH_MAX];
1144  mutt_get_parent_path(mutt_b2s(&LastDirBackup), buf, sizeof(buf));
1145  mutt_buffer_strcpy(&LastDir, buf);
1146 }
1147 
1152 {
1153  if (!nc->event_data || !nc->global_data)
1154  return -1;
1155  if (nc->event_type != NT_CONFIG)
1156  return 0;
1157 
1158  struct EventConfig *ec = nc->event_data;
1159  struct MuttWindow *dlg = nc->global_data;
1160 
1161  if (mutt_str_strcmp(ec->name, "status_on_top") != 0)
1162  return 0;
1163 
1164  struct MuttWindow *win_first = TAILQ_FIRST(&dlg->children);
1165 
1166  if ((C_StatusOnTop && (win_first->type == WT_INDEX)) ||
1167  (!C_StatusOnTop && (win_first->type != WT_INDEX)))
1168  {
1169  // Swap the Index and the IndexBar Windows
1170  TAILQ_REMOVE(&dlg->children, win_first, entries);
1171  TAILQ_INSERT_TAIL(&dlg->children, win_first, entries);
1172  }
1173 
1174  mutt_window_reflow(dlg);
1175  return 0;
1176 }
1177 
1186  char ***files, int *numfiles)
1187 {
1188  char helpstr[1024];
1189  char title[256];
1190  struct BrowserState state = { 0 };
1191  struct Menu *menu = NULL;
1192  bool kill_prefix = false;
1193  bool multiple = (flags & MUTT_SEL_MULTI);
1194  bool folder = (flags & MUTT_SEL_FOLDER);
1195  bool mailbox = (flags & MUTT_SEL_MAILBOX);
1196 
1197  /* Keeps in memory the directory we were in when hitting '='
1198  * to go directly to $folder (#C_Folder) */
1199  char goto_swapper[PATH_MAX] = { 0 };
1200 
1201  mailbox = mailbox && folder;
1202 
1203  struct Buffer *OldLastDir = mutt_buffer_pool_get();
1204  struct Buffer *tmp = mutt_buffer_pool_get();
1205  struct Buffer *buf = mutt_buffer_pool_get();
1206  struct Buffer *prefix = mutt_buffer_pool_get();
1207 
1208  init_lastdir();
1209 
1210 #ifdef USE_NNTP
1211  if (OptNews)
1212  {
1213  if (mutt_buffer_is_empty(file))
1214  {
1216 
1217  /* default state for news reader mode is browse subscribed newsgroups */
1218  mailbox = false;
1219  for (size_t i = 0; i < adata->groups_num; i++)
1220  {
1221  struct NntpMboxData *mdata = adata->groups_list[i];
1222  if (mdata && mdata->subscribed)
1223  {
1224  mailbox = true;
1225  break;
1226  }
1227  }
1228  }
1229  else
1230  {
1231  mutt_buffer_copy(prefix, file);
1232  }
1233  }
1234  else
1235 #endif
1236  if (!mutt_buffer_is_empty(file))
1237  {
1239 #ifdef USE_IMAP
1240  if (imap_path_probe(mutt_b2s(file), NULL) == MUTT_IMAP)
1241  {
1242  init_state(&state, NULL);
1243  state.imap_browse = true;
1244  if (imap_browse(mutt_b2s(file), &state) == 0)
1245  {
1246  mutt_buffer_strcpy(&LastDir, state.folder);
1247  browser_sort(&state);
1248  }
1249  }
1250  else
1251  {
1252 #endif
1253  int i;
1254  for (i = mutt_buffer_len(file) - 1; (i > 0) && ((mutt_b2s(file))[i] != '/'); i--)
1255  ;
1256  if (i > 0)
1257  {
1258  if ((mutt_b2s(file))[0] == '/')
1259  mutt_buffer_strcpy_n(&LastDir, mutt_b2s(file), i);
1260  else
1261  {
1262  mutt_path_getcwd(&LastDir);
1263  mutt_buffer_addch(&LastDir, '/');
1264  mutt_buffer_addstr_n(&LastDir, mutt_b2s(file), i);
1265  }
1266  }
1267  else
1268  {
1269  if ((mutt_b2s(file))[0] == '/')
1270  mutt_buffer_strcpy(&LastDir, "/");
1271  else
1272  mutt_path_getcwd(&LastDir);
1273  }
1274 
1275  if ((i <= 0) && (mutt_b2s(file)[0] != '/'))
1276  mutt_buffer_copy(prefix, file);
1277  else
1278  mutt_buffer_strcpy(prefix, mutt_b2s(file) + i + 1);
1279  kill_prefix = true;
1280 #ifdef USE_IMAP
1281  }
1282 #endif
1283  }
1284  else
1285  {
1286  if (!folder)
1287  mutt_path_getcwd(&LastDir);
1288  else
1289  {
1290  /* Whether we use the tracking feature of the browser depends
1291  * on which sort method we chose to use. This variable is defined
1292  * only to help readability of the code. */
1293  bool browser_track = false;
1294 
1295  switch (C_SortBrowser & SORT_MASK)
1296  {
1297  case SORT_DESC:
1298  case SORT_SUBJECT:
1299  case SORT_ORDER:
1300  browser_track = true;
1301  break;
1302  }
1303 
1304  /* We use mutt_browser_select_dir to initialize the two
1305  * variables (LastDir, LastDirBackup) at the appropriate
1306  * values.
1307  *
1308  * We do it only when LastDir is not set (first pass there)
1309  * or when CurrentFolder and LastDirBackup are not the same.
1310  * This code is executed only when we list files, not when
1311  * we press up/down keys to navigate in a displayed list.
1312  *
1313  * We only do this when CurrentFolder has been set (ie, not
1314  * when listing folders on startup with "neomutt -y").
1315  *
1316  * This tracker is only used when browser_track is true,
1317  * meaning only with sort methods SUBJECT/DESC for now. */
1318  if (CurrentFolder)
1319  {
1320  if (mutt_buffer_is_empty(&LastDir))
1321  {
1322  /* If browsing in "local"-mode, than we chose to define LastDir to
1323  * MailDir */
1324  switch (mx_path_probe(CurrentFolder))
1325  {
1326  case MUTT_IMAP:
1327  case MUTT_MAILDIR:
1328  case MUTT_MBOX:
1329  case MUTT_MH:
1330  case MUTT_MMDF:
1331  if (C_Folder)
1332  mutt_buffer_strcpy(&LastDir, C_Folder);
1333  else if (C_Spoolfile)
1335  break;
1336  default:
1338  break;
1339  }
1340  }
1341  else if (mutt_str_strcmp(CurrentFolder, mutt_b2s(&LastDirBackup)) != 0)
1342  {
1344  }
1345  }
1346 
1347  /* When browser tracking feature is disabled, clear LastDirBackup */
1348  if (!browser_track)
1349  mutt_buffer_reset(&LastDirBackup);
1350  }
1351 
1352 #ifdef USE_IMAP
1353  if (!mailbox && (imap_path_probe(mutt_b2s(&LastDir), NULL) == MUTT_IMAP))
1354  {
1355  init_state(&state, NULL);
1356  state.imap_browse = true;
1357  imap_browse(mutt_b2s(&LastDir), &state);
1358  browser_sort(&state);
1359  }
1360  else
1361 #endif
1362  {
1363  size_t i = mutt_buffer_len(&LastDir);
1364  while ((i > 0) && (mutt_b2s(&LastDir)[--i] == '/'))
1365  LastDir.data[i] = '\0';
1366  mutt_buffer_fix_dptr(&LastDir);
1367  if (mutt_buffer_is_empty(&LastDir))
1368  mutt_path_getcwd(&LastDir);
1369  }
1370  }
1371 
1372  mutt_buffer_reset(file);
1373 
1374  if (mailbox)
1375  {
1376  examine_mailboxes(NULL, &state);
1377  }
1378  else
1379 #ifdef USE_IMAP
1380  if (!state.imap_browse)
1381 #endif
1382  {
1383  if (examine_directory(NULL, &state, mutt_b2s(&LastDir), mutt_b2s(prefix)) == -1)
1384  goto bail;
1385  }
1386 
1387  struct MuttWindow *dlg =
1390  dlg->type = WT_DIALOG;
1391  struct MuttWindow *index =
1394  index->type = WT_INDEX;
1395  struct MuttWindow *ibar = mutt_window_new(
1397  ibar->type = WT_INDEX_BAR;
1398 
1399  if (C_StatusOnTop)
1400  {
1401  mutt_window_add_child(dlg, ibar);
1402  mutt_window_add_child(dlg, index);
1403  }
1404  else
1405  {
1406  mutt_window_add_child(dlg, index);
1407  mutt_window_add_child(dlg, ibar);
1408  }
1409 
1411  dialog_push(dlg);
1412 
1413  menu = mutt_menu_new(MENU_FOLDER);
1414 
1415  menu->pagelen = index->state.rows;
1416  menu->win_index = index;
1417  menu->win_ibar = ibar;
1418 
1421  menu->title = title;
1422  menu->data = state.entry;
1423  if (multiple)
1424  menu->menu_tag = file_tag;
1425 
1426  menu->help = mutt_compile_help(helpstr, sizeof(helpstr), MENU_FOLDER,
1427 #ifdef USE_NNTP
1428  OptNews ? FolderNewsHelp :
1429 #endif
1430  FolderHelp);
1431  mutt_menu_push_current(menu);
1432 
1433  init_menu(&state, menu, title, sizeof(title), mailbox);
1434 
1435  int op;
1436  while (true)
1437  {
1438  switch (op = mutt_menu_loop(menu))
1439  {
1440  case OP_DESCEND_DIRECTORY:
1441  case OP_GENERIC_SELECT_ENTRY:
1442 
1443  if (state.entrylen == 0)
1444  {
1445  mutt_error(_("No files match the file mask"));
1446  break;
1447  }
1448 
1449  if (S_ISDIR(state.entry[menu->current].mode) ||
1450  (S_ISLNK(state.entry[menu->current].mode) &&
1451  link_is_dir(mutt_b2s(&LastDir), state.entry[menu->current].name))
1452 #ifdef USE_IMAP
1453  || state.entry[menu->current].inferiors
1454 #endif
1455  )
1456  {
1457  /* make sure this isn't a MH or maildir mailbox */
1458  if (mailbox)
1459  {
1460  mutt_buffer_strcpy(buf, state.entry[menu->current].name);
1462  }
1463 #ifdef USE_IMAP
1464  else if (state.imap_browse)
1465  {
1466  mutt_buffer_strcpy(buf, state.entry[menu->current].name);
1467  }
1468 #endif
1469  else
1470  {
1471  mutt_buffer_concat_path(buf, mutt_b2s(&LastDir),
1472  state.entry[menu->current].name);
1473  }
1474 
1475  enum MailboxType magic = mx_path_probe(mutt_b2s(buf));
1476  if ((op == OP_DESCEND_DIRECTORY) || (magic == MUTT_MAILBOX_ERROR) ||
1477  (magic == MUTT_UNKNOWN)
1478 #ifdef USE_IMAP
1479  || state.entry[menu->current].inferiors
1480 #endif
1481  )
1482  {
1483  /* save the old directory */
1484  mutt_buffer_copy(OldLastDir, &LastDir);
1485 
1486  if (mutt_str_strcmp(state.entry[menu->current].name, "..") == 0)
1487  {
1488  size_t lastdirlen = mutt_buffer_len(&LastDir);
1489  if ((lastdirlen > 1) &&
1490  (mutt_str_strcmp("..", mutt_b2s(&LastDir) + lastdirlen - 2) == 0))
1491  {
1492  mutt_buffer_addstr(&LastDir, "/..");
1493  }
1494  else
1495  {
1496  char *p = NULL;
1497  if (lastdirlen > 1)
1498  p = strrchr(mutt_b2s(&LastDir) + 1, '/');
1499 
1500  if (p)
1501  {
1502  *p = '\0';
1503  mutt_buffer_fix_dptr(&LastDir);
1504  }
1505  else
1506  {
1507  if (mutt_b2s(&LastDir)[0] == '/')
1508  mutt_buffer_strcpy(&LastDir, "/");
1509  else
1510  mutt_buffer_addstr(&LastDir, "/..");
1511  }
1512  }
1513  }
1514  else if (mailbox)
1515  {
1516  mutt_buffer_strcpy(&LastDir, state.entry[menu->current].name);
1517  mutt_buffer_expand_path(&LastDir);
1518  }
1519 #ifdef USE_IMAP
1520  else if (state.imap_browse)
1521  {
1522  mutt_buffer_strcpy(&LastDir, state.entry[menu->current].name);
1523  /* tack on delimiter here */
1524 
1525  /* special case "" needs no delimiter */
1526  struct Url *url = url_parse(state.entry[menu->current].name);
1527  if (url->path && (state.entry[menu->current].delim != '\0'))
1528  {
1529  mutt_buffer_addch(&LastDir, state.entry[menu->current].delim);
1530  }
1531  url_free(&url);
1532  }
1533 #endif
1534  else
1535  {
1536  mutt_buffer_concat_path(tmp, mutt_b2s(&LastDir),
1537  state.entry[menu->current].name);
1538  mutt_buffer_copy(&LastDir, tmp);
1539  }
1540 
1541  destroy_state(&state);
1542  if (kill_prefix)
1543  {
1544  mutt_buffer_reset(prefix);
1545  kill_prefix = false;
1546  }
1547  mailbox = false;
1548 #ifdef USE_IMAP
1549  if (state.imap_browse)
1550  {
1551  init_state(&state, NULL);
1552  state.imap_browse = true;
1553  imap_browse(mutt_b2s(&LastDir), &state);
1554  browser_sort(&state);
1555  menu->data = state.entry;
1556  }
1557  else
1558 #endif
1559  {
1560  if (examine_directory(menu, &state, mutt_b2s(&LastDir), mutt_b2s(prefix)) == -1)
1561  {
1562  /* try to restore the old values */
1563  mutt_buffer_copy(&LastDir, OldLastDir);
1564  if (examine_directory(menu, &state, mutt_b2s(&LastDir),
1565  mutt_b2s(prefix)) == -1)
1566  {
1567  mutt_buffer_strcpy(&LastDir, NONULL(HomeDir));
1568  goto bail;
1569  }
1570  }
1571  /* resolve paths navigated from GUI */
1572  if (mutt_path_realpath(LastDir.data) == 0)
1573  break;
1574  }
1575 
1576  browser_highlight_default(&state, menu);
1577  init_menu(&state, menu, title, sizeof(title), mailbox);
1578  goto_swapper[0] = '\0';
1579  break;
1580  }
1581  }
1582  else if (op == OP_DESCEND_DIRECTORY)
1583  {
1584  mutt_error(_("%s is not a directory"), state.entry[menu->current].name);
1585  break;
1586  }
1587 
1588  if (mailbox || OptNews) /* USE_NNTP */
1589  {
1590  mutt_buffer_strcpy(file, state.entry[menu->current].name);
1592  }
1593 #ifdef USE_IMAP
1594  else if (state.imap_browse)
1595  mutt_buffer_strcpy(file, state.entry[menu->current].name);
1596 #endif
1597  else
1598  {
1599  mutt_buffer_concat_path(file, mutt_b2s(&LastDir),
1600  state.entry[menu->current].name);
1601  }
1602  /* fallthrough */
1603 
1604  case OP_EXIT:
1605 
1606  if (multiple)
1607  {
1608  char **tfiles = NULL;
1609 
1610  if (menu->tagged)
1611  {
1612  *numfiles = menu->tagged;
1613  tfiles = mutt_mem_calloc(*numfiles, sizeof(char *));
1614  for (int i = 0, j = 0; i < state.entrylen; i++)
1615  {
1616  struct FolderFile ff = state.entry[i];
1617  if (ff.tagged)
1618  {
1619  mutt_buffer_concat_path(tmp, mutt_b2s(&LastDir), ff.name);
1621  tfiles[j++] = mutt_buffer_strdup(tmp);
1622  }
1623  }
1624  *files = tfiles;
1625  }
1626  else if (!mutt_buffer_is_empty(file)) /* no tagged entries. return selected entry */
1627  {
1628  *numfiles = 1;
1629  tfiles = mutt_mem_calloc(*numfiles, sizeof(char *));
1631  tfiles[0] = mutt_buffer_strdup(file);
1632  *files = tfiles;
1633  }
1634  }
1635 
1636  destroy_state(&state);
1637  goto bail;
1638 
1639  case OP_BROWSER_TELL:
1640  if (state.entrylen)
1641  mutt_message("%s", state.entry[menu->current].name);
1642  break;
1643 
1644 #ifdef USE_IMAP
1645  case OP_BROWSER_TOGGLE_LSUB:
1646  bool_str_toggle(NeoMutt->sub, "imap_list_subscribed", NULL);
1647 
1648  mutt_unget_event(0, OP_CHECK_NEW);
1649  break;
1650 
1651  case OP_CREATE_MAILBOX:
1652  if (!state.imap_browse)
1653  {
1654  mutt_error(_("Create is only supported for IMAP mailboxes"));
1655  break;
1656  }
1657 
1658  if (imap_mailbox_create(mutt_b2s(&LastDir)) == 0)
1659  {
1660  /* TODO: find a way to detect if the new folder would appear in
1661  * this window, and insert it without starting over. */
1662  destroy_state(&state);
1663  init_state(&state, NULL);
1664  state.imap_browse = true;
1665  imap_browse(mutt_b2s(&LastDir), &state);
1666  browser_sort(&state);
1667  menu->data = state.entry;
1668  browser_highlight_default(&state, menu);
1669  init_menu(&state, menu, title, sizeof(title), mailbox);
1670  }
1671  /* else leave error on screen */
1672  break;
1673 
1674  case OP_RENAME_MAILBOX:
1675  if (!state.entry[menu->current].imap)
1676  mutt_error(_("Rename is only supported for IMAP mailboxes"));
1677  else
1678  {
1679  int nentry = menu->current;
1680 
1681  if (imap_mailbox_rename(state.entry[nentry].name) >= 0)
1682  {
1683  destroy_state(&state);
1684  init_state(&state, NULL);
1685  state.imap_browse = true;
1686  imap_browse(mutt_b2s(&LastDir), &state);
1687  browser_sort(&state);
1688  menu->data = state.entry;
1689  browser_highlight_default(&state, menu);
1690  init_menu(&state, menu, title, sizeof(title), mailbox);
1691  }
1692  }
1693  break;
1694 
1695  case OP_DELETE_MAILBOX:
1696  if (!state.entry[menu->current].imap)
1697  mutt_error(_("Delete is only supported for IMAP mailboxes"));
1698  else
1699  {
1700  char msg[128];
1701  int nentry = menu->current;
1702 
1703  // TODO(sileht): It could be better to select INBOX instead. But I
1704  // don't want to manipulate Context/Mailboxes/mailbox->account here for now.
1705  // Let's just protect neomutt against crash for now. #1417
1707  state.entry[nentry].name) == 0)
1708  {
1709  mutt_error(_("Can't delete currently selected mailbox"));
1710  break;
1711  }
1712 
1713  snprintf(msg, sizeof(msg), _("Really delete mailbox \"%s\"?"),
1714  state.entry[nentry].name);
1715  if (mutt_yesorno(msg, MUTT_NO) == MUTT_YES)
1716  {
1717  if (imap_delete_mailbox(Context->mailbox, state.entry[nentry].name) == 0)
1718  {
1719  /* free the mailbox from the browser */
1720  FREE(&((state.entry)[nentry].name));
1721  FREE(&((state.entry)[nentry].desc));
1722  /* and move all other entries up */
1723  if ((nentry + 1) < state.entrylen)
1724  {
1725  memmove(state.entry + nentry, state.entry + nentry + 1,
1726  sizeof(struct FolderFile) * (state.entrylen - (nentry + 1)));
1727  }
1728  memset(&state.entry[state.entrylen - 1], 0, sizeof(struct FolderFile));
1729  state.entrylen--;
1730  mutt_message(_("Mailbox deleted"));
1731  init_menu(&state, menu, title, sizeof(title), mailbox);
1732  }
1733  else
1734  mutt_error(_("Mailbox deletion failed"));
1735  }
1736  else
1737  mutt_message(_("Mailbox not deleted"));
1738  }
1739  break;
1740 #endif
1741 
1742  case OP_GOTO_PARENT:
1743  case OP_CHANGE_DIRECTORY:
1744 
1745 #ifdef USE_NNTP
1746  if (OptNews)
1747  break;
1748 #endif
1749 
1750  mutt_buffer_copy(buf, &LastDir);
1751 #ifdef USE_IMAP
1752  if (!state.imap_browse)
1753 #endif
1754  {
1755  /* add '/' at the end of the directory name if not already there */
1756  size_t len = mutt_buffer_len(buf);
1757  if ((len > 0) && (mutt_b2s(&LastDir)[len - 1] != '/'))
1758  mutt_buffer_addch(buf, '/');
1759  }
1760 
1761  if (op == OP_CHANGE_DIRECTORY)
1762  {
1763  /* buf comes from the buffer pool, so defaults to size 1024 */
1764  int ret = mutt_buffer_get_field(_("Chdir to: "), buf, MUTT_FILE);
1765  if ((ret != 0) && mutt_buffer_is_empty(buf))
1766  break;
1767  }
1768  else if (op == OP_GOTO_PARENT)
1769  mutt_get_parent_path(mutt_b2s(buf), buf->data, buf->dsize);
1770 
1771  if (!mutt_buffer_is_empty(buf))
1772  {
1773  mailbox = false;
1775 #ifdef USE_IMAP
1776  if (imap_path_probe(mutt_b2s(buf), NULL) == MUTT_IMAP)
1777  {
1778  mutt_buffer_copy(&LastDir, buf);
1779  destroy_state(&state);
1780  init_state(&state, NULL);
1781  state.imap_browse = true;
1782  imap_browse(mutt_b2s(&LastDir), &state);
1783  browser_sort(&state);
1784  menu->data = state.entry;
1785  browser_highlight_default(&state, menu);
1786  init_menu(&state, menu, title, sizeof(title), mailbox);
1787  }
1788  else
1789 #endif
1790  {
1791  if (mutt_b2s(buf)[0] != '/')
1792  {
1793  /* in case dir is relative, make it relative to LastDir,
1794  * not current working dir */
1795  mutt_buffer_concat_path(tmp, mutt_b2s(&LastDir), mutt_b2s(buf));
1796  mutt_buffer_copy(buf, tmp);
1797  }
1798  /* Resolve path from <chdir>
1799  * Avoids buildup such as /a/b/../../c
1800  * Symlinks are always unraveled to keep code simple */
1801  if (mutt_path_realpath(buf->data) == 0)
1802  break;
1803 
1804  struct stat st;
1805  if (stat(mutt_b2s(buf), &st) == 0)
1806  {
1807  if (S_ISDIR(st.st_mode))
1808  {
1809  destroy_state(&state);
1810  if (examine_directory(menu, &state, mutt_b2s(buf), mutt_b2s(prefix)) == 0)
1811  mutt_buffer_copy(&LastDir, buf);
1812  else
1813  {
1814  mutt_error(_("Error scanning directory"));
1815  if (examine_directory(menu, &state, mutt_b2s(&LastDir),
1816  mutt_b2s(prefix)) == -1)
1817  {
1818  goto bail;
1819  }
1820  }
1821  browser_highlight_default(&state, menu);
1822  init_menu(&state, menu, title, sizeof(title), mailbox);
1823  }
1824  else
1825  mutt_error(_("%s is not a directory"), mutt_b2s(buf));
1826  }
1827  else
1828  mutt_perror(mutt_b2s(buf));
1829  }
1830  }
1831  break;
1832 
1833  case OP_ENTER_MASK:
1834  {
1835  mutt_buffer_strcpy(buf, C_Mask ? C_Mask->pattern : NULL);
1836  if (mutt_get_field(_("File Mask: "), buf->data, buf->dsize, MUTT_COMP_NO_FLAGS) != 0)
1837  break;
1838 
1839  mutt_buffer_fix_dptr(buf);
1840 
1841  mailbox = false;
1842  /* assume that the user wants to see everything */
1843  if (mutt_buffer_is_empty(buf))
1844  mutt_buffer_strcpy(buf, ".");
1845 
1846  struct Buffer errmsg = { 0 };
1847  int rc = cs_subset_str_string_set(NeoMutt->sub, "mask", mutt_b2s(buf), NULL);
1848  if (CSR_RESULT(rc) != CSR_SUCCESS)
1849  {
1850  if (!mutt_buffer_is_empty(&errmsg))
1851  {
1852  mutt_error("%s", errmsg.data);
1853  FREE(&errmsg.data);
1854  }
1855  break;
1856  }
1857 
1858  destroy_state(&state);
1859 #ifdef USE_IMAP
1860  if (state.imap_browse)
1861  {
1862  init_state(&state, NULL);
1863  state.imap_browse = true;
1864  imap_browse(mutt_b2s(&LastDir), &state);
1865  browser_sort(&state);
1866  menu->data = state.entry;
1867  init_menu(&state, menu, title, sizeof(title), mailbox);
1868  }
1869  else
1870 #endif
1871  if (examine_directory(menu, &state, mutt_b2s(&LastDir), NULL) == 0)
1872  init_menu(&state, menu, title, sizeof(title), mailbox);
1873  else
1874  {
1875  mutt_error(_("Error scanning directory"));
1876  goto bail;
1877  }
1878  kill_prefix = false;
1879  if (state.entrylen == 0)
1880  {
1881  mutt_error(_("No files match the file mask"));
1882  break;
1883  }
1884  break;
1885  }
1886 
1887  case OP_SORT:
1888  case OP_SORT_REVERSE:
1889 
1890  {
1891  bool resort = true;
1892  int sort = -1;
1893  int reverse = (op == OP_SORT_REVERSE);
1894 
1895  switch (mutt_multi_choice(
1896  (reverse) ?
1897  /* L10N: The highlighted letters must match the "Sort" options */
1898  _("Reverse sort by (d)ate, (a)lpha, si(z)e, d(e)scription, "
1899  "(c)ount, ne(w) count, or do(n)'t sort?") :
1900  /* L10N: The highlighted letters must match the "Reverse Sort" options */
1901  _("Sort by (d)ate, (a)lpha, si(z)e, d(e)scription, (c)ount, "
1902  "ne(w) count, or do(n)'t sort?"),
1903  /* L10N: These must match the highlighted letters from "Sort" and "Reverse Sort" */
1904  _("dazecwn")))
1905  {
1906  case -1: /* abort */
1907  resort = false;
1908  break;
1909 
1910  case 1: /* (d)ate */
1911  sort = SORT_DATE;
1912  break;
1913 
1914  case 2: /* (a)lpha */
1915  sort = SORT_SUBJECT;
1916  break;
1917 
1918  case 3: /* si(z)e */
1919  sort = SORT_SIZE;
1920  break;
1921 
1922  case 4: /* d(e)scription */
1923  sort = SORT_DESC;
1924  break;
1925 
1926  case 5: /* (c)ount */
1927  sort = SORT_COUNT;
1928  break;
1929 
1930  case 6: /* ne(w) count */
1931  sort = SORT_UNREAD;
1932  break;
1933 
1934  case 7: /* do(n)'t sort */
1935  sort = SORT_ORDER;
1936  resort = false;
1937  break;
1938  }
1939  if (resort)
1940  {
1941  sort |= reverse ? SORT_REVERSE : 0;
1942  cs_subset_str_native_set(NeoMutt->sub, "sort_browser", sort, NULL);
1943  browser_sort(&state);
1944  browser_highlight_default(&state, menu);
1945  menu->redraw = REDRAW_FULL;
1946  }
1947  else
1948  {
1949  cs_subset_str_native_set(NeoMutt->sub, "sort_browser", sort, NULL);
1950  }
1951  break;
1952  }
1953 
1954  case OP_TOGGLE_MAILBOXES:
1955  case OP_BROWSER_GOTO_FOLDER:
1956  case OP_CHECK_NEW:
1957  if (op == OP_TOGGLE_MAILBOXES)
1958  mailbox = !mailbox;
1959 
1960  if (op == OP_BROWSER_GOTO_FOLDER)
1961  {
1962  /* When in mailboxes mode, disables this feature */
1963  if (C_Folder)
1964  {
1965  mutt_debug(LL_DEBUG3, "= hit! Folder: %s, LastDir: %s\n", C_Folder,
1966  mutt_b2s(&LastDir));
1967  if (goto_swapper[0] == '\0')
1968  {
1969  if (mutt_str_strcmp(mutt_b2s(&LastDir), C_Folder) != 0)
1970  {
1971  /* Stores into goto_swapper LastDir, and swaps to C_Folder */
1972  mutt_str_strfcpy(goto_swapper, mutt_b2s(&LastDir), sizeof(goto_swapper));
1973  mutt_buffer_copy(&LastDirBackup, &LastDir);
1974  mutt_buffer_strcpy(&LastDir, C_Folder);
1975  }
1976  }
1977  else
1978  {
1979  mutt_buffer_copy(&LastDirBackup, &LastDir);
1980  mutt_buffer_strcpy(&LastDir, goto_swapper);
1981  goto_swapper[0] = '\0';
1982  }
1983  }
1984  }
1985  destroy_state(&state);
1986  mutt_buffer_reset(prefix);
1987  kill_prefix = false;
1988 
1989  if (mailbox)
1990  {
1991  examine_mailboxes(menu, &state);
1992  }
1993 #ifdef USE_IMAP
1994  else if (imap_path_probe(mutt_b2s(&LastDir), NULL) == MUTT_IMAP)
1995  {
1996  init_state(&state, NULL);
1997  state.imap_browse = true;
1998  imap_browse(mutt_b2s(&LastDir), &state);
1999  browser_sort(&state);
2000  menu->data = state.entry;
2001  }
2002 #endif
2003  else if (examine_directory(menu, &state, mutt_b2s(&LastDir), mutt_b2s(prefix)) == -1)
2004  goto bail;
2005  init_menu(&state, menu, title, sizeof(title), mailbox);
2006  break;
2007 
2008  case OP_MAILBOX_LIST:
2010  break;
2011 
2012  case OP_BROWSER_NEW_FILE:
2013  mutt_buffer_printf(buf, "%s/", mutt_b2s(&LastDir));
2014  /* buf comes from the buffer pool, so defaults to size 1024 */
2015  if (mutt_buffer_get_field(_("New file name: "), buf, MUTT_FILE) == 0)
2016  {
2017  mutt_buffer_copy(file, buf);
2018  destroy_state(&state);
2019  goto bail;
2020  }
2021  break;
2022 
2023  case OP_BROWSER_VIEW_FILE:
2024  if (state.entrylen == 0)
2025  {
2026  mutt_error(_("No files match the file mask"));
2027  break;
2028  }
2029 
2030 #ifdef USE_IMAP
2031  if (state.entry[menu->current].selectable)
2032  {
2033  mutt_buffer_strcpy(file, state.entry[menu->current].name);
2034  destroy_state(&state);
2035  goto bail;
2036  }
2037  else
2038 #endif
2039  if (S_ISDIR(state.entry[menu->current].mode) ||
2040  (S_ISLNK(state.entry[menu->current].mode) &&
2041  link_is_dir(mutt_b2s(&LastDir), state.entry[menu->current].name)))
2042  {
2043  mutt_error(_("Can't view a directory"));
2044  break;
2045  }
2046  else
2047  {
2048  char buf2[PATH_MAX];
2049 
2050  mutt_path_concat(buf2, mutt_b2s(&LastDir),
2051  state.entry[menu->current].name, sizeof(buf2));
2052  struct Body *b = mutt_make_file_attach(buf2);
2053  if (b)
2054  {
2055  mutt_view_attachment(NULL, b, MUTT_VA_REGULAR, NULL, NULL, menu->win_index);
2056  mutt_body_free(&b);
2057  menu->redraw = REDRAW_FULL;
2058  }
2059  else
2060  mutt_error(_("Error trying to view file"));
2061  }
2062  break;
2063 
2064 #ifdef USE_NNTP
2065  case OP_CATCHUP:
2066  case OP_UNCATCHUP:
2067  if (OptNews)
2068  {
2069  struct FolderFile *ff = &state.entry[menu->current];
2070  struct NntpMboxData *mdata = NULL;
2071 
2073  if (rc < 0)
2074  break;
2075 
2076  if (op == OP_CATCHUP)
2078  else
2080 
2081  if (mdata)
2082  {
2084  if ((menu->current + 1) < menu->max)
2085  menu->current++;
2086  menu->redraw = REDRAW_MOTION_RESYNC;
2087  }
2088  if (rc)
2089  menu->redraw = REDRAW_INDEX;
2091  }
2092  break;
2093 
2094  case OP_LOAD_ACTIVE:
2095  if (OptNews)
2096  {
2098 
2099  if (nntp_newsrc_parse(adata) < 0)
2100  break;
2101 
2102  for (size_t i = 0; i < adata->groups_num; i++)
2103  {
2104  struct NntpMboxData *mdata = adata->groups_list[i];
2105  if (mdata)
2106  mdata->deleted = true;
2107  }
2108  nntp_active_fetch(adata, true);
2109  nntp_newsrc_update(adata);
2110  nntp_newsrc_close(adata);
2111 
2112  destroy_state(&state);
2113  if (mailbox)
2114  examine_mailboxes(menu, &state);
2115  else
2116  {
2117  if (examine_directory(menu, &state, NULL, NULL) == -1)
2118  break;
2119  }
2120  init_menu(&state, menu, title, sizeof(title), mailbox);
2121  }
2122  break;
2123 #endif /* USE_NNTP */
2124 
2125 #if defined(USE_IMAP) || defined(USE_NNTP)
2126  case OP_BROWSER_SUBSCRIBE:
2127  case OP_BROWSER_UNSUBSCRIBE:
2128 #endif
2129 #ifdef USE_NNTP
2130  case OP_SUBSCRIBE_PATTERN:
2131  case OP_UNSUBSCRIBE_PATTERN:
2132  if (OptNews)
2133  {
2135  regex_t rx;
2136  memset(&rx, 0, sizeof(rx));
2137  char *s = buf->data;
2138  int i = menu->current;
2139 
2140  if ((op == OP_SUBSCRIBE_PATTERN) || (op == OP_UNSUBSCRIBE_PATTERN))
2141  {
2142  char tmp2[256];
2143 
2144  mutt_buffer_reset(buf);
2145  if (op == OP_SUBSCRIBE_PATTERN)
2146  snprintf(tmp2, sizeof(tmp2), _("Subscribe pattern: "));
2147  else
2148  snprintf(tmp2, sizeof(tmp2), _("Unsubscribe pattern: "));
2149  /* buf comes from the buffer pool, so defaults to size 1024 */
2150  if ((mutt_buffer_get_field(tmp2, buf, MUTT_COMP_NO_FLAGS) != 0) ||
2151  mutt_buffer_is_empty(buf))
2152  {
2153  break;
2154  }
2155 
2156  int err = REG_COMP(&rx, s, REG_NOSUB);
2157  if (err != 0)
2158  {
2159  regerror(err, &rx, buf->data, buf->dsize);
2160  regfree(&rx);
2161  mutt_error("%s", mutt_b2s(buf));
2162  break;
2163  }
2164  menu->redraw = REDRAW_FULL;
2165  i = 0;
2166  }
2167  else if (state.entrylen == 0)
2168  {
2169  mutt_error(_("No newsgroups match the mask"));
2170  break;
2171  }
2172 
2173  int rc = nntp_newsrc_parse(adata);
2174  if (rc < 0)
2175  break;
2176 
2177  for (; i < state.entrylen; i++)
2178  {
2179  struct FolderFile *ff = &state.entry[i];
2180 
2181  if ((op == OP_BROWSER_SUBSCRIBE) || (op == OP_BROWSER_UNSUBSCRIBE) ||
2182  (regexec(&rx, ff->name, 0, NULL, 0) == 0))
2183  {
2184  if ((op == OP_BROWSER_SUBSCRIBE) || (op == OP_SUBSCRIBE_PATTERN))
2185  mutt_newsgroup_subscribe(adata, ff->name);
2186  else
2187  mutt_newsgroup_unsubscribe(adata, ff->name);
2188  }
2189  if ((op == OP_BROWSER_SUBSCRIBE) || (op == OP_BROWSER_UNSUBSCRIBE))
2190  {
2191  if ((menu->current + 1) < menu->max)
2192  menu->current++;
2193  menu->redraw = REDRAW_MOTION_RESYNC;
2194  break;
2195  }
2196  }
2197  if (op == OP_SUBSCRIBE_PATTERN)
2198  {
2199  for (size_t j = 0; adata && (j < adata->groups_num); j++)
2200  {
2201  struct NntpMboxData *mdata = adata->groups_list[j];
2202  if (mdata && mdata->group && !mdata->subscribed)
2203  {
2204  if (regexec(&rx, mdata->group, 0, NULL, 0) == 0)
2205  {
2206  mutt_newsgroup_subscribe(adata, mdata->group);
2207  add_folder(menu, &state, mdata->group, NULL, NULL, NULL, mdata);
2208  }
2209  }
2210  }
2211  init_menu(&state, menu, title, sizeof(title), mailbox);
2212  }
2213  if (rc > 0)
2214  menu->redraw = REDRAW_FULL;
2215  nntp_newsrc_update(adata);
2216  nntp_clear_cache(adata);
2217  nntp_newsrc_close(adata);
2218  if ((op != OP_BROWSER_SUBSCRIBE) && (op != OP_BROWSER_UNSUBSCRIBE))
2219  regfree(&rx);
2220  }
2221 #ifdef USE_IMAP
2222  else
2223 #endif /* USE_IMAP && USE_NNTP */
2224 #endif /* USE_NNTP */
2225 #ifdef USE_IMAP
2226  {
2227  char tmp2[256];
2228  mutt_str_strfcpy(tmp2, state.entry[menu->current].name, sizeof(tmp2));
2229  mutt_expand_path(tmp2, sizeof(tmp2));
2230  imap_subscribe(tmp2, (op == OP_BROWSER_SUBSCRIBE));
2231  }
2232 #endif /* USE_IMAP */
2233  }
2234  }
2235 
2236 bail:
2237  mutt_buffer_pool_release(&OldLastDir);
2240  mutt_buffer_pool_release(&prefix);
2241 
2242  if (menu)
2243  {
2244  mutt_menu_pop_current(menu);
2245  mutt_menu_free(&menu);
2246  dialog_pop();
2248  mutt_window_free(&dlg);
2249  }
2250 
2251  goto_swapper[0] = '\0';
2252 }
2253 
2262 void mutt_select_file(char *file, size_t filelen, SelectFileFlags flags,
2263  char ***files, int *numfiles)
2264 {
2265  struct Buffer *f_buf = mutt_buffer_pool_get();
2266 
2267  mutt_buffer_strcpy(f_buf, NONULL(file));
2268  mutt_buffer_select_file(f_buf, flags, files, numfiles);
2269  mutt_str_strfcpy(file, mutt_b2s(f_buf), filelen);
2270 
2271  mutt_buffer_pool_release(&f_buf);
2272 }
int msg_unread
number of unread messages
Definition: browser.h:75
Convenience wrapper for the gui headers.
mode_t mode
Definition: browser.h:63
time_t mutt_date_epoch(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:411
bool has_new_mail
Definition: lib.h:150
The "current" mailbox.
Definition: context.h:36
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox&#39;s path string.
Definition: mailbox.h:191
WHERE bool C_StatusOnTop
Config: Display the status bar at the top.
Definition: globals.h:255
char * mutt_path_concat(char *d, const char *dir, const char *fname, size_t l)
Join a directory name and a filename.
Definition: path.c:348
WHERE bool C_ImapListSubscribed
Config: (imap) When browsing a mailbox, only display subscribed folders.
Definition: globals.h:224
static int browser_compare_date(const void *a, const void *b)
Compare the date of two browser entries.
Definition: browser.c:193
void mutt_expando_format(char *buf, size_t buflen, size_t col, int cols, const char *src, format_t *callback, unsigned long data, MuttFormatFlags flags)
Expand expandos (x) in a string.
Definition: muttlib.c:876
#define REDRAW_FULL
Redraw everything.
Definition: mutt_menu.h:47
time_t mtime
Definition: browser.h:65
Manage keymappings.
uint8_t MuttFormatFlags
Flags for mutt_expando_format(), e.g. MUTT_FORMAT_FORCESUBJ.
Definition: format_flags.h:29
bool mutt_mailbox_list(void)
List the mailboxes with new mail.
Definition: mutt_mailbox.c:230
#define NONULL(x)
Definition: string2.h:37
bool has_mailbox
Definition: browser.h:84
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
static bool link_is_dir(const char *folder, const char *path)
Does this symlink point to a directory?
Definition: browser.c:343
int msg_count
Total number of messages.
Definition: mailbox.h:90
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:79
size_t mutt_date_localtime_format(char *buf, size_t buflen, const char *format, time_t t)
Format localtime.
Definition: date.c:774
IMAP network mailbox.
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
#define TAILQ_FIRST(head)
Definition: queue.h:716
#define mutt_perror(...)
Definition: logging.h:85
GUI selectable list of items.
Definition: mutt_menu.h:82
#define CSR_RESULT(x)
Definition: set.h:53
enum MailboxType imap_path_probe(const char *path, const struct stat *st)
Is this an IMAP Mailbox? - Implements MxOps::path_probe()
Definition: imap.c:2511
Data passed to a notification function.
Definition: observer.h:40
struct ConnAccount account
Definition: connection.h:36
int msg_unread
Number of unread messages.
Definition: mailbox.h:91
void mutt_browser_cleanup(void)
Clean up working Buffers.
Definition: browser.c:122
static void destroy_state(struct BrowserState *state)
Free the BrowserState.
Definition: browser.c:134
&#39;NNTP&#39; (Usenet) Mailbox type
Definition: mailbox.h:51
Structs that make up an email.
size_t mutt_buffer_copy(struct Buffer *dst, const struct Buffer *src)
Copy a Buffer&#39;s contents to another Buffer.
Definition: buffer.c:445
The "currently-open" mailbox.
void mutt_buffer_select_file(struct Buffer *file, SelectFileFlags flags, char ***files, int *numfiles)
Let the user select a file.
Definition: browser.c:1185
char * desc
Definition: browser.h:71
Dialog (nested Windows) displayed to the user.
Definition: mutt_window.h:68
#define mutt_message(...)
Definition: logging.h:83
int imap_delete_mailbox(struct Mailbox *m, char *path)
Delete a mailbox.
Definition: imap.c:649
A parsed URL proto://user:password@host:port/path?a=1&b=2
Definition: url.h:66
struct AccountList accounts
List of all Accounts.
Definition: neomutt.h:39
void nntp_clear_cache(struct NntpAccountData *adata)
Clear the NNTP cache.
Definition: newsrc.c:844
Window uses all available vertical space.
Definition: mutt_window.h:33
void neomutt_mailboxlist_clear(struct MailboxList *ml)
Free a Mailbox List.
Definition: neomutt.c:135
User answered &#39;Yes&#39;, or assume &#39;Yes&#39;.
Definition: quad.h:40
char * C_NewsgroupsCharset
Config: (nntp) Character set of newsgroups&#39; descriptions.
Definition: browser.c:73
static int file_tag(struct Menu *menu, int sel, int act)
Tag an entry in the menu - Implements Menu::menu_tag()
Definition: browser.c:1111
void dialog_pop(void)
Hide a Window from the user.
Definition: mutt_window.c:623
struct NntpMboxData * mutt_newsgroup_uncatchup(struct Mailbox *m, struct NntpAccountData *adata, char *group)
Uncatchup newsgroup.
Definition: newsrc.c:1336
char * realpath
Used for duplicate detection, context comparison, and the sidebar.
Definition: mailbox.h:83
gid_t gid
Definition: browser.h:67
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
State of the file/mailbox browser.
Definition: browser.h:95
void dialog_push(struct MuttWindow *dlg)
Display a Window to the user.
Definition: mutt_window.c:603
#define MUTT_FORMAT_ARROWCURSOR
Reserve space for arrow_cursor.
Definition: format_flags.h:35
Match any Mailbox type.
Definition: mailbox.h:44
General file/mailbox browser.
Definition: keymap.h:75
char * mutt_buffer_strdup(struct Buffer *buf)
Copy a Buffer&#39;s string.
Definition: buffer.c:432
A config-change event.
Definition: subset.h:68
static void add_folder(struct Menu *menu, struct BrowserState *state, const char *name, const char *desc, const struct stat *s, struct Mailbox *m, void *data)
Add a folder to the browser list.
Definition: browser.c:633
String manipulation buffer.
Definition: buffer.h:33
struct NntpMboxData * mutt_newsgroup_subscribe(struct NntpAccountData *adata, char *group)
Subscribe newsgroup.
Definition: newsrc.c:1247
void mutt_unget_event(int ch, int op)
Return a keystroke to the input buffer.
Definition: curs_lib.c:807
#define _(a)
Definition: message.h:28
Mailbox wasn&#39;t recognised.
Definition: mailbox.h:46
Error occurred examining Mailbox.
Definition: mailbox.h:45
int imap_mailbox_create(const char *path)
Create a new IMAP mailbox.
Definition: browse.c:382
A division of the screen.
Definition: mutt_window.h:86
void nntp_newsrc_close(struct NntpAccountData *adata)
Unlock and close .newsrc file.
Definition: newsrc.c:123
Match case when comparing strings.
Definition: string2.h:67
static int browser_compare_desc(const void *a, const void *b)
Compare the descriptions of two browser entries.
Definition: browser.c:175
short C_SortBrowser
Config: Sort method for the browser.
Definition: browser.c:75
static void init_lastdir(void)
Initialise the browser directories.
Definition: browser.c:108
char * C_FolderFormat
Config: printf-like format string for the browser&#39;s display of folders.
Definition: browser.c:71
void url_free(struct Url **ptr)
Free the contents of a URL.
Definition: url.c:157
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:160
static void folder_make_entry(char *buf, size_t buflen, struct Menu *menu, int line)
Format a menu item for the folder browser - Implements Menu::menu_make_entry()
Definition: browser.c:953
View using default method.
Definition: mutt_attach.h:42
static int browser_compare_count(const void *a, const void *b)
Compare the message count of two browser entries.
Definition: browser.c:229
static int select_file_search(struct Menu *menu, regex_t *rx, int line)
Menu search callback for matching files - Implements Menu::menu_search()
Definition: browser.c:938
Flags to control mutt_expando_format()
int cs_subset_str_string_set(const struct ConfigSubset *sub, const char *name, const char *value, struct Buffer *err)
Set a config item by string.
Definition: subset.c:393
All user-callable functions.
Container for Accounts, Notifications.
Definition: neomutt.h:35
void(* menu_make_entry)(char *buf, size_t buflen, struct Menu *menu, int line)
Format a item for a menu.
Definition: mutt_menu.h:120
void mutt_buffer_pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using &#39;~&#39; or &#39;=&#39;.
Definition: muttlib.c:702
NNTP-specific Account data -.
Definition: lib.h:77
bool notify_observer_add(struct Notify *notify, observer_t callback, void *global_data)
Add an observer to an object.
Definition: notify.c:154
#define MUTT_FORMAT_NO_FLAGS
No flags are set.
Definition: format_flags.h:30
#define mutt_get_field(field, buf, buflen, complete)
Definition: curs_lib.h:90
bool subscribed
Definition: lib.h:149
The body of an email.
Definition: body.h:34
Convenience wrapper for the config headers.
struct FolderFile * ff
Definition: browser.h:54
GUI component for displaying/selecting items from a list.
An Index Window containing a selection list.
Definition: mutt_window.h:72
#define MUTT_SEL_FOLDER
Select a local directory.
Definition: browser.h:47
Hundreds of global variables to back the user variables.
off_t size
Definition: browser.h:64
char * folder
Definition: browser.h:102
bool selectable
Definition: browser.h:81
void mutt_window_free(struct MuttWindow **ptr)
Free a Window and its children.
Definition: mutt_window.c:75
char host[128]
Definition: connaccount.h:63
enum NotifyType event_type
Send: Event type, e.g. NT_ACCOUNT.
Definition: observer.h:42
Some miscellaneous functions.
bool has_new
Mailbox has new mail.
Definition: mailbox.h:87
int cs_subset_str_native_set(const struct ConfigSubset *sub, const char *name, intptr_t value, struct Buffer *err)
Natively set the value of a string config item.
Definition: subset.c:290
Sort by the size of the email.
Definition: sort2.h:51
size_t dsize
Length of data.
Definition: buffer.h:37
char * mutt_expand_path(char *buf, size_t buflen)
Create the canonical path.
Definition: muttlib.c:132
int nntp_active_fetch(struct NntpAccountData *adata, bool mark_new)
Fetch list of all newsgroups from server.
Definition: nntp.c:2058
char * name
A short name for the Mailbox.
Definition: mailbox.h:84
enum QuadOption mutt_yesorno(const char *msg, enum QuadOption def)
Ask the user a Yes/No question.
Definition: curs_lib.c:376
struct Mailbox * mailbox
Definition: context.h:50
Many unsorted constants and some structs.
API for mailboxes.
uid_t uid
Definition: browser.h:66
struct Body * mutt_make_file_attach(const char *path)
Create a file attachment.
Definition: sendlib.c:1649
#define MUTT_COMP_NO_FLAGS
No flags are set.
Definition: mutt.h:62
enum MailboxType magic
Mailbox type.
Definition: mailbox.h:104
struct MuttWindow * win_ibar
Definition: mutt_menu.h:96
Sort by the order the messages appear in the mailbox.
Definition: sort2.h:55
nlink_t nlink
Definition: browser.h:68
const char * name
Definition: pgpmicalg.c:46
struct MailboxList neomutt_mailboxlist_get_all(struct NeoMutt *n, enum MailboxType magic)
Get a List of all Mailboxes.
Definition: neomutt.c:157
static int examine_mailboxes(struct Menu *menu, struct BrowserState *state)
Get list of mailboxes/subscribed newsgroups.
Definition: browser.c:836
Convenience wrapper for the core headers.
char * name
Definition: browser.h:70
#define REG_COMP(preg, regex, cflags)
Compile a regular expression.
Definition: regex3.h:52
WHERE char * HomeDir
User&#39;s home directory.
Definition: globals.h:49
char * group
Definition: lib.h:142
int num
Definition: browser.h:55
void mutt_buffer_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:294
unsigned int entrymax
max entry
Definition: browser.h:99
WHERE char * CurrentFolder
Currently selected mailbox.
Definition: globals.h:54
bool imap
Definition: browser.h:80
void * mdata
Driver specific data.
Definition: mailbox.h:135
void * global_data
Data from notify_observer_add()
Definition: observer.h:45
&#39;Maildir&#39; Mailbox type
Definition: mailbox.h:50
Usenet network mailbox type; talk to an NNTP server.
int flags
e.g. MB_NORMAL
Definition: mailbox.h:133
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:225
void mutt_buffer_fix_dptr(struct Buffer *buf)
Move the dptr to end of the Buffer.
Definition: buffer.c:181
#define CSR_SUCCESS
Action completed successfully.
Definition: set.h:37
Window has a fixed size.
Definition: mutt_window.h:42
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
#define TAILQ_REMOVE(head, elm, field)
Definition: queue.h:834
#define REDRAW_MOTION_RESYNC
Redraw any changing the menu selection.
Definition: mutt_menu.h:44
short cols
Number of columns, can be MUTT_WIN_SIZE_UNLIMITED.
Definition: mutt_window.h:56
#define mutt_b2s(buf)
Definition: buffer.h:41
const char * mutt_path_getcwd(struct Buffer *cwd)
Get the current working directory.
Definition: path.c:560
int mutt_multi_choice(const char *prompt, const char *letters)
Offer the user a multiple choice question.
Definition: curs_lib.c:906
A folder/dir in the browser.
Definition: browser.h:52
Browser entry representing a folder/dir.
Definition: browser.h:61
static int browser_compare_count_new(const void *a, const void *b)
Compare the new count of two browser entries.
Definition: browser.c:253
const char * line
Definition: common.c:36
#define MB_HIDDEN
Definition: mailbox.h:37
struct WindowState state
Current state of the Window.
Definition: mutt_window.h:91
&#39;POP3&#39; Mailbox type
Definition: mailbox.h:54
struct MuttWindow * mutt_window_new(enum MuttWindowOrientation orient, enum MuttWindowSize size, int rows, int cols)
Create a new Window.
Definition: mutt_window.c:57
WHERE char * C_Folder
Config: Base folder for a set of mailboxes.
Definition: globals.h:119
bool notify_observer_remove(struct Notify *notify, observer_t callback, void *global_data)
Remove an observer from an object.
Definition: notify.c:186
size_t mutt_buffer_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:356
A mailbox.
Definition: mailbox.h:80
#define PATH_MAX
Definition: mutt.h:50
User answered &#39;No&#39;, or assume &#39;No&#39;.
Definition: quad.h:39
int mutt_mailbox_check(struct Mailbox *m_cur, int force)
Check all all Mailboxes for new mail.
Definition: mutt_mailbox.c:152
#define MUTT_WIN_SIZE_UNLIMITED
Use as much space as possible.
Definition: mutt_window.h:47
void mutt_select_file(char *file, size_t filelen, SelectFileFlags flags, char ***files, int *numfiles)
Let the user select a file.
Definition: browser.c:2262
short rows
Number of rows, can be MUTT_WIN_SIZE_UNLIMITED.
Definition: mutt_window.h:55
int top
Entry that is the top of the current page.
Definition: mutt_menu.h:108
void * adata
Private data (for Mailbox backends)
Definition: account.h:43
size_t mutt_buffer_strcpy_n(struct Buffer *buf, const char *s, size_t len)
Copy a string into a Buffer.
Definition: buffer.c:327
void mutt_str_pretty_size(char *buf, size_t buflen, size_t num)
Display an abbreviated size, like 3.4K.
Definition: muttlib.c:1760
size_t mutt_buffer_addstr_n(struct Buffer *buf, const char *s, size_t len)
Add a string to a Buffer, expanding it if necessary.
Definition: buffer.c:99
int mutt_inbox_cmp(const char *a, const char *b)
do two folders share the same path and one is an inbox
Definition: muttlib.c:1692
char * data
Pointer to data.
Definition: buffer.h:35
int bool_str_toggle(struct ConfigSubset *sub, const char *name, struct Buffer *err)
Toggle the value of a bool.
Definition: bool.c:239
size_t mutt_path_realpath(char *buf)
resolve path, unraveling symlinks
Definition: path.c:437
unsigned int groups_num
Definition: lib.h:100
static void browser_sort(struct BrowserState *state)
Sort the entries in the browser.
Definition: browser.c:316
static int browser_compare_size(const void *a, const void *b)
Compare the size of two browser entries.
Definition: browser.c:211
&#39;mmdf&#39; Mailbox type
Definition: mailbox.h:48
size_t mutt_str_strfcpy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:773
bool is_mailbox_list
Definition: mutt_menu.h:94
int nntp_newsrc_parse(struct NntpAccountData *adata)
Parse .newsrc file.
Definition: newsrc.c:167
GUI present the user with a selectable list.
static const char * folder_format_str(char *buf, size_t buflen, size_t col, int cols, char op, const char *src, const char *prec, const char *if_str, const char *else_str, unsigned long data, MuttFormatFlags flags)
Format a string for the folder browser - Implements format_t.
Definition: browser.c:379
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition: queue.h:802
&#39;MH&#39; Mailbox type
Definition: mailbox.h:49
bool C_ShowOnlyUnread
Config: (nntp) Only show subscribed newsgroups with unread articles.
Definition: browser.c:74
int msg_count
total number of messages
Definition: browser.h:74
struct NntpMboxData * mutt_newsgroup_unsubscribe(struct NntpAccountData *adata, char *group)
Unsubscribe newsgroup.
Definition: newsrc.c:1271
Handling of email attachments.
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:349
int pagelen
Number of entries per screen.
Definition: mutt_menu.h:92
struct Notify * notify
Notifications handler.
Definition: neomutt.h:37
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:240
static int browser_compare_subject(const void *a, const void *b)
Compare the subject of two browser entries.
Definition: browser.c:155
#define MUTT_SEL_MULTI
Multi-selection is enabled.
Definition: browser.h:46
NNTP-specific Mailbox data -.
Definition: lib.h:140
int tagged
Number of tagged entries.
Definition: mutt_menu.h:111
#define MUTT_FILE
Do file completion.
Definition: mutt.h:64
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:312
char * path
Path.
Definition: url.h:73
void * event_data
Data from notify_send()
Definition: observer.h:44
int max
Number of entries in the menu.
Definition: mutt_menu.h:88
void mutt_window_add_child(struct MuttWindow *parent, struct MuttWindow *child)
Add a child to Window.
Definition: mutt_window.c:471
&#39;Notmuch&#39; (virtual) Mailbox type
Definition: mailbox.h:53
int(* menu_tag)(struct Menu *menu, int sel, int act)
Tag some menu items.
Definition: mutt_menu.h:137
&#39;mbox&#39; Mailbox type
Definition: mailbox.h:47
size_t entrylen
number of real entries
Definition: browser.h:98
size_t mutt_str_startswith(const char *str, const char *prefix, enum CaseSensitivity cs)
Check whether a string starts with a prefix.
Definition: string.c:168
#define mutt_buffer_get_field(field, buf, complete)
Definition: curs_lib.h:84
MuttRedrawFlags redraw
When to redraw the screen.
Definition: mutt_menu.h:89
int(* menu_search)(struct Menu *menu, regex_t *rx, int line)
Search a menu for a item matching a regex.
Definition: mutt_menu.h:129
Config has changed.
Definition: notify_type.h:34
#define MUTT_SEL_MAILBOX
Select a mailbox.
Definition: browser.h:45
char * title
Title of this menu.
Definition: mutt_menu.h:84
struct Connection * conn
Definition: lib.h:104
static void browser_highlight_default(struct BrowserState *state, struct Menu *menu)
Decide which browser item should be highlighted.
Definition: browser.c:984
static int examine_directory(struct Menu *menu, struct BrowserState *state, const char *d, const char *prefix)
Get list of all files/newsgroups with mask.
Definition: browser.c:711
enum MailboxType mx_path_probe(const char *path)
Find a mailbox that understands a path.
Definition: mx.c:1310
void mutt_buffer_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:341
const char * group_index_format_str(char *buf, size_t buflen, size_t col, int cols, char op, const char *src, const char *prec, const char *if_str, const char *else_str, unsigned long data, MuttFormatFlags flags)
Format a string for the newsgroup menu - Implements format_t.
Definition: browse.c:51
MailboxType
Supported mailbox formats.
Definition: mailbox.h:42
void mutt_format_s(char *buf, size_t buflen, const char *prec, const char *s)
Format a simple string.
Definition: curs_lib.c:1217
bool deleted
Definition: lib.h:152
WHERE char * C_Spoolfile
Config: Inbox.
Definition: globals.h:144
Sort by the number of unread emails.
Definition: sort2.h:66
void mutt_browser_select_dir(const char *f)
Remember the last directory selected.
Definition: browser.c:1136
static void init_menu(struct BrowserState *state, struct Menu *menu, char *title, size_t titlelen, bool mailbox)
Set up a new menu.
Definition: browser.c:1010
#define REDRAW_INDEX
Redraw the index.
Definition: mutt_menu.h:42
char * mutt_str_strdup(const char *str)
Copy a string, safely.
Definition: string.c:380
void imap_clean_path(char *path, size_t plen)
Cleans an IMAP path using imap_fix_path.
Definition: util.c:333
void mutt_window_reflow(struct MuttWindow *win)
Resize a Window and its children.
Definition: mutt_window.c:346
anum_t unread
Definition: lib.h:148
#define mutt_error(...)
Definition: logging.h:84
bool C_BrowserAbbreviateMailboxes
Config: Abbreviate mailboxes using &#39;~&#39; and &#39;=&#39; in the browser.
Definition: browser.c:70
WHERE struct Regex * C_Mask
Config: Only display files/dirs matching this regex in the browser.
Definition: globals.h:173
int imap_browse(const char *path, struct BrowserState *state)
IMAP hook into the folder browser.
Definition: browse.c:184
WHERE char * C_DateFormat
Config: strftime format string for the d expando.
Definition: globals.h:106
Sort by the folder&#39;s description.
Definition: sort2.h:70
bool mutt_regex_match(const struct Regex *regex, const char *str)
Shorthand to mutt_regex_capture()
Definition: regex.c:610
Connection Library.
void ** groups_list
Definition: lib.h:102
#define FREE(x)
Definition: memory.h:40
size_t mutt_buffer_concat_path(struct Buffer *buf, const char *dir, const char *fname)
Join a directory name and a filename.
Definition: buffer.c:374
int imap_mailbox_rename(const char *path)
Rename a mailbox.
Definition: browse.c:434
void * data
Extra data for the current menu.
Definition: mutt_menu.h:86
int mutt_dlg_browser_observer(struct NotifyCallback *nc)
Listen for config changes affecting the Browser menu - Implements observer_t()
Definition: browser.c:1151
Mapping between user-readable string and a constant.
Definition: mapping.h:29
struct MuttWindowList children
Children Windows.
Definition: mutt_window.h:99
char * C_VfolderFormat
Config: (notmuch) printf-like format string for the browser&#39;s display of virtual folders.
Definition: browser.c:76
struct NntpAccountData * CurrentNewsSrv
Current NNTP news server.
Definition: nntp.c:79
#define MUTT_FORMAT_OPTIONAL
Allow optional field processing.
Definition: format_flags.h:33
Sort by number of emails in a folder.
Definition: sort2.h:65
List of Mailboxes.
Definition: mailbox.h:144
static int browser_compare(const void *a, const void *b)
Sort the items in the browser.
Definition: browser.c:281
Handling of global boolean variables.
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
bool tagged
Definition: browser.h:86
Miscellaneous functions for sending an email.
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:38
void mutt_body_free(struct Body **ptr)
Free a Body.
Definition: body.c:57
#define TAILQ_EMPTY(head)
Definition: queue.h:714
int nntp_newsrc_update(struct NntpAccountData *adata)
Update .newsrc file.
Definition: newsrc.c:445
int current
Current entry.
Definition: mutt_menu.h:87
char * pattern
printable version
Definition: regex3.h:59
bool local
folder is on local filesystem
Definition: browser.h:85
static void init_state(struct BrowserState *state, struct Menu *menu)
Initialise a browser state.
Definition: browser.c:690
Sort by the date the email was sent.
Definition: sort2.h:50
struct NntpMboxData * mutt_newsgroup_catchup(struct Mailbox *m, struct NntpAccountData *adata, char *group)
Catchup newsgroup.
Definition: newsrc.c:1297
struct MuttWindow * win_index
Definition: mutt_menu.h:95
Convenience wrapper for the library headers.
bool inferiors
Definition: browser.h:82
#define SORT_REVERSE
Reverse the order of the sort.
Definition: sort2.h:86
char * mutt_compile_help(char *buf, size_t buflen, enum MenuType menu, const struct Mapping *items)
Create the text for the help menu.
Definition: help.c:116
char * C_GroupIndexFormat
Config: (nntp) printf-like format string for the browser&#39;s display of newsgroups. ...
Definition: browser.c:72
int mutt_str_strcoll(const char *a, const char *b)
Collate two strings (compare using locale), safely.
Definition: string.c:702
Window wants as much space as possible.
Definition: mutt_window.h:43
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:252
&#39;IMAP&#39; Mailbox type
Definition: mailbox.h:52
bool imap_browse
Definition: browser.h:101
#define N_(a)
Definition: message.h:32
Index Bar containing status info about the Index.
Definition: mutt_window.h:73
enum WindowType type
Window type, e.g. WT_SIDEBAR.
Definition: mutt_window.h:101
const char * name
Name of config item that changed.
Definition: subset.h:71
void mutt_get_parent_path(const char *path, char *buf, size_t buflen)
Find the parent of a path (or mailbox)
Definition: muttlib.c:1637
WHERE bool OptNews
(pseudo) used to change reader mode
Definition: options.h:44
char delim
Definition: browser.h:78
char * help
Quickref for the current menu.
Definition: mutt_menu.h:85
int mutt_str_strcmp(const char *a, const char *b)
Compare two strings, safely.
Definition: string.c:638
Sort by the email&#39;s subject.
Definition: sort2.h:53
struct FolderFile * entry
Definition: browser.h:97
Log at debug level 3.
Definition: logging.h:42
#define SORT_MASK
Mask for the sort id.
Definition: sort2.h:85
int imap_subscribe(char *path, bool subscribe)
Subscribe to a mailbox.
Definition: imap.c:1404
void mutt_buffer_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition: buffer.c:265
int mutt_view_attachment(FILE *fp, struct Body *a, enum ViewAttachMode mode, struct Email *e, struct AttachCtx *actx, struct MuttWindow *win)
View an attachment.
Definition: mutt_attach.c:415
struct Mailbox * mailbox
Mailbox in the list.
Definition: mailbox.h:146
struct Url * url_parse(const char *src)
Fill in Url.
Definition: url.c:199
bool has_new_mail
true if mailbox has "new mail"
Definition: browser.h:73
uint8_t SelectFileFlags
Flags for mutt_select_file(), e.g. MUTT_SEL_MAILBOX.
Definition: browser.h:43