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