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