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 / info-utils.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  17KB  |  673 lines

  1. /* info-utils.c -- Useful functions for manipulating Info file quirks. */
  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>        /* For "NULL".  Yechhh! */
  25. #include <ctype.h>
  26. #include <sys/types.h>
  27. #include <sys/stat.h>
  28. #if defined (HAVE_STRING_H)
  29. #  include <string.h>
  30. #endif /* HAVE_STRING_H */
  31. #include "info-utils.h"
  32.  
  33. #if defined (HANDLE_MAN_PAGES)
  34. #  include "man.h"
  35. #endif /* HANDLE_MAN_PAGES */
  36.  
  37. /* When non-zero, various display and input functions handle ISO Latin
  38.    character sets correctly. */
  39. int ISO_Latin_p = 0;
  40.  
  41. /* Variable which holds the most recent filename parsed as a result of
  42.    calling info_parse_xxx (). */
  43. char *info_parsed_filename = (char *)NULL;
  44.  
  45. /* Variable which holds the most recent nodename parsed as a result of
  46.    calling info_parse_xxx (). */
  47. char *info_parsed_nodename = (char *)NULL;
  48.  
  49. /* Functions to remember a filename or nodename for later return. */
  50. static void save_filename (), saven_filename ();
  51. static void save_nodename (), saven_nodename ();
  52.  
  53. /* How to get a reference (either menu or cross). */
  54. static REFERENCE **info_references_internal ();
  55.  
  56. /* Parse the filename and nodename out of STRING.  If STRING doesn't
  57.    contain a filename (i.e., it is NOT (FILENAME)NODENAME) then set
  58.    INFO_PARSED_FILENAME to NULL.  If second argument NEWLINES_OKAY is
  59.    non-zero, it says to allow the nodename specification to cross a
  60.    newline boundary (i.e., only `,', `.', or `TAB' can end the spec). */
  61. void
  62. info_parse_node (string, newlines_okay)
  63.      char *string;
  64.      int newlines_okay;
  65. {
  66.   register int i = 0;
  67.  
  68.   /* Default the answer. */
  69.   save_filename ((char *)NULL);
  70.   save_nodename ((char *)NULL);
  71.  
  72.   /* Special case of nothing passed.  Return nothing. */
  73.   if (!string || !*string)
  74.     return;
  75.  
  76.   string += skip_whitespace (string);
  77.  
  78.   /* Check for (FILENAME)NODENAME. */
  79.   if (*string == '(')
  80.     {
  81.       i = 0;
  82.       /* Advance past the opening paren. */
  83.       string++;
  84.  
  85.       /* Find the closing paren. */
  86.       while (string[i] && string[i] != ')')
  87.     i++;
  88.  
  89.       /* Remember parsed filename. */
  90.       saven_filename (string, i);
  91.  
  92.       /* Point directly at the nodename. */
  93.       string += i;
  94.  
  95.       if (*string)
  96.     string++;
  97.     }
  98.  
  99.   /* Parse out nodename. */
  100.   i = skip_node_characters (string, newlines_okay);
  101.   saven_nodename (string, i);
  102.   canonicalize_whitespace (info_parsed_nodename);
  103.   if (info_parsed_nodename && !*info_parsed_nodename)
  104.     {
  105.       free (info_parsed_nodename);
  106.       info_parsed_nodename = (char *)NULL;
  107.     }
  108. }
  109.  
  110. /* Return the node addressed by LABEL in NODE (usually one of "Prev:",
  111.    "Next:", "Up:", "File:", or "Node:".  After a call to this function,
  112.    the global INFO_PARSED_NODENAME and INFO_PARSED_FILENAME contain
  113.    the information. */
  114. void
  115. info_parse_label (label, node)
  116.      char *label;
  117.      NODE *node;
  118. {
  119.   register int i;
  120.   char *nodeline;
  121.  
  122.   /* Default answer to failure. */
  123.   save_nodename ((char *)NULL);
  124.   save_filename ((char *)NULL);
  125.  
  126.   /* Find the label in the first line of this node. */
  127.   nodeline = node->contents;
  128.   i = string_in_line (label, nodeline);
  129.  
  130.   if (i == -1)
  131.     return;
  132.  
  133.   nodeline += i;
  134.   nodeline += skip_whitespace (nodeline);
  135.   info_parse_node (nodeline, DONT_SKIP_NEWLINES);
  136. }
  137.  
  138. /* **************************************************************** */
  139. /*                                    */
  140. /*            Finding and Building Menus                */
  141. /*                                    */
  142. /* **************************************************************** */
  143.  
  144. /* Return a NULL terminated array of REFERENCE * which represents the menu
  145.    found in NODE.  If there is no menu in NODE, just return a NULL pointer. */
  146. REFERENCE **
  147. info_menu_of_node (node)
  148.      NODE *node;
  149. {
  150.   long position;
  151.   SEARCH_BINDING search;
  152.   REFERENCE **menu = (REFERENCE **)NULL;
  153.  
  154.   search.buffer = node->contents;
  155.   search.start = 0;
  156.   search.end = node->nodelen;
  157.   search.flags = S_FoldCase;
  158.  
  159.   /* Find the start of the menu. */
  160.   position = search_forward (INFO_MENU_LABEL, &search);
  161.  
  162.   if (position == -1)
  163.     return ((REFERENCE **) NULL);
  164.  
  165.   /* We have the start of the menu now.  Glean menu items from the rest
  166.      of the node. */
  167.   search.start = position + strlen (INFO_MENU_LABEL);
  168.   search.start += skip_line (search.buffer + search.start);
  169.   search.start--;
  170.   menu = info_menu_items (&search);
  171.   return (menu);
  172. }
  173.  
  174. /* Return a NULL terminated array of REFERENCE * which represents the cross
  175.    refrences found in NODE.  If there are no cross references in NODE, just
  176.    return a NULL pointer. */
  177. REFERENCE **
  178. info_xrefs_of_node (node)
  179.      NODE *node;
  180. {
  181.   SEARCH_BINDING search;
  182.  
  183. #if defined (HANDLE_MAN_PAGES)
  184.   if (node->flags & N_IsManPage)
  185.     return (xrefs_of_manpage (node));
  186. #endif
  187.  
  188.   search.buffer = node->contents;
  189.   search.start = 0;
  190.   search.end = node->nodelen;
  191.   search.flags = S_FoldCase;
  192.  
  193.   return (info_xrefs (&search));
  194. }
  195.  
  196. /* Glean menu entries from BINDING->buffer + BINDING->start until we
  197.    have looked at the entire contents of BINDING.  Return an array
  198.    of REFERENCE * that represents each menu item in this range. */
  199. REFERENCE **
  200. info_menu_items (binding)
  201.      SEARCH_BINDING *binding;
  202. {
  203.   return (info_references_internal (INFO_MENU_ENTRY_LABEL, binding));
  204. }
  205.  
  206. /* Glean cross references from BINDING->buffer + BINDING->start until
  207.    BINDING->end.  Return an array of REFERENCE * that represents each
  208.    cross reference in this range. */
  209. REFERENCE **
  210. info_xrefs (binding)
  211.      SEARCH_BINDING *binding;
  212. {
  213.   return (info_references_internal (INFO_XREF_LABEL, binding));
  214. }
  215.  
  216. /* Glean cross references or menu items from BINDING.  Return an array
  217.    of REFERENCE * that represents the items found. */
  218. static REFERENCE **
  219. info_references_internal (label, binding)
  220.      char *label;
  221.      SEARCH_BINDING *binding;
  222. {
  223.   SEARCH_BINDING search;
  224.   REFERENCE **refs = (REFERENCE **)NULL;
  225.   int refs_index = 0, refs_slots = 0;
  226.   int searching_for_menu_items = 0;
  227.   long position;
  228.  
  229.   search.buffer = binding->buffer;
  230.   search.start = binding->start;
  231.   search.end = binding->end;
  232.   search.flags = S_FoldCase | S_SkipDest;
  233.  
  234.   searching_for_menu_items = (strcasecmp (label, INFO_MENU_ENTRY_LABEL) == 0);
  235.  
  236.   while ((position = search_forward (label, &search)) != -1)
  237.     {
  238.       int offset, start;
  239.       char *refdef;
  240.       REFERENCE *entry;
  241.  
  242.       search.start = position;
  243.       search.start += skip_whitespace (search.buffer + search.start);
  244.       start = search.start - binding->start;
  245.       refdef = search.buffer + search.start;
  246.       offset = string_in_line (":", refdef);
  247.  
  248.       /* When searching for menu items, if no colon, there is no
  249.      menu item on this line. */
  250.       if (offset == -1)
  251.     {
  252.       if (searching_for_menu_items)
  253.         continue;
  254.       else
  255.         {
  256.           int temp;
  257.  
  258.           temp = skip_line (refdef);
  259.           offset = string_in_line (":", refdef + temp);
  260.           if (offset == -1)
  261.         continue;    /* Give up? */
  262.           else
  263.         offset += temp;
  264.         }
  265.     }
  266.  
  267.       entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
  268.       entry->filename = (char *)NULL;
  269.       entry->nodename = (char *)NULL;
  270.       entry->label = (char *)xmalloc (offset);
  271.       strncpy (entry->label, refdef, offset - 1);
  272.       entry->label[offset - 1] = '\0';
  273.       canonicalize_whitespace (entry->label);
  274.  
  275.       refdef += offset;
  276.       entry->start = start;
  277.       entry->end = refdef - binding->buffer;
  278.  
  279.       /* If this reference entry continues with another ':' then the
  280.      nodename is the same as the label. */
  281.       if (*refdef == ':')
  282.     {
  283.       entry->nodename = strdup (entry->label);
  284.     }
  285.       else
  286.     {
  287.       /* This entry continues with a specific nodename.  Parse the
  288.          nodename from the specification. */
  289.  
  290.       refdef += skip_whitespace_and_newlines (refdef);
  291.  
  292.       if (searching_for_menu_items)
  293.         info_parse_node (refdef, DONT_SKIP_NEWLINES);
  294.       else
  295.         info_parse_node (refdef, SKIP_NEWLINES);
  296.  
  297.       if (info_parsed_filename)
  298.         entry->filename = strdup (info_parsed_filename);
  299.  
  300.       if (info_parsed_nodename)
  301.         entry->nodename = strdup (info_parsed_nodename);
  302.     }
  303.  
  304.       add_pointer_to_array
  305.     (entry, refs_index, refs, refs_slots, 50, REFERENCE *);
  306.     }
  307.   return (refs);
  308. }
  309.  
  310. /* Get the entry associated with LABEL in MENU.  Return a pointer to the
  311.    REFERENCE if found, or NULL. */
  312. REFERENCE *
  313. info_get_labeled_reference (label, references)
  314.      char *label;
  315.      REFERENCE **references;
  316. {
  317.   register int i;
  318.   REFERENCE *entry;
  319.  
  320.   for (i = 0; references && (entry = references[i]); i++)
  321.     {
  322.       if (strcmp (label, entry->label) == 0)
  323.     return (entry);
  324.     }
  325.   return ((REFERENCE *)NULL);
  326. }
  327.  
  328. /* A utility function for concatenating REFERENCE **.  Returns a new
  329.    REFERENCE ** which is the concatenation of REF1 and REF2.  The REF1
  330.    and REF2 arrays are freed, but their contents are not. */
  331. REFERENCE **
  332. info_concatenate_references (ref1, ref2)
  333.      REFERENCE **ref1, **ref2;
  334. {
  335.   register int i, j;
  336.   REFERENCE **result;
  337.   int size;
  338.  
  339.   /* With one argument passed as NULL, simply return the other arg. */
  340.   if (!ref1)
  341.     return (ref2);
  342.   else if (!ref2)
  343.     return (ref1);
  344.  
  345.   /* Get the total size of the slots that we will need. */
  346.   for (i = 0; ref1[i]; i++);
  347.   size = i;
  348.   for (i = 0; ref2[i]; i++);
  349.   size += i;
  350.  
  351.   result = (REFERENCE **)xmalloc ((1 + size) * sizeof (REFERENCE *));
  352.  
  353.   /* Copy the contents over. */
  354.   for (i = 0; ref1[i]; i++)
  355.     result[i] = ref1[i];
  356.  
  357.   j = i;
  358.   for (i = 0; ref2[i]; i++)
  359.     result[j++] = ref2[i];
  360.  
  361.   result[j] = (REFERENCE *)NULL;
  362.   free (ref1);
  363.   free (ref2);
  364.   return (result);
  365. }
  366.  
  367. /* Free the data associated with REFERENCES. */
  368. void
  369. info_free_references (references)
  370.      REFERENCE **references;
  371. {
  372.   register int i;
  373.   REFERENCE *entry;
  374.  
  375.   if (references)
  376.     {
  377.       for (i = 0; references && (entry = references[i]); i++)
  378.     {
  379.       maybe_free (entry->label);
  380.       maybe_free (entry->filename);
  381.       maybe_free (entry->nodename);
  382.  
  383.       free (entry);
  384.     }
  385.  
  386.       free (references);
  387.     }
  388. }
  389.  
  390. /* Search for sequences of whitespace or newlines in STRING, replacing
  391.    all such sequences with just a single space.  Remove whitespace from
  392.    start and end of string. */
  393. void
  394. canonicalize_whitespace (string)
  395.      char *string;
  396. {
  397.   register int i, j;
  398.   int len, whitespace_found, whitespace_loc;
  399.   char *temp;
  400.  
  401.   if (!string)
  402.     return;
  403.  
  404.   len = strlen (string);
  405.   temp = (char *)xmalloc (1 + len);
  406.  
  407.   /* Search for sequences of whitespace or newlines.  Replace all such
  408.      sequences in the string with just a single space. */
  409.  
  410.   whitespace_found = 0;
  411.   for (i = 0, j = 0; string[i]; i++)
  412.     {
  413.       if (whitespace_or_newline (string[i]))
  414.     {
  415.       whitespace_found++;
  416.       whitespace_loc = i;
  417.       continue;
  418.     }
  419.       else
  420.     {
  421.       if (whitespace_found && whitespace_loc)
  422.         {
  423.           whitespace_found = 0;
  424.  
  425.           /* Suppress whitespace at start of string. */
  426.           if (j)
  427.         temp[j++] = ' ';
  428.         }
  429.  
  430.       temp[j++] = string[i];
  431.     }
  432.     }
  433.  
  434.   /* Kill trailing whitespace. */
  435.   if (j && whitespace (temp[j - 1]))
  436.     j--;
  437.  
  438.   temp[j] = '\0';
  439.   strcpy (string, temp);
  440.   free (temp);
  441. }
  442.  
  443. /* String representation of a char returned by printed_representation (). */
  444. static char the_rep[10];
  445.  
  446. /* Return a pointer to a string which is the printed representation
  447.    of CHARACTER if it were printed at HPOS. */
  448. char *
  449. printed_representation (character, hpos)
  450.      unsigned char character;
  451.      int hpos;
  452. {
  453.   register int i = 0;
  454.   int printable_limit;
  455.  
  456.   if (ISO_Latin_p)
  457.     printable_limit = 160;
  458.   else
  459.     printable_limit = 127;
  460.  
  461.   if (character == '\177')
  462.     {
  463.       the_rep[i++] = '^';
  464.       the_rep[i++] = '?';
  465.     }
  466.   else if (iscntrl (character))
  467.     {
  468.       switch (character)
  469.     {
  470.     case '\r':
  471.     case '\n':
  472.       the_rep[i++] = character;
  473.       break;
  474.  
  475.     case '\t':
  476.       {
  477.         int tw;
  478.  
  479.         tw = ((hpos + 8) & 0xf8) - hpos;
  480.         while (i < tw)
  481.           the_rep[i++] = ' ';
  482.       }
  483.       break;
  484.  
  485.     default:
  486.       the_rep[i++] = '^';
  487.       the_rep[i++] = (character | 0x40);
  488.     }
  489.     }
  490.   else if (character > printable_limit)
  491.     {
  492.       sprintf (the_rep + i, "\\%0o", character);
  493.       i = strlen (the_rep);
  494.     }
  495.   else
  496.     the_rep[i++] = character;
  497.  
  498.   the_rep[i] = '\0';
  499.  
  500.   return (the_rep);
  501. }
  502.  
  503.  
  504. /* **************************************************************** */
  505. /*                                    */
  506. /*            Functions Static To This File            */
  507. /*                                    */
  508. /* **************************************************************** */
  509.  
  510. /* Amount of space allocated to INFO_PARSED_FILENAME via xmalloc (). */
  511. static int parsed_filename_size = 0;
  512.  
  513. /* Amount of space allocated to INFO_PARSED_NODENAME via xmalloc (). */
  514. static int parsed_nodename_size = 0;
  515.  
  516. static void save_string (), saven_string ();
  517.  
  518. /* Remember FILENAME in PARSED_FILENAME.  An empty FILENAME is translated
  519.    to a NULL pointer in PARSED_FILENAME. */
  520. static void
  521. save_filename (filename)
  522.      char *filename;
  523. {
  524.   save_string (filename, &info_parsed_filename, &parsed_filename_size);
  525. }
  526.  
  527. /* Just like save_filename (), but you pass the length of the string. */
  528. static void
  529. saven_filename (filename, len)
  530.      char *filename;
  531.      int len;
  532. {
  533.   saven_string (filename, len,
  534.         &info_parsed_filename, &parsed_filename_size);
  535. }
  536.  
  537. /* Remember NODENAME in PARSED_NODENAME.  An empty NODENAME is translated
  538.    to a NULL pointer in PARSED_NODENAME. */
  539. static void
  540. save_nodename (nodename)
  541.      char *nodename;
  542. {
  543.   save_string (nodename, &info_parsed_nodename, &parsed_nodename_size);
  544. }
  545.  
  546. /* Just like save_nodename (), but you pass the length of the string. */
  547. static void
  548. saven_nodename (nodename, len)
  549.      char *nodename;
  550.      int len;
  551. {
  552.   saven_string (nodename, len,
  553.         &info_parsed_nodename, &parsed_nodename_size);
  554. }
  555.  
  556. /* Remember STRING in STRING_P.  STRING_P should currently have STRING_SIZE_P
  557.    bytes allocated to it.  An empty STRING is translated to a NULL pointer
  558.    in STRING_P. */
  559. static void
  560. save_string (string, string_p, string_size_p)
  561.      char *string;
  562.      char **string_p;
  563.      int *string_size_p;
  564. {
  565.   if (!string || !*string)
  566.     {
  567.       if (*string_p)
  568.     free (*string_p);
  569.  
  570.       *string_p = (char *)NULL;
  571.       *string_size_p = 0;
  572.     }
  573.   else
  574.     {
  575.       if (strlen (string) >= *string_size_p)
  576.     *string_p = (char *)xrealloc
  577.       (*string_p, (*string_size_p = 1 + strlen (string)));
  578.  
  579.       strcpy (*string_p, string);
  580.     }
  581. }
  582.  
  583. /* Just like save_string (), but you also pass the length of STRING. */
  584. static void
  585. saven_string (string, len, string_p, string_size_p)
  586.      char *string;
  587.      int len;
  588.      char **string_p;
  589.      int *string_size_p;
  590. {
  591.   if (!string)
  592.     {
  593.       if (*string_p)
  594.     free (*string_p);
  595.  
  596.       *string_p = (char *)NULL;
  597.       *string_size_p = 0;
  598.     }
  599.   else
  600.     {
  601.       if (len >= *string_size_p)
  602.     *string_p = (char *)xrealloc (*string_p, (*string_size_p = 1 + len));
  603.  
  604.       strncpy (*string_p, string, len);
  605.       (*string_p)[len] = '\0';
  606.     }
  607. }
  608.  
  609. /* Return a pointer to the part of PATHNAME that simply defines the file. */
  610. char *
  611. filename_non_directory (pathname)
  612.      char *pathname;
  613. {
  614.   char *filename;
  615.  
  616.   filename = (char *) strrchr (pathname, '/');
  617.  
  618.   if (filename)
  619.     filename++;
  620.   else
  621.     filename = pathname;
  622.  
  623.   return (filename);
  624. }
  625.  
  626. /* Return non-zero if NODE is one especially created by Info. */
  627. int
  628. internal_info_node_p (node)
  629.      NODE *node;
  630. {
  631. #if defined (NEVER)
  632.   if (node &&
  633.       (node->filename && !*node->filename) &&
  634.       !node->parent && node->nodename)
  635.     return (1);
  636.   else
  637.     return (0);
  638. #else
  639.   return ((node != (NODE *)NULL) && ((node->flags & N_IsInternal) != 0));
  640. #endif /* !NEVER */
  641. }
  642.  
  643. /* Make NODE appear to be one especially created by Info. */
  644. void
  645. name_internal_node (node, name)
  646.      NODE *node;
  647.      char *name;
  648. {
  649.   if (!node)
  650.     return;
  651.  
  652.   node->filename = "";
  653.   node->parent = (char *)NULL;
  654.   node->nodename = name;
  655.   node->flags |= N_IsInternal;
  656. }
  657.  
  658. /* Return the window displaying NAME, the name of an internally created
  659.    Info window. */
  660. WINDOW *
  661. get_internal_info_window (name)
  662.      char *name;
  663. {
  664.   WINDOW *win;
  665.  
  666.   for (win = windows; win; win = win->next)
  667.     if (internal_info_node_p (win->node) &&
  668.     (strcmp (win->node->nodename, name) == 0))
  669.       break;
  670.  
  671.   return (win);
  672. }
  673.