Back to index

tetex-bin  3.0
dir.c
Go to the documentation of this file.
00001 /* dir.c -- how to build a special "dir" node from "localdir" files.
00002    $Id: dir.c,v 1.3 2004/04/11 17:56:45 karl Exp $
00003 
00004    Copyright (C) 1993, 1997, 1998, 2004 Free Software Foundation, Inc.
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 2, or (at your option)
00009    any later version.
00010 
00011    This program is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014    GNU General Public License for more details.
00015 
00016    You should have received a copy of the GNU General Public License
00017    along with this program; if not, write to the Free Software
00018    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00019 
00020    Written by Brian Fox (bfox@ai.mit.edu). */
00021 
00022 #include "info.h"
00023 #include "info-utils.h"
00024 #include "filesys.h"
00025 #include "tilde.h"
00026 
00027 /* The "dir" node can be built from the contents of a file called "dir",
00028    with the addition of the menus of every file named in the array
00029    dirs_to_add which are found in INFOPATH. */
00030 
00031 static void add_menu_to_file_buffer (char *contents, long int size,
00032     FILE_BUFFER *fb);
00033 static void insert_text_into_fb_at_binding (FILE_BUFFER *fb,
00034     SEARCH_BINDING *binding, char *text, int textlen);
00035 void maybe_build_dir_node (char *dirname);
00036 
00037 static char *dirs_to_add[] = {
00038   "dir", "localdir", (char *)NULL
00039 };
00040 
00041 
00042 /* Return zero if the file represented in the stat structure TEST has
00043    already been seen, nonzero else.  */
00044 
00045 typedef struct
00046 {
00047   unsigned long device;
00048   unsigned long inode;
00049 } dir_file_list_entry_type;
00050 
00051 static int
00052 new_dir_file_p (struct stat *test)
00053 {
00054   static unsigned dir_file_list_len = 0;
00055   static dir_file_list_entry_type *dir_file_list = NULL;
00056   unsigned i;
00057   
00058   for (i = 0; i < dir_file_list_len; i++)
00059     {
00060       dir_file_list_entry_type entry;
00061       entry = dir_file_list[i];
00062       if (entry.device == test->st_dev && entry.inode == test->st_ino)
00063         return 0;
00064     }
00065   
00066   dir_file_list_len++;
00067   dir_file_list = xrealloc (dir_file_list, 
00068                         dir_file_list_len * sizeof (dir_file_list_entry_type));
00069   dir_file_list[dir_file_list_len - 1].device = test->st_dev;
00070   dir_file_list[dir_file_list_len - 1].inode = test->st_ino;
00071   return 1;
00072 }
00073 
00074 
00075 void
00076 maybe_build_dir_node (char *dirname)
00077 {
00078   int path_index, update_tags;
00079   char *this_dir;
00080   FILE_BUFFER *dir_buffer = info_find_file (dirname);
00081 
00082   /* If there is no "dir" in the current info path, we cannot build one
00083      from nothing. */
00084   if (!dir_buffer)
00085     return;
00086 
00087   /* If this directory has already been built, return now. */
00088   if (dir_buffer->flags & N_CannotGC)
00089     return;
00090 
00091   /* Initialize the list we use to avoid reading the same dir file twice
00092      with the dir file just found.  */
00093   new_dir_file_p (&dir_buffer->finfo);
00094   
00095   path_index = update_tags = 0;
00096 
00097   /* Using each element of the path, check for one of the files in
00098      DIRS_TO_ADD.  Do not check for "localdir.info.Z" or anything else.
00099      Only files explictly named are eligible.  This is a design decision.
00100      There can be an info file name "localdir.info" which contains
00101      information on the setting up of "localdir" files. */
00102   while ((this_dir = extract_colon_unit (infopath, &path_index)))
00103     {
00104       register int da_index;
00105       char *from_file;
00106 
00107       /* Expand a leading tilde if one is present. */
00108       if (*this_dir == '~')
00109         {
00110           char *tilde_expanded_dirname;
00111 
00112           tilde_expanded_dirname = tilde_expand_word (this_dir);
00113           if (tilde_expanded_dirname != this_dir)
00114             {
00115               free (this_dir);
00116               this_dir = tilde_expanded_dirname;
00117             }
00118         }
00119 
00120       /* For every different file named in DIRS_TO_ADD found in the
00121          search path, add that file's menu to our "dir" node. */
00122       for (da_index = 0; (from_file = dirs_to_add[da_index]); da_index++)
00123         {
00124           struct stat finfo;
00125           int statable;
00126           int namelen = strlen (from_file);
00127           char *fullpath = xmalloc (3 + strlen (this_dir) + namelen);
00128           
00129           strcpy (fullpath, this_dir);
00130           if (!IS_SLASH (fullpath[strlen (fullpath) - 1]))
00131             strcat (fullpath, "/");
00132           strcat (fullpath, from_file);
00133 
00134           statable = (stat (fullpath, &finfo) == 0);
00135 
00136           /* Only add this file if we have not seen it before.  */
00137           if (statable && S_ISREG (finfo.st_mode) && new_dir_file_p (&finfo))
00138             {
00139               long filesize;
00140              int compressed;
00141               char *contents = filesys_read_info_file (fullpath, &filesize,
00142                                                        &finfo, &compressed);
00143               if (contents)
00144                 {
00145                   update_tags++;
00146                   add_menu_to_file_buffer (contents, filesize, dir_buffer);
00147                   free (contents);
00148                 }
00149             }
00150 
00151           free (fullpath);
00152         }
00153       free (this_dir);
00154     }
00155 
00156   if (update_tags)
00157     build_tags_and_nodes (dir_buffer);
00158 
00159   /* Flag that the dir buffer has been built. */
00160   dir_buffer->flags |= N_CannotGC;
00161 }
00162 
00163 /* Given CONTENTS and FB (a file buffer), add the menu found in CONTENTS
00164    to the menu found in FB->contents.  Second argument SIZE is the total
00165    size of CONTENTS. */
00166 static void
00167 add_menu_to_file_buffer (char *contents, long int size, FILE_BUFFER *fb)
00168 {
00169   SEARCH_BINDING contents_binding, fb_binding;
00170   long contents_offset, fb_offset;
00171 
00172   contents_binding.buffer = contents;
00173   contents_binding.start = 0;
00174   contents_binding.end = size;
00175   contents_binding.flags = S_FoldCase | S_SkipDest;
00176 
00177   fb_binding.buffer = fb->contents;
00178   fb_binding.start = 0;
00179   fb_binding.end = fb->filesize;
00180   fb_binding.flags = S_FoldCase | S_SkipDest;
00181 
00182   /* Move to the start of the menus in CONTENTS and FB. */
00183   contents_offset = search_forward (INFO_MENU_LABEL, &contents_binding);
00184   fb_offset = search_forward (INFO_MENU_LABEL, &fb_binding);
00185 
00186   /* If there is no menu in CONTENTS, quit now. */
00187   if (contents_offset == -1)
00188     return;
00189 
00190   /* There is a menu in CONTENTS, and contents_offset points to the first
00191      character following the menu starter string.  Skip all whitespace
00192      and newline characters. */
00193   contents_offset += skip_whitespace_and_newlines (contents + contents_offset);
00194 
00195   /* If there is no menu in FB, make one. */
00196   if (fb_offset == -1)
00197     {
00198       /* Find the start of the second node in this file buffer.  If there
00199          is only one node, we will be adding the contents to the end of
00200          this node. */
00201       fb_offset = find_node_separator (&fb_binding);
00202 
00203       /* If not even a single node separator, give up. */
00204       if (fb_offset == -1)
00205         return;
00206 
00207       fb_binding.start = fb_offset;
00208       fb_binding.start +=
00209         skip_node_separator (fb_binding.buffer + fb_binding.start);
00210 
00211       /* Try to find the next node separator. */
00212       fb_offset = find_node_separator (&fb_binding);
00213 
00214       /* If found one, consider that the start of the menu.  Otherwise, the
00215          start of this menu is the end of the file buffer (i.e., fb->size). */
00216       if (fb_offset != -1)
00217         fb_binding.start = fb_offset;
00218       else
00219         fb_binding.start = fb_binding.end;
00220 
00221       insert_text_into_fb_at_binding
00222         (fb, &fb_binding, INFO_MENU_LABEL, strlen (INFO_MENU_LABEL));
00223 
00224       fb_binding.buffer = fb->contents;
00225       fb_binding.start = 0;
00226       fb_binding.end = fb->filesize;
00227       fb_offset = search_forward (INFO_MENU_LABEL, &fb_binding);
00228       if (fb_offset == -1)
00229         abort ();
00230     }
00231 
00232   /* CONTENTS_OFFSET and FB_OFFSET point to the starts of the menus that
00233      appear in their respective buffers.  Add the remainder of CONTENTS
00234      to the end of FB's menu. */
00235   fb_binding.start = fb_offset;
00236   fb_offset = find_node_separator (&fb_binding);
00237   if (fb_offset != -1)
00238     fb_binding.start = fb_offset;
00239   else
00240     fb_binding.start = fb_binding.end;
00241 
00242   /* Leave exactly one blank line between directory entries. */
00243   {
00244     int num_found = 0;
00245 
00246     while ((fb_binding.start > 0) &&
00247            (whitespace_or_newline (fb_binding.buffer[fb_binding.start - 1])))
00248       {
00249         num_found++;
00250         fb_binding.start--;
00251       }
00252 
00253     /* Optimize if possible. */
00254     if (num_found >= 2)
00255       {
00256         fb_binding.buffer[fb_binding.start++] = '\n';
00257         fb_binding.buffer[fb_binding.start++] = '\n';
00258       }
00259     else
00260       {
00261         /* Do it the hard way. */
00262         insert_text_into_fb_at_binding (fb, &fb_binding, "\n\n", 2);
00263         fb_binding.start += 2;
00264       }
00265   }
00266 
00267   /* Insert the new menu. */
00268   insert_text_into_fb_at_binding
00269     (fb, &fb_binding, contents + contents_offset, size - contents_offset);
00270 }
00271 
00272 static void
00273 insert_text_into_fb_at_binding (FILE_BUFFER *fb,
00274     SEARCH_BINDING *binding, char *text, int textlen)
00275 {
00276   char *contents;
00277   long start, end;
00278 
00279   start = binding->start;
00280   end = fb->filesize;
00281 
00282   contents = (char *)xmalloc (fb->filesize + textlen + 1);
00283   memcpy (contents, fb->contents, start);
00284   memcpy (contents + start, text, textlen);
00285   memcpy (contents + start + textlen, fb->contents + start, end - start);
00286   free (fb->contents);
00287   fb->contents = contents;
00288   fb->filesize += textlen;
00289   fb->finfo.st_size = fb->filesize;
00290 }