Add support for tab-completion when selecting by rule
[alpine.git] / alpine / folder.c
blob1daf7a08ff42c34a98eddb0b3d8e88f158926dcd
1 /*
2 * ========================================================================
3 * Copyright 2013-2022 Eduardo Chappa
4 * Copyright 2006-2008 University of Washington
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
15 /*======================================================================
16 folder.c
18 Screen to display and manage all the users folders
20 This puts up a list of all the folders in the users mail directory on
21 the screen spacing it nicely. The arrow keys move from one to another
22 and the user can delete the folder or select it to change to or copy a
23 message to. The display lets the user scroll up or down a screen full,
24 or search for a folder name.
25 ====*/
28 #include "headers.h"
29 #include "../c-client/utf8aux.h"
30 #include "folder.h"
31 #include "keymenu.h"
32 #include "status.h"
33 #include "context.h"
34 #include "mailview.h"
35 #include "mailindx.h"
36 #include "mailcmd.h"
37 #include "titlebar.h"
38 #include "alpine.h"
39 #include "send.h"
40 #include "help.h"
41 #include "imap.h"
42 #include "signal.h"
43 #include "reply.h"
44 #include "setup.h"
45 #include "../pith/state.h"
46 #include "../pith/conf.h"
47 #include "../pith/folder.h"
48 #include "../pith/flag.h"
49 #include "../pith/filter.h"
50 #include "../pith/msgno.h"
51 #include "../pith/thread.h"
52 #include "../pith/util.h"
53 #include "../pith/stream.h"
54 #include "../pith/save.h"
55 #include "../pith/busy.h"
56 #include "../pith/list.h"
59 #define SUBSCRIBE_PMT \
60 _("Enter newsgroup name (or partial name to get a list): ")
61 #define LISTMODE_GRIPE _("Use \"X\" to mark selections in list mode")
62 #define SEL_ALTER_PMT _("ALTER folder selection : ")
63 #define SEL_TEXT_PMT _("Select by folder Name or Contents ? ")
64 #define SEL_PROP_PMT _("Select by which folder property ? ")
65 #define DIR_FOLD_PMT \
66 _("Folder and directory of the same name will be deleted. Continue")
70 * folder_list_write
72 #define FLW_NONE 0x00
73 #define FLW_LUNK 0x01 /* Using handles */
74 #define FLW_SLCT 0x02 /* Some folder is selected, may need X to show it */
75 #define FLW_LIST 0x04 /* Allow for ListMode for subscribing */
76 #define FLW_UNSEEN 0x08 /* Add (unseen) */
78 /* folder colors */
79 #define CLR_UNSEEN 0x01 /* color folder with unseen/new messages */
80 #define CLR_FOLDER 0x02 /* color a name of folder or directory */
81 #define CLR_DIRECT 0x04 /* color a separator of a directory */
82 #define CLR_FLDRLT 0x08 /* color of explanatory text in list scrn*/
83 #define CLR_NORMAL 0x10 /* use normal color */
85 /*----------------------------------------------------------------------
86 The data needed to redraw the folders screen, including the case where the
87 screen changes size in which case it may recalculate the folder_display.
88 ----*/
92 * Struct managing folder_lister arguments and holding state
93 * for various internal methods
95 typedef struct _folder_screen {
96 CONTEXT_S *context; /* current collection */
97 CONTEXT_S *list_cntxt; /* list mode collection */
98 MAILSTREAM **cache_streamp; /* cached mailstream */
99 char first_folder[MAXFOLDER];
100 unsigned first_dir:1; /* first_folder is a dir */
101 unsigned combined_view:1; /* display flat folder list */
102 unsigned no_dirs:1; /* no dirs in this screen */
103 unsigned no_empty_dirs:1; /* no empty dirs on this screen */
104 unsigned relative_path:1; /* return fully-qual'd specs */
105 unsigned save_sel:1;
106 unsigned force_intro:1;
107 unsigned agg_ops:1;
108 unsigned include_unseen_cnt:1;
109 struct key_menu *km; /* key label/command bindings */
110 struct _func_dispatch {
111 int (*valid)(FOLDER_S *, struct _folder_screen *);
112 struct {
113 HelpType text;
114 char *title;
115 } help;
116 struct {
117 char *bar;
118 TitleBarType style;
119 } title;
120 } f;
121 } FSTATE_S;
125 * Struct managing folder_lister metadata as it gets passed
126 * in and back up thru scrolltool
128 typedef struct _folder_proc {
129 FSTATE_S *fs;
130 STRLIST_S *rv;
131 unsigned done:1; /* done listing folders?... */
132 unsigned all_done:1; /* ...and will list no more forever */
133 } FPROC_S;
135 #define FPROC(X) ((FPROC_S *)(X)->proc.data.p)
138 typedef struct _scanarg {
139 MAILSTREAM *stream;
140 int newstream;
141 CONTEXT_S *context;
142 char *pattern;
143 char type;
144 } SCANARG_S;
147 typedef struct _statarg {
148 MAILSTREAM *stream;
149 int newstream;
150 CONTEXT_S *context;
151 long flags;
152 long nmsgs;
153 int cmp;
154 } STATARG_S;
158 * Internal prototypes
160 STRLIST_S *folders_for_subscribe(struct pine *, CONTEXT_S *, char *);
161 int folders_for_post(struct pine *, CONTEXT_S **, char *);
162 int folder_selector(struct pine *, FSTATE_S *, char *, CONTEXT_S **);
163 void folder_sublist_context(char *, CONTEXT_S *, CONTEXT_S *, FDIR_S **, int);
164 CONTEXT_S *context_screen(CONTEXT_S *, struct key_menu *, int);
165 int exit_collection_add(struct headerentry *, void (*)(void), int, char **);
166 char *cancel_collection_add(void (*)(void));
167 char *cancel_collection_edit(void (*)(void));
168 char *cancel_collection_editing(char *, void (*)(void));
169 int build_namespace(char *, char **, char **, BUILDER_ARG *, int *);
170 int fl_val_gen(FOLDER_S *, FSTATE_S *);
171 int fl_val_writable(FOLDER_S *, FSTATE_S *);
172 int fl_val_subscribe(FOLDER_S *, FSTATE_S *);
173 STRLIST_S *folder_lister(struct pine *, FSTATE_S *);
174 int folder_list_text(struct pine *, FPROC_S *, gf_io_t, HANDLE_S **, int);
175 int folder_list_write(gf_io_t, HANDLE_S **, CONTEXT_S *, int, char *, int);
176 int folder_list_write_prefix(FOLDER_S *, int, gf_io_t);
177 int folder_list_write_middle(FOLDER_S *fp, CONTEXT_S *ctxt, gf_io_t pc, HANDLE_S *);
178 int folder_list_write_suffix(FOLDER_S *, int, gf_io_t);
179 int color_monitored(FOLDER_S *, int, int);
180 int color_test_for_folder(char *, char *);
181 int color_write_for_folder(gf_io_t pc, int testtype);
182 int use_color_for_folder(FOLDER_S *fp);
183 int folder_list_ith(int, CONTEXT_S *);
184 char *folder_list_center_space(char *, int);
185 HANDLE_S *folder_list_handle(FSTATE_S *, HANDLE_S *);
186 int folder_processor(int, MSGNO_S *, SCROLL_S *);
187 int folder_lister_clickclick(SCROLL_S *);
188 int folder_lister_choice(SCROLL_S *);
189 int folder_lister_finish(SCROLL_S *, CONTEXT_S *, int);
190 int folder_lister_addmanually(SCROLL_S *);
191 void folder_lister_km_manager(SCROLL_S *, int);
192 void folder_lister_km_sel_manager(SCROLL_S *, int);
193 void folder_lister_km_sub_manager(SCROLL_S *, int);
194 int folder_select(struct pine *, CONTEXT_S *, int);
195 int folder_lister_select(FSTATE_S *, CONTEXT_S *, int, int);
196 int folder_lister_parent(FSTATE_S *, CONTEXT_S *, int, int);
197 char *folder_lister_fullname(FSTATE_S *, char *);
198 void folder_export(SCROLL_S *);
199 int folder_import(SCROLL_S *, char *, size_t);
200 int folder_select_toggle(CONTEXT_S *, int, int (*)(CONTEXT_S *, int));
201 char *end_bracket_no_nest(char *);
202 int group_subscription(char *, size_t, CONTEXT_S *);
203 int rename_folder(CONTEXT_S *, int, char *, size_t, MAILSTREAM *);
204 int delete_folder(CONTEXT_S *, int, char *, size_t, MAILSTREAM **);
205 void print_folders(FPROC_S *);
206 int scan_get_pattern(char *, char *, int);
207 int folder_select_text(struct pine *, CONTEXT_S *, int);
208 int foreach_do_scan(FOLDER_S *, void *);
209 int scan_scan_folder(MAILSTREAM *, CONTEXT_S *, FOLDER_S *, char *);
210 int folder_select_props(struct pine *, CONTEXT_S *, int);
211 int folder_select_count(long *, int *);
212 int foreach_do_stat(FOLDER_S *, void *);
213 int foreach_folder(CONTEXT_S *, int, int (*)(FOLDER_S *, void *), void *);
214 int folder_delimiter(char *);
215 int shuffle_incoming_folders(CONTEXT_S *, int);
216 int swap_incoming_folders(int, int, FLIST *);
217 int search_folder_list(void *, char *);
218 char *get_post_list(char **);
219 char *quote_brackets_if_needed(char *);
220 #ifdef _WINDOWS
221 int folder_list_popup(SCROLL_S *, int);
222 int folder_list_select_popup(SCROLL_S *, int);
223 void folder_popup_config(FSTATE_S *, struct key_menu *,MPopup *);
224 #endif
227 /*----------------------------------------------------------------------
228 Front end to folder lister when it's called from the main menu
230 Args: ps -- The general pine_state data structure
232 Result: runs context and folder listers
234 ----*/
235 void
236 folder_screen(struct pine *ps)
238 int n = 1;
239 CONTEXT_S *cntxt = ps->context_current;
240 STRLIST_S *folders;
241 FSTATE_S fs;
242 MAILSTREAM *cache_stream = NULL;
244 dprint((1, "=== folder_screen called ====\n"));
245 mailcap_free(); /* free resources we won't be using for a while */
246 ps->next_screen = SCREEN_FUN_NULL;
248 /* Initialize folder state and dispatches */
249 memset(&fs, 0, sizeof(FSTATE_S));
250 fs.context = cntxt;
251 fs.cache_streamp = &cache_stream;
252 fs.combined_view = F_ON(F_CMBND_FOLDER_DISP, ps_global) != 0;
253 fs.agg_ops = F_ON(F_ENABLE_AGG_OPS, ps_global) != 0;
254 fs.relative_path = 1;
255 fs.include_unseen_cnt = 1;
256 fs.f.valid = fl_val_gen;
257 /* TRANSLATORS: The all upper case things are screen titles */
258 fs.f.title.bar = _("FOLDER LIST");
259 fs.f.title.style = FolderName;
260 fs.f.help.text = h_folder_maint;
261 fs.f.help.title = _("HELP FOR FOLDERS");
262 fs.km = &folder_km;
264 if(context_isambig(ps->cur_folder)
265 && (IS_REMOTE(ps->cur_folder) || !is_absolute_path(ps->cur_folder)
266 || (cntxt && cntxt->context && cntxt->context[0] == '{'))){
267 if(strlen(ps_global->cur_folder) < MAXFOLDER - 1){
268 strncpy(fs.first_folder, ps_global->cur_folder, MAXFOLDER);
269 fs.first_folder[MAXFOLDER-1] = '\0';
273 * If we're asked to start in the folder list of the current
274 * folder and it looks like the current folder is part of the
275 * current context, try to start in the list of folders in the
276 * current context.
278 if(ps->start_in_context || fs.combined_view){
279 char tmp[MAILTMPLEN], *p, *q;
280 FDIR_S *fp;
282 ps->start_in_context = 0;
283 n = 0;
285 if(!(NEWS_TEST(cntxt) || (cntxt->use & CNTXT_INCMNG))
286 && cntxt->dir->delim
287 && strchr(ps->cur_folder, cntxt->dir->delim)){
288 for(p = strchr(q = ps->cur_folder, cntxt->dir->delim);
290 p = strchr(q = ++p, cntxt->dir->delim)){
291 strncpy(tmp, q, MIN(p - q, sizeof(tmp)-1));
292 tmp[MIN(p - q, sizeof(tmp)-1)] = '\0';
294 fp = next_folder_dir(cntxt, tmp, FALSE, fs.cache_streamp);
296 fp->desc = folder_lister_desc(cntxt, fp);
298 /* Insert new directory into list */
299 fp->delim = cntxt->dir->delim;
300 fp->prev = cntxt->dir;
301 fp->status |= CNTXT_SUBDIR;
302 cntxt->dir = fp;
308 while(ps->next_screen == SCREEN_FUN_NULL
309 && ((n++) ? (cntxt = context_screen(cntxt,&c_mgr_km,1)) != NULL :1)){
311 fs.context = cntxt;
312 if(F_ON(F_ENABLE_INCOMING_CHECKING, ps) && ps->VAR_INCOMING_FOLDERS && ps->VAR_INCOMING_FOLDERS[0])
313 ps->in_folder_screen = 1;
315 if((folders = folder_lister(ps, &fs)) != NULL){
317 ps->in_folder_screen = 0;
319 if(ps && ps->ttyo){
320 blank_keymenu(ps->ttyo->screen_rows - 2, 0);
321 ps->mangled_footer = 1;
324 if(do_broach_folder((char *) folders->name,
325 fs.context, fs.cache_streamp
326 && *fs.cache_streamp ? fs.cache_streamp
327 : NULL, 0L) == 1){
328 reset_context_folders(ps->context_list);
329 ps->next_screen = mail_index_screen;
332 if(fs.cache_streamp)
333 *fs.cache_streamp = NULL;
334 free_strlist(&folders);
337 ps->in_folder_screen = 0;
340 if(fs.cache_streamp && *fs.cache_streamp)
341 pine_mail_close(*fs.cache_streamp);
343 ps->prev_screen = folder_screen;
347 /*----------------------------------------------------------------------
348 Front end to folder lister when it's called from the main menu
350 Args: ps -- The general pine_state data structure
352 Result: runs context and folder listers
354 ----*/
355 void
356 folder_config_screen(struct pine *ps, int edit_exceptions)
358 CONT_SCR_S css;
359 char title[50], htitle[50];
361 dprint((1, "=== folder_config_screen called ====\n"));
362 mailcap_free(); /* free resources we won't be using for a while */
364 if(edit_exceptions){
365 snprintf(title, sizeof(title), _("SETUP EXCEPTIONS COLLECTION LIST"));
366 snprintf(htitle, sizeof(htitle), _("HELP FOR SETUP EXCEPTIONS COLLECTIONS"));
368 else{
369 snprintf(title, sizeof(title), _("SETUP COLLECTION LIST"));
370 snprintf(htitle, sizeof(htitle), _("HELP FOR SETUP COLLECTIONS"));
373 memset(&css, 0, sizeof(CONT_SCR_S));
374 css.title = title;
375 /* TRANSLATORS: Print something1 using something2.
376 contexts is something1 */
377 css.print_string = _("contexts");
378 css.contexts = &ps_global->context_list;
379 css.help.text = h_collection_maint;
380 css.help.title = htitle;
381 css.keymenu = &c_cfg_km;
382 css.edit = 1;
385 * Use conf_scroll_screen to manage display/selection
386 * of contexts
388 context_config_screen(ps_global, &css, edit_exceptions);
392 /*----------------------------------------------------------------------
393 Browse folders for ^T selection from the Goto Prompt
395 Args: ps --
396 cntxtp -- pointer to addr of context to start in, list, and return
397 folder -- pointer to buffer inwhich to return selected folder
399 Returns: 1 if we have something valid in cntxtp and folder
400 0 if problem or user cancelled
402 ----*/
404 folders_for_goto(struct pine *ps, CONTEXT_S **cntxtp, char *folder, int sublist)
406 int rv;
407 CONTEXT_S fake_context;
408 FDIR_S *fake_dir = NULL;
409 FSTATE_S fs;
411 dprint((1, "=== folders_for_goto called ====\n"));
413 /* Initialize folder state and dispatches */
414 memset(&fs, 0, sizeof(FSTATE_S));
415 fs.context = *cntxtp;
416 fs.combined_view = !sublist && F_ON(F_CMBND_FOLDER_DISP, ps_global) != 0;
417 fs.f.valid = fl_val_gen;
418 fs.f.title.bar = _("GOTO: SELECT FOLDER");
419 fs.f.title.style = FolderName;
420 fs.f.help.text = h_folder_open;
421 fs.f.help.title = _("HELP FOR OPENING FOLDERS");
422 fs.km = &folder_sel_km;
424 /* If we were provided a string,
425 * dummy up a context for a substring match
427 if(sublist && *folder && context_isambig(folder)){
428 if((*cntxtp)->use & CNTXT_INCMNG){
429 q_status_message(SM_ORDER, 0, 3,
430 _("All folders displayed for Incoming Collection"));
432 else{
433 folder_sublist_context(folder, *cntxtp, &fake_context,
434 &fake_dir, sublist);
435 fs.context = &fake_context;
436 fs.relative_path = 1;
437 fs.force_intro = 1;
438 cntxtp = &fs.context;
442 rv = folder_selector(ps, &fs, folder, cntxtp);
444 if(fake_dir)
445 free_fdir(&fake_dir, TRUE);
447 return(rv);
451 /*----------------------------------------------------------------------
452 Browse folders for ^T selection from the Save Prompt
454 Args: ps --
455 cntxtp -- pointer to addr of context to start in, list, and return
456 folder -- pointer to buffer inwhich to return selected folder
458 Returns: 1 if we have something valid in cntxtp and folder
459 0 if problem or user cancelled
461 ----*/
463 folders_for_save(struct pine *ps, CONTEXT_S **cntxtp, char *folder, int sublist)
465 int rv;
466 CONTEXT_S fake_context;
467 FDIR_S *fake_dir = NULL;
468 FSTATE_S fs;
470 dprint((1, "=== folders_for_save called ====\n"));
472 /* Initialize folder state and dispatches */
473 memset(&fs, 0, sizeof(FSTATE_S));
474 fs.context = *cntxtp;
475 fs.combined_view = F_ON(F_CMBND_FOLDER_DISP, ps_global) != 0;
476 fs.f.valid = fl_val_gen;
477 fs.f.title.bar = _("SAVE: SELECT FOLDER");
478 fs.f.title.style = MessageNumber;
479 fs.f.help.text = h_folder_save;
480 fs.f.help.title = _("HELP FOR SAVING MESSAGES TO FOLDERS");
481 fs.km = &folder_sela_km;
483 /* If we were provided a string,
484 * dummy up a context for a substring match
486 if(sublist && *folder && context_isambig(folder)){
487 if((*cntxtp)->use & CNTXT_INCMNG){
488 q_status_message(SM_ORDER, 0, 3,
489 _("All folders displayed for Incoming Collection"));
491 else{
492 folder_sublist_context(folder, *cntxtp, &fake_context,
493 &fake_dir, sublist);
494 fs.context = &fake_context;
495 fs.relative_path = 1;
496 fs.force_intro = 1;
497 cntxtp = &fs.context;
501 rv = folder_selector(ps, &fs, folder, cntxtp);
503 if(fake_dir)
504 free_fdir(&fake_dir, TRUE);
506 return(rv);
510 /*----------------------------------------------------------------------
511 Browse folders for ^T selection from the Subscribe Prompt
513 Args: ps --
514 cntxtp -- pointer to addr of context to start in, list, and return
515 folder -- pointer to buffer inwhich to return selected folder
517 Returns: 1 if we have something valid in cntxtp and folder
518 0 if problem or user cancelled
520 ----*/
521 STRLIST_S *
522 folders_for_subscribe(struct pine *ps, CONTEXT_S *cntxt, char *folder)
524 STRLIST_S *folders = NULL;
525 FSTATE_S fs;
526 void (*redraw)(void);
528 dprint((1, "=== folders_for_sub called ====\n"));
530 /* Initialize folder state and dispatches */
531 memset(&fs, 0, sizeof(FSTATE_S));
532 fs.context = cntxt;
533 fs.f.valid = fl_val_subscribe;
534 fs.f.title.bar = _("SUBSCRIBE: SELECT FOLDER");
535 fs.f.title.style = FolderName;
536 fs.f.help.text = h_folder_subscribe;
537 fs.f.help.title = _("HELP SELECTING NEWSGROUP TO SUBSCRIBE TO");
538 fs.km = &folder_sub_km;
539 fs.force_intro = 1;
541 fs.context = cntxt;
542 redraw = ps_global->redrawer;
543 folders = folder_lister(ps, &fs);
544 ps_global->redrawer = redraw;
545 if(ps_global->redrawer)
546 (*ps_global->redrawer)();
548 return(folders);
552 /*----------------------------------------------------------------------
553 Browse folders for ^T selection for posting
555 Args: ps --
556 cntxtp -- pointer to addr of context to start in, list, and return
557 folder -- pointer to buffer inwhich to return selected folder
559 Returns: 1 if we have something valid in cntxtp and folder
560 0 if problem or user cancelled
562 ----*/
564 folders_for_post(struct pine *ps, CONTEXT_S **cntxtp, char *folder)
566 FSTATE_S fs;
568 dprint((1, "=== folders_for_post called ====\n"));
570 /* Initialize folder state and dispatches */
571 memset(&fs, 0, sizeof(FSTATE_S));
572 fs.context = *cntxtp;
573 fs.f.valid = fl_val_subscribe;
574 fs.f.title.bar = _("NEWS: SELECT GROUP");
575 fs.f.title.style = FolderName;
576 fs.f.help.text = h_folder_postnews;
577 fs.f.help.title = _("HELP FOR SELECTING NEWSGROUP TO POST TO");
578 fs.km = &folder_post_km;
580 return(folder_selector(ps, &fs, folder, cntxtp));
585 folder_selector(struct pine *ps, FSTATE_S *fs, char *folder, CONTEXT_S **cntxtp)
587 int rv = 0;
588 STRLIST_S *folders;
591 fs->context = *cntxtp;
592 if((folders = folder_lister(ps, fs)) != NULL){
593 strncpy(folder, (char *) folders->name, MAILTMPLEN-1);
594 folder[MAILTMPLEN-1] = '\0';
595 free_strlist(&folders);
596 *cntxtp = fs->context;
597 rv++;
598 break;
600 else if(!(fs->context
601 && (fs->context->next || fs->context->prev))
602 || fs->combined_view)
603 break;
605 while((*cntxtp = context_screen(*cntxtp, &c_sel_km, 0)) != NULL);
607 return(rv);
611 void
612 folder_sublist_context(char *folder, CONTEXT_S *cntxt, CONTEXT_S *new_cntxt, FDIR_S **new_dir, int lev)
614 char *p, *q, *ref, *wildcard;
616 *new_cntxt = *cntxt;
617 new_cntxt->next = new_cntxt->prev = NULL;
618 new_cntxt->dir = *new_dir = (FDIR_S *) fs_get(sizeof(FDIR_S));
619 memset(*new_dir, 0, sizeof(FDIR_S));
620 (*new_dir)->status |= CNTXT_NOFIND;
621 (*new_dir)->folders = init_folder_entries();
622 if(!((*new_dir)->delim = cntxt->dir->delim)){
623 /* simple LIST to acquire delimiter, doh */
624 build_folder_list(NULL, new_cntxt, "", NULL,
625 NEWS_TEST(new_cntxt) ? BFL_LSUB : BFL_NONE);
626 new_cntxt->dir->status = CNTXT_NOFIND;
629 wildcard = NEWS_TEST(new_cntxt) ? "*" : "%";
631 if((p = strrindex(folder, (*new_dir)->delim)) != NULL){
632 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s", p + 1, wildcard);
633 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
634 (*new_dir)->view.internal = cpystr(tmp_20k_buf);
635 for(ref = tmp_20k_buf, q = new_cntxt->context;
636 (*ref = *q) != '\0' && !(*q == '%' && *(q+1) == 's');
637 ref++, q++)
640 for(q = folder; q <= p; q++, ref++)
641 *ref = *q;
643 *ref = '\0';
644 (*new_dir)->ref = cpystr(tmp_20k_buf);
646 (*new_dir)->status |= CNTXT_SUBDIR;
648 else{
649 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
650 (lev > 1) ? wildcard : "", folder, wildcard);
651 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
652 (*new_dir)->view.internal = cpystr(tmp_20k_buf);
653 /* leave (*new_dir)->ref == NULL */
656 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("List of folders matching \"%s*\""), folder);
657 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
658 (*new_dir)->desc = cpystr(tmp_20k_buf);
662 /*----------------------------------------------------------------------
663 Browse folders for ^T selection from the composer
665 Args: error_mess -- pointer to place to return an error message
667 Returns: result if folder selected, NULL if not
668 Composer expects the result to be alloc'd here
670 ----*/
671 char *
672 folders_for_fcc(char **errmsg)
674 char *rs = NULL;
675 STRLIST_S *folders;
676 FSTATE_S fs;
678 dprint((1, "=== folders_for_fcc called ====\n"));
680 /* Coming back from composer */
681 fix_windsize(ps_global);
682 init_sigwinch();
684 /* Initialize folder state and dispatches */
685 memset(&fs, 0, sizeof(FSTATE_S));
686 fs.combined_view = F_ON(F_CMBND_FOLDER_DISP, ps_global) != 0;
687 fs.f.valid = fl_val_gen;
688 fs.f.title.bar = _("FCC: SELECT FOLDER");
689 fs.f.title.style = FolderName;
690 fs.f.help.text = h_folder_fcc;
691 fs.f.help.title = _("HELP FOR SELECTING THE FCC");
692 fs.km = &folder_sela_km;
694 /* start in the default save context */
695 fs.context = default_save_context(ps_global->context_list);
698 if((folders = folder_lister(ps_global, &fs)) != NULL){
699 char *name;
701 /* replace nickname with full name */
702 if(!(name = folder_is_nick((char *) folders->name,
703 FOLDERS(fs.context), 0)))
704 name = (char *) folders->name;
706 if(context_isambig(name) && !((fs.context->use) & CNTXT_SAVEDFLT)){
707 char path_in_context[MAILTMPLEN];
709 context_apply(path_in_context, fs.context, name,
710 sizeof(path_in_context));
711 if(!(IS_REMOTE(path_in_context)
712 || is_absolute_path(path_in_context))){
714 * Name is relative to the home directory,
715 * so have to add that. Otherwise, the sender will
716 * assume it is in the primary collection since it
717 * will still be ambiguous.
719 build_path(tmp_20k_buf, ps_global->ui.homedir,
720 path_in_context, SIZEOF_20KBUF);
721 rs = cpystr(tmp_20k_buf);
723 else
724 rs = cpystr(path_in_context);
726 else
727 rs = cpystr(name);
729 free_strlist(&folders);
730 break;
732 else if(!(fs.context && (fs.context->next || fs.context->prev))
733 || fs.combined_view)
734 break;
736 while((fs.context = context_screen(fs.context, &c_fcc_km, 0)) != NULL);
738 return(rs);
742 /*----------------------------------------------------------------------
743 Browse folders for ^T selection from the role editor
745 Returns: result if folder selected, NULL if not
746 Tesult is alloc'd here
748 ----*/
749 char *
750 folder_for_config(int flags)
752 char *rs = NULL;
753 STRLIST_S *folders;
754 FSTATE_S fs;
756 dprint((1, "=== folder_for_config called ====\n"));
758 /* Initialize folder state and dispatches */
759 memset(&fs, 0, sizeof(FSTATE_S));
760 fs.combined_view = F_ON(F_CMBND_FOLDER_DISP, ps_global) != 0;
761 fs.f.valid = fl_val_gen;
762 fs.f.title.bar = _("SELECT FOLDER");
763 fs.f.title.style = FolderName;
764 fs.km = &folder_sela_km;
765 if(flags & FOR_PATTERN){
766 fs.f.help.text = h_folder_pattern_roles;
767 fs.f.help.title = _("HELP FOR SELECTING CURRENT FOLDER");
769 else if(flags & FOR_OPTIONSCREEN){
770 fs.f.help.text = h_folder_stayopen_folders;
771 fs.f.help.title = _("HELP FOR SELECTING FOLDER");
773 else{
774 fs.f.help.text = h_folder_action_roles;
775 fs.f.help.title = _("HELP FOR SELECTING FOLDER");
778 /* start in the current context */
779 fs.context = ps_global->context_current;
782 if((folders = folder_lister(ps_global, &fs)) != NULL){
783 char *name = NULL;
785 /* replace nickname with full name */
786 if(!(flags & (FOR_PATTERN | FOR_OPTIONSCREEN)))
787 name = folder_is_nick((char *) folders->name,
788 FOLDERS(fs.context), 0);
790 if(!name)
791 name = (char *) folders->name;
793 if(context_isambig(name) &&
794 !(flags & (FOR_PATTERN | FOR_OPTIONSCREEN) &&
795 folder_is_nick(name, FOLDERS(fs.context), 0))){
796 char path_in_context[MAILTMPLEN];
798 context_apply(path_in_context, fs.context, name,
799 sizeof(path_in_context));
802 * We may still have a non-fully-qualified name. In the
803 * action case, that will be interpreted in the primary
804 * collection instead of as a local name.
805 * Qualify that name the same way we
806 * qualify it in match_pattern_folder_specific.
808 if(!(IS_REMOTE(path_in_context) ||
809 path_in_context[0] == '#')){
810 if(strlen(path_in_context) < (MAILTMPLEN/2)){
811 char tmp[MAX(MAILTMPLEN,NETMAXMBX)];
812 char *t;
814 t = mailboxfile(tmp, path_in_context);
815 rs = cpystr(t);
817 else{ /* the else part should never happen */
818 build_path(tmp_20k_buf, ps_global->ui.homedir,
819 path_in_context, SIZEOF_20KBUF);
820 rs = cpystr(tmp_20k_buf);
823 else
824 rs = cpystr(path_in_context);
826 else
827 rs = cpystr(name);
829 free_strlist(&folders);
830 break;
832 else if(!(fs.context && (fs.context->next || fs.context->prev))
833 || fs.combined_view)
834 break;
836 while((fs.context = context_screen(fs.context, &c_fcc_km, 0)) != NULL);
838 return(rs);
843 * offer screen with list of contexts to select and some sort
844 * of descriptions
846 CONTEXT_S *
847 context_screen(CONTEXT_S *start, struct key_menu *km, int edit_config)
849 /* If a list, let the user tell us what to do */
850 if(F_OFF(F_CMBND_FOLDER_DISP, ps_global)
851 && ps_global->context_list
852 && ps_global->context_list->next){
853 CONT_SCR_S css;
855 memset(&css, 0, sizeof(CONT_SCR_S));
856 css.title = _("COLLECTION LIST");
857 css.print_string = _("contexts");
858 css.start = start;
859 css.contexts = &ps_global->context_list;
860 css.help.text = h_collection_screen;
861 css.help.title = _("HELP FOR COLLECTION LIST");
862 css.keymenu = km;
863 css.edit = edit_config;
866 * Use conf_scroll_screen to manage display/selection
867 * of contexts
869 return(context_select_screen(ps_global, &css, 0));
872 return(ps_global->context_list);
876 static struct headerentry headents_templ[]={
877 /* TRANSLATORS: these are the headings for setting up a collection of
878 folders, PATH is a filesystem path, VIEW is sort of a technical
879 term that can be used to restrict the View to fewer folders */
880 {"Nickname : ", N_("Nickname"), h_composer_cntxt_nick, 12, 0, NULL,
881 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
882 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
883 {"Server : ", N_("Server"), h_composer_cntxt_server, 12, 0, NULL,
884 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
885 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
886 {"Path : ", N_("Path"), h_composer_cntxt_path, 12, 0, NULL,
887 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
888 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
889 {"View : ", N_("View"), h_composer_cntxt_view, 12, 0, NULL,
890 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
891 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
892 {NULL, NULL, NO_HELP, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
893 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}
895 #define AC_NICK 0
896 #define AC_SERV 1
897 #define AC_PATH 2
898 #define AC_VIEW 3
901 char *
902 context_edit_screen(struct pine *ps, char *func, char *def_nick,
903 char *def_serv, char *def_path, char *def_view)
905 int editor_result, i, j;
906 char servpart[MAILTMPLEN], new_cntxt[MAILTMPLEN];
907 char pathpart[MAILTMPLEN], allbutnick[MAILTMPLEN];
908 char tmp[MAILTMPLEN], *nick, *serv, *path, *view,
909 *return_cntxt = NULL, *val, *p;
910 char nickpmt[100], servpmt[100], pathpmt[100], viewpmt[100];
911 int indent;
912 PICO pbf;
913 STORE_S *msgso;
914 NETMBX mb;
916 standard_picobuf_setup(&pbf);
917 pbf.pine_flags |= P_NOBODY;
918 pbf.exittest = exit_collection_add;
919 pbf.canceltest = (func && !strucmp(func, "EDIT")) ? cancel_collection_edit
920 : cancel_collection_add;
921 snprintf(tmp, sizeof(tmp), _("FOLDER COLLECTION %s"), func);
922 tmp[sizeof(tmp)-1] = '\0';
923 pbf.pine_anchor = set_titlebar(tmp, ps_global->mail_stream,
924 ps_global->context_current,
925 ps_global->cur_folder,ps_global->msgmap,
926 0, FolderName, 0, 0, NULL);
928 /* An informational message */
929 if((msgso = so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
930 pbf.msgtext = (void *) so_text(msgso);
931 so_puts(msgso,
932 _("\n Fill in the fields above to add a Folder Collection to your"));
933 so_puts(msgso,
934 _("\n COLLECTION LIST screen."));
935 so_puts(msgso,
936 _("\n Use the \"^G\" command to get help specific to each item, and"));
937 so_puts(msgso,
938 _("\n use \"^X\" when finished."));
942 pbf.headents = (struct headerentry *)fs_get((sizeof(headents_templ)
943 /sizeof(struct headerentry))
944 * sizeof(struct headerentry));
945 memset((void *) pbf.headents, 0,
946 (sizeof(headents_templ)/sizeof(struct headerentry))
947 * sizeof(struct headerentry));
949 for(i = 0; headents_templ[i].prompt; i++)
950 pbf.headents[i] = headents_templ[i];
952 indent = utf8_width(_("Nickname")) + 2;
954 nick = cpystr(def_nick ? def_nick : "");
955 pbf.headents[AC_NICK].realaddr = &nick;
956 pbf.headents[AC_NICK].maxlen = strlen(nick);
957 utf8_snprintf(nickpmt, sizeof(nickpmt), "%-*.*w: ", indent, indent, _("Nickname"));
958 pbf.headents[AC_NICK].prompt = nickpmt;
959 pbf.headents[AC_NICK].prwid = indent+2;
961 serv = cpystr(def_serv ? def_serv : "");
962 pbf.headents[AC_SERV].realaddr = &serv;
963 pbf.headents[AC_SERV].maxlen = strlen(serv);
964 utf8_snprintf(servpmt, sizeof(servpmt), "%-*.*w: ", indent, indent, _("Server"));
965 pbf.headents[AC_SERV].prompt = servpmt;
966 pbf.headents[AC_SERV].prwid = indent+2;
968 path = cpystr(def_path ? def_path : "");
969 pbf.headents[AC_PATH].realaddr = &path;
970 pbf.headents[AC_PATH].maxlen = strlen(path);
971 pbf.headents[AC_PATH].bldr_private = (void *) 0;
972 utf8_snprintf(pathpmt, sizeof(pathpmt), "%-*.*w: ", indent, indent, _("Path"));
973 pbf.headents[AC_PATH].prompt = pathpmt;
974 pbf.headents[AC_PATH].prwid = indent+2;
976 view = cpystr(def_view ? def_view : "");
977 pbf.headents[AC_VIEW].realaddr = &view;
978 pbf.headents[AC_VIEW].maxlen = strlen(view);
979 utf8_snprintf(viewpmt, sizeof(viewpmt), "%-*.*w: ", indent, indent, _("View"));
980 pbf.headents[AC_VIEW].prompt = viewpmt;
981 pbf.headents[AC_VIEW].prwid = indent+2;
984 * If this is new context, setup to query IMAP server
985 * for location of personal namespace.
987 if(!(def_nick || def_serv || def_path || def_view)){
988 pbf.headents[AC_SERV].builder = build_namespace;
989 pbf.headents[AC_SERV].affected_entry = &pbf.headents[AC_PATH];
990 pbf.headents[AC_SERV].bldr_private = (void *) 0;
993 /* pass to pico and let user change them */
994 editor_result = pico(&pbf);
996 if(editor_result & COMP_GOTHUP){
997 hup_signal();
999 else{
1000 fix_windsize(ps_global);
1001 init_signals();
1004 if(editor_result & COMP_CANCEL){
1005 cmd_cancelled(func);
1007 else if(editor_result & COMP_EXIT){
1008 servpart[0] = pathpart[0] = new_cntxt[0] = allbutnick[0] = '\0';
1009 if(serv && *serv){
1010 if(serv[0] == '{' && serv[strlen(serv)-1] == '}'){
1011 strncpy(servpart, serv, sizeof(servpart)-1);
1012 servpart[sizeof(servpart)-1] = '\0';
1014 else
1015 snprintf(servpart, sizeof(servpart), "{%s}", serv);
1017 if(mail_valid_net_parse(servpart, &mb)){
1018 if(!struncmp(mb.service, "nntp", 4)
1019 && (!path || strncmp(path, "#news.", 6)))
1020 strncat(servpart, "#news.", sizeof(servpart)-1-strlen(servpart));
1022 else
1023 alpine_panic("Unexpected invalid server");
1025 else
1026 servpart[0] = '\0';
1028 servpart[sizeof(servpart)-1] = '\0';
1030 new_cntxt[0] = '\0';
1031 if(nick && *nick){
1032 val = quote_if_needed(nick);
1033 if(val){
1034 strncpy(new_cntxt, val, sizeof(new_cntxt)-2);
1035 new_cntxt[sizeof(new_cntxt)-2] = '\0';
1036 if(val != nick)
1037 fs_give((void **)&val);
1039 strncat(new_cntxt, " ", sizeof(new_cntxt)-strlen(new_cntxt)-1);
1040 new_cntxt[sizeof(new_cntxt)-1] = '\0';
1044 p = allbutnick;
1045 sstrncpy(&p, servpart, sizeof(allbutnick)-1-(p-allbutnick));
1046 allbutnick[sizeof(allbutnick)-1] = '\0';
1048 if(path){
1049 val = quote_brackets_if_needed(path);
1050 if(val){
1051 strncpy(pathpart, val, sizeof(pathpart)-1);
1052 pathpart[sizeof(pathpart)-1] = '\0';
1053 if(val != path)
1054 fs_give((void **)&val);
1057 if(pbf.headents[AC_PATH].bldr_private != (void *) 0){
1058 strncat(pathpart, (char *) pbf.headents[AC_PATH].bldr_private,
1059 sizeof(pathpart)-strlen(pathpart)-1);
1060 pathpart[sizeof(pathpart)-1] = '\0';
1064 sstrncpy(&p, pathpart, sizeof(allbutnick)-1-(p-allbutnick));
1065 allbutnick[sizeof(allbutnick)-1] = '\0';
1067 if(view[0] != '[' && sizeof(allbutnick)-1-(p-allbutnick) > 0){
1068 *p++ = '[';
1069 *p = '\0';
1072 sstrncpy(&p, view, sizeof(allbutnick)-1-(p-allbutnick));
1073 allbutnick[sizeof(allbutnick)-1] = '\0';
1074 if((j=strlen(view)) < 2 || (view[j-1] != ']' &&
1075 sizeof(allbutnick)-1-(p-allbutnick) > 0)){
1076 *p++ = ']';
1077 *p = '\0';
1080 val = quote_if_needed(allbutnick);
1081 if(val){
1082 strncat(new_cntxt, val, sizeof(new_cntxt)-1-strlen(new_cntxt));
1083 new_cntxt[sizeof(new_cntxt)-1] = '\0';
1085 if(val != allbutnick)
1086 fs_give((void **)&val);
1089 return_cntxt = cpystr(new_cntxt);
1092 for(i = 0; headents_templ[i].prompt; i++)
1093 fs_give((void **) pbf.headents[i].realaddr);
1095 if(pbf.headents[AC_PATH].bldr_private != (void *) 0)
1096 fs_give(&pbf.headents[AC_PATH].bldr_private);
1098 fs_give((void **) &pbf.headents);
1100 standard_picobuf_teardown(&pbf);
1102 if(msgso)
1103 so_give(&msgso);
1105 return(return_cntxt);
1110 * Doubles up '[' and ']' characters and returns either
1111 * an allocated string or a pointer to the source string
1112 * if no quoting is needed.
1114 char *
1115 quote_brackets_if_needed(char *src)
1117 char *step1 = NULL, *step2 = NULL, *ret;
1119 ret = src;
1121 if((strpbrk(src, "[]") != NULL)
1122 && ((step1 = add_escapes(src, "[", '[', "", "")) != NULL)
1123 && ((step2 = add_escapes(step1, "]", ']', "", ""))))
1124 ret = step2;
1126 if(step1)
1127 fs_give((void **) &step1);
1129 return(ret);
1134 * Call back for pico to prompt the user for exit confirmation
1136 * Returns: either NULL if the user accepts exit, or string containing
1137 * reason why the user declined.
1140 exit_collection_add(struct headerentry *he, void (*redraw_pico)(void), int allow_flowed,
1141 char **result)
1143 char prompt[256], tmp[MAILTMPLEN], tmpnodel[MAILTMPLEN], *server, *path,
1144 delim = '\0', *rstr = NULL, *p;
1145 int exists = 0, i;
1146 void (*redraw)(void) = ps_global->redrawer;
1147 NETMBX mb;
1149 ps_global->redrawer = redraw_pico;
1150 fix_windsize(ps_global);
1152 server = *he[AC_SERV].realaddr;
1153 removing_trailing_white_space(server);
1154 removing_leading_white_space(server);
1156 path = *he[AC_PATH].realaddr;
1158 if(*server){
1159 /* No brackets? */
1160 if(server[0] == '{' && server[strlen(server)-1] == '}'){
1161 strncpy(tmp, server, sizeof(tmp)-1);
1162 tmp[sizeof(tmp)-1] = '\0';
1164 else /* add them */ /* sizeof(tmp) == MAILTMPLEN */
1165 snprintf(tmp, sizeof(tmp), "{%.*s}", MAILTMPLEN-3, server);
1167 if(mail_valid_net_parse(tmp, &mb)){ /* news? verify namespace */
1168 if(!struncmp(mb.service, "nntp", 4) && strncmp(path, "#news.", 6))
1169 strncat(tmp, "#news.", sizeof(tmp)-1-strlen(tmp));
1171 else
1172 rstr = "Invalid Server entry";
1174 else
1175 tmp[0] = '\0';
1177 tmp[sizeof(tmp)-1] = '\0';
1179 if(!rstr){
1181 * Delimiter acquisition below serves dual purposes. One's to
1182 * get the delimiter so we can make sure it's hanging off the end
1183 * of the path so we can test for the directory existence. The
1184 * second's to make sure the server (and any requested service)
1185 * name we were given exists. It should be handled by the folder
1186 * existence test further below, but it doesn't work with news...
1188 * Update. Now we are stripping the delimiter in the tmpnodel version
1189 * so that we can pass that to folder_exists. Cyrus does not answer
1190 * that the folder exists if we leave the trailing delimiter.
1191 * Hubert 2004-12-17
1193 strncat(tmp, path, sizeof(tmp)-1-strlen(tmp));
1194 tmp[sizeof(tmp)-1] = '\0';
1195 strncpy(tmpnodel, tmp, sizeof(tmpnodel)-1);
1196 tmpnodel[sizeof(tmpnodel)-1] = '\0';
1198 if(he[AC_PATH].bldr_private != (void *) 0)
1199 fs_give(&he[AC_PATH].bldr_private);
1201 ps_global->mm_log_error = 0;
1202 if((delim = folder_delimiter(tmp)) != '\0'){
1203 if(*path){
1204 if(tmp[(i = strlen(tmp)) - 1] == delim)
1205 tmpnodel[i-1] = '\0';
1206 else{
1207 tmp[i] = delim;
1208 tmp[i+1] = '\0';
1209 he[AC_PATH].bldr_private = (void *) cpystr(&tmp[i]);
1213 else if(ps_global->mm_log_error && ps_global->last_error)
1214 /* We used to bail, but this was changed with 4.10
1215 * as some users wanted to add clctn before the server
1216 * was actually around and built.
1218 flush_status_messages(0); /* mail_create gripes */
1219 else
1220 dprint((1, "exit_col_test: No Server Hierarchy!\n"));
1223 if(!rstr){
1224 if(!*tmp
1225 || !delim
1226 || ((*(p = tmp) == '#'
1227 || (*tmp == '{' && (p = strchr(tmp, '}')) && *++p))
1228 && !struncmp(p, "#news.", 6))
1229 || (*tmp == '{' && (p = strchr(tmp, '}')) && !*++p)){
1230 exists = 1;
1232 else if((i = folder_exists(NULL, tmpnodel)) & FEX_ERROR){
1233 if(!(rstr = ps_global->last_error))
1234 rstr = _("Problem testing for directory existence");
1236 else
1237 exists = (i & FEX_ISDIR);
1239 if(exists)
1240 snprintf(prompt, sizeof(prompt), _("Exit and save changes"));
1241 else
1242 snprintf(prompt, sizeof(prompt), _("Exit, saving changes and creating Path"));
1244 if(want_to(prompt, 'y', 0, NO_HELP, WT_NORM) == 'y'){
1245 if(!exists && !pine_mail_create(NULL, tmp)){
1246 flush_status_messages(1); /* mail_create gripes */
1247 if(!(rstr = ps_global->last_error))
1248 rstr = "";
1251 else
1252 rstr = _("Use ^C to abandon changes you've made");
1255 if(result)
1256 *result = rstr;
1258 ps_global->redrawer = redraw;
1259 return((rstr == NULL) ? 0 : 1);
1263 char *
1264 cancel_collection_add(void (*redraw_pico)(void))
1266 return(cancel_collection_editing(_("Add"), redraw_pico));
1270 char *
1271 cancel_collection_edit(void (*redraw_pico)(void))
1273 return(cancel_collection_editing(_("Edit"), redraw_pico));
1277 char *
1278 cancel_collection_editing(char *func, void (*redraw_pico)(void))
1280 char *rstr = NULL;
1281 void (*redraw)(void) = ps_global->redrawer;
1282 static char rbuf[20];
1283 char prompt[256];
1284 #define CCA_PROMPT \
1285 _("Cancel Add (answering \"Yes\" will abandon any changes made) ")
1287 snprintf(prompt, sizeof(prompt), _("Cancel %s (answering \"Yes\" will abandon any changes made) "), func ? func : "Add");
1288 snprintf(rbuf, sizeof(rbuf), _("%s Cancelled) "), func ? func : "Add");
1290 ps_global->redrawer = redraw_pico;
1291 fix_windsize(ps_global);
1293 switch(want_to(prompt, 'y', 'x', NO_HELP, WT_NORM)){
1294 case 'y':
1295 rstr = rbuf;
1296 break;
1298 case 'n':
1299 case 'x':
1300 break;
1303 ps_global->redrawer = redraw;
1304 return(rstr);
1309 build_namespace(char *server, char **server_too, char **error, BUILDER_ARG *barg, int *mangled)
1311 char *p, *name;
1312 int we_cancel = 0;
1313 MAILSTREAM *stream;
1314 NAMESPACE ***namespace;
1315 size_t len;
1317 dprint((5, "- build_namespace - (%s)\n",
1318 server ? server : "nul"));
1320 if(*barg->me){ /* only call this once! */
1321 if(server_too)
1322 *server_too = cpystr(server ? server : "");
1324 return(0);
1326 else
1327 *barg->me = (void *) 1;
1329 if((p = server) != NULL) /* empty string? */
1330 while(*p && isspace((unsigned char) *p))
1331 p++;
1333 if(p && *p){
1334 if(server_too)
1335 *server_too = cpystr(p);
1337 len = strlen(p) + 2;
1338 name = (char *) fs_get((len + 1) * sizeof(char));
1339 snprintf(name, len+1, "{%s}", p);
1341 else{
1342 if(server_too)
1343 *server_too = cpystr("");
1345 return(0);
1348 *mangled |= BUILDER_SCREEN_MANGLED;
1349 fix_windsize(ps_global);
1350 init_sigwinch();
1351 clear_cursor_pos();
1353 we_cancel = busy_cue(_("Fetching default directory"), NULL, 1);
1355 if((stream = pine_mail_open(NULL, name,
1356 OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE,
1357 NULL)) != NULL){
1358 if((namespace = mail_parameters(stream, GET_NAMESPACE, NULL))
1359 && *namespace && (*namespace)[0]
1360 && (*namespace)[0]->name && (*namespace)[0]->name[0]){
1361 if(barg->tptr)
1362 fs_give((void **)&barg->tptr);
1364 barg->tptr = cpystr((*namespace)[0]->name);
1367 pine_mail_close(stream);
1370 if(we_cancel)
1371 cancel_busy_cue(-1);
1373 fs_give((void **) &name);
1375 return(1);
1380 fl_val_gen (FOLDER_S *f, FSTATE_S *fs)
1382 return(f && FLDR_NAME(f));
1387 fl_val_writable (FOLDER_S *f, FSTATE_S *fs)
1389 return(1);
1394 fl_val_subscribe (FOLDER_S *f, FSTATE_S *fs)
1396 if(f->subscribed){
1397 q_status_message1(SM_ORDER, 0, 4, _("Already subscribed to \"%s\""),
1398 FLDR_NAME(f));
1399 return(0);
1402 return(1);
1406 /*----------------------------------------------------------------------
1407 Business end of displaying and operating on a collection of folders
1409 Args: ps -- The pine_state data structure
1410 fs -- Folder screen state structure
1412 Result: A string list containing folders selected,
1413 NULL on Cancel, Error or other problem
1415 ----*/
1416 STRLIST_S *
1417 folder_lister(struct pine *ps, FSTATE_S *fs)
1419 int fltrv, we_cancel = 0;
1420 int first_time_through = 1;
1421 HANDLE_S *handles = NULL;
1422 STORE_S *screen_text = NULL;
1423 FPROC_S folder_proc_data;
1424 gf_io_t pc;
1426 dprint((1, "\n\n ---- FOLDER LISTER ----\n"));
1428 memset(&folder_proc_data, 0, sizeof(FPROC_S));
1429 folder_proc_data.fs = fs;
1431 while(!folder_proc_data.done){
1432 if((screen_text = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
1433 gf_set_so_writec(&pc, screen_text);
1435 else{
1436 q_status_message(SM_ORDER | SM_DING, 3, 3,
1437 "Formatting Error: Can't create space for list");
1438 return(NULL);
1441 we_cancel = busy_cue(_("Fetching folder data"), NULL, 1);
1442 fltrv = folder_list_text(ps, &folder_proc_data, pc, &handles,
1443 ps->ttyo->screen_cols);
1444 if(we_cancel)
1445 cancel_busy_cue(-1);
1447 if(fltrv){
1449 SCROLL_S sargs;
1450 struct key_menu km;
1451 struct key keys[36];
1453 memset(&sargs, 0, sizeof(SCROLL_S));
1454 sargs.text.text = so_text(screen_text);
1455 sargs.text.src = CharStar;
1456 sargs.text.desc = "folder list";
1457 if((sargs.text.handles = folder_list_handle(fs, handles)) != NULL)
1458 sargs.start.on = Handle;
1460 sargs.bar.title = fs->f.title.bar;
1461 sargs.bar.style = fs->f.title.style;
1463 sargs.proc.tool = folder_processor;
1464 sargs.proc.data.p = (void *) &folder_proc_data;
1466 sargs.quell_first_view = first_time_through ? 0 : 1;
1467 first_time_through = 0;
1469 sargs.resize_exit = 1;
1470 sargs.vert_handle = 1;
1471 sargs.srch_handle = 1;
1473 sargs.help.text = fs->f.help.text;
1474 sargs.help.title = fs->f.help.title;
1476 sargs.keys.menu = &km;
1477 km = *fs->km;
1478 km.keys = keys;
1479 memcpy(&keys[0], fs->km->keys,
1480 (km.how_many * 12) * sizeof(struct key));
1481 setbitmap(sargs.keys.bitmap);
1483 if(fs->km == &folder_km){
1484 sargs.keys.each_cmd = folder_lister_km_manager;
1485 #ifdef _WINDOWS
1486 sargs.mouse.popup = folder_list_popup;
1487 #endif
1489 else{
1490 #ifdef _WINDOWS
1491 sargs.mouse.popup = folder_list_select_popup;
1492 #endif
1493 if(fs->km == &folder_sel_km || fs->km == &folder_sela_km)
1494 sargs.keys.each_cmd = folder_lister_km_sel_manager;
1495 else if(fs->km == &folder_sub_km)
1496 sargs.keys.each_cmd = folder_lister_km_sub_manager;
1499 sargs.mouse.clickclick = folder_lister_clickclick;
1501 switch(scrolltool(&sargs)){
1502 case MC_MAIN : /* just leave */
1503 folder_proc_data.done = 1;
1504 break;
1506 case MC_RESIZE : /* loop around rebuilding screen */
1507 if(sargs.text.handles){
1508 FOLDER_S *fp;
1510 fp = folder_entry(sargs.text.handles->h.f.index,
1511 FOLDERS(sargs.text.handles->h.f.context));
1512 if(fp && strlen(FLDR_NAME(fp)) < MAXFOLDER -1){
1513 strncpy(fs->first_folder, FLDR_NAME(fp), MAXFOLDER);
1514 fs->first_folder[MAXFOLDER-1] = '\0';
1517 fs->context = sargs.text.handles->h.f.context;
1520 break;
1522 /*--------- EXIT menu -----------*/
1523 case MC_EXIT :
1524 case MC_EXITQUERY :
1525 fs->list_cntxt = NULL;
1526 folder_proc_data.done = folder_proc_data.all_done = 1;
1527 break;
1529 default :
1530 break;
1533 ps->noticed_change_in_unseen = 0;
1536 if(F_ON(F_BLANK_KEYMENU,ps))
1537 FOOTER_ROWS(ps) = 1;
1539 gf_clear_so_writec(screen_text);
1540 so_give(&screen_text);
1541 free_handles(&handles);
1543 else
1544 folder_proc_data.done = 1;
1547 reset_context_folders(fs->context);
1549 if(folder_proc_data.all_done)
1550 fs->context = NULL;
1552 if(fs->cache_streamp && *fs->cache_streamp){
1553 int i;
1556 * check stream pool to see if currently cached
1557 * stream went away
1559 for(i = 0; i < ps->s_pool.nstream; i++)
1560 if(ps->s_pool.streams[i] == *fs->cache_streamp)
1561 break;
1562 if(i == ps->s_pool.nstream)
1563 *fs->cache_streamp = NULL;
1566 return(folder_proc_data.rv);
1571 * folder_list_text - format collection's contents for display
1574 folder_list_text(struct pine *ps, FPROC_S *fp, gf_io_t pc, HANDLE_S **handlesp, int cols)
1576 int rv = 1, i, j, ftotal, fcount, slot_width, slot_rows,
1577 slot_cols, index, findex, width, shown, selected;
1578 CONTEXT_S *c_list;
1579 char lbuf[6*MAX_SCREEN_COLS+1];
1581 /* disarm this trigger that gets us out of scrolltool */
1582 ps->noticed_change_in_unseen = 0;
1584 if(handlesp)
1585 init_handles(handlesp);
1587 c_list = fp->fs->context;
1588 if(fp->fs->combined_view
1589 && (F_ON(F_CMBND_SUBDIR_DISP, ps_global) || !c_list->dir->prev))
1590 while(c_list->prev) /* rewind to start */
1591 c_list = c_list->prev;
1594 ps->user_says_cancel = 0;
1596 /* If we're displaying folders, fetch the list */
1597 if((shown = (c_list == fp->fs->context
1598 || (c_list->dir->status & CNTXT_NOFIND) == 0
1599 || F_ON(F_EXPANDED_FOLDERS, ps_global))) != 0){
1601 * if select is allowed, flag context so any that are
1602 * are remembered even after the list is destroyed
1604 if(fp->fs->agg_ops)
1605 c_list->use |= CNTXT_PRESRV;
1607 /* Make sure folder list filled in */
1608 refresh_folder_list(c_list, fp->fs->no_dirs, FALSE, fp->fs->cache_streamp);
1611 /* Insert any introductory text here */
1612 if(c_list->next
1613 || c_list->prev
1614 || c_list->dir->prev
1615 || fp->fs->force_intro){
1617 /* Leading horizontal line? */
1618 if(fp->fs->combined_view
1619 && (F_ON(F_CMBND_SUBDIR_DISP,ps_global)
1620 || !c_list->dir->prev)){
1621 if(c_list->prev)
1622 gf_puts("\n", pc); /* blank line */
1624 color_write_for_folder(pc, CLR_FLDRLT);
1625 gf_puts(repeat_char(cols, '-'), pc);
1626 gf_puts("\n", pc);
1627 color_write_for_folder(pc, CLR_NORMAL);
1630 /* nickname or description */
1631 if(F_ON(F_CMBND_FOLDER_DISP, ps_global)
1632 && (!c_list->dir->prev
1633 || F_ON(F_CMBND_SUBDIR_DISP, ps_global))){
1634 char buf[6*MAX_SCREEN_COLS + 1];
1636 color_write_for_folder(pc, CLR_FLDRLT);
1637 if(cols < 40){
1638 snprintf(buf, sizeof(buf), "%.*s", cols,
1639 strsquish(tmp_20k_buf, SIZEOF_20KBUF,
1640 (c_list->nickname)
1641 ? c_list->nickname
1642 : (c_list->label ? c_list->label : ""),
1643 cols));
1644 gf_puts(folder_list_center_space(buf, cols), pc);
1646 else{
1647 int wid;
1649 snprintf(buf, sizeof(buf), "%s-Collection <",
1650 NEWS_TEST(c_list) ? "News" : "Folder");
1651 wid = utf8_width(buf)+1;
1652 wid = MAX(0, cols-wid);
1653 snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%.*s>", wid,
1654 strsquish(tmp_20k_buf, SIZEOF_20KBUF,
1655 (c_list->nickname)
1656 ? c_list->nickname
1657 : (c_list->label ? c_list->label : ""),
1658 wid));
1661 gf_puts(buf, pc);
1662 gf_puts("\n", pc);
1663 color_write_for_folder(pc, CLR_NORMAL);
1665 else if(c_list->label){
1666 if(utf8_width(c_list->label) > ps_global->ttyo->screen_cols)
1667 utf8_pad_to_width(lbuf, c_list->label, sizeof(lbuf), ps_global->ttyo->screen_cols, 1);
1668 else
1669 strncpy(lbuf, c_list->label, sizeof(lbuf));
1671 lbuf[sizeof(lbuf)-1] = '\0';
1673 color_write_for_folder(pc, CLR_FLDRLT);
1674 gf_puts(folder_list_center_space(lbuf, cols), pc);
1675 gf_puts(lbuf, pc);
1676 gf_puts("\n", pc);
1677 color_write_for_folder(pc, CLR_NORMAL);
1680 if(c_list->comment){
1681 if(utf8_width(c_list->comment) > ps_global->ttyo->screen_cols)
1682 utf8_pad_to_width(lbuf, c_list->comment, sizeof(lbuf), ps_global->ttyo->screen_cols, 1);
1683 else
1684 strncpy(lbuf, c_list->comment, sizeof(lbuf));
1686 lbuf[sizeof(lbuf)-1] = '\0';
1688 color_write_for_folder(pc, CLR_FLDRLT);
1689 gf_puts(folder_list_center_space(lbuf, cols), pc);
1690 gf_puts(lbuf, pc);
1691 gf_puts("\n", pc);
1692 color_write_for_folder(pc, CLR_NORMAL);
1695 if(c_list->dir->desc){
1696 char buf[6*MAX_SCREEN_COLS + 1];
1698 strncpy(buf, strsquish(tmp_20k_buf,SIZEOF_20KBUF,c_list->dir->desc,cols),
1699 sizeof(buf)-1);
1700 buf[sizeof(buf)-1] = '\0';
1701 color_write_for_folder(pc, CLR_FLDRLT);
1702 gf_puts(folder_list_center_space(buf, cols), pc);
1703 gf_puts(buf, pc);
1704 gf_puts("\n", pc);
1705 color_write_for_folder(pc, CLR_NORMAL);
1708 if(c_list->use & CNTXT_ZOOM){
1709 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "[ ZOOMED on %d (of %d) %ss ]",
1710 selected_folders(c_list),
1711 folder_total(FOLDERS(c_list)),
1712 (c_list->use & CNTXT_NEWS) ? "Newsgroup" : "Folder");
1713 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1715 if(utf8_width(tmp_20k_buf) > ps_global->ttyo->screen_cols)
1716 utf8_pad_to_width(lbuf, tmp_20k_buf, sizeof(lbuf), ps_global->ttyo->screen_cols, 1);
1717 else
1718 strncpy(lbuf, tmp_20k_buf, sizeof(lbuf));
1720 lbuf[sizeof(lbuf)-1] = '\0';
1722 color_write_for_folder(pc, CLR_FLDRLT);
1723 gf_puts(folder_list_center_space(lbuf, cols), pc);
1724 gf_puts(lbuf, pc);
1725 gf_puts("\n", pc);
1726 color_write_for_folder(pc, CLR_NORMAL);
1729 color_write_for_folder(pc, CLR_FLDRLT);
1730 gf_puts(repeat_char(cols, '-'), pc);
1731 gf_puts("\n\n", pc);
1732 color_write_for_folder(pc, CLR_NORMAL);
1735 if(shown && LUU_YES(c_list->update)){
1736 /* Run thru list formatting as necessary */
1737 if((ftotal = folder_total(FOLDERS(c_list))) != 0){
1738 int use_color;
1739 /* If previously selected, mark members of new list */
1740 selected = selected_folders(c_list);
1742 /* How many screen cells per cell for each folder name? */
1743 slot_width = 1;
1744 for(fcount = i = 0; i < ftotal; i++){
1745 FOLDER_S *f = folder_entry(i, FOLDERS(c_list));
1746 unsigned char *fname;
1748 ps->user_says_cancel = 0;
1750 if((c_list->use & CNTXT_ZOOM) && !f->selected)
1751 continue;
1753 fcount++;
1755 use_color = use_color_for_folder(f);
1756 fname = folder_name_decoded((unsigned char *)FLDR_NAME(f));
1758 width = utf8_width(fname ? (char *)fname : FLDR_NAME(f));
1760 if(fname)
1761 fs_give((void **)&fname);
1763 if(f->isdir)
1764 width += (f->isfolder) ? (use_color ? 1 : 3) : (use_color ? 0 : 1);
1766 if(NEWS_TEST(c_list) && (c_list->use & CNTXT_FINDALL))
1767 /* assume listmode so we don't have to reformat */
1768 /* if listmode is actually entered */
1769 width += 4;
1771 if(selected){
1772 if(F_OFF(F_SELECTED_SHOWN_BOLD, ps_global))
1773 width += 4; /* " X " */
1775 else if(c_list == fp->fs->list_cntxt)
1776 width += 4; /* "[X] " */
1778 if(fp->fs->include_unseen_cnt
1779 && c_list->use & CNTXT_INCMNG
1780 && F_ON(F_ENABLE_INCOMING_CHECKING, ps_global)
1781 && ps_global->VAR_INCOMING_FOLDERS
1782 && ps_global->VAR_INCOMING_FOLDERS[0]){
1784 update_folder_unseen(f, c_list, UFU_ANNOUNCE, NULL);
1785 if(F_ON(F_INCOMING_CHECKING_RECENT, ps_global)
1786 && f->unseen_valid
1787 && (f->new > 0L
1788 || F_ON(F_INCOMING_CHECKING_TOTAL, ps_global))){
1789 width += (strlen(tose(f->new)) + 3);
1790 if(F_ON(F_INCOMING_CHECKING_TOTAL, ps_global))
1791 width += (strlen(tose(f->total)) + 1);
1793 else if(F_OFF(F_INCOMING_CHECKING_RECENT, ps_global)
1794 && f->unseen_valid
1795 && (f->unseen > 0L
1796 || F_ON(F_INCOMING_CHECKING_TOTAL, ps_global))){
1797 width += (strlen(tose(f->unseen)) + 3);
1798 if(F_ON(F_INCOMING_CHECKING_TOTAL, ps_global))
1799 width += (strlen(tose(f->total)) + 1);
1801 else if(!f->unseen_valid && f->last_unseen_update != LUU_NEVERCHK)
1802 width += 4; /* " (?)" */
1805 if(slot_width < width)
1806 slot_width = width;
1809 if(F_ON(F_SINGLE_FOLDER_LIST, ps_global)){
1810 slot_cols = 1;
1811 slot_rows = fcount;
1813 else{
1814 /* fit as many columns as possible */
1815 slot_cols = 1;
1816 while(((slot_cols+1) * slot_width) + slot_cols <= cols)
1817 slot_cols++;
1819 switch(slot_cols){
1820 case 0 :
1821 slot_cols = 1;
1822 /* fall through */
1824 case 1 :
1825 slot_rows = fcount;
1826 break;
1828 default :
1830 * Make the slot_width as large as possible.
1831 * Slot_width is the width of the column, not counting
1832 * the space between columns.
1834 while((slot_cols * (slot_width+1)) + slot_cols-1 <= cols)
1835 slot_width++;
1837 slot_rows = (fcount / slot_cols)
1838 + ((fcount % slot_cols) ? 1 : 0);
1839 break;
1843 for(i = index = 0; i < slot_rows; i++){
1844 if(i)
1845 gf_puts("\n", pc);
1847 for(j = width = 0; j < slot_cols; j++, index++){
1848 if(width){
1849 gf_puts(repeat_char(slot_width + 1 - width, ' '), pc);
1850 width = 0;
1853 if(F_ON(F_VERTICAL_FOLDER_LIST, ps_global))
1854 index = i + (j * slot_rows);
1856 findex = index;
1858 if(c_list->use & CNTXT_ZOOM)
1859 findex = folder_list_ith(index, c_list);
1861 if(findex < ftotal){
1862 int flags = (handlesp) ? FLW_LUNK : FLW_NONE;
1864 if(c_list == fp->fs->list_cntxt)
1865 flags |= FLW_LIST;
1866 else if(selected)
1867 flags |= FLW_SLCT;
1868 else if(F_ON(F_SINGLE_FOLDER_LIST, ps_global)
1869 || ((c_list->use & CNTXT_FINDALL)
1870 && NEWS_TEST(c_list)))
1871 gf_puts(" ", pc);
1873 if(fp->fs->include_unseen_cnt
1874 && c_list->use & CNTXT_INCMNG
1875 && F_ON(F_ENABLE_INCOMING_CHECKING, ps_global)
1876 && ps_global->VAR_INCOMING_FOLDERS
1877 && ps_global->VAR_INCOMING_FOLDERS[0])
1878 flags |= FLW_UNSEEN;
1880 width = folder_list_write(pc, handlesp, c_list,
1881 findex, NULL, flags);
1886 else if(fp->fs->combined_view
1887 && (F_ON(F_CMBND_SUBDIR_DISP, ps_global)
1888 || !c_list->dir->prev)){
1889 char *emptiness = N_("[No Folders in Collection]");
1891 if(utf8_width(_(emptiness)) > ps_global->ttyo->screen_cols)
1892 utf8_pad_to_width(lbuf, _(emptiness), sizeof(lbuf), ps_global->ttyo->screen_cols, 1);
1893 else
1894 strncpy(lbuf, _(emptiness), sizeof(lbuf));
1896 lbuf[sizeof(lbuf)-1] = '\0';
1898 color_write_for_folder(pc, CLR_FLDRLT);
1899 gf_puts(folder_list_center_space(lbuf, cols), pc);
1900 (void) folder_list_write(pc, handlesp, c_list, -1, lbuf,
1901 (handlesp) ? FLW_LUNK : FLW_NONE);
1902 color_write_for_folder(pc, CLR_NORMAL);
1905 else if(fp->fs->combined_view
1906 && (F_ON(F_CMBND_SUBDIR_DISP, ps_global)
1907 || !c_list->dir->prev)){
1908 char *unexpanded = N_("[Select Here to See Expanded List]");
1910 if(utf8_width(_(unexpanded)) > ps_global->ttyo->screen_cols)
1911 utf8_pad_to_width(lbuf, _(unexpanded), sizeof(lbuf), ps_global->ttyo->screen_cols, 1);
1912 else
1913 strncpy(lbuf, _(unexpanded), sizeof(lbuf));
1915 lbuf[sizeof(lbuf)-1] = '\0';
1917 color_write_for_folder(pc, CLR_FLDRLT);
1918 gf_puts(folder_list_center_space(lbuf, cols), pc);
1919 (void) folder_list_write(pc, handlesp, c_list, -1, lbuf,
1920 (handlesp) ? FLW_LUNK : FLW_NONE);
1921 color_write_for_folder(pc, CLR_NORMAL);
1924 gf_puts("\n", pc); /* blank line */
1927 while(fp->fs->combined_view
1928 && (F_ON(F_CMBND_SUBDIR_DISP, ps_global) || !c_list->dir->prev)
1929 && (c_list = c_list->next));
1931 return(rv);
1936 folder_list_write(gf_io_t pc, HANDLE_S **handlesp, CONTEXT_S *ctxt, int fnum, char *alt_name, int flags)
1938 char buf[256];
1939 int width = 0, lprefix = 0, lmiddle = 0, lsuffix = 0;
1940 FOLDER_S *fp;
1941 HANDLE_S *h1 = NULL, *h2 = NULL;
1943 if(flags & FLW_LUNK){
1944 h1 = new_handle(handlesp);
1945 h1->type = Folder;
1946 h1->h.f.index = fnum;
1947 h1->h.f.context = ctxt;
1948 h1->force_display = 1;
1950 snprintf(buf, sizeof(buf), "%d", h1->key);
1951 buf[sizeof(buf)-1] = '\0';
1954 fp = (fnum < 0) ? NULL : folder_entry(fnum, FOLDERS(ctxt));
1956 if(flags & FLW_LUNK && h1 && fp && fp->isdir && fp->isfolder){
1957 h2 = new_handle(handlesp);
1958 h2->type = Folder;
1959 h2->h.f.index = fnum;
1960 h2->h.f.context = ctxt;
1961 h2->force_display = 1;
1963 h1->is_dual_do_open = 1;
1966 if(h1){
1967 /* color unseen? */
1968 if(color_monitored(fp, flags, CLR_UNSEEN)){
1969 h1->color_unseen = 1;
1970 if(h2)
1971 h2->color_unseen = 1;
1973 /* color folder? */
1974 if(color_monitored(fp, flags, CLR_FOLDER)){
1975 h1->color_folder = 1;
1976 if(h2)
1977 h2->color_folder = 1;
1981 /* embed handle pointer */
1982 if(((h1 && h1->color_unseen) ? color_write_for_folder(pc, CLR_UNSEEN)
1983 : ((h1 && h1->color_folder)
1984 ? color_write_for_folder(pc, fp->isfolder ? CLR_FOLDER : CLR_DIRECT)
1985 : 1))
1986 && (h1 ? ((*pc)(TAG_EMBED) && (*pc)(TAG_HANDLE)
1987 && (*pc)(strlen(buf)) && gf_puts(buf, pc)) : 1)
1988 && (fp ? ((lprefix = folder_list_write_prefix(fp, flags, pc)) >= 0
1989 && (lmiddle = folder_list_write_middle(fp, ctxt, pc, h2)) >= 0
1990 && ((lsuffix = folder_list_write_suffix(fp, flags, pc)) >= 0))
1991 : (alt_name ? gf_puts(alt_name, pc) : 0))
1992 && (h1 ? ((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF)
1993 && (*pc)(TAG_EMBED) && (*pc)(TAG_INVOFF)) : 1)
1994 && ((h1 && (h1->color_unseen || h1->color_folder)) ?
1995 color_write_for_folder(pc, CLR_NORMAL): 1)){
1996 if(fp)
1997 width = lprefix + lmiddle + lsuffix;
1998 else if(alt_name)
1999 width = utf8_width(alt_name);
2002 return(width);
2007 folder_list_write_prefix(FOLDER_S *f, int flags, gf_io_t pc)
2009 int rv = 0;
2011 if(flags & FLW_SLCT){
2012 if(F_OFF(F_SELECTED_SHOWN_BOLD, ps_global) || !(flags & FLW_LUNK)){
2013 rv = 4;
2014 if(f->selected){
2015 gf_puts(" X ", pc);
2017 else{
2018 gf_puts(" ", pc);
2021 else
2022 rv = ((*pc)(TAG_EMBED)
2023 && (*pc)((f->selected) ? TAG_BOLDON : TAG_BOLDOFF)) ? 0 : -1;
2025 else if(flags & FLW_LIST){
2026 rv = 4;
2027 /* screen width of "SUB " is 4 */
2028 gf_puts(f->subscribed ? "SUB " : (f->selected ? "[X] " : "[ ] "), pc);
2031 return(rv);
2035 folder_list_write_middle(FOLDER_S *fp, CONTEXT_S *ctxt, gf_io_t pc, HANDLE_S *h2)
2037 int rv = -1, use_color;
2038 char buf[256];
2039 unsigned char *fname;
2041 if(h2){
2042 snprintf(buf, sizeof(buf), "%d", h2->key);
2043 buf[sizeof(buf)-1] = '\0';
2046 if(!fp)
2047 return(rv);
2049 fname = folder_name_decoded((unsigned char *)FLDR_NAME(fp));
2050 use_color = use_color_for_folder(fp);
2052 if(gf_puts(fname ? (char *)fname : FLDR_NAME(fp), pc)
2053 && (h2 ? ((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF) /* tie off handle 1 */
2054 && (*pc)(TAG_EMBED) && (*pc)(TAG_INVOFF)) : 1)
2055 && ((use_color && fp->isdir && fp->isfolder)
2056 ? color_write_for_folder(pc, CLR_DIRECT) : 1)
2057 && (h2 ? ((*pc)(TAG_EMBED) && (*pc)(TAG_HANDLE) /* start handle 2 */
2058 && (*pc)(strlen(buf)) && gf_puts(buf, pc)) : 1)
2059 && use_color
2060 ? (fp->isdir && fp->isfolder ? (*pc)(ctxt->dir->delim) : 1)
2061 : (((fp->isdir && fp->isfolder) ? (*pc)('[') : 1)
2062 && ((fp->isdir) ? (*pc)(ctxt->dir->delim) : 1)
2063 && ((fp->isdir && fp->isfolder) ? (*pc)(']') : 1))){
2064 rv = utf8_width(fname ? (char *)fname : FLDR_NAME(fp));
2065 if(fp->isdir)
2066 rv += (fp->isfolder) ? (use_color ? 1 : 3) : (use_color ? 0 : 1);
2069 if(fname)
2070 fs_give((void **)&fname);
2072 return(rv);
2077 folder_list_write_suffix(FOLDER_S *f, int flags, gf_io_t pc)
2079 int rv = 0;
2081 if(flags & FLW_UNSEEN){
2082 char buf[100];
2084 buf[0] = '\0';
2085 if(F_ON(F_INCOMING_CHECKING_RECENT, ps_global)
2086 && f->unseen_valid
2087 && (f->new > 0L
2088 || (F_ON(F_INCOMING_CHECKING_TOTAL, ps_global)
2089 && f->total > 0L))){
2090 snprintf(buf, sizeof(buf), " (%s%s%s)",
2091 tose(f->new),
2092 F_ON(F_INCOMING_CHECKING_TOTAL, ps_global) ? "/" : "",
2093 F_ON(F_INCOMING_CHECKING_TOTAL, ps_global) ? tose(f->total) : "");
2095 else if(F_OFF(F_INCOMING_CHECKING_RECENT, ps_global)
2096 && f->unseen_valid
2097 && (f->unseen > 0L
2098 || (F_ON(F_INCOMING_CHECKING_TOTAL, ps_global)
2099 && f->total > 0L))){
2100 snprintf(buf, sizeof(buf), " (%s%s%s)",
2101 tose(f->unseen),
2102 F_ON(F_INCOMING_CHECKING_TOTAL, ps_global) ? "/" : "",
2103 F_ON(F_INCOMING_CHECKING_TOTAL, ps_global) ? tose(f->total) : "");
2105 else if(!f->unseen_valid && f->last_unseen_update != LUU_NEVERCHK){
2106 snprintf(buf, sizeof(buf), " (?)");
2109 rv = strlen(buf);
2110 if(rv)
2111 gf_puts(buf, pc);
2114 return(rv);
2118 color_write_for_folder(gf_io_t pc, int testtype)
2120 int rv;
2121 if(!pico_usingcolor())
2122 return 1;
2123 switch (testtype){
2124 case CLR_UNSEEN:
2125 rv = gf_puts(color_embed(ps_global->VAR_INCUNSEEN_FORE_COLOR,
2126 ps_global->VAR_INCUNSEEN_BACK_COLOR), pc);
2127 break;
2128 case CLR_FOLDER:
2129 rv = gf_puts(color_embed(ps_global->VAR_FOLDER_FORE_COLOR,
2130 ps_global->VAR_FOLDER_BACK_COLOR ), pc);
2131 break;
2132 case CLR_FLDRLT:
2133 rv = gf_puts(color_embed(ps_global->VAR_FOLDER_LIST_FORE_COLOR,
2134 ps_global->VAR_FOLDER_LIST_BACK_COLOR ), pc);
2135 break;
2136 case CLR_DIRECT:
2137 rv = gf_puts(color_embed(ps_global->VAR_DIRECTORY_FORE_COLOR,
2138 ps_global->VAR_DIRECTORY_BACK_COLOR), pc);
2139 break;
2140 case CLR_NORMAL:
2141 rv = gf_puts(color_embed(ps_global->VAR_NORM_FORE_COLOR,
2142 ps_global->VAR_NORM_BACK_COLOR), pc);
2143 break;
2144 default:
2145 rv = 0; /* fail */
2146 break;
2148 return rv;
2153 color_test_for_folder(char *color_fore, char *color_back)
2155 return pico_usingcolor()
2156 && pico_is_good_color(color_fore)
2157 && pico_is_good_color(color_back)
2158 && (colorcmp(color_fore, ps_global->VAR_NORM_FORE_COLOR)
2159 || colorcmp(color_back,
2160 ps_global->VAR_NORM_BACK_COLOR)) ? 1 : 0;
2166 use_color_for_folder(FOLDER_S *fp)
2168 int test1 = 0, test2 = 0;
2169 if(fp->isdir)
2170 test1 = color_test_for_folder(ps_global->VAR_DIRECTORY_FORE_COLOR,
2171 ps_global->VAR_DIRECTORY_BACK_COLOR);
2172 if(fp->isfolder)
2173 test2 = color_test_for_folder(ps_global->VAR_FOLDER_FORE_COLOR,
2174 ps_global->VAR_FOLDER_BACK_COLOR);
2175 return (fp->isdir && fp->isfolder) ? test1 + test2
2176 : (fp->isdir ? test1 : (fp->isfolder? test2 : 0));
2181 color_monitored(FOLDER_S *f, int flags, int testtype)
2183 int rv;
2184 switch(testtype){
2185 case CLR_UNSEEN: rv = (flags & FLW_UNSEEN) && f && f->unseen_valid
2186 && ((F_ON(F_INCOMING_CHECKING_RECENT, ps_global) && f->new > 0L)
2187 || (F_OFF(F_INCOMING_CHECKING_RECENT, ps_global) && f->unseen > 0L))
2188 && color_test_for_folder(ps_global->VAR_INCUNSEEN_FORE_COLOR,
2189 ps_global->VAR_INCUNSEEN_BACK_COLOR) ? 1 : 0;
2190 break;
2191 case CLR_FOLDER: rv = f ? use_color_for_folder(f) : 0;
2192 break;
2193 default: rv = 0;
2195 return rv;
2200 folder_list_ith(int n, CONTEXT_S *cntxt)
2202 int index, ftotal;
2203 FOLDER_S *f;
2205 for(index = 0, ftotal = folder_total(FOLDERS(cntxt));
2206 index < ftotal
2207 && (f = folder_entry(index, FOLDERS(cntxt)))
2208 && !(f->selected && !n--);
2209 index++)
2212 return(index);
2216 char *
2217 folder_list_center_space(char *s, int width)
2219 int l;
2221 return(((l = utf8_width(s)) < width) ? repeat_char((width - l)/2, ' ') : "");
2226 * folder_list_handle - return pointer in handle list
2227 * corresponding to "start"
2229 HANDLE_S *
2230 folder_list_handle(FSTATE_S *fs, HANDLE_S *handles)
2232 char *p, *name = NULL;
2233 HANDLE_S *h, *h_found = NULL;
2234 FOLDER_S *fp;
2236 if(handles && fs->context){
2237 if(!(NEWS_TEST(fs->context) || (fs->context->use & CNTXT_INCMNG))
2238 && fs->context->dir->delim)
2239 for(p = strchr(fs->first_folder, fs->context->dir->delim);
2241 p = strchr(p, fs->context->dir->delim))
2242 name = ++p;
2244 for(h = handles; h; h = h->next)
2245 if(h->h.f.context == fs->context){
2246 if(!h_found) /* match at least given context */
2247 h_found = h;
2249 if(!fs->first_folder[0]
2250 || ((fp = folder_entry(h->h.f.index, FOLDERS(h->h.f.context)))
2251 && ((fs->first_dir && fp->isdir)
2252 || (!fs->first_dir && fp->isfolder))
2253 && ((fp->nickname && !strcmp(name ? name : fs->first_folder, fp->nickname))
2254 || (fp->name && !strcmp(name ? name : fs->first_folder, fp->name))))){
2255 h_found = h;
2256 break;
2260 fs->first_folder[0] = '\0';
2263 return(h_found ? h_found : handles);
2268 folder_processor(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
2270 int rv = 0;
2271 char tmp_output[MAILTMPLEN];
2273 switch(cmd){
2274 case MC_FINISH :
2275 FPROC(sparms)->done = rv = 1;;
2276 break;
2278 /*---------- Select or enter a View ----------*/
2279 case MC_CHOICE :
2280 rv = folder_lister_choice(sparms);
2281 break;
2283 /*--------- Hidden "To Fldrs" command -----------*/
2284 case MC_LISTMODE :
2285 if(sparms->text.handles
2286 && folder_total(FOLDERS(sparms->text.handles->h.f.context))){
2287 if(!FPROC(sparms)->fs->list_cntxt){
2288 FPROC(sparms)->fs->list_cntxt
2289 = sparms->text.handles->h.f.context;
2290 rv = scroll_add_listmode(sparms->text.handles->h.f.context,
2291 folder_total(FOLDERS(sparms->text.handles->h.f.context)));
2292 if(!rv){
2293 /* need to set the subscribe key ourselves */
2294 sparms->keys.menu->keys[SB_SUB_KEY].name = "S";
2295 sparms->keys.menu->keys[SB_SUB_KEY].label = N_("Subscribe");
2296 sparms->keys.menu->keys[SB_SUB_KEY].bind.cmd = MC_CHOICE;
2297 sparms->keys.menu->keys[SB_SUB_KEY].bind.ch[0] = 's';
2298 setbitn(SB_SUB_KEY, sparms->keys.bitmap);
2299 ps_global->mangled_screen = 1;
2301 sparms->keys.menu->keys[SB_EXIT_KEY].bind.cmd = MC_EXITQUERY;
2303 q_status_message(SM_ORDER, 0, 1, LISTMODE_GRIPE);
2305 else
2306 q_status_message(SM_ORDER, 0, 4, _("Already in List Mode"));
2308 else
2309 q_status_message(SM_ORDER, 0, 4,
2310 _("No Folders! Can't enter List Mode"));
2312 break;
2315 /*--------- Visit parent directory -----------*/
2316 case MC_PARENT :
2317 if(folder_lister_parent(FPROC(sparms)->fs,
2318 (sparms->text.handles)
2319 ? sparms->text.handles->h.f.context
2320 : FPROC(sparms)->fs->context,
2321 (sparms->text.handles)
2322 ? sparms->text.handles->h.f.index : -1, 0))
2323 rv = 1; /* leave scrolltool to rebuild screen */
2325 break;
2328 /*--------- Open the selected folder -----------*/
2329 case MC_OPENFLDR :
2330 if(sparms->text.handles
2331 && folder_total(FOLDERS(sparms->text.handles->h.f.context)))
2332 rv = folder_lister_finish(sparms, sparms->text.handles->h.f.context,
2333 sparms->text.handles->h.f.index);
2334 else
2335 q_status_message(SM_ORDER, 0, 4,
2336 _("No Folders! Nothing to View"));
2338 break;
2341 /*--------- Export the selected folder -----------*/
2342 case MC_EXPORT :
2343 folder_export(sparms);
2344 break;
2347 /*--------- Import the selected folder -----------*/
2348 case MC_IMPORT :
2350 CONTEXT_S *cntxt = (sparms->text.handles)
2351 ? sparms->text.handles->h.f.context
2352 : FPROC(sparms)->fs->context;
2353 char new_file[2*MAXFOLDER+10];
2354 int r;
2356 new_file[0] = '\0';
2358 r = folder_import(sparms, new_file, sizeof(new_file));
2360 if(r && (cntxt->use & CNTXT_INCMNG || context_isambig(new_file))){
2361 rv = 1; /* rebuild display! */
2362 FPROC(sparms)->fs->context = cntxt;
2363 if(strlen(new_file) < MAXFOLDER - 1){
2364 strncpy(FPROC(sparms)->fs->first_folder, new_file, MAXFOLDER);
2365 FPROC(sparms)->fs->first_folder[MAXFOLDER-1] = '\0';
2368 else
2369 ps_global->mangled_footer++;
2372 break;
2375 /*--------- Return to the Collections Screen -----------*/
2376 case MC_COLLECTIONS :
2377 FPROC(sparms)->done = rv = 1;
2378 break;
2381 /*--------- QUIT pine -----------*/
2382 case MC_QUIT :
2383 ps_global->next_screen = quit_screen;
2384 FPROC(sparms)->done = rv = 1;
2385 break;
2388 /*--------- Compose -----------*/
2389 case MC_COMPOSE :
2390 ps_global->next_screen = compose_screen;
2391 FPROC(sparms)->done = rv = 1;
2392 break;
2395 /*--------- Alt Compose -----------*/
2396 case MC_ROLE :
2397 ps_global->next_screen = alt_compose_screen;
2398 FPROC(sparms)->done = rv = 1;
2399 break;
2402 /*--------- Message Index -----------*/
2403 case MC_INDEX :
2404 if(THREADING()
2405 && sp_viewing_a_thread(ps_global->mail_stream)
2406 && unview_thread(ps_global, ps_global->mail_stream, ps_global->msgmap)){
2407 ps_global->next_screen = mail_index_screen;
2408 ps_global->view_skipped_index = 0;
2409 ps_global->mangled_screen = 1;
2412 ps_global->next_screen = mail_index_screen;
2413 FPROC(sparms)->done = rv = 1;
2414 break;
2417 /*----------------- Add a new folder name -----------*/
2418 case MC_ADDFLDR :
2420 CONTEXT_S *cntxt = (sparms->text.handles)
2421 ? sparms->text.handles->h.f.context
2422 : FPROC(sparms)->fs->context;
2423 char new_file[2*MAXFOLDER+10];
2424 int r;
2426 if(NEWS_TEST(cntxt))
2427 r = group_subscription(new_file, sizeof(new_file), cntxt);
2428 else{
2429 r = add_new_folder(cntxt, Main, V_INCOMING_FOLDERS, new_file,
2430 sizeof(new_file),
2431 FPROC(sparms)->fs->cache_streamp
2432 ? *FPROC(sparms)->fs->cache_streamp : NULL,
2433 NULL);
2434 if(ps_global->prc && ps_global->prc->outstanding_pinerc_changes)
2435 write_pinerc(ps_global, Main, WRP_NONE);
2438 if(r && (cntxt->use & CNTXT_INCMNG || context_isambig(new_file))){
2439 rv = 1; /* rebuild display! */
2440 FPROC(sparms)->fs->context = cntxt;
2441 if(strlen(new_file) < MAXFOLDER - 1){
2442 strncpy(FPROC(sparms)->fs->first_folder, new_file, MAXFOLDER);
2443 FPROC(sparms)->fs->first_folder[MAXFOLDER-1] = '\0';
2446 else
2447 ps_global->mangled_footer++;
2450 break;
2453 /*------ Type in new folder name, e.g., for save ----*/
2454 case MC_ADD :
2455 rv = folder_lister_addmanually(sparms);
2456 break;
2459 /*--------------- Rename folder ----------------*/
2460 case MC_RENAMEFLDR :
2461 if(sparms->text.handles
2462 && folder_total(FOLDERS(sparms->text.handles->h.f.context))){
2463 char new_file[MAXFOLDER+1];
2464 int r;
2466 r = rename_folder(sparms->text.handles->h.f.context,
2467 sparms->text.handles->h.f.index, new_file,
2468 sizeof(new_file),
2469 FPROC(sparms)->fs->cache_streamp
2470 ? *FPROC(sparms)->fs->cache_streamp : NULL);
2472 if(r){
2473 /* repaint, placing cursor on new folder! */
2474 rv = 1;
2475 if(context_isambig(new_file)){
2476 FPROC(sparms)->fs->context
2477 = sparms->text.handles->h.f.context;
2478 if(strlen(new_file) < MAXFOLDER - 1){
2479 strncpy(FPROC(sparms)->fs->first_folder, new_file, MAXFOLDER);
2480 FPROC(sparms)->fs->first_folder[MAXFOLDER-1] = '\0';
2485 ps_global->mangled_footer++;
2487 else
2488 q_status_message(SM_ORDER | SM_DING, 0, 4,
2489 _("Empty folder collection. No folder to rename!"));
2491 break;
2494 /*-------------- Delete --------------------*/
2495 case MC_DELETE :
2496 if(!(sparms->text.handles
2497 && folder_total(FOLDERS(sparms->text.handles->h.f.context)))){
2498 q_status_message(SM_ORDER | SM_DING, 0, 4,
2499 _("Empty folder collection. No folder to delete!"));
2501 else{
2502 char next_folder[MAILTMPLEN+1];
2504 next_folder[0] = '\0';
2505 if(delete_folder(sparms->text.handles->h.f.context,
2506 sparms->text.handles->h.f.index,
2507 next_folder, sizeof(next_folder),
2508 FPROC(sparms)->fs->cache_streamp
2509 && *FPROC(sparms)->fs->cache_streamp
2510 ? FPROC(sparms)->fs->cache_streamp : NULL)){
2512 /* repaint, placing cursor on adjacent folder! */
2513 rv = 1;
2514 if(next_folder[0] && strlen(next_folder) < MAXFOLDER - 1){
2515 strncpy(FPROC(sparms)->fs->first_folder, next_folder, MAXFOLDER);
2516 FPROC(sparms)->fs->first_folder[MAXFOLDER-1] = '\0';
2518 else
2519 sparms->text.handles->h.f.context->use &= ~CNTXT_ZOOM;
2521 else
2522 ps_global->mangled_footer++;
2525 break;
2528 /*----------------- Shuffle incoming folder list -----------*/
2529 case MC_SHUFFLE :
2531 CONTEXT_S *cntxt = (sparms->text.handles)
2532 ? sparms->text.handles->h.f.context : NULL;
2534 if(!(cntxt && cntxt->use & CNTXT_INCMNG))
2535 q_status_message(SM_ORDER, 0, 4,
2536 _("May only shuffle Incoming-Folders."));
2537 else if(folder_total(FOLDERS(cntxt)) == 0)
2538 q_status_message(SM_ORDER, 0, 4,
2539 _("No folders to shuffle."));
2540 else if(folder_total(FOLDERS(cntxt)) < 2)
2541 q_status_message(SM_ORDER, 0, 4,
2542 _("Shuffle only makes sense with more than one folder."));
2543 else{
2544 if(FPROC(sparms) && FPROC(sparms)->fs &&
2545 FPROC(sparms)->fs && sparms->text.handles &&
2546 sparms->text.handles->h.f.index >= 0 &&
2547 sparms->text.handles->h.f.index <
2548 folder_total(FOLDERS(cntxt))){
2549 strncpy(FPROC(sparms)->fs->first_folder,
2550 FLDR_NAME(folder_entry(sparms->text.handles->h.f.index,
2551 FOLDERS(cntxt))), MAXFOLDER);
2552 FPROC(sparms)->fs->first_folder[MAXFOLDER-1] = '\0';
2555 rv = shuffle_incoming_folders(cntxt,
2556 sparms->text.handles->h.f.index);
2560 break;
2563 /*-------------- Goto Folder Prompt --------------------*/
2564 case MC_GOTO :
2566 int notrealinbox;
2567 CONTEXT_S *c = (sparms->text.handles)
2568 ? sparms->text.handles->h.f.context
2569 : FPROC(sparms)->fs->context;
2570 char *new_fold = broach_folder(-FOOTER_ROWS(ps_global), 0, &notrealinbox, &c);
2572 if(ps_global && ps_global->ttyo){
2573 blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);
2574 ps_global->mangled_footer = 1;
2577 if(new_fold && do_broach_folder(new_fold, c, NULL, notrealinbox ? 0L : DB_INBOXWOCNTXT) > 0){
2578 ps_global->next_screen = mail_index_screen;
2579 FPROC(sparms)->done = rv = 1;
2581 else
2582 ps_global->mangled_footer = 1;
2584 if((c = ((sparms->text.handles)
2585 ? sparms->text.handles->h.f.context
2586 : FPROC(sparms)->fs->context))->dir->status & CNTXT_NOFIND)
2587 refresh_folder_list(c, FPROC(sparms)->fs->no_dirs, TRUE, FPROC(sparms)->fs->cache_streamp);
2590 break;
2593 /*------------- Print list of folders ---------*/
2594 case MC_PRINTFLDR :
2595 print_folders(FPROC(sparms));
2596 ps_global->mangled_footer++;
2597 break;
2600 /*----- Select the current folder, or toggle checkbox -----*/
2601 case MC_SELCUR :
2602 /*---------- Select set of folders ----------*/
2603 case MC_SELECT :
2604 if(sparms->text.handles
2605 && folder_total(FOLDERS(sparms->text.handles->h.f.context))){
2606 if(cmd == MC_SELCUR){
2607 rv = folder_select_toggle(sparms->text.handles->h.f.context,
2608 sparms->text.handles->h.f.index,
2609 (NEWS_TEST(sparms->text.handles->h.f.context)
2610 && FPROC(sparms)->fs->list_cntxt)
2611 ? ng_scroll_edit : folder_select_update);
2612 if(!rv) ps_global->mangled_screen = 1;
2614 else
2615 switch(folder_select(ps_global,
2616 sparms->text.handles->h.f.context,
2617 sparms->text.handles->h.f.index)){
2618 case 1 :
2619 rv = 1; /* rebuild screen */
2621 case 0 :
2622 default :
2623 ps_global->mangled_screen++;
2624 break;
2627 if((sparms->text.handles->h.f.context->use & CNTXT_ZOOM)
2628 && !selected_folders(sparms->text.handles->h.f.context)){
2629 sparms->text.handles->h.f.context->use &= ~CNTXT_ZOOM;
2630 rv = 1; /* make sure to redraw */
2633 if(rv){ /* remember where to start */
2634 FOLDER_S *fp;
2636 FPROC(sparms)->fs->context = sparms->text.handles->h.f.context;
2637 if((fp = folder_entry(sparms->text.handles->h.f.index,
2638 FOLDERS(sparms->text.handles->h.f.context))) != NULL){
2639 if(strlen(FLDR_NAME(fp)) < MAXFOLDER - 1){
2640 strncpy(FPROC(sparms)->fs->first_folder, FLDR_NAME(fp), MAXFOLDER);
2641 FPROC(sparms)->fs->first_folder[MAXFOLDER-1] = '\0';
2646 else
2647 q_status_message(SM_ORDER | SM_DING, 0, 4,
2648 _("Empty folder collection. No folder to select!"));
2650 break;
2653 /*---------- Display folders ----------*/
2654 case MC_ZOOM :
2655 if(sparms->text.handles
2656 && folder_total(FOLDERS(sparms->text.handles->h.f.context))){
2657 FOLDER_S *fp;
2658 int n;
2660 if((n = selected_folders(sparms->text.handles->h.f.context)) != 0){
2661 if(sparms->text.handles->h.f.context->use & CNTXT_ZOOM){
2662 sparms->text.handles->h.f.context->use &= ~CNTXT_ZOOM;
2663 q_status_message(SM_ORDER, 0, 3,
2664 _("Folder List Zoom mode is now off"));
2666 else{
2667 q_status_message1(SM_ORDER, 0, 3,
2668 _("In Zoomed list of %s folders. Use \"Z\" to restore regular list"),
2669 int2string(n));
2670 sparms->text.handles->h.f.context->use |= CNTXT_ZOOM;
2673 /* exit scrolltool to rebuild screen */
2674 rv = 1;
2676 /* Set where to start after it's rebuilt */
2677 FPROC(sparms)->fs->context = sparms->text.handles->h.f.context;
2678 FPROC(sparms)->fs->first_folder[0] = '\0';
2679 if((fp = folder_entry(sparms->text.handles->h.f.index,
2680 FOLDERS(sparms->text.handles->h.f.context)))
2681 && !((sparms->text.handles->h.f.context->use & CNTXT_ZOOM)
2682 && !fp->selected)
2683 && strlen(FLDR_NAME(fp)) < MAXFOLDER - 1){
2684 strncpy(FPROC(sparms)->fs->first_folder, FLDR_NAME(fp), MAXFOLDER);
2685 FPROC(sparms)->fs->first_folder[MAXFOLDER-1] = '\0';
2688 else
2689 q_status_message(SM_ORDER, 0, 3,
2690 _("No selected folders to Zoom on"));
2692 else
2693 q_status_message(SM_ORDER, 0, 4, _("No Folders to Zoom on!"));
2695 break;
2697 /*----- Ask user to abandon selection before exiting -----*/
2698 case MC_EXITQUERY :
2699 if(sparms->text.handles
2700 && FOLDERS(sparms->text.handles->h.f.context)){
2701 int i, folder_n;
2702 FOLDER_S *fp;
2704 folder_n = folder_total(FOLDERS(sparms->text.handles->h.f.context));
2705 /* any selected? */
2706 for(i = 0; i < folder_n; i++){
2707 fp = folder_entry(i, FOLDERS(sparms->text.handles->h.f.context));
2708 if(fp->selected)
2709 break;
2712 if(i < folder_n /* some selections have been made */
2713 && want_to(_("Really abandon your selections "),
2714 'y', 'x', NO_HELP, WT_NORM) != 'y'){
2715 break;
2718 rv = 1;
2719 break;
2721 /*------------New Msg command --------------*/
2722 case MC_CHK_RECENT:
2724 * Derived from code provided by
2725 * Rostislav Neplokh neplokh@andrew.cmu.edu and
2726 * Robert Siemborski (rjs3@andrew).
2728 if(sparms->text.handles
2729 && folder_total(FOLDERS(sparms->text.handles->h.f.context))){
2730 FOLDER_S *folder;
2732 folder = folder_entry(sparms->text.handles->h.f.index,
2733 FOLDERS(sparms->text.handles->h.f.context));
2735 if(!folder){
2736 strncpy(tmp_output, _("Invalid Folder Name"), sizeof(tmp_output)-1);
2737 tmp_output[sizeof(tmp_output)-1] = '\0';
2739 else if(folder->isdir && !folder->isfolder){
2740 snprintf(tmp_output, sizeof(tmp_output), _("\"%s\" is a directory"), folder->name);
2742 else{
2743 char mailbox_name[MAXPATH+1];
2744 unsigned long tot, rec;
2745 int we_cancel;
2747 context_apply(mailbox_name,
2748 sparms->text.handles->h.f.context,
2749 folder->name, MAXPATH+1);
2751 we_cancel = busy_cue(NULL, NULL, 1);
2753 if(get_recent_in_folder(mailbox_name, &rec, NULL, &tot, NULL))
2754 snprintf(tmp_output, sizeof(tmp_output),
2755 _("%lu total message%s, %lu of them recent"),
2756 tot, plural(tot), rec);
2757 else
2758 snprintf(tmp_output, sizeof(tmp_output),
2759 _("%s: Trouble checking for recent mail"), folder->name);
2761 if(we_cancel)
2762 cancel_busy_cue(-1);
2765 else{
2766 strncpy(tmp_output, _("No folder to check! Can't get recent info"),
2767 sizeof(tmp_output)-1);
2768 tmp_output[sizeof(tmp_output)-1] = '\0';
2771 q_status_message(SM_ORDER, 0, 3, tmp_output);
2772 break;
2775 /*--------------- Invalid Command --------------*/
2776 default:
2777 q_status_message1(SM_ORDER, 0, 2, "fix this: cmd = %s", comatose(cmd));
2778 break;
2781 return(rv);
2786 folder_lister_clickclick(SCROLL_S *sparms)
2788 if(!FPROC(sparms)->fs->list_cntxt)
2789 return(folder_lister_choice(sparms));
2790 else
2791 return(folder_processor(MC_SELCUR, ps_global->msgmap, sparms));
2795 folder_lister_choice(SCROLL_S *sparms)
2797 int rv = 0, empty = 0;
2798 int index = (sparms->text.handles)
2799 ? sparms->text.handles->h.f.index : 0;
2800 CONTEXT_S *cntxt = (sparms->text.handles)
2801 ? sparms->text.handles->h.f.context : NULL;
2803 if(cntxt){
2805 FPROC(sparms)->fs->context = cntxt;
2807 if(!LUU_YES(cntxt->update)){
2808 cntxt->update = LUU_INIT;
2809 refresh_folder_list(cntxt, FPROC(sparms)->fs->no_dirs, TRUE,
2810 FPROC(sparms)->fs->cache_streamp);
2811 if(!LUU_YES(cntxt->update))
2812 q_status_message(SM_ORDER | SM_DING, 3, 3, _("Collection not updated"));
2813 else
2814 rv = 1;
2816 return rv;
2819 if(index < 0 || (cntxt->dir->status & CNTXT_NOFIND)){
2820 rv = 1; /* leave scrolltool to rebuild screen */
2821 FPROC(sparms)->fs->context = cntxt;
2822 FPROC(sparms)->fs->first_folder[0] = '\0';
2824 else if(folder_total(FOLDERS(cntxt))){
2825 if(folder_lister_select(FPROC(sparms)->fs, cntxt, index,
2826 sparms->text.handles ?
2827 sparms->text.handles->is_dual_do_open : 0)){
2828 rv = 1; /* leave scrolltool to rebuild screen */
2830 else if(FPROC(sparms)->fs->list_cntxt == cntxt){
2831 int n = 0, i, folder_n;
2832 FOLDER_S *fp;
2833 STRLIST_S *sl = NULL, **slp;
2835 /* Scan folder list for selected items */
2836 folder_n = folder_total(FOLDERS(cntxt));
2837 slp = &sl;
2838 for(i = 0; i < folder_n; i++){
2839 fp = folder_entry(i, FOLDERS(cntxt));
2840 if(fp->selected){
2841 n++;
2842 if((*FPROC(sparms)->fs->f.valid)(fp,
2843 FPROC(sparms)->fs)){
2844 *slp = new_strlist(NULL);
2845 (*slp)->name = folder_lister_fullname(
2846 FPROC(sparms)->fs, FLDR_NAME(fp));
2848 slp = &(*slp)->next;
2850 else{
2851 free_strlist(&sl);
2852 break;
2857 if((FPROC(sparms)->rv = sl) != NULL)
2858 FPROC(sparms)->done = rv = 1;
2859 else if(!n)
2860 q_status_message(SM_ORDER, 0, 1, LISTMODE_GRIPE);
2862 else
2863 rv = folder_lister_finish(sparms, cntxt, index);
2865 else
2866 empty++;
2868 else
2869 empty++;
2871 if(empty)
2872 q_status_message(SM_ORDER | SM_DING, 3, 3, _("Empty folder list!"));
2874 return(rv);
2879 folder_lister_finish(SCROLL_S *sparms, CONTEXT_S *cntxt, int index)
2881 FOLDER_S *f = folder_entry(index, FOLDERS(cntxt));
2882 int rv = 0;
2884 if((*FPROC(sparms)->fs->f.valid)(f, FPROC(sparms)->fs)){
2886 * Package up the selected folder names and return...
2888 FPROC(sparms)->fs->context = cntxt;
2889 FPROC(sparms)->rv = new_strlist(NULL);
2890 FPROC(sparms)->rv->name = folder_lister_fullname(FPROC(sparms)->fs,
2891 FLDR_NAME(f));
2892 FPROC(sparms)->done = rv = 1;
2895 return(rv);
2900 * This is so that when you Save and use ^T to go to the folder list, and
2901 * you're in a directory with no folders, you have a way to add a new
2902 * folder there. The add actually gets done by the caller. This is just a
2903 * way to let the user type in the name.
2906 folder_lister_addmanually(SCROLL_S *sparms)
2908 int rc, flags = OE_APPEND_CURRENT, cnt = 0, rv = 0;
2909 char addname[MAXFOLDER+1];
2910 HelpType help;
2911 CONTEXT_S *cntxt = (sparms->text.handles)
2912 ? sparms->text.handles->h.f.context
2913 : FPROC(sparms)->fs->context;
2916 * Get the foldername from the user.
2918 addname[0] = '\0';
2919 help = NO_HELP;
2920 while(1){
2921 rc = optionally_enter(addname, -FOOTER_ROWS(ps_global), 0,
2922 sizeof(addname), _("Name of new folder : "),
2923 NULL, help, &flags);
2924 removing_leading_and_trailing_white_space(addname);
2926 if(rc == 3)
2927 help = (help == NO_HELP) ? h_save_addman : NO_HELP;
2928 else if(rc == 1)
2929 return(rv);
2930 else if(rc == 0){
2931 if(F_OFF(F_ENABLE_DOT_FOLDERS,ps_global) && *addname == '.'){
2932 if(cnt++ <= 0)
2933 q_status_message(SM_ORDER,3,3,
2934 _("Folder name can't begin with dot"));
2935 else
2936 q_status_message1(SM_ORDER,3,3,
2937 _("Config feature \"%s\" enables names beginning with dot"),
2938 pretty_feature_name(feature_list_name(F_ENABLE_DOT_FOLDERS), -1));
2940 display_message(NO_OP_COMMAND);
2941 continue;
2943 else if(!strucmp(addname, ps_global->inbox_name)){
2944 q_status_message1(SM_ORDER, 3, 3,
2945 _("Can't add folder named %s"),
2946 ps_global->inbox_name);
2947 continue;
2950 break;
2954 if(*addname){
2955 FPROC(sparms)->fs->context = cntxt;
2956 FPROC(sparms)->rv = new_strlist(NULL);
2957 FPROC(sparms)->rv->name = folder_lister_fullname(FPROC(sparms)->fs,
2958 addname);
2959 FPROC(sparms)->done = rv = 1;
2962 return(rv);
2966 void
2967 folder_lister_km_manager(SCROLL_S *sparms, int handle_hidden)
2969 FOLDER_S *fp;
2971 /* if we're "in" a sub-directory, offer way out */
2972 if((sparms->text.handles)
2973 ? sparms->text.handles->h.f.context->dir->prev
2974 : FPROC(sparms)->fs->context->dir->prev){
2977 * Leave the command characters alone and just change
2978 * the labels and the bind.cmd for KM_COL_KEY.
2979 * Also, leave KM_MAIN_KEY alone instead of trying to
2980 * turn it off in the else clause when it is redundant.
2982 sparms->keys.menu->keys[KM_COL_KEY].label = N_("ParentDir");
2983 sparms->keys.menu->keys[KM_COL_KEY].bind.cmd = MC_PARENT;
2985 else if((FPROC(sparms)->fs->context->next
2986 || FPROC(sparms)->fs->context->prev)
2987 && !FPROC(sparms)->fs->combined_view){
2988 sparms->keys.menu->keys[KM_COL_KEY].label = N_("ClctnList");
2989 sparms->keys.menu->keys[KM_COL_KEY].bind.cmd = MC_EXIT;
2991 else{
2992 sparms->keys.menu->keys[KM_COL_KEY].label = N_("Main Menu");
2993 sparms->keys.menu->keys[KM_COL_KEY].bind.cmd = MC_MAIN;
2996 if(F_OFF(F_ENABLE_AGG_OPS,ps_global)){
2997 clrbitn(KM_ZOOM_KEY, sparms->keys.bitmap);
2998 clrbitn(KM_SELECT_KEY, sparms->keys.bitmap);
2999 clrbitn(KM_SELCUR_KEY, sparms->keys.bitmap);
3002 if(sparms->text.handles
3003 && (fp = folder_entry(sparms->text.handles->h.f.index,
3004 FOLDERS(sparms->text.handles->h.f.context)))){
3005 if(fp->isdir && !sparms->text.handles->is_dual_do_open){
3006 sparms->keys.menu->keys[KM_SEL_KEY].label = "[" N_("View Dir") "]";
3007 menu_add_binding(sparms->keys.menu, 'v', MC_CHOICE);
3008 menu_add_binding(sparms->keys.menu, ctrl('M'), MC_CHOICE);
3009 menu_add_binding(sparms->keys.menu, ctrl('J'), MC_CHOICE);
3010 setbitn(KM_SEL_KEY, sparms->keys.bitmap);
3011 clrbitn(KM_ALTVIEW_KEY, sparms->keys.bitmap);
3012 clrbitn(KM_EXPORT_KEY, sparms->keys.bitmap);
3013 clrbitn(KM_IMPORT_KEY, sparms->keys.bitmap);
3015 else{
3016 sparms->keys.menu->keys[KM_SEL_KEY].label = "[" N_("View Fldr") "]";
3017 menu_add_binding(sparms->keys.menu, 'v', MC_CHOICE);
3018 menu_add_binding(sparms->keys.menu, ctrl('M'), MC_CHOICE);
3019 menu_add_binding(sparms->keys.menu, ctrl('J'), MC_CHOICE);
3020 setbitn(KM_SEL_KEY, sparms->keys.bitmap);
3021 clrbitn(KM_ALTVIEW_KEY, sparms->keys.bitmap);
3022 setbitn(KM_EXPORT_KEY, sparms->keys.bitmap);
3023 setbitn(KM_IMPORT_KEY, sparms->keys.bitmap);
3026 else if(FPROC(sparms)->fs->combined_view
3027 && sparms->text.handles && sparms->text.handles->h.f.context
3028 && !sparms->text.handles->h.f.context->dir->prev){
3029 sparms->keys.menu->keys[KM_SEL_KEY].label = "[" N_("View Cltn") "]";
3030 menu_add_binding(sparms->keys.menu, 'v', MC_CHOICE);
3031 menu_add_binding(sparms->keys.menu, ctrl('M'), MC_CHOICE);
3032 menu_add_binding(sparms->keys.menu, ctrl('J'), MC_CHOICE);
3033 setbitn(KM_SEL_KEY, sparms->keys.bitmap);
3034 clrbitn(KM_ALTVIEW_KEY, sparms->keys.bitmap);
3035 clrbitn(KM_EXPORT_KEY, sparms->keys.bitmap);
3036 clrbitn(KM_IMPORT_KEY, sparms->keys.bitmap);
3038 else{
3039 clrbitn(KM_SEL_KEY, sparms->keys.bitmap);
3040 clrbitn(KM_ALTVIEW_KEY, sparms->keys.bitmap);
3041 clrbitn(KM_EXPORT_KEY, sparms->keys.bitmap);
3042 clrbitn(KM_IMPORT_KEY, sparms->keys.bitmap);
3045 if((sparms->text.handles &&
3046 sparms->text.handles->h.f.context &&
3047 sparms->text.handles->h.f.context->use & CNTXT_INCMNG) ||
3048 (FPROC(sparms) && FPROC(sparms)->fs &&
3049 FPROC(sparms)->fs->context &&
3050 FPROC(sparms)->fs->context->use & CNTXT_INCMNG))
3051 setbitn(KM_SHUFFLE_KEY, sparms->keys.bitmap);
3052 else
3053 clrbitn(KM_SHUFFLE_KEY, sparms->keys.bitmap);
3055 if(F_ON(F_TAB_CHK_RECENT, ps_global)){
3056 menu_clear_binding(sparms->keys.menu, TAB);
3057 menu_init_binding(sparms->keys.menu, TAB, MC_CHK_RECENT, "Tab",
3058 /* TRANSLATORS: New Messages */
3059 N_("NewMsgs"), KM_RECENT_KEY);
3060 setbitn(KM_RECENT_KEY, sparms->keys.bitmap);
3062 else{
3063 menu_clear_binding(sparms->keys.menu, TAB);
3064 menu_add_binding(sparms->keys.menu, TAB, MC_NEXT_HANDLE);
3065 clrbitn(KM_RECENT_KEY, sparms->keys.bitmap);
3068 /* May have to "undo" what scrolltool "did" */
3069 if(F_ON(F_ARROW_NAV, ps_global)){
3070 if(F_ON(F_RELAXED_ARROW_NAV, ps_global)){
3071 menu_clear_binding(sparms->keys.menu, KEY_LEFT);
3072 menu_add_binding(sparms->keys.menu, KEY_LEFT, MC_PREV_HANDLE);
3073 menu_clear_binding(sparms->keys.menu, KEY_RIGHT);
3074 menu_add_binding(sparms->keys.menu, KEY_RIGHT, MC_NEXT_HANDLE);
3076 else{
3077 menu_clear_binding(sparms->keys.menu, KEY_UP);
3078 menu_add_binding(sparms->keys.menu, KEY_UP, MC_PREV_HANDLE);
3079 menu_clear_binding(sparms->keys.menu, KEY_DOWN);
3080 menu_add_binding(sparms->keys.menu, KEY_DOWN, MC_NEXT_HANDLE);
3086 void
3087 folder_lister_km_sel_manager(SCROLL_S *sparms, int handle_hidden)
3089 FOLDER_S *fp;
3091 /* if we're "in" a sub-directory, offer way out */
3092 if((sparms->text.handles)
3093 ? sparms->text.handles->h.f.context->dir->prev
3094 : FPROC(sparms)->fs->context->dir->prev){
3095 sparms->keys.menu->keys[FC_COL_KEY].name = "<";
3096 /* TRANSLATORS: go to parent directory one level up */
3097 sparms->keys.menu->keys[FC_COL_KEY].label = N_("ParentDir");
3098 sparms->keys.menu->keys[FC_COL_KEY].bind.cmd = MC_PARENT;
3099 sparms->keys.menu->keys[FC_COL_KEY].bind.ch[0] = '<';
3100 sparms->keys.menu->keys[FC_COL_KEY].bind.ch[1] = ',';
3101 if(F_ON(F_ARROW_NAV,ps_global)){
3102 sparms->keys.menu->keys[FC_COL_KEY].bind.nch = 3;
3103 sparms->keys.menu->keys[FC_COL_KEY].bind.ch[2] = KEY_LEFT;
3105 else
3106 sparms->keys.menu->keys[FC_COL_KEY].bind.nch = 2;
3108 /* ExitSelect in position 1 */
3109 setbitn(FC_EXIT_KEY, sparms->keys.bitmap);
3111 else if((FPROC(sparms)->fs->context->next
3112 || FPROC(sparms)->fs->context->prev)
3113 && !FPROC(sparms)->fs->combined_view){
3114 sparms->keys.menu->keys[FC_COL_KEY].name = "<";
3115 /* TRANSLATORS: go to Collection List */
3116 sparms->keys.menu->keys[FC_COL_KEY].label = N_("ClctnList");
3117 sparms->keys.menu->keys[FC_COL_KEY].bind.cmd = MC_COLLECTIONS;
3118 sparms->keys.menu->keys[FC_COL_KEY].bind.ch[0] = '<';
3119 sparms->keys.menu->keys[FC_COL_KEY].bind.ch[1] = ',';
3120 if(F_ON(F_ARROW_NAV,ps_global)){
3121 sparms->keys.menu->keys[FC_COL_KEY].bind.nch = 3;
3122 sparms->keys.menu->keys[FC_COL_KEY].bind.ch[2] = KEY_LEFT;
3124 else
3125 sparms->keys.menu->keys[FC_COL_KEY].bind.nch = 2;
3127 /* ExitSelect in position 1 */
3128 setbitn(FC_EXIT_KEY, sparms->keys.bitmap);
3130 else if(FPROC(sparms)->fs->combined_view){
3132 * This can't be a menu_init_binding() because we don't want
3133 * to remove the ExitSelect command in position FC_EXIT_KEY.
3134 * We just turn it off until we need it again.
3136 sparms->keys.menu->keys[FC_COL_KEY].name = "E";
3137 sparms->keys.menu->keys[FC_COL_KEY].label = N_("ExitSelect");
3138 sparms->keys.menu->keys[FC_COL_KEY].bind.cmd = MC_EXIT;
3139 sparms->keys.menu->keys[FC_COL_KEY].bind.nch = 1;
3140 sparms->keys.menu->keys[FC_COL_KEY].bind.ch[0] = 'e';
3142 /* turn off ExitSelect in position 1, it's in 2 now */
3143 clrbitn(FC_EXIT_KEY, sparms->keys.bitmap);
3146 /* clean up per-entry bindings */
3147 clrbitn(FC_SEL_KEY, sparms->keys.bitmap);
3148 clrbitn(FC_ALTSEL_KEY, sparms->keys.bitmap);
3149 menu_clear_binding(sparms->keys.menu, ctrl('M'));
3150 menu_clear_binding(sparms->keys.menu, ctrl('J'));
3151 menu_clear_binding(sparms->keys.menu, '>');
3152 menu_clear_binding(sparms->keys.menu, '.');
3153 menu_clear_binding(sparms->keys.menu, 's');
3154 if(F_ON(F_ARROW_NAV,ps_global))
3155 menu_clear_binding(sparms->keys.menu, KEY_RIGHT);
3157 /* and then re-assign them as needed */
3158 if(sparms->text.handles
3159 && (fp = folder_entry(sparms->text.handles->h.f.index,
3160 FOLDERS(sparms->text.handles->h.f.context)))){
3161 setbitn(FC_SEL_KEY, sparms->keys.bitmap);
3162 sparms->keys.menu->keys[FC_SEL_KEY].name = ">";
3163 menu_add_binding(sparms->keys.menu, '>', MC_CHOICE);
3164 menu_add_binding(sparms->keys.menu, '.', MC_CHOICE);
3165 if(F_ON(F_ARROW_NAV,ps_global))
3166 menu_add_binding(sparms->keys.menu, KEY_RIGHT, MC_CHOICE);
3167 if(fp->isdir && !sparms->text.handles->is_dual_do_open){
3168 sparms->keys.menu->keys[KM_SEL_KEY].label = "[" N_("View Dir") "]";
3169 menu_add_binding(sparms->keys.menu, 'v', MC_CHOICE);
3170 menu_add_binding(sparms->keys.menu, ctrl('M'), MC_CHOICE);
3171 menu_add_binding(sparms->keys.menu, ctrl('J'), MC_CHOICE);
3172 setbitn(KM_SEL_KEY, sparms->keys.bitmap);
3173 clrbitn(KM_ALTVIEW_KEY, sparms->keys.bitmap);
3174 clrbitn(KM_EXPORT_KEY, sparms->keys.bitmap);
3175 clrbitn(KM_IMPORT_KEY, sparms->keys.bitmap);
3177 else{
3178 sparms->keys.menu->keys[FC_SEL_KEY].name = "S";
3179 sparms->keys.menu->keys[FC_SEL_KEY].label = "[" N_("Select") "]";
3181 menu_add_binding(sparms->keys.menu, 's', MC_CHOICE);
3182 menu_add_binding(sparms->keys.menu, ctrl('M'), MC_CHOICE);
3183 menu_add_binding(sparms->keys.menu, ctrl('J'), MC_CHOICE);
3186 if(F_ON(F_ARROW_NAV,ps_global))
3187 menu_add_binding(sparms->keys.menu, KEY_RIGHT, MC_CHOICE);
3189 else if(FPROC(sparms)->fs->combined_view
3190 && sparms->text.handles && sparms->text.handles->h.f.context
3191 && !sparms->text.handles->h.f.context->dir->prev){
3192 setbitn(FC_SEL_KEY, sparms->keys.bitmap);
3193 sparms->keys.menu->keys[FC_SEL_KEY].name = ">";
3194 sparms->keys.menu->keys[FC_SEL_KEY].label = "[" N_("View Cltn") "]";
3195 menu_add_binding(sparms->keys.menu, '>', MC_CHOICE);
3196 menu_add_binding(sparms->keys.menu, '.', MC_CHOICE);
3197 if(F_ON(F_ARROW_NAV,ps_global))
3198 menu_add_binding(sparms->keys.menu, KEY_RIGHT, MC_CHOICE);
3200 menu_add_binding(sparms->keys.menu, ctrl('M'), MC_CHOICE);
3201 menu_add_binding(sparms->keys.menu, ctrl('J'), MC_CHOICE);
3204 /* May have to "undo" what scrolltool "did" */
3205 if(F_ON(F_ARROW_NAV, ps_global)){
3206 if(F_ON(F_RELAXED_ARROW_NAV, ps_global)){
3207 menu_clear_binding(sparms->keys.menu, KEY_LEFT);
3208 menu_add_binding(sparms->keys.menu, KEY_LEFT, MC_PREV_HANDLE);
3209 menu_clear_binding(sparms->keys.menu, KEY_RIGHT);
3210 menu_add_binding(sparms->keys.menu, KEY_RIGHT, MC_NEXT_HANDLE);
3212 else{
3213 menu_clear_binding(sparms->keys.menu, KEY_UP);
3214 menu_add_binding(sparms->keys.menu, KEY_UP, MC_PREV_HANDLE);
3215 menu_clear_binding(sparms->keys.menu, KEY_DOWN);
3216 menu_add_binding(sparms->keys.menu, KEY_DOWN, MC_NEXT_HANDLE);
3222 void
3223 folder_lister_km_sub_manager(SCROLL_S *sparms, int handle_hidden)
3226 * Folder_processor() also modifies the keymenu.
3228 if(FPROC(sparms)->fs->list_cntxt){
3229 clrbitn(SB_LIST_KEY, sparms->keys.bitmap);
3230 sparms->keys.menu->keys[SB_SEL_KEY].name = "X";
3231 sparms->keys.menu->keys[SB_SEL_KEY].label = "[" N_("Set/Unset") "]";
3232 sparms->keys.menu->keys[SB_SEL_KEY].bind.cmd = MC_SELCUR;
3233 sparms->keys.menu->keys[SB_SEL_KEY].bind.ch[0] = 'x';
3235 else{
3236 clrbitn(SB_SUB_KEY, sparms->keys.bitmap);
3237 sparms->keys.menu->keys[SB_SEL_KEY].name = "S";
3238 sparms->keys.menu->keys[SB_SEL_KEY].label = "[" N_("Subscribe") "]";
3239 sparms->keys.menu->keys[SB_SEL_KEY].bind.cmd = MC_CHOICE;
3240 sparms->keys.menu->keys[SB_SEL_KEY].bind.ch[0] = 's';
3243 /* May have to "undo" what scrolltool "did" */
3244 if(F_ON(F_ARROW_NAV, ps_global)){
3245 if(F_ON(F_RELAXED_ARROW_NAV, ps_global)){
3246 menu_clear_binding(sparms->keys.menu, KEY_LEFT);
3247 menu_add_binding(sparms->keys.menu, KEY_LEFT, MC_PREV_HANDLE);
3248 menu_clear_binding(sparms->keys.menu, KEY_RIGHT);
3249 menu_add_binding(sparms->keys.menu, KEY_RIGHT, MC_NEXT_HANDLE);
3251 else{
3252 menu_clear_binding(sparms->keys.menu, KEY_UP);
3253 menu_add_binding(sparms->keys.menu, KEY_UP, MC_PREV_HANDLE);
3254 menu_clear_binding(sparms->keys.menu, KEY_DOWN);
3255 menu_add_binding(sparms->keys.menu, KEY_DOWN, MC_NEXT_HANDLE);
3263 folder_select(struct pine *ps, CONTEXT_S *context, int cur_index)
3265 int i, j, n, total, old_tot, diff,
3266 q = 0, rv = 0, narrow = 0;
3267 HelpType help = NO_HELP;
3268 ESCKEY_S *sel_opts;
3269 FOLDER_S *f;
3270 static ESCKEY_S self_opts2[] = {
3271 /* TRANSLATORS: keymenu descriptions, select all folders, current folder, select
3272 based on folder properties, or select based on text contents in folders */
3273 {'a', 'a', "A", N_("select All")},
3274 {'c', 'c', "C", N_("select Cur")},
3275 {'p', 'p', "P", N_("Properties")},
3276 {'t', 't', "T", N_("Text")},
3277 {-1, 0, NULL, NULL},
3278 {-1, 0, NULL, NULL}
3280 extern ESCKEY_S sel_opts1[];
3281 extern char *sel_pmt2;
3282 #define N_RECENT 4
3284 f = folder_entry(cur_index, FOLDERS(context));
3286 sel_opts = self_opts2;
3287 if((old_tot = selected_folders(context)) != 0){
3288 sel_opts1[1].label = f->selected ? N_("unselect Cur") : N_("select Cur");
3289 sel_opts += 2; /* disable extra options */
3290 switch(q = radio_buttons(SEL_ALTER_PMT, -FOOTER_ROWS(ps_global),
3291 sel_opts1, 'c', 'x', help, RB_NORM)){
3292 case 'f' : /* flip selection */
3293 n = folder_total(FOLDERS(context));
3294 for(total = i = 0; i < n; i++)
3295 if((f = folder_entry(i, FOLDERS(context))) != NULL)
3296 f->selected = !f->selected;
3298 return(1); /* repaint */
3300 case 'n' : /* narrow selection */
3301 narrow++;
3302 case 'b' : /* broaden selection */
3303 q = 0; /* but don't offer criteria prompt */
3304 if(context->use & CNTXT_INCMNG){
3305 q_status_message1(SM_ORDER | SM_DING, 3, 3,
3306 _("Select \"%s\" not supported in Incoming Folders"),
3307 narrow ? "Narrow" : "Broaden");
3308 return(0);
3311 break;
3313 case 'c' : /* Un/Select Current */
3314 case 'a' : /* Unselect All */
3315 case 'x' : /* cancel */
3316 break;
3318 default :
3319 q_status_message(SM_ORDER | SM_DING, 3, 3,
3320 _("Unsupported Select option"));
3321 return(0);
3325 if(context->use & CNTXT_INCMNG && F_ON(F_ENABLE_INCOMING_CHECKING, ps_global)){
3326 if(F_ON(F_INCOMING_CHECKING_RECENT, ps_global)){
3327 self_opts2[N_RECENT].ch = 'r';
3328 self_opts2[N_RECENT].rval = 'r';
3329 self_opts2[N_RECENT].name = "R";
3330 self_opts2[N_RECENT].label = N_("Recent");
3332 else{
3333 self_opts2[N_RECENT].ch = 'u';
3334 self_opts2[N_RECENT].rval = 'u';
3335 self_opts2[N_RECENT].name = "U";
3336 self_opts2[N_RECENT].label = N_("Unseen");
3339 else{
3340 self_opts2[N_RECENT].ch = -1;
3341 self_opts2[N_RECENT].rval = 0;
3342 self_opts2[N_RECENT].name = NULL;
3343 self_opts2[N_RECENT].label = NULL;
3346 if(!q)
3347 q = radio_buttons(sel_pmt2, -FOOTER_ROWS(ps_global),
3348 sel_opts, 'c', 'x', help, RB_NORM);
3351 * NOTE: See note about MESSAGECACHE "searched" bits above!
3353 switch(q){
3354 case 'x': /* cancel */
3355 cmd_cancelled("Select command");
3356 return(0);
3358 case 'c' : /* toggle current's selected state */
3359 return(folder_select_toggle(context, cur_index, folder_select_update));
3361 case 'a' : /* select/unselect all */
3362 n = folder_total(FOLDERS(context));
3363 for(total = i = 0; i < n; i++)
3364 folder_entry(i, FOLDERS(context))->selected = old_tot == 0;
3366 q_status_message4(SM_ORDER, 0, 2,
3367 "%s%s folder%s %sselected",
3368 old_tot ? "" : "All ",
3369 comatose(old_tot ? old_tot : n),
3370 plural(old_tot ? old_tot : n), old_tot ? "UN" : "");
3371 return(1);
3373 case 't' : /* Text */
3374 if(!folder_select_text(ps, context, narrow))
3375 rv++;
3377 break;
3379 case 'p' : /* Properties */
3380 if(!folder_select_props(ps, context, narrow))
3381 rv++;
3383 break;
3385 case 'r' :
3386 n = folder_total(FOLDERS(context));
3387 for(i = 0; i < n; i++){
3388 f = folder_entry(i, FOLDERS(context));
3389 if(f->unseen_valid && f->new > 0L)
3390 f->selected = 1;
3393 break;
3395 case 'u' :
3396 n = folder_total(FOLDERS(context));
3397 for(i = 0; i < n; i++){
3398 f = folder_entry(i, FOLDERS(context));
3399 if(f->unseen_valid && f->unseen > 0L)
3400 f->selected = 1;
3403 break;
3405 default :
3406 q_status_message(SM_ORDER | SM_DING, 3, 3,
3407 _("Unsupported Select option"));
3408 return(0);
3411 if(rv)
3412 return(0);
3414 /* rectify the scanned vs. selected folders */
3415 n = folder_total(FOLDERS(context));
3416 for(total = i = j = 0; i < n; i++)
3417 if(folder_entry(i, FOLDERS(context))->scanned)
3418 break;
3421 * Any matches at all? If not, the selected set remains the same.
3422 * Note that when Narrowing, only matches in the intersection will
3423 * show up as scanned. We need to reset i to 0 to erase any already
3424 * selected messages which aren't in the intersection.
3426 if(i < n)
3427 for(i = 0; i < n; i++)
3428 if((f = folder_entry(i, FOLDERS(context))) != NULL){
3429 if(narrow){
3430 if(f->selected){
3431 f->selected = f->scanned;
3432 j++;
3435 else if(f->scanned)
3436 f->selected = 1;
3439 if(!(diff = (total = selected_folders(context)) - old_tot)){
3440 if(narrow)
3441 q_status_message4(SM_ORDER, 0, 2,
3442 "%s. %s folder%s remain%s selected.",
3443 j ? _("No change resulted")
3444 : _("No messages in intersection"),
3445 comatose(old_tot), plural(old_tot),
3446 (old_tot == 1L) ? "s" : "");
3447 else if(old_tot && j)
3448 q_status_message(SM_ORDER, 0, 2,
3449 _("No change resulted. Matching folders already selected."));
3450 else
3451 q_status_message1(SM_ORDER | SM_DING, 0, 2,
3452 "Select failed! No %sfolders selected.",
3453 old_tot ? "additional " : "");
3455 else if(old_tot){
3456 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
3457 "Select matched %d folder%s. %s %sfolder%s %sselected.",
3458 (diff > 0) ? diff : old_tot + diff,
3459 plural((diff > 0) ? diff : old_tot + diff),
3460 comatose((diff > 0) ? total : -diff),
3461 (diff > 0) ? "total " : "",
3462 plural((diff > 0) ? total : -diff),
3463 (diff > 0) ? "" : "UN");
3464 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
3465 q_status_message(SM_ORDER, 0, 2, tmp_20k_buf);
3467 else
3468 q_status_message2(SM_ORDER, 0, 2, "Select matched %s folder%s.",
3469 comatose(diff), plural(diff));
3471 return(1);
3478 folder_lister_select(FSTATE_S *fs, CONTEXT_S *context, int index, int is_dual_do_open)
3480 int rv = 0;
3481 FDIR_S *fp;
3482 FOLDER_S *f = folder_entry(index, FOLDERS(context));
3484 /*--- Entering a directory? ---*/
3485 if(f->isdir && !is_dual_do_open){
3486 fp = next_folder_dir(context, f->name, TRUE, fs->cache_streamp);
3488 /* Provide context in new collection header */
3489 fp->desc = folder_lister_desc(context, fp);
3491 /* Insert new directory into list */
3492 free_folder_list(context);
3493 fp->prev = context->dir;
3494 fp->status |= CNTXT_SUBDIR;
3495 context->dir = fp;
3496 q_status_message2(SM_ORDER, 0, 3, "Now in %sdirectory: %s",
3497 folder_total(FOLDERS(context))
3498 ? "" : "EMPTY ", fp->ref);
3499 rv++;
3501 else
3502 rv = folder_lister_parent(fs, context, index, 1);
3504 return(rv);
3509 folder_lister_parent(FSTATE_S *fs, CONTEXT_S *context, int index, int force_parent)
3511 int rv = 0;
3512 FDIR_S *fp;
3514 if(!force_parent && (fp = context->dir->prev)){
3515 char *s, oldir[MAILTMPLEN];
3517 folder_select_preserve(context);
3518 oldir[0] = '\0';
3519 if((s = strrindex(context->dir->ref, context->dir->delim)) != NULL){
3520 char *u, *v;
3521 *s = '\0';
3522 u = strrindex(context->dir->ref, context->dir->delim);
3523 v = strrindex(context->dir->ref, '}');
3524 s = u < v ? v : u;
3525 if(s != NULL){
3526 strncpy(oldir, s+1, sizeof(oldir)-1);
3527 oldir[sizeof(oldir)-1] = '\0';
3531 if(*oldir){
3532 /* remember current directory for hiliting in new list */
3533 fs->context = context;
3534 if(strlen(oldir) < MAXFOLDER - 1){
3535 strncpy(fs->first_folder, oldir, MAXFOLDER);
3536 fs->first_folder[MAXFOLDER-1] = '\0';
3537 fs->first_dir = 1;
3541 free_fdir(&context->dir, 0);
3542 fp->status |= CNTXT_NOFIND;
3544 context->dir = fp;
3546 if(fp->status & CNTXT_SUBDIR)
3547 q_status_message1(SM_ORDER, 0, 3, _("Now in directory: %s"),
3548 strsquish(tmp_20k_buf + 500, SIZEOF_20KBUF-500, fp->ref,
3549 ps_global->ttyo->screen_cols - 22));
3550 else
3551 q_status_message(SM_ORDER, 0, 3,
3552 _("Returned to collection's top directory"));
3554 rv++;
3557 return(rv);
3561 char *
3562 folder_lister_fullname(FSTATE_S *fs, char *name)
3564 if(fs->context->dir->status & CNTXT_SUBDIR){
3565 char tmp[2*MAILTMPLEN+1], tmp2[2*MAILTMPLEN+1], *p;
3567 if(fs->context->dir->ref){
3568 snprintf(tmp, sizeof(tmp), "%.*s%.*s",
3569 MAILTMPLEN, /* MAILTMPLEN == sizeof(tmp)/2 */
3570 ((fs->relative_path || (fs->context->use & CNTXT_SAVEDFLT))
3571 && (p = strstr(fs->context->context, "%s")) && !*(p+2)
3572 && !strncmp(fs->context->dir->ref, fs->context->context,
3573 p - fs->context->context))
3574 ? fs->context->dir->ref + (p - fs->context->context)
3575 : fs->context->dir->ref,
3576 MAILTMPLEN, name);
3577 tmp[sizeof(tmp)-1] = '\0';
3581 * If the applied name is still ambiguous (defined
3582 * that way by the user (i.e., "mail/[]"), then
3583 * we better fix it up...
3585 if(context_isambig(tmp)
3586 && !fs->relative_path
3587 && !(fs->context->use & CNTXT_SAVEDFLT)){
3588 /* if it's in the primary collection, the names relative */
3589 if(fs->context->dir->ref){ /* MAILTMPLEN = sizeof(tmp)/2 */
3590 if(IS_REMOTE(fs->context->context)
3591 && (p = strrindex(fs->context->context, '}'))){
3592 snprintf(tmp2, sizeof(tmp2), "%.*s%.*s",
3593 (int) MIN(p - fs->context->context + 1, sizeof(tmp)/2),
3594 fs->context->context,
3595 MAILTMPLEN, tmp);
3596 tmp2[sizeof(tmp2)-1] = '\0';
3598 else
3599 build_path(tmp2, ps_global->ui.homedir, tmp, sizeof(tmp2));
3601 else
3602 (void) context_apply(tmp2, fs->context, tmp, sizeof(tmp2));
3604 return(cpystr(tmp2));
3607 return(cpystr(tmp));
3610 return(cpystr(name));
3615 * Export a folder from pine space to user's space. It will still be a regular
3616 * mail folder but it will be in the user's home directory or something like
3617 * that.
3619 void
3620 folder_export(SCROLL_S *sparms)
3622 FOLDER_S *f;
3623 MAILSTREAM *stream, *ourstream = NULL;
3624 char expanded_file[MAILTMPLEN], *p,
3625 tmp[MAILTMPLEN], *fname, *fullname = NULL,
3626 filename[MAXPATH+1], full_filename[MAXPATH+1],
3627 deefault[MAXPATH+1];
3628 int open_inbox = 0, we_cancel = 0, width,
3629 index = (sparms && sparms->text.handles)
3630 ? sparms->text.handles->h.f.index : 0;
3631 CONTEXT_S *savecntxt,
3632 *cntxt = (sparms && sparms->text.handles)
3633 ? sparms->text.handles->h.f.context : NULL;
3634 static HISTORY_S *history = NULL;
3636 dprint((4, "\n - folder export -\n"));
3638 if(cntxt){
3639 if(folder_total(FOLDERS(cntxt))){
3640 f = folder_entry(index, FOLDERS(cntxt));
3641 if((*FPROC(sparms)->fs->f.valid)(f, FPROC(sparms)->fs)){
3642 savecntxt = FPROC(sparms)->fs->context; /* necessary? */
3643 FPROC(sparms)->fs->context = cntxt;
3644 strncpy(deefault, FLDR_NAME(f), sizeof(deefault)-1);
3645 deefault[sizeof(deefault)-1] = '\0';
3646 fname = folder_lister_fullname(FPROC(sparms)->fs, FLDR_NAME(f));
3647 FPROC(sparms)->fs->context = savecntxt;
3650 * We have to allow for INBOX and nicknames in
3651 * the incoming collection. Mimic what happens in
3652 * do_broach_folder.
3654 strncpy(expanded_file, fname, sizeof(expanded_file));
3655 expanded_file[sizeof(expanded_file)-1] = '\0';
3657 if(strucmp(fname, ps_global->inbox_name) == 0
3658 || strucmp(fname, ps_global->VAR_INBOX_PATH) == 0)
3659 open_inbox++;
3661 if(!open_inbox && cntxt && context_isambig(fname)){
3662 if((p=folder_is_nick(fname, FOLDERS(cntxt), 0)) != NULL){
3663 strncpy(expanded_file, p, sizeof(expanded_file));
3664 expanded_file[sizeof(expanded_file)-1] = '\0';
3666 else if ((cntxt->use & CNTXT_INCMNG)
3667 && (folder_index(fname, cntxt, FI_FOLDER) < 0)
3668 && !is_absolute_path(fname)){
3669 q_status_message1(SM_ORDER, 3, 4,
3670 _("Can't find Incoming Folder %s."), fname);
3671 return;
3675 if(open_inbox)
3676 fullname = ps_global->VAR_INBOX_PATH;
3677 else{
3679 * We don't want to interpret this name in the context
3680 * of the reference string, that was already done
3681 * above in folder_lister_fullname(), just in the
3682 * regular context. We also don't want to lose the
3683 * reference string because we will still be in the
3684 * subdirectory after this operation completes. So
3685 * temporarily zero out the reference.
3687 FDIR_S *tmpdir;
3689 tmpdir = (cntxt ? cntxt->dir : NULL);
3690 cntxt->dir = NULL;
3691 fullname = context_apply(tmp, cntxt, expanded_file,
3692 sizeof(tmp));
3693 cntxt->dir = tmpdir;
3696 width = MAX(20,
3697 ps_global->ttyo ? ps_global->ttyo->screen_cols : 80);
3698 stream = sp_stream_get(fullname, SP_MATCH | SP_RO_OK);
3699 if(!stream && fullname){
3701 * Just using filename and full_filename as convenient
3702 * temporary buffers here.
3704 snprintf(filename, sizeof(filename), "Opening \"%s\"",
3705 short_str(fullname, full_filename, sizeof(full_filename),
3706 MAX(10, width-17),
3707 MidDots));
3708 filename[sizeof(filename)-1] = '\0';
3709 we_cancel = busy_cue(filename, NULL, 0);
3710 stream = pine_mail_open(NULL, fullname,
3711 OP_READONLY|SP_USEPOOL|SP_TEMPUSE,
3712 NULL);
3713 if(we_cancel)
3714 cancel_busy_cue(0);
3716 ourstream = stream;
3720 * We have a stream for the folder we want to
3721 * export.
3723 if(stream && stream->nmsgs > 0L){
3724 int r = 1;
3725 static ESCKEY_S eopts[] = {
3726 {ctrl('T'), 10, "^T", N_("To Files")},
3727 {-1, 0, NULL, NULL},
3728 {-1, 0, NULL, NULL}};
3730 if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
3731 eopts[r].ch = ctrl('I');
3732 eopts[r].rval = 11;
3733 eopts[r].name = "TAB";
3734 /* TRANSLATORS: Complete is a verb, complete the name of a folder */
3735 eopts[r].label = _("Complete");
3738 eopts[++r].ch = -1;
3740 filename[0] = '\0';
3741 full_filename[0] = '\0';
3743 r = get_export_filename(ps_global, filename, deefault,
3744 full_filename,
3745 sizeof(filename)-20, fname, NULL,
3746 eopts, NULL,
3747 -FOOTER_ROWS(ps_global),
3748 GE_IS_EXPORT | GE_NO_APPEND, &history);
3749 if(r < 0){
3750 switch(r){
3751 default:
3752 case -1:
3753 cmd_cancelled("Export folder");
3754 break;
3756 case -2:
3757 q_status_message1(SM_ORDER, 0, 2,
3758 _("Can't export to file outside of %s"),
3759 ps_global->VAR_OPER_DIR);
3760 break;
3763 else{
3764 int ok;
3765 char *ff;
3767 ps_global->mm_log_error = 0;
3770 * Do the creation in standard unix format, so it
3771 * is readable by all.
3773 rplstr(full_filename, sizeof(full_filename), 0, "#driver.unix/");
3774 ok = pine_mail_create(NULL, full_filename) != 0L;
3776 * ff points to the original filename, without the
3777 * driver prefix. Only mail_create knows how to
3778 * handle driver prefixes.
3780 ff = full_filename + strlen("#driver.unix/");
3782 if(!ok){
3783 if(!ps_global->mm_log_error)
3784 q_status_message(SM_ORDER | SM_DING, 3, 3,
3785 _("Error creating file"));
3787 else{
3788 long l, snmsgs;
3789 MSGNO_S *tmpmap = NULL;
3791 snmsgs = stream->nmsgs;
3792 mn_init(&tmpmap, snmsgs);
3793 for(l = 1L; l <= snmsgs; l++)
3794 if(l == 1L)
3795 mn_set_cur(tmpmap, l);
3796 else
3797 mn_add_cur(tmpmap, l);
3799 blank_keymenu(ps_global->ttyo->screen_rows-2, 0);
3800 we_cancel = busy_cue(_("Copying folder"), NULL, 0);
3801 l = save(ps_global, stream, NULL, ff, tmpmap, 0);
3802 if(we_cancel)
3803 cancel_busy_cue(0);
3805 mn_give(&tmpmap);
3807 if(l == snmsgs)
3808 q_status_message2(SM_ORDER, 0, 3,
3809 "Folder %s exported to %s",
3810 fname, filename);
3811 else{
3812 q_status_message1(SM_ORDER | SM_DING, 3, 3,
3813 _("Error exporting to %s"),
3814 filename);
3815 dprint((2,
3816 "Error exporting to %s: expected %ld msgs, saved %ld\n",
3817 filename, snmsgs, l));
3822 else if(stream)
3823 q_status_message1(SM_ORDER|SM_DING, 3, 3,
3824 _("No messages in %s to export"), fname);
3825 else
3826 q_status_message(SM_ORDER|SM_DING, 3, 3,
3827 _("Can't open folder for exporting"));
3829 if(fname)
3830 fs_give((void **) &fname);
3832 if(ourstream)
3833 pine_mail_close(ourstream);
3837 else
3838 q_status_message(SM_ORDER | SM_DING, 3, 3, _("Empty folder list!"));
3843 * Import a folder from user's space back to pine space.
3844 * We're just importing a regular mail folder, and saving all the messages
3845 * to another folder. It may seem more magical to the user but it isn't.
3846 * The import folder is a local folder, the new one can be remote or whatever.
3847 * Args sparms
3848 * add_folder -- return new folder name here
3849 * len -- length of add_folder
3851 * Returns 1 if we may have to redraw screen, 0 otherwise
3854 folder_import(SCROLL_S *sparms, char *add_folder, size_t len)
3856 MAILSTREAM *istream = NULL;
3857 int r = 1, rv = 0;
3858 char filename[MAXPATH+1], full_filename[MAXPATH+1];
3859 static HISTORY_S *history = NULL;
3860 static ESCKEY_S eopts[] = {
3861 {ctrl('T'), 10, "^T", N_("To Files")},
3862 {-1, 0, NULL, NULL},
3863 {-1, 0, NULL, NULL}};
3865 if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
3866 eopts[r].ch = ctrl('I');
3867 eopts[r].rval = 11;
3868 eopts[r].name = "TAB";
3869 eopts[r].label = N_("Complete");
3872 eopts[++r].ch = -1;
3874 filename[0] = '\0';
3875 full_filename[0] = '\0';
3877 /* get a folder to import */
3878 r = get_export_filename(ps_global, filename, NULL, full_filename,
3879 sizeof(filename)-20, "messages", "IMPORT",
3880 eopts, NULL,
3881 -FOOTER_ROWS(ps_global), GE_IS_IMPORT, &history);
3882 if(r < 0){
3883 switch(r){
3884 default:
3885 case -1:
3886 cmd_cancelled("Import folder");
3887 break;
3889 case -2:
3890 q_status_message1(SM_ORDER, 0, 2,
3891 _("Can't import file outside of %s"),
3892 ps_global->VAR_OPER_DIR);
3893 break;
3896 else{
3897 ps_global->mm_log_error = 0;
3898 if(full_filename && full_filename[0])
3899 istream = pine_mail_open(NULL, full_filename,
3900 OP_READONLY | SP_TEMPUSE, NULL);
3902 if(istream && istream->nmsgs > 0L){
3903 long l;
3904 int we_cancel = 0;
3905 char newfolder[MAILTMPLEN], nmsgs[32];
3906 MSGNO_S *tmpmap = NULL;
3907 CONTEXT_S *cntxt, *ourcntxt;
3909 cntxt = (sparms && sparms->text.handles)
3910 ? sparms->text.handles->h.f.context : NULL;
3911 ourcntxt = cntxt;
3912 newfolder[0] = '\0';
3913 snprintf(nmsgs, sizeof(nmsgs), "%s msgs ", comatose(istream->nmsgs));
3914 nmsgs[sizeof(nmsgs)-1] = '\0';
3917 * Select a folder to save the messages to.
3919 if(save_prompt(ps_global, &cntxt, newfolder, sizeof(newfolder),
3920 nmsgs, NULL, 0L, NULL, NULL, NULL)){
3922 if((cntxt == ourcntxt) && newfolder[0]){
3923 rv = 1;
3924 strncpy(add_folder, newfolder, len-1);
3925 add_folder[len-1] = '\0';
3926 free_folder_list(cntxt);
3929 mn_init(&tmpmap, istream->nmsgs);
3930 for(l = 1; l <= istream->nmsgs; l++)
3931 if(l == 1L)
3932 mn_set_cur(tmpmap, l);
3933 else
3934 mn_add_cur(tmpmap, l);
3936 blank_keymenu(ps_global->ttyo->screen_rows-2, 0);
3937 we_cancel = busy_cue("Importing messages", NULL, 0);
3938 l = save(ps_global, istream, cntxt, newfolder, tmpmap,
3939 SV_INBOXWOCNTXT);
3940 if(we_cancel)
3941 cancel_busy_cue(0);
3943 mn_give(&tmpmap);
3945 if(l == istream->nmsgs)
3946 q_status_message2(SM_ORDER, 0, 3,
3947 "Folder %s imported to %s",
3948 full_filename, newfolder);
3949 else
3950 q_status_message1(SM_ORDER | SM_DING, 3, 3,
3951 _("Error importing to %s"),
3952 newfolder);
3955 else if(istream)
3956 q_status_message1(SM_ORDER | SM_DING, 3, 3,
3957 _("No messages in file %s"),
3958 full_filename);
3959 else if(!ps_global->mm_log_error)
3960 q_status_message1(SM_ORDER | SM_DING, 3, 3,
3961 _("Can't open file %s for import"), full_filename);
3964 if(istream)
3965 pine_mail_close(istream);
3967 return(rv);
3972 folder_select_toggle(CONTEXT_S *context, int index, int (*func) (CONTEXT_S *, int))
3974 FOLDER_S *f;
3976 if((f = folder_entry(index, FOLDERS(context))) != NULL){
3977 f->selected = !f->selected;
3978 return((*func)(context, index));
3980 return 1;
3985 * Find the next '}' that isn't part of a "${"
3986 * that appear for environment variables. We need
3987 * this for configuration, because we want to edit
3988 * the original pinerc entry, and not the digested
3989 * value.
3991 char *
3992 end_bracket_no_nest(char *str)
3994 char *p;
3996 for(p = str; *p; p++){
3997 if(*p == '$' && *(p+1) == '{'){
3998 while(*p && *p != '}')
3999 p++;
4000 if(!*p)
4001 return NULL;
4003 else if(*p == '}')
4004 return p;
4006 /* if we get here then there was no trailing '}' */
4007 return NULL;
4011 /*----------------------------------------------------------------------
4012 Create a new folder
4014 Args: context -- context we're adding in
4015 which -- which config file to operate on
4016 varnum -- which varnum to operate on
4017 add_folder -- return new folder here
4018 add_folderlen -- length of add_folder
4019 possible_stream -- possible stream for re-use
4020 def -- default value to start out with for add_folder
4021 (for now, only for inbox-path editing)
4023 Result: returns nonzero on successful add, 0 otherwise
4024 ----*/
4026 add_new_folder(CONTEXT_S *context, EditWhich which, int varnum, char *add_folder,
4027 size_t add_folderlen, MAILSTREAM *possible_stream, char *def)
4029 char tmp[MAX(MAXFOLDER,6*MAX_SCREEN_COLS)+1], nickname[32],
4030 *p = NULL, *return_val = NULL, buf[MAILTMPLEN],
4031 buf2[MAILTMPLEN], def_in_prompt[MAILTMPLEN], orig_folder[2*MAXFOLDER+10];
4032 HelpType help;
4033 PINERC_S *prc = NULL;
4034 int i, rc, offset, exists, cnt = 0, isdir = 0;
4035 int maildrop = 0, flags = 0, inbox = 0, require_a_subfolder = 0;
4036 char *maildropfolder = NULL, *maildroplongname = NULL;
4037 char *default_mail_drop_host = NULL,
4038 *default_mail_drop_folder = NULL,
4039 *default_dstn_host = NULL,
4040 *default_dstn_folder = NULL,
4041 *copydef = NULL,
4042 *dstnmbox = NULL;
4043 char mdmbox[MAILTMPLEN], ctmp[MAILTMPLEN];
4044 MAILSTREAM *create_stream = NULL;
4045 FOLDER_S *f;
4046 static ESCKEY_S add_key[] = {{ctrl('X'),12,"^X", NULL},
4047 {-1, 0, NULL, NULL}};
4049 dprint((4, "\n - add_new_folder - \n"));
4051 nickname[0] = orig_folder[0] = add_folder[0] = '\0';
4052 inbox = (varnum == V_INBOX_PATH);
4054 if(inbox || context->use & CNTXT_INCMNG){
4055 char inbox_host[MAXPATH], *beg, *end = NULL;
4056 int readonly = 0;
4057 static ESCKEY_S host_key[4];
4059 if(ps_global->restricted)
4060 readonly = 1;
4061 else{
4062 switch(which){
4063 case Main:
4064 prc = ps_global->prc;
4065 break;
4066 case Post:
4067 prc = ps_global->post_prc;
4068 break;
4069 case None:
4070 break;
4073 readonly = prc ? prc->readonly : 1;
4076 if(prc && prc->quit_to_edit){
4077 quit_to_edit_msg(prc);
4078 return(FALSE);
4081 if(readonly){
4082 q_status_message(SM_ORDER,3,5,
4083 _("Cancelled: config file not editable"));
4084 return(FALSE);
4088 * When adding an Incoming folder we can't supply the inbox host
4089 * as a default, because then the user has no way to type just
4090 * a plain RETURN to mean "no host, I want a local folder".
4091 * So we supply it as a ^X command instead. We could supply it
4092 * as the initial value of the string...
4094 * When editing inbox-path we will supply the default value as an
4095 * initial value of the string which can be edited. They can edit it
4096 * or erase it entirely to mean no host.
4099 if(inbox && def){
4101 copydef = cpystr(def);
4102 (void) removing_double_quotes(copydef);
4104 if(check_for_move_mbox(copydef, mdmbox, sizeof(mdmbox), &dstnmbox)){
4107 * Current inbox is using a mail drop. Get the default
4108 * host value for the maildrop.
4111 if(mdmbox
4112 && (mdmbox[0] == '{'
4113 || (mdmbox[0] == '*' && mdmbox[1] == '{'))
4114 && (end = end_bracket_no_nest(mdmbox+1))
4115 && end-mdmbox < add_folderlen){
4116 *end = '\0';
4117 if(mdmbox[0] == '{')
4118 default_mail_drop_host = cpystr(mdmbox+1);
4119 else
4120 default_mail_drop_host = cpystr(mdmbox+2);
4123 if(!default_mail_drop_host)
4124 default_mail_drop_folder = cpystr(mdmbox);
4125 else if(end && *(end+1))
4126 default_mail_drop_folder = cpystr(end+1);
4128 end = NULL;
4129 if(dstnmbox
4130 && (*dstnmbox == '{'
4131 || (*dstnmbox == '*' && *++dstnmbox == '{'))
4132 && (end = end_bracket_no_nest(dstnmbox+1))
4133 && end-dstnmbox < add_folderlen){
4134 *end = '\0';
4135 default_dstn_host = cpystr(dstnmbox+1);
4138 if(!default_dstn_host)
4139 default_dstn_folder = cpystr(dstnmbox);
4140 else if(end && *(end+1))
4141 default_dstn_folder = cpystr(end+1);
4143 maildrop++;
4145 else{
4146 end = NULL;
4147 dstnmbox = copydef;
4148 if(dstnmbox
4149 && (*dstnmbox == '{'
4150 || (*dstnmbox == '*' && *++dstnmbox == '{'))
4151 && (end = end_bracket_no_nest(dstnmbox+1))
4152 && end-dstnmbox < add_folderlen){
4153 *end = '\0';
4154 default_dstn_host = cpystr(dstnmbox+1);
4157 if(!default_dstn_host)
4158 default_dstn_folder = cpystr(dstnmbox);
4159 else if(end && *(end+1))
4160 default_dstn_folder = cpystr(end+1);
4163 if(copydef)
4164 fs_give((void **) &copydef);
4167 get_folder_name:
4169 i = 0;
4170 host_key[i].ch = 0;
4171 host_key[i].rval = 0;
4172 host_key[i].name = "";
4173 host_key[i++].label = "";
4175 inbox_host[0] = '\0';
4176 if(!inbox && (beg = ps_global->VAR_INBOX_PATH)
4177 && (*beg == '{' || (*beg == '*' && *++beg == '{'))
4178 && (end = strindex(ps_global->VAR_INBOX_PATH, '}'))){
4179 strncpy(inbox_host, beg+1, end - beg);
4180 inbox_host[end - beg - 1] = '\0';
4181 host_key[i].ch = ctrl('X');
4182 host_key[i].rval = 12;
4183 host_key[i].name = "^X";
4184 host_key[i++].label = N_("Use Inbox Host");
4186 else{
4187 host_key[i].ch = 0;
4188 host_key[i].rval = 0;
4189 host_key[i].name = "";
4190 host_key[i++].label = "";
4193 if(!maildrop && !maildropfolder){
4194 host_key[i].ch = ctrl('W');
4195 host_key[i].rval = 13;
4196 host_key[i].name = "^W";
4197 /* TRANSLATORS: a mail drop is a place where mail is copied to so you
4198 can read it. */
4199 host_key[i++].label = N_("Use a Mail Drop");
4201 else if(maildrop){
4202 host_key[i].ch = ctrl('W');
4203 host_key[i].rval = 13;
4204 host_key[i].name = "^W";
4205 host_key[i++].label = N_("Do Not use a Mail Drop");
4208 host_key[i].ch = -1;
4209 host_key[i].rval = 0;
4210 host_key[i].name = NULL;
4211 host_key[i].label = NULL;
4213 if(maildrop)
4214 snprintf(tmp, sizeof(tmp), _("Name of Mail Drop server : "));
4215 else if(maildropfolder)
4216 snprintf(tmp, sizeof(tmp), _("Name of server to contain destination folder : "));
4217 else if(inbox)
4218 snprintf(tmp, sizeof(tmp), _("Name of Inbox server : "));
4219 else
4220 snprintf(tmp, sizeof(tmp), _("Name of server to contain added folder : "));
4222 tmp[sizeof(tmp)-1] = '\0';
4224 help = NO_HELP;
4226 /* set up defaults */
4227 if(inbox && def){
4228 flags = OE_APPEND_CURRENT;
4229 if(maildrop && default_mail_drop_host){
4230 strncpy(add_folder, default_mail_drop_host, add_folderlen);
4231 add_folder[add_folderlen-1] = '\0';
4233 else if(!maildrop && default_dstn_host){
4234 strncpy(add_folder, default_dstn_host, add_folderlen);
4235 add_folder[add_folderlen-1] = '\0';
4237 else
4238 add_folder[0] = '\0';
4240 else{
4241 flags = 0;
4242 add_folder[0] = '\0';
4245 while(1){
4246 rc = optionally_enter(add_folder, -FOOTER_ROWS(ps_global), 0,
4247 add_folderlen, tmp, host_key, help, &flags);
4248 removing_leading_and_trailing_white_space(add_folder);
4251 * User went for the whole enchilada and entered a maildrop
4252 * completely without going through the steps.
4253 * Split it up as if they did and then skip over
4254 * some of the code.
4256 if(check_for_move_mbox(add_folder, mdmbox, sizeof(mdmbox),
4257 &dstnmbox)){
4258 maildrop = 1;
4259 if(maildropfolder)
4260 fs_give((void **) &maildropfolder);
4262 maildropfolder = cpystr(mdmbox);
4264 strncpy(add_folder, dstnmbox, add_folderlen);
4265 add_folder[add_folderlen-1] = '\0';
4266 offset = 0;
4267 goto skip_over_folder_input;
4271 * Now check to see if they entered a whole c-client
4272 * spec that isn't a mail drop.
4274 if(add_folder[0] == '{'
4275 && add_folder[1] != '\0'
4276 && (p = srchstr(add_folder, "}"))
4277 && *(p+1) != '\0'){
4278 offset = p+1 - add_folder;
4279 goto skip_over_folder_input;
4283 * And check to see if they entered "INBOX".
4285 if(!strucmp(add_folder, ps_global->inbox_name)){
4286 offset = 0;
4287 goto skip_over_folder_input;
4290 /* remove surrounding braces */
4291 if(add_folder[0] == '{' && add_folder[1] != '\0'){
4292 char *q;
4294 q = add_folder + strlen(add_folder) - 1;
4295 if(*q == '}'){
4296 *q = '\0';
4297 for(q = add_folder; *q; q++)
4298 *q = *(q+1);
4302 if(rc == 3){
4303 if(maildropfolder && inbox)
4304 helper(h_inbox_add_maildrop_destn,
4305 _("HELP FOR DESTINATION SERVER "), HLPD_SIMPLE);
4306 else if(maildropfolder && !inbox)
4307 helper(h_incoming_add_maildrop_destn,
4308 _("HELP FOR DESTINATION SERVER "), HLPD_SIMPLE);
4309 else if(maildrop && inbox)
4310 helper(h_inbox_add_maildrop, _("HELP FOR MAILDROP NAME "),
4311 HLPD_SIMPLE);
4312 else if(maildrop && !inbox)
4313 helper(h_incoming_add_maildrop, _("HELP FOR MAILDROP NAME "),
4314 HLPD_SIMPLE);
4315 else if(inbox)
4316 helper(h_incoming_add_inbox, _("HELP FOR INBOX SERVER "),
4317 HLPD_SIMPLE);
4318 else
4319 helper(h_incoming_add_folder_host, _("HELP FOR SERVER NAME "),
4320 HLPD_SIMPLE);
4322 ps_global->mangled_screen = 1;
4324 else if(rc == 12){
4325 strncpy(add_folder, inbox_host, add_folderlen);
4326 flags |= OE_APPEND_CURRENT;
4328 else if(rc == 13){
4329 if(maildrop){
4330 maildrop = 0;
4331 if(maildropfolder)
4332 fs_give((void **) &maildropfolder);
4334 else{
4335 maildrop++;
4336 if(inbox && def){
4337 default_mail_drop_host = default_dstn_host;
4338 default_dstn_host = NULL;
4339 default_mail_drop_folder = default_dstn_folder;
4340 default_dstn_folder = NULL;
4344 goto get_folder_name;
4346 else if(rc == 1){
4347 q_status_message(SM_ORDER,0,2,
4348 inbox ? _("INBOX change cancelled")
4349 : _("Addition of new folder cancelled"));
4350 if(maildropfolder)
4351 fs_give((void **) &maildropfolder);
4353 if(inbox && def){
4354 if(default_mail_drop_host)
4355 fs_give((void **) &default_mail_drop_host);
4357 if(default_mail_drop_folder)
4358 fs_give((void **) &default_mail_drop_folder);
4360 if(default_dstn_host)
4361 fs_give((void **) &default_dstn_host);
4363 if(default_dstn_folder)
4364 fs_give((void **) &default_dstn_folder);
4367 return(FALSE);
4369 else if(rc == 0)
4370 break;
4374 /* set up default folder, if any */
4375 def_in_prompt[0] = '\0';
4376 if(inbox && def){
4377 if(maildrop && default_mail_drop_folder){
4378 strncpy(def_in_prompt, default_mail_drop_folder,
4379 sizeof(def_in_prompt));
4380 def_in_prompt[sizeof(def_in_prompt)-1] = '\0';
4382 else if(!maildrop && default_dstn_folder){
4383 strncpy(def_in_prompt, default_dstn_folder,
4384 sizeof(def_in_prompt));
4385 def_in_prompt[sizeof(def_in_prompt)-1] = '\0';
4389 if((offset = strlen(add_folder)) != 0){ /* must be host for incoming */
4390 int i;
4391 if(maildrop)
4392 snprintf(tmp, sizeof(tmp),
4393 "Maildrop folder on \"%s\" to copy mail from%s%s%s : ",
4394 short_str(add_folder, buf, sizeof(buf), 15, EndDots),
4395 def_in_prompt[0] ? " [" : "",
4396 short_str(def_in_prompt, buf2, sizeof(buf2), 20, MidDots),
4397 def_in_prompt[0] ? "]" : "");
4398 else if(maildropfolder)
4399 snprintf(tmp, sizeof(tmp),
4400 "Folder on \"%s\" to copy mail to%s%s%s : ",
4401 short_str(add_folder, buf, sizeof(buf), 20, EndDots),
4402 def_in_prompt[0] ? " [" : "",
4403 short_str(def_in_prompt, buf2, sizeof(buf2), 20, MidDots),
4404 def_in_prompt[0] ? "]" : "");
4405 else if(inbox)
4406 snprintf(tmp, sizeof(tmp),
4407 "Folder on \"%s\" to use for INBOX%s%s%s : ",
4408 short_str(add_folder, buf, sizeof(buf), 20, EndDots),
4409 def_in_prompt[0] ? " [" : "",
4410 short_str(def_in_prompt, buf2, sizeof(buf2), 20, MidDots),
4411 def_in_prompt[0] ? "]" : "");
4412 else
4413 snprintf(tmp, sizeof(tmp),
4414 "Folder on \"%s\" to add%s%s%s : ",
4415 short_str(add_folder, buf, sizeof(buf), 25, EndDots),
4416 def_in_prompt[0] ? " [" : "",
4417 short_str(def_in_prompt, buf2, sizeof(buf2), 20, MidDots),
4418 def_in_prompt[0] ? "]" : "");
4420 tmp[sizeof(tmp)-1] = '\0';
4422 for(i = offset;i >= 0; i--)
4423 add_folder[i+1] = add_folder[i];
4425 add_folder[0] = '{';
4426 add_folder[++offset] = '}';
4427 add_folder[++offset] = '\0'; /* +2, total */
4429 else{
4430 if(maildrop)
4431 snprintf(tmp, sizeof(tmp),
4432 "Folder to copy mail from%s%s%s : ",
4433 def_in_prompt[0] ? " [" : "",
4434 short_str(def_in_prompt, buf2, sizeof(buf2), 20, MidDots),
4435 def_in_prompt[0] ? "]" : "");
4436 else if(maildropfolder)
4437 snprintf(tmp, sizeof(tmp),
4438 "Folder to copy mail to%s%s%s : ",
4439 def_in_prompt[0] ? " [" : "",
4440 short_str(def_in_prompt, buf2, sizeof(buf2), 20, MidDots),
4441 def_in_prompt[0] ? "]" : "");
4442 else if(inbox)
4443 snprintf(tmp, sizeof(tmp),
4444 "Folder name to use for INBOX%s%s%s : ",
4445 def_in_prompt[0] ? " [" : "",
4446 short_str(def_in_prompt, buf2, sizeof(buf2), 20, MidDots),
4447 def_in_prompt[0] ? "]" : "");
4448 else
4449 snprintf(tmp, sizeof(tmp),
4450 "Folder name to add%s%s%s : ",
4451 def_in_prompt[0] ? " [" : "",
4452 short_str(def_in_prompt, buf2, sizeof(buf2), 20, MidDots),
4453 def_in_prompt[0] ? "]" : "");
4455 tmp[sizeof(tmp)-1] = '\0';
4458 help = NO_HELP;
4459 while(1){
4461 p = NULL;
4462 if(isdir){
4463 add_key[0].label = N_("Create Folder");
4464 if(tmp[0] == 'F')
4465 rplstr(tmp, sizeof(tmp), 6, N_("Directory"));
4467 else{
4468 add_key[0].label = N_("Create Directory");
4469 if(tmp[0] == 'D')
4470 rplstr(tmp, sizeof(tmp), 9, N_("Folder"));
4473 flags = OE_APPEND_CURRENT;
4474 rc = optionally_enter(&add_folder[offset], -FOOTER_ROWS(ps_global), 0,
4475 add_folderlen - offset, tmp,
4476 ((context->dir->delim) && !maildrop)
4477 ? add_key : NULL,
4478 help, &flags);
4479 /* use default */
4480 if(rc == 0 && def_in_prompt[0] && !add_folder[offset]){
4481 strncpy(&add_folder[offset], def_in_prompt, add_folderlen-offset);
4482 add_folder[add_folderlen-1] = '\0';
4485 removing_leading_and_trailing_white_space(&add_folder[offset]);
4487 /* for non-local folders, transform UTF-8 to MUTF7 */
4488 if(offset > 0 && add_folder[0] == '{'){
4489 unsigned char *mutf7 = utf8_to_mutf7((unsigned char *)&add_folder[offset]);
4490 if(mutf7 != NULL){
4491 strncpy(orig_folder, &add_folder[offset], 2*MAXFOLDER+10);
4492 strncpy(&add_folder[offset], (char *) mutf7, add_folderlen-offset);
4493 add_folder[add_folderlen-1] = '\0';
4494 fs_give((void **)&mutf7);
4498 if(rc == 0 && !(inbox || context->use & CNTXT_INCMNG)
4499 && check_for_move_mbox(add_folder, NULL, 0L, NULL)){
4500 q_status_message(SM_ORDER, 6, 6,
4501 _("#move folders may only be the INBOX or in the Incoming Collection"));
4502 display_message(NO_OP_COMMAND);
4503 continue;
4506 if(rc == 0 && add_folder[offset]){
4507 if(F_OFF(F_ENABLE_DOT_FOLDERS,ps_global)
4508 && add_folder[offset] == '.'){
4509 if(cnt++ <= 0)
4510 q_status_message(SM_ORDER,3,3,
4511 _("Folder name can't begin with dot"));
4512 else
4513 q_status_message1(SM_ORDER,3,3,
4514 _("Config feature \"%s\" enables names beginning with dot"),
4515 pretty_feature_name(feature_list_name(F_ENABLE_DOT_FOLDERS), -1));
4517 display_message(NO_OP_COMMAND);
4518 continue;
4521 /* if last char is delim, blast from new folder */
4522 for(p = &add_folder[offset]; *p && *(p + 1); p++)
4523 ; /* find last char in folder */
4525 if(isdir){
4526 if(*p && *p != context->dir->delim){
4527 *++p = context->dir->delim;
4528 *(p+1) = '\0';
4531 if(F_ON(F_QUELL_EMPTY_DIRS, ps_global))
4532 require_a_subfolder++;
4534 else if(*p == context->dir->delim){
4535 q_status_message(SM_ORDER|SM_DING, 3, 3,
4536 _("Can't have trailing directory delimiters!"));
4537 display_message('X');
4538 continue;
4541 break;
4544 if(rc == 12){
4545 isdir = !isdir; /* toggle directory creation */
4547 else if(rc == 3){
4548 helper(h_incoming_add_folder_name, _("HELP FOR FOLDER NAME "),
4549 HLPD_SIMPLE);
4551 else if(rc == 1 || add_folder[0] == '\0') {
4552 q_status_message(SM_ORDER,0,2,
4553 inbox ? _("INBOX change cancelled")
4554 : _("Addition of new folder cancelled"));
4555 if(maildropfolder)
4556 fs_give((void **) &maildropfolder);
4558 if(inbox && def){
4559 if(default_mail_drop_host)
4560 fs_give((void **) &default_mail_drop_host);
4562 if(default_mail_drop_folder)
4563 fs_give((void **) &default_mail_drop_folder);
4565 if(default_dstn_host)
4566 fs_give((void **) &default_dstn_host);
4568 if(default_dstn_folder)
4569 fs_give((void **) &default_dstn_folder);
4572 return(FALSE);
4576 if(maildrop && !maildropfolder){
4577 maildropfolder = cpystr(add_folder);
4578 maildrop = 0;
4579 goto get_folder_name;
4582 skip_over_folder_input:
4584 if(require_a_subfolder){
4585 /* add subfolder name to directory name */
4586 offset = strlen(add_folder);
4587 tmp[0] = '\0';
4589 if(offset > 0){ /* it had better be */
4590 char save_delim;
4592 save_delim = add_folder[offset-1];
4593 add_folder[offset-1] = '\0';
4595 snprintf(tmp, sizeof(tmp),
4596 "Name of subfolder to add in \"%s\" : ",
4597 short_str(add_folder, buf, sizeof(buf), 15, FrontDots));
4599 tmp[sizeof(tmp)-1] = '\0';
4600 add_folder[offset-1] = save_delim;
4603 while(1){
4604 flags = OE_APPEND_CURRENT;
4605 rc = optionally_enter(&add_folder[offset], -FOOTER_ROWS(ps_global), 0,
4606 add_folderlen - offset, tmp,
4607 NULL, NO_HELP, &flags);
4609 removing_leading_and_trailing_white_space(&add_folder[offset]);
4611 /* for non-local folders, transform UTF-8 to MUTF7 */
4612 if(offset > 0 && add_folder[0] == '{'){
4613 unsigned char *mutf7 = utf8_to_mutf7((unsigned char *)&add_folder[offset]);
4614 if(mutf7 != NULL){
4615 strncpy(orig_folder, &add_folder[offset], 2*MAXFOLDER+10);
4616 strncpy(&add_folder[offset], (char *) mutf7, add_folderlen-offset);
4617 add_folder[add_folderlen-1] = '\0';
4618 fs_give((void **)&mutf7);
4622 /* use default */
4623 if(rc == 0 && !add_folder[offset]){
4624 q_status_message(SM_ORDER, 4, 4,
4625 _("A subfolder name is required, there is no default subfolder name"));
4626 continue;
4629 if(rc == 0 && add_folder[offset]){
4630 break;
4633 if(rc == 3){
4634 helper(h_emptydir_subfolder_name, _("HELP FOR SUBFOLDER NAME "),
4635 HLPD_SIMPLE);
4637 else if(rc == 1 || add_folder[0] == '\0') {
4638 q_status_message(SM_ORDER,0,2, _("Addition of new folder cancelled"));
4639 return(FALSE);
4643 /* the directory is implicit now */
4644 isdir = 0;
4647 if(context == ps_global->context_list
4648 && !(context->dir && context->dir->ref)
4649 && !strucmp(ps_global->inbox_name, add_folder)){
4650 q_status_message1(SM_ORDER,3,3,
4651 _("Cannot add folder %s in current context"),
4652 add_folder);
4653 return(FALSE);
4656 create_stream = sp_stream_get(context_apply(ctmp, context, add_folder,
4657 sizeof(ctmp)),
4658 SP_SAME);
4660 if(!create_stream && possible_stream)
4661 create_stream = context_same_stream(context, add_folder, possible_stream);
4663 help = NO_HELP;
4664 if(!inbox && context->use & CNTXT_INCMNG){
4665 snprintf(tmp, sizeof(tmp), _("Nickname for folder \"%s\" : "), orig_folder[0] ? orig_folder : &add_folder[offset]);
4666 tmp[sizeof(tmp)-1] = '\0';
4667 while(1){
4668 int flags = OE_APPEND_CURRENT;
4670 rc = optionally_enter(nickname, -FOOTER_ROWS(ps_global), 0,
4671 sizeof(nickname), tmp, NULL, help, &flags);
4672 removing_leading_and_trailing_white_space(nickname);
4673 if(rc == 0){
4674 if(strucmp(ps_global->inbox_name, nickname))
4675 break;
4676 else{
4677 q_status_message1(SM_ORDER | SM_DING, 3, 3,
4678 _("Nickname cannot be \"%s\""), nickname);
4682 if(rc == 3){
4683 help = help == NO_HELP
4684 ? h_incoming_add_folder_nickname : NO_HELP;
4686 else if(rc == 1 || (rc != 3 && !*nickname)){
4687 q_status_message(SM_ORDER,0,2,
4688 inbox ? _("INBOX change cancelled")
4689 : _("Addition of new folder cancelled"));
4690 if(maildropfolder)
4691 fs_give((void **) &maildropfolder);
4693 if(inbox && def){
4694 if(default_mail_drop_host)
4695 fs_give((void **) &default_mail_drop_host);
4697 if(default_mail_drop_folder)
4698 fs_give((void **) &default_mail_drop_folder);
4700 if(default_dstn_host)
4701 fs_give((void **) &default_dstn_host);
4703 if(default_dstn_folder)
4704 fs_give((void **) &default_dstn_folder);
4707 return(FALSE);
4712 * Already exist? First, make sure this name won't collide with
4713 * anything else in the list. Next, quickly test to see if it
4714 * the actual mailbox exists so we know any errors from
4715 * context_create() are really bad...
4717 for(offset = 0; offset < folder_total(FOLDERS(context)); offset++){
4718 f = folder_entry(offset, FOLDERS(context));
4719 if(!strucmp(FLDR_NAME(f), nickname[0] ? nickname : add_folder)){
4720 q_status_message1(SM_ORDER | SM_DING, 0, 3,
4721 _("Incoming folder \"%s\" already exists"),
4722 nickname[0] ? nickname : add_folder);
4723 if(maildropfolder)
4724 fs_give((void **) &maildropfolder);
4726 if(inbox && def){
4727 if(default_mail_drop_host)
4728 fs_give((void **) &default_mail_drop_host);
4730 if(default_mail_drop_folder)
4731 fs_give((void **) &default_mail_drop_folder);
4733 if(default_dstn_host)
4734 fs_give((void **) &default_dstn_host);
4736 if(default_dstn_folder)
4737 fs_give((void **) &default_dstn_folder);
4740 return(FALSE);
4744 ps_global->c_client_error[0] = '\0';
4745 exists = folder_exists(context, add_folder);
4746 if(exists == FEX_ERROR){
4747 if(ps_global->c_client_error[0] != '\0')
4748 q_status_message1(SM_ORDER, 3, 3, "%s",
4749 ps_global->c_client_error);
4750 else
4751 q_status_message1(SM_ORDER, 3, 3, _("Error checking for %s"), add_folder);
4754 else if(!inbox)
4755 exists = FEX_NOENT;
4756 else{
4757 exists = FEX_ISFILE;
4759 * If inbox is a maildropfolder, try to create the destination
4760 * folder. But it shouldn't cause a fatal error.
4762 if(maildropfolder && (folder_exists(NULL, add_folder) == FEX_NOENT))
4763 context_create(NULL, NULL, add_folder);
4766 if(exists == FEX_ERROR
4767 || (exists == FEX_NOENT
4768 && !context_create(context, create_stream, add_folder)
4769 && !((context->use & CNTXT_INCMNG)
4770 && !context_isambig(add_folder)))){
4771 if(maildropfolder)
4772 fs_give((void **) &maildropfolder);
4774 if(inbox && def){
4775 if(default_mail_drop_host)
4776 fs_give((void **) &default_mail_drop_host);
4778 if(default_mail_drop_folder)
4779 fs_give((void **) &default_mail_drop_folder);
4781 if(default_dstn_host)
4782 fs_give((void **) &default_dstn_host);
4784 if(default_dstn_folder)
4785 fs_give((void **) &default_dstn_folder);
4788 return(FALSE); /* c-client should've reported error */
4791 if(isdir && p) /* whack off trailing delim */
4792 *p = '\0';
4794 if(inbox || context->use & CNTXT_INCMNG){
4795 char **apval = NULL;
4796 char ***alval = NULL;
4798 if(inbox){
4799 apval = APVAL(&ps_global->vars[varnum], which);
4800 if(apval && *apval)
4801 fs_give((void **) apval);
4803 else{
4804 alval = ALVAL(&ps_global->vars[varnum], which);
4805 if(!*alval){
4806 offset = 0;
4807 *alval = (char **) fs_get(2*sizeof(char *));
4809 else{
4810 for(offset=0; (*alval)[offset]; offset++)
4813 fs_resize((void **) alval, (offset + 2) * sizeof(char *));
4818 * If we're using a Mail Drop we have to assemble the correct
4819 * c-client string to do that. Mail drop syntax looks like
4821 * #move <DELIM> <FromMailbox> <DELIM> <ToMailbox>
4823 * DELIM is any character which does not appear in either of
4824 * the mailbox names.
4826 * And then the nickname is still in front of that mess.
4828 if(maildropfolder){
4829 char *delims = " +-_:!|ABCDEFGHIJKLMNOPQRSTUVWXYZ";
4830 char *c;
4831 size_t len;
4833 len = 5 + 2 + strlen(maildropfolder) + strlen(add_folder);
4834 maildroplongname = (char *) fs_get((len+1) * sizeof(char));
4836 for(c = delims; *c; c++){
4837 if(!strindex(maildropfolder, *c) &&
4838 !strindex(add_folder, *c))
4839 break;
4842 if(*c){
4843 snprintf(maildroplongname, len+1, "#move%c%s%c%s",
4844 *c, maildropfolder, *c, add_folder);
4845 if(strlen(maildroplongname) < add_folderlen){
4846 strncpy(add_folder, maildroplongname, add_folderlen);
4847 add_folder[add_folderlen-1] = '\0';
4850 else{
4851 q_status_message2(SM_ORDER,0,2,
4852 "Can't find delimiter for \"#move %s %s\"",
4853 maildropfolder, add_folder);
4854 dprint((2,
4855 "Can't find delimiter for \"#move %s %s\"",
4856 maildropfolder ? maildropfolder : "?",
4857 add_folder ? add_folder : "?"));
4859 if(maildropfolder)
4860 fs_give((void **) &maildropfolder);
4862 if(inbox && def){
4863 if(default_mail_drop_host)
4864 fs_give((void **) &default_mail_drop_host);
4866 if(default_mail_drop_folder)
4867 fs_give((void **) &default_mail_drop_folder);
4869 if(default_dstn_host)
4870 fs_give((void **) &default_dstn_host);
4872 if(default_dstn_folder)
4873 fs_give((void **) &default_dstn_folder);
4876 return(FALSE);
4879 if(maildroplongname)
4880 fs_give((void **) &maildroplongname);
4883 if(inbox)
4884 *apval = cpystr(add_folder);
4885 else{
4886 (*alval)[offset] = put_pair(nickname, add_folder);
4887 (*alval)[offset+1] = NULL;
4890 set_current_val(&ps_global->vars[varnum], TRUE, FALSE);
4891 if(prc)
4892 prc->outstanding_pinerc_changes = 1;
4894 if(context->use & CNTXT_INCMNG){
4895 if(!inbox && add_folder && add_folder[0] && alval && *alval && (*alval)[offset]){
4897 * Instead of re-initing we try to insert the
4898 * added folder so that we preserve the last_unseen_update
4899 * information.
4901 f = new_folder(add_folder, line_hash((*alval)[offset]));
4902 f->isfolder = 1;
4903 if(nickname && nickname[0]){
4904 f->nickname = cpystr(nickname);
4905 f->name_len = strlen(f->nickname);
4908 if(F_ON(F_ENABLE_INCOMING_CHECKING, ps_global)
4909 && !ps_global->VAR_INCCHECKLIST)
4910 f->last_unseen_update = LUU_INIT;
4911 else
4912 f->last_unseen_update = LUU_NEVERCHK;
4914 folder_insert(folder_total(FOLDERS(context)), f, FOLDERS(context));
4916 else
4917 /* re-init to make sure we got it right */
4918 reinit_incoming_folder_list(ps_global, context);
4921 if(nickname[0]){
4922 strncpy(add_folder, nickname, add_folderlen-1); /* known by new name */
4923 add_folder[add_folderlen-1] = '\0';
4926 if(!inbox)
4927 q_status_message1(SM_ORDER, 0, 3, "Folder \"%s\" created",
4928 orig_folder[0] ? orig_folder : add_folder);
4929 return_val = add_folder;
4931 else if(context_isambig(add_folder)){
4932 free_folder_list(context);
4933 q_status_message2(SM_ORDER, 0, 3, "%s \"%s\" created",
4934 isdir ? "Directory" : "Folder", orig_folder[0] ? orig_folder : add_folder);
4935 return_val = add_folder;
4937 else
4938 q_status_message1(SM_ORDER, 0, 3,
4939 "Folder \"%s\" created outside current collection",
4940 orig_folder[0] ? orig_folder : add_folder);
4942 if(maildropfolder)
4943 fs_give((void **) &maildropfolder);
4945 if(inbox && def){
4946 if(default_mail_drop_host)
4947 fs_give((void **) &default_mail_drop_host);
4949 if(default_mail_drop_folder)
4950 fs_give((void **) &default_mail_drop_folder);
4952 if(default_dstn_host)
4953 fs_give((void **) &default_dstn_host);
4955 if(default_dstn_folder)
4956 fs_give((void **) &default_dstn_folder);
4959 return(return_val != NULL);
4963 /*----------------------------------------------------------------------
4964 Subscribe to a news group
4966 Args: folder -- last folder added
4967 cntxt -- The context the subscription is for
4969 Result: returns the name of the folder subscribed too
4972 This builds a complete context for the entire list of possible news groups.
4973 It also build a context to find the newly created news groups as
4974 determined by data kept in .pinerc. When the find of these new groups is
4975 done the subscribed context is searched and the items marked as new.
4976 A list of new board is never actually created.
4978 ----*/
4980 group_subscription(char *folder, size_t len, CONTEXT_S *cntxt)
4982 STRLIST_S *folders = NULL;
4983 int rc = 0, last_rc, i, n, flags,
4984 last_find_partial = 0, we_cancel = 0;
4985 CONTEXT_S subscribe_cntxt;
4986 HelpType help;
4987 ESCKEY_S subscribe_keys[3];
4989 subscribe_keys[i = 0].ch = ctrl('T');
4990 subscribe_keys[i].rval = 12;
4991 subscribe_keys[i].name = "^T";
4992 subscribe_keys[i++].label = N_("To All Grps");
4994 if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
4995 subscribe_keys[i].ch = ctrl('I');
4996 subscribe_keys[i].rval = 11;
4997 subscribe_keys[i].name = "TAB";
4998 subscribe_keys[i++].label = N_("Complete");
5001 subscribe_keys[i].ch = -1;
5003 /*---- Build a context to find all news groups -----*/
5005 subscribe_cntxt = *cntxt;
5006 subscribe_cntxt.use |= CNTXT_FINDALL;
5007 subscribe_cntxt.use &= ~(CNTXT_PSEUDO | CNTXT_ZOOM | CNTXT_PRESRV);
5008 subscribe_cntxt.next = NULL;
5009 subscribe_cntxt.prev = NULL;
5010 subscribe_cntxt.dir = new_fdir(cntxt->dir->ref,
5011 cntxt->dir->view.internal, '*');
5012 FOLDERS(&subscribe_cntxt) = init_folder_entries();
5014 * Prompt for group name.
5016 folder[0] = '\0';
5017 help = NO_HELP;
5018 while(1){
5019 flags = OE_APPEND_CURRENT;
5020 last_rc = rc;
5021 rc = optionally_enter(folder, -FOOTER_ROWS(ps_global), 0, len,
5022 SUBSCRIBE_PMT, subscribe_keys, help, &flags);
5023 removing_trailing_white_space(folder);
5024 removing_leading_white_space(folder);
5025 if((rc == 0 && folder[0]) || rc == 11 || rc == 12){
5026 we_cancel = busy_cue(_("Fetching newsgroup list"), NULL, 1);
5028 if(last_find_partial){
5029 /* clean up any previous find results */
5030 free_folder_list(&subscribe_cntxt);
5031 last_find_partial = 0;
5034 if(rc == 11){ /* Tab completion! */
5035 if(folder_complete_internal(&subscribe_cntxt,
5036 folder, len, &n, FC_FORCE_LIST)){
5037 continue;
5039 else{
5040 if(!(n && last_rc == 11 && !(flags & OE_USER_MODIFIED))){
5041 Writechar(BELL, 0);
5042 continue;
5047 if(rc == 12){ /* list the whole enchilada */
5048 build_folder_list(NULL, &subscribe_cntxt, NULL, NULL,BFL_NONE);
5050 else if(strlen(folder)){
5051 char tmp[MAILTMPLEN];
5053 snprintf(tmp, sizeof(tmp), "%s%.*s*", (rc == 11) ? "" : "*",
5054 MAILTMPLEN-3, folder);
5055 tmp[sizeof(tmp)-1] = '\0';
5056 build_folder_list(NULL, &subscribe_cntxt, tmp, NULL, BFL_NONE);
5057 subscribe_cntxt.dir->status &= ~(CNTXT_PARTFIND|CNTXT_NOFIND);
5059 else{
5060 q_status_message(SM_ORDER, 0, 2,
5061 _("No group substring to match! Use ^T to list all news groups."));
5062 continue;
5066 * If we did a partial find on matches, then we faked a full
5067 * find which will cause this to just return.
5069 if((i = folder_total(FOLDERS(&subscribe_cntxt))) != 0){
5070 char *f = NULL;
5073 * fake that we've found everything there is to find...
5075 subscribe_cntxt.use &= ~(CNTXT_NOFIND|CNTXT_PARTFIND);
5076 last_find_partial = 1;
5078 if(i == 1){
5079 f = folder_entry(0, FOLDERS(&subscribe_cntxt))->name;
5080 if(!strcmp(f, folder)){
5081 rc = 1; /* success! */
5082 break;
5084 else{ /* else complete the group */
5085 strncpy(folder, f, len-1);
5086 folder[len-1] = '\0';
5087 continue;
5090 else if(!(flags & OE_USER_MODIFIED)){
5092 * See if there wasn't an exact match in the lot.
5094 while(i-- > 0){
5095 f = folder_entry(i,FOLDERS(&subscribe_cntxt))->name;
5096 if(!strcmp(f, folder))
5097 break;
5098 else
5099 f = NULL;
5102 /* if so, then the user picked it from the list the
5103 * last time and didn't change it at the prompt.
5104 * Must mean they're accepting it...
5106 if(f){
5107 rc = 1; /* success! */
5108 break;
5112 else{
5113 if(rc == 12)
5114 q_status_message(SM_ORDER | SM_DING, 3, 3,
5115 _("No groups to select from!"));
5116 else
5117 q_status_message1(SM_ORDER, 3, 3,
5118 _("News group \"%s\" didn't match any existing groups"),
5119 folder);
5120 free_folder_list(&subscribe_cntxt);
5122 continue;
5125 /*----- Mark groups that are currently subscribed too ------*/
5126 /* but first make sure they're found */
5127 build_folder_list(NULL, cntxt, "*", NULL, BFL_LSUB);
5128 for(i = 0 ; i < folder_total(FOLDERS(&subscribe_cntxt)); i++) {
5129 FOLDER_S *f = folder_entry(i, FOLDERS(&subscribe_cntxt));
5131 f->subscribed = search_folder_list(FOLDERS(cntxt),
5132 f->name) != 0;
5135 if(we_cancel)
5136 cancel_busy_cue(-1);
5138 /*----- Call the folder lister to do all the work -----*/
5139 folders = folders_for_subscribe(ps_global,
5140 &subscribe_cntxt, folder);
5142 if(folders){
5143 /* Multiple newsgroups OR Auto-select */
5144 if(folders->next || F_ON(F_SELECT_WO_CONFIRM,ps_global))
5145 break;
5147 strncpy(folder, (char *) folders->name, len-1);
5148 folder[len-1] = '\0';
5149 free_strlist(&folders);
5152 else if(rc == 3){
5153 help = help == NO_HELP ? h_news_subscribe : NO_HELP;
5155 else if(rc == 1 || folder[0] == '\0'){
5156 rc = -1;
5157 break;
5161 if(rc < 0){
5162 folder[0] = '\0'; /* make sure not to return partials */
5163 if(rc == -1)
5164 q_status_message(SM_ORDER, 0, 3, _("Subscribe cancelled"));
5166 else{
5167 MAILSTREAM *sub_stream;
5168 int sclose = 0, errors = 0;
5170 if(folders){ /*------ Actually do the subscription -----*/
5171 STRLIST_S *flp;
5172 int n = 0;
5174 /* subscribe one at a time */
5175 folder[0] = '\0';
5177 * Open stream before subscribing so c-client knows what newsrc
5178 * to use, along with other side-effects.
5180 if((sub_stream = mail_cmd_stream(&subscribe_cntxt, &sclose)) != NULL){
5181 for(flp = folders; flp; flp = flp->next){
5182 (void) context_apply(tmp_20k_buf, &subscribe_cntxt,
5183 (char *) flp->name, SIZEOF_20KBUF);
5184 if(mail_subscribe(sub_stream, tmp_20k_buf) == 0L){
5186 * This message may not make it to the screen,
5187 * because a c-client message about the failure
5188 * will be there. Probably best not to string
5189 * together a whole bunch of errors if there is
5190 * something wrong.
5192 q_status_message1(errors ?SM_INFO : SM_ORDER,
5193 errors ? 0 : 3, 3,
5194 _("Error subscribing to \"%s\""),
5195 (char *) flp->name);
5196 errors++;
5198 else{
5199 n++;
5200 if(!folder[0]){
5201 strncpy(folder, (char *) flp->name, len-1);
5202 folder[len-1] = '\0';
5205 /*---- Update the screen display data structures -----*/
5206 if(ALL_FOUND(cntxt)){
5207 if(cntxt->use & CNTXT_PSEUDO){
5208 folder_delete(0, FOLDERS(cntxt));
5209 cntxt->use &= ~CNTXT_PSEUDO;
5212 folder_insert(-1, new_folder((char *) flp->name, 0),
5213 FOLDERS(cntxt));
5217 if(sclose)
5218 pine_mail_close(sub_stream);
5220 else
5221 errors++;
5223 if(n == 0)
5224 q_status_message(SM_ORDER | SM_DING, 3, 5,
5225 _("Subscriptions failed, subscribed to no new groups"));
5226 else
5227 q_status_message3(SM_ORDER | (errors ? SM_DING : 0),
5228 errors ? 3 : 0,3,
5229 "Subscribed to %s new groups%s%s",
5230 comatose((long)n),
5231 errors ? ", failed on " : "",
5232 errors ? comatose((long)errors) : "");
5234 free_strlist(&folders);
5236 else{
5237 if((sub_stream = mail_cmd_stream(&subscribe_cntxt, &sclose)) != NULL){
5238 (void) context_apply(tmp_20k_buf, &subscribe_cntxt, folder,
5239 SIZEOF_20KBUF);
5240 if(mail_subscribe(sub_stream, tmp_20k_buf) == 0L){
5241 q_status_message1(SM_ORDER | SM_DING, 3, 3,
5242 _("Error subscribing to \"%s\""), folder);
5243 errors++;
5245 else if(ALL_FOUND(cntxt)){
5246 /*---- Update the screen display data structures -----*/
5247 if(cntxt->use & CNTXT_PSEUDO){
5248 folder_delete(0, FOLDERS(cntxt));
5249 cntxt->use &= ~CNTXT_PSEUDO;
5252 folder_insert(-1, new_folder(folder, 0), FOLDERS(cntxt));
5254 if(sclose)
5255 pine_mail_close(sub_stream);
5257 else{
5258 q_status_message1(SM_ORDER | SM_DING, 3, 3,
5259 _("Error subscribing to \"%s\""), folder);
5260 errors++;
5264 if(!errors && folder[0])
5265 q_status_message1(SM_ORDER, 0, 3, _("Subscribed to \"%s\""), folder);
5268 free_fdir(&subscribe_cntxt.dir, 1);
5269 return(folder[0]);
5273 /*----------------------------------------------------------------------
5274 Rename folder
5276 Args: new_name -- buffer to contain new file name
5277 len -- length of new_name buffer
5278 index -- index of folder in folder list to rename
5279 context -- collection of folders making up folder list
5280 possible_stream -- may be able to use this stream
5282 Result: returns the new name of the folder, or NULL if nothing happened.
5284 When either the sent-mail or saved-message folders are renamed, immediately
5285 create a new one in their place so they always exist. The main loop above also
5286 detects this and makes the rename look like an add of the sent-mail or
5287 saved-messages folder. (This behavior may not be optimal, but it keeps things
5288 consistent.
5290 ----*/
5292 rename_folder(CONTEXT_S *context, int index, char *new_name, size_t len, MAILSTREAM *possible_stream)
5294 char *folder, prompt[64], *name_p = NULL;
5295 HelpType help;
5296 FOLDER_S *new_f;
5297 PINERC_S *prc = NULL;
5298 int rc, ren_cur, cnt = 0, isdir = 0, readonly = 0;
5299 EditWhich ew;
5300 MAILSTREAM *strm;
5302 dprint((4, "\n - rename folder -\n"));
5304 if(NEWS_TEST(context)){
5305 q_status_message(SM_ORDER | SM_DING, 3, 3,
5306 _("Can't rename news groups!"));
5307 return(0);
5309 else if(!folder_total(FOLDERS(context))){
5310 q_status_message(SM_ORDER | SM_DING, 0, 4,
5311 _("Empty folder collection. No folder to rename!"));
5312 return(0);
5314 else if((new_f = folder_entry(index, FOLDERS(context)))
5315 && context == ps_global->context_list
5316 && !(context->dir && context->dir->ref)
5317 && !strucmp(FLDR_NAME(new_f), ps_global->inbox_name)) {
5318 q_status_message1(SM_ORDER | SM_DING, 3, 4,
5319 _("Can't change special folder name \"%s\""),
5320 ps_global->inbox_name);
5321 return(0);
5324 ew = config_containing_inc_fldr(new_f);
5325 if(ps_global->restricted)
5326 readonly = 1;
5327 else{
5328 switch(ew){
5329 case Main:
5330 prc = ps_global->prc;
5331 break;
5332 case Post:
5333 prc = ps_global->post_prc;
5334 break;
5335 case None:
5336 break;
5339 readonly = prc ? prc->readonly : 1;
5342 if(prc && prc->quit_to_edit && (context->use & CNTXT_INCMNG)){
5343 quit_to_edit_msg(prc);
5344 return(0);
5347 if(readonly && (context->use & CNTXT_INCMNG)){
5348 q_status_message(SM_ORDER,3,5,
5349 _("Rename cancelled: folder not in editable config file"));
5350 return(0);
5353 if(context->use & CNTXT_INCMNG){
5354 if(!(folder = new_f->nickname))
5355 folder = ""; /* blank nickname */
5357 else
5358 folder = FLDR_NAME(new_f);
5360 ren_cur = strcmp(folder, ps_global->cur_folder) == 0;
5362 snprintf(prompt, sizeof(prompt), "%s %s to : ", _("Rename"),
5363 (context->use & CNTXT_INCMNG)
5364 ? _("nickname")
5365 : (isdir = new_f->isdir)
5366 ? _("directory") : _("folder"));
5367 prompt[sizeof(prompt)-1] = '\0';
5368 help = NO_HELP;
5369 strncpy(new_name, folder, len-1);
5370 new_name[len-1] = '\0';
5371 while(1) {
5372 int flags = OE_APPEND_CURRENT;
5374 rc = optionally_enter(new_name, -FOOTER_ROWS(ps_global), 0,
5375 len, prompt, NULL, help, &flags);
5376 if(rc == 3) {
5377 help = help == NO_HELP ? h_oe_foldrename : NO_HELP;
5378 continue;
5381 removing_leading_and_trailing_white_space(new_name);
5383 if(rc == 0 && (*new_name || (context->use & CNTXT_INCMNG))) {
5384 /* verify characters */
5385 if(F_OFF(F_ENABLE_DOT_FOLDERS,ps_global) && *new_name == '.'){
5386 if(cnt++ <= 0)
5387 q_status_message(SM_ORDER,3,3,
5388 _("Folder name can't begin with dot"));
5389 else
5390 q_status_message1(SM_ORDER,3,3,
5391 _("Config feature \"s\" enables names beginning with dot"),
5392 pretty_feature_name(feature_list_name(F_ENABLE_DOT_FOLDERS), -1));
5394 display_message(NO_OP_COMMAND);
5395 continue;
5398 if(folder_index(new_name, context, FI_ANY|FI_RENAME) >= 0){
5399 q_status_message1(SM_ORDER, 3, 3,
5400 _("Folder \"%s\" already exists"),
5401 pretty_fn(new_name));
5402 display_message(NO_OP_COMMAND);
5403 continue;
5405 else if(context == ps_global->context_list
5406 && !(context->dir && context->dir->ref)
5407 && !strucmp(new_name, ps_global->inbox_name)){
5408 if(context->use & CNTXT_INCMNG)
5409 q_status_message1(SM_ORDER, 3, 3, _("Can't rename incoming folder to %s"),
5410 new_name);
5411 else
5412 q_status_message1(SM_ORDER, 3, 3, _("Can't rename folder to %s"),
5413 new_name);
5415 display_message(NO_OP_COMMAND);
5416 continue;
5420 if(rc != 4) /* redraw */
5421 break; /* no redraw */
5425 if(rc != 1 && isdir){ /* add trailing delim? */
5426 for(name_p = new_name; *name_p && *(name_p+1) ; name_p++)
5429 if(*name_p == context->dir->delim) /* lop off delim */
5430 *name_p = '\0';
5433 if(rc == 1
5434 || !(*new_name || (context->use & CNTXT_INCMNG))
5435 || !strcmp(new_name, folder)){
5436 q_status_message(SM_ORDER, 0, 2, _("Folder rename cancelled"));
5437 return(0);
5440 if(context->use & CNTXT_INCMNG){
5441 char **new_list, **lp, ***alval;
5442 int i;
5444 alval = ALVAL(&ps_global->vars[V_INCOMING_FOLDERS], ew);
5445 for(i = 0; (*alval)[i]; i++)
5448 new_list = (char **) fs_get((i + 1) * sizeof(char *));
5450 for(lp = new_list, i = 0; (*alval)[i]; i++){
5451 /* figure out if this is the one we're renaming */
5452 expand_variables(tmp_20k_buf, SIZEOF_20KBUF, (*alval)[i], 0);
5453 if(new_f->varhash == line_hash(tmp_20k_buf)){
5454 char *folder_string = NULL, *nickname = NULL;
5456 if(new_f->nickname)
5457 fs_give((void **) &new_f->nickname);
5459 if(*new_name)
5460 new_f->nickname = cpystr(new_name);
5463 * Parse folder line for nickname and folder name.
5464 * No nickname on line is OK.
5466 get_pair(tmp_20k_buf, &nickname, &folder_string, 0, 0);
5468 if(nickname)
5469 fs_give((void **)&nickname);
5471 *lp = put_pair(new_name, folder_string);
5473 new_f->varhash = line_hash(*lp++);
5475 else
5476 *lp++ = cpystr((*alval)[i]);
5479 *lp = NULL;
5481 set_variable_list(V_INCOMING_FOLDERS, new_list, TRUE, ew);
5482 free_list_array(&new_list);
5484 return(1);
5487 /* Can't rename open streams */
5488 if((strm = context_already_open_stream(context, folder, AOS_NONE))
5489 || (ren_cur && (strm=ps_global->mail_stream))){
5490 if(possible_stream == strm)
5491 possible_stream = NULL;
5493 pine_mail_actually_close(strm);
5496 if(possible_stream
5497 && !context_same_stream(context, new_name, possible_stream))
5498 possible_stream = NULL;
5500 if((rc = context_rename(context, possible_stream, folder, new_name)) != 0){
5501 if(name_p && *name_p == context->dir->delim)
5502 *name_p = '\0'; /* blat trailing delim */
5504 /* insert new name? */
5505 if(!strindex(new_name, context->dir->delim)){
5506 new_f = new_folder(new_name, 0);
5507 new_f->isdir = isdir;
5508 folder_insert(-1, new_f, FOLDERS(context));
5511 if(strcmp(ps_global->VAR_DEFAULT_FCC, folder) == 0
5512 || strcmp(ps_global->VAR_DEFAULT_SAVE_FOLDER, folder) == 0) {
5513 /* renaming sent-mail or saved-messages */
5514 if(context_create(context, NULL, folder)){
5515 q_status_message3(SM_ORDER,0,3,
5516 "Folder \"%s\" renamed to \"%s\". New \"%s\" created",
5517 folder, new_name,
5518 pretty_fn(
5519 (strcmp(ps_global->VAR_DEFAULT_SAVE_FOLDER,
5520 folder) == 0)
5521 ? ps_global->VAR_DEFAULT_SAVE_FOLDER
5522 : ps_global->VAR_DEFAULT_FCC));
5525 else{
5526 q_status_message1(SM_ORDER | SM_DING, 3, 4,
5527 "Error creating new \"%s\"", folder);
5529 dprint((1, "Error creating \"%s\" in %s context\n",
5530 folder ? folder : "?",
5531 context->context ? context->context : "?"));
5534 else
5535 q_status_message2(SM_ORDER, 0, 3,
5536 "Folder \"%s\" renamed to \"%s\"",
5537 pretty_fn(folder), pretty_fn(new_name));
5539 free_folder_list(context);
5542 if(ren_cur) {
5543 if(ps_global && ps_global->ttyo){
5544 blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);
5545 ps_global->mangled_footer = 1;
5548 /* No reopen the folder we just had open */
5549 do_broach_folder(new_name, context, NULL, 0L);
5552 return(rc);
5556 /*----------------------------------------------------------------------
5557 Confirm and delete a folder
5559 Args: fs -- folder screen state
5561 Result: return 0 if not delete, 1 if deleted.
5562 ----*/
5564 delete_folder(CONTEXT_S *context, int index, char *next_folder, size_t len, MAILSTREAM **possible_streamp)
5566 char *folder, ques_buf[MAX_SCREEN_COLS+1], *target = NULL,
5567 *fnamep, buf[1000];
5568 MAILSTREAM *del_stream = NULL, *sub_stream, *strm = NULL;
5569 FOLDER_S *fp;
5570 EditWhich ew;
5571 PINERC_S *prc = NULL;
5572 int ret, unsub_opened = 0, close_opened = 0, blast_folder = 1,
5573 readonly;
5575 if(!context){
5576 cmd_cancelled("Missing context in Delete");
5577 return(0);
5580 if(NEWS_TEST(context)){
5581 folder = folder_entry(index, FOLDERS(context))->name;
5582 snprintf(ques_buf, sizeof(ques_buf), _("Really unsubscribe from \"%s\""), folder);
5583 ques_buf[sizeof(ques_buf)-1] = '\0';
5585 ret = want_to(ques_buf, 'n', 'x', NO_HELP, WT_NORM);
5586 switch(ret) {
5587 /* ^C */
5588 case 'x':
5589 Writechar(BELL, 0);
5590 /* fall through */
5591 case 'n':
5592 return(0);
5595 dprint((2, "deleting folder \"%s\" in context \"%s\"\n",
5596 folder ? folder : "?",
5597 context->context ? context->context : "?"));
5599 if((sub_stream = mail_cmd_stream(context, &unsub_opened)) != NULL){
5600 (void) context_apply(tmp_20k_buf, context, folder, SIZEOF_20KBUF);
5601 if(!mail_unsubscribe(sub_stream, tmp_20k_buf)){
5602 q_status_message1(SM_ORDER | SM_DING, 3, 3,
5603 _("Error unsubscribing from \"%s\""),folder);
5604 if(unsub_opened)
5605 pine_mail_close(sub_stream);
5606 return(0);
5608 if(unsub_opened)
5609 pine_mail_close(sub_stream);
5613 * Fix up the displayed list
5615 folder_delete(index, FOLDERS(context));
5616 return(1);
5619 fp = folder_entry(index, FOLDERS(context));
5620 if(!fp){
5621 cmd_cancelled("Can't find folder to Delete!");
5622 return(0);
5625 if(!((context->use & CNTXT_INCMNG) && fp->name
5626 && check_for_move_mbox(fp->name, NULL, 0, &target))){
5627 target = NULL;
5630 folder = FLDR_NAME(fp);
5631 dprint((4, "=== delete_folder(%s) ===\n",
5632 folder ? folder : "?"));
5634 ew = config_containing_inc_fldr(fp);
5635 if(ps_global->restricted)
5636 readonly = 1;
5637 else{
5638 switch(ew){
5639 case Main:
5640 prc = ps_global->prc;
5641 break;
5642 case Post:
5643 prc = ps_global->post_prc;
5644 break;
5645 case None:
5646 break;
5649 readonly = prc ? prc->readonly : 1;
5652 if(prc && prc->quit_to_edit && (context->use & CNTXT_INCMNG)){
5653 quit_to_edit_msg(prc);
5654 return(0);
5657 if(context == ps_global->context_list
5658 && !(context->dir && context->dir->ref)
5659 && strucmp(folder, ps_global->inbox_name) == 0){
5660 q_status_message1(SM_ORDER | SM_DING, 3, 4,
5661 _("Can't delete special folder \"%s\"."),
5662 ps_global->inbox_name);
5663 return(0);
5665 else if(readonly && (context->use & CNTXT_INCMNG)){
5666 q_status_message(SM_ORDER,3,5,
5667 _("Deletion cancelled: folder not in editable config file"));
5668 return(0);
5670 else if((fp->name
5671 && (strm=context_already_open_stream(context,fp->name,AOS_NONE)))
5673 (target
5674 && (strm=context_already_open_stream(NULL,target,AOS_NONE)))){
5675 if(strm == ps_global->mail_stream)
5676 close_opened++;
5678 else if(fp->isdir || fp->isdual){ /* NO DELETE if directory isn't EMPTY */
5679 FDIR_S *fdirp = next_folder_dir(context,folder,TRUE,possible_streamp);
5681 if(fp->haschildren)
5682 ret = 1;
5683 else if(fp->hasnochildren)
5684 ret = 0;
5685 else{
5686 ret = folder_total(fdirp->folders) > 0;
5687 free_fdir(&fdirp, 1);
5690 if(ret){
5691 q_status_message2(SM_ORDER | SM_DING, 3, 4,
5692 _("Can't delete non-empty directory \"%s\"%s."),
5693 folder, (fp->isfolder && fp->isdual) ? " (or folder of same name)" : "");
5694 return(0);
5698 * Folder by the same name exist? If so, server's probably going
5699 * to delete it as well. Punt?
5701 if(fp->isdual
5702 && (ret = want_to(DIR_FOLD_PMT,'n','x',NO_HELP,WT_NORM)) != 'y'){
5703 q_status_message(SM_ORDER,0,3, (ret == 'x') ? _("Delete cancelled")
5704 : _("No folder deleted"));
5705 return(0);
5709 if(context->use & CNTXT_INCMNG){
5710 static ESCKEY_S delf_opts[] = {
5711 {'n', 'n', "N", N_("Nickname only")},
5712 {'b', 'b', "B", N_("Both Folder and Nickname")},
5713 {-1, 0, NULL, NULL}
5715 #define DELF_PROMPT _("DELETE only Nickname or Both nickname and folder? ")
5717 switch(radio_buttons(DELF_PROMPT, -FOOTER_ROWS(ps_global),
5718 delf_opts,'n','x',NO_HELP,RB_NORM)){
5719 case 'n' :
5720 blast_folder = 0;
5721 break;
5723 case 'x' :
5724 cmd_cancelled("Delete");
5725 return(0);
5727 default :
5728 break;
5731 else{
5732 unsigned char *fname = folder_name_decoded((unsigned char *)folder);
5733 snprintf(ques_buf, sizeof(ques_buf), "DELETE \"%s\"%s", fname ? (char *) fname : folder,
5734 close_opened ? " (the currently open folder)" :
5735 (fp->isdir && !(fp->isdual || fp->isfolder
5736 || (folder_index(folder, context, FI_FOLDER) >= 0)))
5737 ? " (a directory)" : "");
5738 if(fname) fs_give((void **)&fname);
5739 ques_buf[sizeof(ques_buf)-1] = '\0';
5741 if((ret = want_to(ques_buf, 'n', 'x', NO_HELP, WT_NORM)) != 'y'){
5742 q_status_message(SM_ORDER,0,3, (ret == 'x') ? _("Delete cancelled")
5743 : _("Nothing deleted"));
5744 return(0);
5748 if(blast_folder){
5749 dprint((2,"deleting \"%s\" (%s) in context \"%s\"\n",
5750 fp->name ? fp->name : "?",
5751 fp->nickname ? fp->nickname : "",
5752 context->context ? context->context : "?"));
5753 if(strm){
5755 * Close it, NULL the pointer, and let do_broach_folder fixup
5756 * the rest...
5758 pine_mail_actually_close(strm);
5759 if(close_opened){
5760 if(ps_global && ps_global->ttyo){
5761 blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);
5762 ps_global->mangled_footer = 1;
5765 ps_global->mangled_header = 1;
5766 do_broach_folder(ps_global->inbox_name,
5767 ps_global->context_list, NULL, DB_INBOXWOCNTXT);
5772 * Use fp->name since "folder" may be a nickname...
5774 if(possible_streamp && *possible_streamp
5775 && context_same_stream(context, fp->name, *possible_streamp))
5776 del_stream = *possible_streamp;
5778 fnamep = fp->name;
5780 if(!context_delete(context, del_stream, fnamep)){
5782 * BUG: what if sent-mail or saved-messages????
5784 q_status_message1(SM_ORDER,3,3,"Delete of \"%s\" Failed!", folder);
5785 return(0);
5789 snprintf(buf, sizeof(buf), "%s\"%s\" deleted.",
5790 !blast_folder ? "Nickname " :
5791 fp->isdual ? "Folder/Directory " :
5792 (fp->isdir && fp->isfolder) ? "Folder " :
5793 fp->isdir ? "Directory " :
5794 "Folder ",
5795 folder);
5796 buf[sizeof(buf)-1] = '\0';
5798 q_status_message(SM_ORDER, 0, 3, buf);
5800 if(context->use & CNTXT_INCMNG){
5801 char **new_list, **lp, ***alval;
5802 int i;
5804 alval = ALVAL(&ps_global->vars[V_INCOMING_FOLDERS], ew);
5805 for(i = 0; (*alval)[i]; i++)
5809 * Make it one too big in case we don't find the match for
5810 * some unknown reason.
5812 new_list = (char **) fs_get((i + 1) * sizeof(char *));
5815 * Copy while figuring out which one to skip.
5817 for(lp = new_list, i = 0; (*alval)[i]; i++){
5818 expand_variables(tmp_20k_buf, SIZEOF_20KBUF, (*alval)[i], 0);
5819 if(fp->varhash != line_hash(tmp_20k_buf))
5820 *lp++ = cpystr((*alval)[i]);
5823 *lp = NULL;
5825 set_variable_list(V_INCOMING_FOLDERS, new_list, TRUE, ew);
5826 free_list_array(&new_list);
5830 * Fix up the displayed list.
5832 folder_delete(index, FOLDERS(context));
5835 * Take a guess at what should get hilited next.
5837 if(index < (ret = folder_total(FOLDERS(context)))
5838 || ((index = ret - 1) >= 0)){
5839 if((fp = folder_entry(index, FOLDERS(context)))
5840 && strlen(FLDR_NAME(fp)) < len - 1)
5841 strncpy(next_folder, FLDR_NAME(fp), len-1);
5842 next_folder[len-1] = '\0';
5845 if(!(context->use & CNTXT_INCMNG)){
5847 * Then cause the list to get rebuild 'cause we may've blasted
5848 * some folder that's also a directory or vice versa...
5850 free_folder_list(context);
5853 return(1);
5857 /*----------------------------------------------------------------------
5858 Print the list of folders on paper
5860 Args: list -- The current list of folders
5861 lens -- The list of lengths of the current folders
5862 display -- The current folder display structure
5864 Result: list printed on paper
5866 If the display list was created for 80 columns it is used, otherwise
5867 a new list is created for 80 columns
5869 ----*/
5870 void
5871 print_folders(FPROC_S *fp)
5873 if(open_printer(_("folder list")) == 0){
5874 (void) folder_list_text(ps_global, fp, print_char, NULL, 80);
5876 close_printer();
5882 scan_get_pattern(char *kind, char *pat, int len)
5884 char prompt[256];
5885 int flags;
5887 pat[0] = '\0';
5888 snprintf(prompt, sizeof(prompt), _("String in folder %s to match : "), kind);
5889 prompt[sizeof(prompt)-1] = '\0';
5891 while(1){
5892 flags = OE_APPEND_CURRENT | OE_DISALLOW_HELP;
5893 switch(optionally_enter(pat, -FOOTER_ROWS(ps_global), 0, len,
5894 prompt, NULL, NO_HELP, &flags)){
5896 case 4 :
5897 if(ps_global->redrawer)
5898 (*ps_global->redrawer)();
5900 break;
5902 case 0 :
5903 if(*pat)
5904 return(1);
5906 case 1 :
5907 cmd_cancelled("Select");
5909 default :
5910 return(0);
5917 folder_select_text(struct pine *ps, CONTEXT_S *context, int selected)
5919 char pattern[MAILTMPLEN], type = '\0';
5920 static ESCKEY_S scan_opts[] = {
5921 {'n', 'n', "N", N_("Name Select")},
5922 {'c', 'c', "C", N_("Content Select")},
5923 {-1, 0, NULL, NULL}
5926 if(context->use & CNTXT_INCMNG){
5927 q_status_message(SM_ORDER | SM_DING, 3, 3,
5928 _("Select \"Text\" not supported in Incoming Folders"));
5929 return(0);
5932 switch(radio_buttons(SEL_TEXT_PMT, -FOOTER_ROWS(ps_global),
5933 scan_opts, 'n', 'x', NO_HELP, RB_NORM)){
5934 case 'n' : /* Name search */
5935 if(scan_get_pattern("NAME", pattern, sizeof(pattern)))
5936 type = 'n';
5938 break;
5940 case 'c' : /* content search */
5941 if(scan_get_pattern("CONTENTS", pattern, sizeof(pattern)))
5942 type = 'c';
5944 break;
5946 case 'x' :
5947 default :
5948 break;
5951 if(type){
5952 int rv;
5953 char tmp[MAILTMPLEN];
5954 SCANARG_S args;
5956 memset(&args, 0, sizeof(SCANARG_S));
5957 args.pattern = pattern;
5958 args.type = type;
5959 args.context = context;
5961 if(type == 'c'){
5962 args.stream = sp_stream_get(context_apply(tmp, context,
5963 "xxx", sizeof(tmp)),
5964 SP_SAME);
5965 if(!args.stream){
5966 args.stream = pine_mail_open(NULL, tmp,
5967 OP_SILENT|OP_HALFOPEN|SP_USEPOOL|SP_TEMPUSE,
5968 NULL);
5969 args.newstream = (args.stream != NULL);
5973 rv = foreach_folder(context, selected,
5974 foreach_do_scan, (void *) &args);
5976 if(args.newstream)
5977 pine_mail_close(args.stream);
5979 if(rv)
5980 return(1);
5983 cmd_cancelled("Select");
5984 return(0);
5989 foreach_do_scan(FOLDER_S *f, void *d)
5991 SCANARG_S *sa = (SCANARG_S *) d;
5993 return((sa->type == 'n' && srchstr(FLDR_NAME(f), sa->pattern))
5994 || (sa->type == 'c'
5995 && scan_scan_folder(sa->stream, sa->context, f, sa->pattern)));
6000 scan_scan_folder(MAILSTREAM *stream, CONTEXT_S *context, FOLDER_S *f, char *pattern)
6002 MM_LIST_S ldata;
6003 LISTRES_S response;
6004 int we_cancel = 0;
6005 char *folder, *ref = NULL, tmp[MAILTMPLEN];
6007 folder = f->name;
6008 snprintf(tmp, sizeof(tmp), "Scanning \"%s\"", FLDR_NAME(f));
6009 tmp[sizeof(tmp)-1] = '\0';
6010 we_cancel = busy_cue(tmp, NULL, 1);
6012 mm_list_info = &ldata; /* tie down global reference */
6013 memset(&ldata, 0, sizeof(MM_LIST_S));
6014 ldata.filter = mail_list_response;
6015 memset(ldata.data = &response, 0, sizeof(LISTRES_S));
6018 * If no preset reference string, must be at top of context
6020 if(context && context_isambig(folder) && !(ref = context->dir->ref)){
6021 char *p;
6023 if((p = strstr(context->context, "%s")) != NULL){
6024 if(!*(p+2)){
6025 snprintf(tmp, sizeof(tmp), "%.*s", (int) MIN(p - context->context, sizeof(tmp)-1),
6026 context->context);
6027 tmp[sizeof(tmp)-1] = '\0';
6028 ref = tmp;
6030 else{
6031 snprintf(tmp, sizeof(tmp), context->context, folder);
6032 tmp[sizeof(tmp)-1] = '\0';
6033 folder = tmp;
6034 ref = "";
6037 else
6038 ref = context->context;
6041 mail_scan(stream, ref, folder, pattern);
6043 if(context && context->dir && response.delim)
6044 context->dir->delim = response.delim;
6046 if(we_cancel)
6047 cancel_busy_cue(-1);
6049 return(((response.isfile) ? FEX_ISFILE : 0)
6050 | ((response.isdir) ? FEX_ISDIR : 0));
6055 folder_select_props(struct pine *ps, CONTEXT_S *context, int selected)
6057 int cmp = 0;
6058 long flags = 0L, count;
6059 static ESCKEY_S prop_opts[] = {
6060 {'u', 'u', "U", N_("Unseen msgs")},
6061 {'n', 'n', "N", N_("New msgs")},
6062 {'c', 'c', "C", N_("msg Count")},
6063 {-1, 0, NULL, NULL}
6066 if(context->use & CNTXT_INCMNG){
6067 q_status_message(SM_ORDER | SM_DING, 3, 3,
6068 _("Select \"Properties\" not supported in Incoming Folders"));
6069 return(0);
6072 switch(radio_buttons(SEL_PROP_PMT, -FOOTER_ROWS(ps_global),
6073 prop_opts, 'n', 'x', h_folder_prop, RB_NORM)){
6074 case 'c' : /* message count */
6075 if(folder_select_count(&count, &cmp))
6076 flags = SA_MESSAGES;
6078 break;
6080 case 'n' : /* folders with new */
6081 flags = SA_RECENT;
6082 break;
6084 case 'u' : /* folders with unseen */
6085 flags = SA_UNSEEN;
6086 break;
6088 case 'x' :
6089 default :
6090 break;
6093 if(flags){
6094 int rv;
6095 char tmp[MAILTMPLEN];
6096 STATARG_S args;
6098 memset(&args, 0, sizeof(STATARG_S));
6099 args.flags = flags;
6100 args.context = context;
6101 args.nmsgs = count;
6102 args.cmp = cmp;
6104 args.stream = sp_stream_get(context_apply(tmp, context,
6105 "xxx", sizeof(tmp)),
6106 SP_SAME);
6107 if(!args.stream){
6108 args.stream = pine_mail_open(NULL, tmp,
6109 OP_SILENT|OP_HALFOPEN|SP_USEPOOL|SP_TEMPUSE,
6110 NULL);
6111 args.newstream = (args.stream != NULL);
6114 rv = foreach_folder(context, selected,
6115 foreach_do_stat, (void *) &args);
6117 if(args.newstream)
6118 pine_mail_close(args.stream);
6120 if(rv)
6121 return(1);
6124 cmd_cancelled("Select");
6125 return(0);
6130 folder_select_count(long int *count, int *cmp)
6132 int r, flags;
6133 char number[32], prompt[128];
6134 static char *tense[] = {"EQUAL TO", "LESS THAN", "GREATER THAN"};
6135 static ESCKEY_S sel_num_opt[] = {
6136 {ctrl('W'), 14, "^W", N_("Toggle Comparison")},
6137 {-1, 0, NULL, NULL}
6140 *count = 0L;
6141 while(1){
6142 flags = OE_APPEND_CURRENT | OE_DISALLOW_HELP;
6143 snprintf(number, sizeof(number), "%ld", *count);
6144 number[sizeof(number)-1] = '\0';
6145 snprintf(prompt, sizeof(prompt), "Select folders with messages %s : ", tense[*cmp]);
6146 prompt[sizeof(prompt)-1] = '\0';
6147 r = optionally_enter(number, -FOOTER_ROWS(ps_global), 0, sizeof(number),
6148 prompt, sel_num_opt, NO_HELP, &flags);
6149 switch (r){
6150 case 0 :
6151 if(!*number)
6152 break;
6153 else if((*count = atol(number)) < 0L)
6154 q_status_message(SM_ORDER, 3, 3,
6155 "Can't have NEGATIVE message count!");
6156 else
6157 return(1); /* success */
6159 case 3 : /* help */
6160 case 4 : /* redraw */
6161 continue;
6163 case 14 : /* toggle comparison */
6164 ++(*cmp);
6165 *cmp = *cmp % 3;
6166 continue;
6168 case -1 : /* cancel */
6169 default:
6170 break;
6173 break;
6176 return(0); /* return failure */
6181 foreach_do_stat(FOLDER_S *f, void *d)
6183 STATARG_S *sa = (STATARG_S *) d;
6184 int rv = 0;
6186 if(ps_global->context_current == sa->context
6187 && !strcmp(ps_global->cur_folder, FLDR_NAME(f))){
6188 switch(sa->flags){
6189 case SA_MESSAGES :
6190 switch(sa->cmp){
6191 case 0 : /* equals */
6192 if(ps_global->mail_stream->nmsgs == sa->nmsgs)
6193 rv = 1;
6195 break;
6197 case 1 : /* less than */
6198 if(ps_global->mail_stream->nmsgs < sa->nmsgs)
6199 rv = 1;
6201 break;
6203 case 2 :
6204 if(ps_global->mail_stream->nmsgs > sa->nmsgs)
6205 rv = 1;
6207 break;
6209 default :
6210 break;
6213 break;
6215 case SA_RECENT :
6216 if(count_flagged(ps_global->mail_stream, F_RECENT))
6217 rv = 1;
6219 break;
6221 case SA_UNSEEN :
6222 if(count_flagged(ps_global->mail_stream, F_UNSEEN))
6223 rv = 1;
6225 break;
6227 default :
6228 break;
6231 else{
6232 int we_cancel = 0;
6233 char msg_buf[MAX_BM+1];
6234 extern MAILSTATUS mm_status_result;
6236 snprintf(msg_buf, sizeof(msg_buf), "Checking %s for %s", FLDR_NAME(f),
6237 (sa->flags == SA_UNSEEN)
6238 ? "unseen messages"
6239 : (sa->flags == SA_MESSAGES) ? "message count"
6240 : "recent messages");
6241 msg_buf[sizeof(msg_buf)-1] = '\0';
6242 we_cancel = busy_cue(msg_buf, NULL, 0);
6244 if(!context_status(sa->context, sa->stream, f->name, sa->flags))
6245 mm_status_result.flags = 0L;
6247 if(we_cancel)
6248 cancel_busy_cue(0);
6250 if(sa->flags & mm_status_result.flags)
6251 switch(sa->flags){
6252 case SA_MESSAGES :
6253 switch(sa->cmp){
6254 case 0 : /* equals */
6255 if(mm_status_result.messages == sa->nmsgs)
6256 rv = 1;
6258 break;
6260 case 1 : /* less than */
6261 if(mm_status_result.messages < sa->nmsgs)
6262 rv = 1;
6264 break;
6266 case 2 :
6267 if(mm_status_result.messages > sa->nmsgs)
6268 rv = 1;
6270 break;
6272 default :
6273 break;
6276 break;
6278 case SA_RECENT :
6279 if(mm_status_result.recent)
6280 rv = 1;
6282 break;
6284 case SA_UNSEEN :
6285 if(mm_status_result.unseen)
6286 rv = 1;
6288 break;
6290 default :
6291 break;
6295 return(rv);
6300 foreach_folder(CONTEXT_S *context, int selected, int (*test) (FOLDER_S *, void *), void *args)
6302 int i, n, rv = 1;
6303 int we_turned_on = 0;
6304 FOLDER_S *fp;
6306 we_turned_on = intr_handling_on();
6308 for(i = 0, n = folder_total(FOLDERS(context)); i < n; i++){
6309 if(ps_global->intr_pending){
6310 for(; i >= 0; i--)
6311 folder_entry(i, FOLDERS(context))->scanned = 0;
6313 cmd_cancelled("Select");
6314 rv = 0;
6315 break;
6318 fp = folder_entry(i, FOLDERS(context));
6319 fp->scanned = 0;
6320 if((!selected || fp->selected) && fp->isfolder && (*test)(fp, args))
6321 fp->scanned = 1;
6324 if(we_turned_on)
6325 intr_handling_off();
6327 return(rv);
6331 /*----------------------------------------------------------------------
6332 Return the path delimiter for the given folder on the given server
6334 Args: folder -- folder type for delimiter
6336 ----*/
6338 folder_delimiter(char *folder)
6340 int rv, we_cancel = 0;
6342 we_cancel = busy_cue(NULL, NULL, 1);
6344 rv = get_folder_delimiter(folder);
6346 if(we_cancel)
6347 cancel_busy_cue(-1);
6349 return(rv);
6354 * next_folder - given a current folder in a context, return the next in
6355 * the list, or NULL if no more or there's a problem.
6357 * Args streamp -- If set, try to re-use this stream for checking.
6358 * next -- Put return value here, return points to this
6359 * nextlen -- Length of next
6360 * current -- Current folder, so we know where to start looking
6361 * cntxt --
6362 * find_recent -- Returns the number of recent here. The presence of
6363 * this arg also indicates that we should do the calls
6364 * to figure out whether there is a next interesting folder
6365 * or not.
6366 * did_cancel -- Tell caller if user canceled. Only used if find_recent
6367 * is also set. Also, user will only be given the
6368 * opportunity to cancel if this is set. If it isn't
6369 * set, we just plow ahead when there is an error or
6370 * when the folder does not exist.
6372 char *
6373 next_folder(MAILSTREAM **streamp, char *next, size_t nextlen, char *current, CONTEXT_S *cntxt, long int *find_recent, int *did_cancel)
6375 int index, recent = 0, failed_status = 0, try_fast;
6376 char prompt[128];
6377 FOLDER_S *f = NULL;
6378 char tmp[MAILTMPLEN];
6381 /* note: find_folders may assign "stream" */
6382 build_folder_list(streamp, cntxt, NULL, NULL,
6383 NEWS_TEST(cntxt) ? BFL_LSUB : BFL_NONE);
6385 try_fast = (F_ON(F_ENABLE_FAST_RECENT, ps_global) &&
6386 F_OFF(F_TAB_USES_UNSEEN, ps_global));
6387 if(find_recent)
6388 *find_recent = 0L;
6390 for(index = folder_index(current, cntxt, FI_FOLDER) + 1;
6391 index > 0
6392 && index < folder_total(FOLDERS(cntxt))
6393 && (f = folder_entry(index, FOLDERS(cntxt)))
6394 && !f->isdir;
6395 index++)
6396 if(find_recent){
6397 MAILSTREAM *stream = NULL;
6398 int rv, we_cancel = 0, match;
6399 char msg_buf[MAX_BM+1];
6401 /* must be a folder and it can't be the current one */
6402 if(ps_global->context_current == ps_global->context_list
6403 && !strcmp(ps_global->cur_folder, FLDR_NAME(f)))
6404 continue;
6407 * If we already have the folder open, short circuit all this
6408 * stuff.
6410 match = 0;
6411 if((stream = sp_stream_get(context_apply(tmp, cntxt, f->name,
6412 sizeof(tmp)),
6413 SP_MATCH)) != NULL
6414 || (!IS_REMOTE(tmp) && (stream = already_open_stream(tmp, AOS_NONE)) != NULL)){
6415 (void) pine_mail_ping(stream);
6417 if(F_ON(F_TAB_USES_UNSEEN, ps_global)){
6419 * Just fall through and let the status call below handle
6420 * the already open stream. If we were doing this the
6421 * same as the else case, we would figure out how many
6422 * unseen are in this open stream by doing a search.
6423 * Instead of repeating that code that is already in
6424 * pine_mail_status_full, fall through and note the
6425 * special case by lighting the match variable.
6427 match++;
6429 else{
6430 *find_recent = sp_recent_since_visited(stream);
6431 if(*find_recent){
6432 recent++;
6433 break;
6436 continue;
6440 snprintf(msg_buf, sizeof(msg_buf), "Checking %s for %s messages",
6441 FLDR_NAME(f), F_ON(F_TAB_USES_UNSEEN, ps_global) ? "unseen" : "recent");
6442 msg_buf[sizeof(msg_buf)-1] = '\0';
6443 we_cancel = busy_cue(msg_buf, NULL, 0);
6445 /* First, get a stream for the test */
6446 if(!stream && streamp && *streamp){
6447 if(context_same_stream(cntxt, f->name, *streamp)){
6448 stream = *streamp;
6450 else{
6451 pine_mail_close(*streamp);
6452 *streamp = NULL;
6456 if(!stream){
6457 stream = sp_stream_get(context_apply(tmp, cntxt, f->name,
6458 sizeof(tmp)),
6459 SP_SAME);
6463 * If interestingness is indeterminate or we're
6464 * told to explicitly, look harder...
6468 * We could make this more efficient in the cases where we're
6469 * opening a new stream or using streamp by having folder_exists
6470 * cache the stream. The change would require a folder_exists()
6471 * that caches streams, but most of the time folder_exists just
6472 * uses the inbox stream or ps->mail_stream.
6474 * Another thing to consider is that maybe there should be an
6475 * option to try to LIST a folder before doing a STATUS (or SELECT).
6476 * This isn't done by default for the case where a folder is
6477 * SELECTable but not LISTable, but on some servers doing an
6478 * RLIST first tells the server that we support mailbox referrals.
6480 if(!try_fast
6481 || !((rv = folder_exists(cntxt,f->name))
6482 & (FEX_ISMARKED | FEX_UNMARKED))){
6483 extern MAILSTATUS mm_status_result;
6485 if(try_fast && (rv == 0 || rv & FEX_ERROR)){
6486 failed_status = 1;
6487 mm_status_result.flags = 0L;
6489 else{
6490 if(stream){
6491 if(!context_status_full(cntxt, match ? NULL : stream,
6492 f->name,
6493 F_ON(F_TAB_USES_UNSEEN, ps_global)
6494 ? SA_UNSEEN : SA_RECENT,
6495 &f->uidvalidity,
6496 &f->uidnext)){
6497 failed_status = 1;
6498 mm_status_result.flags = 0L;
6501 else{
6502 /* so we can re-use the stream */
6503 if(!context_status_streamp_full(cntxt, streamp, f->name,
6504 F_ON(F_TAB_USES_UNSEEN, ps_global)
6505 ? SA_UNSEEN : SA_RECENT,
6506 &f->uidvalidity,
6507 &f->uidnext)){
6508 failed_status = 1;
6509 mm_status_result.flags = 0L;
6514 if(F_ON(F_TAB_USES_UNSEEN, ps_global)){
6515 rv = ((mm_status_result.flags & SA_UNSEEN)
6516 && (*find_recent = mm_status_result.unseen))
6517 ? FEX_ISMARKED : 0;
6519 else{
6520 rv = ((mm_status_result.flags & SA_RECENT)
6521 && (*find_recent = mm_status_result.recent))
6522 ? FEX_ISMARKED : 0;
6525 /* we don't know how many in this case */
6526 if(try_fast)
6527 *find_recent = 0L; /* consistency, boy! */
6530 if(we_cancel)
6531 cancel_busy_cue(0);
6533 if(failed_status && did_cancel){
6534 char buf1[6*MAX_SCREEN_COLS+1];
6535 int wid1, wid2;
6537 snprintf(prompt, sizeof(prompt), _("Check of folder %s failed. Continue "), FLDR_NAME(f));
6538 if(utf8_width(prompt) > MAXPROMPT){
6539 snprintf(prompt, sizeof(prompt), _("Check of %s failed. Continue "), FLDR_NAME(f));
6540 if((wid1=utf8_width(prompt)) > MAXPROMPT){
6541 if((wid2=utf8_width(FLDR_NAME(f))) > wid1-MAXPROMPT)
6542 snprintf(prompt, sizeof(prompt), _("Check of %s failed. Continue "), strsquish(buf1, sizeof(buf1), FLDR_NAME(f), wid2-(wid1-MAXPROMPT)));
6543 else
6544 snprintf(prompt, sizeof(prompt), _("Check failed. Continue "));
6548 if(want_to(prompt, 'y', 0, NO_HELP, WT_NORM) == 'n'){
6549 *did_cancel = 1;
6550 break;
6552 else
6553 /* have to reset this lower-level cancel marker */
6554 ps_global->user_says_cancel = 0;
6557 failed_status = 0;
6559 if(rv & FEX_ISMARKED){
6560 recent++;
6561 break;
6565 if(f && (!find_recent || recent)){
6566 strncpy(next, FLDR_NAME(f), nextlen);
6567 next[nextlen-1] = '\0';
6569 else if(nextlen > 0)
6570 *next = '\0';
6572 /* BUG: how can this be made smarter so we cache the list? */
6573 free_folder_list(cntxt);
6574 return((*next) ? next : NULL);
6578 /*----------------------------------------------------------------------
6579 Shuffle order of incoming folders
6580 ----*/
6582 shuffle_incoming_folders(CONTEXT_S *context, int index)
6584 int tot, i, deefault, rv, inheriting = 0;
6585 int new_index, index_within_var, new_index_within_var;
6586 int readonly = 0;
6587 char tmp[200];
6588 HelpType help;
6589 ESCKEY_S opts[3];
6590 char ***alval;
6591 EditWhich ew;
6592 FOLDER_S *fp;
6593 PINERC_S *prc = NULL;
6595 dprint((4, "shuffle_incoming_folders\n"));
6597 if(!(context->use & CNTXT_INCMNG) ||
6598 (tot = folder_total(FOLDERS(context))) < 2 ||
6599 index < 0 || index >= tot)
6600 return(0);
6602 if(index == 0){
6603 q_status_message(SM_ORDER,0,3, _("Cannot shuffle INBOX"));
6604 return(0);
6607 fp = folder_entry(index, FOLDERS(context));
6608 ew = config_containing_inc_fldr(fp);
6610 if(ps_global->restricted)
6611 readonly = 1;
6612 else{
6613 switch(ew){
6614 case Main:
6615 prc = ps_global->prc;
6616 break;
6617 case Post:
6618 prc = ps_global->post_prc;
6619 break;
6620 case None:
6621 break;
6624 readonly = prc ? prc->readonly : 1;
6627 if(prc && prc->quit_to_edit){
6628 quit_to_edit_msg(prc);
6629 return(0);
6632 if(readonly){
6633 q_status_message(SM_ORDER,3,5,
6634 _("Shuffle cancelled: config file not editable"));
6635 return(0);
6638 alval = ALVAL(&ps_global->vars[V_INCOMING_FOLDERS], ew);
6640 if(!(alval && *alval))
6641 return(0);
6643 i = 0;
6644 opts[i].ch = 'b';
6645 opts[i].rval = 'b';
6646 opts[i].name = "B";
6647 opts[i++].label = N_("Back");
6649 opts[i].ch = 'f';
6650 opts[i].rval = 'f';
6651 opts[i].name = "F";
6652 opts[i++].label = N_("Forward");
6654 opts[i].ch = -1;
6655 deefault = 'b';
6657 /* find where this entry is in the particular config list */
6658 index_within_var = -1;
6659 for(i = 0; (*alval)[i]; i++){
6660 expand_variables(tmp_20k_buf, SIZEOF_20KBUF, (*alval)[i], 0);
6661 if(i == 0 && !strcmp(tmp_20k_buf, INHERIT))
6662 inheriting = 1;
6663 else if(fp->varhash == line_hash(tmp_20k_buf)){
6664 index_within_var = i;
6665 break;
6669 if(index_within_var == -1){ /* didn't find it */
6670 q_status_message(SM_ORDER,3,5,
6671 _("Shuffle cancelled: unexpected trouble shuffling"));
6672 return(0);
6675 if(index_within_var == 0 || (inheriting && index_within_var == 1)){
6676 opts[0].ch = -2; /* no back */
6677 deefault = 'f';
6680 if(!(*alval)[i+1]) /* no forward */
6681 opts[1].ch = -2;
6683 if(opts[0].ch == -2 && opts[1].ch == -2){
6684 q_status_message(SM_ORDER, 0, 4,
6685 _("Cannot shuffle from one config file to another."));
6686 return(0);
6689 snprintf(tmp, sizeof(tmp), "Shuffle \"%s\" %s%s%s ? ",
6690 FLDR_NAME(folder_entry(index, FOLDERS(context))),
6691 (opts[0].ch != -2) ? "BACK" : "",
6692 (opts[0].ch != -2 && opts[1].ch != -2) ? " or " : "",
6693 (opts[1].ch != -2) ? "FORWARD" : "");
6694 tmp[sizeof(tmp)-1] = '\0';
6695 help = (opts[0].ch == -2) ? h_incoming_shuf_down
6696 : (opts[1].ch == -2) ? h_incoming_shuf_up
6697 : h_incoming_shuf;
6699 rv = radio_buttons(tmp, -FOOTER_ROWS(ps_global), opts, deefault, 'x',
6700 help, RB_NORM);
6702 new_index = index;
6703 new_index_within_var = index_within_var;
6705 switch(rv){
6706 case 'x':
6707 cmd_cancelled("Shuffle");
6708 return(0);
6710 case 'b':
6711 new_index_within_var--;
6712 new_index--;
6713 break;
6715 case 'f':
6716 new_index_within_var++;
6717 new_index++;
6718 break;
6721 if(swap_incoming_folders(index, new_index, FOLDERS(context))){
6722 char *stmp;
6724 /* swap them in the config variable, too */
6725 stmp = (*alval)[index_within_var];
6726 (*alval)[index_within_var] = (*alval)[new_index_within_var];
6727 (*alval)[new_index_within_var] = stmp;
6729 set_current_val(&ps_global->vars[V_INCOMING_FOLDERS], TRUE, FALSE);
6730 write_pinerc(ps_global, ew, WRP_NONE);
6732 return(1);
6734 else
6735 return(0);
6740 swap_incoming_folders(int index1, int index2, FLIST *flist)
6742 FOLDER_S *ftmp;
6744 if(!flist)
6745 return(0);
6747 if(index1 == index2)
6748 return(1);
6750 if(index1 < 0 || index1 >= flist->used){
6751 dprint((1, "Error in swap_incoming_folders: index1=%d, used=%d\n", index1, flist->used));
6752 return(0);
6755 if(index2 < 0 || index2 >= flist->used){
6756 dprint((1, "Error in swap_incoming_folders: index2=%d, used=%d\n", index2, flist->used));
6757 return(0);
6760 ftmp = flist->folders[index1];
6761 flist->folders[index1] = flist->folders[index2];
6762 flist->folders[index2] = ftmp;
6764 return(1);
6768 /*----------------------------------------------------------------------
6769 Find an entry in the folder list by matching names
6770 ----*/
6772 search_folder_list(void *list, char *name)
6774 int i;
6775 char *n;
6777 for(i = 0; i < folder_total(list); i++) {
6778 n = folder_entry(i, list)->name;
6779 if(strucmp(name, n) == 0)
6780 return(1); /* Found it */
6782 return(0);
6786 static CONTEXT_S *post_cntxt = NULL;
6788 /*----------------------------------------------------------------------
6789 Browse list of newsgroups available for posting
6791 Called from composer when ^T is typed in newsgroups field
6793 Args: none
6795 Returns: pointer to selected newsgroup, or NULL.
6796 Selector call in composer expects this to be alloc'd here.
6798 ----*/
6799 char *
6800 news_group_selector(char **error_mess)
6802 CONTEXT_S *tc;
6803 char *post_folder;
6804 int rc;
6805 char *em;
6807 /* Coming back from composer */
6808 fix_windsize(ps_global);
6809 init_sigwinch();
6811 post_folder = fs_get((size_t)MAILTMPLEN);
6813 /*--- build the post_cntxt -----*/
6814 em = get_post_list(ps_global->VAR_NNTP_SERVER);
6815 if(em != NULL){
6816 if(error_mess != NULL)
6817 *error_mess = cpystr(em);
6819 cancel_busy_cue(-1);
6820 return(NULL);
6823 /*----- Call the browser -------*/
6824 tc = post_cntxt;
6825 if((rc = folders_for_post(ps_global, &tc, post_folder)) != 0)
6826 post_cntxt = tc;
6828 cancel_busy_cue(-1);
6830 if(rc <= 0)
6831 return(NULL);
6833 return(post_folder);
6837 /*----------------------------------------------------------------------
6838 Get the list of news groups that are possible for posting
6840 Args: post_host -- host name for posting
6842 Returns NULL if list is retrieved, pointer to error message if failed
6844 This is kept in a standards "CONTEXT" for a acouple of reasons. First
6845 it makes it very easy to use the folder browser to display the
6846 newsgroup for selection on ^T from the composer. Second it will allow
6847 the same mechanism to be used for all folder lists on memory tight
6848 systems like DOS. The list is kept for the life of the session because
6849 fetching it is a expensive.
6851 ----*/
6852 char *
6853 get_post_list(char **post_host)
6855 char *post_context_string;
6857 if(!post_host || !post_host[0]) {
6858 /* BUG should assume inews and get this from active file */
6859 return(_("Can't post messages, NNTP server needs to be configured"));
6862 if(!post_cntxt){
6863 int we_cancel;
6864 size_t l;
6866 we_cancel = busy_cue(_("Getting full list of groups for posting"), NULL, 1);
6868 l = strlen(post_host[0]) + 20;
6869 post_context_string = (char *) fs_get((l+1) * sizeof(char));
6870 snprintf(post_context_string, l+1, "{%s/nntp}#news.[]", post_host[0]);
6871 post_context_string[l] = '\0';
6873 post_cntxt = new_context(post_context_string, NULL);
6874 post_cntxt->use |= CNTXT_FINDALL;
6875 post_cntxt->dir->status |= CNTXT_NOFIND;
6876 post_cntxt->next = NULL;
6878 build_folder_list(NULL, post_cntxt, NULL, NULL,
6879 NEWS_TEST(post_cntxt) ? BFL_LSUB : BFL_NONE);
6880 if(we_cancel)
6881 cancel_busy_cue(-1);
6883 return(NULL);
6888 fcc_tab_complete (char *prefix, char **answer, int tabtab, unsigned flags)
6890 char tmp[MAILTMPLEN+1];
6891 int n;
6892 CONTEXT_S *mc;
6894 if(prefix == NULL || *prefix == '\0')
6895 return 0;
6897 for(mc = ps_global->context_list; mc != NULL; mc = mc->next)
6898 if(mc->use & CNTXT_SAVEDFLT)
6899 break;
6901 if(mc == NULL) return 0;
6903 strncpy(tmp, prefix, sizeof(tmp));
6904 tmp[sizeof(tmp)-1] = '\0';
6906 if(!folder_complete(mc, tmp, sizeof(tmp), &n)){
6907 if(n){
6908 if(tabtab)
6909 display_folder_list(&mc, tmp, 1,folders_for_goto);
6911 else
6912 Writechar(BELL, 0);
6914 if(n)
6915 *answer = cpystr(tmp);
6916 return n == 0 ? 0 : n == 1 ? 2 : 1;
6921 #ifdef _WINDOWS
6923 folder_list_popup(sparms, in_handle)
6924 SCROLL_S *sparms;
6925 int in_handle;
6927 MPopup fldr_popup[20];
6929 memset(fldr_popup, 0, 20 * sizeof(MPopup));
6930 fldr_popup[0].type = tTail;
6931 if(in_handle){
6932 int i, n = 0;
6933 HANDLE_S *h = get_handle(sparms->text.handles, in_handle);
6934 FOLDER_S *fp = (h) ? folder_entry(h->h.f.index,
6935 FOLDERS(h->h.f.context))
6936 : NULL;
6938 if((i = menu_binding_index(sparms->keys.menu, MC_CHOICE)) >= 0){
6939 fldr_popup[n].type = tQueue;
6940 fldr_popup[n].data.val = sparms->keys.menu->keys[i].bind.ch[0];
6941 fldr_popup[n].label.style = lNormal;
6942 fldr_popup[n++].label.string = (fp && fp->isdir)
6943 ? "&View Directory"
6944 : "&View Folder";
6947 if((i = menu_binding_index(sparms->keys.menu, MC_SELCUR)) >= 0
6948 && bitnset(i, sparms->keys.bitmap)){
6949 fldr_popup[n].type = tQueue;
6950 fldr_popup[n].data.val = sparms->keys.menu->keys[i].bind.ch[0];
6951 fldr_popup[n].label.style = lNormal;
6952 fldr_popup[n++].label.string = (fp && fp->isdir)
6953 ? "&Select Directory"
6954 : "&Select Folder";
6957 if((i = menu_binding_index(sparms->keys.menu, MC_DELETE)) >= 0
6958 && bitnset(i, sparms->keys.bitmap)){
6959 fldr_popup[n].type = tQueue;
6960 fldr_popup[n].data.val = sparms->keys.menu->keys[i].bind.ch[0];
6961 fldr_popup[n].label.style = lNormal;
6962 fldr_popup[n++].label.string = "Delete Folder";
6965 if((i = menu_binding_index(sparms->keys.menu, MC_EXPORT)) >= 0
6966 && bitnset(i, sparms->keys.bitmap)){
6967 fldr_popup[n].type = tQueue;
6968 fldr_popup[n].data.val = sparms->keys.menu->keys[i].bind.ch[0];
6969 fldr_popup[n].label.style = lNormal;
6970 fldr_popup[n++].label.string = "Export Folder";
6973 if((i = menu_binding_index(sparms->keys.menu, MC_CHK_RECENT)) >= 0
6974 && bitnset(i, sparms->keys.bitmap)){
6975 fldr_popup[n].type = tQueue;
6976 fldr_popup[n].data.val = sparms->keys.menu->keys[i].bind.ch[0];
6977 fldr_popup[n].label.style = lNormal;
6978 fldr_popup[n++].label.string = "Check New Messages";
6981 if(n)
6982 fldr_popup[n++].type = tSeparator;
6984 folder_popup_config(((FPROC_S *)sparms->proc.data.p)->fs,
6985 sparms->keys.menu, &fldr_popup[n]);
6987 else
6988 folder_popup_config(((FPROC_S *)sparms->proc.data.p)->fs,
6989 sparms->keys.menu, fldr_popup);
6991 if(fldr_popup[0].type != tTail)
6992 mswin_popup(fldr_popup);
6994 return(0);
6999 folder_list_select_popup(sparms, in_handle)
7000 SCROLL_S *sparms;
7001 int in_handle;
7003 MPopup fldr_popup[20];
7005 memset(fldr_popup, 0, 20 * sizeof(MPopup));
7006 fldr_popup[0].type = tTail;
7007 if(in_handle){
7008 int i, n = 0;
7009 HANDLE_S *h = get_handle(sparms->text.handles, in_handle);
7010 FOLDER_S *fp = (h) ? folder_entry(h->h.f.index,FOLDERS(h->h.f.context))
7011 : NULL;
7013 if((i = menu_binding_index(sparms->keys.menu, MC_CHOICE)) >= 0){
7014 fldr_popup[n].type = tQueue;
7015 fldr_popup[n].data.val = sparms->keys.menu->keys[i].bind.ch[0];
7016 fldr_popup[n].label.style = lNormal;
7017 fldr_popup[n++].label.string = (fp && fp->isdir)
7018 ? "&View Directory"
7019 : "&Select";
7021 fldr_popup[n++].type = tSeparator;
7024 folder_popup_config(((FPROC_S *)sparms->proc.data.p)->fs,
7025 sparms->keys.menu, &fldr_popup[n]);
7027 else
7028 folder_popup_config(((FPROC_S *)sparms->proc.data.p)->fs,
7029 sparms->keys.menu, fldr_popup);
7031 if(fldr_popup[0].type != tTail)
7032 mswin_popup(fldr_popup);
7034 return(0);
7039 * Just a little something to simplify assignments
7041 #define FLDRPOPUP(p, c, s) { \
7042 (p)->type = tQueue; \
7043 (p)->data.val = c; \
7044 (p)->label.style = lNormal; \
7045 (p)->label.string = s; \
7049 /*----------------------------------------------------------------------
7050 Popup Menu configurator
7052 ----*/
7053 void
7054 folder_popup_config(fs, km, popup)
7055 FSTATE_S *fs;
7056 struct key_menu *km;
7057 MPopup *popup;
7059 int i;
7061 if((i = menu_binding_index(km, MC_PARENT)) >= 0){
7062 FLDRPOPUP(popup, km->keys[i].bind.ch[0], "Parent Directory");
7063 popup++;
7066 if(fs->km == &folder_km){
7067 if((fs->context->next || fs->context->prev) && !fs->combined_view){
7068 FLDRPOPUP(popup, '<', "Collection List");
7069 popup++;
7072 else if((i = menu_binding_index(km, MC_COLLECTIONS)) >= 0){
7073 FLDRPOPUP(popup, km->keys[i].bind.ch[0], "Collection List");
7074 popup++;
7077 if((i = menu_binding_index(km, MC_INDEX)) >= 0){
7078 FLDRPOPUP(popup, km->keys[i].bind.ch[0], "Current Folder Index");
7079 popup++;
7082 if((i = menu_binding_index(km, MC_MAIN)) >= 0){
7083 FLDRPOPUP(popup, km->keys[i].bind.ch[0], "Main Menu");
7084 popup++;
7087 popup->type = tTail; /* tie off the array */
7089 #endif /* _WINDOWS */