home *** CD-ROM | disk | FTP | other *** search
/ RISC DISC 2 / RISC_DISC_2.iso / pd_share / utilities / cli / gnuinfo / Source / c / info-utils < prev    next >
Encoding:
Text File  |  1994-10-01  |  16.0 KB  |  657 lines

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