home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / texinfo-3.7-src.tgz / tar.out / fsf / texinfo / info / dir.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  8KB  |  274 lines

  1. /* dir.c -- How to build a special "dir" node from "localdir" files. */
  2.  
  3. /* This file is part of GNU Info, a program for reading online documentation
  4.    stored in Info format.
  5.  
  6.    Copyright (C) 1993 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 <stdio.h>
  25. #include <sys/types.h>
  26. #include <sys/stat.h>
  27. #if defined (HAVE_SYS_FILE_H)
  28. #include <sys/file.h>
  29. #endif /* HAVE_SYS_FILE_H */
  30. #include <sys/errno.h>
  31. #include "info-utils.h"
  32. #include "filesys.h"
  33. #include "tilde.h"
  34.  
  35. /* The "dir" node can be built from the contents of a file called "dir",
  36.    with the addition of the menus of every file named in the array
  37.    dirs_to_add which are found in INFOPATH. */
  38.  
  39. static void add_menu_to_file_buffer (), insert_text_into_fb_at_binding ();
  40. static void build_dir_node_internal ();
  41.  
  42. static char *dirs_to_add[] = {
  43.   "dir", "localdir", (char *)NULL
  44. };
  45.  
  46. void
  47. maybe_build_dir_node (dirname)
  48.      char *dirname;
  49. {
  50.   FILE_BUFFER *dir_buffer;
  51.   int path_index, update_tags;
  52.   char *this_dir;
  53.  
  54.   /* Check to see if the file has already been built.  If so, then
  55.      do not build it again. */
  56.   dir_buffer = info_find_file (dirname);
  57.  
  58.   /* If there is no "dir" in the current info path, we cannot build one
  59.      from nothing. */
  60.   if (!dir_buffer)
  61.     return;
  62.  
  63.   /* If this directory has already been built, return now. */
  64.   if (dir_buffer->flags & N_CannotGC)
  65.     return;
  66.  
  67.   path_index = update_tags = 0;
  68.  
  69.   /* Using each element of the path, check for one of the files in
  70.      DIRS_TO_ADD.  Do not check for "localdir.info.Z" or anything else.
  71.      Only files explictly named are eligible.  This is a design decision.
  72.      There can be an info file name "localdir.info" which contains
  73.      information on the setting up of "localdir" files. */
  74.   while (this_dir = extract_colon_unit (infopath, &path_index))
  75.     {
  76.       register int da_index;
  77.       char *from_file;
  78.  
  79.       /* Expand a leading tilde if one is present. */
  80.       if (*this_dir == '~')
  81.     {
  82.       char *tilde_expanded_dirname;
  83.  
  84.       tilde_expanded_dirname = tilde_expand_word (this_dir);
  85.       if (tilde_expanded_dirname != this_dir)
  86.         {
  87.           free (this_dir);
  88.           this_dir = tilde_expanded_dirname;
  89.         }
  90.     }
  91.  
  92.       /* For every file named in DIRS_TO_ADD found in the search path,
  93.      add the contents of that file's menu to our "dir" node. */
  94.       for (da_index = 0; from_file = dirs_to_add[da_index]; da_index++)
  95.     {
  96.       struct stat finfo;
  97.       char *fullpath;
  98.       int namelen, statable;
  99.  
  100.       namelen = strlen (from_file);
  101.  
  102.       fullpath = (char *)xmalloc (3 + strlen (this_dir) + namelen);
  103.       strcpy (fullpath, this_dir);
  104.       if (fullpath[strlen (fullpath) - 1] != '/')
  105.         strcat (fullpath, "/");
  106.       strcat (fullpath, from_file);
  107.  
  108.       statable = (stat (fullpath, &finfo) == 0);
  109.  
  110.       /* Only add the contents of this file if it is not identical to the
  111.          file of the DIR buffer. */
  112.       if ((statable && S_ISREG (finfo.st_mode)) &&
  113.           (strcmp (dir_buffer->fullpath, fullpath) != 0))
  114.         {
  115.           long filesize;
  116.           char *contents;
  117.  
  118.           contents = filesys_read_info_file (fullpath, &filesize, &finfo);
  119.  
  120.           if (contents)
  121.         {
  122.           update_tags++;
  123.           add_menu_to_file_buffer (contents, filesize, dir_buffer);
  124.           free (contents);
  125.         }
  126.         }
  127.  
  128.       free (fullpath);
  129.     }
  130.       free (this_dir);
  131.     }
  132.  
  133.   if (update_tags)
  134.     build_tags_and_nodes (dir_buffer);
  135.  
  136.   /* Flag that the dir buffer has been built. */
  137.   dir_buffer->flags |= N_CannotGC;
  138. }
  139.  
  140. /* Given CONTENTS and FB (a file buffer), add the menu found in CONTENTS
  141.    to the menu found in FB->contents.  Second argument SIZE is the total
  142.    size of CONTENTS. */
  143. static void
  144. add_menu_to_file_buffer (contents, size, fb)
  145.      char *contents;
  146.      long size;
  147.      FILE_BUFFER *fb;
  148. {
  149.   SEARCH_BINDING contents_binding, fb_binding;
  150.   long contents_offset, fb_offset;
  151.  
  152.   contents_binding.buffer = contents;
  153.   contents_binding.start = 0;
  154.   contents_binding.end = size;
  155.   contents_binding.flags = S_FoldCase | S_SkipDest;
  156.  
  157.   fb_binding.buffer = fb->contents;
  158.   fb_binding.start = 0;
  159.   fb_binding.end = fb->filesize;
  160.   fb_binding.flags = S_FoldCase | S_SkipDest;
  161.  
  162.   /* Move to the start of the menus in CONTENTS and FB. */
  163.   contents_offset = search_forward (INFO_MENU_LABEL, &contents_binding);
  164.   fb_offset = search_forward (INFO_MENU_LABEL, &fb_binding);
  165.  
  166.   /* If there is no menu in CONTENTS, quit now. */
  167.   if (contents_offset == -1)
  168.     return;
  169.  
  170.   /* There is a menu in CONTENTS, and contents_offset points to the first
  171.      character following the menu starter string.  Skip all whitespace
  172.      and newline characters. */
  173.   contents_offset += skip_whitespace_and_newlines (contents + contents_offset);
  174.  
  175.   /* If there is no menu in FB, make one. */
  176.   if (fb_offset == -1)
  177.     {
  178.       /* Find the start of the second node in this file buffer.  If there
  179.      is only one node, we will be adding the contents to the end of
  180.      this node. */
  181.       fb_offset = find_node_separator (&fb_binding);
  182.  
  183.       /* If not even a single node separator, give up. */
  184.       if (fb_offset == -1)
  185.     return;
  186.  
  187.       fb_binding.start = fb_offset;
  188.       fb_binding.start +=
  189.     skip_node_separator (fb_binding.buffer + fb_binding.start);
  190.  
  191.       /* Try to find the next node separator. */
  192.       fb_offset = find_node_separator (&fb_binding);
  193.  
  194.       /* If found one, consider that the start of the menu.  Otherwise, the
  195.      start of this menu is the end of the file buffer (i.e., fb->size). */
  196.       if (fb_offset != -1)
  197.     fb_binding.start = fb_offset;
  198.       else
  199.     fb_binding.start = fb_binding.end;
  200.  
  201.       insert_text_into_fb_at_binding
  202.     (fb, &fb_binding, INFO_MENU_LABEL, strlen (INFO_MENU_LABEL));
  203.  
  204.       fb_binding.buffer = fb->contents;
  205.       fb_binding.start = 0;
  206.       fb_binding.end = fb->filesize;
  207.       fb_offset = search_forward (INFO_MENU_LABEL, &fb_binding);
  208.       if (fb_offset == -1)
  209.     abort ();
  210.     }
  211.  
  212.   /* CONTENTS_OFFSET and FB_OFFSET point to the starts of the menus that
  213.      appear in their respective buffers.  Add the remainder of CONTENTS
  214.      to the end of FB's menu. */
  215.   fb_binding.start = fb_offset;
  216.   fb_offset = find_node_separator (&fb_binding);
  217.   if (fb_offset != -1)
  218.     fb_binding.start = fb_offset;
  219.   else
  220.     fb_binding.start = fb_binding.end;
  221.  
  222.   /* Leave exactly one blank line between directory entries. */
  223.   {
  224.     int num_found = 0;
  225.  
  226.     while ((fb_binding.start > 0) &&
  227.        (whitespace_or_newline (fb_binding.buffer[fb_binding.start - 1])))
  228.       {
  229.     num_found++;
  230.     fb_binding.start--;
  231.       }
  232.  
  233.     /* Optimize if possible. */
  234.     if (num_found >= 2)
  235.       {
  236.     fb_binding.buffer[fb_binding.start++] = '\n';
  237.     fb_binding.buffer[fb_binding.start++] = '\n';
  238.       }
  239.     else
  240.       {
  241.     /* Do it the hard way. */
  242.     insert_text_into_fb_at_binding (fb, &fb_binding, "\n\n", 2);
  243.     fb_binding.start += 2;
  244.       }
  245.   }
  246.  
  247.   /* Insert the new menu. */
  248.   insert_text_into_fb_at_binding
  249.     (fb, &fb_binding, contents + contents_offset, size - contents_offset);
  250. }
  251.  
  252. static void
  253. insert_text_into_fb_at_binding (fb, binding, text, textlen)
  254.      FILE_BUFFER *fb;
  255.      SEARCH_BINDING *binding;
  256.      char *text;
  257.      int textlen;
  258. {
  259.   char *contents;
  260.   long start, end;
  261.  
  262.   start = binding->start;
  263.   end = fb->filesize;
  264.  
  265.   contents = (char *)xmalloc (fb->filesize + textlen + 1);
  266.   memcpy (contents, fb->contents, start);
  267.   memcpy (contents + start, text, textlen);
  268.   memcpy (contents + start + textlen, fb->contents + start, end - start);
  269.   free (fb->contents);
  270.   fb->contents = contents;
  271.   fb->filesize += textlen;
  272.   fb->finfo.st_size = fb->filesize;
  273. }
  274.