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