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