info.c 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205
  1. /* info.c -- Display nodes of Info files in multiple windows.
  2. $Id$
  3. Copyright 1993, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
  4. 2004, 2005, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015,
  5. 2016, 2017 Free Software Foundation, Inc.
  6. This program is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. Originally written by Brian Fox. */
  17. #include "info.h"
  18. #include "filesys.h"
  19. #include "info-utils.h"
  20. #include "session.h"
  21. #include "indices.h"
  22. #include "dribble.h"
  23. #include "getopt.h"
  24. #include "man.h"
  25. #include "variables.h"
  26. char *program_name = "info";
  27. /* Non-zero means search all indices for APROPOS_SEARCH_STRING. */
  28. static int apropos_p = 0;
  29. /* Variable containing the string to search for when apropos_p is non-zero. */
  30. static char *apropos_search_string = NULL;
  31. /* Non-zero means search all indices for INDEX_SEARCH_STRING. Unlike
  32. apropos, this puts the user at the node, running info. */
  33. static int index_search_p = 0;
  34. /* Non-zero means look for the node which describes the invocation
  35. and command-line options of the program, and start the info
  36. session at that node. */
  37. static int goto_invocation_p = 0;
  38. static char *invocation_program_name = 0;
  39. /* Variable containing the string to search for when index_search_p is
  40. non-zero. */
  41. static char *index_search_string = NULL;
  42. /* Non-zero means print version info only. */
  43. static int print_version_p = 0;
  44. /* Non-zero means print a short description of the options. */
  45. static int print_help_p = 0;
  46. /* Name of file to start session with. Default file for --node arguments. */
  47. static char *initial_file = 0;
  48. /* Array of the names of nodes that the user specified with "--node" on the
  49. command line. */
  50. static char **user_nodenames = NULL;
  51. static size_t user_nodenames_index = 0;
  52. static size_t user_nodenames_slots = 0;
  53. /* References to the nodes to start the session with. */
  54. static REFERENCE **ref_list = NULL;
  55. static size_t ref_slots = 0;
  56. static size_t ref_index = 0;
  57. /* String specifying the first file to load. This string can only be set
  58. by the user specifying "--file" on the command line. */
  59. static char *user_filename = NULL;
  60. /* String specifying the name of the file to dump nodes to. This value is
  61. filled if the user speficies "--output" on the command line. */
  62. static char *user_output_filename = NULL;
  63. /* Non-zero indicates that when "--output" is specified, all of the menu
  64. items of the specified nodes (and their subnodes as well) should be
  65. dumped in the order encountered. This basically can print a book. */
  66. int dump_subnodes = 0;
  67. /* Non-zero means make default keybindings be loosely modeled on vi(1). */
  68. int vi_keys_p = 0;
  69. /* Non-zero means don't remove ANSI escape sequences. */
  70. int raw_escapes_p = 1;
  71. /* Print/visit all matching documents. */
  72. static int all_matches_p = 0;
  73. /* Non-zero means print the absolute location of the file to be loaded. */
  74. static int print_where_p = 0;
  75. /* Non-zero means don't try to be smart when searching for nodes. */
  76. int strict_node_location_p = 0;
  77. #if defined(__MSDOS__) || defined(__MINGW32__)
  78. /* Non-zero indicates that screen output should be made 'speech-friendly'.
  79. Since on MSDOS the usual behavior is to write directly to the video
  80. memory, speech synthesizer software cannot grab the output. Therefore,
  81. we provide a user option which tells us to avoid direct screen output
  82. and use stdout instead (which loses the color output). */
  83. int speech_friendly = 0;
  84. #endif
  85. /* Structure describing the options that Info accepts. We pass this structure
  86. to getopt_long (). If you add or otherwise change this structure, you must
  87. also change the string which follows it. */
  88. #define DRIBBLE_OPTION 2
  89. #define RESTORE_OPTION 3
  90. #define IDXSRCH_OPTION 4
  91. #define INITFLE_OPTION 5
  92. #define VIRTIDX_OPTION 6
  93. static struct option long_options[] = {
  94. { "all", 0, 0, 'a' },
  95. { "apropos", 1, 0, 'k' },
  96. { "debug", 1, 0, 'x' },
  97. { "directory", 1, 0, 'd' },
  98. { "dribble", 1, 0, DRIBBLE_OPTION },
  99. { "file", 1, 0, 'f' },
  100. { "help", 0, &print_help_p, 1 },
  101. { "index-search", 1, 0, IDXSRCH_OPTION },
  102. { "init-file", 1, 0, INITFLE_OPTION },
  103. { "location", 0, &print_where_p, 1 },
  104. { "node", 1, 0, 'n' },
  105. { "output", 1, 0, 'o' },
  106. { "raw-escapes", 0, &raw_escapes_p, 1 },
  107. { "no-raw-escapes", 0, &raw_escapes_p, 0 },
  108. { "show-malformed-multibytes", 0, &show_malformed_multibyte_p, 1 },
  109. { "no-show-malformed-multibytes", 0, &show_malformed_multibyte_p, 0 },
  110. { "restore", 1, 0, RESTORE_OPTION },
  111. { "show-options", 0, 0, 'O' },
  112. { "strict-node-location", 0, &strict_node_location_p, 1 },
  113. { "subnodes", 0, &dump_subnodes, 1 },
  114. { "usage", 0, 0, 'O' },
  115. { "variable", 1, 0, 'v' },
  116. { "version", 0, &print_version_p, 1 },
  117. { "vi-keys", 0, &vi_keys_p, 1 },
  118. { "where", 0, &print_where_p, 1 },
  119. #if defined(__MSDOS__) || defined(__MINGW32__)
  120. { "speech-friendly", 0, &speech_friendly, 1 },
  121. #endif
  122. {NULL, 0, NULL, 0}
  123. };
  124. /* String describing the shorthand versions of the long options found above. */
  125. #if defined(__MSDOS__) || defined(__MINGW32__)
  126. static char *short_options = "ak:d:n:f:ho:ORsv:wbx:";
  127. #else
  128. static char *short_options = "ak:d:n:f:ho:ORv:wsx:";
  129. #endif
  130. /* When non-zero, the Info window system has been initialized. */
  131. int info_windows_initialized_p = 0;
  132. /* Some "forward" declarations. */
  133. static void info_short_help (void);
  134. static void init_messages (void);
  135. /* Find the first file to load (and possibly the first node as well).
  136. If the --file option is given, use that as the file, otherwise try to
  137. interpret the first non-option argument, either by looking it up as a dir
  138. entry, looking for a file by that name, or finding a man page by that name.
  139. Set INITIAL_FILE to the name of the initial Info file. */
  140. static void
  141. get_initial_file (int *argc, char ***argv, char **error)
  142. {
  143. REFERENCE *entry;
  144. /* User used "--file". */
  145. if (user_filename)
  146. {
  147. if (!IS_ABSOLUTE(user_filename) && HAS_SLASH(user_filename)
  148. && !(user_filename[0] == '.' && IS_SLASH(user_filename[1])))
  149. {
  150. /* Prefix "./" to the filename to prevent a lookup
  151. in INFOPATH. */
  152. char *s;
  153. asprintf (&s, "%s%s", "./", user_filename);
  154. free (user_filename);
  155. user_filename = s;
  156. }
  157. if (IS_ABSOLUTE(user_filename) || HAS_SLASH(user_filename))
  158. initial_file = info_add_extension (0, user_filename, 0);
  159. else
  160. initial_file = info_find_fullpath (user_filename, 0);
  161. if (!initial_file)
  162. {
  163. if (!filesys_error_number)
  164. filesys_error_number = ENOENT;
  165. *error = filesys_error_string (user_filename, filesys_error_number);
  166. }
  167. return;
  168. }
  169. if (!(*argv)[0])
  170. {
  171. /* No more non-option arguments. */
  172. initial_file = xstrdup("dir");
  173. return;
  174. }
  175. /* If first argument begins with '(', add it as if it were given with
  176. '--node'. This is to support invoking like
  177. "info '(emacs)Buffers'". If it is a well-formed node spec then
  178. the rest of the arguments are menu entries to follow, or an
  179. index entry. */
  180. if ((*argv)[0][0] == '(')
  181. {
  182. info_parse_node ((*argv)[0]);
  183. if (info_parsed_filename)
  184. {
  185. initial_file = info_find_fullpath (info_parsed_filename, 0);
  186. if (initial_file)
  187. {
  188. add_pointer_to_array (info_new_reference (initial_file,
  189. info_parsed_nodename),
  190. ref_index, ref_list, ref_slots, 2);
  191. /* Remove this argument from the argument list. */
  192. memmove (*argv, *argv + 1, *argc-- * sizeof (char *));
  193. return;
  194. }
  195. }
  196. }
  197. /* If there are any more arguments, the initial file is the
  198. dir entry given by the first one. */
  199. {
  200. /* If they say info info (or info -O info, etc.), show them
  201. info-stnd.texi. (Get info.texi with info -f info.) */
  202. if ((*argv)[0] && mbscasecmp ((*argv)[0], "info") == 0)
  203. (*argv)[0] = "info-stnd";
  204. entry = lookup_dir_entry ((*argv)[0], 0);
  205. if (entry)
  206. {
  207. initial_file = info_find_fullpath (entry->filename, 0);
  208. if (initial_file)
  209. {
  210. REFERENCE *copy;
  211. (*argv)++; /* Advance past first remaining argument. */
  212. (*argc)--;
  213. copy = info_copy_reference (entry);
  214. /* Store full path, so that we find the already loaded file in
  215. info_find_file, and show the full path if --where is used. */
  216. free (copy->filename);
  217. copy->filename = xstrdup (initial_file);
  218. add_pointer_to_array (copy, ref_index, ref_list, ref_slots, 2);
  219. return;
  220. }
  221. }
  222. }
  223. /* File name lookup. */
  224. {
  225. /* Try finding a file with this name, in case
  226. it exists, but wasn't listed in dir. */
  227. initial_file = info_find_fullpath ((*argv)[0], 0);
  228. if (initial_file)
  229. {
  230. add_pointer_to_array (info_new_reference ((*argv)[0], "Top"),
  231. ref_index, ref_list, ref_slots, 2);
  232. (*argv)++; /* Advance past first remaining argument. */
  233. (*argc)--;
  234. return;
  235. }
  236. else
  237. asprintf (error, _("No menu item '%s' in node '%s'."),
  238. (*argv)[0], "(dir)Top");
  239. }
  240. /* Fall back to loading man page. */
  241. {
  242. NODE *man_node;
  243. debug (3, ("falling back to manpage node"));
  244. man_node = get_manpage_node ((*argv)[0]);
  245. if (man_node)
  246. {
  247. add_pointer_to_array
  248. (info_new_reference (MANPAGE_FILE_BUFFER_NAME, (*argv)[0]),
  249. ref_index, ref_list, ref_slots, 2);
  250. initial_file = MANPAGE_FILE_BUFFER_NAME;
  251. return;
  252. }
  253. }
  254. /* Inexact dir lookup. */
  255. {
  256. entry = lookup_dir_entry ((*argv)[0], 1);
  257. if (entry)
  258. {
  259. initial_file = info_find_fullpath (entry->filename, 0);
  260. if (initial_file)
  261. {
  262. REFERENCE *copy;
  263. (*argv)++; /* Advance past first remaining argument. */
  264. (*argc)--;
  265. /* Clear error message. */
  266. free (*error);
  267. *error = 0;
  268. copy = info_copy_reference (entry);
  269. /* Store full path, so that we find the already loaded file in
  270. info_find_file, and show the full path if --where is used. */
  271. free (copy->filename);
  272. copy->filename = initial_file;
  273. add_pointer_to_array (copy, ref_index, ref_list, ref_slots, 2);
  274. return;
  275. }
  276. }
  277. }
  278. return;
  279. }
  280. /* Expand list of nodes to be loaded. */
  281. static void
  282. add_initial_nodes (int argc, char **argv, char **error)
  283. {
  284. /* Add nodes specified with --node. */
  285. if (user_nodenames)
  286. {
  287. int i;
  288. /* If any --node arguments were given, the node in ref_list[0] is only
  289. used to set initial_file. */
  290. if (user_nodenames_index > 0 && ref_index > 0)
  291. {
  292. info_reference_free (ref_list[0]);
  293. ref_list[0] = 0;
  294. ref_index = 0;
  295. }
  296. for (i = 0; user_nodenames[i]; i++)
  297. {
  298. char *node_filename = 0;
  299. char *node_nodename = 0;
  300. /* Parse node spec to support invoking
  301. like info --node "(emacs)Buffers". */
  302. info_parse_node (user_nodenames[i]);
  303. if (info_parsed_filename)
  304. {
  305. node_filename = info_parsed_filename;
  306. node_nodename = info_parsed_nodename;
  307. }
  308. else
  309. {
  310. FILE_BUFFER *file_buffer;
  311. TAG *tag;
  312. int j;
  313. if (!initial_file)
  314. continue; /* Shouldn't happen. */
  315. /* Check for a node by this name, and if there isn't one
  316. look for an inexact match. */
  317. node_filename = initial_file;
  318. node_nodename = 0;
  319. file_buffer = info_find_file (node_filename);
  320. /* First look for an exact match. */
  321. for (j = 0; (tag = file_buffer->tags[j]); j++)
  322. if (strcmp (user_nodenames[i], tag->nodename) == 0)
  323. {
  324. node_nodename = tag->nodename;
  325. break;
  326. }
  327. if (!node_nodename)
  328. {
  329. int best_guess = -1;
  330. int len = strlen (user_nodenames[i]);
  331. for (j = 0; (tag = file_buffer->tags[j]); j++)
  332. {
  333. if (mbscasecmp (user_nodenames[i], tag->nodename) == 0)
  334. {
  335. /* Exact, case-insensitive match. */
  336. node_nodename = tag->nodename;
  337. best_guess = -1;
  338. break;
  339. }
  340. else if (best_guess == -1
  341. && (mbsncasecmp (user_nodenames[i],
  342. tag->nodename, len) == 0))
  343. /* Case-insensitive initial substring. */
  344. best_guess = j;
  345. }
  346. if (best_guess != -1)
  347. {
  348. node_nodename = file_buffer->tags[best_guess]->nodename;
  349. }
  350. }
  351. if (!node_nodename)
  352. {
  353. free (*error);
  354. asprintf (error, _("Cannot find node '%s'."),
  355. user_nodenames[i]);
  356. continue;
  357. }
  358. }
  359. if (node_filename && node_nodename)
  360. add_pointer_to_array
  361. (info_new_reference (node_filename, node_nodename),
  362. ref_index, ref_list, ref_slots, 2);
  363. }
  364. }
  365. if (goto_invocation_p)
  366. {
  367. NODE *top_node = 0;
  368. REFERENCE *invoc_ref = 0;
  369. char *program;
  370. if (ref_index == 0)
  371. {
  372. info_error (_("No program name given."));
  373. exit (1);
  374. }
  375. if (invocation_program_name)
  376. program = xstrdup (invocation_program_name);
  377. else if (ref_list[0] && ref_list[0]->filename)
  378. /* If there's no command-line arguments to
  379. supply the program name, use the Info file
  380. name (sans extension and leading directories)
  381. instead. */
  382. program = program_name_from_file_name (ref_list[0]->filename);
  383. else
  384. program = xstrdup ("");
  385. if (ref_index > 0)
  386. top_node = info_get_node (ref_list[0]->filename,
  387. ref_list[0]->nodename);
  388. if (top_node)
  389. invoc_ref = info_intuit_options_node (top_node, program);
  390. if (invoc_ref)
  391. {
  392. info_reference_free (ref_list[0]);
  393. ref_index = 0;
  394. add_pointer_to_array (invoc_ref, ref_index, ref_list, ref_slots, 2);
  395. }
  396. free (program);
  397. }
  398. /* Default is the "Top" node if there were no other nodes. */
  399. if (ref_index == 0 && initial_file)
  400. {
  401. add_pointer_to_array (info_new_reference (initial_file, "Top"),
  402. ref_index, ref_list, ref_slots, 2);
  403. }
  404. /* If there are arguments remaining, they are the names of menu items
  405. in sequential info files starting from the first one loaded. */
  406. if (*argv && ref_index > 0)
  407. {
  408. NODE *initial_node; /* Node to start following menus from. */
  409. NODE *node_via_menus;
  410. initial_node = info_get_node_with_defaults (ref_list[0]->filename,
  411. ref_list[0]->nodename, 0);
  412. if (!initial_node)
  413. return;
  414. node_via_menus = info_follow_menus (initial_node, argv, error, 1);
  415. if (node_via_menus)
  416. {
  417. argv += argc; argc = 0;
  418. info_reference_free (ref_list[0]);
  419. ref_list[0] = info_new_reference (node_via_menus->fullpath,
  420. node_via_menus->nodename);
  421. free_history_node (node_via_menus);
  422. }
  423. /* If no nodes found, and there is exactly one argument remaining,
  424. check for it as an index entry. */
  425. else if (argc == 1 && argv[0])
  426. {
  427. FILE_BUFFER *fb;
  428. REFERENCE *match;
  429. debug (3, ("looking in indices"));
  430. fb = info_find_file (ref_list[0]->filename);
  431. if (fb)
  432. {
  433. match = look_in_indices (fb, argv[0], 0);
  434. if (match)
  435. {
  436. argv += argc; argc = 0;
  437. free (*error); *error = 0;
  438. info_reference_free (ref_list[0]);
  439. ref_list[0] = info_copy_reference (match);
  440. }
  441. }
  442. }
  443. /* If there are arguments remaining, follow menus inexactly. */
  444. if (argc != 0)
  445. {
  446. initial_node = info_get_node_with_defaults (ref_list[0]->filename,
  447. ref_list[0]->nodename,
  448. 0);
  449. free (*error); *error = 0;
  450. node_via_menus = info_follow_menus (initial_node, argv, error, 0);
  451. if (node_via_menus)
  452. {
  453. if (argc >= 2 || !*error)
  454. {
  455. argv += argc; argc = 0;
  456. info_reference_free (ref_list[0]);
  457. ref_list[0] = info_new_reference (node_via_menus->fullpath,
  458. node_via_menus->nodename);
  459. }
  460. free_history_node (node_via_menus);
  461. }
  462. }
  463. /* If still no nodes found, and there is exactly one argument remaining,
  464. look in indices sloppily. */
  465. if (argc == 1)
  466. {
  467. FILE_BUFFER *fb;
  468. REFERENCE *nearest;
  469. debug (3, ("looking in indices sloppily"));
  470. fb = info_find_file (ref_list[0]->filename);
  471. if (fb)
  472. {
  473. nearest = look_in_indices (fb, argv[0], 1);
  474. if (nearest)
  475. {
  476. argv += argc; argc = 0;
  477. free (*error); *error = 0;
  478. info_reference_free (ref_list[0]);
  479. ref_list[0] = info_copy_reference (nearest);
  480. }
  481. }
  482. }
  483. }
  484. return;
  485. }
  486. static void
  487. info_find_matching_files (char *filename)
  488. {
  489. int i;
  490. char *searchdir;
  491. NODE *man_node;
  492. /* Check for dir entries first. */
  493. i = 0;
  494. for (searchdir = infopath_first (&i); searchdir;
  495. searchdir = infopath_next (&i))
  496. {
  497. REFERENCE *new_ref = dir_entry_of_infodir (filename, searchdir);
  498. if (new_ref)
  499. add_pointer_to_array (new_ref, ref_index, ref_list, ref_slots, 2);
  500. }
  501. /* Look for files with matching names. */
  502. i = 0;
  503. while (1)
  504. {
  505. char *p;
  506. int j;
  507. p = info_file_find_next_in_path (filename, &i, 0);
  508. if (!p)
  509. break;
  510. /* Add to list only if the file is not in the list already (which would
  511. happen if there was a dir entry with the label and filename both
  512. being this file). */
  513. for (j = 0; j < ref_index; j++)
  514. {
  515. if (!strcmp (p, ref_list[j]->filename))
  516. break;
  517. }
  518. if (j == ref_index)
  519. {
  520. add_pointer_to_array (info_new_reference (p, 0),
  521. ref_index, ref_list, ref_slots, 2);
  522. }
  523. free (p);
  524. }
  525. /* Check for man page. */
  526. man_node = get_manpage_node (filename);
  527. if (man_node)
  528. {
  529. free (man_node);
  530. add_pointer_to_array
  531. (info_new_reference (MANPAGE_FILE_BUFFER_NAME, filename),
  532. ref_index, ref_list, ref_slots, 2);
  533. }
  534. }
  535. static void
  536. set_debug_level (const char *arg)
  537. {
  538. char *p;
  539. long n = strtol (arg, &p, 10);
  540. if (*p)
  541. {
  542. fprintf (stderr, _("invalid number: %s\n"), arg);
  543. exit (EXIT_FAILURE);
  544. }
  545. if (n < 0 || n > UINT_MAX)
  546. debug_level = UINT_MAX;
  547. else
  548. debug_level = n;
  549. }
  550. static void
  551. add_file_directory_to_path (char *filename)
  552. {
  553. char *directory_name = xstrdup (filename);
  554. char *temp = filename_non_directory (directory_name);
  555. if (temp != directory_name)
  556. {
  557. if (HAVE_DRIVE (directory_name) && temp == directory_name + 2)
  558. {
  559. /* The directory of "d:foo" is stored as "d:.", to avoid
  560. mixing it with "d:/" when a slash is appended. */
  561. *temp = '.';
  562. temp += 2;
  563. }
  564. temp[-1] = 0;
  565. infopath_add (directory_name);
  566. }
  567. free (directory_name);
  568. }
  569. /* **************************************************************** */
  570. /* */
  571. /* Main Entry Point to the Info Program */
  572. /* */
  573. /* **************************************************************** */
  574. int
  575. main (int argc, char *argv[])
  576. {
  577. int getopt_long_index; /* Index returned by getopt_long (). */
  578. char *init_file = 0; /* Name of init file specified. */
  579. char *error = 0; /* Error message to display in mini-buffer. */
  580. #ifdef HAVE_SETLOCALE
  581. /* Set locale via LC_ALL. */
  582. setlocale (LC_ALL, "");
  583. #endif /* HAVE_SETLOCALE */
  584. #ifdef ENABLE_NLS
  585. /* Set the text message domain. */
  586. bindtextdomain (PACKAGE, LOCALEDIR);
  587. textdomain (PACKAGE);
  588. #endif
  589. init_messages ();
  590. while (1)
  591. {
  592. int option_character;
  593. option_character = getopt_long (argc, argv, short_options, long_options,
  594. &getopt_long_index);
  595. /* getopt_long returns EOF when there are no more long options. */
  596. if (option_character == EOF)
  597. break;
  598. /* If this is a long option, then get the short version of it. */
  599. if (option_character == 0 && long_options[getopt_long_index].flag == 0)
  600. option_character = long_options[getopt_long_index].val;
  601. /* Case on the option that we have received. */
  602. switch (option_character)
  603. {
  604. case 0:
  605. break;
  606. case 'a':
  607. all_matches_p = 1;
  608. break;
  609. /* User wants to add a directory. */
  610. case 'd':
  611. infopath_add (optarg);
  612. break;
  613. /* User is specifying a particular node. */
  614. case 'n':
  615. add_pointer_to_array (optarg, user_nodenames_index, user_nodenames,
  616. user_nodenames_slots, 10);
  617. break;
  618. /* User is specifying a particular Info file. */
  619. case 'f':
  620. if (user_filename)
  621. free (user_filename);
  622. user_filename = xstrdup (optarg);
  623. break;
  624. /* Treat -h like --help. */
  625. case 'h':
  626. print_help_p = 1;
  627. break;
  628. /* User is specifying the name of a file to output to. */
  629. case 'o':
  630. if (user_output_filename)
  631. free (user_output_filename);
  632. user_output_filename = xstrdup (optarg);
  633. break;
  634. /* User has specified that she wants to find the "Options"
  635. or "Invocation" node for the program. */
  636. case 'O':
  637. goto_invocation_p = 1;
  638. break;
  639. /* User has specified that she wants the escape sequences
  640. in man pages to be passed thru unaltered. */
  641. case 'R':
  642. raw_escapes_p = 1;
  643. break;
  644. /* User is specifying that she wishes to dump the subnodes of
  645. the node that she is dumping. */
  646. case 's':
  647. dump_subnodes = 1;
  648. break;
  649. /* For compatibility with man, -w is --where. */
  650. case 'w':
  651. print_where_p = 1;
  652. break;
  653. #if defined(__MSDOS__) || defined(__MINGW32__)
  654. /* User wants speech-friendly output. */
  655. case 'b':
  656. speech_friendly = 1;
  657. break;
  658. #endif /* __MSDOS__ || __MINGW32__ */
  659. /* User has specified a string to search all indices for. */
  660. case 'k':
  661. apropos_p = 1;
  662. free (apropos_search_string);
  663. apropos_search_string = xstrdup (optarg);
  664. break;
  665. /* User has specified a dribble file to receive keystrokes. */
  666. case DRIBBLE_OPTION:
  667. close_dribble_file ();
  668. open_dribble_file (optarg);
  669. break;
  670. /* User has specified an alternate input stream. */
  671. case RESTORE_OPTION:
  672. info_set_input_from_file (optarg);
  673. break;
  674. /* User has specified a string to search all indices for. */
  675. case IDXSRCH_OPTION:
  676. index_search_p = 1;
  677. free (index_search_string);
  678. index_search_string = xstrdup (optarg);
  679. break;
  680. /* User has specified a file to use as the init file. */
  681. case INITFLE_OPTION:
  682. init_file = optarg;
  683. break;
  684. case 'v':
  685. {
  686. VARIABLE_ALIST *var;
  687. char *p;
  688. p = strchr (optarg, '=');
  689. if (!p)
  690. {
  691. info_error (_("malformed variable assignment: %s"), optarg);
  692. exit (EXIT_FAILURE);
  693. }
  694. *p++ = 0;
  695. if (!(var = variable_by_name (optarg)))
  696. {
  697. info_error (_("%s: no such variable"), optarg);
  698. exit (EXIT_FAILURE);
  699. }
  700. if (!set_variable_to_value (var, p, SET_ON_COMMAND_LINE))
  701. {
  702. info_error (_("value %s is not valid for variable %s"),
  703. p, optarg);
  704. exit (EXIT_FAILURE);
  705. }
  706. }
  707. break;
  708. case 'x':
  709. set_debug_level (optarg);
  710. break;
  711. default:
  712. fprintf (stderr, _("Try --help for more information.\n"));
  713. exit (EXIT_FAILURE);
  714. }
  715. }
  716. /* If the output device is not a terminal, and no output filename has been
  717. specified, make user_output_filename be "-", so that the info is written
  718. to stdout, and turn on the dumping of subnodes. */
  719. if ((!isatty (fileno (stdout))) && (user_output_filename == NULL))
  720. {
  721. user_output_filename = xstrdup ("-");
  722. dump_subnodes = 1;
  723. }
  724. /* If the user specified --version, then show the version and exit. */
  725. if (print_version_p)
  726. {
  727. printf ("info (GNU %s) %s\n", PACKAGE, VERSION);
  728. puts ("");
  729. printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
  730. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
  731. This is free software: you are free to change and redistribute it.\n\
  732. There is NO WARRANTY, to the extent permitted by law.\n"),
  733. "2017");
  734. exit (EXIT_SUCCESS);
  735. }
  736. /* If the `--help' option was present, show the help and exit. */
  737. if (print_help_p)
  738. {
  739. info_short_help ();
  740. exit (EXIT_SUCCESS);
  741. }
  742. argc -= optind;
  743. argv += optind;
  744. /* If --file was not used and there is a slash in the first non-option
  745. argument (e.g. "info subdir/file.info"), do not search the dir files
  746. for a matching entry. */
  747. if (!user_filename && argv[0] && HAS_SLASH (argv[0]))
  748. {
  749. user_filename = xstrdup (argv[0]);
  750. argv++; /* Advance past first remaining argument. */
  751. argc--;
  752. }
  753. /* If the user specified a particular filename, add the path of that
  754. file to the contents of INFOPATH. */
  755. if (user_filename)
  756. add_file_directory_to_path (user_filename);
  757. /* Load custom key mappings and variable settings */
  758. initialize_terminal_and_keymaps (init_file);
  759. /* Add extra search directories to any already specified with
  760. --directory. */
  761. infopath_init ();
  762. /* If the user wants to search every known index for a given string,
  763. do that now, and report the results. */
  764. if (apropos_p)
  765. {
  766. REFERENCE **apropos_list;
  767. apropos_list = apropos_in_all_indices (apropos_search_string, 0);
  768. if (!apropos_list)
  769. info_error (_(APROPOS_NONE), apropos_search_string);
  770. else
  771. {
  772. register int i;
  773. REFERENCE *entry;
  774. for (i = 0; (entry = apropos_list[i]); i++)
  775. fprintf (stdout, "\"(%s)%s\" -- %s\n",
  776. entry->filename, entry->nodename, entry->label);
  777. }
  778. exit (0);
  779. }
  780. /* Initialize empty list of nodes to load. */
  781. add_pointer_to_array (0, ref_index, ref_list, ref_slots, 2);
  782. ref_index--;
  783. if (all_matches_p && !index_search_p)
  784. {
  785. /* --all */
  786. if (!user_filename && argv[0])
  787. {
  788. user_filename = xstrdup (argv[0]);
  789. argv++; argc--;
  790. }
  791. else if (!user_filename)
  792. {
  793. exit (1);
  794. }
  795. info_find_matching_files (user_filename);
  796. /* If only one match, don't start in a menu of matches. */
  797. if (ref_index == 1)
  798. all_matches_p = 0;
  799. /* --where */
  800. if (print_where_p)
  801. {
  802. int i;
  803. if (!ref_list)
  804. exit (1);
  805. for (i = 0; ref_list[i]; i++)
  806. printf ("%s\n", ref_list[i]->filename);
  807. exit (0);
  808. }
  809. }
  810. else
  811. {
  812. if (goto_invocation_p)
  813. {
  814. /* If they said "info --show-options foo bar baz",
  815. the last of the arguments is the program whose
  816. options they want to see. */
  817. char **p = argv;
  818. if (*p)
  819. {
  820. while (p[1])
  821. p++;
  822. invocation_program_name = *p;
  823. }
  824. }
  825. get_initial_file (&argc, &argv, &error);
  826. /* If the user specified `--index-search=STRING --all', create
  827. and display the menu of results. */
  828. if (index_search_p && all_matches_p && initial_file)
  829. {
  830. FILE_BUFFER *initial_fb;
  831. initial_fb = info_find_file (initial_file);
  832. if (initial_fb)
  833. {
  834. NODE *node = create_virtual_index (initial_fb,
  835. index_search_string);
  836. if (node)
  837. {
  838. if (user_output_filename)
  839. {
  840. FILE *output_stream = 0;
  841. if (strcmp (user_output_filename, "-") == 0)
  842. output_stream = stdout;
  843. else
  844. output_stream = fopen (user_output_filename, "w");
  845. if (output_stream)
  846. {
  847. write_node_to_stream (node, output_stream);
  848. }
  849. exit (0);
  850. }
  851. else
  852. {
  853. initialize_info_session ();
  854. info_set_node_of_window (active_window, node);
  855. info_read_and_dispatch ();
  856. close_info_session ();
  857. exit (0);
  858. }
  859. }
  860. }
  861. }
  862. /* If the user specified `--index-search=STRING',
  863. start the info session in the node corresponding
  864. to what they want. */
  865. else if (index_search_p && initial_file && !user_output_filename)
  866. {
  867. FILE_BUFFER *initial_fb;
  868. initial_fb = info_find_file (initial_file);
  869. if (initial_fb)
  870. {
  871. REFERENCE *result;
  872. int i, match_offset;
  873. result = next_index_match (initial_fb, index_search_string, 0, 1,
  874. &i, &match_offset);
  875. if (result)
  876. {
  877. initialize_info_session ();
  878. report_index_match (i, match_offset);
  879. info_select_reference (active_window, result);
  880. info_read_and_dispatch ();
  881. close_info_session ();
  882. exit (0);
  883. }
  884. }
  885. fprintf (stderr, _("no index entries found for '%s'\n"),
  886. index_search_string);
  887. close_dribble_file ();
  888. exit (1);
  889. }
  890. /* Add nodes to start with (unless we fell back to the man page). */
  891. if (!ref_list[0] || strcmp (ref_list[0]->filename,
  892. MANPAGE_FILE_BUFFER_NAME))
  893. {
  894. add_initial_nodes (argc, argv, &error);
  895. }
  896. /* --where */
  897. if (print_where_p)
  898. {
  899. if (initial_file)
  900. printf ("%s\n", initial_file);
  901. exit (0);
  902. }
  903. }
  904. /* --output */
  905. if (user_output_filename)
  906. {
  907. if (error)
  908. info_error ("%s", error);
  909. preprocess_nodes_p = 0;
  910. dump_nodes_to_file (ref_list, user_output_filename, dump_subnodes);
  911. exit (0);
  912. }
  913. if (ref_index == 0)
  914. {
  915. if (error)
  916. {
  917. info_error ("%s", error);
  918. exit (1);
  919. }
  920. exit (0);
  921. }
  922. info_session (ref_list, all_matches_p ? user_filename : 0, error);
  923. close_info_session ();
  924. exit (0);
  925. }
  926. /* Produce a scaled down description of the available options to Info. */
  927. static void
  928. info_short_help (void)
  929. {
  930. printf (_("\
  931. Usage: %s [OPTION]... [MENU-ITEM...]\n\
  932. \n\
  933. Read documentation in Info format.\n"), program_name);
  934. puts ("");
  935. puts (_("\
  936. Options:\n\
  937. -a, --all use all matching manuals.\n\
  938. -k, --apropos=STRING look up STRING in all indices of all manuals.\n\
  939. -d, --directory=DIR add DIR to INFOPATH.\n\
  940. --dribble=FILE remember user keystrokes in FILENAME.\n\
  941. -f, --file=MANUAL specify Info manual to visit."));
  942. puts (_("\
  943. -h, --help display this help and exit.\n\
  944. --index-search=STRING go to node pointed by index entry STRING.\n\
  945. -n, --node=NODENAME specify nodes in first visited Info file.\n\
  946. -o, --output=FILE output selected nodes to FILE."));
  947. puts (_("\
  948. -R, --raw-escapes output \"raw\" ANSI escapes (default).\n\
  949. --no-raw-escapes output escapes as literal text.\n\
  950. --restore=FILE read initial keystrokes from FILE.\n\
  951. -O, --show-options, --usage go to command-line options node."));
  952. #if defined(__MSDOS__) || defined(__MINGW32__)
  953. puts (_("\
  954. -b, --speech-friendly be friendly to speech synthesizers."));
  955. #endif
  956. puts (_("\
  957. --strict-node-location (for debugging) use Info file pointers as-is.\n\
  958. --subnodes recursively output menu items.\n\
  959. -v, --variable VAR=VALUE assign VALUE to Info variable VAR.\n\
  960. --vi-keys use vi-like and less-like key bindings.\n\
  961. --version display version information and exit.\n\
  962. -w, --where, --location print physical location of Info file.\n\
  963. -x, --debug=NUMBER set debugging level (-1 for all).\n"));
  964. puts (_("\n\
  965. The first non-option argument, if present, is the menu entry to start from;\n\
  966. it is searched for in all 'dir' files along INFOPATH.\n\
  967. If it is not present, info merges all 'dir' files and shows the result.\n\
  968. Any remaining arguments are treated as the names of menu\n\
  969. items relative to the initial node visited."));
  970. puts (_("\n\
  971. For a summary of key bindings, type H within Info."));
  972. puts (_("\n\
  973. Examples:\n\
  974. info show top-level dir menu\n\
  975. info info show the general manual for Info readers\n\
  976. info info-stnd show the manual specific to this Info program\n\
  977. info emacs start at emacs node from top-level dir\n\
  978. info emacs buffers select buffers menu entry in emacs manual\n\
  979. info emacs -n Files start at Files node within emacs manual\n\
  980. info '(emacs)Files' alternative way to start at Files node\n\
  981. info --show-options emacs start at node with emacs' command line options\n\
  982. info --subnodes -o out.txt emacs dump entire manual to out.txt\n\
  983. info -f ./foo.info show file ./foo.info, not searching dir"));
  984. puts ("");
  985. puts (_("\
  986. Email bug reports to bug-texinfo@gnu.org,\n\
  987. general questions and discussion to help-texinfo@gnu.org.\n\
  988. Texinfo home page: http://www.gnu.org/software/texinfo/"));
  989. exit (EXIT_SUCCESS);
  990. }
  991. /* Initialize strings for gettext. Because gettext doesn't handle N_ or
  992. _ within macro definitions, we put shared messages into variables and
  993. use them that way. This also has the advantage that there's only one
  994. copy of the strings. */
  995. const char *msg_cant_find_node;
  996. const char *msg_cant_file_node;
  997. const char *msg_cant_find_window;
  998. const char *msg_cant_find_point;
  999. const char *msg_cant_kill_last;
  1000. const char *msg_no_menu_node;
  1001. const char *msg_no_foot_node;
  1002. const char *msg_no_xref_node;
  1003. const char *msg_no_pointer;
  1004. const char *msg_unknown_command;
  1005. const char *msg_term_too_dumb;
  1006. const char *msg_at_node_bottom;
  1007. const char *msg_at_node_top;
  1008. const char *msg_one_window;
  1009. const char *msg_win_too_small;
  1010. const char *msg_cant_make_help;
  1011. static void
  1012. init_messages (void)
  1013. {
  1014. msg_cant_find_node = _("Cannot find node '%s'.");
  1015. msg_cant_file_node = _("Cannot find node '(%s)%s'.");
  1016. msg_cant_find_window = _("Cannot find a window!");
  1017. msg_cant_find_point = _("Point doesn't appear within this window's node!");
  1018. msg_cant_kill_last = _("Cannot delete the last window.");
  1019. msg_no_menu_node = _("No menu in this node.");
  1020. msg_no_foot_node = _("No footnotes in this node.");
  1021. msg_no_xref_node = _("No cross references in this node.");
  1022. msg_no_pointer = _("No '%s' pointer for this node.");
  1023. msg_unknown_command = _("Unknown Info command '%c'; try '?' for help.");
  1024. msg_term_too_dumb = _("Terminal type '%s' is not smart enough to run Info.");
  1025. msg_at_node_bottom = _("You are already at the last page of this node.");
  1026. msg_at_node_top = _("You are already at the first page of this node.");
  1027. msg_one_window = _("Only one window.");
  1028. msg_win_too_small = _("Resulting window would be too small.");
  1029. msg_cant_make_help = _("Not enough room for a help window, please delete a window.");
  1030. }