2 * ========================================================================
3 * Copyright 2013-2022 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
);
407 helper_getc(unsigned char *c
)
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
;
709 int jo
, lo
, hi
, donejo
, donelo
, donehi
;
711 int cmd
, timestamps
=0, show_level
=-1;
712 char debugkeylabel
[20];
713 /* TRANSLATORS: command label asking pine to include time stamps in output */
714 char timestampkeylabel
[] = N_("Timestamps");
715 /* TRANSLATORS: do not include time stamps in output */
716 char *notimestampkeylabel
= N_("NoTimestamps");
718 if((rmjofirst
< 0 && rmlofirst
< 0 && rmhifirst
< 0)
719 || rmjofirst
>= RMJLEN
|| rmjolast
>= RMJLEN
720 || rmlofirst
>= RMLLEN
|| rmlolast
>= RMLLEN
721 || rmhifirst
>= RMHLEN
|| rmhilast
>= RMHLEN
722 || (rmjofirst
>= 0 && rmjolast
< 0)
723 || (rmlofirst
>= 0 && rmlolast
< 0)
724 || (rmhifirst
>= 0 && rmhilast
< 0))
729 if(!(in_store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) ||
730 !(out_store
= so_get(CharStar
, NULL
, EDIT_ACCESS
))){
734 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
735 _("Failed allocating memory"));
739 add_review_message(_("Turning off new messages while reviewing"), 0);
740 rm_not_right_now
= 1;
742 donejo
= donehi
= donelo
= 0;
755 while(!(donejo
&& donelo
&& donehi
)){
756 REV_MSG_S
*pjo
, *plo
, *phi
, *p
;
759 pjo
= &rmjoarray
[jo
];
764 plo
= &rmloarray
[lo
];
769 phi
= &rmhiarray
[hi
];
773 if(pjo
&& (!plo
|| pjo
->seq
<= plo
->seq
)
774 && (!phi
|| pjo
->seq
<= phi
->seq
))
776 else if(plo
&& (!phi
|| plo
->seq
<= phi
->seq
))
786 if(jo
== rmjofirst
&& (((rmjolast
+ 1) % RMJLEN
) == rmjofirst
))
788 _("**** Journal entries prior to this point have been trimmed. ****\n"));
793 if(show_level
>= 0 &&
794 lo
== rmlofirst
&& (((rmlolast
+ 1) % RMLLEN
) == rmlofirst
))
796 _("**** Debug 0-4 entries prior to this point have been trimmed. ****\n"));
801 if(show_level
>= 5 &&
802 hi
== rmhifirst
&& (((rmhilast
+ 1) % RMHLEN
) == rmhifirst
))
804 _("**** Debug 5-9 entries prior to this point have been trimmed. ****\n"));
813 if(p
->level
<= show_level
){
814 if(timestamps
&& p
->timestamp
&& p
->timestamp
[0]){
815 so_puts(in_store
, p
->timestamp
);
816 so_puts(in_store
, ": ");
819 if(p
->message
&& p
->message
[0]){
821 so_puts(in_store
, ">");
823 so_puts(in_store
, p
->message
);
824 so_puts(in_store
, "\n");
834 jo
= (jo
+ 1) % RMJLEN
;
842 lo
= (lo
+ 1) % RMLLEN
;
850 hi
= (hi
+ 1) % RMHLEN
;
863 so_seek(in_store
, 0L, 0);
865 gf_link_filter(gf_wrap
,
866 gf_wrap_filter_opt(ps_global
->ttyo
->screen_cols
- 4,
867 ps_global
->ttyo
->screen_cols
,
868 NULL
, show_level
< 0 ? 2 : 0, GFW_NONE
));
869 gf_set_so_readc(&gc
, in_store
);
870 gf_set_so_writec(&pc
, out_store
);
872 gf_clear_so_writec(out_store
);
873 gf_clear_so_readc(in_store
);
875 memset(&sargs
, 0, sizeof(SCROLL_S
));
876 sargs
.text
.text
= so_text(out_store
);
877 sargs
.text
.src
= CharStar
;
878 sargs
.text
.desc
= _("journal");
879 sargs
.keys
.menu
= &rev_msg_keymenu
;
880 sargs
.proc
.tool
= journal_processor
;
881 sargs
.start
.on
= LastPage
;
882 sargs
.resize_exit
= 1;
883 sargs
.proc
.data
.p
= (void *)&show_level
;
884 setbitmap(sargs
.keys
.bitmap
);
888 sargs
.jump_is_debug
= 1;
889 /* TRANSLATORS: these are some screen titles */
890 sargs
.help
.title
= _("HELP FOR DEBUG JOURNAL");
891 sargs
.help
.text
= h_debugjournal
;
892 sargs
.bar
.title
= _("REVIEW DEBUGGING");
893 #else /* !DEBUGJOURNAL */
894 clrbitn(DEBUG_KEY
, sargs
.keys
.bitmap
);
895 sargs
.help
.title
= _("HELP FOR JOURNAL");
896 sargs
.help
.text
= h_journal
;
897 sargs
.bar
.title
= _("REVIEW RECENT MESSAGES");
898 #endif /* !DEBUGJOURNAL */
900 clrbitn(DEBUG_KEY
, sargs
.keys
.bitmap
);
901 clrbitn(TIMESTAMP_KEY
, sargs
.keys
.bitmap
);
902 sargs
.help
.title
= _("HELP FOR JOURNAL");
903 sargs
.help
.text
= h_journal
;
904 sargs
.bar
.title
= _("REVIEW RECENT MESSAGES");
908 rev_msg_keys
[TIMESTAMP_KEY
].label
= notimestampkeylabel
;
910 rev_msg_keys
[TIMESTAMP_KEY
].label
= timestampkeylabel
;
913 /* TRANSLATORS: shows what numeric level Debug output is displayed at */
914 snprintf(debugkeylabel
, sizeof(debugkeylabel
), _("Debug (%d)"), show_level
);
916 /* TRANSLATORS: include debug output in journal */
917 strncpy(debugkeylabel
, _("DebugView"), sizeof(debugkeylabel
));
919 debugkeylabel
[sizeof(debugkeylabel
)-1] = '\0';
921 rev_msg_keys
[DEBUG_KEY
].label
= debugkeylabel
;
922 KS_OSDATASET(&rev_msg_keys
[DEBUG_KEY
], KS_NONE
);
924 if((cmd
= scrolltool(&sargs
)) == MC_TOGGLE
)
925 timestamps
= !timestamps
;
930 }while(cmd
!= MC_EXIT
);
932 rm_not_right_now
= 0;
933 add_review_message("Done reviewing", 0);
938 journal_processor(int cmd
, MSGNO_S
*msgmap
, SCROLL_S
*sparms
)
941 case MC_TOGGLE
: /* turn timestamps on or off */
945 alpine_panic("Unexpected command in journal_processor");
954 * standard type of storage object used for body parts...
957 #define PART_SO_TYPE TmpFileStar
959 #define PART_SO_TYPE CharStar
964 gripe_gripe_to(char *url
)
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(struct pine
*ps
, BODY
**body
, long msgno
, int flags
)
1046 static char *err
= "Problem creating space for message text.";
1048 char tmp
[MAILTMPLEN
], *p
;
1050 if((store
= so_get(PicoText
, NULL
, EDIT_ACCESS
)) != NULL
){
1051 *body
= mail_newbody();
1053 if((p
= detoken(NULL
, NULL
, 2, 0, 1, NULL
, NULL
)) != NULL
){
1057 fs_give((void **) &p
);
1061 q_status_message(SM_ORDER
| SM_DING
, 3, 4, err
);
1066 /*---- Might have multiple parts ----*/
1067 (*body
)->type
= TYPEMULTIPART
;
1068 /*---- The TEXT part/body ----*/
1069 (*body
)->nested
.part
= mail_newbody_part();
1070 (*body
)->nested
.part
->body
.type
= TYPETEXT
;
1071 (*body
)->nested
.part
->body
.contents
.text
.data
= (void *) store
;
1073 /*---- create object, and write current config into it ----*/
1074 pp
= &((*body
)->nested
.part
->next
);
1076 if(flags
& GRIPE_OPT_CONF
){
1077 *pp
= mail_newbody_part();
1078 pb
= &((*pp
)->body
);
1079 pp
= &((*pp
)->next
);
1080 pb
->type
= TYPETEXT
;
1081 pb
->id
= generate_message_id(NULL
);
1082 pb
->description
= cpystr("Alpine Configuration Data");
1083 pb
->parameter
= mail_newbody_parameter();
1084 pb
->parameter
->attribute
= cpystr("name");
1085 pb
->parameter
->value
= cpystr("config.txt");
1087 if((store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
){
1088 extern char datestamp
[], hoststamp
[];
1090 pb
->contents
.text
.data
= (void *) store
;
1091 gf_set_so_writec(&pc
, store
);
1092 gf_puts("Alpine built ", pc
);
1093 gf_puts(datestamp
, pc
);
1094 gf_puts(" on host: ", pc
);
1095 gf_puts(hoststamp
, pc
);
1099 dump_pine_struct(ps
, pc
);
1100 dump_config(ps
, pc
, 0);
1103 pb
->size
.bytes
= strlen((char *) so_text(store
));
1104 gf_clear_so_writec(store
);
1107 q_status_message(SM_ORDER
| SM_DING
, 3, 4, err
);
1112 if(flags
& GRIPE_OPT_KEYS
){
1113 *pp
= mail_newbody_part();
1114 pb
= &((*pp
)->body
);
1115 pp
= &((*pp
)->next
);
1116 pb
->type
= TYPETEXT
;
1117 pb
->id
= generate_message_id(NULL
);
1118 pb
->description
= cpystr("Recent User Input");
1119 pb
->parameter
= mail_newbody_parameter();
1120 pb
->parameter
->attribute
= cpystr("name");
1121 pb
->parameter
->value
= cpystr("uinput.txt");
1123 if((store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
){
1124 pb
->contents
.text
.data
= (void *) store
;
1126 so_puts(store
, "User's most recent input:\n");
1128 /* dump last n keystrokes */
1129 so_puts(store
, "========== Latest keystrokes ==========\n");
1130 while((i
= key_playback(0)) != -1){
1131 snprintf(tmp
, sizeof(tmp
), "\t%s\t(0x%x)\n", pretty_command(i
), i
);
1132 tmp
[sizeof(tmp
)-1] = '\0';
1133 so_puts(store
, tmp
);
1136 pb
->size
.bytes
= strlen((char *) so_text(store
));
1139 q_status_message(SM_ORDER
| SM_DING
, 3, 4, err
);
1144 /* check for local debugging info? */
1145 if((flags
& GRIPE_OPT_LOCAL
)
1146 && ps_global
->VAR_BUGS_EXTRAS
1147 && can_access(ps_global
->VAR_BUGS_EXTRAS
, EXECUTE_ACCESS
) == 0){
1150 *pp
= mail_newbody_part();
1151 pb
= &((*pp
)->body
);
1152 pp
= &((*pp
)->next
);
1153 pb
->type
= TYPETEXT
;
1154 pb
->id
= generate_message_id(NULL
);
1155 pb
->description
= cpystr("Local Configuration Data");
1156 pb
->parameter
= mail_newbody_parameter();
1157 pb
->parameter
->attribute
= cpystr("name");
1158 pb
->parameter
->value
= cpystr("lconfig.txt");
1160 if((store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
){
1164 pb
->contents
.text
.data
= (void *) store
;
1165 gf_set_so_writec(&pc
, store
);
1166 if((syspipe
= open_system_pipe(ps_global
->VAR_BUGS_EXTRAS
,
1168 PIPE_READ
| PIPE_STDERR
| PIPE_USER
,
1169 0, pipe_callback
, pipe_report_error
)) != NULL
){
1170 gf_set_readc(&gc
, (void *)syspipe
, 0, PipeStar
, 0);
1172 error
= gf_pipe(gc
, pc
);
1173 (void) close_system_pipe(&syspipe
, NULL
, pipe_callback
);
1176 error
= "executing config collector";
1178 gf_clear_so_writec(store
);
1182 q_status_message1(SM_ORDER
| SM_DING
, 3, 4,
1183 "Problem %s", error
);
1186 else /* fixup attachment's size */
1187 pb
->size
.bytes
= strlen((char *) so_text(store
));
1190 if((flags
& GRIPE_OPT_MSG
) && mn_get_total(ps
->msgmap
) > 0L){
1193 ps
->redrawer
= att_cur_drawer
;
1196 if((ch
= one_try_want_to("Attach current message to report",
1198 WT_FLUSH_IN
|WT_SEQ_SENSITIVE
)) == 'y'){
1199 *pp
= mail_newbody_part();
1200 pb
= &((*pp
)->body
);
1201 pp
= &((*pp
)->next
);
1202 pb
->type
= TYPEMESSAGE
;
1203 pb
->id
= generate_message_id(NULL
);
1204 snprintf(tmp
, sizeof(tmp
), "Problem Message (%ld of %ld)",
1205 mn_get_cur(ps
->msgmap
), mn_get_total(ps
->msgmap
));
1206 tmp
[sizeof(tmp
)-1] = '\0';
1207 pb
->description
= cpystr(tmp
);
1209 /*---- Package each message in a storage object ----*/
1210 if((store
= so_get(PART_SO_TYPE
, NULL
, EDIT_ACCESS
)) != NULL
){
1211 pb
->contents
.text
.data
= (void *) store
;
1214 q_status_message(SM_ORDER
| SM_DING
, 3, 4, err
);
1218 /* write the header */
1219 if((p
= mail_fetch_header(ps
->mail_stream
, msgno
, NIL
, NIL
,
1220 NIL
, FT_PEEK
)) && *p
)
1225 pb
->size
.bytes
= strlen(p
);
1226 so_puts(store
, "\015\012");
1228 if((p
= pine_mail_fetch_text(ps
->mail_stream
,
1229 msgno
, NULL
, NULL
, NIL
))
1235 pb
->size
.bytes
+= strlen(p
);
1238 q_status_message(SM_ORDER
, 0, 3, "Bug report cancelled.");
1244 /*---- Only one part! ----*/
1245 (*body
)->type
= TYPETEXT
;
1246 (*body
)->contents
.text
.data
= (void *) store
;
1254 gripe_token_addr(char *token
)
1259 if(token
&& *token
++ == '_'){
1260 if(!strcmp(token
, "LOCAL_ADDRESS_")){
1261 p
= (ps_global
->VAR_LOCAL_ADDRESS
1262 && ps_global
->VAR_LOCAL_ADDRESS
[0])
1263 ? ps_global
->VAR_LOCAL_ADDRESS
1265 a
= rfc822_parse_mailbox(&p
, ps_global
->maildomain
);
1266 a
->personal
= cpystr((ps_global
->VAR_LOCAL_FULLNAME
1267 && ps_global
->VAR_LOCAL_FULLNAME
[0])
1268 ? ps_global
->VAR_LOCAL_FULLNAME
1269 : "Place to report Alpine Bugs");
1271 else if(!strcmp(token
, "BUGS_ADDRESS_")){
1272 p
= (ps_global
->VAR_BUGS_ADDRESS
1273 && ps_global
->VAR_BUGS_ADDRESS
[0])
1274 ? ps_global
->VAR_BUGS_ADDRESS
: "postmaster";
1275 a
= rfc822_parse_mailbox(&p
, ps_global
->maildomain
);
1276 a
->personal
= cpystr((ps_global
->VAR_BUGS_FULLNAME
1277 && ps_global
->VAR_BUGS_FULLNAME
[0])
1278 ? ps_global
->VAR_BUGS_FULLNAME
1279 : "Place to report Alpine Bugs");
1293 * Build our contribution to the subject; part constant string
1294 * and random 4 character alpha numeric string.
1296 i
= (int)(random() % 36L);
1297 j
= (int)(random() % 36L);
1298 k
= (int)(random() % 36L);
1299 l
= (int)(random() % 36L);
1300 tmp_20k_buf
[0] = '\0';
1301 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s (ID %c%c%d%c%c)", key
,
1302 (i
< 10) ? '0' + i
: 'A' + (i
- 10),
1303 (j
< 10) ? '0' + j
: 'A' + (j
- 10),
1304 (int)(random() % 10L),
1305 (k
< 10) ? '0' + k
: 'A' + (k
- 10),
1306 (l
< 10) ? '0' + l
: 'A' + (l
- 10));
1307 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
1308 return(cpystr(tmp_20k_buf
));
1313 * Used by gripe_tool.
1316 att_cur_drawer(void)
1321 /* blat helpful message to screen */
1325 dline
< ps_global
->ttyo
->screen_rows
- FOOTER_ROWS(ps_global
);
1327 for(i
= 0; i
< 256 && att_cur_msg
[j
] && att_cur_msg
[j
] != '\n'; i
++)
1328 buf
[i
] = att_cur_msg
[j
++];
1336 PutLine0(dline
, 1, buf
);
1347 pcpine_help(HelpType section
)
1349 char **help_lines
, *help_text
= NULL
;
1353 /* assumption here is that HelpType is char ** */
1354 help_lines
= section
;
1355 if (help_lines
!= NULL
){
1356 init_helper_getc(help_lines
);
1357 if(store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)){
1358 gf_set_so_writec(&pc
, store
);
1361 gf_link_filter(gf_local_nvtnl
, NULL
);
1363 gf_link_filter(gf_html2plain
,
1364 gf_html2plain_opt(NULL
,
1365 ps_global
->ttyo
->screen_cols
,
1366 non_messageview_margin(), NULL
, NULL
, GFHP_STRIPPED
));
1368 if(!gf_pipe(helper_getc
, pc
)){
1369 help_text
= (char *) store
->txt
;
1370 store
->txt
= (void *) NULL
;
1373 gf_clear_so_writec(store
);
1386 help_popup(SCROLL_S
*sparms
, int in_handle
)
1392 hp_menu
[++i
].type
= tQueue
;
1393 hp_menu
[i
].label
.style
= lNormal
;
1394 hp_menu
[i
].label
.string
= "View Help Section";
1395 hp_menu
[i
].data
.val
= 'V';
1398 hp_menu
[++i
].type
= tQueue
;
1399 hp_menu
[i
].label
.style
= lNormal
;
1400 hp_menu
[i
].label
.string
= "Exit Help";
1401 hp_menu
[i
].data
.val
= 'E';
1403 hp_menu
[++i
].type
= tTail
;
1405 mswin_popup(hp_menu
);
1415 help_subsection_popup(SCROLL_S
*sparms
, int in_handle
)
1421 hp_menu
[++i
].type
= tQueue
;
1422 hp_menu
[i
].label
.style
= lNormal
;
1423 hp_menu
[i
].label
.string
= "View Help Section";
1424 hp_menu
[i
].data
.val
= 'V';
1427 hp_menu
[++i
].type
= tQueue
;
1428 hp_menu
[i
].label
.style
= lNormal
;
1429 hp_menu
[i
].label
.string
= "Previous Help Section";
1430 hp_menu
[i
].data
.val
= 'P';
1432 hp_menu
[++i
].type
= tQueue
;
1433 hp_menu
[i
].label
.style
= lNormal
;
1434 hp_menu
[i
].label
.string
= "Exit Help";
1435 hp_menu
[i
].data
.val
= 'E';
1437 hp_menu
[++i
].type
= tTail
;
1439 if(mswin_popup(hp_menu
) == (in_handle
? 1 : 0))
1440 /*(void) helper_internal()*/;
1445 #endif /* _WINDOWS */