1 | /* infodoc.c -- functions which build documentation nodes.
|
---|
2 | $Id: infodoc.c,v 1.8 2004/04/11 17:56:45 karl 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_REGENERATED 1
|
---|
29 |
|
---|
30 | /* The name of the node used in the help window. */
|
---|
31 | static char *info_help_nodename = "*Info Help*";
|
---|
32 |
|
---|
33 | /* A node containing printed key bindings and their documentation. */
|
---|
34 | static NODE *internal_info_help_node = (NODE *)NULL;
|
---|
35 |
|
---|
36 | /* A pointer to the contents of the help node. */
|
---|
37 | static char *internal_info_help_node_contents = (char *)NULL;
|
---|
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 |
|
---|
45 | static char *info_internal_help_text[] = {
|
---|
46 | N_("Basic Commands in Info Windows\n\
|
---|
47 | ******************************\n"),
|
---|
48 | "\n",
|
---|
49 | N_("\\%-10[quit-help] Quit this help.\n"),
|
---|
50 | N_("\\%-10[quit] Quit Info altogether.\n"),
|
---|
51 | N_("\\%-10[get-info-help-node] Invoke the Info tutorial.\n"),
|
---|
52 | "\n",
|
---|
53 | N_("Selecting other nodes:\n\
|
---|
54 | ----------------------\n"),
|
---|
55 | 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"),
|
---|
57 | N_("\\%-10[up-node] Move \"up\" from this node.\n"),
|
---|
58 | N_("\\%-10[menu-item] Pick menu item specified by name.\n\
|
---|
59 | 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"),
|
---|
61 | 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"),
|
---|
63 | 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"),
|
---|
65 | 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"),
|
---|
67 | "\n",
|
---|
68 | N_("Moving within a node:\n\
|
---|
69 | ---------------------\n"),
|
---|
70 | 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"),
|
---|
72 | N_("\\%-10[next-line] Scroll forward 1 line.\n"),
|
---|
73 | N_("\\%-10[prev-line] Scroll backward 1 line.\n"),
|
---|
74 | N_("\\%-10[scroll-forward] Scroll forward a page.\n"),
|
---|
75 | N_("\\%-10[scroll-backward] Scroll backward a page.\n"),
|
---|
76 | "\n",
|
---|
77 | N_("Other commands:\n\
|
---|
78 | ---------------\n"),
|
---|
79 | 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"),
|
---|
81 | N_("\\%-10[index-search] Search for a specified string in the index entries of this Info\n\
|
---|
82 | file, and select the node referenced by the first entry found.\n"),
|
---|
83 | N_("\\%-10[goto-node] Move to node specified by name.\n\
|
---|
84 | You may include a filename as well, as in (FILENAME)NODENAME.\n"),
|
---|
85 | N_("\\%-10[search] Search forward for a specified string\n\
|
---|
86 | and select the node in which the next occurrence is found.\n"),
|
---|
87 | N_("\\%-10[search-backward] Search backward for a specified string\n\
|
---|
88 | and select the node in which the previous occurrence is found.\n"),
|
---|
89 | NULL
|
---|
90 | };
|
---|
91 |
|
---|
92 | #else /* !INFOKEY */
|
---|
93 |
|
---|
94 | static char *info_internal_help_text[] = {
|
---|
95 | N_("Basic Commands in Info Windows\n\
|
---|
96 | ******************************\n"),
|
---|
97 | "\n",
|
---|
98 | N_(" %-10s Quit this help.\n"),
|
---|
99 | N_(" %-10s Quit Info altogether.\n"),
|
---|
100 | 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 |
|
---|
140 | static 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 |
|
---|
188 | static char *where_is_internal (Keymap map, InfoCommand *cmd);
|
---|
189 |
|
---|
190 | void
|
---|
191 | dump_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 |
|
---|
273 | static void
|
---|
274 | create_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 |
|
---|
414 | static WINDOW *
|
---|
415 | info_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. */
|
---|
485 | DECLARE_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. */
|
---|
503 | DECLARE_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 | /* */
|
---|
564 | /* Groveling Info Keymaps and Docs */
|
---|
565 | /* */
|
---|
566 | /* **************************************************************** */
|
---|
567 |
|
---|
568 | /* Return the documentation associated with the Info command FUNCTION. */
|
---|
569 | char *
|
---|
570 | function_documentation (InfoCommand *cmd)
|
---|
571 | {
|
---|
572 | char *doc;
|
---|
573 |
|
---|
574 | #if defined (INFOKEY)
|
---|
575 |
|
---|
576 | doc = cmd->doc;
|
---|
577 |
|
---|
578 | #else /* !INFOKEY */
|
---|
579 |
|
---|
580 | register int i;
|
---|
581 |
|
---|
582 | for (i = 0; function_doc_array[i].func; i++)
|
---|
583 | if (InfoFunction(cmd) == function_doc_array[i].func)
|
---|
584 | break;
|
---|
585 |
|
---|
586 | doc = function_doc_array[i].func ? function_doc_array[i].doc : "";
|
---|
587 |
|
---|
588 | #endif /* !INFOKEY */
|
---|
589 |
|
---|
590 | return replace_in_documentation ((strlen (doc) == 0) ? doc : (char *) _(doc), 0);
|
---|
591 | }
|
---|
592 |
|
---|
593 | #if defined (NAMED_FUNCTIONS)
|
---|
594 | /* Return the user-visible name of the function associated with the
|
---|
595 | Info command FUNCTION. */
|
---|
596 | char *
|
---|
597 | function_name (InfoCommand *cmd)
|
---|
598 | {
|
---|
599 | #if defined (INFOKEY)
|
---|
600 |
|
---|
601 | return cmd->func_name;
|
---|
602 |
|
---|
603 | #else /* !INFOKEY */
|
---|
604 |
|
---|
605 | register int i;
|
---|
606 |
|
---|
607 | for (i = 0; function_doc_array[i].func; i++)
|
---|
608 | if (InfoFunction(cmd) == function_doc_array[i].func)
|
---|
609 | break;
|
---|
610 |
|
---|
611 | return (function_doc_array[i].func_name);
|
---|
612 |
|
---|
613 | #endif /* !INFOKEY */
|
---|
614 | }
|
---|
615 |
|
---|
616 | /* Return a pointer to the info command for function NAME. */
|
---|
617 | InfoCommand *
|
---|
618 | named_function (char *name)
|
---|
619 | {
|
---|
620 | register int i;
|
---|
621 |
|
---|
622 | for (i = 0; function_doc_array[i].func; i++)
|
---|
623 | if (strcmp (function_doc_array[i].func_name, name) == 0)
|
---|
624 | break;
|
---|
625 |
|
---|
626 | return (DocInfoCmd(&function_doc_array[i]));
|
---|
627 | }
|
---|
628 | #endif /* NAMED_FUNCTIONS */
|
---|
629 |
|
---|
630 | /* Return the documentation associated with KEY in MAP. */
|
---|
631 | char *
|
---|
632 | key_documentation (char key, Keymap map)
|
---|
633 | {
|
---|
634 | InfoCommand *function = map[key].function;
|
---|
635 |
|
---|
636 | if (function)
|
---|
637 | return (function_documentation (function));
|
---|
638 | else
|
---|
639 | return ((char *)NULL);
|
---|
640 | }
|
---|
641 |
|
---|
642 | DECLARE_INFO_COMMAND (describe_key, _("Print documentation for KEY"))
|
---|
643 | {
|
---|
644 | char keys[50];
|
---|
645 | unsigned char keystroke;
|
---|
646 | char *k = keys;
|
---|
647 | Keymap map;
|
---|
648 |
|
---|
649 | *k = '\0';
|
---|
650 | map = window->keymap;
|
---|
651 |
|
---|
652 | for (;;)
|
---|
653 | {
|
---|
654 | message_in_echo_area ((char *) _("Describe key: %s"),
|
---|
655 | pretty_keyseq (keys), NULL);
|
---|
656 | keystroke = info_get_input_char ();
|
---|
657 | unmessage_in_echo_area ();
|
---|
658 |
|
---|
659 | #if !defined (INFOKEY)
|
---|
660 | if (Meta_p (keystroke))
|
---|
661 | {
|
---|
662 | if (map[ESC].type != ISKMAP)
|
---|
663 | {
|
---|
664 | window_message_in_echo_area
|
---|
665 | (_("ESC %s is undefined."), pretty_keyname (UnMeta (keystroke)));
|
---|
666 | return;
|
---|
667 | }
|
---|
668 |
|
---|
669 | *k++ = '\e';
|
---|
670 | keystroke = UnMeta (keystroke);
|
---|
671 | map = (Keymap)map[ESC].function;
|
---|
672 | }
|
---|
673 | #endif /* !INFOKEY */
|
---|
674 |
|
---|
675 | /* Add the KEYSTROKE to our list. */
|
---|
676 | *k++ = keystroke;
|
---|
677 | *k = '\0';
|
---|
678 |
|
---|
679 | if (map[keystroke].function == (InfoCommand *)NULL)
|
---|
680 | {
|
---|
681 | message_in_echo_area ((char *) _("%s is undefined."),
|
---|
682 | pretty_keyseq (keys), NULL);
|
---|
683 | return;
|
---|
684 | }
|
---|
685 | else if (map[keystroke].type == ISKMAP)
|
---|
686 | {
|
---|
687 | map = (Keymap)map[keystroke].function;
|
---|
688 | continue;
|
---|
689 | }
|
---|
690 | else
|
---|
691 | {
|
---|
692 | char *keyname, *message, *fundoc, *funname = "";
|
---|
693 |
|
---|
694 | #if defined (INFOKEY)
|
---|
695 | /* If the key is bound to do-lowercase-version, but its
|
---|
696 | lower-case variant is undefined, say that this key is
|
---|
697 | also undefined. This is especially important for unbound
|
---|
698 | edit keys that emit an escape sequence: it's terribly
|
---|
699 | confusing to see a message "Home (do-lowercase-version)"
|
---|
700 | or some such when Home is unbound. */
|
---|
701 | if (InfoFunction(map[keystroke].function)
|
---|
702 | == (VFunction *) info_do_lowercase_version)
|
---|
703 | {
|
---|
704 | unsigned char lowerkey = Meta_p(keystroke)
|
---|
705 | ? Meta (tolower (UnMeta (keystroke)))
|
---|
706 | : tolower (keystroke);
|
---|
707 |
|
---|
708 | if (map[lowerkey].function == (InfoCommand *)NULL)
|
---|
709 | {
|
---|
710 | message_in_echo_area ((char *) _("%s is undefined."),
|
---|
711 | pretty_keyseq (keys), NULL);
|
---|
712 | return;
|
---|
713 | }
|
---|
714 | }
|
---|
715 | #endif
|
---|
716 |
|
---|
717 | keyname = pretty_keyseq (keys);
|
---|
718 |
|
---|
719 | #if defined (NAMED_FUNCTIONS)
|
---|
720 | funname = function_name (map[keystroke].function);
|
---|
721 | #endif /* NAMED_FUNCTIONS */
|
---|
722 |
|
---|
723 | fundoc = function_documentation (map[keystroke].function);
|
---|
724 |
|
---|
725 | message = (char *)xmalloc
|
---|
726 | (10 + strlen (keyname) + strlen (fundoc) + strlen (funname));
|
---|
727 |
|
---|
728 | #if defined (NAMED_FUNCTIONS)
|
---|
729 | sprintf (message, "%s (%s): %s.", keyname, funname, fundoc);
|
---|
730 | #else
|
---|
731 | sprintf (message, _("%s is defined to %s."), keyname, fundoc);
|
---|
732 | #endif /* !NAMED_FUNCTIONS */
|
---|
733 |
|
---|
734 | window_message_in_echo_area ("%s", message, NULL);
|
---|
735 | free (message);
|
---|
736 | break;
|
---|
737 | }
|
---|
738 | }
|
---|
739 | }
|
---|
740 |
|
---|
741 | /* Return the pretty printable name of a single character. */
|
---|
742 | char *
|
---|
743 | pretty_keyname (unsigned char key)
|
---|
744 | {
|
---|
745 | static char rep_buffer[30];
|
---|
746 | char *rep;
|
---|
747 |
|
---|
748 | if (Meta_p (key))
|
---|
749 | {
|
---|
750 | char temp[20];
|
---|
751 |
|
---|
752 | rep = pretty_keyname (UnMeta (key));
|
---|
753 |
|
---|
754 | #if defined (INFOKEY)
|
---|
755 | sprintf (temp, "M-%s", rep);
|
---|
756 | #else /* !INFOKEY */
|
---|
757 | sprintf (temp, "ESC %s", rep);
|
---|
758 | #endif /* !INFOKEY */
|
---|
759 | strcpy (rep_buffer, temp);
|
---|
760 | rep = rep_buffer;
|
---|
761 | }
|
---|
762 | else if (Control_p (key))
|
---|
763 | {
|
---|
764 | switch (key)
|
---|
765 | {
|
---|
766 | case '\n': rep = "LFD"; break;
|
---|
767 | case '\t': rep = "TAB"; break;
|
---|
768 | case '\r': rep = "RET"; break;
|
---|
769 | case ESC: rep = "ESC"; break;
|
---|
770 |
|
---|
771 | default:
|
---|
772 | sprintf (rep_buffer, "C-%c", UnControl (key));
|
---|
773 | rep = rep_buffer;
|
---|
774 | }
|
---|
775 | }
|
---|
776 | else
|
---|
777 | {
|
---|
778 | switch (key)
|
---|
779 | {
|
---|
780 | case ' ': rep = "SPC"; break;
|
---|
781 | case DEL: rep = "DEL"; break;
|
---|
782 | default:
|
---|
783 | rep_buffer[0] = key;
|
---|
784 | rep_buffer[1] = '\0';
|
---|
785 | rep = rep_buffer;
|
---|
786 | }
|
---|
787 | }
|
---|
788 | return (rep);
|
---|
789 | }
|
---|
790 |
|
---|
791 | /* Return the pretty printable string which represents KEYSEQ. */
|
---|
792 |
|
---|
793 | static void pretty_keyseq_internal (char *keyseq, char *rep);
|
---|
794 |
|
---|
795 | char *
|
---|
796 | pretty_keyseq (char *keyseq)
|
---|
797 | {
|
---|
798 | static char keyseq_rep[200];
|
---|
799 |
|
---|
800 | keyseq_rep[0] = '\0';
|
---|
801 | if (*keyseq)
|
---|
802 | pretty_keyseq_internal (keyseq, keyseq_rep);
|
---|
803 | return (keyseq_rep);
|
---|
804 | }
|
---|
805 |
|
---|
806 | static void
|
---|
807 | pretty_keyseq_internal (char *keyseq, char *rep)
|
---|
808 | {
|
---|
809 | if (term_kP && strncmp(keyseq, term_kP, strlen(term_kP)) == 0)
|
---|
810 | {
|
---|
811 | strcpy(rep, "PgUp");
|
---|
812 | keyseq += strlen(term_kP);
|
---|
813 | }
|
---|
814 | else if (term_kN && strncmp(keyseq, term_kN, strlen(term_kN)) == 0)
|
---|
815 | {
|
---|
816 | strcpy(rep, "PgDn");
|
---|
817 | keyseq += strlen(term_kN);
|
---|
818 | }
|
---|
819 | #if defined(INFOKEY)
|
---|
820 | else if (term_kh && strncmp(keyseq, term_kh, strlen(term_kh)) == 0)
|
---|
821 | {
|
---|
822 | strcpy(rep, "Home");
|
---|
823 | keyseq += strlen(term_kh);
|
---|
824 | }
|
---|
825 | else if (term_ke && strncmp(keyseq, term_ke, strlen(term_ke)) == 0)
|
---|
826 | {
|
---|
827 | strcpy(rep, "End");
|
---|
828 | keyseq += strlen(term_ke);
|
---|
829 | }
|
---|
830 | else if (term_ki && strncmp(keyseq, term_ki, strlen(term_ki)) == 0)
|
---|
831 | {
|
---|
832 | strcpy(rep, "INS");
|
---|
833 | keyseq += strlen(term_ki);
|
---|
834 | }
|
---|
835 | else if (term_kx && strncmp(keyseq, term_kx, strlen(term_kx)) == 0)
|
---|
836 | {
|
---|
837 | strcpy(rep, "DEL");
|
---|
838 | keyseq += strlen(term_kx);
|
---|
839 | }
|
---|
840 | #endif /* INFOKEY */
|
---|
841 | else if (term_ku && strncmp(keyseq, term_ku, strlen(term_ku)) == 0)
|
---|
842 | {
|
---|
843 | strcpy(rep, "Up");
|
---|
844 | keyseq += strlen(term_ku);
|
---|
845 | }
|
---|
846 | else if (term_kd && strncmp(keyseq, term_kd, strlen(term_kd)) == 0)
|
---|
847 | {
|
---|
848 | strcpy(rep, "Down");
|
---|
849 | keyseq += strlen(term_kd);
|
---|
850 | }
|
---|
851 | else if (term_kl && strncmp(keyseq, term_kl, strlen(term_kl)) == 0)
|
---|
852 | {
|
---|
853 | strcpy(rep, "Left");
|
---|
854 | keyseq += strlen(term_kl);
|
---|
855 | }
|
---|
856 | else if (term_kr && strncmp(keyseq, term_kr, strlen(term_kr)) == 0)
|
---|
857 | {
|
---|
858 | strcpy(rep, "Right");
|
---|
859 | keyseq += strlen(term_kr);
|
---|
860 | }
|
---|
861 | else
|
---|
862 | {
|
---|
863 | strcpy (rep, pretty_keyname (keyseq[0]));
|
---|
864 | keyseq++;
|
---|
865 | }
|
---|
866 | if (*keyseq)
|
---|
867 | {
|
---|
868 | strcat (rep, " ");
|
---|
869 | pretty_keyseq_internal (keyseq, rep + strlen(rep));
|
---|
870 | }
|
---|
871 | }
|
---|
872 |
|
---|
873 | /* Return a pointer to the last character in s that is found in f. */
|
---|
874 | static char *
|
---|
875 | strrpbrk (const char *s, const char *f)
|
---|
876 | {
|
---|
877 | register const char *e = s + strlen(s);
|
---|
878 | register const char *t;
|
---|
879 |
|
---|
880 | while (e-- != s)
|
---|
881 | {
|
---|
882 | for (t = f; *t; t++)
|
---|
883 | if (*e == *t)
|
---|
884 | return (char *)e;
|
---|
885 | }
|
---|
886 | return NULL;
|
---|
887 | }
|
---|
888 |
|
---|
889 | /* Replace the names of functions with the key that invokes them. */
|
---|
890 | char *
|
---|
891 | replace_in_documentation (char *string, int help_is_only_window_p)
|
---|
892 | {
|
---|
893 | unsigned reslen = strlen (string);
|
---|
894 | register int i, start, next;
|
---|
895 | static char *result = (char *)NULL;
|
---|
896 |
|
---|
897 | maybe_free (result);
|
---|
898 | result = (char *)xmalloc (1 + reslen);
|
---|
899 |
|
---|
900 | i = next = start = 0;
|
---|
901 |
|
---|
902 | /* Skip to the beginning of a replaceable function. */
|
---|
903 | for (i = start; string[i]; i++)
|
---|
904 | {
|
---|
905 | int j = i + 1;
|
---|
906 |
|
---|
907 | /* Is this the start of a replaceable function name? */
|
---|
908 | if (string[i] == '\\')
|
---|
909 | {
|
---|
910 | char *fmt = NULL;
|
---|
911 | unsigned min = 0;
|
---|
912 | unsigned max = 0;
|
---|
913 |
|
---|
914 | if(string[j] == '%')
|
---|
915 | {
|
---|
916 | if (string[++j] == '-')
|
---|
917 | j++;
|
---|
918 | if (isdigit(string[j]))
|
---|
919 | {
|
---|
920 | min = atoi(string + j);
|
---|
921 | while (isdigit(string[j]))
|
---|
922 | j++;
|
---|
923 | if (string[j] == '.' && isdigit(string[j + 1]))
|
---|
924 | {
|
---|
925 | j += 1;
|
---|
926 | max = atoi(string + j);
|
---|
927 | while (isdigit(string[j]))
|
---|
928 | j++;
|
---|
929 | }
|
---|
930 | fmt = (char *)xmalloc (j - i + 2);
|
---|
931 | strncpy (fmt, string + i + 1, j - i);
|
---|
932 | fmt[j - i - 1] = 's';
|
---|
933 | fmt[j - i] = '\0';
|
---|
934 | }
|
---|
935 | else
|
---|
936 | j = i + 1;
|
---|
937 | }
|
---|
938 | if (string[j] == '[')
|
---|
939 | {
|
---|
940 | unsigned arg = 0;
|
---|
941 | char *argstr = NULL;
|
---|
942 | char *rep_name, *fun_name, *rep;
|
---|
943 | InfoCommand *command;
|
---|
944 | char *repstr = NULL;
|
---|
945 | unsigned replen;
|
---|
946 |
|
---|
947 | /* Copy in the old text. */
|
---|
948 | strncpy (result + next, string + start, i - start);
|
---|
949 | next += (i - start);
|
---|
950 | start = j + 1;
|
---|
951 |
|
---|
952 | /* Look for an optional numeric arg. */
|
---|
953 | i = start;
|
---|
954 | if (isdigit(string[i])
|
---|
955 | || (string[i] == '-' && isdigit(string[i + 1])) )
|
---|
956 | {
|
---|
957 | arg = atoi(string + i);
|
---|
958 | if (string[i] == '-')
|
---|
959 | i++;
|
---|
960 | while (isdigit(string[i]))
|
---|
961 | i++;
|
---|
962 | }
|
---|
963 | start = i;
|
---|
964 |
|
---|
965 | /* Move to the end of the function name. */
|
---|
966 | for (i = start; string[i] && (string[i] != ']'); i++);
|
---|
967 |
|
---|
968 | rep_name = (char *)xmalloc (1 + i - start);
|
---|
969 | strncpy (rep_name, string + start, i - start);
|
---|
970 | rep_name[i - start] = '\0';
|
---|
971 |
|
---|
972 | /* If we have only one window (because the window size was too
|
---|
973 | small to split it), we have to quit help by going back one
|
---|
974 | noew in the history list, not deleting the window. */
|
---|
975 | if (strcmp (rep_name, "quit-help") == 0)
|
---|
976 | fun_name = help_is_only_window_p ? "history-node"
|
---|
977 | : "delete-window";
|
---|
978 | else
|
---|
979 | fun_name = rep_name;
|
---|
980 |
|
---|
981 | /* Find a key which invokes this function in the info_keymap. */
|
---|
982 | command = named_function (fun_name);
|
---|
983 |
|
---|
984 | free (rep_name);
|
---|
985 |
|
---|
986 | /* If the internal documentation string fails, there is a
|
---|
987 | serious problem with the associated command's documentation.
|
---|
988 | We croak so that it can be fixed immediately. */
|
---|
989 | if (!command)
|
---|
990 | abort ();
|
---|
991 |
|
---|
992 | if (arg)
|
---|
993 | {
|
---|
994 | char *argrep, *p;
|
---|
995 |
|
---|
996 | argrep = where_is (info_keymap, InfoCmd(info_add_digit_to_numeric_arg));
|
---|
997 | p = argrep ? strrpbrk (argrep, "0123456789-") : NULL;
|
---|
998 | if (p)
|
---|
999 | {
|
---|
1000 | argstr = (char *)xmalloc (p - argrep + 21);
|
---|
1001 | strncpy (argstr, argrep, p - argrep);
|
---|
1002 | sprintf (argstr + (p - argrep), "%d", arg);
|
---|
1003 | }
|
---|
1004 | else
|
---|
1005 | command = NULL;
|
---|
1006 | }
|
---|
1007 | rep = command ? where_is (info_keymap, command) : NULL;
|
---|
1008 | if (!rep)
|
---|
1009 | rep = "N/A";
|
---|
1010 | replen = (argstr ? strlen (argstr) : 0) + strlen (rep) + 1;
|
---|
1011 | repstr = (char *)xmalloc (replen);
|
---|
1012 | repstr[0] = '\0';
|
---|
1013 | if (argstr)
|
---|
1014 | {
|
---|
1015 | strcat(repstr, argstr);
|
---|
1016 | strcat(repstr, " ");
|
---|
1017 | free (argstr);
|
---|
1018 | }
|
---|
1019 | strcat(repstr, rep);
|
---|
1020 |
|
---|
1021 | if (fmt)
|
---|
1022 | {
|
---|
1023 | if (replen > max)
|
---|
1024 | replen = max;
|
---|
1025 | if (replen < min)
|
---|
1026 | replen = min;
|
---|
1027 | }
|
---|
1028 | if (next + replen > reslen)
|
---|
1029 | {
|
---|
1030 | reslen = next + replen + 1;
|
---|
1031 | result = (char *)xrealloc (result, reslen + 1);
|
---|
1032 | }
|
---|
1033 |
|
---|
1034 | if (fmt)
|
---|
1035 | sprintf (result + next, fmt, repstr);
|
---|
1036 | else
|
---|
1037 | strcpy (result + next, repstr);
|
---|
1038 |
|
---|
1039 | next = strlen (result);
|
---|
1040 | free (repstr);
|
---|
1041 |
|
---|
1042 | start = i;
|
---|
1043 | if (string[i])
|
---|
1044 | start++;
|
---|
1045 | }
|
---|
1046 |
|
---|
1047 | maybe_free (fmt);
|
---|
1048 | }
|
---|
1049 | }
|
---|
1050 | strcpy (result + next, string + start);
|
---|
1051 | return (result);
|
---|
1052 | }
|
---|
1053 |
|
---|
1054 | /* Return a string of characters which could be typed from the keymap
|
---|
1055 | MAP to invoke FUNCTION. */
|
---|
1056 | static char *where_is_rep = (char *)NULL;
|
---|
1057 | static int where_is_rep_index = 0;
|
---|
1058 | static int where_is_rep_size = 0;
|
---|
1059 |
|
---|
1060 | char *
|
---|
1061 | where_is (Keymap map, InfoCommand *cmd)
|
---|
1062 | {
|
---|
1063 | char *rep;
|
---|
1064 |
|
---|
1065 | if (!where_is_rep_size)
|
---|
1066 | where_is_rep = (char *)xmalloc (where_is_rep_size = 100);
|
---|
1067 | where_is_rep_index = 0;
|
---|
1068 |
|
---|
1069 | rep = where_is_internal (map, cmd);
|
---|
1070 |
|
---|
1071 | /* If it couldn't be found, return "M-x Foo" (or equivalent). */
|
---|
1072 | if (!rep)
|
---|
1073 | {
|
---|
1074 | char *name;
|
---|
1075 |
|
---|
1076 | name = function_name (cmd);
|
---|
1077 | if (!name)
|
---|
1078 | return NULL; /* no such function */
|
---|
1079 |
|
---|
1080 | rep = where_is_internal (map, InfoCmd(info_execute_command));
|
---|
1081 | if (!rep)
|
---|
1082 | return ""; /* function exists but can't be got to by user */
|
---|
1083 |
|
---|
1084 | sprintf (where_is_rep, "%s %s", rep, name);
|
---|
1085 |
|
---|
1086 | rep = where_is_rep;
|
---|
1087 | }
|
---|
1088 | return (rep);
|
---|
1089 | }
|
---|
1090 |
|
---|
1091 | /* Return the printed rep of the keystrokes that invoke FUNCTION,
|
---|
1092 | as found in MAP, or NULL. */
|
---|
1093 | static char *
|
---|
1094 | where_is_internal (Keymap map, InfoCommand *cmd)
|
---|
1095 | {
|
---|
1096 | #if defined(INFOKEY)
|
---|
1097 |
|
---|
1098 | register FUNCTION_KEYSEQ *k;
|
---|
1099 |
|
---|
1100 | for (k = cmd->keys; k; k = k->next)
|
---|
1101 | if (k->map == map)
|
---|
1102 | return pretty_keyseq (k->keyseq);
|
---|
1103 |
|
---|
1104 | return NULL;
|
---|
1105 |
|
---|
1106 | #else /* !INFOKEY */
|
---|
1107 | /* There is a bug in that create_internal_info_help_node calls
|
---|
1108 | where_is_internal without setting where_is_rep_index to zero. This
|
---|
1109 | was found by Mandrake and reported by Thierry Vignaud
|
---|
1110 | <tvignaud@mandrakesoft.com> around April 24, 2002.
|
---|
1111 |
|
---|
1112 | I think the best fix is to make where_is_rep_index another
|
---|
1113 | parameter to this recursively-called function, instead of a static
|
---|
1114 | variable. But this [!INFOKEY] branch of the code is not enabled
|
---|
1115 | any more, so let's just skip the whole thing. --karl, 28sep02. */
|
---|
1116 | register int i;
|
---|
1117 |
|
---|
1118 | /* If the function is directly invokable in MAP, return the representation
|
---|
1119 | of that keystroke. */
|
---|
1120 | for (i = 0; i < 256; i++)
|
---|
1121 | if ((map[i].type == ISFUNC) && map[i].function == cmd)
|
---|
1122 | {
|
---|
1123 | sprintf (where_is_rep + where_is_rep_index, "%s", pretty_keyname (i));
|
---|
1124 | return (where_is_rep);
|
---|
1125 | }
|
---|
1126 |
|
---|
1127 | /* Okay, search subsequent maps for this function. */
|
---|
1128 | for (i = 0; i < 256; i++)
|
---|
1129 | {
|
---|
1130 | if (map[i].type == ISKMAP)
|
---|
1131 | {
|
---|
1132 | int saved_index = where_is_rep_index;
|
---|
1133 | char *rep;
|
---|
1134 |
|
---|
1135 | sprintf (where_is_rep + where_is_rep_index, "%s ",
|
---|
1136 | pretty_keyname (i));
|
---|
1137 |
|
---|
1138 | where_is_rep_index = strlen (where_is_rep);
|
---|
1139 | rep = where_is_internal ((Keymap)map[i].function, cmd);
|
---|
1140 |
|
---|
1141 | if (rep)
|
---|
1142 | return (where_is_rep);
|
---|
1143 |
|
---|
1144 | where_is_rep_index = saved_index;
|
---|
1145 | }
|
---|
1146 | }
|
---|
1147 |
|
---|
1148 | return NULL;
|
---|
1149 |
|
---|
1150 | #endif /* INFOKEY */
|
---|
1151 | }
|
---|
1152 |
|
---|
1153 | DECLARE_INFO_COMMAND (info_where_is,
|
---|
1154 | _("Show what to type to execute a given command"))
|
---|
1155 | {
|
---|
1156 | char *command_name;
|
---|
1157 |
|
---|
1158 | command_name = read_function_name ((char *) _("Where is command: "), window);
|
---|
1159 |
|
---|
1160 | if (!command_name)
|
---|
1161 | {
|
---|
1162 | info_abort_key (active_window, count, key);
|
---|
1163 | return;
|
---|
1164 | }
|
---|
1165 |
|
---|
1166 | if (*command_name)
|
---|
1167 | {
|
---|
1168 | InfoCommand *command;
|
---|
1169 |
|
---|
1170 | command = named_function (command_name);
|
---|
1171 |
|
---|
1172 | if (command)
|
---|
1173 | {
|
---|
1174 | char *location;
|
---|
1175 |
|
---|
1176 | location = where_is (active_window->keymap, command);
|
---|
1177 |
|
---|
1178 | if (!location || !location[0])
|
---|
1179 | {
|
---|
1180 | info_error ((char *) _("`%s' is not on any keys"),
|
---|
1181 | command_name, NULL);
|
---|
1182 | }
|
---|
1183 | else
|
---|
1184 | {
|
---|
1185 | if (strstr (location, function_name (command)))
|
---|
1186 | window_message_in_echo_area
|
---|
1187 | ((char *) _("%s can only be invoked via %s."),
|
---|
1188 | command_name, location);
|
---|
1189 | else
|
---|
1190 | window_message_in_echo_area
|
---|
1191 | ((char *) _("%s can be invoked via %s."),
|
---|
1192 | command_name, location);
|
---|
1193 | }
|
---|
1194 | }
|
---|
1195 | else
|
---|
1196 | info_error ((char *) _("There is no function named `%s'"),
|
---|
1197 | command_name, NULL);
|
---|
1198 | }
|
---|
1199 |
|
---|
1200 | free (command_name);
|
---|
1201 | }
|
---|