1 /* man.c: How to read and format man files.
2 $Id: man.c,v 1.5 1998/03/22 22:35:19 law Exp $
4 Copyright (C) 1995, 97 Free Software Foundation, Inc.
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)
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.
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.
20 Written by Brian Fox Thu May 4 09:17:52 1995 (bfox@ai.mit.edu). */
23 #include <sys/ioctl.h>
25 #if defined (HAVE_SYS_TIME_H)
28 #if defined (HAVE_SYS_WAIT_H)
35 #if !defined (_POSIX_VERSION)
41 # define fd_set_cast(x) (int *)(x)
43 # define fd_set_cast(x) (fd_set *)(x)
47 static char *read_from_fd ();
48 static void clean_manpage ();
49 static NODE
*manpage_node_of_file_buffer ();
50 static char *get_manpage_contents ();
53 make_manpage_node (pagename
)
56 return (info_get_node (MANPAGE_FILE_BUFFER_NAME
, pagename
));
60 get_manpage_node (file_buffer
, pagename
)
61 FILE_BUFFER
*file_buffer
;
66 node
= manpage_node_of_file_buffer (file_buffer
, pagename
);
72 page
= get_manpage_contents (pagename
);
77 long oldsize
, newsize
;
80 sprintf (header
, "\n\n%c\n%s %s, %s %s, %s (dir)\n\n",
82 INFO_FILE_LABEL
, file_buffer
->filename
,
83 INFO_NODE_LABEL
, pagename
,
85 oldsize
= file_buffer
->filesize
;
86 hlen
= strlen (header
);
88 newsize
= (oldsize
+ hlen
+ plen
);
89 file_buffer
->contents
=
90 (char *)xrealloc (file_buffer
->contents
, 1 + newsize
);
91 memcpy (file_buffer
->contents
+ oldsize
, header
, hlen
);
93 memcpy (file_buffer
->contents
+ oldsize
, page
, plen
);
94 file_buffer
->contents
[newsize
] = '\0';
95 file_buffer
->filesize
= newsize
;
96 file_buffer
->finfo
.st_size
= newsize
;
97 build_tags_and_nodes (file_buffer
);
101 node
= manpage_node_of_file_buffer (file_buffer
, pagename
);
108 create_manpage_file_buffer ()
110 FILE_BUFFER
*file_buffer
= make_file_buffer ();
111 file_buffer
->filename
= xstrdup (MANPAGE_FILE_BUFFER_NAME
);
112 file_buffer
->fullpath
= xstrdup (MANPAGE_FILE_BUFFER_NAME
);
113 file_buffer
->finfo
.st_size
= 0;
114 file_buffer
->filesize
= 0;
115 file_buffer
->contents
= (char *)NULL
;
116 file_buffer
->flags
= (N_IsInternal
| N_CannotGC
| N_IsManPage
);
118 return (file_buffer
);
121 /* Scan the list of directories in PATH looking for FILENAME. If we find
122 one that is an executable file, return it as a new string. Otherwise,
123 return a NULL pointer. */
125 executable_file_in_path (filename
, path
)
126 char *filename
, *path
;
130 int statable
, dirname_index
;
134 while ((temp_dirname
= extract_colon_unit (path
, &dirname_index
)))
138 /* Expand a leading tilde if one is present. */
139 if (*temp_dirname
== '~')
141 char *expanded_dirname
;
143 expanded_dirname
= tilde_expand_word (temp_dirname
);
145 temp_dirname
= expanded_dirname
;
148 temp
= (char *)xmalloc (30 + strlen (temp_dirname
) + strlen (filename
));
149 strcpy (temp
, temp_dirname
);
150 if (temp
[(strlen (temp
)) - 1] != '/')
152 strcat (temp
, filename
);
156 statable
= (stat (temp
, &finfo
) == 0);
158 /* If we have found a regular executable file, then use it. */
159 if ((statable
) && (S_ISREG (finfo
.st_mode
)) &&
160 (access (temp
, X_OK
) == 0))
165 return ((char *)NULL
);
168 /* Return the full pathname of the system man page formatter. */
170 find_man_formatter ()
172 return (executable_file_in_path ("man", (char *)getenv ("PATH")));
175 static char *manpage_pagename
= (char *)NULL
;
176 static char *manpage_section
= (char *)NULL
;
179 get_page_and_section (pagename
)
184 if (manpage_pagename
)
185 free (manpage_pagename
);
188 free (manpage_section
);
190 manpage_pagename
= (char *)NULL
;
191 manpage_section
= (char *)NULL
;
193 for (i
= 0; pagename
[i
] != '\0' && pagename
[i
] != '('; i
++);
195 manpage_pagename
= (char *)xmalloc (1 + i
);
196 strncpy (manpage_pagename
, pagename
, i
);
197 manpage_pagename
[i
] = '\0';
199 if (pagename
[i
] == '(')
205 for (i
= start
; pagename
[i
] != '\0' && pagename
[i
] != ')'; i
++);
207 manpage_section
= (char *)xmalloc (1 + (i
- start
));
208 strncpy (manpage_section
, pagename
+ start
, (i
- start
));
209 manpage_section
[i
- start
] = '\0';
222 get_manpage_contents (pagename
)
225 static char *formatter_args
[4] = { (char *)NULL
};
228 char *formatted_page
= (char *)NULL
;
231 if (formatter_args
[0] == (char *)NULL
)
232 formatter_args
[0] = find_man_formatter ();
234 if (formatter_args
[0] == (char *)NULL
)
235 return ((char *)NULL
);
237 get_page_and_section (pagename
);
239 if (manpage_section
!= (char *)NULL
)
240 formatter_args
[arg_index
++] = manpage_section
;
242 formatter_args
[arg_index
++] = manpage_pagename
;
243 formatter_args
[arg_index
] = (char *)NULL
;
245 /* Open a pipe to this program, read the output, and save it away
246 in FORMATTED_PAGE. The reader end of the pipe is pipes[0]; the
247 writer end is pipes[1]. */
250 signal (SIGCHLD
, reap_children
);
255 return ((char *)NULL
);
259 /* In the parent, close the writing end of the pipe, and read from
262 formatted_page
= read_from_fd (pipes
[0]);
267 /* In the child, close the read end of the pipe, make the write end
268 of the pipe be stdout, and execute the man page formatter. */
270 close (fileno (stderr
));
271 close (fileno (stdin
)); /* Don't print errors. */
272 dup2 (pipes
[1], fileno (stdout
));
274 execv (formatter_args
[0], formatter_args
);
276 /* If we get here, we couldn't exec, so close out the pipe and
282 /* If we have the page, then clean it up. */
284 clean_manpage (formatted_page
);
286 return (formatted_page
);
290 clean_manpage (manpage
)
294 int newline_count
= 0;
297 newpage
= (char *)xmalloc (1 + strlen (manpage
));
299 for (i
= 0, j
= 0; (newpage
[j
] = manpage
[i
]); i
++, j
++)
301 if (manpage
[i
] == '\n')
306 if (newline_count
== 3)
312 if (manpage
[i
] == '\b' || manpage
[i
] == '\f')
318 strcpy (manpage
, newpage
);
323 manpage_node_of_file_buffer (file_buffer
, pagename
)
324 FILE_BUFFER
*file_buffer
;
327 NODE
*node
= (NODE
*)NULL
;
328 TAG
*tag
= (TAG
*)NULL
;
330 if (file_buffer
->contents
)
334 for (i
= 0; (tag
= file_buffer
->tags
[i
]); i
++)
336 if (strcasecmp (pagename
, tag
->nodename
) == 0)
343 node
= (NODE
*)xmalloc (sizeof (NODE
));
344 node
->filename
= file_buffer
->filename
;
345 node
->nodename
= tag
->nodename
;
346 node
->contents
= file_buffer
->contents
+ tag
->nodestart
;
347 node
->nodelen
= tag
->nodelen
;
349 node
->parent
= (char *)NULL
;
350 node
->flags
= (N_HasTagsTable
| N_IsManPage
);
351 node
->contents
+= skip_node_separator (node
->contents
);
361 struct timeval timeout
;
362 char *buffer
= (char *)NULL
;
373 FD_SET (fd
, &read_fds
);
375 select_result
= select (fd
+ 1, fd_set_cast (&read_fds
), 0, 0, &timeout
);
380 switch (select_result
)
393 while ((bindex
+ 1024) > (bsize
))
394 buffer
= (char *)xrealloc (buffer
, (bsize
+= 1024));
395 buffer
[bindex
] = '\0';
397 amount_read
= read (fd
, buffer
+ bindex
, 1023);
405 bindex
+= amount_read
;
406 buffer
[bindex
] = '\0';
407 if (amount_read
== 0)
414 if ((buffer
!= (char *)NULL
) && (*buffer
== '\0'))
417 buffer
= (char *)NULL
;
423 static char *reference_section_starters
[] =
425 "\nRELATED INFORMATION",
426 "\nRELATED\tINFORMATION",
427 "RELATED INFORMATION\n",
428 "RELATED\tINFORMATION\n",
436 static SEARCH_BINDING frs_binding
;
438 static SEARCH_BINDING
*
439 find_reference_section (node
)
445 frs_binding
.buffer
= node
->contents
;
446 frs_binding
.start
= 0;
447 frs_binding
.end
= node
->nodelen
;
448 frs_binding
.flags
= S_SkipDest
;
450 for (i
= 0; reference_section_starters
[i
] != (char *)NULL
; i
++)
452 position
= search_forward (reference_section_starters
[i
], &frs_binding
);
458 return ((SEARCH_BINDING
*)NULL
);
460 /* We found the start of the reference section, and point is right after
461 the string which starts it. The text from here to the next header
462 (or end of buffer) contains the only references in this manpage. */
463 frs_binding
.start
= position
;
465 for (i
= frs_binding
.start
; i
< frs_binding
.end
- 2; i
++)
467 if ((frs_binding
.buffer
[i
] == '\n') &&
468 (!whitespace (frs_binding
.buffer
[i
+ 1])))
475 return (&frs_binding
);
479 xrefs_of_manpage (node
)
482 SEARCH_BINDING
*reference_section
;
483 REFERENCE
**refs
= (REFERENCE
**)NULL
;
488 reference_section
= find_reference_section (node
);
490 if (reference_section
== (SEARCH_BINDING
*)NULL
)
491 return ((REFERENCE
**)NULL
);
493 /* Grovel the reference section building a list of references found there.
494 A reference is alphabetic characters followed by non-whitespace text
495 within parenthesis. */
496 reference_section
->flags
= 0;
498 while ((position
= search_forward ("(", reference_section
)) != -1)
500 register int start
, end
;
502 for (start
= position
; start
> reference_section
->start
; start
--)
503 if (whitespace (reference_section
->buffer
[start
]))
508 for (end
= position
; end
< reference_section
->end
; end
++)
510 if (whitespace (reference_section
->buffer
[end
]))
516 if (reference_section
->buffer
[end
] == ')')
526 int len
= end
- start
;
528 entry
= (REFERENCE
*)xmalloc (sizeof (REFERENCE
));
529 entry
->label
= (char *)xmalloc (1 + len
);
530 strncpy (entry
->label
, (reference_section
->buffer
) + start
, len
);
531 entry
->label
[len
] = '\0';
532 entry
->filename
= xstrdup (node
->filename
);
533 entry
->nodename
= xstrdup (entry
->label
);
534 entry
->start
= start
;
538 (entry
, refs_index
, refs
, refs_slots
, 10, REFERENCE
*);
541 reference_section
->start
= position
+ 1;
548 locate_manpage_xref (node
, start
, dir
)
556 refs
= xrefs_of_manpage (node
);
560 register int i
, count
;
563 for (i
= 0; refs
[i
]; i
++);
568 for (i
= 0; (entry
= refs
[i
]); i
++)
569 if (entry
->start
> start
)
571 position
= entry
->start
;
577 for (i
= count
- 1; i
> -1; i
--)
581 if (entry
->start
< start
)
583 position
= entry
->start
;
589 info_free_references (refs
);
594 /* This one was a little tricky. The binding buffer that is passed in has
595 a START and END value of 0 -- strlen (window-line-containing-point).
596 The BUFFER is a pointer to the start of that line. */
598 manpage_xrefs_in_binding (node
, binding
)
600 SEARCH_BINDING
*binding
;
603 REFERENCE
**all_refs
= xrefs_of_manpage (node
);
604 REFERENCE
**brefs
= (REFERENCE
**)NULL
;
611 return ((REFERENCE
**)NULL
);
613 start
= binding
->start
+ (binding
->buffer
- node
->contents
);
614 end
= binding
->end
+ (binding
->buffer
- node
->contents
);
616 for (i
= 0; (entry
= all_refs
[i
]); i
++)
618 if ((entry
->start
> start
) && (entry
->end
< end
))
621 (entry
, brefs_index
, brefs
, brefs_slots
, 10, REFERENCE
*);
625 maybe_free (entry
->label
);
626 maybe_free (entry
->filename
);
627 maybe_free (entry
->nodename
);