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