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