misc.c revision e8f4a63f
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, omask; 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 omask = umask(077); 396 fd = mkstemp(tmp); 397 umask(omask); 398 if (fd < 0) { 399 PopupWarning(man_globals, "Error creating a temp file"); 400 return FALSE; 401 } 402 *output_file = fdopen(fd, "r"); 403 if (*output_file == NULL) { 404 remove(tmp); 405 close(fd); 406 PopupWarning(man_globals, "Error opening temp file"); 407 return FALSE; 408 } 409 strcpy(output, tmp); 410 411#ifdef GZIP_EXTENSION 412 if (streq(filename + strlen(filename) - strlen(GZIP_EXTENSION), 413 GZIP_EXTENSION)) 414 snprintf(cmdbuf, sizeof(cmdbuf), GUNZIP_FORMAT, filename, output); 415 else 416#endif 417#ifdef BZIP2_EXTENSION 418 if (streq(filename + strlen(filename) - strlen(BZIP2_EXTENSION), 419 BZIP2_EXTENSION)) 420 snprintf(cmdbuf, sizeof(cmdbuf), BUNZIP2_FORMAT, filename, output); 421 else 422#endif 423#ifdef LZMA_EXTENSION 424 if (streq(filename + strlen(filename) - strlen(LZMA_EXTENSION), 425 LZMA_EXTENSION)) 426 snprintf(cmdbuf, sizeof(cmdbuf), UNLZMA_FORMAT, filename, output); 427 else 428#endif 429 snprintf(cmdbuf, sizeof(cmdbuf), UNCOMPRESS_FORMAT, filename, output); 430 if (system(cmdbuf) == 0) /* execute search. */ 431 return (TRUE); 432 433 snprintf(error_buf, sizeof(error_buf), 434 "Error while uncompressing, command was: %s", cmdbuf); 435 PopupWarning(man_globals, error_buf); 436 return (FALSE); 437} 438 439#if defined(SMAN) && defined(SFORMAT) 440/* Function Name: SgmlToRoffNamed 441 * Description: This function will attempt to find an SGML man 442 * page and convert it to roff format. 443 * Arguments: man_globals - the pseudo global info. 444 * filename - name of file to uncompress. 445 * RETURNED output - the file name output (must be an allocated string). 446 * Returns:; TRUE if the file was found. 447 */ 448 449static Boolean 450SgmlToRoffNamed(ManpageGlobals * man_globals, char *filename, char *output, 451 FILE ** output_file) 452{ 453 char tmp[BUFSIZ], cmdbuf[BUFSIZ], error_buf[BUFSIZ]; 454 struct stat junk; 455 int fd, omask; 456 457 if (stat(filename, &junk) != 0) { /* Check for existence of the file. */ 458 if (errno != ENOENT) { 459 snprintf(error_buf, sizeof(error_buf), 460 "Error while stating file %s, errno = %d", filename, 461 errno); 462 PopupWarning(man_globals, error_buf); 463 } 464 return (FALSE); 465 } 466 467 strcpy(tmp, MANTEMP); /* get a temp file. */ 468 omask = umask(077): 469 fd = mkstemp(tmp); 470 umask(omask); 471 if (fd < 0) { 472 PopupWarning(man_globals, "Error creating a temp file"); 473 return FALSE; 474 } 475 *output_file = fdopen(fd, "r"); 476 if (*output_file == NULL) { 477 remove(tmp); 478 close(fd); 479 PopupWarning(man_globals, "Error opening temp file"); 480 return FALSE; 481 } 482 strcpy(output, tmp); 483 484 snprintf(cmdbuf, sizeof(cmdbuf), "%s %s >> %s", SFORMAT, filename, output); 485 if (system(cmdbuf) == 0) /* execute search. */ 486 return (TRUE); 487 488 snprintf(error_buf, sizeof(error_buf), 489 "Error while converting from sgml, command was: %s", cmdbuf); 490 PopupWarning(man_globals, error_buf); 491 return (FALSE); 492} 493#endif /* defined (SMAN) && defined(SFORMAT) */ 494 495/* Function Name: Format 496 * Description: This function formats the manual pages and interfaces 497 * with the user. 498 * Arguments: man_globals - the pseudo globals 499 * file - the file pointer to use and return 500 * entry - the current entry struct. 501 * current_box - The current directory being displayed. 502 * Returns: none. 503 */ 504 505/* ARGSUSED */ 506FILE * 507Format(ManpageGlobals * man_globals, const char *entry) 508{ 509 FILE *file = NULL; 510 int fd, omask; 511 512 Widget manpage = man_globals->manpagewidgets.manpage; 513 char cmdbuf[BUFSIZ], tmp[BUFSIZ], filename[BUFSIZ], error_buf[BUFSIZ]; 514 char path[BUFSIZ], sect[BUFSIZ]; 515 XEvent event; 516 Position x, y; /* location to pop up the 517 "would you like to save" widget. */ 518 519 if (!UncompressUnformatted(man_globals, entry, filename, &file)) { 520 /* We Really could not find it, this should never happen, yea right. */ 521 snprintf(error_buf, sizeof(error_buf), 522 "Could not open manual page, %s", entry); 523 PopupWarning(man_globals, error_buf); 524 XtPopdown(XtParent(man_globals->standby)); 525 return (NULL); 526 } 527 528 if (file != NULL) { 529 char line[BUFSIZ]; 530 531 if (fgets(line, sizeof(line), file) != NULL) { 532 if (strncmp(line, ".so ", 4) == 0) { 533 size_t len = strlen(line); /* must be >= 4 to pass strncmp */ 534 if (line[len - 1] == '\n') 535 line[len - 1] = '\0'; 536 fclose(file); 537 remove(filename); 538 if (line[4] != '/') { 539 char *ptr = NULL; 540 541 strcpy(tmp, entry); 542 if ((ptr = strrchr(tmp, '/')) != NULL) { 543 *ptr = '\0'; 544 if ((ptr = strrchr(tmp, '/')) != NULL) 545 ptr[1] = '\0'; 546 } 547 } 548 else 549 *tmp = '\0'; 550 snprintf(filename, sizeof(filename), "%s%s", tmp, line + 4); 551 552 return (Format(man_globals, filename)); 553 } 554 } 555 fclose(file); 556 } 557 558 Popup(XtParent(man_globals->standby), XtGrabExclusive); 559 while (!XCheckTypedWindowEvent(XtDisplay(man_globals->standby), 560 XtWindow(man_globals->standby), 561 Expose, &event)); 562 XtDispatchEvent(&event); 563 XFlush(XtDisplay(man_globals->standby)); 564 565 strcpy(tmp, MANTEMP); /* Get a temp file. */ 566 omask = umask(077); 567 fd = mkstemp(tmp); 568 umask(omask); 569 if (fd >= 0) { 570 file = fdopen(fd, "r"); 571 if (file == NULL) { 572 remove(tmp); 573 close(fd); 574 } 575 } 576 else 577 file = NULL; 578 if (file == NULL) { 579 PopupWarning(man_globals, "Something went wrong in opening the " 580 "temp file, try cleaning up /tmp"); 581 return NULL; 582 } 583 strcpy(man_globals->tempfile, tmp); 584 585 ParseEntry(entry, path, sect, NULL); 586 587#ifndef HANDLE_ROFFSEQ 588 snprintf(cmdbuf, sizeof(cmdbuf), "cd %s ; %s %s %s >> %s %s", path, TBL, 589 filename, FORMAT, man_globals->tempfile, "2> /dev/null"); 590#else 591 /* Handle more flexible way of specifying the formatting pipeline */ 592 if (!ConstructCommand(cmdbuf, path, filename, man_globals->tempfile)) { 593 PopupWarning(man_globals, "Constructed command was too long!"); 594 fclose(file); 595 file = NULL; 596 } 597 else 598#endif /* HANDLE_ROFFSEQ */ 599 600 if (system(cmdbuf) != 0) { /* execute search. */ 601 snprintf(error_buf, sizeof(error_buf), 602 "Something went wrong trying to run the command: %s", cmdbuf); 603 PopupWarning(man_globals, error_buf); 604 fclose(file); 605 file = NULL; 606 } 607 else { 608 if (file != NULL) { 609 XtPopdown(XtParent(man_globals->standby)); 610 611 if ((man_globals->save == NULL) || 612 (man_globals->manpagewidgets.manpage == NULL)) 613 remove(man_globals->tempfile); 614 else { 615 char *ptr, catdir[BUFSIZ]; 616 617 /* 618 * If the catdir is writable then ask the user if he/she wants to 619 * write the man page to it. 620 */ 621 622 strcpy(catdir, man_globals->save_file); 623 if ((ptr = strrchr(catdir, '/')) != NULL) { 624 *ptr = '\0'; 625 626 if (access(catdir, W_OK) != 0) 627 remove(man_globals->tempfile); 628 else { 629 x = (Position) Width(man_globals->manpagewidgets. 630 manpage) / 2; 631 y = (Position) Height(man_globals->manpagewidgets. 632 manpage) / 2; 633 XtTranslateCoords(manpage, x, y, &x, &y); 634 PositionCenter(man_globals->save, (int) x, (int) y, 0, 635 0, 0, 0); 636 XtPopup(man_globals->save, XtGrabExclusive); 637 } 638 } 639 else 640 remove(man_globals->tempfile); 641 } 642 } 643 } 644 645 /* 646 * If the original was compressed or in another format, delete temporary file. 647 */ 648 if (man_globals->deletetempfile) 649 remove(filename); 650 651 return (file); 652} 653 654#ifdef HANDLE_ROFFSEQ 655/* Function Name: ConstructCommand 656 * Description: Constructs the pipeline of commands necessary to format 657 * a manual page. 658 * Arguments: cmdbuf - the buffer into which to write the command 659 * path - the directory in which the original man page resides 660 * filename - the (uncompressed) manpage source file 661 * tempfile - the name of a temporary file to direct the final 662 * output of the pipeline into 663 * Returns: TRUE if the command fit into the buffer, FALSE if it would 664 * be too long (more than BUFSIZ characters) 665 */ 666static Boolean 667ConstructCommand(char *cmdbuf, const char *path, 668 const char *filename, const char *tempfile) 669{ 670#ifdef HAVE_MANDB 671 int used = snprintf(cmdbuf, BUFSIZ, "man -l %s > %s 2>/dev/null", 672 filename, tempfile); 673 if (used >= BUFSIZ - 1) 674 return FALSE; 675 return TRUE; 676#else 677 /* The original code did the following to produce a command line: 678 * sprintf(cmdbuf,"cd %s ; %s %s %s > %s %s", path, TBL, 679 * filename, FORMAT, man_globals->tempfile, "2> /dev/null"); 680 * We are more flexible and follow more or less the algorithm used 681 * by the Linux man command: 682 * + Obtain a string of letters from the following sources in order 683 * of preference: 684 * + a command line option (not implemented in xman; it's probably not 685 * useful) 686 * + the first line of the manpage source, if it is of the form: 687 * '\" <string> 688 * + the MANROFFSEQ environment variable 689 * + a default string; this is "". 690 * + Interpret the string as a pipeline of filters: 691 * + e = eqn g = grap p = pic t = tbl v = vgrind r = refer 692 * + zsoelim is always run as the first preprocessor in any case. 693 * 694 * Strictly speaking we should save a catpage iff the string comes 695 * from the file or is the default. 696 * 697 * You'll notice that we format a man page into ASCII text output and then 698 * attempt to interpret things like L^HL as bold and so forth. This 699 * is so obviously the Wrong Thing it's untrue. 700 */ 701 char *c = cmdbuf; /* current posn in buffer */ 702 int left = BUFSIZ; /* space left in buffer */ 703 int used; 704 const char *fmt; 705 char fmtbuf[128]; 706 707 fmt = NULL; 708 /* If you have a command line option that gives a setting for fmt, 709 set it here. */ 710 711 if (!fmt) { 712 /* This is the tricky bit: extract a format string from the source file 713 * Annoyingly, filename might be relative or absolute. We cheat and 714 * use system to get the thing to a known absolute filename. 715 */ 716 FILE *file; 717 int gotfmt = 0; /* set to 1 if we got a directive from source */ 718 char fname[PATH_MAX]; 719 720 if (filename[0] == '/') { 721 snprintf(fname, sizeof(fname), "%s", filename); 722 } 723 else { 724 snprintf(fname, sizeof(fname), "%s/%s", path, filename); 725 } 726 if ((file = fopen(fname, "r")) != NULL) { 727 if ((fgets(fmtbuf, sizeof(fmtbuf), file)) && 728 (!memcmp(fmtbuf, "'\\\" ", 4))) { 729 /* that's squote-backslash-dquote-space */ 730 int len = strlen(fmtbuf); 731 732 if (len && (fmtbuf[len - 1] == '\n')) { 733 fmtbuf[len - 1] = 0; 734 fmt = fmtbuf + 3; 735 gotfmt++; 736 } 737 } 738 fclose(file); 739 } 740 if (!gotfmt) { /* not there or some error */ 741 fmt = getenv("MANROFFSEQ"); 742 } 743 } 744 745 if (!fmt) { 746 fmt = DEFAULT_MANROFFSEQ; 747 } 748 749 /* Start with the first fixed part of the command line */ 750 used = snprintf(c, left, "cd %s; %s %s ", path, ZSOELIM, filename); 751 left -= used; 752 c += used; 753 if (left <= 1) 754 return (FALSE); 755 756 /* Now add preprocessors of the form '| processor' */ 757 for (; *fmt; fmt++) { 758 const char *filter; 759 760 switch (*fmt) { 761 case 'e': 762 filter = EQN; 763 break; 764 case 'g': 765 filter = GRAP; 766 break; 767 case 'p': 768 filter = ROFF_PIC; 769 break; 770 case 't': 771 filter = TBL; 772 break; 773 case 'v': 774 filter = VGRIND; 775 break; 776 case 'r': 777 filter = REFER; 778 break; 779 default: 780 filter = NULL; 781 break; 782 } 783 if (filter) { 784 used = snprintf(c, left, " | %s ", filter); 785 left -= used; 786 c += used; 787 if (left <= 1) 788 return (FALSE); 789 } 790 } 791 792 /* Now add the fixed trailing part 'formatprog > tempfile 2> /dev/null' */ 793 used = snprintf(c, left, " | %s >> %s 2>/dev/null", FORMAT, tempfile); 794 left -= used; 795 if (left <= 1) 796 return (FALSE); 797 798 return (TRUE); 799#endif /* man-db */ 800} 801#endif /* HANDLE_ROFFSEQ */ 802 803/* Function Name: UncompressUnformatted 804 * Description: Finds an uncompressed unformatted manual page. 805 * Arguments: man_globals - the pseudo global structure. 806 * entry - the manual page entry. 807 * RETURNED filename - location to put the name of the file. 808 * Returns: TRUE if the file was found. 809 */ 810 811static Boolean 812UncompressUnformatted(ManpageGlobals * man_globals, const char *entry, 813 char *filename, FILE ** file) 814{ 815 char path[BUFSIZ], page[BUFSIZ], section[BUFSIZ], input[BUFSIZ]; 816 int len_cat = strlen(CAT), len_man = strlen(MAN); 817 818#if defined(SMAN) && defined(SFORMAT) 819 int len_sman = strlen(SMAN); 820#endif 821 822 ParseEntry(entry, path, section, page); 823 824 man_globals->bzip2 = FALSE; 825 man_globals->lzma = FALSE; 826 827#if defined(__OpenBSD__) || defined(__NetBSD__) 828 /* 829 * look for uncompressed file in machine subdir first 830 */ 831 snprintf(filename, BUFSIZ, "%s/%s%s/%s/%s", path, MAN, 832 section + len_cat, MACHINE, page); 833 if (access(filename, R_OK) == 0) { 834 man_globals->compress = FALSE; 835 man_globals->gzip = FALSE; 836 man_globals->deletetempfile = FALSE; 837 snprintf(man_globals->save_file, sizeof(man_globals->save_file), 838 "%s/%s%s/%s/%s", path, CAT, section + len_cat, MACHINE, page); 839 return (TRUE); 840 } 841 /* 842 * Then for compressed files in an uncompressed directory. 843 */ 844 snprintf(input, sizeof(input), "%s.%s", filename, COMPRESSION_EXTENSION); 845 if (UncompressNamed(man_globals, input, filename, file)) { 846 man_globals->compress = TRUE; 847 man_globals->deletetempfile = TRUE; 848 snprintf(man_globals->save_file, sizeof(man_globals->save_file), 849 "%s/%s%s/%s.%s", path, CAT, section + len_cat, page, 850 COMPRESSION_EXTENSION); 851 return (TRUE); 852 } 853#ifdef GZIP_EXTENSION 854 else { 855 snprintf(input, sizeof(input), "%s.%s", filename, GZIP_EXTENSION); 856 if (UncompressNamed(man_globals, input, filename, file)) { 857 man_globals->compress = TRUE; 858 man_globals->gzip = TRUE; 859 man_globals->deletetempfile = 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 GZIP_EXTENSION); 863 return (TRUE); 864 } 865 } 866#endif /* GZIP_EXTENSION */ 867#endif /* __OpenBSD__ || __NetBSD__ */ 868 869#ifdef BZIP2_EXTENSION 870 { 871 snprintf(input, sizeof(input), "%s.%s", filename, BZIP2_EXTENSION); 872 if (UncompressNamed(man_globals, input, filename, file)) { 873 man_globals->compress = TRUE; 874 man_globals->gzip = FALSE; 875 man_globals->bzip2 = TRUE; 876 snprintf(man_globals->save_file, sizeof(man_globals->save_file), 877 "%s/%s%s/%s.%s", path, CAT, section + len_cat, page, 878 BZIP2_EXTENSION); 879 return (TRUE); 880 } 881 } 882#endif /* BZIP2_EXTENSION */ 883 884#ifdef LZMA_EXTENSION 885 { 886 snprintf(input, sizeof(input), "%s.%s", filename, LZMA_EXTENSION); 887 if (UncompressNamed(man_globals, input, filename, file)) { 888 man_globals->compress = TRUE; 889 man_globals->gzip = FALSE; 890 man_globals->lzma = TRUE; 891 snprintf(man_globals->save_file, sizeof(man_globals->save_file), 892 "%s/%s%s/%s.%s", path, CAT, section + len_cat, page, 893 LZMA_EXTENSION); 894 return (TRUE); 895 } 896 } 897#endif /* LZMA_EXTENSION */ 898 899/* 900 * Look for uncompressed file first. 901 */ 902 903 snprintf(filename, BUFSIZ, "%s/%s%s/%s", path, MAN, section + len_man, 904 page); 905 if (access(filename, R_OK) == 0) { 906 man_globals->compress = FALSE; 907 man_globals->gzip = FALSE; 908 man_globals->deletetempfile = FALSE; 909 snprintf(man_globals->save_file, sizeof(man_globals->save_file), 910 "%s/%s%s/%s", path, CAT, section + len_cat, page); 911 return (TRUE); 912 } 913 914#if defined(SMAN) && defined(SFORMAT) 915 /* 916 * Look for uncompressed sgml file next. 917 */ 918 919 snprintf(input, BUFSIZ, "%s/%s%s/%s", path, SMAN, section + len_sman, page); 920 if (SgmlToRoffNamed(man_globals, input, filename, file)) { 921 man_globals->compress = FALSE; 922 man_globals->gzip = FALSE; 923 man_globals->deletetempfile = TRUE; 924 snprintf(man_globals->save_file, sizeof(man_globals->save_file), 925 "%s/%s%s/%s", path, CAT, section + len_cat, page); 926 return (TRUE); 927 } 928#endif 929 930/* 931 * Then for compressed files in an uncompressed directory. 932 */ 933 934 snprintf(input, sizeof(input), "%s.%s", filename, COMPRESSION_EXTENSION); 935 if (UncompressNamed(man_globals, input, filename, file)) { 936 man_globals->compress = TRUE; 937 man_globals->deletetempfile = 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 COMPRESSION_EXTENSION); 941 return (TRUE); 942 } 943#ifdef GZIP_EXTENSION 944 else { 945 snprintf(input, sizeof(input), "%s.%s", filename, GZIP_EXTENSION); 946 if (UncompressNamed(man_globals, input, filename, file)) { 947 man_globals->compress = TRUE; 948 man_globals->gzip = TRUE; 949 man_globals->deletetempfile = TRUE; 950 snprintf(man_globals->save_file, sizeof(man_globals->save_file), 951 "%s/%s%s/%s.%s", path, CAT, section + len_cat, page, 952 GZIP_EXTENSION); 953 return (TRUE); 954 } 955 } 956#endif 957 958#ifdef BZIP2_EXTENSION 959 { 960 snprintf(input, sizeof(input), "%s.%s", filename, BZIP2_EXTENSION); 961 if (UncompressNamed(man_globals, input, filename, file)) { 962 man_globals->compress = TRUE; 963 man_globals->gzip = TRUE; 964 snprintf(man_globals->save_file, sizeof(man_globals->save_file), 965 "%s/%s%s/%s.%s", path, CAT, section + len_cat, page, 966 BZIP2_EXTENSION); 967 return (TRUE); 968 } 969 } 970#endif 971 972#ifdef LZMA_EXTENSION 973 { 974 snprintf(input, sizeof(input), "%s.%s", filename, LZMA_EXTENSION); 975 if (UncompressNamed(man_globals, input, filename, file)) { 976 man_globals->compress = TRUE; 977 man_globals->lzma = TRUE; 978 snprintf(man_globals->save_file, sizeof(man_globals->save_file), 979 "%s/%s%s/%s.%s", path, CAT, section + len_cat, page, 980 LZMA_EXTENSION); 981 return (TRUE); 982 } 983 } 984#endif 985 986/* 987 * And lastly files in a compressed directory. 988 */ 989 990 snprintf(input, sizeof(input), "%s/%s%s.%s/%s", path, 991 MAN, section + len_man, COMPRESSION_EXTENSION, page); 992 if (UncompressNamed(man_globals, input, filename, file)) { 993 man_globals->compress = TRUE; 994 man_globals->deletetempfile = TRUE; 995 snprintf(man_globals->save_file, sizeof(man_globals->save_file), 996 "%s/%s%s.%s/%s", path, CAT, section + len_cat, 997 COMPRESSION_EXTENSION, page); 998 return (TRUE); 999 } 1000 return (FALSE); 1001} 1002 1003/* Function Name: AddCursor 1004 * Description: This function adds the cursor to the window. 1005 * Arguments: w - the widget to add the cursor to. 1006 * cursor - the cursor to add to this widget. 1007 * Returns: none 1008 */ 1009 1010void 1011AddCursor(Widget w, Cursor cursor) 1012{ 1013 XColor colors[2]; 1014 Arg args[10]; 1015 Cardinal num_args = 0; 1016 Colormap c_map; 1017 1018 if (!XtIsRealized(w)) { 1019 PopupWarning(NULL, "Widget is not realized, no cursor added.\n"); 1020 return; 1021 } 1022 1023 XtSetArg(args[num_args], XtNcolormap, &c_map); 1024 num_args++; 1025 XtGetValues(w, args, num_args); 1026 1027 colors[0].pixel = resources.cursors.fg_color; 1028 colors[1].pixel = resources.cursors.bg_color; 1029 1030 XQueryColors(XtDisplay(w), c_map, colors, 2); 1031 XRecolorCursor(XtDisplay(w), cursor, colors, colors + 1); 1032 XDefineCursor(XtDisplay(w), XtWindow(w), cursor); 1033} 1034 1035/* Function Name: ChangeLabel 1036 * Description: This function changes the label field of the 1037 * given widget to the string in str. 1038 * Arguments: w - the widget. 1039 * str - the string to change the label to. 1040 * Returns: none 1041 */ 1042 1043void 1044ChangeLabel(Widget w, const char *str) 1045{ 1046 Arg arglist[3]; /* An argument list. */ 1047 1048 if (w == NULL) 1049 return; 1050 1051 XtSetArg(arglist[0], XtNlabel, str); 1052 1053/* shouldn't really have to do this. */ 1054 XtSetArg(arglist[1], XtNwidth, 0); 1055 XtSetArg(arglist[2], XtNheight, 0); 1056 1057 XtSetValues(w, arglist, (Cardinal) 1); 1058} 1059 1060/* 1061 * In an ideal world this would be part of the XToolkit, and I would not 1062 * have to do it, but such is life sometimes. Perhaps in X11R3. 1063 */ 1064 1065/* Function Name: PositionCenter 1066 * Description: This function positions the given widgets center 1067 * in the following location. 1068 * Arguments: widget - the widget widget to position 1069 * x,y - The location for the center of the widget 1070 * above - number of pixels above center to locate this widget 1071 * left - number of pixels left of center to locate this widget 1072 * h_space, v_space - how close to get to the edges of the 1073 * parent window. 1074 * Returns: none 1075 * Note: This should only be used with a popup widget that has override 1076 * redirect set. 1077 */ 1078 1079void 1080PositionCenter(Widget widget, int x, int y, int above, int left, int v_space, 1081 int h_space) 1082{ 1083 Arg wargs[2]; 1084 int x_temp, y_temp; /* location of the new window. */ 1085 int parent_height, parent_width; /* Height and width of the parent widget or 1086 the root window if it has no parent. */ 1087 1088 x_temp = x - left - Width(widget) / 2 + BorderWidth(widget); 1089 y_temp = y - above - Height(widget) / 2 + BorderWidth(widget); 1090 1091 parent_height = HeightOfScreen(XtScreen(widget)); 1092 parent_width = WidthOfScreen(XtScreen(widget)); 1093 1094/* 1095 * Check to make sure that all edges are within the viewable part of the 1096 * root window, and if not then force them to be. 1097 */ 1098 1099 if (x_temp < h_space) 1100 x_temp = v_space; 1101 if (y_temp < v_space) 1102 (y_temp = 2); 1103 1104 if (y_temp + Height(widget) + v_space > parent_height) 1105 y_temp = parent_height - Height(widget) - v_space; 1106 1107 if (x_temp + Width(widget) + h_space > parent_width) 1108 x_temp = parent_width - Width(widget) - h_space; 1109 1110 XtSetArg(wargs[0], XtNx, x_temp); 1111 XtSetArg(wargs[1], XtNy, y_temp); 1112 XtSetValues(widget, wargs, 2); 1113} 1114 1115/* Function Name: ParseEntry(entry, path, sect, page) 1116 * Description: Parses the manual pages entry filenames. 1117 * Arguments: str - the full path name. 1118 * path - the path name. RETURNED 1119 * sect - the section name. RETURNED 1120 * page - the page name. RETURNED 1121 * Returns: none. 1122 */ 1123 1124void 1125ParseEntry(const char *entry, char *path, char *sect, char *page) 1126{ 1127 char *c, temp[BUFSIZ]; 1128 1129 strcpy(temp, entry); 1130 1131 c = strrchr(temp, '/'); 1132 if (c == NULL) 1133 PrintError("Failed to find / in ParseEntry."); 1134 *c++ = '\0'; 1135 if (page != NULL) 1136 strcpy(page, c); 1137 1138 c = strrchr(temp, '/'); 1139 if (c == NULL) 1140 PrintError("Failed to find / in ParseEntry."); 1141 *c++ = '\0'; 1142#if defined(SFORMAT) && defined(SMAN) 1143 /* sgmltoroff sometimes puts an extra ./ in the path to .so entries */ 1144 if (strcmp(c, ".") == 0) { 1145 c = strrchr(temp, '/'); 1146 if (c == NULL) 1147 PrintError("Failed to find / in ParseEntry."); 1148 *c++ = '\0'; 1149 } 1150#endif 1151#if defined(__OpenBSD__) || defined(__NetBSD__) 1152 /* Skip machine subdirectory if present */ 1153 if (strcmp(c, MACHINE) == 0) { 1154 c = strrchr(temp, '/'); 1155 if (c == NULL) 1156 PrintError("Failed to find / in ParseEntry."); 1157 *c++ = '\0'; 1158 } 1159#endif 1160 if (sect != NULL) 1161 strcpy(sect, c); 1162 1163 if (path != NULL) 1164 strcpy(path, temp); 1165} 1166 1167/* Function Name: GetGlobals 1168 * Description: Gets the pseudo globals associated with the 1169 * manpage associated with this widget. 1170 * Arguments: w - a widget in the manpage. 1171 * Returns: the pseudo globals. 1172 * Notes: initial_widget is a globals variable. 1173 * manglobals_context is a global variable. 1174 */ 1175 1176ManpageGlobals * 1177GetGlobals(Widget w) 1178{ 1179 Widget temp; 1180 caddr_t data; 1181 1182 while ((temp = XtParent(w)) != initial_widget && (temp != NULL)) 1183 w = temp; 1184 1185 if (temp == NULL) 1186 XtAppError(XtWidgetToApplicationContext(w), 1187 "Xman: Could not locate widget in tree, exiting"); 1188 1189 if (XFindContext(XtDisplay(w), XtWindow(w), 1190 manglobals_context, &data) != XCSUCCESS) 1191 XtAppError(XtWidgetToApplicationContext(w), 1192 "Xman: Could not find global data, exiting"); 1193 1194 return ((ManpageGlobals *) data); 1195} 1196 1197/* Function Name: SaveGlobals 1198 * Description: Saves the pseudo globals on the widget passed 1199 * to this function, although GetGlobals assumes that 1200 * the data is associated with the popup child of topBox. 1201 * Arguments: w - the widget to associate the data with. 1202 * globals - data to associate with this widget. 1203 * Returns: none. 1204 * Notes: WIDGET MUST BE REALIZED. 1205 * manglobals_context is a global variable. 1206 */ 1207 1208void 1209SaveGlobals(Widget w, ManpageGlobals * globals) 1210{ 1211 if (XSaveContext(XtDisplay(w), XtWindow(w), manglobals_context, 1212 (caddr_t) globals) != XCSUCCESS) 1213 XtAppError(XtWidgetToApplicationContext(w), 1214 "Xman: Could not save global data, are you out of memory?"); 1215} 1216 1217/* Function Name: RemoveGlobals 1218 * Description: Removes the pseudo globals from the widget passed 1219 * to this function. 1220 * Arguments: w - the widget to remove the data from. 1221 * Returns: none. 1222 * Notes: WIDGET MUST BE REALIZED. 1223 * manglobals_context is a global variable. 1224 */ 1225 1226void 1227RemoveGlobals(Widget w) 1228{ 1229 if (XDeleteContext(XtDisplay(w), XtWindow(w), 1230 manglobals_context) != XCSUCCESS) 1231 XtAppError(XtWidgetToApplicationContext(w), 1232 "Xman: Could not remove global data?"); 1233} 1234