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 | }
|
---|