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