fix spurious encoding issue in de.po
[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
30 typedef struct folder_t {
31   struct folder_file *ff;
32   int num;
33 } FOLDER;
34
35 static char LastDir[_POSIX_PATH_MAX] = "";
36 static char LastDirBackup[_POSIX_PATH_MAX] = "";
37
38 /* Frees up the memory allocated for the local-global variables.  */
39 static void destroy_state (struct browser_state *state)
40 {
41   int c;
42
43   for (c = 0; c < state->entrylen; c++) {
44     p_delete(&state->entry[c].name);
45     p_delete(&state->entry[c].desc);
46     p_delete(&state->entry[c].st);
47   }
48   p_delete(&state->folder);
49   p_delete(&state->entry);
50 }
51
52 static int browser_compare_subject (const void *a, const void *b)
53 {
54   struct folder_file *pa = (struct folder_file *) a;
55   struct folder_file *pb = (struct folder_file *) b;
56
57   int r = strcoll(NONULL(pa->name), NONULL(pb->name));
58
59   return ((BrowserSort & SORT_REVERSE) ? -r : r);
60 }
61
62 static int browser_compare_date (const void *a, const void *b)
63 {
64   struct folder_file *pa = (struct folder_file *) a;
65   struct folder_file *pb = (struct folder_file *) b;
66
67   int r = pa->mtime - pb->mtime;
68
69   return ((BrowserSort & SORT_REVERSE) ? -r : r);
70 }
71
72 static int browser_compare_size (const void *a, const void *b)
73 {
74   struct folder_file *pa = (struct folder_file *) a;
75   struct folder_file *pb = (struct folder_file *) b;
76
77   int r = pa->size - pb->size;
78
79   return ((BrowserSort & SORT_REVERSE) ? -r : r);
80 }
81
82 static void browser_sort (struct browser_state *state)
83 {
84   int (*f) (const void *, const void *);
85
86   switch (BrowserSort & SORT_MASK) {
87   case SORT_ORDER:
88     return;
89   case SORT_DATE:
90     f = browser_compare_date;
91     break;
92   case SORT_SIZE:
93     f = browser_compare_size;
94     break;
95   case SORT_SUBJECT:
96   default:
97     f = browser_compare_subject;
98     break;
99   }
100   qsort (state->entry, state->entrylen, sizeof (struct folder_file), f);
101 }
102
103 static int link_is_dir (const char *folder, const char *path)
104 {
105   struct stat st;
106   char fullpath[_POSIX_PATH_MAX];
107
108   mutt_concat_path(fullpath, sizeof(fullpath), folder, path);
109
110   if (stat (fullpath, &st) == 0)
111     return (S_ISDIR (st.st_mode));
112   else
113     return 0;
114 }
115
116 static const char *
117 folder_format_str(char *dest, ssize_t destlen, char op,
118                   const char *src, const char *fmt,
119                   const char *ifstr, const char *elstr,
120                   anytype data, format_flag flags)
121 {
122   char fn[STRING], tmp[STRING], permission[11], date[16];
123   const char *t_fmt;
124   time_t tnow;
125   FOLDER *folder = data.ptr;
126   struct passwd *pw;
127   struct group *gr;
128   int optional = (flags & M_FORMAT_OPTIONAL);
129
130   switch (op) {
131   case 'C':
132     snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
133     snprintf (dest, destlen, tmp, folder->num + 1);
134     break;
135
136   case 'd':
137     if (folder->ff->st != NULL) {
138       tnow = time (NULL);
139       t_fmt =
140         tnow - folder->ff->st->st_mtime <
141         31536000 ? "%b %d %H:%M" : "%b %d  %Y";
142       strftime (date, sizeof (date), t_fmt,
143                 localtime (&folder->ff->st->st_mtime));
144       mutt_format_s (dest, destlen, fmt, date);
145     }
146     else
147       mutt_format_s (dest, destlen, fmt, "");
148     break;
149
150   case 'f':
151     {
152       const char *s;
153
154       if (folder->ff->imap)
155         s = NONULL(folder->ff->desc);
156       else
157         s = NONULL(folder->ff->name);
158
159       snprintf (fn, sizeof (fn), "%s%s", s,
160                 folder->ff->st ? (S_ISLNK (folder->ff->st->st_mode) ? "@" :
161                                   (S_ISDIR (folder->ff->st->st_mode) ? "/" :
162                                    ((folder->ff->st->st_mode & S_IXUSR) !=
163                                     0 ? "*" : ""))) : "");
164
165       mutt_format_s (dest, destlen, fmt, fn);
166       break;
167     }
168   case 'F':
169     if (folder->ff->st != NULL) {
170       snprintf (permission, sizeof (permission), "%c%c%c%c%c%c%c%c%c%c",
171                 S_ISDIR(folder->ff->st-> st_mode)
172                 ? 'd' : (S_ISLNK(folder->ff->st-> st_mode) ? 'l' : '-'),
173                 (folder->ff->st->st_mode & S_IRUSR) != 0 ? 'r' : '-',
174                 (folder->ff->st->st_mode & S_IWUSR) != 0 ? 'w' : '-',
175                 (folder->ff->st->st_mode & S_ISUID) !=
176                 0 ? 's' : (folder->ff->st->st_mode & S_IXUSR) !=
177                 0 ? 'x' : '-',
178                 (folder->ff->st->st_mode & S_IRGRP) != 0 ? 'r' : '-',
179                 (folder->ff->st->st_mode & S_IWGRP) != 0 ? 'w' : '-',
180                 (folder->ff->st->st_mode & S_ISGID) !=
181                 0 ? 's' : (folder->ff->st->st_mode & S_IXGRP) !=
182                 0 ? 'x' : '-',
183                 (folder->ff->st->st_mode & S_IROTH) != 0 ? 'r' : '-',
184                 (folder->ff->st->st_mode & S_IWOTH) != 0 ? 'w' : '-',
185                 (folder->ff->st->st_mode & S_ISVTX) !=
186                 0 ? 't' : (folder->ff->st->st_mode & S_IXOTH) !=
187                 0 ? 'x' : '-');
188       mutt_format_s (dest, destlen, fmt, permission);
189     }
190     else if (folder->ff->imap) {
191       /* mark folders with subfolders AND mail */
192       snprintf (permission, sizeof (permission), "IMAP %c",
193                 (folder->ff->inferiors
194                  && folder->ff->selectable) ? '+' : ' ');
195       mutt_format_s (dest, destlen, fmt, permission);
196     }
197     else
198       mutt_format_s (dest, destlen, fmt, "");
199     break;
200
201   case 'g':
202     if (folder->ff->st != NULL) {
203       if ((gr = getgrgid (folder->ff->st->st_gid)))
204         mutt_format_s (dest, destlen, fmt, gr->gr_name);
205       else {
206         snprintf (tmp, sizeof (tmp), "%%%sld", fmt);
207         snprintf (dest, destlen, tmp, folder->ff->st->st_gid);
208       }
209     }
210     else
211       mutt_format_s (dest, destlen, fmt, "");
212     break;
213
214   case 'l':
215     if (folder->ff->st != NULL) {
216       snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
217       snprintf (dest, destlen, tmp, folder->ff->st->st_nlink);
218     }
219     else
220       mutt_format_s (dest, destlen, fmt, "");
221     break;
222
223   case 'N':
224     if (imap_is_magic (folder->ff->desc, NULL) == M_IMAP) {
225       if (!optional) {
226         snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
227         snprintf (dest, destlen, tmp, folder->ff->new);
228       }
229       else if (!folder->ff->new)
230         optional = 0;
231       break;
232     }
233     snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
234     snprintf (dest, destlen, tmp, folder->ff->new ? 'N' : ' ');
235     break;
236
237   case 's':
238     if (folder->ff->st != NULL) {
239       snprintf (tmp, sizeof (tmp), "%%%sld", fmt);
240       snprintf (dest, destlen, tmp, (long) folder->ff->st->st_size);
241     }
242     else
243       mutt_format_s (dest, destlen, fmt, "");
244     break;
245
246   case 't':
247     snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
248     snprintf (dest, destlen, tmp, folder->ff->tagged ? '*' : ' ');
249     break;
250
251   case 'u':
252     if (folder->ff->st != NULL) {
253       if ((pw = getpwuid (folder->ff->st->st_uid)))
254         mutt_format_s (dest, destlen, fmt, pw->pw_name);
255       else {
256         snprintf (tmp, sizeof (tmp), "%%%sld", fmt);
257         snprintf (dest, destlen, tmp, folder->ff->st->st_uid);
258       }
259     }
260     else
261       mutt_format_s (dest, destlen, fmt, "");
262     break;
263
264   default:
265     snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
266     snprintf (dest, destlen, tmp, op);
267     break;
268   }
269
270
271   if (flags & M_FORMAT_OPTIONAL)
272     m_strformat(dest, destlen, 0, optional ? ifstr : elstr,
273                 folder_format_str, data, 0);
274
275   return src;
276 }
277
278
279 static void add_folder (MUTTMENU * m, struct browser_state *state,
280                         const char *name, const struct stat *s, int new)
281 {
282   if (state->entrylen == state->entrymax) {
283     /* need to allocate more space */
284     p_realloc(&state->entry, state->entrymax += 256);
285     p_clear(&state->entry[state->entrylen], 256);
286     if (m)
287       m->data = state->entry;
288   }
289
290   if (s != NULL) {
291     (state->entry)[state->entrylen].mode  = s->st_mode;
292     (state->entry)[state->entrylen].mtime = s->st_mtime;
293     (state->entry)[state->entrylen].size  = s->st_size;
294     (state->entry)[state->entrylen].st    = p_dup(s, 1);
295   }
296
297   (state->entry)[state->entrylen].new = new;
298   (state->entry)[state->entrylen].name = m_strdup(name);
299   (state->entry)[state->entrylen].desc = m_strdup(name);
300   (state->entry)[state->entrylen].imap = 0;
301   (state->entrylen)++;
302 }
303
304 static void init_state (struct browser_state *state, MUTTMENU * menu)
305 {
306   state->entrylen = 0;
307   state->entrymax = 256;
308   state->entry = p_new(struct folder_file, state->entrymax);
309   state->imap_browse = 0;
310   if (menu)
311     menu->data = state->entry;
312 }
313
314 /* get list of all files/newsgroups with mask */
315 static int examine_directory (MUTTMENU * menu, struct browser_state *state,
316                               char *d, const char *prefix)
317 {
318  
319   struct stat s;
320   DIR *dp;
321   struct dirent *de;
322   char buffer[_POSIX_PATH_MAX + STRING];
323   int i = -1;
324
325   while (stat (d, &s) == -1) {
326     if (errno == ENOENT) {
327       /* The last used directory is deleted, try to use the parent dir. */
328       char *c = strrchr (d, '/');
329
330       if (c && (c > d)) {
331         *c = 0;
332         continue;
333       }
334     }
335     mutt_perror (d);
336     return (-1);
337   }
338
339   if (!S_ISDIR (s.st_mode)) {
340     mutt_error (_("%s is not a directory."), d);
341     return (-1);
342   }
343
344   buffy_check (0);
345
346   if ((dp = opendir (d)) == NULL) {
347     mutt_perror (d);
348     return (-1);
349   }
350
351   init_state (state, menu);
352
353   while ((de = readdir (dp)) != NULL) {
354     if (m_strcmp(de->d_name, ".") == 0)
355       continue;               /* we don't need . */
356
357     if (m_strncmp(prefix, de->d_name, m_strlen(prefix)) != 0)
358       continue;
359     if (!((regexec (Mask.rx, de->d_name, 0, NULL, 0) == 0) ^ Mask.neg))
360       continue;
361
362     mutt_concat_path(buffer, sizeof(buffer), d, de->d_name);
363     if (lstat (buffer, &s) == -1)
364       continue;
365
366     if ((!S_ISREG (s.st_mode)) && (!S_ISDIR (s.st_mode)) &&
367         (!S_ISLNK (s.st_mode)))
368       continue;
369
370     i = buffy_lookup (buffer);
371     add_folder (menu, state, de->d_name, &s, i >= 0 ? Incoming.arr[i]->new : 0);
372   }
373   closedir (dp);
374   sidebar_draw ();
375   browser_sort (state);
376   return 0;
377 }
378
379 /* get list of mailboxes/subscribed newsgroups */
380 static int examine_mailboxes (MUTTMENU * menu, struct browser_state *state)
381 {
382   struct stat s;
383   char buffer[LONG_STRING];
384   int i = 0;
385   BUFFY* tmp;
386
387   if (!Incoming.len)
388     return (-1);
389   buffy_check (0);
390
391   init_state (state, menu);
392
393   for (i = 0; i < Incoming.len; i++) {
394     tmp = Incoming.arr[i];
395     tmp->magic = mx_get_magic (tmp->path);
396     if (tmp->magic == M_IMAP) {
397       add_folder (menu, state, tmp->path, NULL, tmp->new);
398       continue;
399     }
400     if (tmp->magic == M_POP) {
401       add_folder (menu, state, tmp->path, NULL, tmp->new);
402       continue;
403     }
404     if (lstat (tmp->path, &s) == -1)
405       continue;
406
407     if ((!S_ISREG (s.st_mode)) && (!S_ISDIR (s.st_mode)) &&
408         (!S_ISLNK (s.st_mode)))
409       continue;
410
411     m_strcpy(buffer, sizeof(buffer), NONULL(tmp->path));
412     mutt_pretty_mailbox (buffer);
413     add_folder (menu, state, buffer, &s, tmp->new);
414   }
415
416   browser_sort (state);
417   return 0;
418 }
419
420 static int select_file_search (MUTTMENU * menu, regex_t * re, int n)
421 {
422   return (regexec
423           (re, ((struct folder_file *) menu->data)[n].name, 0, NULL, 0));
424 }
425
426 static void folder_entry (char *s, ssize_t slen, MUTTMENU * menu, int num)
427 {
428   FOLDER folder;
429
430   folder.ff = &((struct folder_file *) menu->data)[num];
431   folder.num = num;
432
433   m_strformat(s, slen, getmaxx(main_w), FolderFormat, folder_format_str,
434               &folder, 0);
435 }
436
437 static void init_menu (struct browser_state *state, MUTTMENU * menu,
438                        char *title, ssize_t titlelen, int buffy)
439 {
440   char path[_POSIX_PATH_MAX];
441
442   menu->max = state->entrylen;
443
444   if (menu->current >= menu->max)
445     menu->current = menu->max - 1;
446   if (menu->current < 0)
447     menu->current = 0;
448   if (menu->top > menu->current)
449     menu->top = 0;
450
451   menu->tagged = 0;
452
453   if (buffy)
454     snprintf(title, titlelen, _("Mailboxes [%d]"), buffy_check(0));
455   else {
456     m_strcpy(path, sizeof(path), LastDir);
457     mutt_pretty_mailbox (path);
458     if (state->imap_browse && option (OPTIMAPLSUB))
459       snprintf (title, titlelen, _("Subscribed [%s], File mask: %s"),
460                 path, NONULL (Mask.pattern));
461     else
462       snprintf (title, titlelen, _("Directory [%s], File mask: %s"),
463                 path, NONULL (Mask.pattern));
464   }
465   menu->redraw = REDRAW_FULL;
466 }
467
468 static int file_tag (MUTTMENU * menu, int n, int m)
469 {
470   struct folder_file *ff = &(((struct folder_file *) menu->data)[n]);
471   int ot;
472
473   if (S_ISDIR (ff->mode)
474       || (S_ISLNK (ff->mode) && link_is_dir (LastDir, ff->name))) {
475     mutt_error _("Can't attach a directory!");
476
477     return 0;
478   }
479
480   ot = ff->tagged;
481   ff->tagged = (m >= 0 ? m : !ff->tagged);
482
483   return ff->tagged - ot;
484 }
485
486 void mutt_select_file (char *f, ssize_t flen, int flags, char ***files,
487                        int *numfiles)
488 {
489   char buf[_POSIX_PATH_MAX];
490   char prefix[_POSIX_PATH_MAX] = "";
491   char title[STRING];
492   struct browser_state state;
493   MUTTMENU *menu;
494   struct stat st;
495   int i, killPrefix = 0;
496   int multiple = (flags & M_SEL_MULTI) ? 1 : 0;
497   int folder = (flags & M_SEL_FOLDER) ? 1 : 0;
498   int buffy = (flags & M_SEL_BUFFY) ? 1 : 0;
499
500   buffy = buffy && folder;
501
502   p_clear(&state, 1);
503
504   if (!folder)
505     m_strcpy(LastDirBackup, sizeof(LastDirBackup), LastDir);
506
507   if (*f) {
508     mutt_expand_path (f, flen);
509     if (imap_is_magic (f, NULL) == M_IMAP) {
510       init_state (&state, NULL);
511       state.imap_browse = 1;
512       if (!imap_browse (f, &state))
513         m_strcpy(LastDir, sizeof(LastDir), state.folder);
514     }
515     else {
516       for (i = m_strlen(f) - 1; i > 0 && f[i] != '/'; i--);
517       if (i > 0) {
518         if (f[0] == '/') {
519           i = MIN(ssizeof(LastDir) - 1, i);
520           m_strcpy(LastDir, sizeof(LastDir), f);
521         }
522         else {
523           getcwd(LastDir, sizeof(LastDir));
524           m_strcat(LastDir, sizeof(LastDir), "/");
525           m_strncat(LastDir, sizeof(LastDir), f, i);
526         }
527       }
528       else {
529         if (f[0] == '/')
530           m_strcpy(LastDir, sizeof(LastDir), "/");
531         else
532           getcwd (LastDir, sizeof (LastDir));
533       }
534
535       if (i <= 0 && f[0] != '/')
536         m_strcpy(prefix, sizeof(prefix), f);
537       else
538         m_strcpy(prefix, sizeof(prefix), f + i + 1);
539       killPrefix = 1;
540     }
541   }
542   else {
543     if (!folder)
544       getcwd (LastDir, sizeof (LastDir));
545     else if (!LastDir[0])
546       m_strcpy(LastDir, sizeof(LastDir), NONULL(Maildir));
547
548     if (!buffy && imap_is_magic (LastDir, NULL) == M_IMAP) {
549       init_state (&state, NULL);
550       state.imap_browse = 1;
551       imap_browse (LastDir, &state);
552       browser_sort (&state);
553     }
554   }
555
556   *f = 0;
557
558   if (buffy) {
559     if (examine_mailboxes (NULL, &state) == -1)
560       goto bail;
561   }
562   else
563   if (!state.imap_browse)
564     if (examine_directory (NULL, &state, LastDir, prefix) == -1)
565       goto bail;
566
567   menu = mutt_new_menu ();
568   menu->menu = MENU_FOLDER;
569   menu->make_entry = folder_entry;
570   menu->search = select_file_search;
571   menu->title = title;
572   menu->data = state.entry;
573   if (multiple)
574     menu->tag = file_tag;
575   init_menu (&state, menu, title, sizeof (title), buffy);
576
577   for (;;) {
578     switch (i = mutt_menuLoop (menu)) {
579     case OP_GENERIC_SELECT_ENTRY:
580
581       if (!state.entrylen) {
582         mutt_error _("No files match the file mask");
583
584         break;
585       }
586
587       if (S_ISDIR (state.entry[menu->current].mode) ||
588           (S_ISLNK (state.entry[menu->current].mode) &&
589            link_is_dir (LastDir, state.entry[menu->current].name))
590           || state.entry[menu->current].inferiors
591         ) {
592         /* make sure this isn't a MH or maildir mailbox */
593         if (buffy) {
594           m_strcpy(buf, sizeof(buf), state.entry[menu->current].name);
595           mutt_expand_path (buf, sizeof (buf));
596         }
597         else if (state.imap_browse) {
598           m_strcpy(buf, sizeof(buf), state.entry[menu->current].name);
599         }
600         else
601           mutt_concat_path(buf, sizeof(buf), LastDir,
602                            state.entry[menu->current].name);
603
604         if ((mx_get_magic (buf) <= 0)
605             || state.entry[menu->current].inferiors)
606         {
607           char OldLastDir[_POSIX_PATH_MAX];
608
609           /* save the old directory */
610           m_strcpy(OldLastDir, sizeof(OldLastDir), LastDir);
611
612           if (m_strcmp(state.entry[menu->current].name, "..") == 0) {
613             if (m_strcmp("..", LastDir + m_strlen(LastDir) - 2) == 0)
614               m_strcat(LastDir, sizeof(LastDir), "/..");
615             else {
616               char *p = strrchr (LastDir + 1, '/');
617
618               if (p)
619                 *p = 0;
620               else {
621                 if (LastDir[0] == '/')
622                   LastDir[1] = 0;
623                 else
624                   m_strcat(LastDir, sizeof(LastDir), "/..");
625               }
626             }
627           }
628           else if (buffy) {
629             m_strcpy(LastDir, sizeof(LastDir),
630                      state.entry[menu->current].name);
631             mutt_expand_path (LastDir, sizeof (LastDir));
632           }
633           else if (state.imap_browse) {
634             int n;
635             ciss_url_t url;
636
637             m_strcpy(LastDir, sizeof(LastDir),
638                      state.entry[menu->current].name);
639             /* tack on delimiter here */
640             n = m_strlen(LastDir) + 1;
641
642             /* special case "" needs no delimiter */
643             url_parse_ciss (&url, state.entry[menu->current].name);
644             if (url.path &&
645                 (state.entry[menu->current].delim != '\0') &&
646                 (n < ssizeof (LastDir))) {
647               LastDir[n] = '\0';
648               LastDir[n - 1] = state.entry[menu->current].delim;
649             }
650           }
651           else {
652             char tmp[_POSIX_PATH_MAX];
653
654             mutt_concat_path(tmp, sizeof(tmp), LastDir,
655                              state.entry[menu->current].name);
656             m_strcpy(LastDir, sizeof(LastDir), tmp);
657           }
658
659           destroy_state (&state);
660           if (killPrefix) {
661             prefix[0] = 0;
662             killPrefix = 0;
663           }
664           buffy = 0;
665           if (state.imap_browse) {
666             init_state (&state, NULL);
667             state.imap_browse = 1;
668             imap_browse (LastDir, &state);
669             browser_sort (&state);
670             menu->data = state.entry;
671           }
672           else
673           if (examine_directory (menu, &state, LastDir, prefix) == -1) {
674             /* try to restore the old values */
675             m_strcpy(LastDir, sizeof(LastDir), OldLastDir);
676             if (examine_directory (menu, &state, LastDir, prefix) == -1) {
677               m_strcpy(LastDir, sizeof(LastDir), NONULL(mod_core.homedir));
678               goto bail;
679             }
680           }
681           menu->current = 0;
682           menu->top = 0;
683           init_menu (&state, menu, title, sizeof (title), buffy);
684           break;
685         }
686       }
687
688       if (buffy) {
689         m_strcpy(f, flen, state.entry[menu->current].name);
690         mutt_expand_path (f, flen);
691       }
692       else if (state.imap_browse)
693         m_strcpy(f, flen, state.entry[menu->current].name);
694       else
695         mutt_concat_path(f, flen, LastDir, state.entry[menu->current].name);
696
697       /* Fall through to OP_EXIT */
698
699     case OP_EXIT:
700
701       if (multiple) {
702         char **tfiles;
703         int j;
704         int h;
705
706         if (menu->tagged) {
707           *numfiles = menu->tagged;
708           tfiles = p_new(char *, *numfiles);
709           for (h = 0, j = 0; h < state.entrylen; i++) {
710             struct folder_file ff = state.entry[i];
711             char full[_POSIX_PATH_MAX];
712
713             if (ff.tagged) {
714               mutt_concat_path(full, sizeof(full), LastDir, ff.name);
715               mutt_expand_path (full, sizeof (full));
716               tfiles[j++] = m_strdup(full);
717             }
718           }
719           *files = tfiles;
720         }
721         else if (f[0]) {        /* no tagged entries. return selected entry */
722           *numfiles = 1;
723           tfiles = p_new(char *, *numfiles);
724           mutt_expand_path (f, flen);
725           tfiles[0] = m_strdup(f);
726           *files = tfiles;
727         }
728       }
729
730       destroy_state (&state);
731       mutt_menuDestroy (&menu);
732       goto bail;
733
734     case OP_BROWSER_TELL:
735       if (state.entrylen)
736         mutt_message ("%s", state.entry[menu->current].name);
737       break;
738
739     case OP_BROWSER_TOGGLE_LSUB:
740       if (option (OPTIMAPLSUB)) {
741         unset_option (OPTIMAPLSUB);
742       }
743       else {
744         set_option (OPTIMAPLSUB);
745       }
746       mutt_ungetch (0, OP_CHECK_NEW);
747       break;
748
749     case OP_CREATE_MAILBOX:
750       if (!state.imap_browse)
751         mutt_error (_("Create is only supported for IMAP mailboxes"));
752       else {
753         imap_mailbox_create (LastDir);
754         /* TODO: find a way to detect if the new folder would appear in
755          *   this window, and insert it without starting over. */
756         destroy_state (&state);
757         init_state (&state, NULL);
758         state.imap_browse = 1;
759         imap_browse (LastDir, &state);
760         browser_sort (&state);
761         menu->data = state.entry;
762         menu->current = 0;
763         menu->top = 0;
764         init_menu (&state, menu, title, sizeof (title), buffy);
765         MAYBE_REDRAW (menu->redraw);
766       }
767       break;
768
769     case OP_RENAME_MAILBOX:
770       if (!state.entry[menu->current].imap)
771         mutt_error (_("Rename is only supported for IMAP mailboxes"));
772       else {
773         int nentry = menu->current;
774
775         if (imap_mailbox_rename (state.entry[nentry].name) >= 0) {
776           destroy_state (&state);
777           init_state (&state, NULL);
778           state.imap_browse = 1;
779           imap_browse (LastDir, &state);
780           browser_sort (&state);
781           menu->data = state.entry;
782           menu->current = 0;
783           menu->top = 0;
784           init_menu (&state, menu, title, sizeof (title), buffy);
785           MAYBE_REDRAW (menu->redraw);
786         }
787       }
788       break;
789
790     case OP_DELETE_MAILBOX:
791       if (!state.entry[menu->current].imap)
792         mutt_error (_("Delete is only supported for IMAP mailboxes"));
793       else {
794         char msg[STRING];
795         IMAP_MBOX mx;
796         int nentry = menu->current;
797
798         imap_parse_path (state.entry[nentry].name, &mx);
799         snprintf (msg, sizeof (msg), _("Really delete mailbox \"%s\"?"),
800                   mx.mbox);
801         if (mutt_yesorno (msg, M_NO) == M_YES) {
802           if (!imap_delete_mailbox (Context, mx)) {
803             /* free the mailbox from the browser */
804             p_delete(&((state.entry)[nentry].name));
805             p_delete(&((state.entry)[nentry].desc));
806             /* and move all other entries up */
807             if (nentry + 1 < state.entrylen)
808               memmove (state.entry + nentry, state.entry + nentry + 1,
809                        sizeof (struct folder_file) * (state.entrylen -
810                                                       (nentry + 1)));
811             state.entrylen--;
812             mutt_message _("Mailbox deleted.");
813
814             init_menu (&state, menu, title, sizeof (title), buffy);
815             MAYBE_REDRAW (menu->redraw);
816           }
817         }
818         else
819           mutt_message _("Mailbox not deleted.");
820         p_delete(&mx.mbox);
821       }
822       break;
823
824     case OP_CHANGE_DIRECTORY:
825       m_strcpy(buf, sizeof(buf), LastDir);
826       if (!state.imap_browse)
827       {
828         /* add '/' at the end of the directory name if not already there */
829         ssize_t len = m_strlen(LastDir);
830
831         if (len && LastDir[len - 1] != '/' && ssizeof(buf) > len)
832           buf[len] = '/';
833       }
834
835       if (mutt_get_field (_("Chdir to: "), buf, sizeof (buf), M_FILE) == 0 &&
836           buf[0]) {
837         buffy = 0;
838         mutt_expand_path (buf, sizeof (buf));
839         if (imap_is_magic (buf, NULL) == M_IMAP) {
840           m_strcpy(LastDir, sizeof(LastDir), buf);
841           destroy_state (&state);
842           init_state (&state, NULL);
843           state.imap_browse = 1;
844           imap_browse (LastDir, &state);
845           browser_sort (&state);
846           menu->data = state.entry;
847           menu->current = 0;
848           menu->top = 0;
849           init_menu (&state, menu, title, sizeof (title), buffy);
850         }
851         else
852         if (stat (buf, &st) == 0) {
853           if (S_ISDIR (st.st_mode)) {
854             destroy_state (&state);
855             if (examine_directory (menu, &state, buf, prefix) == 0)
856               m_strcpy(LastDir, sizeof(LastDir), buf);
857             else {
858               mutt_error _("Error scanning directory.");
859
860               if (examine_directory (menu, &state, LastDir, prefix) == -1) {
861                 mutt_menuDestroy (&menu);
862                 goto bail;
863               }
864             }
865             menu->current = 0;
866             menu->top = 0;
867             init_menu (&state, menu, title, sizeof (title), buffy);
868           }
869           else
870             mutt_error (_("%s is not a directory."), buf);
871         }
872         else
873           mutt_perror (buf);
874       }
875       MAYBE_REDRAW (menu->redraw);
876       break;
877
878     case OP_ENTER_MASK:
879
880       m_strcpy(buf, sizeof(buf), NONULL(Mask.pattern));
881       if (mutt_get_field (_("File Mask: "), buf, sizeof (buf), 0) == 0) {
882         regex_t *rx = p_new(regex_t, 1);
883         char *s = buf;
884         int neg = 0, err;
885
886         buffy = 0;
887         /* assume that the user wants to see everything */
888         if (!buf[0])
889           m_strcpy(buf, sizeof(buf), ".");
890         s = vskipspaces(s);
891         if (*s == '!') {
892           s = vskipspaces(s + 1);
893           neg = 1;
894         }
895
896         if ((err = REGCOMP (rx, s, REG_NOSUB)) != 0) {
897           regerror (err, rx, buf, sizeof (buf));
898           regfree (rx);
899           p_delete(&rx);
900           mutt_error ("%s", buf);
901         }
902         else {
903           m_strreplace(&Mask.pattern, buf);
904           regfree (Mask.rx);
905           p_delete(&Mask.rx);
906           Mask.rx = rx;
907           Mask.neg = neg;
908
909           destroy_state (&state);
910           if (state.imap_browse) {
911             init_state (&state, NULL);
912             state.imap_browse = 1;
913             imap_browse (LastDir, &state);
914             browser_sort (&state);
915             menu->data = state.entry;
916             init_menu (&state, menu, title, sizeof (title), buffy);
917           }
918           else
919           if (examine_directory (menu, &state, LastDir, NULL) == 0)
920             init_menu (&state, menu, title, sizeof (title), buffy);
921           else {
922             mutt_error _("Error scanning directory.");
923
924             mutt_menuDestroy (&menu);
925             goto bail;
926           }
927           killPrefix = 0;
928           if (!state.entrylen) {
929             mutt_error _("No files match the file mask");
930
931             break;
932           }
933         }
934       }
935       MAYBE_REDRAW (menu->redraw);
936       break;
937
938     case OP_SORT:
939     case OP_SORT_REVERSE:
940
941       {
942         int resort = 1;
943         int reverse = (i == OP_SORT_REVERSE);
944
945         switch (mutt_multi_choice ((reverse) ?
946                                    _
947                                    ("Reverse sort by (d)ate, (a)lpha, si(z)e or do(n)'t sort? ")
948                                    :
949                                    _
950                                    ("Sort by (d)ate, (a)lpha, si(z)e or do(n)'t sort? "),
951                                    _("dazn"))) {
952         case -1:               /* abort */
953           resort = 0;
954           break;
955
956         case 1:                /* (d)ate */
957           BrowserSort = SORT_DATE;
958           break;
959
960         case 2:                /* (a)lpha */
961           BrowserSort = SORT_SUBJECT;
962           break;
963
964         case 3:                /* si(z)e */
965           BrowserSort = SORT_SIZE;
966           break;
967
968         case 4:                /* do(n)'t sort */
969           BrowserSort = SORT_ORDER;
970           resort = 0;
971           break;
972         }
973         if (resort) {
974           BrowserSort |= reverse ? SORT_REVERSE : 0;
975           browser_sort (&state);
976           menu->redraw = REDRAW_FULL;
977         }
978         break;
979       }
980
981     case OP_TOGGLE_MAILBOXES:
982       buffy = 1 - buffy;
983
984     case OP_CHECK_NEW:
985       destroy_state (&state);
986       prefix[0] = 0;
987       killPrefix = 0;
988
989       if (buffy) {
990         if (examine_mailboxes (menu, &state) == -1)
991           goto bail;
992       }
993       else if (imap_is_magic (LastDir, NULL) == M_IMAP) {
994         init_state (&state, NULL);
995         state.imap_browse = 1;
996         imap_browse (LastDir, &state);
997         browser_sort (&state);
998         menu->data = state.entry;
999       }
1000       else if (examine_directory (menu, &state, LastDir, prefix) == -1)
1001         goto bail;
1002       init_menu (&state, menu, title, sizeof (title), buffy);
1003       break;
1004
1005     case OP_BUFFY_LIST:
1006       if (option (OPTFORCEBUFFYCHECK))
1007         buffy_check (1);
1008       buffy_list ();
1009       break;
1010
1011     case OP_BROWSER_NEW_FILE:
1012
1013       snprintf (buf, sizeof (buf), "%s/", LastDir);
1014       if (mutt_get_field (_("New file name: "), buf, sizeof (buf), M_FILE) ==
1015           0) {
1016         m_strcpy(f, flen, buf);
1017         destroy_state (&state);
1018         mutt_menuDestroy (&menu);
1019         goto bail;
1020       }
1021       MAYBE_REDRAW (menu->redraw);
1022       break;
1023
1024     case OP_BROWSER_VIEW_FILE:
1025       if (!state.entrylen) {
1026         mutt_error _("No files match the file mask");
1027
1028         break;
1029       }
1030
1031       if (state.entry[menu->current].selectable) {
1032         m_strcpy(f, flen, state.entry[menu->current].name);
1033         destroy_state (&state);
1034         mutt_menuDestroy (&menu);
1035         goto bail;
1036       }
1037       else
1038       if (S_ISDIR (state.entry[menu->current].mode) ||
1039             (S_ISLNK (state.entry[menu->current].mode) &&
1040                link_is_dir (LastDir, state.entry[menu->current].name))) {
1041         mutt_error _("Can't view a directory");
1042
1043         break;
1044       }
1045       else {
1046         BODY *b;
1047         char nbuf[_POSIX_PATH_MAX];
1048
1049         mutt_concat_path(nbuf, sizeof(nbuf), LastDir,
1050                          state.entry[menu->current].name);
1051         b = mutt_make_file_attach (nbuf);
1052         if (b != NULL) {
1053           mutt_view_attachment (NULL, b, M_REGULAR, NULL, NULL, 0);
1054           body_list_wipe(&b);
1055           menu->redraw = REDRAW_FULL;
1056         }
1057         else
1058           mutt_error _("Error trying to view file");
1059       }
1060       break;
1061
1062     case OP_BROWSER_SUBSCRIBE:
1063     case OP_BROWSER_UNSUBSCRIBE:
1064       if (i == OP_BROWSER_SUBSCRIBE)
1065         imap_subscribe (state.entry[menu->current].name, 1);
1066       else
1067         imap_subscribe (state.entry[menu->current].name, 0);
1068     }
1069   }
1070
1071 bail:
1072
1073   if (!folder)
1074     m_strcpy(LastDir, sizeof(LastDir), LastDirBackup);
1075
1076 }