dir.c 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. /* dir.c -- how to build a special "dir" node from "localdir" files.
  2. $Id$
  3. Copyright 1993, 1997, 1998, 2004, 2007, 2008, 2009, 2012,
  4. 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 "tilde.h"
  20. /* The "dir" node can be built from the contents of a file called "dir",
  21. with the addition of the menus of every file named in the array
  22. dirs_to_add which are found in INFOPATH. */
  23. static void add_menu_to_node (char *contents, size_t size, NODE *node);
  24. static void insert_text_into_node (NODE *node, long start,
  25. char *text, int textlen);
  26. static char *dirs_to_add[] = {
  27. "dir", "localdir", NULL
  28. };
  29. static NODE *dir_node = 0;
  30. static NODE *build_dir_node (void);
  31. /* Return composite directory node. Return value should be freed by caller,
  32. but none of its fields should be. */
  33. NODE *
  34. get_dir_node (void)
  35. {
  36. NODE *node;
  37. if (!dir_node)
  38. dir_node = build_dir_node ();
  39. node = xmalloc (sizeof (NODE));
  40. *node = *dir_node;
  41. return node;
  42. }
  43. static char *dir_contents;
  44. static NODE *
  45. build_dir_node (void)
  46. {
  47. int path_index;
  48. char *this_dir;
  49. NODE *node;
  50. node = info_create_node ();
  51. node->nodename = xstrdup ("Top");
  52. node->fullpath = xstrdup ("dir");
  53. node->contents = xstrdup (
  54. "File: dir, Node: Top, This is the top of the INFO tree.\n"
  55. "\n"
  56. "This is the Info main menu (aka directory node).\n"
  57. "A few useful Info commands:\n"
  58. "\n"
  59. " 'q' quits;\n"
  60. " 'H' lists all Info commands;\n"
  61. " 'h' starts the Info tutorial;\n"
  62. " 'mTexinfo RET' visits the Texinfo manual, etc.\n"
  63. );
  64. node->nodelen = strlen (node->contents);
  65. /* Using each element of the path, check for one of the files in
  66. DIRS_TO_ADD. Do not check for "localdir.info.Z" or anything else.
  67. Only files explictly named are eligible. This is a design decision.
  68. There can be an info file name "localdir.info" which contains
  69. information on the setting up of "localdir" files. */
  70. for (this_dir = infopath_first (&path_index); this_dir;
  71. this_dir = infopath_next (&path_index))
  72. {
  73. register int da_index;
  74. char *from_file;
  75. /* Expand a leading tilde if one is present. */
  76. if (*this_dir == '~')
  77. {
  78. char *tilde_expanded_dirname;
  79. tilde_expanded_dirname = tilde_expand_word (this_dir);
  80. if (tilde_expanded_dirname != this_dir)
  81. {
  82. this_dir = tilde_expanded_dirname;
  83. }
  84. }
  85. /* For every different file named in DIRS_TO_ADD found in the
  86. search path, add that file's menu to our "dir" node. */
  87. for (da_index = 0; (from_file = dirs_to_add[da_index]); da_index++)
  88. {
  89. struct stat finfo;
  90. int statable;
  91. int namelen = strlen (from_file);
  92. char *fullpath = xmalloc (3 + strlen (this_dir) + namelen);
  93. strcpy (fullpath, this_dir);
  94. if (!IS_SLASH (fullpath[strlen (fullpath) - 1]))
  95. strcat (fullpath, "/");
  96. strcat (fullpath, from_file);
  97. statable = (stat (fullpath, &finfo) == 0);
  98. if (statable && S_ISREG (finfo.st_mode))
  99. {
  100. size_t filesize;
  101. int compressed;
  102. char *contents = filesys_read_info_file (fullpath, &filesize,
  103. &finfo, &compressed);
  104. if (contents)
  105. {
  106. add_menu_to_node (contents, filesize, node);
  107. free (contents);
  108. }
  109. }
  110. free (fullpath);
  111. }
  112. }
  113. node->flags |= N_IsDir;
  114. dir_contents = node->contents;
  115. scan_node_contents (node, 0, 0);
  116. return node;
  117. }
  118. /* Given CONTENTS and NODE, add the menu found in CONTENTS to the menu
  119. found in NODE->contents. SIZE is the total size of CONTENTS. */
  120. static void
  121. add_menu_to_node (char *contents, size_t size, NODE *node)
  122. {
  123. SEARCH_BINDING contents_binding, fb_binding;
  124. long contents_offset, fb_offset;
  125. contents_binding.buffer = contents;
  126. contents_binding.start = 0;
  127. contents_binding.end = size;
  128. contents_binding.flags = S_FoldCase | S_SkipDest;
  129. fb_binding.buffer = node->contents;
  130. fb_binding.start = 0;
  131. fb_binding.end = node->nodelen;
  132. fb_binding.flags = S_FoldCase | S_SkipDest;
  133. /* Move to the start of the menus in CONTENTS and NODE. */
  134. if (search_forward (INFO_MENU_LABEL, &contents_binding, &contents_offset)
  135. != search_success)
  136. /* If there is no menu in CONTENTS, quit now. */
  137. return;
  138. /* There is a menu in CONTENTS, and contents_offset points to the first
  139. character following the menu starter string. Skip all whitespace
  140. and newline characters. */
  141. contents_offset += skip_whitespace_and_newlines (contents + contents_offset);
  142. /* If there is no menu in NODE, make one. */
  143. if (search_forward (INFO_MENU_LABEL, &fb_binding, &fb_offset)
  144. != search_success)
  145. {
  146. fb_binding.start = node->nodelen;
  147. insert_text_into_node
  148. (node, fb_binding.start, INFO_MENU_LABEL, strlen (INFO_MENU_LABEL));
  149. fb_binding.buffer = node->contents;
  150. fb_binding.start = 0;
  151. fb_binding.end = node->nodelen;
  152. if (search_forward (INFO_MENU_LABEL, &fb_binding, &fb_offset)
  153. != search_success)
  154. abort ();
  155. }
  156. /* CONTENTS_OFFSET and FB_OFFSET point to the starts of the menus that
  157. appear in their respective buffers. Add the remainder of CONTENTS
  158. to the end of NODE's menu. */
  159. fb_binding.start = fb_offset;
  160. fb_offset = find_node_separator (&fb_binding);
  161. if (fb_offset != -1)
  162. fb_binding.start = fb_offset;
  163. else
  164. fb_binding.start = fb_binding.end;
  165. /* Leave exactly one blank line between directory entries. */
  166. {
  167. int num_found = 0;
  168. while ((fb_binding.start > 0) &&
  169. (whitespace_or_newline (fb_binding.buffer[fb_binding.start - 1])))
  170. {
  171. num_found++;
  172. fb_binding.start--;
  173. }
  174. /* Optimize if possible. */
  175. if (num_found >= 2)
  176. {
  177. fb_binding.buffer[fb_binding.start++] = '\n';
  178. fb_binding.buffer[fb_binding.start++] = '\n';
  179. }
  180. else
  181. {
  182. /* Do it the hard way. */
  183. insert_text_into_node (node, fb_binding.start, "\n\n", 2);
  184. fb_binding.start += 2;
  185. }
  186. }
  187. /* Insert the new menu. */
  188. insert_text_into_node
  189. (node, fb_binding.start, contents + contents_offset, size - contents_offset);
  190. }
  191. static void
  192. insert_text_into_node (NODE *node, long start, char *text, int textlen)
  193. {
  194. char *contents;
  195. long end;
  196. end = node->nodelen;
  197. contents = xmalloc (node->nodelen + textlen + 1);
  198. memcpy (contents, node->contents, start);
  199. memcpy (contents + start, text, textlen);
  200. memcpy (contents + start + textlen, node->contents + start, end - start + 1);
  201. free (node->contents);
  202. node->contents = contents;
  203. node->nodelen += textlen;
  204. }
  205. /* Return directory entry. Return value should not be freed or modified. */
  206. REFERENCE *
  207. lookup_dir_entry (char *label, int sloppy)
  208. {
  209. REFERENCE *entry;
  210. if (!dir_node)
  211. dir_node = build_dir_node ();
  212. entry = info_get_menu_entry_by_label (dir_node, label, sloppy);
  213. return entry;
  214. }
  215. /* Look up entry in "dir" and "localdir" in search directory. Return
  216. value is a pointer to a newly allocated REFERENCE. */
  217. REFERENCE *
  218. dir_entry_of_infodir (char *label, char *searchdir)
  219. {
  220. int da_index;
  221. char *dir_filename;
  222. char *dir_fullpath;
  223. struct stat dummy;
  224. char *entry_fullpath;
  225. NODE *dir_node;
  226. REFERENCE *entry;
  227. for (da_index = 0; (dir_filename = dirs_to_add[da_index]); da_index++)
  228. {
  229. dir_fullpath = info_add_extension (searchdir, dir_filename, &dummy);
  230. if (!dir_fullpath)
  231. continue;
  232. dir_node = info_get_node (dir_fullpath, "Top");
  233. free (dir_fullpath);
  234. entry = info_get_menu_entry_by_label (dir_node, label, 1);
  235. if (!entry || !entry->filename)
  236. {
  237. free_history_node (dir_node);
  238. continue;
  239. /* A dir entry with no filename is unlikely, but not impossible. */
  240. }
  241. entry = info_copy_reference (entry);
  242. entry_fullpath = info_add_extension (searchdir, entry->filename, &dummy);
  243. if (entry_fullpath)
  244. {
  245. free (entry->filename);
  246. entry->filename = entry_fullpath;
  247. }
  248. free_history_node (dir_node);
  249. return entry;
  250. }
  251. return 0;
  252. }