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