browser.c 32.1 KB
Newer Older
Thomas Roessler's avatar
Thomas Roessler committed
1
/*
2
 * Copyright (C) 1996-2000,2007,2010,2013 Michael R. Elkins <me@mutt.org>
Thomas Roessler's avatar
Thomas Roessler committed
3 4 5 6 7 8 9 10 11 12 13 14 15
 * 
 *     This program is free software; you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation; either version 2 of the License, or
 *     (at your option) any later version.
 * 
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 * 
 *     You should have received a copy of the GNU General Public License
 *     along with this program; if not, write to the Free Software
16
 *     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
Thomas Roessler's avatar
Thomas Roessler committed
17 18
 */ 

19 20 21 22
#if HAVE_CONFIG_H
# include "config.h"
#endif

Thomas Roessler's avatar
Thomas Roessler committed
23 24 25
#include "mutt.h"
#include "mutt_curses.h"
#include "mutt_menu.h"
26
#include "attach.h"
Thomas Roessler's avatar
Thomas Roessler committed
27
#include "buffy.h"
Thomas Roessler's avatar
Thomas Roessler committed
28
#include "mapping.h"
Thomas Roessler's avatar
Thomas Roessler committed
29 30
#include "sort.h"
#include "mailbox.h"
31 32 33 34
#include "browser.h"
#ifdef USE_IMAP
#include "imap.h"
#endif
Thomas Roessler's avatar
Thomas Roessler committed
35 36 37 38

#include <stdlib.h>
#include <dirent.h>
#include <string.h>
Thomas Roessler's avatar
Thomas Roessler committed
39
#include <ctype.h>
Thomas Roessler's avatar
Thomas Roessler committed
40 41
#include <unistd.h>
#include <sys/stat.h>
42
#include <errno.h>
43
#include <locale.h>
Thomas Roessler's avatar
Thomas Roessler committed
44

45
static const struct mapping_t FolderHelp[] = {
46 47 48 49
  { N_("Exit"),  OP_EXIT },
  { N_("Chdir"), OP_CHANGE_DIRECTORY },
  { N_("Mask"),  OP_ENTER_MASK },
  { N_("Help"),  OP_HELP },
50
  { NULL,	 0 }
Thomas Roessler's avatar
Thomas Roessler committed
51 52 53 54
};

typedef struct folder_t
{
55 56
  struct folder_file *ff;
  int num;
Thomas Roessler's avatar
Thomas Roessler committed
57 58 59
} FOLDER;

static char LastDir[_POSIX_PATH_MAX] = "";
60
static char LastDirBackup[_POSIX_PATH_MAX] = "";
Thomas Roessler's avatar
Thomas Roessler committed
61 62 63 64 65 66 67 68

/* Frees up the memory allocated for the local-global variables.  */
static void destroy_state (struct browser_state *state)
{
  int c;

  for (c = 0; c < state->entrylen; c++)
  {
69 70
    FREE (&((state->entry)[c].name));
    FREE (&((state->entry)[c].desc));
Thomas Roessler's avatar
Thomas Roessler committed
71
  }
72
#ifdef USE_IMAP
73
  FREE (&state->folder);
74
#endif
75
  FREE (&state->entry);
Thomas Roessler's avatar
Thomas Roessler committed
76 77 78 79 80 81 82
}

static int browser_compare_subject (const void *a, const void *b)
{
  struct folder_file *pa = (struct folder_file *) a;
  struct folder_file *pb = (struct folder_file *) b;

83
  int r = mutt_strcoll (pa->name, pb->name);
Thomas Roessler's avatar
Thomas Roessler committed
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107

  return ((BrowserSort & SORT_REVERSE) ? -r : r);
}

static int browser_compare_date (const void *a, const void *b)
{
  struct folder_file *pa = (struct folder_file *) a;
  struct folder_file *pb = (struct folder_file *) b;

  int r = pa->mtime - pb->mtime;

  return ((BrowserSort & SORT_REVERSE) ? -r : r);
}

static int browser_compare_size (const void *a, const void *b)
{
  struct folder_file *pa = (struct folder_file *) a;
  struct folder_file *pb = (struct folder_file *) b;

  int r = pa->size - pb->size;

  return ((BrowserSort & SORT_REVERSE) ? -r : r);
}

108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
static int browser_compare_count (const void *a, const void *b)
{
  struct folder_file *pa = (struct folder_file *) a;
  struct folder_file *pb = (struct folder_file *) b;

  int r = pa->msg_count - pb->msg_count;

  return ((BrowserSort & SORT_REVERSE) ? -r : r);
}

static int browser_compare_unread (const void *a, const void *b)
{
  struct folder_file *pa = (struct folder_file *) a;
  struct folder_file *pb = (struct folder_file *) b;

  int r = pa->msg_unread - pb->msg_unread;

  return ((BrowserSort & SORT_REVERSE) ? -r : r);
}

Thomas Roessler's avatar
Thomas Roessler committed
128 129 130 131 132 133 134 135 136 137 138 139 140 141
static void browser_sort (struct browser_state *state)
{
  int (*f) (const void *, const void *);

  switch (BrowserSort & SORT_MASK)
  {
    case SORT_ORDER:
      return;
    case SORT_DATE:
      f = browser_compare_date;
      break;
    case SORT_SIZE:
      f = browser_compare_size;
      break;
142 143 144 145 146 147
    case SORT_COUNT:
      f = browser_compare_count;
      break;
    case SORT_UNREAD:
      f = browser_compare_unread;
      break;
Thomas Roessler's avatar
Thomas Roessler committed
148 149 150 151 152 153 154 155
    case SORT_SUBJECT:
    default:
      f = browser_compare_subject;
      break;
  }
  qsort (state->entry, state->entrylen, sizeof (struct folder_file), f);
}

156
static int link_is_dir (const char *folder, const char *path)
Thomas Roessler's avatar
Thomas Roessler committed
157 158
{
  struct stat st;
159 160
  char fullpath[_POSIX_PATH_MAX];
  
161
  mutt_concat_path (fullpath, folder, path, sizeof (fullpath));
162 163
  
  if (stat (fullpath, &st) == 0)
Thomas Roessler's avatar
Thomas Roessler committed
164 165
    return (S_ISDIR (st.st_mode));
  else
166
    return 0;
Thomas Roessler's avatar
Thomas Roessler committed
167 168 169
}

