Back to index

tetex-bin  3.0
man.c
Go to the documentation of this file.
00001 /*  man.c: How to read and format man files.
00002     $Id: man.c,v 1.4 2004/04/11 17:56:46 karl Exp $
00003 
00004    Copyright (C) 1995, 1997, 1998, 1999, 2000, 2002, 2003, 2004 Free Software
00005    Foundation, Inc.
00006 
00007    This program is free software; you can redistribute it and/or modify
00008    it under the terms of the GNU General Public License as published by
00009    the Free Software Foundation; either version 2, or (at your option)
00010    any later version.
00011 
00012    This program is distributed in the hope that it will be useful,
00013    but WITHOUT ANY WARRANTY; without even the implied warranty of
00014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015    GNU General Public License for more details.
00016 
00017    You should have received a copy of the GNU General Public License
00018    along with this program; if not, write to the Free Software
00019    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00020 
00021    Written by Brian Fox Thu May  4 09:17:52 1995 (bfox@ai.mit.edu). */
00022 
00023 #include "info.h"
00024 #include <sys/ioctl.h>
00025 #include "signals.h"
00026 #if defined (HAVE_SYS_TIME_H)
00027 #include <sys/time.h>
00028 #endif
00029 #if defined (HAVE_SYS_WAIT_H)
00030 #include <sys/wait.h>
00031 #endif
00032 
00033 #include "tilde.h"
00034 #include "man.h"
00035 
00036 #if !defined (_POSIX_VERSION)
00037 #define pid_t int
00038 #endif
00039 
00040 #if defined (FD_SET)
00041 #  if defined (hpux)
00042 #    define fd_set_cast(x) (int *)(x)
00043 #  else
00044 #    define fd_set_cast(x) (fd_set *)(x)
00045 #  endif /* !hpux */
00046 #endif /* FD_SET */
00047 
00048 #if STRIP_DOT_EXE
00049 static char const * const exec_extensions[] = {
00050   ".exe", ".com", ".bat", ".btm", ".sh", ".ksh", ".pl", ".sed", "", NULL
00051 };
00052 #else
00053 static char const * const exec_extensions[] = { "", NULL };
00054 #endif
00055 
00056 static char *read_from_fd (int fd);
00057 static void clean_manpage (char *manpage);
00058 static NODE *manpage_node_of_file_buffer (FILE_BUFFER *file_buffer,
00059     char *pagename);
00060 static char *get_manpage_contents (char *pagename);
00061 
00062 NODE *
00063 make_manpage_node (char *pagename)
00064 {
00065   return (info_get_node (MANPAGE_FILE_BUFFER_NAME, pagename));
00066 }
00067 
00068 NODE *
00069 get_manpage_node (FILE_BUFFER *file_buffer, char *pagename)
00070 {
00071   NODE *node;
00072 
00073   node = manpage_node_of_file_buffer (file_buffer, pagename);
00074 
00075   if (!node)
00076     {
00077       char *page;
00078 
00079       page = get_manpage_contents (pagename);
00080 
00081       if (page)
00082         {
00083           char header[1024];
00084           long oldsize, newsize;
00085           int hlen, plen;
00086          char *old_contents = file_buffer->contents;
00087 
00088           sprintf (header, "\n\n%c\n%s %s,  %s %s,  %s (dir)\n\n",
00089                    INFO_COOKIE,
00090                    INFO_FILE_LABEL, file_buffer->filename,
00091                    INFO_NODE_LABEL, pagename,
00092                    INFO_UP_LABEL);
00093           oldsize = file_buffer->filesize;
00094           hlen = strlen (header);
00095           plen = strlen (page);
00096           newsize = (oldsize + hlen + plen);
00097           file_buffer->contents =
00098             (char *)xrealloc (file_buffer->contents, 1 + newsize);
00099           memcpy (file_buffer->contents + oldsize, header, hlen);
00100           memcpy (file_buffer->contents + oldsize + hlen, page, plen);
00101           file_buffer->contents[newsize] = '\0';
00102           file_buffer->filesize = newsize;
00103           file_buffer->finfo.st_size = newsize;
00104           build_tags_and_nodes (file_buffer);
00105           free (page);
00106          /* We have just relocated file_buffer->contents from under
00107             the feet of info_windows[] array.  Therefore, all the
00108             nodes on that list which are showing man pages have their
00109             contents member pointing into the blue.  Undo that harm.  */
00110          if (old_contents && oldsize && old_contents != file_buffer->contents)
00111            {
00112              int iw;
00113              INFO_WINDOW *info_win;
00114              char *old_contents_end = old_contents + oldsize;
00115 
00116              for (iw = 0; (info_win = info_windows[iw]); iw++)
00117               {
00118                 int in;
00119 
00120                 for (in = 0; in < info_win->nodes_index; in++)
00121                   {
00122                     NODE *tmp_node = info_win->nodes[in];
00123 
00124                     /* It really only suffices to see that node->filename
00125                       is "*manpages*".  But after several hours of
00126                       debugging this, would you blame me for being a bit
00127                       paranoid?  */
00128                     if (tmp_node && tmp_node->filename
00129                           && tmp_node->contents
00130                           && strcmp (tmp_node->filename,
00131                               MANPAGE_FILE_BUFFER_NAME) == 0
00132                           && tmp_node->contents >= old_contents
00133                           && tmp_node->contents + tmp_node->nodelen
00134                                 <= old_contents_end)
00135                      {
00136                        info_win->nodes[in] =
00137                          manpage_node_of_file_buffer (file_buffer,
00138                                 tmp_node->nodename);
00139                        free (tmp_node->nodename);
00140                        free (tmp_node);
00141                      }
00142                   }
00143               }
00144            }
00145         }
00146 
00147       node = manpage_node_of_file_buffer (file_buffer, pagename);
00148     }
00149 
00150   return (node);
00151 }
00152 
00153 FILE_BUFFER *
00154 create_manpage_file_buffer (void)
00155 {
00156   FILE_BUFFER *file_buffer = make_file_buffer ();
00157   file_buffer->filename = xstrdup (MANPAGE_FILE_BUFFER_NAME);
00158   file_buffer->fullpath = xstrdup (MANPAGE_FILE_BUFFER_NAME);
00159   file_buffer->finfo.st_size = 0;
00160   file_buffer->filesize = 0;
00161   file_buffer->contents = (char *)NULL;
00162   file_buffer->flags = (N_IsInternal | N_CannotGC | N_IsManPage);
00163 
00164   return (file_buffer);
00165 }
00166 
00167 /* Scan the list of directories in PATH looking for FILENAME.  If we find
00168    one that is an executable file, return it as a new string.  Otherwise,
00169    return a NULL pointer. */
00170 static char *
00171 executable_file_in_path (char *filename, char *path)
00172 {
00173   struct stat finfo;
00174   char *temp_dirname;
00175   int statable, dirname_index;
00176 
00177   dirname_index = 0;
00178 
00179   while ((temp_dirname = extract_colon_unit (path, &dirname_index)))
00180     {
00181       char *temp;
00182       char *temp_end;
00183       int i;
00184 
00185       /* Expand a leading tilde if one is present. */
00186       if (*temp_dirname == '~')
00187         {
00188           char *expanded_dirname;
00189 
00190           expanded_dirname = tilde_expand_word (temp_dirname);
00191           free (temp_dirname);
00192           temp_dirname = expanded_dirname;
00193         }
00194 
00195       temp = (char *)xmalloc (34 + strlen (temp_dirname) + strlen (filename));
00196       strcpy (temp, temp_dirname);
00197       if (!IS_SLASH (temp[(strlen (temp)) - 1]))
00198         strcat (temp, "/");
00199       strcat (temp, filename);
00200       temp_end = temp + strlen (temp);
00201 
00202       free (temp_dirname);
00203 
00204       /* Look for FILENAME, possibly with any of the extensions
00205         in EXEC_EXTENSIONS[].  */
00206       for (i = 0; exec_extensions[i]; i++)
00207        {
00208          if (exec_extensions[i][0])
00209            strcpy (temp_end, exec_extensions[i]);
00210          statable = (stat (temp, &finfo) == 0);
00211 
00212          /* If we have found a regular executable file, then use it. */
00213          if ((statable) && (S_ISREG (finfo.st_mode)) &&
00214              (access (temp, X_OK) == 0))
00215            return (temp);
00216        }
00217 
00218       free (temp);
00219     }
00220   return ((char *)NULL);
00221 }
00222 
00223 /* Return the full pathname of the system man page formatter. */
00224 static char *
00225 find_man_formatter (void)
00226 {
00227   return (executable_file_in_path ("man", (char *)getenv ("PATH")));
00228 }
00229 
00230 static char *manpage_pagename = (char *)NULL;
00231 static char *manpage_section  = (char *)NULL;
00232 
00233 static void
00234 get_page_and_section (char *pagename)
00235 {
00236   register int i;
00237 
00238   if (manpage_pagename)
00239     free (manpage_pagename);
00240 
00241   if (manpage_section)
00242     free (manpage_section);
00243 
00244   manpage_pagename = (char *)NULL;
00245   manpage_section  = (char *)NULL;
00246 
00247   for (i = 0; pagename[i] != '\0' && pagename[i] != '('; i++);
00248 
00249   manpage_pagename = (char *)xmalloc (1 + i);
00250   strncpy (manpage_pagename, pagename, i);
00251   manpage_pagename[i] = '\0';
00252 
00253   if (pagename[i] == '(')
00254     {
00255       int start;
00256 
00257       start = i + 1;
00258 
00259       for (i = start; pagename[i] != '\0' && pagename[i] != ')'; i++);
00260 
00261       manpage_section = (char *)xmalloc (1 + (i - start));
00262       strncpy (manpage_section, pagename + start, (i - start));
00263       manpage_section[i - start] = '\0';
00264     }
00265 }
00266 
00267 #if PIPE_USE_FORK
00268 static void
00269 reap_children (int sig)
00270 {
00271   wait (NULL);
00272 }
00273 #endif
00274 
00275 static char *
00276 get_manpage_contents (char *pagename)
00277 {
00278   static char *formatter_args[4] = { (char *)NULL };
00279   int pipes[2];
00280   pid_t child;
00281   RETSIGTYPE (*sigsave) (int signum);
00282   char *formatted_page = NULL;
00283   int arg_index = 1;
00284 
00285   if (formatter_args[0] == (char *)NULL)
00286     formatter_args[0] = find_man_formatter ();
00287 
00288   if (formatter_args[0] == (char *)NULL)
00289     return ((char *)NULL);
00290 
00291   get_page_and_section (pagename);
00292 
00293   if (manpage_section != (char *)NULL)
00294     formatter_args[arg_index++] = manpage_section;
00295 
00296   formatter_args[arg_index++] = manpage_pagename;
00297   formatter_args[arg_index] = (char *)NULL;
00298 
00299   /* Open a pipe to this program, read the output, and save it away
00300      in FORMATTED_PAGE.  The reader end of the pipe is pipes[0]; the
00301      writer end is pipes[1]. */
00302 #if PIPE_USE_FORK
00303   pipe (pipes);
00304 
00305   sigsave = signal (SIGCHLD, reap_children);
00306 
00307   child = fork ();
00308   if (child == -1)
00309     return ((char *)NULL);
00310 
00311   if (child != 0)
00312     {
00313       /* In the parent, close the writing end of the pipe, and read from
00314          the exec'd child. */
00315       close (pipes[1]);
00316       formatted_page = read_from_fd (pipes[0]);
00317       close (pipes[0]);
00318       signal (SIGCHLD, sigsave);
00319     }
00320   else
00321     { /* In the child, close the read end of the pipe, make the write end
00322          of the pipe be stdout, and execute the man page formatter. */
00323       close (pipes[0]);
00324       freopen (NULL_DEVICE, "w", stderr);
00325       freopen (NULL_DEVICE, "r", stdin);
00326       dup2 (pipes[1], fileno (stdout));
00327 
00328       execv (formatter_args[0], formatter_args);
00329 
00330       /* If we get here, we couldn't exec, so close out the pipe and
00331          exit. */
00332       close (pipes[1]);
00333       xexit (0);
00334     }
00335 #else  /* !PIPE_USE_FORK */
00336   /* Cannot fork/exec, but can popen/pclose.  */
00337   {
00338     FILE *fpipe;
00339     char *cmdline = xmalloc (strlen (formatter_args[0])
00340                           + strlen (manpage_pagename)
00341                           + (arg_index > 2 ? strlen (manpage_section) : 0)
00342                           + 3);
00343     int save_stderr = dup (fileno (stderr));
00344     int fd_err = open (NULL_DEVICE, O_WRONLY, 0666);
00345 
00346     if (fd_err > 2)
00347       dup2 (fd_err, fileno (stderr)); /* Don't print errors. */
00348     sprintf (cmdline, "%s %s %s", formatter_args[0], manpage_pagename,
00349                               arg_index > 2 ? manpage_section : "");
00350     fpipe = popen (cmdline, "r");
00351     free (cmdline);
00352     if (fd_err > 2)
00353       close (fd_err);
00354     dup2 (save_stderr, fileno (stderr));
00355     if (fpipe == 0)
00356       return ((char *)NULL);
00357     formatted_page = read_from_fd (fileno (fpipe));
00358     if (pclose (fpipe) == -1)
00359       {
00360        if (formatted_page)
00361          free (formatted_page);
00362        return ((char *)NULL);
00363       }
00364   }
00365 #endif /* !PIPE_USE_FORK */
00366 
00367   /* If we have the page, then clean it up. */
00368   if (formatted_page)
00369     clean_manpage (formatted_page);
00370 
00371   return (formatted_page);
00372 }
00373 
00374 static void
00375 clean_manpage (char *manpage)
00376 {
00377   register int i, j;
00378   int newline_count = 0;
00379   char *newpage;
00380 
00381   newpage = (char *)xmalloc (1 + strlen (manpage));
00382 
00383   for (i = 0, j = 0; (newpage[j] = manpage[i]); i++, j++)
00384     {
00385       if (manpage[i] == '\n')
00386         newline_count++;
00387       else
00388         newline_count = 0;
00389 
00390       if (newline_count == 3)
00391         {
00392           j--;
00393           newline_count--;
00394         }
00395 
00396       /* A malformed man page could have a \b as its first character,
00397          in which case decrementing j by 2 will cause us to write into
00398          newpage[-1], smashing the hidden info stored there by malloc.  */
00399       if (manpage[i] == '\b' || (manpage[i] == '\f' && j > 0))
00400         j -= 2;
00401       else if (!raw_escapes_p)
00402        {
00403          /* Remove the ANSI escape sequences for color, boldface,
00404             underlining, and italics, generated by some versions of
00405             Groff.  */
00406          if (manpage[i] == '\033' && manpage[i + 1] == '['
00407              && isdigit (manpage[i + 2]))
00408            {
00409              if (isdigit (manpage[i + 3]) && manpage[i + 4] == 'm')
00410               {
00411                 i += 4;
00412                 j--;
00413               }
00414              else if (manpage[i + 3] == 'm')
00415               {
00416                 i += 3;
00417                 j--;
00418               }
00419              /* Else do nothing: it's some unknown escape sequence,
00420                so let's leave it alone.  */
00421            }
00422        }
00423     }
00424 
00425   newpage[j++] = 0;
00426 
00427   strcpy (manpage, newpage);
00428   free (newpage);
00429 }
00430 
00431 static NODE *
00432 manpage_node_of_file_buffer (FILE_BUFFER *file_buffer, char *pagename)
00433 {
00434   NODE *node = (NODE *)NULL;
00435   TAG *tag = (TAG *)NULL;
00436 
00437   if (file_buffer->contents)
00438     {
00439       register int i;
00440 
00441       for (i = 0; (tag = file_buffer->tags[i]); i++)
00442         {
00443           if (strcasecmp (pagename, tag->nodename) == 0)
00444             break;
00445         }
00446     }
00447 
00448   if (tag)
00449     {
00450       node = (NODE *)xmalloc (sizeof (NODE));
00451       node->filename = file_buffer->filename;
00452       node->nodename = xstrdup (tag->nodename);
00453       node->contents = file_buffer->contents + tag->nodestart;
00454       node->nodelen = tag->nodelen;
00455       node->flags    = 0;
00456       node->display_pos = 0;
00457       node->parent   = (char *)NULL;
00458       node->flags = (N_HasTagsTable | N_IsManPage);
00459       node->contents += skip_node_separator (node->contents);
00460     }
00461 
00462   return (node);
00463 }
00464 
00465 static char *
00466 read_from_fd (int fd)
00467 {
00468   struct timeval timeout;
00469   char *buffer = (char *)NULL;
00470   int bsize = 0;
00471   int bindex = 0;
00472   int select_result;
00473 #if defined (FD_SET)
00474   fd_set read_fds;
00475 
00476   timeout.tv_sec = 15;
00477   timeout.tv_usec = 0;
00478 
00479   FD_ZERO (&read_fds);
00480   FD_SET (fd, &read_fds);
00481 
00482   select_result = select (fd + 1, fd_set_cast (&read_fds), 0, 0, &timeout);
00483 #else /* !FD_SET */
00484   select_result = 1;
00485 #endif /* !FD_SET */
00486 
00487   switch (select_result)
00488     {
00489     case 0:
00490     case -1:
00491       break;
00492 
00493     default:
00494       {
00495         int amount_read;
00496         int done = 0;
00497 
00498         while (!done)
00499           {
00500             while ((bindex + 1024) > (bsize))
00501               buffer = (char *)xrealloc (buffer, (bsize += 1024));
00502             buffer[bindex] = '\0';
00503 
00504             amount_read = read (fd, buffer + bindex, 1023);
00505 
00506             if (amount_read < 0)
00507               {
00508                 done = 1;
00509               }
00510             else
00511               {
00512                 bindex += amount_read;
00513                 buffer[bindex] = '\0';
00514                 if (amount_read == 0)
00515                   done = 1;
00516               }
00517           }
00518       }
00519     }
00520 
00521   if ((buffer != (char *)NULL) && (*buffer == '\0'))
00522     {
00523       free (buffer);
00524       buffer = (char *)NULL;
00525     }
00526 
00527   return (buffer);
00528 }
00529 
00530 static char *reference_section_starters[] =
00531 {
00532   "\nRELATED INFORMATION",
00533   "\nRELATED\tINFORMATION",
00534   "RELATED INFORMATION\n",
00535   "RELATED\tINFORMATION\n",
00536   "\nSEE ALSO",
00537   "\nSEE\tALSO",
00538   "SEE ALSO\n",
00539   "SEE\tALSO\n",
00540   (char *)NULL
00541 };
00542 
00543 static SEARCH_BINDING frs_binding;
00544 
00545 static SEARCH_BINDING *
00546 find_reference_section (NODE *node)
00547 {
00548   register int i;
00549   long position = -1;
00550 
00551   frs_binding.buffer = node->contents;
00552   frs_binding.start = 0;
00553   frs_binding.end = node->nodelen;
00554   frs_binding.flags = S_SkipDest;
00555 
00556   for (i = 0; reference_section_starters[i] != (char *)NULL; i++)
00557     {
00558       position = search_forward (reference_section_starters[i], &frs_binding);
00559       if (position != -1)
00560         break;
00561     }
00562 
00563   if (position == -1)
00564     return ((SEARCH_BINDING *)NULL);
00565 
00566   /* We found the start of the reference section, and point is right after
00567      the string which starts it.  The text from here to the next header
00568      (or end of buffer) contains the only references in this manpage. */
00569   frs_binding.start = position;
00570 
00571   for (i = frs_binding.start; i < frs_binding.end - 2; i++)
00572     {
00573       if ((frs_binding.buffer[i] == '\n') &&
00574           (!whitespace (frs_binding.buffer[i + 1])))
00575         {
00576           frs_binding.end = i;
00577           break;
00578         }
00579     }
00580 
00581   return (&frs_binding);
00582 }
00583 
00584 REFERENCE **
00585 xrefs_of_manpage (NODE *node)
00586 {
00587   SEARCH_BINDING *reference_section;
00588   REFERENCE **refs = (REFERENCE **)NULL;
00589   int refs_index = 0;
00590   int refs_slots = 0;
00591   long position;
00592 
00593   reference_section = find_reference_section (node);
00594 
00595   if (reference_section == (SEARCH_BINDING *)NULL)
00596     return ((REFERENCE **)NULL);
00597 
00598   /* Grovel the reference section building a list of references found there.
00599      A reference is alphabetic characters followed by non-whitespace text
00600      within parenthesis. */
00601   reference_section->flags = 0;
00602 
00603   while ((position = search_forward ("(", reference_section)) != -1)
00604     {
00605       register int start, end;
00606 
00607       for (start = position; start > reference_section->start; start--)
00608         if (whitespace (reference_section->buffer[start]))
00609           break;
00610 
00611       start++;
00612 
00613       for (end = position; end < reference_section->end; end++)
00614         {
00615           if (whitespace (reference_section->buffer[end]))
00616             {
00617               end = start;
00618               break;
00619             }
00620 
00621           if (reference_section->buffer[end] == ')')
00622             {
00623               end++;
00624               break;
00625             }
00626         }
00627 
00628       if (end != start)
00629         {
00630           REFERENCE *entry;
00631           int len = end - start;
00632 
00633           entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
00634           entry->label = (char *)xmalloc (1 + len);
00635           strncpy (entry->label, (reference_section->buffer) + start, len);
00636           entry->label[len] = '\0';
00637           entry->filename = xstrdup (node->filename);
00638           entry->nodename = xstrdup (entry->label);
00639           entry->start = start;
00640           entry->end = end;
00641 
00642           add_pointer_to_array
00643             (entry, refs_index, refs, refs_slots, 10, REFERENCE *);
00644         }
00645 
00646       reference_section->start = position + 1;
00647     }
00648 
00649   return (refs);
00650 }
00651 
00652 long
00653 locate_manpage_xref (NODE *node, long int start, int dir)
00654 {
00655   REFERENCE **refs;
00656   long position = -1;
00657 
00658   refs = xrefs_of_manpage (node);
00659 
00660   if (refs)
00661     {
00662       register int i, count;
00663       REFERENCE *entry;
00664 
00665       for (i = 0; refs[i]; i++);
00666       count = i;
00667 
00668       if (dir > 0)
00669         {
00670           for (i = 0; (entry = refs[i]); i++)
00671             if (entry->start > start)
00672               {
00673                 position = entry->start;
00674                 break;
00675               }
00676         }
00677       else
00678         {
00679           for (i = count - 1; i > -1; i--)
00680             {
00681               entry = refs[i];
00682 
00683               if (entry->start < start)
00684                 {
00685                   position = entry->start;
00686                   break;
00687                 }
00688             }
00689         }
00690 
00691       info_free_references (refs);
00692     }
00693   return (position);
00694 }
00695 
00696 /* This one was a little tricky.  The binding buffer that is passed in has
00697    a START and END value of 0 -- strlen (window-line-containing-point).
00698    The BUFFER is a pointer to the start of that line. */
00699 REFERENCE **
00700 manpage_xrefs_in_binding (NODE *node, SEARCH_BINDING *binding)
00701 {
00702   register int i;
00703   REFERENCE **all_refs = xrefs_of_manpage (node);
00704   REFERENCE **brefs = (REFERENCE **)NULL;
00705   REFERENCE *entry;
00706   int brefs_index = 0;
00707   int brefs_slots = 0;
00708   int start, end;
00709 
00710   if (!all_refs)
00711     return ((REFERENCE **)NULL);
00712 
00713   start = binding->start + (binding->buffer - node->contents);
00714   end = binding->end + (binding->buffer - node->contents);
00715 
00716   for (i = 0; (entry = all_refs[i]); i++)
00717     {
00718       if ((entry->start > start) && (entry->end < end))
00719         {
00720           add_pointer_to_array
00721             (entry, brefs_index, brefs, brefs_slots, 10, REFERENCE *);
00722         }
00723       else
00724         {
00725           maybe_free (entry->label);
00726           maybe_free (entry->filename);
00727           maybe_free (entry->nodename);
00728           free (entry);
00729         }
00730     }
00731 
00732   free (all_refs);
00733   return (brefs);
00734 }