1/* 2 3Copyright (c) 1987, 1988 X Consortium 4 5Permission is hereby granted, free of charge, to any person obtaining 6a copy of this software and associated documentation files (the 7"Software"), to deal in the Software without restriction, including 8without limitation the rights to use, copy, modify, merge, publish, 9distribute, sublicense, and/or sell copies of the Software, and to 10permit persons to whom the Software is furnished to do so, subject to 11the following conditions: 12 13The above copyright notice and this permission notice shall be included 14in all copies or substantial portions of the Software. 15 16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR 20OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22OTHER DEALINGS IN THE SOFTWARE. 23 24Except as contained in this notice, the name of the X Consortium shall 25not be used in advertising or otherwise to promote the sale, use or 26other dealings in this Software without prior written authorization 27from the X Consortium. 28 29*/ 30 31/* 32 * xman - X window system manual page display program. 33 * Author: Chris D. Peterson, MIT Project Athena 34 * Created: October 27, 1987 35 */ 36 37#ifdef HAVE_CONFIG_H 38# include "config.h" 39#endif 40 41#include "globals.h" 42#include "vendor.h" 43#include <X11/Xos.h> /* sys/types.h and unistd.h included in here */ 44#include <sys/stat.h> 45#include <errno.h> 46#include <X11/Xaw/Dialog.h> 47#include <X11/Shell.h> 48 49static FILE *Uncompress(ManpageGlobals * man_globals, const char *filename); 50 51static Boolean UncompressNamed(ManpageGlobals * man_globals, 52 const char *filename, char *output, 53 FILE ** output_file); 54static Boolean UncompressUnformatted(ManpageGlobals * man_globals, 55 const char *entry, char *filename, 56 FILE ** file); 57 58#ifdef HANDLE_ROFFSEQ 59static Boolean ConstructCommand(char *cmdbuf, const char *path, 60 const char *filename, const char *tempfile); 61#endif 62 63/* Function Name: PopupWarning 64 * Description: This function pops up a warning message. 65 * Arguments: string - the specific warning string. 66 * Returns: none 67 */ 68 69static Widget warnShell, warnDialog; 70 71static void 72PopdownWarning(Widget w, XtPointer client, XtPointer call) 73{ 74 XtPopdown((Widget) client); 75} 76 77void 78PopupWarning(ManpageGlobals * man_globals, const char *string) 79{ 80 int n; 81 Arg wargs[3]; 82 Dimension topX, topY; 83 char buffer[BUFSIZ]; 84 Boolean hasPosition; 85 86 snprintf(buffer, sizeof(buffer), "Xman Warning: %s", string); 87 hasPosition = FALSE; 88 if (top) { 89 n = 0; 90 XtSetArg(wargs[n], XtNx, &topX); 91 n++; 92 XtSetArg(wargs[n], XtNy, &topY); 93 n++; 94 XtGetValues(top, wargs, n); 95 hasPosition = TRUE; 96 } 97 98 if (man_globals != NULL) 99 ChangeLabel(man_globals->label, buffer); 100 if (man_globals->label == NULL) { 101 n = 0; 102 if (hasPosition) { 103 XtSetArg(wargs[n], XtNx, topX); 104 n++; 105 XtSetArg(wargs[n], XtNy, topY); 106 n++; 107 } 108 XtSetArg(wargs[n], XtNtransientFor, top); 109 n++; 110 warnShell = XtCreatePopupShell("warnShell", transientShellWidgetClass, 111 initial_widget, wargs, n); 112 XtSetArg(wargs[0], XtNlabel, buffer); 113 warnDialog = XtCreateManagedWidget("warnDialog", dialogWidgetClass, 114 warnShell, wargs, 1); 115 XawDialogAddButton(warnDialog, "dismiss", PopdownWarning, 116 (XtPointer) warnShell); 117 XtRealizeWidget(warnShell); 118 Popup(warnShell, XtGrabNone); 119 } 120} 121 122/* Function Name: PrintError 123 * Description: This Function prints an error message and exits. 124 * Arguments: string - the specific message. 125 * Returns: none. - exits though. 126 */ 127 128void 129PrintError(const char *string) 130{ 131 fprintf(stderr, "Xman Error: %s\n", string); 132 exit(EXIT_FAILURE); 133} 134 135/* Function Name: OpenFile 136 * Description: Assigns a file to the manpage. 137 * Arguments: man_globals - global structure. 138 * file - the file pointer. 139 * Returns: none 140 */ 141 142void 143OpenFile(ManpageGlobals * man_globals, FILE * file) 144{ 145 Arg arglist[1]; 146 Cardinal num_args = 0; 147 148 if (man_globals->curr_file) { 149#if 0 /* Ownership rules need to be fixed first */ 150 fclose(man_globals->curr_file); 151#endif 152 } 153 man_globals->curr_file = file; 154 155 XtSetArg(arglist[num_args], XtNfile, man_globals->curr_file); 156 num_args++; 157 XtSetValues(man_globals->manpagewidgets.manpage, arglist, num_args); 158} 159 160 161/* Function Name: FindManualFile 162 * Description: Opens the manual page file given the entry information. 163 * Arguments: man_globals - the globals info for this manpage. 164 * section_num - section number of the man page. 165 * entry_num - entry number of the man page. 166 * Returns: fp - the file pointer 167 * 168 * NOTES: 169 * 170 * If there is a uncompressed section it will look there for uncompressed 171 * manual pages first and then for individually compressed file in the 172 * uncompressed section. 173 * 174 * If there is a compressed directory then it will also look there for 175 * the manual pages. 176 * 177 * If both of these fail then it will attempt to format the manual page. 178 */ 179 180FILE * 181FindManualFile(ManpageGlobals * man_globals, int section_num, int entry_num) 182{ 183 FILE *file; 184 char path[BUFSIZ], page[BUFSIZ], section[BUFSIZ], *temp; 185 char filename[BUFSIZ]; 186 const char *entry = manual[section_num].entries[entry_num]; 187 int len_cat = strlen(CAT); 188 189 temp = CreateManpageName(entry, 0, 0); 190 snprintf(man_globals->manpage_title, sizeof(man_globals->manpage_title), 191 "The current manual page is: %s.", temp); 192 XtFree(temp); 193 194 ParseEntry(entry, path, section, page); 195 196/* 197 * Look for uncompressed files first. 198 */ 199#if defined(__OpenBSD__) || defined(__NetBSD__) 200 /* look in machine subdir first */ 201 snprintf(filename, sizeof(filename), "%s/%s%s/%s/%s", path, CAT, 202 section + len_cat, MACHINE, page); 203 if ((file = fopen(filename, "r")) != NULL) 204 return (file); 205#endif 206 207 snprintf(filename, sizeof(filename), "%s/%s%s/%s", 208 path, CAT, section + len_cat, page); 209 if ((file = fopen(filename, "r")) != NULL) 210 return (file); 211 212/* 213 * Then for compressed files in an uncompressed directory. 214 */ 215 216#if defined(__OpenBSD__) || defined(__NetBSD__) 217 /* look in machine subdir first */ 218 snprintf(filename, sizeof(filename), "%s/%s%s/%s/%s.%s", path, CAT, 219 section + len_cat, MACHINE, page, COMPRESSION_EXTENSION); 220 if ((file = Uncompress(man_globals, filename)) != NULL) 221 return (file); 222#endif 223 snprintf(filename, sizeof(filename), "%s/%s%s/%s.%s", path, CAT, 224 section + len_cat, page, COMPRESSION_EXTENSION); 225 if ((file = Uncompress(man_globals, filename)) != NULL) 226 return (file); 227#ifdef GZIP_EXTENSION 228 else { 229#if defined(__OpenBSD__) || defined(__NetBSD__) 230 /* look in machine subdir first */ 231 snprintf(filename, sizeof(filename), "%s/%s%s/%s/%s.%s", path, CAT, 232 section + len_cat, MACHINE, page, GZIP_EXTENSION); 233 if ((file = Uncompress(man_globals, filename)) != NULL) 234 return (file); 235#endif 236 snprintf(filename, sizeof(filename), "%s/%s%s/%s.%s", path, CAT, 237 section + len_cat, page, GZIP_EXTENSION); 238 if ((file = Uncompress(man_globals, filename)) != NULL) 239 return (file); 240 } 241#endif 242#ifdef BZIP2_EXTENSION 243#if defined(__OpenBSD__) || defined(__NetBSD__) 244 /* look in machine subdir first */ 245 snprintf(filename, sizeof(filename), "%s/%s%s/%s/%s.%s", path, CAT, 246 section + len_cat, MACHINE, page, BZIP2_EXTENSION); 247 if ((file = Uncompress(man_globals, filename)) != NULL) 248 return (file); 249#endif 250 { 251 snprintf(filename, sizeof(filename), "%s/%s%s/%s.%s", path, CAT, 252 section + len_cat, page, BZIP2_EXTENSION); 253 if ((file = Uncompress(man_globals, filename)) != NULL) 254 return (file); 255 } 256#endif 257#ifdef LZMA_EXTENSION 258 { 259 snprintf(filename, sizeof(filename), "%s/%s%s/%s.%s", path, CAT, 260 section + len_cat, page, LZMA_EXTENSION); 261 if ((file = Uncompress(man_globals, filename)) != NULL) 262 return (file); 263 } 264#endif 265 266/* 267 * And lastly files in a compressed directory. 268 * 269 * The directory is not actually compressed it is just named man#.Z 270 * and all files in it are compressed without the .Z extension. 271 * HP does it this way (really :-). 272 */ 273 274 snprintf(filename, sizeof(filename), "%s/%s%s.%s/%s", path, CAT, 275 section + len_cat, COMPRESSION_EXTENSION, page); 276 if ((file = Uncompress(man_globals, filename)) != NULL) 277 return (file); 278/* 279 * We did not find any preformatted manual pages, try to format it. 280 */ 281 282 return (Format(man_globals, entry)); 283} 284 285#ifndef HAVE_MKSTEMP 286/* Emulate mkstemp to allow use of a common API in the many calls below */ 287_X_HIDDEN int 288Xmkstemp (char *template) 289{ 290 int fd = 0; 291 char tmp[PATH_MAX]; 292 293 if (strlen(template) >= sizeof(tmp)) 294 return -1; 295 /* save copy of unmodified template in case we have to try again */ 296 strcpy(tmp, template); 297 298 do { 299 if (fd == -1) 300 strcpy(template, tmp); 301 if ((mktemp(template) == NULL) || (template[0] == '\0')) 302 return -1; 303 fd = open(template, O_RDWR | O_CREAT | O_EXCL, 0600); 304 } while ((fd == -1) && (errno == EEXIST || errno == EINTR)); 305 306 return fd; 307} 308#endif 309 310/* Function Namecompress 311 * Description: This function will attempt to find a compressed man 312 * page and uncompress it. 313 * Arguments: man_globals - the pseudo global info. 314 * filename - name of file to uncompress. 315 * Returns:; a pointer to the file or NULL. 316 */ 317 318static FILE * 319Uncompress(ManpageGlobals * man_globals, const char *filename) 320{ 321 char tmp_file[BUFSIZ]; 322 FILE *file; 323 324 if (!UncompressNamed(man_globals, filename, tmp_file, &file)) { 325 PopupWarning(man_globals, "Something went wrong in retrieving the " 326 "uncompressed manual page try cleaning up /tmp."); 327 return (NULL); 328 } 329 330 remove(tmp_file); /* remove name in tree, it will remain 331 until we close the fd, however. */ 332 return (file); 333} 334 335/* Function Name: UncompressNamed 336 * Description: This function will attempt to find a compressed man 337 * page and uncompress it. 338 * Arguments: man_globals - the pseudo global info. 339 * filename - name of file to uncompress. 340 * RETURNED output - the file name output (must be an allocated string). 341 * Returns:; TRUE if the file was found. 342 */ 343 344static Boolean 345UncompressNamed(ManpageGlobals * man_globals, const char *filename, 346 char *output, FILE ** output_file) 347{ 348 char tmp[BUFSIZ], cmdbuf[BUFSIZ], error_buf[BUFSIZ]; 349 struct stat junk; 350 int fd, omask; 351 352 if (stat(filename, &junk) != 0) { /* Check for existence of the file. */ 353 if (errno != ENOENT) { 354 snprintf(error_buf, sizeof(error_buf), 355 "Error while stating file %s, errno = %d", filename, 356 errno); 357 PopupWarning(man_globals, error_buf); 358 } 359 return (FALSE); 360 } 361 362/* 363 * Using stdin is necessary to fool zcat since we cannot guarantee 364 * the .Z extension. 365 */ 366 367 strcpy(tmp, MANTEMP); /* get a temp file. */ 368 omask = umask(077); 369 fd = mkstemp(tmp); 370 umask(omask); 371 if (fd < 0) { 372 PopupWarning(man_globals, "Error creating a temp file"); 373 return FALSE; 374 } 375 *output_file = fdopen(fd, "r"); 376 if (*output_file == NULL) { 377 remove(tmp); 378 close(fd); 379 PopupWarning(man_globals, "Error opening temp file"); 380 return FALSE; 381 } 382 strcpy(output, tmp); 383 384#ifdef GZIP_EXTENSION 385 if (streq(filename + strlen(filename) - strlen(GZIP_EXTENSION), 386 GZIP_EXTENSION)) 387 snprintf(cmdbuf, sizeof(cmdbuf), GUNZIP_FORMAT, filename, output); 388 else 389#endif 390#ifdef BZIP2_EXTENSION 391 if (streq(filename + strlen(filename) - strlen(BZIP2_EXTENSION), 392 BZIP2_EXTENSION)) 393 snprintf(cmdbuf, sizeof(cmdbuf), BUNZIP2_FORMAT, filename, output); 394 else 395#endif 396#ifdef LZMA_EXTENSION 397 if (streq(filename + strlen(filename) - strlen(LZMA_EXTENSION), 398 LZMA_EXTENSION)) 399 snprintf(cmdbuf, sizeof(cmdbuf), UNLZMA_FORMAT, filename, output); 400 else 401#endif 402 snprintf(cmdbuf, sizeof(cmdbuf), UNCOMPRESS_FORMAT, filename, output); 403 if (system(cmdbuf) == 0) /* execute search. */ 404 return (TRUE); 405 406 snprintf(error_buf, sizeof(error_buf), 407 "Error while uncompressing, command was: %s", cmdbuf); 408 PopupWarning(man_globals, error_buf); 409 return (FALSE); 410} 411 412#if defined(SMAN) && defined(SFORMAT) 413/* Function Name: SgmlToRoffNamed 414 * Description: This function will attempt to find an SGML man 415 * page and convert it to roff format. 416 * Arguments: man_globals - the pseudo global info. 417 * filename - name of file to uncompress. 418 * RETURNED output - the file name output (must be an allocated string). 419 * Returns:; TRUE if the file was found. 420 */ 421 422static Boolean 423SgmlToRoffNamed(ManpageGlobals * man_globals, char *filename, char *output, 424 FILE ** output_file) 425{ 426 char tmp[BUFSIZ], cmdbuf[BUFSIZ], error_buf[BUFSIZ]; 427 struct stat junk; 428 int fd, omask; 429 430 if (stat(filename, &junk) != 0) { /* Check for existence of the file. */ 431 if (errno != ENOENT) { 432 snprintf(error_buf, sizeof(error_buf), 433 "Error while stating file %s, errno = %d", filename, 434 errno); 435 PopupWarning(man_globals, error_buf); 436 } 437 return (FALSE); 438 } 439 440 strcpy(tmp, MANTEMP); /* get a temp file. */ 441 omask = umask(077): 442 fd = mkstemp(tmp); 443 umask(omask); 444 if (fd < 0) { 445 PopupWarning(man_globals, "Error creating a temp file"); 446 return FALSE; 447 } 448 *output_file = fdopen(fd, "r"); 449 if (*output_file == NULL) { 450 remove(tmp); 451 close(fd); 452 PopupWarning(man_globals, "Error opening temp file"); 453 return FALSE; 454 } 455 strcpy(output, tmp); 456 457 snprintf(cmdbuf, sizeof(cmdbuf), "%s %s >> %s", SFORMAT, filename, output); 458 if (system(cmdbuf) == 0) /* execute search. */ 459 return (TRUE); 460 461 snprintf(error_buf, sizeof(error_buf), 462 "Error while converting from sgml, command was: %s", cmdbuf); 463 PopupWarning(man_globals, error_buf); 464 return (FALSE); 465} 466#endif /* defined (SMAN) && defined(SFORMAT) */ 467 468/* Function Name: Format 469 * Description: This function formats the manual pages and interfaces 470 * with the user. 471 * Arguments: man_globals - the pseudo globals 472 * file - the file pointer to use and return 473 * entry - the current entry struct. 474 * current_box - The current directory being displayed. 475 * Returns: none. 476 */ 477 478/* ARGSUSED */ 479FILE * 480Format(ManpageGlobals * man_globals, const char *entry) 481{ 482 FILE *file = NULL; 483 int fd, omask; 484 485 Widget manpage = man_globals->manpagewidgets.manpage; 486 char cmdbuf[BUFSIZ], tmp[BUFSIZ], filename[BUFSIZ], error_buf[BUFSIZ]; 487 char path[BUFSIZ], sect[BUFSIZ]; 488 XEvent event; 489 Position x, y; /* location to pop up the 490 "would you like to save" widget. */ 491 492 if (!UncompressUnformatted(man_globals, entry, filename, &file)) { 493 /* We Really could not find it, this should never happen, yea right. */ 494 snprintf(error_buf, sizeof(error_buf), 495 "Could not open manual page, %s", entry); 496 PopupWarning(man_globals, error_buf); 497 XtPopdown(XtParent(man_globals->standby)); 498 return (NULL); 499 } 500 501 if (file != NULL) { 502 char line[BUFSIZ]; 503 504 if (fgets(line, sizeof(line), file) != NULL) { 505 if (strncmp(line, ".so ", 4) == 0) { 506 size_t len = strlen(line); /* must be >= 4 to pass strncmp */ 507 if (line[len - 1] == '\n') 508 line[len - 1] = '\0'; 509 fclose(file); 510 remove(filename); 511 if (line[4] != '/') { 512 char *ptr = NULL; 513 514 strcpy(tmp, entry); 515 if ((ptr = strrchr(tmp, '/')) != NULL) { 516 *ptr = '\0'; 517 if ((ptr = strrchr(tmp, '/')) != NULL) 518 ptr[1] = '\0'; 519 } 520 } 521 else 522 *tmp = '\0'; 523 snprintf(filename, sizeof(filename), "%s%s", tmp, line + 4); 524 525 return (Format(man_globals, filename)); 526 } 527 } 528 fclose(file); 529 } 530 531 Popup(XtParent(man_globals->standby), XtGrabExclusive); 532 while (!XCheckTypedWindowEvent(XtDisplay(man_globals->standby), 533 XtWindow(man_globals->standby), 534 Expose, &event)); 535 XtDispatchEvent(&event); 536 XFlush(XtDisplay(man_globals->standby)); 537 538 strcpy(tmp, MANTEMP); /* Get a temp file. */ 539 omask = umask(077); 540 fd = mkstemp(tmp); 541 umask(omask); 542 if (fd >= 0) { 543 file = fdopen(fd, "r"); 544 if (file == NULL) { 545 remove(tmp); 546 close(fd); 547 } 548 } 549 else 550 file = NULL; 551 if (file == NULL) { 552 PopupWarning(man_globals, "Something went wrong in opening the " 553 "temp file, try cleaning up /tmp"); 554 return NULL; 555 } 556 strcpy(man_globals->tempfile, tmp); 557 558 ParseEntry(entry, path, sect, NULL); 559 560#ifndef HANDLE_ROFFSEQ 561 snprintf(cmdbuf, sizeof(cmdbuf), "cd %s ; %s %s %s >> %s %s", path, TBL, 562 filename, FORMAT, man_globals->tempfile, "2> /dev/null"); 563#else 564 /* Handle more flexible way of specifying the formatting pipeline */ 565 if (!ConstructCommand(cmdbuf, path, filename, man_globals->tempfile)) { 566 PopupWarning(man_globals, "Constructed command was too long!"); 567 fclose(file); 568 file = NULL; 569 } 570 else 571#endif /* HANDLE_ROFFSEQ */ 572 573 if (system(cmdbuf) != 0) { /* execute search. */ 574 snprintf(error_buf, sizeof(error_buf), 575 "Something went wrong trying to run the command: %s", cmdbuf); 576 PopupWarning(man_globals, error_buf); 577 fclose(file); 578 file = NULL; 579 } 580 else { 581 if (file != NULL) { 582 XtPopdown(XtParent(man_globals->standby)); 583 584 if ((man_globals->save == NULL) || 585 (man_globals->manpagewidgets.manpage == NULL)) 586 remove(man_globals->tempfile); 587 else { 588 char *ptr, catdir[BUFSIZ]; 589 590 /* 591 * If the catdir is writable then ask the user if he/she wants to 592 * write the man page to it. 593 */ 594 595 strcpy(catdir, man_globals->save_file); 596 if ((ptr = strrchr(catdir, '/')) != NULL) { 597 *ptr = '\0'; 598 599 if (access(catdir, W_OK) != 0) 600 remove(man_globals->tempfile); 601 else { 602 x = (Position) Width(man_globals->manpagewidgets. 603 manpage) / 2; 604 y = (Position) Height(man_globals->manpagewidgets. 605 manpage) / 2; 606 XtTranslateCoords(manpage, x, y, &x, &y); 607 PositionCenter(man_globals->save, (int) x, (int) y, 0, 608 0, 0, 0); 609 XtPopup(man_globals->save, XtGrabExclusive); 610 } 611 } 612 else 613 remove(man_globals->tempfile); 614 } 615 } 616 } 617 618 /* 619 * If the original was compressed or in another format, delete temporary file. 620 */ 621 if (man_globals->deletetempfile) 622 remove(filename); 623 624 return (file); 625} 626 627#ifdef HANDLE_ROFFSEQ 628/* Function Name: ConstructCommand 629 * Description: Constructs the pipeline of commands necessary to format 630 * a manual page. 631 * Arguments: cmdbuf - the buffer into which to write the command 632 * path - the directory in which the original man page resides 633 * filename - the (uncompressed) manpage source file 634 * tempfile - the name of a temporary file to direct the final 635 * output of the pipeline into 636 * Returns: TRUE if the command fit into the buffer, FALSE if it would 637 * be too long (more than BUFSIZ characters) 638 */ 639static Boolean 640ConstructCommand(char *cmdbuf, const char *path, 641 const char *filename, const char *tempfile) 642{ 643#ifdef HAVE_MANDB 644 int used = snprintf(cmdbuf, BUFSIZ, "MAN_KEEP_FORMATTING=x GROFF_NO_SGR= " 645 "man -l %s > %s 2>/dev/null", 646 filename, tempfile); 647 if (used >= BUFSIZ - 1) 648 return FALSE; 649 return TRUE; 650#else 651 /* The original code did the following to produce a command line: 652 * sprintf(cmdbuf,"cd %s ; %s %s %s > %s %s", path, TBL, 653 * filename, FORMAT, man_globals->tempfile, "2> /dev/null"); 654 * We are more flexible and follow more or less the algorithm used 655 * by the Linux man command: 656 * + Obtain a string of letters from the following sources in order 657 * of preference: 658 * + a command line option (not implemented in xman; it's probably not 659 * useful) 660 * + the first line of the manpage source, if it is of the form: 661 * '\" <string> 662 * + the MANROFFSEQ environment variable 663 * + a default string; this is "". 664 * + Interpret the string as a pipeline of filters: 665 * + e = eqn g = grap p = pic t = tbl v = vgrind r = refer 666 * + zsoelim is always run as the first preprocessor in any case. 667 * 668 * Strictly speaking we should save a catpage iff the string comes 669 * from the file or is the default. 670 * 671 * You'll notice that we format a man page into ASCII text output and then 672 * attempt to interpret things like L^HL as bold and so forth. This 673 * is so obviously the Wrong Thing it's untrue. 674 */ 675 char *c = cmdbuf; /* current posn in buffer */ 676 int left = BUFSIZ; /* space left in buffer */ 677 int used; 678 const char *fmt; 679 char fmtbuf[128]; 680 681 fmt = NULL; 682 /* If you have a command line option that gives a setting for fmt, 683 set it here. */ 684 685 if (!fmt) { 686 /* This is the tricky bit: extract a format string from the source file 687 * Annoyingly, filename might be relative or absolute. We cheat and 688 * use system to get the thing to a known absolute filename. 689 */ 690 FILE *file; 691 int gotfmt = 0; /* set to 1 if we got a directive from source */ 692 char fname[PATH_MAX]; 693 694 if (filename[0] == '/') { 695 snprintf(fname, sizeof(fname), "%s", filename); 696 } 697 else { 698 snprintf(fname, sizeof(fname), "%s/%s", path, filename); 699 } 700 if ((file = fopen(fname, "r")) != NULL) { 701 if ((fgets(fmtbuf, sizeof(fmtbuf), file)) && 702 (!memcmp(fmtbuf, "'\\\" ", 4))) { 703 /* that's squote-backslash-dquote-space */ 704 int len = strlen(fmtbuf); 705 706 if (len && (fmtbuf[len - 1] == '\n')) { 707 fmtbuf[len - 1] = 0; 708 fmt = fmtbuf + 3; 709 gotfmt++; 710 } 711 } 712 fclose(file); 713 } 714 if (!gotfmt) { /* not there or some error */ 715 fmt = getenv("MANROFFSEQ"); 716 } 717 } 718 719 if (!fmt) { 720 fmt = DEFAULT_MANROFFSEQ; 721 } 722 723 /* Start with the first fixed part of the command line */ 724 used = snprintf(c, left, "cd %s; %s %s ", path, ZSOELIM, filename); 725 left -= used; 726 c += used; 727 if (left <= 1) 728 return (FALSE); 729 730 /* Now add preprocessors of the form '| processor' */ 731 for (; *fmt; fmt++) { 732 const char *filter; 733 734 switch (*fmt) { 735 case 'e': 736 filter = EQN; 737 break; 738 case 'g': 739 filter = GRAP; 740 break; 741 case 'p': 742 filter = ROFF_PIC; 743 break; 744 case 't': 745 filter = TBL; 746 break; 747 case 'v': 748 filter = VGRIND; 749 break; 750 case 'r': 751 filter = REFER; 752 break; 753 default: 754 filter = NULL; 755 break; 756 } 757 if (filter) { 758 used = snprintf(c, left, " | %s ", filter); 759 left -= used; 760 c += used; 761 if (left <= 1) 762 return (FALSE); 763 } 764 } 765 766 /* Now add the fixed trailing part 'formatprog > tempfile 2> /dev/null' */ 767 used = snprintf(c, left, " | %s >> %s 2>/dev/null", FORMAT, tempfile); 768 left -= used; 769 if (left <= 1) 770 return (FALSE); 771 772 return (TRUE); 773#endif /* man-db */ 774} 775#endif /* HANDLE_ROFFSEQ */ 776 777/* Function Name: UncompressUnformatted 778 * Description: Finds an uncompressed unformatted manual page. 779 * Arguments: man_globals - the pseudo global structure. 780 * entry - the manual page entry. 781 * RETURNED filename - location to put the name of the file. 782 * Returns: TRUE if the file was found. 783 */ 784 785static Boolean 786UncompressUnformatted(ManpageGlobals * man_globals, const char *entry, 787 char *filename, FILE ** file) 788{ 789 char path[BUFSIZ], page[BUFSIZ], section[BUFSIZ], input[BUFSIZ]; 790 int len_cat = strlen(CAT), len_man = strlen(MAN); 791 792#if defined(SMAN) && defined(SFORMAT) 793 int len_sman = strlen(SMAN); 794#endif 795 796 ParseEntry(entry, path, section, page); 797 798 man_globals->bzip2 = FALSE; 799 man_globals->lzma = FALSE; 800 801#if defined(__OpenBSD__) || defined(__NetBSD__) 802 /* 803 * look for uncompressed file in machine subdir first 804 */ 805 snprintf(filename, BUFSIZ, "%s/%s%s/%s/%s", path, MAN, 806 section + len_cat, MACHINE, page); 807 if (access(filename, R_OK) == 0) { 808 man_globals->compress = FALSE; 809 man_globals->gzip = FALSE; 810 man_globals->deletetempfile = FALSE; 811 snprintf(man_globals->save_file, sizeof(man_globals->save_file), 812 "%s/%s%s/%s/%s", path, CAT, section + len_cat, MACHINE, page); 813 return (TRUE); 814 } 815 /* 816 * Then for compressed files in an uncompressed directory. 817 */ 818 snprintf(input, sizeof(input), "%s.%s", filename, COMPRESSION_EXTENSION); 819 if (UncompressNamed(man_globals, input, filename, file)) { 820 man_globals->compress = TRUE; 821 man_globals->deletetempfile = TRUE; 822 snprintf(man_globals->save_file, sizeof(man_globals->save_file), 823 "%s/%s%s/%s.%s", path, CAT, section + len_cat, page, 824 COMPRESSION_EXTENSION); 825 return (TRUE); 826 } 827#ifdef GZIP_EXTENSION 828 else { 829 snprintf(input, sizeof(input), "%s.%s", filename, GZIP_EXTENSION); 830 if (UncompressNamed(man_globals, input, filename, file)) { 831 man_globals->compress = TRUE; 832 man_globals->gzip = TRUE; 833 man_globals->deletetempfile = TRUE; 834 snprintf(man_globals->save_file, sizeof(man_globals->save_file), 835 "%s/%s%s/%s.%s", path, CAT, section + len_cat, page, 836 GZIP_EXTENSION); 837 return (TRUE); 838 } 839 } 840#endif /* GZIP_EXTENSION */ 841#endif /* __OpenBSD__ || __NetBSD__ */ 842 843#ifdef BZIP2_EXTENSION 844 { 845 snprintf(input, sizeof(input), "%s.%s", filename, BZIP2_EXTENSION); 846 if (UncompressNamed(man_globals, input, filename, file)) { 847 man_globals->compress = TRUE; 848 man_globals->gzip = FALSE; 849 man_globals->bzip2 = TRUE; 850 snprintf(man_globals->save_file, sizeof(man_globals->save_file), 851 "%s/%s%s/%s.%s", path, CAT, section + len_cat, page, 852 BZIP2_EXTENSION); 853 return (TRUE); 854 } 855 } 856#endif /* BZIP2_EXTENSION */ 857 858#ifdef LZMA_EXTENSION 859 { 860 snprintf(input, sizeof(input), "%s.%s", filename, LZMA_EXTENSION); 861 if (UncompressNamed(man_globals, input, filename, file)) { 862 man_globals->compress = TRUE; 863 man_globals->gzip = FALSE; 864 man_globals->lzma = TRUE; 865 snprintf(man_globals->save_file, sizeof(man_globals->save_file), 866 "%s/%s%s/%s.%s", path, CAT, section + len_cat, page, 867 LZMA_EXTENSION); 868 return (TRUE); 869 } 870 } 871#endif /* LZMA_EXTENSION */ 872 873/* 874 * Look for uncompressed file first. 875 */ 876 877 snprintf(filename, BUFSIZ, "%s/%s%s/%s", path, MAN, section + len_man, 878 page); 879 if (access(filename, R_OK) == 0) { 880 man_globals->compress = FALSE; 881 man_globals->gzip = FALSE; 882 man_globals->deletetempfile = FALSE; 883 snprintf(man_globals->save_file, sizeof(man_globals->save_file), 884 "%s/%s%s/%s", path, CAT, section + len_cat, page); 885 return (TRUE); 886 } 887 888#if defined(SMAN) && defined(SFORMAT) 889 /* 890 * Look for uncompressed sgml file next. 891 */ 892 893 snprintf(input, BUFSIZ, "%s/%s%s/%s", path, SMAN, section + len_sman, page); 894 if (SgmlToRoffNamed(man_globals, input, filename, file)) { 895 man_globals->compress = FALSE; 896 man_globals->gzip = FALSE; 897 man_globals->deletetempfile = TRUE; 898 snprintf(man_globals->save_file, sizeof(man_globals->save_file), 899 "%s/%s%s/%s", path, CAT, section + len_cat, page); 900 return (TRUE); 901 } 902#endif 903 904/* 905 * Then for compressed files in an uncompressed directory. 906 */ 907 908 snprintf(input, sizeof(input), "%s.%s", filename, COMPRESSION_EXTENSION); 909 if (UncompressNamed(man_globals, input, filename, file)) { 910 man_globals->compress = TRUE; 911 man_globals->deletetempfile = TRUE; 912 snprintf(man_globals->save_file, sizeof(man_globals->save_file), 913 "%s/%s%s/%s.%s", path, CAT, section + len_cat, page, 914 COMPRESSION_EXTENSION); 915 return (TRUE); 916 } 917#ifdef GZIP_EXTENSION 918 else { 919 snprintf(input, sizeof(input), "%s.%s", filename, GZIP_EXTENSION); 920 if (UncompressNamed(man_globals, input, filename, file)) { 921 man_globals->compress = TRUE; 922 man_globals->gzip = TRUE; 923 man_globals->deletetempfile = TRUE; 924 snprintf(man_globals->save_file, sizeof(man_globals->save_file), 925 "%s/%s%s/%s.%s", path, CAT, section + len_cat, page, 926 GZIP_EXTENSION); 927 return (TRUE); 928 } 929 } 930#endif 931 932#ifdef BZIP2_EXTENSION 933 { 934 snprintf(input, sizeof(input), "%s.%s", filename, BZIP2_EXTENSION); 935 if (UncompressNamed(man_globals, input, filename, file)) { 936 man_globals->compress = TRUE; 937 man_globals->gzip = TRUE; 938 snprintf(man_globals->save_file, sizeof(man_globals->save_file), 939 "%s/%s%s/%s.%s", path, CAT, section + len_cat, page, 940 BZIP2_EXTENSION); 941 return (TRUE); 942 } 943 } 944#endif 945 946#ifdef LZMA_EXTENSION 947 { 948 snprintf(input, sizeof(input), "%s.%s", filename, LZMA_EXTENSION); 949 if (UncompressNamed(man_globals, input, filename, file)) { 950 man_globals->compress = TRUE; 951 man_globals->lzma = TRUE; 952 snprintf(man_globals->save_file, sizeof(man_globals->save_file), 953 "%s/%s%s/%s.%s", path, CAT, section + len_cat, page, 954 LZMA_EXTENSION); 955 return (TRUE); 956 } 957 } 958#endif 959 960/* 961 * And lastly files in a compressed directory. 962 */ 963 964 snprintf(input, sizeof(input), "%s/%s%s.%s/%s", path, 965 MAN, section + len_man, COMPRESSION_EXTENSION, page); 966 if (UncompressNamed(man_globals, input, filename, file)) { 967 man_globals->compress = TRUE; 968 man_globals->deletetempfile = TRUE; 969 snprintf(man_globals->save_file, sizeof(man_globals->save_file), 970 "%s/%s%s.%s/%s", path, CAT, section + len_cat, 971 COMPRESSION_EXTENSION, page); 972 return (TRUE); 973 } 974 return (FALSE); 975} 976 977/* Function Name: AddCursor 978 * Description: This function adds the cursor to the window. 979 * Arguments: w - the widget to add the cursor to. 980 * cursor - the cursor to add to this widget. 981 * Returns: none 982 */ 983 984void 985AddCursor(Widget w, Cursor cursor) 986{ 987 XColor colors[2]; 988 Arg args[10]; 989 Cardinal num_args = 0; 990 Colormap c_map; 991 992 if (!XtIsRealized(w)) { 993 PopupWarning(NULL, "Widget is not realized, no cursor added.\n"); 994 return; 995 } 996 997 XtSetArg(args[num_args], XtNcolormap, &c_map); 998 num_args++; 999 XtGetValues(w, args, num_args); 1000 1001 colors[0].pixel = resources.cursors.fg_color; 1002 colors[1].pixel = resources.cursors.bg_color; 1003 1004 XQueryColors(XtDisplay(w), c_map, colors, 2); 1005 XRecolorCursor(XtDisplay(w), cursor, colors, colors + 1); 1006 XDefineCursor(XtDisplay(w), XtWindow(w), cursor); 1007} 1008 1009/* Function Name: ChangeLabel 1010 * Description: This function changes the label field of the 1011 * given widget to the string in str. 1012 * Arguments: w - the widget. 1013 * str - the string to change the label to. 1014 * Returns: none 1015 */ 1016 1017void 1018ChangeLabel(Widget w, const char *str) 1019{ 1020 Arg arglist[3]; /* An argument list. */ 1021 1022 if (w == NULL) 1023 return; 1024 1025 XtSetArg(arglist[0], XtNlabel, str); 1026 1027/* shouldn't really have to do this. */ 1028 XtSetArg(arglist[1], XtNwidth, 0); 1029 XtSetArg(arglist[2], XtNheight, 0); 1030 1031 XtSetValues(w, arglist, (Cardinal) 1); 1032} 1033 1034/* 1035 * In an ideal world this would be part of the XToolkit, and I would not 1036 * have to do it, but such is life sometimes. Perhaps in X11R3. 1037 */ 1038 1039/* Function Name: PositionCenter 1040 * Description: This function positions the given widgets center 1041 * in the following location. 1042 * Arguments: widget - the widget widget to position 1043 * x,y - The location for the center of the widget 1044 * above - number of pixels above center to locate this widget 1045 * left - number of pixels left of center to locate this widget 1046 * h_space, v_space - how close to get to the edges of the 1047 * parent window. 1048 * Returns: none 1049 * Note: This should only be used with a popup widget that has override 1050 * redirect set. 1051 */ 1052 1053void 1054PositionCenter(Widget widget, int x, int y, int above, int left, int v_space, 1055 int h_space) 1056{ 1057 Arg wargs[2]; 1058 int x_temp, y_temp; /* location of the new window. */ 1059 int parent_height, parent_width; /* Height and width of the parent widget or 1060 the root window if it has no parent. */ 1061 1062 x_temp = x - left - Width(widget) / 2 + BorderWidth(widget); 1063 y_temp = y - above - Height(widget) / 2 + BorderWidth(widget); 1064 1065 parent_height = HeightOfScreen(XtScreen(widget)); 1066 parent_width = WidthOfScreen(XtScreen(widget)); 1067 1068/* 1069 * Check to make sure that all edges are within the viewable part of the 1070 * root window, and if not then force them to be. 1071 */ 1072 1073 if (x_temp < h_space) 1074 x_temp = v_space; 1075 if (y_temp < v_space) 1076 (y_temp = 2); 1077 1078 if (y_temp + Height(widget) + v_space > parent_height) 1079 y_temp = parent_height - Height(widget) - v_space; 1080 1081 if (x_temp + Width(widget) + h_space > parent_width) 1082 x_temp = parent_width - Width(widget) - h_space; 1083 1084 XtSetArg(wargs[0], XtNx, x_temp); 1085 XtSetArg(wargs[1], XtNy, y_temp); 1086 XtSetValues(widget, wargs, 2); 1087} 1088 1089/* Function Name: ParseEntry(entry, path, sect, page) 1090 * Description: Parses the manual pages entry filenames. 1091 * Arguments: str - the full path name. 1092 * path - the path name. RETURNED 1093 * sect - the section name. RETURNED 1094 * page - the page name. RETURNED 1095 * Returns: none. 1096 */ 1097 1098void 1099ParseEntry(const char *entry, char *path, char *sect, char *page) 1100{ 1101 char *c, temp[BUFSIZ]; 1102 1103 strcpy(temp, entry); 1104 1105 c = strrchr(temp, '/'); 1106 if (c == NULL) 1107 PrintError("Failed to find / in ParseEntry."); 1108 *c++ = '\0'; 1109 if (page != NULL) 1110 strcpy(page, c); 1111 1112 c = strrchr(temp, '/'); 1113 if (c == NULL) 1114 PrintError("Failed to find / in ParseEntry."); 1115 *c++ = '\0'; 1116#if defined(SFORMAT) && defined(SMAN) 1117 /* sgmltoroff sometimes puts an extra ./ in the path to .so entries */ 1118 if (strcmp(c, ".") == 0) { 1119 c = strrchr(temp, '/'); 1120 if (c == NULL) 1121 PrintError("Failed to find / in ParseEntry."); 1122 *c++ = '\0'; 1123 } 1124#endif 1125#if defined(__OpenBSD__) || defined(__NetBSD__) 1126 /* Skip machine subdirectory if present */ 1127 if (strcmp(c, MACHINE) == 0) { 1128 c = strrchr(temp, '/'); 1129 if (c == NULL) 1130 PrintError("Failed to find / in ParseEntry."); 1131 *c++ = '\0'; 1132 } 1133#endif 1134 if (sect != NULL) 1135 strcpy(sect, c); 1136 1137 if (path != NULL) 1138 strcpy(path, temp); 1139} 1140 1141/* Function Name: GetGlobals 1142 * Description: Gets the pseudo globals associated with the 1143 * manpage associated with this widget. 1144 * Arguments: w - a widget in the manpage. 1145 * Returns: the pseudo globals. 1146 * Notes: initial_widget is a globals variable. 1147 * manglobals_context is a global variable. 1148 */ 1149 1150ManpageGlobals * 1151GetGlobals(Widget w) 1152{ 1153 Widget temp; 1154 caddr_t data; 1155 1156 while ((temp = XtParent(w)) != initial_widget && (temp != NULL)) 1157 w = temp; 1158 1159 if (temp == NULL) 1160 XtAppError(XtWidgetToApplicationContext(w), 1161 "Xman: Could not locate widget in tree, exiting"); 1162 1163 if (XFindContext(XtDisplay(w), XtWindow(w), 1164 manglobals_context, &data) != XCSUCCESS) 1165 XtAppError(XtWidgetToApplicationContext(w), 1166 "Xman: Could not find global data, exiting"); 1167 1168 return ((ManpageGlobals *) data); 1169} 1170 1171/* Function Name: SaveGlobals 1172 * Description: Saves the pseudo globals on the widget passed 1173 * to this function, although GetGlobals assumes that 1174 * the data is associated with the popup child of topBox. 1175 * Arguments: w - the widget to associate the data with. 1176 * globals - data to associate with this widget. 1177 * Returns: none. 1178 * Notes: WIDGET MUST BE REALIZED. 1179 * manglobals_context is a global variable. 1180 */ 1181 1182void 1183SaveGlobals(Widget w, ManpageGlobals * globals) 1184{ 1185 if (XSaveContext(XtDisplay(w), XtWindow(w), manglobals_context, 1186 (caddr_t) globals) != XCSUCCESS) 1187 XtAppError(XtWidgetToApplicationContext(w), 1188 "Xman: Could not save global data, are you out of memory?"); 1189} 1190 1191/* Function Name: RemoveGlobals 1192 * Description: Removes the pseudo globals from the widget passed 1193 * to this function. 1194 * Arguments: w - the widget to remove the data from. 1195 * Returns: none. 1196 * Notes: WIDGET MUST BE REALIZED. 1197 * manglobals_context is a global variable. 1198 */ 1199 1200void 1201RemoveGlobals(Widget w) 1202{ 1203 if (XDeleteContext(XtDisplay(w), XtWindow(w), 1204 manglobals_context) != XCSUCCESS) 1205 XtAppError(XtWidgetToApplicationContext(w), 1206 "Xman: Could not remove global data?"); 1207} 1208