static const char *
170
folder_format_str (char *dest, size_t destlen, size_t col, int cols, char op, const char *src,
Thomas Roessler's avatar
Thomas Roessler committed
171 172 173
		   const char *fmt, const char *ifstring, const char *elsestring,
		   unsigned long data, format_flag flags)
{
Thomas Roessler's avatar
Thomas Roessler committed
174
  char fn[SHORT_STRING], tmp[SHORT_STRING], permission[11];
175
  char date[SHORT_STRING], *t_fmt;
Thomas Roessler's avatar
Thomas Roessler committed
176 177 178 179
  time_t tnow;
  FOLDER *folder = (FOLDER *) data;
  struct passwd *pw;
  struct group *gr;
180
  int optional = (flags & MUTT_FORMAT_OPTIONAL);
Thomas Roessler's avatar
Thomas Roessler committed
181

Thomas Roessler's avatar
Thomas Roessler committed
182 183
  switch (op)
  {
184 185 186 187 188
    case 'C':
      snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
      snprintf (dest, destlen, tmp, folder->num + 1);
      break;
      
Thomas Roessler's avatar
Thomas Roessler committed
189
    case 'd':
190
    case 'D':
191
      if (folder->ff->local)
Thomas Roessler's avatar
Thomas Roessler committed
192
      {
Michael Elkins's avatar
Michael Elkins committed
193
	int do_locales = TRUE;
194 195 196 197 198

	if (op == 'D') {
	  t_fmt = NONULL(DateFmt);
	  if (*t_fmt == '!') {
	    ++t_fmt;
Michael Elkins's avatar
Michael Elkins committed
199
	    do_locales = FALSE;
200 201 202
	  }
	} else {
	  tnow = time (NULL);
203
	  t_fmt = tnow - folder->ff->mtime < 31536000 ? "%b %d %H:%M" : "%b %d  %Y";
204
	}
205 206 207 208 209 210

        if (!do_locales)
          setlocale (LC_TIME, "C");
        strftime (date, sizeof (date), t_fmt, localtime (&folder->ff->mtime));
        if (!do_locales)
          setlocale (LC_TIME, "");
211

212
	mutt_format_s (dest, destlen, fmt, date);
Thomas Roessler's avatar
Thomas Roessler committed
213 214
      }
      else
215
	mutt_format_s (dest, destlen, fmt, "");
Thomas Roessler's avatar
Thomas Roessler committed
216
      break;
217
      
Thomas Roessler's avatar
Thomas Roessler committed
218
    case 'f':
219 220
    {
      char *s;
221
#ifdef USE_IMAP
222
      if (folder->ff->imap)
223
	s = NONULL (folder->ff->desc);
224 225
      else
#endif
226 227 228
	s = NONULL (folder->ff->name);

      snprintf (fn, sizeof (fn), "%s%s", s,
229 230 231
		folder->ff->local ? (S_ISLNK (folder->ff->mode) ? "@" :
				  (S_ISDIR (folder->ff->mode) ? "/" :
				   ((folder->ff->mode & S_IXUSR) != 0 ? "*" : ""))) : "");
232
      
233
      mutt_format_s (dest, destlen, fmt, fn);
Thomas Roessler's avatar
Thomas Roessler committed
234
      break;
235
    }
Thomas Roessler's avatar
Thomas Roessler committed
236
    case 'F':
237
      if (folder->ff->local)
Thomas Roessler's avatar
Thomas Roessler committed
238
      {
239
	snprintf (permission, sizeof (permission), "%c%c%c%c%c%c%c%c%c%c",
240 241 242 243 244 245 246 247 248 249
		  S_ISDIR(folder->ff->mode) ? 'd' : (S_ISLNK(folder->ff->mode) ? 'l' : '-'),
		  (folder->ff->mode & S_IRUSR) != 0 ? 'r': '-',
		  (folder->ff->mode & S_IWUSR) != 0 ? 'w' : '-',
		  (folder->ff->mode & S_ISUID) != 0 ? 's' : (folder->ff->mode & S_IXUSR) != 0 ? 'x': '-',
		  (folder->ff->mode & S_IRGRP) != 0 ? 'r' : '-',
		  (folder->ff->mode & S_IWGRP) != 0 ? 'w' : '-',
		  (folder->ff->mode & S_ISGID) != 0 ? 's' : (folder->ff->mode & S_IXGRP) != 0 ? 'x': '-',
		  (folder->ff->mode & S_IROTH) != 0 ? 'r' : '-',
		  (folder->ff->mode & S_IWOTH) != 0 ? 'w' : '-',
		  (folder->ff->mode & S_ISVTX) != 0 ? 't' : (folder->ff->mode & S_IXOTH) != 0 ? 'x': '-');
250
	mutt_format_s (dest, destlen, fmt, permission);
Thomas Roessler's avatar
Thomas Roessler committed
251 252
      }
#ifdef USE_IMAP
Thomas Roessler's avatar
Thomas Roessler committed
253 254 255
      else if (folder->ff->imap)
      {
	/* mark folders with subfolders AND mail */
256 257
	snprintf (permission, sizeof (permission), "IMAP %c",
		  (folder->ff->inferiors && folder->ff->selectable) ? '+' : ' ');
258
	mutt_format_s (dest, destlen, fmt, permission);
Thomas Roessler's avatar
Thomas Roessler committed
259
      }                                        
Thomas Roessler's avatar
Thomas Roessler committed
260
#endif
Thomas Roessler's avatar
Thomas Roessler committed
261
      else
262
	mutt_format_s (dest, destlen, fmt, "");
Thomas Roessler's avatar
Thomas Roessler committed
263
      break;
264
      
Thomas Roessler's avatar
Thomas Roessler committed
265
    case 'g':
266
      if (folder->ff->local)
Thomas Roessler's avatar
Thomas Roessler committed
267
      {
268
	if ((gr = getgrgid (folder->ff->gid)))
269
	  mutt_format_s (dest, destlen, fmt, gr->gr_name);
Thomas Roessler's avatar
Thomas Roessler committed
270 271 272
	else
	{
	  snprintf (tmp, sizeof (tmp), "%%%sld", fmt);
273
	  snprintf (dest, destlen, tmp, folder->ff->gid);
Thomas Roessler's avatar
Thomas Roessler committed
274
	}
Thomas Roessler's avatar
Thomas Roessler committed
275 276
      }
      else
277
	mutt_format_s (dest, destlen, fmt, "");
Thomas Roessler's avatar
Thomas Roessler committed
278
      break;
279
      
Thomas Roessler's avatar
Thomas Roessler committed
280
    case 'l':
281
      if (folder->ff->local)
Thomas Roessler's avatar
Thomas Roessler committed
282 283
      {
	snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
284
	snprintf (dest, destlen, tmp, folder->ff->nlink);
Thomas Roessler's avatar
Thomas Roessler committed
285 286
      }
      else
287
	mutt_format_s (dest, destlen, fmt, "");
Thomas Roessler's avatar
Thomas Roessler committed
288
      break;
289 290 291

    case 'm':
      if (!optional)
Thomas Roessler's avatar
Thomas Roessler committed
292
      {
293 294 295 296 297 298 299
        if (folder->ff->has_buffy)
        {
          snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
          snprintf (dest, destlen, tmp, folder->ff->msg_count);
        }
        else
          mutt_format_s (dest, destlen, fmt, "");
Thomas Roessler's avatar
Thomas Roessler committed
300
      }
301 302 303 304 305
      else if (!folder->ff->msg_count)
        optional = 0;
      break;

    case 'N':
Thomas Roessler's avatar
Thomas Roessler committed
306
      snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
Thomas Roessler's avatar
Thomas Roessler committed
307
      snprintf (dest, destlen, tmp, folder->ff->new ? 'N' : ' ');
Thomas Roessler's avatar
Thomas Roessler committed
308
      break;
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324

    case 'n':
      if (!optional)
      {
        if (folder->ff->has_buffy)
        {
          snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
          snprintf (dest, destlen, tmp, folder->ff->msg_unread);
        }
        else
          mutt_format_s (dest, destlen, fmt, "");
      }
      else if (!folder->ff->msg_unread)
        optional = 0;
      break;

Thomas Roessler's avatar
Thomas Roessler committed
325
    case 's':
326
      if (folder->ff->local)
Thomas Roessler's avatar
Thomas Roessler committed
327
      {
328
	mutt_pretty_size(fn, sizeof(fn), folder->ff->size);
329 330
	snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
	snprintf (dest, destlen, tmp, fn);
Thomas Roessler's avatar
Thomas Roessler committed
331 332
      }
      else
333
	mutt_format_s (dest, destlen, fmt, "");
Thomas Roessler's avatar
Thomas Roessler committed
334
      break;
335

336 337 338 339
    case 't':
      snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
      snprintf (dest, destlen, tmp, folder->ff->tagged ? '*' : ' ');
      break;
340

Thomas Roessler's avatar
Thomas Roessler committed
341
    case 'u':
342
      if (folder->ff->local)
Thomas Roessler's avatar
Thomas Roessler committed
343
      {
344
	if ((pw = getpwuid (folder->ff->uid)))
345
	  mutt_format_s (dest, destlen, fmt, pw->pw_name);
Thomas Roessler's avatar
Thomas Roessler committed
346 347 348
	else
	{
	  snprintf (tmp, sizeof (tmp), "%%%sld", fmt);
349
	  snprintf (dest, destlen, tmp, folder->ff->uid);
Thomas Roessler's avatar
Thomas Roessler committed
350
	}
Thomas Roessler's avatar
Thomas Roessler committed
351 352
      }
      else
353
	mutt_format_s (dest, destlen, fmt, "");
Thomas Roessler's avatar
Thomas Roessler committed
354
      break;
355

356 357 358 359
    default:
      snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
      snprintf (dest, destlen, tmp, op);
      break;
Thomas Roessler's avatar
Thomas Roessler committed
360
  }
361 362

  if (optional)
363
    mutt_FormatString (dest, destlen, col, cols, ifstring, folder_format_str, data, 0);
364
  else if (flags & MUTT_FORMAT_OPTIONAL)
365
    mutt_FormatString (dest, destlen, col, cols, elsestring, folder_format_str, data, 0);
366

Thomas Roessler's avatar
Thomas Roessler committed
367 368 369 370
  return (src);
}

