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