Home | History | Annotate | Line # | Download | only in info
      1  1.1  christos /*	$NetBSD: man.c,v 1.1.1.1 2016/01/14 00:11:29 christos Exp $	*/
      2  1.1  christos 
      3  1.1  christos /*  man.c: How to read and format man files.
      4  1.1  christos     Id: man.c,v 1.4 2004/04/11 17:56:46 karl Exp
      5  1.1  christos 
      6  1.1  christos    Copyright (C) 1995, 1997, 1998, 1999, 2000, 2002, 2003, 2004 Free Software
      7  1.1  christos    Foundation, Inc.
      8  1.1  christos 
      9  1.1  christos    This program is free software; you can redistribute it and/or modify
     10  1.1  christos    it under the terms of the GNU General Public License as published by
     11  1.1  christos    the Free Software Foundation; either version 2, or (at your option)
     12  1.1  christos    any later version.
     13  1.1  christos 
     14  1.1  christos    This program is distributed in the hope that it will be useful,
     15  1.1  christos    but WITHOUT ANY WARRANTY; without even the implied warranty of
     16  1.1  christos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17  1.1  christos    GNU General Public License for more details.
     18  1.1  christos 
     19  1.1  christos    You should have received a copy of the GNU General Public License
     20  1.1  christos    along with this program; if not, write to the Free Software
     21  1.1  christos    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
     22  1.1  christos 
     23  1.1  christos    Written by Brian Fox Thu May  4 09:17:52 1995 (bfox (at) ai.mit.edu). */
     24  1.1  christos 
     25  1.1  christos #include "info.h"
     26  1.1  christos #include <sys/ioctl.h>
     27  1.1  christos #include "signals.h"
     28  1.1  christos #if defined (HAVE_SYS_TIME_H)
     29  1.1  christos #include <sys/time.h>
     30  1.1  christos #endif
     31  1.1  christos #if defined (HAVE_SYS_WAIT_H)
     32  1.1  christos #include <sys/wait.h>
     33  1.1  christos #endif
     34  1.1  christos 
     35  1.1  christos #include "tilde.h"
     36  1.1  christos #include "man.h"
     37  1.1  christos 
     38  1.1  christos #if !defined (_POSIX_VERSION)
     39  1.1  christos #define pid_t int
     40  1.1  christos #endif
     41  1.1  christos 
     42  1.1  christos #if defined (FD_SET)
     43  1.1  christos #  if defined (hpux)
     44  1.1  christos #    define fd_set_cast(x) (int *)(x)
     45  1.1  christos #  else
     46  1.1  christos #    define fd_set_cast(x) (fd_set *)(x)
     47  1.1  christos #  endif /* !hpux */
     48  1.1  christos #endif /* FD_SET */
     49  1.1  christos 
     50  1.1  christos #if STRIP_DOT_EXE
     51  1.1  christos static char const * const exec_extensions[] = {
     52  1.1  christos   ".exe", ".com", ".bat", ".btm", ".sh", ".ksh", ".pl", ".sed", "", NULL
     53  1.1  christos };
     54  1.1  christos #else
     55  1.1  christos static char const * const exec_extensions[] = { "", NULL };
     56  1.1  christos #endif
     57  1.1  christos 
     58  1.1  christos static char *read_from_fd (int fd);
     59  1.1  christos static void clean_manpage (char *manpage);
     60  1.1  christos static NODE *manpage_node_of_file_buffer (FILE_BUFFER *file_buffer,
     61  1.1  christos     char *pagename);
     62  1.1  christos static char *get_manpage_contents (char *pagename);
     63  1.1  christos 
     64  1.1  christos NODE *
     65  1.1  christos make_manpage_node (char *pagename)
     66  1.1  christos {
     67  1.1  christos   return (info_get_node (MANPAGE_FILE_BUFFER_NAME, pagename));
     68  1.1  christos }
     69  1.1  christos 
     70  1.1  christos NODE *
     71  1.1  christos get_manpage_node (FILE_BUFFER *file_buffer, char *pagename)
     72  1.1  christos {
     73  1.1  christos   NODE *node;
     74  1.1  christos 
     75  1.1  christos   node = manpage_node_of_file_buffer (file_buffer, pagename);
     76  1.1  christos 
     77  1.1  christos   if (!node)
     78  1.1  christos     {
     79  1.1  christos       char *page;
     80  1.1  christos 
     81  1.1  christos       page = get_manpage_contents (pagename);
     82  1.1  christos 
     83  1.1  christos       if (page)
     84  1.1  christos         {
     85  1.1  christos           char header[1024];
     86  1.1  christos           long oldsize, newsize;
     87  1.1  christos           int hlen, plen;
     88  1.1  christos 	  char *old_contents = file_buffer->contents;
     89  1.1  christos 
     90  1.1  christos           sprintf (header, "\n\n%c\n%s %s,  %s %s,  %s (dir)\n\n",
     91  1.1  christos                    INFO_COOKIE,
     92  1.1  christos                    INFO_FILE_LABEL, file_buffer->filename,
     93  1.1  christos                    INFO_NODE_LABEL, pagename,
     94  1.1  christos                    INFO_UP_LABEL);
     95  1.1  christos           oldsize = file_buffer->filesize;
     96  1.1  christos           hlen = strlen (header);
     97  1.1  christos           plen = strlen (page);
     98  1.1  christos           newsize = (oldsize + hlen + plen);
     99  1.1  christos           file_buffer->contents =
    100  1.1  christos             (char *)xrealloc (file_buffer->contents, 1 + newsize);
    101  1.1  christos           memcpy (file_buffer->contents + oldsize, header, hlen);
    102  1.1  christos           memcpy (file_buffer->contents + oldsize + hlen, page, plen);
    103  1.1  christos           file_buffer->contents[newsize] = '\0';
    104  1.1  christos           file_buffer->filesize = newsize;
    105  1.1  christos           file_buffer->finfo.st_size = newsize;
    106  1.1  christos           build_tags_and_nodes (file_buffer);
    107  1.1  christos           free (page);
    108  1.1  christos 	  /* We have just relocated file_buffer->contents from under
    109  1.1  christos 	     the feet of info_windows[] array.  Therefore, all the
    110  1.1  christos 	     nodes on that list which are showing man pages have their
    111  1.1  christos 	     contents member pointing into the blue.  Undo that harm.  */
    112  1.1  christos 	  if (old_contents && oldsize && old_contents != file_buffer->contents)
    113  1.1  christos 	    {
    114  1.1  christos 	      int iw;
    115  1.1  christos 	      INFO_WINDOW *info_win;
    116  1.1  christos 	      char *old_contents_end = old_contents + oldsize;
    117  1.1  christos 
    118  1.1  christos 	      for (iw = 0; (info_win = info_windows[iw]); iw++)
    119  1.1  christos 		{
    120  1.1  christos 		  int in;
    121  1.1  christos 
    122  1.1  christos 		  for (in = 0; in < info_win->nodes_index; in++)
    123  1.1  christos 		    {
    124  1.1  christos 		      NODE *tmp_node = info_win->nodes[in];
    125  1.1  christos 
    126  1.1  christos 		      /* It really only suffices to see that node->filename
    127  1.1  christos 			 is "*manpages*".  But after several hours of
    128  1.1  christos 			 debugging this, would you blame me for being a bit
    129  1.1  christos 			 paranoid?  */
    130  1.1  christos 		      if (tmp_node && tmp_node->filename
    131  1.1  christos                           && tmp_node->contents
    132  1.1  christos                           && strcmp (tmp_node->filename,
    133  1.1  christos 				  MANPAGE_FILE_BUFFER_NAME) == 0
    134  1.1  christos                           && tmp_node->contents >= old_contents
    135  1.1  christos                           && tmp_node->contents + tmp_node->nodelen
    136  1.1  christos                                 <= old_contents_end)
    137  1.1  christos 			{
    138  1.1  christos 			  info_win->nodes[in] =
    139  1.1  christos 			    manpage_node_of_file_buffer (file_buffer,
    140  1.1  christos                                 tmp_node->nodename);
    141  1.1  christos 			  free (tmp_node->nodename);
    142  1.1  christos 			  free (tmp_node);
    143  1.1  christos 			}
    144  1.1  christos 		    }
    145  1.1  christos 		}
    146  1.1  christos 	    }
    147  1.1  christos         }
    148  1.1  christos 
    149  1.1  christos       node = manpage_node_of_file_buffer (file_buffer, pagename);
    150  1.1  christos     }
    151  1.1  christos 
    152  1.1  christos   return (node);
    153  1.1  christos }
    154  1.1  christos 
    155  1.1  christos FILE_BUFFER *
    156  1.1  christos create_manpage_file_buffer (void)
    157  1.1  christos {
    158  1.1  christos   FILE_BUFFER *file_buffer = make_file_buffer ();
    159  1.1  christos   file_buffer->filename = xstrdup (MANPAGE_FILE_BUFFER_NAME);
    160  1.1  christos   file_buffer->fullpath = xstrdup (MANPAGE_FILE_BUFFER_NAME);
    161  1.1  christos   file_buffer->finfo.st_size = 0;
    162  1.1  christos   file_buffer->filesize = 0;
    163  1.1  christos   file_buffer->contents = (char *)NULL;
    164  1.1  christos   file_buffer->flags = (N_IsInternal | N_CannotGC | N_IsManPage);
    165  1.1  christos 
    166  1.1  christos   return (file_buffer);
    167  1.1  christos }
    168  1.1  christos 
    169  1.1  christos /* Scan the list of directories in PATH looking for FILENAME.  If we find
    170  1.1  christos    one that is an executable file, return it as a new string.  Otherwise,
    171  1.1  christos    return a NULL pointer. */
    172  1.1  christos static char *
    173  1.1  christos executable_file_in_path (char *filename, char *path)
    174  1.1  christos {
    175  1.1  christos   struct stat finfo;
    176  1.1  christos   char *temp_dirname;
    177  1.1  christos   int statable, dirname_index;
    178  1.1  christos 
    179  1.1  christos   dirname_index = 0;
    180  1.1  christos 
    181  1.1  christos   while ((temp_dirname = extract_colon_unit (path, &dirname_index)))
    182  1.1  christos     {
    183  1.1  christos       char *temp;
    184  1.1  christos       char *temp_end;
    185  1.1  christos       int i;
    186  1.1  christos 
    187  1.1  christos       /* Expand a leading tilde if one is present. */
    188  1.1  christos       if (*temp_dirname == '~')
    189  1.1  christos         {
    190  1.1  christos           char *expanded_dirname;
    191  1.1  christos 
    192  1.1  christos           expanded_dirname = tilde_expand_word (temp_dirname);
    193  1.1  christos           free (temp_dirname);
    194  1.1  christos           temp_dirname = expanded_dirname;
    195  1.1  christos         }
    196  1.1  christos 
    197  1.1  christos       temp = (char *)xmalloc (34 + strlen (temp_dirname) + strlen (filename));
    198  1.1  christos       strcpy (temp, temp_dirname);
    199  1.1  christos       if (!IS_SLASH (temp[(strlen (temp)) - 1]))
    200  1.1  christos         strcat (temp, "/");
    201  1.1  christos       strcat (temp, filename);
    202  1.1  christos       temp_end = temp + strlen (temp);
    203  1.1  christos 
    204  1.1  christos       free (temp_dirname);
    205  1.1  christos 
    206  1.1  christos       /* Look for FILENAME, possibly with any of the extensions
    207  1.1  christos 	 in EXEC_EXTENSIONS[].  */
    208  1.1  christos       for (i = 0; exec_extensions[i]; i++)
    209  1.1  christos 	{
    210  1.1  christos 	  if (exec_extensions[i][0])
    211  1.1  christos 	    strcpy (temp_end, exec_extensions[i]);
    212  1.1  christos 	  statable = (stat (temp, &finfo) == 0);
    213  1.1  christos 
    214  1.1  christos 	  /* If we have found a regular executable file, then use it. */
    215  1.1  christos 	  if ((statable) && (S_ISREG (finfo.st_mode)) &&
    216  1.1  christos 	      (access (temp, X_OK) == 0))
    217  1.1  christos 	    return (temp);
    218  1.1  christos 	}
    219  1.1  christos 
    220  1.1  christos       free (temp);
    221  1.1  christos     }
    222  1.1  christos   return ((char *)NULL);
    223  1.1  christos }
    224  1.1  christos 
    225  1.1  christos /* Return the full pathname of the system man page formatter. */
    226  1.1  christos static char *
    227  1.1  christos find_man_formatter (void)
    228  1.1  christos {
    229  1.1  christos   return (executable_file_in_path ("man", (char *)getenv ("PATH")));
    230  1.1  christos }
    231  1.1  christos 
    232  1.1  christos static char *manpage_pagename = (char *)NULL;
    233  1.1  christos static char *manpage_section  = (char *)NULL;
    234  1.1  christos 
    235  1.1  christos static void
    236  1.1  christos get_page_and_section (char *pagename)
    237  1.1  christos {
    238  1.1  christos   register int i;
    239  1.1  christos 
    240  1.1  christos   if (manpage_pagename)
    241  1.1  christos     free (manpage_pagename);
    242  1.1  christos 
    243  1.1  christos   if (manpage_section)
    244  1.1  christos     free (manpage_section);
    245  1.1  christos 
    246  1.1  christos   manpage_pagename = (char *)NULL;
    247  1.1  christos   manpage_section  = (char *)NULL;
    248  1.1  christos 
    249  1.1  christos   for (i = 0; pagename[i] != '\0' && pagename[i] != '('; i++);
    250  1.1  christos 
    251  1.1  christos   manpage_pagename = (char *)xmalloc (1 + i);
    252  1.1  christos   strncpy (manpage_pagename, pagename, i);
    253  1.1  christos   manpage_pagename[i] = '\0';
    254  1.1  christos 
    255  1.1  christos   if (pagename[i] == '(')
    256  1.1  christos     {
    257  1.1  christos       int start;
    258  1.1  christos 
    259  1.1  christos       start = i + 1;
    260  1.1  christos 
    261  1.1  christos       for (i = start; pagename[i] != '\0' && pagename[i] != ')'; i++);
    262  1.1  christos 
    263  1.1  christos       manpage_section = (char *)xmalloc (1 + (i - start));
    264  1.1  christos       strncpy (manpage_section, pagename + start, (i - start));
    265  1.1  christos       manpage_section[i - start] = '\0';
    266  1.1  christos     }
    267  1.1  christos }
    268  1.1  christos 
    269  1.1  christos #if PIPE_USE_FORK
    270  1.1  christos static void
    271  1.1  christos reap_children (int sig)
    272  1.1  christos {
    273  1.1  christos   wait (NULL);
    274  1.1  christos }
    275  1.1  christos #endif
    276  1.1  christos 
    277  1.1  christos static char *
    278  1.1  christos get_manpage_contents (char *pagename)
    279  1.1  christos {
    280  1.1  christos   static char *formatter_args[4] = { (char *)NULL };
    281  1.1  christos   int pipes[2];
    282  1.1  christos   pid_t child;
    283  1.1  christos   RETSIGTYPE (*sigsave) (int signum);
    284  1.1  christos   char *formatted_page = NULL;
    285  1.1  christos   int arg_index = 1;
    286  1.1  christos 
    287  1.1  christos   if (formatter_args[0] == (char *)NULL)
    288  1.1  christos     formatter_args[0] = find_man_formatter ();
    289  1.1  christos 
    290  1.1  christos   if (formatter_args[0] == (char *)NULL)
    291  1.1  christos     return ((char *)NULL);
    292  1.1  christos 
    293  1.1  christos   get_page_and_section (pagename);
    294  1.1  christos 
    295  1.1  christos   if (manpage_section != (char *)NULL)
    296  1.1  christos     formatter_args[arg_index++] = manpage_section;
    297  1.1  christos 
    298  1.1  christos   formatter_args[arg_index++] = manpage_pagename;
    299  1.1  christos   formatter_args[arg_index] = (char *)NULL;
    300  1.1  christos 
    301  1.1  christos   /* Open a pipe to this program, read the output, and save it away
    302  1.1  christos      in FORMATTED_PAGE.  The reader end of the pipe is pipes[0]; the
    303  1.1  christos      writer end is pipes[1]. */
    304  1.1  christos #if PIPE_USE_FORK
    305  1.1  christos   pipe (pipes);
    306  1.1  christos 
    307  1.1  christos   sigsave = signal (SIGCHLD, reap_children);
    308  1.1  christos 
    309  1.1  christos   child = fork ();
    310  1.1  christos   if (child == -1)
    311  1.1  christos     return ((char *)NULL);
    312  1.1  christos 
    313  1.1  christos   if (child != 0)
    314  1.1  christos     {
    315  1.1  christos       /* In the parent, close the writing end of the pipe, and read from
    316  1.1  christos          the exec'd child. */
    317  1.1  christos       close (pipes[1]);
    318  1.1  christos       formatted_page = read_from_fd (pipes[0]);
    319  1.1  christos       close (pipes[0]);
    320  1.1  christos       signal (SIGCHLD, sigsave);
    321  1.1  christos     }
    322  1.1  christos   else
    323  1.1  christos     { /* In the child, close the read end of the pipe, make the write end
    324  1.1  christos          of the pipe be stdout, and execute the man page formatter. */
    325  1.1  christos       close (pipes[0]);
    326  1.1  christos       freopen (NULL_DEVICE, "w", stderr);
    327  1.1  christos       freopen (NULL_DEVICE, "r", stdin);
    328  1.1  christos       dup2 (pipes[1], fileno (stdout));
    329  1.1  christos 
    330  1.1  christos       execv (formatter_args[0], formatter_args);
    331  1.1  christos 
    332  1.1  christos       /* If we get here, we couldn't exec, so close out the pipe and
    333  1.1  christos          exit. */
    334  1.1  christos       close (pipes[1]);
    335  1.1  christos       xexit (0);
    336  1.1  christos     }
    337  1.1  christos #else  /* !PIPE_USE_FORK */
    338  1.1  christos   /* Cannot fork/exec, but can popen/pclose.  */
    339  1.1  christos   {
    340  1.1  christos     FILE *fpipe;
    341  1.1  christos     char *cmdline = xmalloc (strlen (formatter_args[0])
    342  1.1  christos 			     + strlen (manpage_pagename)
    343  1.1  christos 			     + (arg_index > 2 ? strlen (manpage_section) : 0)
    344  1.1  christos  			     + 3);
    345  1.1  christos     int save_stderr = dup (fileno (stderr));
    346  1.1  christos     int fd_err = open (NULL_DEVICE, O_WRONLY, 0666);
    347  1.1  christos 
    348  1.1  christos     if (fd_err > 2)
    349  1.1  christos       dup2 (fd_err, fileno (stderr)); /* Don't print errors. */
    350  1.1  christos     sprintf (cmdline, "%s %s %s", formatter_args[0], manpage_pagename,
    351  1.1  christos 				  arg_index > 2 ? manpage_section : "");
    352  1.1  christos     fpipe = popen (cmdline, "r");
    353  1.1  christos     free (cmdline);
    354  1.1  christos     if (fd_err > 2)
    355  1.1  christos       close (fd_err);
    356  1.1  christos     dup2 (save_stderr, fileno (stderr));
    357  1.1  christos     if (fpipe == 0)
    358  1.1  christos       return ((char *)NULL);
    359  1.1  christos     formatted_page = read_from_fd (fileno (fpipe));
    360  1.1  christos     if (pclose (fpipe) == -1)
    361  1.1  christos       {
    362  1.1  christos 	if (formatted_page)
    363  1.1  christos 	  free (formatted_page);
    364  1.1  christos 	return ((char *)NULL);
    365  1.1  christos       }
    366  1.1  christos   }
    367  1.1  christos #endif /* !PIPE_USE_FORK */
    368  1.1  christos 
    369  1.1  christos   /* If we have the page, then clean it up. */
    370  1.1  christos   if (formatted_page)
    371  1.1  christos     clean_manpage (formatted_page);
    372  1.1  christos 
    373  1.1  christos   return (formatted_page);
    374  1.1  christos }
    375  1.1  christos 
    376  1.1  christos static void
    377  1.1  christos clean_manpage (char *manpage)
    378  1.1  christos {
    379  1.1  christos   register int i, j;
    380  1.1  christos   int newline_count = 0;
    381  1.1  christos   char *newpage;
    382  1.1  christos 
    383  1.1  christos   newpage = (char *)xmalloc (1 + strlen (manpage));
    384  1.1  christos 
    385  1.1  christos   for (i = 0, j = 0; (newpage[j] = manpage[i]); i++, j++)
    386  1.1  christos     {
    387  1.1  christos       if (manpage[i] == '\n')
    388  1.1  christos         newline_count++;
    389  1.1  christos       else
    390  1.1  christos         newline_count = 0;
    391  1.1  christos 
    392  1.1  christos       if (newline_count == 3)
    393  1.1  christos         {
    394  1.1  christos           j--;
    395  1.1  christos           newline_count--;
    396  1.1  christos         }
    397  1.1  christos 
    398  1.1  christos       /* A malformed man page could have a \b as its first character,
    399  1.1  christos          in which case decrementing j by 2 will cause us to write into
    400  1.1  christos          newpage[-1], smashing the hidden info stored there by malloc.  */
    401  1.1  christos       if (manpage[i] == '\b' || (manpage[i] == '\f' && j > 0))
    402  1.1  christos         j -= 2;
    403  1.1  christos       else if (!raw_escapes_p)
    404  1.1  christos 	{
    405  1.1  christos 	  /* Remove the ANSI escape sequences for color, boldface,
    406  1.1  christos 	     underlining, and italics, generated by some versions of
    407  1.1  christos 	     Groff.  */
    408  1.1  christos 	  if (manpage[i] == '\033' && manpage[i + 1] == '['
    409  1.1  christos 	      && isdigit (manpage[i + 2]))
    410  1.1  christos 	    {
    411  1.1  christos 	      if (isdigit (manpage[i + 3]) && manpage[i + 4] == 'm')
    412  1.1  christos 		{
    413  1.1  christos 		  i += 4;
    414  1.1  christos 		  j--;
    415  1.1  christos 		}
    416  1.1  christos 	      else if (manpage[i + 3] == 'm')
    417  1.1  christos 		{
    418  1.1  christos 		  i += 3;
    419  1.1  christos 		  j--;
    420  1.1  christos 		}
    421  1.1  christos 	      /* Else do nothing: it's some unknown escape sequence,
    422  1.1  christos 		 so let's leave it alone.  */
    423  1.1  christos 	    }
    424  1.1  christos 	}
    425  1.1  christos     }
    426  1.1  christos 
    427  1.1  christos   newpage[j++] = 0;
    428  1.1  christos 
    429  1.1  christos   strcpy (manpage, newpage);
    430  1.1  christos   free (newpage);
    431  1.1  christos }
    432  1.1  christos 
    433  1.1  christos static NODE *
    434  1.1  christos manpage_node_of_file_buffer (FILE_BUFFER *file_buffer, char *pagename)
    435  1.1  christos {
    436  1.1  christos   NODE *node = (NODE *)NULL;
    437  1.1  christos   TAG *tag = (TAG *)NULL;
    438  1.1  christos 
    439  1.1  christos   if (file_buffer->contents)
    440  1.1  christos     {
    441  1.1  christos       register int i;
    442  1.1  christos 
    443  1.1  christos       for (i = 0; (tag = file_buffer->tags[i]); i++)
    444  1.1  christos         {
    445  1.1  christos           if (strcasecmp (pagename, tag->nodename) == 0)
    446  1.1  christos             break;
    447  1.1  christos         }
    448  1.1  christos     }
    449  1.1  christos 
    450  1.1  christos   if (tag)
    451  1.1  christos     {
    452  1.1  christos       node = (NODE *)xmalloc (sizeof (NODE));
    453  1.1  christos       node->filename = file_buffer->filename;
    454  1.1  christos       node->nodename = xstrdup (tag->nodename);
    455  1.1  christos       node->contents = file_buffer->contents + tag->nodestart;
    456  1.1  christos       node->nodelen = tag->nodelen;
    457  1.1  christos       node->flags    = 0;
    458  1.1  christos       node->display_pos = 0;
    459  1.1  christos       node->parent   = (char *)NULL;
    460  1.1  christos       node->flags = (N_HasTagsTable | N_IsManPage);
    461  1.1  christos       node->contents += skip_node_separator (node->contents);
    462  1.1  christos     }
    463  1.1  christos 
    464  1.1  christos   return (node);
    465  1.1  christos }
    466  1.1  christos 
    467  1.1  christos static char *
    468  1.1  christos read_from_fd (int fd)
    469  1.1  christos {
    470  1.1  christos   struct timeval timeout;
    471  1.1  christos   char *buffer = (char *)NULL;
    472  1.1  christos   int bsize = 0;
    473  1.1  christos   int bindex = 0;
    474  1.1  christos   int select_result;
    475  1.1  christos #if defined (FD_SET)
    476  1.1  christos   fd_set read_fds;
    477  1.1  christos 
    478  1.1  christos   timeout.tv_sec = 15;
    479  1.1  christos   timeout.tv_usec = 0;
    480  1.1  christos 
    481  1.1  christos   FD_ZERO (&read_fds);
    482  1.1  christos   FD_SET (fd, &read_fds);
    483  1.1  christos 
    484  1.1  christos   select_result = select (fd + 1, fd_set_cast (&read_fds), 0, 0, &timeout);
    485  1.1  christos #else /* !FD_SET */
    486  1.1  christos   select_result = 1;
    487  1.1  christos #endif /* !FD_SET */
    488  1.1  christos 
    489  1.1  christos   switch (select_result)
    490  1.1  christos     {
    491  1.1  christos     case 0:
    492  1.1  christos     case -1:
    493  1.1  christos       break;
    494  1.1  christos 
    495  1.1  christos     default:
    496  1.1  christos       {
    497  1.1  christos         int amount_read;
    498  1.1  christos         int done = 0;
    499  1.1  christos 
    500  1.1  christos         while (!done)
    501  1.1  christos           {
    502  1.1  christos             while ((bindex + 1024) > (bsize))
    503  1.1  christos               buffer = (char *)xrealloc (buffer, (bsize += 1024));
    504  1.1  christos             buffer[bindex] = '\0';
    505  1.1  christos 
    506  1.1  christos             amount_read = read (fd, buffer + bindex, 1023);
    507  1.1  christos 
    508  1.1  christos             if (amount_read < 0)
    509  1.1  christos               {
    510  1.1  christos                 done = 1;
    511  1.1  christos               }
    512  1.1  christos             else
    513  1.1  christos               {
    514  1.1  christos                 bindex += amount_read;
    515  1.1  christos                 buffer[bindex] = '\0';
    516  1.1  christos                 if (amount_read == 0)
    517  1.1  christos                   done = 1;
    518  1.1  christos               }
    519  1.1  christos           }
    520  1.1  christos       }
    521  1.1  christos     }
    522  1.1  christos 
    523  1.1  christos   if ((buffer != (char *)NULL) && (*buffer == '\0'))
    524  1.1  christos     {
    525  1.1  christos       free (buffer);
    526  1.1  christos       buffer = (char *)NULL;
    527  1.1  christos     }
    528  1.1  christos 
    529  1.1  christos   return (buffer);
    530  1.1  christos }
    531  1.1  christos 
    532  1.1  christos static char *reference_section_starters[] =
    533  1.1  christos {
    534  1.1  christos   "\nRELATED INFORMATION",
    535  1.1  christos   "\nRELATED\tINFORMATION",
    536  1.1  christos   "RELATED INFORMATION\n",
    537  1.1  christos   "RELATED\tINFORMATION\n",
    538  1.1  christos   "\nSEE ALSO",
    539  1.1  christos   "\nSEE\tALSO",
    540  1.1  christos   "SEE ALSO\n",
    541  1.1  christos   "SEE\tALSO\n",
    542  1.1  christos   (char *)NULL
    543  1.1  christos };
    544  1.1  christos 
    545  1.1  christos static SEARCH_BINDING frs_binding;
    546  1.1  christos 
    547  1.1  christos static SEARCH_BINDING *
    548  1.1  christos find_reference_section (NODE *node)
    549  1.1  christos {
    550  1.1  christos   register int i;
    551  1.1  christos   long position = -1;
    552  1.1  christos 
    553  1.1  christos   frs_binding.buffer = node->contents;
    554  1.1  christos   frs_binding.start = 0;
    555  1.1  christos   frs_binding.end = node->nodelen;
    556  1.1  christos   frs_binding.flags = S_SkipDest;
    557  1.1  christos 
    558  1.1  christos   for (i = 0; reference_section_starters[i] != (char *)NULL; i++)
    559  1.1  christos     {
    560  1.1  christos       position = search_forward (reference_section_starters[i], &frs_binding);
    561  1.1  christos       if (position != -1)
    562  1.1  christos         break;
    563  1.1  christos     }
    564  1.1  christos 
    565  1.1  christos   if (position == -1)
    566  1.1  christos     return ((SEARCH_BINDING *)NULL);
    567  1.1  christos 
    568  1.1  christos   /* We found the start of the reference section, and point is right after
    569  1.1  christos      the string which starts it.  The text from here to the next header
    570  1.1  christos      (or end of buffer) contains the only references in this manpage. */
    571  1.1  christos   frs_binding.start = position;
    572  1.1  christos 
    573  1.1  christos   for (i = frs_binding.start; i < frs_binding.end - 2; i++)
    574  1.1  christos     {
    575  1.1  christos       if ((frs_binding.buffer[i] == '\n') &&
    576  1.1  christos           (!whitespace (frs_binding.buffer[i + 1])))
    577  1.1  christos         {
    578  1.1  christos           frs_binding.end = i;
    579  1.1  christos           break;
    580  1.1  christos         }
    581  1.1  christos     }
    582  1.1  christos 
    583  1.1  christos   return (&frs_binding);
    584  1.1  christos }
    585  1.1  christos 
    586  1.1  christos REFERENCE **
    587  1.1  christos xrefs_of_manpage (NODE *node)
    588  1.1  christos {
    589  1.1  christos   SEARCH_BINDING *reference_section;
    590  1.1  christos   REFERENCE **refs = (REFERENCE **)NULL;
    591  1.1  christos   int refs_index = 0;
    592  1.1  christos   int refs_slots = 0;
    593  1.1  christos   long position;
    594  1.1  christos 
    595  1.1  christos   reference_section = find_reference_section (node);
    596  1.1  christos 
    597  1.1  christos   if (reference_section == (SEARCH_BINDING *)NULL)
    598  1.1  christos     return ((REFERENCE **)NULL);
    599  1.1  christos 
    600  1.1  christos   /* Grovel the reference section building a list of references found there.
    601  1.1  christos      A reference is alphabetic characters followed by non-whitespace text
    602  1.1  christos      within parenthesis. */
    603  1.1  christos   reference_section->flags = 0;
    604  1.1  christos 
    605  1.1  christos   while ((position = search_forward ("(", reference_section)) != -1)
    606  1.1  christos     {
    607  1.1  christos       register int start, end;
    608  1.1  christos 
    609  1.1  christos       for (start = position; start > reference_section->start; start--)
    610  1.1  christos         if (whitespace (reference_section->buffer[start]))
    611  1.1  christos           break;
    612  1.1  christos 
    613  1.1  christos       start++;
    614  1.1  christos 
    615  1.1  christos       for (end = position; end < reference_section->end; end++)
    616  1.1  christos         {
    617  1.1  christos           if (whitespace (reference_section->buffer[end]))
    618  1.1  christos             {
    619  1.1  christos               end = start;
    620  1.1  christos               break;
    621  1.1  christos             }
    622  1.1  christos 
    623  1.1  christos           if (reference_section->buffer[end] == ')')
    624  1.1  christos             {
    625  1.1  christos               end++;
    626  1.1  christos               break;
    627  1.1  christos             }
    628  1.1  christos         }
    629  1.1  christos 
    630  1.1  christos       if (end != start)
    631  1.1  christos         {
    632  1.1  christos           REFERENCE *entry;
    633  1.1  christos           int len = end - start;
    634  1.1  christos 
    635  1.1  christos           entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
    636  1.1  christos           entry->label = (char *)xmalloc (1 + len);
    637  1.1  christos           strncpy (entry->label, (reference_section->buffer) + start, len);
    638  1.1  christos           entry->label[len] = '\0';
    639  1.1  christos           entry->filename = xstrdup (node->filename);
    640  1.1  christos           entry->nodename = xstrdup (entry->label);
    641  1.1  christos           entry->start = start;
    642  1.1  christos           entry->end = end;
    643  1.1  christos 
    644  1.1  christos           add_pointer_to_array
    645  1.1  christos             (entry, refs_index, refs, refs_slots, 10, REFERENCE *);
    646  1.1  christos         }
    647  1.1  christos 
    648  1.1  christos       reference_section->start = position + 1;
    649  1.1  christos     }
    650  1.1  christos 
    651  1.1  christos   return (refs);
    652  1.1  christos }
    653  1.1  christos 
    654  1.1  christos long
    655  1.1  christos locate_manpage_xref (NODE *node, long int start, int dir)
    656  1.1  christos {
    657  1.1  christos   REFERENCE **refs;
    658  1.1  christos   long position = -1;
    659  1.1  christos 
    660  1.1  christos   refs = xrefs_of_manpage (node);
    661  1.1  christos 
    662  1.1  christos   if (refs)
    663  1.1  christos     {
    664  1.1  christos       register int i, count;
    665  1.1  christos       REFERENCE *entry;
    666  1.1  christos 
    667  1.1  christos       for (i = 0; refs[i]; i++);
    668  1.1  christos       count = i;
    669  1.1  christos 
    670  1.1  christos       if (dir > 0)
    671  1.1  christos         {
    672  1.1  christos           for (i = 0; (entry = refs[i]); i++)
    673  1.1  christos             if (entry->start > start)
    674  1.1  christos               {
    675  1.1  christos                 position = entry->start;
    676  1.1  christos                 break;
    677  1.1  christos               }
    678  1.1  christos         }
    679  1.1  christos       else
    680  1.1  christos         {
    681  1.1  christos           for (i = count - 1; i > -1; i--)
    682  1.1  christos             {
    683  1.1  christos               entry = refs[i];
    684  1.1  christos 
    685  1.1  christos               if (entry->start < start)
    686  1.1  christos                 {
    687  1.1  christos                   position = entry->start;
    688  1.1  christos                   break;
    689  1.1  christos                 }
    690  1.1  christos             }
    691  1.1  christos         }
    692  1.1  christos 
    693  1.1  christos       info_free_references (refs);
    694  1.1  christos     }
    695  1.1  christos   return (position);
    696  1.1  christos }
    697  1.1  christos 
    698  1.1  christos /* This one was a little tricky.  The binding buffer that is passed in has
    699  1.1  christos    a START and END value of 0 -- strlen (window-line-containing-point).
    700  1.1  christos    The BUFFER is a pointer to the start of that line. */
    701  1.1  christos REFERENCE **
    702  1.1  christos manpage_xrefs_in_binding (NODE *node, SEARCH_BINDING *binding)
    703  1.1  christos {
    704  1.1  christos   register int i;
    705  1.1  christos   REFERENCE **all_refs = xrefs_of_manpage (node);
    706  1.1  christos   REFERENCE **brefs = (REFERENCE **)NULL;
    707  1.1  christos   REFERENCE *entry;
    708  1.1  christos   int brefs_index = 0;
    709  1.1  christos   int brefs_slots = 0;
    710  1.1  christos   int start, end;
    711  1.1  christos 
    712  1.1  christos   if (!all_refs)
    713  1.1  christos     return ((REFERENCE **)NULL);
    714  1.1  christos 
    715  1.1  christos   start = binding->start + (binding->buffer - node->contents);
    716  1.1  christos   end = binding->end + (binding->buffer - node->contents);
    717  1.1  christos 
    718  1.1  christos   for (i = 0; (entry = all_refs[i]); i++)
    719  1.1  christos     {
    720  1.1  christos       if ((entry->start > start) && (entry->end < end))
    721  1.1  christos         {
    722  1.1  christos           add_pointer_to_array
    723  1.1  christos             (entry, brefs_index, brefs, brefs_slots, 10, REFERENCE *);
    724  1.1  christos         }
    725  1.1  christos       else
    726  1.1  christos         {
    727  1.1  christos           maybe_free (entry->label);
    728  1.1  christos           maybe_free (entry->filename);
    729  1.1  christos           maybe_free (entry->nodename);
    730  1.1  christos           free (entry);
    731  1.1  christos         }
    732  1.1  christos     }
    733  1.1  christos 
    734  1.1  christos   free (all_refs);
    735  1.1  christos   return (brefs);
    736  1.1  christos }
    737