static void add_folder (MUTTMENU *m, struct browser_state *state,
371
			const char *name, const struct stat *s, BUFFY *b)
Thomas Roessler's avatar
Thomas Roessler committed
372 373 374 375
{
  if (state->entrylen == state->entrymax)
  {
    /* need to allocate more space */
376
    safe_realloc (&state->entry,
Thomas Roessler's avatar
Thomas Roessler committed
377
		  sizeof (struct folder_file) * (state->entrymax += 256));
378 379
    memset (&state->entry[state->entrylen], 0,
	    sizeof (struct folder_file) * 256);
Thomas Roessler's avatar
Thomas Roessler committed
380 381 382 383
    if (m)
      m->data = state->entry;
  }

Thomas Roessler's avatar
Thomas Roessler committed
384 385
  if (s != NULL)
  {
386 387 388
    (state->entry)[state->entrylen].mode = s->st_mode;
    (state->entry)[state->entrylen].mtime = s->st_mtime;
    (state->entry)[state->entrylen].size = s->st_size;
389 390 391
    (state->entry)[state->entrylen].gid = s->st_gid;
    (state->entry)[state->entrylen].uid = s->st_uid;
    (state->entry)[state->entrylen].nlink = s->st_nlink;
392
    
393
    (state->entry)[state->entrylen].local = 1;
Thomas Roessler's avatar
Thomas Roessler committed
394
  }
395

396 397 398 399 400 401 402 403
  if (b)
  {
    (state->entry)[state->entrylen].has_buffy = 1;
    (state->entry)[state->entrylen].new = b->new;
    (state->entry)[state->entrylen].msg_count = b->msg_count;
    (state->entry)[state->entrylen].msg_unread = b->msg_unread;
  }

Thomas Roessler's avatar
Thomas Roessler committed
404
  (state->entry)[state->entrylen].name = safe_strdup (name);
405
  (state->entry)[state->entrylen].desc = safe_strdup (name);
406
#ifdef USE_IMAP
407
  (state->entry)[state->entrylen].imap = 0;
408
#endif
Thomas Roessler's avatar
Thomas Roessler committed
409 410 411 412 413 414 415
  (state->entrylen)++;
}

static void init_state (struct browser_state *state, MUTTMENU *menu)
{
  state->entrylen = 0;
  state->entrymax = 256;
416
  state->entry = (struct folder_file *) safe_calloc (state->entrymax, sizeof (struct folder_file));
417 418 419
#ifdef USE_IMAP
  state->imap_browse = 0;
#endif
Thomas Roessler's avatar
Thomas Roessler committed
420 421 422 423 424
  if (menu)
    menu->data = state->entry;
}

static int examine_directory (MUTTMENU *menu, struct browser_state *state,
425
			      char *d, const char *prefix)
Thomas Roessler's avatar
Thomas Roessler committed
426 427 428 429 430 431 432
{
  struct stat s;
  DIR *dp;
  struct dirent *de;
  char buffer[_POSIX_PATH_MAX + SHORT_STRING];
  BUFFY *tmp;

433
  while (stat (d, &s) == -1)
Thomas Roessler's avatar
Thomas Roessler committed
434
  {
435 436 437 438 439 440 441 442 443 444 445
    if (errno == ENOENT)
    {
      /* The last used directory is deleted, try to use the parent dir. */
      char *c = strrchr (d, '/');

      if (c && (c > d))
      {
	*c = 0;
	continue;
      }
    }
Thomas Roessler's avatar
Thomas Roessler committed
446 447 448 449 450 451
    mutt_perror (d);
    return (-1);
  }

  if (!S_ISDIR (s.st_mode))
  {
452
    mutt_error (_("%s is not a directory."), d);
Thomas Roessler's avatar
Thomas Roessler committed
453 454 455 456 457 458 459 460 461 462 463 464 465 466 467
    return (-1);
  }

  mutt_buffy_check (0);

  if ((dp = opendir (d)) == NULL)
  {
    mutt_perror (d);
    return (-1);
  }

  init_state (state, menu);

  while ((de = readdir (dp)) != NULL)
  {
468
    if (mutt_strcmp (de->d_name, ".") == 0)
Thomas Roessler's avatar
Thomas Roessler committed
469 470
      continue;    /* we don't need . */
    
471
    if (prefix && *prefix && mutt_strncmp (prefix, de->d_name, mutt_strlen (prefix)) != 0)
Thomas Roessler's avatar
Thomas Roessler committed
472
      continue;
Thomas Roessler's avatar
Thomas Roessler committed
473
    if (!((regexec (Mask.rx, de->d_name, 0, NULL, 0) == 0) ^ Mask.not))
Thomas Roessler's avatar
Thomas Roessler committed
474 475
      continue;

476
    mutt_concat_path (buffer, d, de->d_name, sizeof (buffer));
Thomas Roessler's avatar
Thomas Roessler committed
477 478 479
    if (lstat (buffer, &s) == -1)
      continue;
    
480 481 482 483
    /* No size for directories or symlinks */
    if (S_ISDIR (s.st_mode) || S_ISLNK (s.st_mode))
      s.st_size = 0;
    else if (! S_ISREG (s.st_mode))
Thomas Roessler's avatar
Thomas Roessler committed
484 485 486
      continue;
    
    tmp = Incoming;
487
    while (tmp && mutt_strcmp (buffer, tmp->path))
Thomas Roessler's avatar
Thomas Roessler committed
488
      tmp = tmp->next;
489 490 491 492 493 494 495
    if (tmp && Context &&
        !mutt_strcmp (tmp->realpath, Context->realpath))
    {
      tmp->msg_count = Context->msgcount;
      tmp->msg_unread = Context->unread;
    }
    add_folder (menu, state, de->d_name, &s, tmp);
Thomas Roessler's avatar
Thomas Roessler committed
496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515
  }
  closedir (dp);  
  browser_sort (state);
  return 0;
}

