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   browser_sort (state);
573   return 0;
574 }
575
576 /* get list of mailboxes/subscribed newsgroups */
577 static int examine_mailboxes (MUTTMENU *menu, struct browser_state *state)
578 {
579   struct stat s;
580   char buffer[LONG_STRING];
581
582 #ifdef USE_NNTP
583   if (option (OPTNEWS))
584   {
585     LIST *tmp;
586     NNTP_DATA *data;
587     NNTP_SERVER *news = CurrentNewsSrv;
588
589 /*  mutt_buffy_check (0); */
590     init_state (state, menu);
591
592     for (tmp = news->list; tmp; tmp = tmp->next)
593     {
594       if ((data = (NNTP_DATA *) tmp->data) != NULL && (data->new ||
595           (data->subscribed && (!option (OPTSHOWONLYUNREAD) || data->unread))))
596       add_folder (menu, state, data->group, NULL, data, data->new);
597     }
598   }
599   else
600 #endif
601   {
602     BUFFY *tmp = Incoming;
603
604     if (!Incoming)
605       return (-1);
606     mutt_buffy_check (0);
607
608     init_state (state, menu);
609
610     do
611     {
612 #ifdef USE_IMAP
613       if (mx_is_imap (tmp->path))
614       {
615         add_folder (menu, state, tmp->path, NULL, NULL, tmp->new);
616         continue;
617       }
618 #endif
619 #ifdef USE_POP
620       if (mx_is_pop (tmp->path))
621       {
622         add_folder (menu, state, tmp->path, NULL, NULL, tmp->new);
623         continue;
624       }
625 #endif
626 #ifdef USE_NNTP
627       if (mx_is_nntp (tmp->path))
628       {
629         add_folder (menu, state, tmp->path, NULL, NULL, tmp->new);
630         continue;
631       }
632 #endif
633       if (lstat (tmp->path, &s) == -1)
634         continue;
635
636       if ((! S_ISREG (s.st_mode)) && (! S_ISDIR (s.st_mode)) &&
637           (! S_ISLNK (s.st_mode)))
638         continue;
639     
640       strfcpy (buffer, NONULL(tmp->path), sizeof (buffer));
641       mutt_pretty_mailbox (buffer);
642
643       add_folder (menu, state, buffer, &s, NULL, tmp->new);
644     }
645     while ((tmp = tmp->next));
646   }
647   browser_sort (state);
648   return 0;
649 }
650
651 static int select_file_search (MUTTMENU *menu, regex_t *re, int n)
652 {
653 #ifdef USE_NNTP
654   if (option (OPTNEWS))
655     return (regexec (re, ((struct folder_file *) menu->data)[n].desc, 0, NULL, 0));
656 #endif
657   return (regexec (re, ((struct folder_file *) menu->data)[n].name, 0, NULL, 0));
658 }
659
660 static void folder_entry (char *s, size_t slen, MUTTMENU *menu, int num)
661 {
662   FOLDER folder;
663
664   folder.ff = &((struct folder_file *) menu->data)[num];
665   folder.num = num;
666   
667 #ifdef USE_NNTP
668   if (option (OPTNEWS))
669     mutt_FormatString (s, slen, NONULL(GroupFormat), newsgroup_format_str, 
670       (unsigned long) &folder, M_FORMAT_ARROWCURSOR);
671   else
672 #endif
673     mutt_FormatString (s, slen, NONULL(FolderFormat), folder_format_str, 
674       (unsigned long) &folder, M_FORMAT_ARROWCURSOR);
675 }
676
677 static void init_menu (struct browser_state *state, MUTTMENU *menu, char *title,
678                        size_t titlelen, int buffy)
679 {
680   char path[_POSIX_PATH_MAX];
681
682   menu->max = state->entrylen;
683
684   if(menu->current >= menu->max)
685     menu->current = menu->max - 1;
686   if (menu->current < 0)
687     menu->current = 0;
688   if (menu->top > menu->current)
689     menu->top = 0;
690
691   menu->tagged = 0;
692
693 #ifdef USE_NNTP
694   if (option (OPTNEWS))
695   {
696     if (buffy)
697       snprintf (title, titlelen, "%s", _("Subscribed newsgroups"));
698     else
699       snprintf (title, titlelen, _("Newsgroups on server [%s]"),
700                 CurrentNewsSrv->conn->account.host);
701   }
702   else
703 #endif
704   if (buffy)
705     snprintf (title, titlelen, _("Mailboxes [%d]"), mutt_buffy_check (0));
706   else
707   {
708     strfcpy (path, LastDir, sizeof (path));
709     mutt_pretty_mailbox (path);
710 #ifdef USE_IMAP
711   if (state->imap_browse && option (OPTIMAPLSUB))
712     snprintf (title, titlelen, _("Subscribed [%s], File mask: %s"),
713               path, NONULL (Mask.pattern));
714   else
715 #endif
716     snprintf (title, titlelen, _("Directory [%s], File mask: %s"),
717               path, NONULL(Mask.pattern));
718   }
719   menu->redraw = REDRAW_FULL;
720 }
721
722 static int file_tag (MUTTMENU *menu, int n, int m)
723 {
724   struct folder_file *ff = &(((struct folder_file *)menu->data)[n]);
725   int ot;
726   if (S_ISDIR (ff->mode) || (S_ISLNK (ff->mode) && link_is_dir (LastDir, ff->name)))
727   {
728     mutt_error _("Can't attach a directory!");
729     return 0;
730   }
731   
732   ot = ff->tagged;
733   ff->tagged = (m >= 0 ? m : !ff->tagged);
734   
735   return ff->tagged - ot;
736 }
737
738 void _mutt_select_file (char *f, size_t flen, int flags, char ***files, int *numfiles)
739 {
740   char buf[_POSIX_PATH_MAX];
741   char prefix[_POSIX_PATH_MAX] = "";
742   char helpstr[SHORT_STRING];
743   char title[STRING];
744   struct browser_state state;
745   MUTTMENU *menu;
746   struct stat st;
747   int i, killPrefix = 0;
748   int multiple = (flags & M_SEL_MULTI)  ? 1 : 0;
749   int folder   = (flags & M_SEL_FOLDER) ? 1 : 0;
750   int buffy    = (flags & M_SEL_BUFFY)  ? 1 : 0;
751
752   buffy = buffy && folder;
753   
754   memset (&state, 0, sizeof (struct browser_state));
755
756   if (!folder)
757     strfcpy (LastDirBackup, LastDir, sizeof (LastDirBackup));
758
759 #ifdef USE_NNTP
760   if (option (OPTNEWS))
761   {
762     if (*f)
763       strfcpy (prefix, f, sizeof (prefix));
764     else
765     {
766       LIST *list;
767
768       /* default state for news reader mode is browse subscribed newsgroups */
769       buffy = 0;
770       for (list = CurrentNewsSrv->list; list; list = list->next)
771       {
772         NNTP_DATA *data = (NNTP_DATA *) list->data;
773
774         if (data && data->subscribed)
775         {
776           buffy = 1;
777           break;
778         }
779       }
780     }
781   }
782   else
783 #endif
784   if (*f)
785   {
786     mutt_expand_path (f, flen);
787 #ifdef USE_IMAP
788     if (mx_is_imap (f))
789     {
790       init_state (&state, NULL);
791       state.imap_browse = 1;
792       imap_browse (f, &state);
793       strfcpy (LastDir, state.folder, sizeof (LastDir));
794     }
795     else
796     {
797 #endif
798     for (i = mutt_strlen (f) - 1; i > 0 && f[i] != '/' ; i--);
799     if (i > 0)
800     {
801       if (f[0] == '/')
802       {
803         if (i > sizeof (LastDir) - 1) i = sizeof (LastDir) - 1;
804         strncpy (LastDir, f, i);
805         LastDir[i] = 0;
806       }
807       else
808       {
809         getcwd (LastDir, sizeof (LastDir));
810         safe_strcat (LastDir, sizeof (LastDir), "/");
811         safe_strncat (LastDir, sizeof (LastDir), f, i);
812       }
813     }
814     else
815     {
816       if (f[0] == '/')
817         strcpy (LastDir, "/");          /* __STRCPY_CHECKED__ */
818       else
819         getcwd (LastDir, sizeof (LastDir));
820     }
821
822     if (i <= 0 && f[0] != '/')
823       strfcpy (prefix, f, sizeof (prefix));
824     else
825       strfcpy (prefix, f + i + 1, sizeof (prefix));
826     killPrefix = 1;
827 #ifdef USE_IMAP
828     }
829 #endif
830   }
831   else 
832   {
833     if (!folder)
834       getcwd (LastDir, sizeof (LastDir));
835     else if (!LastDir[0])
836       strfcpy (LastDir, NONULL(Maildir), sizeof (LastDir));
837     
838 #ifdef USE_IMAP
839     if (!buffy && mx_is_imap (LastDir))
840     {
841       init_state (&state, NULL);
842       state.imap_browse = 1;
843       imap_browse (LastDir, &state);
844     }
845 #endif
846   }
847
848   *f = 0;
849
850   if (buffy)
851   {
852     if (examine_mailboxes (NULL, &state) == -1)
853       goto bail;
854   }
855   else
856 #ifdef USE_IMAP
857   if (!state.imap_browse)
858 #endif
859   if (examine_directory (NULL, &state, LastDir, prefix) == -1)
860     goto bail;
861
862   menu = mutt_new_menu ();
863   menu->menu = MENU_FOLDER;
864   menu->make_entry = folder_entry;
865   menu->search = select_file_search;
866   menu->title = title;
867   menu->data = state.entry;
868   if (multiple)
869     menu->tag = file_tag;
870
871   menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_FOLDER,
872 #ifdef USE_NNTP
873     (option (OPTNEWS)) ? FolderNewsHelp :
874 #endif
875     FolderHelp);
876
877   init_menu (&state, menu, title, sizeof (title), buffy);
878
879   FOREVER
880   {
881     switch (i = mutt_menuLoop (menu))
882     {
883       case OP_GENERIC_SELECT_ENTRY:
884
885         if (!state.entrylen)
886         {
887           mutt_error _("No files match the file mask");
888           break;
889         }
890
891         if (S_ISDIR (state.entry[menu->current].mode) ||
892             (S_ISLNK (state.entry[menu->current].mode) &&
893             link_is_dir (LastDir, state.entry[menu->current].name)) 
894 #ifdef USE_IMAP
895             || state.entry[menu->current].inferiors
896 #endif
897             )
898         {
899           /* make sure this isn't a MH or maildir mailbox */
900           if (buffy)
901           {
902             strfcpy (buf, state.entry[menu->current].name, sizeof (buf));
903             mutt_expand_path (buf, sizeof (buf));
904           }
905 #ifdef USE_IMAP
906           else if (state.imap_browse)
907           {
908             strfcpy (buf, state.entry[menu->current].name, sizeof (buf));
909           }
910 #endif
911           else
912             mutt_concat_path (buf, LastDir, state.entry[menu->current].name, sizeof (buf));
913
914           if ((mx_get_magic (buf) <= 0)
915 #ifdef USE_IMAP
916             || state.entry[menu->current].inferiors
917 #endif
918             )
919           {
920             char OldLastDir[_POSIX_PATH_MAX];
921
922             /* save the old directory */
923             strfcpy (OldLastDir, LastDir, sizeof (OldLastDir));
924
925             if (mutt_strcmp (state.entry[menu->current].name, "..") == 0)
926             {
927               if (mutt_strcmp ("..", LastDir + mutt_strlen (LastDir) - 2) == 0)
928                 strcat (LastDir, "/..");        /* __STRCAT_CHECKED__ */
929               else
930               {
931                 char *p = strrchr (LastDir + 1, '/');
932
933                 if (p)
934                   *p = 0;
935                 else
936                 {
937                   if (LastDir[0] == '/')
938                     LastDir[1] = 0;
939                   else
940                     strcat (LastDir, "/..");    /* __STRCAT_CHECKED__ */
941                 }
942               }
943             }
944             else if (buffy)
945             {
946               strfcpy (LastDir, state.entry[menu->current].name, sizeof (LastDir));
947               mutt_expand_path (LastDir, sizeof (LastDir));
948             }
949 #ifdef USE_IMAP
950             else if (state.imap_browse)
951             {
952               int n;
953               ciss_url_t url;
954               
955               strfcpy (LastDir, state.entry[menu->current].name,
956                 sizeof (LastDir));
957               /* tack on delimiter here */
958               n = strlen (LastDir)+1;
959               
960               /* special case "" needs no delimiter */
961               url_parse_ciss (&url, state.entry[menu->current].name);
962               if (url.path &&
963                   (state.entry[menu->current].delim != '\0') &&
964                   (n < sizeof (LastDir)))
965               {
966                 LastDir[n] = '\0';
967                 LastDir[n-1] = state.entry[menu->current].delim;
968               }
969             }
970 #endif
971             else
972             {
973               char tmp[_POSIX_PATH_MAX];
974               mutt_concat_path (tmp, LastDir, state.entry[menu->current].name, sizeof (tmp));
975               strfcpy (LastDir, tmp, sizeof (LastDir));
976             }
977
978             destroy_state (&state);
979             if (killPrefix)
980             {
981               prefix[0] = 0;
982               killPrefix = 0;
983             }
984             buffy = 0;
985 #ifdef USE_IMAP
986             if (state.imap_browse)
987             {
988               init_state (&state, NULL);
989               state.imap_browse = 1;
990               imap_browse (LastDir, &state);
991               menu->data = state.entry;
992             }
993             else
994 #endif
995             if (examine_directory (menu, &state, LastDir, prefix) == -1)
996             {
997               /* try to restore the old values */
998               strfcpy (LastDir, OldLastDir, sizeof (LastDir));
999               if (examine_directory (menu, &state, LastDir, prefix) == -1)
1000               {
1001                 strfcpy (LastDir, NONULL(Homedir), sizeof (LastDir));
1002                 goto bail;
1003               }
1004             }
1005             menu->current = 0; 
1006             menu->top = 0; 
1007             init_menu (&state, menu, title, sizeof (title), buffy);
1008             break;
1009           }
1010         }
1011
1012 #ifdef USE_NNTP
1013         if (buffy || option (OPTNEWS)) /* news have not path */
1014 #else
1015         if (buffy)
1016 #endif
1017         {
1018           strfcpy (f, state.entry[menu->current].name, flen);
1019           mutt_expand_path (f, flen);
1020         }
1021 #ifdef USE_IMAP
1022         else if (state.imap_browse)
1023           strfcpy (f, state.entry[menu->current].name, flen);
1024 #endif
1025         else
1026           mutt_concat_path (f, LastDir, state.entry[menu->current].name, flen);
1027
1028         /* Fall through to OP_EXIT */
1029
1030       case OP_EXIT:
1031
1032         if (multiple)
1033         {
1034           char **tfiles;
1035           int i, j;
1036
1037           if (menu->tagged)
1038           {
1039             *numfiles = menu->tagged;
1040             tfiles = safe_calloc (*numfiles, sizeof (char *));
1041             for (i = 0, j = 0; i < state.entrylen; i++)
1042             {
1043               struct folder_file ff = state.entry[i];
1044               char full[_POSIX_PATH_MAX];
1045               if (ff.tagged)
1046               {
1047                 mutt_concat_path (full, LastDir, ff.name, sizeof (full));
1048                 mutt_expand_path (full, sizeof (full));
1049                 tfiles[j++] = safe_strdup (full);
1050               }
1051             }
1052             *files = tfiles;
1053           }
1054           else if (f[0]) /* no tagged entries. return selected entry */
1055           {
1056             *numfiles = 1;
1057             tfiles = safe_calloc (*numfiles, sizeof (char *));
1058             mutt_expand_path (f, flen);
1059             tfiles[0] = safe_strdup (f);
1060             *files = tfiles;
1061           }
1062         }
1063
1064         destroy_state (&state);
1065         mutt_menuDestroy (&menu);
1066         goto bail;
1067
1068       case OP_BROWSER_TELL:
1069         if(state.entrylen)
1070           mutt_message("%s", state.entry[menu->current].name);
1071         break;
1072
1073 #ifdef USE_IMAP
1074       case OP_BROWSER_TOGGLE_LSUB:
1075         if (option (OPTIMAPLSUB))
1076         {
1077           unset_option (OPTIMAPLSUB);
1078         }
1079         else
1080         {
1081           set_option (OPTIMAPLSUB);
1082         }
1083         mutt_ungetch (0, OP_CHECK_NEW);
1084         break;
1085
1086       case OP_CREATE_MAILBOX:
1087         if (!state.imap_browse)
1088           mutt_error (_("Create is only supported for IMAP mailboxes"));
1089         else
1090         {
1091           imap_mailbox_create (LastDir);
1092           /* TODO: find a way to detect if the new folder would appear in
1093            *   this window, and insert it without starting over. */
1094           destroy_state (&state);
1095           init_state (&state, NULL);
1096           state.imap_browse = 1;
1097           imap_browse (LastDir, &state);
1098           menu->data = state.entry;
1099           menu->current = 0; 
1100           menu->top = 0; 
1101           init_menu (&state, menu, title, sizeof (title), buffy);
1102           MAYBE_REDRAW (menu->redraw);
1103         }
1104         break;
1105
1106       case OP_RENAME_MAILBOX:
1107         if (!state.entry[menu->current].imap)
1108           mutt_error (_("Rename is only supported for IMAP mailboxes"));
1109         else
1110         {
1111           int nentry = menu->current;
1112
1113           if (imap_mailbox_rename (state.entry[nentry].name) >= 0) {
1114             destroy_state (&state);
1115             init_state (&state, NULL);
1116             state.imap_browse = 1;
1117             imap_browse (LastDir, &state);
1118             menu->data = state.entry;
1119             menu->current = 0;
1120             menu->top = 0;
1121             init_menu (&state, menu, title, sizeof (title), buffy);
1122             MAYBE_REDRAW (menu->redraw);
1123           }
1124         }
1125         break;
1126
1127     case OP_DELETE_MAILBOX:
1128         if (!state.entry[menu->current].imap)
1129           mutt_error (_("Delete is only supported for IMAP mailboxes"));
1130         else
1131         {
1132           char msg[SHORT_STRING];
1133           IMAP_MBOX mx;
1134           int nentry = menu->current;
1135           
1136           imap_parse_path (state.entry[nentry].name, &mx);
1137           snprintf (msg, sizeof (msg), _("Really delete mailbox \"%s\"?"),
1138             mx.mbox);
1139           if (mutt_yesorno (msg, M_NO) == M_YES)
1140           {
1141             if (!imap_delete_mailbox (Context, mx))
1142             {
1143               /* free the mailbox from the browser */
1144               FREE (&((state.entry)[nentry].name));
1145               FREE (&((state.entry)[nentry].desc));
1146               /* and move all other entries up */
1147               if (nentry+1 < state.entrylen)
1148                 memmove (state.entry + nentry, state.entry + nentry + 1,
1149                   sizeof (struct folder_file) * (state.entrylen - (nentry+1)));
1150               state.entrylen--;
1151               mutt_message _("Mailbox deleted.");
1152               init_menu (&state, menu, title, sizeof (title), buffy);
1153               MAYBE_REDRAW (menu->redraw);
1154             }
1155           }
1156           else
1157             mutt_message _("Mailbox not deleted.");
1158           FREE (&mx.mbox);
1159         }
1160         break;
1161 #endif
1162       
1163       case OP_CHANGE_DIRECTORY:
1164
1165 #ifdef USE_NNTP
1166         if (option (OPTNEWS))
1167           break;
1168 #endif
1169
1170         strfcpy (buf, LastDir, sizeof (buf));
1171 #ifdef USE_IMAP
1172         if (!state.imap_browse)
1173 #endif
1174         {
1175           /* add '/' at the end of the directory name if not already there */
1176           int len=mutt_strlen(LastDir);
1177           if (len && LastDir[len-1] != '/' && sizeof (buf) > len)
1178             buf[len]='/';
1179         }
1180
1181         if (mutt_get_field (_("Chdir to: "), buf, sizeof (buf), M_FILE) == 0 &&
1182             buf[0])
1183         {
1184           buffy = 0;      
1185           mutt_expand_path (buf, sizeof (buf));
1186 #ifdef USE_IMAP
1187           if (mx_is_imap (buf))
1188           {
1189             strfcpy (LastDir, buf, sizeof (LastDir));
1190             destroy_state (&state);
1191             init_state (&state, NULL);
1192             state.imap_browse = 1;
1193             imap_browse (LastDir, &state);
1194             menu->data = state.entry;
1195             menu->current = 0; 
1196             menu->top = 0; 
1197             init_menu (&state, menu, title, sizeof (title), buffy);
1198           }
1199           else
1200 #endif
1201           if (stat (buf, &st) == 0)
1202           {
1203             if (S_ISDIR (st.st_mode))
1204             {
1205               destroy_state (&state);
1206               if (examine_directory (menu, &state, buf, prefix) == 0)
1207                 strfcpy (LastDir, buf, sizeof (LastDir));
1208               else
1209               {
1210                 mutt_error _("Error scanning directory.");
1211                 if (examine_directory (menu, &state, LastDir, prefix) == -1)
1212                 {
1213                   mutt_menuDestroy (&menu);
1214                   goto bail;
1215                 }
1216               }
1217               menu->current = 0; 
1218               menu->top = 0; 
1219               init_menu (&state, menu, title, sizeof (title), buffy);
1220             }
1221             else
1222               mutt_error (_("%s is not a directory."), buf);
1223           }
1224           else
1225             mutt_perror (buf);
1226         }
1227         MAYBE_REDRAW (menu->redraw);
1228         break;
1229         
1230       case OP_ENTER_MASK:
1231
1232         strfcpy (buf, NONULL(Mask.pattern), sizeof (buf));
1233         if (mutt_get_field (_("File Mask: "), buf, sizeof (buf), 0) == 0)
1234         {
1235           regex_t *rx = (regex_t *) safe_malloc (sizeof (regex_t));
1236           char *s = buf;
1237           int not = 0, err;
1238
1239           buffy = 0;
1240           /* assume that the user wants to see everything */
1241           if (!buf[0])
1242             strfcpy (buf, ".", sizeof (buf));
1243           SKIPWS (s);
1244           if (*s == '!')
1245           {
1246             s++;
1247             SKIPWS (s);
1248             not = 1;
1249           }
1250
1251           if ((err = REGCOMP (rx, s, REG_NOSUB)) != 0)
1252           {
1253             regerror (err, rx, buf, sizeof (buf));
1254             regfree (rx);
1255             FREE (&rx);
1256             mutt_error ("%s", buf);
1257           }
1258           else
1259           {
1260             mutt_str_replace (&Mask.pattern, buf);
1261             regfree (Mask.rx);
1262             FREE (&Mask.rx);
1263             Mask.rx = rx;
1264             Mask.not = not;
1265
1266             destroy_state (&state);
1267 #ifdef USE_IMAP
1268             if (state.imap_browse)
1269             {
1270               init_state (&state, NULL);
1271               state.imap_browse = 1;
1272               imap_browse (LastDir, &state);
1273               menu->data = state.entry;
1274               init_menu (&state, menu, title, sizeof (title), buffy);
1275             }
1276             else
1277 #endif
1278             if (examine_directory (menu, &state, LastDir, NULL) == 0)
1279               init_menu (&state, menu, title, sizeof (title), buffy);
1280             else
1281             {
1282               mutt_error _("Error scanning directory.");
1283               mutt_menuDestroy (&menu);
1284               goto bail;
1285             }
1286             killPrefix = 0;
1287             if (!state.entrylen)
1288             {
1289               mutt_error _("No files match the file mask");
1290               break;
1291             }
1292           }
1293         }
1294         MAYBE_REDRAW (menu->redraw);
1295         break;
1296
1297       case OP_SORT:
1298       case OP_SORT_REVERSE:
1299
1300         {
1301           int resort = 1;
1302           int reverse = (i == OP_SORT_REVERSE);
1303           
1304           switch (mutt_multi_choice ((reverse) ?
1305               _("Reverse sort by (d)ate, (a)lpha, si(z)e or do(n)'t sort? ") :
1306               _("Sort by (d)ate, (a)lpha, si(z)e or do(n)'t sort? "),
1307               _("dazn")))
1308           {
1309             case -1: /* abort */
1310               resort = 0;
1311               break;
1312
1313             case 1: /* (d)ate */
1314               BrowserSort = SORT_DATE;
1315               break;
1316
1317             case 2: /* (a)lpha */
1318               BrowserSort = SORT_SUBJECT;
1319               break;
1320
1321             case 3: /* si(z)e */
1322               BrowserSort = SORT_SIZE;
1323               break;
1324
1325             case 4: /* do(n)'t sort */
1326               BrowserSort = SORT_ORDER;
1327               resort = 0;
1328               break;
1329           }
1330           if (resort)
1331           {
1332             BrowserSort |= reverse ? SORT_REVERSE : 0;
1333             browser_sort (&state);
1334             menu->redraw = REDRAW_FULL;
1335           }
1336           break;
1337         }
1338
1339       case OP_TOGGLE_MAILBOXES:
1340         buffy = 1 - buffy;
1341
1342       case OP_CHECK_NEW:
1343         destroy_state (&state);
1344         prefix[0] = 0;
1345         killPrefix = 0;
1346
1347         if (buffy)
1348         {
1349           if (examine_mailboxes (menu, &state) == -1)
1350             goto bail;
1351         }
1352 #ifdef USE_IMAP
1353         else if (mx_is_imap (LastDir))
1354         {
1355           init_state (&state, NULL);
1356           state.imap_browse = 1;
1357           imap_browse (LastDir, &state);
1358           menu->data = state.entry;
1359         }
1360 #endif
1361         else if (examine_directory (menu, &state, LastDir, prefix) == -1)
1362           goto bail;
1363         init_menu (&state, menu, title, sizeof (title), buffy);
1364         break;
1365
1366       case OP_BUFFY_LIST:
1367         mutt_buffy_list ();
1368         break;
1369
1370       case OP_BROWSER_NEW_FILE:
1371
1372         snprintf (buf, sizeof (buf), "%s/", LastDir);
1373         if (mutt_get_field (_("New file name: "), buf, sizeof (buf), M_FILE) == 0)
1374         {
1375           strfcpy (f, buf, flen);
1376           destroy_state (&state);
1377           mutt_menuDestroy (&menu);
1378           goto bail;
1379         }
1380         MAYBE_REDRAW (menu->redraw);
1381         break;
1382
1383       case OP_BROWSER_VIEW_FILE:
1384         if (!state.entrylen)
1385         {
1386           mutt_error _("No files match the file mask");
1387           break;
1388         }
1389
1390 #ifdef USE_IMAP
1391         if (state.entry[menu->current].selectable)
1392         {
1393           strfcpy (f, state.entry[menu->current].name, flen);
1394           destroy_state (&state);
1395           mutt_menuDestroy (&menu);
1396           goto bail;
1397         }
1398         else
1399 #endif
1400         if (S_ISDIR (state.entry[menu->current].mode) ||
1401             (S_ISLNK (state.entry[menu->current].mode) &&
1402             link_is_dir (LastDir, state.entry[menu->current].name)))
1403         {
1404           mutt_error _("Can't view a directory");
1405           break;
1406         } 
1407         else
1408         {
1409           BODY *b;
1410           char buf[_POSIX_PATH_MAX];
1411           
1412           mutt_concat_path (buf, LastDir, state.entry[menu->current].name, sizeof (buf));
1413           b = mutt_make_file_attach (buf);
1414           if (b != NULL)
1415           {
1416             mutt_view_attachment (NULL, b, M_REGULAR, NULL, NULL, 0);
1417             mutt_free_body (&b);
1418             menu->redraw = REDRAW_FULL;
1419           }
1420           else
1421             mutt_error _("Error trying to view file");
1422         }
1423         break;
1424
1425 #ifdef USE_NNTP
1426       case OP_CATCHUP:
1427       case OP_UNCATCHUP:
1428         if (option (OPTNEWS))
1429         {
1430           struct folder_file *f = &state.entry[menu->current];
1431           NNTP_DATA *nd;
1432
1433           if (i == OP_CATCHUP)
1434             nd = mutt_newsgroup_catchup (CurrentNewsSrv, f->name);
1435           else
1436             nd = mutt_newsgroup_uncatchup (CurrentNewsSrv, f->name);
1437
1438           if (nd)
1439           {
1440 /*          FOLDER folder;
1441             struct folder_file ff;
1442             char buffer[_POSIX_PATH_MAX + SHORT_STRING];
1443
1444             folder.ff = &ff;
1445             folder.ff->name = f->name;
1446             folder.ff->st = NULL;
1447             folder.ff->is_new = nd->new;
1448             folder.ff->nd = nd;
1449             FREE (&f->desc);
1450             mutt_FormatString (buffer, sizeof (buffer), NONULL(GroupFormat),
1451                   newsgroup_format_str, (unsigned long) &folder,
1452                   M_FORMAT_ARROWCURSOR);
1453             f->desc = safe_strdup (buffer); */
1454             if (menu->current + 1 < menu->max)
1455               menu->current++;
1456             menu->redraw = REDRAW_MOTION_RESYNCH;
1457           }
1458         }
1459         break;
1460
1461       case OP_LOAD_ACTIVE:
1462         if (!option (OPTNEWS))
1463           break;
1464
1465         {
1466           LIST *tmp;
1467           NNTP_DATA *data;
1468
1469           for (tmp = CurrentNewsSrv->list; tmp; tmp = tmp->next)
1470           {
1471             if ((data = (NNTP_DATA *)tmp->data))
1472               data->deleted = 1;
1473           }
1474         }
1475         nntp_get_active (CurrentNewsSrv);
1476
1477         destroy_state (&state);
1478         if (buffy)
1479           examine_mailboxes (menu, &state);
1480         else
1481           examine_directory (menu, &state, NULL, NULL);
1482         init_menu (&state, menu, title, sizeof (title), buffy);
1483         break;
1484 #endif /* USE_NNTP */
1485
1486 #if defined USE_IMAP || defined USE_NNTP
1487       case OP_BROWSER_SUBSCRIBE:
1488       case OP_BROWSER_UNSUBSCRIBE:
1489 #endif
1490 #ifdef USE_NNTP
1491       case OP_SUBSCRIBE_PATTERN:
1492       case OP_UNSUBSCRIBE_PATTERN:
1493         if (option (OPTNEWS))
1494         {
1495           regex_t *rx = (regex_t *) safe_malloc (sizeof (regex_t));
1496           char *s = buf;
1497           int j = menu->current;
1498           NNTP_DATA *nd;
1499           NNTP_SERVER *news = CurrentNewsSrv;
1500
1501           if (i == OP_SUBSCRIBE_PATTERN || i == OP_UNSUBSCRIBE_PATTERN)
1502           {
1503             char tmp[STRING];
1504             int err;
1505
1506             buf[0] = 0;
1507             if (i == OP_SUBSCRIBE_PATTERN)
1508               snprintf (tmp, sizeof (tmp), _("Subscribe pattern: "));
1509             else
1510               snprintf (tmp, sizeof (tmp), _("Unsubscribe pattern: "));
1511             if (mutt_get_field (tmp, buf, sizeof (buf), 0) != 0 || !buf[0])
1512             {
1513               FREE (&rx);
1514               break;
1515             }
1516
1517             if ((err = REGCOMP (rx, s, REG_NOSUB)) != 0)
1518             {
1519               regerror (err, rx, buf, sizeof (buf));
1520               regfree (rx);
1521               FREE (&rx);
1522               mutt_error ("%s", buf);
1523               break;
1524             }
1525             menu->redraw = REDRAW_FULL;
1526             j = 0;
1527           }
1528           else if (!state.entrylen)
1529           {
1530             mutt_error _("No newsgroups match the mask");
1531             break;
1532           }
1533
1534           for ( ; j < state.entrylen; j++)
1535           {
1536             struct folder_file *f = &state.entry[j];
1537
1538             if (i == OP_BROWSER_SUBSCRIBE || i == OP_BROWSER_UNSUBSCRIBE ||
1539                   regexec (rx, f->name, 0, NULL, 0) == 0)
1540             {
1541               if (i == OP_BROWSER_SUBSCRIBE || i == OP_SUBSCRIBE_PATTERN)
1542                 nd = mutt_newsgroup_subscribe (news, f->name);
1543               else
1544                 nd = mutt_newsgroup_unsubscribe (news, f->name);
1545 /*            if (nd)
1546               {
1547                 FOLDER folder;
1548                 char buffer[_POSIX_PATH_MAX + SHORT_STRING];
1549
1550                 folder.name = f->name;
1551                 folder.f = NULL;
1552                 folder.new = nd->new;
1553                 folder.nd = nd;
1554                 FREE (&f->desc);
1555                 mutt_FormatString (buffer, sizeof (buffer), NONULL(GroupFormat),
1556                         newsgroup_format_str, (unsigned long) &folder,
1557                         M_FORMAT_ARROWCURSOR);
1558                 f->desc = safe_strdup (buffer);
1559               } */
1560             }
1561             if (i == OP_BROWSER_SUBSCRIBE || i == OP_BROWSER_UNSUBSCRIBE)
1562             {
1563               if (menu->current + 1 < menu->max)
1564                 menu->current++;
1565               menu->redraw = REDRAW_MOTION_RESYNCH;
1566               break;
1567             }
1568           }
1569           if (i == OP_SUBSCRIBE_PATTERN)
1570           {
1571             LIST *grouplist = NULL;
1572
1573             if (news)
1574               grouplist = news->list;
1575             for (; grouplist; grouplist = grouplist->next)
1576             {
1577               nd = (NNTP_DATA *) grouplist->data;
1578               if (nd && nd->group && !nd->subscribed)
1579               {
1580                 if (regexec (rx, nd->group, 0, NULL, 0) == 0)
1581                 {
1582                   mutt_newsgroup_subscribe (news, nd->group);
1583                   add_folder (menu, &state, nd->group, NULL, nd, nd->new);
1584                 }
1585               }
1586             }
1587             init_menu (&state, menu, title, sizeof (title), buffy);
1588           }
1589           mutt_newsrc_update (news);
1590           nntp_clear_cacheindex (news);
1591           if (i != OP_BROWSER_SUBSCRIBE && i != OP_BROWSER_UNSUBSCRIBE)
1592             regfree (rx);
1593           FREE (&rx);
1594         }
1595 #ifdef USE_IMAP
1596         else
1597 #endif /* USE_IMAP && USE_NNTP */
1598 #endif /* USE_NNTP */
1599 #ifdef USE_IMAP
1600         {
1601           if (i == OP_BROWSER_SUBSCRIBE)
1602             imap_subscribe (state.entry[menu->current].name, 1);
1603           else
1604             imap_subscribe (state.entry[menu->current].name, 0);
1605         }
1606 #endif /* USE_IMAP */
1607     }
1608   }
1609   
1610   bail:
1611   
1612   if (!folder)
1613     strfcpy (LastDir, LastDirBackup, sizeof (LastDir));
1614   
1615 }