man.c revision 21495
1/* man.c: How to read and format man files. */ 2 3/* This file is part of GNU Info, a program for reading online documentation 4 stored in Info format. 5 6 Copyright (C) 1995 Free Software Foundation, Inc. 7 8 This program is free software; you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation; either version 2, or (at your option) 11 any later version. 12 13 This program is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with this program; if not, write to the Free Software 20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 21 22 Written by Brian Fox Thu May 4 09:17:52 1995 (bfox@ai.mit.edu). */ 23 24#include "info.h" 25#include <sys/ioctl.h> 26#include <sys/file.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#include "tilde.h" 35 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 50static char *read_from_fd (); 51static void clean_manpage (); 52static NODE *manpage_node_of_file_buffer (); 53static char *get_manpage_contents (); 54 55NODE * 56make_manpage_node (pagename) 57 char *pagename; 58{ 59 return (info_get_node (MANPAGE_FILE_BUFFER_NAME, pagename)); 60} 61 62NODE * 63get_manpage_node (file_buffer, pagename) 64 FILE_BUFFER *file_buffer; 65 char *pagename; 66{ 67 NODE *node; 68 69 node = manpage_node_of_file_buffer (file_buffer, pagename); 70 71 if (!node) 72 { 73 char *page; 74 75 page = get_manpage_contents (pagename); 76 77 if (page) 78 { 79 char header[1024]; 80 long oldsize, newsize; 81 int hlen, plen; 82 83 sprintf (header, "\n\n%c\n%s %s, %s %s, %s (dir)\n\n", 84 INFO_COOKIE, 85 INFO_FILE_LABEL, file_buffer->filename, 86 INFO_NODE_LABEL, pagename, 87 INFO_UP_LABEL); 88 oldsize = file_buffer->filesize; 89 hlen = strlen (header); 90 plen = strlen (page); 91 newsize = (oldsize + hlen + plen); 92 file_buffer->contents = 93 (char *)xrealloc (file_buffer->contents, 1 + newsize); 94 memcpy (file_buffer->contents + oldsize, header, hlen); 95 oldsize += hlen; 96 memcpy (file_buffer->contents + oldsize, page, plen); 97 file_buffer->contents[newsize] = '\0'; 98 file_buffer->filesize = newsize; 99 file_buffer->finfo.st_size = newsize; 100 build_tags_and_nodes (file_buffer); 101 free (page); 102 } 103 104 node = manpage_node_of_file_buffer (file_buffer, pagename); 105 } 106 107 return (node); 108} 109 110FILE_BUFFER * 111create_manpage_file_buffer () 112{ 113 FILE_BUFFER *file_buffer; 114 struct stat *finfo; 115 116 file_buffer = make_file_buffer (); 117 file_buffer->filename = strdup (MANPAGE_FILE_BUFFER_NAME); 118 file_buffer->fullpath = strdup (MANPAGE_FILE_BUFFER_NAME); 119 file_buffer->finfo.st_size = 0; 120 file_buffer->filesize = 0; 121 file_buffer->contents = (char *)NULL; 122 file_buffer->flags = (N_IsInternal | N_CannotGC | N_IsManPage); 123 124 return (file_buffer); 125} 126 127/* Scan the list of directories in PATH looking for FILENAME. If we find 128 one that is an executable file, return it as a new string. Otherwise, 129 return a NULL pointer. */ 130static char * 131executable_file_in_path (filename, path) 132 char *filename, *path; 133{ 134 struct stat finfo; 135 char *temp_dirname; 136 int statable, dirname_index; 137 138 dirname_index = 0; 139 140 while (temp_dirname = extract_colon_unit (path, &dirname_index)) 141 { 142 register int i; 143 char *temp; 144 145 /* Expand a leading tilde if one is present. */ 146 if (*temp_dirname == '~') 147 { 148 char *expanded_dirname; 149 150 expanded_dirname = tilde_expand_word (temp_dirname); 151 free (temp_dirname); 152 temp_dirname = expanded_dirname; 153 } 154 155 temp = (char *)xmalloc (30 + strlen (temp_dirname) + strlen (filename)); 156 strcpy (temp, temp_dirname); 157 if (temp[(strlen (temp)) - 1] != '/') 158 strcat (temp, "/"); 159 strcat (temp, filename); 160 161 free (temp_dirname); 162 163 statable = (stat (temp, &finfo) == 0); 164 165 /* If we have found a regular executable file, then use it. */ 166 if ((statable) && (S_ISREG (finfo.st_mode)) && 167 (access (temp, X_OK) == 0)) 168 return (temp); 169 else 170 free (temp); 171 } 172 return ((char *)NULL); 173} 174 175/* Return the full pathname of the system man page formatter. */ 176static char * 177find_man_formatter () 178{ 179 return (executable_file_in_path ("man", (char *)getenv ("PATH"))); 180} 181 182static char *manpage_pagename = (char *)NULL; 183static char *manpage_section = (char *)NULL; 184 185static void 186get_page_and_section (pagename) 187 char *pagename; 188{ 189 register int i; 190 191 if (manpage_pagename) 192 free (manpage_pagename); 193 194 if (manpage_section) 195 free (manpage_section); 196 197 manpage_pagename = (char *)NULL; 198 manpage_section = (char *)NULL; 199 200 for (i = 0; pagename[i] != '\0' && pagename[i] != '('; i++); 201 202 manpage_pagename = (char *)xmalloc (1 + i); 203 strncpy (manpage_pagename, pagename, i); 204 manpage_pagename[i] = '\0'; 205 206 if (pagename[i] == '(') 207 { 208 int start; 209 210 start = i + 1; 211 212 for (i = start; pagename[i] != '\0' && pagename[i] != ')'; i++); 213 214 manpage_section = (char *)xmalloc (1 + (i - start)); 215 strncpy (manpage_section, pagename + start, (i - start)); 216 manpage_section[i - start] = '\0'; 217 } 218} 219 220static void 221reap_children (sig) 222 int sig; 223{ 224 unsigned int status; 225 wait (&status); 226} 227 228static char * 229get_manpage_contents (pagename) 230 char *pagename; 231{ 232 static char *formatter_args[4] = { (char *)NULL }; 233 int pipes[2]; 234 pid_t child; 235 char *formatted_page = (char *)NULL; 236 char *section = (char *)NULL; 237 int arg_index = 1; 238 239 if (formatter_args[0] == (char *)NULL) 240 formatter_args[0] = find_man_formatter (); 241 242 if (formatter_args[0] == (char *)NULL) 243 return ((char *)NULL); 244 245 get_page_and_section (pagename); 246 247 if (manpage_section != (char *)NULL) 248 formatter_args[arg_index++] = manpage_section; 249 250 formatter_args[arg_index++] = manpage_pagename; 251 formatter_args[arg_index] = (char *)NULL; 252 253 /* Open a pipe to this program, read the output, and save it away 254 in FORMATTED_PAGE. The reader end of the pipe is pipes[0]; the 255 writer end is pipes[1]. */ 256 pipe (pipes); 257 258 signal (SIGCHLD, reap_children); 259 260 child = fork (); 261 262 if (child == -1) 263 return ((char *)NULL); 264 265 if (child != 0) 266 { 267 /* In the parent, close the writing end of the pipe, and read from 268 the exec'd child. */ 269 close (pipes[1]); 270 formatted_page = read_from_fd (pipes[0]); 271 close (pipes[0]); 272 } 273 else 274 { 275 /* In the child, close the read end of the pipe, make the write end 276 of the pipe be stdout, and execute the man page formatter. */ 277 close (pipes[0]); 278 close (fileno (stderr)); 279 close (fileno (stdin)); /* Don't print errors. */ 280 dup2 (pipes[1], fileno (stdout)); 281 282 execv (formatter_args[0], formatter_args); 283 284 /* If we get here, we couldn't exec, so close out the pipe and 285 exit. */ 286 close (pipes[1]); 287 exit (0); 288 } 289 290 /* If we have the page, then clean it up. */ 291 if (formatted_page) 292 clean_manpage (formatted_page); 293 294 return (formatted_page); 295} 296 297static void 298clean_manpage (manpage) 299 char *manpage; 300{ 301 register int i, j; 302 int newline_count = 0; 303 char *newpage; 304 305 newpage = (char *)xmalloc (1 + strlen (manpage)); 306 307 for (i = 0, j = 0; newpage[j] = manpage[i]; i++, j++) 308 { 309 if (manpage[i] == '\n') 310 newline_count++; 311 else 312 newline_count = 0; 313 314 if (newline_count == 3) 315 { 316 j--; 317 newline_count--; 318 } 319 320 if (manpage[i] == '\b' || manpage[i] == '\f') 321 j -= 2; 322 } 323 324 newpage[j++] = '\0'; 325 326 strcpy (manpage, newpage); 327 free (newpage); 328} 329 330static NODE * 331manpage_node_of_file_buffer (file_buffer, pagename) 332 FILE_BUFFER *file_buffer; 333 char *pagename; 334{ 335 NODE *node = (NODE *)NULL; 336 TAG *tag = (TAG *)NULL; 337 338 if (file_buffer->contents) 339 { 340 register int i; 341 342 for (i = 0; tag = file_buffer->tags[i]; i++) 343 { 344 if (strcasecmp (pagename, tag->nodename) == 0) 345 break; 346 } 347 } 348 349 if (tag) 350 { 351 node = (NODE *)xmalloc (sizeof (NODE)); 352 node->filename = file_buffer->filename; 353 node->nodename = tag->nodename; 354 node->contents = file_buffer->contents + tag->nodestart; 355 node->nodelen = tag->nodelen; 356 node->flags = 0; 357 node->parent = (char *)NULL; 358 node->flags = (N_HasTagsTable | N_IsManPage); 359 node->contents += skip_node_separator (node->contents); 360 } 361 362 return (node); 363} 364 365static char * 366read_from_fd (fd) 367 int fd; 368{ 369 struct timeval timeout; 370 char *buffer = (char *)NULL; 371 int bsize = 0; 372 int bindex = 0; 373 int select_result; 374#if defined (FD_SET) 375 fd_set read_fds; 376 377 timeout.tv_sec = 15; 378 timeout.tv_usec = 0; 379 380 FD_ZERO (&read_fds); 381 FD_SET (fd, &read_fds); 382 383 select_result = select (fd + 1, fd_set_cast (&read_fds), 0, 0, &timeout); 384#else /* !FD_SET */ 385 select_result = 1; 386#endif /* !FD_SET */ 387 388 switch (select_result) 389 { 390 case 0: 391 case -1: 392 break; 393 394 default: 395 { 396 int amount_read; 397 int done = 0; 398 399 while (!done) 400 { 401 while ((bindex + 1024) > (bsize)) 402 buffer = (char *)xrealloc (buffer, (bsize += 1024)); 403 buffer[bindex] = '\0'; 404 405 amount_read = read (fd, buffer + bindex, 1023); 406 407 if (amount_read < 0) 408 { 409 done = 1; 410 } 411 else 412 { 413 bindex += amount_read; 414 buffer[bindex] = '\0'; 415 if (amount_read == 0) 416 done = 1; 417 } 418 } 419 } 420 } 421 422 if ((buffer != (char *)NULL) && (*buffer == '\0')) 423 { 424 free (buffer); 425 buffer = (char *)NULL; 426 } 427 428 return (buffer); 429} 430 431static char *reference_section_starters[] = 432{ 433 "\nRELATED INFORMATION", 434 "\nRELATED\tINFORMATION", 435 "RELATED INFORMATION\n", 436 "RELATED\tINFORMATION\n", 437 "\nSEE ALSO", 438 "\nSEE\tALSO", 439 "SEE ALSO\n", 440 "SEE\tALSO\n", 441 (char *)NULL 442}; 443 444static SEARCH_BINDING frs_binding; 445 446static SEARCH_BINDING * 447find_reference_section (node) 448 NODE *node; 449{ 450 register int i; 451 long position = -1; 452 453 frs_binding.buffer = node->contents; 454 frs_binding.start = 0; 455 frs_binding.end = node->nodelen; 456 frs_binding.flags = S_SkipDest; 457 458 for (i = 0; reference_section_starters[i] != (char *)NULL; i++) 459 { 460 position = search_forward (reference_section_starters[i], &frs_binding); 461 if (position != -1) 462 break; 463 } 464 465 if (position == -1) 466 return ((SEARCH_BINDING *)NULL); 467 468 /* We found the start of the reference section, and point is right after 469 the string which starts it. The text from here to the next header 470 (or end of buffer) contains the only references in this manpage. */ 471 frs_binding.start = position; 472 473 for (i = frs_binding.start; i < frs_binding.end - 2; i++) 474 { 475 if ((frs_binding.buffer[i] == '\n') && 476 (!whitespace (frs_binding.buffer[i + 1]))) 477 { 478 frs_binding.end = i; 479 break; 480 } 481 } 482 483 return (&frs_binding); 484} 485 486REFERENCE ** 487xrefs_of_manpage (node) 488 NODE *node; 489{ 490 SEARCH_BINDING *reference_section; 491 REFERENCE **refs = (REFERENCE **)NULL; 492 int refs_index = 0; 493 int refs_slots = 0; 494 long position; 495 496 reference_section = find_reference_section (node); 497 498 if (reference_section == (SEARCH_BINDING *)NULL) 499 return ((REFERENCE **)NULL); 500 501 /* Grovel the reference section building a list of references found there. 502 A reference is alphabetic characters followed by non-whitespace text 503 within parenthesis. */ 504 reference_section->flags = 0; 505 506 while ((position = search_forward ("(", reference_section)) != -1) 507 { 508 register int start, end; 509 510 for (start = position; start > reference_section->start; start--) 511 if (whitespace (reference_section->buffer[start])) 512 break; 513 514 start++; 515 516 for (end = position; end < reference_section->end; end++) 517 { 518 if (whitespace (reference_section->buffer[end])) 519 { 520 end = start; 521 break; 522 } 523 524 if (reference_section->buffer[end] == ')') 525 { 526 end++; 527 break; 528 } 529 } 530 531 if (end != start) 532 { 533 REFERENCE *entry; 534 int len = end - start; 535 536 entry = (REFERENCE *)xmalloc (sizeof (REFERENCE)); 537 entry->label = (char *)xmalloc (1 + len); 538 strncpy (entry->label, (reference_section->buffer) + start, len); 539 entry->label[len] = '\0'; 540 entry->filename = strdup (node->filename); 541 entry->nodename = strdup (entry->label); 542 entry->start = start; 543 entry->end = end; 544 545 add_pointer_to_array 546 (entry, refs_index, refs, refs_slots, 10, REFERENCE *); 547 } 548 549 reference_section->start = position + 1; 550 } 551 552 return (refs); 553} 554 555long 556locate_manpage_xref (node, start, dir) 557 NODE *node; 558 long start; 559 int dir; 560{ 561 register int i, count; 562 REFERENCE **refs; 563 long position = -1; 564 565 refs = xrefs_of_manpage (node); 566 567 if (refs) 568 { 569 register int i, count; 570 REFERENCE *entry; 571 572 for (i = 0; refs[i]; i++); 573 count = i; 574 575 if (dir > 0) 576 { 577 for (i = 0; entry = refs[i]; i++) 578 if (entry->start > start) 579 { 580 position = entry->start; 581 break; 582 } 583 } 584 else 585 { 586 for (i = count - 1; i > -1; i--) 587 { 588 entry = refs[i]; 589 590 if (entry->start < start) 591 { 592 position = entry->start; 593 break; 594 } 595 } 596 } 597 598 info_free_references (refs); 599 } 600 return (position); 601} 602 603/* This one was a little tricky. The binding buffer that is passed in has 604 a START and END value of 0 -- strlen (window-line-containing-point). 605 The BUFFER is a pointer to the start of that line. */ 606REFERENCE ** 607manpage_xrefs_in_binding (node, binding) 608 NODE *node; 609 SEARCH_BINDING *binding; 610{ 611 register int i; 612 REFERENCE **all_refs = xrefs_of_manpage (node); 613 REFERENCE **brefs = (REFERENCE **)NULL; 614 REFERENCE *entry; 615 int brefs_index = 0; 616 int brefs_slots = 0; 617 int start, end; 618 619 if (!all_refs) 620 return ((REFERENCE **)NULL); 621 622 start = binding->start + (binding->buffer - node->contents); 623 end = binding->end + (binding->buffer - node->contents); 624 625 for (i = 0; entry = all_refs[i]; i++) 626 { 627 if ((entry->start > start) && (entry->end < end)) 628 { 629 add_pointer_to_array 630 (entry, brefs_index, brefs, brefs_slots, 10, REFERENCE *); 631 } 632 else 633 { 634 maybe_free (entry->label); 635 maybe_free (entry->filename); 636 maybe_free (entry->nodename); 637 free (entry); 638 } 639 } 640 641 free (all_refs); 642 return (brefs); 643} 644