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