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 $
4 Copyright (C) 1995, 1997, 1998, 1999, 2000, 2002, 2003, 2004, 2005,
5 2007, 2008 Free Software Foundation, Inc.
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.
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.
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/>.
20 Originally written by Brian Fox Thu May 4 09:17:52 1995. */
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)
48 static char const * const exec_extensions[] = {
49 ".exe", ".com", ".bat", ".btm", ".sh", ".ksh", ".pl", ".sed", "", NULL
52 static char const * const exec_extensions[] = { "", NULL };
55 static char *read_from_fd (int fd);
56 static NODE *manpage_node_of_file_buffer (FILE_BUFFER *file_buffer,
58 static char *get_manpage_contents (char *pagename);
61 make_manpage_node (char *pagename)
63 return info_get_node (MANPAGE_FILE_BUFFER_NAME, pagename);
67 get_manpage_node (FILE_BUFFER *file_buffer, char *pagename)
71 node = manpage_node_of_file_buffer (file_buffer, pagename);
77 page = get_manpage_contents (pagename);
82 long oldsize, newsize;
84 char *old_contents = file_buffer->contents;
86 sprintf (header, "\n\n%c\n%s %s, %s %s, %s (dir)\n\n",
88 INFO_FILE_LABEL, file_buffer->filename,
89 INFO_NODE_LABEL, pagename,
91 oldsize = file_buffer->filesize;
92 hlen = strlen (header);
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);
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)
110 INFO_WINDOW *info_win;
111 char *old_contents_end = old_contents + oldsize;
113 for (iw = 0; (info_win = info_windows[iw]); iw++)
117 for (in = 0; in < info_win->nodes_index; in++)
119 NODE *tmp_node = info_win->nodes[in];
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
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
133 info_win->nodes[in] =
134 manpage_node_of_file_buffer (file_buffer,
136 free (tmp_node->nodename);
144 node = manpage_node_of_file_buffer (file_buffer, pagename);
151 create_manpage_file_buffer (void)
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);
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. */
168 executable_file_in_path (char *filename, char *path)
172 int statable, dirname_index;
176 while ((temp_dirname = extract_colon_unit (path, &dirname_index)))
182 /* Expand a leading tilde if one is present. */
183 if (*temp_dirname == '~')
185 char *expanded_dirname;
187 expanded_dirname = tilde_expand_word (temp_dirname);
189 temp_dirname = expanded_dirname;
192 temp = xmalloc (34 + strlen (temp_dirname) + strlen (filename));
193 strcpy (temp, temp_dirname);
194 if (!IS_SLASH (temp[(strlen (temp)) - 1]))
196 strcat (temp, filename);
197 temp_end = temp + strlen (temp);
201 /* Look for FILENAME, possibly with any of the extensions
202 in EXEC_EXTENSIONS[]. */
203 for (i = 0; exec_extensions[i]; i++)
205 if (exec_extensions[i][0])
206 strcpy (temp_end, exec_extensions[i]);
207 statable = (stat (temp, &finfo) == 0);
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))
220 /* Return the full pathname of the system man page formatter. */
222 find_man_formatter (void)
224 char *man_command = getenv ("INFO_MAN_COMMAND");
225 return man_command ? man_command :
226 executable_file_in_path ("man", getenv ("PATH"));
229 static char *manpage_pagename = NULL;
230 static char *manpage_section = NULL;
233 get_page_and_section (char *pagename)
237 if (manpage_pagename)
238 free (manpage_pagename);
241 free (manpage_section);
243 manpage_pagename = NULL;
244 manpage_section = NULL;
246 for (i = 0; pagename[i] != '\0' && pagename[i] != '('; i++);
248 manpage_pagename = xmalloc (1 + i);
249 strncpy (manpage_pagename, pagename, i);
250 manpage_pagename[i] = '\0';
252 if (pagename[i] == '(')
258 for (i = start; pagename[i] != '\0' && pagename[i] != ')'; i++);
260 manpage_section = xmalloc (1 + (i - start));
261 strncpy (manpage_section, pagename + start, (i - start));
262 manpage_section[i - start] = '\0';
268 reap_children (int sig)
275 get_manpage_contents (char *pagename)
277 static char *formatter_args[4] = { NULL };
280 RETSIGTYPE (*sigsave) (int signum);
281 char *formatted_page = NULL;
284 if (formatter_args[0] == NULL)
285 formatter_args[0] = find_man_formatter ();
287 if (formatter_args[0] == NULL)
290 get_page_and_section (pagename);
293 formatter_args[arg_index++] = manpage_section;
295 formatter_args[arg_index++] = "-a";
297 formatter_args[arg_index++] = manpage_pagename;
298 formatter_args[arg_index] = NULL;
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]. */
306 sigsave = signal (SIGCHLD, reap_children);
314 /* In the parent, close the writing end of the pipe, and read from
317 formatted_page = read_from_fd (pipes[0]);
319 signal (SIGCHLD, sigsave);
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. */
325 freopen (NULL_DEVICE, "w", stderr);
326 freopen (NULL_DEVICE, "r", stdin);
327 dup2 (pipes[1], fileno (stdout));
329 execv (formatter_args[0], formatter_args);
331 /* If we get here, we couldn't exec, so close out the pipe and
336 #else /* !PIPE_USE_FORK */
337 /* Cannot fork/exec, but can popen/pclose. */
340 char *cmdline = xmalloc (strlen (formatter_args[0])
341 + strlen (manpage_pagename)
342 + (arg_index > 2 ? strlen (manpage_section) : 0)
344 int save_stderr = dup (fileno (stderr));
345 int fd_err = open (NULL_DEVICE, O_WRONLY, 0666);
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");
355 dup2 (save_stderr, fileno (stderr));
358 formatted_page = read_from_fd (fileno (fpipe));
359 if (pclose (fpipe) == -1)
362 free (formatted_page);
366 #endif /* !PIPE_USE_FORK */
368 /* If we have the page, then clean it up. */
370 clean_manpage (formatted_page);
372 return formatted_page;
376 manpage_node_of_file_buffer (FILE_BUFFER *file_buffer, char *pagename)
381 if (file_buffer->contents)
385 for (i = 0; (tag = file_buffer->tags[i]); i++)
387 if (mbscasecmp (pagename, tag->nodename) == 0)
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;
400 node->display_pos = 0;
402 node->flags = (N_HasTagsTable | N_IsManPage);
403 node->contents += skip_node_separator (node->contents);
410 read_from_fd (int fd)
412 struct timeval timeout;
424 FD_SET (fd, &read_fds);
426 select_result = select (fd + 1, fd_set_cast (&read_fds), 0, 0, &timeout);
431 switch (select_result)
444 while ((bindex + 1024) > (bsize))
445 buffer = xrealloc (buffer, (bsize += 1024));
446 buffer[bindex] = '\0';
448 amount_read = read (fd, buffer + bindex, 1023);
456 bindex += amount_read;
457 buffer[bindex] = '\0';
458 if (amount_read == 0)
465 if ((buffer != NULL) && (*buffer == '\0'))
474 static char *reference_section_starters[] =
476 "\nRELATED INFORMATION",
477 "\nRELATED\tINFORMATION",
478 "RELATED INFORMATION\n",
479 "RELATED\tINFORMATION\n",
487 static SEARCH_BINDING frs_binding;
489 static SEARCH_BINDING *
490 find_reference_section (NODE *node)
495 frs_binding.buffer = node->contents;
496 frs_binding.start = 0;
497 frs_binding.end = node->nodelen;
498 frs_binding.flags = S_SkipDest;
500 for (i = 0; reference_section_starters[i] != NULL; i++)
502 position = search_forward (reference_section_starters[i], &frs_binding);
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;
515 for (i = frs_binding.start; i < frs_binding.end - 2; i++)
517 if ((frs_binding.buffer[i] == '\n') &&
518 (!whitespace (frs_binding.buffer[i + 1])))
529 xrefs_of_manpage (NODE *node)
531 SEARCH_BINDING *reference_section;
532 REFERENCE **refs = NULL;
537 reference_section = find_reference_section (node);
539 if (reference_section == NULL)
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;
547 while ((position = search_forward ("(", reference_section)) != -1)
549 register int start, end;
551 for (start = position; start > reference_section->start; start--)
552 if (whitespace (reference_section->buffer[start]))
557 for (end = position; end < reference_section->end; end++)
559 if (whitespace (reference_section->buffer[end]))
565 if (reference_section->buffer[end] == ')')
575 int len = end - start;
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;
587 (entry, refs_index, refs, refs_slots, 10, REFERENCE *);
590 reference_section->start = position + 1;
597 locate_manpage_xref (NODE *node, long int start, int dir)
602 refs = xrefs_of_manpage (node);
606 register int i, count;
609 for (i = 0; refs[i]; i++);
614 for (i = 0; (entry = refs[i]); i++)
615 if (entry->start > start)
617 position = entry->start;
623 for (i = count - 1; i > -1; i--)
627 if (entry->start < start)
629 position = entry->start;
635 info_free_references (refs);
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. */
644 manpage_xrefs_in_binding (NODE *node, SEARCH_BINDING *binding)
647 REFERENCE **all_refs = xrefs_of_manpage (node);
648 REFERENCE **brefs = NULL;
657 start = binding->start + (binding->buffer - node->contents);
658 end = binding->end + (binding->buffer - node->contents);
660 for (i = 0; (entry = all_refs[i]); i++)
662 if ((entry->start > start) && (entry->end < end))
665 (entry, brefs_index, brefs, brefs_slots, 10, REFERENCE *);
669 maybe_free (entry->label);
670 maybe_free (entry->filename);
671 maybe_free (entry->nodename);