3 /* dir.c -- how to build a special "dir" node from "localdir" files.
4 Id: dir.c,v 1.3 2004/04/11 17:56:45 karl Exp
6 Copyright (C) 1993, 1997, 1998, 2004 Free Software Foundation, Inc.
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)
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.
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.
22 Written by Brian Fox (bfox@ai.mit.edu). */
25 #include "info-utils.h"
29 /* The "dir" node can be built from the contents of a file called "dir",
30 with the addition of the menus of every file named in the array
31 dirs_to_add which are found in INFOPATH. */
33 static void add_menu_to_file_buffer (char *contents
, long int size
,
35 static void insert_text_into_fb_at_binding (FILE_BUFFER
*fb
,
36 SEARCH_BINDING
*binding
, char *text
, int textlen
);
37 void maybe_build_dir_node (char *dirname
);
39 static char *dirs_to_add
[] = {
40 "dir", "localdir", (char *)NULL
44 /* Return zero if the file represented in the stat structure TEST has
45 already been seen, nonzero else. */
51 } dir_file_list_entry_type
;
54 new_dir_file_p (struct stat
*test
)
56 static unsigned dir_file_list_len
= 0;
57 static dir_file_list_entry_type
*dir_file_list
= NULL
;
60 for (i
= 0; i
< dir_file_list_len
; i
++)
62 dir_file_list_entry_type entry
;
63 entry
= dir_file_list
[i
];
64 if (entry
.device
== test
->st_dev
&& entry
.inode
== test
->st_ino
)
69 dir_file_list
= xrealloc (dir_file_list
,
70 dir_file_list_len
* sizeof (dir_file_list_entry_type
));
71 dir_file_list
[dir_file_list_len
- 1].device
= test
->st_dev
;
72 dir_file_list
[dir_file_list_len
- 1].inode
= test
->st_ino
;
78 maybe_build_dir_node (char *dirname
)
80 int path_index
, update_tags
;
82 FILE_BUFFER
*dir_buffer
= info_find_file (dirname
);
84 /* If there is no "dir" in the current info path, we cannot build one
89 /* If this directory has already been built, return now. */
90 if (dir_buffer
->flags
& N_CannotGC
)
93 /* Initialize the list we use to avoid reading the same dir file twice
94 with the dir file just found. */
95 new_dir_file_p (&dir_buffer
->finfo
);
97 path_index
= update_tags
= 0;
99 /* Using each element of the path, check for one of the files in
100 DIRS_TO_ADD. Do not check for "localdir.info.Z" or anything else.
101 Only files explictly named are eligible. This is a design decision.
102 There can be an info file name "localdir.info" which contains
103 information on the setting up of "localdir" files. */
104 while ((this_dir
= extract_colon_unit (infopath
, &path_index
)))
106 register int da_index
;
109 /* Expand a leading tilde if one is present. */
110 if (*this_dir
== '~')
112 char *tilde_expanded_dirname
;
114 tilde_expanded_dirname
= tilde_expand_word (this_dir
);
115 if (tilde_expanded_dirname
!= this_dir
)
118 this_dir
= tilde_expanded_dirname
;
122 /* For every different file named in DIRS_TO_ADD found in the
123 search path, add that file's menu to our "dir" node. */
124 for (da_index
= 0; (from_file
= dirs_to_add
[da_index
]); da_index
++)
128 int namelen
= strlen (from_file
);
129 char *fullpath
= xmalloc (3 + strlen (this_dir
) + namelen
);
131 strcpy (fullpath
, this_dir
);
132 if (!IS_SLASH (fullpath
[strlen (fullpath
) - 1]))
133 strcat (fullpath
, "/");
134 strcat (fullpath
, from_file
);
136 statable
= (stat (fullpath
, &finfo
) == 0);
138 /* Only add this file if we have not seen it before. */
139 if (statable
&& S_ISREG (finfo
.st_mode
) && new_dir_file_p (&finfo
))
143 char *contents
= filesys_read_info_file (fullpath
, &filesize
,
144 &finfo
, &compressed
);
148 add_menu_to_file_buffer (contents
, filesize
, dir_buffer
);
159 build_tags_and_nodes (dir_buffer
);
161 /* Flag that the dir buffer has been built. */
162 dir_buffer
->flags
|= N_CannotGC
;
165 /* Given CONTENTS and FB (a file buffer), add the menu found in CONTENTS
166 to the menu found in FB->contents. Second argument SIZE is the total
169 add_menu_to_file_buffer (char *contents
, long int size
, FILE_BUFFER
*fb
)
171 SEARCH_BINDING contents_binding
, fb_binding
;
172 long contents_offset
, fb_offset
;
174 contents_binding
.buffer
= contents
;
175 contents_binding
.start
= 0;
176 contents_binding
.end
= size
;
177 contents_binding
.flags
= S_FoldCase
| S_SkipDest
;
179 fb_binding
.buffer
= fb
->contents
;
180 fb_binding
.start
= 0;
181 fb_binding
.end
= fb
->filesize
;
182 fb_binding
.flags
= S_FoldCase
| S_SkipDest
;
184 /* Move to the start of the menus in CONTENTS and FB. */
185 contents_offset
= search_forward (INFO_MENU_LABEL
, &contents_binding
);
186 fb_offset
= search_forward (INFO_MENU_LABEL
, &fb_binding
);
188 /* If there is no menu in CONTENTS, quit now. */
189 if (contents_offset
== -1)
192 /* There is a menu in CONTENTS, and contents_offset points to the first
193 character following the menu starter string. Skip all whitespace
194 and newline characters. */
195 contents_offset
+= skip_whitespace_and_newlines (contents
+ contents_offset
);
197 /* If there is no menu in FB, make one. */
200 /* Find the start of the second node in this file buffer. If there
201 is only one node, we will be adding the contents to the end of
203 fb_offset
= find_node_separator (&fb_binding
);
205 /* If not even a single node separator, give up. */
209 fb_binding
.start
= fb_offset
;
211 skip_node_separator (fb_binding
.buffer
+ fb_binding
.start
);
213 /* Try to find the next node separator. */
214 fb_offset
= find_node_separator (&fb_binding
);
216 /* If found one, consider that the start of the menu. Otherwise, the
217 start of this menu is the end of the file buffer (i.e., fb->size). */
219 fb_binding
.start
= fb_offset
;
221 fb_binding
.start
= fb_binding
.end
;
223 insert_text_into_fb_at_binding
224 (fb
, &fb_binding
, INFO_MENU_LABEL
, strlen (INFO_MENU_LABEL
));
226 fb_binding
.buffer
= fb
->contents
;
227 fb_binding
.start
= 0;
228 fb_binding
.end
= fb
->filesize
;
229 fb_offset
= search_forward (INFO_MENU_LABEL
, &fb_binding
);
234 /* CONTENTS_OFFSET and FB_OFFSET point to the starts of the menus that
235 appear in their respective buffers. Add the remainder of CONTENTS
236 to the end of FB's menu. */
237 fb_binding
.start
= fb_offset
;
238 fb_offset
= find_node_separator (&fb_binding
);
240 fb_binding
.start
= fb_offset
;
242 fb_binding
.start
= fb_binding
.end
;
244 /* Leave exactly one blank line between directory entries. */
248 while ((fb_binding
.start
> 0) &&
249 (whitespace_or_newline (fb_binding
.buffer
[fb_binding
.start
- 1])))
255 /* Optimize if possible. */
258 fb_binding
.buffer
[fb_binding
.start
++] = '\n';
259 fb_binding
.buffer
[fb_binding
.start
++] = '\n';
263 /* Do it the hard way. */
264 insert_text_into_fb_at_binding (fb
, &fb_binding
, "\n\n", 2);
265 fb_binding
.start
+= 2;
269 /* Insert the new menu. */
270 insert_text_into_fb_at_binding
271 (fb
, &fb_binding
, contents
+ contents_offset
, size
- contents_offset
);
275 insert_text_into_fb_at_binding (FILE_BUFFER
*fb
,
276 SEARCH_BINDING
*binding
, char *text
, int textlen
)
281 start
= binding
->start
;
284 contents
= (char *)xmalloc (fb
->filesize
+ textlen
+ 1);
285 memcpy (contents
, fb
->contents
, start
);
286 memcpy (contents
+ start
, text
, textlen
);
287 memcpy (contents
+ start
+ textlen
, fb
->contents
+ start
, end
- start
);
289 fb
->contents
= contents
;
290 fb
->filesize
+= textlen
;
291 fb
->finfo
.st_size
= fb
->filesize
;