xref: /netbsd/external/gpl2/texinfo/dist/info/dir.c (revision dc174305)
1 /*	$NetBSD: dir.c,v 1.1.1.1 2016/01/14 00:11:29 christos Exp $	*/
2 
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
5 
6    Copyright (C) 1993, 1997, 1998, 2004 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 "info.h"
25 #include "info-utils.h"
26 #include "filesys.h"
27 #include "tilde.h"
28 
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. */
32 
33 static void add_menu_to_file_buffer (char *contents, long int size,
34     FILE_BUFFER *fb);
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);
38 
39 static char *dirs_to_add[] = {
40   "dir", "localdir", (char *)NULL
41 };
42 
43 
44 /* Return zero if the file represented in the stat structure TEST has
45    already been seen, nonzero else.  */
46 
47 typedef struct
48 {
49   unsigned long device;
50   unsigned long inode;
51 } dir_file_list_entry_type;
52 
53 static int
new_dir_file_p(struct stat * test)54 new_dir_file_p (struct stat *test)
55 {
56   static unsigned dir_file_list_len = 0;
57   static dir_file_list_entry_type *dir_file_list = NULL;
58   unsigned i;
59 
60   for (i = 0; i < dir_file_list_len; i++)
61     {
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)
65         return 0;
66     }
67 
68   dir_file_list_len++;
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;
73   return 1;
74 }
75 
76 
77 void
maybe_build_dir_node(char * dirname)78 maybe_build_dir_node (char *dirname)
79 {
80   int path_index, update_tags;
81   char *this_dir;
82   FILE_BUFFER *dir_buffer = info_find_file (dirname);
83 
84   /* If there is no "dir" in the current info path, we cannot build one
85      from nothing. */
86   if (!dir_buffer)
87     return;
88 
89   /* If this directory has already been built, return now. */
90   if (dir_buffer->flags & N_CannotGC)
91     return;
92 
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);
96 
97   path_index = update_tags = 0;
98 
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)))
105     {
106       register int da_index;
107       char *from_file;
108 
109       /* Expand a leading tilde if one is present. */
110       if (*this_dir == '~')
111         {
112           char *tilde_expanded_dirname;
113 
114           tilde_expanded_dirname = tilde_expand_word (this_dir);
115           if (tilde_expanded_dirname != this_dir)
116             {
117               free (this_dir);
118               this_dir = tilde_expanded_dirname;
119             }
120         }
121 
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++)
125         {
126           struct stat finfo;
127           int statable;
128           int namelen = strlen (from_file);
129           char *fullpath = xmalloc (3 + strlen (this_dir) + namelen);
130 
131           strcpy (fullpath, this_dir);
132           if (!IS_SLASH (fullpath[strlen (fullpath) - 1]))
133             strcat (fullpath, "/");
134           strcat (fullpath, from_file);
135 
136           statable = (stat (fullpath, &finfo) == 0);
137 
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))
140             {
141               long filesize;
142 	      int compressed;
143               char *contents = filesys_read_info_file (fullpath, &filesize,
144                                                        &finfo, &compressed);
145               if (contents)
146                 {
147                   update_tags++;
148                   add_menu_to_file_buffer (contents, filesize, dir_buffer);
149                   free (contents);
150                 }
151             }
152 
153           free (fullpath);
154         }
155       free (this_dir);
156     }
157 
158   if (update_tags)
159     build_tags_and_nodes (dir_buffer);
160 
161   /* Flag that the dir buffer has been built. */
162   dir_buffer->flags |= N_CannotGC;
163 }
164 
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
167    size of CONTENTS. */
168 static void
add_menu_to_file_buffer(char * contents,long int size,FILE_BUFFER * fb)169 add_menu_to_file_buffer (char *contents, long int size, FILE_BUFFER *fb)
170 {
171   SEARCH_BINDING contents_binding, fb_binding;
172   long contents_offset, fb_offset;
173 
174   contents_binding.buffer = contents;
175   contents_binding.start = 0;
176   contents_binding.end = size;
177   contents_binding.flags = S_FoldCase | S_SkipDest;
178 
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;
183 
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);
187 
188   /* If there is no menu in CONTENTS, quit now. */
189   if (contents_offset == -1)
190     return;
191 
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);
196 
197   /* If there is no menu in FB, make one. */
198   if (fb_offset == -1)
199     {
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
202          this node. */
203       fb_offset = find_node_separator (&fb_binding);
204 
205       /* If not even a single node separator, give up. */
206       if (fb_offset == -1)
207         return;
208 
209       fb_binding.start = fb_offset;
210       fb_binding.start +=
211         skip_node_separator (fb_binding.buffer + fb_binding.start);
212 
213       /* Try to find the next node separator. */
214       fb_offset = find_node_separator (&fb_binding);
215 
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). */
218       if (fb_offset != -1)
219         fb_binding.start = fb_offset;
220       else
221         fb_binding.start = fb_binding.end;
222 
223       insert_text_into_fb_at_binding
224         (fb, &fb_binding, INFO_MENU_LABEL, strlen (INFO_MENU_LABEL));
225 
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);
230       if (fb_offset == -1)
231         abort ();
232     }
233 
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);
239   if (fb_offset != -1)
240     fb_binding.start = fb_offset;
241   else
242     fb_binding.start = fb_binding.end;
243 
244   /* Leave exactly one blank line between directory entries. */
245   {
246     int num_found = 0;
247 
248     while ((fb_binding.start > 0) &&
249            (whitespace_or_newline (fb_binding.buffer[fb_binding.start - 1])))
250       {
251         num_found++;
252         fb_binding.start--;
253       }
254 
255     /* Optimize if possible. */
256     if (num_found >= 2)
257       {
258         fb_binding.buffer[fb_binding.start++] = '\n';
259         fb_binding.buffer[fb_binding.start++] = '\n';
260       }
261     else
262       {
263         /* Do it the hard way. */
264         insert_text_into_fb_at_binding (fb, &fb_binding, "\n\n", 2);
265         fb_binding.start += 2;
266       }
267   }
268 
269   /* Insert the new menu. */
270   insert_text_into_fb_at_binding
271     (fb, &fb_binding, contents + contents_offset, size - contents_offset);
272 }
273 
274 static void
insert_text_into_fb_at_binding(FILE_BUFFER * fb,SEARCH_BINDING * binding,char * text,int textlen)275 insert_text_into_fb_at_binding (FILE_BUFFER *fb,
276     SEARCH_BINDING *binding, char *text, int textlen)
277 {
278   char *contents;
279   long start, end;
280 
281   start = binding->start;
282   end = fb->filesize;
283 
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);
288   free (fb->contents);
289   fb->contents = contents;
290   fb->filesize += textlen;
291   fb->finfo.st_size = fb->filesize;
292 }
293