171
|
1 /* infohelp.c -- Functions which build documentation nodes. */ |
|
2 |
|
3 /* This file is part of GNU Info, a program for reading online documentation |
|
4 stored in Info format. |
|
5 |
|
6 Copyright (C) 1993 Free Software Foundation, Inc. |
|
7 |
|
8 This program is free software; you can redistribute it and/or modify |
|
9 it under the terms of the GNU General Public License as published by |
|
10 the Free Software Foundation; either version 2, or (at your option) |
|
11 any later version. |
|
12 |
|
13 This program is distributed in the hope that it will be useful, |
|
14 but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
16 GNU General Public License for more details. |
|
17 |
|
18 You should have received a copy of the GNU General Public License |
|
19 along with this program; if not, write to the Free Software |
1315
|
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
171
|
21 |
|
22 Written by Brian Fox (bfox@ai.mit.edu). */ |
|
23 |
1242
|
24 #ifdef HAVE_CONFIG_H |
|
25 #include <config.h> |
|
26 #endif |
|
27 |
171
|
28 #include "info.h" |
|
29 |
|
30 /* **************************************************************** */ |
|
31 /* */ |
|
32 /* Info Help Windows */ |
|
33 /* */ |
|
34 /* **************************************************************** */ |
|
35 |
|
36 /* The name of the node used in the help window. */ |
|
37 static char *info_help_nodename = "*Info Help*"; |
|
38 |
|
39 /* A node containing printed key bindings and their documentation. */ |
|
40 static NODE *internal_info_help_node = (NODE *)NULL; |
|
41 |
|
42 /* The static text which appears in the internal info help node. */ |
|
43 static char *info_internal_help_text[] = { |
|
44 "Basic Commands in Info Windows", |
|
45 "******************************", |
|
46 "", |
|
47 " h Invoke the Info tutorial.", |
|
48 "", |
|
49 "Selecting other nodes:", |
|
50 "----------------------", |
|
51 " n Move to the \"next\" node of this node.", |
|
52 " p Move to the \"previous\" node of this node.", |
|
53 " u Move \"up\" from this node.", |
|
54 " m Pick menu item specified by name.", |
|
55 " Picking a menu item causes another node to be selected.", |
|
56 " f Follow a cross reference. Reads name of reference.", |
|
57 " l Move to the last node seen in this window.", |
|
58 " d Move to the `directory' node. Equivalent to `g(DIR)'.", |
|
59 "", |
|
60 "Moving within a node:", |
|
61 "---------------------", |
|
62 " SPC Scroll forward a page.", |
|
63 " DEL Scroll backward a page.", |
|
64 " b Go to the beginning of this node.", |
|
65 " e Go to the end of this node.", |
|
66 "", |
|
67 "\"Advanced\" commands:", |
|
68 "--------------------", |
|
69 " q Quit Info.", |
|
70 " 1 Pick first item in node's menu.", |
|
71 " 2-9 Pick second ... ninth item in node's menu.", |
|
72 " 0 Pick last item in node's menu.", |
|
73 " g Move to node specified by name.", |
|
74 " You may include a filename as well, as in (FILENAME)NODENAME.", |
|
75 " s Search through this Info file for a specified string,", |
|
76 " and select the node in which the next occurrence is found.", |
|
77 (char *)NULL |
|
78 }; |
|
79 |
|
80 void |
|
81 dump_map_to_message_buffer (prefix, map) |
|
82 char *prefix; |
|
83 Keymap map; |
|
84 { |
|
85 register int i; |
|
86 |
|
87 for (i = 0; i < 256; i++) |
|
88 { |
|
89 if (map[i].type == ISKMAP) |
|
90 { |
|
91 char *new_prefix, *keyname; |
|
92 |
|
93 keyname = pretty_keyname (i); |
|
94 new_prefix = (char *) |
|
95 xmalloc (3 + strlen (prefix) + strlen (keyname)); |
|
96 sprintf (new_prefix, "%s%s%s ", prefix, *prefix ? " " : "", keyname); |
|
97 |
|
98 dump_map_to_message_buffer (new_prefix, (Keymap)map[i].function); |
|
99 free (new_prefix); |
|
100 } |
|
101 else if (map[i].function) |
|
102 { |
|
103 register int last; |
|
104 char *doc, *name; |
|
105 |
|
106 doc = function_documentation (map[i].function); |
|
107 name = function_name (map[i].function); |
|
108 |
|
109 if (!*doc) |
|
110 continue; |
|
111 |
|
112 /* Find out if there is a series of identical functions, as in |
|
113 ea_insert (). */ |
|
114 for (last = i + 1; last < 256; last++) |
|
115 if ((map[last].type != ISFUNC) || |
|
116 (map[last].function != map[i].function)) |
|
117 break; |
|
118 |
|
119 if (last - 1 != i) |
|
120 { |
|
121 printf_to_message_buffer |
|
122 ("%s%s .. ", prefix, pretty_keyname (i)); |
|
123 printf_to_message_buffer |
|
124 ("%s%s\t", prefix, pretty_keyname (last - 1)); |
|
125 i = last - 1; |
|
126 } |
|
127 else |
|
128 printf_to_message_buffer ("%s%s\t", prefix, pretty_keyname (i)); |
|
129 |
|
130 #if defined (NAMED_FUNCTIONS) |
|
131 /* Print the name of the function, and some padding before the |
|
132 documentation string is printed. */ |
|
133 { |
|
134 int length_so_far; |
|
135 int desired_doc_start = 40; /* Must be multiple of 8. */ |
|
136 |
|
137 printf_to_message_buffer ("(%s)", name); |
|
138 length_so_far = message_buffer_length_this_line (); |
|
139 |
|
140 if ((desired_doc_start + strlen (doc)) >= the_screen->width) |
|
141 printf_to_message_buffer ("\n "); |
|
142 else |
|
143 { |
|
144 while (length_so_far < desired_doc_start) |
|
145 { |
|
146 printf_to_message_buffer ("\t"); |
|
147 length_so_far += character_width ('\t', length_so_far); |
|
148 } |
|
149 } |
|
150 } |
|
151 #endif /* NAMED_FUNCTIONS */ |
|
152 printf_to_message_buffer ("%s\n", doc); |
|
153 } |
|
154 } |
|
155 } |
|
156 |
|
157 /* How to create internal_info_help_node. */ |
|
158 static void |
|
159 create_internal_info_help_node () |
|
160 { |
|
161 register int i; |
|
162 |
|
163 initialize_message_buffer (); |
|
164 |
|
165 for (i = 0; info_internal_help_text[i]; i++) |
|
166 printf_to_message_buffer ("%s\n", info_internal_help_text[i]); |
|
167 |
|
168 printf_to_message_buffer ("---------------------\n\n"); |
|
169 printf_to_message_buffer ("The current search path is:\n"); |
|
170 printf_to_message_buffer (" \"%s\"\n", infopath); |
|
171 printf_to_message_buffer ("---------------------\n\n"); |
|
172 printf_to_message_buffer ("Commands available in Info windows:\n\n"); |
|
173 dump_map_to_message_buffer ("", info_keymap); |
|
174 printf_to_message_buffer ("---------------------\n\n"); |
|
175 printf_to_message_buffer ("Commands available in the echo area:\n\n"); |
|
176 dump_map_to_message_buffer ("", echo_area_keymap); |
|
177 |
|
178 { |
|
179 char *message; |
|
180 |
|
181 message = replace_in_documentation |
|
182 ("--- Use `\\[history-node]' or `\\[kill-node]' to exit ---\n"); |
|
183 printf_to_message_buffer ("%s", message); |
|
184 } |
|
185 |
|
186 internal_info_help_node = message_buffer_to_node (); |
|
187 add_gcable_pointer (internal_info_help_node->contents); |
|
188 name_internal_node (internal_info_help_node, info_help_nodename); |
|
189 |
|
190 /* Even though this is an internal node, we don't want the window |
|
191 system to treat it specially. So we turn off the internalness |
|
192 of it here. */ |
|
193 internal_info_help_node->flags &= ~N_IsInternal; |
|
194 } |
|
195 |
|
196 /* Return a window which is the window showing help in this Info. */ |
|
197 static WINDOW * |
|
198 info_find_or_create_help_window () |
|
199 { |
|
200 WINDOW *help_window; |
|
201 |
|
202 help_window = get_internal_info_window (info_help_nodename); |
|
203 |
|
204 /* If we couldn't find the help window, then make it. */ |
|
205 if (!help_window) |
|
206 { |
|
207 WINDOW *window, *eligible = (WINDOW *)NULL; |
|
208 int max = 0; |
|
209 |
|
210 for (window = windows; window; window = window->next) |
|
211 { |
|
212 if (window->height > max) |
|
213 { |
|
214 max = window->height; |
|
215 eligible = window; |
|
216 } |
|
217 } |
|
218 |
|
219 if (!eligible) |
|
220 return ((WINDOW *)NULL); |
|
221 else |
|
222 { |
|
223 /* Make a new node containing the help text. Split the largest |
|
224 window into 2 windows, and show the help text in that window. */ |
|
225 if (!internal_info_help_node) |
|
226 create_internal_info_help_node (); |
|
227 |
|
228 if (eligible->height > 30) |
|
229 { |
|
230 active_window = eligible; |
|
231 help_window = window_make_window (internal_info_help_node); |
|
232 } |
|
233 else |
|
234 { |
|
235 set_remembered_pagetop_and_point (active_window); |
|
236 window_set_node_of_window |
|
237 (active_window, internal_info_help_node); |
|
238 help_window = active_window; |
|
239 } |
|
240 |
|
241 remember_window_and_node (help_window, help_window->node); |
|
242 } |
|
243 } |
|
244 return (help_window); |
|
245 } |
|
246 |
|
247 /* Create or move to the help window. */ |
|
248 DECLARE_INFO_COMMAND (info_get_help_window, "Display help message") |
|
249 { |
|
250 WINDOW *help_window; |
|
251 |
|
252 help_window = info_find_or_create_help_window (); |
|
253 if (help_window) |
|
254 { |
|
255 active_window = help_window; |
|
256 active_window->flags |= W_UpdateWindow; |
|
257 } |
|
258 else |
|
259 { |
|
260 info_error (CANT_MAKE_HELP); |
|
261 } |
|
262 } |
|
263 |
|
264 /* Show the Info help node. This means that the "info" file is installed |
|
265 where it can easily be found on your system. */ |
|
266 DECLARE_INFO_COMMAND (info_get_info_help_node, "Visit Info node `(info)Help'") |
|
267 { |
|
268 NODE *node; |
|
269 char *nodename; |
|
270 |
|
271 /* If there is a window on the screen showing the node "(info)Help" or |
|
272 the node "(info)Help-Small-Screen", simply select that window. */ |
|
273 { |
|
274 WINDOW *win; |
|
275 |
|
276 for (win = windows; win; win = win->next) |
|
277 { |
|
278 if (win->node && win->node->filename && |
|
279 (stricmp |
|
280 (filename_non_directory (win->node->filename), "info") == 0) && |
|
281 ((strcmp (win->node->nodename, "Help") == 0) || |
|
282 (strcmp (win->node->nodename, "Help-Small-Screen") == 0))) |
|
283 { |
|
284 active_window = win; |
|
285 return; |
|
286 } |
|
287 } |
|
288 } |
|
289 |
|
290 /* If the current window is small, show the small screen help. */ |
|
291 if (active_window->height < 24) |
|
292 nodename = "Help-Small-Screen"; |
|
293 else |
|
294 nodename = "Help"; |
|
295 |
|
296 /* Try to get the info file for Info. */ |
|
297 node = info_get_node ("Info", nodename); |
|
298 |
|
299 if (!node) |
|
300 { |
|
301 if (info_recent_file_error) |
|
302 info_error (info_recent_file_error); |
|
303 else |
|
304 info_error (CANT_FILE_NODE, "Info", nodename); |
|
305 } |
|
306 else |
|
307 { |
|
308 /* If the current window is very large (greater than 45 lines), |
|
309 then split it and show the help node in another window. |
|
310 Otherwise, use the current window. */ |
|
311 |
|
312 if (active_window->height > 45) |
|
313 active_window = window_make_window (node); |
|
314 else |
|
315 { |
|
316 set_remembered_pagetop_and_point (active_window); |
|
317 window_set_node_of_window (active_window, node); |
|
318 } |
|
319 |
|
320 remember_window_and_node (active_window, node); |
|
321 } |
|
322 } |
|
323 |
|
324 /* **************************************************************** */ |
|
325 /* */ |
|
326 /* Groveling Info Keymaps and Docs */ |
|
327 /* */ |
|
328 /* **************************************************************** */ |
|
329 |
|
330 /* Return the documentation associated with the Info command FUNCTION. */ |
|
331 char * |
|
332 function_documentation (function) |
|
333 VFunction *function; |
|
334 { |
|
335 register int i; |
|
336 |
|
337 for (i = 0; function_doc_array[i].func; i++) |
|
338 if (function == function_doc_array[i].func) |
|
339 break; |
|
340 |
|
341 return (replace_in_documentation (function_doc_array[i].doc)); |
|
342 } |
|
343 |
|
344 #if defined (NAMED_FUNCTIONS) |
|
345 /* Return the user-visible name of the function associated with the |
|
346 Info command FUNCTION. */ |
|
347 char * |
|
348 function_name (function) |
|
349 |
|
350 VFunction *function; |
|
351 { |
|
352 register int i; |
|
353 |
|
354 for (i = 0; function_doc_array[i].func; i++) |
|
355 if (function == function_doc_array[i].func) |
|
356 break; |
|
357 |
|
358 return (function_doc_array[i].func_name); |
|
359 } |
|
360 |
|
361 /* Return a pointer to the function named NAME. */ |
|
362 VFunction * |
|
363 named_function (name) |
|
364 char *name; |
|
365 { |
|
366 register int i; |
|
367 |
|
368 for (i = 0; function_doc_array[i].func; i++) |
|
369 if (strcmp (function_doc_array[i].func_name, name) == 0) |
|
370 break; |
|
371 |
|
372 return (function_doc_array[i].func); |
|
373 } |
|
374 #endif /* NAMED_FUNCTIONS */ |
|
375 |
|
376 /* Return the documentation associated with KEY in MAP. */ |
|
377 char * |
|
378 key_documentation (key, map) |
|
379 char key; |
|
380 Keymap map; |
|
381 { |
|
382 VFunction *function = map[key].function; |
|
383 |
|
384 if (function) |
|
385 return (function_documentation (function)); |
|
386 else |
|
387 return ((char *)NULL); |
|
388 } |
|
389 |
|
390 DECLARE_INFO_COMMAND (describe_key, "Print documentation for KEY") |
|
391 { |
|
392 char keyname[50]; |
|
393 int keyname_index = 0; |
|
394 unsigned char keystroke; |
|
395 char *rep; |
|
396 Keymap map; |
|
397 |
|
398 keyname[0] = '\0'; |
|
399 map = window->keymap; |
|
400 |
|
401 while (1) |
|
402 { |
|
403 message_in_echo_area ("Describe key: %s", keyname); |
|
404 keystroke = info_get_input_char (); |
|
405 unmessage_in_echo_area (); |
|
406 |
|
407 if (Meta_p (keystroke) && (!ISO_Latin_p || key < 160)) |
|
408 { |
|
409 if (map[ESC].type != ISKMAP) |
|
410 { |
|
411 window_message_in_echo_area |
|
412 ("ESC %s is undefined.", pretty_keyname (UnMeta (keystroke))); |
|
413 return; |
|
414 } |
|
415 |
|
416 strcpy (keyname + keyname_index, "ESC "); |
|
417 keyname_index = strlen (keyname); |
|
418 keystroke = UnMeta (keystroke); |
|
419 map = (Keymap)map[ESC].function; |
|
420 } |
|
421 |
|
422 /* Add the printed representation of KEYSTROKE to our keyname. */ |
|
423 rep = pretty_keyname (keystroke); |
|
424 strcpy (keyname + keyname_index, rep); |
|
425 keyname_index = strlen (keyname); |
|
426 |
|
427 if (map[keystroke].function == (VFunction *)NULL) |
|
428 { |
|
429 message_in_echo_area ("%s is undefined.", keyname); |
|
430 return; |
|
431 } |
|
432 else if (map[keystroke].type == ISKMAP) |
|
433 { |
|
434 map = (Keymap)map[keystroke].function; |
|
435 strcat (keyname, " "); |
|
436 keyname_index = strlen (keyname); |
|
437 continue; |
|
438 } |
|
439 else |
|
440 { |
|
441 char *message, *fundoc, *funname = ""; |
|
442 |
|
443 #if defined (NAMED_FUNCTIONS) |
|
444 funname = function_name (map[keystroke].function); |
|
445 #endif /* NAMED_FUNCTIONS */ |
|
446 |
|
447 fundoc = function_documentation (map[keystroke].function); |
|
448 |
|
449 message = (char *)xmalloc |
|
450 (10 + strlen (keyname) + strlen (fundoc) + strlen (funname)); |
|
451 |
|
452 #if defined (NAMED_FUNCTIONS) |
|
453 sprintf (message, "%s (%s): %s.", keyname, funname, fundoc); |
|
454 #else |
|
455 sprintf (message, "%s is defined to %s.", keyname, fundoc); |
|
456 #endif /* !NAMED_FUNCTIONS */ |
|
457 |
|
458 window_message_in_echo_area ("%s", message); |
|
459 free (message); |
|
460 break; |
|
461 } |
|
462 } |
|
463 } |
|
464 |
|
465 /* How to get the pretty printable name of a character. */ |
|
466 static char rep_buffer[30]; |
|
467 |
|
468 char * |
|
469 pretty_keyname (key) |
|
470 unsigned char key; |
|
471 { |
|
472 char *rep; |
|
473 |
|
474 if (Meta_p (key)) |
|
475 { |
|
476 char temp[20]; |
|
477 |
|
478 rep = pretty_keyname (UnMeta (key)); |
|
479 |
|
480 sprintf (temp, "ESC %s", rep); |
|
481 strcpy (rep_buffer, temp); |
|
482 rep = rep_buffer; |
|
483 } |
|
484 else if (Control_p (key)) |
|
485 { |
|
486 switch (key) |
|
487 { |
|
488 case '\n': rep = "LFD"; break; |
|
489 case '\t': rep = "TAB"; break; |
|
490 case '\r': rep = "RET"; break; |
|
491 case ESC: rep = "ESC"; break; |
|
492 |
|
493 default: |
|
494 sprintf (rep_buffer, "C-%c", UnControl (key)); |
|
495 rep = rep_buffer; |
|
496 } |
|
497 } |
|
498 else |
|
499 { |
|
500 switch (key) |
|
501 { |
|
502 case ' ': rep = "SPC"; break; |
|
503 case DEL: rep = "DEL"; break; |
|
504 default: |
|
505 rep_buffer[0] = key; |
|
506 rep_buffer[1] = '\0'; |
|
507 rep = rep_buffer; |
|
508 } |
|
509 } |
|
510 return (rep); |
|
511 } |
|
512 |
|
513 /* Replace the names of functions with the key that invokes them. */ |
|
514 static char *where_is (), *where_is_internal (); |
|
515 |
|
516 char * |
|
517 replace_in_documentation (string) |
|
518 char *string; |
|
519 { |
|
520 register int i, start, next; |
|
521 static char *result = (char *)NULL; |
|
522 |
|
523 maybe_free (result); |
|
524 result = (char *)xmalloc (1 + strlen (string)); |
|
525 |
|
526 i = next = start = 0; |
|
527 |
|
528 /* Skip to the beginning of a replaceable function. */ |
|
529 for (i = start; string[i]; i++) |
|
530 { |
|
531 /* Is this the start of a replaceable function name? */ |
|
532 if (string[i] == '\\' && string[i + 1] == '[') |
|
533 { |
|
534 char *fun_name, *rep; |
|
535 VFunction *function; |
|
536 |
|
537 /* Copy in the old text. */ |
|
538 strncpy (result + next, string + start, i - start); |
|
539 next += (i - start); |
|
540 start = i + 2; |
|
541 |
|
542 /* Move to the end of the function name. */ |
|
543 for (i = start; string[i] && (string[i] != ']'); i++); |
|
544 |
|
545 fun_name = (char *)xmalloc (1 + i - start); |
|
546 strncpy (fun_name, string + start, i - start); |
|
547 fun_name[i - start] = '\0'; |
|
548 |
|
549 /* Find a key which invokes this function in the info_keymap. */ |
|
550 function = named_function (fun_name); |
|
551 |
|
552 /* If the internal documentation string fails, there is a |
|
553 serious problem with the associated command's documentation. |
|
554 We croak so that it can be fixed immediately. */ |
|
555 if (!function) |
|
556 abort (); |
|
557 |
|
558 rep = where_is (info_keymap, function); |
|
559 strcpy (result + next, rep); |
|
560 next = strlen (result); |
|
561 |
|
562 start = i; |
|
563 if (string[i]) |
|
564 start++; |
|
565 } |
|
566 } |
|
567 strcpy (result + next, string + start); |
|
568 return (result); |
|
569 } |
|
570 |
|
571 /* Return a string of characters which could be typed from the keymap |
|
572 MAP to invoke FUNCTION. */ |
|
573 static char *where_is_rep = (char *)NULL; |
|
574 static int where_is_rep_index = 0; |
|
575 static int where_is_rep_size = 0; |
|
576 |
|
577 static char * |
|
578 where_is (map, function) |
|
579 Keymap map; |
|
580 VFunction *function; |
|
581 { |
|
582 char *rep; |
|
583 |
|
584 if (!where_is_rep_size) |
|
585 where_is_rep = (char *)xmalloc (where_is_rep_size = 100); |
|
586 where_is_rep_index = 0; |
|
587 |
|
588 rep = where_is_internal (map, function); |
|
589 |
|
590 /* If it couldn't be found, return "M-x Foo". */ |
|
591 if (!rep) |
|
592 { |
|
593 char *name; |
|
594 |
|
595 name = function_name (function); |
|
596 |
|
597 if (name) |
|
598 sprintf (where_is_rep, "M-x %s", name); |
|
599 |
|
600 rep = where_is_rep; |
|
601 } |
|
602 return (rep); |
|
603 } |
|
604 |
|
605 /* Return the printed rep of FUNCTION as found in MAP, or NULL. */ |
|
606 static char * |
|
607 where_is_internal (map, function) |
|
608 Keymap map; |
|
609 VFunction *function; |
|
610 { |
|
611 register int i; |
|
612 |
|
613 /* If the function is directly invokable in MAP, return the representation |
|
614 of that keystroke. */ |
|
615 for (i = 0; i < 256; i++) |
|
616 if ((map[i].type == ISFUNC) && map[i].function == function) |
|
617 { |
|
618 sprintf (where_is_rep + where_is_rep_index, "%s", pretty_keyname (i)); |
|
619 return (where_is_rep); |
|
620 } |
|
621 |
|
622 /* Okay, search subsequent maps for this function. */ |
|
623 for (i = 0; i < 256; i++) |
|
624 { |
|
625 if (map[i].type == ISKMAP) |
|
626 { |
|
627 int saved_index = where_is_rep_index; |
|
628 char *rep; |
|
629 |
|
630 sprintf (where_is_rep + where_is_rep_index, "%s ", |
|
631 pretty_keyname (i)); |
|
632 |
|
633 where_is_rep_index = strlen (where_is_rep); |
|
634 rep = where_is_internal ((Keymap)map[i].function, function); |
|
635 |
|
636 if (rep) |
|
637 return (where_is_rep); |
|
638 |
|
639 where_is_rep_index = saved_index; |
|
640 } |
|
641 } |
|
642 |
|
643 return ((char *)NULL); |
|
644 } |
|
645 |
|
646 extern char *read_function_name (); |
|
647 |
|
648 DECLARE_INFO_COMMAND (info_where_is, |
|
649 "Show what to type to execute a given command") |
|
650 { |
|
651 char *command_name; |
|
652 |
|
653 command_name = read_function_name ("Where is command: ", window); |
|
654 |
|
655 if (!command_name) |
|
656 { |
|
657 info_abort_key (active_window, count, key); |
|
658 return; |
|
659 } |
|
660 |
|
661 if (*command_name) |
|
662 { |
|
663 VFunction *function; |
|
664 |
|
665 function = named_function (command_name); |
|
666 |
|
667 if (function) |
|
668 { |
|
669 char *location; |
|
670 |
|
671 location = where_is (active_window->keymap, function); |
|
672 |
|
673 if (!location) |
|
674 { |
|
675 info_error ("`%s' is not on any keys", command_name); |
|
676 } |
|
677 else |
|
678 { |
|
679 if (strncmp (location, "M-x ", 4) == 0) |
|
680 window_message_in_echo_area |
|
681 ("%s can only be invoked via %s.", command_name, location); |
|
682 else |
|
683 window_message_in_echo_area |
|
684 ("%s can be invoked via %s.", command_name, location); |
|
685 } |
|
686 } |
|
687 else |
|
688 info_error ("There is no function named `%s'", command_name); |
|
689 } |
|
690 |
|
691 free (command_name); |
|
692 } |
|
693 |