man.c revision 21495
1/*  man.c: How to read and format man files. */
2
3/* This file is part of GNU Info, a program for reading online documentation
4   stored in Info format.
5
6   Copyright (C) 1995 Free Software Foundation, Inc.
7
8   This program is free software; you can redistribute it and/or modify
9   it under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 2, or (at your option)
11   any later version.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with this program; if not, write to the Free Software
20   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21
22   Written by Brian Fox Thu May  4 09:17:52 1995 (bfox@ai.mit.edu). */
23
24#include "info.h"
25#include <sys/ioctl.h>
26#include <sys/file.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#include "tilde.h"
35
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
50static char *read_from_fd ();
51static void clean_manpage ();
52static NODE *manpage_node_of_file_buffer ();
53static char *get_manpage_contents ();
54
55NODE *
56make_manpage_node (pagename)
57     char *pagename;
58{
59  return (info_get_node (MANPAGE_FILE_BUFFER_NAME, pagename));
60}
61
62NODE *
63get_manpage_node (file_buffer, pagename)
64     FILE_BUFFER *file_buffer;
65     char *pagename;
66{
67  NODE *node;
68
69  node = manpage_node_of_file_buffer (file_buffer, pagename);
70
71  if (!node)
72    {
73      char *page;
74
75      page = get_manpage_contents (pagename);
76
77      if (page)
78	{
79	  char header[1024];
80	  long oldsize, newsize;
81	  int hlen, plen;
82
83	  sprintf (header, "\n\n%c\n%s %s,  %s %s,  %s (dir)\n\n",
84		   INFO_COOKIE,
85		   INFO_FILE_LABEL, file_buffer->filename,
86		   INFO_NODE_LABEL, pagename,
87		   INFO_UP_LABEL);
88	  oldsize = file_buffer->filesize;
89	  hlen = strlen (header);
90	  plen = strlen (page);
91	  newsize = (oldsize + hlen + plen);
92	  file_buffer->contents =
93	    (char *)xrealloc (file_buffer->contents, 1 + newsize);
94	  memcpy (file_buffer->contents + oldsize, header, hlen);
95	  oldsize += hlen;
96	  memcpy (file_buffer->contents + oldsize, page, plen);
97	  file_buffer->contents[newsize] = '\0';
98	  file_buffer->filesize = newsize;
99	  file_buffer->finfo.st_size = newsize;
100	  build_tags_and_nodes (file_buffer);
101	  free (page);
102	}
103
104      node = manpage_node_of_file_buffer (file_buffer, pagename);
105    }
106
107  return (node);
108}
109
110FILE_BUFFER *
111create_manpage_file_buffer ()
112{
113  FILE_BUFFER *file_buffer;
114  struct stat *finfo;
115
116  file_buffer = make_file_buffer ();
117  file_buffer->filename = strdup (MANPAGE_FILE_BUFFER_NAME);
118  file_buffer->fullpath = strdup (MANPAGE_FILE_BUFFER_NAME);
119  file_buffer->finfo.st_size = 0;
120  file_buffer->filesize = 0;
121  file_buffer->contents = (char *)NULL;
122  file_buffer->flags = (N_IsInternal | N_CannotGC | N_IsManPage);
123
124  return (file_buffer);
125}
126
127/* Scan the list of directories in PATH looking for FILENAME.  If we find
128   one that is an executable file, return it as a new string.  Otherwise,
129   return a NULL pointer. */
130static char *
131executable_file_in_path (filename, path)
132     char *filename, *path;
133{
134  struct stat finfo;
135  char *temp_dirname;
136  int statable, dirname_index;
137
138  dirname_index = 0;
139
140  while (temp_dirname = extract_colon_unit (path, &dirname_index))
141    {
142      register int i;
143      char *temp;
144
145      /* Expand a leading tilde if one is present. */
146      if (*temp_dirname == '~')
147	{
148	  char *expanded_dirname;
149
150	  expanded_dirname = tilde_expand_word (temp_dirname);
151	  free (temp_dirname);
152	  temp_dirname = expanded_dirname;
153	}
154
155      temp = (char *)xmalloc (30 + strlen (temp_dirname) + strlen (filename));
156      strcpy (temp, temp_dirname);
157      if (temp[(strlen (temp)) - 1] != '/')
158	strcat (temp, "/");
159      strcat (temp, filename);
160
161      free (temp_dirname);
162
163      statable = (stat (temp, &finfo) == 0);
164
165      /* If we have found a regular executable file, then use it. */
166      if ((statable) && (S_ISREG (finfo.st_mode)) &&
167	  (access (temp, X_OK) == 0))
168	return (temp);
169      else
170	free (temp);
171    }
172  return ((char *)NULL);
173}
174
175/* Return the full pathname of the system man page formatter. */
176static char *
177find_man_formatter ()
178{
179  return (executable_file_in_path ("man", (char *)getenv ("PATH")));
180}
181
182static char *manpage_pagename = (char *)NULL;
183static char *manpage_section  = (char *)NULL;
184
185static void
186get_page_and_section (pagename)
187     char *pagename;
188{
189  register int i;
190
191  if (manpage_pagename)
192    free (manpage_pagename);
193
194  if (manpage_section)
195    free (manpage_section);
196
197  manpage_pagename = (char *)NULL;
198  manpage_section  = (char *)NULL;
199
200  for (i = 0; pagename[i] != '\0' && pagename[i] != '('; i++);
201
202  manpage_pagename = (char *)xmalloc (1 + i);
203  strncpy (manpage_pagename, pagename, i);
204  manpage_pagename[i] = '\0';
205
206  if (pagename[i] == '(')
207    {
208      int start;
209
210      start = i + 1;
211
212      for (i = start; pagename[i] != '\0' && pagename[i] != ')'; i++);
213
214      manpage_section = (char *)xmalloc (1 + (i - start));
215      strncpy (manpage_section, pagename + start, (i - start));
216      manpage_section[i - start] = '\0';
217    }
218}
219
220static void
221reap_children (sig)
222     int sig;
223{
224  unsigned int status;
225  wait (&status);
226}
227
228static char *
229get_manpage_contents (pagename)
230     char *pagename;
231{
232  static char *formatter_args[4] = { (char *)NULL };
233  int pipes[2];
234  pid_t child;
235  char *formatted_page = (char *)NULL;
236  char *section = (char *)NULL;
237  int arg_index = 1;
238
239  if (formatter_args[0] == (char *)NULL)
240    formatter_args[0] = find_man_formatter ();
241
242  if (formatter_args[0] == (char *)NULL)
243    return ((char *)NULL);
244
245  get_page_and_section (pagename);
246
247  if (manpage_section != (char *)NULL)
248    formatter_args[arg_index++] = manpage_section;
249
250  formatter_args[arg_index++] = manpage_pagename;
251  formatter_args[arg_index] = (char *)NULL;
252
253  /* Open a pipe to this program, read the output, and save it away
254     in FORMATTED_PAGE.  The reader end of the pipe is pipes[0]; the
255     writer end is pipes[1]. */
256  pipe (pipes);
257
258  signal (SIGCHLD, reap_children);
259
260  child = fork ();
261
262  if (child == -1)
263    return ((char *)NULL);
264
265  if (child != 0)
266    {
267      /* In the parent, close the writing end of the pipe, and read from
268	 the exec'd child. */
269      close (pipes[1]);
270      formatted_page = read_from_fd (pipes[0]);
271      close (pipes[0]);
272    }
273  else
274    {
275      /* In the child, close the read end of the pipe, make the write end
276	 of the pipe be stdout, and execute the man page formatter. */
277      close (pipes[0]);
278      close (fileno (stderr));
279      close (fileno (stdin));	/* Don't print errors. */
280      dup2 (pipes[1], fileno (stdout));
281
282      execv (formatter_args[0], formatter_args);
283
284      /* If we get here, we couldn't exec, so close out the pipe and
285	 exit. */
286      close (pipes[1]);
287      exit (0);
288    }
289
290  /* If we have the page, then clean it up. */
291  if (formatted_page)
292    clean_manpage (formatted_page);
293
294  return (formatted_page);
295}
296
297static void
298clean_manpage (manpage)
299     char *manpage;
300{
301  register int i, j;
302  int newline_count = 0;
303  char *newpage;
304
305  newpage = (char *)xmalloc (1 + strlen (manpage));
306
307  for (i = 0, j = 0; newpage[j] = manpage[i]; i++, j++)
308    {
309      if (manpage[i] == '\n')
310	newline_count++;
311      else
312	newline_count = 0;
313
314      if (newline_count == 3)
315	{
316	  j--;
317	  newline_count--;
318	}
319
320      if (manpage[i] == '\b' || manpage[i] == '\f')
321	j -= 2;
322    }
323
324  newpage[j++] = '\0';
325
326  strcpy (manpage, newpage);
327  free (newpage);
328}
329
330static NODE *
331manpage_node_of_file_buffer (file_buffer, pagename)
332     FILE_BUFFER *file_buffer;
333     char *pagename;
334{
335  NODE *node = (NODE *)NULL;
336  TAG *tag = (TAG *)NULL;
337
338  if (file_buffer->contents)
339    {
340      register int i;
341
342      for (i = 0; tag = file_buffer->tags[i]; i++)
343	{
344	  if (strcasecmp (pagename, tag->nodename) == 0)
345	    break;
346	}
347    }
348
349  if (tag)
350    {
351      node = (NODE *)xmalloc (sizeof (NODE));
352      node->filename = file_buffer->filename;
353      node->nodename = tag->nodename;
354      node->contents = file_buffer->contents + tag->nodestart;
355      node->nodelen = tag->nodelen;
356      node->flags    = 0;
357      node->parent   = (char *)NULL;
358      node->flags = (N_HasTagsTable | N_IsManPage);
359      node->contents += skip_node_separator (node->contents);
360    }
361
362  return (node);
363}
364
365static char *
366read_from_fd (fd)
367     int fd;
368{
369  struct timeval timeout;
370  char *buffer = (char *)NULL;
371  int bsize = 0;
372  int bindex = 0;
373  int select_result;
374#if defined (FD_SET)
375  fd_set read_fds;
376
377  timeout.tv_sec = 15;
378  timeout.tv_usec = 0;
379
380  FD_ZERO (&read_fds);
381  FD_SET (fd, &read_fds);
382
383  select_result = select (fd + 1, fd_set_cast (&read_fds), 0, 0, &timeout);
384#else /* !FD_SET */
385  select_result = 1;
386#endif /* !FD_SET */
387
388  switch (select_result)
389    {
390    case 0:
391    case -1:
392      break;
393
394    default:
395      {
396        int amount_read;
397        int done = 0;
398
399        while (!done)
400          {
401            while ((bindex + 1024) > (bsize))
402              buffer = (char *)xrealloc (buffer, (bsize += 1024));
403            buffer[bindex] = '\0';
404
405            amount_read = read (fd, buffer + bindex, 1023);
406
407            if (amount_read < 0)
408              {
409                done = 1;
410              }
411            else
412              {
413                bindex += amount_read;
414                buffer[bindex] = '\0';
415                if (amount_read == 0)
416                  done = 1;
417              }
418          }
419      }
420    }
421
422  if ((buffer != (char *)NULL) && (*buffer == '\0'))
423    {
424      free (buffer);
425      buffer = (char *)NULL;
426    }
427
428  return (buffer);
429}
430
431static char *reference_section_starters[] =
432{
433  "\nRELATED INFORMATION",
434  "\nRELATED\tINFORMATION",
435  "RELATED INFORMATION\n",
436  "RELATED\tINFORMATION\n",
437  "\nSEE ALSO",
438  "\nSEE\tALSO",
439  "SEE ALSO\n",
440  "SEE\tALSO\n",
441  (char *)NULL
442};
443
444static SEARCH_BINDING frs_binding;
445
446static SEARCH_BINDING *
447find_reference_section (node)
448     NODE *node;
449{
450  register int i;
451  long position = -1;
452
453  frs_binding.buffer = node->contents;
454  frs_binding.start = 0;
455  frs_binding.end = node->nodelen;
456  frs_binding.flags = S_SkipDest;
457
458  for (i = 0; reference_section_starters[i] != (char *)NULL; i++)
459    {
460      position = search_forward (reference_section_starters[i], &frs_binding);
461      if (position != -1)
462	break;
463    }
464
465  if (position == -1)
466    return ((SEARCH_BINDING *)NULL);
467
468  /* We found the start of the reference section, and point is right after
469     the string which starts it.  The text from here to the next header
470     (or end of buffer) contains the only references in this manpage. */
471  frs_binding.start = position;
472
473  for (i = frs_binding.start; i < frs_binding.end - 2; i++)
474    {
475      if ((frs_binding.buffer[i] == '\n') &&
476	  (!whitespace (frs_binding.buffer[i + 1])))
477	{
478	  frs_binding.end = i;
479	  break;
480	}
481    }
482
483  return (&frs_binding);
484}
485
486REFERENCE **
487xrefs_of_manpage (node)
488     NODE *node;
489{
490  SEARCH_BINDING *reference_section;
491  REFERENCE **refs = (REFERENCE **)NULL;
492  int refs_index = 0;
493  int refs_slots = 0;
494  long position;
495
496  reference_section = find_reference_section (node);
497
498  if (reference_section == (SEARCH_BINDING *)NULL)
499    return ((REFERENCE **)NULL);
500
501  /* Grovel the reference section building a list of references found there.
502     A reference is alphabetic characters followed by non-whitespace text
503     within parenthesis. */
504  reference_section->flags = 0;
505
506  while ((position = search_forward ("(", reference_section)) != -1)
507    {
508      register int start, end;
509
510      for (start = position; start > reference_section->start; start--)
511	if (whitespace (reference_section->buffer[start]))
512	  break;
513
514      start++;
515
516      for (end = position; end < reference_section->end; end++)
517	{
518	  if (whitespace (reference_section->buffer[end]))
519	    {
520	      end = start;
521	      break;
522	    }
523
524	  if (reference_section->buffer[end] == ')')
525	    {
526	      end++;
527	      break;
528	    }
529	}
530
531      if (end != start)
532	{
533	  REFERENCE *entry;
534	  int len = end - start;
535
536	  entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
537	  entry->label = (char *)xmalloc (1 + len);
538	  strncpy (entry->label, (reference_section->buffer) + start, len);
539	  entry->label[len] = '\0';
540	  entry->filename = strdup (node->filename);
541	  entry->nodename = strdup (entry->label);
542	  entry->start = start;
543	  entry->end = end;
544
545	  add_pointer_to_array
546	    (entry, refs_index, refs, refs_slots, 10, REFERENCE *);
547	}
548
549      reference_section->start = position + 1;
550    }
551
552  return (refs);
553}
554
555long
556locate_manpage_xref (node, start, dir)
557     NODE *node;
558     long start;
559     int dir;
560{
561  register int i, count;
562  REFERENCE **refs;
563  long position = -1;
564
565  refs = xrefs_of_manpage (node);
566
567  if (refs)
568    {
569      register int i, count;
570      REFERENCE *entry;
571
572      for (i = 0; refs[i]; i++);
573      count = i;
574
575      if (dir > 0)
576	{
577	  for (i = 0; entry = refs[i]; i++)
578	    if (entry->start > start)
579	      {
580		position = entry->start;
581		break;
582	      }
583	}
584      else
585	{
586	  for (i = count - 1; i > -1; i--)
587	    {
588	      entry = refs[i];
589
590	      if (entry->start < start)
591		{
592		  position = entry->start;
593		  break;
594		}
595	    }
596	}
597
598      info_free_references (refs);
599    }
600  return (position);
601}
602
603/* This one was a little tricky.  The binding buffer that is passed in has
604   a START and END value of 0 -- strlen (window-line-containing-point).
605   The BUFFER is a pointer to the start of that line. */
606REFERENCE **
607manpage_xrefs_in_binding (node, binding)
608     NODE *node;
609     SEARCH_BINDING *binding;
610{
611  register int i;
612  REFERENCE **all_refs = xrefs_of_manpage (node);
613  REFERENCE **brefs = (REFERENCE **)NULL;
614  REFERENCE *entry;
615  int brefs_index = 0;
616  int brefs_slots = 0;
617  int start, end;
618
619  if (!all_refs)
620    return ((REFERENCE **)NULL);
621
622  start = binding->start + (binding->buffer - node->contents);
623  end = binding->end + (binding->buffer - node->contents);
624
625  for (i = 0; entry = all_refs[i]; i++)
626    {
627      if ((entry->start > start) && (entry->end < end))
628	{
629	  add_pointer_to_array
630	    (entry, brefs_index, brefs, brefs_slots, 10, REFERENCE *);
631	}
632      else
633	{
634	  maybe_free (entry->label);
635	  maybe_free (entry->filename);
636	  maybe_free (entry->nodename);
637	  free (entry);
638	}
639    }
640
641  free (all_refs);
642  return (brefs);
643}
644