1 1.1 christos /* $NetBSD: man.c,v 1.1.1.1 2016/01/14 00:11:29 christos Exp $ */ 2 1.1 christos 3 1.1 christos /* man.c: How to read and format man files. 4 1.1 christos Id: man.c,v 1.4 2004/04/11 17:56:46 karl Exp 5 1.1 christos 6 1.1 christos Copyright (C) 1995, 1997, 1998, 1999, 2000, 2002, 2003, 2004 Free Software 7 1.1 christos Foundation, Inc. 8 1.1 christos 9 1.1 christos This program is free software; you can redistribute it and/or modify 10 1.1 christos it under the terms of the GNU General Public License as published by 11 1.1 christos the Free Software Foundation; either version 2, or (at your option) 12 1.1 christos any later version. 13 1.1 christos 14 1.1 christos This program is distributed in the hope that it will be useful, 15 1.1 christos but WITHOUT ANY WARRANTY; without even the implied warranty of 16 1.1 christos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 1.1 christos GNU General Public License for more details. 18 1.1 christos 19 1.1 christos You should have received a copy of the GNU General Public License 20 1.1 christos along with this program; if not, write to the Free Software 21 1.1 christos Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 22 1.1 christos 23 1.1 christos Written by Brian Fox Thu May 4 09:17:52 1995 (bfox (at) ai.mit.edu). */ 24 1.1 christos 25 1.1 christos #include "info.h" 26 1.1 christos #include <sys/ioctl.h> 27 1.1 christos #include "signals.h" 28 1.1 christos #if defined (HAVE_SYS_TIME_H) 29 1.1 christos #include <sys/time.h> 30 1.1 christos #endif 31 1.1 christos #if defined (HAVE_SYS_WAIT_H) 32 1.1 christos #include <sys/wait.h> 33 1.1 christos #endif 34 1.1 christos 35 1.1 christos #include "tilde.h" 36 1.1 christos #include "man.h" 37 1.1 christos 38 1.1 christos #if !defined (_POSIX_VERSION) 39 1.1 christos #define pid_t int 40 1.1 christos #endif 41 1.1 christos 42 1.1 christos #if defined (FD_SET) 43 1.1 christos # if defined (hpux) 44 1.1 christos # define fd_set_cast(x) (int *)(x) 45 1.1 christos # else 46 1.1 christos # define fd_set_cast(x) (fd_set *)(x) 47 1.1 christos # endif /* !hpux */ 48 1.1 christos #endif /* FD_SET */ 49 1.1 christos 50 1.1 christos #if STRIP_DOT_EXE 51 1.1 christos static char const * const exec_extensions[] = { 52 1.1 christos ".exe", ".com", ".bat", ".btm", ".sh", ".ksh", ".pl", ".sed", "", NULL 53 1.1 christos }; 54 1.1 christos #else 55 1.1 christos static char const * const exec_extensions[] = { "", NULL }; 56 1.1 christos #endif 57 1.1 christos 58 1.1 christos static char *read_from_fd (int fd); 59 1.1 christos static void clean_manpage (char *manpage); 60 1.1 christos static NODE *manpage_node_of_file_buffer (FILE_BUFFER *file_buffer, 61 1.1 christos char *pagename); 62 1.1 christos static char *get_manpage_contents (char *pagename); 63 1.1 christos 64 1.1 christos NODE * 65 1.1 christos make_manpage_node (char *pagename) 66 1.1 christos { 67 1.1 christos return (info_get_node (MANPAGE_FILE_BUFFER_NAME, pagename)); 68 1.1 christos } 69 1.1 christos 70 1.1 christos NODE * 71 1.1 christos get_manpage_node (FILE_BUFFER *file_buffer, char *pagename) 72 1.1 christos { 73 1.1 christos NODE *node; 74 1.1 christos 75 1.1 christos node = manpage_node_of_file_buffer (file_buffer, pagename); 76 1.1 christos 77 1.1 christos if (!node) 78 1.1 christos { 79 1.1 christos char *page; 80 1.1 christos 81 1.1 christos page = get_manpage_contents (pagename); 82 1.1 christos 83 1.1 christos if (page) 84 1.1 christos { 85 1.1 christos char header[1024]; 86 1.1 christos long oldsize, newsize; 87 1.1 christos int hlen, plen; 88 1.1 christos char *old_contents = file_buffer->contents; 89 1.1 christos 90 1.1 christos sprintf (header, "\n\n%c\n%s %s, %s %s, %s (dir)\n\n", 91 1.1 christos INFO_COOKIE, 92 1.1 christos INFO_FILE_LABEL, file_buffer->filename, 93 1.1 christos INFO_NODE_LABEL, pagename, 94 1.1 christos INFO_UP_LABEL); 95 1.1 christos oldsize = file_buffer->filesize; 96 1.1 christos hlen = strlen (header); 97 1.1 christos plen = strlen (page); 98 1.1 christos newsize = (oldsize + hlen + plen); 99 1.1 christos file_buffer->contents = 100 1.1 christos (char *)xrealloc (file_buffer->contents, 1 + newsize); 101 1.1 christos memcpy (file_buffer->contents + oldsize, header, hlen); 102 1.1 christos memcpy (file_buffer->contents + oldsize + hlen, page, plen); 103 1.1 christos file_buffer->contents[newsize] = '\0'; 104 1.1 christos file_buffer->filesize = newsize; 105 1.1 christos file_buffer->finfo.st_size = newsize; 106 1.1 christos build_tags_and_nodes (file_buffer); 107 1.1 christos free (page); 108 1.1 christos /* We have just relocated file_buffer->contents from under 109 1.1 christos the feet of info_windows[] array. Therefore, all the 110 1.1 christos nodes on that list which are showing man pages have their 111 1.1 christos contents member pointing into the blue. Undo that harm. */ 112 1.1 christos if (old_contents && oldsize && old_contents != file_buffer->contents) 113 1.1 christos { 114 1.1 christos int iw; 115 1.1 christos INFO_WINDOW *info_win; 116 1.1 christos char *old_contents_end = old_contents + oldsize; 117 1.1 christos 118 1.1 christos for (iw = 0; (info_win = info_windows[iw]); iw++) 119 1.1 christos { 120 1.1 christos int in; 121 1.1 christos 122 1.1 christos for (in = 0; in < info_win->nodes_index; in++) 123 1.1 christos { 124 1.1 christos NODE *tmp_node = info_win->nodes[in]; 125 1.1 christos 126 1.1 christos /* It really only suffices to see that node->filename 127 1.1 christos is "*manpages*". But after several hours of 128 1.1 christos debugging this, would you blame me for being a bit 129 1.1 christos paranoid? */ 130 1.1 christos if (tmp_node && tmp_node->filename 131 1.1 christos && tmp_node->contents 132 1.1 christos && strcmp (tmp_node->filename, 133 1.1 christos MANPAGE_FILE_BUFFER_NAME) == 0 134 1.1 christos && tmp_node->contents >= old_contents 135 1.1 christos && tmp_node->contents + tmp_node->nodelen 136 1.1 christos <= old_contents_end) 137 1.1 christos { 138 1.1 christos info_win->nodes[in] = 139 1.1 christos manpage_node_of_file_buffer (file_buffer, 140 1.1 christos tmp_node->nodename); 141 1.1 christos free (tmp_node->nodename); 142 1.1 christos free (tmp_node); 143 1.1 christos } 144 1.1 christos } 145 1.1 christos } 146 1.1 christos } 147 1.1 christos } 148 1.1 christos 149 1.1 christos node = manpage_node_of_file_buffer (file_buffer, pagename); 150 1.1 christos } 151 1.1 christos 152 1.1 christos return (node); 153 1.1 christos } 154 1.1 christos 155 1.1 christos FILE_BUFFER * 156 1.1 christos create_manpage_file_buffer (void) 157 1.1 christos { 158 1.1 christos FILE_BUFFER *file_buffer = make_file_buffer (); 159 1.1 christos file_buffer->filename = xstrdup (MANPAGE_FILE_BUFFER_NAME); 160 1.1 christos file_buffer->fullpath = xstrdup (MANPAGE_FILE_BUFFER_NAME); 161 1.1 christos file_buffer->finfo.st_size = 0; 162 1.1 christos file_buffer->filesize = 0; 163 1.1 christos file_buffer->contents = (char *)NULL; 164 1.1 christos file_buffer->flags = (N_IsInternal | N_CannotGC | N_IsManPage); 165 1.1 christos 166 1.1 christos return (file_buffer); 167 1.1 christos } 168 1.1 christos 169 1.1 christos /* Scan the list of directories in PATH looking for FILENAME. If we find 170 1.1 christos one that is an executable file, return it as a new string. Otherwise, 171 1.1 christos return a NULL pointer. */ 172 1.1 christos static char * 173 1.1 christos executable_file_in_path (char *filename, char *path) 174 1.1 christos { 175 1.1 christos struct stat finfo; 176 1.1 christos char *temp_dirname; 177 1.1 christos int statable, dirname_index; 178 1.1 christos 179 1.1 christos dirname_index = 0; 180 1.1 christos 181 1.1 christos while ((temp_dirname = extract_colon_unit (path, &dirname_index))) 182 1.1 christos { 183 1.1 christos char *temp; 184 1.1 christos char *temp_end; 185 1.1 christos int i; 186 1.1 christos 187 1.1 christos /* Expand a leading tilde if one is present. */ 188 1.1 christos if (*temp_dirname == '~') 189 1.1 christos { 190 1.1 christos char *expanded_dirname; 191 1.1 christos 192 1.1 christos expanded_dirname = tilde_expand_word (temp_dirname); 193 1.1 christos free (temp_dirname); 194 1.1 christos temp_dirname = expanded_dirname; 195 1.1 christos } 196 1.1 christos 197 1.1 christos temp = (char *)xmalloc (34 + strlen (temp_dirname) + strlen (filename)); 198 1.1 christos strcpy (temp, temp_dirname); 199 1.1 christos if (!IS_SLASH (temp[(strlen (temp)) - 1])) 200 1.1 christos strcat (temp, "/"); 201 1.1 christos strcat (temp, filename); 202 1.1 christos temp_end = temp + strlen (temp); 203 1.1 christos 204 1.1 christos free (temp_dirname); 205 1.1 christos 206 1.1 christos /* Look for FILENAME, possibly with any of the extensions 207 1.1 christos in EXEC_EXTENSIONS[]. */ 208 1.1 christos for (i = 0; exec_extensions[i]; i++) 209 1.1 christos { 210 1.1 christos if (exec_extensions[i][0]) 211 1.1 christos strcpy (temp_end, exec_extensions[i]); 212 1.1 christos statable = (stat (temp, &finfo) == 0); 213 1.1 christos 214 1.1 christos /* If we have found a regular executable file, then use it. */ 215 1.1 christos if ((statable) && (S_ISREG (finfo.st_mode)) && 216 1.1 christos (access (temp, X_OK) == 0)) 217 1.1 christos return (temp); 218 1.1 christos } 219 1.1 christos 220 1.1 christos free (temp); 221 1.1 christos } 222 1.1 christos return ((char *)NULL); 223 1.1 christos } 224 1.1 christos 225 1.1 christos /* Return the full pathname of the system man page formatter. */ 226 1.1 christos static char * 227 1.1 christos find_man_formatter (void) 228 1.1 christos { 229 1.1 christos return (executable_file_in_path ("man", (char *)getenv ("PATH"))); 230 1.1 christos } 231 1.1 christos 232 1.1 christos static char *manpage_pagename = (char *)NULL; 233 1.1 christos static char *manpage_section = (char *)NULL; 234 1.1 christos 235 1.1 christos static void 236 1.1 christos get_page_and_section (char *pagename) 237 1.1 christos { 238 1.1 christos register int i; 239 1.1 christos 240 1.1 christos if (manpage_pagename) 241 1.1 christos free (manpage_pagename); 242 1.1 christos 243 1.1 christos if (manpage_section) 244 1.1 christos free (manpage_section); 245 1.1 christos 246 1.1 christos manpage_pagename = (char *)NULL; 247 1.1 christos manpage_section = (char *)NULL; 248 1.1 christos 249 1.1 christos for (i = 0; pagename[i] != '\0' && pagename[i] != '('; i++); 250 1.1 christos 251 1.1 christos manpage_pagename = (char *)xmalloc (1 + i); 252 1.1 christos strncpy (manpage_pagename, pagename, i); 253 1.1 christos manpage_pagename[i] = '\0'; 254 1.1 christos 255 1.1 christos if (pagename[i] == '(') 256 1.1 christos { 257 1.1 christos int start; 258 1.1 christos 259 1.1 christos start = i + 1; 260 1.1 christos 261 1.1 christos for (i = start; pagename[i] != '\0' && pagename[i] != ')'; i++); 262 1.1 christos 263 1.1 christos manpage_section = (char *)xmalloc (1 + (i - start)); 264 1.1 christos strncpy (manpage_section, pagename + start, (i - start)); 265 1.1 christos manpage_section[i - start] = '\0'; 266 1.1 christos } 267 1.1 christos } 268 1.1 christos 269 1.1 christos #if PIPE_USE_FORK 270 1.1 christos static void 271 1.1 christos reap_children (int sig) 272 1.1 christos { 273 1.1 christos wait (NULL); 274 1.1 christos } 275 1.1 christos #endif 276 1.1 christos 277 1.1 christos static char * 278 1.1 christos get_manpage_contents (char *pagename) 279 1.1 christos { 280 1.1 christos static char *formatter_args[4] = { (char *)NULL }; 281 1.1 christos int pipes[2]; 282 1.1 christos pid_t child; 283 1.1 christos RETSIGTYPE (*sigsave) (int signum); 284 1.1 christos char *formatted_page = NULL; 285 1.1 christos int arg_index = 1; 286 1.1 christos 287 1.1 christos if (formatter_args[0] == (char *)NULL) 288 1.1 christos formatter_args[0] = find_man_formatter (); 289 1.1 christos 290 1.1 christos if (formatter_args[0] == (char *)NULL) 291 1.1 christos return ((char *)NULL); 292 1.1 christos 293 1.1 christos get_page_and_section (pagename); 294 1.1 christos 295 1.1 christos if (manpage_section != (char *)NULL) 296 1.1 christos formatter_args[arg_index++] = manpage_section; 297 1.1 christos 298 1.1 christos formatter_args[arg_index++] = manpage_pagename; 299 1.1 christos formatter_args[arg_index] = (char *)NULL; 300 1.1 christos 301 1.1 christos /* Open a pipe to this program, read the output, and save it away 302 1.1 christos in FORMATTED_PAGE. The reader end of the pipe is pipes[0]; the 303 1.1 christos writer end is pipes[1]. */ 304 1.1 christos #if PIPE_USE_FORK 305 1.1 christos pipe (pipes); 306 1.1 christos 307 1.1 christos sigsave = signal (SIGCHLD, reap_children); 308 1.1 christos 309 1.1 christos child = fork (); 310 1.1 christos if (child == -1) 311 1.1 christos return ((char *)NULL); 312 1.1 christos 313 1.1 christos if (child != 0) 314 1.1 christos { 315 1.1 christos /* In the parent, close the writing end of the pipe, and read from 316 1.1 christos the exec'd child. */ 317 1.1 christos close (pipes[1]); 318 1.1 christos formatted_page = read_from_fd (pipes[0]); 319 1.1 christos close (pipes[0]); 320 1.1 christos signal (SIGCHLD, sigsave); 321 1.1 christos } 322 1.1 christos else 323 1.1 christos { /* In the child, close the read end of the pipe, make the write end 324 1.1 christos of the pipe be stdout, and execute the man page formatter. */ 325 1.1 christos close (pipes[0]); 326 1.1 christos freopen (NULL_DEVICE, "w", stderr); 327 1.1 christos freopen (NULL_DEVICE, "r", stdin); 328 1.1 christos dup2 (pipes[1], fileno (stdout)); 329 1.1 christos 330 1.1 christos execv (formatter_args[0], formatter_args); 331 1.1 christos 332 1.1 christos /* If we get here, we couldn't exec, so close out the pipe and 333 1.1 christos exit. */ 334 1.1 christos close (pipes[1]); 335 1.1 christos xexit (0); 336 1.1 christos } 337 1.1 christos #else /* !PIPE_USE_FORK */ 338 1.1 christos /* Cannot fork/exec, but can popen/pclose. */ 339 1.1 christos { 340 1.1 christos FILE *fpipe; 341 1.1 christos char *cmdline = xmalloc (strlen (formatter_args[0]) 342 1.1 christos + strlen (manpage_pagename) 343 1.1 christos + (arg_index > 2 ? strlen (manpage_section) : 0) 344 1.1 christos + 3); 345 1.1 christos int save_stderr = dup (fileno (stderr)); 346 1.1 christos int fd_err = open (NULL_DEVICE, O_WRONLY, 0666); 347 1.1 christos 348 1.1 christos if (fd_err > 2) 349 1.1 christos dup2 (fd_err, fileno (stderr)); /* Don't print errors. */ 350 1.1 christos sprintf (cmdline, "%s %s %s", formatter_args[0], manpage_pagename, 351 1.1 christos arg_index > 2 ? manpage_section : ""); 352 1.1 christos fpipe = popen (cmdline, "r"); 353 1.1 christos free (cmdline); 354 1.1 christos if (fd_err > 2) 355 1.1 christos close (fd_err); 356 1.1 christos dup2 (save_stderr, fileno (stderr)); 357 1.1 christos if (fpipe == 0) 358 1.1 christos return ((char *)NULL); 359 1.1 christos formatted_page = read_from_fd (fileno (fpipe)); 360 1.1 christos if (pclose (fpipe) == -1) 361 1.1 christos { 362 1.1 christos if (formatted_page) 363 1.1 christos free (formatted_page); 364 1.1 christos return ((char *)NULL); 365 1.1 christos } 366 1.1 christos } 367 1.1 christos #endif /* !PIPE_USE_FORK */ 368 1.1 christos 369 1.1 christos /* If we have the page, then clean it up. */ 370 1.1 christos if (formatted_page) 371 1.1 christos clean_manpage (formatted_page); 372 1.1 christos 373 1.1 christos return (formatted_page); 374 1.1 christos } 375 1.1 christos 376 1.1 christos static void 377 1.1 christos clean_manpage (char *manpage) 378 1.1 christos { 379 1.1 christos register int i, j; 380 1.1 christos int newline_count = 0; 381 1.1 christos char *newpage; 382 1.1 christos 383 1.1 christos newpage = (char *)xmalloc (1 + strlen (manpage)); 384 1.1 christos 385 1.1 christos for (i = 0, j = 0; (newpage[j] = manpage[i]); i++, j++) 386 1.1 christos { 387 1.1 christos if (manpage[i] == '\n') 388 1.1 christos newline_count++; 389 1.1 christos else 390 1.1 christos newline_count = 0; 391 1.1 christos 392 1.1 christos if (newline_count == 3) 393 1.1 christos { 394 1.1 christos j--; 395 1.1 christos newline_count--; 396 1.1 christos } 397 1.1 christos 398 1.1 christos /* A malformed man page could have a \b as its first character, 399 1.1 christos in which case decrementing j by 2 will cause us to write into 400 1.1 christos newpage[-1], smashing the hidden info stored there by malloc. */ 401 1.1 christos if (manpage[i] == '\b' || (manpage[i] == '\f' && j > 0)) 402 1.1 christos j -= 2; 403 1.1 christos else if (!raw_escapes_p) 404 1.1 christos { 405 1.1 christos /* Remove the ANSI escape sequences for color, boldface, 406 1.1 christos underlining, and italics, generated by some versions of 407 1.1 christos Groff. */ 408 1.1 christos if (manpage[i] == '\033' && manpage[i + 1] == '[' 409 1.1 christos && isdigit (manpage[i + 2])) 410 1.1 christos { 411 1.1 christos if (isdigit (manpage[i + 3]) && manpage[i + 4] == 'm') 412 1.1 christos { 413 1.1 christos i += 4; 414 1.1 christos j--; 415 1.1 christos } 416 1.1 christos else if (manpage[i + 3] == 'm') 417 1.1 christos { 418 1.1 christos i += 3; 419 1.1 christos j--; 420 1.1 christos } 421 1.1 christos /* Else do nothing: it's some unknown escape sequence, 422 1.1 christos so let's leave it alone. */ 423 1.1 christos } 424 1.1 christos } 425 1.1 christos } 426 1.1 christos 427 1.1 christos newpage[j++] = 0; 428 1.1 christos 429 1.1 christos strcpy (manpage, newpage); 430 1.1 christos free (newpage); 431 1.1 christos } 432 1.1 christos 433 1.1 christos static NODE * 434 1.1 christos manpage_node_of_file_buffer (FILE_BUFFER *file_buffer, char *pagename) 435 1.1 christos { 436 1.1 christos NODE *node = (NODE *)NULL; 437 1.1 christos TAG *tag = (TAG *)NULL; 438 1.1 christos 439 1.1 christos if (file_buffer->contents) 440 1.1 christos { 441 1.1 christos register int i; 442 1.1 christos 443 1.1 christos for (i = 0; (tag = file_buffer->tags[i]); i++) 444 1.1 christos { 445 1.1 christos if (strcasecmp (pagename, tag->nodename) == 0) 446 1.1 christos break; 447 1.1 christos } 448 1.1 christos } 449 1.1 christos 450 1.1 christos if (tag) 451 1.1 christos { 452 1.1 christos node = (NODE *)xmalloc (sizeof (NODE)); 453 1.1 christos node->filename = file_buffer->filename; 454 1.1 christos node->nodename = xstrdup (tag->nodename); 455 1.1 christos node->contents = file_buffer->contents + tag->nodestart; 456 1.1 christos node->nodelen = tag->nodelen; 457 1.1 christos node->flags = 0; 458 1.1 christos node->display_pos = 0; 459 1.1 christos node->parent = (char *)NULL; 460 1.1 christos node->flags = (N_HasTagsTable | N_IsManPage); 461 1.1 christos node->contents += skip_node_separator (node->contents); 462 1.1 christos } 463 1.1 christos 464 1.1 christos return (node); 465 1.1 christos } 466 1.1 christos 467 1.1 christos static char * 468 1.1 christos read_from_fd (int fd) 469 1.1 christos { 470 1.1 christos struct timeval timeout; 471 1.1 christos char *buffer = (char *)NULL; 472 1.1 christos int bsize = 0; 473 1.1 christos int bindex = 0; 474 1.1 christos int select_result; 475 1.1 christos #if defined (FD_SET) 476 1.1 christos fd_set read_fds; 477 1.1 christos 478 1.1 christos timeout.tv_sec = 15; 479 1.1 christos timeout.tv_usec = 0; 480 1.1 christos 481 1.1 christos FD_ZERO (&read_fds); 482 1.1 christos FD_SET (fd, &read_fds); 483 1.1 christos 484 1.1 christos select_result = select (fd + 1, fd_set_cast (&read_fds), 0, 0, &timeout); 485 1.1 christos #else /* !FD_SET */ 486 1.1 christos select_result = 1; 487 1.1 christos #endif /* !FD_SET */ 488 1.1 christos 489 1.1 christos switch (select_result) 490 1.1 christos { 491 1.1 christos case 0: 492 1.1 christos case -1: 493 1.1 christos break; 494 1.1 christos 495 1.1 christos default: 496 1.1 christos { 497 1.1 christos int amount_read; 498 1.1 christos int done = 0; 499 1.1 christos 500 1.1 christos while (!done) 501 1.1 christos { 502 1.1 christos while ((bindex + 1024) > (bsize)) 503 1.1 christos buffer = (char *)xrealloc (buffer, (bsize += 1024)); 504 1.1 christos buffer[bindex] = '\0'; 505 1.1 christos 506 1.1 christos amount_read = read (fd, buffer + bindex, 1023); 507 1.1 christos 508 1.1 christos if (amount_read < 0) 509 1.1 christos { 510 1.1 christos done = 1; 511 1.1 christos } 512 1.1 christos else 513 1.1 christos { 514 1.1 christos bindex += amount_read; 515 1.1 christos buffer[bindex] = '\0'; 516 1.1 christos if (amount_read == 0) 517 1.1 christos done = 1; 518 1.1 christos } 519 1.1 christos } 520 1.1 christos } 521 1.1 christos } 522 1.1 christos 523 1.1 christos if ((buffer != (char *)NULL) && (*buffer == '\0')) 524 1.1 christos { 525 1.1 christos free (buffer); 526 1.1 christos buffer = (char *)NULL; 527 1.1 christos } 528 1.1 christos 529 1.1 christos return (buffer); 530 1.1 christos } 531 1.1 christos 532 1.1 christos static char *reference_section_starters[] = 533 1.1 christos { 534 1.1 christos "\nRELATED INFORMATION", 535 1.1 christos "\nRELATED\tINFORMATION", 536 1.1 christos "RELATED INFORMATION\n", 537 1.1 christos "RELATED\tINFORMATION\n", 538 1.1 christos "\nSEE ALSO", 539 1.1 christos "\nSEE\tALSO", 540 1.1 christos "SEE ALSO\n", 541 1.1 christos "SEE\tALSO\n", 542 1.1 christos (char *)NULL 543 1.1 christos }; 544 1.1 christos 545 1.1 christos static SEARCH_BINDING frs_binding; 546 1.1 christos 547 1.1 christos static SEARCH_BINDING * 548 1.1 christos find_reference_section (NODE *node) 549 1.1 christos { 550 1.1 christos register int i; 551 1.1 christos long position = -1; 552 1.1 christos 553 1.1 christos frs_binding.buffer = node->contents; 554 1.1 christos frs_binding.start = 0; 555 1.1 christos frs_binding.end = node->nodelen; 556 1.1 christos frs_binding.flags = S_SkipDest; 557 1.1 christos 558 1.1 christos for (i = 0; reference_section_starters[i] != (char *)NULL; i++) 559 1.1 christos { 560 1.1 christos position = search_forward (reference_section_starters[i], &frs_binding); 561 1.1 christos if (position != -1) 562 1.1 christos break; 563 1.1 christos } 564 1.1 christos 565 1.1 christos if (position == -1) 566 1.1 christos return ((SEARCH_BINDING *)NULL); 567 1.1 christos 568 1.1 christos /* We found the start of the reference section, and point is right after 569 1.1 christos the string which starts it. The text from here to the next header 570 1.1 christos (or end of buffer) contains the only references in this manpage. */ 571 1.1 christos frs_binding.start = position; 572 1.1 christos 573 1.1 christos for (i = frs_binding.start; i < frs_binding.end - 2; i++) 574 1.1 christos { 575 1.1 christos if ((frs_binding.buffer[i] == '\n') && 576 1.1 christos (!whitespace (frs_binding.buffer[i + 1]))) 577 1.1 christos { 578 1.1 christos frs_binding.end = i; 579 1.1 christos break; 580 1.1 christos } 581 1.1 christos } 582 1.1 christos 583 1.1 christos return (&frs_binding); 584 1.1 christos } 585 1.1 christos 586 1.1 christos REFERENCE ** 587 1.1 christos xrefs_of_manpage (NODE *node) 588 1.1 christos { 589 1.1 christos SEARCH_BINDING *reference_section; 590 1.1 christos REFERENCE **refs = (REFERENCE **)NULL; 591 1.1 christos int refs_index = 0; 592 1.1 christos int refs_slots = 0; 593 1.1 christos long position; 594 1.1 christos 595 1.1 christos reference_section = find_reference_section (node); 596 1.1 christos 597 1.1 christos if (reference_section == (SEARCH_BINDING *)NULL) 598 1.1 christos return ((REFERENCE **)NULL); 599 1.1 christos 600 1.1 christos /* Grovel the reference section building a list of references found there. 601 1.1 christos A reference is alphabetic characters followed by non-whitespace text 602 1.1 christos within parenthesis. */ 603 1.1 christos reference_section->flags = 0; 604 1.1 christos 605 1.1 christos while ((position = search_forward ("(", reference_section)) != -1) 606 1.1 christos { 607 1.1 christos register int start, end; 608 1.1 christos 609 1.1 christos for (start = position; start > reference_section->start; start--) 610 1.1 christos if (whitespace (reference_section->buffer[start])) 611 1.1 christos break; 612 1.1 christos 613 1.1 christos start++; 614 1.1 christos 615 1.1 christos for (end = position; end < reference_section->end; end++) 616 1.1 christos { 617 1.1 christos if (whitespace (reference_section->buffer[end])) 618 1.1 christos { 619 1.1 christos end = start; 620 1.1 christos break; 621 1.1 christos } 622 1.1 christos 623 1.1 christos if (reference_section->buffer[end] == ')') 624 1.1 christos { 625 1.1 christos end++; 626 1.1 christos break; 627 1.1 christos } 628 1.1 christos } 629 1.1 christos 630 1.1 christos if (end != start) 631 1.1 christos { 632 1.1 christos REFERENCE *entry; 633 1.1 christos int len = end - start; 634 1.1 christos 635 1.1 christos entry = (REFERENCE *)xmalloc (sizeof (REFERENCE)); 636 1.1 christos entry->label = (char *)xmalloc (1 + len); 637 1.1 christos strncpy (entry->label, (reference_section->buffer) + start, len); 638 1.1 christos entry->label[len] = '\0'; 639 1.1 christos entry->filename = xstrdup (node->filename); 640 1.1 christos entry->nodename = xstrdup (entry->label); 641 1.1 christos entry->start = start; 642 1.1 christos entry->end = end; 643 1.1 christos 644 1.1 christos add_pointer_to_array 645 1.1 christos (entry, refs_index, refs, refs_slots, 10, REFERENCE *); 646 1.1 christos } 647 1.1 christos 648 1.1 christos reference_section->start = position + 1; 649 1.1 christos } 650 1.1 christos 651 1.1 christos return (refs); 652 1.1 christos } 653 1.1 christos 654 1.1 christos long 655 1.1 christos locate_manpage_xref (NODE *node, long int start, int dir) 656 1.1 christos { 657 1.1 christos REFERENCE **refs; 658 1.1 christos long position = -1; 659 1.1 christos 660 1.1 christos refs = xrefs_of_manpage (node); 661 1.1 christos 662 1.1 christos if (refs) 663 1.1 christos { 664 1.1 christos register int i, count; 665 1.1 christos REFERENCE *entry; 666 1.1 christos 667 1.1 christos for (i = 0; refs[i]; i++); 668 1.1 christos count = i; 669 1.1 christos 670 1.1 christos if (dir > 0) 671 1.1 christos { 672 1.1 christos for (i = 0; (entry = refs[i]); i++) 673 1.1 christos if (entry->start > start) 674 1.1 christos { 675 1.1 christos position = entry->start; 676 1.1 christos break; 677 1.1 christos } 678 1.1 christos } 679 1.1 christos else 680 1.1 christos { 681 1.1 christos for (i = count - 1; i > -1; i--) 682 1.1 christos { 683 1.1 christos entry = refs[i]; 684 1.1 christos 685 1.1 christos if (entry->start < start) 686 1.1 christos { 687 1.1 christos position = entry->start; 688 1.1 christos break; 689 1.1 christos } 690 1.1 christos } 691 1.1 christos } 692 1.1 christos 693 1.1 christos info_free_references (refs); 694 1.1 christos } 695 1.1 christos return (position); 696 1.1 christos } 697 1.1 christos 698 1.1 christos /* This one was a little tricky. The binding buffer that is passed in has 699 1.1 christos a START and END value of 0 -- strlen (window-line-containing-point). 700 1.1 christos The BUFFER is a pointer to the start of that line. */ 701 1.1 christos REFERENCE ** 702 1.1 christos manpage_xrefs_in_binding (NODE *node, SEARCH_BINDING *binding) 703 1.1 christos { 704 1.1 christos register int i; 705 1.1 christos REFERENCE **all_refs = xrefs_of_manpage (node); 706 1.1 christos REFERENCE **brefs = (REFERENCE **)NULL; 707 1.1 christos REFERENCE *entry; 708 1.1 christos int brefs_index = 0; 709 1.1 christos int brefs_slots = 0; 710 1.1 christos int start, end; 711 1.1 christos 712 1.1 christos if (!all_refs) 713 1.1 christos return ((REFERENCE **)NULL); 714 1.1 christos 715 1.1 christos start = binding->start + (binding->buffer - node->contents); 716 1.1 christos end = binding->end + (binding->buffer - node->contents); 717 1.1 christos 718 1.1 christos for (i = 0; (entry = all_refs[i]); i++) 719 1.1 christos { 720 1.1 christos if ((entry->start > start) && (entry->end < end)) 721 1.1 christos { 722 1.1 christos add_pointer_to_array 723 1.1 christos (entry, brefs_index, brefs, brefs_slots, 10, REFERENCE *); 724 1.1 christos } 725 1.1 christos else 726 1.1 christos { 727 1.1 christos maybe_free (entry->label); 728 1.1 christos maybe_free (entry->filename); 729 1.1 christos maybe_free (entry->nodename); 730 1.1 christos free (entry); 731 1.1 christos } 732 1.1 christos } 733 1.1 christos 734 1.1 christos free (all_refs); 735 1.1 christos return (brefs); 736 1.1 christos } 737