static int examine_mailboxes (MUTTMENU *menu, struct browser_state *state)
{
  struct stat s;
  char buffer[LONG_STRING];
  BUFFY *tmp = Incoming;

  if (!Incoming)
    return (-1);
  mutt_buffy_check (0);

  init_state (state, menu);

  do
  {
516 517 518 519 520 521 522
    if (Context &&
        !mutt_strcmp (tmp->realpath, Context->realpath))
    {
      tmp->msg_count = Context->msgcount;
      tmp->msg_unread = Context->unread;
    }

523
    strfcpy (buffer, NONULL (tmp->path), sizeof (buffer));
524 525
    if (option (OPTBROWSERABBRMAILBOXES))
      mutt_pretty_mailbox (buffer, sizeof (buffer));
526

Thomas Roessler's avatar
Thomas Roessler committed
527
#ifdef USE_IMAP
528
    if (mx_is_imap (tmp->path))
Thomas Roessler's avatar
Thomas Roessler committed
529
    {
530
      add_folder (menu, state, buffer, NULL, tmp);
531 532 533 534 535 536
      continue;
    }
#endif
#ifdef USE_POP
    if (mx_is_pop (tmp->path))
    {
537
      add_folder (menu, state, buffer, NULL, tmp);
Thomas Roessler's avatar
Thomas Roessler committed
538 539 540
      continue;
    }
#endif
Thomas Roessler's avatar
Thomas Roessler committed
541 542 543 544 545 546
    if (lstat (tmp->path, &s) == -1)
      continue;

    if ((! S_ISREG (s.st_mode)) && (! S_ISDIR (s.st_mode)) &&
	(! S_ISLNK (s.st_mode)))
      continue;
547 548 549 550 551 552 553 554 555 556 557 558 559

    if (mx_is_maildir (tmp->path))
    {
      struct stat st2;
      char md[_POSIX_PATH_MAX];

      snprintf (md, sizeof (md), "%s/new", tmp->path);
      if (stat (md, &s) < 0)
	s.st_mtime = 0;
      snprintf (md, sizeof (md), "%s/cur", tmp->path);
      if (stat (md, &st2) < 0)
	st2.st_mtime = 0;
      if (st2.st_mtime > s.st_mtime)
560
	s.st_mtime = st2.st_mtime;
561
    }
562 563

    add_folder (menu, state, buffer, &s, tmp);
Thomas Roessler's avatar
Thomas Roessler committed
564 565 566 567 568 569
  }
  while ((tmp = tmp->next));
  browser_sort (state);
  return 0;
}

570
static int select_file_search (MUTTMENU *menu, regex_t *re, int n)
Thomas Roessler's avatar
Thomas Roessler committed
571 572 573 574
{
  return (regexec (re, ((struct folder_file *) menu->data)[n].name, 0, NULL, 0));
}

575
static void folder_entry (char *s, size_t slen, MUTTMENU *menu, int num)
Thomas Roessler's avatar
Thomas Roessler committed
576
{
577 578 579 580 581
  FOLDER folder;

  folder.ff = &((struct folder_file *) menu->data)[num];
  folder.num = num;
  
582
  mutt_FormatString (s, slen, 0, MuttIndexWindow->cols, NONULL(FolderFormat), folder_format_str, 
583
      (unsigned long) &folder, MUTT_FORMAT_ARROWCURSOR);
Thomas Roessler's avatar
Thomas Roessler committed
584 585 586 587 588 589 590 591
}

static void init_menu (struct browser_state *state, MUTTMENU *menu, char *title,
		       size_t titlelen, int buffy)
{
  char path[_POSIX_PATH_MAX];

  menu->max = state->entrylen;
Thomas Roessler's avatar
Thomas Roessler committed
592 593 594 595 596

  if(menu->current >= menu->max)
    menu->current = menu->max - 1;
  if (menu->current < 0)
    menu->current = 0;
597 598
  if (menu->top > menu->current)
    menu->top = 0;
Thomas Roessler's avatar
Thomas Roessler committed
599

Thomas Roessler's avatar
Thomas Roessler committed
600 601
  menu->tagged = 0;
  
Thomas Roessler's avatar
Thomas Roessler committed
602
  if (buffy)
603
    snprintf (title, titlelen, _("Mailboxes [%d]"), mutt_buffy_check (0));
Thomas Roessler's avatar
Thomas Roessler committed
604 605 606
  else
  {
    strfcpy (path, LastDir, sizeof (path));
607
    mutt_pretty_mailbox (path, sizeof (path));
608 609 610
#ifdef USE_IMAP
  if (state->imap_browse && option (OPTIMAPLSUB))
    snprintf (title, titlelen, _("Subscribed [%s], File mask: %s"),
611
	      path, NONULL (Mask.pattern));
612 613
  else
#endif
614
    snprintf (title, titlelen, _("Directory [%s], File mask: %s"),
615
	      path, NONULL(Mask.pattern));
Thomas Roessler's avatar
Thomas Roessler committed
616 617 618 619
  }
  menu->redraw = REDRAW_FULL;
}

620
static int file_tag (MUTTMENU *menu, int n, int m)
621 622
{
  struct folder_file *ff = &(((struct folder_file *)menu->data)[n]);
623
  int ot;
624
  if (S_ISDIR (ff->mode) || (S_ISLNK (ff->mode) && link_is_dir (LastDir, ff->name)))
625 626 627 628 629
  {
    mutt_error _("Can't attach a directory!");
    return 0;
  }
  
630 631 632 633
  ot = ff->tagged;
  ff->tagged = (m >= 0 ? m : !ff->tagged);
  
  return ff->tagged - ot;
634 635
}

