1 1.1 christos /* $NetBSD: infodoc.c,v 1.1.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