1 /*
2  * Copyright (c) 2000-2009, 2013-2015, 2018, 2020 Paul Mattes.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *     * Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *     * Redistributions in binary form must reproduce the above copyright
10  *       notice, this list of conditions and the following disclaimer in the
11  *       documentation and/or other materials provided with the distribution.
12  *     * Neither the names of Paul Mattes nor the names of his contributors
13  *       may be used to endorse or promote products derived from this software
14  *       without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY PAUL MATTES "AS IS" AND ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19  * EVENT SHALL PAUL MATTES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 /*
29  *	Help.c
30  *		Help information for c3270.
31  */
32 
33 #include "globals.h"
34 #include "appres.h"
35 #include "resources.h"
36 
37 #include "actions.h"
38 #include "c3270.h"
39 #include "glue.h"
40 #include "help.h"
41 #include "icmdc.h"
42 #include "names.h"
43 #include "popups.h"
44 #include "screen.h"
45 #include "utils.h"
46 
47 #if defined(_WIN32) /*[*/
48 # include "wc3270.h"
49 #endif /*]*/
50 
51 #define P_3270		0x0001	/* 3270 actions */
52 #define P_SCRIPTING	0x0002	/* scripting actions */
53 #define P_INTERACTIVE	0x0004	/* interactive (command-prompt) actions */
54 #define P_OPTIONS	0x0008	/* command-line options */
55 #define P_TRANSFER	0x0010	/* file transfer options */
56 #define P_HTML		0x0020	/* HTML help */
57 
58 #if defined(WC3270) /*[*/
59 #define HELP_W	"w"
60 #else /*][*/
61 #define HELP_W	""
62 #endif /*]*/
63 
64 static struct {
65 	const char *name;
66 	const char *args;
67 	int purpose;
68 	const char *help;
69 } cmd_help[] = {
70 	{ AnAbort,	NULL, P_SCRIPTING, "Abort pending scripts and macros" },
71 	{ AnAnsiText,	NULL, P_SCRIPTING, "Dump pending NVT text" },
72 	{ AnAscii,	NULL, P_SCRIPTING, "Screen contents in ASCII" },
73 	{ AnAscii,	"<n>", P_SCRIPTING,
74 	    "<n> bytes of screen contents from cursor, in ASCII" },
75 	{ AnAscii,	"<row>,<col>,<n>", P_SCRIPTING,
76 	    "<n> bytes of screen contents from <row>,<col> (0-origin), in ASCII" },
77 	{ AnAscii,	"<row>,<col>,<rows>,<cols>", P_SCRIPTING,
78 	    "<rows>x<cols> of screen contents from <row>,<col> (0-origin), in ASCII" },
79 	{ AnAscii1,	"<row>,<col>,<n>", P_SCRIPTING,
80 	    "<n> bytes of screen contents from <row>,<col> (1-origin), in ASCII" },
81 	{ AnAscii1,	"<row>,<col>,<rows>,<cols>", P_SCRIPTING,
82 	    "<rows>x<cols> of screen contents from <row>,<col> (1-origin), in ASCII" },
83 	{ AnAsciiField, NULL, P_SCRIPTING,
84 	    "Contents of current field, in ASCII" },
85 	{ AnAttn, NULL, P_3270, "Send 3270 ATTN sequence (TELNET IP)" },
86 	{ AnBackSpace, NULL, P_3270, "Move cursor left" },
87 	{ AnBackTab, NULL, P_3270, "Move to previous field" },
88 	{ AnBell, NULL, P_SCRIPTING, "Ring the terminal bell" },
89 	{ AnCircumNot, NULL, P_3270,
90 	    "Send ~ in NVT mode, notsign (X'5F', U+00AC) in 3270 mode" },
91 	{ AnClear, NULL, P_3270, "Send CLEAR AID (clear screen)" },
92 	{ AnClose, NULL, P_INTERACTIVE, "Alias for " AnDisconnect "()" },
93 	{ AnCloseScript, NULL, P_SCRIPTING, "Exit peer script" },
94 	{ AnCompose, NULL, P_INTERACTIVE,
95 	    "Interpret next two keystrokes according to the compose map" },
96 	{ AnConnect, "[L:][Y:][A:][<lu>@]<host>[:<port>][=<accept>]",
97 	    P_INTERACTIVE, "Open connection to <host>" },
98 #if defined(LOCAL_PROCESS) /*[*/
99 	{ AnConnect, "-e,[<command>[,<arg>...]]", P_INTERACTIVE,
100 	    "Open connection to a local shell or command" },
101 #endif /*]*/
102 #if defined(WC3270) /*[*/
103 	{ AnCopy, NULL, P_3270, "Copy selected text to Windows clipboard" },
104 #endif /*]*/
105 	{ AnCursorSelect, NULL, P_3270,
106 	    "Light pen select at cursor location" },
107 #if defined(WC3270) /*[*/
108 	{ AnCut, NULL, P_3270,
109 	    "Copy selected text to Windows clipboard, then erase" },
110 #endif /*]*/
111 	{ AnDelete, NULL, P_3270, "Delete character at cursor" },
112 	{ AnDeleteField, NULL, P_3270, "Erase field at cursor location (^U)" },
113 	{ AnDeleteWord, NULL, P_3270,
114 	    "Erase word before cursor location (^W)" },
115 	{ AnDisconnect, NULL, P_INTERACTIVE, "Close connection to host" },
116 	{ AnDown, NULL, P_3270, "Move cursor down" },
117 	{ AnDup, NULL, P_3270, "3270 DUP key (X'1C')" },
118 	{ AnEbcdic,	NULL, P_SCRIPTING, "Screen contents in EBCDIC" },
119 	{ AnEbcdic,	"<n>", P_SCRIPTING,
120 	    "<n> bytes of screen contents from cursor, in EBCDIC" },
121 	{ AnEbcdic,	"<row>,<col>,<n>", P_SCRIPTING,
122 	    "<n> bytes of screen contents from <row>,<col> (0-origin), in EBCDIC" },
123 	{ AnEbcdic,	"<row>,<col>,<rows>,<cols>", P_SCRIPTING,
124 	    "<rows>x<cols> of screen contents from <row>,<col> (0-origin), in EBCDIC" },
125 	{ AnEbcdic1,	"<row>,<col>,<n>", P_SCRIPTING,
126 	    "<n> bytes of screen contents from <row>,<col> (1-origin), in EBCDIC" },
127 	{ AnEbcdic1,	"<row>,<col>,<rows>,<cols>", P_SCRIPTING,
128 	    "<rows>x<cols> of screen contents from <row>,<col> (1-origin), in EBCDIC" },
129 	{ AnEbcdicField, NULL, P_SCRIPTING,
130 	    "Contents of current field, in EBCDIC" },
131 	{ AnEnter, NULL, P_3270, "Send ENTER AID" },
132 	{ AnErase, NULL, P_3270, "Destructive backspace" },
133 	{ AnEraseEOF, NULL, P_3270, "Erase from cursor to end of field" },
134 	{ AnEraseInput, NULL, P_3270, "Erase all input fields" },
135 	{ AnEscape, NULL, P_INTERACTIVE,
136 	    "Escape to '" HELP_W "c3270>' prompt" },
137 	{ AnExecute, "<command>", P_SCRIPTING, "Execute a shell command" },
138 	{ "Exit", NULL, P_INTERACTIVE, "Exit " HELP_W "c3270" },
139 	{ AnExpect, "<pattern>", P_SCRIPTING, "Wait for NVT output" },
140 	{ AnFieldEnd, NULL, P_3270, "Move to end of field" },
141 	{ AnFieldMark, NULL, P_3270, "3270 FIELD MARK key (X'1E')" },
142 	{ AnFlip, NULL, P_3270, "Flip display left-to-right" },
143 	{ AnHelp, "all|interactive|3270|scripting|transfer|<action>",
144 	    P_INTERACTIVE, "Get help" },
145 	{ AnHexString, "<digits>", P_3270|P_SCRIPTING,
146 	    "Input field data in hex" },
147 	{ AnHome, NULL, P_3270, "Move cursor to first field" },
148 	{ Anignore, NULL, P_3270, "Do nothing" },
149 	{ "Info", "<text>", P_SCRIPTING|P_INTERACTIVE, "Display text in OIA" },
150 	{ AnInsert, NULL, P_3270, "Set 3270 insert mode" },
151 	{ AnInterrupt, NULL, P_3270, "In NVT mode, send IAC IP" },
152 	{ AnKey, "<symbol>|0x<nn>", P_3270, "Input one character" },
153 	{ AnKeyboardDisable, "[" ResTrue "|" ResFalse "|" KwForceEnable "]",
154 	    P_SCRIPTING|P_INTERACTIVE,
155 	    "Modify automatic script keyboard locking" },
156 	{ AnKeymap, "[<keymap-name>]", P_SCRIPTING|P_INTERACTIVE,
157 	    "Push temporary keymap, or pop if none specified" },
158 	{ AnKeypad, NULL, P_INTERACTIVE, "Pop up the 3270 keypad" },
159 	{ AnLeft, NULL, P_3270, "Move cursr left" },
160 	{ AnLeft2, NULL, P_3270, "Move cursor left 2 columns" },
161 	{ AnMacro, "<name>", P_SCRIPTING, "Execute a predefined macro" },
162 	{ AnMenu, NULL, P_INTERACTIVE, "Pop up the command menu" },
163 	{ AnMoveCursor, "<row>,<col>", P_3270|P_SCRIPTING,
164 	    "Move cursor to specific location (0-origin, deprecated)" },
165 	{ AnMoveCursor, "<offset>", P_3270|P_SCRIPTING,
166 	    "Move cursor to a buffer offset (0-origin)" },
167 	{ AnMoveCursor1, "<row>,<col>", P_3270|P_SCRIPTING,
168 	    "Move cursor to specific location (1-origin)" },
169 	{ AnNewline, NULL, P_3270, "Move cursor to first field in next row" },
170 	{ AnNextWord, NULL, P_3270, "Move cursor to next word" },
171 	{ AnOpen, NULL, P_INTERACTIVE, "Alias for " AnConnect "()" },
172 	{ AnPA, "<n>", P_3270, "Send 3270 Program Attention" },
173 #if defined(WC3270) /*[*/
174 	{ "Paste", NULL, P_3270, "Paste clipboard contents" },
175 #endif /*]*/
176 	{ AnPF, "<n>", P_3270, "Send 3270 PF AID" },
177 	{ AnPreviousWord, NULL, P_3270, "Move cursor to previous word" },
178 	{ AnPrinter, KwStart "[,lu]|" KwStop, P_3270|P_SCRIPTING|P_INTERACTIVE,
179 	    "Start or stop " HELP_W "pr3287 printer session" },
180         { AnPrintText,
181 	    "[" KwHtml "|" KwRtf ",][" KwModi ",][" KwCaption ",<caption>,][" KwReplace "|" KwAppend ",]" KwFile ",<filename>",
182 	    P_INTERACTIVE|P_SCRIPTING,
183 	    "Save screen image in a file" },
184         { AnPrintText,
185 	    "[" KwModi ",][" KwCaption ",<caption>],"
186 #if defined(WC3270) /*[*/
187 	    "[" KwDialog "|" KwNoDialog ",][<printer-name>]",
188 #else /*][*/
189 	    "[<print-command>]",
190 #endif /*]*/
191 	    P_INTERACTIVE|P_SCRIPTING,
192 	    "Print screen image" },
193 	{ AnPrompt, "[app-name]", P_SCRIPTING|P_INTERACTIVE,
194 	    "Start an external prompt" },
195         { AnQuery, "<keyword>", P_SCRIPTING|P_INTERACTIVE,
196 	    "Query operational parameters" },
197 	{ AnQuit, NULL, P_INTERACTIVE, "Exit " HELP_W "3270" },
198         { AnReadBuffer, "[" KwAscii "|" KwEbcdic "|" KwUnicode "]", P_SCRIPTING,
199 	    "Dump display buffer" },
200         { AnReadBuffer, "[" KwAscii "|" KwEbcdic "|" KwUnicode ",]" KwField, P_SCRIPTING,
201 	    "Dump display buffer for current field" },
202 	{ AnReconnect, NULL, P_INTERACTIVE, "Reconnect to previous host" },
203 	{ AnRedraw, NULL, P_INTERACTIVE|P_3270, "Redraw screen" },
204 	{ AnReset, NULL, P_3270, "Clear keyboard lock" },
205 	{ AnRight, NULL, P_3270, "Move cursor right" },
206 	{ AnRight2, NULL, P_3270, "Move cursor right 2 columns" },
207 	{ AnScreenTrace, KwOn "[[," KwFile "],<filename>]",  P_INTERACTIVE,
208 	    "Save screen images to file" },
209 	{ AnScreenTrace,
210 # if defined(_WIN32) /*[*/
211 	    KwOn "," KwPrinter "[,<printer-name>]",
212 # else /*][*/
213 	    KwOn "," KwPrinter "[,<print-command>]",
214 # endif /*]*/
215 	    P_INTERACTIVE, "Save screen images to printer" },
216 	{ AnScreenTrace, KwOff, P_INTERACTIVE, "Stop saving screen images" },
217 	{ AnScript, "[" KwDashAsync ",][" KwDashNoLock ",][" KwDashSingle ",]"
218 #if defined(_WIN32) /*[*/
219 	    "[" KwDashShareConsole ",]"
220 #endif /*]*/
221 	    "<path>[,<arg>...]",
222 	    P_SCRIPTING, "Run a child script" },
223 	{ AnScroll, KwForward "|" KwBackward, P_INTERACTIVE, "Scroll screen" },
224 	{ AnSet, "[<setting-name>,value]", P_INTERACTIVE|P_SCRIPTING,
225 	    "Change a setting or display all settings" },
226 	{ AnShow, KwCopyright "|" KwStatus "|" KwKeymap, P_INTERACTIVE,
227 	    "Display status and settings" },
228 	{ AnSnap, "<args>", P_SCRIPTING, "Screen snapshot manipulation" },
229         { AnSource, "<file>", P_SCRIPTING|P_INTERACTIVE, "Read actions from file" },
230 	{ AnString, "<text>", P_3270|P_SCRIPTING, "Input a string" },
231 	{ AnSysReq, NULL, P_3270,
232 	    "Send 3270 Attention (TELNET ABORT or SYSREQ AID)" },
233 	{ AnTab, NULL, P_3270, "Move cursor to next field" },
234 	{ AnTemporaryComposeMap, "[<compose-map-name>]",
235 	    P_SCRIPTING|P_INTERACTIVE, "Set or clear temporary compose map" },
236 	{ AnTemporaryKeymap, "[<keymap-name>]", P_SCRIPTING|P_INTERACTIVE,
237 	    "Alias for " AnKeymap "()" },
238 #if defined(WC3270) /*[*/
239 	{ AnTitle, "<text>", P_SCRIPTING|P_INTERACTIVE, "Change window title" },
240 #endif /*]*/
241 	{ AnToggle, "[<toggle-name>[,value]]", P_INTERACTIVE|P_SCRIPTING,
242 	    "Change a toggle" },
243 	{ AnToggleInsert, NULL, P_3270, "Set or clear 3270 insert mode" },
244 	{ AnToggleReverse, NULL, P_3270, "Set or clear reverse-input mode" },
245 	{ AnTrace, KwOn "[,<file>]|" KwOff, P_INTERACTIVE, "Configure tracing" },
246 	{ AnTransfer, "[<args>]", P_INTERACTIVE,
247 	    "IND$FILE file transfer (see 'help file-transfer')" },
248 	{ AnUp, NULL, P_3270, "Move cursor up" },
249 	{ AnWait, "<args>", P_SCRIPTING, "Wait for host events" },
250 	{ NULL,  NULL, 0, NULL }
251 };
252 
253 #if defined(HAVE_START) || defined(WC3270) /*[*/
254 static void html_help(bool);
255 #endif /*]*/
256 
257 static struct {
258 	const char *name;
259 	int flag;
260 	const char *text;
261 	const char **block;
262 	void (*fn)(bool);
263 } help_subcommand[] = {
264 #if defined(HAVE_START) /*[*/
265 	{ "online",		P_HTML,		NULL, NULL, html_help },
266 #endif /*]*/
267 	{ "all",		-1,		NULL, NULL, NULL },
268 	{ "3270",		P_3270,		NULL, NULL, NULL },
269 	{ "interactive",	P_INTERACTIVE,	NULL, NULL, NULL },
270 	{ "options",		P_OPTIONS,	NULL, NULL, &cmdline_help },
271 	{ "scripting",		P_SCRIPTING,	NULL, NULL, NULL },
272 	{ "file-transfer",	P_TRANSFER,	NULL, NULL, ft_help },
273 #if defined(WC3270) /*[*/
274 	{ "html",		P_HTML,		NULL, NULL, html_help },
275 #endif /*]*/
276 	{ NULL, 0, NULL }
277 };
278 
279 /* c3270-specific actions. */
280 static bool
Help_action(ia_t ia,unsigned argc,const char ** argv)281 Help_action(ia_t ia, unsigned argc, const char **argv)
282 {
283     int i;
284     int overall = -1;
285     int match = 0;
286     bool any = false;
287 
288     action_debug(AnHelp, ia, argc, argv);
289     if (check_argc(AnHelp, argc, 0, 1) < 0) {
290 	return false;
291     }
292 
293     if (argc != 1) {
294 	action_output(
295 #if defined(HAVE_START) /*[*/
296 "  help online        launch online help\n"
297 #endif /*]*/
298 "  help all           all actions\n"
299 "  help 3270          3270 actions\n"
300 "  help interactive   interactive (command-prompt) actions\n"
301 "  help <action>      help for one <action>\n"
302 "  help options       command-line options\n"
303 "  help scripting     scripting actions\n"
304 "  help file-transfer file transfer options\n"
305 #if defined(WC3270) /*[*/
306 "  help html          alias for 'help online'\n"
307 #endif /*]*/
308 	);
309 	return true;
310     }
311 
312     /* The (hidden) verify option verifies the integrity of the help list. */
313     if (!strcmp(argv[0], "verify")) {
314 	action_elt_t *e;
315 	bool any = false;
316 
317 	for (i = 0; cmd_help[i].name; i++) {
318 	    bool found = false;
319 
320 	    FOREACH_LLIST(&actions_list, e, action_elt_t *) {
321 		if (!strcasecmp(cmd_help[i].name, e->t.name)) {
322 		    found = true;
323 		    break;
324 		}
325 	    } FOREACH_LLIST_END(&actions_list, e, action_elt_t *);
326 	    if (!found) {
327 		action_output("Help for nonexistent action: %s",
328 			cmd_help[i].name);
329 		any = true;
330 	    }
331 	}
332 	if (!any) {
333 	    action_output("No orphaned help messages.");
334 	}
335 	any = false;
336 	FOREACH_LLIST(&actions_list, e, action_elt_t *) {
337 	    bool found = false;
338 
339 	    for (i = 0; cmd_help[i].name; i++) {
340 
341 		if (!strcasecmp(cmd_help[i].name, e->t.name)) {
342 		    found = true;
343 		    break;
344 		}
345 	    }
346 	    if (!found) {
347 		action_output("No Help for %s", e->t.name);
348 		any = true;
349 	    }
350 	} FOREACH_LLIST_END(&actions_list, e, action_elt_t *);
351 	if (!any) {
352 	    printf("No orphaned actions.\n");
353 	}
354 	return true;
355     }
356 
357     /* Do a substring match on all of the actions. */
358     for (i = 0; cmd_help[i].name != NULL; i++) {
359 	if (!strncasecmp(cmd_help[i].name, argv[0], strlen(argv[0]))) {
360 	    action_output("  %s(%s)\n    %s",
361 		    cmd_help[i].name,
362 		    cmd_help[i].args? cmd_help[i].args: "",
363 		    cmd_help[i].help? cmd_help[i].help: "");
364 		    any = true;
365 	}
366     }
367     if (any) {
368 	return true;
369     }
370 
371     /* Check for an exact match on one of the topics. */
372     for (i = 0; help_subcommand[i].name != NULL; i++) {
373 	if (!strncasecmp(help_subcommand[i].name, argv[0], strlen(argv[0]))) {
374 	    match = help_subcommand[i].flag;
375 	    overall = i;
376 	    break;
377 	}
378     }
379 
380     if (!match) {
381 	action_output("No such command: %s", argv[0]);
382 	return false;
383     }
384 
385     /* Matched on a topic. */
386     if (help_subcommand[overall].text != NULL) {
387 	/* One-line topic. */
388 	action_output("%s", help_subcommand[overall].text);
389 	return true;
390     }
391     if (help_subcommand[overall].block != NULL) {
392 	int j;
393 
394 	/* Multi-line topic. */
395 	for (j = 0; help_subcommand[overall].block[j] != NULL; j++) {
396 	    action_output("%s", help_subcommand[overall].block[j]);
397 	}
398 	return true;
399     }
400     if (help_subcommand[overall].fn != NULL) {
401 	/* Indirect output for topic. */
402 	(*help_subcommand[overall].fn)(true);
403 	return true;
404     }
405 
406     /* Category. */
407     for (i = 0; cmd_help[i].name != NULL; i++) {
408 	if (cmd_help[i].purpose & match) {
409 	    action_output("  %s(%s)\n    %s",
410 		    cmd_help[i].name,
411 		    cmd_help[i].args? cmd_help[i].args: "",
412 		    cmd_help[i].help? cmd_help[i].help: "");
413 	}
414     }
415 
416     return true;
417 }
418 
419 #if defined(HAVE_START) || defined(WC3270) /*[*/
420 static void
html_help(bool ignored _is_unused)421 html_help(bool ignored _is_unused)
422 {
423     start_html_help();
424 }
425 #endif /*]*/
426 
427 /**
428  * Help module registration.
429  */
430 void
help_register(void)431 help_register(void)
432 {
433     static action_table_t help_actions[] = {
434 	{ AnHelp,	Help_action,	ACTION_KE }
435     };
436 
437     /* Register the actions. */
438     register_actions(help_actions, array_count(help_actions));
439 }
440