142660Smarkm/*  man.c: How to read and format man files.
2146515Sru    $Id: man.c,v 1.4 2004/04/11 17:56:46 karl Exp $
321495Sjmacd
4146515Sru   Copyright (C) 1995, 1997, 1998, 1999, 2000, 2002, 2003, 2004 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
56146515Srustatic char *read_from_fd (int fd);
57146515Srustatic void clean_manpage (char *manpage);
58146515Srustatic NODE *manpage_node_of_file_buffer (FILE_BUFFER *file_buffer,
59146515Sru    char *pagename);
60146515Srustatic char *get_manpage_contents (char *pagename);
6121495Sjmacd
6221495SjmacdNODE *
63146515Srumake_manpage_node (char *pagename)
6421495Sjmacd{
6521495Sjmacd  return (info_get_node (MANPAGE_FILE_BUFFER_NAME, pagename));
6621495Sjmacd}
6721495Sjmacd
6821495SjmacdNODE *
69146515Sruget_manpage_node (FILE_BUFFER *file_buffer, char *pagename)
7021495Sjmacd{
7121495Sjmacd  NODE *node;
7221495Sjmacd
7321495Sjmacd  node = manpage_node_of_file_buffer (file_buffer, pagename);
7421495Sjmacd
7521495Sjmacd  if (!node)
7621495Sjmacd    {
7721495Sjmacd      char *page;
7821495Sjmacd
7921495Sjmacd      page = get_manpage_contents (pagename);
8021495Sjmacd
8121495Sjmacd      if (page)
8242660Smarkm        {
8342660Smarkm          char header[1024];
8442660Smarkm          long oldsize, newsize;
8542660Smarkm          int hlen, plen;
8656160Sru	  char *old_contents = file_buffer->contents;
8721495Sjmacd
8842660Smarkm          sprintf (header, "\n\n%c\n%s %s,  %s %s,  %s (dir)\n\n",
8942660Smarkm                   INFO_COOKIE,
9042660Smarkm                   INFO_FILE_LABEL, file_buffer->filename,
9142660Smarkm                   INFO_NODE_LABEL, pagename,
9242660Smarkm                   INFO_UP_LABEL);
9342660Smarkm          oldsize = file_buffer->filesize;
9442660Smarkm          hlen = strlen (header);
9542660Smarkm          plen = strlen (page);
9642660Smarkm          newsize = (oldsize + hlen + plen);
9742660Smarkm          file_buffer->contents =
9842660Smarkm            (char *)xrealloc (file_buffer->contents, 1 + newsize);
9942660Smarkm          memcpy (file_buffer->contents + oldsize, header, hlen);
10056160Sru          memcpy (file_buffer->contents + oldsize + hlen, page, plen);
10142660Smarkm          file_buffer->contents[newsize] = '\0';
10242660Smarkm          file_buffer->filesize = newsize;
10342660Smarkm          file_buffer->finfo.st_size = newsize;
10442660Smarkm          build_tags_and_nodes (file_buffer);
10542660Smarkm          free (page);
10656160Sru	  /* We have just relocated file_buffer->contents from under
10756160Sru	     the feet of info_windows[] array.  Therefore, all the
10856160Sru	     nodes on that list which are showing man pages have their
10956160Sru	     contents member pointing into the blue.  Undo that harm.  */
11056160Sru	  if (old_contents && oldsize && old_contents != file_buffer->contents)
11156160Sru	    {
11256160Sru	      int iw;
11356160Sru	      INFO_WINDOW *info_win;
11456160Sru	      char *old_contents_end = old_contents + oldsize;
11556160Sru
11656160Sru	      for (iw = 0; (info_win = info_windows[iw]); iw++)
11756160Sru		{
11856160Sru		  int in;
11956160Sru
12056160Sru		  for (in = 0; in < info_win->nodes_index; in++)
12156160Sru		    {
122146515Sru		      NODE *tmp_node = info_win->nodes[in];
12356160Sru
12456160Sru		      /* It really only suffices to see that node->filename
12556160Sru			 is "*manpages*".  But after several hours of
12656160Sru			 debugging this, would you blame me for being a bit
12756160Sru			 paranoid?  */
128146515Sru		      if (tmp_node && tmp_node->filename
129146515Sru                          && tmp_node->contents
130146515Sru                          && strcmp (tmp_node->filename,
131146515Sru				  MANPAGE_FILE_BUFFER_NAME) == 0
132146515Sru                          && tmp_node->contents >= old_contents
133146515Sru                          && tmp_node->contents + tmp_node->nodelen
134146515Sru                                <= old_contents_end)
13556160Sru			{
13656160Sru			  info_win->nodes[in] =
13756160Sru			    manpage_node_of_file_buffer (file_buffer,
138146515Sru                                tmp_node->nodename);
139146515Sru			  free (tmp_node->nodename);
140146515Sru			  free (tmp_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 *
154146515Srucreate_manpage_file_buffer (void)
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 *
171146515Sruexecutable_file_in_path (char *filename, char *path)
17221495Sjmacd{
17321495Sjmacd  struct stat finfo;
17421495Sjmacd  char *temp_dirname;
17521495Sjmacd  int statable, dirname_index;
17621495Sjmacd
17721495Sjmacd  dirname_index = 0;
17821495Sjmacd
17942660Smarkm  while ((temp_dirname = extract_colon_unit (path, &dirname_index)))
18021495Sjmacd    {
18121495Sjmacd      char *temp;
18256160Sru      char *temp_end;
18356160Sru      int i;
18421495Sjmacd
18521495Sjmacd      /* Expand a leading tilde if one is present. */
18621495Sjmacd      if (*temp_dirname == '~')
18742660Smarkm        {
18842660Smarkm          char *expanded_dirname;
18921495Sjmacd
19042660Smarkm          expanded_dirname = tilde_expand_word (temp_dirname);
19142660Smarkm          free (temp_dirname);
19242660Smarkm          temp_dirname = expanded_dirname;
19342660Smarkm        }
19421495Sjmacd
19556160Sru      temp = (char *)xmalloc (34 + strlen (temp_dirname) + strlen (filename));
19621495Sjmacd      strcpy (temp, temp_dirname);
19756160Sru      if (!IS_SLASH (temp[(strlen (temp)) - 1]))
19842660Smarkm        strcat (temp, "/");
19921495Sjmacd      strcat (temp, filename);
20056160Sru      temp_end = temp + strlen (temp);
20121495Sjmacd
20221495Sjmacd      free (temp_dirname);
20321495Sjmacd
20456160Sru      /* Look for FILENAME, possibly with any of the extensions
20556160Sru	 in EXEC_EXTENSIONS[].  */
20656160Sru      for (i = 0; exec_extensions[i]; i++)
20756160Sru	{
20856160Sru	  if (exec_extensions[i][0])
20956160Sru	    strcpy (temp_end, exec_extensions[i]);
21056160Sru	  statable = (stat (temp, &finfo) == 0);
21121495Sjmacd
21256160Sru	  /* If we have found a regular executable file, then use it. */
21356160Sru	  if ((statable) && (S_ISREG (finfo.st_mode)) &&
21456160Sru	      (access (temp, X_OK) == 0))
21556160Sru	    return (temp);
21656160Sru	}
21756160Sru
21856160Sru      free (temp);
21921495Sjmacd    }
22021495Sjmacd  return ((char *)NULL);
22121495Sjmacd}
22221495Sjmacd
22321495Sjmacd/* Return the full pathname of the system man page formatter. */
22421495Sjmacdstatic char *
225146515Srufind_man_formatter (void)
22621495Sjmacd{
22721495Sjmacd  return (executable_file_in_path ("man", (char *)getenv ("PATH")));
22821495Sjmacd}
22921495Sjmacd
23021495Sjmacdstatic char *manpage_pagename = (char *)NULL;
23121495Sjmacdstatic char *manpage_section  = (char *)NULL;
23221495Sjmacd
23321495Sjmacdstatic void
234146515Sruget_page_and_section (char *pagename)
23521495Sjmacd{
23621495Sjmacd  register int i;
23721495Sjmacd
23821495Sjmacd  if (manpage_pagename)
23921495Sjmacd    free (manpage_pagename);
24021495Sjmacd
24121495Sjmacd  if (manpage_section)
24221495Sjmacd    free (manpage_section);
24321495Sjmacd
24421495Sjmacd  manpage_pagename = (char *)NULL;
24521495Sjmacd  manpage_section  = (char *)NULL;
24621495Sjmacd
24721495Sjmacd  for (i = 0; pagename[i] != '\0' && pagename[i] != '('; i++);
24821495Sjmacd
24921495Sjmacd  manpage_pagename = (char *)xmalloc (1 + i);
25021495Sjmacd  strncpy (manpage_pagename, pagename, i);
25121495Sjmacd  manpage_pagename[i] = '\0';
25221495Sjmacd
25321495Sjmacd  if (pagename[i] == '(')
25421495Sjmacd    {
25521495Sjmacd      int start;
25621495Sjmacd
25721495Sjmacd      start = i + 1;
25821495Sjmacd
25921495Sjmacd      for (i = start; pagename[i] != '\0' && pagename[i] != ')'; i++);
26021495Sjmacd
26121495Sjmacd      manpage_section = (char *)xmalloc (1 + (i - start));
26221495Sjmacd      strncpy (manpage_section, pagename + start, (i - start));
26321495Sjmacd      manpage_section[i - start] = '\0';
26421495Sjmacd    }
26521495Sjmacd}
26621495Sjmacd
26756160Sru#if PIPE_USE_FORK
26821495Sjmacdstatic void
269146515Srureap_children (int sig)
27021495Sjmacd{
27156160Sru  wait (NULL);
27221495Sjmacd}
27356160Sru#endif
27421495Sjmacd
27521495Sjmacdstatic char *
276146515Sruget_manpage_contents (char *pagename)
27721495Sjmacd{
27821495Sjmacd  static char *formatter_args[4] = { (char *)NULL };
27921495Sjmacd  int pipes[2];
28021495Sjmacd  pid_t child;
281146515Sru  RETSIGTYPE (*sigsave) (int signum);
28256160Sru  char *formatted_page = NULL;
28321495Sjmacd  int arg_index = 1;
28421495Sjmacd
28521495Sjmacd  if (formatter_args[0] == (char *)NULL)
28621495Sjmacd    formatter_args[0] = find_man_formatter ();
28721495Sjmacd
28821495Sjmacd  if (formatter_args[0] == (char *)NULL)
28921495Sjmacd    return ((char *)NULL);
29021495Sjmacd
29121495Sjmacd  get_page_and_section (pagename);
29221495Sjmacd
29321495Sjmacd  if (manpage_section != (char *)NULL)
29421495Sjmacd    formatter_args[arg_index++] = manpage_section;
29521495Sjmacd
29621495Sjmacd  formatter_args[arg_index++] = manpage_pagename;
29721495Sjmacd  formatter_args[arg_index] = (char *)NULL;
29821495Sjmacd
29921495Sjmacd  /* Open a pipe to this program, read the output, and save it away
30021495Sjmacd     in FORMATTED_PAGE.  The reader end of the pipe is pipes[0]; the
30121495Sjmacd     writer end is pipes[1]. */
30256160Sru#if PIPE_USE_FORK
30321495Sjmacd  pipe (pipes);
30421495Sjmacd
30556160Sru  sigsave = signal (SIGCHLD, reap_children);
30621495Sjmacd
30721495Sjmacd  child = fork ();
30821495Sjmacd  if (child == -1)
30921495Sjmacd    return ((char *)NULL);
31021495Sjmacd
31121495Sjmacd  if (child != 0)
31221495Sjmacd    {
31321495Sjmacd      /* In the parent, close the writing end of the pipe, and read from
31442660Smarkm         the exec'd child. */
31521495Sjmacd      close (pipes[1]);
31621495Sjmacd      formatted_page = read_from_fd (pipes[0]);
31721495Sjmacd      close (pipes[0]);
31856160Sru      signal (SIGCHLD, sigsave);
31921495Sjmacd    }
32021495Sjmacd  else
32156160Sru    { /* In the child, close the read end of the pipe, make the write end
32242660Smarkm         of the pipe be stdout, and execute the man page formatter. */
32321495Sjmacd      close (pipes[0]);
32456160Sru      freopen (NULL_DEVICE, "w", stderr);
32556160Sru      freopen (NULL_DEVICE, "r", stdin);
32621495Sjmacd      dup2 (pipes[1], fileno (stdout));
32721495Sjmacd
32821495Sjmacd      execv (formatter_args[0], formatter_args);
32921495Sjmacd
33021495Sjmacd      /* If we get here, we couldn't exec, so close out the pipe and
33142660Smarkm         exit. */
33221495Sjmacd      close (pipes[1]);
33356160Sru      xexit (0);
33421495Sjmacd    }
33556160Sru#else  /* !PIPE_USE_FORK */
33656160Sru  /* Cannot fork/exec, but can popen/pclose.  */
33756160Sru  {
33856160Sru    FILE *fpipe;
33956160Sru    char *cmdline = xmalloc (strlen (formatter_args[0])
34056160Sru			     + strlen (manpage_pagename)
34156160Sru			     + (arg_index > 2 ? strlen (manpage_section) : 0)
34256160Sru 			     + 3);
34356160Sru    int save_stderr = dup (fileno (stderr));
34456160Sru    int fd_err = open (NULL_DEVICE, O_WRONLY, 0666);
34521495Sjmacd
34656160Sru    if (fd_err > 2)
34756160Sru      dup2 (fd_err, fileno (stderr)); /* Don't print errors. */
34856160Sru    sprintf (cmdline, "%s %s %s", formatter_args[0], manpage_pagename,
34956160Sru				  arg_index > 2 ? manpage_section : "");
35056160Sru    fpipe = popen (cmdline, "r");
35156160Sru    free (cmdline);
35256160Sru    if (fd_err > 2)
35356160Sru      close (fd_err);
35456160Sru    dup2 (save_stderr, fileno (stderr));
35556160Sru    if (fpipe == 0)
35656160Sru      return ((char *)NULL);
35756160Sru    formatted_page = read_from_fd (fileno (fpipe));
35856160Sru    if (pclose (fpipe) == -1)
35956160Sru      {
36056160Sru	if (formatted_page)
36156160Sru	  free (formatted_page);
36256160Sru	return ((char *)NULL);
36356160Sru      }
36456160Sru  }
36556160Sru#endif /* !PIPE_USE_FORK */
36656160Sru
36721495Sjmacd  /* If we have the page, then clean it up. */
36821495Sjmacd  if (formatted_page)
36921495Sjmacd    clean_manpage (formatted_page);
37021495Sjmacd
37121495Sjmacd  return (formatted_page);
37221495Sjmacd}
37321495Sjmacd
37421495Sjmacdstatic void
375146515Sruclean_manpage (char *manpage)
37621495Sjmacd{
37721495Sjmacd  register int i, j;
37821495Sjmacd  int newline_count = 0;
37921495Sjmacd  char *newpage;
38021495Sjmacd
38121495Sjmacd  newpage = (char *)xmalloc (1 + strlen (manpage));
38221495Sjmacd
38342660Smarkm  for (i = 0, j = 0; (newpage[j] = manpage[i]); i++, j++)
38421495Sjmacd    {
38521495Sjmacd      if (manpage[i] == '\n')
38642660Smarkm        newline_count++;
38721495Sjmacd      else
38842660Smarkm        newline_count = 0;
38921495Sjmacd
39021495Sjmacd      if (newline_count == 3)
39142660Smarkm        {
39242660Smarkm          j--;
39342660Smarkm          newline_count--;
39442660Smarkm        }
39521495Sjmacd
39693139Sru      /* A malformed man page could have a \b as its first character,
39793139Sru         in which case decrementing j by 2 will cause us to write into
39893139Sru         newpage[-1], smashing the hidden info stored there by malloc.  */
399116525Sru      if (manpage[i] == '\b' || (manpage[i] == '\f' && j > 0))
40042660Smarkm        j -= 2;
40193139Sru      else if (!raw_escapes_p)
40293139Sru	{
40393139Sru	  /* Remove the ANSI escape sequences for color, boldface,
40493139Sru	     underlining, and italics, generated by some versions of
40593139Sru	     Groff.  */
40693139Sru	  if (manpage[i] == '\033' && manpage[i + 1] == '['
40793139Sru	      && isdigit (manpage[i + 2]))
40893139Sru	    {
40993139Sru	      if (isdigit (manpage[i + 3]) && manpage[i + 4] == 'm')
41093139Sru		{
41193139Sru		  i += 4;
41293139Sru		  j--;
41393139Sru		}
41493139Sru	      else if (manpage[i + 3] == 'm')
41593139Sru		{
41693139Sru		  i += 3;
41793139Sru		  j--;
41893139Sru		}
41993139Sru	      /* Else do nothing: it's some unknown escape sequence,
42093139Sru		 so let's leave it alone.  */
42193139Sru	    }
42293139Sru	}
42321495Sjmacd    }
42421495Sjmacd
42593139Sru  newpage[j++] = 0;
42621495Sjmacd
42721495Sjmacd  strcpy (manpage, newpage);
42821495Sjmacd  free (newpage);
42921495Sjmacd}
43021495Sjmacd
43121495Sjmacdstatic NODE *
432146515Srumanpage_node_of_file_buffer (FILE_BUFFER *file_buffer, char *pagename)
43321495Sjmacd{
43421495Sjmacd  NODE *node = (NODE *)NULL;
43521495Sjmacd  TAG *tag = (TAG *)NULL;
43621495Sjmacd
43721495Sjmacd  if (file_buffer->contents)
43821495Sjmacd    {
43921495Sjmacd      register int i;
44021495Sjmacd
44142660Smarkm      for (i = 0; (tag = file_buffer->tags[i]); i++)
44242660Smarkm        {
44342660Smarkm          if (strcasecmp (pagename, tag->nodename) == 0)
44442660Smarkm            break;
44542660Smarkm        }
44621495Sjmacd    }
44721495Sjmacd
44821495Sjmacd  if (tag)
44921495Sjmacd    {
45021495Sjmacd      node = (NODE *)xmalloc (sizeof (NODE));
45121495Sjmacd      node->filename = file_buffer->filename;
45256160Sru      node->nodename = xstrdup (tag->nodename);
45321495Sjmacd      node->contents = file_buffer->contents + tag->nodestart;
45421495Sjmacd      node->nodelen = tag->nodelen;
45521495Sjmacd      node->flags    = 0;
45656160Sru      node->display_pos = 0;
45721495Sjmacd      node->parent   = (char *)NULL;
45821495Sjmacd      node->flags = (N_HasTagsTable | N_IsManPage);
45921495Sjmacd      node->contents += skip_node_separator (node->contents);
46021495Sjmacd    }
46121495Sjmacd
46221495Sjmacd  return (node);
46321495Sjmacd}
46421495Sjmacd
46521495Sjmacdstatic char *
466146515Sruread_from_fd (int fd)
46721495Sjmacd{
46821495Sjmacd  struct timeval timeout;
46921495Sjmacd  char *buffer = (char *)NULL;
47021495Sjmacd  int bsize = 0;
47121495Sjmacd  int bindex = 0;
47221495Sjmacd  int select_result;
47321495Sjmacd#if defined (FD_SET)
47421495Sjmacd  fd_set read_fds;
47521495Sjmacd
47621495Sjmacd  timeout.tv_sec = 15;
47721495Sjmacd  timeout.tv_usec = 0;
47821495Sjmacd
47921495Sjmacd  FD_ZERO (&read_fds);
48021495Sjmacd  FD_SET (fd, &read_fds);
48121495Sjmacd
48221495Sjmacd  select_result = select (fd + 1, fd_set_cast (&read_fds), 0, 0, &timeout);
48321495Sjmacd#else /* !FD_SET */
48421495Sjmacd  select_result = 1;
48521495Sjmacd#endif /* !FD_SET */
48621495Sjmacd
48721495Sjmacd  switch (select_result)
48821495Sjmacd    {
48921495Sjmacd    case 0:
49021495Sjmacd    case -1:
49121495Sjmacd      break;
49221495Sjmacd
49321495Sjmacd    default:
49421495Sjmacd      {
49521495Sjmacd        int amount_read;
49621495Sjmacd        int done = 0;
49721495Sjmacd
49821495Sjmacd        while (!done)
49921495Sjmacd          {
50021495Sjmacd            while ((bindex + 1024) > (bsize))
50121495Sjmacd              buffer = (char *)xrealloc (buffer, (bsize += 1024));
50221495Sjmacd            buffer[bindex] = '\0';
50321495Sjmacd
50421495Sjmacd            amount_read = read (fd, buffer + bindex, 1023);
50521495Sjmacd
50621495Sjmacd            if (amount_read < 0)
50721495Sjmacd              {
50821495Sjmacd                done = 1;
50921495Sjmacd              }
51021495Sjmacd            else
51121495Sjmacd              {
51221495Sjmacd                bindex += amount_read;
51321495Sjmacd                buffer[bindex] = '\0';
51421495Sjmacd                if (amount_read == 0)
51521495Sjmacd                  done = 1;
51621495Sjmacd              }
51721495Sjmacd          }
51821495Sjmacd      }
51921495Sjmacd    }
52021495Sjmacd
52121495Sjmacd  if ((buffer != (char *)NULL) && (*buffer == '\0'))
52221495Sjmacd    {
52321495Sjmacd      free (buffer);
52421495Sjmacd      buffer = (char *)NULL;
52521495Sjmacd    }
52621495Sjmacd
52721495Sjmacd  return (buffer);
52821495Sjmacd}
52921495Sjmacd
53021495Sjmacdstatic char *reference_section_starters[] =
53121495Sjmacd{
53221495Sjmacd  "\nRELATED INFORMATION",
53321495Sjmacd  "\nRELATED\tINFORMATION",
53421495Sjmacd  "RELATED INFORMATION\n",
53521495Sjmacd  "RELATED\tINFORMATION\n",
53621495Sjmacd  "\nSEE ALSO",
53721495Sjmacd  "\nSEE\tALSO",
53821495Sjmacd  "SEE ALSO\n",
53921495Sjmacd  "SEE\tALSO\n",
54021495Sjmacd  (char *)NULL
54121495Sjmacd};
54221495Sjmacd
54321495Sjmacdstatic SEARCH_BINDING frs_binding;
54421495Sjmacd
54521495Sjmacdstatic SEARCH_BINDING *
546146515Srufind_reference_section (NODE *node)
54721495Sjmacd{
54821495Sjmacd  register int i;
54921495Sjmacd  long position = -1;
55021495Sjmacd
55121495Sjmacd  frs_binding.buffer = node->contents;
55221495Sjmacd  frs_binding.start = 0;
55321495Sjmacd  frs_binding.end = node->nodelen;
55421495Sjmacd  frs_binding.flags = S_SkipDest;
55521495Sjmacd
55621495Sjmacd  for (i = 0; reference_section_starters[i] != (char *)NULL; i++)
55721495Sjmacd    {
55821495Sjmacd      position = search_forward (reference_section_starters[i], &frs_binding);
55921495Sjmacd      if (position != -1)
56042660Smarkm        break;
56121495Sjmacd    }
56221495Sjmacd
56321495Sjmacd  if (position == -1)
56421495Sjmacd    return ((SEARCH_BINDING *)NULL);
56521495Sjmacd
56621495Sjmacd  /* We found the start of the reference section, and point is right after
56721495Sjmacd     the string which starts it.  The text from here to the next header
56821495Sjmacd     (or end of buffer) contains the only references in this manpage. */
56921495Sjmacd  frs_binding.start = position;
57021495Sjmacd
57121495Sjmacd  for (i = frs_binding.start; i < frs_binding.end - 2; i++)
57221495Sjmacd    {
57321495Sjmacd      if ((frs_binding.buffer[i] == '\n') &&
57442660Smarkm          (!whitespace (frs_binding.buffer[i + 1])))
57542660Smarkm        {
57642660Smarkm          frs_binding.end = i;
57742660Smarkm          break;
57842660Smarkm        }
57921495Sjmacd    }
58021495Sjmacd
58121495Sjmacd  return (&frs_binding);
58221495Sjmacd}
58321495Sjmacd
58421495SjmacdREFERENCE **
585146515Sruxrefs_of_manpage (NODE *node)
58621495Sjmacd{
58721495Sjmacd  SEARCH_BINDING *reference_section;
58821495Sjmacd  REFERENCE **refs = (REFERENCE **)NULL;
58921495Sjmacd  int refs_index = 0;
59021495Sjmacd  int refs_slots = 0;
59121495Sjmacd  long position;
59221495Sjmacd
59321495Sjmacd  reference_section = find_reference_section (node);
59421495Sjmacd
59521495Sjmacd  if (reference_section == (SEARCH_BINDING *)NULL)
59621495Sjmacd    return ((REFERENCE **)NULL);
59721495Sjmacd
59821495Sjmacd  /* Grovel the reference section building a list of references found there.
59921495Sjmacd     A reference is alphabetic characters followed by non-whitespace text
60021495Sjmacd     within parenthesis. */
60121495Sjmacd  reference_section->flags = 0;
60221495Sjmacd
60321495Sjmacd  while ((position = search_forward ("(", reference_section)) != -1)
60421495Sjmacd    {
60521495Sjmacd      register int start, end;
60621495Sjmacd
60721495Sjmacd      for (start = position; start > reference_section->start; start--)
60842660Smarkm        if (whitespace (reference_section->buffer[start]))
60942660Smarkm          break;
61021495Sjmacd
61121495Sjmacd      start++;
61221495Sjmacd
61321495Sjmacd      for (end = position; end < reference_section->end; end++)
61442660Smarkm        {
61542660Smarkm          if (whitespace (reference_section->buffer[end]))
61642660Smarkm            {
61742660Smarkm              end = start;
61842660Smarkm              break;
61942660Smarkm            }
62021495Sjmacd
62142660Smarkm          if (reference_section->buffer[end] == ')')
62242660Smarkm            {
62342660Smarkm              end++;
62442660Smarkm              break;
62542660Smarkm            }
62642660Smarkm        }
62721495Sjmacd
62821495Sjmacd      if (end != start)
62942660Smarkm        {
63042660Smarkm          REFERENCE *entry;
63142660Smarkm          int len = end - start;
63221495Sjmacd
63342660Smarkm          entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
63442660Smarkm          entry->label = (char *)xmalloc (1 + len);
63542660Smarkm          strncpy (entry->label, (reference_section->buffer) + start, len);
63642660Smarkm          entry->label[len] = '\0';
63742660Smarkm          entry->filename = xstrdup (node->filename);
63842660Smarkm          entry->nodename = xstrdup (entry->label);
63942660Smarkm          entry->start = start;
64042660Smarkm          entry->end = end;
64121495Sjmacd
64242660Smarkm          add_pointer_to_array
64342660Smarkm            (entry, refs_index, refs, refs_slots, 10, REFERENCE *);
64442660Smarkm        }
64521495Sjmacd
64621495Sjmacd      reference_section->start = position + 1;
64721495Sjmacd    }
64821495Sjmacd
64921495Sjmacd  return (refs);
65021495Sjmacd}
65121495Sjmacd
65221495Sjmacdlong
653146515Srulocate_manpage_xref (NODE *node, long int start, int dir)
65421495Sjmacd{
65521495Sjmacd  REFERENCE **refs;
65621495Sjmacd  long position = -1;
65721495Sjmacd
65821495Sjmacd  refs = xrefs_of_manpage (node);
65921495Sjmacd
66021495Sjmacd  if (refs)
66121495Sjmacd    {
66221495Sjmacd      register int i, count;
66321495Sjmacd      REFERENCE *entry;
66421495Sjmacd
66521495Sjmacd      for (i = 0; refs[i]; i++);
66621495Sjmacd      count = i;
66721495Sjmacd
66821495Sjmacd      if (dir > 0)
66942660Smarkm        {
67042660Smarkm          for (i = 0; (entry = refs[i]); i++)
67142660Smarkm            if (entry->start > start)
67242660Smarkm              {
67342660Smarkm                position = entry->start;
67442660Smarkm                break;
67542660Smarkm              }
67642660Smarkm        }
67721495Sjmacd      else
67842660Smarkm        {
67942660Smarkm          for (i = count - 1; i > -1; i--)
68042660Smarkm            {
68142660Smarkm              entry = refs[i];
68221495Sjmacd
68342660Smarkm              if (entry->start < start)
68442660Smarkm                {
68542660Smarkm                  position = entry->start;
68642660Smarkm                  break;
68742660Smarkm                }
68842660Smarkm            }
68942660Smarkm        }
69021495Sjmacd
69121495Sjmacd      info_free_references (refs);
69221495Sjmacd    }
69321495Sjmacd  return (position);
69421495Sjmacd}
69521495Sjmacd
69621495Sjmacd/* This one was a little tricky.  The binding buffer that is passed in has
69721495Sjmacd   a START and END value of 0 -- strlen (window-line-containing-point).
69821495Sjmacd   The BUFFER is a pointer to the start of that line. */
69921495SjmacdREFERENCE **
700146515Srumanpage_xrefs_in_binding (NODE *node, SEARCH_BINDING *binding)
70121495Sjmacd{
70221495Sjmacd  register int i;
70321495Sjmacd  REFERENCE **all_refs = xrefs_of_manpage (node);
70421495Sjmacd  REFERENCE **brefs = (REFERENCE **)NULL;
70521495Sjmacd  REFERENCE *entry;
70621495Sjmacd  int brefs_index = 0;
70721495Sjmacd  int brefs_slots = 0;
70821495Sjmacd  int start, end;
70921495Sjmacd
71021495Sjmacd  if (!all_refs)
71121495Sjmacd    return ((REFERENCE **)NULL);
71221495Sjmacd
71321495Sjmacd  start = binding->start + (binding->buffer - node->contents);
71421495Sjmacd  end = binding->end + (binding->buffer - node->contents);
71521495Sjmacd
71642660Smarkm  for (i = 0; (entry = all_refs[i]); i++)
71721495Sjmacd    {
71821495Sjmacd      if ((entry->start > start) && (entry->end < end))
71942660Smarkm        {
72042660Smarkm          add_pointer_to_array
72142660Smarkm            (entry, brefs_index, brefs, brefs_slots, 10, REFERENCE *);
72242660Smarkm        }
72321495Sjmacd      else
72442660Smarkm        {
72542660Smarkm          maybe_free (entry->label);
72642660Smarkm          maybe_free (entry->filename);
72742660Smarkm          maybe_free (entry->nodename);
72842660Smarkm          free (entry);
72942660Smarkm        }
73021495Sjmacd    }
73121495Sjmacd
73221495Sjmacd  free (all_refs);
73321495Sjmacd  return (brefs);
73421495Sjmacd}
735