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