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