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