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