man.c revision 116525
142660Smarkm/*  man.c: How to read and format man files.
2116525Sru    $Id: man.c,v 1.2 2003/05/13 16:37:54 karl Exp $
321495Sjmacd
4116525Sru   Copyright (C) 1995, 1997, 1998, 1999, 2000, 2002, 2003 Free Software
5114472Sru   Foundation, Inc.
621495Sjmacd
721495Sjmacd   This program is free software; you can redistribute it and/or modify
821495Sjmacd   it under the terms of the GNU General Public License as published by
921495Sjmacd   the Free Software Foundation; either version 2, or (at your option)
1021495Sjmacd   any later version.
1121495Sjmacd
1221495Sjmacd   This program is distributed in the hope that it will be useful,
1321495Sjmacd   but WITHOUT ANY WARRANTY; without even the implied warranty of
1421495Sjmacd   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1521495Sjmacd   GNU General Public License for more details.
1621495Sjmacd
1721495Sjmacd   You should have received a copy of the GNU General Public License
1821495Sjmacd   along with this program; if not, write to the Free Software
1921495Sjmacd   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
2021495Sjmacd
2121495Sjmacd   Written by Brian Fox Thu May  4 09:17:52 1995 (bfox@ai.mit.edu). */
2221495Sjmacd
2321495Sjmacd#include "info.h"
2421495Sjmacd#include <sys/ioctl.h>
2521495Sjmacd#include "signals.h"
2621495Sjmacd#if defined (HAVE_SYS_TIME_H)
2721495Sjmacd#include <sys/time.h>
2821495Sjmacd#endif
2921495Sjmacd#if defined (HAVE_SYS_WAIT_H)
3021495Sjmacd#include <sys/wait.h>
3121495Sjmacd#endif
3242660Smarkm
3321495Sjmacd#include "tilde.h"
3421495Sjmacd#include "man.h"
3521495Sjmacd
3621495Sjmacd#if !defined (_POSIX_VERSION)
3721495Sjmacd#define pid_t int
3821495Sjmacd#endif
3921495Sjmacd
4021495Sjmacd#if defined (FD_SET)
4121495Sjmacd#  if defined (hpux)
4221495Sjmacd#    define fd_set_cast(x) (int *)(x)
4321495Sjmacd#  else
4421495Sjmacd#    define fd_set_cast(x) (fd_set *)(x)
4521495Sjmacd#  endif /* !hpux */
4621495Sjmacd#endif /* FD_SET */
4721495Sjmacd
4856160Sru#if STRIP_DOT_EXE
4956160Srustatic char const * const exec_extensions[] = {
5056160Sru  ".exe", ".com", ".bat", ".btm", ".sh", ".ksh", ".pl", ".sed", "", NULL
5156160Sru};
5256160Sru#else
5356160Srustatic char const * const exec_extensions[] = { "", NULL };
5456160Sru#endif
5556160Sru
5621495Sjmacdstatic char *read_from_fd ();
5721495Sjmacdstatic void clean_manpage ();
5821495Sjmacdstatic NODE *manpage_node_of_file_buffer ();
5921495Sjmacdstatic char *get_manpage_contents ();
6021495Sjmacd
6121495SjmacdNODE *
6221495Sjmacdmake_manpage_node (pagename)
6321495Sjmacd     char *pagename;
6421495Sjmacd{
6521495Sjmacd  return (info_get_node (MANPAGE_FILE_BUFFER_NAME, pagename));
6621495Sjmacd}
6721495Sjmacd
6821495SjmacdNODE *
6921495Sjmacdget_manpage_node (file_buffer, pagename)
7021495Sjmacd     FILE_BUFFER *file_buffer;
7121495Sjmacd     char *pagename;
7221495Sjmacd{
7321495Sjmacd  NODE *node;
7421495Sjmacd
7521495Sjmacd  node = manpage_node_of_file_buffer (file_buffer, pagename);
7621495Sjmacd
7721495Sjmacd  if (!node)
7821495Sjmacd    {
7921495Sjmacd      char *page;
8021495Sjmacd
8121495Sjmacd      page = get_manpage_contents (pagename);
8221495Sjmacd
8321495Sjmacd      if (page)
8442660Smarkm        {
8542660Smarkm          char header[1024];
8642660Smarkm          long oldsize, newsize;
8742660Smarkm          int hlen, plen;
8856160Sru	  char *old_contents = file_buffer->contents;
8921495Sjmacd
9042660Smarkm          sprintf (header, "\n\n%c\n%s %s,  %s %s,  %s (dir)\n\n",
9142660Smarkm                   INFO_COOKIE,
9242660Smarkm                   INFO_FILE_LABEL, file_buffer->filename,
9342660Smarkm                   INFO_NODE_LABEL, pagename,
9442660Smarkm                   INFO_UP_LABEL);
9542660Smarkm          oldsize = file_buffer->filesize;
9642660Smarkm          hlen = strlen (header);
9742660Smarkm          plen = strlen (page);
9842660Smarkm          newsize = (oldsize + hlen + plen);
9942660Smarkm          file_buffer->contents =
10042660Smarkm            (char *)xrealloc (file_buffer->contents, 1 + newsize);
10142660Smarkm          memcpy (file_buffer->contents + oldsize, header, hlen);
10256160Sru          memcpy (file_buffer->contents + oldsize + hlen, page, plen);
10342660Smarkm          file_buffer->contents[newsize] = '\0';
10442660Smarkm          file_buffer->filesize = newsize;
10542660Smarkm          file_buffer->finfo.st_size = newsize;
10642660Smarkm          build_tags_and_nodes (file_buffer);
10742660Smarkm          free (page);
10856160Sru	  /* We have just relocated file_buffer->contents from under
10956160Sru	     the feet of info_windows[] array.  Therefore, all the
11056160Sru	     nodes on that list which are showing man pages have their
11156160Sru	     contents member pointing into the blue.  Undo that harm.  */
11256160Sru	  if (old_contents && oldsize && old_contents != file_buffer->contents)
11356160Sru	    {
11456160Sru	      int iw;
11556160Sru	      INFO_WINDOW *info_win;
11656160Sru	      char *old_contents_end = old_contents + oldsize;
11756160Sru
11856160Sru	      for (iw = 0; (info_win = info_windows[iw]); iw++)
11956160Sru		{
12056160Sru		  int in;
12156160Sru
12256160Sru		  for (in = 0; in < info_win->nodes_index; in++)
12356160Sru		    {
12456160Sru		      NODE *node = info_win->nodes[in];
12556160Sru
12656160Sru		      /* It really only suffices to see that node->filename
12756160Sru			 is "*manpages*".  But after several hours of
12856160Sru			 debugging this, would you blame me for being a bit
12956160Sru			 paranoid?  */
13056160Sru		      if (node && node->filename && node->contents &&
13156160Sru			  strcmp (node->filename,
13256160Sru				  MANPAGE_FILE_BUFFER_NAME) == 0 &&
13356160Sru			  node->contents >= old_contents &&
13456160Sru			  node->contents + node->nodelen <= old_contents_end)
13556160Sru			{
13656160Sru			  info_win->nodes[in] =
13756160Sru			    manpage_node_of_file_buffer (file_buffer,
13856160Sru							 node->nodename);
13956160Sru			  free (node->nodename);
14056160Sru			  free (node);
14156160Sru			}
14256160Sru		    }
14356160Sru		}
14456160Sru	    }
14542660Smarkm        }
14621495Sjmacd
14721495Sjmacd      node = manpage_node_of_file_buffer (file_buffer, pagename);
14821495Sjmacd    }
14921495Sjmacd
15021495Sjmacd  return (node);
15121495Sjmacd}
15221495Sjmacd
15321495SjmacdFILE_BUFFER *
15421495Sjmacdcreate_manpage_file_buffer ()
15521495Sjmacd{
15642660Smarkm  FILE_BUFFER *file_buffer = make_file_buffer ();
15742660Smarkm  file_buffer->filename = xstrdup (MANPAGE_FILE_BUFFER_NAME);
15842660Smarkm  file_buffer->fullpath = xstrdup (MANPAGE_FILE_BUFFER_NAME);
15921495Sjmacd  file_buffer->finfo.st_size = 0;
16021495Sjmacd  file_buffer->filesize = 0;
16121495Sjmacd  file_buffer->contents = (char *)NULL;
16221495Sjmacd  file_buffer->flags = (N_IsInternal | N_CannotGC | N_IsManPage);
163116525Sru
16421495Sjmacd  return (file_buffer);
16521495Sjmacd}
16621495Sjmacd
16721495Sjmacd/* Scan the list of directories in PATH looking for FILENAME.  If we find
16821495Sjmacd   one that is an executable file, return it as a new string.  Otherwise,
16921495Sjmacd   return a NULL pointer. */
17021495Sjmacdstatic char *
17121495Sjmacdexecutable_file_in_path (filename, path)
17221495Sjmacd     char *filename, *path;
17321495Sjmacd{
17421495Sjmacd  struct stat finfo;
17521495Sjmacd  char *temp_dirname;
17621495Sjmacd  int statable, dirname_index;
17721495Sjmacd
17821495Sjmacd  dirname_index = 0;
17921495Sjmacd
18042660Smarkm  while ((temp_dirname = extract_colon_unit (path, &dirname_index)))
18121495Sjmacd    {
18221495Sjmacd      char *temp;
18356160Sru      char *temp_end;
18456160Sru      int i;
18521495Sjmacd
18621495Sjmacd      /* Expand a leading tilde if one is present. */
18721495Sjmacd      if (*temp_dirname == '~')
18842660Smarkm        {
18942660Smarkm          char *expanded_dirname;
19021495Sjmacd
19142660Smarkm          expanded_dirname = tilde_expand_word (temp_dirname);
19242660Smarkm          free (temp_dirname);
19342660Smarkm          temp_dirname = expanded_dirname;
19442660Smarkm        }
19521495Sjmacd
19656160Sru      temp = (char *)xmalloc (34 + strlen (temp_dirname) + strlen (filename));
19721495Sjmacd      strcpy (temp, temp_dirname);
19856160Sru      if (!IS_SLASH (temp[(strlen (temp)) - 1]))
19942660Smarkm        strcat (temp, "/");
20021495Sjmacd      strcat (temp, filename);
20156160Sru      temp_end = temp + strlen (temp);
20221495Sjmacd
20321495Sjmacd      free (temp_dirname);
20421495Sjmacd
20556160Sru      /* Look for FILENAME, possibly with any of the extensions
20656160Sru	 in EXEC_EXTENSIONS[].  */
20756160Sru      for (i = 0; exec_extensions[i]; i++)
20856160Sru	{
20956160Sru	  if (exec_extensions[i][0])
21056160Sru	    strcpy (temp_end, exec_extensions[i]);
21156160Sru	  statable = (stat (temp, &finfo) == 0);
21221495Sjmacd
21356160Sru	  /* If we have found a regular executable file, then use it. */
21456160Sru	  if ((statable) && (S_ISREG (finfo.st_mode)) &&
21556160Sru	      (access (temp, X_OK) == 0))
21656160Sru	    return (temp);
21756160Sru	}
21856160Sru
21956160Sru      free (temp);
22021495Sjmacd    }
22121495Sjmacd  return ((char *)NULL);
22221495Sjmacd}
22321495Sjmacd
22421495Sjmacd/* Return the full pathname of the system man page formatter. */
22521495Sjmacdstatic char *
22621495Sjmacdfind_man_formatter ()
22721495Sjmacd{
22821495Sjmacd  return (executable_file_in_path ("man", (char *)getenv ("PATH")));
22921495Sjmacd}
23021495Sjmacd
23121495Sjmacdstatic char *manpage_pagename = (char *)NULL;
23221495Sjmacdstatic char *manpage_section  = (char *)NULL;
23321495Sjmacd
23421495Sjmacdstatic void
23521495Sjmacdget_page_and_section (pagename)
23621495Sjmacd     char *pagename;
23721495Sjmacd{
23821495Sjmacd  register int i;
23921495Sjmacd
24021495Sjmacd  if (manpage_pagename)
24121495Sjmacd    free (manpage_pagename);
24221495Sjmacd
24321495Sjmacd  if (manpage_section)
24421495Sjmacd    free (manpage_section);
24521495Sjmacd
24621495Sjmacd  manpage_pagename = (char *)NULL;
24721495Sjmacd  manpage_section  = (char *)NULL;
24821495Sjmacd
24921495Sjmacd  for (i = 0; pagename[i] != '\0' && pagename[i] != '('; i++);
25021495Sjmacd
25121495Sjmacd  manpage_pagename = (char *)xmalloc (1 + i);
25221495Sjmacd  strncpy (manpage_pagename, pagename, i);
25321495Sjmacd  manpage_pagename[i] = '\0';
25421495Sjmacd
25521495Sjmacd  if (pagename[i] == '(')
25621495Sjmacd    {
25721495Sjmacd      int start;
25821495Sjmacd
25921495Sjmacd      start = i + 1;
26021495Sjmacd
26121495Sjmacd      for (i = start; pagename[i] != '\0' && pagename[i] != ')'; i++);
26221495Sjmacd
26321495Sjmacd      manpage_section = (char *)xmalloc (1 + (i - start));
26421495Sjmacd      strncpy (manpage_section, pagename + start, (i - start));
26521495Sjmacd      manpage_section[i - start] = '\0';
26621495Sjmacd    }
26721495Sjmacd}
26821495Sjmacd
26956160Sru#if PIPE_USE_FORK
27021495Sjmacdstatic void
27121495Sjmacdreap_children (sig)
27221495Sjmacd     int sig;
27321495Sjmacd{
27456160Sru  wait (NULL);
27521495Sjmacd}
27656160Sru#endif
27721495Sjmacd
27821495Sjmacdstatic char *
27921495Sjmacdget_manpage_contents (pagename)
28021495Sjmacd     char *pagename;
28121495Sjmacd{
28221495Sjmacd  static char *formatter_args[4] = { (char *)NULL };
28321495Sjmacd  int pipes[2];
28421495Sjmacd  pid_t child;
28556160Sru  RETSIGTYPE (*sigsave) ();
28656160Sru  char *formatted_page = NULL;
28721495Sjmacd  int arg_index = 1;
28821495Sjmacd
28921495Sjmacd  if (formatter_args[0] == (char *)NULL)
29021495Sjmacd    formatter_args[0] = find_man_formatter ();
29121495Sjmacd
29221495Sjmacd  if (formatter_args[0] == (char *)NULL)
29321495Sjmacd    return ((char *)NULL);
29421495Sjmacd
29521495Sjmacd  get_page_and_section (pagename);
29621495Sjmacd
29721495Sjmacd  if (manpage_section != (char *)NULL)
29821495Sjmacd    formatter_args[arg_index++] = manpage_section;
29921495Sjmacd
30021495Sjmacd  formatter_args[arg_index++] = manpage_pagename;
30121495Sjmacd  formatter_args[arg_index] = (char *)NULL;
30221495Sjmacd
30321495Sjmacd  /* Open a pipe to this program, read the output, and save it away
30421495Sjmacd     in FORMATTED_PAGE.  The reader end of the pipe is pipes[0]; the
30521495Sjmacd     writer end is pipes[1]. */
30656160Sru#if PIPE_USE_FORK
30721495Sjmacd  pipe (pipes);
30821495Sjmacd
30956160Sru  sigsave = signal (SIGCHLD, reap_children);
31021495Sjmacd
31121495Sjmacd  child = fork ();
31221495Sjmacd  if (child == -1)
31321495Sjmacd    return ((char *)NULL);
31421495Sjmacd
31521495Sjmacd  if (child != 0)
31621495Sjmacd    {
31721495Sjmacd      /* In the parent, close the writing end of the pipe, and read from
31842660Smarkm         the exec'd child. */
31921495Sjmacd      close (pipes[1]);
32021495Sjmacd      formatted_page = read_from_fd (pipes[0]);
32121495Sjmacd      close (pipes[0]);
32256160Sru      signal (SIGCHLD, sigsave);
32321495Sjmacd    }
32421495Sjmacd  else
32556160Sru    { /* In the child, close the read end of the pipe, make the write end
32642660Smarkm         of the pipe be stdout, and execute the man page formatter. */
32721495Sjmacd      close (pipes[0]);
32856160Sru      freopen (NULL_DEVICE, "w", stderr);
32956160Sru      freopen (NULL_DEVICE, "r", stdin);
33021495Sjmacd      dup2 (pipes[1], fileno (stdout));
33121495Sjmacd
33221495Sjmacd      execv (formatter_args[0], formatter_args);
33321495Sjmacd
33421495Sjmacd      /* If we get here, we couldn't exec, so close out the pipe and
33542660Smarkm         exit. */
33621495Sjmacd      close (pipes[1]);
33756160Sru      xexit (0);
33821495Sjmacd    }
33956160Sru#else  /* !PIPE_USE_FORK */
34056160Sru  /* Cannot fork/exec, but can popen/pclose.  */
34156160Sru  {
34256160Sru    FILE *fpipe;
34356160Sru    char *cmdline = xmalloc (strlen (formatter_args[0])
34456160Sru			     + strlen (manpage_pagename)
34556160Sru			     + (arg_index > 2 ? strlen (manpage_section) : 0)
34656160Sru 			     + 3);
34756160Sru    int save_stderr = dup (fileno (stderr));
34856160Sru    int fd_err = open (NULL_DEVICE, O_WRONLY, 0666);
34921495Sjmacd
35056160Sru    if (fd_err > 2)
35156160Sru      dup2 (fd_err, fileno (stderr)); /* Don't print errors. */
35256160Sru    sprintf (cmdline, "%s %s %s", formatter_args[0], manpage_pagename,
35356160Sru				  arg_index > 2 ? manpage_section : "");
35456160Sru    fpipe = popen (cmdline, "r");
35556160Sru    free (cmdline);
35656160Sru    if (fd_err > 2)
35756160Sru      close (fd_err);
35856160Sru    dup2 (save_stderr, fileno (stderr));
35956160Sru    if (fpipe == 0)
36056160Sru      return ((char *)NULL);
36156160Sru    formatted_page = read_from_fd (fileno (fpipe));
36256160Sru    if (pclose (fpipe) == -1)
36356160Sru      {
36456160Sru	if (formatted_page)
36556160Sru	  free (formatted_page);
36656160Sru	return ((char *)NULL);
36756160Sru      }
36856160Sru  }
36956160Sru#endif /* !PIPE_USE_FORK */
37056160Sru
37121495Sjmacd  /* If we have the page, then clean it up. */
37221495Sjmacd  if (formatted_page)
37321495Sjmacd    clean_manpage (formatted_page);
37421495Sjmacd
37521495Sjmacd  return (formatted_page);
37621495Sjmacd}
37721495Sjmacd
37821495Sjmacdstatic void
37921495Sjmacdclean_manpage (manpage)
38021495Sjmacd     char *manpage;
38121495Sjmacd{
38221495Sjmacd  register int i, j;
38321495Sjmacd  int newline_count = 0;
38421495Sjmacd  char *newpage;
38521495Sjmacd
38621495Sjmacd  newpage = (char *)xmalloc (1 + strlen (manpage));
38721495Sjmacd
38842660Smarkm  for (i = 0, j = 0; (newpage[j] = manpage[i]); i++, j++)
38921495Sjmacd    {
39021495Sjmacd      if (manpage[i] == '\n')
39142660Smarkm        newline_count++;
39221495Sjmacd      else
39342660Smarkm        newline_count = 0;
39421495Sjmacd
39521495Sjmacd      if (newline_count == 3)
39642660Smarkm        {
39742660Smarkm          j--;
39842660Smarkm          newline_count--;
39942660Smarkm        }
40021495Sjmacd
40193139Sru      /* A malformed man page could have a \b as its first character,
40293139Sru         in which case decrementing j by 2 will cause us to write into
40393139Sru         newpage[-1], smashing the hidden info stored there by malloc.  */
404116525Sru      if (manpage[i] == '\b' || (manpage[i] == '\f' && j > 0))
40542660Smarkm        j -= 2;
40693139Sru      else if (!raw_escapes_p)
40793139Sru	{
40893139Sru	  /* Remove the ANSI escape sequences for color, boldface,
40993139Sru	     underlining, and italics, generated by some versions of
41093139Sru	     Groff.  */
41193139Sru	  if (manpage[i] == '\033' && manpage[i + 1] == '['
41293139Sru	      && isdigit (manpage[i + 2]))
41393139Sru	    {
41493139Sru	      if (isdigit (manpage[i + 3]) && manpage[i + 4] == 'm')
41593139Sru		{
41693139Sru		  i += 4;
41793139Sru		  j--;
41893139Sru		}
41993139Sru	      else if (manpage[i + 3] == 'm')
42093139Sru		{
42193139Sru		  i += 3;
42293139Sru		  j--;
42393139Sru		}
42493139Sru	      /* Else do nothing: it's some unknown escape sequence,
42593139Sru		 so let's leave it alone.  */
42693139Sru	    }
42793139Sru	}
42821495Sjmacd    }
42921495Sjmacd
43093139Sru  newpage[j++] = 0;
43121495Sjmacd
43221495Sjmacd  strcpy (manpage, newpage);
43321495Sjmacd  free (newpage);
43421495Sjmacd}
43521495Sjmacd
43621495Sjmacdstatic NODE *
43721495Sjmacdmanpage_node_of_file_buffer (file_buffer, pagename)
43821495Sjmacd     FILE_BUFFER *file_buffer;
43921495Sjmacd     char *pagename;
44021495Sjmacd{
44121495Sjmacd  NODE *node = (NODE *)NULL;
44221495Sjmacd  TAG *tag = (TAG *)NULL;
44321495Sjmacd
44421495Sjmacd  if (file_buffer->contents)
44521495Sjmacd    {
44621495Sjmacd      register int i;
44721495Sjmacd
44842660Smarkm      for (i = 0; (tag = file_buffer->tags[i]); i++)
44942660Smarkm        {
45042660Smarkm          if (strcasecmp (pagename, tag->nodename) == 0)
45142660Smarkm            break;
45242660Smarkm        }
45321495Sjmacd    }
45421495Sjmacd
45521495Sjmacd  if (tag)
45621495Sjmacd    {
45721495Sjmacd      node = (NODE *)xmalloc (sizeof (NODE));
45821495Sjmacd      node->filename = file_buffer->filename;
45956160Sru      node->nodename = xstrdup (tag->nodename);
46021495Sjmacd      node->contents = file_buffer->contents + tag->nodestart;
46121495Sjmacd      node->nodelen = tag->nodelen;
46221495Sjmacd      node->flags    = 0;
46356160Sru      node->display_pos = 0;
46421495Sjmacd      node->parent   = (char *)NULL;
46521495Sjmacd      node->flags = (N_HasTagsTable | N_IsManPage);
46621495Sjmacd      node->contents += skip_node_separator (node->contents);
46721495Sjmacd    }
46821495Sjmacd
46921495Sjmacd  return (node);
47021495Sjmacd}
47121495Sjmacd
47221495Sjmacdstatic char *
47321495Sjmacdread_from_fd (fd)
47421495Sjmacd     int fd;
47521495Sjmacd{
47621495Sjmacd  struct timeval timeout;
47721495Sjmacd  char *buffer = (char *)NULL;
47821495Sjmacd  int bsize = 0;
47921495Sjmacd  int bindex = 0;
48021495Sjmacd  int select_result;
48121495Sjmacd#if defined (FD_SET)
48221495Sjmacd  fd_set read_fds;
48321495Sjmacd
48421495Sjmacd  timeout.tv_sec = 15;
48521495Sjmacd  timeout.tv_usec = 0;
48621495Sjmacd
48721495Sjmacd  FD_ZERO (&read_fds);
48821495Sjmacd  FD_SET (fd, &read_fds);
48921495Sjmacd
49021495Sjmacd  select_result = select (fd + 1, fd_set_cast (&read_fds), 0, 0, &timeout);
49121495Sjmacd#else /* !FD_SET */
49221495Sjmacd  select_result = 1;
49321495Sjmacd#endif /* !FD_SET */
49421495Sjmacd
49521495Sjmacd  switch (select_result)
49621495Sjmacd    {
49721495Sjmacd    case 0:
49821495Sjmacd    case -1:
49921495Sjmacd      break;
50021495Sjmacd
50121495Sjmacd    default:
50221495Sjmacd      {
50321495Sjmacd        int amount_read;
50421495Sjmacd        int done = 0;
50521495Sjmacd
50621495Sjmacd        while (!done)
50721495Sjmacd          {
50821495Sjmacd            while ((bindex + 1024) > (bsize))
50921495Sjmacd              buffer = (char *)xrealloc (buffer, (bsize += 1024));
51021495Sjmacd            buffer[bindex] = '\0';
51121495Sjmacd
51221495Sjmacd            amount_read = read (fd, buffer + bindex, 1023);
51321495Sjmacd
51421495Sjmacd            if (amount_read < 0)
51521495Sjmacd              {
51621495Sjmacd                done = 1;
51721495Sjmacd              }
51821495Sjmacd            else
51921495Sjmacd              {
52021495Sjmacd                bindex += amount_read;
52121495Sjmacd                buffer[bindex] = '\0';
52221495Sjmacd                if (amount_read == 0)
52321495Sjmacd                  done = 1;
52421495Sjmacd              }
52521495Sjmacd          }
52621495Sjmacd      }
52721495Sjmacd    }
52821495Sjmacd
52921495Sjmacd  if ((buffer != (char *)NULL) && (*buffer == '\0'))
53021495Sjmacd    {
53121495Sjmacd      free (buffer);
53221495Sjmacd      buffer = (char *)NULL;
53321495Sjmacd    }
53421495Sjmacd
53521495Sjmacd  return (buffer);
53621495Sjmacd}
53721495Sjmacd
53821495Sjmacdstatic char *reference_section_starters[] =
53921495Sjmacd{
54021495Sjmacd  "\nRELATED INFORMATION",
54121495Sjmacd  "\nRELATED\tINFORMATION",
54221495Sjmacd  "RELATED INFORMATION\n",
54321495Sjmacd  "RELATED\tINFORMATION\n",
54421495Sjmacd  "\nSEE ALSO",
54521495Sjmacd  "\nSEE\tALSO",
54621495Sjmacd  "SEE ALSO\n",
54721495Sjmacd  "SEE\tALSO\n",
54821495Sjmacd  (char *)NULL
54921495Sjmacd};
55021495Sjmacd
55121495Sjmacdstatic SEARCH_BINDING frs_binding;
55221495Sjmacd
55321495Sjmacdstatic SEARCH_BINDING *
55421495Sjmacdfind_reference_section (node)
55521495Sjmacd     NODE *node;
55621495Sjmacd{
55721495Sjmacd  register int i;
55821495Sjmacd  long position = -1;
55921495Sjmacd
56021495Sjmacd  frs_binding.buffer = node->contents;
56121495Sjmacd  frs_binding.start = 0;
56221495Sjmacd  frs_binding.end = node->nodelen;
56321495Sjmacd  frs_binding.flags = S_SkipDest;
56421495Sjmacd
56521495Sjmacd  for (i = 0; reference_section_starters[i] != (char *)NULL; i++)
56621495Sjmacd    {
56721495Sjmacd      position = search_forward (reference_section_starters[i], &frs_binding);
56821495Sjmacd      if (position != -1)
56942660Smarkm        break;
57021495Sjmacd    }
57121495Sjmacd
57221495Sjmacd  if (position == -1)
57321495Sjmacd    return ((SEARCH_BINDING *)NULL);
57421495Sjmacd
57521495Sjmacd  /* We found the start of the reference section, and point is right after
57621495Sjmacd     the string which starts it.  The text from here to the next header
57721495Sjmacd     (or end of buffer) contains the only references in this manpage. */
57821495Sjmacd  frs_binding.start = position;
57921495Sjmacd
58021495Sjmacd  for (i = frs_binding.start; i < frs_binding.end - 2; i++)
58121495Sjmacd    {
58221495Sjmacd      if ((frs_binding.buffer[i] == '\n') &&
58342660Smarkm          (!whitespace (frs_binding.buffer[i + 1])))
58442660Smarkm        {
58542660Smarkm          frs_binding.end = i;
58642660Smarkm          break;
58742660Smarkm        }
58821495Sjmacd    }
58921495Sjmacd
59021495Sjmacd  return (&frs_binding);
59121495Sjmacd}
59221495Sjmacd
59321495SjmacdREFERENCE **
59421495Sjmacdxrefs_of_manpage (node)
59521495Sjmacd     NODE *node;
59621495Sjmacd{
59721495Sjmacd  SEARCH_BINDING *reference_section;
59821495Sjmacd  REFERENCE **refs = (REFERENCE **)NULL;
59921495Sjmacd  int refs_index = 0;
60021495Sjmacd  int refs_slots = 0;
60121495Sjmacd  long position;
60221495Sjmacd
60321495Sjmacd  reference_section = find_reference_section (node);
60421495Sjmacd
60521495Sjmacd  if (reference_section == (SEARCH_BINDING *)NULL)
60621495Sjmacd    return ((REFERENCE **)NULL);
60721495Sjmacd
60821495Sjmacd  /* Grovel the reference section building a list of references found there.
60921495Sjmacd     A reference is alphabetic characters followed by non-whitespace text
61021495Sjmacd     within parenthesis. */
61121495Sjmacd  reference_section->flags = 0;
61221495Sjmacd
61321495Sjmacd  while ((position = search_forward ("(", reference_section)) != -1)
61421495Sjmacd    {
61521495Sjmacd      register int start, end;
61621495Sjmacd
61721495Sjmacd      for (start = position; start > reference_section->start; start--)
61842660Smarkm        if (whitespace (reference_section->buffer[start]))
61942660Smarkm          break;
62021495Sjmacd
62121495Sjmacd      start++;
62221495Sjmacd
62321495Sjmacd      for (end = position; end < reference_section->end; end++)
62442660Smarkm        {
62542660Smarkm          if (whitespace (reference_section->buffer[end]))
62642660Smarkm            {
62742660Smarkm              end = start;
62842660Smarkm              break;
62942660Smarkm            }
63021495Sjmacd
63142660Smarkm          if (reference_section->buffer[end] == ')')
63242660Smarkm            {
63342660Smarkm              end++;
63442660Smarkm              break;
63542660Smarkm            }
63642660Smarkm        }
63721495Sjmacd
63821495Sjmacd      if (end != start)
63942660Smarkm        {
64042660Smarkm          REFERENCE *entry;
64142660Smarkm          int len = end - start;
64221495Sjmacd
64342660Smarkm          entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
64442660Smarkm          entry->label = (char *)xmalloc (1 + len);
64542660Smarkm          strncpy (entry->label, (reference_section->buffer) + start, len);
64642660Smarkm          entry->label[len] = '\0';
64742660Smarkm          entry->filename = xstrdup (node->filename);
64842660Smarkm          entry->nodename = xstrdup (entry->label);
64942660Smarkm          entry->start = start;
65042660Smarkm          entry->end = end;
65121495Sjmacd
65242660Smarkm          add_pointer_to_array
65342660Smarkm            (entry, refs_index, refs, refs_slots, 10, REFERENCE *);
65442660Smarkm        }
65521495Sjmacd
65621495Sjmacd      reference_section->start = position + 1;
65721495Sjmacd    }
65821495Sjmacd
65921495Sjmacd  return (refs);
66021495Sjmacd}
66121495Sjmacd
66221495Sjmacdlong
66321495Sjmacdlocate_manpage_xref (node, start, dir)
66421495Sjmacd     NODE *node;
66521495Sjmacd     long start;
66621495Sjmacd     int dir;
66721495Sjmacd{
66821495Sjmacd  REFERENCE **refs;
66921495Sjmacd  long position = -1;
67021495Sjmacd
67121495Sjmacd  refs = xrefs_of_manpage (node);
67221495Sjmacd
67321495Sjmacd  if (refs)
67421495Sjmacd    {
67521495Sjmacd      register int i, count;
67621495Sjmacd      REFERENCE *entry;
67721495Sjmacd
67821495Sjmacd      for (i = 0; refs[i]; i++);
67921495Sjmacd      count = i;
68021495Sjmacd
68121495Sjmacd      if (dir > 0)
68242660Smarkm        {
68342660Smarkm          for (i = 0; (entry = refs[i]); i++)
68442660Smarkm            if (entry->start > start)
68542660Smarkm              {
68642660Smarkm                position = entry->start;
68742660Smarkm                break;
68842660Smarkm              }
68942660Smarkm        }
69021495Sjmacd      else
69142660Smarkm        {
69242660Smarkm          for (i = count - 1; i > -1; i--)
69342660Smarkm            {
69442660Smarkm              entry = refs[i];
69521495Sjmacd
69642660Smarkm              if (entry->start < start)
69742660Smarkm                {
69842660Smarkm                  position = entry->start;
69942660Smarkm                  break;
70042660Smarkm                }
70142660Smarkm            }
70242660Smarkm        }
70321495Sjmacd
70421495Sjmacd      info_free_references (refs);
70521495Sjmacd    }
70621495Sjmacd  return (position);
70721495Sjmacd}
70821495Sjmacd
70921495Sjmacd/* This one was a little tricky.  The binding buffer that is passed in has
71021495Sjmacd   a START and END value of 0 -- strlen (window-line-containing-point).
71121495Sjmacd   The BUFFER is a pointer to the start of that line. */
71221495SjmacdREFERENCE **
71321495Sjmacdmanpage_xrefs_in_binding (node, binding)
71421495Sjmacd     NODE *node;
71521495Sjmacd     SEARCH_BINDING *binding;
71621495Sjmacd{
71721495Sjmacd  register int i;
71821495Sjmacd  REFERENCE **all_refs = xrefs_of_manpage (node);
71921495Sjmacd  REFERENCE **brefs = (REFERENCE **)NULL;
72021495Sjmacd  REFERENCE *entry;
72121495Sjmacd  int brefs_index = 0;
72221495Sjmacd  int brefs_slots = 0;
72321495Sjmacd  int start, end;
72421495Sjmacd
72521495Sjmacd  if (!all_refs)
72621495Sjmacd    return ((REFERENCE **)NULL);
72721495Sjmacd
72821495Sjmacd  start = binding->start + (binding->buffer - node->contents);
72921495Sjmacd  end = binding->end + (binding->buffer - node->contents);
73021495Sjmacd
73142660Smarkm  for (i = 0; (entry = all_refs[i]); i++)
73221495Sjmacd    {
73321495Sjmacd      if ((entry->start > start) && (entry->end < end))
73442660Smarkm        {
73542660Smarkm          add_pointer_to_array
73642660Smarkm            (entry, brefs_index, brefs, brefs_slots, 10, REFERENCE *);
73742660Smarkm        }
73821495Sjmacd      else
73942660Smarkm        {
74042660Smarkm          maybe_free (entry->label);
74142660Smarkm          maybe_free (entry->filename);
74242660Smarkm          maybe_free (entry->nodename);
74342660Smarkm          free (entry);
74442660Smarkm        }
74521495Sjmacd    }
74621495Sjmacd
74721495Sjmacd  free (all_refs);
74821495Sjmacd  return (brefs);
74921495Sjmacd}
750