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