156160Sru/* dir.c -- how to build a special "dir" node from "localdir" files.
2146515Sru   $Id: dir.c,v 1.3 2004/04/11 17:56:45 karl Exp $
321495Sjmacd
4146515Sru   Copyright (C) 1993, 1997, 1998, 2004 Free Software Foundation, Inc.
521495Sjmacd
621495Sjmacd   This program is free software; you can redistribute it and/or modify
721495Sjmacd   it under the terms of the GNU General Public License as published by
821495Sjmacd   the Free Software Foundation; either version 2, or (at your option)
921495Sjmacd   any later version.
1021495Sjmacd
1121495Sjmacd   This program is distributed in the hope that it will be useful,
1221495Sjmacd   but WITHOUT ANY WARRANTY; without even the implied warranty of
1321495Sjmacd   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1421495Sjmacd   GNU General Public License for more details.
1521495Sjmacd
1621495Sjmacd   You should have received a copy of the GNU General Public License
1721495Sjmacd   along with this program; if not, write to the Free Software
1821495Sjmacd   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
1921495Sjmacd
2021495Sjmacd   Written by Brian Fox (bfox@ai.mit.edu). */
2121495Sjmacd
2242660Smarkm#include "info.h"
2321495Sjmacd#include "info-utils.h"
2421495Sjmacd#include "filesys.h"
2521495Sjmacd#include "tilde.h"
2621495Sjmacd
2721495Sjmacd/* The "dir" node can be built from the contents of a file called "dir",
2821495Sjmacd   with the addition of the menus of every file named in the array
2921495Sjmacd   dirs_to_add which are found in INFOPATH. */
3021495Sjmacd
31146515Srustatic void add_menu_to_file_buffer (char *contents, long int size,
32146515Sru    FILE_BUFFER *fb);
33146515Srustatic void insert_text_into_fb_at_binding (FILE_BUFFER *fb,
34146515Sru    SEARCH_BINDING *binding, char *text, int textlen);
35146515Sruvoid maybe_build_dir_node (char *dirname);
3621495Sjmacd
3721495Sjmacdstatic char *dirs_to_add[] = {
3821495Sjmacd  "dir", "localdir", (char *)NULL
3921495Sjmacd};
4021495Sjmacd
4142660Smarkm
4242660Smarkm/* Return zero if the file represented in the stat structure TEST has
4342660Smarkm   already been seen, nonzero else.  */
4442660Smarkm
4542660Smarkmtypedef struct
4642660Smarkm{
4742660Smarkm  unsigned long device;
4842660Smarkm  unsigned long inode;
4942660Smarkm} dir_file_list_entry_type;
5042660Smarkm
5142660Smarkmstatic int
52146515Srunew_dir_file_p (struct stat *test)
5342660Smarkm{
5442660Smarkm  static unsigned dir_file_list_len = 0;
5542660Smarkm  static dir_file_list_entry_type *dir_file_list = NULL;
5642660Smarkm  unsigned i;
5742660Smarkm
5842660Smarkm  for (i = 0; i < dir_file_list_len; i++)
5942660Smarkm    {
6042660Smarkm      dir_file_list_entry_type entry;
6142660Smarkm      entry = dir_file_list[i];
6242660Smarkm      if (entry.device == test->st_dev && entry.inode == test->st_ino)
6342660Smarkm        return 0;
6442660Smarkm    }
6542660Smarkm
6642660Smarkm  dir_file_list_len++;
6742660Smarkm  dir_file_list = xrealloc (dir_file_list,
6842660Smarkm                        dir_file_list_len * sizeof (dir_file_list_entry_type));
6942660Smarkm  dir_file_list[dir_file_list_len - 1].device = test->st_dev;
7042660Smarkm  dir_file_list[dir_file_list_len - 1].inode = test->st_ino;
7142660Smarkm  return 1;
7242660Smarkm}
7342660Smarkm
7442660Smarkm
7521495Sjmacdvoid
76146515Srumaybe_build_dir_node (char *dirname)
7721495Sjmacd{
7821495Sjmacd  int path_index, update_tags;
7921495Sjmacd  char *this_dir;
8042660Smarkm  FILE_BUFFER *dir_buffer = info_find_file (dirname);
8121495Sjmacd
8221495Sjmacd  /* If there is no "dir" in the current info path, we cannot build one
8321495Sjmacd     from nothing. */
8421495Sjmacd  if (!dir_buffer)
8521495Sjmacd    return;
8621495Sjmacd
8721495Sjmacd  /* If this directory has already been built, return now. */
8821495Sjmacd  if (dir_buffer->flags & N_CannotGC)
8921495Sjmacd    return;
9021495Sjmacd
9142660Smarkm  /* Initialize the list we use to avoid reading the same dir file twice
9242660Smarkm     with the dir file just found.  */
9342660Smarkm  new_dir_file_p (&dir_buffer->finfo);
9442660Smarkm
9521495Sjmacd  path_index = update_tags = 0;
9621495Sjmacd
9721495Sjmacd  /* Using each element of the path, check for one of the files in
9821495Sjmacd     DIRS_TO_ADD.  Do not check for "localdir.info.Z" or anything else.
9921495Sjmacd     Only files explictly named are eligible.  This is a design decision.
10021495Sjmacd     There can be an info file name "localdir.info" which contains
10121495Sjmacd     information on the setting up of "localdir" files. */
10242660Smarkm  while ((this_dir = extract_colon_unit (infopath, &path_index)))
10321495Sjmacd    {
10421495Sjmacd      register int da_index;
10521495Sjmacd      char *from_file;
10621495Sjmacd
10721495Sjmacd      /* Expand a leading tilde if one is present. */
10821495Sjmacd      if (*this_dir == '~')
10942660Smarkm        {
11042660Smarkm          char *tilde_expanded_dirname;
11121495Sjmacd
11242660Smarkm          tilde_expanded_dirname = tilde_expand_word (this_dir);
11342660Smarkm          if (tilde_expanded_dirname != this_dir)
11442660Smarkm            {
11542660Smarkm              free (this_dir);
11642660Smarkm              this_dir = tilde_expanded_dirname;
11742660Smarkm            }
11842660Smarkm        }
11921495Sjmacd
12042660Smarkm      /* For every different file named in DIRS_TO_ADD found in the
12142660Smarkm         search path, add that file's menu to our "dir" node. */
12242660Smarkm      for (da_index = 0; (from_file = dirs_to_add[da_index]); da_index++)
12342660Smarkm        {
12442660Smarkm          struct stat finfo;
12542660Smarkm          int statable;
12642660Smarkm          int namelen = strlen (from_file);
12742660Smarkm          char *fullpath = xmalloc (3 + strlen (this_dir) + namelen);
12842660Smarkm
12942660Smarkm          strcpy (fullpath, this_dir);
13056160Sru          if (!IS_SLASH (fullpath[strlen (fullpath) - 1]))
13142660Smarkm            strcat (fullpath, "/");
13242660Smarkm          strcat (fullpath, from_file);
13321495Sjmacd
13442660Smarkm          statable = (stat (fullpath, &finfo) == 0);
13521495Sjmacd
13642660Smarkm          /* Only add this file if we have not seen it before.  */
13742660Smarkm          if (statable && S_ISREG (finfo.st_mode) && new_dir_file_p (&finfo))
13842660Smarkm            {
13942660Smarkm              long filesize;
14056160Sru	      int compressed;
14142660Smarkm              char *contents = filesys_read_info_file (fullpath, &filesize,
14256160Sru                                                       &finfo, &compressed);
14342660Smarkm              if (contents)
14442660Smarkm                {
14542660Smarkm                  update_tags++;
14642660Smarkm                  add_menu_to_file_buffer (contents, filesize, dir_buffer);
14742660Smarkm                  free (contents);
14842660Smarkm                }
14942660Smarkm            }
15021495Sjmacd
15142660Smarkm          free (fullpath);
15242660Smarkm        }
15321495Sjmacd      free (this_dir);
15421495Sjmacd    }
15521495Sjmacd
15621495Sjmacd  if (update_tags)
15721495Sjmacd    build_tags_and_nodes (dir_buffer);
15821495Sjmacd
15921495Sjmacd  /* Flag that the dir buffer has been built. */
16021495Sjmacd  dir_buffer->flags |= N_CannotGC;
16121495Sjmacd}
16221495Sjmacd
16321495Sjmacd/* Given CONTENTS and FB (a file buffer), add the menu found in CONTENTS
16421495Sjmacd   to the menu found in FB->contents.  Second argument SIZE is the total
16521495Sjmacd   size of CONTENTS. */
16621495Sjmacdstatic void
167146515Sruadd_menu_to_file_buffer (char *contents, long int size, FILE_BUFFER *fb)
16821495Sjmacd{
16921495Sjmacd  SEARCH_BINDING contents_binding, fb_binding;
17021495Sjmacd  long contents_offset, fb_offset;
17121495Sjmacd
17221495Sjmacd  contents_binding.buffer = contents;
17321495Sjmacd  contents_binding.start = 0;
17421495Sjmacd  contents_binding.end = size;
17521495Sjmacd  contents_binding.flags = S_FoldCase | S_SkipDest;
17621495Sjmacd
17721495Sjmacd  fb_binding.buffer = fb->contents;
17821495Sjmacd  fb_binding.start = 0;
17921495Sjmacd  fb_binding.end = fb->filesize;
18021495Sjmacd  fb_binding.flags = S_FoldCase | S_SkipDest;
18121495Sjmacd
18221495Sjmacd  /* Move to the start of the menus in CONTENTS and FB. */
18321495Sjmacd  contents_offset = search_forward (INFO_MENU_LABEL, &contents_binding);
18421495Sjmacd  fb_offset = search_forward (INFO_MENU_LABEL, &fb_binding);
18521495Sjmacd
18621495Sjmacd  /* If there is no menu in CONTENTS, quit now. */
18721495Sjmacd  if (contents_offset == -1)
18821495Sjmacd    return;
18921495Sjmacd
19021495Sjmacd  /* There is a menu in CONTENTS, and contents_offset points to the first
19121495Sjmacd     character following the menu starter string.  Skip all whitespace
19221495Sjmacd     and newline characters. */
19321495Sjmacd  contents_offset += skip_whitespace_and_newlines (contents + contents_offset);
19421495Sjmacd
19521495Sjmacd  /* If there is no menu in FB, make one. */
19621495Sjmacd  if (fb_offset == -1)
19721495Sjmacd    {
19821495Sjmacd      /* Find the start of the second node in this file buffer.  If there
19942660Smarkm         is only one node, we will be adding the contents to the end of
20042660Smarkm         this node. */
20121495Sjmacd      fb_offset = find_node_separator (&fb_binding);
20221495Sjmacd
20321495Sjmacd      /* If not even a single node separator, give up. */
20421495Sjmacd      if (fb_offset == -1)
20542660Smarkm        return;
20621495Sjmacd
20721495Sjmacd      fb_binding.start = fb_offset;
20821495Sjmacd      fb_binding.start +=
20942660Smarkm        skip_node_separator (fb_binding.buffer + fb_binding.start);
21021495Sjmacd
21121495Sjmacd      /* Try to find the next node separator. */
21221495Sjmacd      fb_offset = find_node_separator (&fb_binding);
21321495Sjmacd
21421495Sjmacd      /* If found one, consider that the start of the menu.  Otherwise, the
21542660Smarkm         start of this menu is the end of the file buffer (i.e., fb->size). */
21621495Sjmacd      if (fb_offset != -1)
21742660Smarkm        fb_binding.start = fb_offset;
21821495Sjmacd      else
21942660Smarkm        fb_binding.start = fb_binding.end;
22021495Sjmacd
22121495Sjmacd      insert_text_into_fb_at_binding
22242660Smarkm        (fb, &fb_binding, INFO_MENU_LABEL, strlen (INFO_MENU_LABEL));
22321495Sjmacd
22421495Sjmacd      fb_binding.buffer = fb->contents;
22521495Sjmacd      fb_binding.start = 0;
22621495Sjmacd      fb_binding.end = fb->filesize;
22721495Sjmacd      fb_offset = search_forward (INFO_MENU_LABEL, &fb_binding);
22821495Sjmacd      if (fb_offset == -1)
22942660Smarkm        abort ();
23021495Sjmacd    }
23121495Sjmacd
23221495Sjmacd  /* CONTENTS_OFFSET and FB_OFFSET point to the starts of the menus that
23321495Sjmacd     appear in their respective buffers.  Add the remainder of CONTENTS
23421495Sjmacd     to the end of FB's menu. */
23521495Sjmacd  fb_binding.start = fb_offset;
23621495Sjmacd  fb_offset = find_node_separator (&fb_binding);
23721495Sjmacd  if (fb_offset != -1)
23821495Sjmacd    fb_binding.start = fb_offset;
23921495Sjmacd  else
24021495Sjmacd    fb_binding.start = fb_binding.end;
24121495Sjmacd
24221495Sjmacd  /* Leave exactly one blank line between directory entries. */
24321495Sjmacd  {
24421495Sjmacd    int num_found = 0;
24521495Sjmacd
24621495Sjmacd    while ((fb_binding.start > 0) &&
24742660Smarkm           (whitespace_or_newline (fb_binding.buffer[fb_binding.start - 1])))
24821495Sjmacd      {
24942660Smarkm        num_found++;
25042660Smarkm        fb_binding.start--;
25121495Sjmacd      }
25221495Sjmacd
25321495Sjmacd    /* Optimize if possible. */
25421495Sjmacd    if (num_found >= 2)
25521495Sjmacd      {
25642660Smarkm        fb_binding.buffer[fb_binding.start++] = '\n';
25742660Smarkm        fb_binding.buffer[fb_binding.start++] = '\n';
25821495Sjmacd      }
25921495Sjmacd    else
26021495Sjmacd      {
26142660Smarkm        /* Do it the hard way. */
26242660Smarkm        insert_text_into_fb_at_binding (fb, &fb_binding, "\n\n", 2);
26342660Smarkm        fb_binding.start += 2;
26421495Sjmacd      }
26521495Sjmacd  }
26621495Sjmacd
26721495Sjmacd  /* Insert the new menu. */
26821495Sjmacd  insert_text_into_fb_at_binding
26921495Sjmacd    (fb, &fb_binding, contents + contents_offset, size - contents_offset);
27021495Sjmacd}
27121495Sjmacd
27221495Sjmacdstatic void
273146515Sruinsert_text_into_fb_at_binding (FILE_BUFFER *fb,
274146515Sru    SEARCH_BINDING *binding, char *text, int textlen)
27521495Sjmacd{
27621495Sjmacd  char *contents;
27721495Sjmacd  long start, end;
27821495Sjmacd
27921495Sjmacd  start = binding->start;
28021495Sjmacd  end = fb->filesize;
28121495Sjmacd
28221495Sjmacd  contents = (char *)xmalloc (fb->filesize + textlen + 1);
28321495Sjmacd  memcpy (contents, fb->contents, start);
28421495Sjmacd  memcpy (contents + start, text, textlen);
28521495Sjmacd  memcpy (contents + start + textlen, fb->contents + start, end - start);
28621495Sjmacd  free (fb->contents);
28721495Sjmacd  fb->contents = contents;
28821495Sjmacd  fb->filesize += textlen;
28921495Sjmacd  fb->finfo.st_size = fb->filesize;
29021495Sjmacd}
291