Bug Summary

File:src/gnu/usr.bin/texinfo/info/infodoc.c
Warning:line 899, column 3
Value stored to 'i' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name infodoc.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/gnu/usr.bin/texinfo/obj/info -resource-dir /usr/local/lib/clang/13.0.0 -D HAVE_CONFIG_H -D LOCALEDIR="/usr/share/locale" -D INFODIR="/usr/share/info" -D INFODIR2="/usr/share/info" -I . -I /usr/src/gnu/usr.bin/texinfo/info -I .. -I . -I /usr/src/gnu/usr.bin/texinfo/lib -I ../intl -I .. -I /usr/src/gnu/usr.bin/texinfo/info -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/gnu/usr.bin/texinfo/obj/info -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/gnu/usr.bin/texinfo/info/infodoc.c
1/* infodoc.c -- functions which build documentation nodes.
2 $Id: infodoc.c,v 1.5 2006/07/17 16:12:36 espie Exp $
3
4 Copyright (C) 1993, 1997, 1998, 1999, 2001, 2002, 2003, 2004 Free Software
5 Foundation, Inc.
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20
21 Written by Brian Fox (bfox@ai.mit.edu). */
22
23#include "info.h"
24#include "funs.h"
25
26/* HELP_NODE_GETS_REGENERATED is always defined now that keys may get
27 rebound, or other changes in the help text may occur. */
28#define HELP_NODE_GETS_REGENERATED1 1
29
30/* The name of the node used in the help window. */
31static char *info_help_nodename = "*Info Help*";
32
33/* A node containing printed key bindings and their documentation. */
34static NODE *internal_info_help_node = (NODE *)NULL((void *)0);
35
36/* A pointer to the contents of the help node. */
37static char *internal_info_help_node_contents = (char *)NULL((void *)0);
38
39/* The (more or less) static text which appears in the internal info
40 help node. The actual key bindings are inserted. Keep the
41 underlines (****, etc.) in the same N_ call as the text lines they
42 refer to, so translations can make the number of *'s or -'s match. */
43#if defined(INFOKEY)
44
45static char *info_internal_help_text[] = {
46 N_("Basic Commands in Info Windows\n\("Basic Commands in Info Windows\n******************************\n"
)
47******************************\n")("Basic Commands in Info Windows\n******************************\n"
)
,
48 "\n",
49 N_("\\%-10[quit-help] Quit this help.\n")("\\%-10[quit-help] Quit this help.\n"),
50 N_("\\%-10[quit] Quit Info altogether.\n")("\\%-10[quit] Quit Info altogether.\n"),
51 N_("\\%-10[get-info-help-node] Invoke the Info tutorial.\n")("\\%-10[get-info-help-node] Invoke the Info tutorial.\n"),
52 "\n",
53 N_("Selecting other nodes:\n\("Selecting other nodes:\n----------------------\n")
54----------------------\n")("Selecting other nodes:\n----------------------\n"),
55 N_("\\%-10[next-node] Move to the \"next\" node of this node.\n")("\\%-10[next-node] Move to the \"next\" node of this node.\n"
)
,
56 N_("\\%-10[prev-node] Move to the \"previous\" node of this node.\n")("\\%-10[prev-node] Move to the \"previous\" node of this node.\n"
)
,
57 N_("\\%-10[up-node] Move \"up\" from this node.\n")("\\%-10[up-node] Move \"up\" from this node.\n"),
58 N_("\\%-10[menu-item] Pick menu item specified by name.\n\("\\%-10[menu-item] Pick menu item specified by name.\n Picking a menu item causes another node to be selected.\n"
)
59 Picking a menu item causes another node to be selected.\n")("\\%-10[menu-item] Pick menu item specified by name.\n Picking a menu item causes another node to be selected.\n"
)
,
60 N_("\\%-10[xref-item] Follow a cross reference. Reads name of reference.\n")("\\%-10[xref-item] Follow a cross reference. Reads name of reference.\n"
)
,
61 N_("\\%-10[history-node] Move to the last node seen in this window.\n")("\\%-10[history-node] Move to the last node seen in this window.\n"
)
,
62 N_("\\%-10[move-to-next-xref] Skip to next hypertext link within this node.\n")("\\%-10[move-to-next-xref] Skip to next hypertext link within this node.\n"
)
,
63 N_("\\%-10[move-to-prev-xref] Skip to previous hypertext link within this node.\n")("\\%-10[move-to-prev-xref] Skip to previous hypertext link within this node.\n"
)
,
64 N_("\\%-10[select-reference-this-line] Follow the hypertext link under cursor.\n")("\\%-10[select-reference-this-line] Follow the hypertext link under cursor.\n"
)
,
65 N_("\\%-10[dir-node] Move to the `directory' node. Equivalent to `\\[goto-node] (DIR)'.\n")("\\%-10[dir-node] Move to the `directory' node. Equivalent to `\\[goto-node] (DIR)'.\n"
)
,
66 N_("\\%-10[top-node] Move to the Top node. Equivalent to `\\[goto-node] Top'.\n")("\\%-10[top-node] Move to the Top node. Equivalent to `\\[goto-node] Top'.\n"
)
,
67 "\n",
68 N_("Moving within a node:\n\("Moving within a node:\n---------------------\n")
69---------------------\n")("Moving within a node:\n---------------------\n"),
70 N_("\\%-10[beginning-of-node] Go to the beginning of this node.\n")("\\%-10[beginning-of-node] Go to the beginning of this node.\n"
)
,
71 N_("\\%-10[end-of-node] Go to the end of this node.\n")("\\%-10[end-of-node] Go to the end of this node.\n"),
72 N_("\\%-10[next-line] Scroll forward 1 line.\n")("\\%-10[next-line] Scroll forward 1 line.\n"),
73 N_("\\%-10[prev-line] Scroll backward 1 line.\n")("\\%-10[prev-line] Scroll backward 1 line.\n"),
74 N_("\\%-10[scroll-forward] Scroll forward a page.\n")("\\%-10[scroll-forward] Scroll forward a page.\n"),
75 N_("\\%-10[scroll-backward] Scroll backward a page.\n")("\\%-10[scroll-backward] Scroll backward a page.\n"),
76 "\n",
77 N_("Other commands:\n\("Other commands:\n---------------\n")
78---------------\n")("Other commands:\n---------------\n"),
79 N_("\\%-10[menu-digit] Pick first ... ninth item in node's menu.\n")("\\%-10[menu-digit] Pick first ... ninth item in node's menu.\n"
)
,
80 N_("\\%-10[last-menu-item] Pick last item in node's menu.\n")("\\%-10[last-menu-item] Pick last item in node's menu.\n"),
81 N_("\\%-10[index-search] Search for a specified string in the index entries of this Info\n\("\\%-10[index-search] Search for a specified string in the index entries of this Info\n file, and select the node referenced by the first entry found.\n"
)
82 file, and select the node referenced by the first entry found.\n")("\\%-10[index-search] Search for a specified string in the index entries of this Info\n file, and select the node referenced by the first entry found.\n"
)
,
83 N_("\\%-10[goto-node] Move to node specified by name.\n\("\\%-10[goto-node] Move to node specified by name.\n You may include a filename as well, as in (FILENAME)NODENAME.\n"
)
84 You may include a filename as well, as in (FILENAME)NODENAME.\n")("\\%-10[goto-node] Move to node specified by name.\n You may include a filename as well, as in (FILENAME)NODENAME.\n"
)
,
85 N_("\\%-10[search] Search forward for a specified string\n\("\\%-10[search] Search forward for a specified string\n and select the node in which the next occurrence is found.\n"
)
86 and select the node in which the next occurrence is found.\n")("\\%-10[search] Search forward for a specified string\n and select the node in which the next occurrence is found.\n"
)
,
87 N_("\\%-10[search-backward] Search backward for a specified string\n\("\\%-10[search-backward] Search backward for a specified string\n and select the node in which the previous occurrence is found.\n"
)
88 and select the node in which the previous occurrence is found.\n")("\\%-10[search-backward] Search backward for a specified string\n and select the node in which the previous occurrence is found.\n"
)
,
89 NULL((void *)0)
90};
91
92#else /* !INFOKEY */
93
94static char *info_internal_help_text[] = {
95 N_("Basic Commands in Info Windows\n\("Basic Commands in Info Windows\n******************************\n"
)
96******************************\n")("Basic Commands in Info Windows\n******************************\n"
)
,
97 "\n",
98 N_(" %-10s Quit this help.\n")(" %-10s Quit this help.\n"),
99 N_(" %-10s Quit Info altogether.\n")(" %-10s Quit Info altogether.\n"),
100 N_(" %-10s Invoke the Info tutorial.\n")(" %-10s Invoke the Info tutorial.\n"),
101 "\n",
102 N_("Selecting other nodes:\n\
103----------------------\n",
104 N_(" %-10s Move to the `next' node of this node.\n"),
105 N_(" %-10s Move to the `previous' node of this node.\n"),
106 N_(" %-10s Move `up' from this node.\n"),
107 N_(" %-10s Pick menu item specified by name.\n"),
108 N_(" Picking a menu item causes another node to be selected.\n"),
109 N_(" %-10s Follow a cross reference. Reads name of reference.\n"),
110 N_(" %-10s Move to the last node seen in this window.\n"),
111 N_(" %-10s Skip to next hypertext link within this node.\n"),
112 N_(" %-10s Follow the hypertext link under cursor.\n"),
113 N_(" %-10s Move to the `directory' node. Equivalent to `g (DIR)'.\n"),
114 N_(" %-10s Move to the Top node. Equivalent to `g Top'.\n"),
115 "\n",
116 N_("Moving within a node:\n\
117---------------------\n"),
118 N_(" %-10s Scroll forward a page.\n"),
119 N_(" %-10s Scroll backward a page.\n"),
120 N_(" %-10s Go to the beginning of this node.\n"),
121 N_(" %-10s Go to the end of this node.\n"),
122 N_(" %-10s Scroll forward 1 line.\n"),
123 N_(" %-10s Scroll backward 1 line.\n"),
124 "\n",
125 N_("Other commands:\n\
126---------------\n"),
127 N_(" %-10s Pick first ... ninth item in node's menu.\n"),
128 N_(" %-10s Pick last item in node's menu.\n"),
129 N_(" %-10s Search for a specified string in the index entries of this Info\n"),
130 N_(" file, and select the node referenced by the first entry found.\n"),
131 N_(" %-10s Move to node specified by name.\n"),
132 N_(" You may include a filename as well, as in (FILENAME)NODENAME.\n"),
133 N_(" %-10s Search forward for a specified string,\n"),
134 N_(" and select the node in which the next occurrence is found.\n"),
135 N_(" %-10s Search backward for a specified string\n"),
136 N_(" and select the node in which the next occurrence is found.\n"),
137 NULL
138};
139
140static char *info_help_keys_text[][2] = {
141 { "", "" },
142 { "", "" },
143 { "", "" },
144 { "CTRL-x 0", "CTRL-x 0" },
145 { "q", "q" },
146 { "h", "ESC h" },
147 { "", "" },
148 { "", "" },
149 { "", "" },
150 { "SPC", "SPC" },
151 { "DEL", "b" },
152 { "b", "ESC b" },
153 { "e", "ESC e" },
154 { "ESC 1 SPC", "RET" },
155 { "ESC 1 DEL", "y" },
156 { "", "" },
157 { "", "" },
158 { "", "" },
159 { "n", "CTRL-x n" },
160 { "p", "CTRL-x p" },
161 { "u", "CTRL-x u" },
162 { "m", "ESC m" },
163 { "", "" },
164 { "f", "ESC f" },
165 { "l", "l" },
166 { "TAB", "TAB" },
167 { "RET", "CTRL-x RET" },
168 { "d", "ESC d" },
169 { "t", "ESC t" },
170 { "", "" },
171 { "", "" },
172 { "", "" },
173 { "1-9", "ESC 1-9" },
174 { "0", "ESC 0" },
175 { "i", "CTRL-x i" },
176 { "", "" },
177 { "g", "CTRL-x g" },
178 { "", "" },
179 { "s", "/" },
180 { "", "" },
181 { "ESC - s", "?" },
182 { "", "" },
183 NULL
184};
185
186#endif /* !INFOKEY */
187
188static char *where_is_internal (Keymap map, InfoCommand *cmd);
189
190void
191dump_map_to_message_buffer (char *prefix, Keymap map)
192{
193 register int i;
194 unsigned prefix_len = strlen (prefix);
195 char *new_prefix = (char *)xmalloc (prefix_len + 2);
196
197 strncpy (new_prefix, prefix, prefix_len);
198 new_prefix[prefix_len + 1] = '\0';
199
200 for (i = 0; i < 256; i++)
201 {
202 new_prefix[prefix_len] = i;
203 if (map[i].type == ISKMAP)
204 {
205 dump_map_to_message_buffer (new_prefix, (Keymap)map[i].function);
206 }
207 else if (map[i].function)
208 {
209 register int last;
210 char *doc, *name;
211
212 doc = function_documentation (map[i].function);
213 name = function_name (map[i].function);
214
215 if (!*doc)
216 continue;
217
218 /* Find out if there is a series of identical functions, as in
219 ea_insert (). */
220 for (last = i + 1; last < 256; last++)
221 if ((map[last].type != ISFUNC) ||
222 (map[last].function != map[i].function))
223 break;
224
225 if (last - 1 != i)
226 {
227 printf_to_message_buffer ("%s .. ", pretty_keyseq (new_prefix),
228 NULL, NULL);
229 new_prefix[prefix_len] = last - 1;
230 printf_to_message_buffer ("%s\t", pretty_keyseq (new_prefix),
231 NULL, NULL);
232 i = last - 1;
233 }
234 else
235 printf_to_message_buffer ("%s\t", pretty_keyseq (new_prefix),
236 NULL, NULL);
237
238#if defined (NAMED_FUNCTIONS)
239 /* Print the name of the function, and some padding before the
240 documentation string is printed. */
241 {
242 int length_so_far;
243 int desired_doc_start = 40; /* Must be multiple of 8. */
244
245 printf_to_message_buffer ("(%s)", name, NULL, NULL);
246 length_so_far = message_buffer_length_this_line ();
247
248 if ((desired_doc_start + strlen (doc))
249 >= (unsigned int) the_screen->width)
250 printf_to_message_buffer ("\n ", NULL, NULL, NULL);
251 else
252 {
253 while (length_so_far < desired_doc_start)
254 {
255 printf_to_message_buffer ("\t", NULL, NULL, NULL);
256 length_so_far += character_width ('\t', length_so_far);
257 }
258 }
259 }
260#endif /* NAMED_FUNCTIONS */
261 printf_to_message_buffer ("%s\n", doc, NULL, NULL);
262 }
263 }
264 free (new_prefix);
265}
266
267/* How to create internal_info_help_node. HELP_IS_ONLY_WINDOW_P says
268 whether we're going to end up in a second (or more) window of our
269 own, or whether there's only one window and we're going to usurp it.
270 This determines how to quit the help window. Maybe we should just
271 make q do the right thing in both cases. */
272
273static void
274create_internal_info_help_node (int help_is_only_window_p)
275{
276 register int i;
277 NODE *node;
278 char *contents = NULL;
279 char *exec_keys;
280
281#ifndef HELP_NODE_GETS_REGENERATED
282 if (internal_info_help_node_contents)
283 contents = internal_info_help_node_contents;
284#endif /* !HELP_NODE_GETS_REGENERATED */
285
286 if (!contents)
287 {
288 int printed_one_mx = 0;
289
290 initialize_message_buffer ();
291
292 for (i = 0; info_internal_help_text[i]; i++)
293 {
294#ifdef INFOKEY
295 printf_to_message_buffer (replace_in_documentation
296 ((char *) _(info_internal_help_text[i]), help_is_only_window_p),
297 NULL, NULL, NULL);
298#else
299 /* Don't translate blank lines, gettext outputs the po file
300 header in that case. We want a blank line. */
301 char *msg = *(info_internal_help_text[i])
302 ? _(info_internal_help_text[i])
303 : info_internal_help_text[i];
304 char *key = info_help_keys_text[i][vi_keys_p];
305
306 /* If we have only one window (because the window size was too
307 small to split it), CTRL-x 0 doesn't work to `quit' help. */
308 if (STREQ (key, "CTRL-x 0") && help_is_only_window_p)
309 key = "l";
310
311 printf_to_message_buffer (msg, key, NULL, NULL);
312#endif /* !INFOKEY */
313 }
314
315 printf_to_message_buffer ("---------------------\n\n", NULL, NULL, NULL);
316 printf_to_message_buffer ((char *) _("The current search path is:\n"),
317 NULL, NULL, NULL);
318 printf_to_message_buffer (" %s\n", infopath, NULL, NULL);
319 printf_to_message_buffer ("---------------------\n\n", NULL, NULL, NULL);
320 printf_to_message_buffer ((char *) _("Commands available in Info windows:\n\n"),
321 NULL, NULL, NULL);
322 dump_map_to_message_buffer ("", info_keymap);
323 printf_to_message_buffer ("---------------------\n\n", NULL, NULL, NULL);
324 printf_to_message_buffer ((char *) _("Commands available in the echo area:\n\n"),
325 NULL, NULL, NULL);
326 dump_map_to_message_buffer ("", echo_area_keymap);
327
328#if defined (NAMED_FUNCTIONS)
329 /* Get a list of commands which have no keystroke equivs. */
330 exec_keys = where_is (info_keymap, InfoCmd(info_execute_command));
331 if (exec_keys)
332 exec_keys = xstrdup (exec_keys);
333 for (i = 0; function_doc_array[i].func; i++)
334 {
335 InfoCommand *cmd = DocInfoCmd(&function_doc_array[i]);
336
337 if (InfoFunction(cmd) != (VFunction *) info_do_lowercase_version
338 && !where_is_internal (info_keymap, cmd)
339 && !where_is_internal (echo_area_keymap, cmd))
340 {
341 if (!printed_one_mx)
342 {
343 printf_to_message_buffer ("---------------------\n\n",
344 NULL, NULL, NULL);
345 if (exec_keys && exec_keys[0])
346 printf_to_message_buffer
347 ((char *) _("The following commands can only be invoked via %s:\n\n"),
348 exec_keys, NULL, NULL);
349 else
350 printf_to_message_buffer
351 ((char *) _("The following commands cannot be invoked at all:\n\n"),
352 NULL, NULL, NULL);
353 printed_one_mx = 1;
354 }
355
356 printf_to_message_buffer
357 ("%s %s\n %s\n",
358 exec_keys,
359 function_doc_array[i].func_name,
360 replace_in_documentation (strlen (function_doc_array[i].doc)
361 ? (char *) _(function_doc_array[i].doc) : "", 0)
362 );
363
364 }
365 }
366
367 if (printed_one_mx)
368 printf_to_message_buffer ("\n", NULL, NULL, NULL);
369
370 maybe_free (exec_keys);
371#endif /* NAMED_FUNCTIONS */
372
373 printf_to_message_buffer
374 ("%s", replace_in_documentation
375 ((char *) _("--- Use `\\[history-node]' or `\\[kill-node]' to exit ---\n"), 0),
376 NULL, NULL);
377 node = message_buffer_to_node ();
378 internal_info_help_node_contents = node->contents;
379 }
380 else
381 {
382 /* We already had the right contents, so simply use them. */
383 node = build_message_node ("", 0, 0);
384 free (node->contents);
385 node->contents = contents;
386 node->nodelen = 1 + strlen (contents);
387 }
388
389 internal_info_help_node = node;
390
391 /* Do not GC this node's contents. It never changes, and we never need
392 to delete it once it is made. If you change some things (such as
393 placing information about dynamic variables in the help text) then
394 you will need to allow the contents to be gc'd, and you will have to
395 arrange to always regenerate the help node. */
396#if defined (HELP_NODE_GETS_REGENERATED)
397 add_gcable_pointer (internal_info_help_node->contents);
398#endif
399
400 name_internal_node (internal_info_help_node, info_help_nodename);
401
402 /* Even though this is an internal node, we don't want the window
403 system to treat it specially. So we turn off the internalness
404 of it here. */
405 internal_info_help_node->flags &= ~N_IsInternal;
406}
407
408/* Return a window which is the window showing help in this Info. */
409
410/* If the eligible window's height is >= this, split it to make the help
411 window. Otherwise display the help window in the current window. */
412#define HELP_SPLIT_SIZE 24
413
414static WINDOW *
415info_find_or_create_help_window (void)
416{
417 int help_is_only_window_p;
418 WINDOW *eligible = NULL;
419 WINDOW *help_window = get_window_of_node (internal_info_help_node);
420
421 /* If we couldn't find the help window, then make it. */
422 if (!help_window)
423 {
424 WINDOW *window;
425 int max = 0;
426
427 for (window = windows; window; window = window->next)
428 {
429 if (window->height > max)
430 {
431 max = window->height;
432 eligible = window;
433 }
434 }
435
436 if (!eligible)
437 return NULL;
438 }
439#ifndef HELP_NODE_GETS_REGENERATED
440 else
441 /* help window is static, just return it. */
442 return help_window;
443#endif /* not HELP_NODE_GETS_REGENERATED */
444
445 /* Make sure that we have a node containing the help text. The
446 argument is false if help will be the only window (so l must be used
447 to quit help), true if help will be one of several visible windows
448 (so CTRL-x 0 must be used to quit help). */
449 help_is_only_window_p = ((help_window && !windows->next)
450 || (!help_window && eligible->height < HELP_SPLIT_SIZE));
451 create_internal_info_help_node (help_is_only_window_p);
452
453 /* Either use the existing window to display the help node, or create
454 a new window if there was no existing help window. */
455 if (!help_window)
456 { /* Split the largest window into 2 windows, and show the help text
457 in that window. */
458 if (eligible->height >= HELP_SPLIT_SIZE)
459 {
460 active_window = eligible;
461 help_window = window_make_window (internal_info_help_node);
462 }
463 else
464 {
465 set_remembered_pagetop_and_point (active_window);
466 window_set_node_of_window (active_window, internal_info_help_node);
467 help_window = active_window;
468 }
469 }
470 else
471 { /* Case where help node always gets regenerated, and we have an
472 existing window in which to place the node. */
473 if (active_window != help_window)
474 {
475 set_remembered_pagetop_and_point (active_window);
476 active_window = help_window;
477 }
478 window_set_node_of_window (active_window, internal_info_help_node);
479 }
480 remember_window_and_node (help_window, help_window->node);
481 return help_window;
482}
483
484/* Create or move to the help window. */
485DECLARE_INFO_COMMAND (info_get_help_window, _("Display help message"))
486{
487 WINDOW *help_window;
488
489 help_window = info_find_or_create_help_window ();
490 if (help_window)
491 {
492 active_window = help_window;
493 active_window->flags |= W_UpdateWindow;
494 }
495 else
496 {
497 info_error ((char *) msg_cant_make_help, NULL, NULL);
498 }
499}
500
501/* Show the Info help node. This means that the "info" file is installed
502 where it can easily be found on your system. */
503DECLARE_INFO_COMMAND (info_get_info_help_node, _("Visit Info node `(info)Help'"))
504{
505 NODE *node;
506 char *nodename;
507
508 /* If there is a window on the screen showing the node "(info)Help" or
509 the node "(info)Help-Small-Screen", simply select that window. */
510 {
511 WINDOW *win;
512
513 for (win = windows; win; win = win->next)
514 {
515 if (win->node && win->node->filename &&
516 (strcasecmp
517 (filename_non_directory (win->node->filename), "info") == 0) &&
518 ((strcmp (win->node->nodename, "Help") == 0) ||
519 (strcmp (win->node->nodename, "Help-Small-Screen") == 0)))
520 {
521 active_window = win;
522 return;
523 }
524 }
525 }
526
527 /* If the current window is small, show the small screen help. */
528 if (active_window->height < 24)
529 nodename = "Help-Small-Screen";
530 else
531 nodename = "Help";
532
533 /* Try to get the info file for Info. */
534 node = info_get_node ("Info", nodename);
535
536 if (!node)
537 {
538 if (info_recent_file_error)
539 info_error (info_recent_file_error, NULL, NULL);
540 else
541 info_error ((char *) msg_cant_file_node, "Info", nodename);
542 }
543 else
544 {
545 /* If the current window is very large (greater than 45 lines),
546 then split it and show the help node in another window.
547 Otherwise, use the current window. */
548
549 if (active_window->height > 45)
550 active_window = window_make_window (node);
551 else
552 {
553 set_remembered_pagetop_and_point (active_window);
554 window_set_node_of_window (active_window, node);
555 }
556
557 remember_window_and_node (active_window, node);
558 }
559}
560
561/* **************************************************************** */
562/* */
563/* Groveling Info Keymaps and Docs */
564/* */
565/* **************************************************************** */
566
567/* Return the documentation associated with the Info command FUNCTION. */
568char *
569function_documentation (InfoCommand *cmd)
570{
571 char *doc;
572
573#if defined (INFOKEY)
574
575 doc = cmd->doc;
576
577#else /* !INFOKEY */
578
579 register int i;
580
581 for (i = 0; function_doc_array[i].func; i++)
582 if (InfoFunction(cmd) == function_doc_array[i].func)
583 break;
584
585 doc = function_doc_array[i].func ? function_doc_array[i].doc : "";
586
587#endif /* !INFOKEY */
588
589 return replace_in_documentation ((strlen (doc) == 0) ? doc : (char *) _(doc), 0);
590}
591
592#if defined (NAMED_FUNCTIONS)
593/* Return the user-visible name of the function associated with the
594 Info command FUNCTION. */
595char *
596function_name (InfoCommand *cmd)
597{
598#if defined (INFOKEY)
599
600 return cmd->func_name;
601
602#else /* !INFOKEY */
603
604 register int i;
605
606 for (i = 0; function_doc_array[i].func; i++)
607 if (InfoFunction(cmd) == function_doc_array[i].func)
608 break;
609
610 return (function_doc_array[i].func_name);
611
612#endif /* !INFOKEY */
613}
614
615/* Return a pointer to the info command for function NAME. */
616InfoCommand *
617named_function (char *name)
618{
619 register int i;
620
621 for (i = 0; function_doc_array[i].func; i++)
622 if (strcmp (function_doc_array[i].func_name, name) == 0)
623 break;
624
625 return (DocInfoCmd(&function_doc_array[i]));
626}
627#endif /* NAMED_FUNCTIONS */
628
629/* Return the documentation associated with KEY in MAP. */
630char *
631key_documentation (char key, Keymap map)
632{
633 InfoCommand *function = map[key].function;
634
635 if (function)
636 return (function_documentation (function));
637 else
638 return ((char *)NULL);
639}
640
641DECLARE_INFO_COMMAND (describe_key, _("Print documentation for KEY"))
642{
643 char keys[50];
644 unsigned char keystroke;
645 char *k = keys;
646 Keymap map;
647
648 *k = '\0';
649 map = window->keymap;
650
651 for (;;)
652 {
653 message_in_echo_area ((char *) _("Describe key: %s"),
654 pretty_keyseq (keys), NULL);
655 keystroke = info_get_input_char ();
656 unmessage_in_echo_area ();
657
658#if !defined (INFOKEY)
659 if (Meta_p (keystroke))
660 {
661 if (map[ESC].type != ISKMAP)
662 {
663 window_message_in_echo_area
664 (_("ESC %s is undefined."), pretty_keyname (UnMeta (keystroke)));
665 return;
666 }
667
668 *k++ = '\e';
669 keystroke = UnMeta (keystroke);
670 map = (Keymap)map[ESC].function;
671 }
672#endif /* !INFOKEY */
673
674 /* Add the KEYSTROKE to our list. */
675 *k++ = keystroke;
676 *k = '\0';
677
678 if (map[keystroke].function == (InfoCommand *)NULL)
679 {
680 message_in_echo_area ((char *) _("%s is undefined."),
681 pretty_keyseq (keys), NULL);
682 return;
683 }
684 else if (map[keystroke].type == ISKMAP)
685 {
686 map = (Keymap)map[keystroke].function;
687 continue;
688 }
689 else
690 {
691 char *keyname, *message, *fundoc, *funname = "";
692
693#if defined (INFOKEY)
694 /* If the key is bound to do-lowercase-version, but its
695 lower-case variant is undefined, say that this key is
696 also undefined. This is especially important for unbound
697 edit keys that emit an escape sequence: it's terribly
698 confusing to see a message "Home (do-lowercase-version)"
699 or some such when Home is unbound. */
700 if (InfoFunction(map[keystroke].function)
701 == (VFunction *) info_do_lowercase_version)
702 {
703 unsigned char lowerkey = Meta_p(keystroke)
704 ? Meta (tolower (UnMeta (keystroke)))
705 : tolower (keystroke);
706
707 if (map[lowerkey].function == (InfoCommand *)NULL)
708 {
709 message_in_echo_area ((char *) _("%s is undefined."),
710 pretty_keyseq (keys), NULL);
711 return;
712 }
713 }
714#endif
715
716 keyname = pretty_keyseq (keys);
717
718#if defined (NAMED_FUNCTIONS)
719 funname = function_name (map[keystroke].function);
720#endif /* NAMED_FUNCTIONS */
721
722 fundoc = function_documentation (map[keystroke].function);
723
724 message = (char *)xmalloc
725 (10 + strlen (keyname) + strlen (fundoc) + strlen (funname));
726
727#if defined (NAMED_FUNCTIONS)
728 sprintf (message, "%s (%s): %s.", keyname, funname, fundoc);
729#else
730 sprintf (message, _("%s is defined to %s."), keyname, fundoc);
731#endif /* !NAMED_FUNCTIONS */
732
733 window_message_in_echo_area ("%s", message, NULL);
734 free (message);
735 break;
736 }
737 }
738}
739
740/* Return the pretty printable name of a single character. */
741char *
742pretty_keyname (unsigned char key)
743{
744 static char rep_buffer[30];
745 char *rep;
746
747 if (Meta_p (key))
748 {
749 char temp[20];
750
751 rep = pretty_keyname (UnMeta (key));
752
753#if defined (INFOKEY)
754 sprintf (temp, "M-%s", rep);
755#else /* !INFOKEY */
756 sprintf (temp, "ESC %s", rep);
757#endif /* !INFOKEY */
758 strcpy (rep_buffer, temp);
759 rep = rep_buffer;
760 }
761 else if (Control_p (key))
762 {
763 switch (key)
764 {
765 case '\n': rep = "LFD"; break;
766 case '\t': rep = "TAB"; break;
767 case '\r': rep = "RET"; break;
768 case ESC: rep = "ESC"; break;
769
770 default:
771 sprintf (rep_buffer, "C-%c", UnControl (key));
772 rep = rep_buffer;
773 }
774 }
775 else
776 {
777 switch (key)
778 {
779 case ' ': rep = "SPC"; break;
780 case DEL: rep = "DEL"; break;
781 default:
782 rep_buffer[0] = key;
783 rep_buffer[1] = '\0';
784 rep = rep_buffer;
785 }
786 }
787 return (rep);
788}
789
790/* Return the pretty printable string which represents KEYSEQ. */
791
792static void pretty_keyseq_internal (char *keyseq, char *rep);
793
794char *
795pretty_keyseq (char *keyseq)
796{
797 static char keyseq_rep[200];
798
799 keyseq_rep[0] = '\0';
800 if (*keyseq)
801 pretty_keyseq_internal (keyseq, keyseq_rep);
802 return (keyseq_rep);
803}
804
805static void
806pretty_keyseq_internal (char *keyseq, char *rep)
807{
808 if (term_kP && strncmp(keyseq, term_kP, strlen(term_kP)) == 0)
809 {
810 strcpy(rep, "PgUp");
811 keyseq += strlen(term_kP);
812 }
813 else if (term_kN && strncmp(keyseq, term_kN, strlen(term_kN)) == 0)
814 {
815 strcpy(rep, "PgDn");
816 keyseq += strlen(term_kN);
817 }
818#if defined(INFOKEY)
819 else if (term_kh && strncmp(keyseq, term_kh, strlen(term_kh)) == 0)
820 {
821 strcpy(rep, "Home");
822 keyseq += strlen(term_kh);
823 }
824 else if (term_ke && strncmp(keyseq, term_ke, strlen(term_ke)) == 0)
825 {
826 strcpy(rep, "End");
827 keyseq += strlen(term_ke);
828 }
829 else if (term_ki && strncmp(keyseq, term_ki, strlen(term_ki)) == 0)
830 {
831 strcpy(rep, "INS");
832 keyseq += strlen(term_ki);
833 }
834 else if (term_kx && strncmp(keyseq, term_kx, strlen(term_kx)) == 0)
835 {
836 strcpy(rep, "DEL");
837 keyseq += strlen(term_kx);
838 }
839#endif /* INFOKEY */
840 else if (term_ku && strncmp(keyseq, term_ku, strlen(term_ku)) == 0)
841 {
842 strcpy(rep, "Up");
843 keyseq += strlen(term_ku);
844 }
845 else if (term_kd && strncmp(keyseq, term_kd, strlen(term_kd)) == 0)
846 {
847 strcpy(rep, "Down");
848 keyseq += strlen(term_kd);
849 }
850 else if (term_kl && strncmp(keyseq, term_kl, strlen(term_kl)) == 0)
851 {
852 strcpy(rep, "Left");
853 keyseq += strlen(term_kl);
854 }
855 else if (term_kr && strncmp(keyseq, term_kr, strlen(term_kr)) == 0)
856 {
857 strcpy(rep, "Right");
858 keyseq += strlen(term_kr);
859 }
860 else
861 {
862 strcpy (rep, pretty_keyname (keyseq[0]));
863 keyseq++;
864 }
865 if (*keyseq)
866 {
867 strcat (rep, " ");
868 pretty_keyseq_internal (keyseq, rep + strlen(rep));
869 }
870}
871
872/* Return a pointer to the last character in s that is found in f. */
873static char *
874strrpbrk (const char *s, const char *f)
875{
876 register const char *e = s + strlen(s);
877 register const char *t;
878
879 while (e-- != s)
880 {
881 for (t = f; *t; t++)
882 if (*e == *t)
883 return (char *)e;
884 }
885 return NULL;
886}
887
888/* Replace the names of functions with the key that invokes them. */
889char *
890replace_in_documentation (char *string, int help_is_only_window_p)
891{
892 unsigned reslen = strlen (string);
893 register int i, start, next;
894 static char *result = (char *)NULL;
895
896 maybe_free (result);
897 result = (char *)xmalloc (1 + reslen);
898
899 i = next = start = 0;
Value stored to 'i' is never read
900
901 /* Skip to the beginning of a replaceable function. */
902 for (i = start; string[i]; i++)
903 {
904 int j = i + 1;
905
906 /* Is this the start of a replaceable function name? */
907 if (string[i] == '\\')
908 {
909 char *fmt = NULL;
910 unsigned min = 0;
911 unsigned max = 0;
912
913 if(string[j] == '%')
914 {
915 if (string[++j] == '-')
916 j++;
917 if (isdigit(string[j]))
918 {
919 min = atoi(string + j);
920 while (isdigit(string[j]))
921 j++;
922 if (string[j] == '.' && isdigit(string[j + 1]))
923 {
924 j += 1;
925 max = atoi(string + j);
926 while (isdigit(string[j]))
927 j++;
928 }
929 fmt = (char *)xmalloc (j - i + 2);
930 strncpy (fmt, string + i + 1, j - i);
931 fmt[j - i - 1] = 's';
932 fmt[j - i] = '\0';
933 }
934 else
935 j = i + 1;
936 }
937 if (string[j] == '[')
938 {
939 unsigned arg = 0;
940 char *argstr = NULL;
941 char *rep_name, *fun_name, *rep;
942 InfoCommand *command;
943 char *repstr = NULL;
944 unsigned replen;
945
946 /* Copy in the old text. */
947 strncpy (result + next, string + start, i - start);
948 next += (i - start);
949 start = j + 1;
950
951 /* Look for an optional numeric arg. */
952 i = start;
953 if (isdigit(string[i])
954 || (string[i] == '-' && isdigit(string[i + 1])) )
955 {
956 arg = atoi(string + i);
957 if (string[i] == '-')
958 i++;
959 while (isdigit(string[i]))
960 i++;
961 }
962 start = i;
963
964 /* Move to the end of the function name. */
965 for (i = start; string[i] && (string[i] != ']'); i++);
966
967 rep_name = (char *)xmalloc (1 + i - start);
968 strncpy (rep_name, string + start, i - start);
969 rep_name[i - start] = '\0';
970
971 /* If we have only one window (because the window size was too
972 small to split it), we have to quit help by going back one
973 noew in the history list, not deleting the window. */
974 if (strcmp (rep_name, "quit-help") == 0)
975 fun_name = help_is_only_window_p ? "history-node"
976 : "delete-window";
977 else
978 fun_name = rep_name;
979
980 /* Find a key which invokes this function in the info_keymap. */
981 command = named_function (fun_name);
982
983 free (rep_name);
984
985 /* If the internal documentation string fails, there is a
986 serious problem with the associated command's documentation.
987 We croak so that it can be fixed immediately. */
988 if (!command)
989 abort ();
990
991 if (arg)
992 {
993 char *argrep, *p;
994
995 argrep = where_is (info_keymap, InfoCmd(info_add_digit_to_numeric_arg));
996 p = argrep ? strrpbrk (argrep, "0123456789-") : NULL;
997 if (p)
998 {
999 argstr = (char *)xmalloc (p - argrep + 21);
1000 strncpy (argstr, argrep, p - argrep);
1001 sprintf (argstr + (p - argrep), "%d", arg);
1002 }
1003 else
1004 command = NULL;
1005 }
1006 rep = command ? where_is (info_keymap, command) : NULL;
1007 if (!rep)
1008 rep = "N/A";
1009 replen = (argstr ? strlen (argstr) : 0) + strlen (rep) + 1;
1010 repstr = (char *)xmalloc (replen);
1011 repstr[0] = '\0';
1012 if (argstr)
1013 {
1014 strcat(repstr, argstr);
1015 strcat(repstr, " ");
1016 free (argstr);
1017 }
1018 strcat(repstr, rep);
1019
1020 if (fmt)
1021 {
1022 if (replen > max)
1023 replen = max;
1024 if (replen < min)
1025 replen = min;
1026 }
1027 if (next + replen > reslen)
1028 {
1029 reslen = next + replen + 1;
1030 result = (char *)xrealloc (result, reslen + 1);
1031 }
1032
1033 if (fmt)
1034 sprintf (result + next, fmt, repstr);
1035 else
1036 strcpy (result + next, repstr);
1037
1038 next = strlen (result);
1039 free (repstr);
1040
1041 start = i;
1042 if (string[i])
1043 start++;
1044 }
1045
1046 maybe_free (fmt);
1047 }
1048 }
1049 strcpy (result + next, string + start);
1050 return (result);
1051}
1052
1053/* Return a string of characters which could be typed from the keymap
1054 MAP to invoke FUNCTION. */
1055static char *where_is_rep = (char *)NULL;
1056static int where_is_rep_index = 0;
1057static int where_is_rep_size = 0;
1058
1059char *
1060where_is (Keymap map, InfoCommand *cmd)
1061{
1062 char *rep;
1063
1064 if (!where_is_rep_size)
1065 where_is_rep = (char *)xmalloc (where_is_rep_size = 100);
1066 where_is_rep_index = 0;
1067
1068 rep = where_is_internal (map, cmd);
1069
1070 /* If it couldn't be found, return "M-x Foo" (or equivalent). */
1071 if (!rep)
1072 {
1073 char *name;
1074
1075 name = function_name (cmd);
1076 if (!name)
1077 return NULL; /* no such function */
1078
1079 rep = where_is_internal (map, InfoCmd(info_execute_command));
1080 if (!rep)
1081 return ""; /* function exists but can't be got to by user */
1082
1083 sprintf (where_is_rep, "%s %s", rep, name);
1084
1085 rep = where_is_rep;
1086 }
1087 return (rep);
1088}
1089
1090/* Return the printed rep of the keystrokes that invoke FUNCTION,
1091 as found in MAP, or NULL. */
1092static char *
1093where_is_internal (Keymap map, InfoCommand *cmd)
1094{
1095#if defined(INFOKEY)
1096
1097 register FUNCTION_KEYSEQ *k;
1098
1099 for (k = cmd->keys; k; k = k->next)
1100 if (k->map == map)
1101 return pretty_keyseq (k->keyseq);
1102
1103 return NULL;
1104
1105#else /* !INFOKEY */
1106 /* There is a bug in that create_internal_info_help_node calls
1107 where_is_internal without setting where_is_rep_index to zero. This
1108 was found by Mandrake and reported by Thierry Vignaud
1109 <tvignaud@mandrakesoft.com> around April 24, 2002.
1110
1111 I think the best fix is to make where_is_rep_index another
1112 parameter to this recursively-called function, instead of a static
1113 variable. But this [!INFOKEY] branch of the code is not enabled
1114 any more, so let's just skip the whole thing. --karl, 28sep02. */
1115 register int i;
1116
1117 /* If the function is directly invokable in MAP, return the representation
1118 of that keystroke. */
1119 for (i = 0; i < 256; i++)
1120 if ((map[i].type == ISFUNC) && map[i].function == cmd)
1121 {
1122 sprintf (where_is_rep + where_is_rep_index, "%s", pretty_keyname (i));
1123 return (where_is_rep);
1124 }
1125
1126 /* Okay, search subsequent maps for this function. */
1127 for (i = 0; i < 256; i++)
1128 {
1129 if (map[i].type == ISKMAP)
1130 {
1131 int saved_index = where_is_rep_index;
1132 char *rep;
1133
1134 sprintf (where_is_rep + where_is_rep_index, "%s ",
1135 pretty_keyname (i));
1136
1137 where_is_rep_index = strlen (where_is_rep);
1138 rep = where_is_internal ((Keymap)map[i].function, cmd);
1139
1140 if (rep)
1141 return (where_is_rep);
1142
1143 where_is_rep_index = saved_index;
1144 }
1145 }
1146
1147 return NULL;
1148
1149#endif /* INFOKEY */
1150}
1151
1152DECLARE_INFO_COMMAND (info_where_is,
1153 _("Show what to type to execute a given command"))
1154{
1155 char *command_name;
1156
1157 command_name = read_function_name ((char *) _("Where is command: "), window);
1158
1159 if (!command_name)
1160 {
1161 info_abort_key (active_window, count, key);
1162 return;
1163 }
1164
1165 if (*command_name)
1166 {
1167 InfoCommand *command;
1168
1169 command = named_function (command_name);
1170
1171 if (command)
1172 {
1173 char *location;
1174
1175 location = where_is (active_window->keymap, command);
1176
1177 if (!location || !location[0])
1178 {
1179 info_error ((char *) _("`%s' is not on any keys"),
1180 command_name, NULL);
1181 }
1182 else
1183 {
1184 if (strstr (location, function_name (command)))
1185 window_message_in_echo_area
1186 ((char *) _("%s can only be invoked via %s."),
1187 command_name, location);
1188 else
1189 window_message_in_echo_area
1190 ((char *) _("%s can be invoked via %s."),
1191 command_name, location);
1192 }
1193 }
1194 else
1195 info_error ((char *) _("There is no function named `%s'"),
1196 command_name, NULL);
1197 }
1198
1199 free (command_name);
1200}