xref: /netbsd-src/external/gpl2/texinfo/dist/info/man.c (revision 29619d2afe564e54d657b83e5a3ae89584f83720)
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@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 *
make_manpage_node(char * pagename)65 make_manpage_node (char *pagename)
66 {
67   return (info_get_node (MANPAGE_FILE_BUFFER_NAME, pagename));
68 }
69 
70 NODE *
get_manpage_node(FILE_BUFFER * file_buffer,char * pagename)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 *
create_manpage_file_buffer(void)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 *
executable_file_in_path(char * filename,char * path)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 *
find_man_formatter(void)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
get_page_and_section(char * pagename)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
reap_children(int sig)271 reap_children (int sig)
272 {
273   wait (NULL);
274 }
275 #endif
276 
277 static char *
get_manpage_contents(char * pagename)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
clean_manpage(char * manpage)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 *
manpage_node_of_file_buffer(FILE_BUFFER * file_buffer,char * pagename)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 *
read_from_fd(int fd)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 *
find_reference_section(NODE * node)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 **
xrefs_of_manpage(NODE * node)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
locate_manpage_xref(NODE * node,long int start,int dir)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 **
manpage_xrefs_in_binding(NODE * node,SEARCH_BINDING * binding)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