source: trunk/texinfo/info/man.c@ 2619

Last change on this file since 2619 was 2619, checked in by bird, 19 years ago

applied OS/2 patches (manually).

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