NeoMutt  2021-02-05-666-ge300cd
Teaching an old dog new tricks
DOXYGEN
browser.c
Go to the documentation of this file.
1 
71 #include "config.h"
72 #include <dirent.h>
73 #include <errno.h>
74 #include <grp.h>
75 #include <limits.h>
76 #include <locale.h>
77 #include <pwd.h>
78 #include <stdbool.h>
79 #include <stdio.h>
80 #include <string.h>
81 #include <sys/stat.h>
82 #include "mutt/lib.h"
83 #include "config/lib.h"
84 #include "email/lib.h"
85 #include "core/lib.h"
86 #include "conn/lib.h"
87 #include "gui/lib.h"
88 #include "mutt.h"
89 #include "browser.h"
90 #include "menu/lib.h"
91 #include "question/lib.h"
92 #include "send/lib.h"
93 #include "format_flags.h"
94 #include "mutt_attach.h"
95 #include "mutt_globals.h"
96 #include "mutt_mailbox.h"
97 #include "muttlib.h"
98 #include "mx.h"
99 #include "opcodes.h"
100 #include "options.h"
101 #ifdef USE_IMAP
102 #include "imap/lib.h"
103 #endif
104 #ifdef USE_NNTP
105 #include "nntp/lib.h"
106 #include "nntp/adata.h" // IWYU pragma: keep
107 #include "nntp/mdata.h" // IWYU pragma: keep
108 #endif
109 
111 static const struct Mapping FolderHelp[] = {
112  // clang-format off
113  { N_("Exit"), OP_EXIT },
114  { N_("Chdir"), OP_CHANGE_DIRECTORY },
115  { N_("Goto"), OP_BROWSER_GOTO_FOLDER },
116  { N_("Mask"), OP_ENTER_MASK },
117  { N_("Help"), OP_HELP },
118  { NULL, 0 },
119  // clang-format on
120 };
121 
122 #ifdef USE_NNTP
123 static const struct Mapping FolderNewsHelp[] = {
125  // clang-format off
126  { N_("Exit"), OP_EXIT },
127  { N_("List"), OP_TOGGLE_MAILBOXES },
128  { N_("Subscribe"), OP_BROWSER_SUBSCRIBE },
129  { N_("Unsubscribe"), OP_BROWSER_UNSUBSCRIBE },
130  { N_("Catchup"), OP_CATCHUP },
131  { N_("Mask"), OP_ENTER_MASK },
132  { N_("Help"), OP_HELP },
133  { NULL, 0 },
134  // clang-format on
135 };
136 #endif
137 
138 static struct Buffer LastDir = { 0 };
139 static struct Buffer LastDirBackup = { 0 };
140 
146 static void init_lastdir(void)
147 {
148  static bool done = false;
149  if (!done)
150  {
151  mutt_buffer_alloc(&LastDir, PATH_MAX);
152  mutt_buffer_alloc(&LastDirBackup, PATH_MAX);
153  done = true;
154  }
155 }
156 
161 {
162  mutt_buffer_dealloc(&LastDir);
163  mutt_buffer_dealloc(&LastDirBackup);
164 }
165 
172 static void destroy_state(struct BrowserState *state)
173 {
174  struct FolderFile *ff = NULL;
175  ARRAY_FOREACH(ff, &state->entry)
176  {
177  FREE(&ff->name);
178  FREE(&ff->desc);
179  }
180  ARRAY_FREE(&state->entry);
181 
182 #ifdef USE_IMAP
183  FREE(&state->folder);
184 #endif
185 }
186 
190 static int browser_compare_subject(const void *a, const void *b)
191 {
192  const struct FolderFile *pa = (const struct FolderFile *) a;
193  const struct FolderFile *pb = (const struct FolderFile *) b;
194 
195  /* inbox should be sorted ahead of its siblings */
196  int r = mutt_inbox_cmp(pa->name, pb->name);
197  if (r == 0)
198  r = mutt_str_coll(pa->name, pb->name);
199  const short c_sort_browser = cs_subset_sort(NeoMutt->sub, "sort_browser");
200  return (c_sort_browser & SORT_REVERSE) ? -r : r;
201 }
202 
208 static int browser_compare_order(const void *a, const void *b)
209 {
210  const struct FolderFile *pa = (const struct FolderFile *) a;
211  const struct FolderFile *pb = (const struct FolderFile *) b;
212 
213  const short c_sort_browser = cs_subset_sort(NeoMutt->sub, "sort_browser");
214  return ((c_sort_browser & SORT_REVERSE) ? -1 : 1) * (pa->gen - pb->gen);
215 }
216 
220 static int browser_compare_desc(const void *a, const void *b)
221 {
222  const struct FolderFile *pa = (const struct FolderFile *) a;
223  const struct FolderFile *pb = (const struct FolderFile *) b;
224 
225  int r = mutt_str_coll(pa->desc, pb->desc);
226 
227  const short c_sort_browser = cs_subset_sort(NeoMutt->sub, "sort_browser");
228  return (c_sort_browser & SORT_REVERSE) ? -r : r;
229 }
230 
234 static int browser_compare_date(const void *a, const void *b)
235 {
236  const struct FolderFile *pa = (const struct FolderFile *) a;
237  const struct FolderFile *pb = (const struct FolderFile *) b;
238 
239  int r = pa->mtime - pb->mtime;
240 
241  const short c_sort_browser = cs_subset_sort(NeoMutt->sub, "sort_browser");
242  return (c_sort_browser & SORT_REVERSE) ? -r : r;
243 }
244 
248 static int browser_compare_size(const void *a, const void *b)
249 {
250  const struct FolderFile *pa = (const struct FolderFile *) a;
251  const struct FolderFile *pb = (const struct FolderFile *) b;
252 
253  int r = pa->size - pb->size;
254 
255  const short c_sort_browser = cs_subset_sort(NeoMutt->sub, "sort_browser");
256  return (c_sort_browser & SORT_REVERSE) ? -r : r;
257 }
258 
262 static int browser_compare_count(const void *a, const void *b)
263 {
264  const struct FolderFile *pa = (const struct FolderFile *) a;
265  const struct FolderFile *pb = (const struct FolderFile *) b;
266 
267  int r = 0;
268  if (pa->has_mailbox && pb->has_mailbox)
269  r = pa->msg_count - pb->msg_count;
270  else if (pa->has_mailbox)
271  r = -1;
272  else
273  r = 1;
274 
275  const short c_sort_browser = cs_subset_sort(NeoMutt->sub, "sort_browser");
276  return (c_sort_browser & SORT_REVERSE) ? -r : r;
277 }
278 
282 static int browser_compare_count_new(const void *a, const void *b)
283 {
284  const struct FolderFile *pa = (const struct FolderFile *) a;
285  const struct FolderFile *pb = (const struct FolderFile *) b;
286 
287  int r = 0;
288  if (pa->has_mailbox && pb->has_mailbox)
289  r = pa->msg_unread - pb->msg_unread;
290  else if (pa->has_mailbox)
291  r = -1;
292  else
293  r = 1;
294 
295  const short c_sort_browser = cs_subset_sort(NeoMutt->sub, "sort_browser");
296  return (c_sort_browser & SORT_REVERSE) ? -r : r;
297 }
298 
306 static int browser_compare(const void *a, const void *b)
307 {
308  const struct FolderFile *pa = (const struct FolderFile *) a;
309  const struct FolderFile *pb = (const struct FolderFile *) b;
310 
311  if ((mutt_str_coll(pa->desc, "../") == 0) || (mutt_str_coll(pa->desc, "..") == 0))
312  return -1;
313  if ((mutt_str_coll(pb->desc, "../") == 0) || (mutt_str_coll(pb->desc, "..") == 0))
314  return 1;
315 
316  const short c_sort_browser = cs_subset_sort(NeoMutt->sub, "sort_browser");
317  switch (c_sort_browser & SORT_MASK)
318  {
319  case SORT_COUNT:
320  return browser_compare_count(a, b);
321  case SORT_DATE:
322  return browser_compare_date(a, b);
323  case SORT_DESC:
324  return browser_compare_desc(a, b);
325  case SORT_SIZE:
326  return browser_compare_size(a, b);
327  case SORT_UNREAD:
328  return browser_compare_count_new(a, b);
329  case SORT_SUBJECT:
330  return browser_compare_subject(a, b);
331  default:
332  case SORT_ORDER:
333  return browser_compare_order(a, b);
334  }
335 }
336 
344 static void browser_sort(struct BrowserState *state)
345 {
346  const short c_sort_browser = cs_subset_sort(NeoMutt->sub, "sort_browser");
347  switch (c_sort_browser & SORT_MASK)
348  {
349 #ifdef USE_NNTP
350  case SORT_SIZE:
351  case SORT_DATE:
352  if (OptNews)
353  return;
354 #endif
355  default:
356  break;
357  }
358 
359  ARRAY_SORT(&state->entry, browser_compare);
360 }
361 
369 static bool link_is_dir(const char *folder, const char *path)
370 {
371  struct stat st;
372  bool retval = false;
373 
374  struct Buffer *fullpath = mutt_buffer_pool_get();
375  mutt_buffer_concat_path(fullpath, folder, path);
376 
377  if (stat(mutt_buffer_string(fullpath), &st) == 0)
378  retval = S_ISDIR(st.st_mode);
379 
380  mutt_buffer_pool_release(&fullpath);
381 
382  return retval;
383 }
384 
405 static const char *folder_format_str(char *buf, size_t buflen, size_t col, int cols,
406  char op, const char *src, const char *prec,
407  const char *if_str, const char *else_str,
408  intptr_t data, MuttFormatFlags flags)
409 {
410  char fn[128], fmt[128];
411  struct Folder *folder = (struct Folder *) data;
412  bool optional = (flags & MUTT_FORMAT_OPTIONAL);
413 
414  switch (op)
415  {
416  case 'C':
417  snprintf(fmt, sizeof(fmt), "%%%sd", prec);
418  snprintf(buf, buflen, fmt, folder->num + 1);
419  break;
420 
421  case 'd':
422  case 'D':
423  if (folder->ff->local)
424  {
425  bool do_locales = true;
426 
427  const char *t_fmt = NULL;
428  if (op == 'D')
429  {
430  const char *const c_date_format =
431  cs_subset_string(NeoMutt->sub, "date_format");
432  t_fmt = NONULL(c_date_format);
433  if (*t_fmt == '!')
434  {
435  t_fmt++;
436  do_locales = false;
437  }
438  }
439  else
440  {
441  static const time_t one_year = 31536000;
442  t_fmt = ((mutt_date_epoch() - folder->ff->mtime) < one_year) ?
443  "%b %d %H:%M" :
444  "%b %d %Y";
445  }
446 
447  if (!do_locales)
448  setlocale(LC_TIME, "C");
449  char date[128];
450  mutt_date_localtime_format(date, sizeof(date), t_fmt, folder->ff->mtime);
451  if (!do_locales)
452  setlocale(LC_TIME, "");
453 
454  mutt_format_s(buf, buflen, prec, date);
455  }
456  else
457  mutt_format_s(buf, buflen, prec, "");
458  break;
459 
460  case 'f':
461  {
462  char *s = NULL;
463 
464  s = NONULL(folder->ff->name);
465 
466  snprintf(fn, sizeof(fn), "%s%s", s,
467  folder->ff->local ?
468  (S_ISLNK(folder->ff->mode) ?
469  "@" :
470  (S_ISDIR(folder->ff->mode) ?
471  "/" :
472  (((folder->ff->mode & S_IXUSR) != 0) ? "*" : ""))) :
473  "");
474 
475  mutt_format_s(buf, buflen, prec, fn);
476  break;
477  }
478  case 'F':
479  {
480  if (folder->ff->local)
481  {
482  char permission[11];
483  snprintf(permission, sizeof(permission), "%c%c%c%c%c%c%c%c%c%c",
484  S_ISDIR(folder->ff->mode) ? 'd' : (S_ISLNK(folder->ff->mode) ? 'l' : '-'),
485  ((folder->ff->mode & S_IRUSR) != 0) ? 'r' : '-',
486  ((folder->ff->mode & S_IWUSR) != 0) ? 'w' : '-',
487  ((folder->ff->mode & S_ISUID) != 0) ? 's' :
488  ((folder->ff->mode & S_IXUSR) != 0) ? 'x' :
489  '-',
490  ((folder->ff->mode & S_IRGRP) != 0) ? 'r' : '-',
491  ((folder->ff->mode & S_IWGRP) != 0) ? 'w' : '-',
492  ((folder->ff->mode & S_ISGID) != 0) ? 's' :
493  ((folder->ff->mode & S_IXGRP) != 0) ? 'x' :
494  '-',
495  ((folder->ff->mode & S_IROTH) != 0) ? 'r' : '-',
496  ((folder->ff->mode & S_IWOTH) != 0) ? 'w' : '-',
497  ((folder->ff->mode & S_ISVTX) != 0) ? 't' :
498  ((folder->ff->mode & S_IXOTH) != 0) ? 'x' :
499  '-');
500  mutt_format_s(buf, buflen, prec, permission);
501  }
502 #ifdef USE_IMAP
503  else if (folder->ff->imap)
504  {
505  char permission[11];
506  /* mark folders with subfolders AND mail */
507  snprintf(permission, sizeof(permission), "IMAP %c",
508  (folder->ff->inferiors && folder->ff->selectable) ? '+' : ' ');
509  mutt_format_s(buf, buflen, prec, permission);
510  }
511 #endif
512  else
513  mutt_format_s(buf, buflen, prec, "");
514  break;
515  }
516 
517  case 'g':
518  if (folder->ff->local)
519  {
520  struct group *gr = getgrgid(folder->ff->gid);
521  if (gr)
522  mutt_format_s(buf, buflen, prec, gr->gr_name);
523  else
524  {
525  snprintf(fmt, sizeof(fmt), "%%%sld", prec);
526  snprintf(buf, buflen, fmt, folder->ff->gid);
527  }
528  }
529  else
530  mutt_format_s(buf, buflen, prec, "");
531  break;
532 
533  case 'i':
534  {
535  char *s = NULL;
536  if (folder->ff->desc)
537  s = folder->ff->desc;
538  else
539  s = folder->ff->name;
540 
541  snprintf(fn, sizeof(fn), "%s%s", s,
542  folder->ff->local ?
543  (S_ISLNK(folder->ff->mode) ?
544  "@" :
545  (S_ISDIR(folder->ff->mode) ?
546  "/" :
547  (((folder->ff->mode & S_IXUSR) != 0) ? "*" : ""))) :
548  "");
549 
550  mutt_format_s(buf, buflen, prec, fn);
551  break;
552  }
553 
554  case 'l':
555  if (folder->ff->local)
556  {
557  snprintf(fmt, sizeof(fmt), "%%%sd", prec);
558  snprintf(buf, buflen, fmt, folder->ff->nlink);
559  }
560  else
561  mutt_format_s(buf, buflen, prec, "");
562  break;
563 
564  case 'm':
565  if (!optional)
566  {
567  if (folder->ff->has_mailbox)
568  {
569  snprintf(fmt, sizeof(fmt), "%%%sd", prec);
570  snprintf(buf, buflen, fmt, folder->ff->msg_count);
571  }
572  else
573  mutt_format_s(buf, buflen, prec, "");
574  }
575  else if (folder->ff->msg_count == 0)
576  optional = false;
577  break;
578 
579  case 'N':
580  snprintf(fmt, sizeof(fmt), "%%%sc", prec);
581  snprintf(buf, buflen, fmt, folder->ff->has_new_mail ? 'N' : ' ');
582  break;
583 
584  case 'n':
585  if (!optional)
586  {
587  if (folder->ff->has_mailbox)
588  {
589  snprintf(fmt, sizeof(fmt), "%%%sd", prec);
590  snprintf(buf, buflen, fmt, folder->ff->msg_unread);
591  }
592  else
593  mutt_format_s(buf, buflen, prec, "");
594  }
595  else if (folder->ff->msg_unread == 0)
596  optional = false;
597  break;
598 
599  case 's':
600  if (folder->ff->local)
601  {
602  mutt_str_pretty_size(fn, sizeof(fn), folder->ff->size);
603  snprintf(fmt, sizeof(fmt), "%%%ss", prec);
604  snprintf(buf, buflen, fmt, fn);
605  }
606  else
607  mutt_format_s(buf, buflen, prec, "");
608  break;
609 
610  case 't':
611  snprintf(fmt, sizeof(fmt), "%%%sc", prec);
612  snprintf(buf, buflen, fmt, folder->ff->tagged ? '*' : ' ');
613  break;
614 
615  case 'u':
616  if (folder->ff->local)
617  {
618  struct passwd *pw = getpwuid(folder->ff->uid);
619  if (pw)
620  mutt_format_s(buf, buflen, prec, pw->pw_name);
621  else
622  {
623  snprintf(fmt, sizeof(fmt), "%%%sld", prec);
624  snprintf(buf, buflen, fmt, folder->ff->uid);
625  }
626  }
627  else
628  mutt_format_s(buf, buflen, prec, "");
629  break;
630 
631  default:
632  snprintf(fmt, sizeof(fmt), "%%%sc", prec);
633  snprintf(buf, buflen, fmt, op);
634  break;
635  }
636 
637  if (optional)
638  {
639  mutt_expando_format(buf, buflen, col, cols, if_str, folder_format_str, data,
641  }
642  else if (flags & MUTT_FORMAT_OPTIONAL)
643  {
644  mutt_expando_format(buf, buflen, col, cols, else_str, folder_format_str,
645  data, MUTT_FORMAT_NO_FLAGS);
646  }
647 
648  /* We return the format string, unchanged */
649  return src;
650 }
651 
662 static void add_folder(struct Menu *menu, struct BrowserState *state,
663  const char *name, const char *desc, const struct stat *s,
664  struct Mailbox *m, void *data)
665 {
666  if ((!menu || state->is_mailbox_list) && m && (m->flags & MB_HIDDEN))
667  {
668  return;
669  }
670 
671  struct FolderFile ff = { 0 };
672 
673  if (s)
674  {
675  ff.mode = s->st_mode;
676  ff.mtime = s->st_mtime;
677  ff.size = s->st_size;
678  ff.gid = s->st_gid;
679  ff.uid = s->st_uid;
680  ff.nlink = s->st_nlink;
681  ff.local = true;
682  }
683  else
684  ff.local = false;
685 
686  if (m)
687  {
688  ff.has_mailbox = true;
689  ff.gen = m->gen;
690  ff.has_new_mail = m->has_new;
691  ff.msg_count = m->msg_count;
692  ff.msg_unread = m->msg_unread;
693  }
694 
695  ff.name = mutt_str_dup(name);
696  ff.desc = mutt_str_dup(desc ? desc : name);
697 #ifdef USE_IMAP
698  ff.imap = false;
699 #endif
700 #ifdef USE_NNTP
701  if (OptNews)
702  ff.nd = data;
703 #endif
704 
705  ARRAY_ADD(&state->entry, ff);
706 }
707 
713 static void init_state(struct BrowserState *state, struct Menu *menu)
714 {
715  ARRAY_INIT(&state->entry);
716  ARRAY_RESERVE(&state->entry, 256);
717 #ifdef USE_IMAP
718  state->imap_browse = false;
719 #endif
720  if (menu)
721  menu->mdata = &state->entry;
722 }
723 
734 static int examine_directory(struct Mailbox *m, struct Menu *menu,
735  struct BrowserState *state, const char *d, const char *prefix)
736 {
737  int rc = -1;
738  struct Buffer *buf = mutt_buffer_pool_get();
739 #ifdef USE_NNTP
740  if (OptNews)
741  {
743 
744  init_state(state, menu);
745 
746  for (unsigned int i = 0; i < adata->groups_num; i++)
747  {
748  struct NntpMboxData *mdata = adata->groups_list[i];
749  if (!mdata)
750  continue;
751  if (prefix && *prefix && !mutt_str_startswith(mdata->group, prefix))
752  continue;
753  const struct Regex *c_mask = cs_subset_regex(NeoMutt->sub, "mask");
754  if (!mutt_regex_match(c_mask, mdata->group))
755  {
756  continue;
757  }
758  add_folder(menu, state, mdata->group, NULL, NULL, NULL, mdata);
759  }
760  }
761  else
762 #endif /* USE_NNTP */
763  {
764  struct stat s;
765  DIR *dp = NULL;
766  struct dirent *de = NULL;
767 
768  while (stat(d, &s) == -1)
769  {
770  if (errno == ENOENT)
771  {
772  /* The last used directory is deleted, try to use the parent dir. */
773  char *c = strrchr(d, '/');
774 
775  if (c && (c > d))
776  {
777  *c = '\0';
778  continue;
779  }
780  }
781  mutt_perror(d);
782  goto ed_out;
783  }
784 
785  if (!S_ISDIR(s.st_mode))
786  {
787  mutt_error(_("%s is not a directory"), d);
788  goto ed_out;
789  }
790 
791  if (m)
792  mutt_mailbox_check(m, 0);
793 
794  dp = opendir(d);
795  if (!dp)
796  {
797  mutt_perror(d);
798  goto ed_out;
799  }
800 
801  init_state(state, menu);
802 
803  struct MailboxList ml = STAILQ_HEAD_INITIALIZER(ml);
805  while ((de = readdir(dp)))
806  {
807  if (mutt_str_equal(de->d_name, "."))
808  continue; /* we don't need . */
809 
810  if (prefix && *prefix && !mutt_str_startswith(de->d_name, prefix))
811  {
812  continue;
813  }
814  const struct Regex *c_mask = cs_subset_regex(NeoMutt->sub, "mask");
815  if (!mutt_regex_match(c_mask, de->d_name))
816  {
817  continue;
818  }
819 
820  mutt_buffer_concat_path(buf, d, de->d_name);
821  if (lstat(mutt_buffer_string(buf), &s) == -1)
822  continue;
823 
824  /* No size for directories or symlinks */
825  if (S_ISDIR(s.st_mode) || S_ISLNK(s.st_mode))
826  s.st_size = 0;
827  else if (!S_ISREG(s.st_mode))
828  continue;
829 
830  struct MailboxNode *np = NULL;
831  STAILQ_FOREACH(np, &ml, entries)
832  {
834  break;
835  }
836 
837  if (np && m && mutt_str_equal(np->mailbox->realpath, m->realpath))
838  {
839  np->mailbox->msg_count = m->msg_count;
840  np->mailbox->msg_unread = m->msg_unread;
841  }
842  add_folder(menu, state, de->d_name, NULL, &s, np ? np->mailbox : NULL, NULL);
843  }
845  closedir(dp);
846  }
847  browser_sort(state);
848  rc = 0;
849 ed_out:
851  return rc;
852 }
853 
862 static int examine_mailboxes(struct Mailbox *m, struct Menu *menu, struct BrowserState *state)
863 {
864  struct stat s;
865  struct Buffer *md = NULL;
866  struct Buffer *mailbox = NULL;
867 
868 #ifdef USE_NNTP
869  if (OptNews)
870  {
872 
873  init_state(state, menu);
874 
875  for (unsigned int i = 0; i < adata->groups_num; i++)
876  {
877  const bool c_show_only_unread =
878  cs_subset_bool(NeoMutt->sub, "show_only_unread");
879  struct NntpMboxData *mdata = adata->groups_list[i];
880  if (mdata && (mdata->has_new_mail ||
881  (mdata->subscribed && (mdata->unread || !c_show_only_unread))))
882  {
883  add_folder(menu, state, mdata->group, NULL, NULL, NULL, mdata);
884  }
885  }
886  }
887  else
888 #endif
889  {
890  init_state(state, menu);
891 
893  return -1;
894  mailbox = mutt_buffer_pool_get();
895  md = mutt_buffer_pool_get();
896 
897  mutt_mailbox_check(m, 0);
898 
899  struct MailboxList ml = STAILQ_HEAD_INITIALIZER(ml);
901  struct MailboxNode *np = NULL;
902  STAILQ_FOREACH(np, &ml, entries)
903  {
904  if (!np->mailbox)
905  continue;
906 
907  if (m && mutt_str_equal(np->mailbox->realpath, m->realpath))
908  {
909  np->mailbox->msg_count = m->msg_count;
910  np->mailbox->msg_unread = m->msg_unread;
911  }
912 
913  mutt_buffer_strcpy(mailbox, mailbox_path(np->mailbox));
914  const bool c_browser_abbreviate_mailboxes =
915  cs_subset_bool(NeoMutt->sub, "browser_abbreviate_mailboxes");
916  if (c_browser_abbreviate_mailboxes)
918 
919  switch (np->mailbox->type)
920  {
921  case MUTT_IMAP:
922  case MUTT_POP:
923  add_folder(menu, state, mutt_buffer_string(mailbox),
924  np->mailbox->name, NULL, np->mailbox, NULL);
925  continue;
926  case MUTT_NOTMUCH:
927  case MUTT_NNTP:
928  add_folder(menu, state, mailbox_path(np->mailbox), np->mailbox->name,
929  NULL, np->mailbox, NULL);
930  continue;
931  default: /* Continue */
932  break;
933  }
934 
935  if (lstat(mailbox_path(np->mailbox), &s) == -1)
936  continue;
937 
938  if ((!S_ISREG(s.st_mode)) && (!S_ISDIR(s.st_mode)) && (!S_ISLNK(s.st_mode)))
939  continue;
940 
941  if (np->mailbox->type == MUTT_MAILDIR)
942  {
943  struct stat st2;
944 
945  mutt_buffer_printf(md, "%s/new", mailbox_path(np->mailbox));
946  if (stat(mutt_buffer_string(md), &s) < 0)
947  s.st_mtime = 0;
948  mutt_buffer_printf(md, "%s/cur", mailbox_path(np->mailbox));
949  if (stat(mutt_buffer_string(md), &st2) < 0)
950  st2.st_mtime = 0;
951  if (st2.st_mtime > s.st_mtime)
952  s.st_mtime = st2.st_mtime;
953  }
954 
955  add_folder(menu, state, mutt_buffer_string(mailbox), np->mailbox->name,
956  &s, np->mailbox, NULL);
957  }
959  }
960  browser_sort(state);
961 
962  mutt_buffer_pool_release(&mailbox);
964  return 0;
965 }
966 
970 static int select_file_search(struct Menu *menu, regex_t *rx, int line)
971 {
972  struct BrowserStateEntry *entry = menu->mdata;
973 #ifdef USE_NNTP
974  if (OptNews)
975  return regexec(rx, ARRAY_GET(entry, line)->desc, 0, NULL, 0);
976 #endif
977  struct FolderFile *ff = ARRAY_GET(entry, line);
978  char *search_on = ff->desc ? ff->desc : ff->name;
979 
980  return regexec(rx, search_on, 0, NULL, 0);
981 }
982 
986 static void folder_make_entry(struct Menu *menu, char *buf, size_t buflen, int line)
987 {
988  struct BrowserStateEntry *entry = menu->mdata;
989  struct Folder folder = {
990  .ff = ARRAY_GET(entry, line),
991  .num = line,
992  };
993 
994 #ifdef USE_NNTP
995  if (OptNews)
996  {
997  const char *const c_group_index_format =
998  cs_subset_string(NeoMutt->sub, "group_index_format");
999  mutt_expando_format(buf, buflen, 0, menu->win->state.cols,
1000  NONULL(c_group_index_format), group_index_format_str,
1001  (intptr_t) &folder, MUTT_FORMAT_ARROWCURSOR);
1002  }
1003  else
1004 #endif
1005  {
1006  const char *const c_folder_format =
1007  cs_subset_string(NeoMutt->sub, "folder_format");
1008  mutt_expando_format(buf, buflen, 0, menu->win->state.cols, NONULL(c_folder_format),
1009  folder_format_str, (intptr_t) &folder, MUTT_FORMAT_ARROWCURSOR);
1010  }
1011 }
1012 
1021 static void browser_highlight_default(struct BrowserState *state, struct Menu *menu)
1022 {
1023  menu->top = 0;
1024  /* Reset menu position to 1.
1025  * We do not risk overflow as the init_menu function changes
1026  * current if it is bigger than state->entrylen. */
1027  if (!ARRAY_EMPTY(&state->entry) &&
1028  (mutt_str_equal(ARRAY_FIRST(&state->entry)->desc, "..") ||
1029  mutt_str_equal(ARRAY_FIRST(&state->entry)->desc, "../")))
1030  {
1031  /* Skip the first entry, unless there's only one entry. */
1032  menu_set_index(menu, (menu->max > 1));
1033  }
1034  else
1035  {
1036  menu_set_index(menu, 0);
1037  }
1038 }
1039 
1047 static void init_menu(struct BrowserState *state, struct Menu *menu,
1048  struct Mailbox *m, struct MuttWindow *sbar)
1049 {
1050  char title[256] = { 0 };
1051  menu->max = ARRAY_SIZE(&state->entry);
1052 
1053  int index = menu_get_index(menu);
1054  if (index >= menu->max)
1055  menu_set_index(menu, menu->max - 1);
1056  if (index < 0)
1057  menu_set_index(menu, 0);
1058  if (menu->top > index)
1059  menu->top = 0;
1060 
1061  menu->tagged = 0;
1062 
1063 #ifdef USE_NNTP
1064  if (OptNews)
1065  {
1066  if (state->is_mailbox_list)
1067  snprintf(title, sizeof(title), _("Subscribed newsgroups"));
1068  else
1069  {
1070  snprintf(title, sizeof(title), _("Newsgroups on server [%s]"),
1072  }
1073  }
1074  else
1075 #endif
1076  {
1077  if (state->is_mailbox_list)
1078  {
1079  snprintf(title, sizeof(title), _("Mailboxes [%d]"), mutt_mailbox_check(m, 0));
1080  }
1081  else
1082  {
1083  struct Buffer *path = mutt_buffer_pool_get();
1084  mutt_buffer_copy(path, &LastDir);
1086  const struct Regex *c_mask = cs_subset_regex(NeoMutt->sub, "mask");
1087 #ifdef USE_IMAP
1088  const bool c_imap_list_subscribed =
1089  cs_subset_bool(NeoMutt->sub, "imap_list_subscribed");
1090  if (state->imap_browse && c_imap_list_subscribed)
1091  {
1092  snprintf(title, sizeof(title), _("Subscribed [%s], File mask: %s"),
1093  mutt_buffer_string(path), NONULL(c_mask ? c_mask->pattern : NULL));
1094  }
1095  else
1096 #endif
1097  {
1098  snprintf(title, sizeof(title), _("Directory [%s], File mask: %s"),
1099  mutt_buffer_string(path), NONULL(c_mask ? c_mask->pattern : NULL));
1100  }
1101  mutt_buffer_pool_release(&path);
1102  }
1103  }
1104  sbar_set_title(sbar, title);
1105 
1106  /* Browser tracking feature.
1107  * The goal is to highlight the good directory if LastDir is the parent dir
1108  * of LastDirBackup (this occurs mostly when one hit "../"). It should also work
1109  * properly when the user is in examine_mailboxes-mode. */
1110  if (mutt_str_startswith(mutt_buffer_string(&LastDirBackup), mutt_buffer_string(&LastDir)))
1111  {
1112  char target_dir[PATH_MAX] = { 0 };
1113 
1114 #ifdef USE_IMAP
1115  /* Check what kind of dir LastDirBackup is. */
1116  if (imap_path_probe(mutt_buffer_string(&LastDirBackup), NULL) == MUTT_IMAP)
1117  {
1118  mutt_str_copy(target_dir, mutt_buffer_string(&LastDirBackup), sizeof(target_dir));
1119  imap_clean_path(target_dir, sizeof(target_dir));
1120  }
1121  else
1122 #endif
1123  mutt_str_copy(target_dir, strrchr(mutt_buffer_string(&LastDirBackup), '/') + 1,
1124  sizeof(target_dir));
1125 
1126  /* If we get here, it means that LastDir is the parent directory of
1127  * LastDirBackup. I.e., we're returning from a subdirectory, and we want
1128  * to position the cursor on the directory we're returning from. */
1129  bool matched = false;
1130  struct FolderFile *ff = NULL;
1131  ARRAY_FOREACH(ff, &state->entry)
1132  {
1133  if (mutt_str_equal(ff->name, target_dir))
1134  {
1135  menu_set_index(menu, ARRAY_FOREACH_IDX);
1136  matched = true;
1137  break;
1138  }
1139  }
1140  if (!matched)
1141  browser_highlight_default(state, menu);
1142  }
1143  else
1144  browser_highlight_default(state, menu);
1145 
1147 }
1148 
1152 static int file_tag(struct Menu *menu, int sel, int act)
1153 {
1154  struct BrowserStateEntry *entry = menu->mdata;
1155  struct FolderFile *ff = ARRAY_GET(entry, sel);
1156  if (S_ISDIR(ff->mode) ||
1157  (S_ISLNK(ff->mode) && link_is_dir(mutt_buffer_string(&LastDir), ff->name)))
1158  {
1159  mutt_error(_("Can't attach a directory"));
1160  return 0;
1161  }
1162 
1163  bool ot = ff->tagged;
1164  ff->tagged = ((act >= 0) ? act : !ff->tagged);
1165 
1166  return ff->tagged - ot;
1167 }
1168 
1173 {
1174  if ((nc->event_type != NT_CONFIG) || !nc->global_data || !nc->event_data)
1175  return -1;
1176 
1177  struct EventConfig *ev_c = nc->event_data;
1178 
1179  if (!mutt_str_equal(ev_c->name, "browser_abbreviate_mailboxes") &&
1180  !mutt_str_equal(ev_c->name, "date_format") && !mutt_str_equal(ev_c->name, "folder") &&
1181  !mutt_str_equal(ev_c->name, "folder_format") &&
1182  !mutt_str_equal(ev_c->name, "group_index_format") &&
1183  !mutt_str_equal(ev_c->name, "sort_browser"))
1184  {
1185  return 0;
1186  }
1187 
1188  struct Menu *menu = nc->global_data;
1190  mutt_debug(LL_DEBUG5, "config done, request WA_RECALC, MENU_REDRAW_FULL\n");
1191 
1192  return 0;
1193 }
1194 
1203 {
1204  if ((nc->event_type != NT_WINDOW) || !nc->global_data || !nc->event_data)
1205  return -1;
1206 
1207  if (nc->event_subtype != NT_WINDOW_DELETE)
1208  return 0;
1209 
1210  struct MuttWindow *win_menu = nc->global_data;
1211  struct EventWindow *ev_w = nc->event_data;
1212  if (ev_w->win != win_menu)
1213  return 0;
1214 
1215  struct Menu *menu = win_menu->wdata;
1216 
1219 
1220  mutt_debug(LL_DEBUG5, "window delete done\n");
1221  return 0;
1222 }
1223 
1234 void mutt_browser_select_dir(const char *f)
1235 {
1236  init_lastdir();
1237 
1238  mutt_buffer_strcpy(&LastDirBackup, f);
1239 
1240  /* Method that will fetch the parent path depending on the type of the path. */
1241  char buf[PATH_MAX];
1242  mutt_get_parent_path(mutt_buffer_string(&LastDirBackup), buf, sizeof(buf));
1243  mutt_buffer_strcpy(&LastDir, buf);
1244 }
1245 
1255  struct Mailbox *m, char ***files, int *numfiles)
1256 {
1257  struct BrowserState state = { { 0 } };
1258  struct Menu *menu = NULL;
1259  struct MuttWindow *dlg = NULL;
1260  bool kill_prefix = false;
1261  bool multiple = (flags & MUTT_SEL_MULTI);
1262  bool folder = (flags & MUTT_SEL_FOLDER);
1263  state.is_mailbox_list = (flags & MUTT_SEL_MAILBOX) && folder;
1264 
1265  /* Keeps in memory the directory we were in when hitting '='
1266  * to go directly to $folder (`$folder`) */
1267  char goto_swapper[PATH_MAX] = { 0 };
1268 
1269  struct Buffer *OldLastDir = mutt_buffer_pool_get();
1270  struct Buffer *tmp = mutt_buffer_pool_get();
1271  struct Buffer *buf = mutt_buffer_pool_get();
1272  struct Buffer *prefix = mutt_buffer_pool_get();
1273 
1274  init_lastdir();
1275 
1276 #ifdef USE_NNTP
1277  if (OptNews)
1278  {
1279  if (mutt_buffer_is_empty(file))
1280  {
1282 
1283  /* default state for news reader mode is browse subscribed newsgroups */
1284  state.is_mailbox_list = false;
1285  for (size_t i = 0; i < adata->groups_num; i++)
1286  {
1287  struct NntpMboxData *mdata = adata->groups_list[i];
1288  if (mdata && mdata->subscribed)
1289  {
1290  state.is_mailbox_list = true;
1291  break;
1292  }
1293  }
1294  }
1295  else
1296  {
1297  mutt_buffer_copy(prefix, file);
1298  }
1299  }
1300  else
1301 #endif
1302  if (!mutt_buffer_is_empty(file))
1303  {
1305 #ifdef USE_IMAP
1306  if (imap_path_probe(mutt_buffer_string(file), NULL) == MUTT_IMAP)
1307  {
1308  init_state(&state, NULL);
1309  state.imap_browse = true;
1310  if (imap_browse(mutt_buffer_string(file), &state) == 0)
1311  {
1312  mutt_buffer_strcpy(&LastDir, state.folder);
1313  browser_sort(&state);
1314  }
1315  }
1316  else
1317  {
1318 #endif
1319  int i;
1320  for (i = mutt_buffer_len(file) - 1;
1321  (i > 0) && ((mutt_buffer_string(file))[i] != '/'); i--)
1322  {
1323  ; // do nothing
1324  }
1325 
1326  if (i > 0)
1327  {
1328  if ((mutt_buffer_string(file))[0] == '/')
1329  mutt_buffer_strcpy_n(&LastDir, mutt_buffer_string(file), i);
1330  else
1331  {
1332  mutt_path_getcwd(&LastDir);
1333  mutt_buffer_addch(&LastDir, '/');
1334  mutt_buffer_addstr_n(&LastDir, mutt_buffer_string(file), i);
1335  }
1336  }
1337  else
1338  {
1339  if ((mutt_buffer_string(file))[0] == '/')
1340  mutt_buffer_strcpy(&LastDir, "/");
1341  else
1342  mutt_path_getcwd(&LastDir);
1343  }
1344 
1345  if ((i <= 0) && (mutt_buffer_string(file)[0] != '/'))
1346  mutt_buffer_copy(prefix, file);
1347  else
1348  mutt_buffer_strcpy(prefix, mutt_buffer_string(file) + i + 1);
1349  kill_prefix = true;
1350 #ifdef USE_IMAP
1351  }
1352 #endif
1353  }
1354  else
1355  {
1356  if (!folder)
1357  mutt_path_getcwd(&LastDir);
1358  else
1359  {
1360  /* Whether we use the tracking feature of the browser depends
1361  * on which sort method we chose to use. This variable is defined
1362  * only to help readability of the code. */
1363  bool browser_track = false;
1364 
1365  const short c_sort_browser = cs_subset_sort(NeoMutt->sub, "sort_browser");
1366  switch (c_sort_browser & SORT_MASK)
1367  {
1368  case SORT_DESC:
1369  case SORT_SUBJECT:
1370  case SORT_ORDER:
1371  browser_track = true;
1372  break;
1373  }
1374 
1375  /* We use mutt_browser_select_dir to initialize the two
1376  * variables (LastDir, LastDirBackup) at the appropriate
1377  * values.
1378  *
1379  * We do it only when LastDir is not set (first pass there)
1380  * or when CurrentFolder and LastDirBackup are not the same.
1381  * This code is executed only when we list files, not when
1382  * we press up/down keys to navigate in a displayed list.
1383  *
1384  * We only do this when CurrentFolder has been set (ie, not
1385  * when listing folders on startup with "neomutt -y").
1386  *
1387  * This tracker is only used when browser_track is true,
1388  * meaning only with sort methods SUBJECT/DESC for now. */
1389  if (CurrentFolder)
1390  {
1391  if (mutt_buffer_is_empty(&LastDir))
1392  {
1393  /* If browsing in "local"-mode, than we chose to define LastDir to
1394  * MailDir */
1395  switch (mx_path_probe(CurrentFolder))
1396  {
1397  case MUTT_IMAP:
1398  case MUTT_MAILDIR:
1399  case MUTT_MBOX:
1400  case MUTT_MH:
1401  case MUTT_MMDF:
1402  {
1403  const char *const c_folder =
1404  cs_subset_string(NeoMutt->sub, "folder");
1405  const char *const c_spool_file = cs_subset_string(NeoMutt->sub, "spool_file");
1406  if (c_folder)
1407  mutt_buffer_strcpy(&LastDir, c_folder);
1408  else if (c_spool_file)
1409  mutt_browser_select_dir(c_spool_file);
1410  break;
1411  }
1412  default:
1414  break;
1415  }
1416  }
1417  else if (!mutt_str_equal(CurrentFolder, mutt_buffer_string(&LastDirBackup)))
1418  {
1420  }
1421  }
1422 
1423  /* When browser tracking feature is disabled, clear LastDirBackup */
1424  if (!browser_track)
1425  mutt_buffer_reset(&LastDirBackup);
1426  }
1427 
1428 #ifdef USE_IMAP
1429  if (!state.is_mailbox_list &&
1430  (imap_path_probe(mutt_buffer_string(&LastDir), NULL) == MUTT_IMAP))
1431  {
1432  init_state(&state, NULL);
1433  state.imap_browse = true;
1434  imap_browse(mutt_buffer_string(&LastDir), &state);
1435  browser_sort(&state);
1436  }
1437  else
1438 #endif
1439  {
1440  size_t i = mutt_buffer_len(&LastDir);
1441  while ((i > 0) && (mutt_buffer_string(&LastDir)[--i] == '/'))
1442  LastDir.data[i] = '\0';
1443  mutt_buffer_fix_dptr(&LastDir);
1444  if (mutt_buffer_is_empty(&LastDir))
1445  mutt_path_getcwd(&LastDir);
1446  }
1447  }
1448 
1449  mutt_buffer_reset(file);
1450 
1451  const struct Mapping *help_data = NULL;
1452 #ifdef USE_NNTP
1453  if (OptNews)
1454  help_data = FolderNewsHelp;
1455  else
1456 #endif
1457  help_data = FolderHelp;
1458 
1459  dlg = simple_dialog_new(MENU_FOLDER, WT_DLG_BROWSER, help_data);
1460 
1461  menu = dlg->wdata;
1462  menu->make_entry = folder_make_entry;
1463  menu->search = select_file_search;
1464  if (multiple)
1465  menu->tag = file_tag;
1466 
1467  struct MuttWindow *sbar = window_find_child(dlg, WT_STATUS_BAR);
1468 
1469  struct MuttWindow *win_menu = menu->win;
1470 
1471  // NT_COLOR is handled by the SimpleDialog
1474 
1475  if (state.is_mailbox_list)
1476  {
1477  examine_mailboxes(m, NULL, &state);
1478  }
1479  else
1480 #ifdef USE_IMAP
1481  if (!state.imap_browse)
1482 #endif
1483  {
1484  // examine_directory() calls add_folder() which needs the menu
1485  if (examine_directory(m, menu, &state, mutt_buffer_string(&LastDir),
1486  mutt_buffer_string(prefix)) == -1)
1487  {
1488  goto bail;
1489  }
1490  }
1491 
1492  init_menu(&state, menu, m, sbar);
1493  // only now do we have a valid state to attach
1494  menu->mdata = &state.entry;
1495 
1496  int last_selected_mailbox = -1;
1497 
1498  while (true)
1499  {
1500  if (state.is_mailbox_list && (last_selected_mailbox >= 0) &&
1501  (last_selected_mailbox < menu->max))
1502  {
1503  menu_set_index(menu, last_selected_mailbox);
1504  }
1505  int op = menu_loop(menu);
1506  if (op >= 0)
1507  mutt_debug(LL_DEBUG1, "Got op %s (%d)\n", OpStrings[op][0], op);
1508  int index = menu_get_index(menu);
1509  struct FolderFile *ff = ARRAY_GET(&state.entry, index);
1510  switch (op)
1511  {
1512  case OP_DESCEND_DIRECTORY:
1513  case OP_GENERIC_SELECT_ENTRY:
1514  {
1515  if (ARRAY_EMPTY(&state.entry))
1516  {
1517  mutt_error(_("No files match the file mask"));
1518  break;
1519  }
1520 
1521  if (S_ISDIR(ff->mode) ||
1522  (S_ISLNK(ff->mode) && link_is_dir(mutt_buffer_string(&LastDir), ff->name))
1523 #ifdef USE_IMAP
1524  || ff->inferiors
1525 #endif
1526  )
1527  {
1528  /* make sure this isn't a MH or maildir mailbox */
1529  if (state.is_mailbox_list)
1530  {
1531  mutt_buffer_strcpy(buf, ff->name);
1533  }
1534 #ifdef USE_IMAP
1535  else if (state.imap_browse)
1536  {
1537  mutt_buffer_strcpy(buf, ff->name);
1538  }
1539 #endif
1540  else
1541  {
1542  mutt_buffer_concat_path(buf, mutt_buffer_string(&LastDir), ff->name);
1543  }
1544 
1545  enum MailboxType type = mx_path_probe(mutt_buffer_string(buf));
1546  if ((op == OP_DESCEND_DIRECTORY) || (type == MUTT_MAILBOX_ERROR) ||
1547  (type == MUTT_UNKNOWN)
1548 #ifdef USE_IMAP
1549  || ff->inferiors
1550 #endif
1551  )
1552  {
1553  /* save the old directory */
1554  mutt_buffer_copy(OldLastDir, &LastDir);
1555 
1556  if (mutt_str_equal(ff->name, ".."))
1557  {
1558  size_t lastdirlen = mutt_buffer_len(&LastDir);
1559  if ((lastdirlen > 1) &&
1560  mutt_str_equal("..", mutt_buffer_string(&LastDir) + lastdirlen - 2))
1561  {
1562  mutt_buffer_addstr(&LastDir, "/..");
1563  }
1564  else
1565  {
1566  char *p = NULL;
1567  if (lastdirlen > 1)
1568  p = strrchr(mutt_buffer_string(&LastDir) + 1, '/');
1569 
1570  if (p)
1571  {
1572  *p = '\0';
1573  mutt_buffer_fix_dptr(&LastDir);
1574  }
1575  else
1576  {
1577  if (mutt_buffer_string(&LastDir)[0] == '/')
1578  mutt_buffer_strcpy(&LastDir, "/");
1579  else
1580  mutt_buffer_addstr(&LastDir, "/..");
1581  }
1582  }
1583  }
1584  else if (state.is_mailbox_list)
1585  {
1586  mutt_buffer_strcpy(&LastDir, ff->name);
1587  mutt_buffer_expand_path(&LastDir);
1588  }
1589 #ifdef USE_IMAP
1590  else if (state.imap_browse)
1591  {
1592  mutt_buffer_strcpy(&LastDir, ff->name);
1593  /* tack on delimiter here */
1594 
1595  /* special case "" needs no delimiter */
1596  struct Url *url = url_parse(ff->name);
1597  if (url && url->path && (ff->delim != '\0'))
1598  {
1599  mutt_buffer_addch(&LastDir, ff->delim);
1600  }
1601  url_free(&url);
1602  }
1603 #endif
1604  else
1605  {
1606  mutt_buffer_concat_path(tmp, mutt_buffer_string(&LastDir), ff->name);
1607  mutt_buffer_copy(&LastDir, tmp);
1608  }
1609 
1610  destroy_state(&state);
1611  if (kill_prefix)
1612  {
1613  mutt_buffer_reset(prefix);
1614  kill_prefix = false;
1615  }
1616  state.is_mailbox_list = false;
1617 #ifdef USE_IMAP
1618  if (state.imap_browse)
1619  {
1620  init_state(&state, NULL);
1621  state.imap_browse = true;
1622  imap_browse(mutt_buffer_string(&LastDir), &state);
1623  browser_sort(&state);
1624  menu->mdata = &state.entry;
1625  }
1626  else
1627 #endif
1628  {
1629  if (examine_directory(m, menu, &state, mutt_buffer_string(&LastDir),
1630  mutt_buffer_string(prefix)) == -1)
1631  {
1632  /* try to restore the old values */
1633  mutt_buffer_copy(&LastDir, OldLastDir);
1634  if (examine_directory(m, menu, &state, mutt_buffer_string(&LastDir),
1635  mutt_buffer_string(prefix)) == -1)
1636  {
1637  mutt_buffer_strcpy(&LastDir, NONULL(HomeDir));
1638  goto bail;
1639  }
1640  }
1641  /* resolve paths navigated from GUI */
1642  if (mutt_path_realpath(LastDir.data) == 0)
1643  break;
1644  }
1645 
1646  browser_highlight_default(&state, menu);
1647  init_menu(&state, menu, m, sbar);
1648  goto_swapper[0] = '\0';
1649  break;
1650  }
1651  }
1652  else if (op == OP_DESCEND_DIRECTORY)
1653  {
1654  mutt_error(_("%s is not a directory"), ARRAY_GET(&state.entry, index)->name);
1655  break;
1656  }
1657 
1658  if (state.is_mailbox_list || OptNews) /* USE_NNTP */
1659  {
1660  mutt_buffer_strcpy(file, ff->name);
1662  }
1663 #ifdef USE_IMAP
1664  else if (state.imap_browse)
1665  mutt_buffer_strcpy(file, ff->name);
1666 #endif
1667  else
1668  {
1669  mutt_buffer_concat_path(file, mutt_buffer_string(&LastDir), ff->name);
1670  }
1671  }
1672  /* fallthrough */
1673 
1674  case OP_EXIT:
1675 
1676  if (multiple)
1677  {
1678  char **tfiles = NULL;
1679 
1680  if (menu->tagged)
1681  {
1682  *numfiles = menu->tagged;
1683  tfiles = mutt_mem_calloc(*numfiles, sizeof(char *));
1684  size_t j = 0;
1685  ARRAY_FOREACH(ff, &state.entry)
1686  {
1687  if (ff->tagged)
1688  {
1689  mutt_buffer_concat_path(tmp, mutt_buffer_string(&LastDir), ff->name);
1691  tfiles[j++] = mutt_buffer_strdup(tmp);
1692  }
1693  }
1694  *files = tfiles;
1695  }
1696  else if (!mutt_buffer_is_empty(file)) /* no tagged entries. return selected entry */
1697  {
1698  *numfiles = 1;
1699  tfiles = mutt_mem_calloc(*numfiles, sizeof(char *));
1701  tfiles[0] = mutt_buffer_strdup(file);
1702  *files = tfiles;
1703  }
1704  }
1705 
1706  destroy_state(&state);
1707  goto bail;
1708 
1709  case OP_BROWSER_TELL:
1710  if (!ARRAY_EMPTY(&state.entry))
1711  mutt_message("%s", ARRAY_GET(&state.entry, index)->name);
1712  break;
1713 
1714 #ifdef USE_IMAP
1715  case OP_BROWSER_TOGGLE_LSUB:
1716  bool_str_toggle(NeoMutt->sub, "imap_list_subscribed", NULL);
1717 
1718  mutt_unget_event(0, OP_CHECK_NEW);
1719  break;
1720 
1721  case OP_CREATE_MAILBOX:
1722  if (!state.imap_browse)
1723  {
1724  mutt_error(_("Create is only supported for IMAP mailboxes"));
1725  break;
1726  }
1727 
1728  if (imap_mailbox_create(mutt_buffer_string(&LastDir)) == 0)
1729  {
1730  /* TODO: find a way to detect if the new folder would appear in
1731  * this window, and insert it without starting over. */
1732  destroy_state(&state);
1733  init_state(&state, NULL);
1734  state.imap_browse = true;
1735  imap_browse(mutt_buffer_string(&LastDir), &state);
1736  browser_sort(&state);
1737  menu->mdata = &state.entry;
1738  browser_highlight_default(&state, menu);
1739  init_menu(&state, menu, m, sbar);
1740  }
1741  /* else leave error on screen */
1742  break;
1743 
1744  case OP_RENAME_MAILBOX:
1745  if (!ff->imap)
1746  mutt_error(_("Rename is only supported for IMAP mailboxes"));
1747  else
1748  {
1749  if (imap_mailbox_rename(ff->name) >= 0)
1750  {
1751  destroy_state(&state);
1752  init_state(&state, NULL);
1753  state.imap_browse = true;
1754  imap_browse(mutt_buffer_string(&LastDir), &state);
1755  browser_sort(&state);
1756  menu->mdata = &state.entry;
1757  browser_highlight_default(&state, menu);
1758  init_menu(&state, menu, m, sbar);
1759  }
1760  }
1761  break;
1762 
1763  case OP_DELETE_MAILBOX:
1764  if (!ff->imap)
1765  mutt_error(_("Delete is only supported for IMAP mailboxes"));
1766  else
1767  {
1768  char msg[128];
1769 
1770  // TODO(sileht): It could be better to select INBOX instead. But I
1771  // don't want to manipulate Context/Mailboxes/mailbox->account here for now.
1772  // Let's just protect neomutt against crash for now. #1417
1773  if (mutt_str_equal(mailbox_path(m), ff->name))
1774  {
1775  mutt_error(_("Can't delete currently selected mailbox"));
1776  break;
1777  }
1778 
1779  snprintf(msg, sizeof(msg), _("Really delete mailbox \"%s\"?"), ff->name);
1780  if (mutt_yesorno(msg, MUTT_NO) == MUTT_YES)
1781  {
1782  if (imap_delete_mailbox(m, ff->name) == 0)
1783  {
1784  /* free the mailbox from the browser */
1785  FREE(&ff->name);
1786  FREE(&ff->desc);
1787  /* and move all other entries up */
1788  ARRAY_REMOVE(&state.entry, ff);
1789  mutt_message(_("Mailbox deleted"));
1790  init_menu(&state, menu, m, sbar);
1791  }
1792  else
1793  mutt_error(_("Mailbox deletion failed"));
1794  }
1795  else
1796  mutt_message(_("Mailbox not deleted"));
1797  }
1798  break;
1799 #endif
1800 
1801  case OP_GOTO_PARENT:
1802  case OP_CHANGE_DIRECTORY:
1803 
1804 #ifdef USE_NNTP
1805  if (OptNews)
1806  break;
1807 #endif
1808 
1809  mutt_buffer_copy(buf, &LastDir);
1810 #ifdef USE_IMAP
1811  if (!state.imap_browse)
1812 #endif
1813  {
1814  /* add '/' at the end of the directory name if not already there */
1815  size_t len = mutt_buffer_len(buf);
1816  if ((len > 0) && (mutt_buffer_string(&LastDir)[len - 1] != '/'))
1817  mutt_buffer_addch(buf, '/');
1818  }
1819 
1820  if (op == OP_CHANGE_DIRECTORY)
1821  {
1822  /* buf comes from the buffer pool, so defaults to size 1024 */
1823  int ret = mutt_buffer_get_field(_("Chdir to: "), buf, MUTT_FILE,
1824  false, NULL, NULL, NULL);
1825  if ((ret != 0) && mutt_buffer_is_empty(buf))
1826  break;
1827  }
1828  else if (op == OP_GOTO_PARENT)
1830 
1831  if (!mutt_buffer_is_empty(buf))
1832  {
1833  state.is_mailbox_list = false;
1835 #ifdef USE_IMAP
1836  if (imap_path_probe(mutt_buffer_string(buf), NULL) == MUTT_IMAP)
1837  {
1838  mutt_buffer_copy(&LastDir, buf);
1839  destroy_state(&state);
1840  init_state(&state, NULL);
1841  state.imap_browse = true;
1842  imap_browse(mutt_buffer_string(&LastDir), &state);
1843  browser_sort(&state);
1844  menu->mdata = &state.entry;
1845  browser_highlight_default(&state, menu);
1846  init_menu(&state, menu, m, sbar);
1847  }
1848  else
1849 #endif
1850  {
1851  if (mutt_buffer_string(buf)[0] != '/')
1852  {
1853  /* in case dir is relative, make it relative to LastDir,
1854  * not current working dir */
1856  mutt_buffer_string(buf));
1857  mutt_buffer_copy(buf, tmp);
1858  }
1859  /* Resolve path from <chdir>
1860  * Avoids buildup such as /a/b/../../c
1861  * Symlinks are always unraveled to keep code simple */
1862  if (mutt_path_realpath(buf->data) == 0)
1863  break;
1864 
1865  struct stat st;
1866  if (stat(mutt_buffer_string(buf), &st) == 0)
1867  {
1868  if (S_ISDIR(st.st_mode))
1869  {
1870  destroy_state(&state);
1871  if (examine_directory(m, menu, &state, mutt_buffer_string(buf),
1872  mutt_buffer_string(prefix)) == 0)
1873  {
1874  mutt_buffer_copy(&LastDir, buf);
1875  }
1876  else
1877  {
1878  mutt_error(_("Error scanning directory"));
1879  if (examine_directory(m, menu, &state, mutt_buffer_string(&LastDir),
1880  mutt_buffer_string(prefix)) == -1)
1881  {
1882  goto bail;
1883  }
1884  }
1885  browser_highlight_default(&state, menu);
1886  init_menu(&state, menu, m, sbar);
1887  }
1888  else
1889  mutt_error(_("%s is not a directory"), mutt_buffer_string(buf));
1890  }
1891  else
1893  }
1894  }
1895  break;
1896 
1897  case OP_ENTER_MASK:
1898  {
1899  const struct Regex *c_mask = cs_subset_regex(NeoMutt->sub, "mask");
1900  mutt_buffer_strcpy(buf, c_mask ? c_mask->pattern : NULL);
1901  if (mutt_get_field(_("File Mask: "), buf->data, buf->dsize,
1902  MUTT_COMP_NO_FLAGS, false, NULL, NULL) != 0)
1903  {
1904  break;
1905  }
1906 
1907  mutt_buffer_fix_dptr(buf);
1908 
1909  state.is_mailbox_list = false;
1910  /* assume that the user wants to see everything */
1911  if (mutt_buffer_is_empty(buf))
1912  mutt_buffer_strcpy(buf, ".");
1913 
1914  struct Buffer errmsg = mutt_buffer_make(256);
1915  int rc = cs_subset_str_string_set(NeoMutt->sub, "mask",
1916  mutt_buffer_string(buf), &errmsg);
1917  if (CSR_RESULT(rc) != CSR_SUCCESS)
1918  {
1919  if (!mutt_buffer_is_empty(&errmsg))
1920  {
1921  mutt_error("%s", mutt_buffer_string(&errmsg));
1922  mutt_buffer_dealloc(&errmsg);
1923  }
1924  break;
1925  }
1926  mutt_buffer_dealloc(&errmsg);
1927 
1928  destroy_state(&state);
1929 #ifdef USE_IMAP
1930  if (state.imap_browse)
1931  {
1932  init_state(&state, NULL);
1933  state.imap_browse = true;
1934  imap_browse(mutt_buffer_string(&LastDir), &state);
1935  browser_sort(&state);
1936  menu->mdata = &state.entry;
1937  init_menu(&state, menu, m, sbar);
1938  }
1939  else
1940 #endif
1941  if (examine_directory(m, menu, &state, mutt_buffer_string(&LastDir), NULL) == 0)
1942  {
1943  init_menu(&state, menu, m, sbar);
1944  }
1945  else
1946  {
1947  mutt_error(_("Error scanning directory"));
1948  goto bail;
1949  }
1950  kill_prefix = false;
1951  if (ARRAY_EMPTY(&state.entry))
1952  {
1953  mutt_error(_("No files match the file mask"));
1954  break;
1955  }
1956  break;
1957  }
1958 
1959  case OP_SORT:
1960  case OP_SORT_REVERSE:
1961 
1962  {
1963  bool resort = true;
1964  int sort = -1;
1965  int reverse = (op == OP_SORT_REVERSE);
1966 
1967  switch (mutt_multi_choice(
1968  (reverse) ?
1969  /* L10N: The highlighted letters must match the "Sort" options */
1970  _("Reverse sort by (d)ate, (a)lpha, si(z)e, d(e)scription, "
1971  "(c)ount, ne(w) count, or do(n)'t sort?") :
1972  /* L10N: The highlighted letters must match the "Reverse Sort" options */
1973  _("Sort by (d)ate, (a)lpha, si(z)e, d(e)scription, (c)ount, "
1974  "ne(w) count, or do(n)'t sort?"),
1975  /* L10N: These must match the highlighted letters from "Sort" and "Reverse Sort" */
1976  _("dazecwn")))
1977  {
1978  case -1: /* abort */
1979  resort = false;
1980  break;
1981 
1982  case 1: /* (d)ate */
1983  sort = SORT_DATE;
1984  break;
1985 
1986  case 2: /* (a)lpha */
1987  sort = SORT_SUBJECT;
1988  break;
1989 
1990  case 3: /* si(z)e */
1991  sort = SORT_SIZE;
1992  break;
1993 
1994  case 4: /* d(e)scription */
1995  sort = SORT_DESC;
1996  break;
1997 
1998  case 5: /* (c)ount */
1999  sort = SORT_COUNT;
2000  break;
2001 
2002  case 6: /* ne(w) count */
2003  sort = SORT_UNREAD;
2004  break;
2005 
2006  case 7: /* do(n)'t sort */
2007  sort = SORT_ORDER;
2008  break;
2009  }
2010  if (resort)
2011  {
2012  sort |= reverse ? SORT_REVERSE : 0;
2013  cs_subset_str_native_set(NeoMutt->sub, "sort_browser", sort, NULL);
2014  browser_sort(&state);
2015  browser_highlight_default(&state, menu);
2017  }
2018  else
2019  {
2020  cs_subset_str_native_set(NeoMutt->sub, "sort_browser", sort, NULL);
2021  }
2022  break;
2023  }
2024 
2025  case OP_TOGGLE_MAILBOXES:
2026  case OP_BROWSER_GOTO_FOLDER:
2027  case OP_CHECK_NEW:
2028  if (state.is_mailbox_list)
2029  {
2030  last_selected_mailbox = menu->current;
2031  }
2032 
2033  if (op == OP_TOGGLE_MAILBOXES)
2034  {
2035  state.is_mailbox_list = !state.is_mailbox_list;
2036  }
2037 
2038  if (op == OP_BROWSER_GOTO_FOLDER)
2039  {
2040  /* When in mailboxes mode, disables this feature */
2041  const char *const c_folder = cs_subset_string(NeoMutt->sub, "folder");
2042  if (c_folder)
2043  {
2044  mutt_debug(LL_DEBUG3, "= hit! Folder: %s, LastDir: %s\n", c_folder,
2045  mutt_buffer_string(&LastDir));
2046  if (goto_swapper[0] == '\0')
2047  {
2048  if (!mutt_str_equal(mutt_buffer_string(&LastDir), c_folder))
2049  {
2050  /* Stores into goto_swapper LastDir, and swaps to `$folder` */
2051  mutt_str_copy(goto_swapper, mutt_buffer_string(&LastDir),
2052  sizeof(goto_swapper));
2053  mutt_buffer_copy(&LastDirBackup, &LastDir);
2054  mutt_buffer_strcpy(&LastDir, c_folder);
2055  }
2056  }
2057  else
2058  {
2059  mutt_buffer_copy(&LastDirBackup, &LastDir);
2060  mutt_buffer_strcpy(&LastDir, goto_swapper);
2061  goto_swapper[0] = '\0';
2062  }
2063  }
2064  }
2065  destroy_state(&state);
2066  mutt_buffer_reset(prefix);
2067  kill_prefix = false;
2068 
2069  if (state.is_mailbox_list)
2070  {
2071  examine_mailboxes(m, menu, &state);
2072  }
2073 #ifdef USE_IMAP
2074  else if (imap_path_probe(mutt_buffer_string(&LastDir), NULL) == MUTT_IMAP)
2075  {
2076  init_state(&state, NULL);
2077  state.imap_browse = true;
2078  imap_browse(mutt_buffer_string(&LastDir), &state);
2079  browser_sort(&state);
2080  menu->mdata = &state.entry;
2081  }
2082 #endif
2083  else if (examine_directory(m, menu, &state, mutt_buffer_string(&LastDir),
2084  mutt_buffer_string(prefix)) == -1)
2085  {
2086  goto bail;
2087  }
2088  init_menu(&state, menu, m, sbar);
2089  break;
2090 
2091  case OP_MAILBOX_LIST:
2093  break;
2094 
2095  case OP_BROWSER_NEW_FILE:
2096  mutt_buffer_printf(buf, "%s/", mutt_buffer_string(&LastDir));
2097  /* buf comes from the buffer pool, so defaults to size 1024 */
2098  if (mutt_buffer_get_field(_("New file name: "), buf, MUTT_FILE, false,
2099  NULL, NULL, NULL) == 0)
2100  {
2101  mutt_buffer_copy(file, buf);
2102  destroy_state(&state);
2103  goto bail;
2104  }
2105  break;
2106 
2107  case OP_BROWSER_VIEW_FILE:
2108  if (ARRAY_EMPTY(&state.entry))
2109  {
2110  mutt_error(_("No files match the file mask"));
2111  break;
2112  }
2113 
2114 #ifdef USE_IMAP
2115  if (ff->selectable)
2116  {
2117  mutt_buffer_strcpy(file, ff->name);
2118  destroy_state(&state);
2119  goto bail;
2120  }
2121  else
2122 #endif
2123  if (S_ISDIR(ff->mode) ||
2124  (S_ISLNK(ff->mode) && link_is_dir(mutt_buffer_string(&LastDir), ff->name)))
2125  {
2126  mutt_error(_("Can't view a directory"));
2127  break;
2128  }
2129  else
2130  {
2131  char buf2[PATH_MAX];
2132 
2133  mutt_path_concat(buf2, mutt_buffer_string(&LastDir), ff->name, sizeof(buf2));
2134  struct Body *b = mutt_make_file_attach(buf2, NeoMutt->sub);
2135  if (b)
2136  {
2137  mutt_view_attachment(NULL, b, MUTT_VA_REGULAR, NULL, NULL, menu->win);
2138  mutt_body_free(&b);
2140  }
2141  else
2142  mutt_error(_("Error trying to view file"));
2143  }
2144  break;
2145 
2146 #ifdef USE_NNTP
2147  case OP_CATCHUP:
2148  case OP_UNCATCHUP:
2149  {
2150  if (!OptNews)
2151  break;
2152 
2153  struct NntpMboxData *mdata = NULL;
2154 
2156  if (rc < 0)
2157  break;
2158 
2159  if (op == OP_CATCHUP)
2160  mdata = mutt_newsgroup_catchup(m, CurrentNewsSrv, ff->name);
2161  else
2162  mdata = mutt_newsgroup_uncatchup(m, CurrentNewsSrv, ff->name);
2163 
2164  if (mdata)
2165  {
2167  index = menu_get_index(menu) + 1;
2168  if (index < menu->max)
2169  menu_set_index(menu, index);
2170  }
2171  if (rc)
2174  break;
2175  }
2176 
2177  case OP_LOAD_ACTIVE:
2178  {
2179  if (!OptNews)
2180  break;
2181 
2183 
2184  if (nntp_newsrc_parse(adata) < 0)
2185  break;
2186 
2187  for (size_t i = 0; i < adata->groups_num; i++)
2188  {
2189  struct NntpMboxData *mdata = adata->groups_list[i];
2190  if (mdata)
2191  mdata->deleted = true;
2192  }
2193  nntp_active_fetch(adata, true);
2194  nntp_newsrc_update(adata);
2195  nntp_newsrc_close(adata);
2196 
2197  destroy_state(&state);
2198  if (state.is_mailbox_list)
2199  {
2200  examine_mailboxes(m, menu, &state);
2201  }
2202  else
2203  {
2204  if (examine_directory(m, menu, &state, NULL, NULL) == -1)
2205  break;
2206  }
2207  init_menu(&state, menu, m, sbar);
2208  break;
2209  }
2210 #endif /* USE_NNTP */
2211 
2212 #if defined(USE_IMAP) || defined(USE_NNTP)
2213  case OP_BROWSER_SUBSCRIBE:
2214  case OP_BROWSER_UNSUBSCRIBE:
2215 #endif
2216 #ifdef USE_NNTP
2217  case OP_SUBSCRIBE_PATTERN:
2218  case OP_UNSUBSCRIBE_PATTERN:
2219  {
2220  if (OptNews)
2221  {
2223  regex_t rx;
2224  memset(&rx, 0, sizeof(rx));
2225  char *s = buf->data;
2226  index = menu_get_index(menu);
2227 
2228  if ((op == OP_SUBSCRIBE_PATTERN) || (op == OP_UNSUBSCRIBE_PATTERN))
2229  {
2230  char tmp2[256];
2231 
2232  mutt_buffer_reset(buf);
2233  if (op == OP_SUBSCRIBE_PATTERN)
2234  snprintf(tmp2, sizeof(tmp2), _("Subscribe pattern: "));
2235  else
2236  snprintf(tmp2, sizeof(tmp2), _("Unsubscribe pattern: "));
2237  /* buf comes from the buffer pool, so defaults to size 1024 */
2238  if ((mutt_buffer_get_field(tmp2, buf, MUTT_PATTERN, false, NULL, NULL, NULL) != 0) ||
2239  mutt_buffer_is_empty(buf))
2240  {
2241  break;
2242  }
2243 
2244  int err = REG_COMP(&rx, s, REG_NOSUB);
2245  if (err != 0)
2246  {
2247  regerror(err, &rx, buf->data, buf->dsize);
2248  regfree(&rx);
2249  mutt_error("%s", mutt_buffer_string(buf));
2250  break;
2251  }
2253  index = 0;
2254  }
2255  else if (ARRAY_EMPTY(&state.entry))
2256  {
2257  mutt_error(_("No newsgroups match the mask"));
2258  break;
2259  }
2260 
2261  int rc = nntp_newsrc_parse(adata);
2262  if (rc < 0)
2263  break;
2264 
2265  ARRAY_FOREACH_FROM(ff, &state.entry, index)
2266  {
2267  if ((op == OP_BROWSER_SUBSCRIBE) || (op == OP_BROWSER_UNSUBSCRIBE) ||
2268  (regexec(&rx, ff->name, 0, NULL, 0) == 0))
2269  {
2270  if ((op == OP_BROWSER_SUBSCRIBE) || (op == OP_SUBSCRIBE_PATTERN))
2271  mutt_newsgroup_subscribe(adata, ff->name);
2272  else
2273  mutt_newsgroup_unsubscribe(adata, ff->name);
2274  }
2275  if ((op == OP_BROWSER_SUBSCRIBE) || (op == OP_BROWSER_UNSUBSCRIBE))
2276  {
2277  if ((index + 1) < menu->max)
2278  menu_set_index(menu, index + 1);
2279  break;
2280  }
2281  }
2282 
2283  if (op == OP_SUBSCRIBE_PATTERN)
2284  {
2285  for (size_t j = 0; adata && (j < adata->groups_num); j++)
2286  {
2287  struct NntpMboxData *mdata = adata->groups_list[j];
2288  if (mdata && mdata->group && !mdata->subscribed)
2289  {
2290  if (regexec(&rx, mdata->group, 0, NULL, 0) == 0)
2291  {
2292  mutt_newsgroup_subscribe(adata, mdata->group);
2293  add_folder(menu, &state, mdata->group, NULL, NULL, NULL, mdata);
2294  }
2295  }
2296  }
2297  init_menu(&state, menu, m, sbar);
2298  }
2299  if (rc > 0)
2301  nntp_newsrc_update(adata);
2302  nntp_clear_cache(adata);
2303  nntp_newsrc_close(adata);
2304  if ((op != OP_BROWSER_SUBSCRIBE) && (op != OP_BROWSER_UNSUBSCRIBE))
2305  regfree(&rx);
2306  }
2307 #ifdef USE_IMAP
2308  else
2309 #endif /* USE_IMAP && USE_NNTP */
2310 #endif /* USE_NNTP */
2311 #ifdef USE_IMAP
2312  {
2313  char tmp2[256];
2314  mutt_str_copy(tmp2, ff->name, sizeof(tmp2));
2315  mutt_expand_path(tmp2, sizeof(tmp2));
2316  imap_subscribe(tmp2, (op == OP_BROWSER_SUBSCRIBE));
2317  }
2318 #endif /* USE_IMAP */
2319  }
2320  }
2321  }
2322 
2323 bail:
2324  mutt_buffer_pool_release(&OldLastDir);
2327  mutt_buffer_pool_release(&prefix);
2328 
2329  simple_dialog_free(&dlg);
2330 
2331  goto_swapper[0] = '\0';
2332 }
2333 
2343 void mutt_select_file(char *file, size_t filelen, SelectFileFlags flags,
2344  struct Mailbox *m, char ***files, int *numfiles)
2345 {
2346  struct Buffer *f_buf = mutt_buffer_pool_get();
2347 
2348  mutt_buffer_strcpy(f_buf, NONULL(file));
2349  mutt_buffer_select_file(f_buf, flags, m, files, numfiles);
2350  mutt_str_copy(file, mutt_buffer_string(f_buf), filelen);
2351 
2352  mutt_buffer_pool_release(&f_buf);
2353 }
#define CSR_SUCCESS
Action completed successfully.
Definition: set.h:35
int msg_unread
number of unread messages
Definition: browser.h:67
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:904
Convenience wrapper for the gui headers.
mode_t mode
Definition: browser.h:55
time_t mutt_date_epoch(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:427
bool has_new_mail
Definition: mdata.h:42
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox&#39;s path string.
Definition: mailbox.h:215
struct MuttWindow * window_find_child(struct MuttWindow *win, enum WindowType type)
Recursively find a child Window of a given type.
Definition: mutt_window.c:550
#define ARRAY_RESERVE(head, num)
Reserve memory for the array.
Definition: array.h:185
static int browser_compare_date(const void *a, const void *b)
Compare the date of two browser entries - Implements sort_t.
Definition: browser.c:234
void mutt_expando_format(char *buf, size_t buflen, size_t col, int cols, const char *src, format_t callback, intptr_t data, MuttFormatFlags flags)
Expand expandos (x) in a string -.
Definition: muttlib.c:780
time_t mtime
Definition: browser.h:57
enum MailboxType type
Mailbox type.
Definition: mailbox.h:105
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:222
#define NONULL(x)
Definition: string2.h:37
bool has_mailbox
Definition: browser.h:76
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:369
int msg_count
Total number of messages.
Definition: mailbox.h:91
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:79
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
size_t mutt_date_localtime_format(char *buf, size_t buflen, const char *format, time_t t)
Format localtime.
Definition: date.c:691
IMAP network mailbox.
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
void simple_dialog_free(struct MuttWindow **ptr)
Destroy a simple index Dialog.
Definition: simple.c:165
Definition: lib.h:67
Data passed to a notification function.
Definition: observer.h:39
struct MuttWindow * win
Window that changed.
Definition: mutt_window.h:217
static int examine_directory(struct Mailbox *m, struct Menu *menu, struct BrowserState *state, const char *d, const char *prefix)
Get list of all files/newsgroups with mask.
Definition: browser.c:734
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:40
int msg_unread
Number of unread messages.
Definition: mailbox.h:92
void mutt_browser_cleanup(void)
Clean up working Buffers.
Definition: browser.c:160
static void destroy_state(struct BrowserState *state)
Free the BrowserState.
Definition: browser.c:172
&#39;NNTP&#39; (Usenet) Mailbox type
Definition: mailbox.h:52
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
An Event that happened to a Window.
Definition: mutt_window.h:215
#define mutt_error(...)
Definition: logging.h:88
char * desc
Definition: browser.h:63
char * mutt_buffer_strdup(const struct Buffer *buf)
Copy a Buffer&#39;s string.
Definition: buffer.c:432
Convenience wrapper for the send headers.
int mutt_buffer_get_field(const char *field, struct Buffer *buf, CompletionFlags complete, bool multiple, struct Mailbox *m, char ***files, int *numfiles)
Ask the user for a string.
Definition: curs_lib.c:268
int imap_delete_mailbox(struct Mailbox *m, char *path)
Delete a mailbox.
Definition: imap.c:516
A parsed URL proto://user:password@host:port/path?a=1&b=2
Definition: url.h:68
struct AccountList accounts
List of all Accounts.
Definition: neomutt.h:40
void nntp_clear_cache(struct NntpAccountData *adata)
Clear the NNTP cache.
Definition: newsrc.c:846
void neomutt_mailboxlist_clear(struct MailboxList *ml)
Free a Mailbox List.
Definition: neomutt.c:141
int mutt_get_field(const char *field, char *buf, size_t buflen, CompletionFlags complete, bool multiple, char ***files, int *numfiles)
Ask the user for a string.
Definition: curs_lib.c:335
#define ARRAY_FIRST(head)
Convenience method to get the first element.
Definition: array.h:131
struct NntpMboxData * mutt_newsgroup_uncatchup(struct Mailbox *m, struct NntpAccountData *adata, char *group)
Uncatchup newsgroup.
Definition: newsrc.c:1347
#define ARRAY_FREE(head)
Release all memory.
Definition: array.h:198
char * realpath
Used for duplicate detection, context comparison, and the sidebar.
Definition: mailbox.h:84
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition: array.h:206
gid_t gid
Definition: browser.h:59
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:91
#define MUTT_FORMAT_ARROWCURSOR
Reserve space for arrow_cursor.
Definition: format_flags.h:35
Match any Mailbox type.
Definition: mailbox.h:45
size_t neomutt_mailboxlist_get_all(struct MailboxList *head, struct NeoMutt *n, enum MailboxType type)
Get a List of all Mailboxes.
Definition: neomutt.c:164
#define ARRAY_GET(head, idx)
Return the element at index.
Definition: array.h:105
struct Buffer mutt_buffer_make(size_t size)
Make a new buffer on the stack.
Definition: buffer.c:61
A config-change event.
Definition: subset.h:69
static int browser_compare_order(const void *a, const void *b)
Compare the order of creation of two browser entries - Implements sort_t.
Definition: browser.c:208
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:662
String manipulation buffer.
Definition: buffer.h:33
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
struct NntpMboxData * mutt_newsgroup_subscribe(struct NntpAccountData *adata, char *group)
Subscribe newsgroup.
Definition: newsrc.c:1256
void mutt_unget_event(int ch, int op)
Return a keystroke to the input buffer.
Definition: curs_lib.c:583
#define _(a)
Definition: message.h:28
Mailbox wasn&#39;t recognised.
Definition: mailbox.h:47
struct BrowserStateEntry entry
Definition: browser.h:93
Error occurred examining Mailbox.
Definition: mailbox.h:46
Window is about to be deleted.
Definition: mutt_window.h:206
#define REG_COMP(preg, regex, cflags)
Compile a regular expression.
Definition: regex3.h:54
int imap_mailbox_create(const char *path)
Create a new IMAP mailbox.
Definition: browse.c:384
static int browser_window_observer(struct NotifyCallback *nc)
Notification that a Window has changed - Implements observer_t.
Definition: browser.c:1202
const char * mutt_path_getcwd(struct Buffer *cwd)
Get the current working directory.
Definition: path.c:563
int gen
Definition: browser.h:83
void nntp_newsrc_close(struct NntpAccountData *adata)
Unlock and close .newsrc file.
Definition: newsrc.c:118
static void init_menu(struct BrowserState *state, struct Menu *menu, struct Mailbox *m, struct MuttWindow *sbar)
Set up a new menu.
Definition: browser.c:1047
static int browser_compare_desc(const void *a, const void *b)
Compare the descriptions of two browser entries - Implements sort_t.
Definition: browser.c:220
static void init_lastdir(void)
Initialise the browser directories.
Definition: browser.c:146
void url_free(struct Url **ptr)
Free the contents of a URL.
Definition: url.c:123
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:160
View using default method.
Definition: mutt_attach.h:43
static int browser_compare_count(const void *a, const void *b)
Compare the message count of two browser entries - Implements sort_t.
Definition: browser.c:262
static int examine_mailboxes(struct Mailbox *m, struct Menu *menu, struct BrowserState *state)
Get list of mailboxes/subscribed newsgroups.
Definition: browser.c:862
Flags to control mutt_expando_format()
All user-callable functions.
int mutt_str_coll(const char *a, const char *b)
Collate two strings (compare using locale), safely.
Definition: string.c:677
#define mutt_perror(...)
Definition: logging.h:89
Container for Accounts, Notifications.
Definition: neomutt.h:36
void mutt_buffer_pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using &#39;~&#39; or &#39;=&#39;.
Definition: muttlib.c:603
#define MUTT_PATTERN
Pattern mode - only used for history classes.
Definition: mutt.h:60
NNTP-specific Account data -.
Definition: adata.h:36
MuttWindow has changed, NotifyWindow, EventWindow.
Definition: notify_type.h:53
#define MUTT_FORMAT_NO_FLAGS
No flags are set.
Definition: format_flags.h:30
bool subscribed
Definition: mdata.h:41
The body of an email.
Definition: body.h:34
Status Bar containing extra info about the Index/Pager/etc.
Definition: mutt_window.h:102
Convenience wrapper for the config headers.
struct FolderFile * ff
Definition: browser.h:46
GUI component for displaying/selecting items from a list.
void(* make_entry)(struct Menu *menu, char *buf, size_t buflen, int line)
Definition: lib.h:105
#define MUTT_SEL_FOLDER
Select a local directory.
Definition: browser.h:39
off_t size
Definition: browser.h:56
#define CSR_RESULT(x)
Definition: set.h:52
char * HomeDir
User&#39;s home directory.
Definition: mutt_globals.h:45
char * folder
Definition: browser.h:96
bool selectable
Definition: browser.h:73
char host[128]
Server to login to.
Definition: connaccount.h:53
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
int event_subtype
Send: Event subtype, e.g. NT_ACCOUNT_ADD.
Definition: observer.h:43
enum NotifyType event_type
Send: Event type, e.g. NT_ACCOUNT.
Definition: observer.h:42
Some miscellaneous functions.
const struct Regex * cs_subset_regex(const struct ConfigSubset *sub, const char *name)
Get a regex config item by name.
Definition: helpers.c:243
bool has_new
Mailbox has new mail.
Definition: mailbox.h:88
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:305
Sort by the size of the email.
Definition: sort2.h:44
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:122
int nntp_active_fetch(struct NntpAccountData *adata, bool mark_new)
Fetch list of all newsgroups from server.
Definition: nntp.c:1963
char * name
A short name for the Mailbox.
Definition: mailbox.h:85
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:408
struct Notify * notify
Notifications: NotifyWindow, EventWindow.
Definition: mutt_window.h:138
Many unsorted constants and some structs.
API for mailboxes.
uid_t uid
Definition: browser.h:58
#define MUTT_COMP_NO_FLAGS
No flags are set.
Definition: mutt.h:52
struct MuttWindow * win
Window holding the Menu.
Definition: lib.h:76
Sort by the order the messages appear in the mailbox.
Definition: sort2.h:48
nlink_t nlink
Definition: browser.h:60
Convenience wrapper for the core headers.
struct Body * mutt_make_file_attach(const char *path, struct ConfigSubset *sub)
Create a file attachment.
Definition: sendlib.c:1088
char * name
Definition: browser.h:62
char * group
Definition: mdata.h:34
int num
Definition: browser.h:47
void mutt_buffer_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:294
size_t mutt_path_realpath(char *buf)
resolve path, unraveling symlinks
Definition: path.c:440
enum MailboxType imap_path_probe(const char *path, const struct stat *st)
Is this an IMAP Mailbox? - Implements MxOps::path_probe() -.
Definition: imap.c:2402
bool imap
Definition: browser.h:72
int bool_str_toggle(struct ConfigSubset *sub, const char *name, struct Buffer *err)
Toggle the value of a bool.
Definition: bool.c:214
const char * OpStrings[][2]
Definition: opcodes.c:34
void * mdata
Driver specific data.
Definition: mailbox.h:136
bool notify_observer_add(struct Notify *notify, enum NotifyType type, observer_t callback, void *global_data)
Add an observer to an object.
Definition: notify.c:189
Browser Dialog, mutt_buffer_select_file()
Definition: mutt_window.h:80
void * global_data
Data from notify_observer_add()
Definition: observer.h:45
&#39;Maildir&#39; Mailbox type
Definition: mailbox.h:51
Usenet network mailbox type; talk to an NNTP server.
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
short cs_subset_sort(const struct ConfigSubset *sub, const char *name)
Get a sort config item by name.
Definition: helpers.c:292
short cols
Number of columns, can be MUTT_WIN_SIZE_UNLIMITED.
Definition: mutt_window.h:60
static int browser_config_observer(struct NotifyCallback *nc)
Notification that a Config Variable has changed - Implements observer_t.
Definition: browser.c:1172
A folder/dir in the browser.
Definition: browser.h:44
Browser entry representing a folder/dir.
Definition: browser.h:53
static int browser_compare_count_new(const void *a, const void *b)
Compare the new count of two browser entries - Implements sort_t.
Definition: browser.c:282
static const struct Mapping FolderNewsHelp[]
Help Bar for the NNTP Mailbox browser dialog.
Definition: browser.c:124
#define MB_HIDDEN
Definition: mailbox.h:38
struct WindowState state
Current state of the Window.
Definition: mutt_window.h:127
void * mdata
Private data.
Definition: lib.h:155
&#39;POP3&#39; Mailbox type
Definition: mailbox.h:55
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:160
size_t mutt_buffer_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:356
A mailbox.
Definition: mailbox.h:81
#define PATH_MAX
Definition: mutt.h:40
int mutt_mailbox_check(struct Mailbox *m_cur, int force)
Check all all Mailboxes for new mail.
Definition: mutt_mailbox.c:137
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, intptr_t data, MuttFormatFlags flags)
Format a string for the folder browser - Implements format_t -Expando Description %C Current file nu...
Definition: browser.c:405
int top
Entry that is the top of the current page.
Definition: lib.h:89
void * adata
Private data (for Mailbox backends)
Definition: account.h:43
#define ARRAY_EMPTY(head)
Check if an array is empty.
Definition: array.h:70
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:1679
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:1611
char * data
Pointer to data.
Definition: buffer.h:35
unsigned int groups_num
Definition: adata.h:59
static void browser_sort(struct BrowserState *state)
Sort the entries in the browser.
Definition: browser.c:344
Nntp-specific Mailbox data.
static int browser_compare_size(const void *a, const void *b)
Compare the size of two browser entries - Implements sort_t.
Definition: browser.c:248
&#39;mmdf&#39; Mailbox type
Definition: mailbox.h:49
int nntp_newsrc_parse(struct NntpAccountData *adata)
Parse .newsrc file.
Definition: newsrc.c:162
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
int mutt_multi_choice(const char *prompt, const char *letters)
Offer the user a multiple choice question.
Definition: question.c:49
&#39;MH&#39; Mailbox type
Definition: mailbox.h:50
int msg_count
total number of messages
Definition: browser.h:66
Ask the user a question.
void mutt_body_free(struct Body **ptr)
Free a Body.
Definition: body.c:57
struct NntpMboxData * mutt_newsgroup_unsubscribe(struct NntpAccountData *adata, char *group)
Unsubscribe newsgroup.
Definition: newsrc.c:1280
static int select_file_search(struct Menu *menu, regex_t *rx, int line)
Menu search callback for matching files - Implements Menu::search() -.
Definition: browser.c:970
Handling of email attachments.
void sbar_set_title(struct MuttWindow *win, const char *title)
Set the title for the Simple Bar.
Definition: sbar.c:221
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
int(* tag)(struct Menu *menu, int sel, int act)
Definition: lib.h:130
void mutt_select_file(char *file, size_t filelen, SelectFileFlags flags, struct Mailbox *m, char ***files, int *numfiles)
Let the user select a file.
Definition: browser.c:2343
struct Notify * notify
Notifications handler.
Definition: neomutt.h:38
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:240
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
static int browser_compare_subject(const void *a, const void *b)
Compare the subject of two browser entries - Implements sort_t.
Definition: browser.c:190
#define MUTT_SEL_MULTI
Multi-selection is enabled.
Definition: browser.h:38
NNTP-specific Mailbox data -.
Definition: mdata.h:32
int tagged
Number of tagged entries.
Definition: lib.h:92
#define MUTT_FILE
Do file completion.
Definition: mutt.h:54
enum QuadOption mutt_yesorno(const char *msg, enum QuadOption def)
Ask the user a Yes/No question.
Definition: question.c:180
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:75
void * event_data
Data from notify_send()
Definition: observer.h:44
int max
Number of entries in the menu.
Definition: lib.h:71
&#39;Notmuch&#39; (virtual) Mailbox type
Definition: mailbox.h:54
&#39;mbox&#39; Mailbox type
Definition: mailbox.h:48
#define ARRAY_FOREACH_FROM(elem, head, from)
Iterate from an index to the end.
Definition: array.h:217
Config has changed, NotifyConfig, EventConfig.
Definition: notify_type.h:42
General file/mailbox browser.
Definition: type.h:44
#define MUTT_SEL_MAILBOX
Select a mailbox.
Definition: browser.h:37
uint8_t flags
e.g. MB_NORMAL
Definition: mailbox.h:134
struct Connection * conn
Definition: adata.h:63
static void browser_highlight_default(struct BrowserState *state, struct Menu *menu)
Decide which browser item should be highlighted.
Definition: browser.c:1021
enum MailboxType mx_path_probe(const char *path)
Find a mailbox that understands a path.
Definition: mx.c:1317
void mutt_buffer_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:322
MailboxType
Supported mailbox formats.
Definition: mailbox.h:43
void mutt_format_s(char *buf, size_t buflen, const char *prec, const char *s)
Format a simple string.
Definition: curs_lib.c:867
Log at debug level 1.
Definition: logging.h:40
bool deleted
Definition: mdata.h:44
Sort by the number of unread emails.
Definition: sort2.h:59
void mutt_browser_select_dir(const char *f)
Remember the last directory selected.
Definition: browser.c:1234
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:749
static int file_tag(struct Menu *menu, int sel, int act)
Tag an entry in the menu - Implements Menu::tag() -.
Definition: browser.c:1152
void imap_clean_path(char *path, size_t plen)
Cleans an IMAP path using imap_fix_path.
Definition: util.c:188
#define ARRAY_REMOVE(head, elem)
Remove an entry from the array, shifting down the subsequent entries.
Definition: array.h:261
Cached regular expression.
Definition: regex3.h:89
anum_t unread
Definition: mdata.h:40
int imap_browse(const char *path, struct BrowserState *state)
IMAP hook into the folder browser.
Definition: browse.c:182
Sort by the folder&#39;s description.
Definition: sort2.h:63
WHERE char * CurrentFolder
Currently selected mailbox.
Definition: mutt_globals.h:50
bool mutt_regex_match(const struct Regex *regex, const char *str)
Shorthand to mutt_regex_capture()
Definition: regex.c:613
Connection Library.
void ** groups_list
Definition: adata.h:61
void mutt_buffer_select_file(struct Buffer *file, SelectFileFlags flags, struct Mailbox *m, char ***files, int *numfiles)
Let the user select a file.
Definition: browser.c:1254
#define mutt_message(...)
Definition: logging.h:87
#define FREE(x)
Definition: memory.h:40
#define ARRAY_ADD(head, elem)
Add an element at the end of the array.
Definition: array.h:152
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:437
Mapping between user-readable string and a constant.
Definition: mapping.h:31
#define STAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:324
struct NntpAccountData * CurrentNewsSrv
Current NNTP news server.
Definition: nntp.c:78
User answered &#39;No&#39;, or assume &#39;No&#39;.
Definition: quad.h:38
#define ARRAY_SORT(head, fn)
Sort an array.
Definition: array.h:271
#define MUTT_FORMAT_OPTIONAL
Allow optional field processing.
Definition: format_flags.h:33
bool notify_observer_remove(struct Notify *notify, observer_t callback, void *global_data)
Remove an observer from an object.
Definition: notify.c:228
Sort by number of emails in a folder.
Definition: sort2.h:58
struct MuttWindow * simple_dialog_new(enum MenuType mtype, enum WindowType wtype, const struct Mapping *help_data)
Create a simple index Dialog.
Definition: simple.c:128
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:351
List of Mailboxes.
Definition: mailbox.h:156
static int browser_compare(const void *a, const void *b)
Sort the items in the browser - Implements sort_t.
Definition: browser.c:306
Hundreds of global variables to back the user variables.
Handling of global boolean variables.
bool tagged
Definition: browser.h:78
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
struct NntpMboxData * nd
Definition: browser.h:80
#define TAILQ_EMPTY(head)
Definition: queue.h:721
int nntp_newsrc_update(struct NntpAccountData *adata)
Update .newsrc file.
Definition: newsrc.c:439
int current
Current entry.
Definition: lib.h:70
char * pattern
printable version
Definition: regex3.h:91
bool local
folder is on local filesystem
Definition: browser.h:77
static void init_state(struct BrowserState *state, struct Menu *menu)
Initialise a browser state.
Definition: browser.c:713
Sort by the date the email was sent.
Definition: sort2.h:43
Log at debug level 5.
Definition: logging.h:44
struct NntpMboxData * mutt_newsgroup_catchup(struct Mailbox *m, struct NntpAccountData *adata, char *group)
Catchup newsgroup.
Definition: newsrc.c:1308
Nntp-specific Account data.
Convenience wrapper for the library headers.
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, intptr_t data, MuttFormatFlags flags)
Format a string for the newsgroup menu - Implements format_t -Expando Description %C Current newsgro...
Definition: browse.c:55
bool inferiors
Definition: browser.h:74
#define SORT_REVERSE
Reverse the order of the sort.
Definition: sort2.h:79
static const struct Mapping FolderHelp[]
Help Bar for the File/Dir/Mailbox browser dialog.
Definition: browser.c:111
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:252
void * wdata
Private data.
Definition: mutt_window.h:145
&#39;IMAP&#39; Mailbox type
Definition: mailbox.h:53
bool imap_browse
Definition: browser.h:95
#define N_(a)
Definition: message.h:32
int(* search)(struct Menu *menu, regex_t *rx, int line)
Definition: lib.h:118
const char * name
Name of config item that changed.
Definition: subset.h:72
void mutt_get_parent_path(const char *path, char *buf, size_t buflen)
Find the parent of a path (or mailbox)
Definition: muttlib.c:1555
bool is_mailbox_list
Definition: browser.h:98
WHERE bool OptNews
(pseudo) used to change reader mode
Definition: options.h:44
char delim
Definition: browser.h:70
int gen
Generation number, for sorting.
Definition: mailbox.h:150
Mailbox helper functions.
Sort by the email&#39;s subject.
Definition: sort2.h:46
Log at debug level 3.
Definition: logging.h:42
#define SORT_MASK
Mask for the sort id.
Definition: sort2.h:78
static void folder_make_entry(struct Menu *menu, char *buf, size_t buflen, int line)
Format a menu item for the folder browser - Implements Menu::make_entry() -.
Definition: browser.c:986
#define ARRAY_SIZE(head)
The number of elements stored.
Definition: array.h:83
User answered &#39;Yes&#39;, or assume &#39;Yes&#39;.
Definition: quad.h:39
int imap_subscribe(char *path, bool subscribe)
Subscribe to a mailbox.
Definition: imap.c:1286
#define ARRAY_INIT(head)
Initialize an array.
Definition: array.h:61
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:158
struct Url * url_parse(const char *src)
Fill in Url.
Definition: url.c:234
bool has_new_mail
true if mailbox has "new mail"
Definition: browser.h:65
uint8_t SelectFileFlags
Flags for mutt_select_file(), e.g. MUTT_SEL_MAILBOX.
Definition: browser.h:33