aboutsummaryrefslogtreecommitdiff
path: root/texinfo/info/info-utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'texinfo/info/info-utils.c')
-rw-r--r--texinfo/info/info-utils.c672
1 files changed, 672 insertions, 0 deletions
diff --git a/texinfo/info/info-utils.c b/texinfo/info/info-utils.c
new file mode 100644
index 00000000000..6af3dd0e2ca
--- /dev/null
+++ b/texinfo/info/info-utils.c
@@ -0,0 +1,672 @@
+/* info-utils.c -- Useful functions for manipulating Info file quirks. */
+
+/* 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 <stdio.h> /* For "NULL". Yechhh! */
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#if defined (HAVE_STRING_H)
+# include <string.h>
+#endif /* HAVE_STRING_H */
+#include "info-utils.h"
+
+#if defined (HANDLE_MAN_PAGES)
+# include "man.h"
+#endif /* HANDLE_MAN_PAGES */
+
+/* When non-zero, various display and input functions handle ISO Latin
+ character sets correctly. */
+int ISO_Latin_p = 0;
+
+/* Variable which holds the most recent filename parsed as a result of
+ calling info_parse_xxx (). */
+char *info_parsed_filename = (char *)NULL;
+
+/* Variable which holds the most recent nodename parsed as a result of
+ calling info_parse_xxx (). */
+char *info_parsed_nodename = (char *)NULL;
+
+/* Functions to remember a filename or nodename for later return. */
+static void save_filename (), saven_filename ();
+static void save_nodename (), saven_nodename ();
+
+/* How to get a reference (either menu or cross). */
+static REFERENCE **info_references_internal ();
+
+/* Parse the filename and nodename out of STRING. If STRING doesn't
+ contain a filename (i.e., it is NOT (FILENAME)NODENAME) then set
+ INFO_PARSED_FILENAME to NULL. If second argument NEWLINES_OKAY is
+ non-zero, it says to allow the nodename specification to cross a
+ newline boundary (i.e., only `,', `.', or `TAB' can end the spec). */
+void
+info_parse_node (string, newlines_okay)
+ char *string;
+ int newlines_okay;
+{
+ register int i = 0;
+
+ /* Default the answer. */
+ save_filename ((char *)NULL);
+ save_nodename ((char *)NULL);
+
+ /* Special case of nothing passed. Return nothing. */
+ if (!string || !*string)
+ return;
+
+ string += skip_whitespace (string);
+
+ /* Check for (FILENAME)NODENAME. */
+ if (*string == '(')
+ {
+ i = 0;
+ /* Advance past the opening paren. */
+ string++;
+
+ /* Find the closing paren. */
+ while (string[i] && string[i] != ')')
+ i++;
+
+ /* Remember parsed filename. */
+ saven_filename (string, i);
+
+ /* Point directly at the nodename. */
+ string += i;
+
+ if (*string)
+ string++;
+ }
+
+ /* Parse out nodename. */
+ i = skip_node_characters (string, newlines_okay);
+ saven_nodename (string, i);
+ canonicalize_whitespace (info_parsed_nodename);
+ if (info_parsed_nodename && !*info_parsed_nodename)
+ {
+ free (info_parsed_nodename);
+ info_parsed_nodename = (char *)NULL;
+ }
+}
+
+/* Return the node addressed by LABEL in NODE (usually one of "Prev:",
+ "Next:", "Up:", "File:", or "Node:". After a call to this function,
+ the global INFO_PARSED_NODENAME and INFO_PARSED_FILENAME contain
+ the information. */
+void
+info_parse_label (label, node)
+ char *label;
+ NODE *node;
+{
+ register int i;
+ char *nodeline;
+
+ /* Default answer to failure. */
+ save_nodename ((char *)NULL);
+ save_filename ((char *)NULL);
+
+ /* Find the label in the first line of this node. */
+ nodeline = node->contents;
+ i = string_in_line (label, nodeline);
+
+ if (i == -1)
+ return;
+
+ nodeline += i;
+ nodeline += skip_whitespace (nodeline);
+ info_parse_node (nodeline, DONT_SKIP_NEWLINES);
+}
+
+/* **************************************************************** */
+/* */
+/* Finding and Building Menus */
+/* */
+/* **************************************************************** */
+
+/* Return a NULL terminated array of REFERENCE * which represents the menu
+ found in NODE. If there is no menu in NODE, just return a NULL pointer. */
+REFERENCE **
+info_menu_of_node (node)
+ NODE *node;
+{
+ long position;
+ SEARCH_BINDING search;
+ REFERENCE **menu = (REFERENCE **)NULL;
+
+ search.buffer = node->contents;
+ search.start = 0;
+ search.end = node->nodelen;
+ search.flags = S_FoldCase;
+
+ /* Find the start of the menu. */
+ position = search_forward (INFO_MENU_LABEL, &search);
+
+ if (position == -1)
+ return ((REFERENCE **) NULL);
+
+ /* We have the start of the menu now. Glean menu items from the rest
+ of the node. */
+ search.start = position + strlen (INFO_MENU_LABEL);
+ search.start += skip_line (search.buffer + search.start);
+ search.start--;
+ menu = info_menu_items (&search);
+ return (menu);
+}
+
+/* Return a NULL terminated array of REFERENCE * which represents the cross
+ refrences found in NODE. If there are no cross references in NODE, just
+ return a NULL pointer. */
+REFERENCE **
+info_xrefs_of_node (node)
+ NODE *node;
+{
+ SEARCH_BINDING search;
+
+#if defined (HANDLE_MAN_PAGES)
+ if (node->flags & N_IsManPage)
+ return (xrefs_of_manpage (node));
+#endif
+
+ search.buffer = node->contents;
+ search.start = 0;
+ search.end = node->nodelen;
+ search.flags = S_FoldCase;
+
+ return (info_xrefs (&search));
+}
+
+/* Glean menu entries from BINDING->buffer + BINDING->start until we
+ have looked at the entire contents of BINDING. Return an array
+ of REFERENCE * that represents each menu item in this range. */
+REFERENCE **
+info_menu_items (binding)
+ SEARCH_BINDING *binding;
+{
+ return (info_references_internal (INFO_MENU_ENTRY_LABEL, binding));
+}
+
+/* Glean cross references from BINDING->buffer + BINDING->start until
+ BINDING->end. Return an array of REFERENCE * that represents each
+ cross reference in this range. */
+REFERENCE **
+info_xrefs (binding)
+ SEARCH_BINDING *binding;
+{
+ return (info_references_internal (INFO_XREF_LABEL, binding));
+}
+
+/* Glean cross references or menu items from BINDING. Return an array
+ of REFERENCE * that represents the items found. */
+static REFERENCE **
+info_references_internal (label, binding)
+ char *label;
+ SEARCH_BINDING *binding;
+{
+ SEARCH_BINDING search;
+ REFERENCE **refs = (REFERENCE **)NULL;
+ int refs_index = 0, refs_slots = 0;
+ int searching_for_menu_items = 0;
+ long position;
+
+ search.buffer = binding->buffer;
+ search.start = binding->start;
+ search.end = binding->end;
+ search.flags = S_FoldCase | S_SkipDest;
+
+ searching_for_menu_items = (strcasecmp (label, INFO_MENU_ENTRY_LABEL) == 0);
+
+ while ((position = search_forward (label, &search)) != -1)
+ {
+ int offset, start;
+ char *refdef;
+ REFERENCE *entry;
+
+ search.start = position;
+ search.start += skip_whitespace (search.buffer + search.start);
+ start = search.start - binding->start;
+ refdef = search.buffer + search.start;
+ offset = string_in_line (":", refdef);
+
+ /* When searching for menu items, if no colon, there is no
+ menu item on this line. */
+ if (offset == -1)
+ {
+ if (searching_for_menu_items)
+ continue;
+ else
+ {
+ int temp;
+
+ temp = skip_line (refdef);
+ offset = string_in_line (":", refdef + temp);
+ if (offset == -1)
+ continue; /* Give up? */
+ else
+ offset += temp;
+ }
+ }
+
+ entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
+ entry->filename = (char *)NULL;
+ entry->nodename = (char *)NULL;
+ entry->label = (char *)xmalloc (offset);
+ strncpy (entry->label, refdef, offset - 1);
+ entry->label[offset - 1] = '\0';
+ canonicalize_whitespace (entry->label);
+
+ refdef += offset;
+ entry->start = start;
+ entry->end = refdef - binding->buffer;
+
+ /* If this reference entry continues with another ':' then the
+ nodename is the same as the label. */
+ if (*refdef == ':')
+ {
+ entry->nodename = strdup (entry->label);
+ }
+ else
+ {
+ /* This entry continues with a specific nodename. Parse the
+ nodename from the specification. */
+
+ refdef += skip_whitespace_and_newlines (refdef);
+
+ if (searching_for_menu_items)
+ info_parse_node (refdef, DONT_SKIP_NEWLINES);
+ else
+ info_parse_node (refdef, SKIP_NEWLINES);
+
+ if (info_parsed_filename)
+ entry->filename = strdup (info_parsed_filename);
+
+ if (info_parsed_nodename)
+ entry->nodename = strdup (info_parsed_nodename);
+ }
+
+ add_pointer_to_array
+ (entry, refs_index, refs, refs_slots, 50, REFERENCE *);
+ }
+ return (refs);
+}
+
+/* Get the entry associated with LABEL in MENU. Return a pointer to the
+ REFERENCE if found, or NULL. */
+REFERENCE *
+info_get_labeled_reference (label, references)
+ char *label;
+ REFERENCE **references;
+{
+ register int i;
+ REFERENCE *entry;
+
+ for (i = 0; references && (entry = references[i]); i++)
+ {
+ if (strcmp (label, entry->label) == 0)
+ return (entry);
+ }
+ return ((REFERENCE *)NULL);
+}
+
+/* A utility function for concatenating REFERENCE **. Returns a new
+ REFERENCE ** which is the concatenation of REF1 and REF2. The REF1
+ and REF2 arrays are freed, but their contents are not. */
+REFERENCE **
+info_concatenate_references (ref1, ref2)
+ REFERENCE **ref1, **ref2;
+{
+ register int i, j;
+ REFERENCE **result;
+ int size;
+
+ /* With one argument passed as NULL, simply return the other arg. */
+ if (!ref1)
+ return (ref2);
+ else if (!ref2)
+ return (ref1);
+
+ /* Get the total size of the slots that we will need. */
+ for (i = 0; ref1[i]; i++);
+ size = i;
+ for (i = 0; ref2[i]; i++);
+ size += i;
+
+ result = (REFERENCE **)xmalloc ((1 + size) * sizeof (REFERENCE *));
+
+ /* Copy the contents over. */
+ for (i = 0; ref1[i]; i++)
+ result[i] = ref1[i];
+
+ j = i;
+ for (i = 0; ref2[i]; i++)
+ result[j++] = ref2[i];
+
+ result[j] = (REFERENCE *)NULL;
+ free (ref1);
+ free (ref2);
+ return (result);
+}
+
+/* Free the data associated with REFERENCES. */
+void
+info_free_references (references)
+ REFERENCE **references;
+{
+ register int i;
+ REFERENCE *entry;
+
+ if (references)
+ {
+ for (i = 0; references && (entry = references[i]); i++)
+ {
+ maybe_free (entry->label);
+ maybe_free (entry->filename);
+ maybe_free (entry->nodename);
+
+ free (entry);
+ }
+
+ free (references);
+ }
+}
+
+/* Search for sequences of whitespace or newlines in STRING, replacing
+ all such sequences with just a single space. Remove whitespace from
+ start and end of string. */
+void
+canonicalize_whitespace (string)
+ char *string;
+{
+ register int i, j;
+ int len, whitespace_found, whitespace_loc;
+ char *temp;
+
+ if (!string)
+ return;
+
+ len = strlen (string);
+ temp = (char *)xmalloc (1 + len);
+
+ /* Search for sequences of whitespace or newlines. Replace all such
+ sequences in the string with just a single space. */
+
+ whitespace_found = 0;
+ for (i = 0, j = 0; string[i]; i++)
+ {
+ if (whitespace_or_newline (string[i]))
+ {
+ whitespace_found++;
+ whitespace_loc = i;
+ continue;
+ }
+ else
+ {
+ if (whitespace_found && whitespace_loc)
+ {
+ whitespace_found = 0;
+
+ /* Suppress whitespace at start of string. */
+ if (j)
+ temp[j++] = ' ';
+ }
+
+ temp[j++] = string[i];
+ }
+ }
+
+ /* Kill trailing whitespace. */
+ if (j && whitespace (temp[j - 1]))
+ j--;
+
+ temp[j] = '\0';
+ strcpy (string, temp);
+ free (temp);
+}
+
+/* String representation of a char returned by printed_representation (). */
+static char the_rep[10];
+
+/* Return a pointer to a string which is the printed representation
+ of CHARACTER if it were printed at HPOS. */
+char *
+printed_representation (character, hpos)
+ unsigned char character;
+ int hpos;
+{
+ register int i = 0;
+ int printable_limit;
+
+ if (ISO_Latin_p)
+ printable_limit = 160;
+ else
+ printable_limit = 127;
+
+ if (character == '\177')
+ {
+ the_rep[i++] = '^';
+ the_rep[i++] = '?';
+ }
+ else if (iscntrl (character))
+ {
+ switch (character)
+ {
+ case '\r':
+ case '\n':
+ the_rep[i++] = character;
+ break;
+
+ case '\t':
+ {
+ int tw;
+
+ tw = ((hpos + 8) & 0xf8) - hpos;
+ while (i < tw)
+ the_rep[i++] = ' ';
+ }
+ break;
+
+ default:
+ the_rep[i++] = '^';
+ the_rep[i++] = (character | 0x40);
+ }
+ }
+ else if (character > printable_limit)
+ {
+ sprintf (the_rep + i, "\\%0o", character);
+ i = strlen (the_rep);
+ }
+ else
+ the_rep[i++] = character;
+
+ the_rep[i] = '\0';
+
+ return (the_rep);
+}
+
+
+/* **************************************************************** */
+/* */
+/* Functions Static To This File */
+/* */
+/* **************************************************************** */
+
+/* Amount of space allocated to INFO_PARSED_FILENAME via xmalloc (). */
+static int parsed_filename_size = 0;
+
+/* Amount of space allocated to INFO_PARSED_NODENAME via xmalloc (). */
+static int parsed_nodename_size = 0;
+
+static void save_string (), saven_string ();
+
+/* Remember FILENAME in PARSED_FILENAME. An empty FILENAME is translated
+ to a NULL pointer in PARSED_FILENAME. */
+static void
+save_filename (filename)
+ char *filename;
+{
+ save_string (filename, &info_parsed_filename, &parsed_filename_size);
+}
+
+/* Just like save_filename (), but you pass the length of the string. */
+static void
+saven_filename (filename, len)
+ char *filename;
+ int len;
+{
+ saven_string (filename, len,
+ &info_parsed_filename, &parsed_filename_size);
+}
+
+/* Remember NODENAME in PARSED_NODENAME. An empty NODENAME is translated
+ to a NULL pointer in PARSED_NODENAME. */
+static void
+save_nodename (nodename)
+ char *nodename;
+{
+ save_string (nodename, &info_parsed_nodename, &parsed_nodename_size);
+}
+
+/* Just like save_nodename (), but you pass the length of the string. */
+static void
+saven_nodename (nodename, len)
+ char *nodename;
+ int len;
+{
+ saven_string (nodename, len,
+ &info_parsed_nodename, &parsed_nodename_size);
+}
+
+/* Remember STRING in STRING_P. STRING_P should currently have STRING_SIZE_P
+ bytes allocated to it. An empty STRING is translated to a NULL pointer
+ in STRING_P. */
+static void
+save_string (string, string_p, string_size_p)
+ char *string;
+ char **string_p;
+ int *string_size_p;
+{
+ if (!string || !*string)
+ {
+ if (*string_p)
+ free (*string_p);
+
+ *string_p = (char *)NULL;
+ *string_size_p = 0;
+ }
+ else
+ {
+ if (strlen (string) >= *string_size_p)
+ *string_p = (char *)xrealloc
+ (*string_p, (*string_size_p = 1 + strlen (string)));
+
+ strcpy (*string_p, string);
+ }
+}
+
+/* Just like save_string (), but you also pass the length of STRING. */
+static void
+saven_string (string, len, string_p, string_size_p)
+ char *string;
+ int len;
+ char **string_p;
+ int *string_size_p;
+{
+ if (!string)
+ {
+ if (*string_p)
+ free (*string_p);
+
+ *string_p = (char *)NULL;
+ *string_size_p = 0;
+ }
+ else
+ {
+ if (len >= *string_size_p)
+ *string_p = (char *)xrealloc (*string_p, (*string_size_p = 1 + len));
+
+ strncpy (*string_p, string, len);
+ (*string_p)[len] = '\0';
+ }
+}
+
+/* Return a pointer to the part of PATHNAME that simply defines the file. */
+char *
+filename_non_directory (pathname)
+ char *pathname;
+{
+ char *filename;
+
+ filename = (char *) strrchr (pathname, '/');
+
+ if (filename)
+ filename++;
+ else
+ filename = pathname;
+
+ return (filename);
+}
+
+/* Return non-zero if NODE is one especially created by Info. */
+int
+internal_info_node_p (node)
+ NODE *node;
+{
+#if defined (NEVER)
+ if (node &&
+ (node->filename && !*node->filename) &&
+ !node->parent && node->nodename)
+ return (1);
+ else
+ return (0);
+#else
+ return ((node != (NODE *)NULL) && ((node->flags & N_IsInternal) != 0));
+#endif /* !NEVER */
+}
+
+/* Make NODE appear to be one especially created by Info. */
+void
+name_internal_node (node, name)
+ NODE *node;
+ char *name;
+{
+ if (!node)
+ return;
+
+ node->filename = "";
+ node->parent = (char *)NULL;
+ node->nodename = name;
+ node->flags |= N_IsInternal;
+}
+
+/* Return the window displaying NAME, the name of an internally created
+ Info window. */
+WINDOW *
+get_internal_info_window (name)
+ char *name;
+{
+ WINDOW *win;
+
+ for (win = windows; win; win = win->next)
+ if (internal_info_node_p (win->node) &&
+ (strcmp (win->node->nodename, name) == 0))
+ break;
+
+ return (win);
+}