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