* Changes in the source code of Alpine to define internal prototypes
[alpine.git] / alpine / help.c
blobc5d4db22a513244c3ca51683ced02457b6320fc5
1 /*
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 * ========================================================================
15 #include "headers.h"
16 #include "help.h"
17 #include "keymenu.h"
18 #include "status.h"
19 #include "mailview.h"
20 #include "mailindx.h"
21 #include "mailcmd.h"
22 #include "reply.h"
23 #include "signal.h"
24 #include "radio.h"
25 #include "send.h"
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 */
41 } HELP_SCROLL_S;
44 static struct {
45 unsigned crlf:1;
46 char **line,
47 *offset;
48 } g_h_text;
51 typedef struct _help_print_state {
52 int page;
53 char *title;
54 int title_len;
55 } HPRT_S;
57 static HPRT_S *g_hprt;
60 static char att_cur_msg[] = "\
61 Reporting a bug...\n\
62 \n\
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\
70 \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
82 * Internal prototypes
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);
97 #ifdef _WINDOWS
98 int help_subsection_popup(SCROLL_S *, int);
99 #endif
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.
115 -----*/
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;
121 long offset = 0L;
122 char *error = NULL, tmp_title[MAX_SCREEN_COLS + 1];
123 STORE_S *store;
124 HANDLE_S *handles = NULL, *htmp;
125 HELP_SCROLL_S hscroll;
126 gf_o_t pc;
128 dprint((1, "\n\n ---- HELPER ----\n"));
130 /* assumption here is that HelpType is char ** */
131 shown_text = text;
132 is_external = 0;
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."));
146 return MC_FINISH;
148 else{
149 is_external = 1;
150 if(help_text == NULL) external_text = NO_HELP;
151 else{
152 char *s, *t;
153 int nlines;
155 for(nlines = 0, s = help_text; s != NULL; nlines++){
156 s = strchr(s, '\n');
157 if(s != NULL) s++;
159 rv = external_text = fs_get((nlines + 1)*sizeof(char *));
160 for(s = help_text; s != NULL; s = t){
161 t = strchr(s, '\n');
162 if(t != NULL) *t++ = '\0';
163 *rv++ = cpystr(s);
165 *rv = NULL;
168 if(we_cancel)
169 cancel_busy_cue(-1);
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);
180 init_sigwinch();
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);
195 gf_filter_init();
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));
203 else
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);
213 if(!error){
214 SCROLL_S sargs;
215 struct key_menu km;
216 struct key keys[24];
218 for(htmp = handles; htmp; htmp = htmp->next)
219 if(htmp->type == URL
220 && htmp->h.url.path
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
229 * stuff
231 ClearScreen();
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){
245 char *p;
246 int i;
248 /* if we're looking at html, look for a <title>
249 * in the <head>... */
250 for(i = 1;
251 hscroll.help_source[0][i]
252 && struncmp(hscroll.help_source[i], "</head>", 7);
253 i++)
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)
258 *p = '\0';
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,
264 _(tmp_20k_buf),
265 ps_global->ttyo->screen_cols / 3));
266 tmp_title[sizeof(tmp_title)-1] = '\0';
267 break;
271 if(!sargs.bar.title)
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;
282 km = help_keymenu;
283 km.keys = keys;
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);
307 else{
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);
324 else
325 sargs.bogus_input = help_bogus_input;
327 if(handles){
328 sargs.keys.each_cmd = help_keymenu_tweek;
329 hscroll.keys_formatted = 0;
331 else{
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);
341 if(frag){
342 sargs.start.on = Fragment;
343 sargs.start.loc.frag = frag;
344 frag = NULL; /* ignore next time */
346 else if(offset){
347 sargs.start.on = Offset;
348 sargs.start.loc.offset = offset;
350 else
351 sargs.start.on = FirstPage;
353 #ifdef _WINDOWS
354 sargs.mouse.popup = (flags & HLPD_FROMHELP)
355 ? help_subsection_popup : help_popup;
356 #endif
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;
365 ClearScreen();
368 so_give(&store);
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);
381 return(cmd);
386 * helper -- compatibility function around newer helper_internal
389 helper(HelpType text, char *title, int flags)
391 return(helper_internal(text, NULL, title, flags));
395 void
396 init_helper_getc(char **help_txt)
398 g_h_text.crlf = 0;
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)
409 if(g_h_text.crlf){
410 *c = '\012';
411 g_h_text.crlf = 0;
412 return(1);
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);
420 *c = '\015';
421 g_h_text.crlf = 1;
424 return(1);
427 return(0);
432 help_processor(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
434 int rv = 0;
435 char message[64];
436 struct help_texts *t;
438 switch(cmd){
439 /*----------- Print all the help ------------*/
440 case MC_PRINTALL :
441 print_all_help();
442 break;
444 case MC_PRINTMSG :
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);
449 close_printer();
452 break;
454 case MC_EXPORT: /* reuse old definition, so as not to patch pine.h */
455 {char help_name[40];
456 help_name[0] = '\0';
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';
461 break;
463 if(help_name[0])
464 q_status_message1(SM_ORDER, 0, 2,
465 "Internal Name: x-alpine-help:%s", help_name);
466 else
467 q_status_message(SM_ORDER|SM_DING, 1, 2,
468 "Can not find link for text help");
470 break;
472 case MC_FINISH :
473 rv = 1;
474 break;
476 default :
477 alpine_panic("Unhandled case");
480 return(rv);
484 void
485 help_keymenu_tweek(SCROLL_S *sparms, int handle_hidden)
487 if(handle_hidden){
488 sparms->keys.menu->keys[HLP_VIEW_HANDLE].name = "";
489 sparms->keys.menu->keys[HLP_VIEW_HANDLE].label = "";
491 else{
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
508 void
509 print_help(char **text)
511 char *error, buf[256];
512 HPRT_S help_data;
514 init_helper_getc(text);
516 memset(g_hprt = &help_data, 0, sizeof(HPRT_S));
518 help_data.page = 1;
520 gf_filter_init();
522 if(!struncmp(text[0], "<html>", 6)){
523 int i;
524 char *p;
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>"))
532 && p - text[i] > 7){
533 help_data.title = text[i] + 7;
534 help_data.title_len = p - help_data.title;
535 break;
538 else
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);
546 print_text(buf);
547 print_text(NEWLINE); /* terminate it */
548 print_text(NEWLINE); /* and write two blank links */
549 print_text(NEWLINE);
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. */
558 void
559 print_all_help(void)
561 struct help_texts *t;
562 char **h;
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"));
571 break;
574 print_help(h);
577 if(we_turned_on)
578 intr_handling_off();
580 close_printer();
586 * print_help_page_title --
588 void
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
602 * help text
605 print_help_page_break(long int linenum, char *line, LT_INS_S **ins, void *local)
607 char buf[256];
609 if(((linenum + (g_hprt->page * 3)) % 62) == 0){
610 g_hprt->page++; /* start on new page */
611 buf[0] = ctrl('L');
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));
618 return(0);
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);
630 return(0);
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))){
639 char *frag;
640 HelpType newhelp;
642 /* internal fragment reference? */
643 if((frag = strchr(url, '#')) != NULL){
644 size_t len;
646 if((len = frag - url) != 0){
647 newhelp = help_name2section(url, len);
649 else{
650 url_local_fragment(url);
651 return(1);
654 else
655 newhelp = help_name2section(url, strlen(url));
658 if(newhelp != NO_HELP){
659 int rv;
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);
670 return(0);
675 url_local_config(char *url)
677 if(!struncmp(url, "x-alpine-config:", 16)){
678 char **config;
679 int rv = MC_NONE;
681 config = get_supported_options();
682 if(config){
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);
695 return(0);
699 /*----------------------------------------------------------------------
700 Review latest status messages
701 -----*/
702 void
703 review_messages(void)
705 SCROLL_S sargs;
706 STORE_S *in_store = NULL, *out_store = NULL;
707 gf_i_t gc;
708 gf_o_t pc;
709 int jo, lo, hi, donejo, donelo, donehi;
710 RMCat rmcat;
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))
725 return;
729 if(!(in_store = so_get(CharStar, NULL, EDIT_ACCESS)) ||
730 !(out_store = so_get(CharStar, NULL, EDIT_ACCESS))){
731 if(in_store)
732 so_give(&in_store);
734 q_status_message(SM_ORDER | SM_DING, 3, 4,
735 _("Failed allocating memory"));
736 return;
739 add_review_message(_("Turning off new messages while reviewing"), 0);
740 rm_not_right_now = 1;
742 donejo = donehi = donelo = 0;
743 jo = rmjofirst;
744 if(jo < 0)
745 donejo = 1;
747 lo = rmlofirst;
748 if(lo < 0)
749 donelo = 1;
751 hi = rmhifirst;
752 if(hi < 0)
753 donehi = 1;
755 while(!(donejo && donelo && donehi)){
756 REV_MSG_S *pjo, *plo, *phi, *p;
758 if(!donejo)
759 pjo = &rmjoarray[jo];
760 else
761 pjo = NULL;
763 if(!donelo)
764 plo = &rmloarray[lo];
765 else
766 plo = NULL;
768 if(!donehi)
769 phi = &rmhiarray[hi];
770 else
771 phi = NULL;
773 if(pjo && (!plo || pjo->seq <= plo->seq)
774 && (!phi || pjo->seq <= phi->seq))
775 rmcat = Jo;
776 else if(plo && (!phi || plo->seq <= phi->seq))
777 rmcat = Lo;
778 else if(phi)
779 rmcat = Hi;
780 else
781 rmcat = No;
783 switch(rmcat){
784 case Jo:
785 p = pjo;
786 if(jo == rmjofirst && (((rmjolast + 1) % RMJLEN) == rmjofirst))
787 so_puts(in_store,
788 _("**** Journal entries prior to this point have been trimmed. ****\n"));
789 break;
791 case Lo:
792 p = plo;
793 if(show_level >= 0 &&
794 lo == rmlofirst && (((rmlolast + 1) % RMLLEN) == rmlofirst))
795 so_puts(in_store,
796 _("**** Debug 0-4 entries prior to this point have been trimmed. ****\n"));
797 break;
799 case Hi:
800 p = phi;
801 if(show_level >= 5 &&
802 hi == rmhifirst && (((rmhilast + 1) % RMHLEN) == rmhifirst))
803 so_puts(in_store,
804 _("**** Debug 5-9 entries prior to this point have been trimmed. ****\n"));
805 break;
807 default:
808 p = NULL;
809 break;
812 if(p){
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]){
820 if(p->continuation)
821 so_puts(in_store, ">");
823 so_puts(in_store, p->message);
824 so_puts(in_store, "\n");
829 switch(rmcat){
830 case Jo:
831 if(jo == rmjolast)
832 donejo++;
833 else
834 jo = (jo + 1) % RMJLEN;
836 break;
838 case Lo:
839 if(lo == rmlolast)
840 donelo++;
841 else
842 lo = (lo + 1) % RMLLEN;
844 break;
846 case Hi:
847 if(hi == rmhilast)
848 donehi++;
849 else
850 hi = (hi + 1) % RMHLEN;
852 break;
854 default:
855 donejo++;
856 donelo++;
857 donehi++;
858 break;
863 so_seek(in_store, 0L, 0);
864 gf_filter_init();
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);
871 gf_pipe(gc, pc);
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);
886 #ifdef DEBUG
887 #ifdef DEBUGJOURNAL
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 */
899 #else /* !DEBUG */
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");
905 #endif /* !DEBUG */
907 if(timestamps)
908 rev_msg_keys[TIMESTAMP_KEY].label = notimestampkeylabel;
909 else
910 rev_msg_keys[TIMESTAMP_KEY].label = timestampkeylabel;
912 if(show_level >= 0)
913 /* TRANSLATORS: shows what numeric level Debug output is displayed at */
914 snprintf(debugkeylabel, sizeof(debugkeylabel), _("Debug (%d)"), show_level);
915 else
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;
927 so_give(&in_store);
928 so_give(&out_store);
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)
940 switch(cmd){
941 case MC_TOGGLE: /* turn timestamps on or off */
942 break;
944 default:
945 alpine_panic("Unexpected command in journal_processor");
946 break;
949 return(1);
954 * standard type of storage object used for body parts...
956 #ifdef DOS
957 #define PART_SO_TYPE TmpFileStar
958 #else
959 #define PART_SO_TYPE CharStar
960 #endif
964 gripe_gripe_to(char *url)
966 char *composer_title, *url_copy, *optstr, *p;
967 int opts = 0;
968 BODY *body = NULL;
969 ENVELOPE *outgoing = NULL;
970 REPLY_S fake_reply;
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)
977 *optstr++ = '\0';
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");
984 dprint((1,
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);
995 * Sniff thru options
997 while(optstr){
998 if((p = strchr(optstr, '?')) != NULL) /* tie off list item */
999 *p++ = '\0';
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;
1010 optstr = p;
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);
1030 if(body)
1031 pine_free_body(&body);
1033 fs_give((void **) &url_copy);
1035 return(10);
1040 gripe_newbody(struct pine *ps, BODY **body, long msgno, int flags)
1042 BODY *pb;
1043 PART **pp;
1044 STORE_S *store;
1045 gf_o_t pc;
1046 static char *err = "Problem creating space for message text.";
1047 int i;
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){
1054 if(*p)
1055 so_puts(store, p);
1057 fs_give((void **) &p);
1060 else{
1061 q_status_message(SM_ORDER | SM_DING, 3, 4, err);
1062 return(-1);
1065 if(flags){
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);
1096 gf_puts("\n", pc);
1098 #ifdef DEBUG
1099 dump_pine_struct(ps, pc);
1100 dump_config(ps, pc, 0);
1101 #endif /* DEBUG */
1103 pb->size.bytes = strlen((char *) so_text(store));
1104 gf_clear_so_writec(store);
1106 else{
1107 q_status_message(SM_ORDER | SM_DING, 3, 4, err);
1108 return(-1);
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));
1138 else{
1139 q_status_message(SM_ORDER | SM_DING, 3, 4, err);
1140 return(-1);
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){
1148 char *error = NULL;
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){
1161 PIPE_S *syspipe;
1162 gf_i_t gc;
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,
1167 NULL, NULL,
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);
1171 gf_filter_init();
1172 error = gf_pipe(gc, pc);
1173 (void) close_system_pipe(&syspipe, NULL, pipe_callback);
1175 else
1176 error = "executing config collector";
1178 gf_clear_so_writec(store);
1181 if(error){
1182 q_status_message1(SM_ORDER | SM_DING, 3, 4,
1183 "Problem %s", error);
1184 return(-1);
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){
1191 int ch = 0;
1193 ps->redrawer = att_cur_drawer;
1194 att_cur_drawer();
1196 if((ch = one_try_want_to("Attach current message to report",
1197 'y','x',NO_HELP,
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;
1213 else{
1214 q_status_message(SM_ORDER | SM_DING, 3, 4, err);
1215 return(-1);
1218 /* write the header */
1219 if((p = mail_fetch_header(ps->mail_stream, msgno, NIL, NIL,
1220 NIL, FT_PEEK)) && *p)
1221 so_puts(store, p);
1222 else
1223 return(-1);
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))
1230 && *p)
1231 so_puts(store, p);
1232 else
1233 return(-1);
1235 pb->size.bytes += strlen(p);
1237 else if(ch == 'x'){
1238 q_status_message(SM_ORDER, 0, 3, "Bug report cancelled.");
1239 return(-1);
1243 else{
1244 /*---- Only one part! ----*/
1245 (*body)->type = TYPETEXT;
1246 (*body)->contents.text.data = (void *) store;
1249 return(0);
1253 ADDRESS *
1254 gripe_token_addr(char *token)
1256 char *p;
1257 ADDRESS *a = NULL;
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
1264 : "postmaster";
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");
1283 return(a);
1287 char *
1288 gripe_id(char *key)
1290 int i,j,k,l;
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.
1315 void
1316 att_cur_drawer(void)
1318 int i, dline, j;
1319 char buf[256+1];
1321 /* blat helpful message to screen */
1322 ClearBody();
1323 j = 0;
1324 for(dline = 2;
1325 dline < ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global);
1326 dline++){
1327 for(i = 0; i < 256 && att_cur_msg[j] && att_cur_msg[j] != '\n'; i++)
1328 buf[i] = att_cur_msg[j++];
1330 buf[i] = '\0';
1331 if(att_cur_msg[j])
1332 j++;
1333 else if(!i)
1334 break;
1336 PutLine0(dline, 1, buf);
1341 #ifdef _WINDOWS
1346 char *
1347 pcpine_help(HelpType section)
1349 char **help_lines, *help_text = NULL;
1350 STORE_S *store;
1351 gf_o_t pc;
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);
1359 gf_filter_init();
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);
1374 so_give(&store);
1378 return(help_text);
1386 help_popup(SCROLL_S *sparms, int in_handle)
1388 MPopup hp_menu[10];
1389 int i = -1;
1391 if(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);
1407 return(0);
1415 help_subsection_popup(SCROLL_S *sparms, int in_handle)
1417 MPopup hp_menu[10];
1418 int i = -1;
1420 if(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()*/;
1442 return(0);
1445 #endif /* _WINDOWS */