man.c revision 116525
142660Smarkm/* man.c: How to read and format man files. 2116525Sru $Id: man.c,v 1.2 2003/05/13 16:37:54 karl Exp $ 321495Sjmacd 4116525Sru Copyright (C) 1995, 1997, 1998, 1999, 2000, 2002, 2003 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 5621495Sjmacdstatic char *read_from_fd (); 5721495Sjmacdstatic void clean_manpage (); 5821495Sjmacdstatic NODE *manpage_node_of_file_buffer (); 5921495Sjmacdstatic char *get_manpage_contents (); 6021495Sjmacd 6121495SjmacdNODE * 6221495Sjmacdmake_manpage_node (pagename) 6321495Sjmacd char *pagename; 6421495Sjmacd{ 6521495Sjmacd return (info_get_node (MANPAGE_FILE_BUFFER_NAME, pagename)); 6621495Sjmacd} 6721495Sjmacd 6821495SjmacdNODE * 6921495Sjmacdget_manpage_node (file_buffer, pagename) 7021495Sjmacd FILE_BUFFER *file_buffer; 7121495Sjmacd char *pagename; 7221495Sjmacd{ 7321495Sjmacd NODE *node; 7421495Sjmacd 7521495Sjmacd node = manpage_node_of_file_buffer (file_buffer, pagename); 7621495Sjmacd 7721495Sjmacd if (!node) 7821495Sjmacd { 7921495Sjmacd char *page; 8021495Sjmacd 8121495Sjmacd page = get_manpage_contents (pagename); 8221495Sjmacd 8321495Sjmacd if (page) 8442660Smarkm { 8542660Smarkm char header[1024]; 8642660Smarkm long oldsize, newsize; 8742660Smarkm int hlen, plen; 8856160Sru char *old_contents = file_buffer->contents; 8921495Sjmacd 9042660Smarkm sprintf (header, "\n\n%c\n%s %s, %s %s, %s (dir)\n\n", 9142660Smarkm INFO_COOKIE, 9242660Smarkm INFO_FILE_LABEL, file_buffer->filename, 9342660Smarkm INFO_NODE_LABEL, pagename, 9442660Smarkm INFO_UP_LABEL); 9542660Smarkm oldsize = file_buffer->filesize; 9642660Smarkm hlen = strlen (header); 9742660Smarkm plen = strlen (page); 9842660Smarkm newsize = (oldsize + hlen + plen); 9942660Smarkm file_buffer->contents = 10042660Smarkm (char *)xrealloc (file_buffer->contents, 1 + newsize); 10142660Smarkm memcpy (file_buffer->contents + oldsize, header, hlen); 10256160Sru memcpy (file_buffer->contents + oldsize + hlen, page, plen); 10342660Smarkm file_buffer->contents[newsize] = '\0'; 10442660Smarkm file_buffer->filesize = newsize; 10542660Smarkm file_buffer->finfo.st_size = newsize; 10642660Smarkm build_tags_and_nodes (file_buffer); 10742660Smarkm free (page); 10856160Sru /* We have just relocated file_buffer->contents from under 10956160Sru the feet of info_windows[] array. Therefore, all the 11056160Sru nodes on that list which are showing man pages have their 11156160Sru contents member pointing into the blue. Undo that harm. */ 11256160Sru if (old_contents && oldsize && old_contents != file_buffer->contents) 11356160Sru { 11456160Sru int iw; 11556160Sru INFO_WINDOW *info_win; 11656160Sru char *old_contents_end = old_contents + oldsize; 11756160Sru 11856160Sru for (iw = 0; (info_win = info_windows[iw]); iw++) 11956160Sru { 12056160Sru int in; 12156160Sru 12256160Sru for (in = 0; in < info_win->nodes_index; in++) 12356160Sru { 12456160Sru NODE *node = info_win->nodes[in]; 12556160Sru 12656160Sru /* It really only suffices to see that node->filename 12756160Sru is "*manpages*". But after several hours of 12856160Sru debugging this, would you blame me for being a bit 12956160Sru paranoid? */ 13056160Sru if (node && node->filename && node->contents && 13156160Sru strcmp (node->filename, 13256160Sru MANPAGE_FILE_BUFFER_NAME) == 0 && 13356160Sru node->contents >= old_contents && 13456160Sru node->contents + node->nodelen <= old_contents_end) 13556160Sru { 13656160Sru info_win->nodes[in] = 13756160Sru manpage_node_of_file_buffer (file_buffer, 13856160Sru node->nodename); 13956160Sru free (node->nodename); 14056160Sru free (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 * 15421495Sjmacdcreate_manpage_file_buffer () 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 * 17121495Sjmacdexecutable_file_in_path (filename, path) 17221495Sjmacd char *filename, *path; 17321495Sjmacd{ 17421495Sjmacd struct stat finfo; 17521495Sjmacd char *temp_dirname; 17621495Sjmacd int statable, dirname_index; 17721495Sjmacd 17821495Sjmacd dirname_index = 0; 17921495Sjmacd 18042660Smarkm while ((temp_dirname = extract_colon_unit (path, &dirname_index))) 18121495Sjmacd { 18221495Sjmacd char *temp; 18356160Sru char *temp_end; 18456160Sru int i; 18521495Sjmacd 18621495Sjmacd /* Expand a leading tilde if one is present. */ 18721495Sjmacd if (*temp_dirname == '~') 18842660Smarkm { 18942660Smarkm char *expanded_dirname; 19021495Sjmacd 19142660Smarkm expanded_dirname = tilde_expand_word (temp_dirname); 19242660Smarkm free (temp_dirname); 19342660Smarkm temp_dirname = expanded_dirname; 19442660Smarkm } 19521495Sjmacd 19656160Sru temp = (char *)xmalloc (34 + strlen (temp_dirname) + strlen (filename)); 19721495Sjmacd strcpy (temp, temp_dirname); 19856160Sru if (!IS_SLASH (temp[(strlen (temp)) - 1])) 19942660Smarkm strcat (temp, "/"); 20021495Sjmacd strcat (temp, filename); 20156160Sru temp_end = temp + strlen (temp); 20221495Sjmacd 20321495Sjmacd free (temp_dirname); 20421495Sjmacd 20556160Sru /* Look for FILENAME, possibly with any of the extensions 20656160Sru in EXEC_EXTENSIONS[]. */ 20756160Sru for (i = 0; exec_extensions[i]; i++) 20856160Sru { 20956160Sru if (exec_extensions[i][0]) 21056160Sru strcpy (temp_end, exec_extensions[i]); 21156160Sru statable = (stat (temp, &finfo) == 0); 21221495Sjmacd 21356160Sru /* If we have found a regular executable file, then use it. */ 21456160Sru if ((statable) && (S_ISREG (finfo.st_mode)) && 21556160Sru (access (temp, X_OK) == 0)) 21656160Sru return (temp); 21756160Sru } 21856160Sru 21956160Sru free (temp); 22021495Sjmacd } 22121495Sjmacd return ((char *)NULL); 22221495Sjmacd} 22321495Sjmacd 22421495Sjmacd/* Return the full pathname of the system man page formatter. */ 22521495Sjmacdstatic char * 22621495Sjmacdfind_man_formatter () 22721495Sjmacd{ 22821495Sjmacd return (executable_file_in_path ("man", (char *)getenv ("PATH"))); 22921495Sjmacd} 23021495Sjmacd 23121495Sjmacdstatic char *manpage_pagename = (char *)NULL; 23221495Sjmacdstatic char *manpage_section = (char *)NULL; 23321495Sjmacd 23421495Sjmacdstatic void 23521495Sjmacdget_page_and_section (pagename) 23621495Sjmacd char *pagename; 23721495Sjmacd{ 23821495Sjmacd register int i; 23921495Sjmacd 24021495Sjmacd if (manpage_pagename) 24121495Sjmacd free (manpage_pagename); 24221495Sjmacd 24321495Sjmacd if (manpage_section) 24421495Sjmacd free (manpage_section); 24521495Sjmacd 24621495Sjmacd manpage_pagename = (char *)NULL; 24721495Sjmacd manpage_section = (char *)NULL; 24821495Sjmacd 24921495Sjmacd for (i = 0; pagename[i] != '\0' && pagename[i] != '('; i++); 25021495Sjmacd 25121495Sjmacd manpage_pagename = (char *)xmalloc (1 + i); 25221495Sjmacd strncpy (manpage_pagename, pagename, i); 25321495Sjmacd manpage_pagename[i] = '\0'; 25421495Sjmacd 25521495Sjmacd if (pagename[i] == '(') 25621495Sjmacd { 25721495Sjmacd int start; 25821495Sjmacd 25921495Sjmacd start = i + 1; 26021495Sjmacd 26121495Sjmacd for (i = start; pagename[i] != '\0' && pagename[i] != ')'; i++); 26221495Sjmacd 26321495Sjmacd manpage_section = (char *)xmalloc (1 + (i - start)); 26421495Sjmacd strncpy (manpage_section, pagename + start, (i - start)); 26521495Sjmacd manpage_section[i - start] = '\0'; 26621495Sjmacd } 26721495Sjmacd} 26821495Sjmacd 26956160Sru#if PIPE_USE_FORK 27021495Sjmacdstatic void 27121495Sjmacdreap_children (sig) 27221495Sjmacd int sig; 27321495Sjmacd{ 27456160Sru wait (NULL); 27521495Sjmacd} 27656160Sru#endif 27721495Sjmacd 27821495Sjmacdstatic char * 27921495Sjmacdget_manpage_contents (pagename) 28021495Sjmacd char *pagename; 28121495Sjmacd{ 28221495Sjmacd static char *formatter_args[4] = { (char *)NULL }; 28321495Sjmacd int pipes[2]; 28421495Sjmacd pid_t child; 28556160Sru RETSIGTYPE (*sigsave) (); 28656160Sru char *formatted_page = NULL; 28721495Sjmacd int arg_index = 1; 28821495Sjmacd 28921495Sjmacd if (formatter_args[0] == (char *)NULL) 29021495Sjmacd formatter_args[0] = find_man_formatter (); 29121495Sjmacd 29221495Sjmacd if (formatter_args[0] == (char *)NULL) 29321495Sjmacd return ((char *)NULL); 29421495Sjmacd 29521495Sjmacd get_page_and_section (pagename); 29621495Sjmacd 29721495Sjmacd if (manpage_section != (char *)NULL) 29821495Sjmacd formatter_args[arg_index++] = manpage_section; 29921495Sjmacd 30021495Sjmacd formatter_args[arg_index++] = manpage_pagename; 30121495Sjmacd formatter_args[arg_index] = (char *)NULL; 30221495Sjmacd 30321495Sjmacd /* Open a pipe to this program, read the output, and save it away 30421495Sjmacd in FORMATTED_PAGE. The reader end of the pipe is pipes[0]; the 30521495Sjmacd writer end is pipes[1]. */ 30656160Sru#if PIPE_USE_FORK 30721495Sjmacd pipe (pipes); 30821495Sjmacd 30956160Sru sigsave = signal (SIGCHLD, reap_children); 31021495Sjmacd 31121495Sjmacd child = fork (); 31221495Sjmacd if (child == -1) 31321495Sjmacd return ((char *)NULL); 31421495Sjmacd 31521495Sjmacd if (child != 0) 31621495Sjmacd { 31721495Sjmacd /* In the parent, close the writing end of the pipe, and read from 31842660Smarkm the exec'd child. */ 31921495Sjmacd close (pipes[1]); 32021495Sjmacd formatted_page = read_from_fd (pipes[0]); 32121495Sjmacd close (pipes[0]); 32256160Sru signal (SIGCHLD, sigsave); 32321495Sjmacd } 32421495Sjmacd else 32556160Sru { /* In the child, close the read end of the pipe, make the write end 32642660Smarkm of the pipe be stdout, and execute the man page formatter. */ 32721495Sjmacd close (pipes[0]); 32856160Sru freopen (NULL_DEVICE, "w", stderr); 32956160Sru freopen (NULL_DEVICE, "r", stdin); 33021495Sjmacd dup2 (pipes[1], fileno (stdout)); 33121495Sjmacd 33221495Sjmacd execv (formatter_args[0], formatter_args); 33321495Sjmacd 33421495Sjmacd /* If we get here, we couldn't exec, so close out the pipe and 33542660Smarkm exit. */ 33621495Sjmacd close (pipes[1]); 33756160Sru xexit (0); 33821495Sjmacd } 33956160Sru#else /* !PIPE_USE_FORK */ 34056160Sru /* Cannot fork/exec, but can popen/pclose. */ 34156160Sru { 34256160Sru FILE *fpipe; 34356160Sru char *cmdline = xmalloc (strlen (formatter_args[0]) 34456160Sru + strlen (manpage_pagename) 34556160Sru + (arg_index > 2 ? strlen (manpage_section) : 0) 34656160Sru + 3); 34756160Sru int save_stderr = dup (fileno (stderr)); 34856160Sru int fd_err = open (NULL_DEVICE, O_WRONLY, 0666); 34921495Sjmacd 35056160Sru if (fd_err > 2) 35156160Sru dup2 (fd_err, fileno (stderr)); /* Don't print errors. */ 35256160Sru sprintf (cmdline, "%s %s %s", formatter_args[0], manpage_pagename, 35356160Sru arg_index > 2 ? manpage_section : ""); 35456160Sru fpipe = popen (cmdline, "r"); 35556160Sru free (cmdline); 35656160Sru if (fd_err > 2) 35756160Sru close (fd_err); 35856160Sru dup2 (save_stderr, fileno (stderr)); 35956160Sru if (fpipe == 0) 36056160Sru return ((char *)NULL); 36156160Sru formatted_page = read_from_fd (fileno (fpipe)); 36256160Sru if (pclose (fpipe) == -1) 36356160Sru { 36456160Sru if (formatted_page) 36556160Sru free (formatted_page); 36656160Sru return ((char *)NULL); 36756160Sru } 36856160Sru } 36956160Sru#endif /* !PIPE_USE_FORK */ 37056160Sru 37121495Sjmacd /* If we have the page, then clean it up. */ 37221495Sjmacd if (formatted_page) 37321495Sjmacd clean_manpage (formatted_page); 37421495Sjmacd 37521495Sjmacd return (formatted_page); 37621495Sjmacd} 37721495Sjmacd 37821495Sjmacdstatic void 37921495Sjmacdclean_manpage (manpage) 38021495Sjmacd char *manpage; 38121495Sjmacd{ 38221495Sjmacd register int i, j; 38321495Sjmacd int newline_count = 0; 38421495Sjmacd char *newpage; 38521495Sjmacd 38621495Sjmacd newpage = (char *)xmalloc (1 + strlen (manpage)); 38721495Sjmacd 38842660Smarkm for (i = 0, j = 0; (newpage[j] = manpage[i]); i++, j++) 38921495Sjmacd { 39021495Sjmacd if (manpage[i] == '\n') 39142660Smarkm newline_count++; 39221495Sjmacd else 39342660Smarkm newline_count = 0; 39421495Sjmacd 39521495Sjmacd if (newline_count == 3) 39642660Smarkm { 39742660Smarkm j--; 39842660Smarkm newline_count--; 39942660Smarkm } 40021495Sjmacd 40193139Sru /* A malformed man page could have a \b as its first character, 40293139Sru in which case decrementing j by 2 will cause us to write into 40393139Sru newpage[-1], smashing the hidden info stored there by malloc. */ 404116525Sru if (manpage[i] == '\b' || (manpage[i] == '\f' && j > 0)) 40542660Smarkm j -= 2; 40693139Sru else if (!raw_escapes_p) 40793139Sru { 40893139Sru /* Remove the ANSI escape sequences for color, boldface, 40993139Sru underlining, and italics, generated by some versions of 41093139Sru Groff. */ 41193139Sru if (manpage[i] == '\033' && manpage[i + 1] == '[' 41293139Sru && isdigit (manpage[i + 2])) 41393139Sru { 41493139Sru if (isdigit (manpage[i + 3]) && manpage[i + 4] == 'm') 41593139Sru { 41693139Sru i += 4; 41793139Sru j--; 41893139Sru } 41993139Sru else if (manpage[i + 3] == 'm') 42093139Sru { 42193139Sru i += 3; 42293139Sru j--; 42393139Sru } 42493139Sru /* Else do nothing: it's some unknown escape sequence, 42593139Sru so let's leave it alone. */ 42693139Sru } 42793139Sru } 42821495Sjmacd } 42921495Sjmacd 43093139Sru newpage[j++] = 0; 43121495Sjmacd 43221495Sjmacd strcpy (manpage, newpage); 43321495Sjmacd free (newpage); 43421495Sjmacd} 43521495Sjmacd 43621495Sjmacdstatic NODE * 43721495Sjmacdmanpage_node_of_file_buffer (file_buffer, pagename) 43821495Sjmacd FILE_BUFFER *file_buffer; 43921495Sjmacd char *pagename; 44021495Sjmacd{ 44121495Sjmacd NODE *node = (NODE *)NULL; 44221495Sjmacd TAG *tag = (TAG *)NULL; 44321495Sjmacd 44421495Sjmacd if (file_buffer->contents) 44521495Sjmacd { 44621495Sjmacd register int i; 44721495Sjmacd 44842660Smarkm for (i = 0; (tag = file_buffer->tags[i]); i++) 44942660Smarkm { 45042660Smarkm if (strcasecmp (pagename, tag->nodename) == 0) 45142660Smarkm break; 45242660Smarkm } 45321495Sjmacd } 45421495Sjmacd 45521495Sjmacd if (tag) 45621495Sjmacd { 45721495Sjmacd node = (NODE *)xmalloc (sizeof (NODE)); 45821495Sjmacd node->filename = file_buffer->filename; 45956160Sru node->nodename = xstrdup (tag->nodename); 46021495Sjmacd node->contents = file_buffer->contents + tag->nodestart; 46121495Sjmacd node->nodelen = tag->nodelen; 46221495Sjmacd node->flags = 0; 46356160Sru node->display_pos = 0; 46421495Sjmacd node->parent = (char *)NULL; 46521495Sjmacd node->flags = (N_HasTagsTable | N_IsManPage); 46621495Sjmacd node->contents += skip_node_separator (node->contents); 46721495Sjmacd } 46821495Sjmacd 46921495Sjmacd return (node); 47021495Sjmacd} 47121495Sjmacd 47221495Sjmacdstatic char * 47321495Sjmacdread_from_fd (fd) 47421495Sjmacd int fd; 47521495Sjmacd{ 47621495Sjmacd struct timeval timeout; 47721495Sjmacd char *buffer = (char *)NULL; 47821495Sjmacd int bsize = 0; 47921495Sjmacd int bindex = 0; 48021495Sjmacd int select_result; 48121495Sjmacd#if defined (FD_SET) 48221495Sjmacd fd_set read_fds; 48321495Sjmacd 48421495Sjmacd timeout.tv_sec = 15; 48521495Sjmacd timeout.tv_usec = 0; 48621495Sjmacd 48721495Sjmacd FD_ZERO (&read_fds); 48821495Sjmacd FD_SET (fd, &read_fds); 48921495Sjmacd 49021495Sjmacd select_result = select (fd + 1, fd_set_cast (&read_fds), 0, 0, &timeout); 49121495Sjmacd#else /* !FD_SET */ 49221495Sjmacd select_result = 1; 49321495Sjmacd#endif /* !FD_SET */ 49421495Sjmacd 49521495Sjmacd switch (select_result) 49621495Sjmacd { 49721495Sjmacd case 0: 49821495Sjmacd case -1: 49921495Sjmacd break; 50021495Sjmacd 50121495Sjmacd default: 50221495Sjmacd { 50321495Sjmacd int amount_read; 50421495Sjmacd int done = 0; 50521495Sjmacd 50621495Sjmacd while (!done) 50721495Sjmacd { 50821495Sjmacd while ((bindex + 1024) > (bsize)) 50921495Sjmacd buffer = (char *)xrealloc (buffer, (bsize += 1024)); 51021495Sjmacd buffer[bindex] = '\0'; 51121495Sjmacd 51221495Sjmacd amount_read = read (fd, buffer + bindex, 1023); 51321495Sjmacd 51421495Sjmacd if (amount_read < 0) 51521495Sjmacd { 51621495Sjmacd done = 1; 51721495Sjmacd } 51821495Sjmacd else 51921495Sjmacd { 52021495Sjmacd bindex += amount_read; 52121495Sjmacd buffer[bindex] = '\0'; 52221495Sjmacd if (amount_read == 0) 52321495Sjmacd done = 1; 52421495Sjmacd } 52521495Sjmacd } 52621495Sjmacd } 52721495Sjmacd } 52821495Sjmacd 52921495Sjmacd if ((buffer != (char *)NULL) && (*buffer == '\0')) 53021495Sjmacd { 53121495Sjmacd free (buffer); 53221495Sjmacd buffer = (char *)NULL; 53321495Sjmacd } 53421495Sjmacd 53521495Sjmacd return (buffer); 53621495Sjmacd} 53721495Sjmacd 53821495Sjmacdstatic char *reference_section_starters[] = 53921495Sjmacd{ 54021495Sjmacd "\nRELATED INFORMATION", 54121495Sjmacd "\nRELATED\tINFORMATION", 54221495Sjmacd "RELATED INFORMATION\n", 54321495Sjmacd "RELATED\tINFORMATION\n", 54421495Sjmacd "\nSEE ALSO", 54521495Sjmacd "\nSEE\tALSO", 54621495Sjmacd "SEE ALSO\n", 54721495Sjmacd "SEE\tALSO\n", 54821495Sjmacd (char *)NULL 54921495Sjmacd}; 55021495Sjmacd 55121495Sjmacdstatic SEARCH_BINDING frs_binding; 55221495Sjmacd 55321495Sjmacdstatic SEARCH_BINDING * 55421495Sjmacdfind_reference_section (node) 55521495Sjmacd NODE *node; 55621495Sjmacd{ 55721495Sjmacd register int i; 55821495Sjmacd long position = -1; 55921495Sjmacd 56021495Sjmacd frs_binding.buffer = node->contents; 56121495Sjmacd frs_binding.start = 0; 56221495Sjmacd frs_binding.end = node->nodelen; 56321495Sjmacd frs_binding.flags = S_SkipDest; 56421495Sjmacd 56521495Sjmacd for (i = 0; reference_section_starters[i] != (char *)NULL; i++) 56621495Sjmacd { 56721495Sjmacd position = search_forward (reference_section_starters[i], &frs_binding); 56821495Sjmacd if (position != -1) 56942660Smarkm break; 57021495Sjmacd } 57121495Sjmacd 57221495Sjmacd if (position == -1) 57321495Sjmacd return ((SEARCH_BINDING *)NULL); 57421495Sjmacd 57521495Sjmacd /* We found the start of the reference section, and point is right after 57621495Sjmacd the string which starts it. The text from here to the next header 57721495Sjmacd (or end of buffer) contains the only references in this manpage. */ 57821495Sjmacd frs_binding.start = position; 57921495Sjmacd 58021495Sjmacd for (i = frs_binding.start; i < frs_binding.end - 2; i++) 58121495Sjmacd { 58221495Sjmacd if ((frs_binding.buffer[i] == '\n') && 58342660Smarkm (!whitespace (frs_binding.buffer[i + 1]))) 58442660Smarkm { 58542660Smarkm frs_binding.end = i; 58642660Smarkm break; 58742660Smarkm } 58821495Sjmacd } 58921495Sjmacd 59021495Sjmacd return (&frs_binding); 59121495Sjmacd} 59221495Sjmacd 59321495SjmacdREFERENCE ** 59421495Sjmacdxrefs_of_manpage (node) 59521495Sjmacd NODE *node; 59621495Sjmacd{ 59721495Sjmacd SEARCH_BINDING *reference_section; 59821495Sjmacd REFERENCE **refs = (REFERENCE **)NULL; 59921495Sjmacd int refs_index = 0; 60021495Sjmacd int refs_slots = 0; 60121495Sjmacd long position; 60221495Sjmacd 60321495Sjmacd reference_section = find_reference_section (node); 60421495Sjmacd 60521495Sjmacd if (reference_section == (SEARCH_BINDING *)NULL) 60621495Sjmacd return ((REFERENCE **)NULL); 60721495Sjmacd 60821495Sjmacd /* Grovel the reference section building a list of references found there. 60921495Sjmacd A reference is alphabetic characters followed by non-whitespace text 61021495Sjmacd within parenthesis. */ 61121495Sjmacd reference_section->flags = 0; 61221495Sjmacd 61321495Sjmacd while ((position = search_forward ("(", reference_section)) != -1) 61421495Sjmacd { 61521495Sjmacd register int start, end; 61621495Sjmacd 61721495Sjmacd for (start = position; start > reference_section->start; start--) 61842660Smarkm if (whitespace (reference_section->buffer[start])) 61942660Smarkm break; 62021495Sjmacd 62121495Sjmacd start++; 62221495Sjmacd 62321495Sjmacd for (end = position; end < reference_section->end; end++) 62442660Smarkm { 62542660Smarkm if (whitespace (reference_section->buffer[end])) 62642660Smarkm { 62742660Smarkm end = start; 62842660Smarkm break; 62942660Smarkm } 63021495Sjmacd 63142660Smarkm if (reference_section->buffer[end] == ')') 63242660Smarkm { 63342660Smarkm end++; 63442660Smarkm break; 63542660Smarkm } 63642660Smarkm } 63721495Sjmacd 63821495Sjmacd if (end != start) 63942660Smarkm { 64042660Smarkm REFERENCE *entry; 64142660Smarkm int len = end - start; 64221495Sjmacd 64342660Smarkm entry = (REFERENCE *)xmalloc (sizeof (REFERENCE)); 64442660Smarkm entry->label = (char *)xmalloc (1 + len); 64542660Smarkm strncpy (entry->label, (reference_section->buffer) + start, len); 64642660Smarkm entry->label[len] = '\0'; 64742660Smarkm entry->filename = xstrdup (node->filename); 64842660Smarkm entry->nodename = xstrdup (entry->label); 64942660Smarkm entry->start = start; 65042660Smarkm entry->end = end; 65121495Sjmacd 65242660Smarkm add_pointer_to_array 65342660Smarkm (entry, refs_index, refs, refs_slots, 10, REFERENCE *); 65442660Smarkm } 65521495Sjmacd 65621495Sjmacd reference_section->start = position + 1; 65721495Sjmacd } 65821495Sjmacd 65921495Sjmacd return (refs); 66021495Sjmacd} 66121495Sjmacd 66221495Sjmacdlong 66321495Sjmacdlocate_manpage_xref (node, start, dir) 66421495Sjmacd NODE *node; 66521495Sjmacd long start; 66621495Sjmacd int dir; 66721495Sjmacd{ 66821495Sjmacd REFERENCE **refs; 66921495Sjmacd long position = -1; 67021495Sjmacd 67121495Sjmacd refs = xrefs_of_manpage (node); 67221495Sjmacd 67321495Sjmacd if (refs) 67421495Sjmacd { 67521495Sjmacd register int i, count; 67621495Sjmacd REFERENCE *entry; 67721495Sjmacd 67821495Sjmacd for (i = 0; refs[i]; i++); 67921495Sjmacd count = i; 68021495Sjmacd 68121495Sjmacd if (dir > 0) 68242660Smarkm { 68342660Smarkm for (i = 0; (entry = refs[i]); i++) 68442660Smarkm if (entry->start > start) 68542660Smarkm { 68642660Smarkm position = entry->start; 68742660Smarkm break; 68842660Smarkm } 68942660Smarkm } 69021495Sjmacd else 69142660Smarkm { 69242660Smarkm for (i = count - 1; i > -1; i--) 69342660Smarkm { 69442660Smarkm entry = refs[i]; 69521495Sjmacd 69642660Smarkm if (entry->start < start) 69742660Smarkm { 69842660Smarkm position = entry->start; 69942660Smarkm break; 70042660Smarkm } 70142660Smarkm } 70242660Smarkm } 70321495Sjmacd 70421495Sjmacd info_free_references (refs); 70521495Sjmacd } 70621495Sjmacd return (position); 70721495Sjmacd} 70821495Sjmacd 70921495Sjmacd/* This one was a little tricky. The binding buffer that is passed in has 71021495Sjmacd a START and END value of 0 -- strlen (window-line-containing-point). 71121495Sjmacd The BUFFER is a pointer to the start of that line. */ 71221495SjmacdREFERENCE ** 71321495Sjmacdmanpage_xrefs_in_binding (node, binding) 71421495Sjmacd NODE *node; 71521495Sjmacd SEARCH_BINDING *binding; 71621495Sjmacd{ 71721495Sjmacd register int i; 71821495Sjmacd REFERENCE **all_refs = xrefs_of_manpage (node); 71921495Sjmacd REFERENCE **brefs = (REFERENCE **)NULL; 72021495Sjmacd REFERENCE *entry; 72121495Sjmacd int brefs_index = 0; 72221495Sjmacd int brefs_slots = 0; 72321495Sjmacd int start, end; 72421495Sjmacd 72521495Sjmacd if (!all_refs) 72621495Sjmacd return ((REFERENCE **)NULL); 72721495Sjmacd 72821495Sjmacd start = binding->start + (binding->buffer - node->contents); 72921495Sjmacd end = binding->end + (binding->buffer - node->contents); 73021495Sjmacd 73142660Smarkm for (i = 0; (entry = all_refs[i]); i++) 73221495Sjmacd { 73321495Sjmacd if ((entry->start > start) && (entry->end < end)) 73442660Smarkm { 73542660Smarkm add_pointer_to_array 73642660Smarkm (entry, brefs_index, brefs, brefs_slots, 10, REFERENCE *); 73742660Smarkm } 73821495Sjmacd else 73942660Smarkm { 74042660Smarkm maybe_free (entry->label); 74142660Smarkm maybe_free (entry->filename); 74242660Smarkm maybe_free (entry->nodename); 74342660Smarkm free (entry); 74442660Smarkm } 74521495Sjmacd } 74621495Sjmacd 74721495Sjmacd free (all_refs); 74821495Sjmacd return (brefs); 74921495Sjmacd} 750