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