1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: mailcmd.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
6 * ========================================================================
7 * Copyright 2013-2018 Eduardo Chappa
8 * Copyright 2006-2009 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 /*======================================================================
21 The meat and pototoes of mail processing here:
22 - initial command processing and dispatch
24 - capture address off incoming mail
25 - jump to specific numbered message
26 - open (broach) a new folder
27 - search message headers (where is) command
35 #include "flagmaint.h"
52 #include "../pith/state.h"
53 #include "../pith/msgno.h"
54 #include "../pith/store.h"
55 #include "../pith/thread.h"
56 #include "../pith/flag.h"
57 #include "../pith/sort.h"
58 #include "../pith/maillist.h"
59 #include "../pith/save.h"
60 #include "../pith/pipe.h"
61 #include "../pith/news.h"
62 #include "../pith/util.h"
63 #include "../pith/sequence.h"
64 #include "../pith/keyword.h"
65 #include "../pith/stream.h"
66 #include "../pith/mailcmd.h"
67 #include "../pith/hist.h"
68 #include "../pith/list.h"
69 #include "../pith/icache.h"
70 #include "../pith/busy.h"
71 #include "../pith/mimedesc.h"
72 #include "../pith/pattern.h"
73 #include "../pith/tempfile.h"
74 #include "../pith/search.h"
75 #include "../pith/margin.h"
77 #include "../pico/osdep/mswin.h"
83 int cmd_flag(struct pine
*, MSGNO_S
*, int);
84 int cmd_flag_prompt(struct pine
*, struct flag_screen
*, int);
85 void free_flag_table(struct flag_table
**);
86 int cmd_reply(struct pine
*, MSGNO_S
*, int, ACTION_S
*);
87 int cmd_forward(struct pine
*, MSGNO_S
*, int, ACTION_S
*);
88 int cmd_bounce(struct pine
*, MSGNO_S
*, int, ACTION_S
*);
89 int cmd_save(struct pine
*, MAILSTREAM
*, MSGNO_S
*, int, CmdWhere
);
90 void role_compose(struct pine
*);
91 int cmd_expunge(struct pine
*, MAILSTREAM
*, MSGNO_S
*, int);
92 int cmd_export(struct pine
*, MSGNO_S
*, int, int);
93 char *cmd_delete_action(struct pine
*, MSGNO_S
*, CmdWhere
);
94 char *cmd_delete_view(struct pine
*, MSGNO_S
*);
95 char *cmd_delete_index(struct pine
*, MSGNO_S
*);
96 long get_level(int, UCS
, SCROLL_S
*);
97 long closest_jump_target(long, MAILSTREAM
*, MSGNO_S
*, int, CmdWhere
, char *, size_t);
98 int update_folder_spec(char *, size_t, char *);
99 int cmd_print(struct pine
*, MSGNO_S
*, int, CmdWhere
);
100 int cmd_pipe(struct pine
*, MSGNO_S
*, int);
101 STORE_S
*list_mgmt_text(RFC2369_S
*, long);
102 void list_mgmt_screen(STORE_S
*);
103 int aggregate_select(struct pine
*, MSGNO_S
*, int, CmdWhere
);
104 int select_by_number(MAILSTREAM
*, MSGNO_S
*, SEARCHSET
**);
105 int select_by_thrd_number(MAILSTREAM
*, MSGNO_S
*, SEARCHSET
**);
106 int select_by_date(MAILSTREAM
*, MSGNO_S
*, long, SEARCHSET
**);
107 int select_by_text(MAILSTREAM
*, MSGNO_S
*, long, SEARCHSET
**);
108 int select_by_size(MAILSTREAM
*, SEARCHSET
**);
109 SEARCHSET
*visible_searchset(MAILSTREAM
*, MSGNO_S
*);
110 int select_by_status(MAILSTREAM
*, SEARCHSET
**);
111 int select_by_rule(MAILSTREAM
*, SEARCHSET
**);
112 int select_by_thread(MAILSTREAM
*, MSGNO_S
*, SEARCHSET
**);
113 char *choose_a_rule(int);
114 int select_by_keyword(MAILSTREAM
*, SEARCHSET
**);
115 char *choose_a_keyword(void);
116 int select_sort(struct pine
*, int, SortOrder
*, int *);
117 int print_index(struct pine
*, MSGNO_S
*, int);
120 * List of Select options used by apply_* functions...
122 static char *sel_pmt1
= N_("ALTER message selection : ");
123 ESCKEY_S sel_opts1
[] = {
124 /* TRANSLATORS: these are keymenu names for selecting. Broaden selection means
125 we will add more messages to the selection, Narrow selection means we will
126 remove some selections (like a logical AND instead of logical OR), and Flip
127 Selected means that all the messages that are currently selected become unselected,
128 and all the unselected messages become selected. */
129 {'a', 'a', "A", N_("unselect All")},
130 {'c', 'c', "C", NULL
},
131 {'b', 'b', "B", N_("Broaden selctn")},
132 {'n', 'n', "N", N_("Narrow selctn")},
133 {'f', 'f', "F", N_("Flip selected")},
138 #define SEL_OPTS_THREAD 9 /* index number of "tHread" */
139 #define SEL_OPTS_THREAD_CH 'h'
141 char *sel_pmt2
= "SELECT criteria : ";
142 static ESCKEY_S sel_opts2
[] = {
143 /* TRANSLATORS: very short descriptions of message selection criteria. Select Cur
144 means select the currently highlighted message; select by Number is by message
145 number; Status is by status of the message, for example the message might be
146 New or it might be Unseen or marked Important; Size has the Z upper case because
147 it is a Z command; Keyword is an alpine keyword that has been set by the user;
148 and Rule is an alpine rule */
149 {'a', 'a', "A", N_("select All")},
150 {'c', 'c', "C", N_("select Cur")},
151 {'n', 'n', "N", N_("Number")},
152 {'d', 'd', "D", N_("Date")},
153 {'t', 't', "T", N_("Text")},
154 {'s', 's', "S", N_("Status")},
155 {'z', 'z', "Z", N_("siZe")},
156 {'k', 'k', "K", N_("Keyword")},
157 {'r', 'r', "R", N_("Rule")},
158 {SEL_OPTS_THREAD_CH
, 'h', "H", N_("tHread")},
163 static ESCKEY_S sel_opts3
[] = {
164 /* TRANSLATORS: these are operations we can do on a set of selected messages.
165 Del is Delete; Undel is Undelete; TakeAddr means to Take some Addresses into
166 the address book; Save means to save the messages into another alpine folder;
167 Export means to copy the messages to a file outside of alpine, external to
169 {'d', 'd', "D", N_("Del")},
170 {'u', 'u', "U", N_("Undel")},
171 {'r', 'r', "R", N_("Reply")},
172 {'f', 'f', "F", N_("Forward")},
173 {'%', '%', "%", N_("Print")},
174 {'t', 't', "T", N_("TakeAddr")},
175 {'s', 's', "S", N_("Save")},
176 {'e', 'e', "E", N_("Export")},
177 { -1, 0, NULL
, NULL
},
178 { -1, 0, NULL
, NULL
},
179 { -1, 0, NULL
, NULL
},
180 { -1, 0, NULL
, NULL
},
181 { -1, 0, NULL
, NULL
},
182 { -1, 0, NULL
, NULL
},
183 { -1, 0, NULL
, NULL
},
187 static ESCKEY_S sel_opts4
[] = {
188 {'a', 'a', "A", N_("select All")},
189 /* TRANSLATORS: select currrently highlighted message Thread */
190 {'c', 'c', "C", N_("select Curthrd")},
191 {'n', 'n', "N", N_("Number")},
192 {'d', 'd', "D", N_("Date")},
193 {'t', 't', "T", N_("Text")},
194 {'s', 's', "S", N_("Status")},
195 {'z', 'z', "Z", N_("siZe")},
196 {'k', 'k', "K", N_("Keyword")},
197 {'r', 'r', "R", N_("Rule")},
198 {SEL_OPTS_THREAD_CH
, 'h', "H", N_("tHread")},
203 static char *sel_flag
=
204 N_("Select New, Deleted, Answered, Forwarded, or Important messages ? ");
205 static char *sel_flag_not
=
206 N_("Select NOT New, NOT Deleted, NOT Answered, NOT Forwarded or NOT Important msgs ? ");
207 static ESCKEY_S sel_flag_opt
[] = {
208 /* TRANSLATORS: When selecting messages by message Status these are the
209 different types of Status you can select on. Is the message New, Recent,
210 and so on. Not means flip the meaning of the selection to the opposite
211 thing, so message is not New or not Important. */
212 {'n', 'n', "N", N_("New")},
213 {'*', '*', "*", N_("Important")},
214 {'d', 'd', "D", N_("Deleted")},
215 {'a', 'a', "A", N_("Answered")},
216 {'f', 'f', "F", N_("Forwarded")},
218 {'!', '!', "!", N_("Not")},
220 {'r', 'r', "R", N_("Recent")},
221 {'u', 'u', "U", N_("Unseen")},
226 static ESCKEY_S sel_date_opt
[] = {
228 /* TRANSLATORS: options when selecting messages by Date */
229 {ctrl('P'), 12, "^P", N_("Prev Day")},
230 {ctrl('N'), 13, "^N", N_("Next Day")},
231 {ctrl('X'), 11, "^X", N_("Cur Msg")},
232 {ctrl('W'), 14, "^W", N_("Toggle When")},
233 {KEY_UP
, 12, "", ""},
234 {KEY_DOWN
, 13, "", ""},
239 static char *sel_text
=
240 N_("Select based on To, From, Cc, Recip, Partic, Subject fields or All msg text ? ");
241 static char *sel_text_not
=
242 N_("Select based on NOT To, From, Cc, Recip, Partic, Subject or All msg text ? ");
243 static ESCKEY_S sel_text_opt
[] = {
244 /* TRANSLATORS: Select messages based on the text contained in the From line, or
245 the Subject line, and so on. */
246 {'f', 'f', "F", N_("From")},
247 {'s', 's', "S", N_("Subject")},
248 {'t', 't', "T", N_("To")},
249 {'a', 'a', "A", N_("All Text")},
250 {'c', 'c', "C", N_("Cc")},
251 {'!', '!', "!", N_("Not")},
252 {'r', 'r', "R", N_("Recipient")},
253 {'p', 'p', "P", N_("Participant")},
254 {'b', 'b', "B", N_("Body")},
255 {'h', 'h', "H", N_("Header")},
259 static ESCKEY_S choose_action
[] = {
260 {'c', 'c', "C", N_("Compose")},
261 {'r', 'r', "R", N_("Reply")},
262 {'f', 'f', "F", N_("Forward")},
263 {'b', 'b', "B", N_("Bounce")},
267 static char *select_num
=
268 N_("Enter comma-delimited list of numbers (dash between ranges): ");
270 static char *select_size_larger_msg
=
271 N_("Select messages with size larger than: ");
273 static char *select_size_smaller_msg
=
274 N_("Select messages with size smaller than: ");
276 static char *sel_size_larger
= N_("Larger");
277 static char *sel_size_smaller
= N_("Smaller");
278 static ESCKEY_S sel_size_opt
[] = {
280 {ctrl('W'), 14, "^W", NULL
},
284 static ESCKEY_S sel_key_opt
[] = {
286 {ctrl('T'), 14, "^T", N_("To List")},
288 {'!', '!', "!", N_("Not")},
292 static ESCKEY_S flag_text_opt
[] = {
293 /* TRANSLATORS: these are types of flags (markers) that the user can
294 set. For example, they can flag the message as an important message. */
295 {'n', 'n', "N", N_("New")},
296 {'*', '*', "*", N_("Important")},
297 {'d', 'd', "D", N_("Deleted")},
298 {'a', 'a', "A", N_("Answered")},
299 {'f', 'f', "F", N_("Forwarded")},
300 {'!', '!', "!", N_("Not")},
301 {ctrl('T'), 10, "^T", N_("To Flag Details")},
306 alpine_smime_confirm_save(char *email
)
310 snprintf(prompt
, sizeof(prompt
), _("Save certificate for <%s>"),
311 email
? email
: _("missing address"));
312 return want_to(prompt
, 'n', 'x', NO_HELP
, WT_NORM
) == 'y';
316 alpine_get_password(char *prompt
, char *pass
, size_t len
)
318 int flags
= OE_PASSWD
| OE_DISALLOW_HELP
;
320 return optionally_enter(pass
,
321 -(ps_global
->ttyo
? FOOTER_ROWS(ps_global
) : 3),
322 0, len
, prompt
, NULL
, NO_HELP
, &flags
);
326 smime_import_certificate(char *filename
, char *full_filename
, char *what
, size_t len
)
329 static HISTORY_S
*history
= NULL
;
330 static ESCKEY_S eopts
[] = {
331 {ctrl('T'), 10, "^T", N_("To Files")},
333 {-1, 0, NULL
, NULL
}};
335 /* special call to free history */
336 if(filename
== NULL
&& full_filename
== NULL
&& what
== NULL
&& len
== 0){
342 if(F_ON(F_ENABLE_TAB_COMPLETE
,ps_global
)){
343 eopts
[r
].ch
= ctrl('I');
345 eopts
[r
].name
= "TAB";
346 eopts
[r
].label
= N_("Complete");
352 full_filename
[0] = '\0';
354 r
= get_export_filename(ps_global
, filename
, NULL
, full_filename
,
355 len
, what
, "IMPORT", eopts
, NULL
,
356 -FOOTER_ROWS(ps_global
), GE_IS_IMPORT
, &history
);
362 /*----------------------------------------------------------------------
363 The giant switch on the commands for index and viewing
365 Input: command -- The command char/code
366 in_index -- flag indicating command is from index
367 orig_command -- The original command typed before pre-processing
368 Output: force_mailchk -- Set to tell caller to force call to new_mail().
372 Returns 1 if the message number or attachment to show changed
375 process_cmd(struct pine
*state
, MAILSTREAM
*stream
, MSGNO_S
*msgmap
,
376 int command
, CmdWhere in_index
, int *force_mailchk
)
378 int question_line
, a_changed
, flags
= 0, ret
, j
;
380 long new_msgno
, del_count
, old_msgno
, i
;
382 char *newfolder
, prompt
[MAX_SCREEN_COLS
+1];
385 #if defined(DOS) && !defined(_WINDOWS)
386 extern long coreleft();
389 dprint((4, "\n - process_cmd(cmd=%d) -\n", command
));
391 question_line
= -FOOTER_ROWS(state
);
392 state
->mangled_screen
= 0;
393 state
->mangled_footer
= 0;
394 state
->mangled_header
= 0;
395 state
->next_screen
= SCREEN_FUN_NULL
;
396 old_msgno
= mn_get_cur(msgmap
);
401 /*------------- Help --------*/
404 * We're not using the h_mail_view portion of this right now because
405 * that call is being handled in scrolltool() before it gets
406 * here. Leave it in case we change how it works.
408 helper((in_index
== MsgIndx
)
412 : h_mail_thread_index
,
413 (in_index
== MsgIndx
)
414 ? _("HELP FOR MESSAGE INDEX")
416 ? _("HELP FOR MESSAGE TEXT")
417 : _("HELP FOR THREAD INDEX"),
419 dprint((4,"MAIL_CMD: did help command\n"));
420 state
->mangled_screen
= 1;
424 /*--------- Return to main menu ------------*/
426 state
->next_screen
= main_menu_screen
;
427 dprint((2,"MAIL_CMD: going back to main menu\n"));
431 /*------- View message text --------*/
434 if(any_messages(msgmap
, NULL
, "to View")){
435 state
->next_screen
= mail_view_screen
;
441 /*------- View attachment --------*/
443 state
->next_screen
= attachment_screen
;
444 dprint((2,"MAIL_CMD: going to attachment screen\n"));
448 /*---------- Previous message ----------*/
450 if(any_messages(msgmap
, NULL
, NULL
)){
451 if((i
= mn_get_cur(msgmap
)) > 1L){
452 mn_dec_cur(stream
, msgmap
,
453 (in_index
== View
&& THREADING()
454 && sp_viewing_a_thread(stream
))
457 ? MH_ANYTHD
: MH_NONE
);
458 if(i
== mn_get_cur(msgmap
)){
459 PINETHRD_S
*thrd
= NULL
, *topthrd
= NULL
;
461 if(THRD_INDX_ENABLED()){
462 mn_dec_cur(stream
, msgmap
, MH_ANYTHD
);
463 if(i
== mn_get_cur(msgmap
))
464 q_status_message1(SM_ORDER
, 0, 2,
465 _("Already on first %s in Zoomed Index"),
466 THRD_INDX() ? _("thread") : _("message"));
469 || F_ON(F_NEXT_THRD_WO_CONFIRM
, state
))
472 ret
= want_to(_("View previous thread"), 'y', 'x',
476 q_status_message(SM_ORDER
, 0, 2,
477 _("Viewing previous thread"));
478 new_msgno
= mn_get_cur(msgmap
);
479 mn_set_cur(msgmap
, i
);
480 if(unview_thread(state
, stream
, msgmap
)){
481 state
->next_screen
= mail_index_screen
;
482 state
->view_skipped_index
= 0;
483 state
->mangled_screen
= 1;
486 mn_set_cur(msgmap
, new_msgno
);
487 if(THRD_AUTO_VIEW() && in_index
== View
){
489 thrd
= fetch_thread(stream
,
492 if(count_lflags_in_thread(stream
, thrd
,
495 if(view_thread(state
, stream
, msgmap
, 1)){
496 if(current_index_state
)
497 msgmap
->top_after_thrd
= current_index_state
->msg_at_top
;
499 state
->view_skipped_index
= 1;
500 command
= MC_VIEW_TEXT
;
507 if(THRD_AUTO_VIEW() && in_index
!= View
){
508 thrd
= fetch_thread(stream
, mn_m2raw(msgmap
, new_msgno
));
509 if(thrd
&& thrd
->top
)
510 topthrd
= fetch_thread(stream
, thrd
->top
);
513 j
= count_lflags_in_thread(stream
, topthrd
, msgmap
, MN_NONE
);
516 if(!THRD_AUTO_VIEW() || in_index
== View
|| j
!= 1){
517 if(view_thread(state
, stream
, msgmap
, 1)
518 && current_index_state
)
519 msgmap
->top_after_thrd
= current_index_state
->msg_at_top
;
523 state
->next_screen
= SCREEN_FUN_NULL
;
526 mn_set_cur(msgmap
, i
); /* put it back */
530 q_status_message1(SM_ORDER
, 0, 2,
531 _("Already on first %s in Zoomed Index"),
532 THRD_INDX() ? _("thread") : _("message"));
539 && ((now
= time(0)) > state
->last_nextitem_forcechk
)){
541 /* check at most once a second */
542 state
->last_nextitem_forcechk
= now
;
545 q_status_message1(SM_ORDER
, 0, 1, _("Already on first %s"),
546 THRD_INDX() ? _("thread") : _("message"));
553 /*---------- Next Message ----------*/
555 if(mn_get_total(msgmap
) > 0L
556 && ((i
= mn_get_cur(msgmap
)) < mn_get_total(msgmap
))){
557 mn_inc_cur(stream
, msgmap
,
558 (in_index
== View
&& THREADING()
559 && sp_viewing_a_thread(stream
))
562 ? MH_ANYTHD
: MH_NONE
);
563 if(i
== mn_get_cur(msgmap
)){
564 PINETHRD_S
*thrd
, *topthrd
;
566 if(THRD_INDX_ENABLED()){
568 mn_inc_cur(stream
, msgmap
, MH_ANYTHD
);
570 if(i
== mn_get_cur(msgmap
)){
571 if(any_lflagged(msgmap
, MN_HIDE
))
572 any_messages(NULL
, "more", "in Zoomed Index");
578 || F_ON(F_NEXT_THRD_WO_CONFIRM
, state
))
581 ret
= want_to(_("View next thread"), 'y', 'x',
585 q_status_message(SM_ORDER
, 0, 2,
586 _("Viewing next thread"));
587 new_msgno
= mn_get_cur(msgmap
);
588 mn_set_cur(msgmap
, i
);
589 if(unview_thread(state
, stream
, msgmap
)){
590 state
->next_screen
= mail_index_screen
;
591 state
->view_skipped_index
= 0;
592 state
->mangled_screen
= 1;
595 mn_set_cur(msgmap
, new_msgno
);
596 if(THRD_AUTO_VIEW() && in_index
== View
){
598 thrd
= fetch_thread(stream
,
601 if(count_lflags_in_thread(stream
, thrd
,
604 if(view_thread(state
, stream
, msgmap
, 1)){
605 if(current_index_state
)
606 msgmap
->top_after_thrd
= current_index_state
->msg_at_top
;
608 state
->view_skipped_index
= 1;
609 command
= MC_VIEW_TEXT
;
616 if(THRD_AUTO_VIEW() && in_index
!= View
){
617 thrd
= fetch_thread(stream
, mn_m2raw(msgmap
, new_msgno
));
618 if(thrd
&& thrd
->top
)
619 topthrd
= fetch_thread(stream
, thrd
->top
);
622 j
= count_lflags_in_thread(stream
, topthrd
, msgmap
, MN_NONE
);
625 if(!THRD_AUTO_VIEW() || in_index
== View
|| j
!= 1){
626 if(view_thread(state
, stream
, msgmap
, 1)
627 && current_index_state
)
628 msgmap
->top_after_thrd
= current_index_state
->msg_at_top
;
632 state
->next_screen
= SCREEN_FUN_NULL
;
635 mn_set_cur(msgmap
, i
); /* put it back */
639 && (thrd
= fetch_thread(stream
, mn_m2raw(msgmap
, i
)))
641 && get_lflag(stream
, NULL
, thrd
->rawno
, MN_COLL
)){
642 q_status_message(SM_ORDER
, 0, 2,
643 _("Expand collapsed thread to see more messages"));
646 any_messages(NULL
, "more", "in Zoomed Index");
654 || (state
->context_current
->use
& CNTXT_INCMNG
)){
655 char nextfolder
[MAXPATH
];
657 strncpy(nextfolder
, state
->cur_folder
, sizeof(nextfolder
));
658 nextfolder
[sizeof(nextfolder
)-1] = '\0';
659 if(next_folder(NULL
, nextfolder
, sizeof(nextfolder
), nextfolder
,
660 state
->context_current
, NULL
, NULL
))
661 strncpy(prompt
, _(". Press TAB for next folder."),
664 strncpy(prompt
, _(". No more folders to TAB to."),
667 prompt
[sizeof(prompt
)-1] = '\0';
670 any_messages(NULL
, (mn_get_total(msgmap
) > 0L) ? "more" : NULL
,
671 prompt
[0] ? prompt
: NULL
);
674 && ((now
= time(0)) > state
->last_nextitem_forcechk
)){
676 /* check at most once a second */
677 state
->last_nextitem_forcechk
= now
;
684 /*---------- Delete message ----------*/
686 (void) cmd_delete(state
, msgmap
, MCMD_NONE
,
687 (in_index
== View
) ? cmd_delete_view
: cmd_delete_index
);
691 /*---------- Undelete message ----------*/
693 (void) cmd_undelete(state
, msgmap
, MCMD_NONE
);
694 update_titlebar_status();
698 /*---------- Reply to message ----------*/
700 (void) cmd_reply(state
, msgmap
, MCMD_NONE
, NULL
);
704 /*---------- Forward message ----------*/
706 (void) cmd_forward(state
, msgmap
, MCMD_NONE
, NULL
);
710 /*---------- Quit pine ------------*/
712 state
->next_screen
= quit_screen
;
713 dprint((1,"MAIL_CMD: quit\n"));
717 /*---------- Compose message ----------*/
719 state
->prev_screen
= (in_index
== View
) ? mail_view_screen
721 compose_screen(state
);
722 state
->mangled_screen
= 1;
723 if (state
->next_screen
)
728 /*---------- Alt Compose message ----------*/
730 state
->prev_screen
= (in_index
== View
) ? mail_view_screen
733 if(state
->next_screen
)
739 /*--------- Folders menu ------------*/
741 state
->start_in_context
= 1;
743 /*--------- Top of Folders list menu ------------*/
744 case MC_COLLECTIONS
:
745 state
->next_screen
= folder_screen
;
746 dprint((2,"MAIL_CMD: going to folder/collection menu\n"));
750 /*---------- Open specific new folder ----------*/
752 tc
= (state
->context_last
&& !NEWS_TEST(state
->context_current
))
753 ? state
->context_last
: state
->context_current
;
755 newfolder
= broach_folder(question_line
, 1, ¬realinbox
, &tc
);
757 visit_folder(state
, newfolder
, tc
, NULL
, notrealinbox
? 0L : DB_INBOXWOCNTXT
);
764 /*------- Go to Index Screen ----------*/
766 state
->next_screen
= mail_index_screen
;
769 /*------- Skip to next interesting message -----------*/
775 * If we're in the thread index, start looking after this
776 * thread. We don't want to match something in the current
779 start
= mn_get_cur(msgmap
);
780 thrd
= fetch_thread(stream
, mn_m2raw(msgmap
, mn_get_cur(msgmap
)));
781 if(mn_get_revsort(msgmap
)){
782 /* if reversed, top of thread is last one before next thread */
783 if(thrd
&& thrd
->top
)
784 start
= mn_raw2m(msgmap
, thrd
->top
);
787 /* last msg of thread is at the ends of the branches/nexts */
789 start
= mn_raw2m(msgmap
, thrd
->rawno
);
791 thrd
= fetch_thread(stream
, thrd
->branch
);
793 thrd
= fetch_thread(stream
, thrd
->next
);
800 * Flags is 0 in this case because we want to not skip
801 * messages inside of threads so that we can find threads
802 * which have some unseen messages even though the top-level
803 * of the thread is already seen.
804 * If new_msgno ends up being a message which is not visible
805 * because it isn't at the top-level, the current message #
806 * will be adjusted below in adjust_cur.
809 new_msgno
= next_sorted_flagged((F_UNDEL
811 | ((F_ON(F_TAB_TO_NEW
,state
))
813 stream
, start
, &flags
);
815 else if(THREADING() && sp_viewing_a_thread(stream
)){
816 PINETHRD_S
*thrd
, *topthrd
= NULL
;
818 start
= mn_get_cur(msgmap
);
821 * Things are especially complicated when we're viewing_a_thread
822 * from the thread index. First we have to check within the
823 * current thread for a new message. If none is found, then
824 * we search in the next threads and offer to continue in
825 * them. Then we offer to go to the next folder.
827 flags
= NSF_SKIP_CHID
;
828 new_msgno
= next_sorted_flagged((F_UNDEL
830 | ((F_ON(F_TAB_TO_NEW
,state
))
832 stream
, start
, &flags
);
834 * If we found a match then we are done, that is another message
835 * in the current thread index. Otherwise, we have to look
838 if(!(flags
& NSF_FLAG_MATCH
)){
843 new_msgno
= next_sorted_flagged((F_UNDEL
845 | ((F_ON(F_TAB_TO_NEW
,
848 stream
, start
, &flags
);
850 * If we got a match, new_msgno is a message in
851 * a different thread from the one we are viewing.
853 if(flags
& NSF_FLAG_MATCH
){
854 thrd
= fetch_thread(stream
, mn_m2raw(msgmap
,new_msgno
));
855 if(thrd
&& thrd
->top
)
856 topthrd
= fetch_thread(stream
, thrd
->top
);
858 if(F_OFF(F_AUTO_OPEN_NEXT_UNREAD
, state
)){
859 static ESCKEY_S next_opt
[] = {
860 {'y', 'y', "Y", N_("Yes")},
861 {'n', 'n', "N", N_("No")},
862 {TAB
, 'n', "Tab", N_("NextNew")},
867 snprintf(prompt
, sizeof(prompt
), _("View thread number %s? "),
868 topthrd
? comatose(topthrd
->thrdno
) : "?");
870 snprintf(prompt
, sizeof(prompt
),
871 _("View message in thread number %s? "),
872 topthrd
? comatose(topthrd
->thrdno
) : "?");
874 prompt
[sizeof(prompt
)-1] = '\0';
876 ret
= radio_buttons(prompt
, -FOOTER_ROWS(state
),
877 next_opt
, 'y', 'x', NO_HELP
,
888 if(unview_thread(state
, stream
, msgmap
)){
889 state
->next_screen
= mail_index_screen
;
890 state
->view_skipped_index
= 0;
891 state
->mangled_screen
= 1;
894 mn_set_cur(msgmap
, new_msgno
);
895 if(THRD_AUTO_VIEW()){
897 if(count_lflags_in_thread(stream
, topthrd
,
898 msgmap
, MN_NONE
) == 1){
899 if(view_thread(state
, stream
, msgmap
, 1)){
900 if(current_index_state
)
901 msgmap
->top_after_thrd
= current_index_state
->msg_at_top
;
903 state
->view_skipped_index
= 1;
904 command
= MC_VIEW_TEXT
;
910 if(view_thread(state
, stream
, msgmap
, 1) && current_index_state
)
911 msgmap
->top_after_thrd
= current_index_state
->msg_at_top
;
913 state
->next_screen
= SCREEN_FUN_NULL
;
916 else if(ret
== 'n' && topthrd
){
918 * skip to end of this thread and look starting
919 * in the next thread.
921 if(mn_get_revsort(msgmap
)){
923 * if reversed, top of thread is last one
926 start
= mn_raw2m(msgmap
, topthrd
->rawno
);
930 * last msg of thread is at the ends of
935 start
= mn_raw2m(msgmap
, thrd
->rawno
);
937 thrd
= fetch_thread(stream
, thrd
->branch
);
939 thrd
= fetch_thread(stream
, thrd
->next
);
955 start
= mn_get_cur(msgmap
);
958 * If we are on a collapsed thread, start looking after the
959 * collapsed part, unless we are viewing the message.
961 if(THREADING() && in_index
!= View
){
966 rawno
= mn_m2raw(msgmap
, start
);
967 thrd
= fetch_thread(stream
, rawno
);
968 collapsed
= thrd
&& thrd
->next
969 && get_lflag(stream
, NULL
, rawno
, MN_COLL
);
972 if(mn_get_revsort(msgmap
)){
973 if(thrd
&& thrd
->top
)
974 start
= mn_raw2m(msgmap
, thrd
->top
);
978 start
= mn_raw2m(msgmap
, thrd
->rawno
);
980 thrd
= fetch_thread(stream
, thrd
->branch
);
982 thrd
= fetch_thread(stream
, thrd
->next
);
991 new_msgno
= next_sorted_flagged((F_UNDEL
993 | ((F_ON(F_TAB_TO_NEW
,state
))
995 stream
, start
, &flags
);
999 * If there weren't any unread messages left, OR there
1000 * aren't any messages at all, we may want to offer to
1001 * go on to the next folder...
1003 if(flags
& NSF_FLAG_MATCH
){
1004 mn_set_cur(msgmap
, new_msgno
);
1005 if(in_index
!= View
)
1006 adjust_cur_to_visible(stream
, msgmap
);
1009 int in_inbox
= sp_flagged(stream
, SP_INBOX
);
1011 if(state
->context_current
1012 && ((NEWS_TEST(state
->context_current
)
1013 && context_isambig(state
->cur_folder
))
1014 || ((state
->context_current
->use
& CNTXT_INCMNG
)
1016 || folder_index(state
->cur_folder
,
1017 state
->context_current
,
1018 FI_FOLDER
) >= 0)))){
1019 char nextfolder
[MAXPATH
];
1020 MAILSTREAM
*nextstream
= NULL
;
1024 strncpy(nextfolder
, state
->cur_folder
, sizeof(nextfolder
));
1025 nextfolder
[sizeof(nextfolder
)-1] = '\0';
1027 if(!(next_folder(&nextstream
, nextfolder
, sizeof(nextfolder
), nextfolder
,
1028 state
->context_current
, &recent_cnt
,
1029 F_ON(F_TAB_NO_CONFIRM
,state
)
1030 ? NULL
: &did_cancel
))){
1032 static ESCKEY_S inbox_opt
[] = {
1033 {'y', 'y', "Y", N_("Yes")},
1034 {'n', 'n', "N", N_("No")},
1035 {TAB
, 'z', "Tab", N_("To Inbox")},
1039 if(F_ON(F_RET_INBOX_NO_CONFIRM
,state
))
1042 /* TRANSLATORS: this is a question, with some information followed
1043 by Return to INBOX? */
1044 if(state
->context_current
->use
&CNTXT_INCMNG
)
1045 snprintf(prompt
, sizeof(prompt
), _("No more incoming folders. Return to \"%s\"? "), state
->inbox_name
);
1047 snprintf(prompt
, sizeof(prompt
), _("No more news groups. Return to \"%s\"? "), state
->inbox_name
);
1049 ret
= radio_buttons(prompt
, -FOOTER_ROWS(state
),
1050 inbox_opt
, 'y', 'x',
1055 * 'z' is a synonym for 'y'. It is not 'y'
1056 * so that it isn't displayed as a default
1057 * action with square-brackets around it
1060 if(ret
== 'y' || ret
== 'z'){
1061 visit_folder(state
, state
->inbox_name
,
1062 state
->context_current
,
1063 NULL
, DB_INBOXWOCNTXT
);
1067 else if (did_cancel
)
1068 cmd_cancelled(NULL
);
1070 if(state
->context_current
->use
&CNTXT_INCMNG
)
1071 q_status_message(SM_ORDER
, 0, 2, _("No more incoming folders"));
1073 q_status_message(SM_ORDER
, 0, 2, _("No more news groups"));
1081 char *front
, type
[80], cnt
[CNTLEN
], fbuf
[MAX_SCREEN_COLS
/2+1];
1082 int rbspace
, avail
, need
, take_back
;
1086 * Incoming_folder_ or news_group_ or folder_ or group_
1088 * _(13 recent) or _(some recent) or nothing
1091 front
= "View next";
1093 (state
->context_current
->use
& CNTXT_INCMNG
)
1094 ? "Incoming folder" : "news group",
1096 type
[sizeof(type
)-1] = '\0';
1097 snprintf(cnt
, sizeof(cnt
), " (%.*s %s)", CNTLEN
-20,
1098 recent_cnt
? long2string(recent_cnt
) : "some",
1099 F_ON(F_TAB_USES_UNSEEN
, ps_global
)
1100 ? "unseen" : "recent");
1101 cnt
[sizeof(cnt
)-1] = '\0';
1104 * Space reserved for radio_buttons call.
1105 * If we make this 3 then radio_buttons won't mess
1106 * with the prompt. If we make it 2, then we get
1107 * one more character to use but radio_buttons will
1108 * cut off the last character of our prompt, which is
1109 * ok because it is a space.
1112 avail
= ps_global
->ttyo
? ps_global
->ttyo
->screen_cols
1114 need
= strlen(front
)+1 + strlen(type
)+1 +
1115 + strlen(nextfolder
)+2 + strlen(cnt
) +
1118 take_back
= strlen(type
);
1120 (state
->context_current
->use
& CNTXT_INCMNG
)
1121 ? "folder" : "group", sizeof(type
));
1122 take_back
-= strlen(type
);
1125 need
-= strlen(cnt
);
1129 /* MAX_SCREEN_COLS+1 = sizeof(prompt) */
1130 snprintf(prompt
, sizeof(prompt
), "%.*s %.*s \"%.*s\"%.*s? ",
1131 (MAX_SCREEN_COLS
+1)/8, front
,
1132 (MAX_SCREEN_COLS
+1)/8, type
,
1133 (MAX_SCREEN_COLS
+1)/2,
1134 short_str(nextfolder
, fbuf
, sizeof(fbuf
),
1135 strlen(nextfolder
) -
1136 ((need
>avail
) ? (need
-avail
) : 0),
1138 (MAX_SCREEN_COLS
+1)/8, cnt
);
1139 prompt
[sizeof(prompt
)-1] = '\0';
1143 * When help gets added, this'll have to become
1144 * a loop like the rest...
1146 if(F_OFF(F_AUTO_OPEN_NEXT_UNREAD
, state
)){
1147 static ESCKEY_S next_opt
[] = {
1148 {'y', 'y', "Y", N_("Yes")},
1149 {'n', 'n', "N", N_("No")},
1150 {TAB
, 'n', "Tab", N_("NextNew")},
1154 ret
= radio_buttons(prompt
, -FOOTER_ROWS(state
),
1155 next_opt
, 'y', 'x', NO_HELP
,
1158 cmd_cancelled(NULL
);
1166 if(nextstream
&& sp_dead_stream(nextstream
))
1169 visit_folder(state
, nextfolder
,
1170 state
->context_current
, nextstream
,
1172 /* visit_folder takes care of nextstream */
1180 pine_mail_close(nextstream
);
1184 (mn_get_total(msgmap
) > 0L)
1185 ? IS_NEWS(stream
) ? "more undeleted" : "more new"
1195 /*------- Zoom -----------*/
1198 * Right now the way zoom is implemented is sort of silly.
1199 * There are two per-message flags where just one and a
1200 * global "zoom mode" flag to suppress messags from the index
1203 if(any_messages(msgmap
, NULL
, "to Zoom on")){
1204 if(unzoom_index(state
, stream
, msgmap
)){
1205 dprint((4, "\n\n ---- Exiting ZOOM mode ----\n"));
1206 q_status_message(SM_ORDER
,0,2, _("Index Zoom Mode is now off"));
1208 else if((i
= zoom_index(state
, stream
, msgmap
, MN_SLCT
)) != 0){
1209 if(any_lflagged(msgmap
, MN_HIDE
)){
1210 dprint((4,"\n\n ---- Entering ZOOM mode ----\n"));
1211 q_status_message4(SM_ORDER
, 0, 2,
1212 _("In Zoomed Index of %s%s%s%s. Use \"Z\" to restore regular Index"),
1213 THRD_INDX() ? "" : comatose(i
),
1214 THRD_INDX() ? "" : " ",
1215 THRD_INDX() ? _("threads") : _("message"),
1216 THRD_INDX() ? "" : plural(i
));
1219 q_status_message(SM_ORDER
, 0, 2,
1220 _("All messages selected, so not entering Index Zoom Mode"));
1223 any_messages(NULL
, "selected", "to Zoom on");
1229 /*---------- print message on paper ----------*/
1231 if(any_messages(msgmap
, NULL
, "to print"))
1232 (void) cmd_print(state
, msgmap
, MCMD_NONE
, in_index
);
1237 /*---------- Take Address ----------*/
1239 if(F_ON(F_ENABLE_ROLE_TAKE
, state
) ||
1240 any_messages(msgmap
, NULL
, "to Take address from"))
1241 (void) cmd_take_addr(state
, msgmap
, MCMD_NONE
);
1246 /*---------- Save Message ----------*/
1248 if(any_messages(msgmap
, NULL
, "to Save"))
1249 (void) cmd_save(state
, stream
, msgmap
, MCMD_NONE
, in_index
);
1254 /*---------- Export message ----------*/
1256 if(any_messages(msgmap
, NULL
, "to Export")){
1257 (void) cmd_export(state
, msgmap
, question_line
, MCMD_NONE
);
1258 state
->mangled_footer
= 1;
1264 /*---------- Expunge ----------*/
1266 (void) cmd_expunge(state
, stream
, msgmap
, MCMD_NONE
);
1270 /*------- Unexclude -----------*/
1272 if(!(IS_NEWS(stream
) && stream
->rdonly
)){
1273 q_status_message(SM_ORDER
, 0, 3,
1274 _("Unexclude not available for mail folders"));
1276 else if(any_lflagged(msgmap
, MN_EXLD
)){
1282 * Since excluded means "hidden deleted" and "killed",
1283 * the count should reflect the former.
1285 pgm
= mail_newsearchpgm();
1287 pine_mail_search_full(stream
, NULL
, pgm
, SE_NOPREFETCH
| SE_FREE
);
1288 for(i
= 1L, del_count
= 0L; i
<= stream
->nmsgs
; i
++)
1289 if((mc
= mail_elt(stream
, i
)) && mc
->searched
1290 && get_lflag(stream
, NULL
, i
, MN_EXLD
)
1291 && !(msgno_exceptions(stream
, i
, "0", &exbits
, FALSE
)
1292 && (exbits
& MSG_EX_FILTERED
)))
1296 state
->mangled_footer
= 1; /* MAX_SCREEN_COLS+1 = sizeof(prompt) */
1297 snprintf(prompt
, sizeof(prompt
), "UNexclude %ld message%s in %.*s", del_count
,
1298 plural(del_count
), MAX_SCREEN_COLS
+1-40,
1299 pretty_fn(state
->cur_folder
));
1300 prompt
[sizeof(prompt
)-1] = '\0';
1301 if(F_ON(F_FULL_AUTO_EXPUNGE
, state
)
1302 || (F_ON(F_AUTO_EXPUNGE
, state
)
1303 && (state
->context_current
1304 && (state
->context_current
->use
& CNTXT_INCMNG
))
1305 && context_isambig(state
->cur_folder
))
1306 || want_to(prompt
, 'y', 0, NO_HELP
, WT_NORM
) == 'y'){
1307 long save_cur_rawno
;
1308 int were_viewing_a_thread
;
1310 save_cur_rawno
= mn_m2raw(msgmap
, mn_get_cur(msgmap
));
1311 were_viewing_a_thread
= (THREADING()
1312 && sp_viewing_a_thread(stream
));
1314 if(msgno_include(stream
, msgmap
, MI_NONE
)){
1315 clear_index_cache(stream
, 0);
1317 if(stream
&& stream
->spare
)
1318 erase_threading_info(stream
, msgmap
);
1320 refresh_sort(stream
, msgmap
, SRT_NON
);
1323 if(were_viewing_a_thread
){
1324 if(save_cur_rawno
> 0L)
1325 mn_set_cur(msgmap
, mn_raw2m(msgmap
,save_cur_rawno
));
1327 if(view_thread(state
, stream
, msgmap
, 1) && current_index_state
)
1328 msgmap
->top_after_thrd
= current_index_state
->msg_at_top
;
1331 if(save_cur_rawno
> 0L)
1332 mn_set_cur(msgmap
, mn_raw2m(msgmap
,save_cur_rawno
));
1334 state
->mangled_screen
= 1;
1335 q_status_message2(SM_ORDER
, 0, 4,
1336 "%s message%s UNexcluded",
1337 long2string(del_count
),
1340 if(in_index
!= View
)
1341 adjust_cur_to_visible(stream
, msgmap
);
1344 any_messages(NULL
, NULL
, "UNexcluded");
1347 any_messages(NULL
, "excluded", "to UNexclude");
1350 any_messages(NULL
, "excluded", "to UNexclude");
1355 /*------- Make Selection -----------*/
1357 if(any_messages(msgmap
, NULL
, "to Select")){
1358 if(aggregate_select(state
, msgmap
, question_line
, in_index
) == 0
1359 && (in_index
== MsgIndx
|| in_index
== ThrdIndx
)
1360 && F_ON(F_AUTO_ZOOM
, state
)
1361 && any_lflagged(msgmap
, MN_SLCT
) > 0L
1362 && !any_lflagged(msgmap
, MN_HIDE
))
1363 (void) zoom_index(state
, stream
, msgmap
, MN_SLCT
);
1369 /*------- Toggle Current Message Selection State -----------*/
1371 if(any_messages(msgmap
, NULL
, NULL
)){
1372 if((select_by_current(state
, msgmap
, in_index
)
1373 || (F_OFF(F_UNSELECT_WONT_ADVANCE
, state
)
1374 && !any_lflagged(msgmap
, MN_HIDE
)))
1375 && (i
= mn_get_cur(msgmap
)) < mn_get_total(msgmap
)){
1376 /* advance current */
1377 mn_inc_cur(stream
, msgmap
,
1378 (in_index
== View
&& THREADING()
1379 && sp_viewing_a_thread(stream
))
1381 : (in_index
== View
)
1382 ? MH_ANYTHD
: MH_NONE
);
1389 /*------- Apply command -----------*/
1391 if(any_messages(msgmap
, NULL
, NULL
)){
1392 if(any_lflagged(msgmap
, MN_SLCT
) > 0L){
1393 if(apply_command(state
, stream
, msgmap
, 0,
1394 AC_NONE
, question_line
)){
1395 if(F_ON(F_AUTO_UNSELECT
, state
)){
1396 agg_select_all(stream
, msgmap
, NULL
, 0);
1397 unzoom_index(state
, stream
, msgmap
);
1399 else if(F_ON(F_AUTO_UNZOOM
, state
))
1400 unzoom_index(state
, stream
, msgmap
);
1404 any_messages(NULL
, NULL
, "to Apply command to. Try \"Select\"");
1410 /*-------- Sort command -------*/
1413 int were_threading
= THREADING();
1414 SortOrder sort
= mn_get_sort(msgmap
);
1415 int rev
= mn_get_revsort(msgmap
);
1417 dprint((1,"MAIL_CMD: sort\n"));
1418 if(select_sort(state
, question_line
, &sort
, &rev
)){
1419 /* $ command reinitializes threading collapsed/expanded info */
1420 if(SORT_IS_THREADED(msgmap
) && !SEP_THRDINDX())
1421 erase_threading_info(stream
, msgmap
);
1423 if(ps_global
&& ps_global
->ttyo
){
1424 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
1425 ps_global
->mangled_footer
= 1;
1428 sort_folder(stream
, msgmap
, sort
, rev
, SRT_VRB
|SRT_MAN
);
1431 state
->mangled_footer
= 1;
1434 * We've changed whether we are threading or not so we need to
1435 * exit the index and come back in so that we switch between the
1436 * thread index and the regular index. Sort_folder will have
1437 * reset viewing_a_thread if necessary.
1440 && ((!were_threading
&& THREADING())
1441 || (were_threading
&& !THREADING()))){
1442 state
->next_screen
= mail_index_screen
;
1443 state
->mangled_screen
= 1;
1450 /*------- Toggle Full Headers -----------*/
1452 state
->full_header
++;
1453 if(state
->full_header
== 1){
1454 if(!(state
->quote_suppression_threshold
1455 && (state
->some_quoting_was_suppressed
|| in_index
!= View
)))
1456 state
->full_header
++;
1458 else if(state
->full_header
> 2)
1459 state
->full_header
= 0;
1461 switch(state
->full_header
){
1463 q_status_message(SM_ORDER
, 0, 3,
1464 _("Display of full headers is now off."));
1468 q_status_message1(SM_ORDER
, 0, 3,
1469 _("Quotes displayed, use %s to see full headers"),
1470 F_ON(F_USE_FK
, state
) ? "F9" : "H");
1474 q_status_message(SM_ORDER
, 0, 3,
1475 _("Display of full headers is now on."));
1490 /*------- Try to decrypt message -----------*/
1492 if(state
->smime
&& state
->smime
->need_passphrase
)
1493 smime_get_passphrase();
1499 smime_info_screen(state
);
1504 /*------- Bounce -----------*/
1506 (void) cmd_bounce(state
, msgmap
, MCMD_NONE
, NULL
);
1510 /*------- Flag -----------*/
1512 dprint((4, "\n - flag message -\n"));
1513 (void) cmd_flag(state
, msgmap
, MCMD_NONE
);
1517 /*------- Pipe message -----------*/
1519 (void) cmd_pipe(state
, msgmap
, MCMD_NONE
);
1523 /*--------- Default, unknown command ----------*/
1525 alpine_panic("Unexpected command case");
1529 return((a_changed
|| mn_get_cur(msgmap
) != old_msgno
) ? 1 : 0);
1534 /*----------------------------------------------------------------------
1535 Map some of the special characters into sensible strings for human
1537 c is a UCS-4 character!
1540 pretty_command(UCS c
)
1542 static char buf
[10];
1549 case ' ' : s
= "SPACE"; break;
1550 case '\033' : s
= "ESC"; break;
1551 case '\177' : s
= "DEL"; break;
1552 case ctrl('I') : s
= "TAB"; break;
1553 case ctrl('J') : s
= "LINEFEED"; break;
1554 case ctrl('M') : s
= "RETURN"; break;
1555 case ctrl('Q') : s
= "XON"; break;
1556 case ctrl('S') : s
= "XOFF"; break;
1557 case KEY_UP
: s
= "Up Arrow"; break;
1558 case KEY_DOWN
: s
= "Down Arrow"; break;
1559 case KEY_RIGHT
: s
= "Right Arrow"; break;
1560 case KEY_LEFT
: s
= "Left Arrow"; break;
1561 case KEY_PGUP
: s
= "Prev Page"; break;
1562 case KEY_PGDN
: s
= "Next Page"; break;
1563 case KEY_HOME
: s
= "Home"; break;
1564 case KEY_END
: s
= "End"; break;
1565 case KEY_DEL
: s
= "Delete"; break; /* Not necessary DEL! */
1566 case KEY_JUNK
: s
= "Junk!"; break;
1567 case BADESC
: s
= "Bad Esc"; break;
1568 case NO_OP_IDLE
: s
= "NO_OP_IDLE"; break;
1569 case NO_OP_COMMAND
: s
= "NO_OP_COMMAND"; break;
1570 case KEY_RESIZE
: s
= "KEY_RESIZE"; break;
1571 case KEY_UTF8
: s
= "KEY_UTF8"; break;
1572 case KEY_MOUSE
: s
= "KEY_MOUSE"; break;
1573 case KEY_SCRLUPL
: s
= "KEY_SCRLUPL"; break;
1574 case KEY_SCRLDNL
: s
= "KEY_SCRLDNL"; break;
1575 case KEY_SCRLTO
: s
= "KEY_SCRLTO"; break;
1576 case KEY_XTERM_MOUSE
: s
= "KEY_XTERM_MOUSE"; break;
1577 case KEY_DOUBLE_ESC
: s
= "KEY_DOUBLE_ESC"; break;
1578 case CTRL_KEY_UP
: s
= "Ctrl Up Arrow"; break;
1579 case CTRL_KEY_DOWN
: s
= "Ctrl Down Arrow"; break;
1580 case CTRL_KEY_RIGHT
: s
= "Ctrl Right Arrow"; break;
1581 case CTRL_KEY_LEFT
: s
= "Ctrl Left Arrow"; break;
1594 snprintf(s
= buf
, sizeof(buf
), "F%ld", (long) (c
- PF1
+ 1));
1598 if(c
< ' ' || (c
>= 0x80 && c
< 0xA0)){
1603 d
= (c
& 0x1f) + 'A' - 1;
1604 snprintf(s
= buf
, sizeof(buf
), "%c%c", c1
? '~' : '^', d
);
1607 memset(buf
, 0, sizeof(buf
));
1608 utf8_put((unsigned char *) buf
, (unsigned long) c
);
1618 /*----------------------------------------------------------------------
1619 Complain about bogus input
1621 Args: ch -- input command to complain about
1622 help -- string indicating where to get help
1626 bogus_command(UCS cmd
, char *help
)
1628 if(cmd
== ctrl('Q') || cmd
== ctrl('S'))
1629 q_status_message1(SM_ASYNC
, 0, 2,
1630 "%s char received. Set \"preserve-start-stop\" feature in Setup/Config.",
1631 pretty_command(cmd
));
1632 else if(cmd
== KEY_JUNK
)
1633 q_status_message3(SM_ORDER
, 0, 2,
1634 "Invalid key pressed.%s%s%s",
1635 (help
) ? " Use " : "",
1637 (help
) ? " for help" : "");
1639 q_status_message4(SM_ORDER
, 0, 2,
1640 "Command \"%s\" not defined for this screen.%s%s%s",
1641 pretty_command(cmd
),
1642 (help
) ? " Use " : "",
1644 (help
) ? " for help" : "");
1649 bogus_utf8_command(char *cmd
, char *help
)
1651 q_status_message4(SM_ORDER
, 0, 2,
1652 "Command \"%s\" not defined for this screen.%s%s%s",
1654 (help
) ? " Use " : "",
1656 (help
) ? " for help" : "");
1660 /*----------------------------------------------------------------------
1661 Execute FLAG message command
1663 Args: state -- Various satate info
1664 msgmap -- map of c-client to local message numbers
1666 Result: with side effect of "current" message FLAG flag set or UNset
1670 cmd_flag(struct pine
*state
, MSGNO_S
*msgmap
, int aopt
)
1672 char *flagit
, *seq
, *screen_text
[20], **exp
, **p
, *answer
= NULL
;
1673 char *keyword_array
[2];
1674 int user_defined_flags
= 0, mailbox_flags
= 0;
1675 int directly_to_maint_screen
= 0;
1676 long unflagged
, flagged
, flags
, rawno
;
1677 MESSAGECACHE
*mc
= NULL
;
1679 int i
, cnt
, is_set
, trouble
= 0, rv
= 0;
1681 struct flag_table
*fp
, *ftbl
= NULL
;
1682 struct flag_screen flag_screen
;
1683 static char *flag_screen_text1
[] = {
1684 N_(" Set desired flags for current message below. An 'X' means set"),
1685 N_(" it, and a ' ' means to unset it. Choose \"Exit\" when finished."),
1689 static char *flag_screen_text2
[] = {
1690 N_(" Set desired flags below for selected messages. A '?' means to"),
1691 N_(" leave the flag unchanged, 'X' means to set it, and a ' ' means"),
1692 N_(" to unset it. Use the \"Return\" key to toggle, and choose"),
1693 N_(" \"Exit\" when finished."),
1697 static struct flag_table default_ftbl
[] = {
1698 {N_("Important"), h_flag_important
, F_FLAG
, 0, 0, NULL
, NULL
},
1699 {N_("New"), h_flag_new
, F_SEEN
, 0, 0, NULL
, NULL
},
1700 {N_("Answered"), h_flag_answered
, F_ANS
, 0, 0, NULL
, NULL
},
1701 {N_("Forwarded"), h_flag_forwarded
, F_FWD
, 0, 0, NULL
, NULL
},
1702 {N_("Deleted"), h_flag_deleted
, F_DEL
, 0, 0, NULL
, NULL
},
1703 {NULL
, NO_HELP
, 0, 0, 0, NULL
, NULL
}
1706 /* Only check for dead stream for now. Should check permanent flags
1709 if(!(any_messages(msgmap
, NULL
, "to Flag") && can_set_flag(state
, "flag", 1)))
1712 if(sp_io_error_on_stream(state
->mail_stream
)){
1713 sp_set_io_error_on_stream(state
->mail_stream
, 0);
1714 pine_mail_check(state
->mail_stream
); /* forces write */
1720 user_defined_flags
= 0;
1726 /* count how large ftbl will be */
1727 for(cnt
= 0; default_ftbl
[cnt
].name
; cnt
++)
1730 /* add user flags */
1731 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
){
1732 if(!((kw
->nick
&& !strucmp(FORWARDED_FLAG
, kw
->nick
)) || (kw
->kw
&& !strucmp(FORWARDED_FLAG
, kw
->kw
)))){
1733 user_defined_flags
++;
1739 * Add mailbox flags that aren't user-defined flags.
1740 * Don't consider it if it matches either one of our defined
1741 * keywords or one of our defined nicknames for a keyword.
1743 for(i
= 0; stream_to_user_flag_name(state
->mail_stream
, i
); i
++){
1746 q
= stream_to_user_flag_name(state
->mail_stream
, i
);
1748 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
){
1749 if((kw
->nick
&& !strucmp(kw
->nick
, q
)) || (kw
->kw
&& !strucmp(kw
->kw
, q
)))
1754 if(!kw
&& !(q
&& !strucmp(FORWARDED_FLAG
, q
))){
1760 cnt
+= (user_defined_flags
? 2 : 0) + (mailbox_flags
? 2 : 0);
1762 /* set up ftbl, first the system flags */
1763 ftbl
= (struct flag_table
*) fs_get((cnt
+1) * sizeof(*ftbl
));
1764 memset(ftbl
, 0, (cnt
+1) * sizeof(*ftbl
));
1765 for(i
= 0, fp
= ftbl
; default_ftbl
[i
].name
; i
++, fp
++){
1766 fp
->name
= cpystr(default_ftbl
[i
].name
);
1767 fp
->help
= default_ftbl
[i
].help
;
1768 fp
->flag
= default_ftbl
[i
].flag
;
1769 fp
->set
= default_ftbl
[i
].set
;
1770 fp
->ukn
= default_ftbl
[i
].ukn
;
1773 if(user_defined_flags
){
1774 fp
->flag
= F_COMMENT
;
1775 fp
->name
= cpystr("");
1777 fp
->flag
= F_COMMENT
;
1778 len
= strlen(_("User-defined Keywords from Setup/Config"));
1779 fp
->name
= (char *) fs_get((len
+6+6+1) * sizeof(char));
1780 snprintf(fp
->name
, len
+6+6+1, "----- %s -----", _("User-defined Keywords from Setup/Config"));
1784 /* then the user-defined keywords */
1785 if(user_defined_flags
)
1786 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
){
1787 if(!((kw
->nick
&& !strucmp(FORWARDED_FLAG
, kw
->nick
))
1788 || (kw
->kw
&& !strucmp(FORWARDED_FLAG
, kw
->kw
)))){
1789 fp
->name
= cpystr(kw
->nick
? kw
->nick
: kw
->kw
? kw
->kw
: "");
1790 fp
->keyword
= cpystr(kw
->kw
? kw
->kw
: "");
1791 if(kw
->nick
&& kw
->kw
){
1794 l
= strlen(kw
->kw
)+2;
1795 fp
->comment
= (char *) fs_get((l
+1) * sizeof(char));
1796 snprintf(fp
->comment
, l
+1, "(%.*s)", (int) strlen(kw
->kw
), kw
->kw
);
1797 fp
->comment
[l
] = '\0';
1800 fp
->help
= h_flag_user_flag
;
1801 fp
->flag
= F_KEYWORD
;
1809 fp
->flag
= F_COMMENT
;
1810 fp
->name
= cpystr("");
1812 fp
->flag
= F_COMMENT
;
1813 len
= strlen(_("Other keywords in the mailbox that are not user-defined"));
1814 fp
->name
= (char *) fs_get((len
+6+6+1) * sizeof(char));
1815 snprintf(fp
->name
, len
+6+6+1, "----- %s -----", _("Other keywords in the mailbox that are not user-defined"));
1819 /* then the extra mailbox-defined keywords */
1821 for(i
= 0; stream_to_user_flag_name(state
->mail_stream
, i
); i
++){
1824 q
= stream_to_user_flag_name(state
->mail_stream
, i
);
1826 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
){
1827 if((kw
->nick
&& !strucmp(kw
->nick
, q
)) || (kw
->kw
&& !strucmp(kw
->kw
, q
)))
1832 if(!kw
&& !(q
&& !strucmp(FORWARDED_FLAG
, q
))){
1833 fp
->name
= cpystr(q
);
1834 fp
->keyword
= cpystr(q
);
1835 fp
->help
= h_flag_user_flag
;
1836 fp
->flag
= F_KEYWORD
;
1843 flag_screen
.flag_table
= &ftbl
;
1844 flag_screen
.explanation
= screen_text
;
1846 if(MCMD_ISAGG(aopt
)){
1847 if(!pseudo_selected(ps_global
->mail_stream
, msgmap
)){
1848 free_flag_table(&ftbl
);
1852 exp
= flag_screen_text2
;
1853 for(fp
= ftbl
; fp
->name
; fp
++){
1854 fp
->set
= CMD_FLAG_UNKN
; /* set to unknown */
1858 else if(state
->mail_stream
1859 && (rawno
= mn_m2raw(msgmap
, mn_get_cur(msgmap
))) > 0L
1860 && rawno
<= state
->mail_stream
->nmsgs
1861 && (mc
= mail_elt(state
->mail_stream
, rawno
))){
1862 exp
= flag_screen_text1
;
1863 for(fp
= &ftbl
[0]; fp
->name
; fp
++){
1865 if(fp
->flag
== F_KEYWORD
){
1866 /* see if this keyword is defined for this message */
1867 fp
->set
= CMD_FLAG_CLEAR
;
1868 if(user_flag_is_set(state
->mail_stream
,
1869 rawno
, fp
->keyword
))
1870 fp
->set
= CMD_FLAG_SET
;
1872 else if(fp
->flag
== F_FWD
){
1873 /* see if forwarded keyword is defined for this message */
1874 fp
->set
= CMD_FLAG_CLEAR
;
1875 if(user_flag_is_set(state
->mail_stream
,
1876 rawno
, FORWARDED_FLAG
))
1877 fp
->set
= CMD_FLAG_SET
;
1879 else if(fp
->flag
!= F_COMMENT
)
1880 fp
->set
= ((fp
->flag
== F_SEEN
&& !mc
->seen
)
1881 || (fp
->flag
== F_DEL
&& mc
->deleted
)
1882 || (fp
->flag
== F_FLAG
&& mc
->flagged
)
1883 || (fp
->flag
== F_ANS
&& mc
->answered
))
1884 ? CMD_FLAG_SET
: CMD_FLAG_CLEAR
;
1888 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
1889 _("Error accessing message data"));
1890 free_flag_table(&ftbl
);
1894 if(directly_to_maint_screen
)
1895 goto the_maint_screen
;
1898 if (mswin_usedialog ()) {
1899 if (!os_flagmsgdialog (&ftbl
[0])){
1900 free_flag_table(&ftbl
);
1907 int use_maint_screen
;
1908 int keyword_shortcut
= 0;
1910 use_maint_screen
= F_ON(F_FLAG_SCREEN_DFLT
, ps_global
);
1912 if(!use_maint_screen
){
1914 * We're going to call cmd_flag_prompt(). We need
1915 * to decide whether or not to offer the keyword setting
1916 * shortcut. We'll offer it if the user has the feature
1917 * enabled AND there are some possible keywords that could
1920 if(F_ON(F_FLAG_SCREEN_KW_SHORTCUT
, ps_global
)){
1921 for(fp
= &ftbl
[0]; fp
->name
&& !keyword_shortcut
; fp
++){
1922 if(fp
->flag
== F_KEYWORD
){
1926 first_char
= (fp
->name
&& fp
->name
[0])
1928 if(isascii(first_char
) && isupper(first_char
))
1929 first_char
= tolower((unsigned char) first_char
);
1931 for(tp
=flag_text_opt
; tp
->ch
!= -1; tp
++)
1932 if(tp
->ch
== first_char
)
1941 use_maint_screen
= !cmd_flag_prompt(state
, &flag_screen
,
1946 if(use_maint_screen
){
1947 for(p
= &screen_text
[0]; *exp
; p
++, exp
++)
1952 directly_to_maint_screen
= flag_maintenance_screen(state
, &flag_screen
);
1956 /* reaquire the elt pointer */
1957 mc
= (state
->mail_stream
1958 && (rawno
= mn_m2raw(msgmap
, mn_get_cur(msgmap
))) > 0L
1959 && rawno
<= state
->mail_stream
->nmsgs
)
1960 ? mail_elt(state
->mail_stream
, rawno
) : NULL
;
1962 for(fp
= ftbl
; mc
&& fp
->name
; fp
++){
1966 if((!MCMD_ISAGG(aopt
) && fp
->set
!= !mc
->seen
)
1967 || (MCMD_ISAGG(aopt
) && fp
->set
!= CMD_FLAG_UNKN
)){
1975 unflagged
= F_UNSEEN
;
1982 if((!MCMD_ISAGG(aopt
) && fp
->set
!= mc
->answered
)
1983 || (MCMD_ISAGG(aopt
) && fp
->set
!= CMD_FLAG_UNKN
)){
1984 flagit
= "\\ANSWERED";
1987 unflagged
= F_UNANS
;
1998 if((!MCMD_ISAGG(aopt
) && fp
->set
!= mc
->deleted
)
1999 || (MCMD_ISAGG(aopt
) && fp
->set
!= CMD_FLAG_UNKN
)){
2000 flagit
= "\\DELETED";
2003 unflagged
= F_UNDEL
;
2014 if((!MCMD_ISAGG(aopt
) && fp
->set
!= mc
->flagged
)
2015 || (MCMD_ISAGG(aopt
) && fp
->set
!= CMD_FLAG_UNKN
)){
2016 flagit
= "\\FLAGGED";
2019 unflagged
= F_UNFLAG
;
2030 if(!MCMD_ISAGG(aopt
)){
2031 /* see if forwarded is defined for this message */
2032 is_set
= CMD_FLAG_CLEAR
;
2033 if(user_flag_is_set(state
->mail_stream
,
2034 mn_m2raw(msgmap
, mn_get_cur(msgmap
)),
2036 is_set
= CMD_FLAG_SET
;
2039 if((!MCMD_ISAGG(aopt
) && fp
->set
!= is_set
)
2040 || (MCMD_ISAGG(aopt
) && fp
->set
!= CMD_FLAG_UNKN
)){
2041 flagit
= FORWARDED_FLAG
;
2044 unflagged
= F_UNFWD
;
2055 if(!MCMD_ISAGG(aopt
)){
2056 /* see if this keyword is defined for this message */
2057 is_set
= CMD_FLAG_CLEAR
;
2058 if(user_flag_is_set(state
->mail_stream
,
2059 mn_m2raw(msgmap
, mn_get_cur(msgmap
)),
2061 is_set
= CMD_FLAG_SET
;
2064 if((!MCMD_ISAGG(aopt
) && fp
->set
!= is_set
)
2065 || (MCMD_ISAGG(aopt
) && fp
->set
!= CMD_FLAG_UNKN
)){
2066 flagit
= fp
->keyword
;
2067 keyword_array
[0] = fp
->keyword
;
2068 keyword_array
[1] = NULL
;
2071 unflagged
= F_UNKEYWORD
;
2075 unflagged
= F_KEYWORD
;
2087 && (seq
= currentf_sequence(state
->mail_stream
, msgmap
,
2088 unflagged
, &flagged
, unflagged
& F_DEL
,
2089 (fp
->flag
== F_KEYWORD
2090 && unflagged
== F_KEYWORD
)
2091 ? keyword_array
: NULL
,
2092 (fp
->flag
== F_KEYWORD
2093 && unflagged
== F_UNKEYWORD
)
2094 ? keyword_array
: NULL
))){
2096 * For user keywords, we may have to create the flag in
2097 * the folder if it doesn't already exist and we are setting
2098 * it (as opposed to clearing it). Mail_flag will
2099 * do that for us, but it's failure isn't very friendly
2100 * error-wise. So we try to make it a little smoother.
2102 if(!(fp
->flag
== F_KEYWORD
|| fp
->flag
== F_FWD
) || !fp
->set
2103 || ((i
=user_flag_index(state
->mail_stream
, flagit
)) >= 0
2105 mail_flag(state
->mail_stream
, seq
, flagit
, flags
);
2107 /* trouble, see if we can add the user flag */
2108 if(state
->mail_stream
->kwd_create
)
2109 mail_flag(state
->mail_stream
, seq
, flagit
, flags
);
2113 if(some_user_flags_defined(state
->mail_stream
))
2114 q_status_message(SM_ORDER
, 3, 4,
2115 _("No more keywords allowed in this folder!"));
2116 else if(fp
->flag
== F_FWD
)
2117 q_status_message(SM_ORDER
, 3, 4,
2118 _("Cannot add keywords for this folder so cannot set Forwarded flag"));
2120 q_status_message(SM_ORDER
, 3, 4,
2121 _("Cannot add keywords for this folder"));
2125 fs_give((void **) &seq
);
2126 if(flagged
&& !trouble
){
2127 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%slagged%s%s%s%s%s message%s%s \"%s\"",
2128 (fp
->set
) ? "F" : "Unf",
2129 MCMD_ISAGG(aopt
) ? " " : "",
2130 MCMD_ISAGG(aopt
) ? long2string(flagged
) : "",
2131 (MCMD_ISAGG(aopt
) && flagged
!= mn_total_cur(msgmap
))
2133 (MCMD_ISAGG(aopt
) && flagged
!= mn_total_cur(msgmap
))
2134 ? comatose(mn_total_cur(msgmap
)) : "",
2135 (MCMD_ISAGG(aopt
) && flagged
!= mn_total_cur(msgmap
))
2137 MCMD_ISAGG(aopt
) ? plural(flagged
) : " ",
2138 MCMD_ISAGG(aopt
) ? "" : long2string(mn_get_cur(msgmap
)),
2140 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
2141 q_status_message(SM_ORDER
, 0, 2, answer
= tmp_20k_buf
);
2147 free_flag_table(&ftbl
);
2149 if(directly_to_maint_screen
)
2152 if(MCMD_ISAGG(aopt
))
2153 restore_selected(msgmap
);
2156 q_status_message(SM_ORDER
, 0, 2, _("No flags changed."));
2162 /*----------------------------------------------------------------------
2163 Offer concise status line flag prompt
2165 Args: state -- Various satate info
2166 flags -- flags to offer setting
2168 Result: TRUE if flag to set specified in flags struct or FALSE otw
2172 cmd_flag_prompt(struct pine
*state
, struct flag_screen
*flags
, int allow_keyword_shortcuts
)
2174 int r
, setflag
= 1, first_char
;
2175 struct flag_table
*fp
;
2177 char *ftext
, *ftext_not
;
2178 static char *flag_text
=
2179 N_("Flag New, Deleted, Answered, Forwarded or Important ? ");
2180 static char *flag_text_ak
=
2181 N_("Flag New, Deleted, Answered, Forwarded, Important or Keyword initial ? ");
2182 static char *flag_text_not
=
2183 N_("Flag !New, !Deleted, !Answered, !Forwarded, or !Important ? ");
2184 static char *flag_text_ak_not
=
2185 N_("Flag !New, !Deleted, !Answered, !Forwarded, !Important or !Keyword initial ? ");
2187 if(allow_keyword_shortcuts
){
2189 ESCKEY_S
*dp
, *sp
, *tp
;
2191 for(sp
=flag_text_opt
; sp
->ch
!= -1; sp
++)
2194 for(fp
=(flags
->flag_table
? *flags
->flag_table
: NULL
); fp
->name
; fp
++)
2195 if(fp
->flag
== F_KEYWORD
)
2198 /* set up an ESCKEY_S list which includes invisible keys for keywords */
2199 ek
= (ESCKEY_S
*) fs_get((cnt
+ 1) * sizeof(*ek
));
2200 memset(ek
, 0, (cnt
+1) * sizeof(*ek
));
2201 for(dp
=ek
, sp
=flag_text_opt
; sp
->ch
!= -1; sp
++, dp
++)
2204 for(fp
=(flags
->flag_table
? *flags
->flag_table
: NULL
); fp
->name
; fp
++){
2205 if(fp
->flag
== F_KEYWORD
){
2206 first_char
= (fp
->name
&& fp
->name
[0]) ? fp
->name
[0] : -2;
2207 if(isascii(first_char
) && isupper(first_char
))
2208 first_char
= tolower((unsigned char) first_char
);
2211 * Check to see if an earlier keyword in the list, or one of
2212 * the builtin system letters already uses this character.
2213 * If so, the first one wins.
2215 for(tp
=ek
; tp
->ch
!= 0; tp
++)
2216 if(tp
->ch
== first_char
)
2220 continue; /* skip it, already used that char */
2222 dp
->ch
= first_char
;
2223 dp
->rval
= first_char
;
2231 ftext
= _(flag_text_ak
);
2232 ftext_not
= _(flag_text_ak_not
);
2236 ftext
= _(flag_text
);
2237 ftext_not
= _(flag_text_not
);
2241 r
= radio_buttons(setflag
? ftext
: ftext_not
,
2242 -FOOTER_ROWS(state
), ek
, '*', SEQ_EXCEPTION
-1,
2243 NO_HELP
, RB_NORM
| RB_SEQ_SENSITIVE
);
2245 * It is SEQ_EXCEPTION-1 just so that it is some value that isn't
2246 * being used otherwise. The keywords use up all the possible
2247 * letters, so a negative number is good, but it has to be different
2248 * from other negative return values.
2250 if(r
== SEQ_EXCEPTION
-1) /* ol'cancelrooney */
2252 else if(r
== 10) /* return and goto flag screen */
2254 else if(r
== '!') /* flip intention */
2260 for(fp
= (flags
->flag_table
? *flags
->flag_table
: NULL
); fp
->name
; fp
++){
2261 if(r
== 'n' || r
== '*' || r
== 'd' || r
== 'a' || r
== 'f'){
2262 if((r
== 'n' && fp
->flag
== F_SEEN
)
2263 || (r
== '*' && fp
->flag
== F_FLAG
)
2264 || (r
== 'd' && fp
->flag
== F_DEL
)
2265 || (r
== 'f' && fp
->flag
== F_FWD
)
2266 || (r
== 'a' && fp
->flag
== F_ANS
)){
2267 fp
->set
= setflag
? CMD_FLAG_SET
: CMD_FLAG_CLEAR
;
2271 else if(allow_keyword_shortcuts
&& fp
->flag
== F_KEYWORD
){
2272 first_char
= (fp
->name
&& fp
->name
[0]) ? fp
->name
[0] : -2;
2273 if(isascii(first_char
) && isupper(first_char
))
2274 first_char
= tolower((unsigned char) first_char
);
2276 if(r
== first_char
){
2277 fp
->set
= setflag
? CMD_FLAG_SET
: CMD_FLAG_CLEAR
;
2283 if(ek
!= flag_text_opt
)
2284 fs_give((void **) &ek
);
2291 * (*ft) is an array of flag_table entries.
2294 free_flag_table(struct flag_table
**ft
)
2296 struct flag_table
*fp
;
2299 for(fp
= (*ft
); fp
->name
; fp
++){
2301 fs_give((void **) &fp
->name
);
2304 fs_give((void **) &fp
->keyword
);
2307 fs_give((void **) &fp
->comment
);
2310 fs_give((void **) ft
);
2315 /*----------------------------------------------------------------------
2316 Execute REPLY message command
2318 Args: state -- Various satate info
2319 msgmap -- map of c-client to local message numbers
2321 Result: reply sent or not
2325 cmd_reply(struct pine
*state
, MSGNO_S
*msgmap
, int aopt
, ACTION_S
*role
)
2329 if(any_messages(msgmap
, NULL
, "to Reply to")){
2330 if(MCMD_ISAGG(aopt
) && !pseudo_selected(state
->mail_stream
, msgmap
))
2333 rv
= reply(state
, role
);
2335 if(MCMD_ISAGG(aopt
))
2336 restore_selected(msgmap
);
2338 state
->mangled_screen
= 1;
2345 /*----------------------------------------------------------------------
2346 Execute FORWARD message command
2348 Args: state -- Various satate info
2349 msgmap -- map of c-client to local message numbers
2351 Result: selected message[s] forwarded or not
2355 cmd_forward(struct pine
*state
, MSGNO_S
*msgmap
, int aopt
, ACTION_S
*role
)
2359 if(any_messages(msgmap
, NULL
, "to Forward")){
2360 if(MCMD_ISAGG(aopt
) && !pseudo_selected(state
->mail_stream
, msgmap
))
2363 rv
= forward(state
, role
);
2365 if(MCMD_ISAGG(aopt
))
2366 restore_selected(msgmap
);
2368 state
->mangled_screen
= 1;
2375 /*----------------------------------------------------------------------
2376 Execute BOUNCE message command
2378 Args: state -- Various satate info
2379 msgmap -- map of c-client to local message numbers
2380 aopt -- aggregate options
2382 Result: selected message[s] bounced or not
2386 cmd_bounce(struct pine
*state
, MSGNO_S
*msgmap
, int aopt
, ACTION_S
*role
)
2390 if(any_messages(msgmap
, NULL
, "to Bounce")){
2392 if(MCMD_ISAGG(aopt
)){
2393 if(!pseudo_selected(state
->mail_stream
, msgmap
))
2396 else if((i
= any_lflagged(msgmap
, MN_SLCT
)) > 0
2397 && get_lflag(state
->mail_stream
, msgmap
,
2398 mn_m2raw(msgmap
, mn_get_cur(msgmap
)), MN_SLCT
) == 0)
2399 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
2400 _("WARNING: non-selected message is being bounced!"));
2402 && get_lflag(state
->mail_stream
, msgmap
,
2403 mn_m2raw(msgmap
, mn_get_cur(msgmap
)), MN_SLCT
))
2404 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
2405 _("WARNING: not bouncing all selected messages!"));
2407 rv
= bounce(state
, role
);
2409 if(MCMD_ISAGG(aopt
))
2410 restore_selected(msgmap
);
2412 state
->mangled_footer
= 1;
2419 /*----------------------------------------------------------------------
2420 Execute save message command: prompt for folder and call function to save
2422 Args: screen_line -- Line on the screen to prompt on
2423 message -- The MESSAGECACHE entry of message to save
2425 Result: The folder lister can be called to make selection; mangled screen set
2427 This does the prompting for the folder name to save to, possibly calling
2428 up the folder display for selection of folder by user.
2431 cmd_save(struct pine
*state
, MAILSTREAM
*stream
, MSGNO_S
*msgmap
, int aopt
, CmdWhere in_index
)
2433 char newfolder
[MAILTMPLEN
], nmsgs
[32], *nick
;
2434 int we_cancel
= 0, rv
= 0, save_flags
;
2436 CONTEXT_S
*cntxt
= NULL
;
2438 SaveDel del
= DontAsk
;
2439 SavePreserveOrder pre
= DontAskPreserve
;
2441 dprint((4, "\n - saving message -\n"));
2443 if(MCMD_ISAGG(aopt
) && !pseudo_selected(stream
, msgmap
))
2446 state
->ugly_consider_advancing_bit
= 0;
2447 if(F_OFF(F_SAVE_PARTIAL_WO_CONFIRM
, state
)
2448 && msgno_any_deletedparts(stream
, msgmap
)
2449 && want_to(_("Saved copy will NOT include entire message! Continue"),
2450 'y', 'n', NO_HELP
, WT_FLUSH_IN
| WT_SEQ_SENSITIVE
) != 'y'){
2451 restore_selected(msgmap
);
2452 cmd_cancelled("Save message");
2456 raw
= mn_m2raw(msgmap
, mn_get_cur(msgmap
));
2458 if(mn_total_cur(msgmap
) <= 1L){
2459 snprintf(nmsgs
, sizeof(nmsgs
), "Msg #%ld ", mn_get_cur(msgmap
));
2460 nmsgs
[sizeof(nmsgs
)-1] = '\0';
2461 e
= pine_mail_fetchstructure(stream
, raw
, NULL
);
2463 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
2464 _("Can't save message. Error accessing folder"));
2465 restore_selected(msgmap
);
2470 snprintf(nmsgs
, sizeof(nmsgs
), "%s msgs ", comatose(mn_total_cur(msgmap
)));
2471 nmsgs
[sizeof(nmsgs
)-1] = '\0';
2473 /* e is just used to get a default save folder from the first msg */
2474 e
= pine_mail_fetchstructure(stream
,
2475 mn_m2raw(msgmap
, mn_first_cur(msgmap
)),
2479 del
= (!READONLY_FOLDER(stream
) && F_OFF(F_SAVE_WONT_DELETE
, ps_global
))
2481 if(mn_total_cur(msgmap
) > 1L)
2482 pre
= F_OFF(F_AGG_SEQ_COPY
, ps_global
) ? Preserve
: NoPreserve
;
2484 pre
= DontAskPreserve
;
2486 if(save_prompt(state
, &cntxt
, newfolder
, sizeof(newfolder
), nmsgs
, e
,
2487 raw
, NULL
, &del
, &pre
)){
2489 if(ps_global
&& ps_global
->ttyo
){
2490 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
2491 ps_global
->mangled_footer
= 1;
2494 save_flags
= SV_FIX_DELS
;
2495 if(pre
== RetPreserve
)
2496 save_flags
|= SV_PRESERVE
;
2498 save_flags
|= SV_DELETE
;
2499 if(ps_global
->context_list
== cntxt
&& !strucmp(newfolder
, ps_global
->inbox_name
))
2500 save_flags
|= SV_INBOXWOCNTXT
;
2502 we_cancel
= busy_cue(_("Saving"), NULL
, 1);
2503 i
= save(state
, stream
, cntxt
, newfolder
, msgmap
, save_flags
);
2507 if(i
== mn_total_cur(msgmap
)){
2509 if(mn_total_cur(msgmap
) <= 1L){
2510 int need
, avail
= ps_global
->ttyo
->screen_cols
- 2;
2511 int lennick
, lenfldr
;
2514 && ps_global
->context_list
->next
2515 && context_isambig(newfolder
)){
2516 lennick
= MIN(strlen(cntxt
->nickname
), 500);
2517 lenfldr
= MIN(strlen(newfolder
), 500);
2518 need
= 27 + strlen(long2string(mn_get_cur(msgmap
))) +
2522 need
-= MIN(lennick
-10, need
-avail
);
2523 lennick
-= MIN(lennick
-10, need
-avail
);
2526 if(need
> avail
&& lenfldr
> 10)
2527 lenfldr
-= MIN(lenfldr
-10, need
-avail
);
2530 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,
2531 "Message %s copied to \"%s\" in <%s>",
2532 long2string(mn_get_cur(msgmap
)),
2533 short_str(newfolder
, (char *)(tmp_20k_buf
+1000), 1000,
2535 short_str(cntxt
->nickname
,
2536 (char *)(tmp_20k_buf
+2000), 1000,
2538 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
2540 else if((nick
=folder_is_target_of_nick(newfolder
, cntxt
)) != NULL
){
2541 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,
2542 "Message %s copied to \"%s\"",
2543 long2string(mn_get_cur(msgmap
)),
2545 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
2548 char *f
= " folder";
2550 lenfldr
= MIN(strlen(newfolder
), 500);
2551 need
= 28 + strlen(long2string(mn_get_cur(msgmap
))) +
2556 if(need
> avail
&& lenfldr
> 10)
2557 lenfldr
-= MIN(lenfldr
-10, need
-avail
);
2560 snprintf(tmp_20k_buf
,SIZEOF_20KBUF
,
2561 "Message %s copied to%s \"%s\"",
2562 long2string(mn_get_cur(msgmap
)), f
,
2563 short_str(newfolder
, (char *)(tmp_20k_buf
+1000), 1000,
2565 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
2569 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s messages saved",
2570 comatose(mn_total_cur(msgmap
)));
2571 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
2575 strncat(tmp_20k_buf
, " and deleted", SIZEOF_20KBUF
-strlen(tmp_20k_buf
)-1);
2576 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
2579 q_status_message(SM_ORDER
, 0, 3, tmp_20k_buf
);
2581 if(!MCMD_ISAGG(aopt
) && F_ON(F_SAVE_ADVANCES
, state
)){
2582 if(sp_new_mail_count(stream
))
2583 process_filter_patterns(stream
, msgmap
,
2584 sp_new_mail_count(stream
));
2586 mn_inc_cur(stream
, msgmap
,
2587 (in_index
== View
&& THREADING()
2588 && sp_viewing_a_thread(stream
))
2590 : (in_index
== View
)
2591 ? MH_ANYTHD
: MH_NONE
);
2594 state
->ugly_consider_advancing_bit
= 1;
2598 if(MCMD_ISAGG(aopt
)) /* straighten out fakes */
2599 restore_selected(msgmap
);
2602 update_titlebar_status(); /* make sure they see change */
2609 role_compose(struct pine
*state
)
2613 if(F_ON(F_ALT_ROLE_MENU
, state
) && mn_get_total(state
->msgmap
) > 0L){
2616 if(nonempty_patterns(ROLE_DO_ROLES
, &pstate
) && first_pattern(&pstate
)){
2617 action
= radio_buttons(_("Compose, Forward, Reply, or Bounce? "),
2618 -FOOTER_ROWS(state
), choose_action
,
2619 'c', 'x', h_role_compose
, RB_NORM
);
2622 q_status_message(SM_ORDER
, 0, 3,
2623 _("No roles available. Use Setup/Rules to add roles."));
2630 if(action
== 'c' || action
== 'r' || action
== 'f' || action
== 'b'){
2631 ACTION_S
*role
= NULL
;
2632 void (*prev_screen
)(struct pine
*) = NULL
, (*redraw
)(void) = NULL
;
2634 redraw
= state
->redrawer
;
2635 state
->redrawer
= NULL
;
2636 prev_screen
= state
->prev_screen
;
2638 state
->next_screen
= SCREEN_FUN_NULL
;
2641 if(role_select_screen(state
, &role
,
2642 action
== 'f' ? MC_FORWARD
:
2643 action
== 'r' ? MC_REPLY
:
2644 action
== 'b' ? MC_BOUNCE
:
2645 action
== 'c' ? MC_COMPOSE
: 0) < 0){
2646 cmd_cancelled(action
== 'f' ? _("Forward") :
2647 action
== 'r' ? _("Reply") :
2648 action
== 'c' ? _("Composition") : _("Bounce"));
2649 state
->next_screen
= prev_screen
;
2650 state
->redrawer
= redraw
;
2651 state
->mangled_screen
= 1;
2655 * If default role was selected (NULL) we need to make
2656 * up a role which won't do anything, but will cause
2657 * compose_mail to think there's already a role so that
2658 * it won't try to confirm the default.
2661 role
= combine_inherited_role(role
);
2663 role
= (ACTION_S
*) fs_get(sizeof(*role
));
2664 memset((void *) role
, 0, sizeof(*role
));
2665 role
->nick
= cpystr("Default Role");
2668 state
->redrawer
= NULL
;
2671 compose_mail(NULL
, NULL
, role
, NULL
, NULL
);
2675 (void) reply(state
, role
);
2679 (void) forward(state
, role
);
2683 (void) bounce(state
, role
);
2690 state
->next_screen
= prev_screen
;
2691 state
->redrawer
= redraw
;
2692 state
->mangled_screen
= 1;
2698 /*----------------------------------------------------------------------
2699 Do the dirty work of prompting the user for a folder name
2702 nfldr should be a buffer at least MAILTMPLEN long
2703 dela -- a pointer to a SaveDel. If it is
2704 DontAsk on input, don't offer Delete prompt
2705 Del on input, offer Delete command with default of Delete
2707 RetDel and RetNoDel are return values
2714 save_prompt(struct pine
*state
, CONTEXT_S
**cntxt
, char *nfldr
, size_t len_nfldr
,
2715 char *nmsgs
, ENVELOPE
*env
, long int rawmsgno
, char *section
,
2716 SaveDel
*dela
, SavePreserveOrder
*prea
)
2718 int rc
, ku
= -1, n
, flags
, last_rc
= 0, saveable_count
= 0, done
= 0;
2719 int delindex
, preindex
, r
;
2720 char prompt
[6*MAX_SCREEN_COLS
+1], *p
, expanded
[MAILTMPLEN
];
2721 char *buf
= tmp_20k_buf
;
2725 SaveDel del
= DontAsk
;
2726 SavePreserveOrder pre
= DontAskPreserve
;
2727 char *deltext
= NULL
;
2728 static HISTORY_S
*history
= NULL
;
2732 if(state
== NULL
&& cntxt
== NULL
&& nfldr
== NULL
&& len_nfldr
== 0
2733 && nmsgs
== NULL
&& env
== NULL
&& rawmsgno
== 0L && section
== NULL
2734 && dela
== NULL
&& prea
== NULL
){
2736 free_hist(&history
);
2741 alpine_panic("no context ptr in save_prompt");
2743 init_hist(&history
, HISTSIZE
);
2745 if(!(folder
= save_get_default(state
, env
, rawmsgno
, section
, cntxt
)))
2746 return(0); /* message expunged! */
2748 /* how many context's can be saved to... */
2749 for(tc
= state
->context_list
; tc
; tc
= tc
->next
)
2753 /* set up extra command option keys */
2755 ekey
[rc
].ch
= ctrl('T');
2757 ekey
[rc
].name
= "^T";
2758 /* TRANSLATORS: command means go to Folders list */
2759 ekey
[rc
++].label
= N_("To Fldrs");
2761 if(saveable_count
> 1){
2762 ekey
[rc
].ch
= ctrl('P');
2764 ekey
[rc
].name
= "^P";
2765 ekey
[rc
++].label
= N_("Prev Collection");
2767 ekey
[rc
].ch
= ctrl('N');
2769 ekey
[rc
].name
= "^N";
2770 ekey
[rc
++].label
= N_("Next Collection");
2773 if(F_ON(F_ENABLE_TAB_COMPLETE
, ps_global
)){
2776 ekey
[rc
].name
= "TAB";
2777 /* TRANSLATORS: command asks alpine to complete the name when tab is typed */
2778 ekey
[rc
++].label
= N_("Complete");
2781 if(F_ON(F_ENABLE_SUB_LISTS
, ps_global
)){
2782 ekey
[rc
].ch
= ctrl('X');
2784 ekey
[rc
].name
= "^X";
2785 /* TRANSLATORS: list all the matches */
2786 ekey
[rc
++].label
= N_("ListMatches");
2789 if(dela
&& (*dela
== NoDel
|| *dela
== Del
)){
2790 ekey
[rc
].ch
= ctrl('R');
2792 ekey
[rc
].name
= "^R";
2797 if(prea
&& (*prea
== NoPreserve
|| *prea
== Preserve
)){
2798 ekey
[rc
].ch
= ctrl('W');
2800 ekey
[rc
].name
= "^W";
2805 if(saveable_count
> 1 && F_ON(F_DISABLE_SAVE_INPUT_HISTORY
, ps_global
)){
2806 ekey
[rc
].ch
= KEY_UP
;
2809 ekey
[rc
++].label
= "";
2811 ekey
[rc
].ch
= KEY_DOWN
;
2814 ekey
[rc
++].label
= "";
2816 else if(F_OFF(F_DISABLE_SAVE_INPUT_HISTORY
, ps_global
)){
2817 ekey
[rc
].ch
= KEY_UP
;
2821 ekey
[rc
++].label
= "";
2823 ekey
[rc
].ch
= KEY_DOWN
;
2826 ekey
[rc
++].label
= "";
2834 /* only show collection number if more than one available */
2835 if(ps_global
->context_list
->next
)
2836 snprintf(prompt
, sizeof(prompt
), "SAVE%s %sto folder in <%s> [%s] : ",
2837 deltext
? deltext
: "",
2839 short_str((*cntxt
)->nickname
, shortbuf
, sizeof(shortbuf
), 16, EndDots
),
2840 strsquish(buf
, SIZEOF_20KBUF
, folder
, 25));
2842 snprintf(prompt
, sizeof(prompt
), "SAVE%s %sto folder [%s] : ",
2843 deltext
? deltext
: "",
2844 nmsgs
, strsquish(buf
, SIZEOF_20KBUF
, folder
, 40));
2846 prompt
[sizeof(prompt
)-1] = '\0';
2849 * If the prompt won't fit, try removing deltext.
2851 if(state
->ttyo
->screen_cols
< strlen(prompt
) + MIN_OPT_ENT_WIDTH
&& deltext
){
2852 if(ps_global
->context_list
->next
)
2853 snprintf(prompt
, sizeof(prompt
), "SAVE %sto folder in <%s> [%s] : ",
2855 short_str((*cntxt
)->nickname
, shortbuf
, sizeof(shortbuf
), 16, EndDots
),
2856 strsquish(buf
, SIZEOF_20KBUF
, folder
, 25));
2858 snprintf(prompt
, sizeof(prompt
), "SAVE %sto folder [%s] : ",
2859 nmsgs
, strsquish(buf
, SIZEOF_20KBUF
, folder
, 40));
2861 prompt
[sizeof(prompt
)-1] = '\0';
2865 * If the prompt still won't fit, remove the extra info contained
2868 if(state
->ttyo
->screen_cols
< strlen(prompt
) + MIN_OPT_ENT_WIDTH
&& *nmsgs
){
2869 if(ps_global
->context_list
->next
)
2870 snprintf(prompt
, sizeof(prompt
), "SAVE to folder in <%s> [%s] : ",
2871 short_str((*cntxt
)->nickname
, shortbuf
, sizeof(shortbuf
), 16, EndDots
),
2872 strsquish(buf
, SIZEOF_20KBUF
, folder
, 25));
2874 snprintf(prompt
, sizeof(prompt
), "SAVE to folder [%s] : ",
2875 strsquish(buf
, SIZEOF_20KBUF
, folder
, 25));
2877 prompt
[sizeof(prompt
)-1] = '\0';
2881 ekey
[delindex
].label
= (del
== NoDel
) ? "Delete" : "No Delete";
2883 if(pre
!= DontAskPreserve
)
2884 ekey
[preindex
].label
= (pre
== NoPreserve
) ? "Preserve Order" : "Any Order";
2887 if(items_in_hist(history
) > 1){
2888 ekey
[ku
].name
= HISTORY_UP_KEYNAME
;
2889 ekey
[ku
].label
= HISTORY_KEYLABEL
;
2890 ekey
[ku
+1].name
= HISTORY_DOWN_KEYNAME
;
2891 ekey
[ku
+1].label
= HISTORY_KEYLABEL
;
2895 ekey
[ku
].label
= "";
2896 ekey
[ku
+1].name
= "";
2897 ekey
[ku
+1].label
= "";
2901 flags
= OE_APPEND_CURRENT
| OE_SEQ_SENSITIVE
;
2902 rc
= optionally_enter(nfldr
, -FOOTER_ROWS(state
), 0, len_nfldr
,
2903 prompt
, ekey
, help
, &flags
);
2907 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
2908 _("Error reading folder name"));
2913 removing_trailing_white_space(nfldr
);
2914 removing_leading_white_space(nfldr
);
2916 if(*nfldr
|| *folder
){
2917 char *p
, *name
, *fullname
= NULL
;
2918 int exists
, breakout
= FALSE
;
2921 strncpy(nfldr
, folder
, len_nfldr
-1);
2922 nfldr
[len_nfldr
-1] = '\0';
2925 save_hist(history
, nfldr
, 0, (void *) *cntxt
);
2927 if(!(name
= folder_is_nick(nfldr
, FOLDERS(*cntxt
), 0)))
2930 if(update_folder_spec(expanded
, sizeof(expanded
), name
)){
2931 strncpy(name
= nfldr
, expanded
, len_nfldr
-1);
2932 nfldr
[len_nfldr
-1] = '\0';
2935 exists
= folder_name_exists(*cntxt
, name
, &fullname
);
2937 if(exists
== FEX_ERROR
){
2938 q_status_message1(SM_ORDER
, 0, 3,
2939 _("Problem accessing folder \"%s\""),
2945 strncpy(name
= nfldr
, fullname
, len_nfldr
-1);
2946 nfldr
[len_nfldr
-1] = '\0';
2947 fs_give((void **) &fullname
);
2951 if(exists
& FEX_ISFILE
){
2954 else if((exists
& FEX_ISDIR
)){
2955 char tmp
[MAILTMPLEN
];
2959 CONTEXT_S
*fake_context
;
2962 strncpy(tmp
, name
, sizeof(tmp
));
2963 tmp
[sizeof(tmp
)-2-1] = '\0';
2964 if(tmp
[(l
= strlen(tmp
)) - 1] != tc
->dir
->delim
){
2965 if(l
< sizeof(tmp
)){
2966 tmp
[l
] = tc
->dir
->delim
;
2967 strncpy(&tmp
[l
+1], "[]", sizeof(tmp
)-(l
+1));
2971 strncat(tmp
, "[]", sizeof(tmp
)-strlen(tmp
)-1);
2973 tmp
[sizeof(tmp
)-1] = '\0';
2975 fake_context
= new_context(tmp
, 0);
2977 done
= display_folder_list(&fake_context
, nfldr
,
2978 1, folders_for_save
);
2979 free_context(&fake_context
);
2981 else if(tc
->dir
->delim
2982 && (p
= strrindex(name
, tc
->dir
->delim
))
2984 done
= display_folder_list(cntxt
, nfldr
,
2985 1, folders_for_save
);
2987 q_status_message1(SM_ORDER
, 3, 3,
2988 _("\"%s\" is a directory"), name
);
2990 && !((p
=strrindex(name
, tc
->dir
->delim
)) && *(p
+1) == '\0')){
2991 strncpy(tmp
, name
, sizeof(tmp
));
2992 tmp
[sizeof(tmp
)-1] = '\0';
2993 snprintf(nfldr
, len_nfldr
, "%s%c", tmp
, tc
->dir
->delim
);
2997 else{ /* Doesn't exist, create! */
2998 if((fullname
= folder_as_breakout(*cntxt
, name
)) != NULL
){
2999 strncpy(name
= nfldr
, fullname
, len_nfldr
-1);
3000 nfldr
[len_nfldr
-1] = '\0';
3001 fs_give((void **) &fullname
);
3004 switch(create_for_save(*cntxt
, name
)){
3005 case 1 : /* success */
3008 case 0 : /* error */
3009 case -1 : /* declined */
3018 /* else fall thru like they cancelled */
3021 cmd_cancelled("Save message");
3026 r
= display_folder_list(cntxt
, nfldr
, 0, folders_for_save
);
3034 helper(h_save
, _("HELP FOR SAVE"), HLPD_SIMPLE
);
3035 ps_global
->mangled_screen
= 1;
3038 case 4 : /* redraw */
3041 case 10 : /* previous collection */
3042 for(tc
= (*cntxt
)->prev
; tc
; tc
= tc
->prev
)
3049 for(tc2
= (tc
= (*cntxt
))->next
; tc2
; tc2
= tc2
->next
)
3057 case 11 : /* next collection */
3061 if(((*cntxt
) = (*cntxt
)->next
) == NULL
)
3062 (*cntxt
) = ps_global
->context_list
;
3063 while(NEWS_TEST(*cntxt
) && (*cntxt
) != tc
);
3066 case 12 : /* file name completion */
3067 if(!folder_complete(*cntxt
, nfldr
, len_nfldr
, &n
)){
3068 if(n
&& last_rc
== 12 && !(flags
& OE_USER_MODIFIED
)){
3069 r
= display_folder_list(cntxt
, nfldr
, 1, folders_for_save
);
3071 done
++; /* bingo! */
3073 rc
= 0; /* burn last_rc */
3081 case 14 : /* file name completion */
3082 r
= display_folder_list(cntxt
, nfldr
, 2, folders_for_save
);
3084 done
++; /* bingo! */
3086 rc
= 0; /* burn last_rc */
3090 case 15 : /* Delete / No Delete */
3091 del
= (del
== NoDel
) ? Del
: NoDel
;
3092 deltext
= (del
== NoDel
) ? " (no delete)" : " (and delete)";
3095 case 16 : /* Preserve Order or not */
3096 pre
= (pre
== NoPreserve
) ? Preserve
: NoPreserve
;
3100 if((p
= get_prev_hist(history
, nfldr
, 0, (void *) *cntxt
)) != NULL
){
3101 strncpy(nfldr
, p
, len_nfldr
);
3102 nfldr
[len_nfldr
-1] = '\0';
3103 if(history
->hist
[history
->curindex
])
3104 *cntxt
= (CONTEXT_S
*) history
->hist
[history
->curindex
]->cntxt
;
3112 if((p
= get_next_hist(history
, nfldr
, 0, (void *) *cntxt
)) != NULL
){
3113 strncpy(nfldr
, p
, len_nfldr
);
3114 nfldr
[len_nfldr
-1] = '\0';
3115 if(history
->hist
[history
->curindex
])
3116 *cntxt
= (CONTEXT_S
*) history
->hist
[history
->curindex
]->cntxt
;
3124 alpine_panic("Unhandled case");
3131 ps_global
->mangled_footer
= 1;
3137 strncpy(ps_global
->last_save_folder
, nfldr
, sizeof(ps_global
->last_save_folder
)-1);
3138 ps_global
->last_save_folder
[sizeof(ps_global
->last_save_folder
)-1] = '\0';
3140 ps_global
->last_save_context
= *cntxt
;
3143 strncpy(nfldr
, folder
, len_nfldr
-1);
3144 nfldr
[len_nfldr
-1] = '\0';
3147 /* nickname? Copy real name to nfldr */
3149 && context_isambig(nfldr
)
3150 && (p
= folder_is_nick(nfldr
, FOLDERS(*cntxt
), 0))){
3151 strncpy(nfldr
, p
, len_nfldr
-1);
3152 nfldr
[len_nfldr
-1] = '\0';
3155 if(dela
&& (*dela
== NoDel
|| *dela
== Del
))
3156 *dela
= (del
== NoDel
) ? RetNoDel
: RetDel
;
3158 if(prea
&& (*prea
== NoPreserve
|| *prea
== Preserve
))
3159 *prea
= (pre
== NoPreserve
) ? RetNoPreserve
: RetPreserve
;
3165 /*----------------------------------------------------------------------
3166 Prompt user before implicitly creating a folder for saving
3168 Args: context - context to create folder in
3169 folder - folder name to create
3171 Result: 1 on proceed, -1 on decline, 0 on error
3175 create_for_save_prompt(CONTEXT_S
*context
, char *folder
, int sequence_sensitive
)
3177 if(context
&& ps_global
->context_list
->next
&& context_isambig(folder
)){
3178 if(context
->use
& CNTXT_INCMNG
){
3179 snprintf(tmp_20k_buf
,SIZEOF_20KBUF
,
3180 _("\"%.15s%s\" doesn't exist - Add it in FOLDER LIST screen"),
3181 folder
, (strlen(folder
) > 15) ? "..." : "");
3182 q_status_message(SM_ORDER
, 3, 3, tmp_20k_buf
);
3183 return(0); /* error */
3186 snprintf(tmp_20k_buf
,SIZEOF_20KBUF
,
3187 _("Folder \"%.15s%s\" in <%.15s%s> doesn't exist. Create"),
3188 folder
, (strlen(folder
) > 15) ? "..." : "",
3190 (strlen(context
->nickname
) > 15) ? "..." : "");
3193 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,
3194 _("Folder \"%.40s%s\" doesn't exist. Create"),
3195 folder
, strlen(folder
) > 40 ? "..." : "");
3197 if(want_to(tmp_20k_buf
, 'y', 'n',
3198 NO_HELP
, (sequence_sensitive
) ? WT_SEQ_SENSITIVE
: WT_NORM
) != 'y'){
3199 cmd_cancelled("Save message");
3208 /*----------------------------------------------------------------------
3209 Expunge messages from current folder
3211 Args: state -- pointer to struct holding a bunch of pine state
3212 msgmap -- table mapping msg nums to c-client sequence nums
3213 qline -- screen line to ask questions on
3214 agg -- boolean indicating we're to operate on aggregate set
3219 cmd_expunge(struct pine
*state
, MAILSTREAM
*stream
, MSGNO_S
*msgmap
, int agg
)
3221 long del_count
, prefilter_del_count
;
3222 int we_cancel
= 0, rv
= 0;
3223 char prompt
[MAX_SCREEN_COLS
+1];
3225 COLOR_PAIR
*lastc
= NULL
;
3227 dprint((2, "\n - expunge -\n"));
3231 sequence
= MCMD_ISAGG(agg
) ? selected_sequence(stream
, msgmap
, NULL
, 0) : NULL
;
3233 if(MCMD_ISAGG(agg
)){
3236 for(i
= 1L; i
<= stream
->nmsgs
; i
++){
3237 if((mc
= mail_elt(stream
, i
)) != NULL
3238 && mc
->sequence
&& mc
->deleted
)
3242 q_status_message(SM_ORDER
, 0, 4,
3243 _("No selected messages are deleted"));
3247 if(!any_messages(msgmap
, NULL
, "to Expunge"))
3251 if(IS_NEWS(stream
) && stream
->rdonly
){
3252 if(!MCMD_ISAGG(agg
))
3253 del_count
= count_flagged(stream
, F_DEL
);
3255 state
->mangled_footer
= 1; /* MAX_SCREEN_COLS+1 = sizeof(prompt) */
3256 snprintf(prompt
, sizeof(prompt
), "Exclude %ld message%s from %.*s", del_count
,
3257 plural(del_count
), MAX_SCREEN_COLS
+1-40,
3258 pretty_fn(state
->cur_folder
));
3259 prompt
[sizeof(prompt
)-1] = '\0';
3260 if(F_ON(F_FULL_AUTO_EXPUNGE
, state
)
3261 || (F_ON(F_AUTO_EXPUNGE
, state
)
3262 && (state
->context_current
3263 && (state
->context_current
->use
& CNTXT_INCMNG
))
3264 && context_isambig(state
->cur_folder
))
3265 || want_to(prompt
, 'y', 0, NO_HELP
, WT_NORM
) == 'y'){
3267 if(F_ON(F_NEWS_CROSS_DELETE
, state
))
3268 cross_delete_crossposts(stream
);
3270 msgno_exclude_deleted(stream
, msgmap
, sequence
);
3271 clear_index_cache(stream
, 0);
3274 * This is kind of surprising at first. For most sort
3275 * orders, if the whole set is sorted, then any subset
3276 * is also sorted. Not so for threaded sorts.
3278 if(SORT_IS_THREADED(msgmap
))
3279 refresh_sort(stream
, msgmap
, SRT_NON
);
3281 state
->mangled_body
= 1;
3282 state
->mangled_header
= 1;
3283 q_status_message2(SM_ORDER
, 0, 4,
3284 "%s message%s excluded",
3285 long2string(del_count
),
3289 any_messages(NULL
, NULL
, "Excluded");
3292 any_messages(NULL
, "deleted", "to Exclude");
3296 else if(READONLY_FOLDER(stream
)){
3297 q_status_message(SM_ORDER
, 0, 4,
3298 _("Can't expunge. Folder is read-only"));
3302 if(!MCMD_ISAGG(agg
)){
3303 prefilter_del_count
= count_flagged(stream
, F_DEL
|F_NOFILT
);
3304 mail_expunge_prefilter(stream
, MI_NONE
);
3305 del_count
= count_flagged(stream
, F_DEL
|F_NOFILT
);
3310 unsigned char *fname
= folder_name_decoded((unsigned char *)state
->cur_folder
);
3311 /* MAX_SCREEN_COLS+1 = sizeof(prompt) */
3312 snprintf(prompt
, sizeof(prompt
), "Expunge %ld message%s from %.*s", del_count
,
3313 plural(del_count
), MAX_SCREEN_COLS
+1-40,
3314 pretty_fn((char *) fname
));
3315 if(fname
) fs_give((void **)&fname
);
3316 prompt
[sizeof(prompt
)-1] = '\0';
3317 state
->mangled_footer
= 1;
3319 if(F_ON(F_FULL_AUTO_EXPUNGE
, state
)
3320 || (F_ON(F_AUTO_EXPUNGE
, state
)
3321 && ((!strucmp(state
->cur_folder
,state
->inbox_name
))
3322 || (state
->context_current
->use
& CNTXT_INCMNG
))
3323 && context_isambig(state
->cur_folder
))
3324 || (ret
=want_to(prompt
, 'y', 0, NO_HELP
, WT_NORM
)) == 'y')
3328 cmd_cancelled("Expunge");
3334 dprint((8, "Expunge max:%ld cur:%ld kill:%d\n",
3335 mn_get_total(msgmap
), mn_get_cur(msgmap
), del_count
));
3337 lastc
= pico_set_colors(state
->VAR_TITLE_FORE_COLOR
,
3338 state
->VAR_TITLE_BACK_COLOR
,
3341 PutLine0(0, 0, "**"); /* indicate delay */
3344 (void)pico_set_colorp(lastc
, PSC_NONE
);
3345 free_color_pair(&lastc
);
3348 MoveCursor(state
->ttyo
->screen_rows
-FOOTER_ROWS(state
), 0);
3351 we_cancel
= busy_cue(_("Expunging"), NULL
, 1);
3353 if(cmd_expunge_work(stream
, msgmap
, sequence
))
3354 state
->mangled_body
= 1;
3357 fs_give((void **)&sequence
);
3360 cancel_busy_cue((sp_expunge_count(stream
) > 0) ? 0 : -1);
3362 lastc
= pico_set_colors(state
->VAR_TITLE_FORE_COLOR
,
3363 state
->VAR_TITLE_BACK_COLOR
,
3365 PutLine0(0, 0, " "); /* indicate delay's over */
3368 (void)pico_set_colorp(lastc
, PSC_NONE
);
3369 free_color_pair(&lastc
);
3374 if(sp_expunge_count(stream
) > 0){
3376 * This is kind of surprising at first. For most sort
3377 * orders, if the whole set is sorted, then any subset
3378 * is also sorted. Not so for threaded sorts.
3380 if(SORT_IS_THREADED(msgmap
))
3381 refresh_sort(stream
, msgmap
, SRT_NON
);
3385 unsigned char *fname
= folder_name_decoded((unsigned char *)state
->cur_folder
);
3386 q_status_message1(SM_ORDER
, 0, 3,
3387 _("No messages expunged from folder \"%s\""),
3388 pretty_fn((char *) fname
));
3389 if(fname
) fs_give((void **)&fname
);
3391 else if(!prefilter_del_count
)
3392 q_status_message(SM_ORDER
, 0, 3,
3393 _("No messages marked deleted. No messages expunged."));
3399 /*----------------------------------------------------------------------
3400 Expunge_and_close callback to prompt user for confirmation
3402 Args: stream -- folder's stream
3403 folder -- name of folder containing folders
3404 deleted -- number of del'd msgs
3406 Result: 'y' to continue with expunge
3409 expunge_prompt(MAILSTREAM
*stream
, char *folder
, long int deleted
)
3413 char prompt_b
[MAX_SCREEN_COLS
+1], temp
[MAILTMPLEN
+1], buff
[MAX_SCREEN_COLS
+1];
3414 char *short_folder_name
;
3419 snprintf(temp
, sizeof(temp
), "%ld", deleted
);
3420 charcnt
= strlen(temp
)+1;
3423 max_folder
= MAX(1,MAXPROMPT
- (36+charcnt
));
3424 strncpy(temp
, folder
, sizeof(temp
));
3425 temp
[sizeof(temp
)-1] = '\0';
3426 short_folder_name
= short_str(temp
,buff
,sizeof(buff
),max_folder
,FrontDots
);
3429 snprintf(prompt_b
, sizeof(prompt_b
),
3430 "Delete %s%ld message%s from \"%s\"",
3431 (deleted
> 1L) ? "all " : "", deleted
,
3432 plural(deleted
), short_folder_name
);
3434 snprintf(prompt_b
, sizeof(prompt_b
),
3435 "Expunge the %ld deleted message%s from \"%s\"",
3436 deleted
, deleted
== 1 ? "" : "s",
3439 return(want_to(prompt_b
, 'y', 0, NO_HELP
, WT_NORM
));
3444 * This is used with multiple append saves. Call it once before
3445 * the series of appends with SSCP_INIT and once after all are
3446 * done with SSCP_END. In between, it is called automatically
3447 * from save_fetch_append or save_fetch_append_cb when we need
3448 * to ask the user if he or she wants to continue even though
3449 * announced message size doesn't match the actual message size.
3450 * As of 2008-02-29 the gmail IMAP server has these size mismatches
3451 * on a regular basis even though the data is ok.
3454 save_size_changed_prompt(long msgno
, int flags
)
3458 static int remember_the_yes
= 0;
3459 static int possible_corruption
= 0;
3460 static ESCKEY_S save_size_opts
[] = {
3461 {'y', 'y', "Y", "Yes"},
3462 {'n', 'n', "N", "No"},
3463 {'a', 'a', "A", "yes to All"},
3467 if(F_ON(F_IGNORE_SIZE
, ps_global
))
3470 if(flags
& SSCP_INIT
|| flags
& SSCP_END
){
3471 if(flags
& SSCP_END
&& possible_corruption
)
3472 q_status_message(SM_ORDER
, 3, 3, "There is possible data corruption, check the results");
3474 remember_the_yes
= 0;
3475 possible_corruption
= 0;
3476 ps_global
->noshow_error
= 0;
3477 ps_global
->noshow_warn
= 0;
3481 if(remember_the_yes
){
3482 snprintf(prompt
, sizeof(prompt
),
3483 "Message to save shrank! (msg # %ld): Continuing", msgno
);
3484 q_status_message(SM_ORDER
, 0, 3, prompt
);
3485 display_message('x');
3486 return(remember_the_yes
);
3489 snprintf(prompt
, sizeof(prompt
),
3490 "Message to save shrank! (msg # %ld): Continue anyway ? ", msgno
);
3491 ret
= radio_buttons(prompt
, -FOOTER_ROWS(ps_global
), save_size_opts
,
3492 'n', 0, h_save_size_changed
, RB_NORM
);
3496 remember_the_yes
= 'y';
3497 possible_corruption
++;
3498 return(remember_the_yes
);
3501 possible_corruption
++;
3505 possible_corruption
= 0;
3506 ps_global
->noshow_error
= 1;
3507 ps_global
->noshow_warn
= 1;
3515 /*----------------------------------------------------------------------
3516 Expunge_and_close callback that happens once the decision to expunge
3517 and close has been made and before expunging and closing begins
3520 Args: stream -- folder's stream
3521 folder -- name of folder containing folders
3522 deleted -- number of del'd msgs
3524 Result: 'y' to continue with expunge
3527 expunge_and_close_begins(int flags
, char *folder
)
3529 if(!(flags
& EC_NO_CLOSE
)){
3530 unsigned char *fname
= folder_name_decoded((unsigned char *)folder
);
3531 q_status_message1(SM_INFO
, 0, 1, "Closing \"%.200s\"...", (char *) fname
);
3532 flush_status_messages(1);
3533 if(fname
) fs_give((void **)&fname
);
3538 /*----------------------------------------------------------------------
3539 Export a message to a plain file in users home directory
3541 Args: state -- pointer to struct holding a bunch of pine state
3542 msgmap -- table mapping msg nums to c-client sequence nums
3543 qline -- screen line to ask questions on
3544 agg -- boolean indicating we're to operate on aggregate set
3549 cmd_export(struct pine
*state
, MSGNO_S
*msgmap
, int qline
, int aopt
)
3551 char filename
[MAXPATH
+1], full_filename
[MAXPATH
+1], *err
;
3553 int r
, leading_nl
, failure
= 0, orig_errno
, rflags
= GER_NONE
;
3554 int flags
= GE_IS_EXPORT
| GE_SEQ_SENSITIVE
, rv
= 0;
3558 long i
, count
= 0L, start_of_append
, rawno
;
3561 struct variable
*vars
= state
? ps_global
->vars
: NULL
;
3562 ESCKEY_S export_opts
[5];
3563 static HISTORY_S
*history
= NULL
;
3565 if(state
== NULL
&& msgmap
== NULL
&& qline
== 0 && aopt
== 0){
3567 free_hist(&history
);
3571 if(ps_global
->restricted
){
3572 q_status_message(SM_ORDER
, 0, 3,
3573 "Alpine demo can't export messages to files");
3577 if(MCMD_ISAGG(aopt
) && !pseudo_selected(state
->mail_stream
, msgmap
))
3580 export_opts
[i
= 0].ch
= ctrl('T');
3581 export_opts
[i
].rval
= 10;
3582 export_opts
[i
].name
= "^T";
3583 export_opts
[i
++].label
= N_("To Files");
3585 #if !defined(DOS) && !defined(MAC) && !defined(OS2)
3586 if(ps_global
->VAR_DOWNLOAD_CMD
&& ps_global
->VAR_DOWNLOAD_CMD
[0]){
3587 export_opts
[i
].ch
= ctrl('V');
3588 export_opts
[i
].rval
= 12;
3589 export_opts
[i
].name
= "^V";
3590 /* TRANSLATORS: this is an abbreviation for Download Messages */
3591 export_opts
[i
++].label
= N_("Downld Msg");
3593 #endif /* !(DOS || MAC) */
3595 if(F_ON(F_ENABLE_TAB_COMPLETE
,ps_global
)){
3596 export_opts
[i
].ch
= ctrl('I');
3597 export_opts
[i
].rval
= 11;
3598 export_opts
[i
].name
= "TAB";
3599 export_opts
[i
++].label
= N_("Complete");
3603 /* Commented out since it's not yet support! */
3604 if(F_ON(F_ENABLE_SUB_LISTS
,ps_global
)){
3605 export_opts
[i
].ch
= ctrl('X');
3606 export_opts
[i
].rval
= 14;
3607 export_opts
[i
].name
= "^X";
3608 export_opts
[i
++].label
= N_("ListMatches");
3613 * If message has attachments, add a toggle that will allow the user
3614 * to save all of the attachments to a single directory, using the
3615 * names provided with the attachments or part names. What we'll do is
3616 * export the message as usual, and then export the attachments into
3617 * a subdirectory that did not exist before. The subdir will be named
3618 * something based on the name of the file being saved to, but a
3621 if(!MCMD_ISAGG(aopt
)
3622 && state
->mail_stream
3623 && (rawno
= mn_m2raw(msgmap
, mn_get_cur(msgmap
))) > 0L
3624 && rawno
<= state
->mail_stream
->nmsgs
3625 && (env
= pine_mail_fetchstructure(state
->mail_stream
, rawno
, &b
))
3627 && b
->type
== TYPEMULTIPART
3629 && strucmp(b
->subtype
, "ALTERNATIVE") != 0){
3632 part
= b
->nested
.part
; /* 1st part */
3633 if(part
&& part
->next
)
3634 flags
|= GE_ALLPARTS
;
3637 export_opts
[i
].ch
= -1;
3640 if(mn_total_cur(msgmap
) <= 1L){
3641 snprintf(nmsgs
, sizeof(nmsgs
), "Msg #%ld", mn_get_cur(msgmap
));
3642 nmsgs
[sizeof(nmsgs
)-1] = '\0';
3645 snprintf(nmsgs
, sizeof(nmsgs
), "%s messages", comatose(mn_total_cur(msgmap
)));
3646 nmsgs
[sizeof(nmsgs
)-1] = '\0';
3649 r
= get_export_filename(state
, filename
, NULL
, full_filename
,
3650 sizeof(filename
), nmsgs
, "EXPORT",
3651 export_opts
, &rflags
, qline
, flags
, &history
);
3656 cmd_cancelled("Export message");
3660 q_status_message1(SM_ORDER
, 0, 2,
3661 _("Can't export to file outside of %s"),
3668 #if !defined(DOS) && !defined(MAC) && !defined(OS2)
3669 else if(r
== 12){ /* Download */
3670 char cmd
[MAXPATH
], *tfp
= NULL
;
3676 if(ps_global
->restricted
){
3677 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
3678 "Download disallowed in restricted mode");
3683 tfp
= temp_nam(NULL
, "pd");
3684 build_updown_cmd(cmd
, sizeof(cmd
), ps_global
->VAR_DOWNLOAD_CMD_PREFIX
,
3685 ps_global
->VAR_DOWNLOAD_CMD
, tfp
);
3686 dprint((1, "Download cmd called: \"%s\"\n", cmd
));
3687 if((so
= so_get(FileStar
, tfp
, WRITE_ACCESS
|OWNER_ONLY
|WRITE_TO_LOCALE
)) != NULL
){
3688 gf_set_so_writec(&pc
, so
);
3690 for(i
= mn_first_cur(msgmap
); i
> 0L; i
= mn_next_cur(msgmap
)){
3691 if(!(state
->mail_stream
3692 && (rawno
= mn_m2raw(msgmap
, i
)) > 0L
3693 && rawno
<= state
->mail_stream
->nmsgs
3694 && (mc
= mail_elt(state
->mail_stream
, rawno
))
3698 if(!(env
= pine_mail_fetchstructure(state
->mail_stream
,
3699 mn_m2raw(msgmap
, i
), &b
))
3700 || !bezerk_delimiter(env
, mc
, pc
, next
++)
3701 || !format_message(mn_m2raw(msgmap
, mn_get_cur(msgmap
)),
3702 env
, b
, NULL
, FM_NEW_MESS
| FM_NOWRAP
, pc
)){
3703 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
3704 err
= "Error writing tempfile for download");
3709 gf_clear_so_writec(so
);
3710 if(so_give(&so
)){ /* close file */
3712 err
= "Error writing tempfile for download";
3716 if((syspipe
= open_system_pipe(cmd
, NULL
, NULL
,
3717 PIPE_USER
| PIPE_RESET
,
3718 0, pipe_callback
, pipe_report_error
)) != NULL
)
3719 (void) close_system_pipe(&syspipe
, NULL
, pipe_callback
);
3721 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
3722 err
= _("Error running download command"));
3726 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
3727 err
= "Error building temp file for download");
3731 fs_give((void **)&tfp
);
3735 q_status_message(SM_ORDER
, 0, 3, _("Download Command Completed"));
3739 #endif /* !(DOS || MAC) */
3742 if(rflags
& GER_APPEND
)
3747 dprint((5, "Opening file \"%s\" for export\n",
3748 full_filename
? full_filename
: "?"));
3750 if(!(store
= so_get(FileStar
, full_filename
, WRITE_ACCESS
|WRITE_TO_LOCALE
))){
3751 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
3752 /* TRANSLATORS: error opening file "<filename>" to export message: <error text> */
3753 _("Error opening file \"%s\" to export message: %s"),
3754 full_filename
, error_description(errno
));
3758 gf_set_so_writec(&pc
, store
);
3761 for(i
= mn_first_cur(msgmap
); i
> 0L; i
= mn_next_cur(msgmap
), count
++){
3762 env
= pine_mail_fetchstructure(state
->mail_stream
, mn_m2raw(msgmap
, i
),
3765 err
= _("Can't export message. Error accessing mail folder");
3770 if(!(state
->mail_stream
3771 && (rawno
= mn_m2raw(msgmap
, i
)) > 0L
3772 && rawno
<= state
->mail_stream
->nmsgs
3773 && (mc
= mail_elt(state
->mail_stream
, rawno
))
3777 start_of_append
= so_tell(store
);
3778 if(!bezerk_delimiter(env
, mc
, pc
, leading_nl
)
3779 || !format_message(mn_m2raw(msgmap
, i
), env
, b
, NULL
,
3780 FM_NEW_MESS
| FM_NOWRAP
, pc
)){
3781 orig_errno
= errno
; /* save incase things are really bad */
3782 failure
= 1; /* pop out of here */
3789 gf_clear_so_writec(store
);
3790 if(so_give(&store
)) /* release storage */
3794 our_truncate(full_filename
, (off_t
)start_of_append
);
3796 dprint((1, "FAILED Export: fetch(%ld): %s\n",
3797 i
, err
? err
: "?"));
3798 q_status_message(SM_ORDER
| SM_DING
, 3, 4, err
);
3801 dprint((1, "FAILED Export: file \"%s\" : %s\n",
3802 full_filename
? full_filename
: "?",
3803 error_description(orig_errno
)));
3804 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
3805 /* TRANSLATORS: Error exporting to <filename>: <error text> */
3806 _("Error exporting to \"%s\" : %s"),
3807 filename
, error_description(orig_errno
));
3811 if(rflags
& GER_ALLPARTS
&& full_filename
[0]){
3812 char dir
[MAXPATH
+1];
3813 char lfile
[MAXPATH
+1];
3814 int ok
= 0, tries
= 0, saved
= 0, errs
= 0, counter
= 2;
3818 * Now we want to save all of the attachments to a subdirectory.
3819 * To make it easier for us and probably easier for the user, and
3820 * to prevent the user from shooting himself in the foot, we
3821 * make a new subdirectory so that we can't possibly step on
3822 * any existing files, and we don't need any interaction with the
3823 * user while saving.
3825 * We'll just use the directory name full_filename.d or if that
3826 * already exists and isn't empty, we'll try adding a suffix to
3827 * that until we get something to use.
3830 if(strlen(full_filename
) + strlen(".d") + 1 > sizeof(dir
)){
3831 q_status_message1(SM_ORDER
| SM_DING
, 3, 4,
3832 _("Can't save attachments, filename too long: %s"),
3838 snprintf(dir
, sizeof(dir
), "%s.d", full_filename
);
3839 dir
[sizeof(dir
)-1] = '\0';
3843 switch(r
= is_writable_dir(dir
)){
3844 case 0: /* exists and is a writable dir */
3846 * We could figure out if it is empty and use it in
3847 * that case, but that sounds like a lot of work, so
3848 * just fall through to default.
3852 if(strlen(full_filename
) + strlen(".d") + 1 +
3853 1 + strlen(long2string((long) tries
)) > sizeof(dir
)){
3854 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
3855 "Problem saving attachments");
3859 snprintf(dir
, sizeof(dir
), "%s.d_%s", full_filename
,
3860 long2string((long) tries
));
3861 dir
[sizeof(dir
)-1] = '\0';
3864 case 3: /* doesn't exist, that's good! */
3865 /* make new directory */
3869 } while(!ok
&& tries
< 1000);
3872 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
3873 _("Problem saving attachments"));
3877 /* create the new directory */
3878 if(our_mkdir(dir
, 0700)){
3879 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
3880 _("Problem saving attachments: %s: %s"), dir
,
3881 error_description(errno
));
3885 if(!(state
->mail_stream
3886 && (rawno
= mn_m2raw(msgmap
, mn_get_cur(msgmap
))) > 0L
3887 && rawno
<= state
->mail_stream
->nmsgs
3888 && (env
=pine_mail_fetchstructure(state
->mail_stream
,rawno
,&b
))
3890 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
3891 _("Problem reading message"));
3895 zero_atmts(state
->atmts
);
3896 describe_mime(b
, "", 1, 1, 0, 0);
3899 if(a
&& a
->description
) /* skip main body part */
3902 for(; a
->description
!= NULL
; a
++){
3903 /* skip over these parts of the message */
3904 if(MIME_MSG_A(a
) || MIME_DGST_A(a
) || MIME_VCARD_A(a
))
3908 (void) get_filename_parameter(lfile
, sizeof(lfile
), a
->body
, NULL
);
3910 if(lfile
[0] == '\0'){ /* MAXPATH + 1 = sizeof(lfile) */
3911 snprintf(lfile
, sizeof(lfile
), "part_%.*s", MAXPATH
+1-6,
3912 a
->number
? a
->number
: "?");
3913 lfile
[sizeof(lfile
)-1] = '\0';
3916 if(strlen(dir
) + strlen(S_FILESEP
) + strlen(lfile
) + 1
3917 > sizeof(filename
)){
3919 "FAILED Att Export: name too long: %s\n",
3920 dir
, S_FILESEP
, lfile
));
3925 /* although files are being saved in a unique directory, there is
3926 * no guarantee that attachment names have unique names, so we have
3927 * to make sure that we are not constantly rewriting the same file name
3928 * over and over. In order to avoid this we test if the file already exists,
3929 * and if so, we write a counter name in the file name, just before the
3930 * extension of the file, and separate it with an underscore.
3932 snprintf(filename
, sizeof(filename
), "%s%s%s", dir
, S_FILESEP
, lfile
);
3933 filename
[sizeof(filename
)-1] = '\0';
3934 while((ok
= can_access(filename
, ACCESS_EXISTS
)) == 0 && errs
== 0){
3936 snprintf(filename
, sizeof(filename
), "%d", counter
);
3937 if(strlen(dir
) + strlen(S_FILESEP
) + strlen(lfile
) + strlen(filename
) + 2
3938 > sizeof(filename
)){
3940 "FAILED Att Export: name too long: %s\n",
3941 dir
, S_FILESEP
, lfile
));
3945 if((ext
= strrchr(lfile
, '.')) != NULL
)
3947 snprintf(filename
, sizeof(filename
), "%s%s%s%s%d%s%s",
3948 dir
, S_FILESEP
, lfile
,
3949 ext
? "_" : "", counter
++, ext
? "." : "", ext
? ext
+1 : "");
3950 filename
[sizeof(filename
)-1] = '\0';
3953 if(write_attachment_to_file(state
->mail_stream
, rawno
,
3954 a
, GER_NONE
, filename
) == 1)
3962 q_status_message1(SM_ORDER
, 3, 3,
3963 "Errors saving some attachments, %s attachments saved",
3964 long2string((long) saved
));
3966 q_status_message(SM_ORDER
, 3, 3,
3967 _("Problems saving attachments"));
3971 q_status_message2(SM_ORDER
, 0, 3,
3972 /* TRANSLATORS: Saved <how many> attachements to <directory name> */
3973 _("Saved %s attachments to %s"),
3974 long2string((long) saved
), dir
);
3976 q_status_message(SM_ORDER
, 3, 3, _("No attachments to save"));
3979 else if(mn_total_cur(msgmap
) > 1L)
3980 q_status_message4(SM_ORDER
,0,3,
3981 "%s message%s %s to file \"%s\"",
3982 long2string(count
), plural(count
),
3985 : rflags
& GER_APPEND
? "appended" : "exported",
3988 q_status_message3(SM_ORDER
,0,3,
3989 "Message %s %s to file \"%s\"",
3990 long2string(mn_get_cur(msgmap
)),
3993 : rflags
& GER_APPEND
? "appended" : "exported",
3999 if(MCMD_ISAGG(aopt
))
4000 restore_selected(msgmap
);
4007 * Ask user what file to export to. Export from srcstore to that file.
4009 * Args ps -- pine struct
4010 * srctext -- pointer to source text
4011 * srctype -- type of that source text
4012 * prompt_msg -- see get_export_filename
4015 * Returns: != 0 : error
4019 simple_export(struct pine
*ps
, void *srctext
, SourceType srctype
, char *prompt_msg
, char *lister_msg
)
4021 int r
= 1, rflags
= GER_NONE
;
4022 char filename
[MAXPATH
+1], full_filename
[MAXPATH
+1];
4023 STORE_S
*store
= NULL
;
4024 struct variable
*vars
= ps
? ps
->vars
: NULL
;
4025 static HISTORY_S
*history
= NULL
;
4026 static ESCKEY_S simple_export_opts
[] = {
4027 {ctrl('T'), 10, "^T", N_("To Files")},
4028 {-1, 0, NULL
, NULL
},
4029 {-1, 0, NULL
, NULL
}};
4031 if(ps
== NULL
&& srctext
== NULL
&& srctype
== CharStar
4032 && prompt_msg
== NULL
&& lister_msg
== NULL
){
4034 free_hist(&history
);
4038 if(F_ON(F_ENABLE_TAB_COMPLETE
,ps
)){
4039 simple_export_opts
[r
].ch
= ctrl('I');
4040 simple_export_opts
[r
].rval
= 11;
4041 simple_export_opts
[r
].name
= "TAB";
4042 simple_export_opts
[r
].label
= N_("Complete");
4046 q_status_message(SM_ORDER
, 0, 2, _("Error allocating space"));
4051 simple_export_opts
[++r
].ch
= -1;
4053 full_filename
[0] = '\0';
4055 r
= get_export_filename(ps
, filename
, NULL
, full_filename
, sizeof(filename
),
4056 prompt_msg
, lister_msg
, simple_export_opts
, &rflags
,
4057 -FOOTER_ROWS(ps
), GE_IS_EXPORT
, &history
);
4061 else if(!full_filename
[0]){
4066 dprint((5, "Opening file \"%s\" for export\n",
4067 full_filename
? full_filename
: "?"));
4069 if((store
= so_get(FileStar
, full_filename
, WRITE_ACCESS
|WRITE_TO_LOCALE
)) != NULL
){
4073 gf_set_so_writec(&pc
, store
);
4074 gf_set_readc(&gc
, srctext
, (srctype
== CharStar
)
4075 ? strlen((char *)srctext
)
4079 if((pipe_err
= gf_pipe(gc
, pc
)) != NULL
){
4080 q_status_message2(SM_ORDER
| SM_DING
, 3, 3,
4081 /* TRANSLATORS: Problem saving to <filename>: <error text> */
4082 _("Problem saving to \"%s\": %s"),
4083 filename
, pipe_err
);
4089 gf_clear_so_writec(store
);
4090 if(so_give(&store
)){
4091 q_status_message2(SM_ORDER
| SM_DING
, 3, 3,
4092 _("Problem saving to \"%s\": %s"),
4093 filename
, error_description(errno
));
4098 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
4099 _("Error opening file \"%s\" for export: %s"),
4100 full_filename
, error_description(errno
));
4107 /* overloading full_filename */
4108 snprintf(full_filename
, sizeof(full_filename
), "%c%s",
4109 (prompt_msg
&& prompt_msg
[0])
4110 ? (islower((unsigned char)prompt_msg
[0])
4111 ? toupper((unsigned char)prompt_msg
[0]) : prompt_msg
[0])
4113 (prompt_msg
&& prompt_msg
[0]) ? prompt_msg
+1 : "ext");
4114 full_filename
[sizeof(full_filename
)-1] = '\0';
4115 q_status_message3(SM_ORDER
,0,2,"%s %s to \"%s\"",
4119 : rflags
& GER_APPEND
? "appended" : "exported",
4124 cmd_cancelled("Export");
4128 q_status_message1(SM_ORDER
, 0, 2,
4129 _("Can't export to file outside of %s"), VAR_OPER_DIR
);
4133 ps
->mangled_footer
= 1;
4139 * Ask user what file to export to.
4141 * filename -- On input, this is the filename to start with. On exit,
4142 * this is the filename chosen. (but this isn't used)
4143 * deefault -- This is the default value if user hits return. The
4144 * prompt will have [deefault] added to it automatically.
4145 * full_filename -- This is the full filename on exit.
4146 * len -- Minimum length of _both_ filename and full_filename.
4147 * prompt_msg -- Message to insert in prompt.
4148 * lister_msg -- Message to insert in file_lister.
4149 * opts -- Key options.
4150 * There is a tangled relationship between the callers
4151 * and this routine as far as opts are concerned. Some
4152 * of the opts are handled here. In particular, r == 3,
4153 * r == 10, r == 11, and r == 13 are all handled here.
4154 * Don't use those values unless you want what happens
4155 * here. r == 12 and others are handled by the caller.
4156 * rflags -- Return flags
4157 * GER_OVER - overwrite of existing file
4158 * GER_APPEND - append of existing file
4159 * else file did not exist before
4161 * GER_ALLPARTS - AllParts toggle was turned on
4163 * qline -- Command line to prompt on.
4164 * flags -- Logically OR'd flags
4165 * GE_IS_EXPORT - The command was an Export command
4166 * so the prompt should include
4168 * GE_SEQ_SENSITIVE - The command that got us here is
4169 * sensitive to sequence number changes
4170 * caused by unsolicited expunges.
4171 * GE_NO_APPEND - We will not allow append to an
4172 * existing file, only removal of the
4173 * file if it exists.
4174 * GE_IS_IMPORT - We are selecting for reading.
4175 * No overwriting or checking for
4176 * existence at all. Don't use this
4177 * together with GE_NO_APPEND.
4178 * GE_ALLPARTS - Turn on AllParts toggle.
4179 * GE_BINARY - Turn on Binary toggle.
4181 * Returns: -1 cancelled
4182 * -2 prohibited by VAR_OPER_DIR
4183 * -3 other error, already reported here
4185 * 12 user chose 12 command from opts
4188 get_export_filename(struct pine
*ps
, char *filename
, char *deefault
,
4189 char *full_filename
, size_t len
, char *prompt_msg
,
4190 char *lister_msg
, ESCKEY_S
*optsarg
, int *rflags
,
4191 int qline
, int flags
, HISTORY_S
**history
)
4193 char dir
[MAXPATH
+1], dir2
[MAXPATH
+1], orig_dir
[MAXPATH
+1];
4194 char precolon
[MAXPATH
+1], postcolon
[MAXPATH
+1];
4195 char filename2
[MAXPATH
+1], tmp
[MAXPATH
+1], *fn
, *ill
;
4196 int l
, i
, ku
= -1, kp
= -1, r
, fatal
, homedir
= 0, was_abs_path
=0, avail
, ret
= 0;
4197 int allparts
= 0, binary
= 0;
4198 char prompt_buf
[400];
4200 ESCKEY_S
*opts
= NULL
;
4201 struct variable
*vars
= ps
->vars
;
4202 static HISTORY_S
*dir_hist
= NULL
;
4204 int pos
, hist_len
= 0;
4207 /* we will fake a history with the ps_global->VAR_HISTORY variable
4208 * We fake that we combine this variable into a history variable
4209 * by stacking VAR_HISTORY on top of dir_hist. We keep track of this
4210 * by looking at the variable pos.
4212 if(ps_global
->VAR_HISTORY
!= NULL
)
4213 for(hist_len
= 0; ps_global
->VAR_HISTORY
[hist_len
]
4214 && ps_global
->VAR_HISTORY
[hist_len
][0]; hist_len
++)
4217 pos
= hist_len
+ items_in_hist(dir_hist
);
4219 if(flags
& GE_ALLPARTS
|| history
|| dir_hist
){
4221 * Copy the opts and add one to the end of the list.
4223 for(i
= 0; optsarg
[i
].ch
!= -1; i
++)
4226 if(dir_hist
|| hist_len
> 0)
4230 i
+= dir_hist
|| hist_len
> 0 ? 2 : 4;
4232 if(flags
& GE_ALLPARTS
)
4235 if(flags
& GE_BINARY
)
4238 opts
= (ESCKEY_S
*) fs_get((i
+1) * sizeof(*opts
));
4239 memset(opts
, 0, (i
+1) * sizeof(*opts
));
4241 for(i
= 0; optsarg
[i
].ch
!= -1; i
++){
4242 opts
[i
].ch
= optsarg
[i
].ch
;
4243 opts
[i
].rval
= optsarg
[i
].rval
;
4244 opts
[i
].name
= optsarg
[i
].name
; /* no need to make a copy */
4245 opts
[i
].label
= optsarg
[i
].label
; /* " */
4248 if(flags
& GE_ALLPARTS
){
4250 opts
[i
].ch
= ctrl('P');
4252 opts
[i
].name
= "^P";
4253 /* TRANSLATORS: Export all attachment parts */
4254 opts
[i
++].label
= N_("AllParts");
4257 if(flags
& GE_BINARY
){
4259 opts
[i
].ch
= ctrl('R');
4261 opts
[i
].name
= "^R";
4262 opts
[i
++].label
= N_("Binary");
4265 rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
,
4266 SIZEOF_20KBUF
, filename
);
4267 if(strcmp(tmp_20k_buf
, filename
)){
4268 opts
[i
].ch
= ctrl('N');
4270 opts
[i
].name
= "^N";
4271 opts
[i
++].label
= "Name UTF8";
4274 if(dir_hist
|| hist_len
> 0){
4275 opts
[i
].ch
= ctrl('Y');
4279 opts
[i
++].label
= "";
4281 opts
[i
].ch
= ctrl('V');
4284 opts
[i
++].label
= "";
4288 opts
[i
].ch
= KEY_UP
;
4292 opts
[i
++].label
= "";
4294 opts
[i
].ch
= KEY_DOWN
;
4297 opts
[i
++].label
= "";
4303 init_hist(history
, HISTSIZE
);
4304 init_hist(&dir_hist
, HISTSIZE
); /* reset history to the end */
4312 if(F_ON(F_USE_CURRENT_DIR
, ps
))
4314 else if(VAR_OPER_DIR
){
4315 strncpy(dir
, VAR_OPER_DIR
, sizeof(dir
));
4316 dir
[sizeof(dir
)-1] = '\0';
4318 #if defined(DOS) || defined(OS2)
4319 else if(VAR_FILE_DIR
){
4320 strncpy(dir
, VAR_FILE_DIR
, sizeof(dir
));
4321 dir
[sizeof(dir
)-1] = '\0';
4329 strncpy(orig_dir
, dir
, sizeof(orig_dir
));
4330 orig_dir
[sizeof(orig_dir
)-1] = '\0';
4332 postcolon
[0] = '\0';
4333 strncpy(precolon
, dir
, sizeof(precolon
));
4334 precolon
[sizeof(precolon
)-1] = '\0';
4336 strncpy(def
, deefault
, sizeof(def
)-1);
4337 def
[sizeof(def
)-1] = '\0';
4338 removing_leading_and_trailing_white_space(def
);
4343 avail
= MAX(20, ps_global
->ttyo
? ps_global
->ttyo
->screen_cols
: 80) - MIN_OPT_ENT_WIDTH
;
4345 /*---------- Prompt the user for the file name -------------*/
4348 char dirb
[50], fileb
[50];
4349 int l1
, l2
, l3
, l4
, l5
, needed
;
4350 char *p
, p1
[100], p2
[100], *p3
, p4
[100], p5
[100];
4352 snprintf(p1
, sizeof(p1
), "%sCopy ",
4353 (flags
& GE_IS_EXPORT
) ? "EXPORT: " :
4354 (flags
& GE_IS_IMPORT
) ? "IMPORT: " : "SAVE: ");
4355 p1
[sizeof(p1
)-1] = '\0';
4358 strncpy(p2
, prompt_msg
? prompt_msg
: "", sizeof(p2
)-1);
4359 p2
[sizeof(p2
)-1] = '\0';
4362 if(rflags
&& *rflags
& GER_ALLPARTS
)
4369 snprintf(p4
, sizeof(p4
), " %s file%s%s",
4370 (flags
& GE_IS_IMPORT
) ? "from" : "to",
4371 is_absolute_path(filename
) ? "" : " in ",
4372 is_absolute_path(filename
) ? "" :
4373 (!dir
[0] ? "current directory"
4374 : (dir
[0] == '~' && !dir
[1]) ? "home directory"
4375 : short_str(dir
,dirb
,sizeof(dirb
),30,FrontDots
)));
4376 p4
[sizeof(p4
)-1] = '\0';
4379 snprintf(p5
, sizeof(p5
), "%s%s%s: ",
4381 *def
? short_str(def
,fileb
,sizeof(fileb
),40,EndDots
) : "",
4383 p5
[sizeof(p5
)-1] = '\0';
4386 if((needed
= l1
+l2
+l3
+l4
+l5
-avail
) > 0){
4387 snprintf(p4
, sizeof(p4
), " %s file%s%s",
4388 (flags
& GE_IS_IMPORT
) ? "from" : "to",
4389 is_absolute_path(filename
) ? "" : " in ",
4390 is_absolute_path(filename
) ? "" :
4391 (!dir
[0] ? "current dir"
4392 : (dir
[0] == '~' && !dir
[1]) ? "home dir"
4393 : short_str(dir
,dirb
,sizeof(dirb
),10,FrontDots
)));
4394 p4
[sizeof(p4
)-1] = '\0';
4398 if((needed
= l1
+l2
+l3
+l4
+l5
-avail
) > 0 && l5
> 0){
4399 snprintf(p5
, sizeof(p5
), "%s%s%s: ",
4401 *def
? short_str(def
,fileb
,sizeof(fileb
),
4402 MAX(15,l5
-5-needed
),EndDots
) : "",
4404 p5
[sizeof(p5
)-1] = '\0';
4408 if((needed
= l1
+l2
+l3
+l4
+l5
-avail
) > 0 && l2
> 0){
4411 * 14 is about the shortest we can make this, because there are
4412 * fixed length strings of length 14 coming in here.
4414 p
= short_str(prompt_msg
, p2
, sizeof(p2
), MAX(14,l2
-needed
), FrontDots
);
4416 strncpy(p2
, p
, sizeof(p2
)-1);
4417 p2
[sizeof(p2
)-1] = '\0';
4423 if((needed
= l1
+l2
+l3
+l4
+l5
-avail
) > 0){
4424 strncpy(p1
, "Copy ", sizeof(p1
)-1);
4425 p1
[sizeof(p1
)-1] = '\0';
4429 if((needed
= l1
+l2
+l3
+l4
+l5
-avail
) > 0 && l5
> 0){
4430 snprintf(p5
, sizeof(p5
), "%s%s%s: ",
4432 *def
? short_str(def
,fileb
, sizeof(fileb
),
4433 MAX(10,l5
-5-needed
),EndDots
) : "",
4435 p5
[sizeof(p5
)-1] = '\0';
4439 if((needed
= l1
+l2
+l3
+l4
+l5
-avail
) > 0 && l3
> 0){
4440 if(needed
<= l3
- strlen(" (+ atts)"))
4442 else if(needed
<= l3
- strlen(" (atts)"))
4444 else if(needed
<= l3
- strlen(" (+)"))
4446 else if(needed
<= l3
- strlen("+"))
4454 snprintf(prompt_buf
, sizeof(prompt_buf
), "%s%s%s%s%s", p1
, p2
, p3
, p4
, p5
);
4455 prompt_buf
[sizeof(prompt_buf
)-1] = '\0';
4458 if(items_in_hist(dir_hist
) > 0 || hist_len
> 0){ /* any directories */
4459 opts
[kp
].name
= "^Y";
4460 opts
[kp
].label
= "Prev Dir";
4461 opts
[kp
+1].name
= "^V";
4462 opts
[kp
+1].label
= "Next Dir";
4466 opts
[kp
].label
= "";
4467 opts
[kp
+1].name
= "";
4468 opts
[kp
+1].label
= "";
4473 if(items_in_hist(*history
) > 0){
4474 opts
[ku
].name
= HISTORY_UP_KEYNAME
;
4475 opts
[ku
].label
= HISTORY_KEYLABEL
;
4476 opts
[ku
+1].name
= HISTORY_DOWN_KEYNAME
;
4477 opts
[ku
+1].label
= HISTORY_KEYLABEL
;
4481 opts
[ku
].label
= "";
4482 opts
[ku
+1].name
= "";
4483 opts
[ku
+1].label
= "";
4487 oeflags
= OE_APPEND_CURRENT
|
4488 ((flags
& GE_SEQ_SENSITIVE
) ? OE_SEQ_SENSITIVE
: 0);
4489 r
= optionally_enter(filename
, qline
, 0, len
, prompt_buf
,
4490 opts
, NO_HELP
, &oeflags
);
4492 dprint((2, "\n - export_filename = \"%s\", r = %d -\n", filename
, r
));
4496 * Helps may not be right if you add another caller or change
4497 * things. Check it out.
4499 if(flags
& GE_IS_IMPORT
)
4500 helper(h_ge_import
, _("HELP FOR IMPORT FILE SELECT"), HLPD_SIMPLE
);
4501 else if(flags
& GE_ALLPARTS
)
4502 helper(h_ge_allparts
, _("HELP FOR EXPORT FILE SELECT"), HLPD_SIMPLE
);
4504 helper(h_ge_export
, _("HELP FOR EXPORT FILE SELECT"), HLPD_SIMPLE
);
4506 ps
->mangled_screen
= 1;
4510 else if(r
== 10 || r
== 11){ /* Browser or File Completion */
4511 if(filename
[0]=='~'){
4512 if(filename
[1] == C_FILESEP
&& filename
[2]!='\0'){
4515 for(i
=0; filename
[i
+2] != '\0' && i
+2 < len
-1; i
++)
4516 filename
[i
] = filename
[i
+2];
4518 strncpy(dir
, precolon
, sizeof(dir
)-1);
4519 dir
[sizeof(dir
)-1] = '\0';
4521 else if(filename
[1]=='\0' ||
4522 (filename
[1] == C_FILESEP
&& filename
[2] == '\0')){
4526 strncpy(dir
, precolon
, sizeof(dir
)-1);
4527 dir
[sizeof(dir
)-1] = '\0';
4530 else if(!dir
[0] && !is_absolute_path(filename
) && was_abs_path
){
4534 strncpy(dir
, precolon
, sizeof(dir
)-1);
4535 dir
[sizeof(dir
)-1] = '\0';
4544 strncpy(tmp
, filename
, sizeof(tmp
)-1);
4545 tmp
[sizeof(tmp
)-1] = '\0';
4546 if(*tmp
&& is_absolute_path(tmp
))
4547 fnexpand(tmp
, sizeof(tmp
));
4548 if(strncmp(tmp
,postcolon
, strlen(postcolon
)))
4549 postcolon
[0] = '\0';
4551 if(*tmp
&& (fn
= last_cmpnt(tmp
))){
4553 strncpy(filename2
, fn
, sizeof(filename2
)-1);
4554 filename2
[sizeof(filename2
)-1] = '\0';
4555 if(is_absolute_path(tmp
)){
4556 strncpy(dir2
, tmp
, MIN(fn
- tmp
, sizeof(dir2
)-1));
4557 dir2
[MIN(fn
- tmp
, sizeof(dir2
)-1)] = '\0';
4559 if(tmp
[1]==':' && tmp
[2]=='\\' && dir2
[2]=='\0'){
4564 strncpy(postcolon
, dir2
, sizeof(postcolon
)-1);
4565 postcolon
[sizeof(postcolon
)-1] = '\0';
4571 * Just building the directory name in dir2,
4572 * full_filename is overloaded.
4574 snprintf(full_filename
, len
, "%.*s", (int) MIN(fn
-tmp
,len
-1), tmp
);
4575 full_filename
[len
-1] = '\0';
4576 strncpy(postcolon
, full_filename
, sizeof(postcolon
)-1);
4577 postcolon
[sizeof(postcolon
)-1] = '\0';
4578 build_path(dir2
, !dir
[0] ? p
= (char *)getcwd(NULL
,MAXPATH
)
4579 : (dir
[0] == '~' && !dir
[1])
4582 full_filename
, sizeof(dir2
));
4588 if(is_absolute_path(tmp
)){
4589 strncpy(dir2
, tmp
, sizeof(dir2
)-1);
4590 dir2
[sizeof(dir2
)-1] = '\0';
4592 if(dir2
[2]=='\0' && dir2
[1]==':'){
4595 strncpy(postcolon
,dir2
,sizeof(postcolon
)-1);
4596 postcolon
[sizeof(postcolon
)-1] = '\0';
4599 filename2
[0] = '\0';
4603 strncpy(filename2
, tmp
, sizeof(filename2
)-1);
4604 filename2
[sizeof(filename2
)-1] = '\0';
4606 if(getcwd(dir2
, sizeof(dir2
)) == NULL
)
4607 alpine_panic(_("getcwd() call failed at get_export_filename"));
4609 else if(dir
[0] == '~' && !dir
[1]){
4610 strncpy(dir2
, ps
->home_dir
, sizeof(dir2
)-1);
4611 dir2
[sizeof(dir2
)-1] = '\0';
4614 strncpy(dir2
, dir
, sizeof(dir2
)-1);
4615 dir2
[sizeof(dir2
)-1] = '\0';
4618 postcolon
[0] = '\0';
4622 build_path(full_filename
, dir2
, filename2
, len
);
4623 if(!strcmp(full_filename
, dir2
))
4624 filename2
[0] = '\0';
4625 if(full_filename
[strlen(full_filename
)-1] == C_FILESEP
4626 && isdir(full_filename
,NULL
,NULL
)){
4627 if(strlen(full_filename
) == 1)
4628 strncpy(postcolon
, full_filename
, sizeof(postcolon
)-1);
4629 else if(filename2
[0])
4630 strncpy(postcolon
, filename2
, sizeof(postcolon
)-1);
4631 postcolon
[sizeof(postcolon
)-1] = '\0';
4632 strncpy(dir2
, full_filename
, sizeof(dir2
)-1);
4633 dir2
[sizeof(dir2
)-1] = '\0';
4634 filename2
[0] = '\0';
4636 #ifdef _WINDOWS /* use full_filename even if not a valid directory */
4637 else if(full_filename
[strlen(full_filename
)-1] == C_FILESEP
){
4638 strncpy(postcolon
, filename2
, sizeof(postcolon
)-1);
4639 postcolon
[sizeof(postcolon
)-1] = '\0';
4640 strncpy(dir2
, full_filename
, sizeof(dir2
)-1);
4641 dir2
[sizeof(dir2
)-1] = '\0';
4642 filename2
[0] = '\0';
4645 if(dir2
[strlen(dir2
)-1] == C_FILESEP
&& strlen(dir2
)!=1
4646 && strcmp(dir2
+1, ":\\"))
4647 /* last condition to prevent stripping of '\\'
4648 in windows partition */
4649 dir2
[strlen(dir2
)-1] = '\0';
4651 if(r
== 10){ /* File Browser */
4652 r
= file_lister(lister_msg
? lister_msg
: "EXPORT",
4653 dir2
, sizeof(dir2
), filename2
, sizeof(filename2
),
4655 (flags
& GE_IS_IMPORT
) ? FB_READ
: FB_SAVE
);
4657 /* Windows has a special "feature" in which entering the file browser will
4658 change the working directory if the directory is changed at all (even
4659 clicking "Cancel" will change the working directory).
4661 if(F_ON(F_USE_CURRENT_DIR
, ps
))
4662 (void)getcwd(dir2
,sizeof(dir2
));
4664 if(isdir(dir2
,NULL
,NULL
)){
4665 strncpy(precolon
, dir2
, sizeof(precolon
)-1);
4666 precolon
[sizeof(precolon
)-1] = '\0';
4668 strncpy(postcolon
, filename2
, sizeof(postcolon
)-1);
4669 postcolon
[sizeof(postcolon
)-1] = '\0';
4671 build_path(full_filename
, dir2
, filename2
, len
);
4672 if(isdir(full_filename
, NULL
, NULL
)){
4673 strncpy(dir
, full_filename
, sizeof(dir
)-1);
4674 dir
[sizeof(dir
)-1] = '\0';
4678 fn
= last_cmpnt(full_filename
);
4679 strncpy(dir
, full_filename
,
4680 MIN(fn
- full_filename
, sizeof(dir
)-1));
4681 dir
[MIN(fn
- full_filename
, sizeof(dir
)-1)] = '\0';
4682 if(fn
- full_filename
> 1)
4683 dir
[fn
- full_filename
- 1] = '\0';
4686 if(!strcmp(dir
, ps
->home_dir
)){
4691 strncpy(filename
, fn
, len
-1);
4692 filename
[len
-1] = '\0';
4695 else{ /* File Completion */
4696 if(!pico_fncomplete(dir2
, filename2
, sizeof(filename2
)))
4698 strncat(postcolon
, filename2
,
4699 sizeof(postcolon
)-1-strlen(postcolon
));
4700 postcolon
[sizeof(postcolon
)-1] = '\0';
4702 was_abs_path
= is_absolute_path(filename
);
4704 if(!strcmp(dir
, ps
->home_dir
)){
4709 strncpy(filename
, postcolon
, len
-1);
4710 filename
[len
-1] = '\0';
4711 strncpy(dir
, precolon
, sizeof(dir
)-1);
4712 dir
[sizeof(dir
)-1] = '\0';
4714 if(filename
[0] == '~' && !filename
[1]){
4722 else if(r
== 12){ /* Download, caller handles it */
4726 else if(r
== 13){ /* toggle AllParts bit */
4728 if(*rflags
& GER_ALLPARTS
){
4729 *rflags
&= ~GER_ALLPARTS
;
4730 opts
[allparts
].label
= N_("AllParts");
4733 *rflags
|= GER_ALLPARTS
;
4734 /* opposite of All Parts, No All Parts */
4735 opts
[allparts
].label
= N_("NoAllParts");
4742 else if(r
== 14){ /* List file names matching partial? */
4746 else if(r
== 15){ /* toggle Binary bit */
4748 if(*rflags
& GER_BINARY
){
4749 *rflags
&= ~GER_BINARY
;
4750 opts
[binary
].label
= N_("Binary");
4753 *rflags
|= GER_BINARY
;
4754 opts
[binary
].label
= N_("No Binary");
4760 else if(r
== 1){ /* Cancel */
4767 else if(r
>= 30 && r
<= 33){
4770 if(r
== 30 || r
== 31){
4773 p
= get_prev_hist(*history
, filename
, 0, NULL
);
4775 p
= get_next_hist(*history
, filename
, 0, NULL
);
4779 if(r
== 32 || r
== 33){
4780 int nitems
= items_in_hist(dir_hist
);
4781 if(dir_hist
|| hist_len
> 0){
4784 p
= hist_in_pos(--pos
, ps_global
->VAR_HISTORY
, hist_len
, dir_hist
, nitems
);
4788 if(pos
< hist_len
+ nitems
)
4789 p
= hist_in_pos(++pos
, ps_global
->VAR_HISTORY
, hist_len
, dir_hist
, nitems
);
4791 if(p
== NULL
|| *p
== '\0')
4795 last
= p
; /* save it! */
4797 if(p
!= NULL
&& *p
!= '\0'){
4798 if(r
== 30 || r
== 31){
4799 if((fn
= last_cmpnt(p
)) != NULL
){
4800 strncpy(dir
, p
, MIN(fn
- p
, sizeof(dir
)-1));
4801 dir
[MIN(fn
- p
, sizeof(dir
)-1)] = '\0';
4803 dir
[fn
- p
- 1] = '\0';
4804 strncpy(filename
, fn
, len
-1);
4805 filename
[len
-1] = '\0';
4807 } else { /* r == 32 || r == 33 */
4808 strncpy(dir
, p
, sizeof(dir
)-1);
4809 dir
[sizeof(dir
)-1] = '\0';
4812 if(!strcmp(dir
, ps
->home_dir
)){
4822 rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
,
4823 SIZEOF_20KBUF
, filename
);
4824 strncpy(filename
, tmp_20k_buf
, len
);
4825 filename
[len
-1] = '\0';
4833 removing_leading_and_trailing_white_space(filename
);
4836 if(!*def
){ /* Cancel */
4841 strncpy(filename
, def
, len
-1);
4842 filename
[len
-1] = '\0';
4845 #if defined(DOS) || defined(OS2)
4846 if(is_absolute_path(filename
)){
4847 fixpath(filename
, len
);
4850 if(filename
[0] == '~'){
4851 if(fnexpand(filename
, len
) == NULL
){
4852 char *p
= strindex(filename
, '/');
4855 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
4856 _("Error expanding file name: \"%s\" unknown user"),
4863 if(is_absolute_path(filename
)){
4864 strncpy(full_filename
, filename
, len
-1);
4865 full_filename
[len
-1] = '\0';
4869 build_path(full_filename
, (char *)getcwd(dir
,sizeof(dir
)),
4871 else if(dir
[0] == '~' && !dir
[1])
4872 build_path(full_filename
, ps
->home_dir
, filename
, len
);
4874 build_path(full_filename
, dir
, filename
, len
);
4877 if((ill
= filter_filename(full_filename
, &fatal
,
4878 ps_global
->restricted
|| ps_global
->VAR_OPER_DIR
)) != NULL
){
4880 q_status_message1(SM_ORDER
| SM_DING
, 3, 3, "%s", ill
);
4884 /* BUG: we should beep when the key's pressed rather than bitch later */
4885 /* Warn and ask for confirmation. */
4886 snprintf(prompt_buf
, sizeof(prompt_buf
), "File name contains a '%s'. %s anyway",
4887 ill
, (flags
& GE_IS_EXPORT
) ? "Export" : "Save");
4888 prompt_buf
[sizeof(prompt_buf
)-1] = '\0';
4889 if(want_to(prompt_buf
, 'n', 0, NO_HELP
,
4890 ((flags
& GE_SEQ_SENSITIVE
) ? RB_SEQ_SENSITIVE
: 0)) != 'y')
4895 break; /* Must have got an OK file name */
4898 if(VAR_OPER_DIR
&& !in_dir(VAR_OPER_DIR
, full_filename
)){
4903 if(!can_access(full_filename
, ACCESS_EXISTS
)){
4905 static ESCKEY_S access_opts
[] = {
4906 /* TRANSLATORS: asking user if they want to overwrite (replace contents of)
4907 a file or append to the end of the file */
4908 {'o', 'o', "O", N_("Overwrite")},
4909 {'a', 'a', "A", N_("Append")},
4910 {-1, 0, NULL
, NULL
}};
4912 rbflags
= RB_NORM
| ((flags
& GE_SEQ_SENSITIVE
) ? RB_SEQ_SENSITIVE
: 0);
4914 if(flags
& GE_NO_APPEND
){
4915 r
= strlen(filename
);
4916 snprintf(prompt_buf
, sizeof(prompt_buf
),
4917 /* TRANSLATORS: asking user whether to overwrite a file or not,
4918 File <filename> already exists. Overwrite it ? */
4919 _("File \"%s%s\" already exists. Overwrite it "),
4920 (r
> 20) ? "..." : "",
4921 filename
+ ((r
> 20) ? r
- 20 : 0));
4922 prompt_buf
[sizeof(prompt_buf
)-1] = '\0';
4923 if(want_to(prompt_buf
, 'n', 'x', NO_HELP
, rbflags
) == 'y'){
4925 *rflags
|= GER_OVER
;
4927 if(our_unlink(full_filename
) < 0){
4928 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
4929 /* TRANSLATORS: Cannot remove old <filename>: <error text> */
4930 _("Cannot remove old %s: %s"),
4931 full_filename
, error_description(errno
));
4939 else if(!(flags
& GE_IS_IMPORT
)){
4940 r
= strlen(filename
);
4941 snprintf(prompt_buf
, sizeof(prompt_buf
),
4942 /* TRANSLATORS: File <filename> already exists. Overwrite or append to it ? */
4943 _("File \"%s%s\" already exists. Overwrite or append to it ? "),
4944 (r
> 20) ? "..." : "",
4945 filename
+ ((r
> 20) ? r
- 20 : 0));
4946 prompt_buf
[sizeof(prompt_buf
)-1] = '\0';
4947 switch(radio_buttons(prompt_buf
, -FOOTER_ROWS(ps_global
),
4948 access_opts
, 'a', 'x', NO_HELP
, rbflags
)){
4951 *rflags
|= GER_OVER
;
4953 if(our_truncate(full_filename
, (off_t
)0) < 0)
4954 /* trouble truncating, but we'll give it a try anyway */
4955 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
4956 /* TRANSLATORS: Warning: Cannot truncate old <filename>: <error text> */
4957 _("Warning: Cannot truncate old %s: %s"),
4958 full_filename
, error_description(errno
));
4963 *rflags
|= GER_APPEND
;
4976 if(history
&& ret
== 0){
4977 save_hist(*history
, full_filename
, 0, NULL
);
4978 strncpy(tmp
, full_filename
, MAXPATH
);
4979 tmp
[MAXPATH
] = '\0';
4980 if((fn
= strrchr(tmp
, C_FILESEP
)) != NULL
)
4985 save_hist(dir_hist
, tmp
, 0, NULL
);
4988 if(opts
&& opts
!= optsarg
)
4989 fs_give((void **) &opts
);
4995 /*----------------------------------------------------------------------
4996 parse the config'd upload/download command
4998 Args: cmd -- buffer to return command fit for shellin'
5001 fname -- file name to build into the command
5003 Returns: pointer to cmd_str buffer or NULL on real bad error
5005 NOTE: One SIDE EFFECT is that any defined "prefix" string in the
5006 cfg_str is written to standard out right before a successful
5007 return of this function. The call immediately following this
5008 function darn well better be the shell exec...
5011 build_updown_cmd(char *cmd
, size_t cmdlen
, char *prefix
, char *cfg_str
, char *fname
)
5014 int fname_found
= 0;
5016 if(prefix
&& *prefix
){
5017 /* loop thru replacing all occurances of _FILE_ */
5018 p
= strncpy(cmd
, prefix
, cmdlen
);
5019 cmd
[cmdlen
-1] = '\0';
5020 while((p
= strstr(p
, "_FILE_")))
5021 rplstr(p
, cmdlen
-(p
-cmd
), 6, fname
);
5026 /* loop thru replacing all occurances of _FILE_ */
5027 p
= strncpy(cmd
, cfg_str
, cmdlen
);
5028 cmd
[cmdlen
-1] = '\0';
5029 while((p
= strstr(p
, "_FILE_"))){
5030 rplstr(p
, cmdlen
-(p
-cmd
), 6, fname
);
5035 snprintf(cmd
+strlen(cmd
), cmdlen
-strlen(cmd
), " %s", fname
);
5037 cmd
[cmdlen
-1] = '\0';
5039 dprint((4, "\n - build_updown_cmd = \"%s\" -\n",
5045 /*----------------------------------------------------------------------
5046 Write a berzerk format message delimiter using the given putc function
5048 Args: e -- envelope of message to write
5049 pc -- function to use
5051 Returns: TRUE if we could write it, FALSE if there was a problem
5053 NOTE: follows delimiter with OS-dependent newline
5056 bezerk_delimiter(ENVELOPE
*env
, MESSAGECACHE
*mc
, gf_io_t pc
, int leading_newline
)
5062 /* write "[\n]From mailbox[@host] " */
5063 if(!((leading_newline
? gf_puts(NEWLINE
, pc
) : 1)
5064 && gf_puts("From ", pc
)
5065 && gf_puts((env
&& env
->from
) ? env
->from
->mailbox
5066 : "the-concourse-on-high", pc
)
5067 && gf_puts((env
&& env
->from
&& env
->from
->host
) ? "@" : "", pc
)
5068 && gf_puts((env
&& env
->from
&& env
->from
->host
) ? env
->from
->host
5074 when
= mail_longdate(mc
);
5075 else if(env
&& env
->date
&& env
->date
[0]
5076 && mail_parse_date(&telt
,env
->date
))
5077 when
= mail_longdate(&telt
);
5083 while(p
&& *p
&& *p
!= '\n') /* write date */
5087 if(!gf_puts(NEWLINE
, pc
)) /* write terminating newline */
5094 /*----------------------------------------------------------------------
5095 Execute command to jump to a given message number
5097 Args: qline -- Line to ask question on
5099 Result: returns true if the use selected a new message, false otherwise
5103 jump_to(MSGNO_S
*msgmap
, int qline
, UCS first_num
, SCROLL_S
*sparms
, CmdWhere in_index
)
5105 char jump_num_string
[80], *j
, prompt
[70];
5108 static ESCKEY_S jump_to_key
[] = { {0, 0, NULL
, NULL
},
5109 /* TRANSLATORS: go to First Message */
5110 {ctrl('Y'), 10, "^Y", N_("First Msg")},
5111 {ctrl('V'), 11, "^V", N_("Last Msg")},
5112 {-1, 0, NULL
, NULL
} };
5114 dprint((4, "\n - jump_to -\n"));
5117 if(sparms
&& sparms
->jump_is_debug
)
5118 return(get_level(qline
, first_num
, sparms
));
5121 if(!any_messages(msgmap
, NULL
, "to Jump to"))
5124 if(first_num
&& first_num
< 0x80 && isdigit((unsigned char) first_num
)){
5125 jump_num_string
[0] = first_num
;
5126 jump_num_string
[1] = '\0';
5129 jump_num_string
[0] = '\0';
5131 if(mn_total_cur(msgmap
) > 1L){
5132 snprintf(prompt
, sizeof(prompt
), "Unselect %s msgs in favor of number to be entered",
5133 comatose(mn_total_cur(msgmap
)));
5134 prompt
[sizeof(prompt
)-1] = '\0';
5135 if((rc
= want_to(prompt
, 'n', 0, NO_HELP
, WT_NORM
)) == 'n')
5139 snprintf(prompt
, sizeof(prompt
), "%s number to jump to : ", in_index
== ThrdIndx
5142 prompt
[sizeof(prompt
)-1] = '\0';
5146 int flags
= OE_APPEND_CURRENT
;
5148 rc
= optionally_enter(jump_num_string
, qline
, 0,
5149 sizeof(jump_num_string
), prompt
,
5150 jump_to_key
, help
, &flags
);
5152 help
= help
== NO_HELP
5153 ? (in_index
== ThrdIndx
? h_oe_jump_thd
: h_oe_jump
)
5157 else if(rc
== 10 || rc
== 11){
5161 closest
= closest_jump_target(rc
== 10 ? 1L
5162 : ((in_index
== ThrdIndx
)
5163 ? msgmap
->max_thrdno
5164 : mn_get_total(msgmap
)),
5165 ps_global
->mail_stream
,
5167 in_index
, warning
, sizeof(warning
));
5168 /* ignore warning */
5173 * If we take out the *jump_num_string nonempty test in this if
5174 * then the closest_jump_target routine will offer a jump to the
5175 * last message. However, it is slow because you have to wait for
5176 * the status message and it is annoying for people who hit J command
5177 * by mistake and just want to hit return to do nothing, like has
5178 * always worked. So the test is there for now. Hubert 2002-08-19
5180 * Jumping to first/last message is now possible through ^Y/^V
5181 * commands above. jpf 2002-08-21
5182 * (and through "end" hubert 2006-07-07)
5184 if(rc
== 0 && *jump_num_string
!= '\0'){
5185 removing_leading_and_trailing_white_space(jump_num_string
);
5186 for(j
=jump_num_string
; isdigit((unsigned char)*j
) || *j
=='-'; j
++)
5190 if(!strucmp("end", j
))
5191 return((in_index
== ThrdIndx
) ? msgmap
->max_thrdno
: mn_get_total(msgmap
));
5193 q_status_message(SM_ORDER
| SM_DING
, 2, 2,
5194 _("Invalid number entered. Use only digits 0-9"));
5195 jump_num_string
[0] = '\0';
5199 long closest
, jump_num
;
5201 if(*jump_num_string
)
5202 jump_num
= atol(jump_num_string
);
5207 closest
= closest_jump_target(jump_num
, ps_global
->mail_stream
,
5209 *jump_num_string
? 0 : 1,
5210 in_index
, warning
, sizeof(warning
));
5212 q_status_message(SM_ORDER
| SM_DING
, 2, 2, warning
);
5214 if(closest
== jump_num
)
5218 jump_num_string
[0] = '\0';
5220 strncpy(jump_num_string
, long2string(closest
),
5221 sizeof(jump_num_string
));
5236 * cmd_delete_action - handle msgno advance and such after single message deletion
5239 cmd_delete_action(struct pine
*state
, MSGNO_S
*msgmap
, CmdWhere in_index
)
5245 msgno
= mn_get_cur(msgmap
);
5246 advance_cur_after_delete(state
, state
->mail_stream
, msgmap
, in_index
);
5248 if(IS_NEWS(state
->mail_stream
)
5249 || ((state
->context_current
->use
& CNTXT_INCMNG
)
5250 && context_isambig(state
->cur_folder
))){
5252 opts
= (NSF_TRUST_FLAGS
| NSF_SKIP_CHID
);
5253 if(in_index
== View
)
5254 opts
&= ~NSF_SKIP_CHID
;
5256 (void)next_sorted_flagged(F_UNDEL
|F_UNSEEN
, state
->mail_stream
, msgno
, &opts
);
5257 if(!(opts
& NSF_FLAG_MATCH
)){
5258 char nextfolder
[MAXPATH
];
5260 strncpy(nextfolder
, state
->cur_folder
, sizeof(nextfolder
));
5261 nextfolder
[sizeof(nextfolder
)-1] = '\0';
5262 rv
= next_folder(NULL
, nextfolder
, sizeof(nextfolder
), nextfolder
,
5263 state
->context_current
, NULL
, NULL
)
5264 ? ". Press TAB for next folder."
5265 : ". No more folders to TAB to.";
5274 * cmd_delete_index - fixup msgmap or whatever after cmd_delete has done it's thing
5277 cmd_delete_index(struct pine
*state
, MSGNO_S
*msgmap
)
5279 return(cmd_delete_action(state
, msgmap
,MsgIndx
));
5283 * cmd_delete_view - fixup msgmap or whatever after cmd_delete has done it's thing
5286 cmd_delete_view(struct pine
*state
, MSGNO_S
*msgmap
)
5288 return(cmd_delete_action(state
, msgmap
, View
));
5293 advance_cur_after_delete(struct pine
*state
, MAILSTREAM
*stream
, MSGNO_S
*msgmap
, CmdWhere in_index
)
5295 long new_msgno
, msgno
;
5298 new_msgno
= msgno
= mn_get_cur(msgmap
);
5299 opts
= NSF_TRUST_FLAGS
;
5301 if(F_ON(F_DEL_SKIPS_DEL
, state
)){
5303 if(THREADING() && sp_viewing_a_thread(stream
))
5304 opts
|= NSF_SKIP_CHID
;
5306 new_msgno
= next_sorted_flagged(F_UNDEL
, stream
, msgno
, &opts
);
5309 mn_inc_cur(stream
, msgmap
,
5310 (in_index
== View
&& THREADING()
5311 && sp_viewing_a_thread(stream
))
5313 : (in_index
== View
)
5314 ? MH_ANYTHD
: MH_NONE
);
5315 new_msgno
= mn_get_cur(msgmap
);
5316 if(new_msgno
!= msgno
)
5317 opts
|= NSF_FLAG_MATCH
;
5321 * Viewing_a_thread is the complicated case because we want to ignore
5322 * other threads at first and then look in other threads if we have to.
5323 * By ignoring other threads we also ignore collapsed partial threads
5324 * in our own thread.
5326 if(THREADING() && sp_viewing_a_thread(stream
) && !(opts
& NSF_FLAG_MATCH
)){
5327 long rawno
, orig_thrdno
;
5328 PINETHRD_S
*thrd
, *topthrd
= NULL
;
5330 rawno
= mn_m2raw(msgmap
, msgno
);
5331 thrd
= fetch_thread(stream
, rawno
);
5332 if(thrd
&& thrd
->top
)
5333 topthrd
= fetch_thread(stream
, thrd
->top
);
5335 orig_thrdno
= topthrd
? topthrd
->thrdno
: -1L;
5337 opts
= NSF_TRUST_FLAGS
;
5338 new_msgno
= next_sorted_flagged(F_UNDEL
, stream
, msgno
, &opts
);
5341 * If we got a match, new_msgno may be a message in
5342 * a different thread from the one we are viewing, or it could be
5343 * in a collapsed part of this thread.
5345 if(opts
& NSF_FLAG_MATCH
){
5350 thrd
= fetch_thread(stream
, mn_m2raw(msgmap
,new_msgno
));
5351 if(thrd
&& thrd
->top
)
5352 topthrd
= fetch_thread(stream
, thrd
->top
);
5355 * If this match is in the same thread we're already in
5356 * then we're done, else we have to ask the user and maybe
5359 if(!(orig_thrdno
> 0L && topthrd
5360 && topthrd
->thrdno
== orig_thrdno
)){
5362 if(F_OFF(F_AUTO_OPEN_NEXT_UNREAD
, state
)){
5363 if(in_index
== View
)
5364 snprintf(pmt
, sizeof(pmt
),
5365 "View message in thread number %.10s",
5366 topthrd
? comatose(topthrd
->thrdno
) : "?");
5368 snprintf(pmt
, sizeof(pmt
), "View thread number %.10s",
5369 topthrd
? comatose(topthrd
->thrdno
) : "?");
5371 ret
= want_to(pmt
, 'y', 'x', NO_HELP
, WT_NORM
);
5377 unview_thread(state
, stream
, msgmap
);
5378 mn_set_cur(msgmap
, new_msgno
);
5380 && (count_lflags_in_thread(stream
, topthrd
, msgmap
,
5382 && view_thread(state
, stream
, msgmap
, 1)){
5383 if(current_index_state
)
5384 msgmap
->top_after_thrd
= current_index_state
->msg_at_top
;
5386 state
->view_skipped_index
= 1;
5387 state
->next_screen
= mail_view_screen
;
5390 view_thread(state
, stream
, msgmap
, 1);
5391 if(current_index_state
)
5392 msgmap
->top_after_thrd
= current_index_state
->msg_at_top
;
5394 state
->next_screen
= SCREEN_FUN_NULL
;
5398 new_msgno
= msgno
; /* stick with original */
5403 mn_set_cur(msgmap
, new_msgno
);
5404 if(in_index
!= View
)
5405 adjust_cur_to_visible(stream
, msgmap
);
5411 get_level(int qline
, UCS first_num
, SCROLL_S
*sparms
)
5413 char debug_num_string
[80], *j
, prompt
[70];
5418 if(first_num
&& first_num
< 0x80 && isdigit((unsigned char)first_num
)){
5419 debug_num_string
[0] = first_num
;
5420 debug_num_string
[1] = '\0';
5421 debug_num
= atol(debug_num_string
);
5422 *(int *)(sparms
->proc
.data
.p
) = debug_num
;
5423 q_status_message1(SM_ORDER
, 0, 3, "Show debug <= level %s",
5424 comatose(debug_num
));
5428 debug_num_string
[0] = '\0';
5430 snprintf(prompt
, sizeof(prompt
), "Show debug <= this level (0-%d) : ", MAX(debug
, 9));
5431 prompt
[sizeof(prompt
)-1] = '\0';
5435 int flags
= OE_APPEND_CURRENT
;
5437 rc
= optionally_enter(debug_num_string
, qline
, 0,
5438 sizeof(debug_num_string
), prompt
,
5439 NULL
, help
, &flags
);
5441 help
= help
== NO_HELP
? h_oe_debuglevel
: NO_HELP
;
5446 removing_leading_and_trailing_white_space(debug_num_string
);
5447 for(j
=debug_num_string
; isdigit((unsigned char)*j
); j
++)
5451 q_status_message(SM_ORDER
| SM_DING
, 2, 2,
5452 _("Invalid number entered. Use only digits 0-9"));
5453 debug_num_string
[0] = '\0';
5456 debug_num
= atol(debug_num_string
);
5458 q_status_message(SM_ORDER
| SM_DING
, 2, 2,
5459 _("Number should be >= 0"));
5460 else if(debug_num
> MAX(debug
,9))
5461 q_status_message1(SM_ORDER
| SM_DING
, 2, 2,
5462 _("Maximum is %s"), comatose(MAX(debug
,9)));
5464 *(int *)(sparms
->proc
.data
.p
) = debug_num
;
5465 q_status_message1(SM_ORDER
, 0, 3,
5466 "Show debug <= level %s",
5467 comatose(debug_num
));
5485 * Returns the message number closest to target that isn't hidden.
5486 * Make warning at least 100 chars.
5487 * A return of 0 means there is no message to jump to.
5490 closest_jump_target(long int target
, MAILSTREAM
*stream
, MSGNO_S
*msgmap
, int no_target
, CmdWhere in_index
, char *warning
, size_t warninglen
)
5492 long i
, start
, closest
= 0L;
5497 maxnum
= (in_index
== ThrdIndx
) ? msgmap
->max_thrdno
: mn_get_total(msgmap
);
5502 snprintf(warning
, warninglen
, "No %s number entered, jump to end? ",
5503 (in_index
== ThrdIndx
) ? "thread" : "message");
5504 warning
[warninglen
-1] = '\0';
5506 else if(target
< 1L)
5507 start
= 1L - target
;
5508 else if(target
> maxnum
)
5509 start
= target
- maxnum
;
5513 if(target
> 0L && target
<= maxnum
)
5514 if(in_index
== ThrdIndx
5515 || !msgline_hidden(stream
, msgmap
, target
, 0))
5518 for(i
= start
; target
+i
<= maxnum
|| target
-i
> 0L; i
++){
5520 if(target
+i
> 0L && target
+i
<= maxnum
&&
5521 (in_index
== ThrdIndx
5522 || !msgline_hidden(stream
, msgmap
, target
+i
, 0))){
5527 if(target
-i
> 0L && target
-i
<= maxnum
&&
5528 (in_index
== ThrdIndx
5529 || !msgline_hidden(stream
, msgmap
, target
-i
, 0))){
5535 strncpy(buf
, long2string(closest
), sizeof(buf
));
5536 buf
[sizeof(buf
)-1] = '\0';
5539 strncpy(warning
, "Nothing to jump to", warninglen
);
5540 else if(target
< 1L)
5541 snprintf(warning
, warninglen
, "%s number (%s) must be at least %s",
5542 (in_index
== ThrdIndx
) ? "Thread" : "Message",
5543 long2string(target
), buf
);
5544 else if(target
> maxnum
)
5545 snprintf(warning
, warninglen
, "%s number (%s) may be no more than %s",
5546 (in_index
== ThrdIndx
) ? "Thread" : "Message",
5547 long2string(target
), buf
);
5549 snprintf(warning
, warninglen
,
5550 "Message number (%s) is not in \"Zoomed Index\" - Closest is(%s)",
5551 long2string(target
), buf
);
5553 warning
[warninglen
-1] = '\0';
5559 /*----------------------------------------------------------------------
5560 Prompt for folder name to open, expand the name and return it
5562 Args: qline -- Screen line to prompt on
5563 allow_list -- if 1, allow ^T to bring up collection lister
5565 Result: returns the folder name or NULL
5566 pine structure mangled_footer flag is set
5567 may call the collection lister in which case mangled screen will be set
5569 This prompts the user for the folder to open, possibly calling up
5570 the collection lister if the user types ^T.
5571 ----------------------------------------------------------------------*/
5573 broach_folder(int qline
, int allow_list
, int *notrealinbox
, CONTEXT_S
**context
)
5576 static char newfolder
[MAILTMPLEN
];
5577 char expanded
[MAXPATH
+1],
5578 prompt
[MAX_SCREEN_COLS
+1],
5580 unsigned char *f1
, *f2
, *f3
;
5581 static HISTORY_S
*history
= NULL
;
5582 CONTEXT_S
*tc
, *tc2
;
5584 int rc
, r
, ku
= -1, n
, flags
, last_rc
= 0, inbox
, done
= 0;
5586 if(qline
== 0 && allow_list
== 0 && notrealinbox
== NULL
&& context
== NULL
){
5588 free_hist(&history
);
5593 * the idea is to provide a clue for the context the file name
5594 * will be saved in (if a non-imap names is typed), and to
5595 * only show the previous if it was also in the same context
5602 (*notrealinbox
) = 1;
5604 init_hist(&history
, HISTSIZE
);
5606 tc
= broach_get_folder(context
? *context
: NULL
, &inbox
, NULL
);
5608 /* set up extra command option keys */
5610 ekey
[rc
].ch
= (allow_list
) ? ctrl('T') : 0 ;
5611 ekey
[rc
].rval
= (allow_list
) ? 2 : 0;
5612 ekey
[rc
].name
= (allow_list
) ? "^T" : "";
5613 ekey
[rc
++].label
= (allow_list
) ? N_("ToFldrs") : "";
5615 if(ps_global
->context_list
->next
){
5616 ekey
[rc
].ch
= ctrl('P');
5618 ekey
[rc
].name
= "^P";
5619 ekey
[rc
++].label
= N_("Prev Collection");
5621 ekey
[rc
].ch
= ctrl('N');
5623 ekey
[rc
].name
= "^N";
5624 ekey
[rc
++].label
= N_("Next Collection");
5627 ekey
[rc
].ch
= ctrl('W');
5629 ekey
[rc
].name
= "^W";
5630 ekey
[rc
++].label
= N_("INBOX");
5632 if(F_ON(F_ENABLE_TAB_COMPLETE
,ps_global
)){
5635 ekey
[rc
].name
= "TAB";
5636 ekey
[rc
++].label
= N_("Complete");
5639 if(F_ON(F_ENABLE_SUB_LISTS
, ps_global
)){
5640 ekey
[rc
].ch
= ctrl('X');
5642 ekey
[rc
].name
= "^X";
5643 ekey
[rc
++].label
= N_("ListMatches");
5646 if(ps_global
->context_list
->next
&& F_ON(F_DISABLE_SAVE_INPUT_HISTORY
, ps_global
)){
5647 ekey
[rc
].ch
= KEY_UP
;
5650 ekey
[rc
++].label
= "";
5652 ekey
[rc
].ch
= KEY_DOWN
;
5655 ekey
[rc
++].label
= "";
5657 else if(F_OFF(F_DISABLE_SAVE_INPUT_HISTORY
, ps_global
)){
5658 ekey
[rc
].ch
= KEY_UP
;
5662 ekey
[rc
++].label
= "";
5664 ekey
[rc
].ch
= KEY_DOWN
;
5667 ekey
[rc
++].label
= "";
5674 * Figure out next default value for this context. The idea
5675 * is that in each context the last folder opened is cached.
5676 * It's up to pick it out and display it. This is fine
5677 * and dandy if we've currently got the inbox open, BUT
5678 * if not, make the inbox the default the first time thru.
5681 last_folder
= ps_global
->inbox_name
;
5682 inbox
= 1; /* pretend we're in inbox from here on out */
5685 last_folder
= (ps_global
->last_unambig_folder
[0])
5686 ? ps_global
->last_unambig_folder
5687 : ((tc
->last_folder
[0]) ? tc
->last_folder
: NULL
);
5689 if(last_folder
){ /* MAXPATH + 1 = sizeof(expanded) */
5690 unsigned char *fname
= folder_name_decoded((unsigned char *)last_folder
);
5691 snprintf(expanded
, sizeof(expanded
), " [%.*s]", MAXPATH
+1-5,
5692 fname
? (char *) fname
: last_folder
);
5693 if(fname
) fs_give((void **)&fname
);
5698 expanded
[sizeof(expanded
)-1] = '\0';
5700 /* only show collection number if more than one available */
5701 if(ps_global
->context_list
->next
) /* MAX_SCREEN_COLS+1 == sizeof(prompt) */
5702 snprintf(prompt
, sizeof(prompt
), "GOTO %s in <%s> %.*s%s: ",
5703 NEWS_TEST(tc
) ? "news group" : "folder",
5704 tc
->nickname
, MAX_SCREEN_COLS
+1-50, expanded
,
5705 *expanded
? " " : "");
5706 else /* MAX_SCREEN_COLS+1 == sizeof(prompt) */
5707 snprintf(prompt
, sizeof(prompt
), "GOTO folder %.*s%s: ", MAX_SCREEN_COLS
+1-20, expanded
,
5708 *expanded
? " " : "");
5710 prompt
[sizeof(prompt
)-1] = '\0';
5712 if(utf8_width(prompt
) > MAXPROMPT
){
5713 if(ps_global
->context_list
->next
) /* MAX_SCREEN_COLS+1 == sizeof(prompt) */
5714 snprintf(prompt
, sizeof(prompt
), "GOTO <%s> %.*s%s: ",
5715 tc
->nickname
, MAX_SCREEN_COLS
+1-50, expanded
,
5716 *expanded
? " " : "");
5717 else /* MAX_SCREEN_COLS+1 == sizeof(prompt) */
5718 snprintf(prompt
, sizeof(prompt
), "GOTO %.*s%s: ", MAX_SCREEN_COLS
+1-20, expanded
,
5719 *expanded
? " " : "");
5721 prompt
[sizeof(prompt
)-1] = '\0';
5723 if(utf8_width(prompt
) > MAXPROMPT
){
5724 if(ps_global
->context_list
->next
) /* MAX_SCREEN_COLS+1 == sizeof(prompt) */
5725 snprintf(prompt
, sizeof(prompt
), "<%s> %.*s%s: ",
5726 tc
->nickname
, MAX_SCREEN_COLS
+1-50, expanded
,
5727 *expanded
? " " : "");
5728 else /* MAX_SCREEN_COLS+1 == sizeof(prompt) */
5729 snprintf(prompt
, sizeof(prompt
), "%.*s%s: ", MAX_SCREEN_COLS
+1-20, expanded
,
5730 *expanded
? " " : "");
5732 prompt
[sizeof(prompt
)-1] = '\0';
5737 if(items_in_hist(history
) > 1){
5738 ekey
[ku
].name
= HISTORY_UP_KEYNAME
;
5739 ekey
[ku
].label
= HISTORY_KEYLABEL
;
5740 ekey
[ku
+1].name
= HISTORY_DOWN_KEYNAME
;
5741 ekey
[ku
+1].label
= HISTORY_KEYLABEL
;
5745 ekey
[ku
].label
= "";
5746 ekey
[ku
+1].name
= "";
5747 ekey
[ku
+1].label
= "";
5751 /* is there any other way to do this? The point is that we
5752 * are trying to hide mutf7 from the user, and use the utf8
5753 * equivalent. So we create a variable f to take place of
5754 * newfolder, including content and size. f2 is copy of f1
5755 * that has to freed. Sigh!
5757 f3
= (unsigned char *) cpystr(newfolder
);
5758 f1
= fs_get(sizeof(newfolder
));
5759 f2
= folder_name_decoded(f3
);
5760 if(f3
) fs_give((void **)&f3
);
5761 strncpy((char *)f1
, (char *)f2
, sizeof(newfolder
));
5762 f1
[sizeof(newfolder
)-1] = '\0';
5763 if(f2
) fs_give((void **)&f2
);
5765 flags
= OE_APPEND_CURRENT
;
5766 rc
= optionally_enter((char *) f1
, qline
, 0, sizeof(newfolder
),
5767 (char *) prompt
, ekey
, help
, &flags
);
5769 f2
= folder_name_encoded(f1
);
5770 strncpy(newfolder
, (char *)f2
, sizeof(newfolder
));
5771 if(f1
) fs_give((void **)&f1
);
5772 if(f2
) fs_give((void **)&f2
);
5774 ps_global
->mangled_footer
= 1;
5777 case -1 : /* o_e says error! */
5778 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
5779 _("Error reading folder name"));
5782 case 0 : /* o_e says normal entry */
5783 removing_trailing_white_space(newfolder
);
5784 removing_leading_white_space(newfolder
);
5787 char *name
, *fullname
= NULL
;
5788 int exists
, breakout
= 0;
5790 save_hist(history
, newfolder
, 0, tc
);
5792 if(!(name
= folder_is_nick(newfolder
, FOLDERS(tc
),
5796 if(update_folder_spec(expanded
, sizeof(expanded
), name
)){
5797 strncpy(name
= newfolder
, expanded
, sizeof(newfolder
));
5798 newfolder
[sizeof(newfolder
)-1] = '\0';
5801 exists
= folder_name_exists(tc
, name
, &fullname
);
5804 strncpy(name
= newfolder
, fullname
, sizeof(newfolder
));
5805 newfolder
[sizeof(newfolder
)-1] = '\0';
5806 fs_give((void **) &fullname
);
5811 * if we know the things a folder, open it.
5812 * else if we know its a directory, visit it.
5813 * else we're not sure (it either doesn't really
5814 * exist or its unLISTable) so try opening it anyway
5816 if(exists
& FEX_ISFILE
){
5820 else if((exists
& FEX_ISDIR
)){
5822 CONTEXT_S
*fake_context
;
5823 char tmp
[MAILTMPLEN
];
5826 strncpy(tmp
, name
, sizeof(tmp
));
5827 tmp
[sizeof(tmp
)-2-1] = '\0';
5828 if(tmp
[(l
= strlen(tmp
)) - 1] != tc
->dir
->delim
){
5829 if(l
< sizeof(tmp
)){
5830 tmp
[l
] = tc
->dir
->delim
;
5831 strncpy(&tmp
[l
+1], "[]", sizeof(tmp
)-(l
+1));
5835 strncat(tmp
, "[]", sizeof(tmp
)-strlen(tmp
)-1);
5837 tmp
[sizeof(tmp
)-1] = '\0';
5839 fake_context
= new_context(tmp
, 0);
5840 newfolder
[0] = '\0';
5841 done
= display_folder_list(&fake_context
, newfolder
,
5842 1, folders_for_goto
);
5843 free_context(&fake_context
);
5846 else if(!(tc
->use
& CNTXT_INCMNG
)){
5847 done
= display_folder_list(&tc
, newfolder
,
5848 1, folders_for_goto
);
5852 else if((exists
& FEX_ERROR
)){
5853 q_status_message1(SM_ORDER
, 0, 3,
5854 _("Problem accessing folder \"%s\""),
5863 if(exists
== FEX_ERROR
)
5864 q_status_message1(SM_ORDER
, 0, 3,
5865 _("Problem accessing folder \"%s\""),
5867 else if(tc
->use
& CNTXT_INCMNG
)
5868 q_status_message1(SM_ORDER
, 0, 3,
5869 _("Can't find Incoming Folder: %s"),
5871 else if(context_isambig(newfolder
))
5872 q_status_message2(SM_ORDER
, 0, 3,
5873 _("Can't find folder \"%s\" in %s"),
5874 newfolder
, (void *) tc
->nickname
);
5876 q_status_message1(SM_ORDER
, 0, 3,
5877 _("Can't find folder \"%s\""),
5882 else if(last_folder
){
5883 if(ps_global
->goto_default_rule
== GOTO_FIRST_CLCTN_DEF_INBOX
5884 && !strucmp(last_folder
, ps_global
->inbox_name
)
5885 && tc
== ((ps_global
->context_list
->use
& CNTXT_INCMNG
)
5886 ? ps_global
->context_list
->next
: ps_global
->context_list
)){
5888 (*notrealinbox
) = 0;
5890 tc
= ps_global
->context_list
;
5893 strncpy(newfolder
, last_folder
, sizeof(newfolder
));
5894 newfolder
[sizeof(newfolder
)-1] = '\0';
5895 save_hist(history
, newfolder
, 0, tc
);
5899 /* fall thru like they cancelled */
5901 case 1 : /* o_e says user cancel */
5902 cmd_cancelled("Open folder");
5905 case 2 : /* o_e says user wants list */
5906 r
= display_folder_list(&tc
, newfolder
, 0, folders_for_goto
);
5912 case 3 : /* o_e says user wants help */
5913 help
= help
== NO_HELP
? h_oe_broach
: NO_HELP
;
5916 case 4 : /* redraw */
5919 case 10 : /* Previous collection */
5920 tc2
= ps_global
->context_list
;
5921 while(tc2
->next
&& tc2
->next
!= tc
)
5927 case 11 : /* Next collection */
5928 tc
= (tc
->next
) ? tc
->next
: ps_global
->context_list
;
5931 case 12 : /* file name completion */
5932 if(!folder_complete(tc
, newfolder
, sizeof(newfolder
), &n
)){
5933 if(n
&& last_rc
== 12 && !(flags
& OE_USER_MODIFIED
)){
5934 r
= display_folder_list(&tc
, newfolder
, 1,folders_for_goto
);
5936 done
++; /* bingo! */
5938 rc
= 0; /* burn last_rc */
5946 case 14 : /* file name completion */
5947 r
= display_folder_list(&tc
, newfolder
, 2, folders_for_goto
);
5949 done
++; /* bingo! */
5951 rc
= 0; /* burn last_rc */
5955 case 17 : /* GoTo INBOX */
5957 strncpy(newfolder
, ps_global
->inbox_name
, sizeof(newfolder
)-1);
5958 newfolder
[sizeof(newfolder
)-1] = '\0';
5960 (*notrealinbox
) = 0;
5962 tc
= ps_global
->context_list
;
5963 save_hist(history
, newfolder
, 0, tc
);
5968 if((p
= get_prev_hist(history
, newfolder
, 0, tc
)) != NULL
){
5969 strncpy(newfolder
, p
, sizeof(newfolder
));
5970 newfolder
[sizeof(newfolder
)-1] = '\0';
5971 if(history
->hist
[history
->curindex
])
5972 tc
= history
->hist
[history
->curindex
]->cntxt
;
5980 if((p
= get_next_hist(history
, newfolder
, 0, tc
)) != NULL
){
5981 strncpy(newfolder
, p
, sizeof(newfolder
));
5982 newfolder
[sizeof(newfolder
)-1] = '\0';
5983 if(history
->hist
[history
->curindex
])
5984 tc
= history
->hist
[history
->curindex
]->cntxt
;
5992 alpine_panic("Unhandled case");
5999 dprint((2, "broach folder, name entered \"%s\"\n",
6000 newfolder
? newfolder
: "?"));
6002 /*-- Just check that we can expand this. It gets done for real later --*/
6003 strncpy(expanded
, newfolder
, sizeof(expanded
));
6004 expanded
[sizeof(expanded
)-1] = '\0';
6006 if(!expand_foldername(expanded
, sizeof(expanded
))) {
6007 dprint((1, "Error: Failed on expansion of filename %s (do_broach)\n",
6008 expanded
? expanded
: "?"));
6017 /*----------------------------------------------------------------------
6018 Check to see if user wants to reopen dead stream.
6023 Result: 1 if the folder was successfully updatedn
6028 ask_mailbox_reopen(struct pine
*ps
, int *reopenp
)
6030 if(((ps
->mail_stream
->dtb
6031 && ((ps
->mail_stream
->dtb
->flags
& DR_NONEWMAIL
)
6032 || (ps
->mail_stream
->rdonly
6033 && ps
->mail_stream
->dtb
->flags
& DR_NONEWMAILRONLY
)))
6034 && (ps
->reopen_rule
== REOPEN_ASK_ASK_Y
6035 || ps
->reopen_rule
== REOPEN_ASK_ASK_N
6036 || ps
->reopen_rule
== REOPEN_ASK_NO_Y
6037 || ps
->reopen_rule
== REOPEN_ASK_NO_N
))
6038 || ((ps
->mail_stream
->dtb
6039 && ps
->mail_stream
->rdonly
6040 && !(ps
->mail_stream
->dtb
->flags
& DR_LOCAL
))
6041 && (ps
->reopen_rule
== REOPEN_YES_ASK_Y
6042 || ps
->reopen_rule
== REOPEN_YES_ASK_N
6043 || ps
->reopen_rule
== REOPEN_ASK_ASK_Y
6044 || ps
->reopen_rule
== REOPEN_ASK_ASK_N
))){
6047 switch(ps
->reopen_rule
){
6048 case REOPEN_YES_ASK_Y
:
6049 case REOPEN_ASK_ASK_Y
:
6050 case REOPEN_ASK_NO_Y
:
6059 switch(want_to("Re-open folder to check for new messages", deefault
,
6060 'x', h_reopen_folder
, WT_NORM
)){
6075 /*----------------------------------------------------------------------
6076 Check to see if user input is in form of old c-client mailbox speck
6081 Result: 1 if the folder was successfully updatedn
6086 update_folder_spec(char *new, size_t newlen
, char *old
)
6092 if(*(p
= old
) == '*') /* old form? */
6095 if(*old
== '{') /* copy host spec */
6097 switch(*new = *old
++){
6102 if(!struncmp(old
, "nntp", 4))
6110 while(*new++ != '}' && (new-orignew
) < newlen
-1);
6112 if((*p
== '*' && *old
) || ((*old
== '*') ? *++old
: 0)){
6114 * OK, some heuristics here. If it looks like a newsgroup
6115 * then we plunk it into the #news namespace else we
6116 * assume that they're trying to get at a #public folder...
6119 *p
&& (isalnum((unsigned char) *p
) || strindex(".-", *p
));
6123 sstrncpy(&new, (*p
&& !nntp
) ? "#public/" : "#news.", newlen
-(new-orignew
));
6124 strncpy(new, old
, newlen
-(new-orignew
));
6128 orignew
[newlen
-1] = '\0';
6134 /*----------------------------------------------------------------------
6135 Open the requested folder in the requested context
6137 Args: state -- usual pine state struct
6138 newfolder -- folder to open
6139 new_context -- folder context might live in
6140 stream -- candidate for recycling
6142 Result: New folder open or not (if error), and we're set to
6143 enter the index screen.
6146 visit_folder(struct pine
*state
, char *newfolder
, CONTEXT_S
*new_context
,
6147 MAILSTREAM
*stream
, long unsigned int flags
)
6149 dprint((9, "visit_folder(%s, %s)\n",
6150 newfolder
? newfolder
: "?",
6151 (new_context
&& new_context
->context
)
6152 ? new_context
->context
: "(NULL)"));
6154 if(ps_global
&& ps_global
->ttyo
){
6155 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
6156 ps_global
->mangled_footer
= 1;
6159 if(do_broach_folder(newfolder
, new_context
, stream
? &stream
: NULL
,
6161 || !sp_flagged(state
->mail_stream
, SP_LOCKED
))
6162 state
->next_screen
= mail_index_screen
;
6164 state
->next_screen
= folder_screen
;
6168 /*----------------------------------------------------------------------
6169 Move read messages from folder if listed in archive
6175 read_msg_prompt(long int n
, char *f
)
6177 char buf
[MAX_SCREEN_COLS
+1];
6179 snprintf(buf
, sizeof(buf
), "Save the %ld read message%s in \"%s\"", n
, plural(n
), f
);
6180 buf
[sizeof(buf
)-1] = '\0';
6181 return(want_to(buf
, 'y', 0, NO_HELP
, WT_NORM
) == 'y');
6185 /*----------------------------------------------------------------------
6186 Print current message[s] or folder index
6188 Args: state -- pointer to struct holding a bunch of pine state
6189 msgmap -- table mapping msg nums to c-client sequence nums
6190 aopt -- aggregate options
6191 in_index -- boolean indicating we're called from Index Screen
6193 Filters the original header and sends stuff to printer
6196 cmd_print(struct pine
*state
, MSGNO_S
*msgmap
, int aopt
, CmdWhere in_index
)
6199 long i
, msgs
, rawno
;
6200 int next
= 0, do_index
= 0, rv
= 0;
6205 if(MCMD_ISAGG(aopt
) && !pseudo_selected(state
->mail_stream
, msgmap
))
6208 msgs
= mn_total_cur(msgmap
);
6210 if((in_index
!= View
) && F_ON(F_PRINT_INDEX
, state
)){
6213 static ESCKEY_S prt_opts
[] = {
6214 {'i', 'i', "I", N_("Index")},
6215 {'m', 'm', "M", NULL
},
6216 {-1, 0, NULL
, NULL
}};
6218 if(in_index
== ThrdIndx
){
6219 /* TRANSLATORS: This is a question, Print Index ? */
6220 if(want_to(_("Print Index"), 'y', 'x', NO_HELP
, WT_NORM
) == 'y')
6226 snprintf(m
, sizeof(m
), "Message%s", (msgs
>1L) ? "s" : "");
6227 m
[sizeof(m
)-1] = '\0';
6228 prt_opts
[1].label
= m
;
6229 snprintf(prompt
, sizeof(prompt
), "Print %sFolder Index or %s %s? ",
6230 (aopt
& MCMD_AGG_2
) ? "thread " : MCMD_ISAGG(aopt
) ? "selected " : "",
6231 (aopt
& MCMD_AGG_2
) ? "thread" : MCMD_ISAGG(aopt
) ? "selected" : "current", m
);
6232 prompt
[sizeof(prompt
)-1] = '\0';
6234 ans
= radio_buttons(prompt
, -FOOTER_ROWS(state
), prt_opts
, 'm', 'x',
6235 NO_HELP
, RB_NORM
|RB_SEQ_SENSITIVE
);
6240 cmd_cancelled("Print");
6241 if(MCMD_ISAGG(aopt
))
6242 restore_selected(msgmap
);
6257 snprintf(prompt
, sizeof(prompt
), "%sFolder Index",
6258 (aopt
& MCMD_AGG_2
) ? "Thread " : MCMD_ISAGG(aopt
) ? "Selected " : "");
6260 snprintf(prompt
, sizeof(prompt
), "%s messages", long2string(msgs
));
6262 snprintf(prompt
, sizeof(prompt
), "Message %s", long2string(mn_get_cur(msgmap
)));
6264 prompt
[sizeof(prompt
)-1] = '\0';
6266 if(open_printer(prompt
) < 0){
6267 if(MCMD_ISAGG(aopt
))
6268 restore_selected(msgmap
);
6276 tc
= format_titlebar();
6278 /* Print titlebar... */
6279 print_text1("%s\n\n", tc
? tc
->titlebar_line
: "");
6280 /* then all the index members... */
6281 if(!print_index(state
, msgmap
, MCMD_ISAGG(aopt
)))
6282 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
6283 _("Error printing folder index"));
6289 for(i
= mn_first_cur(msgmap
); i
> 0L; i
= mn_next_cur(msgmap
), next
++){
6290 if(next
&& F_ON(F_AGG_PRINT_FF
, state
))
6291 if(!print_char(FORMFEED
)){
6296 if(!(state
->mail_stream
6297 && (rawno
= mn_m2raw(msgmap
, i
)) > 0L
6298 && rawno
<= state
->mail_stream
->nmsgs
6299 && (mc
= mail_elt(state
->mail_stream
, rawno
))
6303 if(!(e
=pine_mail_fetchstructure(state
->mail_stream
,
6306 || (F_ON(F_FROM_DELIM_IN_PRINT
, ps_global
)
6307 && !bezerk_delimiter(e
, mc
, print_char
, next
))
6308 || !format_message(mn_m2raw(msgmap
, mn_get_cur(msgmap
)),
6309 e
, b
, NULL
, FM_NEW_MESS
| FM_NOINDENT
,
6311 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
6312 _("Error printing message"));
6321 if(MCMD_ISAGG(aopt
))
6322 restore_selected(msgmap
);
6328 /*----------------------------------------------------------------------
6331 Args: state -- various pine state bits
6332 msgmap -- Message number mapping table
6333 aopt -- option flags
6335 Filters the original header and sends stuff to specified command
6338 cmd_pipe(struct pine
*state
, MSGNO_S
*msgmap
, int aopt
)
6344 char *resultfilename
= NULL
, prompt
[80], *p
;
6345 int done
= 0, rv
= 0;
6347 int fourlabel
= -1, j
= 0, next
= 0, ku
;
6348 int pipe_rv
; /* rv of proc to separate from close_system_pipe rv */
6350 unsigned flagsforhist
= 1; /* raw=8/delimit=4/newpipe=2/capture=1 */
6351 static HISTORY_S
*history
= NULL
;
6352 int capture
= 1, raw
= 0, delimit
= 0, newpipe
= 0;
6353 char pipe_command
[MAXPATH
];
6354 ESCKEY_S pipe_opt
[8];
6356 if(state
== NULL
&& msgmap
== NULL
&& aopt
== 0){
6358 free_hist(&history
);
6362 if(ps_global
->restricted
){
6363 q_status_message(SM_ORDER
| SM_DING
, 0, 4,
6364 "Alpine demo can't pipe messages");
6367 else if(!any_messages(msgmap
, NULL
, "to Pipe"))
6370 pipe_command
[0] = '\0';
6371 init_hist(&history
, HISTSIZE
);
6372 flagsforhist
= (raw
? 0x8 : 0) +
6373 (delimit
? 0x4 : 0) +
6374 (newpipe
? 0x2 : 0) +
6375 (capture
? 0x1 : 0);
6376 if((p
= get_prev_hist(history
, "", flagsforhist
, NULL
)) != NULL
){
6377 strncpy(pipe_command
, p
, sizeof(pipe_command
));
6378 pipe_command
[sizeof(pipe_command
)-1] = '\0';
6379 if(history
->hist
[history
->curindex
]){
6380 flagsforhist
= history
->hist
[history
->curindex
]->flags
;
6381 raw
= (flagsforhist
& 0x8) ? 1 : 0;
6382 delimit
= (flagsforhist
& 0x4) ? 1 : 0;
6383 newpipe
= (flagsforhist
& 0x2) ? 1 : 0;
6384 capture
= (flagsforhist
& 0x1) ? 1 : 0;
6389 pipe_opt
[j
].rval
= 0;
6390 pipe_opt
[j
].name
= "";
6391 pipe_opt
[j
++].label
= "";
6393 pipe_opt
[j
].ch
= ctrl('W');
6394 pipe_opt
[j
].rval
= 10;
6395 pipe_opt
[j
].name
= "^W";
6396 pipe_opt
[j
++].label
= NULL
;
6398 pipe_opt
[j
].ch
= ctrl('Y');
6399 pipe_opt
[j
].rval
= 11;
6400 pipe_opt
[j
].name
= "^Y";
6401 pipe_opt
[j
++].label
= NULL
;
6403 pipe_opt
[j
].ch
= ctrl('R');
6404 pipe_opt
[j
].rval
= 12;
6405 pipe_opt
[j
].name
= "^R";
6406 pipe_opt
[j
++].label
= NULL
;
6408 if(MCMD_ISAGG(aopt
)){
6409 if(!pseudo_selected(state
->mail_stream
, msgmap
))
6413 pipe_opt
[j
].ch
= ctrl('T');
6414 pipe_opt
[j
].rval
= 13;
6415 pipe_opt
[j
].name
= "^T";
6416 pipe_opt
[j
++].label
= NULL
;
6420 pipe_opt
[j
].ch
= KEY_UP
;
6421 pipe_opt
[j
].rval
= 30;
6422 pipe_opt
[j
].name
= "";
6424 pipe_opt
[j
++].label
= "";
6426 pipe_opt
[j
].ch
= KEY_DOWN
;
6427 pipe_opt
[j
].rval
= 31;
6428 pipe_opt
[j
].name
= "";
6429 pipe_opt
[j
++].label
= "";
6431 pipe_opt
[j
].ch
= -1;
6436 snprintf(prompt
, sizeof(prompt
), "Pipe %smessage%s%s to %s%s%s%s%s%s%s: ",
6438 MCMD_ISAGG(aopt
) ? "s" : " ",
6439 MCMD_ISAGG(aopt
) ? "" : comatose(mn_get_cur(msgmap
)),
6440 (!capture
|| delimit
|| (newpipe
&& MCMD_ISAGG(aopt
))) ? "(" : "",
6441 capture
? "" : "uncaptured",
6442 (!capture
&& delimit
) ? "," : "",
6443 delimit
? "delimited" : "",
6444 ((!capture
|| delimit
) && newpipe
&& MCMD_ISAGG(aopt
)) ? "," : "",
6445 (newpipe
&& MCMD_ISAGG(aopt
)) ? "new pipe" : "",
6446 (!capture
|| delimit
|| (newpipe
&& MCMD_ISAGG(aopt
))) ? ") " : "");
6447 prompt
[sizeof(prompt
)-1] = '\0';
6448 pipe_opt
[1].label
= raw
? N_("Shown Text") : N_("Raw Text");
6449 pipe_opt
[2].label
= capture
? N_("Free Output") : N_("Capture Output");
6450 pipe_opt
[3].label
= delimit
? N_("No Delimiter") : N_("With Delimiter");
6452 pipe_opt
[fourlabel
].label
= newpipe
? N_("To Same Pipe") : N_("To Individual Pipes");
6456 * 2 is really 1 because there will be one real entry and
6457 * one entry of "" because of the get_prev_hist above.
6459 if(items_in_hist(history
) > 2){
6460 pipe_opt
[ku
].name
= HISTORY_UP_KEYNAME
;
6461 pipe_opt
[ku
].label
= HISTORY_KEYLABEL
;
6462 pipe_opt
[ku
+1].name
= HISTORY_DOWN_KEYNAME
;
6463 pipe_opt
[ku
+1].label
= HISTORY_KEYLABEL
;
6466 pipe_opt
[ku
].name
= "";
6467 pipe_opt
[ku
].label
= "";
6468 pipe_opt
[ku
+1].name
= "";
6469 pipe_opt
[ku
+1].label
= "";
6472 flags
= OE_APPEND_CURRENT
| OE_SEQ_SENSITIVE
;
6473 switch(optionally_enter(pipe_command
, -FOOTER_ROWS(state
), 0,
6474 sizeof(pipe_command
), prompt
,
6475 pipe_opt
, NO_HELP
, &flags
)){
6477 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
6478 _("Internal problem encountered"));
6482 case 10 : /* flip raw bit */
6486 case 11 : /* flip capture bit */
6490 case 12 : /* flip delimit bit */
6494 case 13 : /* flip newpipe bit */
6499 flagsforhist
= (raw
? 0x8 : 0) +
6500 (delimit
? 0x4 : 0) +
6501 (newpipe
? 0x2 : 0) +
6502 (capture
? 0x1 : 0);
6503 if((p
= get_prev_hist(history
, pipe_command
, flagsforhist
, NULL
)) != NULL
){
6504 strncpy(pipe_command
, p
, sizeof(pipe_command
));
6505 pipe_command
[sizeof(pipe_command
)-1] = '\0';
6506 if(history
->hist
[history
->curindex
]){
6507 flagsforhist
= history
->hist
[history
->curindex
]->flags
;
6508 raw
= (flagsforhist
& 0x8) ? 1 : 0;
6509 delimit
= (flagsforhist
& 0x4) ? 1 : 0;
6510 newpipe
= (flagsforhist
& 0x2) ? 1 : 0;
6511 capture
= (flagsforhist
& 0x1) ? 1 : 0;
6520 flagsforhist
= (raw
? 0x8 : 0) +
6521 (delimit
? 0x4 : 0) +
6522 (newpipe
? 0x2 : 0) +
6523 (capture
? 0x1 : 0);
6524 if((p
= get_next_hist(history
, pipe_command
, flagsforhist
, NULL
)) != NULL
){
6525 strncpy(pipe_command
, p
, sizeof(pipe_command
));
6526 pipe_command
[sizeof(pipe_command
)-1] = '\0';
6527 if(history
->hist
[history
->curindex
]){
6528 flagsforhist
= history
->hist
[history
->curindex
]->flags
;
6529 raw
= (flagsforhist
& 0x8) ? 1 : 0;
6530 delimit
= (flagsforhist
& 0x4) ? 1 : 0;
6531 newpipe
= (flagsforhist
& 0x2) ? 1 : 0;
6532 capture
= (flagsforhist
& 0x1) ? 1 : 0;
6541 if(pipe_command
[0]){
6543 flagsforhist
= (raw
? 0x8 : 0) +
6544 (delimit
? 0x4 : 0) +
6545 (newpipe
? 0x2 : 0) +
6546 (capture
? 0x1 : 0);
6547 save_hist(history
, pipe_command
, flagsforhist
, NULL
);
6549 flags
= PIPE_USER
| PIPE_WRITE
| PIPE_STDERR
;
6550 flags
|= (raw
? PIPE_RAW
: 0);
6556 ps_global
->mangled_screen
= 1;
6557 ps_global
->in_init_seq
= 1;
6559 flags
|= PIPE_RESET
;
6562 if(!newpipe
&& !(syspipe
= cmd_pipe_open(pipe_command
,
6563 (flags
& PIPE_RESET
)
6569 for(i
= mn_first_cur(msgmap
);
6571 i
= mn_next_cur(msgmap
)){
6572 e
= pine_mail_fetchstructure(ps_global
->mail_stream
,
6573 mn_m2raw(msgmap
, i
), &b
);
6574 if(!(state
->mail_stream
6575 && (rawno
= mn_m2raw(msgmap
, i
)) > 0L
6576 && rawno
<= state
->mail_stream
->nmsgs
6577 && (mc
= mail_elt(state
->mail_stream
, rawno
))
6582 && !(syspipe
= cmd_pipe_open(pipe_command
,
6583 (flags
& PIPE_RESET
)
6587 || (delimit
&& !bezerk_delimiter(e
, mc
, pc
, next
++)))
6594 prime_raw_pipe_getc(ps_global
->mail_stream
,
6595 mn_m2raw(msgmap
, i
), -1L, 0L);
6597 gf_link_filter(gf_nvtnl_local
, NULL
);
6598 if((pipe_err
= gf_pipe(raw_pipe_getc
, pc
)) != NULL
){
6599 q_status_message1(SM_ORDER
|SM_DING
,
6601 _("Internal Error: %s"),
6606 else if(!format_message(mn_m2raw(msgmap
, i
), e
, b
,
6607 NULL
, FM_NEW_MESS
| FM_NOWRAP
, pc
))
6612 if(close_system_pipe(&syspipe
, &pipe_rv
, pipe_callback
) == -1)
6617 ps_global
->in_init_seq
= 0;
6620 if(close_system_pipe(&syspipe
, &pipe_rv
, pipe_callback
) == -1)
6622 if(done
) /* say we had a problem */
6623 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
6624 _("Error piping message"));
6625 else if(resultfilename
){
6627 /* only display if no error */
6628 display_output_file(resultfilename
, "PIPE MESSAGE",
6630 fs_give((void **)&resultfilename
);
6634 q_status_message(SM_ORDER
, 0, 2, _("Pipe command completed"));
6640 /* else fall thru as if cancelled */
6643 cmd_cancelled("Pipe command");
6648 helper(h_common_pipe
, _("HELP FOR PIPE COMMAND"), HLPD_SIMPLE
);
6649 ps_global
->mangled_screen
= 1;
6652 case 2 : /* no place to escape to */
6653 case 4 : /* can't suspend */
6659 ps_global
->mangled_footer
= 1;
6660 if(MCMD_ISAGG(aopt
))
6661 restore_selected(msgmap
);
6667 /*----------------------------------------------------------------------
6668 Screen to offer list management commands contained in message
6670 Args: state -- pointer to struct holding a bunch of pine state
6671 msgmap -- table mapping msg nums to c-client sequence nums
6672 aopt -- aggregate options
6676 NOTE: Inspired by contrib from Jeremy Blackman <loki@maison-otaku.net>
6679 rfc2369_display(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, long int msgno
)
6682 char *h
, *hdrs
[MLCMD_COUNT
+ 1];
6683 long index_no
= mn_raw2m(msgmap
, msgno
);
6684 RFC2369_S data
[MLCMD_COUNT
];
6686 /* for each header field */
6687 if((h
= pine_fetchheader_lines(stream
, msgno
, NULL
, rfc2369_hdrs(hdrs
))) != NULL
){
6688 memset(&data
[0], 0, sizeof(RFC2369_S
) * MLCMD_COUNT
);
6689 if(rfc2369_parse_fields(h
, &data
[0])){
6692 if((explain
= list_mgmt_text(data
, index_no
)) != NULL
){
6693 list_mgmt_screen(explain
);
6694 ps_global
->mangled_screen
= 1;
6700 fs_give((void **) &h
);
6704 q_status_message1(SM_ORDER
, 0, 3,
6705 "Message %s contains no list management information",
6706 comatose(index_no
));
6711 list_mgmt_text(RFC2369_S
*data
, long int msgno
)
6714 int i
, j
, n
, fields
= 0;
6715 static char *rfc2369_intro1
=
6716 "<HTML><HEAD></HEAD><BODY><H1>Mail List Commands</H1>Message ";
6717 static char *rfc2369_intro2
[] = {
6718 N_(" has information associated with it "),
6719 N_("that explains how to participate in an email list. An "),
6720 N_("email list is represented by a single email address that "),
6721 N_("users sharing a common interest can send messages to (known "),
6722 N_("as posting) which are then redistributed to all members "),
6723 N_("of the list (sometimes after review by a moderator)."),
6724 N_("<P>List participation commands in this message include:"),
6728 if((store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
){
6730 /* Insert introductory text */
6731 so_puts(store
, rfc2369_intro1
);
6733 so_puts(store
, comatose(msgno
));
6735 for(i
= 0; rfc2369_intro2
[i
]; i
++)
6736 so_puts(store
, _(rfc2369_intro2
[i
]));
6738 so_puts(store
, "<P>");
6739 for(i
= 0; i
< MLCMD_COUNT
; i
++)
6740 if(data
[i
].data
[0].value
6741 || data
[i
].data
[0].comment
6742 || data
[i
].data
[0].error
){
6744 so_puts(store
, "<UL>");
6746 so_puts(store
, "<LI>");
6748 (n
= (data
[i
].data
[1].value
|| data
[i
].data
[1].comment
))
6752 so_puts(store
, data
[i
].field
.description
);
6753 so_puts(store
, ". ");
6756 so_puts(store
, "<OL>");
6760 && (data
[i
].data
[j
].comment
6761 || data
[i
].data
[j
].value
6762 || data
[i
].data
[j
].error
);
6765 so_puts(store
, n
? "<P><LI>" : "<P>");
6767 if(data
[i
].data
[j
].comment
){
6769 _("With the provided comment:<P><BLOCKQUOTE>"));
6770 so_puts(store
, data
[i
].data
[j
].comment
);
6771 so_puts(store
, "</BLOCKQUOTE><P>");
6774 if(data
[i
].data
[j
].value
){
6776 && !strucmp(data
[i
].data
[j
].value
, "NO")){
6778 _("Posting is <EM>not</EM> allowed on this list"));
6781 so_puts(store
, "Select <A HREF=\"");
6782 so_puts(store
, data
[i
].data
[j
].value
);
6783 so_puts(store
, "\">HERE</A> to ");
6784 so_puts(store
, (data
[i
].field
.action
)
6785 ? data
[i
].field
.action
6789 so_puts(store
, ".");
6792 if(data
[i
].data
[j
].error
){
6793 so_puts(store
, "<P>Unfortunately, Alpine can not offer");
6794 so_puts(store
, " to take direct action based upon it");
6795 so_puts(store
, " because it was improperly formatted.");
6796 so_puts(store
, " The unrecognized data associated with");
6797 so_puts(store
, " the \"");
6798 so_puts(store
, data
[i
].field
.name
);
6799 so_puts(store
, "\" header field was:<P><BLOCKQUOTE>");
6800 so_puts(store
, data
[i
].data
[j
].error
);
6801 so_puts(store
, "</BLOCKQUOTE>");
6804 so_puts(store
, "<P>");
6808 so_puts(store
, "</OL>");
6812 so_puts(store
, "</UL>");
6814 so_puts(store
, "</BODY></HTML>");
6822 list_mgmt_screen(STORE_S
*html
)
6828 HANDLE_S
*handles
= NULL
;
6832 so_seek(html
, 0L, 0);
6833 gf_set_so_readc(&gc
, html
);
6835 init_handles(&handles
);
6837 if((store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
){
6838 gf_set_so_writec(&pc
, store
);
6841 gf_link_filter(gf_html2plain
,
6842 gf_html2plain_opt(NULL
, ps_global
->ttyo
->screen_cols
,
6843 non_messageview_margin(), &handles
, NULL
, 0));
6845 error
= gf_pipe(gc
, pc
);
6847 gf_clear_so_writec(store
);
6852 memset(&sargs
, 0, sizeof(SCROLL_S
));
6853 sargs
.text
.text
= so_text(store
);
6854 sargs
.text
.src
= CharStar
;
6855 sargs
.text
.desc
= "list commands";
6856 sargs
.text
.handles
= handles
;
6858 sargs
.start
.on
= Offset
;
6859 sargs
.start
.loc
.offset
= offset
;
6862 sargs
.bar
.title
= _("MAIL LIST COMMANDS");
6863 sargs
.bar
.style
= MessageNumber
;
6864 sargs
.resize_exit
= 1;
6865 sargs
.help
.text
= h_special_list_commands
;
6866 sargs
.help
.title
= _("HELP FOR LIST COMMANDS");
6867 sargs
.keys
.menu
= &listmgr_keymenu
;
6868 setbitmap(sargs
.keys
.bitmap
);
6870 clrbitn(LM_TRY_KEY
, sargs
.keys
.bitmap
);
6871 clrbitn(LM_PREV_KEY
, sargs
.keys
.bitmap
);
6872 clrbitn(LM_NEXT_KEY
, sargs
.keys
.bitmap
);
6875 cmd
= scrolltool(&sargs
);
6876 offset
= sargs
.start
.loc
.offset
;
6882 free_handles(&handles
);
6883 gf_clear_so_readc(html
);
6885 while(cmd
== MC_RESIZE
);
6889 /*----------------------------------------------------------------------
6890 Prompt the user for the type of select desired
6892 NOTE: any and all functions that successfully exit the second
6893 switch() statement below (currently "select_*() functions"),
6894 *MUST* update the folder's MESSAGECACHE element's "searched"
6895 bits to reflect the search result. Functions using
6896 mail_search() get this for free, the others must update 'em
6899 Returns -1 if canceled without changing selection
6900 0 if selection may have changed
6903 aggregate_select(struct pine
*state
, MSGNO_S
*msgmap
, int q_line
, CmdWhere in_index
)
6905 long i
, diff
, old_tot
, msgno
, raw
;
6906 int q
= 0, rv
= 0, narrow
= 0, hidden
, ret
= -1;
6909 SEARCHSET
*limitsrch
= NULL
;
6911 extern MAILSTREAM
*mm_search_stream
;
6912 extern long mm_search_count
;
6914 hidden
= any_lflagged(msgmap
, MN_HIDE
) > 0L;
6915 mm_search_stream
= state
->mail_stream
;
6916 mm_search_count
= 0L;
6918 sel_opts
= THRD_INDX() ? sel_opts4
: sel_opts2
;
6920 sel_opts
[SEL_OPTS_THREAD
].ch
= SEL_OPTS_THREAD_CH
;
6923 sel_opts
[SEL_OPTS_THREAD
].ch
= -1;
6926 if((old_tot
= any_lflagged(msgmap
, MN_SLCT
)) != 0){
6929 thrd
= fetch_thread(state
->mail_stream
,
6930 mn_m2raw(msgmap
, mn_get_cur(msgmap
)));
6931 /* check if whole thread is selected or not */
6933 count_lflags_in_thread(state
->mail_stream
,thrd
,msgmap
,MN_SLCT
)
6935 count_lflags_in_thread(state
->mail_stream
,thrd
,msgmap
,MN_NONE
))
6938 sel_opts1
[1].label
= i
? N_("unselect Curthrd") : N_("select Curthrd");
6941 i
= get_lflag(state
->mail_stream
, msgmap
, mn_get_cur(msgmap
),
6943 sel_opts1
[1].label
= i
? N_("unselect Cur") : N_("select Cur");
6946 sel_opts
+= 2; /* disable extra options */
6947 switch(q
= radio_buttons(_(sel_pmt1
), q_line
, sel_opts1
, 'c', 'x', NO_HELP
,
6949 case 'f' : /* flip selection */
6951 for(i
= 1L; i
<= mn_get_total(msgmap
); i
++){
6953 q
= !get_lflag(state
->mail_stream
, msgmap
, i
, MN_SLCT
);
6954 set_lflag(state
->mail_stream
, msgmap
, i
, MN_SLCT
, q
);
6956 set_lflag(state
->mail_stream
, msgmap
, i
, MN_HIDE
, !q
);
6958 mn_reset_cur(msgmap
, msgno
= i
);
6964 case 'n' : /* narrow selection */
6966 case 'b' : /* broaden selection */
6967 q
= 0; /* offer criteria prompt */
6970 case 'c' : /* Un/Select Current */
6971 case 'a' : /* Unselect All */
6972 case 'x' : /* cancel */
6976 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
6977 "Unsupported Select option");
6984 q
= radio_buttons(sel_pmt2
, q_line
, sel_opts
, 'c', 'x',
6985 NO_HELP
, RB_NORM
|RB_RET_HELP
);
6988 helper(h_index_cmd_select
, _("HELP FOR SELECT"), HLPD_SIMPLE
);
6989 ps_global
->mangled_screen
= 1;
6997 * The purpose of this is to add the appropriate searchset to the
6998 * search so that the search can be limited to only looking at what
6999 * it needs to look at. That is, if we are narrowing then we only need
7000 * to look at messages which are already selected, and if we are
7001 * broadening, then we only need to look at messages which are not
7002 * yet selected. This routine will work whether or not
7003 * limiting_searchset properly limits the search set. In particular,
7004 * the searchset returned by limiting_searchset may include messages
7005 * which really shouldn't be included. We do that because a too-large
7006 * searchset will break some IMAP servers. It is even possible that it
7007 * becomes inefficient to send the whole set. If the select function
7008 * frees limitsrch, it should be sure to set it to NULL so we won't
7009 * try freeing it again here.
7011 limitsrch
= limiting_searchset(state
->mail_stream
, narrow
);
7014 * NOTE: See note about MESSAGECACHE "searched" bits above!
7017 case 'x': /* cancel */
7018 cmd_cancelled("Select command");
7021 case 'c' : /* select/unselect current */
7022 (void) select_by_current(state
, msgmap
, in_index
);
7026 case 'a' : /* select/unselect all */
7027 msgno
= any_lflagged(msgmap
, MN_SLCT
);
7028 diff
= (!msgno
) ? mn_get_total(msgmap
) : 0L;
7030 agg_select_all(state
->mail_stream
, msgmap
, &diff
,
7031 any_lflagged(msgmap
, MN_SLCT
) <= 0L);
7032 q_status_message4(SM_ORDER
,0,2,
7033 "%s%s message%s %sselected",
7034 msgno
? "" : "All ", comatose(diff
),
7035 plural(diff
), msgno
? "UN" : "");
7038 case 'n' : /* Select by Number */
7041 rv
= select_by_thrd_number(state
->mail_stream
, msgmap
, &limitsrch
);
7043 rv
= select_by_number(state
->mail_stream
, msgmap
, &limitsrch
);
7047 case 'd' : /* Select by Date */
7049 rv
= select_by_date(state
->mail_stream
, msgmap
, mn_get_cur(msgmap
),
7053 case 't' : /* Text */
7055 rv
= select_by_text(state
->mail_stream
, msgmap
, mn_get_cur(msgmap
),
7059 case 'z' : /* Size */
7061 rv
= select_by_size(state
->mail_stream
, &limitsrch
);
7064 case 's' : /* Status */
7066 rv
= select_by_status(state
->mail_stream
, &limitsrch
);
7069 case 'k' : /* Keyword */
7071 rv
= select_by_keyword(state
->mail_stream
, &limitsrch
);
7074 case 'r' : /* Rule */
7076 rv
= select_by_rule(state
->mail_stream
, &limitsrch
);
7079 case 'h' : /* Thread */
7081 rv
= select_by_thread(state
->mail_stream
, msgmap
, &limitsrch
);
7085 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
7086 "Unsupported Select option");
7091 mail_free_searchset(&limitsrch
);
7093 if(rv
) /* bad return value.. */
7094 return(ret
); /* error already displayed */
7096 if(narrow
) /* make sure something was selected */
7097 for(i
= 1L; i
<= mn_get_total(msgmap
); i
++)
7098 if((raw
= mn_m2raw(msgmap
, i
)) > 0L && state
->mail_stream
7099 && raw
<= state
->mail_stream
->nmsgs
7100 && (mc
= mail_elt(state
->mail_stream
, raw
)) && mc
->searched
){
7101 if(get_lflag(state
->mail_stream
, msgmap
, i
, MN_SLCT
))
7108 if(mm_search_count
){
7110 * loop thru all the messages, adjusting local flag bits
7111 * based on their "searched" bit...
7113 for(i
= 1L, msgno
= 0L; i
<= mn_get_total(msgmap
); i
++)
7115 /* turning OFF selectedness if the "searched" bit isn't lit. */
7116 if(get_lflag(state
->mail_stream
, msgmap
, i
, MN_SLCT
)){
7117 if((raw
= mn_m2raw(msgmap
, i
)) > 0L && state
->mail_stream
7118 && raw
<= state
->mail_stream
->nmsgs
7119 && (mc
= mail_elt(state
->mail_stream
, raw
))
7122 set_lflag(state
->mail_stream
, msgmap
, i
, MN_SLCT
, 0);
7124 set_lflag(state
->mail_stream
, msgmap
, i
, MN_HIDE
, 1);
7126 /* adjust current message in case we unselect and hide it */
7127 else if(msgno
< mn_get_cur(msgmap
)
7129 || !get_lflag(state
->mail_stream
, msgmap
,
7134 else if((raw
= mn_m2raw(msgmap
, i
)) > 0L && state
->mail_stream
7135 && raw
<= state
->mail_stream
->nmsgs
7136 && (mc
= mail_elt(state
->mail_stream
, raw
)) && mc
->searched
){
7137 /* turn ON selectedness if "searched" bit is lit. */
7138 if(!get_lflag(state
->mail_stream
, msgmap
, i
, MN_SLCT
)){
7140 set_lflag(state
->mail_stream
, msgmap
, i
, MN_SLCT
, 1);
7142 set_lflag(state
->mail_stream
, msgmap
, i
, MN_HIDE
, 0);
7146 /* if we're zoomed and the current message was unselected */
7148 && get_lflag(state
->mail_stream
,msgmap
,mn_get_cur(msgmap
),MN_HIDE
))
7149 mn_reset_cur(msgmap
, msgno
);
7154 q_status_message4(SM_ORDER
, 3, 3,
7155 "%s. %s message%s remain%s selected.",
7157 ? "No change resulted"
7158 : "No messages in intersection",
7159 comatose(old_tot
), plural(old_tot
),
7160 (old_tot
== 1L) ? "s" : "");
7162 q_status_message(SM_ORDER
, 3, 3,
7163 _("No change resulted. Matching messages already selected."));
7165 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
7166 _("Select failed. No %smessages selected."),
7167 old_tot
? _("additional ") : "");
7170 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,
7171 "Select matched %ld message%s. %s %smessage%s %sselected.",
7172 (diff
> 0) ? diff
: old_tot
+ diff
,
7173 plural((diff
> 0) ? diff
: old_tot
+ diff
),
7174 comatose((diff
> 0) ? any_lflagged(msgmap
, MN_SLCT
) : -diff
),
7175 (diff
> 0) ? "total " : "",
7176 plural((diff
> 0) ? any_lflagged(msgmap
, MN_SLCT
) : -diff
),
7177 (diff
> 0) ? "" : "UN");
7178 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
7179 q_status_message(SM_ORDER
, 3, 3, tmp_20k_buf
);
7182 q_status_message2(SM_ORDER
, 3, 3, _("Select matched %s message%s!"),
7183 comatose(diff
), plural(diff
));
7189 /*----------------------------------------------------------------------
7190 Toggle the state of the current message
7192 Args: state -- pointer pine's state variables
7193 msgmap -- message collection to operate on
7194 in_index -- in the message index view
7195 Returns: TRUE if current marked selected, FALSE otw
7198 select_by_current(struct pine
*state
, MSGNO_S
*msgmap
, CmdWhere in_index
)
7201 int all_selected
= 0;
7202 unsigned long was
, tot
, rawno
;
7205 cur
= mn_get_cur(msgmap
);
7208 thrd
= fetch_thread(state
->mail_stream
, mn_m2raw(msgmap
, cur
));
7212 was
= count_lflags_in_thread(state
->mail_stream
, thrd
, msgmap
, MN_SLCT
);
7213 tot
= count_lflags_in_thread(state
->mail_stream
, thrd
, msgmap
, MN_NONE
);
7218 set_thread_lflags(state
->mail_stream
, thrd
, msgmap
, MN_SLCT
, 0);
7219 if(any_lflagged(msgmap
, MN_HIDE
) > 0L){
7220 set_thread_lflags(state
->mail_stream
, thrd
, msgmap
, MN_HIDE
, 1);
7222 * See if there's anything left to zoom on. If so,
7223 * pick an adjacent one for highlighting, else make
7224 * sure nothing is left hidden...
7226 if(any_lflagged(msgmap
, MN_SLCT
)){
7227 mn_inc_cur(state
->mail_stream
, msgmap
,
7228 (in_index
== View
&& THREADING()
7229 && sp_viewing_a_thread(state
->mail_stream
))
7231 : (in_index
== View
)
7232 ? MH_ANYTHD
: MH_NONE
);
7233 if(mn_get_cur(msgmap
) == cur
)
7234 mn_dec_cur(state
->mail_stream
, msgmap
,
7235 (in_index
== View
&& THREADING()
7236 && sp_viewing_a_thread(state
->mail_stream
))
7238 : (in_index
== View
)
7239 ? MH_ANYTHD
: MH_NONE
);
7241 else /* clear all hidden flags */
7242 (void) unzoom_index(state
, state
->mail_stream
, msgmap
);
7246 set_thread_lflags(state
->mail_stream
, thrd
, msgmap
, MN_SLCT
, 1);
7248 q_status_message3(SM_ORDER
, 0, 2, "%s message%s %sselected",
7249 comatose(all_selected
? was
: tot
-was
),
7250 plural(all_selected
? was
: tot
-was
),
7251 all_selected
? "UN" : "");
7253 /* collapsed thread */
7255 && ((rawno
= mn_m2raw(msgmap
, cur
)) != 0L)
7256 && ((thrd
= fetch_thread(state
->mail_stream
, rawno
)) != NULL
)
7257 && (thrd
&& thrd
->next
&& get_lflag(state
->mail_stream
, NULL
, rawno
, MN_COLL
))){
7259 * This doesn't work quite the same as the colon command works, but
7260 * it is arguably doing the correct thing. The difference is
7261 * that aggregate_select will zoom after selecting back where it
7262 * was called from, but selecting a thread with colon won't zoom.
7263 * Maybe it makes sense to zoom after a select but not after a colon
7264 * command even though they are very similar.
7266 thread_command(state
, state
->mail_stream
, msgmap
, ':', -FOOTER_ROWS(state
));
7270 get_lflag(state
->mail_stream
, msgmap
, cur
, MN_SLCT
)) != 0){ /* set? */
7271 set_lflag(state
->mail_stream
, msgmap
, cur
, MN_SLCT
, 0);
7272 if(any_lflagged(msgmap
, MN_HIDE
) > 0L){
7273 set_lflag(state
->mail_stream
, msgmap
, cur
, MN_HIDE
, 1);
7275 * See if there's anything left to zoom on. If so,
7276 * pick an adjacent one for highlighting, else make
7277 * sure nothing is left hidden...
7279 if(any_lflagged(msgmap
, MN_SLCT
)){
7280 mn_inc_cur(state
->mail_stream
, msgmap
,
7281 (in_index
== View
&& THREADING()
7282 && sp_viewing_a_thread(state
->mail_stream
))
7284 : (in_index
== View
)
7285 ? MH_ANYTHD
: MH_NONE
);
7286 if(mn_get_cur(msgmap
) == cur
)
7287 mn_dec_cur(state
->mail_stream
, msgmap
,
7288 (in_index
== View
&& THREADING()
7289 && sp_viewing_a_thread(state
->mail_stream
))
7291 : (in_index
== View
)
7292 ? MH_ANYTHD
: MH_NONE
);
7294 else /* clear all hidden flags */
7295 (void) unzoom_index(state
, state
->mail_stream
, msgmap
);
7299 set_lflag(state
->mail_stream
, msgmap
, cur
, MN_SLCT
, 1);
7301 q_status_message2(SM_ORDER
, 0, 2, "Message %s %sselected",
7302 long2string(cur
), all_selected
? "UN" : "");
7306 return(!all_selected
);
7310 /*----------------------------------------------------------------------
7311 Prompt the user for the command to perform on selected messages
7313 Args: state -- pointer pine's state variables
7314 msgmap -- message collection to operate on
7315 q_line -- line on display to write prompts
7316 Returns: 1 if the selected messages are suitably commanded,
7317 0 if the choice to pick the command was declined
7321 apply_command(struct pine
*state
, MAILSTREAM
*stream
, MSGNO_S
*msgmap
,
7322 UCS preloadkeystroke
, int flags
, int q_line
)
7324 int i
= 8, /* number of static entries in sel_opts3 */
7328 agg
= (flags
& AC_FROM_THREAD
) ? MCMD_AGG_2
: MCMD_AGG
;
7333 * To do this "right", we really ought to have access to the keymenu
7334 * here and change the typed command into a real command by running
7335 * it through menu_command. Then the switch below would be against
7336 * results from menu_command. If we did that we'd also pass the
7337 * results of menu_command in as preloadkeystroke instead of passing
7338 * the keystroke itself. But we don't have the keymenu handy,
7339 * so we have to fake it. The only complication that we run into
7340 * is that KEY_DEL is an escape sequence so we change a typed
7341 * KEY_DEL esc seq into the letter D.
7344 if(!preloadkeystroke
){
7345 if(F_ON(F_ENABLE_FLAG
,state
)){ /* flag? */
7346 sel_opts3
[i
].ch
= '*';
7347 sel_opts3
[i
].rval
= '*';
7348 sel_opts3
[i
].name
= "*";
7349 sel_opts3
[i
++].label
= N_("Flag");
7352 if(F_ON(F_ENABLE_PIPE
,state
)){ /* pipe? */
7353 sel_opts3
[i
].ch
= '|';
7354 sel_opts3
[i
].rval
= '|';
7355 sel_opts3
[i
].name
= "|";
7356 sel_opts3
[i
++].label
= N_("Pipe");
7359 if(F_ON(F_ENABLE_BOUNCE
,state
)){ /* bounce? */
7360 sel_opts3
[i
].ch
= 'b';
7361 sel_opts3
[i
].rval
= 'b';
7362 sel_opts3
[i
].name
= "B";
7363 sel_opts3
[i
++].label
= N_("Bounce");
7366 if(flags
& AC_FROM_THREAD
){
7367 if(flags
& (AC_COLL
| AC_EXPN
)){
7368 sel_opts3
[i
].ch
= '/';
7369 sel_opts3
[i
].rval
= '/';
7370 sel_opts3
[i
].name
= "/";
7371 sel_opts3
[i
++].label
= (flags
& AC_COLL
) ? N_("Collapse")
7375 sel_opts3
[i
].ch
= ';';
7376 sel_opts3
[i
].rval
= ';';
7377 sel_opts3
[i
].name
= ";";
7378 if(flags
& AC_UNSEL
)
7379 sel_opts3
[i
++].label
= N_("UnSelect");
7381 sel_opts3
[i
++].label
= N_("Select");
7384 if(F_ON(F_ENABLE_PRYNT
, state
)){ /* this one is invisible */
7385 sel_opts3
[i
].ch
= 'y';
7386 sel_opts3
[i
].rval
= '%';
7387 sel_opts3
[i
].name
= "";
7388 sel_opts3
[i
++].label
= "";
7391 if(!is_imap_stream(stream
) || LEVELUIDPLUS(stream
)){ /* expunge selected messages */
7392 sel_opts3
[i
].ch
= 'x';
7393 sel_opts3
[i
].rval
= 'x';
7394 sel_opts3
[i
].name
= "X";
7395 sel_opts3
[i
++].label
= N_("Expunge");
7398 if(nonempty_patterns(ROLE_DO_ROLES
, &pstate
) && first_pattern(&pstate
)){
7399 sel_opts3
[i
].ch
= '#';
7400 sel_opts3
[i
].rval
= '#';
7401 sel_opts3
[i
].name
= "#";
7402 sel_opts3
[i
++].label
= N_("Set Role");
7405 sel_opts3
[i
].ch
= KEY_DEL
; /* also invisible */
7406 sel_opts3
[i
].rval
= 'd';
7407 sel_opts3
[i
].name
= "";
7408 sel_opts3
[i
++].label
= "";
7410 sel_opts3
[i
].ch
= -1;
7412 snprintf(prompt
, sizeof(prompt
), "%s command : ",
7413 (flags
& AC_FROM_THREAD
) ? "THREAD" : "APPLY");
7414 prompt
[sizeof(prompt
)-1] = '\0';
7415 cmd
= double_radio_buttons(prompt
, q_line
, sel_opts3
, 'z', 'c', NO_HELP
,
7421 if(preloadkeystroke
== KEY_DEL
)
7424 if(preloadkeystroke
< 0x80 && isupper((int) preloadkeystroke
))
7425 cmd
= tolower((int) preloadkeystroke
);
7427 cmd
= (int) preloadkeystroke
; /* shouldn't happen */
7432 case 'd' : /* delete */
7433 we_cancel
= busy_cue(NULL
, NULL
, 1);
7434 rv
= cmd_delete(state
, msgmap
, agg
, NULL
); /* don't advance or offer "TAB" */
7439 case 'u' : /* undelete */
7440 we_cancel
= busy_cue(NULL
, NULL
, 1);
7441 rv
= cmd_undelete(state
, msgmap
, agg
);
7446 case 'r' : /* reply */
7447 rv
= cmd_reply(state
, msgmap
, agg
, NULL
);
7450 case 'f' : /* Forward */
7451 rv
= cmd_forward(state
, msgmap
, agg
, NULL
);
7454 case '%' : /* print */
7455 rv
= cmd_print(state
, msgmap
, agg
, MsgIndx
);
7458 case 't' : /* take address */
7459 rv
= cmd_take_addr(state
, msgmap
, agg
);
7462 case 's' : /* save */
7463 rv
= cmd_save(state
, stream
, msgmap
, agg
, MsgIndx
);
7466 case 'e' : /* export */
7467 rv
= cmd_export(state
, msgmap
, q_line
, agg
);
7470 case '|' : /* pipe */
7471 rv
= cmd_pipe(state
, msgmap
, agg
);
7474 case '*' : /* flag */
7475 we_cancel
= busy_cue(NULL
, NULL
, 1);
7476 rv
= cmd_flag(state
, msgmap
, agg
);
7481 case 'b' : /* bounce */
7482 rv
= cmd_bounce(state
, msgmap
, agg
, NULL
);
7486 collapse_or_expand(state
, stream
, msgmap
,
7487 F_ON(F_SLASH_COLL_ENTIRE
, ps_global
)
7489 : mn_get_cur(msgmap
));
7493 select_thread_stmp(state
, stream
, msgmap
);
7496 case 'x' : /* Expunge */
7497 rv
= cmd_expunge(state
, stream
, msgmap
, agg
);
7500 case 'c' : /* cancel */
7501 cmd_cancelled((flags
& AC_FROM_THREAD
) ? "Thread command"
7506 if(nonempty_patterns(ROLE_DO_ROLES
, &pstate
) && first_pattern(&pstate
)){
7507 static ESCKEY_S choose_role
[] = {
7508 {'r', 'r', "R", N_("Reply")},
7509 {'f', 'f', "F", N_("Forward")},
7510 {'b', 'b', "B", N_("Bounce")},
7514 ACTION_S
*role
= NULL
;
7516 action
= radio_buttons(_("Reply, Forward or Bounce using a role? "),
7517 -FOOTER_ROWS(state
), choose_role
,
7518 'r', 'x', h_role_aggregate
, RB_NORM
);
7519 if(action
== 'r' || action
== 'f' || action
== 'b'){
7520 void (*prev_screen
)(struct pine
*) = NULL
, (*redraw
)(void) = NULL
;
7522 redraw
= state
->redrawer
;
7523 state
->redrawer
= NULL
;
7524 prev_screen
= state
->prev_screen
;
7526 state
->next_screen
= SCREEN_FUN_NULL
;
7528 if(role_select_screen(state
, &role
,
7529 action
== 'f' ? MC_FORWARD
:
7530 action
== 'r' ? MC_REPLY
:
7531 action
== 'b' ? MC_BOUNCE
: 0) < 0){
7532 cmd_cancelled(action
== 'f' ? _("Forward") :
7533 action
== 'r' ? _("Reply") : _("Bounce"));
7534 state
->next_screen
= prev_screen
;
7535 state
->redrawer
= redraw
;
7536 state
->mangled_screen
= 1;
7540 role
= combine_inherited_role(role
);
7542 role
= (ACTION_S
*) fs_get(sizeof(*role
));
7543 memset((void *) role
, 0, sizeof(*role
));
7544 role
->nick
= cpystr("Default Role");
7547 state
->redrawer
= NULL
;
7550 (void) cmd_reply(state
, msgmap
, agg
, role
);
7554 (void) cmd_forward(state
, msgmap
, agg
, role
);
7558 (void) cmd_bounce(state
, msgmap
, agg
, role
);
7568 state
->next_screen
= prev_screen
;
7569 state
->redrawer
= redraw
;
7570 state
->mangled_screen
= 1;
7576 case 'z' : /* default */
7577 q_status_message(SM_INFO
, 0, 2,
7578 "Cancelled, there is no default command");
7590 * Select by message number ranges.
7591 * Sets searched bits in mail_elts
7593 * Args limitsrch -- limit search to this searchset
7595 * Returns 0 on success.
7598 select_by_number(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, SEARCHSET
**limitsrch
)
7602 char number1
[16], number2
[16], numbers
[80], *p
, *t
;
7607 ps_global
->mangled_footer
= 1;
7610 int flags
= OE_APPEND_CURRENT
;
7612 r
= optionally_enter(numbers
, -FOOTER_ROWS(ps_global
), 0,
7613 sizeof(numbers
), _(select_num
), NULL
, help
, &flags
);
7618 help
= (help
== NO_HELP
) ? h_select_by_num
: NO_HELP
;
7622 for(t
= p
= numbers
; *p
; p
++) /* strip whitespace */
7623 if(!isspace((unsigned char)*p
))
7628 if(r
== 1 || numbers
[0] == '\0'){
7629 cmd_cancelled("Selection by number");
7636 for(n1
= 1; n1
<= stream
->nmsgs
; n1
++)
7637 if((mc
= mail_elt(stream
, n1
)) != NULL
)
7638 mc
->searched
= 0; /* clear searched bits */
7640 for(p
= numbers
; *p
; p
++){
7642 while(*p
&& isdigit((unsigned char)*p
))
7648 if(number1
[0] == '\0'){
7650 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
7651 _("Invalid number range, missing number before \"-\": %s"),
7655 else if(!strucmp("end", p
)){
7659 else if(!strucmp("$", p
)){
7668 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
7669 _("Invalid message number: %s"), numbers
);
7675 n1
= mn_get_total(msgmap
);
7677 n1
= mn_get_cur(msgmap
);
7678 else if((n1
= atol(number1
)) < 1L || n1
> mn_get_total(msgmap
)){
7679 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
7680 _("\"%s\" out of message number range"),
7687 while(*++p
&& isdigit((unsigned char)*p
))
7693 if(number2
[0] == '\0'){
7694 if(!strucmp("end", p
)){
7698 else if(!strucmp(p
, "$")){
7707 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
7708 _("Invalid number range, missing number after \"-\": %s"),
7715 n2
= mn_get_total(msgmap
);
7717 n2
= mn_get_cur(msgmap
);
7718 else if((n2
= atol(number2
)) < 1L || n2
> mn_get_total(msgmap
)){
7719 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
7720 _("\"%s\" out of message number range"),
7728 strncpy(t
, long2string(n1
), sizeof(t
));
7729 t
[sizeof(t
)-1] = '\0';
7730 q_status_message2(SM_ORDER
| SM_DING
, 0, 2,
7731 _("Invalid reverse message number range: %s-%s"),
7732 t
, long2string(n2
));
7736 for(;n1
<= n2
; n1
++){
7737 raw
= mn_m2raw(msgmap
, n1
);
7739 && (!(limitsrch
&& *limitsrch
)
7740 || in_searchset(*limitsrch
, (unsigned long) raw
)))
7741 mm_searched(stream
, raw
);
7745 raw
= mn_m2raw(msgmap
, n1
);
7747 && (!(limitsrch
&& *limitsrch
)
7748 || in_searchset(*limitsrch
, (unsigned long) raw
)))
7749 mm_searched(stream
, raw
);
7761 * Select by thread number ranges.
7762 * Sets searched bits in mail_elts
7764 * Args limitsrch -- limit search to this searchset
7766 * Returns 0 on success.
7769 select_by_thrd_number(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, SEARCHSET
**msgset
)
7773 char number1
[16], number2
[16], numbers
[80], *p
, *t
;
7775 PINETHRD_S
*thrd
= NULL
, *th
;
7779 ps_global
->mangled_footer
= 1;
7782 int flags
= OE_APPEND_CURRENT
;
7784 r
= optionally_enter(numbers
, -FOOTER_ROWS(ps_global
), 0,
7785 sizeof(numbers
), _(select_num
), NULL
, help
, &flags
);
7790 help
= (help
== NO_HELP
) ? h_select_by_thrdnum
: NO_HELP
;
7794 for(t
= p
= numbers
; *p
; p
++) /* strip whitespace */
7795 if(!isspace((unsigned char)*p
))
7800 if(r
== 1 || numbers
[0] == '\0'){
7801 cmd_cancelled("Selection by number");
7808 for(n1
= 1; n1
<= stream
->nmsgs
; n1
++)
7809 if((mc
= mail_elt(stream
, n1
)) != NULL
)
7810 mc
->searched
= 0; /* clear searched bits */
7812 for(p
= numbers
; *p
; p
++){
7814 while(*p
&& isdigit((unsigned char)*p
))
7820 if(number1
[0] == '\0'){
7822 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
7823 _("Invalid number range, missing number before \"-\": %s"),
7827 else if(!strucmp("end", p
)){
7831 else if(!strucmp(p
, "$")){
7840 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
7841 _("Invalid thread number: %s"), numbers
);
7847 n1
= msgmap
->max_thrdno
;
7849 th
= fetch_thread(stream
, mn_m2raw(msgmap
, mn_get_cur(msgmap
)));
7852 else if((n1
= atol(number1
)) < 1L || n1
> msgmap
->max_thrdno
){
7853 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
7854 _("\"%s\" out of thread number range"),
7862 while(*++p
&& isdigit((unsigned char)*p
))
7868 if(number2
[0] == '\0'){
7869 if(!strucmp("end", p
)){
7873 else if(!strucmp("$", p
)){
7882 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
7883 _("Invalid number range, missing number after \"-\": %s"),
7890 n2
= msgmap
->max_thrdno
;
7892 th
= fetch_thread(stream
, mn_m2raw(msgmap
, mn_get_cur(msgmap
)));
7895 else if((n2
= atol(number2
)) < 1L || n2
> msgmap
->max_thrdno
){
7896 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
7897 _("\"%s\" out of thread number range"),
7905 strncpy(t
, long2string(n1
), sizeof(t
));
7906 t
[sizeof(t
)-1] = '\0';
7907 q_status_message2(SM_ORDER
| SM_DING
, 0, 2,
7908 _("Invalid reverse message number range: %s-%s"),
7909 t
, long2string(n2
));
7913 for(;n1
<= n2
; n1
++){
7914 thrd
= find_thread_by_number(stream
, msgmap
, n1
, thrd
);
7917 set_search_bit_for_thread(stream
, thrd
, msgset
);
7921 thrd
= find_thread_by_number(stream
, msgmap
, n1
, NULL
);
7924 set_search_bit_for_thread(stream
, thrd
, msgset
);
7936 * Select by message dates.
7937 * Sets searched bits in mail_elts
7939 * Args limitsrch -- limit search to this searchset
7941 * Returns 0 on success.
7944 select_by_date(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, long int msgno
, SEARCHSET
**limitsrch
)
7946 int r
, we_cancel
= 0, when
= 0;
7947 char date
[100], defdate
[100], prompt
[128];
7948 time_t seldate
= time(0);
7949 struct tm
*seldate_tm
;
7952 static struct _tense
{
7957 {"were ", "SENT SINCE", " (inclusive)"},
7958 {"were ", "SENT BEFORE", " (exclusive)"},
7959 {"were ", "SENT ON", "" },
7960 {"", "ARRIVED SINCE", " (inclusive)"},
7961 {"", "ARRIVED BEFORE", " (exclusive)"},
7962 {"", "ARRIVED ON", "" }
7966 ps_global
->mangled_footer
= 1;
7970 * If talking to an old server, default to SINCE instead of
7971 * SENTSINCE, which was added later.
7973 if(is_imap_stream(stream
) && !modern_imap_stream(stream
))
7977 int flags
= OE_APPEND_CURRENT
;
7979 seldate_tm
= localtime(&seldate
);
7980 snprintf(defdate
, sizeof(defdate
), "%.2d-%.4s-%.4d", seldate_tm
->tm_mday
,
7981 month_abbrev(seldate_tm
->tm_mon
+ 1),
7982 seldate_tm
->tm_year
+ 1900);
7983 defdate
[sizeof(defdate
)-1] = '\0';
7984 snprintf(prompt
,sizeof(prompt
),"Select messages which %s%s%s [%s]: ",
7985 tense
[when
].preamble
, tense
[when
].range
,
7986 tense
[when
].scope
, defdate
);
7987 prompt
[sizeof(prompt
)-1] = '\0';
7988 r
= optionally_enter(date
,-FOOTER_ROWS(ps_global
), 0, sizeof(date
),
7989 prompt
, sel_date_opt
, help
, &flags
);
7992 cmd_cancelled("Selection by date");
7996 help
= (help
== NO_HELP
) ? h_select_date
: NO_HELP
;
8007 if(stream
&& (rawno
= mn_m2raw(msgmap
, msgno
)) > 0L
8008 && rawno
<= stream
->nmsgs
8009 && (mc
= mail_elt(stream
, rawno
))){
8011 /* cache not filled in yet? */
8015 if(stream
->dtb
&& stream
->dtb
->flags
& DR_NEWS
){
8017 ulong2string(mail_uid(stream
, rawno
)),
8019 seq
[sizeof(seq
)-1] = '\0';
8020 mail_fetch_overview(stream
, seq
, NULL
);
8023 strncpy(seq
, long2string(rawno
),
8025 seq
[sizeof(seq
)-1] = '\0';
8026 mail_fetch_fast(stream
, seq
, 0L);
8030 /* mail_date returns fixed field width date */
8031 mail_date(date
, mc
);
8038 case 12 : /* set default to PREVIOUS day */
8042 case 13 : /* set default to NEXT day */
8047 when
= (when
+1) % (sizeof(tense
) / sizeof(struct _tense
));
8054 removing_leading_white_space(date
);
8055 removing_trailing_white_space(date
);
8057 strncpy(date
, defdate
, sizeof(date
));
8058 date
[sizeof(date
)-1] = '\0';
8064 if((pgm
= mail_newsearchpgm()) != NULL
){
8066 short converted_date
;
8068 if(mail_parse_date(&elt
, (unsigned char *) date
)){
8069 converted_date
= mail_shortdate(elt
.year
, elt
.month
, elt
.day
);
8073 pgm
->sentsince
= converted_date
;
8076 pgm
->sentbefore
= converted_date
;
8079 pgm
->senton
= converted_date
;
8082 pgm
->since
= converted_date
;
8085 pgm
->before
= converted_date
;
8088 pgm
->on
= converted_date
;
8092 pgm
->msgno
= (limitsrch
? *limitsrch
: NULL
);
8094 if(ps_global
&& ps_global
->ttyo
){
8095 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
8096 ps_global
->mangled_footer
= 1;
8099 we_cancel
= busy_cue(_("Selecting"), NULL
, 1);
8101 pine_mail_search_full(stream
, NULL
, pgm
, SE_NOPREFETCH
| SE_FREE
);
8106 /* we know this was freed in mail_search, let caller know */
8111 mail_free_searchpgm(&pgm
);
8112 q_status_message1(SM_ORDER
, 3, 3,
8113 _("Invalid date entered: %s"), date
);
8123 * Select by searching in message headers or body.
8124 * Sets searched bits in mail_elts
8126 * Args limitsrch -- limit search to this searchset
8128 * Returns 0 on success.
8131 select_by_text(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, long int msgno
, SEARCHSET
**limitsrch
)
8133 int r
, ku
, type
, we_cancel
= 0, flags
, rv
, ekeyi
= 0;
8134 int not = 0, me
= 0;
8135 char sstring
[80], savedsstring
[80], tmp
[128];
8136 char *p
, *sval
= NULL
;
8137 char buftmp
[MAILTMPLEN
], namehdr
[80];
8139 ENVELOPE
*env
= NULL
;
8141 unsigned flagsforhist
= 0;
8142 static HISTORY_S
*history
= NULL
;
8143 static char *recip
= "RECIPIENTS";
8144 static char *partic
= "PARTICIPANTS";
8145 static char *match_me
= N_("[Match_My_Addresses]");
8146 static char *dont_match_me
= N_("[Don't_Match_My_Addresses]");
8148 if(stream
== NULL
&& msgmap
== NULL
&& msgno
== 0 && limitsrch
== NULL
){
8150 free_hist(&history
);
8154 ps_global
->mangled_footer
= 1;
8155 savedsstring
[0] = '\0';
8156 ekey
[0].ch
= ekey
[1].ch
= ekey
[2].ch
= ekey
[3].ch
= -1;
8159 type
= radio_buttons(not ? _(sel_text_not
) : _(sel_text
),
8160 -FOOTER_ROWS(ps_global
), sel_text_opt
,
8161 's', 'x', NO_HELP
, RB_NORM
|RB_RET_HELP
);
8166 helper(h_select_text
, "HELP FOR SELECT BASED ON CONTENTS",
8168 ps_global
->mangled_screen
= 1;
8175 * prepare some friendly defaults...
8178 case 't' : /* address fields, offer To or From */
8183 sval
= (type
== 't') ? "TO" :
8184 (type
== 'f') ? "FROM" :
8185 (type
== 'c') ? "CC" :
8186 (type
== 'r') ? recip
: partic
;
8187 ekey
[ekeyi
].ch
= ctrl('T');
8188 ekey
[ekeyi
].name
= "^T";
8189 ekey
[ekeyi
].rval
= 10;
8190 /* TRANSLATORS: use Current To Address */
8191 ekey
[ekeyi
++].label
= N_("Cur To");
8192 ekey
[ekeyi
].ch
= ctrl('R');
8193 ekey
[ekeyi
].name
= "^R";
8194 ekey
[ekeyi
].rval
= 11;
8195 /* TRANSLATORS: use Current From Address */
8196 ekey
[ekeyi
++].label
= N_("Cur From");
8197 ekey
[ekeyi
].ch
= ctrl('W');
8198 ekey
[ekeyi
].name
= "^W";
8199 ekey
[ekeyi
].rval
= 12;
8200 /* TRANSLATORS: use Current Cc Address */
8201 ekey
[ekeyi
++].label
= N_("Cur Cc");
8202 ekey
[ekeyi
].ch
= ctrl('Y');
8203 ekey
[ekeyi
].name
= "^Y";
8204 ekey
[ekeyi
].rval
= 13;
8205 /* TRANSLATORS: Match Me means match my address */
8206 ekey
[ekeyi
++].label
= N_("Match Me");
8208 ekey
[ekeyi
].name
= "";
8209 ekey
[ekeyi
].rval
= 0;
8210 ekey
[ekeyi
++].label
= "";
8215 ekey
[ekeyi
].ch
= ctrl('X');
8216 ekey
[ekeyi
].name
= "^X";
8217 ekey
[ekeyi
].rval
= 14;
8218 /* TRANSLATORS: use Current Subject */
8219 ekey
[ekeyi
++].label
= N_("Cur Subject");
8231 strncpy(tmp
, "Name of HEADER to match : ", sizeof(tmp
)-1);
8232 tmp
[sizeof(tmp
)-1] = '\0';
8233 flags
= OE_APPEND_CURRENT
;
8239 r
= optionally_enter(namehdr
, -FOOTER_ROWS(ps_global
), 0,
8240 sizeof(namehdr
), tmp
, ekey
, NO_HELP
, &flags
);
8242 cmd_cancelled("Selection by text");
8245 removing_leading_white_space(namehdr
);
8247 while ((namehdr
[0] != '\0') && /* remove trailing ":" */
8248 (namehdr
[strlen(namehdr
) - 1] == ':'))
8249 namehdr
[strlen(namehdr
) - 1] = '\0';
8250 if ((namehdr
[0] != '\0')
8251 && isspace((unsigned char) namehdr
[strlen(namehdr
) - 1]))
8252 removing_trailing_white_space(namehdr
);
8256 if (strchr(namehdr
,' ') || strchr(namehdr
,'\t') ||
8257 strchr(namehdr
,':'))
8259 if (namehdr
[0] == '\0')
8269 dprint((1,"\n - BOTCH: select_text unrecognized option\n"));
8273 ekey
[ekeyi
].ch
= KEY_UP
;
8274 ekey
[ekeyi
].rval
= 30;
8275 ekey
[ekeyi
].name
= "";
8277 ekey
[ekeyi
++].label
= "";
8279 ekey
[ekeyi
].ch
= KEY_DOWN
;
8280 ekey
[ekeyi
].rval
= 31;
8281 ekey
[ekeyi
].name
= "";
8282 ekey
[ekeyi
++].label
= "";
8284 ekey
[ekeyi
].ch
= -1;
8288 init_hist(&history
, HISTSIZE
);
8290 if(ekey
[0].ch
> -1 && msgno
> 0L
8291 && !(env
=pine_mail_fetchstructure(stream
,mn_m2raw(msgmap
,msgno
),
8300 /* TRANSLATORS: character String in message <message number> to NOT match : " */
8301 snprintf(tmp
, sizeof(tmp
), "String in message %s to NOT match : ", sval
);
8303 snprintf(tmp
, sizeof(tmp
), "String in message %s to match : ", sval
);
8305 if(items_in_hist(history
) > 0){
8306 ekey
[ku
].name
= HISTORY_UP_KEYNAME
;
8307 ekey
[ku
].label
= HISTORY_KEYLABEL
;
8308 ekey
[ku
+1].name
= HISTORY_DOWN_KEYNAME
;
8309 ekey
[ku
+1].label
= HISTORY_KEYLABEL
;
8313 ekey
[ku
].label
= "";
8314 ekey
[ku
+1].name
= "";
8315 ekey
[ku
+1].label
= "";
8318 flags
= OE_APPEND_CURRENT
| OE_KEEP_TRAILING_SPACE
;
8319 r
= optionally_enter(sstring
, -FOOTER_ROWS(ps_global
), 0,
8320 79, tmp
, ekey
, help
, &flags
);
8322 if(me
&& r
== 0 && ((!not && strcmp(sstring
, _(match_me
))) || (not && strcmp(sstring
, _(dont_match_me
)))))
8327 help
= (help
== NO_HELP
)
8329 ? ((type
== 'f') ? h_select_txt_not_from
8330 : (type
== 't') ? h_select_txt_not_to
8331 : (type
== 'c') ? h_select_txt_not_cc
8332 : (type
== 's') ? h_select_txt_not_subj
8333 : (type
== 'a') ? h_select_txt_not_all
8334 : (type
== 'r') ? h_select_txt_not_recip
8335 : (type
== 'p') ? h_select_txt_not_partic
8336 : (type
== 'b') ? h_select_txt_not_body
8338 : ((type
== 'f') ? h_select_txt_from
8339 : (type
== 't') ? h_select_txt_to
8340 : (type
== 'c') ? h_select_txt_cc
8341 : (type
== 's') ? h_select_txt_subj
8342 : (type
== 'a') ? h_select_txt_all
8343 : (type
== 'r') ? h_select_txt_recip
8344 : (type
== 'p') ? h_select_txt_partic
8345 : (type
== 'b') ? h_select_txt_body
8352 case 10 : /* To: default */
8353 if(env
&& env
->to
&& env
->to
->mailbox
){
8354 snprintf(sstring
, sizeof(sstring
), "%s%s%s", env
->to
->mailbox
,
8355 env
->to
->host
? "@" : "",
8356 env
->to
->host
? env
->to
->host
: "");
8357 sstring
[sizeof(sstring
)-1] = '\0';
8361 case 11 : /* From: default */
8362 if(env
&& env
->from
&& env
->from
->mailbox
){
8363 snprintf(sstring
, sizeof(sstring
), "%s%s%s", env
->from
->mailbox
,
8364 env
->from
->host
? "@" : "",
8365 env
->from
->host
? env
->from
->host
: "");
8366 sstring
[sizeof(sstring
)-1] = '\0';
8370 case 12 : /* Cc: default */
8371 if(env
&& env
->cc
&& env
->cc
->mailbox
){
8372 snprintf(sstring
, sizeof(sstring
), "%s%s%s", env
->cc
->mailbox
,
8373 env
->cc
->host
? "@" : "",
8374 env
->cc
->host
? env
->cc
->host
: "");
8375 sstring
[sizeof(sstring
)-1] = '\0';
8379 case 13 : /* Match my addresses */
8381 snprintf(sstring
, sizeof(sstring
), "%s", not ? _(dont_match_me
) : _(match_me
));
8384 case 14 : /* Subject: default */
8385 if(env
&& env
->subject
&& env
->subject
[0]){
8388 snprintf(buftmp
, sizeof(buftmp
), "%.75s", env
->subject
);
8389 buftmp
[sizeof(buftmp
)-1] = '\0';
8390 q
= (char *) rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
,
8391 SIZEOF_20KBUF
, buftmp
);
8392 if(q
!= env
->subject
){
8393 snprintf(savedsstring
, sizeof(savedsstring
), "%.70s", q
);
8394 savedsstring
[sizeof(savedsstring
)-1] = '\0';
8397 snprintf(sstring
, sizeof(sstring
), "%s", q
);
8398 sstring
[sizeof(sstring
)-1] = '\0';
8404 flagsforhist
= (not ? 0x1 : 0) + (me
? 0x2 : 0);
8405 if((p
= get_prev_hist(history
, sstring
, flagsforhist
, NULL
)) != NULL
){
8406 strncpy(sstring
, p
, sizeof(sstring
));
8407 sstring
[sizeof(sstring
)-1] = '\0';
8408 if(history
->hist
[history
->curindex
]){
8409 flagsforhist
= history
->hist
[history
->curindex
]->flags
;
8410 not = (flagsforhist
& 0x1) ? 1 : 0;
8411 me
= (flagsforhist
& 0x2) ? 1 : 0;
8420 flagsforhist
= (not ? 0x1 : 0) + (me
? 0x2 : 0);
8421 if((p
= get_next_hist(history
, sstring
, flagsforhist
, NULL
)) != NULL
){
8422 strncpy(sstring
, p
, sizeof(sstring
));
8423 sstring
[sizeof(sstring
)-1] = '\0';
8424 if(history
->hist
[history
->curindex
]){
8425 flagsforhist
= history
->hist
[history
->curindex
]->flags
;
8426 not = (flagsforhist
& 0x1) ? 1 : 0;
8427 me
= (flagsforhist
& 0x2) ? 1 : 0;
8439 if(r
== 1 || sstring
[0] == '\0')
8446 if(type
== 'x' || r
== 'x'){
8447 cmd_cancelled("Selection by text");
8451 if(ps_global
&& ps_global
->ttyo
){
8452 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
8453 ps_global
->mangled_footer
= 1;
8456 we_cancel
= busy_cue(_("Selecting"), NULL
, 1);
8458 flagsforhist
= (not ? 0x1 : 0) + (me
? 0x2 : 0);
8459 save_hist(history
, sstring
, flagsforhist
, NULL
);
8461 rv
= agg_text_select(stream
, msgmap
, type
, namehdr
, not, me
, sstring
, "utf-8", limitsrch
);
8470 * Select by message size.
8471 * Sets searched bits in mail_elts
8473 * Args limitsrch -- limit search to this searchset
8475 * Returns 0 on success.
8478 select_by_size(MAILSTREAM
*stream
, SEARCHSET
**limitsrch
)
8480 int r
, large
= 1, we_cancel
= 0;
8481 unsigned long n
, mult
= 1L, numerator
= 0L, divisor
= 1L;
8482 char size
[16], numbers
[80], *p
, *t
;
8485 long flags
= (SE_NOPREFETCH
| SE_FREE
);
8488 ps_global
->mangled_footer
= 1;
8492 int flgs
= OE_APPEND_CURRENT
;
8494 sel_size_opt
[1].label
= large
? sel_size_smaller
: sel_size_larger
;
8496 r
= optionally_enter(numbers
, -FOOTER_ROWS(ps_global
), 0,
8497 sizeof(numbers
), large
? _(select_size_larger_msg
)
8498 : _(select_size_smaller_msg
),
8499 sel_size_opt
, help
, &flgs
);
8509 help
= (help
== NO_HELP
) ? (large
? h_select_by_larger_size
8510 : h_select_by_smaller_size
)
8515 for(t
= p
= numbers
; *p
; p
++) /* strip whitespace */
8516 if(!isspace((unsigned char)*p
))
8521 if(r
== 1 || numbers
[0] == '\0'){
8522 cmd_cancelled("Selection by size");
8529 if(numbers
[0] == '-'){
8530 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
8531 _("Invalid size entered: %s"), numbers
);
8538 while(*p
&& isdigit((unsigned char)*p
))
8543 if(size
[0] == '\0' && *p
== '.' && isdigit(*(p
+1))){
8548 if(size
[0] == '\0'){
8549 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
8550 _("Invalid size entered: %s"), numbers
);
8554 n
= strtoul(size
, (char **)NULL
, 10);
8559 * We probably ought to just use atof() to convert 1.1 into a
8560 * double, but since we haven't used atof() anywhere else I'm
8561 * reluctant to use it because of portability concerns.
8565 while(*p
&& isdigit((unsigned char)*p
)){
8573 numerator
= strtoul(size
, (char **)NULL
, 10);
8593 n
= n
* mult
+ (numerator
* mult
) / divisor
;
8595 pgm
= mail_newsearchpgm();
8601 if(is_imap_stream(stream
) && !modern_imap_stream(stream
))
8602 flags
|= SE_NOSERVER
;
8604 if(ps_global
&& ps_global
->ttyo
){
8605 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
8606 ps_global
->mangled_footer
= 1;
8609 we_cancel
= busy_cue(_("Selecting"), NULL
, 1);
8611 pgm
->msgno
= (limitsrch
? *limitsrch
: NULL
);
8612 pine_mail_search_full(stream
, NULL
, pgm
, SE_NOPREFETCH
| SE_FREE
);
8613 /* we know this was freed in mail_search, let caller know */
8625 * visible_searchset -- return c-client search set unEXLDed
8629 visible_searchset(MAILSTREAM
*stream
, MSGNO_S
*msgmap
)
8632 SEARCHSET
*full_set
= NULL
, **set
;
8635 * If we're talking to anything other than a server older than
8636 * imap 4rev1, build a searchset otherwise it'll choke.
8638 if(!(is_imap_stream(stream
) && !modern_imap_stream(stream
))){
8639 if(any_lflagged(msgmap
, MN_EXLD
)){
8640 for(n
= 1L, set
= &full_set
, run
= 0L; n
<= stream
->nmsgs
; n
++)
8641 if(get_lflag(stream
, NULL
, n
, MN_EXLD
)){
8642 if(run
){ /* previous NOT excluded? */
8644 (*set
)->last
= n
- 1L;
8646 set
= &(*set
)->next
;
8650 else if(run
++){ /* next in run */
8653 else{ /* start of run */
8654 *set
= mail_newsearchset();
8659 full_set
= mail_newsearchset();
8660 full_set
->first
= 1L;
8661 full_set
->last
= stream
->nmsgs
;
8670 * Select by message status bits.
8671 * Sets searched bits in mail_elts
8673 * Args limitsrch -- limit search to this searchset
8675 * Returns 0 on success.
8678 select_by_status(MAILSTREAM
*stream
, SEARCHSET
**limitsrch
)
8680 int s
, not = 0, we_cancel
= 0, rv
;
8683 s
= radio_buttons((not) ? _(sel_flag_not
) : _(sel_flag
),
8684 -FOOTER_ROWS(ps_global
), sel_flag_opt
, '*', 'x',
8685 NO_HELP
, RB_NORM
|RB_RET_HELP
);
8688 cmd_cancelled("Selection by status");
8692 helper(h_select_status
, _("HELP FOR SELECT BASED ON STATUS"),
8694 ps_global
->mangled_screen
= 1;
8702 if(ps_global
&& ps_global
->ttyo
){
8703 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
8704 ps_global
->mangled_footer
= 1;
8707 we_cancel
= busy_cue(_("Selecting"), NULL
, 1);
8708 rv
= agg_flag_select(stream
, not, s
, limitsrch
);
8717 * Select by rule. Usually srch, indexcolor, and roles would be most useful.
8718 * Sets searched bits in mail_elts
8720 * Args limitsrch -- limit search to this searchset
8722 * Returns 0 on success.
8725 select_by_rule(MAILSTREAM
*stream
, SEARCHSET
**limitsrch
)
8727 char rulenick
[1000], *nick
;
8729 int r
, not = 0, we_cancel
= 0, rflags
= ROLE_DO_SRCH
8737 ps_global
->mangled_footer
= 1;
8742 oe_flags
= OE_APPEND_CURRENT
;
8743 r
= optionally_enter(rulenick
, -FOOTER_ROWS(ps_global
), 0,
8745 not ? _("Rule to NOT match: ")
8746 : _("Rule to match: "),
8747 sel_key_opt
, NO_HELP
, &oe_flags
);
8750 /* select rulenick from a list */
8751 if((nick
=choose_a_rule(rflags
)) != NULL
){
8752 strncpy(rulenick
, nick
, sizeof(rulenick
)-1);
8753 rulenick
[sizeof(rulenick
)-1] = '\0';
8754 fs_give((void **) &nick
);
8763 helper(h_select_rule
, _("HELP FOR SELECT BY RULE"), HLPD_SIMPLE
);
8764 ps_global
->mangled_screen
= 1;
8767 cmd_cancelled("Selection by Rule");
8771 removing_leading_and_trailing_white_space(rulenick
);
8773 }while(r
== 3 || r
== 4 || r
== '!');
8777 * The approach of requiring a nickname instead of just allowing the
8778 * user to select from the list of rules has the drawback that a rule
8779 * may not have a nickname, or there may be more than one rule with
8780 * the same nickname. However, it has the benefit of allowing the user
8781 * to type in the nickname and, most importantly, allows us to set
8782 * up the ! (not). We could incorporate the ! into the selection
8783 * screen, but this is easier and also allows the typing of nicks.
8784 * User can just set up nicknames if they want to use this feature.
8786 patgrp
= nick_to_patgrp(rulenick
, rflags
);
8789 if(ps_global
&& ps_global
->ttyo
){
8790 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
8791 ps_global
->mangled_footer
= 1;
8794 we_cancel
= busy_cue(_("Selecting"), NULL
, 1);
8795 match_pattern(patgrp
, stream
, limitsrch
? *limitsrch
: 0, NULL
,
8797 (not ? MP_NOT
: 0) | SE_NOPREFETCH
);
8798 free_patgrp(&patgrp
);
8803 if(limitsrch
&& *limitsrch
){
8804 mail_free_searchset(limitsrch
);
8813 * Allow user to choose a rule from their list of rules.
8815 * Returns an allocated rule nickname on success, NULL otherwise.
8818 choose_a_rule(int rflags
)
8820 char *choice
= NULL
;
8821 char **rule_list
, **lp
;
8826 if(!(nonempty_patterns(rflags
, &pstate
) && first_pattern(&pstate
))){
8827 q_status_message(SM_ORDER
, 3, 3,
8828 _("No rules available. Use Setup/Rules to add some."));
8833 * Build a list of rules to choose from.
8836 for(pat
= first_pattern(&pstate
); pat
; pat
= next_pattern(&pstate
))
8840 q_status_message(SM_ORDER
, 3, 4, _("No rules defined, use Setup/Rules"));
8844 lp
= rule_list
= (char **) fs_get((cnt
+ 1) * sizeof(*rule_list
));
8845 memset(rule_list
, 0, (cnt
+1) * sizeof(*rule_list
));
8847 for(pat
= first_pattern(&pstate
); pat
; pat
= next_pattern(&pstate
))
8848 *lp
++ = cpystr((pat
->patgrp
&& pat
->patgrp
->nick
)
8849 ? pat
->patgrp
->nick
: "?");
8851 /* TRANSLATORS: SELECT A RULE is a screen title
8852 TRANSLATORS: Print something1 using something2.
8853 "rules" is something1 */
8854 choice
= choose_item_from_list(rule_list
, NULL
, _("SELECT A RULE"),
8855 _("rules"), h_select_rule_screen
,
8856 _("HELP FOR SELECTING A RULE NICKNAME"), NULL
);
8859 q_status_message(SM_ORDER
, 1, 4, "No choice");
8861 free_list_array(&rule_list
);
8868 * Select by current thread.
8869 * Sets searched bits in mail_elts for this entire thread
8871 * Args limitsrch -- limit search to this searchset
8873 * Returns 0 on success.
8876 select_by_thread(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, SEARCHSET
**limitsrch
)
8879 PINETHRD_S
*thrd
= NULL
;
8886 for(n
= 1L; n
<= stream
->nmsgs
; n
++)
8887 if((mc
= mail_elt(stream
, n
)) != NULL
)
8888 mc
->searched
= 0; /* clear searched bits */
8890 thrd
= fetch_thread(stream
, mn_m2raw(msgmap
, mn_get_cur(msgmap
)));
8891 if(thrd
&& thrd
->top
&& thrd
->top
!= thrd
->rawno
)
8892 thrd
= fetch_thread(stream
, thrd
->top
);
8895 * This doesn't unselect if the thread is already selected
8896 * (like select current does), it always selects.
8897 * There is no way to select ! this thread.
8900 set_search_bit_for_thread(stream
, thrd
, limitsrch
);
8909 * Select by message keywords.
8910 * Sets searched bits in mail_elts
8912 * Args limitsrch -- limit search to this searchset
8914 * Returns 0 on success.
8917 select_by_keyword(MAILSTREAM
*stream
, SEARCHSET
**limitsrch
)
8919 int r
, not = 0, we_cancel
= 0;
8920 char keyword
[MAXUSERFLAG
+1], *kword
;
8921 char *error
= NULL
, *p
, *prompt
;
8926 ps_global
->mangled_footer
= 1;
8933 q_status_message(SM_ORDER
, 3, 4, error
);
8934 fs_give((void **) &error
);
8937 if(F_ON(F_FLAG_SCREEN_KW_SHORTCUT
, ps_global
) && ps_global
->keywords
){
8939 prompt
= _("Keyword (or keyword initial) to NOT match: ");
8941 prompt
= _("Keyword (or keyword initial) to match: ");
8945 prompt
= _("Keyword to NOT match: ");
8947 prompt
= _("Keyword to match: ");
8950 oe_flags
= OE_APPEND_CURRENT
;
8951 r
= optionally_enter(keyword
, -FOOTER_ROWS(ps_global
), 0,
8953 prompt
, sel_key_opt
, help
, &oe_flags
);
8956 /* select keyword from a list */
8957 if((kword
=choose_a_keyword()) != NULL
){
8958 strncpy(keyword
, kword
, sizeof(keyword
)-1);
8959 keyword
[sizeof(keyword
)-1] = '\0';
8960 fs_give((void **) &kword
);
8969 help
= help
== NO_HELP
? h_select_keyword
: NO_HELP
;
8971 cmd_cancelled("Selection by keyword");
8975 removing_leading_and_trailing_white_space(keyword
);
8977 }while(r
== 3 || r
== 4 || r
== '!' || keyword_check(keyword
, &error
));
8980 if(F_ON(F_FLAG_SCREEN_KW_SHORTCUT
, ps_global
) && ps_global
->keywords
){
8981 p
= initial_to_keyword(keyword
);
8983 strncpy(keyword
, p
, sizeof(keyword
)-1);
8984 keyword
[sizeof(keyword
)-1] = '\0';
8989 * We want to check the keyword, not the nickname of the keyword,
8990 * so convert it to the keyword if necessary.
8992 p
= nick_to_keyword(keyword
);
8994 strncpy(keyword
, p
, sizeof(keyword
)-1);
8995 keyword
[sizeof(keyword
)-1] = '\0';
8998 pgm
= mail_newsearchpgm();
9000 pgm
->unkeyword
= mail_newstringlist();
9001 pgm
->unkeyword
->text
.data
= (unsigned char *) cpystr(keyword
);
9002 pgm
->unkeyword
->text
.size
= strlen(keyword
);
9005 pgm
->keyword
= mail_newstringlist();
9006 pgm
->keyword
->text
.data
= (unsigned char *) cpystr(keyword
);
9007 pgm
->keyword
->text
.size
= strlen(keyword
);
9010 if(ps_global
&& ps_global
->ttyo
){
9011 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
9012 ps_global
->mangled_footer
= 1;
9015 we_cancel
= busy_cue(_("Selecting"), NULL
, 1);
9017 pgm
->msgno
= (limitsrch
? *limitsrch
: NULL
);
9018 pine_mail_search_full(stream
, "UTF-8", pgm
, SE_NOPREFETCH
| SE_FREE
);
9019 /* we know this was freed in mail_search, let caller know */
9031 * Allow user to choose a keyword from their list of keywords.
9033 * Returns an allocated keyword on success, NULL otherwise.
9036 choose_a_keyword(void)
9038 char *choice
= NULL
;
9039 char **keyword_list
, **lp
;
9044 * Build a list of keywords to choose from.
9047 for(cnt
= 0, kw
= ps_global
->keywords
; kw
; kw
= kw
->next
)
9051 q_status_message(SM_ORDER
, 3, 4,
9052 _("No keywords defined, use \"keywords\" option in Setup/Config"));
9056 lp
= keyword_list
= (char **) fs_get((cnt
+ 1) * sizeof(*keyword_list
));
9057 memset(keyword_list
, 0, (cnt
+1) * sizeof(*keyword_list
));
9059 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
)
9060 *lp
++ = cpystr(kw
->nick
? kw
->nick
: kw
->kw
? kw
->kw
: "");
9062 /* TRANSLATORS: SELECT A KEYWORD is a screen title
9063 TRANSLATORS: Print something1 using something2.
9064 "keywords" is something1 */
9065 choice
= choose_item_from_list(keyword_list
, NULL
, _("SELECT A KEYWORD"),
9066 _("keywords"), h_select_keyword_screen
,
9067 _("HELP FOR SELECTING A KEYWORD"), NULL
);
9070 q_status_message(SM_ORDER
, 1, 4, "No choice");
9072 free_list_array(&keyword_list
);
9079 * Allow user to choose a list of keywords from their list of keywords.
9081 * Returns allocated list.
9084 choose_list_of_keywords(void)
9086 LIST_SEL_S
*listhead
, *ls
, *p
;
9092 * Build a list of keywords to choose from.
9095 p
= listhead
= NULL
;
9096 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
){
9098 ls
= (LIST_SEL_S
*) fs_get(sizeof(*ls
));
9099 memset(ls
, 0, sizeof(*ls
));
9100 ls
->item
= cpystr(kw
->nick
? kw
->nick
: kw
->kw
? kw
->kw
: "");
9113 /* TRANSLATORS: SELECT KEYWORDS is a screen title
9114 Print something1 using something2.
9115 "keywords" is something1 */
9116 if(!select_from_list_screen(listhead
, SFL_ALLOW_LISTMODE
,
9117 _("SELECT KEYWORDS"), _("keywords"),
9118 h_select_multkeyword_screen
,
9119 _("HELP FOR SELECTING KEYWORDS"), NULL
)){
9120 for(cnt
= 0, p
= listhead
; p
; p
= p
->next
)
9124 ret
= (char **) fs_get((cnt
+1) * sizeof(*ret
));
9125 memset(ret
, 0, (cnt
+1) * sizeof(*ret
));
9126 for(i
= 0, p
= listhead
; p
; p
= p
->next
)
9128 ret
[i
++] = cpystr(p
->item
? p
->item
: "");
9131 free_list_sel(&listhead
);
9138 * Allow user to choose a charset
9140 * Returns an allocated charset on success, NULL otherwise.
9143 choose_a_charset(int which_charsets
)
9145 char *choice
= NULL
;
9146 char **charset_list
, **lp
;
9151 * Build a list of charsets to choose from.
9154 for(cnt
= 0, cs
= utf8_charset(NIL
); cs
&& cs
->name
; cs
++){
9155 if(!(cs
->flags
& (CF_UNSUPRT
|CF_NOEMAIL
))
9156 && ((which_charsets
& CAC_ALL
)
9157 || (which_charsets
& CAC_POSTING
9158 && cs
->flags
& CF_POSTING
)
9159 || (which_charsets
& CAC_DISPLAY
9160 && cs
->type
!= CT_2022
9161 && (cs
->flags
& (CF_PRIMARY
|CF_DISPLAY
)) == (CF_PRIMARY
|CF_DISPLAY
))))
9166 q_status_message(SM_ORDER
, 3, 4,
9167 _("No charsets found? Enter charset manually."));
9171 lp
= charset_list
= (char **) fs_get((cnt
+ 1) * sizeof(*charset_list
));
9172 memset(charset_list
, 0, (cnt
+1) * sizeof(*charset_list
));
9174 for(cs
= utf8_charset(NIL
); cs
&& cs
->name
; cs
++){
9175 if(!(cs
->flags
& (CF_UNSUPRT
|CF_NOEMAIL
))
9176 && ((which_charsets
& CAC_ALL
)
9177 || (which_charsets
& CAC_POSTING
9178 && cs
->flags
& CF_POSTING
)
9179 || (which_charsets
& CAC_DISPLAY
9180 && cs
->type
!= CT_2022
9181 && (cs
->flags
& (CF_PRIMARY
|CF_DISPLAY
)) == (CF_PRIMARY
|CF_DISPLAY
))))
9182 *lp
++ = cpystr(cs
->name
);
9185 /* TRANSLATORS: SELECT A CHARACTER SET is a screen title
9186 TRANSLATORS: Print something1 using something2.
9187 "character sets" is something1 */
9188 choice
= choose_item_from_list(charset_list
, NULL
, _("SELECT A CHARACTER SET"),
9189 _("character sets"), h_select_charset_screen
,
9190 _("HELP FOR SELECTING A CHARACTER SET"), NULL
);
9193 q_status_message(SM_ORDER
, 1, 4, "No choice");
9195 free_list_array(&charset_list
);
9202 * Allow user to choose a list of character sets and/or scripts
9204 * Returns allocated list.
9207 choose_list_of_charsets(void)
9209 LIST_SEL_S
*listhead
, *ls
, *p
;
9211 int cnt
, i
, got_one
;
9216 char buf
[1024], *folded
;
9219 * Build a list of charsets to choose from.
9222 p
= listhead
= NULL
;
9224 /* this width is determined by select_from_list_screen() */
9225 width
= ps_global
->ttyo
->screen_cols
- 4;
9227 /* first comes a list of scripts (sets of character sets) */
9228 for(s
= utf8_script(NIL
); s
&& s
->name
; s
++){
9230 limit
= sizeof(buf
)-1;
9232 memset(q
, 0, limit
+1);
9235 sstrncpy(&q
, s
->name
, limit
);
9238 sstrncpy(&q
, " (", limit
-(q
-buf
));
9239 sstrncpy(&q
, s
->description
, limit
-(q
-buf
));
9240 sstrncpy(&q
, ")", limit
-(q
-buf
));
9243 /* add the list of charsets that are in this script */
9245 for(cs
= utf8_charset(NIL
);
9246 cs
&& cs
->name
&& (q
-buf
) < limit
; cs
++){
9247 if(cs
->script
& s
->script
){
9249 * Filter out some un-useful members of the list.
9250 * UTF-7 and UTF-8 weren't actually in the list at the
9251 * time this was written. Just making sure.
9253 if(!strucmp(cs
->name
, "ISO-2022-JP-2")
9254 || !strucmp(cs
->name
, "UTF-7")
9255 || !strucmp(cs
->name
, "UTF-8"))
9259 sstrncpy(&q
, " ", limit
-(q
-buf
));
9262 sstrncpy(&q
, " {", limit
-(q
-buf
));
9265 sstrncpy(&q
, cs
->name
, limit
-(q
-buf
));
9270 sstrncpy(&q
, "}", limit
-(q
-buf
));
9272 /* fold this line so that it can all be seen on the screen */
9273 folded
= fold(buf
, width
, width
, "", " ", FLD_NONE
);
9276 while(t
&& *t
&& (q
= strindex(t
, '\n')) != NULL
){
9279 ls
= (LIST_SEL_S
*) fs_get(sizeof(*ls
));
9280 memset(ls
, 0, sizeof(*ls
));
9282 ls
->item
= cpystr(s
->name
);
9284 ls
->flags
= SFL_NOSELECT
;
9286 ls
->display_item
= cpystr(t
);
9296 listhead
= (LIST_SEL_S
*) fs_get(sizeof(*ls
));
9297 memset(listhead
, 0, sizeof(*listhead
));
9298 listhead
->flags
= SFL_NOSELECT
;
9299 listhead
->display_item
=
9300 cpystr(_("Scripts representing groups of related character sets"));
9301 listhead
->next
= (LIST_SEL_S
*) fs_get(sizeof(*ls
));
9302 memset(listhead
->next
, 0, sizeof(*listhead
));
9303 listhead
->next
->flags
= SFL_NOSELECT
;
9304 listhead
->next
->display_item
=
9305 cpystr(repeat_char(width
, '-'));
9307 listhead
->next
->next
= ls
;
9312 fs_give((void **) &folded
);
9316 ls
= (LIST_SEL_S
*) fs_get(sizeof(*ls
));
9317 memset(ls
, 0, sizeof(*ls
));
9318 ls
->flags
= SFL_NOSELECT
;
9326 ls
= (LIST_SEL_S
*) fs_get(sizeof(*ls
));
9327 memset(ls
, 0, sizeof(*ls
));
9328 ls
->flags
= SFL_NOSELECT
;
9330 cpystr(_("Individual character sets, may be mixed with scripts"));
9334 ls
= (LIST_SEL_S
*) fs_get(sizeof(*ls
));
9335 memset(ls
, 0, sizeof(*ls
));
9336 ls
->flags
= SFL_NOSELECT
;
9338 cpystr(repeat_char(width
, '-'));
9342 /* then comes a list of individual character sets */
9343 for(cs
= utf8_charset(NIL
); cs
&& cs
->name
; cs
++){
9344 ls
= (LIST_SEL_S
*) fs_get(sizeof(*ls
));
9345 memset(ls
, 0, sizeof(*ls
));
9346 ls
->item
= cpystr(cs
->name
);
9359 /* TRANSLATORS: SELECT CHARACTER SETS is a screen title
9360 Print something1 using something2.
9361 "character sets" is something1 */
9362 if(!select_from_list_screen(listhead
, SFL_ALLOW_LISTMODE
,
9363 _("SELECT CHARACTER SETS"), _("character sets"),
9364 h_select_multcharsets_screen
,
9365 _("HELP FOR SELECTING CHARACTER SETS"), NULL
)){
9366 for(cnt
= 0, p
= listhead
; p
; p
= p
->next
)
9370 ret
= (char **) fs_get((cnt
+1) * sizeof(*ret
));
9371 memset(ret
, 0, (cnt
+1) * sizeof(*ret
));
9372 for(i
= 0, p
= listhead
; p
; p
= p
->next
)
9374 ret
[i
++] = cpystr(p
->item
? p
->item
: "");
9377 free_list_sel(&listhead
);
9382 /* Report quota summary resources in an IMAP server */
9385 cmd_quota (struct pine
*state
)
9387 QUOTALIST
*imapquota
;
9392 if(!state
->mail_stream
|| !is_imap_stream(state
->mail_stream
)){
9393 q_status_message(SM_ORDER
, 1, 5, "Quota only available for IMAP folders");
9397 if (state
->mail_stream
9398 && !sp_dead_stream(state
->mail_stream
)
9399 && state
->mail_stream
->mailbox
9400 && *state
->mail_stream
->mailbox
9401 && mail_valid_net_parse(state
->mail_stream
->mailbox
, &mb
))
9402 imap_getquotaroot(state
->mail_stream
, mb
.mailbox
);
9404 if(!state
->quota
) /* failed ? */
9405 return; /* go back... */
9407 if(!(store
= so_get(CharStar
, NULL
, EDIT_ACCESS
))){
9408 q_status_message(SM_ORDER
| SM_DING
, 3, 3, "Error allocating space.");
9412 so_puts(store
, "Quota Report for ");
9413 so_puts(store
, state
->mail_stream
->original_mailbox
);
9414 so_puts(store
, "\n\n");
9416 for (imapquota
= state
->quota
; imapquota
; imapquota
= imapquota
->next
){
9418 so_puts(store
, _("Resource : "));
9419 so_puts(store
, imapquota
->name
);
9420 so_writec('\n', store
);
9422 so_puts(store
, _("Usage : "));
9423 so_puts(store
, long2string(imapquota
->usage
));
9424 if(!strucmp(imapquota
->name
,"STORAGE"))
9425 so_puts(store
, " KiB ");
9426 if(!strucmp(imapquota
->name
,"MESSAGE")){
9427 so_puts(store
, _(" message"));
9428 if(imapquota
->usage
!= 1)
9429 so_puts(store
, _("s ")); /* plural */
9431 so_puts(store
, _(" "));
9433 so_writec('(', store
);
9434 so_puts(store
, long2string(100*imapquota
->usage
/imapquota
->limit
));
9435 so_puts(store
, "%)\n");
9437 so_puts(store
, _("Limit : "));
9438 so_puts(store
, long2string(imapquota
->limit
));
9439 if(!strucmp(imapquota
->name
,"STORAGE"))
9440 so_puts(store
, " KiB\n\n");
9441 if(!strucmp(imapquota
->name
,"MESSAGE")){
9442 so_puts(store
, _(" message"));
9443 if(imapquota
->usage
!= 1)
9444 so_puts(store
, _("s\n\n")); /* plural */
9446 so_puts(store
, _("\n\n"));
9450 memset(&sargs
, 0, sizeof(SCROLL_S
));
9451 sargs
.text
.text
= so_text(store
);
9452 sargs
.text
.src
= CharStar
;
9453 sargs
.text
.desc
= _("Quota Resources Summary");
9454 sargs
.bar
.title
= _("QUOTA SUMMARY");
9455 sargs
.proc
.tool
= NULL
;
9456 sargs
.help
.text
= h_quota_command
;
9457 sargs
.help
.title
= NULL
;
9458 sargs
.keys
.menu
= NULL
;
9459 setbitmap(sargs
.keys
.bitmap
);
9465 mail_free_quotalist(&(state
->quota
));
9468 /*----------------------------------------------------------------------
9469 Prompt the user for the type of sort he desires
9471 Args: state -- pine state pointer
9472 q1 -- Line to prompt on
9474 Returns 0 if it was cancelled, 1 otherwise.
9477 select_sort(struct pine
*state
, int ql
, SortOrder
*sort
, int *rev
)
9479 char prompt
[200], tmp
[3], *p
;
9481 int deefault
= 'a', retval
= 1;
9486 DLG_SORTPARAM sortsel
;
9488 if (mswin_usedialog ()) {
9490 sortsel
.reverse
= mn_get_revsort (state
->msgmap
);
9491 sortsel
.cursort
= mn_get_sort (state
->msgmap
);
9492 /* assumption here that HelpType is char ** */
9493 sortsel
.helptext
= h_select_sort
;
9496 if ((retval
= os_sortdialog (&sortsel
))) {
9497 *sort
= sortsel
.cursort
;
9498 *rev
= sortsel
.reverse
;
9505 /*----- String together the prompt ------*/
9507 if(F_ON(F_USE_FK
,ps_global
))
9508 strncpy(prompt
, _("Choose type of sort : "), sizeof(prompt
));
9510 strncpy(prompt
, _("Choose type of sort, or 'R' to reverse current sort : "),
9513 for(i
= 0; state
->sort_types
[i
] != EndofList
; i
++) {
9515 p
= sorts
[i
].label
= sort_name(state
->sort_types
[i
]);
9516 while(*(p
+1) && islower((unsigned char)*p
))
9519 sorts
[i
].ch
= tolower((unsigned char)(tmp
[0] = *p
));
9520 sorts
[i
].name
= cpystr(tmp
);
9522 if(mn_get_sort(state
->msgmap
) == state
->sort_types
[i
])
9523 deefault
= sorts
[i
].rval
;
9527 sorts
[i
].rval
= 'r';
9528 sorts
[i
].name
= cpystr("R");
9529 if(F_ON(F_USE_FK
,ps_global
))
9530 sorts
[i
].label
= N_("Reverse");
9532 sorts
[i
].label
= "";
9535 help
= h_select_sort
;
9537 if((F_ON(F_USE_FK
,ps_global
)
9538 && ((s
= double_radio_buttons(prompt
,ql
,sorts
,deefault
,'x',
9539 help
,RB_NORM
)) != 'x'))
9541 (F_OFF(F_USE_FK
,ps_global
)
9542 && ((s
= radio_buttons(prompt
,ql
,sorts
,deefault
,'x',
9543 help
,RB_NORM
)) != 'x'))){
9544 state
->mangled_body
= 1; /* signal screen's changed */
9546 *rev
= !mn_get_revsort(state
->msgmap
);
9548 *sort
= state
->sort_types
[s
];
9550 if(F_ON(F_SHOW_SORT
, ps_global
))
9551 ps_global
->mangled_header
= 1;
9555 cmd_cancelled("Sort");
9559 fs_give((void **)&sorts
[i
].name
);
9561 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
9566 /*---------------------------------------------------------------------
9567 Build list of folders in the given context for user selection
9569 Args: c -- pointer to pointer to folder's context context
9570 f -- folder prefix to display
9571 sublist -- whether or not to use 'f's contents as prefix
9572 lister -- function used to do the actual display
9574 Returns: malloc'd string containing sequence, else NULL if
9575 no messages in msgmap with local "selected" flag.
9578 display_folder_list(CONTEXT_S
**c
, char *f
, int sublist
, int (*lister
) (struct pine
*, CONTEXT_S
**, char *, int))
9582 void (*redraw
)(void) = ps_global
->redrawer
;
9584 push_titlebar_state();
9586 if((rc
= (*lister
)(ps_global
, &tc
, f
, sublist
)) != 0)
9590 pop_titlebar_state();
9592 if((ps_global
->redrawer
= redraw
) != NULL
) /* reset old value, and test */
9593 (*ps_global
->redrawer
)();
9595 if(rc
== 1 && F_ON(F_SELECT_WO_CONFIRM
, ps_global
))
9603 * Allow user to choose a single item from a list of strings.
9605 * Args list -- Array of strings to choose from, NULL terminated.
9606 * displist -- Array of strings to display instead of displaying list.
9607 * Indices correspond to the list array. Display the displist
9608 * but return the item from list if displist non-NULL.
9609 * title -- For conf_scroll_screen
9610 * pdesc -- For conf_scroll_screen
9611 * help -- For conf_scroll_screen
9612 * htitle -- For conf_scroll_screen
9614 * Returns an allocated copy of the chosen item or NULL.
9617 choose_item_from_list(char **list
, char **displist
, char *title
, char *pdesc
, HelpType help
,
9618 char *htitle
, char *cursor_location
)
9620 LIST_SEL_S
*listhead
, *ls
, *p
, *starting_val
= NULL
;
9622 char *ret
= NULL
, *choice
= NULL
;
9624 /* build the LIST_SEL_S list */
9625 p
= listhead
= NULL
;
9626 for(t
= list
, dl
= displist
; *t
; t
++, dl
++){
9627 ls
= (LIST_SEL_S
*) fs_get(sizeof(*ls
));
9628 memset(ls
, 0, sizeof(*ls
));
9629 ls
->item
= cpystr(*t
);
9631 ls
->display_item
= cpystr(*dl
);
9633 if(cursor_location
&& (cursor_location
== (*t
)))
9647 if(!select_from_list_screen(listhead
, SFL_NONE
, title
, pdesc
,
9648 help
, htitle
, starting_val
))
9649 for(p
= listhead
; !choice
&& p
; p
= p
->next
)
9654 ret
= cpystr(choice
);
9656 free_list_sel(&listhead
);
9663 free_list_sel(LIST_SEL_S
**lsel
)
9666 free_list_sel(&(*lsel
)->next
);
9668 fs_give((void **) &(*lsel
)->item
);
9670 if((*lsel
)->display_item
)
9671 fs_give((void **) &(*lsel
)->display_item
);
9673 fs_give((void **) lsel
);
9679 * file_lister - call pico library's file lister
9682 file_lister(char *title
, char *path
, size_t pathlen
, char *file
, size_t filelen
, int newmail
, int flags
)
9686 void (*redraw
)(void) = ps_global
->redrawer
;
9688 standard_picobuf_setup(&pbf
);
9689 push_titlebar_state();
9693 /* BUG: what about help command and text? */
9694 pbf
.pine_anchor
= title
;
9696 rv
= pico_file_browse(&pbf
, path
, pathlen
, file
, filelen
, NULL
, 0, flags
);
9697 standard_picobuf_teardown(&pbf
);
9698 fix_windsize(ps_global
);
9699 init_signals(); /* has it's own signal stuff */
9701 /* Restore display's titlebar and body */
9702 pop_titlebar_state();
9704 if((ps_global
->redrawer
= redraw
) != NULL
)
9705 (*ps_global
->redrawer
)();
9711 /*----------------------------------------------------------------------
9712 Print current folder index
9716 print_index(struct pine
*state
, MSGNO_S
*msgmap
, int agg
)
9720 char buf
[MAX_SCREEN_COLS
+1];
9722 for(i
= 1L; i
<= mn_get_total(msgmap
); i
++){
9723 if(agg
&& !get_lflag(state
->mail_stream
, msgmap
, i
, MN_SLCT
))
9726 if(!agg
&& msgline_hidden(state
->mail_stream
, msgmap
, i
, 0))
9729 ice
= build_header_line(state
, state
->mail_stream
, msgmap
, i
, NULL
);
9733 * I don't understand why we'd want to mark the current message
9734 * instead of printing out the first character of the status
9735 * so I'm taking it out and including the first character of the
9736 * line instead. Hubert 2006-02-09
9738 if(!print_char((mn_is_cur(msgmap, i)) ? '>' : ' '))
9742 if(!gf_puts(simple_index_line(buf
,sizeof(buf
),ice
,i
),
9744 || !gf_puts(NEWLINE
, print_char
))
9754 free_mailcmd_globals(void)
9756 /* these calls are not possible during normal operations, so these
9757 * are hacks to free history memory
9759 smime_import_certificate(NULL
, NULL
, NULL
, 0);
9760 save_prompt(NULL
, NULL
, NULL
, 0, NULL
, NULL
, 0, NULL
, NULL
, NULL
);
9761 cmd_export(NULL
, NULL
, 0, 0);
9762 simple_export(NULL
, NULL
, CharStar
, NULL
, NULL
);
9763 broach_folder(0, 0, NULL
, NULL
);
9764 cmd_pipe(NULL
, NULL
, 0);
9765 select_by_text(NULL
, NULL
, 0, NULL
);
9772 * windows callback to get/set header mode state
9775 header_mode_callback(set
, args
)
9779 return(ps_global
->full_header
);
9784 * windows callback to get/set zoom mode state
9787 zoom_mode_callback(set
, args
)
9791 return(any_lflagged(ps_global
->msgmap
, MN_HIDE
) != 0);
9796 * windows callback to get/set zoom mode state
9799 any_selected_callback(set
, args
)
9803 return(any_lflagged(ps_global
->msgmap
, MN_SLCT
) != 0);
9811 flag_callback(set
, flags
)
9821 case 1: /* Important */
9822 permflag
= ps_global
->mail_stream
->perm_flagged
;
9826 permflag
= ps_global
->mail_stream
->perm_seen
;
9829 case 3: /* Answered */
9830 permflag
= ps_global
->mail_stream
->perm_answered
;
9833 case 4: /* Deleted */
9834 permflag
= ps_global
->mail_stream
->perm_deleted
;
9839 if(!(any_messages(ps_global
->msgmap
, NULL
, "to Flag")
9840 && can_set_flag(ps_global
, "flag", permflag
)))
9843 if(sp_io_error_on_stream(ps_global
->mail_stream
)){
9844 sp_set_io_error_on_stream(ps_global
->mail_stream
, 0);
9845 pine_mail_check(ps_global
->mail_stream
); /* forces write */
9849 msgno
= mn_m2raw(ps_global
->msgmap
, mn_get_cur(ps_global
->msgmap
));
9850 if(msgno
> 0L && ps_global
->mail_stream
9851 && msgno
<= ps_global
->mail_stream
->nmsgs
9852 && (mc
= mail_elt(ps_global
->mail_stream
, msgno
))
9855 * NOTE: code below is *VERY* sensitive to the order of
9856 * the messages defined in resource.h for flag handling.
9857 * Don't change it unless you know what you're doing.
9864 case 1 : /* Important */
9865 flagstr
= "\\FLAGGED";
9866 mflag
= (mc
->flagged
) ? 0L : ST_SET
;
9871 mflag
= (mc
->seen
) ? 0L : ST_SET
;
9874 case 3 : /* Answered */
9875 flagstr
= "\\ANSWERED";
9876 mflag
= (mc
->answered
) ? 0L : ST_SET
;
9879 case 4 : /* Deleted */
9880 flagstr
= "\\DELETED";
9881 mflag
= (mc
->deleted
) ? 0L : ST_SET
;
9884 default : /* bogus */
9888 mail_flag(ps_global
->mail_stream
, long2string(msgno
),
9891 if(ps_global
->redrawer
)
9892 (*ps_global
->redrawer
)();
9919 * BUG: Should teach this about keywords
9925 static MPopup flag_submenu
[] = {
9926 {tMessage
, {N_("Important"), lNormal
}, {IDM_MI_FLAGIMPORTANT
}},
9927 {tMessage
, {N_("New"), lNormal
}, {IDM_MI_FLAGNEW
}},
9928 {tMessage
, {N_("Answered"), lNormal
}, {IDM_MI_FLAGANSWERED
}},
9929 {tMessage
, {N_("Deleted"), lNormal
}, {IDM_MI_FLAGDELETED
}},
9934 flag_submenu
[0].label
.style
= (mc
&& mc
->flagged
) ? lChecked
: lNormal
;
9937 flag_submenu
[1].label
.style
= (mc
&& mc
->seen
) ? lNormal
: lChecked
;
9940 flag_submenu
[2].label
.style
= (mc
&& mc
->answered
) ? lChecked
: lNormal
;
9943 flag_submenu
[3].label
.style
= (mc
&& mc
->deleted
) ? lChecked
: lNormal
;
9945 return(flag_submenu
);
9948 #endif /* _WINDOWS */