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, ACTION_S
*);
87 int cmd_forward(struct pine
*, MSGNO_S
*, int, ACTION_S
*);
88 int cmd_bounce(struct pine
*, MSGNO_S
*, int, ACTION_S
*);
89 int cmd_save(struct pine
*, MAILSTREAM
*, MSGNO_S
*, int, CmdWhere
);
90 void role_compose(struct pine
*);
91 int cmd_expunge(struct pine
*, MAILSTREAM
*, MSGNO_S
*, int);
92 int cmd_export(struct pine
*, MSGNO_S
*, int, int);
93 char *cmd_delete_action(struct pine
*, MSGNO_S
*, CmdWhere
);
94 char *cmd_delete_view(struct pine
*, MSGNO_S
*);
95 char *cmd_delete_index(struct pine
*, MSGNO_S
*);
96 long get_level(int, UCS
, SCROLL_S
*);
97 long closest_jump_target(long, MAILSTREAM
*, MSGNO_S
*, int, CmdWhere
, char *, size_t);
98 int update_folder_spec(char *, size_t, char *);
99 int cmd_print(struct pine
*, MSGNO_S
*, int, CmdWhere
);
100 int cmd_pipe(struct pine
*, MSGNO_S
*, int);
101 STORE_S
*list_mgmt_text(RFC2369_S
*, long);
102 void list_mgmt_screen(STORE_S
*);
103 int aggregate_select(struct pine
*, MSGNO_S
*, int, CmdWhere
);
104 int select_by_number(MAILSTREAM
*, MSGNO_S
*, SEARCHSET
**);
105 int select_by_thrd_number(MAILSTREAM
*, MSGNO_S
*, SEARCHSET
**);
106 int select_by_date(MAILSTREAM
*, MSGNO_S
*, long, SEARCHSET
**);
107 int select_by_text(MAILSTREAM
*, MSGNO_S
*, long, SEARCHSET
**);
108 int select_by_size(MAILSTREAM
*, SEARCHSET
**);
109 SEARCHSET
*visible_searchset(MAILSTREAM
*, MSGNO_S
*);
110 int select_by_status(MAILSTREAM
*, SEARCHSET
**);
111 int select_by_rule(MAILSTREAM
*, SEARCHSET
**);
112 int select_by_thread(MAILSTREAM
*, MSGNO_S
*, SEARCHSET
**);
113 char *choose_a_rule(int);
114 int select_by_keyword(MAILSTREAM
*, SEARCHSET
**);
115 char *choose_a_keyword(void);
116 int select_sort(struct pine
*, int, SortOrder
*, int *);
117 int print_index(struct pine
*, MSGNO_S
*, int);
120 * List of Select options used by apply_* functions...
122 static char *sel_pmt1
= N_("ALTER message selection : ");
123 ESCKEY_S sel_opts1
[] = {
124 /* TRANSLATORS: these are keymenu names for selecting. Broaden selection means
125 we will add more messages to the selection, Narrow selection means we will
126 remove some selections (like a logical AND instead of logical OR), and Flip
127 Selected means that all the messages that are currently selected become unselected,
128 and all the unselected messages become selected. */
129 {'a', 'a', "A", N_("unselect All")},
130 {'c', 'c', "C", NULL
},
131 {'b', 'b', "B", N_("Broaden selctn")},
132 {'n', 'n', "N", N_("Narrow selctn")},
133 {'f', 'f', "F", N_("Flip selected")},
138 #define SEL_OPTS_THREAD 9 /* index number of "tHread" */
139 #define SEL_OPTS_THREAD_CH 'h'
141 char *sel_pmt2
= "SELECT criteria : ";
142 static ESCKEY_S sel_opts2
[] = {
143 /* TRANSLATORS: very short descriptions of message selection criteria. Select Cur
144 means select the currently highlighted message; select by Number is by message
145 number; Status is by status of the message, for example the message might be
146 New or it might be Unseen or marked Important; Size has the Z upper case because
147 it is a Z command; Keyword is an alpine keyword that has been set by the user;
148 and Rule is an alpine rule */
149 {'a', 'a', "A", N_("select All")},
150 {'c', 'c', "C", N_("select Cur")},
151 {'n', 'n', "N", N_("Number")},
152 {'d', 'd', "D", N_("Date")},
153 {'t', 't', "T", N_("Text")},
154 {'s', 's', "S", N_("Status")},
155 {'z', 'z', "Z", N_("siZe")},
156 {'k', 'k', "K", N_("Keyword")},
157 {'r', 'r', "R", N_("Rule")},
158 {SEL_OPTS_THREAD_CH
, 'h', "H", N_("tHread")},
163 static ESCKEY_S sel_opts3
[] = {
164 /* TRANSLATORS: these are operations we can do on a set of selected messages.
165 Del is Delete; Undel is Undelete; TakeAddr means to Take some Addresses into
166 the address book; Save means to save the messages into another alpine folder;
167 Export means to copy the messages to a file outside of alpine, external to
169 {'d', 'd', "D", N_("Del")},
170 {'u', 'u', "U", N_("Undel")},
171 {'r', 'r', "R", N_("Reply")},
172 {'f', 'f', "F", N_("Forward")},
173 {'%', '%', "%", N_("Print")},
174 {'t', 't', "T", N_("TakeAddr")},
175 {'s', 's', "S", N_("Save")},
176 {'e', 'e', "E", N_("Export")},
177 { -1, 0, NULL
, NULL
},
178 { -1, 0, NULL
, NULL
},
179 { -1, 0, NULL
, NULL
},
180 { -1, 0, NULL
, NULL
},
181 { -1, 0, NULL
, NULL
},
182 { -1, 0, NULL
, NULL
},
183 { -1, 0, NULL
, NULL
},
187 static ESCKEY_S sel_opts4
[] = {
188 {'a', 'a', "A", N_("select All")},
189 /* TRANSLATORS: select currrently highlighted message Thread */
190 {'c', 'c', "C", N_("select Curthrd")},
191 {'n', 'n', "N", N_("Number")},
192 {'d', 'd', "D", N_("Date")},
193 {'t', 't', "T", N_("Text")},
194 {'s', 's', "S", N_("Status")},
195 {'z', 'z', "Z", N_("siZe")},
196 {'k', 'k', "K", N_("Keyword")},
197 {'r', 'r', "R", N_("Rule")},
198 {SEL_OPTS_THREAD_CH
, 'h', "H", N_("tHread")},
203 static char *sel_flag
=
204 N_("Select New, Deleted, Answered, Forwarded, or Important messages ? ");
205 static char *sel_flag_not
=
206 N_("Select NOT New, NOT Deleted, NOT Answered, NOT Forwarded or NOT Important msgs ? ");
207 static ESCKEY_S sel_flag_opt
[] = {
208 /* TRANSLATORS: When selecting messages by message Status these are the
209 different types of Status you can select on. Is the message New, Recent,
210 and so on. Not means flip the meaning of the selection to the opposite
211 thing, so message is not New or not Important. */
212 {'n', 'n', "N", N_("New")},
213 {'*', '*', "*", N_("Important")},
214 {'d', 'd', "D", N_("Deleted")},
215 {'a', 'a', "A", N_("Answered")},
216 {'f', 'f', "F", N_("Forwarded")},
218 {'!', '!', "!", N_("Not")},
220 {'r', 'r', "R", N_("Recent")},
221 {'u', 'u', "U", N_("Unseen")},
226 static ESCKEY_S sel_date_opt
[] = {
228 /* TRANSLATORS: options when selecting messages by Date */
229 {ctrl('P'), 12, "^P", N_("Prev Day")},
230 {ctrl('N'), 13, "^N", N_("Next Day")},
231 {ctrl('X'), 11, "^X", N_("Cur Msg")},
232 {ctrl('W'), 14, "^W", N_("Toggle When")},
233 {KEY_UP
, 12, "", ""},
234 {KEY_DOWN
, 13, "", ""},
239 static char *sel_text
=
240 N_("Select based on To, From, Cc, Recip, Partic, Subject fields or All msg text ? ");
241 static char *sel_text_not
=
242 N_("Select based on NOT To, From, Cc, Recip, Partic, Subject or All msg text ? ");
243 static ESCKEY_S sel_text_opt
[] = {
244 /* TRANSLATORS: Select messages based on the text contained in the From line, or
245 the Subject line, and so on. */
246 {'f', 'f', "F", N_("From")},
247 {'s', 's', "S", N_("Subject")},
248 {'t', 't', "T", N_("To")},
249 {'a', 'a', "A", N_("All Text")},
250 {'c', 'c', "C", N_("Cc")},
251 {'!', '!', "!", N_("Not")},
252 {'r', 'r', "R", N_("Recipient")},
253 {'p', 'p', "P", N_("Participant")},
254 {'b', 'b', "B", N_("Body")},
255 {'h', 'h', "H", N_("Header")},
259 static ESCKEY_S choose_action
[] = {
260 {'c', 'c', "C", N_("Compose")},
261 {'r', 'r', "R", N_("Reply")},
262 {'f', 'f', "F", N_("Forward")},
263 {'b', 'b', "B", N_("Bounce")},
267 static char *select_num
=
268 N_("Enter comma-delimited list of numbers (dash between ranges): ");
270 static char *select_size_larger_msg
=
271 N_("Select messages with size larger than: ");
273 static char *select_size_smaller_msg
=
274 N_("Select messages with size smaller than: ");
276 static char *sel_size_larger
= N_("Larger");
277 static char *sel_size_smaller
= N_("Smaller");
278 static ESCKEY_S sel_size_opt
[] = {
280 {ctrl('W'), 14, "^W", NULL
},
284 static ESCKEY_S sel_key_opt
[] = {
286 {ctrl('T'), 14, "^T", N_("To List")},
288 {'!', '!', "!", N_("Not")},
292 static ESCKEY_S flag_text_opt
[] = {
293 /* TRANSLATORS: these are types of flags (markers) that the user can
294 set. For example, they can flag the message as an important message. */
295 {'n', 'n', "N", N_("New")},
296 {'*', '*', "*", N_("Important")},
297 {'d', 'd', "D", N_("Deleted")},
298 {'a', 'a', "A", N_("Answered")},
299 {'f', 'f', "F", N_("Forwarded")},
300 {'!', '!', "!", N_("Not")},
301 {ctrl('T'), 10, "^T", N_("To Flag Details")},
306 alpine_smime_confirm_save(char *email
)
310 snprintf(prompt
, sizeof(prompt
), _("Save certificate for <%s>"),
311 email
? email
: _("missing address"));
312 return want_to(prompt
, 'n', 'x', NO_HELP
, WT_NORM
) == 'y';
316 alpine_get_password(char *prompt
, char *pass
, size_t len
)
318 int flags
= OE_PASSWD
| OE_DISALLOW_HELP
;
320 return optionally_enter(pass
,
321 -(ps_global
->ttyo
? FOOTER_ROWS(ps_global
) : 3),
322 0, len
, prompt
, NULL
, NO_HELP
, &flags
);
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
, NULL
);
696 /*---------- Forward message ----------*/
698 (void) cmd_forward(state
, msgmap
, MCMD_NONE
, NULL
);
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
, NULL
);
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
, ACTION_S
*role
)
2321 if(any_messages(msgmap
, NULL
, "to Reply to")){
2322 if(MCMD_ISAGG(aopt
) && !pseudo_selected(state
->mail_stream
, msgmap
))
2325 rv
= reply(state
, role
);
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
, ACTION_S
*role
)
2351 if(any_messages(msgmap
, NULL
, "to Forward")){
2352 if(MCMD_ISAGG(aopt
) && !pseudo_selected(state
->mail_stream
, msgmap
))
2355 rv
= forward(state
, role
);
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
, ACTION_S
*role
)
2382 if(any_messages(msgmap
, NULL
, "to Bounce")){
2384 if(MCMD_ISAGG(aopt
)){
2385 if(!pseudo_selected(state
->mail_stream
, msgmap
))
2388 else if((i
= any_lflagged(msgmap
, MN_SLCT
)) > 0
2389 && get_lflag(state
->mail_stream
, msgmap
,
2390 mn_m2raw(msgmap
, mn_get_cur(msgmap
)), MN_SLCT
) == 0)
2391 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
2392 _("WARNING: non-selected message is being bounced!"));
2394 && get_lflag(state
->mail_stream
, msgmap
,
2395 mn_m2raw(msgmap
, mn_get_cur(msgmap
)), MN_SLCT
))
2396 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
2397 _("WARNING: not bouncing all selected messages!"));
2399 rv
= bounce(state
, role
);
2401 if(MCMD_ISAGG(aopt
))
2402 restore_selected(msgmap
);
2404 state
->mangled_footer
= 1;
2411 /*----------------------------------------------------------------------
2412 Execute save message command: prompt for folder and call function to save
2414 Args: screen_line -- Line on the screen to prompt on
2415 message -- The MESSAGECACHE entry of message to save
2417 Result: The folder lister can be called to make selection; mangled screen set
2419 This does the prompting for the folder name to save to, possibly calling
2420 up the folder display for selection of folder by user.
2423 cmd_save(struct pine
*state
, MAILSTREAM
*stream
, MSGNO_S
*msgmap
, int aopt
, CmdWhere in_index
)
2425 char newfolder
[MAILTMPLEN
], nmsgs
[32], *nick
;
2426 int we_cancel
= 0, rv
= 0, save_flags
;
2428 CONTEXT_S
*cntxt
= NULL
;
2430 SaveDel del
= DontAsk
;
2431 SavePreserveOrder pre
= DontAskPreserve
;
2433 dprint((4, "\n - saving message -\n"));
2435 if(MCMD_ISAGG(aopt
) && !pseudo_selected(stream
, msgmap
))
2438 state
->ugly_consider_advancing_bit
= 0;
2439 if(F_OFF(F_SAVE_PARTIAL_WO_CONFIRM
, state
)
2440 && msgno_any_deletedparts(stream
, msgmap
)
2441 && want_to(_("Saved copy will NOT include entire message! Continue"),
2442 'y', 'n', NO_HELP
, WT_FLUSH_IN
| WT_SEQ_SENSITIVE
) != 'y'){
2443 restore_selected(msgmap
);
2444 cmd_cancelled("Save message");
2448 raw
= mn_m2raw(msgmap
, mn_get_cur(msgmap
));
2450 if(mn_total_cur(msgmap
) <= 1L){
2451 snprintf(nmsgs
, sizeof(nmsgs
), "Msg #%ld ", mn_get_cur(msgmap
));
2452 nmsgs
[sizeof(nmsgs
)-1] = '\0';
2453 e
= pine_mail_fetchstructure(stream
, raw
, NULL
);
2455 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
2456 _("Can't save message. Error accessing folder"));
2457 restore_selected(msgmap
);
2462 snprintf(nmsgs
, sizeof(nmsgs
), "%s msgs ", comatose(mn_total_cur(msgmap
)));
2463 nmsgs
[sizeof(nmsgs
)-1] = '\0';
2465 /* e is just used to get a default save folder from the first msg */
2466 e
= pine_mail_fetchstructure(stream
,
2467 mn_m2raw(msgmap
, mn_first_cur(msgmap
)),
2471 del
= (!READONLY_FOLDER(stream
) && F_OFF(F_SAVE_WONT_DELETE
, ps_global
))
2473 if(mn_total_cur(msgmap
) > 1L)
2474 pre
= F_OFF(F_AGG_SEQ_COPY
, ps_global
) ? Preserve
: NoPreserve
;
2476 pre
= DontAskPreserve
;
2478 if(save_prompt(state
, &cntxt
, newfolder
, sizeof(newfolder
), nmsgs
, e
,
2479 raw
, NULL
, &del
, &pre
)){
2481 if(ps_global
&& ps_global
->ttyo
){
2482 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
2483 ps_global
->mangled_footer
= 1;
2486 save_flags
= SV_FIX_DELS
;
2487 if(pre
== RetPreserve
)
2488 save_flags
|= SV_PRESERVE
;
2490 save_flags
|= SV_DELETE
;
2491 if(ps_global
->context_list
== cntxt
&& !strucmp(newfolder
, ps_global
->inbox_name
))
2492 save_flags
|= SV_INBOXWOCNTXT
;
2494 we_cancel
= busy_cue(_("Saving"), NULL
, 1);
2495 i
= save(state
, stream
, cntxt
, newfolder
, msgmap
, save_flags
);
2499 if(i
== mn_total_cur(msgmap
)){
2501 if(mn_total_cur(msgmap
) <= 1L){
2502 int need
, avail
= ps_global
->ttyo
->screen_cols
- 2;
2503 int lennick
, lenfldr
;
2506 && ps_global
->context_list
->next
2507 && context_isambig(newfolder
)){
2508 lennick
= MIN(strlen(cntxt
->nickname
), 500);
2509 lenfldr
= MIN(strlen(newfolder
), 500);
2510 need
= 27 + strlen(long2string(mn_get_cur(msgmap
))) +
2514 need
-= MIN(lennick
-10, need
-avail
);
2515 lennick
-= MIN(lennick
-10, need
-avail
);
2518 if(need
> avail
&& lenfldr
> 10)
2519 lenfldr
-= MIN(lenfldr
-10, need
-avail
);
2522 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,
2523 "Message %s copied to \"%s\" in <%s>",
2524 long2string(mn_get_cur(msgmap
)),
2525 short_str(newfolder
, (char *)(tmp_20k_buf
+1000), 1000,
2527 short_str(cntxt
->nickname
,
2528 (char *)(tmp_20k_buf
+2000), 1000,
2530 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
2532 else if((nick
=folder_is_target_of_nick(newfolder
, cntxt
)) != NULL
){
2533 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,
2534 "Message %s copied to \"%s\"",
2535 long2string(mn_get_cur(msgmap
)),
2537 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
2540 char *f
= " folder";
2542 lenfldr
= MIN(strlen(newfolder
), 500);
2543 need
= 28 + strlen(long2string(mn_get_cur(msgmap
))) +
2548 if(need
> avail
&& lenfldr
> 10)
2549 lenfldr
-= MIN(lenfldr
-10, need
-avail
);
2552 snprintf(tmp_20k_buf
,SIZEOF_20KBUF
,
2553 "Message %s copied to%s \"%s\"",
2554 long2string(mn_get_cur(msgmap
)), f
,
2555 short_str(newfolder
, (char *)(tmp_20k_buf
+1000), 1000,
2557 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
2561 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s messages saved",
2562 comatose(mn_total_cur(msgmap
)));
2563 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
2567 strncat(tmp_20k_buf
, " and deleted", SIZEOF_20KBUF
-strlen(tmp_20k_buf
)-1);
2568 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
2571 q_status_message(SM_ORDER
, 0, 3, tmp_20k_buf
);
2573 if(!MCMD_ISAGG(aopt
) && F_ON(F_SAVE_ADVANCES
, state
)){
2574 if(sp_new_mail_count(stream
))
2575 process_filter_patterns(stream
, msgmap
,
2576 sp_new_mail_count(stream
));
2578 mn_inc_cur(stream
, msgmap
,
2579 (in_index
== View
&& THREADING()
2580 && sp_viewing_a_thread(stream
))
2582 : (in_index
== View
)
2583 ? MH_ANYTHD
: MH_NONE
);
2586 state
->ugly_consider_advancing_bit
= 1;
2590 if(MCMD_ISAGG(aopt
)) /* straighten out fakes */
2591 restore_selected(msgmap
);
2594 update_titlebar_status(); /* make sure they see change */
2601 role_compose(struct pine
*state
)
2605 if(F_ON(F_ALT_ROLE_MENU
, state
) && mn_get_total(state
->msgmap
) > 0L){
2608 if(nonempty_patterns(ROLE_DO_ROLES
, &pstate
) && first_pattern(&pstate
)){
2609 action
= radio_buttons(_("Compose, Forward, Reply, or Bounce? "),
2610 -FOOTER_ROWS(state
), choose_action
,
2611 'c', 'x', h_role_compose
, RB_NORM
);
2614 q_status_message(SM_ORDER
, 0, 3,
2615 _("No roles available. Use Setup/Rules to add roles."));
2622 if(action
== 'c' || action
== 'r' || action
== 'f' || action
== 'b'){
2623 ACTION_S
*role
= NULL
;
2624 void (*prev_screen
)(struct pine
*) = NULL
, (*redraw
)(void) = NULL
;
2626 redraw
= state
->redrawer
;
2627 state
->redrawer
= NULL
;
2628 prev_screen
= state
->prev_screen
;
2630 state
->next_screen
= SCREEN_FUN_NULL
;
2633 if(role_select_screen(state
, &role
,
2634 action
== 'f' ? MC_FORWARD
:
2635 action
== 'r' ? MC_REPLY
:
2636 action
== 'b' ? MC_BOUNCE
:
2637 action
== 'c' ? MC_COMPOSE
: 0) < 0){
2638 cmd_cancelled(action
== 'f' ? _("Forward") :
2639 action
== 'r' ? _("Reply") :
2640 action
== 'c' ? _("Composition") : _("Bounce"));
2641 state
->next_screen
= prev_screen
;
2642 state
->redrawer
= redraw
;
2643 state
->mangled_screen
= 1;
2647 * If default role was selected (NULL) we need to make
2648 * up a role which won't do anything, but will cause
2649 * compose_mail to think there's already a role so that
2650 * it won't try to confirm the default.
2653 role
= combine_inherited_role(role
);
2655 role
= (ACTION_S
*) fs_get(sizeof(*role
));
2656 memset((void *) role
, 0, sizeof(*role
));
2657 role
->nick
= cpystr("Default Role");
2660 state
->redrawer
= NULL
;
2663 compose_mail(NULL
, NULL
, role
, NULL
, NULL
);
2667 (void) reply(state
, role
);
2671 (void) forward(state
, role
);
2675 (void) bounce(state
, role
);
2682 state
->next_screen
= prev_screen
;
2683 state
->redrawer
= redraw
;
2684 state
->mangled_screen
= 1;
2690 /*----------------------------------------------------------------------
2691 Do the dirty work of prompting the user for a folder name
2694 nfldr should be a buffer at least MAILTMPLEN long
2695 dela -- a pointer to a SaveDel. If it is
2696 DontAsk on input, don't offer Delete prompt
2697 Del on input, offer Delete command with default of Delete
2699 RetDel and RetNoDel are return values
2706 save_prompt(struct pine
*state
, CONTEXT_S
**cntxt
, char *nfldr
, size_t len_nfldr
,
2707 char *nmsgs
, ENVELOPE
*env
, long int rawmsgno
, char *section
,
2708 SaveDel
*dela
, SavePreserveOrder
*prea
)
2710 int rc
, ku
= -1, n
, flags
, last_rc
= 0, saveable_count
= 0, done
= 0;
2711 int delindex
, preindex
, r
;
2712 char prompt
[6*MAX_SCREEN_COLS
+1], *p
, expanded
[MAILTMPLEN
];
2713 char *buf
= tmp_20k_buf
;
2717 SaveDel del
= DontAsk
;
2718 SavePreserveOrder pre
= DontAskPreserve
;
2719 char *deltext
= NULL
;
2720 static HISTORY_S
*history
= NULL
;
2725 alpine_panic("no context ptr in save_prompt");
2727 init_hist(&history
, HISTSIZE
);
2729 if(!(folder
= save_get_default(state
, env
, rawmsgno
, section
, cntxt
)))
2730 return(0); /* message expunged! */
2732 /* how many context's can be saved to... */
2733 for(tc
= state
->context_list
; tc
; tc
= tc
->next
)
2737 /* set up extra command option keys */
2739 ekey
[rc
].ch
= ctrl('T');
2741 ekey
[rc
].name
= "^T";
2742 /* TRANSLATORS: command means go to Folders list */
2743 ekey
[rc
++].label
= N_("To Fldrs");
2745 if(saveable_count
> 1){
2746 ekey
[rc
].ch
= ctrl('P');
2748 ekey
[rc
].name
= "^P";
2749 ekey
[rc
++].label
= N_("Prev Collection");
2751 ekey
[rc
].ch
= ctrl('N');
2753 ekey
[rc
].name
= "^N";
2754 ekey
[rc
++].label
= N_("Next Collection");
2757 if(F_ON(F_ENABLE_TAB_COMPLETE
, ps_global
)){
2760 ekey
[rc
].name
= "TAB";
2761 /* TRANSLATORS: command asks alpine to complete the name when tab is typed */
2762 ekey
[rc
++].label
= N_("Complete");
2765 if(F_ON(F_ENABLE_SUB_LISTS
, ps_global
)){
2766 ekey
[rc
].ch
= ctrl('X');
2768 ekey
[rc
].name
= "^X";
2769 /* TRANSLATORS: list all the matches */
2770 ekey
[rc
++].label
= N_("ListMatches");
2773 if(dela
&& (*dela
== NoDel
|| *dela
== Del
)){
2774 ekey
[rc
].ch
= ctrl('R');
2776 ekey
[rc
].name
= "^R";
2781 if(prea
&& (*prea
== NoPreserve
|| *prea
== Preserve
)){
2782 ekey
[rc
].ch
= ctrl('W');
2784 ekey
[rc
].name
= "^W";
2789 if(saveable_count
> 1 && F_ON(F_DISABLE_SAVE_INPUT_HISTORY
, ps_global
)){
2790 ekey
[rc
].ch
= KEY_UP
;
2793 ekey
[rc
++].label
= "";
2795 ekey
[rc
].ch
= KEY_DOWN
;
2798 ekey
[rc
++].label
= "";
2800 else if(F_OFF(F_DISABLE_SAVE_INPUT_HISTORY
, ps_global
)){
2801 ekey
[rc
].ch
= KEY_UP
;
2805 ekey
[rc
++].label
= "";
2807 ekey
[rc
].ch
= KEY_DOWN
;
2810 ekey
[rc
++].label
= "";
2818 /* only show collection number if more than one available */
2819 if(ps_global
->context_list
->next
)
2820 snprintf(prompt
, sizeof(prompt
), "SAVE%s %sto folder in <%s> [%s] : ",
2821 deltext
? deltext
: "",
2823 short_str((*cntxt
)->nickname
, shortbuf
, sizeof(shortbuf
), 16, EndDots
),
2824 strsquish(buf
, SIZEOF_20KBUF
, folder
, 25));
2826 snprintf(prompt
, sizeof(prompt
), "SAVE%s %sto folder [%s] : ",
2827 deltext
? deltext
: "",
2828 nmsgs
, strsquish(buf
, SIZEOF_20KBUF
, folder
, 40));
2830 prompt
[sizeof(prompt
)-1] = '\0';
2833 * If the prompt won't fit, try removing deltext.
2835 if(state
->ttyo
->screen_cols
< strlen(prompt
) + MIN_OPT_ENT_WIDTH
&& deltext
){
2836 if(ps_global
->context_list
->next
)
2837 snprintf(prompt
, sizeof(prompt
), "SAVE %sto folder in <%s> [%s] : ",
2839 short_str((*cntxt
)->nickname
, shortbuf
, sizeof(shortbuf
), 16, EndDots
),
2840 strsquish(buf
, SIZEOF_20KBUF
, folder
, 25));
2842 snprintf(prompt
, sizeof(prompt
), "SAVE %sto folder [%s] : ",
2843 nmsgs
, strsquish(buf
, SIZEOF_20KBUF
, folder
, 40));
2845 prompt
[sizeof(prompt
)-1] = '\0';
2849 * If the prompt still won't fit, remove the extra info contained
2852 if(state
->ttyo
->screen_cols
< strlen(prompt
) + MIN_OPT_ENT_WIDTH
&& *nmsgs
){
2853 if(ps_global
->context_list
->next
)
2854 snprintf(prompt
, sizeof(prompt
), "SAVE to folder in <%s> [%s] : ",
2855 short_str((*cntxt
)->nickname
, shortbuf
, sizeof(shortbuf
), 16, EndDots
),
2856 strsquish(buf
, SIZEOF_20KBUF
, folder
, 25));
2858 snprintf(prompt
, sizeof(prompt
), "SAVE to folder [%s] : ",
2859 strsquish(buf
, SIZEOF_20KBUF
, folder
, 25));
2861 prompt
[sizeof(prompt
)-1] = '\0';
2865 ekey
[delindex
].label
= (del
== NoDel
) ? "Delete" : "No Delete";
2867 if(pre
!= DontAskPreserve
)
2868 ekey
[preindex
].label
= (pre
== NoPreserve
) ? "Preserve Order" : "Any Order";
2871 if(items_in_hist(history
) > 1){
2872 ekey
[ku
].name
= HISTORY_UP_KEYNAME
;
2873 ekey
[ku
].label
= HISTORY_KEYLABEL
;
2874 ekey
[ku
+1].name
= HISTORY_DOWN_KEYNAME
;
2875 ekey
[ku
+1].label
= HISTORY_KEYLABEL
;
2879 ekey
[ku
].label
= "";
2880 ekey
[ku
+1].name
= "";
2881 ekey
[ku
+1].label
= "";
2885 flags
= OE_APPEND_CURRENT
| OE_SEQ_SENSITIVE
;
2886 rc
= optionally_enter(nfldr
, -FOOTER_ROWS(state
), 0, len_nfldr
,
2887 prompt
, ekey
, help
, &flags
);
2891 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
2892 _("Error reading folder name"));
2897 removing_trailing_white_space(nfldr
);
2898 removing_leading_white_space(nfldr
);
2900 if(*nfldr
|| *folder
){
2901 char *p
, *name
, *fullname
= NULL
;
2902 int exists
, breakout
= FALSE
;
2905 strncpy(nfldr
, folder
, len_nfldr
-1);
2906 nfldr
[len_nfldr
-1] = '\0';
2909 save_hist(history
, nfldr
, 0, (void *) *cntxt
);
2911 if(!(name
= folder_is_nick(nfldr
, FOLDERS(*cntxt
), 0)))
2914 if(update_folder_spec(expanded
, sizeof(expanded
), name
)){
2915 strncpy(name
= nfldr
, expanded
, len_nfldr
-1);
2916 nfldr
[len_nfldr
-1] = '\0';
2919 exists
= folder_name_exists(*cntxt
, name
, &fullname
);
2921 if(exists
== FEX_ERROR
){
2922 q_status_message1(SM_ORDER
, 0, 3,
2923 _("Problem accessing folder \"%s\""),
2929 strncpy(name
= nfldr
, fullname
, len_nfldr
-1);
2930 nfldr
[len_nfldr
-1] = '\0';
2931 fs_give((void **) &fullname
);
2935 if(exists
& FEX_ISFILE
){
2938 else if((exists
& FEX_ISDIR
)){
2939 char tmp
[MAILTMPLEN
];
2943 CONTEXT_S
*fake_context
;
2946 strncpy(tmp
, name
, sizeof(tmp
));
2947 tmp
[sizeof(tmp
)-2-1] = '\0';
2948 if(tmp
[(l
= strlen(tmp
)) - 1] != tc
->dir
->delim
){
2949 if(l
< sizeof(tmp
)){
2950 tmp
[l
] = tc
->dir
->delim
;
2951 strncpy(&tmp
[l
+1], "[]", sizeof(tmp
)-(l
+1));
2955 strncat(tmp
, "[]", sizeof(tmp
)-strlen(tmp
)-1);
2957 tmp
[sizeof(tmp
)-1] = '\0';
2959 fake_context
= new_context(tmp
, 0);
2961 done
= display_folder_list(&fake_context
, nfldr
,
2962 1, folders_for_save
);
2963 free_context(&fake_context
);
2965 else if(tc
->dir
->delim
2966 && (p
= strrindex(name
, tc
->dir
->delim
))
2968 done
= display_folder_list(cntxt
, nfldr
,
2969 1, folders_for_save
);
2971 q_status_message1(SM_ORDER
, 3, 3,
2972 _("\"%s\" is a directory"), name
);
2974 && !((p
=strrindex(name
, tc
->dir
->delim
)) && *(p
+1) == '\0')){
2975 strncpy(tmp
, name
, sizeof(tmp
));
2976 tmp
[sizeof(tmp
)-1] = '\0';
2977 snprintf(nfldr
, len_nfldr
, "%s%c", tmp
, tc
->dir
->delim
);
2981 else{ /* Doesn't exist, create! */
2982 if((fullname
= folder_as_breakout(*cntxt
, name
)) != NULL
){
2983 strncpy(name
= nfldr
, fullname
, len_nfldr
-1);
2984 nfldr
[len_nfldr
-1] = '\0';
2985 fs_give((void **) &fullname
);
2988 switch(create_for_save(*cntxt
, name
)){
2989 case 1 : /* success */
2992 case 0 : /* error */
2993 case -1 : /* declined */
3002 /* else fall thru like they cancelled */
3005 cmd_cancelled("Save message");
3010 r
= display_folder_list(cntxt
, nfldr
, 0, folders_for_save
);
3018 helper(h_save
, _("HELP FOR SAVE"), HLPD_SIMPLE
);
3019 ps_global
->mangled_screen
= 1;
3022 case 4 : /* redraw */
3025 case 10 : /* previous collection */
3026 for(tc
= (*cntxt
)->prev
; tc
; tc
= tc
->prev
)
3033 for(tc2
= (tc
= (*cntxt
))->next
; tc2
; tc2
= tc2
->next
)
3041 case 11 : /* next collection */
3045 if(((*cntxt
) = (*cntxt
)->next
) == NULL
)
3046 (*cntxt
) = ps_global
->context_list
;
3047 while(NEWS_TEST(*cntxt
) && (*cntxt
) != tc
);
3050 case 12 : /* file name completion */
3051 if(!folder_complete(*cntxt
, nfldr
, len_nfldr
, &n
)){
3052 if(n
&& last_rc
== 12 && !(flags
& OE_USER_MODIFIED
)){
3053 r
= display_folder_list(cntxt
, nfldr
, 1, folders_for_save
);
3055 done
++; /* bingo! */
3057 rc
= 0; /* burn last_rc */
3065 case 14 : /* file name completion */
3066 r
= display_folder_list(cntxt
, nfldr
, 2, folders_for_save
);
3068 done
++; /* bingo! */
3070 rc
= 0; /* burn last_rc */
3074 case 15 : /* Delete / No Delete */
3075 del
= (del
== NoDel
) ? Del
: NoDel
;
3076 deltext
= (del
== NoDel
) ? " (no delete)" : " (and delete)";
3079 case 16 : /* Preserve Order or not */
3080 pre
= (pre
== NoPreserve
) ? Preserve
: NoPreserve
;
3084 if((p
= get_prev_hist(history
, nfldr
, 0, (void *) *cntxt
)) != NULL
){
3085 strncpy(nfldr
, p
, len_nfldr
);
3086 nfldr
[len_nfldr
-1] = '\0';
3087 if(history
->hist
[history
->curindex
])
3088 *cntxt
= (CONTEXT_S
*) history
->hist
[history
->curindex
]->cntxt
;
3096 if((p
= get_next_hist(history
, nfldr
, 0, (void *) *cntxt
)) != NULL
){
3097 strncpy(nfldr
, p
, len_nfldr
);
3098 nfldr
[len_nfldr
-1] = '\0';
3099 if(history
->hist
[history
->curindex
])
3100 *cntxt
= (CONTEXT_S
*) history
->hist
[history
->curindex
]->cntxt
;
3108 alpine_panic("Unhandled case");
3115 ps_global
->mangled_footer
= 1;
3121 strncpy(ps_global
->last_save_folder
, nfldr
, sizeof(ps_global
->last_save_folder
)-1);
3122 ps_global
->last_save_folder
[sizeof(ps_global
->last_save_folder
)-1] = '\0';
3124 ps_global
->last_save_context
= *cntxt
;
3127 strncpy(nfldr
, folder
, len_nfldr
-1);
3128 nfldr
[len_nfldr
-1] = '\0';
3131 /* nickname? Copy real name to nfldr */
3133 && context_isambig(nfldr
)
3134 && (p
= folder_is_nick(nfldr
, FOLDERS(*cntxt
), 0))){
3135 strncpy(nfldr
, p
, len_nfldr
-1);
3136 nfldr
[len_nfldr
-1] = '\0';
3139 if(dela
&& (*dela
== NoDel
|| *dela
== Del
))
3140 *dela
= (del
== NoDel
) ? RetNoDel
: RetDel
;
3142 if(prea
&& (*prea
== NoPreserve
|| *prea
== Preserve
))
3143 *prea
= (pre
== NoPreserve
) ? RetNoPreserve
: RetPreserve
;
3149 /*----------------------------------------------------------------------
3150 Prompt user before implicitly creating a folder for saving
3152 Args: context - context to create folder in
3153 folder - folder name to create
3155 Result: 1 on proceed, -1 on decline, 0 on error
3159 create_for_save_prompt(CONTEXT_S
*context
, char *folder
, int sequence_sensitive
)
3161 if(context
&& ps_global
->context_list
->next
&& context_isambig(folder
)){
3162 if(context
->use
& CNTXT_INCMNG
){
3163 snprintf(tmp_20k_buf
,SIZEOF_20KBUF
,
3164 _("\"%.15s%s\" doesn't exist - Add it in FOLDER LIST screen"),
3165 folder
, (strlen(folder
) > 15) ? "..." : "");
3166 q_status_message(SM_ORDER
, 3, 3, tmp_20k_buf
);
3167 return(0); /* error */
3170 snprintf(tmp_20k_buf
,SIZEOF_20KBUF
,
3171 _("Folder \"%.15s%s\" in <%.15s%s> doesn't exist. Create"),
3172 folder
, (strlen(folder
) > 15) ? "..." : "",
3174 (strlen(context
->nickname
) > 15) ? "..." : "");
3177 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,
3178 _("Folder \"%.40s%s\" doesn't exist. Create"),
3179 folder
, strlen(folder
) > 40 ? "..." : "");
3181 if(want_to(tmp_20k_buf
, 'y', 'n',
3182 NO_HELP
, (sequence_sensitive
) ? WT_SEQ_SENSITIVE
: WT_NORM
) != 'y'){
3183 cmd_cancelled("Save message");
3192 /*----------------------------------------------------------------------
3193 Expunge messages from current folder
3195 Args: state -- pointer to struct holding a bunch of pine state
3196 msgmap -- table mapping msg nums to c-client sequence nums
3197 qline -- screen line to ask questions on
3198 agg -- boolean indicating we're to operate on aggregate set
3203 cmd_expunge(struct pine
*state
, MAILSTREAM
*stream
, MSGNO_S
*msgmap
, int agg
)
3205 long del_count
, prefilter_del_count
;
3206 int we_cancel
= 0, rv
= 0;
3207 char prompt
[MAX_SCREEN_COLS
+1];
3209 COLOR_PAIR
*lastc
= NULL
;
3211 dprint((2, "\n - expunge -\n"));
3215 sequence
= MCMD_ISAGG(agg
) ? selected_sequence(stream
, msgmap
, NULL
, 0) : NULL
;
3217 if(MCMD_ISAGG(agg
)){
3220 for(i
= 1L; i
<= stream
->nmsgs
; i
++){
3221 if((mc
= mail_elt(stream
, i
)) != NULL
3222 && mc
->sequence
&& mc
->deleted
)
3226 q_status_message(SM_ORDER
, 0, 4,
3227 _("No selected messages are deleted"));
3231 if(!any_messages(msgmap
, NULL
, "to Expunge"))
3235 if(IS_NEWS(stream
) && stream
->rdonly
){
3236 if(!MCMD_ISAGG(agg
))
3237 del_count
= count_flagged(stream
, F_DEL
);
3239 state
->mangled_footer
= 1; /* MAX_SCREEN_COLS+1 = sizeof(prompt) */
3240 snprintf(prompt
, sizeof(prompt
), "Exclude %ld message%s from %.*s", del_count
,
3241 plural(del_count
), MAX_SCREEN_COLS
+1-40,
3242 pretty_fn(state
->cur_folder
));
3243 prompt
[sizeof(prompt
)-1] = '\0';
3244 if(F_ON(F_FULL_AUTO_EXPUNGE
, state
)
3245 || (F_ON(F_AUTO_EXPUNGE
, state
)
3246 && (state
->context_current
3247 && (state
->context_current
->use
& CNTXT_INCMNG
))
3248 && context_isambig(state
->cur_folder
))
3249 || want_to(prompt
, 'y', 0, NO_HELP
, WT_NORM
) == 'y'){
3251 if(F_ON(F_NEWS_CROSS_DELETE
, state
))
3252 cross_delete_crossposts(stream
);
3254 msgno_exclude_deleted(stream
, msgmap
, sequence
);
3255 clear_index_cache(stream
, 0);
3258 * This is kind of surprising at first. For most sort
3259 * orders, if the whole set is sorted, then any subset
3260 * is also sorted. Not so for threaded sorts.
3262 if(SORT_IS_THREADED(msgmap
))
3263 refresh_sort(stream
, msgmap
, SRT_NON
);
3265 state
->mangled_body
= 1;
3266 state
->mangled_header
= 1;
3267 q_status_message2(SM_ORDER
, 0, 4,
3268 "%s message%s excluded",
3269 long2string(del_count
),
3273 any_messages(NULL
, NULL
, "Excluded");
3276 any_messages(NULL
, "deleted", "to Exclude");
3280 else if(READONLY_FOLDER(stream
)){
3281 q_status_message(SM_ORDER
, 0, 4,
3282 _("Can't expunge. Folder is read-only"));
3286 if(!MCMD_ISAGG(agg
)){
3287 prefilter_del_count
= count_flagged(stream
, F_DEL
|F_NOFILT
);
3288 mail_expunge_prefilter(stream
, MI_NONE
);
3289 del_count
= count_flagged(stream
, F_DEL
|F_NOFILT
);
3294 unsigned char *fname
= folder_name_decoded((unsigned char *)state
->cur_folder
);
3295 /* MAX_SCREEN_COLS+1 = sizeof(prompt) */
3296 snprintf(prompt
, sizeof(prompt
), "Expunge %ld message%s from %.*s", del_count
,
3297 plural(del_count
), MAX_SCREEN_COLS
+1-40,
3298 pretty_fn((char *) fname
));
3299 if(fname
) fs_give((void **)&fname
);
3300 prompt
[sizeof(prompt
)-1] = '\0';
3301 state
->mangled_footer
= 1;
3303 if(F_ON(F_FULL_AUTO_EXPUNGE
, state
)
3304 || (F_ON(F_AUTO_EXPUNGE
, state
)
3305 && ((!strucmp(state
->cur_folder
,state
->inbox_name
))
3306 || (state
->context_current
->use
& CNTXT_INCMNG
))
3307 && context_isambig(state
->cur_folder
))
3308 || (ret
=want_to(prompt
, 'y', 0, NO_HELP
, WT_NORM
)) == 'y')
3312 cmd_cancelled("Expunge");
3318 dprint((8, "Expunge max:%ld cur:%ld kill:%d\n",
3319 mn_get_total(msgmap
), mn_get_cur(msgmap
), del_count
));
3321 lastc
= pico_set_colors(state
->VAR_TITLE_FORE_COLOR
,
3322 state
->VAR_TITLE_BACK_COLOR
,
3325 PutLine0(0, 0, "**"); /* indicate delay */
3328 (void)pico_set_colorp(lastc
, PSC_NONE
);
3329 free_color_pair(&lastc
);
3332 MoveCursor(state
->ttyo
->screen_rows
-FOOTER_ROWS(state
), 0);
3335 we_cancel
= busy_cue(_("Expunging"), NULL
, 1);
3337 if(cmd_expunge_work(stream
, msgmap
, sequence
))
3338 state
->mangled_body
= 1;
3341 fs_give((void **)&sequence
);
3344 cancel_busy_cue((sp_expunge_count(stream
) > 0) ? 0 : -1);
3346 lastc
= pico_set_colors(state
->VAR_TITLE_FORE_COLOR
,
3347 state
->VAR_TITLE_BACK_COLOR
,
3349 PutLine0(0, 0, " "); /* indicate delay's over */
3352 (void)pico_set_colorp(lastc
, PSC_NONE
);
3353 free_color_pair(&lastc
);
3358 if(sp_expunge_count(stream
) > 0){
3360 * This is kind of surprising at first. For most sort
3361 * orders, if the whole set is sorted, then any subset
3362 * is also sorted. Not so for threaded sorts.
3364 if(SORT_IS_THREADED(msgmap
))
3365 refresh_sort(stream
, msgmap
, SRT_NON
);
3369 unsigned char *fname
= folder_name_decoded((unsigned char *)state
->cur_folder
);
3370 q_status_message1(SM_ORDER
, 0, 3,
3371 _("No messages expunged from folder \"%s\""),
3372 pretty_fn((char *) fname
));
3373 if(fname
) fs_give((void **)&fname
);
3375 else if(!prefilter_del_count
)
3376 q_status_message(SM_ORDER
, 0, 3,
3377 _("No messages marked deleted. No messages expunged."));
3383 /*----------------------------------------------------------------------
3384 Expunge_and_close callback to prompt user for confirmation
3386 Args: stream -- folder's stream
3387 folder -- name of folder containing folders
3388 deleted -- number of del'd msgs
3390 Result: 'y' to continue with expunge
3393 expunge_prompt(MAILSTREAM
*stream
, char *folder
, long int deleted
)
3397 char prompt_b
[MAX_SCREEN_COLS
+1], temp
[MAILTMPLEN
+1], buff
[MAX_SCREEN_COLS
+1];
3398 char *short_folder_name
;
3403 snprintf(temp
, sizeof(temp
), "%ld", deleted
);
3404 charcnt
= strlen(temp
)+1;
3407 max_folder
= MAX(1,MAXPROMPT
- (36+charcnt
));
3408 strncpy(temp
, folder
, sizeof(temp
));
3409 temp
[sizeof(temp
)-1] = '\0';
3410 short_folder_name
= short_str(temp
,buff
,sizeof(buff
),max_folder
,FrontDots
);
3413 snprintf(prompt_b
, sizeof(prompt_b
),
3414 "Delete %s%ld message%s from \"%s\"",
3415 (deleted
> 1L) ? "all " : "", deleted
,
3416 plural(deleted
), short_folder_name
);
3418 snprintf(prompt_b
, sizeof(prompt_b
),
3419 "Expunge the %ld deleted message%s from \"%s\"",
3420 deleted
, deleted
== 1 ? "" : "s",
3423 return(want_to(prompt_b
, 'y', 0, NO_HELP
, WT_NORM
));
3428 * This is used with multiple append saves. Call it once before
3429 * the series of appends with SSCP_INIT and once after all are
3430 * done with SSCP_END. In between, it is called automatically
3431 * from save_fetch_append or save_fetch_append_cb when we need
3432 * to ask the user if he or she wants to continue even though
3433 * announced message size doesn't match the actual message size.
3434 * As of 2008-02-29 the gmail IMAP server has these size mismatches
3435 * on a regular basis even though the data is ok.
3438 save_size_changed_prompt(long msgno
, int flags
)
3442 static int remember_the_yes
= 0;
3443 static int possible_corruption
= 0;
3444 static ESCKEY_S save_size_opts
[] = {
3445 {'y', 'y', "Y", "Yes"},
3446 {'n', 'n', "N", "No"},
3447 {'a', 'a', "A", "yes to All"},
3451 if(F_ON(F_IGNORE_SIZE
, ps_global
))
3454 if(flags
& SSCP_INIT
|| flags
& SSCP_END
){
3455 if(flags
& SSCP_END
&& possible_corruption
)
3456 q_status_message(SM_ORDER
, 3, 3, "There is possible data corruption, check the results");
3458 remember_the_yes
= 0;
3459 possible_corruption
= 0;
3460 ps_global
->noshow_error
= 0;
3461 ps_global
->noshow_warn
= 0;
3465 if(remember_the_yes
){
3466 snprintf(prompt
, sizeof(prompt
),
3467 "Message to save shrank! (msg # %ld): Continuing", msgno
);
3468 q_status_message(SM_ORDER
, 0, 3, prompt
);
3469 display_message('x');
3470 return(remember_the_yes
);
3473 snprintf(prompt
, sizeof(prompt
),
3474 "Message to save shrank! (msg # %ld): Continue anyway ? ", msgno
);
3475 ret
= radio_buttons(prompt
, -FOOTER_ROWS(ps_global
), save_size_opts
,
3476 'n', 0, h_save_size_changed
, RB_NORM
);
3480 remember_the_yes
= 'y';
3481 possible_corruption
++;
3482 return(remember_the_yes
);
3485 possible_corruption
++;
3489 possible_corruption
= 0;
3490 ps_global
->noshow_error
= 1;
3491 ps_global
->noshow_warn
= 1;
3499 /*----------------------------------------------------------------------
3500 Expunge_and_close callback that happens once the decision to expunge
3501 and close has been made and before expunging and closing begins
3504 Args: stream -- folder's stream
3505 folder -- name of folder containing folders
3506 deleted -- number of del'd msgs
3508 Result: 'y' to continue with expunge
3511 expunge_and_close_begins(int flags
, char *folder
)
3513 if(!(flags
& EC_NO_CLOSE
)){
3514 unsigned char *fname
= folder_name_decoded((unsigned char *)folder
);
3515 q_status_message1(SM_INFO
, 0, 1, "Closing \"%.200s\"...", (char *) fname
);
3516 flush_status_messages(1);
3517 if(fname
) fs_give((void **)&fname
);
3522 /*----------------------------------------------------------------------
3523 Export a message to a plain file in users home directory
3525 Args: state -- pointer to struct holding a bunch of pine state
3526 msgmap -- table mapping msg nums to c-client sequence nums
3527 qline -- screen line to ask questions on
3528 agg -- boolean indicating we're to operate on aggregate set
3533 cmd_export(struct pine
*state
, MSGNO_S
*msgmap
, int qline
, int aopt
)
3535 char filename
[MAXPATH
+1], full_filename
[MAXPATH
+1], *err
;
3537 int r
, leading_nl
, failure
= 0, orig_errno
, rflags
= GER_NONE
;
3538 int flags
= GE_IS_EXPORT
| GE_SEQ_SENSITIVE
, rv
= 0;
3542 long i
, count
= 0L, start_of_append
, rawno
;
3545 struct variable
*vars
= ps_global
->vars
;
3546 ESCKEY_S export_opts
[5];
3547 static HISTORY_S
*history
= NULL
;
3549 if(ps_global
->restricted
){
3550 q_status_message(SM_ORDER
, 0, 3,
3551 "Alpine demo can't export messages to files");
3555 if(MCMD_ISAGG(aopt
) && !pseudo_selected(state
->mail_stream
, msgmap
))
3558 export_opts
[i
= 0].ch
= ctrl('T');
3559 export_opts
[i
].rval
= 10;
3560 export_opts
[i
].name
= "^T";
3561 export_opts
[i
++].label
= N_("To Files");
3563 #if !defined(DOS) && !defined(MAC) && !defined(OS2)
3564 if(ps_global
->VAR_DOWNLOAD_CMD
&& ps_global
->VAR_DOWNLOAD_CMD
[0]){
3565 export_opts
[i
].ch
= ctrl('V');
3566 export_opts
[i
].rval
= 12;
3567 export_opts
[i
].name
= "^V";
3568 /* TRANSLATORS: this is an abbreviation for Download Messages */
3569 export_opts
[i
++].label
= N_("Downld Msg");
3571 #endif /* !(DOS || MAC) */
3573 if(F_ON(F_ENABLE_TAB_COMPLETE
,ps_global
)){
3574 export_opts
[i
].ch
= ctrl('I');
3575 export_opts
[i
].rval
= 11;
3576 export_opts
[i
].name
= "TAB";
3577 export_opts
[i
++].label
= N_("Complete");
3581 /* Commented out since it's not yet support! */
3582 if(F_ON(F_ENABLE_SUB_LISTS
,ps_global
)){
3583 export_opts
[i
].ch
= ctrl('X');
3584 export_opts
[i
].rval
= 14;
3585 export_opts
[i
].name
= "^X";
3586 export_opts
[i
++].label
= N_("ListMatches");
3591 * If message has attachments, add a toggle that will allow the user
3592 * to save all of the attachments to a single directory, using the
3593 * names provided with the attachments or part names. What we'll do is
3594 * export the message as usual, and then export the attachments into
3595 * a subdirectory that did not exist before. The subdir will be named
3596 * something based on the name of the file being saved to, but a
3599 if(!MCMD_ISAGG(aopt
)
3600 && state
->mail_stream
3601 && (rawno
= mn_m2raw(msgmap
, mn_get_cur(msgmap
))) > 0L
3602 && rawno
<= state
->mail_stream
->nmsgs
3603 && (env
= pine_mail_fetchstructure(state
->mail_stream
, rawno
, &b
))
3605 && b
->type
== TYPEMULTIPART
3607 && strucmp(b
->subtype
, "ALTERNATIVE") != 0){
3610 part
= b
->nested
.part
; /* 1st part */
3611 if(part
&& part
->next
)
3612 flags
|= GE_ALLPARTS
;
3615 export_opts
[i
].ch
= -1;
3618 if(mn_total_cur(msgmap
) <= 1L){
3619 snprintf(nmsgs
, sizeof(nmsgs
), "Msg #%ld", mn_get_cur(msgmap
));
3620 nmsgs
[sizeof(nmsgs
)-1] = '\0';
3623 snprintf(nmsgs
, sizeof(nmsgs
), "%s messages", comatose(mn_total_cur(msgmap
)));
3624 nmsgs
[sizeof(nmsgs
)-1] = '\0';
3627 r
= get_export_filename(state
, filename
, NULL
, full_filename
,
3628 sizeof(filename
), nmsgs
, "EXPORT",
3629 export_opts
, &rflags
, qline
, flags
, &history
);
3634 cmd_cancelled("Export message");
3638 q_status_message1(SM_ORDER
, 0, 2,
3639 _("Can't export to file outside of %s"),
3646 #if !defined(DOS) && !defined(MAC) && !defined(OS2)
3647 else if(r
== 12){ /* Download */
3648 char cmd
[MAXPATH
], *tfp
= NULL
;
3654 if(ps_global
->restricted
){
3655 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
3656 "Download disallowed in restricted mode");
3661 tfp
= temp_nam(NULL
, "pd");
3662 build_updown_cmd(cmd
, sizeof(cmd
), ps_global
->VAR_DOWNLOAD_CMD_PREFIX
,
3663 ps_global
->VAR_DOWNLOAD_CMD
, tfp
);
3664 dprint((1, "Download cmd called: \"%s\"\n", cmd
));
3665 if((so
= so_get(FileStar
, tfp
, WRITE_ACCESS
|OWNER_ONLY
|WRITE_TO_LOCALE
)) != NULL
){
3666 gf_set_so_writec(&pc
, so
);
3668 for(i
= mn_first_cur(msgmap
); i
> 0L; i
= mn_next_cur(msgmap
)){
3669 if(!(state
->mail_stream
3670 && (rawno
= mn_m2raw(msgmap
, i
)) > 0L
3671 && rawno
<= state
->mail_stream
->nmsgs
3672 && (mc
= mail_elt(state
->mail_stream
, rawno
))
3676 if(!(env
= pine_mail_fetchstructure(state
->mail_stream
,
3677 mn_m2raw(msgmap
, i
), &b
))
3678 || !bezerk_delimiter(env
, mc
, pc
, next
++)
3679 || !format_message(mn_m2raw(msgmap
, mn_get_cur(msgmap
)),
3680 env
, b
, NULL
, FM_NEW_MESS
| FM_NOWRAP
, pc
)){
3681 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
3682 err
= "Error writing tempfile for download");
3687 gf_clear_so_writec(so
);
3688 if(so_give(&so
)){ /* close file */
3690 err
= "Error writing tempfile for download";
3694 if((syspipe
= open_system_pipe(cmd
, NULL
, NULL
,
3695 PIPE_USER
| PIPE_RESET
,
3696 0, pipe_callback
, pipe_report_error
)) != NULL
)
3697 (void) close_system_pipe(&syspipe
, NULL
, pipe_callback
);
3699 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
3700 err
= _("Error running download command"));
3704 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
3705 err
= "Error building temp file for download");
3709 fs_give((void **)&tfp
);
3713 q_status_message(SM_ORDER
, 0, 3, _("Download Command Completed"));
3717 #endif /* !(DOS || MAC) */
3720 if(rflags
& GER_APPEND
)
3725 dprint((5, "Opening file \"%s\" for export\n",
3726 full_filename
? full_filename
: "?"));
3728 if(!(store
= so_get(FileStar
, full_filename
, WRITE_ACCESS
|WRITE_TO_LOCALE
))){
3729 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
3730 /* TRANSLATORS: error opening file "<filename>" to export message: <error text> */
3731 _("Error opening file \"%s\" to export message: %s"),
3732 full_filename
, error_description(errno
));
3736 gf_set_so_writec(&pc
, store
);
3739 for(i
= mn_first_cur(msgmap
); i
> 0L; i
= mn_next_cur(msgmap
), count
++){
3740 env
= pine_mail_fetchstructure(state
->mail_stream
, mn_m2raw(msgmap
, i
),
3743 err
= _("Can't export message. Error accessing mail folder");
3748 if(!(state
->mail_stream
3749 && (rawno
= mn_m2raw(msgmap
, i
)) > 0L
3750 && rawno
<= state
->mail_stream
->nmsgs
3751 && (mc
= mail_elt(state
->mail_stream
, rawno
))
3755 start_of_append
= so_tell(store
);
3756 if(!bezerk_delimiter(env
, mc
, pc
, leading_nl
)
3757 || !format_message(mn_m2raw(msgmap
, i
), env
, b
, NULL
,
3758 FM_NEW_MESS
| FM_NOWRAP
, pc
)){
3759 orig_errno
= errno
; /* save incase things are really bad */
3760 failure
= 1; /* pop out of here */
3767 gf_clear_so_writec(store
);
3768 if(so_give(&store
)) /* release storage */
3772 our_truncate(full_filename
, (off_t
)start_of_append
);
3774 dprint((1, "FAILED Export: fetch(%ld): %s\n",
3775 i
, err
? err
: "?"));
3776 q_status_message(SM_ORDER
| SM_DING
, 3, 4, err
);
3779 dprint((1, "FAILED Export: file \"%s\" : %s\n",
3780 full_filename
? full_filename
: "?",
3781 error_description(orig_errno
)));
3782 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
3783 /* TRANSLATORS: Error exporting to <filename>: <error text> */
3784 _("Error exporting to \"%s\" : %s"),
3785 filename
, error_description(orig_errno
));
3789 if(rflags
& GER_ALLPARTS
&& full_filename
[0]){
3790 char dir
[MAXPATH
+1];
3791 char lfile
[MAXPATH
+1];
3792 int ok
= 0, tries
= 0, saved
= 0, errs
= 0, counter
= 2;
3796 * Now we want to save all of the attachments to a subdirectory.
3797 * To make it easier for us and probably easier for the user, and
3798 * to prevent the user from shooting himself in the foot, we
3799 * make a new subdirectory so that we can't possibly step on
3800 * any existing files, and we don't need any interaction with the
3801 * user while saving.
3803 * We'll just use the directory name full_filename.d or if that
3804 * already exists and isn't empty, we'll try adding a suffix to
3805 * that until we get something to use.
3808 if(strlen(full_filename
) + strlen(".d") + 1 > sizeof(dir
)){
3809 q_status_message1(SM_ORDER
| SM_DING
, 3, 4,
3810 _("Can't save attachments, filename too long: %s"),
3816 snprintf(dir
, sizeof(dir
), "%s.d", full_filename
);
3817 dir
[sizeof(dir
)-1] = '\0';
3821 switch(r
= is_writable_dir(dir
)){
3822 case 0: /* exists and is a writable dir */
3824 * We could figure out if it is empty and use it in
3825 * that case, but that sounds like a lot of work, so
3826 * just fall through to default.
3830 if(strlen(full_filename
) + strlen(".d") + 1 +
3831 1 + strlen(long2string((long) tries
)) > sizeof(dir
)){
3832 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
3833 "Problem saving attachments");
3837 snprintf(dir
, sizeof(dir
), "%s.d_%s", full_filename
,
3838 long2string((long) tries
));
3839 dir
[sizeof(dir
)-1] = '\0';
3842 case 3: /* doesn't exist, that's good! */
3843 /* make new directory */
3847 } while(!ok
&& tries
< 1000);
3850 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
3851 _("Problem saving attachments"));
3855 /* create the new directory */
3856 if(our_mkdir(dir
, 0700)){
3857 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
3858 _("Problem saving attachments: %s: %s"), dir
,
3859 error_description(errno
));
3863 if(!(state
->mail_stream
3864 && (rawno
= mn_m2raw(msgmap
, mn_get_cur(msgmap
))) > 0L
3865 && rawno
<= state
->mail_stream
->nmsgs
3866 && (env
=pine_mail_fetchstructure(state
->mail_stream
,rawno
,&b
))
3868 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
3869 _("Problem reading message"));
3873 zero_atmts(state
->atmts
);
3874 describe_mime(b
, "", 1, 1, 0, 0);
3877 if(a
&& a
->description
) /* skip main body part */
3880 for(; a
->description
!= NULL
; a
++){
3881 /* skip over these parts of the message */
3882 if(MIME_MSG_A(a
) || MIME_DGST_A(a
) || MIME_VCARD_A(a
))
3886 (void) get_filename_parameter(lfile
, sizeof(lfile
), a
->body
, NULL
);
3888 if(lfile
[0] == '\0'){ /* MAXPATH + 1 = sizeof(lfile) */
3889 snprintf(lfile
, sizeof(lfile
), "part_%.*s", MAXPATH
+1-6,
3890 a
->number
? a
->number
: "?");
3891 lfile
[sizeof(lfile
)-1] = '\0';
3894 if(strlen(dir
) + strlen(S_FILESEP
) + strlen(lfile
) + 1
3895 > sizeof(filename
)){
3897 "FAILED Att Export: name too long: %s\n",
3898 dir
, S_FILESEP
, lfile
));
3903 /* although files are being saved in a unique directory, there is
3904 * no guarantee that attachment names have unique names, so we have
3905 * to make sure that we are not constantly rewriting the same file name
3906 * over and over. In order to avoid this we test if the file already exists,
3907 * and if so, we write a counter name in the file name, just before the
3908 * extension of the file, and separate it with an underscore.
3910 snprintf(filename
, sizeof(filename
), "%s%s%s", dir
, S_FILESEP
, lfile
);
3911 filename
[sizeof(filename
)-1] = '\0';
3912 while((ok
= can_access(filename
, ACCESS_EXISTS
)) == 0 && errs
== 0){
3914 snprintf(filename
, sizeof(filename
), "%d", counter
);
3915 if(strlen(dir
) + strlen(S_FILESEP
) + strlen(lfile
) + strlen(filename
) + 2
3916 > sizeof(filename
)){
3918 "FAILED Att Export: name too long: %s\n",
3919 dir
, S_FILESEP
, lfile
));
3923 if((ext
= strrchr(lfile
, '.')) != NULL
)
3925 snprintf(filename
, sizeof(filename
), "%s%s%s%s%d%s%s",
3926 dir
, S_FILESEP
, lfile
,
3927 ext
? "_" : "", counter
++, ext
? "." : "", ext
? ext
+1 : "");
3928 filename
[sizeof(filename
)-1] = '\0';
3931 if(write_attachment_to_file(state
->mail_stream
, rawno
,
3932 a
, GER_NONE
, filename
) == 1)
3940 q_status_message1(SM_ORDER
, 3, 3,
3941 "Errors saving some attachments, %s attachments saved",
3942 long2string((long) saved
));
3944 q_status_message(SM_ORDER
, 3, 3,
3945 _("Problems saving attachments"));
3949 q_status_message2(SM_ORDER
, 0, 3,
3950 /* TRANSLATORS: Saved <how many> attachements to <directory name> */
3951 _("Saved %s attachments to %s"),
3952 long2string((long) saved
), dir
);
3954 q_status_message(SM_ORDER
, 3, 3, _("No attachments to save"));
3957 else if(mn_total_cur(msgmap
) > 1L)
3958 q_status_message4(SM_ORDER
,0,3,
3959 "%s message%s %s to file \"%s\"",
3960 long2string(count
), plural(count
),
3963 : rflags
& GER_APPEND
? "appended" : "exported",
3966 q_status_message3(SM_ORDER
,0,3,
3967 "Message %s %s to file \"%s\"",
3968 long2string(mn_get_cur(msgmap
)),
3971 : rflags
& GER_APPEND
? "appended" : "exported",
3977 if(MCMD_ISAGG(aopt
))
3978 restore_selected(msgmap
);
3985 * Ask user what file to export to. Export from srcstore to that file.
3987 * Args ps -- pine struct
3988 * srctext -- pointer to source text
3989 * srctype -- type of that source text
3990 * prompt_msg -- see get_export_filename
3993 * Returns: != 0 : error
3997 simple_export(struct pine
*ps
, void *srctext
, SourceType srctype
, char *prompt_msg
, char *lister_msg
)
3999 int r
= 1, rflags
= GER_NONE
;
4000 char filename
[MAXPATH
+1], full_filename
[MAXPATH
+1];
4001 STORE_S
*store
= NULL
;
4002 struct variable
*vars
= ps
->vars
;
4003 static HISTORY_S
*history
= NULL
;
4004 static ESCKEY_S simple_export_opts
[] = {
4005 {ctrl('T'), 10, "^T", N_("To Files")},
4006 {-1, 0, NULL
, NULL
},
4007 {-1, 0, NULL
, NULL
}};
4009 if(F_ON(F_ENABLE_TAB_COMPLETE
,ps
)){
4010 simple_export_opts
[r
].ch
= ctrl('I');
4011 simple_export_opts
[r
].rval
= 11;
4012 simple_export_opts
[r
].name
= "TAB";
4013 simple_export_opts
[r
].label
= N_("Complete");
4017 q_status_message(SM_ORDER
, 0, 2, _("Error allocating space"));
4022 simple_export_opts
[++r
].ch
= -1;
4024 full_filename
[0] = '\0';
4026 r
= get_export_filename(ps
, filename
, NULL
, full_filename
, sizeof(filename
),
4027 prompt_msg
, lister_msg
, simple_export_opts
, &rflags
,
4028 -FOOTER_ROWS(ps
), GE_IS_EXPORT
, &history
);
4032 else if(!full_filename
[0]){
4037 dprint((5, "Opening file \"%s\" for export\n",
4038 full_filename
? full_filename
: "?"));
4040 if((store
= so_get(FileStar
, full_filename
, WRITE_ACCESS
|WRITE_TO_LOCALE
)) != NULL
){
4044 gf_set_so_writec(&pc
, store
);
4045 gf_set_readc(&gc
, srctext
, (srctype
== CharStar
)
4046 ? strlen((char *)srctext
)
4050 if((pipe_err
= gf_pipe(gc
, pc
)) != NULL
){
4051 q_status_message2(SM_ORDER
| SM_DING
, 3, 3,
4052 /* TRANSLATORS: Problem saving to <filename>: <error text> */
4053 _("Problem saving to \"%s\": %s"),
4054 filename
, pipe_err
);
4060 gf_clear_so_writec(store
);
4061 if(so_give(&store
)){
4062 q_status_message2(SM_ORDER
| SM_DING
, 3, 3,
4063 _("Problem saving to \"%s\": %s"),
4064 filename
, error_description(errno
));
4069 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
4070 _("Error opening file \"%s\" for export: %s"),
4071 full_filename
, error_description(errno
));
4078 /* overloading full_filename */
4079 snprintf(full_filename
, sizeof(full_filename
), "%c%s",
4080 (prompt_msg
&& prompt_msg
[0])
4081 ? (islower((unsigned char)prompt_msg
[0])
4082 ? toupper((unsigned char)prompt_msg
[0]) : prompt_msg
[0])
4084 (prompt_msg
&& prompt_msg
[0]) ? prompt_msg
+1 : "ext");
4085 full_filename
[sizeof(full_filename
)-1] = '\0';
4086 q_status_message3(SM_ORDER
,0,2,"%s %s to \"%s\"",
4090 : rflags
& GER_APPEND
? "appended" : "exported",
4095 cmd_cancelled("Export");
4099 q_status_message1(SM_ORDER
, 0, 2,
4100 _("Can't export to file outside of %s"), VAR_OPER_DIR
);
4104 ps
->mangled_footer
= 1;
4110 * Ask user what file to export to.
4112 * filename -- On input, this is the filename to start with. On exit,
4113 * this is the filename chosen. (but this isn't used)
4114 * deefault -- This is the default value if user hits return. The
4115 * prompt will have [deefault] added to it automatically.
4116 * full_filename -- This is the full filename on exit.
4117 * len -- Minimum length of _both_ filename and full_filename.
4118 * prompt_msg -- Message to insert in prompt.
4119 * lister_msg -- Message to insert in file_lister.
4120 * opts -- Key options.
4121 * There is a tangled relationship between the callers
4122 * and this routine as far as opts are concerned. Some
4123 * of the opts are handled here. In particular, r == 3,
4124 * r == 10, r == 11, and r == 13 are all handled here.
4125 * Don't use those values unless you want what happens
4126 * here. r == 12 and others are handled by the caller.
4127 * rflags -- Return flags
4128 * GER_OVER - overwrite of existing file
4129 * GER_APPEND - append of existing file
4130 * else file did not exist before
4132 * GER_ALLPARTS - AllParts toggle was turned on
4134 * qline -- Command line to prompt on.
4135 * flags -- Logically OR'd flags
4136 * GE_IS_EXPORT - The command was an Export command
4137 * so the prompt should include
4139 * GE_SEQ_SENSITIVE - The command that got us here is
4140 * sensitive to sequence number changes
4141 * caused by unsolicited expunges.
4142 * GE_NO_APPEND - We will not allow append to an
4143 * existing file, only removal of the
4144 * file if it exists.
4145 * GE_IS_IMPORT - We are selecting for reading.
4146 * No overwriting or checking for
4147 * existence at all. Don't use this
4148 * together with GE_NO_APPEND.
4149 * GE_ALLPARTS - Turn on AllParts toggle.
4150 * GE_BINARY - Turn on Binary toggle.
4152 * Returns: -1 cancelled
4153 * -2 prohibited by VAR_OPER_DIR
4154 * -3 other error, already reported here
4156 * 12 user chose 12 command from opts
4159 get_export_filename(struct pine
*ps
, char *filename
, char *deefault
,
4160 char *full_filename
, size_t len
, char *prompt_msg
,
4161 char *lister_msg
, ESCKEY_S
*optsarg
, int *rflags
,
4162 int qline
, int flags
, HISTORY_S
**history
)
4164 char dir
[MAXPATH
+1], dir2
[MAXPATH
+1], orig_dir
[MAXPATH
+1];
4165 char precolon
[MAXPATH
+1], postcolon
[MAXPATH
+1];
4166 char filename2
[MAXPATH
+1], tmp
[MAXPATH
+1], *fn
, *ill
;
4167 int l
, i
, ku
= -1, kp
= -1, r
, fatal
, homedir
= 0, was_abs_path
=0, avail
, ret
= 0;
4168 int allparts
= 0, binary
= 0;
4169 char prompt_buf
[400];
4171 ESCKEY_S
*opts
= NULL
;
4172 struct variable
*vars
= ps
->vars
;
4173 static HISTORY_S
*dir_hist
= NULL
;
4175 int pos
, hist_len
= 0;
4178 /* we will fake a history with the ps_global->VAR_HISTORY variable
4179 * We fake that we combine this variable into a history variable
4180 * by stacking VAR_HISTORY on top of dir_hist. We keep track of this
4181 * by looking at the variable pos.
4183 if(ps_global
->VAR_HISTORY
!= NULL
)
4184 for(hist_len
= 0; ps_global
->VAR_HISTORY
[hist_len
]
4185 && ps_global
->VAR_HISTORY
[hist_len
][0]; hist_len
++)
4188 pos
= hist_len
+ items_in_hist(dir_hist
);
4190 if(flags
& GE_ALLPARTS
|| history
|| dir_hist
){
4192 * Copy the opts and add one to the end of the list.
4194 for(i
= 0; optsarg
[i
].ch
!= -1; i
++)
4197 if(dir_hist
|| hist_len
> 0)
4201 i
+= dir_hist
|| hist_len
> 0 ? 2 : 4;
4203 if(flags
& GE_ALLPARTS
)
4206 if(flags
& GE_BINARY
)
4209 opts
= (ESCKEY_S
*) fs_get((i
+1) * sizeof(*opts
));
4210 memset(opts
, 0, (i
+1) * sizeof(*opts
));
4212 for(i
= 0; optsarg
[i
].ch
!= -1; i
++){
4213 opts
[i
].ch
= optsarg
[i
].ch
;
4214 opts
[i
].rval
= optsarg
[i
].rval
;
4215 opts
[i
].name
= optsarg
[i
].name
; /* no need to make a copy */
4216 opts
[i
].label
= optsarg
[i
].label
; /* " */
4219 if(flags
& GE_ALLPARTS
){
4221 opts
[i
].ch
= ctrl('P');
4223 opts
[i
].name
= "^P";
4224 /* TRANSLATORS: Export all attachment parts */
4225 opts
[i
++].label
= N_("AllParts");
4228 if(flags
& GE_BINARY
){
4230 opts
[i
].ch
= ctrl('R');
4232 opts
[i
].name
= "^R";
4233 opts
[i
++].label
= N_("Binary");
4236 rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
,
4237 SIZEOF_20KBUF
, filename
);
4238 if(strcmp(tmp_20k_buf
, filename
)){
4239 opts
[i
].ch
= ctrl('N');
4241 opts
[i
].name
= "^N";
4242 opts
[i
++].label
= "Name UTF8";
4245 if(dir_hist
|| hist_len
> 0){
4246 opts
[i
].ch
= ctrl('Y');
4250 opts
[i
++].label
= "";
4252 opts
[i
].ch
= ctrl('V');
4255 opts
[i
++].label
= "";
4259 opts
[i
].ch
= KEY_UP
;
4263 opts
[i
++].label
= "";
4265 opts
[i
].ch
= KEY_DOWN
;
4268 opts
[i
++].label
= "";
4274 init_hist(history
, HISTSIZE
);
4275 init_hist(&dir_hist
, HISTSIZE
); /* reset history to the end */
4283 if(F_ON(F_USE_CURRENT_DIR
, ps
))
4285 else if(VAR_OPER_DIR
){
4286 strncpy(dir
, VAR_OPER_DIR
, sizeof(dir
));
4287 dir
[sizeof(dir
)-1] = '\0';
4289 #if defined(DOS) || defined(OS2)
4290 else if(VAR_FILE_DIR
){
4291 strncpy(dir
, VAR_FILE_DIR
, sizeof(dir
));
4292 dir
[sizeof(dir
)-1] = '\0';
4300 strncpy(orig_dir
, dir
, sizeof(orig_dir
));
4301 orig_dir
[sizeof(orig_dir
)-1] = '\0';
4303 postcolon
[0] = '\0';
4304 strncpy(precolon
, dir
, sizeof(precolon
));
4305 precolon
[sizeof(precolon
)-1] = '\0';
4307 strncpy(def
, deefault
, sizeof(def
)-1);
4308 def
[sizeof(def
)-1] = '\0';
4309 removing_leading_and_trailing_white_space(def
);
4314 avail
= MAX(20, ps_global
->ttyo
? ps_global
->ttyo
->screen_cols
: 80) - MIN_OPT_ENT_WIDTH
;
4316 /*---------- Prompt the user for the file name -------------*/
4319 char dirb
[50], fileb
[50];
4320 int l1
, l2
, l3
, l4
, l5
, needed
;
4321 char *p
, p1
[100], p2
[100], *p3
, p4
[100], p5
[100];
4323 snprintf(p1
, sizeof(p1
), "%sCopy ",
4324 (flags
& GE_IS_EXPORT
) ? "EXPORT: " :
4325 (flags
& GE_IS_IMPORT
) ? "IMPORT: " : "SAVE: ");
4326 p1
[sizeof(p1
)-1] = '\0';
4329 strncpy(p2
, prompt_msg
? prompt_msg
: "", sizeof(p2
)-1);
4330 p2
[sizeof(p2
)-1] = '\0';
4333 if(rflags
&& *rflags
& GER_ALLPARTS
)
4340 snprintf(p4
, sizeof(p4
), " %s file%s%s",
4341 (flags
& GE_IS_IMPORT
) ? "from" : "to",
4342 is_absolute_path(filename
) ? "" : " in ",
4343 is_absolute_path(filename
) ? "" :
4344 (!dir
[0] ? "current directory"
4345 : (dir
[0] == '~' && !dir
[1]) ? "home directory"
4346 : short_str(dir
,dirb
,sizeof(dirb
),30,FrontDots
)));
4347 p4
[sizeof(p4
)-1] = '\0';
4350 snprintf(p5
, sizeof(p5
), "%s%s%s: ",
4352 *def
? short_str(def
,fileb
,sizeof(fileb
),40,EndDots
) : "",
4354 p5
[sizeof(p5
)-1] = '\0';
4357 if((needed
= l1
+l2
+l3
+l4
+l5
-avail
) > 0){
4358 snprintf(p4
, sizeof(p4
), " %s file%s%s",
4359 (flags
& GE_IS_IMPORT
) ? "from" : "to",
4360 is_absolute_path(filename
) ? "" : " in ",
4361 is_absolute_path(filename
) ? "" :
4362 (!dir
[0] ? "current dir"
4363 : (dir
[0] == '~' && !dir
[1]) ? "home dir"
4364 : short_str(dir
,dirb
,sizeof(dirb
),10,FrontDots
)));
4365 p4
[sizeof(p4
)-1] = '\0';
4369 if((needed
= l1
+l2
+l3
+l4
+l5
-avail
) > 0 && l5
> 0){
4370 snprintf(p5
, sizeof(p5
), "%s%s%s: ",
4372 *def
? short_str(def
,fileb
,sizeof(fileb
),
4373 MAX(15,l5
-5-needed
),EndDots
) : "",
4375 p5
[sizeof(p5
)-1] = '\0';
4379 if((needed
= l1
+l2
+l3
+l4
+l5
-avail
) > 0 && l2
> 0){
4382 * 14 is about the shortest we can make this, because there are
4383 * fixed length strings of length 14 coming in here.
4385 p
= short_str(prompt_msg
, p2
, sizeof(p2
), MAX(14,l2
-needed
), FrontDots
);
4387 strncpy(p2
, p
, sizeof(p2
)-1);
4388 p2
[sizeof(p2
)-1] = '\0';
4394 if((needed
= l1
+l2
+l3
+l4
+l5
-avail
) > 0){
4395 strncpy(p1
, "Copy ", sizeof(p1
)-1);
4396 p1
[sizeof(p1
)-1] = '\0';
4400 if((needed
= l1
+l2
+l3
+l4
+l5
-avail
) > 0 && l5
> 0){
4401 snprintf(p5
, sizeof(p5
), "%s%s%s: ",
4403 *def
? short_str(def
,fileb
, sizeof(fileb
),
4404 MAX(10,l5
-5-needed
),EndDots
) : "",
4406 p5
[sizeof(p5
)-1] = '\0';
4410 if((needed
= l1
+l2
+l3
+l4
+l5
-avail
) > 0 && l3
> 0){
4411 if(needed
<= l3
- strlen(" (+ atts)"))
4413 else if(needed
<= l3
- strlen(" (atts)"))
4415 else if(needed
<= l3
- strlen(" (+)"))
4417 else if(needed
<= l3
- strlen("+"))
4425 snprintf(prompt_buf
, sizeof(prompt_buf
), "%s%s%s%s%s", p1
, p2
, p3
, p4
, p5
);
4426 prompt_buf
[sizeof(prompt_buf
)-1] = '\0';
4429 if(items_in_hist(dir_hist
) > 0 || hist_len
> 0){ /* any directories */
4430 opts
[kp
].name
= "^Y";
4431 opts
[kp
].label
= "Prev Dir";
4432 opts
[kp
+1].name
= "^V";
4433 opts
[kp
+1].label
= "Next Dir";
4437 opts
[kp
].label
= "";
4438 opts
[kp
+1].name
= "";
4439 opts
[kp
+1].label
= "";
4444 if(items_in_hist(*history
) > 0){
4445 opts
[ku
].name
= HISTORY_UP_KEYNAME
;
4446 opts
[ku
].label
= HISTORY_KEYLABEL
;
4447 opts
[ku
+1].name
= HISTORY_DOWN_KEYNAME
;
4448 opts
[ku
+1].label
= HISTORY_KEYLABEL
;
4452 opts
[ku
].label
= "";
4453 opts
[ku
+1].name
= "";
4454 opts
[ku
+1].label
= "";
4458 oeflags
= OE_APPEND_CURRENT
|
4459 ((flags
& GE_SEQ_SENSITIVE
) ? OE_SEQ_SENSITIVE
: 0);
4460 r
= optionally_enter(filename
, qline
, 0, len
, prompt_buf
,
4461 opts
, NO_HELP
, &oeflags
);
4463 dprint((2, "\n - export_filename = \"%s\", r = %d -\n", filename
, r
));
4467 * Helps may not be right if you add another caller or change
4468 * things. Check it out.
4470 if(flags
& GE_IS_IMPORT
)
4471 helper(h_ge_import
, _("HELP FOR IMPORT FILE SELECT"), HLPD_SIMPLE
);
4472 else if(flags
& GE_ALLPARTS
)
4473 helper(h_ge_allparts
, _("HELP FOR EXPORT FILE SELECT"), HLPD_SIMPLE
);
4475 helper(h_ge_export
, _("HELP FOR EXPORT FILE SELECT"), HLPD_SIMPLE
);
4477 ps
->mangled_screen
= 1;
4481 else if(r
== 10 || r
== 11){ /* Browser or File Completion */
4482 if(filename
[0]=='~'){
4483 if(filename
[1] == C_FILESEP
&& filename
[2]!='\0'){
4486 for(i
=0; filename
[i
+2] != '\0' && i
+2 < len
-1; i
++)
4487 filename
[i
] = filename
[i
+2];
4489 strncpy(dir
, precolon
, sizeof(dir
)-1);
4490 dir
[sizeof(dir
)-1] = '\0';
4492 else if(filename
[1]=='\0' ||
4493 (filename
[1] == C_FILESEP
&& filename
[2] == '\0')){
4497 strncpy(dir
, precolon
, sizeof(dir
)-1);
4498 dir
[sizeof(dir
)-1] = '\0';
4501 else if(!dir
[0] && !is_absolute_path(filename
) && was_abs_path
){
4505 strncpy(dir
, precolon
, sizeof(dir
)-1);
4506 dir
[sizeof(dir
)-1] = '\0';
4515 strncpy(tmp
, filename
, sizeof(tmp
)-1);
4516 tmp
[sizeof(tmp
)-1] = '\0';
4517 if(*tmp
&& is_absolute_path(tmp
))
4518 fnexpand(tmp
, sizeof(tmp
));
4519 if(strncmp(tmp
,postcolon
, strlen(postcolon
)))
4520 postcolon
[0] = '\0';
4522 if(*tmp
&& (fn
= last_cmpnt(tmp
))){
4524 strncpy(filename2
, fn
, sizeof(filename2
)-1);
4525 filename2
[sizeof(filename2
)-1] = '\0';
4526 if(is_absolute_path(tmp
)){
4527 strncpy(dir2
, tmp
, MIN(fn
- tmp
, sizeof(dir2
)-1));
4528 dir2
[MIN(fn
- tmp
, sizeof(dir2
)-1)] = '\0';
4530 if(tmp
[1]==':' && tmp
[2]=='\\' && dir2
[2]=='\0'){
4535 strncpy(postcolon
, dir2
, sizeof(postcolon
)-1);
4536 postcolon
[sizeof(postcolon
)-1] = '\0';
4542 * Just building the directory name in dir2,
4543 * full_filename is overloaded.
4545 snprintf(full_filename
, len
, "%.*s", (int) MIN(fn
-tmp
,len
-1), tmp
);
4546 full_filename
[len
-1] = '\0';
4547 strncpy(postcolon
, full_filename
, sizeof(postcolon
)-1);
4548 postcolon
[sizeof(postcolon
)-1] = '\0';
4549 build_path(dir2
, !dir
[0] ? p
= (char *)getcwd(NULL
,MAXPATH
)
4550 : (dir
[0] == '~' && !dir
[1])
4553 full_filename
, sizeof(dir2
));
4559 if(is_absolute_path(tmp
)){
4560 strncpy(dir2
, tmp
, sizeof(dir2
)-1);
4561 dir2
[sizeof(dir2
)-1] = '\0';
4563 if(dir2
[2]=='\0' && dir2
[1]==':'){
4566 strncpy(postcolon
,dir2
,sizeof(postcolon
)-1);
4567 postcolon
[sizeof(postcolon
)-1] = '\0';
4570 filename2
[0] = '\0';
4574 strncpy(filename2
, tmp
, sizeof(filename2
)-1);
4575 filename2
[sizeof(filename2
)-1] = '\0';
4577 if(getcwd(dir2
, sizeof(dir2
)) == NULL
)
4578 alpine_panic(_("getcwd() call failed at get_export_filename"));
4580 else if(dir
[0] == '~' && !dir
[1]){
4581 strncpy(dir2
, ps
->home_dir
, sizeof(dir2
)-1);
4582 dir2
[sizeof(dir2
)-1] = '\0';
4585 strncpy(dir2
, dir
, sizeof(dir2
)-1);
4586 dir2
[sizeof(dir2
)-1] = '\0';
4589 postcolon
[0] = '\0';
4593 build_path(full_filename
, dir2
, filename2
, len
);
4594 if(!strcmp(full_filename
, dir2
))
4595 filename2
[0] = '\0';
4596 if(full_filename
[strlen(full_filename
)-1] == C_FILESEP
4597 && isdir(full_filename
,NULL
,NULL
)){
4598 if(strlen(full_filename
) == 1)
4599 strncpy(postcolon
, full_filename
, sizeof(postcolon
)-1);
4600 else if(filename2
[0])
4601 strncpy(postcolon
, filename2
, sizeof(postcolon
)-1);
4602 postcolon
[sizeof(postcolon
)-1] = '\0';
4603 strncpy(dir2
, full_filename
, sizeof(dir2
)-1);
4604 dir2
[sizeof(dir2
)-1] = '\0';
4605 filename2
[0] = '\0';
4607 #ifdef _WINDOWS /* use full_filename even if not a valid directory */
4608 else if(full_filename
[strlen(full_filename
)-1] == C_FILESEP
){
4609 strncpy(postcolon
, filename2
, sizeof(postcolon
)-1);
4610 postcolon
[sizeof(postcolon
)-1] = '\0';
4611 strncpy(dir2
, full_filename
, sizeof(dir2
)-1);
4612 dir2
[sizeof(dir2
)-1] = '\0';
4613 filename2
[0] = '\0';
4616 if(dir2
[strlen(dir2
)-1] == C_FILESEP
&& strlen(dir2
)!=1
4617 && strcmp(dir2
+1, ":\\"))
4618 /* last condition to prevent stripping of '\\'
4619 in windows partition */
4620 dir2
[strlen(dir2
)-1] = '\0';
4622 if(r
== 10){ /* File Browser */
4623 r
= file_lister(lister_msg
? lister_msg
: "EXPORT",
4624 dir2
, sizeof(dir2
), filename2
, sizeof(filename2
),
4626 (flags
& GE_IS_IMPORT
) ? FB_READ
: FB_SAVE
);
4628 /* Windows has a special "feature" in which entering the file browser will
4629 change the working directory if the directory is changed at all (even
4630 clicking "Cancel" will change the working directory).
4632 if(F_ON(F_USE_CURRENT_DIR
, ps
))
4633 (void)getcwd(dir2
,sizeof(dir2
));
4635 if(isdir(dir2
,NULL
,NULL
)){
4636 strncpy(precolon
, dir2
, sizeof(precolon
)-1);
4637 precolon
[sizeof(precolon
)-1] = '\0';
4639 strncpy(postcolon
, filename2
, sizeof(postcolon
)-1);
4640 postcolon
[sizeof(postcolon
)-1] = '\0';
4642 build_path(full_filename
, dir2
, filename2
, len
);
4643 if(isdir(full_filename
, NULL
, NULL
)){
4644 strncpy(dir
, full_filename
, sizeof(dir
)-1);
4645 dir
[sizeof(dir
)-1] = '\0';
4649 fn
= last_cmpnt(full_filename
);
4650 strncpy(dir
, full_filename
,
4651 MIN(fn
- full_filename
, sizeof(dir
)-1));
4652 dir
[MIN(fn
- full_filename
, sizeof(dir
)-1)] = '\0';
4653 if(fn
- full_filename
> 1)
4654 dir
[fn
- full_filename
- 1] = '\0';
4657 if(!strcmp(dir
, ps
->home_dir
)){
4662 strncpy(filename
, fn
, len
-1);
4663 filename
[len
-1] = '\0';
4666 else{ /* File Completion */
4667 if(!pico_fncomplete(dir2
, filename2
, sizeof(filename2
)))
4669 strncat(postcolon
, filename2
,
4670 sizeof(postcolon
)-1-strlen(postcolon
));
4671 postcolon
[sizeof(postcolon
)-1] = '\0';
4673 was_abs_path
= is_absolute_path(filename
);
4675 if(!strcmp(dir
, ps
->home_dir
)){
4680 strncpy(filename
, postcolon
, len
-1);
4681 filename
[len
-1] = '\0';
4682 strncpy(dir
, precolon
, sizeof(dir
)-1);
4683 dir
[sizeof(dir
)-1] = '\0';
4685 if(filename
[0] == '~' && !filename
[1]){
4693 else if(r
== 12){ /* Download, caller handles it */
4697 else if(r
== 13){ /* toggle AllParts bit */
4699 if(*rflags
& GER_ALLPARTS
){
4700 *rflags
&= ~GER_ALLPARTS
;
4701 opts
[allparts
].label
= N_("AllParts");
4704 *rflags
|= GER_ALLPARTS
;
4705 /* opposite of All Parts, No All Parts */
4706 opts
[allparts
].label
= N_("NoAllParts");
4713 else if(r
== 14){ /* List file names matching partial? */
4717 else if(r
== 15){ /* toggle Binary bit */
4719 if(*rflags
& GER_BINARY
){
4720 *rflags
&= ~GER_BINARY
;
4721 opts
[binary
].label
= N_("Binary");
4724 *rflags
|= GER_BINARY
;
4725 opts
[binary
].label
= N_("No Binary");
4731 else if(r
== 1){ /* Cancel */
4738 else if(r
>= 30 && r
<= 33){
4741 if(r
== 30 || r
== 31){
4744 p
= get_prev_hist(*history
, filename
, 0, NULL
);
4746 p
= get_next_hist(*history
, filename
, 0, NULL
);
4750 if(r
== 32 || r
== 33){
4751 int nitems
= items_in_hist(dir_hist
);
4752 if(dir_hist
|| hist_len
> 0){
4755 p
= hist_in_pos(--pos
, ps_global
->VAR_HISTORY
, hist_len
, dir_hist
, nitems
);
4759 if(pos
< hist_len
+ nitems
)
4760 p
= hist_in_pos(++pos
, ps_global
->VAR_HISTORY
, hist_len
, dir_hist
, nitems
);
4762 if(p
== NULL
|| *p
== '\0')
4766 last
= p
; /* save it! */
4768 if(p
!= NULL
&& *p
!= '\0'){
4769 if(r
== 30 || r
== 31){
4770 if((fn
= last_cmpnt(p
)) != NULL
){
4771 strncpy(dir
, p
, MIN(fn
- p
, sizeof(dir
)-1));
4772 dir
[MIN(fn
- p
, sizeof(dir
)-1)] = '\0';
4774 dir
[fn
- p
- 1] = '\0';
4775 strncpy(filename
, fn
, len
-1);
4776 filename
[len
-1] = '\0';
4778 } else { /* r == 32 || r == 33 */
4779 strncpy(dir
, p
, sizeof(dir
)-1);
4780 dir
[sizeof(dir
)-1] = '\0';
4783 if(!strcmp(dir
, ps
->home_dir
)){
4793 rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
,
4794 SIZEOF_20KBUF
, filename
);
4795 strncpy(filename
, tmp_20k_buf
, len
);
4796 filename
[len
-1] = '\0';
4804 removing_leading_and_trailing_white_space(filename
);
4807 if(!*def
){ /* Cancel */
4812 strncpy(filename
, def
, len
-1);
4813 filename
[len
-1] = '\0';
4816 #if defined(DOS) || defined(OS2)
4817 if(is_absolute_path(filename
)){
4818 fixpath(filename
, len
);
4821 if(filename
[0] == '~'){
4822 if(fnexpand(filename
, len
) == NULL
){
4823 char *p
= strindex(filename
, '/');
4826 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
4827 _("Error expanding file name: \"%s\" unknown user"),
4834 if(is_absolute_path(filename
)){
4835 strncpy(full_filename
, filename
, len
-1);
4836 full_filename
[len
-1] = '\0';
4840 build_path(full_filename
, (char *)getcwd(dir
,sizeof(dir
)),
4842 else if(dir
[0] == '~' && !dir
[1])
4843 build_path(full_filename
, ps
->home_dir
, filename
, len
);
4845 build_path(full_filename
, dir
, filename
, len
);
4848 if((ill
= filter_filename(full_filename
, &fatal
,
4849 ps_global
->restricted
|| ps_global
->VAR_OPER_DIR
)) != NULL
){
4851 q_status_message1(SM_ORDER
| SM_DING
, 3, 3, "%s", ill
);
4855 /* BUG: we should beep when the key's pressed rather than bitch later */
4856 /* Warn and ask for confirmation. */
4857 snprintf(prompt_buf
, sizeof(prompt_buf
), "File name contains a '%s'. %s anyway",
4858 ill
, (flags
& GE_IS_EXPORT
) ? "Export" : "Save");
4859 prompt_buf
[sizeof(prompt_buf
)-1] = '\0';
4860 if(want_to(prompt_buf
, 'n', 0, NO_HELP
,
4861 ((flags
& GE_SEQ_SENSITIVE
) ? RB_SEQ_SENSITIVE
: 0)) != 'y')
4866 break; /* Must have got an OK file name */
4869 if(VAR_OPER_DIR
&& !in_dir(VAR_OPER_DIR
, full_filename
)){
4874 if(!can_access(full_filename
, ACCESS_EXISTS
)){
4876 static ESCKEY_S access_opts
[] = {
4877 /* TRANSLATORS: asking user if they want to overwrite (replace contents of)
4878 a file or append to the end of the file */
4879 {'o', 'o', "O", N_("Overwrite")},
4880 {'a', 'a', "A", N_("Append")},
4881 {-1, 0, NULL
, NULL
}};
4883 rbflags
= RB_NORM
| ((flags
& GE_SEQ_SENSITIVE
) ? RB_SEQ_SENSITIVE
: 0);
4885 if(flags
& GE_NO_APPEND
){
4886 r
= strlen(filename
);
4887 snprintf(prompt_buf
, sizeof(prompt_buf
),
4888 /* TRANSLATORS: asking user whether to overwrite a file or not,
4889 File <filename> already exists. Overwrite it ? */
4890 _("File \"%s%s\" already exists. Overwrite it "),
4891 (r
> 20) ? "..." : "",
4892 filename
+ ((r
> 20) ? r
- 20 : 0));
4893 prompt_buf
[sizeof(prompt_buf
)-1] = '\0';
4894 if(want_to(prompt_buf
, 'n', 'x', NO_HELP
, rbflags
) == 'y'){
4896 *rflags
|= GER_OVER
;
4898 if(our_unlink(full_filename
) < 0){
4899 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
4900 /* TRANSLATORS: Cannot remove old <filename>: <error text> */
4901 _("Cannot remove old %s: %s"),
4902 full_filename
, error_description(errno
));
4910 else if(!(flags
& GE_IS_IMPORT
)){
4911 r
= strlen(filename
);
4912 snprintf(prompt_buf
, sizeof(prompt_buf
),
4913 /* TRANSLATORS: File <filename> already exists. Overwrite or append to it ? */
4914 _("File \"%s%s\" already exists. Overwrite or append to it ? "),
4915 (r
> 20) ? "..." : "",
4916 filename
+ ((r
> 20) ? r
- 20 : 0));
4917 prompt_buf
[sizeof(prompt_buf
)-1] = '\0';
4918 switch(radio_buttons(prompt_buf
, -FOOTER_ROWS(ps_global
),
4919 access_opts
, 'a', 'x', NO_HELP
, rbflags
)){
4922 *rflags
|= GER_OVER
;
4924 if(our_truncate(full_filename
, (off_t
)0) < 0)
4925 /* trouble truncating, but we'll give it a try anyway */
4926 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
4927 /* TRANSLATORS: Warning: Cannot truncate old <filename>: <error text> */
4928 _("Warning: Cannot truncate old %s: %s"),
4929 full_filename
, error_description(errno
));
4934 *rflags
|= GER_APPEND
;
4947 if(history
&& ret
== 0){
4948 save_hist(*history
, full_filename
, 0, NULL
);
4949 strncpy(tmp
, full_filename
, MAXPATH
);
4950 tmp
[MAXPATH
] = '\0';
4951 if((fn
= strrchr(tmp
, C_FILESEP
)) != NULL
)
4956 save_hist(dir_hist
, tmp
, 0, NULL
);
4959 if(opts
&& opts
!= optsarg
)
4960 fs_give((void **) &opts
);
4966 /*----------------------------------------------------------------------
4967 parse the config'd upload/download command
4969 Args: cmd -- buffer to return command fit for shellin'
4972 fname -- file name to build into the command
4974 Returns: pointer to cmd_str buffer or NULL on real bad error
4976 NOTE: One SIDE EFFECT is that any defined "prefix" string in the
4977 cfg_str is written to standard out right before a successful
4978 return of this function. The call immediately following this
4979 function darn well better be the shell exec...
4982 build_updown_cmd(char *cmd
, size_t cmdlen
, char *prefix
, char *cfg_str
, char *fname
)
4985 int fname_found
= 0;
4987 if(prefix
&& *prefix
){
4988 /* loop thru replacing all occurances of _FILE_ */
4989 p
= strncpy(cmd
, prefix
, cmdlen
);
4990 cmd
[cmdlen
-1] = '\0';
4991 while((p
= strstr(p
, "_FILE_")))
4992 rplstr(p
, cmdlen
-(p
-cmd
), 6, fname
);
4997 /* loop thru replacing all occurances of _FILE_ */
4998 p
= strncpy(cmd
, cfg_str
, cmdlen
);
4999 cmd
[cmdlen
-1] = '\0';
5000 while((p
= strstr(p
, "_FILE_"))){
5001 rplstr(p
, cmdlen
-(p
-cmd
), 6, fname
);
5006 snprintf(cmd
+strlen(cmd
), cmdlen
-strlen(cmd
), " %s", fname
);
5008 cmd
[cmdlen
-1] = '\0';
5010 dprint((4, "\n - build_updown_cmd = \"%s\" -\n",
5016 /*----------------------------------------------------------------------
5017 Write a berzerk format message delimiter using the given putc function
5019 Args: e -- envelope of message to write
5020 pc -- function to use
5022 Returns: TRUE if we could write it, FALSE if there was a problem
5024 NOTE: follows delimiter with OS-dependent newline
5027 bezerk_delimiter(ENVELOPE
*env
, MESSAGECACHE
*mc
, gf_io_t pc
, int leading_newline
)
5033 /* write "[\n]From mailbox[@host] " */
5034 if(!((leading_newline
? gf_puts(NEWLINE
, pc
) : 1)
5035 && gf_puts("From ", pc
)
5036 && gf_puts((env
&& env
->from
) ? env
->from
->mailbox
5037 : "the-concourse-on-high", pc
)
5038 && gf_puts((env
&& env
->from
&& env
->from
->host
) ? "@" : "", pc
)
5039 && gf_puts((env
&& env
->from
&& env
->from
->host
) ? env
->from
->host
5045 when
= mail_longdate(mc
);
5046 else if(env
&& env
->date
&& env
->date
[0]
5047 && mail_parse_date(&telt
,env
->date
))
5048 when
= mail_longdate(&telt
);
5054 while(p
&& *p
&& *p
!= '\n') /* write date */
5058 if(!gf_puts(NEWLINE
, pc
)) /* write terminating newline */
5065 /*----------------------------------------------------------------------
5066 Execute command to jump to a given message number
5068 Args: qline -- Line to ask question on
5070 Result: returns true if the use selected a new message, false otherwise
5074 jump_to(MSGNO_S
*msgmap
, int qline
, UCS first_num
, SCROLL_S
*sparms
, CmdWhere in_index
)
5076 char jump_num_string
[80], *j
, prompt
[70];
5079 static ESCKEY_S jump_to_key
[] = { {0, 0, NULL
, NULL
},
5080 /* TRANSLATORS: go to First Message */
5081 {ctrl('Y'), 10, "^Y", N_("First Msg")},
5082 {ctrl('V'), 11, "^V", N_("Last Msg")},
5083 {-1, 0, NULL
, NULL
} };
5085 dprint((4, "\n - jump_to -\n"));
5088 if(sparms
&& sparms
->jump_is_debug
)
5089 return(get_level(qline
, first_num
, sparms
));
5092 if(!any_messages(msgmap
, NULL
, "to Jump to"))
5095 if(first_num
&& first_num
< 0x80 && isdigit((unsigned char) first_num
)){
5096 jump_num_string
[0] = first_num
;
5097 jump_num_string
[1] = '\0';
5100 jump_num_string
[0] = '\0';
5102 if(mn_total_cur(msgmap
) > 1L){
5103 snprintf(prompt
, sizeof(prompt
), "Unselect %s msgs in favor of number to be entered",
5104 comatose(mn_total_cur(msgmap
)));
5105 prompt
[sizeof(prompt
)-1] = '\0';
5106 if((rc
= want_to(prompt
, 'n', 0, NO_HELP
, WT_NORM
)) == 'n')
5110 snprintf(prompt
, sizeof(prompt
), "%s number to jump to : ", in_index
== ThrdIndx
5113 prompt
[sizeof(prompt
)-1] = '\0';
5117 int flags
= OE_APPEND_CURRENT
;
5119 rc
= optionally_enter(jump_num_string
, qline
, 0,
5120 sizeof(jump_num_string
), prompt
,
5121 jump_to_key
, help
, &flags
);
5123 help
= help
== NO_HELP
5124 ? (in_index
== ThrdIndx
? h_oe_jump_thd
: h_oe_jump
)
5128 else if(rc
== 10 || rc
== 11){
5132 closest
= closest_jump_target(rc
== 10 ? 1L
5133 : ((in_index
== ThrdIndx
)
5134 ? msgmap
->max_thrdno
5135 : mn_get_total(msgmap
)),
5136 ps_global
->mail_stream
,
5138 in_index
, warning
, sizeof(warning
));
5139 /* ignore warning */
5144 * If we take out the *jump_num_string nonempty test in this if
5145 * then the closest_jump_target routine will offer a jump to the
5146 * last message. However, it is slow because you have to wait for
5147 * the status message and it is annoying for people who hit J command
5148 * by mistake and just want to hit return to do nothing, like has
5149 * always worked. So the test is there for now. Hubert 2002-08-19
5151 * Jumping to first/last message is now possible through ^Y/^V
5152 * commands above. jpf 2002-08-21
5153 * (and through "end" hubert 2006-07-07)
5155 if(rc
== 0 && *jump_num_string
!= '\0'){
5156 removing_leading_and_trailing_white_space(jump_num_string
);
5157 for(j
=jump_num_string
; isdigit((unsigned char)*j
) || *j
=='-'; j
++)
5161 if(!strucmp("end", j
))
5162 return((in_index
== ThrdIndx
) ? msgmap
->max_thrdno
: mn_get_total(msgmap
));
5164 q_status_message(SM_ORDER
| SM_DING
, 2, 2,
5165 _("Invalid number entered. Use only digits 0-9"));
5166 jump_num_string
[0] = '\0';
5170 long closest
, jump_num
;
5172 if(*jump_num_string
)
5173 jump_num
= atol(jump_num_string
);
5178 closest
= closest_jump_target(jump_num
, ps_global
->mail_stream
,
5180 *jump_num_string
? 0 : 1,
5181 in_index
, warning
, sizeof(warning
));
5183 q_status_message(SM_ORDER
| SM_DING
, 2, 2, warning
);
5185 if(closest
== jump_num
)
5189 jump_num_string
[0] = '\0';
5191 strncpy(jump_num_string
, long2string(closest
),
5192 sizeof(jump_num_string
));
5207 * cmd_delete_action - handle msgno advance and such after single message deletion
5210 cmd_delete_action(struct pine
*state
, MSGNO_S
*msgmap
, CmdWhere in_index
)
5216 msgno
= mn_get_cur(msgmap
);
5217 advance_cur_after_delete(state
, state
->mail_stream
, msgmap
, in_index
);
5219 if(IS_NEWS(state
->mail_stream
)
5220 || ((state
->context_current
->use
& CNTXT_INCMNG
)
5221 && context_isambig(state
->cur_folder
))){
5223 opts
= (NSF_TRUST_FLAGS
| NSF_SKIP_CHID
);
5224 if(in_index
== View
)
5225 opts
&= ~NSF_SKIP_CHID
;
5227 (void)next_sorted_flagged(F_UNDEL
|F_UNSEEN
, state
->mail_stream
, msgno
, &opts
);
5228 if(!(opts
& NSF_FLAG_MATCH
)){
5229 char nextfolder
[MAXPATH
];
5231 strncpy(nextfolder
, state
->cur_folder
, sizeof(nextfolder
));
5232 nextfolder
[sizeof(nextfolder
)-1] = '\0';
5233 rv
= next_folder(NULL
, nextfolder
, sizeof(nextfolder
), nextfolder
,
5234 state
->context_current
, NULL
, NULL
)
5235 ? ". Press TAB for next folder."
5236 : ". No more folders to TAB to.";
5245 * cmd_delete_index - fixup msgmap or whatever after cmd_delete has done it's thing
5248 cmd_delete_index(struct pine
*state
, MSGNO_S
*msgmap
)
5250 return(cmd_delete_action(state
, msgmap
,MsgIndx
));
5254 * cmd_delete_view - fixup msgmap or whatever after cmd_delete has done it's thing
5257 cmd_delete_view(struct pine
*state
, MSGNO_S
*msgmap
)
5259 return(cmd_delete_action(state
, msgmap
, View
));
5264 advance_cur_after_delete(struct pine
*state
, MAILSTREAM
*stream
, MSGNO_S
*msgmap
, CmdWhere in_index
)
5266 long new_msgno
, msgno
;
5269 new_msgno
= msgno
= mn_get_cur(msgmap
);
5270 opts
= NSF_TRUST_FLAGS
;
5272 if(F_ON(F_DEL_SKIPS_DEL
, state
)){
5274 if(THREADING() && sp_viewing_a_thread(stream
))
5275 opts
|= NSF_SKIP_CHID
;
5277 new_msgno
= next_sorted_flagged(F_UNDEL
, stream
, msgno
, &opts
);
5280 mn_inc_cur(stream
, msgmap
,
5281 (in_index
== View
&& THREADING()
5282 && sp_viewing_a_thread(stream
))
5284 : (in_index
== View
)
5285 ? MH_ANYTHD
: MH_NONE
);
5286 new_msgno
= mn_get_cur(msgmap
);
5287 if(new_msgno
!= msgno
)
5288 opts
|= NSF_FLAG_MATCH
;
5292 * Viewing_a_thread is the complicated case because we want to ignore
5293 * other threads at first and then look in other threads if we have to.
5294 * By ignoring other threads we also ignore collapsed partial threads
5295 * in our own thread.
5297 if(THREADING() && sp_viewing_a_thread(stream
) && !(opts
& NSF_FLAG_MATCH
)){
5298 long rawno
, orig_thrdno
;
5299 PINETHRD_S
*thrd
, *topthrd
= NULL
;
5301 rawno
= mn_m2raw(msgmap
, msgno
);
5302 thrd
= fetch_thread(stream
, rawno
);
5303 if(thrd
&& thrd
->top
)
5304 topthrd
= fetch_thread(stream
, thrd
->top
);
5306 orig_thrdno
= topthrd
? topthrd
->thrdno
: -1L;
5308 opts
= NSF_TRUST_FLAGS
;
5309 new_msgno
= next_sorted_flagged(F_UNDEL
, stream
, msgno
, &opts
);
5312 * If we got a match, new_msgno may be a message in
5313 * a different thread from the one we are viewing, or it could be
5314 * in a collapsed part of this thread.
5316 if(opts
& NSF_FLAG_MATCH
){
5321 thrd
= fetch_thread(stream
, mn_m2raw(msgmap
,new_msgno
));
5322 if(thrd
&& thrd
->top
)
5323 topthrd
= fetch_thread(stream
, thrd
->top
);
5326 * If this match is in the same thread we're already in
5327 * then we're done, else we have to ask the user and maybe
5330 if(!(orig_thrdno
> 0L && topthrd
5331 && topthrd
->thrdno
== orig_thrdno
)){
5333 if(F_OFF(F_AUTO_OPEN_NEXT_UNREAD
, state
)){
5334 if(in_index
== View
)
5335 snprintf(pmt
, sizeof(pmt
),
5336 "View message in thread number %.10s",
5337 topthrd
? comatose(topthrd
->thrdno
) : "?");
5339 snprintf(pmt
, sizeof(pmt
), "View thread number %.10s",
5340 topthrd
? comatose(topthrd
->thrdno
) : "?");
5342 ret
= want_to(pmt
, 'y', 'x', NO_HELP
, WT_NORM
);
5348 unview_thread(state
, stream
, msgmap
);
5349 mn_set_cur(msgmap
, new_msgno
);
5351 && (count_lflags_in_thread(stream
, topthrd
, msgmap
,
5353 && view_thread(state
, stream
, msgmap
, 1)){
5354 if(current_index_state
)
5355 msgmap
->top_after_thrd
= current_index_state
->msg_at_top
;
5357 state
->view_skipped_index
= 1;
5358 state
->next_screen
= mail_view_screen
;
5361 view_thread(state
, stream
, msgmap
, 1);
5362 if(current_index_state
)
5363 msgmap
->top_after_thrd
= current_index_state
->msg_at_top
;
5365 state
->next_screen
= SCREEN_FUN_NULL
;
5369 new_msgno
= msgno
; /* stick with original */
5374 mn_set_cur(msgmap
, new_msgno
);
5375 if(in_index
!= View
)
5376 adjust_cur_to_visible(stream
, msgmap
);
5382 get_level(int qline
, UCS first_num
, SCROLL_S
*sparms
)
5384 char debug_num_string
[80], *j
, prompt
[70];
5389 if(first_num
&& first_num
< 0x80 && isdigit((unsigned char)first_num
)){
5390 debug_num_string
[0] = first_num
;
5391 debug_num_string
[1] = '\0';
5392 debug_num
= atol(debug_num_string
);
5393 *(int *)(sparms
->proc
.data
.p
) = debug_num
;
5394 q_status_message1(SM_ORDER
, 0, 3, "Show debug <= level %s",
5395 comatose(debug_num
));
5399 debug_num_string
[0] = '\0';
5401 snprintf(prompt
, sizeof(prompt
), "Show debug <= this level (0-%d) : ", MAX(debug
, 9));
5402 prompt
[sizeof(prompt
)-1] = '\0';
5406 int flags
= OE_APPEND_CURRENT
;
5408 rc
= optionally_enter(debug_num_string
, qline
, 0,
5409 sizeof(debug_num_string
), prompt
,
5410 NULL
, help
, &flags
);
5412 help
= help
== NO_HELP
? h_oe_debuglevel
: NO_HELP
;
5417 removing_leading_and_trailing_white_space(debug_num_string
);
5418 for(j
=debug_num_string
; isdigit((unsigned char)*j
); j
++)
5422 q_status_message(SM_ORDER
| SM_DING
, 2, 2,
5423 _("Invalid number entered. Use only digits 0-9"));
5424 debug_num_string
[0] = '\0';
5427 debug_num
= atol(debug_num_string
);
5429 q_status_message(SM_ORDER
| SM_DING
, 2, 2,
5430 _("Number should be >= 0"));
5431 else if(debug_num
> MAX(debug
,9))
5432 q_status_message1(SM_ORDER
| SM_DING
, 2, 2,
5433 _("Maximum is %s"), comatose(MAX(debug
,9)));
5435 *(int *)(sparms
->proc
.data
.p
) = debug_num
;
5436 q_status_message1(SM_ORDER
, 0, 3,
5437 "Show debug <= level %s",
5438 comatose(debug_num
));
5456 * Returns the message number closest to target that isn't hidden.
5457 * Make warning at least 100 chars.
5458 * A return of 0 means there is no message to jump to.
5461 closest_jump_target(long int target
, MAILSTREAM
*stream
, MSGNO_S
*msgmap
, int no_target
, CmdWhere in_index
, char *warning
, size_t warninglen
)
5463 long i
, start
, closest
= 0L;
5468 maxnum
= (in_index
== ThrdIndx
) ? msgmap
->max_thrdno
: mn_get_total(msgmap
);
5473 snprintf(warning
, warninglen
, "No %s number entered, jump to end? ",
5474 (in_index
== ThrdIndx
) ? "thread" : "message");
5475 warning
[warninglen
-1] = '\0';
5477 else if(target
< 1L)
5478 start
= 1L - target
;
5479 else if(target
> maxnum
)
5480 start
= target
- maxnum
;
5484 if(target
> 0L && target
<= maxnum
)
5485 if(in_index
== ThrdIndx
5486 || !msgline_hidden(stream
, msgmap
, target
, 0))
5489 for(i
= start
; target
+i
<= maxnum
|| target
-i
> 0L; i
++){
5491 if(target
+i
> 0L && target
+i
<= maxnum
&&
5492 (in_index
== ThrdIndx
5493 || !msgline_hidden(stream
, msgmap
, target
+i
, 0))){
5498 if(target
-i
> 0L && target
-i
<= maxnum
&&
5499 (in_index
== ThrdIndx
5500 || !msgline_hidden(stream
, msgmap
, target
-i
, 0))){
5506 strncpy(buf
, long2string(closest
), sizeof(buf
));
5507 buf
[sizeof(buf
)-1] = '\0';
5510 strncpy(warning
, "Nothing to jump to", warninglen
);
5511 else if(target
< 1L)
5512 snprintf(warning
, warninglen
, "%s number (%s) must be at least %s",
5513 (in_index
== ThrdIndx
) ? "Thread" : "Message",
5514 long2string(target
), buf
);
5515 else if(target
> maxnum
)
5516 snprintf(warning
, warninglen
, "%s number (%s) may be no more than %s",
5517 (in_index
== ThrdIndx
) ? "Thread" : "Message",
5518 long2string(target
), buf
);
5520 snprintf(warning
, warninglen
,
5521 "Message number (%s) is not in \"Zoomed Index\" - Closest is(%s)",
5522 long2string(target
), buf
);
5524 warning
[warninglen
-1] = '\0';
5530 /*----------------------------------------------------------------------
5531 Prompt for folder name to open, expand the name and return it
5533 Args: qline -- Screen line to prompt on
5534 allow_list -- if 1, allow ^T to bring up collection lister
5536 Result: returns the folder name or NULL
5537 pine structure mangled_footer flag is set
5538 may call the collection lister in which case mangled screen will be set
5540 This prompts the user for the folder to open, possibly calling up
5541 the collection lister if the user types ^T.
5542 ----------------------------------------------------------------------*/
5544 broach_folder(int qline
, int allow_list
, int *notrealinbox
, CONTEXT_S
**context
)
5547 static char newfolder
[MAILTMPLEN
];
5548 char expanded
[MAXPATH
+1],
5549 prompt
[MAX_SCREEN_COLS
+1],
5551 unsigned char *f1
, *f2
, *f3
;
5552 static HISTORY_S
*history
= NULL
;
5553 CONTEXT_S
*tc
, *tc2
;
5555 int rc
, r
, ku
= -1, n
, flags
, last_rc
= 0, inbox
, done
= 0;
5558 * the idea is to provide a clue for the context the file name
5559 * will be saved in (if a non-imap names is typed), and to
5560 * only show the previous if it was also in the same context
5567 (*notrealinbox
) = 1;
5569 init_hist(&history
, HISTSIZE
);
5571 tc
= broach_get_folder(context
? *context
: NULL
, &inbox
, NULL
);
5573 /* set up extra command option keys */
5575 ekey
[rc
].ch
= (allow_list
) ? ctrl('T') : 0 ;
5576 ekey
[rc
].rval
= (allow_list
) ? 2 : 0;
5577 ekey
[rc
].name
= (allow_list
) ? "^T" : "";
5578 ekey
[rc
++].label
= (allow_list
) ? N_("ToFldrs") : "";
5580 if(ps_global
->context_list
->next
){
5581 ekey
[rc
].ch
= ctrl('P');
5583 ekey
[rc
].name
= "^P";
5584 ekey
[rc
++].label
= N_("Prev Collection");
5586 ekey
[rc
].ch
= ctrl('N');
5588 ekey
[rc
].name
= "^N";
5589 ekey
[rc
++].label
= N_("Next Collection");
5592 ekey
[rc
].ch
= ctrl('W');
5594 ekey
[rc
].name
= "^W";
5595 ekey
[rc
++].label
= N_("INBOX");
5597 if(F_ON(F_ENABLE_TAB_COMPLETE
,ps_global
)){
5600 ekey
[rc
].name
= "TAB";
5601 ekey
[rc
++].label
= N_("Complete");
5604 if(F_ON(F_ENABLE_SUB_LISTS
, ps_global
)){
5605 ekey
[rc
].ch
= ctrl('X');
5607 ekey
[rc
].name
= "^X";
5608 ekey
[rc
++].label
= N_("ListMatches");
5611 if(ps_global
->context_list
->next
&& F_ON(F_DISABLE_SAVE_INPUT_HISTORY
, ps_global
)){
5612 ekey
[rc
].ch
= KEY_UP
;
5615 ekey
[rc
++].label
= "";
5617 ekey
[rc
].ch
= KEY_DOWN
;
5620 ekey
[rc
++].label
= "";
5622 else if(F_OFF(F_DISABLE_SAVE_INPUT_HISTORY
, ps_global
)){
5623 ekey
[rc
].ch
= KEY_UP
;
5627 ekey
[rc
++].label
= "";
5629 ekey
[rc
].ch
= KEY_DOWN
;
5632 ekey
[rc
++].label
= "";
5639 * Figure out next default value for this context. The idea
5640 * is that in each context the last folder opened is cached.
5641 * It's up to pick it out and display it. This is fine
5642 * and dandy if we've currently got the inbox open, BUT
5643 * if not, make the inbox the default the first time thru.
5646 last_folder
= ps_global
->inbox_name
;
5647 inbox
= 1; /* pretend we're in inbox from here on out */
5650 last_folder
= (ps_global
->last_unambig_folder
[0])
5651 ? ps_global
->last_unambig_folder
5652 : ((tc
->last_folder
[0]) ? tc
->last_folder
: NULL
);
5654 if(last_folder
){ /* MAXPATH + 1 = sizeof(expanded) */
5655 unsigned char *fname
= folder_name_decoded((unsigned char *)last_folder
);
5656 snprintf(expanded
, sizeof(expanded
), " [%.*s]", MAXPATH
+1-5,
5657 fname
? (char *) fname
: last_folder
);
5658 if(fname
) fs_give((void **)&fname
);
5663 expanded
[sizeof(expanded
)-1] = '\0';
5665 /* only show collection number if more than one available */
5666 if(ps_global
->context_list
->next
) /* MAX_SCREEN_COLS+1 == sizeof(prompt) */
5667 snprintf(prompt
, sizeof(prompt
), "GOTO %s in <%s> %.*s%s: ",
5668 NEWS_TEST(tc
) ? "news group" : "folder",
5669 tc
->nickname
, MAX_SCREEN_COLS
+1-50, expanded
,
5670 *expanded
? " " : "");
5671 else /* MAX_SCREEN_COLS+1 == sizeof(prompt) */
5672 snprintf(prompt
, sizeof(prompt
), "GOTO folder %.*s%s: ", MAX_SCREEN_COLS
+1-20, expanded
,
5673 *expanded
? " " : "");
5675 prompt
[sizeof(prompt
)-1] = '\0';
5677 if(utf8_width(prompt
) > MAXPROMPT
){
5678 if(ps_global
->context_list
->next
) /* MAX_SCREEN_COLS+1 == sizeof(prompt) */
5679 snprintf(prompt
, sizeof(prompt
), "GOTO <%s> %.*s%s: ",
5680 tc
->nickname
, MAX_SCREEN_COLS
+1-50, expanded
,
5681 *expanded
? " " : "");
5682 else /* MAX_SCREEN_COLS+1 == sizeof(prompt) */
5683 snprintf(prompt
, sizeof(prompt
), "GOTO %.*s%s: ", MAX_SCREEN_COLS
+1-20, expanded
,
5684 *expanded
? " " : "");
5686 prompt
[sizeof(prompt
)-1] = '\0';
5688 if(utf8_width(prompt
) > MAXPROMPT
){
5689 if(ps_global
->context_list
->next
) /* MAX_SCREEN_COLS+1 == sizeof(prompt) */
5690 snprintf(prompt
, sizeof(prompt
), "<%s> %.*s%s: ",
5691 tc
->nickname
, MAX_SCREEN_COLS
+1-50, expanded
,
5692 *expanded
? " " : "");
5693 else /* MAX_SCREEN_COLS+1 == sizeof(prompt) */
5694 snprintf(prompt
, sizeof(prompt
), "%.*s%s: ", MAX_SCREEN_COLS
+1-20, expanded
,
5695 *expanded
? " " : "");
5697 prompt
[sizeof(prompt
)-1] = '\0';
5702 if(items_in_hist(history
) > 1){
5703 ekey
[ku
].name
= HISTORY_UP_KEYNAME
;
5704 ekey
[ku
].label
= HISTORY_KEYLABEL
;
5705 ekey
[ku
+1].name
= HISTORY_DOWN_KEYNAME
;
5706 ekey
[ku
+1].label
= HISTORY_KEYLABEL
;
5710 ekey
[ku
].label
= "";
5711 ekey
[ku
+1].name
= "";
5712 ekey
[ku
+1].label
= "";
5716 /* is there any other way to do this? The point is that we
5717 * are trying to hide mutf7 from the user, and use the utf8
5718 * equivalent. So we create a variable f to take place of
5719 * newfolder, including content and size. f2 is copy of f1
5720 * that has to freed. Sigh!
5722 f3
= (unsigned char *) cpystr(newfolder
);
5723 f1
= fs_get(sizeof(newfolder
));
5724 f2
= folder_name_decoded(f3
);
5725 if(f3
) fs_give((void **)&f3
);
5726 strncpy((char *)f1
, (char *)f2
, sizeof(newfolder
));
5727 f1
[sizeof(newfolder
)-1] = '\0';
5728 if(f2
) fs_give((void **)&f2
);
5730 flags
= OE_APPEND_CURRENT
;
5731 rc
= optionally_enter((char *) f1
, qline
, 0, sizeof(newfolder
),
5732 (char *) prompt
, ekey
, help
, &flags
);
5734 f2
= folder_name_encoded(f1
);
5735 strncpy(newfolder
, (char *)f2
, sizeof(newfolder
));
5736 if(f1
) fs_give((void **)&f1
);
5737 if(f2
) fs_give((void **)&f2
);
5739 ps_global
->mangled_footer
= 1;
5742 case -1 : /* o_e says error! */
5743 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
5744 _("Error reading folder name"));
5747 case 0 : /* o_e says normal entry */
5748 removing_trailing_white_space(newfolder
);
5749 removing_leading_white_space(newfolder
);
5752 char *name
, *fullname
= NULL
;
5753 int exists
, breakout
= 0;
5755 save_hist(history
, newfolder
, 0, tc
);
5757 if(!(name
= folder_is_nick(newfolder
, FOLDERS(tc
),
5761 if(update_folder_spec(expanded
, sizeof(expanded
), name
)){
5762 strncpy(name
= newfolder
, expanded
, sizeof(newfolder
));
5763 newfolder
[sizeof(newfolder
)-1] = '\0';
5766 exists
= folder_name_exists(tc
, name
, &fullname
);
5769 strncpy(name
= newfolder
, fullname
, sizeof(newfolder
));
5770 newfolder
[sizeof(newfolder
)-1] = '\0';
5771 fs_give((void **) &fullname
);
5776 * if we know the things a folder, open it.
5777 * else if we know its a directory, visit it.
5778 * else we're not sure (it either doesn't really
5779 * exist or its unLISTable) so try opening it anyway
5781 if(exists
& FEX_ISFILE
){
5785 else if((exists
& FEX_ISDIR
)){
5787 CONTEXT_S
*fake_context
;
5788 char tmp
[MAILTMPLEN
];
5791 strncpy(tmp
, name
, sizeof(tmp
));
5792 tmp
[sizeof(tmp
)-2-1] = '\0';
5793 if(tmp
[(l
= strlen(tmp
)) - 1] != tc
->dir
->delim
){
5794 if(l
< sizeof(tmp
)){
5795 tmp
[l
] = tc
->dir
->delim
;
5796 strncpy(&tmp
[l
+1], "[]", sizeof(tmp
)-(l
+1));
5800 strncat(tmp
, "[]", sizeof(tmp
)-strlen(tmp
)-1);
5802 tmp
[sizeof(tmp
)-1] = '\0';
5804 fake_context
= new_context(tmp
, 0);
5805 newfolder
[0] = '\0';
5806 done
= display_folder_list(&fake_context
, newfolder
,
5807 1, folders_for_goto
);
5808 free_context(&fake_context
);
5811 else if(!(tc
->use
& CNTXT_INCMNG
)){
5812 done
= display_folder_list(&tc
, newfolder
,
5813 1, folders_for_goto
);
5817 else if((exists
& FEX_ERROR
)){
5818 q_status_message1(SM_ORDER
, 0, 3,
5819 _("Problem accessing folder \"%s\""),
5828 if(exists
== FEX_ERROR
)
5829 q_status_message1(SM_ORDER
, 0, 3,
5830 _("Problem accessing folder \"%s\""),
5832 else if(tc
->use
& CNTXT_INCMNG
)
5833 q_status_message1(SM_ORDER
, 0, 3,
5834 _("Can't find Incoming Folder: %s"),
5836 else if(context_isambig(newfolder
))
5837 q_status_message2(SM_ORDER
, 0, 3,
5838 _("Can't find folder \"%s\" in %s"),
5839 newfolder
, (void *) tc
->nickname
);
5841 q_status_message1(SM_ORDER
, 0, 3,
5842 _("Can't find folder \"%s\""),
5847 else if(last_folder
){
5848 if(ps_global
->goto_default_rule
== GOTO_FIRST_CLCTN_DEF_INBOX
5849 && !strucmp(last_folder
, ps_global
->inbox_name
)
5850 && tc
== ((ps_global
->context_list
->use
& CNTXT_INCMNG
)
5851 ? ps_global
->context_list
->next
: ps_global
->context_list
)){
5853 (*notrealinbox
) = 0;
5855 tc
= ps_global
->context_list
;
5858 strncpy(newfolder
, last_folder
, sizeof(newfolder
));
5859 newfolder
[sizeof(newfolder
)-1] = '\0';
5860 save_hist(history
, newfolder
, 0, tc
);
5864 /* fall thru like they cancelled */
5866 case 1 : /* o_e says user cancel */
5867 cmd_cancelled("Open folder");
5870 case 2 : /* o_e says user wants list */
5871 r
= display_folder_list(&tc
, newfolder
, 0, folders_for_goto
);
5877 case 3 : /* o_e says user wants help */
5878 help
= help
== NO_HELP
? h_oe_broach
: NO_HELP
;
5881 case 4 : /* redraw */
5884 case 10 : /* Previous collection */
5885 tc2
= ps_global
->context_list
;
5886 while(tc2
->next
&& tc2
->next
!= tc
)
5892 case 11 : /* Next collection */
5893 tc
= (tc
->next
) ? tc
->next
: ps_global
->context_list
;
5896 case 12 : /* file name completion */
5897 if(!folder_complete(tc
, newfolder
, sizeof(newfolder
), &n
)){
5898 if(n
&& last_rc
== 12 && !(flags
& OE_USER_MODIFIED
)){
5899 r
= display_folder_list(&tc
, newfolder
, 1,folders_for_goto
);
5901 done
++; /* bingo! */
5903 rc
= 0; /* burn last_rc */
5911 case 14 : /* file name completion */
5912 r
= display_folder_list(&tc
, newfolder
, 2, folders_for_goto
);
5914 done
++; /* bingo! */
5916 rc
= 0; /* burn last_rc */
5920 case 17 : /* GoTo INBOX */
5922 strncpy(newfolder
, ps_global
->inbox_name
, sizeof(newfolder
)-1);
5923 newfolder
[sizeof(newfolder
)-1] = '\0';
5925 (*notrealinbox
) = 0;
5927 tc
= ps_global
->context_list
;
5928 save_hist(history
, newfolder
, 0, tc
);
5933 if((p
= get_prev_hist(history
, newfolder
, 0, tc
)) != NULL
){
5934 strncpy(newfolder
, p
, sizeof(newfolder
));
5935 newfolder
[sizeof(newfolder
)-1] = '\0';
5936 if(history
->hist
[history
->curindex
])
5937 tc
= history
->hist
[history
->curindex
]->cntxt
;
5945 if((p
= get_next_hist(history
, newfolder
, 0, tc
)) != NULL
){
5946 strncpy(newfolder
, p
, sizeof(newfolder
));
5947 newfolder
[sizeof(newfolder
)-1] = '\0';
5948 if(history
->hist
[history
->curindex
])
5949 tc
= history
->hist
[history
->curindex
]->cntxt
;
5957 alpine_panic("Unhandled case");
5964 dprint((2, "broach folder, name entered \"%s\"\n",
5965 newfolder
? newfolder
: "?"));
5967 /*-- Just check that we can expand this. It gets done for real later --*/
5968 strncpy(expanded
, newfolder
, sizeof(expanded
));
5969 expanded
[sizeof(expanded
)-1] = '\0';
5971 if(!expand_foldername(expanded
, sizeof(expanded
))) {
5972 dprint((1, "Error: Failed on expansion of filename %s (do_broach)\n",
5973 expanded
? expanded
: "?"));
5982 /*----------------------------------------------------------------------
5983 Check to see if user wants to reopen dead stream.
5988 Result: 1 if the folder was successfully updatedn
5993 ask_mailbox_reopen(struct pine
*ps
, int *reopenp
)
5995 if(((ps
->mail_stream
->dtb
5996 && ((ps
->mail_stream
->dtb
->flags
& DR_NONEWMAIL
)
5997 || (ps
->mail_stream
->rdonly
5998 && ps
->mail_stream
->dtb
->flags
& DR_NONEWMAILRONLY
)))
5999 && (ps
->reopen_rule
== REOPEN_ASK_ASK_Y
6000 || ps
->reopen_rule
== REOPEN_ASK_ASK_N
6001 || ps
->reopen_rule
== REOPEN_ASK_NO_Y
6002 || ps
->reopen_rule
== REOPEN_ASK_NO_N
))
6003 || ((ps
->mail_stream
->dtb
6004 && ps
->mail_stream
->rdonly
6005 && !(ps
->mail_stream
->dtb
->flags
& DR_LOCAL
))
6006 && (ps
->reopen_rule
== REOPEN_YES_ASK_Y
6007 || ps
->reopen_rule
== REOPEN_YES_ASK_N
6008 || ps
->reopen_rule
== REOPEN_ASK_ASK_Y
6009 || ps
->reopen_rule
== REOPEN_ASK_ASK_N
))){
6012 switch(ps
->reopen_rule
){
6013 case REOPEN_YES_ASK_Y
:
6014 case REOPEN_ASK_ASK_Y
:
6015 case REOPEN_ASK_NO_Y
:
6024 switch(want_to("Re-open folder to check for new messages", deefault
,
6025 'x', h_reopen_folder
, WT_NORM
)){
6040 /*----------------------------------------------------------------------
6041 Check to see if user input is in form of old c-client mailbox speck
6046 Result: 1 if the folder was successfully updatedn
6051 update_folder_spec(char *new, size_t newlen
, char *old
)
6057 if(*(p
= old
) == '*') /* old form? */
6060 if(*old
== '{') /* copy host spec */
6062 switch(*new = *old
++){
6067 if(!struncmp(old
, "nntp", 4))
6075 while(*new++ != '}' && (new-orignew
) < newlen
-1);
6077 if((*p
== '*' && *old
) || ((*old
== '*') ? *++old
: 0)){
6079 * OK, some heuristics here. If it looks like a newsgroup
6080 * then we plunk it into the #news namespace else we
6081 * assume that they're trying to get at a #public folder...
6084 *p
&& (isalnum((unsigned char) *p
) || strindex(".-", *p
));
6088 sstrncpy(&new, (*p
&& !nntp
) ? "#public/" : "#news.", newlen
-(new-orignew
));
6089 strncpy(new, old
, newlen
-(new-orignew
));
6093 orignew
[newlen
-1] = '\0';
6099 /*----------------------------------------------------------------------
6100 Open the requested folder in the requested context
6102 Args: state -- usual pine state struct
6103 newfolder -- folder to open
6104 new_context -- folder context might live in
6105 stream -- candidate for recycling
6107 Result: New folder open or not (if error), and we're set to
6108 enter the index screen.
6111 visit_folder(struct pine
*state
, char *newfolder
, CONTEXT_S
*new_context
,
6112 MAILSTREAM
*stream
, long unsigned int flags
)
6114 dprint((9, "visit_folder(%s, %s)\n",
6115 newfolder
? newfolder
: "?",
6116 (new_context
&& new_context
->context
)
6117 ? new_context
->context
: "(NULL)"));
6119 if(ps_global
&& ps_global
->ttyo
){
6120 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
6121 ps_global
->mangled_footer
= 1;
6124 if(do_broach_folder(newfolder
, new_context
, stream
? &stream
: NULL
,
6126 || !sp_flagged(state
->mail_stream
, SP_LOCKED
))
6127 state
->next_screen
= mail_index_screen
;
6129 state
->next_screen
= folder_screen
;
6133 /*----------------------------------------------------------------------
6134 Move read messages from folder if listed in archive
6140 read_msg_prompt(long int n
, char *f
)
6142 char buf
[MAX_SCREEN_COLS
+1];
6144 snprintf(buf
, sizeof(buf
), "Save the %ld read message%s in \"%s\"", n
, plural(n
), f
);
6145 buf
[sizeof(buf
)-1] = '\0';
6146 return(want_to(buf
, 'y', 0, NO_HELP
, WT_NORM
) == 'y');
6150 /*----------------------------------------------------------------------
6151 Print current message[s] or folder index
6153 Args: state -- pointer to struct holding a bunch of pine state
6154 msgmap -- table mapping msg nums to c-client sequence nums
6155 aopt -- aggregate options
6156 in_index -- boolean indicating we're called from Index Screen
6158 Filters the original header and sends stuff to printer
6161 cmd_print(struct pine
*state
, MSGNO_S
*msgmap
, int aopt
, CmdWhere in_index
)
6164 long i
, msgs
, rawno
;
6165 int next
= 0, do_index
= 0, rv
= 0;
6170 if(MCMD_ISAGG(aopt
) && !pseudo_selected(state
->mail_stream
, msgmap
))
6173 msgs
= mn_total_cur(msgmap
);
6175 if((in_index
!= View
) && F_ON(F_PRINT_INDEX
, state
)){
6178 static ESCKEY_S prt_opts
[] = {
6179 {'i', 'i', "I", N_("Index")},
6180 {'m', 'm', "M", NULL
},
6181 {-1, 0, NULL
, NULL
}};
6183 if(in_index
== ThrdIndx
){
6184 /* TRANSLATORS: This is a question, Print Index ? */
6185 if(want_to(_("Print Index"), 'y', 'x', NO_HELP
, WT_NORM
) == 'y')
6191 snprintf(m
, sizeof(m
), "Message%s", (msgs
>1L) ? "s" : "");
6192 m
[sizeof(m
)-1] = '\0';
6193 prt_opts
[1].label
= m
;
6194 snprintf(prompt
, sizeof(prompt
), "Print %sFolder Index or %s %s? ",
6195 (aopt
& MCMD_AGG_2
) ? "thread " : MCMD_ISAGG(aopt
) ? "selected " : "",
6196 (aopt
& MCMD_AGG_2
) ? "thread" : MCMD_ISAGG(aopt
) ? "selected" : "current", m
);
6197 prompt
[sizeof(prompt
)-1] = '\0';
6199 ans
= radio_buttons(prompt
, -FOOTER_ROWS(state
), prt_opts
, 'm', 'x',
6200 NO_HELP
, RB_NORM
|RB_SEQ_SENSITIVE
);
6205 cmd_cancelled("Print");
6206 if(MCMD_ISAGG(aopt
))
6207 restore_selected(msgmap
);
6222 snprintf(prompt
, sizeof(prompt
), "%sFolder Index",
6223 (aopt
& MCMD_AGG_2
) ? "Thread " : MCMD_ISAGG(aopt
) ? "Selected " : "");
6225 snprintf(prompt
, sizeof(prompt
), "%s messages", long2string(msgs
));
6227 snprintf(prompt
, sizeof(prompt
), "Message %s", long2string(mn_get_cur(msgmap
)));
6229 prompt
[sizeof(prompt
)-1] = '\0';
6231 if(open_printer(prompt
) < 0){
6232 if(MCMD_ISAGG(aopt
))
6233 restore_selected(msgmap
);
6241 tc
= format_titlebar();
6243 /* Print titlebar... */
6244 print_text1("%s\n\n", tc
? tc
->titlebar_line
: "");
6245 /* then all the index members... */
6246 if(!print_index(state
, msgmap
, MCMD_ISAGG(aopt
)))
6247 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
6248 _("Error printing folder index"));
6254 for(i
= mn_first_cur(msgmap
); i
> 0L; i
= mn_next_cur(msgmap
), next
++){
6255 if(next
&& F_ON(F_AGG_PRINT_FF
, state
))
6256 if(!print_char(FORMFEED
)){
6261 if(!(state
->mail_stream
6262 && (rawno
= mn_m2raw(msgmap
, i
)) > 0L
6263 && rawno
<= state
->mail_stream
->nmsgs
6264 && (mc
= mail_elt(state
->mail_stream
, rawno
))
6268 if(!(e
=pine_mail_fetchstructure(state
->mail_stream
,
6271 || (F_ON(F_FROM_DELIM_IN_PRINT
, ps_global
)
6272 && !bezerk_delimiter(e
, mc
, print_char
, next
))
6273 || !format_message(mn_m2raw(msgmap
, mn_get_cur(msgmap
)),
6274 e
, b
, NULL
, FM_NEW_MESS
| FM_NOINDENT
,
6276 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
6277 _("Error printing message"));
6286 if(MCMD_ISAGG(aopt
))
6287 restore_selected(msgmap
);
6293 /*----------------------------------------------------------------------
6296 Args: state -- various pine state bits
6297 msgmap -- Message number mapping table
6298 aopt -- option flags
6300 Filters the original header and sends stuff to specified command
6303 cmd_pipe(struct pine
*state
, MSGNO_S
*msgmap
, int aopt
)
6309 char *resultfilename
= NULL
, prompt
[80], *p
;
6310 int done
= 0, rv
= 0;
6312 int fourlabel
= -1, j
= 0, next
= 0, ku
;
6313 int pipe_rv
; /* rv of proc to separate from close_system_pipe rv */
6315 unsigned flagsforhist
= 1; /* raw=8/delimit=4/newpipe=2/capture=1 */
6316 static HISTORY_S
*history
= NULL
;
6317 int capture
= 1, raw
= 0, delimit
= 0, newpipe
= 0;
6318 char pipe_command
[MAXPATH
];
6319 ESCKEY_S pipe_opt
[8];
6321 if(ps_global
->restricted
){
6322 q_status_message(SM_ORDER
| SM_DING
, 0, 4,
6323 "Alpine demo can't pipe messages");
6326 else if(!any_messages(msgmap
, NULL
, "to Pipe"))
6329 pipe_command
[0] = '\0';
6330 init_hist(&history
, HISTSIZE
);
6331 flagsforhist
= (raw
? 0x8 : 0) +
6332 (delimit
? 0x4 : 0) +
6333 (newpipe
? 0x2 : 0) +
6334 (capture
? 0x1 : 0);
6335 if((p
= get_prev_hist(history
, "", flagsforhist
, NULL
)) != NULL
){
6336 strncpy(pipe_command
, p
, sizeof(pipe_command
));
6337 pipe_command
[sizeof(pipe_command
)-1] = '\0';
6338 if(history
->hist
[history
->curindex
]){
6339 flagsforhist
= history
->hist
[history
->curindex
]->flags
;
6340 raw
= (flagsforhist
& 0x8) ? 1 : 0;
6341 delimit
= (flagsforhist
& 0x4) ? 1 : 0;
6342 newpipe
= (flagsforhist
& 0x2) ? 1 : 0;
6343 capture
= (flagsforhist
& 0x1) ? 1 : 0;
6348 pipe_opt
[j
].rval
= 0;
6349 pipe_opt
[j
].name
= "";
6350 pipe_opt
[j
++].label
= "";
6352 pipe_opt
[j
].ch
= ctrl('W');
6353 pipe_opt
[j
].rval
= 10;
6354 pipe_opt
[j
].name
= "^W";
6355 pipe_opt
[j
++].label
= NULL
;
6357 pipe_opt
[j
].ch
= ctrl('Y');
6358 pipe_opt
[j
].rval
= 11;
6359 pipe_opt
[j
].name
= "^Y";
6360 pipe_opt
[j
++].label
= NULL
;
6362 pipe_opt
[j
].ch
= ctrl('R');
6363 pipe_opt
[j
].rval
= 12;
6364 pipe_opt
[j
].name
= "^R";
6365 pipe_opt
[j
++].label
= NULL
;
6367 if(MCMD_ISAGG(aopt
)){
6368 if(!pseudo_selected(state
->mail_stream
, msgmap
))
6372 pipe_opt
[j
].ch
= ctrl('T');
6373 pipe_opt
[j
].rval
= 13;
6374 pipe_opt
[j
].name
= "^T";
6375 pipe_opt
[j
++].label
= NULL
;
6379 pipe_opt
[j
].ch
= KEY_UP
;
6380 pipe_opt
[j
].rval
= 30;
6381 pipe_opt
[j
].name
= "";
6383 pipe_opt
[j
++].label
= "";
6385 pipe_opt
[j
].ch
= KEY_DOWN
;
6386 pipe_opt
[j
].rval
= 31;
6387 pipe_opt
[j
].name
= "";
6388 pipe_opt
[j
++].label
= "";
6390 pipe_opt
[j
].ch
= -1;
6395 snprintf(prompt
, sizeof(prompt
), "Pipe %smessage%s%s to %s%s%s%s%s%s%s: ",
6397 MCMD_ISAGG(aopt
) ? "s" : " ",
6398 MCMD_ISAGG(aopt
) ? "" : comatose(mn_get_cur(msgmap
)),
6399 (!capture
|| delimit
|| (newpipe
&& MCMD_ISAGG(aopt
))) ? "(" : "",
6400 capture
? "" : "uncaptured",
6401 (!capture
&& delimit
) ? "," : "",
6402 delimit
? "delimited" : "",
6403 ((!capture
|| delimit
) && newpipe
&& MCMD_ISAGG(aopt
)) ? "," : "",
6404 (newpipe
&& MCMD_ISAGG(aopt
)) ? "new pipe" : "",
6405 (!capture
|| delimit
|| (newpipe
&& MCMD_ISAGG(aopt
))) ? ") " : "");
6406 prompt
[sizeof(prompt
)-1] = '\0';
6407 pipe_opt
[1].label
= raw
? N_("Shown Text") : N_("Raw Text");
6408 pipe_opt
[2].label
= capture
? N_("Free Output") : N_("Capture Output");
6409 pipe_opt
[3].label
= delimit
? N_("No Delimiter") : N_("With Delimiter");
6411 pipe_opt
[fourlabel
].label
= newpipe
? N_("To Same Pipe") : N_("To Individual Pipes");
6415 * 2 is really 1 because there will be one real entry and
6416 * one entry of "" because of the get_prev_hist above.
6418 if(items_in_hist(history
) > 2){
6419 pipe_opt
[ku
].name
= HISTORY_UP_KEYNAME
;
6420 pipe_opt
[ku
].label
= HISTORY_KEYLABEL
;
6421 pipe_opt
[ku
+1].name
= HISTORY_DOWN_KEYNAME
;
6422 pipe_opt
[ku
+1].label
= HISTORY_KEYLABEL
;
6425 pipe_opt
[ku
].name
= "";
6426 pipe_opt
[ku
].label
= "";
6427 pipe_opt
[ku
+1].name
= "";
6428 pipe_opt
[ku
+1].label
= "";
6431 flags
= OE_APPEND_CURRENT
| OE_SEQ_SENSITIVE
;
6432 switch(optionally_enter(pipe_command
, -FOOTER_ROWS(state
), 0,
6433 sizeof(pipe_command
), prompt
,
6434 pipe_opt
, NO_HELP
, &flags
)){
6436 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
6437 _("Internal problem encountered"));
6441 case 10 : /* flip raw bit */
6445 case 11 : /* flip capture bit */
6449 case 12 : /* flip delimit bit */
6453 case 13 : /* flip newpipe bit */
6458 flagsforhist
= (raw
? 0x8 : 0) +
6459 (delimit
? 0x4 : 0) +
6460 (newpipe
? 0x2 : 0) +
6461 (capture
? 0x1 : 0);
6462 if((p
= get_prev_hist(history
, pipe_command
, flagsforhist
, NULL
)) != NULL
){
6463 strncpy(pipe_command
, p
, sizeof(pipe_command
));
6464 pipe_command
[sizeof(pipe_command
)-1] = '\0';
6465 if(history
->hist
[history
->curindex
]){
6466 flagsforhist
= history
->hist
[history
->curindex
]->flags
;
6467 raw
= (flagsforhist
& 0x8) ? 1 : 0;
6468 delimit
= (flagsforhist
& 0x4) ? 1 : 0;
6469 newpipe
= (flagsforhist
& 0x2) ? 1 : 0;
6470 capture
= (flagsforhist
& 0x1) ? 1 : 0;
6479 flagsforhist
= (raw
? 0x8 : 0) +
6480 (delimit
? 0x4 : 0) +
6481 (newpipe
? 0x2 : 0) +
6482 (capture
? 0x1 : 0);
6483 if((p
= get_next_hist(history
, pipe_command
, flagsforhist
, NULL
)) != NULL
){
6484 strncpy(pipe_command
, p
, sizeof(pipe_command
));
6485 pipe_command
[sizeof(pipe_command
)-1] = '\0';
6486 if(history
->hist
[history
->curindex
]){
6487 flagsforhist
= history
->hist
[history
->curindex
]->flags
;
6488 raw
= (flagsforhist
& 0x8) ? 1 : 0;
6489 delimit
= (flagsforhist
& 0x4) ? 1 : 0;
6490 newpipe
= (flagsforhist
& 0x2) ? 1 : 0;
6491 capture
= (flagsforhist
& 0x1) ? 1 : 0;
6500 if(pipe_command
[0]){
6502 flagsforhist
= (raw
? 0x8 : 0) +
6503 (delimit
? 0x4 : 0) +
6504 (newpipe
? 0x2 : 0) +
6505 (capture
? 0x1 : 0);
6506 save_hist(history
, pipe_command
, flagsforhist
, NULL
);
6508 flags
= PIPE_USER
| PIPE_WRITE
| PIPE_STDERR
;
6509 flags
|= (raw
? PIPE_RAW
: 0);
6515 ps_global
->mangled_screen
= 1;
6516 ps_global
->in_init_seq
= 1;
6518 flags
|= PIPE_RESET
;
6521 if(!newpipe
&& !(syspipe
= cmd_pipe_open(pipe_command
,
6522 (flags
& PIPE_RESET
)
6528 for(i
= mn_first_cur(msgmap
);
6530 i
= mn_next_cur(msgmap
)){
6531 e
= pine_mail_fetchstructure(ps_global
->mail_stream
,
6532 mn_m2raw(msgmap
, i
), &b
);
6533 if(!(state
->mail_stream
6534 && (rawno
= mn_m2raw(msgmap
, i
)) > 0L
6535 && rawno
<= state
->mail_stream
->nmsgs
6536 && (mc
= mail_elt(state
->mail_stream
, rawno
))
6541 && !(syspipe
= cmd_pipe_open(pipe_command
,
6542 (flags
& PIPE_RESET
)
6546 || (delimit
&& !bezerk_delimiter(e
, mc
, pc
, next
++)))
6553 prime_raw_pipe_getc(ps_global
->mail_stream
,
6554 mn_m2raw(msgmap
, i
), -1L, 0L);
6556 gf_link_filter(gf_nvtnl_local
, NULL
);
6557 if((pipe_err
= gf_pipe(raw_pipe_getc
, pc
)) != NULL
){
6558 q_status_message1(SM_ORDER
|SM_DING
,
6560 _("Internal Error: %s"),
6565 else if(!format_message(mn_m2raw(msgmap
, i
), e
, b
,
6566 NULL
, FM_NEW_MESS
| FM_NOWRAP
, pc
))
6571 if(close_system_pipe(&syspipe
, &pipe_rv
, pipe_callback
) == -1)
6576 ps_global
->in_init_seq
= 0;
6579 if(close_system_pipe(&syspipe
, &pipe_rv
, pipe_callback
) == -1)
6581 if(done
) /* say we had a problem */
6582 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
6583 _("Error piping message"));
6584 else if(resultfilename
){
6586 /* only display if no error */
6587 display_output_file(resultfilename
, "PIPE MESSAGE",
6589 fs_give((void **)&resultfilename
);
6593 q_status_message(SM_ORDER
, 0, 2, _("Pipe command completed"));
6599 /* else fall thru as if cancelled */
6602 cmd_cancelled("Pipe command");
6607 helper(h_common_pipe
, _("HELP FOR PIPE COMMAND"), HLPD_SIMPLE
);
6608 ps_global
->mangled_screen
= 1;
6611 case 2 : /* no place to escape to */
6612 case 4 : /* can't suspend */
6618 ps_global
->mangled_footer
= 1;
6619 if(MCMD_ISAGG(aopt
))
6620 restore_selected(msgmap
);
6626 /*----------------------------------------------------------------------
6627 Screen to offer list management commands contained in message
6629 Args: state -- pointer to struct holding a bunch of pine state
6630 msgmap -- table mapping msg nums to c-client sequence nums
6631 aopt -- aggregate options
6635 NOTE: Inspired by contrib from Jeremy Blackman <loki@maison-otaku.net>
6638 rfc2369_display(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, long int msgno
)
6641 char *h
, *hdrs
[MLCMD_COUNT
+ 1];
6642 long index_no
= mn_raw2m(msgmap
, msgno
);
6643 RFC2369_S data
[MLCMD_COUNT
];
6645 /* for each header field */
6646 if((h
= pine_fetchheader_lines(stream
, msgno
, NULL
, rfc2369_hdrs(hdrs
))) != NULL
){
6647 memset(&data
[0], 0, sizeof(RFC2369_S
) * MLCMD_COUNT
);
6648 if(rfc2369_parse_fields(h
, &data
[0])){
6651 if((explain
= list_mgmt_text(data
, index_no
)) != NULL
){
6652 list_mgmt_screen(explain
);
6653 ps_global
->mangled_screen
= 1;
6659 fs_give((void **) &h
);
6663 q_status_message1(SM_ORDER
, 0, 3,
6664 "Message %s contains no list management information",
6665 comatose(index_no
));
6670 list_mgmt_text(RFC2369_S
*data
, long int msgno
)
6673 int i
, j
, n
, fields
= 0;
6674 static char *rfc2369_intro1
=
6675 "<HTML><HEAD></HEAD><BODY><H1>Mail List Commands</H1>Message ";
6676 static char *rfc2369_intro2
[] = {
6677 N_(" has information associated with it "),
6678 N_("that explains how to participate in an email list. An "),
6679 N_("email list is represented by a single email address that "),
6680 N_("users sharing a common interest can send messages to (known "),
6681 N_("as posting) which are then redistributed to all members "),
6682 N_("of the list (sometimes after review by a moderator)."),
6683 N_("<P>List participation commands in this message include:"),
6687 if((store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
){
6689 /* Insert introductory text */
6690 so_puts(store
, rfc2369_intro1
);
6692 so_puts(store
, comatose(msgno
));
6694 for(i
= 0; rfc2369_intro2
[i
]; i
++)
6695 so_puts(store
, _(rfc2369_intro2
[i
]));
6697 so_puts(store
, "<P>");
6698 for(i
= 0; i
< MLCMD_COUNT
; i
++)
6699 if(data
[i
].data
[0].value
6700 || data
[i
].data
[0].comment
6701 || data
[i
].data
[0].error
){
6703 so_puts(store
, "<UL>");
6705 so_puts(store
, "<LI>");
6707 (n
= (data
[i
].data
[1].value
|| data
[i
].data
[1].comment
))
6711 so_puts(store
, data
[i
].field
.description
);
6712 so_puts(store
, ". ");
6715 so_puts(store
, "<OL>");
6719 && (data
[i
].data
[j
].comment
6720 || data
[i
].data
[j
].value
6721 || data
[i
].data
[j
].error
);
6724 so_puts(store
, n
? "<P><LI>" : "<P>");
6726 if(data
[i
].data
[j
].comment
){
6728 _("With the provided comment:<P><BLOCKQUOTE>"));
6729 so_puts(store
, data
[i
].data
[j
].comment
);
6730 so_puts(store
, "</BLOCKQUOTE><P>");
6733 if(data
[i
].data
[j
].value
){
6735 && !strucmp(data
[i
].data
[j
].value
, "NO")){
6737 _("Posting is <EM>not</EM> allowed on this list"));
6740 so_puts(store
, "Select <A HREF=\"");
6741 so_puts(store
, data
[i
].data
[j
].value
);
6742 so_puts(store
, "\">HERE</A> to ");
6743 so_puts(store
, (data
[i
].field
.action
)
6744 ? data
[i
].field
.action
6748 so_puts(store
, ".");
6751 if(data
[i
].data
[j
].error
){
6752 so_puts(store
, "<P>Unfortunately, Alpine can not offer");
6753 so_puts(store
, " to take direct action based upon it");
6754 so_puts(store
, " because it was improperly formatted.");
6755 so_puts(store
, " The unrecognized data associated with");
6756 so_puts(store
, " the \"");
6757 so_puts(store
, data
[i
].field
.name
);
6758 so_puts(store
, "\" header field was:<P><BLOCKQUOTE>");
6759 so_puts(store
, data
[i
].data
[j
].error
);
6760 so_puts(store
, "</BLOCKQUOTE>");
6763 so_puts(store
, "<P>");
6767 so_puts(store
, "</OL>");
6771 so_puts(store
, "</UL>");
6773 so_puts(store
, "</BODY></HTML>");
6781 list_mgmt_screen(STORE_S
*html
)
6787 HANDLE_S
*handles
= NULL
;
6791 so_seek(html
, 0L, 0);
6792 gf_set_so_readc(&gc
, html
);
6794 init_handles(&handles
);
6796 if((store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
){
6797 gf_set_so_writec(&pc
, store
);
6800 gf_link_filter(gf_html2plain
,
6801 gf_html2plain_opt(NULL
, ps_global
->ttyo
->screen_cols
,
6802 non_messageview_margin(), &handles
, NULL
, 0));
6804 error
= gf_pipe(gc
, pc
);
6806 gf_clear_so_writec(store
);
6811 memset(&sargs
, 0, sizeof(SCROLL_S
));
6812 sargs
.text
.text
= so_text(store
);
6813 sargs
.text
.src
= CharStar
;
6814 sargs
.text
.desc
= "list commands";
6815 sargs
.text
.handles
= handles
;
6817 sargs
.start
.on
= Offset
;
6818 sargs
.start
.loc
.offset
= offset
;
6821 sargs
.bar
.title
= _("MAIL LIST COMMANDS");
6822 sargs
.bar
.style
= MessageNumber
;
6823 sargs
.resize_exit
= 1;
6824 sargs
.help
.text
= h_special_list_commands
;
6825 sargs
.help
.title
= _("HELP FOR LIST COMMANDS");
6826 sargs
.keys
.menu
= &listmgr_keymenu
;
6827 setbitmap(sargs
.keys
.bitmap
);
6829 clrbitn(LM_TRY_KEY
, sargs
.keys
.bitmap
);
6830 clrbitn(LM_PREV_KEY
, sargs
.keys
.bitmap
);
6831 clrbitn(LM_NEXT_KEY
, sargs
.keys
.bitmap
);
6834 cmd
= scrolltool(&sargs
);
6835 offset
= sargs
.start
.loc
.offset
;
6841 free_handles(&handles
);
6842 gf_clear_so_readc(html
);
6844 while(cmd
== MC_RESIZE
);
6848 /*----------------------------------------------------------------------
6849 Prompt the user for the type of select desired
6851 NOTE: any and all functions that successfully exit the second
6852 switch() statement below (currently "select_*() functions"),
6853 *MUST* update the folder's MESSAGECACHE element's "searched"
6854 bits to reflect the search result. Functions using
6855 mail_search() get this for free, the others must update 'em
6858 Returns -1 if canceled without changing selection
6859 0 if selection may have changed
6862 aggregate_select(struct pine
*state
, MSGNO_S
*msgmap
, int q_line
, CmdWhere in_index
)
6864 long i
, diff
, old_tot
, msgno
, raw
;
6865 int q
= 0, rv
= 0, narrow
= 0, hidden
, ret
= -1;
6868 SEARCHSET
*limitsrch
= NULL
;
6870 extern MAILSTREAM
*mm_search_stream
;
6871 extern long mm_search_count
;
6873 hidden
= any_lflagged(msgmap
, MN_HIDE
) > 0L;
6874 mm_search_stream
= state
->mail_stream
;
6875 mm_search_count
= 0L;
6877 sel_opts
= THRD_INDX() ? sel_opts4
: sel_opts2
;
6879 sel_opts
[SEL_OPTS_THREAD
].ch
= SEL_OPTS_THREAD_CH
;
6882 sel_opts
[SEL_OPTS_THREAD
].ch
= -1;
6885 if((old_tot
= any_lflagged(msgmap
, MN_SLCT
)) != 0){
6888 thrd
= fetch_thread(state
->mail_stream
,
6889 mn_m2raw(msgmap
, mn_get_cur(msgmap
)));
6890 /* check if whole thread is selected or not */
6892 count_lflags_in_thread(state
->mail_stream
,thrd
,msgmap
,MN_SLCT
)
6894 count_lflags_in_thread(state
->mail_stream
,thrd
,msgmap
,MN_NONE
))
6897 sel_opts1
[1].label
= i
? N_("unselect Curthrd") : N_("select Curthrd");
6900 i
= get_lflag(state
->mail_stream
, msgmap
, mn_get_cur(msgmap
),
6902 sel_opts1
[1].label
= i
? N_("unselect Cur") : N_("select Cur");
6905 sel_opts
+= 2; /* disable extra options */
6906 switch(q
= radio_buttons(_(sel_pmt1
), q_line
, sel_opts1
, 'c', 'x', NO_HELP
,
6908 case 'f' : /* flip selection */
6910 for(i
= 1L; i
<= mn_get_total(msgmap
); i
++){
6912 q
= !get_lflag(state
->mail_stream
, msgmap
, i
, MN_SLCT
);
6913 set_lflag(state
->mail_stream
, msgmap
, i
, MN_SLCT
, q
);
6915 set_lflag(state
->mail_stream
, msgmap
, i
, MN_HIDE
, !q
);
6917 mn_reset_cur(msgmap
, msgno
= i
);
6923 case 'n' : /* narrow selection */
6925 case 'b' : /* broaden selection */
6926 q
= 0; /* offer criteria prompt */
6929 case 'c' : /* Un/Select Current */
6930 case 'a' : /* Unselect All */
6931 case 'x' : /* cancel */
6935 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
6936 "Unsupported Select option");
6943 q
= radio_buttons(sel_pmt2
, q_line
, sel_opts
, 'c', 'x',
6944 NO_HELP
, RB_NORM
|RB_RET_HELP
);
6947 helper(h_index_cmd_select
, _("HELP FOR SELECT"), HLPD_SIMPLE
);
6948 ps_global
->mangled_screen
= 1;
6956 * The purpose of this is to add the appropriate searchset to the
6957 * search so that the search can be limited to only looking at what
6958 * it needs to look at. That is, if we are narrowing then we only need
6959 * to look at messages which are already selected, and if we are
6960 * broadening, then we only need to look at messages which are not
6961 * yet selected. This routine will work whether or not
6962 * limiting_searchset properly limits the search set. In particular,
6963 * the searchset returned by limiting_searchset may include messages
6964 * which really shouldn't be included. We do that because a too-large
6965 * searchset will break some IMAP servers. It is even possible that it
6966 * becomes inefficient to send the whole set. If the select function
6967 * frees limitsrch, it should be sure to set it to NULL so we won't
6968 * try freeing it again here.
6970 limitsrch
= limiting_searchset(state
->mail_stream
, narrow
);
6973 * NOTE: See note about MESSAGECACHE "searched" bits above!
6976 case 'x': /* cancel */
6977 cmd_cancelled("Select command");
6980 case 'c' : /* select/unselect current */
6981 (void) select_by_current(state
, msgmap
, in_index
);
6985 case 'a' : /* select/unselect all */
6986 msgno
= any_lflagged(msgmap
, MN_SLCT
);
6987 diff
= (!msgno
) ? mn_get_total(msgmap
) : 0L;
6989 agg_select_all(state
->mail_stream
, msgmap
, &diff
,
6990 any_lflagged(msgmap
, MN_SLCT
) <= 0L);
6991 q_status_message4(SM_ORDER
,0,2,
6992 "%s%s message%s %sselected",
6993 msgno
? "" : "All ", comatose(diff
),
6994 plural(diff
), msgno
? "UN" : "");
6997 case 'n' : /* Select by Number */
7000 rv
= select_by_thrd_number(state
->mail_stream
, msgmap
, &limitsrch
);
7002 rv
= select_by_number(state
->mail_stream
, msgmap
, &limitsrch
);
7006 case 'd' : /* Select by Date */
7008 rv
= select_by_date(state
->mail_stream
, msgmap
, mn_get_cur(msgmap
),
7012 case 't' : /* Text */
7014 rv
= select_by_text(state
->mail_stream
, msgmap
, mn_get_cur(msgmap
),
7018 case 'z' : /* Size */
7020 rv
= select_by_size(state
->mail_stream
, &limitsrch
);
7023 case 's' : /* Status */
7025 rv
= select_by_status(state
->mail_stream
, &limitsrch
);
7028 case 'k' : /* Keyword */
7030 rv
= select_by_keyword(state
->mail_stream
, &limitsrch
);
7033 case 'r' : /* Rule */
7035 rv
= select_by_rule(state
->mail_stream
, &limitsrch
);
7038 case 'h' : /* Thread */
7040 rv
= select_by_thread(state
->mail_stream
, msgmap
, &limitsrch
);
7044 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
7045 "Unsupported Select option");
7050 mail_free_searchset(&limitsrch
);
7052 if(rv
) /* bad return value.. */
7053 return(ret
); /* error already displayed */
7055 if(narrow
) /* make sure something was selected */
7056 for(i
= 1L; i
<= mn_get_total(msgmap
); i
++)
7057 if((raw
= mn_m2raw(msgmap
, i
)) > 0L && state
->mail_stream
7058 && raw
<= state
->mail_stream
->nmsgs
7059 && (mc
= mail_elt(state
->mail_stream
, raw
)) && mc
->searched
){
7060 if(get_lflag(state
->mail_stream
, msgmap
, i
, MN_SLCT
))
7067 if(mm_search_count
){
7069 * loop thru all the messages, adjusting local flag bits
7070 * based on their "searched" bit...
7072 for(i
= 1L, msgno
= 0L; i
<= mn_get_total(msgmap
); i
++)
7074 /* turning OFF selectedness if the "searched" bit isn't lit. */
7075 if(get_lflag(state
->mail_stream
, msgmap
, i
, MN_SLCT
)){
7076 if((raw
= mn_m2raw(msgmap
, i
)) > 0L && state
->mail_stream
7077 && raw
<= state
->mail_stream
->nmsgs
7078 && (mc
= mail_elt(state
->mail_stream
, raw
))
7081 set_lflag(state
->mail_stream
, msgmap
, i
, MN_SLCT
, 0);
7083 set_lflag(state
->mail_stream
, msgmap
, i
, MN_HIDE
, 1);
7085 /* adjust current message in case we unselect and hide it */
7086 else if(msgno
< mn_get_cur(msgmap
)
7088 || !get_lflag(state
->mail_stream
, msgmap
,
7093 else if((raw
= mn_m2raw(msgmap
, i
)) > 0L && state
->mail_stream
7094 && raw
<= state
->mail_stream
->nmsgs
7095 && (mc
= mail_elt(state
->mail_stream
, raw
)) && mc
->searched
){
7096 /* turn ON selectedness if "searched" bit is lit. */
7097 if(!get_lflag(state
->mail_stream
, msgmap
, i
, MN_SLCT
)){
7099 set_lflag(state
->mail_stream
, msgmap
, i
, MN_SLCT
, 1);
7101 set_lflag(state
->mail_stream
, msgmap
, i
, MN_HIDE
, 0);
7105 /* if we're zoomed and the current message was unselected */
7107 && get_lflag(state
->mail_stream
,msgmap
,mn_get_cur(msgmap
),MN_HIDE
))
7108 mn_reset_cur(msgmap
, msgno
);
7113 q_status_message4(SM_ORDER
, 3, 3,
7114 "%s. %s message%s remain%s selected.",
7116 ? "No change resulted"
7117 : "No messages in intersection",
7118 comatose(old_tot
), plural(old_tot
),
7119 (old_tot
== 1L) ? "s" : "");
7121 q_status_message(SM_ORDER
, 3, 3,
7122 _("No change resulted. Matching messages already selected."));
7124 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
7125 _("Select failed. No %smessages selected."),
7126 old_tot
? _("additional ") : "");
7129 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,
7130 "Select matched %ld message%s. %s %smessage%s %sselected.",
7131 (diff
> 0) ? diff
: old_tot
+ diff
,
7132 plural((diff
> 0) ? diff
: old_tot
+ diff
),
7133 comatose((diff
> 0) ? any_lflagged(msgmap
, MN_SLCT
) : -diff
),
7134 (diff
> 0) ? "total " : "",
7135 plural((diff
> 0) ? any_lflagged(msgmap
, MN_SLCT
) : -diff
),
7136 (diff
> 0) ? "" : "UN");
7137 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
7138 q_status_message(SM_ORDER
, 3, 3, tmp_20k_buf
);
7141 q_status_message2(SM_ORDER
, 3, 3, _("Select matched %s message%s!"),
7142 comatose(diff
), plural(diff
));
7148 /*----------------------------------------------------------------------
7149 Toggle the state of the current message
7151 Args: state -- pointer pine's state variables
7152 msgmap -- message collection to operate on
7153 in_index -- in the message index view
7154 Returns: TRUE if current marked selected, FALSE otw
7157 select_by_current(struct pine
*state
, MSGNO_S
*msgmap
, CmdWhere in_index
)
7160 int all_selected
= 0;
7161 unsigned long was
, tot
, rawno
;
7164 cur
= mn_get_cur(msgmap
);
7167 thrd
= fetch_thread(state
->mail_stream
, mn_m2raw(msgmap
, cur
));
7171 was
= count_lflags_in_thread(state
->mail_stream
, thrd
, msgmap
, MN_SLCT
);
7172 tot
= count_lflags_in_thread(state
->mail_stream
, thrd
, msgmap
, MN_NONE
);
7177 set_thread_lflags(state
->mail_stream
, thrd
, msgmap
, MN_SLCT
, 0);
7178 if(any_lflagged(msgmap
, MN_HIDE
) > 0L){
7179 set_thread_lflags(state
->mail_stream
, thrd
, msgmap
, MN_HIDE
, 1);
7181 * See if there's anything left to zoom on. If so,
7182 * pick an adjacent one for highlighting, else make
7183 * sure nothing is left hidden...
7185 if(any_lflagged(msgmap
, MN_SLCT
)){
7186 mn_inc_cur(state
->mail_stream
, msgmap
,
7187 (in_index
== View
&& THREADING()
7188 && sp_viewing_a_thread(state
->mail_stream
))
7190 : (in_index
== View
)
7191 ? MH_ANYTHD
: MH_NONE
);
7192 if(mn_get_cur(msgmap
) == cur
)
7193 mn_dec_cur(state
->mail_stream
, msgmap
,
7194 (in_index
== View
&& THREADING()
7195 && sp_viewing_a_thread(state
->mail_stream
))
7197 : (in_index
== View
)
7198 ? MH_ANYTHD
: MH_NONE
);
7200 else /* clear all hidden flags */
7201 (void) unzoom_index(state
, state
->mail_stream
, msgmap
);
7205 set_thread_lflags(state
->mail_stream
, thrd
, msgmap
, MN_SLCT
, 1);
7207 q_status_message3(SM_ORDER
, 0, 2, "%s message%s %sselected",
7208 comatose(all_selected
? was
: tot
-was
),
7209 plural(all_selected
? was
: tot
-was
),
7210 all_selected
? "UN" : "");
7212 /* collapsed thread */
7214 && ((rawno
= mn_m2raw(msgmap
, cur
)) != 0L)
7215 && ((thrd
= fetch_thread(state
->mail_stream
, rawno
)) != NULL
)
7216 && (thrd
&& thrd
->next
&& get_lflag(state
->mail_stream
, NULL
, rawno
, MN_COLL
))){
7218 * This doesn't work quite the same as the colon command works, but
7219 * it is arguably doing the correct thing. The difference is
7220 * that aggregate_select will zoom after selecting back where it
7221 * was called from, but selecting a thread with colon won't zoom.
7222 * Maybe it makes sense to zoom after a select but not after a colon
7223 * command even though they are very similar.
7225 thread_command(state
, state
->mail_stream
, msgmap
, ':', -FOOTER_ROWS(state
));
7229 get_lflag(state
->mail_stream
, msgmap
, cur
, MN_SLCT
)) != 0){ /* set? */
7230 set_lflag(state
->mail_stream
, msgmap
, cur
, MN_SLCT
, 0);
7231 if(any_lflagged(msgmap
, MN_HIDE
) > 0L){
7232 set_lflag(state
->mail_stream
, msgmap
, cur
, MN_HIDE
, 1);
7234 * See if there's anything left to zoom on. If so,
7235 * pick an adjacent one for highlighting, else make
7236 * sure nothing is left hidden...
7238 if(any_lflagged(msgmap
, MN_SLCT
)){
7239 mn_inc_cur(state
->mail_stream
, msgmap
,
7240 (in_index
== View
&& THREADING()
7241 && sp_viewing_a_thread(state
->mail_stream
))
7243 : (in_index
== View
)
7244 ? MH_ANYTHD
: MH_NONE
);
7245 if(mn_get_cur(msgmap
) == cur
)
7246 mn_dec_cur(state
->mail_stream
, msgmap
,
7247 (in_index
== View
&& THREADING()
7248 && sp_viewing_a_thread(state
->mail_stream
))
7250 : (in_index
== View
)
7251 ? MH_ANYTHD
: MH_NONE
);
7253 else /* clear all hidden flags */
7254 (void) unzoom_index(state
, state
->mail_stream
, msgmap
);
7258 set_lflag(state
->mail_stream
, msgmap
, cur
, MN_SLCT
, 1);
7260 q_status_message2(SM_ORDER
, 0, 2, "Message %s %sselected",
7261 long2string(cur
), all_selected
? "UN" : "");
7265 return(!all_selected
);
7269 /*----------------------------------------------------------------------
7270 Prompt the user for the command to perform on selected messages
7272 Args: state -- pointer pine's state variables
7273 msgmap -- message collection to operate on
7274 q_line -- line on display to write prompts
7275 Returns: 1 if the selected messages are suitably commanded,
7276 0 if the choice to pick the command was declined
7280 apply_command(struct pine
*state
, MAILSTREAM
*stream
, MSGNO_S
*msgmap
,
7281 UCS preloadkeystroke
, int flags
, int q_line
)
7283 int i
= 8, /* number of static entries in sel_opts3 */
7287 agg
= (flags
& AC_FROM_THREAD
) ? MCMD_AGG_2
: MCMD_AGG
;
7292 * To do this "right", we really ought to have access to the keymenu
7293 * here and change the typed command into a real command by running
7294 * it through menu_command. Then the switch below would be against
7295 * results from menu_command. If we did that we'd also pass the
7296 * results of menu_command in as preloadkeystroke instead of passing
7297 * the keystroke itself. But we don't have the keymenu handy,
7298 * so we have to fake it. The only complication that we run into
7299 * is that KEY_DEL is an escape sequence so we change a typed
7300 * KEY_DEL esc seq into the letter D.
7303 if(!preloadkeystroke
){
7304 if(F_ON(F_ENABLE_FLAG
,state
)){ /* flag? */
7305 sel_opts3
[i
].ch
= '*';
7306 sel_opts3
[i
].rval
= '*';
7307 sel_opts3
[i
].name
= "*";
7308 sel_opts3
[i
++].label
= N_("Flag");
7311 if(F_ON(F_ENABLE_PIPE
,state
)){ /* pipe? */
7312 sel_opts3
[i
].ch
= '|';
7313 sel_opts3
[i
].rval
= '|';
7314 sel_opts3
[i
].name
= "|";
7315 sel_opts3
[i
++].label
= N_("Pipe");
7318 if(F_ON(F_ENABLE_BOUNCE
,state
)){ /* bounce? */
7319 sel_opts3
[i
].ch
= 'b';
7320 sel_opts3
[i
].rval
= 'b';
7321 sel_opts3
[i
].name
= "B";
7322 sel_opts3
[i
++].label
= N_("Bounce");
7325 if(flags
& AC_FROM_THREAD
){
7326 if(flags
& (AC_COLL
| AC_EXPN
)){
7327 sel_opts3
[i
].ch
= '/';
7328 sel_opts3
[i
].rval
= '/';
7329 sel_opts3
[i
].name
= "/";
7330 sel_opts3
[i
++].label
= (flags
& AC_COLL
) ? N_("Collapse")
7334 sel_opts3
[i
].ch
= ';';
7335 sel_opts3
[i
].rval
= ';';
7336 sel_opts3
[i
].name
= ";";
7337 if(flags
& AC_UNSEL
)
7338 sel_opts3
[i
++].label
= N_("UnSelect");
7340 sel_opts3
[i
++].label
= N_("Select");
7343 if(F_ON(F_ENABLE_PRYNT
, state
)){ /* this one is invisible */
7344 sel_opts3
[i
].ch
= 'y';
7345 sel_opts3
[i
].rval
= '%';
7346 sel_opts3
[i
].name
= "";
7347 sel_opts3
[i
++].label
= "";
7350 if(!is_imap_stream(stream
) || LEVELUIDPLUS(stream
)){ /* expunge selected messages */
7351 sel_opts3
[i
].ch
= 'x';
7352 sel_opts3
[i
].rval
= 'x';
7353 sel_opts3
[i
].name
= "X";
7354 sel_opts3
[i
++].label
= N_("Expunge");
7357 if(nonempty_patterns(ROLE_DO_ROLES
, &pstate
) && first_pattern(&pstate
)){
7358 sel_opts3
[i
].ch
= '#';
7359 sel_opts3
[i
].rval
= '#';
7360 sel_opts3
[i
].name
= "#";
7361 sel_opts3
[i
++].label
= N_("Set Role");
7364 sel_opts3
[i
].ch
= KEY_DEL
; /* also invisible */
7365 sel_opts3
[i
].rval
= 'd';
7366 sel_opts3
[i
].name
= "";
7367 sel_opts3
[i
++].label
= "";
7369 sel_opts3
[i
].ch
= -1;
7371 snprintf(prompt
, sizeof(prompt
), "%s command : ",
7372 (flags
& AC_FROM_THREAD
) ? "THREAD" : "APPLY");
7373 prompt
[sizeof(prompt
)-1] = '\0';
7374 cmd
= double_radio_buttons(prompt
, q_line
, sel_opts3
, 'z', 'c', NO_HELP
,
7380 if(preloadkeystroke
== KEY_DEL
)
7383 if(preloadkeystroke
< 0x80 && isupper((int) preloadkeystroke
))
7384 cmd
= tolower((int) preloadkeystroke
);
7386 cmd
= (int) preloadkeystroke
; /* shouldn't happen */
7391 case 'd' : /* delete */
7392 we_cancel
= busy_cue(NULL
, NULL
, 1);
7393 rv
= cmd_delete(state
, msgmap
, agg
, NULL
); /* don't advance or offer "TAB" */
7398 case 'u' : /* undelete */
7399 we_cancel
= busy_cue(NULL
, NULL
, 1);
7400 rv
= cmd_undelete(state
, msgmap
, agg
);
7405 case 'r' : /* reply */
7406 rv
= cmd_reply(state
, msgmap
, agg
, NULL
);
7409 case 'f' : /* Forward */
7410 rv
= cmd_forward(state
, msgmap
, agg
, NULL
);
7413 case '%' : /* print */
7414 rv
= cmd_print(state
, msgmap
, agg
, MsgIndx
);
7417 case 't' : /* take address */
7418 rv
= cmd_take_addr(state
, msgmap
, agg
);
7421 case 's' : /* save */
7422 rv
= cmd_save(state
, stream
, msgmap
, agg
, MsgIndx
);
7425 case 'e' : /* export */
7426 rv
= cmd_export(state
, msgmap
, q_line
, agg
);
7429 case '|' : /* pipe */
7430 rv
= cmd_pipe(state
, msgmap
, agg
);
7433 case '*' : /* flag */
7434 we_cancel
= busy_cue(NULL
, NULL
, 1);
7435 rv
= cmd_flag(state
, msgmap
, agg
);
7440 case 'b' : /* bounce */
7441 rv
= cmd_bounce(state
, msgmap
, agg
, NULL
);
7445 collapse_or_expand(state
, stream
, msgmap
,
7446 F_ON(F_SLASH_COLL_ENTIRE
, ps_global
)
7448 : mn_get_cur(msgmap
));
7452 select_thread_stmp(state
, stream
, msgmap
);
7455 case 'x' : /* Expunge */
7456 rv
= cmd_expunge(state
, stream
, msgmap
, agg
);
7459 case 'c' : /* cancel */
7460 cmd_cancelled((flags
& AC_FROM_THREAD
) ? "Thread command"
7465 if(nonempty_patterns(ROLE_DO_ROLES
, &pstate
) && first_pattern(&pstate
)){
7466 static ESCKEY_S choose_role
[] = {
7467 {'r', 'r', "R", N_("Reply")},
7468 {'f', 'f', "F", N_("Forward")},
7469 {'b', 'b', "B", N_("Bounce")},
7473 ACTION_S
*role
= NULL
;
7475 action
= radio_buttons(_("Reply, Forward or Bounce using a role? "),
7476 -FOOTER_ROWS(state
), choose_role
,
7477 'r', 'x', h_role_aggregate
, RB_NORM
);
7478 if(action
== 'r' || action
== 'f' || action
== 'b'){
7479 void (*prev_screen
)(struct pine
*) = NULL
, (*redraw
)(void) = NULL
;
7481 redraw
= state
->redrawer
;
7482 state
->redrawer
= NULL
;
7483 prev_screen
= state
->prev_screen
;
7485 state
->next_screen
= SCREEN_FUN_NULL
;
7487 if(role_select_screen(state
, &role
,
7488 action
== 'f' ? MC_FORWARD
:
7489 action
== 'r' ? MC_REPLY
:
7490 action
== 'b' ? MC_BOUNCE
: 0) < 0){
7491 cmd_cancelled(action
== 'f' ? _("Forward") :
7492 action
== 'r' ? _("Reply") : _("Bounce"));
7493 state
->next_screen
= prev_screen
;
7494 state
->redrawer
= redraw
;
7495 state
->mangled_screen
= 1;
7499 role
= combine_inherited_role(role
);
7501 role
= (ACTION_S
*) fs_get(sizeof(*role
));
7502 memset((void *) role
, 0, sizeof(*role
));
7503 role
->nick
= cpystr("Default Role");
7506 state
->redrawer
= NULL
;
7509 (void) cmd_reply(state
, msgmap
, agg
, role
);
7513 (void) cmd_forward(state
, msgmap
, agg
, role
);
7517 (void) cmd_bounce(state
, msgmap
, agg
, role
);
7527 state
->next_screen
= prev_screen
;
7528 state
->redrawer
= redraw
;
7529 state
->mangled_screen
= 1;
7535 case 'z' : /* default */
7536 q_status_message(SM_INFO
, 0, 2,
7537 "Cancelled, there is no default command");
7549 * Select by message number ranges.
7550 * Sets searched bits in mail_elts
7552 * Args limitsrch -- limit search to this searchset
7554 * Returns 0 on success.
7557 select_by_number(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, SEARCHSET
**limitsrch
)
7561 char number1
[16], number2
[16], numbers
[80], *p
, *t
;
7566 ps_global
->mangled_footer
= 1;
7569 int flags
= OE_APPEND_CURRENT
;
7571 r
= optionally_enter(numbers
, -FOOTER_ROWS(ps_global
), 0,
7572 sizeof(numbers
), _(select_num
), NULL
, help
, &flags
);
7577 help
= (help
== NO_HELP
) ? h_select_by_num
: NO_HELP
;
7581 for(t
= p
= numbers
; *p
; p
++) /* strip whitespace */
7582 if(!isspace((unsigned char)*p
))
7587 if(r
== 1 || numbers
[0] == '\0'){
7588 cmd_cancelled("Selection by number");
7595 for(n1
= 1; n1
<= stream
->nmsgs
; n1
++)
7596 if((mc
= mail_elt(stream
, n1
)) != NULL
)
7597 mc
->searched
= 0; /* clear searched bits */
7599 for(p
= numbers
; *p
; p
++){
7601 while(*p
&& isdigit((unsigned char)*p
))
7607 if(number1
[0] == '\0'){
7609 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
7610 _("Invalid number range, missing number before \"-\": %s"),
7614 else if(!strucmp("end", p
)){
7618 else if(!strucmp("$", p
)){
7627 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
7628 _("Invalid message number: %s"), numbers
);
7634 n1
= mn_get_total(msgmap
);
7636 n1
= mn_get_cur(msgmap
);
7637 else if((n1
= atol(number1
)) < 1L || n1
> mn_get_total(msgmap
)){
7638 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
7639 _("\"%s\" out of message number range"),
7646 while(*++p
&& isdigit((unsigned char)*p
))
7652 if(number2
[0] == '\0'){
7653 if(!strucmp("end", p
)){
7657 else if(!strucmp(p
, "$")){
7666 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
7667 _("Invalid number range, missing number after \"-\": %s"),
7674 n2
= mn_get_total(msgmap
);
7676 n2
= mn_get_cur(msgmap
);
7677 else if((n2
= atol(number2
)) < 1L || n2
> mn_get_total(msgmap
)){
7678 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
7679 _("\"%s\" out of message number range"),
7687 strncpy(t
, long2string(n1
), sizeof(t
));
7688 t
[sizeof(t
)-1] = '\0';
7689 q_status_message2(SM_ORDER
| SM_DING
, 0, 2,
7690 _("Invalid reverse message number range: %s-%s"),
7691 t
, long2string(n2
));
7695 for(;n1
<= n2
; n1
++){
7696 raw
= mn_m2raw(msgmap
, n1
);
7698 && (!(limitsrch
&& *limitsrch
)
7699 || in_searchset(*limitsrch
, (unsigned long) raw
)))
7700 mm_searched(stream
, raw
);
7704 raw
= mn_m2raw(msgmap
, n1
);
7706 && (!(limitsrch
&& *limitsrch
)
7707 || in_searchset(*limitsrch
, (unsigned long) raw
)))
7708 mm_searched(stream
, raw
);
7720 * Select by thread number ranges.
7721 * Sets searched bits in mail_elts
7723 * Args limitsrch -- limit search to this searchset
7725 * Returns 0 on success.
7728 select_by_thrd_number(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, SEARCHSET
**msgset
)
7732 char number1
[16], number2
[16], numbers
[80], *p
, *t
;
7734 PINETHRD_S
*thrd
= NULL
, *th
;
7738 ps_global
->mangled_footer
= 1;
7741 int flags
= OE_APPEND_CURRENT
;
7743 r
= optionally_enter(numbers
, -FOOTER_ROWS(ps_global
), 0,
7744 sizeof(numbers
), _(select_num
), NULL
, help
, &flags
);
7749 help
= (help
== NO_HELP
) ? h_select_by_thrdnum
: NO_HELP
;
7753 for(t
= p
= numbers
; *p
; p
++) /* strip whitespace */
7754 if(!isspace((unsigned char)*p
))
7759 if(r
== 1 || numbers
[0] == '\0'){
7760 cmd_cancelled("Selection by number");
7767 for(n1
= 1; n1
<= stream
->nmsgs
; n1
++)
7768 if((mc
= mail_elt(stream
, n1
)) != NULL
)
7769 mc
->searched
= 0; /* clear searched bits */
7771 for(p
= numbers
; *p
; p
++){
7773 while(*p
&& isdigit((unsigned char)*p
))
7779 if(number1
[0] == '\0'){
7781 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
7782 _("Invalid number range, missing number before \"-\": %s"),
7786 else if(!strucmp("end", p
)){
7790 else if(!strucmp(p
, "$")){
7799 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
7800 _("Invalid thread number: %s"), numbers
);
7806 n1
= msgmap
->max_thrdno
;
7808 th
= fetch_thread(stream
, mn_m2raw(msgmap
, mn_get_cur(msgmap
)));
7811 else if((n1
= atol(number1
)) < 1L || n1
> msgmap
->max_thrdno
){
7812 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
7813 _("\"%s\" out of thread number range"),
7821 while(*++p
&& isdigit((unsigned char)*p
))
7827 if(number2
[0] == '\0'){
7828 if(!strucmp("end", p
)){
7832 else if(!strucmp("$", p
)){
7841 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
7842 _("Invalid number range, missing number after \"-\": %s"),
7849 n2
= msgmap
->max_thrdno
;
7851 th
= fetch_thread(stream
, mn_m2raw(msgmap
, mn_get_cur(msgmap
)));
7854 else if((n2
= atol(number2
)) < 1L || n2
> msgmap
->max_thrdno
){
7855 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
7856 _("\"%s\" out of thread number range"),
7864 strncpy(t
, long2string(n1
), sizeof(t
));
7865 t
[sizeof(t
)-1] = '\0';
7866 q_status_message2(SM_ORDER
| SM_DING
, 0, 2,
7867 _("Invalid reverse message number range: %s-%s"),
7868 t
, long2string(n2
));
7872 for(;n1
<= n2
; n1
++){
7873 thrd
= find_thread_by_number(stream
, msgmap
, n1
, thrd
);
7876 set_search_bit_for_thread(stream
, thrd
, msgset
);
7880 thrd
= find_thread_by_number(stream
, msgmap
, n1
, NULL
);
7883 set_search_bit_for_thread(stream
, thrd
, msgset
);
7895 * Select by message dates.
7896 * Sets searched bits in mail_elts
7898 * Args limitsrch -- limit search to this searchset
7900 * Returns 0 on success.
7903 select_by_date(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, long int msgno
, SEARCHSET
**limitsrch
)
7905 int r
, we_cancel
= 0, when
= 0;
7906 char date
[100], defdate
[100], prompt
[128];
7907 time_t seldate
= time(0);
7908 struct tm
*seldate_tm
;
7911 static struct _tense
{
7916 {"were ", "SENT SINCE", " (inclusive)"},
7917 {"were ", "SENT BEFORE", " (exclusive)"},
7918 {"were ", "SENT ON", "" },
7919 {"", "ARRIVED SINCE", " (inclusive)"},
7920 {"", "ARRIVED BEFORE", " (exclusive)"},
7921 {"", "ARRIVED ON", "" }
7925 ps_global
->mangled_footer
= 1;
7929 * If talking to an old server, default to SINCE instead of
7930 * SENTSINCE, which was added later.
7932 if(is_imap_stream(stream
) && !modern_imap_stream(stream
))
7936 int flags
= OE_APPEND_CURRENT
;
7938 seldate_tm
= localtime(&seldate
);
7939 snprintf(defdate
, sizeof(defdate
), "%.2d-%.4s-%.4d", seldate_tm
->tm_mday
,
7940 month_abbrev(seldate_tm
->tm_mon
+ 1),
7941 seldate_tm
->tm_year
+ 1900);
7942 defdate
[sizeof(defdate
)-1] = '\0';
7943 snprintf(prompt
,sizeof(prompt
),"Select messages which %s%s%s [%s]: ",
7944 tense
[when
].preamble
, tense
[when
].range
,
7945 tense
[when
].scope
, defdate
);
7946 prompt
[sizeof(prompt
)-1] = '\0';
7947 r
= optionally_enter(date
,-FOOTER_ROWS(ps_global
), 0, sizeof(date
),
7948 prompt
, sel_date_opt
, help
, &flags
);
7951 cmd_cancelled("Selection by date");
7955 help
= (help
== NO_HELP
) ? h_select_date
: NO_HELP
;
7966 if(stream
&& (rawno
= mn_m2raw(msgmap
, msgno
)) > 0L
7967 && rawno
<= stream
->nmsgs
7968 && (mc
= mail_elt(stream
, rawno
))){
7970 /* cache not filled in yet? */
7974 if(stream
->dtb
&& stream
->dtb
->flags
& DR_NEWS
){
7976 ulong2string(mail_uid(stream
, rawno
)),
7978 seq
[sizeof(seq
)-1] = '\0';
7979 mail_fetch_overview(stream
, seq
, NULL
);
7982 strncpy(seq
, long2string(rawno
),
7984 seq
[sizeof(seq
)-1] = '\0';
7985 mail_fetch_fast(stream
, seq
, 0L);
7989 /* mail_date returns fixed field width date */
7990 mail_date(date
, mc
);
7997 case 12 : /* set default to PREVIOUS day */
8001 case 13 : /* set default to NEXT day */
8006 when
= (when
+1) % (sizeof(tense
) / sizeof(struct _tense
));
8013 removing_leading_white_space(date
);
8014 removing_trailing_white_space(date
);
8016 strncpy(date
, defdate
, sizeof(date
));
8017 date
[sizeof(date
)-1] = '\0';
8023 if((pgm
= mail_newsearchpgm()) != NULL
){
8025 short converted_date
;
8027 if(mail_parse_date(&elt
, (unsigned char *) date
)){
8028 converted_date
= mail_shortdate(elt
.year
, elt
.month
, elt
.day
);
8032 pgm
->sentsince
= converted_date
;
8035 pgm
->sentbefore
= converted_date
;
8038 pgm
->senton
= converted_date
;
8041 pgm
->since
= converted_date
;
8044 pgm
->before
= converted_date
;
8047 pgm
->on
= converted_date
;
8051 pgm
->msgno
= (limitsrch
? *limitsrch
: NULL
);
8053 if(ps_global
&& ps_global
->ttyo
){
8054 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
8055 ps_global
->mangled_footer
= 1;
8058 we_cancel
= busy_cue(_("Selecting"), NULL
, 1);
8060 pine_mail_search_full(stream
, NULL
, pgm
, SE_NOPREFETCH
| SE_FREE
);
8065 /* we know this was freed in mail_search, let caller know */
8070 mail_free_searchpgm(&pgm
);
8071 q_status_message1(SM_ORDER
, 3, 3,
8072 _("Invalid date entered: %s"), date
);
8082 * Select by searching in message headers or body.
8083 * Sets searched bits in mail_elts
8085 * Args limitsrch -- limit search to this searchset
8087 * Returns 0 on success.
8090 select_by_text(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, long int msgno
, SEARCHSET
**limitsrch
)
8092 int r
, ku
, type
, we_cancel
= 0, flags
, rv
, ekeyi
= 0;
8093 int not = 0, me
= 0;
8094 char sstring
[80], savedsstring
[80], tmp
[128];
8095 char *p
, *sval
= NULL
;
8096 char buftmp
[MAILTMPLEN
], namehdr
[80];
8098 ENVELOPE
*env
= NULL
;
8100 unsigned flagsforhist
= 0;
8101 static HISTORY_S
*history
= NULL
;
8102 static char *recip
= "RECIPIENTS";
8103 static char *partic
= "PARTICIPANTS";
8104 static char *match_me
= N_("[Match_My_Addresses]");
8105 static char *dont_match_me
= N_("[Don't_Match_My_Addresses]");
8107 ps_global
->mangled_footer
= 1;
8108 savedsstring
[0] = '\0';
8109 ekey
[0].ch
= ekey
[1].ch
= ekey
[2].ch
= ekey
[3].ch
= -1;
8112 type
= radio_buttons(not ? _(sel_text_not
) : _(sel_text
),
8113 -FOOTER_ROWS(ps_global
), sel_text_opt
,
8114 's', 'x', NO_HELP
, RB_NORM
|RB_RET_HELP
);
8119 helper(h_select_text
, "HELP FOR SELECT BASED ON CONTENTS",
8121 ps_global
->mangled_screen
= 1;
8128 * prepare some friendly defaults...
8131 case 't' : /* address fields, offer To or From */
8136 sval
= (type
== 't') ? "TO" :
8137 (type
== 'f') ? "FROM" :
8138 (type
== 'c') ? "CC" :
8139 (type
== 'r') ? recip
: partic
;
8140 ekey
[ekeyi
].ch
= ctrl('T');
8141 ekey
[ekeyi
].name
= "^T";
8142 ekey
[ekeyi
].rval
= 10;
8143 /* TRANSLATORS: use Current To Address */
8144 ekey
[ekeyi
++].label
= N_("Cur To");
8145 ekey
[ekeyi
].ch
= ctrl('R');
8146 ekey
[ekeyi
].name
= "^R";
8147 ekey
[ekeyi
].rval
= 11;
8148 /* TRANSLATORS: use Current From Address */
8149 ekey
[ekeyi
++].label
= N_("Cur From");
8150 ekey
[ekeyi
].ch
= ctrl('W');
8151 ekey
[ekeyi
].name
= "^W";
8152 ekey
[ekeyi
].rval
= 12;
8153 /* TRANSLATORS: use Current Cc Address */
8154 ekey
[ekeyi
++].label
= N_("Cur Cc");
8155 ekey
[ekeyi
].ch
= ctrl('Y');
8156 ekey
[ekeyi
].name
= "^Y";
8157 ekey
[ekeyi
].rval
= 13;
8158 /* TRANSLATORS: Match Me means match my address */
8159 ekey
[ekeyi
++].label
= N_("Match Me");
8161 ekey
[ekeyi
].name
= "";
8162 ekey
[ekeyi
].rval
= 0;
8163 ekey
[ekeyi
++].label
= "";
8168 ekey
[ekeyi
].ch
= ctrl('X');
8169 ekey
[ekeyi
].name
= "^X";
8170 ekey
[ekeyi
].rval
= 14;
8171 /* TRANSLATORS: use Current Subject */
8172 ekey
[ekeyi
++].label
= N_("Cur Subject");
8184 strncpy(tmp
, "Name of HEADER to match : ", sizeof(tmp
)-1);
8185 tmp
[sizeof(tmp
)-1] = '\0';
8186 flags
= OE_APPEND_CURRENT
;
8192 r
= optionally_enter(namehdr
, -FOOTER_ROWS(ps_global
), 0,
8193 sizeof(namehdr
), tmp
, ekey
, NO_HELP
, &flags
);
8195 cmd_cancelled("Selection by text");
8198 removing_leading_white_space(namehdr
);
8200 while ((namehdr
[0] != '\0') && /* remove trailing ":" */
8201 (namehdr
[strlen(namehdr
) - 1] == ':'))
8202 namehdr
[strlen(namehdr
) - 1] = '\0';
8203 if ((namehdr
[0] != '\0')
8204 && isspace((unsigned char) namehdr
[strlen(namehdr
) - 1]))
8205 removing_trailing_white_space(namehdr
);
8209 if (strchr(namehdr
,' ') || strchr(namehdr
,'\t') ||
8210 strchr(namehdr
,':'))
8212 if (namehdr
[0] == '\0')
8222 dprint((1,"\n - BOTCH: select_text unrecognized option\n"));
8226 ekey
[ekeyi
].ch
= KEY_UP
;
8227 ekey
[ekeyi
].rval
= 30;
8228 ekey
[ekeyi
].name
= "";
8230 ekey
[ekeyi
++].label
= "";
8232 ekey
[ekeyi
].ch
= KEY_DOWN
;
8233 ekey
[ekeyi
].rval
= 31;
8234 ekey
[ekeyi
].name
= "";
8235 ekey
[ekeyi
++].label
= "";
8237 ekey
[ekeyi
].ch
= -1;
8241 init_hist(&history
, HISTSIZE
);
8243 if(ekey
[0].ch
> -1 && msgno
> 0L
8244 && !(env
=pine_mail_fetchstructure(stream
,mn_m2raw(msgmap
,msgno
),
8253 /* TRANSLATORS: character String in message <message number> to NOT match : " */
8254 snprintf(tmp
, sizeof(tmp
), "String in message %s to NOT match : ", sval
);
8256 snprintf(tmp
, sizeof(tmp
), "String in message %s to match : ", sval
);
8258 if(items_in_hist(history
) > 0){
8259 ekey
[ku
].name
= HISTORY_UP_KEYNAME
;
8260 ekey
[ku
].label
= HISTORY_KEYLABEL
;
8261 ekey
[ku
+1].name
= HISTORY_DOWN_KEYNAME
;
8262 ekey
[ku
+1].label
= HISTORY_KEYLABEL
;
8266 ekey
[ku
].label
= "";
8267 ekey
[ku
+1].name
= "";
8268 ekey
[ku
+1].label
= "";
8271 flags
= OE_APPEND_CURRENT
| OE_KEEP_TRAILING_SPACE
;
8272 r
= optionally_enter(sstring
, -FOOTER_ROWS(ps_global
), 0,
8273 79, tmp
, ekey
, help
, &flags
);
8275 if(me
&& r
== 0 && ((!not && strcmp(sstring
, _(match_me
))) || (not && strcmp(sstring
, _(dont_match_me
)))))
8280 help
= (help
== NO_HELP
)
8282 ? ((type
== 'f') ? h_select_txt_not_from
8283 : (type
== 't') ? h_select_txt_not_to
8284 : (type
== 'c') ? h_select_txt_not_cc
8285 : (type
== 's') ? h_select_txt_not_subj
8286 : (type
== 'a') ? h_select_txt_not_all
8287 : (type
== 'r') ? h_select_txt_not_recip
8288 : (type
== 'p') ? h_select_txt_not_partic
8289 : (type
== 'b') ? h_select_txt_not_body
8291 : ((type
== 'f') ? h_select_txt_from
8292 : (type
== 't') ? h_select_txt_to
8293 : (type
== 'c') ? h_select_txt_cc
8294 : (type
== 's') ? h_select_txt_subj
8295 : (type
== 'a') ? h_select_txt_all
8296 : (type
== 'r') ? h_select_txt_recip
8297 : (type
== 'p') ? h_select_txt_partic
8298 : (type
== 'b') ? h_select_txt_body
8305 case 10 : /* To: default */
8306 if(env
&& env
->to
&& env
->to
->mailbox
){
8307 snprintf(sstring
, sizeof(sstring
), "%s%s%s", env
->to
->mailbox
,
8308 env
->to
->host
? "@" : "",
8309 env
->to
->host
? env
->to
->host
: "");
8310 sstring
[sizeof(sstring
)-1] = '\0';
8314 case 11 : /* From: default */
8315 if(env
&& env
->from
&& env
->from
->mailbox
){
8316 snprintf(sstring
, sizeof(sstring
), "%s%s%s", env
->from
->mailbox
,
8317 env
->from
->host
? "@" : "",
8318 env
->from
->host
? env
->from
->host
: "");
8319 sstring
[sizeof(sstring
)-1] = '\0';
8323 case 12 : /* Cc: default */
8324 if(env
&& env
->cc
&& env
->cc
->mailbox
){
8325 snprintf(sstring
, sizeof(sstring
), "%s%s%s", env
->cc
->mailbox
,
8326 env
->cc
->host
? "@" : "",
8327 env
->cc
->host
? env
->cc
->host
: "");
8328 sstring
[sizeof(sstring
)-1] = '\0';
8332 case 13 : /* Match my addresses */
8334 snprintf(sstring
, sizeof(sstring
), "%s", not ? _(dont_match_me
) : _(match_me
));
8337 case 14 : /* Subject: default */
8338 if(env
&& env
->subject
&& env
->subject
[0]){
8341 snprintf(buftmp
, sizeof(buftmp
), "%.75s", env
->subject
);
8342 buftmp
[sizeof(buftmp
)-1] = '\0';
8343 q
= (char *) rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
,
8344 SIZEOF_20KBUF
, buftmp
);
8345 if(q
!= env
->subject
){
8346 snprintf(savedsstring
, sizeof(savedsstring
), "%.70s", q
);
8347 savedsstring
[sizeof(savedsstring
)-1] = '\0';
8350 snprintf(sstring
, sizeof(sstring
), "%s", q
);
8351 sstring
[sizeof(sstring
)-1] = '\0';
8357 flagsforhist
= (not ? 0x1 : 0) + (me
? 0x2 : 0);
8358 if((p
= get_prev_hist(history
, sstring
, flagsforhist
, NULL
)) != NULL
){
8359 strncpy(sstring
, p
, sizeof(sstring
));
8360 sstring
[sizeof(sstring
)-1] = '\0';
8361 if(history
->hist
[history
->curindex
]){
8362 flagsforhist
= history
->hist
[history
->curindex
]->flags
;
8363 not = (flagsforhist
& 0x1) ? 1 : 0;
8364 me
= (flagsforhist
& 0x2) ? 1 : 0;
8373 flagsforhist
= (not ? 0x1 : 0) + (me
? 0x2 : 0);
8374 if((p
= get_next_hist(history
, sstring
, flagsforhist
, NULL
)) != NULL
){
8375 strncpy(sstring
, p
, sizeof(sstring
));
8376 sstring
[sizeof(sstring
)-1] = '\0';
8377 if(history
->hist
[history
->curindex
]){
8378 flagsforhist
= history
->hist
[history
->curindex
]->flags
;
8379 not = (flagsforhist
& 0x1) ? 1 : 0;
8380 me
= (flagsforhist
& 0x2) ? 1 : 0;
8392 if(r
== 1 || sstring
[0] == '\0')
8399 if(type
== 'x' || r
== 'x'){
8400 cmd_cancelled("Selection by text");
8404 if(ps_global
&& ps_global
->ttyo
){
8405 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
8406 ps_global
->mangled_footer
= 1;
8409 we_cancel
= busy_cue(_("Selecting"), NULL
, 1);
8411 flagsforhist
= (not ? 0x1 : 0) + (me
? 0x2 : 0);
8412 save_hist(history
, sstring
, flagsforhist
, NULL
);
8414 rv
= agg_text_select(stream
, msgmap
, type
, namehdr
, not, me
, sstring
, "utf-8", limitsrch
);
8423 * Select by message size.
8424 * Sets searched bits in mail_elts
8426 * Args limitsrch -- limit search to this searchset
8428 * Returns 0 on success.
8431 select_by_size(MAILSTREAM
*stream
, SEARCHSET
**limitsrch
)
8433 int r
, large
= 1, we_cancel
= 0;
8434 unsigned long n
, mult
= 1L, numerator
= 0L, divisor
= 1L;
8435 char size
[16], numbers
[80], *p
, *t
;
8438 long flags
= (SE_NOPREFETCH
| SE_FREE
);
8441 ps_global
->mangled_footer
= 1;
8445 int flgs
= OE_APPEND_CURRENT
;
8447 sel_size_opt
[1].label
= large
? sel_size_smaller
: sel_size_larger
;
8449 r
= optionally_enter(numbers
, -FOOTER_ROWS(ps_global
), 0,
8450 sizeof(numbers
), large
? _(select_size_larger_msg
)
8451 : _(select_size_smaller_msg
),
8452 sel_size_opt
, help
, &flgs
);
8462 help
= (help
== NO_HELP
) ? (large
? h_select_by_larger_size
8463 : h_select_by_smaller_size
)
8468 for(t
= p
= numbers
; *p
; p
++) /* strip whitespace */
8469 if(!isspace((unsigned char)*p
))
8474 if(r
== 1 || numbers
[0] == '\0'){
8475 cmd_cancelled("Selection by size");
8482 if(numbers
[0] == '-'){
8483 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
8484 _("Invalid size entered: %s"), numbers
);
8491 while(*p
&& isdigit((unsigned char)*p
))
8496 if(size
[0] == '\0' && *p
== '.' && isdigit(*(p
+1))){
8501 if(size
[0] == '\0'){
8502 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
8503 _("Invalid size entered: %s"), numbers
);
8507 n
= strtoul(size
, (char **)NULL
, 10);
8512 * We probably ought to just use atof() to convert 1.1 into a
8513 * double, but since we haven't used atof() anywhere else I'm
8514 * reluctant to use it because of portability concerns.
8518 while(*p
&& isdigit((unsigned char)*p
)){
8526 numerator
= strtoul(size
, (char **)NULL
, 10);
8546 n
= n
* mult
+ (numerator
* mult
) / divisor
;
8548 pgm
= mail_newsearchpgm();
8554 if(is_imap_stream(stream
) && !modern_imap_stream(stream
))
8555 flags
|= SE_NOSERVER
;
8557 if(ps_global
&& ps_global
->ttyo
){
8558 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
8559 ps_global
->mangled_footer
= 1;
8562 we_cancel
= busy_cue(_("Selecting"), NULL
, 1);
8564 pgm
->msgno
= (limitsrch
? *limitsrch
: NULL
);
8565 pine_mail_search_full(stream
, NULL
, pgm
, SE_NOPREFETCH
| SE_FREE
);
8566 /* we know this was freed in mail_search, let caller know */
8578 * visible_searchset -- return c-client search set unEXLDed
8582 visible_searchset(MAILSTREAM
*stream
, MSGNO_S
*msgmap
)
8585 SEARCHSET
*full_set
= NULL
, **set
;
8588 * If we're talking to anything other than a server older than
8589 * imap 4rev1, build a searchset otherwise it'll choke.
8591 if(!(is_imap_stream(stream
) && !modern_imap_stream(stream
))){
8592 if(any_lflagged(msgmap
, MN_EXLD
)){
8593 for(n
= 1L, set
= &full_set
, run
= 0L; n
<= stream
->nmsgs
; n
++)
8594 if(get_lflag(stream
, NULL
, n
, MN_EXLD
)){
8595 if(run
){ /* previous NOT excluded? */
8597 (*set
)->last
= n
- 1L;
8599 set
= &(*set
)->next
;
8603 else if(run
++){ /* next in run */
8606 else{ /* start of run */
8607 *set
= mail_newsearchset();
8612 full_set
= mail_newsearchset();
8613 full_set
->first
= 1L;
8614 full_set
->last
= stream
->nmsgs
;
8623 * Select by message status bits.
8624 * Sets searched bits in mail_elts
8626 * Args limitsrch -- limit search to this searchset
8628 * Returns 0 on success.
8631 select_by_status(MAILSTREAM
*stream
, SEARCHSET
**limitsrch
)
8633 int s
, not = 0, we_cancel
= 0, rv
;
8636 s
= radio_buttons((not) ? _(sel_flag_not
) : _(sel_flag
),
8637 -FOOTER_ROWS(ps_global
), sel_flag_opt
, '*', 'x',
8638 NO_HELP
, RB_NORM
|RB_RET_HELP
);
8641 cmd_cancelled("Selection by status");
8645 helper(h_select_status
, _("HELP FOR SELECT BASED ON STATUS"),
8647 ps_global
->mangled_screen
= 1;
8655 if(ps_global
&& ps_global
->ttyo
){
8656 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
8657 ps_global
->mangled_footer
= 1;
8660 we_cancel
= busy_cue(_("Selecting"), NULL
, 1);
8661 rv
= agg_flag_select(stream
, not, s
, limitsrch
);
8670 * Select by rule. Usually srch, indexcolor, and roles would be most useful.
8671 * Sets searched bits in mail_elts
8673 * Args limitsrch -- limit search to this searchset
8675 * Returns 0 on success.
8678 select_by_rule(MAILSTREAM
*stream
, SEARCHSET
**limitsrch
)
8680 char rulenick
[1000], *nick
;
8682 int r
, not = 0, we_cancel
= 0, rflags
= ROLE_DO_SRCH
8690 ps_global
->mangled_footer
= 1;
8695 oe_flags
= OE_APPEND_CURRENT
;
8696 r
= optionally_enter(rulenick
, -FOOTER_ROWS(ps_global
), 0,
8698 not ? _("Rule to NOT match: ")
8699 : _("Rule to match: "),
8700 sel_key_opt
, NO_HELP
, &oe_flags
);
8703 /* select rulenick from a list */
8704 if((nick
=choose_a_rule(rflags
)) != NULL
){
8705 strncpy(rulenick
, nick
, sizeof(rulenick
)-1);
8706 rulenick
[sizeof(rulenick
)-1] = '\0';
8707 fs_give((void **) &nick
);
8716 helper(h_select_rule
, _("HELP FOR SELECT BY RULE"), HLPD_SIMPLE
);
8717 ps_global
->mangled_screen
= 1;
8720 cmd_cancelled("Selection by Rule");
8724 removing_leading_and_trailing_white_space(rulenick
);
8726 }while(r
== 3 || r
== 4 || r
== '!');
8730 * The approach of requiring a nickname instead of just allowing the
8731 * user to select from the list of rules has the drawback that a rule
8732 * may not have a nickname, or there may be more than one rule with
8733 * the same nickname. However, it has the benefit of allowing the user
8734 * to type in the nickname and, most importantly, allows us to set
8735 * up the ! (not). We could incorporate the ! into the selection
8736 * screen, but this is easier and also allows the typing of nicks.
8737 * User can just set up nicknames if they want to use this feature.
8739 patgrp
= nick_to_patgrp(rulenick
, rflags
);
8742 if(ps_global
&& ps_global
->ttyo
){
8743 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
8744 ps_global
->mangled_footer
= 1;
8747 we_cancel
= busy_cue(_("Selecting"), NULL
, 1);
8748 match_pattern(patgrp
, stream
, limitsrch
? *limitsrch
: 0, NULL
,
8750 (not ? MP_NOT
: 0) | SE_NOPREFETCH
);
8751 free_patgrp(&patgrp
);
8756 if(limitsrch
&& *limitsrch
){
8757 mail_free_searchset(limitsrch
);
8766 * Allow user to choose a rule from their list of rules.
8768 * Returns an allocated rule nickname on success, NULL otherwise.
8771 choose_a_rule(int rflags
)
8773 char *choice
= NULL
;
8774 char **rule_list
, **lp
;
8779 if(!(nonempty_patterns(rflags
, &pstate
) && first_pattern(&pstate
))){
8780 q_status_message(SM_ORDER
, 3, 3,
8781 _("No rules available. Use Setup/Rules to add some."));
8786 * Build a list of rules to choose from.
8789 for(pat
= first_pattern(&pstate
); pat
; pat
= next_pattern(&pstate
))
8793 q_status_message(SM_ORDER
, 3, 4, _("No rules defined, use Setup/Rules"));
8797 lp
= rule_list
= (char **) fs_get((cnt
+ 1) * sizeof(*rule_list
));
8798 memset(rule_list
, 0, (cnt
+1) * sizeof(*rule_list
));
8800 for(pat
= first_pattern(&pstate
); pat
; pat
= next_pattern(&pstate
))
8801 *lp
++ = cpystr((pat
->patgrp
&& pat
->patgrp
->nick
)
8802 ? pat
->patgrp
->nick
: "?");
8804 /* TRANSLATORS: SELECT A RULE is a screen title
8805 TRANSLATORS: Print something1 using something2.
8806 "rules" is something1 */
8807 choice
= choose_item_from_list(rule_list
, NULL
, _("SELECT A RULE"),
8808 _("rules"), h_select_rule_screen
,
8809 _("HELP FOR SELECTING A RULE NICKNAME"), NULL
);
8812 q_status_message(SM_ORDER
, 1, 4, "No choice");
8814 free_list_array(&rule_list
);
8821 * Select by current thread.
8822 * Sets searched bits in mail_elts for this entire thread
8824 * Args limitsrch -- limit search to this searchset
8826 * Returns 0 on success.
8829 select_by_thread(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, SEARCHSET
**limitsrch
)
8832 PINETHRD_S
*thrd
= NULL
;
8839 for(n
= 1L; n
<= stream
->nmsgs
; n
++)
8840 if((mc
= mail_elt(stream
, n
)) != NULL
)
8841 mc
->searched
= 0; /* clear searched bits */
8843 thrd
= fetch_thread(stream
, mn_m2raw(msgmap
, mn_get_cur(msgmap
)));
8844 if(thrd
&& thrd
->top
&& thrd
->top
!= thrd
->rawno
)
8845 thrd
= fetch_thread(stream
, thrd
->top
);
8848 * This doesn't unselect if the thread is already selected
8849 * (like select current does), it always selects.
8850 * There is no way to select ! this thread.
8853 set_search_bit_for_thread(stream
, thrd
, limitsrch
);
8862 * Select by message keywords.
8863 * Sets searched bits in mail_elts
8865 * Args limitsrch -- limit search to this searchset
8867 * Returns 0 on success.
8870 select_by_keyword(MAILSTREAM
*stream
, SEARCHSET
**limitsrch
)
8872 int r
, not = 0, we_cancel
= 0;
8873 char keyword
[MAXUSERFLAG
+1], *kword
;
8874 char *error
= NULL
, *p
, *prompt
;
8879 ps_global
->mangled_footer
= 1;
8886 q_status_message(SM_ORDER
, 3, 4, error
);
8887 fs_give((void **) &error
);
8890 if(F_ON(F_FLAG_SCREEN_KW_SHORTCUT
, ps_global
) && ps_global
->keywords
){
8892 prompt
= _("Keyword (or keyword initial) to NOT match: ");
8894 prompt
= _("Keyword (or keyword initial) to match: ");
8898 prompt
= _("Keyword to NOT match: ");
8900 prompt
= _("Keyword to match: ");
8903 oe_flags
= OE_APPEND_CURRENT
;
8904 r
= optionally_enter(keyword
, -FOOTER_ROWS(ps_global
), 0,
8906 prompt
, sel_key_opt
, help
, &oe_flags
);
8909 /* select keyword from a list */
8910 if((kword
=choose_a_keyword()) != NULL
){
8911 strncpy(keyword
, kword
, sizeof(keyword
)-1);
8912 keyword
[sizeof(keyword
)-1] = '\0';
8913 fs_give((void **) &kword
);
8922 help
= help
== NO_HELP
? h_select_keyword
: NO_HELP
;
8924 cmd_cancelled("Selection by keyword");
8928 removing_leading_and_trailing_white_space(keyword
);
8930 }while(r
== 3 || r
== 4 || r
== '!' || keyword_check(keyword
, &error
));
8933 if(F_ON(F_FLAG_SCREEN_KW_SHORTCUT
, ps_global
) && ps_global
->keywords
){
8934 p
= initial_to_keyword(keyword
);
8936 strncpy(keyword
, p
, sizeof(keyword
)-1);
8937 keyword
[sizeof(keyword
)-1] = '\0';
8942 * We want to check the keyword, not the nickname of the keyword,
8943 * so convert it to the keyword if necessary.
8945 p
= nick_to_keyword(keyword
);
8947 strncpy(keyword
, p
, sizeof(keyword
)-1);
8948 keyword
[sizeof(keyword
)-1] = '\0';
8951 pgm
= mail_newsearchpgm();
8953 pgm
->unkeyword
= mail_newstringlist();
8954 pgm
->unkeyword
->text
.data
= (unsigned char *) cpystr(keyword
);
8955 pgm
->unkeyword
->text
.size
= strlen(keyword
);
8958 pgm
->keyword
= mail_newstringlist();
8959 pgm
->keyword
->text
.data
= (unsigned char *) cpystr(keyword
);
8960 pgm
->keyword
->text
.size
= strlen(keyword
);
8963 if(ps_global
&& ps_global
->ttyo
){
8964 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
8965 ps_global
->mangled_footer
= 1;
8968 we_cancel
= busy_cue(_("Selecting"), NULL
, 1);
8970 pgm
->msgno
= (limitsrch
? *limitsrch
: NULL
);
8971 pine_mail_search_full(stream
, "UTF-8", pgm
, SE_NOPREFETCH
| SE_FREE
);
8972 /* we know this was freed in mail_search, let caller know */
8984 * Allow user to choose a keyword from their list of keywords.
8986 * Returns an allocated keyword on success, NULL otherwise.
8989 choose_a_keyword(void)
8991 char *choice
= NULL
;
8992 char **keyword_list
, **lp
;
8997 * Build a list of keywords to choose from.
9000 for(cnt
= 0, kw
= ps_global
->keywords
; kw
; kw
= kw
->next
)
9004 q_status_message(SM_ORDER
, 3, 4,
9005 _("No keywords defined, use \"keywords\" option in Setup/Config"));
9009 lp
= keyword_list
= (char **) fs_get((cnt
+ 1) * sizeof(*keyword_list
));
9010 memset(keyword_list
, 0, (cnt
+1) * sizeof(*keyword_list
));
9012 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
)
9013 *lp
++ = cpystr(kw
->nick
? kw
->nick
: kw
->kw
? kw
->kw
: "");
9015 /* TRANSLATORS: SELECT A KEYWORD is a screen title
9016 TRANSLATORS: Print something1 using something2.
9017 "keywords" is something1 */
9018 choice
= choose_item_from_list(keyword_list
, NULL
, _("SELECT A KEYWORD"),
9019 _("keywords"), h_select_keyword_screen
,
9020 _("HELP FOR SELECTING A KEYWORD"), NULL
);
9023 q_status_message(SM_ORDER
, 1, 4, "No choice");
9025 free_list_array(&keyword_list
);
9032 * Allow user to choose a list of keywords from their list of keywords.
9034 * Returns allocated list.
9037 choose_list_of_keywords(void)
9039 LIST_SEL_S
*listhead
, *ls
, *p
;
9045 * Build a list of keywords to choose from.
9048 p
= listhead
= NULL
;
9049 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
){
9051 ls
= (LIST_SEL_S
*) fs_get(sizeof(*ls
));
9052 memset(ls
, 0, sizeof(*ls
));
9053 ls
->item
= cpystr(kw
->nick
? kw
->nick
: kw
->kw
? kw
->kw
: "");
9066 /* TRANSLATORS: SELECT KEYWORDS is a screen title
9067 Print something1 using something2.
9068 "keywords" is something1 */
9069 if(!select_from_list_screen(listhead
, SFL_ALLOW_LISTMODE
,
9070 _("SELECT KEYWORDS"), _("keywords"),
9071 h_select_multkeyword_screen
,
9072 _("HELP FOR SELECTING KEYWORDS"), NULL
)){
9073 for(cnt
= 0, p
= listhead
; p
; p
= p
->next
)
9077 ret
= (char **) fs_get((cnt
+1) * sizeof(*ret
));
9078 memset(ret
, 0, (cnt
+1) * sizeof(*ret
));
9079 for(i
= 0, p
= listhead
; p
; p
= p
->next
)
9081 ret
[i
++] = cpystr(p
->item
? p
->item
: "");
9084 free_list_sel(&listhead
);
9091 * Allow user to choose a charset
9093 * Returns an allocated charset on success, NULL otherwise.
9096 choose_a_charset(int which_charsets
)
9098 char *choice
= NULL
;
9099 char **charset_list
, **lp
;
9104 * Build a list of charsets to choose from.
9107 for(cnt
= 0, cs
= utf8_charset(NIL
); cs
&& cs
->name
; cs
++){
9108 if(!(cs
->flags
& (CF_UNSUPRT
|CF_NOEMAIL
))
9109 && ((which_charsets
& CAC_ALL
)
9110 || (which_charsets
& CAC_POSTING
9111 && cs
->flags
& CF_POSTING
)
9112 || (which_charsets
& CAC_DISPLAY
9113 && cs
->type
!= CT_2022
9114 && (cs
->flags
& (CF_PRIMARY
|CF_DISPLAY
)) == (CF_PRIMARY
|CF_DISPLAY
))))
9119 q_status_message(SM_ORDER
, 3, 4,
9120 _("No charsets found? Enter charset manually."));
9124 lp
= charset_list
= (char **) fs_get((cnt
+ 1) * sizeof(*charset_list
));
9125 memset(charset_list
, 0, (cnt
+1) * sizeof(*charset_list
));
9127 for(cs
= utf8_charset(NIL
); cs
&& cs
->name
; cs
++){
9128 if(!(cs
->flags
& (CF_UNSUPRT
|CF_NOEMAIL
))
9129 && ((which_charsets
& CAC_ALL
)
9130 || (which_charsets
& CAC_POSTING
9131 && cs
->flags
& CF_POSTING
)
9132 || (which_charsets
& CAC_DISPLAY
9133 && cs
->type
!= CT_2022
9134 && (cs
->flags
& (CF_PRIMARY
|CF_DISPLAY
)) == (CF_PRIMARY
|CF_DISPLAY
))))
9135 *lp
++ = cpystr(cs
->name
);
9138 /* TRANSLATORS: SELECT A CHARACTER SET is a screen title
9139 TRANSLATORS: Print something1 using something2.
9140 "character sets" is something1 */
9141 choice
= choose_item_from_list(charset_list
, NULL
, _("SELECT A CHARACTER SET"),
9142 _("character sets"), h_select_charset_screen
,
9143 _("HELP FOR SELECTING A CHARACTER SET"), NULL
);
9146 q_status_message(SM_ORDER
, 1, 4, "No choice");
9148 free_list_array(&charset_list
);
9155 * Allow user to choose a list of character sets and/or scripts
9157 * Returns allocated list.
9160 choose_list_of_charsets(void)
9162 LIST_SEL_S
*listhead
, *ls
, *p
;
9164 int cnt
, i
, got_one
;
9169 char buf
[1024], *folded
;
9172 * Build a list of charsets to choose from.
9175 p
= listhead
= NULL
;
9177 /* this width is determined by select_from_list_screen() */
9178 width
= ps_global
->ttyo
->screen_cols
- 4;
9180 /* first comes a list of scripts (sets of character sets) */
9181 for(s
= utf8_script(NIL
); s
&& s
->name
; s
++){
9183 limit
= sizeof(buf
)-1;
9185 memset(q
, 0, limit
+1);
9188 sstrncpy(&q
, s
->name
, limit
);
9191 sstrncpy(&q
, " (", limit
-(q
-buf
));
9192 sstrncpy(&q
, s
->description
, limit
-(q
-buf
));
9193 sstrncpy(&q
, ")", limit
-(q
-buf
));
9196 /* add the list of charsets that are in this script */
9198 for(cs
= utf8_charset(NIL
);
9199 cs
&& cs
->name
&& (q
-buf
) < limit
; cs
++){
9200 if(cs
->script
& s
->script
){
9202 * Filter out some un-useful members of the list.
9203 * UTF-7 and UTF-8 weren't actually in the list at the
9204 * time this was written. Just making sure.
9206 if(!strucmp(cs
->name
, "ISO-2022-JP-2")
9207 || !strucmp(cs
->name
, "UTF-7")
9208 || !strucmp(cs
->name
, "UTF-8"))
9212 sstrncpy(&q
, " ", limit
-(q
-buf
));
9215 sstrncpy(&q
, " {", limit
-(q
-buf
));
9218 sstrncpy(&q
, cs
->name
, limit
-(q
-buf
));
9223 sstrncpy(&q
, "}", limit
-(q
-buf
));
9225 /* fold this line so that it can all be seen on the screen */
9226 folded
= fold(buf
, width
, width
, "", " ", FLD_NONE
);
9229 while(t
&& *t
&& (q
= strindex(t
, '\n')) != NULL
){
9232 ls
= (LIST_SEL_S
*) fs_get(sizeof(*ls
));
9233 memset(ls
, 0, sizeof(*ls
));
9235 ls
->item
= cpystr(s
->name
);
9237 ls
->flags
= SFL_NOSELECT
;
9239 ls
->display_item
= cpystr(t
);
9249 listhead
= (LIST_SEL_S
*) fs_get(sizeof(*ls
));
9250 memset(listhead
, 0, sizeof(*listhead
));
9251 listhead
->flags
= SFL_NOSELECT
;
9252 listhead
->display_item
=
9253 cpystr(_("Scripts representing groups of related character sets"));
9254 listhead
->next
= (LIST_SEL_S
*) fs_get(sizeof(*ls
));
9255 memset(listhead
->next
, 0, sizeof(*listhead
));
9256 listhead
->next
->flags
= SFL_NOSELECT
;
9257 listhead
->next
->display_item
=
9258 cpystr(repeat_char(width
, '-'));
9260 listhead
->next
->next
= ls
;
9265 fs_give((void **) &folded
);
9269 ls
= (LIST_SEL_S
*) fs_get(sizeof(*ls
));
9270 memset(ls
, 0, sizeof(*ls
));
9271 ls
->flags
= SFL_NOSELECT
;
9279 ls
= (LIST_SEL_S
*) fs_get(sizeof(*ls
));
9280 memset(ls
, 0, sizeof(*ls
));
9281 ls
->flags
= SFL_NOSELECT
;
9283 cpystr(_("Individual character sets, may be mixed with scripts"));
9287 ls
= (LIST_SEL_S
*) fs_get(sizeof(*ls
));
9288 memset(ls
, 0, sizeof(*ls
));
9289 ls
->flags
= SFL_NOSELECT
;
9291 cpystr(repeat_char(width
, '-'));
9295 /* then comes a list of individual character sets */
9296 for(cs
= utf8_charset(NIL
); cs
&& cs
->name
; cs
++){
9297 ls
= (LIST_SEL_S
*) fs_get(sizeof(*ls
));
9298 memset(ls
, 0, sizeof(*ls
));
9299 ls
->item
= cpystr(cs
->name
);
9312 /* TRANSLATORS: SELECT CHARACTER SETS is a screen title
9313 Print something1 using something2.
9314 "character sets" is something1 */
9315 if(!select_from_list_screen(listhead
, SFL_ALLOW_LISTMODE
,
9316 _("SELECT CHARACTER SETS"), _("character sets"),
9317 h_select_multcharsets_screen
,
9318 _("HELP FOR SELECTING CHARACTER SETS"), NULL
)){
9319 for(cnt
= 0, p
= listhead
; p
; p
= p
->next
)
9323 ret
= (char **) fs_get((cnt
+1) * sizeof(*ret
));
9324 memset(ret
, 0, (cnt
+1) * sizeof(*ret
));
9325 for(i
= 0, p
= listhead
; p
; p
= p
->next
)
9327 ret
[i
++] = cpystr(p
->item
? p
->item
: "");
9330 free_list_sel(&listhead
);
9335 /* Report quota summary resources in an IMAP server */
9337 void cmd_quota (struct pine
*state
)
9339 QUOTALIST
*imapquota
;
9344 if(!state
->mail_stream
|| !is_imap_stream(state
->mail_stream
)){
9345 q_status_message(SM_ORDER
, 1, 5, "Quota only available for IMAP folders");
9349 if (state
->mail_stream
9350 && !sp_dead_stream(state
->mail_stream
)
9351 && state
->mail_stream
->mailbox
9352 && *state
->mail_stream
->mailbox
9353 && mail_valid_net_parse(state
->mail_stream
->mailbox
, &mb
))
9354 imap_getquotaroot(state
->mail_stream
, mb
.mailbox
);
9356 if(!state
->quota
) /* failed ? */
9357 return; /* go back... */
9359 if(!(store
= so_get(CharStar
, NULL
, EDIT_ACCESS
))){
9360 q_status_message(SM_ORDER
| SM_DING
, 3, 3, "Error allocating space.");
9364 so_puts(store
, "Quota Report for ");
9365 so_puts(store
, state
->mail_stream
->original_mailbox
);
9366 so_puts(store
, "\n\n");
9368 for (imapquota
= state
->quota
; imapquota
; imapquota
= imapquota
->next
){
9370 so_puts(store
, _("Resource : "));
9371 so_puts(store
, imapquota
->name
);
9372 so_writec('\n', store
);
9374 so_puts(store
, _("Usage : "));
9375 so_puts(store
, long2string(imapquota
->usage
));
9376 if(!strucmp(imapquota
->name
,"STORAGE"))
9377 so_puts(store
, " KiB ");
9378 if(!strucmp(imapquota
->name
,"MESSAGE")){
9379 so_puts(store
, _(" message"));
9380 if(imapquota
->usage
!= 1)
9381 so_puts(store
, _("s ")); /* plural */
9383 so_puts(store
, _(" "));
9385 so_writec('(', store
);
9386 so_puts(store
, long2string(100*imapquota
->usage
/imapquota
->limit
));
9387 so_puts(store
, "%)\n");
9389 so_puts(store
, _("Limit : "));
9390 so_puts(store
, long2string(imapquota
->limit
));
9391 if(!strucmp(imapquota
->name
,"STORAGE"))
9392 so_puts(store
, " KiB\n\n");
9393 if(!strucmp(imapquota
->name
,"MESSAGE")){
9394 so_puts(store
, _(" message"));
9395 if(imapquota
->usage
!= 1)
9396 so_puts(store
, _("s\n\n")); /* plural */
9398 so_puts(store
, _("\n\n"));
9402 memset(&sargs
, 0, sizeof(SCROLL_S
));
9403 sargs
.text
.text
= so_text(store
);
9404 sargs
.text
.src
= CharStar
;
9405 sargs
.text
.desc
= _("Quota Resources Summary");
9406 sargs
.bar
.title
= _("QUOTA SUMMARY");
9407 sargs
.proc
.tool
= NULL
;
9408 sargs
.help
.text
= h_quota_command
;
9409 sargs
.help
.title
= NULL
;
9410 sargs
.keys
.menu
= NULL
;
9411 setbitmap(sargs
.keys
.bitmap
);
9417 mail_free_quotalist(&(state
->quota
));
9420 /*----------------------------------------------------------------------
9421 Prompt the user for the type of sort he desires
9423 Args: state -- pine state pointer
9424 q1 -- Line to prompt on
9426 Returns 0 if it was cancelled, 1 otherwise.
9429 select_sort(struct pine
*state
, int ql
, SortOrder
*sort
, int *rev
)
9431 char prompt
[200], tmp
[3], *p
;
9433 int deefault
= 'a', retval
= 1;
9438 DLG_SORTPARAM sortsel
;
9440 if (mswin_usedialog ()) {
9442 sortsel
.reverse
= mn_get_revsort (state
->msgmap
);
9443 sortsel
.cursort
= mn_get_sort (state
->msgmap
);
9444 /* assumption here that HelpType is char ** */
9445 sortsel
.helptext
= h_select_sort
;
9448 if ((retval
= os_sortdialog (&sortsel
))) {
9449 *sort
= sortsel
.cursort
;
9450 *rev
= sortsel
.reverse
;
9457 /*----- String together the prompt ------*/
9459 if(F_ON(F_USE_FK
,ps_global
))
9460 strncpy(prompt
, _("Choose type of sort : "), sizeof(prompt
));
9462 strncpy(prompt
, _("Choose type of sort, or 'R' to reverse current sort : "),
9465 for(i
= 0; state
->sort_types
[i
] != EndofList
; i
++) {
9467 p
= sorts
[i
].label
= sort_name(state
->sort_types
[i
]);
9468 while(*(p
+1) && islower((unsigned char)*p
))
9471 sorts
[i
].ch
= tolower((unsigned char)(tmp
[0] = *p
));
9472 sorts
[i
].name
= cpystr(tmp
);
9474 if(mn_get_sort(state
->msgmap
) == state
->sort_types
[i
])
9475 deefault
= sorts
[i
].rval
;
9479 sorts
[i
].rval
= 'r';
9480 sorts
[i
].name
= cpystr("R");
9481 if(F_ON(F_USE_FK
,ps_global
))
9482 sorts
[i
].label
= N_("Reverse");
9484 sorts
[i
].label
= "";
9487 help
= h_select_sort
;
9489 if((F_ON(F_USE_FK
,ps_global
)
9490 && ((s
= double_radio_buttons(prompt
,ql
,sorts
,deefault
,'x',
9491 help
,RB_NORM
)) != 'x'))
9493 (F_OFF(F_USE_FK
,ps_global
)
9494 && ((s
= radio_buttons(prompt
,ql
,sorts
,deefault
,'x',
9495 help
,RB_NORM
)) != 'x'))){
9496 state
->mangled_body
= 1; /* signal screen's changed */
9498 *rev
= !mn_get_revsort(state
->msgmap
);
9500 *sort
= state
->sort_types
[s
];
9502 if(F_ON(F_SHOW_SORT
, ps_global
))
9503 ps_global
->mangled_header
= 1;
9507 cmd_cancelled("Sort");
9511 fs_give((void **)&sorts
[i
].name
);
9513 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
9518 /*---------------------------------------------------------------------
9519 Build list of folders in the given context for user selection
9521 Args: c -- pointer to pointer to folder's context context
9522 f -- folder prefix to display
9523 sublist -- whether or not to use 'f's contents as prefix
9524 lister -- function used to do the actual display
9526 Returns: malloc'd string containing sequence, else NULL if
9527 no messages in msgmap with local "selected" flag.
9530 display_folder_list(CONTEXT_S
**c
, char *f
, int sublist
, int (*lister
) (struct pine
*, CONTEXT_S
**, char *, int))
9534 void (*redraw
)(void) = ps_global
->redrawer
;
9536 push_titlebar_state();
9538 if((rc
= (*lister
)(ps_global
, &tc
, f
, sublist
)) != 0)
9542 pop_titlebar_state();
9544 if((ps_global
->redrawer
= redraw
) != NULL
) /* reset old value, and test */
9545 (*ps_global
->redrawer
)();
9547 if(rc
== 1 && F_ON(F_SELECT_WO_CONFIRM
, ps_global
))
9555 * Allow user to choose a single item from a list of strings.
9557 * Args list -- Array of strings to choose from, NULL terminated.
9558 * displist -- Array of strings to display instead of displaying list.
9559 * Indices correspond to the list array. Display the displist
9560 * but return the item from list if displist non-NULL.
9561 * title -- For conf_scroll_screen
9562 * pdesc -- For conf_scroll_screen
9563 * help -- For conf_scroll_screen
9564 * htitle -- For conf_scroll_screen
9566 * Returns an allocated copy of the chosen item or NULL.
9569 choose_item_from_list(char **list
, char **displist
, char *title
, char *pdesc
, HelpType help
,
9570 char *htitle
, char *cursor_location
)
9572 LIST_SEL_S
*listhead
, *ls
, *p
, *starting_val
= NULL
;
9574 char *ret
= NULL
, *choice
= NULL
;
9576 /* build the LIST_SEL_S list */
9577 p
= listhead
= NULL
;
9578 for(t
= list
, dl
= displist
; *t
; t
++, dl
++){
9579 ls
= (LIST_SEL_S
*) fs_get(sizeof(*ls
));
9580 memset(ls
, 0, sizeof(*ls
));
9581 ls
->item
= cpystr(*t
);
9583 ls
->display_item
= cpystr(*dl
);
9585 if(cursor_location
&& (cursor_location
== (*t
)))
9599 if(!select_from_list_screen(listhead
, SFL_NONE
, title
, pdesc
,
9600 help
, htitle
, starting_val
))
9601 for(p
= listhead
; !choice
&& p
; p
= p
->next
)
9606 ret
= cpystr(choice
);
9608 free_list_sel(&listhead
);
9615 free_list_sel(LIST_SEL_S
**lsel
)
9618 free_list_sel(&(*lsel
)->next
);
9620 fs_give((void **) &(*lsel
)->item
);
9622 if((*lsel
)->display_item
)
9623 fs_give((void **) &(*lsel
)->display_item
);
9625 fs_give((void **) lsel
);
9631 * file_lister - call pico library's file lister
9634 file_lister(char *title
, char *path
, size_t pathlen
, char *file
, size_t filelen
, int newmail
, int flags
)
9638 void (*redraw
)(void) = ps_global
->redrawer
;
9640 standard_picobuf_setup(&pbf
);
9641 push_titlebar_state();
9645 /* BUG: what about help command and text? */
9646 pbf
.pine_anchor
= title
;
9648 rv
= pico_file_browse(&pbf
, path
, pathlen
, file
, filelen
, NULL
, 0, flags
);
9649 standard_picobuf_teardown(&pbf
);
9650 fix_windsize(ps_global
);
9651 init_signals(); /* has it's own signal stuff */
9653 /* Restore display's titlebar and body */
9654 pop_titlebar_state();
9656 if((ps_global
->redrawer
= redraw
) != NULL
)
9657 (*ps_global
->redrawer
)();
9663 /*----------------------------------------------------------------------
9664 Print current folder index
9668 print_index(struct pine
*state
, MSGNO_S
*msgmap
, int agg
)
9672 char buf
[MAX_SCREEN_COLS
+1];
9674 for(i
= 1L; i
<= mn_get_total(msgmap
); i
++){
9675 if(agg
&& !get_lflag(state
->mail_stream
, msgmap
, i
, MN_SLCT
))
9678 if(!agg
&& msgline_hidden(state
->mail_stream
, msgmap
, i
, 0))
9681 ice
= build_header_line(state
, state
->mail_stream
, msgmap
, i
, NULL
);
9685 * I don't understand why we'd want to mark the current message
9686 * instead of printing out the first character of the status
9687 * so I'm taking it out and including the first character of the
9688 * line instead. Hubert 2006-02-09
9690 if(!print_char((mn_is_cur(msgmap, i)) ? '>' : ' '))
9694 if(!gf_puts(simple_index_line(buf
,sizeof(buf
),ice
,i
),
9696 || !gf_puts(NEWLINE
, print_char
))
9708 * windows callback to get/set header mode state
9711 header_mode_callback(set
, args
)
9715 return(ps_global
->full_header
);
9720 * windows callback to get/set zoom mode state
9723 zoom_mode_callback(set
, args
)
9727 return(any_lflagged(ps_global
->msgmap
, MN_HIDE
) != 0);
9732 * windows callback to get/set zoom mode state
9735 any_selected_callback(set
, args
)
9739 return(any_lflagged(ps_global
->msgmap
, MN_SLCT
) != 0);
9747 flag_callback(set
, flags
)
9757 case 1: /* Important */
9758 permflag
= ps_global
->mail_stream
->perm_flagged
;
9762 permflag
= ps_global
->mail_stream
->perm_seen
;
9765 case 3: /* Answered */
9766 permflag
= ps_global
->mail_stream
->perm_answered
;
9769 case 4: /* Deleted */
9770 permflag
= ps_global
->mail_stream
->perm_deleted
;
9775 if(!(any_messages(ps_global
->msgmap
, NULL
, "to Flag")
9776 && can_set_flag(ps_global
, "flag", permflag
)))
9779 if(sp_io_error_on_stream(ps_global
->mail_stream
)){
9780 sp_set_io_error_on_stream(ps_global
->mail_stream
, 0);
9781 pine_mail_check(ps_global
->mail_stream
); /* forces write */
9785 msgno
= mn_m2raw(ps_global
->msgmap
, mn_get_cur(ps_global
->msgmap
));
9786 if(msgno
> 0L && ps_global
->mail_stream
9787 && msgno
<= ps_global
->mail_stream
->nmsgs
9788 && (mc
= mail_elt(ps_global
->mail_stream
, msgno
))
9791 * NOTE: code below is *VERY* sensitive to the order of
9792 * the messages defined in resource.h for flag handling.
9793 * Don't change it unless you know what you're doing.
9800 case 1 : /* Important */
9801 flagstr
= "\\FLAGGED";
9802 mflag
= (mc
->flagged
) ? 0L : ST_SET
;
9807 mflag
= (mc
->seen
) ? 0L : ST_SET
;
9810 case 3 : /* Answered */
9811 flagstr
= "\\ANSWERED";
9812 mflag
= (mc
->answered
) ? 0L : ST_SET
;
9815 case 4 : /* Deleted */
9816 flagstr
= "\\DELETED";
9817 mflag
= (mc
->deleted
) ? 0L : ST_SET
;
9820 default : /* bogus */
9824 mail_flag(ps_global
->mail_stream
, long2string(msgno
),
9827 if(ps_global
->redrawer
)
9828 (*ps_global
->redrawer
)();
9855 * BUG: Should teach this about keywords
9861 static MPopup flag_submenu
[] = {
9862 {tMessage
, {N_("Important"), lNormal
}, {IDM_MI_FLAGIMPORTANT
}},
9863 {tMessage
, {N_("New"), lNormal
}, {IDM_MI_FLAGNEW
}},
9864 {tMessage
, {N_("Answered"), lNormal
}, {IDM_MI_FLAGANSWERED
}},
9865 {tMessage
, {N_("Deleted"), lNormal
}, {IDM_MI_FLAGDELETED
}},
9870 flag_submenu
[0].label
.style
= (mc
&& mc
->flagged
) ? lChecked
: lNormal
;
9873 flag_submenu
[1].label
.style
= (mc
&& mc
->seen
) ? lNormal
: lChecked
;
9876 flag_submenu
[2].label
.style
= (mc
&& mc
->answered
) ? lChecked
: lNormal
;
9879 flag_submenu
[3].label
.style
= (mc
&& mc
->deleted
) ? lChecked
: lNormal
;
9881 return(flag_submenu
);
9884 #endif /* _WINDOWS */