1 /* man.c: How to read and format man files.
2 $Id: man.c,v 1.5 2006/07/17 16:12:36 espie Exp $
3
4 Copyright (C) 1995, 1997, 1998, 1999, 2000, 2002, 2003, 2004 Free Software
5 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 2, or (at your option)
10 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, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20
21 Written by Brian Fox Thu May 4 09:17:52 1995 (bfox@ai.mit.edu). */
22
23 #include "info.h"
24 #include <sys/ioctl.h>
25 #include "signals.h"
26 #if defined (HAVE_SYS_TIME_H)
27 #include <sys/time.h>
28 #endif
29 #if defined (HAVE_SYS_WAIT_H)
30 #include <sys/wait.h>
31 #endif
32
33 #include "tilde.h"
34 #include "man.h"
35
36 #if !defined (_POSIX_VERSION)
37 #define pid_t int
38 #endif
39
40 #if defined (FD_SET)
41 # if defined (hpux)
42 # define fd_set_cast(x) (int *)(x)
43 # else
44 # define fd_set_cast(x) (fd_set *)(x)
45 # endif /* !hpux */
46 #endif /* FD_SET */
47
48 #if STRIP_DOT_EXE
49 static char const * const exec_extensions[] = {
50 ".exe", ".com", ".bat", ".btm", ".sh", ".ksh", ".pl", ".sed", "", NULL
51 };
52 #else
53 static char const * const exec_extensions[] = { "", NULL };
54 #endif
55
56 static char *read_from_fd (int fd);
57 static void clean_manpage (char *manpage);
58 static NODE *manpage_node_of_file_buffer (FILE_BUFFER *file_buffer,
59 char *pagename);
60 static char *get_manpage_contents (char *pagename);
61
62 NODE *
make_manpage_node(char * pagename)63 make_manpage_node (char *pagename)
64 {
65 return (info_get_node (MANPAGE_FILE_BUFFER_NAME, pagename));
66 }
67
68 NODE *
get_manpage_node(FILE_BUFFER * file_buffer,char * pagename)69 get_manpage_node (FILE_BUFFER *file_buffer, char *pagename)
70 {
71 NODE *node;
72
73 node = manpage_node_of_file_buffer (file_buffer, pagename);
74
75 if (!node)
76 {
77 char *page;
78
79 page = get_manpage_contents (pagename);
80
81 if (page)
82 {
83 char header[1024];
84 long oldsize, newsize;
85 int hlen, plen;
86 char *old_contents = file_buffer->contents;
87
88 sprintf (header, "\n\n%c\n%s %s, %s %s, %s (dir)\n\n",
89 INFO_COOKIE,
90 INFO_FILE_LABEL, file_buffer->filename,
91 INFO_NODE_LABEL, pagename,
92 INFO_UP_LABEL);
93 oldsize = file_buffer->filesize;
94 hlen = strlen (header);
95 plen = strlen (page);
96 newsize = (oldsize + hlen + plen);
97 file_buffer->contents =
98 (char *)xrealloc (file_buffer->contents, 1 + newsize);
99 memcpy (file_buffer->contents + oldsize, header, hlen);
100 memcpy (file_buffer->contents + oldsize + hlen, page, plen);
101 file_buffer->contents[newsize] = '\0';
102 file_buffer->filesize = newsize;
103 file_buffer->finfo.st_size = newsize;
104 build_tags_and_nodes (file_buffer);
105 free (page);
106 /* We have just relocated file_buffer->contents from under
107 the feet of info_windows[] array. Therefore, all the
108 nodes on that list which are showing man pages have their
109 contents member pointing into the blue. Undo that harm. */
110 if (old_contents && oldsize && old_contents != file_buffer->contents)
111 {
112 int iw;
113 INFO_WINDOW *info_win;
114 char *old_contents_end = old_contents + oldsize;
115
116 for (iw = 0; (info_win = info_windows[iw]); iw++)
117 {
118 int in;
119
120 for (in = 0; in < info_win->nodes_index; in++)
121 {
122 NODE *tmp_node = info_win->nodes[in];
123
124 /* It really only suffices to see that node->filename
125 is "*manpages*". But after several hours of
126 debugging this, would you blame me for being a bit
127 paranoid? */
128 if (tmp_node && tmp_node->filename
129 && tmp_node->contents
130 && strcmp (tmp_node->filename,
131 MANPAGE_FILE_BUFFER_NAME) == 0
132 && tmp_node->contents >= old_contents
133 && tmp_node->contents + tmp_node->nodelen
134 <= old_contents_end)
135 {
136 info_win->nodes[in] =
137 manpage_node_of_file_buffer (file_buffer,
138 tmp_node->nodename);
139 free (tmp_node->nodename);
140 free (tmp_node);
141 }
142 }
143 }
144 }
145 }
146
147 node = manpage_node_of_file_buffer (file_buffer, pagename);
148 }
149
150 return (node);
151 }
152
153 FILE_BUFFER *
create_manpage_file_buffer(void)154 create_manpage_file_buffer (void)
155 {
156 FILE_BUFFER *file_buffer = make_file_buffer ();
157 file_buffer->filename = xstrdup (MANPAGE_FILE_BUFFER_NAME);
158 file_buffer->fullpath = xstrdup (MANPAGE_FILE_BUFFER_NAME);
159 file_buffer->finfo.st_size = 0;
160 file_buffer->filesize = 0;
161 file_buffer->contents = (char *)NULL;
162 file_buffer->flags = (N_IsInternal | N_CannotGC | N_IsManPage);
163
164 return (file_buffer);
165 }
166
167 /* Scan the list of directories in PATH looking for FILENAME. If we find
168 one that is an executable file, return it as a new string. Otherwise,
169 return a NULL pointer. */
170 static char *
executable_file_in_path(char * filename,char * path)171 executable_file_in_path (char *filename, char *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 *
find_man_formatter(void)225 find_man_formatter (void)
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
get_page_and_section(char * pagename)234 get_page_and_section (char *pagename)
235 {
236 register int i;
237
238 if (manpage_pagename)
239 free (manpage_pagename);
240
241 if (manpage_section)
242 free (manpage_section);
243
244 manpage_pagename = (char *)NULL;
245 manpage_section = (char *)NULL;
246
247 for (i = 0; pagename[i] != '\0' && pagename[i] != '('; i++);
248
249 manpage_pagename = (char *)xmalloc (1 + i);
250 strncpy (manpage_pagename, pagename, i);
251 manpage_pagename[i] = '\0';
252
253 if (pagename[i] == '(')
254 {
255 int start;
256
257 start = i + 1;
258
259 for (i = start; pagename[i] != '\0' && pagename[i] != ')'; i++);
260
261 manpage_section = (char *)xmalloc (1 + (i - start));
262 strncpy (manpage_section, pagename + start, (i - start));
263 manpage_section[i - start] = '\0';
264 }
265 }
266
267 #if PIPE_USE_FORK
268 static void
reap_children(int sig)269 reap_children (int sig)
270 {
271 wait (NULL);
272 }
273 #endif
274
275 static char *
get_manpage_contents(char * pagename)276 get_manpage_contents (char *pagename)
277 {
278 static char *formatter_args[4] = { (char *)NULL };
279 int pipes[2];
280 pid_t child;
281 RETSIGTYPE (*sigsave) (int signum);
282 char *formatted_page = NULL;
283 int arg_index = 1;
284
285 if (formatter_args[0] == (char *)NULL)
286 formatter_args[0] = find_man_formatter ();
287
288 if (formatter_args[0] == (char *)NULL)
289 return ((char *)NULL);
290
291 get_page_and_section (pagename);
292
293 if (manpage_section != (char *)NULL)
294 formatter_args[arg_index++] = manpage_section;
295
296 formatter_args[arg_index++] = manpage_pagename;
297 formatter_args[arg_index] = (char *)NULL;
298
299 /* Open a pipe to this program, read the output, and save it away
300 in FORMATTED_PAGE. The reader end of the pipe is pipes[0]; the
301 writer end is pipes[1]. */
302 #if PIPE_USE_FORK
303 pipe (pipes);
304
305 sigsave = signal (SIGCHLD, reap_children);
306
307 child = fork ();
308 if (child == -1)
309 return ((char *)NULL);
310
311 if (child != 0)
312 {
313 /* In the parent, close the writing end of the pipe, and read from
314 the exec'd child. */
315 close (pipes[1]);
316 formatted_page = read_from_fd (pipes[0]);
317 close (pipes[0]);
318 signal (SIGCHLD, sigsave);
319 }
320 else
321 { /* In the child, close the read end of the pipe, make the write end
322 of the pipe be stdout, and execute the man page formatter. */
323 close (pipes[0]);
324 freopen (NULL_DEVICE, "w", stderr);
325 freopen (NULL_DEVICE, "r", stdin);
326 dup2 (pipes[1], fileno (stdout));
327
328 execv (formatter_args[0], formatter_args);
329
330 /* If we get here, we couldn't exec, so close out the pipe and
331 exit. */
332 close (pipes[1]);
333 xexit (0);
334 }
335 #else /* !PIPE_USE_FORK */
336 /* Cannot fork/exec, but can popen/pclose. */
337 {
338 FILE *fpipe;
339 char *cmdline = xmalloc (strlen (formatter_args[0])
340 + strlen (manpage_pagename)
341 + (arg_index > 2 ? strlen (manpage_section) : 0)
342 + 3);
343 int save_stderr = dup (fileno (stderr));
344 int fd_err = open (NULL_DEVICE, O_WRONLY, 0666);
345
346 if (fd_err > 2)
347 dup2 (fd_err, fileno (stderr)); /* Don't print errors. */
348 sprintf (cmdline, "%s %s %s", formatter_args[0], manpage_pagename,
349 arg_index > 2 ? manpage_section : "");
350 fpipe = popen (cmdline, "r");
351 free (cmdline);
352 if (fd_err > 2)
353 close (fd_err);
354 dup2 (save_stderr, fileno (stderr));
355 if (fpipe == 0)
356 return ((char *)NULL);
357 formatted_page = read_from_fd (fileno (fpipe));
358 if (pclose (fpipe) == -1)
359 {
360 if (formatted_page)
361 free (formatted_page);
362 return ((char *)NULL);
363 }
364 }
365 #endif /* !PIPE_USE_FORK */
366
367 /* If we have the page, then clean it up. */
368 if (formatted_page)
369 clean_manpage (formatted_page);
370
371 return (formatted_page);
372 }
373
374 static void
clean_manpage(char * manpage)375 clean_manpage (char *manpage)
376 {
377 register int i, j;
378 int newline_count = 0;
379 char *newpage;
380
381 newpage = (char *)xmalloc (1 + strlen (manpage));
382
383 for (i = 0, j = 0; (newpage[j] = manpage[i]); i++, j++)
384 {
385 if (manpage[i] == '\n')
386 newline_count++;
387 else
388 newline_count = 0;
389
390 if (newline_count == 3)
391 {
392 j--;
393 newline_count--;
394 }
395
396 /* A malformed man page could have a \b as its first character,
397 in which case decrementing j by 2 will cause us to write into
398 newpage[-1], smashing the hidden info stored there by malloc. */
399 if (manpage[i] == '\b' || (manpage[i] == '\f' && j > 0))
400 j -= 2;
401 else if (!raw_escapes_p)
402 {
403 /* Remove the ANSI escape sequences for color, boldface,
404 underlining, and italics, generated by some versions of
405 Groff. */
406 if (manpage[i] == '\033' && manpage[i + 1] == '['
407 && isdigit (manpage[i + 2]))
408 {
409 if (isdigit (manpage[i + 3]) && manpage[i + 4] == 'm')
410 {
411 i += 4;
412 j--;
413 }
414 else if (manpage[i + 3] == 'm')
415 {
416 i += 3;
417 j--;
418 }
419 /* Else do nothing: it's some unknown escape sequence,
420 so let's leave it alone. */
421 }
422 }
423 }
424
425 newpage[j++] = 0;
426
427 strcpy (manpage, newpage);
428 free (newpage);
429 }
430
431 static NODE *
manpage_node_of_file_buffer(FILE_BUFFER * file_buffer,char * pagename)432 manpage_node_of_file_buffer (FILE_BUFFER *file_buffer, char *pagename)
433 {
434 NODE *node = (NODE *)NULL;
435 TAG *tag = (TAG *)NULL;
436
437 if (file_buffer->contents)
438 {
439 register int i;
440
441 for (i = 0; (tag = file_buffer->tags[i]); i++)
442 {
443 if (strcasecmp (pagename, tag->nodename) == 0)
444 break;
445 }
446 }
447
448 if (tag)
449 {
450 node = (NODE *)xmalloc (sizeof (NODE));
451 node->filename = file_buffer->filename;
452 node->nodename = xstrdup (tag->nodename);
453 node->contents = file_buffer->contents + tag->nodestart;
454 node->nodelen = tag->nodelen;
455 node->flags = 0;
456 node->display_pos = 0;
457 node->parent = (char *)NULL;
458 node->flags = (N_HasTagsTable | N_IsManPage);
459 node->contents += skip_node_separator (node->contents);
460 }
461
462 return (node);
463 }
464
465 static char *
read_from_fd(int fd)466 read_from_fd (int fd)
467 {
468 struct timeval timeout;
469 char *buffer = (char *)NULL;
470 int bsize = 0;
471 int bindex = 0;
472 int select_result;
473 #if defined (FD_SET)
474 fd_set read_fds;
475
476 timeout.tv_sec = 15;
477 timeout.tv_usec = 0;
478
479 FD_ZERO (&read_fds);
480 FD_SET (fd, &read_fds);
481
482 select_result = select (fd + 1, fd_set_cast (&read_fds), 0, 0, &timeout);
483 #else /* !FD_SET */
484 select_result = 1;
485 #endif /* !FD_SET */
486
487 switch (select_result)
488 {
489 case 0:
490 case -1:
491 break;
492
493 default:
494 {
495 int amount_read;
496 int done = 0;
497
498 while (!done)
499 {
500 while ((bindex + 1024) > (bsize))
501 buffer = (char *)xrealloc (buffer, (bsize += 1024));
502 buffer[bindex] = '\0';
503
504 amount_read = read (fd, buffer + bindex, 1023);
505
506 if (amount_read < 0)
507 {
508 done = 1;
509 }
510 else
511 {
512 bindex += amount_read;
513 buffer[bindex] = '\0';
514 if (amount_read == 0)
515 done = 1;
516 }
517 }
518 }
519 }
520
521 if ((buffer != (char *)NULL) && (*buffer == '\0'))
522 {
523 free (buffer);
524 buffer = (char *)NULL;
525 }
526
527 return (buffer);
528 }
529
530 static char *reference_section_starters[] =
531 {
532 "\nRELATED INFORMATION",
533 "\nRELATED\tINFORMATION",
534 "RELATED INFORMATION\n",
535 "RELATED\tINFORMATION\n",
536 "\nSEE ALSO",
537 "\nSEE\tALSO",
538 "SEE ALSO\n",
539 "SEE\tALSO\n",
540 (char *)NULL
541 };
542
543 static SEARCH_BINDING frs_binding;
544
545 static SEARCH_BINDING *
find_reference_section(NODE * node)546 find_reference_section (NODE *node)
547 {
548 register int i;
549 long position = -1;
550
551 frs_binding.buffer = node->contents;
552 frs_binding.start = 0;
553 frs_binding.end = node->nodelen;
554 frs_binding.flags = S_SkipDest;
555
556 for (i = 0; reference_section_starters[i] != (char *)NULL; i++)
557 {
558 position = search_forward (reference_section_starters[i], &frs_binding);
559 if (position != -1)
560 break;
561 }
562
563 if (position == -1)
564 return ((SEARCH_BINDING *)NULL);
565
566 /* We found the start of the reference section, and point is right after
567 the string which starts it. The text from here to the next header
568 (or end of buffer) contains the only references in this manpage. */
569 frs_binding.start = position;
570
571 for (i = frs_binding.start; i < frs_binding.end - 2; i++)
572 {
573 if ((frs_binding.buffer[i] == '\n') &&
574 (!whitespace (frs_binding.buffer[i + 1])))
575 {
576 frs_binding.end = i;
577 break;
578 }
579 }
580
581 return (&frs_binding);
582 }
583
584 REFERENCE **
xrefs_of_manpage(NODE * node)585 xrefs_of_manpage (NODE *node)
586 {
587 SEARCH_BINDING *reference_section;
588 REFERENCE **refs = (REFERENCE **)NULL;
589 int refs_index = 0;
590 int refs_slots = 0;
591 long position;
592
593 reference_section = find_reference_section (node);
594
595 if (reference_section == (SEARCH_BINDING *)NULL)
596 return ((REFERENCE **)NULL);
597
598 /* Grovel the reference section building a list of references found there.
599 A reference is alphabetic characters followed by non-whitespace text
600 within parenthesis. */
601 reference_section->flags = 0;
602
603 while ((position = search_forward ("(", reference_section)) != -1)
604 {
605 register int start, end;
606
607 for (start = position; start > reference_section->start; start--)
608 if (whitespace (reference_section->buffer[start]))
609 break;
610
611 start++;
612
613 for (end = position; end < reference_section->end; end++)
614 {
615 if (whitespace (reference_section->buffer[end]))
616 {
617 end = start;
618 break;
619 }
620
621 if (reference_section->buffer[end] == ')')
622 {
623 end++;
624 break;
625 }
626 }
627
628 if (end != start)
629 {
630 REFERENCE *entry;
631 int len = end - start;
632
633 entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
634 entry->label = (char *)xmalloc (1 + len);
635 strncpy (entry->label, (reference_section->buffer) + start, len);
636 entry->label[len] = '\0';
637 entry->filename = xstrdup (node->filename);
638 entry->nodename = xstrdup (entry->label);
639 entry->start = start;
640 entry->end = end;
641
642 add_pointer_to_array
643 (entry, refs_index, refs, refs_slots, 10, REFERENCE *);
644 }
645
646 reference_section->start = position + 1;
647 }
648
649 return (refs);
650 }
651
652 long
locate_manpage_xref(NODE * node,long int start,int dir)653 locate_manpage_xref (NODE *node, long int start, int dir)
654 {
655 REFERENCE **refs;
656 long position = -1;
657
658 refs = xrefs_of_manpage (node);
659
660 if (refs)
661 {
662 register int i, count;
663 REFERENCE *entry;
664
665 for (i = 0; refs[i]; i++);
666 count = i;
667
668 if (dir > 0)
669 {
670 for (i = 0; (entry = refs[i]); i++)
671 if (entry->start > start)
672 {
673 position = entry->start;
674 break;
675 }
676 }
677 else
678 {
679 for (i = count - 1; i > -1; i--)
680 {
681 entry = refs[i];
682
683 if (entry->start < start)
684 {
685 position = entry->start;
686 break;
687 }
688 }
689 }
690
691 info_free_references (refs);
692 }
693 return (position);
694 }
695
696 /* This one was a little tricky. The binding buffer that is passed in has
697 a START and END value of 0 -- strlen (window-line-containing-point).
698 The BUFFER is a pointer to the start of that line. */
699 REFERENCE **
manpage_xrefs_in_binding(NODE * node,SEARCH_BINDING * binding)700 manpage_xrefs_in_binding (NODE *node, SEARCH_BINDING *binding)
701 {
702 register int i;
703 REFERENCE **all_refs = xrefs_of_manpage (node);
704 REFERENCE **brefs = (REFERENCE **)NULL;
705 REFERENCE *entry;
706 int brefs_index = 0;
707 int brefs_slots = 0;
708 int start, end;
709
710 if (!all_refs)
711 return ((REFERENCE **)NULL);
712
713 start = binding->start + (binding->buffer - node->contents);
714 end = binding->end + (binding->buffer - node->contents);
715
716 for (i = 0; (entry = all_refs[i]); i++)
717 {
718 if ((entry->start > start) && (entry->end < end))
719 {
720 add_pointer_to_array
721 (entry, brefs_index, brefs, brefs_slots, 10, REFERENCE *);
722 }
723 else
724 {
725 maybe_free (entry->label);
726 maybe_free (entry->filename);
727 maybe_free (entry->nodename);
728 free (entry);
729 }
730 }
731
732 free (all_refs);
733 return (brefs);
734 }
735