1 /* man.c: How to read and format man files.
2 $Id: man.c,v 1.16 2002/02/23 19:12:02 karl Exp $
4 Copyright (C) 1995, 97, 98, 99, 2000 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)
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 ();
56 static void clean_manpage ();
57 static NODE *manpage_node_of_file_buffer ();
58 static char *get_manpage_contents ();
61 make_manpage_node (pagename)
64 return (info_get_node (MANPAGE_FILE_BUFFER_NAME, pagename));
68 get_manpage_node (file_buffer, pagename)
69 FILE_BUFFER *file_buffer;
74 node = manpage_node_of_file_buffer (file_buffer, pagename);
80 page = get_manpage_contents (pagename);
85 long oldsize, newsize;
87 char *old_contents = file_buffer->contents;
89 sprintf (header, "\n\n%c\n%s %s, %s %s, %s (dir)\n\n",
91 INFO_FILE_LABEL, file_buffer->filename,
92 INFO_NODE_LABEL, pagename,
94 oldsize = file_buffer->filesize;
95 hlen = strlen (header);
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);
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)
114 INFO_WINDOW *info_win;
115 char *old_contents_end = old_contents + oldsize;
117 for (iw = 0; (info_win = info_windows[iw]); iw++)
121 for (in = 0; in < info_win->nodes_index; in++)
123 NODE *node = info_win->nodes[in];
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
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)
135 info_win->nodes[in] =
136 manpage_node_of_file_buffer (file_buffer,
138 free (node->nodename);
146 node = manpage_node_of_file_buffer (file_buffer, pagename);
153 create_manpage_file_buffer ()
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);
163 return (file_buffer);
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. */
170 executable_file_in_path (filename, path)
171 char *filename, *path;
175 int statable, dirname_index;
179 while ((temp_dirname = extract_colon_unit (path, &dirname_index)))
185 /* Expand a leading tilde if one is present. */
186 if (*temp_dirname == '~')
188 char *expanded_dirname;
190 expanded_dirname = tilde_expand_word (temp_dirname);
192 temp_dirname = expanded_dirname;
195 temp = (char *)xmalloc (34 + strlen (temp_dirname) + strlen (filename));
196 strcpy (temp, temp_dirname);
197 if (!IS_SLASH (temp[(strlen (temp)) - 1]))
199 strcat (temp, filename);
200 temp_end = temp + strlen (temp);
204 /* Look for FILENAME, possibly with any of the extensions
205 in EXEC_EXTENSIONS[]. */
206 for (i = 0; exec_extensions[i]; i++)
208 if (exec_extensions[i][0])
209 strcpy (temp_end, exec_extensions[i]);
210 statable = (stat (temp, &finfo) == 0);
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))
220 return ((char *)NULL);
223 /* Return the full pathname of the system man page formatter. */
225 find_man_formatter ()
227 return (executable_file_in_path ("man", (char *)getenv ("PATH")));
230 static char *manpage_pagename = (char *)NULL;
231 static char *manpage_section = (char *)NULL;
234 get_page_and_section (pagename)
239 if (manpage_pagename)
240 free (manpage_pagename);
243 free (manpage_section);
245 manpage_pagename = (char *)NULL;
246 manpage_section = (char *)NULL;
248 for (i = 0; pagename[i] != '\0' && pagename[i] != '('; i++);
250 manpage_pagename = (char *)xmalloc (1 + i);
251 strncpy (manpage_pagename, pagename, i);
252 manpage_pagename[i] = '\0';
254 if (pagename[i] == '(')
260 for (i = start; pagename[i] != '\0' && pagename[i] != ')'; i++);
262 manpage_section = (char *)xmalloc (1 + (i - start));
263 strncpy (manpage_section, pagename + start, (i - start));
264 manpage_section[i - start] = '\0';
278 get_manpage_contents (pagename)
281 static char *formatter_args[4] = { (char *)NULL };
284 RETSIGTYPE (*sigsave) ();
285 char *formatted_page = NULL;
288 if (formatter_args[0] == (char *)NULL)
289 formatter_args[0] = find_man_formatter ();
291 if (formatter_args[0] == (char *)NULL)
292 return ((char *)NULL);
294 get_page_and_section (pagename);
296 if (manpage_section != (char *)NULL)
297 formatter_args[arg_index++] = manpage_section;
299 formatter_args[arg_index++] = manpage_pagename;
300 formatter_args[arg_index] = (char *)NULL;
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]. */
308 sigsave = signal (SIGCHLD, reap_children);
312 return ((char *)NULL);
316 /* In the parent, close the writing end of the pipe, and read from
319 formatted_page = read_from_fd (pipes[0]);
321 signal (SIGCHLD, sigsave);
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. */
327 freopen (NULL_DEVICE, "w", stderr);
328 freopen (NULL_DEVICE, "r", stdin);
329 dup2 (pipes[1], fileno (stdout));
331 execv (formatter_args[0], formatter_args);
333 /* If we get here, we couldn't exec, so close out the pipe and
338 #else /* !PIPE_USE_FORK */
339 /* Cannot fork/exec, but can popen/pclose. */
342 char *cmdline = xmalloc (strlen (formatter_args[0])
343 + strlen (manpage_pagename)
344 + (arg_index > 2 ? strlen (manpage_section) : 0)
346 int save_stderr = dup (fileno (stderr));
347 int fd_err = open (NULL_DEVICE, O_WRONLY, 0666);
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");
357 dup2 (save_stderr, fileno (stderr));
359 return ((char *)NULL);
360 formatted_page = read_from_fd (fileno (fpipe));
361 if (pclose (fpipe) == -1)
364 free (formatted_page);
365 return ((char *)NULL);
368 #endif /* !PIPE_USE_FORK */
370 /* If we have the page, then clean it up. */
372 clean_manpage (formatted_page);
374 return (formatted_page);
378 clean_manpage (manpage)
382 int newline_count = 0;
385 newpage = (char *)xmalloc (1 + strlen (manpage));
387 for (i = 0, j = 0; (newpage[j] = manpage[i]); i++, j++)
389 if (manpage[i] == '\n')
394 if (newline_count == 3)
400 /* A malformed man page could have a \b as its first character,
401 in which case decrementing j by 2 will cause us to write into
402 newpage[-1], smashing the hidden info stored there by malloc. */
403 if (manpage[i] == '\b' || manpage[i] == '\f' && j > 0)
405 else if (!raw_escapes_p)
407 /* Remove the ANSI escape sequences for color, boldface,
408 underlining, and italics, generated by some versions of
410 if (manpage[i] == '\033' && manpage[i + 1] == '['
411 && isdigit (manpage[i + 2]))
413 if (isdigit (manpage[i + 3]) && manpage[i + 4] == 'm')
418 else if (manpage[i + 3] == 'm')
423 /* Else do nothing: it's some unknown escape sequence,
424 so let's leave it alone. */
431 strcpy (manpage, newpage);
436 manpage_node_of_file_buffer (file_buffer, pagename)
437 FILE_BUFFER *file_buffer;
440 NODE *node = (NODE *)NULL;
441 TAG *tag = (TAG *)NULL;
443 if (file_buffer->contents)
447 for (i = 0; (tag = file_buffer->tags[i]); i++)
449 if (strcasecmp (pagename, tag->nodename) == 0)
456 node = (NODE *)xmalloc (sizeof (NODE));
457 node->filename = file_buffer->filename;
458 node->nodename = xstrdup (tag->nodename);
459 node->contents = file_buffer->contents + tag->nodestart;
460 node->nodelen = tag->nodelen;
462 node->display_pos = 0;
463 node->parent = (char *)NULL;
464 node->flags = (N_HasTagsTable | N_IsManPage);
465 node->contents += skip_node_separator (node->contents);
475 struct timeval timeout;
476 char *buffer = (char *)NULL;
487 FD_SET (fd, &read_fds);
489 select_result = select (fd + 1, fd_set_cast (&read_fds), 0, 0, &timeout);
494 switch (select_result)
507 while ((bindex + 1024) > (bsize))
508 buffer = (char *)xrealloc (buffer, (bsize += 1024));
509 buffer[bindex] = '\0';
511 amount_read = read (fd, buffer + bindex, 1023);
519 bindex += amount_read;
520 buffer[bindex] = '\0';
521 if (amount_read == 0)
528 if ((buffer != (char *)NULL) && (*buffer == '\0'))
531 buffer = (char *)NULL;
537 static char *reference_section_starters[] =
539 "\nRELATED INFORMATION",
540 "\nRELATED\tINFORMATION",
541 "RELATED INFORMATION\n",
542 "RELATED\tINFORMATION\n",
550 static SEARCH_BINDING frs_binding;
552 static SEARCH_BINDING *
553 find_reference_section (node)
559 frs_binding.buffer = node->contents;
560 frs_binding.start = 0;
561 frs_binding.end = node->nodelen;
562 frs_binding.flags = S_SkipDest;
564 for (i = 0; reference_section_starters[i] != (char *)NULL; i++)
566 position = search_forward (reference_section_starters[i], &frs_binding);
572 return ((SEARCH_BINDING *)NULL);
574 /* We found the start of the reference section, and point is right after
575 the string which starts it. The text from here to the next header
576 (or end of buffer) contains the only references in this manpage. */
577 frs_binding.start = position;
579 for (i = frs_binding.start; i < frs_binding.end - 2; i++)
581 if ((frs_binding.buffer[i] == '\n') &&
582 (!whitespace (frs_binding.buffer[i + 1])))
589 return (&frs_binding);
593 xrefs_of_manpage (node)
596 SEARCH_BINDING *reference_section;
597 REFERENCE **refs = (REFERENCE **)NULL;
602 reference_section = find_reference_section (node);
604 if (reference_section == (SEARCH_BINDING *)NULL)
605 return ((REFERENCE **)NULL);
607 /* Grovel the reference section building a list of references found there.
608 A reference is alphabetic characters followed by non-whitespace text
609 within parenthesis. */
610 reference_section->flags = 0;
612 while ((position = search_forward ("(", reference_section)) != -1)
614 register int start, end;
616 for (start = position; start > reference_section->start; start--)
617 if (whitespace (reference_section->buffer[start]))
622 for (end = position; end < reference_section->end; end++)
624 if (whitespace (reference_section->buffer[end]))
630 if (reference_section->buffer[end] == ')')
640 int len = end - start;
642 entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
643 entry->label = (char *)xmalloc (1 + len);
644 strncpy (entry->label, (reference_section->buffer) + start, len);
645 entry->label[len] = '\0';
646 entry->filename = xstrdup (node->filename);
647 entry->nodename = xstrdup (entry->label);
648 entry->start = start;
652 (entry, refs_index, refs, refs_slots, 10, REFERENCE *);
655 reference_section->start = position + 1;
662 locate_manpage_xref (node, start, dir)
670 refs = xrefs_of_manpage (node);
674 register int i, count;
677 for (i = 0; refs[i]; i++);
682 for (i = 0; (entry = refs[i]); i++)
683 if (entry->start > start)
685 position = entry->start;
691 for (i = count - 1; i > -1; i--)
695 if (entry->start < start)
697 position = entry->start;
703 info_free_references (refs);
708 /* This one was a little tricky. The binding buffer that is passed in has
709 a START and END value of 0 -- strlen (window-line-containing-point).
710 The BUFFER is a pointer to the start of that line. */
712 manpage_xrefs_in_binding (node, binding)
714 SEARCH_BINDING *binding;
717 REFERENCE **all_refs = xrefs_of_manpage (node);
718 REFERENCE **brefs = (REFERENCE **)NULL;
725 return ((REFERENCE **)NULL);
727 start = binding->start + (binding->buffer - node->contents);
728 end = binding->end + (binding->buffer - node->contents);
730 for (i = 0; (entry = all_refs[i]); i++)
732 if ((entry->start > start) && (entry->end < end))
735 (entry, brefs_index, brefs, brefs_slots, 10, REFERENCE *);
739 maybe_free (entry->label);
740 maybe_free (entry->filename);
741 maybe_free (entry->nodename);