1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: help.c 1032 2008-04-11 00:30:04Z hubert@u.washington.edu $";
6 * ========================================================================
7 * Copyright 2006-2008 University of Washington
8 * Copyright 2013-2016 Eduardo Chappa
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 * ========================================================================
30 #include "../pith/state.h"
31 #include "../pith/conf.h"
32 #include "../pith/filter.h"
33 #include "../pith/msgno.h"
34 #include "../pith/pipe.h"
35 #include "../pith/util.h"
36 #include "../pith/detoken.h"
37 #include "../pith/list.h"
38 #include "../pith/margin.h"
41 typedef struct _help_scroll
{
42 unsigned keys_formatted
:1; /* Has full keymenu been formatted? */
43 char **help_source
; /* Source of displayed help text */
54 typedef struct _help_print_state
{
60 static HPRT_S
*g_hprt
;
63 static char att_cur_msg
[] = "\
66 If you think that the \"current\" message may be related to the bug you\n\
67 are reporting you may include it as an attachment. If you want to\n\
68 include a message but you aren't sure if it is the current message,\n\
69 cancel this bug report, go to the folder index, place the cursor on\n\
70 the message you wish to include, then return to the main menu and run\n\
71 the bug report command again. Answer \"Y\" when asked the question\n\
72 \"Attach current message to report?\"\n\
74 This bug report will also automatically include your pine\n\
75 configuration file, which is helpful when investigating the problem.";
78 #define GRIPE_OPT_CONF 0x01
79 #define GRIPE_OPT_MSG 0x02
80 #define GRIPE_OPT_LOCAL 0x04
81 #define GRIPE_OPT_KEYS 0x08
87 int helper_internal(HelpType
, char *, char *, int);
88 int help_processor(int, MSGNO_S
*, SCROLL_S
*);
89 void help_keymenu_tweek(SCROLL_S
*, int);
90 void print_all_help(void);
91 void print_help_page_title(char *, size_t, HPRT_S
*);
92 int print_help_page_break(long, char *, LT_INS_S
**, void *);
93 int help_bogus_input(UCS
);
94 int gripe_newbody(struct pine
*, BODY
**, long, int);
95 ADDRESS
*gripe_token_addr(char *);
96 char *gripe_id(char *);
97 void att_cur_drawer(void);
98 int journal_processor(int, MSGNO_S
*, SCROLL_S
*);
99 int help_popup(SCROLL_S
*, int);
101 int help_subsection_popup(SCROLL_S
*, int);
106 /*----------------------------------------------------------------------
107 Get the help text in the proper format and call scroller
109 Args: text -- The help text to display (from pine.help --> helptext.c)
110 title -- The title of the help text
112 Result: format text and call scroller
114 The pages array contains the line number of the start of the pages in
115 the text. Page 1 is in the 0th element of the array.
116 The list is ended with a page line number of -1. Line number 0 is also
117 the first line in the text.
120 helper_internal(HelpType text
, char *frag
, char *title
, int flags
)
125 char *error
= NULL
, tmp_title
[MAX_SCREEN_COLS
+ 1];
127 HANDLE_S
*handles
= NULL
, *htmp
;
128 HELP_SCROLL_S hscroll
;
131 dprint((1, "\n\n ---- HELPER ----\n"));
133 /* assumption here is that HelpType is char ** */
136 if(F_ON(F_BLANK_KEYMENU
,ps_global
)){
137 FOOTER_ROWS(ps_global
) = 3;
138 clearfooter(ps_global
);
139 ps_global
->mangled_screen
= 1;
142 if(flags
& HLPD_NEWWIN
){
143 fix_windsize(ps_global
);
148 * At this point, shown_text is a charstarstar with html
149 * Turn it into a charstar with digested html
152 init_helper_getc(shown_text
);
153 init_handles(&handles
);
155 memset(&hscroll
, 0, sizeof(HELP_SCROLL_S
));
156 hscroll
.help_source
= shown_text
;
157 if((store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
){
158 gf_set_so_writec(&pc
, store
);
161 if(!struncmp(shown_text
[0], "<html>", 6))
162 gf_link_filter(gf_html2plain
,
163 gf_html2plain_opt("x-alpine-help:",
164 ps_global
->ttyo
->screen_cols
,
165 non_messageview_margin(),
166 &handles
, NULL
, GFHP_LOCAL_HANDLES
));
168 gf_link_filter(gf_wrap
, gf_wrap_filter_opt(
169 ps_global
->ttyo
->screen_cols
,
170 ps_global
->ttyo
->screen_cols
,
171 NULL
, 0, GFW_HANDLES
| GFW_SOFTHYPHEN
));
173 error
= gf_pipe(helper_getc
, pc
);
175 gf_clear_so_writec(store
);
182 for(htmp
= handles
; htmp
; htmp
= htmp
->next
)
185 && (htmp
->h
.url
.path
[0] == 'x'
186 || htmp
->h
.url
.path
[0] == '#'))
187 htmp
->force_display
= 1;
189 /* This is mostly here to get the curses variables
190 * for line and column in sync with where the
191 * cursor is on the screen. This gets warped when
192 * the composer is called because it does it's own
197 memset(&sargs
, 0, sizeof(SCROLL_S
));
198 sargs
.text
.text
= so_text(store
);
199 sargs
.text
.src
= CharStar
;
200 sargs
.text
.desc
= _("help text");
201 if((sargs
.text
.handles
= handles
) != NULL
)
202 while(sargs
.text
.handles
->type
== URL
203 && !sargs
.text
.handles
->h
.url
.path
204 && sargs
.text
.handles
->next
)
205 sargs
.text
.handles
= sargs
.text
.handles
->next
;
207 if(!(sargs
.bar
.title
= title
)){
208 if(!struncmp(shown_text
[0], "<html>", 6)){
212 /* if we're looking at html, look for a <title>
213 * in the <head>... */
216 && struncmp(shown_text
[i
], "</head>", 7);
218 if(!struncmp(shown_text
[i
], "<title>", 7)){
219 strncpy(tmp_20k_buf
, &shown_text
[i
][7], SIZEOF_20KBUF
);
220 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
221 if((p
= strchr(tmp_20k_buf
, '<')) != NULL
)
224 snprintf(sargs
.bar
.title
= tmp_title
, sizeof(tmp_title
),
225 "%s -- %.*s", _("HELP"),
226 ps_global
->ttyo
->screen_cols
-10,
227 strsquish(tmp_20k_buf
+ 500, SIZEOF_20KBUF
,
229 ps_global
->ttyo
->screen_cols
/ 3));
230 tmp_title
[sizeof(tmp_title
)-1] = '\0';
236 sargs
.bar
.title
= _("HELP TEXT");
239 sargs
.bar
.style
= TextPercent
;
240 sargs
.proc
.tool
= help_processor
;
241 sargs
.proc
.data
.p
= (void *) &hscroll
;
242 sargs
.resize_exit
= 1;
243 sargs
.help
.text
= h_special_help_nav
;
244 sargs
.help
.title
= _("HELP FOR HELP TEXT");
245 sargs
.keys
.menu
= &km
;
248 memcpy(&keys
[0], help_keymenu
.keys
,
249 (help_keymenu
.how_many
* 12) * sizeof(struct key
));
250 setbitmap(sargs
.keys
.bitmap
);
251 if(flags
& HLPD_FROMHELP
){
252 km
.keys
[HLP_EXIT_KEY
].name
= "P";
253 km
.keys
[HLP_EXIT_KEY
].label
= _("Prev Help");
254 km
.keys
[HLP_EXIT_KEY
].bind
.cmd
= MC_FINISH
;
255 km
.keys
[HLP_EXIT_KEY
].bind
.ch
[0] = 'p';
257 km
.keys
[HLP_SUBEXIT_KEY
].name
= "E";
258 km
.keys
[HLP_SUBEXIT_KEY
].label
= _("Exit Help");
259 km
.keys
[HLP_SUBEXIT_KEY
].bind
.cmd
= MC_EXIT
;
260 km
.keys
[HLP_SUBEXIT_KEY
].bind
.ch
[0] = 'e';
262 else if(text
== h_special_help_nav
){
263 km
.keys
[HLP_EXIT_KEY
].name
= "P";
264 km
.keys
[HLP_EXIT_KEY
].label
= _("Prev Help");
265 km
.keys
[HLP_EXIT_KEY
].bind
.cmd
= MC_FINISH
;
266 km
.keys
[HLP_EXIT_KEY
].bind
.ch
[0] = 'p';
268 clrbitn(HLP_MAIN_KEY
, sargs
.keys
.bitmap
);
269 clrbitn(HLP_SUBEXIT_KEY
, sargs
.keys
.bitmap
);
272 km
.keys
[HLP_EXIT_KEY
].name
= "E";
273 km
.keys
[HLP_EXIT_KEY
].label
= _("Exit Help");
274 km
.keys
[HLP_EXIT_KEY
].bind
.cmd
= MC_EXIT
;
275 km
.keys
[HLP_EXIT_KEY
].bind
.ch
[0] = 'e';
277 km
.keys
[HLP_SUBEXIT_KEY
].name
= "?";
278 /* TRANSLATORS: this is the label of a command where
279 the user is asking for Help about the Help command */
280 km
.keys
[HLP_SUBEXIT_KEY
].label
= _("Help Help");
281 km
.keys
[HLP_SUBEXIT_KEY
].bind
.cmd
= MC_HELP
;
282 km
.keys
[HLP_SUBEXIT_KEY
].bind
.ch
[0] = '?';
285 if(flags
& HLPD_SIMPLE
){
286 clrbitn(HLP_MAIN_KEY
, sargs
.keys
.bitmap
);
289 sargs
.bogus_input
= help_bogus_input
;
292 sargs
.keys
.each_cmd
= help_keymenu_tweek
;
293 hscroll
.keys_formatted
= 0;
296 clrbitn(HLP_VIEW_HANDLE
, sargs
.keys
.bitmap
);
297 clrbitn(HLP_PREV_HANDLE
, sargs
.keys
.bitmap
);
298 clrbitn(HLP_NEXT_HANDLE
, sargs
.keys
.bitmap
);
301 if(text
!= main_menu_tx
302 && text
!= h_mainhelp_pinehelp
)
303 clrbitn(HLP_ALL_KEY
, sargs
.keys
.bitmap
);
306 sargs
.start
.on
= Fragment
;
307 sargs
.start
.loc
.frag
= frag
;
308 frag
= NULL
; /* ignore next time */
311 sargs
.start
.on
= Offset
;
312 sargs
.start
.loc
.offset
= offset
;
315 sargs
.start
.on
= FirstPage
;
318 sargs
.mouse
.popup
= (flags
& HLPD_FROMHELP
)
319 ? help_subsection_popup
: help_popup
;
322 cmd
= scrolltool(&sargs
);
324 offset
= sargs
.start
.loc
.offset
;
326 if(F_ON(F_BLANK_KEYMENU
,ps_global
))
327 FOOTER_ROWS(ps_global
) = 1;
335 free_handles(&handles
);
337 while(cmd
== MC_RESIZE
);
344 * helper -- compatibility function around newer helper_internal
347 helper(HelpType text
, char *title
, int flags
)
349 return(helper_internal(text
, NULL
, title
, flags
));
354 init_helper_getc(char **help_txt
)
357 g_h_text
.line
= help_txt
;
358 g_h_text
.offset
= *g_h_text
.line
;
359 if(g_h_text
.offset
&& g_h_text
.offset
[0])
360 g_h_text
.offset
= _(g_h_text
.offset
);
372 else if(g_h_text
.offset
&& *g_h_text
.line
){
373 if(!(*c
= *g_h_text
.offset
++)){
374 g_h_text
.offset
= *++g_h_text
.line
;
375 if(g_h_text
.offset
&& g_h_text
.offset
[0])
376 g_h_text
.offset
= _(g_h_text
.offset
);
390 help_processor(int cmd
, MSGNO_S
*msgmap
, SCROLL_S
*sparms
)
394 struct help_texts
*t
;
397 /*----------- Print all the help ------------*/
403 snprintf(message
, sizeof(message
), "%s", STYLE_NAME(sparms
));
404 message
[sizeof(message
)-1] = '\0';
405 if(open_printer(message
) == 0){
406 print_help(((HELP_SCROLL_S
*)sparms
->proc
.data
.p
)->help_source
);
412 case MC_EXPORT
: /* reuse old definition, so as not to patch pine.h */
415 for(t
= h_texts
; t
->help_text
!= NO_HELP
; t
++)
416 if(t
->help_text
== ((HELP_SCROLL_S
*)sparms
->proc
.data
.p
)->help_source
){
417 strncpy(help_name
, t
->tag
, sizeof(help_name
)-1);
418 help_name
[sizeof(help_name
)-1] = '\0';
422 q_status_message1(SM_ORDER
, 0, 2,
423 "Internal Name: x-alpine-help:%s", help_name
);
425 q_status_message(SM_ORDER
|SM_DING
, 1, 2,
426 "Can not find link for text help");
435 alpine_panic("Unhandled case");
443 help_keymenu_tweek(SCROLL_S
*sparms
, int handle_hidden
)
446 sparms
->keys
.menu
->keys
[HLP_VIEW_HANDLE
].name
= "";
447 sparms
->keys
.menu
->keys
[HLP_VIEW_HANDLE
].label
= "";
450 if(!((HELP_SCROLL_S
*)sparms
->proc
.data
.p
)->keys_formatted
){
451 /* If label's always been blank, force reformatting */
452 mark_keymenu_dirty();
453 sparms
->keys
.menu
->width
= 0;
454 ((HELP_SCROLL_S
*)sparms
->proc
.data
.p
)->keys_formatted
= 1;
457 sparms
->keys
.menu
->keys
[HLP_VIEW_HANDLE
].name
= "V";
458 sparms
->keys
.menu
->keys
[HLP_VIEW_HANDLE
].label
= "[" N_("View Link") "]";
464 * print_help - send the raw array of lines to printer
467 print_help(char **text
)
469 char *error
, buf
[256];
472 init_helper_getc(text
);
474 memset(g_hprt
= &help_data
, 0, sizeof(HPRT_S
));
480 if(!struncmp(text
[0], "<html>", 6)){
484 gf_link_filter(gf_html2plain
,
485 gf_html2plain_opt(NULL
,80,non_messageview_margin(),
486 NULL
,NULL
,GFHP_STRIPPED
));
487 for(i
= 1; i
<= 5 && text
[i
]; i
++)
488 if(!struncmp(text
[i
], "<title>", 7)
489 && (p
= srchstr(text
[i
] + 7, "</title>"))
491 help_data
.title
= text
[i
] + 7;
492 help_data
.title_len
= p
- help_data
.title
;
497 gf_link_filter(gf_wrap
, gf_wrap_filter_opt(80, 80, NULL
, 0, GFW_NONE
));
499 gf_link_filter(gf_line_test
,
500 gf_line_test_opt(print_help_page_break
, NULL
));
501 gf_link_filter(gf_nvtnl_local
, NULL
);
503 print_help_page_title(buf
, sizeof(buf
), &help_data
);
505 print_text(NEWLINE
); /* terminate it */
506 print_text(NEWLINE
); /* and write two blank links */
509 if((error
= gf_pipe(helper_getc
, print_char
)) != NULL
)
510 q_status_message1(SM_ORDER
| SM_DING
, 3, 3, _("Printing Error: %s"), error
);
512 print_char(ctrl('L')); /* new page. */
519 struct help_texts
*t
;
521 int we_turned_on
= 0;
523 if(open_printer(_("all 150+ pages of help text")) == 0) {
524 we_turned_on
= intr_handling_on();
525 for(t
= h_texts
; (h
= t
->help_text
) != NO_HELP
; t
++) {
526 if(ps_global
->intr_pending
){
527 q_status_message(SM_ORDER
, 3, 3,
528 _("Print of all help cancelled"));
544 * print_help_page_title --
547 print_help_page_title(char *buf
, size_t buflen
, HPRT_S
*hprt
)
549 snprintf(buf
, buflen
, " Alpine Help%s%.*s%*s%d",
550 hprt
->title_len
? ": " : " Text",
551 MIN(55, hprt
->title_len
), hprt
->title_len
? hprt
->title
: "",
552 59 - (hprt
->title_len
? MIN(55, hprt
->title_len
) : 5),
553 "Page ", hprt
->page
);
554 buf
[buflen
-1] = '\0';
559 * print_help_page_break -- insert page breaks and such for printed
563 print_help_page_break(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
567 if(((linenum
+ (g_hprt
->page
* 3)) % 62) == 0){
568 g_hprt
->page
++; /* start on new page */
570 print_help_page_title(buf
+ 1, sizeof(buf
)-1, g_hprt
);
571 strncat(buf
, "\015\012\015\012\015\012", sizeof(buf
)-strlen(buf
)-1);
572 buf
[sizeof(buf
)-1] = '\0';
573 ins
= gf_line_test_new_ins(ins
, line
, buf
, strlen(buf
));
581 * help_bogus_input - used by scrolltool to complain about
582 * invalid user input.
585 help_bogus_input(UCS ch
)
587 bogus_command(ch
, NULL
);
593 url_local_helper(char *url
)
595 if((!struncmp(url
, "x-alpine-help:", 14) && *(url
+= 14))
596 || (!struncmp(url
, "x-pine-help:", 12) && *(url
+= 12))){
600 /* internal fragment reference? */
601 if((frag
= strchr(url
, '#')) != NULL
){
604 if((len
= frag
- url
) != 0){
605 newhelp
= help_name2section(url
, len
);
608 url_local_fragment(url
);
613 newhelp
= help_name2section(url
, strlen(url
));
616 if(newhelp
!= NO_HELP
){
619 rv
= helper_internal(newhelp
, frag
, _("HELP SUB-SECTION"),
620 HLPD_NEWWIN
| HLPD_SIMPLE
| HLPD_FROMHELP
);
621 ps_global
->mangled_screen
= 1;
622 return((rv
== MC_EXIT
) ? 2 : 1);
626 q_status_message1(SM_ORDER
| SM_DING
, 0, 3,
627 _("Unrecognized Internal help: \"%s\""), url
);
633 url_local_config(char *url
)
635 if(!struncmp(url
, "x-alpine-config:", 16)){
639 config
= get_supported_options();
641 /* TRANSLATORS: Help for configuration */
642 rv
= helper_internal(config
, NULL
, _("HELP CONFIG"),
643 HLPD_NEWWIN
| HLPD_SIMPLE
| HLPD_FROMHELP
);
644 free_list_array(&config
);
647 ps_global
->mangled_screen
= 1;
648 return((rv
== MC_EXIT
) ? 2 : 1);
651 q_status_message1(SM_ORDER
| SM_DING
, 0, 3,
652 _("Unrecognized Internal help: \"%s\""), url
);
657 /*----------------------------------------------------------------------
658 Review latest status messages
661 review_messages(void)
664 STORE_S
*in_store
= NULL
, *out_store
= NULL
;
666 int jo
, lo
, hi
, donejo
, donelo
, donehi
;
668 int cmd
, timestamps
=0, show_level
=-1;
669 char debugkeylabel
[20];
670 /* TRANSLATORS: command label asking pine to include time stamps in output */
671 char timestampkeylabel
[] = N_("Timestamps");
672 /* TRANSLATORS: do not include time stamps in output */
673 char *notimestampkeylabel
= N_("NoTimestamps");
675 if((rmjofirst
< 0 && rmlofirst
< 0 && rmhifirst
< 0)
676 || rmjofirst
>= RMJLEN
|| rmjolast
>= RMJLEN
677 || rmlofirst
>= RMLLEN
|| rmlolast
>= RMLLEN
678 || rmhifirst
>= RMHLEN
|| rmhilast
>= RMHLEN
679 || (rmjofirst
>= 0 && rmjolast
< 0)
680 || (rmlofirst
>= 0 && rmlolast
< 0)
681 || (rmhifirst
>= 0 && rmhilast
< 0))
686 if(!(in_store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) ||
687 !(out_store
= so_get(CharStar
, NULL
, EDIT_ACCESS
))){
691 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
692 _("Failed allocating memory"));
696 add_review_message(_("Turning off new messages while reviewing"), 0);
697 rm_not_right_now
= 1;
699 donejo
= donehi
= donelo
= 0;
712 while(!(donejo
&& donelo
&& donehi
)){
713 REV_MSG_S
*pjo
, *plo
, *phi
, *p
;
716 pjo
= &rmjoarray
[jo
];
721 plo
= &rmloarray
[lo
];
726 phi
= &rmhiarray
[hi
];
730 if(pjo
&& (!plo
|| pjo
->seq
<= plo
->seq
)
731 && (!phi
|| pjo
->seq
<= phi
->seq
))
733 else if(plo
&& (!phi
|| plo
->seq
<= phi
->seq
))
743 if(jo
== rmjofirst
&& (((rmjolast
+ 1) % RMJLEN
) == rmjofirst
))
745 _("**** Journal entries prior to this point have been trimmed. ****\n"));
750 if(show_level
>= 0 &&
751 lo
== rmlofirst
&& (((rmlolast
+ 1) % RMLLEN
) == rmlofirst
))
753 _("**** Debug 0-4 entries prior to this point have been trimmed. ****\n"));
758 if(show_level
>= 5 &&
759 hi
== rmhifirst
&& (((rmhilast
+ 1) % RMHLEN
) == rmhifirst
))
761 _("**** Debug 5-9 entries prior to this point have been trimmed. ****\n"));
770 if(p
->level
<= show_level
){
771 if(timestamps
&& p
->timestamp
&& p
->timestamp
[0]){
772 so_puts(in_store
, p
->timestamp
);
773 so_puts(in_store
, ": ");
776 if(p
->message
&& p
->message
[0]){
778 so_puts(in_store
, ">");
780 so_puts(in_store
, p
->message
);
781 so_puts(in_store
, "\n");
791 jo
= (jo
+ 1) % RMJLEN
;
799 lo
= (lo
+ 1) % RMLLEN
;
807 hi
= (hi
+ 1) % RMHLEN
;
820 so_seek(in_store
, 0L, 0);
822 gf_link_filter(gf_wrap
,
823 gf_wrap_filter_opt(ps_global
->ttyo
->screen_cols
- 4,
824 ps_global
->ttyo
->screen_cols
,
825 NULL
, show_level
< 0 ? 2 : 0, GFW_NONE
));
826 gf_set_so_readc(&gc
, in_store
);
827 gf_set_so_writec(&pc
, out_store
);
829 gf_clear_so_writec(out_store
);
830 gf_clear_so_readc(in_store
);
832 memset(&sargs
, 0, sizeof(SCROLL_S
));
833 sargs
.text
.text
= so_text(out_store
);
834 sargs
.text
.src
= CharStar
;
835 sargs
.text
.desc
= _("journal");
836 sargs
.keys
.menu
= &rev_msg_keymenu
;
837 sargs
.proc
.tool
= journal_processor
;
838 sargs
.start
.on
= LastPage
;
839 sargs
.resize_exit
= 1;
840 sargs
.proc
.data
.p
= (void *)&show_level
;
841 setbitmap(sargs
.keys
.bitmap
);
845 sargs
.jump_is_debug
= 1;
846 /* TRANSLATORS: these are some screen titles */
847 sargs
.help
.title
= _("HELP FOR DEBUG JOURNAL");
848 sargs
.help
.text
= h_debugjournal
;
849 sargs
.bar
.title
= _("REVIEW DEBUGGING");
850 #else /* !DEBUGJOURNAL */
851 clrbitn(DEBUG_KEY
, sargs
.keys
.bitmap
);
852 sargs
.help
.title
= _("HELP FOR JOURNAL");
853 sargs
.help
.text
= h_journal
;
854 sargs
.bar
.title
= _("REVIEW RECENT MESSAGES");
855 #endif /* !DEBUGJOURNAL */
857 clrbitn(DEBUG_KEY
, sargs
.keys
.bitmap
);
858 clrbitn(TIMESTAMP_KEY
, sargs
.keys
.bitmap
);
859 sargs
.help
.title
= _("HELP FOR JOURNAL");
860 sargs
.help
.text
= h_journal
;
861 sargs
.bar
.title
= _("REVIEW RECENT MESSAGES");
865 rev_msg_keys
[TIMESTAMP_KEY
].label
= notimestampkeylabel
;
867 rev_msg_keys
[TIMESTAMP_KEY
].label
= timestampkeylabel
;
870 /* TRANSLATORS: shows what numeric level Debug output is displayed at */
871 snprintf(debugkeylabel
, sizeof(debugkeylabel
), _("Debug (%d)"), show_level
);
873 /* TRANSLATORS: include debug output in journal */
874 strncpy(debugkeylabel
, _("DebugView"), sizeof(debugkeylabel
));
876 debugkeylabel
[sizeof(debugkeylabel
)-1] = '\0';
878 rev_msg_keys
[DEBUG_KEY
].label
= debugkeylabel
;
879 KS_OSDATASET(&rev_msg_keys
[DEBUG_KEY
], KS_NONE
);
881 if((cmd
= scrolltool(&sargs
)) == MC_TOGGLE
)
882 timestamps
= !timestamps
;
887 }while(cmd
!= MC_EXIT
);
889 rm_not_right_now
= 0;
890 add_review_message("Done reviewing", 0);
895 journal_processor(int cmd
, MSGNO_S
*msgmap
, SCROLL_S
*sparms
)
898 case MC_TOGGLE
: /* turn timestamps on or off */
902 alpine_panic("Unexpected command in journal_processor");
911 * standard type of storage object used for body parts...
914 #define PART_SO_TYPE TmpFileStar
916 #define PART_SO_TYPE CharStar
924 char *composer_title
, *url_copy
, *optstr
, *p
;
927 ENVELOPE
*outgoing
= NULL
;
929 PINEFIELD
*pf
= NULL
;
930 long msgno
= mn_m2raw(ps_global
->msgmap
,
931 mn_get_cur(ps_global
->msgmap
));
933 url_copy
= cpystr(url
+ strlen("x-alpine-gripe:"));
934 if((optstr
= strchr(url_copy
, '?')) != NULL
)
937 outgoing
= mail_newenvelope();
938 outgoing
->message_id
= generate_message_id();
940 if((outgoing
->to
= gripe_token_addr(url_copy
)) != NULL
){
941 composer_title
= _("COMPOSE TO LOCAL SUPPORT");
943 "\n\n -- Send to local support(%s@%s) --\n",
944 outgoing
->to
->mailbox
? outgoing
->to
->mailbox
: "NULL",
945 outgoing
->to
->host
? outgoing
->to
->host
: "NULL"));
947 else{ /* must be global */
948 composer_title
= _("REQUEST FOR ASSISTANCE");
949 rfc822_parse_adrlist(&outgoing
->to
, url_copy
, ps_global
->maildomain
);
956 if((p
= strchr(optstr
, '?')) != NULL
) /* tie off list item */
959 if(!strucmp(optstr
, "config"))
960 opts
|= GRIPE_OPT_CONF
;
961 else if(!strucmp(optstr
, "curmsg"))
962 opts
|= GRIPE_OPT_MSG
;
963 else if(!strucmp(optstr
, "local"))
964 opts
|= GRIPE_OPT_LOCAL
;
965 else if(!strucmp(optstr
, "keys"))
966 opts
|= GRIPE_OPT_KEYS
;
971 /* build body and hand off to composer... */
972 if(gripe_newbody(ps_global
, &body
, msgno
, opts
) == 0){
973 pf
= (PINEFIELD
*) fs_get(sizeof(PINEFIELD
));
974 memset(pf
, 0, sizeof(PINEFIELD
));
975 pf
->name
= cpystr("X-Generated-Via");
977 pf
->textbuf
= gripe_id("Alpine Bug Report screen");
978 memset((void *)&fake_reply
, 0, sizeof(fake_reply
));
979 fake_reply
.pseudo
= 1;
980 fake_reply
.data
.pico_flags
= P_HEADEND
;
981 pine_send(outgoing
, &body
, composer_title
, NULL
, NULL
,
982 &fake_reply
, NULL
, NULL
, pf
, PS_STICKY_TO
);
985 ps_global
->mangled_screen
= 1;
986 mail_free_envelope(&outgoing
);
989 pine_free_body(&body
);
991 fs_give((void **) &url_copy
);
998 gripe_newbody(ps
, body
, msgno
, flags
)
1008 static char *err
= "Problem creating space for message text.";
1010 char tmp
[MAILTMPLEN
], *p
;
1012 if((store
= so_get(PicoText
, NULL
, EDIT_ACCESS
)) != NULL
){
1013 *body
= mail_newbody();
1015 if((p
= detoken(NULL
, NULL
, 2, 0, 1, NULL
, NULL
)) != NULL
){
1019 fs_give((void **) &p
);
1023 q_status_message(SM_ORDER
| SM_DING
, 3, 4, err
);
1028 /*---- Might have multiple parts ----*/
1029 (*body
)->type
= TYPEMULTIPART
;
1030 /*---- The TEXT part/body ----*/
1031 (*body
)->nested
.part
= mail_newbody_part();
1032 (*body
)->nested
.part
->body
.type
= TYPETEXT
;
1033 (*body
)->nested
.part
->body
.contents
.text
.data
= (void *) store
;
1035 /*---- create object, and write current config into it ----*/
1036 pp
= &((*body
)->nested
.part
->next
);
1038 if(flags
& GRIPE_OPT_CONF
){
1039 *pp
= mail_newbody_part();
1040 pb
= &((*pp
)->body
);
1041 pp
= &((*pp
)->next
);
1042 pb
->type
= TYPETEXT
;
1043 pb
->id
= generate_message_id();
1044 pb
->description
= cpystr("Alpine Configuration Data");
1045 pb
->parameter
= mail_newbody_parameter();
1046 pb
->parameter
->attribute
= cpystr("name");
1047 pb
->parameter
->value
= cpystr("config.txt");
1049 if((store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
){
1050 extern char datestamp
[], hoststamp
[];
1052 pb
->contents
.text
.data
= (void *) store
;
1053 gf_set_so_writec(&pc
, store
);
1054 gf_puts("Alpine built ", pc
);
1055 gf_puts(datestamp
, pc
);
1056 gf_puts(" on host: ", pc
);
1057 gf_puts(hoststamp
, pc
);
1061 dump_pine_struct(ps
, pc
);
1062 dump_config(ps
, pc
, 0);
1065 pb
->size
.bytes
= strlen((char *) so_text(store
));
1066 gf_clear_so_writec(store
);
1069 q_status_message(SM_ORDER
| SM_DING
, 3, 4, err
);
1074 if(flags
& GRIPE_OPT_KEYS
){
1075 *pp
= mail_newbody_part();
1076 pb
= &((*pp
)->body
);
1077 pp
= &((*pp
)->next
);
1078 pb
->type
= TYPETEXT
;
1079 pb
->id
= generate_message_id();
1080 pb
->description
= cpystr("Recent User Input");
1081 pb
->parameter
= mail_newbody_parameter();
1082 pb
->parameter
->attribute
= cpystr("name");
1083 pb
->parameter
->value
= cpystr("uinput.txt");
1085 if((store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
){
1086 pb
->contents
.text
.data
= (void *) store
;
1088 so_puts(store
, "User's most recent input:\n");
1090 /* dump last n keystrokes */
1091 so_puts(store
, "========== Latest keystrokes ==========\n");
1092 while((i
= key_playback(0)) != -1){
1093 snprintf(tmp
, sizeof(tmp
), "\t%s\t(0x%x)\n", pretty_command(i
), i
);
1094 tmp
[sizeof(tmp
)-1] = '\0';
1095 so_puts(store
, tmp
);
1098 pb
->size
.bytes
= strlen((char *) so_text(store
));
1101 q_status_message(SM_ORDER
| SM_DING
, 3, 4, err
);
1106 /* check for local debugging info? */
1107 if((flags
& GRIPE_OPT_LOCAL
)
1108 && ps_global
->VAR_BUGS_EXTRAS
1109 && can_access(ps_global
->VAR_BUGS_EXTRAS
, EXECUTE_ACCESS
) == 0){
1112 *pp
= mail_newbody_part();
1113 pb
= &((*pp
)->body
);
1114 pp
= &((*pp
)->next
);
1115 pb
->type
= TYPETEXT
;
1116 pb
->id
= generate_message_id();
1117 pb
->description
= cpystr("Local Configuration Data");
1118 pb
->parameter
= mail_newbody_parameter();
1119 pb
->parameter
->attribute
= cpystr("name");
1120 pb
->parameter
->value
= cpystr("lconfig.txt");
1122 if((store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
){
1126 pb
->contents
.text
.data
= (void *) store
;
1127 gf_set_so_writec(&pc
, store
);
1128 if((syspipe
= open_system_pipe(ps_global
->VAR_BUGS_EXTRAS
,
1130 PIPE_READ
| PIPE_STDERR
| PIPE_USER
,
1131 0, pipe_callback
, pipe_report_error
)) != NULL
){
1132 gf_set_readc(&gc
, (void *)syspipe
, 0, PipeStar
, 0);
1134 error
= gf_pipe(gc
, pc
);
1135 (void) close_system_pipe(&syspipe
, NULL
, pipe_callback
);
1138 error
= "executing config collector";
1140 gf_clear_so_writec(store
);
1144 q_status_message1(SM_ORDER
| SM_DING
, 3, 4,
1145 "Problem %s", error
);
1148 else /* fixup attachment's size */
1149 pb
->size
.bytes
= strlen((char *) so_text(store
));
1152 if((flags
& GRIPE_OPT_MSG
) && mn_get_total(ps
->msgmap
) > 0L){
1155 ps
->redrawer
= att_cur_drawer
;
1158 if((ch
= one_try_want_to("Attach current message to report",
1160 WT_FLUSH_IN
|WT_SEQ_SENSITIVE
)) == 'y'){
1161 *pp
= mail_newbody_part();
1162 pb
= &((*pp
)->body
);
1163 pp
= &((*pp
)->next
);
1164 pb
->type
= TYPEMESSAGE
;
1165 pb
->id
= generate_message_id();
1166 snprintf(tmp
, sizeof(tmp
), "Problem Message (%ld of %ld)",
1167 mn_get_cur(ps
->msgmap
), mn_get_total(ps
->msgmap
));
1168 tmp
[sizeof(tmp
)-1] = '\0';
1169 pb
->description
= cpystr(tmp
);
1171 /*---- Package each message in a storage object ----*/
1172 if((store
= so_get(PART_SO_TYPE
, NULL
, EDIT_ACCESS
)) != NULL
){
1173 pb
->contents
.text
.data
= (void *) store
;
1176 q_status_message(SM_ORDER
| SM_DING
, 3, 4, err
);
1180 /* write the header */
1181 if((p
= mail_fetch_header(ps
->mail_stream
, msgno
, NIL
, NIL
,
1182 NIL
, FT_PEEK
)) && *p
)
1187 pb
->size
.bytes
= strlen(p
);
1188 so_puts(store
, "\015\012");
1190 if((p
= pine_mail_fetch_text(ps
->mail_stream
,
1191 msgno
, NULL
, NULL
, NIL
))
1197 pb
->size
.bytes
+= strlen(p
);
1200 q_status_message(SM_ORDER
, 0, 3, "Bug report cancelled.");
1206 /*---- Only one part! ----*/
1207 (*body
)->type
= TYPETEXT
;
1208 (*body
)->contents
.text
.data
= (void *) store
;
1216 gripe_token_addr(token
)
1222 if(token
&& *token
++ == '_'){
1223 if(!strcmp(token
, "LOCAL_ADDRESS_")){
1224 p
= (ps_global
->VAR_LOCAL_ADDRESS
1225 && ps_global
->VAR_LOCAL_ADDRESS
[0])
1226 ? ps_global
->VAR_LOCAL_ADDRESS
1228 a
= rfc822_parse_mailbox(&p
, ps_global
->maildomain
);
1229 a
->personal
= cpystr((ps_global
->VAR_LOCAL_FULLNAME
1230 && ps_global
->VAR_LOCAL_FULLNAME
[0])
1231 ? ps_global
->VAR_LOCAL_FULLNAME
1232 : "Place to report Alpine Bugs");
1234 else if(!strcmp(token
, "BUGS_ADDRESS_")){
1235 p
= (ps_global
->VAR_BUGS_ADDRESS
1236 && ps_global
->VAR_BUGS_ADDRESS
[0])
1237 ? ps_global
->VAR_BUGS_ADDRESS
: "postmaster";
1238 a
= rfc822_parse_mailbox(&p
, ps_global
->maildomain
);
1239 a
->personal
= cpystr((ps_global
->VAR_BUGS_FULLNAME
1240 && ps_global
->VAR_BUGS_FULLNAME
[0])
1241 ? ps_global
->VAR_BUGS_FULLNAME
1242 : "Place to report Alpine Bugs");
1257 * Build our contribution to the subject; part constant string
1258 * and random 4 character alpha numeric string.
1260 i
= (int)(random() % 36L);
1261 j
= (int)(random() % 36L);
1262 k
= (int)(random() % 36L);
1263 l
= (int)(random() % 36L);
1264 tmp_20k_buf
[0] = '\0';
1265 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s (ID %c%c%d%c%c)", key
,
1266 (i
< 10) ? '0' + i
: 'A' + (i
- 10),
1267 (j
< 10) ? '0' + j
: 'A' + (j
- 10),
1268 (int)(random() % 10L),
1269 (k
< 10) ? '0' + k
: 'A' + (k
- 10),
1270 (l
< 10) ? '0' + l
: 'A' + (l
- 10));
1271 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
1272 return(cpystr(tmp_20k_buf
));
1277 * Used by gripe_tool.
1280 att_cur_drawer(void)
1285 /* blat helpful message to screen */
1289 dline
< ps_global
->ttyo
->screen_rows
- FOOTER_ROWS(ps_global
);
1291 for(i
= 0; i
< 256 && att_cur_msg
[j
] && att_cur_msg
[j
] != '\n'; i
++)
1292 buf
[i
] = att_cur_msg
[j
++];
1300 PutLine0(dline
, 1, buf
);
1311 pcpine_help(HelpType section
)
1313 char **help_lines
, *help_text
= NULL
;
1317 /* assumption here is that HelpType is char ** */
1318 help_lines
= section
;
1319 if (help_lines
!= NULL
){
1320 init_helper_getc(help_lines
);
1321 if(store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)){
1322 gf_set_so_writec(&pc
, store
);
1325 gf_link_filter(gf_local_nvtnl
, NULL
);
1327 gf_link_filter(gf_html2plain
,
1328 gf_html2plain_opt(NULL
,
1329 ps_global
->ttyo
->screen_cols
,
1330 non_messageview_margin(), NULL
, NULL
, GFHP_STRIPPED
));
1332 if(!gf_pipe(helper_getc
, pc
)){
1333 help_text
= (char *) store
->txt
;
1334 store
->txt
= (void *) NULL
;
1337 gf_clear_so_writec(store
);
1350 help_popup(SCROLL_S
*sparms
, int in_handle
)
1356 hp_menu
[++i
].type
= tQueue
;
1357 hp_menu
[i
].label
.style
= lNormal
;
1358 hp_menu
[i
].label
.string
= "View Help Section";
1359 hp_menu
[i
].data
.val
= 'V';
1362 hp_menu
[++i
].type
= tQueue
;
1363 hp_menu
[i
].label
.style
= lNormal
;
1364 hp_menu
[i
].label
.string
= "Exit Help";
1365 hp_menu
[i
].data
.val
= 'E';
1367 hp_menu
[++i
].type
= tTail
;
1369 mswin_popup(hp_menu
);
1379 help_subsection_popup(SCROLL_S
*sparms
, int in_handle
)
1385 hp_menu
[++i
].type
= tQueue
;
1386 hp_menu
[i
].label
.style
= lNormal
;
1387 hp_menu
[i
].label
.string
= "View Help Section";
1388 hp_menu
[i
].data
.val
= 'V';
1391 hp_menu
[++i
].type
= tQueue
;
1392 hp_menu
[i
].label
.style
= lNormal
;
1393 hp_menu
[i
].label
.string
= "Previous Help Section";
1394 hp_menu
[i
].data
.val
= 'P';
1396 hp_menu
[++i
].type
= tQueue
;
1397 hp_menu
[i
].label
.style
= lNormal
;
1398 hp_menu
[i
].label
.string
= "Exit Help";
1399 hp_menu
[i
].data
.val
= 'E';
1401 hp_menu
[++i
].type
= tTail
;
1403 if(mswin_popup(hp_menu
) == (in_handle
? 1 : 0))
1404 /*(void) helper_internal()*/;
1409 #endif /* _WINDOWS */