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