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