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