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