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