File: | src/gnu/usr.bin/texinfo/info/infodoc.c |
Warning: | line 899, column 3 Value stored to 'i' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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. */ |
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((void *)0); |
35 | |
36 | /* A pointer to the contents of the help node. */ |
37 | static 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 | |
45 | static 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 | |
94 | static 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 | |
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 | /* Groveling Info Keymaps and Docs */ |
564 | /* */ |
565 | /* **************************************************************** */ |
566 | |
567 | /* Return the documentation associated with the Info command FUNCTION. */ |
568 | char * |
569 | function_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. */ |
595 | char * |
596 | function_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. */ |
616 | InfoCommand * |
617 | named_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. */ |
630 | char * |
631 | key_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 | |
641 | DECLARE_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. */ |
741 | char * |
742 | pretty_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 | |
792 | static void pretty_keyseq_internal (char *keyseq, char *rep); |
793 | |
794 | char * |
795 | pretty_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 | |
805 | static void |
806 | pretty_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. */ |
873 | static char * |
874 | strrpbrk (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. */ |
889 | char * |
890 | replace_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. */ |
1055 | static char *where_is_rep = (char *)NULL; |
1056 | static int where_is_rep_index = 0; |
1057 | static int where_is_rep_size = 0; |
1058 | |
1059 | char * |
1060 | where_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. */ |
1092 | static char * |
1093 | where_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 | |
1152 | DECLARE_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 | } |