* Update to packages/alpine.spec to account for the new location of man
[alpine.git] / alpine / help.c
blobb20c6eac01655db7ce3e0892983a9bcde06fd3bf
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: help.c 1032 2008-04-11 00:30:04Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2013-2017 Eduardo Chappa
8 * Copyright 2006-2008 University of Washington
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 #include "headers.h"
20 #include "help.h"
21 #include "keymenu.h"
22 #include "status.h"
23 #include "mailview.h"
24 #include "mailindx.h"
25 #include "mailcmd.h"
26 #include "reply.h"
27 #include "signal.h"
28 #include "radio.h"
29 #include "send.h"
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 */
44 } HELP_SCROLL_S;
47 static struct {
48 unsigned crlf:1;
49 char **line,
50 *offset;
51 } g_h_text;
54 typedef struct _help_print_state {
55 int page;
56 char *title;
57 int title_len;
58 } HPRT_S;
60 static HPRT_S *g_hprt;
63 static char att_cur_msg[] = "\
64 Reporting a bug...\n\
65 \n\
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\
73 \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
85 * Internal prototypes
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);
100 #ifdef _WINDOWS
101 int help_subsection_popup(SCROLL_S *, int);
102 #endif
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.
118 -----*/
120 helper_internal(HelpType text, char *frag, char *title, int flags)
122 char **shown_text;
123 int cmd = MC_NONE;
124 long offset = 0L;
125 char *error = NULL, tmp_title[MAX_SCREEN_COLS + 1];
126 STORE_S *store;
127 HANDLE_S *handles = NULL, *htmp;
128 HELP_SCROLL_S hscroll;
129 gf_io_t pc;
131 dprint((1, "\n\n ---- HELPER ----\n"));
133 /* assumption here is that HelpType is char ** */
134 shown_text = text;
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);
144 init_sigwinch();
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);
159 gf_filter_init();
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));
167 else
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);
177 if(!error){
178 SCROLL_S sargs;
179 struct key_menu km;
180 struct key keys[24];
182 for(htmp = handles; htmp; htmp = htmp->next)
183 if(htmp->type == URL
184 && htmp->h.url.path
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
193 * stuff
195 ClearScreen();
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)){
209 char *p;
210 int i;
212 /* if we're looking at html, look for a <title>
213 * in the <head>... */
214 for(i = 1;
215 shown_text[i]
216 && struncmp(shown_text[i], "</head>", 7);
217 i++)
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)
222 *p = '\0';
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,
228 _(tmp_20k_buf),
229 ps_global->ttyo->screen_cols / 3));
230 tmp_title[sizeof(tmp_title)-1] = '\0';
231 break;
235 if(!sargs.bar.title)
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;
246 km = help_keymenu;
247 km.keys = keys;
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);
271 else{
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);
288 else
289 sargs.bogus_input = help_bogus_input;
291 if(handles){
292 sargs.keys.each_cmd = help_keymenu_tweek;
293 hscroll.keys_formatted = 0;
295 else{
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);
305 if(frag){
306 sargs.start.on = Fragment;
307 sargs.start.loc.frag = frag;
308 frag = NULL; /* ignore next time */
310 else if(offset){
311 sargs.start.on = Offset;
312 sargs.start.loc.offset = offset;
314 else
315 sargs.start.on = FirstPage;
317 #ifdef _WINDOWS
318 sargs.mouse.popup = (flags & HLPD_FROMHELP)
319 ? help_subsection_popup : help_popup;
320 #endif
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;
329 ClearScreen();
332 so_give(&store);
335 free_handles(&handles);
337 while(cmd == MC_RESIZE);
339 return(cmd);
344 * helper -- compatibility function around newer helper_internal
347 helper(HelpType text, char *title, int flags)
349 return(helper_internal(text, NULL, title, flags));
353 void
354 init_helper_getc(char **help_txt)
356 g_h_text.crlf = 0;
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);
365 helper_getc(char *c)
367 if(g_h_text.crlf){
368 *c = '\012';
369 g_h_text.crlf = 0;
370 return(1);
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);
378 *c = '\015';
379 g_h_text.crlf = 1;
382 return(1);
385 return(0);
390 help_processor(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
392 int rv = 0;
393 char message[64];
394 struct help_texts *t;
396 switch(cmd){
397 /*----------- Print all the help ------------*/
398 case MC_PRINTALL :
399 print_all_help();
400 break;
402 case MC_PRINTMSG :
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);
407 close_printer();
410 break;
412 case MC_EXPORT: /* reuse old definition, so as not to patch pine.h */
413 {char help_name[40];
414 help_name[0] = '\0';
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';
419 break;
421 if(help_name[0])
422 q_status_message1(SM_ORDER, 0, 2,
423 "Internal Name: x-alpine-help:%s", help_name);
424 else
425 q_status_message(SM_ORDER|SM_DING, 1, 2,
426 "Can not find link for text help");
428 break;
430 case MC_FINISH :
431 rv = 1;
432 break;
434 default :
435 alpine_panic("Unhandled case");
438 return(rv);
442 void
443 help_keymenu_tweek(SCROLL_S *sparms, int handle_hidden)
445 if(handle_hidden){
446 sparms->keys.menu->keys[HLP_VIEW_HANDLE].name = "";
447 sparms->keys.menu->keys[HLP_VIEW_HANDLE].label = "";
449 else{
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
466 void
467 print_help(char **text)
469 char *error, buf[256];
470 HPRT_S help_data;
472 init_helper_getc(text);
474 memset(g_hprt = &help_data, 0, sizeof(HPRT_S));
476 help_data.page = 1;
478 gf_filter_init();
480 if(!struncmp(text[0], "<html>", 6)){
481 int i;
482 char *p;
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>"))
490 && p - text[i] > 7){
491 help_data.title = text[i] + 7;
492 help_data.title_len = p - help_data.title;
493 break;
496 else
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);
504 print_text(buf);
505 print_text(NEWLINE); /* terminate it */
506 print_text(NEWLINE); /* and write two blank links */
507 print_text(NEWLINE);
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. */
516 void
517 print_all_help(void)
519 struct help_texts *t;
520 char **h;
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"));
529 break;
532 print_help(h);
535 if(we_turned_on)
536 intr_handling_off();
538 close_printer();
544 * print_help_page_title --
546 void
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
560 * help text
563 print_help_page_break(long int linenum, char *line, LT_INS_S **ins, void *local)
565 char buf[256];
567 if(((linenum + (g_hprt->page * 3)) % 62) == 0){
568 g_hprt->page++; /* start on new page */
569 buf[0] = ctrl('L');
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));
576 return(0);
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);
588 return(0);
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))){
597 char *frag;
598 HelpType newhelp;
600 /* internal fragment reference? */
601 if((frag = strchr(url, '#')) != NULL){
602 size_t len;
604 if((len = frag - url) != 0){
605 newhelp = help_name2section(url, len);
607 else{
608 url_local_fragment(url);
609 return(1);
612 else
613 newhelp = help_name2section(url, strlen(url));
616 if(newhelp != NO_HELP){
617 int rv;
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);
628 return(0);
633 url_local_config(char *url)
635 if(!struncmp(url, "x-alpine-config:", 16)){
636 char **config;
637 int rv = MC_NONE;
639 config = get_supported_options();
640 if(config){
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);
653 return(0);
657 /*----------------------------------------------------------------------
658 Review latest status messages
659 -----*/
660 void
661 review_messages(void)
663 SCROLL_S sargs;
664 STORE_S *in_store = NULL, *out_store = NULL;
665 gf_io_t gc, pc;
666 int jo, lo, hi, donejo, donelo, donehi;
667 RMCat rmcat;
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))
682 return;
686 if(!(in_store = so_get(CharStar, NULL, EDIT_ACCESS)) ||
687 !(out_store = so_get(CharStar, NULL, EDIT_ACCESS))){
688 if(in_store)
689 so_give(&in_store);
691 q_status_message(SM_ORDER | SM_DING, 3, 4,
692 _("Failed allocating memory"));
693 return;
696 add_review_message(_("Turning off new messages while reviewing"), 0);
697 rm_not_right_now = 1;
699 donejo = donehi = donelo = 0;
700 jo = rmjofirst;
701 if(jo < 0)
702 donejo = 1;
704 lo = rmlofirst;
705 if(lo < 0)
706 donelo = 1;
708 hi = rmhifirst;
709 if(hi < 0)
710 donehi = 1;
712 while(!(donejo && donelo && donehi)){
713 REV_MSG_S *pjo, *plo, *phi, *p;
715 if(!donejo)
716 pjo = &rmjoarray[jo];
717 else
718 pjo = NULL;
720 if(!donelo)
721 plo = &rmloarray[lo];
722 else
723 plo = NULL;
725 if(!donehi)
726 phi = &rmhiarray[hi];
727 else
728 phi = NULL;
730 if(pjo && (!plo || pjo->seq <= plo->seq)
731 && (!phi || pjo->seq <= phi->seq))
732 rmcat = Jo;
733 else if(plo && (!phi || plo->seq <= phi->seq))
734 rmcat = Lo;
735 else if(phi)
736 rmcat = Hi;
737 else
738 rmcat = No;
740 switch(rmcat){
741 case Jo:
742 p = pjo;
743 if(jo == rmjofirst && (((rmjolast + 1) % RMJLEN) == rmjofirst))
744 so_puts(in_store,
745 _("**** Journal entries prior to this point have been trimmed. ****\n"));
746 break;
748 case Lo:
749 p = plo;
750 if(show_level >= 0 &&
751 lo == rmlofirst && (((rmlolast + 1) % RMLLEN) == rmlofirst))
752 so_puts(in_store,
753 _("**** Debug 0-4 entries prior to this point have been trimmed. ****\n"));
754 break;
756 case Hi:
757 p = phi;
758 if(show_level >= 5 &&
759 hi == rmhifirst && (((rmhilast + 1) % RMHLEN) == rmhifirst))
760 so_puts(in_store,
761 _("**** Debug 5-9 entries prior to this point have been trimmed. ****\n"));
762 break;
764 default:
765 p = NULL;
766 break;
769 if(p){
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]){
777 if(p->continuation)
778 so_puts(in_store, ">");
780 so_puts(in_store, p->message);
781 so_puts(in_store, "\n");
786 switch(rmcat){
787 case Jo:
788 if(jo == rmjolast)
789 donejo++;
790 else
791 jo = (jo + 1) % RMJLEN;
793 break;
795 case Lo:
796 if(lo == rmlolast)
797 donelo++;
798 else
799 lo = (lo + 1) % RMLLEN;
801 break;
803 case Hi:
804 if(hi == rmhilast)
805 donehi++;
806 else
807 hi = (hi + 1) % RMHLEN;
809 break;
811 default:
812 donejo++;
813 donelo++;
814 donehi++;
815 break;
820 so_seek(in_store, 0L, 0);
821 gf_filter_init();
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);
828 gf_pipe(gc, pc);
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);
843 #ifdef DEBUG
844 #ifdef DEBUGJOURNAL
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 */
856 #else /* !DEBUG */
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");
862 #endif /* !DEBUG */
864 if(timestamps)
865 rev_msg_keys[TIMESTAMP_KEY].label = notimestampkeylabel;
866 else
867 rev_msg_keys[TIMESTAMP_KEY].label = timestampkeylabel;
869 if(show_level >= 0)
870 /* TRANSLATORS: shows what numeric level Debug output is displayed at */
871 snprintf(debugkeylabel, sizeof(debugkeylabel), _("Debug (%d)"), show_level);
872 else
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;
884 so_give(&in_store);
885 so_give(&out_store);
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)
897 switch(cmd){
898 case MC_TOGGLE: /* turn timestamps on or off */
899 break;
901 default:
902 alpine_panic("Unexpected command in journal_processor");
903 break;
906 return(1);
911 * standard type of storage object used for body parts...
913 #ifdef DOS
914 #define PART_SO_TYPE TmpFileStar
915 #else
916 #define PART_SO_TYPE CharStar
917 #endif
921 gripe_gripe_to(url)
922 char *url;
924 char *composer_title, *url_copy, *optstr, *p;
925 int opts = 0;
926 BODY *body = NULL;
927 ENVELOPE *outgoing = NULL;
928 REPLY_S fake_reply;
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)
935 *optstr++ = '\0';
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");
942 dprint((1,
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);
953 * Sniff thru options
955 while(optstr){
956 if((p = strchr(optstr, '?')) != NULL) /* tie off list item */
957 *p++ = '\0';
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;
968 optstr = p;
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");
976 pf->type = FreeText;
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);
988 if(body)
989 pine_free_body(&body);
991 fs_give((void **) &url_copy);
993 return(10);
998 gripe_newbody(ps, body, msgno, flags)
999 struct pine *ps;
1000 BODY **body;
1001 long msgno;
1002 int flags;
1004 BODY *pb;
1005 PART **pp;
1006 STORE_S *store;
1007 gf_io_t pc;
1008 static char *err = "Problem creating space for message text.";
1009 int i;
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){
1016 if(*p)
1017 so_puts(store, p);
1019 fs_give((void **) &p);
1022 else{
1023 q_status_message(SM_ORDER | SM_DING, 3, 4, err);
1024 return(-1);
1027 if(flags){
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);
1058 gf_puts("\n", pc);
1060 #ifdef DEBUG
1061 dump_pine_struct(ps, pc);
1062 dump_config(ps, pc, 0);
1063 #endif /* DEBUG */
1065 pb->size.bytes = strlen((char *) so_text(store));
1066 gf_clear_so_writec(store);
1068 else{
1069 q_status_message(SM_ORDER | SM_DING, 3, 4, err);
1070 return(-1);
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));
1100 else{
1101 q_status_message(SM_ORDER | SM_DING, 3, 4, err);
1102 return(-1);
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){
1110 char *error = NULL;
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){
1123 PIPE_S *syspipe;
1124 gf_io_t gc;
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,
1129 NULL, NULL,
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);
1133 gf_filter_init();
1134 error = gf_pipe(gc, pc);
1135 (void) close_system_pipe(&syspipe, NULL, pipe_callback);
1137 else
1138 error = "executing config collector";
1140 gf_clear_so_writec(store);
1143 if(error){
1144 q_status_message1(SM_ORDER | SM_DING, 3, 4,
1145 "Problem %s", error);
1146 return(-1);
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){
1153 int ch = 0;
1155 ps->redrawer = att_cur_drawer;
1156 att_cur_drawer();
1158 if((ch = one_try_want_to("Attach current message to report",
1159 'y','x',NO_HELP,
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;
1175 else{
1176 q_status_message(SM_ORDER | SM_DING, 3, 4, err);
1177 return(-1);
1180 /* write the header */
1181 if((p = mail_fetch_header(ps->mail_stream, msgno, NIL, NIL,
1182 NIL, FT_PEEK)) && *p)
1183 so_puts(store, p);
1184 else
1185 return(-1);
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))
1192 && *p)
1193 so_puts(store, p);
1194 else
1195 return(-1);
1197 pb->size.bytes += strlen(p);
1199 else if(ch == 'x'){
1200 q_status_message(SM_ORDER, 0, 3, "Bug report cancelled.");
1201 return(-1);
1205 else{
1206 /*---- Only one part! ----*/
1207 (*body)->type = TYPETEXT;
1208 (*body)->contents.text.data = (void *) store;
1211 return(0);
1215 ADDRESS *
1216 gripe_token_addr(token)
1217 char *token;
1219 char *p;
1220 ADDRESS *a = NULL;
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
1227 : "postmaster";
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");
1246 return(a);
1250 char *
1251 gripe_id(key)
1252 char *key;
1254 int i,j,k,l;
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.
1279 void
1280 att_cur_drawer(void)
1282 int i, dline, j;
1283 char buf[256+1];
1285 /* blat helpful message to screen */
1286 ClearBody();
1287 j = 0;
1288 for(dline = 2;
1289 dline < ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global);
1290 dline++){
1291 for(i = 0; i < 256 && att_cur_msg[j] && att_cur_msg[j] != '\n'; i++)
1292 buf[i] = att_cur_msg[j++];
1294 buf[i] = '\0';
1295 if(att_cur_msg[j])
1296 j++;
1297 else if(!i)
1298 break;
1300 PutLine0(dline, 1, buf);
1305 #ifdef _WINDOWS
1310 char *
1311 pcpine_help(HelpType section)
1313 char **help_lines, *help_text = NULL;
1314 STORE_S *store;
1315 gf_io_t pc;
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);
1323 gf_filter_init();
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);
1338 so_give(&store);
1342 return(help_text);
1350 help_popup(SCROLL_S *sparms, int in_handle)
1352 MPopup hp_menu[10];
1353 int i = -1;
1355 if(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);
1371 return(0);
1379 help_subsection_popup(SCROLL_S *sparms, int in_handle)
1381 MPopup hp_menu[10];
1382 int i = -1;
1384 if(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()*/;
1406 return(0);
1409 #endif /* _WINDOWS */