636
void _mutt_select_file (char *f, size_t flen, int flags, char ***files, int *numfiles)
Thomas Roessler's avatar
Thomas Roessler committed
637
{
Thomas Roessler's avatar
Thomas Roessler committed
638
  char buf[_POSIX_PATH_MAX];
Thomas Roessler's avatar
Thomas Roessler committed
639
  char prefix[_POSIX_PATH_MAX] = "";
640
  char helpstr[LONG_STRING];
Thomas Roessler's avatar
Thomas Roessler committed
641 642
  char title[STRING];
  struct browser_state state;
643
  MUTTMENU *menu = NULL;
Thomas Roessler's avatar
Thomas Roessler committed
644 645
  struct stat st;
  int i, killPrefix = 0;
646 647 648
  int multiple = (flags & MUTT_SEL_MULTI)  ? 1 : 0;
  int folder   = (flags & MUTT_SEL_FOLDER) ? 1 : 0;
  int buffy    = (flags & MUTT_SEL_BUFFY)  ? 1 : 0;
Thomas Roessler's avatar
Thomas Roessler committed
649

650 651
  buffy = buffy && folder;
  
Thomas Roessler's avatar
Thomas Roessler committed
652 653
  memset (&state, 0, sizeof (struct browser_state));

654 655 656
  if (!folder)
    strfcpy (LastDirBackup, LastDir, sizeof (LastDirBackup));

Thomas Roessler's avatar
Thomas Roessler committed
657 658 659
  if (*f)
  {
    mutt_expand_path (f, flen);
660 661 662 663 664
#ifdef USE_IMAP
    if (mx_is_imap (f))
    {
      init_state (&state, NULL);
      state.imap_browse = 1;
665 666
      if (!imap_browse (f, &state))
        strfcpy (LastDir, state.folder, sizeof (LastDir));
667 668 669 670
    }
    else
    {
#endif
671
    for (i = mutt_strlen (f) - 1; i > 0 && f[i] != '/' ; i--);
Thomas Roessler's avatar
Thomas Roessler committed
672 673 674 675 676 677 678 679 680 681
    if (i > 0)
    {
      if (f[0] == '/')
      {
	if (i > sizeof (LastDir) - 1) i = sizeof (LastDir) - 1;
	strncpy (LastDir, f, i);
	LastDir[i] = 0;
      }
      else
      {
Michael Elkins's avatar
Michael Elkins committed
682
	getcwd (LastDir, sizeof (LastDir));
683 684
	safe_strcat (LastDir, sizeof (LastDir), "/");
	safe_strncat (LastDir, sizeof (LastDir), f, i);
Thomas Roessler's avatar
Thomas Roessler committed
685 686 687 688 689
      }
    }
    else
    {
      if (f[0] == '/')
690
	strcpy (LastDir, "/");		/* __STRCPY_CHECKED__ */
Thomas Roessler's avatar
Thomas Roessler committed
691
      else
Michael Elkins's avatar
Michael Elkins committed
692
	getcwd (LastDir, sizeof (LastDir));
Thomas Roessler's avatar
Thomas Roessler committed
693 694 695 696 697 698 699
    }

    if (i <= 0 && f[0] != '/')
      strfcpy (prefix, f, sizeof (prefix));
    else
      strfcpy (prefix, f + i + 1, sizeof (prefix));
    killPrefix = 1;
700 701 702 703 704 705
#ifdef USE_IMAP
    }
#endif
  }
  else 
  {
706
    if (!folder)
Michael Elkins's avatar
Michael Elkins committed
707
      getcwd (LastDir, sizeof (LastDir));
708
    else if (!LastDir[0])
709
      strfcpy (LastDir, NONULL(Maildir), sizeof (LastDir));
710
    
711 712 713 714 715
#ifdef USE_IMAP
    if (!buffy && mx_is_imap (LastDir))
    {
      init_state (&state, NULL);
      state.imap_browse = 1;
716
      imap_browse (LastDir, &state);
717
      browser_sort (&state);
718
    }
719
    else
720
#endif
721 722 723 724 725
    {
      i = mutt_strlen (LastDir);
      while (i && LastDir[--i] == '/')
        LastDir[i] = '\0';
      if (!LastDir[0])
Michael Elkins's avatar
Michael Elkins committed
726
        getcwd (LastDir, sizeof (LastDir));
727
    }
Thomas Roessler's avatar
Thomas Roessler committed
728 729 730 731 732 733 734
  }

  *f = 0;

  if (buffy)
  {
    if (examine_mailboxes (NULL, &state) == -1)
735
      goto bail;
Thomas Roessler's avatar
Thomas Roessler committed
736
  }
737 738 739 740 741
  else
#ifdef USE_IMAP
  if (!state.imap_browse)
#endif
  if (examine_directory (NULL, &state, LastDir, prefix) == -1)
742
    goto bail;
Thomas Roessler's avatar
Thomas Roessler committed
743

744
  menu = mutt_new_menu (MENU_FOLDER);
Thomas Roessler's avatar
Thomas Roessler committed
745 746 747 748
  menu->make_entry = folder_entry;
  menu->search = select_file_search;
  menu->title = title;
  menu->data = state.entry;
749 750
  if (multiple)
    menu->tag = file_tag;
Thomas Roessler's avatar
Thomas Roessler committed
751

752 753
  menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_FOLDER,
    FolderHelp);
754
  mutt_push_current_menu (menu);
