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