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