1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: folder.c 1144 2008-08-14 16:53:34Z hubert@u.washington.edu $";
6 * ========================================================================
7 * Copyright 2013-2020 Eduardo Chappa
8 * Copyright 2006-2008 University of Washington
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 /*======================================================================
22 Screen to display and manage all the users folders
24 This puts up a list of all the folders in the users mail directory on
25 the screen spacing it nicely. The arrow keys move from one to another
26 and the user can delete the folder or select it to change to or copy a
27 message to. The display lets the user scroll up or down a screen full,
28 or search for a folder name.
33 #include "../c-client/utf8aux.h"
49 #include "../pith/state.h"
50 #include "../pith/conf.h"
51 #include "../pith/folder.h"
52 #include "../pith/flag.h"
53 #include "../pith/filter.h"
54 #include "../pith/msgno.h"
55 #include "../pith/thread.h"
56 #include "../pith/util.h"
57 #include "../pith/stream.h"
58 #include "../pith/save.h"
59 #include "../pith/busy.h"
60 #include "../pith/list.h"
63 #define SUBSCRIBE_PMT \
64 _("Enter newsgroup name (or partial name to get a list): ")
65 #define LISTMODE_GRIPE _("Use \"X\" to mark selections in list mode")
66 #define SEL_ALTER_PMT _("ALTER folder selection : ")
67 #define SEL_TEXT_PMT _("Select by folder Name or Contents ? ")
68 #define SEL_PROP_PMT _("Select by which folder property ? ")
69 #define DIR_FOLD_PMT \
70 _("Folder and directory of the same name will be deleted. Continue")
77 #define FLW_LUNK 0x01 /* Using handles */
78 #define FLW_SLCT 0x02 /* Some folder is selected, may need X to show it */
79 #define FLW_LIST 0x04 /* Allow for ListMode for subscribing */
80 #define FLW_UNSEEN 0x08 /* Add (unseen) */
83 #define CLR_UNSEEN 0x01 /* color folder with unseen/new messages */
84 #define CLR_FOLDER 0x02 /* color a name of folder or directory */
85 #define CLR_DIRECT 0x04 /* color a separator of a directory */
86 #define CLR_FLDRLT 0x08 /* color of explanatory text in list scrn*/
87 #define CLR_NORMAL 0x10 /* use normal color */
89 /*----------------------------------------------------------------------
90 The data needed to redraw the folders screen, including the case where the
91 screen changes size in which case it may recalculate the folder_display.
96 * Struct managing folder_lister arguments and holding state
97 * for various internal methods
99 typedef struct _folder_screen
{
100 CONTEXT_S
*context
; /* current collection */
101 CONTEXT_S
*list_cntxt
; /* list mode collection */
102 MAILSTREAM
**cache_streamp
; /* cached mailstream */
103 char first_folder
[MAXFOLDER
];
104 unsigned first_dir
:1; /* first_folder is a dir */
105 unsigned combined_view
:1; /* display flat folder list */
106 unsigned no_dirs
:1; /* no dirs in this screen */
107 unsigned no_empty_dirs
:1; /* no empty dirs on this screen */
108 unsigned relative_path
:1; /* return fully-qual'd specs */
110 unsigned force_intro
:1;
112 unsigned include_unseen_cnt
:1;
113 struct key_menu
*km
; /* key label/command bindings */
114 struct _func_dispatch
{
115 int (*valid
)(FOLDER_S
*, struct _folder_screen
*);
129 * Struct mananging folder_lister metadata as it gets passed
130 * in and back up thru scrolltool
132 typedef struct _folder_proc
{
135 unsigned done
:1; /* done listing folders?... */
136 unsigned all_done
:1; /* ...and will list no more forever */
139 #define FPROC(X) ((FPROC_S *)(X)->proc.data.p)
142 typedef struct _scanarg
{
151 typedef struct _statarg
{
162 * Internal prototypes
164 STRLIST_S
*folders_for_subscribe(struct pine
*, CONTEXT_S
*, char *);
165 int folders_for_post(struct pine
*, CONTEXT_S
**, char *);
166 int folder_selector(struct pine
*, FSTATE_S
*, char *, CONTEXT_S
**);
167 void folder_sublist_context(char *, CONTEXT_S
*, CONTEXT_S
*, FDIR_S
**, int);
168 CONTEXT_S
*context_screen(CONTEXT_S
*, struct key_menu
*, int);
169 int exit_collection_add(struct headerentry
*, void (*)(void), int, char **);
170 char *cancel_collection_add(void (*)(void));
171 char *cancel_collection_edit(void (*)(void));
172 char *cancel_collection_editing(char *, void (*)(void));
173 int build_namespace(char *, char **, char **, BUILDER_ARG
*, int *);
174 int fl_val_gen(FOLDER_S
*, FSTATE_S
*);
175 int fl_val_writable(FOLDER_S
*, FSTATE_S
*);
176 int fl_val_subscribe(FOLDER_S
*, FSTATE_S
*);
177 STRLIST_S
*folder_lister(struct pine
*, FSTATE_S
*);
178 int folder_list_text(struct pine
*, FPROC_S
*, gf_io_t
, HANDLE_S
**, int);
179 int folder_list_write(gf_io_t
, HANDLE_S
**, CONTEXT_S
*, int, char *, int);
180 int folder_list_write_prefix(FOLDER_S
*, int, gf_io_t
);
181 int folder_list_write_middle(FOLDER_S
*fp
, CONTEXT_S
*ctxt
, gf_io_t pc
, HANDLE_S
*);
182 int folder_list_write_suffix(FOLDER_S
*, int, gf_io_t
);
183 int color_monitored(FOLDER_S
*, int, int);
184 int color_test_for_folder(char *, char *);
185 int color_write_for_folder(gf_io_t pc
, int testtype
);
186 int use_color_for_folder(FOLDER_S
*fp
);
187 int folder_list_ith(int, CONTEXT_S
*);
188 char *folder_list_center_space(char *, int);
189 HANDLE_S
*folder_list_handle(FSTATE_S
*, HANDLE_S
*);
190 int folder_processor(int, MSGNO_S
*, SCROLL_S
*);
191 int folder_lister_clickclick(SCROLL_S
*);
192 int folder_lister_choice(SCROLL_S
*);
193 int folder_lister_finish(SCROLL_S
*, CONTEXT_S
*, int);
194 int folder_lister_addmanually(SCROLL_S
*);
195 void folder_lister_km_manager(SCROLL_S
*, int);
196 void folder_lister_km_sel_manager(SCROLL_S
*, int);
197 void folder_lister_km_sub_manager(SCROLL_S
*, int);
198 int folder_select(struct pine
*, CONTEXT_S
*, int);
199 int folder_lister_select(FSTATE_S
*, CONTEXT_S
*, int, int);
200 int folder_lister_parent(FSTATE_S
*, CONTEXT_S
*, int, int);
201 char *folder_lister_fullname(FSTATE_S
*, char *);
202 void folder_export(SCROLL_S
*);
203 int folder_import(SCROLL_S
*, char *, size_t);
204 int folder_select_toggle(CONTEXT_S
*, int, int (*)(CONTEXT_S
*, int));
205 char *end_bracket_no_nest(char *);
206 int group_subscription(char *, size_t, CONTEXT_S
*);
207 int rename_folder(CONTEXT_S
*, int, char *, size_t, MAILSTREAM
*);
208 int delete_folder(CONTEXT_S
*, int, char *, size_t, MAILSTREAM
**);
209 void print_folders(FPROC_S
*);
210 int scan_get_pattern(char *, char *, int);
211 int folder_select_text(struct pine
*, CONTEXT_S
*, int);
212 int foreach_do_scan(FOLDER_S
*, void *);
213 int scan_scan_folder(MAILSTREAM
*, CONTEXT_S
*, FOLDER_S
*, char *);
214 int folder_select_props(struct pine
*, CONTEXT_S
*, int);
215 int folder_select_count(long *, int *);
216 int foreach_do_stat(FOLDER_S
*, void *);
217 int foreach_folder(CONTEXT_S
*, int, int (*)(FOLDER_S
*, void *), void *);
218 int folder_delimiter(char *);
219 int shuffle_incoming_folders(CONTEXT_S
*, int);
220 int swap_incoming_folders(int, int, FLIST
*);
221 int search_folder_list(void *, char *);
222 char *get_post_list(char **);
223 char *quote_brackets_if_needed(char *);
225 int folder_list_popup(SCROLL_S
*, int);
226 int folder_list_select_popup(SCROLL_S
*, int);
227 void folder_popup_config(FSTATE_S
*, struct key_menu
*,MPopup
*);
231 /*----------------------------------------------------------------------
232 Front end to folder lister when it's called from the main menu
234 Args: ps -- The general pine_state data structure
236 Result: runs context and folder listers
240 folder_screen(struct pine
*ps
)
243 CONTEXT_S
*cntxt
= ps
->context_current
;
246 MAILSTREAM
*cache_stream
= NULL
;
248 dprint((1, "=== folder_screen called ====\n"));
249 mailcap_free(); /* free resources we won't be using for a while */
250 ps
->next_screen
= SCREEN_FUN_NULL
;
252 /* Initialize folder state and dispatches */
253 memset(&fs
, 0, sizeof(FSTATE_S
));
255 fs
.cache_streamp
= &cache_stream
;
256 fs
.combined_view
= F_ON(F_CMBND_FOLDER_DISP
, ps_global
) != 0;
257 fs
.agg_ops
= F_ON(F_ENABLE_AGG_OPS
, ps_global
) != 0;
258 fs
.relative_path
= 1;
259 fs
.include_unseen_cnt
= 1;
260 fs
.f
.valid
= fl_val_gen
;
261 /* TRANSLATORS: The all upper case things are screen titles */
262 fs
.f
.title
.bar
= _("FOLDER LIST");
263 fs
.f
.title
.style
= FolderName
;
264 fs
.f
.help
.text
= h_folder_maint
;
265 fs
.f
.help
.title
= _("HELP FOR FOLDERS");
268 if(context_isambig(ps
->cur_folder
)
269 && (IS_REMOTE(ps
->cur_folder
) || !is_absolute_path(ps
->cur_folder
)
270 || (cntxt
&& cntxt
->context
&& cntxt
->context
[0] == '{'))){
271 if(strlen(ps_global
->cur_folder
) < MAXFOLDER
- 1){
272 strncpy(fs
.first_folder
, ps_global
->cur_folder
, MAXFOLDER
);
273 fs
.first_folder
[MAXFOLDER
-1] = '\0';
277 * If we're asked to start in the folder list of the current
278 * folder and it looks like the current folder is part of the
279 * current context, try to start in the list of folders in the
282 if(ps
->start_in_context
|| fs
.combined_view
){
283 char tmp
[MAILTMPLEN
], *p
, *q
;
286 ps
->start_in_context
= 0;
289 if(!(NEWS_TEST(cntxt
) || (cntxt
->use
& CNTXT_INCMNG
))
291 && strchr(ps
->cur_folder
, cntxt
->dir
->delim
)){
292 for(p
= strchr(q
= ps
->cur_folder
, cntxt
->dir
->delim
);
294 p
= strchr(q
= ++p
, cntxt
->dir
->delim
)){
295 strncpy(tmp
, q
, MIN(p
- q
, sizeof(tmp
)-1));
296 tmp
[MIN(p
- q
, sizeof(tmp
)-1)] = '\0';
298 fp
= next_folder_dir(cntxt
, tmp
, FALSE
, fs
.cache_streamp
);
300 fp
->desc
= folder_lister_desc(cntxt
, fp
);
302 /* Insert new directory into list */
303 fp
->delim
= cntxt
->dir
->delim
;
304 fp
->prev
= cntxt
->dir
;
305 fp
->status
|= CNTXT_SUBDIR
;
312 while(ps
->next_screen
== SCREEN_FUN_NULL
313 && ((n
++) ? (cntxt
= context_screen(cntxt
,&c_mgr_km
,1)) != NULL
:1)){
316 if(F_ON(F_ENABLE_INCOMING_CHECKING
, ps
) && ps
->VAR_INCOMING_FOLDERS
&& ps
->VAR_INCOMING_FOLDERS
[0])
317 ps
->in_folder_screen
= 1;
319 if((folders
= folder_lister(ps
, &fs
)) != NULL
){
321 ps
->in_folder_screen
= 0;
324 blank_keymenu(ps
->ttyo
->screen_rows
- 2, 0);
325 ps
->mangled_footer
= 1;
328 if(do_broach_folder((char *) folders
->name
,
329 fs
.context
, fs
.cache_streamp
330 && *fs
.cache_streamp
? fs
.cache_streamp
332 reset_context_folders(ps
->context_list
);
333 ps
->next_screen
= mail_index_screen
;
337 *fs
.cache_streamp
= NULL
;
338 free_strlist(&folders
);
341 ps
->in_folder_screen
= 0;
344 if(fs
.cache_streamp
&& *fs
.cache_streamp
)
345 pine_mail_close(*fs
.cache_streamp
);
347 ps
->prev_screen
= folder_screen
;
351 /*----------------------------------------------------------------------
352 Front end to folder lister when it's called from the main menu
354 Args: ps -- The general pine_state data structure
356 Result: runs context and folder listers
360 folder_config_screen(struct pine
*ps
, int edit_exceptions
)
363 char title
[50], htitle
[50];
365 dprint((1, "=== folder_config_screen called ====\n"));
366 mailcap_free(); /* free resources we won't be using for a while */
369 snprintf(title
, sizeof(title
), _("SETUP EXCEPTIONS COLLECTION LIST"));
370 snprintf(htitle
, sizeof(htitle
), _("HELP FOR SETUP EXCEPTIONS COLLECTIONS"));
373 snprintf(title
, sizeof(title
), _("SETUP COLLECTION LIST"));
374 snprintf(htitle
, sizeof(htitle
), _("HELP FOR SETUP COLLECTIONS"));
377 memset(&css
, 0, sizeof(CONT_SCR_S
));
379 /* TRANSLATORS: Print something1 using something2.
380 contexts is something1 */
381 css
.print_string
= _("contexts");
382 css
.contexts
= &ps_global
->context_list
;
383 css
.help
.text
= h_collection_maint
;
384 css
.help
.title
= htitle
;
385 css
.keymenu
= &c_cfg_km
;
389 * Use conf_scroll_screen to manage display/selection
392 context_config_screen(ps_global
, &css
, edit_exceptions
);
396 /*----------------------------------------------------------------------
397 Browse folders for ^T selection from the Goto Prompt
400 cntxtp -- pointer to addr of context to start in, list, and return
401 folder -- pointer to buffer inwhich to return selected folder
403 Returns: 1 if we have something valid in cntxtp and folder
404 0 if problem or user cancelled
408 folders_for_goto(struct pine
*ps
, CONTEXT_S
**cntxtp
, char *folder
, int sublist
)
411 CONTEXT_S fake_context
;
412 FDIR_S
*fake_dir
= NULL
;
415 dprint((1, "=== folders_for_goto called ====\n"));
417 /* Initialize folder state and dispatches */
418 memset(&fs
, 0, sizeof(FSTATE_S
));
419 fs
.context
= *cntxtp
;
420 fs
.combined_view
= !sublist
&& F_ON(F_CMBND_FOLDER_DISP
, ps_global
) != 0;
421 fs
.f
.valid
= fl_val_gen
;
422 fs
.f
.title
.bar
= _("GOTO: SELECT FOLDER");
423 fs
.f
.title
.style
= FolderName
;
424 fs
.f
.help
.text
= h_folder_open
;
425 fs
.f
.help
.title
= _("HELP FOR OPENING FOLDERS");
426 fs
.km
= &folder_sel_km
;
428 /* If we were provided a string,
429 * dummy up a context for a substring match
431 if(sublist
&& *folder
&& context_isambig(folder
)){
432 if((*cntxtp
)->use
& CNTXT_INCMNG
){
433 q_status_message(SM_ORDER
, 0, 3,
434 _("All folders displayed for Incoming Collection"));
437 folder_sublist_context(folder
, *cntxtp
, &fake_context
,
439 fs
.context
= &fake_context
;
440 fs
.relative_path
= 1;
442 cntxtp
= &fs
.context
;
446 rv
= folder_selector(ps
, &fs
, folder
, cntxtp
);
449 free_fdir(&fake_dir
, TRUE
);
455 /*----------------------------------------------------------------------
456 Browse folders for ^T selection from the Save Prompt
459 cntxtp -- pointer to addr of context to start in, list, and return
460 folder -- pointer to buffer inwhich to return selected folder
462 Returns: 1 if we have something valid in cntxtp and folder
463 0 if problem or user cancelled
467 folders_for_save(struct pine
*ps
, CONTEXT_S
**cntxtp
, char *folder
, int sublist
)
470 CONTEXT_S fake_context
;
471 FDIR_S
*fake_dir
= NULL
;
474 dprint((1, "=== folders_for_save called ====\n"));
476 /* Initialize folder state and dispatches */
477 memset(&fs
, 0, sizeof(FSTATE_S
));
478 fs
.context
= *cntxtp
;
479 fs
.combined_view
= F_ON(F_CMBND_FOLDER_DISP
, ps_global
) != 0;
480 fs
.f
.valid
= fl_val_gen
;
481 fs
.f
.title
.bar
= _("SAVE: SELECT FOLDER");
482 fs
.f
.title
.style
= MessageNumber
;
483 fs
.f
.help
.text
= h_folder_save
;
484 fs
.f
.help
.title
= _("HELP FOR SAVING MESSAGES TO FOLDERS");
485 fs
.km
= &folder_sela_km
;
487 /* If we were provided a string,
488 * dummy up a context for a substring match
490 if(sublist
&& *folder
&& context_isambig(folder
)){
491 if((*cntxtp
)->use
& CNTXT_INCMNG
){
492 q_status_message(SM_ORDER
, 0, 3,
493 _("All folders displayed for Incoming Collection"));
496 folder_sublist_context(folder
, *cntxtp
, &fake_context
,
498 fs
.context
= &fake_context
;
499 fs
.relative_path
= 1;
501 cntxtp
= &fs
.context
;
505 rv
= folder_selector(ps
, &fs
, folder
, cntxtp
);
508 free_fdir(&fake_dir
, TRUE
);
514 /*----------------------------------------------------------------------
515 Browse folders for ^T selection from the Subscribe Prompt
518 cntxtp -- pointer to addr of context to start in, list, and return
519 folder -- pointer to buffer inwhich to return selected folder
521 Returns: 1 if we have something valid in cntxtp and folder
522 0 if problem or user cancelled
526 folders_for_subscribe(struct pine
*ps
, CONTEXT_S
*cntxt
, char *folder
)
528 STRLIST_S
*folders
= NULL
;
530 void (*redraw
)(void);
532 dprint((1, "=== folders_for_sub called ====\n"));
534 /* Initialize folder state and dispatches */
535 memset(&fs
, 0, sizeof(FSTATE_S
));
537 fs
.f
.valid
= fl_val_subscribe
;
538 fs
.f
.title
.bar
= _("SUBSCRIBE: SELECT FOLDER");
539 fs
.f
.title
.style
= FolderName
;
540 fs
.f
.help
.text
= h_folder_subscribe
;
541 fs
.f
.help
.title
= _("HELP SELECTING NEWSGROUP TO SUBSCRIBE TO");
542 fs
.km
= &folder_sub_km
;
546 redraw
= ps_global
->redrawer
;
547 folders
= folder_lister(ps
, &fs
);
548 ps_global
->redrawer
= redraw
;
549 if(ps_global
->redrawer
)
550 (*ps_global
->redrawer
)();
556 /*----------------------------------------------------------------------
557 Browse folders for ^T selection for posting
560 cntxtp -- pointer to addr of context to start in, list, and return
561 folder -- pointer to buffer inwhich to return selected folder
563 Returns: 1 if we have something valid in cntxtp and folder
564 0 if problem or user cancelled
568 folders_for_post(struct pine
*ps
, CONTEXT_S
**cntxtp
, char *folder
)
572 dprint((1, "=== folders_for_post called ====\n"));
574 /* Initialize folder state and dispatches */
575 memset(&fs
, 0, sizeof(FSTATE_S
));
576 fs
.context
= *cntxtp
;
577 fs
.f
.valid
= fl_val_subscribe
;
578 fs
.f
.title
.bar
= _("NEWS: SELECT GROUP");
579 fs
.f
.title
.style
= FolderName
;
580 fs
.f
.help
.text
= h_folder_postnews
;
581 fs
.f
.help
.title
= _("HELP FOR SELECTING NEWSGROUP TO POST TO");
582 fs
.km
= &folder_post_km
;
584 return(folder_selector(ps
, &fs
, folder
, cntxtp
));
589 folder_selector(struct pine
*ps
, FSTATE_S
*fs
, char *folder
, CONTEXT_S
**cntxtp
)
595 fs
->context
= *cntxtp
;
596 if((folders
= folder_lister(ps
, fs
)) != NULL
){
597 strncpy(folder
, (char *) folders
->name
, MAILTMPLEN
-1);
598 folder
[MAILTMPLEN
-1] = '\0';
599 free_strlist(&folders
);
600 *cntxtp
= fs
->context
;
604 else if(!(fs
->context
605 && (fs
->context
->next
|| fs
->context
->prev
))
606 || fs
->combined_view
)
609 while((*cntxtp
= context_screen(*cntxtp
, &c_sel_km
, 0)) != NULL
);
616 folder_sublist_context(char *folder
, CONTEXT_S
*cntxt
, CONTEXT_S
*new_cntxt
, FDIR_S
**new_dir
, int lev
)
618 char *p
, *q
, *ref
, *wildcard
;
621 new_cntxt
->next
= new_cntxt
->prev
= NULL
;
622 new_cntxt
->dir
= *new_dir
= (FDIR_S
*) fs_get(sizeof(FDIR_S
));
623 memset(*new_dir
, 0, sizeof(FDIR_S
));
624 (*new_dir
)->status
|= CNTXT_NOFIND
;
625 (*new_dir
)->folders
= init_folder_entries();
626 if(!((*new_dir
)->delim
= cntxt
->dir
->delim
)){
627 /* simple LIST to acquire delimiter, doh */
628 build_folder_list(NULL
, new_cntxt
, "", NULL
,
629 NEWS_TEST(new_cntxt
) ? BFL_LSUB
: BFL_NONE
);
630 new_cntxt
->dir
->status
= CNTXT_NOFIND
;
633 wildcard
= NEWS_TEST(new_cntxt
) ? "*" : "%";
635 if((p
= strrindex(folder
, (*new_dir
)->delim
)) != NULL
){
636 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s", p
+ 1, wildcard
);
637 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
638 (*new_dir
)->view
.internal
= cpystr(tmp_20k_buf
);
639 for(ref
= tmp_20k_buf
, q
= new_cntxt
->context
;
640 (*ref
= *q
) != '\0' && !(*q
== '%' && *(q
+1) == 's');
644 for(q
= folder
; q
<= p
; q
++, ref
++)
648 (*new_dir
)->ref
= cpystr(tmp_20k_buf
);
650 (*new_dir
)->status
|= CNTXT_SUBDIR
;
653 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s%s",
654 (lev
> 1) ? wildcard
: "", folder
, wildcard
);
655 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
656 (*new_dir
)->view
.internal
= cpystr(tmp_20k_buf
);
657 /* leave (*new_dir)->ref == NULL */
660 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, _("List of folders matching \"%s*\""), folder
);
661 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
662 (*new_dir
)->desc
= cpystr(tmp_20k_buf
);
666 /*----------------------------------------------------------------------
667 Browse folders for ^T selection from the composer
669 Args: error_mess -- pointer to place to return an error message
671 Returns: result if folder selected, NULL if not
672 Composer expects the result to be alloc'd here
676 folders_for_fcc(char **errmsg
)
682 dprint((1, "=== folders_for_fcc called ====\n"));
684 /* Coming back from composer */
685 fix_windsize(ps_global
);
688 /* Initialize folder state and dispatches */
689 memset(&fs
, 0, sizeof(FSTATE_S
));
690 fs
.combined_view
= F_ON(F_CMBND_FOLDER_DISP
, ps_global
) != 0;
691 fs
.f
.valid
= fl_val_gen
;
692 fs
.f
.title
.bar
= _("FCC: SELECT FOLDER");
693 fs
.f
.title
.style
= FolderName
;
694 fs
.f
.help
.text
= h_folder_fcc
;
695 fs
.f
.help
.title
= _("HELP FOR SELECTING THE FCC");
696 fs
.km
= &folder_sela_km
;
698 /* start in the default save context */
699 fs
.context
= default_save_context(ps_global
->context_list
);
702 if((folders
= folder_lister(ps_global
, &fs
)) != NULL
){
705 /* replace nickname with full name */
706 if(!(name
= folder_is_nick((char *) folders
->name
,
707 FOLDERS(fs
.context
), 0)))
708 name
= (char *) folders
->name
;
710 if(context_isambig(name
) && !((fs
.context
->use
) & CNTXT_SAVEDFLT
)){
711 char path_in_context
[MAILTMPLEN
];
713 context_apply(path_in_context
, fs
.context
, name
,
714 sizeof(path_in_context
));
715 if(!(IS_REMOTE(path_in_context
)
716 || is_absolute_path(path_in_context
))){
718 * Name is relative to the home directory,
719 * so have to add that. Otherwise, the sender will
720 * assume it is in the primary collection since it
721 * will still be ambiguous.
723 build_path(tmp_20k_buf
, ps_global
->ui
.homedir
,
724 path_in_context
, SIZEOF_20KBUF
);
725 rs
= cpystr(tmp_20k_buf
);
728 rs
= cpystr(path_in_context
);
733 free_strlist(&folders
);
736 else if(!(fs
.context
&& (fs
.context
->next
|| fs
.context
->prev
))
740 while((fs
.context
= context_screen(fs
.context
, &c_fcc_km
, 0)) != NULL
);
746 /*----------------------------------------------------------------------
747 Browse folders for ^T selection from the role editor
749 Returns: result if folder selected, NULL if not
750 Tesult is alloc'd here
754 folder_for_config(int flags
)
760 dprint((1, "=== folder_for_config called ====\n"));
762 /* Initialize folder state and dispatches */
763 memset(&fs
, 0, sizeof(FSTATE_S
));
764 fs
.combined_view
= F_ON(F_CMBND_FOLDER_DISP
, ps_global
) != 0;
765 fs
.f
.valid
= fl_val_gen
;
766 fs
.f
.title
.bar
= _("SELECT FOLDER");
767 fs
.f
.title
.style
= FolderName
;
768 fs
.km
= &folder_sela_km
;
769 if(flags
& FOR_PATTERN
){
770 fs
.f
.help
.text
= h_folder_pattern_roles
;
771 fs
.f
.help
.title
= _("HELP FOR SELECTING CURRENT FOLDER");
773 else if(flags
& FOR_OPTIONSCREEN
){
774 fs
.f
.help
.text
= h_folder_stayopen_folders
;
775 fs
.f
.help
.title
= _("HELP FOR SELECTING FOLDER");
778 fs
.f
.help
.text
= h_folder_action_roles
;
779 fs
.f
.help
.title
= _("HELP FOR SELECTING FOLDER");
782 /* start in the current context */
783 fs
.context
= ps_global
->context_current
;
786 if((folders
= folder_lister(ps_global
, &fs
)) != NULL
){
789 /* replace nickname with full name */
790 if(!(flags
& (FOR_PATTERN
| FOR_OPTIONSCREEN
)))
791 name
= folder_is_nick((char *) folders
->name
,
792 FOLDERS(fs
.context
), 0);
795 name
= (char *) folders
->name
;
797 if(context_isambig(name
) &&
798 !(flags
& (FOR_PATTERN
| FOR_OPTIONSCREEN
) &&
799 folder_is_nick(name
, FOLDERS(fs
.context
), 0))){
800 char path_in_context
[MAILTMPLEN
];
802 context_apply(path_in_context
, fs
.context
, name
,
803 sizeof(path_in_context
));
806 * We may still have a non-fully-qualified name. In the
807 * action case, that will be interpreted in the primary
808 * collection instead of as a local name.
809 * Qualify that name the same way we
810 * qualify it in match_pattern_folder_specific.
812 if(!(IS_REMOTE(path_in_context
) ||
813 path_in_context
[0] == '#')){
814 if(strlen(path_in_context
) < (MAILTMPLEN
/2)){
815 char tmp
[MAX(MAILTMPLEN
,NETMAXMBX
)];
818 t
= mailboxfile(tmp
, path_in_context
);
821 else{ /* the else part should never happen */
822 build_path(tmp_20k_buf
, ps_global
->ui
.homedir
,
823 path_in_context
, SIZEOF_20KBUF
);
824 rs
= cpystr(tmp_20k_buf
);
828 rs
= cpystr(path_in_context
);
833 free_strlist(&folders
);
836 else if(!(fs
.context
&& (fs
.context
->next
|| fs
.context
->prev
))
840 while((fs
.context
= context_screen(fs
.context
, &c_fcc_km
, 0)) != NULL
);
847 * offer screen with list of contexts to select and some sort
851 context_screen(CONTEXT_S
*start
, struct key_menu
*km
, int edit_config
)
853 /* If a list, let the user tell us what to do */
854 if(F_OFF(F_CMBND_FOLDER_DISP
, ps_global
)
855 && ps_global
->context_list
856 && ps_global
->context_list
->next
){
859 memset(&css
, 0, sizeof(CONT_SCR_S
));
860 css
.title
= _("COLLECTION LIST");
861 css
.print_string
= _("contexts");
863 css
.contexts
= &ps_global
->context_list
;
864 css
.help
.text
= h_collection_screen
;
865 css
.help
.title
= _("HELP FOR COLLECTION LIST");
867 css
.edit
= edit_config
;
870 * Use conf_scroll_screen to manage display/selection
873 return(context_select_screen(ps_global
, &css
, 0));
876 return(ps_global
->context_list
);
880 static struct headerentry headents_templ
[]={
881 /* TRANSLATORS: these are the headings for setting up a collection of
882 folders, PATH is a filesystem path, VIEW is sort of a technical
883 term that can be used to restrict the View to fewer folders */
884 {"Nickname : ", N_("Nickname"), h_composer_cntxt_nick
, 12, 0, NULL
,
885 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
886 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
887 {"Server : ", N_("Server"), h_composer_cntxt_server
, 12, 0, NULL
,
888 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
889 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
890 {"Path : ", N_("Path"), h_composer_cntxt_path
, 12, 0, NULL
,
891 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
892 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
893 {"View : ", N_("View"), h_composer_cntxt_view
, 12, 0, NULL
,
894 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
895 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
896 {NULL
, NULL
, NO_HELP
, 0, 0, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
897 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
}
906 context_edit_screen(struct pine
*ps
, char *func
, char *def_nick
,
907 char *def_serv
, char *def_path
, char *def_view
)
909 int editor_result
, i
, j
;
910 char servpart
[MAILTMPLEN
], new_cntxt
[MAILTMPLEN
];
911 char pathpart
[MAILTMPLEN
], allbutnick
[MAILTMPLEN
];
912 char tmp
[MAILTMPLEN
], *nick
, *serv
, *path
, *view
,
913 *return_cntxt
= NULL
, *val
, *p
;
914 char nickpmt
[100], servpmt
[100], pathpmt
[100], viewpmt
[100];
920 standard_picobuf_setup(&pbf
);
921 pbf
.pine_flags
|= P_NOBODY
;
922 pbf
.exittest
= exit_collection_add
;
923 pbf
.canceltest
= (func
&& !strucmp(func
, "EDIT")) ? cancel_collection_edit
924 : cancel_collection_add
;
925 snprintf(tmp
, sizeof(tmp
), _("FOLDER COLLECTION %s"), func
);
926 tmp
[sizeof(tmp
)-1] = '\0';
927 pbf
.pine_anchor
= set_titlebar(tmp
, ps_global
->mail_stream
,
928 ps_global
->context_current
,
929 ps_global
->cur_folder
,ps_global
->msgmap
,
930 0, FolderName
, 0, 0, NULL
);
932 /* An informational message */
933 if((msgso
= so_get(PicoText
, NULL
, EDIT_ACCESS
)) != NULL
){
934 pbf
.msgtext
= (void *) so_text(msgso
);
936 _("\n Fill in the fields above to add a Folder Collection to your"));
938 _("\n COLLECTION LIST screen."));
940 _("\n Use the \"^G\" command to get help specific to each item, and"));
942 _("\n use \"^X\" when finished."));
946 pbf
.headents
= (struct headerentry
*)fs_get((sizeof(headents_templ
)
947 /sizeof(struct headerentry
))
948 * sizeof(struct headerentry
));
949 memset((void *) pbf
.headents
, 0,
950 (sizeof(headents_templ
)/sizeof(struct headerentry
))
951 * sizeof(struct headerentry
));
953 for(i
= 0; headents_templ
[i
].prompt
; i
++)
954 pbf
.headents
[i
] = headents_templ
[i
];
956 indent
= utf8_width(_("Nickname")) + 2;
958 nick
= cpystr(def_nick
? def_nick
: "");
959 pbf
.headents
[AC_NICK
].realaddr
= &nick
;
960 pbf
.headents
[AC_NICK
].maxlen
= strlen(nick
);
961 utf8_snprintf(nickpmt
, sizeof(nickpmt
), "%-*.*w: ", indent
, indent
, _("Nickname"));
962 pbf
.headents
[AC_NICK
].prompt
= nickpmt
;
963 pbf
.headents
[AC_NICK
].prwid
= indent
+2;
965 serv
= cpystr(def_serv
? def_serv
: "");
966 pbf
.headents
[AC_SERV
].realaddr
= &serv
;
967 pbf
.headents
[AC_SERV
].maxlen
= strlen(serv
);
968 utf8_snprintf(servpmt
, sizeof(servpmt
), "%-*.*w: ", indent
, indent
, _("Server"));
969 pbf
.headents
[AC_SERV
].prompt
= servpmt
;
970 pbf
.headents
[AC_SERV
].prwid
= indent
+2;
972 path
= cpystr(def_path
? def_path
: "");
973 pbf
.headents
[AC_PATH
].realaddr
= &path
;
974 pbf
.headents
[AC_PATH
].maxlen
= strlen(path
);
975 pbf
.headents
[AC_PATH
].bldr_private
= (void *) 0;
976 utf8_snprintf(pathpmt
, sizeof(pathpmt
), "%-*.*w: ", indent
, indent
, _("Path"));
977 pbf
.headents
[AC_PATH
].prompt
= pathpmt
;
978 pbf
.headents
[AC_PATH
].prwid
= indent
+2;
980 view
= cpystr(def_view
? def_view
: "");
981 pbf
.headents
[AC_VIEW
].realaddr
= &view
;
982 pbf
.headents
[AC_VIEW
].maxlen
= strlen(view
);
983 utf8_snprintf(viewpmt
, sizeof(viewpmt
), "%-*.*w: ", indent
, indent
, _("View"));
984 pbf
.headents
[AC_VIEW
].prompt
= viewpmt
;
985 pbf
.headents
[AC_VIEW
].prwid
= indent
+2;
988 * If this is new context, setup to query IMAP server
989 * for location of personal namespace.
991 if(!(def_nick
|| def_serv
|| def_path
|| def_view
)){
992 pbf
.headents
[AC_SERV
].builder
= build_namespace
;
993 pbf
.headents
[AC_SERV
].affected_entry
= &pbf
.headents
[AC_PATH
];
994 pbf
.headents
[AC_SERV
].bldr_private
= (void *) 0;
997 /* pass to pico and let user change them */
998 editor_result
= pico(&pbf
);
1000 if(editor_result
& COMP_GOTHUP
){
1004 fix_windsize(ps_global
);
1008 if(editor_result
& COMP_CANCEL
){
1009 cmd_cancelled(func
);
1011 else if(editor_result
& COMP_EXIT
){
1012 servpart
[0] = pathpart
[0] = new_cntxt
[0] = allbutnick
[0] = '\0';
1014 if(serv
[0] == '{' && serv
[strlen(serv
)-1] == '}'){
1015 strncpy(servpart
, serv
, sizeof(servpart
)-1);
1016 servpart
[sizeof(servpart
)-1] = '\0';
1019 snprintf(servpart
, sizeof(servpart
), "{%s}", serv
);
1021 if(mail_valid_net_parse(servpart
, &mb
)){
1022 if(!struncmp(mb
.service
, "nntp", 4)
1023 && (!path
|| strncmp(path
, "#news.", 6)))
1024 strncat(servpart
, "#news.", sizeof(servpart
)-1-strlen(servpart
));
1027 alpine_panic("Unexpected invalid server");
1032 servpart
[sizeof(servpart
)-1] = '\0';
1034 new_cntxt
[0] = '\0';
1036 val
= quote_if_needed(nick
);
1038 strncpy(new_cntxt
, val
, sizeof(new_cntxt
)-2);
1039 new_cntxt
[sizeof(new_cntxt
)-2] = '\0';
1041 fs_give((void **)&val
);
1043 strncat(new_cntxt
, " ", sizeof(new_cntxt
)-strlen(new_cntxt
)-1);
1044 new_cntxt
[sizeof(new_cntxt
)-1] = '\0';
1049 sstrncpy(&p
, servpart
, sizeof(allbutnick
)-1-(p
-allbutnick
));
1050 allbutnick
[sizeof(allbutnick
)-1] = '\0';
1053 val
= quote_brackets_if_needed(path
);
1055 strncpy(pathpart
, val
, sizeof(pathpart
)-1);
1056 pathpart
[sizeof(pathpart
)-1] = '\0';
1058 fs_give((void **)&val
);
1061 if(pbf
.headents
[AC_PATH
].bldr_private
!= (void *) 0){
1062 strncat(pathpart
, (char *) pbf
.headents
[AC_PATH
].bldr_private
,
1063 sizeof(pathpart
)-strlen(pathpart
)-1);
1064 pathpart
[sizeof(pathpart
)-1] = '\0';
1068 sstrncpy(&p
, pathpart
, sizeof(allbutnick
)-1-(p
-allbutnick
));
1069 allbutnick
[sizeof(allbutnick
)-1] = '\0';
1071 if(view
[0] != '[' && sizeof(allbutnick
)-1-(p
-allbutnick
) > 0){
1076 sstrncpy(&p
, view
, sizeof(allbutnick
)-1-(p
-allbutnick
));
1077 allbutnick
[sizeof(allbutnick
)-1] = '\0';
1078 if((j
=strlen(view
)) < 2 || (view
[j
-1] != ']' &&
1079 sizeof(allbutnick
)-1-(p
-allbutnick
) > 0)){
1084 val
= quote_if_needed(allbutnick
);
1086 strncat(new_cntxt
, val
, sizeof(new_cntxt
)-1-strlen(new_cntxt
));
1087 new_cntxt
[sizeof(new_cntxt
)-1] = '\0';
1089 if(val
!= allbutnick
)
1090 fs_give((void **)&val
);
1093 return_cntxt
= cpystr(new_cntxt
);
1096 for(i
= 0; headents_templ
[i
].prompt
; i
++)
1097 fs_give((void **) pbf
.headents
[i
].realaddr
);
1099 if(pbf
.headents
[AC_PATH
].bldr_private
!= (void *) 0)
1100 fs_give(&pbf
.headents
[AC_PATH
].bldr_private
);
1102 fs_give((void **) &pbf
.headents
);
1104 standard_picobuf_teardown(&pbf
);
1109 return(return_cntxt
);
1114 * Doubles up '[' and ']' characters and returns either
1115 * an allocated string or a pointer to the source string
1116 * if no quoting is needed.
1119 quote_brackets_if_needed(char *src
)
1121 char *step1
= NULL
, *step2
= NULL
, *ret
;
1125 if((strpbrk(src
, "[]") != NULL
)
1126 && ((step1
= add_escapes(src
, "[", '[', "", "")) != NULL
)
1127 && ((step2
= add_escapes(step1
, "]", ']', "", ""))))
1131 fs_give((void **) &step1
);
1138 * Call back for pico to prompt the user for exit confirmation
1140 * Returns: either NULL if the user accepts exit, or string containing
1141 * reason why the user declined.
1144 exit_collection_add(struct headerentry
*he
, void (*redraw_pico
)(void), int allow_flowed
,
1147 char prompt
[256], tmp
[MAILTMPLEN
], tmpnodel
[MAILTMPLEN
], *server
, *path
,
1148 delim
= '\0', *rstr
= NULL
, *p
;
1150 void (*redraw
)(void) = ps_global
->redrawer
;
1153 ps_global
->redrawer
= redraw_pico
;
1154 fix_windsize(ps_global
);
1156 server
= *he
[AC_SERV
].realaddr
;
1157 removing_trailing_white_space(server
);
1158 removing_leading_white_space(server
);
1160 path
= *he
[AC_PATH
].realaddr
;
1164 if(server
[0] == '{' && server
[strlen(server
)-1] == '}'){
1165 strncpy(tmp
, server
, sizeof(tmp
)-1);
1166 tmp
[sizeof(tmp
)-1] = '\0';
1168 else /* add them */ /* sizeof(tmp) == MAILTMPLEN */
1169 snprintf(tmp
, sizeof(tmp
), "{%.*s}", MAILTMPLEN
-3, server
);
1171 if(mail_valid_net_parse(tmp
, &mb
)){ /* news? verify namespace */
1172 if(!struncmp(mb
.service
, "nntp", 4) && strncmp(path
, "#news.", 6))
1173 strncat(tmp
, "#news.", sizeof(tmp
)-1-strlen(tmp
));
1176 rstr
= "Invalid Server entry";
1181 tmp
[sizeof(tmp
)-1] = '\0';
1185 * Delimiter acquisition below serves dual purposes. One's to
1186 * get the delimiter so we can make sure it's hanging off the end
1187 * of the path so we can test for the directory existence. The
1188 * second's to make sure the server (and any requested service)
1189 * name we were given exists. It should be handled by the folder
1190 * existence test further below, but it doesn't work with news...
1192 * Update. Now we are stripping the delimiter in the tmpnodel version
1193 * so that we can pass that to folder_exists. Cyrus does not answer
1194 * that the folder exists if we leave the trailing delimiter.
1197 strncat(tmp
, path
, sizeof(tmp
)-1-strlen(tmp
));
1198 tmp
[sizeof(tmp
)-1] = '\0';
1199 strncpy(tmpnodel
, tmp
, sizeof(tmpnodel
)-1);
1200 tmpnodel
[sizeof(tmpnodel
)-1] = '\0';
1202 if(he
[AC_PATH
].bldr_private
!= (void *) 0)
1203 fs_give(&he
[AC_PATH
].bldr_private
);
1205 ps_global
->mm_log_error
= 0;
1206 if((delim
= folder_delimiter(tmp
)) != '\0'){
1208 if(tmp
[(i
= strlen(tmp
)) - 1] == delim
)
1209 tmpnodel
[i
-1] = '\0';
1213 he
[AC_PATH
].bldr_private
= (void *) cpystr(&tmp
[i
]);
1217 else if(ps_global
->mm_log_error
&& ps_global
->last_error
)
1218 /* We used to bail, but this was changed with 4.10
1219 * as some users wanted to add clctn before the server
1220 * was actually around and built.
1222 flush_status_messages(0); /* mail_create gripes */
1224 dprint((1, "exit_col_test: No Server Hierarchy!\n"));
1230 || ((*(p
= tmp
) == '#'
1231 || (*tmp
== '{' && (p
= strchr(tmp
, '}')) && *++p
))
1232 && !struncmp(p
, "#news.", 6))
1233 || (*tmp
== '{' && (p
= strchr(tmp
, '}')) && !*++p
)){
1236 else if((i
= folder_exists(NULL
, tmpnodel
)) & FEX_ERROR
){
1237 if(!(rstr
= ps_global
->last_error
))
1238 rstr
= _("Problem testing for directory existence");
1241 exists
= (i
& FEX_ISDIR
);
1244 snprintf(prompt
, sizeof(prompt
), _("Exit and save changes"));
1246 snprintf(prompt
, sizeof(prompt
), _("Exit, saving changes and creating Path"));
1248 if(want_to(prompt
, 'y', 0, NO_HELP
, WT_NORM
) == 'y'){
1249 if(!exists
&& !pine_mail_create(NULL
, tmp
)){
1250 flush_status_messages(1); /* mail_create gripes */
1251 if(!(rstr
= ps_global
->last_error
))
1256 rstr
= _("Use ^C to abandon changes you've made");
1262 ps_global
->redrawer
= redraw
;
1263 return((rstr
== NULL
) ? 0 : 1);
1268 cancel_collection_add(void (*redraw_pico
)(void))
1270 return(cancel_collection_editing(_("Add"), redraw_pico
));
1275 cancel_collection_edit(void (*redraw_pico
)(void))
1277 return(cancel_collection_editing(_("Edit"), redraw_pico
));
1282 cancel_collection_editing(char *func
, void (*redraw_pico
)(void))
1285 void (*redraw
)(void) = ps_global
->redrawer
;
1286 static char rbuf
[20];
1288 #define CCA_PROMPT \
1289 _("Cancel Add (answering \"Yes\" will abandon any changes made) ")
1291 snprintf(prompt
, sizeof(prompt
), _("Cancel %s (answering \"Yes\" will abandon any changes made) "), func
? func
: "Add");
1292 snprintf(rbuf
, sizeof(rbuf
), _("%s Cancelled) "), func
? func
: "Add");
1294 ps_global
->redrawer
= redraw_pico
;
1295 fix_windsize(ps_global
);
1297 switch(want_to(prompt
, 'y', 'x', NO_HELP
, WT_NORM
)){
1307 ps_global
->redrawer
= redraw
;
1313 build_namespace(char *server
, char **server_too
, char **error
, BUILDER_ARG
*barg
, int *mangled
)
1318 NAMESPACE
***namespace;
1321 dprint((5, "- build_namespace - (%s)\n",
1322 server
? server
: "nul"));
1324 if(*barg
->me
){ /* only call this once! */
1326 *server_too
= cpystr(server
? server
: "");
1331 *barg
->me
= (void *) 1;
1333 if((p
= server
) != NULL
) /* empty string? */
1334 while(*p
&& isspace((unsigned char) *p
))
1339 *server_too
= cpystr(p
);
1341 len
= strlen(p
) + 2;
1342 name
= (char *) fs_get((len
+ 1) * sizeof(char));
1343 snprintf(name
, len
+1, "{%s}", p
);
1347 *server_too
= cpystr("");
1352 *mangled
|= BUILDER_SCREEN_MANGLED
;
1353 fix_windsize(ps_global
);
1357 we_cancel
= busy_cue(_("Fetching default directory"), NULL
, 1);
1359 if((stream
= pine_mail_open(NULL
, name
,
1360 OP_HALFOPEN
| OP_SILENT
| SP_USEPOOL
| SP_TEMPUSE
,
1362 if((namespace = mail_parameters(stream
, GET_NAMESPACE
, NULL
))
1363 && *namespace && (*namespace)[0]
1364 && (*namespace)[0]->name
&& (*namespace)[0]->name
[0]){
1366 fs_give((void **)&barg
->tptr
);
1368 barg
->tptr
= cpystr((*namespace)[0]->name
);
1371 pine_mail_close(stream
);
1375 cancel_busy_cue(-1);
1377 fs_give((void **) &name
);
1384 fl_val_gen (FOLDER_S
*f
, FSTATE_S
*fs
)
1386 return(f
&& FLDR_NAME(f
));
1391 fl_val_writable (FOLDER_S
*f
, FSTATE_S
*fs
)
1398 fl_val_subscribe (FOLDER_S
*f
, FSTATE_S
*fs
)
1401 q_status_message1(SM_ORDER
, 0, 4, _("Already subscribed to \"%s\""),
1410 /*----------------------------------------------------------------------
1411 Business end of displaying and operating on a collection of folders
1413 Args: ps -- The pine_state data structure
1414 fs -- Folder screen state structure
1416 Result: A string list containing folders selected,
1417 NULL on Cancel, Error or other problem
1421 folder_lister(struct pine
*ps
, FSTATE_S
*fs
)
1423 int fltrv
, we_cancel
= 0;
1424 int first_time_through
= 1;
1425 HANDLE_S
*handles
= NULL
;
1426 STORE_S
*screen_text
= NULL
;
1427 FPROC_S folder_proc_data
;
1430 dprint((1, "\n\n ---- FOLDER LISTER ----\n"));
1432 memset(&folder_proc_data
, 0, sizeof(FPROC_S
));
1433 folder_proc_data
.fs
= fs
;
1435 while(!folder_proc_data
.done
){
1436 if((screen_text
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
){
1437 gf_set_so_writec(&pc
, screen_text
);
1440 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
1441 "Formatting Error: Can't create space for list");
1445 we_cancel
= busy_cue(_("Fetching folder data"), NULL
, 1);
1446 fltrv
= folder_list_text(ps
, &folder_proc_data
, pc
, &handles
,
1447 ps
->ttyo
->screen_cols
);
1449 cancel_busy_cue(-1);
1455 struct key keys
[36];
1457 memset(&sargs
, 0, sizeof(SCROLL_S
));
1458 sargs
.text
.text
= so_text(screen_text
);
1459 sargs
.text
.src
= CharStar
;
1460 sargs
.text
.desc
= "folder list";
1461 if((sargs
.text
.handles
= folder_list_handle(fs
, handles
)) != NULL
)
1462 sargs
.start
.on
= Handle
;
1464 sargs
.bar
.title
= fs
->f
.title
.bar
;
1465 sargs
.bar
.style
= fs
->f
.title
.style
;
1467 sargs
.proc
.tool
= folder_processor
;
1468 sargs
.proc
.data
.p
= (void *) &folder_proc_data
;
1470 sargs
.quell_first_view
= first_time_through
? 0 : 1;
1471 first_time_through
= 0;
1473 sargs
.resize_exit
= 1;
1474 sargs
.vert_handle
= 1;
1475 sargs
.srch_handle
= 1;
1477 sargs
.help
.text
= fs
->f
.help
.text
;
1478 sargs
.help
.title
= fs
->f
.help
.title
;
1480 sargs
.keys
.menu
= &km
;
1483 memcpy(&keys
[0], fs
->km
->keys
,
1484 (km
.how_many
* 12) * sizeof(struct key
));
1485 setbitmap(sargs
.keys
.bitmap
);
1487 if(fs
->km
== &folder_km
){
1488 sargs
.keys
.each_cmd
= folder_lister_km_manager
;
1490 sargs
.mouse
.popup
= folder_list_popup
;
1495 sargs
.mouse
.popup
= folder_list_select_popup
;
1497 if(fs
->km
== &folder_sel_km
|| fs
->km
== &folder_sela_km
)
1498 sargs
.keys
.each_cmd
= folder_lister_km_sel_manager
;
1499 else if(fs
->km
== &folder_sub_km
)
1500 sargs
.keys
.each_cmd
= folder_lister_km_sub_manager
;
1503 sargs
.mouse
.clickclick
= folder_lister_clickclick
;
1505 switch(scrolltool(&sargs
)){
1506 case MC_MAIN
: /* just leave */
1507 folder_proc_data
.done
= 1;
1510 case MC_RESIZE
: /* loop around rebuilding screen */
1511 if(sargs
.text
.handles
){
1514 fp
= folder_entry(sargs
.text
.handles
->h
.f
.index
,
1515 FOLDERS(sargs
.text
.handles
->h
.f
.context
));
1516 if(fp
&& strlen(FLDR_NAME(fp
)) < MAXFOLDER
-1){
1517 strncpy(fs
->first_folder
, FLDR_NAME(fp
), MAXFOLDER
);
1518 fs
->first_folder
[MAXFOLDER
-1] = '\0';
1521 fs
->context
= sargs
.text
.handles
->h
.f
.context
;
1526 /*--------- EXIT menu -----------*/
1529 fs
->list_cntxt
= NULL
;
1530 folder_proc_data
.done
= folder_proc_data
.all_done
= 1;
1537 ps
->noticed_change_in_unseen
= 0;
1540 if(F_ON(F_BLANK_KEYMENU
,ps
))
1541 FOOTER_ROWS(ps
) = 1;
1543 gf_clear_so_writec(screen_text
);
1544 so_give(&screen_text
);
1545 free_handles(&handles
);
1548 folder_proc_data
.done
= 1;
1551 reset_context_folders(fs
->context
);
1553 if(folder_proc_data
.all_done
)
1556 if(fs
->cache_streamp
&& *fs
->cache_streamp
){
1560 * check stream pool to see if currently cached
1563 for(i
= 0; i
< ps
->s_pool
.nstream
; i
++)
1564 if(ps
->s_pool
.streams
[i
] == *fs
->cache_streamp
)
1566 if(i
== ps
->s_pool
.nstream
)
1567 *fs
->cache_streamp
= NULL
;
1570 return(folder_proc_data
.rv
);
1575 * folder_list_text - format collection's contents for display
1578 folder_list_text(struct pine
*ps
, FPROC_S
*fp
, gf_io_t pc
, HANDLE_S
**handlesp
, int cols
)
1580 int rv
= 1, i
, j
, ftotal
, fcount
, slot_width
, slot_rows
,
1581 slot_cols
, index
, findex
, width
, shown
, selected
;
1583 char lbuf
[6*MAX_SCREEN_COLS
+1];
1585 /* disarm this trigger that gets us out of scrolltool */
1586 ps
->noticed_change_in_unseen
= 0;
1589 init_handles(handlesp
);
1591 c_list
= fp
->fs
->context
;
1592 if(fp
->fs
->combined_view
1593 && (F_ON(F_CMBND_SUBDIR_DISP
, ps_global
) || !c_list
->dir
->prev
))
1594 while(c_list
->prev
) /* rewind to start */
1595 c_list
= c_list
->prev
;
1598 ps
->user_says_cancel
= 0;
1600 /* If we're displaying folders, fetch the list */
1601 if((shown
= (c_list
== fp
->fs
->context
1602 || (c_list
->dir
->status
& CNTXT_NOFIND
) == 0
1603 || F_ON(F_EXPANDED_FOLDERS
, ps_global
))) != 0){
1605 * if select is allowed, flag context so any that are
1606 * are remembered even after the list is destroyed
1609 c_list
->use
|= CNTXT_PRESRV
;
1611 /* Make sure folder list filled in */
1612 refresh_folder_list(c_list
, fp
->fs
->no_dirs
, FALSE
, fp
->fs
->cache_streamp
);
1615 /* Insert any introductory text here */
1618 || c_list
->dir
->prev
1619 || fp
->fs
->force_intro
){
1621 /* Leading horizontal line? */
1622 if(fp
->fs
->combined_view
1623 && (F_ON(F_CMBND_SUBDIR_DISP
,ps_global
)
1624 || !c_list
->dir
->prev
)){
1626 gf_puts("\n", pc
); /* blank line */
1628 color_write_for_folder(pc
, CLR_FLDRLT
);
1629 gf_puts(repeat_char(cols
, '-'), pc
);
1631 color_write_for_folder(pc
, CLR_NORMAL
);
1634 /* nickname or description */
1635 if(F_ON(F_CMBND_FOLDER_DISP
, ps_global
)
1636 && (!c_list
->dir
->prev
1637 || F_ON(F_CMBND_SUBDIR_DISP
, ps_global
))){
1638 char buf
[6*MAX_SCREEN_COLS
+ 1];
1640 color_write_for_folder(pc
, CLR_FLDRLT
);
1642 snprintf(buf
, sizeof(buf
), "%.*s", cols
,
1643 strsquish(tmp_20k_buf
, SIZEOF_20KBUF
,
1646 : (c_list
->label
? c_list
->label
: ""),
1648 gf_puts(folder_list_center_space(buf
, cols
), pc
);
1653 snprintf(buf
, sizeof(buf
), "%s-Collection <",
1654 NEWS_TEST(c_list
) ? "News" : "Folder");
1655 wid
= utf8_width(buf
)+1;
1656 wid
= MAX(0, cols
-wid
);
1657 snprintf(buf
+strlen(buf
), sizeof(buf
)-strlen(buf
), "%.*s>", wid
,
1658 strsquish(tmp_20k_buf
, SIZEOF_20KBUF
,
1661 : (c_list
->label
? c_list
->label
: ""),
1667 color_write_for_folder(pc
, CLR_NORMAL
);
1669 else if(c_list
->label
){
1670 if(utf8_width(c_list
->label
) > ps_global
->ttyo
->screen_cols
)
1671 utf8_pad_to_width(lbuf
, c_list
->label
, sizeof(lbuf
), ps_global
->ttyo
->screen_cols
, 1);
1673 strncpy(lbuf
, c_list
->label
, sizeof(lbuf
));
1675 lbuf
[sizeof(lbuf
)-1] = '\0';
1677 color_write_for_folder(pc
, CLR_FLDRLT
);
1678 gf_puts(folder_list_center_space(lbuf
, cols
), pc
);
1681 color_write_for_folder(pc
, CLR_NORMAL
);
1684 if(c_list
->comment
){
1685 if(utf8_width(c_list
->comment
) > ps_global
->ttyo
->screen_cols
)
1686 utf8_pad_to_width(lbuf
, c_list
->comment
, sizeof(lbuf
), ps_global
->ttyo
->screen_cols
, 1);
1688 strncpy(lbuf
, c_list
->comment
, sizeof(lbuf
));
1690 lbuf
[sizeof(lbuf
)-1] = '\0';
1692 color_write_for_folder(pc
, CLR_FLDRLT
);
1693 gf_puts(folder_list_center_space(lbuf
, cols
), pc
);
1696 color_write_for_folder(pc
, CLR_NORMAL
);
1699 if(c_list
->dir
->desc
){
1700 char buf
[6*MAX_SCREEN_COLS
+ 1];
1702 strncpy(buf
, strsquish(tmp_20k_buf
,SIZEOF_20KBUF
,c_list
->dir
->desc
,cols
),
1704 buf
[sizeof(buf
)-1] = '\0';
1705 color_write_for_folder(pc
, CLR_FLDRLT
);
1706 gf_puts(folder_list_center_space(buf
, cols
), pc
);
1709 color_write_for_folder(pc
, CLR_NORMAL
);
1712 if(c_list
->use
& CNTXT_ZOOM
){
1713 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "[ ZOOMED on %d (of %d) %ss ]",
1714 selected_folders(c_list
),
1715 folder_total(FOLDERS(c_list
)),
1716 (c_list
->use
& CNTXT_NEWS
) ? "Newsgroup" : "Folder");
1717 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
1719 if(utf8_width(tmp_20k_buf
) > ps_global
->ttyo
->screen_cols
)
1720 utf8_pad_to_width(lbuf
, tmp_20k_buf
, sizeof(lbuf
), ps_global
->ttyo
->screen_cols
, 1);
1722 strncpy(lbuf
, tmp_20k_buf
, sizeof(lbuf
));
1724 lbuf
[sizeof(lbuf
)-1] = '\0';
1726 color_write_for_folder(pc
, CLR_FLDRLT
);
1727 gf_puts(folder_list_center_space(lbuf
, cols
), pc
);
1730 color_write_for_folder(pc
, CLR_NORMAL
);
1733 color_write_for_folder(pc
, CLR_FLDRLT
);
1734 gf_puts(repeat_char(cols
, '-'), pc
);
1735 gf_puts("\n\n", pc
);
1736 color_write_for_folder(pc
, CLR_NORMAL
);
1739 if(shown
&& LUU_YES(c_list
->update
)){
1740 /* Run thru list formatting as necessary */
1741 if((ftotal
= folder_total(FOLDERS(c_list
))) != 0){
1743 /* If previously selected, mark members of new list */
1744 selected
= selected_folders(c_list
);
1746 /* How many screen cells per cell for each folder name? */
1748 for(fcount
= i
= 0; i
< ftotal
; i
++){
1749 FOLDER_S
*f
= folder_entry(i
, FOLDERS(c_list
));
1750 unsigned char *fname
;
1752 ps
->user_says_cancel
= 0;
1754 if((c_list
->use
& CNTXT_ZOOM
) && !f
->selected
)
1759 use_color
= use_color_for_folder(f
);
1760 fname
= folder_name_decoded((unsigned char *)FLDR_NAME(f
));
1762 width
= utf8_width(fname
? (char *)fname
: FLDR_NAME(f
));
1765 fs_give((void **)&fname
);
1768 width
+= (f
->isfolder
) ? (use_color
? 1 : 3) : (use_color
? 0 : 1);
1770 if(NEWS_TEST(c_list
) && (c_list
->use
& CNTXT_FINDALL
))
1771 /* assume listmode so we don't have to reformat */
1772 /* if listmode is actually entered */
1776 if(F_OFF(F_SELECTED_SHOWN_BOLD
, ps_global
))
1777 width
+= 4; /* " X " */
1779 else if(c_list
== fp
->fs
->list_cntxt
)
1780 width
+= 4; /* "[X] " */
1782 if(fp
->fs
->include_unseen_cnt
1783 && c_list
->use
& CNTXT_INCMNG
1784 && F_ON(F_ENABLE_INCOMING_CHECKING
, ps_global
)
1785 && ps_global
->VAR_INCOMING_FOLDERS
1786 && ps_global
->VAR_INCOMING_FOLDERS
[0]){
1788 update_folder_unseen(f
, c_list
, UFU_ANNOUNCE
, NULL
);
1789 if(F_ON(F_INCOMING_CHECKING_RECENT
, ps_global
)
1792 || F_ON(F_INCOMING_CHECKING_TOTAL
, ps_global
))){
1793 width
+= (strlen(tose(f
->new)) + 3);
1794 if(F_ON(F_INCOMING_CHECKING_TOTAL
, ps_global
))
1795 width
+= (strlen(tose(f
->total
)) + 1);
1797 else if(F_OFF(F_INCOMING_CHECKING_RECENT
, ps_global
)
1800 || F_ON(F_INCOMING_CHECKING_TOTAL
, ps_global
))){
1801 width
+= (strlen(tose(f
->unseen
)) + 3);
1802 if(F_ON(F_INCOMING_CHECKING_TOTAL
, ps_global
))
1803 width
+= (strlen(tose(f
->total
)) + 1);
1805 else if(!f
->unseen_valid
&& f
->last_unseen_update
!= LUU_NEVERCHK
)
1806 width
+= 4; /* " (?)" */
1809 if(slot_width
< width
)
1813 if(F_ON(F_SINGLE_FOLDER_LIST
, ps_global
)){
1818 /* fit as many columns as possible */
1820 while(((slot_cols
+1) * slot_width
) + slot_cols
<= cols
)
1834 * Make the slot_width as large as possible.
1835 * Slot_width is the width of the column, not counting
1836 * the space between columns.
1838 while((slot_cols
* (slot_width
+1)) + slot_cols
-1 <= cols
)
1841 slot_rows
= (fcount
/ slot_cols
)
1842 + ((fcount
% slot_cols
) ? 1 : 0);
1847 for(i
= index
= 0; i
< slot_rows
; i
++){
1851 for(j
= width
= 0; j
< slot_cols
; j
++, index
++){
1853 gf_puts(repeat_char(slot_width
+ 1 - width
, ' '), pc
);
1857 if(F_ON(F_VERTICAL_FOLDER_LIST
, ps_global
))
1858 index
= i
+ (j
* slot_rows
);
1862 if(c_list
->use
& CNTXT_ZOOM
)
1863 findex
= folder_list_ith(index
, c_list
);
1865 if(findex
< ftotal
){
1866 int flags
= (handlesp
) ? FLW_LUNK
: FLW_NONE
;
1868 if(c_list
== fp
->fs
->list_cntxt
)
1872 else if(F_ON(F_SINGLE_FOLDER_LIST
, ps_global
)
1873 || ((c_list
->use
& CNTXT_FINDALL
)
1874 && NEWS_TEST(c_list
)))
1877 if(fp
->fs
->include_unseen_cnt
1878 && c_list
->use
& CNTXT_INCMNG
1879 && F_ON(F_ENABLE_INCOMING_CHECKING
, ps_global
)
1880 && ps_global
->VAR_INCOMING_FOLDERS
1881 && ps_global
->VAR_INCOMING_FOLDERS
[0])
1882 flags
|= FLW_UNSEEN
;
1884 width
= folder_list_write(pc
, handlesp
, c_list
,
1885 findex
, NULL
, flags
);
1890 else if(fp
->fs
->combined_view
1891 && (F_ON(F_CMBND_SUBDIR_DISP
, ps_global
)
1892 || !c_list
->dir
->prev
)){
1893 char *emptiness
= N_("[No Folders in Collection]");
1895 if(utf8_width(_(emptiness
)) > ps_global
->ttyo
->screen_cols
)
1896 utf8_pad_to_width(lbuf
, _(emptiness
), sizeof(lbuf
), ps_global
->ttyo
->screen_cols
, 1);
1898 strncpy(lbuf
, _(emptiness
), sizeof(lbuf
));
1900 lbuf
[sizeof(lbuf
)-1] = '\0';
1902 color_write_for_folder(pc
, CLR_FLDRLT
);
1903 gf_puts(folder_list_center_space(lbuf
, cols
), pc
);
1904 (void) folder_list_write(pc
, handlesp
, c_list
, -1, lbuf
,
1905 (handlesp
) ? FLW_LUNK
: FLW_NONE
);
1906 color_write_for_folder(pc
, CLR_NORMAL
);
1909 else if(fp
->fs
->combined_view
1910 && (F_ON(F_CMBND_SUBDIR_DISP
, ps_global
)
1911 || !c_list
->dir
->prev
)){
1912 char *unexpanded
= N_("[Select Here to See Expanded List]");
1914 if(utf8_width(_(unexpanded
)) > ps_global
->ttyo
->screen_cols
)
1915 utf8_pad_to_width(lbuf
, _(unexpanded
), sizeof(lbuf
), ps_global
->ttyo
->screen_cols
, 1);
1917 strncpy(lbuf
, _(unexpanded
), sizeof(lbuf
));
1919 lbuf
[sizeof(lbuf
)-1] = '\0';
1921 color_write_for_folder(pc
, CLR_FLDRLT
);
1922 gf_puts(folder_list_center_space(lbuf
, cols
), pc
);
1923 (void) folder_list_write(pc
, handlesp
, c_list
, -1, lbuf
,
1924 (handlesp
) ? FLW_LUNK
: FLW_NONE
);
1925 color_write_for_folder(pc
, CLR_NORMAL
);
1928 gf_puts("\n", pc
); /* blank line */
1931 while(fp
->fs
->combined_view
1932 && (F_ON(F_CMBND_SUBDIR_DISP
, ps_global
) || !c_list
->dir
->prev
)
1933 && (c_list
= c_list
->next
));
1940 folder_list_write(gf_io_t pc
, HANDLE_S
**handlesp
, CONTEXT_S
*ctxt
, int fnum
, char *alt_name
, int flags
)
1943 int width
= 0, lprefix
= 0, lmiddle
= 0, lsuffix
= 0;
1945 HANDLE_S
*h1
= NULL
, *h2
= NULL
;
1947 if(flags
& FLW_LUNK
){
1948 h1
= new_handle(handlesp
);
1950 h1
->h
.f
.index
= fnum
;
1951 h1
->h
.f
.context
= ctxt
;
1952 h1
->force_display
= 1;
1954 snprintf(buf
, sizeof(buf
), "%d", h1
->key
);
1955 buf
[sizeof(buf
)-1] = '\0';
1958 fp
= (fnum
< 0) ? NULL
: folder_entry(fnum
, FOLDERS(ctxt
));
1960 if(flags
& FLW_LUNK
&& h1
&& fp
&& fp
->isdir
&& fp
->isfolder
){
1961 h2
= new_handle(handlesp
);
1963 h2
->h
.f
.index
= fnum
;
1964 h2
->h
.f
.context
= ctxt
;
1965 h2
->force_display
= 1;
1967 h1
->is_dual_do_open
= 1;
1972 if(color_monitored(fp
, flags
, CLR_UNSEEN
)){
1973 h1
->color_unseen
= 1;
1975 h2
->color_unseen
= 1;
1978 if(color_monitored(fp
, flags
, CLR_FOLDER
)){
1979 h1
->color_folder
= 1;
1981 h2
->color_folder
= 1;
1985 /* embed handle pointer */
1986 if(((h1
&& h1
->color_unseen
) ? color_write_for_folder(pc
, CLR_UNSEEN
)
1987 : ((h1
&& h1
->color_folder
)
1988 ? color_write_for_folder(pc
, fp
->isfolder
? CLR_FOLDER
: CLR_DIRECT
)
1990 && (h1
? ((*pc
)(TAG_EMBED
) && (*pc
)(TAG_HANDLE
)
1991 && (*pc
)(strlen(buf
)) && gf_puts(buf
, pc
)) : 1)
1992 && (fp
? ((lprefix
= folder_list_write_prefix(fp
, flags
, pc
)) >= 0
1993 && (lmiddle
= folder_list_write_middle(fp
, ctxt
, pc
, h2
)) >= 0
1994 && ((lsuffix
= folder_list_write_suffix(fp
, flags
, pc
)) >= 0))
1995 : (alt_name
? gf_puts(alt_name
, pc
) : 0))
1996 && (h1
? ((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDOFF
)
1997 && (*pc
)(TAG_EMBED
) && (*pc
)(TAG_INVOFF
)) : 1)
1998 && ((h1
&& (h1
->color_unseen
|| h1
->color_folder
)) ?
1999 color_write_for_folder(pc
, CLR_NORMAL
): 1)){
2001 width
= lprefix
+ lmiddle
+ lsuffix
;
2003 width
= utf8_width(alt_name
);
2011 folder_list_write_prefix(FOLDER_S
*f
, int flags
, gf_io_t pc
)
2015 if(flags
& FLW_SLCT
){
2016 if(F_OFF(F_SELECTED_SHOWN_BOLD
, ps_global
) || !(flags
& FLW_LUNK
)){
2026 rv
= ((*pc
)(TAG_EMBED
)
2027 && (*pc
)((f
->selected
) ? TAG_BOLDON
: TAG_BOLDOFF
)) ? 0 : -1;
2029 else if(flags
& FLW_LIST
){
2031 /* screen width of "SUB " is 4 */
2032 gf_puts(f
->subscribed
? "SUB " : (f
->selected
? "[X] " : "[ ] "), pc
);
2039 folder_list_write_middle(FOLDER_S
*fp
, CONTEXT_S
*ctxt
, gf_io_t pc
, HANDLE_S
*h2
)
2041 int rv
= -1, use_color
;
2043 unsigned char *fname
;
2046 snprintf(buf
, sizeof(buf
), "%d", h2
->key
);
2047 buf
[sizeof(buf
)-1] = '\0';
2053 fname
= folder_name_decoded((unsigned char *)FLDR_NAME(fp
));
2054 use_color
= use_color_for_folder(fp
);
2056 if(gf_puts(fname
? (char *)fname
: FLDR_NAME(fp
), pc
)
2057 && (h2
? ((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDOFF
) /* tie off handle 1 */
2058 && (*pc
)(TAG_EMBED
) && (*pc
)(TAG_INVOFF
)) : 1)
2059 && ((use_color
&& fp
->isdir
&& fp
->isfolder
)
2060 ? color_write_for_folder(pc
, CLR_DIRECT
) : 1)
2061 && (h2
? ((*pc
)(TAG_EMBED
) && (*pc
)(TAG_HANDLE
) /* start handle 2 */
2062 && (*pc
)(strlen(buf
)) && gf_puts(buf
, pc
)) : 1)
2064 ? (fp
->isdir
&& fp
->isfolder
? (*pc
)(ctxt
->dir
->delim
) : 1)
2065 : (((fp
->isdir
&& fp
->isfolder
) ? (*pc
)('[') : 1)
2066 && ((fp
->isdir
) ? (*pc
)(ctxt
->dir
->delim
) : 1)
2067 && ((fp
->isdir
&& fp
->isfolder
) ? (*pc
)(']') : 1))){
2068 rv
= utf8_width(fname
? (char *)fname
: FLDR_NAME(fp
));
2070 rv
+= (fp
->isfolder
) ? (use_color
? 1 : 3) : (use_color
? 0 : 1);
2074 fs_give((void **)&fname
);
2081 folder_list_write_suffix(FOLDER_S
*f
, int flags
, gf_io_t pc
)
2085 if(flags
& FLW_UNSEEN
){
2089 if(F_ON(F_INCOMING_CHECKING_RECENT
, ps_global
)
2092 || (F_ON(F_INCOMING_CHECKING_TOTAL
, ps_global
)
2093 && f
->total
> 0L))){
2094 snprintf(buf
, sizeof(buf
), " (%s%s%s)",
2096 F_ON(F_INCOMING_CHECKING_TOTAL
, ps_global
) ? "/" : "",
2097 F_ON(F_INCOMING_CHECKING_TOTAL
, ps_global
) ? tose(f
->total
) : "");
2099 else if(F_OFF(F_INCOMING_CHECKING_RECENT
, ps_global
)
2102 || (F_ON(F_INCOMING_CHECKING_TOTAL
, ps_global
)
2103 && f
->total
> 0L))){
2104 snprintf(buf
, sizeof(buf
), " (%s%s%s)",
2106 F_ON(F_INCOMING_CHECKING_TOTAL
, ps_global
) ? "/" : "",
2107 F_ON(F_INCOMING_CHECKING_TOTAL
, ps_global
) ? tose(f
->total
) : "");
2109 else if(!f
->unseen_valid
&& f
->last_unseen_update
!= LUU_NEVERCHK
){
2110 snprintf(buf
, sizeof(buf
), " (?)");
2122 color_write_for_folder(gf_io_t pc
, int testtype
)
2125 if(!pico_usingcolor())
2129 rv
= gf_puts(color_embed(ps_global
->VAR_INCUNSEEN_FORE_COLOR
,
2130 ps_global
->VAR_INCUNSEEN_BACK_COLOR
), pc
);
2133 rv
= gf_puts(color_embed(ps_global
->VAR_FOLDER_FORE_COLOR
,
2134 ps_global
->VAR_FOLDER_BACK_COLOR
), pc
);
2137 rv
= gf_puts(color_embed(ps_global
->VAR_FOLDER_LIST_FORE_COLOR
,
2138 ps_global
->VAR_FOLDER_LIST_BACK_COLOR
), pc
);
2141 rv
= gf_puts(color_embed(ps_global
->VAR_DIRECTORY_FORE_COLOR
,
2142 ps_global
->VAR_DIRECTORY_BACK_COLOR
), pc
);
2145 rv
= gf_puts(color_embed(ps_global
->VAR_NORM_FORE_COLOR
,
2146 ps_global
->VAR_NORM_BACK_COLOR
), pc
);
2157 color_test_for_folder(char *color_fore
, char *color_back
)
2159 return pico_usingcolor()
2160 && pico_is_good_color(color_fore
)
2161 && pico_is_good_color(color_back
)
2162 && (colorcmp(color_fore
, ps_global
->VAR_NORM_FORE_COLOR
)
2163 || colorcmp(color_back
,
2164 ps_global
->VAR_NORM_BACK_COLOR
)) ? 1 : 0;
2170 use_color_for_folder(FOLDER_S
*fp
)
2174 test1
= color_test_for_folder(ps_global
->VAR_DIRECTORY_FORE_COLOR
,
2175 ps_global
->VAR_DIRECTORY_BACK_COLOR
);
2177 test2
= color_test_for_folder(ps_global
->VAR_FOLDER_FORE_COLOR
,
2178 ps_global
->VAR_FOLDER_BACK_COLOR
);
2179 return (fp
->isdir
&& fp
->isfolder
) ? test1
+ test2
2180 : (fp
->isdir
? test1
: (fp
->isfolder
? test2
: 0));
2185 color_monitored(FOLDER_S
*f
, int flags
, int testtype
)
2189 case CLR_UNSEEN
: rv
= (flags
& FLW_UNSEEN
) && f
&& f
->unseen_valid
2190 && ((F_ON(F_INCOMING_CHECKING_RECENT
, ps_global
) && f
->new > 0L)
2191 || (F_OFF(F_INCOMING_CHECKING_RECENT
, ps_global
) && f
->unseen
> 0L))
2192 && color_test_for_folder(ps_global
->VAR_INCUNSEEN_FORE_COLOR
,
2193 ps_global
->VAR_INCUNSEEN_BACK_COLOR
) ? 1 : 0;
2195 case CLR_FOLDER
: rv
= f
? use_color_for_folder(f
) : 0;
2204 folder_list_ith(int n
, CONTEXT_S
*cntxt
)
2209 for(index
= 0, ftotal
= folder_total(FOLDERS(cntxt
));
2211 && (f
= folder_entry(index
, FOLDERS(cntxt
)))
2212 && !(f
->selected
&& !n
--);
2221 folder_list_center_space(char *s
, int width
)
2225 return(((l
= utf8_width(s
)) < width
) ? repeat_char((width
- l
)/2, ' ') : "");
2230 * folder_list_handle - return pointer in handle list
2231 * corresponding to "start"
2234 folder_list_handle(FSTATE_S
*fs
, HANDLE_S
*handles
)
2236 char *p
, *name
= NULL
;
2237 HANDLE_S
*h
, *h_found
= NULL
;
2240 if(handles
&& fs
->context
){
2241 if(!(NEWS_TEST(fs
->context
) || (fs
->context
->use
& CNTXT_INCMNG
))
2242 && fs
->context
->dir
->delim
)
2243 for(p
= strchr(fs
->first_folder
, fs
->context
->dir
->delim
);
2245 p
= strchr(p
, fs
->context
->dir
->delim
))
2248 for(h
= handles
; h
; h
= h
->next
)
2249 if(h
->h
.f
.context
== fs
->context
){
2250 if(!h_found
) /* match at least given context */
2253 if(!fs
->first_folder
[0]
2254 || ((fp
= folder_entry(h
->h
.f
.index
, FOLDERS(h
->h
.f
.context
)))
2255 && ((fs
->first_dir
&& fp
->isdir
)
2256 || (!fs
->first_dir
&& fp
->isfolder
))
2257 && ((fp
->nickname
&& !strcmp(name
? name
: fs
->first_folder
, fp
->nickname
))
2258 || (fp
->name
&& !strcmp(name
? name
: fs
->first_folder
, fp
->name
))))){
2264 fs
->first_folder
[0] = '\0';
2267 return(h_found
? h_found
: handles
);
2272 folder_processor(int cmd
, MSGNO_S
*msgmap
, SCROLL_S
*sparms
)
2275 char tmp_output
[MAILTMPLEN
];
2279 FPROC(sparms
)->done
= rv
= 1;;
2282 /*---------- Select or enter a View ----------*/
2284 rv
= folder_lister_choice(sparms
);
2287 /*--------- Hidden "To Fldrs" command -----------*/
2289 if(sparms
->text
.handles
2290 && folder_total(FOLDERS(sparms
->text
.handles
->h
.f
.context
))){
2291 if(!FPROC(sparms
)->fs
->list_cntxt
){
2292 FPROC(sparms
)->fs
->list_cntxt
2293 = sparms
->text
.handles
->h
.f
.context
;
2294 rv
= scroll_add_listmode(sparms
->text
.handles
->h
.f
.context
,
2295 folder_total(FOLDERS(sparms
->text
.handles
->h
.f
.context
)));
2297 /* need to set the subscribe key ourselves */
2298 sparms
->keys
.menu
->keys
[SB_SUB_KEY
].name
= "S";
2299 sparms
->keys
.menu
->keys
[SB_SUB_KEY
].label
= N_("Subscribe");
2300 sparms
->keys
.menu
->keys
[SB_SUB_KEY
].bind
.cmd
= MC_CHOICE
;
2301 sparms
->keys
.menu
->keys
[SB_SUB_KEY
].bind
.ch
[0] = 's';
2302 setbitn(SB_SUB_KEY
, sparms
->keys
.bitmap
);
2303 ps_global
->mangled_screen
= 1;
2305 sparms
->keys
.menu
->keys
[SB_EXIT_KEY
].bind
.cmd
= MC_EXITQUERY
;
2307 q_status_message(SM_ORDER
, 0, 1, LISTMODE_GRIPE
);
2310 q_status_message(SM_ORDER
, 0, 4, _("Already in List Mode"));
2313 q_status_message(SM_ORDER
, 0, 4,
2314 _("No Folders! Can't enter List Mode"));
2319 /*--------- Visit parent directory -----------*/
2321 if(folder_lister_parent(FPROC(sparms
)->fs
,
2322 (sparms
->text
.handles
)
2323 ? sparms
->text
.handles
->h
.f
.context
2324 : FPROC(sparms
)->fs
->context
,
2325 (sparms
->text
.handles
)
2326 ? sparms
->text
.handles
->h
.f
.index
: -1, 0))
2327 rv
= 1; /* leave scrolltool to rebuild screen */
2332 /*--------- Open the selected folder -----------*/
2334 if(sparms
->text
.handles
2335 && folder_total(FOLDERS(sparms
->text
.handles
->h
.f
.context
)))
2336 rv
= folder_lister_finish(sparms
, sparms
->text
.handles
->h
.f
.context
,
2337 sparms
->text
.handles
->h
.f
.index
);
2339 q_status_message(SM_ORDER
, 0, 4,
2340 _("No Folders! Nothing to View"));
2345 /*--------- Export the selected folder -----------*/
2347 folder_export(sparms
);
2351 /*--------- Import the selected folder -----------*/
2354 CONTEXT_S
*cntxt
= (sparms
->text
.handles
)
2355 ? sparms
->text
.handles
->h
.f
.context
2356 : FPROC(sparms
)->fs
->context
;
2357 char new_file
[2*MAXFOLDER
+10];
2362 r
= folder_import(sparms
, new_file
, sizeof(new_file
));
2364 if(r
&& (cntxt
->use
& CNTXT_INCMNG
|| context_isambig(new_file
))){
2365 rv
= 1; /* rebuild display! */
2366 FPROC(sparms
)->fs
->context
= cntxt
;
2367 if(strlen(new_file
) < MAXFOLDER
- 1){
2368 strncpy(FPROC(sparms
)->fs
->first_folder
, new_file
, MAXFOLDER
);
2369 FPROC(sparms
)->fs
->first_folder
[MAXFOLDER
-1] = '\0';
2373 ps_global
->mangled_footer
++;
2379 /*--------- Return to the Collections Screen -----------*/
2380 case MC_COLLECTIONS
:
2381 FPROC(sparms
)->done
= rv
= 1;
2385 /*--------- QUIT pine -----------*/
2387 ps_global
->next_screen
= quit_screen
;
2388 FPROC(sparms
)->done
= rv
= 1;
2392 /*--------- Compose -----------*/
2394 ps_global
->next_screen
= compose_screen
;
2395 FPROC(sparms
)->done
= rv
= 1;
2399 /*--------- Alt Compose -----------*/
2401 ps_global
->next_screen
= alt_compose_screen
;
2402 FPROC(sparms
)->done
= rv
= 1;
2406 /*--------- Message Index -----------*/
2409 && sp_viewing_a_thread(ps_global
->mail_stream
)
2410 && unview_thread(ps_global
, ps_global
->mail_stream
, ps_global
->msgmap
)){
2411 ps_global
->next_screen
= mail_index_screen
;
2412 ps_global
->view_skipped_index
= 0;
2413 ps_global
->mangled_screen
= 1;
2416 ps_global
->next_screen
= mail_index_screen
;
2417 FPROC(sparms
)->done
= rv
= 1;
2421 /*----------------- Add a new folder name -----------*/
2424 CONTEXT_S
*cntxt
= (sparms
->text
.handles
)
2425 ? sparms
->text
.handles
->h
.f
.context
2426 : FPROC(sparms
)->fs
->context
;
2427 char new_file
[2*MAXFOLDER
+10];
2430 if(NEWS_TEST(cntxt
))
2431 r
= group_subscription(new_file
, sizeof(new_file
), cntxt
);
2433 r
= add_new_folder(cntxt
, Main
, V_INCOMING_FOLDERS
, new_file
,
2435 FPROC(sparms
)->fs
->cache_streamp
2436 ? *FPROC(sparms
)->fs
->cache_streamp
: NULL
,
2438 if(ps_global
->prc
&& ps_global
->prc
->outstanding_pinerc_changes
)
2439 write_pinerc(ps_global
, Main
, WRP_NONE
);
2442 if(r
&& (cntxt
->use
& CNTXT_INCMNG
|| context_isambig(new_file
))){
2443 rv
= 1; /* rebuild display! */
2444 FPROC(sparms
)->fs
->context
= cntxt
;
2445 if(strlen(new_file
) < MAXFOLDER
- 1){
2446 strncpy(FPROC(sparms
)->fs
->first_folder
, new_file
, MAXFOLDER
);
2447 FPROC(sparms
)->fs
->first_folder
[MAXFOLDER
-1] = '\0';
2451 ps_global
->mangled_footer
++;
2457 /*------ Type in new folder name, e.g., for save ----*/
2459 rv
= folder_lister_addmanually(sparms
);
2463 /*--------------- Rename folder ----------------*/
2464 case MC_RENAMEFLDR
:
2465 if(sparms
->text
.handles
2466 && folder_total(FOLDERS(sparms
->text
.handles
->h
.f
.context
))){
2467 char new_file
[MAXFOLDER
+1];
2470 r
= rename_folder(sparms
->text
.handles
->h
.f
.context
,
2471 sparms
->text
.handles
->h
.f
.index
, new_file
,
2473 FPROC(sparms
)->fs
->cache_streamp
2474 ? *FPROC(sparms
)->fs
->cache_streamp
: NULL
);
2477 /* repaint, placing cursor on new folder! */
2479 if(context_isambig(new_file
)){
2480 FPROC(sparms
)->fs
->context
2481 = sparms
->text
.handles
->h
.f
.context
;
2482 if(strlen(new_file
) < MAXFOLDER
- 1){
2483 strncpy(FPROC(sparms
)->fs
->first_folder
, new_file
, MAXFOLDER
);
2484 FPROC(sparms
)->fs
->first_folder
[MAXFOLDER
-1] = '\0';
2489 ps_global
->mangled_footer
++;
2492 q_status_message(SM_ORDER
| SM_DING
, 0, 4,
2493 _("Empty folder collection. No folder to rename!"));
2498 /*-------------- Delete --------------------*/
2500 if(!(sparms
->text
.handles
2501 && folder_total(FOLDERS(sparms
->text
.handles
->h
.f
.context
)))){
2502 q_status_message(SM_ORDER
| SM_DING
, 0, 4,
2503 _("Empty folder collection. No folder to delete!"));
2506 char next_folder
[MAILTMPLEN
+1];
2508 next_folder
[0] = '\0';
2509 if(delete_folder(sparms
->text
.handles
->h
.f
.context
,
2510 sparms
->text
.handles
->h
.f
.index
,
2511 next_folder
, sizeof(next_folder
),
2512 FPROC(sparms
)->fs
->cache_streamp
2513 && *FPROC(sparms
)->fs
->cache_streamp
2514 ? FPROC(sparms
)->fs
->cache_streamp
: NULL
)){
2516 /* repaint, placing cursor on adjacent folder! */
2518 if(next_folder
[0] && strlen(next_folder
) < MAXFOLDER
- 1){
2519 strncpy(FPROC(sparms
)->fs
->first_folder
, next_folder
, MAXFOLDER
);
2520 FPROC(sparms
)->fs
->first_folder
[MAXFOLDER
-1] = '\0';
2523 sparms
->text
.handles
->h
.f
.context
->use
&= ~CNTXT_ZOOM
;
2526 ps_global
->mangled_footer
++;
2532 /*----------------- Shuffle incoming folder list -----------*/
2535 CONTEXT_S
*cntxt
= (sparms
->text
.handles
)
2536 ? sparms
->text
.handles
->h
.f
.context
: NULL
;
2538 if(!(cntxt
&& cntxt
->use
& CNTXT_INCMNG
))
2539 q_status_message(SM_ORDER
, 0, 4,
2540 _("May only shuffle Incoming-Folders."));
2541 else if(folder_total(FOLDERS(cntxt
)) == 0)
2542 q_status_message(SM_ORDER
, 0, 4,
2543 _("No folders to shuffle."));
2544 else if(folder_total(FOLDERS(cntxt
)) < 2)
2545 q_status_message(SM_ORDER
, 0, 4,
2546 _("Shuffle only makes sense with more than one folder."));
2548 if(FPROC(sparms
) && FPROC(sparms
)->fs
&&
2549 FPROC(sparms
)->fs
&& sparms
->text
.handles
&&
2550 sparms
->text
.handles
->h
.f
.index
>= 0 &&
2551 sparms
->text
.handles
->h
.f
.index
<
2552 folder_total(FOLDERS(cntxt
))){
2553 strncpy(FPROC(sparms
)->fs
->first_folder
,
2554 FLDR_NAME(folder_entry(sparms
->text
.handles
->h
.f
.index
,
2555 FOLDERS(cntxt
))), MAXFOLDER
);
2556 FPROC(sparms
)->fs
->first_folder
[MAXFOLDER
-1] = '\0';
2559 rv
= shuffle_incoming_folders(cntxt
,
2560 sparms
->text
.handles
->h
.f
.index
);
2567 /*-------------- Goto Folder Prompt --------------------*/
2571 CONTEXT_S
*c
= (sparms
->text
.handles
)
2572 ? sparms
->text
.handles
->h
.f
.context
2573 : FPROC(sparms
)->fs
->context
;
2574 char *new_fold
= broach_folder(-FOOTER_ROWS(ps_global
), 0, ¬realinbox
, &c
);
2576 if(ps_global
&& ps_global
->ttyo
){
2577 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
2578 ps_global
->mangled_footer
= 1;
2581 if(new_fold
&& do_broach_folder(new_fold
, c
, NULL
, notrealinbox
? 0L : DB_INBOXWOCNTXT
) > 0){
2582 ps_global
->next_screen
= mail_index_screen
;
2583 FPROC(sparms
)->done
= rv
= 1;
2586 ps_global
->mangled_footer
= 1;
2588 if((c
= ((sparms
->text
.handles
)
2589 ? sparms
->text
.handles
->h
.f
.context
2590 : FPROC(sparms
)->fs
->context
))->dir
->status
& CNTXT_NOFIND
)
2591 refresh_folder_list(c
, FPROC(sparms
)->fs
->no_dirs
, TRUE
, FPROC(sparms
)->fs
->cache_streamp
);
2597 /*------------- Print list of folders ---------*/
2599 print_folders(FPROC(sparms
));
2600 ps_global
->mangled_footer
++;
2604 /*----- Select the current folder, or toggle checkbox -----*/
2606 /*---------- Select set of folders ----------*/
2608 if(sparms
->text
.handles
2609 && folder_total(FOLDERS(sparms
->text
.handles
->h
.f
.context
))){
2610 if(cmd
== MC_SELCUR
){
2611 rv
= folder_select_toggle(sparms
->text
.handles
->h
.f
.context
,
2612 sparms
->text
.handles
->h
.f
.index
,
2613 (NEWS_TEST(sparms
->text
.handles
->h
.f
.context
)
2614 && FPROC(sparms
)->fs
->list_cntxt
)
2615 ? ng_scroll_edit
: folder_select_update
);
2616 if(!rv
) ps_global
->mangled_screen
= 1;
2619 switch(folder_select(ps_global
,
2620 sparms
->text
.handles
->h
.f
.context
,
2621 sparms
->text
.handles
->h
.f
.index
)){
2623 rv
= 1; /* rebuild screen */
2627 ps_global
->mangled_screen
++;
2631 if((sparms
->text
.handles
->h
.f
.context
->use
& CNTXT_ZOOM
)
2632 && !selected_folders(sparms
->text
.handles
->h
.f
.context
)){
2633 sparms
->text
.handles
->h
.f
.context
->use
&= ~CNTXT_ZOOM
;
2634 rv
= 1; /* make sure to redraw */
2637 if(rv
){ /* remember where to start */
2640 FPROC(sparms
)->fs
->context
= sparms
->text
.handles
->h
.f
.context
;
2641 if((fp
= folder_entry(sparms
->text
.handles
->h
.f
.index
,
2642 FOLDERS(sparms
->text
.handles
->h
.f
.context
))) != NULL
){
2643 if(strlen(FLDR_NAME(fp
)) < MAXFOLDER
- 1){
2644 strncpy(FPROC(sparms
)->fs
->first_folder
, FLDR_NAME(fp
), MAXFOLDER
);
2645 FPROC(sparms
)->fs
->first_folder
[MAXFOLDER
-1] = '\0';
2651 q_status_message(SM_ORDER
| SM_DING
, 0, 4,
2652 _("Empty folder collection. No folder to select!"));
2657 /*---------- Display folders ----------*/
2659 if(sparms
->text
.handles
2660 && folder_total(FOLDERS(sparms
->text
.handles
->h
.f
.context
))){
2664 if((n
= selected_folders(sparms
->text
.handles
->h
.f
.context
)) != 0){
2665 if(sparms
->text
.handles
->h
.f
.context
->use
& CNTXT_ZOOM
){
2666 sparms
->text
.handles
->h
.f
.context
->use
&= ~CNTXT_ZOOM
;
2667 q_status_message(SM_ORDER
, 0, 3,
2668 _("Folder List Zoom mode is now off"));
2671 q_status_message1(SM_ORDER
, 0, 3,
2672 _("In Zoomed list of %s folders. Use \"Z\" to restore regular list"),
2674 sparms
->text
.handles
->h
.f
.context
->use
|= CNTXT_ZOOM
;
2677 /* exit scrolltool to rebuild screen */
2680 /* Set where to start after it's rebuilt */
2681 FPROC(sparms
)->fs
->context
= sparms
->text
.handles
->h
.f
.context
;
2682 FPROC(sparms
)->fs
->first_folder
[0] = '\0';
2683 if((fp
= folder_entry(sparms
->text
.handles
->h
.f
.index
,
2684 FOLDERS(sparms
->text
.handles
->h
.f
.context
)))
2685 && !((sparms
->text
.handles
->h
.f
.context
->use
& CNTXT_ZOOM
)
2687 && strlen(FLDR_NAME(fp
)) < MAXFOLDER
- 1){
2688 strncpy(FPROC(sparms
)->fs
->first_folder
, FLDR_NAME(fp
), MAXFOLDER
);
2689 FPROC(sparms
)->fs
->first_folder
[MAXFOLDER
-1] = '\0';
2693 q_status_message(SM_ORDER
, 0, 3,
2694 _("No selected folders to Zoom on"));
2697 q_status_message(SM_ORDER
, 0, 4, _("No Folders to Zoom on!"));
2701 /*----- Ask user to abandon selection before exiting -----*/
2703 if(sparms
->text
.handles
2704 && FOLDERS(sparms
->text
.handles
->h
.f
.context
)){
2708 folder_n
= folder_total(FOLDERS(sparms
->text
.handles
->h
.f
.context
));
2710 for(i
= 0; i
< folder_n
; i
++){
2711 fp
= folder_entry(i
, FOLDERS(sparms
->text
.handles
->h
.f
.context
));
2716 if(i
< folder_n
/* some selections have been made */
2717 && want_to(_("Really abandon your selections "),
2718 'y', 'x', NO_HELP
, WT_NORM
) != 'y'){
2725 /*------------New Msg command --------------*/
2728 * Derived from code provided by
2729 * Rostislav Neplokh neplokh@andrew.cmu.edu and
2730 * Robert Siemborski (rjs3@andrew).
2732 if(sparms
->text
.handles
2733 && folder_total(FOLDERS(sparms
->text
.handles
->h
.f
.context
))){
2736 folder
= folder_entry(sparms
->text
.handles
->h
.f
.index
,
2737 FOLDERS(sparms
->text
.handles
->h
.f
.context
));
2740 strncpy(tmp_output
, _("Invalid Folder Name"), sizeof(tmp_output
)-1);
2741 tmp_output
[sizeof(tmp_output
)-1] = '\0';
2743 else if(folder
->isdir
&& !folder
->isfolder
){
2744 snprintf(tmp_output
, sizeof(tmp_output
), _("\"%s\" is a directory"), folder
->name
);
2747 char mailbox_name
[MAXPATH
+1];
2748 unsigned long tot
, rec
;
2751 context_apply(mailbox_name
,
2752 sparms
->text
.handles
->h
.f
.context
,
2753 folder
->name
, MAXPATH
+1);
2755 we_cancel
= busy_cue(NULL
, NULL
, 1);
2757 if(get_recent_in_folder(mailbox_name
, &rec
, NULL
, &tot
, NULL
))
2758 snprintf(tmp_output
, sizeof(tmp_output
),
2759 _("%lu total message%s, %lu of them recent"),
2760 tot
, plural(tot
), rec
);
2762 snprintf(tmp_output
, sizeof(tmp_output
),
2763 _("%s: Trouble checking for recent mail"), folder
->name
);
2766 cancel_busy_cue(-1);
2770 strncpy(tmp_output
, _("No folder to check! Can't get recent info"),
2771 sizeof(tmp_output
)-1);
2772 tmp_output
[sizeof(tmp_output
)-1] = '\0';
2775 q_status_message(SM_ORDER
, 0, 3, tmp_output
);
2779 /*--------------- Invalid Command --------------*/
2781 q_status_message1(SM_ORDER
, 0, 2, "fix this: cmd = %s", comatose(cmd
));
2790 folder_lister_clickclick(SCROLL_S
*sparms
)
2792 if(!FPROC(sparms
)->fs
->list_cntxt
)
2793 return(folder_lister_choice(sparms
));
2795 return(folder_processor(MC_SELCUR
, ps_global
->msgmap
, sparms
));
2799 folder_lister_choice(SCROLL_S
*sparms
)
2801 int rv
= 0, empty
= 0;
2802 int index
= (sparms
->text
.handles
)
2803 ? sparms
->text
.handles
->h
.f
.index
: 0;
2804 CONTEXT_S
*cntxt
= (sparms
->text
.handles
)
2805 ? sparms
->text
.handles
->h
.f
.context
: NULL
;
2809 FPROC(sparms
)->fs
->context
= cntxt
;
2811 if(!LUU_YES(cntxt
->update
)){
2812 cntxt
->update
= LUU_INIT
;
2813 refresh_folder_list(cntxt
, FPROC(sparms
)->fs
->no_dirs
, TRUE
,
2814 FPROC(sparms
)->fs
->cache_streamp
);
2815 if(!LUU_YES(cntxt
->update
))
2816 q_status_message(SM_ORDER
| SM_DING
, 3, 3, _("Collection not updated"));
2823 if(index
< 0 || (cntxt
->dir
->status
& CNTXT_NOFIND
)){
2824 rv
= 1; /* leave scrolltool to rebuild screen */
2825 FPROC(sparms
)->fs
->context
= cntxt
;
2826 FPROC(sparms
)->fs
->first_folder
[0] = '\0';
2828 else if(folder_total(FOLDERS(cntxt
))){
2829 if(folder_lister_select(FPROC(sparms
)->fs
, cntxt
, index
,
2830 sparms
->text
.handles
?
2831 sparms
->text
.handles
->is_dual_do_open
: 0)){
2832 rv
= 1; /* leave scrolltool to rebuild screen */
2834 else if(FPROC(sparms
)->fs
->list_cntxt
== cntxt
){
2835 int n
= 0, i
, folder_n
;
2837 STRLIST_S
*sl
= NULL
, **slp
;
2839 /* Scan folder list for selected items */
2840 folder_n
= folder_total(FOLDERS(cntxt
));
2842 for(i
= 0; i
< folder_n
; i
++){
2843 fp
= folder_entry(i
, FOLDERS(cntxt
));
2846 if((*FPROC(sparms
)->fs
->f
.valid
)(fp
,
2847 FPROC(sparms
)->fs
)){
2848 *slp
= new_strlist(NULL
);
2849 (*slp
)->name
= folder_lister_fullname(
2850 FPROC(sparms
)->fs
, FLDR_NAME(fp
));
2852 slp
= &(*slp
)->next
;
2861 if((FPROC(sparms
)->rv
= sl
) != NULL
)
2862 FPROC(sparms
)->done
= rv
= 1;
2864 q_status_message(SM_ORDER
, 0, 1, LISTMODE_GRIPE
);
2867 rv
= folder_lister_finish(sparms
, cntxt
, index
);
2876 q_status_message(SM_ORDER
| SM_DING
, 3, 3, _("Empty folder list!"));
2883 folder_lister_finish(SCROLL_S
*sparms
, CONTEXT_S
*cntxt
, int index
)
2885 FOLDER_S
*f
= folder_entry(index
, FOLDERS(cntxt
));
2888 if((*FPROC(sparms
)->fs
->f
.valid
)(f
, FPROC(sparms
)->fs
)){
2890 * Package up the selected folder names and return...
2892 FPROC(sparms
)->fs
->context
= cntxt
;
2893 FPROC(sparms
)->rv
= new_strlist(NULL
);
2894 FPROC(sparms
)->rv
->name
= folder_lister_fullname(FPROC(sparms
)->fs
,
2896 FPROC(sparms
)->done
= rv
= 1;
2904 * This is so that when you Save and use ^T to go to the folder list, and
2905 * you're in a directory with no folders, you have a way to add a new
2906 * folder there. The add actually gets done by the caller. This is just a
2907 * way to let the user type in the name.
2910 folder_lister_addmanually(SCROLL_S
*sparms
)
2912 int rc
, flags
= OE_APPEND_CURRENT
, cnt
= 0, rv
= 0;
2913 char addname
[MAXFOLDER
+1];
2915 CONTEXT_S
*cntxt
= (sparms
->text
.handles
)
2916 ? sparms
->text
.handles
->h
.f
.context
2917 : FPROC(sparms
)->fs
->context
;
2920 * Get the foldername from the user.
2925 rc
= optionally_enter(addname
, -FOOTER_ROWS(ps_global
), 0,
2926 sizeof(addname
), _("Name of new folder : "),
2927 NULL
, help
, &flags
);
2928 removing_leading_and_trailing_white_space(addname
);
2931 help
= (help
== NO_HELP
) ? h_save_addman
: NO_HELP
;
2935 if(F_OFF(F_ENABLE_DOT_FOLDERS
,ps_global
) && *addname
== '.'){
2937 q_status_message(SM_ORDER
,3,3,
2938 _("Folder name can't begin with dot"));
2940 q_status_message1(SM_ORDER
,3,3,
2941 _("Config feature \"%s\" enables names beginning with dot"),
2942 pretty_feature_name(feature_list_name(F_ENABLE_DOT_FOLDERS
), -1));
2944 display_message(NO_OP_COMMAND
);
2947 else if(!strucmp(addname
, ps_global
->inbox_name
)){
2948 q_status_message1(SM_ORDER
, 3, 3,
2949 _("Can't add folder named %s"),
2950 ps_global
->inbox_name
);
2959 FPROC(sparms
)->fs
->context
= cntxt
;
2960 FPROC(sparms
)->rv
= new_strlist(NULL
);
2961 FPROC(sparms
)->rv
->name
= folder_lister_fullname(FPROC(sparms
)->fs
,
2963 FPROC(sparms
)->done
= rv
= 1;
2971 folder_lister_km_manager(SCROLL_S
*sparms
, int handle_hidden
)
2975 /* if we're "in" a sub-directory, offer way out */
2976 if((sparms
->text
.handles
)
2977 ? sparms
->text
.handles
->h
.f
.context
->dir
->prev
2978 : FPROC(sparms
)->fs
->context
->dir
->prev
){
2981 * Leave the command characters alone and just change
2982 * the labels and the bind.cmd for KM_COL_KEY.
2983 * Also, leave KM_MAIN_KEY alone instead of trying to
2984 * turn it off in the else clause when it is redundant.
2986 sparms
->keys
.menu
->keys
[KM_COL_KEY
].label
= N_("ParentDir");
2987 sparms
->keys
.menu
->keys
[KM_COL_KEY
].bind
.cmd
= MC_PARENT
;
2989 else if((FPROC(sparms
)->fs
->context
->next
2990 || FPROC(sparms
)->fs
->context
->prev
)
2991 && !FPROC(sparms
)->fs
->combined_view
){
2992 sparms
->keys
.menu
->keys
[KM_COL_KEY
].label
= N_("ClctnList");
2993 sparms
->keys
.menu
->keys
[KM_COL_KEY
].bind
.cmd
= MC_EXIT
;
2996 sparms
->keys
.menu
->keys
[KM_COL_KEY
].label
= N_("Main Menu");
2997 sparms
->keys
.menu
->keys
[KM_COL_KEY
].bind
.cmd
= MC_MAIN
;
3000 if(F_OFF(F_ENABLE_AGG_OPS
,ps_global
)){
3001 clrbitn(KM_ZOOM_KEY
, sparms
->keys
.bitmap
);
3002 clrbitn(KM_SELECT_KEY
, sparms
->keys
.bitmap
);
3003 clrbitn(KM_SELCUR_KEY
, sparms
->keys
.bitmap
);
3006 if(sparms
->text
.handles
3007 && (fp
= folder_entry(sparms
->text
.handles
->h
.f
.index
,
3008 FOLDERS(sparms
->text
.handles
->h
.f
.context
)))){
3009 if(fp
->isdir
&& !sparms
->text
.handles
->is_dual_do_open
){
3010 sparms
->keys
.menu
->keys
[KM_SEL_KEY
].label
= "[" N_("View Dir") "]";
3011 menu_add_binding(sparms
->keys
.menu
, 'v', MC_CHOICE
);
3012 menu_add_binding(sparms
->keys
.menu
, ctrl('M'), MC_CHOICE
);
3013 menu_add_binding(sparms
->keys
.menu
, ctrl('J'), MC_CHOICE
);
3014 setbitn(KM_SEL_KEY
, sparms
->keys
.bitmap
);
3015 clrbitn(KM_ALTVIEW_KEY
, sparms
->keys
.bitmap
);
3016 clrbitn(KM_EXPORT_KEY
, sparms
->keys
.bitmap
);
3017 clrbitn(KM_IMPORT_KEY
, sparms
->keys
.bitmap
);
3020 sparms
->keys
.menu
->keys
[KM_SEL_KEY
].label
= "[" N_("View Fldr") "]";
3021 menu_add_binding(sparms
->keys
.menu
, 'v', MC_CHOICE
);
3022 menu_add_binding(sparms
->keys
.menu
, ctrl('M'), MC_CHOICE
);
3023 menu_add_binding(sparms
->keys
.menu
, ctrl('J'), MC_CHOICE
);
3024 setbitn(KM_SEL_KEY
, sparms
->keys
.bitmap
);
3025 clrbitn(KM_ALTVIEW_KEY
, sparms
->keys
.bitmap
);
3026 setbitn(KM_EXPORT_KEY
, sparms
->keys
.bitmap
);
3027 setbitn(KM_IMPORT_KEY
, sparms
->keys
.bitmap
);
3030 else if(FPROC(sparms
)->fs
->combined_view
3031 && sparms
->text
.handles
&& sparms
->text
.handles
->h
.f
.context
3032 && !sparms
->text
.handles
->h
.f
.context
->dir
->prev
){
3033 sparms
->keys
.menu
->keys
[KM_SEL_KEY
].label
= "[" N_("View Cltn") "]";
3034 menu_add_binding(sparms
->keys
.menu
, 'v', MC_CHOICE
);
3035 menu_add_binding(sparms
->keys
.menu
, ctrl('M'), MC_CHOICE
);
3036 menu_add_binding(sparms
->keys
.menu
, ctrl('J'), MC_CHOICE
);
3037 setbitn(KM_SEL_KEY
, sparms
->keys
.bitmap
);
3038 clrbitn(KM_ALTVIEW_KEY
, sparms
->keys
.bitmap
);
3039 clrbitn(KM_EXPORT_KEY
, sparms
->keys
.bitmap
);
3040 clrbitn(KM_IMPORT_KEY
, sparms
->keys
.bitmap
);
3043 clrbitn(KM_SEL_KEY
, sparms
->keys
.bitmap
);
3044 clrbitn(KM_ALTVIEW_KEY
, sparms
->keys
.bitmap
);
3045 clrbitn(KM_EXPORT_KEY
, sparms
->keys
.bitmap
);
3046 clrbitn(KM_IMPORT_KEY
, sparms
->keys
.bitmap
);
3049 if((sparms
->text
.handles
&&
3050 sparms
->text
.handles
->h
.f
.context
&&
3051 sparms
->text
.handles
->h
.f
.context
->use
& CNTXT_INCMNG
) ||
3052 (FPROC(sparms
) && FPROC(sparms
)->fs
&&
3053 FPROC(sparms
)->fs
->context
&&
3054 FPROC(sparms
)->fs
->context
->use
& CNTXT_INCMNG
))
3055 setbitn(KM_SHUFFLE_KEY
, sparms
->keys
.bitmap
);
3057 clrbitn(KM_SHUFFLE_KEY
, sparms
->keys
.bitmap
);
3059 if(F_ON(F_TAB_CHK_RECENT
, ps_global
)){
3060 menu_clear_binding(sparms
->keys
.menu
, TAB
);
3061 menu_init_binding(sparms
->keys
.menu
, TAB
, MC_CHK_RECENT
, "Tab",
3062 /* TRANSLATORS: New Messages */
3063 N_("NewMsgs"), KM_RECENT_KEY
);
3064 setbitn(KM_RECENT_KEY
, sparms
->keys
.bitmap
);
3067 menu_clear_binding(sparms
->keys
.menu
, TAB
);
3068 menu_add_binding(sparms
->keys
.menu
, TAB
, MC_NEXT_HANDLE
);
3069 clrbitn(KM_RECENT_KEY
, sparms
->keys
.bitmap
);
3072 /* May have to "undo" what scrolltool "did" */
3073 if(F_ON(F_ARROW_NAV
, ps_global
)){
3074 if(F_ON(F_RELAXED_ARROW_NAV
, ps_global
)){
3075 menu_clear_binding(sparms
->keys
.menu
, KEY_LEFT
);
3076 menu_add_binding(sparms
->keys
.menu
, KEY_LEFT
, MC_PREV_HANDLE
);
3077 menu_clear_binding(sparms
->keys
.menu
, KEY_RIGHT
);
3078 menu_add_binding(sparms
->keys
.menu
, KEY_RIGHT
, MC_NEXT_HANDLE
);
3081 menu_clear_binding(sparms
->keys
.menu
, KEY_UP
);
3082 menu_add_binding(sparms
->keys
.menu
, KEY_UP
, MC_PREV_HANDLE
);
3083 menu_clear_binding(sparms
->keys
.menu
, KEY_DOWN
);
3084 menu_add_binding(sparms
->keys
.menu
, KEY_DOWN
, MC_NEXT_HANDLE
);
3091 folder_lister_km_sel_manager(SCROLL_S
*sparms
, int handle_hidden
)
3095 /* if we're "in" a sub-directory, offer way out */
3096 if((sparms
->text
.handles
)
3097 ? sparms
->text
.handles
->h
.f
.context
->dir
->prev
3098 : FPROC(sparms
)->fs
->context
->dir
->prev
){
3099 sparms
->keys
.menu
->keys
[FC_COL_KEY
].name
= "<";
3100 /* TRANSLATORS: go to parent directory one level up */
3101 sparms
->keys
.menu
->keys
[FC_COL_KEY
].label
= N_("ParentDir");
3102 sparms
->keys
.menu
->keys
[FC_COL_KEY
].bind
.cmd
= MC_PARENT
;
3103 sparms
->keys
.menu
->keys
[FC_COL_KEY
].bind
.ch
[0] = '<';
3104 sparms
->keys
.menu
->keys
[FC_COL_KEY
].bind
.ch
[1] = ',';
3105 if(F_ON(F_ARROW_NAV
,ps_global
)){
3106 sparms
->keys
.menu
->keys
[FC_COL_KEY
].bind
.nch
= 3;
3107 sparms
->keys
.menu
->keys
[FC_COL_KEY
].bind
.ch
[2] = KEY_LEFT
;
3110 sparms
->keys
.menu
->keys
[FC_COL_KEY
].bind
.nch
= 2;
3112 /* ExitSelect in position 1 */
3113 setbitn(FC_EXIT_KEY
, sparms
->keys
.bitmap
);
3115 else if((FPROC(sparms
)->fs
->context
->next
3116 || FPROC(sparms
)->fs
->context
->prev
)
3117 && !FPROC(sparms
)->fs
->combined_view
){
3118 sparms
->keys
.menu
->keys
[FC_COL_KEY
].name
= "<";
3119 /* TRANSLATORS: go to Collection List */
3120 sparms
->keys
.menu
->keys
[FC_COL_KEY
].label
= N_("ClctnList");
3121 sparms
->keys
.menu
->keys
[FC_COL_KEY
].bind
.cmd
= MC_COLLECTIONS
;
3122 sparms
->keys
.menu
->keys
[FC_COL_KEY
].bind
.ch
[0] = '<';
3123 sparms
->keys
.menu
->keys
[FC_COL_KEY
].bind
.ch
[1] = ',';
3124 if(F_ON(F_ARROW_NAV
,ps_global
)){
3125 sparms
->keys
.menu
->keys
[FC_COL_KEY
].bind
.nch
= 3;
3126 sparms
->keys
.menu
->keys
[FC_COL_KEY
].bind
.ch
[2] = KEY_LEFT
;
3129 sparms
->keys
.menu
->keys
[FC_COL_KEY
].bind
.nch
= 2;
3131 /* ExitSelect in position 1 */
3132 setbitn(FC_EXIT_KEY
, sparms
->keys
.bitmap
);
3134 else if(FPROC(sparms
)->fs
->combined_view
){
3136 * This can't be a menu_init_binding() because we don't want
3137 * to remove the ExitSelect command in position FC_EXIT_KEY.
3138 * We just turn it off until we need it again.
3140 sparms
->keys
.menu
->keys
[FC_COL_KEY
].name
= "E";
3141 sparms
->keys
.menu
->keys
[FC_COL_KEY
].label
= N_("ExitSelect");
3142 sparms
->keys
.menu
->keys
[FC_COL_KEY
].bind
.cmd
= MC_EXIT
;
3143 sparms
->keys
.menu
->keys
[FC_COL_KEY
].bind
.nch
= 1;
3144 sparms
->keys
.menu
->keys
[FC_COL_KEY
].bind
.ch
[0] = 'e';
3146 /* turn off ExitSelect in position 1, it's in 2 now */
3147 clrbitn(FC_EXIT_KEY
, sparms
->keys
.bitmap
);
3150 /* clean up per-entry bindings */
3151 clrbitn(FC_SEL_KEY
, sparms
->keys
.bitmap
);
3152 clrbitn(FC_ALTSEL_KEY
, sparms
->keys
.bitmap
);
3153 menu_clear_binding(sparms
->keys
.menu
, ctrl('M'));
3154 menu_clear_binding(sparms
->keys
.menu
, ctrl('J'));
3155 menu_clear_binding(sparms
->keys
.menu
, '>');
3156 menu_clear_binding(sparms
->keys
.menu
, '.');
3157 menu_clear_binding(sparms
->keys
.menu
, 's');
3158 if(F_ON(F_ARROW_NAV
,ps_global
))
3159 menu_clear_binding(sparms
->keys
.menu
, KEY_RIGHT
);
3161 /* and then re-assign them as needed */
3162 if(sparms
->text
.handles
3163 && (fp
= folder_entry(sparms
->text
.handles
->h
.f
.index
,
3164 FOLDERS(sparms
->text
.handles
->h
.f
.context
)))){
3165 setbitn(FC_SEL_KEY
, sparms
->keys
.bitmap
);
3167 sparms
->keys
.menu
->keys
[FC_SEL_KEY
].name
= ">";
3168 menu_add_binding(sparms
->keys
.menu
, '>', MC_CHOICE
);
3169 menu_add_binding(sparms
->keys
.menu
, '.', MC_CHOICE
);
3170 if(F_ON(F_ARROW_NAV
,ps_global
))
3171 menu_add_binding(sparms
->keys
.menu
, KEY_RIGHT
, MC_CHOICE
);
3174 sparms
->keys
.menu
->keys
[FC_SEL_KEY
].label
= N_("View Dir");
3175 setbitn(FC_ALTSEL_KEY
, sparms
->keys
.bitmap
);
3176 menu_add_binding(sparms
->keys
.menu
, 's', MC_OPENFLDR
);
3177 menu_add_binding(sparms
->keys
.menu
, ctrl('M'), MC_OPENFLDR
);
3178 menu_add_binding(sparms
->keys
.menu
, ctrl('J'), MC_OPENFLDR
);
3181 sparms
->keys
.menu
->keys
[FC_SEL_KEY
].label
= "[" N_("View Dir") "]";
3182 menu_add_binding(sparms
->keys
.menu
, ctrl('M'), MC_CHOICE
);
3183 menu_add_binding(sparms
->keys
.menu
, ctrl('J'), MC_CHOICE
);
3187 sparms
->keys
.menu
->keys
[FC_SEL_KEY
].name
= "S";
3188 sparms
->keys
.menu
->keys
[FC_SEL_KEY
].label
= "[" N_("Select") "]";
3190 menu_add_binding(sparms
->keys
.menu
, 's', MC_CHOICE
);
3191 menu_add_binding(sparms
->keys
.menu
, ctrl('M'), MC_CHOICE
);
3192 menu_add_binding(sparms
->keys
.menu
, ctrl('J'), MC_CHOICE
);
3195 if(F_ON(F_ARROW_NAV
,ps_global
))
3196 menu_add_binding(sparms
->keys
.menu
, KEY_RIGHT
, MC_CHOICE
);
3198 else if(FPROC(sparms
)->fs
->combined_view
3199 && sparms
->text
.handles
&& sparms
->text
.handles
->h
.f
.context
3200 && !sparms
->text
.handles
->h
.f
.context
->dir
->prev
){
3201 setbitn(FC_SEL_KEY
, sparms
->keys
.bitmap
);
3202 sparms
->keys
.menu
->keys
[FC_SEL_KEY
].name
= ">";
3203 sparms
->keys
.menu
->keys
[FC_SEL_KEY
].label
= "[" N_("View Cltn") "]";
3204 menu_add_binding(sparms
->keys
.menu
, '>', MC_CHOICE
);
3205 menu_add_binding(sparms
->keys
.menu
, '.', MC_CHOICE
);
3206 if(F_ON(F_ARROW_NAV
,ps_global
))
3207 menu_add_binding(sparms
->keys
.menu
, KEY_RIGHT
, MC_CHOICE
);
3209 menu_add_binding(sparms
->keys
.menu
, ctrl('M'), MC_CHOICE
);
3210 menu_add_binding(sparms
->keys
.menu
, ctrl('J'), MC_CHOICE
);
3213 /* May have to "undo" what scrolltool "did" */
3214 if(F_ON(F_ARROW_NAV
, ps_global
)){
3215 if(F_ON(F_RELAXED_ARROW_NAV
, ps_global
)){
3216 menu_clear_binding(sparms
->keys
.menu
, KEY_LEFT
);
3217 menu_add_binding(sparms
->keys
.menu
, KEY_LEFT
, MC_PREV_HANDLE
);
3218 menu_clear_binding(sparms
->keys
.menu
, KEY_RIGHT
);
3219 menu_add_binding(sparms
->keys
.menu
, KEY_RIGHT
, MC_NEXT_HANDLE
);
3222 menu_clear_binding(sparms
->keys
.menu
, KEY_UP
);
3223 menu_add_binding(sparms
->keys
.menu
, KEY_UP
, MC_PREV_HANDLE
);
3224 menu_clear_binding(sparms
->keys
.menu
, KEY_DOWN
);
3225 menu_add_binding(sparms
->keys
.menu
, KEY_DOWN
, MC_NEXT_HANDLE
);
3232 folder_lister_km_sub_manager(SCROLL_S
*sparms
, int handle_hidden
)
3235 * Folder_processor() also modifies the keymenu.
3237 if(FPROC(sparms
)->fs
->list_cntxt
){
3238 clrbitn(SB_LIST_KEY
, sparms
->keys
.bitmap
);
3239 sparms
->keys
.menu
->keys
[SB_SEL_KEY
].name
= "X";
3240 sparms
->keys
.menu
->keys
[SB_SEL_KEY
].label
= "[" N_("Set/Unset") "]";
3241 sparms
->keys
.menu
->keys
[SB_SEL_KEY
].bind
.cmd
= MC_SELCUR
;
3242 sparms
->keys
.menu
->keys
[SB_SEL_KEY
].bind
.ch
[0] = 'x';
3245 clrbitn(SB_SUB_KEY
, sparms
->keys
.bitmap
);
3246 sparms
->keys
.menu
->keys
[SB_SEL_KEY
].name
= "S";
3247 sparms
->keys
.menu
->keys
[SB_SEL_KEY
].label
= "[" N_("Subscribe") "]";
3248 sparms
->keys
.menu
->keys
[SB_SEL_KEY
].bind
.cmd
= MC_CHOICE
;
3249 sparms
->keys
.menu
->keys
[SB_SEL_KEY
].bind
.ch
[0] = 's';
3252 /* May have to "undo" what scrolltool "did" */
3253 if(F_ON(F_ARROW_NAV
, ps_global
)){
3254 if(F_ON(F_RELAXED_ARROW_NAV
, ps_global
)){
3255 menu_clear_binding(sparms
->keys
.menu
, KEY_LEFT
);
3256 menu_add_binding(sparms
->keys
.menu
, KEY_LEFT
, MC_PREV_HANDLE
);
3257 menu_clear_binding(sparms
->keys
.menu
, KEY_RIGHT
);
3258 menu_add_binding(sparms
->keys
.menu
, KEY_RIGHT
, MC_NEXT_HANDLE
);
3261 menu_clear_binding(sparms
->keys
.menu
, KEY_UP
);
3262 menu_add_binding(sparms
->keys
.menu
, KEY_UP
, MC_PREV_HANDLE
);
3263 menu_clear_binding(sparms
->keys
.menu
, KEY_DOWN
);
3264 menu_add_binding(sparms
->keys
.menu
, KEY_DOWN
, MC_NEXT_HANDLE
);
3272 folder_select(struct pine
*ps
, CONTEXT_S
*context
, int cur_index
)
3274 int i
, j
, n
, total
, old_tot
, diff
,
3275 q
= 0, rv
= 0, narrow
= 0;
3276 HelpType help
= NO_HELP
;
3279 static ESCKEY_S self_opts2
[] = {
3280 /* TRANSLATORS: keymenu descriptions, select all folders, current folder, select
3281 based on folder properties, or select based on text contents in folders */
3282 {'a', 'a', "A", N_("select All")},
3283 {'c', 'c', "C", N_("select Cur")},
3284 {'p', 'p', "P", N_("Properties")},
3285 {'t', 't', "T", N_("Text")},
3286 {-1, 0, NULL
, NULL
},
3289 extern ESCKEY_S sel_opts1
[];
3290 extern char *sel_pmt2
;
3293 f
= folder_entry(cur_index
, FOLDERS(context
));
3295 sel_opts
= self_opts2
;
3296 if((old_tot
= selected_folders(context
)) != 0){
3297 sel_opts1
[1].label
= f
->selected
? N_("unselect Cur") : N_("select Cur");
3298 sel_opts
+= 2; /* disable extra options */
3299 switch(q
= radio_buttons(SEL_ALTER_PMT
, -FOOTER_ROWS(ps_global
),
3300 sel_opts1
, 'c', 'x', help
, RB_NORM
)){
3301 case 'f' : /* flip selection */
3302 n
= folder_total(FOLDERS(context
));
3303 for(total
= i
= 0; i
< n
; i
++)
3304 if((f
= folder_entry(i
, FOLDERS(context
))) != NULL
)
3305 f
->selected
= !f
->selected
;
3307 return(1); /* repaint */
3309 case 'n' : /* narrow selection */
3311 case 'b' : /* broaden selection */
3312 q
= 0; /* but don't offer criteria prompt */
3313 if(context
->use
& CNTXT_INCMNG
){
3314 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
3315 _("Select \"%s\" not supported in Incoming Folders"),
3316 narrow
? "Narrow" : "Broaden");
3322 case 'c' : /* Un/Select Current */
3323 case 'a' : /* Unselect All */
3324 case 'x' : /* cancel */
3328 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
3329 _("Unsupported Select option"));
3334 if(context
->use
& CNTXT_INCMNG
&& F_ON(F_ENABLE_INCOMING_CHECKING
, ps_global
)){
3335 if(F_ON(F_INCOMING_CHECKING_RECENT
, ps_global
)){
3336 self_opts2
[N_RECENT
].ch
= 'r';
3337 self_opts2
[N_RECENT
].rval
= 'r';
3338 self_opts2
[N_RECENT
].name
= "R";
3339 self_opts2
[N_RECENT
].label
= N_("Recent");
3342 self_opts2
[N_RECENT
].ch
= 'u';
3343 self_opts2
[N_RECENT
].rval
= 'u';
3344 self_opts2
[N_RECENT
].name
= "U";
3345 self_opts2
[N_RECENT
].label
= N_("Unseen");
3349 self_opts2
[N_RECENT
].ch
= -1;
3350 self_opts2
[N_RECENT
].rval
= 0;
3351 self_opts2
[N_RECENT
].name
= NULL
;
3352 self_opts2
[N_RECENT
].label
= NULL
;
3356 q
= radio_buttons(sel_pmt2
, -FOOTER_ROWS(ps_global
),
3357 sel_opts
, 'c', 'x', help
, RB_NORM
);
3360 * NOTE: See note about MESSAGECACHE "searched" bits above!
3363 case 'x': /* cancel */
3364 cmd_cancelled("Select command");
3367 case 'c' : /* toggle current's selected state */
3368 return(folder_select_toggle(context
, cur_index
, folder_select_update
));
3370 case 'a' : /* select/unselect all */
3371 n
= folder_total(FOLDERS(context
));
3372 for(total
= i
= 0; i
< n
; i
++)
3373 folder_entry(i
, FOLDERS(context
))->selected
= old_tot
== 0;
3375 q_status_message4(SM_ORDER
, 0, 2,
3376 "%s%s folder%s %sselected",
3377 old_tot
? "" : "All ",
3378 comatose(old_tot
? old_tot
: n
),
3379 plural(old_tot
? old_tot
: n
), old_tot
? "UN" : "");
3382 case 't' : /* Text */
3383 if(!folder_select_text(ps
, context
, narrow
))
3388 case 'p' : /* Properties */
3389 if(!folder_select_props(ps
, context
, narrow
))
3395 n
= folder_total(FOLDERS(context
));
3396 for(i
= 0; i
< n
; i
++){
3397 f
= folder_entry(i
, FOLDERS(context
));
3398 if(f
->unseen_valid
&& f
->new > 0L)
3405 n
= folder_total(FOLDERS(context
));
3406 for(i
= 0; i
< n
; i
++){
3407 f
= folder_entry(i
, FOLDERS(context
));
3408 if(f
->unseen_valid
&& f
->unseen
> 0L)
3415 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
3416 _("Unsupported Select option"));
3423 /* rectify the scanned vs. selected folders */
3424 n
= folder_total(FOLDERS(context
));
3425 for(total
= i
= j
= 0; i
< n
; i
++)
3426 if(folder_entry(i
, FOLDERS(context
))->scanned
)
3430 * Any matches at all? If not, the selected set remains the same.
3431 * Note that when Narrowing, only matches in the intersection will
3432 * show up as scanned. We need to reset i to 0 to erase any already
3433 * selected messages which aren't in the intersection.
3436 for(i
= 0; i
< n
; i
++)
3437 if((f
= folder_entry(i
, FOLDERS(context
))) != NULL
){
3440 f
->selected
= f
->scanned
;
3448 if(!(diff
= (total
= selected_folders(context
)) - old_tot
)){
3450 q_status_message4(SM_ORDER
, 0, 2,
3451 "%s. %s folder%s remain%s selected.",
3452 j
? _("No change resulted")
3453 : _("No messages in intersection"),
3454 comatose(old_tot
), plural(old_tot
),
3455 (old_tot
== 1L) ? "s" : "");
3456 else if(old_tot
&& j
)
3457 q_status_message(SM_ORDER
, 0, 2,
3458 _("No change resulted. Matching folders already selected."));
3460 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
3461 "Select failed! No %sfolders selected.",
3462 old_tot
? "additional " : "");
3465 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,
3466 "Select matched %d folder%s. %s %sfolder%s %sselected.",
3467 (diff
> 0) ? diff
: old_tot
+ diff
,
3468 plural((diff
> 0) ? diff
: old_tot
+ diff
),
3469 comatose((diff
> 0) ? total
: -diff
),
3470 (diff
> 0) ? "total " : "",
3471 plural((diff
> 0) ? total
: -diff
),
3472 (diff
> 0) ? "" : "UN");
3473 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
3474 q_status_message(SM_ORDER
, 0, 2, tmp_20k_buf
);
3477 q_status_message2(SM_ORDER
, 0, 2, "Select matched %s folder%s.",
3478 comatose(diff
), plural(diff
));
3487 folder_lister_select(FSTATE_S
*fs
, CONTEXT_S
*context
, int index
, int is_dual_do_open
)
3491 FOLDER_S
*f
= folder_entry(index
, FOLDERS(context
));
3493 /*--- Entering a directory? ---*/
3494 if(f
->isdir
&& !is_dual_do_open
){
3495 fp
= next_folder_dir(context
, f
->name
, TRUE
, fs
->cache_streamp
);
3497 /* Provide context in new collection header */
3498 fp
->desc
= folder_lister_desc(context
, fp
);
3500 /* Insert new directory into list */
3501 free_folder_list(context
);
3502 fp
->prev
= context
->dir
;
3503 fp
->status
|= CNTXT_SUBDIR
;
3505 q_status_message2(SM_ORDER
, 0, 3, "Now in %sdirectory: %s",
3506 folder_total(FOLDERS(context
))
3507 ? "" : "EMPTY ", fp
->ref
);
3511 rv
= folder_lister_parent(fs
, context
, index
, 1);
3518 folder_lister_parent(FSTATE_S
*fs
, CONTEXT_S
*context
, int index
, int force_parent
)
3523 if(!force_parent
&& (fp
= context
->dir
->prev
)){
3524 char *s
, oldir
[MAILTMPLEN
];
3526 folder_select_preserve(context
);
3528 if((s
= strrindex(context
->dir
->ref
, context
->dir
->delim
)) != NULL
){
3531 u
= strrindex(context
->dir
->ref
, context
->dir
->delim
);
3532 v
= strrindex(context
->dir
->ref
, '}');
3535 strncpy(oldir
, s
+1, sizeof(oldir
)-1);
3536 oldir
[sizeof(oldir
)-1] = '\0';
3541 /* remember current directory for hiliting in new list */
3542 fs
->context
= context
;
3543 if(strlen(oldir
) < MAXFOLDER
- 1){
3544 strncpy(fs
->first_folder
, oldir
, MAXFOLDER
);
3545 fs
->first_folder
[MAXFOLDER
-1] = '\0';
3550 free_fdir(&context
->dir
, 0);
3551 fp
->status
|= CNTXT_NOFIND
;
3555 if(fp
->status
& CNTXT_SUBDIR
)
3556 q_status_message1(SM_ORDER
, 0, 3, _("Now in directory: %s"),
3557 strsquish(tmp_20k_buf
+ 500, SIZEOF_20KBUF
-500, fp
->ref
,
3558 ps_global
->ttyo
->screen_cols
- 22));
3560 q_status_message(SM_ORDER
, 0, 3,
3561 _("Returned to collection's top directory"));
3571 folder_lister_fullname(FSTATE_S
*fs
, char *name
)
3573 if(fs
->context
->dir
->status
& CNTXT_SUBDIR
){
3574 char tmp
[2*MAILTMPLEN
], tmp2
[2*MAILTMPLEN
], *p
;
3576 if(fs
->context
->dir
->ref
){
3577 snprintf(tmp
, sizeof(tmp
), "%.*s%.*s",
3578 MAILTMPLEN
, /* MAILTMPLEN == sizeof(tmp)/2 */
3579 ((fs
->relative_path
|| (fs
->context
->use
& CNTXT_SAVEDFLT
))
3580 && (p
= strstr(fs
->context
->context
, "%s")) && !*(p
+2)
3581 && !strncmp(fs
->context
->dir
->ref
, fs
->context
->context
,
3582 p
- fs
->context
->context
))
3583 ? fs
->context
->dir
->ref
+ (p
- fs
->context
->context
)
3584 : fs
->context
->dir
->ref
,
3586 tmp
[sizeof(tmp
)-1] = '\0';
3590 * If the applied name is still ambiguous (defined
3591 * that way by the user (i.e., "mail/[]"), then
3592 * we better fix it up...
3594 if(context_isambig(tmp
)
3595 && !fs
->relative_path
3596 && !(fs
->context
->use
& CNTXT_SAVEDFLT
)){
3597 /* if it's in the primary collection, the names relative */
3598 if(fs
->context
->dir
->ref
){ /* MAILTMPLEN = sizeof(tmp)/2 */
3599 if(IS_REMOTE(fs
->context
->context
)
3600 && (p
= strrindex(fs
->context
->context
, '}'))){
3601 snprintf(tmp2
, sizeof(tmp2
), "%.*s%.*s",
3602 (int) MIN(p
- fs
->context
->context
+ 1, sizeof(tmp
)/2),
3603 fs
->context
->context
,
3605 tmp2
[sizeof(tmp2
)-1] = '\0';
3608 build_path(tmp2
, ps_global
->ui
.homedir
, tmp
, sizeof(tmp2
));
3611 (void) context_apply(tmp2
, fs
->context
, tmp
, sizeof(tmp2
));
3613 return(cpystr(tmp2
));
3616 return(cpystr(tmp
));
3619 return(cpystr(name
));
3624 * Export a folder from pine space to user's space. It will still be a regular
3625 * mail folder but it will be in the user's home directory or something like
3629 folder_export(SCROLL_S
*sparms
)
3632 MAILSTREAM
*stream
, *ourstream
= NULL
;
3633 char expanded_file
[MAILTMPLEN
], *p
,
3634 tmp
[MAILTMPLEN
], *fname
, *fullname
= NULL
,
3635 filename
[MAXPATH
+1], full_filename
[MAXPATH
+1],
3636 deefault
[MAXPATH
+1];
3637 int open_inbox
= 0, we_cancel
= 0, width
,
3638 index
= (sparms
&& sparms
->text
.handles
)
3639 ? sparms
->text
.handles
->h
.f
.index
: 0;
3640 CONTEXT_S
*savecntxt
,
3641 *cntxt
= (sparms
&& sparms
->text
.handles
)
3642 ? sparms
->text
.handles
->h
.f
.context
: NULL
;
3643 static HISTORY_S
*history
= NULL
;
3645 dprint((4, "\n - folder export -\n"));
3648 if(folder_total(FOLDERS(cntxt
))){
3649 f
= folder_entry(index
, FOLDERS(cntxt
));
3650 if((*FPROC(sparms
)->fs
->f
.valid
)(f
, FPROC(sparms
)->fs
)){
3651 savecntxt
= FPROC(sparms
)->fs
->context
; /* necessary? */
3652 FPROC(sparms
)->fs
->context
= cntxt
;
3653 strncpy(deefault
, FLDR_NAME(f
), sizeof(deefault
)-1);
3654 deefault
[sizeof(deefault
)-1] = '\0';
3655 fname
= folder_lister_fullname(FPROC(sparms
)->fs
, FLDR_NAME(f
));
3656 FPROC(sparms
)->fs
->context
= savecntxt
;
3659 * We have to allow for INBOX and nicknames in
3660 * the incoming collection. Mimic what happens in
3663 strncpy(expanded_file
, fname
, sizeof(expanded_file
));
3664 expanded_file
[sizeof(expanded_file
)-1] = '\0';
3666 if(strucmp(fname
, ps_global
->inbox_name
) == 0
3667 || strucmp(fname
, ps_global
->VAR_INBOX_PATH
) == 0)
3670 if(!open_inbox
&& cntxt
&& context_isambig(fname
)){
3671 if((p
=folder_is_nick(fname
, FOLDERS(cntxt
), 0)) != NULL
){
3672 strncpy(expanded_file
, p
, sizeof(expanded_file
));
3673 expanded_file
[sizeof(expanded_file
)-1] = '\0';
3675 else if ((cntxt
->use
& CNTXT_INCMNG
)
3676 && (folder_index(fname
, cntxt
, FI_FOLDER
) < 0)
3677 && !is_absolute_path(fname
)){
3678 q_status_message1(SM_ORDER
, 3, 4,
3679 _("Can't find Incoming Folder %s."), fname
);
3685 fullname
= ps_global
->VAR_INBOX_PATH
;
3688 * We don't want to interpret this name in the context
3689 * of the reference string, that was already done
3690 * above in folder_lister_fullname(), just in the
3691 * regular context. We also don't want to lose the
3692 * reference string because we will still be in the
3693 * subdirectory after this operation completes. So
3694 * temporarily zero out the reference.
3698 tmpdir
= (cntxt
? cntxt
->dir
: NULL
);
3700 fullname
= context_apply(tmp
, cntxt
, expanded_file
,
3702 cntxt
->dir
= tmpdir
;
3706 ps_global
->ttyo
? ps_global
->ttyo
->screen_cols
: 80);
3707 stream
= sp_stream_get(fullname
, SP_MATCH
| SP_RO_OK
);
3708 if(!stream
&& fullname
){
3710 * Just using filename and full_filename as convenient
3711 * temporary buffers here.
3713 snprintf(filename
, sizeof(filename
), "Opening \"%s\"",
3714 short_str(fullname
, full_filename
, sizeof(full_filename
),
3717 filename
[sizeof(filename
)-1] = '\0';
3718 we_cancel
= busy_cue(filename
, NULL
, 0);
3719 stream
= pine_mail_open(NULL
, fullname
,
3720 OP_READONLY
|SP_USEPOOL
|SP_TEMPUSE
,
3729 * We have a stream for the folder we want to
3732 if(stream
&& stream
->nmsgs
> 0L){
3734 static ESCKEY_S eopts
[] = {
3735 {ctrl('T'), 10, "^T", N_("To Files")},
3736 {-1, 0, NULL
, NULL
},
3737 {-1, 0, NULL
, NULL
}};
3739 if(F_ON(F_ENABLE_TAB_COMPLETE
,ps_global
)){
3740 eopts
[r
].ch
= ctrl('I');
3742 eopts
[r
].name
= "TAB";
3743 /* TRANSLATORS: Complete is a verb, complete the name of a folder */
3744 eopts
[r
].label
= _("Complete");
3750 full_filename
[0] = '\0';
3752 r
= get_export_filename(ps_global
, filename
, deefault
,
3754 sizeof(filename
)-20, fname
, NULL
,
3756 -FOOTER_ROWS(ps_global
),
3757 GE_IS_EXPORT
| GE_NO_APPEND
, &history
);
3762 cmd_cancelled("Export folder");
3766 q_status_message1(SM_ORDER
, 0, 2,
3767 _("Can't export to file outside of %s"),
3768 ps_global
->VAR_OPER_DIR
);
3776 ps_global
->mm_log_error
= 0;
3779 * Do the creation in standard unix format, so it
3780 * is readable by all.
3782 rplstr(full_filename
, sizeof(full_filename
), 0, "#driver.unix/");
3783 ok
= pine_mail_create(NULL
, full_filename
) != 0L;
3785 * ff points to the original filename, without the
3786 * driver prefix. Only mail_create knows how to
3787 * handle driver prefixes.
3789 ff
= full_filename
+ strlen("#driver.unix/");
3792 if(!ps_global
->mm_log_error
)
3793 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
3794 _("Error creating file"));
3798 MSGNO_S
*tmpmap
= NULL
;
3800 snmsgs
= stream
->nmsgs
;
3801 mn_init(&tmpmap
, snmsgs
);
3802 for(l
= 1L; l
<= snmsgs
; l
++)
3804 mn_set_cur(tmpmap
, l
);
3806 mn_add_cur(tmpmap
, l
);
3808 blank_keymenu(ps_global
->ttyo
->screen_rows
-2, 0);
3809 we_cancel
= busy_cue(_("Copying folder"), NULL
, 0);
3810 l
= save(ps_global
, stream
, NULL
, ff
, tmpmap
, 0);
3817 q_status_message2(SM_ORDER
, 0, 3,
3818 "Folder %s exported to %s",
3821 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
3822 _("Error exporting to %s"),
3825 "Error exporting to %s: expected %ld msgs, saved %ld\n",
3826 filename
, snmsgs
, l
));
3832 q_status_message1(SM_ORDER
|SM_DING
, 3, 3,
3833 _("No messages in %s to export"), fname
);
3835 q_status_message(SM_ORDER
|SM_DING
, 3, 3,
3836 _("Can't open folder for exporting"));
3839 fs_give((void **) &fname
);
3842 pine_mail_close(ourstream
);
3847 q_status_message(SM_ORDER
| SM_DING
, 3, 3, _("Empty folder list!"));
3852 * Import a folder from user's space back to pine space.
3853 * We're just importing a regular mail folder, and saving all the messages
3854 * to another folder. It may seem more magical to the user but it isn't.
3855 * The import folder is a local folder, the new one can be remote or whatever.
3857 * add_folder -- return new folder name here
3858 * len -- length of add_folder
3860 * Returns 1 if we may have to redraw screen, 0 otherwise
3863 folder_import(SCROLL_S
*sparms
, char *add_folder
, size_t len
)
3865 MAILSTREAM
*istream
= NULL
;
3867 char filename
[MAXPATH
+1], full_filename
[MAXPATH
+1];
3868 static HISTORY_S
*history
= NULL
;
3869 static ESCKEY_S eopts
[] = {
3870 {ctrl('T'), 10, "^T", N_("To Files")},
3871 {-1, 0, NULL
, NULL
},
3872 {-1, 0, NULL
, NULL
}};
3874 if(F_ON(F_ENABLE_TAB_COMPLETE
,ps_global
)){
3875 eopts
[r
].ch
= ctrl('I');
3877 eopts
[r
].name
= "TAB";
3878 eopts
[r
].label
= N_("Complete");
3884 full_filename
[0] = '\0';
3886 /* get a folder to import */
3887 r
= get_export_filename(ps_global
, filename
, NULL
, full_filename
,
3888 sizeof(filename
)-20, "messages", "IMPORT",
3890 -FOOTER_ROWS(ps_global
), GE_IS_IMPORT
, &history
);
3895 cmd_cancelled("Import folder");
3899 q_status_message1(SM_ORDER
, 0, 2,
3900 _("Can't import file outside of %s"),
3901 ps_global
->VAR_OPER_DIR
);
3906 ps_global
->mm_log_error
= 0;
3907 if(full_filename
&& full_filename
[0])
3908 istream
= pine_mail_open(NULL
, full_filename
,
3909 OP_READONLY
| SP_TEMPUSE
, NULL
);
3911 if(istream
&& istream
->nmsgs
> 0L){
3914 char newfolder
[MAILTMPLEN
], nmsgs
[32];
3915 MSGNO_S
*tmpmap
= NULL
;
3916 CONTEXT_S
*cntxt
, *ourcntxt
;
3918 cntxt
= (sparms
&& sparms
->text
.handles
)
3919 ? sparms
->text
.handles
->h
.f
.context
: NULL
;
3921 newfolder
[0] = '\0';
3922 snprintf(nmsgs
, sizeof(nmsgs
), "%s msgs ", comatose(istream
->nmsgs
));
3923 nmsgs
[sizeof(nmsgs
)-1] = '\0';
3926 * Select a folder to save the messages to.
3928 if(save_prompt(ps_global
, &cntxt
, newfolder
, sizeof(newfolder
),
3929 nmsgs
, NULL
, 0L, NULL
, NULL
, NULL
)){
3931 if((cntxt
== ourcntxt
) && newfolder
[0]){
3933 strncpy(add_folder
, newfolder
, len
-1);
3934 add_folder
[len
-1] = '\0';
3935 free_folder_list(cntxt
);
3938 mn_init(&tmpmap
, istream
->nmsgs
);
3939 for(l
= 1; l
<= istream
->nmsgs
; l
++)
3941 mn_set_cur(tmpmap
, l
);
3943 mn_add_cur(tmpmap
, l
);
3945 blank_keymenu(ps_global
->ttyo
->screen_rows
-2, 0);
3946 we_cancel
= busy_cue("Importing messages", NULL
, 0);
3947 l
= save(ps_global
, istream
, cntxt
, newfolder
, tmpmap
,
3954 if(l
== istream
->nmsgs
)
3955 q_status_message2(SM_ORDER
, 0, 3,
3956 "Folder %s imported to %s",
3957 full_filename
, newfolder
);
3959 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
3960 _("Error importing to %s"),
3965 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
3966 _("No messages in file %s"),
3968 else if(!ps_global
->mm_log_error
)
3969 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
3970 _("Can't open file %s for import"), full_filename
);
3974 pine_mail_close(istream
);
3981 folder_select_toggle(CONTEXT_S
*context
, int index
, int (*func
) (CONTEXT_S
*, int))
3985 if((f
= folder_entry(index
, FOLDERS(context
))) != NULL
){
3986 f
->selected
= !f
->selected
;
3987 return((*func
)(context
, index
));
3994 * Find the next '}' that isn't part of a "${"
3995 * that appear for environment variables. We need
3996 * this for configuration, because we want to edit
3997 * the original pinerc entry, and not the digested
4001 end_bracket_no_nest(char *str
)
4005 for(p
= str
; *p
; p
++){
4006 if(*p
== '$' && *(p
+1) == '{'){
4007 while(*p
&& *p
!= '}')
4015 /* if we get here then there was no trailing '}' */
4020 /*----------------------------------------------------------------------
4023 Args: context -- context we're adding in
4024 which -- which config file to operate on
4025 varnum -- which varnum to operate on
4026 add_folder -- return new folder here
4027 add_folderlen -- length of add_folder
4028 possible_stream -- possible stream for re-use
4029 def -- default value to start out with for add_folder
4030 (for now, only for inbox-path editing)
4032 Result: returns nonzero on successful add, 0 otherwise
4035 add_new_folder(CONTEXT_S
*context
, EditWhich which
, int varnum
, char *add_folder
,
4036 size_t add_folderlen
, MAILSTREAM
*possible_stream
, char *def
)
4038 char tmp
[MAX(MAXFOLDER
,6*MAX_SCREEN_COLS
)+1], nickname
[32],
4039 *p
= NULL
, *return_val
= NULL
, buf
[MAILTMPLEN
],
4040 buf2
[MAILTMPLEN
], def_in_prompt
[MAILTMPLEN
], orig_folder
[2*MAXFOLDER
+10];
4042 PINERC_S
*prc
= NULL
;
4043 int i
, rc
, offset
, exists
, cnt
= 0, isdir
= 0;
4044 int maildrop
= 0, flags
= 0, inbox
= 0, require_a_subfolder
= 0;
4045 char *maildropfolder
= NULL
, *maildroplongname
= NULL
;
4046 char *default_mail_drop_host
= NULL
,
4047 *default_mail_drop_folder
= NULL
,
4048 *default_dstn_host
= NULL
,
4049 *default_dstn_folder
= NULL
,
4052 char mdmbox
[MAILTMPLEN
], ctmp
[MAILTMPLEN
];
4053 MAILSTREAM
*create_stream
= NULL
;
4055 static ESCKEY_S add_key
[] = {{ctrl('X'),12,"^X", NULL
},
4056 {-1, 0, NULL
, NULL
}};
4058 dprint((4, "\n - add_new_folder - \n"));
4060 nickname
[0] = orig_folder
[0] = add_folder
[0] = '\0';
4061 inbox
= (varnum
== V_INBOX_PATH
);
4063 if(inbox
|| context
->use
& CNTXT_INCMNG
){
4064 char inbox_host
[MAXPATH
], *beg
, *end
= NULL
;
4066 static ESCKEY_S host_key
[4];
4068 if(ps_global
->restricted
)
4073 prc
= ps_global
->prc
;
4076 prc
= ps_global
->post_prc
;
4082 readonly
= prc
? prc
->readonly
: 1;
4085 if(prc
&& prc
->quit_to_edit
){
4086 quit_to_edit_msg(prc
);
4091 q_status_message(SM_ORDER
,3,5,
4092 _("Cancelled: config file not editable"));
4097 * When adding an Incoming folder we can't supply the inbox host
4098 * as a default, because then the user has no way to type just
4099 * a plain RETURN to mean "no host, I want a local folder".
4100 * So we supply it as a ^X command instead. We could supply it
4101 * as the initial value of the string...
4103 * When editing inbox-path we will supply the default value as an
4104 * initial value of the string which can be edited. They can edit it
4105 * or erase it entirely to mean no host.
4110 copydef
= cpystr(def
);
4111 (void) removing_double_quotes(copydef
);
4113 if(check_for_move_mbox(copydef
, mdmbox
, sizeof(mdmbox
), &dstnmbox
)){
4116 * Current inbox is using a mail drop. Get the default
4117 * host value for the maildrop.
4121 && (mdmbox
[0] == '{'
4122 || (mdmbox
[0] == '*' && mdmbox
[1] == '{'))
4123 && (end
= end_bracket_no_nest(mdmbox
+1))
4124 && end
-mdmbox
< add_folderlen
){
4126 if(mdmbox
[0] == '{')
4127 default_mail_drop_host
= cpystr(mdmbox
+1);
4129 default_mail_drop_host
= cpystr(mdmbox
+2);
4132 if(!default_mail_drop_host
)
4133 default_mail_drop_folder
= cpystr(mdmbox
);
4134 else if(end
&& *(end
+1))
4135 default_mail_drop_folder
= cpystr(end
+1);
4139 && (*dstnmbox
== '{'
4140 || (*dstnmbox
== '*' && *++dstnmbox
== '{'))
4141 && (end
= end_bracket_no_nest(dstnmbox
+1))
4142 && end
-dstnmbox
< add_folderlen
){
4144 default_dstn_host
= cpystr(dstnmbox
+1);
4147 if(!default_dstn_host
)
4148 default_dstn_folder
= cpystr(dstnmbox
);
4149 else if(end
&& *(end
+1))
4150 default_dstn_folder
= cpystr(end
+1);
4158 && (*dstnmbox
== '{'
4159 || (*dstnmbox
== '*' && *++dstnmbox
== '{'))
4160 && (end
= end_bracket_no_nest(dstnmbox
+1))
4161 && end
-dstnmbox
< add_folderlen
){
4163 default_dstn_host
= cpystr(dstnmbox
+1);
4166 if(!default_dstn_host
)
4167 default_dstn_folder
= cpystr(dstnmbox
);
4168 else if(end
&& *(end
+1))
4169 default_dstn_folder
= cpystr(end
+1);
4173 fs_give((void **) ©def
);
4180 host_key
[i
].rval
= 0;
4181 host_key
[i
].name
= "";
4182 host_key
[i
++].label
= "";
4184 inbox_host
[0] = '\0';
4185 if(!inbox
&& (beg
= ps_global
->VAR_INBOX_PATH
)
4186 && (*beg
== '{' || (*beg
== '*' && *++beg
== '{'))
4187 && (end
= strindex(ps_global
->VAR_INBOX_PATH
, '}'))){
4188 strncpy(inbox_host
, beg
+1, end
- beg
);
4189 inbox_host
[end
- beg
- 1] = '\0';
4190 host_key
[i
].ch
= ctrl('X');
4191 host_key
[i
].rval
= 12;
4192 host_key
[i
].name
= "^X";
4193 host_key
[i
++].label
= N_("Use Inbox Host");
4197 host_key
[i
].rval
= 0;
4198 host_key
[i
].name
= "";
4199 host_key
[i
++].label
= "";
4202 if(!maildrop
&& !maildropfolder
){
4203 host_key
[i
].ch
= ctrl('W');
4204 host_key
[i
].rval
= 13;
4205 host_key
[i
].name
= "^W";
4206 /* TRANSLATORS: a mail drop is a place where mail is copied to so you
4208 host_key
[i
++].label
= N_("Use a Mail Drop");
4211 host_key
[i
].ch
= ctrl('W');
4212 host_key
[i
].rval
= 13;
4213 host_key
[i
].name
= "^W";
4214 host_key
[i
++].label
= N_("Do Not use a Mail Drop");
4217 host_key
[i
].ch
= -1;
4218 host_key
[i
].rval
= 0;
4219 host_key
[i
].name
= NULL
;
4220 host_key
[i
].label
= NULL
;
4223 snprintf(tmp
, sizeof(tmp
), _("Name of Mail Drop server : "));
4224 else if(maildropfolder
)
4225 snprintf(tmp
, sizeof(tmp
), _("Name of server to contain destination folder : "));
4227 snprintf(tmp
, sizeof(tmp
), _("Name of Inbox server : "));
4229 snprintf(tmp
, sizeof(tmp
), _("Name of server to contain added folder : "));
4231 tmp
[sizeof(tmp
)-1] = '\0';
4235 /* set up defaults */
4237 flags
= OE_APPEND_CURRENT
;
4238 if(maildrop
&& default_mail_drop_host
){
4239 strncpy(add_folder
, default_mail_drop_host
, add_folderlen
);
4240 add_folder
[add_folderlen
-1] = '\0';
4242 else if(!maildrop
&& default_dstn_host
){
4243 strncpy(add_folder
, default_dstn_host
, add_folderlen
);
4244 add_folder
[add_folderlen
-1] = '\0';
4247 add_folder
[0] = '\0';
4251 add_folder
[0] = '\0';
4255 rc
= optionally_enter(add_folder
, -FOOTER_ROWS(ps_global
), 0,
4256 add_folderlen
, tmp
, host_key
, help
, &flags
);
4257 removing_leading_and_trailing_white_space(add_folder
);
4260 * User went for the whole enchilada and entered a maildrop
4261 * completely without going through the steps.
4262 * Split it up as if they did and then skip over
4265 if(check_for_move_mbox(add_folder
, mdmbox
, sizeof(mdmbox
),
4269 fs_give((void **) &maildropfolder
);
4271 maildropfolder
= cpystr(mdmbox
);
4273 strncpy(add_folder
, dstnmbox
, add_folderlen
);
4274 add_folder
[add_folderlen
-1] = '\0';
4276 goto skip_over_folder_input
;
4280 * Now check to see if they entered a whole c-client
4281 * spec that isn't a mail drop.
4283 if(add_folder
[0] == '{'
4284 && add_folder
[1] != '\0'
4285 && (p
= srchstr(add_folder
, "}"))
4287 offset
= p
+1 - add_folder
;
4288 goto skip_over_folder_input
;
4292 * And check to see if they entered "INBOX".
4294 if(!strucmp(add_folder
, ps_global
->inbox_name
)){
4296 goto skip_over_folder_input
;
4299 /* remove surrounding braces */
4300 if(add_folder
[0] == '{' && add_folder
[1] != '\0'){
4303 q
= add_folder
+ strlen(add_folder
) - 1;
4306 for(q
= add_folder
; *q
; q
++)
4312 if(maildropfolder
&& inbox
)
4313 helper(h_inbox_add_maildrop_destn
,
4314 _("HELP FOR DESTINATION SERVER "), HLPD_SIMPLE
);
4315 else if(maildropfolder
&& !inbox
)
4316 helper(h_incoming_add_maildrop_destn
,
4317 _("HELP FOR DESTINATION SERVER "), HLPD_SIMPLE
);
4318 else if(maildrop
&& inbox
)
4319 helper(h_inbox_add_maildrop
, _("HELP FOR MAILDROP NAME "),
4321 else if(maildrop
&& !inbox
)
4322 helper(h_incoming_add_maildrop
, _("HELP FOR MAILDROP NAME "),
4325 helper(h_incoming_add_inbox
, _("HELP FOR INBOX SERVER "),
4328 helper(h_incoming_add_folder_host
, _("HELP FOR SERVER NAME "),
4331 ps_global
->mangled_screen
= 1;
4334 strncpy(add_folder
, inbox_host
, add_folderlen
);
4335 flags
|= OE_APPEND_CURRENT
;
4341 fs_give((void **) &maildropfolder
);
4346 default_mail_drop_host
= default_dstn_host
;
4347 default_dstn_host
= NULL
;
4348 default_mail_drop_folder
= default_dstn_folder
;
4349 default_dstn_folder
= NULL
;
4353 goto get_folder_name
;
4356 q_status_message(SM_ORDER
,0,2,
4357 inbox
? _("INBOX change cancelled")
4358 : _("Addition of new folder cancelled"));
4360 fs_give((void **) &maildropfolder
);
4363 if(default_mail_drop_host
)
4364 fs_give((void **) &default_mail_drop_host
);
4366 if(default_mail_drop_folder
)
4367 fs_give((void **) &default_mail_drop_folder
);
4369 if(default_dstn_host
)
4370 fs_give((void **) &default_dstn_host
);
4372 if(default_dstn_folder
)
4373 fs_give((void **) &default_dstn_folder
);
4383 /* set up default folder, if any */
4384 def_in_prompt
[0] = '\0';
4386 if(maildrop
&& default_mail_drop_folder
){
4387 strncpy(def_in_prompt
, default_mail_drop_folder
,
4388 sizeof(def_in_prompt
));
4389 def_in_prompt
[sizeof(def_in_prompt
)-1] = '\0';
4391 else if(!maildrop
&& default_dstn_folder
){
4392 strncpy(def_in_prompt
, default_dstn_folder
,
4393 sizeof(def_in_prompt
));
4394 def_in_prompt
[sizeof(def_in_prompt
)-1] = '\0';
4398 if((offset
= strlen(add_folder
)) != 0){ /* must be host for incoming */
4401 snprintf(tmp
, sizeof(tmp
),
4402 "Maildrop folder on \"%s\" to copy mail from%s%s%s : ",
4403 short_str(add_folder
, buf
, sizeof(buf
), 15, EndDots
),
4404 def_in_prompt
[0] ? " [" : "",
4405 short_str(def_in_prompt
, buf2
, sizeof(buf2
), 20, MidDots
),
4406 def_in_prompt
[0] ? "]" : "");
4407 else if(maildropfolder
)
4408 snprintf(tmp
, sizeof(tmp
),
4409 "Folder on \"%s\" to copy mail to%s%s%s : ",
4410 short_str(add_folder
, buf
, sizeof(buf
), 20, EndDots
),
4411 def_in_prompt
[0] ? " [" : "",
4412 short_str(def_in_prompt
, buf2
, sizeof(buf2
), 20, MidDots
),
4413 def_in_prompt
[0] ? "]" : "");
4415 snprintf(tmp
, sizeof(tmp
),
4416 "Folder on \"%s\" to use for INBOX%s%s%s : ",
4417 short_str(add_folder
, buf
, sizeof(buf
), 20, EndDots
),
4418 def_in_prompt
[0] ? " [" : "",
4419 short_str(def_in_prompt
, buf2
, sizeof(buf2
), 20, MidDots
),
4420 def_in_prompt
[0] ? "]" : "");
4422 snprintf(tmp
, sizeof(tmp
),
4423 "Folder on \"%s\" to add%s%s%s : ",
4424 short_str(add_folder
, buf
, sizeof(buf
), 25, EndDots
),
4425 def_in_prompt
[0] ? " [" : "",
4426 short_str(def_in_prompt
, buf2
, sizeof(buf2
), 20, MidDots
),
4427 def_in_prompt
[0] ? "]" : "");
4429 tmp
[sizeof(tmp
)-1] = '\0';
4431 for(i
= offset
;i
>= 0; i
--)
4432 add_folder
[i
+1] = add_folder
[i
];
4434 add_folder
[0] = '{';
4435 add_folder
[++offset
] = '}';
4436 add_folder
[++offset
] = '\0'; /* +2, total */
4440 snprintf(tmp
, sizeof(tmp
),
4441 "Folder to copy mail from%s%s%s : ",
4442 def_in_prompt
[0] ? " [" : "",
4443 short_str(def_in_prompt
, buf2
, sizeof(buf2
), 20, MidDots
),
4444 def_in_prompt
[0] ? "]" : "");
4445 else if(maildropfolder
)
4446 snprintf(tmp
, sizeof(tmp
),
4447 "Folder to copy mail to%s%s%s : ",
4448 def_in_prompt
[0] ? " [" : "",
4449 short_str(def_in_prompt
, buf2
, sizeof(buf2
), 20, MidDots
),
4450 def_in_prompt
[0] ? "]" : "");
4452 snprintf(tmp
, sizeof(tmp
),
4453 "Folder name to use for INBOX%s%s%s : ",
4454 def_in_prompt
[0] ? " [" : "",
4455 short_str(def_in_prompt
, buf2
, sizeof(buf2
), 20, MidDots
),
4456 def_in_prompt
[0] ? "]" : "");
4458 snprintf(tmp
, sizeof(tmp
),
4459 "Folder name to add%s%s%s : ",
4460 def_in_prompt
[0] ? " [" : "",
4461 short_str(def_in_prompt
, buf2
, sizeof(buf2
), 20, MidDots
),
4462 def_in_prompt
[0] ? "]" : "");
4464 tmp
[sizeof(tmp
)-1] = '\0';
4472 add_key
[0].label
= N_("Create Folder");
4474 rplstr(tmp
, sizeof(tmp
), 6, N_("Directory"));
4477 add_key
[0].label
= N_("Create Directory");
4479 rplstr(tmp
, sizeof(tmp
), 9, N_("Folder"));
4482 flags
= OE_APPEND_CURRENT
;
4483 rc
= optionally_enter(&add_folder
[offset
], -FOOTER_ROWS(ps_global
), 0,
4484 add_folderlen
- offset
, tmp
,
4485 ((context
->dir
->delim
) && !maildrop
)
4489 if(rc
== 0 && def_in_prompt
[0] && !add_folder
[offset
]){
4490 strncpy(&add_folder
[offset
], def_in_prompt
, add_folderlen
-offset
);
4491 add_folder
[add_folderlen
-1] = '\0';
4494 removing_leading_and_trailing_white_space(&add_folder
[offset
]);
4496 /* for non-local folders, transform UTF-8 to MUTF7 */
4497 if(offset
> 0 && add_folder
[0] == '{'){
4498 unsigned char *mutf7
= utf8_to_mutf7((unsigned char *)&add_folder
[offset
]);
4500 strncpy(orig_folder
, &add_folder
[offset
], 2*MAXFOLDER
+10);
4501 strncpy(&add_folder
[offset
], (char *) mutf7
, add_folderlen
-offset
);
4502 add_folder
[add_folderlen
-1] = '\0';
4503 fs_give((void **)&mutf7
);
4507 if(rc
== 0 && !(inbox
|| context
->use
& CNTXT_INCMNG
)
4508 && check_for_move_mbox(add_folder
, NULL
, 0L, NULL
)){
4509 q_status_message(SM_ORDER
, 6, 6,
4510 _("#move folders may only be the INBOX or in the Incoming Collection"));
4511 display_message(NO_OP_COMMAND
);
4515 if(rc
== 0 && add_folder
[offset
]){
4516 if(F_OFF(F_ENABLE_DOT_FOLDERS
,ps_global
)
4517 && add_folder
[offset
] == '.'){
4519 q_status_message(SM_ORDER
,3,3,
4520 _("Folder name can't begin with dot"));
4522 q_status_message1(SM_ORDER
,3,3,
4523 _("Config feature \"%s\" enables names beginning with dot"),
4524 pretty_feature_name(feature_list_name(F_ENABLE_DOT_FOLDERS
), -1));
4526 display_message(NO_OP_COMMAND
);
4530 /* if last char is delim, blast from new folder */
4531 for(p
= &add_folder
[offset
]; *p
&& *(p
+ 1); p
++)
4532 ; /* find last char in folder */
4535 if(*p
&& *p
!= context
->dir
->delim
){
4536 *++p
= context
->dir
->delim
;
4540 if(F_ON(F_QUELL_EMPTY_DIRS
, ps_global
))
4541 require_a_subfolder
++;
4543 else if(*p
== context
->dir
->delim
){
4544 q_status_message(SM_ORDER
|SM_DING
, 3, 3,
4545 _("Can't have trailing directory delimiters!"));
4546 display_message('X');
4554 isdir
= !isdir
; /* toggle directory creation */
4557 helper(h_incoming_add_folder_name
, _("HELP FOR FOLDER NAME "),
4560 else if(rc
== 1 || add_folder
[0] == '\0') {
4561 q_status_message(SM_ORDER
,0,2,
4562 inbox
? _("INBOX change cancelled")
4563 : _("Addition of new folder cancelled"));
4565 fs_give((void **) &maildropfolder
);
4568 if(default_mail_drop_host
)
4569 fs_give((void **) &default_mail_drop_host
);
4571 if(default_mail_drop_folder
)
4572 fs_give((void **) &default_mail_drop_folder
);
4574 if(default_dstn_host
)
4575 fs_give((void **) &default_dstn_host
);
4577 if(default_dstn_folder
)
4578 fs_give((void **) &default_dstn_folder
);
4585 if(maildrop
&& !maildropfolder
){
4586 maildropfolder
= cpystr(add_folder
);
4588 goto get_folder_name
;
4591 skip_over_folder_input
:
4593 if(require_a_subfolder
){
4594 /* add subfolder name to directory name */
4595 offset
= strlen(add_folder
);
4598 if(offset
> 0){ /* it had better be */
4601 save_delim
= add_folder
[offset
-1];
4602 add_folder
[offset
-1] = '\0';
4604 snprintf(tmp
, sizeof(tmp
),
4605 "Name of subfolder to add in \"%s\" : ",
4606 short_str(add_folder
, buf
, sizeof(buf
), 15, FrontDots
));
4608 tmp
[sizeof(tmp
)-1] = '\0';
4609 add_folder
[offset
-1] = save_delim
;
4613 flags
= OE_APPEND_CURRENT
;
4614 rc
= optionally_enter(&add_folder
[offset
], -FOOTER_ROWS(ps_global
), 0,
4615 add_folderlen
- offset
, tmp
,
4616 NULL
, NO_HELP
, &flags
);
4618 removing_leading_and_trailing_white_space(&add_folder
[offset
]);
4620 /* for non-local folders, transform UTF-8 to MUTF7 */
4621 if(offset
> 0 && add_folder
[0] == '{'){
4622 unsigned char *mutf7
= utf8_to_mutf7((unsigned char *)&add_folder
[offset
]);
4624 strncpy(orig_folder
, &add_folder
[offset
], 2*MAXFOLDER
+10);
4625 strncpy(&add_folder
[offset
], (char *) mutf7
, add_folderlen
-offset
);
4626 add_folder
[add_folderlen
-1] = '\0';
4627 fs_give((void **)&mutf7
);
4632 if(rc
== 0 && !add_folder
[offset
]){
4633 q_status_message(SM_ORDER
, 4, 4,
4634 _("A subfolder name is required, there is no default subfolder name"));
4638 if(rc
== 0 && add_folder
[offset
]){
4643 helper(h_emptydir_subfolder_name
, _("HELP FOR SUBFOLDER NAME "),
4646 else if(rc
== 1 || add_folder
[0] == '\0') {
4647 q_status_message(SM_ORDER
,0,2, _("Addition of new folder cancelled"));
4652 /* the directory is implicit now */
4656 if(context
== ps_global
->context_list
4657 && !(context
->dir
&& context
->dir
->ref
)
4658 && !strucmp(ps_global
->inbox_name
, add_folder
)){
4659 q_status_message1(SM_ORDER
,3,3,
4660 _("Cannot add folder %s in current context"),
4665 create_stream
= sp_stream_get(context_apply(ctmp
, context
, add_folder
,
4669 if(!create_stream
&& possible_stream
)
4670 create_stream
= context_same_stream(context
, add_folder
, possible_stream
);
4673 if(!inbox
&& context
->use
& CNTXT_INCMNG
){
4674 snprintf(tmp
, sizeof(tmp
), _("Nickname for folder \"%s\" : "), orig_folder
[0] ? orig_folder
: &add_folder
[offset
]);
4675 tmp
[sizeof(tmp
)-1] = '\0';
4677 int flags
= OE_APPEND_CURRENT
;
4679 rc
= optionally_enter(nickname
, -FOOTER_ROWS(ps_global
), 0,
4680 sizeof(nickname
), tmp
, NULL
, help
, &flags
);
4681 removing_leading_and_trailing_white_space(nickname
);
4683 if(strucmp(ps_global
->inbox_name
, nickname
))
4686 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
4687 _("Nickname cannot be \"%s\""), nickname
);
4692 help
= help
== NO_HELP
4693 ? h_incoming_add_folder_nickname
: NO_HELP
;
4695 else if(rc
== 1 || (rc
!= 3 && !*nickname
)){
4696 q_status_message(SM_ORDER
,0,2,
4697 inbox
? _("INBOX change cancelled")
4698 : _("Addition of new folder cancelled"));
4700 fs_give((void **) &maildropfolder
);
4703 if(default_mail_drop_host
)
4704 fs_give((void **) &default_mail_drop_host
);
4706 if(default_mail_drop_folder
)
4707 fs_give((void **) &default_mail_drop_folder
);
4709 if(default_dstn_host
)
4710 fs_give((void **) &default_dstn_host
);
4712 if(default_dstn_folder
)
4713 fs_give((void **) &default_dstn_folder
);
4721 * Already exist? First, make sure this name won't collide with
4722 * anything else in the list. Next, quickly test to see if it
4723 * the actual mailbox exists so we know any errors from
4724 * context_create() are really bad...
4726 for(offset
= 0; offset
< folder_total(FOLDERS(context
)); offset
++){
4727 f
= folder_entry(offset
, FOLDERS(context
));
4728 if(!strucmp(FLDR_NAME(f
), nickname
[0] ? nickname
: add_folder
)){
4729 q_status_message1(SM_ORDER
| SM_DING
, 0, 3,
4730 _("Incoming folder \"%s\" already exists"),
4731 nickname
[0] ? nickname
: add_folder
);
4733 fs_give((void **) &maildropfolder
);
4736 if(default_mail_drop_host
)
4737 fs_give((void **) &default_mail_drop_host
);
4739 if(default_mail_drop_folder
)
4740 fs_give((void **) &default_mail_drop_folder
);
4742 if(default_dstn_host
)
4743 fs_give((void **) &default_dstn_host
);
4745 if(default_dstn_folder
)
4746 fs_give((void **) &default_dstn_folder
);
4753 ps_global
->c_client_error
[0] = '\0';
4754 exists
= folder_exists(context
, add_folder
);
4755 if(exists
== FEX_ERROR
){
4756 if(ps_global
->c_client_error
[0] != '\0')
4757 q_status_message1(SM_ORDER
, 3, 3, "%s",
4758 ps_global
->c_client_error
);
4760 q_status_message1(SM_ORDER
, 3, 3, _("Error checking for %s"), add_folder
);
4766 exists
= FEX_ISFILE
;
4768 * If inbox is a maildropfolder, try to create the destination
4769 * folder. But it shouldn't cause a fatal error.
4771 if(maildropfolder
&& (folder_exists(NULL
, add_folder
) == FEX_NOENT
))
4772 context_create(NULL
, NULL
, add_folder
);
4775 if(exists
== FEX_ERROR
4776 || (exists
== FEX_NOENT
4777 && !context_create(context
, create_stream
, add_folder
)
4778 && !((context
->use
& CNTXT_INCMNG
)
4779 && !context_isambig(add_folder
)))){
4781 fs_give((void **) &maildropfolder
);
4784 if(default_mail_drop_host
)
4785 fs_give((void **) &default_mail_drop_host
);
4787 if(default_mail_drop_folder
)
4788 fs_give((void **) &default_mail_drop_folder
);
4790 if(default_dstn_host
)
4791 fs_give((void **) &default_dstn_host
);
4793 if(default_dstn_folder
)
4794 fs_give((void **) &default_dstn_folder
);
4797 return(FALSE
); /* c-client should've reported error */
4800 if(isdir
&& p
) /* whack off trailing delim */
4803 if(inbox
|| context
->use
& CNTXT_INCMNG
){
4808 apval
= APVAL(&ps_global
->vars
[varnum
], which
);
4810 fs_give((void **) apval
);
4813 alval
= ALVAL(&ps_global
->vars
[varnum
], which
);
4816 *alval
= (char **) fs_get(2*sizeof(char *));
4819 for(offset
=0; (*alval
)[offset
]; offset
++)
4822 fs_resize((void **) alval
, (offset
+ 2) * sizeof(char *));
4827 * If we're using a Mail Drop we have to assemble the correct
4828 * c-client string to do that. Mail drop syntax looks like
4830 * #move <DELIM> <FromMailbox> <DELIM> <ToMailbox>
4832 * DELIM is any character which does not appear in either of
4833 * the mailbox names.
4835 * And then the nickname is still in front of that mess.
4838 char *delims
= " +-_:!|ABCDEFGHIJKLMNOPQRSTUVWXYZ";
4842 len
= 5 + 2 + strlen(maildropfolder
) + strlen(add_folder
);
4843 maildroplongname
= (char *) fs_get((len
+1) * sizeof(char));
4845 for(c
= delims
; *c
; c
++){
4846 if(!strindex(maildropfolder
, *c
) &&
4847 !strindex(add_folder
, *c
))
4852 snprintf(maildroplongname
, len
+1, "#move%c%s%c%s",
4853 *c
, maildropfolder
, *c
, add_folder
);
4854 if(strlen(maildroplongname
) < add_folderlen
){
4855 strncpy(add_folder
, maildroplongname
, add_folderlen
);
4856 add_folder
[add_folderlen
-1] = '\0';
4860 q_status_message2(SM_ORDER
,0,2,
4861 "Can't find delimiter for \"#move %s %s\"",
4862 maildropfolder
, add_folder
);
4864 "Can't find delimiter for \"#move %s %s\"",
4865 maildropfolder
? maildropfolder
: "?",
4866 add_folder
? add_folder
: "?"));
4869 fs_give((void **) &maildropfolder
);
4872 if(default_mail_drop_host
)
4873 fs_give((void **) &default_mail_drop_host
);
4875 if(default_mail_drop_folder
)
4876 fs_give((void **) &default_mail_drop_folder
);
4878 if(default_dstn_host
)
4879 fs_give((void **) &default_dstn_host
);
4881 if(default_dstn_folder
)
4882 fs_give((void **) &default_dstn_folder
);
4888 if(maildroplongname
)
4889 fs_give((void **) &maildroplongname
);
4893 *apval
= cpystr(add_folder
);
4895 (*alval
)[offset
] = put_pair(nickname
, add_folder
);
4896 (*alval
)[offset
+1] = NULL
;
4899 set_current_val(&ps_global
->vars
[varnum
], TRUE
, FALSE
);
4901 prc
->outstanding_pinerc_changes
= 1;
4903 if(context
->use
& CNTXT_INCMNG
){
4904 if(!inbox
&& add_folder
&& add_folder
[0] && alval
&& *alval
&& (*alval
)[offset
]){
4906 * Instead of re-initing we try to insert the
4907 * added folder so that we preserve the last_unseen_update
4910 f
= new_folder(add_folder
, line_hash((*alval
)[offset
]));
4912 if(nickname
&& nickname
[0]){
4913 f
->nickname
= cpystr(nickname
);
4914 f
->name_len
= strlen(f
->nickname
);
4917 if(F_ON(F_ENABLE_INCOMING_CHECKING
, ps_global
)
4918 && !ps_global
->VAR_INCCHECKLIST
)
4919 f
->last_unseen_update
= LUU_INIT
;
4921 f
->last_unseen_update
= LUU_NEVERCHK
;
4923 folder_insert(folder_total(FOLDERS(context
)), f
, FOLDERS(context
));
4926 /* re-init to make sure we got it right */
4927 reinit_incoming_folder_list(ps_global
, context
);
4931 strncpy(add_folder
, nickname
, add_folderlen
-1); /* known by new name */
4932 add_folder
[add_folderlen
-1] = '\0';
4936 q_status_message1(SM_ORDER
, 0, 3, "Folder \"%s\" created",
4937 orig_folder
[0] ? orig_folder
: add_folder
);
4938 return_val
= add_folder
;
4940 else if(context_isambig(add_folder
)){
4941 free_folder_list(context
);
4942 q_status_message2(SM_ORDER
, 0, 3, "%s \"%s\" created",
4943 isdir
? "Directory" : "Folder", orig_folder
[0] ? orig_folder
: add_folder
);
4944 return_val
= add_folder
;
4947 q_status_message1(SM_ORDER
, 0, 3,
4948 "Folder \"%s\" created outside current collection",
4949 orig_folder
[0] ? orig_folder
: add_folder
);
4952 fs_give((void **) &maildropfolder
);
4955 if(default_mail_drop_host
)
4956 fs_give((void **) &default_mail_drop_host
);
4958 if(default_mail_drop_folder
)
4959 fs_give((void **) &default_mail_drop_folder
);
4961 if(default_dstn_host
)
4962 fs_give((void **) &default_dstn_host
);
4964 if(default_dstn_folder
)
4965 fs_give((void **) &default_dstn_folder
);
4968 return(return_val
!= NULL
);
4972 /*----------------------------------------------------------------------
4973 Subscribe to a news group
4975 Args: folder -- last folder added
4976 cntxt -- The context the subscription is for
4978 Result: returns the name of the folder subscribed too
4981 This builds a complete context for the entire list of possible news groups.
4982 It also build a context to find the newly created news groups as
4983 determined by data kept in .pinerc. When the find of these new groups is
4984 done the subscribed context is searched and the items marked as new.
4985 A list of new board is never actually created.
4989 group_subscription(char *folder
, size_t len
, CONTEXT_S
*cntxt
)
4991 STRLIST_S
*folders
= NULL
;
4992 int rc
= 0, last_rc
, i
, n
, flags
,
4993 last_find_partial
= 0, we_cancel
= 0;
4994 CONTEXT_S subscribe_cntxt
;
4996 ESCKEY_S subscribe_keys
[3];
4998 subscribe_keys
[i
= 0].ch
= ctrl('T');
4999 subscribe_keys
[i
].rval
= 12;
5000 subscribe_keys
[i
].name
= "^T";
5001 subscribe_keys
[i
++].label
= N_("To All Grps");
5003 if(F_ON(F_ENABLE_TAB_COMPLETE
,ps_global
)){
5004 subscribe_keys
[i
].ch
= ctrl('I');
5005 subscribe_keys
[i
].rval
= 11;
5006 subscribe_keys
[i
].name
= "TAB";
5007 subscribe_keys
[i
++].label
= N_("Complete");
5010 subscribe_keys
[i
].ch
= -1;
5012 /*---- Build a context to find all news groups -----*/
5014 subscribe_cntxt
= *cntxt
;
5015 subscribe_cntxt
.use
|= CNTXT_FINDALL
;
5016 subscribe_cntxt
.use
&= ~(CNTXT_PSEUDO
| CNTXT_ZOOM
| CNTXT_PRESRV
);
5017 subscribe_cntxt
.next
= NULL
;
5018 subscribe_cntxt
.prev
= NULL
;
5019 subscribe_cntxt
.dir
= new_fdir(cntxt
->dir
->ref
,
5020 cntxt
->dir
->view
.internal
, '*');
5021 FOLDERS(&subscribe_cntxt
) = init_folder_entries();
5023 * Prompt for group name.
5028 flags
= OE_APPEND_CURRENT
;
5030 rc
= optionally_enter(folder
, -FOOTER_ROWS(ps_global
), 0, len
,
5031 SUBSCRIBE_PMT
, subscribe_keys
, help
, &flags
);
5032 removing_trailing_white_space(folder
);
5033 removing_leading_white_space(folder
);
5034 if((rc
== 0 && folder
[0]) || rc
== 11 || rc
== 12){
5035 we_cancel
= busy_cue(_("Fetching newsgroup list"), NULL
, 1);
5037 if(last_find_partial
){
5038 /* clean up any previous find results */
5039 free_folder_list(&subscribe_cntxt
);
5040 last_find_partial
= 0;
5043 if(rc
== 11){ /* Tab completion! */
5044 if(folder_complete_internal(&subscribe_cntxt
,
5045 folder
, len
, &n
, FC_FORCE_LIST
)){
5049 if(!(n
&& last_rc
== 11 && !(flags
& OE_USER_MODIFIED
))){
5056 if(rc
== 12){ /* list the whole enchilada */
5057 build_folder_list(NULL
, &subscribe_cntxt
, NULL
, NULL
,BFL_NONE
);
5059 else if(strlen(folder
)){
5060 char tmp
[MAILTMPLEN
];
5062 snprintf(tmp
, sizeof(tmp
), "%s%.*s*", (rc
== 11) ? "" : "*",
5063 MAILTMPLEN
-3, folder
);
5064 tmp
[sizeof(tmp
)-1] = '\0';
5065 build_folder_list(NULL
, &subscribe_cntxt
, tmp
, NULL
, BFL_NONE
);
5066 subscribe_cntxt
.dir
->status
&= ~(CNTXT_PARTFIND
|CNTXT_NOFIND
);
5069 q_status_message(SM_ORDER
, 0, 2,
5070 _("No group substring to match! Use ^T to list all news groups."));
5075 * If we did a partial find on matches, then we faked a full
5076 * find which will cause this to just return.
5078 if((i
= folder_total(FOLDERS(&subscribe_cntxt
))) != 0){
5082 * fake that we've found everything there is to find...
5084 subscribe_cntxt
.use
&= ~(CNTXT_NOFIND
|CNTXT_PARTFIND
);
5085 last_find_partial
= 1;
5088 f
= folder_entry(0, FOLDERS(&subscribe_cntxt
))->name
;
5089 if(!strcmp(f
, folder
)){
5090 rc
= 1; /* success! */
5093 else{ /* else complete the group */
5094 strncpy(folder
, f
, len
-1);
5095 folder
[len
-1] = '\0';
5099 else if(!(flags
& OE_USER_MODIFIED
)){
5101 * See if there wasn't an exact match in the lot.
5104 f
= folder_entry(i
,FOLDERS(&subscribe_cntxt
))->name
;
5105 if(!strcmp(f
, folder
))
5111 /* if so, then the user picked it from the list the
5112 * last time and didn't change it at the prompt.
5113 * Must mean they're accepting it...
5116 rc
= 1; /* success! */
5123 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
5124 _("No groups to select from!"));
5126 q_status_message1(SM_ORDER
, 3, 3,
5127 _("News group \"%s\" didn't match any existing groups"),
5129 free_folder_list(&subscribe_cntxt
);
5134 /*----- Mark groups that are currently subscribed too ------*/
5135 /* but first make sure they're found */
5136 build_folder_list(NULL
, cntxt
, "*", NULL
, BFL_LSUB
);
5137 for(i
= 0 ; i
< folder_total(FOLDERS(&subscribe_cntxt
)); i
++) {
5138 FOLDER_S
*f
= folder_entry(i
, FOLDERS(&subscribe_cntxt
));
5140 f
->subscribed
= search_folder_list(FOLDERS(cntxt
),
5145 cancel_busy_cue(-1);
5147 /*----- Call the folder lister to do all the work -----*/
5148 folders
= folders_for_subscribe(ps_global
,
5149 &subscribe_cntxt
, folder
);
5152 /* Multiple newsgroups OR Auto-select */
5153 if(folders
->next
|| F_ON(F_SELECT_WO_CONFIRM
,ps_global
))
5156 strncpy(folder
, (char *) folders
->name
, len
-1);
5157 folder
[len
-1] = '\0';
5158 free_strlist(&folders
);
5162 help
= help
== NO_HELP
? h_news_subscribe
: NO_HELP
;
5164 else if(rc
== 1 || folder
[0] == '\0'){
5171 folder
[0] = '\0'; /* make sure not to return partials */
5173 q_status_message(SM_ORDER
, 0, 3, _("Subscribe cancelled"));
5176 MAILSTREAM
*sub_stream
;
5177 int sclose
= 0, errors
= 0;
5179 if(folders
){ /*------ Actually do the subscription -----*/
5183 /* subscribe one at a time */
5186 * Open stream before subscribing so c-client knows what newsrc
5187 * to use, along with other side-effects.
5189 if((sub_stream
= mail_cmd_stream(&subscribe_cntxt
, &sclose
)) != NULL
){
5190 for(flp
= folders
; flp
; flp
= flp
->next
){
5191 (void) context_apply(tmp_20k_buf
, &subscribe_cntxt
,
5192 (char *) flp
->name
, SIZEOF_20KBUF
);
5193 if(mail_subscribe(sub_stream
, tmp_20k_buf
) == 0L){
5195 * This message may not make it to the screen,
5196 * because a c-client message about the failure
5197 * will be there. Probably best not to string
5198 * together a whole bunch of errors if there is
5201 q_status_message1(errors
?SM_INFO
: SM_ORDER
,
5203 _("Error subscribing to \"%s\""),
5204 (char *) flp
->name
);
5210 strncpy(folder
, (char *) flp
->name
, len
-1);
5211 folder
[len
-1] = '\0';
5214 /*---- Update the screen display data structures -----*/
5215 if(ALL_FOUND(cntxt
)){
5216 if(cntxt
->use
& CNTXT_PSEUDO
){
5217 folder_delete(0, FOLDERS(cntxt
));
5218 cntxt
->use
&= ~CNTXT_PSEUDO
;
5221 folder_insert(-1, new_folder((char *) flp
->name
, 0),
5227 pine_mail_close(sub_stream
);
5233 q_status_message(SM_ORDER
| SM_DING
, 3, 5,
5234 _("Subscriptions failed, subscribed to no new groups"));
5236 q_status_message3(SM_ORDER
| (errors
? SM_DING
: 0),
5238 "Subscribed to %s new groups%s%s",
5240 errors
? ", failed on " : "",
5241 errors
? comatose((long)errors
) : "");
5243 free_strlist(&folders
);
5246 if((sub_stream
= mail_cmd_stream(&subscribe_cntxt
, &sclose
)) != NULL
){
5247 (void) context_apply(tmp_20k_buf
, &subscribe_cntxt
, folder
,
5249 if(mail_subscribe(sub_stream
, tmp_20k_buf
) == 0L){
5250 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
5251 _("Error subscribing to \"%s\""), folder
);
5254 else if(ALL_FOUND(cntxt
)){
5255 /*---- Update the screen display data structures -----*/
5256 if(cntxt
->use
& CNTXT_PSEUDO
){
5257 folder_delete(0, FOLDERS(cntxt
));
5258 cntxt
->use
&= ~CNTXT_PSEUDO
;
5261 folder_insert(-1, new_folder(folder
, 0), FOLDERS(cntxt
));
5264 pine_mail_close(sub_stream
);
5267 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
5268 _("Error subscribing to \"%s\""), folder
);
5273 if(!errors
&& folder
[0])
5274 q_status_message1(SM_ORDER
, 0, 3, _("Subscribed to \"%s\""), folder
);
5277 free_fdir(&subscribe_cntxt
.dir
, 1);
5282 /*----------------------------------------------------------------------
5285 Args: new_name -- buffer to contain new file name
5286 len -- length of new_name buffer
5287 index -- index of folder in folder list to rename
5288 context -- collection of folders making up folder list
5289 possible_stream -- may be able to use this stream
5291 Result: returns the new name of the folder, or NULL if nothing happened.
5293 When either the sent-mail or saved-message folders are renamed, immediately
5294 create a new one in their place so they always exist. The main loop above also
5295 detects this and makes the rename look like an add of the sent-mail or
5296 saved-messages folder. (This behavior may not be optimal, but it keeps things
5301 rename_folder(CONTEXT_S
*context
, int index
, char *new_name
, size_t len
, MAILSTREAM
*possible_stream
)
5303 char *folder
, prompt
[64], *name_p
= NULL
;
5306 PINERC_S
*prc
= NULL
;
5307 int rc
, ren_cur
, cnt
= 0, isdir
= 0, readonly
= 0;
5311 dprint((4, "\n - rename folder -\n"));
5313 if(NEWS_TEST(context
)){
5314 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
5315 _("Can't rename news groups!"));
5318 else if(!folder_total(FOLDERS(context
))){
5319 q_status_message(SM_ORDER
| SM_DING
, 0, 4,
5320 _("Empty folder collection. No folder to rename!"));
5323 else if((new_f
= folder_entry(index
, FOLDERS(context
)))
5324 && context
== ps_global
->context_list
5325 && !(context
->dir
&& context
->dir
->ref
)
5326 && !strucmp(FLDR_NAME(new_f
), ps_global
->inbox_name
)) {
5327 q_status_message1(SM_ORDER
| SM_DING
, 3, 4,
5328 _("Can't change special folder name \"%s\""),
5329 ps_global
->inbox_name
);
5333 ew
= config_containing_inc_fldr(new_f
);
5334 if(ps_global
->restricted
)
5339 prc
= ps_global
->prc
;
5342 prc
= ps_global
->post_prc
;
5348 readonly
= prc
? prc
->readonly
: 1;
5351 if(prc
&& prc
->quit_to_edit
&& (context
->use
& CNTXT_INCMNG
)){
5352 quit_to_edit_msg(prc
);
5356 if(readonly
&& (context
->use
& CNTXT_INCMNG
)){
5357 q_status_message(SM_ORDER
,3,5,
5358 _("Rename cancelled: folder not in editable config file"));
5362 if(context
->use
& CNTXT_INCMNG
){
5363 if(!(folder
= new_f
->nickname
))
5364 folder
= ""; /* blank nickname */
5367 folder
= FLDR_NAME(new_f
);
5369 ren_cur
= strcmp(folder
, ps_global
->cur_folder
) == 0;
5371 snprintf(prompt
, sizeof(prompt
), "%s %s to : ", _("Rename"),
5372 (context
->use
& CNTXT_INCMNG
)
5374 : (isdir
= new_f
->isdir
)
5375 ? _("directory") : _("folder"));
5376 prompt
[sizeof(prompt
)-1] = '\0';
5378 strncpy(new_name
, folder
, len
-1);
5379 new_name
[len
-1] = '\0';
5381 int flags
= OE_APPEND_CURRENT
;
5383 rc
= optionally_enter(new_name
, -FOOTER_ROWS(ps_global
), 0,
5384 len
, prompt
, NULL
, help
, &flags
);
5386 help
= help
== NO_HELP
? h_oe_foldrename
: NO_HELP
;
5390 removing_leading_and_trailing_white_space(new_name
);
5392 if(rc
== 0 && (*new_name
|| (context
->use
& CNTXT_INCMNG
))) {
5393 /* verify characters */
5394 if(F_OFF(F_ENABLE_DOT_FOLDERS
,ps_global
) && *new_name
== '.'){
5396 q_status_message(SM_ORDER
,3,3,
5397 _("Folder name can't begin with dot"));
5399 q_status_message1(SM_ORDER
,3,3,
5400 _("Config feature \"s\" enables names beginning with dot"),
5401 pretty_feature_name(feature_list_name(F_ENABLE_DOT_FOLDERS
), -1));
5403 display_message(NO_OP_COMMAND
);
5407 if(folder_index(new_name
, context
, FI_ANY
|FI_RENAME
) >= 0){
5408 q_status_message1(SM_ORDER
, 3, 3,
5409 _("Folder \"%s\" already exists"),
5410 pretty_fn(new_name
));
5411 display_message(NO_OP_COMMAND
);
5414 else if(context
== ps_global
->context_list
5415 && !(context
->dir
&& context
->dir
->ref
)
5416 && !strucmp(new_name
, ps_global
->inbox_name
)){
5417 if(context
->use
& CNTXT_INCMNG
)
5418 q_status_message1(SM_ORDER
, 3, 3, _("Can't rename incoming folder to %s"),
5421 q_status_message1(SM_ORDER
, 3, 3, _("Can't rename folder to %s"),
5424 display_message(NO_OP_COMMAND
);
5429 if(rc
!= 4) /* redraw */
5430 break; /* no redraw */
5434 if(rc
!= 1 && isdir
){ /* add trailing delim? */
5435 for(name_p
= new_name
; *name_p
&& *(name_p
+1) ; name_p
++)
5438 if(*name_p
== context
->dir
->delim
) /* lop off delim */
5443 || !(*new_name
|| (context
->use
& CNTXT_INCMNG
))
5444 || !strcmp(new_name
, folder
)){
5445 q_status_message(SM_ORDER
, 0, 2, _("Folder rename cancelled"));
5449 if(context
->use
& CNTXT_INCMNG
){
5450 char **new_list
, **lp
, ***alval
;
5453 alval
= ALVAL(&ps_global
->vars
[V_INCOMING_FOLDERS
], ew
);
5454 for(i
= 0; (*alval
)[i
]; i
++)
5457 new_list
= (char **) fs_get((i
+ 1) * sizeof(char *));
5459 for(lp
= new_list
, i
= 0; (*alval
)[i
]; i
++){
5460 /* figure out if this is the one we're renaming */
5461 expand_variables(tmp_20k_buf
, SIZEOF_20KBUF
, (*alval
)[i
], 0);
5462 if(new_f
->varhash
== line_hash(tmp_20k_buf
)){
5463 char *folder_string
= NULL
, *nickname
= NULL
;
5466 fs_give((void **) &new_f
->nickname
);
5469 new_f
->nickname
= cpystr(new_name
);
5472 * Parse folder line for nickname and folder name.
5473 * No nickname on line is OK.
5475 get_pair(tmp_20k_buf
, &nickname
, &folder_string
, 0, 0);
5478 fs_give((void **)&nickname
);
5480 *lp
= put_pair(new_name
, folder_string
);
5482 new_f
->varhash
= line_hash(*lp
++);
5485 *lp
++ = cpystr((*alval
)[i
]);
5490 set_variable_list(V_INCOMING_FOLDERS
, new_list
, TRUE
, ew
);
5491 free_list_array(&new_list
);
5496 /* Can't rename open streams */
5497 if((strm
= context_already_open_stream(context
, folder
, AOS_NONE
))
5498 || (ren_cur
&& (strm
=ps_global
->mail_stream
))){
5499 if(possible_stream
== strm
)
5500 possible_stream
= NULL
;
5502 pine_mail_actually_close(strm
);
5506 && !context_same_stream(context
, new_name
, possible_stream
))
5507 possible_stream
= NULL
;
5509 if((rc
= context_rename(context
, possible_stream
, folder
, new_name
)) != 0){
5510 if(name_p
&& *name_p
== context
->dir
->delim
)
5511 *name_p
= '\0'; /* blat trailing delim */
5513 /* insert new name? */
5514 if(!strindex(new_name
, context
->dir
->delim
)){
5515 new_f
= new_folder(new_name
, 0);
5516 new_f
->isdir
= isdir
;
5517 folder_insert(-1, new_f
, FOLDERS(context
));
5520 if(strcmp(ps_global
->VAR_DEFAULT_FCC
, folder
) == 0
5521 || strcmp(ps_global
->VAR_DEFAULT_SAVE_FOLDER
, folder
) == 0) {
5522 /* renaming sent-mail or saved-messages */
5523 if(context_create(context
, NULL
, folder
)){
5524 q_status_message3(SM_ORDER
,0,3,
5525 "Folder \"%s\" renamed to \"%s\". New \"%s\" created",
5528 (strcmp(ps_global
->VAR_DEFAULT_SAVE_FOLDER
,
5530 ? ps_global
->VAR_DEFAULT_SAVE_FOLDER
5531 : ps_global
->VAR_DEFAULT_FCC
));
5535 q_status_message1(SM_ORDER
| SM_DING
, 3, 4,
5536 "Error creating new \"%s\"", folder
);
5538 dprint((1, "Error creating \"%s\" in %s context\n",
5539 folder
? folder
: "?",
5540 context
->context
? context
->context
: "?"));
5544 q_status_message2(SM_ORDER
, 0, 3,
5545 "Folder \"%s\" renamed to \"%s\"",
5546 pretty_fn(folder
), pretty_fn(new_name
));
5548 free_folder_list(context
);
5552 if(ps_global
&& ps_global
->ttyo
){
5553 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
5554 ps_global
->mangled_footer
= 1;
5557 /* No reopen the folder we just had open */
5558 do_broach_folder(new_name
, context
, NULL
, 0L);
5565 /*----------------------------------------------------------------------
5566 Confirm and delete a folder
5568 Args: fs -- folder screen state
5570 Result: return 0 if not delete, 1 if deleted.
5573 delete_folder(CONTEXT_S
*context
, int index
, char *next_folder
, size_t len
, MAILSTREAM
**possible_streamp
)
5575 char *folder
, ques_buf
[MAX_SCREEN_COLS
+1], *target
= NULL
,
5577 MAILSTREAM
*del_stream
= NULL
, *sub_stream
, *strm
= NULL
;
5580 PINERC_S
*prc
= NULL
;
5581 int ret
, unsub_opened
= 0, close_opened
= 0, blast_folder
= 1,
5585 cmd_cancelled("Missing context in Delete");
5589 if(NEWS_TEST(context
)){
5590 folder
= folder_entry(index
, FOLDERS(context
))->name
;
5591 snprintf(ques_buf
, sizeof(ques_buf
), _("Really unsubscribe from \"%s\""), folder
);
5592 ques_buf
[sizeof(ques_buf
)-1] = '\0';
5594 ret
= want_to(ques_buf
, 'n', 'x', NO_HELP
, WT_NORM
);
5604 dprint((2, "deleting folder \"%s\" in context \"%s\"\n",
5605 folder
? folder
: "?",
5606 context
->context
? context
->context
: "?"));
5608 if((sub_stream
= mail_cmd_stream(context
, &unsub_opened
)) != NULL
){
5609 (void) context_apply(tmp_20k_buf
, context
, folder
, SIZEOF_20KBUF
);
5610 if(!mail_unsubscribe(sub_stream
, tmp_20k_buf
)){
5611 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
5612 _("Error unsubscribing from \"%s\""),folder
);
5614 pine_mail_close(sub_stream
);
5618 pine_mail_close(sub_stream
);
5622 * Fix up the displayed list
5624 folder_delete(index
, FOLDERS(context
));
5628 fp
= folder_entry(index
, FOLDERS(context
));
5630 cmd_cancelled("Can't find folder to Delete!");
5634 if(!((context
->use
& CNTXT_INCMNG
) && fp
->name
5635 && check_for_move_mbox(fp
->name
, NULL
, 0, &target
))){
5639 folder
= FLDR_NAME(fp
);
5640 dprint((4, "=== delete_folder(%s) ===\n",
5641 folder
? folder
: "?"));
5643 ew
= config_containing_inc_fldr(fp
);
5644 if(ps_global
->restricted
)
5649 prc
= ps_global
->prc
;
5652 prc
= ps_global
->post_prc
;
5658 readonly
= prc
? prc
->readonly
: 1;
5661 if(prc
&& prc
->quit_to_edit
&& (context
->use
& CNTXT_INCMNG
)){
5662 quit_to_edit_msg(prc
);
5666 if(context
== ps_global
->context_list
5667 && !(context
->dir
&& context
->dir
->ref
)
5668 && strucmp(folder
, ps_global
->inbox_name
) == 0){
5669 q_status_message1(SM_ORDER
| SM_DING
, 3, 4,
5670 _("Can't delete special folder \"%s\"."),
5671 ps_global
->inbox_name
);
5674 else if(readonly
&& (context
->use
& CNTXT_INCMNG
)){
5675 q_status_message(SM_ORDER
,3,5,
5676 _("Deletion cancelled: folder not in editable config file"));
5680 && (strm
=context_already_open_stream(context
,fp
->name
,AOS_NONE
)))
5683 && (strm
=context_already_open_stream(NULL
,target
,AOS_NONE
)))){
5684 if(strm
== ps_global
->mail_stream
)
5687 else if(fp
->isdir
|| fp
->isdual
){ /* NO DELETE if directory isn't EMPTY */
5688 FDIR_S
*fdirp
= next_folder_dir(context
,folder
,TRUE
,possible_streamp
);
5692 else if(fp
->hasnochildren
)
5695 ret
= folder_total(fdirp
->folders
) > 0;
5696 free_fdir(&fdirp
, 1);
5700 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
5701 _("Can't delete non-empty directory \"%s\"%s."),
5702 folder
, (fp
->isfolder
&& fp
->isdual
) ? " (or folder of same name)" : "");
5707 * Folder by the same name exist? If so, server's probably going
5708 * to delete it as well. Punt?
5711 && (ret
= want_to(DIR_FOLD_PMT
,'n','x',NO_HELP
,WT_NORM
)) != 'y'){
5712 q_status_message(SM_ORDER
,0,3, (ret
== 'x') ? _("Delete cancelled")
5713 : _("No folder deleted"));
5718 if(context
->use
& CNTXT_INCMNG
){
5719 static ESCKEY_S delf_opts
[] = {
5720 {'n', 'n', "N", N_("Nickname only")},
5721 {'b', 'b', "B", N_("Both Folder and Nickname")},
5724 #define DELF_PROMPT _("DELETE only Nickname or Both nickname and folder? ")
5726 switch(radio_buttons(DELF_PROMPT
, -FOOTER_ROWS(ps_global
),
5727 delf_opts
,'n','x',NO_HELP
,RB_NORM
)){
5733 cmd_cancelled("Delete");
5741 unsigned char *fname
= folder_name_decoded((unsigned char *)folder
);
5742 snprintf(ques_buf
, sizeof(ques_buf
), "DELETE \"%s\"%s", fname
? (char *) fname
: folder
,
5743 close_opened
? " (the currently open folder)" :
5744 (fp
->isdir
&& !(fp
->isdual
|| fp
->isfolder
5745 || (folder_index(folder
, context
, FI_FOLDER
) >= 0)))
5746 ? " (a directory)" : "");
5747 if(fname
) fs_give((void **)&fname
);
5748 ques_buf
[sizeof(ques_buf
)-1] = '\0';
5750 if((ret
= want_to(ques_buf
, 'n', 'x', NO_HELP
, WT_NORM
)) != 'y'){
5751 q_status_message(SM_ORDER
,0,3, (ret
== 'x') ? _("Delete cancelled")
5752 : _("Nothing deleted"));
5758 dprint((2,"deleting \"%s\" (%s) in context \"%s\"\n",
5759 fp
->name
? fp
->name
: "?",
5760 fp
->nickname
? fp
->nickname
: "",
5761 context
->context
? context
->context
: "?"));
5764 * Close it, NULL the pointer, and let do_broach_folder fixup
5767 pine_mail_actually_close(strm
);
5769 if(ps_global
&& ps_global
->ttyo
){
5770 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
5771 ps_global
->mangled_footer
= 1;
5774 ps_global
->mangled_header
= 1;
5775 do_broach_folder(ps_global
->inbox_name
,
5776 ps_global
->context_list
, NULL
, DB_INBOXWOCNTXT
);
5781 * Use fp->name since "folder" may be a nickname...
5783 if(possible_streamp
&& *possible_streamp
5784 && context_same_stream(context
, fp
->name
, *possible_streamp
))
5785 del_stream
= *possible_streamp
;
5789 if(!context_delete(context
, del_stream
, fnamep
)){
5791 * BUG: what if sent-mail or saved-messages????
5793 q_status_message1(SM_ORDER
,3,3,"Delete of \"%s\" Failed!", folder
);
5798 snprintf(buf
, sizeof(buf
), "%s\"%s\" deleted.",
5799 !blast_folder
? "Nickname " :
5800 fp
->isdual
? "Folder/Directory " :
5801 (fp
->isdir
&& fp
->isfolder
) ? "Folder " :
5802 fp
->isdir
? "Directory " :
5805 buf
[sizeof(buf
)-1] = '\0';
5807 q_status_message(SM_ORDER
, 0, 3, buf
);
5809 if(context
->use
& CNTXT_INCMNG
){
5810 char **new_list
, **lp
, ***alval
;
5813 alval
= ALVAL(&ps_global
->vars
[V_INCOMING_FOLDERS
], ew
);
5814 for(i
= 0; (*alval
)[i
]; i
++)
5818 * Make it one too big in case we don't find the match for
5819 * some unknown reason.
5821 new_list
= (char **) fs_get((i
+ 1) * sizeof(char *));
5824 * Copy while figuring out which one to skip.
5826 for(lp
= new_list
, i
= 0; (*alval
)[i
]; i
++){
5827 expand_variables(tmp_20k_buf
, SIZEOF_20KBUF
, (*alval
)[i
], 0);
5828 if(fp
->varhash
!= line_hash(tmp_20k_buf
))
5829 *lp
++ = cpystr((*alval
)[i
]);
5834 set_variable_list(V_INCOMING_FOLDERS
, new_list
, TRUE
, ew
);
5835 free_list_array(&new_list
);
5839 * Fix up the displayed list.
5841 folder_delete(index
, FOLDERS(context
));
5844 * Take a guess at what should get hilited next.
5846 if(index
< (ret
= folder_total(FOLDERS(context
)))
5847 || ((index
= ret
- 1) >= 0)){
5848 if((fp
= folder_entry(index
, FOLDERS(context
)))
5849 && strlen(FLDR_NAME(fp
)) < len
- 1)
5850 strncpy(next_folder
, FLDR_NAME(fp
), len
-1);
5851 next_folder
[len
-1] = '\0';
5854 if(!(context
->use
& CNTXT_INCMNG
)){
5856 * Then cause the list to get rebuild 'cause we may've blasted
5857 * some folder that's also a directory or vice versa...
5859 free_folder_list(context
);
5866 /*----------------------------------------------------------------------
5867 Print the list of folders on paper
5869 Args: list -- The current list of folders
5870 lens -- The list of lengths of the current folders
5871 display -- The current folder display structure
5873 Result: list printed on paper
5875 If the display list was created for 80 columns it is used, otherwise
5876 a new list is created for 80 columns
5880 print_folders(FPROC_S
*fp
)
5882 if(open_printer(_("folder list")) == 0){
5883 (void) folder_list_text(ps_global
, fp
, print_char
, NULL
, 80);
5891 scan_get_pattern(char *kind
, char *pat
, int len
)
5897 snprintf(prompt
, sizeof(prompt
), _("String in folder %s to match : "), kind
);
5898 prompt
[sizeof(prompt
)-1] = '\0';
5901 flags
= OE_APPEND_CURRENT
| OE_DISALLOW_HELP
;
5902 switch(optionally_enter(pat
, -FOOTER_ROWS(ps_global
), 0, len
,
5903 prompt
, NULL
, NO_HELP
, &flags
)){
5906 if(ps_global
->redrawer
)
5907 (*ps_global
->redrawer
)();
5916 cmd_cancelled("Select");
5926 folder_select_text(struct pine
*ps
, CONTEXT_S
*context
, int selected
)
5928 char pattern
[MAILTMPLEN
], type
= '\0';
5929 static ESCKEY_S scan_opts
[] = {
5930 {'n', 'n', "N", N_("Name Select")},
5931 {'c', 'c', "C", N_("Content Select")},
5935 if(context
->use
& CNTXT_INCMNG
){
5936 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
5937 _("Select \"Text\" not supported in Incoming Folders"));
5941 switch(radio_buttons(SEL_TEXT_PMT
, -FOOTER_ROWS(ps_global
),
5942 scan_opts
, 'n', 'x', NO_HELP
, RB_NORM
)){
5943 case 'n' : /* Name search */
5944 if(scan_get_pattern("NAME", pattern
, sizeof(pattern
)))
5949 case 'c' : /* content search */
5950 if(scan_get_pattern("CONTENTS", pattern
, sizeof(pattern
)))
5962 char tmp
[MAILTMPLEN
];
5965 memset(&args
, 0, sizeof(SCANARG_S
));
5966 args
.pattern
= pattern
;
5968 args
.context
= context
;
5971 args
.stream
= sp_stream_get(context_apply(tmp
, context
,
5972 "xxx", sizeof(tmp
)),
5975 args
.stream
= pine_mail_open(NULL
, tmp
,
5976 OP_SILENT
|OP_HALFOPEN
|SP_USEPOOL
|SP_TEMPUSE
,
5978 args
.newstream
= (args
.stream
!= NULL
);
5982 rv
= foreach_folder(context
, selected
,
5983 foreach_do_scan
, (void *) &args
);
5986 pine_mail_close(args
.stream
);
5992 cmd_cancelled("Select");
5998 foreach_do_scan(FOLDER_S
*f
, void *d
)
6000 SCANARG_S
*sa
= (SCANARG_S
*) d
;
6002 return((sa
->type
== 'n' && srchstr(FLDR_NAME(f
), sa
->pattern
))
6004 && scan_scan_folder(sa
->stream
, sa
->context
, f
, sa
->pattern
)));
6009 scan_scan_folder(MAILSTREAM
*stream
, CONTEXT_S
*context
, FOLDER_S
*f
, char *pattern
)
6014 char *folder
, *ref
= NULL
, tmp
[MAILTMPLEN
];
6017 snprintf(tmp
, sizeof(tmp
), "Scanning \"%s\"", FLDR_NAME(f
));
6018 tmp
[sizeof(tmp
)-1] = '\0';
6019 we_cancel
= busy_cue(tmp
, NULL
, 1);
6021 mm_list_info
= &ldata
; /* tie down global reference */
6022 memset(&ldata
, 0, sizeof(MM_LIST_S
));
6023 ldata
.filter
= mail_list_response
;
6024 memset(ldata
.data
= &response
, 0, sizeof(LISTRES_S
));
6027 * If no preset reference string, must be at top of context
6029 if(context
&& context_isambig(folder
) && !(ref
= context
->dir
->ref
)){
6032 if((p
= strstr(context
->context
, "%s")) != NULL
){
6034 snprintf(tmp
, sizeof(tmp
), "%.*s", (int) MIN(p
- context
->context
, sizeof(tmp
)-1),
6036 tmp
[sizeof(tmp
)-1] = '\0';
6040 snprintf(tmp
, sizeof(tmp
), context
->context
, folder
);
6041 tmp
[sizeof(tmp
)-1] = '\0';
6047 ref
= context
->context
;
6050 mail_scan(stream
, ref
, folder
, pattern
);
6052 if(context
&& context
->dir
&& response
.delim
)
6053 context
->dir
->delim
= response
.delim
;
6056 cancel_busy_cue(-1);
6058 return(((response
.isfile
) ? FEX_ISFILE
: 0)
6059 | ((response
.isdir
) ? FEX_ISDIR
: 0));
6064 folder_select_props(struct pine
*ps
, CONTEXT_S
*context
, int selected
)
6067 long flags
= 0L, count
;
6068 static ESCKEY_S prop_opts
[] = {
6069 {'u', 'u', "U", N_("Unseen msgs")},
6070 {'n', 'n', "N", N_("New msgs")},
6071 {'c', 'c', "C", N_("msg Count")},
6075 if(context
->use
& CNTXT_INCMNG
){
6076 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
6077 _("Select \"Properties\" not supported in Incoming Folders"));
6081 switch(radio_buttons(SEL_PROP_PMT
, -FOOTER_ROWS(ps_global
),
6082 prop_opts
, 'n', 'x', h_folder_prop
, RB_NORM
)){
6083 case 'c' : /* message count */
6084 if(folder_select_count(&count
, &cmp
))
6085 flags
= SA_MESSAGES
;
6089 case 'n' : /* folders with new */
6093 case 'u' : /* folders with unseen */
6104 char tmp
[MAILTMPLEN
];
6107 memset(&args
, 0, sizeof(STATARG_S
));
6109 args
.context
= context
;
6113 args
.stream
= sp_stream_get(context_apply(tmp
, context
,
6114 "xxx", sizeof(tmp
)),
6117 args
.stream
= pine_mail_open(NULL
, tmp
,
6118 OP_SILENT
|OP_HALFOPEN
|SP_USEPOOL
|SP_TEMPUSE
,
6120 args
.newstream
= (args
.stream
!= NULL
);
6123 rv
= foreach_folder(context
, selected
,
6124 foreach_do_stat
, (void *) &args
);
6127 pine_mail_close(args
.stream
);
6133 cmd_cancelled("Select");
6139 folder_select_count(long int *count
, int *cmp
)
6142 char number
[32], prompt
[128];
6143 static char *tense
[] = {"EQUAL TO", "LESS THAN", "GREATER THAN"};
6144 static ESCKEY_S sel_num_opt
[] = {
6145 {ctrl('W'), 14, "^W", N_("Toggle Comparison")},
6151 flags
= OE_APPEND_CURRENT
| OE_DISALLOW_HELP
;
6152 snprintf(number
, sizeof(number
), "%ld", *count
);
6153 number
[sizeof(number
)-1] = '\0';
6154 snprintf(prompt
, sizeof(prompt
), "Select folders with messages %s : ", tense
[*cmp
]);
6155 prompt
[sizeof(prompt
)-1] = '\0';
6156 r
= optionally_enter(number
, -FOOTER_ROWS(ps_global
), 0, sizeof(number
),
6157 prompt
, sel_num_opt
, NO_HELP
, &flags
);
6162 else if((*count
= atol(number
)) < 0L)
6163 q_status_message(SM_ORDER
, 3, 3,
6164 "Can't have NEGATIVE message count!");
6166 return(1); /* success */
6169 case 4 : /* redraw */
6172 case 14 : /* toggle comparison */
6177 case -1 : /* cancel */
6185 return(0); /* return failure */
6190 foreach_do_stat(FOLDER_S
*f
, void *d
)
6192 STATARG_S
*sa
= (STATARG_S
*) d
;
6195 if(ps_global
->context_current
== sa
->context
6196 && !strcmp(ps_global
->cur_folder
, FLDR_NAME(f
))){
6200 case 0 : /* equals */
6201 if(ps_global
->mail_stream
->nmsgs
== sa
->nmsgs
)
6206 case 1 : /* less than */
6207 if(ps_global
->mail_stream
->nmsgs
< sa
->nmsgs
)
6213 if(ps_global
->mail_stream
->nmsgs
> sa
->nmsgs
)
6225 if(count_flagged(ps_global
->mail_stream
, F_RECENT
))
6231 if(count_flagged(ps_global
->mail_stream
, F_UNSEEN
))
6242 char msg_buf
[MAX_BM
+1];
6243 extern MAILSTATUS mm_status_result
;
6245 snprintf(msg_buf
, sizeof(msg_buf
), "Checking %s for %s", FLDR_NAME(f
),
6246 (sa
->flags
== SA_UNSEEN
)
6248 : (sa
->flags
== SA_MESSAGES
) ? "message count"
6249 : "recent messages");
6250 msg_buf
[sizeof(msg_buf
)-1] = '\0';
6251 we_cancel
= busy_cue(msg_buf
, NULL
, 0);
6253 if(!context_status(sa
->context
, sa
->stream
, f
->name
, sa
->flags
))
6254 mm_status_result
.flags
= 0L;
6259 if(sa
->flags
& mm_status_result
.flags
)
6263 case 0 : /* equals */
6264 if(mm_status_result
.messages
== sa
->nmsgs
)
6269 case 1 : /* less than */
6270 if(mm_status_result
.messages
< sa
->nmsgs
)
6276 if(mm_status_result
.messages
> sa
->nmsgs
)
6288 if(mm_status_result
.recent
)
6294 if(mm_status_result
.unseen
)
6309 foreach_folder(CONTEXT_S
*context
, int selected
, int (*test
) (FOLDER_S
*, void *), void *args
)
6312 int we_turned_on
= 0;
6315 we_turned_on
= intr_handling_on();
6317 for(i
= 0, n
= folder_total(FOLDERS(context
)); i
< n
; i
++){
6318 if(ps_global
->intr_pending
){
6320 folder_entry(i
, FOLDERS(context
))->scanned
= 0;
6322 cmd_cancelled("Select");
6327 fp
= folder_entry(i
, FOLDERS(context
));
6329 if((!selected
|| fp
->selected
) && fp
->isfolder
&& (*test
)(fp
, args
))
6334 intr_handling_off();
6340 /*----------------------------------------------------------------------
6341 Return the path delimiter for the given folder on the given server
6343 Args: folder -- folder type for delimiter
6347 folder_delimiter(char *folder
)
6349 int rv
, we_cancel
= 0;
6351 we_cancel
= busy_cue(NULL
, NULL
, 1);
6353 rv
= get_folder_delimiter(folder
);
6356 cancel_busy_cue(-1);
6363 * next_folder - given a current folder in a context, return the next in
6364 * the list, or NULL if no more or there's a problem.
6366 * Args streamp -- If set, try to re-use this stream for checking.
6367 * next -- Put return value here, return points to this
6368 * nextlen -- Length of next
6369 * current -- Current folder, so we know where to start looking
6371 * find_recent -- Returns the number of recent here. The presence of
6372 * this arg also indicates that we should do the calls
6373 * to figure out whether there is a next interesting folder
6375 * did_cancel -- Tell caller if user canceled. Only used if find_recent
6376 * is also set. Also, user will only be given the
6377 * opportunity to cancel if this is set. If it isn't
6378 * set, we just plow ahead when there is an error or
6379 * when the folder does not exist.
6382 next_folder(MAILSTREAM
**streamp
, char *next
, size_t nextlen
, char *current
, CONTEXT_S
*cntxt
, long int *find_recent
, int *did_cancel
)
6384 int index
, recent
= 0, failed_status
= 0, try_fast
;
6387 char tmp
[MAILTMPLEN
];
6390 /* note: find_folders may assign "stream" */
6391 build_folder_list(streamp
, cntxt
, NULL
, NULL
,
6392 NEWS_TEST(cntxt
) ? BFL_LSUB
: BFL_NONE
);
6394 try_fast
= (F_ON(F_ENABLE_FAST_RECENT
, ps_global
) &&
6395 F_OFF(F_TAB_USES_UNSEEN
, ps_global
));
6399 for(index
= folder_index(current
, cntxt
, FI_FOLDER
) + 1;
6401 && index
< folder_total(FOLDERS(cntxt
))
6402 && (f
= folder_entry(index
, FOLDERS(cntxt
)))
6406 MAILSTREAM
*stream
= NULL
;
6407 int rv
, we_cancel
= 0, match
;
6408 char msg_buf
[MAX_BM
+1];
6410 /* must be a folder and it can't be the current one */
6411 if(ps_global
->context_current
== ps_global
->context_list
6412 && !strcmp(ps_global
->cur_folder
, FLDR_NAME(f
)))
6416 * If we already have the folder open, short circuit all this
6420 if((stream
= sp_stream_get(context_apply(tmp
, cntxt
, f
->name
,
6423 || (!IS_REMOTE(tmp
) && (stream
= already_open_stream(tmp
, AOS_NONE
)) != NULL
)){
6424 (void) pine_mail_ping(stream
);
6426 if(F_ON(F_TAB_USES_UNSEEN
, ps_global
)){
6428 * Just fall through and let the status call below handle
6429 * the already open stream. If we were doing this the
6430 * same as the else case, we would figure out how many
6431 * unseen are in this open stream by doing a search.
6432 * Instead of repeating that code that is already in
6433 * pine_mail_status_full, fall through and note the
6434 * special case by lighting the match variable.
6439 *find_recent
= sp_recent_since_visited(stream
);
6449 snprintf(msg_buf
, sizeof(msg_buf
), "Checking %s for %s messages",
6450 FLDR_NAME(f
), F_ON(F_TAB_USES_UNSEEN
, ps_global
) ? "unseen" : "recent");
6451 msg_buf
[sizeof(msg_buf
)-1] = '\0';
6452 we_cancel
= busy_cue(msg_buf
, NULL
, 0);
6454 /* First, get a stream for the test */
6455 if(!stream
&& streamp
&& *streamp
){
6456 if(context_same_stream(cntxt
, f
->name
, *streamp
)){
6460 pine_mail_close(*streamp
);
6466 stream
= sp_stream_get(context_apply(tmp
, cntxt
, f
->name
,
6472 * If interestingness is indeterminate or we're
6473 * told to explicitly, look harder...
6477 * We could make this more efficient in the cases where we're
6478 * opening a new stream or using streamp by having folder_exists
6479 * cache the stream. The change would require a folder_exists()
6480 * that caches streams, but most of the time folder_exists just
6481 * uses the inbox stream or ps->mail_stream.
6483 * Another thing to consider is that maybe there should be an
6484 * option to try to LIST a folder before doing a STATUS (or SELECT).
6485 * This isn't done by default for the case where a folder is
6486 * SELECTable but not LISTable, but on some servers doing an
6487 * RLIST first tells the server that we support mailbox referrals.
6490 || !((rv
= folder_exists(cntxt
,f
->name
))
6491 & (FEX_ISMARKED
| FEX_UNMARKED
))){
6492 extern MAILSTATUS mm_status_result
;
6494 if(try_fast
&& (rv
== 0 || rv
& FEX_ERROR
)){
6496 mm_status_result
.flags
= 0L;
6500 if(!context_status_full(cntxt
, match
? NULL
: stream
,
6502 F_ON(F_TAB_USES_UNSEEN
, ps_global
)
6503 ? SA_UNSEEN
: SA_RECENT
,
6507 mm_status_result
.flags
= 0L;
6511 /* so we can re-use the stream */
6512 if(!context_status_streamp_full(cntxt
, streamp
, f
->name
,
6513 F_ON(F_TAB_USES_UNSEEN
, ps_global
)
6514 ? SA_UNSEEN
: SA_RECENT
,
6518 mm_status_result
.flags
= 0L;
6523 if(F_ON(F_TAB_USES_UNSEEN
, ps_global
)){
6524 rv
= ((mm_status_result
.flags
& SA_UNSEEN
)
6525 && (*find_recent
= mm_status_result
.unseen
))
6529 rv
= ((mm_status_result
.flags
& SA_RECENT
)
6530 && (*find_recent
= mm_status_result
.recent
))
6534 /* we don't know how many in this case */
6536 *find_recent
= 0L; /* consistency, boy! */
6542 if(failed_status
&& did_cancel
){
6543 char buf1
[6*MAX_SCREEN_COLS
+1];
6546 snprintf(prompt
, sizeof(prompt
), _("Check of folder %s failed. Continue "), FLDR_NAME(f
));
6547 if(utf8_width(prompt
) > MAXPROMPT
){
6548 snprintf(prompt
, sizeof(prompt
), _("Check of %s failed. Continue "), FLDR_NAME(f
));
6549 if((wid1
=utf8_width(prompt
)) > MAXPROMPT
){
6550 if((wid2
=utf8_width(FLDR_NAME(f
))) > wid1
-MAXPROMPT
)
6551 snprintf(prompt
, sizeof(prompt
), _("Check of %s failed. Continue "), strsquish(buf1
, sizeof(buf1
), FLDR_NAME(f
), wid2
-(wid1
-MAXPROMPT
)));
6553 snprintf(prompt
, sizeof(prompt
), _("Check failed. Continue "));
6557 if(want_to(prompt
, 'y', 0, NO_HELP
, WT_NORM
) == 'n'){
6562 /* have to reset this lower-level cancel marker */
6563 ps_global
->user_says_cancel
= 0;
6568 if(rv
& FEX_ISMARKED
){
6574 if(f
&& (!find_recent
|| recent
)){
6575 strncpy(next
, FLDR_NAME(f
), nextlen
);
6576 next
[nextlen
-1] = '\0';
6578 else if(nextlen
> 0)
6581 /* BUG: how can this be made smarter so we cache the list? */
6582 free_folder_list(cntxt
);
6583 return((*next
) ? next
: NULL
);
6587 /*----------------------------------------------------------------------
6588 Shuffle order of incoming folders
6591 shuffle_incoming_folders(CONTEXT_S
*context
, int index
)
6593 int tot
, i
, deefault
, rv
, inheriting
= 0;
6594 int new_index
, index_within_var
, new_index_within_var
;
6602 PINERC_S
*prc
= NULL
;
6604 dprint((4, "shuffle_incoming_folders\n"));
6606 if(!(context
->use
& CNTXT_INCMNG
) ||
6607 (tot
= folder_total(FOLDERS(context
))) < 2 ||
6608 index
< 0 || index
>= tot
)
6612 q_status_message(SM_ORDER
,0,3, _("Cannot shuffle INBOX"));
6616 fp
= folder_entry(index
, FOLDERS(context
));
6617 ew
= config_containing_inc_fldr(fp
);
6619 if(ps_global
->restricted
)
6624 prc
= ps_global
->prc
;
6627 prc
= ps_global
->post_prc
;
6633 readonly
= prc
? prc
->readonly
: 1;
6636 if(prc
&& prc
->quit_to_edit
){
6637 quit_to_edit_msg(prc
);
6642 q_status_message(SM_ORDER
,3,5,
6643 _("Shuffle cancelled: config file not editable"));
6647 alval
= ALVAL(&ps_global
->vars
[V_INCOMING_FOLDERS
], ew
);
6649 if(!(alval
&& *alval
))
6656 opts
[i
++].label
= N_("Back");
6661 opts
[i
++].label
= N_("Forward");
6666 /* find where this entry is in the particular config list */
6667 index_within_var
= -1;
6668 for(i
= 0; (*alval
)[i
]; i
++){
6669 expand_variables(tmp_20k_buf
, SIZEOF_20KBUF
, (*alval
)[i
], 0);
6670 if(i
== 0 && !strcmp(tmp_20k_buf
, INHERIT
))
6672 else if(fp
->varhash
== line_hash(tmp_20k_buf
)){
6673 index_within_var
= i
;
6678 if(index_within_var
== -1){ /* didn't find it */
6679 q_status_message(SM_ORDER
,3,5,
6680 _("Shuffle cancelled: unexpected trouble shuffling"));
6684 if(index_within_var
== 0 || (inheriting
&& index_within_var
== 1)){
6685 opts
[0].ch
= -2; /* no back */
6689 if(!(*alval
)[i
+1]) /* no forward */
6692 if(opts
[0].ch
== -2 && opts
[1].ch
== -2){
6693 q_status_message(SM_ORDER
, 0, 4,
6694 _("Cannot shuffle from one config file to another."));
6698 snprintf(tmp
, sizeof(tmp
), "Shuffle \"%s\" %s%s%s ? ",
6699 FLDR_NAME(folder_entry(index
, FOLDERS(context
))),
6700 (opts
[0].ch
!= -2) ? "BACK" : "",
6701 (opts
[0].ch
!= -2 && opts
[1].ch
!= -2) ? " or " : "",
6702 (opts
[1].ch
!= -2) ? "FORWARD" : "");
6703 tmp
[sizeof(tmp
)-1] = '\0';
6704 help
= (opts
[0].ch
== -2) ? h_incoming_shuf_down
6705 : (opts
[1].ch
== -2) ? h_incoming_shuf_up
6708 rv
= radio_buttons(tmp
, -FOOTER_ROWS(ps_global
), opts
, deefault
, 'x',
6712 new_index_within_var
= index_within_var
;
6716 cmd_cancelled("Shuffle");
6720 new_index_within_var
--;
6725 new_index_within_var
++;
6730 if(swap_incoming_folders(index
, new_index
, FOLDERS(context
))){
6733 /* swap them in the config variable, too */
6734 stmp
= (*alval
)[index_within_var
];
6735 (*alval
)[index_within_var
] = (*alval
)[new_index_within_var
];
6736 (*alval
)[new_index_within_var
] = stmp
;
6738 set_current_val(&ps_global
->vars
[V_INCOMING_FOLDERS
], TRUE
, FALSE
);
6739 write_pinerc(ps_global
, ew
, WRP_NONE
);
6749 swap_incoming_folders(int index1
, int index2
, FLIST
*flist
)
6756 if(index1
== index2
)
6759 if(index1
< 0 || index1
>= flist
->used
){
6760 dprint((1, "Error in swap_incoming_folders: index1=%d, used=%d\n", index1
, flist
->used
));
6764 if(index2
< 0 || index2
>= flist
->used
){
6765 dprint((1, "Error in swap_incoming_folders: index2=%d, used=%d\n", index2
, flist
->used
));
6769 ftmp
= flist
->folders
[index1
];
6770 flist
->folders
[index1
] = flist
->folders
[index2
];
6771 flist
->folders
[index2
] = ftmp
;
6777 /*----------------------------------------------------------------------
6778 Find an entry in the folder list by matching names
6781 search_folder_list(void *list
, char *name
)
6786 for(i
= 0; i
< folder_total(list
); i
++) {
6787 n
= folder_entry(i
, list
)->name
;
6788 if(strucmp(name
, n
) == 0)
6789 return(1); /* Found it */
6795 static CONTEXT_S
*post_cntxt
= NULL
;
6797 /*----------------------------------------------------------------------
6798 Browse list of newsgroups available for posting
6800 Called from composer when ^T is typed in newsgroups field
6804 Returns: pointer to selected newsgroup, or NULL.
6805 Selector call in composer expects this to be alloc'd here.
6809 news_group_selector(char **error_mess
)
6816 /* Coming back from composer */
6817 fix_windsize(ps_global
);
6820 post_folder
= fs_get((size_t)MAILTMPLEN
);
6822 /*--- build the post_cntxt -----*/
6823 em
= get_post_list(ps_global
->VAR_NNTP_SERVER
);
6825 if(error_mess
!= NULL
)
6826 *error_mess
= cpystr(em
);
6828 cancel_busy_cue(-1);
6832 /*----- Call the browser -------*/
6834 if((rc
= folders_for_post(ps_global
, &tc
, post_folder
)) != 0)
6837 cancel_busy_cue(-1);
6842 return(post_folder
);
6846 /*----------------------------------------------------------------------
6847 Get the list of news groups that are possible for posting
6849 Args: post_host -- host name for posting
6851 Returns NULL if list is retrieved, pointer to error message if failed
6853 This is kept in a standards "CONTEXT" for a acouple of reasons. First
6854 it makes it very easy to use the folder browser to display the
6855 newsgroup for selection on ^T from the composer. Second it will allow
6856 the same mechanism to be used for all folder lists on memory tight
6857 systems like DOS. The list is kept for the life of the session because
6858 fetching it is a expensive.
6862 get_post_list(char **post_host
)
6864 char *post_context_string
;
6866 if(!post_host
|| !post_host
[0]) {
6867 /* BUG should assume inews and get this from active file */
6868 return(_("Can't post messages, NNTP server needs to be configured"));
6875 we_cancel
= busy_cue(_("Getting full list of groups for posting"), NULL
, 1);
6877 l
= strlen(post_host
[0]) + 20;
6878 post_context_string
= (char *) fs_get((l
+1) * sizeof(char));
6879 snprintf(post_context_string
, l
+1, "{%s/nntp}#news.[]", post_host
[0]);
6880 post_context_string
[l
] = '\0';
6882 post_cntxt
= new_context(post_context_string
, NULL
);
6883 post_cntxt
->use
|= CNTXT_FINDALL
;
6884 post_cntxt
->dir
->status
|= CNTXT_NOFIND
;
6885 post_cntxt
->next
= NULL
;
6887 build_folder_list(NULL
, post_cntxt
, NULL
, NULL
,
6888 NEWS_TEST(post_cntxt
) ? BFL_LSUB
: BFL_NONE
);
6890 cancel_busy_cue(-1);
6897 fcc_tab_complete (char *prefix
, char **answer
, int tabtab
, unsigned flags
)
6899 char tmp
[MAILTMPLEN
+1];
6903 if(prefix
== NULL
|| *prefix
== '\0')
6906 for(mc
= ps_global
->context_list
; mc
!= NULL
; mc
= mc
->next
)
6907 if(mc
->use
& CNTXT_SAVEDFLT
)
6910 if(mc
== NULL
) return 0;
6912 strncpy(tmp
, prefix
, sizeof(tmp
));
6913 tmp
[sizeof(tmp
)-1] = '\0';
6915 if(!folder_complete(mc
, tmp
, sizeof(tmp
), &n
)){
6918 display_folder_list(&mc
, tmp
, 1,folders_for_goto
);
6924 *answer
= cpystr(tmp
);
6925 return n
== 0 ? 0 : n
== 1 ? 2 : 1;
6932 folder_list_popup(sparms
, in_handle
)
6936 MPopup fldr_popup
[20];
6938 memset(fldr_popup
, 0, 20 * sizeof(MPopup
));
6939 fldr_popup
[0].type
= tTail
;
6942 HANDLE_S
*h
= get_handle(sparms
->text
.handles
, in_handle
);
6943 FOLDER_S
*fp
= (h
) ? folder_entry(h
->h
.f
.index
,
6944 FOLDERS(h
->h
.f
.context
))
6947 if((i
= menu_binding_index(sparms
->keys
.menu
, MC_CHOICE
)) >= 0){
6948 fldr_popup
[n
].type
= tQueue
;
6949 fldr_popup
[n
].data
.val
= sparms
->keys
.menu
->keys
[i
].bind
.ch
[0];
6950 fldr_popup
[n
].label
.style
= lNormal
;
6951 fldr_popup
[n
++].label
.string
= (fp
&& fp
->isdir
)
6956 if((i
= menu_binding_index(sparms
->keys
.menu
, MC_SELCUR
)) >= 0
6957 && bitnset(i
, sparms
->keys
.bitmap
)){
6958 fldr_popup
[n
].type
= tQueue
;
6959 fldr_popup
[n
].data
.val
= sparms
->keys
.menu
->keys
[i
].bind
.ch
[0];
6960 fldr_popup
[n
].label
.style
= lNormal
;
6961 fldr_popup
[n
++].label
.string
= (fp
&& fp
->isdir
)
6962 ? "&Select Directory"
6966 if((i
= menu_binding_index(sparms
->keys
.menu
, MC_DELETE
)) >= 0
6967 && bitnset(i
, sparms
->keys
.bitmap
)){
6968 fldr_popup
[n
].type
= tQueue
;
6969 fldr_popup
[n
].data
.val
= sparms
->keys
.menu
->keys
[i
].bind
.ch
[0];
6970 fldr_popup
[n
].label
.style
= lNormal
;
6971 fldr_popup
[n
++].label
.string
= "Delete Folder";
6974 if((i
= menu_binding_index(sparms
->keys
.menu
, MC_EXPORT
)) >= 0
6975 && bitnset(i
, sparms
->keys
.bitmap
)){
6976 fldr_popup
[n
].type
= tQueue
;
6977 fldr_popup
[n
].data
.val
= sparms
->keys
.menu
->keys
[i
].bind
.ch
[0];
6978 fldr_popup
[n
].label
.style
= lNormal
;
6979 fldr_popup
[n
++].label
.string
= "Export Folder";
6982 if((i
= menu_binding_index(sparms
->keys
.menu
, MC_CHK_RECENT
)) >= 0
6983 && bitnset(i
, sparms
->keys
.bitmap
)){
6984 fldr_popup
[n
].type
= tQueue
;
6985 fldr_popup
[n
].data
.val
= sparms
->keys
.menu
->keys
[i
].bind
.ch
[0];
6986 fldr_popup
[n
].label
.style
= lNormal
;
6987 fldr_popup
[n
++].label
.string
= "Check New Messages";
6991 fldr_popup
[n
++].type
= tSeparator
;
6993 folder_popup_config(((FPROC_S
*)sparms
->proc
.data
.p
)->fs
,
6994 sparms
->keys
.menu
, &fldr_popup
[n
]);
6997 folder_popup_config(((FPROC_S
*)sparms
->proc
.data
.p
)->fs
,
6998 sparms
->keys
.menu
, fldr_popup
);
7000 if(fldr_popup
[0].type
!= tTail
)
7001 mswin_popup(fldr_popup
);
7008 folder_list_select_popup(sparms
, in_handle
)
7012 MPopup fldr_popup
[20];
7014 memset(fldr_popup
, 0, 20 * sizeof(MPopup
));
7015 fldr_popup
[0].type
= tTail
;
7018 HANDLE_S
*h
= get_handle(sparms
->text
.handles
, in_handle
);
7019 FOLDER_S
*fp
= (h
) ? folder_entry(h
->h
.f
.index
,FOLDERS(h
->h
.f
.context
))
7022 if((i
= menu_binding_index(sparms
->keys
.menu
, MC_CHOICE
)) >= 0){
7023 fldr_popup
[n
].type
= tQueue
;
7024 fldr_popup
[n
].data
.val
= sparms
->keys
.menu
->keys
[i
].bind
.ch
[0];
7025 fldr_popup
[n
].label
.style
= lNormal
;
7026 fldr_popup
[n
++].label
.string
= (fp
&& fp
->isdir
)
7030 fldr_popup
[n
++].type
= tSeparator
;
7033 folder_popup_config(((FPROC_S
*)sparms
->proc
.data
.p
)->fs
,
7034 sparms
->keys
.menu
, &fldr_popup
[n
]);
7037 folder_popup_config(((FPROC_S
*)sparms
->proc
.data
.p
)->fs
,
7038 sparms
->keys
.menu
, fldr_popup
);
7040 if(fldr_popup
[0].type
!= tTail
)
7041 mswin_popup(fldr_popup
);
7048 * Just a little something to simplify assignments
7050 #define FLDRPOPUP(p, c, s) { \
7051 (p)->type = tQueue; \
7052 (p)->data.val = c; \
7053 (p)->label.style = lNormal; \
7054 (p)->label.string = s; \
7058 /*----------------------------------------------------------------------
7059 Popup Menu configurator
7063 folder_popup_config(fs
, km
, popup
)
7065 struct key_menu
*km
;
7070 if((i
= menu_binding_index(km
, MC_PARENT
)) >= 0){
7071 FLDRPOPUP(popup
, km
->keys
[i
].bind
.ch
[0], "Parent Directory");
7075 if(fs
->km
== &folder_km
){
7076 if((fs
->context
->next
|| fs
->context
->prev
) && !fs
->combined_view
){
7077 FLDRPOPUP(popup
, '<', "Collection List");
7081 else if((i
= menu_binding_index(km
, MC_COLLECTIONS
)) >= 0){
7082 FLDRPOPUP(popup
, km
->keys
[i
].bind
.ch
[0], "Collection List");
7086 if((i
= menu_binding_index(km
, MC_INDEX
)) >= 0){
7087 FLDRPOPUP(popup
, km
->keys
[i
].bind
.ch
[0], "Current Folder Index");
7091 if((i
= menu_binding_index(km
, MC_MAIN
)) >= 0){
7092 FLDRPOPUP(popup
, km
->keys
[i
].bind
.ch
[0], "Main Menu");
7096 popup
->type
= tTail
; /* tie off the array */
7098 #endif /* _WINDOWS */