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