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-2020 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_gm_content(MAILSTREAM
*, MSGNO_S
*, long, SEARCHSET
**);
109 int select_by_size(MAILSTREAM
*, SEARCHSET
**);
110 SEARCHSET
*visible_searchset(MAILSTREAM
*, MSGNO_S
*);
111 int select_by_status(MAILSTREAM
*, SEARCHSET
**);
112 int select_by_rule(MAILSTREAM
*, SEARCHSET
**);
113 int select_by_thread(MAILSTREAM
*, MSGNO_S
*, SEARCHSET
**);
114 char *choose_a_rule(int);
115 int select_by_keyword(MAILSTREAM
*, SEARCHSET
**);
116 char *choose_a_keyword(void);
117 int select_sort(struct pine
*, int, SortOrder
*, int *);
118 int print_index(struct pine
*, MSGNO_S
*, int);
121 * List of Select options used by apply_* functions...
123 static char *sel_pmt1
= N_("ALTER message selection : ");
124 ESCKEY_S sel_opts1
[] = {
125 /* TRANSLATORS: these are keymenu names for selecting. Broaden selection means
126 we will add more messages to the selection, Narrow selection means we will
127 remove some selections (like a logical AND instead of logical OR), and Flip
128 Selected means that all the messages that are currently selected become unselected,
129 and all the unselected messages become selected. */
130 {'a', 'a', "A", N_("unselect All")},
131 {'c', 'c', "C", NULL
},
132 {'b', 'b', "B", N_("Broaden selctn")},
133 {'n', 'n', "N", N_("Narrow selctn")},
134 {'f', 'f', "F", N_("Flip selected")},
135 {'r', 'r', "R", N_("Replace selctn")},
140 #define SEL_OPTS_THREAD 9 /* index number of "tHread" */
141 #define SEL_OPTS_THREAD_CH 'h'
142 #define SEL_OPTS_XGMEXT 10 /* index number of "boX" */
143 #define SEL_OPTS_XGMEXT_CH 'g'
145 char *sel_pmt2
= "SELECT criteria : ";
146 static ESCKEY_S sel_opts2
[] = {
147 /* TRANSLATORS: very short descriptions of message selection criteria. Select Cur
148 means select the currently highlighted message; select by Number is by message
149 number; Status is by status of the message, for example the message might be
150 New or it might be Unseen or marked Important; Size has the Z upper case because
151 it is a Z command; Keyword is an alpine keyword that has been set by the user;
152 and Rule is an alpine rule */
153 {'a', 'a', "A", N_("select All")},
154 {'c', 'c', "C", N_("select Cur")},
155 {'n', 'n', "N", N_("Number")},
156 {'d', 'd', "D", N_("Date")},
157 {'t', 't', "T", N_("Text")},
158 {'s', 's', "S", N_("Status")},
159 {'z', 'z', "Z", N_("siZe")},
160 {'k', 'k', "K", N_("Keyword")},
161 {'r', 'r', "R", N_("Rule")},
162 {SEL_OPTS_THREAD_CH
, 'h', "H", N_("tHread")},
167 static ESCKEY_S sel_opts3
[] = {
168 /* TRANSLATORS: these are operations we can do on a set of selected messages.
169 Del is Delete; Undel is Undelete; TakeAddr means to Take some Addresses into
170 the address book; Save means to save the messages into another alpine folder;
171 Export means to copy the messages to a file outside of alpine, external to
173 {'d', 'd', "D", N_("Del")},
174 {'u', 'u', "U", N_("Undel")},
175 {'r', 'r', "R", N_("Reply")},
176 {'f', 'f', "F", N_("Forward")},
177 {'%', '%', "%", N_("Print")},
178 {'t', 't', "T", N_("TakeAddr")},
179 {'s', 's', "S", N_("Save")},
180 {'e', 'e', "E", N_("Export")},
181 { -1, 0, NULL
, NULL
},
182 { -1, 0, NULL
, NULL
},
183 { -1, 0, NULL
, NULL
},
184 { -1, 0, NULL
, NULL
},
185 { -1, 0, NULL
, NULL
},
186 { -1, 0, NULL
, NULL
},
187 { -1, 0, NULL
, NULL
},
191 static ESCKEY_S sel_opts4
[] = {
192 {'a', 'a', "A", N_("select All")},
193 /* TRANSLATORS: select currently highlighted message Thread */
194 {'c', 'c', "C", N_("select Curthrd")},
195 {'n', 'n', "N", N_("Number")},
196 {'d', 'd', "D", N_("Date")},
197 {'t', 't', "T", N_("Text")},
198 {'s', 's', "S", N_("Status")},
199 {'z', 'z', "Z", N_("siZe")},
200 {'k', 'k', "K", N_("Keyword")},
201 {'r', 'r', "R", N_("Rule")},
202 {SEL_OPTS_THREAD_CH
, 'h', "H", N_("tHread")},
206 static ESCKEY_S sel_opts5
[] = {
207 /* TRANSLATORS: very short descriptions of message selection criteria. Select Cur
208 means select the currently highlighted message; select by Number is by message
209 number; Status is by status of the message, for example the message might be
210 New or it might be Unseen or marked Important; Size has the Z upper case because
211 it is a Z command; Keyword is an alpine keyword that has been set by the user;
212 and Rule is an alpine rule */
213 {'a', 'a', "A", N_("select All")},
214 {'c', 'c', "C", N_("select Cur")},
215 {'n', 'n', "N", N_("Number")},
216 {'d', 'd', "D", N_("Date")},
217 {'t', 't', "T", N_("Text")},
218 {'s', 's', "S", N_("Status")},
219 {'z', 'z', "Z", N_("siZe")},
220 {'k', 'k', "K", N_("Keyword")},
221 {'r', 'r', "R", N_("Rule")},
222 {SEL_OPTS_THREAD_CH
, 'h', "H", N_("tHread")},
223 {SEL_OPTS_XGMEXT_CH
, 'g', "G", N_("GmSearch")},
231 static ESCKEY_S sel_opts6
[] = {
232 {'a', 'a', "A", N_("select All")},
233 /* TRANSLATORS: select currently highlighted message Thread */
234 {'c', 'c', "C", N_("select Curthrd")},
235 {'n', 'n', "N", N_("Number")},
236 {'d', 'd', "D", N_("Date")},
237 {'t', 't', "T", N_("Text")},
238 {'s', 's', "S", N_("Status")},
239 {'z', 'z', "Z", N_("siZe")},
240 {'k', 'k', "K", N_("Keyword")},
241 {'r', 'r', "R", N_("Rule")},
242 {SEL_OPTS_THREAD_CH
, 'h', "H", N_("tHread")},
243 {SEL_OPTS_XGMEXT_CH
, 'g', "G", N_("Gmail")},
252 static char *sel_flag
=
253 N_("Select New, Deleted, Answered, Forwarded, or Important messages ? ");
254 static char *sel_flag_not
=
255 N_("Select NOT New, NOT Deleted, NOT Answered, NOT Forwarded or NOT Important msgs ? ");
256 static ESCKEY_S sel_flag_opt
[] = {
257 /* TRANSLATORS: When selecting messages by message Status these are the
258 different types of Status you can select on. Is the message New, Recent,
259 and so on. Not means flip the meaning of the selection to the opposite
260 thing, so message is not New or not Important. */
261 {'n', 'n', "N", N_("New")},
262 {'*', '*', "*", N_("Important")},
263 {'d', 'd', "D", N_("Deleted")},
264 {'a', 'a', "A", N_("Answered")},
265 {'f', 'f', "F", N_("Forwarded")},
267 {'!', '!', "!", N_("Not")},
269 {'r', 'r', "R", N_("Recent")},
270 {'u', 'u', "U", N_("Unseen")},
275 static ESCKEY_S sel_date_opt
[] = {
277 /* TRANSLATORS: options when selecting messages by Date */
278 {ctrl('P'), 12, "^P", N_("Prev Day")},
279 {ctrl('N'), 13, "^N", N_("Next Day")},
280 {ctrl('X'), 11, "^X", N_("Cur Msg")},
281 {ctrl('W'), 14, "^W", N_("Toggle When")},
282 {KEY_UP
, 12, "", ""},
283 {KEY_DOWN
, 13, "", ""},
288 static char *sel_x_gm_ext
=
290 static char *sel_text
=
291 N_("Select based on To, From, Cc, Recip, Partic, Subject fields or All msg text ? ");
292 static char *sel_text_not
=
293 N_("Select based on NOT To, From, Cc, Recip, Partic, Subject or All msg text ? ");
294 static ESCKEY_S sel_text_opt
[] = {
295 /* TRANSLATORS: Select messages based on the text contained in the From line, or
296 the Subject line, and so on. */
297 {'f', 'f', "F", N_("From")},
298 {'s', 's', "S", N_("Subject")},
299 {'t', 't', "T", N_("To")},
300 {'a', 'a', "A", N_("All Text")},
301 {'c', 'c', "C", N_("Cc")},
302 {'!', '!', "!", N_("Not")},
303 {'r', 'r', "R", N_("Recipient")},
304 {'p', 'p', "P", N_("Participant")},
305 {'b', 'b', "B", N_("Body")},
306 {'h', 'h', "H", N_("Header")},
310 static ESCKEY_S choose_action
[] = {
311 {'c', 'c', "C", N_("Compose")},
312 {'r', 'r', "R", N_("Reply")},
313 {'f', 'f', "F", N_("Forward")},
314 {'b', 'b', "B", N_("Bounce")},
318 static char *select_num
=
319 N_("Enter comma-delimited list of numbers (dash between ranges): ");
321 static char *select_size_larger_msg
=
322 N_("Select messages with size larger than: ");
324 static char *select_size_smaller_msg
=
325 N_("Select messages with size smaller than: ");
327 static char *sel_size_larger
= N_("Larger");
328 static char *sel_size_smaller
= N_("Smaller");
329 static ESCKEY_S sel_size_opt
[] = {
331 {ctrl('W'), 14, "^W", NULL
},
335 static ESCKEY_S sel_key_opt
[] = {
337 {ctrl('T'), 14, "^T", N_("To List")},
339 {'!', '!', "!", N_("Not")},
343 static ESCKEY_S flag_text_opt
[] = {
344 /* TRANSLATORS: these are types of flags (markers) that the user can
345 set. For example, they can flag the message as an important message. */
346 {'n', 'n', "N", N_("New")},
347 {'*', '*', "*", N_("Important")},
348 {'d', 'd', "D", N_("Deleted")},
349 {'a', 'a', "A", N_("Answered")},
350 {'f', 'f', "F", N_("Forwarded")},
351 {'!', '!', "!", N_("Not")},
352 {ctrl('T'), 10, "^T", N_("To Flag Details")},
357 alpine_smime_confirm_save(char *email
)
361 snprintf(prompt
, sizeof(prompt
), _("Save certificate for <%s>"),
362 email
? email
: _("missing address"));
363 return want_to(prompt
, 'n', 'x', NO_HELP
, WT_NORM
) == 'y';
367 alpine_get_password(char *prompt
, char *pass
, size_t len
)
369 int flags
= OE_PASSWD
| OE_DISALLOW_HELP
;
371 return optionally_enter(pass
,
372 -(ps_global
->ttyo
? FOOTER_ROWS(ps_global
) : 3),
373 0, len
, prompt
, NULL
, NO_HELP
, &flags
);
377 smime_import_certificate(char *filename
, char *full_filename
, char *what
, size_t len
)
380 static HISTORY_S
*history
= NULL
;
381 static ESCKEY_S eopts
[] = {
382 {ctrl('T'), 10, "^T", N_("To Files")},
384 {-1, 0, NULL
, NULL
}};
386 if(F_ON(F_ENABLE_TAB_COMPLETE
,ps_global
)){
387 eopts
[r
].ch
= ctrl('I');
389 eopts
[r
].name
= "TAB";
390 eopts
[r
].label
= N_("Complete");
396 full_filename
[0] = '\0';
398 r
= get_export_filename(ps_global
, filename
, NULL
, full_filename
,
399 len
, what
, "IMPORT", eopts
, NULL
,
400 -FOOTER_ROWS(ps_global
), GE_IS_IMPORT
, &history
);
406 /*----------------------------------------------------------------------
407 The giant switch on the commands for index and viewing
409 Input: command -- The command char/code
410 in_index -- flag indicating command is from index
411 orig_command -- The original command typed before pre-processing
412 Output: force_mailchk -- Set to tell caller to force call to new_mail().
416 Returns 1 if the message number or attachment to show changed
419 process_cmd(struct pine
*state
, MAILSTREAM
*stream
, MSGNO_S
*msgmap
,
420 int command
, CmdWhere in_index
, int *force_mailchk
)
422 int question_line
, a_changed
, flags
= 0, ret
, j
;
424 long new_msgno
, del_count
, old_msgno
, i
;
426 char *newfolder
, prompt
[MAX_SCREEN_COLS
+1];
429 #if defined(DOS) && !defined(_WINDOWS)
430 extern long coreleft();
433 dprint((4, "\n - process_cmd(cmd=%d) -\n", command
));
435 question_line
= -FOOTER_ROWS(state
);
436 state
->mangled_screen
= 0;
437 state
->mangled_footer
= 0;
438 state
->mangled_header
= 0;
439 state
->next_screen
= SCREEN_FUN_NULL
;
440 old_msgno
= mn_get_cur(msgmap
);
445 /*------------- Help --------*/
448 * We're not using the h_mail_view portion of this right now because
449 * that call is being handled in scrolltool() before it gets
450 * here. Leave it in case we change how it works.
452 helper((in_index
== MsgIndx
)
456 : h_mail_thread_index
,
457 (in_index
== MsgIndx
)
458 ? _("HELP FOR MESSAGE INDEX")
460 ? _("HELP FOR MESSAGE TEXT")
461 : _("HELP FOR THREAD INDEX"),
463 dprint((4,"MAIL_CMD: did help command\n"));
464 state
->mangled_screen
= 1;
468 /*--------- Return to main menu ------------*/
470 state
->next_screen
= main_menu_screen
;
471 dprint((2,"MAIL_CMD: going back to main menu\n"));
475 /*------- View message text --------*/
478 if(any_messages(msgmap
, NULL
, "to View")){
479 state
->next_screen
= mail_view_screen
;
485 /*------- View attachment --------*/
487 state
->next_screen
= attachment_screen
;
488 dprint((2,"MAIL_CMD: going to attachment screen\n"));
492 /*---------- Previous message ----------*/
494 if(any_messages(msgmap
, NULL
, NULL
)){
495 if((i
= mn_get_cur(msgmap
)) > 1L){
496 mn_dec_cur(stream
, msgmap
,
497 (in_index
== View
&& THREADING()
498 && sp_viewing_a_thread(stream
))
501 ? MH_ANYTHD
: MH_NONE
);
502 if(i
== mn_get_cur(msgmap
)){
503 PINETHRD_S
*thrd
= NULL
, *topthrd
= NULL
;
505 if(THRD_INDX_ENABLED()){
506 mn_dec_cur(stream
, msgmap
, MH_ANYTHD
);
507 if(i
== mn_get_cur(msgmap
))
508 q_status_message1(SM_ORDER
, 0, 2,
509 _("Already on first %s in Zoomed Index"),
510 THRD_INDX() ? _("thread") : _("message"));
513 || F_ON(F_NEXT_THRD_WO_CONFIRM
, state
))
516 ret
= want_to(_("View previous thread"), 'y', 'x',
520 q_status_message(SM_ORDER
, 0, 2,
521 _("Viewing previous thread"));
522 new_msgno
= mn_get_cur(msgmap
);
523 mn_set_cur(msgmap
, i
);
524 if(unview_thread(state
, stream
, msgmap
)){
525 state
->next_screen
= mail_index_screen
;
526 state
->view_skipped_index
= 0;
527 state
->mangled_screen
= 1;
530 mn_set_cur(msgmap
, new_msgno
);
531 if(THRD_AUTO_VIEW() && in_index
== View
){
533 thrd
= fetch_thread(stream
,
536 if(count_lflags_in_thread(stream
, thrd
,
539 if(view_thread(state
, stream
, msgmap
, 1)){
540 if(current_index_state
)
541 msgmap
->top_after_thrd
= current_index_state
->msg_at_top
;
543 state
->view_skipped_index
= 1;
544 command
= MC_VIEW_TEXT
;
551 if(THRD_AUTO_VIEW() && in_index
!= View
){
552 thrd
= fetch_thread(stream
, mn_m2raw(msgmap
, new_msgno
));
553 if(thrd
&& thrd
->top
)
554 topthrd
= fetch_thread(stream
, thrd
->top
);
557 j
= count_lflags_in_thread(stream
, topthrd
, msgmap
, MN_NONE
);
560 if(!THRD_AUTO_VIEW() || in_index
== View
|| j
!= 1){
561 if(view_thread(state
, stream
, msgmap
, 1)
562 && current_index_state
)
563 msgmap
->top_after_thrd
= current_index_state
->msg_at_top
;
567 state
->next_screen
= SCREEN_FUN_NULL
;
570 mn_set_cur(msgmap
, i
); /* put it back */
574 q_status_message1(SM_ORDER
, 0, 2,
575 _("Already on first %s in Zoomed Index"),
576 THRD_INDX() ? _("thread") : _("message"));
583 && ((now
= time(0)) > state
->last_nextitem_forcechk
)){
585 /* check at most once a second */
586 state
->last_nextitem_forcechk
= now
;
589 q_status_message1(SM_ORDER
, 0, 1, _("Already on first %s"),
590 THRD_INDX() ? _("thread") : _("message"));
597 /*---------- Next Message ----------*/
599 if(mn_get_total(msgmap
) > 0L
600 && ((i
= mn_get_cur(msgmap
)) < mn_get_total(msgmap
))){
601 mn_inc_cur(stream
, msgmap
,
602 (in_index
== View
&& THREADING()
603 && sp_viewing_a_thread(stream
))
606 ? MH_ANYTHD
: MH_NONE
);
607 if(i
== mn_get_cur(msgmap
)){
608 PINETHRD_S
*thrd
, *topthrd
;
610 if(THRD_INDX_ENABLED()){
612 mn_inc_cur(stream
, msgmap
, MH_ANYTHD
);
614 if(i
== mn_get_cur(msgmap
)){
615 if(any_lflagged(msgmap
, MN_HIDE
))
616 any_messages(NULL
, "more", "in Zoomed Index");
622 || F_ON(F_NEXT_THRD_WO_CONFIRM
, state
))
625 ret
= want_to(_("View next thread"), 'y', 'x',
629 q_status_message(SM_ORDER
, 0, 2,
630 _("Viewing next thread"));
631 new_msgno
= mn_get_cur(msgmap
);
632 mn_set_cur(msgmap
, i
);
633 if(unview_thread(state
, stream
, msgmap
)){
634 state
->next_screen
= mail_index_screen
;
635 state
->view_skipped_index
= 0;
636 state
->mangled_screen
= 1;
639 mn_set_cur(msgmap
, new_msgno
);
640 if(THRD_AUTO_VIEW() && in_index
== View
){
642 thrd
= fetch_thread(stream
,
645 if(count_lflags_in_thread(stream
, thrd
,
648 if(view_thread(state
, stream
, msgmap
, 1)){
649 if(current_index_state
)
650 msgmap
->top_after_thrd
= current_index_state
->msg_at_top
;
652 state
->view_skipped_index
= 1;
653 command
= MC_VIEW_TEXT
;
660 if(THRD_AUTO_VIEW() && in_index
!= View
){
661 thrd
= fetch_thread(stream
, mn_m2raw(msgmap
, new_msgno
));
662 if(thrd
&& thrd
->top
)
663 topthrd
= fetch_thread(stream
, thrd
->top
);
666 j
= count_lflags_in_thread(stream
, topthrd
, msgmap
, MN_NONE
);
669 if(!THRD_AUTO_VIEW() || in_index
== View
|| j
!= 1){
670 if(view_thread(state
, stream
, msgmap
, 1)
671 && current_index_state
)
672 msgmap
->top_after_thrd
= current_index_state
->msg_at_top
;
676 state
->next_screen
= SCREEN_FUN_NULL
;
679 mn_set_cur(msgmap
, i
); /* put it back */
683 && (thrd
= fetch_thread(stream
, mn_m2raw(msgmap
, i
)))
685 && get_lflag(stream
, NULL
, thrd
->rawno
, MN_COLL
)){
686 q_status_message(SM_ORDER
, 0, 2,
687 _("Expand collapsed thread to see more messages"));
690 any_messages(NULL
, "more", "in Zoomed Index");
698 || (state
->context_current
->use
& CNTXT_INCMNG
)){
699 char nextfolder
[MAXPATH
];
701 strncpy(nextfolder
, state
->cur_folder
, sizeof(nextfolder
));
702 nextfolder
[sizeof(nextfolder
)-1] = '\0';
703 if(next_folder(NULL
, nextfolder
, sizeof(nextfolder
), nextfolder
,
704 state
->context_current
, NULL
, NULL
))
705 strncpy(prompt
, _(". Press TAB for next folder."),
708 strncpy(prompt
, _(". No more folders to TAB to."),
711 prompt
[sizeof(prompt
)-1] = '\0';
714 any_messages(NULL
, (mn_get_total(msgmap
) > 0L) ? "more" : NULL
,
715 prompt
[0] ? prompt
: NULL
);
718 && ((now
= time(0)) > state
->last_nextitem_forcechk
)){
720 /* check at most once a second */
721 state
->last_nextitem_forcechk
= now
;
728 /*---------- Delete message ----------*/
730 (void) cmd_delete(state
, msgmap
, MCMD_NONE
,
731 (in_index
== View
) ? cmd_delete_view
: cmd_delete_index
);
735 /*---------- Undelete message ----------*/
737 (void) cmd_undelete(state
, msgmap
, MCMD_NONE
);
738 update_titlebar_status();
742 /*---------- Reply to message ----------*/
744 (void) cmd_reply(state
, msgmap
, MCMD_NONE
, NULL
);
748 /*---------- Forward message ----------*/
750 (void) cmd_forward(state
, msgmap
, MCMD_NONE
, NULL
);
754 /*---------- Quit pine ------------*/
756 state
->next_screen
= quit_screen
;
757 dprint((1,"MAIL_CMD: quit\n"));
761 /*---------- Compose message ----------*/
763 state
->prev_screen
= (in_index
== View
) ? mail_view_screen
765 compose_screen(state
);
766 state
->mangled_screen
= 1;
767 if (state
->next_screen
)
772 /*---------- Alt Compose message ----------*/
774 state
->prev_screen
= (in_index
== View
) ? mail_view_screen
777 if(state
->next_screen
)
783 /*--------- Folders menu ------------*/
785 state
->start_in_context
= 1;
787 /*--------- Top of Folders list menu ------------*/
788 case MC_COLLECTIONS
:
789 state
->next_screen
= folder_screen
;
790 dprint((2,"MAIL_CMD: going to folder/collection menu\n"));
794 /*---------- Open specific new folder ----------*/
796 tc
= (state
->context_last
&& !NEWS_TEST(state
->context_current
))
797 ? state
->context_last
: state
->context_current
;
799 newfolder
= broach_folder(question_line
, 1, ¬realinbox
, &tc
);
801 visit_folder(state
, newfolder
, tc
, NULL
, notrealinbox
? 0L : DB_INBOXWOCNTXT
);
808 /*------- Go to Index Screen ----------*/
810 state
->next_screen
= mail_index_screen
;
813 /*------- Skip to next interesting message -----------*/
819 * If we're in the thread index, start looking after this
820 * thread. We don't want to match something in the current
823 start
= mn_get_cur(msgmap
);
824 thrd
= fetch_thread(stream
, mn_m2raw(msgmap
, mn_get_cur(msgmap
)));
825 if(mn_get_revsort(msgmap
)){
826 /* if reversed, top of thread is last one before next thread */
827 if(thrd
&& thrd
->top
)
828 start
= mn_raw2m(msgmap
, thrd
->top
);
831 /* last msg of thread is at the ends of the branches/nexts */
833 start
= mn_raw2m(msgmap
, thrd
->rawno
);
835 thrd
= fetch_thread(stream
, thrd
->branch
);
837 thrd
= fetch_thread(stream
, thrd
->next
);
844 * Flags is 0 in this case because we want to not skip
845 * messages inside of threads so that we can find threads
846 * which have some unseen messages even though the top-level
847 * of the thread is already seen.
848 * If new_msgno ends up being a message which is not visible
849 * because it isn't at the top-level, the current message #
850 * will be adjusted below in adjust_cur.
853 new_msgno
= next_sorted_flagged((F_UNDEL
855 | ((F_ON(F_TAB_TO_NEW
,state
))
857 stream
, start
, &flags
);
859 else if(THREADING() && sp_viewing_a_thread(stream
)){
860 PINETHRD_S
*thrd
, *topthrd
= NULL
;
862 start
= mn_get_cur(msgmap
);
865 * Things are especially complicated when we're viewing_a_thread
866 * from the thread index. First we have to check within the
867 * current thread for a new message. If none is found, then
868 * we search in the next threads and offer to continue in
869 * them. Then we offer to go to the next folder.
871 flags
= NSF_SKIP_CHID
;
872 new_msgno
= next_sorted_flagged((F_UNDEL
874 | ((F_ON(F_TAB_TO_NEW
,state
))
876 stream
, start
, &flags
);
878 * If we found a match then we are done, that is another message
879 * in the current thread index. Otherwise, we have to look
882 if(!(flags
& NSF_FLAG_MATCH
)){
887 new_msgno
= next_sorted_flagged((F_UNDEL
889 | ((F_ON(F_TAB_TO_NEW
,
892 stream
, start
, &flags
);
894 * If we got a match, new_msgno is a message in
895 * a different thread from the one we are viewing.
897 if(flags
& NSF_FLAG_MATCH
){
898 thrd
= fetch_thread(stream
, mn_m2raw(msgmap
,new_msgno
));
899 if(thrd
&& thrd
->top
)
900 topthrd
= fetch_thread(stream
, thrd
->top
);
902 if(F_OFF(F_AUTO_OPEN_NEXT_UNREAD
, state
)){
903 static ESCKEY_S next_opt
[] = {
904 {'y', 'y', "Y", N_("Yes")},
905 {'n', 'n', "N", N_("No")},
906 {TAB
, 'n', "Tab", N_("NextNew")},
911 snprintf(prompt
, sizeof(prompt
), _("View thread number %s? "),
912 topthrd
? comatose(topthrd
->thrdno
) : "?");
914 snprintf(prompt
, sizeof(prompt
),
915 _("View message in thread number %s? "),
916 topthrd
? comatose(topthrd
->thrdno
) : "?");
918 prompt
[sizeof(prompt
)-1] = '\0';
920 ret
= radio_buttons(prompt
, -FOOTER_ROWS(state
),
921 next_opt
, 'y', 'x', NO_HELP
,
932 if(unview_thread(state
, stream
, msgmap
)){
933 state
->next_screen
= mail_index_screen
;
934 state
->view_skipped_index
= 0;
935 state
->mangled_screen
= 1;
938 mn_set_cur(msgmap
, new_msgno
);
939 if(THRD_AUTO_VIEW()){
941 if(count_lflags_in_thread(stream
, topthrd
,
942 msgmap
, MN_NONE
) == 1){
943 if(view_thread(state
, stream
, msgmap
, 1)){
944 if(current_index_state
)
945 msgmap
->top_after_thrd
= current_index_state
->msg_at_top
;
947 state
->view_skipped_index
= 1;
948 command
= MC_VIEW_TEXT
;
954 if(view_thread(state
, stream
, msgmap
, 1) && current_index_state
)
955 msgmap
->top_after_thrd
= current_index_state
->msg_at_top
;
957 state
->next_screen
= SCREEN_FUN_NULL
;
960 else if(ret
== 'n' && topthrd
){
962 * skip to end of this thread and look starting
963 * in the next thread.
965 if(mn_get_revsort(msgmap
)){
967 * if reversed, top of thread is last one
970 start
= mn_raw2m(msgmap
, topthrd
->rawno
);
974 * last msg of thread is at the ends of
979 start
= mn_raw2m(msgmap
, thrd
->rawno
);
981 thrd
= fetch_thread(stream
, thrd
->branch
);
983 thrd
= fetch_thread(stream
, thrd
->next
);
999 start
= mn_get_cur(msgmap
);
1002 * If we are on a collapsed thread, start looking after the
1003 * collapsed part, unless we are viewing the message.
1005 if(THREADING() && in_index
!= View
){
1010 rawno
= mn_m2raw(msgmap
, start
);
1011 thrd
= fetch_thread(stream
, rawno
);
1012 collapsed
= thrd
&& thrd
->next
1013 && get_lflag(stream
, NULL
, rawno
, MN_COLL
);
1016 if(mn_get_revsort(msgmap
)){
1017 if(thrd
&& thrd
->top
)
1018 start
= mn_raw2m(msgmap
, thrd
->top
);
1022 start
= mn_raw2m(msgmap
, thrd
->rawno
);
1024 thrd
= fetch_thread(stream
, thrd
->branch
);
1026 thrd
= fetch_thread(stream
, thrd
->next
);
1035 new_msgno
= next_sorted_flagged((F_UNDEL
1037 | ((F_ON(F_TAB_TO_NEW
,state
))
1039 stream
, start
, &flags
);
1043 * If there weren't any unread messages left, OR there
1044 * aren't any messages at all, we may want to offer to
1045 * go on to the next folder...
1047 if(flags
& NSF_FLAG_MATCH
){
1048 mn_set_cur(msgmap
, new_msgno
);
1049 if(in_index
!= View
)
1050 adjust_cur_to_visible(stream
, msgmap
);
1053 int in_inbox
= sp_flagged(stream
, SP_INBOX
);
1055 if(state
->context_current
1056 && ((NEWS_TEST(state
->context_current
)
1057 && context_isambig(state
->cur_folder
))
1058 || ((state
->context_current
->use
& CNTXT_INCMNG
)
1060 || folder_index(state
->cur_folder
,
1061 state
->context_current
,
1062 FI_FOLDER
) >= 0)))){
1063 char nextfolder
[MAXPATH
];
1064 MAILSTREAM
*nextstream
= NULL
;
1068 strncpy(nextfolder
, state
->cur_folder
, sizeof(nextfolder
));
1069 nextfolder
[sizeof(nextfolder
)-1] = '\0';
1071 if(!(next_folder(&nextstream
, nextfolder
, sizeof(nextfolder
), nextfolder
,
1072 state
->context_current
, &recent_cnt
,
1073 F_ON(F_TAB_NO_CONFIRM
,state
)
1074 ? NULL
: &did_cancel
))){
1076 static ESCKEY_S inbox_opt
[] = {
1077 {'y', 'y', "Y", N_("Yes")},
1078 {'n', 'n', "N", N_("No")},
1079 {TAB
, 'z', "Tab", N_("To Inbox")},
1083 if(F_ON(F_RET_INBOX_NO_CONFIRM
,state
))
1086 /* TRANSLATORS: this is a question, with some information followed
1087 by Return to INBOX? */
1088 if(state
->context_current
->use
&CNTXT_INCMNG
)
1089 snprintf(prompt
, sizeof(prompt
), _("No more incoming folders. Return to \"%s\"? "), state
->inbox_name
);
1091 snprintf(prompt
, sizeof(prompt
), _("No more news groups. Return to \"%s\"? "), state
->inbox_name
);
1093 ret
= radio_buttons(prompt
, -FOOTER_ROWS(state
),
1094 inbox_opt
, 'y', 'x',
1099 * 'z' is a synonym for 'y'. It is not 'y'
1100 * so that it isn't displayed as a default
1101 * action with square-brackets around it
1104 if(ret
== 'y' || ret
== 'z'){
1105 visit_folder(state
, state
->inbox_name
,
1106 state
->context_current
,
1107 NULL
, DB_INBOXWOCNTXT
);
1111 else if (did_cancel
)
1112 cmd_cancelled(NULL
);
1114 if(state
->context_current
->use
&CNTXT_INCMNG
)
1115 q_status_message(SM_ORDER
, 0, 2, _("No more incoming folders"));
1117 q_status_message(SM_ORDER
, 0, 2, _("No more news groups"));
1125 char *front
, type
[80], cnt
[CNTLEN
], fbuf
[MAX_SCREEN_COLS
/2+1];
1126 int rbspace
, avail
, need
, take_back
;
1130 * Incoming_folder_ or news_group_ or folder_ or group_
1132 * _(13 recent) or _(some recent) or nothing
1135 front
= "View next";
1137 (state
->context_current
->use
& CNTXT_INCMNG
)
1138 ? "Incoming folder" : "news group",
1140 type
[sizeof(type
)-1] = '\0';
1141 snprintf(cnt
, sizeof(cnt
), " (%.*s %s)", CNTLEN
-20,
1142 recent_cnt
? long2string(recent_cnt
) : "some",
1143 F_ON(F_TAB_USES_UNSEEN
, ps_global
)
1144 ? "unseen" : "recent");
1145 cnt
[sizeof(cnt
)-1] = '\0';
1148 * Space reserved for radio_buttons call.
1149 * If we make this 3 then radio_buttons won't mess
1150 * with the prompt. If we make it 2, then we get
1151 * one more character to use but radio_buttons will
1152 * cut off the last character of our prompt, which is
1153 * ok because it is a space.
1156 avail
= ps_global
->ttyo
? ps_global
->ttyo
->screen_cols
1158 need
= strlen(front
)+1 + strlen(type
)+1 +
1159 + strlen(nextfolder
)+2 + strlen(cnt
) +
1162 take_back
= strlen(type
);
1164 (state
->context_current
->use
& CNTXT_INCMNG
)
1165 ? "folder" : "group", sizeof(type
));
1166 take_back
-= strlen(type
);
1169 need
-= strlen(cnt
);
1173 /* MAX_SCREEN_COLS+1 = sizeof(prompt) */
1174 snprintf(prompt
, sizeof(prompt
), "%.*s %.*s \"%.*s\"%.*s? ",
1175 (MAX_SCREEN_COLS
+1)/8, front
,
1176 (MAX_SCREEN_COLS
+1)/8, type
,
1177 (MAX_SCREEN_COLS
+1)/2,
1178 short_str(nextfolder
, fbuf
, sizeof(fbuf
),
1179 strlen(nextfolder
) -
1180 ((need
>avail
) ? (need
-avail
) : 0),
1182 (MAX_SCREEN_COLS
+1)/8, cnt
);
1183 prompt
[sizeof(prompt
)-1] = '\0';
1187 * When help gets added, this'll have to become
1188 * a loop like the rest...
1190 if(F_OFF(F_AUTO_OPEN_NEXT_UNREAD
, state
)){
1191 static ESCKEY_S next_opt
[] = {
1192 {'y', 'y', "Y", N_("Yes")},
1193 {'n', 'n', "N", N_("No")},
1194 {TAB
, 'n', "Tab", N_("NextNew")},
1198 ret
= radio_buttons(prompt
, -FOOTER_ROWS(state
),
1199 next_opt
, 'y', 'x', NO_HELP
,
1202 cmd_cancelled(NULL
);
1210 if(nextstream
&& sp_dead_stream(nextstream
))
1213 visit_folder(state
, nextfolder
,
1214 state
->context_current
, nextstream
,
1216 /* visit_folder takes care of nextstream */
1224 pine_mail_close(nextstream
);
1228 (mn_get_total(msgmap
) > 0L)
1229 ? IS_NEWS(stream
) ? "more undeleted" : "more new"
1239 /*------- Zoom -----------*/
1242 * Right now the way zoom is implemented is sort of silly.
1243 * There are two per-message flags where just one and a
1244 * global "zoom mode" flag to suppress messages from the index
1247 if(any_messages(msgmap
, NULL
, "to Zoom on")){
1248 if(unzoom_index(state
, stream
, msgmap
)){
1249 dprint((4, "\n\n ---- Exiting ZOOM mode ----\n"));
1250 q_status_message(SM_ORDER
,0,2, _("Index Zoom Mode is now off"));
1252 else if((i
= zoom_index(state
, stream
, msgmap
, MN_SLCT
)) != 0){
1253 if(any_lflagged(msgmap
, MN_HIDE
)){
1254 dprint((4,"\n\n ---- Entering ZOOM mode ----\n"));
1255 q_status_message4(SM_ORDER
, 0, 2,
1256 _("In Zoomed Index of %s%s%s%s. Use \"Z\" to restore regular Index"),
1257 THRD_INDX() ? "" : comatose(i
),
1258 THRD_INDX() ? "" : " ",
1259 THRD_INDX() ? _("threads") : _("message"),
1260 THRD_INDX() ? "" : plural(i
));
1263 q_status_message(SM_ORDER
, 0, 2,
1264 _("All messages selected, so not entering Index Zoom Mode"));
1267 any_messages(NULL
, "selected", "to Zoom on");
1273 /*---------- print message on paper ----------*/
1275 if(any_messages(msgmap
, NULL
, "to print"))
1276 (void) cmd_print(state
, msgmap
, MCMD_NONE
, in_index
);
1281 /*---------- Take Address ----------*/
1283 if(F_ON(F_ENABLE_ROLE_TAKE
, state
) ||
1284 any_messages(msgmap
, NULL
, "to Take address from"))
1285 (void) cmd_take_addr(state
, msgmap
, MCMD_NONE
);
1290 /*---------- Save Message ----------*/
1292 if(any_messages(msgmap
, NULL
, "to Save"))
1293 (void) cmd_save(state
, stream
, msgmap
, MCMD_NONE
, in_index
);
1298 /*---------- Export message ----------*/
1300 if(any_messages(msgmap
, NULL
, "to Export")){
1301 (void) cmd_export(state
, msgmap
, question_line
, MCMD_NONE
);
1302 state
->mangled_footer
= 1;
1308 /*---------- Expunge ----------*/
1310 (void) cmd_expunge(state
, stream
, msgmap
, MCMD_NONE
);
1314 /*------- Unexclude -----------*/
1316 if(!(IS_NEWS(stream
) && stream
->rdonly
)){
1317 q_status_message(SM_ORDER
, 0, 3,
1318 _("Unexclude not available for mail folders"));
1320 else if(any_lflagged(msgmap
, MN_EXLD
)){
1326 * Since excluded means "hidden deleted" and "killed",
1327 * the count should reflect the former.
1329 pgm
= mail_newsearchpgm();
1331 pine_mail_search_full(stream
, NULL
, pgm
, SE_NOPREFETCH
| SE_FREE
);
1332 for(i
= 1L, del_count
= 0L; i
<= stream
->nmsgs
; i
++)
1333 if((mc
= mail_elt(stream
, i
)) && mc
->searched
1334 && get_lflag(stream
, NULL
, i
, MN_EXLD
)
1335 && !(msgno_exceptions(stream
, i
, "0", &exbits
, FALSE
)
1336 && (exbits
& MSG_EX_FILTERED
)))
1340 state
->mangled_footer
= 1; /* MAX_SCREEN_COLS+1 = sizeof(prompt) */
1341 snprintf(prompt
, sizeof(prompt
), "UNexclude %ld message%s in %.*s", del_count
,
1342 plural(del_count
), MAX_SCREEN_COLS
+1-40,
1343 pretty_fn(state
->cur_folder
));
1344 prompt
[sizeof(prompt
)-1] = '\0';
1345 if(F_ON(F_FULL_AUTO_EXPUNGE
, state
)
1346 || (F_ON(F_AUTO_EXPUNGE
, state
)
1347 && (state
->context_current
1348 && (state
->context_current
->use
& CNTXT_INCMNG
))
1349 && context_isambig(state
->cur_folder
))
1350 || want_to(prompt
, 'y', 0, NO_HELP
, WT_NORM
) == 'y'){
1351 long save_cur_rawno
;
1352 int were_viewing_a_thread
;
1354 save_cur_rawno
= mn_m2raw(msgmap
, mn_get_cur(msgmap
));
1355 were_viewing_a_thread
= (THREADING()
1356 && sp_viewing_a_thread(stream
));
1358 if(msgno_include(stream
, msgmap
, MI_NONE
)){
1359 clear_index_cache(stream
, 0);
1361 if(stream
&& stream
->spare
)
1362 erase_threading_info(stream
, msgmap
);
1364 refresh_sort(stream
, msgmap
, SRT_NON
);
1367 if(were_viewing_a_thread
){
1368 if(save_cur_rawno
> 0L)
1369 mn_set_cur(msgmap
, mn_raw2m(msgmap
,save_cur_rawno
));
1371 if(view_thread(state
, stream
, msgmap
, 1) && current_index_state
)
1372 msgmap
->top_after_thrd
= current_index_state
->msg_at_top
;
1375 if(save_cur_rawno
> 0L)
1376 mn_set_cur(msgmap
, mn_raw2m(msgmap
,save_cur_rawno
));
1378 state
->mangled_screen
= 1;
1379 q_status_message2(SM_ORDER
, 0, 4,
1380 "%s message%s UNexcluded",
1381 long2string(del_count
),
1384 if(in_index
!= View
)
1385 adjust_cur_to_visible(stream
, msgmap
);
1388 any_messages(NULL
, NULL
, "UNexcluded");
1391 any_messages(NULL
, "excluded", "to UNexclude");
1394 any_messages(NULL
, "excluded", "to UNexclude");
1399 /*------- Make Selection -----------*/
1401 if(any_messages(msgmap
, NULL
, "to Select")){
1402 if(aggregate_select(state
, msgmap
, question_line
, in_index
) == 0
1403 && (in_index
== MsgIndx
|| in_index
== ThrdIndx
)
1404 && F_ON(F_AUTO_ZOOM
, state
)
1405 && any_lflagged(msgmap
, MN_SLCT
) > 0L
1406 && !any_lflagged(msgmap
, MN_HIDE
))
1407 (void) zoom_index(state
, stream
, msgmap
, MN_SLCT
);
1413 /*------- Toggle Current Message Selection State -----------*/
1415 if(any_messages(msgmap
, NULL
, NULL
)){
1416 if((select_by_current(state
, msgmap
, in_index
)
1417 || (F_OFF(F_UNSELECT_WONT_ADVANCE
, state
)
1418 && !any_lflagged(msgmap
, MN_HIDE
)))
1419 && (i
= mn_get_cur(msgmap
)) < mn_get_total(msgmap
)){
1420 /* advance current */
1421 mn_inc_cur(stream
, msgmap
,
1422 (in_index
== View
&& THREADING()
1423 && sp_viewing_a_thread(stream
))
1425 : (in_index
== View
)
1426 ? MH_ANYTHD
: MH_NONE
);
1433 /*------- Apply command -----------*/
1435 if(any_messages(msgmap
, NULL
, NULL
)){
1436 if(any_lflagged(msgmap
, MN_SLCT
) > 0L){
1437 if(apply_command(state
, stream
, msgmap
, 0,
1438 AC_NONE
, question_line
)){
1439 if(F_ON(F_AUTO_UNSELECT
, state
)){
1440 agg_select_all(stream
, msgmap
, NULL
, 0);
1441 unzoom_index(state
, stream
, msgmap
);
1443 else if(F_ON(F_AUTO_UNZOOM
, state
))
1444 unzoom_index(state
, stream
, msgmap
);
1448 any_messages(NULL
, NULL
, "to Apply command to. Try \"Select\"");
1454 /*-------- Sort command -------*/
1457 int were_threading
= THREADING();
1458 SortOrder sort
= mn_get_sort(msgmap
);
1459 int rev
= mn_get_revsort(msgmap
);
1461 dprint((1,"MAIL_CMD: sort\n"));
1462 if(select_sort(state
, question_line
, &sort
, &rev
)){
1463 /* $ command reinitializes threading collapsed/expanded info */
1464 if(SORT_IS_THREADED(msgmap
) && !SEP_THRDINDX())
1465 erase_threading_info(stream
, msgmap
);
1467 if(ps_global
&& ps_global
->ttyo
){
1468 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
1469 ps_global
->mangled_footer
= 1;
1472 sort_folder(stream
, msgmap
, sort
, rev
, SRT_VRB
|SRT_MAN
);
1475 state
->mangled_footer
= 1;
1478 * We've changed whether we are threading or not so we need to
1479 * exit the index and come back in so that we switch between the
1480 * thread index and the regular index. Sort_folder will have
1481 * reset viewing_a_thread if necessary.
1484 && ((!were_threading
&& THREADING())
1485 || (were_threading
&& !THREADING()))){
1486 state
->next_screen
= mail_index_screen
;
1487 state
->mangled_screen
= 1;
1494 /*------- Toggle Full Headers -----------*/
1496 state
->full_header
++;
1497 if(state
->full_header
== 1){
1498 if(!(state
->quote_suppression_threshold
1499 && (state
->some_quoting_was_suppressed
|| in_index
!= View
)))
1500 state
->full_header
++;
1502 else if(state
->full_header
> 2)
1503 state
->full_header
= 0;
1505 switch(state
->full_header
){
1507 q_status_message(SM_ORDER
, 0, 3,
1508 _("Display of full headers is now off."));
1512 q_status_message1(SM_ORDER
, 0, 3,
1513 _("Quotes displayed, use %s to see full headers"),
1514 F_ON(F_USE_FK
, state
) ? "F9" : "H");
1518 q_status_message(SM_ORDER
, 0, 3,
1519 _("Display of full headers is now on."));
1534 /*------- Try to decrypt message -----------*/
1536 if(state
->smime
&& state
->smime
->need_passphrase
)
1537 smime_get_passphrase();
1543 smime_info_screen(state
);
1548 /*------- Bounce -----------*/
1550 (void) cmd_bounce(state
, msgmap
, MCMD_NONE
, NULL
);
1554 /*------- Flag -----------*/
1556 dprint((4, "\n - flag message -\n"));
1557 (void) cmd_flag(state
, msgmap
, MCMD_NONE
);
1561 /*------- Pipe message -----------*/
1563 (void) cmd_pipe(state
, msgmap
, MCMD_NONE
);
1567 /*--------- Default, unknown command ----------*/
1569 alpine_panic("Unexpected command case");
1573 return((a_changed
|| mn_get_cur(msgmap
) != old_msgno
) ? 1 : 0);
1578 /*----------------------------------------------------------------------
1579 Map some of the special characters into sensible strings for human
1581 c is a UCS-4 character!
1584 pretty_command(UCS c
)
1586 static char buf
[10];
1593 case ' ' : s
= "SPACE"; break;
1594 case '\033' : s
= "ESC"; break;
1595 case '\177' : s
= "DEL"; break;
1596 case ctrl('I') : s
= "TAB"; break;
1597 case ctrl('J') : s
= "LINEFEED"; break;
1598 case ctrl('M') : s
= "RETURN"; break;
1599 case ctrl('Q') : s
= "XON"; break;
1600 case ctrl('S') : s
= "XOFF"; break;
1601 case KEY_UP
: s
= "Up Arrow"; break;
1602 case KEY_DOWN
: s
= "Down Arrow"; break;
1603 case KEY_RIGHT
: s
= "Right Arrow"; break;
1604 case KEY_LEFT
: s
= "Left Arrow"; break;
1605 case KEY_PGUP
: s
= "Prev Page"; break;
1606 case KEY_PGDN
: s
= "Next Page"; break;
1607 case KEY_HOME
: s
= "Home"; break;
1608 case KEY_END
: s
= "End"; break;
1609 case KEY_DEL
: s
= "Delete"; break; /* Not necessary DEL! */
1610 case KEY_JUNK
: s
= "Junk!"; break;
1611 case BADESC
: s
= "Bad Esc"; break;
1612 case NO_OP_IDLE
: s
= "NO_OP_IDLE"; break;
1613 case NO_OP_COMMAND
: s
= "NO_OP_COMMAND"; break;
1614 case KEY_RESIZE
: s
= "KEY_RESIZE"; break;
1615 case KEY_UTF8
: s
= "KEY_UTF8"; break;
1616 case KEY_MOUSE
: s
= "KEY_MOUSE"; break;
1617 case KEY_SCRLUPL
: s
= "KEY_SCRLUPL"; break;
1618 case KEY_SCRLDNL
: s
= "KEY_SCRLDNL"; break;
1619 case KEY_SCRLTO
: s
= "KEY_SCRLTO"; break;
1620 case KEY_XTERM_MOUSE
: s
= "KEY_XTERM_MOUSE"; break;
1621 case KEY_DOUBLE_ESC
: s
= "KEY_DOUBLE_ESC"; break;
1622 case CTRL_KEY_UP
: s
= "Ctrl Up Arrow"; break;
1623 case CTRL_KEY_DOWN
: s
= "Ctrl Down Arrow"; break;
1624 case CTRL_KEY_RIGHT
: s
= "Ctrl Right Arrow"; break;
1625 case CTRL_KEY_LEFT
: s
= "Ctrl Left Arrow"; break;
1638 snprintf(s
= buf
, sizeof(buf
), "F%ld", (long) (c
- PF1
+ 1));
1642 if(c
< ' ' || (c
>= 0x80 && c
< 0xA0)){
1647 d
= (c
& 0x1f) + 'A' - 1;
1648 snprintf(s
= buf
, sizeof(buf
), "%c%c", c1
? '~' : '^', d
);
1651 memset(buf
, 0, sizeof(buf
));
1652 utf8_put((unsigned char *) buf
, (unsigned long) c
);
1662 /*----------------------------------------------------------------------
1663 Complain about bogus input
1665 Args: ch -- input command to complain about
1666 help -- string indicating where to get help
1670 bogus_command(UCS cmd
, char *help
)
1672 if(cmd
== ctrl('Q') || cmd
== ctrl('S'))
1673 q_status_message1(SM_ASYNC
, 0, 2,
1674 "%s char received. Set \"preserve-start-stop\" feature in Setup/Config.",
1675 pretty_command(cmd
));
1676 else if(cmd
== KEY_JUNK
)
1677 q_status_message3(SM_ORDER
, 0, 2,
1678 "Invalid key pressed.%s%s%s",
1679 (help
) ? " Use " : "",
1681 (help
) ? " for help" : "");
1683 q_status_message4(SM_ORDER
, 0, 2,
1684 "Command \"%s\" not defined for this screen.%s%s%s",
1685 pretty_command(cmd
),
1686 (help
) ? " Use " : "",
1688 (help
) ? " for help" : "");
1693 bogus_utf8_command(char *cmd
, char *help
)
1695 q_status_message4(SM_ORDER
, 0, 2,
1696 "Command \"%s\" not defined for this screen.%s%s%s",
1698 (help
) ? " Use " : "",
1700 (help
) ? " for help" : "");
1704 /*----------------------------------------------------------------------
1705 Execute FLAG message command
1707 Args: state -- Various satate info
1708 msgmap -- map of c-client to local message numbers
1710 Result: with side effect of "current" message FLAG flag set or UNset
1714 cmd_flag(struct pine
*state
, MSGNO_S
*msgmap
, int aopt
)
1716 char *flagit
, *seq
, *screen_text
[20], **exp
, **p
, *answer
= NULL
;
1717 char *keyword_array
[2];
1718 int user_defined_flags
= 0, mailbox_flags
= 0;
1719 int directly_to_maint_screen
= 0;
1720 long unflagged
, flagged
, flags
, rawno
;
1721 MESSAGECACHE
*mc
= NULL
;
1723 int i
, cnt
, is_set
, trouble
= 0, rv
= 0;
1725 struct flag_table
*fp
, *ftbl
= NULL
;
1726 struct flag_screen flag_screen
;
1727 static char *flag_screen_text1
[] = {
1728 N_(" Set desired flags for current message below. An 'X' means set"),
1729 N_(" it, and a ' ' means to unset it. Choose \"Exit\" when finished."),
1733 static char *flag_screen_text2
[] = {
1734 N_(" Set desired flags below for selected messages. A '?' means to"),
1735 N_(" leave the flag unchanged, 'X' means to set it, and a ' ' means"),
1736 N_(" to unset it. Use the \"Return\" key to toggle, and choose"),
1737 N_(" \"Exit\" when finished."),
1741 static struct flag_table default_ftbl
[] = {
1742 {N_("Important"), h_flag_important
, F_FLAG
, 0, 0, NULL
, NULL
},
1743 {N_("New"), h_flag_new
, F_SEEN
, 0, 0, NULL
, NULL
},
1744 {N_("Answered"), h_flag_answered
, F_ANS
, 0, 0, NULL
, NULL
},
1745 {N_("Forwarded"), h_flag_forwarded
, F_FWD
, 0, 0, NULL
, NULL
},
1746 {N_("Deleted"), h_flag_deleted
, F_DEL
, 0, 0, NULL
, NULL
},
1747 {NULL
, NO_HELP
, 0, 0, 0, NULL
, NULL
}
1750 /* Only check for dead stream for now. Should check permanent flags
1753 if(!(any_messages(msgmap
, NULL
, "to Flag") && can_set_flag(state
, "flag", 1)))
1756 if(sp_io_error_on_stream(state
->mail_stream
)){
1757 sp_set_io_error_on_stream(state
->mail_stream
, 0);
1758 pine_mail_check(state
->mail_stream
); /* forces write */
1764 user_defined_flags
= 0;
1770 /* count how large ftbl will be */
1771 for(cnt
= 0; default_ftbl
[cnt
].name
; cnt
++)
1774 /* add user flags */
1775 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
){
1776 if(!((kw
->nick
&& !strucmp(FORWARDED_FLAG
, kw
->nick
)) || (kw
->kw
&& !strucmp(FORWARDED_FLAG
, kw
->kw
)))){
1777 user_defined_flags
++;
1783 * Add mailbox flags that aren't user-defined flags.
1784 * Don't consider it if it matches either one of our defined
1785 * keywords or one of our defined nicknames for a keyword.
1787 for(i
= 0; stream_to_user_flag_name(state
->mail_stream
, i
); i
++){
1790 q
= stream_to_user_flag_name(state
->mail_stream
, i
);
1792 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
){
1793 if((kw
->nick
&& !strucmp(kw
->nick
, q
)) || (kw
->kw
&& !strucmp(kw
->kw
, q
)))
1798 if(!kw
&& !(q
&& !strucmp(FORWARDED_FLAG
, q
))){
1804 cnt
+= (user_defined_flags
? 2 : 0) + (mailbox_flags
? 2 : 0);
1806 /* set up ftbl, first the system flags */
1807 ftbl
= (struct flag_table
*) fs_get((cnt
+1) * sizeof(*ftbl
));
1808 memset(ftbl
, 0, (cnt
+1) * sizeof(*ftbl
));
1809 for(i
= 0, fp
= ftbl
; default_ftbl
[i
].name
; i
++, fp
++){
1810 fp
->name
= cpystr(default_ftbl
[i
].name
);
1811 fp
->help
= default_ftbl
[i
].help
;
1812 fp
->flag
= default_ftbl
[i
].flag
;
1813 fp
->set
= default_ftbl
[i
].set
;
1814 fp
->ukn
= default_ftbl
[i
].ukn
;
1817 if(user_defined_flags
){
1818 fp
->flag
= F_COMMENT
;
1819 fp
->name
= cpystr("");
1821 fp
->flag
= F_COMMENT
;
1822 len
= strlen(_("User-defined Keywords from Setup/Config"));
1823 fp
->name
= (char *) fs_get((len
+6+6+1) * sizeof(char));
1824 snprintf(fp
->name
, len
+6+6+1, "----- %s -----", _("User-defined Keywords from Setup/Config"));
1828 /* then the user-defined keywords */
1829 if(user_defined_flags
)
1830 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
){
1831 if(!((kw
->nick
&& !strucmp(FORWARDED_FLAG
, kw
->nick
))
1832 || (kw
->kw
&& !strucmp(FORWARDED_FLAG
, kw
->kw
)))){
1833 fp
->name
= cpystr(kw
->nick
? kw
->nick
: kw
->kw
? kw
->kw
: "");
1834 fp
->keyword
= cpystr(kw
->kw
? kw
->kw
: "");
1835 if(kw
->nick
&& kw
->kw
){
1838 l
= strlen(kw
->kw
)+2;
1839 fp
->comment
= (char *) fs_get((l
+1) * sizeof(char));
1840 snprintf(fp
->comment
, l
+1, "(%.*s)", (int) strlen(kw
->kw
), kw
->kw
);
1841 fp
->comment
[l
] = '\0';
1844 fp
->help
= h_flag_user_flag
;
1845 fp
->flag
= F_KEYWORD
;
1853 fp
->flag
= F_COMMENT
;
1854 fp
->name
= cpystr("");
1856 fp
->flag
= F_COMMENT
;
1857 len
= strlen(_("Other keywords in the mailbox that are not user-defined"));
1858 fp
->name
= (char *) fs_get((len
+6+6+1) * sizeof(char));
1859 snprintf(fp
->name
, len
+6+6+1, "----- %s -----", _("Other keywords in the mailbox that are not user-defined"));
1863 /* then the extra mailbox-defined keywords */
1865 for(i
= 0; stream_to_user_flag_name(state
->mail_stream
, i
); i
++){
1868 q
= stream_to_user_flag_name(state
->mail_stream
, i
);
1870 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
){
1871 if((kw
->nick
&& !strucmp(kw
->nick
, q
)) || (kw
->kw
&& !strucmp(kw
->kw
, q
)))
1876 if(!kw
&& !(q
&& !strucmp(FORWARDED_FLAG
, q
))){
1877 fp
->name
= cpystr(q
);
1878 fp
->keyword
= cpystr(q
);
1879 fp
->help
= h_flag_user_flag
;
1880 fp
->flag
= F_KEYWORD
;
1887 flag_screen
.flag_table
= &ftbl
;
1888 flag_screen
.explanation
= screen_text
;
1890 if(MCMD_ISAGG(aopt
)){
1891 if(!pseudo_selected(ps_global
->mail_stream
, msgmap
)){
1892 free_flag_table(&ftbl
);
1896 exp
= flag_screen_text2
;
1897 for(fp
= ftbl
; fp
->name
; fp
++){
1898 fp
->set
= CMD_FLAG_UNKN
; /* set to unknown */
1902 else if(state
->mail_stream
1903 && (rawno
= mn_m2raw(msgmap
, mn_get_cur(msgmap
))) > 0L
1904 && rawno
<= state
->mail_stream
->nmsgs
1905 && (mc
= mail_elt(state
->mail_stream
, rawno
))){
1906 exp
= flag_screen_text1
;
1907 for(fp
= &ftbl
[0]; fp
->name
; fp
++){
1909 if(fp
->flag
== F_KEYWORD
){
1910 /* see if this keyword is defined for this message */
1911 fp
->set
= CMD_FLAG_CLEAR
;
1912 if(user_flag_is_set(state
->mail_stream
,
1913 rawno
, fp
->keyword
))
1914 fp
->set
= CMD_FLAG_SET
;
1916 else if(fp
->flag
== F_FWD
){
1917 /* see if forwarded keyword is defined for this message */
1918 fp
->set
= CMD_FLAG_CLEAR
;
1919 if(user_flag_is_set(state
->mail_stream
,
1920 rawno
, FORWARDED_FLAG
))
1921 fp
->set
= CMD_FLAG_SET
;
1923 else if(fp
->flag
!= F_COMMENT
)
1924 fp
->set
= ((fp
->flag
== F_SEEN
&& !mc
->seen
)
1925 || (fp
->flag
== F_DEL
&& mc
->deleted
)
1926 || (fp
->flag
== F_FLAG
&& mc
->flagged
)
1927 || (fp
->flag
== F_ANS
&& mc
->answered
))
1928 ? CMD_FLAG_SET
: CMD_FLAG_CLEAR
;
1932 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
1933 _("Error accessing message data"));
1934 free_flag_table(&ftbl
);
1938 if(directly_to_maint_screen
)
1939 goto the_maint_screen
;
1942 if (mswin_usedialog ()) {
1943 if (!os_flagmsgdialog (&ftbl
[0])){
1944 free_flag_table(&ftbl
);
1951 int use_maint_screen
;
1952 int keyword_shortcut
= 0;
1954 use_maint_screen
= F_ON(F_FLAG_SCREEN_DFLT
, ps_global
);
1956 if(!use_maint_screen
){
1958 * We're going to call cmd_flag_prompt(). We need
1959 * to decide whether or not to offer the keyword setting
1960 * shortcut. We'll offer it if the user has the feature
1961 * enabled AND there are some possible keywords that could
1964 if(F_ON(F_FLAG_SCREEN_KW_SHORTCUT
, ps_global
)){
1965 for(fp
= &ftbl
[0]; fp
->name
&& !keyword_shortcut
; fp
++){
1966 if(fp
->flag
== F_KEYWORD
){
1970 first_char
= (fp
->name
&& fp
->name
[0])
1972 if(isascii(first_char
) && isupper(first_char
))
1973 first_char
= tolower((unsigned char) first_char
);
1975 for(tp
=flag_text_opt
; tp
->ch
!= -1; tp
++)
1976 if(tp
->ch
== first_char
)
1985 use_maint_screen
= !cmd_flag_prompt(state
, &flag_screen
,
1990 if(use_maint_screen
){
1991 for(p
= &screen_text
[0]; *exp
; p
++, exp
++)
1996 directly_to_maint_screen
= flag_maintenance_screen(state
, &flag_screen
);
2000 /* reacquire the elt pointer */
2001 mc
= (state
->mail_stream
2002 && (rawno
= mn_m2raw(msgmap
, mn_get_cur(msgmap
))) > 0L
2003 && rawno
<= state
->mail_stream
->nmsgs
)
2004 ? mail_elt(state
->mail_stream
, rawno
) : NULL
;
2006 for(fp
= ftbl
; mc
&& fp
->name
; fp
++){
2010 if((!MCMD_ISAGG(aopt
) && fp
->set
!= !mc
->seen
)
2011 || (MCMD_ISAGG(aopt
) && fp
->set
!= CMD_FLAG_UNKN
)){
2019 unflagged
= F_UNSEEN
;
2026 if((!MCMD_ISAGG(aopt
) && fp
->set
!= mc
->answered
)
2027 || (MCMD_ISAGG(aopt
) && fp
->set
!= CMD_FLAG_UNKN
)){
2028 flagit
= "\\ANSWERED";
2031 unflagged
= F_UNANS
;
2042 if((!MCMD_ISAGG(aopt
) && fp
->set
!= mc
->deleted
)
2043 || (MCMD_ISAGG(aopt
) && fp
->set
!= CMD_FLAG_UNKN
)){
2044 flagit
= "\\DELETED";
2047 unflagged
= F_UNDEL
;
2058 if((!MCMD_ISAGG(aopt
) && fp
->set
!= mc
->flagged
)
2059 || (MCMD_ISAGG(aopt
) && fp
->set
!= CMD_FLAG_UNKN
)){
2060 flagit
= "\\FLAGGED";
2063 unflagged
= F_UNFLAG
;
2074 if(!MCMD_ISAGG(aopt
)){
2075 /* see if forwarded is defined for this message */
2076 is_set
= CMD_FLAG_CLEAR
;
2077 if(user_flag_is_set(state
->mail_stream
,
2078 mn_m2raw(msgmap
, mn_get_cur(msgmap
)),
2080 is_set
= CMD_FLAG_SET
;
2083 if((!MCMD_ISAGG(aopt
) && fp
->set
!= is_set
)
2084 || (MCMD_ISAGG(aopt
) && fp
->set
!= CMD_FLAG_UNKN
)){
2085 flagit
= FORWARDED_FLAG
;
2088 unflagged
= F_UNFWD
;
2099 if(!MCMD_ISAGG(aopt
)){
2100 /* see if this keyword is defined for this message */
2101 is_set
= CMD_FLAG_CLEAR
;
2102 if(user_flag_is_set(state
->mail_stream
,
2103 mn_m2raw(msgmap
, mn_get_cur(msgmap
)),
2105 is_set
= CMD_FLAG_SET
;
2108 if((!MCMD_ISAGG(aopt
) && fp
->set
!= is_set
)
2109 || (MCMD_ISAGG(aopt
) && fp
->set
!= CMD_FLAG_UNKN
)){
2110 flagit
= fp
->keyword
;
2111 keyword_array
[0] = fp
->keyword
;
2112 keyword_array
[1] = NULL
;
2115 unflagged
= F_UNKEYWORD
;
2119 unflagged
= F_KEYWORD
;
2131 && (seq
= currentf_sequence(state
->mail_stream
, msgmap
,
2132 unflagged
, &flagged
, unflagged
& F_DEL
,
2133 (fp
->flag
== F_KEYWORD
2134 && unflagged
== F_KEYWORD
)
2135 ? keyword_array
: NULL
,
2136 (fp
->flag
== F_KEYWORD
2137 && unflagged
== F_UNKEYWORD
)
2138 ? keyword_array
: NULL
))){
2140 * For user keywords, we may have to create the flag in
2141 * the folder if it doesn't already exist and we are setting
2142 * it (as opposed to clearing it). Mail_flag will
2143 * do that for us, but it's failure isn't very friendly
2144 * error-wise. So we try to make it a little smoother.
2146 if(!(fp
->flag
== F_KEYWORD
|| fp
->flag
== F_FWD
) || !fp
->set
2147 || ((i
=user_flag_index(state
->mail_stream
, flagit
)) >= 0
2149 mail_flag(state
->mail_stream
, seq
, flagit
, flags
);
2151 /* trouble, see if we can add the user flag */
2152 if(state
->mail_stream
->kwd_create
)
2153 mail_flag(state
->mail_stream
, seq
, flagit
, flags
);
2157 if(some_user_flags_defined(state
->mail_stream
))
2158 q_status_message(SM_ORDER
, 3, 4,
2159 _("No more keywords allowed in this folder!"));
2160 else if(fp
->flag
== F_FWD
)
2161 q_status_message(SM_ORDER
, 3, 4,
2162 _("Cannot add keywords for this folder so cannot set Forwarded flag"));
2164 q_status_message(SM_ORDER
, 3, 4,
2165 _("Cannot add keywords for this folder"));
2169 fs_give((void **) &seq
);
2170 if(flagged
&& !trouble
){
2171 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%slagged%s%s%s%s%s message%s%s \"%s\"",
2172 (fp
->set
) ? "F" : "Unf",
2173 MCMD_ISAGG(aopt
) ? " " : "",
2174 MCMD_ISAGG(aopt
) ? long2string(flagged
) : "",
2175 (MCMD_ISAGG(aopt
) && flagged
!= mn_total_cur(msgmap
))
2177 (MCMD_ISAGG(aopt
) && flagged
!= mn_total_cur(msgmap
))
2178 ? comatose(mn_total_cur(msgmap
)) : "",
2179 (MCMD_ISAGG(aopt
) && flagged
!= mn_total_cur(msgmap
))
2181 MCMD_ISAGG(aopt
) ? plural(flagged
) : " ",
2182 MCMD_ISAGG(aopt
) ? "" : long2string(mn_get_cur(msgmap
)),
2184 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
2185 q_status_message(SM_ORDER
, 0, 2, answer
= tmp_20k_buf
);
2191 free_flag_table(&ftbl
);
2193 if(directly_to_maint_screen
)
2196 if(MCMD_ISAGG(aopt
))
2197 restore_selected(msgmap
);
2200 q_status_message(SM_ORDER
, 0, 2, _("No flags changed."));
2206 /*----------------------------------------------------------------------
2207 Offer concise status line flag prompt
2209 Args: state -- Various satate info
2210 flags -- flags to offer setting
2212 Result: TRUE if flag to set specified in flags struct or FALSE otw
2216 cmd_flag_prompt(struct pine
*state
, struct flag_screen
*flags
, int allow_keyword_shortcuts
)
2218 int r
, setflag
= 1, first_char
;
2219 struct flag_table
*fp
;
2221 char *ftext
, *ftext_not
;
2222 static char *flag_text
=
2223 N_("Flag New, Deleted, Answered, Forwarded or Important ? ");
2224 static char *flag_text_ak
=
2225 N_("Flag New, Deleted, Answered, Forwarded, Important or Keyword initial ? ");
2226 static char *flag_text_not
=
2227 N_("Flag !New, !Deleted, !Answered, !Forwarded, or !Important ? ");
2228 static char *flag_text_ak_not
=
2229 N_("Flag !New, !Deleted, !Answered, !Forwarded, !Important or !Keyword initial ? ");
2231 if(allow_keyword_shortcuts
){
2233 ESCKEY_S
*dp
, *sp
, *tp
;
2235 for(sp
=flag_text_opt
; sp
->ch
!= -1; sp
++)
2238 for(fp
=(flags
->flag_table
? *flags
->flag_table
: NULL
); fp
->name
; fp
++)
2239 if(fp
->flag
== F_KEYWORD
)
2242 /* set up an ESCKEY_S list which includes invisible keys for keywords */
2243 ek
= (ESCKEY_S
*) fs_get((cnt
+ 1) * sizeof(*ek
));
2244 memset(ek
, 0, (cnt
+1) * sizeof(*ek
));
2245 for(dp
=ek
, sp
=flag_text_opt
; sp
->ch
!= -1; sp
++, dp
++)
2248 for(fp
=(flags
->flag_table
? *flags
->flag_table
: NULL
); fp
->name
; fp
++){
2249 if(fp
->flag
== F_KEYWORD
){
2250 first_char
= (fp
->name
&& fp
->name
[0]) ? fp
->name
[0] : -2;
2251 if(isascii(first_char
) && isupper(first_char
))
2252 first_char
= tolower((unsigned char) first_char
);
2255 * Check to see if an earlier keyword in the list, or one of
2256 * the builtin system letters already uses this character.
2257 * If so, the first one wins.
2259 for(tp
=ek
; tp
->ch
!= 0; tp
++)
2260 if(tp
->ch
== first_char
)
2264 continue; /* skip it, already used that char */
2266 dp
->ch
= first_char
;
2267 dp
->rval
= first_char
;
2275 ftext
= _(flag_text_ak
);
2276 ftext_not
= _(flag_text_ak_not
);
2280 ftext
= _(flag_text
);
2281 ftext_not
= _(flag_text_not
);
2285 r
= radio_buttons(setflag
? ftext
: ftext_not
,
2286 -FOOTER_ROWS(state
), ek
, '*', SEQ_EXCEPTION
-1,
2287 NO_HELP
, RB_NORM
| RB_SEQ_SENSITIVE
);
2289 * It is SEQ_EXCEPTION-1 just so that it is some value that isn't
2290 * being used otherwise. The keywords use up all the possible
2291 * letters, so a negative number is good, but it has to be different
2292 * from other negative return values.
2294 if(r
== SEQ_EXCEPTION
-1) /* ol'cancelrooney */
2296 else if(r
== 10) /* return and goto flag screen */
2298 else if(r
== '!') /* flip intention */
2304 for(fp
= (flags
->flag_table
? *flags
->flag_table
: NULL
); fp
->name
; fp
++){
2305 if(r
== 'n' || r
== '*' || r
== 'd' || r
== 'a' || r
== 'f'){
2306 if((r
== 'n' && fp
->flag
== F_SEEN
)
2307 || (r
== '*' && fp
->flag
== F_FLAG
)
2308 || (r
== 'd' && fp
->flag
== F_DEL
)
2309 || (r
== 'f' && fp
->flag
== F_FWD
)
2310 || (r
== 'a' && fp
->flag
== F_ANS
)){
2311 fp
->set
= setflag
? CMD_FLAG_SET
: CMD_FLAG_CLEAR
;
2315 else if(allow_keyword_shortcuts
&& fp
->flag
== F_KEYWORD
){
2316 first_char
= (fp
->name
&& fp
->name
[0]) ? fp
->name
[0] : -2;
2317 if(isascii(first_char
) && isupper(first_char
))
2318 first_char
= tolower((unsigned char) first_char
);
2320 if(r
== first_char
){
2321 fp
->set
= setflag
? CMD_FLAG_SET
: CMD_FLAG_CLEAR
;
2327 if(ek
!= flag_text_opt
)
2328 fs_give((void **) &ek
);
2335 * (*ft) is an array of flag_table entries.
2338 free_flag_table(struct flag_table
**ft
)
2340 struct flag_table
*fp
;
2343 for(fp
= (*ft
); fp
->name
; fp
++){
2345 fs_give((void **) &fp
->name
);
2348 fs_give((void **) &fp
->keyword
);
2351 fs_give((void **) &fp
->comment
);
2354 fs_give((void **) ft
);
2359 /*----------------------------------------------------------------------
2360 Execute REPLY message command
2362 Args: state -- Various satate info
2363 msgmap -- map of c-client to local message numbers
2365 Result: reply sent or not
2369 cmd_reply(struct pine
*state
, MSGNO_S
*msgmap
, int aopt
, ACTION_S
*role
)
2373 if(any_messages(msgmap
, NULL
, "to Reply to")){
2374 if(MCMD_ISAGG(aopt
) && !pseudo_selected(state
->mail_stream
, msgmap
))
2377 rv
= reply(state
, role
);
2379 if(MCMD_ISAGG(aopt
))
2380 restore_selected(msgmap
);
2382 state
->mangled_screen
= 1;
2389 /*----------------------------------------------------------------------
2390 Execute FORWARD message command
2392 Args: state -- Various satate info
2393 msgmap -- map of c-client to local message numbers
2395 Result: selected message[s] forwarded or not
2399 cmd_forward(struct pine
*state
, MSGNO_S
*msgmap
, int aopt
, ACTION_S
*role
)
2403 if(any_messages(msgmap
, NULL
, "to Forward")){
2404 if(MCMD_ISAGG(aopt
) && !pseudo_selected(state
->mail_stream
, msgmap
))
2407 rv
= forward(state
, role
);
2409 if(MCMD_ISAGG(aopt
))
2410 restore_selected(msgmap
);
2412 state
->mangled_screen
= 1;
2419 /*----------------------------------------------------------------------
2420 Execute BOUNCE message command
2422 Args: state -- Various satate info
2423 msgmap -- map of c-client to local message numbers
2424 aopt -- aggregate options
2426 Result: selected message[s] bounced or not
2430 cmd_bounce(struct pine
*state
, MSGNO_S
*msgmap
, int aopt
, ACTION_S
*role
)
2434 if(any_messages(msgmap
, NULL
, "to Bounce")){
2436 if(MCMD_ISAGG(aopt
)){
2437 if(!pseudo_selected(state
->mail_stream
, msgmap
))
2440 else if((i
= any_lflagged(msgmap
, MN_SLCT
)) > 0
2441 && get_lflag(state
->mail_stream
, msgmap
,
2442 mn_m2raw(msgmap
, mn_get_cur(msgmap
)), MN_SLCT
) == 0)
2443 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
2444 _("WARNING: non-selected message is being bounced!"));
2446 && get_lflag(state
->mail_stream
, msgmap
,
2447 mn_m2raw(msgmap
, mn_get_cur(msgmap
)), MN_SLCT
))
2448 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
2449 _("WARNING: not bouncing all selected messages!"));
2451 rv
= bounce(state
, role
);
2453 if(MCMD_ISAGG(aopt
))
2454 restore_selected(msgmap
);
2456 state
->mangled_footer
= 1;
2463 /*----------------------------------------------------------------------
2464 Execute save message command: prompt for folder and call function to save
2466 Args: screen_line -- Line on the screen to prompt on
2467 message -- The MESSAGECACHE entry of message to save
2469 Result: The folder lister can be called to make selection; mangled screen set
2471 This does the prompting for the folder name to save to, possibly calling
2472 up the folder display for selection of folder by user.
2475 cmd_save(struct pine
*state
, MAILSTREAM
*stream
, MSGNO_S
*msgmap
, int aopt
, CmdWhere in_index
)
2477 char newfolder
[MAILTMPLEN
], nmsgs
[32], *nick
;
2478 int we_cancel
= 0, rv
= 0, save_flags
;
2480 CONTEXT_S
*cntxt
= NULL
;
2482 SaveDel del
= DontAsk
;
2483 SavePreserveOrder pre
= DontAskPreserve
;
2485 dprint((4, "\n - saving message -\n"));
2487 if(MCMD_ISAGG(aopt
) && !pseudo_selected(stream
, msgmap
))
2490 state
->ugly_consider_advancing_bit
= 0;
2491 if(F_OFF(F_SAVE_PARTIAL_WO_CONFIRM
, state
)
2492 && msgno_any_deletedparts(stream
, msgmap
)
2493 && want_to(_("Saved copy will NOT include entire message! Continue"),
2494 'y', 'n', NO_HELP
, WT_FLUSH_IN
| WT_SEQ_SENSITIVE
) != 'y'){
2495 restore_selected(msgmap
);
2496 cmd_cancelled("Save message");
2500 raw
= mn_m2raw(msgmap
, mn_get_cur(msgmap
));
2502 if(mn_total_cur(msgmap
) <= 1L){
2503 snprintf(nmsgs
, sizeof(nmsgs
), "Msg #%ld ", mn_get_cur(msgmap
));
2504 nmsgs
[sizeof(nmsgs
)-1] = '\0';
2505 e
= pine_mail_fetchstructure(stream
, raw
, NULL
);
2507 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
2508 _("Can't save message. Error accessing folder"));
2509 restore_selected(msgmap
);
2514 snprintf(nmsgs
, sizeof(nmsgs
), "%s msgs ", comatose(mn_total_cur(msgmap
)));
2515 nmsgs
[sizeof(nmsgs
)-1] = '\0';
2517 /* e is just used to get a default save folder from the first msg */
2518 e
= pine_mail_fetchstructure(stream
,
2519 mn_m2raw(msgmap
, mn_first_cur(msgmap
)),
2523 del
= (!READONLY_FOLDER(stream
) && F_OFF(F_SAVE_WONT_DELETE
, ps_global
))
2525 if(mn_total_cur(msgmap
) > 1L)
2526 pre
= F_OFF(F_AGG_SEQ_COPY
, ps_global
) ? Preserve
: NoPreserve
;
2528 pre
= DontAskPreserve
;
2530 if(save_prompt(state
, &cntxt
, newfolder
, sizeof(newfolder
), nmsgs
, e
,
2531 raw
, NULL
, &del
, &pre
)){
2533 if(ps_global
&& ps_global
->ttyo
){
2534 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
2535 ps_global
->mangled_footer
= 1;
2538 save_flags
= SV_FIX_DELS
;
2539 if(pre
== RetPreserve
)
2540 save_flags
|= SV_PRESERVE
;
2542 save_flags
|= SV_DELETE
;
2543 if(ps_global
->context_list
== cntxt
&& !strucmp(newfolder
, ps_global
->inbox_name
))
2544 save_flags
|= SV_INBOXWOCNTXT
;
2546 we_cancel
= busy_cue(_("Saving"), NULL
, 1);
2547 i
= save(state
, stream
, cntxt
, newfolder
, msgmap
, save_flags
);
2551 if(i
== mn_total_cur(msgmap
)){
2553 if(mn_total_cur(msgmap
) <= 1L){
2554 int need
, avail
= ps_global
->ttyo
->screen_cols
- 2;
2555 int lennick
, lenfldr
;
2558 && ps_global
->context_list
->next
2559 && context_isambig(newfolder
)){
2560 lennick
= MIN(strlen(cntxt
->nickname
), 500);
2561 lenfldr
= MIN(strlen(newfolder
), 500);
2562 need
= 27 + strlen(long2string(mn_get_cur(msgmap
))) +
2566 need
-= MIN(lennick
-10, need
-avail
);
2567 lennick
-= MIN(lennick
-10, need
-avail
);
2570 if(need
> avail
&& lenfldr
> 10)
2571 lenfldr
-= MIN(lenfldr
-10, need
-avail
);
2574 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,
2575 "Message %s copied to \"%s\" in <%s>",
2576 long2string(mn_get_cur(msgmap
)),
2577 short_str(newfolder
, (char *)(tmp_20k_buf
+1000), 1000,
2579 short_str(cntxt
->nickname
,
2580 (char *)(tmp_20k_buf
+2000), 1000,
2582 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
2584 else if((nick
=folder_is_target_of_nick(newfolder
, cntxt
)) != NULL
){
2585 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,
2586 "Message %s copied to \"%s\"",
2587 long2string(mn_get_cur(msgmap
)),
2589 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
2592 char *f
= " folder";
2594 lenfldr
= MIN(strlen(newfolder
), 500);
2595 need
= 28 + strlen(long2string(mn_get_cur(msgmap
))) +
2600 if(need
> avail
&& lenfldr
> 10)
2601 lenfldr
-= MIN(lenfldr
-10, need
-avail
);
2604 snprintf(tmp_20k_buf
,SIZEOF_20KBUF
,
2605 "Message %s copied to%s \"%s\"",
2606 long2string(mn_get_cur(msgmap
)), f
,
2607 short_str(newfolder
, (char *)(tmp_20k_buf
+1000), 1000,
2609 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
2613 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s messages saved",
2614 comatose(mn_total_cur(msgmap
)));
2615 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
2619 strncat(tmp_20k_buf
, " and deleted", SIZEOF_20KBUF
-strlen(tmp_20k_buf
)-1);
2620 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
2623 q_status_message(SM_ORDER
, 0, 3, tmp_20k_buf
);
2625 if(!MCMD_ISAGG(aopt
) && F_ON(F_SAVE_ADVANCES
, state
)){
2626 if(sp_new_mail_count(stream
))
2627 process_filter_patterns(stream
, msgmap
,
2628 sp_new_mail_count(stream
));
2630 mn_inc_cur(stream
, msgmap
,
2631 (in_index
== View
&& THREADING()
2632 && sp_viewing_a_thread(stream
))
2634 : (in_index
== View
)
2635 ? MH_ANYTHD
: MH_NONE
);
2638 state
->ugly_consider_advancing_bit
= 1;
2642 if(MCMD_ISAGG(aopt
)) /* straighten out fakes */
2643 restore_selected(msgmap
);
2646 update_titlebar_status(); /* make sure they see change */
2653 role_compose(struct pine
*state
)
2657 if(F_ON(F_ALT_ROLE_MENU
, state
) && mn_get_total(state
->msgmap
) > 0L){
2660 if(nonempty_patterns(ROLE_DO_ROLES
, &pstate
) && first_pattern(&pstate
)){
2661 action
= radio_buttons(_("Compose, Forward, Reply, or Bounce? "),
2662 -FOOTER_ROWS(state
), choose_action
,
2663 'c', 'x', h_role_compose
, RB_NORM
);
2666 q_status_message(SM_ORDER
, 0, 3,
2667 _("No roles available. Use Setup/Rules to add roles."));
2674 if(action
== 'c' || action
== 'r' || action
== 'f' || action
== 'b'){
2675 ACTION_S
*role
= NULL
;
2676 void (*prev_screen
)(struct pine
*) = NULL
, (*redraw
)(void) = NULL
;
2678 redraw
= state
->redrawer
;
2679 state
->redrawer
= NULL
;
2680 prev_screen
= state
->prev_screen
;
2682 state
->next_screen
= SCREEN_FUN_NULL
;
2685 if(role_select_screen(state
, &role
,
2686 action
== 'f' ? MC_FORWARD
:
2687 action
== 'r' ? MC_REPLY
:
2688 action
== 'b' ? MC_BOUNCE
:
2689 action
== 'c' ? MC_COMPOSE
: 0) < 0){
2690 cmd_cancelled(action
== 'f' ? _("Forward") :
2691 action
== 'r' ? _("Reply") :
2692 action
== 'c' ? _("Composition") : _("Bounce"));
2693 state
->next_screen
= prev_screen
;
2694 state
->redrawer
= redraw
;
2695 state
->mangled_screen
= 1;
2699 * If default role was selected (NULL) we need to make
2700 * up a role which won't do anything, but will cause
2701 * compose_mail to think there's already a role so that
2702 * it won't try to confirm the default.
2705 role
= combine_inherited_role(role
);
2707 role
= (ACTION_S
*) fs_get(sizeof(*role
));
2708 memset((void *) role
, 0, sizeof(*role
));
2709 role
->nick
= cpystr("Default Role");
2712 state
->redrawer
= NULL
;
2715 compose_mail(NULL
, NULL
, role
, NULL
, NULL
);
2719 (void) reply(state
, role
);
2723 (void) forward(state
, role
);
2727 (void) bounce(state
, role
);
2734 state
->next_screen
= prev_screen
;
2735 state
->redrawer
= redraw
;
2736 state
->mangled_screen
= 1;
2742 /*----------------------------------------------------------------------
2743 Do the dirty work of prompting the user for a folder name
2746 nfldr should be a buffer at least MAILTMPLEN long
2747 dela -- a pointer to a SaveDel. If it is
2748 DontAsk on input, don't offer Delete prompt
2749 Del on input, offer Delete command with default of Delete
2751 RetDel and RetNoDel are return values
2758 save_prompt(struct pine
*state
, CONTEXT_S
**cntxt
, char *nfldr
, size_t len_nfldr
,
2759 char *nmsgs
, ENVELOPE
*env
, long int rawmsgno
, char *section
,
2760 SaveDel
*dela
, SavePreserveOrder
*prea
)
2762 int rc
, ku
= -1, n
, flags
, last_rc
= 0, saveable_count
= 0, done
= 0;
2763 int delindex
, preindex
, r
;
2764 char prompt
[6*MAX_SCREEN_COLS
+1], *p
, expanded
[MAILTMPLEN
];
2765 char *buf
= tmp_20k_buf
;
2769 SaveDel del
= DontAsk
;
2770 SavePreserveOrder pre
= DontAskPreserve
;
2771 char *deltext
= NULL
;
2772 static HISTORY_S
*history
= NULL
;
2777 alpine_panic("no context ptr in save_prompt");
2779 init_hist(&history
, HISTSIZE
);
2781 if(!(folder
= save_get_default(state
, env
, rawmsgno
, section
, cntxt
)))
2782 return(0); /* message expunged! */
2784 /* how many context's can be saved to... */
2785 for(tc
= state
->context_list
; tc
; tc
= tc
->next
)
2789 /* set up extra command option keys */
2791 ekey
[rc
].ch
= ctrl('T');
2793 ekey
[rc
].name
= "^T";
2794 /* TRANSLATORS: command means go to Folders list */
2795 ekey
[rc
++].label
= N_("To Fldrs");
2797 if(saveable_count
> 1){
2798 ekey
[rc
].ch
= ctrl('P');
2800 ekey
[rc
].name
= "^P";
2801 ekey
[rc
++].label
= N_("Prev Collection");
2803 ekey
[rc
].ch
= ctrl('N');
2805 ekey
[rc
].name
= "^N";
2806 ekey
[rc
++].label
= N_("Next Collection");
2809 if(F_ON(F_ENABLE_TAB_COMPLETE
, ps_global
)){
2812 ekey
[rc
].name
= "TAB";
2813 /* TRANSLATORS: command asks alpine to complete the name when tab is typed */
2814 ekey
[rc
++].label
= N_("Complete");
2817 if(F_ON(F_ENABLE_SUB_LISTS
, ps_global
)){
2818 ekey
[rc
].ch
= ctrl('X');
2820 ekey
[rc
].name
= "^X";
2821 /* TRANSLATORS: list all the matches */
2822 ekey
[rc
++].label
= N_("ListMatches");
2825 if(dela
&& (*dela
== NoDel
|| *dela
== Del
)){
2826 ekey
[rc
].ch
= ctrl('R');
2828 ekey
[rc
].name
= "^R";
2833 if(prea
&& (*prea
== NoPreserve
|| *prea
== Preserve
)){
2834 ekey
[rc
].ch
= ctrl('W');
2836 ekey
[rc
].name
= "^W";
2841 if(saveable_count
> 1 && F_ON(F_DISABLE_SAVE_INPUT_HISTORY
, ps_global
)){
2842 ekey
[rc
].ch
= KEY_UP
;
2845 ekey
[rc
++].label
= "";
2847 ekey
[rc
].ch
= KEY_DOWN
;
2850 ekey
[rc
++].label
= "";
2852 else if(F_OFF(F_DISABLE_SAVE_INPUT_HISTORY
, ps_global
)){
2853 ekey
[rc
].ch
= KEY_UP
;
2857 ekey
[rc
++].label
= "";
2859 ekey
[rc
].ch
= KEY_DOWN
;
2862 ekey
[rc
++].label
= "";
2870 /* only show collection number if more than one available */
2871 if(ps_global
->context_list
->next
)
2872 snprintf(prompt
, sizeof(prompt
), "SAVE%s %sto folder in <%s> [%s] : ",
2873 deltext
? deltext
: "",
2875 short_str((*cntxt
)->nickname
, shortbuf
, sizeof(shortbuf
), 16, EndDots
),
2876 strsquish(buf
, SIZEOF_20KBUF
, folder
, 25));
2878 snprintf(prompt
, sizeof(prompt
), "SAVE%s %sto folder [%s] : ",
2879 deltext
? deltext
: "",
2880 nmsgs
, strsquish(buf
, SIZEOF_20KBUF
, folder
, 40));
2882 prompt
[sizeof(prompt
)-1] = '\0';
2885 * If the prompt won't fit, try removing deltext.
2887 if(state
->ttyo
->screen_cols
< strlen(prompt
) + MIN_OPT_ENT_WIDTH
&& deltext
){
2888 if(ps_global
->context_list
->next
)
2889 snprintf(prompt
, sizeof(prompt
), "SAVE %sto folder in <%s> [%s] : ",
2891 short_str((*cntxt
)->nickname
, shortbuf
, sizeof(shortbuf
), 16, EndDots
),
2892 strsquish(buf
, SIZEOF_20KBUF
, folder
, 25));
2894 snprintf(prompt
, sizeof(prompt
), "SAVE %sto folder [%s] : ",
2895 nmsgs
, strsquish(buf
, SIZEOF_20KBUF
, folder
, 40));
2897 prompt
[sizeof(prompt
)-1] = '\0';
2901 * If the prompt still won't fit, remove the extra info contained
2904 if(state
->ttyo
->screen_cols
< strlen(prompt
) + MIN_OPT_ENT_WIDTH
&& *nmsgs
){
2905 if(ps_global
->context_list
->next
)
2906 snprintf(prompt
, sizeof(prompt
), "SAVE to folder in <%s> [%s] : ",
2907 short_str((*cntxt
)->nickname
, shortbuf
, sizeof(shortbuf
), 16, EndDots
),
2908 strsquish(buf
, SIZEOF_20KBUF
, folder
, 25));
2910 snprintf(prompt
, sizeof(prompt
), "SAVE to folder [%s] : ",
2911 strsquish(buf
, SIZEOF_20KBUF
, folder
, 25));
2913 prompt
[sizeof(prompt
)-1] = '\0';
2917 ekey
[delindex
].label
= (del
== NoDel
) ? "Delete" : "No Delete";
2919 if(pre
!= DontAskPreserve
)
2920 ekey
[preindex
].label
= (pre
== NoPreserve
) ? "Preserve Order" : "Any Order";
2923 if(items_in_hist(history
) > 1){
2924 ekey
[ku
].name
= HISTORY_UP_KEYNAME
;
2925 ekey
[ku
].label
= HISTORY_KEYLABEL
;
2926 ekey
[ku
+1].name
= HISTORY_DOWN_KEYNAME
;
2927 ekey
[ku
+1].label
= HISTORY_KEYLABEL
;
2931 ekey
[ku
].label
= "";
2932 ekey
[ku
+1].name
= "";
2933 ekey
[ku
+1].label
= "";
2937 flags
= OE_APPEND_CURRENT
| OE_SEQ_SENSITIVE
;
2938 rc
= optionally_enter(nfldr
, -FOOTER_ROWS(state
), 0, len_nfldr
,
2939 prompt
, ekey
, help
, &flags
);
2943 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
2944 _("Error reading folder name"));
2949 removing_trailing_white_space(nfldr
);
2950 removing_leading_white_space(nfldr
);
2952 if(*nfldr
|| *folder
){
2953 char *p
, *name
, *fullname
= NULL
;
2954 int exists
, breakout
= FALSE
;
2957 strncpy(nfldr
, folder
, len_nfldr
-1);
2958 nfldr
[len_nfldr
-1] = '\0';
2961 save_hist(history
, nfldr
, 0, (void *) *cntxt
);
2963 if(!(name
= folder_is_nick(nfldr
, FOLDERS(*cntxt
), 0)))
2966 if(update_folder_spec(expanded
, sizeof(expanded
), name
)){
2967 strncpy(name
= nfldr
, expanded
, len_nfldr
-1);
2968 nfldr
[len_nfldr
-1] = '\0';
2971 exists
= folder_name_exists(*cntxt
, name
, &fullname
);
2973 if(exists
== FEX_ERROR
){
2974 q_status_message1(SM_ORDER
, 0, 3,
2975 _("Problem accessing folder \"%s\""),
2981 strncpy(name
= nfldr
, fullname
, len_nfldr
-1);
2982 nfldr
[len_nfldr
-1] = '\0';
2983 fs_give((void **) &fullname
);
2987 if(exists
& FEX_ISFILE
){
2990 else if((exists
& FEX_ISDIR
)){
2991 char tmp
[MAILTMPLEN
];
2995 CONTEXT_S
*fake_context
;
2998 strncpy(tmp
, name
, sizeof(tmp
));
2999 tmp
[sizeof(tmp
)-2-1] = '\0';
3000 if(tmp
[(l
= strlen(tmp
)) - 1] != tc
->dir
->delim
){
3001 if(l
< sizeof(tmp
)){
3002 tmp
[l
] = tc
->dir
->delim
;
3003 strncpy(&tmp
[l
+1], "[]", sizeof(tmp
)-(l
+1));
3007 strncat(tmp
, "[]", sizeof(tmp
)-strlen(tmp
)-1);
3009 tmp
[sizeof(tmp
)-1] = '\0';
3011 fake_context
= new_context(tmp
, 0);
3013 done
= display_folder_list(&fake_context
, nfldr
,
3014 1, folders_for_save
);
3015 free_context(&fake_context
);
3017 else if(tc
->dir
->delim
3018 && (p
= strrindex(name
, tc
->dir
->delim
))
3020 done
= display_folder_list(cntxt
, nfldr
,
3021 1, folders_for_save
);
3023 q_status_message1(SM_ORDER
, 3, 3,
3024 _("\"%s\" is a directory"), name
);
3026 && !((p
=strrindex(name
, tc
->dir
->delim
)) && *(p
+1) == '\0')){
3027 strncpy(tmp
, name
, sizeof(tmp
));
3028 tmp
[sizeof(tmp
)-1] = '\0';
3029 snprintf(nfldr
, len_nfldr
, "%s%c", tmp
, tc
->dir
->delim
);
3033 else{ /* Doesn't exist, create! */
3034 if((fullname
= folder_as_breakout(*cntxt
, name
)) != NULL
){
3035 strncpy(name
= nfldr
, fullname
, len_nfldr
-1);
3036 nfldr
[len_nfldr
-1] = '\0';
3037 fs_give((void **) &fullname
);
3040 switch(create_for_save(*cntxt
, name
)){
3041 case 1 : /* success */
3044 case 0 : /* error */
3045 case -1 : /* declined */
3054 /* else fall thru like they cancelled */
3057 cmd_cancelled("Save message");
3062 r
= display_folder_list(cntxt
, nfldr
, 0, folders_for_save
);
3070 helper(h_save
, _("HELP FOR SAVE"), HLPD_SIMPLE
);
3071 ps_global
->mangled_screen
= 1;
3074 case 4 : /* redraw */
3077 case 10 : /* previous collection */
3078 for(tc
= (*cntxt
)->prev
; tc
; tc
= tc
->prev
)
3085 for(tc2
= (tc
= (*cntxt
))->next
; tc2
; tc2
= tc2
->next
)
3093 case 11 : /* next collection */
3097 if(((*cntxt
) = (*cntxt
)->next
) == NULL
)
3098 (*cntxt
) = ps_global
->context_list
;
3099 while(NEWS_TEST(*cntxt
) && (*cntxt
) != tc
);
3102 case 12 : /* file name completion */
3103 if(!folder_complete(*cntxt
, nfldr
, len_nfldr
, &n
)){
3104 if(n
&& last_rc
== 12 && !(flags
& OE_USER_MODIFIED
)){
3105 r
= display_folder_list(cntxt
, nfldr
, 1, folders_for_save
);
3107 done
++; /* bingo! */
3109 rc
= 0; /* burn last_rc */
3117 case 14 : /* file name completion */
3118 r
= display_folder_list(cntxt
, nfldr
, 2, folders_for_save
);
3120 done
++; /* bingo! */
3122 rc
= 0; /* burn last_rc */
3126 case 15 : /* Delete / No Delete */
3127 del
= (del
== NoDel
) ? Del
: NoDel
;
3128 deltext
= (del
== NoDel
) ? " (no delete)" : " (and delete)";
3131 case 16 : /* Preserve Order or not */
3132 pre
= (pre
== NoPreserve
) ? Preserve
: NoPreserve
;
3136 if((p
= get_prev_hist(history
, nfldr
, 0, (void *) *cntxt
)) != NULL
){
3137 strncpy(nfldr
, p
, len_nfldr
);
3138 nfldr
[len_nfldr
-1] = '\0';
3139 if(history
->hist
[history
->curindex
])
3140 *cntxt
= (CONTEXT_S
*) history
->hist
[history
->curindex
]->cntxt
;
3148 if((p
= get_next_hist(history
, nfldr
, 0, (void *) *cntxt
)) != NULL
){
3149 strncpy(nfldr
, p
, len_nfldr
);
3150 nfldr
[len_nfldr
-1] = '\0';
3151 if(history
->hist
[history
->curindex
])
3152 *cntxt
= (CONTEXT_S
*) history
->hist
[history
->curindex
]->cntxt
;
3160 alpine_panic("Unhandled case");
3167 ps_global
->mangled_footer
= 1;
3173 strncpy(ps_global
->last_save_folder
, nfldr
, sizeof(ps_global
->last_save_folder
)-1);
3174 ps_global
->last_save_folder
[sizeof(ps_global
->last_save_folder
)-1] = '\0';
3176 ps_global
->last_save_context
= *cntxt
;
3179 strncpy(nfldr
, folder
, len_nfldr
-1);
3180 nfldr
[len_nfldr
-1] = '\0';
3183 /* nickname? Copy real name to nfldr */
3185 && context_isambig(nfldr
)
3186 && (p
= folder_is_nick(nfldr
, FOLDERS(*cntxt
), 0))){
3187 strncpy(nfldr
, p
, len_nfldr
-1);
3188 nfldr
[len_nfldr
-1] = '\0';
3191 if(dela
&& (*dela
== NoDel
|| *dela
== Del
))
3192 *dela
= (del
== NoDel
) ? RetNoDel
: RetDel
;
3194 if(prea
&& (*prea
== NoPreserve
|| *prea
== Preserve
))
3195 *prea
= (pre
== NoPreserve
) ? RetNoPreserve
: RetPreserve
;
3201 /*----------------------------------------------------------------------
3202 Prompt user before implicitly creating a folder for saving
3204 Args: context - context to create folder in
3205 folder - folder name to create
3207 Result: 1 on proceed, -1 on decline, 0 on error
3211 create_for_save_prompt(CONTEXT_S
*context
, char *folder
, int sequence_sensitive
)
3213 if(context
&& ps_global
->context_list
->next
&& context_isambig(folder
)){
3214 if(context
->use
& CNTXT_INCMNG
){
3215 snprintf(tmp_20k_buf
,SIZEOF_20KBUF
,
3216 _("\"%.15s%s\" doesn't exist - Add it in FOLDER LIST screen"),
3217 folder
, (strlen(folder
) > 15) ? "..." : "");
3218 q_status_message(SM_ORDER
, 3, 3, tmp_20k_buf
);
3219 return(0); /* error */
3222 snprintf(tmp_20k_buf
,SIZEOF_20KBUF
,
3223 _("Folder \"%.15s%s\" in <%.15s%s> doesn't exist. Create"),
3224 folder
, (strlen(folder
) > 15) ? "..." : "",
3226 (strlen(context
->nickname
) > 15) ? "..." : "");
3229 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,
3230 _("Folder \"%.40s%s\" doesn't exist. Create"),
3231 folder
, strlen(folder
) > 40 ? "..." : "");
3233 if(want_to(tmp_20k_buf
, 'y', 'n',
3234 NO_HELP
, (sequence_sensitive
) ? WT_SEQ_SENSITIVE
: WT_NORM
) != 'y'){
3235 cmd_cancelled("Save message");
3244 /*----------------------------------------------------------------------
3245 Expunge messages from current folder
3247 Args: state -- pointer to struct holding a bunch of pine state
3248 msgmap -- table mapping msg nums to c-client sequence nums
3249 qline -- screen line to ask questions on
3250 agg -- boolean indicating we're to operate on aggregate set
3255 cmd_expunge(struct pine
*state
, MAILSTREAM
*stream
, MSGNO_S
*msgmap
, int agg
)
3257 long del_count
, prefilter_del_count
;
3258 int we_cancel
= 0, rv
= 0;
3259 char prompt
[MAX_SCREEN_COLS
+1];
3261 COLOR_PAIR
*lastc
= NULL
;
3263 dprint((2, "\n - expunge -\n"));
3267 sequence
= MCMD_ISAGG(agg
) ? selected_sequence(stream
, msgmap
, NULL
, 0) : NULL
;
3269 if(MCMD_ISAGG(agg
)){
3272 for(i
= 1L; i
<= stream
->nmsgs
; i
++){
3273 if((mc
= mail_elt(stream
, i
)) != NULL
3274 && mc
->sequence
&& mc
->deleted
)
3278 q_status_message(SM_ORDER
, 0, 4,
3279 _("No selected messages are deleted"));
3283 if(!any_messages(msgmap
, NULL
, "to Expunge"))
3287 if(IS_NEWS(stream
) && stream
->rdonly
){
3288 if(!MCMD_ISAGG(agg
))
3289 del_count
= count_flagged(stream
, F_DEL
);
3291 state
->mangled_footer
= 1; /* MAX_SCREEN_COLS+1 = sizeof(prompt) */
3292 snprintf(prompt
, sizeof(prompt
), "Exclude %ld message%s from %.*s", del_count
,
3293 plural(del_count
), MAX_SCREEN_COLS
+1-40,
3294 pretty_fn(state
->cur_folder
));
3295 prompt
[sizeof(prompt
)-1] = '\0';
3296 if(F_ON(F_FULL_AUTO_EXPUNGE
, state
)
3297 || (F_ON(F_AUTO_EXPUNGE
, state
)
3298 && (state
->context_current
3299 && (state
->context_current
->use
& CNTXT_INCMNG
))
3300 && context_isambig(state
->cur_folder
))
3301 || want_to(prompt
, 'y', 0, NO_HELP
, WT_NORM
) == 'y'){
3303 if(F_ON(F_NEWS_CROSS_DELETE
, state
))
3304 cross_delete_crossposts(stream
);
3306 msgno_exclude_deleted(stream
, msgmap
, sequence
);
3307 clear_index_cache(stream
, 0);
3310 * This is kind of surprising at first. For most sort
3311 * orders, if the whole set is sorted, then any subset
3312 * is also sorted. Not so for threaded sorts.
3314 if(SORT_IS_THREADED(msgmap
))
3315 refresh_sort(stream
, msgmap
, SRT_NON
);
3317 state
->mangled_body
= 1;
3318 state
->mangled_header
= 1;
3319 q_status_message2(SM_ORDER
, 0, 4,
3320 "%s message%s excluded",
3321 long2string(del_count
),
3325 any_messages(NULL
, NULL
, "Excluded");
3328 any_messages(NULL
, "deleted", "to Exclude");
3332 else if(READONLY_FOLDER(stream
)){
3333 q_status_message(SM_ORDER
, 0, 4,
3334 _("Can't expunge. Folder is read-only"));
3338 if(!MCMD_ISAGG(agg
)){
3339 prefilter_del_count
= count_flagged(stream
, F_DEL
|F_NOFILT
);
3340 mail_expunge_prefilter(stream
, MI_NONE
);
3341 del_count
= count_flagged(stream
, F_DEL
|F_NOFILT
);
3346 unsigned char *fname
= folder_name_decoded((unsigned char *)state
->cur_folder
);
3347 /* MAX_SCREEN_COLS+1 = sizeof(prompt) */
3348 snprintf(prompt
, sizeof(prompt
), "Expunge %ld message%s from %.*s", del_count
,
3349 plural(del_count
), MAX_SCREEN_COLS
+1-40,
3350 pretty_fn((char *) fname
));
3351 if(fname
) fs_give((void **)&fname
);
3352 prompt
[sizeof(prompt
)-1] = '\0';
3353 state
->mangled_footer
= 1;
3355 if(F_ON(F_FULL_AUTO_EXPUNGE
, state
)
3356 || (F_ON(F_AUTO_EXPUNGE
, state
)
3357 && ((!strucmp(state
->cur_folder
,state
->inbox_name
))
3358 || (state
->context_current
->use
& CNTXT_INCMNG
))
3359 && context_isambig(state
->cur_folder
))
3360 || (ret
=want_to(prompt
, 'y', 0, NO_HELP
, WT_NORM
)) == 'y')
3364 cmd_cancelled("Expunge");
3370 dprint((8, "Expunge max:%ld cur:%ld kill:%d\n",
3371 mn_get_total(msgmap
), mn_get_cur(msgmap
), del_count
));
3373 lastc
= pico_set_colors(state
->VAR_TITLE_FORE_COLOR
,
3374 state
->VAR_TITLE_BACK_COLOR
,
3377 PutLine0(0, 0, "**"); /* indicate delay */
3380 (void)pico_set_colorp(lastc
, PSC_NONE
);
3381 free_color_pair(&lastc
);
3384 MoveCursor(state
->ttyo
->screen_rows
-FOOTER_ROWS(state
), 0);
3387 we_cancel
= busy_cue(_("Expunging"), NULL
, 1);
3389 if(cmd_expunge_work(stream
, msgmap
, sequence
))
3390 state
->mangled_body
= 1;
3393 fs_give((void **)&sequence
);
3396 cancel_busy_cue((sp_expunge_count(stream
) > 0) ? 0 : -1);
3398 lastc
= pico_set_colors(state
->VAR_TITLE_FORE_COLOR
,
3399 state
->VAR_TITLE_BACK_COLOR
,
3401 PutLine0(0, 0, " "); /* indicate delay's over */
3404 (void)pico_set_colorp(lastc
, PSC_NONE
);
3405 free_color_pair(&lastc
);
3410 if(sp_expunge_count(stream
) > 0){
3412 * This is kind of surprising at first. For most sort
3413 * orders, if the whole set is sorted, then any subset
3414 * is also sorted. Not so for threaded sorts.
3416 if(SORT_IS_THREADED(msgmap
))
3417 refresh_sort(stream
, msgmap
, SRT_NON
);
3421 unsigned char *fname
= folder_name_decoded((unsigned char *)state
->cur_folder
);
3422 q_status_message1(SM_ORDER
, 0, 3,
3423 _("No messages expunged from folder \"%s\""),
3424 pretty_fn((char *) fname
));
3425 if(fname
) fs_give((void **)&fname
);
3427 else if(!prefilter_del_count
)
3428 q_status_message(SM_ORDER
, 0, 3,
3429 _("No messages marked deleted. No messages expunged."));
3435 /*----------------------------------------------------------------------
3436 Expunge_and_close callback to prompt user for confirmation
3438 Args: stream -- folder's stream
3439 folder -- name of folder containing folders
3440 deleted -- number of del'd msgs
3442 Result: 'y' to continue with expunge
3445 expunge_prompt(MAILSTREAM
*stream
, char *folder
, long int deleted
)
3449 char prompt_b
[MAX_SCREEN_COLS
+1], temp
[MAILTMPLEN
+1], buff
[MAX_SCREEN_COLS
+1];
3450 char *short_folder_name
;
3455 snprintf(temp
, sizeof(temp
), "%ld", deleted
);
3456 charcnt
= strlen(temp
)+1;
3459 max_folder
= MAX(1,MAXPROMPT
- (36+charcnt
));
3460 strncpy(temp
, folder
, sizeof(temp
));
3461 temp
[sizeof(temp
)-1] = '\0';
3462 short_folder_name
= short_str(temp
,buff
,sizeof(buff
),max_folder
,FrontDots
);
3465 snprintf(prompt_b
, sizeof(prompt_b
),
3466 "Delete %s%ld message%s from \"%s\"",
3467 (deleted
> 1L) ? "all " : "", deleted
,
3468 plural(deleted
), short_folder_name
);
3470 snprintf(prompt_b
, sizeof(prompt_b
),
3471 "Expunge the %ld deleted message%s from \"%s\"",
3472 deleted
, deleted
== 1 ? "" : "s",
3475 return(want_to(prompt_b
, 'y', 0, NO_HELP
, WT_NORM
));
3480 * This is used with multiple append saves. Call it once before
3481 * the series of appends with SSCP_INIT and once after all are
3482 * done with SSCP_END. In between, it is called automatically
3483 * from save_fetch_append or save_fetch_append_cb when we need
3484 * to ask the user if he or she wants to continue even though
3485 * announced message size doesn't match the actual message size.
3486 * As of 2008-02-29 the gmail IMAP server has these size mismatches
3487 * on a regular basis even though the data is ok.
3490 save_size_changed_prompt(long msgno
, int flags
)
3494 static int remember_the_yes
= 0;
3495 static int possible_corruption
= 0;
3496 static ESCKEY_S save_size_opts
[] = {
3497 {'y', 'y', "Y", "Yes"},
3498 {'n', 'n', "N", "No"},
3499 {'a', 'a', "A", "yes to All"},
3503 if(F_ON(F_IGNORE_SIZE
, ps_global
))
3506 if(flags
& SSCP_INIT
|| flags
& SSCP_END
){
3507 if(flags
& SSCP_END
&& possible_corruption
)
3508 q_status_message(SM_ORDER
, 3, 3, "There is possible data corruption, check the results");
3510 remember_the_yes
= 0;
3511 possible_corruption
= 0;
3512 ps_global
->noshow_error
= 0;
3513 ps_global
->noshow_warn
= 0;
3517 if(remember_the_yes
){
3518 snprintf(prompt
, sizeof(prompt
),
3519 "Message to save shrank! (msg # %ld): Continuing", msgno
);
3520 q_status_message(SM_ORDER
, 0, 3, prompt
);
3521 display_message('x');
3522 return(remember_the_yes
);
3525 snprintf(prompt
, sizeof(prompt
),
3526 "Message to save shrank! (msg # %ld): Continue anyway ? ", msgno
);
3527 ret
= radio_buttons(prompt
, -FOOTER_ROWS(ps_global
), save_size_opts
,
3528 'n', 0, h_save_size_changed
, RB_NORM
|RB_NO_NEWMAIL
);
3532 remember_the_yes
= 'y';
3533 possible_corruption
++;
3534 return(remember_the_yes
);
3537 possible_corruption
++;
3541 possible_corruption
= 0;
3542 ps_global
->noshow_error
= 1;
3543 ps_global
->noshow_warn
= 1;
3551 /*----------------------------------------------------------------------
3552 Expunge_and_close callback that happens once the decision to expunge
3553 and close has been made and before expunging and closing begins
3556 Args: stream -- folder's stream
3557 folder -- name of folder containing folders
3558 deleted -- number of del'd msgs
3560 Result: 'y' to continue with expunge
3563 expunge_and_close_begins(int flags
, char *folder
)
3565 if(!(flags
& EC_NO_CLOSE
)){
3566 unsigned char *fname
= folder_name_decoded((unsigned char *)folder
);
3567 q_status_message1(SM_INFO
, 0, 1, "Closing \"%.200s\"...", (char *) fname
);
3568 flush_status_messages(1);
3569 if(fname
) fs_give((void **)&fname
);
3574 /*----------------------------------------------------------------------
3575 Export a message to a plain file in users home directory
3577 Args: state -- pointer to struct holding a bunch of pine state
3578 msgmap -- table mapping msg nums to c-client sequence nums
3579 qline -- screen line to ask questions on
3580 agg -- boolean indicating we're to operate on aggregate set
3585 cmd_export(struct pine
*state
, MSGNO_S
*msgmap
, int qline
, int aopt
)
3587 char filename
[MAXPATH
+1], full_filename
[MAXPATH
+1], *err
;
3589 int r
, leading_nl
, failure
= 0, orig_errno
, rflags
= GER_NONE
;
3590 int flags
= GE_IS_EXPORT
| GE_SEQ_SENSITIVE
, rv
= 0;
3594 long i
, count
= 0L, start_of_append
, rawno
;
3597 struct variable
*vars
= state
? ps_global
->vars
: NULL
;
3598 ESCKEY_S export_opts
[5];
3599 static HISTORY_S
*history
= NULL
;
3601 if(ps_global
->restricted
){
3602 q_status_message(SM_ORDER
, 0, 3,
3603 "Alpine demo can't export messages to files");
3607 if(MCMD_ISAGG(aopt
) && !pseudo_selected(state
->mail_stream
, msgmap
))
3610 export_opts
[i
= 0].ch
= ctrl('T');
3611 export_opts
[i
].rval
= 10;
3612 export_opts
[i
].name
= "^T";
3613 export_opts
[i
++].label
= N_("To Files");
3615 #if !defined(DOS) && !defined(MAC) && !defined(OS2)
3616 if(ps_global
->VAR_DOWNLOAD_CMD
&& ps_global
->VAR_DOWNLOAD_CMD
[0]){
3617 export_opts
[i
].ch
= ctrl('V');
3618 export_opts
[i
].rval
= 12;
3619 export_opts
[i
].name
= "^V";
3620 /* TRANSLATORS: this is an abbreviation for Download Messages */
3621 export_opts
[i
++].label
= N_("Downld Msg");
3623 #endif /* !(DOS || MAC) */
3625 if(F_ON(F_ENABLE_TAB_COMPLETE
,ps_global
)){
3626 export_opts
[i
].ch
= ctrl('I');
3627 export_opts
[i
].rval
= 11;
3628 export_opts
[i
].name
= "TAB";
3629 export_opts
[i
++].label
= N_("Complete");
3633 /* Commented out since it's not yet support! */
3634 if(F_ON(F_ENABLE_SUB_LISTS
,ps_global
)){
3635 export_opts
[i
].ch
= ctrl('X');
3636 export_opts
[i
].rval
= 14;
3637 export_opts
[i
].name
= "^X";
3638 export_opts
[i
++].label
= N_("ListMatches");
3643 * If message has attachments, add a toggle that will allow the user
3644 * to save all of the attachments to a single directory, using the
3645 * names provided with the attachments or part names. What we'll do is
3646 * export the message as usual, and then export the attachments into
3647 * a subdirectory that did not exist before. The subdir will be named
3648 * something based on the name of the file being saved to, but a
3651 if(!MCMD_ISAGG(aopt
)
3652 && state
->mail_stream
3653 && (rawno
= mn_m2raw(msgmap
, mn_get_cur(msgmap
))) > 0L
3654 && rawno
<= state
->mail_stream
->nmsgs
3655 && (env
= pine_mail_fetchstructure(state
->mail_stream
, rawno
, &b
))
3657 && b
->type
== TYPEMULTIPART
3659 && strucmp(b
->subtype
, "ALTERNATIVE") != 0){
3662 part
= b
->nested
.part
; /* 1st part */
3663 if(part
&& part
->next
)
3664 flags
|= GE_ALLPARTS
;
3667 export_opts
[i
].ch
= -1;
3670 if(mn_total_cur(msgmap
) <= 1L){
3671 snprintf(nmsgs
, sizeof(nmsgs
), "Msg #%ld", mn_get_cur(msgmap
));
3672 nmsgs
[sizeof(nmsgs
)-1] = '\0';
3675 snprintf(nmsgs
, sizeof(nmsgs
), "%s messages", comatose(mn_total_cur(msgmap
)));
3676 nmsgs
[sizeof(nmsgs
)-1] = '\0';
3679 r
= get_export_filename(state
, filename
, NULL
, full_filename
,
3680 sizeof(filename
), nmsgs
, "EXPORT",
3681 export_opts
, &rflags
, qline
, flags
, &history
);
3686 cmd_cancelled("Export message");
3690 q_status_message1(SM_ORDER
, 0, 2,
3691 _("Can't export to file outside of %s"),
3698 #if !defined(DOS) && !defined(MAC) && !defined(OS2)
3699 else if(r
== 12){ /* Download */
3700 char cmd
[MAXPATH
], *tfp
= NULL
;
3706 if(ps_global
->restricted
){
3707 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
3708 "Download disallowed in restricted mode");
3713 tfp
= temp_nam(NULL
, "pd");
3714 build_updown_cmd(cmd
, sizeof(cmd
), ps_global
->VAR_DOWNLOAD_CMD_PREFIX
,
3715 ps_global
->VAR_DOWNLOAD_CMD
, tfp
);
3716 dprint((1, "Download cmd called: \"%s\"\n", cmd
));
3717 if((so
= so_get(FileStar
, tfp
, WRITE_ACCESS
|OWNER_ONLY
|WRITE_TO_LOCALE
)) != NULL
){
3718 gf_set_so_writec(&pc
, so
);
3720 for(i
= mn_first_cur(msgmap
); i
> 0L; i
= mn_next_cur(msgmap
)){
3721 if(!(state
->mail_stream
3722 && (rawno
= mn_m2raw(msgmap
, i
)) > 0L
3723 && rawno
<= state
->mail_stream
->nmsgs
3724 && (mc
= mail_elt(state
->mail_stream
, rawno
))
3728 if(!(env
= pine_mail_fetchstructure(state
->mail_stream
,
3729 mn_m2raw(msgmap
, i
), &b
))
3730 || !bezerk_delimiter(env
, mc
, pc
, next
++)
3731 || !format_message(mn_m2raw(msgmap
, mn_get_cur(msgmap
)),
3732 env
, b
, NULL
, FM_NEW_MESS
| FM_NOWRAP
, pc
)){
3733 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
3734 err
= "Error writing tempfile for download");
3739 gf_clear_so_writec(so
);
3740 if(so_give(&so
)){ /* close file */
3742 err
= "Error writing tempfile for download";
3746 if((syspipe
= open_system_pipe(cmd
, NULL
, NULL
,
3747 PIPE_USER
| PIPE_RESET
,
3748 0, pipe_callback
, pipe_report_error
)) != NULL
)
3749 (void) close_system_pipe(&syspipe
, NULL
, pipe_callback
);
3751 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
3752 err
= _("Error running download command"));
3756 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
3757 err
= "Error building temp file for download");
3761 fs_give((void **)&tfp
);
3765 q_status_message(SM_ORDER
, 0, 3, _("Download Command Completed"));
3769 #endif /* !(DOS || MAC) */
3772 if(rflags
& GER_APPEND
)
3777 dprint((5, "Opening file \"%s\" for export\n",
3778 full_filename
? full_filename
: "?"));
3780 if(!(store
= so_get(FileStar
, full_filename
, WRITE_ACCESS
|WRITE_TO_LOCALE
))){
3781 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
3782 /* TRANSLATORS: error opening file "<filename>" to export message: <error text> */
3783 _("Error opening file \"%s\" to export message: %s"),
3784 full_filename
, error_description(errno
));
3788 gf_set_so_writec(&pc
, store
);
3791 for(i
= mn_first_cur(msgmap
); i
> 0L; i
= mn_next_cur(msgmap
), count
++){
3792 env
= pine_mail_fetchstructure(state
->mail_stream
, mn_m2raw(msgmap
, i
),
3795 err
= _("Can't export message. Error accessing mail folder");
3800 if(!(state
->mail_stream
3801 && (rawno
= mn_m2raw(msgmap
, i
)) > 0L
3802 && rawno
<= state
->mail_stream
->nmsgs
3803 && (mc
= mail_elt(state
->mail_stream
, rawno
))
3807 start_of_append
= so_tell(store
);
3808 if(!bezerk_delimiter(env
, mc
, pc
, leading_nl
)
3809 || !format_message(mn_m2raw(msgmap
, i
), env
, b
, NULL
,
3810 FM_NEW_MESS
| FM_NOWRAP
, pc
)){
3811 orig_errno
= errno
; /* save in case things are really bad */
3812 failure
= 1; /* pop out of here */
3819 gf_clear_so_writec(store
);
3820 if(so_give(&store
)) /* release storage */
3824 our_truncate(full_filename
, (off_t
)start_of_append
);
3826 dprint((1, "FAILED Export: fetch(%ld): %s\n",
3827 i
, err
? err
: "?"));
3828 q_status_message(SM_ORDER
| SM_DING
, 3, 4, err
);
3831 dprint((1, "FAILED Export: file \"%s\" : %s\n",
3832 full_filename
? full_filename
: "?",
3833 error_description(orig_errno
)));
3834 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
3835 /* TRANSLATORS: Error exporting to <filename>: <error text> */
3836 _("Error exporting to \"%s\" : %s"),
3837 filename
, error_description(orig_errno
));
3841 if(rflags
& GER_ALLPARTS
&& full_filename
[0]){
3842 char dir
[MAXPATH
+1];
3843 char lfile
[MAXPATH
+1];
3844 int ok
= 0, tries
= 0, saved
= 0, errs
= 0, counter
= 2;
3848 * Now we want to save all of the attachments to a subdirectory.
3849 * To make it easier for us and probably easier for the user, and
3850 * to prevent the user from shooting himself in the foot, we
3851 * make a new subdirectory so that we can't possibly step on
3852 * any existing files, and we don't need any interaction with the
3853 * user while saving.
3855 * We'll just use the directory name full_filename.d or if that
3856 * already exists and isn't empty, we'll try adding a suffix to
3857 * that until we get something to use.
3860 if(strlen(full_filename
) + strlen(".d") + 1 > sizeof(dir
)){
3861 q_status_message1(SM_ORDER
| SM_DING
, 3, 4,
3862 _("Can't save attachments, filename too long: %s"),
3868 snprintf(dir
, sizeof(dir
), "%s.d", full_filename
);
3869 dir
[sizeof(dir
)-1] = '\0';
3873 switch(r
= is_writable_dir(dir
)){
3874 case 0: /* exists and is a writable dir */
3876 * We could figure out if it is empty and use it in
3877 * that case, but that sounds like a lot of work, so
3878 * just fall through to default.
3882 if(strlen(full_filename
) + strlen(".d") + 1 +
3883 1 + strlen(long2string((long) tries
)) > sizeof(dir
)){
3884 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
3885 "Problem saving attachments");
3889 snprintf(dir
, sizeof(dir
), "%s.d_%s", full_filename
,
3890 long2string((long) tries
));
3891 dir
[sizeof(dir
)-1] = '\0';
3894 case 3: /* doesn't exist, that's good! */
3895 /* make new directory */
3899 } while(!ok
&& tries
< 1000);
3902 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
3903 _("Problem saving attachments"));
3907 /* create the new directory */
3908 if(our_mkdir(dir
, 0700)){
3909 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
3910 _("Problem saving attachments: %s: %s"), dir
,
3911 error_description(errno
));
3915 if(!(state
->mail_stream
3916 && (rawno
= mn_m2raw(msgmap
, mn_get_cur(msgmap
))) > 0L
3917 && rawno
<= state
->mail_stream
->nmsgs
3918 && (env
=pine_mail_fetchstructure(state
->mail_stream
,rawno
,&b
))
3920 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
3921 _("Problem reading message"));
3925 zero_atmts(state
->atmts
);
3926 describe_mime(b
, "", 1, 1, 0, 0);
3929 if(a
&& a
->description
) /* skip main body part */
3932 for(; a
->description
!= NULL
; a
++){
3933 /* skip over these parts of the message */
3934 if(MIME_MSG_A(a
) || MIME_DGST_A(a
) || MIME_VCARD_A(a
))
3938 (void) get_filename_parameter(lfile
, sizeof(lfile
), a
->body
, NULL
);
3940 if(lfile
[0] == '\0'){ /* MAXPATH + 1 = sizeof(lfile) */
3941 snprintf(lfile
, sizeof(lfile
), "part_%.*s", MAXPATH
+1-6,
3942 a
->number
? a
->number
: "?");
3943 lfile
[sizeof(lfile
)-1] = '\0';
3946 if(strlen(dir
) + strlen(S_FILESEP
) + strlen(lfile
) + 1
3947 > sizeof(filename
)){
3949 "FAILED Att Export: name too long: %s\n",
3950 dir
, S_FILESEP
, lfile
));
3955 /* although files are being saved in a unique directory, there is
3956 * no guarantee that attachment names have unique names, so we have
3957 * to make sure that we are not constantly rewriting the same file name
3958 * over and over. In order to avoid this we test if the file already exists,
3959 * and if so, we write a counter name in the file name, just before the
3960 * extension of the file, and separate it with an underscore.
3962 snprintf(filename
, sizeof(filename
), "%s%s%s", dir
, S_FILESEP
, lfile
);
3963 filename
[sizeof(filename
)-1] = '\0';
3964 while((ok
= can_access(filename
, ACCESS_EXISTS
)) == 0 && errs
== 0){
3966 snprintf(filename
, sizeof(filename
), "%d", counter
);
3967 if(strlen(dir
) + strlen(S_FILESEP
) + strlen(lfile
) + strlen(filename
) + 2
3968 > sizeof(filename
)){
3970 "FAILED Att Export: name too long: %s\n",
3971 dir
, S_FILESEP
, lfile
));
3975 if((ext
= strrchr(lfile
, '.')) != NULL
)
3977 snprintf(filename
, sizeof(filename
), "%s%s%s%s%d%s%s",
3978 dir
, S_FILESEP
, lfile
,
3979 ext
? "_" : "", counter
++, ext
? "." : "", ext
? ext
+1 : "");
3980 filename
[sizeof(filename
)-1] = '\0';
3983 if(write_attachment_to_file(state
->mail_stream
, rawno
,
3984 a
, GER_NONE
, filename
) == 1)
3992 q_status_message1(SM_ORDER
, 3, 3,
3993 "Errors saving some attachments, %s attachments saved",
3994 long2string((long) saved
));
3996 q_status_message(SM_ORDER
, 3, 3,
3997 _("Problems saving attachments"));
4001 q_status_message2(SM_ORDER
, 0, 3,
4002 /* TRANSLATORS: Saved <how many> attachments to <directory name> */
4003 _("Saved %s attachments to %s"),
4004 long2string((long) saved
), dir
);
4006 q_status_message(SM_ORDER
, 3, 3, _("No attachments to save"));
4009 else if(mn_total_cur(msgmap
) > 1L)
4010 q_status_message4(SM_ORDER
,0,3,
4011 "%s message%s %s to file \"%s\"",
4012 long2string(count
), plural(count
),
4015 : rflags
& GER_APPEND
? "appended" : "exported",
4018 q_status_message3(SM_ORDER
,0,3,
4019 "Message %s %s to file \"%s\"",
4020 long2string(mn_get_cur(msgmap
)),
4023 : rflags
& GER_APPEND
? "appended" : "exported",
4029 if(MCMD_ISAGG(aopt
))
4030 restore_selected(msgmap
);
4037 * Ask user what file to export to. Export from srcstore to that file.
4039 * Args ps -- pine struct
4040 * srctext -- pointer to source text
4041 * srctype -- type of that source text
4042 * prompt_msg -- see get_export_filename
4045 * Returns: != 0 : error
4049 simple_export(struct pine
*ps
, void *srctext
, SourceType srctype
, char *prompt_msg
, char *lister_msg
)
4051 int r
= 1, rflags
= GER_NONE
;
4052 char filename
[MAXPATH
+1], full_filename
[MAXPATH
+1];
4053 STORE_S
*store
= NULL
;
4054 struct variable
*vars
= ps
? ps
->vars
: NULL
;
4055 static HISTORY_S
*history
= NULL
;
4056 static ESCKEY_S simple_export_opts
[] = {
4057 {ctrl('T'), 10, "^T", N_("To Files")},
4058 {-1, 0, NULL
, NULL
},
4059 {-1, 0, NULL
, NULL
}};
4061 if(F_ON(F_ENABLE_TAB_COMPLETE
,ps
)){
4062 simple_export_opts
[r
].ch
= ctrl('I');
4063 simple_export_opts
[r
].rval
= 11;
4064 simple_export_opts
[r
].name
= "TAB";
4065 simple_export_opts
[r
].label
= N_("Complete");
4069 q_status_message(SM_ORDER
, 0, 2, _("Error allocating space"));
4074 simple_export_opts
[++r
].ch
= -1;
4076 full_filename
[0] = '\0';
4078 r
= get_export_filename(ps
, filename
, NULL
, full_filename
, sizeof(filename
),
4079 prompt_msg
, lister_msg
, simple_export_opts
, &rflags
,
4080 -FOOTER_ROWS(ps
), GE_IS_EXPORT
, &history
);
4084 else if(!full_filename
[0]){
4089 dprint((5, "Opening file \"%s\" for export\n",
4090 full_filename
? full_filename
: "?"));
4092 if((store
= so_get(FileStar
, full_filename
, WRITE_ACCESS
|WRITE_TO_LOCALE
)) != NULL
){
4096 gf_set_so_writec(&pc
, store
);
4097 gf_set_readc(&gc
, srctext
, (srctype
== CharStar
)
4098 ? strlen((char *)srctext
)
4102 if((pipe_err
= gf_pipe(gc
, pc
)) != NULL
){
4103 q_status_message2(SM_ORDER
| SM_DING
, 3, 3,
4104 /* TRANSLATORS: Problem saving to <filename>: <error text> */
4105 _("Problem saving to \"%s\": %s"),
4106 filename
, pipe_err
);
4112 gf_clear_so_writec(store
);
4113 if(so_give(&store
)){
4114 q_status_message2(SM_ORDER
| SM_DING
, 3, 3,
4115 _("Problem saving to \"%s\": %s"),
4116 filename
, error_description(errno
));
4121 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
4122 _("Error opening file \"%s\" for export: %s"),
4123 full_filename
, error_description(errno
));
4130 /* overloading full_filename */
4131 snprintf(full_filename
, sizeof(full_filename
), "%c%s",
4132 (prompt_msg
&& prompt_msg
[0])
4133 ? (islower((unsigned char)prompt_msg
[0])
4134 ? toupper((unsigned char)prompt_msg
[0]) : prompt_msg
[0])
4136 (prompt_msg
&& prompt_msg
[0]) ? prompt_msg
+1 : "ext");
4137 full_filename
[sizeof(full_filename
)-1] = '\0';
4138 q_status_message3(SM_ORDER
,0,2,"%s %s to \"%s\"",
4142 : rflags
& GER_APPEND
? "appended" : "exported",
4147 cmd_cancelled("Export");
4151 q_status_message1(SM_ORDER
, 0, 2,
4152 _("Can't export to file outside of %s"), VAR_OPER_DIR
);
4156 ps
->mangled_footer
= 1;
4162 * Ask user what file to export to.
4164 * filename -- On input, this is the filename to start with. On exit,
4165 * this is the filename chosen. (but this isn't used)
4166 * deefault -- This is the default value if user hits return. The
4167 * prompt will have [deefault] added to it automatically.
4168 * full_filename -- This is the full filename on exit.
4169 * len -- Minimum length of _both_ filename and full_filename.
4170 * prompt_msg -- Message to insert in prompt.
4171 * lister_msg -- Message to insert in file_lister.
4172 * opts -- Key options.
4173 * There is a tangled relationship between the callers
4174 * and this routine as far as opts are concerned. Some
4175 * of the opts are handled here. In particular, r == 3,
4176 * r == 10, r == 11, and r == 13 are all handled here.
4177 * Don't use those values unless you want what happens
4178 * here. r == 12 and others are handled by the caller.
4179 * rflags -- Return flags
4180 * GER_OVER - overwrite of existing file
4181 * GER_APPEND - append of existing file
4182 * else file did not exist before
4184 * GER_ALLPARTS - AllParts toggle was turned on
4186 * qline -- Command line to prompt on.
4187 * flags -- Logically OR'd flags
4188 * GE_IS_EXPORT - The command was an Export command
4189 * so the prompt should include
4191 * GE_SEQ_SENSITIVE - The command that got us here is
4192 * sensitive to sequence number changes
4193 * caused by unsolicited expunges.
4194 * GE_NO_APPEND - We will not allow append to an
4195 * existing file, only removal of the
4196 * file if it exists.
4197 * GE_IS_IMPORT - We are selecting for reading.
4198 * No overwriting or checking for
4199 * existence at all. Don't use this
4200 * together with GE_NO_APPEND.
4201 * GE_ALLPARTS - Turn on AllParts toggle.
4202 * GE_BINARY - Turn on Binary toggle.
4204 * Returns: -1 cancelled
4205 * -2 prohibited by VAR_OPER_DIR
4206 * -3 other error, already reported here
4208 * 12 user chose 12 command from opts
4211 get_export_filename(struct pine
*ps
, char *filename
, char *deefault
,
4212 char *full_filename
, size_t len
, char *prompt_msg
,
4213 char *lister_msg
, ESCKEY_S
*optsarg
, int *rflags
,
4214 int qline
, int flags
, HISTORY_S
**history
)
4216 char dir
[MAXPATH
+1], dir2
[MAXPATH
+1], orig_dir
[MAXPATH
+1];
4217 char precolon
[MAXPATH
+1], postcolon
[MAXPATH
+1];
4218 char filename2
[MAXPATH
+1], tmp
[MAXPATH
+1], *fn
, *ill
;
4219 int l
, i
, ku
= -1, kp
= -1, r
, fatal
, homedir
= 0, was_abs_path
=0, avail
, ret
= 0;
4220 int allparts
= 0, binary
= 0;
4221 char prompt_buf
[400];
4223 ESCKEY_S
*opts
= NULL
;
4224 struct variable
*vars
= ps
->vars
;
4225 static HISTORY_S
*dir_hist
= NULL
;
4227 int pos
, hist_len
= 0;
4230 /* we will fake a history with the ps_global->VAR_HISTORY variable
4231 * We fake that we combine this variable into a history variable
4232 * by stacking VAR_HISTORY on top of dir_hist. We keep track of this
4233 * by looking at the variable pos.
4235 if(ps_global
->VAR_HISTORY
!= NULL
)
4236 for(hist_len
= 0; ps_global
->VAR_HISTORY
[hist_len
]
4237 && ps_global
->VAR_HISTORY
[hist_len
][0]; hist_len
++)
4240 pos
= hist_len
+ items_in_hist(dir_hist
);
4242 if(flags
& GE_ALLPARTS
|| history
|| dir_hist
){
4244 * Copy the opts and add one to the end of the list.
4246 for(i
= 0; optsarg
[i
].ch
!= -1; i
++)
4249 if(dir_hist
|| hist_len
> 0)
4253 i
+= dir_hist
|| hist_len
> 0 ? 2 : 4;
4255 if(flags
& GE_ALLPARTS
)
4258 if(flags
& GE_BINARY
)
4261 opts
= (ESCKEY_S
*) fs_get((i
+1) * sizeof(*opts
));
4262 memset(opts
, 0, (i
+1) * sizeof(*opts
));
4264 for(i
= 0; optsarg
[i
].ch
!= -1; i
++){
4265 opts
[i
].ch
= optsarg
[i
].ch
;
4266 opts
[i
].rval
= optsarg
[i
].rval
;
4267 opts
[i
].name
= optsarg
[i
].name
; /* no need to make a copy */
4268 opts
[i
].label
= optsarg
[i
].label
; /* " */
4271 if(flags
& GE_ALLPARTS
){
4273 opts
[i
].ch
= ctrl('P');
4275 opts
[i
].name
= "^P";
4276 /* TRANSLATORS: Export all attachment parts */
4277 opts
[i
++].label
= N_("AllParts");
4280 if(flags
& GE_BINARY
){
4282 opts
[i
].ch
= ctrl('R');
4284 opts
[i
].name
= "^R";
4285 opts
[i
++].label
= N_("Binary");
4288 rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
,
4289 SIZEOF_20KBUF
, filename
);
4291 /* In the Windows operating system we always return the UTF8 encoded name */
4292 if(strcmp(tmp_20k_buf
, filename
)){
4293 opts
[i
].ch
= ctrl('N');
4295 opts
[i
].name
= "^N";
4296 opts
[i
++].label
= "Name UTF8";
4299 strncpy(filename
, tmp_20k_buf
, len
);
4300 filename
[len
-1] = '\0';
4301 #endif /* _WINDOWS */
4303 if(dir_hist
|| hist_len
> 0){
4304 opts
[i
].ch
= ctrl('Y');
4308 opts
[i
++].label
= "";
4310 opts
[i
].ch
= ctrl('V');
4313 opts
[i
++].label
= "";
4317 opts
[i
].ch
= KEY_UP
;
4321 opts
[i
++].label
= "";
4323 opts
[i
].ch
= KEY_DOWN
;
4326 opts
[i
++].label
= "";
4332 init_hist(history
, HISTSIZE
);
4333 init_hist(&dir_hist
, HISTSIZE
); /* reset history to the end */
4341 if(F_ON(F_USE_CURRENT_DIR
, ps
))
4343 else if(VAR_OPER_DIR
){
4344 strncpy(dir
, VAR_OPER_DIR
, sizeof(dir
));
4345 dir
[sizeof(dir
)-1] = '\0';
4347 #if defined(DOS) || defined(OS2)
4348 else if(VAR_FILE_DIR
){
4349 strncpy(dir
, VAR_FILE_DIR
, sizeof(dir
));
4350 dir
[sizeof(dir
)-1] = '\0';
4358 strncpy(orig_dir
, dir
, sizeof(orig_dir
));
4359 orig_dir
[sizeof(orig_dir
)-1] = '\0';
4361 postcolon
[0] = '\0';
4362 strncpy(precolon
, dir
, sizeof(precolon
));
4363 precolon
[sizeof(precolon
)-1] = '\0';
4365 strncpy(def
, deefault
, sizeof(def
)-1);
4366 def
[sizeof(def
)-1] = '\0';
4367 removing_leading_and_trailing_white_space(def
);
4372 avail
= MAX(20, ps_global
->ttyo
? ps_global
->ttyo
->screen_cols
: 80) - MIN_OPT_ENT_WIDTH
;
4374 /*---------- Prompt the user for the file name -------------*/
4377 char dirb
[50], fileb
[50];
4378 int l1
, l2
, l3
, l4
, l5
, needed
;
4379 char *p
, p1
[100], p2
[100], *p3
, p4
[100], p5
[100];
4381 snprintf(p1
, sizeof(p1
), "%sCopy ",
4382 (flags
& GE_IS_EXPORT
) ? "EXPORT: " :
4383 (flags
& GE_IS_IMPORT
) ? "IMPORT: " : "SAVE: ");
4384 p1
[sizeof(p1
)-1] = '\0';
4387 strncpy(p2
, prompt_msg
? prompt_msg
: "", sizeof(p2
)-1);
4388 p2
[sizeof(p2
)-1] = '\0';
4391 if(rflags
&& *rflags
& GER_ALLPARTS
)
4398 snprintf(p4
, sizeof(p4
), " %s file%s%s",
4399 (flags
& GE_IS_IMPORT
) ? "from" : "to",
4400 is_absolute_path(filename
) ? "" : " in ",
4401 is_absolute_path(filename
) ? "" :
4402 (!dir
[0] ? "current directory"
4403 : (dir
[0] == '~' && !dir
[1]) ? "home directory"
4404 : short_str(dir
,dirb
,sizeof(dirb
),30,FrontDots
)));
4405 p4
[sizeof(p4
)-1] = '\0';
4408 snprintf(p5
, sizeof(p5
), "%s%s%s: ",
4410 *def
? short_str(def
,fileb
,sizeof(fileb
),40,EndDots
) : "",
4412 p5
[sizeof(p5
)-1] = '\0';
4415 if((needed
= l1
+l2
+l3
+l4
+l5
-avail
) > 0){
4416 snprintf(p4
, sizeof(p4
), " %s file%s%s",
4417 (flags
& GE_IS_IMPORT
) ? "from" : "to",
4418 is_absolute_path(filename
) ? "" : " in ",
4419 is_absolute_path(filename
) ? "" :
4420 (!dir
[0] ? "current dir"
4421 : (dir
[0] == '~' && !dir
[1]) ? "home dir"
4422 : short_str(dir
,dirb
,sizeof(dirb
),10,FrontDots
)));
4423 p4
[sizeof(p4
)-1] = '\0';
4427 if((needed
= l1
+l2
+l3
+l4
+l5
-avail
) > 0 && l5
> 0){
4428 snprintf(p5
, sizeof(p5
), "%s%s%s: ",
4430 *def
? short_str(def
,fileb
,sizeof(fileb
),
4431 MAX(15,l5
-5-needed
),EndDots
) : "",
4433 p5
[sizeof(p5
)-1] = '\0';
4437 if((needed
= l1
+l2
+l3
+l4
+l5
-avail
) > 0 && l2
> 0){
4440 * 14 is about the shortest we can make this, because there are
4441 * fixed length strings of length 14 coming in here.
4443 p
= short_str(prompt_msg
, p2
, sizeof(p2
), MAX(14,l2
-needed
), FrontDots
);
4445 strncpy(p2
, p
, sizeof(p2
)-1);
4446 p2
[sizeof(p2
)-1] = '\0';
4452 if((needed
= l1
+l2
+l3
+l4
+l5
-avail
) > 0){
4453 strncpy(p1
, "Copy ", sizeof(p1
)-1);
4454 p1
[sizeof(p1
)-1] = '\0';
4458 if((needed
= l1
+l2
+l3
+l4
+l5
-avail
) > 0 && l5
> 0){
4459 snprintf(p5
, sizeof(p5
), "%s%s%s: ",
4461 *def
? short_str(def
,fileb
, sizeof(fileb
),
4462 MAX(10,l5
-5-needed
),EndDots
) : "",
4464 p5
[sizeof(p5
)-1] = '\0';
4468 if((needed
= l1
+l2
+l3
+l4
+l5
-avail
) > 0 && l3
> 0){
4469 if(needed
<= l3
- strlen(" (+ atts)"))
4471 else if(needed
<= l3
- strlen(" (atts)"))
4473 else if(needed
<= l3
- strlen(" (+)"))
4475 else if(needed
<= l3
- strlen("+"))
4483 snprintf(prompt_buf
, sizeof(prompt_buf
), "%s%s%s%s%s", p1
, p2
, p3
, p4
, p5
);
4484 prompt_buf
[sizeof(prompt_buf
)-1] = '\0';
4487 if(items_in_hist(dir_hist
) > 0 || hist_len
> 0){ /* any directories */
4488 opts
[kp
].name
= "^Y";
4489 opts
[kp
].label
= "Prev Dir";
4490 opts
[kp
+1].name
= "^V";
4491 opts
[kp
+1].label
= "Next Dir";
4495 opts
[kp
].label
= "";
4496 opts
[kp
+1].name
= "";
4497 opts
[kp
+1].label
= "";
4502 if(items_in_hist(*history
) > 0){
4503 opts
[ku
].name
= HISTORY_UP_KEYNAME
;
4504 opts
[ku
].label
= HISTORY_KEYLABEL
;
4505 opts
[ku
+1].name
= HISTORY_DOWN_KEYNAME
;
4506 opts
[ku
+1].label
= HISTORY_KEYLABEL
;
4510 opts
[ku
].label
= "";
4511 opts
[ku
+1].name
= "";
4512 opts
[ku
+1].label
= "";
4516 oeflags
= OE_APPEND_CURRENT
|
4517 ((flags
& GE_SEQ_SENSITIVE
) ? OE_SEQ_SENSITIVE
: 0);
4518 r
= optionally_enter(filename
, qline
, 0, len
, prompt_buf
,
4519 opts
, NO_HELP
, &oeflags
);
4521 dprint((2, "\n - export_filename = \"%s\", r = %d -\n", filename
, r
));
4525 * Helps may not be right if you add another caller or change
4526 * things. Check it out.
4528 if(flags
& GE_IS_IMPORT
)
4529 helper(h_ge_import
, _("HELP FOR IMPORT FILE SELECT"), HLPD_SIMPLE
);
4530 else if(flags
& GE_ALLPARTS
)
4531 helper(h_ge_allparts
, _("HELP FOR EXPORT FILE SELECT"), HLPD_SIMPLE
);
4533 helper(h_ge_export
, _("HELP FOR EXPORT FILE SELECT"), HLPD_SIMPLE
);
4535 ps
->mangled_screen
= 1;
4539 else if(r
== 10 || r
== 11){ /* Browser or File Completion */
4540 if(filename
[0]=='~'){
4541 if(filename
[1] == C_FILESEP
&& filename
[2]!='\0'){
4544 for(i
=0; filename
[i
+2] != '\0' && i
+2 < len
-1; i
++)
4545 filename
[i
] = filename
[i
+2];
4547 strncpy(dir
, precolon
, sizeof(dir
)-1);
4548 dir
[sizeof(dir
)-1] = '\0';
4550 else if(filename
[1]=='\0' ||
4551 (filename
[1] == C_FILESEP
&& filename
[2] == '\0')){
4555 strncpy(dir
, precolon
, sizeof(dir
)-1);
4556 dir
[sizeof(dir
)-1] = '\0';
4559 else if(!dir
[0] && !is_absolute_path(filename
) && was_abs_path
){
4563 strncpy(dir
, precolon
, sizeof(dir
)-1);
4564 dir
[sizeof(dir
)-1] = '\0';
4573 strncpy(tmp
, filename
, sizeof(tmp
)-1);
4574 tmp
[sizeof(tmp
)-1] = '\0';
4575 if(*tmp
&& is_absolute_path(tmp
))
4576 fnexpand(tmp
, sizeof(tmp
));
4577 if(strncmp(tmp
,postcolon
, strlen(postcolon
)))
4578 postcolon
[0] = '\0';
4580 if(*tmp
&& (fn
= last_cmpnt(tmp
))){
4582 strncpy(filename2
, fn
, sizeof(filename2
)-1);
4583 filename2
[sizeof(filename2
)-1] = '\0';
4584 if(is_absolute_path(tmp
)){
4585 strncpy(dir2
, tmp
, MIN(fn
- tmp
, sizeof(dir2
)-1));
4586 dir2
[MIN(fn
- tmp
, sizeof(dir2
)-1)] = '\0';
4588 if(tmp
[1]==':' && tmp
[2]=='\\' && dir2
[2]=='\0'){
4593 strncpy(postcolon
, dir2
, sizeof(postcolon
)-1);
4594 postcolon
[sizeof(postcolon
)-1] = '\0';
4600 * Just building the directory name in dir2,
4601 * full_filename is overloaded.
4603 snprintf(full_filename
, len
, "%.*s", (int) MIN(fn
-tmp
,len
-1), tmp
);
4604 full_filename
[len
-1] = '\0';
4605 strncpy(postcolon
, full_filename
, sizeof(postcolon
)-1);
4606 postcolon
[sizeof(postcolon
)-1] = '\0';
4607 build_path(dir2
, !dir
[0] ? p
= (char *)getcwd(NULL
,MAXPATH
)
4608 : (dir
[0] == '~' && !dir
[1])
4611 full_filename
, sizeof(dir2
));
4617 if(is_absolute_path(tmp
)){
4618 strncpy(dir2
, tmp
, sizeof(dir2
)-1);
4619 dir2
[sizeof(dir2
)-1] = '\0';
4621 if(dir2
[2]=='\0' && dir2
[1]==':'){
4624 strncpy(postcolon
,dir2
,sizeof(postcolon
)-1);
4625 postcolon
[sizeof(postcolon
)-1] = '\0';
4628 filename2
[0] = '\0';
4632 strncpy(filename2
, tmp
, sizeof(filename2
)-1);
4633 filename2
[sizeof(filename2
)-1] = '\0';
4635 if(getcwd(dir2
, sizeof(dir2
)) == NULL
)
4636 alpine_panic(_("getcwd() call failed at get_export_filename"));
4638 else if(dir
[0] == '~' && !dir
[1]){
4639 strncpy(dir2
, ps
->home_dir
, sizeof(dir2
)-1);
4640 dir2
[sizeof(dir2
)-1] = '\0';
4643 strncpy(dir2
, dir
, sizeof(dir2
)-1);
4644 dir2
[sizeof(dir2
)-1] = '\0';
4647 postcolon
[0] = '\0';
4651 build_path(full_filename
, dir2
, filename2
, len
);
4652 if(!strcmp(full_filename
, dir2
))
4653 filename2
[0] = '\0';
4654 if(full_filename
[strlen(full_filename
)-1] == C_FILESEP
4655 && isdir(full_filename
,NULL
,NULL
)){
4656 if(strlen(full_filename
) == 1)
4657 strncpy(postcolon
, full_filename
, sizeof(postcolon
)-1);
4658 else if(filename2
[0])
4659 strncpy(postcolon
, filename2
, sizeof(postcolon
)-1);
4660 postcolon
[sizeof(postcolon
)-1] = '\0';
4661 strncpy(dir2
, full_filename
, sizeof(dir2
)-1);
4662 dir2
[sizeof(dir2
)-1] = '\0';
4663 filename2
[0] = '\0';
4665 #ifdef _WINDOWS /* use full_filename even if not a valid directory */
4666 else if(full_filename
[strlen(full_filename
)-1] == C_FILESEP
){
4667 strncpy(postcolon
, filename2
, sizeof(postcolon
)-1);
4668 postcolon
[sizeof(postcolon
)-1] = '\0';
4669 strncpy(dir2
, full_filename
, sizeof(dir2
)-1);
4670 dir2
[sizeof(dir2
)-1] = '\0';
4671 filename2
[0] = '\0';
4674 if(dir2
[strlen(dir2
)-1] == C_FILESEP
&& strlen(dir2
)!=1
4675 && strcmp(dir2
+1, ":\\"))
4676 /* last condition to prevent stripping of '\\'
4677 in windows partition */
4678 dir2
[strlen(dir2
)-1] = '\0';
4680 if(r
== 10){ /* File Browser */
4681 r
= file_lister(lister_msg
? lister_msg
: "EXPORT",
4682 dir2
, sizeof(dir2
), filename2
, sizeof(filename2
),
4684 (flags
& GE_IS_IMPORT
) ? FB_READ
: FB_SAVE
);
4686 /* Windows has a special "feature" in which entering the file browser will
4687 change the working directory if the directory is changed at all (even
4688 clicking "Cancel" will change the working directory).
4690 if(F_ON(F_USE_CURRENT_DIR
, ps
))
4691 (void)getcwd(dir2
,sizeof(dir2
));
4693 if(isdir(dir2
,NULL
,NULL
)){
4694 strncpy(precolon
, dir2
, sizeof(precolon
)-1);
4695 precolon
[sizeof(precolon
)-1] = '\0';
4697 strncpy(postcolon
, filename2
, sizeof(postcolon
)-1);
4698 postcolon
[sizeof(postcolon
)-1] = '\0';
4700 build_path(full_filename
, dir2
, filename2
, len
);
4701 if(isdir(full_filename
, NULL
, NULL
)){
4702 strncpy(dir
, full_filename
, sizeof(dir
)-1);
4703 dir
[sizeof(dir
)-1] = '\0';
4707 fn
= last_cmpnt(full_filename
);
4708 strncpy(dir
, full_filename
,
4709 MIN(fn
- full_filename
, sizeof(dir
)-1));
4710 dir
[MIN(fn
- full_filename
, sizeof(dir
)-1)] = '\0';
4711 if(fn
- full_filename
> 1)
4712 dir
[fn
- full_filename
- 1] = '\0';
4715 if(!strcmp(dir
, ps
->home_dir
)){
4720 strncpy(filename
, fn
, len
-1);
4721 filename
[len
-1] = '\0';
4724 else{ /* File Completion */
4725 if(!pico_fncomplete(dir2
, filename2
, sizeof(filename2
)))
4727 strncat(postcolon
, filename2
,
4728 sizeof(postcolon
)-1-strlen(postcolon
));
4729 postcolon
[sizeof(postcolon
)-1] = '\0';
4731 was_abs_path
= is_absolute_path(filename
);
4733 if(!strcmp(dir
, ps
->home_dir
)){
4738 strncpy(filename
, postcolon
, len
-1);
4739 filename
[len
-1] = '\0';
4740 strncpy(dir
, precolon
, sizeof(dir
)-1);
4741 dir
[sizeof(dir
)-1] = '\0';
4743 if(filename
[0] == '~' && !filename
[1]){
4751 else if(r
== 12){ /* Download, caller handles it */
4755 else if(r
== 13){ /* toggle AllParts bit */
4757 if(*rflags
& GER_ALLPARTS
){
4758 *rflags
&= ~GER_ALLPARTS
;
4759 opts
[allparts
].label
= N_("AllParts");
4762 *rflags
|= GER_ALLPARTS
;
4763 /* opposite of All Parts, No All Parts */
4764 opts
[allparts
].label
= N_("NoAllParts");
4771 else if(r
== 14){ /* List file names matching partial? */
4775 else if(r
== 15){ /* toggle Binary bit */
4777 if(*rflags
& GER_BINARY
){
4778 *rflags
&= ~GER_BINARY
;
4779 opts
[binary
].label
= N_("Binary");
4782 *rflags
|= GER_BINARY
;
4783 opts
[binary
].label
= N_("No Binary");
4789 else if(r
== 1){ /* Cancel */
4796 else if(r
>= 30 && r
<= 33){
4799 if(r
== 30 || r
== 31){
4802 p
= get_prev_hist(*history
, filename
, 0, NULL
);
4804 p
= get_next_hist(*history
, filename
, 0, NULL
);
4808 if(r
== 32 || r
== 33){
4809 int nitems
= items_in_hist(dir_hist
);
4810 if(dir_hist
|| hist_len
> 0){
4813 p
= hist_in_pos(--pos
, ps_global
->VAR_HISTORY
, hist_len
, dir_hist
, nitems
);
4817 if(pos
< hist_len
+ nitems
)
4818 p
= hist_in_pos(++pos
, ps_global
->VAR_HISTORY
, hist_len
, dir_hist
, nitems
);
4820 if(p
== NULL
|| *p
== '\0')
4824 last
= p
; /* save it! */
4826 if(p
!= NULL
&& *p
!= '\0'){
4827 if(r
== 30 || r
== 31){
4828 if((fn
= last_cmpnt(p
)) != NULL
){
4829 strncpy(dir
, p
, MIN(fn
- p
, sizeof(dir
)-1));
4830 dir
[MIN(fn
- p
, sizeof(dir
)-1)] = '\0';
4832 dir
[fn
- p
- 1] = '\0';
4833 strncpy(filename
, fn
, len
-1);
4834 filename
[len
-1] = '\0';
4836 } else { /* r == 32 || r == 33 */
4837 strncpy(dir
, p
, sizeof(dir
)-1);
4838 dir
[sizeof(dir
)-1] = '\0';
4841 if(!strcmp(dir
, ps
->home_dir
)){
4852 rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
,
4853 SIZEOF_20KBUF
, filename
);
4854 strncpy(filename
, tmp_20k_buf
, len
);
4855 filename
[len
-1] = '\0';
4858 #endif /* _WINDOWS */
4864 removing_leading_and_trailing_white_space(filename
);
4867 if(!*def
){ /* Cancel */
4872 strncpy(filename
, def
, len
-1);
4873 filename
[len
-1] = '\0';
4876 #if defined(DOS) || defined(OS2)
4877 if(is_absolute_path(filename
)){
4878 fixpath(filename
, len
);
4881 if(filename
[0] == '~'){
4882 if(fnexpand(filename
, len
) == NULL
){
4883 char *p
= strindex(filename
, '/');
4886 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
4887 _("Error expanding file name: \"%s\" unknown user"),
4894 if(is_absolute_path(filename
)){
4895 strncpy(full_filename
, filename
, len
-1);
4896 full_filename
[len
-1] = '\0';
4900 build_path(full_filename
, (char *)getcwd(dir
,sizeof(dir
)),
4902 else if(dir
[0] == '~' && !dir
[1])
4903 build_path(full_filename
, ps
->home_dir
, filename
, len
);
4905 build_path(full_filename
, dir
, filename
, len
);
4908 if((ill
= filter_filename(full_filename
, &fatal
,
4909 ps_global
->restricted
|| ps_global
->VAR_OPER_DIR
)) != NULL
){
4911 q_status_message1(SM_ORDER
| SM_DING
, 3, 3, "%s", ill
);
4915 /* BUG: we should beep when the key's pressed rather than bitch later */
4916 /* Warn and ask for confirmation. */
4917 snprintf(prompt_buf
, sizeof(prompt_buf
), "File name contains a '%s'. %s anyway",
4918 ill
, (flags
& GE_IS_EXPORT
) ? "Export" : "Save");
4919 prompt_buf
[sizeof(prompt_buf
)-1] = '\0';
4920 if(want_to(prompt_buf
, 'n', 0, NO_HELP
,
4921 ((flags
& GE_SEQ_SENSITIVE
) ? RB_SEQ_SENSITIVE
: 0)) != 'y')
4926 break; /* Must have got an OK file name */
4929 if(VAR_OPER_DIR
&& !in_dir(VAR_OPER_DIR
, full_filename
)){
4934 if(!can_access(full_filename
, ACCESS_EXISTS
)){
4936 static ESCKEY_S access_opts
[] = {
4937 /* TRANSLATORS: asking user if they want to overwrite (replace contents of)
4938 a file or append to the end of the file */
4939 {'o', 'o', "O", N_("Overwrite")},
4940 {'a', 'a', "A", N_("Append")},
4941 {-1, 0, NULL
, NULL
}};
4943 rbflags
= RB_NORM
| ((flags
& GE_SEQ_SENSITIVE
) ? RB_SEQ_SENSITIVE
: 0);
4945 if(flags
& GE_NO_APPEND
){
4946 r
= strlen(filename
);
4947 snprintf(prompt_buf
, sizeof(prompt_buf
),
4948 /* TRANSLATORS: asking user whether to overwrite a file or not,
4949 File <filename> already exists. Overwrite it ? */
4950 _("File \"%s%s\" already exists. Overwrite it "),
4951 (r
> 20) ? "..." : "",
4952 filename
+ ((r
> 20) ? r
- 20 : 0));
4953 prompt_buf
[sizeof(prompt_buf
)-1] = '\0';
4954 if(want_to(prompt_buf
, 'n', 'x', NO_HELP
, rbflags
) == 'y'){
4956 *rflags
|= GER_OVER
;
4958 if(our_unlink(full_filename
) < 0){
4959 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
4960 /* TRANSLATORS: Cannot remove old <filename>: <error text> */
4961 _("Cannot remove old %s: %s"),
4962 full_filename
, error_description(errno
));
4970 else if(!(flags
& GE_IS_IMPORT
)){
4971 r
= strlen(filename
);
4972 snprintf(prompt_buf
, sizeof(prompt_buf
),
4973 /* TRANSLATORS: File <filename> already exists. Overwrite or append to it ? */
4974 _("File \"%s%s\" already exists. Overwrite or append to it ? "),
4975 (r
> 20) ? "..." : "",
4976 filename
+ ((r
> 20) ? r
- 20 : 0));
4977 prompt_buf
[sizeof(prompt_buf
)-1] = '\0';
4978 switch(radio_buttons(prompt_buf
, -FOOTER_ROWS(ps_global
),
4979 access_opts
, 'a', 'x', NO_HELP
, rbflags
)){
4982 *rflags
|= GER_OVER
;
4984 if(our_truncate(full_filename
, (off_t
)0) < 0)
4985 /* trouble truncating, but we'll give it a try anyway */
4986 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
4987 /* TRANSLATORS: Warning: Cannot truncate old <filename>: <error text> */
4988 _("Warning: Cannot truncate old %s: %s"),
4989 full_filename
, error_description(errno
));
4994 *rflags
|= GER_APPEND
;
5007 if(history
&& ret
== 0){
5008 save_hist(*history
, full_filename
, 0, NULL
);
5009 strncpy(tmp
, full_filename
, MAXPATH
);
5010 tmp
[MAXPATH
] = '\0';
5011 if((fn
= strrchr(tmp
, C_FILESEP
)) != NULL
)
5016 save_hist(dir_hist
, tmp
, 0, NULL
);
5019 if(opts
&& opts
!= optsarg
)
5020 fs_give((void **) &opts
);
5026 /*----------------------------------------------------------------------
5027 parse the config'd upload/download command
5029 Args: cmd -- buffer to return command fit for shellin'
5032 fname -- file name to build into the command
5034 Returns: pointer to cmd_str buffer or NULL on real bad error
5036 NOTE: One SIDE EFFECT is that any defined "prefix" string in the
5037 cfg_str is written to standard out right before a successful
5038 return of this function. The call immediately following this
5039 function darn well better be the shell exec...
5042 build_updown_cmd(char *cmd
, size_t cmdlen
, char *prefix
, char *cfg_str
, char *fname
)
5045 int fname_found
= 0;
5047 if(prefix
&& *prefix
){
5048 /* loop thru replacing all occurrences of _FILE_ */
5049 p
= strncpy(cmd
, prefix
, cmdlen
);
5050 cmd
[cmdlen
-1] = '\0';
5051 while((p
= strstr(p
, "_FILE_")))
5052 rplstr(p
, cmdlen
-(p
-cmd
), 6, fname
);
5057 /* loop thru replacing all occurrences of _FILE_ */
5058 p
= strncpy(cmd
, cfg_str
, cmdlen
);
5059 cmd
[cmdlen
-1] = '\0';
5060 while((p
= strstr(p
, "_FILE_"))){
5061 rplstr(p
, cmdlen
-(p
-cmd
), 6, fname
);
5066 snprintf(cmd
+strlen(cmd
), cmdlen
-strlen(cmd
), " %s", fname
);
5068 cmd
[cmdlen
-1] = '\0';
5070 dprint((4, "\n - build_updown_cmd = \"%s\" -\n",
5076 /*----------------------------------------------------------------------
5077 Write a berzerk format message delimiter using the given putc function
5079 Args: e -- envelope of message to write
5080 pc -- function to use
5082 Returns: TRUE if we could write it, FALSE if there was a problem
5084 NOTE: follows delimiter with OS-dependent newline
5087 bezerk_delimiter(ENVELOPE
*env
, MESSAGECACHE
*mc
, gf_io_t pc
, int leading_newline
)
5093 /* write "[\n]From mailbox[@host] " */
5094 if(!((leading_newline
? gf_puts(NEWLINE
, pc
) : 1)
5095 && gf_puts("From ", pc
)
5096 && gf_puts((env
&& env
->from
) ? env
->from
->mailbox
5097 : "the-concourse-on-high", pc
)
5098 && gf_puts((env
&& env
->from
&& env
->from
->host
) ? "@" : "", pc
)
5099 && gf_puts((env
&& env
->from
&& env
->from
->host
) ? env
->from
->host
5105 when
= mail_longdate(mc
);
5106 else if(env
&& env
->date
&& env
->date
[0]
5107 && mail_parse_date(&telt
,env
->date
))
5108 when
= mail_longdate(&telt
);
5114 while(p
&& *p
&& *p
!= '\n') /* write date */
5118 if(!gf_puts(NEWLINE
, pc
)) /* write terminating newline */
5125 /*----------------------------------------------------------------------
5126 Execute command to jump to a given message number
5128 Args: qline -- Line to ask question on
5130 Result: returns true if the use selected a new message, false otherwise
5134 jump_to(MSGNO_S
*msgmap
, int qline
, UCS first_num
, SCROLL_S
*sparms
, CmdWhere in_index
)
5136 char jump_num_string
[80], *j
, prompt
[70];
5139 static ESCKEY_S jump_to_key
[] = { {0, 0, NULL
, NULL
},
5140 /* TRANSLATORS: go to First Message */
5141 {ctrl('Y'), 10, "^Y", N_("First Msg")},
5142 {ctrl('V'), 11, "^V", N_("Last Msg")},
5143 {-1, 0, NULL
, NULL
} };
5145 dprint((4, "\n - jump_to -\n"));
5148 if(sparms
&& sparms
->jump_is_debug
)
5149 return(get_level(qline
, first_num
, sparms
));
5152 if(!any_messages(msgmap
, NULL
, "to Jump to"))
5155 if(first_num
&& first_num
< 0x80 && isdigit((unsigned char) first_num
)){
5156 jump_num_string
[0] = first_num
;
5157 jump_num_string
[1] = '\0';
5160 jump_num_string
[0] = '\0';
5162 if(mn_total_cur(msgmap
) > 1L){
5163 snprintf(prompt
, sizeof(prompt
), "Unselect %s msgs in favor of number to be entered",
5164 comatose(mn_total_cur(msgmap
)));
5165 prompt
[sizeof(prompt
)-1] = '\0';
5166 if((rc
= want_to(prompt
, 'n', 0, NO_HELP
, WT_NORM
)) == 'n')
5170 snprintf(prompt
, sizeof(prompt
), "%s number to jump to : ", in_index
== ThrdIndx
5173 prompt
[sizeof(prompt
)-1] = '\0';
5177 int flags
= OE_APPEND_CURRENT
;
5179 rc
= optionally_enter(jump_num_string
, qline
, 0,
5180 sizeof(jump_num_string
), prompt
,
5181 jump_to_key
, help
, &flags
);
5183 help
= help
== NO_HELP
5184 ? (in_index
== ThrdIndx
? h_oe_jump_thd
: h_oe_jump
)
5188 else if(rc
== 10 || rc
== 11){
5192 closest
= closest_jump_target(rc
== 10 ? 1L
5193 : ((in_index
== ThrdIndx
)
5194 ? msgmap
->max_thrdno
5195 : mn_get_total(msgmap
)),
5196 ps_global
->mail_stream
,
5198 in_index
, warning
, sizeof(warning
));
5199 /* ignore warning */
5204 * If we take out the *jump_num_string nonempty test in this if
5205 * then the closest_jump_target routine will offer a jump to the
5206 * last message. However, it is slow because you have to wait for
5207 * the status message and it is annoying for people who hit J command
5208 * by mistake and just want to hit return to do nothing, like has
5209 * always worked. So the test is there for now. Hubert 2002-08-19
5211 * Jumping to first/last message is now possible through ^Y/^V
5212 * commands above. jpf 2002-08-21
5213 * (and through "end" hubert 2006-07-07)
5215 if(rc
== 0 && *jump_num_string
!= '\0'){
5216 removing_leading_and_trailing_white_space(jump_num_string
);
5217 for(j
=jump_num_string
; isdigit((unsigned char)*j
) || *j
=='-'; j
++)
5221 if(!strucmp("end", j
))
5222 return((in_index
== ThrdIndx
) ? msgmap
->max_thrdno
: mn_get_total(msgmap
));
5224 q_status_message(SM_ORDER
| SM_DING
, 2, 2,
5225 _("Invalid number entered. Use only digits 0-9"));
5226 jump_num_string
[0] = '\0';
5230 long closest
, jump_num
;
5232 if(*jump_num_string
)
5233 jump_num
= atol(jump_num_string
);
5238 closest
= closest_jump_target(jump_num
, ps_global
->mail_stream
,
5240 *jump_num_string
? 0 : 1,
5241 in_index
, warning
, sizeof(warning
));
5243 q_status_message(SM_ORDER
| SM_DING
, 2, 2, warning
);
5245 if(closest
== jump_num
)
5249 jump_num_string
[0] = '\0';
5251 strncpy(jump_num_string
, long2string(closest
),
5252 sizeof(jump_num_string
));
5267 * cmd_delete_action - handle msgno advance and such after single message deletion
5270 cmd_delete_action(struct pine
*state
, MSGNO_S
*msgmap
, CmdWhere in_index
)
5276 msgno
= mn_get_cur(msgmap
);
5277 advance_cur_after_delete(state
, state
->mail_stream
, msgmap
, in_index
);
5279 if(IS_NEWS(state
->mail_stream
)
5280 || ((state
->context_current
->use
& CNTXT_INCMNG
)
5281 && context_isambig(state
->cur_folder
))){
5283 opts
= (NSF_TRUST_FLAGS
| NSF_SKIP_CHID
);
5284 if(in_index
== View
)
5285 opts
&= ~NSF_SKIP_CHID
;
5287 (void)next_sorted_flagged(F_UNDEL
|F_UNSEEN
, state
->mail_stream
, msgno
, &opts
);
5288 if(!(opts
& NSF_FLAG_MATCH
)){
5289 char nextfolder
[MAXPATH
];
5291 strncpy(nextfolder
, state
->cur_folder
, sizeof(nextfolder
));
5292 nextfolder
[sizeof(nextfolder
)-1] = '\0';
5293 rv
= next_folder(NULL
, nextfolder
, sizeof(nextfolder
), nextfolder
,
5294 state
->context_current
, NULL
, NULL
)
5295 ? ". Press TAB for next folder."
5296 : ". No more folders to TAB to.";
5305 * cmd_delete_index - fixup msgmap or whatever after cmd_delete has done it's thing
5308 cmd_delete_index(struct pine
*state
, MSGNO_S
*msgmap
)
5310 return(cmd_delete_action(state
, msgmap
,MsgIndx
));
5314 * cmd_delete_view - fixup msgmap or whatever after cmd_delete has done it's thing
5317 cmd_delete_view(struct pine
*state
, MSGNO_S
*msgmap
)
5319 return(cmd_delete_action(state
, msgmap
, View
));
5324 advance_cur_after_delete(struct pine
*state
, MAILSTREAM
*stream
, MSGNO_S
*msgmap
, CmdWhere in_index
)
5326 long new_msgno
, msgno
;
5329 new_msgno
= msgno
= mn_get_cur(msgmap
);
5330 opts
= NSF_TRUST_FLAGS
;
5332 if(F_ON(F_DEL_SKIPS_DEL
, state
)){
5334 if(THREADING() && sp_viewing_a_thread(stream
))
5335 opts
|= NSF_SKIP_CHID
;
5337 new_msgno
= next_sorted_flagged(F_UNDEL
, stream
, msgno
, &opts
);
5340 mn_inc_cur(stream
, msgmap
,
5341 (in_index
== View
&& THREADING()
5342 && sp_viewing_a_thread(stream
))
5344 : (in_index
== View
)
5345 ? MH_ANYTHD
: MH_NONE
);
5346 new_msgno
= mn_get_cur(msgmap
);
5347 if(new_msgno
!= msgno
)
5348 opts
|= NSF_FLAG_MATCH
;
5352 * Viewing_a_thread is the complicated case because we want to ignore
5353 * other threads at first and then look in other threads if we have to.
5354 * By ignoring other threads we also ignore collapsed partial threads
5355 * in our own thread.
5357 if(THREADING() && sp_viewing_a_thread(stream
) && !(opts
& NSF_FLAG_MATCH
)){
5358 long rawno
, orig_thrdno
;
5359 PINETHRD_S
*thrd
, *topthrd
= NULL
;
5361 rawno
= mn_m2raw(msgmap
, msgno
);
5362 thrd
= fetch_thread(stream
, rawno
);
5363 if(thrd
&& thrd
->top
)
5364 topthrd
= fetch_thread(stream
, thrd
->top
);
5366 orig_thrdno
= topthrd
? topthrd
->thrdno
: -1L;
5368 opts
= NSF_TRUST_FLAGS
;
5369 new_msgno
= next_sorted_flagged(F_UNDEL
, stream
, msgno
, &opts
);
5372 * If we got a match, new_msgno may be a message in
5373 * a different thread from the one we are viewing, or it could be
5374 * in a collapsed part of this thread.
5376 if(opts
& NSF_FLAG_MATCH
){
5381 thrd
= fetch_thread(stream
, mn_m2raw(msgmap
,new_msgno
));
5382 if(thrd
&& thrd
->top
)
5383 topthrd
= fetch_thread(stream
, thrd
->top
);
5386 * If this match is in the same thread we're already in
5387 * then we're done, else we have to ask the user and maybe
5390 if(!(orig_thrdno
> 0L && topthrd
5391 && topthrd
->thrdno
== orig_thrdno
)){
5393 if(F_OFF(F_AUTO_OPEN_NEXT_UNREAD
, state
)){
5394 if(in_index
== View
)
5395 snprintf(pmt
, sizeof(pmt
),
5396 "View message in thread number %.10s",
5397 topthrd
? comatose(topthrd
->thrdno
) : "?");
5399 snprintf(pmt
, sizeof(pmt
), "View thread number %.10s",
5400 topthrd
? comatose(topthrd
->thrdno
) : "?");
5402 ret
= want_to(pmt
, 'y', 'x', NO_HELP
, WT_NORM
);
5408 unview_thread(state
, stream
, msgmap
);
5409 mn_set_cur(msgmap
, new_msgno
);
5411 && (count_lflags_in_thread(stream
, topthrd
, msgmap
,
5413 && view_thread(state
, stream
, msgmap
, 1)){
5414 if(current_index_state
)
5415 msgmap
->top_after_thrd
= current_index_state
->msg_at_top
;
5417 state
->view_skipped_index
= 1;
5418 state
->next_screen
= mail_view_screen
;
5421 view_thread(state
, stream
, msgmap
, 1);
5422 if(current_index_state
)
5423 msgmap
->top_after_thrd
= current_index_state
->msg_at_top
;
5425 state
->next_screen
= SCREEN_FUN_NULL
;
5429 new_msgno
= msgno
; /* stick with original */
5434 mn_set_cur(msgmap
, new_msgno
);
5435 if(in_index
!= View
)
5436 adjust_cur_to_visible(stream
, msgmap
);
5442 get_level(int qline
, UCS first_num
, SCROLL_S
*sparms
)
5444 char debug_num_string
[80], *j
, prompt
[70];
5449 if(first_num
&& first_num
< 0x80 && isdigit((unsigned char)first_num
)){
5450 debug_num_string
[0] = first_num
;
5451 debug_num_string
[1] = '\0';
5452 debug_num
= atol(debug_num_string
);
5453 *(int *)(sparms
->proc
.data
.p
) = debug_num
;
5454 q_status_message1(SM_ORDER
, 0, 3, "Show debug <= level %s",
5455 comatose(debug_num
));
5459 debug_num_string
[0] = '\0';
5461 snprintf(prompt
, sizeof(prompt
), "Show debug <= this level (0-%d) : ", MAX(debug
, 9));
5462 prompt
[sizeof(prompt
)-1] = '\0';
5466 int flags
= OE_APPEND_CURRENT
;
5468 rc
= optionally_enter(debug_num_string
, qline
, 0,
5469 sizeof(debug_num_string
), prompt
,
5470 NULL
, help
, &flags
);
5472 help
= help
== NO_HELP
? h_oe_debuglevel
: NO_HELP
;
5477 removing_leading_and_trailing_white_space(debug_num_string
);
5478 for(j
=debug_num_string
; isdigit((unsigned char)*j
); j
++)
5482 q_status_message(SM_ORDER
| SM_DING
, 2, 2,
5483 _("Invalid number entered. Use only digits 0-9"));
5484 debug_num_string
[0] = '\0';
5487 debug_num
= atol(debug_num_string
);
5489 q_status_message(SM_ORDER
| SM_DING
, 2, 2,
5490 _("Number should be >= 0"));
5491 else if(debug_num
> MAX(debug
,9))
5492 q_status_message1(SM_ORDER
| SM_DING
, 2, 2,
5493 _("Maximum is %s"), comatose(MAX(debug
,9)));
5495 *(int *)(sparms
->proc
.data
.p
) = debug_num
;
5496 q_status_message1(SM_ORDER
, 0, 3,
5497 "Show debug <= level %s",
5498 comatose(debug_num
));
5516 * Returns the message number closest to target that isn't hidden.
5517 * Make warning at least 100 chars.
5518 * A return of 0 means there is no message to jump to.
5521 closest_jump_target(long int target
, MAILSTREAM
*stream
, MSGNO_S
*msgmap
, int no_target
, CmdWhere in_index
, char *warning
, size_t warninglen
)
5523 long i
, start
, closest
= 0L;
5528 maxnum
= (in_index
== ThrdIndx
) ? msgmap
->max_thrdno
: mn_get_total(msgmap
);
5533 snprintf(warning
, warninglen
, "No %s number entered, jump to end? ",
5534 (in_index
== ThrdIndx
) ? "thread" : "message");
5535 warning
[warninglen
-1] = '\0';
5537 else if(target
< 1L)
5538 start
= 1L - target
;
5539 else if(target
> maxnum
)
5540 start
= target
- maxnum
;
5544 if(target
> 0L && target
<= maxnum
)
5545 if(in_index
== ThrdIndx
5546 || !msgline_hidden(stream
, msgmap
, target
, 0))
5549 for(i
= start
; target
+i
<= maxnum
|| target
-i
> 0L; i
++){
5551 if(target
+i
> 0L && target
+i
<= maxnum
&&
5552 (in_index
== ThrdIndx
5553 || !msgline_hidden(stream
, msgmap
, target
+i
, 0))){
5558 if(target
-i
> 0L && target
-i
<= maxnum
&&
5559 (in_index
== ThrdIndx
5560 || !msgline_hidden(stream
, msgmap
, target
-i
, 0))){
5566 strncpy(buf
, long2string(closest
), sizeof(buf
));
5567 buf
[sizeof(buf
)-1] = '\0';
5570 strncpy(warning
, "Nothing to jump to", warninglen
);
5571 else if(target
< 1L)
5572 snprintf(warning
, warninglen
, "%s number (%s) must be at least %s",
5573 (in_index
== ThrdIndx
) ? "Thread" : "Message",
5574 long2string(target
), buf
);
5575 else if(target
> maxnum
)
5576 snprintf(warning
, warninglen
, "%s number (%s) may be no more than %s",
5577 (in_index
== ThrdIndx
) ? "Thread" : "Message",
5578 long2string(target
), buf
);
5580 snprintf(warning
, warninglen
,
5581 "Message number (%s) is not in \"Zoomed Index\" - Closest is(%s)",
5582 long2string(target
), buf
);
5584 warning
[warninglen
-1] = '\0';
5590 /*----------------------------------------------------------------------
5591 Prompt for folder name to open, expand the name and return it
5593 Args: qline -- Screen line to prompt on
5594 allow_list -- if 1, allow ^T to bring up collection lister
5596 Result: returns the folder name or NULL
5597 pine structure mangled_footer flag is set
5598 may call the collection lister in which case mangled screen will be set
5600 This prompts the user for the folder to open, possibly calling up
5601 the collection lister if the user types ^T.
5602 ----------------------------------------------------------------------*/
5604 broach_folder(int qline
, int allow_list
, int *notrealinbox
, CONTEXT_S
**context
)
5607 static char newfolder
[MAILTMPLEN
];
5608 char expanded
[MAXPATH
+1],
5609 prompt
[MAX_SCREEN_COLS
+1],
5611 unsigned char *f1
, *f2
, *f3
;
5612 static HISTORY_S
*history
= NULL
;
5613 CONTEXT_S
*tc
, *tc2
;
5615 int rc
, r
, ku
= -1, n
, flags
, last_rc
= 0, inbox
, done
= 0;
5618 * the idea is to provide a clue for the context the file name
5619 * will be saved in (if a non-imap names is typed), and to
5620 * only show the previous if it was also in the same context
5627 (*notrealinbox
) = 1;
5629 init_hist(&history
, HISTSIZE
);
5631 tc
= broach_get_folder(context
? *context
: NULL
, &inbox
, NULL
);
5633 /* set up extra command option keys */
5635 ekey
[rc
].ch
= (allow_list
) ? ctrl('T') : 0 ;
5636 ekey
[rc
].rval
= (allow_list
) ? 2 : 0;
5637 ekey
[rc
].name
= (allow_list
) ? "^T" : "";
5638 ekey
[rc
++].label
= (allow_list
) ? N_("ToFldrs") : "";
5640 if(ps_global
->context_list
->next
){
5641 ekey
[rc
].ch
= ctrl('P');
5643 ekey
[rc
].name
= "^P";
5644 ekey
[rc
++].label
= N_("Prev Collection");
5646 ekey
[rc
].ch
= ctrl('N');
5648 ekey
[rc
].name
= "^N";
5649 ekey
[rc
++].label
= N_("Next Collection");
5652 ekey
[rc
].ch
= ctrl('W');
5654 ekey
[rc
].name
= "^W";
5655 ekey
[rc
++].label
= N_("INBOX");
5657 if(F_ON(F_ENABLE_TAB_COMPLETE
,ps_global
)){
5660 ekey
[rc
].name
= "TAB";
5661 ekey
[rc
++].label
= N_("Complete");
5664 if(F_ON(F_ENABLE_SUB_LISTS
, ps_global
)){
5665 ekey
[rc
].ch
= ctrl('X');
5667 ekey
[rc
].name
= "^X";
5668 ekey
[rc
++].label
= N_("ListMatches");
5671 if(ps_global
->context_list
->next
&& F_ON(F_DISABLE_SAVE_INPUT_HISTORY
, ps_global
)){
5672 ekey
[rc
].ch
= KEY_UP
;
5675 ekey
[rc
++].label
= "";
5677 ekey
[rc
].ch
= KEY_DOWN
;
5680 ekey
[rc
++].label
= "";
5682 else if(F_OFF(F_DISABLE_SAVE_INPUT_HISTORY
, ps_global
)){
5683 ekey
[rc
].ch
= KEY_UP
;
5687 ekey
[rc
++].label
= "";
5689 ekey
[rc
].ch
= KEY_DOWN
;
5692 ekey
[rc
++].label
= "";
5699 * Figure out next default value for this context. The idea
5700 * is that in each context the last folder opened is cached.
5701 * It's up to pick it out and display it. This is fine
5702 * and dandy if we've currently got the inbox open, BUT
5703 * if not, make the inbox the default the first time thru.
5706 last_folder
= ps_global
->inbox_name
;
5707 inbox
= 1; /* pretend we're in inbox from here on out */
5710 last_folder
= (ps_global
->last_unambig_folder
[0])
5711 ? ps_global
->last_unambig_folder
5712 : ((tc
->last_folder
[0]) ? tc
->last_folder
: NULL
);
5714 if(last_folder
){ /* MAXPATH + 1 = sizeof(expanded) */
5715 unsigned char *fname
= folder_name_decoded((unsigned char *)last_folder
);
5716 snprintf(expanded
, sizeof(expanded
), " [%.*s]", MAXPATH
+1-5,
5717 fname
? (char *) fname
: last_folder
);
5718 if(fname
) fs_give((void **)&fname
);
5723 expanded
[sizeof(expanded
)-1] = '\0';
5725 /* only show collection number if more than one available */
5726 if(ps_global
->context_list
->next
) /* MAX_SCREEN_COLS+1 == sizeof(prompt) */
5727 snprintf(prompt
, sizeof(prompt
), "GOTO %s in <%s> %.*s%s: ",
5728 NEWS_TEST(tc
) ? "news group" : "folder",
5729 tc
->nickname
, MAX_SCREEN_COLS
+1-50, expanded
,
5730 *expanded
? " " : "");
5731 else /* MAX_SCREEN_COLS+1 == sizeof(prompt) */
5732 snprintf(prompt
, sizeof(prompt
), "GOTO folder %.*s%s: ", MAX_SCREEN_COLS
+1-20, expanded
,
5733 *expanded
? " " : "");
5735 prompt
[sizeof(prompt
)-1] = '\0';
5737 if(utf8_width(prompt
) > MAXPROMPT
){
5738 if(ps_global
->context_list
->next
) /* MAX_SCREEN_COLS+1 == sizeof(prompt) */
5739 snprintf(prompt
, sizeof(prompt
), "GOTO <%s> %.*s%s: ",
5740 tc
->nickname
, MAX_SCREEN_COLS
+1-50, expanded
,
5741 *expanded
? " " : "");
5742 else /* MAX_SCREEN_COLS+1 == sizeof(prompt) */
5743 snprintf(prompt
, sizeof(prompt
), "GOTO %.*s%s: ", MAX_SCREEN_COLS
+1-20, expanded
,
5744 *expanded
? " " : "");
5746 prompt
[sizeof(prompt
)-1] = '\0';
5748 if(utf8_width(prompt
) > MAXPROMPT
){
5749 if(ps_global
->context_list
->next
) /* MAX_SCREEN_COLS+1 == sizeof(prompt) */
5750 snprintf(prompt
, sizeof(prompt
), "<%s> %.*s%s: ",
5751 tc
->nickname
, MAX_SCREEN_COLS
+1-50, expanded
,
5752 *expanded
? " " : "");
5753 else /* MAX_SCREEN_COLS+1 == sizeof(prompt) */
5754 snprintf(prompt
, sizeof(prompt
), "%.*s%s: ", MAX_SCREEN_COLS
+1-20, expanded
,
5755 *expanded
? " " : "");
5757 prompt
[sizeof(prompt
)-1] = '\0';
5762 if(items_in_hist(history
) > 1){
5763 ekey
[ku
].name
= HISTORY_UP_KEYNAME
;
5764 ekey
[ku
].label
= HISTORY_KEYLABEL
;
5765 ekey
[ku
+1].name
= HISTORY_DOWN_KEYNAME
;
5766 ekey
[ku
+1].label
= HISTORY_KEYLABEL
;
5770 ekey
[ku
].label
= "";
5771 ekey
[ku
+1].name
= "";
5772 ekey
[ku
+1].label
= "";
5776 /* is there any other way to do this? The point is that we
5777 * are trying to hide mutf7 from the user, and use the utf8
5778 * equivalent. So we create a variable f to take place of
5779 * newfolder, including content and size. f2 is copy of f1
5780 * that has to freed. Sigh!
5782 f3
= (unsigned char *) cpystr(newfolder
);
5783 f1
= fs_get(sizeof(newfolder
));
5784 f2
= folder_name_decoded(f3
);
5785 if(f3
) fs_give((void **)&f3
);
5786 strncpy((char *)f1
, (char *)f2
, sizeof(newfolder
));
5787 f1
[sizeof(newfolder
)-1] = '\0';
5788 if(f2
) fs_give((void **)&f2
);
5790 flags
= OE_APPEND_CURRENT
;
5791 rc
= optionally_enter((char *) f1
, qline
, 0, sizeof(newfolder
),
5792 (char *) prompt
, ekey
, help
, &flags
);
5794 f2
= folder_name_encoded(f1
);
5795 strncpy(newfolder
, (char *)f2
, sizeof(newfolder
));
5796 if(f1
) fs_give((void **)&f1
);
5797 if(f2
) fs_give((void **)&f2
);
5799 ps_global
->mangled_footer
= 1;
5802 case -1 : /* o_e says error! */
5803 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
5804 _("Error reading folder name"));
5807 case 0 : /* o_e says normal entry */
5808 removing_trailing_white_space(newfolder
);
5809 removing_leading_white_space(newfolder
);
5812 char *name
, *fullname
= NULL
;
5813 int exists
, breakout
= 0;
5815 save_hist(history
, newfolder
, 0, tc
);
5817 if(!(name
= folder_is_nick(newfolder
, FOLDERS(tc
),
5821 if(update_folder_spec(expanded
, sizeof(expanded
), name
)){
5822 strncpy(name
= newfolder
, expanded
, sizeof(newfolder
));
5823 newfolder
[sizeof(newfolder
)-1] = '\0';
5826 exists
= folder_name_exists(tc
, name
, &fullname
);
5829 strncpy(name
= newfolder
, fullname
, sizeof(newfolder
));
5830 newfolder
[sizeof(newfolder
)-1] = '\0';
5831 fs_give((void **) &fullname
);
5836 * if we know the things a folder, open it.
5837 * else if we know its a directory, visit it.
5838 * else we're not sure (it either doesn't really
5839 * exist or its unLISTable) so try opening it anyway
5841 if(exists
& FEX_ISFILE
){
5845 else if((exists
& FEX_ISDIR
)){
5847 CONTEXT_S
*fake_context
;
5848 char tmp
[MAILTMPLEN
];
5851 strncpy(tmp
, name
, sizeof(tmp
));
5852 tmp
[sizeof(tmp
)-2-1] = '\0';
5853 if(tmp
[(l
= strlen(tmp
)) - 1] != tc
->dir
->delim
){
5854 if(l
< sizeof(tmp
)){
5855 tmp
[l
] = tc
->dir
->delim
;
5856 strncpy(&tmp
[l
+1], "[]", sizeof(tmp
)-(l
+1));
5860 strncat(tmp
, "[]", sizeof(tmp
)-strlen(tmp
)-1);
5862 tmp
[sizeof(tmp
)-1] = '\0';
5864 fake_context
= new_context(tmp
, 0);
5865 newfolder
[0] = '\0';
5866 done
= display_folder_list(&fake_context
, newfolder
,
5867 1, folders_for_goto
);
5868 free_context(&fake_context
);
5871 else if(!(tc
->use
& CNTXT_INCMNG
)){
5872 done
= display_folder_list(&tc
, newfolder
,
5873 1, folders_for_goto
);
5877 else if((exists
& FEX_ERROR
)){
5878 q_status_message1(SM_ORDER
, 0, 3,
5879 _("Problem accessing folder \"%s\""),
5888 if(exists
== FEX_ERROR
)
5889 q_status_message1(SM_ORDER
, 0, 3,
5890 _("Problem accessing folder \"%s\""),
5892 else if(tc
->use
& CNTXT_INCMNG
)
5893 q_status_message1(SM_ORDER
, 0, 3,
5894 _("Can't find Incoming Folder: %s"),
5896 else if(context_isambig(newfolder
))
5897 q_status_message2(SM_ORDER
, 0, 3,
5898 _("Can't find folder \"%s\" in %s"),
5899 newfolder
, (void *) tc
->nickname
);
5901 q_status_message1(SM_ORDER
, 0, 3,
5902 _("Can't find folder \"%s\""),
5907 else if(last_folder
){
5908 if(ps_global
->goto_default_rule
== GOTO_FIRST_CLCTN_DEF_INBOX
5909 && !strucmp(last_folder
, ps_global
->inbox_name
)
5910 && tc
== ((ps_global
->context_list
->use
& CNTXT_INCMNG
)
5911 ? ps_global
->context_list
->next
: ps_global
->context_list
)){
5913 (*notrealinbox
) = 0;
5915 tc
= ps_global
->context_list
;
5918 strncpy(newfolder
, last_folder
, sizeof(newfolder
));
5919 newfolder
[sizeof(newfolder
)-1] = '\0';
5920 save_hist(history
, newfolder
, 0, tc
);
5924 /* fall thru like they cancelled */
5926 case 1 : /* o_e says user cancel */
5927 cmd_cancelled("Open folder");
5930 case 2 : /* o_e says user wants list */
5931 r
= display_folder_list(&tc
, newfolder
, 0, folders_for_goto
);
5937 case 3 : /* o_e says user wants help */
5938 help
= help
== NO_HELP
? h_oe_broach
: NO_HELP
;
5941 case 4 : /* redraw */
5944 case 10 : /* Previous collection */
5945 tc2
= ps_global
->context_list
;
5946 while(tc2
->next
&& tc2
->next
!= tc
)
5952 case 11 : /* Next collection */
5953 tc
= (tc
->next
) ? tc
->next
: ps_global
->context_list
;
5956 case 12 : /* file name completion */
5957 if(!folder_complete(tc
, newfolder
, sizeof(newfolder
), &n
)){
5958 if(n
&& last_rc
== 12 && !(flags
& OE_USER_MODIFIED
)){
5959 r
= display_folder_list(&tc
, newfolder
, 1,folders_for_goto
);
5961 done
++; /* bingo! */
5963 rc
= 0; /* burn last_rc */
5971 case 14 : /* file name completion */
5972 r
= display_folder_list(&tc
, newfolder
, 2, folders_for_goto
);
5974 done
++; /* bingo! */
5976 rc
= 0; /* burn last_rc */
5980 case 17 : /* GoTo INBOX */
5982 strncpy(newfolder
, ps_global
->inbox_name
, sizeof(newfolder
)-1);
5983 newfolder
[sizeof(newfolder
)-1] = '\0';
5985 (*notrealinbox
) = 0;
5987 tc
= ps_global
->context_list
;
5988 save_hist(history
, newfolder
, 0, tc
);
5993 if((p
= get_prev_hist(history
, newfolder
, 0, tc
)) != NULL
){
5994 strncpy(newfolder
, p
, sizeof(newfolder
));
5995 newfolder
[sizeof(newfolder
)-1] = '\0';
5996 if(history
->hist
[history
->curindex
])
5997 tc
= history
->hist
[history
->curindex
]->cntxt
;
6005 if((p
= get_next_hist(history
, newfolder
, 0, tc
)) != NULL
){
6006 strncpy(newfolder
, p
, sizeof(newfolder
));
6007 newfolder
[sizeof(newfolder
)-1] = '\0';
6008 if(history
->hist
[history
->curindex
])
6009 tc
= history
->hist
[history
->curindex
]->cntxt
;
6017 alpine_panic("Unhandled case");
6024 dprint((2, "broach folder, name entered \"%s\"\n",
6025 newfolder
? newfolder
: "?"));
6027 /*-- Just check that we can expand this. It gets done for real later --*/
6028 strncpy(expanded
, newfolder
, sizeof(expanded
));
6029 expanded
[sizeof(expanded
)-1] = '\0';
6031 if(!expand_foldername(expanded
, sizeof(expanded
))) {
6032 dprint((1, "Error: Failed on expansion of filename %s (do_broach)\n",
6033 expanded
? expanded
: "?"));
6042 /*----------------------------------------------------------------------
6043 Check to see if user wants to reopen dead stream.
6048 Result: 1 if the folder was successfully updatedn
6053 ask_mailbox_reopen(struct pine
*ps
, int *reopenp
)
6055 if(((ps
->mail_stream
->dtb
6056 && ((ps
->mail_stream
->dtb
->flags
& DR_NONEWMAIL
)
6057 || (ps
->mail_stream
->rdonly
6058 && ps
->mail_stream
->dtb
->flags
& DR_NONEWMAILRONLY
)))
6059 && (ps
->reopen_rule
== REOPEN_ASK_ASK_Y
6060 || ps
->reopen_rule
== REOPEN_ASK_ASK_N
6061 || ps
->reopen_rule
== REOPEN_ASK_NO_Y
6062 || ps
->reopen_rule
== REOPEN_ASK_NO_N
))
6063 || ((ps
->mail_stream
->dtb
6064 && ps
->mail_stream
->rdonly
6065 && !(ps
->mail_stream
->dtb
->flags
& DR_LOCAL
))
6066 && (ps
->reopen_rule
== REOPEN_YES_ASK_Y
6067 || ps
->reopen_rule
== REOPEN_YES_ASK_N
6068 || ps
->reopen_rule
== REOPEN_ASK_ASK_Y
6069 || ps
->reopen_rule
== REOPEN_ASK_ASK_N
))){
6072 switch(ps
->reopen_rule
){
6073 case REOPEN_YES_ASK_Y
:
6074 case REOPEN_ASK_ASK_Y
:
6075 case REOPEN_ASK_NO_Y
:
6084 switch(want_to("Re-open folder to check for new messages", deefault
,
6085 'x', h_reopen_folder
, WT_NORM
)){
6100 /*----------------------------------------------------------------------
6101 Check to see if user input is in form of old c-client mailbox speck
6106 Result: 1 if the folder was successfully updatedn
6111 update_folder_spec(char *new, size_t newlen
, char *old
)
6117 if(*(p
= old
) == '*') /* old form? */
6120 if(*old
== '{') /* copy host spec */
6122 switch(*new = *old
++){
6127 if(!struncmp(old
, "nntp", 4))
6135 while(*new++ != '}' && (new-orignew
) < newlen
-1);
6137 if((*p
== '*' && *old
) || ((*old
== '*') ? *++old
: 0)){
6139 * OK, some heuristics here. If it looks like a newsgroup
6140 * then we plunk it into the #news namespace else we
6141 * assume that they're trying to get at a #public folder...
6144 *p
&& (isalnum((unsigned char) *p
) || strindex(".-", *p
));
6148 sstrncpy(&new, (*p
&& !nntp
) ? "#public/" : "#news.", newlen
-(new-orignew
));
6149 strncpy(new, old
, newlen
-(new-orignew
));
6153 orignew
[newlen
-1] = '\0';
6159 /*----------------------------------------------------------------------
6160 Open the requested folder in the requested context
6162 Args: state -- usual pine state struct
6163 newfolder -- folder to open
6164 new_context -- folder context might live in
6165 stream -- candidate for recycling
6167 Result: New folder open or not (if error), and we're set to
6168 enter the index screen.
6171 visit_folder(struct pine
*state
, char *newfolder
, CONTEXT_S
*new_context
,
6172 MAILSTREAM
*stream
, long unsigned int flags
)
6174 dprint((9, "visit_folder(%s, %s)\n",
6175 newfolder
? newfolder
: "?",
6176 (new_context
&& new_context
->context
)
6177 ? new_context
->context
: "(NULL)"));
6179 if(ps_global
&& ps_global
->ttyo
){
6180 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
6181 ps_global
->mangled_footer
= 1;
6184 if(do_broach_folder(newfolder
, new_context
, stream
? &stream
: NULL
,
6186 || !sp_flagged(state
->mail_stream
, SP_LOCKED
))
6187 state
->next_screen
= mail_index_screen
;
6189 state
->next_screen
= folder_screen
;
6193 /*----------------------------------------------------------------------
6194 Move read messages from folder if listed in archive
6200 read_msg_prompt(long int n
, char *f
)
6202 char buf
[MAX_SCREEN_COLS
+1];
6204 snprintf(buf
, sizeof(buf
), "Save the %ld read message%s in \"%s\"", n
, plural(n
), f
);
6205 buf
[sizeof(buf
)-1] = '\0';
6206 return(want_to(buf
, 'y', 0, NO_HELP
, WT_NORM
) == 'y');
6210 /*----------------------------------------------------------------------
6211 Print current message[s] or folder index
6213 Args: state -- pointer to struct holding a bunch of pine state
6214 msgmap -- table mapping msg nums to c-client sequence nums
6215 aopt -- aggregate options
6216 in_index -- boolean indicating we're called from Index Screen
6218 Filters the original header and sends stuff to printer
6221 cmd_print(struct pine
*state
, MSGNO_S
*msgmap
, int aopt
, CmdWhere in_index
)
6224 long i
, msgs
, rawno
;
6225 int next
= 0, do_index
= 0, rv
= 0;
6230 if(MCMD_ISAGG(aopt
) && !pseudo_selected(state
->mail_stream
, msgmap
))
6233 msgs
= mn_total_cur(msgmap
);
6235 if((in_index
!= View
) && F_ON(F_PRINT_INDEX
, state
)){
6238 static ESCKEY_S prt_opts
[] = {
6239 {'i', 'i', "I", N_("Index")},
6240 {'m', 'm', "M", NULL
},
6241 {-1, 0, NULL
, NULL
}};
6243 if(in_index
== ThrdIndx
){
6244 /* TRANSLATORS: This is a question, Print Index ? */
6245 if(want_to(_("Print Index"), 'y', 'x', NO_HELP
, WT_NORM
) == 'y')
6251 snprintf(m
, sizeof(m
), "Message%s", (msgs
>1L) ? "s" : "");
6252 m
[sizeof(m
)-1] = '\0';
6253 prt_opts
[1].label
= m
;
6254 snprintf(prompt
, sizeof(prompt
), "Print %sFolder Index or %s %s? ",
6255 (aopt
& MCMD_AGG_2
) ? "thread " : MCMD_ISAGG(aopt
) ? "selected " : "",
6256 (aopt
& MCMD_AGG_2
) ? "thread" : MCMD_ISAGG(aopt
) ? "selected" : "current", m
);
6257 prompt
[sizeof(prompt
)-1] = '\0';
6259 ans
= radio_buttons(prompt
, -FOOTER_ROWS(state
), prt_opts
, 'm', 'x',
6260 NO_HELP
, RB_NORM
|RB_SEQ_SENSITIVE
);
6265 cmd_cancelled("Print");
6266 if(MCMD_ISAGG(aopt
))
6267 restore_selected(msgmap
);
6282 snprintf(prompt
, sizeof(prompt
), "%sFolder Index",
6283 (aopt
& MCMD_AGG_2
) ? "Thread " : MCMD_ISAGG(aopt
) ? "Selected " : "");
6285 snprintf(prompt
, sizeof(prompt
), "%s messages", long2string(msgs
));
6287 snprintf(prompt
, sizeof(prompt
), "Message %s", long2string(mn_get_cur(msgmap
)));
6289 prompt
[sizeof(prompt
)-1] = '\0';
6291 if(open_printer(prompt
) < 0){
6292 if(MCMD_ISAGG(aopt
))
6293 restore_selected(msgmap
);
6301 tc
= format_titlebar();
6303 /* Print titlebar... */
6304 print_text1("%s\n\n", tc
? tc
->titlebar_line
: "");
6305 /* then all the index members... */
6306 if(!print_index(state
, msgmap
, MCMD_ISAGG(aopt
)))
6307 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
6308 _("Error printing folder index"));
6314 for(i
= mn_first_cur(msgmap
); i
> 0L; i
= mn_next_cur(msgmap
), next
++){
6315 if(next
&& F_ON(F_AGG_PRINT_FF
, state
))
6316 if(!print_char(FORMFEED
)){
6321 if(!(state
->mail_stream
6322 && (rawno
= mn_m2raw(msgmap
, i
)) > 0L
6323 && rawno
<= state
->mail_stream
->nmsgs
6324 && (mc
= mail_elt(state
->mail_stream
, rawno
))
6328 if(!(e
=pine_mail_fetchstructure(state
->mail_stream
,
6331 || (F_ON(F_FROM_DELIM_IN_PRINT
, ps_global
)
6332 && !bezerk_delimiter(e
, mc
, print_char
, next
))
6333 || !format_message(mn_m2raw(msgmap
, mn_get_cur(msgmap
)),
6334 e
, b
, NULL
, FM_NEW_MESS
| FM_NOINDENT
,
6336 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
6337 _("Error printing message"));
6346 if(MCMD_ISAGG(aopt
))
6347 restore_selected(msgmap
);
6353 /*----------------------------------------------------------------------
6356 Args: state -- various pine state bits
6357 msgmap -- Message number mapping table
6358 aopt -- option flags
6360 Filters the original header and sends stuff to specified command
6363 cmd_pipe(struct pine
*state
, MSGNO_S
*msgmap
, int aopt
)
6369 char *resultfilename
= NULL
, prompt
[80], *p
;
6370 int done
= 0, rv
= 0;
6372 int fourlabel
= -1, j
= 0, next
= 0, ku
;
6373 int pipe_rv
; /* rv of proc to separate from close_system_pipe rv */
6375 unsigned flagsforhist
= 1; /* raw=8/delimit=4/newpipe=2/capture=1 */
6376 static HISTORY_S
*history
= NULL
;
6377 int capture
= 1, raw
= 0, delimit
= 0, newpipe
= 0;
6378 char pipe_command
[MAXPATH
];
6379 ESCKEY_S pipe_opt
[8];
6381 if(ps_global
->restricted
){
6382 q_status_message(SM_ORDER
| SM_DING
, 0, 4,
6383 "Alpine demo can't pipe messages");
6386 else if(!any_messages(msgmap
, NULL
, "to Pipe"))
6389 pipe_command
[0] = '\0';
6390 init_hist(&history
, HISTSIZE
);
6391 flagsforhist
= (raw
? 0x8 : 0) +
6392 (delimit
? 0x4 : 0) +
6393 (newpipe
? 0x2 : 0) +
6394 (capture
? 0x1 : 0);
6395 if((p
= get_prev_hist(history
, "", flagsforhist
, NULL
)) != NULL
){
6396 strncpy(pipe_command
, p
, sizeof(pipe_command
));
6397 pipe_command
[sizeof(pipe_command
)-1] = '\0';
6398 if(history
->hist
[history
->curindex
]){
6399 flagsforhist
= history
->hist
[history
->curindex
]->flags
;
6400 raw
= (flagsforhist
& 0x8) ? 1 : 0;
6401 delimit
= (flagsforhist
& 0x4) ? 1 : 0;
6402 newpipe
= (flagsforhist
& 0x2) ? 1 : 0;
6403 capture
= (flagsforhist
& 0x1) ? 1 : 0;
6408 pipe_opt
[j
].rval
= 0;
6409 pipe_opt
[j
].name
= "";
6410 pipe_opt
[j
++].label
= "";
6412 pipe_opt
[j
].ch
= ctrl('W');
6413 pipe_opt
[j
].rval
= 10;
6414 pipe_opt
[j
].name
= "^W";
6415 pipe_opt
[j
++].label
= NULL
;
6417 pipe_opt
[j
].ch
= ctrl('Y');
6418 pipe_opt
[j
].rval
= 11;
6419 pipe_opt
[j
].name
= "^Y";
6420 pipe_opt
[j
++].label
= NULL
;
6422 pipe_opt
[j
].ch
= ctrl('R');
6423 pipe_opt
[j
].rval
= 12;
6424 pipe_opt
[j
].name
= "^R";
6425 pipe_opt
[j
++].label
= NULL
;
6427 if(MCMD_ISAGG(aopt
)){
6428 if(!pseudo_selected(state
->mail_stream
, msgmap
))
6432 pipe_opt
[j
].ch
= ctrl('T');
6433 pipe_opt
[j
].rval
= 13;
6434 pipe_opt
[j
].name
= "^T";
6435 pipe_opt
[j
++].label
= NULL
;
6439 pipe_opt
[j
].ch
= KEY_UP
;
6440 pipe_opt
[j
].rval
= 30;
6441 pipe_opt
[j
].name
= "";
6443 pipe_opt
[j
++].label
= "";
6445 pipe_opt
[j
].ch
= KEY_DOWN
;
6446 pipe_opt
[j
].rval
= 31;
6447 pipe_opt
[j
].name
= "";
6448 pipe_opt
[j
++].label
= "";
6450 pipe_opt
[j
].ch
= -1;
6455 snprintf(prompt
, sizeof(prompt
), "Pipe %smessage%s%s to %s%s%s%s%s%s%s: ",
6457 MCMD_ISAGG(aopt
) ? "s" : " ",
6458 MCMD_ISAGG(aopt
) ? "" : comatose(mn_get_cur(msgmap
)),
6459 (!capture
|| delimit
|| (newpipe
&& MCMD_ISAGG(aopt
))) ? "(" : "",
6460 capture
? "" : "uncaptured",
6461 (!capture
&& delimit
) ? "," : "",
6462 delimit
? "delimited" : "",
6463 ((!capture
|| delimit
) && newpipe
&& MCMD_ISAGG(aopt
)) ? "," : "",
6464 (newpipe
&& MCMD_ISAGG(aopt
)) ? "new pipe" : "",
6465 (!capture
|| delimit
|| (newpipe
&& MCMD_ISAGG(aopt
))) ? ") " : "");
6466 prompt
[sizeof(prompt
)-1] = '\0';
6467 pipe_opt
[1].label
= raw
? N_("Shown Text") : N_("Raw Text");
6468 pipe_opt
[2].label
= capture
? N_("Free Output") : N_("Capture Output");
6469 pipe_opt
[3].label
= delimit
? N_("No Delimiter") : N_("With Delimiter");
6471 pipe_opt
[fourlabel
].label
= newpipe
? N_("To Same Pipe") : N_("To Individual Pipes");
6475 * 2 is really 1 because there will be one real entry and
6476 * one entry of "" because of the get_prev_hist above.
6478 if(items_in_hist(history
) > 2){
6479 pipe_opt
[ku
].name
= HISTORY_UP_KEYNAME
;
6480 pipe_opt
[ku
].label
= HISTORY_KEYLABEL
;
6481 pipe_opt
[ku
+1].name
= HISTORY_DOWN_KEYNAME
;
6482 pipe_opt
[ku
+1].label
= HISTORY_KEYLABEL
;
6485 pipe_opt
[ku
].name
= "";
6486 pipe_opt
[ku
].label
= "";
6487 pipe_opt
[ku
+1].name
= "";
6488 pipe_opt
[ku
+1].label
= "";
6491 flags
= OE_APPEND_CURRENT
| OE_SEQ_SENSITIVE
;
6492 switch(optionally_enter(pipe_command
, -FOOTER_ROWS(state
), 0,
6493 sizeof(pipe_command
), prompt
,
6494 pipe_opt
, NO_HELP
, &flags
)){
6496 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
6497 _("Internal problem encountered"));
6501 case 10 : /* flip raw bit */
6505 case 11 : /* flip capture bit */
6509 case 12 : /* flip delimit bit */
6513 case 13 : /* flip newpipe bit */
6518 flagsforhist
= (raw
? 0x8 : 0) +
6519 (delimit
? 0x4 : 0) +
6520 (newpipe
? 0x2 : 0) +
6521 (capture
? 0x1 : 0);
6522 if((p
= get_prev_hist(history
, pipe_command
, flagsforhist
, NULL
)) != NULL
){
6523 strncpy(pipe_command
, p
, sizeof(pipe_command
));
6524 pipe_command
[sizeof(pipe_command
)-1] = '\0';
6525 if(history
->hist
[history
->curindex
]){
6526 flagsforhist
= history
->hist
[history
->curindex
]->flags
;
6527 raw
= (flagsforhist
& 0x8) ? 1 : 0;
6528 delimit
= (flagsforhist
& 0x4) ? 1 : 0;
6529 newpipe
= (flagsforhist
& 0x2) ? 1 : 0;
6530 capture
= (flagsforhist
& 0x1) ? 1 : 0;
6539 flagsforhist
= (raw
? 0x8 : 0) +
6540 (delimit
? 0x4 : 0) +
6541 (newpipe
? 0x2 : 0) +
6542 (capture
? 0x1 : 0);
6543 if((p
= get_next_hist(history
, pipe_command
, flagsforhist
, NULL
)) != NULL
){
6544 strncpy(pipe_command
, p
, sizeof(pipe_command
));
6545 pipe_command
[sizeof(pipe_command
)-1] = '\0';
6546 if(history
->hist
[history
->curindex
]){
6547 flagsforhist
= history
->hist
[history
->curindex
]->flags
;
6548 raw
= (flagsforhist
& 0x8) ? 1 : 0;
6549 delimit
= (flagsforhist
& 0x4) ? 1 : 0;
6550 newpipe
= (flagsforhist
& 0x2) ? 1 : 0;
6551 capture
= (flagsforhist
& 0x1) ? 1 : 0;
6560 if(pipe_command
[0]){
6562 flagsforhist
= (raw
? 0x8 : 0) +
6563 (delimit
? 0x4 : 0) +
6564 (newpipe
? 0x2 : 0) +
6565 (capture
? 0x1 : 0);
6566 save_hist(history
, pipe_command
, flagsforhist
, NULL
);
6568 flags
= PIPE_USER
| PIPE_WRITE
| PIPE_STDERR
;
6569 flags
|= (raw
? PIPE_RAW
: 0);
6575 ps_global
->mangled_screen
= 1;
6576 ps_global
->in_init_seq
= 1;
6578 flags
|= PIPE_RESET
;
6581 if(!newpipe
&& !(syspipe
= cmd_pipe_open(pipe_command
,
6582 (flags
& PIPE_RESET
)
6588 for(i
= mn_first_cur(msgmap
);
6590 i
= mn_next_cur(msgmap
)){
6591 e
= pine_mail_fetchstructure(ps_global
->mail_stream
,
6592 mn_m2raw(msgmap
, i
), &b
);
6593 if(!(state
->mail_stream
6594 && (rawno
= mn_m2raw(msgmap
, i
)) > 0L
6595 && rawno
<= state
->mail_stream
->nmsgs
6596 && (mc
= mail_elt(state
->mail_stream
, rawno
))
6601 && !(syspipe
= cmd_pipe_open(pipe_command
,
6602 (flags
& PIPE_RESET
)
6606 || (delimit
&& !bezerk_delimiter(e
, mc
, pc
, next
++)))
6613 prime_raw_pipe_getc(ps_global
->mail_stream
,
6614 mn_m2raw(msgmap
, i
), -1L, 0L);
6616 gf_link_filter(gf_nvtnl_local
, NULL
);
6617 if((pipe_err
= gf_pipe(raw_pipe_getc
, pc
)) != NULL
){
6618 q_status_message1(SM_ORDER
|SM_DING
,
6620 _("Internal Error: %s"),
6625 else if(!format_message(mn_m2raw(msgmap
, i
), e
, b
,
6626 NULL
, FM_NEW_MESS
| FM_NOWRAP
, pc
))
6631 if(close_system_pipe(&syspipe
, &pipe_rv
, pipe_callback
) == -1)
6636 ps_global
->in_init_seq
= 0;
6639 if(close_system_pipe(&syspipe
, &pipe_rv
, pipe_callback
) == -1)
6641 if(done
) /* say we had a problem */
6642 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
6643 _("Error piping message"));
6644 else if(resultfilename
){
6646 /* only display if no error */
6647 display_output_file(resultfilename
, "PIPE MESSAGE",
6649 fs_give((void **)&resultfilename
);
6653 q_status_message(SM_ORDER
, 0, 2, _("Pipe command completed"));
6659 /* else fall thru as if cancelled */
6662 cmd_cancelled("Pipe command");
6667 helper(h_common_pipe
, _("HELP FOR PIPE COMMAND"), HLPD_SIMPLE
);
6668 ps_global
->mangled_screen
= 1;
6671 case 2 : /* no place to escape to */
6672 case 4 : /* can't suspend */
6678 ps_global
->mangled_footer
= 1;
6679 if(MCMD_ISAGG(aopt
))
6680 restore_selected(msgmap
);
6686 /*----------------------------------------------------------------------
6687 Screen to offer list management commands contained in message
6689 Args: state -- pointer to struct holding a bunch of pine state
6690 msgmap -- table mapping msg nums to c-client sequence nums
6691 aopt -- aggregate options
6695 NOTE: Inspired by contrib from Jeremy Blackman <loki@maison-otaku.net>
6698 rfc2369_display(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, long int msgno
)
6701 char *h
, *hdrs
[MLCMD_COUNT
+ 1];
6702 long index_no
= mn_raw2m(msgmap
, msgno
);
6703 RFC2369_S data
[MLCMD_COUNT
];
6705 /* for each header field */
6706 if((h
= pine_fetchheader_lines(stream
, msgno
, NULL
, rfc2369_hdrs(hdrs
))) != NULL
){
6707 memset(&data
[0], 0, sizeof(RFC2369_S
) * MLCMD_COUNT
);
6708 if(rfc2369_parse_fields(h
, &data
[0])){
6711 if((explain
= list_mgmt_text(data
, index_no
)) != NULL
){
6712 list_mgmt_screen(explain
);
6713 ps_global
->mangled_screen
= 1;
6719 fs_give((void **) &h
);
6723 q_status_message1(SM_ORDER
, 0, 3,
6724 "Message %s contains no list management information",
6725 comatose(index_no
));
6730 list_mgmt_text(RFC2369_S
*data
, long int msgno
)
6733 int i
, j
, n
, fields
= 0;
6734 static char *rfc2369_intro1
=
6735 "<HTML><HEAD></HEAD><BODY><H1>Mail List Commands</H1>Message ";
6736 static char *rfc2369_intro2
[] = {
6737 N_(" has information associated with it "),
6738 N_("that explains how to participate in an email list. An "),
6739 N_("email list is represented by a single email address that "),
6740 N_("users sharing a common interest can send messages to (known "),
6741 N_("as posting) which are then redistributed to all members "),
6742 N_("of the list (sometimes after review by a moderator)."),
6743 N_("<P>List participation commands in this message include:"),
6747 if((store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
){
6749 /* Insert introductory text */
6750 so_puts(store
, rfc2369_intro1
);
6752 so_puts(store
, comatose(msgno
));
6754 for(i
= 0; rfc2369_intro2
[i
]; i
++)
6755 so_puts(store
, _(rfc2369_intro2
[i
]));
6757 so_puts(store
, "<P>");
6758 for(i
= 0; i
< MLCMD_COUNT
; i
++)
6759 if(data
[i
].data
[0].value
6760 || data
[i
].data
[0].comment
6761 || data
[i
].data
[0].error
){
6763 so_puts(store
, "<UL>");
6765 so_puts(store
, "<LI>");
6767 (n
= (data
[i
].data
[1].value
|| data
[i
].data
[1].comment
))
6771 so_puts(store
, data
[i
].field
.description
);
6772 so_puts(store
, ". ");
6775 so_puts(store
, "<OL>");
6779 && (data
[i
].data
[j
].comment
6780 || data
[i
].data
[j
].value
6781 || data
[i
].data
[j
].error
);
6784 so_puts(store
, n
? "<P><LI>" : "<P>");
6786 if(data
[i
].data
[j
].comment
){
6788 _("With the provided comment:<P><BLOCKQUOTE>"));
6789 so_puts(store
, data
[i
].data
[j
].comment
);
6790 so_puts(store
, "</BLOCKQUOTE><P>");
6793 if(data
[i
].data
[j
].value
){
6795 && !strucmp(data
[i
].data
[j
].value
, "NO")){
6797 _("Posting is <EM>not</EM> allowed on this list"));
6800 so_puts(store
, "Select <A HREF=\"");
6801 so_puts(store
, data
[i
].data
[j
].value
);
6802 so_puts(store
, "\">HERE</A> to ");
6803 so_puts(store
, (data
[i
].field
.action
)
6804 ? data
[i
].field
.action
6808 so_puts(store
, ".");
6811 if(data
[i
].data
[j
].error
){
6812 so_puts(store
, "<P>Unfortunately, Alpine can not offer");
6813 so_puts(store
, " to take direct action based upon it");
6814 so_puts(store
, " because it was improperly formatted.");
6815 so_puts(store
, " The unrecognized data associated with");
6816 so_puts(store
, " the \"");
6817 so_puts(store
, data
[i
].field
.name
);
6818 so_puts(store
, "\" header field was:<P><BLOCKQUOTE>");
6819 so_puts(store
, data
[i
].data
[j
].error
);
6820 so_puts(store
, "</BLOCKQUOTE>");
6823 so_puts(store
, "<P>");
6827 so_puts(store
, "</OL>");
6831 so_puts(store
, "</UL>");
6833 so_puts(store
, "</BODY></HTML>");
6841 list_mgmt_screen(STORE_S
*html
)
6847 HANDLE_S
*handles
= NULL
;
6851 so_seek(html
, 0L, 0);
6852 gf_set_so_readc(&gc
, html
);
6854 init_handles(&handles
);
6856 if((store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
){
6857 gf_set_so_writec(&pc
, store
);
6860 gf_link_filter(gf_html2plain
,
6861 gf_html2plain_opt(NULL
, ps_global
->ttyo
->screen_cols
,
6862 non_messageview_margin(), &handles
, NULL
, 0));
6864 error
= gf_pipe(gc
, pc
);
6866 gf_clear_so_writec(store
);
6871 memset(&sargs
, 0, sizeof(SCROLL_S
));
6872 sargs
.text
.text
= so_text(store
);
6873 sargs
.text
.src
= CharStar
;
6874 sargs
.text
.desc
= "list commands";
6875 sargs
.text
.handles
= handles
;
6877 sargs
.start
.on
= Offset
;
6878 sargs
.start
.loc
.offset
= offset
;
6881 sargs
.bar
.title
= _("MAIL LIST COMMANDS");
6882 sargs
.bar
.style
= MessageNumber
;
6883 sargs
.resize_exit
= 1;
6884 sargs
.help
.text
= h_special_list_commands
;
6885 sargs
.help
.title
= _("HELP FOR LIST COMMANDS");
6886 sargs
.keys
.menu
= &listmgr_keymenu
;
6887 setbitmap(sargs
.keys
.bitmap
);
6889 clrbitn(LM_TRY_KEY
, sargs
.keys
.bitmap
);
6890 clrbitn(LM_PREV_KEY
, sargs
.keys
.bitmap
);
6891 clrbitn(LM_NEXT_KEY
, sargs
.keys
.bitmap
);
6894 cmd
= scrolltool(&sargs
);
6895 offset
= sargs
.start
.loc
.offset
;
6901 free_handles(&handles
);
6902 gf_clear_so_readc(html
);
6904 while(cmd
== MC_RESIZE
);
6908 /*----------------------------------------------------------------------
6909 Prompt the user for the type of select desired
6911 NOTE: any and all functions that successfully exit the second
6912 switch() statement below (currently "select_*() functions"),
6913 *MUST* update the folder's MESSAGECACHE element's "searched"
6914 bits to reflect the search result. Functions using
6915 mail_search() get this for free, the others must update 'em
6918 Returns -1 if canceled without changing selection
6919 0 if selection may have changed
6922 aggregate_select(struct pine
*state
, MSGNO_S
*msgmap
, int q_line
, CmdWhere in_index
)
6924 long i
, diff
, old_tot
, msgno
, raw
;
6925 int p
= 0, q
= 0, rv
= 0, narrow
= 0, hidden
, ret
= -1;
6928 SEARCHSET
*limitsrch
= NULL
;
6930 extern MAILSTREAM
*mm_search_stream
;
6931 extern long mm_search_count
;
6933 hidden
= any_lflagged(msgmap
, MN_HIDE
) > 0L;
6934 mm_search_stream
= state
->mail_stream
;
6935 mm_search_count
= 0L;
6937 if(is_imap_stream(state
->mail_stream
) && XGMEXT1(state
->mail_stream
))
6938 sel_opts
= THRD_INDX() ? sel_opts6
: sel_opts5
;
6940 sel_opts
= THRD_INDX() ? sel_opts4
: sel_opts2
;
6943 sel_opts
[SEL_OPTS_THREAD
].ch
= SEL_OPTS_THREAD_CH
;
6946 if(is_imap_stream(state
->mail_stream
) && XGMEXT1(state
->mail_stream
)){
6947 sel_opts
[SEL_OPTS_THREAD
].ch
= SEL_OPTS_XGMEXT_CH
;
6948 sel_opts
[SEL_OPTS_THREAD
].rval
= sel_opts
[SEL_OPTS_XGMEXT
].rval
;
6949 sel_opts
[SEL_OPTS_THREAD
].name
= sel_opts
[SEL_OPTS_XGMEXT
].name
;
6950 sel_opts
[SEL_OPTS_THREAD
].label
= sel_opts
[SEL_OPTS_XGMEXT
].label
;
6951 sel_opts
[SEL_OPTS_XGMEXT
].ch
= -1;
6954 sel_opts
[SEL_OPTS_THREAD
].ch
= -1;
6957 if((old_tot
= any_lflagged(msgmap
, MN_SLCT
)) != 0){
6960 thrd
= fetch_thread(state
->mail_stream
,
6961 mn_m2raw(msgmap
, mn_get_cur(msgmap
)));
6962 /* check if whole thread is selected or not */
6964 count_lflags_in_thread(state
->mail_stream
,thrd
,msgmap
,MN_SLCT
)
6966 count_lflags_in_thread(state
->mail_stream
,thrd
,msgmap
,MN_NONE
))
6969 sel_opts1
[1].label
= i
? N_("unselect Curthrd") : N_("select Curthrd");
6972 i
= get_lflag(state
->mail_stream
, msgmap
, mn_get_cur(msgmap
),
6974 sel_opts1
[1].label
= i
? N_("unselect Cur") : N_("select Cur");
6977 sel_opts
+= 2; /* disable extra options */
6978 if(is_imap_stream(state
->mail_stream
) && XGMEXT1(state
->mail_stream
))
6979 q
= double_radio_buttons(_(sel_pmt1
), q_line
, sel_opts1
, 'c', 'x', NO_HELP
,
6982 q
= radio_buttons(_(sel_pmt1
), q_line
, sel_opts1
, 'c', 'x', NO_HELP
,
6985 case 'f' : /* flip selection */
6987 for(i
= 1L; i
<= mn_get_total(msgmap
); i
++){
6989 q
= !get_lflag(state
->mail_stream
, msgmap
, i
, MN_SLCT
);
6990 set_lflag(state
->mail_stream
, msgmap
, i
, MN_SLCT
, q
);
6992 set_lflag(state
->mail_stream
, msgmap
, i
, MN_HIDE
, !q
);
6994 mn_reset_cur(msgmap
, msgno
= i
);
7000 case 'n' : /* narrow selection */
7004 case 'r' : /* replace selection */
7005 p
= 1; /* set flag we want to replace */
7006 sel_opts
-= 2; /* re-enable first two options */
7007 case 'b' : /* broaden selection */
7008 q
= 0; /* offer criteria prompt */
7011 case 'c' : /* Un/Select Current */
7012 case 'a' : /* Unselect All */
7013 case 'x' : /* cancel */
7017 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
7018 "Unsupported Select option");
7025 if(is_imap_stream(state
->mail_stream
) && XGMEXT1(state
->mail_stream
))
7026 q
= double_radio_buttons(sel_pmt2
, q_line
, sel_opts
, 'c', 'x', NO_HELP
,
7029 q
= radio_buttons(sel_pmt2
, q_line
, sel_opts
, 'c', 'x', NO_HELP
,
7030 RB_NORM
|RB_RET_HELP
);
7033 helper(h_index_cmd_select
, _("HELP FOR SELECT"), HLPD_SIMPLE
);
7034 ps_global
->mangled_screen
= 1;
7041 /* When we are replacing the search, unselect all messages first, unless
7042 * we are cancelling, in whose case, leave the screen as is, because we
7045 if(p
== 1 && q
!= 'x'){
7046 msgno
= any_lflagged(msgmap
, MN_SLCT
);
7047 diff
= (!msgno
) ? mn_get_total(msgmap
) : 0L;
7048 agg_select_all(state
->mail_stream
, msgmap
, &diff
,
7049 any_lflagged(msgmap
, MN_SLCT
) <= 0L);
7053 * The purpose of this is to add the appropriate searchset to the
7054 * search so that the search can be limited to only looking at what
7055 * it needs to look at. That is, if we are narrowing then we only need
7056 * to look at messages which are already selected, and if we are
7057 * broadening, then we only need to look at messages which are not
7058 * yet selected. This routine will work whether or not
7059 * limiting_searchset properly limits the search set. In particular,
7060 * the searchset returned by limiting_searchset may include messages
7061 * which really shouldn't be included. We do that because a too-large
7062 * searchset will break some IMAP servers. It is even possible that it
7063 * becomes inefficient to send the whole set. If the select function
7064 * frees limitsrch, it should be sure to set it to NULL so we won't
7065 * try freeing it again here.
7067 limitsrch
= limiting_searchset(state
->mail_stream
, narrow
);
7070 * NOTE: See note about MESSAGECACHE "searched" bits above!
7073 case 'x': /* cancel */
7074 cmd_cancelled("Select command");
7077 case 'c' : /* select/unselect current */
7078 (void) select_by_current(state
, msgmap
, in_index
);
7082 case 'a' : /* select/unselect all */
7083 msgno
= any_lflagged(msgmap
, MN_SLCT
);
7084 diff
= (!msgno
) ? mn_get_total(msgmap
) : 0L;
7086 agg_select_all(state
->mail_stream
, msgmap
, &diff
,
7087 any_lflagged(msgmap
, MN_SLCT
) <= 0L);
7088 q_status_message4(SM_ORDER
,0,2,
7089 "%s%s message%s %sselected",
7090 msgno
? "" : "All ", comatose(diff
),
7091 plural(diff
), msgno
? "UN" : "");
7094 case 'n' : /* Select by Number */
7097 rv
= select_by_thrd_number(state
->mail_stream
, msgmap
, &limitsrch
);
7099 rv
= select_by_number(state
->mail_stream
, msgmap
, &limitsrch
);
7103 case 'd' : /* Select by Date */
7105 rv
= select_by_date(state
->mail_stream
, msgmap
, mn_get_cur(msgmap
),
7109 case 't' : /* Text */
7111 rv
= select_by_text(state
->mail_stream
, msgmap
, mn_get_cur(msgmap
),
7115 case 'z' : /* Size */
7117 rv
= select_by_size(state
->mail_stream
, &limitsrch
);
7120 case 's' : /* Status */
7122 rv
= select_by_status(state
->mail_stream
, &limitsrch
);
7125 case 'k' : /* Keyword */
7127 rv
= select_by_keyword(state
->mail_stream
, &limitsrch
);
7130 case 'r' : /* Rule */
7132 rv
= select_by_rule(state
->mail_stream
, &limitsrch
);
7135 case 'h' : /* Thread */
7137 rv
= select_by_thread(state
->mail_stream
, msgmap
, &limitsrch
);
7140 case 'g' : /* X-GM-EXT-1 */
7142 rv
= select_by_gm_content(state
->mail_stream
, msgmap
, mn_get_cur(msgmap
), &limitsrch
);
7146 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
7147 "Unsupported Select option");
7152 mail_free_searchset(&limitsrch
);
7154 if(rv
) /* bad return value.. */
7155 return(ret
); /* error already displayed */
7157 if(narrow
) /* make sure something was selected */
7158 for(i
= 1L; i
<= mn_get_total(msgmap
); i
++)
7159 if((raw
= mn_m2raw(msgmap
, i
)) > 0L && state
->mail_stream
7160 && raw
<= state
->mail_stream
->nmsgs
7161 && (mc
= mail_elt(state
->mail_stream
, raw
)) && mc
->searched
){
7162 if(get_lflag(state
->mail_stream
, msgmap
, i
, MN_SLCT
))
7169 if(mm_search_count
){
7171 * loop thru all the messages, adjusting local flag bits
7172 * based on their "searched" bit...
7174 for(i
= 1L, msgno
= 0L; i
<= mn_get_total(msgmap
); i
++)
7176 /* turning OFF selectedness if the "searched" bit isn't lit. */
7177 if(get_lflag(state
->mail_stream
, msgmap
, i
, MN_SLCT
)){
7178 if((raw
= mn_m2raw(msgmap
, i
)) > 0L && state
->mail_stream
7179 && raw
<= state
->mail_stream
->nmsgs
7180 && (mc
= mail_elt(state
->mail_stream
, raw
))
7183 set_lflag(state
->mail_stream
, msgmap
, i
, MN_SLCT
, 0);
7185 set_lflag(state
->mail_stream
, msgmap
, i
, MN_HIDE
, 1);
7187 /* adjust current message in case we unselect and hide it */
7188 else if(msgno
< mn_get_cur(msgmap
)
7190 || !get_lflag(state
->mail_stream
, msgmap
,
7195 else if((raw
= mn_m2raw(msgmap
, i
)) > 0L && state
->mail_stream
7196 && raw
<= state
->mail_stream
->nmsgs
7197 && (mc
= mail_elt(state
->mail_stream
, raw
)) && mc
->searched
){
7198 /* turn ON selectedness if "searched" bit is lit. */
7199 if(!get_lflag(state
->mail_stream
, msgmap
, i
, MN_SLCT
)){
7201 set_lflag(state
->mail_stream
, msgmap
, i
, MN_SLCT
, 1);
7203 set_lflag(state
->mail_stream
, msgmap
, i
, MN_HIDE
, 0);
7207 /* if we're zoomed and the current message was unselected */
7209 && get_lflag(state
->mail_stream
,msgmap
,mn_get_cur(msgmap
),MN_HIDE
))
7210 mn_reset_cur(msgmap
, msgno
);
7215 q_status_message4(SM_ORDER
, 3, 3,
7216 "%s. %s message%s remain%s selected.",
7218 ? "No change resulted"
7219 : "No messages in intersection",
7220 comatose(old_tot
), plural(old_tot
),
7221 (old_tot
== 1L) ? "s" : "");
7223 q_status_message(SM_ORDER
, 3, 3,
7224 _("No change resulted. Matching messages already selected."));
7226 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
7227 _("Select failed. No %smessages selected."),
7228 old_tot
? _("additional ") : "");
7231 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,
7232 "Select matched %ld message%s. %s %smessage%s %sselected.",
7233 (diff
> 0) ? diff
: old_tot
+ diff
,
7234 plural((diff
> 0) ? diff
: old_tot
+ diff
),
7235 comatose((diff
> 0) ? any_lflagged(msgmap
, MN_SLCT
) : -diff
),
7236 (diff
> 0) ? "total " : "",
7237 plural((diff
> 0) ? any_lflagged(msgmap
, MN_SLCT
) : -diff
),
7238 (diff
> 0) ? "" : "UN");
7239 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
7240 q_status_message(SM_ORDER
, 3, 3, tmp_20k_buf
);
7243 q_status_message2(SM_ORDER
, 3, 3, _("Select matched %s message%s!"),
7244 comatose(diff
), plural(diff
));
7250 /*----------------------------------------------------------------------
7251 Toggle the state of the current message
7253 Args: state -- pointer pine's state variables
7254 msgmap -- message collection to operate on
7255 in_index -- in the message index view
7256 Returns: TRUE if current marked selected, FALSE otw
7259 select_by_current(struct pine
*state
, MSGNO_S
*msgmap
, CmdWhere in_index
)
7262 int all_selected
= 0;
7263 unsigned long was
, tot
, rawno
;
7266 cur
= mn_get_cur(msgmap
);
7269 thrd
= fetch_thread(state
->mail_stream
, mn_m2raw(msgmap
, cur
));
7273 was
= count_lflags_in_thread(state
->mail_stream
, thrd
, msgmap
, MN_SLCT
);
7274 tot
= count_lflags_in_thread(state
->mail_stream
, thrd
, msgmap
, MN_NONE
);
7279 set_thread_lflags(state
->mail_stream
, thrd
, msgmap
, MN_SLCT
, 0);
7280 if(any_lflagged(msgmap
, MN_HIDE
) > 0L){
7281 set_thread_lflags(state
->mail_stream
, thrd
, msgmap
, MN_HIDE
, 1);
7283 * See if there's anything left to zoom on. If so,
7284 * pick an adjacent one for highlighting, else make
7285 * sure nothing is left hidden...
7287 if(any_lflagged(msgmap
, MN_SLCT
)){
7288 mn_inc_cur(state
->mail_stream
, msgmap
,
7289 (in_index
== View
&& THREADING()
7290 && sp_viewing_a_thread(state
->mail_stream
))
7292 : (in_index
== View
)
7293 ? MH_ANYTHD
: MH_NONE
);
7294 if(mn_get_cur(msgmap
) == cur
)
7295 mn_dec_cur(state
->mail_stream
, msgmap
,
7296 (in_index
== View
&& THREADING()
7297 && sp_viewing_a_thread(state
->mail_stream
))
7299 : (in_index
== View
)
7300 ? MH_ANYTHD
: MH_NONE
);
7302 else /* clear all hidden flags */
7303 (void) unzoom_index(state
, state
->mail_stream
, msgmap
);
7307 set_thread_lflags(state
->mail_stream
, thrd
, msgmap
, MN_SLCT
, 1);
7309 q_status_message3(SM_ORDER
, 0, 2, "%s message%s %sselected",
7310 comatose(all_selected
? was
: tot
-was
),
7311 plural(all_selected
? was
: tot
-was
),
7312 all_selected
? "UN" : "");
7314 /* collapsed thread */
7316 && ((rawno
= mn_m2raw(msgmap
, cur
)) != 0L)
7317 && ((thrd
= fetch_thread(state
->mail_stream
, rawno
)) != NULL
)
7318 && (thrd
&& thrd
->next
&& get_lflag(state
->mail_stream
, NULL
, rawno
, MN_COLL
))){
7320 * This doesn't work quite the same as the colon command works, but
7321 * it is arguably doing the correct thing. The difference is
7322 * that aggregate_select will zoom after selecting back where it
7323 * was called from, but selecting a thread with colon won't zoom.
7324 * Maybe it makes sense to zoom after a select but not after a colon
7325 * command even though they are very similar.
7327 thread_command(state
, state
->mail_stream
, msgmap
, ':', -FOOTER_ROWS(state
));
7331 get_lflag(state
->mail_stream
, msgmap
, cur
, MN_SLCT
)) != 0){ /* set? */
7332 set_lflag(state
->mail_stream
, msgmap
, cur
, MN_SLCT
, 0);
7333 if(any_lflagged(msgmap
, MN_HIDE
) > 0L){
7334 set_lflag(state
->mail_stream
, msgmap
, cur
, MN_HIDE
, 1);
7336 * See if there's anything left to zoom on. If so,
7337 * pick an adjacent one for highlighting, else make
7338 * sure nothing is left hidden...
7340 if(any_lflagged(msgmap
, MN_SLCT
)){
7341 mn_inc_cur(state
->mail_stream
, msgmap
,
7342 (in_index
== View
&& THREADING()
7343 && sp_viewing_a_thread(state
->mail_stream
))
7345 : (in_index
== View
)
7346 ? MH_ANYTHD
: MH_NONE
);
7347 if(mn_get_cur(msgmap
) == cur
)
7348 mn_dec_cur(state
->mail_stream
, msgmap
,
7349 (in_index
== View
&& THREADING()
7350 && sp_viewing_a_thread(state
->mail_stream
))
7352 : (in_index
== View
)
7353 ? MH_ANYTHD
: MH_NONE
);
7355 else /* clear all hidden flags */
7356 (void) unzoom_index(state
, state
->mail_stream
, msgmap
);
7360 set_lflag(state
->mail_stream
, msgmap
, cur
, MN_SLCT
, 1);
7362 q_status_message2(SM_ORDER
, 0, 2, "Message %s %sselected",
7363 long2string(cur
), all_selected
? "UN" : "");
7367 return(!all_selected
);
7371 /*----------------------------------------------------------------------
7372 Prompt the user for the command to perform on selected messages
7374 Args: state -- pointer pine's state variables
7375 msgmap -- message collection to operate on
7376 q_line -- line on display to write prompts
7377 Returns: 1 if the selected messages are suitably commanded,
7378 0 if the choice to pick the command was declined
7382 apply_command(struct pine
*state
, MAILSTREAM
*stream
, MSGNO_S
*msgmap
,
7383 UCS preloadkeystroke
, int flags
, int q_line
)
7385 int i
= 8, /* number of static entries in sel_opts3 */
7389 agg
= (flags
& AC_FROM_THREAD
) ? MCMD_AGG_2
: MCMD_AGG
;
7394 * To do this "right", we really ought to have access to the keymenu
7395 * here and change the typed command into a real command by running
7396 * it through menu_command. Then the switch below would be against
7397 * results from menu_command. If we did that we'd also pass the
7398 * results of menu_command in as preloadkeystroke instead of passing
7399 * the keystroke itself. But we don't have the keymenu handy,
7400 * so we have to fake it. The only complication that we run into
7401 * is that KEY_DEL is an escape sequence so we change a typed
7402 * KEY_DEL esc seq into the letter D.
7405 if(!preloadkeystroke
){
7406 if(F_ON(F_ENABLE_FLAG
,state
)){ /* flag? */
7407 sel_opts3
[i
].ch
= '*';
7408 sel_opts3
[i
].rval
= '*';
7409 sel_opts3
[i
].name
= "*";
7410 sel_opts3
[i
++].label
= N_("Flag");
7413 if(F_ON(F_ENABLE_PIPE
,state
)){ /* pipe? */
7414 sel_opts3
[i
].ch
= '|';
7415 sel_opts3
[i
].rval
= '|';
7416 sel_opts3
[i
].name
= "|";
7417 sel_opts3
[i
++].label
= N_("Pipe");
7420 if(F_ON(F_ENABLE_BOUNCE
,state
)){ /* bounce? */
7421 sel_opts3
[i
].ch
= 'b';
7422 sel_opts3
[i
].rval
= 'b';
7423 sel_opts3
[i
].name
= "B";
7424 sel_opts3
[i
++].label
= N_("Bounce");
7427 if(flags
& AC_FROM_THREAD
){
7428 if(flags
& (AC_COLL
| AC_EXPN
)){
7429 sel_opts3
[i
].ch
= '/';
7430 sel_opts3
[i
].rval
= '/';
7431 sel_opts3
[i
].name
= "/";
7432 sel_opts3
[i
++].label
= (flags
& AC_COLL
) ? N_("Collapse")
7436 sel_opts3
[i
].ch
= ';';
7437 sel_opts3
[i
].rval
= ';';
7438 sel_opts3
[i
].name
= ";";
7439 if(flags
& AC_UNSEL
)
7440 sel_opts3
[i
++].label
= N_("UnSelect");
7442 sel_opts3
[i
++].label
= N_("Select");
7445 if(F_ON(F_ENABLE_PRYNT
, state
)){ /* this one is invisible */
7446 sel_opts3
[i
].ch
= 'y';
7447 sel_opts3
[i
].rval
= '%';
7448 sel_opts3
[i
].name
= "";
7449 sel_opts3
[i
++].label
= "";
7452 if(!is_imap_stream(stream
) || LEVELUIDPLUS(stream
)){ /* expunge selected messages */
7453 sel_opts3
[i
].ch
= 'x';
7454 sel_opts3
[i
].rval
= 'x';
7455 sel_opts3
[i
].name
= "X";
7456 sel_opts3
[i
++].label
= N_("Expunge");
7459 if(nonempty_patterns(ROLE_DO_ROLES
, &pstate
) && first_pattern(&pstate
)){
7460 sel_opts3
[i
].ch
= '#';
7461 sel_opts3
[i
].rval
= '#';
7462 sel_opts3
[i
].name
= "#";
7463 sel_opts3
[i
++].label
= N_("Set Role");
7466 sel_opts3
[i
].ch
= KEY_DEL
; /* also invisible */
7467 sel_opts3
[i
].rval
= 'd';
7468 sel_opts3
[i
].name
= "";
7469 sel_opts3
[i
++].label
= "";
7471 sel_opts3
[i
].ch
= -1;
7473 snprintf(prompt
, sizeof(prompt
), "%s command : ",
7474 (flags
& AC_FROM_THREAD
) ? "THREAD" : "APPLY");
7475 prompt
[sizeof(prompt
)-1] = '\0';
7476 cmd
= double_radio_buttons(prompt
, q_line
, sel_opts3
, 'z', 'c', NO_HELP
,
7482 if(preloadkeystroke
== KEY_DEL
)
7485 if(preloadkeystroke
< 0x80 && isupper((int) preloadkeystroke
))
7486 cmd
= tolower((int) preloadkeystroke
);
7488 cmd
= (int) preloadkeystroke
; /* shouldn't happen */
7493 case 'd' : /* delete */
7494 we_cancel
= busy_cue(NULL
, NULL
, 1);
7495 rv
= cmd_delete(state
, msgmap
, agg
, NULL
); /* don't advance or offer "TAB" */
7500 case 'u' : /* undelete */
7501 we_cancel
= busy_cue(NULL
, NULL
, 1);
7502 rv
= cmd_undelete(state
, msgmap
, agg
);
7507 case 'r' : /* reply */
7508 rv
= cmd_reply(state
, msgmap
, agg
, NULL
);
7511 case 'f' : /* Forward */
7512 rv
= cmd_forward(state
, msgmap
, agg
, NULL
);
7515 case '%' : /* print */
7516 rv
= cmd_print(state
, msgmap
, agg
, MsgIndx
);
7519 case 't' : /* take address */
7520 rv
= cmd_take_addr(state
, msgmap
, agg
);
7523 case 's' : /* save */
7524 rv
= cmd_save(state
, stream
, msgmap
, agg
, MsgIndx
);
7527 case 'e' : /* export */
7528 rv
= cmd_export(state
, msgmap
, q_line
, agg
);
7531 case '|' : /* pipe */
7532 rv
= cmd_pipe(state
, msgmap
, agg
);
7535 case '*' : /* flag */
7536 we_cancel
= busy_cue(NULL
, NULL
, 1);
7537 rv
= cmd_flag(state
, msgmap
, agg
);
7542 case 'b' : /* bounce */
7543 rv
= cmd_bounce(state
, msgmap
, agg
, NULL
);
7547 collapse_or_expand(state
, stream
, msgmap
,
7548 F_ON(F_SLASH_COLL_ENTIRE
, ps_global
)
7550 : mn_get_cur(msgmap
));
7554 select_thread_stmp(state
, stream
, msgmap
);
7557 case 'x' : /* Expunge */
7558 rv
= cmd_expunge(state
, stream
, msgmap
, agg
);
7561 case 'c' : /* cancel */
7562 cmd_cancelled((flags
& AC_FROM_THREAD
) ? "Thread command"
7567 if(nonempty_patterns(ROLE_DO_ROLES
, &pstate
) && first_pattern(&pstate
)){
7568 static ESCKEY_S choose_role
[] = {
7569 {'r', 'r', "R", N_("Reply")},
7570 {'f', 'f', "F", N_("Forward")},
7571 {'b', 'b', "B", N_("Bounce")},
7575 ACTION_S
*role
= NULL
;
7577 action
= radio_buttons(_("Reply, Forward or Bounce using a role? "),
7578 -FOOTER_ROWS(state
), choose_role
,
7579 'r', 'x', h_role_aggregate
, RB_NORM
);
7580 if(action
== 'r' || action
== 'f' || action
== 'b'){
7581 void (*prev_screen
)(struct pine
*) = NULL
, (*redraw
)(void) = NULL
;
7583 redraw
= state
->redrawer
;
7584 state
->redrawer
= NULL
;
7585 prev_screen
= state
->prev_screen
;
7587 state
->next_screen
= SCREEN_FUN_NULL
;
7589 if(role_select_screen(state
, &role
,
7590 action
== 'f' ? MC_FORWARD
:
7591 action
== 'r' ? MC_REPLY
:
7592 action
== 'b' ? MC_BOUNCE
: 0) < 0){
7593 cmd_cancelled(action
== 'f' ? _("Forward") :
7594 action
== 'r' ? _("Reply") : _("Bounce"));
7595 state
->next_screen
= prev_screen
;
7596 state
->redrawer
= redraw
;
7597 state
->mangled_screen
= 1;
7601 role
= combine_inherited_role(role
);
7603 role
= (ACTION_S
*) fs_get(sizeof(*role
));
7604 memset((void *) role
, 0, sizeof(*role
));
7605 role
->nick
= cpystr("Default Role");
7608 state
->redrawer
= NULL
;
7611 (void) cmd_reply(state
, msgmap
, agg
, role
);
7615 (void) cmd_forward(state
, msgmap
, agg
, role
);
7619 (void) cmd_bounce(state
, msgmap
, agg
, role
);
7629 state
->next_screen
= prev_screen
;
7630 state
->redrawer
= redraw
;
7631 state
->mangled_screen
= 1;
7637 case 'z' : /* default */
7638 q_status_message(SM_INFO
, 0, 2,
7639 "Cancelled, there is no default command");
7651 * Select by message number ranges.
7652 * Sets searched bits in mail_elts
7654 * Args limitsrch -- limit search to this searchset
7656 * Returns 0 on success.
7659 select_by_number(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, SEARCHSET
**limitsrch
)
7663 char number1
[16], number2
[16], numbers
[80], *p
, *t
;
7668 ps_global
->mangled_footer
= 1;
7671 int flags
= OE_APPEND_CURRENT
;
7673 r
= optionally_enter(numbers
, -FOOTER_ROWS(ps_global
), 0,
7674 sizeof(numbers
), _(select_num
), NULL
, help
, &flags
);
7679 help
= (help
== NO_HELP
) ? h_select_by_num
: NO_HELP
;
7683 for(t
= p
= numbers
; *p
; p
++) /* strip whitespace */
7684 if(!isspace((unsigned char)*p
))
7689 if(r
== 1 || numbers
[0] == '\0'){
7690 cmd_cancelled("Selection by number");
7697 for(n1
= 1; n1
<= stream
->nmsgs
; n1
++)
7698 if((mc
= mail_elt(stream
, n1
)) != NULL
)
7699 mc
->searched
= 0; /* clear searched bits */
7701 for(p
= numbers
; *p
; p
++){
7703 while(*p
&& isdigit((unsigned char)*p
))
7709 if(number1
[0] == '\0'){
7711 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
7712 _("Invalid number range, missing number before \"-\": %s"),
7716 else if(!strucmp("end", p
)){
7720 else if(!strucmp("$", p
)){
7729 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
7730 _("Invalid message number: %s"), numbers
);
7736 n1
= mn_get_total(msgmap
);
7738 n1
= mn_get_cur(msgmap
);
7739 else if((n1
= atol(number1
)) < 1L || n1
> mn_get_total(msgmap
)){
7740 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
7741 _("\"%s\" out of message number range"),
7748 while(*++p
&& isdigit((unsigned char)*p
))
7754 if(number2
[0] == '\0'){
7755 if(!strucmp("end", p
)){
7759 else if(!strucmp(p
, "$")){
7768 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
7769 _("Invalid number range, missing number after \"-\": %s"),
7776 n2
= mn_get_total(msgmap
);
7778 n2
= mn_get_cur(msgmap
);
7779 else if((n2
= atol(number2
)) < 1L || n2
> mn_get_total(msgmap
)){
7780 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
7781 _("\"%s\" out of message number range"),
7789 strncpy(t
, long2string(n1
), sizeof(t
));
7790 t
[sizeof(t
)-1] = '\0';
7791 q_status_message2(SM_ORDER
| SM_DING
, 0, 2,
7792 _("Invalid reverse message number range: %s-%s"),
7793 t
, long2string(n2
));
7797 for(;n1
<= n2
; n1
++){
7798 raw
= mn_m2raw(msgmap
, n1
);
7800 && (!(limitsrch
&& *limitsrch
)
7801 || in_searchset(*limitsrch
, (unsigned long) raw
)))
7802 mm_searched(stream
, raw
);
7806 raw
= mn_m2raw(msgmap
, n1
);
7808 && (!(limitsrch
&& *limitsrch
)
7809 || in_searchset(*limitsrch
, (unsigned long) raw
)))
7810 mm_searched(stream
, raw
);
7822 * Select by thread number ranges.
7823 * Sets searched bits in mail_elts
7825 * Args limitsrch -- limit search to this searchset
7827 * Returns 0 on success.
7830 select_by_thrd_number(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, SEARCHSET
**msgset
)
7834 char number1
[16], number2
[16], numbers
[80], *p
, *t
;
7836 PINETHRD_S
*thrd
= NULL
, *th
;
7840 ps_global
->mangled_footer
= 1;
7843 int flags
= OE_APPEND_CURRENT
;
7845 r
= optionally_enter(numbers
, -FOOTER_ROWS(ps_global
), 0,
7846 sizeof(numbers
), _(select_num
), NULL
, help
, &flags
);
7851 help
= (help
== NO_HELP
) ? h_select_by_thrdnum
: NO_HELP
;
7855 for(t
= p
= numbers
; *p
; p
++) /* strip whitespace */
7856 if(!isspace((unsigned char)*p
))
7861 if(r
== 1 || numbers
[0] == '\0'){
7862 cmd_cancelled("Selection by number");
7869 for(n1
= 1; n1
<= stream
->nmsgs
; n1
++)
7870 if((mc
= mail_elt(stream
, n1
)) != NULL
)
7871 mc
->searched
= 0; /* clear searched bits */
7873 for(p
= numbers
; *p
; p
++){
7875 while(*p
&& isdigit((unsigned char)*p
))
7881 if(number1
[0] == '\0'){
7883 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
7884 _("Invalid number range, missing number before \"-\": %s"),
7888 else if(!strucmp("end", p
)){
7892 else if(!strucmp(p
, "$")){
7901 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
7902 _("Invalid thread number: %s"), numbers
);
7908 n1
= msgmap
->max_thrdno
;
7910 th
= fetch_thread(stream
, mn_m2raw(msgmap
, mn_get_cur(msgmap
)));
7913 else if((n1
= atol(number1
)) < 1L || n1
> msgmap
->max_thrdno
){
7914 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
7915 _("\"%s\" out of thread number range"),
7923 while(*++p
&& isdigit((unsigned char)*p
))
7929 if(number2
[0] == '\0'){
7930 if(!strucmp("end", p
)){
7934 else if(!strucmp("$", p
)){
7943 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
7944 _("Invalid number range, missing number after \"-\": %s"),
7951 n2
= msgmap
->max_thrdno
;
7953 th
= fetch_thread(stream
, mn_m2raw(msgmap
, mn_get_cur(msgmap
)));
7956 else if((n2
= atol(number2
)) < 1L || n2
> msgmap
->max_thrdno
){
7957 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
7958 _("\"%s\" out of thread number range"),
7966 strncpy(t
, long2string(n1
), sizeof(t
));
7967 t
[sizeof(t
)-1] = '\0';
7968 q_status_message2(SM_ORDER
| SM_DING
, 0, 2,
7969 _("Invalid reverse message number range: %s-%s"),
7970 t
, long2string(n2
));
7974 for(;n1
<= n2
; n1
++){
7975 thrd
= find_thread_by_number(stream
, msgmap
, n1
, thrd
);
7978 set_search_bit_for_thread(stream
, thrd
, msgset
);
7982 thrd
= find_thread_by_number(stream
, msgmap
, n1
, NULL
);
7985 set_search_bit_for_thread(stream
, thrd
, msgset
);
7997 * Select by message dates.
7998 * Sets searched bits in mail_elts
8000 * Args limitsrch -- limit search to this searchset
8002 * Returns 0 on success.
8005 select_by_date(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, long int msgno
, SEARCHSET
**limitsrch
)
8007 int r
, we_cancel
= 0, when
= 0;
8008 char date
[100], defdate
[100], prompt
[128];
8009 time_t seldate
= time(0);
8010 struct tm
*seldate_tm
;
8013 static struct _tense
{
8018 {"were ", "SENT SINCE", " (inclusive)"},
8019 {"were ", "SENT BEFORE", " (exclusive)"},
8020 {"were ", "SENT ON", "" },
8021 {"", "ARRIVED SINCE", " (inclusive)"},
8022 {"", "ARRIVED BEFORE", " (exclusive)"},
8023 {"", "ARRIVED ON", "" }
8027 ps_global
->mangled_footer
= 1;
8031 * If talking to an old server, default to SINCE instead of
8032 * SENTSINCE, which was added later.
8034 if(is_imap_stream(stream
) && !modern_imap_stream(stream
))
8038 int flags
= OE_APPEND_CURRENT
;
8040 seldate_tm
= localtime(&seldate
);
8041 snprintf(defdate
, sizeof(defdate
), "%.2d-%.4s-%.4d", seldate_tm
->tm_mday
,
8042 month_abbrev(seldate_tm
->tm_mon
+ 1),
8043 seldate_tm
->tm_year
+ 1900);
8044 defdate
[sizeof(defdate
)-1] = '\0';
8045 snprintf(prompt
,sizeof(prompt
),"Select messages which %s%s%s [%s]: ",
8046 tense
[when
].preamble
, tense
[when
].range
,
8047 tense
[when
].scope
, defdate
);
8048 prompt
[sizeof(prompt
)-1] = '\0';
8049 r
= optionally_enter(date
,-FOOTER_ROWS(ps_global
), 0, sizeof(date
),
8050 prompt
, sel_date_opt
, help
, &flags
);
8053 cmd_cancelled("Selection by date");
8057 help
= (help
== NO_HELP
) ? h_select_date
: NO_HELP
;
8068 if(stream
&& (rawno
= mn_m2raw(msgmap
, msgno
)) > 0L
8069 && rawno
<= stream
->nmsgs
8070 && (mc
= mail_elt(stream
, rawno
))){
8072 /* cache not filled in yet? */
8076 if(stream
->dtb
&& stream
->dtb
->flags
& DR_NEWS
){
8078 ulong2string(mail_uid(stream
, rawno
)),
8080 seq
[sizeof(seq
)-1] = '\0';
8081 mail_fetch_overview(stream
, seq
, NULL
);
8084 strncpy(seq
, long2string(rawno
),
8086 seq
[sizeof(seq
)-1] = '\0';
8087 mail_fetch_fast(stream
, seq
, 0L);
8091 /* mail_date returns fixed field width date */
8092 mail_date(date
, mc
);
8099 case 12 : /* set default to PREVIOUS day */
8103 case 13 : /* set default to NEXT day */
8108 when
= (when
+1) % (sizeof(tense
) / sizeof(struct _tense
));
8115 removing_leading_white_space(date
);
8116 removing_trailing_white_space(date
);
8118 strncpy(date
, defdate
, sizeof(date
));
8119 date
[sizeof(date
)-1] = '\0';
8125 if((pgm
= mail_newsearchpgm()) != NULL
){
8127 short converted_date
;
8129 if(mail_parse_date(&elt
, (unsigned char *) date
)){
8130 converted_date
= mail_shortdate(elt
.year
, elt
.month
, elt
.day
);
8134 pgm
->sentsince
= converted_date
;
8137 pgm
->sentbefore
= converted_date
;
8140 pgm
->senton
= converted_date
;
8143 pgm
->since
= converted_date
;
8146 pgm
->before
= converted_date
;
8149 pgm
->on
= converted_date
;
8153 pgm
->msgno
= (limitsrch
? *limitsrch
: NULL
);
8155 if(ps_global
&& ps_global
->ttyo
){
8156 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
8157 ps_global
->mangled_footer
= 1;
8160 we_cancel
= busy_cue(_("Selecting"), NULL
, 1);
8162 pine_mail_search_full(stream
, NULL
, pgm
, SE_NOPREFETCH
| SE_FREE
);
8167 /* we know this was freed in mail_search, let caller know */
8172 mail_free_searchpgm(&pgm
);
8173 q_status_message1(SM_ORDER
, 3, 3,
8174 _("Invalid date entered: %s"), date
);
8183 * Select by searching in message headers or body
8184 * using the x-gm-ext-1 capaility. This function
8185 * reads the input from the user and passes it to
8186 * the server directly. We need a x-gm-ext-1 variable
8187 * in the search pgm to carry this information to the
8190 * Sets searched bits in mail_elts
8192 * Args limitsrch -- limit search to this searchset
8194 * Returns 0 on success.
8197 select_by_gm_content(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, long int msgno
, SEARCHSET
**limitsrch
)
8199 int r
, we_cancel
= 0, rv
;
8201 char namehdr
[MAILTMPLEN
];
8205 ps_global
->mangled_footer
= 1;
8206 ekey
[0].ch
= ekey
[1].ch
= ekey
[2].ch
= ekey
[3].ch
= -1;
8208 strncpy(tmp
, sel_x_gm_ext
, sizeof(tmp
)-1);
8209 tmp
[sizeof(tmp
)-1] = '\0';
8215 int flags
= OE_APPEND_CURRENT
;
8217 r
= optionally_enter(namehdr
, -FOOTER_ROWS(ps_global
), 0,
8218 sizeof(namehdr
), tmp
, ekey
, help
, &flags
);
8224 help
= (help
== NO_HELP
) ? h_select_by_gm_content
: NO_HELP
;
8229 cmd_cancelled("Selection by content");
8233 removing_leading_white_space(namehdr
);
8235 if ((namehdr
[0] != '\0')
8236 && isspace((unsigned char) namehdr
[strlen(namehdr
) - 1]))
8237 removing_trailing_white_space(namehdr
);
8239 if (namehdr
[0] != '\0')
8243 if(ps_global
&& ps_global
->ttyo
){
8244 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
8245 ps_global
->mangled_footer
= 1;
8248 we_cancel
= busy_cue(_("Selecting"), NULL
, 1);
8250 rv
= agg_text_select(stream
, msgmap
, 'g', namehdr
, 0, 0, NULL
, "utf-8", limitsrch
);
8258 * Select by searching in message headers or body.
8259 * Sets searched bits in mail_elts
8261 * Args limitsrch -- limit search to this searchset
8263 * Returns 0 on success.
8266 select_by_text(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, long int msgno
, SEARCHSET
**limitsrch
)
8268 int r
, ku
, type
, we_cancel
= 0, flags
, rv
, ekeyi
= 0;
8269 int not = 0, me
= 0;
8270 char sstring
[80], tmp
[128];
8271 char *p
, *sval
= NULL
;
8272 char buftmp
[MAILTMPLEN
], namehdr
[80];
8274 ENVELOPE
*env
= NULL
;
8276 unsigned flagsforhist
= 0;
8277 static HISTORY_S
*history
= NULL
;
8278 static char *recip
= "RECIPIENTS";
8279 static char *partic
= "PARTICIPANTS";
8280 static char *match_me
= N_("[Match_My_Addresses]");
8281 static char *dont_match_me
= N_("[Don't_Match_My_Addresses]");
8283 ps_global
->mangled_footer
= 1;
8284 ekey
[0].ch
= ekey
[1].ch
= ekey
[2].ch
= ekey
[3].ch
= -1;
8287 type
= radio_buttons(not ? _(sel_text_not
) : _(sel_text
),
8288 -FOOTER_ROWS(ps_global
), sel_text_opt
,
8289 's', 'x', NO_HELP
, RB_NORM
|RB_RET_HELP
);
8294 helper(h_select_text
, "HELP FOR SELECT BASED ON CONTENTS",
8296 ps_global
->mangled_screen
= 1;
8303 * prepare some friendly defaults...
8306 case 't' : /* address fields, offer To or From */
8311 sval
= (type
== 't') ? "TO" :
8312 (type
== 'f') ? "FROM" :
8313 (type
== 'c') ? "CC" :
8314 (type
== 'r') ? recip
: partic
;
8315 ekey
[ekeyi
].ch
= ctrl('T');
8316 ekey
[ekeyi
].name
= "^T";
8317 ekey
[ekeyi
].rval
= 10;
8318 /* TRANSLATORS: use Current To Address */
8319 ekey
[ekeyi
++].label
= N_("Cur To");
8320 ekey
[ekeyi
].ch
= ctrl('R');
8321 ekey
[ekeyi
].name
= "^R";
8322 ekey
[ekeyi
].rval
= 11;
8323 /* TRANSLATORS: use Current From Address */
8324 ekey
[ekeyi
++].label
= N_("Cur From");
8325 ekey
[ekeyi
].ch
= ctrl('W');
8326 ekey
[ekeyi
].name
= "^W";
8327 ekey
[ekeyi
].rval
= 12;
8328 /* TRANSLATORS: use Current Cc Address */
8329 ekey
[ekeyi
++].label
= N_("Cur Cc");
8330 ekey
[ekeyi
].ch
= ctrl('Y');
8331 ekey
[ekeyi
].name
= "^Y";
8332 ekey
[ekeyi
].rval
= 13;
8333 /* TRANSLATORS: Match Me means match my address */
8334 ekey
[ekeyi
++].label
= N_("Match Me");
8336 ekey
[ekeyi
].name
= "";
8337 ekey
[ekeyi
].rval
= 0;
8338 ekey
[ekeyi
++].label
= "";
8343 ekey
[ekeyi
].ch
= ctrl('X');
8344 ekey
[ekeyi
].name
= "^X";
8345 ekey
[ekeyi
].rval
= 14;
8346 /* TRANSLATORS: use Current Subject */
8347 ekey
[ekeyi
++].label
= N_("Cur Subject");
8359 strncpy(tmp
, "Name of HEADER to match : ", sizeof(tmp
)-1);
8360 tmp
[sizeof(tmp
)-1] = '\0';
8361 flags
= OE_APPEND_CURRENT
;
8367 r
= optionally_enter(namehdr
, -FOOTER_ROWS(ps_global
), 0,
8368 sizeof(namehdr
), tmp
, ekey
, NO_HELP
, &flags
);
8370 cmd_cancelled("Selection by text");
8373 removing_leading_white_space(namehdr
);
8375 while ((namehdr
[0] != '\0') && /* remove trailing ":" */
8376 (namehdr
[strlen(namehdr
) - 1] == ':'))
8377 namehdr
[strlen(namehdr
) - 1] = '\0';
8378 if ((namehdr
[0] != '\0')
8379 && isspace((unsigned char) namehdr
[strlen(namehdr
) - 1]))
8380 removing_trailing_white_space(namehdr
);
8384 if (strchr(namehdr
,' ') || strchr(namehdr
,'\t') ||
8385 strchr(namehdr
,':'))
8387 if (namehdr
[0] == '\0')
8397 dprint((1,"\n - BOTCH: select_text unrecognized option\n"));
8401 ekey
[ekeyi
].ch
= KEY_UP
;
8402 ekey
[ekeyi
].rval
= 30;
8403 ekey
[ekeyi
].name
= "";
8405 ekey
[ekeyi
++].label
= "";
8407 ekey
[ekeyi
].ch
= KEY_DOWN
;
8408 ekey
[ekeyi
].rval
= 31;
8409 ekey
[ekeyi
].name
= "";
8410 ekey
[ekeyi
++].label
= "";
8412 ekey
[ekeyi
].ch
= -1;
8416 init_hist(&history
, HISTSIZE
);
8418 if(ekey
[0].ch
> -1 && msgno
> 0L
8419 && !(env
=pine_mail_fetchstructure(stream
,mn_m2raw(msgmap
,msgno
),
8428 /* TRANSLATORS: character String in message <message number> to NOT match : " */
8429 snprintf(tmp
, sizeof(tmp
), "String in message %s to NOT match : ", sval
);
8431 snprintf(tmp
, sizeof(tmp
), "String in message %s to match : ", sval
);
8433 if(items_in_hist(history
) > 0){
8434 ekey
[ku
].name
= HISTORY_UP_KEYNAME
;
8435 ekey
[ku
].label
= HISTORY_KEYLABEL
;
8436 ekey
[ku
+1].name
= HISTORY_DOWN_KEYNAME
;
8437 ekey
[ku
+1].label
= HISTORY_KEYLABEL
;
8441 ekey
[ku
].label
= "";
8442 ekey
[ku
+1].name
= "";
8443 ekey
[ku
+1].label
= "";
8446 flags
= OE_APPEND_CURRENT
| OE_KEEP_TRAILING_SPACE
;
8447 r
= optionally_enter(sstring
, -FOOTER_ROWS(ps_global
), 0,
8448 79, tmp
, ekey
, help
, &flags
);
8450 if(me
&& r
== 0 && ((!not && strcmp(sstring
, _(match_me
))) || (not && strcmp(sstring
, _(dont_match_me
)))))
8455 help
= (help
== NO_HELP
)
8457 ? ((type
== 'f') ? h_select_txt_not_from
8458 : (type
== 't') ? h_select_txt_not_to
8459 : (type
== 'c') ? h_select_txt_not_cc
8460 : (type
== 's') ? h_select_txt_not_subj
8461 : (type
== 'a') ? h_select_txt_not_all
8462 : (type
== 'r') ? h_select_txt_not_recip
8463 : (type
== 'p') ? h_select_txt_not_partic
8464 : (type
== 'b') ? h_select_txt_not_body
8466 : ((type
== 'f') ? h_select_txt_from
8467 : (type
== 't') ? h_select_txt_to
8468 : (type
== 'c') ? h_select_txt_cc
8469 : (type
== 's') ? h_select_txt_subj
8470 : (type
== 'a') ? h_select_txt_all
8471 : (type
== 'r') ? h_select_txt_recip
8472 : (type
== 'p') ? h_select_txt_partic
8473 : (type
== 'b') ? h_select_txt_body
8480 case 10 : /* To: default */
8481 if(env
&& env
->to
&& env
->to
->mailbox
){
8482 snprintf(sstring
, sizeof(sstring
), "%s%s%s", env
->to
->mailbox
,
8483 env
->to
->host
? "@" : "",
8484 env
->to
->host
? env
->to
->host
: "");
8485 sstring
[sizeof(sstring
)-1] = '\0';
8489 case 11 : /* From: default */
8490 if(env
&& env
->from
&& env
->from
->mailbox
){
8491 snprintf(sstring
, sizeof(sstring
), "%s%s%s", env
->from
->mailbox
,
8492 env
->from
->host
? "@" : "",
8493 env
->from
->host
? env
->from
->host
: "");
8494 sstring
[sizeof(sstring
)-1] = '\0';
8498 case 12 : /* Cc: default */
8499 if(env
&& env
->cc
&& env
->cc
->mailbox
){
8500 snprintf(sstring
, sizeof(sstring
), "%s%s%s", env
->cc
->mailbox
,
8501 env
->cc
->host
? "@" : "",
8502 env
->cc
->host
? env
->cc
->host
: "");
8503 sstring
[sizeof(sstring
)-1] = '\0';
8507 case 13 : /* Match my addresses */
8509 snprintf(sstring
, sizeof(sstring
), "%s", not ? _(dont_match_me
) : _(match_me
));
8512 case 14 : /* Subject: default */
8513 if(env
&& env
->subject
&& env
->subject
[0]){
8516 q
= (char *) rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
,
8517 SIZEOF_20KBUF
, env
->subject
);
8518 snprintf(sstring
, sizeof(sstring
), "%s", q
);
8519 sstring
[sizeof(sstring
)-1] = '\0';
8525 flagsforhist
= (not ? 0x1 : 0) + (me
? 0x2 : 0);
8526 if((p
= get_prev_hist(history
, sstring
, flagsforhist
, NULL
)) != NULL
){
8527 strncpy(sstring
, p
, sizeof(sstring
));
8528 sstring
[sizeof(sstring
)-1] = '\0';
8529 if(history
->hist
[history
->curindex
]){
8530 flagsforhist
= history
->hist
[history
->curindex
]->flags
;
8531 not = (flagsforhist
& 0x1) ? 1 : 0;
8532 me
= (flagsforhist
& 0x2) ? 1 : 0;
8541 flagsforhist
= (not ? 0x1 : 0) + (me
? 0x2 : 0);
8542 if((p
= get_next_hist(history
, sstring
, flagsforhist
, NULL
)) != NULL
){
8543 strncpy(sstring
, p
, sizeof(sstring
));
8544 sstring
[sizeof(sstring
)-1] = '\0';
8545 if(history
->hist
[history
->curindex
]){
8546 flagsforhist
= history
->hist
[history
->curindex
]->flags
;
8547 not = (flagsforhist
& 0x1) ? 1 : 0;
8548 me
= (flagsforhist
& 0x2) ? 1 : 0;
8560 if(r
== 1 || sstring
[0] == '\0')
8567 if(type
== 'x' || r
== 'x'){
8568 cmd_cancelled("Selection by text");
8572 if(ps_global
&& ps_global
->ttyo
){
8573 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
8574 ps_global
->mangled_footer
= 1;
8577 we_cancel
= busy_cue(_("Selecting"), NULL
, 1);
8579 flagsforhist
= (not ? 0x1 : 0) + (me
? 0x2 : 0);
8580 save_hist(history
, sstring
, flagsforhist
, NULL
);
8582 rv
= agg_text_select(stream
, msgmap
, type
, namehdr
, not, me
, sstring
, "utf-8", limitsrch
);
8591 * Select by message size.
8592 * Sets searched bits in mail_elts
8594 * Args limitsrch -- limit search to this searchset
8596 * Returns 0 on success.
8599 select_by_size(MAILSTREAM
*stream
, SEARCHSET
**limitsrch
)
8601 int r
, large
= 1, we_cancel
= 0;
8602 unsigned long n
, mult
= 1L, numerator
= 0L, divisor
= 1L;
8603 char size
[16], numbers
[80], *p
, *t
;
8606 long flags
= (SE_NOPREFETCH
| SE_FREE
);
8609 ps_global
->mangled_footer
= 1;
8613 int flgs
= OE_APPEND_CURRENT
;
8615 sel_size_opt
[1].label
= large
? sel_size_smaller
: sel_size_larger
;
8617 r
= optionally_enter(numbers
, -FOOTER_ROWS(ps_global
), 0,
8618 sizeof(numbers
), large
? _(select_size_larger_msg
)
8619 : _(select_size_smaller_msg
),
8620 sel_size_opt
, help
, &flgs
);
8630 help
= (help
== NO_HELP
) ? (large
? h_select_by_larger_size
8631 : h_select_by_smaller_size
)
8636 for(t
= p
= numbers
; *p
; p
++) /* strip whitespace */
8637 if(!isspace((unsigned char)*p
))
8642 if(r
== 1 || numbers
[0] == '\0'){
8643 cmd_cancelled("Selection by size");
8650 if(numbers
[0] == '-'){
8651 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
8652 _("Invalid size entered: %s"), numbers
);
8659 while(*p
&& isdigit((unsigned char)*p
))
8664 if(size
[0] == '\0' && *p
== '.' && isdigit(*(p
+1))){
8669 if(size
[0] == '\0'){
8670 q_status_message1(SM_ORDER
| SM_DING
, 0, 2,
8671 _("Invalid size entered: %s"), numbers
);
8675 n
= strtoul(size
, (char **)NULL
, 10);
8680 * We probably ought to just use atof() to convert 1.1 into a
8681 * double, but since we haven't used atof() anywhere else I'm
8682 * reluctant to use it because of portability concerns.
8686 while(*p
&& isdigit((unsigned char)*p
)){
8694 numerator
= strtoul(size
, (char **)NULL
, 10);
8714 n
= n
* mult
+ (numerator
* mult
) / divisor
;
8716 pgm
= mail_newsearchpgm();
8722 if(is_imap_stream(stream
) && !modern_imap_stream(stream
))
8723 flags
|= SE_NOSERVER
;
8725 if(ps_global
&& ps_global
->ttyo
){
8726 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
8727 ps_global
->mangled_footer
= 1;
8730 we_cancel
= busy_cue(_("Selecting"), NULL
, 1);
8732 pgm
->msgno
= (limitsrch
? *limitsrch
: NULL
);
8733 pine_mail_search_full(stream
, NULL
, pgm
, SE_NOPREFETCH
| SE_FREE
);
8734 /* we know this was freed in mail_search, let caller know */
8746 * visible_searchset -- return c-client search set unEXLDed
8750 visible_searchset(MAILSTREAM
*stream
, MSGNO_S
*msgmap
)
8753 SEARCHSET
*full_set
= NULL
, **set
;
8756 * If we're talking to anything other than a server older than
8757 * imap 4rev1, build a searchset otherwise it'll choke.
8759 if(!(is_imap_stream(stream
) && !modern_imap_stream(stream
))){
8760 if(any_lflagged(msgmap
, MN_EXLD
)){
8761 for(n
= 1L, set
= &full_set
, run
= 0L; n
<= stream
->nmsgs
; n
++)
8762 if(get_lflag(stream
, NULL
, n
, MN_EXLD
)){
8763 if(run
){ /* previous NOT excluded? */
8765 (*set
)->last
= n
- 1L;
8767 set
= &(*set
)->next
;
8771 else if(run
++){ /* next in run */
8774 else{ /* start of run */
8775 *set
= mail_newsearchset();
8780 full_set
= mail_newsearchset();
8781 full_set
->first
= 1L;
8782 full_set
->last
= stream
->nmsgs
;
8791 * Select by message status bits.
8792 * Sets searched bits in mail_elts
8794 * Args limitsrch -- limit search to this searchset
8796 * Returns 0 on success.
8799 select_by_status(MAILSTREAM
*stream
, SEARCHSET
**limitsrch
)
8801 int s
, not = 0, we_cancel
= 0, rv
;
8804 s
= radio_buttons((not) ? _(sel_flag_not
) : _(sel_flag
),
8805 -FOOTER_ROWS(ps_global
), sel_flag_opt
, '*', 'x',
8806 NO_HELP
, RB_NORM
|RB_RET_HELP
);
8809 cmd_cancelled("Selection by status");
8813 helper(h_select_status
, _("HELP FOR SELECT BASED ON STATUS"),
8815 ps_global
->mangled_screen
= 1;
8823 if(ps_global
&& ps_global
->ttyo
){
8824 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
8825 ps_global
->mangled_footer
= 1;
8828 we_cancel
= busy_cue(_("Selecting"), NULL
, 1);
8829 rv
= agg_flag_select(stream
, not, s
, limitsrch
);
8838 * Select by rule. Usually srch, indexcolor, and roles would be most useful.
8839 * Sets searched bits in mail_elts
8841 * Args limitsrch -- limit search to this searchset
8843 * Returns 0 on success.
8846 select_by_rule(MAILSTREAM
*stream
, SEARCHSET
**limitsrch
)
8848 char rulenick
[1000], *nick
;
8850 int r
, not = 0, we_cancel
= 0, rflags
= ROLE_DO_SRCH
8858 ps_global
->mangled_footer
= 1;
8863 oe_flags
= OE_APPEND_CURRENT
;
8864 r
= optionally_enter(rulenick
, -FOOTER_ROWS(ps_global
), 0,
8866 not ? _("Rule to NOT match: ")
8867 : _("Rule to match: "),
8868 sel_key_opt
, NO_HELP
, &oe_flags
);
8871 /* select rulenick from a list */
8872 if((nick
=choose_a_rule(rflags
)) != NULL
){
8873 strncpy(rulenick
, nick
, sizeof(rulenick
)-1);
8874 rulenick
[sizeof(rulenick
)-1] = '\0';
8875 fs_give((void **) &nick
);
8884 helper(h_select_rule
, _("HELP FOR SELECT BY RULE"), HLPD_SIMPLE
);
8885 ps_global
->mangled_screen
= 1;
8888 cmd_cancelled("Selection by Rule");
8892 removing_leading_and_trailing_white_space(rulenick
);
8894 }while(r
== 3 || r
== 4 || r
== '!');
8898 * The approach of requiring a nickname instead of just allowing the
8899 * user to select from the list of rules has the drawback that a rule
8900 * may not have a nickname, or there may be more than one rule with
8901 * the same nickname. However, it has the benefit of allowing the user
8902 * to type in the nickname and, most importantly, allows us to set
8903 * up the ! (not). We could incorporate the ! into the selection
8904 * screen, but this is easier and also allows the typing of nicks.
8905 * User can just set up nicknames if they want to use this feature.
8907 patgrp
= nick_to_patgrp(rulenick
, rflags
);
8910 if(ps_global
&& ps_global
->ttyo
){
8911 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
8912 ps_global
->mangled_footer
= 1;
8915 we_cancel
= busy_cue(_("Selecting"), NULL
, 1);
8916 match_pattern(patgrp
, stream
, limitsrch
? *limitsrch
: 0, NULL
,
8918 (not ? MP_NOT
: 0) | SE_NOPREFETCH
);
8919 free_patgrp(&patgrp
);
8924 if(limitsrch
&& *limitsrch
){
8925 mail_free_searchset(limitsrch
);
8934 * Allow user to choose a rule from their list of rules.
8936 * Returns an allocated rule nickname on success, NULL otherwise.
8939 choose_a_rule(int rflags
)
8941 char *choice
= NULL
;
8942 char **rule_list
, **lp
;
8947 if(!(nonempty_patterns(rflags
, &pstate
) && first_pattern(&pstate
))){
8948 q_status_message(SM_ORDER
, 3, 3,
8949 _("No rules available. Use Setup/Rules to add some."));
8954 * Build a list of rules to choose from.
8957 for(pat
= first_pattern(&pstate
); pat
; pat
= next_pattern(&pstate
))
8961 q_status_message(SM_ORDER
, 3, 4, _("No rules defined, use Setup/Rules"));
8965 lp
= rule_list
= (char **) fs_get((cnt
+ 1) * sizeof(*rule_list
));
8966 memset(rule_list
, 0, (cnt
+1) * sizeof(*rule_list
));
8968 for(pat
= first_pattern(&pstate
); pat
; pat
= next_pattern(&pstate
))
8969 *lp
++ = cpystr((pat
->patgrp
&& pat
->patgrp
->nick
)
8970 ? pat
->patgrp
->nick
: "?");
8972 /* TRANSLATORS: SELECT A RULE is a screen title
8973 TRANSLATORS: Print something1 using something2.
8974 "rules" is something1 */
8975 choice
= choose_item_from_list(rule_list
, NULL
, _("SELECT A RULE"),
8976 _("rules"), h_select_rule_screen
,
8977 _("HELP FOR SELECTING A RULE NICKNAME"), NULL
);
8980 q_status_message(SM_ORDER
, 1, 4, "No choice");
8982 free_list_array(&rule_list
);
8989 * Select by current thread.
8990 * Sets searched bits in mail_elts for this entire thread
8992 * Args limitsrch -- limit search to this searchset
8994 * Returns 0 on success.
8997 select_by_thread(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, SEARCHSET
**limitsrch
)
9000 PINETHRD_S
*thrd
= NULL
;
9007 for(n
= 1L; n
<= stream
->nmsgs
; n
++)
9008 if((mc
= mail_elt(stream
, n
)) != NULL
)
9009 mc
->searched
= 0; /* clear searched bits */
9011 thrd
= fetch_thread(stream
, mn_m2raw(msgmap
, mn_get_cur(msgmap
)));
9012 if(thrd
&& thrd
->top
&& thrd
->top
!= thrd
->rawno
)
9013 thrd
= fetch_thread(stream
, thrd
->top
);
9016 * This doesn't unselect if the thread is already selected
9017 * (like select current does), it always selects.
9018 * There is no way to select ! this thread.
9021 set_search_bit_for_thread(stream
, thrd
, limitsrch
);
9030 * Select by message keywords.
9031 * Sets searched bits in mail_elts
9033 * Args limitsrch -- limit search to this searchset
9035 * Returns 0 on success.
9038 select_by_keyword(MAILSTREAM
*stream
, SEARCHSET
**limitsrch
)
9040 int r
, not = 0, we_cancel
= 0;
9041 char keyword
[MAXUSERFLAG
+1], *kword
;
9042 char *error
= NULL
, *p
, *prompt
;
9047 ps_global
->mangled_footer
= 1;
9054 q_status_message(SM_ORDER
, 3, 4, error
);
9055 fs_give((void **) &error
);
9058 if(F_ON(F_FLAG_SCREEN_KW_SHORTCUT
, ps_global
) && ps_global
->keywords
){
9060 prompt
= _("Keyword (or keyword initial) to NOT match: ");
9062 prompt
= _("Keyword (or keyword initial) to match: ");
9066 prompt
= _("Keyword to NOT match: ");
9068 prompt
= _("Keyword to match: ");
9071 oe_flags
= OE_APPEND_CURRENT
;
9072 r
= optionally_enter(keyword
, -FOOTER_ROWS(ps_global
), 0,
9074 prompt
, sel_key_opt
, help
, &oe_flags
);
9077 /* select keyword from a list */
9078 if((kword
=choose_a_keyword()) != NULL
){
9079 strncpy(keyword
, kword
, sizeof(keyword
)-1);
9080 keyword
[sizeof(keyword
)-1] = '\0';
9081 fs_give((void **) &kword
);
9090 help
= help
== NO_HELP
? h_select_keyword
: NO_HELP
;
9092 cmd_cancelled("Selection by keyword");
9096 removing_leading_and_trailing_white_space(keyword
);
9098 }while(r
== 3 || r
== 4 || r
== '!' || keyword_check(keyword
, &error
));
9101 if(F_ON(F_FLAG_SCREEN_KW_SHORTCUT
, ps_global
) && ps_global
->keywords
){
9102 p
= initial_to_keyword(keyword
);
9104 strncpy(keyword
, p
, sizeof(keyword
)-1);
9105 keyword
[sizeof(keyword
)-1] = '\0';
9110 * We want to check the keyword, not the nickname of the keyword,
9111 * so convert it to the keyword if necessary.
9113 p
= nick_to_keyword(keyword
);
9115 strncpy(keyword
, p
, sizeof(keyword
)-1);
9116 keyword
[sizeof(keyword
)-1] = '\0';
9119 pgm
= mail_newsearchpgm();
9121 pgm
->unkeyword
= mail_newstringlist();
9122 pgm
->unkeyword
->text
.data
= (unsigned char *) cpystr(keyword
);
9123 pgm
->unkeyword
->text
.size
= strlen(keyword
);
9126 pgm
->keyword
= mail_newstringlist();
9127 pgm
->keyword
->text
.data
= (unsigned char *) cpystr(keyword
);
9128 pgm
->keyword
->text
.size
= strlen(keyword
);
9131 if(ps_global
&& ps_global
->ttyo
){
9132 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
9133 ps_global
->mangled_footer
= 1;
9136 we_cancel
= busy_cue(_("Selecting"), NULL
, 1);
9138 pgm
->msgno
= (limitsrch
? *limitsrch
: NULL
);
9139 pine_mail_search_full(stream
, "UTF-8", pgm
, SE_NOPREFETCH
| SE_FREE
);
9140 /* we know this was freed in mail_search, let caller know */
9152 * Allow user to choose a keyword from their list of keywords.
9154 * Returns an allocated keyword on success, NULL otherwise.
9157 choose_a_keyword(void)
9159 char *choice
= NULL
;
9160 char **keyword_list
, **lp
;
9165 * Build a list of keywords to choose from.
9168 for(cnt
= 0, kw
= ps_global
->keywords
; kw
; kw
= kw
->next
)
9172 q_status_message(SM_ORDER
, 3, 4,
9173 _("No keywords defined, use \"keywords\" option in Setup/Config"));
9177 lp
= keyword_list
= (char **) fs_get((cnt
+ 1) * sizeof(*keyword_list
));
9178 memset(keyword_list
, 0, (cnt
+1) * sizeof(*keyword_list
));
9180 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
)
9181 *lp
++ = cpystr(kw
->nick
? kw
->nick
: kw
->kw
? kw
->kw
: "");
9183 /* TRANSLATORS: SELECT A KEYWORD is a screen title
9184 TRANSLATORS: Print something1 using something2.
9185 "keywords" is something1 */
9186 choice
= choose_item_from_list(keyword_list
, NULL
, _("SELECT A KEYWORD"),
9187 _("keywords"), h_select_keyword_screen
,
9188 _("HELP FOR SELECTING A KEYWORD"), NULL
);
9191 q_status_message(SM_ORDER
, 1, 4, "No choice");
9193 free_list_array(&keyword_list
);
9200 * Allow user to choose a list of keywords from their list of keywords.
9202 * Returns allocated list.
9205 choose_list_of_keywords(void)
9207 LIST_SEL_S
*listhead
, *ls
, *p
;
9213 * Build a list of keywords to choose from.
9216 p
= listhead
= NULL
;
9217 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
){
9219 ls
= (LIST_SEL_S
*) fs_get(sizeof(*ls
));
9220 memset(ls
, 0, sizeof(*ls
));
9221 ls
->item
= cpystr(kw
->nick
? kw
->nick
: kw
->kw
? kw
->kw
: "");
9234 /* TRANSLATORS: SELECT KEYWORDS is a screen title
9235 Print something1 using something2.
9236 "keywords" is something1 */
9237 if(!select_from_list_screen(listhead
, SFL_ALLOW_LISTMODE
,
9238 _("SELECT KEYWORDS"), _("keywords"),
9239 h_select_multkeyword_screen
,
9240 _("HELP FOR SELECTING KEYWORDS"), NULL
)){
9241 for(cnt
= 0, p
= listhead
; p
; p
= p
->next
)
9245 ret
= (char **) fs_get((cnt
+1) * sizeof(*ret
));
9246 memset(ret
, 0, (cnt
+1) * sizeof(*ret
));
9247 for(i
= 0, p
= listhead
; p
; p
= p
->next
)
9249 ret
[i
++] = cpystr(p
->item
? p
->item
: "");
9252 free_list_sel(&listhead
);
9259 * Allow user to choose a charset
9261 * Returns an allocated charset on success, NULL otherwise.
9264 choose_a_charset(int which_charsets
)
9266 char *choice
= NULL
;
9267 char **charset_list
, **lp
;
9272 * Build a list of charsets to choose from.
9275 for(cnt
= 0, cs
= utf8_charset(NIL
); cs
&& cs
->name
; cs
++){
9276 if(!(cs
->flags
& (CF_UNSUPRT
|CF_NOEMAIL
))
9277 && ((which_charsets
& CAC_ALL
)
9278 || (which_charsets
& CAC_POSTING
9279 && cs
->flags
& CF_POSTING
)
9280 || (which_charsets
& CAC_DISPLAY
9281 && cs
->type
!= CT_2022
9282 && (cs
->flags
& (CF_PRIMARY
|CF_DISPLAY
)) == (CF_PRIMARY
|CF_DISPLAY
))))
9287 q_status_message(SM_ORDER
, 3, 4,
9288 _("No charsets found? Enter charset manually."));
9292 lp
= charset_list
= (char **) fs_get((cnt
+ 1) * sizeof(*charset_list
));
9293 memset(charset_list
, 0, (cnt
+1) * sizeof(*charset_list
));
9295 for(cs
= utf8_charset(NIL
); cs
&& cs
->name
; cs
++){
9296 if(!(cs
->flags
& (CF_UNSUPRT
|CF_NOEMAIL
))
9297 && ((which_charsets
& CAC_ALL
)
9298 || (which_charsets
& CAC_POSTING
9299 && cs
->flags
& CF_POSTING
)
9300 || (which_charsets
& CAC_DISPLAY
9301 && cs
->type
!= CT_2022
9302 && (cs
->flags
& (CF_PRIMARY
|CF_DISPLAY
)) == (CF_PRIMARY
|CF_DISPLAY
))))
9303 *lp
++ = cpystr(cs
->name
);
9306 /* TRANSLATORS: SELECT A CHARACTER SET is a screen title
9307 TRANSLATORS: Print something1 using something2.
9308 "character sets" is something1 */
9309 choice
= choose_item_from_list(charset_list
, NULL
, _("SELECT A CHARACTER SET"),
9310 _("character sets"), h_select_charset_screen
,
9311 _("HELP FOR SELECTING A CHARACTER SET"), NULL
);
9314 q_status_message(SM_ORDER
, 1, 4, "No choice");
9316 free_list_array(&charset_list
);
9323 * Allow user to choose a list of character sets and/or scripts
9325 * Returns allocated list.
9328 choose_list_of_charsets(void)
9330 LIST_SEL_S
*listhead
, *ls
, *p
;
9332 int cnt
, i
, got_one
;
9337 char buf
[1024], *folded
;
9340 * Build a list of charsets to choose from.
9343 p
= listhead
= NULL
;
9345 /* this width is determined by select_from_list_screen() */
9346 width
= ps_global
->ttyo
->screen_cols
- 4;
9348 /* first comes a list of scripts (sets of character sets) */
9349 for(s
= utf8_script(NIL
); s
&& s
->name
; s
++){
9351 limit
= sizeof(buf
)-1;
9353 memset(q
, 0, limit
+1);
9356 sstrncpy(&q
, s
->name
, limit
);
9359 sstrncpy(&q
, " (", limit
-(q
-buf
));
9360 sstrncpy(&q
, s
->description
, limit
-(q
-buf
));
9361 sstrncpy(&q
, ")", limit
-(q
-buf
));
9364 /* add the list of charsets that are in this script */
9366 for(cs
= utf8_charset(NIL
);
9367 cs
&& cs
->name
&& (q
-buf
) < limit
; cs
++){
9368 if(cs
->script
& s
->script
){
9370 * Filter out some un-useful members of the list.
9371 * UTF-7 and UTF-8 weren't actually in the list at the
9372 * time this was written. Just making sure.
9374 if(!strucmp(cs
->name
, "ISO-2022-JP-2")
9375 || !strucmp(cs
->name
, "UTF-7")
9376 || !strucmp(cs
->name
, "UTF-8"))
9380 sstrncpy(&q
, " ", limit
-(q
-buf
));
9383 sstrncpy(&q
, " {", limit
-(q
-buf
));
9386 sstrncpy(&q
, cs
->name
, limit
-(q
-buf
));
9391 sstrncpy(&q
, "}", limit
-(q
-buf
));
9393 /* fold this line so that it can all be seen on the screen */
9394 folded
= fold(buf
, width
, width
, "", " ", FLD_NONE
);
9397 while(t
&& *t
&& (q
= strindex(t
, '\n')) != NULL
){
9400 ls
= (LIST_SEL_S
*) fs_get(sizeof(*ls
));
9401 memset(ls
, 0, sizeof(*ls
));
9403 ls
->item
= cpystr(s
->name
);
9405 ls
->flags
= SFL_NOSELECT
;
9407 ls
->display_item
= cpystr(t
);
9417 listhead
= (LIST_SEL_S
*) fs_get(sizeof(*ls
));
9418 memset(listhead
, 0, sizeof(*listhead
));
9419 listhead
->flags
= SFL_NOSELECT
;
9420 listhead
->display_item
=
9421 cpystr(_("Scripts representing groups of related character sets"));
9422 listhead
->next
= (LIST_SEL_S
*) fs_get(sizeof(*ls
));
9423 memset(listhead
->next
, 0, sizeof(*listhead
));
9424 listhead
->next
->flags
= SFL_NOSELECT
;
9425 listhead
->next
->display_item
=
9426 cpystr(repeat_char(width
, '-'));
9428 listhead
->next
->next
= ls
;
9433 fs_give((void **) &folded
);
9437 ls
= (LIST_SEL_S
*) fs_get(sizeof(*ls
));
9438 memset(ls
, 0, sizeof(*ls
));
9439 ls
->flags
= SFL_NOSELECT
;
9447 ls
= (LIST_SEL_S
*) fs_get(sizeof(*ls
));
9448 memset(ls
, 0, sizeof(*ls
));
9449 ls
->flags
= SFL_NOSELECT
;
9451 cpystr(_("Individual character sets, may be mixed with scripts"));
9455 ls
= (LIST_SEL_S
*) fs_get(sizeof(*ls
));
9456 memset(ls
, 0, sizeof(*ls
));
9457 ls
->flags
= SFL_NOSELECT
;
9459 cpystr(repeat_char(width
, '-'));
9463 /* then comes a list of individual character sets */
9464 for(cs
= utf8_charset(NIL
); cs
&& cs
->name
; cs
++){
9465 ls
= (LIST_SEL_S
*) fs_get(sizeof(*ls
));
9466 memset(ls
, 0, sizeof(*ls
));
9467 ls
->item
= cpystr(cs
->name
);
9480 /* TRANSLATORS: SELECT CHARACTER SETS is a screen title
9481 Print something1 using something2.
9482 "character sets" is something1 */
9483 if(!select_from_list_screen(listhead
, SFL_ALLOW_LISTMODE
,
9484 _("SELECT CHARACTER SETS"), _("character sets"),
9485 h_select_multcharsets_screen
,
9486 _("HELP FOR SELECTING CHARACTER SETS"), NULL
)){
9487 for(cnt
= 0, p
= listhead
; p
; p
= p
->next
)
9491 ret
= (char **) fs_get((cnt
+1) * sizeof(*ret
));
9492 memset(ret
, 0, (cnt
+1) * sizeof(*ret
));
9493 for(i
= 0, p
= listhead
; p
; p
= p
->next
)
9495 ret
[i
++] = cpystr(p
->item
? p
->item
: "");
9498 free_list_sel(&listhead
);
9503 /* Report quota summary resources in an IMAP server */
9506 cmd_quota (struct pine
*state
)
9508 QUOTALIST
*imapquota
;
9513 if(!state
->mail_stream
|| !is_imap_stream(state
->mail_stream
)){
9514 q_status_message(SM_ORDER
, 1, 5, "Quota only available for IMAP folders");
9518 if (state
->mail_stream
9519 && !sp_dead_stream(state
->mail_stream
)
9520 && state
->mail_stream
->mailbox
9521 && *state
->mail_stream
->mailbox
9522 && mail_valid_net_parse(state
->mail_stream
->mailbox
, &mb
))
9523 imap_getquotaroot(state
->mail_stream
, mb
.mailbox
);
9525 if(!state
->quota
) /* failed ? */
9526 return; /* go back... */
9528 if(!(store
= so_get(CharStar
, NULL
, EDIT_ACCESS
))){
9529 q_status_message(SM_ORDER
| SM_DING
, 3, 3, "Error allocating space.");
9533 so_puts(store
, "Quota Report for ");
9534 so_puts(store
, state
->mail_stream
->original_mailbox
);
9535 so_puts(store
, "\n\n");
9537 for (imapquota
= state
->quota
; imapquota
; imapquota
= imapquota
->next
){
9539 so_puts(store
, _("Resource : "));
9540 so_puts(store
, imapquota
->name
);
9541 so_writec('\n', store
);
9543 so_puts(store
, _("Usage : "));
9544 so_puts(store
, long2string(imapquota
->usage
));
9545 if(!strucmp(imapquota
->name
,"STORAGE"))
9546 so_puts(store
, " KiB ");
9547 if(!strucmp(imapquota
->name
,"MESSAGE")){
9548 so_puts(store
, _(" message"));
9549 if(imapquota
->usage
!= 1)
9550 so_puts(store
, _("s ")); /* plural */
9552 so_puts(store
, _(" "));
9554 so_writec('(', store
);
9555 so_puts(store
, long2string(100*imapquota
->usage
/imapquota
->limit
));
9556 so_puts(store
, "%)\n");
9558 so_puts(store
, _("Limit : "));
9559 so_puts(store
, long2string(imapquota
->limit
));
9560 if(!strucmp(imapquota
->name
,"STORAGE"))
9561 so_puts(store
, " KiB\n\n");
9562 if(!strucmp(imapquota
->name
,"MESSAGE")){
9563 so_puts(store
, _(" message"));
9564 if(imapquota
->usage
!= 1)
9565 so_puts(store
, _("s\n\n")); /* plural */
9567 so_puts(store
, _("\n\n"));
9571 memset(&sargs
, 0, sizeof(SCROLL_S
));
9572 sargs
.text
.text
= so_text(store
);
9573 sargs
.text
.src
= CharStar
;
9574 sargs
.text
.desc
= _("Quota Resources Summary");
9575 sargs
.bar
.title
= _("QUOTA SUMMARY");
9576 sargs
.proc
.tool
= NULL
;
9577 sargs
.help
.text
= h_quota_command
;
9578 sargs
.help
.title
= NULL
;
9579 sargs
.keys
.menu
= NULL
;
9580 setbitmap(sargs
.keys
.bitmap
);
9586 mail_free_quotalist(&(state
->quota
));
9589 /*----------------------------------------------------------------------
9590 Prompt the user for the type of sort he desires
9592 Args: state -- pine state pointer
9593 q1 -- Line to prompt on
9595 Returns 0 if it was cancelled, 1 otherwise.
9598 select_sort(struct pine
*state
, int ql
, SortOrder
*sort
, int *rev
)
9600 char prompt
[200], tmp
[3], *p
;
9602 int deefault
= 'a', retval
= 1;
9607 DLG_SORTPARAM sortsel
;
9609 if (mswin_usedialog ()) {
9611 sortsel
.reverse
= mn_get_revsort (state
->msgmap
);
9612 sortsel
.cursort
= mn_get_sort (state
->msgmap
);
9613 /* assumption here that HelpType is char ** */
9614 sortsel
.helptext
= h_select_sort
;
9617 if ((retval
= os_sortdialog (&sortsel
))) {
9618 *sort
= sortsel
.cursort
;
9619 *rev
= sortsel
.reverse
;
9626 /*----- String together the prompt ------*/
9628 if(F_ON(F_USE_FK
,ps_global
))
9629 strncpy(prompt
, _("Choose type of sort : "), sizeof(prompt
));
9631 strncpy(prompt
, _("Choose type of sort, or 'R' to reverse current sort : "),
9634 for(i
= 0; state
->sort_types
[i
] != EndofList
; i
++) {
9636 p
= sorts
[i
].label
= sort_name(state
->sort_types
[i
]);
9637 while(*(p
+1) && islower((unsigned char)*p
))
9640 sorts
[i
].ch
= tolower((unsigned char)(tmp
[0] = *p
));
9641 sorts
[i
].name
= cpystr(tmp
);
9643 if(mn_get_sort(state
->msgmap
) == state
->sort_types
[i
])
9644 deefault
= sorts
[i
].rval
;
9648 sorts
[i
].rval
= 'r';
9649 sorts
[i
].name
= cpystr("R");
9650 if(F_ON(F_USE_FK
,ps_global
))
9651 sorts
[i
].label
= N_("Reverse");
9653 sorts
[i
].label
= "";
9656 help
= h_select_sort
;
9658 if((F_ON(F_USE_FK
,ps_global
)
9659 && ((s
= double_radio_buttons(prompt
,ql
,sorts
,deefault
,'x',
9660 help
,RB_NORM
)) != 'x'))
9662 (F_OFF(F_USE_FK
,ps_global
)
9663 && ((s
= radio_buttons(prompt
,ql
,sorts
,deefault
,'x',
9664 help
,RB_NORM
)) != 'x'))){
9665 state
->mangled_body
= 1; /* signal screen's changed */
9667 *rev
= !mn_get_revsort(state
->msgmap
);
9669 *sort
= state
->sort_types
[s
];
9671 if(F_ON(F_SHOW_SORT
, ps_global
))
9672 ps_global
->mangled_header
= 1;
9676 cmd_cancelled("Sort");
9680 fs_give((void **)&sorts
[i
].name
);
9682 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
9687 /*---------------------------------------------------------------------
9688 Build list of folders in the given context for user selection
9690 Args: c -- pointer to pointer to folder's context context
9691 f -- folder prefix to display
9692 sublist -- whether or not to use 'f's contents as prefix
9693 lister -- function used to do the actual display
9695 Returns: malloc'd string containing sequence, else NULL if
9696 no messages in msgmap with local "selected" flag.
9699 display_folder_list(CONTEXT_S
**c
, char *f
, int sublist
, int (*lister
) (struct pine
*, CONTEXT_S
**, char *, int))
9703 void (*redraw
)(void) = ps_global
->redrawer
;
9705 push_titlebar_state();
9707 if((rc
= (*lister
)(ps_global
, &tc
, f
, sublist
)) != 0)
9711 pop_titlebar_state();
9713 if((ps_global
->redrawer
= redraw
) != NULL
) /* reset old value, and test */
9714 (*ps_global
->redrawer
)();
9716 if(rc
== 1 && F_ON(F_SELECT_WO_CONFIRM
, ps_global
))
9724 * Allow user to choose a single item from a list of strings.
9726 * Args list -- Array of strings to choose from, NULL terminated.
9727 * displist -- Array of strings to display instead of displaying list.
9728 * Indices correspond to the list array. Display the displist
9729 * but return the item from list if displist non-NULL.
9730 * title -- For conf_scroll_screen
9731 * pdesc -- For conf_scroll_screen
9732 * help -- For conf_scroll_screen
9733 * htitle -- For conf_scroll_screen
9735 * Returns an allocated copy of the chosen item or NULL.
9738 choose_item_from_list(char **list
, char **displist
, char *title
, char *pdesc
, HelpType help
,
9739 char *htitle
, char *cursor_location
)
9741 LIST_SEL_S
*listhead
, *ls
, *p
, *starting_val
= NULL
;
9743 char *ret
= NULL
, *choice
= NULL
;
9745 /* build the LIST_SEL_S list */
9746 p
= listhead
= NULL
;
9747 for(t
= list
, dl
= displist
; *t
; t
++, dl
++){
9748 ls
= (LIST_SEL_S
*) fs_get(sizeof(*ls
));
9749 memset(ls
, 0, sizeof(*ls
));
9750 ls
->item
= cpystr(*t
);
9752 ls
->display_item
= cpystr(*dl
);
9754 if(cursor_location
&& (cursor_location
== (*t
)))
9768 if(!select_from_list_screen(listhead
, SFL_NONE
, title
, pdesc
,
9769 help
, htitle
, starting_val
))
9770 for(p
= listhead
; !choice
&& p
; p
= p
->next
)
9775 ret
= cpystr(choice
);
9777 free_list_sel(&listhead
);
9784 free_list_sel(LIST_SEL_S
**lsel
)
9787 free_list_sel(&(*lsel
)->next
);
9789 fs_give((void **) &(*lsel
)->item
);
9791 if((*lsel
)->display_item
)
9792 fs_give((void **) &(*lsel
)->display_item
);
9794 fs_give((void **) lsel
);
9800 * file_lister - call pico library's file lister
9803 file_lister(char *title
, char *path
, size_t pathlen
, char *file
, size_t filelen
, int newmail
, int flags
)
9807 void (*redraw
)(void) = ps_global
->redrawer
;
9809 standard_picobuf_setup(&pbf
);
9810 push_titlebar_state();
9814 /* BUG: what about help command and text? */
9815 pbf
.pine_anchor
= title
;
9817 rv
= pico_file_browse(&pbf
, path
, pathlen
, file
, filelen
, NULL
, 0, flags
);
9818 standard_picobuf_teardown(&pbf
);
9819 fix_windsize(ps_global
);
9820 init_signals(); /* has it's own signal stuff */
9822 /* Restore display's titlebar and body */
9823 pop_titlebar_state();
9825 if((ps_global
->redrawer
= redraw
) != NULL
)
9826 (*ps_global
->redrawer
)();
9832 /*----------------------------------------------------------------------
9833 Print current folder index
9837 print_index(struct pine
*state
, MSGNO_S
*msgmap
, int agg
)
9841 char buf
[MAX_SCREEN_COLS
+1];
9843 for(i
= 1L; i
<= mn_get_total(msgmap
); i
++){
9844 if(agg
&& !get_lflag(state
->mail_stream
, msgmap
, i
, MN_SLCT
))
9847 if(!agg
&& msgline_hidden(state
->mail_stream
, msgmap
, i
, 0))
9850 ice
= build_header_line(state
, state
->mail_stream
, msgmap
, i
, NULL
);
9854 * I don't understand why we'd want to mark the current message
9855 * instead of printing out the first character of the status
9856 * so I'm taking it out and including the first character of the
9857 * line instead. Hubert 2006-02-09
9859 if(!print_char((mn_is_cur(msgmap, i)) ? '>' : ' '))
9863 if(!gf_puts(simple_index_line(buf
,sizeof(buf
),ice
,i
),
9865 || !gf_puts(NEWLINE
, print_char
))
9876 * windows callback to get/set header mode state
9879 header_mode_callback(set
, args
)
9883 return(ps_global
->full_header
);
9888 * windows callback to get/set zoom mode state
9891 zoom_mode_callback(set
, args
)
9895 return(any_lflagged(ps_global
->msgmap
, MN_HIDE
) != 0);
9900 * windows callback to get/set zoom mode state
9903 any_selected_callback(set
, args
)
9907 return(any_lflagged(ps_global
->msgmap
, MN_SLCT
) != 0);
9915 flag_callback(set
, flags
)
9925 case 1: /* Important */
9926 permflag
= ps_global
->mail_stream
->perm_flagged
;
9930 permflag
= ps_global
->mail_stream
->perm_seen
;
9933 case 3: /* Answered */
9934 permflag
= ps_global
->mail_stream
->perm_answered
;
9937 case 4: /* Deleted */
9938 permflag
= ps_global
->mail_stream
->perm_deleted
;
9943 if(!(any_messages(ps_global
->msgmap
, NULL
, "to Flag")
9944 && can_set_flag(ps_global
, "flag", permflag
)))
9947 if(sp_io_error_on_stream(ps_global
->mail_stream
)){
9948 sp_set_io_error_on_stream(ps_global
->mail_stream
, 0);
9949 pine_mail_check(ps_global
->mail_stream
); /* forces write */
9953 msgno
= mn_m2raw(ps_global
->msgmap
, mn_get_cur(ps_global
->msgmap
));
9954 if(msgno
> 0L && ps_global
->mail_stream
9955 && msgno
<= ps_global
->mail_stream
->nmsgs
9956 && (mc
= mail_elt(ps_global
->mail_stream
, msgno
))
9959 * NOTE: code below is *VERY* sensitive to the order of
9960 * the messages defined in resource.h for flag handling.
9961 * Don't change it unless you know what you're doing.
9968 case 1 : /* Important */
9969 flagstr
= "\\FLAGGED";
9970 mflag
= (mc
->flagged
) ? 0L : ST_SET
;
9975 mflag
= (mc
->seen
) ? 0L : ST_SET
;
9978 case 3 : /* Answered */
9979 flagstr
= "\\ANSWERED";
9980 mflag
= (mc
->answered
) ? 0L : ST_SET
;
9983 case 4 : /* Deleted */
9984 flagstr
= "\\DELETED";
9985 mflag
= (mc
->deleted
) ? 0L : ST_SET
;
9988 default : /* bogus */
9992 mail_flag(ps_global
->mail_stream
, long2string(msgno
),
9995 if(ps_global
->redrawer
)
9996 (*ps_global
->redrawer
)();
10001 newflags
|= 0x0001;
10005 newflags
|= 0x0002;
10009 newflags
|= 0x0004;
10013 newflags
|= 0x0008;
10023 * BUG: Should teach this about keywords
10029 static MPopup flag_submenu
[] = {
10030 {tMessage
, {N_("Important"), lNormal
}, {IDM_MI_FLAGIMPORTANT
}},
10031 {tMessage
, {N_("New"), lNormal
}, {IDM_MI_FLAGNEW
}},
10032 {tMessage
, {N_("Answered"), lNormal
}, {IDM_MI_FLAGANSWERED
}},
10033 {tMessage
, {N_("Deleted"), lNormal
}, {IDM_MI_FLAGDELETED
}},
10038 flag_submenu
[0].label
.style
= (mc
&& mc
->flagged
) ? lChecked
: lNormal
;
10041 flag_submenu
[1].label
.style
= (mc
&& mc
->seen
) ? lNormal
: lChecked
;
10044 flag_submenu
[2].label
.style
= (mc
&& mc
->answered
) ? lChecked
: lNormal
;
10047 flag_submenu
[3].label
.style
= (mc
&& mc
->deleted
) ? lChecked
: lNormal
;
10049 return(flag_submenu
);
10052 #endif /* _WINDOWS */