142660Smarkm/* man.c: How to read and format man files. 2146515Sru $Id: man.c,v 1.4 2004/04/11 17:56:46 karl Exp $ 321495Sjmacd 4146515Sru Copyright (C) 1995, 1997, 1998, 1999, 2000, 2002, 2003, 2004 Free Software 5114472Sru Foundation, Inc. 621495Sjmacd 721495Sjmacd This program is free software; you can redistribute it and/or modify 821495Sjmacd it under the terms of the GNU General Public License as published by 921495Sjmacd the Free Software Foundation; either version 2, or (at your option) 1021495Sjmacd any later version. 1121495Sjmacd 1221495Sjmacd This program is distributed in the hope that it will be useful, 1321495Sjmacd but WITHOUT ANY WARRANTY; without even the implied warranty of 1421495Sjmacd MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1521495Sjmacd GNU General Public License for more details. 1621495Sjmacd 1721495Sjmacd You should have received a copy of the GNU General Public License 1821495Sjmacd along with this program; if not, write to the Free Software 1921495Sjmacd Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 2021495Sjmacd 2121495Sjmacd Written by Brian Fox Thu May 4 09:17:52 1995 (bfox@ai.mit.edu). */ 2221495Sjmacd 2321495Sjmacd#include "info.h" 2421495Sjmacd#include <sys/ioctl.h> 2521495Sjmacd#include "signals.h" 2621495Sjmacd#if defined (HAVE_SYS_TIME_H) 2721495Sjmacd#include <sys/time.h> 2821495Sjmacd#endif 2921495Sjmacd#if defined (HAVE_SYS_WAIT_H) 3021495Sjmacd#include <sys/wait.h> 3121495Sjmacd#endif 3242660Smarkm 3321495Sjmacd#include "tilde.h" 3421495Sjmacd#include "man.h" 3521495Sjmacd 3621495Sjmacd#if !defined (_POSIX_VERSION) 3721495Sjmacd#define pid_t int 3821495Sjmacd#endif 3921495Sjmacd 4021495Sjmacd#if defined (FD_SET) 4121495Sjmacd# if defined (hpux) 4221495Sjmacd# define fd_set_cast(x) (int *)(x) 4321495Sjmacd# else 4421495Sjmacd# define fd_set_cast(x) (fd_set *)(x) 4521495Sjmacd# endif /* !hpux */ 4621495Sjmacd#endif /* FD_SET */ 4721495Sjmacd 4856160Sru#if STRIP_DOT_EXE 4956160Srustatic char const * const exec_extensions[] = { 5056160Sru ".exe", ".com", ".bat", ".btm", ".sh", ".ksh", ".pl", ".sed", "", NULL 5156160Sru}; 5256160Sru#else 5356160Srustatic char const * const exec_extensions[] = { "", NULL }; 5456160Sru#endif 5556160Sru 56146515Srustatic char *read_from_fd (int fd); 57146515Srustatic void clean_manpage (char *manpage); 58146515Srustatic NODE *manpage_node_of_file_buffer (FILE_BUFFER *file_buffer, 59146515Sru char *pagename); 60146515Srustatic char *get_manpage_contents (char *pagename); 6121495Sjmacd 6221495SjmacdNODE * 63146515Srumake_manpage_node (char *pagename) 6421495Sjmacd{ 6521495Sjmacd return (info_get_node (MANPAGE_FILE_BUFFER_NAME, pagename)); 6621495Sjmacd} 6721495Sjmacd 6821495SjmacdNODE * 69146515Sruget_manpage_node (FILE_BUFFER *file_buffer, char *pagename) 7021495Sjmacd{ 7121495Sjmacd NODE *node; 7221495Sjmacd 7321495Sjmacd node = manpage_node_of_file_buffer (file_buffer, pagename); 7421495Sjmacd 7521495Sjmacd if (!node) 7621495Sjmacd { 7721495Sjmacd char *page; 7821495Sjmacd 7921495Sjmacd page = get_manpage_contents (pagename); 8021495Sjmacd 8121495Sjmacd if (page) 8242660Smarkm { 8342660Smarkm char header[1024]; 8442660Smarkm long oldsize, newsize; 8542660Smarkm int hlen, plen; 8656160Sru char *old_contents = file_buffer->contents; 8721495Sjmacd 8842660Smarkm sprintf (header, "\n\n%c\n%s %s, %s %s, %s (dir)\n\n", 8942660Smarkm INFO_COOKIE, 9042660Smarkm INFO_FILE_LABEL, file_buffer->filename, 9142660Smarkm INFO_NODE_LABEL, pagename, 9242660Smarkm INFO_UP_LABEL); 9342660Smarkm oldsize = file_buffer->filesize; 9442660Smarkm hlen = strlen (header); 9542660Smarkm plen = strlen (page); 9642660Smarkm newsize = (oldsize + hlen + plen); 9742660Smarkm file_buffer->contents = 9842660Smarkm (char *)xrealloc (file_buffer->contents, 1 + newsize); 9942660Smarkm memcpy (file_buffer->contents + oldsize, header, hlen); 10056160Sru memcpy (file_buffer->contents + oldsize + hlen, page, plen); 10142660Smarkm file_buffer->contents[newsize] = '\0'; 10242660Smarkm file_buffer->filesize = newsize; 10342660Smarkm file_buffer->finfo.st_size = newsize; 10442660Smarkm build_tags_and_nodes (file_buffer); 10542660Smarkm free (page); 10656160Sru /* We have just relocated file_buffer->contents from under 10756160Sru the feet of info_windows[] array. Therefore, all the 10856160Sru nodes on that list which are showing man pages have their 10956160Sru contents member pointing into the blue. Undo that harm. */ 11056160Sru if (old_contents && oldsize && old_contents != file_buffer->contents) 11156160Sru { 11256160Sru int iw; 11356160Sru INFO_WINDOW *info_win; 11456160Sru char *old_contents_end = old_contents + oldsize; 11556160Sru 11656160Sru for (iw = 0; (info_win = info_windows[iw]); iw++) 11756160Sru { 11856160Sru int in; 11956160Sru 12056160Sru for (in = 0; in < info_win->nodes_index; in++) 12156160Sru { 122146515Sru NODE *tmp_node = info_win->nodes[in]; 12356160Sru 12456160Sru /* It really only suffices to see that node->filename 12556160Sru is "*manpages*". But after several hours of 12656160Sru debugging this, would you blame me for being a bit 12756160Sru paranoid? */ 128146515Sru if (tmp_node && tmp_node->filename 129146515Sru && tmp_node->contents 130146515Sru && strcmp (tmp_node->filename, 131146515Sru MANPAGE_FILE_BUFFER_NAME) == 0 132146515Sru && tmp_node->contents >= old_contents 133146515Sru && tmp_node->contents + tmp_node->nodelen 134146515Sru <= old_contents_end) 13556160Sru { 13656160Sru info_win->nodes[in] = 13756160Sru manpage_node_of_file_buffer (file_buffer, 138146515Sru tmp_node->nodename); 139146515Sru free (tmp_node->nodename); 140146515Sru free (tmp_node); 14156160Sru } 14256160Sru } 14356160Sru } 14456160Sru } 14542660Smarkm } 14621495Sjmacd 14721495Sjmacd node = manpage_node_of_file_buffer (file_buffer, pagename); 14821495Sjmacd } 14921495Sjmacd 15021495Sjmacd return (node); 15121495Sjmacd} 15221495Sjmacd 15321495SjmacdFILE_BUFFER * 154146515Srucreate_manpage_file_buffer (void) 15521495Sjmacd{ 15642660Smarkm FILE_BUFFER *file_buffer = make_file_buffer (); 15742660Smarkm file_buffer->filename = xstrdup (MANPAGE_FILE_BUFFER_NAME); 15842660Smarkm file_buffer->fullpath = xstrdup (MANPAGE_FILE_BUFFER_NAME); 15921495Sjmacd file_buffer->finfo.st_size = 0; 16021495Sjmacd file_buffer->filesize = 0; 16121495Sjmacd file_buffer->contents = (char *)NULL; 16221495Sjmacd file_buffer->flags = (N_IsInternal | N_CannotGC | N_IsManPage); 163116525Sru 16421495Sjmacd return (file_buffer); 16521495Sjmacd} 16621495Sjmacd 16721495Sjmacd/* Scan the list of directories in PATH looking for FILENAME. If we find 16821495Sjmacd one that is an executable file, return it as a new string. Otherwise, 16921495Sjmacd return a NULL pointer. */ 17021495Sjmacdstatic char * 171146515Sruexecutable_file_in_path (char *filename, char *path) 17221495Sjmacd{ 17321495Sjmacd struct stat finfo; 17421495Sjmacd char *temp_dirname; 17521495Sjmacd int statable, dirname_index; 17621495Sjmacd 17721495Sjmacd dirname_index = 0; 17821495Sjmacd 17942660Smarkm while ((temp_dirname = extract_colon_unit (path, &dirname_index))) 18021495Sjmacd { 18121495Sjmacd char *temp; 18256160Sru char *temp_end; 18356160Sru int i; 18421495Sjmacd 18521495Sjmacd /* Expand a leading tilde if one is present. */ 18621495Sjmacd if (*temp_dirname == '~') 18742660Smarkm { 18842660Smarkm char *expanded_dirname; 18921495Sjmacd 19042660Smarkm expanded_dirname = tilde_expand_word (temp_dirname); 19142660Smarkm free (temp_dirname); 19242660Smarkm temp_dirname = expanded_dirname; 19342660Smarkm } 19421495Sjmacd 19556160Sru temp = (char *)xmalloc (34 + strlen (temp_dirname) + strlen (filename)); 19621495Sjmacd strcpy (temp, temp_dirname); 19756160Sru if (!IS_SLASH (temp[(strlen (temp)) - 1])) 19842660Smarkm strcat (temp, "/"); 19921495Sjmacd strcat (temp, filename); 20056160Sru temp_end = temp + strlen (temp); 20121495Sjmacd 20221495Sjmacd free (temp_dirname); 20321495Sjmacd 20456160Sru /* Look for FILENAME, possibly with any of the extensions 20556160Sru in EXEC_EXTENSIONS[]. */ 20656160Sru for (i = 0; exec_extensions[i]; i++) 20756160Sru { 20856160Sru if (exec_extensions[i][0]) 20956160Sru strcpy (temp_end, exec_extensions[i]); 21056160Sru statable = (stat (temp, &finfo) == 0); 21121495Sjmacd 21256160Sru /* If we have found a regular executable file, then use it. */ 21356160Sru if ((statable) && (S_ISREG (finfo.st_mode)) && 21456160Sru (access (temp, X_OK) == 0)) 21556160Sru return (temp); 21656160Sru } 21756160Sru 21856160Sru free (temp); 21921495Sjmacd } 22021495Sjmacd return ((char *)NULL); 22121495Sjmacd} 22221495Sjmacd 22321495Sjmacd/* Return the full pathname of the system man page formatter. */ 22421495Sjmacdstatic char * 225146515Srufind_man_formatter (void) 22621495Sjmacd{ 22721495Sjmacd return (executable_file_in_path ("man", (char *)getenv ("PATH"))); 22821495Sjmacd} 22921495Sjmacd 23021495Sjmacdstatic char *manpage_pagename = (char *)NULL; 23121495Sjmacdstatic char *manpage_section = (char *)NULL; 23221495Sjmacd 23321495Sjmacdstatic void 234146515Sruget_page_and_section (char *pagename) 23521495Sjmacd{ 23621495Sjmacd register int i; 23721495Sjmacd 23821495Sjmacd if (manpage_pagename) 23921495Sjmacd free (manpage_pagename); 24021495Sjmacd 24121495Sjmacd if (manpage_section) 24221495Sjmacd free (manpage_section); 24321495Sjmacd 24421495Sjmacd manpage_pagename = (char *)NULL; 24521495Sjmacd manpage_section = (char *)NULL; 24621495Sjmacd 24721495Sjmacd for (i = 0; pagename[i] != '\0' && pagename[i] != '('; i++); 24821495Sjmacd 24921495Sjmacd manpage_pagename = (char *)xmalloc (1 + i); 25021495Sjmacd strncpy (manpage_pagename, pagename, i); 25121495Sjmacd manpage_pagename[i] = '\0'; 25221495Sjmacd 25321495Sjmacd if (pagename[i] == '(') 25421495Sjmacd { 25521495Sjmacd int start; 25621495Sjmacd 25721495Sjmacd start = i + 1; 25821495Sjmacd 25921495Sjmacd for (i = start; pagename[i] != '\0' && pagename[i] != ')'; i++); 26021495Sjmacd 26121495Sjmacd manpage_section = (char *)xmalloc (1 + (i - start)); 26221495Sjmacd strncpy (manpage_section, pagename + start, (i - start)); 26321495Sjmacd manpage_section[i - start] = '\0'; 26421495Sjmacd } 26521495Sjmacd} 26621495Sjmacd 26756160Sru#if PIPE_USE_FORK 26821495Sjmacdstatic void 269146515Srureap_children (int sig) 27021495Sjmacd{ 27156160Sru wait (NULL); 27221495Sjmacd} 27356160Sru#endif 27421495Sjmacd 27521495Sjmacdstatic char * 276146515Sruget_manpage_contents (char *pagename) 27721495Sjmacd{ 27821495Sjmacd static char *formatter_args[4] = { (char *)NULL }; 27921495Sjmacd int pipes[2]; 28021495Sjmacd pid_t child; 281146515Sru RETSIGTYPE (*sigsave) (int signum); 28256160Sru char *formatted_page = NULL; 28321495Sjmacd int arg_index = 1; 28421495Sjmacd 28521495Sjmacd if (formatter_args[0] == (char *)NULL) 28621495Sjmacd formatter_args[0] = find_man_formatter (); 28721495Sjmacd 28821495Sjmacd if (formatter_args[0] == (char *)NULL) 28921495Sjmacd return ((char *)NULL); 29021495Sjmacd 29121495Sjmacd get_page_and_section (pagename); 29221495Sjmacd 29321495Sjmacd if (manpage_section != (char *)NULL) 29421495Sjmacd formatter_args[arg_index++] = manpage_section; 29521495Sjmacd 29621495Sjmacd formatter_args[arg_index++] = manpage_pagename; 29721495Sjmacd formatter_args[arg_index] = (char *)NULL; 29821495Sjmacd 29921495Sjmacd /* Open a pipe to this program, read the output, and save it away 30021495Sjmacd in FORMATTED_PAGE. The reader end of the pipe is pipes[0]; the 30121495Sjmacd writer end is pipes[1]. */ 30256160Sru#if PIPE_USE_FORK 30321495Sjmacd pipe (pipes); 30421495Sjmacd 30556160Sru sigsave = signal (SIGCHLD, reap_children); 30621495Sjmacd 30721495Sjmacd child = fork (); 30821495Sjmacd if (child == -1) 30921495Sjmacd return ((char *)NULL); 31021495Sjmacd 31121495Sjmacd if (child != 0) 31221495Sjmacd { 31321495Sjmacd /* In the parent, close the writing end of the pipe, and read from 31442660Smarkm the exec'd child. */ 31521495Sjmacd close (pipes[1]); 31621495Sjmacd formatted_page = read_from_fd (pipes[0]); 31721495Sjmacd close (pipes[0]); 31856160Sru signal (SIGCHLD, sigsave); 31921495Sjmacd } 32021495Sjmacd else 32156160Sru { /* In the child, close the read end of the pipe, make the write end 32242660Smarkm of the pipe be stdout, and execute the man page formatter. */ 32321495Sjmacd close (pipes[0]); 32456160Sru freopen (NULL_DEVICE, "w", stderr); 32556160Sru freopen (NULL_DEVICE, "r", stdin); 32621495Sjmacd dup2 (pipes[1], fileno (stdout)); 32721495Sjmacd 32821495Sjmacd execv (formatter_args[0], formatter_args); 32921495Sjmacd 33021495Sjmacd /* If we get here, we couldn't exec, so close out the pipe and 33142660Smarkm exit. */ 33221495Sjmacd close (pipes[1]); 33356160Sru xexit (0); 33421495Sjmacd } 33556160Sru#else /* !PIPE_USE_FORK */ 33656160Sru /* Cannot fork/exec, but can popen/pclose. */ 33756160Sru { 33856160Sru FILE *fpipe; 33956160Sru char *cmdline = xmalloc (strlen (formatter_args[0]) 34056160Sru + strlen (manpage_pagename) 34156160Sru + (arg_index > 2 ? strlen (manpage_section) : 0) 34256160Sru + 3); 34356160Sru int save_stderr = dup (fileno (stderr)); 34456160Sru int fd_err = open (NULL_DEVICE, O_WRONLY, 0666); 34521495Sjmacd 34656160Sru if (fd_err > 2) 34756160Sru dup2 (fd_err, fileno (stderr)); /* Don't print errors. */ 34856160Sru sprintf (cmdline, "%s %s %s", formatter_args[0], manpage_pagename, 34956160Sru arg_index > 2 ? manpage_section : ""); 35056160Sru fpipe = popen (cmdline, "r"); 35156160Sru free (cmdline); 35256160Sru if (fd_err > 2) 35356160Sru close (fd_err); 35456160Sru dup2 (save_stderr, fileno (stderr)); 35556160Sru if (fpipe == 0) 35656160Sru return ((char *)NULL); 35756160Sru formatted_page = read_from_fd (fileno (fpipe)); 35856160Sru if (pclose (fpipe) == -1) 35956160Sru { 36056160Sru if (formatted_page) 36156160Sru free (formatted_page); 36256160Sru return ((char *)NULL); 36356160Sru } 36456160Sru } 36556160Sru#endif /* !PIPE_USE_FORK */ 36656160Sru 36721495Sjmacd /* If we have the page, then clean it up. */ 36821495Sjmacd if (formatted_page) 36921495Sjmacd clean_manpage (formatted_page); 37021495Sjmacd 37121495Sjmacd return (formatted_page); 37221495Sjmacd} 37321495Sjmacd 37421495Sjmacdstatic void 375146515Sruclean_manpage (char *manpage) 37621495Sjmacd{ 37721495Sjmacd register int i, j; 37821495Sjmacd int newline_count = 0; 37921495Sjmacd char *newpage; 38021495Sjmacd 38121495Sjmacd newpage = (char *)xmalloc (1 + strlen (manpage)); 38221495Sjmacd 38342660Smarkm for (i = 0, j = 0; (newpage[j] = manpage[i]); i++, j++) 38421495Sjmacd { 38521495Sjmacd if (manpage[i] == '\n') 38642660Smarkm newline_count++; 38721495Sjmacd else 38842660Smarkm newline_count = 0; 38921495Sjmacd 39021495Sjmacd if (newline_count == 3) 39142660Smarkm { 39242660Smarkm j--; 39342660Smarkm newline_count--; 39442660Smarkm } 39521495Sjmacd 39693139Sru /* A malformed man page could have a \b as its first character, 39793139Sru in which case decrementing j by 2 will cause us to write into 39893139Sru newpage[-1], smashing the hidden info stored there by malloc. */ 399116525Sru if (manpage[i] == '\b' || (manpage[i] == '\f' && j > 0)) 40042660Smarkm j -= 2; 40193139Sru else if (!raw_escapes_p) 40293139Sru { 40393139Sru /* Remove the ANSI escape sequences for color, boldface, 40493139Sru underlining, and italics, generated by some versions of 40593139Sru Groff. */ 40693139Sru if (manpage[i] == '\033' && manpage[i + 1] == '[' 40793139Sru && isdigit (manpage[i + 2])) 40893139Sru { 40993139Sru if (isdigit (manpage[i + 3]) && manpage[i + 4] == 'm') 41093139Sru { 41193139Sru i += 4; 41293139Sru j--; 41393139Sru } 41493139Sru else if (manpage[i + 3] == 'm') 41593139Sru { 41693139Sru i += 3; 41793139Sru j--; 41893139Sru } 41993139Sru /* Else do nothing: it's some unknown escape sequence, 42093139Sru so let's leave it alone. */ 42193139Sru } 42293139Sru } 42321495Sjmacd } 42421495Sjmacd 42593139Sru newpage[j++] = 0; 42621495Sjmacd 42721495Sjmacd strcpy (manpage, newpage); 42821495Sjmacd free (newpage); 42921495Sjmacd} 43021495Sjmacd 43121495Sjmacdstatic NODE * 432146515Srumanpage_node_of_file_buffer (FILE_BUFFER *file_buffer, char *pagename) 43321495Sjmacd{ 43421495Sjmacd NODE *node = (NODE *)NULL; 43521495Sjmacd TAG *tag = (TAG *)NULL; 43621495Sjmacd 43721495Sjmacd if (file_buffer->contents) 43821495Sjmacd { 43921495Sjmacd register int i; 44021495Sjmacd 44142660Smarkm for (i = 0; (tag = file_buffer->tags[i]); i++) 44242660Smarkm { 44342660Smarkm if (strcasecmp (pagename, tag->nodename) == 0) 44442660Smarkm break; 44542660Smarkm } 44621495Sjmacd } 44721495Sjmacd 44821495Sjmacd if (tag) 44921495Sjmacd { 45021495Sjmacd node = (NODE *)xmalloc (sizeof (NODE)); 45121495Sjmacd node->filename = file_buffer->filename; 45256160Sru node->nodename = xstrdup (tag->nodename); 45321495Sjmacd node->contents = file_buffer->contents + tag->nodestart; 45421495Sjmacd node->nodelen = tag->nodelen; 45521495Sjmacd node->flags = 0; 45656160Sru node->display_pos = 0; 45721495Sjmacd node->parent = (char *)NULL; 45821495Sjmacd node->flags = (N_HasTagsTable | N_IsManPage); 45921495Sjmacd node->contents += skip_node_separator (node->contents); 46021495Sjmacd } 46121495Sjmacd 46221495Sjmacd return (node); 46321495Sjmacd} 46421495Sjmacd 46521495Sjmacdstatic char * 466146515Sruread_from_fd (int fd) 46721495Sjmacd{ 46821495Sjmacd struct timeval timeout; 46921495Sjmacd char *buffer = (char *)NULL; 47021495Sjmacd int bsize = 0; 47121495Sjmacd int bindex = 0; 47221495Sjmacd int select_result; 47321495Sjmacd#if defined (FD_SET) 47421495Sjmacd fd_set read_fds; 47521495Sjmacd 47621495Sjmacd timeout.tv_sec = 15; 47721495Sjmacd timeout.tv_usec = 0; 47821495Sjmacd 47921495Sjmacd FD_ZERO (&read_fds); 48021495Sjmacd FD_SET (fd, &read_fds); 48121495Sjmacd 48221495Sjmacd select_result = select (fd + 1, fd_set_cast (&read_fds), 0, 0, &timeout); 48321495Sjmacd#else /* !FD_SET */ 48421495Sjmacd select_result = 1; 48521495Sjmacd#endif /* !FD_SET */ 48621495Sjmacd 48721495Sjmacd switch (select_result) 48821495Sjmacd { 48921495Sjmacd case 0: 49021495Sjmacd case -1: 49121495Sjmacd break; 49221495Sjmacd 49321495Sjmacd default: 49421495Sjmacd { 49521495Sjmacd int amount_read; 49621495Sjmacd int done = 0; 49721495Sjmacd 49821495Sjmacd while (!done) 49921495Sjmacd { 50021495Sjmacd while ((bindex + 1024) > (bsize)) 50121495Sjmacd buffer = (char *)xrealloc (buffer, (bsize += 1024)); 50221495Sjmacd buffer[bindex] = '\0'; 50321495Sjmacd 50421495Sjmacd amount_read = read (fd, buffer + bindex, 1023); 50521495Sjmacd 50621495Sjmacd if (amount_read < 0) 50721495Sjmacd { 50821495Sjmacd done = 1; 50921495Sjmacd } 51021495Sjmacd else 51121495Sjmacd { 51221495Sjmacd bindex += amount_read; 51321495Sjmacd buffer[bindex] = '\0'; 51421495Sjmacd if (amount_read == 0) 51521495Sjmacd done = 1; 51621495Sjmacd } 51721495Sjmacd } 51821495Sjmacd } 51921495Sjmacd } 52021495Sjmacd 52121495Sjmacd if ((buffer != (char *)NULL) && (*buffer == '\0')) 52221495Sjmacd { 52321495Sjmacd free (buffer); 52421495Sjmacd buffer = (char *)NULL; 52521495Sjmacd } 52621495Sjmacd 52721495Sjmacd return (buffer); 52821495Sjmacd} 52921495Sjmacd 53021495Sjmacdstatic char *reference_section_starters[] = 53121495Sjmacd{ 53221495Sjmacd "\nRELATED INFORMATION", 53321495Sjmacd "\nRELATED\tINFORMATION", 53421495Sjmacd "RELATED INFORMATION\n", 53521495Sjmacd "RELATED\tINFORMATION\n", 53621495Sjmacd "\nSEE ALSO", 53721495Sjmacd "\nSEE\tALSO", 53821495Sjmacd "SEE ALSO\n", 53921495Sjmacd "SEE\tALSO\n", 54021495Sjmacd (char *)NULL 54121495Sjmacd}; 54221495Sjmacd 54321495Sjmacdstatic SEARCH_BINDING frs_binding; 54421495Sjmacd 54521495Sjmacdstatic SEARCH_BINDING * 546146515Srufind_reference_section (NODE *node) 54721495Sjmacd{ 54821495Sjmacd register int i; 54921495Sjmacd long position = -1; 55021495Sjmacd 55121495Sjmacd frs_binding.buffer = node->contents; 55221495Sjmacd frs_binding.start = 0; 55321495Sjmacd frs_binding.end = node->nodelen; 55421495Sjmacd frs_binding.flags = S_SkipDest; 55521495Sjmacd 55621495Sjmacd for (i = 0; reference_section_starters[i] != (char *)NULL; i++) 55721495Sjmacd { 55821495Sjmacd position = search_forward (reference_section_starters[i], &frs_binding); 55921495Sjmacd if (position != -1) 56042660Smarkm break; 56121495Sjmacd } 56221495Sjmacd 56321495Sjmacd if (position == -1) 56421495Sjmacd return ((SEARCH_BINDING *)NULL); 56521495Sjmacd 56621495Sjmacd /* We found the start of the reference section, and point is right after 56721495Sjmacd the string which starts it. The text from here to the next header 56821495Sjmacd (or end of buffer) contains the only references in this manpage. */ 56921495Sjmacd frs_binding.start = position; 57021495Sjmacd 57121495Sjmacd for (i = frs_binding.start; i < frs_binding.end - 2; i++) 57221495Sjmacd { 57321495Sjmacd if ((frs_binding.buffer[i] == '\n') && 57442660Smarkm (!whitespace (frs_binding.buffer[i + 1]))) 57542660Smarkm { 57642660Smarkm frs_binding.end = i; 57742660Smarkm break; 57842660Smarkm } 57921495Sjmacd } 58021495Sjmacd 58121495Sjmacd return (&frs_binding); 58221495Sjmacd} 58321495Sjmacd 58421495SjmacdREFERENCE ** 585146515Sruxrefs_of_manpage (NODE *node) 58621495Sjmacd{ 58721495Sjmacd SEARCH_BINDING *reference_section; 58821495Sjmacd REFERENCE **refs = (REFERENCE **)NULL; 58921495Sjmacd int refs_index = 0; 59021495Sjmacd int refs_slots = 0; 59121495Sjmacd long position; 59221495Sjmacd 59321495Sjmacd reference_section = find_reference_section (node); 59421495Sjmacd 59521495Sjmacd if (reference_section == (SEARCH_BINDING *)NULL) 59621495Sjmacd return ((REFERENCE **)NULL); 59721495Sjmacd 59821495Sjmacd /* Grovel the reference section building a list of references found there. 59921495Sjmacd A reference is alphabetic characters followed by non-whitespace text 60021495Sjmacd within parenthesis. */ 60121495Sjmacd reference_section->flags = 0; 60221495Sjmacd 60321495Sjmacd while ((position = search_forward ("(", reference_section)) != -1) 60421495Sjmacd { 60521495Sjmacd register int start, end; 60621495Sjmacd 60721495Sjmacd for (start = position; start > reference_section->start; start--) 60842660Smarkm if (whitespace (reference_section->buffer[start])) 60942660Smarkm break; 61021495Sjmacd 61121495Sjmacd start++; 61221495Sjmacd 61321495Sjmacd for (end = position; end < reference_section->end; end++) 61442660Smarkm { 61542660Smarkm if (whitespace (reference_section->buffer[end])) 61642660Smarkm { 61742660Smarkm end = start; 61842660Smarkm break; 61942660Smarkm } 62021495Sjmacd 62142660Smarkm if (reference_section->buffer[end] == ')') 62242660Smarkm { 62342660Smarkm end++; 62442660Smarkm break; 62542660Smarkm } 62642660Smarkm } 62721495Sjmacd 62821495Sjmacd if (end != start) 62942660Smarkm { 63042660Smarkm REFERENCE *entry; 63142660Smarkm int len = end - start; 63221495Sjmacd 63342660Smarkm entry = (REFERENCE *)xmalloc (sizeof (REFERENCE)); 63442660Smarkm entry->label = (char *)xmalloc (1 + len); 63542660Smarkm strncpy (entry->label, (reference_section->buffer) + start, len); 63642660Smarkm entry->label[len] = '\0'; 63742660Smarkm entry->filename = xstrdup (node->filename); 63842660Smarkm entry->nodename = xstrdup (entry->label); 63942660Smarkm entry->start = start; 64042660Smarkm entry->end = end; 64121495Sjmacd 64242660Smarkm add_pointer_to_array 64342660Smarkm (entry, refs_index, refs, refs_slots, 10, REFERENCE *); 64442660Smarkm } 64521495Sjmacd 64621495Sjmacd reference_section->start = position + 1; 64721495Sjmacd } 64821495Sjmacd 64921495Sjmacd return (refs); 65021495Sjmacd} 65121495Sjmacd 65221495Sjmacdlong 653146515Srulocate_manpage_xref (NODE *node, long int start, int dir) 65421495Sjmacd{ 65521495Sjmacd REFERENCE **refs; 65621495Sjmacd long position = -1; 65721495Sjmacd 65821495Sjmacd refs = xrefs_of_manpage (node); 65921495Sjmacd 66021495Sjmacd if (refs) 66121495Sjmacd { 66221495Sjmacd register int i, count; 66321495Sjmacd REFERENCE *entry; 66421495Sjmacd 66521495Sjmacd for (i = 0; refs[i]; i++); 66621495Sjmacd count = i; 66721495Sjmacd 66821495Sjmacd if (dir > 0) 66942660Smarkm { 67042660Smarkm for (i = 0; (entry = refs[i]); i++) 67142660Smarkm if (entry->start > start) 67242660Smarkm { 67342660Smarkm position = entry->start; 67442660Smarkm break; 67542660Smarkm } 67642660Smarkm } 67721495Sjmacd else 67842660Smarkm { 67942660Smarkm for (i = count - 1; i > -1; i--) 68042660Smarkm { 68142660Smarkm entry = refs[i]; 68221495Sjmacd 68342660Smarkm if (entry->start < start) 68442660Smarkm { 68542660Smarkm position = entry->start; 68642660Smarkm break; 68742660Smarkm } 68842660Smarkm } 68942660Smarkm } 69021495Sjmacd 69121495Sjmacd info_free_references (refs); 69221495Sjmacd } 69321495Sjmacd return (position); 69421495Sjmacd} 69521495Sjmacd 69621495Sjmacd/* This one was a little tricky. The binding buffer that is passed in has 69721495Sjmacd a START and END value of 0 -- strlen (window-line-containing-point). 69821495Sjmacd The BUFFER is a pointer to the start of that line. */ 69921495SjmacdREFERENCE ** 700146515Srumanpage_xrefs_in_binding (NODE *node, SEARCH_BINDING *binding) 70121495Sjmacd{ 70221495Sjmacd register int i; 70321495Sjmacd REFERENCE **all_refs = xrefs_of_manpage (node); 70421495Sjmacd REFERENCE **brefs = (REFERENCE **)NULL; 70521495Sjmacd REFERENCE *entry; 70621495Sjmacd int brefs_index = 0; 70721495Sjmacd int brefs_slots = 0; 70821495Sjmacd int start, end; 70921495Sjmacd 71021495Sjmacd if (!all_refs) 71121495Sjmacd return ((REFERENCE **)NULL); 71221495Sjmacd 71321495Sjmacd start = binding->start + (binding->buffer - node->contents); 71421495Sjmacd end = binding->end + (binding->buffer - node->contents); 71521495Sjmacd 71642660Smarkm for (i = 0; (entry = all_refs[i]); i++) 71721495Sjmacd { 71821495Sjmacd if ((entry->start > start) && (entry->end < end)) 71942660Smarkm { 72042660Smarkm add_pointer_to_array 72142660Smarkm (entry, brefs_index, brefs, brefs_slots, 10, REFERENCE *); 72242660Smarkm } 72321495Sjmacd else 72442660Smarkm { 72542660Smarkm maybe_free (entry->label); 72642660Smarkm maybe_free (entry->filename); 72742660Smarkm maybe_free (entry->nodename); 72842660Smarkm free (entry); 72942660Smarkm } 73021495Sjmacd } 73121495Sjmacd 73221495Sjmacd free (all_refs); 73321495Sjmacd return (brefs); 73421495Sjmacd} 735