| 1 | /* dir.c -- how to build a special "dir" node from "localdir" files. | 
|---|
| 2 | $Id: dir.c,v 1.3 2004/04/11 17:56:45 karl Exp $ | 
|---|
| 3 |  | 
|---|
| 4 | Copyright (C) 1993, 1997, 1998, 2004 Free Software Foundation, Inc. | 
|---|
| 5 |  | 
|---|
| 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 2, or (at your option) | 
|---|
| 9 | any later version. | 
|---|
| 10 |  | 
|---|
| 11 | This program is distributed in the hope that it will be useful, | 
|---|
| 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|---|
| 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|---|
| 14 | GNU General Public License for more details. | 
|---|
| 15 |  | 
|---|
| 16 | You should have received a copy of the GNU General Public License | 
|---|
| 17 | along with this program; if not, write to the Free Software | 
|---|
| 18 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | 
|---|
| 19 |  | 
|---|
| 20 | Written by Brian Fox (bfox@ai.mit.edu). */ | 
|---|
| 21 |  | 
|---|
| 22 | #include "info.h" | 
|---|
| 23 | #include "info-utils.h" | 
|---|
| 24 | #include "filesys.h" | 
|---|
| 25 | #include "tilde.h" | 
|---|
| 26 |  | 
|---|
| 27 | /* The "dir" node can be built from the contents of a file called "dir", | 
|---|
| 28 | with the addition of the menus of every file named in the array | 
|---|
| 29 | dirs_to_add which are found in INFOPATH. */ | 
|---|
| 30 |  | 
|---|
| 31 | static void add_menu_to_file_buffer (char *contents, long int size, | 
|---|
| 32 | FILE_BUFFER *fb); | 
|---|
| 33 | static void insert_text_into_fb_at_binding (FILE_BUFFER *fb, | 
|---|
| 34 | SEARCH_BINDING *binding, char *text, int textlen); | 
|---|
| 35 | void maybe_build_dir_node (char *dirname); | 
|---|
| 36 |  | 
|---|
| 37 | static char *dirs_to_add[] = { | 
|---|
| 38 | "dir", "localdir", (char *)NULL | 
|---|
| 39 | }; | 
|---|
| 40 |  | 
|---|
| 41 |  | 
|---|
| 42 | /* Return zero if the file represented in the stat structure TEST has | 
|---|
| 43 | already been seen, nonzero else.  */ | 
|---|
| 44 |  | 
|---|
| 45 | typedef struct | 
|---|
| 46 | { | 
|---|
| 47 | unsigned long device; | 
|---|
| 48 | unsigned long inode; | 
|---|
| 49 | } dir_file_list_entry_type; | 
|---|
| 50 |  | 
|---|
| 51 | static int | 
|---|
| 52 | new_dir_file_p (struct stat *test) | 
|---|
| 53 | { | 
|---|
| 54 | static unsigned dir_file_list_len = 0; | 
|---|
| 55 | static dir_file_list_entry_type *dir_file_list = NULL; | 
|---|
| 56 | unsigned i; | 
|---|
| 57 |  | 
|---|
| 58 | for (i = 0; i < dir_file_list_len; i++) | 
|---|
| 59 | { | 
|---|
| 60 | dir_file_list_entry_type entry; | 
|---|
| 61 | entry = dir_file_list[i]; | 
|---|
| 62 | if (entry.device == test->st_dev && entry.inode == test->st_ino) | 
|---|
| 63 | return 0; | 
|---|
| 64 | } | 
|---|
| 65 |  | 
|---|
| 66 | dir_file_list_len++; | 
|---|
| 67 | dir_file_list = xrealloc (dir_file_list, | 
|---|
| 68 | dir_file_list_len * sizeof (dir_file_list_entry_type)); | 
|---|
| 69 | dir_file_list[dir_file_list_len - 1].device = test->st_dev; | 
|---|
| 70 | dir_file_list[dir_file_list_len - 1].inode = test->st_ino; | 
|---|
| 71 | return 1; | 
|---|
| 72 | } | 
|---|
| 73 |  | 
|---|
| 74 |  | 
|---|
| 75 | void | 
|---|
| 76 | maybe_build_dir_node (char *dirname) | 
|---|
| 77 | { | 
|---|
| 78 | int path_index, update_tags; | 
|---|
| 79 | char *this_dir; | 
|---|
| 80 | FILE_BUFFER *dir_buffer = info_find_file (dirname); | 
|---|
| 81 |  | 
|---|
| 82 | /* If there is no "dir" in the current info path, we cannot build one | 
|---|
| 83 | from nothing. */ | 
|---|
| 84 | if (!dir_buffer) | 
|---|
| 85 | return; | 
|---|
| 86 |  | 
|---|
| 87 | /* If this directory has already been built, return now. */ | 
|---|
| 88 | if (dir_buffer->flags & N_CannotGC) | 
|---|
| 89 | return; | 
|---|
| 90 |  | 
|---|
| 91 | /* Initialize the list we use to avoid reading the same dir file twice | 
|---|
| 92 | with the dir file just found.  */ | 
|---|
| 93 | new_dir_file_p (&dir_buffer->finfo); | 
|---|
| 94 |  | 
|---|
| 95 | path_index = update_tags = 0; | 
|---|
| 96 |  | 
|---|
| 97 | /* Using each element of the path, check for one of the files in | 
|---|
| 98 | DIRS_TO_ADD.  Do not check for "localdir.info.Z" or anything else. | 
|---|
| 99 | Only files explictly named are eligible.  This is a design decision. | 
|---|
| 100 | There can be an info file name "localdir.info" which contains | 
|---|
| 101 | information on the setting up of "localdir" files. */ | 
|---|
| 102 | while ((this_dir = extract_colon_unit (infopath, &path_index))) | 
|---|
| 103 | { | 
|---|
| 104 | register int da_index; | 
|---|
| 105 | char *from_file; | 
|---|
| 106 |  | 
|---|
| 107 | /* Expand a leading tilde if one is present. */ | 
|---|
| 108 | if (*this_dir == '~') | 
|---|
| 109 | { | 
|---|
| 110 | char *tilde_expanded_dirname; | 
|---|
| 111 |  | 
|---|
| 112 | tilde_expanded_dirname = tilde_expand_word (this_dir); | 
|---|
| 113 | if (tilde_expanded_dirname != this_dir) | 
|---|
| 114 | { | 
|---|
| 115 | free (this_dir); | 
|---|
| 116 | this_dir = tilde_expanded_dirname; | 
|---|
| 117 | } | 
|---|
| 118 | } | 
|---|
| 119 |  | 
|---|
| 120 | /* For every different file named in DIRS_TO_ADD found in the | 
|---|
| 121 | search path, add that file's menu to our "dir" node. */ | 
|---|
| 122 | for (da_index = 0; (from_file = dirs_to_add[da_index]); da_index++) | 
|---|
| 123 | { | 
|---|
| 124 | struct stat finfo; | 
|---|
| 125 | int statable; | 
|---|
| 126 | int namelen = strlen (from_file); | 
|---|
| 127 | char *fullpath = xmalloc (3 + strlen (this_dir) + namelen); | 
|---|
| 128 |  | 
|---|
| 129 | strcpy (fullpath, this_dir); | 
|---|
| 130 | if (!IS_SLASH (fullpath[strlen (fullpath) - 1])) | 
|---|
| 131 | strcat (fullpath, "/"); | 
|---|
| 132 | strcat (fullpath, from_file); | 
|---|
| 133 |  | 
|---|
| 134 | statable = (stat (fullpath, &finfo) == 0); | 
|---|
| 135 |  | 
|---|
| 136 | /* Only add this file if we have not seen it before.  */ | 
|---|
| 137 | if (statable && S_ISREG (finfo.st_mode) && new_dir_file_p (&finfo)) | 
|---|
| 138 | { | 
|---|
| 139 | long filesize; | 
|---|
| 140 | int compressed; | 
|---|
| 141 | char *contents = filesys_read_info_file (fullpath, &filesize, | 
|---|
| 142 | &finfo, &compressed); | 
|---|
| 143 | if (contents) | 
|---|
| 144 | { | 
|---|
| 145 | update_tags++; | 
|---|
| 146 | add_menu_to_file_buffer (contents, filesize, dir_buffer); | 
|---|
| 147 | free (contents); | 
|---|
| 148 | } | 
|---|
| 149 | } | 
|---|
| 150 |  | 
|---|
| 151 | free (fullpath); | 
|---|
| 152 | } | 
|---|
| 153 | free (this_dir); | 
|---|
| 154 | } | 
|---|
| 155 |  | 
|---|
| 156 | if (update_tags) | 
|---|
| 157 | build_tags_and_nodes (dir_buffer); | 
|---|
| 158 |  | 
|---|
| 159 | /* Flag that the dir buffer has been built. */ | 
|---|
| 160 | dir_buffer->flags |= N_CannotGC; | 
|---|
| 161 | } | 
|---|
| 162 |  | 
|---|
| 163 | /* Given CONTENTS and FB (a file buffer), add the menu found in CONTENTS | 
|---|
| 164 | to the menu found in FB->contents.  Second argument SIZE is the total | 
|---|
| 165 | size of CONTENTS. */ | 
|---|
| 166 | static void | 
|---|
| 167 | add_menu_to_file_buffer (char *contents, long int size, FILE_BUFFER *fb) | 
|---|
| 168 | { | 
|---|
| 169 | SEARCH_BINDING contents_binding, fb_binding; | 
|---|
| 170 | long contents_offset, fb_offset; | 
|---|
| 171 |  | 
|---|
| 172 | contents_binding.buffer = contents; | 
|---|
| 173 | contents_binding.start = 0; | 
|---|
| 174 | contents_binding.end = size; | 
|---|
| 175 | contents_binding.flags = S_FoldCase | S_SkipDest; | 
|---|
| 176 |  | 
|---|
| 177 | fb_binding.buffer = fb->contents; | 
|---|
| 178 | fb_binding.start = 0; | 
|---|
| 179 | fb_binding.end = fb->filesize; | 
|---|
| 180 | fb_binding.flags = S_FoldCase | S_SkipDest; | 
|---|
| 181 |  | 
|---|
| 182 | /* Move to the start of the menus in CONTENTS and FB. */ | 
|---|
| 183 | contents_offset = search_forward (INFO_MENU_LABEL, &contents_binding); | 
|---|
| 184 | fb_offset = search_forward (INFO_MENU_LABEL, &fb_binding); | 
|---|
| 185 |  | 
|---|
| 186 | /* If there is no menu in CONTENTS, quit now. */ | 
|---|
| 187 | if (contents_offset == -1) | 
|---|
| 188 | return; | 
|---|
| 189 |  | 
|---|
| 190 | /* There is a menu in CONTENTS, and contents_offset points to the first | 
|---|
| 191 | character following the menu starter string.  Skip all whitespace | 
|---|
| 192 | and newline characters. */ | 
|---|
| 193 | contents_offset += skip_whitespace_and_newlines (contents + contents_offset); | 
|---|
| 194 |  | 
|---|
| 195 | /* If there is no menu in FB, make one. */ | 
|---|
| 196 | if (fb_offset == -1) | 
|---|
| 197 | { | 
|---|
| 198 | /* Find the start of the second node in this file buffer.  If there | 
|---|
| 199 | is only one node, we will be adding the contents to the end of | 
|---|
| 200 | this node. */ | 
|---|
| 201 | fb_offset = find_node_separator (&fb_binding); | 
|---|
| 202 |  | 
|---|
| 203 | /* If not even a single node separator, give up. */ | 
|---|
| 204 | if (fb_offset == -1) | 
|---|
| 205 | return; | 
|---|
| 206 |  | 
|---|
| 207 | fb_binding.start = fb_offset; | 
|---|
| 208 | fb_binding.start += | 
|---|
| 209 | skip_node_separator (fb_binding.buffer + fb_binding.start); | 
|---|
| 210 |  | 
|---|
| 211 | /* Try to find the next node separator. */ | 
|---|
| 212 | fb_offset = find_node_separator (&fb_binding); | 
|---|
| 213 |  | 
|---|
| 214 | /* If found one, consider that the start of the menu.  Otherwise, the | 
|---|
| 215 | start of this menu is the end of the file buffer (i.e., fb->size). */ | 
|---|
| 216 | if (fb_offset != -1) | 
|---|
| 217 | fb_binding.start = fb_offset; | 
|---|
| 218 | else | 
|---|
| 219 | fb_binding.start = fb_binding.end; | 
|---|
| 220 |  | 
|---|
| 221 | insert_text_into_fb_at_binding | 
|---|
| 222 | (fb, &fb_binding, INFO_MENU_LABEL, strlen (INFO_MENU_LABEL)); | 
|---|
| 223 |  | 
|---|
| 224 | fb_binding.buffer = fb->contents; | 
|---|
| 225 | fb_binding.start = 0; | 
|---|
| 226 | fb_binding.end = fb->filesize; | 
|---|
| 227 | fb_offset = search_forward (INFO_MENU_LABEL, &fb_binding); | 
|---|
| 228 | if (fb_offset == -1) | 
|---|
| 229 | abort (); | 
|---|
| 230 | } | 
|---|
| 231 |  | 
|---|
| 232 | /* CONTENTS_OFFSET and FB_OFFSET point to the starts of the menus that | 
|---|
| 233 | appear in their respective buffers.  Add the remainder of CONTENTS | 
|---|
| 234 | to the end of FB's menu. */ | 
|---|
| 235 | fb_binding.start = fb_offset; | 
|---|
| 236 | fb_offset = find_node_separator (&fb_binding); | 
|---|
| 237 | if (fb_offset != -1) | 
|---|
| 238 | fb_binding.start = fb_offset; | 
|---|
| 239 | else | 
|---|
| 240 | fb_binding.start = fb_binding.end; | 
|---|
| 241 |  | 
|---|
| 242 | /* Leave exactly one blank line between directory entries. */ | 
|---|
| 243 | { | 
|---|
| 244 | int num_found = 0; | 
|---|
| 245 |  | 
|---|
| 246 | while ((fb_binding.start > 0) && | 
|---|
| 247 | (whitespace_or_newline (fb_binding.buffer[fb_binding.start - 1]))) | 
|---|
| 248 | { | 
|---|
| 249 | num_found++; | 
|---|
| 250 | fb_binding.start--; | 
|---|
| 251 | } | 
|---|
| 252 |  | 
|---|
| 253 | /* Optimize if possible. */ | 
|---|
| 254 | if (num_found >= 2) | 
|---|
| 255 | { | 
|---|
| 256 | fb_binding.buffer[fb_binding.start++] = '\n'; | 
|---|
| 257 | fb_binding.buffer[fb_binding.start++] = '\n'; | 
|---|
| 258 | } | 
|---|
| 259 | else | 
|---|
| 260 | { | 
|---|
| 261 | /* Do it the hard way. */ | 
|---|
| 262 | insert_text_into_fb_at_binding (fb, &fb_binding, "\n\n", 2); | 
|---|
| 263 | fb_binding.start += 2; | 
|---|
| 264 | } | 
|---|
| 265 | } | 
|---|
| 266 |  | 
|---|
| 267 | /* Insert the new menu. */ | 
|---|
| 268 | insert_text_into_fb_at_binding | 
|---|
| 269 | (fb, &fb_binding, contents + contents_offset, size - contents_offset); | 
|---|
| 270 | } | 
|---|
| 271 |  | 
|---|
| 272 | static void | 
|---|
| 273 | insert_text_into_fb_at_binding (FILE_BUFFER *fb, | 
|---|
| 274 | SEARCH_BINDING *binding, char *text, int textlen) | 
|---|
| 275 | { | 
|---|
| 276 | char *contents; | 
|---|
| 277 | long start, end; | 
|---|
| 278 |  | 
|---|
| 279 | start = binding->start; | 
|---|
| 280 | end = fb->filesize; | 
|---|
| 281 |  | 
|---|
| 282 | contents = (char *)xmalloc (fb->filesize + textlen + 1); | 
|---|
| 283 | memcpy (contents, fb->contents, start); | 
|---|
| 284 | memcpy (contents + start, text, textlen); | 
|---|
| 285 | memcpy (contents + start + textlen, fb->contents + start, end - start); | 
|---|
| 286 | free (fb->contents); | 
|---|
| 287 | fb->contents = contents; | 
|---|
| 288 | fb->filesize += textlen; | 
|---|
| 289 | fb->finfo.st_size = fb->filesize; | 
|---|
| 290 | } | 
|---|