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