Add support for tab-completion when selecting by rule
[alpine.git] / alpine / help.c
blob4784dc9bc6a679100dd7d75bd97bf0354a9efcbe
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_io_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(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_io_t gc, pc;
708 int jo, lo, hi, donejo, donelo, donehi;
709 RMCat rmcat;
710 int cmd, timestamps=0, show_level=-1;
711 char debugkeylabel[20];
712 /* TRANSLATORS: command label asking pine to include time stamps in output */
713 char timestampkeylabel[] = N_("Timestamps");
714 /* TRANSLATORS: do not include time stamps in output */
715 char *notimestampkeylabel = N_("NoTimestamps");
717 if((rmjofirst < 0 && rmlofirst < 0 && rmhifirst < 0)
718 || rmjofirst >= RMJLEN || rmjolast >= RMJLEN
719 || rmlofirst >= RMLLEN || rmlolast >= RMLLEN
720 || rmhifirst >= RMHLEN || rmhilast >= RMHLEN
721 || (rmjofirst >= 0 && rmjolast < 0)
722 || (rmlofirst >= 0 && rmlolast < 0)
723 || (rmhifirst >= 0 && rmhilast < 0))
724 return;
728 if(!(in_store = so_get(CharStar, NULL, EDIT_ACCESS)) ||
729 !(out_store = so_get(CharStar, NULL, EDIT_ACCESS))){
730 if(in_store)
731 so_give(&in_store);
733 q_status_message(SM_ORDER | SM_DING, 3, 4,
734 _("Failed allocating memory"));
735 return;
738 add_review_message(_("Turning off new messages while reviewing"), 0);
739 rm_not_right_now = 1;
741 donejo = donehi = donelo = 0;
742 jo = rmjofirst;
743 if(jo < 0)
744 donejo = 1;
746 lo = rmlofirst;
747 if(lo < 0)
748 donelo = 1;
750 hi = rmhifirst;
751 if(hi < 0)
752 donehi = 1;
754 while(!(donejo && donelo && donehi)){
755 REV_MSG_S *pjo, *plo, *phi, *p;
757 if(!donejo)
758 pjo = &rmjoarray[jo];
759 else
760 pjo = NULL;
762 if(!donelo)
763 plo = &rmloarray[lo];
764 else
765 plo = NULL;
767 if(!donehi)
768 phi = &rmhiarray[hi];
769 else
770 phi = NULL;
772 if(pjo && (!plo || pjo->seq <= plo->seq)
773 && (!phi || pjo->seq <= phi->seq))
774 rmcat = Jo;
775 else if(plo && (!phi || plo->seq <= phi->seq))
776 rmcat = Lo;
777 else if(phi)
778 rmcat = Hi;
779 else
780 rmcat = No;
782 switch(rmcat){
783 case Jo:
784 p = pjo;
785 if(jo == rmjofirst && (((rmjolast + 1) % RMJLEN) == rmjofirst))
786 so_puts(in_store,
787 _("**** Journal entries prior to this point have been trimmed. ****\n"));
788 break;
790 case Lo:
791 p = plo;
792 if(show_level >= 0 &&
793 lo == rmlofirst && (((rmlolast + 1) % RMLLEN) == rmlofirst))
794 so_puts(in_store,
795 _("**** Debug 0-4 entries prior to this point have been trimmed. ****\n"));
796 break;
798 case Hi:
799 p = phi;
800 if(show_level >= 5 &&
801 hi == rmhifirst && (((rmhilast + 1) % RMHLEN) == rmhifirst))
802 so_puts(in_store,
803 _("**** Debug 5-9 entries prior to this point have been trimmed. ****\n"));
804 break;
806 default:
807 p = NULL;
808 break;
811 if(p){
812 if(p->level <= show_level){
813 if(timestamps && p->timestamp && p->timestamp[0]){
814 so_puts(in_store, p->timestamp);
815 so_puts(in_store, ": ");
818 if(p->message && p->message[0]){
819 if(p->continuation)
820 so_puts(in_store, ">");
822 so_puts(in_store, p->message);
823 so_puts(in_store, "\n");
828 switch(rmcat){
829 case Jo:
830 if(jo == rmjolast)
831 donejo++;
832 else
833 jo = (jo + 1) % RMJLEN;
835 break;
837 case Lo:
838 if(lo == rmlolast)
839 donelo++;
840 else
841 lo = (lo + 1) % RMLLEN;
843 break;
845 case Hi:
846 if(hi == rmhilast)
847 donehi++;
848 else
849 hi = (hi + 1) % RMHLEN;
851 break;
853 default:
854 donejo++;
855 donelo++;
856 donehi++;
857 break;
862 so_seek(in_store, 0L, 0);
863 gf_filter_init();
864 gf_link_filter(gf_wrap,
865 gf_wrap_filter_opt(ps_global->ttyo->screen_cols - 4,
866 ps_global->ttyo->screen_cols,
867 NULL, show_level < 0 ? 2 : 0, GFW_NONE));
868 gf_set_so_readc(&gc, in_store);
869 gf_set_so_writec(&pc, out_store);
870 gf_pipe(gc, pc);
871 gf_clear_so_writec(out_store);
872 gf_clear_so_readc(in_store);
874 memset(&sargs, 0, sizeof(SCROLL_S));
875 sargs.text.text = so_text(out_store);
876 sargs.text.src = CharStar;
877 sargs.text.desc = _("journal");
878 sargs.keys.menu = &rev_msg_keymenu;
879 sargs.proc.tool = journal_processor;
880 sargs.start.on = LastPage;
881 sargs.resize_exit = 1;
882 sargs.proc.data.p = (void *)&show_level;
883 setbitmap(sargs.keys.bitmap);
885 #ifdef DEBUG
886 #ifdef DEBUGJOURNAL
887 sargs.jump_is_debug = 1;
888 /* TRANSLATORS: these are some screen titles */
889 sargs.help.title = _("HELP FOR DEBUG JOURNAL");
890 sargs.help.text = h_debugjournal;
891 sargs.bar.title = _("REVIEW DEBUGGING");
892 #else /* !DEBUGJOURNAL */
893 clrbitn(DEBUG_KEY, sargs.keys.bitmap);
894 sargs.help.title = _("HELP FOR JOURNAL");
895 sargs.help.text = h_journal;
896 sargs.bar.title = _("REVIEW RECENT MESSAGES");
897 #endif /* !DEBUGJOURNAL */
898 #else /* !DEBUG */
899 clrbitn(DEBUG_KEY, sargs.keys.bitmap);
900 clrbitn(TIMESTAMP_KEY, sargs.keys.bitmap);
901 sargs.help.title = _("HELP FOR JOURNAL");
902 sargs.help.text = h_journal;
903 sargs.bar.title = _("REVIEW RECENT MESSAGES");
904 #endif /* !DEBUG */
906 if(timestamps)
907 rev_msg_keys[TIMESTAMP_KEY].label = notimestampkeylabel;
908 else
909 rev_msg_keys[TIMESTAMP_KEY].label = timestampkeylabel;
911 if(show_level >= 0)
912 /* TRANSLATORS: shows what numeric level Debug output is displayed at */
913 snprintf(debugkeylabel, sizeof(debugkeylabel), _("Debug (%d)"), show_level);
914 else
915 /* TRANSLATORS: include debug output in journal */
916 strncpy(debugkeylabel, _("DebugView"), sizeof(debugkeylabel));
918 debugkeylabel[sizeof(debugkeylabel)-1] = '\0';
920 rev_msg_keys[DEBUG_KEY].label = debugkeylabel;
921 KS_OSDATASET(&rev_msg_keys[DEBUG_KEY], KS_NONE);
923 if((cmd = scrolltool(&sargs)) == MC_TOGGLE)
924 timestamps = !timestamps;
926 so_give(&in_store);
927 so_give(&out_store);
929 }while(cmd != MC_EXIT);
931 rm_not_right_now = 0;
932 add_review_message("Done reviewing", 0);
937 journal_processor(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
939 switch(cmd){
940 case MC_TOGGLE: /* turn timestamps on or off */
941 break;
943 default:
944 alpine_panic("Unexpected command in journal_processor");
945 break;
948 return(1);
953 * standard type of storage object used for body parts...
955 #ifdef DOS
956 #define PART_SO_TYPE TmpFileStar
957 #else
958 #define PART_SO_TYPE CharStar
959 #endif
963 gripe_gripe_to(url)
964 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(ps, body, msgno, flags)
1041 struct pine *ps;
1042 BODY **body;
1043 long msgno;
1044 int flags;
1046 BODY *pb;
1047 PART **pp;
1048 STORE_S *store;
1049 gf_io_t pc;
1050 static char *err = "Problem creating space for message text.";
1051 int i;
1052 char tmp[MAILTMPLEN], *p;
1054 if((store = so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
1055 *body = mail_newbody();
1057 if((p = detoken(NULL, NULL, 2, 0, 1, NULL, NULL)) != NULL){
1058 if(*p)
1059 so_puts(store, p);
1061 fs_give((void **) &p);
1064 else{
1065 q_status_message(SM_ORDER | SM_DING, 3, 4, err);
1066 return(-1);
1069 if(flags){
1070 /*---- Might have multiple parts ----*/
1071 (*body)->type = TYPEMULTIPART;
1072 /*---- The TEXT part/body ----*/
1073 (*body)->nested.part = mail_newbody_part();
1074 (*body)->nested.part->body.type = TYPETEXT;
1075 (*body)->nested.part->body.contents.text.data = (void *) store;
1077 /*---- create object, and write current config into it ----*/
1078 pp = &((*body)->nested.part->next);
1080 if(flags & GRIPE_OPT_CONF){
1081 *pp = mail_newbody_part();
1082 pb = &((*pp)->body);
1083 pp = &((*pp)->next);
1084 pb->type = TYPETEXT;
1085 pb->id = generate_message_id(NULL);
1086 pb->description = cpystr("Alpine Configuration Data");
1087 pb->parameter = mail_newbody_parameter();
1088 pb->parameter->attribute = cpystr("name");
1089 pb->parameter->value = cpystr("config.txt");
1091 if((store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
1092 extern char datestamp[], hoststamp[];
1094 pb->contents.text.data = (void *) store;
1095 gf_set_so_writec(&pc, store);
1096 gf_puts("Alpine built ", pc);
1097 gf_puts(datestamp, pc);
1098 gf_puts(" on host: ", pc);
1099 gf_puts(hoststamp, pc);
1100 gf_puts("\n", pc);
1102 #ifdef DEBUG
1103 dump_pine_struct(ps, pc);
1104 dump_config(ps, pc, 0);
1105 #endif /* DEBUG */
1107 pb->size.bytes = strlen((char *) so_text(store));
1108 gf_clear_so_writec(store);
1110 else{
1111 q_status_message(SM_ORDER | SM_DING, 3, 4, err);
1112 return(-1);
1116 if(flags & GRIPE_OPT_KEYS){
1117 *pp = mail_newbody_part();
1118 pb = &((*pp)->body);
1119 pp = &((*pp)->next);
1120 pb->type = TYPETEXT;
1121 pb->id = generate_message_id(NULL);
1122 pb->description = cpystr("Recent User Input");
1123 pb->parameter = mail_newbody_parameter();
1124 pb->parameter->attribute = cpystr("name");
1125 pb->parameter->value = cpystr("uinput.txt");
1127 if((store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
1128 pb->contents.text.data = (void *) store;
1130 so_puts(store, "User's most recent input:\n");
1132 /* dump last n keystrokes */
1133 so_puts(store, "========== Latest keystrokes ==========\n");
1134 while((i = key_playback(0)) != -1){
1135 snprintf(tmp, sizeof(tmp), "\t%s\t(0x%x)\n", pretty_command(i), i);
1136 tmp[sizeof(tmp)-1] = '\0';
1137 so_puts(store, tmp);
1140 pb->size.bytes = strlen((char *) so_text(store));
1142 else{
1143 q_status_message(SM_ORDER | SM_DING, 3, 4, err);
1144 return(-1);
1148 /* check for local debugging info? */
1149 if((flags & GRIPE_OPT_LOCAL)
1150 && ps_global->VAR_BUGS_EXTRAS
1151 && can_access(ps_global->VAR_BUGS_EXTRAS, EXECUTE_ACCESS) == 0){
1152 char *error = NULL;
1154 *pp = mail_newbody_part();
1155 pb = &((*pp)->body);
1156 pp = &((*pp)->next);
1157 pb->type = TYPETEXT;
1158 pb->id = generate_message_id(NULL);
1159 pb->description = cpystr("Local Configuration Data");
1160 pb->parameter = mail_newbody_parameter();
1161 pb->parameter->attribute = cpystr("name");
1162 pb->parameter->value = cpystr("lconfig.txt");
1164 if((store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
1165 PIPE_S *syspipe;
1166 gf_io_t gc;
1168 pb->contents.text.data = (void *) store;
1169 gf_set_so_writec(&pc, store);
1170 if((syspipe = open_system_pipe(ps_global->VAR_BUGS_EXTRAS,
1171 NULL, NULL,
1172 PIPE_READ | PIPE_STDERR | PIPE_USER,
1173 0, pipe_callback, pipe_report_error)) != NULL){
1174 gf_set_readc(&gc, (void *)syspipe, 0, PipeStar, 0);
1175 gf_filter_init();
1176 error = gf_pipe(gc, pc);
1177 (void) close_system_pipe(&syspipe, NULL, pipe_callback);
1179 else
1180 error = "executing config collector";
1182 gf_clear_so_writec(store);
1185 if(error){
1186 q_status_message1(SM_ORDER | SM_DING, 3, 4,
1187 "Problem %s", error);
1188 return(-1);
1190 else /* fixup attachment's size */
1191 pb->size.bytes = strlen((char *) so_text(store));
1194 if((flags & GRIPE_OPT_MSG) && mn_get_total(ps->msgmap) > 0L){
1195 int ch = 0;
1197 ps->redrawer = att_cur_drawer;
1198 att_cur_drawer();
1200 if((ch = one_try_want_to("Attach current message to report",
1201 'y','x',NO_HELP,
1202 WT_FLUSH_IN|WT_SEQ_SENSITIVE)) == 'y'){
1203 *pp = mail_newbody_part();
1204 pb = &((*pp)->body);
1205 pp = &((*pp)->next);
1206 pb->type = TYPEMESSAGE;
1207 pb->id = generate_message_id(NULL);
1208 snprintf(tmp, sizeof(tmp), "Problem Message (%ld of %ld)",
1209 mn_get_cur(ps->msgmap), mn_get_total(ps->msgmap));
1210 tmp[sizeof(tmp)-1] = '\0';
1211 pb->description = cpystr(tmp);
1213 /*---- Package each message in a storage object ----*/
1214 if((store = so_get(PART_SO_TYPE, NULL, EDIT_ACCESS)) != NULL){
1215 pb->contents.text.data = (void *) store;
1217 else{
1218 q_status_message(SM_ORDER | SM_DING, 3, 4, err);
1219 return(-1);
1222 /* write the header */
1223 if((p = mail_fetch_header(ps->mail_stream, msgno, NIL, NIL,
1224 NIL, FT_PEEK)) && *p)
1225 so_puts(store, p);
1226 else
1227 return(-1);
1229 pb->size.bytes = strlen(p);
1230 so_puts(store, "\015\012");
1232 if((p = pine_mail_fetch_text(ps->mail_stream,
1233 msgno, NULL, NULL, NIL))
1234 && *p)
1235 so_puts(store, p);
1236 else
1237 return(-1);
1239 pb->size.bytes += strlen(p);
1241 else if(ch == 'x'){
1242 q_status_message(SM_ORDER, 0, 3, "Bug report cancelled.");
1243 return(-1);
1247 else{
1248 /*---- Only one part! ----*/
1249 (*body)->type = TYPETEXT;
1250 (*body)->contents.text.data = (void *) store;
1253 return(0);
1257 ADDRESS *
1258 gripe_token_addr(token)
1259 char *token;
1261 char *p;
1262 ADDRESS *a = NULL;
1264 if(token && *token++ == '_'){
1265 if(!strcmp(token, "LOCAL_ADDRESS_")){
1266 p = (ps_global->VAR_LOCAL_ADDRESS
1267 && ps_global->VAR_LOCAL_ADDRESS[0])
1268 ? ps_global->VAR_LOCAL_ADDRESS
1269 : "postmaster";
1270 a = rfc822_parse_mailbox(&p, ps_global->maildomain);
1271 a->personal = cpystr((ps_global->VAR_LOCAL_FULLNAME
1272 && ps_global->VAR_LOCAL_FULLNAME[0])
1273 ? ps_global->VAR_LOCAL_FULLNAME
1274 : "Place to report Alpine Bugs");
1276 else if(!strcmp(token, "BUGS_ADDRESS_")){
1277 p = (ps_global->VAR_BUGS_ADDRESS
1278 && ps_global->VAR_BUGS_ADDRESS[0])
1279 ? ps_global->VAR_BUGS_ADDRESS : "postmaster";
1280 a = rfc822_parse_mailbox(&p, ps_global->maildomain);
1281 a->personal = cpystr((ps_global->VAR_BUGS_FULLNAME
1282 && ps_global->VAR_BUGS_FULLNAME[0])
1283 ? ps_global->VAR_BUGS_FULLNAME
1284 : "Place to report Alpine Bugs");
1288 return(a);
1292 char *
1293 gripe_id(key)
1294 char *key;
1296 int i,j,k,l;
1299 * Build our contribution to the subject; part constant string
1300 * and random 4 character alpha numeric string.
1302 i = (int)(random() % 36L);
1303 j = (int)(random() % 36L);
1304 k = (int)(random() % 36L);
1305 l = (int)(random() % 36L);
1306 tmp_20k_buf[0] = '\0';
1307 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s (ID %c%c%d%c%c)", key,
1308 (i < 10) ? '0' + i : 'A' + (i - 10),
1309 (j < 10) ? '0' + j : 'A' + (j - 10),
1310 (int)(random() % 10L),
1311 (k < 10) ? '0' + k : 'A' + (k - 10),
1312 (l < 10) ? '0' + l : 'A' + (l - 10));
1313 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1314 return(cpystr(tmp_20k_buf));
1319 * Used by gripe_tool.
1321 void
1322 att_cur_drawer(void)
1324 int i, dline, j;
1325 char buf[256+1];
1327 /* blat helpful message to screen */
1328 ClearBody();
1329 j = 0;
1330 for(dline = 2;
1331 dline < ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global);
1332 dline++){
1333 for(i = 0; i < 256 && att_cur_msg[j] && att_cur_msg[j] != '\n'; i++)
1334 buf[i] = att_cur_msg[j++];
1336 buf[i] = '\0';
1337 if(att_cur_msg[j])
1338 j++;
1339 else if(!i)
1340 break;
1342 PutLine0(dline, 1, buf);
1347 #ifdef _WINDOWS
1352 char *
1353 pcpine_help(HelpType section)
1355 char **help_lines, *help_text = NULL;
1356 STORE_S *store;
1357 gf_io_t pc;
1359 /* assumption here is that HelpType is char ** */
1360 help_lines = section;
1361 if (help_lines != NULL){
1362 init_helper_getc(help_lines);
1363 if(store = so_get(CharStar, NULL, EDIT_ACCESS)){
1364 gf_set_so_writec(&pc, store);
1365 gf_filter_init();
1367 gf_link_filter(gf_local_nvtnl, NULL);
1369 gf_link_filter(gf_html2plain,
1370 gf_html2plain_opt(NULL,
1371 ps_global->ttyo->screen_cols,
1372 non_messageview_margin(), NULL, NULL, GFHP_STRIPPED));
1374 if(!gf_pipe(helper_getc, pc)){
1375 help_text = (char *) store->txt;
1376 store->txt = (void *) NULL;
1379 gf_clear_so_writec(store);
1380 so_give(&store);
1384 return(help_text);
1392 help_popup(SCROLL_S *sparms, int in_handle)
1394 MPopup hp_menu[10];
1395 int i = -1;
1397 if(in_handle){
1398 hp_menu[++i].type = tQueue;
1399 hp_menu[i].label.style = lNormal;
1400 hp_menu[i].label.string = "View Help Section";
1401 hp_menu[i].data.val = 'V';
1404 hp_menu[++i].type = tQueue;
1405 hp_menu[i].label.style = lNormal;
1406 hp_menu[i].label.string = "Exit Help";
1407 hp_menu[i].data.val = 'E';
1409 hp_menu[++i].type = tTail;
1411 mswin_popup(hp_menu);
1413 return(0);
1421 help_subsection_popup(SCROLL_S *sparms, int in_handle)
1423 MPopup hp_menu[10];
1424 int i = -1;
1426 if(in_handle){
1427 hp_menu[++i].type = tQueue;
1428 hp_menu[i].label.style = lNormal;
1429 hp_menu[i].label.string = "View Help Section";
1430 hp_menu[i].data.val = 'V';
1433 hp_menu[++i].type = tQueue;
1434 hp_menu[i].label.style = lNormal;
1435 hp_menu[i].label.string = "Previous Help Section";
1436 hp_menu[i].data.val = 'P';
1438 hp_menu[++i].type = tQueue;
1439 hp_menu[i].label.style = lNormal;
1440 hp_menu[i].label.string = "Exit Help";
1441 hp_menu[i].data.val = 'E';
1443 hp_menu[++i].type = tTail;
1445 if(mswin_popup(hp_menu) == (in_handle ? 1 : 0))
1446 /*(void) helper_internal()*/;
1448 return(0);
1451 #endif /* _WINDOWS */