* clear out some warnings by gcc 9.3.1.
[alpine.git] / alpine / folder.c
bloba391181aac00f8e971b42504e4950b5610ff84d5
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: folder.c 1144 2008-08-14 16:53:34Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2013-2020 Eduardo Chappa
8 * Copyright 2006-2008 University of Washington
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 /*======================================================================
20 folder.c
22 Screen to display and manage all the users folders
24 This puts up a list of all the folders in the users mail directory on
25 the screen spacing it nicely. The arrow keys move from one to another
26 and the user can delete the folder or select it to change to or copy a
27 message to. The display lets the user scroll up or down a screen full,
28 or search for a folder name.
29 ====*/
32 #include "headers.h"
33 #include "../c-client/utf8aux.h"
34 #include "folder.h"
35 #include "keymenu.h"
36 #include "status.h"
37 #include "context.h"
38 #include "mailview.h"
39 #include "mailindx.h"
40 #include "mailcmd.h"
41 #include "titlebar.h"
42 #include "alpine.h"
43 #include "send.h"
44 #include "help.h"
45 #include "imap.h"
46 #include "signal.h"
47 #include "reply.h"
48 #include "setup.h"
49 #include "../pith/state.h"
50 #include "../pith/conf.h"
51 #include "../pith/folder.h"
52 #include "../pith/flag.h"
53 #include "../pith/filter.h"
54 #include "../pith/msgno.h"
55 #include "../pith/thread.h"
56 #include "../pith/util.h"
57 #include "../pith/stream.h"
58 #include "../pith/save.h"
59 #include "../pith/busy.h"
60 #include "../pith/list.h"
63 #define SUBSCRIBE_PMT \
64 _("Enter newsgroup name (or partial name to get a list): ")
65 #define LISTMODE_GRIPE _("Use \"X\" to mark selections in list mode")
66 #define SEL_ALTER_PMT _("ALTER folder selection : ")
67 #define SEL_TEXT_PMT _("Select by folder Name or Contents ? ")
68 #define SEL_PROP_PMT _("Select by which folder property ? ")
69 #define DIR_FOLD_PMT \
70 _("Folder and directory of the same name will be deleted. Continue")
74 * folder_list_write
76 #define FLW_NONE 0x00
77 #define FLW_LUNK 0x01 /* Using handles */
78 #define FLW_SLCT 0x02 /* Some folder is selected, may need X to show it */
79 #define FLW_LIST 0x04 /* Allow for ListMode for subscribing */
80 #define FLW_UNSEEN 0x08 /* Add (unseen) */
82 /* folder colors */
83 #define CLR_UNSEEN 0x01 /* color folder with unseen/new messages */
84 #define CLR_FOLDER 0x02 /* color a name of folder or directory */
85 #define CLR_DIRECT 0x04 /* color a separator of a directory */
86 #define CLR_FLDRLT 0x08 /* color of explanatory text in list scrn*/
87 #define CLR_NORMAL 0x10 /* use normal color */
89 /*----------------------------------------------------------------------
90 The data needed to redraw the folders screen, including the case where the
91 screen changes size in which case it may recalculate the folder_display.
92 ----*/
96 * Struct managing folder_lister arguments and holding state
97 * for various internal methods
99 typedef struct _folder_screen {
100 CONTEXT_S *context; /* current collection */
101 CONTEXT_S *list_cntxt; /* list mode collection */
102 MAILSTREAM **cache_streamp; /* cached mailstream */
103 char first_folder[MAXFOLDER];
104 unsigned first_dir:1; /* first_folder is a dir */
105 unsigned combined_view:1; /* display flat folder list */
106 unsigned no_dirs:1; /* no dirs in this screen */
107 unsigned no_empty_dirs:1; /* no empty dirs on this screen */
108 unsigned relative_path:1; /* return fully-qual'd specs */
109 unsigned save_sel:1;
110 unsigned force_intro:1;
111 unsigned agg_ops:1;
112 unsigned include_unseen_cnt:1;
113 struct key_menu *km; /* key label/command bindings */
114 struct _func_dispatch {
115 int (*valid)(FOLDER_S *, struct _folder_screen *);
116 struct {
117 HelpType text;
118 char *title;
119 } help;
120 struct {
121 char *bar;
122 TitleBarType style;
123 } title;
124 } f;
125 } FSTATE_S;
129 * Struct mananging folder_lister metadata as it gets passed
130 * in and back up thru scrolltool
132 typedef struct _folder_proc {
133 FSTATE_S *fs;
134 STRLIST_S *rv;
135 unsigned done:1; /* done listing folders?... */
136 unsigned all_done:1; /* ...and will list no more forever */
137 } FPROC_S;
139 #define FPROC(X) ((FPROC_S *)(X)->proc.data.p)
142 typedef struct _scanarg {
143 MAILSTREAM *stream;
144 int newstream;
145 CONTEXT_S *context;
146 char *pattern;
147 char type;
148 } SCANARG_S;
151 typedef struct _statarg {
152 MAILSTREAM *stream;
153 int newstream;
154 CONTEXT_S *context;
155 long flags;
156 long nmsgs;
157 int cmp;
158 } STATARG_S;
162 * Internal prototypes
164 STRLIST_S *folders_for_subscribe(struct pine *, CONTEXT_S *, char *);
165 int folders_for_post(struct pine *, CONTEXT_S **, char *);
166 int folder_selector(struct pine *, FSTATE_S *, char *, CONTEXT_S **);
167 void folder_sublist_context(char *, CONTEXT_S *, CONTEXT_S *, FDIR_S **, int);
168 CONTEXT_S *context_screen(CONTEXT_S *, struct key_menu *, int);
169 int exit_collection_add(struct headerentry *, void (*)(void), int, char **);
170 char *cancel_collection_add(void (*)(void));
171 char *cancel_collection_edit(void (*)(void));
172 char *cancel_collection_editing(char *, void (*)(void));
173 int build_namespace(char *, char **, char **, BUILDER_ARG *, int *);
174 int fl_val_gen(FOLDER_S *, FSTATE_S *);
175 int fl_val_writable(FOLDER_S *, FSTATE_S *);
176 int fl_val_subscribe(FOLDER_S *, FSTATE_S *);
177 STRLIST_S *folder_lister(struct pine *, FSTATE_S *);
178 int folder_list_text(struct pine *, FPROC_S *, gf_io_t, HANDLE_S **, int);
179 int folder_list_write(gf_io_t, HANDLE_S **, CONTEXT_S *, int, char *, int);
180 int folder_list_write_prefix(FOLDER_S *, int, gf_io_t);
181 int folder_list_write_middle(FOLDER_S *fp, CONTEXT_S *ctxt, gf_io_t pc, HANDLE_S *);
182 int folder_list_write_suffix(FOLDER_S *, int, gf_io_t);
183 int color_monitored(FOLDER_S *, int, int);
184 int color_test_for_folder(char *, char *);
185 int color_write_for_folder(gf_io_t pc, int testtype);
186 int use_color_for_folder(FOLDER_S *fp);
187 int folder_list_ith(int, CONTEXT_S *);
188 char *folder_list_center_space(char *, int);
189 HANDLE_S *folder_list_handle(FSTATE_S *, HANDLE_S *);
190 int folder_processor(int, MSGNO_S *, SCROLL_S *);
191 int folder_lister_clickclick(SCROLL_S *);
192 int folder_lister_choice(SCROLL_S *);
193 int folder_lister_finish(SCROLL_S *, CONTEXT_S *, int);
194 int folder_lister_addmanually(SCROLL_S *);
195 void folder_lister_km_manager(SCROLL_S *, int);
196 void folder_lister_km_sel_manager(SCROLL_S *, int);
197 void folder_lister_km_sub_manager(SCROLL_S *, int);
198 int folder_select(struct pine *, CONTEXT_S *, int);
199 int folder_lister_select(FSTATE_S *, CONTEXT_S *, int, int);
200 int folder_lister_parent(FSTATE_S *, CONTEXT_S *, int, int);
201 char *folder_lister_fullname(FSTATE_S *, char *);
202 void folder_export(SCROLL_S *);
203 int folder_import(SCROLL_S *, char *, size_t);
204 int folder_select_toggle(CONTEXT_S *, int, int (*)(CONTEXT_S *, int));
205 char *end_bracket_no_nest(char *);
206 int group_subscription(char *, size_t, CONTEXT_S *);
207 int rename_folder(CONTEXT_S *, int, char *, size_t, MAILSTREAM *);
208 int delete_folder(CONTEXT_S *, int, char *, size_t, MAILSTREAM **);
209 void print_folders(FPROC_S *);
210 int scan_get_pattern(char *, char *, int);
211 int folder_select_text(struct pine *, CONTEXT_S *, int);
212 int foreach_do_scan(FOLDER_S *, void *);
213 int scan_scan_folder(MAILSTREAM *, CONTEXT_S *, FOLDER_S *, char *);
214 int folder_select_props(struct pine *, CONTEXT_S *, int);
215 int folder_select_count(long *, int *);
216 int foreach_do_stat(FOLDER_S *, void *);
217 int foreach_folder(CONTEXT_S *, int, int (*)(FOLDER_S *, void *), void *);
218 int folder_delimiter(char *);
219 int shuffle_incoming_folders(CONTEXT_S *, int);
220 int swap_incoming_folders(int, int, FLIST *);
221 int search_folder_list(void *, char *);
222 char *get_post_list(char **);
223 char *quote_brackets_if_needed(char *);
224 #ifdef _WINDOWS
225 int folder_list_popup(SCROLL_S *, int);
226 int folder_list_select_popup(SCROLL_S *, int);
227 void folder_popup_config(FSTATE_S *, struct key_menu *,MPopup *);
228 #endif
231 /*----------------------------------------------------------------------
232 Front end to folder lister when it's called from the main menu
234 Args: ps -- The general pine_state data structure
236 Result: runs context and folder listers
238 ----*/
239 void
240 folder_screen(struct pine *ps)
242 int n = 1;
243 CONTEXT_S *cntxt = ps->context_current;
244 STRLIST_S *folders;
245 FSTATE_S fs;
246 MAILSTREAM *cache_stream = NULL;
248 dprint((1, "=== folder_screen called ====\n"));
249 mailcap_free(); /* free resources we won't be using for a while */
250 ps->next_screen = SCREEN_FUN_NULL;
252 /* Initialize folder state and dispatches */
253 memset(&fs, 0, sizeof(FSTATE_S));
254 fs.context = cntxt;
255 fs.cache_streamp = &cache_stream;
256 fs.combined_view = F_ON(F_CMBND_FOLDER_DISP, ps_global) != 0;
257 fs.agg_ops = F_ON(F_ENABLE_AGG_OPS, ps_global) != 0;
258 fs.relative_path = 1;
259 fs.include_unseen_cnt = 1;
260 fs.f.valid = fl_val_gen;
261 /* TRANSLATORS: The all upper case things are screen titles */
262 fs.f.title.bar = _("FOLDER LIST");
263 fs.f.title.style = FolderName;
264 fs.f.help.text = h_folder_maint;
265 fs.f.help.title = _("HELP FOR FOLDERS");
266 fs.km = &folder_km;
268 if(context_isambig(ps->cur_folder)
269 && (IS_REMOTE(ps->cur_folder) || !is_absolute_path(ps->cur_folder)
270 || (cntxt && cntxt->context && cntxt->context[0] == '{'))){
271 if(strlen(ps_global->cur_folder) < MAXFOLDER - 1){
272 strncpy(fs.first_folder, ps_global->cur_folder, MAXFOLDER);
273 fs.first_folder[MAXFOLDER-1] = '\0';
277 * If we're asked to start in the folder list of the current
278 * folder and it looks like the current folder is part of the
279 * current context, try to start in the list of folders in the
280 * current context.
282 if(ps->start_in_context || fs.combined_view){
283 char tmp[MAILTMPLEN], *p, *q;
284 FDIR_S *fp;
286 ps->start_in_context = 0;
287 n = 0;
289 if(!(NEWS_TEST(cntxt) || (cntxt->use & CNTXT_INCMNG))
290 && cntxt->dir->delim
291 && strchr(ps->cur_folder, cntxt->dir->delim)){
292 for(p = strchr(q = ps->cur_folder, cntxt->dir->delim);
294 p = strchr(q = ++p, cntxt->dir->delim)){
295 strncpy(tmp, q, MIN(p - q, sizeof(tmp)-1));
296 tmp[MIN(p - q, sizeof(tmp)-1)] = '\0';
298 fp = next_folder_dir(cntxt, tmp, FALSE, fs.cache_streamp);
300 fp->desc = folder_lister_desc(cntxt, fp);
302 /* Insert new directory into list */
303 fp->delim = cntxt->dir->delim;
304 fp->prev = cntxt->dir;
305 fp->status |= CNTXT_SUBDIR;
306 cntxt->dir = fp;
312 while(ps->next_screen == SCREEN_FUN_NULL
313 && ((n++) ? (cntxt = context_screen(cntxt,&c_mgr_km,1)) != NULL :1)){
315 fs.context = cntxt;
316 if(F_ON(F_ENABLE_INCOMING_CHECKING, ps) && ps->VAR_INCOMING_FOLDERS && ps->VAR_INCOMING_FOLDERS[0])
317 ps->in_folder_screen = 1;
319 if((folders = folder_lister(ps, &fs)) != NULL){
321 ps->in_folder_screen = 0;
323 if(ps && ps->ttyo){
324 blank_keymenu(ps->ttyo->screen_rows - 2, 0);
325 ps->mangled_footer = 1;
328 if(do_broach_folder((char *) folders->name,
329 fs.context, fs.cache_streamp
330 && *fs.cache_streamp ? fs.cache_streamp
331 : NULL, 0L) == 1){
332 reset_context_folders(ps->context_list);
333 ps->next_screen = mail_index_screen;
336 if(fs.cache_streamp)
337 *fs.cache_streamp = NULL;
338 free_strlist(&folders);
341 ps->in_folder_screen = 0;
344 if(fs.cache_streamp && *fs.cache_streamp)
345 pine_mail_close(*fs.cache_streamp);
347 ps->prev_screen = folder_screen;
351 /*----------------------------------------------------------------------
352 Front end to folder lister when it's called from the main menu
354 Args: ps -- The general pine_state data structure
356 Result: runs context and folder listers
358 ----*/
359 void
360 folder_config_screen(struct pine *ps, int edit_exceptions)
362 CONT_SCR_S css;
363 char title[50], htitle[50];
365 dprint((1, "=== folder_config_screen called ====\n"));
366 mailcap_free(); /* free resources we won't be using for a while */
368 if(edit_exceptions){
369 snprintf(title, sizeof(title), _("SETUP EXCEPTIONS COLLECTION LIST"));
370 snprintf(htitle, sizeof(htitle), _("HELP FOR SETUP EXCEPTIONS COLLECTIONS"));
372 else{
373 snprintf(title, sizeof(title), _("SETUP COLLECTION LIST"));
374 snprintf(htitle, sizeof(htitle), _("HELP FOR SETUP COLLECTIONS"));
377 memset(&css, 0, sizeof(CONT_SCR_S));
378 css.title = title;
379 /* TRANSLATORS: Print something1 using something2.
380 contexts is something1 */
381 css.print_string = _("contexts");
382 css.contexts = &ps_global->context_list;
383 css.help.text = h_collection_maint;
384 css.help.title = htitle;
385 css.keymenu = &c_cfg_km;
386 css.edit = 1;
389 * Use conf_scroll_screen to manage display/selection
390 * of contexts
392 context_config_screen(ps_global, &css, edit_exceptions);
396 /*----------------------------------------------------------------------
397 Browse folders for ^T selection from the Goto Prompt
399 Args: ps --
400 cntxtp -- pointer to addr of context to start in, list, and return
401 folder -- pointer to buffer inwhich to return selected folder
403 Returns: 1 if we have something valid in cntxtp and folder
404 0 if problem or user cancelled
406 ----*/
408 folders_for_goto(struct pine *ps, CONTEXT_S **cntxtp, char *folder, int sublist)
410 int rv;
411 CONTEXT_S fake_context;
412 FDIR_S *fake_dir = NULL;
413 FSTATE_S fs;
415 dprint((1, "=== folders_for_goto called ====\n"));
417 /* Initialize folder state and dispatches */
418 memset(&fs, 0, sizeof(FSTATE_S));
419 fs.context = *cntxtp;
420 fs.combined_view = !sublist && F_ON(F_CMBND_FOLDER_DISP, ps_global) != 0;
421 fs.f.valid = fl_val_gen;
422 fs.f.title.bar = _("GOTO: SELECT FOLDER");
423 fs.f.title.style = FolderName;
424 fs.f.help.text = h_folder_open;
425 fs.f.help.title = _("HELP FOR OPENING FOLDERS");
426 fs.km = &folder_sel_km;
428 /* If we were provided a string,
429 * dummy up a context for a substring match
431 if(sublist && *folder && context_isambig(folder)){
432 if((*cntxtp)->use & CNTXT_INCMNG){
433 q_status_message(SM_ORDER, 0, 3,
434 _("All folders displayed for Incoming Collection"));
436 else{
437 folder_sublist_context(folder, *cntxtp, &fake_context,
438 &fake_dir, sublist);
439 fs.context = &fake_context;
440 fs.relative_path = 1;
441 fs.force_intro = 1;
442 cntxtp = &fs.context;
446 rv = folder_selector(ps, &fs, folder, cntxtp);
448 if(fake_dir)
449 free_fdir(&fake_dir, TRUE);
451 return(rv);
455 /*----------------------------------------------------------------------
456 Browse folders for ^T selection from the Save Prompt
458 Args: ps --
459 cntxtp -- pointer to addr of context to start in, list, and return
460 folder -- pointer to buffer inwhich to return selected folder
462 Returns: 1 if we have something valid in cntxtp and folder
463 0 if problem or user cancelled
465 ----*/
467 folders_for_save(struct pine *ps, CONTEXT_S **cntxtp, char *folder, int sublist)
469 int rv;
470 CONTEXT_S fake_context;
471 FDIR_S *fake_dir = NULL;
472 FSTATE_S fs;
474 dprint((1, "=== folders_for_save called ====\n"));
476 /* Initialize folder state and dispatches */
477 memset(&fs, 0, sizeof(FSTATE_S));
478 fs.context = *cntxtp;
479 fs.combined_view = F_ON(F_CMBND_FOLDER_DISP, ps_global) != 0;
480 fs.f.valid = fl_val_gen;
481 fs.f.title.bar = _("SAVE: SELECT FOLDER");
482 fs.f.title.style = MessageNumber;
483 fs.f.help.text = h_folder_save;
484 fs.f.help.title = _("HELP FOR SAVING MESSAGES TO FOLDERS");
485 fs.km = &folder_sela_km;
487 /* If we were provided a string,
488 * dummy up a context for a substring match
490 if(sublist && *folder && context_isambig(folder)){
491 if((*cntxtp)->use & CNTXT_INCMNG){
492 q_status_message(SM_ORDER, 0, 3,
493 _("All folders displayed for Incoming Collection"));
495 else{
496 folder_sublist_context(folder, *cntxtp, &fake_context,
497 &fake_dir, sublist);
498 fs.context = &fake_context;
499 fs.relative_path = 1;
500 fs.force_intro = 1;
501 cntxtp = &fs.context;
505 rv = folder_selector(ps, &fs, folder, cntxtp);
507 if(fake_dir)
508 free_fdir(&fake_dir, TRUE);
510 return(rv);
514 /*----------------------------------------------------------------------
515 Browse folders for ^T selection from the Subscribe Prompt
517 Args: ps --
518 cntxtp -- pointer to addr of context to start in, list, and return
519 folder -- pointer to buffer inwhich to return selected folder
521 Returns: 1 if we have something valid in cntxtp and folder
522 0 if problem or user cancelled
524 ----*/
525 STRLIST_S *
526 folders_for_subscribe(struct pine *ps, CONTEXT_S *cntxt, char *folder)
528 STRLIST_S *folders = NULL;
529 FSTATE_S fs;
530 void (*redraw)(void);
532 dprint((1, "=== folders_for_sub called ====\n"));
534 /* Initialize folder state and dispatches */
535 memset(&fs, 0, sizeof(FSTATE_S));
536 fs.context = cntxt;
537 fs.f.valid = fl_val_subscribe;
538 fs.f.title.bar = _("SUBSCRIBE: SELECT FOLDER");
539 fs.f.title.style = FolderName;
540 fs.f.help.text = h_folder_subscribe;
541 fs.f.help.title = _("HELP SELECTING NEWSGROUP TO SUBSCRIBE TO");
542 fs.km = &folder_sub_km;
543 fs.force_intro = 1;
545 fs.context = cntxt;
546 redraw = ps_global->redrawer;
547 folders = folder_lister(ps, &fs);
548 ps_global->redrawer = redraw;
549 if(ps_global->redrawer)
550 (*ps_global->redrawer)();
552 return(folders);
556 /*----------------------------------------------------------------------
557 Browse folders for ^T selection for posting
559 Args: ps --
560 cntxtp -- pointer to addr of context to start in, list, and return
561 folder -- pointer to buffer inwhich to return selected folder
563 Returns: 1 if we have something valid in cntxtp and folder
564 0 if problem or user cancelled
566 ----*/
568 folders_for_post(struct pine *ps, CONTEXT_S **cntxtp, char *folder)
570 FSTATE_S fs;
572 dprint((1, "=== folders_for_post called ====\n"));
574 /* Initialize folder state and dispatches */
575 memset(&fs, 0, sizeof(FSTATE_S));
576 fs.context = *cntxtp;
577 fs.f.valid = fl_val_subscribe;
578 fs.f.title.bar = _("NEWS: SELECT GROUP");
579 fs.f.title.style = FolderName;
580 fs.f.help.text = h_folder_postnews;
581 fs.f.help.title = _("HELP FOR SELECTING NEWSGROUP TO POST TO");
582 fs.km = &folder_post_km;
584 return(folder_selector(ps, &fs, folder, cntxtp));
589 folder_selector(struct pine *ps, FSTATE_S *fs, char *folder, CONTEXT_S **cntxtp)
591 int rv = 0;
592 STRLIST_S *folders;
595 fs->context = *cntxtp;
596 if((folders = folder_lister(ps, fs)) != NULL){
597 strncpy(folder, (char *) folders->name, MAILTMPLEN-1);
598 folder[MAILTMPLEN-1] = '\0';
599 free_strlist(&folders);
600 *cntxtp = fs->context;
601 rv++;
602 break;
604 else if(!(fs->context
605 && (fs->context->next || fs->context->prev))
606 || fs->combined_view)
607 break;
609 while((*cntxtp = context_screen(*cntxtp, &c_sel_km, 0)) != NULL);
611 return(rv);
615 void
616 folder_sublist_context(char *folder, CONTEXT_S *cntxt, CONTEXT_S *new_cntxt, FDIR_S **new_dir, int lev)
618 char *p, *q, *ref, *wildcard;
620 *new_cntxt = *cntxt;
621 new_cntxt->next = new_cntxt->prev = NULL;
622 new_cntxt->dir = *new_dir = (FDIR_S *) fs_get(sizeof(FDIR_S));
623 memset(*new_dir, 0, sizeof(FDIR_S));
624 (*new_dir)->status |= CNTXT_NOFIND;
625 (*new_dir)->folders = init_folder_entries();
626 if(!((*new_dir)->delim = cntxt->dir->delim)){
627 /* simple LIST to acquire delimiter, doh */
628 build_folder_list(NULL, new_cntxt, "", NULL,
629 NEWS_TEST(new_cntxt) ? BFL_LSUB : BFL_NONE);
630 new_cntxt->dir->status = CNTXT_NOFIND;
633 wildcard = NEWS_TEST(new_cntxt) ? "*" : "%";
635 if((p = strrindex(folder, (*new_dir)->delim)) != NULL){
636 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s", p + 1, wildcard);
637 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
638 (*new_dir)->view.internal = cpystr(tmp_20k_buf);
639 for(ref = tmp_20k_buf, q = new_cntxt->context;
640 (*ref = *q) != '\0' && !(*q == '%' && *(q+1) == 's');
641 ref++, q++)
644 for(q = folder; q <= p; q++, ref++)
645 *ref = *q;
647 *ref = '\0';
648 (*new_dir)->ref = cpystr(tmp_20k_buf);
650 (*new_dir)->status |= CNTXT_SUBDIR;
652 else{
653 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
654 (lev > 1) ? wildcard : "", folder, wildcard);
655 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
656 (*new_dir)->view.internal = cpystr(tmp_20k_buf);
657 /* leave (*new_dir)->ref == NULL */
660 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("List of folders matching \"%s*\""), folder);
661 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
662 (*new_dir)->desc = cpystr(tmp_20k_buf);
666 /*----------------------------------------------------------------------
667 Browse folders for ^T selection from the composer
669 Args: error_mess -- pointer to place to return an error message
671 Returns: result if folder selected, NULL if not
672 Composer expects the result to be alloc'd here
674 ----*/
675 char *
676 folders_for_fcc(char **errmsg)
678 char *rs = NULL;
679 STRLIST_S *folders;
680 FSTATE_S fs;
682 dprint((1, "=== folders_for_fcc called ====\n"));
684 /* Coming back from composer */
685 fix_windsize(ps_global);
686 init_sigwinch();
688 /* Initialize folder state and dispatches */
689 memset(&fs, 0, sizeof(FSTATE_S));
690 fs.combined_view = F_ON(F_CMBND_FOLDER_DISP, ps_global) != 0;
691 fs.f.valid = fl_val_gen;
692 fs.f.title.bar = _("FCC: SELECT FOLDER");
693 fs.f.title.style = FolderName;
694 fs.f.help.text = h_folder_fcc;
695 fs.f.help.title = _("HELP FOR SELECTING THE FCC");
696 fs.km = &folder_sela_km;
698 /* start in the default save context */
699 fs.context = default_save_context(ps_global->context_list);
702 if((folders = folder_lister(ps_global, &fs)) != NULL){
703 char *name;
705 /* replace nickname with full name */
706 if(!(name = folder_is_nick((char *) folders->name,
707 FOLDERS(fs.context), 0)))
708 name = (char *) folders->name;
710 if(context_isambig(name) && !((fs.context->use) & CNTXT_SAVEDFLT)){
711 char path_in_context[MAILTMPLEN];
713 context_apply(path_in_context, fs.context, name,
714 sizeof(path_in_context));
715 if(!(IS_REMOTE(path_in_context)
716 || is_absolute_path(path_in_context))){
718 * Name is relative to the home directory,
719 * so have to add that. Otherwise, the sender will
720 * assume it is in the primary collection since it
721 * will still be ambiguous.
723 build_path(tmp_20k_buf, ps_global->ui.homedir,
724 path_in_context, SIZEOF_20KBUF);
725 rs = cpystr(tmp_20k_buf);
727 else
728 rs = cpystr(path_in_context);
730 else
731 rs = cpystr(name);
733 free_strlist(&folders);
734 break;
736 else if(!(fs.context && (fs.context->next || fs.context->prev))
737 || fs.combined_view)
738 break;
740 while((fs.context = context_screen(fs.context, &c_fcc_km, 0)) != NULL);
742 return(rs);
746 /*----------------------------------------------------------------------
747 Browse folders for ^T selection from the role editor
749 Returns: result if folder selected, NULL if not
750 Tesult is alloc'd here
752 ----*/
753 char *
754 folder_for_config(int flags)
756 char *rs = NULL;
757 STRLIST_S *folders;
758 FSTATE_S fs;
760 dprint((1, "=== folder_for_config called ====\n"));
762 /* Initialize folder state and dispatches */
763 memset(&fs, 0, sizeof(FSTATE_S));
764 fs.combined_view = F_ON(F_CMBND_FOLDER_DISP, ps_global) != 0;
765 fs.f.valid = fl_val_gen;
766 fs.f.title.bar = _("SELECT FOLDER");
767 fs.f.title.style = FolderName;
768 fs.km = &folder_sela_km;
769 if(flags & FOR_PATTERN){
770 fs.f.help.text = h_folder_pattern_roles;
771 fs.f.help.title = _("HELP FOR SELECTING CURRENT FOLDER");
773 else if(flags & FOR_OPTIONSCREEN){
774 fs.f.help.text = h_folder_stayopen_folders;
775 fs.f.help.title = _("HELP FOR SELECTING FOLDER");
777 else{
778 fs.f.help.text = h_folder_action_roles;
779 fs.f.help.title = _("HELP FOR SELECTING FOLDER");
782 /* start in the current context */
783 fs.context = ps_global->context_current;
786 if((folders = folder_lister(ps_global, &fs)) != NULL){
787 char *name = NULL;
789 /* replace nickname with full name */
790 if(!(flags & (FOR_PATTERN | FOR_OPTIONSCREEN)))
791 name = folder_is_nick((char *) folders->name,
792 FOLDERS(fs.context), 0);
794 if(!name)
795 name = (char *) folders->name;
797 if(context_isambig(name) &&
798 !(flags & (FOR_PATTERN | FOR_OPTIONSCREEN) &&
799 folder_is_nick(name, FOLDERS(fs.context), 0))){
800 char path_in_context[MAILTMPLEN];
802 context_apply(path_in_context, fs.context, name,
803 sizeof(path_in_context));
806 * We may still have a non-fully-qualified name. In the
807 * action case, that will be interpreted in the primary
808 * collection instead of as a local name.
809 * Qualify that name the same way we
810 * qualify it in match_pattern_folder_specific.
812 if(!(IS_REMOTE(path_in_context) ||
813 path_in_context[0] == '#')){
814 if(strlen(path_in_context) < (MAILTMPLEN/2)){
815 char tmp[MAX(MAILTMPLEN,NETMAXMBX)];
816 char *t;
818 t = mailboxfile(tmp, path_in_context);
819 rs = cpystr(t);
821 else{ /* the else part should never happen */
822 build_path(tmp_20k_buf, ps_global->ui.homedir,
823 path_in_context, SIZEOF_20KBUF);
824 rs = cpystr(tmp_20k_buf);
827 else
828 rs = cpystr(path_in_context);
830 else
831 rs = cpystr(name);
833 free_strlist(&folders);
834 break;
836 else if(!(fs.context && (fs.context->next || fs.context->prev))
837 || fs.combined_view)
838 break;
840 while((fs.context = context_screen(fs.context, &c_fcc_km, 0)) != NULL);
842 return(rs);
847 * offer screen with list of contexts to select and some sort
848 * of descriptions
850 CONTEXT_S *
851 context_screen(CONTEXT_S *start, struct key_menu *km, int edit_config)
853 /* If a list, let the user tell us what to do */
854 if(F_OFF(F_CMBND_FOLDER_DISP, ps_global)
855 && ps_global->context_list
856 && ps_global->context_list->next){
857 CONT_SCR_S css;
859 memset(&css, 0, sizeof(CONT_SCR_S));
860 css.title = _("COLLECTION LIST");
861 css.print_string = _("contexts");
862 css.start = start;
863 css.contexts = &ps_global->context_list;
864 css.help.text = h_collection_screen;
865 css.help.title = _("HELP FOR COLLECTION LIST");
866 css.keymenu = km;
867 css.edit = edit_config;
870 * Use conf_scroll_screen to manage display/selection
871 * of contexts
873 return(context_select_screen(ps_global, &css, 0));
876 return(ps_global->context_list);
880 static struct headerentry headents_templ[]={
881 /* TRANSLATORS: these are the headings for setting up a collection of
882 folders, PATH is a filesystem path, VIEW is sort of a technical
883 term that can be used to restrict the View to fewer folders */
884 {"Nickname : ", N_("Nickname"), h_composer_cntxt_nick, 12, 0, NULL,
885 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
886 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
887 {"Server : ", N_("Server"), h_composer_cntxt_server, 12, 0, NULL,
888 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
889 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
890 {"Path : ", N_("Path"), h_composer_cntxt_path, 12, 0, NULL,
891 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
892 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
893 {"View : ", N_("View"), h_composer_cntxt_view, 12, 0, NULL,
894 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
895 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
896 {NULL, NULL, NO_HELP, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
897 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}
899 #define AC_NICK 0
900 #define AC_SERV 1
901 #define AC_PATH 2
902 #define AC_VIEW 3
905 char *
906 context_edit_screen(struct pine *ps, char *func, char *def_nick,
907 char *def_serv, char *def_path, char *def_view)
909 int editor_result, i, j;
910 char servpart[MAILTMPLEN], new_cntxt[MAILTMPLEN];
911 char pathpart[MAILTMPLEN], allbutnick[MAILTMPLEN];
912 char tmp[MAILTMPLEN], *nick, *serv, *path, *view,
913 *return_cntxt = NULL, *val, *p;
914 char nickpmt[100], servpmt[100], pathpmt[100], viewpmt[100];
915 int indent;
916 PICO pbf;
917 STORE_S *msgso;
918 NETMBX mb;
920 standard_picobuf_setup(&pbf);
921 pbf.pine_flags |= P_NOBODY;
922 pbf.exittest = exit_collection_add;
923 pbf.canceltest = (func && !strucmp(func, "EDIT")) ? cancel_collection_edit
924 : cancel_collection_add;
925 snprintf(tmp, sizeof(tmp), _("FOLDER COLLECTION %s"), func);
926 tmp[sizeof(tmp)-1] = '\0';
927 pbf.pine_anchor = set_titlebar(tmp, ps_global->mail_stream,
928 ps_global->context_current,
929 ps_global->cur_folder,ps_global->msgmap,
930 0, FolderName, 0, 0, NULL);
932 /* An informational message */
933 if((msgso = so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
934 pbf.msgtext = (void *) so_text(msgso);
935 so_puts(msgso,
936 _("\n Fill in the fields above to add a Folder Collection to your"));
937 so_puts(msgso,
938 _("\n COLLECTION LIST screen."));
939 so_puts(msgso,
940 _("\n Use the \"^G\" command to get help specific to each item, and"));
941 so_puts(msgso,
942 _("\n use \"^X\" when finished."));
946 pbf.headents = (struct headerentry *)fs_get((sizeof(headents_templ)
947 /sizeof(struct headerentry))
948 * sizeof(struct headerentry));
949 memset((void *) pbf.headents, 0,
950 (sizeof(headents_templ)/sizeof(struct headerentry))
951 * sizeof(struct headerentry));
953 for(i = 0; headents_templ[i].prompt; i++)
954 pbf.headents[i] = headents_templ[i];
956 indent = utf8_width(_("Nickname")) + 2;
958 nick = cpystr(def_nick ? def_nick : "");
959 pbf.headents[AC_NICK].realaddr = &nick;
960 pbf.headents[AC_NICK].maxlen = strlen(nick);
961 utf8_snprintf(nickpmt, sizeof(nickpmt), "%-*.*w: ", indent, indent, _("Nickname"));
962 pbf.headents[AC_NICK].prompt = nickpmt;
963 pbf.headents[AC_NICK].prwid = indent+2;
965 serv = cpystr(def_serv ? def_serv : "");
966 pbf.headents[AC_SERV].realaddr = &serv;
967 pbf.headents[AC_SERV].maxlen = strlen(serv);
968 utf8_snprintf(servpmt, sizeof(servpmt), "%-*.*w: ", indent, indent, _("Server"));
969 pbf.headents[AC_SERV].prompt = servpmt;
970 pbf.headents[AC_SERV].prwid = indent+2;
972 path = cpystr(def_path ? def_path : "");
973 pbf.headents[AC_PATH].realaddr = &path;
974 pbf.headents[AC_PATH].maxlen = strlen(path);
975 pbf.headents[AC_PATH].bldr_private = (void *) 0;
976 utf8_snprintf(pathpmt, sizeof(pathpmt), "%-*.*w: ", indent, indent, _("Path"));
977 pbf.headents[AC_PATH].prompt = pathpmt;
978 pbf.headents[AC_PATH].prwid = indent+2;
980 view = cpystr(def_view ? def_view : "");
981 pbf.headents[AC_VIEW].realaddr = &view;
982 pbf.headents[AC_VIEW].maxlen = strlen(view);
983 utf8_snprintf(viewpmt, sizeof(viewpmt), "%-*.*w: ", indent, indent, _("View"));
984 pbf.headents[AC_VIEW].prompt = viewpmt;
985 pbf.headents[AC_VIEW].prwid = indent+2;
988 * If this is new context, setup to query IMAP server
989 * for location of personal namespace.
991 if(!(def_nick || def_serv || def_path || def_view)){
992 pbf.headents[AC_SERV].builder = build_namespace;
993 pbf.headents[AC_SERV].affected_entry = &pbf.headents[AC_PATH];
994 pbf.headents[AC_SERV].bldr_private = (void *) 0;
997 /* pass to pico and let user change them */
998 editor_result = pico(&pbf);
1000 if(editor_result & COMP_GOTHUP){
1001 hup_signal();
1003 else{
1004 fix_windsize(ps_global);
1005 init_signals();
1008 if(editor_result & COMP_CANCEL){
1009 cmd_cancelled(func);
1011 else if(editor_result & COMP_EXIT){
1012 servpart[0] = pathpart[0] = new_cntxt[0] = allbutnick[0] = '\0';
1013 if(serv && *serv){
1014 if(serv[0] == '{' && serv[strlen(serv)-1] == '}'){
1015 strncpy(servpart, serv, sizeof(servpart)-1);
1016 servpart[sizeof(servpart)-1] = '\0';
1018 else
1019 snprintf(servpart, sizeof(servpart), "{%s}", serv);
1021 if(mail_valid_net_parse(servpart, &mb)){
1022 if(!struncmp(mb.service, "nntp", 4)
1023 && (!path || strncmp(path, "#news.", 6)))
1024 strncat(servpart, "#news.", sizeof(servpart)-1-strlen(servpart));
1026 else
1027 alpine_panic("Unexpected invalid server");
1029 else
1030 servpart[0] = '\0';
1032 servpart[sizeof(servpart)-1] = '\0';
1034 new_cntxt[0] = '\0';
1035 if(nick && *nick){
1036 val = quote_if_needed(nick);
1037 if(val){
1038 strncpy(new_cntxt, val, sizeof(new_cntxt)-2);
1039 new_cntxt[sizeof(new_cntxt)-2] = '\0';
1040 if(val != nick)
1041 fs_give((void **)&val);
1043 strncat(new_cntxt, " ", sizeof(new_cntxt)-strlen(new_cntxt)-1);
1044 new_cntxt[sizeof(new_cntxt)-1] = '\0';
1048 p = allbutnick;
1049 sstrncpy(&p, servpart, sizeof(allbutnick)-1-(p-allbutnick));
1050 allbutnick[sizeof(allbutnick)-1] = '\0';
1052 if(path){
1053 val = quote_brackets_if_needed(path);
1054 if(val){
1055 strncpy(pathpart, val, sizeof(pathpart)-1);
1056 pathpart[sizeof(pathpart)-1] = '\0';
1057 if(val != path)
1058 fs_give((void **)&val);
1061 if(pbf.headents[AC_PATH].bldr_private != (void *) 0){
1062 strncat(pathpart, (char *) pbf.headents[AC_PATH].bldr_private,
1063 sizeof(pathpart)-strlen(pathpart)-1);
1064 pathpart[sizeof(pathpart)-1] = '\0';
1068 sstrncpy(&p, pathpart, sizeof(allbutnick)-1-(p-allbutnick));
1069 allbutnick[sizeof(allbutnick)-1] = '\0';
1071 if(view[0] != '[' && sizeof(allbutnick)-1-(p-allbutnick) > 0){
1072 *p++ = '[';
1073 *p = '\0';
1076 sstrncpy(&p, view, sizeof(allbutnick)-1-(p-allbutnick));
1077 allbutnick[sizeof(allbutnick)-1] = '\0';
1078 if((j=strlen(view)) < 2 || (view[j-1] != ']' &&
1079 sizeof(allbutnick)-1-(p-allbutnick) > 0)){
1080 *p++ = ']';
1081 *p = '\0';
1084 val = quote_if_needed(allbutnick);
1085 if(val){
1086 strncat(new_cntxt, val, sizeof(new_cntxt)-1-strlen(new_cntxt));
1087 new_cntxt[sizeof(new_cntxt)-1] = '\0';
1089 if(val != allbutnick)
1090 fs_give((void **)&val);
1093 return_cntxt = cpystr(new_cntxt);
1096 for(i = 0; headents_templ[i].prompt; i++)
1097 fs_give((void **) pbf.headents[i].realaddr);
1099 if(pbf.headents[AC_PATH].bldr_private != (void *) 0)
1100 fs_give(&pbf.headents[AC_PATH].bldr_private);
1102 fs_give((void **) &pbf.headents);
1104 standard_picobuf_teardown(&pbf);
1106 if(msgso)
1107 so_give(&msgso);
1109 return(return_cntxt);
1114 * Doubles up '[' and ']' characters and returns either
1115 * an allocated string or a pointer to the source string
1116 * if no quoting is needed.
1118 char *
1119 quote_brackets_if_needed(char *src)
1121 char *step1 = NULL, *step2 = NULL, *ret;
1123 ret = src;
1125 if((strpbrk(src, "[]") != NULL)
1126 && ((step1 = add_escapes(src, "[", '[', "", "")) != NULL)
1127 && ((step2 = add_escapes(step1, "]", ']', "", ""))))
1128 ret = step2;
1130 if(step1)
1131 fs_give((void **) &step1);
1133 return(ret);
1138 * Call back for pico to prompt the user for exit confirmation
1140 * Returns: either NULL if the user accepts exit, or string containing
1141 * reason why the user declined.
1144 exit_collection_add(struct headerentry *he, void (*redraw_pico)(void), int allow_flowed,
1145 char **result)
1147 char prompt[256], tmp[MAILTMPLEN], tmpnodel[MAILTMPLEN], *server, *path,
1148 delim = '\0', *rstr = NULL, *p;
1149 int exists = 0, i;
1150 void (*redraw)(void) = ps_global->redrawer;
1151 NETMBX mb;
1153 ps_global->redrawer = redraw_pico;
1154 fix_windsize(ps_global);
1156 server = *he[AC_SERV].realaddr;
1157 removing_trailing_white_space(server);
1158 removing_leading_white_space(server);
1160 path = *he[AC_PATH].realaddr;
1162 if(*server){
1163 /* No brackets? */
1164 if(server[0] == '{' && server[strlen(server)-1] == '}'){
1165 strncpy(tmp, server, sizeof(tmp)-1);
1166 tmp[sizeof(tmp)-1] = '\0';
1168 else /* add them */ /* sizeof(tmp) == MAILTMPLEN */
1169 snprintf(tmp, sizeof(tmp), "{%.*s}", MAILTMPLEN-3, server);
1171 if(mail_valid_net_parse(tmp, &mb)){ /* news? verify namespace */
1172 if(!struncmp(mb.service, "nntp", 4) && strncmp(path, "#news.", 6))
1173 strncat(tmp, "#news.", sizeof(tmp)-1-strlen(tmp));
1175 else
1176 rstr = "Invalid Server entry";
1178 else
1179 tmp[0] = '\0';
1181 tmp[sizeof(tmp)-1] = '\0';
1183 if(!rstr){
1185 * Delimiter acquisition below serves dual purposes. One's to
1186 * get the delimiter so we can make sure it's hanging off the end
1187 * of the path so we can test for the directory existence. The
1188 * second's to make sure the server (and any requested service)
1189 * name we were given exists. It should be handled by the folder
1190 * existence test further below, but it doesn't work with news...
1192 * Update. Now we are stripping the delimiter in the tmpnodel version
1193 * so that we can pass that to folder_exists. Cyrus does not answer
1194 * that the folder exists if we leave the trailing delimiter.
1195 * Hubert 2004-12-17
1197 strncat(tmp, path, sizeof(tmp)-1-strlen(tmp));
1198 tmp[sizeof(tmp)-1] = '\0';
1199 strncpy(tmpnodel, tmp, sizeof(tmpnodel)-1);
1200 tmpnodel[sizeof(tmpnodel)-1] = '\0';
1202 if(he[AC_PATH].bldr_private != (void *) 0)
1203 fs_give(&he[AC_PATH].bldr_private);
1205 ps_global->mm_log_error = 0;
1206 if((delim = folder_delimiter(tmp)) != '\0'){
1207 if(*path){
1208 if(tmp[(i = strlen(tmp)) - 1] == delim)
1209 tmpnodel[i-1] = '\0';
1210 else{
1211 tmp[i] = delim;
1212 tmp[i+1] = '\0';
1213 he[AC_PATH].bldr_private = (void *) cpystr(&tmp[i]);
1217 else if(ps_global->mm_log_error && ps_global->last_error)
1218 /* We used to bail, but this was changed with 4.10
1219 * as some users wanted to add clctn before the server
1220 * was actually around and built.
1222 flush_status_messages(0); /* mail_create gripes */
1223 else
1224 dprint((1, "exit_col_test: No Server Hierarchy!\n"));
1227 if(!rstr){
1228 if(!*tmp
1229 || !delim
1230 || ((*(p = tmp) == '#'
1231 || (*tmp == '{' && (p = strchr(tmp, '}')) && *++p))
1232 && !struncmp(p, "#news.", 6))
1233 || (*tmp == '{' && (p = strchr(tmp, '}')) && !*++p)){
1234 exists = 1;
1236 else if((i = folder_exists(NULL, tmpnodel)) & FEX_ERROR){
1237 if(!(rstr = ps_global->last_error))
1238 rstr = _("Problem testing for directory existence");
1240 else
1241 exists = (i & FEX_ISDIR);
1243 if(exists)
1244 snprintf(prompt, sizeof(prompt), _("Exit and save changes"));
1245 else
1246 snprintf(prompt, sizeof(prompt), _("Exit, saving changes and creating Path"));
1248 if(want_to(prompt, 'y', 0, NO_HELP, WT_NORM) == 'y'){
1249 if(!exists && !pine_mail_create(NULL, tmp)){
1250 flush_status_messages(1); /* mail_create gripes */
1251 if(!(rstr = ps_global->last_error))
1252 rstr = "";
1255 else
1256 rstr = _("Use ^C to abandon changes you've made");
1259 if(result)
1260 *result = rstr;
1262 ps_global->redrawer = redraw;
1263 return((rstr == NULL) ? 0 : 1);
1267 char *
1268 cancel_collection_add(void (*redraw_pico)(void))
1270 return(cancel_collection_editing(_("Add"), redraw_pico));
1274 char *
1275 cancel_collection_edit(void (*redraw_pico)(void))
1277 return(cancel_collection_editing(_("Edit"), redraw_pico));
1281 char *
1282 cancel_collection_editing(char *func, void (*redraw_pico)(void))
1284 char *rstr = NULL;
1285 void (*redraw)(void) = ps_global->redrawer;
1286 static char rbuf[20];
1287 char prompt[256];
1288 #define CCA_PROMPT \
1289 _("Cancel Add (answering \"Yes\" will abandon any changes made) ")
1291 snprintf(prompt, sizeof(prompt), _("Cancel %s (answering \"Yes\" will abandon any changes made) "), func ? func : "Add");
1292 snprintf(rbuf, sizeof(rbuf), _("%s Cancelled) "), func ? func : "Add");
1294 ps_global->redrawer = redraw_pico;
1295 fix_windsize(ps_global);
1297 switch(want_to(prompt, 'y', 'x', NO_HELP, WT_NORM)){
1298 case 'y':
1299 rstr = rbuf;
1300 break;
1302 case 'n':
1303 case 'x':
1304 break;
1307 ps_global->redrawer = redraw;
1308 return(rstr);
1313 build_namespace(char *server, char **server_too, char **error, BUILDER_ARG *barg, int *mangled)
1315 char *p, *name;
1316 int we_cancel = 0;
1317 MAILSTREAM *stream;
1318 NAMESPACE ***namespace;
1319 size_t len;
1321 dprint((5, "- build_namespace - (%s)\n",
1322 server ? server : "nul"));
1324 if(*barg->me){ /* only call this once! */
1325 if(server_too)
1326 *server_too = cpystr(server ? server : "");
1328 return(0);
1330 else
1331 *barg->me = (void *) 1;
1333 if((p = server) != NULL) /* empty string? */
1334 while(*p && isspace((unsigned char) *p))
1335 p++;
1337 if(p && *p){
1338 if(server_too)
1339 *server_too = cpystr(p);
1341 len = strlen(p) + 2;
1342 name = (char *) fs_get((len + 1) * sizeof(char));
1343 snprintf(name, len+1, "{%s}", p);
1345 else{
1346 if(server_too)
1347 *server_too = cpystr("");
1349 return(0);
1352 *mangled |= BUILDER_SCREEN_MANGLED;
1353 fix_windsize(ps_global);
1354 init_sigwinch();
1355 clear_cursor_pos();
1357 we_cancel = busy_cue(_("Fetching default directory"), NULL, 1);
1359 if((stream = pine_mail_open(NULL, name,
1360 OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE,
1361 NULL)) != NULL){
1362 if((namespace = mail_parameters(stream, GET_NAMESPACE, NULL))
1363 && *namespace && (*namespace)[0]
1364 && (*namespace)[0]->name && (*namespace)[0]->name[0]){
1365 if(barg->tptr)
1366 fs_give((void **)&barg->tptr);
1368 barg->tptr = cpystr((*namespace)[0]->name);
1371 pine_mail_close(stream);
1374 if(we_cancel)
1375 cancel_busy_cue(-1);
1377 fs_give((void **) &name);
1379 return(1);
1384 fl_val_gen (FOLDER_S *f, FSTATE_S *fs)
1386 return(f && FLDR_NAME(f));
1391 fl_val_writable (FOLDER_S *f, FSTATE_S *fs)
1393 return(1);
1398 fl_val_subscribe (FOLDER_S *f, FSTATE_S *fs)
1400 if(f->subscribed){
1401 q_status_message1(SM_ORDER, 0, 4, _("Already subscribed to \"%s\""),
1402 FLDR_NAME(f));
1403 return(0);
1406 return(1);
1410 /*----------------------------------------------------------------------
1411 Business end of displaying and operating on a collection of folders
1413 Args: ps -- The pine_state data structure
1414 fs -- Folder screen state structure
1416 Result: A string list containing folders selected,
1417 NULL on Cancel, Error or other problem
1419 ----*/
1420 STRLIST_S *
1421 folder_lister(struct pine *ps, FSTATE_S *fs)
1423 int fltrv, we_cancel = 0;
1424 int first_time_through = 1;
1425 HANDLE_S *handles = NULL;
1426 STORE_S *screen_text = NULL;
1427 FPROC_S folder_proc_data;
1428 gf_io_t pc;
1430 dprint((1, "\n\n ---- FOLDER LISTER ----\n"));
1432 memset(&folder_proc_data, 0, sizeof(FPROC_S));
1433 folder_proc_data.fs = fs;
1435 while(!folder_proc_data.done){
1436 if((screen_text = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
1437 gf_set_so_writec(&pc, screen_text);
1439 else{
1440 q_status_message(SM_ORDER | SM_DING, 3, 3,
1441 "Formatting Error: Can't create space for list");
1442 return(NULL);
1445 we_cancel = busy_cue(_("Fetching folder data"), NULL, 1);
1446 fltrv = folder_list_text(ps, &folder_proc_data, pc, &handles,
1447 ps->ttyo->screen_cols);
1448 if(we_cancel)
1449 cancel_busy_cue(-1);
1451 if(fltrv){
1453 SCROLL_S sargs;
1454 struct key_menu km;
1455 struct key keys[36];
1457 memset(&sargs, 0, sizeof(SCROLL_S));
1458 sargs.text.text = so_text(screen_text);
1459 sargs.text.src = CharStar;
1460 sargs.text.desc = "folder list";
1461 if((sargs.text.handles = folder_list_handle(fs, handles)) != NULL)
1462 sargs.start.on = Handle;
1464 sargs.bar.title = fs->f.title.bar;
1465 sargs.bar.style = fs->f.title.style;
1467 sargs.proc.tool = folder_processor;
1468 sargs.proc.data.p = (void *) &folder_proc_data;
1470 sargs.quell_first_view = first_time_through ? 0 : 1;
1471 first_time_through = 0;
1473 sargs.resize_exit = 1;
1474 sargs.vert_handle = 1;
1475 sargs.srch_handle = 1;
1477 sargs.help.text = fs->f.help.text;
1478 sargs.help.title = fs->f.help.title;
1480 sargs.keys.menu = &km;
1481 km = *fs->km;
1482 km.keys = keys;
1483 memcpy(&keys[0], fs->km->keys,
1484 (km.how_many * 12) * sizeof(struct key));
1485 setbitmap(sargs.keys.bitmap);
1487 if(fs->km == &folder_km){
1488 sargs.keys.each_cmd = folder_lister_km_manager;
1489 #ifdef _WINDOWS
1490 sargs.mouse.popup = folder_list_popup;
1491 #endif
1493 else{
1494 #ifdef _WINDOWS
1495 sargs.mouse.popup = folder_list_select_popup;
1496 #endif
1497 if(fs->km == &folder_sel_km || fs->km == &folder_sela_km)
1498 sargs.keys.each_cmd = folder_lister_km_sel_manager;
1499 else if(fs->km == &folder_sub_km)
1500 sargs.keys.each_cmd = folder_lister_km_sub_manager;
1503 sargs.mouse.clickclick = folder_lister_clickclick;
1505 switch(scrolltool(&sargs)){
1506 case MC_MAIN : /* just leave */
1507 folder_proc_data.done = 1;
1508 break;
1510 case MC_RESIZE : /* loop around rebuilding screen */
1511 if(sargs.text.handles){
1512 FOLDER_S *fp;
1514 fp = folder_entry(sargs.text.handles->h.f.index,
1515 FOLDERS(sargs.text.handles->h.f.context));
1516 if(fp && strlen(FLDR_NAME(fp)) < MAXFOLDER -1){
1517 strncpy(fs->first_folder, FLDR_NAME(fp), MAXFOLDER);
1518 fs->first_folder[MAXFOLDER-1] = '\0';
1521 fs->context = sargs.text.handles->h.f.context;
1524 break;
1526 /*--------- EXIT menu -----------*/
1527 case MC_EXIT :
1528 case MC_EXITQUERY :
1529 fs->list_cntxt = NULL;
1530 folder_proc_data.done = folder_proc_data.all_done = 1;
1531 break;
1533 default :
1534 break;
1537 ps->noticed_change_in_unseen = 0;
1540 if(F_ON(F_BLANK_KEYMENU,ps))
1541 FOOTER_ROWS(ps) = 1;
1543 gf_clear_so_writec(screen_text);
1544 so_give(&screen_text);
1545 free_handles(&handles);
1547 else
1548 folder_proc_data.done = 1;
1551 reset_context_folders(fs->context);
1553 if(folder_proc_data.all_done)
1554 fs->context = NULL;
1556 if(fs->cache_streamp && *fs->cache_streamp){
1557 int i;
1560 * check stream pool to see if currently cached
1561 * stream went away
1563 for(i = 0; i < ps->s_pool.nstream; i++)
1564 if(ps->s_pool.streams[i] == *fs->cache_streamp)
1565 break;
1566 if(i == ps->s_pool.nstream)
1567 *fs->cache_streamp = NULL;
1570 return(folder_proc_data.rv);
1575 * folder_list_text - format collection's contents for display
1578 folder_list_text(struct pine *ps, FPROC_S *fp, gf_io_t pc, HANDLE_S **handlesp, int cols)
1580 int rv = 1, i, j, ftotal, fcount, slot_width, slot_rows,
1581 slot_cols, index, findex, width, shown, selected;
1582 CONTEXT_S *c_list;
1583 char lbuf[6*MAX_SCREEN_COLS+1];
1585 /* disarm this trigger that gets us out of scrolltool */
1586 ps->noticed_change_in_unseen = 0;
1588 if(handlesp)
1589 init_handles(handlesp);
1591 c_list = fp->fs->context;
1592 if(fp->fs->combined_view
1593 && (F_ON(F_CMBND_SUBDIR_DISP, ps_global) || !c_list->dir->prev))
1594 while(c_list->prev) /* rewind to start */
1595 c_list = c_list->prev;
1598 ps->user_says_cancel = 0;
1600 /* If we're displaying folders, fetch the list */
1601 if((shown = (c_list == fp->fs->context
1602 || (c_list->dir->status & CNTXT_NOFIND) == 0
1603 || F_ON(F_EXPANDED_FOLDERS, ps_global))) != 0){
1605 * if select is allowed, flag context so any that are
1606 * are remembered even after the list is destroyed
1608 if(fp->fs->agg_ops)
1609 c_list->use |= CNTXT_PRESRV;
1611 /* Make sure folder list filled in */
1612 refresh_folder_list(c_list, fp->fs->no_dirs, FALSE, fp->fs->cache_streamp);
1615 /* Insert any introductory text here */
1616 if(c_list->next
1617 || c_list->prev
1618 || c_list->dir->prev
1619 || fp->fs->force_intro){
1621 /* Leading horizontal line? */
1622 if(fp->fs->combined_view
1623 && (F_ON(F_CMBND_SUBDIR_DISP,ps_global)
1624 || !c_list->dir->prev)){
1625 if(c_list->prev)
1626 gf_puts("\n", pc); /* blank line */
1628 color_write_for_folder(pc, CLR_FLDRLT);
1629 gf_puts(repeat_char(cols, '-'), pc);
1630 gf_puts("\n", pc);
1631 color_write_for_folder(pc, CLR_NORMAL);
1634 /* nickname or description */
1635 if(F_ON(F_CMBND_FOLDER_DISP, ps_global)
1636 && (!c_list->dir->prev
1637 || F_ON(F_CMBND_SUBDIR_DISP, ps_global))){
1638 char buf[6*MAX_SCREEN_COLS + 1];
1640 color_write_for_folder(pc, CLR_FLDRLT);
1641 if(cols < 40){
1642 snprintf(buf, sizeof(buf), "%.*s", cols,
1643 strsquish(tmp_20k_buf, SIZEOF_20KBUF,
1644 (c_list->nickname)
1645 ? c_list->nickname
1646 : (c_list->label ? c_list->label : ""),
1647 cols));
1648 gf_puts(folder_list_center_space(buf, cols), pc);
1650 else{
1651 int wid;
1653 snprintf(buf, sizeof(buf), "%s-Collection <",
1654 NEWS_TEST(c_list) ? "News" : "Folder");
1655 wid = utf8_width(buf)+1;
1656 wid = MAX(0, cols-wid);
1657 snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%.*s>", wid,
1658 strsquish(tmp_20k_buf, SIZEOF_20KBUF,
1659 (c_list->nickname)
1660 ? c_list->nickname
1661 : (c_list->label ? c_list->label : ""),
1662 wid));
1665 gf_puts(buf, pc);
1666 gf_puts("\n", pc);
1667 color_write_for_folder(pc, CLR_NORMAL);
1669 else if(c_list->label){
1670 if(utf8_width(c_list->label) > ps_global->ttyo->screen_cols)
1671 utf8_pad_to_width(lbuf, c_list->label, sizeof(lbuf), ps_global->ttyo->screen_cols, 1);
1672 else
1673 strncpy(lbuf, c_list->label, sizeof(lbuf));
1675 lbuf[sizeof(lbuf)-1] = '\0';
1677 color_write_for_folder(pc, CLR_FLDRLT);
1678 gf_puts(folder_list_center_space(lbuf, cols), pc);
1679 gf_puts(lbuf, pc);
1680 gf_puts("\n", pc);
1681 color_write_for_folder(pc, CLR_NORMAL);
1684 if(c_list->comment){
1685 if(utf8_width(c_list->comment) > ps_global->ttyo->screen_cols)
1686 utf8_pad_to_width(lbuf, c_list->comment, sizeof(lbuf), ps_global->ttyo->screen_cols, 1);
1687 else
1688 strncpy(lbuf, c_list->comment, sizeof(lbuf));
1690 lbuf[sizeof(lbuf)-1] = '\0';
1692 color_write_for_folder(pc, CLR_FLDRLT);
1693 gf_puts(folder_list_center_space(lbuf, cols), pc);
1694 gf_puts(lbuf, pc);
1695 gf_puts("\n", pc);
1696 color_write_for_folder(pc, CLR_NORMAL);
1699 if(c_list->dir->desc){
1700 char buf[6*MAX_SCREEN_COLS + 1];
1702 strncpy(buf, strsquish(tmp_20k_buf,SIZEOF_20KBUF,c_list->dir->desc,cols),
1703 sizeof(buf)-1);
1704 buf[sizeof(buf)-1] = '\0';
1705 color_write_for_folder(pc, CLR_FLDRLT);
1706 gf_puts(folder_list_center_space(buf, cols), pc);
1707 gf_puts(buf, pc);
1708 gf_puts("\n", pc);
1709 color_write_for_folder(pc, CLR_NORMAL);
1712 if(c_list->use & CNTXT_ZOOM){
1713 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "[ ZOOMED on %d (of %d) %ss ]",
1714 selected_folders(c_list),
1715 folder_total(FOLDERS(c_list)),
1716 (c_list->use & CNTXT_NEWS) ? "Newsgroup" : "Folder");
1717 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1719 if(utf8_width(tmp_20k_buf) > ps_global->ttyo->screen_cols)
1720 utf8_pad_to_width(lbuf, tmp_20k_buf, sizeof(lbuf), ps_global->ttyo->screen_cols, 1);
1721 else
1722 strncpy(lbuf, tmp_20k_buf, sizeof(lbuf));
1724 lbuf[sizeof(lbuf)-1] = '\0';
1726 color_write_for_folder(pc, CLR_FLDRLT);
1727 gf_puts(folder_list_center_space(lbuf, cols), pc);
1728 gf_puts(lbuf, pc);
1729 gf_puts("\n", pc);
1730 color_write_for_folder(pc, CLR_NORMAL);
1733 color_write_for_folder(pc, CLR_FLDRLT);
1734 gf_puts(repeat_char(cols, '-'), pc);
1735 gf_puts("\n\n", pc);
1736 color_write_for_folder(pc, CLR_NORMAL);
1739 if(shown && LUU_YES(c_list->update)){
1740 /* Run thru list formatting as necessary */
1741 if((ftotal = folder_total(FOLDERS(c_list))) != 0){
1742 int use_color;
1743 /* If previously selected, mark members of new list */
1744 selected = selected_folders(c_list);
1746 /* How many screen cells per cell for each folder name? */
1747 slot_width = 1;
1748 for(fcount = i = 0; i < ftotal; i++){
1749 FOLDER_S *f = folder_entry(i, FOLDERS(c_list));
1750 unsigned char *fname;
1752 ps->user_says_cancel = 0;
1754 if((c_list->use & CNTXT_ZOOM) && !f->selected)
1755 continue;
1757 fcount++;
1759 use_color = use_color_for_folder(f);
1760 fname = folder_name_decoded((unsigned char *)FLDR_NAME(f));
1762 width = utf8_width(fname ? (char *)fname : FLDR_NAME(f));
1764 if(fname)
1765 fs_give((void **)&fname);
1767 if(f->isdir)
1768 width += (f->isfolder) ? (use_color ? 1 : 3) : (use_color ? 0 : 1);
1770 if(NEWS_TEST(c_list) && (c_list->use & CNTXT_FINDALL))
1771 /* assume listmode so we don't have to reformat */
1772 /* if listmode is actually entered */
1773 width += 4;
1775 if(selected){
1776 if(F_OFF(F_SELECTED_SHOWN_BOLD, ps_global))
1777 width += 4; /* " X " */
1779 else if(c_list == fp->fs->list_cntxt)
1780 width += 4; /* "[X] " */
1782 if(fp->fs->include_unseen_cnt
1783 && c_list->use & CNTXT_INCMNG
1784 && F_ON(F_ENABLE_INCOMING_CHECKING, ps_global)
1785 && ps_global->VAR_INCOMING_FOLDERS
1786 && ps_global->VAR_INCOMING_FOLDERS[0]){
1788 update_folder_unseen(f, c_list, UFU_ANNOUNCE, NULL);
1789 if(F_ON(F_INCOMING_CHECKING_RECENT, ps_global)
1790 && f->unseen_valid
1791 && (f->new > 0L
1792 || F_ON(F_INCOMING_CHECKING_TOTAL, ps_global))){
1793 width += (strlen(tose(f->new)) + 3);
1794 if(F_ON(F_INCOMING_CHECKING_TOTAL, ps_global))
1795 width += (strlen(tose(f->total)) + 1);
1797 else if(F_OFF(F_INCOMING_CHECKING_RECENT, ps_global)
1798 && f->unseen_valid
1799 && (f->unseen > 0L
1800 || F_ON(F_INCOMING_CHECKING_TOTAL, ps_global))){
1801 width += (strlen(tose(f->unseen)) + 3);
1802 if(F_ON(F_INCOMING_CHECKING_TOTAL, ps_global))
1803 width += (strlen(tose(f->total)) + 1);
1805 else if(!f->unseen_valid && f->last_unseen_update != LUU_NEVERCHK)
1806 width += 4; /* " (?)" */
1809 if(slot_width < width)
1810 slot_width = width;
1813 if(F_ON(F_SINGLE_FOLDER_LIST, ps_global)){
1814 slot_cols = 1;
1815 slot_rows = fcount;
1817 else{
1818 /* fit as many columns as possible */
1819 slot_cols = 1;
1820 while(((slot_cols+1) * slot_width) + slot_cols <= cols)
1821 slot_cols++;
1823 switch(slot_cols){
1824 case 0 :
1825 slot_cols = 1;
1826 /* fall through */
1828 case 1 :
1829 slot_rows = fcount;
1830 break;
1832 default :
1834 * Make the slot_width as large as possible.
1835 * Slot_width is the width of the column, not counting
1836 * the space between columns.
1838 while((slot_cols * (slot_width+1)) + slot_cols-1 <= cols)
1839 slot_width++;
1841 slot_rows = (fcount / slot_cols)
1842 + ((fcount % slot_cols) ? 1 : 0);
1843 break;
1847 for(i = index = 0; i < slot_rows; i++){
1848 if(i)
1849 gf_puts("\n", pc);
1851 for(j = width = 0; j < slot_cols; j++, index++){
1852 if(width){
1853 gf_puts(repeat_char(slot_width + 1 - width, ' '), pc);
1854 width = 0;
1857 if(F_ON(F_VERTICAL_FOLDER_LIST, ps_global))
1858 index = i + (j * slot_rows);
1860 findex = index;
1862 if(c_list->use & CNTXT_ZOOM)
1863 findex = folder_list_ith(index, c_list);
1865 if(findex < ftotal){
1866 int flags = (handlesp) ? FLW_LUNK : FLW_NONE;
1868 if(c_list == fp->fs->list_cntxt)
1869 flags |= FLW_LIST;
1870 else if(selected)
1871 flags |= FLW_SLCT;
1872 else if(F_ON(F_SINGLE_FOLDER_LIST, ps_global)
1873 || ((c_list->use & CNTXT_FINDALL)
1874 && NEWS_TEST(c_list)))
1875 gf_puts(" ", pc);
1877 if(fp->fs->include_unseen_cnt
1878 && c_list->use & CNTXT_INCMNG
1879 && F_ON(F_ENABLE_INCOMING_CHECKING, ps_global)
1880 && ps_global->VAR_INCOMING_FOLDERS
1881 && ps_global->VAR_INCOMING_FOLDERS[0])
1882 flags |= FLW_UNSEEN;
1884 width = folder_list_write(pc, handlesp, c_list,
1885 findex, NULL, flags);
1890 else if(fp->fs->combined_view
1891 && (F_ON(F_CMBND_SUBDIR_DISP, ps_global)
1892 || !c_list->dir->prev)){
1893 char *emptiness = N_("[No Folders in Collection]");
1895 if(utf8_width(_(emptiness)) > ps_global->ttyo->screen_cols)
1896 utf8_pad_to_width(lbuf, _(emptiness), sizeof(lbuf), ps_global->ttyo->screen_cols, 1);
1897 else
1898 strncpy(lbuf, _(emptiness), sizeof(lbuf));
1900 lbuf[sizeof(lbuf)-1] = '\0';
1902 color_write_for_folder(pc, CLR_FLDRLT);
1903 gf_puts(folder_list_center_space(lbuf, cols), pc);
1904 (void) folder_list_write(pc, handlesp, c_list, -1, lbuf,
1905 (handlesp) ? FLW_LUNK : FLW_NONE);
1906 color_write_for_folder(pc, CLR_NORMAL);
1909 else if(fp->fs->combined_view
1910 && (F_ON(F_CMBND_SUBDIR_DISP, ps_global)
1911 || !c_list->dir->prev)){
1912 char *unexpanded = N_("[Select Here to See Expanded List]");
1914 if(utf8_width(_(unexpanded)) > ps_global->ttyo->screen_cols)
1915 utf8_pad_to_width(lbuf, _(unexpanded), sizeof(lbuf), ps_global->ttyo->screen_cols, 1);
1916 else
1917 strncpy(lbuf, _(unexpanded), sizeof(lbuf));
1919 lbuf[sizeof(lbuf)-1] = '\0';
1921 color_write_for_folder(pc, CLR_FLDRLT);
1922 gf_puts(folder_list_center_space(lbuf, cols), pc);
1923 (void) folder_list_write(pc, handlesp, c_list, -1, lbuf,
1924 (handlesp) ? FLW_LUNK : FLW_NONE);
1925 color_write_for_folder(pc, CLR_NORMAL);
1928 gf_puts("\n", pc); /* blank line */
1931 while(fp->fs->combined_view
1932 && (F_ON(F_CMBND_SUBDIR_DISP, ps_global) || !c_list->dir->prev)
1933 && (c_list = c_list->next));
1935 return(rv);
1940 folder_list_write(gf_io_t pc, HANDLE_S **handlesp, CONTEXT_S *ctxt, int fnum, char *alt_name, int flags)
1942 char buf[256];
1943 int width = 0, lprefix = 0, lmiddle = 0, lsuffix = 0;
1944 FOLDER_S *fp;
1945 HANDLE_S *h1 = NULL, *h2 = NULL;
1947 if(flags & FLW_LUNK){
1948 h1 = new_handle(handlesp);
1949 h1->type = Folder;
1950 h1->h.f.index = fnum;
1951 h1->h.f.context = ctxt;
1952 h1->force_display = 1;
1954 snprintf(buf, sizeof(buf), "%d", h1->key);
1955 buf[sizeof(buf)-1] = '\0';
1958 fp = (fnum < 0) ? NULL : folder_entry(fnum, FOLDERS(ctxt));
1960 if(flags & FLW_LUNK && h1 && fp && fp->isdir && fp->isfolder){
1961 h2 = new_handle(handlesp);
1962 h2->type = Folder;
1963 h2->h.f.index = fnum;
1964 h2->h.f.context = ctxt;
1965 h2->force_display = 1;
1967 h1->is_dual_do_open = 1;
1970 if(h1){
1971 /* color unseen? */
1972 if(color_monitored(fp, flags, CLR_UNSEEN)){
1973 h1->color_unseen = 1;
1974 if(h2)
1975 h2->color_unseen = 1;
1977 /* color folder? */
1978 if(color_monitored(fp, flags, CLR_FOLDER)){
1979 h1->color_folder = 1;
1980 if(h2)
1981 h2->color_folder = 1;
1985 /* embed handle pointer */
1986 if(((h1 && h1->color_unseen) ? color_write_for_folder(pc, CLR_UNSEEN)
1987 : ((h1 && h1->color_folder)
1988 ? color_write_for_folder(pc, fp->isfolder ? CLR_FOLDER : CLR_DIRECT)
1989 : 1))
1990 && (h1 ? ((*pc)(TAG_EMBED) && (*pc)(TAG_HANDLE)
1991 && (*pc)(strlen(buf)) && gf_puts(buf, pc)) : 1)
1992 && (fp ? ((lprefix = folder_list_write_prefix(fp, flags, pc)) >= 0
1993 && (lmiddle = folder_list_write_middle(fp, ctxt, pc, h2)) >= 0
1994 && ((lsuffix = folder_list_write_suffix(fp, flags, pc)) >= 0))
1995 : (alt_name ? gf_puts(alt_name, pc) : 0))
1996 && (h1 ? ((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF)
1997 && (*pc)(TAG_EMBED) && (*pc)(TAG_INVOFF)) : 1)
1998 && ((h1 && (h1->color_unseen || h1->color_folder)) ?
1999 color_write_for_folder(pc, CLR_NORMAL): 1)){
2000 if(fp)
2001 width = lprefix + lmiddle + lsuffix;
2002 else if(alt_name)
2003 width = utf8_width(alt_name);
2006 return(width);
2011 folder_list_write_prefix(FOLDER_S *f, int flags, gf_io_t pc)
2013 int rv = 0;
2015 if(flags & FLW_SLCT){
2016 if(F_OFF(F_SELECTED_SHOWN_BOLD, ps_global) || !(flags & FLW_LUNK)){
2017 rv = 4;
2018 if(f->selected){
2019 gf_puts(" X ", pc);
2021 else{
2022 gf_puts(" ", pc);
2025 else
2026 rv = ((*pc)(TAG_EMBED)
2027 && (*pc)((f->selected) ? TAG_BOLDON : TAG_BOLDOFF)) ? 0 : -1;
2029 else if(flags & FLW_LIST){
2030 rv = 4;
2031 /* screen width of "SUB " is 4 */
2032 gf_puts(f->subscribed ? "SUB " : (f->selected ? "[X] " : "[ ] "), pc);
2035 return(rv);
2039 folder_list_write_middle(FOLDER_S *fp, CONTEXT_S *ctxt, gf_io_t pc, HANDLE_S *h2)
2041 int rv = -1, use_color;
2042 char buf[256];
2043 unsigned char *fname;
2045 if(h2){
2046 snprintf(buf, sizeof(buf), "%d", h2->key);
2047 buf[sizeof(buf)-1] = '\0';
2050 if(!fp)
2051 return(rv);
2053 fname = folder_name_decoded((unsigned char *)FLDR_NAME(fp));
2054 use_color = use_color_for_folder(fp);
2056 if(gf_puts(fname ? (char *)fname : FLDR_NAME(fp), pc)
2057 && (h2 ? ((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF) /* tie off handle 1 */
2058 && (*pc)(TAG_EMBED) && (*pc)(TAG_INVOFF)) : 1)
2059 && ((use_color && fp->isdir && fp->isfolder)
2060 ? color_write_for_folder(pc, CLR_DIRECT) : 1)
2061 && (h2 ? ((*pc)(TAG_EMBED) && (*pc)(TAG_HANDLE) /* start handle 2 */
2062 && (*pc)(strlen(buf)) && gf_puts(buf, pc)) : 1)
2063 && use_color
2064 ? (fp->isdir && fp->isfolder ? (*pc)(ctxt->dir->delim) : 1)
2065 : (((fp->isdir && fp->isfolder) ? (*pc)('[') : 1)
2066 && ((fp->isdir) ? (*pc)(ctxt->dir->delim) : 1)
2067 && ((fp->isdir && fp->isfolder) ? (*pc)(']') : 1))){
2068 rv = utf8_width(fname ? (char *)fname : FLDR_NAME(fp));
2069 if(fp->isdir)
2070 rv += (fp->isfolder) ? (use_color ? 1 : 3) : (use_color ? 0 : 1);
2073 if(fname)
2074 fs_give((void **)&fname);
2076 return(rv);
2081 folder_list_write_suffix(FOLDER_S *f, int flags, gf_io_t pc)
2083 int rv = 0;
2085 if(flags & FLW_UNSEEN){
2086 char buf[100];
2088 buf[0] = '\0';
2089 if(F_ON(F_INCOMING_CHECKING_RECENT, ps_global)
2090 && f->unseen_valid
2091 && (f->new > 0L
2092 || (F_ON(F_INCOMING_CHECKING_TOTAL, ps_global)
2093 && f->total > 0L))){
2094 snprintf(buf, sizeof(buf), " (%s%s%s)",
2095 tose(f->new),
2096 F_ON(F_INCOMING_CHECKING_TOTAL, ps_global) ? "/" : "",
2097 F_ON(F_INCOMING_CHECKING_TOTAL, ps_global) ? tose(f->total) : "");
2099 else if(F_OFF(F_INCOMING_CHECKING_RECENT, ps_global)
2100 && f->unseen_valid
2101 && (f->unseen > 0L
2102 || (F_ON(F_INCOMING_CHECKING_TOTAL, ps_global)
2103 && f->total > 0L))){
2104 snprintf(buf, sizeof(buf), " (%s%s%s)",
2105 tose(f->unseen),
2106 F_ON(F_INCOMING_CHECKING_TOTAL, ps_global) ? "/" : "",
2107 F_ON(F_INCOMING_CHECKING_TOTAL, ps_global) ? tose(f->total) : "");
2109 else if(!f->unseen_valid && f->last_unseen_update != LUU_NEVERCHK){
2110 snprintf(buf, sizeof(buf), " (?)");
2113 rv = strlen(buf);
2114 if(rv)
2115 gf_puts(buf, pc);
2118 return(rv);
2122 color_write_for_folder(gf_io_t pc, int testtype)
2124 int rv;
2125 if(!pico_usingcolor())
2126 return 1;
2127 switch (testtype){
2128 case CLR_UNSEEN:
2129 rv = gf_puts(color_embed(ps_global->VAR_INCUNSEEN_FORE_COLOR,
2130 ps_global->VAR_INCUNSEEN_BACK_COLOR), pc);
2131 break;
2132 case CLR_FOLDER:
2133 rv = gf_puts(color_embed(ps_global->VAR_FOLDER_FORE_COLOR,
2134 ps_global->VAR_FOLDER_BACK_COLOR ), pc);
2135 break;
2136 case CLR_FLDRLT:
2137 rv = gf_puts(color_embed(ps_global->VAR_FOLDER_LIST_FORE_COLOR,
2138 ps_global->VAR_FOLDER_LIST_BACK_COLOR ), pc);
2139 break;
2140 case CLR_DIRECT:
2141 rv = gf_puts(color_embed(ps_global->VAR_DIRECTORY_FORE_COLOR,
2142 ps_global->VAR_DIRECTORY_BACK_COLOR), pc);
2143 break;
2144 case CLR_NORMAL:
2145 rv = gf_puts(color_embed(ps_global->VAR_NORM_FORE_COLOR,
2146 ps_global->VAR_NORM_BACK_COLOR), pc);
2147 break;
2148 default:
2149 rv = 0; /* fail */
2150 break;
2152 return rv;
2157 color_test_for_folder(char *color_fore, char *color_back)
2159 return pico_usingcolor()
2160 && pico_is_good_color(color_fore)
2161 && pico_is_good_color(color_back)
2162 && (colorcmp(color_fore, ps_global->VAR_NORM_FORE_COLOR)
2163 || colorcmp(color_back,
2164 ps_global->VAR_NORM_BACK_COLOR)) ? 1 : 0;
2170 use_color_for_folder(FOLDER_S *fp)
2172 int test1, test2;
2173 if(fp->isdir)
2174 test1 = color_test_for_folder(ps_global->VAR_DIRECTORY_FORE_COLOR,
2175 ps_global->VAR_DIRECTORY_BACK_COLOR);
2176 if(fp->isfolder)
2177 test2 = color_test_for_folder(ps_global->VAR_FOLDER_FORE_COLOR,
2178 ps_global->VAR_FOLDER_BACK_COLOR);
2179 return (fp->isdir && fp->isfolder) ? test1 + test2
2180 : (fp->isdir ? test1 : (fp->isfolder? test2 : 0));
2185 color_monitored(FOLDER_S *f, int flags, int testtype)
2187 int rv;
2188 switch(testtype){
2189 case CLR_UNSEEN: rv = (flags & FLW_UNSEEN) && f && f->unseen_valid
2190 && ((F_ON(F_INCOMING_CHECKING_RECENT, ps_global) && f->new > 0L)
2191 || (F_OFF(F_INCOMING_CHECKING_RECENT, ps_global) && f->unseen > 0L))
2192 && color_test_for_folder(ps_global->VAR_INCUNSEEN_FORE_COLOR,
2193 ps_global->VAR_INCUNSEEN_BACK_COLOR) ? 1 : 0;
2194 break;
2195 case CLR_FOLDER: rv = f ? use_color_for_folder(f) : 0;
2196 break;
2197 default: rv = 0;
2199 return rv;
2204 folder_list_ith(int n, CONTEXT_S *cntxt)
2206 int index, ftotal;
2207 FOLDER_S *f;
2209 for(index = 0, ftotal = folder_total(FOLDERS(cntxt));
2210 index < ftotal
2211 && (f = folder_entry(index, FOLDERS(cntxt)))
2212 && !(f->selected && !n--);
2213 index++)
2216 return(index);
2220 char *
2221 folder_list_center_space(char *s, int width)
2223 int l;
2225 return(((l = utf8_width(s)) < width) ? repeat_char((width - l)/2, ' ') : "");
2230 * folder_list_handle - return pointer in handle list
2231 * corresponding to "start"
2233 HANDLE_S *
2234 folder_list_handle(FSTATE_S *fs, HANDLE_S *handles)
2236 char *p, *name = NULL;
2237 HANDLE_S *h, *h_found = NULL;
2238 FOLDER_S *fp;
2240 if(handles && fs->context){
2241 if(!(NEWS_TEST(fs->context) || (fs->context->use & CNTXT_INCMNG))
2242 && fs->context->dir->delim)
2243 for(p = strchr(fs->first_folder, fs->context->dir->delim);
2245 p = strchr(p, fs->context->dir->delim))
2246 name = ++p;
2248 for(h = handles; h; h = h->next)
2249 if(h->h.f.context == fs->context){
2250 if(!h_found) /* match at least given context */
2251 h_found = h;
2253 if(!fs->first_folder[0]
2254 || ((fp = folder_entry(h->h.f.index, FOLDERS(h->h.f.context)))
2255 && ((fs->first_dir && fp->isdir)
2256 || (!fs->first_dir && fp->isfolder))
2257 && ((fp->nickname && !strcmp(name ? name : fs->first_folder, fp->nickname))
2258 || (fp->name && !strcmp(name ? name : fs->first_folder, fp->name))))){
2259 h_found = h;
2260 break;
2264 fs->first_folder[0] = '\0';
2267 return(h_found ? h_found : handles);
2272 folder_processor(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
2274 int rv = 0;
2275 char tmp_output[MAILTMPLEN];
2277 switch(cmd){
2278 case MC_FINISH :
2279 FPROC(sparms)->done = rv = 1;;
2280 break;
2282 /*---------- Select or enter a View ----------*/
2283 case MC_CHOICE :
2284 rv = folder_lister_choice(sparms);
2285 break;
2287 /*--------- Hidden "To Fldrs" command -----------*/
2288 case MC_LISTMODE :
2289 if(sparms->text.handles
2290 && folder_total(FOLDERS(sparms->text.handles->h.f.context))){
2291 if(!FPROC(sparms)->fs->list_cntxt){
2292 FPROC(sparms)->fs->list_cntxt
2293 = sparms->text.handles->h.f.context;
2294 rv = scroll_add_listmode(sparms->text.handles->h.f.context,
2295 folder_total(FOLDERS(sparms->text.handles->h.f.context)));
2296 if(!rv){
2297 /* need to set the subscribe key ourselves */
2298 sparms->keys.menu->keys[SB_SUB_KEY].name = "S";
2299 sparms->keys.menu->keys[SB_SUB_KEY].label = N_("Subscribe");
2300 sparms->keys.menu->keys[SB_SUB_KEY].bind.cmd = MC_CHOICE;
2301 sparms->keys.menu->keys[SB_SUB_KEY].bind.ch[0] = 's';
2302 setbitn(SB_SUB_KEY, sparms->keys.bitmap);
2303 ps_global->mangled_screen = 1;
2305 sparms->keys.menu->keys[SB_EXIT_KEY].bind.cmd = MC_EXITQUERY;
2307 q_status_message(SM_ORDER, 0, 1, LISTMODE_GRIPE);
2309 else
2310 q_status_message(SM_ORDER, 0, 4, _("Already in List Mode"));
2312 else
2313 q_status_message(SM_ORDER, 0, 4,
2314 _("No Folders! Can't enter List Mode"));
2316 break;
2319 /*--------- Visit parent directory -----------*/
2320 case MC_PARENT :
2321 if(folder_lister_parent(FPROC(sparms)->fs,
2322 (sparms->text.handles)
2323 ? sparms->text.handles->h.f.context
2324 : FPROC(sparms)->fs->context,
2325 (sparms->text.handles)
2326 ? sparms->text.handles->h.f.index : -1, 0))
2327 rv = 1; /* leave scrolltool to rebuild screen */
2329 break;
2332 /*--------- Open the selected folder -----------*/
2333 case MC_OPENFLDR :
2334 if(sparms->text.handles
2335 && folder_total(FOLDERS(sparms->text.handles->h.f.context)))
2336 rv = folder_lister_finish(sparms, sparms->text.handles->h.f.context,
2337 sparms->text.handles->h.f.index);
2338 else
2339 q_status_message(SM_ORDER, 0, 4,
2340 _("No Folders! Nothing to View"));
2342 break;
2345 /*--------- Export the selected folder -----------*/
2346 case MC_EXPORT :
2347 folder_export(sparms);
2348 break;
2351 /*--------- Import the selected folder -----------*/
2352 case MC_IMPORT :
2354 CONTEXT_S *cntxt = (sparms->text.handles)
2355 ? sparms->text.handles->h.f.context
2356 : FPROC(sparms)->fs->context;
2357 char new_file[2*MAXFOLDER+10];
2358 int r;
2360 new_file[0] = '\0';
2362 r = folder_import(sparms, new_file, sizeof(new_file));
2364 if(r && (cntxt->use & CNTXT_INCMNG || context_isambig(new_file))){
2365 rv = 1; /* rebuild display! */
2366 FPROC(sparms)->fs->context = cntxt;
2367 if(strlen(new_file) < MAXFOLDER - 1){
2368 strncpy(FPROC(sparms)->fs->first_folder, new_file, MAXFOLDER);
2369 FPROC(sparms)->fs->first_folder[MAXFOLDER-1] = '\0';
2372 else
2373 ps_global->mangled_footer++;
2376 break;
2379 /*--------- Return to the Collections Screen -----------*/
2380 case MC_COLLECTIONS :
2381 FPROC(sparms)->done = rv = 1;
2382 break;
2385 /*--------- QUIT pine -----------*/
2386 case MC_QUIT :
2387 ps_global->next_screen = quit_screen;
2388 FPROC(sparms)->done = rv = 1;
2389 break;
2392 /*--------- Compose -----------*/
2393 case MC_COMPOSE :
2394 ps_global->next_screen = compose_screen;
2395 FPROC(sparms)->done = rv = 1;
2396 break;
2399 /*--------- Alt Compose -----------*/
2400 case MC_ROLE :
2401 ps_global->next_screen = alt_compose_screen;
2402 FPROC(sparms)->done = rv = 1;
2403 break;
2406 /*--------- Message Index -----------*/
2407 case MC_INDEX :
2408 if(THREADING()
2409 && sp_viewing_a_thread(ps_global->mail_stream)
2410 && unview_thread(ps_global, ps_global->mail_stream, ps_global->msgmap)){
2411 ps_global->next_screen = mail_index_screen;
2412 ps_global->view_skipped_index = 0;
2413 ps_global->mangled_screen = 1;
2416 ps_global->next_screen = mail_index_screen;
2417 FPROC(sparms)->done = rv = 1;
2418 break;
2421 /*----------------- Add a new folder name -----------*/
2422 case MC_ADDFLDR :
2424 CONTEXT_S *cntxt = (sparms->text.handles)
2425 ? sparms->text.handles->h.f.context
2426 : FPROC(sparms)->fs->context;
2427 char new_file[2*MAXFOLDER+10];
2428 int r;
2430 if(NEWS_TEST(cntxt))
2431 r = group_subscription(new_file, sizeof(new_file), cntxt);
2432 else{
2433 r = add_new_folder(cntxt, Main, V_INCOMING_FOLDERS, new_file,
2434 sizeof(new_file),
2435 FPROC(sparms)->fs->cache_streamp
2436 ? *FPROC(sparms)->fs->cache_streamp : NULL,
2437 NULL);
2438 if(ps_global->prc && ps_global->prc->outstanding_pinerc_changes)
2439 write_pinerc(ps_global, Main, WRP_NONE);
2442 if(r && (cntxt->use & CNTXT_INCMNG || context_isambig(new_file))){
2443 rv = 1; /* rebuild display! */
2444 FPROC(sparms)->fs->context = cntxt;
2445 if(strlen(new_file) < MAXFOLDER - 1){
2446 strncpy(FPROC(sparms)->fs->first_folder, new_file, MAXFOLDER);
2447 FPROC(sparms)->fs->first_folder[MAXFOLDER-1] = '\0';
2450 else
2451 ps_global->mangled_footer++;
2454 break;
2457 /*------ Type in new folder name, e.g., for save ----*/
2458 case MC_ADD :
2459 rv = folder_lister_addmanually(sparms);
2460 break;
2463 /*--------------- Rename folder ----------------*/
2464 case MC_RENAMEFLDR :
2465 if(sparms->text.handles
2466 && folder_total(FOLDERS(sparms->text.handles->h.f.context))){
2467 char new_file[MAXFOLDER+1];
2468 int r;
2470 r = rename_folder(sparms->text.handles->h.f.context,
2471 sparms->text.handles->h.f.index, new_file,
2472 sizeof(new_file),
2473 FPROC(sparms)->fs->cache_streamp
2474 ? *FPROC(sparms)->fs->cache_streamp : NULL);
2476 if(r){
2477 /* repaint, placing cursor on new folder! */
2478 rv = 1;
2479 if(context_isambig(new_file)){
2480 FPROC(sparms)->fs->context
2481 = sparms->text.handles->h.f.context;
2482 if(strlen(new_file) < MAXFOLDER - 1){
2483 strncpy(FPROC(sparms)->fs->first_folder, new_file, MAXFOLDER);
2484 FPROC(sparms)->fs->first_folder[MAXFOLDER-1] = '\0';
2489 ps_global->mangled_footer++;
2491 else
2492 q_status_message(SM_ORDER | SM_DING, 0, 4,
2493 _("Empty folder collection. No folder to rename!"));
2495 break;
2498 /*-------------- Delete --------------------*/
2499 case MC_DELETE :
2500 if(!(sparms->text.handles
2501 && folder_total(FOLDERS(sparms->text.handles->h.f.context)))){
2502 q_status_message(SM_ORDER | SM_DING, 0, 4,
2503 _("Empty folder collection. No folder to delete!"));
2505 else{
2506 char next_folder[MAILTMPLEN+1];
2508 next_folder[0] = '\0';
2509 if(delete_folder(sparms->text.handles->h.f.context,
2510 sparms->text.handles->h.f.index,
2511 next_folder, sizeof(next_folder),
2512 FPROC(sparms)->fs->cache_streamp
2513 && *FPROC(sparms)->fs->cache_streamp
2514 ? FPROC(sparms)->fs->cache_streamp : NULL)){
2516 /* repaint, placing cursor on adjacent folder! */
2517 rv = 1;
2518 if(next_folder[0] && strlen(next_folder) < MAXFOLDER - 1){
2519 strncpy(FPROC(sparms)->fs->first_folder, next_folder, MAXFOLDER);
2520 FPROC(sparms)->fs->first_folder[MAXFOLDER-1] = '\0';
2522 else
2523 sparms->text.handles->h.f.context->use &= ~CNTXT_ZOOM;
2525 else
2526 ps_global->mangled_footer++;
2529 break;
2532 /*----------------- Shuffle incoming folder list -----------*/
2533 case MC_SHUFFLE :
2535 CONTEXT_S *cntxt = (sparms->text.handles)
2536 ? sparms->text.handles->h.f.context : NULL;
2538 if(!(cntxt && cntxt->use & CNTXT_INCMNG))
2539 q_status_message(SM_ORDER, 0, 4,
2540 _("May only shuffle Incoming-Folders."));
2541 else if(folder_total(FOLDERS(cntxt)) == 0)
2542 q_status_message(SM_ORDER, 0, 4,
2543 _("No folders to shuffle."));
2544 else if(folder_total(FOLDERS(cntxt)) < 2)
2545 q_status_message(SM_ORDER, 0, 4,
2546 _("Shuffle only makes sense with more than one folder."));
2547 else{
2548 if(FPROC(sparms) && FPROC(sparms)->fs &&
2549 FPROC(sparms)->fs && sparms->text.handles &&
2550 sparms->text.handles->h.f.index >= 0 &&
2551 sparms->text.handles->h.f.index <
2552 folder_total(FOLDERS(cntxt))){
2553 strncpy(FPROC(sparms)->fs->first_folder,
2554 FLDR_NAME(folder_entry(sparms->text.handles->h.f.index,
2555 FOLDERS(cntxt))), MAXFOLDER);
2556 FPROC(sparms)->fs->first_folder[MAXFOLDER-1] = '\0';
2559 rv = shuffle_incoming_folders(cntxt,
2560 sparms->text.handles->h.f.index);
2564 break;
2567 /*-------------- Goto Folder Prompt --------------------*/
2568 case MC_GOTO :
2570 int notrealinbox;
2571 CONTEXT_S *c = (sparms->text.handles)
2572 ? sparms->text.handles->h.f.context
2573 : FPROC(sparms)->fs->context;
2574 char *new_fold = broach_folder(-FOOTER_ROWS(ps_global), 0, &notrealinbox, &c);
2576 if(ps_global && ps_global->ttyo){
2577 blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);
2578 ps_global->mangled_footer = 1;
2581 if(new_fold && do_broach_folder(new_fold, c, NULL, notrealinbox ? 0L : DB_INBOXWOCNTXT) > 0){
2582 ps_global->next_screen = mail_index_screen;
2583 FPROC(sparms)->done = rv = 1;
2585 else
2586 ps_global->mangled_footer = 1;
2588 if((c = ((sparms->text.handles)
2589 ? sparms->text.handles->h.f.context
2590 : FPROC(sparms)->fs->context))->dir->status & CNTXT_NOFIND)
2591 refresh_folder_list(c, FPROC(sparms)->fs->no_dirs, TRUE, FPROC(sparms)->fs->cache_streamp);
2594 break;
2597 /*------------- Print list of folders ---------*/
2598 case MC_PRINTFLDR :
2599 print_folders(FPROC(sparms));
2600 ps_global->mangled_footer++;
2601 break;
2604 /*----- Select the current folder, or toggle checkbox -----*/
2605 case MC_SELCUR :
2606 /*---------- Select set of folders ----------*/
2607 case MC_SELECT :
2608 if(sparms->text.handles
2609 && folder_total(FOLDERS(sparms->text.handles->h.f.context))){
2610 if(cmd == MC_SELCUR){
2611 rv = folder_select_toggle(sparms->text.handles->h.f.context,
2612 sparms->text.handles->h.f.index,
2613 (NEWS_TEST(sparms->text.handles->h.f.context)
2614 && FPROC(sparms)->fs->list_cntxt)
2615 ? ng_scroll_edit : folder_select_update);
2616 if(!rv) ps_global->mangled_screen = 1;
2618 else
2619 switch(folder_select(ps_global,
2620 sparms->text.handles->h.f.context,
2621 sparms->text.handles->h.f.index)){
2622 case 1 :
2623 rv = 1; /* rebuild screen */
2625 case 0 :
2626 default :
2627 ps_global->mangled_screen++;
2628 break;
2631 if((sparms->text.handles->h.f.context->use & CNTXT_ZOOM)
2632 && !selected_folders(sparms->text.handles->h.f.context)){
2633 sparms->text.handles->h.f.context->use &= ~CNTXT_ZOOM;
2634 rv = 1; /* make sure to redraw */
2637 if(rv){ /* remember where to start */
2638 FOLDER_S *fp;
2640 FPROC(sparms)->fs->context = sparms->text.handles->h.f.context;
2641 if((fp = folder_entry(sparms->text.handles->h.f.index,
2642 FOLDERS(sparms->text.handles->h.f.context))) != NULL){
2643 if(strlen(FLDR_NAME(fp)) < MAXFOLDER - 1){
2644 strncpy(FPROC(sparms)->fs->first_folder, FLDR_NAME(fp), MAXFOLDER);
2645 FPROC(sparms)->fs->first_folder[MAXFOLDER-1] = '\0';
2650 else
2651 q_status_message(SM_ORDER | SM_DING, 0, 4,
2652 _("Empty folder collection. No folder to select!"));
2654 break;
2657 /*---------- Display folders ----------*/
2658 case MC_ZOOM :
2659 if(sparms->text.handles
2660 && folder_total(FOLDERS(sparms->text.handles->h.f.context))){
2661 FOLDER_S *fp;
2662 int n;
2664 if((n = selected_folders(sparms->text.handles->h.f.context)) != 0){
2665 if(sparms->text.handles->h.f.context->use & CNTXT_ZOOM){
2666 sparms->text.handles->h.f.context->use &= ~CNTXT_ZOOM;
2667 q_status_message(SM_ORDER, 0, 3,
2668 _("Folder List Zoom mode is now off"));
2670 else{
2671 q_status_message1(SM_ORDER, 0, 3,
2672 _("In Zoomed list of %s folders. Use \"Z\" to restore regular list"),
2673 int2string(n));
2674 sparms->text.handles->h.f.context->use |= CNTXT_ZOOM;
2677 /* exit scrolltool to rebuild screen */
2678 rv = 1;
2680 /* Set where to start after it's rebuilt */
2681 FPROC(sparms)->fs->context = sparms->text.handles->h.f.context;
2682 FPROC(sparms)->fs->first_folder[0] = '\0';
2683 if((fp = folder_entry(sparms->text.handles->h.f.index,
2684 FOLDERS(sparms->text.handles->h.f.context)))
2685 && !((sparms->text.handles->h.f.context->use & CNTXT_ZOOM)
2686 && !fp->selected)
2687 && strlen(FLDR_NAME(fp)) < MAXFOLDER - 1){
2688 strncpy(FPROC(sparms)->fs->first_folder, FLDR_NAME(fp), MAXFOLDER);
2689 FPROC(sparms)->fs->first_folder[MAXFOLDER-1] = '\0';
2692 else
2693 q_status_message(SM_ORDER, 0, 3,
2694 _("No selected folders to Zoom on"));
2696 else
2697 q_status_message(SM_ORDER, 0, 4, _("No Folders to Zoom on!"));
2699 break;
2701 /*----- Ask user to abandon selection before exiting -----*/
2702 case MC_EXITQUERY :
2703 if(sparms->text.handles
2704 && FOLDERS(sparms->text.handles->h.f.context)){
2705 int i, folder_n;
2706 FOLDER_S *fp;
2708 folder_n = folder_total(FOLDERS(sparms->text.handles->h.f.context));
2709 /* any selected? */
2710 for(i = 0; i < folder_n; i++){
2711 fp = folder_entry(i, FOLDERS(sparms->text.handles->h.f.context));
2712 if(fp->selected)
2713 break;
2716 if(i < folder_n /* some selections have been made */
2717 && want_to(_("Really abandon your selections "),
2718 'y', 'x', NO_HELP, WT_NORM) != 'y'){
2719 break;
2722 rv = 1;
2723 break;
2725 /*------------New Msg command --------------*/
2726 case MC_CHK_RECENT:
2728 * Derived from code provided by
2729 * Rostislav Neplokh neplokh@andrew.cmu.edu and
2730 * Robert Siemborski (rjs3@andrew).
2732 if(sparms->text.handles
2733 && folder_total(FOLDERS(sparms->text.handles->h.f.context))){
2734 FOLDER_S *folder;
2736 folder = folder_entry(sparms->text.handles->h.f.index,
2737 FOLDERS(sparms->text.handles->h.f.context));
2739 if(!folder){
2740 strncpy(tmp_output, _("Invalid Folder Name"), sizeof(tmp_output)-1);
2741 tmp_output[sizeof(tmp_output)-1] = '\0';
2743 else if(folder->isdir && !folder->isfolder){
2744 snprintf(tmp_output, sizeof(tmp_output), _("\"%s\" is a directory"), folder->name);
2746 else{
2747 char mailbox_name[MAXPATH+1];
2748 unsigned long tot, rec;
2749 int we_cancel;
2751 context_apply(mailbox_name,
2752 sparms->text.handles->h.f.context,
2753 folder->name, MAXPATH+1);
2755 we_cancel = busy_cue(NULL, NULL, 1);
2757 if(get_recent_in_folder(mailbox_name, &rec, NULL, &tot, NULL))
2758 snprintf(tmp_output, sizeof(tmp_output),
2759 _("%lu total message%s, %lu of them recent"),
2760 tot, plural(tot), rec);
2761 else
2762 snprintf(tmp_output, sizeof(tmp_output),
2763 _("%s: Trouble checking for recent mail"), folder->name);
2765 if(we_cancel)
2766 cancel_busy_cue(-1);
2769 else{
2770 strncpy(tmp_output, _("No folder to check! Can't get recent info"),
2771 sizeof(tmp_output)-1);
2772 tmp_output[sizeof(tmp_output)-1] = '\0';
2775 q_status_message(SM_ORDER, 0, 3, tmp_output);
2776 break;
2779 /*--------------- Invalid Command --------------*/
2780 default:
2781 q_status_message1(SM_ORDER, 0, 2, "fix this: cmd = %s", comatose(cmd));
2782 break;
2785 return(rv);
2790 folder_lister_clickclick(SCROLL_S *sparms)
2792 if(!FPROC(sparms)->fs->list_cntxt)
2793 return(folder_lister_choice(sparms));
2794 else
2795 return(folder_processor(MC_SELCUR, ps_global->msgmap, sparms));
2799 folder_lister_choice(SCROLL_S *sparms)
2801 int rv = 0, empty = 0;
2802 int index = (sparms->text.handles)
2803 ? sparms->text.handles->h.f.index : 0;
2804 CONTEXT_S *cntxt = (sparms->text.handles)
2805 ? sparms->text.handles->h.f.context : NULL;
2807 if(cntxt){
2809 FPROC(sparms)->fs->context = cntxt;
2811 if(!LUU_YES(cntxt->update)){
2812 cntxt->update = LUU_INIT;
2813 refresh_folder_list(cntxt, FPROC(sparms)->fs->no_dirs, TRUE,
2814 FPROC(sparms)->fs->cache_streamp);
2815 if(!LUU_YES(cntxt->update))
2816 q_status_message(SM_ORDER | SM_DING, 3, 3, _("Collection not updated"));
2817 else
2818 rv = 1;
2820 return rv;
2823 if(index < 0 || (cntxt->dir->status & CNTXT_NOFIND)){
2824 rv = 1; /* leave scrolltool to rebuild screen */
2825 FPROC(sparms)->fs->context = cntxt;
2826 FPROC(sparms)->fs->first_folder[0] = '\0';
2828 else if(folder_total(FOLDERS(cntxt))){
2829 if(folder_lister_select(FPROC(sparms)->fs, cntxt, index,
2830 sparms->text.handles ?
2831 sparms->text.handles->is_dual_do_open : 0)){
2832 rv = 1; /* leave scrolltool to rebuild screen */
2834 else if(FPROC(sparms)->fs->list_cntxt == cntxt){
2835 int n = 0, i, folder_n;
2836 FOLDER_S *fp;
2837 STRLIST_S *sl = NULL, **slp;
2839 /* Scan folder list for selected items */
2840 folder_n = folder_total(FOLDERS(cntxt));
2841 slp = &sl;
2842 for(i = 0; i < folder_n; i++){
2843 fp = folder_entry(i, FOLDERS(cntxt));
2844 if(fp->selected){
2845 n++;
2846 if((*FPROC(sparms)->fs->f.valid)(fp,
2847 FPROC(sparms)->fs)){
2848 *slp = new_strlist(NULL);
2849 (*slp)->name = folder_lister_fullname(
2850 FPROC(sparms)->fs, FLDR_NAME(fp));
2852 slp = &(*slp)->next;
2854 else{
2855 free_strlist(&sl);
2856 break;
2861 if((FPROC(sparms)->rv = sl) != NULL)
2862 FPROC(sparms)->done = rv = 1;
2863 else if(!n)
2864 q_status_message(SM_ORDER, 0, 1, LISTMODE_GRIPE);
2866 else
2867 rv = folder_lister_finish(sparms, cntxt, index);
2869 else
2870 empty++;
2872 else
2873 empty++;
2875 if(empty)
2876 q_status_message(SM_ORDER | SM_DING, 3, 3, _("Empty folder list!"));
2878 return(rv);
2883 folder_lister_finish(SCROLL_S *sparms, CONTEXT_S *cntxt, int index)
2885 FOLDER_S *f = folder_entry(index, FOLDERS(cntxt));
2886 int rv = 0;
2888 if((*FPROC(sparms)->fs->f.valid)(f, FPROC(sparms)->fs)){
2890 * Package up the selected folder names and return...
2892 FPROC(sparms)->fs->context = cntxt;
2893 FPROC(sparms)->rv = new_strlist(NULL);
2894 FPROC(sparms)->rv->name = folder_lister_fullname(FPROC(sparms)->fs,
2895 FLDR_NAME(f));
2896 FPROC(sparms)->done = rv = 1;
2899 return(rv);
2904 * This is so that when you Save and use ^T to go to the folder list, and
2905 * you're in a directory with no folders, you have a way to add a new
2906 * folder there. The add actually gets done by the caller. This is just a
2907 * way to let the user type in the name.
2910 folder_lister_addmanually(SCROLL_S *sparms)
2912 int rc, flags = OE_APPEND_CURRENT, cnt = 0, rv = 0;
2913 char addname[MAXFOLDER+1];
2914 HelpType help;
2915 CONTEXT_S *cntxt = (sparms->text.handles)
2916 ? sparms->text.handles->h.f.context
2917 : FPROC(sparms)->fs->context;
2920 * Get the foldername from the user.
2922 addname[0] = '\0';
2923 help = NO_HELP;
2924 while(1){
2925 rc = optionally_enter(addname, -FOOTER_ROWS(ps_global), 0,
2926 sizeof(addname), _("Name of new folder : "),
2927 NULL, help, &flags);
2928 removing_leading_and_trailing_white_space(addname);
2930 if(rc == 3)
2931 help = (help == NO_HELP) ? h_save_addman : NO_HELP;
2932 else if(rc == 1)
2933 return(rv);
2934 else if(rc == 0){
2935 if(F_OFF(F_ENABLE_DOT_FOLDERS,ps_global) && *addname == '.'){
2936 if(cnt++ <= 0)
2937 q_status_message(SM_ORDER,3,3,
2938 _("Folder name can't begin with dot"));
2939 else
2940 q_status_message1(SM_ORDER,3,3,
2941 _("Config feature \"%s\" enables names beginning with dot"),
2942 pretty_feature_name(feature_list_name(F_ENABLE_DOT_FOLDERS), -1));
2944 display_message(NO_OP_COMMAND);
2945 continue;
2947 else if(!strucmp(addname, ps_global->inbox_name)){
2948 q_status_message1(SM_ORDER, 3, 3,
2949 _("Can't add folder named %s"),
2950 ps_global->inbox_name);
2951 continue;
2954 break;
2958 if(*addname){
2959 FPROC(sparms)->fs->context = cntxt;
2960 FPROC(sparms)->rv = new_strlist(NULL);
2961 FPROC(sparms)->rv->name = folder_lister_fullname(FPROC(sparms)->fs,
2962 addname);
2963 FPROC(sparms)->done = rv = 1;
2966 return(rv);
2970 void
2971 folder_lister_km_manager(SCROLL_S *sparms, int handle_hidden)
2973 FOLDER_S *fp;
2975 /* if we're "in" a sub-directory, offer way out */
2976 if((sparms->text.handles)
2977 ? sparms->text.handles->h.f.context->dir->prev
2978 : FPROC(sparms)->fs->context->dir->prev){
2981 * Leave the command characters alone and just change
2982 * the labels and the bind.cmd for KM_COL_KEY.
2983 * Also, leave KM_MAIN_KEY alone instead of trying to
2984 * turn it off in the else clause when it is redundant.
2986 sparms->keys.menu->keys[KM_COL_KEY].label = N_("ParentDir");
2987 sparms->keys.menu->keys[KM_COL_KEY].bind.cmd = MC_PARENT;
2989 else if((FPROC(sparms)->fs->context->next
2990 || FPROC(sparms)->fs->context->prev)
2991 && !FPROC(sparms)->fs->combined_view){
2992 sparms->keys.menu->keys[KM_COL_KEY].label = N_("ClctnList");
2993 sparms->keys.menu->keys[KM_COL_KEY].bind.cmd = MC_EXIT;
2995 else{
2996 sparms->keys.menu->keys[KM_COL_KEY].label = N_("Main Menu");
2997 sparms->keys.menu->keys[KM_COL_KEY].bind.cmd = MC_MAIN;
3000 if(F_OFF(F_ENABLE_AGG_OPS,ps_global)){
3001 clrbitn(KM_ZOOM_KEY, sparms->keys.bitmap);
3002 clrbitn(KM_SELECT_KEY, sparms->keys.bitmap);
3003 clrbitn(KM_SELCUR_KEY, sparms->keys.bitmap);
3006 if(sparms->text.handles
3007 && (fp = folder_entry(sparms->text.handles->h.f.index,
3008 FOLDERS(sparms->text.handles->h.f.context)))){
3009 if(fp->isdir && !sparms->text.handles->is_dual_do_open){
3010 sparms->keys.menu->keys[KM_SEL_KEY].label = "[" N_("View Dir") "]";
3011 menu_add_binding(sparms->keys.menu, 'v', MC_CHOICE);
3012 menu_add_binding(sparms->keys.menu, ctrl('M'), MC_CHOICE);
3013 menu_add_binding(sparms->keys.menu, ctrl('J'), MC_CHOICE);
3014 setbitn(KM_SEL_KEY, sparms->keys.bitmap);
3015 clrbitn(KM_ALTVIEW_KEY, sparms->keys.bitmap);
3016 clrbitn(KM_EXPORT_KEY, sparms->keys.bitmap);
3017 clrbitn(KM_IMPORT_KEY, sparms->keys.bitmap);
3019 else{
3020 sparms->keys.menu->keys[KM_SEL_KEY].label = "[" N_("View Fldr") "]";
3021 menu_add_binding(sparms->keys.menu, 'v', MC_CHOICE);
3022 menu_add_binding(sparms->keys.menu, ctrl('M'), MC_CHOICE);
3023 menu_add_binding(sparms->keys.menu, ctrl('J'), MC_CHOICE);
3024 setbitn(KM_SEL_KEY, sparms->keys.bitmap);
3025 clrbitn(KM_ALTVIEW_KEY, sparms->keys.bitmap);
3026 setbitn(KM_EXPORT_KEY, sparms->keys.bitmap);
3027 setbitn(KM_IMPORT_KEY, sparms->keys.bitmap);
3030 else if(FPROC(sparms)->fs->combined_view
3031 && sparms->text.handles && sparms->text.handles->h.f.context
3032 && !sparms->text.handles->h.f.context->dir->prev){
3033 sparms->keys.menu->keys[KM_SEL_KEY].label = "[" N_("View Cltn") "]";
3034 menu_add_binding(sparms->keys.menu, 'v', MC_CHOICE);
3035 menu_add_binding(sparms->keys.menu, ctrl('M'), MC_CHOICE);
3036 menu_add_binding(sparms->keys.menu, ctrl('J'), MC_CHOICE);
3037 setbitn(KM_SEL_KEY, sparms->keys.bitmap);
3038 clrbitn(KM_ALTVIEW_KEY, sparms->keys.bitmap);
3039 clrbitn(KM_EXPORT_KEY, sparms->keys.bitmap);
3040 clrbitn(KM_IMPORT_KEY, sparms->keys.bitmap);
3042 else{
3043 clrbitn(KM_SEL_KEY, sparms->keys.bitmap);
3044 clrbitn(KM_ALTVIEW_KEY, sparms->keys.bitmap);
3045 clrbitn(KM_EXPORT_KEY, sparms->keys.bitmap);
3046 clrbitn(KM_IMPORT_KEY, sparms->keys.bitmap);
3049 if((sparms->text.handles &&
3050 sparms->text.handles->h.f.context &&
3051 sparms->text.handles->h.f.context->use & CNTXT_INCMNG) ||
3052 (FPROC(sparms) && FPROC(sparms)->fs &&
3053 FPROC(sparms)->fs->context &&
3054 FPROC(sparms)->fs->context->use & CNTXT_INCMNG))
3055 setbitn(KM_SHUFFLE_KEY, sparms->keys.bitmap);
3056 else
3057 clrbitn(KM_SHUFFLE_KEY, sparms->keys.bitmap);
3059 if(F_ON(F_TAB_CHK_RECENT, ps_global)){
3060 menu_clear_binding(sparms->keys.menu, TAB);
3061 menu_init_binding(sparms->keys.menu, TAB, MC_CHK_RECENT, "Tab",
3062 /* TRANSLATORS: New Messages */
3063 N_("NewMsgs"), KM_RECENT_KEY);
3064 setbitn(KM_RECENT_KEY, sparms->keys.bitmap);
3066 else{
3067 menu_clear_binding(sparms->keys.menu, TAB);
3068 menu_add_binding(sparms->keys.menu, TAB, MC_NEXT_HANDLE);
3069 clrbitn(KM_RECENT_KEY, sparms->keys.bitmap);
3072 /* May have to "undo" what scrolltool "did" */
3073 if(F_ON(F_ARROW_NAV, ps_global)){
3074 if(F_ON(F_RELAXED_ARROW_NAV, ps_global)){
3075 menu_clear_binding(sparms->keys.menu, KEY_LEFT);
3076 menu_add_binding(sparms->keys.menu, KEY_LEFT, MC_PREV_HANDLE);
3077 menu_clear_binding(sparms->keys.menu, KEY_RIGHT);
3078 menu_add_binding(sparms->keys.menu, KEY_RIGHT, MC_NEXT_HANDLE);
3080 else{
3081 menu_clear_binding(sparms->keys.menu, KEY_UP);
3082 menu_add_binding(sparms->keys.menu, KEY_UP, MC_PREV_HANDLE);
3083 menu_clear_binding(sparms->keys.menu, KEY_DOWN);
3084 menu_add_binding(sparms->keys.menu, KEY_DOWN, MC_NEXT_HANDLE);
3090 void
3091 folder_lister_km_sel_manager(SCROLL_S *sparms, int handle_hidden)
3093 FOLDER_S *fp;
3095 /* if we're "in" a sub-directory, offer way out */
3096 if((sparms->text.handles)
3097 ? sparms->text.handles->h.f.context->dir->prev
3098 : FPROC(sparms)->fs->context->dir->prev){
3099 sparms->keys.menu->keys[FC_COL_KEY].name = "<";
3100 /* TRANSLATORS: go to parent directory one level up */
3101 sparms->keys.menu->keys[FC_COL_KEY].label = N_("ParentDir");
3102 sparms->keys.menu->keys[FC_COL_KEY].bind.cmd = MC_PARENT;
3103 sparms->keys.menu->keys[FC_COL_KEY].bind.ch[0] = '<';
3104 sparms->keys.menu->keys[FC_COL_KEY].bind.ch[1] = ',';
3105 if(F_ON(F_ARROW_NAV,ps_global)){
3106 sparms->keys.menu->keys[FC_COL_KEY].bind.nch = 3;
3107 sparms->keys.menu->keys[FC_COL_KEY].bind.ch[2] = KEY_LEFT;
3109 else
3110 sparms->keys.menu->keys[FC_COL_KEY].bind.nch = 2;
3112 /* ExitSelect in position 1 */
3113 setbitn(FC_EXIT_KEY, sparms->keys.bitmap);
3115 else if((FPROC(sparms)->fs->context->next
3116 || FPROC(sparms)->fs->context->prev)
3117 && !FPROC(sparms)->fs->combined_view){
3118 sparms->keys.menu->keys[FC_COL_KEY].name = "<";
3119 /* TRANSLATORS: go to Collection List */
3120 sparms->keys.menu->keys[FC_COL_KEY].label = N_("ClctnList");
3121 sparms->keys.menu->keys[FC_COL_KEY].bind.cmd = MC_COLLECTIONS;
3122 sparms->keys.menu->keys[FC_COL_KEY].bind.ch[0] = '<';
3123 sparms->keys.menu->keys[FC_COL_KEY].bind.ch[1] = ',';
3124 if(F_ON(F_ARROW_NAV,ps_global)){
3125 sparms->keys.menu->keys[FC_COL_KEY].bind.nch = 3;
3126 sparms->keys.menu->keys[FC_COL_KEY].bind.ch[2] = KEY_LEFT;
3128 else
3129 sparms->keys.menu->keys[FC_COL_KEY].bind.nch = 2;
3131 /* ExitSelect in position 1 */
3132 setbitn(FC_EXIT_KEY, sparms->keys.bitmap);
3134 else if(FPROC(sparms)->fs->combined_view){
3136 * This can't be a menu_init_binding() because we don't want
3137 * to remove the ExitSelect command in position FC_EXIT_KEY.
3138 * We just turn it off until we need it again.
3140 sparms->keys.menu->keys[FC_COL_KEY].name = "E";
3141 sparms->keys.menu->keys[FC_COL_KEY].label = N_("ExitSelect");
3142 sparms->keys.menu->keys[FC_COL_KEY].bind.cmd = MC_EXIT;
3143 sparms->keys.menu->keys[FC_COL_KEY].bind.nch = 1;
3144 sparms->keys.menu->keys[FC_COL_KEY].bind.ch[0] = 'e';
3146 /* turn off ExitSelect in position 1, it's in 2 now */
3147 clrbitn(FC_EXIT_KEY, sparms->keys.bitmap);
3150 /* clean up per-entry bindings */
3151 clrbitn(FC_SEL_KEY, sparms->keys.bitmap);
3152 clrbitn(FC_ALTSEL_KEY, sparms->keys.bitmap);
3153 menu_clear_binding(sparms->keys.menu, ctrl('M'));
3154 menu_clear_binding(sparms->keys.menu, ctrl('J'));
3155 menu_clear_binding(sparms->keys.menu, '>');
3156 menu_clear_binding(sparms->keys.menu, '.');
3157 menu_clear_binding(sparms->keys.menu, 's');
3158 if(F_ON(F_ARROW_NAV,ps_global))
3159 menu_clear_binding(sparms->keys.menu, KEY_RIGHT);
3161 /* and then re-assign them as needed */
3162 if(sparms->text.handles
3163 && (fp = folder_entry(sparms->text.handles->h.f.index,
3164 FOLDERS(sparms->text.handles->h.f.context)))){
3165 setbitn(FC_SEL_KEY, sparms->keys.bitmap);
3166 if(fp->isdir){
3167 sparms->keys.menu->keys[FC_SEL_KEY].name = ">";
3168 menu_add_binding(sparms->keys.menu, '>', MC_CHOICE);
3169 menu_add_binding(sparms->keys.menu, '.', MC_CHOICE);
3170 if(F_ON(F_ARROW_NAV,ps_global))
3171 menu_add_binding(sparms->keys.menu, KEY_RIGHT, MC_CHOICE);
3173 if(fp->isfolder){
3174 sparms->keys.menu->keys[FC_SEL_KEY].label = N_("View Dir");
3175 setbitn(FC_ALTSEL_KEY, sparms->keys.bitmap);
3176 menu_add_binding(sparms->keys.menu, 's', MC_OPENFLDR);
3177 menu_add_binding(sparms->keys.menu, ctrl('M'), MC_OPENFLDR);
3178 menu_add_binding(sparms->keys.menu, ctrl('J'), MC_OPENFLDR);
3180 else{
3181 sparms->keys.menu->keys[FC_SEL_KEY].label = "[" N_("View Dir") "]";
3182 menu_add_binding(sparms->keys.menu, ctrl('M'), MC_CHOICE);
3183 menu_add_binding(sparms->keys.menu, ctrl('J'), MC_CHOICE);
3186 else{
3187 sparms->keys.menu->keys[FC_SEL_KEY].name = "S";
3188 sparms->keys.menu->keys[FC_SEL_KEY].label = "[" N_("Select") "]";
3190 menu_add_binding(sparms->keys.menu, 's', MC_CHOICE);
3191 menu_add_binding(sparms->keys.menu, ctrl('M'), MC_CHOICE);
3192 menu_add_binding(sparms->keys.menu, ctrl('J'), MC_CHOICE);
3195 if(F_ON(F_ARROW_NAV,ps_global))
3196 menu_add_binding(sparms->keys.menu, KEY_RIGHT, MC_CHOICE);
3198 else if(FPROC(sparms)->fs->combined_view
3199 && sparms->text.handles && sparms->text.handles->h.f.context
3200 && !sparms->text.handles->h.f.context->dir->prev){
3201 setbitn(FC_SEL_KEY, sparms->keys.bitmap);
3202 sparms->keys.menu->keys[FC_SEL_KEY].name = ">";
3203 sparms->keys.menu->keys[FC_SEL_KEY].label = "[" N_("View Cltn") "]";
3204 menu_add_binding(sparms->keys.menu, '>', MC_CHOICE);
3205 menu_add_binding(sparms->keys.menu, '.', MC_CHOICE);
3206 if(F_ON(F_ARROW_NAV,ps_global))
3207 menu_add_binding(sparms->keys.menu, KEY_RIGHT, MC_CHOICE);
3209 menu_add_binding(sparms->keys.menu, ctrl('M'), MC_CHOICE);
3210 menu_add_binding(sparms->keys.menu, ctrl('J'), MC_CHOICE);
3213 /* May have to "undo" what scrolltool "did" */
3214 if(F_ON(F_ARROW_NAV, ps_global)){
3215 if(F_ON(F_RELAXED_ARROW_NAV, ps_global)){
3216 menu_clear_binding(sparms->keys.menu, KEY_LEFT);
3217 menu_add_binding(sparms->keys.menu, KEY_LEFT, MC_PREV_HANDLE);
3218 menu_clear_binding(sparms->keys.menu, KEY_RIGHT);
3219 menu_add_binding(sparms->keys.menu, KEY_RIGHT, MC_NEXT_HANDLE);
3221 else{
3222 menu_clear_binding(sparms->keys.menu, KEY_UP);
3223 menu_add_binding(sparms->keys.menu, KEY_UP, MC_PREV_HANDLE);
3224 menu_clear_binding(sparms->keys.menu, KEY_DOWN);
3225 menu_add_binding(sparms->keys.menu, KEY_DOWN, MC_NEXT_HANDLE);
3231 void
3232 folder_lister_km_sub_manager(SCROLL_S *sparms, int handle_hidden)
3235 * Folder_processor() also modifies the keymenu.
3237 if(FPROC(sparms)->fs->list_cntxt){
3238 clrbitn(SB_LIST_KEY, sparms->keys.bitmap);
3239 sparms->keys.menu->keys[SB_SEL_KEY].name = "X";
3240 sparms->keys.menu->keys[SB_SEL_KEY].label = "[" N_("Set/Unset") "]";
3241 sparms->keys.menu->keys[SB_SEL_KEY].bind.cmd = MC_SELCUR;
3242 sparms->keys.menu->keys[SB_SEL_KEY].bind.ch[0] = 'x';
3244 else{
3245 clrbitn(SB_SUB_KEY, sparms->keys.bitmap);
3246 sparms->keys.menu->keys[SB_SEL_KEY].name = "S";
3247 sparms->keys.menu->keys[SB_SEL_KEY].label = "[" N_("Subscribe") "]";
3248 sparms->keys.menu->keys[SB_SEL_KEY].bind.cmd = MC_CHOICE;
3249 sparms->keys.menu->keys[SB_SEL_KEY].bind.ch[0] = 's';
3252 /* May have to "undo" what scrolltool "did" */
3253 if(F_ON(F_ARROW_NAV, ps_global)){
3254 if(F_ON(F_RELAXED_ARROW_NAV, ps_global)){
3255 menu_clear_binding(sparms->keys.menu, KEY_LEFT);
3256 menu_add_binding(sparms->keys.menu, KEY_LEFT, MC_PREV_HANDLE);
3257 menu_clear_binding(sparms->keys.menu, KEY_RIGHT);
3258 menu_add_binding(sparms->keys.menu, KEY_RIGHT, MC_NEXT_HANDLE);
3260 else{
3261 menu_clear_binding(sparms->keys.menu, KEY_UP);
3262 menu_add_binding(sparms->keys.menu, KEY_UP, MC_PREV_HANDLE);
3263 menu_clear_binding(sparms->keys.menu, KEY_DOWN);
3264 menu_add_binding(sparms->keys.menu, KEY_DOWN, MC_NEXT_HANDLE);
3272 folder_select(struct pine *ps, CONTEXT_S *context, int cur_index)
3274 int i, j, n, total, old_tot, diff,
3275 q = 0, rv = 0, narrow = 0;
3276 HelpType help = NO_HELP;
3277 ESCKEY_S *sel_opts;
3278 FOLDER_S *f;
3279 static ESCKEY_S self_opts2[] = {
3280 /* TRANSLATORS: keymenu descriptions, select all folders, current folder, select
3281 based on folder properties, or select based on text contents in folders */
3282 {'a', 'a', "A", N_("select All")},
3283 {'c', 'c', "C", N_("select Cur")},
3284 {'p', 'p', "P", N_("Properties")},
3285 {'t', 't', "T", N_("Text")},
3286 {-1, 0, NULL, NULL},
3287 {-1, 0, NULL, NULL}
3289 extern ESCKEY_S sel_opts1[];
3290 extern char *sel_pmt2;
3291 #define N_RECENT 4
3293 f = folder_entry(cur_index, FOLDERS(context));
3295 sel_opts = self_opts2;
3296 if((old_tot = selected_folders(context)) != 0){
3297 sel_opts1[1].label = f->selected ? N_("unselect Cur") : N_("select Cur");
3298 sel_opts += 2; /* disable extra options */
3299 switch(q = radio_buttons(SEL_ALTER_PMT, -FOOTER_ROWS(ps_global),
3300 sel_opts1, 'c', 'x', help, RB_NORM)){
3301 case 'f' : /* flip selection */
3302 n = folder_total(FOLDERS(context));
3303 for(total = i = 0; i < n; i++)
3304 if((f = folder_entry(i, FOLDERS(context))) != NULL)
3305 f->selected = !f->selected;
3307 return(1); /* repaint */
3309 case 'n' : /* narrow selection */
3310 narrow++;
3311 case 'b' : /* broaden selection */
3312 q = 0; /* but don't offer criteria prompt */
3313 if(context->use & CNTXT_INCMNG){
3314 q_status_message1(SM_ORDER | SM_DING, 3, 3,
3315 _("Select \"%s\" not supported in Incoming Folders"),
3316 narrow ? "Narrow" : "Broaden");
3317 return(0);
3320 break;
3322 case 'c' : /* Un/Select Current */
3323 case 'a' : /* Unselect All */
3324 case 'x' : /* cancel */
3325 break;
3327 default :
3328 q_status_message(SM_ORDER | SM_DING, 3, 3,
3329 _("Unsupported Select option"));
3330 return(0);
3334 if(context->use & CNTXT_INCMNG && F_ON(F_ENABLE_INCOMING_CHECKING, ps_global)){
3335 if(F_ON(F_INCOMING_CHECKING_RECENT, ps_global)){
3336 self_opts2[N_RECENT].ch = 'r';
3337 self_opts2[N_RECENT].rval = 'r';
3338 self_opts2[N_RECENT].name = "R";
3339 self_opts2[N_RECENT].label = N_("Recent");
3341 else{
3342 self_opts2[N_RECENT].ch = 'u';
3343 self_opts2[N_RECENT].rval = 'u';
3344 self_opts2[N_RECENT].name = "U";
3345 self_opts2[N_RECENT].label = N_("Unseen");
3348 else{
3349 self_opts2[N_RECENT].ch = -1;
3350 self_opts2[N_RECENT].rval = 0;
3351 self_opts2[N_RECENT].name = NULL;
3352 self_opts2[N_RECENT].label = NULL;
3355 if(!q)
3356 q = radio_buttons(sel_pmt2, -FOOTER_ROWS(ps_global),
3357 sel_opts, 'c', 'x', help, RB_NORM);
3360 * NOTE: See note about MESSAGECACHE "searched" bits above!
3362 switch(q){
3363 case 'x': /* cancel */
3364 cmd_cancelled("Select command");
3365 return(0);
3367 case 'c' : /* toggle current's selected state */
3368 return(folder_select_toggle(context, cur_index, folder_select_update));
3370 case 'a' : /* select/unselect all */
3371 n = folder_total(FOLDERS(context));
3372 for(total = i = 0; i < n; i++)
3373 folder_entry(i, FOLDERS(context))->selected = old_tot == 0;
3375 q_status_message4(SM_ORDER, 0, 2,
3376 "%s%s folder%s %sselected",
3377 old_tot ? "" : "All ",
3378 comatose(old_tot ? old_tot : n),
3379 plural(old_tot ? old_tot : n), old_tot ? "UN" : "");
3380 return(1);
3382 case 't' : /* Text */
3383 if(!folder_select_text(ps, context, narrow))
3384 rv++;
3386 break;
3388 case 'p' : /* Properties */
3389 if(!folder_select_props(ps, context, narrow))
3390 rv++;
3392 break;
3394 case 'r' :
3395 n = folder_total(FOLDERS(context));
3396 for(i = 0; i < n; i++){
3397 f = folder_entry(i, FOLDERS(context));
3398 if(f->unseen_valid && f->new > 0L)
3399 f->selected = 1;
3402 break;
3404 case 'u' :
3405 n = folder_total(FOLDERS(context));
3406 for(i = 0; i < n; i++){
3407 f = folder_entry(i, FOLDERS(context));
3408 if(f->unseen_valid && f->unseen > 0L)
3409 f->selected = 1;
3412 break;
3414 default :
3415 q_status_message(SM_ORDER | SM_DING, 3, 3,
3416 _("Unsupported Select option"));
3417 return(0);
3420 if(rv)
3421 return(0);
3423 /* rectify the scanned vs. selected folders */
3424 n = folder_total(FOLDERS(context));
3425 for(total = i = j = 0; i < n; i++)
3426 if(folder_entry(i, FOLDERS(context))->scanned)
3427 break;
3430 * Any matches at all? If not, the selected set remains the same.
3431 * Note that when Narrowing, only matches in the intersection will
3432 * show up as scanned. We need to reset i to 0 to erase any already
3433 * selected messages which aren't in the intersection.
3435 if(i < n)
3436 for(i = 0; i < n; i++)
3437 if((f = folder_entry(i, FOLDERS(context))) != NULL){
3438 if(narrow){
3439 if(f->selected){
3440 f->selected = f->scanned;
3441 j++;
3444 else if(f->scanned)
3445 f->selected = 1;
3448 if(!(diff = (total = selected_folders(context)) - old_tot)){
3449 if(narrow)
3450 q_status_message4(SM_ORDER, 0, 2,
3451 "%s. %s folder%s remain%s selected.",
3452 j ? _("No change resulted")
3453 : _("No messages in intersection"),
3454 comatose(old_tot), plural(old_tot),
3455 (old_tot == 1L) ? "s" : "");
3456 else if(old_tot && j)
3457 q_status_message(SM_ORDER, 0, 2,
3458 _("No change resulted. Matching folders already selected."));
3459 else
3460 q_status_message1(SM_ORDER | SM_DING, 0, 2,
3461 "Select failed! No %sfolders selected.",
3462 old_tot ? "additional " : "");
3464 else if(old_tot){
3465 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
3466 "Select matched %d folder%s. %s %sfolder%s %sselected.",
3467 (diff > 0) ? diff : old_tot + diff,
3468 plural((diff > 0) ? diff : old_tot + diff),
3469 comatose((diff > 0) ? total : -diff),
3470 (diff > 0) ? "total " : "",
3471 plural((diff > 0) ? total : -diff),
3472 (diff > 0) ? "" : "UN");
3473 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
3474 q_status_message(SM_ORDER, 0, 2, tmp_20k_buf);
3476 else
3477 q_status_message2(SM_ORDER, 0, 2, "Select matched %s folder%s.",
3478 comatose(diff), plural(diff));
3480 return(1);
3487 folder_lister_select(FSTATE_S *fs, CONTEXT_S *context, int index, int is_dual_do_open)
3489 int rv = 0;
3490 FDIR_S *fp;
3491 FOLDER_S *f = folder_entry(index, FOLDERS(context));
3493 /*--- Entering a directory? ---*/
3494 if(f->isdir && !is_dual_do_open){
3495 fp = next_folder_dir(context, f->name, TRUE, fs->cache_streamp);
3497 /* Provide context in new collection header */
3498 fp->desc = folder_lister_desc(context, fp);
3500 /* Insert new directory into list */
3501 free_folder_list(context);
3502 fp->prev = context->dir;
3503 fp->status |= CNTXT_SUBDIR;
3504 context->dir = fp;
3505 q_status_message2(SM_ORDER, 0, 3, "Now in %sdirectory: %s",
3506 folder_total(FOLDERS(context))
3507 ? "" : "EMPTY ", fp->ref);
3508 rv++;
3510 else
3511 rv = folder_lister_parent(fs, context, index, 1);
3513 return(rv);
3518 folder_lister_parent(FSTATE_S *fs, CONTEXT_S *context, int index, int force_parent)
3520 int rv = 0;
3521 FDIR_S *fp;
3523 if(!force_parent && (fp = context->dir->prev)){
3524 char *s, oldir[MAILTMPLEN];
3526 folder_select_preserve(context);
3527 oldir[0] = '\0';
3528 if((s = strrindex(context->dir->ref, context->dir->delim)) != NULL){
3529 char *u, *v;
3530 *s = '\0';
3531 u = strrindex(context->dir->ref, context->dir->delim);
3532 v = strrindex(context->dir->ref, '}');
3533 s = u < v ? v : u;
3534 if(s != NULL){
3535 strncpy(oldir, s+1, sizeof(oldir)-1);
3536 oldir[sizeof(oldir)-1] = '\0';
3540 if(*oldir){
3541 /* remember current directory for hiliting in new list */
3542 fs->context = context;
3543 if(strlen(oldir) < MAXFOLDER - 1){
3544 strncpy(fs->first_folder, oldir, MAXFOLDER);
3545 fs->first_folder[MAXFOLDER-1] = '\0';
3546 fs->first_dir = 1;
3550 free_fdir(&context->dir, 0);
3551 fp->status |= CNTXT_NOFIND;
3553 context->dir = fp;
3555 if(fp->status & CNTXT_SUBDIR)
3556 q_status_message1(SM_ORDER, 0, 3, _("Now in directory: %s"),
3557 strsquish(tmp_20k_buf + 500, SIZEOF_20KBUF-500, fp->ref,
3558 ps_global->ttyo->screen_cols - 22));
3559 else
3560 q_status_message(SM_ORDER, 0, 3,
3561 _("Returned to collection's top directory"));
3563 rv++;
3566 return(rv);
3570 char *
3571 folder_lister_fullname(FSTATE_S *fs, char *name)
3573 if(fs->context->dir->status & CNTXT_SUBDIR){
3574 char tmp[2*MAILTMPLEN], tmp2[2*MAILTMPLEN], *p;
3576 if(fs->context->dir->ref){
3577 snprintf(tmp, sizeof(tmp), "%.*s%.*s",
3578 MAILTMPLEN, /* MAILTMPLEN == sizeof(tmp)/2 */
3579 ((fs->relative_path || (fs->context->use & CNTXT_SAVEDFLT))
3580 && (p = strstr(fs->context->context, "%s")) && !*(p+2)
3581 && !strncmp(fs->context->dir->ref, fs->context->context,
3582 p - fs->context->context))
3583 ? fs->context->dir->ref + (p - fs->context->context)
3584 : fs->context->dir->ref,
3585 MAILTMPLEN, name);
3586 tmp[sizeof(tmp)-1] = '\0';
3590 * If the applied name is still ambiguous (defined
3591 * that way by the user (i.e., "mail/[]"), then
3592 * we better fix it up...
3594 if(context_isambig(tmp)
3595 && !fs->relative_path
3596 && !(fs->context->use & CNTXT_SAVEDFLT)){
3597 /* if it's in the primary collection, the names relative */
3598 if(fs->context->dir->ref){ /* MAILTMPLEN = sizeof(tmp)/2 */
3599 if(IS_REMOTE(fs->context->context)
3600 && (p = strrindex(fs->context->context, '}'))){
3601 snprintf(tmp2, sizeof(tmp2), "%.*s%.*s",
3602 (int) MIN(p - fs->context->context + 1, sizeof(tmp)/2),
3603 fs->context->context,
3604 MAILTMPLEN, tmp);
3605 tmp2[sizeof(tmp2)-1] = '\0';
3607 else
3608 build_path(tmp2, ps_global->ui.homedir, tmp, sizeof(tmp2));
3610 else
3611 (void) context_apply(tmp2, fs->context, tmp, sizeof(tmp2));
3613 return(cpystr(tmp2));
3616 return(cpystr(tmp));
3619 return(cpystr(name));
3624 * Export a folder from pine space to user's space. It will still be a regular
3625 * mail folder but it will be in the user's home directory or something like
3626 * that.
3628 void
3629 folder_export(SCROLL_S *sparms)
3631 FOLDER_S *f;
3632 MAILSTREAM *stream, *ourstream = NULL;
3633 char expanded_file[MAILTMPLEN], *p,
3634 tmp[MAILTMPLEN], *fname, *fullname = NULL,
3635 filename[MAXPATH+1], full_filename[MAXPATH+1],
3636 deefault[MAXPATH+1];
3637 int open_inbox = 0, we_cancel = 0, width,
3638 index = (sparms && sparms->text.handles)
3639 ? sparms->text.handles->h.f.index : 0;
3640 CONTEXT_S *savecntxt,
3641 *cntxt = (sparms && sparms->text.handles)
3642 ? sparms->text.handles->h.f.context : NULL;
3643 static HISTORY_S *history = NULL;
3645 dprint((4, "\n - folder export -\n"));
3647 if(cntxt){
3648 if(folder_total(FOLDERS(cntxt))){
3649 f = folder_entry(index, FOLDERS(cntxt));
3650 if((*FPROC(sparms)->fs->f.valid)(f, FPROC(sparms)->fs)){
3651 savecntxt = FPROC(sparms)->fs->context; /* necessary? */
3652 FPROC(sparms)->fs->context = cntxt;
3653 strncpy(deefault, FLDR_NAME(f), sizeof(deefault)-1);
3654 deefault[sizeof(deefault)-1] = '\0';
3655 fname = folder_lister_fullname(FPROC(sparms)->fs, FLDR_NAME(f));
3656 FPROC(sparms)->fs->context = savecntxt;
3659 * We have to allow for INBOX and nicknames in
3660 * the incoming collection. Mimic what happens in
3661 * do_broach_folder.
3663 strncpy(expanded_file, fname, sizeof(expanded_file));
3664 expanded_file[sizeof(expanded_file)-1] = '\0';
3666 if(strucmp(fname, ps_global->inbox_name) == 0
3667 || strucmp(fname, ps_global->VAR_INBOX_PATH) == 0)
3668 open_inbox++;
3670 if(!open_inbox && cntxt && context_isambig(fname)){
3671 if((p=folder_is_nick(fname, FOLDERS(cntxt), 0)) != NULL){
3672 strncpy(expanded_file, p, sizeof(expanded_file));
3673 expanded_file[sizeof(expanded_file)-1] = '\0';
3675 else if ((cntxt->use & CNTXT_INCMNG)
3676 && (folder_index(fname, cntxt, FI_FOLDER) < 0)
3677 && !is_absolute_path(fname)){
3678 q_status_message1(SM_ORDER, 3, 4,
3679 _("Can't find Incoming Folder %s."), fname);
3680 return;
3684 if(open_inbox)
3685 fullname = ps_global->VAR_INBOX_PATH;
3686 else{
3688 * We don't want to interpret this name in the context
3689 * of the reference string, that was already done
3690 * above in folder_lister_fullname(), just in the
3691 * regular context. We also don't want to lose the
3692 * reference string because we will still be in the
3693 * subdirectory after this operation completes. So
3694 * temporarily zero out the reference.
3696 FDIR_S *tmpdir;
3698 tmpdir = (cntxt ? cntxt->dir : NULL);
3699 cntxt->dir = NULL;
3700 fullname = context_apply(tmp, cntxt, expanded_file,
3701 sizeof(tmp));
3702 cntxt->dir = tmpdir;
3705 width = MAX(20,
3706 ps_global->ttyo ? ps_global->ttyo->screen_cols : 80);
3707 stream = sp_stream_get(fullname, SP_MATCH | SP_RO_OK);
3708 if(!stream && fullname){
3710 * Just using filename and full_filename as convenient
3711 * temporary buffers here.
3713 snprintf(filename, sizeof(filename), "Opening \"%s\"",
3714 short_str(fullname, full_filename, sizeof(full_filename),
3715 MAX(10, width-17),
3716 MidDots));
3717 filename[sizeof(filename)-1] = '\0';
3718 we_cancel = busy_cue(filename, NULL, 0);
3719 stream = pine_mail_open(NULL, fullname,
3720 OP_READONLY|SP_USEPOOL|SP_TEMPUSE,
3721 NULL);
3722 if(we_cancel)
3723 cancel_busy_cue(0);
3725 ourstream = stream;
3729 * We have a stream for the folder we want to
3730 * export.
3732 if(stream && stream->nmsgs > 0L){
3733 int r = 1;
3734 static ESCKEY_S eopts[] = {
3735 {ctrl('T'), 10, "^T", N_("To Files")},
3736 {-1, 0, NULL, NULL},
3737 {-1, 0, NULL, NULL}};
3739 if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
3740 eopts[r].ch = ctrl('I');
3741 eopts[r].rval = 11;
3742 eopts[r].name = "TAB";
3743 /* TRANSLATORS: Complete is a verb, complete the name of a folder */
3744 eopts[r].label = _("Complete");
3747 eopts[++r].ch = -1;
3749 filename[0] = '\0';
3750 full_filename[0] = '\0';
3752 r = get_export_filename(ps_global, filename, deefault,
3753 full_filename,
3754 sizeof(filename)-20, fname, NULL,
3755 eopts, NULL,
3756 -FOOTER_ROWS(ps_global),
3757 GE_IS_EXPORT | GE_NO_APPEND, &history);
3758 if(r < 0){
3759 switch(r){
3760 default:
3761 case -1:
3762 cmd_cancelled("Export folder");
3763 break;
3765 case -2:
3766 q_status_message1(SM_ORDER, 0, 2,
3767 _("Can't export to file outside of %s"),
3768 ps_global->VAR_OPER_DIR);
3769 break;
3772 else{
3773 int ok;
3774 char *ff;
3776 ps_global->mm_log_error = 0;
3779 * Do the creation in standard unix format, so it
3780 * is readable by all.
3782 rplstr(full_filename, sizeof(full_filename), 0, "#driver.unix/");
3783 ok = pine_mail_create(NULL, full_filename) != 0L;
3785 * ff points to the original filename, without the
3786 * driver prefix. Only mail_create knows how to
3787 * handle driver prefixes.
3789 ff = full_filename + strlen("#driver.unix/");
3791 if(!ok){
3792 if(!ps_global->mm_log_error)
3793 q_status_message(SM_ORDER | SM_DING, 3, 3,
3794 _("Error creating file"));
3796 else{
3797 long l, snmsgs;
3798 MSGNO_S *tmpmap = NULL;
3800 snmsgs = stream->nmsgs;
3801 mn_init(&tmpmap, snmsgs);
3802 for(l = 1L; l <= snmsgs; l++)
3803 if(l == 1L)
3804 mn_set_cur(tmpmap, l);
3805 else
3806 mn_add_cur(tmpmap, l);
3808 blank_keymenu(ps_global->ttyo->screen_rows-2, 0);
3809 we_cancel = busy_cue(_("Copying folder"), NULL, 0);
3810 l = save(ps_global, stream, NULL, ff, tmpmap, 0);
3811 if(we_cancel)
3812 cancel_busy_cue(0);
3814 mn_give(&tmpmap);
3816 if(l == snmsgs)
3817 q_status_message2(SM_ORDER, 0, 3,
3818 "Folder %s exported to %s",
3819 fname, filename);
3820 else{
3821 q_status_message1(SM_ORDER | SM_DING, 3, 3,
3822 _("Error exporting to %s"),
3823 filename);
3824 dprint((2,
3825 "Error exporting to %s: expected %ld msgs, saved %ld\n",
3826 filename, snmsgs, l));
3831 else if(stream)
3832 q_status_message1(SM_ORDER|SM_DING, 3, 3,
3833 _("No messages in %s to export"), fname);
3834 else
3835 q_status_message(SM_ORDER|SM_DING, 3, 3,
3836 _("Can't open folder for exporting"));
3838 if(fname)
3839 fs_give((void **) &fname);
3841 if(ourstream)
3842 pine_mail_close(ourstream);
3846 else
3847 q_status_message(SM_ORDER | SM_DING, 3, 3, _("Empty folder list!"));
3852 * Import a folder from user's space back to pine space.
3853 * We're just importing a regular mail folder, and saving all the messages
3854 * to another folder. It may seem more magical to the user but it isn't.
3855 * The import folder is a local folder, the new one can be remote or whatever.
3856 * Args sparms
3857 * add_folder -- return new folder name here
3858 * len -- length of add_folder
3860 * Returns 1 if we may have to redraw screen, 0 otherwise
3863 folder_import(SCROLL_S *sparms, char *add_folder, size_t len)
3865 MAILSTREAM *istream = NULL;
3866 int r = 1, rv = 0;
3867 char filename[MAXPATH+1], full_filename[MAXPATH+1];
3868 static HISTORY_S *history = NULL;
3869 static ESCKEY_S eopts[] = {
3870 {ctrl('T'), 10, "^T", N_("To Files")},
3871 {-1, 0, NULL, NULL},
3872 {-1, 0, NULL, NULL}};
3874 if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
3875 eopts[r].ch = ctrl('I');
3876 eopts[r].rval = 11;
3877 eopts[r].name = "TAB";
3878 eopts[r].label = N_("Complete");
3881 eopts[++r].ch = -1;
3883 filename[0] = '\0';
3884 full_filename[0] = '\0';
3886 /* get a folder to import */
3887 r = get_export_filename(ps_global, filename, NULL, full_filename,
3888 sizeof(filename)-20, "messages", "IMPORT",
3889 eopts, NULL,
3890 -FOOTER_ROWS(ps_global), GE_IS_IMPORT, &history);
3891 if(r < 0){
3892 switch(r){
3893 default:
3894 case -1:
3895 cmd_cancelled("Import folder");
3896 break;
3898 case -2:
3899 q_status_message1(SM_ORDER, 0, 2,
3900 _("Can't import file outside of %s"),
3901 ps_global->VAR_OPER_DIR);
3902 break;
3905 else{
3906 ps_global->mm_log_error = 0;
3907 if(full_filename && full_filename[0])
3908 istream = pine_mail_open(NULL, full_filename,
3909 OP_READONLY | SP_TEMPUSE, NULL);
3911 if(istream && istream->nmsgs > 0L){
3912 long l;
3913 int we_cancel = 0;
3914 char newfolder[MAILTMPLEN], nmsgs[32];
3915 MSGNO_S *tmpmap = NULL;
3916 CONTEXT_S *cntxt, *ourcntxt;
3918 cntxt = (sparms && sparms->text.handles)
3919 ? sparms->text.handles->h.f.context : NULL;
3920 ourcntxt = cntxt;
3921 newfolder[0] = '\0';
3922 snprintf(nmsgs, sizeof(nmsgs), "%s msgs ", comatose(istream->nmsgs));
3923 nmsgs[sizeof(nmsgs)-1] = '\0';
3926 * Select a folder to save the messages to.
3928 if(save_prompt(ps_global, &cntxt, newfolder, sizeof(newfolder),
3929 nmsgs, NULL, 0L, NULL, NULL, NULL)){
3931 if((cntxt == ourcntxt) && newfolder[0]){
3932 rv = 1;
3933 strncpy(add_folder, newfolder, len-1);
3934 add_folder[len-1] = '\0';
3935 free_folder_list(cntxt);
3938 mn_init(&tmpmap, istream->nmsgs);
3939 for(l = 1; l <= istream->nmsgs; l++)
3940 if(l == 1L)
3941 mn_set_cur(tmpmap, l);
3942 else
3943 mn_add_cur(tmpmap, l);
3945 blank_keymenu(ps_global->ttyo->screen_rows-2, 0);
3946 we_cancel = busy_cue("Importing messages", NULL, 0);
3947 l = save(ps_global, istream, cntxt, newfolder, tmpmap,
3948 SV_INBOXWOCNTXT);
3949 if(we_cancel)
3950 cancel_busy_cue(0);
3952 mn_give(&tmpmap);
3954 if(l == istream->nmsgs)
3955 q_status_message2(SM_ORDER, 0, 3,
3956 "Folder %s imported to %s",
3957 full_filename, newfolder);
3958 else
3959 q_status_message1(SM_ORDER | SM_DING, 3, 3,
3960 _("Error importing to %s"),
3961 newfolder);
3964 else if(istream)
3965 q_status_message1(SM_ORDER | SM_DING, 3, 3,
3966 _("No messages in file %s"),
3967 full_filename);
3968 else if(!ps_global->mm_log_error)
3969 q_status_message1(SM_ORDER | SM_DING, 3, 3,
3970 _("Can't open file %s for import"), full_filename);
3973 if(istream)
3974 pine_mail_close(istream);
3976 return(rv);
3981 folder_select_toggle(CONTEXT_S *context, int index, int (*func) (CONTEXT_S *, int))
3983 FOLDER_S *f;
3985 if((f = folder_entry(index, FOLDERS(context))) != NULL){
3986 f->selected = !f->selected;
3987 return((*func)(context, index));
3989 return 1;
3994 * Find the next '}' that isn't part of a "${"
3995 * that appear for environment variables. We need
3996 * this for configuration, because we want to edit
3997 * the original pinerc entry, and not the digested
3998 * value.
4000 char *
4001 end_bracket_no_nest(char *str)
4003 char *p;
4005 for(p = str; *p; p++){
4006 if(*p == '$' && *(p+1) == '{'){
4007 while(*p && *p != '}')
4008 p++;
4009 if(!*p)
4010 return NULL;
4012 else if(*p == '}')
4013 return p;
4015 /* if we get here then there was no trailing '}' */
4016 return NULL;
4020 /*----------------------------------------------------------------------
4021 Create a new folder
4023 Args: context -- context we're adding in
4024 which -- which config file to operate on
4025 varnum -- which varnum to operate on
4026 add_folder -- return new folder here
4027 add_folderlen -- length of add_folder
4028 possible_stream -- possible stream for re-use
4029 def -- default value to start out with for add_folder
4030 (for now, only for inbox-path editing)
4032 Result: returns nonzero on successful add, 0 otherwise
4033 ----*/
4035 add_new_folder(CONTEXT_S *context, EditWhich which, int varnum, char *add_folder,
4036 size_t add_folderlen, MAILSTREAM *possible_stream, char *def)
4038 char tmp[MAX(MAXFOLDER,6*MAX_SCREEN_COLS)+1], nickname[32],
4039 *p = NULL, *return_val = NULL, buf[MAILTMPLEN],
4040 buf2[MAILTMPLEN], def_in_prompt[MAILTMPLEN], orig_folder[2*MAXFOLDER+10];
4041 HelpType help;
4042 PINERC_S *prc = NULL;
4043 int i, rc, offset, exists, cnt = 0, isdir = 0;
4044 int maildrop = 0, flags = 0, inbox = 0, require_a_subfolder = 0;
4045 char *maildropfolder = NULL, *maildroplongname = NULL;
4046 char *default_mail_drop_host = NULL,
4047 *default_mail_drop_folder = NULL,
4048 *default_dstn_host = NULL,
4049 *default_dstn_folder = NULL,
4050 *copydef = NULL,
4051 *dstnmbox = NULL;
4052 char mdmbox[MAILTMPLEN], ctmp[MAILTMPLEN];
4053 MAILSTREAM *create_stream = NULL;
4054 FOLDER_S *f;
4055 static ESCKEY_S add_key[] = {{ctrl('X'),12,"^X", NULL},
4056 {-1, 0, NULL, NULL}};
4058 dprint((4, "\n - add_new_folder - \n"));
4060 nickname[0] = orig_folder[0] = add_folder[0] = '\0';
4061 inbox = (varnum == V_INBOX_PATH);
4063 if(inbox || context->use & CNTXT_INCMNG){
4064 char inbox_host[MAXPATH], *beg, *end = NULL;
4065 int readonly = 0;
4066 static ESCKEY_S host_key[4];
4068 if(ps_global->restricted)
4069 readonly = 1;
4070 else{
4071 switch(which){
4072 case Main:
4073 prc = ps_global->prc;
4074 break;
4075 case Post:
4076 prc = ps_global->post_prc;
4077 break;
4078 case None:
4079 break;
4082 readonly = prc ? prc->readonly : 1;
4085 if(prc && prc->quit_to_edit){
4086 quit_to_edit_msg(prc);
4087 return(FALSE);
4090 if(readonly){
4091 q_status_message(SM_ORDER,3,5,
4092 _("Cancelled: config file not editable"));
4093 return(FALSE);
4097 * When adding an Incoming folder we can't supply the inbox host
4098 * as a default, because then the user has no way to type just
4099 * a plain RETURN to mean "no host, I want a local folder".
4100 * So we supply it as a ^X command instead. We could supply it
4101 * as the initial value of the string...
4103 * When editing inbox-path we will supply the default value as an
4104 * initial value of the string which can be edited. They can edit it
4105 * or erase it entirely to mean no host.
4108 if(inbox && def){
4110 copydef = cpystr(def);
4111 (void) removing_double_quotes(copydef);
4113 if(check_for_move_mbox(copydef, mdmbox, sizeof(mdmbox), &dstnmbox)){
4116 * Current inbox is using a mail drop. Get the default
4117 * host value for the maildrop.
4120 if(mdmbox
4121 && (mdmbox[0] == '{'
4122 || (mdmbox[0] == '*' && mdmbox[1] == '{'))
4123 && (end = end_bracket_no_nest(mdmbox+1))
4124 && end-mdmbox < add_folderlen){
4125 *end = '\0';
4126 if(mdmbox[0] == '{')
4127 default_mail_drop_host = cpystr(mdmbox+1);
4128 else
4129 default_mail_drop_host = cpystr(mdmbox+2);
4132 if(!default_mail_drop_host)
4133 default_mail_drop_folder = cpystr(mdmbox);
4134 else if(end && *(end+1))
4135 default_mail_drop_folder = cpystr(end+1);
4137 end = NULL;
4138 if(dstnmbox
4139 && (*dstnmbox == '{'
4140 || (*dstnmbox == '*' && *++dstnmbox == '{'))
4141 && (end = end_bracket_no_nest(dstnmbox+1))
4142 && end-dstnmbox < add_folderlen){
4143 *end = '\0';
4144 default_dstn_host = cpystr(dstnmbox+1);
4147 if(!default_dstn_host)
4148 default_dstn_folder = cpystr(dstnmbox);
4149 else if(end && *(end+1))
4150 default_dstn_folder = cpystr(end+1);
4152 maildrop++;
4154 else{
4155 end = NULL;
4156 dstnmbox = copydef;
4157 if(dstnmbox
4158 && (*dstnmbox == '{'
4159 || (*dstnmbox == '*' && *++dstnmbox == '{'))
4160 && (end = end_bracket_no_nest(dstnmbox+1))
4161 && end-dstnmbox < add_folderlen){
4162 *end = '\0';
4163 default_dstn_host = cpystr(dstnmbox+1);
4166 if(!default_dstn_host)
4167 default_dstn_folder = cpystr(dstnmbox);
4168 else if(end && *(end+1))
4169 default_dstn_folder = cpystr(end+1);
4172 if(copydef)
4173 fs_give((void **) &copydef);
4176 get_folder_name:
4178 i = 0;
4179 host_key[i].ch = 0;
4180 host_key[i].rval = 0;
4181 host_key[i].name = "";
4182 host_key[i++].label = "";
4184 inbox_host[0] = '\0';
4185 if(!inbox && (beg = ps_global->VAR_INBOX_PATH)
4186 && (*beg == '{' || (*beg == '*' && *++beg == '{'))
4187 && (end = strindex(ps_global->VAR_INBOX_PATH, '}'))){
4188 strncpy(inbox_host, beg+1, end - beg);
4189 inbox_host[end - beg - 1] = '\0';
4190 host_key[i].ch = ctrl('X');
4191 host_key[i].rval = 12;
4192 host_key[i].name = "^X";
4193 host_key[i++].label = N_("Use Inbox Host");
4195 else{
4196 host_key[i].ch = 0;
4197 host_key[i].rval = 0;
4198 host_key[i].name = "";
4199 host_key[i++].label = "";
4202 if(!maildrop && !maildropfolder){
4203 host_key[i].ch = ctrl('W');
4204 host_key[i].rval = 13;
4205 host_key[i].name = "^W";
4206 /* TRANSLATORS: a mail drop is a place where mail is copied to so you
4207 can read it. */
4208 host_key[i++].label = N_("Use a Mail Drop");
4210 else if(maildrop){
4211 host_key[i].ch = ctrl('W');
4212 host_key[i].rval = 13;
4213 host_key[i].name = "^W";
4214 host_key[i++].label = N_("Do Not use a Mail Drop");
4217 host_key[i].ch = -1;
4218 host_key[i].rval = 0;
4219 host_key[i].name = NULL;
4220 host_key[i].label = NULL;
4222 if(maildrop)
4223 snprintf(tmp, sizeof(tmp), _("Name of Mail Drop server : "));
4224 else if(maildropfolder)
4225 snprintf(tmp, sizeof(tmp), _("Name of server to contain destination folder : "));
4226 else if(inbox)
4227 snprintf(tmp, sizeof(tmp), _("Name of Inbox server : "));
4228 else
4229 snprintf(tmp, sizeof(tmp), _("Name of server to contain added folder : "));
4231 tmp[sizeof(tmp)-1] = '\0';
4233 help = NO_HELP;
4235 /* set up defaults */
4236 if(inbox && def){
4237 flags = OE_APPEND_CURRENT;
4238 if(maildrop && default_mail_drop_host){
4239 strncpy(add_folder, default_mail_drop_host, add_folderlen);
4240 add_folder[add_folderlen-1] = '\0';
4242 else if(!maildrop && default_dstn_host){
4243 strncpy(add_folder, default_dstn_host, add_folderlen);
4244 add_folder[add_folderlen-1] = '\0';
4246 else
4247 add_folder[0] = '\0';
4249 else{
4250 flags = 0;
4251 add_folder[0] = '\0';
4254 while(1){
4255 rc = optionally_enter(add_folder, -FOOTER_ROWS(ps_global), 0,
4256 add_folderlen, tmp, host_key, help, &flags);
4257 removing_leading_and_trailing_white_space(add_folder);
4260 * User went for the whole enchilada and entered a maildrop
4261 * completely without going through the steps.
4262 * Split it up as if they did and then skip over
4263 * some of the code.
4265 if(check_for_move_mbox(add_folder, mdmbox, sizeof(mdmbox),
4266 &dstnmbox)){
4267 maildrop = 1;
4268 if(maildropfolder)
4269 fs_give((void **) &maildropfolder);
4271 maildropfolder = cpystr(mdmbox);
4273 strncpy(add_folder, dstnmbox, add_folderlen);
4274 add_folder[add_folderlen-1] = '\0';
4275 offset = 0;
4276 goto skip_over_folder_input;
4280 * Now check to see if they entered a whole c-client
4281 * spec that isn't a mail drop.
4283 if(add_folder[0] == '{'
4284 && add_folder[1] != '\0'
4285 && (p = srchstr(add_folder, "}"))
4286 && *(p+1) != '\0'){
4287 offset = p+1 - add_folder;
4288 goto skip_over_folder_input;
4292 * And check to see if they entered "INBOX".
4294 if(!strucmp(add_folder, ps_global->inbox_name)){
4295 offset = 0;
4296 goto skip_over_folder_input;
4299 /* remove surrounding braces */
4300 if(add_folder[0] == '{' && add_folder[1] != '\0'){
4301 char *q;
4303 q = add_folder + strlen(add_folder) - 1;
4304 if(*q == '}'){
4305 *q = '\0';
4306 for(q = add_folder; *q; q++)
4307 *q = *(q+1);
4311 if(rc == 3){
4312 if(maildropfolder && inbox)
4313 helper(h_inbox_add_maildrop_destn,
4314 _("HELP FOR DESTINATION SERVER "), HLPD_SIMPLE);
4315 else if(maildropfolder && !inbox)
4316 helper(h_incoming_add_maildrop_destn,
4317 _("HELP FOR DESTINATION SERVER "), HLPD_SIMPLE);
4318 else if(maildrop && inbox)
4319 helper(h_inbox_add_maildrop, _("HELP FOR MAILDROP NAME "),
4320 HLPD_SIMPLE);
4321 else if(maildrop && !inbox)
4322 helper(h_incoming_add_maildrop, _("HELP FOR MAILDROP NAME "),
4323 HLPD_SIMPLE);
4324 else if(inbox)
4325 helper(h_incoming_add_inbox, _("HELP FOR INBOX SERVER "),
4326 HLPD_SIMPLE);
4327 else
4328 helper(h_incoming_add_folder_host, _("HELP FOR SERVER NAME "),
4329 HLPD_SIMPLE);
4331 ps_global->mangled_screen = 1;
4333 else if(rc == 12){
4334 strncpy(add_folder, inbox_host, add_folderlen);
4335 flags |= OE_APPEND_CURRENT;
4337 else if(rc == 13){
4338 if(maildrop){
4339 maildrop = 0;
4340 if(maildropfolder)
4341 fs_give((void **) &maildropfolder);
4343 else{
4344 maildrop++;
4345 if(inbox && def){
4346 default_mail_drop_host = default_dstn_host;
4347 default_dstn_host = NULL;
4348 default_mail_drop_folder = default_dstn_folder;
4349 default_dstn_folder = NULL;
4353 goto get_folder_name;
4355 else if(rc == 1){
4356 q_status_message(SM_ORDER,0,2,
4357 inbox ? _("INBOX change cancelled")
4358 : _("Addition of new folder cancelled"));
4359 if(maildropfolder)
4360 fs_give((void **) &maildropfolder);
4362 if(inbox && def){
4363 if(default_mail_drop_host)
4364 fs_give((void **) &default_mail_drop_host);
4366 if(default_mail_drop_folder)
4367 fs_give((void **) &default_mail_drop_folder);
4369 if(default_dstn_host)
4370 fs_give((void **) &default_dstn_host);
4372 if(default_dstn_folder)
4373 fs_give((void **) &default_dstn_folder);
4376 return(FALSE);
4378 else if(rc == 0)
4379 break;
4383 /* set up default folder, if any */
4384 def_in_prompt[0] = '\0';
4385 if(inbox && def){
4386 if(maildrop && default_mail_drop_folder){
4387 strncpy(def_in_prompt, default_mail_drop_folder,
4388 sizeof(def_in_prompt));
4389 def_in_prompt[sizeof(def_in_prompt)-1] = '\0';
4391 else if(!maildrop && default_dstn_folder){
4392 strncpy(def_in_prompt, default_dstn_folder,
4393 sizeof(def_in_prompt));
4394 def_in_prompt[sizeof(def_in_prompt)-1] = '\0';
4398 if((offset = strlen(add_folder)) != 0){ /* must be host for incoming */
4399 int i;
4400 if(maildrop)
4401 snprintf(tmp, sizeof(tmp),
4402 "Maildrop folder on \"%s\" to copy mail from%s%s%s : ",
4403 short_str(add_folder, buf, sizeof(buf), 15, EndDots),
4404 def_in_prompt[0] ? " [" : "",
4405 short_str(def_in_prompt, buf2, sizeof(buf2), 20, MidDots),
4406 def_in_prompt[0] ? "]" : "");
4407 else if(maildropfolder)
4408 snprintf(tmp, sizeof(tmp),
4409 "Folder on \"%s\" to copy mail to%s%s%s : ",
4410 short_str(add_folder, buf, sizeof(buf), 20, EndDots),
4411 def_in_prompt[0] ? " [" : "",
4412 short_str(def_in_prompt, buf2, sizeof(buf2), 20, MidDots),
4413 def_in_prompt[0] ? "]" : "");
4414 else if(inbox)
4415 snprintf(tmp, sizeof(tmp),
4416 "Folder on \"%s\" to use for INBOX%s%s%s : ",
4417 short_str(add_folder, buf, sizeof(buf), 20, EndDots),
4418 def_in_prompt[0] ? " [" : "",
4419 short_str(def_in_prompt, buf2, sizeof(buf2), 20, MidDots),
4420 def_in_prompt[0] ? "]" : "");
4421 else
4422 snprintf(tmp, sizeof(tmp),
4423 "Folder on \"%s\" to add%s%s%s : ",
4424 short_str(add_folder, buf, sizeof(buf), 25, EndDots),
4425 def_in_prompt[0] ? " [" : "",
4426 short_str(def_in_prompt, buf2, sizeof(buf2), 20, MidDots),
4427 def_in_prompt[0] ? "]" : "");
4429 tmp[sizeof(tmp)-1] = '\0';
4431 for(i = offset;i >= 0; i--)
4432 add_folder[i+1] = add_folder[i];
4434 add_folder[0] = '{';
4435 add_folder[++offset] = '}';
4436 add_folder[++offset] = '\0'; /* +2, total */
4438 else{
4439 if(maildrop)
4440 snprintf(tmp, sizeof(tmp),
4441 "Folder to copy mail from%s%s%s : ",
4442 def_in_prompt[0] ? " [" : "",
4443 short_str(def_in_prompt, buf2, sizeof(buf2), 20, MidDots),
4444 def_in_prompt[0] ? "]" : "");
4445 else if(maildropfolder)
4446 snprintf(tmp, sizeof(tmp),
4447 "Folder to copy mail to%s%s%s : ",
4448 def_in_prompt[0] ? " [" : "",
4449 short_str(def_in_prompt, buf2, sizeof(buf2), 20, MidDots),
4450 def_in_prompt[0] ? "]" : "");
4451 else if(inbox)
4452 snprintf(tmp, sizeof(tmp),
4453 "Folder name to use for INBOX%s%s%s : ",
4454 def_in_prompt[0] ? " [" : "",
4455 short_str(def_in_prompt, buf2, sizeof(buf2), 20, MidDots),
4456 def_in_prompt[0] ? "]" : "");
4457 else
4458 snprintf(tmp, sizeof(tmp),
4459 "Folder name to add%s%s%s : ",
4460 def_in_prompt[0] ? " [" : "",
4461 short_str(def_in_prompt, buf2, sizeof(buf2), 20, MidDots),
4462 def_in_prompt[0] ? "]" : "");
4464 tmp[sizeof(tmp)-1] = '\0';
4467 help = NO_HELP;
4468 while(1){
4470 p = NULL;
4471 if(isdir){
4472 add_key[0].label = N_("Create Folder");
4473 if(tmp[0] == 'F')
4474 rplstr(tmp, sizeof(tmp), 6, N_("Directory"));
4476 else{
4477 add_key[0].label = N_("Create Directory");
4478 if(tmp[0] == 'D')
4479 rplstr(tmp, sizeof(tmp), 9, N_("Folder"));
4482 flags = OE_APPEND_CURRENT;
4483 rc = optionally_enter(&add_folder[offset], -FOOTER_ROWS(ps_global), 0,
4484 add_folderlen - offset, tmp,
4485 ((context->dir->delim) && !maildrop)
4486 ? add_key : NULL,
4487 help, &flags);
4488 /* use default */
4489 if(rc == 0 && def_in_prompt[0] && !add_folder[offset]){
4490 strncpy(&add_folder[offset], def_in_prompt, add_folderlen-offset);
4491 add_folder[add_folderlen-1] = '\0';
4494 removing_leading_and_trailing_white_space(&add_folder[offset]);
4496 /* for non-local folders, transform UTF-8 to MUTF7 */
4497 if(offset > 0 && add_folder[0] == '{'){
4498 unsigned char *mutf7 = utf8_to_mutf7((unsigned char *)&add_folder[offset]);
4499 if(mutf7 != NULL){
4500 strncpy(orig_folder, &add_folder[offset], 2*MAXFOLDER+10);
4501 strncpy(&add_folder[offset], (char *) mutf7, add_folderlen-offset);
4502 add_folder[add_folderlen-1] = '\0';
4503 fs_give((void **)&mutf7);
4507 if(rc == 0 && !(inbox || context->use & CNTXT_INCMNG)
4508 && check_for_move_mbox(add_folder, NULL, 0L, NULL)){
4509 q_status_message(SM_ORDER, 6, 6,
4510 _("#move folders may only be the INBOX or in the Incoming Collection"));
4511 display_message(NO_OP_COMMAND);
4512 continue;
4515 if(rc == 0 && add_folder[offset]){
4516 if(F_OFF(F_ENABLE_DOT_FOLDERS,ps_global)
4517 && add_folder[offset] == '.'){
4518 if(cnt++ <= 0)
4519 q_status_message(SM_ORDER,3,3,
4520 _("Folder name can't begin with dot"));
4521 else
4522 q_status_message1(SM_ORDER,3,3,
4523 _("Config feature \"%s\" enables names beginning with dot"),
4524 pretty_feature_name(feature_list_name(F_ENABLE_DOT_FOLDERS), -1));
4526 display_message(NO_OP_COMMAND);
4527 continue;
4530 /* if last char is delim, blast from new folder */
4531 for(p = &add_folder[offset]; *p && *(p + 1); p++)
4532 ; /* find last char in folder */
4534 if(isdir){
4535 if(*p && *p != context->dir->delim){
4536 *++p = context->dir->delim;
4537 *(p+1) = '\0';
4540 if(F_ON(F_QUELL_EMPTY_DIRS, ps_global))
4541 require_a_subfolder++;
4543 else if(*p == context->dir->delim){
4544 q_status_message(SM_ORDER|SM_DING, 3, 3,
4545 _("Can't have trailing directory delimiters!"));
4546 display_message('X');
4547 continue;
4550 break;
4553 if(rc == 12){
4554 isdir = !isdir; /* toggle directory creation */
4556 else if(rc == 3){
4557 helper(h_incoming_add_folder_name, _("HELP FOR FOLDER NAME "),
4558 HLPD_SIMPLE);
4560 else if(rc == 1 || add_folder[0] == '\0') {
4561 q_status_message(SM_ORDER,0,2,
4562 inbox ? _("INBOX change cancelled")
4563 : _("Addition of new folder cancelled"));
4564 if(maildropfolder)
4565 fs_give((void **) &maildropfolder);
4567 if(inbox && def){
4568 if(default_mail_drop_host)
4569 fs_give((void **) &default_mail_drop_host);
4571 if(default_mail_drop_folder)
4572 fs_give((void **) &default_mail_drop_folder);
4574 if(default_dstn_host)
4575 fs_give((void **) &default_dstn_host);
4577 if(default_dstn_folder)
4578 fs_give((void **) &default_dstn_folder);
4581 return(FALSE);
4585 if(maildrop && !maildropfolder){
4586 maildropfolder = cpystr(add_folder);
4587 maildrop = 0;
4588 goto get_folder_name;
4591 skip_over_folder_input:
4593 if(require_a_subfolder){
4594 /* add subfolder name to directory name */
4595 offset = strlen(add_folder);
4596 tmp[0] = '\0';
4598 if(offset > 0){ /* it had better be */
4599 char save_delim;
4601 save_delim = add_folder[offset-1];
4602 add_folder[offset-1] = '\0';
4604 snprintf(tmp, sizeof(tmp),
4605 "Name of subfolder to add in \"%s\" : ",
4606 short_str(add_folder, buf, sizeof(buf), 15, FrontDots));
4608 tmp[sizeof(tmp)-1] = '\0';
4609 add_folder[offset-1] = save_delim;
4612 while(1){
4613 flags = OE_APPEND_CURRENT;
4614 rc = optionally_enter(&add_folder[offset], -FOOTER_ROWS(ps_global), 0,
4615 add_folderlen - offset, tmp,
4616 NULL, NO_HELP, &flags);
4618 removing_leading_and_trailing_white_space(&add_folder[offset]);
4620 /* for non-local folders, transform UTF-8 to MUTF7 */
4621 if(offset > 0 && add_folder[0] == '{'){
4622 unsigned char *mutf7 = utf8_to_mutf7((unsigned char *)&add_folder[offset]);
4623 if(mutf7 != NULL){
4624 strncpy(orig_folder, &add_folder[offset], 2*MAXFOLDER+10);
4625 strncpy(&add_folder[offset], (char *) mutf7, add_folderlen-offset);
4626 add_folder[add_folderlen-1] = '\0';
4627 fs_give((void **)&mutf7);
4631 /* use default */
4632 if(rc == 0 && !add_folder[offset]){
4633 q_status_message(SM_ORDER, 4, 4,
4634 _("A subfolder name is required, there is no default subfolder name"));
4635 continue;
4638 if(rc == 0 && add_folder[offset]){
4639 break;
4642 if(rc == 3){
4643 helper(h_emptydir_subfolder_name, _("HELP FOR SUBFOLDER NAME "),
4644 HLPD_SIMPLE);
4646 else if(rc == 1 || add_folder[0] == '\0') {
4647 q_status_message(SM_ORDER,0,2, _("Addition of new folder cancelled"));
4648 return(FALSE);
4652 /* the directory is implicit now */
4653 isdir = 0;
4656 if(context == ps_global->context_list
4657 && !(context->dir && context->dir->ref)
4658 && !strucmp(ps_global->inbox_name, add_folder)){
4659 q_status_message1(SM_ORDER,3,3,
4660 _("Cannot add folder %s in current context"),
4661 add_folder);
4662 return(FALSE);
4665 create_stream = sp_stream_get(context_apply(ctmp, context, add_folder,
4666 sizeof(ctmp)),
4667 SP_SAME);
4669 if(!create_stream && possible_stream)
4670 create_stream = context_same_stream(context, add_folder, possible_stream);
4672 help = NO_HELP;
4673 if(!inbox && context->use & CNTXT_INCMNG){
4674 snprintf(tmp, sizeof(tmp), _("Nickname for folder \"%s\" : "), orig_folder[0] ? orig_folder : &add_folder[offset]);
4675 tmp[sizeof(tmp)-1] = '\0';
4676 while(1){
4677 int flags = OE_APPEND_CURRENT;
4679 rc = optionally_enter(nickname, -FOOTER_ROWS(ps_global), 0,
4680 sizeof(nickname), tmp, NULL, help, &flags);
4681 removing_leading_and_trailing_white_space(nickname);
4682 if(rc == 0){
4683 if(strucmp(ps_global->inbox_name, nickname))
4684 break;
4685 else{
4686 q_status_message1(SM_ORDER | SM_DING, 3, 3,
4687 _("Nickname cannot be \"%s\""), nickname);
4691 if(rc == 3){
4692 help = help == NO_HELP
4693 ? h_incoming_add_folder_nickname : NO_HELP;
4695 else if(rc == 1 || (rc != 3 && !*nickname)){
4696 q_status_message(SM_ORDER,0,2,
4697 inbox ? _("INBOX change cancelled")
4698 : _("Addition of new folder cancelled"));
4699 if(maildropfolder)
4700 fs_give((void **) &maildropfolder);
4702 if(inbox && def){
4703 if(default_mail_drop_host)
4704 fs_give((void **) &default_mail_drop_host);
4706 if(default_mail_drop_folder)
4707 fs_give((void **) &default_mail_drop_folder);
4709 if(default_dstn_host)
4710 fs_give((void **) &default_dstn_host);
4712 if(default_dstn_folder)
4713 fs_give((void **) &default_dstn_folder);
4716 return(FALSE);
4721 * Already exist? First, make sure this name won't collide with
4722 * anything else in the list. Next, quickly test to see if it
4723 * the actual mailbox exists so we know any errors from
4724 * context_create() are really bad...
4726 for(offset = 0; offset < folder_total(FOLDERS(context)); offset++){
4727 f = folder_entry(offset, FOLDERS(context));
4728 if(!strucmp(FLDR_NAME(f), nickname[0] ? nickname : add_folder)){
4729 q_status_message1(SM_ORDER | SM_DING, 0, 3,
4730 _("Incoming folder \"%s\" already exists"),
4731 nickname[0] ? nickname : add_folder);
4732 if(maildropfolder)
4733 fs_give((void **) &maildropfolder);
4735 if(inbox && def){
4736 if(default_mail_drop_host)
4737 fs_give((void **) &default_mail_drop_host);
4739 if(default_mail_drop_folder)
4740 fs_give((void **) &default_mail_drop_folder);
4742 if(default_dstn_host)
4743 fs_give((void **) &default_dstn_host);
4745 if(default_dstn_folder)
4746 fs_give((void **) &default_dstn_folder);
4749 return(FALSE);
4753 ps_global->c_client_error[0] = '\0';
4754 exists = folder_exists(context, add_folder);
4755 if(exists == FEX_ERROR){
4756 if(ps_global->c_client_error[0] != '\0')
4757 q_status_message1(SM_ORDER, 3, 3, "%s",
4758 ps_global->c_client_error);
4759 else
4760 q_status_message1(SM_ORDER, 3, 3, _("Error checking for %s"), add_folder);
4763 else if(!inbox)
4764 exists = FEX_NOENT;
4765 else{
4766 exists = FEX_ISFILE;
4768 * If inbox is a maildropfolder, try to create the destination
4769 * folder. But it shouldn't cause a fatal error.
4771 if(maildropfolder && (folder_exists(NULL, add_folder) == FEX_NOENT))
4772 context_create(NULL, NULL, add_folder);
4775 if(exists == FEX_ERROR
4776 || (exists == FEX_NOENT
4777 && !context_create(context, create_stream, add_folder)
4778 && !((context->use & CNTXT_INCMNG)
4779 && !context_isambig(add_folder)))){
4780 if(maildropfolder)
4781 fs_give((void **) &maildropfolder);
4783 if(inbox && def){
4784 if(default_mail_drop_host)
4785 fs_give((void **) &default_mail_drop_host);
4787 if(default_mail_drop_folder)
4788 fs_give((void **) &default_mail_drop_folder);
4790 if(default_dstn_host)
4791 fs_give((void **) &default_dstn_host);
4793 if(default_dstn_folder)
4794 fs_give((void **) &default_dstn_folder);
4797 return(FALSE); /* c-client should've reported error */
4800 if(isdir && p) /* whack off trailing delim */
4801 *p = '\0';
4803 if(inbox || context->use & CNTXT_INCMNG){
4804 char **apval;
4805 char ***alval;
4807 if(inbox){
4808 apval = APVAL(&ps_global->vars[varnum], which);
4809 if(apval && *apval)
4810 fs_give((void **) apval);
4812 else{
4813 alval = ALVAL(&ps_global->vars[varnum], which);
4814 if(!*alval){
4815 offset = 0;
4816 *alval = (char **) fs_get(2*sizeof(char *));
4818 else{
4819 for(offset=0; (*alval)[offset]; offset++)
4822 fs_resize((void **) alval, (offset + 2) * sizeof(char *));
4827 * If we're using a Mail Drop we have to assemble the correct
4828 * c-client string to do that. Mail drop syntax looks like
4830 * #move <DELIM> <FromMailbox> <DELIM> <ToMailbox>
4832 * DELIM is any character which does not appear in either of
4833 * the mailbox names.
4835 * And then the nickname is still in front of that mess.
4837 if(maildropfolder){
4838 char *delims = " +-_:!|ABCDEFGHIJKLMNOPQRSTUVWXYZ";
4839 char *c;
4840 size_t len;
4842 len = 5 + 2 + strlen(maildropfolder) + strlen(add_folder);
4843 maildroplongname = (char *) fs_get((len+1) * sizeof(char));
4845 for(c = delims; *c; c++){
4846 if(!strindex(maildropfolder, *c) &&
4847 !strindex(add_folder, *c))
4848 break;
4851 if(*c){
4852 snprintf(maildroplongname, len+1, "#move%c%s%c%s",
4853 *c, maildropfolder, *c, add_folder);
4854 if(strlen(maildroplongname) < add_folderlen){
4855 strncpy(add_folder, maildroplongname, add_folderlen);
4856 add_folder[add_folderlen-1] = '\0';
4859 else{
4860 q_status_message2(SM_ORDER,0,2,
4861 "Can't find delimiter for \"#move %s %s\"",
4862 maildropfolder, add_folder);
4863 dprint((2,
4864 "Can't find delimiter for \"#move %s %s\"",
4865 maildropfolder ? maildropfolder : "?",
4866 add_folder ? add_folder : "?"));
4868 if(maildropfolder)
4869 fs_give((void **) &maildropfolder);
4871 if(inbox && def){
4872 if(default_mail_drop_host)
4873 fs_give((void **) &default_mail_drop_host);
4875 if(default_mail_drop_folder)
4876 fs_give((void **) &default_mail_drop_folder);
4878 if(default_dstn_host)
4879 fs_give((void **) &default_dstn_host);
4881 if(default_dstn_folder)
4882 fs_give((void **) &default_dstn_folder);
4885 return(FALSE);
4888 if(maildroplongname)
4889 fs_give((void **) &maildroplongname);
4892 if(inbox)
4893 *apval = cpystr(add_folder);
4894 else{
4895 (*alval)[offset] = put_pair(nickname, add_folder);
4896 (*alval)[offset+1] = NULL;
4899 set_current_val(&ps_global->vars[varnum], TRUE, FALSE);
4900 if(prc)
4901 prc->outstanding_pinerc_changes = 1;
4903 if(context->use & CNTXT_INCMNG){
4904 if(!inbox && add_folder && add_folder[0] && alval && *alval && (*alval)[offset]){
4906 * Instead of re-initing we try to insert the
4907 * added folder so that we preserve the last_unseen_update
4908 * information.
4910 f = new_folder(add_folder, line_hash((*alval)[offset]));
4911 f->isfolder = 1;
4912 if(nickname && nickname[0]){
4913 f->nickname = cpystr(nickname);
4914 f->name_len = strlen(f->nickname);
4917 if(F_ON(F_ENABLE_INCOMING_CHECKING, ps_global)
4918 && !ps_global->VAR_INCCHECKLIST)
4919 f->last_unseen_update = LUU_INIT;
4920 else
4921 f->last_unseen_update = LUU_NEVERCHK;
4923 folder_insert(folder_total(FOLDERS(context)), f, FOLDERS(context));
4925 else
4926 /* re-init to make sure we got it right */
4927 reinit_incoming_folder_list(ps_global, context);
4930 if(nickname[0]){
4931 strncpy(add_folder, nickname, add_folderlen-1); /* known by new name */
4932 add_folder[add_folderlen-1] = '\0';
4935 if(!inbox)
4936 q_status_message1(SM_ORDER, 0, 3, "Folder \"%s\" created",
4937 orig_folder[0] ? orig_folder : add_folder);
4938 return_val = add_folder;
4940 else if(context_isambig(add_folder)){
4941 free_folder_list(context);
4942 q_status_message2(SM_ORDER, 0, 3, "%s \"%s\" created",
4943 isdir ? "Directory" : "Folder", orig_folder[0] ? orig_folder : add_folder);
4944 return_val = add_folder;
4946 else
4947 q_status_message1(SM_ORDER, 0, 3,
4948 "Folder \"%s\" created outside current collection",
4949 orig_folder[0] ? orig_folder : add_folder);
4951 if(maildropfolder)
4952 fs_give((void **) &maildropfolder);
4954 if(inbox && def){
4955 if(default_mail_drop_host)
4956 fs_give((void **) &default_mail_drop_host);
4958 if(default_mail_drop_folder)
4959 fs_give((void **) &default_mail_drop_folder);
4961 if(default_dstn_host)
4962 fs_give((void **) &default_dstn_host);
4964 if(default_dstn_folder)
4965 fs_give((void **) &default_dstn_folder);
4968 return(return_val != NULL);
4972 /*----------------------------------------------------------------------
4973 Subscribe to a news group
4975 Args: folder -- last folder added
4976 cntxt -- The context the subscription is for
4978 Result: returns the name of the folder subscribed too
4981 This builds a complete context for the entire list of possible news groups.
4982 It also build a context to find the newly created news groups as
4983 determined by data kept in .pinerc. When the find of these new groups is
4984 done the subscribed context is searched and the items marked as new.
4985 A list of new board is never actually created.
4987 ----*/
4989 group_subscription(char *folder, size_t len, CONTEXT_S *cntxt)
4991 STRLIST_S *folders = NULL;
4992 int rc = 0, last_rc, i, n, flags,
4993 last_find_partial = 0, we_cancel = 0;
4994 CONTEXT_S subscribe_cntxt;
4995 HelpType help;
4996 ESCKEY_S subscribe_keys[3];
4998 subscribe_keys[i = 0].ch = ctrl('T');
4999 subscribe_keys[i].rval = 12;
5000 subscribe_keys[i].name = "^T";
5001 subscribe_keys[i++].label = N_("To All Grps");
5003 if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
5004 subscribe_keys[i].ch = ctrl('I');
5005 subscribe_keys[i].rval = 11;
5006 subscribe_keys[i].name = "TAB";
5007 subscribe_keys[i++].label = N_("Complete");
5010 subscribe_keys[i].ch = -1;
5012 /*---- Build a context to find all news groups -----*/
5014 subscribe_cntxt = *cntxt;
5015 subscribe_cntxt.use |= CNTXT_FINDALL;
5016 subscribe_cntxt.use &= ~(CNTXT_PSEUDO | CNTXT_ZOOM | CNTXT_PRESRV);
5017 subscribe_cntxt.next = NULL;
5018 subscribe_cntxt.prev = NULL;
5019 subscribe_cntxt.dir = new_fdir(cntxt->dir->ref,
5020 cntxt->dir->view.internal, '*');
5021 FOLDERS(&subscribe_cntxt) = init_folder_entries();
5023 * Prompt for group name.
5025 folder[0] = '\0';
5026 help = NO_HELP;
5027 while(1){
5028 flags = OE_APPEND_CURRENT;
5029 last_rc = rc;
5030 rc = optionally_enter(folder, -FOOTER_ROWS(ps_global), 0, len,
5031 SUBSCRIBE_PMT, subscribe_keys, help, &flags);
5032 removing_trailing_white_space(folder);
5033 removing_leading_white_space(folder);
5034 if((rc == 0 && folder[0]) || rc == 11 || rc == 12){
5035 we_cancel = busy_cue(_("Fetching newsgroup list"), NULL, 1);
5037 if(last_find_partial){
5038 /* clean up any previous find results */
5039 free_folder_list(&subscribe_cntxt);
5040 last_find_partial = 0;
5043 if(rc == 11){ /* Tab completion! */
5044 if(folder_complete_internal(&subscribe_cntxt,
5045 folder, len, &n, FC_FORCE_LIST)){
5046 continue;
5048 else{
5049 if(!(n && last_rc == 11 && !(flags & OE_USER_MODIFIED))){
5050 Writechar(BELL, 0);
5051 continue;
5056 if(rc == 12){ /* list the whole enchilada */
5057 build_folder_list(NULL, &subscribe_cntxt, NULL, NULL,BFL_NONE);
5059 else if(strlen(folder)){
5060 char tmp[MAILTMPLEN];
5062 snprintf(tmp, sizeof(tmp), "%s%.*s*", (rc == 11) ? "" : "*",
5063 MAILTMPLEN-3, folder);
5064 tmp[sizeof(tmp)-1] = '\0';
5065 build_folder_list(NULL, &subscribe_cntxt, tmp, NULL, BFL_NONE);
5066 subscribe_cntxt.dir->status &= ~(CNTXT_PARTFIND|CNTXT_NOFIND);
5068 else{
5069 q_status_message(SM_ORDER, 0, 2,
5070 _("No group substring to match! Use ^T to list all news groups."));
5071 continue;
5075 * If we did a partial find on matches, then we faked a full
5076 * find which will cause this to just return.
5078 if((i = folder_total(FOLDERS(&subscribe_cntxt))) != 0){
5079 char *f;
5082 * fake that we've found everything there is to find...
5084 subscribe_cntxt.use &= ~(CNTXT_NOFIND|CNTXT_PARTFIND);
5085 last_find_partial = 1;
5087 if(i == 1){
5088 f = folder_entry(0, FOLDERS(&subscribe_cntxt))->name;
5089 if(!strcmp(f, folder)){
5090 rc = 1; /* success! */
5091 break;
5093 else{ /* else complete the group */
5094 strncpy(folder, f, len-1);
5095 folder[len-1] = '\0';
5096 continue;
5099 else if(!(flags & OE_USER_MODIFIED)){
5101 * See if there wasn't an exact match in the lot.
5103 while(i-- > 0){
5104 f = folder_entry(i,FOLDERS(&subscribe_cntxt))->name;
5105 if(!strcmp(f, folder))
5106 break;
5107 else
5108 f = NULL;
5111 /* if so, then the user picked it from the list the
5112 * last time and didn't change it at the prompt.
5113 * Must mean they're accepting it...
5115 if(f){
5116 rc = 1; /* success! */
5117 break;
5121 else{
5122 if(rc == 12)
5123 q_status_message(SM_ORDER | SM_DING, 3, 3,
5124 _("No groups to select from!"));
5125 else
5126 q_status_message1(SM_ORDER, 3, 3,
5127 _("News group \"%s\" didn't match any existing groups"),
5128 folder);
5129 free_folder_list(&subscribe_cntxt);
5131 continue;
5134 /*----- Mark groups that are currently subscribed too ------*/
5135 /* but first make sure they're found */
5136 build_folder_list(NULL, cntxt, "*", NULL, BFL_LSUB);
5137 for(i = 0 ; i < folder_total(FOLDERS(&subscribe_cntxt)); i++) {
5138 FOLDER_S *f = folder_entry(i, FOLDERS(&subscribe_cntxt));
5140 f->subscribed = search_folder_list(FOLDERS(cntxt),
5141 f->name) != 0;
5144 if(we_cancel)
5145 cancel_busy_cue(-1);
5147 /*----- Call the folder lister to do all the work -----*/
5148 folders = folders_for_subscribe(ps_global,
5149 &subscribe_cntxt, folder);
5151 if(folders){
5152 /* Multiple newsgroups OR Auto-select */
5153 if(folders->next || F_ON(F_SELECT_WO_CONFIRM,ps_global))
5154 break;
5156 strncpy(folder, (char *) folders->name, len-1);
5157 folder[len-1] = '\0';
5158 free_strlist(&folders);
5161 else if(rc == 3){
5162 help = help == NO_HELP ? h_news_subscribe : NO_HELP;
5164 else if(rc == 1 || folder[0] == '\0'){
5165 rc = -1;
5166 break;
5170 if(rc < 0){
5171 folder[0] = '\0'; /* make sure not to return partials */
5172 if(rc == -1)
5173 q_status_message(SM_ORDER, 0, 3, _("Subscribe cancelled"));
5175 else{
5176 MAILSTREAM *sub_stream;
5177 int sclose = 0, errors = 0;
5179 if(folders){ /*------ Actually do the subscription -----*/
5180 STRLIST_S *flp;
5181 int n = 0;
5183 /* subscribe one at a time */
5184 folder[0] = '\0';
5186 * Open stream before subscribing so c-client knows what newsrc
5187 * to use, along with other side-effects.
5189 if((sub_stream = mail_cmd_stream(&subscribe_cntxt, &sclose)) != NULL){
5190 for(flp = folders; flp; flp = flp->next){
5191 (void) context_apply(tmp_20k_buf, &subscribe_cntxt,
5192 (char *) flp->name, SIZEOF_20KBUF);
5193 if(mail_subscribe(sub_stream, tmp_20k_buf) == 0L){
5195 * This message may not make it to the screen,
5196 * because a c-client message about the failure
5197 * will be there. Probably best not to string
5198 * together a whole bunch of errors if there is
5199 * something wrong.
5201 q_status_message1(errors ?SM_INFO : SM_ORDER,
5202 errors ? 0 : 3, 3,
5203 _("Error subscribing to \"%s\""),
5204 (char *) flp->name);
5205 errors++;
5207 else{
5208 n++;
5209 if(!folder[0]){
5210 strncpy(folder, (char *) flp->name, len-1);
5211 folder[len-1] = '\0';
5214 /*---- Update the screen display data structures -----*/
5215 if(ALL_FOUND(cntxt)){
5216 if(cntxt->use & CNTXT_PSEUDO){
5217 folder_delete(0, FOLDERS(cntxt));
5218 cntxt->use &= ~CNTXT_PSEUDO;
5221 folder_insert(-1, new_folder((char *) flp->name, 0),
5222 FOLDERS(cntxt));
5226 if(sclose)
5227 pine_mail_close(sub_stream);
5229 else
5230 errors++;
5232 if(n == 0)
5233 q_status_message(SM_ORDER | SM_DING, 3, 5,
5234 _("Subscriptions failed, subscribed to no new groups"));
5235 else
5236 q_status_message3(SM_ORDER | (errors ? SM_DING : 0),
5237 errors ? 3 : 0,3,
5238 "Subscribed to %s new groups%s%s",
5239 comatose((long)n),
5240 errors ? ", failed on " : "",
5241 errors ? comatose((long)errors) : "");
5243 free_strlist(&folders);
5245 else{
5246 if((sub_stream = mail_cmd_stream(&subscribe_cntxt, &sclose)) != NULL){
5247 (void) context_apply(tmp_20k_buf, &subscribe_cntxt, folder,
5248 SIZEOF_20KBUF);
5249 if(mail_subscribe(sub_stream, tmp_20k_buf) == 0L){
5250 q_status_message1(SM_ORDER | SM_DING, 3, 3,
5251 _("Error subscribing to \"%s\""), folder);
5252 errors++;
5254 else if(ALL_FOUND(cntxt)){
5255 /*---- Update the screen display data structures -----*/
5256 if(cntxt->use & CNTXT_PSEUDO){
5257 folder_delete(0, FOLDERS(cntxt));
5258 cntxt->use &= ~CNTXT_PSEUDO;
5261 folder_insert(-1, new_folder(folder, 0), FOLDERS(cntxt));
5263 if(sclose)
5264 pine_mail_close(sub_stream);
5266 else{
5267 q_status_message1(SM_ORDER | SM_DING, 3, 3,
5268 _("Error subscribing to \"%s\""), folder);
5269 errors++;
5273 if(!errors && folder[0])
5274 q_status_message1(SM_ORDER, 0, 3, _("Subscribed to \"%s\""), folder);
5277 free_fdir(&subscribe_cntxt.dir, 1);
5278 return(folder[0]);
5282 /*----------------------------------------------------------------------
5283 Rename folder
5285 Args: new_name -- buffer to contain new file name
5286 len -- length of new_name buffer
5287 index -- index of folder in folder list to rename
5288 context -- collection of folders making up folder list
5289 possible_stream -- may be able to use this stream
5291 Result: returns the new name of the folder, or NULL if nothing happened.
5293 When either the sent-mail or saved-message folders are renamed, immediately
5294 create a new one in their place so they always exist. The main loop above also
5295 detects this and makes the rename look like an add of the sent-mail or
5296 saved-messages folder. (This behavior may not be optimal, but it keeps things
5297 consistent.
5299 ----*/
5301 rename_folder(CONTEXT_S *context, int index, char *new_name, size_t len, MAILSTREAM *possible_stream)
5303 char *folder, prompt[64], *name_p = NULL;
5304 HelpType help;
5305 FOLDER_S *new_f;
5306 PINERC_S *prc = NULL;
5307 int rc, ren_cur, cnt = 0, isdir = 0, readonly = 0;
5308 EditWhich ew;
5309 MAILSTREAM *strm;
5311 dprint((4, "\n - rename folder -\n"));
5313 if(NEWS_TEST(context)){
5314 q_status_message(SM_ORDER | SM_DING, 3, 3,
5315 _("Can't rename news groups!"));
5316 return(0);
5318 else if(!folder_total(FOLDERS(context))){
5319 q_status_message(SM_ORDER | SM_DING, 0, 4,
5320 _("Empty folder collection. No folder to rename!"));
5321 return(0);
5323 else if((new_f = folder_entry(index, FOLDERS(context)))
5324 && context == ps_global->context_list
5325 && !(context->dir && context->dir->ref)
5326 && !strucmp(FLDR_NAME(new_f), ps_global->inbox_name)) {
5327 q_status_message1(SM_ORDER | SM_DING, 3, 4,
5328 _("Can't change special folder name \"%s\""),
5329 ps_global->inbox_name);
5330 return(0);
5333 ew = config_containing_inc_fldr(new_f);
5334 if(ps_global->restricted)
5335 readonly = 1;
5336 else{
5337 switch(ew){
5338 case Main:
5339 prc = ps_global->prc;
5340 break;
5341 case Post:
5342 prc = ps_global->post_prc;
5343 break;
5344 case None:
5345 break;
5348 readonly = prc ? prc->readonly : 1;
5351 if(prc && prc->quit_to_edit && (context->use & CNTXT_INCMNG)){
5352 quit_to_edit_msg(prc);
5353 return(0);
5356 if(readonly && (context->use & CNTXT_INCMNG)){
5357 q_status_message(SM_ORDER,3,5,
5358 _("Rename cancelled: folder not in editable config file"));
5359 return(0);
5362 if(context->use & CNTXT_INCMNG){
5363 if(!(folder = new_f->nickname))
5364 folder = ""; /* blank nickname */
5366 else
5367 folder = FLDR_NAME(new_f);
5369 ren_cur = strcmp(folder, ps_global->cur_folder) == 0;
5371 snprintf(prompt, sizeof(prompt), "%s %s to : ", _("Rename"),
5372 (context->use & CNTXT_INCMNG)
5373 ? _("nickname")
5374 : (isdir = new_f->isdir)
5375 ? _("directory") : _("folder"));
5376 prompt[sizeof(prompt)-1] = '\0';
5377 help = NO_HELP;
5378 strncpy(new_name, folder, len-1);
5379 new_name[len-1] = '\0';
5380 while(1) {
5381 int flags = OE_APPEND_CURRENT;
5383 rc = optionally_enter(new_name, -FOOTER_ROWS(ps_global), 0,
5384 len, prompt, NULL, help, &flags);
5385 if(rc == 3) {
5386 help = help == NO_HELP ? h_oe_foldrename : NO_HELP;
5387 continue;
5390 removing_leading_and_trailing_white_space(new_name);
5392 if(rc == 0 && (*new_name || (context->use & CNTXT_INCMNG))) {
5393 /* verify characters */
5394 if(F_OFF(F_ENABLE_DOT_FOLDERS,ps_global) && *new_name == '.'){
5395 if(cnt++ <= 0)
5396 q_status_message(SM_ORDER,3,3,
5397 _("Folder name can't begin with dot"));
5398 else
5399 q_status_message1(SM_ORDER,3,3,
5400 _("Config feature \"s\" enables names beginning with dot"),
5401 pretty_feature_name(feature_list_name(F_ENABLE_DOT_FOLDERS), -1));
5403 display_message(NO_OP_COMMAND);
5404 continue;
5407 if(folder_index(new_name, context, FI_ANY|FI_RENAME) >= 0){
5408 q_status_message1(SM_ORDER, 3, 3,
5409 _("Folder \"%s\" already exists"),
5410 pretty_fn(new_name));
5411 display_message(NO_OP_COMMAND);
5412 continue;
5414 else if(context == ps_global->context_list
5415 && !(context->dir && context->dir->ref)
5416 && !strucmp(new_name, ps_global->inbox_name)){
5417 if(context->use & CNTXT_INCMNG)
5418 q_status_message1(SM_ORDER, 3, 3, _("Can't rename incoming folder to %s"),
5419 new_name);
5420 else
5421 q_status_message1(SM_ORDER, 3, 3, _("Can't rename folder to %s"),
5422 new_name);
5424 display_message(NO_OP_COMMAND);
5425 continue;
5429 if(rc != 4) /* redraw */
5430 break; /* no redraw */
5434 if(rc != 1 && isdir){ /* add trailing delim? */
5435 for(name_p = new_name; *name_p && *(name_p+1) ; name_p++)
5438 if(*name_p == context->dir->delim) /* lop off delim */
5439 *name_p = '\0';
5442 if(rc == 1
5443 || !(*new_name || (context->use & CNTXT_INCMNG))
5444 || !strcmp(new_name, folder)){
5445 q_status_message(SM_ORDER, 0, 2, _("Folder rename cancelled"));
5446 return(0);
5449 if(context->use & CNTXT_INCMNG){
5450 char **new_list, **lp, ***alval;
5451 int i;
5453 alval = ALVAL(&ps_global->vars[V_INCOMING_FOLDERS], ew);
5454 for(i = 0; (*alval)[i]; i++)
5457 new_list = (char **) fs_get((i + 1) * sizeof(char *));
5459 for(lp = new_list, i = 0; (*alval)[i]; i++){
5460 /* figure out if this is the one we're renaming */
5461 expand_variables(tmp_20k_buf, SIZEOF_20KBUF, (*alval)[i], 0);
5462 if(new_f->varhash == line_hash(tmp_20k_buf)){
5463 char *folder_string = NULL, *nickname = NULL;
5465 if(new_f->nickname)
5466 fs_give((void **) &new_f->nickname);
5468 if(*new_name)
5469 new_f->nickname = cpystr(new_name);
5472 * Parse folder line for nickname and folder name.
5473 * No nickname on line is OK.
5475 get_pair(tmp_20k_buf, &nickname, &folder_string, 0, 0);
5477 if(nickname)
5478 fs_give((void **)&nickname);
5480 *lp = put_pair(new_name, folder_string);
5482 new_f->varhash = line_hash(*lp++);
5484 else
5485 *lp++ = cpystr((*alval)[i]);
5488 *lp = NULL;
5490 set_variable_list(V_INCOMING_FOLDERS, new_list, TRUE, ew);
5491 free_list_array(&new_list);
5493 return(1);
5496 /* Can't rename open streams */
5497 if((strm = context_already_open_stream(context, folder, AOS_NONE))
5498 || (ren_cur && (strm=ps_global->mail_stream))){
5499 if(possible_stream == strm)
5500 possible_stream = NULL;
5502 pine_mail_actually_close(strm);
5505 if(possible_stream
5506 && !context_same_stream(context, new_name, possible_stream))
5507 possible_stream = NULL;
5509 if((rc = context_rename(context, possible_stream, folder, new_name)) != 0){
5510 if(name_p && *name_p == context->dir->delim)
5511 *name_p = '\0'; /* blat trailing delim */
5513 /* insert new name? */
5514 if(!strindex(new_name, context->dir->delim)){
5515 new_f = new_folder(new_name, 0);
5516 new_f->isdir = isdir;
5517 folder_insert(-1, new_f, FOLDERS(context));
5520 if(strcmp(ps_global->VAR_DEFAULT_FCC, folder) == 0
5521 || strcmp(ps_global->VAR_DEFAULT_SAVE_FOLDER, folder) == 0) {
5522 /* renaming sent-mail or saved-messages */
5523 if(context_create(context, NULL, folder)){
5524 q_status_message3(SM_ORDER,0,3,
5525 "Folder \"%s\" renamed to \"%s\". New \"%s\" created",
5526 folder, new_name,
5527 pretty_fn(
5528 (strcmp(ps_global->VAR_DEFAULT_SAVE_FOLDER,
5529 folder) == 0)
5530 ? ps_global->VAR_DEFAULT_SAVE_FOLDER
5531 : ps_global->VAR_DEFAULT_FCC));
5534 else{
5535 q_status_message1(SM_ORDER | SM_DING, 3, 4,
5536 "Error creating new \"%s\"", folder);
5538 dprint((1, "Error creating \"%s\" in %s context\n",
5539 folder ? folder : "?",
5540 context->context ? context->context : "?"));
5543 else
5544 q_status_message2(SM_ORDER, 0, 3,
5545 "Folder \"%s\" renamed to \"%s\"",
5546 pretty_fn(folder), pretty_fn(new_name));
5548 free_folder_list(context);
5551 if(ren_cur) {
5552 if(ps_global && ps_global->ttyo){
5553 blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);
5554 ps_global->mangled_footer = 1;
5557 /* No reopen the folder we just had open */
5558 do_broach_folder(new_name, context, NULL, 0L);
5561 return(rc);
5565 /*----------------------------------------------------------------------
5566 Confirm and delete a folder
5568 Args: fs -- folder screen state
5570 Result: return 0 if not delete, 1 if deleted.
5571 ----*/
5573 delete_folder(CONTEXT_S *context, int index, char *next_folder, size_t len, MAILSTREAM **possible_streamp)
5575 char *folder, ques_buf[MAX_SCREEN_COLS+1], *target = NULL,
5576 *fnamep, buf[1000];
5577 MAILSTREAM *del_stream = NULL, *sub_stream, *strm = NULL;
5578 FOLDER_S *fp;
5579 EditWhich ew;
5580 PINERC_S *prc = NULL;
5581 int ret, unsub_opened = 0, close_opened = 0, blast_folder = 1,
5582 readonly;
5584 if(!context){
5585 cmd_cancelled("Missing context in Delete");
5586 return(0);
5589 if(NEWS_TEST(context)){
5590 folder = folder_entry(index, FOLDERS(context))->name;
5591 snprintf(ques_buf, sizeof(ques_buf), _("Really unsubscribe from \"%s\""), folder);
5592 ques_buf[sizeof(ques_buf)-1] = '\0';
5594 ret = want_to(ques_buf, 'n', 'x', NO_HELP, WT_NORM);
5595 switch(ret) {
5596 /* ^C */
5597 case 'x':
5598 Writechar(BELL, 0);
5599 /* fall through */
5600 case 'n':
5601 return(0);
5604 dprint((2, "deleting folder \"%s\" in context \"%s\"\n",
5605 folder ? folder : "?",
5606 context->context ? context->context : "?"));
5608 if((sub_stream = mail_cmd_stream(context, &unsub_opened)) != NULL){
5609 (void) context_apply(tmp_20k_buf, context, folder, SIZEOF_20KBUF);
5610 if(!mail_unsubscribe(sub_stream, tmp_20k_buf)){
5611 q_status_message1(SM_ORDER | SM_DING, 3, 3,
5612 _("Error unsubscribing from \"%s\""),folder);
5613 if(unsub_opened)
5614 pine_mail_close(sub_stream);
5615 return(0);
5617 if(unsub_opened)
5618 pine_mail_close(sub_stream);
5622 * Fix up the displayed list
5624 folder_delete(index, FOLDERS(context));
5625 return(1);
5628 fp = folder_entry(index, FOLDERS(context));
5629 if(!fp){
5630 cmd_cancelled("Can't find folder to Delete!");
5631 return(0);
5634 if(!((context->use & CNTXT_INCMNG) && fp->name
5635 && check_for_move_mbox(fp->name, NULL, 0, &target))){
5636 target = NULL;
5639 folder = FLDR_NAME(fp);
5640 dprint((4, "=== delete_folder(%s) ===\n",
5641 folder ? folder : "?"));
5643 ew = config_containing_inc_fldr(fp);
5644 if(ps_global->restricted)
5645 readonly = 1;
5646 else{
5647 switch(ew){
5648 case Main:
5649 prc = ps_global->prc;
5650 break;
5651 case Post:
5652 prc = ps_global->post_prc;
5653 break;
5654 case None:
5655 break;
5658 readonly = prc ? prc->readonly : 1;
5661 if(prc && prc->quit_to_edit && (context->use & CNTXT_INCMNG)){
5662 quit_to_edit_msg(prc);
5663 return(0);
5666 if(context == ps_global->context_list
5667 && !(context->dir && context->dir->ref)
5668 && strucmp(folder, ps_global->inbox_name) == 0){
5669 q_status_message1(SM_ORDER | SM_DING, 3, 4,
5670 _("Can't delete special folder \"%s\"."),
5671 ps_global->inbox_name);
5672 return(0);
5674 else if(readonly && (context->use & CNTXT_INCMNG)){
5675 q_status_message(SM_ORDER,3,5,
5676 _("Deletion cancelled: folder not in editable config file"));
5677 return(0);
5679 else if((fp->name
5680 && (strm=context_already_open_stream(context,fp->name,AOS_NONE)))
5682 (target
5683 && (strm=context_already_open_stream(NULL,target,AOS_NONE)))){
5684 if(strm == ps_global->mail_stream)
5685 close_opened++;
5687 else if(fp->isdir || fp->isdual){ /* NO DELETE if directory isn't EMPTY */
5688 FDIR_S *fdirp = next_folder_dir(context,folder,TRUE,possible_streamp);
5690 if(fp->haschildren)
5691 ret = 1;
5692 else if(fp->hasnochildren)
5693 ret = 0;
5694 else{
5695 ret = folder_total(fdirp->folders) > 0;
5696 free_fdir(&fdirp, 1);
5699 if(ret){
5700 q_status_message2(SM_ORDER | SM_DING, 3, 4,
5701 _("Can't delete non-empty directory \"%s\"%s."),
5702 folder, (fp->isfolder && fp->isdual) ? " (or folder of same name)" : "");
5703 return(0);
5707 * Folder by the same name exist? If so, server's probably going
5708 * to delete it as well. Punt?
5710 if(fp->isdual
5711 && (ret = want_to(DIR_FOLD_PMT,'n','x',NO_HELP,WT_NORM)) != 'y'){
5712 q_status_message(SM_ORDER,0,3, (ret == 'x') ? _("Delete cancelled")
5713 : _("No folder deleted"));
5714 return(0);
5718 if(context->use & CNTXT_INCMNG){
5719 static ESCKEY_S delf_opts[] = {
5720 {'n', 'n', "N", N_("Nickname only")},
5721 {'b', 'b', "B", N_("Both Folder and Nickname")},
5722 {-1, 0, NULL, NULL}
5724 #define DELF_PROMPT _("DELETE only Nickname or Both nickname and folder? ")
5726 switch(radio_buttons(DELF_PROMPT, -FOOTER_ROWS(ps_global),
5727 delf_opts,'n','x',NO_HELP,RB_NORM)){
5728 case 'n' :
5729 blast_folder = 0;
5730 break;
5732 case 'x' :
5733 cmd_cancelled("Delete");
5734 return(0);
5736 default :
5737 break;
5740 else{
5741 unsigned char *fname = folder_name_decoded((unsigned char *)folder);
5742 snprintf(ques_buf, sizeof(ques_buf), "DELETE \"%s\"%s", fname ? (char *) fname : folder,
5743 close_opened ? " (the currently open folder)" :
5744 (fp->isdir && !(fp->isdual || fp->isfolder
5745 || (folder_index(folder, context, FI_FOLDER) >= 0)))
5746 ? " (a directory)" : "");
5747 if(fname) fs_give((void **)&fname);
5748 ques_buf[sizeof(ques_buf)-1] = '\0';
5750 if((ret = want_to(ques_buf, 'n', 'x', NO_HELP, WT_NORM)) != 'y'){
5751 q_status_message(SM_ORDER,0,3, (ret == 'x') ? _("Delete cancelled")
5752 : _("Nothing deleted"));
5753 return(0);
5757 if(blast_folder){
5758 dprint((2,"deleting \"%s\" (%s) in context \"%s\"\n",
5759 fp->name ? fp->name : "?",
5760 fp->nickname ? fp->nickname : "",
5761 context->context ? context->context : "?"));
5762 if(strm){
5764 * Close it, NULL the pointer, and let do_broach_folder fixup
5765 * the rest...
5767 pine_mail_actually_close(strm);
5768 if(close_opened){
5769 if(ps_global && ps_global->ttyo){
5770 blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);
5771 ps_global->mangled_footer = 1;
5774 ps_global->mangled_header = 1;
5775 do_broach_folder(ps_global->inbox_name,
5776 ps_global->context_list, NULL, DB_INBOXWOCNTXT);
5781 * Use fp->name since "folder" may be a nickname...
5783 if(possible_streamp && *possible_streamp
5784 && context_same_stream(context, fp->name, *possible_streamp))
5785 del_stream = *possible_streamp;
5787 fnamep = fp->name;
5789 if(!context_delete(context, del_stream, fnamep)){
5791 * BUG: what if sent-mail or saved-messages????
5793 q_status_message1(SM_ORDER,3,3,"Delete of \"%s\" Failed!", folder);
5794 return(0);
5798 snprintf(buf, sizeof(buf), "%s\"%s\" deleted.",
5799 !blast_folder ? "Nickname " :
5800 fp->isdual ? "Folder/Directory " :
5801 (fp->isdir && fp->isfolder) ? "Folder " :
5802 fp->isdir ? "Directory " :
5803 "Folder ",
5804 folder);
5805 buf[sizeof(buf)-1] = '\0';
5807 q_status_message(SM_ORDER, 0, 3, buf);
5809 if(context->use & CNTXT_INCMNG){
5810 char **new_list, **lp, ***alval;
5811 int i;
5813 alval = ALVAL(&ps_global->vars[V_INCOMING_FOLDERS], ew);
5814 for(i = 0; (*alval)[i]; i++)
5818 * Make it one too big in case we don't find the match for
5819 * some unknown reason.
5821 new_list = (char **) fs_get((i + 1) * sizeof(char *));
5824 * Copy while figuring out which one to skip.
5826 for(lp = new_list, i = 0; (*alval)[i]; i++){
5827 expand_variables(tmp_20k_buf, SIZEOF_20KBUF, (*alval)[i], 0);
5828 if(fp->varhash != line_hash(tmp_20k_buf))
5829 *lp++ = cpystr((*alval)[i]);
5832 *lp = NULL;
5834 set_variable_list(V_INCOMING_FOLDERS, new_list, TRUE, ew);
5835 free_list_array(&new_list);
5839 * Fix up the displayed list.
5841 folder_delete(index, FOLDERS(context));
5844 * Take a guess at what should get hilited next.
5846 if(index < (ret = folder_total(FOLDERS(context)))
5847 || ((index = ret - 1) >= 0)){
5848 if((fp = folder_entry(index, FOLDERS(context)))
5849 && strlen(FLDR_NAME(fp)) < len - 1)
5850 strncpy(next_folder, FLDR_NAME(fp), len-1);
5851 next_folder[len-1] = '\0';
5854 if(!(context->use & CNTXT_INCMNG)){
5856 * Then cause the list to get rebuild 'cause we may've blasted
5857 * some folder that's also a directory or vice versa...
5859 free_folder_list(context);
5862 return(1);
5866 /*----------------------------------------------------------------------
5867 Print the list of folders on paper
5869 Args: list -- The current list of folders
5870 lens -- The list of lengths of the current folders
5871 display -- The current folder display structure
5873 Result: list printed on paper
5875 If the display list was created for 80 columns it is used, otherwise
5876 a new list is created for 80 columns
5878 ----*/
5879 void
5880 print_folders(FPROC_S *fp)
5882 if(open_printer(_("folder list")) == 0){
5883 (void) folder_list_text(ps_global, fp, print_char, NULL, 80);
5885 close_printer();
5891 scan_get_pattern(char *kind, char *pat, int len)
5893 char prompt[256];
5894 int flags;
5896 pat[0] = '\0';
5897 snprintf(prompt, sizeof(prompt), _("String in folder %s to match : "), kind);
5898 prompt[sizeof(prompt)-1] = '\0';
5900 while(1){
5901 flags = OE_APPEND_CURRENT | OE_DISALLOW_HELP;
5902 switch(optionally_enter(pat, -FOOTER_ROWS(ps_global), 0, len,
5903 prompt, NULL, NO_HELP, &flags)){
5905 case 4 :
5906 if(ps_global->redrawer)
5907 (*ps_global->redrawer)();
5909 break;
5911 case 0 :
5912 if(*pat)
5913 return(1);
5915 case 1 :
5916 cmd_cancelled("Select");
5918 default :
5919 return(0);
5926 folder_select_text(struct pine *ps, CONTEXT_S *context, int selected)
5928 char pattern[MAILTMPLEN], type = '\0';
5929 static ESCKEY_S scan_opts[] = {
5930 {'n', 'n', "N", N_("Name Select")},
5931 {'c', 'c', "C", N_("Content Select")},
5932 {-1, 0, NULL, NULL}
5935 if(context->use & CNTXT_INCMNG){
5936 q_status_message(SM_ORDER | SM_DING, 3, 3,
5937 _("Select \"Text\" not supported in Incoming Folders"));
5938 return(0);
5941 switch(radio_buttons(SEL_TEXT_PMT, -FOOTER_ROWS(ps_global),
5942 scan_opts, 'n', 'x', NO_HELP, RB_NORM)){
5943 case 'n' : /* Name search */
5944 if(scan_get_pattern("NAME", pattern, sizeof(pattern)))
5945 type = 'n';
5947 break;
5949 case 'c' : /* content search */
5950 if(scan_get_pattern("CONTENTS", pattern, sizeof(pattern)))
5951 type = 'c';
5953 break;
5955 case 'x' :
5956 default :
5957 break;
5960 if(type){
5961 int rv;
5962 char tmp[MAILTMPLEN];
5963 SCANARG_S args;
5965 memset(&args, 0, sizeof(SCANARG_S));
5966 args.pattern = pattern;
5967 args.type = type;
5968 args.context = context;
5970 if(type == 'c'){
5971 args.stream = sp_stream_get(context_apply(tmp, context,
5972 "xxx", sizeof(tmp)),
5973 SP_SAME);
5974 if(!args.stream){
5975 args.stream = pine_mail_open(NULL, tmp,
5976 OP_SILENT|OP_HALFOPEN|SP_USEPOOL|SP_TEMPUSE,
5977 NULL);
5978 args.newstream = (args.stream != NULL);
5982 rv = foreach_folder(context, selected,
5983 foreach_do_scan, (void *) &args);
5985 if(args.newstream)
5986 pine_mail_close(args.stream);
5988 if(rv)
5989 return(1);
5992 cmd_cancelled("Select");
5993 return(0);
5998 foreach_do_scan(FOLDER_S *f, void *d)
6000 SCANARG_S *sa = (SCANARG_S *) d;
6002 return((sa->type == 'n' && srchstr(FLDR_NAME(f), sa->pattern))
6003 || (sa->type == 'c'
6004 && scan_scan_folder(sa->stream, sa->context, f, sa->pattern)));
6009 scan_scan_folder(MAILSTREAM *stream, CONTEXT_S *context, FOLDER_S *f, char *pattern)
6011 MM_LIST_S ldata;
6012 LISTRES_S response;
6013 int we_cancel = 0;
6014 char *folder, *ref = NULL, tmp[MAILTMPLEN];
6016 folder = f->name;
6017 snprintf(tmp, sizeof(tmp), "Scanning \"%s\"", FLDR_NAME(f));
6018 tmp[sizeof(tmp)-1] = '\0';
6019 we_cancel = busy_cue(tmp, NULL, 1);
6021 mm_list_info = &ldata; /* tie down global reference */
6022 memset(&ldata, 0, sizeof(MM_LIST_S));
6023 ldata.filter = mail_list_response;
6024 memset(ldata.data = &response, 0, sizeof(LISTRES_S));
6027 * If no preset reference string, must be at top of context
6029 if(context && context_isambig(folder) && !(ref = context->dir->ref)){
6030 char *p;
6032 if((p = strstr(context->context, "%s")) != NULL){
6033 if(!*(p+2)){
6034 snprintf(tmp, sizeof(tmp), "%.*s", (int) MIN(p - context->context, sizeof(tmp)-1),
6035 context->context);
6036 tmp[sizeof(tmp)-1] = '\0';
6037 ref = tmp;
6039 else{
6040 snprintf(tmp, sizeof(tmp), context->context, folder);
6041 tmp[sizeof(tmp)-1] = '\0';
6042 folder = tmp;
6043 ref = "";
6046 else
6047 ref = context->context;
6050 mail_scan(stream, ref, folder, pattern);
6052 if(context && context->dir && response.delim)
6053 context->dir->delim = response.delim;
6055 if(we_cancel)
6056 cancel_busy_cue(-1);
6058 return(((response.isfile) ? FEX_ISFILE : 0)
6059 | ((response.isdir) ? FEX_ISDIR : 0));
6064 folder_select_props(struct pine *ps, CONTEXT_S *context, int selected)
6066 int cmp = 0;
6067 long flags = 0L, count;
6068 static ESCKEY_S prop_opts[] = {
6069 {'u', 'u', "U", N_("Unseen msgs")},
6070 {'n', 'n', "N", N_("New msgs")},
6071 {'c', 'c', "C", N_("msg Count")},
6072 {-1, 0, NULL, NULL}
6075 if(context->use & CNTXT_INCMNG){
6076 q_status_message(SM_ORDER | SM_DING, 3, 3,
6077 _("Select \"Properties\" not supported in Incoming Folders"));
6078 return(0);
6081 switch(radio_buttons(SEL_PROP_PMT, -FOOTER_ROWS(ps_global),
6082 prop_opts, 'n', 'x', h_folder_prop, RB_NORM)){
6083 case 'c' : /* message count */
6084 if(folder_select_count(&count, &cmp))
6085 flags = SA_MESSAGES;
6087 break;
6089 case 'n' : /* folders with new */
6090 flags = SA_RECENT;
6091 break;
6093 case 'u' : /* folders with unseen */
6094 flags = SA_UNSEEN;
6095 break;
6097 case 'x' :
6098 default :
6099 break;
6102 if(flags){
6103 int rv;
6104 char tmp[MAILTMPLEN];
6105 STATARG_S args;
6107 memset(&args, 0, sizeof(STATARG_S));
6108 args.flags = flags;
6109 args.context = context;
6110 args.nmsgs = count;
6111 args.cmp = cmp;
6113 args.stream = sp_stream_get(context_apply(tmp, context,
6114 "xxx", sizeof(tmp)),
6115 SP_SAME);
6116 if(!args.stream){
6117 args.stream = pine_mail_open(NULL, tmp,
6118 OP_SILENT|OP_HALFOPEN|SP_USEPOOL|SP_TEMPUSE,
6119 NULL);
6120 args.newstream = (args.stream != NULL);
6123 rv = foreach_folder(context, selected,
6124 foreach_do_stat, (void *) &args);
6126 if(args.newstream)
6127 pine_mail_close(args.stream);
6129 if(rv)
6130 return(1);
6133 cmd_cancelled("Select");
6134 return(0);
6139 folder_select_count(long int *count, int *cmp)
6141 int r, flags;
6142 char number[32], prompt[128];
6143 static char *tense[] = {"EQUAL TO", "LESS THAN", "GREATER THAN"};
6144 static ESCKEY_S sel_num_opt[] = {
6145 {ctrl('W'), 14, "^W", N_("Toggle Comparison")},
6146 {-1, 0, NULL, NULL}
6149 *count = 0L;
6150 while(1){
6151 flags = OE_APPEND_CURRENT | OE_DISALLOW_HELP;
6152 snprintf(number, sizeof(number), "%ld", *count);
6153 number[sizeof(number)-1] = '\0';
6154 snprintf(prompt, sizeof(prompt), "Select folders with messages %s : ", tense[*cmp]);
6155 prompt[sizeof(prompt)-1] = '\0';
6156 r = optionally_enter(number, -FOOTER_ROWS(ps_global), 0, sizeof(number),
6157 prompt, sel_num_opt, NO_HELP, &flags);
6158 switch (r){
6159 case 0 :
6160 if(!*number)
6161 break;
6162 else if((*count = atol(number)) < 0L)
6163 q_status_message(SM_ORDER, 3, 3,
6164 "Can't have NEGATIVE message count!");
6165 else
6166 return(1); /* success */
6168 case 3 : /* help */
6169 case 4 : /* redraw */
6170 continue;
6172 case 14 : /* toggle comparison */
6173 ++(*cmp);
6174 *cmp = *cmp % 3;
6175 continue;
6177 case -1 : /* cancel */
6178 default:
6179 break;
6182 break;
6185 return(0); /* return failure */
6190 foreach_do_stat(FOLDER_S *f, void *d)
6192 STATARG_S *sa = (STATARG_S *) d;
6193 int rv = 0;
6195 if(ps_global->context_current == sa->context
6196 && !strcmp(ps_global->cur_folder, FLDR_NAME(f))){
6197 switch(sa->flags){
6198 case SA_MESSAGES :
6199 switch(sa->cmp){
6200 case 0 : /* equals */
6201 if(ps_global->mail_stream->nmsgs == sa->nmsgs)
6202 rv = 1;
6204 break;
6206 case 1 : /* less than */
6207 if(ps_global->mail_stream->nmsgs < sa->nmsgs)
6208 rv = 1;
6210 break;
6212 case 2 :
6213 if(ps_global->mail_stream->nmsgs > sa->nmsgs)
6214 rv = 1;
6216 break;
6218 default :
6219 break;
6222 break;
6224 case SA_RECENT :
6225 if(count_flagged(ps_global->mail_stream, F_RECENT))
6226 rv = 1;
6228 break;
6230 case SA_UNSEEN :
6231 if(count_flagged(ps_global->mail_stream, F_UNSEEN))
6232 rv = 1;
6234 break;
6236 default :
6237 break;
6240 else{
6241 int we_cancel = 0;
6242 char msg_buf[MAX_BM+1];
6243 extern MAILSTATUS mm_status_result;
6245 snprintf(msg_buf, sizeof(msg_buf), "Checking %s for %s", FLDR_NAME(f),
6246 (sa->flags == SA_UNSEEN)
6247 ? "unseen messages"
6248 : (sa->flags == SA_MESSAGES) ? "message count"
6249 : "recent messages");
6250 msg_buf[sizeof(msg_buf)-1] = '\0';
6251 we_cancel = busy_cue(msg_buf, NULL, 0);
6253 if(!context_status(sa->context, sa->stream, f->name, sa->flags))
6254 mm_status_result.flags = 0L;
6256 if(we_cancel)
6257 cancel_busy_cue(0);
6259 if(sa->flags & mm_status_result.flags)
6260 switch(sa->flags){
6261 case SA_MESSAGES :
6262 switch(sa->cmp){
6263 case 0 : /* equals */
6264 if(mm_status_result.messages == sa->nmsgs)
6265 rv = 1;
6267 break;
6269 case 1 : /* less than */
6270 if(mm_status_result.messages < sa->nmsgs)
6271 rv = 1;
6273 break;
6275 case 2 :
6276 if(mm_status_result.messages > sa->nmsgs)
6277 rv = 1;
6279 break;
6281 default :
6282 break;
6285 break;
6287 case SA_RECENT :
6288 if(mm_status_result.recent)
6289 rv = 1;
6291 break;
6293 case SA_UNSEEN :
6294 if(mm_status_result.unseen)
6295 rv = 1;
6297 break;
6299 default :
6300 break;
6304 return(rv);
6309 foreach_folder(CONTEXT_S *context, int selected, int (*test) (FOLDER_S *, void *), void *args)
6311 int i, n, rv = 1;
6312 int we_turned_on = 0;
6313 FOLDER_S *fp;
6315 we_turned_on = intr_handling_on();
6317 for(i = 0, n = folder_total(FOLDERS(context)); i < n; i++){
6318 if(ps_global->intr_pending){
6319 for(; i >= 0; i--)
6320 folder_entry(i, FOLDERS(context))->scanned = 0;
6322 cmd_cancelled("Select");
6323 rv = 0;
6324 break;
6327 fp = folder_entry(i, FOLDERS(context));
6328 fp->scanned = 0;
6329 if((!selected || fp->selected) && fp->isfolder && (*test)(fp, args))
6330 fp->scanned = 1;
6333 if(we_turned_on)
6334 intr_handling_off();
6336 return(rv);
6340 /*----------------------------------------------------------------------
6341 Return the path delimiter for the given folder on the given server
6343 Args: folder -- folder type for delimiter
6345 ----*/
6347 folder_delimiter(char *folder)
6349 int rv, we_cancel = 0;
6351 we_cancel = busy_cue(NULL, NULL, 1);
6353 rv = get_folder_delimiter(folder);
6355 if(we_cancel)
6356 cancel_busy_cue(-1);
6358 return(rv);
6363 * next_folder - given a current folder in a context, return the next in
6364 * the list, or NULL if no more or there's a problem.
6366 * Args streamp -- If set, try to re-use this stream for checking.
6367 * next -- Put return value here, return points to this
6368 * nextlen -- Length of next
6369 * current -- Current folder, so we know where to start looking
6370 * cntxt --
6371 * find_recent -- Returns the number of recent here. The presence of
6372 * this arg also indicates that we should do the calls
6373 * to figure out whether there is a next interesting folder
6374 * or not.
6375 * did_cancel -- Tell caller if user canceled. Only used if find_recent
6376 * is also set. Also, user will only be given the
6377 * opportunity to cancel if this is set. If it isn't
6378 * set, we just plow ahead when there is an error or
6379 * when the folder does not exist.
6381 char *
6382 next_folder(MAILSTREAM **streamp, char *next, size_t nextlen, char *current, CONTEXT_S *cntxt, long int *find_recent, int *did_cancel)
6384 int index, recent = 0, failed_status = 0, try_fast;
6385 char prompt[128];
6386 FOLDER_S *f = NULL;
6387 char tmp[MAILTMPLEN];
6390 /* note: find_folders may assign "stream" */
6391 build_folder_list(streamp, cntxt, NULL, NULL,
6392 NEWS_TEST(cntxt) ? BFL_LSUB : BFL_NONE);
6394 try_fast = (F_ON(F_ENABLE_FAST_RECENT, ps_global) &&
6395 F_OFF(F_TAB_USES_UNSEEN, ps_global));
6396 if(find_recent)
6397 *find_recent = 0L;
6399 for(index = folder_index(current, cntxt, FI_FOLDER) + 1;
6400 index > 0
6401 && index < folder_total(FOLDERS(cntxt))
6402 && (f = folder_entry(index, FOLDERS(cntxt)))
6403 && !f->isdir;
6404 index++)
6405 if(find_recent){
6406 MAILSTREAM *stream = NULL;
6407 int rv, we_cancel = 0, match;
6408 char msg_buf[MAX_BM+1];
6410 /* must be a folder and it can't be the current one */
6411 if(ps_global->context_current == ps_global->context_list
6412 && !strcmp(ps_global->cur_folder, FLDR_NAME(f)))
6413 continue;
6416 * If we already have the folder open, short circuit all this
6417 * stuff.
6419 match = 0;
6420 if((stream = sp_stream_get(context_apply(tmp, cntxt, f->name,
6421 sizeof(tmp)),
6422 SP_MATCH)) != NULL
6423 || (!IS_REMOTE(tmp) && (stream = already_open_stream(tmp, AOS_NONE)) != NULL)){
6424 (void) pine_mail_ping(stream);
6426 if(F_ON(F_TAB_USES_UNSEEN, ps_global)){
6428 * Just fall through and let the status call below handle
6429 * the already open stream. If we were doing this the
6430 * same as the else case, we would figure out how many
6431 * unseen are in this open stream by doing a search.
6432 * Instead of repeating that code that is already in
6433 * pine_mail_status_full, fall through and note the
6434 * special case by lighting the match variable.
6436 match++;
6438 else{
6439 *find_recent = sp_recent_since_visited(stream);
6440 if(*find_recent){
6441 recent++;
6442 break;
6445 continue;
6449 snprintf(msg_buf, sizeof(msg_buf), "Checking %s for %s messages",
6450 FLDR_NAME(f), F_ON(F_TAB_USES_UNSEEN, ps_global) ? "unseen" : "recent");
6451 msg_buf[sizeof(msg_buf)-1] = '\0';
6452 we_cancel = busy_cue(msg_buf, NULL, 0);
6454 /* First, get a stream for the test */
6455 if(!stream && streamp && *streamp){
6456 if(context_same_stream(cntxt, f->name, *streamp)){
6457 stream = *streamp;
6459 else{
6460 pine_mail_close(*streamp);
6461 *streamp = NULL;
6465 if(!stream){
6466 stream = sp_stream_get(context_apply(tmp, cntxt, f->name,
6467 sizeof(tmp)),
6468 SP_SAME);
6472 * If interestingness is indeterminate or we're
6473 * told to explicitly, look harder...
6477 * We could make this more efficient in the cases where we're
6478 * opening a new stream or using streamp by having folder_exists
6479 * cache the stream. The change would require a folder_exists()
6480 * that caches streams, but most of the time folder_exists just
6481 * uses the inbox stream or ps->mail_stream.
6483 * Another thing to consider is that maybe there should be an
6484 * option to try to LIST a folder before doing a STATUS (or SELECT).
6485 * This isn't done by default for the case where a folder is
6486 * SELECTable but not LISTable, but on some servers doing an
6487 * RLIST first tells the server that we support mailbox referrals.
6489 if(!try_fast
6490 || !((rv = folder_exists(cntxt,f->name))
6491 & (FEX_ISMARKED | FEX_UNMARKED))){
6492 extern MAILSTATUS mm_status_result;
6494 if(try_fast && (rv == 0 || rv & FEX_ERROR)){
6495 failed_status = 1;
6496 mm_status_result.flags = 0L;
6498 else{
6499 if(stream){
6500 if(!context_status_full(cntxt, match ? NULL : stream,
6501 f->name,
6502 F_ON(F_TAB_USES_UNSEEN, ps_global)
6503 ? SA_UNSEEN : SA_RECENT,
6504 &f->uidvalidity,
6505 &f->uidnext)){
6506 failed_status = 1;
6507 mm_status_result.flags = 0L;
6510 else{
6511 /* so we can re-use the stream */
6512 if(!context_status_streamp_full(cntxt, streamp, f->name,
6513 F_ON(F_TAB_USES_UNSEEN, ps_global)
6514 ? SA_UNSEEN : SA_RECENT,
6515 &f->uidvalidity,
6516 &f->uidnext)){
6517 failed_status = 1;
6518 mm_status_result.flags = 0L;
6523 if(F_ON(F_TAB_USES_UNSEEN, ps_global)){
6524 rv = ((mm_status_result.flags & SA_UNSEEN)
6525 && (*find_recent = mm_status_result.unseen))
6526 ? FEX_ISMARKED : 0;
6528 else{
6529 rv = ((mm_status_result.flags & SA_RECENT)
6530 && (*find_recent = mm_status_result.recent))
6531 ? FEX_ISMARKED : 0;
6534 /* we don't know how many in this case */
6535 if(try_fast)
6536 *find_recent = 0L; /* consistency, boy! */
6539 if(we_cancel)
6540 cancel_busy_cue(0);
6542 if(failed_status && did_cancel){
6543 char buf1[6*MAX_SCREEN_COLS+1];
6544 int wid1, wid2;
6546 snprintf(prompt, sizeof(prompt), _("Check of folder %s failed. Continue "), FLDR_NAME(f));
6547 if(utf8_width(prompt) > MAXPROMPT){
6548 snprintf(prompt, sizeof(prompt), _("Check of %s failed. Continue "), FLDR_NAME(f));
6549 if((wid1=utf8_width(prompt)) > MAXPROMPT){
6550 if((wid2=utf8_width(FLDR_NAME(f))) > wid1-MAXPROMPT)
6551 snprintf(prompt, sizeof(prompt), _("Check of %s failed. Continue "), strsquish(buf1, sizeof(buf1), FLDR_NAME(f), wid2-(wid1-MAXPROMPT)));
6552 else
6553 snprintf(prompt, sizeof(prompt), _("Check failed. Continue "));
6557 if(want_to(prompt, 'y', 0, NO_HELP, WT_NORM) == 'n'){
6558 *did_cancel = 1;
6559 break;
6561 else
6562 /* have to reset this lower-level cancel marker */
6563 ps_global->user_says_cancel = 0;
6566 failed_status = 0;
6568 if(rv & FEX_ISMARKED){
6569 recent++;
6570 break;
6574 if(f && (!find_recent || recent)){
6575 strncpy(next, FLDR_NAME(f), nextlen);
6576 next[nextlen-1] = '\0';
6578 else if(nextlen > 0)
6579 *next = '\0';
6581 /* BUG: how can this be made smarter so we cache the list? */
6582 free_folder_list(cntxt);
6583 return((*next) ? next : NULL);
6587 /*----------------------------------------------------------------------
6588 Shuffle order of incoming folders
6589 ----*/
6591 shuffle_incoming_folders(CONTEXT_S *context, int index)
6593 int tot, i, deefault, rv, inheriting = 0;
6594 int new_index, index_within_var, new_index_within_var;
6595 int readonly = 0;
6596 char tmp[200];
6597 HelpType help;
6598 ESCKEY_S opts[3];
6599 char ***alval;
6600 EditWhich ew;
6601 FOLDER_S *fp;
6602 PINERC_S *prc = NULL;
6604 dprint((4, "shuffle_incoming_folders\n"));
6606 if(!(context->use & CNTXT_INCMNG) ||
6607 (tot = folder_total(FOLDERS(context))) < 2 ||
6608 index < 0 || index >= tot)
6609 return(0);
6611 if(index == 0){
6612 q_status_message(SM_ORDER,0,3, _("Cannot shuffle INBOX"));
6613 return(0);
6616 fp = folder_entry(index, FOLDERS(context));
6617 ew = config_containing_inc_fldr(fp);
6619 if(ps_global->restricted)
6620 readonly = 1;
6621 else{
6622 switch(ew){
6623 case Main:
6624 prc = ps_global->prc;
6625 break;
6626 case Post:
6627 prc = ps_global->post_prc;
6628 break;
6629 case None:
6630 break;
6633 readonly = prc ? prc->readonly : 1;
6636 if(prc && prc->quit_to_edit){
6637 quit_to_edit_msg(prc);
6638 return(0);
6641 if(readonly){
6642 q_status_message(SM_ORDER,3,5,
6643 _("Shuffle cancelled: config file not editable"));
6644 return(0);
6647 alval = ALVAL(&ps_global->vars[V_INCOMING_FOLDERS], ew);
6649 if(!(alval && *alval))
6650 return(0);
6652 i = 0;
6653 opts[i].ch = 'b';
6654 opts[i].rval = 'b';
6655 opts[i].name = "B";
6656 opts[i++].label = N_("Back");
6658 opts[i].ch = 'f';
6659 opts[i].rval = 'f';
6660 opts[i].name = "F";
6661 opts[i++].label = N_("Forward");
6663 opts[i].ch = -1;
6664 deefault = 'b';
6666 /* find where this entry is in the particular config list */
6667 index_within_var = -1;
6668 for(i = 0; (*alval)[i]; i++){
6669 expand_variables(tmp_20k_buf, SIZEOF_20KBUF, (*alval)[i], 0);
6670 if(i == 0 && !strcmp(tmp_20k_buf, INHERIT))
6671 inheriting = 1;
6672 else if(fp->varhash == line_hash(tmp_20k_buf)){
6673 index_within_var = i;
6674 break;
6678 if(index_within_var == -1){ /* didn't find it */
6679 q_status_message(SM_ORDER,3,5,
6680 _("Shuffle cancelled: unexpected trouble shuffling"));
6681 return(0);
6684 if(index_within_var == 0 || (inheriting && index_within_var == 1)){
6685 opts[0].ch = -2; /* no back */
6686 deefault = 'f';
6689 if(!(*alval)[i+1]) /* no forward */
6690 opts[1].ch = -2;
6692 if(opts[0].ch == -2 && opts[1].ch == -2){
6693 q_status_message(SM_ORDER, 0, 4,
6694 _("Cannot shuffle from one config file to another."));
6695 return(0);
6698 snprintf(tmp, sizeof(tmp), "Shuffle \"%s\" %s%s%s ? ",
6699 FLDR_NAME(folder_entry(index, FOLDERS(context))),
6700 (opts[0].ch != -2) ? "BACK" : "",
6701 (opts[0].ch != -2 && opts[1].ch != -2) ? " or " : "",
6702 (opts[1].ch != -2) ? "FORWARD" : "");
6703 tmp[sizeof(tmp)-1] = '\0';
6704 help = (opts[0].ch == -2) ? h_incoming_shuf_down
6705 : (opts[1].ch == -2) ? h_incoming_shuf_up
6706 : h_incoming_shuf;
6708 rv = radio_buttons(tmp, -FOOTER_ROWS(ps_global), opts, deefault, 'x',
6709 help, RB_NORM);
6711 new_index = index;
6712 new_index_within_var = index_within_var;
6714 switch(rv){
6715 case 'x':
6716 cmd_cancelled("Shuffle");
6717 return(0);
6719 case 'b':
6720 new_index_within_var--;
6721 new_index--;
6722 break;
6724 case 'f':
6725 new_index_within_var++;
6726 new_index++;
6727 break;
6730 if(swap_incoming_folders(index, new_index, FOLDERS(context))){
6731 char *stmp;
6733 /* swap them in the config variable, too */
6734 stmp = (*alval)[index_within_var];
6735 (*alval)[index_within_var] = (*alval)[new_index_within_var];
6736 (*alval)[new_index_within_var] = stmp;
6738 set_current_val(&ps_global->vars[V_INCOMING_FOLDERS], TRUE, FALSE);
6739 write_pinerc(ps_global, ew, WRP_NONE);
6741 return(1);
6743 else
6744 return(0);
6749 swap_incoming_folders(int index1, int index2, FLIST *flist)
6751 FOLDER_S *ftmp;
6753 if(!flist)
6754 return(0);
6756 if(index1 == index2)
6757 return(1);
6759 if(index1 < 0 || index1 >= flist->used){
6760 dprint((1, "Error in swap_incoming_folders: index1=%d, used=%d\n", index1, flist->used));
6761 return(0);
6764 if(index2 < 0 || index2 >= flist->used){
6765 dprint((1, "Error in swap_incoming_folders: index2=%d, used=%d\n", index2, flist->used));
6766 return(0);
6769 ftmp = flist->folders[index1];
6770 flist->folders[index1] = flist->folders[index2];
6771 flist->folders[index2] = ftmp;
6773 return(1);
6777 /*----------------------------------------------------------------------
6778 Find an entry in the folder list by matching names
6779 ----*/
6781 search_folder_list(void *list, char *name)
6783 int i;
6784 char *n;
6786 for(i = 0; i < folder_total(list); i++) {
6787 n = folder_entry(i, list)->name;
6788 if(strucmp(name, n) == 0)
6789 return(1); /* Found it */
6791 return(0);
6795 static CONTEXT_S *post_cntxt = NULL;
6797 /*----------------------------------------------------------------------
6798 Browse list of newsgroups available for posting
6800 Called from composer when ^T is typed in newsgroups field
6802 Args: none
6804 Returns: pointer to selected newsgroup, or NULL.
6805 Selector call in composer expects this to be alloc'd here.
6807 ----*/
6808 char *
6809 news_group_selector(char **error_mess)
6811 CONTEXT_S *tc;
6812 char *post_folder;
6813 int rc;
6814 char *em;
6816 /* Coming back from composer */
6817 fix_windsize(ps_global);
6818 init_sigwinch();
6820 post_folder = fs_get((size_t)MAILTMPLEN);
6822 /*--- build the post_cntxt -----*/
6823 em = get_post_list(ps_global->VAR_NNTP_SERVER);
6824 if(em != NULL){
6825 if(error_mess != NULL)
6826 *error_mess = cpystr(em);
6828 cancel_busy_cue(-1);
6829 return(NULL);
6832 /*----- Call the browser -------*/
6833 tc = post_cntxt;
6834 if((rc = folders_for_post(ps_global, &tc, post_folder)) != 0)
6835 post_cntxt = tc;
6837 cancel_busy_cue(-1);
6839 if(rc <= 0)
6840 return(NULL);
6842 return(post_folder);
6846 /*----------------------------------------------------------------------
6847 Get the list of news groups that are possible for posting
6849 Args: post_host -- host name for posting
6851 Returns NULL if list is retrieved, pointer to error message if failed
6853 This is kept in a standards "CONTEXT" for a acouple of reasons. First
6854 it makes it very easy to use the folder browser to display the
6855 newsgroup for selection on ^T from the composer. Second it will allow
6856 the same mechanism to be used for all folder lists on memory tight
6857 systems like DOS. The list is kept for the life of the session because
6858 fetching it is a expensive.
6860 ----*/
6861 char *
6862 get_post_list(char **post_host)
6864 char *post_context_string;
6866 if(!post_host || !post_host[0]) {
6867 /* BUG should assume inews and get this from active file */
6868 return(_("Can't post messages, NNTP server needs to be configured"));
6871 if(!post_cntxt){
6872 int we_cancel;
6873 size_t l;
6875 we_cancel = busy_cue(_("Getting full list of groups for posting"), NULL, 1);
6877 l = strlen(post_host[0]) + 20;
6878 post_context_string = (char *) fs_get((l+1) * sizeof(char));
6879 snprintf(post_context_string, l+1, "{%s/nntp}#news.[]", post_host[0]);
6880 post_context_string[l] = '\0';
6882 post_cntxt = new_context(post_context_string, NULL);
6883 post_cntxt->use |= CNTXT_FINDALL;
6884 post_cntxt->dir->status |= CNTXT_NOFIND;
6885 post_cntxt->next = NULL;
6887 build_folder_list(NULL, post_cntxt, NULL, NULL,
6888 NEWS_TEST(post_cntxt) ? BFL_LSUB : BFL_NONE);
6889 if(we_cancel)
6890 cancel_busy_cue(-1);
6892 return(NULL);
6897 fcc_tab_complete (char *prefix, char **answer, int tabtab, unsigned flags)
6899 char tmp[MAILTMPLEN+1];
6900 int n;
6901 CONTEXT_S *mc;
6903 if(prefix == NULL || *prefix == '\0')
6904 return 0;
6906 for(mc = ps_global->context_list; mc != NULL; mc = mc->next)
6907 if(mc->use & CNTXT_SAVEDFLT)
6908 break;
6910 if(mc == NULL) return 0;
6912 strncpy(tmp, prefix, sizeof(tmp));
6913 tmp[sizeof(tmp)-1] = '\0';
6915 if(!folder_complete(mc, tmp, sizeof(tmp), &n)){
6916 if(n){
6917 if(tabtab)
6918 display_folder_list(&mc, tmp, 1,folders_for_goto);
6920 else
6921 Writechar(BELL, 0);
6923 if(n)
6924 *answer = cpystr(tmp);
6925 return n == 0 ? 0 : n == 1 ? 2 : 1;
6930 #ifdef _WINDOWS
6932 folder_list_popup(sparms, in_handle)
6933 SCROLL_S *sparms;
6934 int in_handle;
6936 MPopup fldr_popup[20];
6938 memset(fldr_popup, 0, 20 * sizeof(MPopup));
6939 fldr_popup[0].type = tTail;
6940 if(in_handle){
6941 int i, n = 0;
6942 HANDLE_S *h = get_handle(sparms->text.handles, in_handle);
6943 FOLDER_S *fp = (h) ? folder_entry(h->h.f.index,
6944 FOLDERS(h->h.f.context))
6945 : NULL;
6947 if((i = menu_binding_index(sparms->keys.menu, MC_CHOICE)) >= 0){
6948 fldr_popup[n].type = tQueue;
6949 fldr_popup[n].data.val = sparms->keys.menu->keys[i].bind.ch[0];
6950 fldr_popup[n].label.style = lNormal;
6951 fldr_popup[n++].label.string = (fp && fp->isdir)
6952 ? "&View Directory"
6953 : "&View Folder";
6956 if((i = menu_binding_index(sparms->keys.menu, MC_SELCUR)) >= 0
6957 && bitnset(i, sparms->keys.bitmap)){
6958 fldr_popup[n].type = tQueue;
6959 fldr_popup[n].data.val = sparms->keys.menu->keys[i].bind.ch[0];
6960 fldr_popup[n].label.style = lNormal;
6961 fldr_popup[n++].label.string = (fp && fp->isdir)
6962 ? "&Select Directory"
6963 : "&Select Folder";
6966 if((i = menu_binding_index(sparms->keys.menu, MC_DELETE)) >= 0
6967 && bitnset(i, sparms->keys.bitmap)){
6968 fldr_popup[n].type = tQueue;
6969 fldr_popup[n].data.val = sparms->keys.menu->keys[i].bind.ch[0];
6970 fldr_popup[n].label.style = lNormal;
6971 fldr_popup[n++].label.string = "Delete Folder";
6974 if((i = menu_binding_index(sparms->keys.menu, MC_EXPORT)) >= 0
6975 && bitnset(i, sparms->keys.bitmap)){
6976 fldr_popup[n].type = tQueue;
6977 fldr_popup[n].data.val = sparms->keys.menu->keys[i].bind.ch[0];
6978 fldr_popup[n].label.style = lNormal;
6979 fldr_popup[n++].label.string = "Export Folder";
6982 if((i = menu_binding_index(sparms->keys.menu, MC_CHK_RECENT)) >= 0
6983 && bitnset(i, sparms->keys.bitmap)){
6984 fldr_popup[n].type = tQueue;
6985 fldr_popup[n].data.val = sparms->keys.menu->keys[i].bind.ch[0];
6986 fldr_popup[n].label.style = lNormal;
6987 fldr_popup[n++].label.string = "Check New Messages";
6990 if(n)
6991 fldr_popup[n++].type = tSeparator;
6993 folder_popup_config(((FPROC_S *)sparms->proc.data.p)->fs,
6994 sparms->keys.menu, &fldr_popup[n]);
6996 else
6997 folder_popup_config(((FPROC_S *)sparms->proc.data.p)->fs,
6998 sparms->keys.menu, fldr_popup);
7000 if(fldr_popup[0].type != tTail)
7001 mswin_popup(fldr_popup);
7003 return(0);
7008 folder_list_select_popup(sparms, in_handle)
7009 SCROLL_S *sparms;
7010 int in_handle;
7012 MPopup fldr_popup[20];
7014 memset(fldr_popup, 0, 20 * sizeof(MPopup));
7015 fldr_popup[0].type = tTail;
7016 if(in_handle){
7017 int i, n = 0;
7018 HANDLE_S *h = get_handle(sparms->text.handles, in_handle);
7019 FOLDER_S *fp = (h) ? folder_entry(h->h.f.index,FOLDERS(h->h.f.context))
7020 : NULL;
7022 if((i = menu_binding_index(sparms->keys.menu, MC_CHOICE)) >= 0){
7023 fldr_popup[n].type = tQueue;
7024 fldr_popup[n].data.val = sparms->keys.menu->keys[i].bind.ch[0];
7025 fldr_popup[n].label.style = lNormal;
7026 fldr_popup[n++].label.string = (fp && fp->isdir)
7027 ? "&View Directory"
7028 : "&Select";
7030 fldr_popup[n++].type = tSeparator;
7033 folder_popup_config(((FPROC_S *)sparms->proc.data.p)->fs,
7034 sparms->keys.menu, &fldr_popup[n]);
7036 else
7037 folder_popup_config(((FPROC_S *)sparms->proc.data.p)->fs,
7038 sparms->keys.menu, fldr_popup);
7040 if(fldr_popup[0].type != tTail)
7041 mswin_popup(fldr_popup);
7043 return(0);
7048 * Just a little something to simplify assignments
7050 #define FLDRPOPUP(p, c, s) { \
7051 (p)->type = tQueue; \
7052 (p)->data.val = c; \
7053 (p)->label.style = lNormal; \
7054 (p)->label.string = s; \
7058 /*----------------------------------------------------------------------
7059 Popup Menu configurator
7061 ----*/
7062 void
7063 folder_popup_config(fs, km, popup)
7064 FSTATE_S *fs;
7065 struct key_menu *km;
7066 MPopup *popup;
7068 int i;
7070 if((i = menu_binding_index(km, MC_PARENT)) >= 0){
7071 FLDRPOPUP(popup, km->keys[i].bind.ch[0], "Parent Directory");
7072 popup++;
7075 if(fs->km == &folder_km){
7076 if((fs->context->next || fs->context->prev) && !fs->combined_view){
7077 FLDRPOPUP(popup, '<', "Collection List");
7078 popup++;
7081 else if((i = menu_binding_index(km, MC_COLLECTIONS)) >= 0){
7082 FLDRPOPUP(popup, km->keys[i].bind.ch[0], "Collection List");
7083 popup++;
7086 if((i = menu_binding_index(km, MC_INDEX)) >= 0){
7087 FLDRPOPUP(popup, km->keys[i].bind.ch[0], "Current Folder Index");
7088 popup++;
7091 if((i = menu_binding_index(km, MC_MAIN)) >= 0){
7092 FLDRPOPUP(popup, km->keys[i].bind.ch[0], "Main Menu");
7093 popup++;
7096 popup->type = tTail; /* tie off the array */
7098 #endif /* _WINDOWS */