📄 nodes.c
字号:
/* nodes.c -- How to get an Info file and node. *//* This file is part of GNU Info, a program for reading online documentation stored in Info format. Copyright (C) 1993 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Written by Brian Fox (bfox@ai.mit.edu). */#include "info.h"#include "nodes.h"#include "search.h"#include "filesys.h"#include "info-utils.h"#if defined (HANDLE_MAN_PAGES)# include "man.h"#endif /* HANDLE_MAN_PAGES *//* **************************************************************** *//* *//* Functions Static to this File *//* *//* **************************************************************** */static void forget_info_file (), remember_info_file ();static void free_file_buffer_tags (), free_info_tag ();static void get_nodes_of_tags_table (), get_nodes_of_info_file ();static void get_tags_of_indirect_tags_table ();static void info_reload_file_buffer_contents ();static char *adjust_nodestart ();static FILE_BUFFER *info_load_file_internal (), *info_find_file_internal ();static NODE *info_node_of_file_buffer_tags ();static long get_node_length ();/* Magic number that RMS used to decide how much a tags table pointer could be off by. I feel that it should be much smaller, like on the order of 4. */#define DEFAULT_INFO_FUDGE 1000/* Passed to *_internal functions. INFO_GET_TAGS says to do what is neccessary to fill in the nodes or tags arrays in FILE_BUFFER. */#define INFO_NO_TAGS 0#define INFO_GET_TAGS 1/* **************************************************************** *//* *//* Global Variables *//* *//* **************************************************************** *//* When non-zero, this is a string describing the recent file error. */char *info_recent_file_error = (char *)NULL;/* The list of already loaded nodes. */FILE_BUFFER **info_loaded_files = (FILE_BUFFER **)NULL;/* The number of slots currently allocated to LOADED_FILES. */int info_loaded_files_slots = 0;/* **************************************************************** *//* *//* Public Functions for Node Manipulation *//* *//* **************************************************************** *//* Used to build "dir" menu from "localdir" files found in INFOPATH. */extern void maybe_build_dir_node ();/* Return a pointer to a NODE structure for the Info node (FILENAME)NODENAME. FILENAME can be passed as NULL, in which case the filename of "dir" is used. NODENAME can be passed as NULL, in which case the nodename of "Top" is used. If the node cannot be found, return a NULL pointer. */NODE *info_get_node (filename, nodename) char *filename, *nodename;{ FILE_BUFFER *file_buffer; NODE *node; file_buffer = (FILE_BUFFER *)NULL; info_recent_file_error = (char *)NULL; info_parse_node (nodename, DONT_SKIP_NEWLINES); nodename = (char *)NULL; if (info_parsed_filename) filename = info_parsed_filename; if (info_parsed_nodename) nodename = info_parsed_nodename; /* If FILENAME is not specified, it defaults to "dir". */ if (!filename) filename = "dir"; /* If the file to be looked up is "dir", build the contents from all of the "dir"s and "localdir"s found in INFOPATH. */ if (strcasecmp (filename, "dir") == 0) maybe_build_dir_node (filename); /* Find the correct info file. */ file_buffer = info_find_file (filename); if (!file_buffer) { if (filesys_error_number) info_recent_file_error = filesys_error_string (filename, filesys_error_number); return ((NODE *)NULL); } node = info_get_node_of_file_buffer (nodename, file_buffer); /* If the node looked for was "Top", try again looking for the node under a slightly different name. */ if (!node && (nodename == NULL || strcasecmp (nodename, "Top") == 0)) { node = info_get_node_of_file_buffer ("Top", file_buffer); if (!node) node = info_get_node_of_file_buffer ("top", file_buffer); if (!node) node = info_get_node_of_file_buffer ("TOP", file_buffer); } return (node);}/* Return a pointer to a NODE structure for the Info node NODENAME in FILE_BUFFER. NODENAME can be passed as NULL, in which case the nodename of "Top" is used. If the node cannot be found, return a NULL pointer. */NODE *info_get_node_of_file_buffer (nodename, file_buffer) char *nodename; FILE_BUFFER *file_buffer;{ NODE *node = (NODE *)NULL; /* If we are unable to find the file, we have to give up. There isn't anything else we can do. */ if (!file_buffer) return ((NODE *)NULL); /* If the file buffer was gc'ed, reload the contents now. */ if (!file_buffer->contents) info_reload_file_buffer_contents (file_buffer); /* If NODENAME is not specified, it defaults to "Top". */ if (!nodename) nodename = "Top"; /* If the name of the node that we wish to find is exactly "*", then the node body is the contents of the entire file. Create and return such a node. */ if (strcmp (nodename, "*") == 0) { node = (NODE *)xmalloc (sizeof (NODE)); node->filename = file_buffer->fullpath; node->parent = (char *)NULL; node->nodename = xstrdup ("*"); node->contents = file_buffer->contents; node->nodelen = file_buffer->filesize; node->flags = 0; }#if defined (HANDLE_MAN_PAGES) /* If the file buffer is the magic one associated with manpages, call the manpage node finding function instead. */ else if (file_buffer->flags & N_IsManPage) { node = get_manpage_node (file_buffer, nodename); }#endif /* HANDLE_MAN_PAGES */ /* If this is the "main" info file, it might contain a tags table. Search the tags table for an entry which matches the node that we want. If there is a tags table, get the file which contains this node, but don't bother building a node list for it. */ else if (file_buffer->tags) { node = info_node_of_file_buffer_tags (file_buffer, nodename); } /* Return the results of our node search. */ return (node);}/* Locate the file named by FILENAME, and return the information structure describing this file. The file may appear in our list of loaded files already, or it may not. If it does not already appear, find the file, and add it to the list of loaded files. If the file cannot be found, return a NULL FILE_BUFFER *. */FILE_BUFFER *info_find_file (filename) char *filename;{ return (info_find_file_internal (filename, INFO_GET_TAGS));}/* Load the info file FILENAME, remembering information about it in a file buffer. */FILE_BUFFER *info_load_file (filename) char *filename;{ return (info_load_file_internal (filename, INFO_GET_TAGS));}/* **************************************************************** *//* *//* Private Functions Implementation *//* *//* **************************************************************** *//* The workhorse for info_find_file (). Non-zero 2nd argument says to try to build a tags table (or otherwise glean the nodes) for this file once found. By default, we build the tags table, but when this function is called by info_get_node () when we already have a valid tags table describing the nodes, it is unnecessary. */static FILE_BUFFER *info_find_file_internal (filename, get_tags) char *filename; int get_tags;{ register int i; register FILE_BUFFER *file_buffer; /* First try to find the file in our list of already loaded files. */ if (info_loaded_files) { for (i = 0; (file_buffer = info_loaded_files[i]); i++) if ((strcmp (filename, file_buffer->filename) == 0) || (strcmp (filename, file_buffer->fullpath) == 0) || ((*filename != '/') && strcmp (filename, filename_non_directory (file_buffer->fullpath)) == 0)) { struct stat new_info, *old_info; /* This file is loaded. If the filename that we want is specifically "dir", then simply return the file buffer. */ if (strcasecmp (filename_non_directory (filename), "dir") == 0) return (file_buffer);#if defined (HANDLE_MAN_PAGES) /* Do the same for the magic MANPAGE file. */ if (file_buffer->flags & N_IsManPage) return (file_buffer);#endif /* HANDLE_MAN_PAGES */ /* The file appears to be already loaded, and it is not "dir". Check to see if it has changed since the last time it was loaded. */ if (stat (file_buffer->fullpath, &new_info) == -1) { filesys_error_number = errno; return ((FILE_BUFFER *)NULL); } old_info = &file_buffer->finfo; if ((new_info.st_size != old_info->st_size) || (new_info.st_mtime != old_info->st_mtime)) { /* The file has changed. Forget that we ever had loaded it in the first place. */ forget_info_file (filename); break; } else { /* The info file exists, and has not changed since the last time it was loaded. If the caller requested a nodes list for this file, and there isn't one here, build the nodes for this file_buffer. In any case, return the file_buffer object. */ if (get_tags && !file_buffer->tags) build_tags_and_nodes (file_buffer); return (file_buffer); } } } /* The file wasn't loaded. Try to load it now. */#if defined (HANDLE_MAN_PAGES) /* If the name of the file that we want is our special file buffer for Unix manual pages, then create the file buffer, and return it now. */ if (strcasecmp (filename, MANPAGE_FILE_BUFFER_NAME) == 0) file_buffer = create_manpage_file_buffer (); else#endif /* HANDLE_MAN_PAGES */ file_buffer = info_load_file_internal (filename, get_tags); /* If the file was loaded, remember the name under which it was found. */ if (file_buffer) remember_info_file (file_buffer); return (file_buffer);}/* The workhorse function for info_load_file (). Non-zero second argument says to build a list of tags (or nodes) for this file. This is the default behaviour when info_load_file () is called, but it is not necessary when loading a subfile for which we already have tags. */static FILE_BUFFER *info_load_file_internal (filename, get_tags) char *filename; int get_tags;{ char *fullpath, *contents; long filesize; struct stat finfo; int retcode; FILE_BUFFER *file_buffer = (FILE_BUFFER *)NULL; /* Get the full pathname of this file, as known by the info system. That is to say, search along INFOPATH and expand tildes, etc. */ fullpath = info_find_fullpath (filename); /* Did we actually find the file? */ retcode = stat (fullpath, &finfo); /* If the file referenced by the name returned from info_find_fullpath () doesn't exist, then try again with the last part of the filename appearing in lowercase. */ if (retcode < 0) { char *lowered_name; char *basename; lowered_name = xstrdup (filename); basename = (char *) strrchr (lowered_name, '/'); if (basename) basename++; else basename = lowered_name; while (*basename) { if (isupper (*basename)) *basename = tolower (*basename); basename++; } fullpath = info_find_fullpath (lowered_name); free (lowered_name); retcode = stat (fullpath, &finfo); } /* If the file wasn't found, give up, returning a NULL pointer. */ if (retcode < 0) { filesys_error_number = errno; return ((FILE_BUFFER *)NULL); } /* Otherwise, try to load the file. */ contents = filesys_read_info_file (fullpath, &filesize, &finfo); if (!contents) return ((FILE_BUFFER *)NULL); /* The file was found, and can be read. Allocate FILE_BUFFER and fill in the various members. */ file_buffer = make_file_buffer (); file_buffer->filename = xstrdup (filename); file_buffer->fullpath = xstrdup (fullpath); file_buffer->finfo = finfo; file_buffer->filesize = filesize; file_buffer->contents = contents; if (file_buffer->filesize != file_buffer->finfo.st_size) file_buffer->flags |= N_IsCompressed; /* If requested, build the tags and nodes for this file buffer. */ if (get_tags) build_tags_and_nodes (file_buffer); return (file_buffer);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -