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