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