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-2016 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);
87 int cmd_forward(struct pine
*, MSGNO_S
*, int);
88 int cmd_bounce(struct pine
*, MSGNO_S
*, int);
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
);
325 int smime_import_certificate(char *filename
, char *full_filename
, char *what
, size_t len
)
328 static HISTORY_S
*history
= NULL
;
329 static ESCKEY_S eopts
[] = {
330 {ctrl('T'), 10, "^T", N_("To Files")},
332 {-1, 0, NULL
, NULL
}};
334 if(F_ON(F_ENABLE_TAB_COMPLETE
,ps_global
)){
335 eopts
[r
].ch
= ctrl('I');
337 eopts
[r
].name
= "TAB";
338 eopts
[r
].label
= N_("Complete");
344 full_filename
[0] = '\0';
346 r
= get_export_filename(ps_global
, filename
, NULL
, full_filename
,
347 len
, what
, "IMPORT", eopts
, NULL
,
348 -FOOTER_ROWS(ps_global
), GE_IS_IMPORT
, &history
);
354 /*----------------------------------------------------------------------
355 The giant switch on the commands for index and viewing
357 Input: command -- The command char/code
358 in_index -- flag indicating command is from index
359 orig_command -- The original command typed before pre-processing
360 Output: force_mailchk -- Set to tell caller to force call to new_mail().
364 Returns 1 if the message number or attachment to show changed
367 process_cmd(struct pine
*state
, MAILSTREAM
*stream
, MSGNO_S
*msgmap
,
368 int command
, CmdWhere in_index
, int *force_mailchk
)
370 int question_line
, a_changed
, flags
= 0, ret
, j
;
372 long new_msgno
, del_count
, old_msgno
, i
;
374 char *newfolder
, prompt
[MAX_SCREEN_COLS
+1];
377 #if defined(DOS) && !defined(_WINDOWS)
378 extern long coreleft();
381 dprint((4, "\n - process_cmd(cmd=%d) -\n", command
));
383 question_line
= -FOOTER_ROWS(state
);
384 state
->mangled_screen
= 0;
385 state
->mangled_footer
= 0;
386 state
->mangled_header
= 0;
387 state
->next_screen
= SCREEN_FUN_NULL
;
388 old_msgno
= mn_get_cur(msgmap
);
393 /*------------- Help --------*/
396 * We're not using the h_mail_view portion of this right now because
397 * that call is being handled in scrolltool() before it gets
398 * here. Leave it in case we change how it works.
400 helper((in_index
== MsgIndx
)
404 : h_mail_thread_index
,
405 (in_index
== MsgIndx
)
406 ? _("HELP FOR MESSAGE INDEX")
408 ? _("HELP FOR MESSAGE TEXT")
409 : _("HELP FOR THREAD INDEX"),
411 dprint((4,"MAIL_CMD: did help command\n"));
412 state
->mangled_screen
= 1;
416 /*--------- Return to main menu ------------*/
418 state
->next_screen
= main_menu_screen
;
419 dprint((2,"MAIL_CMD: going back to main menu\n"));
423 /*------- View message text --------*/
426 if(any_messages(msgmap
, NULL
, "to View")){
427 state
->next_screen
= mail_view_screen
;
433 /*------- View attachment --------*/
435 state
->next_screen
= attachment_screen
;
436 dprint((2,"MAIL_CMD: going to attachment screen\n"));
440 /*---------- Previous message ----------*/
442 if(any_messages(msgmap
, NULL
, NULL
)){
443 if((i
= mn_get_cur(msgmap
)) > 1L){
444 mn_dec_cur(stream
, msgmap
,
445 (in_index
== View
&& THREADING()
446 && sp_viewing_a_thread(stream
))
449 ? MH_ANYTHD
: MH_NONE
);
450 if(i
== mn_get_cur(msgmap
)){
451 PINETHRD_S
*thrd
= NULL
, *topthrd
= NULL
;
453 if(THRD_INDX_ENABLED()){
454 mn_dec_cur(stream
, msgmap
, MH_ANYTHD
);
455 if(i
== mn_get_cur(msgmap
))
456 q_status_message1(SM_ORDER
, 0, 2,
457 _("Already on first %s in Zoomed Index"),
458 THRD_INDX() ? _("thread") : _("message"));
461 || F_ON(F_NEXT_THRD_WO_CONFIRM
, state
))
464 ret
= want_to(_("View previous thread"), 'y', 'x',
468 q_status_message(SM_ORDER
, 0, 2,
469 _("Viewing previous thread"));
470 new_msgno
= mn_get_cur(msgmap
);
471 mn_set_cur(msgmap
, i
);
472 if(unview_thread(state
, stream
, msgmap
)){
473 state
->next_screen
= mail_index_screen
;
474 state
->view_skipped_index
= 0;
475 state
->mangled_screen
= 1;
478 mn_set_cur(msgmap
, new_msgno
);
479 if(THRD_AUTO_VIEW() && in_index
== View
){
481 thrd
= fetch_thread(stream
,
484 if(count_lflags_in_thread(stream
, thrd
,
487 if(view_thread(state
, stream
, msgmap
, 1)){
488 if(current_index_state
)
489 msgmap
->top_after_thrd
= current_index_state
->msg_at_top
;
491 state
->view_skipped_index
= 1;
492 command
= MC_VIEW_TEXT
;
499 if(THRD_AUTO_VIEW() && in_index
!= View
){
500 thrd
= fetch_thread(stream
, mn_m2raw(msgmap
, new_msgno
));
501 if(thrd
&& thrd
->top
)
502 topthrd
= fetch_thread(stream
, thrd
->top
);
505 j
= count_lflags_in_thread(stream
, topthrd
, msgmap
, MN_NONE
);
508 if(!THRD_AUTO_VIEW() || in_index
== View
|| j
!= 1){
509 if(view_thread(state
, stream
, msgmap
, 1)
510 && current_index_state
)
511 msgmap
->top_after_thrd
= current_index_state
->msg_at_top
;
515 state
->next_screen
= SCREEN_FUN_NULL
;
518 mn_set_cur(msgmap
, i
); /* put it back */
522 q_status_message1(SM_ORDER
, 0, 2,
523 _("Already on first %s in Zoomed Index"),
524 THRD_INDX() ? _("thread") : _("message"));
531 && ((now
= time(0)) > state
->last_nextitem_forcechk
)){
533 /* check at most once a second */
534 state
->last_nextitem_forcechk
= now
;
537 q_status_message1(SM_ORDER
, 0, 1, _("Already on first %s"),
538 THRD_INDX() ? _("thread") : _("message"));
545 /*---------- Next Message ----------*/
547 if(mn_get_total(msgmap
) > 0L
548 && ((i
= mn_get_cur(msgmap
)) < mn_get_total(msgmap
))){
549 mn_inc_cur(stream
, msgmap
,
550 (in_index
== View
&& THREADING()
551 && sp_viewing_a_thread(stream
))
554 ? MH_ANYTHD
: MH_NONE
);
555 if(i
== mn_get_cur(msgmap
)){
556 PINETHRD_S
*thrd
, *topthrd
;
558 if(THRD_INDX_ENABLED()){
560 mn_inc_cur(stream
, msgmap
, MH_ANYTHD
);
562 if(i
== mn_get_cur(msgmap
)){
563 if(any_lflagged(msgmap
, MN_HIDE
))
564 any_messages(NULL
, "more", "in Zoomed Index");
570 || F_ON(F_NEXT_THRD_WO_CONFIRM
, state
))
573 ret
= want_to(_("View next thread"), 'y', 'x',
577 q_status_message(SM_ORDER
, 0, 2,
578 _("Viewing next thread"));
579 new_msgno
= mn_get_cur(msgmap
);
580 mn_set_cur(msgmap
, i
);
581 if(unview_thread(state
, stream
, msgmap
)){
582 state
->next_screen
= mail_index_screen
;
583 state
->view_skipped_index
= 0;
584 state
->mangled_screen
= 1;
587 mn_set_cur(msgmap
, new_msgno
);
588 if(THRD_AUTO_VIEW() && in_index
== View
){
590 thrd
= fetch_thread(stream
,
593 if(count_lflags_in_thread(stream
, thrd
,
596 if(view_thread(state
, stream
, msgmap
, 1)){
597 if(current_index_state
)
598 msgmap
->top_after_thrd
= current_index_state
->msg_at_top
;
600 state
->view_skipped_index
= 1;
601 command
= MC_VIEW_TEXT
;
608 if(THRD_AUTO_VIEW() && in_index
!= View
){
609 thrd
= fetch_thread(stream
, mn_m2raw(msgmap
, new_msgno
));
610 if(thrd
&& thrd
->top
)
611 topthrd
= fetch_thread(stream
, thrd
->top
);
614 j
= count_lflags_in_thread(stream
, topthrd
, msgmap
, MN_NONE
);
617 if(!THRD_AUTO_VIEW() || in_index
== View
|| j
!= 1){
618 if(view_thread(state
, stream
, msgmap
, 1)
619 && current_index_state
)
620 msgmap
->top_after_thrd
= current_index_state
->msg_at_top
;
624 state
->next_screen
= SCREEN_FUN_NULL
;
627 mn_set_cur(msgmap
, i
); /* put it back */
631 && (thrd
= fetch_thread(stream
, mn_m2raw(msgmap
, i
)))
633 && get_lflag(stream
, NULL
, thrd
->rawno
, MN_COLL
)){
634 q_status_message(SM_ORDER
, 0, 2,
635 _("Expand collapsed thread to see more messages"));
638 any_messages(NULL
, "more", "in Zoomed Index");
646 || (state
->context_current
->use
& CNTXT_INCMNG
)){
647 char nextfolder
[MAXPATH
];
649 strncpy(nextfolder
, state
->cur_folder
, sizeof(nextfolder
));
650 nextfolder
[sizeof(nextfolder
)-1] = '\0';
651 if(next_folder(NULL
, nextfolder
, sizeof(nextfolder
), nextfolder
,
652 state
->context_current
, NULL
, NULL
))
653 strncpy(prompt
, _(". Press TAB for next folder."),
656 strncpy(prompt
, _(". No more folders to TAB to."),
659 prompt
[sizeof(prompt
)-1] = '\0';
662 any_messages(NULL
, (mn_get_total(msgmap
) > 0L) ? "more" : NULL
,
663 prompt
[0] ? prompt
: NULL
);
666 && ((now
= time(0)) > state
->last_nextitem_forcechk
)){
668 /* check at most once a second */
669 state
->last_nextitem_forcechk
= now
;
676 /*---------- Delete message ----------*/
678 (void) cmd_delete(state
, msgmap
, MCMD_NONE
,
679 (in_index
== View
) ? cmd_delete_view
: cmd_delete_index
);
683 /*---------- Undelete message ----------*/
685 (void) cmd_undelete(state
, msgmap
, MCMD_NONE
);
686 update_titlebar_status();
690 /*---------- Reply to message ----------*/
692 (void) cmd_reply(state
, msgmap
, MCMD_NONE
);
696 /*---------- Forward message ----------*/
698 (void) cmd_forward(state
, msgmap
, MCMD_NONE
);
702 /*---------- Quit pine ------------*/
704 state
->next_screen
= quit_screen
;
705 dprint((1,"MAIL_CMD: quit\n"));
709 /*---------- Compose message ----------*/
711 state
->prev_screen
= (in_index
== View
) ? mail_view_screen
713 compose_screen(state
);
714 state
->mangled_screen
= 1;
715 if (state
->next_screen
)
720 /*---------- Alt Compose message ----------*/
722 state
->prev_screen
= (in_index
== View
) ? mail_view_screen
725 if(state
->next_screen
)
731 /*--------- Folders menu ------------*/
733 state
->start_in_context
= 1;
735 /*--------- Top of Folders list menu ------------*/
736 case MC_COLLECTIONS
:
737 state
->next_screen
= folder_screen
;
738 dprint((2,"MAIL_CMD: going to folder/collection menu\n"));
742 /*---------- Open specific new folder ----------*/
744 tc
= (state
->context_last
&& !NEWS_TEST(state
->context_current
))
745 ? state
->context_last
: state
->context_current
;
747 newfolder
= broach_folder(question_line
, 1, ¬realinbox
, &tc
);
749 visit_folder(state
, newfolder
, tc
, NULL
, notrealinbox
? 0L : DB_INBOXWOCNTXT
);
756 /*------- Go to Index Screen ----------*/
758 state
->next_screen
= mail_index_screen
;
761 /*------- Skip to next interesting message -----------*/
767 * If we're in the thread index, start looking after this
768 * thread. We don't want to match something in the current
771 start
= mn_get_cur(msgmap
);
772 thrd
= fetch_thread(stream
, mn_m2raw(msgmap
, mn_get_cur(msgmap
)));
773 if(mn_get_revsort(msgmap
)){
774 /* if reversed, top of thread is last one before next thread */
775 if(thrd
&& thrd
->top
)
776 start
= mn_raw2m(msgmap
, thrd
->top
);
779 /* last msg of thread is at the ends of the branches/nexts */
781 start
= mn_raw2m(msgmap
, thrd
->rawno
);
783 thrd
= fetch_thread(stream
, thrd
->branch
);
785 thrd
= fetch_thread(stream
, thrd
->next
);
792 * Flags is 0 in this case because we want to not skip
793 * messages inside of threads so that we can find threads
794 * which have some unseen messages even though the top-level
795 * of the thread is already seen.
796 * If new_msgno ends up being a message which is not visible
797 * because it isn't at the top-level, the current message #
798 * will be adjusted below in adjust_cur.
801 new_msgno
= next_sorted_flagged((F_UNDEL
803 | ((F_ON(F_TAB_TO_NEW
,state
))
805 stream
, start
, &flags
);
807 else if(THREADING() && sp_viewing_a_thread(stream
)){
808 PINETHRD_S
*thrd
, *topthrd
= NULL
;
810 start
= mn_get_cur(msgmap
);
813 * Things are especially complicated when we're viewing_a_thread
814 * from the thread index. First we have to check within the
815 * current thread for a new message. If none is found, then
816 * we search in the next threads and offer to continue in
817 * them. Then we offer to go to the next folder.
819 flags
= NSF_SKIP_CHID
;
820 new_msgno
= next_sorted_flagged((F_UNDEL
822 | ((F_ON(F_TAB_TO_NEW
,state
))
824 stream
, start
, &flags
);
826 * If we found a match then we are done, that is another message
827 * in the current thread index. Otherwise, we have to look
830 if(!(flags
& NSF_FLAG_MATCH
)){
835 new_msgno
= next_sorted_flagged((F_UNDEL
837 | ((F_ON(F_TAB_TO_NEW
,
840 stream
, start
, &flags
);
842 * If we got a match, new_msgno is a message in
843 * a different thread from the one we are viewing.
845 if(flags
& NSF_FLAG_MATCH
){
846 thrd
= fetch_thread(stream
, mn_m2raw(msgmap
,new_msgno
));
847 if(thrd
&& thrd
->top
)
848 topthrd
= fetch_thread(stream
, thrd
->top
);
850 if(F_OFF(F_AUTO_OPEN_NEXT_UNREAD
, state
)){
851 static ESCKEY_S next_opt
[] = {
852 {'y', 'y', "Y", N_("Yes")},
853 {'n', 'n', "N", N_("No")},
854 {TAB
, 'n', "Tab", N_("NextNew")},
859 snprintf(prompt
, sizeof(prompt
), _("View thread number %s? "),
860 topthrd
? comatose(topthrd
->thrdno
) : "?");
862 snprintf(prompt
, sizeof(prompt
),
863 _("View message in thread number %s? "),
864 topthrd
? comatose(topthrd
->thrdno
) : "?");
866 prompt
[sizeof(prompt
)-1] = '\0';
868 ret
= radio_buttons(prompt
, -FOOTER_ROWS(state
),
869 next_opt
, 'y', 'x', NO_HELP
,
880 if(unview_thread(state
, stream
, msgmap
)){
881 state
->next_screen
= mail_index_screen
;
882 state
->view_skipped_index
= 0;
883 state
->mangled_screen
= 1;
886 mn_set_cur(msgmap
, new_msgno
);
887 if(THRD_AUTO_VIEW()){
889 if(count_lflags_in_thread(stream
, topthrd
,
890 msgmap
, MN_NONE
) == 1){
891 if(view_thread(state
, stream
, msgmap
, 1)){
892 if(current_index_state
)
893 msgmap
->top_after_thrd
= current_index_state
->msg_at_top
;
895 state
->view_skipped_index
= 1;
896 command
= MC_VIEW_TEXT
;
902 if(view_thread(state
, stream
, msgmap
, 1) && current_index_state
)
903 msgmap
->top_after_thrd
= current_index_state
->msg_at_top
;
905 state
->next_screen
= SCREEN_FUN_NULL
;
908 else if(ret
== 'n' && topthrd
){
910 * skip to end of this thread and look starting
911 * in the next thread.
913 if(mn_get_revsort(msgmap
)){
915 * if reversed, top of thread is last one
918 start
= mn_raw2m(msgmap
, topthrd
->rawno
);
922 * last msg of thread is at the ends of
927 start
= mn_raw2m(msgmap
, thrd
->rawno
);
929 thrd
= fetch_thread(stream
, thrd
->branch
);
931 thrd
= fetch_thread(stream
, thrd
->next
);
947 start
= mn_get_cur(msgmap
);
950 * If we are on a collapsed thread, start looking after the
951 * collapsed part, unless we are viewing the message.
953 if(THREADING() && in_index
!= View
){
958 rawno
= mn_m2raw(msgmap
, start
);
959 thrd
= fetch_thread(stream
, rawno
);
960 collapsed
= thrd
&& thrd
->next
961 && get_lflag(stream
, NULL
, rawno
, MN_COLL
);
964 if(mn_get_revsort(msgmap
)){
965 if(thrd
&& thrd
->top
)
966 start
= mn_raw2m(msgmap
, thrd
->top
);
970 start
= mn_raw2m(msgmap
, thrd
->rawno
);
972 thrd
= fetch_thread(stream
, thrd
->branch
);
974 thrd
= fetch_thread(stream
, thrd
->next
);
983 new_msgno
= next_sorted_flagged((F_UNDEL
985 | ((F_ON(F_TAB_TO_NEW
,state
))
987 stream
, start
, &flags
);
991 * If there weren't any unread messages left, OR there
992 * aren't any messages at all, we may want to offer to
993 * go on to the next folder...
995 if(flags
& NSF_FLAG_MATCH
){
996 mn_set_cur(msgmap
, new_msgno
);
998 adjust_cur_to_visible(stream
, msgmap
);
1001 int in_inbox
= sp_flagged(stream
, SP_INBOX
);
1003 if(state
->context_current
1004 && ((NEWS_TEST(state
->context_current
)
1005 && context_isambig(state
->cur_folder
))
1006 || ((state
->context_current
->use
& CNTXT_INCMNG
)
1008 || folder_index(state
->cur_folder
,
1009 state
->context_current
,
1010 FI_FOLDER
) >= 0)))){
1011 char nextfolder
[MAXPATH
];
1012 MAILSTREAM
*nextstream
= NULL
;
1016 strncpy(nextfolder
, state
->cur_folder
, sizeof(nextfolder
));
1017 nextfolder
[sizeof(nextfolder
)-1] = '\0';
1019 if(!(next_folder(&nextstream
, nextfolder
, sizeof(nextfolder
), nextfolder
,
1020 state
->context_current
, &recent_cnt
,
1021 F_ON(F_TAB_NO_CONFIRM
,state
)
1022 ? NULL
: &did_cancel
))){
1024 static ESCKEY_S inbox_opt
[] = {
1025 {'y', 'y', "Y", N_("Yes")},
1026 {'n', 'n', "N", N_("No")},
1027 {TAB
, 'z', "Tab", N_("To Inbox")},
1031 if(F_ON(F_RET_INBOX_NO_CONFIRM
,state
))
1034 /* TRANSLATORS: this is a question, with some information followed
1035 by Return to INBOX? */
1036 if(state
->context_current
->use
&CNTXT_INCMNG
)
1037 snprintf(prompt
, sizeof(prompt
), _("No more incoming folders. Return to \"%s\"? "), state
->inbox_name
);
1039 snprintf(prompt
, sizeof(prompt
), _("No more news groups. Return to \"%s\"? "), state
->inbox_name
);
1041 ret
= radio_buttons(prompt
, -FOOTER_ROWS(state
),
1042 inbox_opt
, 'y', 'x',
1047 * 'z' is a synonym for 'y'. It is not 'y'
1048 * so that it isn't displayed as a default
1049 * action with square-brackets around it
1052 if(ret
== 'y' || ret
== 'z'){
1053 visit_folder(state
, state
->inbox_name
,
1054 state
->context_current
,
1055 NULL
, DB_INBOXWOCNTXT
);
1059 else if (did_cancel
)
1060 cmd_cancelled(NULL
);
1062 if(state
->context_current
->use
&CNTXT_INCMNG
)
1063 q_status_message(SM_ORDER
, 0, 2, _("No more incoming folders"));
1065 q_status_message(SM_ORDER
, 0, 2, _("No more news groups"));
1073 char *front
, type
[80], cnt
[CNTLEN
], fbuf
[MAX_SCREEN_COLS
/2+1];
1074 int rbspace
, avail
, need
, take_back
;
1078 * Incoming_folder_ or news_group_ or folder_ or group_
1080 * _(13 recent) or _(some recent) or nothing
1083 front
= "View next";
1085 (state
->context_current
->use
& CNTXT_INCMNG
)
1086 ? "Incoming folder" : "news group",
1088 type
[sizeof(type
)-1] = '\0';
1089 snprintf(cnt
, sizeof(cnt
), " (%.*s %s)", CNTLEN
-20,
1090 recent_cnt
? long2string(recent_cnt
) : "some",
1091 F_ON(F_TAB_USES_UNSEEN
, ps_global
)
1092 ? "unseen" : "recent");
1093 cnt
[sizeof(cnt
)-1] = '\0';
1096 * Space reserved for radio_buttons call.
1097 * If we make this 3 then radio_buttons won't mess
1098 * with the prompt. If we make it 2, then we get
1099 * one more character to use but radio_buttons will
1100 * cut off the last character of our prompt, which is
1101 * ok because it is a space.
1104 avail
= ps_global
->ttyo
? ps_global
->ttyo
->screen_cols
1106 need
= strlen(front
)+1 + strlen(type
)+1 +
1107 + strlen(nextfolder
)+2 + strlen(cnt
) +
1110 take_back
= strlen(type
);
1112 (state
->context_current
->use
& CNTXT_INCMNG
)
1113 ? "folder" : "group", sizeof(type
));
1114 take_back
-= strlen(type
);
1117 need
-= strlen(cnt
);
1121 /* MAX_SCREEN_COLS+1 = sizeof(prompt) */
1122 snprintf(prompt
, sizeof(prompt
), "%.*s %.*s \"%.*s\"%.*s? ",
1123 (MAX_SCREEN_COLS
+1)/8, front
,
1124 (MAX_SCREEN_COLS
+1)/8, type
,
1125 (MAX_SCREEN_COLS
+1)/2,
1126 short_str(nextfolder
, fbuf
, sizeof(fbuf
),
1127 strlen(nextfolder
) -
1128 ((need
>avail
) ? (need
-avail
) : 0),
1130 (MAX_SCREEN_COLS
+1)/8, cnt
);
1131 prompt
[sizeof(prompt
)-1] = '\0';
1135 * When help gets added, this'll have to become
1136 * a loop like the rest...
1138 if(F_OFF(F_AUTO_OPEN_NEXT_UNREAD
, state
)){
1139 static ESCKEY_S next_opt
[] = {
1140 {'y', 'y', "Y", N_("Yes")},
1141 {'n', 'n', "N", N_("No")},
1142 {TAB
, 'n', "Tab", N_("NextNew")},
1146 ret
= radio_buttons(prompt
, -FOOTER_ROWS(state
),
1147 next_opt
, 'y', 'x', NO_HELP
,
1150 cmd_cancelled(NULL
);
1158 if(nextstream
&& sp_dead_stream(nextstream
))
1161 visit_folder(state
, nextfolder
,
1162 state
->context_current
, nextstream
,
1164 /* visit_folder takes care of nextstream */
1172 pine_mail_close(nextstream
);
1176 (mn_get_total(msgmap
) > 0L)
1177 ? IS_NEWS(stream
) ? "more undeleted" : "more new"
1187 /*------- Zoom -----------*/
1190 * Right now the way zoom is implemented is sort of silly.
1191 * There are two per-message flags where just one and a
1192 * global "zoom mode" flag to suppress messags from the index
1195 if(any_messages(msgmap
, NULL
, "to Zoom on")){
1196 if(unzoom_index(state
, stream
, msgmap
)){
1197 dprint((4, "\n\n ---- Exiting ZOOM mode ----\n"));
1198 q_status_message(SM_ORDER
,0,2, _("Index Zoom Mode is now off"));
1200 else if((i
= zoom_index(state
, stream
, msgmap
, MN_SLCT
)) != 0){
1201 if(any_lflagged(msgmap
, MN_HIDE
)){
1202 dprint((4,"\n\n ---- Entering ZOOM mode ----\n"));
1203 q_status_message4(SM_ORDER
, 0, 2,
1204 _("In Zoomed Index of %s%s%s%s. Use \"Z\" to restore regular Index"),
1205 THRD_INDX() ? "" : comatose(i
),
1206 THRD_INDX() ? "" : " ",
1207 THRD_INDX() ? _("threads") : _("message"),
1208 THRD_INDX() ? "" : plural(i
));
1211 q_status_message(SM_ORDER
, 0, 2,
1212 _("All messages selected, so not entering Index Zoom Mode"));
1215 any_messages(NULL
, "selected", "to Zoom on");
1221 /*---------- print message on paper ----------*/
1223 if(any_messages(msgmap
, NULL
, "to print"))
1224 (void) cmd_print(state
, msgmap
, MCMD_NONE
, in_index
);
1229 /*---------- Take Address ----------*/
1231 if(F_ON(F_ENABLE_ROLE_TAKE
, state
) ||
1232 any_messages(msgmap
, NULL
, "to Take address from"))
1233 (void) cmd_take_addr(state
, msgmap
, MCMD_NONE
);
1238 /*---------- Save Message ----------*/
1240 if(any_messages(msgmap
, NULL
, "to Save"))
1241 (void) cmd_save(state
, stream
, msgmap
, MCMD_NONE
, in_index
);
1246 /*---------- Export message ----------*/
1248 if(any_messages(msgmap
, NULL
, "to Export")){
1249 (void) cmd_export(state
, msgmap
, question_line
, MCMD_NONE
);
1250 state
->mangled_footer
= 1;
1256 /*---------- Expunge ----------*/
1258 (void) cmd_expunge(state
, stream
, msgmap
, MCMD_NONE
);
1262 /*------- Unexclude -----------*/
1264 if(!(IS_NEWS(stream
) && stream
->rdonly
)){
1265 q_status_message(SM_ORDER
, 0, 3,
1266 _("Unexclude not available for mail folders"));
1268 else if(any_lflagged(msgmap
, MN_EXLD
)){
1274 * Since excluded means "hidden deleted" and "killed",
1275 * the count should reflect the former.
1277 pgm
= mail_newsearchpgm();
1279 pine_mail_search_full(stream
, NULL
, pgm
, SE_NOPREFETCH
| SE_FREE
);
1280 for(i
= 1L, del_count
= 0L; i
<= stream
->nmsgs
; i
++)
1281 if((mc
= mail_elt(stream
, i
)) && mc
->searched
1282 && get_lflag(stream
, NULL
, i
, MN_EXLD
)
1283 && !(msgno_exceptions(stream
, i
, "0", &exbits
, FALSE
)
1284 && (exbits
& MSG_EX_FILTERED
)))
1288 state
->mangled_footer
= 1; /* MAX_SCREEN_COLS+1 = sizeof(prompt) */
1289 snprintf(prompt
, sizeof(prompt
), "UNexclude %ld message%s in %.*s", del_count
,
1290 plural(del_count
), MAX_SCREEN_COLS
+1-40,
1291 pretty_fn(state
->cur_folder
));
1292 prompt
[sizeof(prompt
)-1] = '\0';
1293 if(F_ON(F_FULL_AUTO_EXPUNGE
, state
)
1294 || (F_ON(F_AUTO_EXPUNGE
, state
)
1295 && (state
->context_current
1296 && (state
->context_current
->use
& CNTXT_INCMNG
))
1297 && context_isambig(state
->cur_folder
))
1298 || want_to(prompt
, 'y', 0, NO_HELP
, WT_NORM
) == 'y'){
1299 long save_cur_rawno
;
1300 int were_viewing_a_thread
;
1302 save_cur_rawno
= mn_m2raw(msgmap
, mn_get_cur(msgmap
));
1303 were_viewing_a_thread
= (THREADING()
1304 && sp_viewing_a_thread(stream
));
1306 if(msgno_include(stream
, msgmap
, MI_NONE
)){
1307 clear_index_cache(stream
, 0);
1309 if(stream
&& stream
->spare
)
1310 erase_threading_info(stream
, msgmap
);
1312 refresh_sort(stream
, msgmap
, SRT_NON
);
1315 if(were_viewing_a_thread
){
1316 if(save_cur_rawno
> 0L)
1317 mn_set_cur(msgmap
, mn_raw2m(msgmap
,save_cur_rawno
));
1319 if(view_thread(state
, stream
, msgmap
, 1) && current_index_state
)
1320 msgmap
->top_after_thrd
= current_index_state
->msg_at_top
;
1323 if(save_cur_rawno
> 0L)
1324 mn_set_cur(msgmap
, mn_raw2m(msgmap
,save_cur_rawno
));
1326 state
->mangled_screen
= 1;
1327 q_status_message2(SM_ORDER
, 0, 4,
1328 "%s message%s UNexcluded",
1329 long2string(del_count
),
1332 if(in_index
!= View
)
1333 adjust_cur_to_visible(stream
, msgmap
);
1336 any_messages(NULL
, NULL
, "UNexcluded");
1339 any_messages(NULL
, "excluded", "to UNexclude");
1342 any_messages(NULL
, "excluded", "to UNexclude");
1347 /*------- Make Selection -----------*/
1349 if(any_messages(msgmap
, NULL
, "to Select")){
1350 if(aggregate_select(state
, msgmap
, question_line
, in_index
) == 0
1351 && (in_index
== MsgIndx
|| in_index
== ThrdIndx
)
1352 && F_ON(F_AUTO_ZOOM
, state
)
1353 && any_lflagged(msgmap
, MN_SLCT
) > 0L
1354 && !any_lflagged(msgmap
, MN_HIDE
))
1355 (void) zoom_index(state
, stream
, msgmap
, MN_SLCT
);
1361 /*------- Toggle Current Message Selection State -----------*/
1363 if(any_messages(msgmap
, NULL
, NULL
)){
1364 if((select_by_current(state
, msgmap
, in_index
)
1365 || (F_OFF(F_UNSELECT_WONT_ADVANCE
, state
)
1366 && !any_lflagged(msgmap
, MN_HIDE
)))
1367 && (i
= mn_get_cur(msgmap
)) < mn_get_total(msgmap
)){
1368 /* advance current */
1369 mn_inc_cur(stream
, msgmap
,
1370 (in_index
== View
&& THREADING()
1371 && sp_viewing_a_thread(stream
))
1373 : (in_index
== View
)
1374 ? MH_ANYTHD
: MH_NONE
);
1381 /*------- Apply command -----------*/
1383 if(any_messages(msgmap
, NULL
, NULL
)){
1384 if(any_lflagged(msgmap
, MN_SLCT
) > 0L){
1385 if(apply_command(state
, stream
, msgmap
, 0,
1386 AC_NONE
, question_line
)){
1387 if(F_ON(F_AUTO_UNSELECT
, state
)){
1388 agg_select_all(stream
, msgmap
, NULL
, 0);
1389 unzoom_index(state
, stream
, msgmap
);
1391 else if(F_ON(F_AUTO_UNZOOM
, state
))
1392 unzoom_index(state
, stream
, msgmap
);
1396 any_messages(NULL
, NULL
, "to Apply command to. Try \"Select\"");
1402 /*-------- Sort command -------*/
1405 int were_threading
= THREADING();
1406 SortOrder sort
= mn_get_sort(msgmap
);
1407 int rev
= mn_get_revsort(msgmap
);
1409 dprint((1,"MAIL_CMD: sort\n"));
1410 if(select_sort(state
, question_line
, &sort
, &rev
)){
1411 /* $ command reinitializes threading collapsed/expanded info */
1412 if(SORT_IS_THREADED(msgmap
) && !SEP_THRDINDX())
1413 erase_threading_info(stream
, msgmap
);
1415 if(ps_global
&& ps_global
->ttyo
){
1416 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
1417 ps_global
->mangled_footer
= 1;
1420 sort_folder(stream
, msgmap
, sort
, rev
, SRT_VRB
|SRT_MAN
);
1423 state
->mangled_footer
= 1;
1426 * We've changed whether we are threading or not so we need to
1427 * exit the index and come back in so that we switch between the
1428 * thread index and the regular index. Sort_folder will have
1429 * reset viewing_a_thread if necessary.
1432 && ((!were_threading
&& THREADING())
1433 || (were_threading
&& !THREADING()))){
1434 state
->next_screen
= mail_index_screen
;
1435 state
->mangled_screen
= 1;
1442 /*------- Toggle Full Headers -----------*/
1444 state
->full_header
++;
1445 if(state
->full_header
== 1){
1446 if(!(state
->quote_suppression_threshold
1447 && (state
->some_quoting_was_suppressed
|| in_index
!= View
)))
1448 state
->full_header
++;
1450 else if(state
->full_header
> 2)
1451 state
->full_header
= 0;
1453 switch(state
->full_header
){
1455 q_status_message(SM_ORDER
, 0, 3,
1456 _("Display of full headers is now off."));
1460 q_status_message1(SM_ORDER
, 0, 3,
1461 _("Quotes displayed, use %s to see full headers"),
1462 F_ON(F_USE_FK
, state
) ? "F9" : "H");
1466 q_status_message(SM_ORDER
, 0, 3,
1467 _("Display of full headers is now on."));
1482 /*------- Try to decrypt message -----------*/
1484 if(state
->smime
&& state
->smime
->need_passphrase
)
1485 smime_get_passphrase();
1491 smime_info_screen(state
);
1496 /*------- Bounce -----------*/
1498 (void) cmd_bounce(state
, msgmap
, MCMD_NONE
);
1502 /*------- Flag -----------*/
1504 dprint((4, "\n - flag message -\n"));
1505 (void) cmd_flag(state
, msgmap
, MCMD_NONE
);
1509 /*------- Pipe message -----------*/
1511 (void) cmd_pipe(state
, msgmap
, MCMD_NONE
);
1515 /*--------- Default, unknown command ----------*/
1517 alpine_panic("Unexpected command case");
1521 return((a_changed
|| mn_get_cur(msgmap
) != old_msgno
) ? 1 : 0);
1526 /*----------------------------------------------------------------------
1527 Map some of the special characters into sensible strings for human
1529 c is a UCS-4 character!
1532 pretty_command(UCS c
)
1534 static char buf
[10];
1541 case ' ' : s
= "SPACE"; break;
1542 case '\033' : s
= "ESC"; break;
1543 case '\177' : s
= "DEL"; break;
1544 case ctrl('I') : s
= "TAB"; break;
1545 case ctrl('J') : s
= "LINEFEED"; break;
1546 case ctrl('M') : s
= "RETURN"; break;
1547 case ctrl('Q') : s
= "XON"; break;
1548 case ctrl('S') : s
= "XOFF"; break;
1549 case KEY_UP
: s
= "Up Arrow"; break;
1550 case KEY_DOWN
: s
= "Down Arrow"; break;
1551 case KEY_RIGHT
: s
= "Right Arrow"; break;
1552 case KEY_LEFT
: s
= "Left Arrow"; break;
1553 case KEY_PGUP
: s
= "Prev Page"; break;
1554 case KEY_PGDN
: s
= "Next Page"; break;
1555 case KEY_HOME
: s
= "Home"; break;
1556 case KEY_END
: s
= "End"; break;
1557 case KEY_DEL
: s
= "Delete"; break; /* Not necessary DEL! */
1558 case KEY_JUNK
: s
= "Junk!"; break;
1559 case BADESC
: s
= "Bad Esc"; break;
1560 case NO_OP_IDLE
: s
= "NO_OP_IDLE"; break;
1561 case NO_OP_COMMAND
: s
= "NO_OP_COMMAND"; break;
1562 case KEY_RESIZE
: s
= "KEY_RESIZE"; break;
1563 case KEY_UTF8
: s
= "KEY_UTF8"; break;
1564 case KEY_MOUSE
: s
= "KEY_MOUSE"; break;
1565 case KEY_SCRLUPL
: s
= "KEY_SCRLUPL"; break;
1566 case KEY_SCRLDNL
: s
= "KEY_SCRLDNL"; break;
1567 case KEY_SCRLTO
: s
= "KEY_SCRLTO"; break;
1568 case KEY_XTERM_MOUSE
: s
= "KEY_XTERM_MOUSE"; break;
1569 case KEY_DOUBLE_ESC
: s
= "KEY_DOUBLE_ESC"; break;
1570 case CTRL_KEY_UP
: s
= "Ctrl Up Arrow"; break;
1571 case CTRL_KEY_DOWN
: s
= "Ctrl Down Arrow"; break;
1572 case CTRL_KEY_RIGHT
: s
= "Ctrl Right Arrow"; break;
1573 case CTRL_KEY_LEFT
: s
= "Ctrl Left Arrow"; break;
1586 snprintf(s
= buf
, sizeof(buf
), "F%ld", (long) (c
- PF1
+ 1));
1590 if(c
< ' ' || (c
>= 0x80 && c
< 0xA0)){
1595 d
= (c
& 0x1f) + 'A' - 1;
1596 snprintf(s
= buf
, sizeof(buf
), "%c%c", c1
? '~' : '^', d
);
1599 memset(buf
, 0, sizeof(buf
));
1600 utf8_put((unsigned char *) buf
, (unsigned long) c
);
1610 /*----------------------------------------------------------------------
1611 Complain about bogus input
1613 Args: ch -- input command to complain about
1614 help -- string indicating where to get help
1618 bogus_command(UCS cmd
, char *help
)
1620 if(cmd
== ctrl('Q') || cmd
== ctrl('S'))
1621 q_status_message1(SM_ASYNC
, 0, 2,
1622 "%s char received. Set \"preserve-start-stop\" feature in Setup/Config.",
1623 pretty_command(cmd
));
1624 else if(cmd
== KEY_JUNK
)
1625 q_status_message3(SM_ORDER
, 0, 2,
1626 "Invalid key pressed.%s%s%s",
1627 (help
) ? " Use " : "",
1629 (help
) ? " for help" : "");
1631 q_status_message4(SM_ORDER
, 0, 2,
1632 "Command \"%s\" not defined for this screen.%s%s%s",
1633 pretty_command(cmd
),
1634 (help
) ? " Use " : "",
1636 (help
) ? " for help" : "");
1641 bogus_utf8_command(char *cmd
, char *help
)
1643 q_status_message4(SM_ORDER
, 0, 2,
1644 "Command \"%s\" not defined for this screen.%s%s%s",
1646 (help
) ? " Use " : "",
1648 (help
) ? " for help" : "");
1652 /*----------------------------------------------------------------------
1653 Execute FLAG message command
1655 Args: state -- Various satate info
1656 msgmap -- map of c-client to local message numbers
1658 Result: with side effect of "current" message FLAG flag set or UNset
1662 cmd_flag(struct pine
*state
, MSGNO_S
*msgmap
, int aopt
)
1664 char *flagit
, *seq
, *screen_text
[20], **exp
, **p
, *answer
= NULL
;
1665 char *keyword_array
[2];
1666 int user_defined_flags
= 0, mailbox_flags
= 0;
1667 int directly_to_maint_screen
= 0;
1668 long unflagged
, flagged
, flags
, rawno
;
1669 MESSAGECACHE
*mc
= NULL
;
1671 int i
, cnt
, is_set
, trouble
= 0, rv
= 0;
1673 struct flag_table
*fp
, *ftbl
= NULL
;
1674 struct flag_screen flag_screen
;
1675 static char *flag_screen_text1
[] = {
1676 N_(" Set desired flags for current message below. An 'X' means set"),
1677 N_(" it, and a ' ' means to unset it. Choose \"Exit\" when finished."),
1681 static char *flag_screen_text2
[] = {
1682 N_(" Set desired flags below for selected messages. A '?' means to"),
1683 N_(" leave the flag unchanged, 'X' means to set it, and a ' ' means"),
1684 N_(" to unset it. Use the \"Return\" key to toggle, and choose"),
1685 N_(" \"Exit\" when finished."),
1689 static struct flag_table default_ftbl
[] = {
1690 {N_("Important"), h_flag_important
, F_FLAG
, 0, 0, NULL
, NULL
},
1691 {N_("New"), h_flag_new
, F_SEEN
, 0, 0, NULL
, NULL
},
1692 {N_("Answered"), h_flag_answered
, F_ANS
, 0, 0, NULL
, NULL
},
1693 {N_("Forwarded"), h_flag_forwarded
, F_FWD
, 0, 0, NULL
, NULL
},
1694 {N_("Deleted"), h_flag_deleted
, F_DEL
, 0, 0, NULL
, NULL
},
1695 {NULL
, NO_HELP
, 0, 0, 0, NULL
, NULL
}
1698 /* Only check for dead stream for now. Should check permanent flags
1701 if(!(any_messages(msgmap
, NULL
, "to Flag") && can_set_flag(state
, "flag", 1)))
1704 if(sp_io_error_on_stream(state
->mail_stream
)){
1705 sp_set_io_error_on_stream(state
->mail_stream
, 0);
1706 pine_mail_check(state
->mail_stream
); /* forces write */
1712 user_defined_flags
= 0;
1718 /* count how large ftbl will be */
1719 for(cnt
= 0; default_ftbl
[cnt
].name
; cnt
++)
1722 /* add user flags */
1723 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
){
1724 if(!((kw
->nick
&& !strucmp(FORWARDED_FLAG
, kw
->nick
)) || (kw
->kw
&& !strucmp(FORWARDED_FLAG
, kw
->kw
)))){
1725 user_defined_flags
++;
1731 * Add mailbox flags that aren't user-defined flags.
1732 * Don't consider it if it matches either one of our defined
1733 * keywords or one of our defined nicknames for a keyword.
1735 for(i
= 0; stream_to_user_flag_name(state
->mail_stream
, i
); i
++){
1738 q
= stream_to_user_flag_name(state
->mail_stream
, i
);
1740 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
){
1741 if((kw
->nick
&& !strucmp(kw
->nick
, q
)) || (kw
->kw
&& !strucmp(kw
->kw
, q
)))
1746 if(!kw
&& !(q
&& !strucmp(FORWARDED_FLAG
, q
))){
1752 cnt
+= (user_defined_flags
? 2 : 0) + (mailbox_flags
? 2 : 0);
1754 /* set up ftbl, first the system flags */
1755 ftbl
= (struct flag_table
*) fs_get((cnt
+1) * sizeof(*ftbl
));
1756 memset(ftbl
, 0, (cnt
+1) * sizeof(*ftbl
));
1757 for(i
= 0, fp
= ftbl
; default_ftbl
[i
].name
; i
++, fp
++){
1758 fp
->name
= cpystr(default_ftbl
[i
].name
);
1759 fp
->help
= default_ftbl
[i
].help
;
1760 fp
->flag
= default_ftbl
[i
].flag
;
1761 fp
->set
= default_ftbl
[i
].set
;
1762 fp
->ukn
= default_ftbl
[i
].ukn
;
1765 if(user_defined_flags
){
1766 fp
->flag
= F_COMMENT
;
1767 fp
->name
= cpystr("");
1769 fp
->flag
= F_COMMENT
;
1770 len
= strlen(_("User-defined Keywords from Setup/Config"));
1771 fp
->name
= (char *) fs_get((len
+6+6+1) * sizeof(char));
1772 snprintf(fp
->name
, len
+6+6+1, "----- %s -----", _("User-defined Keywords from Setup/Config"));
1776 /* then the user-defined keywords */
1777 if(user_defined_flags
)
1778 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
){
1779 if(!((kw
->nick
&& !strucmp(FORWARDED_FLAG
, kw
->nick
))
1780 || (kw
->kw
&& !strucmp(FORWARDED_FLAG
, kw
->kw
)))){
1781 fp
->name
= cpystr(kw
->nick
? kw
->nick
: kw
->kw
? kw
->kw
: "");
1782 fp
->keyword
= cpystr(kw
->kw
? kw
->kw
: "");
1783 if(kw
->nick
&& kw
->kw
){
1786 l
= strlen(kw
->kw
)+2;
1787 fp
->comment
= (char *) fs_get((l
+1) * sizeof(char));
1788 snprintf(fp
->comment
, l
+1, "(%.*s)", (int) strlen(kw
->kw
), kw
->kw
);
1789 fp
->comment
[l
] = '\0';
1792 fp
->help
= h_flag_user_flag
;
1793 fp
->flag
= F_KEYWORD
;
1801 fp
->flag
= F_COMMENT
;
1802 fp
->name
= cpystr("");
1804 fp
->flag
= F_COMMENT
;
1805 len
= strlen(_("Other keywords in the mailbox that are not user-defined"));
1806 fp
->name
= (char *) fs_get((len
+6+6+1) * sizeof(char));
1807 snprintf(fp
->name
, len
+6+6+1, "----- %s -----", _("Other keywords in the mailbox that are not user-defined"));
1811 /* then the extra mailbox-defined keywords */
1813 for(i
= 0; stream_to_user_flag_name(state
->mail_stream
, i
); i
++){
1816 q
= stream_to_user_flag_name(state
->mail_stream
, i
);
1818 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
){
1819 if((kw
->nick
&& !strucmp(kw
->nick
, q
)) || (kw
->kw
&& !strucmp(kw
->kw
, q
)))
1824 if(!kw
&& !(q
&& !strucmp(FORWARDED_FLAG
, q
))){
1825 fp
->name
= cpystr(q
);
1826 fp
->keyword
= cpystr(q
);
1827 fp
->help
= h_flag_user_flag
;
1828 fp
->flag
= F_KEYWORD
;
1835 flag_screen
.flag_table
= &ftbl
;
1836 flag_screen
.explanation
= screen_text
;
1838 if(MCMD_ISAGG(aopt
)){
1839 if(!pseudo_selected(ps_global
->mail_stream
, msgmap
)){
1840 free_flag_table(&ftbl
);
1844 exp
= flag_screen_text2
;
1845 for(fp
= ftbl
; fp
->name
; fp
++){
1846 fp
->set
= CMD_FLAG_UNKN
; /* set to unknown */
1850 else if(state
->mail_stream
1851 && (rawno
= mn_m2raw(msgmap
, mn_get_cur(msgmap
))) > 0L
1852 && rawno
<= state
->mail_stream
->nmsgs
1853 && (mc
= mail_elt(state
->mail_stream
, rawno
))){
1854 exp
= flag_screen_text1
;
1855 for(fp
= &ftbl
[0]; fp
->name
; fp
++){
1857 if(fp
->flag
== F_KEYWORD
){
1858 /* see if this keyword is defined for this message */
1859 fp
->set
= CMD_FLAG_CLEAR
;
1860 if(user_flag_is_set(state
->mail_stream
,
1861 rawno
, fp
->keyword
))
1862 fp
->set
= CMD_FLAG_SET
;
1864 else if(fp
->flag
== F_FWD
){
1865 /* see if forwarded keyword is defined for this message */
1866 fp
->set
= CMD_FLAG_CLEAR
;
1867 if(user_flag_is_set(state
->mail_stream
,
1868 rawno
, FORWARDED_FLAG
))
1869 fp
->set
= CMD_FLAG_SET
;
1871 else if(fp
->flag
!= F_COMMENT
)
1872 fp
->set
= ((fp
->flag
== F_SEEN
&& !mc
->seen
)
1873 || (fp
->flag
== F_DEL
&& mc
->deleted
)
1874 || (fp
->flag
== F_FLAG
&& mc
->flagged
)
1875 || (fp
->flag
== F_ANS
&& mc
->answered
))
1876 ? CMD_FLAG_SET
: CMD_FLAG_CLEAR
;
1880 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
1881 _("Error accessing message data"));
1882 free_flag_table(&ftbl
);
1886 if(directly_to_maint_screen
)
1887 goto the_maint_screen
;
1890 if (mswin_usedialog ()) {
1891 if (!os_flagmsgdialog (&ftbl
[0])){
1892 free_flag_table(&ftbl
);
1899 int use_maint_screen
;
1900 int keyword_shortcut
= 0;
1902 use_maint_screen
= F_ON(F_FLAG_SCREEN_DFLT
, ps_global
);
1904 if(!use_maint_screen
){
1906 * We're going to call cmd_flag_prompt(). We need
1907 * to decide whether or not to offer the keyword setting
1908 * shortcut. We'll offer it if the user has the feature
1909 * enabled AND there are some possible keywords that could
1912 if(F_ON(F_FLAG_SCREEN_KW_SHORTCUT
, ps_global
)){
1913 for(fp
= &ftbl
[0]; fp
->name
&& !keyword_shortcut
; fp
++){
1914 if(fp
->flag
== F_KEYWORD
){
1918 first_char
= (fp
->name
&& fp
->name
[0])
1920 if(isascii(first_char
) && isupper(first_char
))
1921 first_char
= tolower((unsigned char) first_char
);
1923 for(tp
=flag_text_opt
; tp
->ch
!= -1; tp
++)
1924 if(tp
->ch
== first_char
)
1933 use_maint_screen
= !cmd_flag_prompt(state
, &flag_screen
,
1938 if(use_maint_screen
){
1939 for(p
= &screen_text
[0]; *exp
; p
++, exp
++)
1944 directly_to_maint_screen
= flag_maintenance_screen(state
, &flag_screen
);
1948 /* reaquire the elt pointer */
1949 mc
= (state
->mail_stream
1950 && (rawno
= mn_m2raw(msgmap
, mn_get_cur(msgmap
))) > 0L
1951 && rawno
<= state
->mail_stream
->nmsgs
)
1952 ? mail_elt(state
->mail_stream
, rawno
) : NULL
;
1954 for(fp
= ftbl
; mc
&& fp
->name
; fp
++){
1958 if((!MCMD_ISAGG(aopt
) && fp
->set
!= !mc
->seen
)
1959 || (MCMD_ISAGG(aopt
) && fp
->set
!= CMD_FLAG_UNKN
)){
1967 unflagged
= F_UNSEEN
;
1974 if((!MCMD_ISAGG(aopt
) && fp
->set
!= mc
->answered
)
1975 || (MCMD_ISAGG(aopt
) && fp
->set
!= CMD_FLAG_UNKN
)){
1976 flagit
= "\\ANSWERED";
1979 unflagged
= F_UNANS
;
1990 if((!MCMD_ISAGG(aopt
) && fp
->set
!= mc
->deleted
)
1991 || (MCMD_ISAGG(aopt
) && fp
->set
!= CMD_FLAG_UNKN
)){
1992 flagit
= "\\DELETED";
1995 unflagged
= F_UNDEL
;
2006 if((!MCMD_ISAGG(aopt
) && fp
->set
!= mc
->flagged
)
2007 || (MCMD_ISAGG(aopt
) && fp
->set
!= CMD_FLAG_UNKN
)){
2008 flagit
= "\\FLAGGED";
2011 unflagged
= F_UNFLAG
;
2022 if(!MCMD_ISAGG(aopt
)){
2023 /* see if forwarded is defined for this message */
2024 is_set
= CMD_FLAG_CLEAR
;
2025 if(user_flag_is_set(state
->mail_stream
,
2026 mn_m2raw(msgmap
, mn_get_cur(msgmap
)),
2028 is_set
= CMD_FLAG_SET
;
2031 if((!MCMD_ISAGG(aopt
) && fp
->set
!= is_set
)
2032 || (MCMD_ISAGG(aopt
) && fp
->set
!= CMD_FLAG_UNKN
)){
2033 flagit
= FORWARDED_FLAG
;
2036 unflagged
= F_UNFWD
;
2047 if(!MCMD_ISAGG(aopt
)){
2048 /* see if this keyword is defined for this message */
2049 is_set
= CMD_FLAG_CLEAR
;
2050 if(user_flag_is_set(state
->mail_stream
,
2051 mn_m2raw(msgmap
, mn_get_cur(msgmap
)),
2053 is_set
= CMD_FLAG_SET
;
2056 if((!MCMD_ISAGG(aopt
) && fp
->set
!= is_set
)
2057 || (MCMD_ISAGG(aopt
) && fp
->set
!= CMD_FLAG_UNKN
)){
2058 flagit
= fp
->keyword
;
2059 keyword_array
[0] = fp
->keyword
;
2060 keyword_array
[1] = NULL
;
2063 unflagged
= F_UNKEYWORD
;
2067 unflagged
= F_KEYWORD
;
2079 && (seq
= currentf_sequence(state
->mail_stream
, msgmap
,
2080 unflagged
, &flagged
, unflagged
& F_DEL
,
2081 (fp
->flag
== F_KEYWORD
2082 && unflagged
== F_KEYWORD
)
2083 ? keyword_array
: NULL
,
2084 (fp
->flag
== F_KEYWORD
2085 && unflagged
== F_UNKEYWORD
)
2086 ? keyword_array
: NULL
))){
2088 * For user keywords, we may have to create the flag in
2089 * the folder if it doesn't already exist and we are setting
2090 * it (as opposed to clearing it). Mail_flag will
2091 * do that for us, but it's failure isn't very friendly
2092 * error-wise. So we try to make it a little smoother.
2094 if(!(fp
->flag
== F_KEYWORD
|| fp
->flag
== F_FWD
) || !fp
->set
2095 || ((i
=user_flag_index(state
->mail_stream
, flagit
)) >= 0
2097 mail_flag(state
->mail_stream
, seq
, flagit
, flags
);
2099 /* trouble, see if we can add the user flag */
2100 if(state
->mail_stream
->kwd_create
)
2101 mail_flag(state
->mail_stream
, seq
, flagit
, flags
);
2105 if(some_user_flags_defined(state
->mail_stream
))
2106 q_status_message(SM_ORDER
, 3, 4,
2107 _("No more keywords allowed in this folder!"));
2108 else if(fp
->flag
== F_FWD
)
2109 q_status_message(SM_ORDER
, 3, 4,
2110 _("Cannot add keywords for this folder so cannot set Forwarded flag"));
2112 q_status_message(SM_ORDER
, 3, 4,
2113 _("Cannot add keywords for this folder"));
2117 fs_give((void **) &seq
);
2118 if(flagged
&& !trouble
){
2119 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%slagged%s%s%s%s%s message%s%s \"%s\"",
2120 (fp
->set
) ? "F" : "Unf",
2121 MCMD_ISAGG(aopt
) ? " " : "",
2122 MCMD_ISAGG(aopt
) ? long2string(flagged
) : "",
2123 (MCMD_ISAGG(aopt
) && flagged
!= mn_total_cur(msgmap
))
2125 (MCMD_ISAGG(aopt
) && flagged
!= mn_total_cur(msgmap
))
2126 ? comatose(mn_total_cur(msgmap
)) : "",
2127 (MCMD_ISAGG(aopt
) && flagged
!= mn_total_cur(msgmap
))
2129 MCMD_ISAGG(aopt
) ? plural(flagged
) : " ",
2130 MCMD_ISAGG(aopt
) ? "" : long2string(mn_get_cur(msgmap
)),
2132 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
2133 q_status_message(SM_ORDER
, 0, 2, answer
= tmp_20k_buf
);
2139 free_flag_table(&ftbl
);
2141 if(directly_to_maint_screen
)
2144 if(MCMD_ISAGG(aopt
))
2145 restore_selected(msgmap
);
2148 q_status_message(SM_ORDER
, 0, 2, _("No flags changed."));
2154 /*----------------------------------------------------------------------
2155 Offer concise status line flag prompt
2157 Args: state -- Various satate info
2158 flags -- flags to offer setting
2160 Result: TRUE if flag to set specified in flags struct or FALSE otw
2164 cmd_flag_prompt(struct pine
*state
, struct flag_screen
*flags
, int allow_keyword_shortcuts
)
2166 int r
, setflag
= 1, first_char
;
2167 struct flag_table
*fp
;
2169 char *ftext
, *ftext_not
;
2170 static char *flag_text
=
2171 N_("Flag New, Deleted, Answered, Forwarded or Important ? ");
2172 static char *flag_text_ak
=
2173 N_("Flag New, Deleted, Answered, Forwarded, Important or Keyword initial ? ");
2174 static char *flag_text_not
=
2175 N_("Flag !New, !Deleted, !Answered, !Forwarded, or !Important ? ");
2176 static char *flag_text_ak_not
=
2177 N_("Flag !New, !Deleted, !Answered, !Forwarded, !Important or !Keyword initial ? ");
2179 if(allow_keyword_shortcuts
){
2181 ESCKEY_S
*dp
, *sp
, *tp
;
2183 for(sp
=flag_text_opt
; sp
->ch
!= -1; sp
++)
2186 for(fp
=(flags
->flag_table
? *flags
->flag_table
: NULL
); fp
->name
; fp
++)
2187 if(fp
->flag
== F_KEYWORD
)
2190 /* set up an ESCKEY_S list which includes invisible keys for keywords */
2191 ek
= (ESCKEY_S
*) fs_get((cnt
+ 1) * sizeof(*ek
));
2192 memset(ek
, 0, (cnt
+1) * sizeof(*ek
));
2193 for(dp
=ek
, sp
=flag_text_opt
; sp
->ch
!= -1; sp
++, dp
++)
2196 for(fp
=(flags
->flag_table
? *flags
->flag_table
: NULL
); fp
->name
; fp
++){
2197 if(fp
->flag
== F_KEYWORD
){
2198 first_char
= (fp
->name
&& fp
->name
[0]) ? fp
->name
[0] : -2;
2199 if(isascii(first_char
) && isupper(first_char
))
2200 first_char
= tolower((unsigned char) first_char
);
2203 * Check to see if an earlier keyword in the list, or one of
2204 * the builtin system letters already uses this character.
2205 * If so, the first one wins.
2207 for(tp
=ek
; tp
->ch
!= 0; tp
++)
2208 if(tp
->ch
== first_char
)
2212 continue; /* skip it, already used that char */
2214 dp
->ch
= first_char
;
2215 dp
->rval
= first_char
;
2223 ftext
= _(flag_text_ak
);
2224 ftext_not
= _(flag_text_ak_not
);
2228 ftext
= _(flag_text
);
2229 ftext_not
= _(flag_text_not
);
2233 r
= radio_buttons(setflag
? ftext
: ftext_not
,
2234 -FOOTER_ROWS(state
), ek
, '*', SEQ_EXCEPTION
-1,
2235 NO_HELP
, RB_NORM
| RB_SEQ_SENSITIVE
);
2237 * It is SEQ_EXCEPTION-1 just so that it is some value that isn't
2238 * being used otherwise. The keywords use up all the possible
2239 * letters, so a negative number is good, but it has to be different
2240 * from other negative return values.
2242 if(r
== SEQ_EXCEPTION
-1) /* ol'cancelrooney */
2244 else if(r
== 10) /* return and goto flag screen */
2246 else if(r
== '!') /* flip intention */
2252 for(fp
= (flags
->flag_table
? *flags
->flag_table
: NULL
); fp
->name
; fp
++){
2253 if(r
== 'n' || r
== '*' || r
== 'd' || r
== 'a' || r
== 'f'){
2254 if((r
== 'n' && fp
->flag
== F_SEEN
)
2255 || (r
== '*' && fp
->flag
== F_FLAG
)
2256 || (r
== 'd' && fp
->flag
== F_DEL
)
2257 || (r
== 'f' && fp
->flag
== F_FWD
)
2258 || (r
== 'a' && fp
->flag
== F_ANS
)){
2259 fp
->set
= setflag
? CMD_FLAG_SET
: CMD_FLAG_CLEAR
;
2263 else if(allow_keyword_shortcuts
&& fp
->flag
== F_KEYWORD
){
2264 first_char
= (fp
->name
&& fp
->name
[0]) ? fp
->name
[0] : -2;
2265 if(isascii(first_char
) && isupper(first_char
))
2266 first_char
= tolower((unsigned char) first_char
);
2268 if(r
== first_char
){
2269 fp
->set
= setflag
? CMD_FLAG_SET
: CMD_FLAG_CLEAR
;
2275 if(ek
!= flag_text_opt
)
2276 fs_give((void **) &ek
);
2283 * (*ft) is an array of flag_table entries.
2286 free_flag_table(struct flag_table
**ft
)
2288 struct flag_table
*fp
;
2291 for(fp
= (*ft
); fp
->name
; fp
++){
2293 fs_give((void **) &fp
->name
);
2296 fs_give((void **) &fp
->keyword
);
2299 fs_give((void **) &fp
->comment
);
2302 fs_give((void **) ft
);
2307 /*----------------------------------------------------------------------
2308 Execute REPLY message command
2310 Args: state -- Various satate info
2311 msgmap -- map of c-client to local message numbers
2313 Result: reply sent or not
2317 cmd_reply(struct pine
*state
, MSGNO_S
*msgmap
, int aopt
)
2321 if(any_messages(msgmap
, NULL
, "to Reply to")){
2322 if(MCMD_ISAGG(aopt
) && !pseudo_selected(state
->mail_stream
, msgmap
))
2325 rv
= reply(state
, NULL
);
2327 if(MCMD_ISAGG(aopt
))
2328 restore_selected(msgmap
);
2330 state
->mangled_screen
= 1;
2337 /*----------------------------------------------------------------------
2338 Execute FORWARD message command
2340 Args: state -- Various satate info
2341 msgmap -- map of c-client to local message numbers
2343 Result: selected message[s] forwarded or not
2347 cmd_forward(struct pine
*state
, MSGNO_S
*msgmap
, int aopt
)
2351 if(any_messages(msgmap
, NULL
, "to Forward")){
2352 if(MCMD_ISAGG(aopt
) && !pseudo_selected(state
->mail_stream
, msgmap
))
2355 rv
= forward(state
, NULL
);
2357 if(MCMD_ISAGG(aopt
))
2358 restore_selected(msgmap
);
2360 state
->mangled_screen
= 1;
2367 /*----------------------------------------------------------------------
2368 Execute BOUNCE message command
2370 Args: state -- Various satate info
2371 msgmap -- map of c-client to local message numbers
2372 aopt -- aggregate options
2374 Result: selected message[s] bounced or not
2378 cmd_bounce(struct pine
*state
, MSGNO_S
*msgmap
, int aopt
)
2381 ACTION_S
*role
= NULL
;
2383 if(any_messages(msgmap
, NULL
, "to Bounce")){
2384 if(MCMD_ISAGG(aopt
) && !pseudo_selected(state
->mail_stream
, msgmap
))
2387 if(MCMD_ISAGG(aopt
)){ /* check for possible role */
2391 if(nonempty_patterns(ROLE_DO_ROLES
, &pstate
) && first_pattern(&pstate
)){
2392 static ESCKEY_S yesno_opts
[] = {
2393 {'y', 'y', "Y", N_("Yes")},
2394 {'n', 'n', "N", N_("No")},
2398 action
= radio_buttons(_("Bounce messages using a role? "),
2399 -FOOTER_ROWS(state
), yesno_opts
,
2400 'y', 'x', h_role_compose
, RB_NORM
);
2402 void (*prev_screen
)(struct pine
*) = NULL
, (*redraw
)(void) = NULL
;
2404 redraw
= state
->redrawer
;
2405 state
->redrawer
= NULL
;
2406 prev_screen
= state
->prev_screen
;
2408 state
->next_screen
= SCREEN_FUN_NULL
;
2410 if(role_select_screen(state
, &role
, MC_BOUNCE
) < 0)
2411 cmd_cancelled(_("Bounce"));
2414 role
= combine_inherited_role(role
);
2416 role
= (ACTION_S
*) fs_get(sizeof(*role
));
2417 memset((void *) role
, 0, sizeof(*role
));
2418 role
->nick
= cpystr("Default Role");
2425 state
->next_screen
= prev_screen
;
2426 state
->redrawer
= redraw
;
2427 state
->mangled_screen
= 1;
2433 /* When we bounce a message, we will leave the original message
2434 * intact, which means that it will not be signed or encrypted,
2435 * so we turn off signing and encrypting now. It will be turned
2436 * on again in send_exit_for_pico().
2438 if(ps_global
->smime
)
2439 ps_global
->smime
->do_sign
= ps_global
->smime
->do_encrypt
= 0;
2442 rv
= bounce(state
, role
);
2447 if(MCMD_ISAGG(aopt
))
2448 restore_selected(msgmap
);
2450 state
->mangled_footer
= 1;
2457 /*----------------------------------------------------------------------
2458 Execute save message command: prompt for folder and call function to save
2460 Args: screen_line -- Line on the screen to prompt on
2461 message -- The MESSAGECACHE entry of message to save
2463 Result: The folder lister can be called to make selection; mangled screen set
2465 This does the prompting for the folder name to save to, possibly calling
2466 up the folder display for selection of folder by user.
2469 cmd_save(struct pine
*state
, MAILSTREAM
*stream
, MSGNO_S
*msgmap
, int aopt
, CmdWhere in_index
)
2471 char newfolder
[MAILTMPLEN
], nmsgs
[32], *nick
;
2472 int we_cancel
= 0, rv
= 0, save_flags
;
2474 CONTEXT_S
*cntxt
= NULL
;
2476 SaveDel del
= DontAsk
;
2477 SavePreserveOrder pre
= DontAskPreserve
;
2479 dprint((4, "\n - saving message -\n"));
2481 if(MCMD_ISAGG(aopt
) && !pseudo_selected(stream
, msgmap
))
2484 state
->ugly_consider_advancing_bit
= 0;
2485 if(F_OFF(F_SAVE_PARTIAL_WO_CONFIRM
, state
)
2486 && msgno_any_deletedparts(stream
, msgmap
)
2487 && want_to(_("Saved copy will NOT include entire message! Continue"),
2488 'y', 'n', NO_HELP
, WT_FLUSH_IN
| WT_SEQ_SENSITIVE
) != 'y'){
2489 restore_selected(msgmap
);
2490 cmd_cancelled("Save message");
2494 raw
= mn_m2raw(msgmap
, mn_get_cur(msgmap
));
2496 if(mn_total_cur(msgmap
) <= 1L){
2497 snprintf(nmsgs
, sizeof(nmsgs
), "Msg #%ld ", mn_get_cur(msgmap
));
2498 nmsgs
[sizeof(nmsgs
)-1] = '\0';
2499 e
= pine_mail_fetchstructure(stream
, raw
, NULL
);
2501 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
2502 _("Can't save message. Error accessing folder"));
2503 restore_selected(msgmap
);
2508 snprintf(nmsgs
, sizeof(nmsgs
), "%s msgs ", comatose(mn_total_cur(msgmap
)));
2509 nmsgs
[sizeof(nmsgs
)-1] = '\0';
2511 /* e is just used to get a default save folder from the first msg */
2512 e
= pine_mail_fetchstructure(stream
,
2513 mn_m2raw(msgmap
, mn_first_cur(msgmap
)),
2517 del
= (!READONLY_FOLDER(stream
) && F_OFF(F_SAVE_WONT_DELETE
, ps_global
))
2519 if(mn_total_cur(msgmap
) > 1L)
2520 pre
= F_OFF(F_AGG_SEQ_COPY
, ps_global
) ? Preserve
: NoPreserve
;
2522 pre
= DontAskPreserve
;
2524 if(save_prompt(state
, &cntxt
, newfolder
, sizeof(newfolder
), nmsgs
, e
,
2525 raw
, NULL
, &del
, &pre
)){
2527 if(ps_global
&& ps_global
->ttyo
){
2528 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
2529 ps_global
->mangled_footer
= 1;
2532 save_flags
= SV_FIX_DELS
;
2533 if(pre
== RetPreserve
)
2534 save_flags
|= SV_PRESERVE
;
2536 save_flags
|= SV_DELETE
;
2537 if(ps_global
->context_list
== cntxt
&& !strucmp(newfolder
, ps_global
->inbox_name
))
2538 save_flags
|= SV_INBOXWOCNTXT
;
2540 we_cancel
= busy_cue(_("Saving"), NULL
, 1);
2541 i
= save(state
, stream
, cntxt
, newfolder
, msgmap
, save_flags
);
2545 if(i
== mn_total_cur(msgmap
)){
2547 if(mn_total_cur(msgmap
) <= 1L){
2548 int need
, avail
= ps_global
->ttyo
->screen_cols
- 2;
2549 int lennick
, lenfldr
;
2552 && ps_global
->context_list
->next
2553 && context_isambig(newfolder
)){
2554 lennick
= MIN(strlen(cntxt
->nickname
), 500);
2555 lenfldr
= MIN(strlen(newfolder
), 500);
2556 need
= 27 + strlen(long2string(mn_get_cur(msgmap
))) +
2560 need
-= MIN(lennick
-10, need
-avail
);
2561 lennick
-= MIN(lennick
-10, need
-avail
);
2564 if(need
> avail
&& lenfldr
> 10)
2565 lenfldr
-= MIN(lenfldr
-10, need
-avail
);
2568 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,
2569 "Message %s copied to \"%s\" in <%s>",
2570 long2string(mn_get_cur(msgmap
)),
2571 short_str(newfolder
, (char *)(tmp_20k_buf
+1000), 1000,
2573 short_str(cntxt
->nickname
,
2574 (char *)(tmp_20k_buf
+2000), 1000,
2576 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
2578 else if((nick
=folder_is_target_of_nick(newfolder
, cntxt
)) != NULL
){
2579 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,
2580 "Message %s copied to \"%s\"",
2581 long2string(mn_get_cur(msgmap
)),
2583 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
2586 char *f
= " folder";
2588 lenfldr
= MIN(strlen(newfolder
), 500);
2589 need
= 28 + strlen(long2string(mn_get_cur(msgmap
))) +
2594 if(need
> avail
&& lenfldr
> 10)
2595 lenfldr
-= MIN(lenfldr
-10, need
-avail
);
2598 snprintf(tmp_20k_buf
,SIZEOF_20KBUF
,
2599 "Message %s copied to%s \"%s\"",
2600 long2string(mn_get_cur(msgmap
)), f
,
2601 short_str(newfolder
, (char *)(tmp_20k_buf
+1000), 1000,
2603 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
2607 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s messages saved",
2608 comatose(mn_total_cur(msgmap
)));
2609 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
2613 strncat(tmp_20k_buf
, " and deleted", SIZEOF_20KBUF
-strlen(tmp_20k_buf
)-1);
2614 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
2617 q_status_message(SM_ORDER
, 0, 3, tmp_20k_buf
);
2619 if(!MCMD_ISAGG(aopt
) && F_ON(F_SAVE_ADVANCES
, state
)){
2620 if(sp_new_mail_count(stream
))
2621 process_filter_patterns(stream
, msgmap
,
2622 sp_new_mail_count(stream
));
2624 mn_inc_cur(stream
, msgmap
,
2625 (in_index
== View
&& THREADING()
2626 && sp_viewing_a_thread(stream
))
2628 : (in_index
== View
)
2629 ? MH_ANYTHD
: MH_NONE
);
2632 state
->ugly_consider_advancing_bit
= 1;
2636 if(MCMD_ISAGG(aopt
)) /* straighten out fakes */
2637 restore_selected(msgmap
);
2640 update_titlebar_status(); /* make sure they see change */
2647 role_compose(struct pine
*state
)
2651 if(F_ON(F_ALT_ROLE_MENU
, state
) && mn_get_total(state
->msgmap
) > 0L){
2654 if(nonempty_patterns(ROLE_DO_ROLES
, &pstate
) && first_pattern(&pstate
)){
2655 action
= radio_buttons(_("Compose, Forward, Reply, or Bounce? "),
2656 -FOOTER_ROWS(state
), choose_action
,
2657 'c', 'x', h_role_compose
, RB_NORM
);
2660 q_status_message(SM_ORDER
, 0, 3,
2661 _("No roles available. Use Setup/Rules to add roles."));
2668 if(action
== 'c' || action
== 'r' || action
== 'f' || action
== 'b'){
2669 ACTION_S
*role
= NULL
;
2670 void (*prev_screen
)(struct pine
*) = NULL
, (*redraw
)(void) = NULL
;
2672 redraw
= state
->redrawer
;
2673 state
->redrawer
= NULL
;
2674 prev_screen
= state
->prev_screen
;
2676 state
->next_screen
= SCREEN_FUN_NULL
;
2679 if(role_select_screen(state
, &role
,
2680 action
== 'f' ? MC_FORWARD
:
2681 action
== 'r' ? MC_REPLY
:
2682 action
== 'b' ? MC_BOUNCE
:
2683 action
== 'c' ? MC_COMPOSE
: 0) < 0){
2684 cmd_cancelled(action
== 'f' ? _("Forward") :
2685 action
== 'r' ? _("Reply") :
2686 action
== 'c' ? _("Composition") : _("Bounce"));
2687 state
->next_screen
= prev_screen
;
2688 state
->redrawer
= redraw
;
2689 state
->mangled_screen
= 1;
2693 * If default role was selected (NULL) we need to make
2694 * up a role which won't do anything, but will cause
2695 * compose_mail to think there's already a role so that
2696 * it won't try to confirm the default.
2699 role
= combine_inherited_role(role
);
2701 role
= (ACTION_S
*) fs_get(sizeof(*role
));
2702 memset((void *) role
, 0, sizeof(*role
));
2703 role
->nick
= cpystr("Default Role");
2706 state
->redrawer
= NULL
;
2709 compose_mail(NULL
, NULL
, role
, NULL
, NULL
);
2713 (void) reply(state
, role
);
2717 (void) forward(state
, role
);
2721 (void) bounce(state
, role
);
2728 state
->next_screen
= prev_screen
;
2729 state
->redrawer
= redraw
;
2730 state
->mangled_screen
= 1;
2736 /*----------------------------------------------------------------------
2737 Do the dirty work of prompting the user for a folder name
2740 nfldr should be a buffer at least MAILTMPLEN long
2741 dela -- a pointer to a SaveDel. If it is
2742 DontAsk on input, don't offer Delete prompt
2743 Del on input, offer Delete command with default of Delete
2745 RetDel and RetNoDel are return values
2752 save_prompt(struct pine
*state
, CONTEXT_S
**cntxt
, char *nfldr
, size_t len_nfldr
,
2753 char *nmsgs
, ENVELOPE
*env
, long int rawmsgno
, char *section
,
2754 SaveDel
*dela
, SavePreserveOrder
*prea
)
2756 int rc
, ku
= -1, n
, flags
, last_rc
= 0, saveable_count
= 0, done
= 0;
2757 int delindex
, preindex
, r
;
2758 char prompt
[6*MAX_SCREEN_COLS
+1], *p
, expanded
[MAILTMPLEN
];
2759 char *buf
= tmp_20k_buf
;
2763 SaveDel del
= DontAsk
;
2764 SavePreserveOrder pre
= DontAskPreserve
;
2765 char *deltext
= NULL
;
2766 static HISTORY_S
*history
= NULL
;
2771 alpine_panic("no context ptr in save_prompt");
2773 init_hist(&history
, HISTSIZE
);
2775 if(!(folder
= save_get_default(state
, env
, rawmsgno
, section
, cntxt
)))
2776 return(0); /* message expunged! */
2778 /* how many context's can be saved to... */
2779 for(tc
= state
->context_list
; tc
; tc
= tc
->next
)
2783 /* set up extra command option keys */
2785 ekey
[rc
].ch
= ctrl('T');
2787 ekey
[rc
].name
= "^T";
2788 /* TRANSLATORS: command means go to Folders list */
2789 ekey
[rc
++].label
= N_("To Fldrs");
2791 if(saveable_count
> 1){
2792 ekey
[rc
].ch
= ctrl('P');
2794 ekey
[rc
].name
= "^P";
2795 ekey
[rc
++].label
= N_("Prev Collection");
2797 ekey
[rc
].ch
= ctrl('N');
2799 ekey
[rc
].name
= "^N";
2800 ekey
[rc
++].label
= N_("Next Collection");
2803 if(F_ON(F_ENABLE_TAB_COMPLETE
, ps_global
)){
2806 ekey
[rc
].name
= "TAB";
2807 /* TRANSLATORS: command asks alpine to complete the name when tab is typed */
2808 ekey
[rc
++].label
= N_("Complete");
2811 if(F_ON(F_ENABLE_SUB_LISTS
, ps_global
)){
2812 ekey
[rc
].ch
= ctrl('X');
2814 ekey
[rc
].name
= "^X";
2815 /* TRANSLATORS: list all the matches */
2816 ekey
[rc
++].label
= N_("ListMatches");
2819 if(dela
&& (*dela
== NoDel
|| *dela
== Del
)){
2820 ekey
[rc
].ch
= ctrl('R');
2822 ekey
[rc
].name
= "^R";
2827 if(prea
&& (*prea
== NoPreserve
|| *prea
== Preserve
)){
2828 ekey
[rc
].ch
= ctrl('W');
2830 ekey
[rc
].name
= "^W";
2835 if(saveable_count
> 1 && F_ON(F_DISABLE_SAVE_INPUT_HISTORY
, ps_global
)){
2836 ekey
[rc
].ch
= KEY_UP
;
2839 ekey
[rc
++].label
= "";
2841 ekey
[rc
].ch
= KEY_DOWN
;
2844 ekey
[rc
++].label
= "";
2846 else if(F_OFF(F_DISABLE_SAVE_INPUT_HISTORY
, ps_global
)){
2847 ekey
[rc
].ch
= KEY_UP
;
2851 ekey
[rc
++].label
= "";
2853 ekey
[rc
].ch
= KEY_DOWN
;
2856 ekey
[rc
++].label
= "";
2864 /* only show collection number if more than one available */
2865 if(ps_global
->context_list
->next
)
2866 snprintf(prompt
, sizeof(prompt
), "SAVE%s %sto folder in <%s> [%s] : ",
2867 deltext
? deltext
: "",
2869 short_str((*cntxt
)->nickname
, shortbuf
, sizeof(shortbuf
), 16, EndDots
),
2870 strsquish(buf
, SIZEOF_20KBUF
, folder
, 25));
2872 snprintf(prompt
, sizeof(prompt
), "SAVE%s %sto folder [%s] : ",
2873 deltext
? deltext
: "",
2874 nmsgs
, strsquish(buf
, SIZEOF_20KBUF
, folder
, 40));
2876 prompt
[sizeof(prompt
)-1] = '\0';
2879 * If the prompt won't fit, try removing deltext.
2881 if(state
->ttyo
->screen_cols
< strlen(prompt
) + MIN_OPT_ENT_WIDTH
&& deltext
){
2882 if(ps_global
->context_list
->next
)
2883 snprintf(prompt
, sizeof(prompt
), "SAVE %sto folder in <%s> [%s] : ",
2885 short_str((*cntxt
)->nickname
, shortbuf
, sizeof(shortbuf
), 16, EndDots
),
2886 strsquish(buf
, SIZEOF_20KBUF
, folder
, 25));
2888 snprintf(prompt
, sizeof(prompt
), "SAVE %sto folder [%s] : ",
2889 nmsgs
, strsquish(buf
, SIZEOF_20KBUF
, folder
, 40));
2891 prompt
[sizeof(prompt
)-1] = '\0';
2895 * If the prompt still won't fit, remove the extra info contained
2898 if(state
->ttyo
->screen_cols
< strlen(prompt
) + MIN_OPT_ENT_WIDTH
&& *nmsgs
){
2899 if(ps_global
->context_list
->next
)
2900 snprintf(prompt
, sizeof(prompt
), "SAVE to folder in <%s> [%s] : ",
2901 short_str((*cntxt
)->nickname
, shortbuf
, sizeof(shortbuf
), 16, EndDots
),
2902 strsquish(buf
, SIZEOF_20KBUF
, folder
, 25));
2904 snprintf(prompt
, sizeof(prompt
), "SAVE to folder [%s] : ",
2905 strsquish(buf
, SIZEOF_20KBUF
, folder
, 25));
2907 prompt
[sizeof(prompt
)-1] = '\0';
2911 ekey
[delindex
].label
= (del
== NoDel
) ? "Delete" : "No Delete";
2913 if(pre
!= DontAskPreserve
)
2914 ekey
[preindex
].label
= (pre
== NoPreserve
) ? "Preserve Order" : "Any Order";
2917 if(items_in_hist(history
) > 1){
2918 ekey
[ku
].name
= HISTORY_UP_KEYNAME
;
2919 ekey
[ku
].label
= HISTORY_KEYLABEL
;
2920 ekey
[ku
+1].name
= HISTORY_DOWN_KEYNAME
;
2921 ekey
[ku
+1].label
= HISTORY_KEYLABEL
;
2925 ekey
[ku
].label
= "";
2926 ekey
[ku
+1].name
= "";
2927 ekey
[ku
+1].label
= "";
2931 flags
= OE_APPEND_CURRENT
| OE_SEQ_SENSITIVE
;
2932 rc
= optionally_enter(nfldr
, -FOOTER_ROWS(state
), 0, len_nfldr
,
2933 prompt
, ekey
, help
, &flags
);
2937 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
2938 _("Error reading folder name"));
2943 removing_trailing_white_space(nfldr
);
2944 removing_leading_white_space(nfldr
);
2946 if(*nfldr
|| *folder
){
2947 char *p
, *name
, *fullname
= NULL
;
2948 int exists
, breakout
= FALSE
;
2951 strncpy(nfldr
, folder
, len_nfldr
-1);
2952 nfldr
[len_nfldr
-1] = '\0';
2955 save_hist(history
, nfldr
, 0, (void *) *cntxt
);
2957 if(!(name
= folder_is_nick(nfldr
, FOLDERS(*cntxt
), 0)))
2960 if(update_folder_spec(expanded
, sizeof(expanded
), name
)){
2961 strncpy(name
= nfldr
, expanded
, len_nfldr
-1);
2962 nfldr
[len_nfldr
-1] = '\0';
2965 exists
= folder_name_exists(*cntxt
, name
, &fullname
);
2967 if(exists
== FEX_ERROR
){
2968 q_status_message1(SM_ORDER
, 0, 3,
2969 _("Problem accessing folder \"%s\""),
2975 strncpy(name
= nfldr
, fullname
, len_nfldr
-1);
2976 nfldr
[len_nfldr
-1] = '\0';
2977 fs_give((void **) &fullname
);
2981 if(exists
& FEX_ISFILE
){
2984 else if((exists
& FEX_ISDIR
)){
2985 char tmp
[MAILTMPLEN
];
2989 CONTEXT_S
*fake_context
;
2992 strncpy(tmp
, name
, sizeof(tmp
));
2993 tmp
[sizeof(tmp
)-2-1] = '\0';
2994 if(tmp
[(l
= strlen(tmp
)) - 1] != tc
->dir
->delim
){
2995 if(l
< sizeof(tmp
)){
2996 tmp
[l
] = tc
->dir
->delim
;
2997 strncpy(&tmp
[l
+1], "[]", sizeof(tmp
)-(l
+1));
3001 strncat(tmp
, "[]", sizeof(tmp
)-strlen(tmp
)-1);
3003 tmp
[sizeof(tmp
)-1] = '\0';
3005 fake_context
= new_context(tmp
, 0);
3007 done
= display_folder_list(&fake_context
, nfldr
,
3008 1, folders_for_save
);
3009 free_context(&fake_context
);
3011 else if(tc
->dir
->delim
3012 && (p
= strrindex(name
, tc
->dir
->delim
))
3014 done
= display_folder_list(cntxt
, nfldr
,
3015 1, folders_for_save
);
3017 q_status_message1(SM_ORDER
, 3, 3,
3018 _("\"%s\" is a directory"), name
);
3020 && !((p
=strrindex(name
, tc
->dir
->delim
)) && *(p
+1) == '\0')){
3021 strncpy(tmp
, name
, sizeof(tmp
));
3022 tmp
[sizeof(tmp
)-1] = '\0';
3023 snprintf(nfldr
, len_nfldr
, "%s%c", tmp
, tc
->dir
->delim
);
3027 else{ /* Doesn't exist, create! */
3028 if((fullname
= folder_as_breakout(*cntxt
, name
)) != NULL
){
3029 strncpy(name
= nfldr
, fullname
, len_nfldr
-1);
3030 nfldr
[len_nfldr
-1] = '\0';
3031 fs_give((void **) &fullname
);
3034 switch(create_for_save(*cntxt
, name
)){
3035 case 1 : /* success */
3038 case 0 : /* error */
3039 case -1 : /* declined */
3048 /* else fall thru like they cancelled */
3051 cmd_cancelled("Save message");
3056 r
= display_folder_list(cntxt
, nfldr
, 0, folders_for_save
);
3064 helper(h_save
, _("HELP FOR SAVE"), HLPD_SIMPLE
);
3065 ps_global
->mangled_screen
= 1;
3068 case 4 : /* redraw */
3071 case 10 : /* previous collection */
3072 for(tc
= (*cntxt
)->prev
; tc
; tc
= tc
->prev
)
3079 for(tc2
= (tc
= (*cntxt
))->next
; tc2
; tc2
= tc2
->next
)
3087 case 11 : /* next collection */
3091 if(((*cntxt
) = (*cntxt
)->next
) == NULL
)
3092 (*cntxt
) = ps_global
->context_list
;
3093 while(NEWS_TEST(*cntxt
) && (*cntxt
) != tc
);
3096 case 12 : /* file name completion */
3097 if(!folder_complete(*cntxt
, nfldr
, len_nfldr
, &n
)){
3098 if(n
&& last_rc
== 12 && !(flags
& OE_USER_MODIFIED
)){
3099 r
= display_folder_list(cntxt
, nfldr
, 1, folders_for_save
);
3101 done
++; /* bingo! */
3103 rc
= 0; /* burn last_rc */
3111 case 14 : /* file name completion */
3112 r
= display_folder_list(cntxt
, nfldr
, 2, folders_for_save
);
3114 done
++; /* bingo! */
3116 rc
= 0; /* burn last_rc */
3120 case 15 : /* Delete / No Delete */
3121 del
= (del
== NoDel
) ? Del
: NoDel
;
3122 deltext
= (del
== NoDel
) ? " (no delete)" : " (and delete)";
3125 case 16 : /* Preserve Order or not */
3126 pre
= (pre
== NoPreserve
) ? Preserve
: NoPreserve
;
3130 if((p
= get_prev_hist(history
, nfldr
, 0, (void *) *cntxt
)) != NULL
){
3131 strncpy(nfldr
, p
, len_nfldr
);
3132 nfldr
[len_nfldr
-1] = '\0';
3133 if(history
->hist
[history
->curindex
])
3134 *cntxt
= (CONTEXT_S
*) history
->hist
[history
->curindex
]->cntxt
;
3142 if((p
= get_next_hist(history
, nfldr
, 0, (void *) *cntxt
)) != NULL
){
3143 strncpy(nfldr
, p
, len_nfldr
);
3144 nfldr
[len_nfldr
-1] = '\0';
3145 if(history
->hist
[history
->curindex
])
3146 *cntxt
= (CONTEXT_S
*) history
->hist
[history
->curindex
]->cntxt
;
3154 alpine_panic("Unhandled case");
3161 ps_global
->mangled_footer
= 1;
3167 strncpy(ps_global
->last_save_folder
, nfldr
, sizeof(ps_global
->last_save_folder
)-1);
3168 ps_global
->last_save_folder
[sizeof(ps_global
->last_save_folder
)-1] = '\0';
3170 ps_global
->last_save_context
= *cntxt
;
3173 strncpy(nfldr
, folder
, len_nfldr
-1);
3174 nfldr
[len_nfldr
-1] = '\0';
3177 /* nickname? Copy real name to nfldr */
3179 && context_isambig(nfldr
)
3180 && (p
= folder_is_nick(nfldr
, FOLDERS(*cntxt
), 0))){
3181 strncpy(nfldr
, p
, len_nfldr
-1);
3182 nfldr
[len_nfldr
-1] = '\0';
3185 if(dela
&& (*dela
== NoDel
|| *dela
== Del
))
3186 *dela
= (del
== NoDel
) ? RetNoDel
: RetDel
;
3188 if(prea
&& (*prea
== NoPreserve
|| *prea
== Preserve
))
3189 *prea
= (pre
== NoPreserve
) ? RetNoPreserve
: RetPreserve
;
3195 /*----------------------------------------------------------------------
3196 Prompt user before implicitly creating a folder for saving
3198 Args: context - context to create folder in
3199 folder - folder name to create
3201 Result: 1 on proceed, -1 on decline, 0 on error
3205 create_for_save_prompt(CONTEXT_S
*context
, char *folder
, int sequence_sensitive
)
3207 if(context
&& ps_global
->context_list
->next
&& context_isambig(folder
)){
3208 if(context
->use
& CNTXT_INCMNG
){
3209 snprintf(tmp_20k_buf
,SIZEOF_20KBUF
,
3210 _("\"%.15s%s\" doesn't exist - Add it in FOLDER LIST screen"),
3211 folder
, (strlen(folder
) > 15) ? "..." : "");
3212 q_status_message(SM_ORDER
, 3, 3, tmp_20k_buf
);
3213 return(0); /* error */
3216 snprintf(tmp_20k_buf
,SIZEOF_20KBUF
,
3217 _("Folder \"%.15s%s\" in <%.15s%s> doesn't exist. Create"),
3218 folder
, (strlen(folder
) > 15) ? "..." : "",
3220 (strlen(context
->nickname
) > 15) ? "..." : "");
3223 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,
3224 _("Folder \"%.40s%s\" doesn't exist. Create"),
3225 folder
, strlen(folder
) > 40 ? "..." : "");
3227 if(want_to(tmp_20k_buf
, 'y', 'n',
3228 NO_HELP
, (sequence_sensitive
) ? WT_SEQ_SENSITIVE
: WT_NORM
) != 'y'){
3229 cmd_cancelled("Save message");
3238 /*----------------------------------------------------------------------
3239 Expunge messages from current folder
3241 Args: state -- pointer to struct holding a bunch of pine state
3242 msgmap -- table mapping msg nums to c-client sequence nums
3243 qline -- screen line to ask questions on
3244 agg -- boolean indicating we're to operate on aggregate set
3249 cmd_expunge(struct pine
*state
, MAILSTREAM
*stream
, MSGNO_S
*msgmap
, int agg
)
3251 long del_count
, prefilter_del_count
;
3252 int we_cancel
= 0, rv
= 0;
3253 char prompt
[MAX_SCREEN_COLS
+1];
3255 COLOR_PAIR
*lastc
= NULL
;
3257 dprint((2, "\n - expunge -\n"));
3261 sequence
= MCMD_ISAGG(agg
) ? selected_sequence(stream
, msgmap
, NULL
, 0) : NULL
;
3263 if(MCMD_ISAGG(agg
)){
3266 for(i
= 1L; i
<= stream
->nmsgs
; i
++){
3267 if((mc
= mail_elt(stream
, i
)) != NULL
3268 && mc
->sequence
&& mc
->deleted
)
3272 q_status_message(SM_ORDER
, 0, 4,
3273 _("No selected messages are deleted"));
3277 if(!any_messages(msgmap
, NULL
, "to Expunge"))
3281 if(IS_NEWS(stream
) && stream
->rdonly
){
3282 if(!MCMD_ISAGG(agg
))
3283 del_count
= count_flagged(stream
, F_DEL
);
3285 state
->mangled_footer
= 1; /* MAX_SCREEN_COLS+1 = sizeof(prompt) */
3286 snprintf(prompt
, sizeof(prompt
), "Exclude %ld message%s from %.*s", del_count
,
3287 plural(del_count
), MAX_SCREEN_COLS
+1-40,
3288 pretty_fn(state
->cur_folder
));
3289 prompt
[sizeof(prompt
)-1] = '\0';
3290 if(F_ON(F_FULL_AUTO_EXPUNGE
, state
)
3291 || (F_ON(F_AUTO_EXPUNGE
, state
)
3292 && (state
->context_current
3293 && (state
->context_current
->use
& CNTXT_INCMNG
))
3294 && context_isambig(state
->cur_folder
))
3295 || want_to(prompt
, 'y', 0, NO_HELP
, WT_NORM
) == 'y'){
3297 if(F_ON(F_NEWS_CROSS_DELETE
, state
))
3298 cross_delete_crossposts(stream
);
3300 msgno_exclude_deleted(stream
, msgmap
, sequence
);
3301 clear_index_cache(stream
, 0);
3304 * This is kind of surprising at first. For most sort
3305 * orders, if the whole set is sorted, then any subset
3306 * is also sorted. Not so for threaded sorts.
3308 if(SORT_IS_THREADED(msgmap
))
3309 refresh_sort(stream
, msgmap
, SRT_NON
);
3311 state
->mangled_body
= 1;
3312 state
->mangled_header
= 1;
3313 q_status_message2(SM_ORDER
, 0, 4,
3314 "%s message%s excluded",
3315 long2string(del_count
),
3319 any_messages(NULL
, NULL
, "Excluded");
3322 any_messages(NULL
, "deleted", "to Exclude");
3326 else if(READONLY_FOLDER(stream
)){
3327 q_status_message(SM_ORDER
, 0, 4,
3328 _("Can't expunge. Folder is read-only"));
3332 if(!MCMD_ISAGG(agg
)){
3333 prefilter_del_count
= count_flagged(stream
, F_DEL
|F_NOFILT
);
3334 mail_expunge_prefilter(stream
, MI_NONE
);
3335 del_count
= count_flagged(stream
, F_DEL
|F_NOFILT
);
3340 unsigned char *fname
= folder_name_decoded((unsigned char *)state
->cur_folder
);
3341 /* MAX_SCREEN_COLS+1 = sizeof(prompt) */
3342 snprintf(prompt
, sizeof(prompt
), "Expunge %ld message%s from %.*s", del_count
,
3343 plural(del_count
), MAX_SCREEN_COLS
+1-40,
3344 pretty_fn((char *) fname
));
3345 if(fname
) fs_give((void **)&fname
);
3346 prompt
[sizeof(prompt
)-1] = '\0';
3347 state
->mangled_footer
= 1;
3349 if(F_ON(F_FULL_AUTO_EXPUNGE
, state
)
3350 || (F_ON(F_AUTO_EXPUNGE
, state
)
3351 && ((!strucmp(state
->cur_folder
,state
->inbox_name
))
3352 || (state
->context_current
->use
& CNTXT_INCMNG
))
3353 && context_isambig(state
->cur_folder
))
3354 || (ret
=want_to(prompt
, 'y', 0, NO_HELP
, WT_NORM
)) == 'y')
3358 cmd_cancelled("Expunge");
3364 dprint((8, "Expunge max:%ld cur:%ld kill:%d\n",
3365 mn_get_total(msgmap
), mn_get_cur(msgmap
), del_count
));
3367 lastc
= pico_set_colors(state
->VAR_TITLE_FORE_COLOR
,
3368 state
->VAR_TITLE_BACK_COLOR
,
3371 PutLine0(0, 0, "**"); /* indicate delay */
3374 (void)pico_set_colorp(lastc
, PSC_NONE
);
3375 free_color_pair(&lastc
);
3378 MoveCursor(state
->ttyo
->screen_rows
-FOOTER_ROWS(state
), 0);
3381 we_cancel
= busy_cue(_("Expunging"), NULL
, 1);
3383 if(cmd_expunge_work(stream
, msgmap
, sequence
))
3384 state
->mangled_body
= 1;
3387 fs_give((void **)&sequence
);
3390 cancel_busy_cue((sp_expunge_count(stream
) > 0) ? 0 : -1);
3392 lastc
= pico_set_colors(state
->VAR_TITLE_FORE_COLOR
,
3393 state
->VAR_TITLE_BACK_COLOR
,
3395 PutLine0(0, 0, " "); /* indicate delay's over */
3398 (void)pico_set_colorp(lastc
, PSC_NONE
);
3399 free_color_pair(&lastc
);
3404 if(sp_expunge_count(stream
) > 0){
3406 * This is kind of surprising at first. For most sort
3407 * orders, if the whole set is sorted, then any subset
3408 * is also sorted. Not so for threaded sorts.
3410 if(SORT_IS_THREADED(msgmap
))
3411 refresh_sort(stream
, msgmap
, SRT_NON
);
3415 unsigned char *fname
= folder_name_decoded((unsigned char *)state
->cur_folder
);
3416 q_status_message1(SM_ORDER
, 0, 3,
3417 _("No messages expunged from folder \"%s\""),
3418 pretty_fn((char *) fname
));
3419 if(fname
) fs_give((void **)&fname
);
3421 else if(!prefilter_del_count
)
3422 q_status_message(SM_ORDER
, 0, 3,
3423 _("No messages marked deleted. No messages expunged."));
3429 /*----------------------------------------------------------------------
3430 Expunge_and_close callback to prompt user for confirmation
3432 Args: stream -- folder's stream
3433 folder -- name of folder containing folders
3434 deleted -- number of del'd msgs
3436 Result: 'y' to continue with expunge
3439 expunge_prompt(MAILSTREAM
*stream
, char *folder
, long int deleted
)
3443 char prompt_b
[MAX_SCREEN_COLS
+1], temp
[MAILTMPLEN
+1], buff
[MAX_SCREEN_COLS
+1];
3444 char *short_folder_name
;
3449 snprintf(temp
, sizeof(temp
), "%ld", deleted
);
3450 charcnt
= strlen(temp
)+1;
3453 max_folder
= MAX(1,MAXPROMPT
- (36+charcnt
));
3454 strncpy(temp
, folder
, sizeof(temp
));
3455 temp
[sizeof(temp
)-1] = '\0';
3456 short_folder_name
= short_str(temp
,buff
,sizeof(buff
),max_folder
,FrontDots
);
3459 snprintf(prompt_b
, sizeof(prompt_b
),
3460 "Delete %s%ld message%s from \"%s\"",
3461 (deleted
> 1L) ? "all " : "", deleted
,
3462 plural(deleted
), short_folder_name
);
3464 snprintf(prompt_b
, sizeof(prompt_b
),
3465 "Expunge the %ld deleted message%s from \"%s\"",
3466 deleted
, deleted
== 1 ? "" : "s",
3469 return(want_to(prompt_b
, 'y', 0, NO_HELP
, WT_NORM
));
3474 * This is used with multiple append saves. Call it once before
3475 * the series of appends with SSCP_INIT and once after all are
3476 * done with SSCP_END. In between, it is called automatically
3477 * from save_fetch_append or save_fetch_append_cb when we need
3478 * to ask the user if he or she wants to continue even though
3479 * announced message size doesn't match the actual message size.
3480 * As of 2008-02-29 the gmail IMAP server has these size mismatches
3481 * on a regular basis even though the data is ok.
3484 save_size_changed_prompt(long msgno
, int flags
)
3488 static int remember_the_yes
= 0;
3489 static int possible_corruption
= 0;
3490 static ESCKEY_S save_size_opts
[] = {
3491 {'y', 'y', "Y", "Yes"},
3492 {'n', 'n', "N", "No"},
3493 {'a', 'a', "A", "yes to All"},
3497 if(F_ON(F_IGNORE_SIZE
, ps_global
))
3500 if(flags
& SSCP_INIT
|| flags
& SSCP_END
){
3501 if(flags
& SSCP_END
&& possible_corruption
)
3502 q_status_message(SM_ORDER
, 3, 3, "There is possible data corruption, check the results");
3504 remember_the_yes
= 0;
3505 possible_corruption
= 0;
3506 ps_global
->noshow_error
= 0;
3507 ps_global
->noshow_warn
= 0;
3511 if(remember_the_yes
){
3512 snprintf(prompt
, sizeof(prompt
),
3513 "Message to save shrank! (msg # %ld): Continuing", msgno
);
3514 q_status_message(SM_ORDER
, 0, 3, prompt
);
3515 display_message('x');
3516 return(remember_the_yes
);
3519 snprintf(prompt
, sizeof(prompt
),
3520 "Message to save shrank! (msg # %ld): Continue anyway ? ", msgno
);
3521 ret
= radio_buttons(prompt
, -FOOTER_ROWS(ps_global
), save_size_opts
,
3522 'n', 0, h_save_size_changed
, RB_NORM
);
3526 remember_the_yes
= 'y';
3527 possible_corruption
++;
3528 return(remember_the_yes
);
3531 possible_corruption
++;
3535 possible_corruption
= 0;
3536 ps_global
->noshow_error
= 1;
3537 ps_global
->noshow_warn
= 1;
3545 /*----------------------------------------------------------------------
3546 Expunge_and_close callback that happens once the decision to expunge
3547 and close has been made and before expunging and closing begins
3550 Args: stream -- folder's stream
3551 folder -- name of folder containing folders
3552 deleted -- number of del'd msgs
3554 Result: 'y' to continue with expunge
3557 expunge_and_close_begins(int flags
, char *folder
)
3559 if(!(flags
& EC_NO_CLOSE
)){
3560 unsigned char *fname
= folder_name_decoded((unsigned char *)folder
);
3561 q_status_message1(SM_INFO
, 0, 1, "Closing \"%.200s\"...", (char *) fname
);
3562 flush_status_messages(1);
3563 if(fname
) fs_give((void **)&fname
);
3568 /*----------------------------------------------------------------------
3569 Export a message to a plain file in users home directory
3571 Args: state -- pointer to struct holding a bunch of pine state
3572 msgmap -- table mapping msg nums to c-client sequence nums
3573 qline -- screen line to ask questions on
3574 agg -- boolean indicating we're to operate on aggregate set
3579 cmd_export(struct pine
*state
, MSGNO_S
*msgmap
, int qline
, int aopt
)
3581 char filename
[MAXPATH
+1], full_filename
[MAXPATH
+1], *err
;
3583 int r
, leading_nl
, failure
= 0, orig_errno
, rflags
= GER_NONE
;
3584 int flags
= GE_IS_EXPORT
| GE_SEQ_SENSITIVE
, rv
= 0;
3588 long i
, count
= 0L, start_of_append
, rawno
;
3591 struct variable
*vars
= ps_global
->vars
;
3592 ESCKEY_S export_opts
[5];
3593 static HISTORY_S
*history
= NULL
;
3595 if(ps_global
->restricted
){
3596 q_status_message(SM_ORDER
, 0, 3,
3597 "Alpine demo can't export messages to files");
3601 if(MCMD_ISAGG(aopt
) && !pseudo_selected(state
->mail_stream
, msgmap
))
3604 export_opts
[i
= 0].ch
= ctrl('T');
3605 export_opts
[i
].rval
= 10;
3606 export_opts
[i
].name
= "^T";
3607 export_opts
[i
++].label
= N_("To Files");
3609 #if !defined(DOS) && !defined(MAC) && !defined(OS2)
3610 if(ps_global
->VAR_DOWNLOAD_CMD
&& ps_global
->VAR_DOWNLOAD_CMD
[0]){
3611 export_opts
[i
].ch
= ctrl('V');
3612 export_opts
[i
].rval
= 12;
3613 export_opts
[i
].name
= "^V";
3614 /* TRANSLATORS: this is an abbreviation for Download Messages */
3615 export_opts
[i
++].label
= N_("Downld Msg");
3617 #endif /* !(DOS || MAC) */
3619 if(F_ON(F_ENABLE_TAB_COMPLETE
,ps_global
)){
3620 export_opts
[i
].ch
= ctrl('I');
3621 export_opts
[i
].rval
= 11;
3622 export_opts
[i
].name
= "TAB";
3623 export_opts
[i
++].label
= N_("Complete");
3627 /* Commented out since it's not yet support! */
3628 if(F_ON(F_ENABLE_SUB_LISTS
,ps_global
)){
3629 export_opts
[i
].ch
= ctrl('X');
3630 export_opts
[i
].rval
= 14;
3631 export_opts
[i
].name
= "^X";
3632 export_opts
[i
++].label
= N_("ListMatches");
3637 * If message has attachments, add a toggle that will allow the user
3638 * to save all of the attachments to a single directory, using the
3639 * names provided with the attachments or part names. What we'll do is
3640 * export the message as usual, and then export the attachments into
3641 * a subdirectory that did not exist before. The subdir will be named
3642 * something based on the name of the file being saved to, but a
3645 if(!MCMD_ISAGG(aopt
)
3646 && state
->mail_stream
3647 && (rawno
= mn_m2raw(msgmap
, mn_get_cur(msgmap
))) > 0L
3648 && rawno
<= state
->mail_stream
->nmsgs
3649 && (env
= pine_mail_fetchstructure(state
->mail_stream
, rawno
, &b
))
3651 && b
->type
== TYPEMULTIPART
3653 && strucmp(b
->subtype
, "ALTERNATIVE") != 0){
3656 part
= b
->nested
.part
; /* 1st part */
3657 if(part
&& part
->next
)
3658 flags
|= GE_ALLPARTS
;
3661 export_opts
[i
].ch
= -1;
3664 if(mn_total_cur(msgmap
) <= 1L){
3665 snprintf(nmsgs
, sizeof(nmsgs
), "Msg #%ld", mn_get_cur(msgmap
));
3666 nmsgs
[sizeof(nmsgs
)-1] = '\0';
3669 snprintf(nmsgs
, sizeof(nmsgs
), "%s messages", comatose(mn_total_cur(msgmap
)));
3670 nmsgs
[sizeof(nmsgs
)-1] = '\0';
3673 r
= get_export_filename(state
, filename
, NULL
, full_filename
,
3674 sizeof(filename
), nmsgs
, "EXPORT",
3675 export_opts
, &rflags
, qline
, flags
, &history
);
3680 cmd_cancelled("Export message");
3684 q_status_message1(SM_ORDER
, 0, 2,
3685 _("Can't export to file outside of %s"),
3692 #if !defined(DOS) && !defined(MAC) && !defined(OS2)
3693 else if(r
== 12){ /* Download */
3694 char cmd
[MAXPATH
], *tfp
= NULL
;
3700 if(ps_global
->restricted
){
3701 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
3702 "Download disallowed in restricted mode");
3707 tfp
= temp_nam(NULL
, "pd");
3708 build_updown_cmd(cmd
, sizeof(cmd
), ps_global
->VAR_DOWNLOAD_CMD_PREFIX
,
3709 ps_global
->VAR_DOWNLOAD_CMD
, tfp
);
3710 dprint((1, "Download cmd called: \"%s\"\n", cmd
));
3711 if((so
= so_get(FileStar
, tfp
, WRITE_ACCESS
|OWNER_ONLY
|WRITE_TO_LOCALE
)) != NULL
){
3712 gf_set_so_writec(&pc
, so
);
3714 for(i
= mn_first_cur(msgmap
); i
> 0L; i
= mn_next_cur(msgmap
)){
3715 if(!(state
->mail_stream
3716 && (rawno
= mn_m2raw(msgmap
, i
)) > 0L
3717 && rawno
<= state
->mail_stream
->nmsgs
3718 && (mc
= mail_elt(state
->mail_stream
, rawno
))
3722 if(!(env
= pine_mail_fetchstructure(state
->mail_stream
,
3723 mn_m2raw(msgmap
, i
), &b
))
3724 || !bezerk_delimiter(env
, mc
, pc
, next
++)
3725 || !format_message(mn_m2raw(msgmap
, mn_get_cur(msgmap
)),
3726 env
, b
, NULL
, FM_NEW_MESS
| FM_NOWRAP
, pc
)){
3727 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
3728 err
= "Error writing tempfile for download");
3733 gf_clear_so_writec(so
);
3734 if(so_give(&so
)){ /* close file */
3736 err
= "Error writing tempfile for download";
3740 if((syspipe
= open_system_pipe(cmd
, NULL
, NULL
,
3741 PIPE_USER
| PIPE_RESET
,
3742 0, pipe_callback
, pipe_report_error
)) != NULL
)
3743 (void) close_system_pipe(&syspipe
, NULL
, pipe_callback
);
3745 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
3746 err
= _("Error running download command"));
3750 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
3751 err
= "Error building temp file for download");
3755 fs_give((void **)&tfp
);
3759 q_status_message(SM_ORDER
, 0, 3, _("Download Command Completed"));
3763 #endif /* !(DOS || MAC) */
3766 if(rflags
& GER_APPEND
)
3771 dprint((5, "Opening file \"%s\" for export\n",
3772 full_filename
? full_filename
: "?"));
3774 if(!(store
= so_get(FileStar
, full_filename
, WRITE_ACCESS
|WRITE_TO_LOCALE
))){
3775 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
3776 /* TRANSLATORS: error opening file "<filename>" to export message: <error text> */
3777 _("Error opening file \"%s\" to export message: %s"),
3778 full_filename
, error_description(errno
));
3782 gf_set_so_writec(&pc
, store
);
3785 for(i
= mn_first_cur(msgmap
); i
> 0L; i
= mn_next_cur(msgmap
), count
++){
3786 env
= pine_mail_fetchstructure(state
->mail_stream
, mn_m2raw(msgmap
, i
),
3789 err
= _("Can't export message. Error accessing mail folder");
3794 if(!(state
->mail_stream
3795 && (rawno
= mn_m2raw(msgmap
, i
)) > 0L
3796 && rawno
<= state
->mail_stream
->nmsgs
3797 && (mc
= mail_elt(state
->mail_stream
, rawno
))
3801 start_of_append
= so_tell(store
);
3802 if(!bezerk_delimiter(env
, mc
, pc
, leading_nl
)
3803 || !format_message(mn_m2raw(msgmap
, i
), env
, b
, NULL
,
3804 FM_NEW_MESS
| FM_NOWRAP
, pc
)){
3805 orig_errno
= errno
; /* save incase things are really bad */
3806 failure
= 1; /* pop out of here */
3813 gf_clear_so_writec(store
);
3814 if(so_give(&store
)) /* release storage */
3818 our_truncate(full_filename
, (off_t
)start_of_append
);
3820 dprint((1, "FAILED Export: fetch(%ld): %s\n",
3821 i
, err
? err
: "?"));
3822 q_status_message(SM_ORDER
| SM_DING
, 3, 4, err
);
3825 dprint((1, "FAILED Export: file \"%s\" : %s\n",
3826 full_filename
? full_filename
: "?",
3827 error_description(orig_errno
)));
3828 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
3829 /* TRANSLATORS: Error exporting to <filename>: <error text> */
3830 _("Error exporting to \"%s\" : %s"),
3831 filename
, error_description(orig_errno
));
3835 if(rflags
& GER_ALLPARTS
&& full_filename
[0]){
3836 char dir
[MAXPATH
+1];
3837 char lfile
[MAXPATH
+1];
3838 int ok
= 0, tries
= 0, saved
= 0, errs
= 0, counter
= 2;
3842 * Now we want to save all of the attachments to a subdirectory.
3843 * To make it easier for us and probably easier for the user, and
3844 * to prevent the user from shooting himself in the foot, we
3845 * make a new subdirectory so that we can't possibly step on
3846 * any existing files, and we don't need any interaction with the
3847 * user while saving.
3849 * We'll just use the directory name full_filename.d or if that
3850 * already exists and isn't empty, we'll try adding a suffix to
3851 * that until we get something to use.
3854 if(strlen(full_filename
) + strlen(".d") + 1 > sizeof(dir
)){
3855 q_status_message1(SM_ORDER
| SM_DING
, 3, 4,
3856 _("Can't save attachments, filename too long: %s"),
3862 snprintf(dir
, sizeof(dir
), "%s.d", full_filename
);
3863 dir
[sizeof(dir
)-1] = '\0';
3867 switch(r
= is_writable_dir(dir
)){
3868 case 0: /* exists and is a writable dir */
3870 * We could figure out if it is empty and use it in
3871 * that case, but that sounds like a lot of work, so
3872 * just fall through to default.
3876 if(strlen(full_filename
) + strlen(".d") + 1 +
3877 1 + strlen(long2string((long) tries
)) > sizeof(dir
)){
3878 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
3879 "Problem saving attachments");
3883 snprintf(dir
, sizeof(dir
), "%s.d_%s", full_filename
,
3884 long2string((long) tries
));
3885 dir
[sizeof(dir
)-1] = '\0';
3888 case 3: /* doesn't exist, that's good! */
3889 /* make new directory */
3893 } while(!ok
&& tries
< 1000);
3896 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
3897 _("Problem saving attachments"));
3901 /* create the new directory */
3902 if(our_mkdir(dir
, 0700)){
3903 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
3904 _("Problem saving attachments: %s: %s"), dir
,
3905 error_description(errno
));
3909 if(!(state
->mail_stream
3910 && (rawno
= mn_m2raw(msgmap
, mn_get_cur(msgmap
))) > 0L
3911 && rawno
<= state
->mail_stream
->nmsgs
3912 && (env
=pine_mail_fetchstructure(state
->mail_stream
,rawno
,&b
))
3914 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
3915 _("Problem reading message"));
3919 zero_atmts(state
->atmts
);
3920 describe_mime(b
, "", 1, 1, 0, 0);
3923 if(a
&& a
->description
) /* skip main body part */
3926 for(; a
->description
!= NULL
; a
++){
3927 /* skip over these parts of the message */
3928 if(MIME_MSG_A(a
) || MIME_DGST_A(a
) || MIME_VCARD_A(a
))
3932 (void) get_filename_parameter(lfile
, sizeof(lfile
), a
->body
, NULL
);
3934 if(lfile
[0] == '\0'){ /* MAXPATH + 1 = sizeof(lfile) */
3935 snprintf(lfile
, sizeof(lfile
), "part_%.*s", MAXPATH
+1-6,
3936 a
->number
? a
->number
: "?");
3937 lfile
[sizeof(lfile
)-1] = '\0';
3940 if(strlen(dir
) + strlen(S_FILESEP
) + strlen(lfile
) + 1
3941 > sizeof(filename
)){
3943 "FAILED Att Export: name too long: %s\n",
3944 dir
, S_FILESEP
, lfile
));
3949 /* although files are being saved in a unique directory, there is
3950 * no guarantee that attachment names have unique names, so we have
3951 * to make sure that we are not constantly rewriting the same file name
3952 * over and over. In order to avoid this we test if the file already exists,
3953 * and if so, we write a counter name in the file name, just before the
3954 * extension of the file, and separate it with an underscore.
3956 snprintf(filename
, sizeof(filename
), "%s%s%s", dir
, S_FILESEP
, lfile
);
3957 filename
[sizeof(filename
)-1] = '\0';
3958 while((ok
= can_access(filename
, ACCESS_EXISTS
)) == 0 && errs
== 0){
3960 snprintf(filename
, sizeof(filename
), "%d", counter
);
3961 if(strlen(dir
) + strlen(S_FILESEP
) + strlen(lfile
) + strlen(filename
) + 2
3962 > sizeof(filename
)){
3964 "FAILED Att Export: name too long: %s\n",
3965 dir
, S_FILESEP
, lfile
));
3969 if((ext
= strrchr(lfile
, '.')) != NULL
)
3971 snprintf(filename
, sizeof(filename
), "%s%s%s%s%d%s%s",
3972 dir
, S_FILESEP
, lfile
,
3973 ext
? "_" : "", counter
++, ext
? "." : "", ext
? ext
+1 : "");
3974 filename
[sizeof(filename
)-1] = '\0';
3977 if(write_attachment_to_file(state
->mail_stream
, rawno
,
3978 a
, GER_NONE
, filename
) == 1)
3986 q_status_message1(SM_ORDER
, 3, 3,
3987 "Errors saving some attachments, %s attachments saved",
3988 long2string((long) saved
));
3990 q_status_message(SM_ORDER
, 3, 3,
3991 _("Problems saving attachments"));
3995 q_status_message2(SM_ORDER
, 0, 3,
3996 /* TRANSLATORS: Saved <how many> attachements to <directory name> */
3997 _("Saved %s attachments to %s"),
3998 long2string((long) saved
), dir
);
4000 q_status_message(SM_ORDER
, 3, 3, _("No attachments to save"));
4003 else if(mn_total_cur(msgmap
) > 1L)
4004 q_status_message4(SM_ORDER
,0,3,
4005 "%s message%s %s to file \"%s\"",
4006 long2string(count
), plural(count
),
4009 : rflags
& GER_APPEND
? "appended" : "exported",
4012 q_status_message3(SM_ORDER
,0,3,
4013 "Message %s %s to file \"%s\"",
4014 long2string(mn_get_cur(msgmap
)),
4017 : rflags
& GER_APPEND
? "appended" : "exported",
4023 if(MCMD_ISAGG(aopt
))
4024 restore_selected(msgmap
);
4031 * Ask user what file to export to. Export from srcstore to that file.
4033 * Args ps -- pine struct
4034 * srctext -- pointer to source text
4035 * srctype -- type of that source text
4036 * prompt_msg -- see get_export_filename
4039 * Returns: != 0 : error
4043 simple_export(struct pine
*ps
, void *srctext
, SourceType srctype
, char *prompt_msg
, char *lister_msg
)
4045 int r
= 1, rflags
= GER_NONE
;
4046 char filename
[MAXPATH
+1], full_filename
[MAXPATH
+1];
4047 STORE_S
*store
= NULL
;
4048 struct variable
*vars
= ps
->vars
;
4049 static HISTORY_S
*history
= NULL
;
4050 static ESCKEY_S simple_export_opts
[] = {
4051 {ctrl('T'), 10, "^T", N_("To Files")},
4052 {-1, 0, NULL
, NULL
},
4053 {-1, 0, NULL
, NULL
}};
4055 if(F_ON(F_ENABLE_TAB_COMPLETE
,ps
)){
4056 simple_export_opts
[r
].ch
= ctrl('I');
4057 simple_export_opts
[r
].rval
= 11;
4058 simple_export_opts
[r
].name
= "TAB";
4059 simple_export_opts
[r
].label
= N_("Complete");
4063 q_status_message(SM_ORDER
, 0, 2, _("Error allocating space"));
4068 simple_export_opts
[++r
].ch
= -1;
4070 full_filename
[0] = '\0';
4072 r
= get_export_filename(ps
, filename
, NULL
, full_filename
, sizeof(filename
),
4073 prompt_msg
, lister_msg
, simple_export_opts
, &rflags
,
4074 -FOOTER_ROWS(ps
), GE_IS_EXPORT
, &history
);
4078 else if(!full_filename
[0]){
4083 dprint((5, "Opening file \"%s\" for export\n",
4084 full_filename
? full_filename
: "?"));
4086 if((store
= so_get(FileStar
, full_filename
, WRITE_ACCESS
|WRITE_TO_LOCALE
)) != NULL
){
4090 gf_set_so_writec(&pc
, store
);
4091 gf_set_readc(&gc
, srctext
, (srctype
== CharStar
)
4092 ? strlen((char *)srctext
)
4096 if((pipe_err
= gf_pipe(gc
, pc
)) != NULL
){
4097 q_status_message2(SM_ORDER
| SM_DING
, 3, 3,
4098 /* TRANSLATORS: Problem saving to <filename>: <error text> */
4099 _("Problem saving to \"%s\": %s"),
4100 filename
, pipe_err
);
4106 gf_clear_so_writec(store
);
4107 if(so_give(&store
)){
4108 q_status_message2(SM_ORDER
| SM_DING
, 3, 3,
4109 _("Problem saving to \"%s\": %s"),
4110 filename
, error_description(errno
));
4115 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
4116 _("Error opening file \"%s\" for export: %s"),
4117 full_filename
, error_description(errno
));
4124 /* overloading full_filename */
4125 snprintf(full_filename
, sizeof(full_filename
), "%c%s",
4126 (prompt_msg
&& prompt_msg
[0])
4127 ? (islower((unsigned char)prompt_msg
[0])
4128 ? toupper((unsigned char)prompt_msg
[0]) : prompt_msg
[0])
4130 (prompt_msg
&& prompt_msg
[0]) ? prompt_msg
+1 : "ext");
4131 full_filename
[sizeof(full_filename
)-1] = '\0';
4132 q_status_message3(SM_ORDER
,0,2,"%s %s to \"%s\"",
4136 : rflags
& GER_APPEND
? "appended" : "exported",
4141 cmd_cancelled("Export");
4145 q_status_message1(SM_ORDER
, 0, 2,
4146 _("Can't export to file outside of %s"), VAR_OPER_DIR
);
4150 ps
->mangled_footer
= 1;
4156 * Ask user what file to export to.
4158 * filename -- On input, this is the filename to start with. On exit,
4159 * this is the filename chosen. (but this isn't used)
4160 * deefault -- This is the default value if user hits return. The
4161 * prompt will have [deefault] added to it automatically.
4162 * full_filename -- This is the full filename on exit.
4163 * len -- Minimum length of _both_ filename and full_filename.
4164 * prompt_msg -- Message to insert in prompt.
4165 * lister_msg -- Message to insert in file_lister.
4166 * opts -- Key options.
4167 * There is a tangled relationship between the callers
4168 * and this routine as far as opts are concerned. Some
4169 * of the opts are handled here. In particular, r == 3,
4170 * r == 10, r == 11, and r == 13 are all handled here.
4171 * Don't use those values unless you want what happens
4172 * here. r == 12 and others are handled by the caller.
4173 * rflags -- Return flags
4174 * GER_OVER - overwrite of existing file
4175 * GER_APPEND - append of existing file
4176 * else file did not exist before
4178 * GER_ALLPARTS - AllParts toggle was turned on
4180 * qline -- Command line to prompt on.
4181 * flags -- Logically OR'd flags
4182 * GE_IS_EXPORT - The command was an Export command
4183 * so the prompt should include
4185 * GE_SEQ_SENSITIVE - The command that got us here is
4186 * sensitive to sequence number changes
4187 * caused by unsolicited expunges.
4188 * GE_NO_APPEND - We will not allow append to an
4189 * existing file, only removal of the
4190 * file if it exists.
4191 * GE_IS_IMPORT - We are selecting for reading.
4192 * No overwriting or checking for
4193 * existence at all. Don't use this
4194 * together with GE_NO_APPEND.
4195 * GE_ALLPARTS - Turn on AllParts toggle.
4196 * GE_BINARY - Turn on Binary toggle.
4198 * Returns: -1 cancelled
4199 * -2 prohibited by VAR_OPER_DIR
4200 * -3 other error, already reported here
4202 * 12 user chose 12 command from opts
4205 get_export_filename(struct pine
*ps
, char *filename
, char *deefault
,
4206 char *full_filename
, size_t len
, char *prompt_msg
,
4207 char *lister_msg
, ESCKEY_S
*optsarg
, int *rflags
,
4208 int qline
, int flags
, HISTORY_S
**history
)
4210 char dir
[MAXPATH
+1], dir2
[MAXPATH
+1], orig_dir
[MAXPATH
+1];
4211 char precolon
[MAXPATH
+1], postcolon
[MAXPATH
+1];
4212 char filename2
[MAXPATH
+1], tmp
[MAXPATH
+1], *fn
, *ill
;
4213 int l
, i
, ku
= -1, kp
= -1, r
, fatal
, homedir
= 0, was_abs_path
=0, avail
, ret
= 0;
4214 int allparts
= 0, binary
= 0;
4215 char prompt_buf
[400];
4217 ESCKEY_S
*opts
= NULL
;
4218 struct variable
*vars
= ps
->vars
;
4219 static HISTORY_S
*dir_hist
= NULL
;
4221 int pos
, hist_len
= 0;
4224 /* we will fake a history with the ps_global->VAR_HISTORY variable
4225 * We fake that we combine this variable into a history variable
4226 * by stacking VAR_HISTORY on top of dir_hist. We keep track of this
4227 * by looking at the variable pos.
4229 if(ps_global
->VAR_HISTORY
!= NULL
)
4230 for(hist_len
= 0; ps_global
->VAR_HISTORY
[hist_len
]
4231 && ps_global
->VAR_HISTORY
[hist_len
][0]; hist_len
++)
4234 pos
= hist_len
+ items_in_hist(dir_hist
);
4236 if(flags
& GE_ALLPARTS
|| history
|| dir_hist
){
4238 * Copy the opts and add one to the end of the list.
4240 for(i
= 0; optsarg
[i
].ch
!= -1; i
++)
4243 if(dir_hist
|| hist_len
> 0)
4247 i
+= dir_hist
|| hist_len
> 0 ? 2 : 4;
4249 if(flags
& GE_ALLPARTS
)
4252 if(flags
& GE_BINARY
)
4255 opts
= (ESCKEY_S
*) fs_get((i
+1) * sizeof(*opts
));
4256 memset(opts
, 0, (i
+1) * sizeof(*opts
));
4258 for(i
= 0; optsarg
[i
].ch
!= -1; i
++){
4259 opts
[i
].ch
= optsarg
[i
].ch
;
4260 opts
[i
].rval
= optsarg
[i
].rval
;
4261 opts
[i
].name
= optsarg
[i
].name
; /* no need to make a copy */
4262 opts
[i
].label
= optsarg
[i
].label
; /* " */
4265 if(flags
& GE_ALLPARTS
){
4267 opts
[i
].ch
= ctrl('P');
4269 opts
[i
].name
= "^P";
4270 /* TRANSLATORS: Export all attachment parts */
4271 opts
[i
++].label
= N_("AllParts");
4274 if(flags
& GE_BINARY
){
4276 opts
[i
].ch
= ctrl('R');
4278 opts
[i
].name
= "^R";
4279 opts
[i
++].label
= N_("Binary");
4282 rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
,
4283 SIZEOF_20KBUF
, filename
);
4284 if(strcmp(tmp_20k_buf
, filename
)){
4285 opts
[i
].ch
= ctrl('N');
4287 opts
[i
].name
= "^N";
4288 opts
[i
++].label
= "Name UTF8";
4291 if(dir_hist
|| hist_len
> 0){
4292 opts
[i
].ch
= ctrl('Y');
4296 opts
[i
++].label
= "";
4298 opts
[i
].ch
= ctrl('V');
4301 opts
[i
++].label
= "";
4305 opts
[i
].ch
= KEY_UP
;
4309 opts
[i
++].label
= "";
4311 opts
[i
].ch
= KEY_DOWN
;
4314 opts
[i
++].label
= "";
4320 init_hist(history
, HISTSIZE
);
4321 init_hist(&dir_hist
, HISTSIZE
); /* reset history to the end */
4329 if(F_ON(F_USE_CURRENT_DIR
, ps
))
4331 else if(VAR_OPER_DIR
){
4332 strncpy(dir
, VAR_OPER_DIR
, sizeof(dir
));
4333 dir
[sizeof(dir
)-1] = '\0';
4335 #if defined(DOS) || defined(OS2)
4336 else if(VAR_FILE_DIR
){
4337 strncpy(dir
, VAR_FILE_DIR
, sizeof(dir
));
4338 dir
[sizeof(dir
)-1] = '\0';
4346 strncpy(orig_dir
, dir
, sizeof(orig_dir
));
4347 orig_dir
[sizeof(orig_dir
)-1] = '\0';
4349 postcolon
[0] = '\0';
4350 strncpy(precolon
, dir
, sizeof(precolon
));
4351 precolon
[sizeof(precolon
)-1] = '\0';
4353 strncpy(def
, deefault
, sizeof(def
)-1);
4354 def
[sizeof(def
)-1] = '\0';
4355 removing_leading_and_trailing_white_space(def
);
4360 avail
= MAX(20, ps_global
->ttyo
? ps_global
->ttyo
->screen_cols
: 80) - MIN_OPT_ENT_WIDTH
;
4362 /*---------- Prompt the user for the file name -------------*/
4365 char dirb
[50], fileb
[50];
4366 int l1
, l2
, l3
, l4
, l5
, needed
;
4367 char *p
, p1
[100], p2
[100], *p3
, p4
[100], p5
[100];
4369 snprintf(p1
, sizeof(p1
), "%sCopy ",
4370 (flags
& GE_IS_EXPORT
) ? "EXPORT: " :
4371 (flags
& GE_IS_IMPORT
) ? "IMPORT: " : "SAVE: ");
4372 p1
[sizeof(p1
)-1] = '\0';
4375 strncpy(p2
, prompt_msg
? prompt_msg
: "", sizeof(p2
)-1);
4376 p2
[sizeof(p2
)-1] = '\0';
4379 if(rflags
&& *rflags
& GER_ALLPARTS
)
4386 snprintf(p4
, sizeof(p4
), " %s file%s%s",
4387 (flags
& GE_IS_IMPORT
) ? "from" : "to",
4388 is_absolute_path(filename
) ? "" : " in ",
4389 is_absolute_path(filename
) ? "" :
4390 (!dir
[0] ? "current directory"
4391 : (dir
[0] == '~' && !dir
[1]) ? "home directory"
4392 : short_str(dir
,dirb
,sizeof(dirb
),30,FrontDots
)));
4393 p4
[sizeof(p4
)-1] = '\0';
4396 snprintf(p5
, sizeof(p5
), "%s%s%s: ",
4398 *def
? short_str(def
,fileb
,sizeof(fileb
),40,EndDots
) : "",
4400 p5
[sizeof(p5
)-1] = '\0';
4403 if((needed
= l1
+l2
+l3
+l4
+l5
-avail
) > 0){
4404 snprintf(p4
, sizeof(p4
), " %s file%s%s",
4405 (flags
& GE_IS_IMPORT
) ? "from" : "to",
4406 is_absolute_path(filename
) ? "" : " in ",
4407 is_absolute_path(filename
) ? "" :
4408 (!dir
[0] ? "current dir"
4409 : (dir
[0] == '~' && !dir
[1]) ? "home dir"
4410 : short_str(dir
,dirb
,sizeof(dirb
),10,FrontDots
)));
4411 p4
[sizeof(p4
)-1] = '\0';
4415 if((needed
= l1
+l2
+l3
+l4
+l5
-avail
) > 0 && l5
> 0){
4416 snprintf(p5
, sizeof(p5
), "%s%s%s: ",
4418 *def
? short_str(def
,fileb
,sizeof(fileb
),
4419 MAX(15,l5
-5-needed
),EndDots
) : "",
4421 p5
[sizeof(p5
)-1] = '\0';
4425 if((needed
= l1
+l2
+l3
+l4
+l5
-avail
) > 0 && l2
> 0){
4428 * 14 is about the shortest we can make this, because there are
4429 * fixed length strings of length 14 coming in here.
4431 p
= short_str(prompt_msg
, p2
, sizeof(p2
), MAX(14,l2
-needed
), FrontDots
);
4433 strncpy(p2
, p
, sizeof(p2
)-1);
4434 p2
[sizeof(p2
)-1] = '\0';
4440 if((needed
= l1
+l2
+l3
+l4
+l5
-avail
) > 0){
4441 strncpy(p1
, "Copy ", sizeof(p1
)-1);
4442 p1
[sizeof(p1
)-1] = '\0';
4446 if((needed
= l1
+l2
+l3
+l4
+l5
-avail
) > 0 && l5
> 0){
4447 snprintf(p5
, sizeof(p5
), "%s%s%s: ",
4449 *def
? short_str(def
,fileb
, sizeof(fileb
),
4450 MAX(10,l5
-5-needed
),EndDots
) : "",
4452 p5
[sizeof(p5
)-1] = '\0';
4456 if((needed
= l1
+l2
+l3
+l4
+l5
-avail
) > 0 && l3
> 0){
4457 if(needed
<= l3
- strlen(" (+ atts)"))
4459 else if(needed
<= l3
- strlen(" (atts)"))
4461 else if(needed
<= l3
- strlen(" (+)"))
4463 else if(needed
<= l3
- strlen("+"))
4471 snprintf(prompt_buf
, sizeof(prompt_buf
), "%s%s%s%s%s", p1
, p2
, p3
, p4
, p5
);
4472 prompt_buf
[sizeof(prompt_buf
)-1] = '\0';
4475 if(items_in_hist(dir_hist
) > 0 || hist_len
> 0){ /* any directories */
4476 opts
[kp
].name
= "^Y";
4477 opts
[kp
].label
= "Prev Dir";
4478 opts
[kp
+1].name
= "^V";
4479 opts
[kp
+1].label
= "Next Dir";
4483 opts
[kp
].label
= "";
4484 opts
[kp
+1].name
= "";
4485 opts
[kp
+1].label
= "";
4490 if(items_in_hist(*history
) > 0){
4491 opts
[ku
].name
= HISTORY_UP_KEYNAME
;
4492 opts
[ku
].label
= HISTORY_KEYLABEL
;
4493 opts
[ku
+1].name
= HISTORY_DOWN_KEYNAME
;
4494 opts
[ku
+1].label
= HISTORY_KEYLABEL
;
4498 opts
[ku
].label
= "";
4499 opts
[ku
+1].name
= "";
4500 opts
[ku
+1].label
= "";
4504 oeflags
= OE_APPEND_CURRENT
|
4505 ((flags
& GE_SEQ_SENSITIVE
) ? OE_SEQ_SENSITIVE
: 0);
4506 r
= optionally_enter(filename
, qline
, 0, len
, prompt_buf
,
4507 opts
, NO_HELP
, &oeflags
);
4509 dprint((2, "\n - export_filename = \"%s\", r = %d -\n", filename
, r
));
4513 * Helps may not be right if you add another caller or change
4514 * things. Check it out.
4516 if(flags
& GE_IS_IMPORT
)
4517 helper(h_ge_import
, _("HELP FOR IMPORT FILE SELECT"), HLPD_SIMPLE
);
4518 else if(flags
& GE_ALLPARTS
)
4519 helper(h_ge_allparts
, _("HELP FOR EXPORT FILE SELECT"), HLPD_SIMPLE
);
4521 helper(h_ge_export
, _("HELP FOR EXPORT FILE SELECT"), HLPD_SIMPLE
);
4523 ps
->mangled_screen
= 1;
4527 else if(r
== 10 || r
== 11){ /* Browser or File Completion */
4528 if(filename
[0]=='~'){
4529 if(filename
[1] == C_FILESEP
&& filename
[2]!='\0'){
4532 for(i
=0; filename
[i
+2] != '\0' && i
+2 < len
-1; i
++)
4533 filename
[i
] = filename
[i
+2];
4535 strncpy(dir
, precolon
, sizeof(dir
)-1);
4536 dir
[sizeof(dir
)-1] = '\0';
4538 else if(filename
[1]=='\0' ||
4539 (filename
[1] == C_FILESEP
&& filename
[2] == '\0')){
4543 strncpy(dir
, precolon
, sizeof(dir
)-1);
4544 dir
[sizeof(dir
)-1] = '\0';
4547 else if(!dir
[0] && !is_absolute_path(filename
) && was_abs_path
){
4551 strncpy(dir
, precolon
, sizeof(dir
)-1);
4552 dir
[sizeof(dir
)-1] = '\0';
4561 strncpy(tmp
, filename
, sizeof(tmp
)-1);
4562 tmp
[sizeof(tmp
)-1] = '\0';
4563 if(*tmp
&& is_absolute_path(tmp
))
4564 fnexpand(tmp
, sizeof(tmp
));
4565 if(strncmp(tmp
,postcolon
, strlen(postcolon
)))
4566 postcolon
[0] = '\0';
4568 if(*tmp
&& (fn
= last_cmpnt(tmp
))){
4570 strncpy(filename2
, fn
, sizeof(filename2
)-1);
4571 filename2
[sizeof(filename2
)-1] = '\0';
4572 if(is_absolute_path(tmp
)){
4573 strncpy(dir2
, tmp
, MIN(fn
- tmp
, sizeof(dir2
)-1));
4574 dir2
[MIN(fn
- tmp
, sizeof(dir2
)-1)] = '\0';
4576 if(tmp
[1]==':' && tmp
[2]=='\\' && dir2
[2]=='\0'){
4581 strncpy(postcolon
, dir2
, sizeof(postcolon
)-1);
4582 postcolon
[sizeof(postcolon
)-1] = '\0';
4588 * Just building the directory name in dir2,
4589 * full_filename is overloaded.
4591 snprintf(full_filename
, len
, "%.*s", (int) MIN(fn
-tmp
,len
-1), tmp
);
4592 full_filename
[len
-1] = '\0';
4593 strncpy(postcolon
, full_filename
, sizeof(postcolon
)-1);
4594 postcolon
[sizeof(postcolon
)-1] = '\0';
4595 build_path(dir2
, !dir
[0] ? p
= (char *)getcwd(NULL
,MAXPATH
)
4596 : (dir
[0] == '~' && !dir
[1])
4599 full_filename
, sizeof(dir2
));
4605 if(is_absolute_path(tmp
)){
4606 strncpy(dir2
, tmp
, sizeof(dir2
)-1);
4607 dir2
[sizeof(dir2
)-1] = '\0';
4609 if(dir2
[2]=='\0' && dir2
[1]==':'){
4612 strncpy(postcolon
,dir2
,sizeof(postcolon
)-1);
4613 postcolon
[sizeof(postcolon
)-1] = '\0';
4616 filename2
[0] = '\0';
4620 strncpy(filename2
, tmp
, sizeof(filename2
)-1);
4621 filename2
[sizeof(filename2
)-1] = '\0';
4623 (void)getcwd(dir2
, sizeof(dir2
));
4624 else if(dir
[0] == '~' && !dir
[1]){
4625 strncpy(dir2
, ps
->home_dir
, sizeof(dir2
)-1);
4626 dir2
[sizeof(dir2
)-1] = '\0';
4629 strncpy(dir2
, dir
, sizeof(dir2
)-1);
4630 dir2
[sizeof(dir2
)-1] = '\0';
4633 postcolon
[0] = '\0';
4637 build_path(full_filename
, dir2
, filename2
, len
);
4638 if(!strcmp(full_filename
, dir2
))
4639 filename2
[0] = '\0';
4640 if(full_filename
[strlen(full_filename
)-1] == C_FILESEP
4641 && isdir(full_filename
,NULL
,NULL
)){
4642 if(strlen(full_filename
) == 1)
4643 strncpy(postcolon
, full_filename
, sizeof(postcolon
)-1);
4644 else if(filename2
[0])
4645 strncpy(postcolon
, filename2
, sizeof(postcolon
)-1);
4646 postcolon
[sizeof(postcolon
)-1] = '\0';
4647 strncpy(dir2
, full_filename
, sizeof(dir2
)-1);
4648 dir2
[sizeof(dir2
)-1] = '\0';
4649 filename2
[0] = '\0';
4651 #ifdef _WINDOWS /* use full_filename even if not a valid directory */
4652 else if(full_filename
[strlen(full_filename
)-1] == C_FILESEP
){
4653 strncpy(postcolon
, filename2
, sizeof(postcolon
)-1);
4654 postcolon
[sizeof(postcolon
)-1] = '\0';
4655 strncpy(dir2
, full_filename
, sizeof(dir2
)-1);
4656 dir2
[sizeof(dir2
)-1] = '\0';
4657 filename2
[0] = '\0';
4660 if(dir2
[strlen(dir2
)-1] == C_FILESEP
&& strlen(dir2
)!=1
4661 && strcmp(dir2
+1, ":\\"))
4662 /* last condition to prevent stripping of '\\'
4663 in windows partition */
4664 dir2
[strlen(dir2
)-1] = '\0';
4666 if(r
== 10){ /* File Browser */
4667 r
= file_lister(lister_msg
? lister_msg
: "EXPORT",
4668 dir2
, sizeof(dir2
), filename2
, sizeof(filename2
),
4670 (flags
& GE_IS_IMPORT
) ? FB_READ
: FB_SAVE
);
4672 /* Windows has a special "feature" in which entering the file browser will
4673 change the working directory if the directory is changed at all (even
4674 clicking "Cancel" will change the working directory).
4676 if(F_ON(F_USE_CURRENT_DIR
, ps
))
4677 (void)getcwd(dir2
,sizeof(dir2
));
4679 if(isdir(dir2
,NULL
,NULL
)){
4680 strncpy(precolon
, dir2
, sizeof(precolon
)-1);
4681 precolon
[sizeof(precolon
)-1] = '\0';
4683 strncpy(postcolon
, filename2
, sizeof(postcolon
)-1);
4684 postcolon
[sizeof(postcolon
)-1] = '\0';
4686 build_path(full_filename
, dir2
, filename2
, len
);
4687 if(isdir(full_filename
, NULL
, NULL
)){
4688 strncpy(dir
, full_filename
, sizeof(dir
)-1);
4689 dir
[sizeof(dir
)-1] = '\0';
4693 fn
= last_cmpnt(full_filename
);
4694 strncpy(dir
, full_filename
,
4695 MIN(fn
- full_filename
, sizeof(dir
)-1));
4696 dir
[MIN(fn
- full_filename
, sizeof(dir
)-1)] = '\0';
4697 if(fn
- full_filename
> 1)
4698 dir
[fn
- full_filename
- 1] = '\0';
4701 if(!strcmp(dir
, ps
->home_dir
)){
4706 strncpy(filename
, fn
, len
-1);
4707 filename
[len
-1] = '\0';
4710 else{ /* File Completion */
4711 if(!pico_fncomplete(dir2
, filename2
, sizeof(filename2
)))
4713 strncat(postcolon
, filename2
,
4714 sizeof(postcolon
)-1-strlen(postcolon
));
4715 postcolon
[sizeof(postcolon
)-1] = '\0';
4717 was_abs_path
= is_absolute_path(filename
);
4719 if(!strcmp(dir
, ps
->home_dir
)){
4724 strncpy(filename
, postcolon
, len
-1);
4725 filename
[len
-1] = '\0';
4726 strncpy(dir
, precolon
, sizeof(dir
)-1);
4727 dir
[sizeof(dir
)-1] = '\0';
4729 if(filename
[0] == '~' && !filename
[1]){
4737 else if(r
== 12){ /* Download, caller handles it */
4741 else if(r
== 13){ /* toggle AllParts bit */
4743 if(*rflags
& GER_ALLPARTS
){
4744 *rflags
&= ~GER_ALLPARTS
;
4745 opts
[allparts
].label
= N_("AllParts");
4748 *rflags
|= GER_ALLPARTS
;
4749 /* opposite of All Parts, No All Parts */
4750 opts
[allparts
].label
= N_("NoAllParts");
4757 else if(r
== 14){ /* List file names matching partial? */
4761 else if(r
== 15){ /* toggle Binary bit */
4763 if(*rflags
& GER_BINARY
){
4764 *rflags
&= ~GER_BINARY
;
4765 opts
[binary
].label
= N_("Binary");
4768 *rflags
|= GER_BINARY
;
4769 opts
[binary
].label
= N_("No Binary");
4775 else if(r
== 1){ /* Cancel */
4782 else if(r
>= 30 && r
<= 33){
4785 if(r
== 30 || r
== 31){
4788 p
= get_prev_hist(*history
, filename
, 0, NULL
);
4790 p
= get_next_hist(*history
, filename
, 0, NULL
);
4794 if(r
== 32 || r
== 33){
4795 int nitems
= items_in_hist(dir_hist
);
4796 if(dir_hist
|| hist_len
> 0){
4799 p
= hist_in_pos(--pos
, ps_global
->VAR_HISTORY
, hist_len
, dir_hist
, nitems
);
4803 if(pos
< hist_len
+ nitems
)
4804 p
= hist_in_pos(++pos
, ps_global
->VAR_HISTORY
, hist_len
, dir_hist
, nitems
);
4806 if(p
== NULL
|| *p
== '\0')
4810 last
= p
; /* save it! */
4812 if(p
!= NULL
&& *p
!= '\0'){
4813 if(r
== 30 || r
== 31){
4814 if((fn
= last_cmpnt(p
)) != NULL
){
4815 strncpy(dir
, p
, MIN(fn
- p
, sizeof(dir
)-1));
4816 dir
[MIN(fn
- p
, sizeof(dir
)-1)] = '\0';
4818 dir
[fn
- p
- 1] = '\0';
4819 strncpy(filename
, fn
, len
-1);
4820 filename
[len
-1] = '\0';
4822 } else { /* r == 32 || r == 33 */
4823 strncpy(dir
, p
, sizeof(dir
)-1);
4824 dir
[sizeof(dir
)-1] = '\0';
4827 if(!strcmp(dir
, ps
->home_dir
)){
4837 rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
,
4838 SIZEOF_20KBUF
, filename
);
4839 strncpy(filename
, tmp_20k_buf
, len
);
4840 filename
[len
-1] = '\0';
4848 removing_leading_and_trailing_white_space(filename
);
4851 if(!*def
){ /* Cancel */
4856 strncpy(filename
, def
, len
-1);
4857 filename
[len
-1] = '\0';
4860 #if defined(DOS) || defined(OS2)
4861 if(is_absolute_path(filename
)){
4862 fixpath(filename
, len
);
4865 if(filename
[0] == '~'){
4866 if(fnexpand(filename
, len
) == NULL
){
4867 char *p
= strindex(filename
, '/');
4870 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
4871 _("Error expanding file name: \"%s\" unknown user"),
4878 if(is_absolute_path(filename
)){
4879 strncpy(full_filename
, filename
, len
-1);
4880 full_filename
[len
-1] = '\0';
4884 build_path(full_filename
, (char *)getcwd(dir
,sizeof(dir
)),
4886 else if(dir
[0] == '~' && !dir
[1])
4887 build_path(full_filename
, ps
->home_dir
, filename
, len
);
4889 build_path(full_filename
, dir
, filename
, len
);
4892 if((ill
= filter_filename(full_filename
, &fatal
,
4893 ps_global
->restricted
|| ps_global
->VAR_OPER_DIR
)) != NULL
){
4895 q_status_message1(SM_ORDER
| SM_DING
, 3, 3, "%s", ill
);
4899 /* BUG: we should beep when the key's pressed rather than bitch later */
4900 /* Warn and ask for confirmation. */
4901 snprintf(prompt_buf
, sizeof(prompt_buf
), "File name contains a '%s'. %s anyway",
4902 ill
, (flags
& GE_IS_EXPORT
) ? "Export" : "Save");
4903 prompt_buf
[sizeof(prompt_buf
)-1] = '\0';
4904 if(want_to(prompt_buf
, 'n', 0, NO_HELP
,
4905 ((flags
& GE_SEQ_SENSITIVE
) ? RB_SEQ_SENSITIVE
: 0)) != 'y')
4910 break; /* Must have got an OK file name */
4913 if(VAR_OPER_DIR
&& !in_dir(VAR_OPER_DIR
, full_filename
)){
4918 if(!can_access(full_filename
, ACCESS_EXISTS
)){
4920 static ESCKEY_S access_opts
[] = {
4921 /* TRANSLATORS: asking user if they want to overwrite (replace contents of)
4922 a file or append to the end of the file */
4923 {'o', 'o', "O", N_("Overwrite")},
4924 {'a', 'a', "A", N_("Append")},
4925 {-1, 0, NULL
, NULL
}};
4927 rbflags
= RB_NORM
| ((flags
& GE_SEQ_SENSITIVE
) ? RB_SEQ_SENSITIVE
: 0);
4929 if(flags
& GE_NO_APPEND
){
4930 r
= strlen(filename
);
4931 snprintf(prompt_buf
, sizeof(prompt_buf
),
4932 /* TRANSLATORS: asking user whether to overwrite a file or not,
4933 File <filename> already exists. Overwrite it ? */
4934 _("File \"%s%s\" already exists. Overwrite it "),
4935 (r
> 20) ? "..." : "",
4936 filename
+ ((r
> 20) ? r
- 20 : 0));
4937 prompt_buf
[sizeof(prompt_buf
)-1] = '\0';
4938 if(want_to(prompt_buf
, 'n', 'x', NO_HELP
, rbflags
) == 'y'){
4940 *rflags
|= GER_OVER
;
4942 if(our_unlink(full_filename
) < 0){
4943 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
4944 /* TRANSLATORS: Cannot remove old <filename>: <error text> */
4945 _("Cannot remove old %s: %s"),
4946 full_filename
, error_description(errno
));
4954 else if(!(flags
& GE_IS_IMPORT
)){
4955 r
= strlen(filename
);
4956 snprintf(prompt_buf
, sizeof(prompt_buf
),
4957 /* TRANSLATORS: File <filename> already exists. Overwrite or append to it ? */
4958 _("File \"%s%s\" already exists. Overwrite or append to it ? "),
4959 (r
> 20) ? "..." : "",
4960 filename
+ ((r
> 20) ? r
- 20 : 0));
4961 prompt_buf
[sizeof(prompt_buf
)-1] = '\0';
4962 switch(radio_buttons(prompt_buf
, -FOOTER_ROWS(ps_global
),
4963 access_opts
, 'a', 'x', NO_HELP
, rbflags
)){
4966 *rflags
|= GER_OVER
;
4968 if(our_truncate(full_filename
, (off_t
)0) < 0)
4969 /* trouble truncating, but we'll give it a try anyway */
4970 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
4971 /* TRANSLATORS: Warning: Cannot truncate old <filename>: <error text> */
4972 _("Warning: Cannot truncate old %s: %s"),
4973 full_filename
, error_description(errno
));
4978 *rflags
|= GER_APPEND
;
4991 if(history
&& ret
== 0){
4992 save_hist(*history
, full_filename
, 0, NULL
);
4993 strncpy(tmp
, full_filename
, MAXPATH
);
4994 tmp
[MAXPATH
] = '\0';
4995 if((fn
= strrchr(tmp
, C_FILESEP
)) != NULL
)
5000 save_hist(dir_hist
, tmp
, 0, NULL
);
5003 if(opts
&& opts
!= optsarg
)
5004 fs_give((void **) &opts
);
5010 /*----------------------------------------------------------------------
5011 parse the config'd upload/download command
5013 Args: cmd -- buffer to return command fit for shellin'
5016 fname -- file name to build into the command
5018 Returns: pointer to cmd_str buffer or NULL on real bad error
5020 NOTE: One SIDE EFFECT is that any defined "prefix" string in the
5021 cfg_str is written to standard out right before a successful
5022 return of this function. The call immediately following this
5023 function darn well better be the shell exec...
5026 build_updown_cmd(char *cmd
, size_t cmdlen
, char *prefix
, char *cfg_str
, char *fname
)
5029 int fname_found
= 0;
5031 if(prefix
&& *prefix
){
5032 /* loop thru replacing all occurances of _FILE_ */
5033 p
= strncpy(cmd
, prefix
, cmdlen
);
5034 cmd
[cmdlen
-1] = '\0';
5035 while((p
= strstr(p
, "_FILE_")))
5036 rplstr(p
, cmdlen
-(p
-cmd
), 6, fname
);
5041 /* loop thru replacing all occurances of _FILE_ */
5042 p
= strncpy(cmd
, cfg_str
, cmdlen
);
5043 cmd
[cmdlen
-1] = '\0';
5044 while((p
= strstr(p
, "_FILE_"))){
5045 rplstr(p
, cmdlen
-(p
-cmd
), 6, fname
);
5050 snprintf(cmd
+strlen(cmd
), cmdlen
-strlen(cmd
), " %s", fname
);
5052 cmd
[cmdlen
-1] = '\0';
5054 dprint((4, "\n - build_updown_cmd = \"%s\" -\n",
5060 /*----------------------------------------------------------------------
5061 Write a berzerk format message delimiter using the given putc function
5063 Args: e -- envelope of message to write
5064 pc -- function to use
5066 Returns: TRUE if we could write it, FALSE if there was a problem
5068 NOTE: follows delimiter with OS-dependent newline
5071 bezerk_delimiter(ENVELOPE
*env
, MESSAGECACHE
*mc
, gf_io_t pc
, int leading_newline
)
5077 /* write "[\n]From mailbox[@host] " */
5078 if(!((leading_newline
? gf_puts(NEWLINE
, pc
) : 1)
5079 && gf_puts("From ", pc
)
5080 && gf_puts((env
&& env
->from
) ? env
->from
->mailbox
5081 : "the-concourse-on-high", pc
)
5082 && gf_puts((env
&& env
->from
&& env
->from
->host
) ? "@" : "", pc
)
5083 && gf_puts((env
&& env
->from
&& env
->from
->host
) ? env
->from
->host
5089 when
= mail_longdate(mc
);
5090 else if(env
&& env
->date
&& env
->date
[0]
5091 && mail_parse_date(&telt
,env
->date
))
5092 when
= mail_longdate(&telt
);
5098 while(p
&& *p
&& *p
!= '\n') /* write date */
5102 if(!gf_puts(NEWLINE
, pc
)) /* write terminating newline */
5109 /*----------------------------------------------------------------------
5110 Execute command to jump to a given message number
5112 Args: qline -- Line to ask question on
5114 Result: returns true if the use selected a new message, false otherwise
5118 jump_to(MSGNO_S
*msgmap
, int qline
, UCS first_num
, SCROLL_S
*sparms
, CmdWhere in_index
)
5120 char jump_num_string
[80], *j
, prompt
[70];
5123 static ESCKEY_S jump_to_key
[] = { {0, 0, NULL
, NULL
},
5124 /* TRANSLATORS: go to First Message */
5125 {ctrl('Y'), 10, "^Y", N_("First Msg")},
5126 {ctrl('V'), 11, "^V", N_("Last Msg")},
5127 {-1, 0, NULL
, NULL
} };
5129 dprint((4, "\n - jump_to -\n"));
5132 if(sparms
&& sparms
->jump_is_debug
)
5133 return(get_level(qline
, first_num
, sparms
));
5136 if(!any_messages(msgmap
, NULL
, "to Jump to"))
5139 if(first_num
&& first_num
< 0x80 && isdigit((unsigned char) first_num
)){
5140 jump_num_string
[0] = first_num
;
5141 jump_num_string
[1] = '\0';
5144 jump_num_string
[0] = '\0';
5146 if(mn_total_cur(msgmap
) > 1L){
5147 snprintf(prompt
, sizeof(prompt
), "Unselect %s msgs in favor of number to be entered",
5148 comatose(mn_total_cur(msgmap
)));
5149 prompt
[sizeof(prompt
)-1] = '\0';
5150 if((rc
= want_to(prompt
, 'n', 0, NO_HELP
, WT_NORM
)) == 'n')
5154 snprintf(prompt
, sizeof(prompt
), "%s number to jump to : ", in_index
== ThrdIndx
5157 prompt
[sizeof(prompt
)-1] = '\0';
5161 int flags
= OE_APPEND_CURRENT
;
5163 rc
= optionally_enter(jump_num_string
, qline
, 0,
5164 sizeof(jump_num_string
), prompt
,
5165 jump_to_key
, help
, &flags
);
5167 help
= help
== NO_HELP
5168 ? (in_index
== ThrdIndx
? h_oe_jump_thd
: h_oe_jump
)
5172 else if(rc
== 10 || rc
== 11){
5176 closest
= closest_jump_target(rc
== 10 ? 1L
5177 : ((in_index
== ThrdIndx
)
5178 ? msgmap
->max_thrdno
5179 : mn_get_total(msgmap
)),
5180 ps_global
->mail_stream
,
5182 in_index
, warning
, sizeof(warning
));
5183 /* ignore warning */
5188 * If we take out the *jump_num_string nonempty test in this if
5189 * then the closest_jump_target routine will offer a jump to the
5190 * last message. However, it is slow because you have to wait for
5191 * the status message and it is annoying for people who hit J command
5192 * by mistake and just want to hit return to do nothing, like has
5193 * always worked. So the test is there for now. Hubert 2002-08-19
5195 * Jumping to first/last message is now possible through ^Y/^V
5196 * commands above. jpf 2002-08-21
5197 * (and through "end" hubert 2006-07-07)
5199 if(rc
== 0 && *jump_num_string
!= '\0'){
5200 removing_leading_and_trailing_white_space(jump_num_string
);
5201 for(j
=jump_num_string
; isdigit((unsigned char)*j
) || *j
=='-'; j
++)
5205 if(!strucmp("end", j
))
5206 return((in_index
== ThrdIndx
) ? msgmap
->max_thrdno
: mn_get_total(msgmap
));
5208 q_status_message(SM_ORDER
| SM_DING
, 2, 2,
5209 _("Invalid number entered. Use only digits 0-9"));
5210 jump_num_string
[0] = '\0';
5214 long closest
, jump_num
;
5216 if(*jump_num_string
)
5217 jump_num
= atol(jump_num_string
);
5222 closest
= closest_jump_target(jump_num
, ps_global
->mail_stream
,
5224 *jump_num_string
? 0 : 1,
5225 in_index
, warning
, sizeof(warning
));
5227 q_status_message(SM_ORDER
| SM_DING
, 2, 2, warning
);
5229 if(closest
== jump_num
)
5233 jump_num_string
[0] = '\0';
5235 strncpy(jump_num_string
, long2string(closest
),
5236 sizeof(jump_num_string
));
5251 * cmd_delete_action - handle msgno advance and such after single message deletion
5254 cmd_delete_action(struct pine
*state
, MSGNO_S
*msgmap
, CmdWhere in_index
)
5260 msgno
= mn_get_cur(msgmap
);
5261 advance_cur_after_delete(state
, state
->mail_stream
, msgmap
, in_index
);
5263 if(IS_NEWS(state
->mail_stream
)
5264 || ((state
->context_current
->use
& CNTXT_INCMNG
)
5265 && context_isambig(state
->cur_folder
))){
5267 opts
= (NSF_TRUST_FLAGS
| NSF_SKIP_CHID
);
5268 if(in_index
== View
)
5269 opts
&= ~NSF_SKIP_CHID
;
5271 (void)next_sorted_flagged(F_UNDEL
|F_UNSEEN
, state
->mail_stream
, msgno
, &opts
);
5272 if(!(opts
& NSF_FLAG_MATCH
)){
5273 char nextfolder
[MAXPATH
];
5275 strncpy(nextfolder
, state
->cur_folder
, sizeof(nextfolder
));
5276 nextfolder
[sizeof(nextfolder
)-1] = '\0';
5277 rv
= next_folder(NULL
, nextfolder
, sizeof(nextfolder
), nextfolder
,
5278 state
->context_current
, NULL
, NULL
)
5279 ? ". Press TAB for next folder."
5280 : ". No more folders to TAB to.";
5289 * cmd_delete_index - fixup msgmap or whatever after cmd_delete has done it's thing
5292 cmd_delete_index(struct pine
*state
, MSGNO_S
*msgmap
)
5294 return(cmd_delete_action(state
, msgmap
,MsgIndx
));
5298 * cmd_delete_view - fixup msgmap or whatever after cmd_delete has done it's thing
5301 cmd_delete_view(struct pine
*state
, MSGNO_S
*msgmap
)
5303 return(cmd_delete_action(state
, msgmap
, View
));
5308 advance_cur_after_delete(struct pine
*state
, MAILSTREAM
*stream
, MSGNO_S
*msgmap
, CmdWhere in_index
)
5310 long new_msgno
, msgno
;
5313 new_msgno
= msgno
= mn_get_cur(msgmap
);
5314 opts
= NSF_TRUST_FLAGS
;
5316 if(F_ON(F_DEL_SKIPS_DEL
, state
)){
5318 if(THREADING() && sp_viewing_a_thread(stream
))
5319 opts
|= NSF_SKIP_CHID
;
5321 new_msgno
= next_sorted_flagged(F_UNDEL
, stream
, msgno
, &opts
);
5324 mn_inc_cur(stream
, msgmap
,
5325 (in_index
== View
&& THREADING()
5326 && sp_viewing_a_thread(stream
))
5328 : (in_index
== View
)
5329 ? MH_ANYTHD
: MH_NONE
);
5330 new_msgno
= mn_get_cur(msgmap
);
5331 if(new_msgno
!= msgno
)
5332 opts
|= NSF_FLAG_MATCH
;
5336 * Viewing_a_thread is the complicated case because we want to ignore
5337 * other threads at first and then look in other threads if we have to.
5338 * By ignoring other threads we also ignore collapsed partial threads
5339 * in our own thread.
5341 if(THREADING() && sp_viewing_a_thread(stream
) && !(opts
& NSF_FLAG_MATCH
)){
5342 long rawno
, orig_thrdno
;
5343 PINETHRD_S
*thrd
, *topthrd
= NULL
;
5345 rawno
= mn_m2raw(msgmap
, msgno
);
5346 thrd
= fetch_thread(stream
, rawno
);
5347 if(thrd
&& thrd
->top
)
5348 topthrd
= fetch_thread(stream
, thrd
->top
);
5350 orig_thrdno
= topthrd
? topthrd
->thrdno
: -1L;
5352 opts
= NSF_TRUST_FLAGS
;
5353 new_msgno
= next_sorted_flagged(F_UNDEL
, stream
, msgno
, &opts
);
5356 * If we got a match, new_msgno may be a message in
5357 * a different thread from the one we are viewing, or it could be
5358 * in a collapsed part of this thread.
5360 if(opts
& NSF_FLAG_MATCH
){
5365 thrd
= fetch_thread(stream
, mn_m2raw(msgmap
,new_msgno
));
5366 if(thrd
&& thrd
->top
)
5367 topthrd
= fetch_thread(stream
, thrd
->top
);
5370 * If this match is in the same thread we're already in
5371 * then we're done, else we have to ask the user and maybe
5374 if(!(orig_thrdno
> 0L && topthrd
5375 && topthrd
->thrdno
== orig_thrdno
)){
5377 if(F_OFF(F_AUTO_OPEN_NEXT_UNREAD
, state
)){
5378 if(in_index
== View
)
5379 snprintf(pmt
, sizeof(pmt
),
5380 "View message in thread number %.10s",
5381 topthrd
? comatose(topthrd
->thrdno
) : "?");
5383 snprintf(pmt
, sizeof(pmt
), "View thread number %.10s",
5384 topthrd
? comatose(topthrd
->thrdno
) : "?");
5386 ret
= want_to(pmt
, 'y', 'x', NO_HELP
, WT_NORM
);
5392 unview_thread(state
, stream
, msgmap
);
5393 mn_set_cur(msgmap
, new_msgno
);
5395 && (count_lflags_in_thread(stream
, topthrd
, msgmap
,
5397 && view_thread(state
, stream
, msgmap
, 1)){
5398 if(current_index_state
)
5399 msgmap
->top_after_thrd
= current_index_state
->msg_at_top
;
5401 state
->view_skipped_index
= 1;
5402 state
->next_screen
= mail_view_screen
;
5405 view_thread(state
, stream
, msgmap
, 1);
5406 if(current_index_state
)
5407 msgmap
->top_after_thrd
= current_index_state
->msg_at_top
;
5409 state
->next_screen
= SCREEN_FUN_NULL
;
5413 new_msgno
= msgno
; /* stick with original */
5418 mn_set_cur(msgmap
, new_msgno
);
5419 if(in_index
!= View
)
5420 adjust_cur_to_visible(stream
, msgmap
);
5426 get_level(int qline
, UCS first_num
, SCROLL_S
*sparms
)
5428 char debug_num_string
[80], *j
, prompt
[70];
5433 if(first_num
&& first_num
< 0x80 && isdigit((unsigned char)first_num
)){
5434 debug_num_string
[0] = first_num
;
5435 debug_num_string
[1] = '\0';
5436 debug_num
= atol(debug_num_string
);
5437 *(int *)(sparms
->proc
.data
.p
) = debug_num
;
5438 q_status_message1(SM_ORDER
, 0, 3, "Show debug <= level %s",
5439 comatose(debug_num
));
5443 debug_num_string
[0] = '\0';
5445 snprintf(prompt
, sizeof(prompt
), "Show debug <= this level (0-%d) : ", MAX(debug
, 9));
5446 prompt
[sizeof(prompt
)-1] = '\0';
5450 int flags
= OE_APPEND_CURRENT
;
5452 rc
= optionally_enter(debug_num_string
, qline
, 0,
5453 sizeof(debug_num_string
), prompt
,
5454 NULL
, help
, &flags
);
5456 help
= help
== NO_HELP
? h_oe_debuglevel
: NO_HELP
;
5461 removing_leading_and_trailing_white_space(debug_num_string
);
5462 for(j
=debug_num_string
; isdigit((unsigned char)*j
); j
++)
5466 q_status_message(SM_ORDER
| SM_DING
, 2, 2,
5467 _("Invalid number entered. Use only digits 0-9"));
5468 debug_num_string
[0] = '\0';
5471 debug_num
= atol(debug_num_string
);
5473 q_status_message(SM_ORDER
| SM_DING
, 2, 2,
5474 _("Number should be >= 0"));
5475 else if(debug_num
> MAX(debug
,9))
5476 q_status_message1(SM_ORDER
| SM_DING
, 2, 2,
5477 _("Maximum is %s"), comatose(MAX(debug
,9)));
5479 *(int *)(sparms
->proc
.data
.p
) = debug_num
;
5480 q_status_message1(SM_ORDER
, 0, 3,
5481 "Show debug <= level %s",
5482 comatose(debug_num
));
5500 * Returns the message number closest to target that isn't hidden.
5501 * Make warning at least 100 chars.
5502 * A return of 0 means there is no message to jump to.
5505 closest_jump_target(long int target
, MAILSTREAM
*stream
, MSGNO_S
*msgmap
, int no_target
, CmdWhere in_index
, char *warning
, size_t warninglen
)
5507 long i
, start
, closest
= 0L;
5512 maxnum
= (in_index
== ThrdIndx
) ? msgmap
->max_thrdno
: mn_get_total(msgmap
);
5517 snprintf(warning
, warninglen
, "No %s number entered, jump to end? ",
5518 (in_index
== ThrdIndx
) ? "thread" : "message");
5519 warning
[warninglen
-1] = '\0';
5521 else if(target
< 1L)
5522 start
= 1L - target
;
5523 else if(target
> maxnum
)
5524 start
= target
- maxnum
;
5528 if(target
> 0L && target
<= maxnum
)
5529 if(in_index
== ThrdIndx
5530 || !msgline_hidden(stream
, msgmap
, target
, 0))
5533 for(i
= start
; target
+i
<= maxnum
|| target
-i
> 0L; i
++){
5535 if(target
+i
> 0L && target
+i
<= maxnum
&&
5536 (in_index
== ThrdIndx
5537 || !msgline_hidden(stream
, msgmap
, target
+i
, 0))){
5542 if(target
-i
> 0L && target
-i
<= maxnum
&&
5543 (in_index
== ThrdIndx
5544 || !msgline_hidden(stream
, msgmap
, target
-i
, 0))){
5550 strncpy(buf
, long2string(closest
), sizeof(buf
));
5551 buf
[sizeof(buf
)-1] = '\0';
5554 strncpy(warning
, "Nothing to jump to", warninglen
);
5555 else if(target
< 1L)
5556 snprintf(warning
, warninglen
, "%s number (%s) must be at least %s",
5557 (in_index
== ThrdIndx
) ? "Thread" : "Message",
5558 long2string(target
), buf
);
5559 else if(target
> maxnum
)
5560 snprintf(warning
, warninglen
, "%s number (%s) may be no more than %s",
5561 (in_index
== ThrdIndx
) ? "Thread" : "Message",
5562 long2string(target
), buf
);
5564 snprintf(warning
, warninglen
,
5565 "Message number (%s) is not in \"Zoomed Index\" - Closest is(%s)",
5566 long2string(target
), buf
);
5568 warning
[warninglen
-1] = '\0';
5574 /*----------------------------------------------------------------------
5575 Prompt for folder name to open, expand the name and return it
5577 Args: qline -- Screen line to prompt on
5578 allow_list -- if 1, allow ^T to bring up collection lister
5580 Result: returns the folder name or NULL
5581 pine structure mangled_footer flag is set
5582 may call the collection lister in which case mangled screen will be set
5584 This prompts the user for the folder to open, possibly calling up
5585 the collection lister if the user types ^T.
5586 ----------------------------------------------------------------------*/
5588 broach_folder(int qline
, int allow_list
, int *notrealinbox
, CONTEXT_S
**context
)
5591 static char newfolder
[MAILTMPLEN
];
5592 char expanded
[MAXPATH
+1],
5593 prompt
[MAX_SCREEN_COLS
+1],
5595 unsigned char *f1
, *f2
, *f3
;
5596 static HISTORY_S
*history
= NULL
;
5597 CONTEXT_S
*tc
, *tc2
;
5599 int rc
, r
, ku
= -1, n
, flags
, last_rc
= 0, inbox
, done
= 0;
5602 * the idea is to provide a clue for the context the file name
5603 * will be saved in (if a non-imap names is typed), and to
5604 * only show the previous if it was also in the same context
5611 (*notrealinbox
) = 1;
5613 init_hist(&history
, HISTSIZE
);
5615 tc
= broach_get_folder(context
? *context
: NULL
, &inbox
, NULL
);
5617 /* set up extra command option keys */
5619 ekey
[rc
].ch
= (allow_list
) ? ctrl('T') : 0 ;
5620 ekey
[rc
].rval
= (allow_list
) ? 2 : 0;
5621 ekey
[rc
].name
= (allow_list
) ? "^T" : "";
5622 ekey
[rc
++].label
= (allow_list
) ? N_("ToFldrs") : "";
5624 if(ps_global
->context_list
->next
){
5625 ekey
[rc
].ch
= ctrl('P');
5627 ekey
[rc
].name
= "^P";
5628 ekey
[rc
++].label
= N_("Prev Collection");
5630 ekey
[rc
].ch
= ctrl('N');
5632 ekey
[rc
].name
= "^N";
5633 ekey
[rc
++].label
= N_("Next Collection");
5636 ekey
[rc
].ch
= ctrl('W');
5638 ekey
[rc
].name
= "^W";
5639 ekey
[rc
++].label
= N_("INBOX");
5641 if(F_ON(F_ENABLE_TAB_COMPLETE
,ps_global
)){
5644 ekey
[rc
].name
= "TAB";
5645 ekey
[rc
++].label
= N_("Complete");
5648 if(F_ON(F_ENABLE_SUB_LISTS
, ps_global
)){
5649 ekey
[rc
].ch
= ctrl('X');
5651 ekey
[rc
].name
= "^X";
5652 ekey
[rc
++].label
= N_("ListMatches");
5655 if(ps_global
->context_list
->next
&& F_ON(F_DISABLE_SAVE_INPUT_HISTORY
, ps_global
)){
5656 ekey
[rc
].ch
= KEY_UP
;
5659 ekey
[rc
++].label
= "";
5661 ekey
[rc
].ch
= KEY_DOWN
;
5664 ekey
[rc
++].label
= "";
5666 else if(F_OFF(F_DISABLE_SAVE_INPUT_HISTORY
, ps_global
)){
5667 ekey
[rc
].ch
= KEY_UP
;
5671 ekey
[rc
++].label
= "";
5673 ekey
[rc
].ch
= KEY_DOWN
;
5676 ekey
[rc
++].label
= "";
5683 * Figure out next default value for this context. The idea
5684 * is that in each context the last folder opened is cached.
5685 * It's up to pick it out and display it. This is fine
5686 * and dandy if we've currently got the inbox open, BUT
5687 * if not, make the inbox the default the first time thru.
5690 last_folder
= ps_global
->inbox_name
;
5691 inbox
= 1; /* pretend we're in inbox from here on out */
5694 last_folder
= (ps_global
->last_unambig_folder
[0])
5695 ? ps_global
->last_unambig_folder
5696 : ((tc
->last_folder
[0]) ? tc
->last_folder
: NULL
);
5698 if(last_folder
){ /* MAXPATH + 1 = sizeof(expanded) */
5699 unsigned char *fname
= folder_name_decoded((unsigned char *)last_folder
);
5700 snprintf(expanded
, sizeof(expanded
), " [%.*s]", MAXPATH
+1-5,
5701 fname
? (char *) fname
: last_folder
);
5702 if(fname
) fs_give((void **)&fname
);
5707 expanded
[sizeof(expanded
)-1] = '\0';
5709 /* only show collection number if more than one available */
5710 if(ps_global
->context_list
->next
) /* MAX_SCREEN_COLS+1 == sizeof(prompt) */
5711 snprintf(prompt
, sizeof(prompt
), "GOTO %s in <%s> %.*s%s: ",
5712 NEWS_TEST(tc
) ? "news group" : "folder",
5713 tc
->nickname
, MAX_SCREEN_COLS
+1-50, expanded
,
5714 *expanded
? " " : "");
5715 else /* MAX_SCREEN_COLS+1 == sizeof(prompt) */
5716 snprintf(prompt
, sizeof(prompt
), "GOTO folder %.*s%s: ", MAX_SCREEN_COLS
+1-20, expanded
,
5717 *expanded
? " " : "");
5719 prompt
[sizeof(prompt
)-1] = '\0';
5721 if(utf8_width(prompt
) > MAXPROMPT
){
5722 if(ps_global
->context_list
->next
) /* MAX_SCREEN_COLS+1 == sizeof(prompt) */
5723 snprintf(prompt
, sizeof(prompt
), "GOTO <%s> %.*s%s: ",
5724 tc
->nickname
, MAX_SCREEN_COLS
+1-50, expanded
,
5725 *expanded
? " " : "");
5726 else /* MAX_SCREEN_COLS+1 == sizeof(prompt) */
5727 snprintf(prompt
, sizeof(prompt
), "GOTO %.*s%s: ", MAX_SCREEN_COLS
+1-20, expanded
,
5728 *expanded
? " " : "");
5730 prompt
[sizeof(prompt
)-1] = '\0';
5732 if(utf8_width(prompt
) > MAXPROMPT
){
5733 if(ps_global
->context_list
->next
) /* MAX_SCREEN_COLS+1 == sizeof(prompt) */
5734 snprintf(prompt
, sizeof(prompt
), "<%s> %.*s%s: ",
5735 tc
->nickname
, MAX_SCREEN_COLS
+1-50, expanded
,
5736 *expanded
? " " : "");
5737 else /* MAX_SCREEN_COLS+1 == sizeof(prompt) */
5738 snprintf(prompt
, sizeof(prompt
), "%.*s%s: ", MAX_SCREEN_COLS
+1-20, expanded
,
5739 *expanded
? " " : "");
5741 prompt
[sizeof(prompt
)-1] = '\0';
5746 if(items_in_hist(history
) > 1){
5747 ekey
[ku
].name
= HISTORY_UP_KEYNAME
;
5748 ekey
[ku
].label
= HISTORY_KEYLABEL
;
5749 ekey
[ku
+1].name
= HISTORY_DOWN_KEYNAME
;
5750 ekey
[ku
+1].label
= HISTORY_KEYLABEL
;
5754 ekey
[ku
].label
= "";
5755 ekey
[ku
+1].name
= "";
5756 ekey
[ku
+1].label
= "";
5760 /* is there any other way to do this? The point is that we
5761 * are trying to hide mutf7 from the user, and use the utf8
5762 * equivalent. So we create a variable f to take place of
5763 * newfolder, including content and size. f2 is copy of f1
5764 * that has to freed. Sigh!
5766 f3
= (unsigned char *) cpystr(newfolder
);
5767 f1
= fs_get(sizeof(newfolder
));
5768 f2
= folder_name_decoded(f3
);
5769 if(f3
) fs_give((void **)&f3
);
5770 strncpy((char *)f1
, (char *)f2
, sizeof(newfolder
));
5771 f1
[sizeof(newfolder
)-1] = '\0';
5772 if(f2
) fs_give((void **)&f2
);
5774 flags
= OE_APPEND_CURRENT
;
5775 rc
= optionally_enter((char *) f1
, qline
, 0, sizeof(newfolder
),
5776 (char *) prompt
, ekey
, help
, &flags
);
5778 f2
= folder_name_encoded(f1
);
5779 strncpy(newfolder
, (char *)f2
, sizeof(newfolder
));
5780 if(f1
) fs_give((void **)&f1
);
5781 if(f2
) fs_give((void **)&f2
);
5783 ps_global
->mangled_footer
= 1;
5786 case -1 : /* o_e says error! */
5787 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
5788 _("Error reading folder name"));
5791 case 0 : /* o_e says normal entry */
5792 removing_trailing_white_space(newfolder
);
5793 removing_leading_white_space(newfolder
);
5796 char *name
, *fullname
= NULL
;
5797 int exists
, breakout
= 0;
5799 save_hist(history
, newfolder
, 0, tc
);
5801 if(!(name
= folder_is_nick(newfolder
, FOLDERS(tc
),
5805 if(update_folder_spec(expanded
, sizeof(expanded
), name
)){
5806 strncpy(name
= newfolder
, expanded
, sizeof(newfolder
));
5807 newfolder
[sizeof(newfolder
)-1] = '\0';
5810 exists
= folder_name_exists(tc
, name
, &fullname
);
5813 strncpy(name
= newfolder
, fullname
, sizeof(newfolder
));
5814 newfolder
[sizeof(newfolder
)-1] = '\0';
5815 fs_give((void **) &fullname
);
5820 * if we know the things a folder, open it.
5821 * else if we know its a directory, visit it.
5822 * else we're not sure (it either doesn't really
5823 * exist or its unLISTable) so try opening it anyway
5825 if(exists
& FEX_ISFILE
){
5829 else if((exists
& FEX_ISDIR
)){
5831 CONTEXT_S
*fake_context
;
5832 char tmp
[MAILTMPLEN
];
5835 strncpy(tmp
, name
, sizeof(tmp
));
5836 tmp
[sizeof(tmp
)-2-1] = '\0';
5837 if(tmp
[(l
= strlen(tmp
)) - 1] != tc
->dir
->delim
){
5838 if(l
< sizeof(tmp
)){
5839 tmp
[l
] = tc
->dir
->delim
;
5840 strncpy(&tmp
[l
+1], "[]", sizeof(tmp
)-(l
+1));
5844 strncat(tmp
, "[]", sizeof(tmp
)-strlen(tmp
)-1);
5846 tmp
[sizeof(tmp
)-1] = '\0';
5848 fake_context
= new_context(tmp
, 0);
5849 newfolder
[0] = '\0';
5850 done
= display_folder_list(&fake_context
, newfolder
,
5851 1, folders_for_goto
);
5852 free_context(&fake_context
);
5855 else if(!(tc
->use
& CNTXT_INCMNG
)){
5856 done
= display_folder_list(&tc
, newfolder
,
5857 1, folders_for_goto
);
5861 else if((exists
& FEX_ERROR
)){
5862 q_status_message1(SM_ORDER
, 0, 3,
5863 _("Problem accessing folder \"%s\""),
5872 if(exists
== FEX_ERROR
)
5873 q_status_message1(SM_ORDER
, 0, 3,
5874 _("Problem accessing folder \"%s\""),
5876 else if(tc
->use
& CNTXT_INCMNG
)
5877 q_status_message1(SM_ORDER
, 0, 3,
5878 _("Can't find Incoming Folder: %s"),
5880 else if(context_isambig(newfolder
))
5881 q_status_message2(SM_ORDER
, 0, 3,
5882 _("Can't find folder \"%s\" in %s"),
5883 newfolder
, (void *) tc
->nickname
);
5885 q_status_message1(SM_ORDER
, 0, 3,
5886 _("Can't find folder \"%s\""),
5891 else if(last_folder
){
5892 if(ps_global
->goto_default_rule
== GOTO_FIRST_CLCTN_DEF_INBOX
5893 && !strucmp(last_folder
, ps_global
->inbox_name
)
5894 && tc
== ((ps_global
->context_list
->use
& CNTXT_INCMNG
)
5895 ? ps_global
->context_list
->next
: ps_global
->context_list
)){
5897 (*notrealinbox
) = 0;
5899 tc
= ps_global
->context_list
;
5902 strncpy(newfolder
, last_folder
, sizeof(newfolder
));
5903 newfolder
[sizeof(newfolder
)-1] = '\0';
5904 save_hist(history
, newfolder
, 0, tc
);
5908 /* fall thru like they cancelled */
5910 case 1 : /* o_e says user cancel */
5911 cmd_cancelled("Open folder");
5914 case 2 : /* o_e says user wants list */
5915 r
= display_folder_list(&tc
, newfolder
, 0, folders_for_goto
);
5921 case 3 : /* o_e says user wants help */
5922 help
= help
== NO_HELP
? h_oe_broach
: NO_HELP
;
5925 case 4 : /* redraw */
5928 case 10 : /* Previous collection */
5929 tc2
= ps_global
->context_list
;
5930 while(tc2
->next
&& tc2
->next
!= tc
)
5936 case 11 : /* Next collection */
5937 tc
= (tc
->next
) ? tc
->next
: ps_global
->context_list
;
5940 case 12 : /* file name completion */
5941 if(!folder_complete(tc
, newfolder
, sizeof(newfolder
), &n
)){
5942 if(n
&& last_rc
== 12 && !(flags
& OE_USER_MODIFIED
)){
5943 r
= display_folder_list(&tc
, newfolder
, 1,folders_for_goto
);
5945 done
++; /* bingo! */
5947 rc
= 0; /* burn last_rc */
5955 case 14 : /* file name completion */
5956 r
= display_folder_list(&tc
, newfolder
, 2, folders_for_goto
);
5958 done
++; /* bingo! */
5960 rc
= 0; /* burn last_rc */
5964 case 17 : /* GoTo INBOX */
5966 strncpy(newfolder
, ps_global
->inbox_name
, sizeof(newfolder
)-1);
5967 newfolder
[sizeof(newfolder
)-1] = '\0';
5969 (*notrealinbox
) = 0;
5971 tc
= ps_global
->context_list
;
5972 save_hist(history
, newfolder
, 0, tc
);
5977 if((p
= get_prev_hist(history
, newfolder
, 0, tc
)) != NULL
){
5978 strncpy(newfolder
, p
, sizeof(newfolder
));
5979 newfolder
[sizeof(newfolder
)-1] = '\0';
5980 if(history
->hist
[history
->curindex
])
5981 tc
= history
->hist
[history
->curindex
]->cntxt
;
5989 if((p
= get_next_hist(history
, newfolder
, 0, tc
)) != NULL
){
5990 strncpy(newfolder
, p
, sizeof(newfolder
));
5991 newfolder
[sizeof(newfolder
)-1] = '\0';
5992 if(history
->hist
[history
->curindex
])
5993 tc
= history
->hist
[history
->curindex
]->cntxt
;
6001 alpine_panic("Unhandled case");
6008 dprint((2, "broach folder, name entered \"%s\"\n",
6009 newfolder
? newfolder
: "?"));
6011 /*-- Just check that we can expand this. It gets done for real later --*/
6012 strncpy(expanded
, newfolder
, sizeof(expanded
));
6013 expanded
[sizeof(expanded
)-1] = '\0';
6015 if(!expand_foldername(expanded
, sizeof(expanded
))) {
6016 dprint((1, "Error: Failed on expansion of filename %s (do_broach)\n",
6017 expanded
? expanded
: "?"));
6026 /*----------------------------------------------------------------------
6027 Check to see if user wants to reopen dead stream.
6032 Result: 1 if the folder was successfully updatedn
6037 ask_mailbox_reopen(struct pine
*ps
, int *reopenp
)
6039 if(((ps
->mail_stream
->dtb
6040 && ((ps
->mail_stream
->dtb
->flags
& DR_NONEWMAIL
)
6041 || (ps
->mail_stream
->rdonly
6042 && ps
->mail_stream
->dtb
->flags
& DR_NONEWMAILRONLY
)))
6043 && (ps
->reopen_rule
== REOPEN_ASK_ASK_Y
6044 || ps
->reopen_rule
== REOPEN_ASK_ASK_N
6045 || ps
->reopen_rule
== REOPEN_ASK_NO_Y
6046 || ps
->reopen_rule
== REOPEN_ASK_NO_N
))
6047 || ((ps
->mail_stream
->dtb
6048 && ps
->mail_stream
->rdonly
6049 && !(ps
->mail_stream
->dtb
->flags
& DR_LOCAL
))
6050 && (ps
->reopen_rule
== REOPEN_YES_ASK_Y
6051 || ps
->reopen_rule
== REOPEN_YES_ASK_N
6052 || ps
->reopen_rule
== REOPEN_ASK_ASK_Y
6053 || ps
->reopen_rule
== REOPEN_ASK_ASK_N
))){
6056 switch(ps
->reopen_rule
){
6057 case REOPEN_YES_ASK_Y
:
6058 case REOPEN_ASK_ASK_Y
:
6059 case REOPEN_ASK_NO_Y
:
6068 switch(want_to("Re-open folder to check for new messages", deefault
,
6069 'x', h_reopen_folder
, WT_NORM
)){
6084 /*----------------------------------------------------------------------
6085 Check to see if user input is in form of old c-client mailbox speck
6090 Result: 1 if the folder was successfully updatedn
6095 update_folder_spec(char *new, size_t newlen
, char *old
)
6101 if(*(p
= old
) == '*') /* old form? */
6104 if(*old
== '{') /* copy host spec */
6106 switch(*new = *old
++){
6111 if(!struncmp(old
, "nntp", 4))
6119 while(*new++ != '}' && (new-orignew
) < newlen
-1);
6121 if((*p
== '*' && *old
) || ((*old
== '*') ? *++old
: 0)){
6123 * OK, some heuristics here. If it looks like a newsgroup
6124 * then we plunk it into the #news namespace else we
6125 * assume that they're trying to get at a #public folder...
6128 *p
&& (isalnum((unsigned char) *p
) || strindex(".-", *p
));
6132 sstrncpy(&new, (*p
&& !nntp
) ? "#public/" : "#news.", newlen
-(new-orignew
));
6133 strncpy(new, old
, newlen
-(new-orignew
));
6137 orignew
[newlen
-1] = '\0';
6143 /*----------------------------------------------------------------------
6144 Open the requested folder in the requested context
6146 Args: state -- usual pine state struct
6147 newfolder -- folder to open
6148 new_context -- folder context might live in
6149 stream -- candidate for recycling
6151 Result: New folder open or not (if error), and we're set to
6152 enter the index screen.
6155 visit_folder(struct pine
*state
, char *newfolder
, CONTEXT_S
*new_context
,
6156 MAILSTREAM
*stream
, long unsigned int flags
)
6158 dprint((9, "visit_folder(%s, %s)\n",
6159 newfolder
? newfolder
: "?",
6160 (new_context
&& new_context
->context
)
6161 ? new_context
->context
: "(NULL)"));
6163 if(ps_global
&& ps_global
->ttyo
){
6164 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
6165 ps_global
->mangled_footer
= 1;
6168 if(do_broach_folder(newfolder
, new_context
, stream
? &stream
: NULL
,
6170 || !sp_flagged(state
->mail_stream
, SP_LOCKED
))
6171 state
->next_screen
= mail_index_screen
;
6173 state
->next_screen
= folder_screen
;
6177 /*----------------------------------------------------------------------
6178 Move read messages from folder if listed in archive
6184 read_msg_prompt(long int n
, char *f
)
6186 char buf
[MAX_SCREEN_COLS
+1];
6188 snprintf(buf
, sizeof(buf
), "Save the %ld read message%s in \"%s\"", n
, plural(n
), f
);
6189 buf
[sizeof(buf
)-1] = '\0';
6190 return(want_to(buf
, 'y', 0, NO_HELP
, WT_NORM
) == 'y');
6194 /*----------------------------------------------------------------------
6195 Print current message[s] or folder index
6197 Args: state -- pointer to struct holding a bunch of pine state
6198 msgmap -- table mapping msg nums to c-client sequence nums
6199 aopt -- aggregate options
6200 in_index -- boolean indicating we're called from Index Screen
6202 Filters the original header and sends stuff to printer
6205 cmd_print(struct pine
*state
, MSGNO_S
*msgmap
, int aopt
, CmdWhere in_index
)
6208 long i
, msgs
, rawno
;
6209 int next
= 0, do_index
= 0, rv
= 0;
6214 if(MCMD_ISAGG(aopt
) && !pseudo_selected(state
->mail_stream
, msgmap
))
6217 msgs
= mn_total_cur(msgmap
);
6219 if((in_index
!= View
) && F_ON(F_PRINT_INDEX
, state
)){
6222 static ESCKEY_S prt_opts
[] = {
6223 {'i', 'i', "I", N_("Index")},
6224 {'m', 'm', "M", NULL
},
6225 {-1, 0, NULL
, NULL
}};
6227 if(in_index
== ThrdIndx
){
6228 /* TRANSLATORS: This is a question, Print Index ? */
6229 if(want_to(_("Print Index"), 'y', 'x', NO_HELP
, WT_NORM
) == 'y')
6235 snprintf(m
, sizeof(m
), "Message%s", (msgs
>1L) ? "s" : "");
6236 m
[sizeof(m
)-1] = '\0';
6237 prt_opts
[1].label
= m
;
6238 snprintf(prompt
, sizeof(prompt
), "Print %sFolder Index or %s %s? ",
6239 (aopt
& MCMD_AGG_2
) ? "thread " : MCMD_ISAGG(aopt
) ? "selected " : "",
6240 (aopt
& MCMD_AGG_2
) ? "thread" : MCMD_ISAGG(aopt
) ? "selected" : "current", m
);
6241 prompt
[sizeof(prompt
)-1] = '\0';
6243 ans
= radio_buttons(prompt
, -FOOTER_ROWS(state
), prt_opts
, 'm', 'x',
6244 NO_HELP
, RB_NORM
|RB_SEQ_SENSITIVE
);
6249 cmd_cancelled("Print");
6250 if(MCMD_ISAGG(aopt
))
6251 restore_selected(msgmap
);
6266 snprintf(prompt
, sizeof(prompt
), "%sFolder Index",
6267 (aopt
& MCMD_AGG_2
) ? "Thread " : MCMD_ISAGG(aopt
) ? "Selected " : "");
6269 snprintf(prompt
, sizeof(prompt
), "%s messages", long2string(msgs
));
6271 snprintf(prompt
, sizeof(prompt
), "Message %s", long2string(mn_get_cur(msgmap
)));
6273 prompt
[sizeof(prompt
)-1] = '\0';
6275 if(open_printer(prompt
) < 0){
6276 if(MCMD_ISAGG(aopt
))
6277 restore_selected(msgmap
);
6285 tc
= format_titlebar();
6287 /* Print titlebar... */
6288 print_text1("%s\n\n", tc
? tc
->titlebar_line
: "");
6289 /* then all the index members... */
6290 if(!print_index(state
, msgmap
, MCMD_ISAGG(aopt
)))
6291 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
6292 _("Error printing folder index"));
6298 for(i
= mn_first_cur(msgmap
); i
> 0L; i
= mn_next_cur(msgmap
), next
++){
6299 if(next
&& F_ON(F_AGG_PRINT_FF
, state
))
6300 if(!print_char(FORMFEED
)){
6305 if(!(state
->mail_stream
6306 && (rawno
= mn_m2raw(msgmap
, i
)) > 0L
6307 && rawno
<= state
->mail_stream
->nmsgs
6308 && (mc
= mail_elt(state
->mail_stream
, rawno
))
6312 if(!(e
=pine_mail_fetchstructure(state
->mail_stream
,
6315 || (F_ON(F_FROM_DELIM_IN_PRINT
, ps_global
)
6316 && !bezerk_delimiter(e
, mc
, print_char
, next
))
6317 || !format_message(mn_m2raw(msgmap
, mn_get_cur(msgmap
)),
6318 e
, b
, NULL
, FM_NEW_MESS
| FM_NOINDENT
,
6320 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
6321 _("Error printing message"));
6330 if(MCMD_ISAGG(aopt
))
6331 restore_selected(msgmap
);
6337 /*----------------------------------------------------------------------
6340 Args: state -- various pine state bits
6341 msgmap -- Message number mapping table
6342 aopt -- option flags
6344 Filters the original header and sends stuff to specified command
6347 cmd_pipe(struct pine
*state
, MSGNO_S
*msgmap
, int aopt
)
6353 char *resultfilename
= NULL
, prompt
[80], *p
;
6354 int done
= 0, rv
= 0;
6356 int fourlabel
= -1, j
= 0, next
= 0, ku
;
6357 int pipe_rv
; /* rv of proc to separate from close_system_pipe rv */
6359 unsigned flagsforhist
= 1; /* raw=8/delimit=4/newpipe=2/capture=1 */
6360 static HISTORY_S
*history
= NULL
;
6361 int capture
= 1, raw
= 0, delimit
= 0, newpipe
= 0;
6362 char pipe_command
[MAXPATH
];
6363 ESCKEY_S pipe_opt
[8];
6365 if(ps_global
->restricted
){
6366 q_status_message(SM_ORDER
| SM_DING
, 0, 4,
6367 "Alpine demo can't pipe messages");
6370 else if(!any_messages(msgmap
, NULL
, "to Pipe"))
6373 pipe_command
[0] = '\0';
6374 init_hist(&history
, HISTSIZE
);
6375 flagsforhist
= (raw
? 0x8 : 0) +
6376 (delimit
? 0x4 : 0) +
6377 (newpipe
? 0x2 : 0) +
6378 (capture
? 0x1 : 0);
6379 if((p
= get_prev_hist(history
, "", flagsforhist
, NULL
)) != NULL
){
6380 strncpy(pipe_command
, p
, sizeof(pipe_command
));
6381 pipe_command
[sizeof(pipe_command
)-1] = '\0';
6382 if(history
->hist
[history
->curindex
]){
6383 flagsforhist
= history
->hist
[history
->curindex
]->flags
;
6384 raw
= (flagsforhist
& 0x8) ? 1 : 0;
6385 delimit
= (flagsforhist
& 0x4) ? 1 : 0;
6386 newpipe
= (flagsforhist
& 0x2) ? 1 : 0;
6387 capture
= (flagsforhist
& 0x1) ? 1 : 0;
6392 pipe_opt
[j
].rval
= 0;
6393 pipe_opt
[j
].name
= "";
6394 pipe_opt
[j
++].label
= "";
6396 pipe_opt
[j
].ch
= ctrl('W');
6397 pipe_opt
[j
].rval
= 10;
6398 pipe_opt
[j
].name
= "^W";
6399 pipe_opt
[j
++].label
= NULL
;
6401 pipe_opt
[j
].ch
= ctrl('Y');
6402 pipe_opt
[j
].rval
= 11;
6403 pipe_opt
[j
].name
= "^Y";
6404 pipe_opt
[j
++].label
= NULL
;
6406 pipe_opt
[j
].ch
= ctrl('R');
6407 pipe_opt
[j
].rval
= 12;
6408 pipe_opt
[j
].name
= "^R";
6409 pipe_opt
[j
++].label
= NULL
;
6411 if(MCMD_ISAGG(aopt
)){
6412 if(!pseudo_selected(state
->mail_stream
, msgmap
))
6416 pipe_opt
[j
].ch
= ctrl('T');
6417 pipe_opt
[j
].rval
= 13;
6418 pipe_opt
[j
].name
= "^T";
6419 pipe_opt
[j
++].label
= NULL
;
6423 pipe_opt
[j
].ch
= KEY_UP
;
6424 pipe_opt
[j
].rval
= 30;
6425 pipe_opt
[j
].name
= "";
6427 pipe_opt
[j
++].label
= "";
6429 pipe_opt
[j
].ch
= KEY_DOWN
;
6430 pipe_opt
[j
].rval
= 31;
6431 pipe_opt
[j
].name
= "";
6432 pipe_opt
[j
++].label
= "";
6434 pipe_opt
[j
].ch
= -1;
6439 snprintf(prompt
, sizeof(prompt
), "Pipe %smessage%s%s to %s%s%s%s%s%s%s: ",
6441 MCMD_ISAGG(aopt
) ? "s" : " ",
6442 MCMD_ISAGG(aopt
) ? "" : comatose(mn_get_cur(msgmap
)),
6443 (!capture
|| delimit
|| (newpipe
&& MCMD_ISAGG(aopt
))) ? "(" : "",
6444 capture
? "" : "uncaptured",
6445 (!capture
&& delimit
) ? "," : "",
6446 delimit
? "delimited" : "",
6447 ((!capture
|| delimit
) && newpipe
&& MCMD_ISAGG(aopt
)) ? "," : "",
6448 (newpipe
&& MCMD_ISAGG(aopt
)) ? "new pipe" : "",
6449 (!capture
|| delimit
|| (newpipe
&& MCMD_ISAGG(aopt
))) ? ") " : "");
6450 prompt
[sizeof(prompt
)-1] = '\0';
6451 pipe_opt
[1].label
= raw
? N_("Shown Text") : N_("Raw Text");
6452 pipe_opt
[2].label
= capture
? N_("Free Output") : N_("Capture Output");
6453 pipe_opt
[3].label
= delimit
? N_("No Delimiter") : N_("With Delimiter");
6455 pipe_opt
[fourlabel
].label
= newpipe
? N_("To Same Pipe") : N_("To Individual Pipes");
6459 * 2 is really 1 because there will be one real entry and
6460 * one entry of "" because of the get_prev_hist above.
6462 if(items_in_hist(history
) > 2){
6463 pipe_opt
[ku
].name
= HISTORY_UP_KEYNAME
;
6464 pipe_opt
[ku
].label
= HISTORY_KEYLABEL
;
6465 pipe_opt
[ku
+1].name
= HISTORY_DOWN_KEYNAME
;
6466 pipe_opt
[ku
+1].label
= HISTORY_KEYLABEL
;
6469 pipe_opt
[ku
].name
= "";
6470 pipe_opt
[ku
].label
= "";
6471 pipe_opt
[ku
+1].name
= "";
6472 pipe_opt
[ku
+1].label
= "";
6475 flags
= OE_APPEND_CURRENT
| OE_SEQ_SENSITIVE
;
6476 switch(optionally_enter(pipe_command
, -FOOTER_ROWS(state
), 0,
6477 sizeof(pipe_command
), prompt
,
6478 pipe_opt
, NO_HELP
, &flags
)){
6480 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
6481 _("Internal problem encountered"));
6485 case 10 : /* flip raw bit */
6489 case 11 : /* flip capture bit */
6493 case 12 : /* flip delimit bit */
6497 case 13 : /* flip newpipe bit */
6502 flagsforhist
= (raw
? 0x8 : 0) +
6503 (delimit
? 0x4 : 0) +
6504 (newpipe
? 0x2 : 0) +
6505 (capture
? 0x1 : 0);
6506 if((p
= get_prev_hist(history
, pipe_command
, flagsforhist
, NULL
)) != NULL
){
6507 strncpy(pipe_command
, p
, sizeof(pipe_command
));
6508 pipe_command
[sizeof(pipe_command
)-1] = '\0';
6509 if(history
->hist
[history
->curindex
]){
6510 flagsforhist
= history
->hist
[history
->curindex
]->flags
;
6511 raw
= (flagsforhist
& 0x8) ? 1 : 0;
6512 delimit
= (flagsforhist
& 0x4) ? 1 : 0;
6513 newpipe
= (flagsforhist
& 0x2) ? 1 : 0;
6514 capture
= (flagsforhist
& 0x1) ? 1 : 0;
6523 flagsforhist
= (raw
? 0x8 : 0) +
6524 (delimit
? 0x4 : 0) +
6525 (newpipe
? 0x2 : 0) +
6526 (capture
? 0x1 : 0);
6527 if((p
= get_next_hist(history
, pipe_command
, flagsforhist
, NULL
)) != NULL
){
6528 strncpy(pipe_command
, p
, sizeof(pipe_command
));
6529 pipe_command
[sizeof(pipe_command
)-1] = '\0';
6530 if(history
->hist
[history
->curindex
]){
6531 flagsforhist
= history
->hist
[history
->curindex
]->flags
;
6532 raw
= (flagsforhist
& 0x8) ? 1 : 0;
6533 delimit
= (flagsforhist
& 0x4) ? 1 : 0;
6534 newpipe
= (flagsforhist
& 0x2) ? 1 : 0;
6535 capture
= (flagsforhist
& 0x1) ? 1 : 0;
6544 if(pipe_command
[0]){
6546 flagsforhist
= (raw
? 0x8 : 0) +
6547 (delimit
? 0x4 : 0) +
6548 (newpipe
? 0x2 : 0) +
6549 (capture
? 0x1 : 0);
6550 save_hist(history
, pipe_command
, flagsforhist
, NULL
);
6552 flags
= PIPE_USER
| PIPE_WRITE
| PIPE_STDERR
;
6553 flags
|= (raw
? PIPE_RAW
: 0);
6559 ps_global
->mangled_screen
= 1;
6560 ps_global
->in_init_seq
= 1;
6562 flags
|= PIPE_RESET
;
6565 if(!newpipe
&& !(syspipe
= cmd_pipe_open(pipe_command
,
6566 (flags
& PIPE_RESET
)
6572 for(i
= mn_first_cur(msgmap
);
6574 i
= mn_next_cur(msgmap
)){
6575 e
= pine_mail_fetchstructure(ps_global
->mail_stream
,
6576 mn_m2raw(msgmap
, i
), &b
);
6577 if(!(state
->mail_stream
6578 && (rawno
= mn_m2raw(msgmap
, i
)) > 0L
6579 && rawno
<= state
->mail_stream
->nmsgs
6580 && (mc
= mail_elt(state
->mail_stream
, rawno
))
6585 && !(syspipe
= cmd_pipe_open(pipe_command
,
6586 (flags
& PIPE_RESET
)
6590 || (delimit
&& !bezerk_delimiter(e
, mc
, pc
, next
++)))
6597 prime_raw_pipe_getc(ps_global
->mail_stream
,
6598 mn_m2raw(msgmap
, i
), -1L, 0L);
6600 gf_link_filter(gf_nvtnl_local
, NULL
);
6601 if((pipe_err
= gf_pipe(raw_pipe_getc
, pc
)) != NULL
){
6602 q_status_message1(SM_ORDER
|SM_DING
,
6604 _("Internal Error: %s"),
6609 else if(!format_message(mn_m2raw(msgmap
, i
), e
, b
,
6610 NULL
, FM_NEW_MESS
| FM_NOWRAP
, pc
))
6615 if(close_system_pipe(&syspipe
, &pipe_rv
, pipe_callback
) == -1)
6620 ps_global
->in_init_seq
= 0;
6623 if(close_system_pipe(&syspipe
, &pipe_rv
, pipe_callback
) == -1)
6625 if(done
) /* say we had a problem */
6626 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
6627 _("Error piping message"));
6628 else if(resultfilename
){
6630 /* only display if no error */
6631 display_output_file(resultfilename
, "PIPE MESSAGE",
6633 fs_give((void **)&resultfilename
);
6637 q_status_message(SM_ORDER
, 0, 2, _("Pipe command completed"));
6643 /* else fall thru as if cancelled */
6646 cmd_cancelled("Pipe command");
6651 helper(h_common_pipe
, _("HELP FOR PIPE COMMAND"), HLPD_SIMPLE
);
6652 ps_global
->mangled_screen
= 1;
6655 case 2 : /* no place to escape to */
6656 case 4 : /* can't suspend */
6662 ps_global
->mangled_footer
= 1;
6663 if(MCMD_ISAGG(aopt
))
6664 restore_selected(msgmap
);
6670 /*----------------------------------------------------------------------
6671 Screen to offer list management commands contained in message
6673 Args: state -- pointer to struct holding a bunch of pine state
6674 msgmap -- table mapping msg nums to c-client sequence nums
6675 aopt -- aggregate options
6679 NOTE: Inspired by contrib from Jeremy Blackman <loki@maison-otaku.net>
6682 rfc2369_display(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, long int msgno
)
6685 char *h
, *hdrs
[MLCMD_COUNT
+ 1];
6686 long index_no
= mn_raw2m(msgmap
, msgno
);
6687 RFC2369_S data
[MLCMD_COUNT
];
6689 /* for each header field */
6690 if((h
= pine_fetchheader_lines(stream
, msgno
, NULL
, rfc2369_hdrs(hdrs
))) != NULL
){
6691 memset(&data
[0], 0, sizeof(RFC2369_S
) * MLCMD_COUNT
);
6692 if(rfc2369_parse_fields(h
, &data
[0])){
6695 if((explain
= list_mgmt_text(data
, index_no
)) != NULL
){
6696 list_mgmt_screen(explain
);
6697 ps_global
->mangled_screen
= 1;
6703 fs_give((void **) &h
);
6707 q_status_message1(SM_ORDER
, 0, 3,
6708 "Message %s contains no list management information",
6709 comatose(index_no
));
6714 list_mgmt_text(RFC2369_S
*data
, long int msgno
)
6717 int i
, j
, n
, fields
= 0;
6718 static char *rfc2369_intro1
=
6719 "<HTML><HEAD></HEAD><BODY><H1>Mail List Commands</H1>Message ";
6720 static char *rfc2369_intro2
[] = {
6721 N_(" has information associated with it "),
6722 N_("that explains how to participate in an email list. An "),
6723 N_("email list is represented by a single email address that "),
6724 N_("users sharing a common interest can send messages to (known "),
6725 N_("as posting) which are then redistributed to all members "),
6726 N_("of the list (sometimes after review by a moderator)."),
6727 N_("<P>List participation commands in this message include:"),
6731 if((store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
){
6733 /* Insert introductory text */
6734 so_puts(store
, rfc2369_intro1
);
6736 so_puts(store
, comatose(msgno
));
6738 for(i
= 0; rfc2369_intro2
[i
]; i
++)
6739 so_puts(store
, _(rfc2369_intro2
[i
]));
6741 so_puts(store
, "<P>");
6742 for(i
= 0; i
< MLCMD_COUNT
; i
++)
6743 if(data
[i
].data
[0].value
6744 || data
[i
].data
[0].comment
6745 || data
[i
].data
[0].error
){
6747 so_puts(store
, "<UL>");
6749 so_puts(store
, "<LI>");
6751 (n
= (data
[i
].data
[1].value
|| data
[i
].data
[1].comment
))
6755 so_puts(store
, data
[i
].field
.description
);
6756 so_puts(store
, ". ");
6759 so_puts(store
, "<OL>");
6763 && (data
[i
].data
[j
].comment
6764 || data
[i
].data
[j
].value
6765 || data
[i
].data
[j
].error
);
6768 so_puts(store
, n
? "<P><LI>" : "<P>");
6770 if(data
[i
].data
[j
].comment
){
6772 _("With the provided comment:<P><BLOCKQUOTE>"));
6773 so_puts(store
, data
[i
].data
[j
].comment
);
6774 so_puts(store
, "</BLOCKQUOTE><P>");
6777 if(data
[i
].data
[j
].value
){
6779 && !strucmp(data
[i
].data
[j
].value
, "NO")){
6781 _("Posting is <EM>not</EM> allowed on this list"));
6784 so_puts(store
, "Select <A HREF=\"");
6785 so_puts(store
, data
[i
].data
[j
].value
);
6786 so_puts(store
, "\">HERE</A> to ");
6787 so_puts(store
, (data
[i
].field
.action
)
6788 ? data
[i
].field
.action
6792 so_puts(store
, ".");
6795 if(data
[i
].data
[j
].error
){
6796 so_puts(store
, "<P>Unfortunately, Alpine can not offer");
6797 so_puts(store
, " to take direct action based upon it");
6798 so_puts(store
, " because it was improperly formatted.");
6799 so_puts(store
, " The unrecognized data associated with");
6800 so_puts(store
, " the \"");
6801 so_puts(store
, data
[i
].field
.name
);
6802 so_puts(store
, "\" header field was:<P><BLOCKQUOTE>");
6803 so_puts(store
, data
[i
].data
[j
].error
);
6804 so_puts(store
, "</BLOCKQUOTE>");
6807 so_puts(store
, "<P>");
6811 so_puts(store
, "</OL>");
6815 so_puts(store
, "</UL>");
6817 so_puts(store
, "</BODY></HTML>");
6825 list_mgmt_screen(STORE_S
*html
)
6831 HANDLE_S
*handles
= NULL
;
6835 so_seek(html
, 0L, 0);
6836 gf_set_so_readc(&gc
, html
);
6838 init_handles(&handles
);
6840 if((store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
){
6841 gf_set_so_writec(&pc
, store
);
6844 gf_link_filter(gf_html2plain
,
6845 gf_html2plain_opt(NULL
, ps_global
->ttyo
->screen_cols
,
6846 non_messageview_margin(), &handles
, NULL
, 0));
6848 error
= gf_pipe(gc
, pc
);
6850 gf_clear_so_writec(store
);
6855 memset(&sargs
, 0, sizeof(SCROLL_S
));
6856 sargs
.text
.text
= so_text(store
);
6857 sargs
.text
.src
= CharStar
;
6858 sargs
.text
.desc
= "list commands";
6859 sargs
.text
.handles
= handles
;
6861 sargs
.start
.on
= Offset
;
6862 sargs
.start
.loc
.offset
= offset
;
6865 sargs
.bar
.title
= _("MAIL LIST COMMANDS");
6866 sargs
.bar
.style
= MessageNumber
;
6867 sargs
.resize_exit
= 1;
6868 sargs
.help
.text
= h_special_list_commands
;
6869 sargs
.help
.title
= _("HELP FOR LIST COMMANDS");
6870 sargs
.keys
.menu
= &listmgr_keymenu
;
6871 setbitmap(sargs
.keys
.bitmap
);
6873 clrbitn(LM_TRY_KEY
, sargs
.keys
.bitmap
);
6874 clrbitn(LM_PREV_KEY
, sargs
.keys
.bitmap
);
6875 clrbitn(LM_NEXT_KEY
, sargs
.keys
.bitmap
);
6878 cmd
= scrolltool(&sargs
);
6879 offset
= sargs
.start
.loc
.offset
;
6885 free_handles(&handles
);
6886 gf_clear_so_readc(html
);
6888 while(cmd
== MC_RESIZE
);
6892 /*----------------------------------------------------------------------
6893 Prompt the user for the type of select desired
6895 NOTE: any and all functions that successfully exit the second
6896 switch() statement below (currently "select_*() functions"),
6897 *MUST* update the folder's MESSAGECACHE element's "searched"
6898 bits to reflect the search result. Functions using
6899 mail_search() get this for free, the others must update 'em
6902 Returns -1 if canceled without changing selection
6903 0 if selection may have changed
6906 aggregate_select(struct pine
*state
, MSGNO_S
*msgmap
, int q_line
, CmdWhere in_index
)
6908 long i
, diff
, old_tot
, msgno
, raw
;
6909 int q
= 0, rv
= 0, narrow
= 0, hidden
, ret
= -1;
6912 SEARCHSET
*limitsrch
= NULL
;
6914 extern MAILSTREAM
*mm_search_stream
;
6915 extern long mm_search_count
;
6917 hidden
= any_lflagged(msgmap
, MN_HIDE
) > 0L;
6918 mm_search_stream
= state
->mail_stream
;
6919 mm_search_count
= 0L;
6921 sel_opts
= THRD_INDX() ? sel_opts4
: sel_opts2
;
6923 sel_opts
[SEL_OPTS_THREAD
].ch
= SEL_OPTS_THREAD_CH
;
6926 sel_opts
[SEL_OPTS_THREAD
].ch
= -1;
6929 if((old_tot
= any_lflagged(msgmap
, MN_SLCT
)) != 0){
6932 thrd
= fetch_thread(state
->mail_stream
,
6933 mn_m2raw(msgmap
, mn_get_cur(msgmap
)));
6934 /* check if whole thread is selected or not */
6936 count_lflags_in_thread(state
->mail_stream
,thrd
,msgmap
,MN_SLCT
)
6938 count_lflags_in_thread(state
->mail_stream
,thrd
,msgmap
,MN_NONE
))
6941 sel_opts1
[1].label
= i
? N_("unselect Curthrd") : N_("select Curthrd");
6944 i
= get_lflag(state
->mail_stream
, msgmap
, mn_get_cur(msgmap
),
6946 sel_opts1
[1].label
= i
? N_("unselect Cur") : N_("select Cur");
6949 sel_opts
+= 2; /* disable extra options */
6950 switch(q
= radio_buttons(_(sel_pmt1
), q_line
, sel_opts1
, 'c', 'x', NO_HELP
,
6952 case 'f' : /* flip selection */
6954 for(i
= 1L; i
<= mn_get_total(msgmap
); i
++){
6956 q
= !get_lflag(state
->mail_stream
, msgmap
, i
, MN_SLCT
);
6957 set_lflag(state
->mail_stream
, msgmap
, i
, MN_SLCT
, q
);
6959 set_lflag(state
->mail_stream
, msgmap
, i
, MN_HIDE
, !q
);
6961 mn_reset_cur(msgmap
, msgno
= i
);
6967 case 'n' : /* narrow selection */
6969 case 'b' : /* broaden selection */
6970 q
= 0; /* offer criteria prompt */
6973 case 'c' : /* Un/Select Current */
6974 case 'a' : /* Unselect All */
6975 case 'x' : /* cancel */
6979 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
6980 "Unsupported Select option");
6987 q
= radio_buttons(sel_pmt2
, q_line
, sel_opts
, 'c', 'x',
6988 NO_HELP
, RB_NORM
|RB_RET_HELP
);
6991 helper(h_index_cmd_select
, _("HELP FOR SELECT"), HLPD_SIMPLE
);
6992 ps_global
->mangled_screen
= 1;
7000 * The purpose of this is to add the appropriate searchset to the
7001 * search so that the search can be limited to only looking at what
7002 * it needs to look at. That is, if we are narrowing then we only need
7003 * to look at messages which are already selected, and if we are
7004 * broadening, then we only need to look at messages which are not
7005 * yet selected. This routine will work whether or not
7006 * limiting_searchset properly limits the search set. In particular,
7007 * the searchset returned by limiting_searchset may include messages
7008 * which really shouldn't be included. We do that because a too-large
7009 * searchset will break some IMAP servers. It is even possible that it
7010 * becomes inefficient to send the whole set. If the select function
7011 * frees limitsrch, it should be sure to set it to NULL so we won't
7012 * try freeing it again here.
7014 limitsrch
= limiting_searchset(state
->mail_stream
, narrow
);
7017 * NOTE: See note about MESSAGECACHE "searched" bits above!
7020 case 'x': /* cancel */
7021 cmd_cancelled("Select command");
7024 case 'c' : /* select/unselect current */
7025 (void) select_by_current(state
, msgmap
, in_index
);
7029 case 'a' : /* select/unselect all */
7030 msgno
= any_lflagged(msgmap
, MN_SLCT
);
7031 diff
= (!msgno
) ? mn_get_total(msgmap
) : 0L;
7033 agg_select_all(state
->mail_stream
, msgmap
, &diff
,
7034 any_lflagged(msgmap
, MN_SLCT
) <= 0L);
7035 q_status_message4(SM_ORDER
,0,2,
7036 "%s%s message%s %sselected",
7037 msgno
? "" : "All ", comatose(diff
),
7038 plural(diff
), msgno
? "UN" : "");
7041 case 'n' : /* Select by Number */
7044 rv
= select_by_thrd_number(state
->mail_stream
, msgmap
, &limitsrch
);
7046 rv
= select_by_number(state
->mail_stream
, msgmap
, &limitsrch
);
7050 case 'd' : /* Select by Date */
7052 rv
= select_by_date(state
->mail_stream
, msgmap
, mn_get_cur(msgmap
),
7056 case 't' : /* Text */
7058 rv
= select_by_text(state
->mail_stream
, msgmap
, mn_get_cur(msgmap
),
7062 case 'z' : /* Size */
7064 rv
= select_by_size(state
->mail_stream
, &limitsrch
);
7067 case 's' : /* Status */
7069 rv
= select_by_status(state
->mail_stream
, &limitsrch
);
7072 case 'k' : /* Keyword */
7074 rv
= select_by_keyword(state
->mail_stream
, &limitsrch
);
7077 case 'r' : /* Rule */
7079 rv
= select_by_rule(state
->mail_stream
, &limitsrch
);
7082 case 'h' : /* Thread */
7084 rv
= select_by_thread(state
->mail_stream
, msgmap
, &limitsrch
);
7088 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
7089 "Unsupported Select option");
7094 mail_free_searchset(&limitsrch
);
7096 if(rv
) /* bad return value.. */
7097 return(ret
); /* error already displayed */
7099 if(narrow
) /* make sure something was selected */
7100 for(i
= 1L; i
<= mn_get_total(msgmap
); i
++)
7101 if((raw
= mn_m2raw(msgmap
, i
)) > 0L && state
->mail_stream
7102 && raw
<= state
->mail_stream
->nmsgs
7103 && (mc
= mail_elt(state
->mail_stream
, raw
)) && mc
->searched
){
7104 if(get_lflag(state
->mail_stream
, msgmap
, i
, MN_SLCT
))
7111 if(mm_search_count
){
7113 * loop thru all the messages, adjusting local flag bits
7114 * based on their "searched" bit...
7116 for(i
= 1L, msgno
= 0L; i
<= mn_get_total(msgmap
); i
++)
7118 /* turning OFF selectedness if the "searched" bit isn't lit. */
7119 if(get_lflag(state
->mail_stream
, msgmap
, i
, MN_SLCT
)){
7120 if((raw
= mn_m2raw(msgmap
, i
)) > 0L && state
->mail_stream
7121 && raw
<= state
->mail_stream
->nmsgs
7122 && (mc
= mail_elt(state
->mail_stream
, raw
))
7125 set_lflag(state
->mail_stream
, msgmap
, i
, MN_SLCT
, 0);
7127 set_lflag(state
->mail_stream
, msgmap
, i
, MN_HIDE
, 1);
7129 /* adjust current message in case we unselect and hide it */
7130 else if(msgno
< mn_get_cur(msgmap
)
7132 || !get_lflag(state
->mail_stream
, msgmap
,
7137 else if((raw
= mn_m2raw(msgmap
, i
)) > 0L && state
->mail_stream
7138 && raw
<= state
->mail_stream
->nmsgs
7139 && (mc
= mail_elt(state
->mail_stream
, raw
)) && mc
->searched
){
7140 /* turn ON selectedness if "searched" bit is lit. */
7141 if(!get_lflag(state
->mail_stream
, msgmap
, i
, MN_SLCT
)){
7143 set_lflag(state
->mail_stream
, msgmap
, i
, MN_SLCT
, 1);
7145 set_lflag(state
->mail_stream
, msgmap
, i
, MN_HIDE
, 0);
7149 /* if we're zoomed and the current message was unselected */
7151 && get_lflag(state
->mail_stream
,msgmap
,mn_get_cur(msgmap
),MN_HIDE
))
7152 mn_reset_cur(msgmap
, msgno
);
7157 q_status_message4(SM_ORDER
, 3, 3,
7158 "%s. %s message%s remain%s selected.",
7160 ? "No change resulted"
7161 : "No messages in intersection",
7162 comatose(old_tot
), plural(old_tot
),
7163 (old_tot
== 1L) ? "s" : "");
7165 q_status_message(SM_ORDER
, 3, 3,
7166 _("No change resulted. Matching messages already selected."));
7168 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
7169 _("Select failed. No %smessages selected."),
7170 old_tot
? _("additional ") : "");
7173 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,
7174 "Select matched %ld message%s. %s %smessage%s %sselected.",
7175 (diff
> 0) ? diff
: old_tot
+ diff
,
7176 plural((diff
> 0) ? diff
: old_tot
+ diff
),
7177 comatose((diff
> 0) ? any_lflagged(msgmap
, MN_SLCT
) : -diff
),
7178 (diff
> 0) ? "total " : "",
7179 plural((diff
> 0) ? any_lflagged(msgmap
, MN_SLCT
) : -diff
),
7180 (diff
> 0) ? "" : "UN");
7181 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
7182 q_status_message(SM_ORDER
, 3, 3, tmp_20k_buf
);
7185 q_status_message2(SM_ORDER
, 3, 3, _("Select matched %s message%s!"),
7186 comatose(diff
), plural(diff
));
7192 /*----------------------------------------------------------------------
7193 Toggle the state of the current message
7195 Args: state -- pointer pine's state variables
7196 msgmap -- message collection to operate on
7197 in_index -- in the message index view
7198 Returns: TRUE if current marked selected, FALSE otw
7201 select_by_current(struct pine
*state
, MSGNO_S
*msgmap
, CmdWhere in_index
)
7204 int all_selected
= 0;
7205 unsigned long was
, tot
, rawno
;
7208 cur
= mn_get_cur(msgmap
);
7211 thrd
= fetch_thread(state
->mail_stream
, mn_m2raw(msgmap
, cur
));
7215 was
= count_lflags_in_thread(state
->mail_stream
, thrd
, msgmap
, MN_SLCT
);
7216 tot
= count_lflags_in_thread(state
->mail_stream
, thrd
, msgmap
, MN_NONE
);
7221 set_thread_lflags(state
->mail_stream
, thrd
, msgmap
, MN_SLCT
, 0);
7222 if(any_lflagged(msgmap
, MN_HIDE
) > 0L){
7223 set_thread_lflags(state
->mail_stream
, thrd
, msgmap
, MN_HIDE
, 1);
7225 * See if there's anything left to zoom on. If so,
7226 * pick an adjacent one for highlighting, else make
7227 * sure nothing is left hidden...
7229 if(any_lflagged(msgmap
, MN_SLCT
)){
7230 mn_inc_cur(state
->mail_stream
, msgmap
,
7231 (in_index
== View
&& THREADING()
7232 && sp_viewing_a_thread(state
->mail_stream
))
7234 : (in_index
== View
)
7235 ? MH_ANYTHD
: MH_NONE
);
7236 if(mn_get_cur(msgmap
) == cur
)
7237 mn_dec_cur(state
->mail_stream
, msgmap
,
7238 (in_index
== View
&& THREADING()
7239 && sp_viewing_a_thread(state
->mail_stream
))
7241 : (in_index
== View
)
7242 ? MH_ANYTHD
: MH_NONE
);
7244 else /* clear all hidden flags */
7245 (void) unzoom_index(state
, state
->mail_stream
, msgmap
);
7249 set_thread_lflags(state
->mail_stream
, thrd
, msgmap
, MN_SLCT
, 1);
7251 q_status_message3(SM_ORDER
, 0, 2, "%s message%s %sselected",
7252 comatose(all_selected
? was
: tot
-was
),
7253 plural(all_selected
? was
: tot
-was
),
7254 all_selected
? "UN" : "");
7256 /* collapsed thread */
7258 && ((rawno
= mn_m2raw(msgmap
, cur
)) != 0L)
7259 && ((thrd
= fetch_thread(state
->mail_stream
, rawno
)) != NULL
)
7260 && (thrd
&& thrd
->next
&& get_lflag(state
->mail_stream
, NULL
, rawno
, MN_COLL
))){
7262 * This doesn't work quite the same as the colon command works, but
7263 * it is arguably doing the correct thing. The difference is
7264 * that aggregate_select will zoom after selecting back where it
7265 * was called from, but selecting a thread with colon won't zoom.
7266 * Maybe it makes sense to zoom after a select but not after a colon
7267 * command even though they are very similar.
7269 thread_command(state
, state
->mail_stream
, msgmap
, ':', -FOOTER_ROWS(state
));
7273 get_lflag(state
->mail_stream
, msgmap
, cur
, MN_SLCT
)) != 0){ /* set? */
7274 set_lflag(state
->mail_stream
, msgmap
, cur
, MN_SLCT
, 0);
7275 if(any_lflagged(msgmap
, MN_HIDE
) > 0L){
7276 set_lflag(state
->mail_stream
, msgmap
, cur
, MN_HIDE
, 1);
7278 * See if there's anything left to zoom on. If so,
7279 * pick an adjacent one for highlighting, else make
7280 * sure nothing is left hidden...
7282 if(any_lflagged(msgmap
, MN_SLCT
)){
7283 mn_inc_cur(state
->mail_stream
, msgmap
,
7284 (in_index
== View
&& THREADING()
7285 && sp_viewing_a_thread(state
->mail_stream
))
7287 : (in_index
== View
)
7288 ? MH_ANYTHD
: MH_NONE
);
7289 if(mn_get_cur(msgmap
) == cur
)
7290 mn_dec_cur(state
->mail_stream
, msgmap
,
7291 (in_index
== View
&& THREADING()
7292 && sp_viewing_a_thread(state
->mail_stream
))
7294 : (in_index
== View
)
7295 ? MH_ANYTHD
: MH_NONE
);
7297 else /* clear all hidden flags */
7298 (void) unzoom_index(state
, state
->mail_stream
, msgmap
);
7302 set_lflag(state
->mail_stream
, msgmap
, cur
, MN_SLCT
, 1);
7304 q_status_message2(SM_ORDER
, 0, 2, "Message %s %sselected",
7305 long2string(cur
), all_selected
? "UN" : "");
7309 return(!all_selected
);
7313 /*----------------------------------------------------------------------
7314 Prompt the user for the command to perform on selected messages
7316 Args: state -- pointer pine's state variables
7317 msgmap -- message collection to operate on
7318 q_line -- line on display to write prompts
7319 Returns: 1 if the selected messages are suitably commanded,
7320 0 if the choice to pick the command was declined
7324 apply_command(struct pine
*state
, MAILSTREAM
*stream
, MSGNO_S
*msgmap
,
7325 UCS preloadkeystroke
, int flags
, int q_line
)
7327 int i
= 8, /* number of static entries in sel_opts3 */
7331 agg
= (flags
& AC_FROM_THREAD
) ? MCMD_AGG_2
: MCMD_AGG
;
7335 * To do this "right", we really ought to have access to the keymenu
7336 * here and change the typed command into a real command by running
7337 * it through menu_command. Then the switch below would be against
7338 * results from menu_command. If we did that we'd also pass the
7339 * results of menu_command in as preloadkeystroke instead of passing
7340 * the keystroke itself. But we don't have the keymenu handy,
7341 * so we have to fake it. The only complication that we run into
7342 * is that KEY_DEL is an escape sequence so we change a typed
7343 * KEY_DEL esc seq into the letter D.
7346 if(!preloadkeystroke
){
7347 if(F_ON(F_ENABLE_FLAG
,state
)){ /* flag? */
7348 sel_opts3
[i
].ch
= '*';
7349 sel_opts3
[i
].rval
= '*';
7350 sel_opts3
[i
].name
= "*";
7351 sel_opts3
[i
++].label
= N_("Flag");
7354 if(F_ON(F_ENABLE_PIPE
,state
)){ /* pipe? */
7355 sel_opts3
[i
].ch
= '|';
7356 sel_opts3
[i
].rval
= '|';
7357 sel_opts3
[i
].name
= "|";
7358 sel_opts3
[i
++].label
= N_("Pipe");
7361 if(F_ON(F_ENABLE_BOUNCE
,state
)){ /* bounce? */
7362 sel_opts3
[i
].ch
= 'b';
7363 sel_opts3
[i
].rval
= 'b';
7364 sel_opts3
[i
].name
= "B";
7365 sel_opts3
[i
++].label
= N_("Bounce");
7368 if(flags
& AC_FROM_THREAD
){
7369 if(flags
& (AC_COLL
| AC_EXPN
)){
7370 sel_opts3
[i
].ch
= '/';
7371 sel_opts3
[i
].rval
= '/';
7372 sel_opts3
[i
].name
= "/";
7373 sel_opts3
[i
++].label
= (flags
& AC_COLL
) ? N_("Collapse")
7377 sel_opts3
[i
].ch
= ';';
7378 sel_opts3
[i
].rval
= ';';
7379 sel_opts3
[i
].name
= ";";
7380 if(flags
& AC_UNSEL
)
7381 sel_opts3
[i
++].label
= N_("UnSelect");
7383 sel_opts3
[i
++].label
= N_("Select");
7386 if(F_ON(F_ENABLE_PRYNT
, state
)){ /* this one is invisible */
7387 sel_opts3
[i
].ch
= 'y';
7388 sel_opts3
[i
].rval
= '%';
7389 sel_opts3
[i
].name
= "";
7390 sel_opts3
[i
++].label
= "";
7393 if(!is_imap_stream(stream
) || LEVELUIDPLUS(stream
)){ /* expunge selected messages */
7394 sel_opts3
[i
].ch
= 'x';
7395 sel_opts3
[i
].rval
= 'x';
7396 sel_opts3
[i
].name
= "X";
7397 sel_opts3
[i
++].label
= N_("Expunge");
7400 sel_opts3
[i
].ch
= KEY_DEL
; /* also invisible */
7401 sel_opts3
[i
].rval
= 'd';
7402 sel_opts3
[i
].name
= "";
7403 sel_opts3
[i
++].label
= "";
7405 sel_opts3
[i
].ch
= -1;
7407 snprintf(prompt
, sizeof(prompt
), "%s command : ",
7408 (flags
& AC_FROM_THREAD
) ? "THREAD" : "APPLY");
7409 prompt
[sizeof(prompt
)-1] = '\0';
7410 cmd
= double_radio_buttons(prompt
, q_line
, sel_opts3
, 'z', 'c', NO_HELP
,
7416 if(preloadkeystroke
== KEY_DEL
)
7419 if(preloadkeystroke
< 0x80 && isupper((int) preloadkeystroke
))
7420 cmd
= tolower((int) preloadkeystroke
);
7422 cmd
= (int) preloadkeystroke
; /* shouldn't happen */
7427 case 'd' : /* delete */
7428 we_cancel
= busy_cue(NULL
, NULL
, 1);
7429 rv
= cmd_delete(state
, msgmap
, agg
, NULL
); /* don't advance or offer "TAB" */
7434 case 'u' : /* undelete */
7435 we_cancel
= busy_cue(NULL
, NULL
, 1);
7436 rv
= cmd_undelete(state
, msgmap
, agg
);
7441 case 'r' : /* reply */
7442 rv
= cmd_reply(state
, msgmap
, agg
);
7445 case 'f' : /* Forward */
7446 rv
= cmd_forward(state
, msgmap
, agg
);
7449 case '%' : /* print */
7450 rv
= cmd_print(state
, msgmap
, agg
, MsgIndx
);
7453 case 't' : /* take address */
7454 rv
= cmd_take_addr(state
, msgmap
, agg
);
7457 case 's' : /* save */
7458 rv
= cmd_save(state
, stream
, msgmap
, agg
, MsgIndx
);
7461 case 'e' : /* export */
7462 rv
= cmd_export(state
, msgmap
, q_line
, agg
);
7465 case '|' : /* pipe */
7466 rv
= cmd_pipe(state
, msgmap
, agg
);
7469 case '*' : /* flag */
7470 we_cancel
= busy_cue(NULL
, NULL
, 1);
7471 rv
= cmd_flag(state
, msgmap
, agg
);
7476 case 'b' : /* bounce */
7477 rv
= cmd_bounce(state
, msgmap
, agg
);
7481 collapse_or_expand(state
, stream
, msgmap
,
7482 F_ON(F_SLASH_COLL_ENTIRE
, ps_global
)
7484 : mn_get_cur(msgmap
));
7488 select_thread_stmp(state
, stream
, msgmap
);
7491 case 'x' : /* Expunge */
7492 rv
= cmd_expunge(state
, stream
, msgmap
, agg
);
7495 case 'c' : /* cancel */
7496 cmd_cancelled((flags
& AC_FROM_THREAD
) ? "Thread command"
7500 case 'z' : /* default */
7501 q_status_message(SM_INFO
, 0, 2,
7502 "Cancelled, there is no default command");
7514 * Select by message number ranges.
7515 * Sets searched bits in mail_elts
7517 * Args limitsrch -- limit search to this searchset
7519 * Returns 0 on success.
7522 select_by_number(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, SEARCHSET
**limitsrch
)
7526 char number1
[16], number2
[16], numbers
[80], *p
, *t
;
7531 ps_global
->mangled_footer
= 1;
7534 int flags
= OE_APPEND_CURRENT
;
7536 r
= optionally_enter(numbers
, -FOOTER_ROWS(ps_global
), 0,
7537 sizeof(numbers
), _(select_num
), NULL
, help
, &flags
);
7542 help
= (help
== NO_HELP
) ? h_select_by_num
: NO_HELP
;
7546 for(t
= p
= numbers
; *p
; p
++) /* strip whitespace */
7547 if(!isspace((unsigned char)*p
))
7552 if(r
== 1 || numbers
[0] == '\0'){
7553 cmd_cancelled("Selection by number");
7560 for(n1
= 1; n1
<= stream
->nmsgs
; n1
++)
7561 if((mc
= mail_elt(stream
, n1
)) != NULL
)
7562 mc
->searched
= 0; /* clear searched bits */
7564 for(p
= numbers
; *p
; p
++){
7566 while(*p
&& isdigit((unsigned char)*p
))
7572 if(number1
[0] == '\0'){
7574 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
7575 _("Invalid number range, missing number before \"-\": %s"),
7579 else if(!strucmp("end", p
)){
7584 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
7585 _("Invalid message number: %s"), numbers
);
7591 n1
= mn_get_total(msgmap
);
7592 else if((n1
= atol(number1
)) < 1L || n1
> mn_get_total(msgmap
)){
7593 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
7594 _("\"%s\" out of message number range"),
7601 while(*++p
&& isdigit((unsigned char)*p
))
7607 if(number2
[0] == '\0'){
7608 if(!strucmp("end", p
)){
7613 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
7614 _("Invalid number range, missing number after \"-\": %s"),
7621 n2
= mn_get_total(msgmap
);
7622 else if((n2
= atol(number2
)) < 1L || n2
> mn_get_total(msgmap
)){
7623 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
7624 _("\"%s\" out of message number range"),
7632 strncpy(t
, long2string(n1
), sizeof(t
));
7633 t
[sizeof(t
)-1] = '\0';
7634 q_status_message2(SM_ORDER
| SM_DING
, 0, 2,
7635 _("Invalid reverse message number range: %s-%s"),
7636 t
, long2string(n2
));
7640 for(;n1
<= n2
; n1
++){
7641 raw
= mn_m2raw(msgmap
, n1
);
7643 && (!(limitsrch
&& *limitsrch
)
7644 || in_searchset(*limitsrch
, (unsigned long) raw
)))
7645 mm_searched(stream
, raw
);
7649 raw
= mn_m2raw(msgmap
, n1
);
7651 && (!(limitsrch
&& *limitsrch
)
7652 || in_searchset(*limitsrch
, (unsigned long) raw
)))
7653 mm_searched(stream
, raw
);
7665 * Select by thread number ranges.
7666 * Sets searched bits in mail_elts
7668 * Args limitsrch -- limit search to this searchset
7670 * Returns 0 on success.
7673 select_by_thrd_number(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, SEARCHSET
**msgset
)
7677 char number1
[16], number2
[16], numbers
[80], *p
, *t
;
7679 PINETHRD_S
*thrd
= NULL
;
7683 ps_global
->mangled_footer
= 1;
7686 int flags
= OE_APPEND_CURRENT
;
7688 r
= optionally_enter(numbers
, -FOOTER_ROWS(ps_global
), 0,
7689 sizeof(numbers
), _(select_num
), NULL
, help
, &flags
);
7694 help
= (help
== NO_HELP
) ? h_select_by_thrdnum
: NO_HELP
;
7698 for(t
= p
= numbers
; *p
; p
++) /* strip whitespace */
7699 if(!isspace((unsigned char)*p
))
7704 if(r
== 1 || numbers
[0] == '\0'){
7705 cmd_cancelled("Selection by number");
7712 for(n1
= 1; n1
<= stream
->nmsgs
; n1
++)
7713 if((mc
= mail_elt(stream
, n1
)) != NULL
)
7714 mc
->searched
= 0; /* clear searched bits */
7716 for(p
= numbers
; *p
; p
++){
7718 while(*p
&& isdigit((unsigned char)*p
))
7724 if(number1
[0] == '\0'){
7726 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
7727 _("Invalid number range, missing number before \"-\": %s"),
7731 else if(!strucmp("end", p
)){
7736 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
7737 _("Invalid thread number: %s"), numbers
);
7743 n1
= msgmap
->max_thrdno
;
7744 else if((n1
= atol(number1
)) < 1L || n1
> msgmap
->max_thrdno
){
7745 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
7746 _("\"%s\" out of thread number range"),
7754 while(*++p
&& isdigit((unsigned char)*p
))
7760 if(number2
[0] == '\0'){
7761 if(!strucmp("end", p
)){
7766 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
7767 _("Invalid number range, missing number after \"-\": %s"),
7774 n2
= msgmap
->max_thrdno
;
7775 else if((n2
= atol(number2
)) < 1L || n2
> msgmap
->max_thrdno
){
7776 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
7777 _("\"%s\" out of thread number range"),
7785 strncpy(t
, long2string(n1
), sizeof(t
));
7786 t
[sizeof(t
)-1] = '\0';
7787 q_status_message2(SM_ORDER
| SM_DING
, 0, 2,
7788 _("Invalid reverse message number range: %s-%s"),
7789 t
, long2string(n2
));
7793 for(;n1
<= n2
; n1
++){
7794 thrd
= find_thread_by_number(stream
, msgmap
, n1
, thrd
);
7797 set_search_bit_for_thread(stream
, thrd
, msgset
);
7801 thrd
= find_thread_by_number(stream
, msgmap
, n1
, NULL
);
7804 set_search_bit_for_thread(stream
, thrd
, msgset
);
7816 * Select by message dates.
7817 * Sets searched bits in mail_elts
7819 * Args limitsrch -- limit search to this searchset
7821 * Returns 0 on success.
7824 select_by_date(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, long int msgno
, SEARCHSET
**limitsrch
)
7826 int r
, we_cancel
= 0, when
= 0;
7827 char date
[100], defdate
[100], prompt
[128];
7828 time_t seldate
= time(0);
7829 struct tm
*seldate_tm
;
7832 static struct _tense
{
7837 {"were ", "SENT SINCE", " (inclusive)"},
7838 {"were ", "SENT BEFORE", " (exclusive)"},
7839 {"were ", "SENT ON", "" },
7840 {"", "ARRIVED SINCE", " (inclusive)"},
7841 {"", "ARRIVED BEFORE", " (exclusive)"},
7842 {"", "ARRIVED ON", "" }
7846 ps_global
->mangled_footer
= 1;
7850 * If talking to an old server, default to SINCE instead of
7851 * SENTSINCE, which was added later.
7853 if(is_imap_stream(stream
) && !modern_imap_stream(stream
))
7857 int flags
= OE_APPEND_CURRENT
;
7859 seldate_tm
= localtime(&seldate
);
7860 snprintf(defdate
, sizeof(defdate
), "%.2d-%.4s-%.4d", seldate_tm
->tm_mday
,
7861 month_abbrev(seldate_tm
->tm_mon
+ 1),
7862 seldate_tm
->tm_year
+ 1900);
7863 defdate
[sizeof(defdate
)-1] = '\0';
7864 snprintf(prompt
,sizeof(prompt
),"Select messages which %s%s%s [%s]: ",
7865 tense
[when
].preamble
, tense
[when
].range
,
7866 tense
[when
].scope
, defdate
);
7867 prompt
[sizeof(prompt
)-1] = '\0';
7868 r
= optionally_enter(date
,-FOOTER_ROWS(ps_global
), 0, sizeof(date
),
7869 prompt
, sel_date_opt
, help
, &flags
);
7872 cmd_cancelled("Selection by date");
7876 help
= (help
== NO_HELP
) ? h_select_date
: NO_HELP
;
7887 if(stream
&& (rawno
= mn_m2raw(msgmap
, msgno
)) > 0L
7888 && rawno
<= stream
->nmsgs
7889 && (mc
= mail_elt(stream
, rawno
))){
7891 /* cache not filled in yet? */
7895 if(stream
->dtb
&& stream
->dtb
->flags
& DR_NEWS
){
7897 ulong2string(mail_uid(stream
, rawno
)),
7899 seq
[sizeof(seq
)-1] = '\0';
7900 mail_fetch_overview(stream
, seq
, NULL
);
7903 strncpy(seq
, long2string(rawno
),
7905 seq
[sizeof(seq
)-1] = '\0';
7906 mail_fetch_fast(stream
, seq
, 0L);
7910 /* mail_date returns fixed field width date */
7911 mail_date(date
, mc
);
7918 case 12 : /* set default to PREVIOUS day */
7922 case 13 : /* set default to NEXT day */
7927 when
= (when
+1) % (sizeof(tense
) / sizeof(struct _tense
));
7934 removing_leading_white_space(date
);
7935 removing_trailing_white_space(date
);
7937 strncpy(date
, defdate
, sizeof(date
));
7938 date
[sizeof(date
)-1] = '\0';
7944 if((pgm
= mail_newsearchpgm()) != NULL
){
7946 short converted_date
;
7948 if(mail_parse_date(&elt
, (unsigned char *) date
)){
7949 converted_date
= mail_shortdate(elt
.year
, elt
.month
, elt
.day
);
7953 pgm
->sentsince
= converted_date
;
7956 pgm
->sentbefore
= converted_date
;
7959 pgm
->senton
= converted_date
;
7962 pgm
->since
= converted_date
;
7965 pgm
->before
= converted_date
;
7968 pgm
->on
= converted_date
;
7972 pgm
->msgno
= (limitsrch
? *limitsrch
: NULL
);
7974 if(ps_global
&& ps_global
->ttyo
){
7975 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
7976 ps_global
->mangled_footer
= 1;
7979 we_cancel
= busy_cue(_("Selecting"), NULL
, 1);
7981 pine_mail_search_full(stream
, NULL
, pgm
, SE_NOPREFETCH
| SE_FREE
);
7986 /* we know this was freed in mail_search, let caller know */
7991 mail_free_searchpgm(&pgm
);
7992 q_status_message1(SM_ORDER
, 3, 3,
7993 _("Invalid date entered: %s"), date
);
8003 * Select by searching in message headers or body.
8004 * Sets searched bits in mail_elts
8006 * Args limitsrch -- limit search to this searchset
8008 * Returns 0 on success.
8011 select_by_text(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, long int msgno
, SEARCHSET
**limitsrch
)
8013 int r
, ku
, type
, we_cancel
= 0, flags
, rv
, ekeyi
= 0;
8014 int not = 0, me
= 0;
8015 char sstring
[80], savedsstring
[80], tmp
[128];
8016 char *p
, *sval
= NULL
;
8017 char buftmp
[MAILTMPLEN
], namehdr
[80];
8019 ENVELOPE
*env
= NULL
;
8021 unsigned flagsforhist
= 0;
8022 static HISTORY_S
*history
= NULL
;
8023 static char *recip
= "RECIPIENTS";
8024 static char *partic
= "PARTICIPANTS";
8025 static char *match_me
= N_("[Match_My_Addresses]");
8026 static char *dont_match_me
= N_("[Don't_Match_My_Addresses]");
8028 ps_global
->mangled_footer
= 1;
8029 savedsstring
[0] = '\0';
8030 ekey
[0].ch
= ekey
[1].ch
= ekey
[2].ch
= ekey
[3].ch
= -1;
8033 type
= radio_buttons(not ? _(sel_text_not
) : _(sel_text
),
8034 -FOOTER_ROWS(ps_global
), sel_text_opt
,
8035 's', 'x', NO_HELP
, RB_NORM
|RB_RET_HELP
);
8040 helper(h_select_text
, "HELP FOR SELECT BASED ON CONTENTS",
8042 ps_global
->mangled_screen
= 1;
8049 * prepare some friendly defaults...
8052 case 't' : /* address fields, offer To or From */
8057 sval
= (type
== 't') ? "TO" :
8058 (type
== 'f') ? "FROM" :
8059 (type
== 'c') ? "CC" :
8060 (type
== 'r') ? recip
: partic
;
8061 ekey
[ekeyi
].ch
= ctrl('T');
8062 ekey
[ekeyi
].name
= "^T";
8063 ekey
[ekeyi
].rval
= 10;
8064 /* TRANSLATORS: use Current To Address */
8065 ekey
[ekeyi
++].label
= N_("Cur To");
8066 ekey
[ekeyi
].ch
= ctrl('R');
8067 ekey
[ekeyi
].name
= "^R";
8068 ekey
[ekeyi
].rval
= 11;
8069 /* TRANSLATORS: use Current From Address */
8070 ekey
[ekeyi
++].label
= N_("Cur From");
8071 ekey
[ekeyi
].ch
= ctrl('W');
8072 ekey
[ekeyi
].name
= "^W";
8073 ekey
[ekeyi
].rval
= 12;
8074 /* TRANSLATORS: use Current Cc Address */
8075 ekey
[ekeyi
++].label
= N_("Cur Cc");
8076 ekey
[ekeyi
].ch
= ctrl('Y');
8077 ekey
[ekeyi
].name
= "^Y";
8078 ekey
[ekeyi
].rval
= 13;
8079 /* TRANSLATORS: Match Me means match my address */
8080 ekey
[ekeyi
++].label
= N_("Match Me");
8082 ekey
[ekeyi
].name
= "";
8083 ekey
[ekeyi
].rval
= 0;
8084 ekey
[ekeyi
++].label
= "";
8089 ekey
[ekeyi
].ch
= ctrl('X');
8090 ekey
[ekeyi
].name
= "^X";
8091 ekey
[ekeyi
].rval
= 14;
8092 /* TRANSLATORS: use Current Subject */
8093 ekey
[ekeyi
++].label
= N_("Cur Subject");
8105 strncpy(tmp
, "Name of HEADER to match : ", sizeof(tmp
)-1);
8106 tmp
[sizeof(tmp
)-1] = '\0';
8107 flags
= OE_APPEND_CURRENT
;
8113 r
= optionally_enter(namehdr
, -FOOTER_ROWS(ps_global
), 0,
8114 sizeof(namehdr
), tmp
, ekey
, NO_HELP
, &flags
);
8116 cmd_cancelled("Selection by text");
8119 removing_leading_white_space(namehdr
);
8121 while ((namehdr
[0] != '\0') && /* remove trailing ":" */
8122 (namehdr
[strlen(namehdr
) - 1] == ':'))
8123 namehdr
[strlen(namehdr
) - 1] = '\0';
8124 if ((namehdr
[0] != '\0')
8125 && isspace((unsigned char) namehdr
[strlen(namehdr
) - 1]))
8126 removing_trailing_white_space(namehdr
);
8130 if (strchr(namehdr
,' ') || strchr(namehdr
,'\t') ||
8131 strchr(namehdr
,':'))
8133 if (namehdr
[0] == '\0')
8143 dprint((1,"\n - BOTCH: select_text unrecognized option\n"));
8147 ekey
[ekeyi
].ch
= KEY_UP
;
8148 ekey
[ekeyi
].rval
= 30;
8149 ekey
[ekeyi
].name
= "";
8151 ekey
[ekeyi
++].label
= "";
8153 ekey
[ekeyi
].ch
= KEY_DOWN
;
8154 ekey
[ekeyi
].rval
= 31;
8155 ekey
[ekeyi
].name
= "";
8156 ekey
[ekeyi
++].label
= "";
8158 ekey
[ekeyi
].ch
= -1;
8162 init_hist(&history
, HISTSIZE
);
8164 if(ekey
[0].ch
> -1 && msgno
> 0L
8165 && !(env
=pine_mail_fetchstructure(stream
,mn_m2raw(msgmap
,msgno
),
8174 /* TRANSLATORS: character String in message <message number> to NOT match : " */
8175 snprintf(tmp
, sizeof(tmp
), "String in message %s to NOT match : ", sval
);
8177 snprintf(tmp
, sizeof(tmp
), "String in message %s to match : ", sval
);
8179 if(items_in_hist(history
) > 0){
8180 ekey
[ku
].name
= HISTORY_UP_KEYNAME
;
8181 ekey
[ku
].label
= HISTORY_KEYLABEL
;
8182 ekey
[ku
+1].name
= HISTORY_DOWN_KEYNAME
;
8183 ekey
[ku
+1].label
= HISTORY_KEYLABEL
;
8187 ekey
[ku
].label
= "";
8188 ekey
[ku
+1].name
= "";
8189 ekey
[ku
+1].label
= "";
8192 flags
= OE_APPEND_CURRENT
| OE_KEEP_TRAILING_SPACE
;
8193 r
= optionally_enter(sstring
, -FOOTER_ROWS(ps_global
), 0,
8194 79, tmp
, ekey
, help
, &flags
);
8196 if(me
&& r
== 0 && ((!not && strcmp(sstring
, _(match_me
))) || (not && strcmp(sstring
, _(dont_match_me
)))))
8201 help
= (help
== NO_HELP
)
8203 ? ((type
== 'f') ? h_select_txt_not_from
8204 : (type
== 't') ? h_select_txt_not_to
8205 : (type
== 'c') ? h_select_txt_not_cc
8206 : (type
== 's') ? h_select_txt_not_subj
8207 : (type
== 'a') ? h_select_txt_not_all
8208 : (type
== 'r') ? h_select_txt_not_recip
8209 : (type
== 'p') ? h_select_txt_not_partic
8210 : (type
== 'b') ? h_select_txt_not_body
8212 : ((type
== 'f') ? h_select_txt_from
8213 : (type
== 't') ? h_select_txt_to
8214 : (type
== 'c') ? h_select_txt_cc
8215 : (type
== 's') ? h_select_txt_subj
8216 : (type
== 'a') ? h_select_txt_all
8217 : (type
== 'r') ? h_select_txt_recip
8218 : (type
== 'p') ? h_select_txt_partic
8219 : (type
== 'b') ? h_select_txt_body
8226 case 10 : /* To: default */
8227 if(env
&& env
->to
&& env
->to
->mailbox
){
8228 snprintf(sstring
, sizeof(sstring
), "%s%s%s", env
->to
->mailbox
,
8229 env
->to
->host
? "@" : "",
8230 env
->to
->host
? env
->to
->host
: "");
8231 sstring
[sizeof(sstring
)-1] = '\0';
8235 case 11 : /* From: default */
8236 if(env
&& env
->from
&& env
->from
->mailbox
){
8237 snprintf(sstring
, sizeof(sstring
), "%s%s%s", env
->from
->mailbox
,
8238 env
->from
->host
? "@" : "",
8239 env
->from
->host
? env
->from
->host
: "");
8240 sstring
[sizeof(sstring
)-1] = '\0';
8244 case 12 : /* Cc: default */
8245 if(env
&& env
->cc
&& env
->cc
->mailbox
){
8246 snprintf(sstring
, sizeof(sstring
), "%s%s%s", env
->cc
->mailbox
,
8247 env
->cc
->host
? "@" : "",
8248 env
->cc
->host
? env
->cc
->host
: "");
8249 sstring
[sizeof(sstring
)-1] = '\0';
8253 case 13 : /* Match my addresses */
8255 snprintf(sstring
, sizeof(sstring
), "%s", not ? _(dont_match_me
) : _(match_me
));
8258 case 14 : /* Subject: default */
8259 if(env
&& env
->subject
&& env
->subject
[0]){
8262 snprintf(buftmp
, sizeof(buftmp
), "%.75s", env
->subject
);
8263 buftmp
[sizeof(buftmp
)-1] = '\0';
8264 q
= (char *) rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
,
8265 SIZEOF_20KBUF
, buftmp
);
8266 if(q
!= env
->subject
){
8267 snprintf(savedsstring
, sizeof(savedsstring
), "%.70s", q
);
8268 savedsstring
[sizeof(savedsstring
)-1] = '\0';
8271 snprintf(sstring
, sizeof(sstring
), "%s", q
);
8272 sstring
[sizeof(sstring
)-1] = '\0';
8278 flagsforhist
= (not ? 0x1 : 0) + (me
? 0x2 : 0);
8279 if((p
= get_prev_hist(history
, sstring
, flagsforhist
, NULL
)) != NULL
){
8280 strncpy(sstring
, p
, sizeof(sstring
));
8281 sstring
[sizeof(sstring
)-1] = '\0';
8282 if(history
->hist
[history
->curindex
]){
8283 flagsforhist
= history
->hist
[history
->curindex
]->flags
;
8284 not = (flagsforhist
& 0x1) ? 1 : 0;
8285 me
= (flagsforhist
& 0x2) ? 1 : 0;
8294 flagsforhist
= (not ? 0x1 : 0) + (me
? 0x2 : 0);
8295 if((p
= get_next_hist(history
, sstring
, flagsforhist
, NULL
)) != NULL
){
8296 strncpy(sstring
, p
, sizeof(sstring
));
8297 sstring
[sizeof(sstring
)-1] = '\0';
8298 if(history
->hist
[history
->curindex
]){
8299 flagsforhist
= history
->hist
[history
->curindex
]->flags
;
8300 not = (flagsforhist
& 0x1) ? 1 : 0;
8301 me
= (flagsforhist
& 0x2) ? 1 : 0;
8313 if(r
== 1 || sstring
[0] == '\0')
8320 if(type
== 'x' || r
== 'x'){
8321 cmd_cancelled("Selection by text");
8325 if(ps_global
&& ps_global
->ttyo
){
8326 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
8327 ps_global
->mangled_footer
= 1;
8330 we_cancel
= busy_cue(_("Selecting"), NULL
, 1);
8332 flagsforhist
= (not ? 0x1 : 0) + (me
? 0x2 : 0);
8333 save_hist(history
, sstring
, flagsforhist
, NULL
);
8335 rv
= agg_text_select(stream
, msgmap
, type
, namehdr
, not, me
, sstring
, "utf-8", limitsrch
);
8344 * Select by message size.
8345 * Sets searched bits in mail_elts
8347 * Args limitsrch -- limit search to this searchset
8349 * Returns 0 on success.
8352 select_by_size(MAILSTREAM
*stream
, SEARCHSET
**limitsrch
)
8354 int r
, large
= 1, we_cancel
= 0;
8355 unsigned long n
, mult
= 1L, numerator
= 0L, divisor
= 1L;
8356 char size
[16], numbers
[80], *p
, *t
;
8359 long flags
= (SE_NOPREFETCH
| SE_FREE
);
8362 ps_global
->mangled_footer
= 1;
8366 int flgs
= OE_APPEND_CURRENT
;
8368 sel_size_opt
[1].label
= large
? sel_size_smaller
: sel_size_larger
;
8370 r
= optionally_enter(numbers
, -FOOTER_ROWS(ps_global
), 0,
8371 sizeof(numbers
), large
? _(select_size_larger_msg
)
8372 : _(select_size_smaller_msg
),
8373 sel_size_opt
, help
, &flgs
);
8383 help
= (help
== NO_HELP
) ? (large
? h_select_by_larger_size
8384 : h_select_by_smaller_size
)
8389 for(t
= p
= numbers
; *p
; p
++) /* strip whitespace */
8390 if(!isspace((unsigned char)*p
))
8395 if(r
== 1 || numbers
[0] == '\0'){
8396 cmd_cancelled("Selection by size");
8403 if(numbers
[0] == '-'){
8404 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
8405 _("Invalid size entered: %s"), numbers
);
8412 while(*p
&& isdigit((unsigned char)*p
))
8417 if(size
[0] == '\0' && *p
== '.' && isdigit(*(p
+1))){
8422 if(size
[0] == '\0'){
8423 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
8424 _("Invalid size entered: %s"), numbers
);
8428 n
= strtoul(size
, (char **)NULL
, 10);
8433 * We probably ought to just use atof() to convert 1.1 into a
8434 * double, but since we haven't used atof() anywhere else I'm
8435 * reluctant to use it because of portability concerns.
8439 while(*p
&& isdigit((unsigned char)*p
)){
8447 numerator
= strtoul(size
, (char **)NULL
, 10);
8467 n
= n
* mult
+ (numerator
* mult
) / divisor
;
8469 pgm
= mail_newsearchpgm();
8475 if(is_imap_stream(stream
) && !modern_imap_stream(stream
))
8476 flags
|= SE_NOSERVER
;
8478 if(ps_global
&& ps_global
->ttyo
){
8479 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
8480 ps_global
->mangled_footer
= 1;
8483 we_cancel
= busy_cue(_("Selecting"), NULL
, 1);
8485 pgm
->msgno
= (limitsrch
? *limitsrch
: NULL
);
8486 pine_mail_search_full(stream
, NULL
, pgm
, SE_NOPREFETCH
| SE_FREE
);
8487 /* we know this was freed in mail_search, let caller know */
8499 * visible_searchset -- return c-client search set unEXLDed
8503 visible_searchset(MAILSTREAM
*stream
, MSGNO_S
*msgmap
)
8506 SEARCHSET
*full_set
= NULL
, **set
;
8509 * If we're talking to anything other than a server older than
8510 * imap 4rev1, build a searchset otherwise it'll choke.
8512 if(!(is_imap_stream(stream
) && !modern_imap_stream(stream
))){
8513 if(any_lflagged(msgmap
, MN_EXLD
)){
8514 for(n
= 1L, set
= &full_set
, run
= 0L; n
<= stream
->nmsgs
; n
++)
8515 if(get_lflag(stream
, NULL
, n
, MN_EXLD
)){
8516 if(run
){ /* previous NOT excluded? */
8518 (*set
)->last
= n
- 1L;
8520 set
= &(*set
)->next
;
8524 else if(run
++){ /* next in run */
8527 else{ /* start of run */
8528 *set
= mail_newsearchset();
8533 full_set
= mail_newsearchset();
8534 full_set
->first
= 1L;
8535 full_set
->last
= stream
->nmsgs
;
8544 * Select by message status bits.
8545 * Sets searched bits in mail_elts
8547 * Args limitsrch -- limit search to this searchset
8549 * Returns 0 on success.
8552 select_by_status(MAILSTREAM
*stream
, SEARCHSET
**limitsrch
)
8554 int s
, not = 0, we_cancel
= 0, rv
;
8557 s
= radio_buttons((not) ? _(sel_flag_not
) : _(sel_flag
),
8558 -FOOTER_ROWS(ps_global
), sel_flag_opt
, '*', 'x',
8559 NO_HELP
, RB_NORM
|RB_RET_HELP
);
8562 cmd_cancelled("Selection by status");
8566 helper(h_select_status
, _("HELP FOR SELECT BASED ON STATUS"),
8568 ps_global
->mangled_screen
= 1;
8576 if(ps_global
&& ps_global
->ttyo
){
8577 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
8578 ps_global
->mangled_footer
= 1;
8581 we_cancel
= busy_cue(_("Selecting"), NULL
, 1);
8582 rv
= agg_flag_select(stream
, not, s
, limitsrch
);
8591 * Select by rule. Usually srch, indexcolor, and roles would be most useful.
8592 * Sets searched bits in mail_elts
8594 * Args limitsrch -- limit search to this searchset
8596 * Returns 0 on success.
8599 select_by_rule(MAILSTREAM
*stream
, SEARCHSET
**limitsrch
)
8601 char rulenick
[1000], *nick
;
8603 int r
, not = 0, we_cancel
= 0, rflags
= ROLE_DO_SRCH
8611 ps_global
->mangled_footer
= 1;
8616 oe_flags
= OE_APPEND_CURRENT
;
8617 r
= optionally_enter(rulenick
, -FOOTER_ROWS(ps_global
), 0,
8619 not ? _("Rule to NOT match: ")
8620 : _("Rule to match: "),
8621 sel_key_opt
, NO_HELP
, &oe_flags
);
8624 /* select rulenick from a list */
8625 if((nick
=choose_a_rule(rflags
)) != NULL
){
8626 strncpy(rulenick
, nick
, sizeof(rulenick
)-1);
8627 rulenick
[sizeof(rulenick
)-1] = '\0';
8628 fs_give((void **) &nick
);
8637 helper(h_select_rule
, _("HELP FOR SELECT BY RULE"), HLPD_SIMPLE
);
8638 ps_global
->mangled_screen
= 1;
8641 cmd_cancelled("Selection by Rule");
8645 removing_leading_and_trailing_white_space(rulenick
);
8647 }while(r
== 3 || r
== 4 || r
== '!');
8651 * The approach of requiring a nickname instead of just allowing the
8652 * user to select from the list of rules has the drawback that a rule
8653 * may not have a nickname, or there may be more than one rule with
8654 * the same nickname. However, it has the benefit of allowing the user
8655 * to type in the nickname and, most importantly, allows us to set
8656 * up the ! (not). We could incorporate the ! into the selection
8657 * screen, but this is easier and also allows the typing of nicks.
8658 * User can just set up nicknames if they want to use this feature.
8660 patgrp
= nick_to_patgrp(rulenick
, rflags
);
8663 if(ps_global
&& ps_global
->ttyo
){
8664 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
8665 ps_global
->mangled_footer
= 1;
8668 we_cancel
= busy_cue(_("Selecting"), NULL
, 1);
8669 match_pattern(patgrp
, stream
, limitsrch
? *limitsrch
: 0, NULL
,
8671 (not ? MP_NOT
: 0) | SE_NOPREFETCH
);
8672 free_patgrp(&patgrp
);
8677 if(limitsrch
&& *limitsrch
){
8678 mail_free_searchset(limitsrch
);
8687 * Allow user to choose a rule from their list of rules.
8689 * Returns an allocated rule nickname on success, NULL otherwise.
8692 choose_a_rule(int rflags
)
8694 char *choice
= NULL
;
8695 char **rule_list
, **lp
;
8700 if(!(nonempty_patterns(rflags
, &pstate
) && first_pattern(&pstate
))){
8701 q_status_message(SM_ORDER
, 3, 3,
8702 _("No rules available. Use Setup/Rules to add some."));
8707 * Build a list of rules to choose from.
8710 for(pat
= first_pattern(&pstate
); pat
; pat
= next_pattern(&pstate
))
8714 q_status_message(SM_ORDER
, 3, 4, _("No rules defined, use Setup/Rules"));
8718 lp
= rule_list
= (char **) fs_get((cnt
+ 1) * sizeof(*rule_list
));
8719 memset(rule_list
, 0, (cnt
+1) * sizeof(*rule_list
));
8721 for(pat
= first_pattern(&pstate
); pat
; pat
= next_pattern(&pstate
))
8722 *lp
++ = cpystr((pat
->patgrp
&& pat
->patgrp
->nick
)
8723 ? pat
->patgrp
->nick
: "?");
8725 /* TRANSLATORS: SELECT A RULE is a screen title
8726 TRANSLATORS: Print something1 using something2.
8727 "rules" is something1 */
8728 choice
= choose_item_from_list(rule_list
, NULL
, _("SELECT A RULE"),
8729 _("rules"), h_select_rule_screen
,
8730 _("HELP FOR SELECTING A RULE NICKNAME"), NULL
);
8733 q_status_message(SM_ORDER
, 1, 4, "No choice");
8735 free_list_array(&rule_list
);
8742 * Select by current thread.
8743 * Sets searched bits in mail_elts for this entire thread
8745 * Args limitsrch -- limit search to this searchset
8747 * Returns 0 on success.
8750 select_by_thread(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, SEARCHSET
**limitsrch
)
8753 PINETHRD_S
*thrd
= NULL
;
8760 for(n
= 1L; n
<= stream
->nmsgs
; n
++)
8761 if((mc
= mail_elt(stream
, n
)) != NULL
)
8762 mc
->searched
= 0; /* clear searched bits */
8764 thrd
= fetch_thread(stream
, mn_m2raw(msgmap
, mn_get_cur(msgmap
)));
8765 if(thrd
&& thrd
->top
&& thrd
->top
!= thrd
->rawno
)
8766 thrd
= fetch_thread(stream
, thrd
->top
);
8769 * This doesn't unselect if the thread is already selected
8770 * (like select current does), it always selects.
8771 * There is no way to select ! this thread.
8774 set_search_bit_for_thread(stream
, thrd
, limitsrch
);
8783 * Select by message keywords.
8784 * Sets searched bits in mail_elts
8786 * Args limitsrch -- limit search to this searchset
8788 * Returns 0 on success.
8791 select_by_keyword(MAILSTREAM
*stream
, SEARCHSET
**limitsrch
)
8793 int r
, not = 0, we_cancel
= 0;
8794 char keyword
[MAXUSERFLAG
+1], *kword
;
8795 char *error
= NULL
, *p
, *prompt
;
8800 ps_global
->mangled_footer
= 1;
8807 q_status_message(SM_ORDER
, 3, 4, error
);
8808 fs_give((void **) &error
);
8811 if(F_ON(F_FLAG_SCREEN_KW_SHORTCUT
, ps_global
) && ps_global
->keywords
){
8813 prompt
= _("Keyword (or keyword initial) to NOT match: ");
8815 prompt
= _("Keyword (or keyword initial) to match: ");
8819 prompt
= _("Keyword to NOT match: ");
8821 prompt
= _("Keyword to match: ");
8824 oe_flags
= OE_APPEND_CURRENT
;
8825 r
= optionally_enter(keyword
, -FOOTER_ROWS(ps_global
), 0,
8827 prompt
, sel_key_opt
, help
, &oe_flags
);
8830 /* select keyword from a list */
8831 if((kword
=choose_a_keyword()) != NULL
){
8832 strncpy(keyword
, kword
, sizeof(keyword
)-1);
8833 keyword
[sizeof(keyword
)-1] = '\0';
8834 fs_give((void **) &kword
);
8843 help
= help
== NO_HELP
? h_select_keyword
: NO_HELP
;
8845 cmd_cancelled("Selection by keyword");
8849 removing_leading_and_trailing_white_space(keyword
);
8851 }while(r
== 3 || r
== 4 || r
== '!' || keyword_check(keyword
, &error
));
8854 if(F_ON(F_FLAG_SCREEN_KW_SHORTCUT
, ps_global
) && ps_global
->keywords
){
8855 p
= initial_to_keyword(keyword
);
8857 strncpy(keyword
, p
, sizeof(keyword
)-1);
8858 keyword
[sizeof(keyword
)-1] = '\0';
8863 * We want to check the keyword, not the nickname of the keyword,
8864 * so convert it to the keyword if necessary.
8866 p
= nick_to_keyword(keyword
);
8868 strncpy(keyword
, p
, sizeof(keyword
)-1);
8869 keyword
[sizeof(keyword
)-1] = '\0';
8872 pgm
= mail_newsearchpgm();
8874 pgm
->unkeyword
= mail_newstringlist();
8875 pgm
->unkeyword
->text
.data
= (unsigned char *) cpystr(keyword
);
8876 pgm
->unkeyword
->text
.size
= strlen(keyword
);
8879 pgm
->keyword
= mail_newstringlist();
8880 pgm
->keyword
->text
.data
= (unsigned char *) cpystr(keyword
);
8881 pgm
->keyword
->text
.size
= strlen(keyword
);
8884 if(ps_global
&& ps_global
->ttyo
){
8885 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
8886 ps_global
->mangled_footer
= 1;
8889 we_cancel
= busy_cue(_("Selecting"), NULL
, 1);
8891 pgm
->msgno
= (limitsrch
? *limitsrch
: NULL
);
8892 pine_mail_search_full(stream
, "UTF-8", pgm
, SE_NOPREFETCH
| SE_FREE
);
8893 /* we know this was freed in mail_search, let caller know */
8905 * Allow user to choose a keyword from their list of keywords.
8907 * Returns an allocated keyword on success, NULL otherwise.
8910 choose_a_keyword(void)
8912 char *choice
= NULL
;
8913 char **keyword_list
, **lp
;
8918 * Build a list of keywords to choose from.
8921 for(cnt
= 0, kw
= ps_global
->keywords
; kw
; kw
= kw
->next
)
8925 q_status_message(SM_ORDER
, 3, 4,
8926 _("No keywords defined, use \"keywords\" option in Setup/Config"));
8930 lp
= keyword_list
= (char **) fs_get((cnt
+ 1) * sizeof(*keyword_list
));
8931 memset(keyword_list
, 0, (cnt
+1) * sizeof(*keyword_list
));
8933 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
)
8934 *lp
++ = cpystr(kw
->nick
? kw
->nick
: kw
->kw
? kw
->kw
: "");
8936 /* TRANSLATORS: SELECT A KEYWORD is a screen title
8937 TRANSLATORS: Print something1 using something2.
8938 "keywords" is something1 */
8939 choice
= choose_item_from_list(keyword_list
, NULL
, _("SELECT A KEYWORD"),
8940 _("keywords"), h_select_keyword_screen
,
8941 _("HELP FOR SELECTING A KEYWORD"), NULL
);
8944 q_status_message(SM_ORDER
, 1, 4, "No choice");
8946 free_list_array(&keyword_list
);
8953 * Allow user to choose a list of keywords from their list of keywords.
8955 * Returns allocated list.
8958 choose_list_of_keywords(void)
8960 LIST_SEL_S
*listhead
, *ls
, *p
;
8966 * Build a list of keywords to choose from.
8969 p
= listhead
= NULL
;
8970 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
){
8972 ls
= (LIST_SEL_S
*) fs_get(sizeof(*ls
));
8973 memset(ls
, 0, sizeof(*ls
));
8974 ls
->item
= cpystr(kw
->nick
? kw
->nick
: kw
->kw
? kw
->kw
: "");
8987 /* TRANSLATORS: SELECT KEYWORDS is a screen title
8988 Print something1 using something2.
8989 "keywords" is something1 */
8990 if(!select_from_list_screen(listhead
, SFL_ALLOW_LISTMODE
,
8991 _("SELECT KEYWORDS"), _("keywords"),
8992 h_select_multkeyword_screen
,
8993 _("HELP FOR SELECTING KEYWORDS"), NULL
)){
8994 for(cnt
= 0, p
= listhead
; p
; p
= p
->next
)
8998 ret
= (char **) fs_get((cnt
+1) * sizeof(*ret
));
8999 memset(ret
, 0, (cnt
+1) * sizeof(*ret
));
9000 for(i
= 0, p
= listhead
; p
; p
= p
->next
)
9002 ret
[i
++] = cpystr(p
->item
? p
->item
: "");
9005 free_list_sel(&listhead
);
9012 * Allow user to choose a charset
9014 * Returns an allocated charset on success, NULL otherwise.
9017 choose_a_charset(int which_charsets
)
9019 char *choice
= NULL
;
9020 char **charset_list
, **lp
;
9025 * Build a list of charsets to choose from.
9028 for(cnt
= 0, cs
= utf8_charset(NIL
); cs
&& cs
->name
; cs
++){
9029 if(!(cs
->flags
& (CF_UNSUPRT
|CF_NOEMAIL
))
9030 && ((which_charsets
& CAC_ALL
)
9031 || (which_charsets
& CAC_POSTING
9032 && cs
->flags
& CF_POSTING
)
9033 || (which_charsets
& CAC_DISPLAY
9034 && cs
->type
!= CT_2022
9035 && (cs
->flags
& (CF_PRIMARY
|CF_DISPLAY
)) == (CF_PRIMARY
|CF_DISPLAY
))))
9040 q_status_message(SM_ORDER
, 3, 4,
9041 _("No charsets found? Enter charset manually."));
9045 lp
= charset_list
= (char **) fs_get((cnt
+ 1) * sizeof(*charset_list
));
9046 memset(charset_list
, 0, (cnt
+1) * sizeof(*charset_list
));
9048 for(cs
= utf8_charset(NIL
); cs
&& cs
->name
; cs
++){
9049 if(!(cs
->flags
& (CF_UNSUPRT
|CF_NOEMAIL
))
9050 && ((which_charsets
& CAC_ALL
)
9051 || (which_charsets
& CAC_POSTING
9052 && cs
->flags
& CF_POSTING
)
9053 || (which_charsets
& CAC_DISPLAY
9054 && cs
->type
!= CT_2022
9055 && (cs
->flags
& (CF_PRIMARY
|CF_DISPLAY
)) == (CF_PRIMARY
|CF_DISPLAY
))))
9056 *lp
++ = cpystr(cs
->name
);
9059 /* TRANSLATORS: SELECT A CHARACTER SET is a screen title
9060 TRANSLATORS: Print something1 using something2.
9061 "character sets" is something1 */
9062 choice
= choose_item_from_list(charset_list
, NULL
, _("SELECT A CHARACTER SET"),
9063 _("character sets"), h_select_charset_screen
,
9064 _("HELP FOR SELECTING A CHARACTER SET"), NULL
);
9067 q_status_message(SM_ORDER
, 1, 4, "No choice");
9069 free_list_array(&charset_list
);
9076 * Allow user to choose a list of character sets and/or scripts
9078 * Returns allocated list.
9081 choose_list_of_charsets(void)
9083 LIST_SEL_S
*listhead
, *ls
, *p
;
9085 int cnt
, i
, got_one
;
9090 char buf
[1024], *folded
;
9093 * Build a list of charsets to choose from.
9096 p
= listhead
= NULL
;
9098 /* this width is determined by select_from_list_screen() */
9099 width
= ps_global
->ttyo
->screen_cols
- 4;
9101 /* first comes a list of scripts (sets of character sets) */
9102 for(s
= utf8_script(NIL
); s
&& s
->name
; s
++){
9104 limit
= sizeof(buf
)-1;
9106 memset(q
, 0, limit
+1);
9109 sstrncpy(&q
, s
->name
, limit
);
9112 sstrncpy(&q
, " (", limit
-(q
-buf
));
9113 sstrncpy(&q
, s
->description
, limit
-(q
-buf
));
9114 sstrncpy(&q
, ")", limit
-(q
-buf
));
9117 /* add the list of charsets that are in this script */
9119 for(cs
= utf8_charset(NIL
);
9120 cs
&& cs
->name
&& (q
-buf
) < limit
; cs
++){
9121 if(cs
->script
& s
->script
){
9123 * Filter out some un-useful members of the list.
9124 * UTF-7 and UTF-8 weren't actually in the list at the
9125 * time this was written. Just making sure.
9127 if(!strucmp(cs
->name
, "ISO-2022-JP-2")
9128 || !strucmp(cs
->name
, "UTF-7")
9129 || !strucmp(cs
->name
, "UTF-8"))
9133 sstrncpy(&q
, " ", limit
-(q
-buf
));
9136 sstrncpy(&q
, " {", limit
-(q
-buf
));
9139 sstrncpy(&q
, cs
->name
, limit
-(q
-buf
));
9144 sstrncpy(&q
, "}", limit
-(q
-buf
));
9146 /* fold this line so that it can all be seen on the screen */
9147 folded
= fold(buf
, width
, width
, "", " ", FLD_NONE
);
9150 while(t
&& *t
&& (q
= strindex(t
, '\n')) != NULL
){
9153 ls
= (LIST_SEL_S
*) fs_get(sizeof(*ls
));
9154 memset(ls
, 0, sizeof(*ls
));
9156 ls
->item
= cpystr(s
->name
);
9158 ls
->flags
= SFL_NOSELECT
;
9160 ls
->display_item
= cpystr(t
);
9170 listhead
= (LIST_SEL_S
*) fs_get(sizeof(*ls
));
9171 memset(listhead
, 0, sizeof(*listhead
));
9172 listhead
->flags
= SFL_NOSELECT
;
9173 listhead
->display_item
=
9174 cpystr(_("Scripts representing groups of related character sets"));
9175 listhead
->next
= (LIST_SEL_S
*) fs_get(sizeof(*ls
));
9176 memset(listhead
->next
, 0, sizeof(*listhead
));
9177 listhead
->next
->flags
= SFL_NOSELECT
;
9178 listhead
->next
->display_item
=
9179 cpystr(repeat_char(width
, '-'));
9181 listhead
->next
->next
= ls
;
9186 fs_give((void **) &folded
);
9190 ls
= (LIST_SEL_S
*) fs_get(sizeof(*ls
));
9191 memset(ls
, 0, sizeof(*ls
));
9192 ls
->flags
= SFL_NOSELECT
;
9200 ls
= (LIST_SEL_S
*) fs_get(sizeof(*ls
));
9201 memset(ls
, 0, sizeof(*ls
));
9202 ls
->flags
= SFL_NOSELECT
;
9204 cpystr(_("Individual character sets, may be mixed with scripts"));
9208 ls
= (LIST_SEL_S
*) fs_get(sizeof(*ls
));
9209 memset(ls
, 0, sizeof(*ls
));
9210 ls
->flags
= SFL_NOSELECT
;
9212 cpystr(repeat_char(width
, '-'));
9216 /* then comes a list of individual character sets */
9217 for(cs
= utf8_charset(NIL
); cs
&& cs
->name
; cs
++){
9218 ls
= (LIST_SEL_S
*) fs_get(sizeof(*ls
));
9219 memset(ls
, 0, sizeof(*ls
));
9220 ls
->item
= cpystr(cs
->name
);
9233 /* TRANSLATORS: SELECT CHARACTER SETS is a screen title
9234 Print something1 using something2.
9235 "character sets" is something1 */
9236 if(!select_from_list_screen(listhead
, SFL_ALLOW_LISTMODE
,
9237 _("SELECT CHARACTER SETS"), _("character sets"),
9238 h_select_multcharsets_screen
,
9239 _("HELP FOR SELECTING CHARACTER SETS"), NULL
)){
9240 for(cnt
= 0, p
= listhead
; p
; p
= p
->next
)
9244 ret
= (char **) fs_get((cnt
+1) * sizeof(*ret
));
9245 memset(ret
, 0, (cnt
+1) * sizeof(*ret
));
9246 for(i
= 0, p
= listhead
; p
; p
= p
->next
)
9248 ret
[i
++] = cpystr(p
->item
? p
->item
: "");
9251 free_list_sel(&listhead
);
9256 /* Report quota summary resources in an IMAP server */
9258 void cmd_quota (struct pine
*state
)
9260 QUOTALIST
*imapquota
;
9265 if(!state
->mail_stream
|| !is_imap_stream(state
->mail_stream
)){
9266 q_status_message(SM_ORDER
, 1, 5, "Quota only available for IMAP folders");
9270 if (state
->mail_stream
9271 && !sp_dead_stream(state
->mail_stream
)
9272 && state
->mail_stream
->mailbox
9273 && *state
->mail_stream
->mailbox
9274 && mail_valid_net_parse(state
->mail_stream
->mailbox
, &mb
))
9275 imap_getquotaroot(state
->mail_stream
, mb
.mailbox
);
9277 if(!state
->quota
) /* failed ? */
9278 return; /* go back... */
9280 if(!(store
= so_get(CharStar
, NULL
, EDIT_ACCESS
))){
9281 q_status_message(SM_ORDER
| SM_DING
, 3, 3, "Error allocating space.");
9285 so_puts(store
, "Quota Report for ");
9286 so_puts(store
, state
->mail_stream
->original_mailbox
);
9287 so_puts(store
, "\n\n");
9289 for (imapquota
= state
->quota
; imapquota
; imapquota
= imapquota
->next
){
9291 so_puts(store
, _("Resource : "));
9292 so_puts(store
, imapquota
->name
);
9293 so_writec('\n', store
);
9295 so_puts(store
, _("Usage : "));
9296 so_puts(store
, long2string(imapquota
->usage
));
9297 if(!strucmp(imapquota
->name
,"STORAGE"))
9298 so_puts(store
, " KiB ");
9299 if(!strucmp(imapquota
->name
,"MESSAGE")){
9300 so_puts(store
, _(" message"));
9301 if(imapquota
->usage
!= 1)
9302 so_puts(store
, _("s ")); /* plural */
9304 so_puts(store
, _(" "));
9306 so_writec('(', store
);
9307 so_puts(store
, long2string(100*imapquota
->usage
/imapquota
->limit
));
9308 so_puts(store
, "%)\n");
9310 so_puts(store
, _("Limit : "));
9311 so_puts(store
, long2string(imapquota
->limit
));
9312 if(!strucmp(imapquota
->name
,"STORAGE"))
9313 so_puts(store
, " KiB\n\n");
9314 if(!strucmp(imapquota
->name
,"MESSAGE")){
9315 so_puts(store
, _(" message"));
9316 if(imapquota
->usage
!= 1)
9317 so_puts(store
, _("s\n\n")); /* plural */
9319 so_puts(store
, _("\n\n"));
9323 memset(&sargs
, 0, sizeof(SCROLL_S
));
9324 sargs
.text
.text
= so_text(store
);
9325 sargs
.text
.src
= CharStar
;
9326 sargs
.text
.desc
= _("Quota Resources Summary");
9327 sargs
.bar
.title
= _("QUOTA SUMMARY");
9328 sargs
.proc
.tool
= NULL
;
9329 sargs
.help
.text
= h_quota_command
;
9330 sargs
.help
.title
= NULL
;
9331 sargs
.keys
.menu
= NULL
;
9332 setbitmap(sargs
.keys
.bitmap
);
9338 mail_free_quotalist(&(state
->quota
));
9341 /*----------------------------------------------------------------------
9342 Prompt the user for the type of sort he desires
9344 Args: state -- pine state pointer
9345 q1 -- Line to prompt on
9347 Returns 0 if it was cancelled, 1 otherwise.
9350 select_sort(struct pine
*state
, int ql
, SortOrder
*sort
, int *rev
)
9352 char prompt
[200], tmp
[3], *p
;
9354 int deefault
= 'a', retval
= 1;
9359 DLG_SORTPARAM sortsel
;
9361 if (mswin_usedialog ()) {
9363 sortsel
.reverse
= mn_get_revsort (state
->msgmap
);
9364 sortsel
.cursort
= mn_get_sort (state
->msgmap
);
9365 /* assumption here that HelpType is char ** */
9366 sortsel
.helptext
= h_select_sort
;
9369 if ((retval
= os_sortdialog (&sortsel
))) {
9370 *sort
= sortsel
.cursort
;
9371 *rev
= sortsel
.reverse
;
9378 /*----- String together the prompt ------*/
9380 if(F_ON(F_USE_FK
,ps_global
))
9381 strncpy(prompt
, _("Choose type of sort : "), sizeof(prompt
));
9383 strncpy(prompt
, _("Choose type of sort, or 'R' to reverse current sort : "),
9386 for(i
= 0; state
->sort_types
[i
] != EndofList
; i
++) {
9388 p
= sorts
[i
].label
= sort_name(state
->sort_types
[i
]);
9389 while(*(p
+1) && islower((unsigned char)*p
))
9392 sorts
[i
].ch
= tolower((unsigned char)(tmp
[0] = *p
));
9393 sorts
[i
].name
= cpystr(tmp
);
9395 if(mn_get_sort(state
->msgmap
) == state
->sort_types
[i
])
9396 deefault
= sorts
[i
].rval
;
9400 sorts
[i
].rval
= 'r';
9401 sorts
[i
].name
= cpystr("R");
9402 if(F_ON(F_USE_FK
,ps_global
))
9403 sorts
[i
].label
= N_("Reverse");
9405 sorts
[i
].label
= "";
9408 help
= h_select_sort
;
9410 if((F_ON(F_USE_FK
,ps_global
)
9411 && ((s
= double_radio_buttons(prompt
,ql
,sorts
,deefault
,'x',
9412 help
,RB_NORM
)) != 'x'))
9414 (F_OFF(F_USE_FK
,ps_global
)
9415 && ((s
= radio_buttons(prompt
,ql
,sorts
,deefault
,'x',
9416 help
,RB_NORM
)) != 'x'))){
9417 state
->mangled_body
= 1; /* signal screen's changed */
9419 *rev
= !mn_get_revsort(state
->msgmap
);
9421 *sort
= state
->sort_types
[s
];
9423 if(F_ON(F_SHOW_SORT
, ps_global
))
9424 ps_global
->mangled_header
= 1;
9428 cmd_cancelled("Sort");
9432 fs_give((void **)&sorts
[i
].name
);
9434 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
9439 /*---------------------------------------------------------------------
9440 Build list of folders in the given context for user selection
9442 Args: c -- pointer to pointer to folder's context context
9443 f -- folder prefix to display
9444 sublist -- whether or not to use 'f's contents as prefix
9445 lister -- function used to do the actual display
9447 Returns: malloc'd string containing sequence, else NULL if
9448 no messages in msgmap with local "selected" flag.
9451 display_folder_list(CONTEXT_S
**c
, char *f
, int sublist
, int (*lister
) (struct pine
*, CONTEXT_S
**, char *, int))
9455 void (*redraw
)(void) = ps_global
->redrawer
;
9457 push_titlebar_state();
9459 if((rc
= (*lister
)(ps_global
, &tc
, f
, sublist
)) != 0)
9463 pop_titlebar_state();
9465 if((ps_global
->redrawer
= redraw
) != NULL
) /* reset old value, and test */
9466 (*ps_global
->redrawer
)();
9468 if(rc
== 1 && F_ON(F_SELECT_WO_CONFIRM
, ps_global
))
9476 * Allow user to choose a single item from a list of strings.
9478 * Args list -- Array of strings to choose from, NULL terminated.
9479 * displist -- Array of strings to display instead of displaying list.
9480 * Indices correspond to the list array. Display the displist
9481 * but return the item from list if displist non-NULL.
9482 * title -- For conf_scroll_screen
9483 * pdesc -- For conf_scroll_screen
9484 * help -- For conf_scroll_screen
9485 * htitle -- For conf_scroll_screen
9487 * Returns an allocated copy of the chosen item or NULL.
9490 choose_item_from_list(char **list
, char **displist
, char *title
, char *pdesc
, HelpType help
,
9491 char *htitle
, char *cursor_location
)
9493 LIST_SEL_S
*listhead
, *ls
, *p
, *starting_val
= NULL
;
9495 char *ret
= NULL
, *choice
= NULL
;
9497 /* build the LIST_SEL_S list */
9498 p
= listhead
= NULL
;
9499 for(t
= list
, dl
= displist
; *t
; t
++, dl
++){
9500 ls
= (LIST_SEL_S
*) fs_get(sizeof(*ls
));
9501 memset(ls
, 0, sizeof(*ls
));
9502 ls
->item
= cpystr(*t
);
9504 ls
->display_item
= cpystr(*dl
);
9506 if(cursor_location
&& (cursor_location
== (*t
)))
9520 if(!select_from_list_screen(listhead
, SFL_NONE
, title
, pdesc
,
9521 help
, htitle
, starting_val
))
9522 for(p
= listhead
; !choice
&& p
; p
= p
->next
)
9527 ret
= cpystr(choice
);
9529 free_list_sel(&listhead
);
9536 free_list_sel(LIST_SEL_S
**lsel
)
9539 free_list_sel(&(*lsel
)->next
);
9541 fs_give((void **) &(*lsel
)->item
);
9543 if((*lsel
)->display_item
)
9544 fs_give((void **) &(*lsel
)->display_item
);
9546 fs_give((void **) lsel
);
9552 * file_lister - call pico library's file lister
9555 file_lister(char *title
, char *path
, size_t pathlen
, char *file
, size_t filelen
, int newmail
, int flags
)
9559 void (*redraw
)(void) = ps_global
->redrawer
;
9561 standard_picobuf_setup(&pbf
);
9562 push_titlebar_state();
9566 /* BUG: what about help command and text? */
9567 pbf
.pine_anchor
= title
;
9569 rv
= pico_file_browse(&pbf
, path
, pathlen
, file
, filelen
, NULL
, 0, flags
);
9570 standard_picobuf_teardown(&pbf
);
9571 fix_windsize(ps_global
);
9572 init_signals(); /* has it's own signal stuff */
9574 /* Restore display's titlebar and body */
9575 pop_titlebar_state();
9577 if((ps_global
->redrawer
= redraw
) != NULL
)
9578 (*ps_global
->redrawer
)();
9584 /*----------------------------------------------------------------------
9585 Print current folder index
9589 print_index(struct pine
*state
, MSGNO_S
*msgmap
, int agg
)
9593 char buf
[MAX_SCREEN_COLS
+1];
9595 for(i
= 1L; i
<= mn_get_total(msgmap
); i
++){
9596 if(agg
&& !get_lflag(state
->mail_stream
, msgmap
, i
, MN_SLCT
))
9599 if(!agg
&& msgline_hidden(state
->mail_stream
, msgmap
, i
, 0))
9602 ice
= build_header_line(state
, state
->mail_stream
, msgmap
, i
, NULL
);
9606 * I don't understand why we'd want to mark the current message
9607 * instead of printing out the first character of the status
9608 * so I'm taking it out and including the first character of the
9609 * line instead. Hubert 2006-02-09
9611 if(!print_char((mn_is_cur(msgmap, i)) ? '>' : ' '))
9615 if(!gf_puts(simple_index_line(buf
,sizeof(buf
),ice
,i
),
9617 || !gf_puts(NEWLINE
, print_char
))
9629 * windows callback to get/set header mode state
9632 header_mode_callback(set
, args
)
9636 return(ps_global
->full_header
);
9641 * windows callback to get/set zoom mode state
9644 zoom_mode_callback(set
, args
)
9648 return(any_lflagged(ps_global
->msgmap
, MN_HIDE
) != 0);
9653 * windows callback to get/set zoom mode state
9656 any_selected_callback(set
, args
)
9660 return(any_lflagged(ps_global
->msgmap
, MN_SLCT
) != 0);
9668 flag_callback(set
, flags
)
9678 case 1: /* Important */
9679 permflag
= ps_global
->mail_stream
->perm_flagged
;
9683 permflag
= ps_global
->mail_stream
->perm_seen
;
9686 case 3: /* Answered */
9687 permflag
= ps_global
->mail_stream
->perm_answered
;
9690 case 4: /* Deleted */
9691 permflag
= ps_global
->mail_stream
->perm_deleted
;
9696 if(!(any_messages(ps_global
->msgmap
, NULL
, "to Flag")
9697 && can_set_flag(ps_global
, "flag", permflag
)))
9700 if(sp_io_error_on_stream(ps_global
->mail_stream
)){
9701 sp_set_io_error_on_stream(ps_global
->mail_stream
, 0);
9702 pine_mail_check(ps_global
->mail_stream
); /* forces write */
9706 msgno
= mn_m2raw(ps_global
->msgmap
, mn_get_cur(ps_global
->msgmap
));
9707 if(msgno
> 0L && ps_global
->mail_stream
9708 && msgno
<= ps_global
->mail_stream
->nmsgs
9709 && (mc
= mail_elt(ps_global
->mail_stream
, msgno
))
9712 * NOTE: code below is *VERY* sensitive to the order of
9713 * the messages defined in resource.h for flag handling.
9714 * Don't change it unless you know what you're doing.
9721 case 1 : /* Important */
9722 flagstr
= "\\FLAGGED";
9723 mflag
= (mc
->flagged
) ? 0L : ST_SET
;
9728 mflag
= (mc
->seen
) ? 0L : ST_SET
;
9731 case 3 : /* Answered */
9732 flagstr
= "\\ANSWERED";
9733 mflag
= (mc
->answered
) ? 0L : ST_SET
;
9736 case 4 : /* Deleted */
9737 flagstr
= "\\DELETED";
9738 mflag
= (mc
->deleted
) ? 0L : ST_SET
;
9741 default : /* bogus */
9745 mail_flag(ps_global
->mail_stream
, long2string(msgno
),
9748 if(ps_global
->redrawer
)
9749 (*ps_global
->redrawer
)();
9776 * BUG: Should teach this about keywords
9782 static MPopup flag_submenu
[] = {
9783 {tMessage
, {N_("Important"), lNormal
}, {IDM_MI_FLAGIMPORTANT
}},
9784 {tMessage
, {N_("New"), lNormal
}, {IDM_MI_FLAGNEW
}},
9785 {tMessage
, {N_("Answered"), lNormal
}, {IDM_MI_FLAGANSWERED
}},
9786 {tMessage
, {N_("Deleted"), lNormal
}, {IDM_MI_FLAGDELETED
}},
9791 flag_submenu
[0].label
.style
= (mc
&& mc
->flagged
) ? lChecked
: lNormal
;
9794 flag_submenu
[1].label
.style
= (mc
&& mc
->seen
) ? lNormal
: lChecked
;
9797 flag_submenu
[2].label
.style
= (mc
&& mc
->answered
) ? lChecked
: lNormal
;
9800 flag_submenu
[3].label
.style
= (mc
&& mc
->deleted
) ? lChecked
: lNormal
;
9802 return(flag_submenu
);
9805 #endif /* _WINDOWS */