home *** CD-ROM | disk | FTP | other *** search
/ Dream 52 / Amiga_Dream_52.iso / OS2 / gnuinfo.zip / info / dir.c next >
C/C++ Source or Header  |  1998-01-04  |  10KB  |  322 lines

  1. /* dir.c -- How to build a special "dir" node from "localdir" files.
  2.    $Id: dir.c,v 1.6 1997/07/27 21:09:20 karl Exp $
  3.  
  4.    Copyright (C) 1993, 97 Free Software Foundation, Inc.
  5.  
  6.    This program is free software; you can redistribute it and/or modify
  7.    it under the terms of the GNU General Public License as published by
  8.    the Free Software Foundation; either version 2, or (at your option)
  9.    any later version.
  10.  
  11.    This program is distributed in the hope that it will be useful,
  12.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.    GNU General Public License for more details.
  15.  
  16.    You should have received a copy of the GNU General Public License
  17.    along with this program; if not, write to the Free Software
  18.    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  19.  
  20.    Written by Brian Fox (bfox@ai.mit.edu). */
  21.  
  22. #include "info.h"
  23. #include "info-utils.h"
  24. #include "filesys.h"
  25. #include "tilde.h"
  26.  
  27. /* The "dir" node can be built from the contents of a file called "dir",
  28.    with the addition of the menus of every file named in the array
  29.    dirs_to_add which are found in INFOPATH. */
  30.  
  31. static void add_menu_to_file_buffer (), insert_text_into_fb_at_binding ();
  32.  
  33. static char *dirs_to_add[] = {
  34.   "dir", "localdir", (char *)NULL
  35. };
  36.  
  37.  
  38. /* Return zero if the file represented in the stat structure TEST has
  39.    already been seen, nonzero else.  */
  40.  
  41. typedef struct
  42. {
  43. #ifdef __EMX__
  44.   char *path;
  45. #else
  46.   unsigned long device;
  47.   unsigned long inode;
  48. #endif
  49. } dir_file_list_entry_type;
  50.  
  51. static int
  52. new_dir_file_p (test)
  53. #ifdef __EMX__
  54.     char *test;
  55. #else
  56.     struct stat *test;
  57. #endif
  58. {
  59.   static unsigned dir_file_list_len = 0;
  60.   static dir_file_list_entry_type *dir_file_list = NULL;
  61.   unsigned i;
  62. #ifdef __EMX__
  63.   char path[1024];
  64.   _fullpath(path, test, sizeof(path));
  65. #endif
  66.   
  67.   for (i = 0; i < dir_file_list_len; i++)
  68.     {
  69.       dir_file_list_entry_type entry;
  70.       entry = dir_file_list[i];
  71. #ifdef __EMX__
  72.       if (stricmp(path, entry.path) == 0)
  73. #else
  74.       if (entry.device == test->st_dev && entry.inode == test->st_ino)
  75. #endif
  76.         return 0;
  77.     }
  78.   
  79.   dir_file_list_len++;
  80.   dir_file_list = xrealloc (dir_file_list, 
  81.                         dir_file_list_len * sizeof (dir_file_list_entry_type));
  82. #ifdef __EMX__
  83.   dir_file_list[dir_file_list_len - 1].path = strdup(path);
  84. #else
  85.   dir_file_list[dir_file_list_len - 1].device = test->st_dev;
  86.   dir_file_list[dir_file_list_len - 1].inode = test->st_ino;
  87. #endif
  88.   return 1;
  89. }
  90.  
  91.  
  92. void
  93. maybe_build_dir_node (dirname)
  94.      char *dirname;
  95. {
  96.   int path_index, update_tags;
  97.   char *this_dir;
  98.   FILE_BUFFER *dir_buffer = info_find_file (dirname);
  99.  
  100.   /* If there is no "dir" in the current info path, we cannot build one
  101.      from nothing. */
  102.   if (!dir_buffer)
  103.     return;
  104.  
  105.   /* If this directory has already been built, return now. */
  106.   if (dir_buffer->flags & N_CannotGC)
  107.     return;
  108.  
  109.   /* Initialize the list we use to avoid reading the same dir file twice
  110.      with the dir file just found.  */
  111. #ifdef __EMX__
  112.   new_dir_file_p (dir_buffer->fullpath);
  113. #else
  114.   new_dir_file_p (&dir_buffer->finfo);
  115. #endif
  116.   
  117.   path_index = update_tags = 0;
  118.  
  119.   /* Using each element of the path, check for one of the files in
  120.      DIRS_TO_ADD.  Do not check for "localdir.info.Z" or anything else.
  121.      Only files explictly named are eligible.  This is a design decision.
  122.      There can be an info file name "localdir.info" which contains
  123.      information on the setting up of "localdir" files. */
  124.   while ((this_dir = extract_colon_unit (infopath, &path_index)))
  125.     {
  126.       register int da_index;
  127.       char *from_file;
  128.  
  129.       /* Expand a leading tilde if one is present. */
  130.       if (*this_dir == '~')
  131.         {
  132.           char *tilde_expanded_dirname;
  133.  
  134.           tilde_expanded_dirname = tilde_expand_word (this_dir);
  135.           if (tilde_expanded_dirname != this_dir)
  136.             {
  137.               free (this_dir);
  138.               this_dir = tilde_expanded_dirname;
  139.             }
  140.         }
  141.  
  142.       /* For every different file named in DIRS_TO_ADD found in the
  143.          search path, add that file's menu to our "dir" node. */
  144.       for (da_index = 0; (from_file = dirs_to_add[da_index]); da_index++)
  145.         {
  146.           struct stat finfo;
  147.           int statable;
  148.           int namelen = strlen (from_file);
  149.           char *fullpath = xmalloc (3 + strlen (this_dir) + namelen);
  150.           
  151.           strcpy (fullpath, this_dir);
  152.           if (fullpath[strlen (fullpath) - 1] != '/')
  153.             strcat (fullpath, "/");
  154.           strcat (fullpath, from_file);
  155.  
  156.           statable = (stat (fullpath, &finfo) == 0);
  157.  
  158.           /* Only add this file if we have not seen it before.  */
  159. #ifdef __EMX__
  160.           if (statable && S_ISREG (finfo.st_mode) && new_dir_file_p (fullpath))
  161. #else
  162.           if (statable && S_ISREG (finfo.st_mode) && new_dir_file_p (&finfo))
  163. #endif
  164.             {
  165.               long filesize;
  166.               char *contents = filesys_read_info_file (fullpath, &filesize,
  167.                                                        &finfo);
  168.               if (contents)
  169.                 {
  170.                   update_tags++;
  171.                   add_menu_to_file_buffer (contents, filesize, dir_buffer);
  172.                   free (contents);
  173.                 }
  174.             }
  175.  
  176.           free (fullpath);
  177.         }
  178.       free (this_dir);
  179.     }
  180.  
  181.   if (update_tags)
  182.     build_tags_and_nodes (dir_buffer);
  183.  
  184.   /* Flag that the dir buffer has been built. */
  185.   dir_buffer->flags |= N_CannotGC;
  186. }
  187.  
  188. /* Given CONTENTS and FB (a file buffer), add the menu found in CONTENTS
  189.    to the menu found in FB->contents.  Second argument SIZE is the total
  190.    size of CONTENTS. */
  191. static void
  192. add_menu_to_file_buffer (contents, size, fb)
  193.      char *contents;
  194.      long size;
  195.      FILE_BUFFER *fb;
  196. {
  197.   SEARCH_BINDING contents_binding, fb_binding;
  198.   long contents_offset, fb_offset;
  199.  
  200.   contents_binding.buffer = contents;
  201.   contents_binding.start = 0;
  202.   contents_binding.end = size;
  203.   contents_binding.flags = S_FoldCase | S_SkipDest;
  204.  
  205.   fb_binding.buffer = fb->contents;
  206.   fb_binding.start = 0;
  207.   fb_binding.end = fb->filesize;
  208.   fb_binding.flags = S_FoldCase | S_SkipDest;
  209.  
  210.   /* Move to the start of the menus in CONTENTS and FB. */
  211.   contents_offset = search_forward (INFO_MENU_LABEL, &contents_binding);
  212.   fb_offset = search_forward (INFO_MENU_LABEL, &fb_binding);
  213.  
  214.   /* If there is no menu in CONTENTS, quit now. */
  215.   if (contents_offset == -1)
  216.     return;
  217.  
  218.   /* There is a menu in CONTENTS, and contents_offset points to the first
  219.      character following the menu starter string.  Skip all whitespace
  220.      and newline characters. */
  221.   contents_offset += skip_whitespace_and_newlines (contents + contents_offset);
  222.  
  223.   /* If there is no menu in FB, make one. */
  224.   if (fb_offset == -1)
  225.     {
  226.       /* Find the start of the second node in this file buffer.  If there
  227.          is only one node, we will be adding the contents to the end of
  228.          this node. */
  229.       fb_offset = find_node_separator (&fb_binding);
  230.  
  231.       /* If not even a single node separator, give up. */
  232.       if (fb_offset == -1)
  233.         return;
  234.  
  235.       fb_binding.start = fb_offset;
  236.       fb_binding.start +=
  237.         skip_node_separator (fb_binding.buffer + fb_binding.start);
  238.  
  239.       /* Try to find the next node separator. */
  240.       fb_offset = find_node_separator (&fb_binding);
  241.  
  242.       /* If found one, consider that the start of the menu.  Otherwise, the
  243.          start of this menu is the end of the file buffer (i.e., fb->size). */
  244.       if (fb_offset != -1)
  245.         fb_binding.start = fb_offset;
  246.       else
  247.         fb_binding.start = fb_binding.end;
  248.  
  249.       insert_text_into_fb_at_binding
  250.         (fb, &fb_binding, INFO_MENU_LABEL, strlen (INFO_MENU_LABEL));
  251.  
  252.       fb_binding.buffer = fb->contents;
  253.       fb_binding.start = 0;
  254.       fb_binding.end = fb->filesize;
  255.       fb_offset = search_forward (INFO_MENU_LABEL, &fb_binding);
  256.       if (fb_offset == -1)
  257.         abort ();
  258.     }
  259.  
  260.   /* CONTENTS_OFFSET and FB_OFFSET point to the starts of the menus that
  261.      appear in their respective buffers.  Add the remainder of CONTENTS
  262.      to the end of FB's menu. */
  263.   fb_binding.start = fb_offset;
  264.   fb_offset = find_node_separator (&fb_binding);
  265.   if (fb_offset != -1)
  266.     fb_binding.start = fb_offset;
  267.   else
  268.     fb_binding.start = fb_binding.end;
  269.  
  270.   /* Leave exactly one blank line between directory entries. */
  271.   {
  272.     int num_found = 0;
  273.  
  274.     while ((fb_binding.start > 0) &&
  275.            (whitespace_or_newline (fb_binding.buffer[fb_binding.start - 1])))
  276.       {
  277.         num_found++;
  278.         fb_binding.start--;
  279.       }
  280.  
  281.     /* Optimize if possible. */
  282.     if (num_found >= 2)
  283.       {
  284.         fb_binding.buffer[fb_binding.start++] = '\n';
  285.         fb_binding.buffer[fb_binding.start++] = '\n';
  286.       }
  287.     else
  288.       {
  289.         /* Do it the hard way. */
  290.         insert_text_into_fb_at_binding (fb, &fb_binding, "\n\n", 2);
  291.         fb_binding.start += 2;
  292.       }
  293.   }
  294.  
  295.   /* Insert the new menu. */
  296.   insert_text_into_fb_at_binding
  297.     (fb, &fb_binding, contents + contents_offset, size - contents_offset);
  298. }
  299.  
  300. static void
  301. insert_text_into_fb_at_binding (fb, binding, text, textlen)
  302.      FILE_BUFFER *fb;
  303.      SEARCH_BINDING *binding;
  304.      char *text;
  305.      int textlen;
  306. {
  307.   char *contents;
  308.   long start, end;
  309.  
  310.   start = binding->start;
  311.   end = fb->filesize;
  312.  
  313.   contents = (char *)xmalloc (fb->filesize + textlen + 1);
  314.   memcpy (contents, fb->contents, start);
  315.   memcpy (contents + start, text, textlen);
  316.   memcpy (contents + start + textlen, fb->contents + start, end - start);
  317.   free (fb->contents);
  318.   fb->contents = contents;
  319.   fb->filesize += textlen;
  320.   fb->finfo.st_size = fb->filesize;
  321. }
  322.