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