2 * ========================================================================
3 * Copyright 2013-2021 Eduardo Chappa
4 * Copyright 2006-2008 University of Washington
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
26 #include "../pith/state.h"
27 #include "../pith/conf.h"
28 #include "../pith/filter.h"
29 #include "../pith/msgno.h"
30 #include "../pith/pipe.h"
31 #include "../pith/util.h"
32 #include "../pith/detoken.h"
33 #include "../pith/list.h"
34 #include "../pith/margin.h"
35 #include "../pith/busy.h"
38 typedef struct _help_scroll
{
39 unsigned keys_formatted
:1; /* Has full keymenu been formatted? */
40 char **help_source
; /* Source of displayed help text */
51 typedef struct _help_print_state
{
57 static HPRT_S
*g_hprt
;
60 static char att_cur_msg
[] = "\
63 If you think that the \"current\" message may be related to the bug you\n\
64 are reporting you may include it as an attachment. If you want to\n\
65 include a message but you aren't sure if it is the current message,\n\
66 cancel this bug report, go to the folder index, place the cursor on\n\
67 the message you wish to include, then return to the main menu and run\n\
68 the bug report command again. Answer \"Y\" when asked the question\n\
69 \"Attach current message to report?\"\n\
71 This bug report will also automatically include your pine\n\
72 configuration file, which is helpful when investigating the problem.";
75 #define GRIPE_OPT_CONF 0x01
76 #define GRIPE_OPT_MSG 0x02
77 #define GRIPE_OPT_LOCAL 0x04
78 #define GRIPE_OPT_KEYS 0x08
84 int helper_internal(HelpType
, char *, char *, int);
85 int help_processor(int, MSGNO_S
*, SCROLL_S
*);
86 void help_keymenu_tweek(SCROLL_S
*, int);
87 void print_all_help(void);
88 void print_help_page_title(char *, size_t, HPRT_S
*);
89 int print_help_page_break(long, char *, LT_INS_S
**, void *);
90 int help_bogus_input(UCS
);
91 int gripe_newbody(struct pine
*, BODY
**, long, int);
92 ADDRESS
*gripe_token_addr(char *);
93 char *gripe_id(char *);
94 void att_cur_drawer(void);
95 int journal_processor(int, MSGNO_S
*, SCROLL_S
*);
96 int help_popup(SCROLL_S
*, int);
98 int help_subsection_popup(SCROLL_S
*, int);
103 /*----------------------------------------------------------------------
104 Get the help text in the proper format and call scroller
106 Args: text -- The help text to display (from pine.help --> helptext.c)
107 title -- The title of the help text
109 Result: format text and call scroller
111 The pages array contains the line number of the start of the pages in
112 the text. Page 1 is in the 0th element of the array.
113 The list is ended with a page line number of -1. Line number 0 is also
114 the first line in the text.
117 helper_internal(HelpType text
, char *frag
, char *title
, int flags
)
119 char **shown_text
, **external_text
= NULL
, *help_text
= NULL
, **rv
;
120 int cmd
= MC_NONE
, is_external
;
122 char *error
= NULL
, tmp_title
[MAX_SCREEN_COLS
+ 1];
124 HANDLE_S
*handles
= NULL
, *htmp
;
125 HELP_SCROLL_S hscroll
;
128 dprint((1, "\n\n ---- HELPER ----\n"));
130 /* assumption here is that HelpType is char ** */
134 if(shown_text
&& *shown_text
&& !struncmp(*shown_text
, "x-alpine-http:", 14)){
135 int status
, we_cancel
= 0;
137 we_cancel
= busy_cue(_("Retrieving help text"), NULL
, 1);
138 HTTPSTREAM
*stream
= http_open((unsigned char *) (*shown_text
+ 14));
139 if(stream
) help_text
= (char *) http_get(stream
, NULL
);
140 status
= stream
&& stream
->status
? stream
->status
->code
: -1;
141 if(stream
) http_close(stream
);
142 if(status
!= HTTP_OK
){
143 shown_text
= NO_HELP
;
144 if(help_text
) fs_give((void **) &help_text
);
145 q_status_message(SM_ORDER
, 0, 2, _("Failed to retrieve help text. Try again later."));
150 if(help_text
== NULL
) external_text
= NO_HELP
;
155 for(nlines
= 0, s
= help_text
; s
!= NULL
; nlines
++){
159 rv
= external_text
= fs_get((nlines
+ 1)*sizeof(char *));
160 for(s
= help_text
; s
!= NULL
; s
= t
){
162 if(t
!= NULL
) *t
++ = '\0';
172 if(F_ON(F_BLANK_KEYMENU
,ps_global
)){
173 FOOTER_ROWS(ps_global
) = 3;
174 clearfooter(ps_global
);
175 ps_global
->mangled_screen
= 1;
178 if(flags
& HLPD_NEWWIN
){
179 fix_windsize(ps_global
);
184 * At this point, shown_text is a charstarstar with html
185 * Turn it into a charstar with digested html
188 init_helper_getc(is_external
? external_text
: shown_text
);
189 init_handles(&handles
);
191 memset(&hscroll
, 0, sizeof(HELP_SCROLL_S
));
192 hscroll
.help_source
= is_external
? external_text
: shown_text
;
193 if((store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
){
194 gf_set_so_writec(&pc
, store
);
197 if(!struncmp(hscroll
.help_source
[0], "<html>", 6) || is_external
)
198 gf_link_filter(gf_html2plain
,
199 gf_html2plain_opt("x-alpine-help:",
200 ps_global
->ttyo
->screen_cols
,
201 non_messageview_margin(),
202 &handles
, NULL
, GFHP_LOCAL_HANDLES
));
204 gf_link_filter(gf_wrap
, gf_wrap_filter_opt(
205 ps_global
->ttyo
->screen_cols
,
206 ps_global
->ttyo
->screen_cols
,
207 NULL
, 0, GFW_HANDLES
| GFW_SOFTHYPHEN
));
209 error
= gf_pipe(helper_getc
, pc
);
211 gf_clear_so_writec(store
);
218 for(htmp
= handles
; htmp
; htmp
= htmp
->next
)
221 && (htmp
->h
.url
.path
[0] == 'x'
222 || htmp
->h
.url
.path
[0] == '#'))
223 htmp
->force_display
= 1;
225 /* This is mostly here to get the curses variables
226 * for line and column in sync with where the
227 * cursor is on the screen. This gets warped when
228 * the composer is called because it does it's own
233 memset(&sargs
, 0, sizeof(SCROLL_S
));
234 sargs
.text
.text
= so_text(store
);
235 sargs
.text
.src
= CharStar
;
236 sargs
.text
.desc
= _("help text");
237 if((sargs
.text
.handles
= handles
) != NULL
)
238 while(sargs
.text
.handles
->type
== URL
239 && !sargs
.text
.handles
->h
.url
.path
240 && sargs
.text
.handles
->next
)
241 sargs
.text
.handles
= sargs
.text
.handles
->next
;
243 if(!(sargs
.bar
.title
= title
)){
244 if(!struncmp(hscroll
.help_source
[0], "<html>", 6) || is_external
){
248 /* if we're looking at html, look for a <title>
249 * in the <head>... */
251 hscroll
.help_source
[0][i
]
252 && struncmp(hscroll
.help_source
[i
], "</head>", 7);
254 if(!struncmp(hscroll
.help_source
[i
], "<title>", 7)){
255 strncpy(tmp_20k_buf
, &hscroll
.help_source
[i
][7], SIZEOF_20KBUF
);
256 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
257 if((p
= strchr(tmp_20k_buf
, '<')) != NULL
)
260 snprintf(sargs
.bar
.title
= tmp_title
, sizeof(tmp_title
),
261 "%s -- %.*s", _("HELP"),
262 ps_global
->ttyo
->screen_cols
-10,
263 strsquish(tmp_20k_buf
+ 500, SIZEOF_20KBUF
,
265 ps_global
->ttyo
->screen_cols
/ 3));
266 tmp_title
[sizeof(tmp_title
)-1] = '\0';
272 sargs
.bar
.title
= _("HELP TEXT");
275 sargs
.bar
.style
= TextPercent
;
276 sargs
.proc
.tool
= help_processor
;
277 sargs
.proc
.data
.p
= (void *) &hscroll
;
278 sargs
.resize_exit
= 1;
279 sargs
.help
.text
= h_special_help_nav
;
280 sargs
.help
.title
= _("HELP FOR HELP TEXT");
281 sargs
.keys
.menu
= &km
;
284 memcpy(&keys
[0], help_keymenu
.keys
,
285 (help_keymenu
.how_many
* 12) * sizeof(struct key
));
286 setbitmap(sargs
.keys
.bitmap
);
287 if(flags
& HLPD_FROMHELP
){
288 km
.keys
[HLP_EXIT_KEY
].name
= "P";
289 km
.keys
[HLP_EXIT_KEY
].label
= _("Prev Help");
290 km
.keys
[HLP_EXIT_KEY
].bind
.cmd
= MC_FINISH
;
291 km
.keys
[HLP_EXIT_KEY
].bind
.ch
[0] = 'p';
293 km
.keys
[HLP_SUBEXIT_KEY
].name
= "E";
294 km
.keys
[HLP_SUBEXIT_KEY
].label
= _("Exit Help");
295 km
.keys
[HLP_SUBEXIT_KEY
].bind
.cmd
= MC_EXIT
;
296 km
.keys
[HLP_SUBEXIT_KEY
].bind
.ch
[0] = 'e';
298 else if(text
== h_special_help_nav
){
299 km
.keys
[HLP_EXIT_KEY
].name
= "P";
300 km
.keys
[HLP_EXIT_KEY
].label
= _("Prev Help");
301 km
.keys
[HLP_EXIT_KEY
].bind
.cmd
= MC_FINISH
;
302 km
.keys
[HLP_EXIT_KEY
].bind
.ch
[0] = 'p';
304 clrbitn(HLP_MAIN_KEY
, sargs
.keys
.bitmap
);
305 clrbitn(HLP_SUBEXIT_KEY
, sargs
.keys
.bitmap
);
308 km
.keys
[HLP_EXIT_KEY
].name
= "E";
309 km
.keys
[HLP_EXIT_KEY
].label
= _("Exit Help");
310 km
.keys
[HLP_EXIT_KEY
].bind
.cmd
= MC_EXIT
;
311 km
.keys
[HLP_EXIT_KEY
].bind
.ch
[0] = 'e';
313 km
.keys
[HLP_SUBEXIT_KEY
].name
= "?";
314 /* TRANSLATORS: this is the label of a command where
315 the user is asking for Help about the Help command */
316 km
.keys
[HLP_SUBEXIT_KEY
].label
= _("Help Help");
317 km
.keys
[HLP_SUBEXIT_KEY
].bind
.cmd
= MC_HELP
;
318 km
.keys
[HLP_SUBEXIT_KEY
].bind
.ch
[0] = '?';
321 if(flags
& HLPD_SIMPLE
){
322 clrbitn(HLP_MAIN_KEY
, sargs
.keys
.bitmap
);
325 sargs
.bogus_input
= help_bogus_input
;
328 sargs
.keys
.each_cmd
= help_keymenu_tweek
;
329 hscroll
.keys_formatted
= 0;
332 clrbitn(HLP_VIEW_HANDLE
, sargs
.keys
.bitmap
);
333 clrbitn(HLP_PREV_HANDLE
, sargs
.keys
.bitmap
);
334 clrbitn(HLP_NEXT_HANDLE
, sargs
.keys
.bitmap
);
337 if(text
!= main_menu_tx
338 && text
!= h_mainhelp_pinehelp
)
339 clrbitn(HLP_ALL_KEY
, sargs
.keys
.bitmap
);
342 sargs
.start
.on
= Fragment
;
343 sargs
.start
.loc
.frag
= frag
;
344 frag
= NULL
; /* ignore next time */
347 sargs
.start
.on
= Offset
;
348 sargs
.start
.loc
.offset
= offset
;
351 sargs
.start
.on
= FirstPage
;
354 sargs
.mouse
.popup
= (flags
& HLPD_FROMHELP
)
355 ? help_subsection_popup
: help_popup
;
358 cmd
= scrolltool(&sargs
);
360 offset
= sargs
.start
.loc
.offset
;
362 if(F_ON(F_BLANK_KEYMENU
,ps_global
))
363 FOOTER_ROWS(ps_global
) = 1;
371 free_handles(&handles
);
373 while(cmd
== MC_RESIZE
);
375 if(external_text
!= NULL
){
376 for(rv
= external_text
; *rv
!= NULL
; rv
++)
377 fs_give((void **) &*rv
);
378 fs_give((void **) external_text
);
380 if(help_text
) fs_give((void **) &help_text
);
386 * helper -- compatibility function around newer helper_internal
389 helper(HelpType text
, char *title
, int flags
)
391 return(helper_internal(text
, NULL
, title
, flags
));
396 init_helper_getc(char **help_txt
)
399 g_h_text
.line
= help_txt
;
400 g_h_text
.offset
= *g_h_text
.line
;
401 if(g_h_text
.offset
&& g_h_text
.offset
[0])
402 g_h_text
.offset
= _(g_h_text
.offset
);
414 else if(g_h_text
.offset
&& *g_h_text
.line
){
415 if(!(*c
= *g_h_text
.offset
++)){
416 g_h_text
.offset
= *++g_h_text
.line
;
417 if(g_h_text
.offset
&& g_h_text
.offset
[0])
418 g_h_text
.offset
= _(g_h_text
.offset
);
432 help_processor(int cmd
, MSGNO_S
*msgmap
, SCROLL_S
*sparms
)
436 struct help_texts
*t
;
439 /*----------- Print all the help ------------*/
445 snprintf(message
, sizeof(message
), "%s", STYLE_NAME(sparms
));
446 message
[sizeof(message
)-1] = '\0';
447 if(open_printer(message
) == 0){
448 print_help(((HELP_SCROLL_S
*)sparms
->proc
.data
.p
)->help_source
);
454 case MC_EXPORT
: /* reuse old definition, so as not to patch pine.h */
457 for(t
= h_texts
; t
->help_text
!= NO_HELP
; t
++)
458 if(t
->help_text
== ((HELP_SCROLL_S
*)sparms
->proc
.data
.p
)->help_source
){
459 strncpy(help_name
, t
->tag
, sizeof(help_name
)-1);
460 help_name
[sizeof(help_name
)-1] = '\0';
464 q_status_message1(SM_ORDER
, 0, 2,
465 "Internal Name: x-alpine-help:%s", help_name
);
467 q_status_message(SM_ORDER
|SM_DING
, 1, 2,
468 "Can not find link for text help");
477 alpine_panic("Unhandled case");
485 help_keymenu_tweek(SCROLL_S
*sparms
, int handle_hidden
)
488 sparms
->keys
.menu
->keys
[HLP_VIEW_HANDLE
].name
= "";
489 sparms
->keys
.menu
->keys
[HLP_VIEW_HANDLE
].label
= "";
492 if(!((HELP_SCROLL_S
*)sparms
->proc
.data
.p
)->keys_formatted
){
493 /* If label's always been blank, force reformatting */
494 mark_keymenu_dirty();
495 sparms
->keys
.menu
->width
= 0;
496 ((HELP_SCROLL_S
*)sparms
->proc
.data
.p
)->keys_formatted
= 1;
499 sparms
->keys
.menu
->keys
[HLP_VIEW_HANDLE
].name
= "V";
500 sparms
->keys
.menu
->keys
[HLP_VIEW_HANDLE
].label
= "[" N_("View Link") "]";
506 * print_help - send the raw array of lines to printer
509 print_help(char **text
)
511 char *error
, buf
[256];
514 init_helper_getc(text
);
516 memset(g_hprt
= &help_data
, 0, sizeof(HPRT_S
));
522 if(!struncmp(text
[0], "<html>", 6)){
526 gf_link_filter(gf_html2plain
,
527 gf_html2plain_opt(NULL
,80,non_messageview_margin(),
528 NULL
,NULL
,GFHP_STRIPPED
));
529 for(i
= 1; i
<= 5 && text
[i
]; i
++)
530 if(!struncmp(text
[i
], "<title>", 7)
531 && (p
= srchstr(text
[i
] + 7, "</title>"))
533 help_data
.title
= text
[i
] + 7;
534 help_data
.title_len
= p
- help_data
.title
;
539 gf_link_filter(gf_wrap
, gf_wrap_filter_opt(80, 80, NULL
, 0, GFW_NONE
));
541 gf_link_filter(gf_line_test
,
542 gf_line_test_opt(print_help_page_break
, NULL
));
543 gf_link_filter(gf_nvtnl_local
, NULL
);
545 print_help_page_title(buf
, sizeof(buf
), &help_data
);
547 print_text(NEWLINE
); /* terminate it */
548 print_text(NEWLINE
); /* and write two blank links */
551 if((error
= gf_pipe(helper_getc
, print_char
)) != NULL
)
552 q_status_message1(SM_ORDER
| SM_DING
, 3, 3, _("Printing Error: %s"), error
);
554 print_char(ctrl('L')); /* new page. */
561 struct help_texts
*t
;
563 int we_turned_on
= 0;
565 if(open_printer(_("all 150+ pages of help text")) == 0) {
566 we_turned_on
= intr_handling_on();
567 for(t
= h_texts
; (h
= t
->help_text
) != NO_HELP
; t
++) {
568 if(ps_global
->intr_pending
){
569 q_status_message(SM_ORDER
, 3, 3,
570 _("Print of all help cancelled"));
586 * print_help_page_title --
589 print_help_page_title(char *buf
, size_t buflen
, HPRT_S
*hprt
)
591 snprintf(buf
, buflen
, " Alpine Help%s%.*s%*s%d",
592 hprt
->title_len
? ": " : " Text",
593 MIN(55, hprt
->title_len
), hprt
->title_len
? hprt
->title
: "",
594 59 - (hprt
->title_len
? MIN(55, hprt
->title_len
) : 5),
595 "Page ", hprt
->page
);
596 buf
[buflen
-1] = '\0';
601 * print_help_page_break -- insert page breaks and such for printed
605 print_help_page_break(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
609 if(((linenum
+ (g_hprt
->page
* 3)) % 62) == 0){
610 g_hprt
->page
++; /* start on new page */
612 print_help_page_title(buf
+ 1, sizeof(buf
)-1, g_hprt
);
613 strncat(buf
, "\015\012\015\012\015\012", sizeof(buf
)-strlen(buf
)-1);
614 buf
[sizeof(buf
)-1] = '\0';
615 ins
= gf_line_test_new_ins(ins
, line
, buf
, strlen(buf
));
623 * help_bogus_input - used by scrolltool to complain about
624 * invalid user input.
627 help_bogus_input(UCS ch
)
629 bogus_command(ch
, NULL
);
635 url_local_helper(char *url
)
637 if((!struncmp(url
, "x-alpine-help:", 14) && *(url
+= 14))
638 || (!struncmp(url
, "x-pine-help:", 12) && *(url
+= 12))){
642 /* internal fragment reference? */
643 if((frag
= strchr(url
, '#')) != NULL
){
646 if((len
= frag
- url
) != 0){
647 newhelp
= help_name2section(url
, len
);
650 url_local_fragment(url
);
655 newhelp
= help_name2section(url
, strlen(url
));
658 if(newhelp
!= NO_HELP
){
661 rv
= helper_internal(newhelp
, frag
, _("HELP SUB-SECTION"),
662 HLPD_NEWWIN
| HLPD_SIMPLE
| HLPD_FROMHELP
);
663 ps_global
->mangled_screen
= 1;
664 return((rv
== MC_EXIT
) ? 2 : 1);
668 q_status_message1(SM_ORDER
| SM_DING
, 0, 3,
669 _("Unrecognized Internal help: \"%s\""), url
);
675 url_local_config(char *url
)
677 if(!struncmp(url
, "x-alpine-config:", 16)){
681 config
= get_supported_options();
683 /* TRANSLATORS: Help for configuration */
684 rv
= helper_internal(config
, NULL
, _("HELP CONFIG"),
685 HLPD_NEWWIN
| HLPD_SIMPLE
| HLPD_FROMHELP
);
686 free_list_array(&config
);
689 ps_global
->mangled_screen
= 1;
690 return((rv
== MC_EXIT
) ? 2 : 1);
693 q_status_message1(SM_ORDER
| SM_DING
, 0, 3,
694 _("Unrecognized Internal help: \"%s\""), url
);
699 /*----------------------------------------------------------------------
700 Review latest status messages
703 review_messages(void)
706 STORE_S
*in_store
= NULL
, *out_store
= NULL
;
708 int jo
, lo
, hi
, donejo
, donelo
, donehi
;
710 int cmd
, timestamps
=0, show_level
=-1;
711 char debugkeylabel
[20];
712 /* TRANSLATORS: command label asking pine to include time stamps in output */
713 char timestampkeylabel
[] = N_("Timestamps");
714 /* TRANSLATORS: do not include time stamps in output */
715 char *notimestampkeylabel
= N_("NoTimestamps");
717 if((rmjofirst
< 0 && rmlofirst
< 0 && rmhifirst
< 0)
718 || rmjofirst
>= RMJLEN
|| rmjolast
>= RMJLEN
719 || rmlofirst
>= RMLLEN
|| rmlolast
>= RMLLEN
720 || rmhifirst
>= RMHLEN
|| rmhilast
>= RMHLEN
721 || (rmjofirst
>= 0 && rmjolast
< 0)
722 || (rmlofirst
>= 0 && rmlolast
< 0)
723 || (rmhifirst
>= 0 && rmhilast
< 0))
728 if(!(in_store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) ||
729 !(out_store
= so_get(CharStar
, NULL
, EDIT_ACCESS
))){
733 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
734 _("Failed allocating memory"));
738 add_review_message(_("Turning off new messages while reviewing"), 0);
739 rm_not_right_now
= 1;
741 donejo
= donehi
= donelo
= 0;
754 while(!(donejo
&& donelo
&& donehi
)){
755 REV_MSG_S
*pjo
, *plo
, *phi
, *p
;
758 pjo
= &rmjoarray
[jo
];
763 plo
= &rmloarray
[lo
];
768 phi
= &rmhiarray
[hi
];
772 if(pjo
&& (!plo
|| pjo
->seq
<= plo
->seq
)
773 && (!phi
|| pjo
->seq
<= phi
->seq
))
775 else if(plo
&& (!phi
|| plo
->seq
<= phi
->seq
))
785 if(jo
== rmjofirst
&& (((rmjolast
+ 1) % RMJLEN
) == rmjofirst
))
787 _("**** Journal entries prior to this point have been trimmed. ****\n"));
792 if(show_level
>= 0 &&
793 lo
== rmlofirst
&& (((rmlolast
+ 1) % RMLLEN
) == rmlofirst
))
795 _("**** Debug 0-4 entries prior to this point have been trimmed. ****\n"));
800 if(show_level
>= 5 &&
801 hi
== rmhifirst
&& (((rmhilast
+ 1) % RMHLEN
) == rmhifirst
))
803 _("**** Debug 5-9 entries prior to this point have been trimmed. ****\n"));
812 if(p
->level
<= show_level
){
813 if(timestamps
&& p
->timestamp
&& p
->timestamp
[0]){
814 so_puts(in_store
, p
->timestamp
);
815 so_puts(in_store
, ": ");
818 if(p
->message
&& p
->message
[0]){
820 so_puts(in_store
, ">");
822 so_puts(in_store
, p
->message
);
823 so_puts(in_store
, "\n");
833 jo
= (jo
+ 1) % RMJLEN
;
841 lo
= (lo
+ 1) % RMLLEN
;
849 hi
= (hi
+ 1) % RMHLEN
;
862 so_seek(in_store
, 0L, 0);
864 gf_link_filter(gf_wrap
,
865 gf_wrap_filter_opt(ps_global
->ttyo
->screen_cols
- 4,
866 ps_global
->ttyo
->screen_cols
,
867 NULL
, show_level
< 0 ? 2 : 0, GFW_NONE
));
868 gf_set_so_readc(&gc
, in_store
);
869 gf_set_so_writec(&pc
, out_store
);
871 gf_clear_so_writec(out_store
);
872 gf_clear_so_readc(in_store
);
874 memset(&sargs
, 0, sizeof(SCROLL_S
));
875 sargs
.text
.text
= so_text(out_store
);
876 sargs
.text
.src
= CharStar
;
877 sargs
.text
.desc
= _("journal");
878 sargs
.keys
.menu
= &rev_msg_keymenu
;
879 sargs
.proc
.tool
= journal_processor
;
880 sargs
.start
.on
= LastPage
;
881 sargs
.resize_exit
= 1;
882 sargs
.proc
.data
.p
= (void *)&show_level
;
883 setbitmap(sargs
.keys
.bitmap
);
887 sargs
.jump_is_debug
= 1;
888 /* TRANSLATORS: these are some screen titles */
889 sargs
.help
.title
= _("HELP FOR DEBUG JOURNAL");
890 sargs
.help
.text
= h_debugjournal
;
891 sargs
.bar
.title
= _("REVIEW DEBUGGING");
892 #else /* !DEBUGJOURNAL */
893 clrbitn(DEBUG_KEY
, sargs
.keys
.bitmap
);
894 sargs
.help
.title
= _("HELP FOR JOURNAL");
895 sargs
.help
.text
= h_journal
;
896 sargs
.bar
.title
= _("REVIEW RECENT MESSAGES");
897 #endif /* !DEBUGJOURNAL */
899 clrbitn(DEBUG_KEY
, sargs
.keys
.bitmap
);
900 clrbitn(TIMESTAMP_KEY
, sargs
.keys
.bitmap
);
901 sargs
.help
.title
= _("HELP FOR JOURNAL");
902 sargs
.help
.text
= h_journal
;
903 sargs
.bar
.title
= _("REVIEW RECENT MESSAGES");
907 rev_msg_keys
[TIMESTAMP_KEY
].label
= notimestampkeylabel
;
909 rev_msg_keys
[TIMESTAMP_KEY
].label
= timestampkeylabel
;
912 /* TRANSLATORS: shows what numeric level Debug output is displayed at */
913 snprintf(debugkeylabel
, sizeof(debugkeylabel
), _("Debug (%d)"), show_level
);
915 /* TRANSLATORS: include debug output in journal */
916 strncpy(debugkeylabel
, _("DebugView"), sizeof(debugkeylabel
));
918 debugkeylabel
[sizeof(debugkeylabel
)-1] = '\0';
920 rev_msg_keys
[DEBUG_KEY
].label
= debugkeylabel
;
921 KS_OSDATASET(&rev_msg_keys
[DEBUG_KEY
], KS_NONE
);
923 if((cmd
= scrolltool(&sargs
)) == MC_TOGGLE
)
924 timestamps
= !timestamps
;
929 }while(cmd
!= MC_EXIT
);
931 rm_not_right_now
= 0;
932 add_review_message("Done reviewing", 0);
937 journal_processor(int cmd
, MSGNO_S
*msgmap
, SCROLL_S
*sparms
)
940 case MC_TOGGLE
: /* turn timestamps on or off */
944 alpine_panic("Unexpected command in journal_processor");
953 * standard type of storage object used for body parts...
956 #define PART_SO_TYPE TmpFileStar
958 #define PART_SO_TYPE CharStar
966 char *composer_title
, *url_copy
, *optstr
, *p
;
969 ENVELOPE
*outgoing
= NULL
;
971 PINEFIELD
*pf
= NULL
;
972 long msgno
= mn_m2raw(ps_global
->msgmap
,
973 mn_get_cur(ps_global
->msgmap
));
975 url_copy
= cpystr(url
+ strlen("x-alpine-gripe:"));
976 if((optstr
= strchr(url_copy
, '?')) != NULL
)
979 outgoing
= mail_newenvelope();
980 outgoing
->message_id
= generate_message_id(NULL
);
982 if((outgoing
->to
= gripe_token_addr(url_copy
)) != NULL
){
983 composer_title
= _("COMPOSE TO LOCAL SUPPORT");
985 "\n\n -- Send to local support(%s@%s) --\n",
986 outgoing
->to
->mailbox
? outgoing
->to
->mailbox
: "NULL",
987 outgoing
->to
->host
? outgoing
->to
->host
: "NULL"));
989 else{ /* must be global */
990 composer_title
= _("REQUEST FOR ASSISTANCE");
991 rfc822_parse_adrlist(&outgoing
->to
, url_copy
, ps_global
->maildomain
);
998 if((p
= strchr(optstr
, '?')) != NULL
) /* tie off list item */
1001 if(!strucmp(optstr
, "config"))
1002 opts
|= GRIPE_OPT_CONF
;
1003 else if(!strucmp(optstr
, "curmsg"))
1004 opts
|= GRIPE_OPT_MSG
;
1005 else if(!strucmp(optstr
, "local"))
1006 opts
|= GRIPE_OPT_LOCAL
;
1007 else if(!strucmp(optstr
, "keys"))
1008 opts
|= GRIPE_OPT_KEYS
;
1013 /* build body and hand off to composer... */
1014 if(gripe_newbody(ps_global
, &body
, msgno
, opts
) == 0){
1015 pf
= (PINEFIELD
*) fs_get(sizeof(PINEFIELD
));
1016 memset(pf
, 0, sizeof(PINEFIELD
));
1017 pf
->name
= cpystr("X-Generated-Via");
1018 pf
->type
= FreeText
;
1019 pf
->textbuf
= gripe_id("Alpine Bug Report screen");
1020 memset((void *)&fake_reply
, 0, sizeof(fake_reply
));
1021 fake_reply
.pseudo
= 1;
1022 fake_reply
.data
.pico_flags
= P_HEADEND
;
1023 pine_send(outgoing
, &body
, composer_title
, NULL
, NULL
,
1024 &fake_reply
, NULL
, NULL
, pf
, PS_STICKY_TO
);
1027 ps_global
->mangled_screen
= 1;
1028 mail_free_envelope(&outgoing
);
1031 pine_free_body(&body
);
1033 fs_give((void **) &url_copy
);
1040 gripe_newbody(ps
, body
, msgno
, flags
)
1050 static char *err
= "Problem creating space for message text.";
1052 char tmp
[MAILTMPLEN
], *p
;
1054 if((store
= so_get(PicoText
, NULL
, EDIT_ACCESS
)) != NULL
){
1055 *body
= mail_newbody();
1057 if((p
= detoken(NULL
, NULL
, 2, 0, 1, NULL
, NULL
)) != NULL
){
1061 fs_give((void **) &p
);
1065 q_status_message(SM_ORDER
| SM_DING
, 3, 4, err
);
1070 /*---- Might have multiple parts ----*/
1071 (*body
)->type
= TYPEMULTIPART
;
1072 /*---- The TEXT part/body ----*/
1073 (*body
)->nested
.part
= mail_newbody_part();
1074 (*body
)->nested
.part
->body
.type
= TYPETEXT
;
1075 (*body
)->nested
.part
->body
.contents
.text
.data
= (void *) store
;
1077 /*---- create object, and write current config into it ----*/
1078 pp
= &((*body
)->nested
.part
->next
);
1080 if(flags
& GRIPE_OPT_CONF
){
1081 *pp
= mail_newbody_part();
1082 pb
= &((*pp
)->body
);
1083 pp
= &((*pp
)->next
);
1084 pb
->type
= TYPETEXT
;
1085 pb
->id
= generate_message_id(NULL
);
1086 pb
->description
= cpystr("Alpine Configuration Data");
1087 pb
->parameter
= mail_newbody_parameter();
1088 pb
->parameter
->attribute
= cpystr("name");
1089 pb
->parameter
->value
= cpystr("config.txt");
1091 if((store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
){
1092 extern char datestamp
[], hoststamp
[];
1094 pb
->contents
.text
.data
= (void *) store
;
1095 gf_set_so_writec(&pc
, store
);
1096 gf_puts("Alpine built ", pc
);
1097 gf_puts(datestamp
, pc
);
1098 gf_puts(" on host: ", pc
);
1099 gf_puts(hoststamp
, pc
);
1103 dump_pine_struct(ps
, pc
);
1104 dump_config(ps
, pc
, 0);
1107 pb
->size
.bytes
= strlen((char *) so_text(store
));
1108 gf_clear_so_writec(store
);
1111 q_status_message(SM_ORDER
| SM_DING
, 3, 4, err
);
1116 if(flags
& GRIPE_OPT_KEYS
){
1117 *pp
= mail_newbody_part();
1118 pb
= &((*pp
)->body
);
1119 pp
= &((*pp
)->next
);
1120 pb
->type
= TYPETEXT
;
1121 pb
->id
= generate_message_id(NULL
);
1122 pb
->description
= cpystr("Recent User Input");
1123 pb
->parameter
= mail_newbody_parameter();
1124 pb
->parameter
->attribute
= cpystr("name");
1125 pb
->parameter
->value
= cpystr("uinput.txt");
1127 if((store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
){
1128 pb
->contents
.text
.data
= (void *) store
;
1130 so_puts(store
, "User's most recent input:\n");
1132 /* dump last n keystrokes */
1133 so_puts(store
, "========== Latest keystrokes ==========\n");
1134 while((i
= key_playback(0)) != -1){
1135 snprintf(tmp
, sizeof(tmp
), "\t%s\t(0x%x)\n", pretty_command(i
), i
);
1136 tmp
[sizeof(tmp
)-1] = '\0';
1137 so_puts(store
, tmp
);
1140 pb
->size
.bytes
= strlen((char *) so_text(store
));
1143 q_status_message(SM_ORDER
| SM_DING
, 3, 4, err
);
1148 /* check for local debugging info? */
1149 if((flags
& GRIPE_OPT_LOCAL
)
1150 && ps_global
->VAR_BUGS_EXTRAS
1151 && can_access(ps_global
->VAR_BUGS_EXTRAS
, EXECUTE_ACCESS
) == 0){
1154 *pp
= mail_newbody_part();
1155 pb
= &((*pp
)->body
);
1156 pp
= &((*pp
)->next
);
1157 pb
->type
= TYPETEXT
;
1158 pb
->id
= generate_message_id(NULL
);
1159 pb
->description
= cpystr("Local Configuration Data");
1160 pb
->parameter
= mail_newbody_parameter();
1161 pb
->parameter
->attribute
= cpystr("name");
1162 pb
->parameter
->value
= cpystr("lconfig.txt");
1164 if((store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
){
1168 pb
->contents
.text
.data
= (void *) store
;
1169 gf_set_so_writec(&pc
, store
);
1170 if((syspipe
= open_system_pipe(ps_global
->VAR_BUGS_EXTRAS
,
1172 PIPE_READ
| PIPE_STDERR
| PIPE_USER
,
1173 0, pipe_callback
, pipe_report_error
)) != NULL
){
1174 gf_set_readc(&gc
, (void *)syspipe
, 0, PipeStar
, 0);
1176 error
= gf_pipe(gc
, pc
);
1177 (void) close_system_pipe(&syspipe
, NULL
, pipe_callback
);
1180 error
= "executing config collector";
1182 gf_clear_so_writec(store
);
1186 q_status_message1(SM_ORDER
| SM_DING
, 3, 4,
1187 "Problem %s", error
);
1190 else /* fixup attachment's size */
1191 pb
->size
.bytes
= strlen((char *) so_text(store
));
1194 if((flags
& GRIPE_OPT_MSG
) && mn_get_total(ps
->msgmap
) > 0L){
1197 ps
->redrawer
= att_cur_drawer
;
1200 if((ch
= one_try_want_to("Attach current message to report",
1202 WT_FLUSH_IN
|WT_SEQ_SENSITIVE
)) == 'y'){
1203 *pp
= mail_newbody_part();
1204 pb
= &((*pp
)->body
);
1205 pp
= &((*pp
)->next
);
1206 pb
->type
= TYPEMESSAGE
;
1207 pb
->id
= generate_message_id(NULL
);
1208 snprintf(tmp
, sizeof(tmp
), "Problem Message (%ld of %ld)",
1209 mn_get_cur(ps
->msgmap
), mn_get_total(ps
->msgmap
));
1210 tmp
[sizeof(tmp
)-1] = '\0';
1211 pb
->description
= cpystr(tmp
);
1213 /*---- Package each message in a storage object ----*/
1214 if((store
= so_get(PART_SO_TYPE
, NULL
, EDIT_ACCESS
)) != NULL
){
1215 pb
->contents
.text
.data
= (void *) store
;
1218 q_status_message(SM_ORDER
| SM_DING
, 3, 4, err
);
1222 /* write the header */
1223 if((p
= mail_fetch_header(ps
->mail_stream
, msgno
, NIL
, NIL
,
1224 NIL
, FT_PEEK
)) && *p
)
1229 pb
->size
.bytes
= strlen(p
);
1230 so_puts(store
, "\015\012");
1232 if((p
= pine_mail_fetch_text(ps
->mail_stream
,
1233 msgno
, NULL
, NULL
, NIL
))
1239 pb
->size
.bytes
+= strlen(p
);
1242 q_status_message(SM_ORDER
, 0, 3, "Bug report cancelled.");
1248 /*---- Only one part! ----*/
1249 (*body
)->type
= TYPETEXT
;
1250 (*body
)->contents
.text
.data
= (void *) store
;
1258 gripe_token_addr(token
)
1264 if(token
&& *token
++ == '_'){
1265 if(!strcmp(token
, "LOCAL_ADDRESS_")){
1266 p
= (ps_global
->VAR_LOCAL_ADDRESS
1267 && ps_global
->VAR_LOCAL_ADDRESS
[0])
1268 ? ps_global
->VAR_LOCAL_ADDRESS
1270 a
= rfc822_parse_mailbox(&p
, ps_global
->maildomain
);
1271 a
->personal
= cpystr((ps_global
->VAR_LOCAL_FULLNAME
1272 && ps_global
->VAR_LOCAL_FULLNAME
[0])
1273 ? ps_global
->VAR_LOCAL_FULLNAME
1274 : "Place to report Alpine Bugs");
1276 else if(!strcmp(token
, "BUGS_ADDRESS_")){
1277 p
= (ps_global
->VAR_BUGS_ADDRESS
1278 && ps_global
->VAR_BUGS_ADDRESS
[0])
1279 ? ps_global
->VAR_BUGS_ADDRESS
: "postmaster";
1280 a
= rfc822_parse_mailbox(&p
, ps_global
->maildomain
);
1281 a
->personal
= cpystr((ps_global
->VAR_BUGS_FULLNAME
1282 && ps_global
->VAR_BUGS_FULLNAME
[0])
1283 ? ps_global
->VAR_BUGS_FULLNAME
1284 : "Place to report Alpine Bugs");
1299 * Build our contribution to the subject; part constant string
1300 * and random 4 character alpha numeric string.
1302 i
= (int)(random() % 36L);
1303 j
= (int)(random() % 36L);
1304 k
= (int)(random() % 36L);
1305 l
= (int)(random() % 36L);
1306 tmp_20k_buf
[0] = '\0';
1307 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s (ID %c%c%d%c%c)", key
,
1308 (i
< 10) ? '0' + i
: 'A' + (i
- 10),
1309 (j
< 10) ? '0' + j
: 'A' + (j
- 10),
1310 (int)(random() % 10L),
1311 (k
< 10) ? '0' + k
: 'A' + (k
- 10),
1312 (l
< 10) ? '0' + l
: 'A' + (l
- 10));
1313 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
1314 return(cpystr(tmp_20k_buf
));
1319 * Used by gripe_tool.
1322 att_cur_drawer(void)
1327 /* blat helpful message to screen */
1331 dline
< ps_global
->ttyo
->screen_rows
- FOOTER_ROWS(ps_global
);
1333 for(i
= 0; i
< 256 && att_cur_msg
[j
] && att_cur_msg
[j
] != '\n'; i
++)
1334 buf
[i
] = att_cur_msg
[j
++];
1342 PutLine0(dline
, 1, buf
);
1353 pcpine_help(HelpType section
)
1355 char **help_lines
, *help_text
= NULL
;
1359 /* assumption here is that HelpType is char ** */
1360 help_lines
= section
;
1361 if (help_lines
!= NULL
){
1362 init_helper_getc(help_lines
);
1363 if(store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)){
1364 gf_set_so_writec(&pc
, store
);
1367 gf_link_filter(gf_local_nvtnl
, NULL
);
1369 gf_link_filter(gf_html2plain
,
1370 gf_html2plain_opt(NULL
,
1371 ps_global
->ttyo
->screen_cols
,
1372 non_messageview_margin(), NULL
, NULL
, GFHP_STRIPPED
));
1374 if(!gf_pipe(helper_getc
, pc
)){
1375 help_text
= (char *) store
->txt
;
1376 store
->txt
= (void *) NULL
;
1379 gf_clear_so_writec(store
);
1392 help_popup(SCROLL_S
*sparms
, int in_handle
)
1398 hp_menu
[++i
].type
= tQueue
;
1399 hp_menu
[i
].label
.style
= lNormal
;
1400 hp_menu
[i
].label
.string
= "View Help Section";
1401 hp_menu
[i
].data
.val
= 'V';
1404 hp_menu
[++i
].type
= tQueue
;
1405 hp_menu
[i
].label
.style
= lNormal
;
1406 hp_menu
[i
].label
.string
= "Exit Help";
1407 hp_menu
[i
].data
.val
= 'E';
1409 hp_menu
[++i
].type
= tTail
;
1411 mswin_popup(hp_menu
);
1421 help_subsection_popup(SCROLL_S
*sparms
, int in_handle
)
1427 hp_menu
[++i
].type
= tQueue
;
1428 hp_menu
[i
].label
.style
= lNormal
;
1429 hp_menu
[i
].label
.string
= "View Help Section";
1430 hp_menu
[i
].data
.val
= 'V';
1433 hp_menu
[++i
].type
= tQueue
;
1434 hp_menu
[i
].label
.style
= lNormal
;
1435 hp_menu
[i
].label
.string
= "Previous Help Section";
1436 hp_menu
[i
].data
.val
= 'P';
1438 hp_menu
[++i
].type
= tQueue
;
1439 hp_menu
[i
].label
.style
= lNormal
;
1440 hp_menu
[i
].label
.string
= "Exit Help";
1441 hp_menu
[i
].data
.val
= 'E';
1443 hp_menu
[++i
].type
= tTail
;
1445 if(mswin_popup(hp_menu
) == (in_handle
? 1 : 0))
1446 /*(void) helper_internal()*/;
1451 #endif /* _WINDOWS */