infodoc.c 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917
  1. /* infodoc.c -- functions which build documentation nodes.
  2. $Id$
  3. Copyright 1993, 1997, 1998, 1999, 2001, 2002, 2003, 2004, 2006,
  4. 2007, 2008, 2011, 2013, 2014, 2015, 2016 Free Software Foundation, Inc.
  5. This program is free software: you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation, either version 3 of the License, or
  8. (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. Originally written by Brian Fox. */
  16. #include "info.h"
  17. #include "info-utils.h"
  18. #include "filesys.h"
  19. #include "session.h"
  20. #include "doc.h"
  21. #include "funs.h"
  22. /* The name of the node used in the help window. */
  23. static char *info_help_nodename = "*Info Help*";
  24. /* A node containing printed key bindings and their documentation. */
  25. static NODE *internal_info_help_node = NULL;
  26. /* The (more or less) static text which appears in the internal info
  27. help node. The actual key bindings are inserted. Keep the
  28. underlines (****, etc.) in the same N_ call as the text lines they
  29. refer to, so translations can make the number of *'s or -'s match. */
  30. static char *info_internal_help_text[] = {
  31. N_("Basic Info command keys\n"),
  32. "\n",
  33. N_("\\%-10[quit-help] Close this help window.\n"),
  34. N_("\\%-10[quit] Quit Info altogether.\n"),
  35. N_("\\%-10[get-info-help-node] Invoke the Info tutorial.\n"),
  36. "\n",
  37. N_("\\%-10[prev-line] Move up one line.\n"),
  38. N_("\\%-10[next-line] Move down one line.\n"),
  39. N_("\\%-10[scroll-backward] Scroll backward one screenful.\n"),
  40. N_("\\%-10[scroll-forward] Scroll forward one screenful.\n"),
  41. N_("\\%-10[beginning-of-node] Go to the beginning of this node.\n"),
  42. N_("\\%-10[end-of-node] Go to the end of this node.\n"),
  43. "\n",
  44. N_("\\%-10[move-to-next-xref] Skip to the next hypertext link.\n"),
  45. N_("\\%-10[select-reference-this-line] Follow the hypertext link under the cursor.\n"),
  46. N_("\\%-10[history-node] Go back to the last node seen in this window.\n"),
  47. "\n",
  48. N_("\\%-10[global-prev-node] Go to the previous node in the document.\n"),
  49. N_("\\%-10[global-next-node] Go to the next node in the document.\n"),
  50. N_("\\%-10[prev-node] Go to the previous node on this level.\n"),
  51. N_("\\%-10[next-node] Go to the next node on this level.\n"),
  52. N_("\\%-10[up-node] Go up one level.\n"),
  53. N_("\\%-10[top-node] Go to the top node of this document.\n"),
  54. N_("\\%-10[dir-node] Go to the main 'directory' node.\n"),
  55. "\n",
  56. N_("1...9 Pick the first...ninth item in this node's menu.\n"),
  57. N_("\\%-10[last-menu-item] Pick the last item in this node's menu.\n"),
  58. N_("\\%-10[menu-item] Pick a menu item specified by name.\n"),
  59. N_("\\%-10[xref-item] Follow a cross reference specified by name.\n"),
  60. N_("\\%-10[goto-node] Go to a node specified by name.\n"),
  61. "\n",
  62. N_("\\%-10[search] Search forward for a specified string.\n"),
  63. N_("\\%-10[search-previous] Search for previous occurrence.\n"),
  64. N_("\\%-10[search-next] Search for next occurrence.\n"),
  65. N_("\\%-10[index-search] Search for a specified string in the index, and\n\
  66. select the node referenced by the first entry found.\n"),
  67. N_("\\%-10[virtual-index] Synthesize menu of matching index entries.\n"),
  68. "\n",
  69. N_("\\%-10[abort-key] Cancel the current operation.\n"),
  70. "\n",
  71. NULL
  72. };
  73. static char *where_is_internal (Keymap map, InfoCommand *cmd);
  74. static void
  75. dump_map_to_text_buffer (struct text_buffer *tb, int *prefix,
  76. int prefix_len, Keymap map)
  77. {
  78. register int i;
  79. int *new_prefix = xmalloc ((prefix_len + 2) * sizeof (int));
  80. memcpy (new_prefix, prefix, prefix_len * sizeof (int));
  81. new_prefix[prefix_len + 1] = 0;
  82. for (i = 0; i < KEYMAP_SIZE; i++)
  83. {
  84. if (i == 128)
  85. i = 256;
  86. if (i == 128 + KEYMAP_META_BASE)
  87. i = 256 + KEYMAP_META_BASE;
  88. new_prefix[prefix_len] = i;
  89. if (map[i].type == ISKMAP)
  90. {
  91. dump_map_to_text_buffer (tb, new_prefix, prefix_len + 1,
  92. map[i].value.keymap);
  93. }
  94. else if (map[i].value.function)
  95. {
  96. long start_of_line = tb->off;
  97. register int last;
  98. char *doc, *name;
  99. /* Hide some key mappings. */
  100. if (map[i].value.function
  101. && (map[i].value.function->func == info_do_lowercase_version))
  102. continue;
  103. doc = function_documentation (map[i].value.function);
  104. name = function_name (map[i].value.function);
  105. if (!*doc)
  106. continue;
  107. /* Find out if there is a series of identical functions, as in
  108. add-digit-to-numeric-arg. */
  109. for (last = i + 1; last < KEYMAP_SIZE; last++)
  110. if ((map[last].type != ISFUNC) ||
  111. (map[last].value.function != map[i].value.function))
  112. break;
  113. if (last - 1 != i)
  114. {
  115. text_buffer_printf (tb, "%s .. ", pretty_keyseq (new_prefix));
  116. new_prefix[prefix_len] = last - 1;
  117. text_buffer_printf (tb, "%s", pretty_keyseq (new_prefix));
  118. i = last - 1;
  119. }
  120. else
  121. text_buffer_printf (tb, "%s", pretty_keyseq (new_prefix));
  122. while (tb->off - start_of_line < 8)
  123. text_buffer_printf (tb, " ");
  124. /* Print the name of the function, and some padding before the
  125. documentation string is printed. */
  126. {
  127. int length_so_far;
  128. int desired_doc_start = 40;
  129. text_buffer_printf (tb, "(%s)", name);
  130. length_so_far = tb->off - start_of_line;
  131. if ((desired_doc_start + strlen (doc))
  132. >= (unsigned int) the_screen->width)
  133. text_buffer_printf (tb, "\n ");
  134. else
  135. {
  136. while (length_so_far < desired_doc_start)
  137. {
  138. text_buffer_printf (tb, " ");
  139. length_so_far++;
  140. }
  141. }
  142. }
  143. text_buffer_printf (tb, "%s\n", doc);
  144. }
  145. }
  146. free (new_prefix);
  147. }
  148. /* How to create internal_info_help_node. HELP_IS_ONLY_WINDOW_P says
  149. whether we're going to end up in a second (or more) window of our
  150. own, or whether there's only one window and we're going to usurp it.
  151. This determines how to quit the help window. Maybe we should just
  152. make q do the right thing in both cases. */
  153. static void
  154. create_internal_info_help_node (int help_is_only_window_p)
  155. {
  156. register int i;
  157. NODE *node;
  158. char *exec_keys;
  159. int printed_one_mx = 0;
  160. struct text_buffer msg;
  161. char *infopath_str = infopath_string ();
  162. text_buffer_init (&msg);
  163. for (i = 0; info_internal_help_text[i]; i++)
  164. text_buffer_printf (&msg, replace_in_documentation
  165. (_(info_internal_help_text[i]),
  166. help_is_only_window_p), NULL, NULL, NULL);
  167. text_buffer_printf (&msg, "---------------------\n");
  168. text_buffer_printf (&msg, _("This is GNU Info version %s. "), VERSION);
  169. text_buffer_printf (&msg, _("The current search path is:\n"));
  170. text_buffer_printf (&msg, "%s\n", infopath_str);
  171. text_buffer_printf (&msg, "---------------------\n\n");
  172. free (infopath_str);
  173. text_buffer_printf (&msg, _("Commands available in Info windows:\n\n"));
  174. dump_map_to_text_buffer (&msg, 0, 0, info_keymap);
  175. text_buffer_printf (&msg, "---------------------\n\n");
  176. text_buffer_printf (&msg, _("Commands available in the echo area:\n\n"));
  177. dump_map_to_text_buffer (&msg, 0, 0, echo_area_keymap);
  178. /* Get a list of commands which have no keystroke equivs. */
  179. exec_keys = where_is (info_keymap, InfoCmd(info_execute_command));
  180. if (exec_keys)
  181. exec_keys = xstrdup (exec_keys);
  182. for (i = 0; function_doc_array[i].func; i++)
  183. {
  184. InfoCommand *cmd = &function_doc_array[i];
  185. if (cmd->func != info_do_lowercase_version
  186. && !where_is_internal (info_keymap, cmd)
  187. && !where_is_internal (echo_area_keymap, cmd))
  188. {
  189. if (!printed_one_mx)
  190. {
  191. text_buffer_printf (&msg, "---------------------\n\n");
  192. if (exec_keys && exec_keys[0])
  193. text_buffer_printf (&msg,
  194. _("The following commands can only be invoked via "
  195. "%s:\n\n"),
  196. exec_keys);
  197. else
  198. text_buffer_printf (&msg,
  199. _("The following commands cannot be invoked at all:\n\n"));
  200. printed_one_mx = 1;
  201. }
  202. text_buffer_printf (&msg,
  203. "%s %s\n %s\n",
  204. exec_keys,
  205. function_doc_array[i].func_name,
  206. replace_in_documentation (strlen (function_doc_array[i].doc)
  207. ? _(function_doc_array[i].doc) : "", 0)
  208. );
  209. }
  210. }
  211. free (exec_keys);
  212. node = text_buffer_to_node (&msg);
  213. internal_info_help_node = node;
  214. name_internal_node (internal_info_help_node, xstrdup (info_help_nodename));
  215. }
  216. /* Return a window which is the window showing help in this Info. */
  217. /* If the eligible window's height is >= this, split it to make the help
  218. window. Otherwise display the help window in the current window. */
  219. #define HELP_SPLIT_SIZE 24
  220. static WINDOW *
  221. info_find_or_create_help_window (void)
  222. {
  223. int help_is_only_window_p;
  224. WINDOW *eligible = NULL;
  225. WINDOW *help_window = get_internal_info_window (info_help_nodename);
  226. /* Close help window if in it already. */
  227. if (help_window && help_window == active_window)
  228. {
  229. info_delete_window_internal (help_window);
  230. return NULL;
  231. }
  232. /* If we couldn't find the help window, then make it. */
  233. if (!help_window)
  234. {
  235. WINDOW *window;
  236. int max = 0;
  237. for (window = windows; window; window = window->next)
  238. {
  239. if (window->height > max)
  240. {
  241. max = window->height;
  242. eligible = window;
  243. }
  244. }
  245. if (!eligible)
  246. {
  247. info_error ("%s", msg_cant_make_help);
  248. return NULL;
  249. }
  250. }
  251. /* Make sure that we have a node containing the help text. The
  252. argument is false if help will be the only window (so l must be used
  253. to quit help), true if help will be one of several visible windows
  254. (so CTRL-x 0 must be used to quit help). */
  255. help_is_only_window_p = ((help_window && !windows->next)
  256. || (!help_window && eligible->height < HELP_SPLIT_SIZE));
  257. create_internal_info_help_node (help_is_only_window_p);
  258. /* Either use the existing window to display the help node, or create
  259. a new window if there was no existing help window. */
  260. if (!help_window)
  261. { /* Split the largest window into 2 windows, and show the help text
  262. in that window. */
  263. if (eligible->height >= HELP_SPLIT_SIZE)
  264. {
  265. active_window = eligible;
  266. help_window = window_make_window ();
  267. info_set_node_of_window (help_window, internal_info_help_node);
  268. }
  269. else
  270. {
  271. info_set_node_of_window (active_window, internal_info_help_node);
  272. help_window = active_window;
  273. }
  274. }
  275. else
  276. { /* Case where help node always gets regenerated, and we have an
  277. existing window in which to place the node. */
  278. if (active_window != help_window)
  279. {
  280. active_window = help_window;
  281. }
  282. info_set_node_of_window (active_window, internal_info_help_node);
  283. }
  284. return help_window;
  285. }
  286. /* Create or move to the help window. */
  287. DECLARE_INFO_COMMAND (info_get_help_window, _("Display help message"))
  288. {
  289. WINDOW *help_window;
  290. help_window = info_find_or_create_help_window ();
  291. if (help_window)
  292. {
  293. active_window = help_window;
  294. }
  295. }
  296. /* Show the Info help node. This means that the "info" file is installed
  297. where it can easily be found on your system. */
  298. DECLARE_INFO_COMMAND (info_get_info_help_node, _("Visit Info node '(info)Help'"))
  299. {
  300. NODE *node;
  301. char *nodename;
  302. /* If there is a window on the screen showing the node "(info)Help" or
  303. the node "(info)Help-Small-Screen", simply select that window. */
  304. {
  305. WINDOW *win;
  306. for (win = windows; win; win = win->next)
  307. {
  308. if (win->node && win->node->fullpath
  309. && !mbscasecmp ("info",
  310. filename_non_directory (win->node->fullpath))
  311. && (!strcmp (win->node->nodename, "Help")
  312. || !strcmp (win->node->nodename, "Help-Small-Screen")))
  313. {
  314. active_window = win;
  315. return;
  316. }
  317. }
  318. }
  319. /* If there is more than one window on the screen, check if the user typed
  320. "H" for help message before typing "h" for tutorial. If so, close help
  321. message so the tutorial will not be in a small window. */
  322. if (windows->next)
  323. {
  324. WINDOW *help_window = get_internal_info_window (info_help_nodename);
  325. if (help_window && help_window == active_window)
  326. {
  327. info_delete_window_internal (help_window);
  328. }
  329. }
  330. /* If the current window is small, show the small screen help. */
  331. if (active_window->height < 24)
  332. nodename = "Help-Small-Screen";
  333. else
  334. nodename = "Help";
  335. /* Try to get the info file for Info. */
  336. node = info_get_node ("info", nodename);
  337. /* info.info is distributed with Emacs, not Texinfo, so fall back to
  338. info-stnd.info if it isn't there. */
  339. if (!node)
  340. node = info_get_node ("info-stnd", "Top");
  341. if (!node)
  342. {
  343. if (info_recent_file_error)
  344. info_error ("%s", info_recent_file_error);
  345. else
  346. info_error (msg_cant_file_node, "info", nodename);
  347. return;
  348. }
  349. info_set_node_of_window (active_window, node);
  350. }
  351. /* **************************************************************** */
  352. /* */
  353. /* Groveling Info Keymaps and Docs */
  354. /* */
  355. /* **************************************************************** */
  356. /* Return the documentation associated with the Info command FUNCTION. */
  357. char *
  358. function_documentation (InfoCommand *cmd)
  359. {
  360. char *doc;
  361. doc = cmd->doc;
  362. return replace_in_documentation ((strlen (doc) == 0) ? doc : _(doc), 0);
  363. }
  364. /* Return the user-visible name of the function associated with the
  365. Info command FUNCTION. */
  366. char *
  367. function_name (InfoCommand *cmd)
  368. {
  369. return cmd->func_name;
  370. }
  371. /* Return a pointer to the info command for function NAME. */
  372. InfoCommand *
  373. named_function (char *name)
  374. {
  375. register int i;
  376. for (i = 0; function_doc_array[i].func; i++)
  377. if (strcmp (function_doc_array[i].func_name, name) == 0)
  378. break;
  379. if (!function_doc_array[i].func)
  380. return 0;
  381. else
  382. return &function_doc_array[i];
  383. }
  384. DECLARE_INFO_COMMAND (describe_key, _("Print documentation for KEY"))
  385. {
  386. int keys[50];
  387. int keystroke;
  388. int *k = keys;
  389. Keymap map = info_keymap;
  390. *k = '\0';
  391. for (;;)
  392. {
  393. message_in_echo_area (_("Describe key: %s"), pretty_keyseq (keys));
  394. keystroke = get_input_key ();
  395. unmessage_in_echo_area ();
  396. /* Add the KEYSTROKE to our list. */
  397. *k++ = keystroke;
  398. *k = '\0';
  399. if (map[keystroke].value.function == NULL)
  400. {
  401. message_in_echo_area (_("%s is undefined."), pretty_keyseq (keys));
  402. return;
  403. }
  404. else if (map[keystroke].type == ISKMAP)
  405. {
  406. map = map[keystroke].value.keymap;
  407. continue;
  408. }
  409. else
  410. {
  411. char *keyname, *message, *fundoc, *funname = "";
  412. /* If the key is bound to do-lowercase-version, but its
  413. lower-case variant is undefined, say that this key is
  414. also undefined. This is especially important for unbound
  415. edit keys that emit an escape sequence: it's terribly
  416. confusing to see a message "Home (do-lowercase-version)"
  417. or some such when Home is unbound. */
  418. if (map[keystroke].value.function
  419. && map[keystroke].value.function->func
  420. == info_do_lowercase_version)
  421. {
  422. int lowerkey;
  423. if (keystroke >= KEYMAP_META_BASE)
  424. {
  425. lowerkey = keystroke;
  426. lowerkey -= KEYMAP_META_BASE;
  427. lowerkey = tolower (lowerkey);
  428. lowerkey += KEYMAP_META_BASE;
  429. }
  430. else
  431. lowerkey = tolower (keystroke);
  432. if (map[lowerkey].value.function == NULL)
  433. {
  434. message_in_echo_area (_("%s is undefined."),
  435. pretty_keyseq (keys));
  436. return;
  437. }
  438. }
  439. keyname = pretty_keyseq (keys);
  440. funname = function_name (map[keystroke].value.function);
  441. fundoc = function_documentation (map[keystroke].value.function);
  442. message = xmalloc
  443. (10 + strlen (keyname) + strlen (fundoc) + strlen (funname));
  444. sprintf (message, "%s (%s): %s.", keyname, funname, fundoc);
  445. window_message_in_echo_area ("%s", message);
  446. free (message);
  447. break;
  448. }
  449. }
  450. }
  451. /* Return the pretty-printable name of a single key. */
  452. char *
  453. pretty_keyname (int key)
  454. {
  455. static char rep_buffer[30];
  456. char *rep;
  457. if (key >= KEYMAP_META_BASE)
  458. {
  459. char temp[20];
  460. rep = pretty_keyname (key - KEYMAP_META_BASE);
  461. sprintf (temp, "M-%s", rep);
  462. strcpy (rep_buffer, temp);
  463. rep = rep_buffer;
  464. }
  465. else if (Control_p (key))
  466. {
  467. switch (key)
  468. {
  469. case '\n': rep = "LFD"; break;
  470. case '\t': rep = "TAB"; break;
  471. case '\r': rep = "RET"; break;
  472. case ESC: rep = "ESC"; break;
  473. default:
  474. sprintf (rep_buffer, "C-%c", UnControl (key));
  475. rep = rep_buffer;
  476. }
  477. }
  478. else if (key >= 256)
  479. switch (key)
  480. {
  481. case KEY_RIGHT_ARROW:
  482. rep = "Right"; break;
  483. case KEY_LEFT_ARROW:
  484. rep = "Left"; break;
  485. case KEY_UP_ARROW:
  486. rep = "Up"; break;
  487. case KEY_DOWN_ARROW:
  488. rep = "Down"; break;
  489. case KEY_PAGE_UP:
  490. rep = "PgUp"; break;
  491. case KEY_PAGE_DOWN:
  492. rep = "PgDn"; break;
  493. case KEY_HOME:
  494. rep = "Home"; break;
  495. case KEY_END:
  496. rep = "End"; break;
  497. case KEY_DELETE:
  498. rep = "DEL"; break;
  499. case KEY_INSERT:
  500. rep = "INS"; break;
  501. case KEY_BACK_TAB:
  502. rep = "BackTab"; break;
  503. case KEY_MOUSE:
  504. rep = "(mouse event)"; break;
  505. default:
  506. rep = "(unknown key)"; break; /* This shouldn't be displayed. */
  507. }
  508. else
  509. {
  510. switch (key)
  511. {
  512. case ' ': rep = "SPC"; break;
  513. case DEL: rep = "DEL"; break;
  514. default:
  515. rep_buffer[0] = key;
  516. rep_buffer[1] = '\0';
  517. rep = rep_buffer;
  518. }
  519. }
  520. return rep;
  521. }
  522. /* Return the pretty printable string which represents KEYSEQ. Return
  523. value should not be freed by caller. */
  524. char *
  525. pretty_keyseq (int *keyseq)
  526. {
  527. static struct text_buffer rep = { 0 };
  528. if (!text_buffer_base (&rep))
  529. text_buffer_init (&rep);
  530. else
  531. text_buffer_reset (&rep);
  532. if (!*keyseq)
  533. return "";
  534. while (1)
  535. {
  536. text_buffer_printf (&rep, "%s", pretty_keyname (keyseq[0]));
  537. keyseq++;
  538. if (!*keyseq)
  539. break;
  540. text_buffer_add_char (&rep, ' ');
  541. }
  542. return text_buffer_base (&rep);
  543. }
  544. /* Return a pointer to the last character in s that is found in f. */
  545. static const char *
  546. strrpbrk (const char *s, const char *f)
  547. {
  548. register const char *e = s + strlen(s);
  549. register const char *t;
  550. while (e-- != s)
  551. {
  552. for (t = f; *t; t++)
  553. if (*e == *t)
  554. return e;
  555. }
  556. return NULL;
  557. }
  558. /* Replace the names of functions with the key that invokes them. */
  559. char *
  560. replace_in_documentation (const char *string, int help_is_only_window_p)
  561. {
  562. unsigned reslen = strlen (string);
  563. register int i, start, next;
  564. static char *result = NULL;
  565. free (result);
  566. result = xmalloc (1 + reslen);
  567. next = start = 0;
  568. /* Skip to the beginning of a replaceable function. */
  569. for (i = start; string[i]; i++)
  570. {
  571. int j = i + 1;
  572. /* Is this the start of a replaceable function name? */
  573. if (string[i] == '\\')
  574. {
  575. char *fmt = NULL;
  576. unsigned min = 0;
  577. unsigned max = 0;
  578. if(string[j] == '%')
  579. {
  580. if (string[++j] == '-')
  581. j++;
  582. if (isdigit(string[j]))
  583. {
  584. min = atoi(string + j);
  585. while (isdigit(string[j]))
  586. j++;
  587. if (string[j] == '.' && isdigit(string[j + 1]))
  588. {
  589. j += 1;
  590. max = atoi(string + j);
  591. while (isdigit(string[j]))
  592. j++;
  593. }
  594. fmt = xmalloc (j - i + 2);
  595. strncpy (fmt, string + i + 1, j - i);
  596. fmt[j - i - 1] = 's';
  597. fmt[j - i] = '\0';
  598. }
  599. else
  600. j = i + 1;
  601. }
  602. if (string[j] == '[')
  603. {
  604. unsigned arg = 0;
  605. char *argstr = NULL;
  606. char *rep_name, *fun_name, *rep;
  607. InfoCommand *command;
  608. char *repstr = NULL;
  609. unsigned replen;
  610. /* Copy in the old text. */
  611. strncpy (result + next, string + start, i - start);
  612. next += (i - start);
  613. start = j + 1;
  614. /* Look for an optional numeric arg. */
  615. i = start;
  616. if (isdigit(string[i])
  617. || (string[i] == '-' && isdigit(string[i + 1])) )
  618. {
  619. arg = atoi(string + i);
  620. if (string[i] == '-')
  621. i++;
  622. while (isdigit(string[i]))
  623. i++;
  624. }
  625. start = i;
  626. /* Move to the end of the function name. */
  627. for (i = start; string[i] && (string[i] != ']'); i++);
  628. rep_name = xmalloc (1 + i - start);
  629. strncpy (rep_name, string + start, i - start);
  630. rep_name[i - start] = '\0';
  631. /* If we have only one window (because the window size was too
  632. small to split it), we have to quit help by going back one
  633. node in the history list, not deleting the window. */
  634. if (strcmp (rep_name, "quit-help") == 0)
  635. fun_name = help_is_only_window_p ? "history-node"
  636. : "get-help-window";
  637. else
  638. fun_name = rep_name;
  639. /* Find a key which invokes this function in the info_keymap. */
  640. command = named_function (fun_name);
  641. free (rep_name);
  642. /* If the internal documentation string fails, there is a
  643. serious problem with the associated command's documentation.
  644. We croak so that it can be fixed immediately. */
  645. if (!command)
  646. abort ();
  647. if (arg)
  648. {
  649. char *argrep;
  650. const char *p;
  651. argrep = where_is (info_keymap, InfoCmd(info_add_digit_to_numeric_arg));
  652. p = argrep ? strrpbrk (argrep, "0123456789-") : NULL;
  653. if (p)
  654. {
  655. argstr = xmalloc (p - argrep + 21);
  656. strncpy (argstr, argrep, p - argrep);
  657. sprintf (argstr + (p - argrep), "%d", arg);
  658. }
  659. else
  660. command = NULL;
  661. }
  662. rep = command ? where_is (info_keymap, command) : NULL;
  663. if (!rep)
  664. rep = "N/A";
  665. replen = (argstr ? strlen (argstr) : 0) + strlen (rep) + 1;
  666. repstr = xmalloc (replen);
  667. repstr[0] = '\0';
  668. if (argstr)
  669. {
  670. strcat(repstr, argstr);
  671. strcat(repstr, " ");
  672. free (argstr);
  673. }
  674. strcat(repstr, rep);
  675. if (fmt)
  676. {
  677. if (replen > max)
  678. replen = max;
  679. if (replen < min)
  680. replen = min;
  681. }
  682. if (next + replen > reslen)
  683. {
  684. reslen = next + replen + 1;
  685. result = xrealloc (result, reslen + 1);
  686. }
  687. if (fmt)
  688. sprintf (result + next, fmt, repstr);
  689. else
  690. strcpy (result + next, repstr);
  691. next = strlen (result);
  692. free (repstr);
  693. start = i;
  694. if (string[i])
  695. start++;
  696. }
  697. free (fmt);
  698. }
  699. }
  700. strcpy (result + next, string + start);
  701. return result;
  702. }
  703. /* Return a string of characters which could be typed from the keymap
  704. MAP to invoke FUNCTION. */
  705. static char *where_is_rep = NULL;
  706. static int where_is_rep_index = 0;
  707. static int where_is_rep_size = 0;
  708. char *
  709. where_is (Keymap map, InfoCommand *cmd)
  710. {
  711. char *rep;
  712. if (!where_is_rep_size)
  713. where_is_rep = xmalloc (where_is_rep_size = 100);
  714. where_is_rep_index = 0;
  715. rep = where_is_internal (map, cmd);
  716. /* If it couldn't be found, return "M-x Foo" (or equivalent). */
  717. if (!rep)
  718. {
  719. char *name;
  720. name = function_name (cmd);
  721. if (!name)
  722. return NULL; /* no such function */
  723. rep = where_is_internal (map, InfoCmd(info_execute_command));
  724. if (!rep)
  725. return ""; /* function exists but can't be got to by user */
  726. sprintf (where_is_rep, "%s %s", rep, name);
  727. rep = where_is_rep;
  728. }
  729. return rep;
  730. }
  731. /* Return the printed rep of the keystrokes that invoke FUNCTION,
  732. as found in MAP, or NULL. */
  733. static char *
  734. where_is_internal (Keymap map, InfoCommand *cmd)
  735. {
  736. register FUNCTION_KEYSEQ *k;
  737. for (k = cmd->keys; k; k = k->next)
  738. if (k->map == map)
  739. return pretty_keyseq (k->keyseq);
  740. return NULL;
  741. }
  742. DECLARE_INFO_COMMAND (info_where_is,
  743. _("Show what to type to execute a given command"))
  744. {
  745. char *command_name;
  746. command_name = read_function_name (_("Where is command: "), window);
  747. if (!command_name)
  748. {
  749. info_abort_key (active_window, count);
  750. return;
  751. }
  752. if (*command_name)
  753. {
  754. InfoCommand *command;
  755. command = named_function (command_name);
  756. if (command)
  757. {
  758. char *location;
  759. location = where_is (info_keymap, command);
  760. if (!location || !location[0])
  761. {
  762. info_error (_("'%s' is not on any keys"), command_name);
  763. }
  764. else
  765. {
  766. if (strstr (location, function_name (command)))
  767. window_message_in_echo_area
  768. (_("%s can only be invoked via %s."),
  769. command_name, location);
  770. else
  771. window_message_in_echo_area
  772. (_("%s can be invoked via %s."),
  773. command_name, location);
  774. }
  775. }
  776. else
  777. info_error (_("There is no function named '%s'"), command_name);
  778. }
  779. free (command_name);
  780. }