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