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