xref: /openbsd-src/gnu/usr.bin/texinfo/info/man.c (revision 1076333c323c9f213f0d653fc52002328f47dbe9)
1840175f0Skstailey /*  man.c: How to read and format man files.
2*1076333cSespie     $Id: man.c,v 1.5 2006/07/17 16:12:36 espie Exp $
3fbc94a17Sniklas 
4*1076333cSespie    Copyright (C) 1995, 1997, 1998, 1999, 2000, 2002, 2003, 2004 Free Software
5*1076333cSespie    Foundation, Inc.
6fbc94a17Sniklas 
7fbc94a17Sniklas    This program is free software; you can redistribute it and/or modify
8fbc94a17Sniklas    it under the terms of the GNU General Public License as published by
9fbc94a17Sniklas    the Free Software Foundation; either version 2, or (at your option)
10fbc94a17Sniklas    any later version.
11fbc94a17Sniklas 
12fbc94a17Sniklas    This program is distributed in the hope that it will be useful,
13fbc94a17Sniklas    but WITHOUT ANY WARRANTY; without even the implied warranty of
14fbc94a17Sniklas    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15fbc94a17Sniklas    GNU General Public License for more details.
16fbc94a17Sniklas 
17fbc94a17Sniklas    You should have received a copy of the GNU General Public License
18fbc94a17Sniklas    along with this program; if not, write to the Free Software
19fbc94a17Sniklas    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20fbc94a17Sniklas 
21fbc94a17Sniklas    Written by Brian Fox Thu May  4 09:17:52 1995 (bfox@ai.mit.edu). */
22fbc94a17Sniklas 
23fbc94a17Sniklas #include "info.h"
24fbc94a17Sniklas #include <sys/ioctl.h>
25fbc94a17Sniklas #include "signals.h"
26fbc94a17Sniklas #if defined (HAVE_SYS_TIME_H)
27fbc94a17Sniklas #include <sys/time.h>
28fbc94a17Sniklas #endif
29fbc94a17Sniklas #if defined (HAVE_SYS_WAIT_H)
30fbc94a17Sniklas #include <sys/wait.h>
31fbc94a17Sniklas #endif
32fbc94a17Sniklas 
33840175f0Skstailey #include "tilde.h"
34fbc94a17Sniklas #include "man.h"
35fbc94a17Sniklas 
36fbc94a17Sniklas #if !defined (_POSIX_VERSION)
37fbc94a17Sniklas #define pid_t int
38fbc94a17Sniklas #endif
39fbc94a17Sniklas 
40fbc94a17Sniklas #if defined (FD_SET)
41fbc94a17Sniklas #  if defined (hpux)
42fbc94a17Sniklas #    define fd_set_cast(x) (int *)(x)
43fbc94a17Sniklas #  else
44fbc94a17Sniklas #    define fd_set_cast(x) (fd_set *)(x)
45fbc94a17Sniklas #  endif /* !hpux */
46fbc94a17Sniklas #endif /* FD_SET */
47fbc94a17Sniklas 
48672dff93Sespie #if STRIP_DOT_EXE
49672dff93Sespie static char const * const exec_extensions[] = {
50672dff93Sespie   ".exe", ".com", ".bat", ".btm", ".sh", ".ksh", ".pl", ".sed", "", NULL
51672dff93Sespie };
52672dff93Sespie #else
53672dff93Sespie static char const * const exec_extensions[] = { "", NULL };
54672dff93Sespie #endif
55672dff93Sespie 
56*1076333cSespie static char *read_from_fd (int fd);
57*1076333cSespie static void clean_manpage (char *manpage);
58*1076333cSespie static NODE *manpage_node_of_file_buffer (FILE_BUFFER *file_buffer,
59*1076333cSespie     char *pagename);
60*1076333cSespie static char *get_manpage_contents (char *pagename);
61fbc94a17Sniklas 
62fbc94a17Sniklas NODE *
make_manpage_node(char * pagename)63*1076333cSespie make_manpage_node (char *pagename)
64fbc94a17Sniklas {
65fbc94a17Sniklas   return (info_get_node (MANPAGE_FILE_BUFFER_NAME, pagename));
66fbc94a17Sniklas }
67fbc94a17Sniklas 
68fbc94a17Sniklas NODE *
get_manpage_node(FILE_BUFFER * file_buffer,char * pagename)69*1076333cSespie get_manpage_node (FILE_BUFFER *file_buffer, char *pagename)
70fbc94a17Sniklas {
71fbc94a17Sniklas   NODE *node;
72fbc94a17Sniklas 
73fbc94a17Sniklas   node = manpage_node_of_file_buffer (file_buffer, pagename);
74fbc94a17Sniklas 
75fbc94a17Sniklas   if (!node)
76fbc94a17Sniklas     {
77fbc94a17Sniklas       char *page;
78fbc94a17Sniklas 
79fbc94a17Sniklas       page = get_manpage_contents (pagename);
80fbc94a17Sniklas 
81fbc94a17Sniklas       if (page)
82fbc94a17Sniklas         {
83fbc94a17Sniklas           char header[1024];
84fbc94a17Sniklas           long oldsize, newsize;
85fbc94a17Sniklas           int hlen, plen;
86672dff93Sespie 	  char *old_contents = file_buffer->contents;
87fbc94a17Sniklas 
88fbc94a17Sniklas           sprintf (header, "\n\n%c\n%s %s,  %s %s,  %s (dir)\n\n",
89fbc94a17Sniklas                    INFO_COOKIE,
90fbc94a17Sniklas                    INFO_FILE_LABEL, file_buffer->filename,
91fbc94a17Sniklas                    INFO_NODE_LABEL, pagename,
92fbc94a17Sniklas                    INFO_UP_LABEL);
93fbc94a17Sniklas           oldsize = file_buffer->filesize;
94fbc94a17Sniklas           hlen = strlen (header);
95fbc94a17Sniklas           plen = strlen (page);
96fbc94a17Sniklas           newsize = (oldsize + hlen + plen);
97fbc94a17Sniklas           file_buffer->contents =
98fbc94a17Sniklas             (char *)xrealloc (file_buffer->contents, 1 + newsize);
99fbc94a17Sniklas           memcpy (file_buffer->contents + oldsize, header, hlen);
100672dff93Sespie           memcpy (file_buffer->contents + oldsize + hlen, page, plen);
101fbc94a17Sniklas           file_buffer->contents[newsize] = '\0';
102fbc94a17Sniklas           file_buffer->filesize = newsize;
103fbc94a17Sniklas           file_buffer->finfo.st_size = newsize;
104fbc94a17Sniklas           build_tags_and_nodes (file_buffer);
105fbc94a17Sniklas           free (page);
106672dff93Sespie 	  /* We have just relocated file_buffer->contents from under
107672dff93Sespie 	     the feet of info_windows[] array.  Therefore, all the
108672dff93Sespie 	     nodes on that list which are showing man pages have their
109672dff93Sespie 	     contents member pointing into the blue.  Undo that harm.  */
110672dff93Sespie 	  if (old_contents && oldsize && old_contents != file_buffer->contents)
111672dff93Sespie 	    {
112672dff93Sespie 	      int iw;
113672dff93Sespie 	      INFO_WINDOW *info_win;
114672dff93Sespie 	      char *old_contents_end = old_contents + oldsize;
115672dff93Sespie 
116672dff93Sespie 	      for (iw = 0; (info_win = info_windows[iw]); iw++)
117672dff93Sespie 		{
118672dff93Sespie 		  int in;
119672dff93Sespie 
120672dff93Sespie 		  for (in = 0; in < info_win->nodes_index; in++)
121672dff93Sespie 		    {
122*1076333cSespie 		      NODE *tmp_node = info_win->nodes[in];
123672dff93Sespie 
124672dff93Sespie 		      /* It really only suffices to see that node->filename
125672dff93Sespie 			 is "*manpages*".  But after several hours of
126672dff93Sespie 			 debugging this, would you blame me for being a bit
127672dff93Sespie 			 paranoid?  */
128*1076333cSespie 		      if (tmp_node && tmp_node->filename
129*1076333cSespie                           && tmp_node->contents
130*1076333cSespie                           && strcmp (tmp_node->filename,
131*1076333cSespie 				  MANPAGE_FILE_BUFFER_NAME) == 0
132*1076333cSespie                           && tmp_node->contents >= old_contents
133*1076333cSespie                           && tmp_node->contents + tmp_node->nodelen
134*1076333cSespie                                 <= old_contents_end)
135672dff93Sespie 			{
136672dff93Sespie 			  info_win->nodes[in] =
137672dff93Sespie 			    manpage_node_of_file_buffer (file_buffer,
138*1076333cSespie                                 tmp_node->nodename);
139*1076333cSespie 			  free (tmp_node->nodename);
140*1076333cSespie 			  free (tmp_node);
141672dff93Sespie 			}
142672dff93Sespie 		    }
143672dff93Sespie 		}
144672dff93Sespie 	    }
145fbc94a17Sniklas         }
146fbc94a17Sniklas 
147fbc94a17Sniklas       node = manpage_node_of_file_buffer (file_buffer, pagename);
148fbc94a17Sniklas     }
149fbc94a17Sniklas 
150fbc94a17Sniklas   return (node);
151fbc94a17Sniklas }
152fbc94a17Sniklas 
153fbc94a17Sniklas FILE_BUFFER *
create_manpage_file_buffer(void)154*1076333cSespie create_manpage_file_buffer (void)
155fbc94a17Sniklas {
156840175f0Skstailey   FILE_BUFFER *file_buffer = make_file_buffer ();
157840175f0Skstailey   file_buffer->filename = xstrdup (MANPAGE_FILE_BUFFER_NAME);
158840175f0Skstailey   file_buffer->fullpath = xstrdup (MANPAGE_FILE_BUFFER_NAME);
159fbc94a17Sniklas   file_buffer->finfo.st_size = 0;
160fbc94a17Sniklas   file_buffer->filesize = 0;
161fbc94a17Sniklas   file_buffer->contents = (char *)NULL;
162fbc94a17Sniklas   file_buffer->flags = (N_IsInternal | N_CannotGC | N_IsManPage);
163fbc94a17Sniklas 
164fbc94a17Sniklas   return (file_buffer);
165fbc94a17Sniklas }
166fbc94a17Sniklas 
167fbc94a17Sniklas /* Scan the list of directories in PATH looking for FILENAME.  If we find
168fbc94a17Sniklas    one that is an executable file, return it as a new string.  Otherwise,
169fbc94a17Sniklas    return a NULL pointer. */
170fbc94a17Sniklas static char *
executable_file_in_path(char * filename,char * path)171*1076333cSespie executable_file_in_path (char *filename, char *path)
172fbc94a17Sniklas {
173fbc94a17Sniklas   struct stat finfo;
174fbc94a17Sniklas   char *temp_dirname;
175fbc94a17Sniklas   int statable, dirname_index;
176fbc94a17Sniklas 
177fbc94a17Sniklas   dirname_index = 0;
178fbc94a17Sniklas 
179840175f0Skstailey   while ((temp_dirname = extract_colon_unit (path, &dirname_index)))
180fbc94a17Sniklas     {
181fbc94a17Sniklas       char *temp;
182672dff93Sespie       char *temp_end;
183672dff93Sespie       int i;
184fbc94a17Sniklas 
185fbc94a17Sniklas       /* Expand a leading tilde if one is present. */
186fbc94a17Sniklas       if (*temp_dirname == '~')
187fbc94a17Sniklas         {
188fbc94a17Sniklas           char *expanded_dirname;
189fbc94a17Sniklas 
190fbc94a17Sniklas           expanded_dirname = tilde_expand_word (temp_dirname);
191fbc94a17Sniklas           free (temp_dirname);
192fbc94a17Sniklas           temp_dirname = expanded_dirname;
193fbc94a17Sniklas         }
194fbc94a17Sniklas 
195672dff93Sespie       temp = (char *)xmalloc (34 + strlen (temp_dirname) + strlen (filename));
196fbc94a17Sniklas       strcpy (temp, temp_dirname);
197672dff93Sespie       if (!IS_SLASH (temp[(strlen (temp)) - 1]))
198fbc94a17Sniklas         strcat (temp, "/");
199fbc94a17Sniklas       strcat (temp, filename);
200672dff93Sespie       temp_end = temp + strlen (temp);
201fbc94a17Sniklas 
202fbc94a17Sniklas       free (temp_dirname);
203fbc94a17Sniklas 
204672dff93Sespie       /* Look for FILENAME, possibly with any of the extensions
205672dff93Sespie 	 in EXEC_EXTENSIONS[].  */
206672dff93Sespie       for (i = 0; exec_extensions[i]; i++)
207672dff93Sespie 	{
208672dff93Sespie 	  if (exec_extensions[i][0])
209672dff93Sespie 	    strcpy (temp_end, exec_extensions[i]);
210fbc94a17Sniklas 	  statable = (stat (temp, &finfo) == 0);
211fbc94a17Sniklas 
212fbc94a17Sniklas 	  /* If we have found a regular executable file, then use it. */
213fbc94a17Sniklas 	  if ((statable) && (S_ISREG (finfo.st_mode)) &&
214fbc94a17Sniklas 	      (access (temp, X_OK) == 0))
215fbc94a17Sniklas 	    return (temp);
216672dff93Sespie 	}
217672dff93Sespie 
218fbc94a17Sniklas       free (temp);
219fbc94a17Sniklas     }
220fbc94a17Sniklas   return ((char *)NULL);
221fbc94a17Sniklas }
222fbc94a17Sniklas 
223fbc94a17Sniklas /* Return the full pathname of the system man page formatter. */
224fbc94a17Sniklas static char *
find_man_formatter(void)225*1076333cSespie find_man_formatter (void)
226fbc94a17Sniklas {
227fbc94a17Sniklas   return (executable_file_in_path ("man", (char *)getenv ("PATH")));
228fbc94a17Sniklas }
229fbc94a17Sniklas 
230fbc94a17Sniklas static char *manpage_pagename = (char *)NULL;
231fbc94a17Sniklas static char *manpage_section  = (char *)NULL;
232fbc94a17Sniklas 
233fbc94a17Sniklas static void
get_page_and_section(char * pagename)234*1076333cSespie get_page_and_section (char *pagename)
235fbc94a17Sniklas {
236fbc94a17Sniklas   register int i;
237fbc94a17Sniklas 
238fbc94a17Sniklas   if (manpage_pagename)
239fbc94a17Sniklas     free (manpage_pagename);
240fbc94a17Sniklas 
241fbc94a17Sniklas   if (manpage_section)
242fbc94a17Sniklas     free (manpage_section);
243fbc94a17Sniklas 
244fbc94a17Sniklas   manpage_pagename = (char *)NULL;
245fbc94a17Sniklas   manpage_section  = (char *)NULL;
246fbc94a17Sniklas 
247fbc94a17Sniklas   for (i = 0; pagename[i] != '\0' && pagename[i] != '('; i++);
248fbc94a17Sniklas 
249fbc94a17Sniklas   manpage_pagename = (char *)xmalloc (1 + i);
250fbc94a17Sniklas   strncpy (manpage_pagename, pagename, i);
251fbc94a17Sniklas   manpage_pagename[i] = '\0';
252fbc94a17Sniklas 
253fbc94a17Sniklas   if (pagename[i] == '(')
254fbc94a17Sniklas     {
255fbc94a17Sniklas       int start;
256fbc94a17Sniklas 
257fbc94a17Sniklas       start = i + 1;
258fbc94a17Sniklas 
259fbc94a17Sniklas       for (i = start; pagename[i] != '\0' && pagename[i] != ')'; i++);
260fbc94a17Sniklas 
261fbc94a17Sniklas       manpage_section = (char *)xmalloc (1 + (i - start));
262fbc94a17Sniklas       strncpy (manpage_section, pagename + start, (i - start));
263fbc94a17Sniklas       manpage_section[i - start] = '\0';
264fbc94a17Sniklas     }
265fbc94a17Sniklas }
266fbc94a17Sniklas 
267672dff93Sespie #if PIPE_USE_FORK
268fbc94a17Sniklas static void
reap_children(int sig)269*1076333cSespie reap_children (int sig)
270fbc94a17Sniklas {
271672dff93Sespie   wait (NULL);
272fbc94a17Sniklas }
273672dff93Sespie #endif
274fbc94a17Sniklas 
275fbc94a17Sniklas static char *
get_manpage_contents(char * pagename)276*1076333cSespie get_manpage_contents (char *pagename)
277fbc94a17Sniklas {
278fbc94a17Sniklas   static char *formatter_args[4] = { (char *)NULL };
279fbc94a17Sniklas   int pipes[2];
280fbc94a17Sniklas   pid_t child;
281*1076333cSespie   RETSIGTYPE (*sigsave) (int signum);
282672dff93Sespie   char *formatted_page = NULL;
283fbc94a17Sniklas   int arg_index = 1;
284fbc94a17Sniklas 
285fbc94a17Sniklas   if (formatter_args[0] == (char *)NULL)
286fbc94a17Sniklas     formatter_args[0] = find_man_formatter ();
287fbc94a17Sniklas 
288fbc94a17Sniklas   if (formatter_args[0] == (char *)NULL)
289fbc94a17Sniklas     return ((char *)NULL);
290fbc94a17Sniklas 
291fbc94a17Sniklas   get_page_and_section (pagename);
292fbc94a17Sniklas 
293fbc94a17Sniklas   if (manpage_section != (char *)NULL)
294fbc94a17Sniklas     formatter_args[arg_index++] = manpage_section;
295fbc94a17Sniklas 
296fbc94a17Sniklas   formatter_args[arg_index++] = manpage_pagename;
297fbc94a17Sniklas   formatter_args[arg_index] = (char *)NULL;
298fbc94a17Sniklas 
299fbc94a17Sniklas   /* Open a pipe to this program, read the output, and save it away
300fbc94a17Sniklas      in FORMATTED_PAGE.  The reader end of the pipe is pipes[0]; the
301fbc94a17Sniklas      writer end is pipes[1]. */
302672dff93Sespie #if PIPE_USE_FORK
303fbc94a17Sniklas   pipe (pipes);
304fbc94a17Sniklas 
305672dff93Sespie   sigsave = signal (SIGCHLD, reap_children);
306fbc94a17Sniklas 
307fbc94a17Sniklas   child = fork ();
308fbc94a17Sniklas   if (child == -1)
309fbc94a17Sniklas     return ((char *)NULL);
310fbc94a17Sniklas 
311fbc94a17Sniklas   if (child != 0)
312fbc94a17Sniklas     {
313fbc94a17Sniklas       /* In the parent, close the writing end of the pipe, and read from
314fbc94a17Sniklas          the exec'd child. */
315fbc94a17Sniklas       close (pipes[1]);
316fbc94a17Sniklas       formatted_page = read_from_fd (pipes[0]);
317fbc94a17Sniklas       close (pipes[0]);
318672dff93Sespie       signal (SIGCHLD, sigsave);
319fbc94a17Sniklas     }
320fbc94a17Sniklas   else
321672dff93Sespie     { /* In the child, close the read end of the pipe, make the write end
322fbc94a17Sniklas          of the pipe be stdout, and execute the man page formatter. */
323fbc94a17Sniklas       close (pipes[0]);
324672dff93Sespie       freopen (NULL_DEVICE, "w", stderr);
325672dff93Sespie       freopen (NULL_DEVICE, "r", stdin);
326fbc94a17Sniklas       dup2 (pipes[1], fileno (stdout));
327fbc94a17Sniklas 
328fbc94a17Sniklas       execv (formatter_args[0], formatter_args);
329fbc94a17Sniklas 
330fbc94a17Sniklas       /* If we get here, we couldn't exec, so close out the pipe and
331fbc94a17Sniklas          exit. */
332fbc94a17Sniklas       close (pipes[1]);
333672dff93Sespie       xexit (0);
334fbc94a17Sniklas     }
335672dff93Sespie #else  /* !PIPE_USE_FORK */
336672dff93Sespie   /* Cannot fork/exec, but can popen/pclose.  */
337672dff93Sespie   {
338672dff93Sespie     FILE *fpipe;
339672dff93Sespie     char *cmdline = xmalloc (strlen (formatter_args[0])
340672dff93Sespie 			     + strlen (manpage_pagename)
341672dff93Sespie 			     + (arg_index > 2 ? strlen (manpage_section) : 0)
342672dff93Sespie  			     + 3);
343672dff93Sespie     int save_stderr = dup (fileno (stderr));
344672dff93Sespie     int fd_err = open (NULL_DEVICE, O_WRONLY, 0666);
345672dff93Sespie 
346672dff93Sespie     if (fd_err > 2)
347672dff93Sespie       dup2 (fd_err, fileno (stderr)); /* Don't print errors. */
348672dff93Sespie     sprintf (cmdline, "%s %s %s", formatter_args[0], manpage_pagename,
349672dff93Sespie 				  arg_index > 2 ? manpage_section : "");
350672dff93Sespie     fpipe = popen (cmdline, "r");
351672dff93Sespie     free (cmdline);
352672dff93Sespie     if (fd_err > 2)
353672dff93Sespie       close (fd_err);
354672dff93Sespie     dup2 (save_stderr, fileno (stderr));
355672dff93Sespie     if (fpipe == 0)
356672dff93Sespie       return ((char *)NULL);
357672dff93Sespie     formatted_page = read_from_fd (fileno (fpipe));
358672dff93Sespie     if (pclose (fpipe) == -1)
359672dff93Sespie       {
360672dff93Sespie 	if (formatted_page)
361672dff93Sespie 	  free (formatted_page);
362672dff93Sespie 	return ((char *)NULL);
363672dff93Sespie       }
364672dff93Sespie   }
365672dff93Sespie #endif /* !PIPE_USE_FORK */
366fbc94a17Sniklas 
367fbc94a17Sniklas   /* If we have the page, then clean it up. */
368fbc94a17Sniklas   if (formatted_page)
369fbc94a17Sniklas     clean_manpage (formatted_page);
370fbc94a17Sniklas 
371fbc94a17Sniklas   return (formatted_page);
372fbc94a17Sniklas }
373fbc94a17Sniklas 
374fbc94a17Sniklas static void
clean_manpage(char * manpage)375*1076333cSespie clean_manpage (char *manpage)
376fbc94a17Sniklas {
377fbc94a17Sniklas   register int i, j;
378fbc94a17Sniklas   int newline_count = 0;
379fbc94a17Sniklas   char *newpage;
380fbc94a17Sniklas 
381fbc94a17Sniklas   newpage = (char *)xmalloc (1 + strlen (manpage));
382fbc94a17Sniklas 
383840175f0Skstailey   for (i = 0, j = 0; (newpage[j] = manpage[i]); i++, j++)
384fbc94a17Sniklas     {
385fbc94a17Sniklas       if (manpage[i] == '\n')
386fbc94a17Sniklas         newline_count++;
387fbc94a17Sniklas       else
388fbc94a17Sniklas         newline_count = 0;
389fbc94a17Sniklas 
390fbc94a17Sniklas       if (newline_count == 3)
391fbc94a17Sniklas         {
392fbc94a17Sniklas           j--;
393fbc94a17Sniklas           newline_count--;
394fbc94a17Sniklas         }
395fbc94a17Sniklas 
3963aa90977Sespie       /* A malformed man page could have a \b as its first character,
3973aa90977Sespie          in which case decrementing j by 2 will cause us to write into
3983aa90977Sespie          newpage[-1], smashing the hidden info stored there by malloc.  */
399*1076333cSespie       if (manpage[i] == '\b' || (manpage[i] == '\f' && j > 0))
400fbc94a17Sniklas         j -= 2;
4013aa90977Sespie       else if (!raw_escapes_p)
4023aa90977Sespie 	{
4033aa90977Sespie 	  /* Remove the ANSI escape sequences for color, boldface,
4043aa90977Sespie 	     underlining, and italics, generated by some versions of
4053aa90977Sespie 	     Groff.  */
4063aa90977Sespie 	  if (manpage[i] == '\033' && manpage[i + 1] == '['
4073aa90977Sespie 	      && isdigit (manpage[i + 2]))
4083aa90977Sespie 	    {
4093aa90977Sespie 	      if (isdigit (manpage[i + 3]) && manpage[i + 4] == 'm')
4103aa90977Sespie 		{
4113aa90977Sespie 		  i += 4;
4123aa90977Sespie 		  j--;
4133aa90977Sespie 		}
4143aa90977Sespie 	      else if (manpage[i + 3] == 'm')
4153aa90977Sespie 		{
4163aa90977Sespie 		  i += 3;
4173aa90977Sespie 		  j--;
4183aa90977Sespie 		}
4193aa90977Sespie 	      /* Else do nothing: it's some unknown escape sequence,
4203aa90977Sespie 		 so let's leave it alone.  */
4213aa90977Sespie 	    }
4223aa90977Sespie 	}
423fbc94a17Sniklas     }
424fbc94a17Sniklas 
4253aa90977Sespie   newpage[j++] = 0;
426fbc94a17Sniklas 
427fbc94a17Sniklas   strcpy (manpage, newpage);
428fbc94a17Sniklas   free (newpage);
429fbc94a17Sniklas }
430fbc94a17Sniklas 
431fbc94a17Sniklas static NODE *
manpage_node_of_file_buffer(FILE_BUFFER * file_buffer,char * pagename)432*1076333cSespie manpage_node_of_file_buffer (FILE_BUFFER *file_buffer, char *pagename)
433fbc94a17Sniklas {
434fbc94a17Sniklas   NODE *node = (NODE *)NULL;
435fbc94a17Sniklas   TAG *tag = (TAG *)NULL;
436fbc94a17Sniklas 
437fbc94a17Sniklas   if (file_buffer->contents)
438fbc94a17Sniklas     {
439fbc94a17Sniklas       register int i;
440fbc94a17Sniklas 
441840175f0Skstailey       for (i = 0; (tag = file_buffer->tags[i]); i++)
442fbc94a17Sniklas         {
443fbc94a17Sniklas           if (strcasecmp (pagename, tag->nodename) == 0)
444fbc94a17Sniklas             break;
445fbc94a17Sniklas         }
446fbc94a17Sniklas     }
447fbc94a17Sniklas 
448fbc94a17Sniklas   if (tag)
449fbc94a17Sniklas     {
450fbc94a17Sniklas       node = (NODE *)xmalloc (sizeof (NODE));
451fbc94a17Sniklas       node->filename = file_buffer->filename;
452672dff93Sespie       node->nodename = xstrdup (tag->nodename);
453fbc94a17Sniklas       node->contents = file_buffer->contents + tag->nodestart;
454fbc94a17Sniklas       node->nodelen = tag->nodelen;
455fbc94a17Sniklas       node->flags    = 0;
456672dff93Sespie       node->display_pos = 0;
457fbc94a17Sniklas       node->parent   = (char *)NULL;
458fbc94a17Sniklas       node->flags = (N_HasTagsTable | N_IsManPage);
459fbc94a17Sniklas       node->contents += skip_node_separator (node->contents);
460fbc94a17Sniklas     }
461fbc94a17Sniklas 
462fbc94a17Sniklas   return (node);
463fbc94a17Sniklas }
464fbc94a17Sniklas 
465fbc94a17Sniklas static char *
read_from_fd(int fd)466*1076333cSespie read_from_fd (int fd)
467fbc94a17Sniklas {
468fbc94a17Sniklas   struct timeval timeout;
469fbc94a17Sniklas   char *buffer = (char *)NULL;
470fbc94a17Sniklas   int bsize = 0;
471fbc94a17Sniklas   int bindex = 0;
472fbc94a17Sniklas   int select_result;
473fbc94a17Sniklas #if defined (FD_SET)
474fbc94a17Sniklas   fd_set read_fds;
475fbc94a17Sniklas 
476fbc94a17Sniklas   timeout.tv_sec = 15;
477fbc94a17Sniklas   timeout.tv_usec = 0;
478fbc94a17Sniklas 
479fbc94a17Sniklas   FD_ZERO (&read_fds);
480fbc94a17Sniklas   FD_SET (fd, &read_fds);
481fbc94a17Sniklas 
482fbc94a17Sniklas   select_result = select (fd + 1, fd_set_cast (&read_fds), 0, 0, &timeout);
483fbc94a17Sniklas #else /* !FD_SET */
484fbc94a17Sniklas   select_result = 1;
485fbc94a17Sniklas #endif /* !FD_SET */
486fbc94a17Sniklas 
487fbc94a17Sniklas   switch (select_result)
488fbc94a17Sniklas     {
489fbc94a17Sniklas     case 0:
490fbc94a17Sniklas     case -1:
491fbc94a17Sniklas       break;
492fbc94a17Sniklas 
493fbc94a17Sniklas     default:
494fbc94a17Sniklas       {
495fbc94a17Sniklas         int amount_read;
496fbc94a17Sniklas         int done = 0;
497fbc94a17Sniklas 
498fbc94a17Sniklas         while (!done)
499fbc94a17Sniklas           {
500fbc94a17Sniklas             while ((bindex + 1024) > (bsize))
501fbc94a17Sniklas               buffer = (char *)xrealloc (buffer, (bsize += 1024));
502fbc94a17Sniklas             buffer[bindex] = '\0';
503fbc94a17Sniklas 
504fbc94a17Sniklas             amount_read = read (fd, buffer + bindex, 1023);
505fbc94a17Sniklas 
506fbc94a17Sniklas             if (amount_read < 0)
507fbc94a17Sniklas               {
508fbc94a17Sniklas                 done = 1;
509fbc94a17Sniklas               }
510fbc94a17Sniklas             else
511fbc94a17Sniklas               {
512fbc94a17Sniklas                 bindex += amount_read;
513fbc94a17Sniklas                 buffer[bindex] = '\0';
514fbc94a17Sniklas                 if (amount_read == 0)
515fbc94a17Sniklas                   done = 1;
516fbc94a17Sniklas               }
517fbc94a17Sniklas           }
518fbc94a17Sniklas       }
519fbc94a17Sniklas     }
520fbc94a17Sniklas 
521fbc94a17Sniklas   if ((buffer != (char *)NULL) && (*buffer == '\0'))
522fbc94a17Sniklas     {
523fbc94a17Sniklas       free (buffer);
524fbc94a17Sniklas       buffer = (char *)NULL;
525fbc94a17Sniklas     }
526fbc94a17Sniklas 
527fbc94a17Sniklas   return (buffer);
528fbc94a17Sniklas }
529fbc94a17Sniklas 
530fbc94a17Sniklas static char *reference_section_starters[] =
531fbc94a17Sniklas {
532fbc94a17Sniklas   "\nRELATED INFORMATION",
533fbc94a17Sniklas   "\nRELATED\tINFORMATION",
534fbc94a17Sniklas   "RELATED INFORMATION\n",
535fbc94a17Sniklas   "RELATED\tINFORMATION\n",
536fbc94a17Sniklas   "\nSEE ALSO",
537fbc94a17Sniklas   "\nSEE\tALSO",
538fbc94a17Sniklas   "SEE ALSO\n",
539fbc94a17Sniklas   "SEE\tALSO\n",
540fbc94a17Sniklas   (char *)NULL
541fbc94a17Sniklas };
542fbc94a17Sniklas 
543fbc94a17Sniklas static SEARCH_BINDING frs_binding;
544fbc94a17Sniklas 
545fbc94a17Sniklas static SEARCH_BINDING *
find_reference_section(NODE * node)546*1076333cSespie find_reference_section (NODE *node)
547fbc94a17Sniklas {
548fbc94a17Sniklas   register int i;
549fbc94a17Sniklas   long position = -1;
550fbc94a17Sniklas 
551fbc94a17Sniklas   frs_binding.buffer = node->contents;
552fbc94a17Sniklas   frs_binding.start = 0;
553fbc94a17Sniklas   frs_binding.end = node->nodelen;
554fbc94a17Sniklas   frs_binding.flags = S_SkipDest;
555fbc94a17Sniklas 
556fbc94a17Sniklas   for (i = 0; reference_section_starters[i] != (char *)NULL; i++)
557fbc94a17Sniklas     {
558fbc94a17Sniklas       position = search_forward (reference_section_starters[i], &frs_binding);
559fbc94a17Sniklas       if (position != -1)
560fbc94a17Sniklas         break;
561fbc94a17Sniklas     }
562fbc94a17Sniklas 
563fbc94a17Sniklas   if (position == -1)
564fbc94a17Sniklas     return ((SEARCH_BINDING *)NULL);
565fbc94a17Sniklas 
566fbc94a17Sniklas   /* We found the start of the reference section, and point is right after
567fbc94a17Sniklas      the string which starts it.  The text from here to the next header
568fbc94a17Sniklas      (or end of buffer) contains the only references in this manpage. */
569fbc94a17Sniklas   frs_binding.start = position;
570fbc94a17Sniklas 
571fbc94a17Sniklas   for (i = frs_binding.start; i < frs_binding.end - 2; i++)
572fbc94a17Sniklas     {
573fbc94a17Sniklas       if ((frs_binding.buffer[i] == '\n') &&
574fbc94a17Sniklas           (!whitespace (frs_binding.buffer[i + 1])))
575fbc94a17Sniklas         {
576fbc94a17Sniklas           frs_binding.end = i;
577fbc94a17Sniklas           break;
578fbc94a17Sniklas         }
579fbc94a17Sniklas     }
580fbc94a17Sniklas 
581fbc94a17Sniklas   return (&frs_binding);
582fbc94a17Sniklas }
583fbc94a17Sniklas 
584fbc94a17Sniklas REFERENCE **
xrefs_of_manpage(NODE * node)585*1076333cSespie xrefs_of_manpage (NODE *node)
586fbc94a17Sniklas {
587fbc94a17Sniklas   SEARCH_BINDING *reference_section;
588fbc94a17Sniklas   REFERENCE **refs = (REFERENCE **)NULL;
589fbc94a17Sniklas   int refs_index = 0;
590fbc94a17Sniklas   int refs_slots = 0;
591fbc94a17Sniklas   long position;
592fbc94a17Sniklas 
593fbc94a17Sniklas   reference_section = find_reference_section (node);
594fbc94a17Sniklas 
595fbc94a17Sniklas   if (reference_section == (SEARCH_BINDING *)NULL)
596fbc94a17Sniklas     return ((REFERENCE **)NULL);
597fbc94a17Sniklas 
598fbc94a17Sniklas   /* Grovel the reference section building a list of references found there.
599fbc94a17Sniklas      A reference is alphabetic characters followed by non-whitespace text
600fbc94a17Sniklas      within parenthesis. */
601fbc94a17Sniklas   reference_section->flags = 0;
602fbc94a17Sniklas 
603fbc94a17Sniklas   while ((position = search_forward ("(", reference_section)) != -1)
604fbc94a17Sniklas     {
605fbc94a17Sniklas       register int start, end;
606fbc94a17Sniklas 
607fbc94a17Sniklas       for (start = position; start > reference_section->start; start--)
608fbc94a17Sniklas         if (whitespace (reference_section->buffer[start]))
609fbc94a17Sniklas           break;
610fbc94a17Sniklas 
611fbc94a17Sniklas       start++;
612fbc94a17Sniklas 
613fbc94a17Sniklas       for (end = position; end < reference_section->end; end++)
614fbc94a17Sniklas         {
615fbc94a17Sniklas           if (whitespace (reference_section->buffer[end]))
616fbc94a17Sniklas             {
617fbc94a17Sniklas               end = start;
618fbc94a17Sniklas               break;
619fbc94a17Sniklas             }
620fbc94a17Sniklas 
621fbc94a17Sniklas           if (reference_section->buffer[end] == ')')
622fbc94a17Sniklas             {
623fbc94a17Sniklas               end++;
624fbc94a17Sniklas               break;
625fbc94a17Sniklas             }
626fbc94a17Sniklas         }
627fbc94a17Sniklas 
628fbc94a17Sniklas       if (end != start)
629fbc94a17Sniklas         {
630fbc94a17Sniklas           REFERENCE *entry;
631fbc94a17Sniklas           int len = end - start;
632fbc94a17Sniklas 
633fbc94a17Sniklas           entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
634fbc94a17Sniklas           entry->label = (char *)xmalloc (1 + len);
635fbc94a17Sniklas           strncpy (entry->label, (reference_section->buffer) + start, len);
636fbc94a17Sniklas           entry->label[len] = '\0';
637840175f0Skstailey           entry->filename = xstrdup (node->filename);
638840175f0Skstailey           entry->nodename = xstrdup (entry->label);
639fbc94a17Sniklas           entry->start = start;
640fbc94a17Sniklas           entry->end = end;
641fbc94a17Sniklas 
642fbc94a17Sniklas           add_pointer_to_array
643fbc94a17Sniklas             (entry, refs_index, refs, refs_slots, 10, REFERENCE *);
644fbc94a17Sniklas         }
645fbc94a17Sniklas 
646fbc94a17Sniklas       reference_section->start = position + 1;
647fbc94a17Sniklas     }
648fbc94a17Sniklas 
649fbc94a17Sniklas   return (refs);
650fbc94a17Sniklas }
651fbc94a17Sniklas 
652fbc94a17Sniklas long
locate_manpage_xref(NODE * node,long int start,int dir)653*1076333cSespie locate_manpage_xref (NODE *node, long int start, int dir)
654fbc94a17Sniklas {
655fbc94a17Sniklas   REFERENCE **refs;
656fbc94a17Sniklas   long position = -1;
657fbc94a17Sniklas 
658fbc94a17Sniklas   refs = xrefs_of_manpage (node);
659fbc94a17Sniklas 
660fbc94a17Sniklas   if (refs)
661fbc94a17Sniklas     {
662fbc94a17Sniklas       register int i, count;
663fbc94a17Sniklas       REFERENCE *entry;
664fbc94a17Sniklas 
665fbc94a17Sniklas       for (i = 0; refs[i]; i++);
666fbc94a17Sniklas       count = i;
667fbc94a17Sniklas 
668fbc94a17Sniklas       if (dir > 0)
669fbc94a17Sniklas         {
670840175f0Skstailey           for (i = 0; (entry = refs[i]); i++)
671fbc94a17Sniklas             if (entry->start > start)
672fbc94a17Sniklas               {
673fbc94a17Sniklas                 position = entry->start;
674fbc94a17Sniklas                 break;
675fbc94a17Sniklas               }
676fbc94a17Sniklas         }
677fbc94a17Sniklas       else
678fbc94a17Sniklas         {
6797ab9cc48Sniklas           for (i = count - 1; i > -1; i--)
680fbc94a17Sniklas             {
681fbc94a17Sniklas               entry = refs[i];
682fbc94a17Sniklas 
683fbc94a17Sniklas               if (entry->start < start)
684fbc94a17Sniklas                 {
685fbc94a17Sniklas                   position = entry->start;
686fbc94a17Sniklas                   break;
687fbc94a17Sniklas                 }
688fbc94a17Sniklas             }
689fbc94a17Sniklas         }
690fbc94a17Sniklas 
691fbc94a17Sniklas       info_free_references (refs);
692fbc94a17Sniklas     }
693fbc94a17Sniklas   return (position);
694fbc94a17Sniklas }
695fbc94a17Sniklas 
696fbc94a17Sniklas /* This one was a little tricky.  The binding buffer that is passed in has
697fbc94a17Sniklas    a START and END value of 0 -- strlen (window-line-containing-point).
698fbc94a17Sniklas    The BUFFER is a pointer to the start of that line. */
699fbc94a17Sniklas REFERENCE **
manpage_xrefs_in_binding(NODE * node,SEARCH_BINDING * binding)700*1076333cSespie manpage_xrefs_in_binding (NODE *node, SEARCH_BINDING *binding)
701fbc94a17Sniklas {
702fbc94a17Sniklas   register int i;
703fbc94a17Sniklas   REFERENCE **all_refs = xrefs_of_manpage (node);
704fbc94a17Sniklas   REFERENCE **brefs = (REFERENCE **)NULL;
705fbc94a17Sniklas   REFERENCE *entry;
706fbc94a17Sniklas   int brefs_index = 0;
707fbc94a17Sniklas   int brefs_slots = 0;
708fbc94a17Sniklas   int start, end;
709fbc94a17Sniklas 
710fbc94a17Sniklas   if (!all_refs)
711fbc94a17Sniklas     return ((REFERENCE **)NULL);
712fbc94a17Sniklas 
713fbc94a17Sniklas   start = binding->start + (binding->buffer - node->contents);
714fbc94a17Sniklas   end = binding->end + (binding->buffer - node->contents);
715fbc94a17Sniklas 
716840175f0Skstailey   for (i = 0; (entry = all_refs[i]); i++)
717fbc94a17Sniklas     {
718fbc94a17Sniklas       if ((entry->start > start) && (entry->end < end))
719fbc94a17Sniklas         {
720fbc94a17Sniklas           add_pointer_to_array
721fbc94a17Sniklas             (entry, brefs_index, brefs, brefs_slots, 10, REFERENCE *);
722fbc94a17Sniklas         }
723fbc94a17Sniklas       else
724fbc94a17Sniklas         {
725fbc94a17Sniklas           maybe_free (entry->label);
726fbc94a17Sniklas           maybe_free (entry->filename);
727fbc94a17Sniklas           maybe_free (entry->nodename);
728fbc94a17Sniklas           free (entry);
729fbc94a17Sniklas         }
730fbc94a17Sniklas     }
731fbc94a17Sniklas 
732fbc94a17Sniklas   free (all_refs);
733fbc94a17Sniklas   return (brefs);
734fbc94a17Sniklas }
735