simplfications
[apps/madmutt.git] / browser.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
4  *
5  * This file is part of mutt-ng, see http://www.muttng.org/.
6  * It's licensed under the GNU General Public License,
7  * please see the file GPL in the top level source directory.
8  */
9
10 #include <lib-lib/lib-lib.h>
11
12 #include <dirent.h>
13 #include <pwd.h>
14 #include <grp.h>
15
16 #include <lib-ui/lib-ui.h>
17 #include <lib-ui/enter.h>
18 #include <lib-ui/menu.h>
19 #include <lib-ui/sidebar.h>
20 #include <lib-mx/mx.h>
21
22 #include "mutt.h"
23 #include "buffy.h"
24 #include "sort.h"
25 #include "browser.h"
26 #include "attach.h"
27
28 #include <imap/imap.h>
29 #ifdef USE_NNTP
30 #include "nntp.h"
31 #endif
32
33 typedef struct folder_t {
34   struct folder_file *ff;
35   int num;
36 } FOLDER;
37
38 static char LastDir[_POSIX_PATH_MAX] = "";
39 static char LastDirBackup[_POSIX_PATH_MAX] = "";
40
41 /* Frees up the memory allocated for the local-global variables.  */
42 static void destroy_state (struct browser_state *state)
43 {
44   int c;
45
46   for (c = 0; c < state->entrylen; c++) {
47     p_delete(&state->entry[c].name);
48     p_delete(&state->entry[c].desc);
49     p_delete(&state->entry[c].st);
50   }
51   p_delete(&state->folder);
52   p_delete(&state->entry);
53 }
54
55 static int browser_compare_subject (const void *a, const void *b)
56 {
57   struct folder_file *pa = (struct folder_file *) a;
58   struct folder_file *pb = (struct folder_file *) b;
59
60   int r = strcoll(NONULL(pa->name), NONULL(pb->name));
61
62   return ((BrowserSort & SORT_REVERSE) ? -r : r);
63 }
64
65 static int browser_compare_date (const void *a, const void *b)
66 {
67   struct folder_file *pa = (struct folder_file *) a;
68   struct folder_file *pb = (struct folder_file *) b;
69
70   int r = pa->mtime - pb->mtime;
71
72   return ((BrowserSort & SORT_REVERSE) ? -r : r);
73 }
74
75 static int browser_compare_size (const void *a, const void *b)
76 {
77   struct folder_file *pa = (struct folder_file *) a;
78   struct folder_file *pb = (struct folder_file *) b;
79
80   int r = pa->size - pb->size;
81
82   return ((BrowserSort & SORT_REVERSE) ? -r : r);
83 }
84
85 static void browser_sort (struct browser_state *state)
86 {
87   int (*f) (const void *, const void *);
88
89   switch (BrowserSort & SORT_MASK) {
90   case SORT_ORDER:
91     return;
92   case SORT_DATE:
93 #ifdef USE_NNTP
94     if (option (OPTNEWS))
95       return;
96 #endif
97     f = browser_compare_date;
98     break;
99   case SORT_SIZE:
100 #ifdef USE_NNTP
101     if (option (OPTNEWS))
102       return;
103 #endif
104     f = browser_compare_size;
105     break;
106   case SORT_SUBJECT:
107   default:
108     f = browser_compare_subject;
109     break;
110   }
111   qsort (state->entry, state->entrylen, sizeof (struct folder_file), f);
112 }
113
114 static int link_is_dir (const char *folder, const char *path)
115 {
116   struct stat st;
117   char fullpath[_POSIX_PATH_MAX];
118
119   mutt_concat_path(fullpath, sizeof(fullpath), folder, path);
120
121   if (stat (fullpath, &st) == 0)
122     return (S_ISDIR (st.st_mode));
123   else
124     return 0;
125 }
126
127 static const char *
128 folder_format_str(char *dest, ssize_t destlen, char op,
129                   const char *src, const char *fmt,
130                   const char *ifstr, const char *elstr,
131                   anytype data, format_flag flags)
132 {
133   char fn[STRING], tmp[STRING], permission[11], date[16];
134   const char *t_fmt;
135   time_t tnow;
136   FOLDER *folder = data.ptr;
137   struct passwd *pw;
138   struct group *gr;
139   int optional = (flags & M_FORMAT_OPTIONAL);
140
141   switch (op) {
142   case 'C':
143     snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
144     snprintf (dest, destlen, tmp, folder->num + 1);
145     break;
146
147   case 'd':
148     if (folder->ff->st != NULL) {
149       tnow = time (NULL);
150       t_fmt =
151         tnow - folder->ff->st->st_mtime <
152         31536000 ? "%b %d %H:%M" : "%b %d  %Y";
153       strftime (date, sizeof (date), t_fmt,
154                 localtime (&folder->ff->st->st_mtime));
155       mutt_format_s (dest, destlen, fmt, date);
156     }
157     else
158       mutt_format_s (dest, destlen, fmt, "");
159     break;
160
161   case 'f':
162     {
163       const char *s;
164
165       if (folder->ff->imap)
166         s = NONULL(folder->ff->desc);
167       else
168         s = NONULL(folder->ff->name);
169
170       snprintf (fn, sizeof (fn), "%s%s", s,
171                 folder->ff->st ? (S_ISLNK (folder->ff->st->st_mode) ? "@" :
172                                   (S_ISDIR (folder->ff->st->st_mode) ? "/" :
173                                    ((folder->ff->st->st_mode & S_IXUSR) !=
174                                     0 ? "*" : ""))) : "");
175
176       mutt_format_s (dest, destlen, fmt, fn);
177       break;
178     }
179   case 'F':
180     if (folder->ff->st != NULL) {
181       snprintf (permission, sizeof (permission), "%c%c%c%c%c%c%c%c%c%c",
182                 S_ISDIR(folder->ff->st-> st_mode)
183                 ? 'd' : (S_ISLNK(folder->ff->st-> st_mode) ? 'l' : '-'),
184                 (folder->ff->st->st_mode & S_IRUSR) != 0 ? 'r' : '-',
185                 (folder->ff->st->st_mode & S_IWUSR) != 0 ? 'w' : '-',
186                 (folder->ff->st->st_mode & S_ISUID) !=
187                 0 ? 's' : (folder->ff->st->st_mode & S_IXUSR) !=
188                 0 ? 'x' : '-',
189                 (folder->ff->st->st_mode & S_IRGRP) != 0 ? 'r' : '-',
190                 (folder->ff->st->st_mode & S_IWGRP) != 0 ? 'w' : '-',
191                 (folder->ff->st->st_mode & S_ISGID) !=
192                 0 ? 's' : (folder->ff->st->st_mode & S_IXGRP) !=
193                 0 ? 'x' : '-',
194                 (folder->ff->st->st_mode & S_IROTH) != 0 ? 'r' : '-',
195                 (folder->ff->st->st_mode & S_IWOTH) != 0 ? 'w' : '-',
196                 (folder->ff->st->st_mode & S_ISVTX) !=
197                 0 ? 't' : (folder->ff->st->st_mode & S_IXOTH) !=
198                 0 ? 'x' : '-');
199       mutt_format_s (dest, destlen, fmt, permission);
200     }
201     else if (folder->ff->imap) {
202       /* mark folders with subfolders AND mail */
203       snprintf (permission, sizeof (permission), "IMAP %c",
204                 (folder->ff->inferiors
205                  && folder->ff->selectable) ? '+' : ' ');
206       mutt_format_s (dest, destlen, fmt, permission);
207     }
208     else
209       mutt_format_s (dest, destlen, fmt, "");
210     break;
211
212   case 'g':
213     if (folder->ff->st != NULL) {
214       if ((gr = getgrgid (folder->ff->st->st_gid)))
215         mutt_format_s (dest, destlen, fmt, gr->gr_name);
216       else {
217         snprintf (tmp, sizeof (tmp), "%%%sld", fmt);
218         snprintf (dest, destlen, tmp, folder->ff->st->st_gid);
219       }
220     }
221     else
222       mutt_format_s (dest, destlen, fmt, "");
223     break;
224
225   case 'l':
226     if (folder->ff->st != NULL) {
227       snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
228       snprintf (dest, destlen, tmp, folder->ff->st->st_nlink);
229     }
230     else
231       mutt_format_s (dest, destlen, fmt, "");
232     break;
233
234   case 'N':
235     if (imap_is_magic (folder->ff->desc, NULL) == M_IMAP) {
236       if (!optional) {
237         snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
238         snprintf (dest, destlen, tmp, folder->ff->new);
239       }
240       else if (!folder->ff->new)
241         optional = 0;
242       break;
243     }
244     snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
245     snprintf (dest, destlen, tmp, folder->ff->new ? 'N' : ' ');
246     break;
247
248   case 's':
249     if (folder->ff->st != NULL) {
250       snprintf (tmp, sizeof (tmp), "%%%sld", fmt);
251       snprintf (dest, destlen, tmp, (long) folder->ff->st->st_size);
252     }
253     else
254       mutt_format_s (dest, destlen, fmt, "");
255     break;
256
257   case 't':
258     snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
259     snprintf (dest, destlen, tmp, folder->ff->tagged ? '*' : ' ');
260     break;
261
262   case 'u':
263     if (folder->ff->st != NULL) {
264       if ((pw = getpwuid (folder->ff->st->st_uid)))
265         mutt_format_s (dest, destlen, fmt, pw->pw_name);
266       else {
267         snprintf (tmp, sizeof (tmp), "%%%sld", fmt);
268         snprintf (dest, destlen, tmp, folder->ff->st->st_uid);
269       }
270     }
271     else
272       mutt_format_s (dest, destlen, fmt, "");
273     break;
274
275   default:
276     snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
277     snprintf (dest, destlen, tmp, op);
278     break;
279   }
280
281
282   if (flags & M_FORMAT_OPTIONAL)
283     m_strformat(dest, destlen, 0, optional ? ifstr : elstr,
284                 folder_format_str, data, 0);
285
286   return src;
287 }
288
289 #ifdef USE_NNTP
290 static const char *
291 newsgroup_format_str(char *dest, ssize_t destlen, char op,
292                      const char *src, const char *fmt,
293                      const char *ifstr, const char *elstr,
294                      anytype data, format_flag flags)
295 {
296   char fn[STRING], tmp[STRING];
297   FOLDER *folder = data.ptr;
298
299   switch (op) {
300   case 'C':
301     snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
302     snprintf (dest, destlen, tmp, folder->num + 1);
303     break;
304
305   case 'f':
306     m_strcpy(fn, sizeof(fn), folder->ff->name);
307     snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
308     snprintf (dest, destlen, tmp, fn);
309     break;
310
311   case 'N':
312     snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
313     if (folder->ff->nd->subscribed)
314       snprintf (dest, destlen, tmp, ' ');
315     else
316       snprintf (dest, destlen, tmp, folder->ff->new ? 'N' : 'u');
317     break;
318
319   case 'M':
320     snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
321     if (folder->ff->nd->deleted)
322       snprintf (dest, destlen, tmp, 'D');
323     else
324       snprintf (dest, destlen, tmp, folder->ff->nd->allowed ? ' ' : '-');
325     break;
326
327   case 's':
328     if (flags & M_FORMAT_OPTIONAL) {
329       if (folder->ff->nd->unread != 0)
330         m_strformat(dest, destlen, 0, ifstr, newsgroup_format_str,
331                     data, flags);
332       else
333         m_strformat (dest, destlen, 0, elstr, newsgroup_format_str,
334                      data, flags);
335     }
336     else if (Context && Context->data == folder->ff->nd) {
337       snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
338       snprintf (dest, destlen, tmp, Context->unread);
339     }
340     else {
341       snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
342       snprintf (dest, destlen, tmp, folder->ff->nd->unread);
343     }
344     break;
345
346   case 'n':
347     if (Context && Context->data == folder->ff->nd) {
348       snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
349       snprintf (dest, destlen, tmp, Context->new);
350     }
351     else if (option (OPTMARKOLD) &&
352              folder->ff->nd->lastCached >= folder->ff->nd->first &&
353              folder->ff->nd->lastCached <= folder->ff->nd->last) {
354       snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
355       snprintf (dest, destlen, tmp,
356                 folder->ff->nd->last - folder->ff->nd->lastCached);
357     }
358     else {
359       snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
360       snprintf (dest, destlen, tmp, folder->ff->nd->unread);
361     }
362     break;
363
364   case 'd':
365     if (folder->ff->nd->desc != NULL) {
366       snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
367       snprintf (dest, destlen, tmp, folder->ff->nd->desc);
368     }
369     else {
370       snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
371       snprintf (dest, destlen, tmp, "");
372     }
373     break;
374   }
375   return (src);
376 }
377 #endif /* USE_NNTP */
378
379 #ifdef USE_NNTP
380 static void add_folder (MUTTMENU * m, struct browser_state *state,
381                         const char *name, const struct stat *s,
382                         void *data, int new)
383 #else
384 static void add_folder (MUTTMENU * m, struct browser_state *state,
385                         const char *name, const struct stat *s,
386                         int new)
387 #endif
388 {
389   if (state->entrylen == state->entrymax) {
390     /* need to allocate more space */
391     p_realloc(&state->entry, state->entrymax += 256);
392     p_clear(&state->entry[state->entrylen], 256);
393     if (m)
394       m->data = state->entry;
395   }
396
397   if (s != NULL) {
398     (state->entry)[state->entrylen].mode  = s->st_mode;
399     (state->entry)[state->entrylen].mtime = s->st_mtime;
400     (state->entry)[state->entrylen].size  = s->st_size;
401     (state->entry)[state->entrylen].st    = p_dup(s, 1);
402   }
403
404   (state->entry)[state->entrylen].new = new;
405   (state->entry)[state->entrylen].name = m_strdup(name);
406   (state->entry)[state->entrylen].desc = m_strdup(name);
407   (state->entry)[state->entrylen].imap = 0;
408 #ifdef USE_NNTP
409   if (option (OPTNEWS))
410     (state->entry)[state->entrylen].nd = data;
411 #endif
412   (state->entrylen)++;
413 }
414
415 static void init_state (struct browser_state *state, MUTTMENU * menu)
416 {
417   state->entrylen = 0;
418   state->entrymax = 256;
419   state->entry = p_new(struct folder_file, state->entrymax);
420   state->imap_browse = 0;
421   if (menu)
422     menu->data = state->entry;
423 }
424
425 /* get list of all files/newsgroups with mask */
426 static int examine_directory (MUTTMENU * menu, struct browser_state *state,
427                               char *d, const char *prefix)
428 {
429 #ifdef USE_NNTP
430   if (option (OPTNEWS)) {
431     nntp_data_t *data;
432     nntp_server_t *news = CurrentNewsSrv;
433
434 /*  buffy_check (0); */
435     init_state (state, menu);
436
437     for (data = news->list; data; data = data->next) {
438       nntp_sync_sidebar(data);
439       if (m_strncmp (prefix, data->group, m_strlen(prefix)) != 0)
440         continue;
441       if (!((regexec (Mask.rx, data->group, 0, NULL, 0) == 0) ^ Mask.neg))
442         continue;
443       add_folder (menu, state, data->group, NULL, data, data->new);
444     }
445     sidebar_draw ();
446   }
447   else
448 #endif /* USE_NNTP */
449   {
450     struct stat s;
451     DIR *dp;
452     struct dirent *de;
453     char buffer[_POSIX_PATH_MAX + STRING];
454     int i = -1;
455
456     while (stat (d, &s) == -1) {
457       if (errno == ENOENT) {
458         /* The last used directory is deleted, try to use the parent dir. */
459         char *c = strrchr (d, '/');
460
461         if (c && (c > d)) {
462           *c = 0;
463           continue;
464         }
465       }
466       mutt_perror (d);
467       return (-1);
468     }
469
470     if (!S_ISDIR (s.st_mode)) {
471       mutt_error (_("%s is not a directory."), d);
472       return (-1);
473     }
474
475     buffy_check (0);
476
477     if ((dp = opendir (d)) == NULL) {
478       mutt_perror (d);
479       return (-1);
480     }
481
482     init_state (state, menu);
483
484     while ((de = readdir (dp)) != NULL) {
485       if (m_strcmp(de->d_name, ".") == 0)
486         continue;               /* we don't need . */
487
488       if (m_strncmp(prefix, de->d_name, m_strlen(prefix)) != 0)
489         continue;
490       if (!((regexec (Mask.rx, de->d_name, 0, NULL, 0) == 0) ^ Mask.neg))
491         continue;
492
493       mutt_concat_path(buffer, sizeof(buffer), d, de->d_name);
494       if (lstat (buffer, &s) == -1)
495         continue;
496
497       if ((!S_ISREG (s.st_mode)) && (!S_ISDIR (s.st_mode)) &&
498           (!S_ISLNK (s.st_mode)))
499         continue;
500
501       i = buffy_lookup (buffer);
502 #ifdef USE_NNTP
503       add_folder (menu, state, de->d_name, &s, NULL, i >= 0 ? Incoming.arr[i]->new : 0);
504 #else
505       add_folder (menu, state, de->d_name, &s, i >= 0 ? Incoming.arr[i]->new : 0);
506 #endif
507     }
508     closedir (dp);
509   }
510   sidebar_draw ();
511   browser_sort (state);
512   return 0;
513 }
514
515 /* get list of mailboxes/subscribed newsgroups */
516 static int examine_mailboxes (MUTTMENU * menu, struct browser_state *state)
517 {
518   struct stat s;
519   char buffer[LONG_STRING];
520
521 #ifdef USE_NNTP
522   if (option (OPTNEWS)) {
523     nntp_data_t *data;
524     nntp_server_t *news = CurrentNewsSrv;
525
526 /*  buffy_check (0); */
527     init_state (state, menu);
528
529     for (data = news->list; data; data = data->next) {
530       nntp_sync_sidebar (data);
531       if ((data->new || (data->subscribed && 
532                          (!option (OPTSHOWONLYUNREAD)|| data->unread))))
533         add_folder (menu, state, data->group, NULL, data, data->new);
534     }
535     sidebar_draw ();
536   }
537   else
538 #endif
539   {
540     int i = 0;
541     BUFFY* tmp;
542
543     if (!Incoming.len)
544       return (-1);
545     buffy_check (0);
546
547     init_state (state, menu);
548
549     for (i = 0; i < Incoming.len; i++) {
550       tmp = Incoming.arr[i];
551       tmp->magic = mx_get_magic (tmp->path);
552       if (tmp->magic == M_IMAP) {
553 #ifdef USE_NNTP
554         add_folder (menu, state, tmp->path, NULL, NULL, tmp->new);
555 #else
556         add_folder (menu, state, tmp->path, NULL, tmp->new);
557 #endif
558         continue;
559       }
560       if (tmp->magic == M_POP) {
561 #ifdef USE_NNTP
562         add_folder (menu, state, tmp->path, NULL, NULL, tmp->new);
563 #else
564         add_folder (menu, state, tmp->path, NULL, tmp->new);
565 #endif
566         continue;
567       }
568 #ifdef USE_NNTP
569       if (tmp->magic == M_NNTP) {
570         add_folder (menu, state, tmp->path, NULL, NULL, tmp->new);
571         continue;
572       }
573 #endif
574       if (lstat (tmp->path, &s) == -1)
575         continue;
576
577       if ((!S_ISREG (s.st_mode)) && (!S_ISDIR (s.st_mode)) &&
578           (!S_ISLNK (s.st_mode)))
579         continue;
580
581       m_strcpy(buffer, sizeof(buffer), NONULL(tmp->path));
582       mutt_pretty_mailbox (buffer);
583
584 #ifdef USE_NNTP
585       add_folder (menu, state, buffer, &s, NULL, tmp->new);
586 #else
587       add_folder (menu, state, buffer, &s, tmp->new);
588 #endif
589     }
590   }
591   browser_sort (state);
592   return 0;
593 }
594
595 static int select_file_search (MUTTMENU * menu, regex_t * re, int n)
596 {
597 #ifdef USE_NNTP
598   if (option (OPTNEWS))
599     return (regexec
600             (re, ((struct folder_file *) menu->data)[n].desc, 0, NULL, 0));
601 #endif
602   return (regexec
603           (re, ((struct folder_file *) menu->data)[n].name, 0, NULL, 0));
604 }
605
606 static void folder_entry (char *s, ssize_t slen, MUTTMENU * menu, int num)
607 {
608   FOLDER folder;
609
610   folder.ff = &((struct folder_file *) menu->data)[num];
611   folder.num = num;
612
613 #ifdef USE_NNTP
614   if (option (OPTNEWS))
615     m_strformat(s, slen, getmaxx(main_w), GroupFormat, newsgroup_format_str,
616                 &folder, 0);
617   else
618 #endif
619     m_strformat(s, slen, getmaxx(main_w), FolderFormat, folder_format_str,
620                 &folder, 0);
621 }
622
623 static void init_menu (struct browser_state *state, MUTTMENU * menu,
624                        char *title, ssize_t titlelen, int buffy)
625 {
626   char path[_POSIX_PATH_MAX];
627
628   menu->max = state->entrylen;
629
630   if (menu->current >= menu->max)
631     menu->current = menu->max - 1;
632   if (menu->current < 0)
633     menu->current = 0;
634   if (menu->top > menu->current)
635     menu->top = 0;
636
637   menu->tagged = 0;
638
639 #ifdef USE_NNTP
640   if (option (OPTNEWS)) {
641     if (buffy)
642       snprintf (title, titlelen, "%s", _("Subscribed newsgroups"));
643     else
644       snprintf (title, titlelen, _("Newsgroups on server [%s]"),
645                 CurrentNewsSrv->conn->account.host);
646   }
647   else
648 #endif
649   if (buffy)
650     snprintf(title, titlelen, _("Mailboxes [%d]"), buffy_check(0));
651   else {
652     m_strcpy(path, sizeof(path), LastDir);
653     mutt_pretty_mailbox (path);
654     if (state->imap_browse && option (OPTIMAPLSUB))
655       snprintf (title, titlelen, _("Subscribed [%s], File mask: %s"),
656                 path, NONULL (Mask.pattern));
657     else
658       snprintf (title, titlelen, _("Directory [%s], File mask: %s"),
659                 path, NONULL (Mask.pattern));
660   }
661   menu->redraw = REDRAW_FULL;
662 }
663
664 static int file_tag (MUTTMENU * menu, int n, int m)
665 {
666   struct folder_file *ff = &(((struct folder_file *) menu->data)[n]);
667   int ot;
668
669   if (S_ISDIR (ff->mode)
670       || (S_ISLNK (ff->mode) && link_is_dir (LastDir, ff->name))) {
671     mutt_error _("Can't attach a directory!");
672
673     return 0;
674   }
675
676   ot = ff->tagged;
677   ff->tagged = (m >= 0 ? m : !ff->tagged);
678
679   return ff->tagged - ot;
680 }
681
682 void mutt_select_file (char *f, ssize_t flen, int flags, char ***files,
683                        int *numfiles)
684 {
685   char buf[_POSIX_PATH_MAX];
686   char prefix[_POSIX_PATH_MAX] = "";
687   char title[STRING];
688   struct browser_state state;
689   MUTTMENU *menu;
690   struct stat st;
691   int i, killPrefix = 0;
692   int multiple = (flags & M_SEL_MULTI) ? 1 : 0;
693   int folder = (flags & M_SEL_FOLDER) ? 1 : 0;
694   int buffy = (flags & M_SEL_BUFFY) ? 1 : 0;
695
696   buffy = buffy && folder;
697
698   p_clear(&state, 1);
699
700   if (!folder)
701     m_strcpy(LastDirBackup, sizeof(LastDirBackup), LastDir);
702
703 #ifdef USE_NNTP
704   if (option (OPTNEWS)) {
705     if (*f)
706       m_strcpy(prefix, sizeof(prefix), f);
707     else {
708       nntp_data_t *list;
709
710       /* default state for news reader mode is browse subscribed newsgroups */
711       buffy = 0;
712       for (list = CurrentNewsSrv->list; list; list = list->next) {
713         if (list->subscribed) {
714           buffy = 1;
715           break;
716         }
717       }
718     }
719   }
720   else
721 #endif
722   if (*f) {
723     mutt_expand_path (f, flen);
724     if (imap_is_magic (f, NULL) == M_IMAP) {
725       init_state (&state, NULL);
726       state.imap_browse = 1;
727       if (!imap_browse (f, &state))
728         m_strcpy(LastDir, sizeof(LastDir), state.folder);
729     }
730     else {
731       for (i = m_strlen(f) - 1; i > 0 && f[i] != '/'; i--);
732       if (i > 0) {
733         if (f[0] == '/') {
734           i = MIN(ssizeof(LastDir) - 1, i);
735           m_strcpy(LastDir, sizeof(LastDir), f);
736         }
737         else {
738           getcwd(LastDir, sizeof(LastDir));
739           m_strcat(LastDir, sizeof(LastDir), "/");
740           m_strncat(LastDir, sizeof(LastDir), f, i);
741         }
742       }
743       else {
744         if (f[0] == '/')
745           m_strcpy(LastDir, sizeof(LastDir), "/");
746         else
747           getcwd (LastDir, sizeof (LastDir));
748       }
749
750       if (i <= 0 && f[0] != '/')
751         m_strcpy(prefix, sizeof(prefix), f);
752       else
753         m_strcpy(prefix, sizeof(prefix), f + i + 1);
754       killPrefix = 1;
755     }
756   }
757   else {
758     if (!folder)
759       getcwd (LastDir, sizeof (LastDir));
760     else if (!LastDir[0])
761       m_strcpy(LastDir, sizeof(LastDir), NONULL(Maildir));
762
763     if (!buffy && imap_is_magic (LastDir, NULL) == M_IMAP) {
764       init_state (&state, NULL);
765       state.imap_browse = 1;
766       imap_browse (LastDir, &state);
767       browser_sort (&state);
768     }
769   }
770
771   *f = 0;
772
773   if (buffy) {
774     if (examine_mailboxes (NULL, &state) == -1)
775       goto bail;
776   }
777   else
778   if (!state.imap_browse)
779     if (examine_directory (NULL, &state, LastDir, prefix) == -1)
780       goto bail;
781
782   menu = mutt_new_menu ();
783   menu->menu = MENU_FOLDER;
784   menu->make_entry = folder_entry;
785   menu->search = select_file_search;
786   menu->title = title;
787   menu->data = state.entry;
788   if (multiple)
789     menu->tag = file_tag;
790   init_menu (&state, menu, title, sizeof (title), buffy);
791
792   for (;;) {
793     switch (i = mutt_menuLoop (menu)) {
794     case OP_GENERIC_SELECT_ENTRY:
795
796       if (!state.entrylen) {
797         mutt_error _("No files match the file mask");
798
799         break;
800       }
801
802       if (S_ISDIR (state.entry[menu->current].mode) ||
803           (S_ISLNK (state.entry[menu->current].mode) &&
804            link_is_dir (LastDir, state.entry[menu->current].name))
805           || state.entry[menu->current].inferiors
806         ) {
807         /* make sure this isn't a MH or maildir mailbox */
808         if (buffy) {
809           m_strcpy(buf, sizeof(buf), state.entry[menu->current].name);
810           mutt_expand_path (buf, sizeof (buf));
811         }
812         else if (state.imap_browse) {
813           m_strcpy(buf, sizeof(buf), state.entry[menu->current].name);
814         }
815         else
816           mutt_concat_path(buf, sizeof(buf), LastDir,
817                            state.entry[menu->current].name);
818
819         if ((mx_get_magic (buf) <= 0)
820             || state.entry[menu->current].inferiors)
821         {
822           char OldLastDir[_POSIX_PATH_MAX];
823
824           /* save the old directory */
825           m_strcpy(OldLastDir, sizeof(OldLastDir), LastDir);
826
827           if (m_strcmp(state.entry[menu->current].name, "..") == 0) {
828             if (m_strcmp("..", LastDir + m_strlen(LastDir) - 2) == 0)
829               m_strcat(LastDir, sizeof(LastDir), "/..");
830             else {
831               char *p = strrchr (LastDir + 1, '/');
832
833               if (p)
834                 *p = 0;
835               else {
836                 if (LastDir[0] == '/')
837                   LastDir[1] = 0;
838                 else
839                   m_strcat(LastDir, sizeof(LastDir), "/..");
840               }
841             }
842           }
843           else if (buffy) {
844             m_strcpy(LastDir, sizeof(LastDir),
845                      state.entry[menu->current].name);
846             mutt_expand_path (LastDir, sizeof (LastDir));
847           }
848           else if (state.imap_browse) {
849             int n;
850             ciss_url_t url;
851
852             m_strcpy(LastDir, sizeof(LastDir),
853                      state.entry[menu->current].name);
854             /* tack on delimiter here */
855             n = m_strlen(LastDir) + 1;
856
857             /* special case "" needs no delimiter */
858             url_parse_ciss (&url, state.entry[menu->current].name);
859             if (url.path &&
860                 (state.entry[menu->current].delim != '\0') &&
861                 (n < ssizeof (LastDir))) {
862               LastDir[n] = '\0';
863               LastDir[n - 1] = state.entry[menu->current].delim;
864             }
865           }
866           else {
867             char tmp[_POSIX_PATH_MAX];
868
869             mutt_concat_path(tmp, sizeof(tmp), LastDir,
870                              state.entry[menu->current].name);
871             m_strcpy(LastDir, sizeof(LastDir), tmp);
872           }
873
874           destroy_state (&state);
875           if (killPrefix) {
876             prefix[0] = 0;
877             killPrefix = 0;
878           }
879           buffy = 0;
880           if (state.imap_browse) {
881             init_state (&state, NULL);
882             state.imap_browse = 1;
883             imap_browse (LastDir, &state);
884             browser_sort (&state);
885             menu->data = state.entry;
886           }
887           else
888           if (examine_directory (menu, &state, LastDir, prefix) == -1) {
889             /* try to restore the old values */
890             m_strcpy(LastDir, sizeof(LastDir), OldLastDir);
891             if (examine_directory (menu, &state, LastDir, prefix) == -1) {
892               m_strcpy(LastDir, sizeof(LastDir), NONULL(mod_core.homedir));
893               goto bail;
894             }
895           }
896           menu->current = 0;
897           menu->top = 0;
898           init_menu (&state, menu, title, sizeof (title), buffy);
899           break;
900         }
901       }
902
903 #ifdef USE_NNTP
904       if (buffy || option (OPTNEWS))    /* news have not path */
905 #else
906       if (buffy)
907 #endif
908       {
909         m_strcpy(f, flen, state.entry[menu->current].name);
910         mutt_expand_path (f, flen);
911       }
912       else if (state.imap_browse)
913         m_strcpy(f, flen, state.entry[menu->current].name);
914       else
915         mutt_concat_path(f, flen, LastDir, state.entry[menu->current].name);
916
917       /* Fall through to OP_EXIT */
918
919     case OP_EXIT:
920
921       if (multiple) {
922         char **tfiles;
923         int j;
924         int h;
925
926         if (menu->tagged) {
927           *numfiles = menu->tagged;
928           tfiles = p_new(char *, *numfiles);
929           for (h = 0, j = 0; h < state.entrylen; i++) {
930             struct folder_file ff = state.entry[i];
931             char full[_POSIX_PATH_MAX];
932
933             if (ff.tagged) {
934               mutt_concat_path(full, sizeof(full), LastDir, ff.name);
935               mutt_expand_path (full, sizeof (full));
936               tfiles[j++] = m_strdup(full);
937             }
938           }
939           *files = tfiles;
940         }
941         else if (f[0]) {        /* no tagged entries. return selected entry */
942           *numfiles = 1;
943           tfiles = p_new(char *, *numfiles);
944           mutt_expand_path (f, flen);
945           tfiles[0] = m_strdup(f);
946           *files = tfiles;
947         }
948       }
949
950       destroy_state (&state);
951       mutt_menuDestroy (&menu);
952       goto bail;
953
954     case OP_BROWSER_TELL:
955       if (state.entrylen)
956         mutt_message ("%s", state.entry[menu->current].name);
957       break;
958
959     case OP_BROWSER_TOGGLE_LSUB:
960       if (option (OPTIMAPLSUB)) {
961         unset_option (OPTIMAPLSUB);
962       }
963       else {
964         set_option (OPTIMAPLSUB);
965       }
966       mutt_ungetch (0, OP_CHECK_NEW);
967       break;
968
969     case OP_CREATE_MAILBOX:
970       if (!state.imap_browse)
971         mutt_error (_("Create is only supported for IMAP mailboxes"));
972       else {
973         imap_mailbox_create (LastDir);
974         /* TODO: find a way to detect if the new folder would appear in
975          *   this window, and insert it without starting over. */
976         destroy_state (&state);
977         init_state (&state, NULL);
978         state.imap_browse = 1;
979         imap_browse (LastDir, &state);
980         browser_sort (&state);
981         menu->data = state.entry;
982         menu->current = 0;
983         menu->top = 0;
984         init_menu (&state, menu, title, sizeof (title), buffy);
985         MAYBE_REDRAW (menu->redraw);
986       }
987       break;
988
989     case OP_RENAME_MAILBOX:
990       if (!state.entry[menu->current].imap)
991         mutt_error (_("Rename is only supported for IMAP mailboxes"));
992       else {
993         int nentry = menu->current;
994
995         if (imap_mailbox_rename (state.entry[nentry].name) >= 0) {
996           destroy_state (&state);
997           init_state (&state, NULL);
998           state.imap_browse = 1;
999           imap_browse (LastDir, &state);
1000           browser_sort (&state);
1001           menu->data = state.entry;
1002           menu->current = 0;
1003           menu->top = 0;
1004           init_menu (&state, menu, title, sizeof (title), buffy);
1005           MAYBE_REDRAW (menu->redraw);
1006         }
1007       }
1008       break;
1009
1010     case OP_DELETE_MAILBOX:
1011       if (!state.entry[menu->current].imap)
1012         mutt_error (_("Delete is only supported for IMAP mailboxes"));
1013       else {
1014         char msg[STRING];
1015         IMAP_MBOX mx;
1016         int nentry = menu->current;
1017
1018         imap_parse_path (state.entry[nentry].name, &mx);
1019         snprintf (msg, sizeof (msg), _("Really delete mailbox \"%s\"?"),
1020                   mx.mbox);
1021         if (mutt_yesorno (msg, M_NO) == M_YES) {
1022           if (!imap_delete_mailbox (Context, mx)) {
1023             /* free the mailbox from the browser */
1024             p_delete(&((state.entry)[nentry].name));
1025             p_delete(&((state.entry)[nentry].desc));
1026             /* and move all other entries up */
1027             if (nentry + 1 < state.entrylen)
1028               memmove (state.entry + nentry, state.entry + nentry + 1,
1029                        sizeof (struct folder_file) * (state.entrylen -
1030                                                       (nentry + 1)));
1031             state.entrylen--;
1032             mutt_message _("Mailbox deleted.");
1033
1034             init_menu (&state, menu, title, sizeof (title), buffy);
1035             MAYBE_REDRAW (menu->redraw);
1036           }
1037         }
1038         else
1039           mutt_message _("Mailbox not deleted.");
1040         p_delete(&mx.mbox);
1041       }
1042       break;
1043
1044     case OP_CHANGE_DIRECTORY:
1045
1046 #ifdef USE_NNTP
1047       if (option (OPTNEWS))
1048         break;
1049 #endif
1050
1051       m_strcpy(buf, sizeof(buf), LastDir);
1052       if (!state.imap_browse)
1053       {
1054         /* add '/' at the end of the directory name if not already there */
1055         ssize_t len = m_strlen(LastDir);
1056
1057         if (len && LastDir[len - 1] != '/' && ssizeof(buf) > len)
1058           buf[len] = '/';
1059       }
1060
1061       if (mutt_get_field (_("Chdir to: "), buf, sizeof (buf), M_FILE) == 0 &&
1062           buf[0]) {
1063         buffy = 0;
1064         mutt_expand_path (buf, sizeof (buf));
1065         if (imap_is_magic (buf, NULL) == M_IMAP) {
1066           m_strcpy(LastDir, sizeof(LastDir), buf);
1067           destroy_state (&state);
1068           init_state (&state, NULL);
1069           state.imap_browse = 1;
1070           imap_browse (LastDir, &state);
1071           browser_sort (&state);
1072           menu->data = state.entry;
1073           menu->current = 0;
1074           menu->top = 0;
1075           init_menu (&state, menu, title, sizeof (title), buffy);
1076         }
1077         else
1078         if (stat (buf, &st) == 0) {
1079           if (S_ISDIR (st.st_mode)) {
1080             destroy_state (&state);
1081             if (examine_directory (menu, &state, buf, prefix) == 0)
1082               m_strcpy(LastDir, sizeof(LastDir), buf);
1083             else {
1084               mutt_error _("Error scanning directory.");
1085
1086               if (examine_directory (menu, &state, LastDir, prefix) == -1) {
1087                 mutt_menuDestroy (&menu);
1088                 goto bail;
1089               }
1090             }
1091             menu->current = 0;
1092             menu->top = 0;
1093             init_menu (&state, menu, title, sizeof (title), buffy);
1094           }
1095           else
1096             mutt_error (_("%s is not a directory."), buf);
1097         }
1098         else
1099           mutt_perror (buf);
1100       }
1101       MAYBE_REDRAW (menu->redraw);
1102       break;
1103
1104     case OP_ENTER_MASK:
1105
1106       m_strcpy(buf, sizeof(buf), NONULL(Mask.pattern));
1107       if (mutt_get_field (_("File Mask: "), buf, sizeof (buf), 0) == 0) {
1108         regex_t *rx = p_new(regex_t, 1);
1109         char *s = buf;
1110         int neg = 0, err;
1111
1112         buffy = 0;
1113         /* assume that the user wants to see everything */
1114         if (!buf[0])
1115           m_strcpy(buf, sizeof(buf), ".");
1116         s = vskipspaces(s);
1117         if (*s == '!') {
1118           s = vskipspaces(s + 1);
1119           neg = 1;
1120         }
1121
1122         if ((err = REGCOMP (rx, s, REG_NOSUB)) != 0) {
1123           regerror (err, rx, buf, sizeof (buf));
1124           regfree (rx);
1125           p_delete(&rx);
1126           mutt_error ("%s", buf);
1127         }
1128         else {
1129           m_strreplace(&Mask.pattern, buf);
1130           regfree (Mask.rx);
1131           p_delete(&Mask.rx);
1132           Mask.rx = rx;
1133           Mask.neg = neg;
1134
1135           destroy_state (&state);
1136           if (state.imap_browse) {
1137             init_state (&state, NULL);
1138             state.imap_browse = 1;
1139             imap_browse (LastDir, &state);
1140             browser_sort (&state);
1141             menu->data = state.entry;
1142             init_menu (&state, menu, title, sizeof (title), buffy);
1143           }
1144           else
1145           if (examine_directory (menu, &state, LastDir, NULL) == 0)
1146             init_menu (&state, menu, title, sizeof (title), buffy);
1147           else {
1148             mutt_error _("Error scanning directory.");
1149
1150             mutt_menuDestroy (&menu);
1151             goto bail;
1152           }
1153           killPrefix = 0;
1154           if (!state.entrylen) {
1155             mutt_error _("No files match the file mask");
1156
1157             break;
1158           }
1159         }
1160       }
1161       MAYBE_REDRAW (menu->redraw);
1162       break;
1163
1164     case OP_SORT:
1165     case OP_SORT_REVERSE:
1166
1167       {
1168         int resort = 1;
1169         int reverse = (i == OP_SORT_REVERSE);
1170
1171         switch (mutt_multi_choice ((reverse) ?
1172                                    _
1173                                    ("Reverse sort by (d)ate, (a)lpha, si(z)e or do(n)'t sort? ")
1174                                    :
1175                                    _
1176                                    ("Sort by (d)ate, (a)lpha, si(z)e or do(n)'t sort? "),
1177                                    _("dazn"))) {
1178         case -1:               /* abort */
1179           resort = 0;
1180           break;
1181
1182         case 1:                /* (d)ate */
1183           BrowserSort = SORT_DATE;
1184           break;
1185
1186         case 2:                /* (a)lpha */
1187           BrowserSort = SORT_SUBJECT;
1188           break;
1189
1190         case 3:                /* si(z)e */
1191           BrowserSort = SORT_SIZE;
1192           break;
1193
1194         case 4:                /* do(n)'t sort */
1195           BrowserSort = SORT_ORDER;
1196           resort = 0;
1197           break;
1198         }
1199         if (resort) {
1200           BrowserSort |= reverse ? SORT_REVERSE : 0;
1201           browser_sort (&state);
1202           menu->redraw = REDRAW_FULL;
1203         }
1204         break;
1205       }
1206
1207     case OP_TOGGLE_MAILBOXES:
1208       buffy = 1 - buffy;
1209
1210     case OP_CHECK_NEW:
1211       destroy_state (&state);
1212       prefix[0] = 0;
1213       killPrefix = 0;
1214
1215       if (buffy) {
1216         if (examine_mailboxes (menu, &state) == -1)
1217           goto bail;
1218       }
1219       else if (imap_is_magic (LastDir, NULL) == M_IMAP) {
1220         init_state (&state, NULL);
1221         state.imap_browse = 1;
1222         imap_browse (LastDir, &state);
1223         browser_sort (&state);
1224         menu->data = state.entry;
1225       }
1226       else if (examine_directory (menu, &state, LastDir, prefix) == -1)
1227         goto bail;
1228       init_menu (&state, menu, title, sizeof (title), buffy);
1229       break;
1230
1231     case OP_BUFFY_LIST:
1232       if (option (OPTFORCEBUFFYCHECK))
1233         buffy_check (1);
1234       buffy_list ();
1235       break;
1236
1237     case OP_BROWSER_NEW_FILE:
1238
1239       snprintf (buf, sizeof (buf), "%s/", LastDir);
1240       if (mutt_get_field (_("New file name: "), buf, sizeof (buf), M_FILE) ==
1241           0) {
1242         m_strcpy(f, flen, buf);
1243         destroy_state (&state);
1244         mutt_menuDestroy (&menu);
1245         goto bail;
1246       }
1247       MAYBE_REDRAW (menu->redraw);
1248       break;
1249
1250     case OP_BROWSER_VIEW_FILE:
1251       if (!state.entrylen) {
1252         mutt_error _("No files match the file mask");
1253
1254         break;
1255       }
1256
1257       if (state.entry[menu->current].selectable) {
1258         m_strcpy(f, flen, state.entry[menu->current].name);
1259         destroy_state (&state);
1260         mutt_menuDestroy (&menu);
1261         goto bail;
1262       }
1263       else
1264       if (S_ISDIR (state.entry[menu->current].mode) ||
1265             (S_ISLNK (state.entry[menu->current].mode) &&
1266                link_is_dir (LastDir, state.entry[menu->current].name))) {
1267         mutt_error _("Can't view a directory");
1268
1269         break;
1270       }
1271       else {
1272         BODY *b;
1273         char nbuf[_POSIX_PATH_MAX];
1274
1275         mutt_concat_path(nbuf, sizeof(nbuf), LastDir,
1276                          state.entry[menu->current].name);
1277         b = mutt_make_file_attach (nbuf);
1278         if (b != NULL) {
1279           mutt_view_attachment (NULL, b, M_REGULAR, NULL, NULL, 0);
1280           body_list_wipe(&b);
1281           menu->redraw = REDRAW_FULL;
1282         }
1283         else
1284           mutt_error _("Error trying to view file");
1285       }
1286       break;
1287
1288 #ifdef USE_NNTP
1289     case OP_CATCHUP:
1290     case OP_UNCATCHUP:
1291       if (option (OPTNEWS)) {
1292         struct folder_file *folder_f = &state.entry[menu->current];
1293         nntp_data_t *nd;
1294
1295         if (i == OP_CATCHUP)
1296           nd = mutt_newsgroup_catchup (CurrentNewsSrv, folder_f->name);
1297         else
1298           nd = mutt_newsgroup_uncatchup (CurrentNewsSrv, folder_f->name);
1299
1300         if (nd) {
1301           if (menu->current + 1 < menu->max)
1302             menu->current++;
1303           menu->redraw = REDRAW_MOTION_RESYNCH;
1304         }
1305       }
1306       break;
1307
1308     case OP_LOAD_ACTIVE:
1309       if (!option (OPTNEWS))
1310         break;
1311
1312       {
1313         nntp_data_t *data;
1314
1315         for (data = CurrentNewsSrv->list; data; data = data->next) {
1316           data->deleted = 1;
1317         }
1318       }
1319       nntp_get_active (CurrentNewsSrv);
1320
1321       destroy_state (&state);
1322       if (buffy)
1323         examine_mailboxes (menu, &state);
1324       else
1325         examine_directory (menu, &state, NULL, NULL);
1326       init_menu (&state, menu, title, sizeof (title), buffy);
1327       break;
1328 #endif /* USE_NNTP */
1329
1330     case OP_BROWSER_SUBSCRIBE:
1331     case OP_BROWSER_UNSUBSCRIBE:
1332 #ifdef USE_NNTP
1333     case OP_SUBSCRIBE_PATTERN:
1334     case OP_UNSUBSCRIBE_PATTERN:
1335       if (option (OPTNEWS)) {
1336         regex_t *rx = p_new(regex_t, 1);
1337         char *s = buf;
1338         int j = menu->current;
1339         nntp_data_t *nd;
1340         nntp_server_t *news = CurrentNewsSrv;
1341
1342         if (i == OP_SUBSCRIBE_PATTERN || i == OP_UNSUBSCRIBE_PATTERN) {
1343           char tmp[STRING];
1344           int err;
1345
1346           buf[0] = 0;
1347           if (i == OP_SUBSCRIBE_PATTERN)
1348             snprintf (tmp, sizeof (tmp), _("Subscribe pattern: "));
1349           else
1350             snprintf (tmp, sizeof (tmp), _("Unsubscribe pattern: "));
1351           if (mutt_get_field (tmp, buf, sizeof (buf), 0) != 0 || !buf[0]) {
1352             p_delete(&rx);
1353             break;
1354           }
1355
1356           if ((err = REGCOMP (rx, s, REG_NOSUB)) != 0) {
1357             regerror (err, rx, buf, sizeof (buf));
1358             regfree (rx);
1359             p_delete(&rx);
1360             mutt_error ("%s", buf);
1361             break;
1362           }
1363           menu->redraw = REDRAW_FULL;
1364           j = 0;
1365         }
1366         else if (!state.entrylen) {
1367           mutt_error _("No newsgroups match the mask");
1368
1369           break;
1370         }
1371
1372         for (; j < state.entrylen; j++) {
1373           struct folder_file *folderf = &state.entry[j];
1374
1375           if (i == OP_BROWSER_SUBSCRIBE || i == OP_BROWSER_UNSUBSCRIBE ||
1376               regexec (rx, folderf->name, 0, NULL, 0) == 0) {
1377             if (i == OP_BROWSER_SUBSCRIBE || i == OP_SUBSCRIBE_PATTERN)
1378               nd = mutt_newsgroup_subscribe (news, folderf->name);
1379             else
1380               nd = mutt_newsgroup_unsubscribe (news, folderf->name);
1381           }
1382           if (i == OP_BROWSER_SUBSCRIBE || i == OP_BROWSER_UNSUBSCRIBE) {
1383             if (menu->current + 1 < menu->max)
1384               menu->current++;
1385             menu->redraw = REDRAW_MOTION_RESYNCH;
1386             break;
1387           }
1388         }
1389         if (i == OP_SUBSCRIBE_PATTERN) {
1390           nd = news ? news->list : NULL;
1391           for (; nd; nd = nd->next) {
1392             if (nd->group && !nd->subscribed) {
1393               if (regexec (rx, nd->group, 0, NULL, 0) == 0) {
1394                 mutt_newsgroup_subscribe (news, nd->group);
1395                 add_folder (menu, &state, nd->group, NULL, nd, nd->new);
1396               }
1397             }
1398           }
1399           init_menu (&state, menu, title, sizeof (title), buffy);
1400         }
1401         mutt_newsrc_update (news);
1402         if (i != OP_BROWSER_SUBSCRIBE && i != OP_BROWSER_UNSUBSCRIBE)
1403           regfree (rx);
1404         p_delete(&rx);
1405       }
1406       else
1407 #endif /* USE_NNTP */
1408       {
1409         if (i == OP_BROWSER_SUBSCRIBE)
1410           imap_subscribe (state.entry[menu->current].name, 1);
1411         else
1412           imap_subscribe (state.entry[menu->current].name, 0);
1413       }
1414     }
1415   }
1416
1417 bail:
1418
1419   if (!folder)
1420     m_strcpy(LastDir, sizeof(LastDir), LastDirBackup);
1421
1422 }