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