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