Thomas Roessler's avatar
Thomas Roessler committed
755 756 757 758 759 760 761 762 763 764 765

  init_menu (&state, menu, title, sizeof (title), buffy);

  FOREVER
  {
    switch (i = mutt_menuLoop (menu))
    {
      case OP_GENERIC_SELECT_ENTRY:

	if (!state.entrylen)
	{
766
	  mutt_error _("No files match the file mask");
Thomas Roessler's avatar
Thomas Roessler committed
767 768 769 770 771
	  break;
	}

        if (S_ISDIR (state.entry[menu->current].mode) ||
	    (S_ISLNK (state.entry[menu->current].mode) &&
772
	    link_is_dir (LastDir, state.entry[menu->current].name)) 
773
#ifdef USE_IMAP
774
	    || state.entry[menu->current].inferiors
775 776
#endif
	    )
Thomas Roessler's avatar
Thomas Roessler committed
777 778 779 780 781 782 783
	{
	  /* make sure this isn't a MH or maildir mailbox */
	  if (buffy)
	  {
	    strfcpy (buf, state.entry[menu->current].name, sizeof (buf));
	    mutt_expand_path (buf, sizeof (buf));
	  }
784 785 786
#ifdef USE_IMAP
	  else if (state.imap_browse)
	  {
787
            strfcpy (buf, state.entry[menu->current].name, sizeof (buf));
788 789
	  }
#endif
Thomas Roessler's avatar
Thomas Roessler committed
790
	  else
791
	    mutt_concat_path (buf, LastDir, state.entry[menu->current].name, sizeof (buf));
Thomas Roessler's avatar
Thomas Roessler committed
792

793 794
	  if ((mx_get_magic (buf) <= 0)
#ifdef USE_IMAP
795
	    || state.entry[menu->current].inferiors
796 797
#endif
	    )
Thomas Roessler's avatar
Thomas Roessler committed
798 799 800 801 802 803
	  {
	    char OldLastDir[_POSIX_PATH_MAX];

	    /* save the old directory */
	    strfcpy (OldLastDir, LastDir, sizeof (OldLastDir));

804
	    if (mutt_strcmp (state.entry[menu->current].name, "..") == 0)
Thomas Roessler's avatar
Thomas Roessler committed
805
	    {
806
	      if (mutt_strcmp ("..", LastDir + mutt_strlen (LastDir) - 2) == 0)
807
		strcat (LastDir, "/..");	/* __STRCAT_CHECKED__ */
Thomas Roessler's avatar
Thomas Roessler committed
808 809 810 811 812 813 814 815 816 817 818
	      else
	      {
		char *p = strrchr (LastDir + 1, '/');

		if (p)
		  *p = 0;
		else
		{
		  if (LastDir[0] == '/')
		    LastDir[1] = 0;
		  else
819
		    strcat (LastDir, "/..");	/* __STRCAT_CHECKED__ */
Thomas Roessler's avatar
Thomas Roessler committed
820 821 822 823 824
		}
	      }
	    }
	    else if (buffy)
	    {
825
	      strfcpy (LastDir, state.entry[menu->current].name, sizeof (LastDir));
Thomas Roessler's avatar
Thomas Roessler committed
826 827
	      mutt_expand_path (LastDir, sizeof (LastDir));
	    }
828 829 830
#ifdef USE_IMAP
	    else if (state.imap_browse)
	    {
831
	      int n;
Thomas Roessler's avatar
Thomas Roessler committed
832
	      ciss_url_t url;
833
	      
834 835
              strfcpy (LastDir, state.entry[menu->current].name,
                sizeof (LastDir));
836
	      /* tack on delimiter here */
837 838 839
	      n = strlen (LastDir)+1;
	      
	      /* special case "" needs no delimiter */
Thomas Roessler's avatar
Thomas Roessler committed
840 841
	      url_parse_ciss (&url, state.entry[menu->current].name);
	      if (url.path &&
842 843
		  (state.entry[menu->current].delim != '\0') &&
		  (n < sizeof (LastDir)))
844 845 846 847
	      {
		LastDir[n] = '\0';
		LastDir[n-1] = state.entry[menu->current].delim;
	      }
848 849
	    }
#endif
Thomas Roessler's avatar
Thomas Roessler committed
850
	    else
851 852
	    {
	      char tmp[_POSIX_PATH_MAX];
853
	      mutt_concat_path (tmp, LastDir, state.entry[menu->current].name, sizeof (tmp));
854 855
	      strfcpy (LastDir, tmp, sizeof (LastDir));
	    }
Thomas Roessler's avatar
Thomas Roessler committed
856 857 858 859 860 861 862 863

	    destroy_state (&state);
	    if (killPrefix)
	    {
	      prefix[0] = 0;
	      killPrefix = 0;
	    }
	    buffy = 0;
864 865 866 867 868
#ifdef USE_IMAP
	    if (state.imap_browse)
	    {
	      init_state (&state, NULL);
	      state.imap_browse = 1;
869
	      imap_browse (LastDir, &state);
870
	      browser_sort (&state);
871 872 873 874
	      menu->data = state.entry;
	    }
	    else
#endif
Thomas Roessler's avatar
Thomas Roessler committed
875 876 877 878 879 880
	    if (examine_directory (menu, &state, LastDir, prefix) == -1)
	    {
	      /* try to restore the old values */
	      strfcpy (LastDir, OldLastDir, sizeof (LastDir));
	      if (examine_directory (menu, &state, LastDir, prefix) == -1)
	      {
Thomas Roessler's avatar
Thomas Roessler committed
881
		strfcpy (LastDir, NONULL(Homedir), sizeof (LastDir));
882
		goto bail;
Thomas Roessler's avatar
Thomas Roessler committed
883 884
	      }
	    }
Thomas Roessler's avatar
Thomas Roessler committed
885 886
	    menu->current = 0; 
	    menu->top = 0; 
Thomas Roessler's avatar
Thomas Roessler committed
887 888 889 890 891 892 893 894 895 896
	    init_menu (&state, menu, title, sizeof (title), buffy);
	    break;
	  }
	}

	if (buffy)
	{
	  strfcpy (f, state.entry[menu->current].name, flen);
	  mutt_expand_path (f, flen);
	}
897 898
#ifdef USE_IMAP
	else if (state.imap_browse)
899
          strfcpy (f, state.entry[menu->current].name, flen);
900
#endif
Thomas Roessler's avatar
Thomas Roessler committed
901
	else
902
	  mutt_concat_path (f, LastDir, state.entry[menu->current].name, flen);
Thomas Roessler's avatar
Thomas Roessler committed
903 904 905 906 907

	/* Fall through to OP_EXIT */

      case OP_EXIT:

908 909 910 911 912 913 914 915
	if (multiple)
	{
	  char **tfiles;
	  int i, j;

	  if (menu->tagged)
	  {
	    *numfiles = menu->tagged;
916
	    tfiles = safe_calloc (*numfiles, sizeof (char *));
917 918 919 920 921 922
	    for (i = 0, j = 0; i < state.entrylen; i++)
	    {
	      struct folder_file ff = state.entry[i];
	      char full[_POSIX_PATH_MAX];
	      if (ff.tagged)
	      {
923
		mutt_concat_path (full, LastDir, ff.name, sizeof (full));
924 925 926 927 928 929 930 931 932
		mutt_expand_path (full, sizeof (full));
		tfiles[j++] = safe_strdup (full);
	      }
	    }
	    *files = tfiles;
	  }
	  else if (f[0]) /* no tagged entries. return selected entry */
	  {
	    *numfiles = 1;
933
	    tfiles = safe_calloc (*numfiles, sizeof (char *));
934 935 936 937 938 939
	    mutt_expand_path (f, flen);
	    tfiles[0] = safe_strdup (f);
	    *files = tfiles;
	  }
	}

Thomas Roessler's avatar
Thomas Roessler committed
940
	destroy_state (&state);
941
	goto bail;
Thomas Roessler's avatar
Thomas Roessler committed
942

943 944
      case OP_BROWSER_TELL:
        if(state.entrylen)
945
	  mutt_message("%s", state.entry[menu->current].name);
946
        break;
947 948 949 950 951 952 953 954 955 956 957 958 959 960 961

#ifdef USE_IMAP
      case OP_BROWSER_SUBSCRIBE:
	imap_subscribe (state.entry[menu->current].name, 1);
	break;

      case OP_BROWSER_UNSUBSCRIBE:
	imap_subscribe (state.entry[menu->current].name, 0);
	break;

      case OP_BROWSER_TOGGLE_LSUB:
	if (option (OPTIMAPLSUB))
	  unset_option (OPTIMAPLSUB);
	else
	  set_option (OPTIMAPLSUB);
Brendan Cully's avatar
Brendan Cully committed
962

963
	mutt_unget_event (0, OP_CHECK_NEW);
964
	break;
965

Thomas Roessler's avatar
Thomas Roessler committed
966 967
      case OP_CREATE_MAILBOX:
	if (!state.imap_browse)
Brendan Cully's avatar
Brendan Cully committed
968
	{
Thomas Roessler's avatar
Thomas Roessler committed
969
	  mutt_error (_("Create is only supported for IMAP mailboxes"));
Brendan Cully's avatar
Brendan Cully committed
970 971 972 973
	  break;
	}

	if (!imap_mailbox_create (LastDir))
Thomas Roessler's avatar
Thomas Roessler committed
974
	{
Brendan Cully's avatar
Brendan Cully committed
975 976 977 978 979 980 981 982 983 984 985
	  /* TODO: find a way to detect if the new folder would appear in
	   *   this window, and insert it without starting over. */
	  destroy_state (&state);
	  init_state (&state, NULL);
	  state.imap_browse = 1;
	  imap_browse (LastDir, &state);
	  browser_sort (&state);
	  menu->data = state.entry;
	  menu->current = 0; 
	  menu->top = 0; 
	  init_menu (&state, menu, title, sizeof (title), buffy);
Thomas Roessler's avatar
Thomas Roessler committed
986
	}
Brendan Cully's avatar
Brendan Cully committed
987
	/* else leave error on screen */
988 989
	break;

990 991 992 993 994 995 996
      case OP_RENAME_MAILBOX:
	if (!state.entry[menu->current].imap)
	  mutt_error (_("Rename is only supported for IMAP mailboxes"));
	else
	{
	  int nentry = menu->current;

Brendan Cully's avatar
Brendan Cully committed
997 998
	  if (imap_mailbox_rename (state.entry[nentry].name) >= 0)
	  {
999 1000 1001 1002
	    destroy_state (&state);
	    init_state (&state, NULL);
	    state.imap_browse = 1;
	    imap_browse (LastDir, &state);
1003
	    browser_sort (&state);
1004 1005 1006 1007 1008 1009 1010 1011 1012
	    menu->data = state.entry;
	    menu->current = 0;
	    menu->top = 0;
	    init_menu (&state, menu, title, sizeof (title), buffy);
	  }
	}
	break;

    case OP_DELETE_MAILBOX:
1013
	if (!state.entry[menu->current].imap)
1014 1015 1016
	  mutt_error (_("Delete is only supported for IMAP mailboxes"));
	else
        {
Thomas Roessler's avatar
Thomas Roessler committed
1017
	  char msg[SHORT_STRING];
1018
	  IMAP_MBOX mx;
1019
	  int nentry = menu->current;
1020

1021
	  imap_parse_path (state.entry[nentry].name, &mx);
1022 1023 1024 1025 1026
	  if (!mx.mbox)
	  {
	    mutt_error _("Cannot delete root folder");
	    break;
	  }
1027
	  snprintf (msg, sizeof (msg), _("Really delete mailbox \"%s\"?"),
1028
            mx.mbox);
1029
	  if (mutt_yesorno (msg, MUTT_NO) == MUTT_YES)
1030
          {
1031
	    if (!imap_delete_mailbox (Context, mx))
1032 1033
            {
	      /* free the mailbox from the browser */
1034 1035
	      FREE (&((state.entry)[nentry].name));
	      FREE (&((state.entry)[nentry].desc));
1036 1037 1038 1039
	      /* and move all other entries up */
	      if (nentry+1 < state.entrylen)
		memmove (state.entry + nentry, state.entry + nentry + 1,
                  sizeof (struct folder_file) * (state.entrylen - (nentry+1)));
1040 1041
              memset (&state.entry[state.entrylen - 1], 0,
                      sizeof (struct folder_file));
1042 1043 1044 1045
	      state.entrylen--;
	      mutt_message _("Mailbox deleted.");
	      init_menu (&state, menu, title, sizeof (title), buffy);
	    }
1046 1047
            else
              mutt_error _("Mailbox deletion failed.");
1048 1049 1050
	  }
	  else
	    mutt_message _("Mailbox not deleted.");
Thomas Roessler's avatar
Thomas Roessler committed
1051
	  FREE (&mx.mbox);
1052 1053
        }
        break;
1054
#endif
1055
      
Thomas Roessler's avatar
Thomas Roessler committed
1056 1057 1058
      case OP_CHANGE_DIRECTORY:

	strfcpy (buf, LastDir, sizeof (buf));
1059 1060 1061
#ifdef USE_IMAP
	if (!state.imap_browse)
#endif
1062
	{
1063
	  /* add '/' at the end of the directory name if not already there */
1064
	  int len=mutt_strlen(LastDir);
1065
	  if (len && LastDir[len-1] != '/' && sizeof (buf) > len)
1066
	    buf[len]='/';
Thomas Roessler's avatar
Thomas Roessler committed
1067 1068
	}

1069
	if (mutt_get_field (_("Chdir to: "), buf, sizeof (buf), MUTT_FILE) == 0 &&
Thomas Roessler's avatar
Thomas Roessler committed
1070 1071 1072 1073
	    buf[0])
	{
	  buffy = 0;	  
	  mutt_expand_path (buf, sizeof (buf));
1074 1075 1076 1077 1078 1079 1080
#ifdef USE_IMAP
	  if (mx_is_imap (buf))
	  {
	    strfcpy (LastDir, buf, sizeof (LastDir));
	    destroy_state (&state);
	    init_state (&state, NULL);
	    state.imap_browse = 1;
1081
	    imap_browse (LastDir, &state);
1082
	    browser_sort (&state);
1083 1084 1085 1086 1087 1088 1089
	    menu->data = state.entry;
	    menu->current = 0; 
	    menu->top = 0; 
	    init_menu (&state, menu, title, sizeof (title), buffy);
	  }
	  else
#endif
Thomas Roessler's avatar
Thomas Roessler committed
1090
	  {
1091
	    if (*buf != '/')
Thomas Roessler's avatar
Thomas Roessler committed
1092
	    {
1093 1094 1095 1096 1097 1098 1099 1100 1101
	      /* in case dir is relative, make it relative to LastDir,
	       * not current working dir */
	      char tmp[_POSIX_PATH_MAX];
	      mutt_concat_path (tmp, LastDir, buf, sizeof (tmp));
	      strfcpy (buf, tmp, sizeof (buf));
	    }
	    if (stat (buf, &st) == 0)
	    {
	      if (S_ISDIR (st.st_mode))
Thomas Roessler's avatar
Thomas Roessler committed
1102
	      {
1103 1104 1105 1106
		destroy_state (&state);
		if (examine_directory (menu, &state, buf, prefix) == 0)
		  strfcpy (LastDir, buf, sizeof (LastDir));
		else
1107
		{
1108 1109 1110 1111 1112
		  mutt_error _("Error scanning directory.");
		  if (examine_directory (menu, &state, LastDir, prefix) == -1)
		  {
		    goto bail;
		  }
1113
		}
1114 1115 1116
		menu->current = 0;
		menu->top = 0;
		init_menu (&state, menu, title, sizeof (title), buffy);
Thomas Roessler's avatar
Thomas Roessler committed
1117
	      }
1118 1119
	      else
		mutt_error (_("%s is not a directory."), buf);
Thomas Roessler's avatar
Thomas Roessler committed
1120 1121
	    }
	    else
1122
	      mutt_perror (buf);
Thomas Roessler's avatar
Thomas Roessler committed
1123 1124 1125 1126 1127 1128
	  }
	}
	break;
	
      case OP_ENTER_MASK:

1129
	strfcpy (buf, NONULL(Mask.pattern), sizeof (buf));
1130
	if (mutt_get_field (_("File Mask: "), buf, sizeof (buf), 0) == 0)
Thomas Roessler's avatar
Thomas Roessler committed
1131 1132
	{
	  regex_t *rx = (regex_t *) safe_malloc (sizeof (regex_t));
Thomas Roessler's avatar
Thomas Roessler committed
1133 1134
	  char *s = buf;
	  int not = 0, err;
Thomas Roessler's avatar
Thomas Roessler committed
1135 1136 1137 1138 1139

	  buffy = 0;
	  /* assume that the user wants to see everything */
	  if (!buf[0])
	    strfcpy (buf, ".", sizeof (buf));
Thomas Roessler's avatar
Thomas Roessler committed
1140 1141 1142 1143 1144 1145 1146
	  SKIPWS (s);
	  if (*s == '!')
	  {
	    s++;
	    SKIPWS (s);
	    not = 1;
	  }
Thomas Roessler's avatar
Thomas Roessler committed
1147

Thomas Roessler's avatar
Thomas Roessler committed
1148
	  if ((err = REGCOMP (rx, s, REG_NOSUB)) != 0)
Thomas Roessler's avatar
Thomas Roessler committed
1149 1150
	  {
	    regerror (err, rx, buf, sizeof (buf));
1151
	    FREE (&rx);
Thomas Roessler's avatar
Thomas Roessler committed
1152 1153 1154 1155
	    mutt_error ("%s", buf);
	  }
	  else
	  {
1156
	    mutt_str_replace (&Mask.pattern, buf);
Thomas Roessler's avatar
Thomas Roessler committed
1157
	    regfree (Mask.rx);
1158
	    FREE (&Mask.rx);
Thomas Roessler's avatar
Thomas Roessler committed
1159
	    Mask.rx = rx;
Thomas Roessler's avatar
Thomas Roessler committed
1160
	    Mask.not = not;
Thomas Roessler's avatar
Thomas Roessler committed
1161 1162

	    destroy_state (&state);
1163 1164 1165 1166 1167
#ifdef USE_IMAP
	    if (state.imap_browse)
	    {
	      init_state (&state, NULL);
	      state.imap_browse = 1;
1168
	      imap_browse (LastDir, &state);
1169
	      browser_sort (&state);
1170 1171 1172 1173 1174
	      menu->data = state.entry;
	      init_menu (&state, menu, title, sizeof (title), buffy);
	    }
	    else
#endif
Thomas Roessler's avatar
Thomas Roessler committed
1175 1176 1177 1178
	    if (examine_directory (menu, &state, LastDir, NULL) == 0)
	      init_menu (&state, menu, title, sizeof (title), buffy);
	    else
	    {
1179
	      mutt_error _("Error scanning directory.");
1180
	      goto bail;
Thomas Roessler's avatar
Thomas Roessler committed
1181 1182
	    }
	    killPrefix = 0;
Thomas Roessler's avatar
Thomas Roessler committed
1183 1184
	    if (!state.entrylen)
	    {
1185
	      mutt_error _("No files match the file mask");
Thomas Roessler's avatar
Thomas Roessler committed
1186 1187
	      break;
	    }
Thomas Roessler's avatar
Thomas Roessler committed
1188 1189 1190 1191 1192 1193 1194
	  }
	}
	break;

      case OP_SORT:
      case OP_SORT_REVERSE:

1195 1196 1197 1198 1199
        {
	  int resort = 1;
	  int reverse = (i == OP_SORT_REVERSE);
	  
	  switch (mutt_multi_choice ((reverse) ?
1200 1201 1202
	      _("Reverse sort by (d)ate, (a)lpha, si(z)e, (c)ount, (u)nread, or do(n)'t sort? ") :
	      _("Sort by (d)ate, (a)lpha, si(z)e, (c)ount, (u)nread, or do(n)'t sort? "),
	      _("dazcun")))
Thomas Roessler's avatar
Thomas Roessler committed
1203
	  {
1204 1205
	    case -1: /* abort */
	      resort = 0;
1206 1207
	      break;

1208
            case 1: /* (d)ate */
1209
	      BrowserSort = SORT_DATE;
Thomas Roessler's avatar
Thomas Roessler committed
1210
	      break;
1211

1212
            case 2: /* (a)lpha */
1213
	      BrowserSort = SORT_SUBJECT;
1214
	      break;
1215 1216

            case 3: /* si(z)e */
1217
	      BrowserSort = SORT_SIZE;
1218
	      break;
1219

1220 1221 1222 1223 1224 1225 1226 1227 1228
            case 4: /* (c)ount */
	      BrowserSort = SORT_COUNT;
	      break;

            case 5: /* (u)nread */
	      BrowserSort = SORT_UNREAD;
	      break;

            case 6: /* do(n)'t sort */
1229
	      BrowserSort = SORT_ORDER;
1230
	      resort = 0;
1231
	      break;
Thomas Roessler's avatar
Thomas Roessler committed
1232
	  }
1233 1234
	  if (resort)
	  {
1235
	    BrowserSort |= reverse ? SORT_REVERSE : 0;
1236 1237 1238 1239
	    browser_sort (&state);
	    menu->redraw = REDRAW_FULL;
	  }
	  break;
Thomas Roessler's avatar
Thomas Roessler committed
1240 1241
	}

Thomas Roessler's avatar
Thomas Roessler committed
1242
      case OP_TOGGLE_MAILBOXES:
Thomas Roessler's avatar
Thomas Roessler committed
1243
	buffy = 1 - buffy;
Thomas Roessler's avatar
Thomas Roessler committed
1244 1245

      case OP_CHECK_NEW:
Thomas Roessler's avatar
Thomas Roessler committed
1246 1247 1248
	destroy_state (&state);
	prefix[0] = 0;
	killPrefix = 0;
1249

Thomas Roessler's avatar
Thomas Roessler committed
1250 1251 1252
	if (buffy)
	{
	  if (examine_mailboxes (menu, &state) == -1)
1253
	    goto bail;
Thomas Roessler's avatar
Thomas Roessler committed
1254
	}
1255 1256 1257 1258 1259
#ifdef USE_IMAP
	else if (mx_is_imap (LastDir))
	{
	  init_state (&state, NULL);
	  state.imap_browse = 1;