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